Lecture 12: Rational Functions and Conversions

One of the main problems in computer algebra is expression swell. In an exact calculation, expression swell happens when the numbers and or expressions grow exponentially in size.

A rational number is simplified immediately, but in rational expressions removing the greatest common denominator may not always result in a smaller expression and may even lead to expression swell. If we consider \(\frac{x^d - 1}{x - 1}\), for any degree \(d\) then the reason becomes clear.

Rational Expressions

SageMath recognizes rational expressions as of type fraction_field_elements of sage.rings.

x = polygen(QQ)
p = x^3 - 1; q = x^2 - 1; r = p/q
print(r, 'has type', type(r))

What is remarkable is that the rational expression is normalized automatically. The normalization of a rational expression is the removal of the greatest common divisor of numerator and denominator. We see that the common factor x - 1 is removed in r.

print(factor(p)); print(factor(q))

This automatic normalization may lead to expression swell.

f = (x^1000 - 1)/(x-1); print(f)

We see an expression with 1,000 terms! Instead of printing the entire expression, we may just as well only ask for just the number of terms. The problem is that f is still not a polynomial and we have to take its fraction before getting the number of coefficients.

fn = f.numerator()

We can freeze an expression, by conversion to a Symbolic Ring (SR).

g = SR(p)/q; print(g);

This shows the original expression (x^3 - 1)/(x^2 - 1). We can ask for the denominator, with or without normalization


The same for the numerator, with or without normalization


We may represent a rational expression with numerator and denominator in factored form, or not.

fn = g.numerator(normalize=False).factor()
fd = g.denominator(normalize=False).factor()

This prints (x^2 + x + 1)*(x - 1)/(x^2 - 1) and (x^3 - 1)/((x + 1)*(x - 1)).

Factoring or expanding the numerator, factoring or expanding the denominator defines four normal forms of rational expressions. If we really do not want to remove common factors, then we must explicitly convert to strings.

print(str(fn) + '/' + '(' + str(fd) + ')')

which shows (x^2 + x + 1)*(x - 1)/((x + 1)*(x - 1)).


The coercion to a symbolic ring SR on a polynomial shows the so-called Horner form of the polynomial.

x = SR.var('x')
p = x^4 + 2*x^3 + 3*x^2 + 4*x + 5

The Horner form gives an efficient way to evaluate a polynomial, which requires as many multiplications as additions, for the example polynomial p, the Horner form is (((x+2)*x + 3)*x + 4)*x + 5.

The disadvantage of working with x = polygen(QQ) is that we cannot select a random element from the ring.

P.<x> = PolynomialRing(CC)
rn = P.random_element(degree = 4)
rd = P.random_element(degree = 4)
f = rn/rd
print(f, 'has type', type(f))

To evaluate a rational expression efficiently, we may consider a partial fraction decomposition.



  1. Assign to r the expression (4*x^2 + x)/(x^2 + 4). Use the methods operands() and operator() to draw the expression tree of r.

  2. Consider the rational expression p = (x^4 + x^3 - 4*x^2-4*x)/(x^4 + x^3 - x^2 - x). What are the commands to transform p into (x + 2)*(x + 1)*(x - 2)/(x^3 + x^2 - x - 1)?

  3. What is the partial fraction decomposition of p = (x^4 + x^3 - 4*x^2-4*x)/(x^4 + x^3 - x^2 - x)?

  4. Consider the polynomial \(p\) in \(x\) with rational coefficients:

    \[p = \frac{1}{12} x^8 + \frac{93}{4} x^7 - x^6 + 2 x^5 + \frac{5}{39} x^4 + \frac{1}{5} x^3 + x^2 - \frac{1}{5} x + \frac{1}{2}.\]

    Without retyping \(p\), convert \(p\) into the polynomial

    \[q = \frac{1}{2} x^8 - \frac{1}{5} x^7 + x^6 + \frac{1}{5} x^5 + \frac{5}{39} x^4 + 2 x^3 - x^2 + \frac{93}{4} x + \frac{1}{12}.\]

    which has the same coefficients as \(p\), but swapped. If \(c_k\) is the coefficient with \(x^k\) in \(p\), then in \(q\), the monomial \(x^{8-k}\) has coefficient \(c_k\), for \(k = 0, 1, \ldots, 8\).