Lecture 15: Normalizing Expressions

Deciding whether two expressions represent the same mathematical object is an important problem in symbolic computation. We distinguish between the canonical and a normal form. Rewriting expressions using collection and sorting is a way of normalization. The quick, numerical, and probabilistic way to check whether two expressions are mathematically equivalent is by evaluation at a random point.

Normal and Canonical Form

Expressions can take many forms. We call two expressions to be equal if they are mathematically the same.

e1 = x*(1+y)
e2 = x + x*y
print('e1 = ', e1)
print('e2 = ', e2)
print('e1 == e1 : ', e1 == e2)

From the last print we see that the equality operator == does not evaluate to True or False. We could compare identities, but this would only result in True if both names would refer to the same object,

print(id(e1) == id(e2))

which is not the case as we see False printed. If we are comparing expressions, then we could compare operands and operators.

print(e1.operands(), e1.operator())
print(e2.operands(), e2.operator())

But then we see [x, y + 1] <built-in function mul> for e1 and [x*y, x] <built-in function add> for e2. At the data level, the two expressions are different.

What is we would take the difference of the two expressions?

print('e1 - e2 :', e1 - e2)

which then shows e1 - e2 : x*(y + 1) - x*y - x so Sage does not simplify. To check for equality, we can normalize, fully expand the expressions.

ee1 = e1.expand()
print('e1 expanded :', ee1)
print('e2 :', e2)
print('e1 expanded - e2 :', ee1 - e2)

Then the difference between the expanded e1 and the e2 leads to 0.

Expanding polynomials in several variables, for a given order of the variables and an order of the monomials, gives a normal form. Normalization is the process of bringing a mathematical expression in a normal form. For rational expressions we have seen that we can normalize numerator and denominator by removing common factors. A normal form is usually not unique, as we can then still represent numerator and denominator in expanded or factored form, which could lead to four different forms for the same expression.

The canonical form is the unique form of an expression. For polynomials in one variable, we can fully expand the polynomial, remove superfluous terms (like x - x) and then sort the monomials by degree in descending order (highest degree first).

Rewriting Multivariate Polynomials

For a multivariate polynomial, once we fix the order of the variables, we can order the terms lexicographically, first all monomials which contains the first variable, in order of degree in that variable, before all terms that do not contain the first variable.

R.<x,y,z> = PolynomialRing(QQ, order='lex')
p = R.random_element(degree=5, terms=20)

The default order is the degree lexicographic order, which places the terms with the highest degree first. Terms with the same degree are ordered lexicographically, first come the terms where the degree in the first variable is highest.

S.<x,y,z> = PolynomialRing(QQ, order='deglex')
q = S(p)
print(q, 'has type', type(q))

The statement S(p) converts the polynomial p into an object of the ring S where the terms are sorted in the degree lexicographical order.

We can rewrite the polynomial as a polynomial in one variable, where the coefficients are polynomials in the other variables. We illustrate this on a random polynomial.

P = QQ[x,y,z]
q = P.random_element(degree=5,terms=20)
print(q, 'has type', type(q))

We see that multivariate polynomials in Sage are implemented via libSINGULAR. SINGULAR is a computer algebra system for computational algebraic geometry.

We can write a polynomial in several variables recursively as a polynomial in one variable with its coefficients again polynomials in the same form.

To write the polynomial q as a polynomial in z, with coefficients polynomials in y that have coefficients in x, we do


This is another normal form for multivariate polynomials.

A Numerical Test on Equality

There is a numerical probability-one test n the equality of expressions. First we generate a random point, choosing random complex numbers for x and y.

rx = CC.random_element()
ry = CC.random_element()
print('a random point :', (rx, ry))

And then we evaluate the expressions.

v1 = e1(x=rx, y=ry)
v2 = e2(x=rx, y=ry)
print(v1 - v2)

we see 0.000000000000000 With probability one (we could have picked a point on some common factor of e1 - e2), a number (close to) zero will guarantee equality of expressions. We can increase our confidence in this test by generating more random points and increasing the working precision of the computations.


  1. Consider the polynomial \(p = (x^2 + x y + x + y) (x+y)\).

    1. Order the monomials in \(p\) in total degree order, using \(x > y\).

    2. Order the monomials in \(p\) in pure lexicographic order, using \(x > y\).

  2. Consider the polynomial \(p = (x^2 + x y + x + y) (x+y)\). Rewrite \(p\) as a polynomial in \(x\) with coefficients in \(y\).

  3. Consider the expressions \(x+1\) and \((x^2 - 1)/(x - 1)\).

    1. Are these expressions symbolically the same? Give the Sage commands to illustrate your answer.

    2. Verify the equality numerically. Show how a numerical equality test can go wrong.