diff --git a/README.md b/README.md index 044096c..638f08c 100644 --- a/README.md +++ b/README.md @@ -82,8 +82,8 @@ For using Rings solely in Java there is Maven artifact: ``` -Examples: algebra, GCDs, factorization --------------------------------------- +Examples: algebra, GCDs, factorization, programming +--------------------------------------------------- Below examples can be evaluated directly in the Rings.repl. If using Rings in Scala, the following preambula will import all required things from Rings library: @@ -336,6 +336,97 @@ implicit val ring = MultivariateRing(cfRing, Array("a", "b", "c")) val poly = ring("1 - (1 - z^3) * a^6*b + (1 - 2*z) * c^33 + a^66") ``` + +------------------------------------------------------------------------ + +Implement generic function for solving linear Diophantine equations: + +```scala +/** + * Solves equation \sum f_i s_i = gcd(f_1, \dots, f_N) for given f_i and unknown s_i + * @return a tuple (gcd, solution) + */ +def solveDiophantine[E](fi: Seq[E])(implicit ring: Ring[E]) = + fi.foldLeft((ring(0), Seq.empty[E])) { case ((gcd, seq), f) => + val xgcd = ring.extendedGCD(gcd, f) + (xgcd(0), seq.map(_ * xgcd(1)) :+ xgcd(2)) + } +``` + +Implement generic function for computing partial fraction decomposition: + +```scala +/** Computes partial fraction decomposition of given rational */ +def apart[E](frac: Rational[E]) = { + implicit val ring: Ring[E] = frac.ring + val factors = ring.factor(frac.denominator).map {case (f, exp) => f.pow(exp)} + val (gcd, nums) = solveDiophantine(factors.map(frac.denominator / _)) + val (ints, rats) = (nums zip factors) + .map { case (num, den) => Rational(frac.numerator * num, den * gcd) } + .flatMap(_.normal) // extract integral parts from fractions + .partition(_.isIntegral) // separate integrals and fractions + rats :+ ints.foldLeft(Rational(ring(0)))(_ + _) +} +``` + + +Apply that function to elements of different rings: + +```scala +// partial fraction decomposition for rationals +// gives List(184/479, (-10)/13, 1/8, (-10)/47, 1) +val qFracs = apart( Q("1234213 / 2341352")) + +// partial fraction decomposition for rational functions +val ufRing = Frac(UnivariateRingZp64(17, "x")) +// gives List(4/(16+x), 1/(10+x), 15/(1+x), (14*x)/(15+7*x+x^2)) +val pFracs = apart( ufRing("1 / (3 - 3*x^2 - x^3 + x^5)") ) +``` + + +------------------------------------------------------------------------ + +Implement Lagrange method for univariate interpolation: + +```latex + p(x) = \sum_i p(x_i) \Pi_{j \ne i} \frac{x_{\phantom{i}} - x_j}{x_i -x_j} +``` + +```scala +/** Lagrange polynomial interpolation formula */ +def interpolate[Poly <: IUnivariatePolynomial[Poly], Coef] + (points: Seq[(Coef, Coef)]) + (implicit ring: IUnivariateRing[Poly, Coef]) = { + // implicit coefficient ring (setups algebraic operators on type Coef) + implicit val cfRing: Ring[Coef] = ring.cfRing + if (!cfRing.isField) throw new IllegalArgumentException + points.indices + .foldLeft(ring(0)) { case (sum, i) => + sum + points.indices + .filter(_ != i) + .foldLeft(ring(points(i)._2)) { case (product, j) => + product * (ring.`x` - points(j)._1) / (points(i)._1 - points(j)._1) + } + } + } +``` + + +Interpolate polynomial from `Frac(Z_{13}[a,b,c])[x]`: + + +```scala +// coefficient ring Frac(Z/13[a,b,c]) +val cfRing = Frac(MultivariateRingZp64(2, Array("a", "b", "c"))) +val (a, b, c) = cfRing("a", "b", "c") + +implicit val ring = UnivariateRing(cfRing, "x") +// interpolate with Lagrange formula +val data = Seq(a -> b, b -> c, c -> a) +val poly = interpolate(data) +assert(data.forall { case (p, v) => poly.eval(p) == v }) +``` + Highlighted benchmarks ---------------------- diff --git a/doc/quickstart.rst b/doc/quickstart.rst index 033bd7d..fc17a1f 100644 --- a/doc/quickstart.rst +++ b/doc/quickstart.rst @@ -71,8 +71,8 @@ For using |Rings| solely in Java there is Maven artifact: 2.1 -Examples: algebra, GCDs, factorization -====================================== +Examples: algebra, GCDs, factorization, programming +=================================================== Below examples can be evaluated directly in the |Rings|\ *.repl*. For Scala/Java the following preambula will import all required things from |Rings| library: @@ -617,6 +617,110 @@ Ring of multivariate polynomials over elements of Galois field :math:`GF(7^{3})[ poly = ring.parse("1 - (1 - z^3) * a^6*b + (1 - 2*z) * c^33 + a^66"); +---- + +Implement generic function for solving linear Diophantine equations: + + +.. tabs:: + + .. code-tab:: scala + + /** + * Solves equation \sum f_i s_i = gcd(f_1, \dots, f_N) for given f_i and unknown s_i + * @return a tuple (gcd, solution) + */ + def solveDiophantine[E](fi: Seq[E])(implicit ring: Ring[E]) = + fi.foldLeft((ring(0), Seq.empty[E])) { case ((gcd, seq), f) => + val xgcd = ring.extendedGCD(gcd, f) + (xgcd(0), seq.map(_ * xgcd(1)) :+ xgcd(2)) + } + + +Implement generic function for computing partial fraction decomposition: + +.. tabs:: + + .. code-tab:: scala + + /** Computes partial fraction decomposition of given rational */ + def apart[E](frac: Rational[E]) = { + implicit val ring: Ring[E] = frac.ring + val factors = ring.factor(frac.denominator).map {case (f, exp) => f.pow(exp)} + val (gcd, nums) = solveDiophantine(factors.map(frac.denominator / _)) + val (ints, rats) = (nums zip factors) + .map { case (num, den) => Rational(frac.numerator * num, den * gcd) } + .flatMap(_.normal) // extract integral parts from fractions + .partition(_.isIntegral) // separate integrals and fractions + rats :+ ints.foldLeft(Rational(ring(0)))(_ + _) + } + + +Apply that function to elements of different rings: + + +.. tabs:: + + .. code-tab:: scala + + // partial fraction decomposition for rationals + // gives List(184/479, (-10)/13, 1/8, (-10)/47, 1) + val qFracs = apart( Q("1234213 / 2341352")) + + // partial fraction decomposition for rational functions + val ufRing = Frac(UnivariateRingZp64(17, "x")) + // gives List(4/(16+x), 1/(10+x), 15/(1+x), (14*x)/(15+7*x+x^2)) + val pFracs = apart( ufRing("1 / (3 - 3*x^2 - x^3 + x^5)") ) + + +---- + +Implement Lagrange method for univariate interpolation: + +.. math:: + + p(x) = \sum_i p(x_i) \Pi_{j \ne i} \frac{x_{\phantom{i}} - x_j}{x_i -x_j} + + +.. tabs:: + + .. code-tab:: scala + + /** Lagrange polynomial interpolation formula */ + def interpolate[Poly <: IUnivariatePolynomial[Poly], Coef] + (points: Seq[(Coef, Coef)]) + (implicit ring: IUnivariateRing[Poly, Coef]) = { + // implicit coefficient ring (setups algebraic operators on type Coef) + implicit val cfRing: Ring[Coef] = ring.cfRing + if (!cfRing.isField) throw new IllegalArgumentException + points.indices + .foldLeft(ring(0)) { case (sum, i) => + sum + points.indices + .filter(_ != i) + .foldLeft(ring(points(i)._2)) { case (product, j) => + product * (ring.`x` - points(j)._1) / (points(i)._1 - points(j)._1) + } + } + } + + +Interpolate polynomial from :math:`Frac(Z_{13}[a,b,c])[x]`: + + +.. tabs:: + + .. code-tab:: scala + + // coefficient ring Frac(Z/13[a,b,c]) + val cfRing = Frac(MultivariateRingZp64(2, Array("a", "b", "c"))) + val (a, b, c) = cfRing("a", "b", "c") + + implicit val ring = UnivariateRing(cfRing, "x") + // interpolate with Lagrange formula + val data = Seq(a -> b, b -> c, c -> a) + val poly = interpolate(data) + assert(data.forall { case (p, v) => poly.eval(p) == v }) + .. _ref-some-benchamrks: