This tutorial tries to give an overview of the functionality concerning polynomials within SymPy. All code examples assume:
>>> from sympy import *
>>> x, y, z = symbols('xyz')
These functions provide different algorithms dealing with polynomials in the form of SymPy expression, like symbols, sums etc.
The function div() provides division of polynomials with remainder. That is, for polynomials f and g, it computes q and r, such that f == g*q + r and degree(r) < q. For polynomials in one variables with coefficients in a field, say, the rational numbers, q and r are uniquely defined this way.
>>> from sympy import pprint
>>> import sys
>>> sys.displayhook = pprint
>>> f = 5*x**2 + 10*x + 3
>>> g = 2*x + 2
>>> q, r = div(f, g, x, y)
>>> q
5*x
5/2 + ---
2
>>> r
-2
>>> (q*g + r).expand()
2
3 + 10*x + 5*x
As you can see, q has a non-integer coefficient. If you want to do division only in the ring of polynomials with integer coefficients you can specify an additional parameter.
>>> q, r = div(f, g, coeff='int') #doctest: +SKIP
>>> q #doctest: +SKIP
0
>>> r #doctest: +SKIP
13 + 5*x**2 + 15*x
But be warned, that this ring is no longer euclidean and that the degree of the remainder doesn’t need to be smaller than that of f. Since 2 doesn’t divide 5, 2*x doesn’t divide 5*x**2, even if the degree is smaller. But:
>>> g = 5*x + 1
>>> q, r = div(f, g, x, coeff='int') #doctest: +SKIP
>>> q #doctest: +SKIP
x
>>> r #doctest: +SKIP
13 + 14*x
>>> (q*g + r).expand() #doctest: +SKIP
13 + 5*x**2 + 15*x
This also works for polynomials with multiple variables.
>>> f = x*y + y*z
>>> g = 3*x + 3*z
>>> q, r = div(f, g, x, y)
>>> q
y
-
3
>>> r
0
In the last examples, all of the three variables x, y and z are assumed to be variables of the polynomials. But if you have some unrelated constant as coefficient, you can specify the variables explicitly:
>>> a, b, c = symbols('abc')
>>> f = a*x**2 + b*x + c
>>> g = 3*x + 2
>>> q, r = div(f, g, x)
>>> q
2*a b a*x
- --- + - + ---
9 3 3
>>> r
2*b 4*a
c - --- + ---
3 9
Another option is division by multiple polynomials at the same time. In general, the output is not unique and depends on the order of the divisors and the given monomial order (if specified).
>>> f = x*y + y*z + z*x
>>> g1 = x + 1
>>> g2 = 2*y + 1
>>> q, r = div(f, [g1, g2], x, y, z)
>>> q
z
[y + z, -1/2 + -]
2
>>> r
3*z
1/2 - ---
2
>>> (q[0]*g1 + q[1]*g2 + r).expand()
x*y + x*z + y*z
>>> q, r = div(f, [g2, g1], x, y, z)
>>> q
x z
[- + -, -1/2 + z]
2 2
>>> r
3*z
1/2 - ---
2
>>> (q[0]*g2 + q[1]*g1 + r).expand()
x*y + x*z + y*z
With division, there is also the computation of the greatest common divisor and the least common multiple.
When the polynomials have integer coefficients, the contents’ gcd is also considered.
>>> f = 12*(x + 1)*x
>>> g = 16*x**2
>>> gcd(f, g, x)
4*x
It also works with multiple variables. In this case, the variables are ordered alphabetically, be default, which has influence on the leading coefficient.
>>> f = x*y/2 + y**2
>>> g = 3*x + 6*y
>>> gcd(f, g, x, y)
x + 2*y
>>> gcd(f, g, y, x)
x
y + -
2
The lcm is connected with the gcd and one can be computed using the other.
>>> f = x*y**2 + x**2*y
>>> g = x**2*y**2
>>> gcd(f, g, x, y)
x*y
>>> lcm(f, g, x, y)
3 2 2 3
x *y + x *y
>>> (f*g).expand()
4 3 3 4
x *y + x *y
>>> (gcd(f, g, x, y)*lcm(f, g, x, y)).expand()
4 3 3 4
x *y + x *y
The square-free factorization of a univariate polynomial is the product of all factors (not irreducible) of degree 1, 2 ...
>>> sqf((x + 2)*(x*(x + 1))**2, x)
2
[2 + x, x + x ]
This function provides factorization of univariate and multivariate polynomials with rational coefficients.
>>> factor(Rational(1,2)*x**4 + Rational(5,12)*x**3 - Rational(1,3)*x**2)
2
-x *(1 - 2*x)*(4 + 3*x)
-----------------------
12
>>> factor(x**2 + 4*x*y + 4*y**2)
2
(x + 2*y)
Buchberger’s algorithm is implemented, supporting various monomial orders.
>>> groebner([x**2 + 1, y**4*x + x**3], x, y, order='lex')
2 4
[1 + x , -1 + y ]
>>> groebner([x**2 + 1, y**4*x + x**3, x*y*z**3], x, y, z, order='grevlex')
4 3 2
[-1 + y , z , 1 + x ]
We have (incomplete) methods to find the complex or even symbolic roots of polynomials and to solve some systems of polynomial equations.
>>> from sympy import roots, solve_poly_system
>>> solve(x**3 + 2*x + 3, x)
____ ____
I*\/ 11 I*\/ 11
[1/2 - --------, -1, 1/2 + --------]
2 2
>>> p = Symbol('p')
>>> q = Symbol('q')
>>> sorted(solve(x**2 + p*x + q, x))
___________ ___________
/ 2 / 2
p \/ -4*q + p p \/ -4*q + p
[- - + --------------, - - - --------------]
2 2 2 2
>>> solve_poly_system([y - x, x - 5], x, y)
[(5, 5)]
>>> solve_poly_system([y**2 - x**3 + 1, y*x], x, y)
___ ___
I*\/ 3 I*\/ 3
[(0, I), (0, -I), (1, 0), (-1/2 + -------, 0), (-1/2 - -------, 0)]
2 2
Represents polynomials with symbolic coefficients.
Polynomials are internally represented as two lists containing coefficients and monomials (tuples of exponents) respectively. Stored are only terms with non-zero coefficients, so generally all polynomials are considered sparse. However algorithms will detect dense polynomials and use the appropriate method to solve the given problem in the most efficient way.
The most common way to initialize a polynomial instance is to provide a valid expression together witch a set of ordered symbols and, additionally, monomial ordering:
Poly(expression, x_1, x_2, ..., x_n, order=’grlex’)
If the given expression is not a polynomial with respect to the given variables, an exception is raised. Alternatively you can use Basic.as_poly to avoid exception handling.
By default ordering of monomials can be omitted. In this case graded lexicographic order will be used. Anyway remember that ‘order’ is a keyword argument.
Currently there are supported four standard orderings:
[1] lex -> lexicographic order [2] grlex -> graded lex order [3] grevlex -> reversed grlex order [4] 1-el -> first elimination order
Polynomial can be also constructed explicitly by specifying a collection of coefficients and monomials. This can be done in at least three different ways:
[1] [(c_1, M_1), (c_2, M_2), ..., (c_1, M_1)]
[2] (c_1, c2, ..., c_n), (M_1, M_2, ..., M_n)
[3] { M_1 : c_1, M_2 : c_2, ..., M_n : c_n }
Although all three representation look similar, they are designed for different tasks and have specific properties:
- [1] All coefficients and monomials are validated before
- polynomial instance is created. Monomials are sorted with respect to the given order.
[2] No validity checking, no sorting, just a raw copy.
- [3] Also no validity checking however monomials are
- sorted with respect to the given order.
For interactive usage choose [1] as it’s safe and relatively fast. Use [2] or [3] internally for time critical algorithms, when you know that coefficients and monomials will be valid.
Implemented methods:
- U –> handles uniformly int | long and Basic coefficients
- (other methods need to be overridden in IntegerPoly)
P –> a property (with appropriate decorator)
- –> otherwise
[1] Representation conversion:
[1.1] [–] as_basic –> converts to a valid sympy expression [1.2] [U-] as_dict –> returns { M_1 : c_1, ..., M_1 : c_1 } [1.3] [U-] as_uv_dict –> returns dict with integer keys
[2] Polynomial internals:
[2.1] [UP] coeffs –> list of coefficients [2.2] [UP] monoms –> list of monomials [2.3] [UP] symbols –> an ordered list of symbols [2.4] [UP] order –> the ordering of monomials [2.5] [UP] stamp –> frozen set of polynomial’s variables [2.6] [UP] domain –> coefficient domain, see [8] for details [2.7] [UP] args –> required for printing and hashing [2.8] [UP] flags –> dict of keyword arguments to Poly
[3] General characteristics:
[3.1] [UP] norm –> computes oo-norm of coefficients [3.2] [UP] length –> counts the total number of terms [3.3] [UP] degree –> returns degree of the leading monomial [3.4] [UP] total_degree –> returns true total degree of a polynomial
[4] Items generators:
[4.1] [U-] iter_coeffs –> iterate over coefficients [4.2] [U-] iter_monoms –> iterate over monomials [4.3] [U-] iter_terms –> iterate over terms
[4.4] [U-] iter_all_coeffs –> iterate over all coefficients [4.5] [U-] iter_all_monoms –> iterate over all monomials [4.6] [U-] iter_all_terms –> iterate over all terms
[5] Leading and trailing items:
[5.1] [UP] lead_coeff or LC –> returns the leading coefficient [5.2] [UP] lead_monom or LM –> returns the leading monomial [5.3] [UP] lead_term or LT –> returns the leading term
[5.4] [UP] tail_coeff or TC –> returns the trailing coefficient [5.5] [UP] tail_monom or TM –> returns the trailing monomial [5.6] [UP] tail_term or TT –> returns the trailing term
[6] Arithmetic operators:
[6.1] [U-] __abs__ –> for all i, abs(c_i) [6.2] [U-] __neg__ –> polynomial negation [6.3] [U-] __add__ –> polynomial addition [6.4] [U-] __sub__ –> polynomial subtraction [6.5] [–] __mul__ –> polynomial multiplication [6.6] [U-] __pow__ –> polynomial exponentiation [6.7] [U-] __div__ –> polynomial quotient [6.8] [U-] __mod__ –> polynomial remainder [6.9] [U-] __divmod__ –> both ‘div’ and ‘mod’ [6.10] [U-] __truediv__ –> the same as ‘div’
[7] Polynomial properties:
[7.1] [UP] is_zero –> only one term c_0 == 0 [7.2] [UP] is_one –> only one term c_0 == 1 [7.3] [-P] is_number –> only numeric constant term [7.4] [UP] is_constant –> only arbitrary constant term [7.5] [UP] is_monomial –> number of terms == 1 [7.6] [UP] is_univariate –> number of variables == 1 [7.7] [UP] is_multivariate –> number of variables > 1 [7.8] [UP] is_homogeneous –> has constant term [7.9] [UP] is_inhomogeneous –> no constant term [7.10] [UP] is_sparse –> filled with less than 90% of terms [7.11] [UP] is_dense –> filled with more than 90% of terms [7.12] [UP] is_monic –> returns True if leading coeff is one [7.13] [UP] is_primitive –> returns True if content is one [7.14] [UP] is_squarefree –> no factors of multiplicity >= 2 [7.15] [UP] is_linear –> all monomials of degree <= 1
[8] Coefficients properties:
[8.1] [UP] has_integer_coeffs –> for all i, c_i in Z [8.2] [UP] has_rational_coeffs –> for all i, c_i in Q [8.3] [UP] has_radical_coeffs –> for all i, c_i in R [8.4] [UP] has_complex_coeffs –> for all i, c_i in C [8.5] [UP] has_symbolic_coeffs –> otherwise
[9] Special functionality:
[9.1] [–] as_monic –> divides all coefficients by LC [9.2] [–] as_integer –> returns polynomial with integer coeffs
[9.3] [-P] content –> returns GCD of polynomial coefficients [9.4] [–] as_primitive –> returns content and primitive part
[9.5] [U-] as_squarefree –> returns square-free part of a polynomial
[9.6] [U-] as_reduced –> extract GCD of polynomial monomials
[9.7] [–] map_coeffs –> applies a function to all coefficients [9.8] [U-] coeff –> returns coefficient of the given monomial
[9.9] [U-] unify_with –> returns polys with a common set of symbols
[9.10] [U-] diff –> efficient polynomial differentiation [9.11] [U-] integrate –> efficient polynomial integration
[9.12] [U-] invert –> computes multiplicative inverse in K[t]
[10] Operations on terms:
[10.1] [U-] add_term –> add a term to a polynomial [10.2] [U-] sub_term –> subtract a term from a polynomial
[10.3] [–] mul_term –> multiply a polynomial with a term [10.4] [–] div_term –> divide a polynomial by a term
[10.5] [U-] remove_lead_term –> remove leading term (up to order) [10.6] [U-] remove_last_term –> remove last term (up to order)
[11] Substitution and evaluation:
[11.1] [U-] __call__ –> evaluates poly a the given point [11.2] [U-] evaluate –> evaluates poly for specific vars [11.3] [–] _eval_subs –> efficiently substitute variables
[12] Comparison functions:
[12.1] [U-] __eq__ –> P1 == P, up to order of monomials [12.2] [U-] __ne__ –> P1 != P, up to order of monomials [12.3] [U-] __nonzero__ –> check if zero polynomial
[13] String representation functions:
[13.1] [U-] repr –> returns represantation of ‘self’ [13.3] [U-] str –> returns pretty representation
[14] Other (derived from Basic) functionality:
[14.1] [U-] _eval_is_polynomial –> checks if poly is a poly
For general information on polynomials and algorithms, refer to:
See also documentation of particular methods.
Efficiently add a single term to ‘self’.
The list of monomials is sorted at initialization time, this motivates usage of binary search algorithm to find an index of an existing monomial or a suitable place for a new one. This gives O(lg(n)) complexity, where ‘n’ is the initial number of terms, superior to naive approach.
For more information on the implemented algorithm refer to:
Converts polynomial to a valid sympy expression.
Takes list representation of a polynomial and constructs expression using symbols used during creation of a given polynomial. If you wish to have different symbols in the resulting expressions, use subs() first.
Note that the result is always returned in expanded form.
>>> from sympy import *
>>> x,y = symbols('xy')
>>> p = Poly(x**2+3, x)
>>> p.as_basic()
3 + x**2
Convert list representation to dictionary representation.
Due to hashing of immutable expressions in Python, we can not store a dictionary directly in a polynomial instance. Also it would be inconvenient in case of ordering of monomials (unnecessary sorting).
This method creates dictionary efficiently, so it can be used in several algorithms which perform best when dicts are being used, eg. addition, multiplication etc.
>>> from sympy import *
>>> x,y = symbols('xy')
>>> p = Poly(x**2+3, x)
>>> p.as_dict()
{(0,): 3, (2,): 1}
Makes all coefficients integers if possible.
Given a polynomial with rational coefficients, returns a tuple consisting of a common denominator of those coefficients and an integer polynomial. Otherwise an exception is being raised.
>>> from sympy import *
>>> x,y = symbols("xy")
>>> Poly(3*x**2 + x, x).as_integer()
(1, Poly(3*x**2 + x, x))
>>> Poly(3*x**2 + x/2, x).as_integer()
(2, Poly(6*x**2 + x, x))
Returns a monic polynomial.
>>> from sympy import *
>>> x,y = symbols('xy')
>>> Poly(x**2 + 4, x).as_monic()
Poly(x**2 + 4, x)
>>> Poly(2*x**2 + 4, x).as_monic()
Poly(x**2 + 2, x)
>>> Poly(y*x**2 + y**2 + y, x).as_monic()
Poly(x**2 + 1 + y, x)
Returns content and primitive part of a polynomial.
>>> from sympy import *
>>> x,y = symbols('xy')
>>> p = Poly(4*x**2 + 2*x, x)
>>> p.as_primitive()
(2, Poly(2*x**2 + x, x))
Remove GCD of monomials from ‘self’.
>>> from sympy import *
>>> x,y = symbols('xy')
>>> Poly(x**3 + x, x).as_reduced()
((1,), Poly(x**2 + 1, x))
>>> Poly(x**3*y+x**2*y**2, x, y).as_reduced()
((2, 1), Poly(x + y, x, y))
Returns square-free part of a polynomial.
>>> from sympy import *
>>> x,y = symbols('xy')
>>> Poly((x-1)**2, x).as_squarefree()
Poly(x - 1, x)
Return dictionary representation with integer keys.
In case of univariate polynomials it is inefficient to to handle exponents as singleton tuples, as an example see multiplication algorithm. This method will return dictionary representation with all tuples converted to explicit integers.
>>> from sympy import *
>>> x,y = symbols('xy')
>>> p = Poly(x**2+3, x)
>>> p.as_uv_dict()
{0: 3, 2: 1}
Cancel common factors in a fractional expression.
Given a quotient of polynomials, cancel common factors from the numerator and the denominator. The result is formed in an expanded form, even if there was no cancellation.
To improve the speed of calculations a list of symbols can be specified. This can be particularly useful in univariate case with additional parameters, to avoid Groebner basis computations.
The input fraction can be given as a single expression or as a tuple consisting of the numerator and denominator.
>>> from sympy import *
>>> x,y = symbols('xy')
>>> Poly.cancel((x**2-y**2)/(x-y), x, y)
x + y
>>> Poly.cancel((x**2-y**2, x-y), x, y)
x + y
Returns coefficient of a particular monomial.
>>> from sympy import *
>>> x,y = symbols('xy')
>>> p = Poly(3*x**2*y + 4*x*y**2 + 1, x, y)
>>> p.coeff(2, 1)
3
>>> p.coeff(1, 2)
4
>>> p.coeff(1, 1)
0
If len(monom) == 0 then returns coeff of the constant term:
>>> p.coeff(0, 0)
1
>>> p.coeff()
1
Returns integer GCD of all the coefficients.
>>> from sympy import *
>>> x,y,z = symbols('xyz')
>>> p = Poly(2*x + 5*x*y, x, y)
>>> p.content
1
>>> p = Poly(2*x + 4*x*y, x, y)
>>> p.content
2
>>> p = Poly(2*x + z*x*y, x, y)
>>> p.content
1
Efficiently differentiate polynomials.
Differentiate a polynomial with respect to a set of symbols. If a symbol is polynomial’s variable, then monomial differentiation is performed and coefficients updated with integer factors. In other case each of the coefficients is differentiated.
Additionally, for each of the symbols you can specify a single positive integer which will indicate the number of times to perform differentiation using this symbol.
>>> from sympy import *
>>> x,y,z = symbols('xyz')
>>> p = Poly(x**2*y + z**2, x, y)
>>> p.diff(x)
Poly(2*x*y, x, y)
>>> p.diff(x, 2)
Poly(2*y, x, y)
>>> p.diff(x, 2, y)
Poly(2, x, y)
>>> p.diff(z)
Poly(2*z, x, y)
Efficiently integrate polynomials.
Integrate a polynomial with respect a set of symbols. If a symbol is polynomial’s variable, then monomial integration is performed and coefficients updated with integer factors. In other case each of the coefficients is integrated.
Additionally, for each of the symbols you can specify a single positive integer which will indicate the number of times to perform integration using this symbol.
>>> from sympy import *
>>> x,y,z = symbols('xyz')
>>> p = Poly(x**2*y + z**2, x, y)
>>> p.integrate(x)
Poly(1/3*x**3*y + z**2*x, x, y)
>>> p.integrate(x, 2)
Poly(1/12*x**4*y + z**2/2*x**2, x, y)
>>> p.integrate(x, 2, y)
Poly(1/24*x**4*y**2 + z**2/2*x**2*y, x, y)
>>> p.integrate(z)
Poly(z*x**2*y + z**3/3, x, y)
Returns True if ‘self’ is dense.
Let ‘self’ be a polynomial in M variables of a total degree N and having L terms (with non-zero coefficients). Let K be the number of monomials in M variables of degree at most N. Then ‘self’ is considered dense if it’s at least 90% filled.
The total number of monomials is given by (M + N)! / (M! N!), where the factorials are estimated using improved Stirling’s approximation:
n! = sqrt((2 n + 1/3) pi) n**n exp(-n)
For more information on this subject refer to:
http://mathworld.wolfram.com/StirlingsApproximation.html
Returns True if ‘self’ has no factors of multiplicity >= 2.
>>> from sympy import *
>>> x,y = symbols('xy')
>>> Poly(x-1, x).is_squarefree
True
>>> Poly((x-1)**2, x).is_squarefree
False
Apply a function to all the coefficients.
>>> from sympy import *
>>> x,y,u,v = symbols('xyuv')
>>> p = Poly(x**2 + 2*x*y, x, y)
>>> q = p.map_coeffs(lambda c: 2*c)
>>> q.as_basic()
4*x*y + 2*x**2
>>> p = Poly(u*x**2 + v*x*y, x, y)
>>> q = p.map_coeffs(expand, complex=True)
>>> q.as_basic()
x**2*(I*im(u) + re(u)) + x*y*(I*im(v) + re(v))
Efficiently subtract a single term from ‘self’.
The list of monomials is sorted at initialization time, this motivates usage of binary search algorithm to find an index of an existing monomial or a suitable place for a new one. This gives O(lg(n)) complexity, where ‘n’ is the initial number of terms, superior to naive approach.
For more information on the implemented algorithm refer to:
Unify ‘self’ with a polynomial or a set of polynomials.
This method will return polynomials of the same type, dominated by ‘self’, with a common set of symbols (which is a union of all symbols from all polynomials) and with common monomial ordering.
You can pass a polynomial or an expression to this method, or a list or a tuple of polynomials or expressions. If any of the inputs would be an expression then it will be converted to a polynomial.