Spaces:
Sleeping
Sleeping
| .. _polys-wester: | |
| ============================== | |
| Examples from Wester's Article | |
| ============================== | |
| Introduction | |
| ============ | |
| In this tutorial we present examples from Wester's article concerning | |
| comparison and critique of mathematical abilities of several computer | |
| algebra systems (see [Wester1999]_). All the examples are related to | |
| polynomial and algebraic computations and SymPy specific remarks were | |
| added to all of them. | |
| Examples | |
| ======== | |
| All examples in this tutorial are computable, so one can just copy and | |
| paste them into a Python shell and do something useful with them. All | |
| computations were done using the following setup:: | |
| >>> from sympy import * | |
| >>> init_printing(use_unicode=True) | |
| >>> var('x,y,z,s,c') | |
| (x, y, z, s, c) | |
| Simple univariate polynomial factorization | |
| ------------------------------------------ | |
| To obtain a factorization of a polynomial use :func:`~.factor` function. | |
| By default :func:`~.factor` returns the result in unevaluated form, so the | |
| content of the input polynomial is left unexpanded, as in the following | |
| example:: | |
| >>> factor(6*x - 10) | |
| 2โ (3โ x - 5) | |
| To achieve the same effect in a more systematic way use :func:`~.primitive` | |
| function, which returns the content and the primitive part of the input | |
| polynomial:: | |
| >>> primitive(6*x - 10) | |
| (2, 3โ x - 5) | |
| .. note:: | |
| The content and the primitive part can be computed only over a ring. To | |
| simplify coefficients of a polynomial over a field use :func:`~.monic`. | |
| Univariate GCD, resultant and factorization | |
| ------------------------------------------- | |
| Consider univariate polynomials ``f``, ``g`` and ``h`` over integers:: | |
| >>> f = 64*x**34 - 21*x**47 - 126*x**8 - 46*x**5 - 16*x**60 - 81 | |
| >>> g = 72*x**60 - 25*x**25 - 19*x**23 - 22*x**39 - 83*x**52 + 54*x**10 + 81 | |
| >>> h = 34*x**19 - 25*x**16 + 70*x**7 + 20*x**3 - 91*x - 86 | |
| We can compute the greatest common divisor (GCD) of two polynomials using | |
| function:: | |
| >>> gcd(f, g) | |
| 1 | |
| We see that ``f`` and ``g`` have no common factors. However, ``f*h`` and ``g*h`` | |
| have an obvious factor ``h``:: | |
| >>> gcd(expand(f*h), expand(g*h)) - h | |
| 0 | |
| The same can be verified using the resultant of univariate polynomials:: | |
| >>> resultant(expand(f*h), expand(g*h)) | |
| 0 | |
| Factorization of large univariate polynomials (of degree 120 in this case) over | |
| integers is also possible:: | |
| >>> factor(expand(f*g)) | |
| โ 60 47 34 8 5 โ โ 60 52 39 25 23 10 โ | |
| -โ16โ x + 21โ x - 64โ x + 126โ x + 46โ x + 81โ โ โ72โ x - 83โ x - 22โ x - 25โ x - 19โ x + 54โ x + 81โ | |
| Multivariate GCD and factorization | |
| ---------------------------------- | |
| What can be done in univariate case, can be also done for multivariate | |
| polynomials. Consider the following polynomials ``f``, ``g`` and ``h`` | |
| in `\mathbb{Z}[x,y,z]`:: | |
| >>> f = 24*x*y**19*z**8 - 47*x**17*y**5*z**8 + 6*x**15*y**9*z**2 - 3*x**22 + 5 | |
| >>> g = 34*x**5*y**8*z**13 + 20*x**7*y**7*z**7 + 12*x**9*y**16*z**4 + 80*y**14*z | |
| >>> h = 11*x**12*y**7*z**13 - 23*x**2*y**8*z**10 + 47*x**17*y**5*z**8 | |
| As previously, we can verify that ``f`` and ``g`` have no common factors:: | |
| >>> gcd(f, g) | |
| 1 | |
| However, ``f*h`` and ``g*h`` have an obvious factor ``h``:: | |
| >>> gcd(expand(f*h), expand(g*h)) - h | |
| 0 | |
| Multivariate factorization of large polynomials is also possible:: | |
| >>> factor(expand(f*g)) | |
| 7 โ 9 9 3 7 6 5 12 7โ โ 22 17 5 8 15 9 2 19 8 โ | |
| -2โ y โ zโ โ6โ x โ y โ z + 10โ x โ z + 17โ x โ yโ z + 40โ y โ โ โ3โ x + 47โ x โ y โ z - 6โ x โ y โ z - 24โ xโ y โ z - 5โ | |
| Support for symbols in exponents | |
| -------------------------------- | |
| Polynomial manipulation functions provided by :mod:`sympy.polys` are mostly | |
| used with integer exponents. However, it's perfectly valid to compute with | |
| symbolic exponents, e.g.:: | |
| >>> n = var('n') | |
| >>> gcd(x**n - x**(2*n), x**n) | |
| n | |
| x | |
| Results may depend on powers being expanded (which will depend on | |
| assumptions of the base): | |
| >>> gcd(x**(n + 4), x**(n + 1) + 3*x**n) | |
| 1 | |
| >>> x = var('x', positive=True) | |
| >>> gcd(x**(n + 4), x**(n + 1) + 3*x**n) | |
| n | |
| x | |
| Testing if polynomials have common zeros | |
| ---------------------------------------- | |
| To test if two polynomials have a root in common we can use :func:`~.resultant` | |
| function. The theory says that the resultant of two polynomials vanishes if | |
| there is a common zero of those polynomials. For example:: | |
| >>> x = var('x') | |
| >>> resultant(3*x**4 + 3*x**3 + x**2 - x - 2, x**3 - 3*x**2 + x + 5) | |
| 0 | |
| We can visualize this fact by factoring the polynomials:: | |
| >>> factor(3*x**4 + 3*x**3 + x**2 - x - 2) | |
| โ 3 โ | |
| (x + 1)โ โ3โ x + x - 2โ | |
| >>> factor(x**3 - 3*x**2 + x + 5) | |
| โ 2 โ | |
| (x + 1)โ โx - 4โ x + 5โ | |
| In both cases we obtained the factor `x + 1` which tells us that the common | |
| root is `x = -1`. | |
| Normalizing simple rational functions | |
| ------------------------------------- | |
| To remove common factors from the numerator and the denominator of a rational | |
| function the elegant way, use :func:`~.cancel` function. For example:: | |
| >>> cancel((x**2 - 4)/(x**2 + 4*x + 4)) | |
| x - 2 | |
| โโโโโ | |
| x + 2 | |
| Expanding expressions and factoring back | |
| ---------------------------------------- | |
| One can work easily we expressions in both expanded and factored forms. | |
| Consider a polynomial ``f`` in expanded form. We differentiate it and | |
| factor the result back:: | |
| >>> f = expand((x + 1)**20) | |
| >>> g = diff(f, x) | |
| >>> factor(g) | |
| 19 | |
| 20โ (x + 1) | |
| The same can be achieved in factored form:: | |
| >>> diff((x + 1)**20, x) | |
| 19 | |
| 20โ (x + 1) | |
| Factoring in terms of cyclotomic polynomials | |
| -------------------------------------------- | |
| SymPy can very efficiently decompose polynomials of the form `x^n \pm 1` in | |
| terms of cyclotomic polynomials:: | |
| >>> factor(x**15 - 1) | |
| โ 2 โ โ 4 3 2 โ โ 8 7 5 4 3 โ | |
| (x - 1)โ โx + x + 1โ โ โx + x + x + x + 1โ โ โx - x + x - x + x - x + 1โ | |
| The original Wester`s example was `x^{100} - 1`, but was truncated for | |
| readability purpose. Note that this is not a big struggle for :func:`~.factor` | |
| to decompose polynomials of degree 1000 or greater. | |
| Univariate factoring over Gaussian numbers | |
| ------------------------------------------ | |
| Consider a univariate polynomial ``f`` with integer coefficients:: | |
| >>> f = 4*x**4 + 8*x**3 + 77*x**2 + 18*x + 153 | |
| We want to obtain a factorization of ``f`` over Gaussian numbers. To do this | |
| we use :func:`~.factor` as previously, but this time we set ``gaussian`` keyword | |
| to ``True``:: | |
| >>> factor(f, gaussian=True) | |
| โ 3โ โ โ โ 3โ โ โ | |
| 4โ โx - โโโโโ โx + โโโโโ (x + 1 - 4โ โ )โ (x + 1 + 4โ โ ) | |
| โ 2 โ โ 2 โ | |
| As the result we got a splitting factorization of ``f`` with monic factors | |
| (this is a general rule when computing in a field with SymPy). The ``gaussian`` | |
| keyword is useful for improving code readability, however the same result can | |
| be computed using more general syntax:: | |
| >>> factor(f, extension=I) | |
| โ 3โ โ โ โ 3โ โ โ | |
| 4โ โx - โโโโโ โx + โโโโโ (x + 1 - 4โ โ )โ (x + 1 + 4โ โ ) | |
| โ 2 โ โ 2 โ | |
| Computing with automatic field extensions | |
| ----------------------------------------- | |
| Consider two univariate polynomials ``f`` and ``g``:: | |
| >>> f = x**3 + (sqrt(2) - 2)*x**2 - (2*sqrt(2) + 3)*x - 3*sqrt(2) | |
| >>> g = x**2 - 2 | |
| We would like to reduce degrees of the numerator and the denominator of a | |
| rational function ``f/g``. To do this we employ :func:`~.cancel` function:: | |
| >>> cancel(f/g) | |
| 3 2 2 | |
| x - 2โ x + โ2โ x - 3โ x - 2โ โ2โ x - 3โ โ2 | |
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 2 | |
| x - 2 | |
| Unfortunately nothing interesting happened. This is because by default SymPy | |
| treats `\sqrt{2}` as a generator, obtaining a bivariate polynomial for the | |
| numerator. To make :func:`~.cancel` recognize algebraic properties of `\sqrt{2}`, | |
| one needs to use ``extension`` keyword:: | |
| >>> cancel(f/g, extension=True) | |
| 2 | |
| x - 2โ x - 3 | |
| โโโโโโโโโโโโ | |
| x - โ2 | |
| Setting ``extension=True`` tells :func:`~.cancel` to find minimal algebraic | |
| number domain for the coefficients of ``f/g``. The automatically inferred | |
| domain is `\mathbb{Q}(\sqrt{2})`. If one doesn't want to rely on automatic | |
| inference, the same result can be obtained by setting the ``extension`` | |
| keyword with an explicit algebraic number:: | |
| >>> cancel(f/g, extension=sqrt(2)) | |
| 2 | |
| x - 2โ x - 3 | |
| โโโโโโโโโโโโ | |
| x - โ2 | |
| Univariate factoring over various domains | |
| ----------------------------------------- | |
| Consider a univariate polynomial ``f`` with integer coefficients:: | |
| >>> f = x**4 - 3*x**2 + 1 | |
| With :mod:`sympy.polys` we can obtain factorizations of ``f`` over different | |
| domains, which includes: | |
| * rationals:: | |
| >>> factor(f) | |
| โ 2 โ โ 2 โ | |
| โx - x - 1โ โ โx + x - 1โ | |
| * finite fields:: | |
| >>> factor(f, modulus=5) | |
| 2 2 | |
| (x - 2) โ (x + 2) | |
| * algebraic numbers:: | |
| >>> alg = AlgebraicNumber((sqrt(5) - 1)/2, alias='alpha') | |
| >>> factor(f, extension=alg) | |
| (x - ฮฑ)โ (x + ฮฑ)โ (x - 1 - ฮฑ)โ (x + ฮฑ + 1) | |
| Factoring polynomials into linear factors | |
| ----------------------------------------- | |
| Currently SymPy can factor polynomials into irreducibles over various domains, | |
| which can result in a splitting factorization (into linear factors). However, | |
| there is currently no systematic way to infer a splitting field (algebraic | |
| number field) automatically. In future the following syntax will be | |
| implemented:: | |
| >>> factor(x**3 + x**2 - 7, split=True) | |
| Traceback (most recent call last): | |
| ... | |
| NotImplementedError: 'split' option is not implemented yet | |
| Note this is different from ``extension=True``, because the later only tells how | |
| expression parsing should be done, not what should be the domain of computation. | |
| One can simulate the ``split`` keyword for several classes of polynomials using | |
| function. | |
| Advanced factoring over finite fields | |
| ------------------------------------- | |
| Consider a univariate polynomial ``f`` with integer coefficients:: | |
| >>> f = x**11 + x + 1 | |
| We can factor ``f`` over a large finite field `F_{65537}`:: | |
| >>> factor(f, modulus=65537) | |
| โ 2 โ โ 9 8 6 5 3 2 โ | |
| โx + x + 1โ โ โx - x + x - x + x - x + 1โ | |
| and expand the resulting factorization back:: | |
| >>> expand(_) | |
| 11 | |
| x + x + 1 | |
| obtaining polynomial ``f``. This was done using symmetric polynomial | |
| representation over finite fields The same thing can be done using | |
| non-symmetric representation:: | |
| >>> factor(f, modulus=65537, symmetric=False) | |
| โ 2 โ โ 9 8 6 5 3 2 โ | |
| โx + x + 1โ โ โx + 65536โ x + x + 65536โ x + x + 65536โ x + 1โ | |
| As with symmetric representation we can expand the factorization | |
| to get the input polynomial back. This time, however, we need to | |
| truncate coefficients of the expanded polynomial modulo 65537:: | |
| >>> trunc(expand(_), 65537) | |
| 11 | |
| x + x + 1 | |
| Working with expressions as polynomials | |
| --------------------------------------- | |
| Consider a multivariate polynomial ``f`` in `\mathbb{Z}[x,y,z]`:: | |
| >>> f = expand((x - 2*y**2 + 3*z**3)**20) | |
| We want to compute factorization of ``f``. To do this we use ``factor`` as | |
| usually, however we note that the polynomial in consideration is already | |
| in expanded form, so we can tell the factorization routine to skip | |
| expanding ``f``:: | |
| >>> factor(f, expand=False) | |
| 20 | |
| โ 2 3โ | |
| โx - 2โ y + 3โ z โ | |
| The default in :mod:`sympy.polys` is to expand all expressions given as | |
| arguments to polynomial manipulation functions and :class:`~.Poly` class. | |
| If we know that expanding is unnecessary, then by setting ``expand=False`` | |
| we can save quite a lot of time for complicated inputs. This can be really | |
| important when computing with expressions like:: | |
| >>> g = expand((sin(x) - 2*cos(y)**2 + 3*tan(z)**3)**20) | |
| >>> factor(g, expand=False) | |
| 20 | |
| โ 2 3 โ | |
| โ-sin(x) + 2โ cos (y) - 3โ tan (z)โ | |
| Computing reduced Grรถbner bases | |
| ------------------------------- | |
| To compute a reduced Grรถbner basis for a set of polynomials use the | |
| function. The function accepts various | |
| monomial orderings, e.g.: ``lex``, ``grlex`` and ``grevlex``, or a user | |
| defined one, via ``order`` keyword. The ``lex`` ordering is the most | |
| interesting because it has elimination property, which means that if the | |
| system of polynomial equations to :func:`~sympy.polys.polytools.groebner` is | |
| zero-dimensional (has finite number of solutions) the last element of the | |
| basis is a univariate polynomial. Consider the following example:: | |
| >>> f = expand((1 - c**2)**5 * (1 - s**2)**5 * (c**2 + s**2)**10) | |
| >>> groebner([f, c**2 + s**2 - 1]) | |
| โโก 2 2 20 18 16 14 12 10โค โ | |
| GroebnerBasisโโฃc + s - 1, c - 5โ c + 10โ c - 10โ c + 5โ c - c โฆ, s, c, domain=โค, order=lexโ | |
| The result is an ordinary Python list, so we can easily apply a function to | |
| all its elements, for example we can factor those elements:: | |
| >>> list(map(factor, _)) | |
| โก 2 2 10 5 5โค | |
| โฃc + s - 1, c โ (c - 1) โ (c + 1) โฆ | |
| From the above we can easily find all solutions of the system of polynomial | |
| equations. Or we can use :func:`~.solve` to achieve this in a more systematic | |
| way:: | |
| >>> solve([f, s**2 + c**2 - 1], c, s) | |
| [(-1, 0), (0, -1), (0, 1), (1, 0)] | |
| Multivariate factoring over algebraic numbers | |
| --------------------------------------------- | |
| Computing with multivariate polynomials over various domains is as simple as | |
| in univariate case. For example consider the following factorization over | |
| `\mathbb{Q}(\sqrt{-3})`:: | |
| >>> factor(x**3 + y**3, extension=sqrt(-3)) | |
| โ โ 1 โ3โ โ โโ โ โ 1 โ3โ โ โโ | |
| (x + y)โ โx + yโ โ- โ - โโโโโโโ โx + yโ โ- โ + โโโโโโ | |
| โ โ 2 2 โ โ โ โ 2 2 โ โ | |
| .. note:: Currently multivariate polynomials over finite fields aren't supported. | |
| Partial fraction decomposition | |
| ------------------------------ | |
| Consider a univariate rational function ``f`` with integer coefficients:: | |
| >>> f = (x**2 + 2*x + 3)/(x**3 + 4*x**2 + 5*x + 2) | |
| To decompose ``f`` into partial fractions use :func:`~.apart` function:: | |
| >>> apart(f) | |
| 3 2 2 | |
| โโโโโ - โโโโโ + โโโโโโโโ | |
| x + 2 x + 1 2 | |
| (x + 1) | |
| To return from partial fractions to the rational function use | |
| a composition of :func:`~.together` and :func:`~.cancel`:: | |
| >>> cancel(together(_)) | |
| 2 | |
| x + 2โ x + 3 | |
| โโโโโโโโโโโโโโโโโโโ | |
| 3 2 | |
| x + 4โ x + 5โ x + 2 | |
| Literature | |
| ========== | |
| .. [Wester1999] Michael J. Wester, A Critique of the Mathematical Abilities of | |
| CA Systems, 1999, `<https://www.math.unm.edu/~wester/cas/book/Wester.pdf>`_ | |