This notebook is licenced under CC BY-SA 4.0.

FriCAS Tutorial (Laurent Polynomial)

Ralf Hemmecke <ralf@hemmecke.org>

Sources at Github.

In [1]:
)set message type off
)set output algebra off
setFormat!(FormatMathJax)$JFriCASSupport
)set message type on
In [2]:
)version
Value = "FriCAS a9422d32eb6f6b03d98ac6adfc9bf05ec0f5e8bd compiled at Sa 03 Apr 2021 00:29:13 CEST"

Builtin LaurentPolynomial constructor

There is a builtin constructor for univariate (not multivariate) Laurent polynomials. See also the fricas-devel mailing list for a discussion of its problems.

Below we construct univariate Laurent polynomials (in x) over $\mathbb{Q}$.

In [8]:
Z ==> Integer
Q ==> Fraction Z
P ==> UnivariatePolynomial('x, Q)
R ==> Fraction P
L ==> LaurentPolynomial(Q, P)
px := x :: P
lx := x :: L
Out[8]:
Out[8]:
Out[8]:
Out[8]:
Out[8]:
Out[8]:
\[ x \]
Out[8]:
\[ x \]

Unfortunately, since exponentiation with a negative power is not implemented (in general the result is not a Laurent polynomial), we cannot simply enter a Laurent polynomial like this.

In [9]:
l := lx^(-3)+5*lx^(-1)+7+lx^2
Out[9]:
\[ \frac{{x}^{6}+7\, {x}^{4}+5\, {x}^{3}+x}{{x}^{4}} \]

There is, however, an operation that can create a negative power of the variable. In fact, there are two.

In [11]:
ix := divide(1,lx).quotient
ix := monomial(1, -1)$L

We can enter the above polynomial like this.

In [13]:
m(x,y) ==> monomial(x, y)$L
l := m(1,-3) + m(5,-1) + 7 + m(1,2)
Out[13]:
Out[13]:
\[ {x}^{2}+7+5\, {x}^{-1}+{x}^{-3} \]

On can also create Laurent polynomials by the following function.

In [14]:
separate((11+px+px^10)/(px^3*(1-px)))$L
Out[14]:
\[ \left[polyPart=-{x}^{6}-{x}^{5}-{x}^{4}-{x}^{3}-{x}^{2}-x-1+12\, {x}^{-1}+12\, {x}^{-2}+11\, {x}^{-3}, fracPart=-\frac{13}{x-1}\right] \]

The above $l$ might then be written as follows.

In [15]:
l := (separate((1+5*px^2+7*px^3+px^5)/px^3)$L).polyPart
Out[15]:
\[ {x}^{2}+7+5\, {x}^{-1}+{x}^{-3} \]

We also want to truncate a Laurent series at a certain point and optain a Laurent polynomial.

In [18]:
S ==> UnivariateLaurentSeries(Q, 'x, 0)
sx := x :: S
s := (sx^3-7*sx)/(3*sx^7-sx^4)
Out[18]:
Out[18]:
\[ x \]
Out[18]:
\[ 7\, {x}^{-3}-{x}^{-1}+21-3\, {x}^{2}+63\, {x}^{3}-9\, {x}^{5}+189\, {x}^{6}+O\left({x}^{8}\right) \]
In [19]:
rf := rationalFunction(s, 3)
Out[19]:
\[ \frac{63\, {x}^{6}-3\, {x}^{5}+21\, {x}^{3}-{x}^{2}+7}{{x}^{3}} \]

rationalFunction leads to a more general type than we want. Therefore, we simply try to coerce/retract it into the domain we want, i.e., a quotient field of univariate polynomials.

In [20]:
pf := rf :: R
Out[20]:
\[ \frac{63\, {x}^{6}-3\, {x}^{5}+21\, {x}^{3}-{x}^{2}+7}{{x}^{3}} \]
In [21]:
(separate(pf)$L).polyPart
Out[21]:
\[ 63\, {x}^{3}-3\, {x}^{2}+21-{x}^{-1}+7\, {x}^{-3} \]

Putting the above into a macro, we can make truncation a little simpler.

In [23]:
TRUNC(s, n) ==> (separate(rationalFunction(s, n)::R)$L).polyPart
TRUNC(s, 3)
Out[23]:
Out[23]:
\[ 63\, {x}^{3}-3\, {x}^{2}+21-{x}^{-1}+7\, {x}^{-3} \]

Univariate case

For (internal) computation, one can also be more direct and create a monoid ring with the monoid being the integers and the coefficients being the rational numbers. Clearly, also this is a representation of a Laurent polynomial ring.

In [24]:
QZ ==> PolynomialRing(Q, Z)
Out[24]:

Unfortunately, entering an element of this domain is a bit tricky and also the output does not look as one would expect from a Laurent polynomial.

Our example polynomial from above would look like this.

In [28]:
qx := monomial(1, 1)$QZ
iqx := monomial(1, -1)$QZ
l := iqx^3 + 5*iqx + 7 + qx^2
l^2
Out[28]:
\[ 1 \]
Out[28]:
\[ -1 \]
Out[28]:
\[ 2+7\, 0+5\, \left(-1\right)-3 \]
Out[28]:
\[ 4+14\, 2+49\, 0+35\, \left(-1\right)+7\, \left(-3\right) \]

That is certainly hard to interpret, but one can easily add a new domain that behaves like integers, but prints in a multiplicative form.

In the following we escape to the shell to create a file with the contents shown below. This will only work in a jFriCAS (i.e., the Jupyter notebook frontend for FriCAS). If you are working with another frontend, then simply create a file /tmp/xint.spad with the contents given between the lines "cat <<'EOF' > /tmp/xint.spad" and "EOF".

In [16]:
)!
cat <<'EOF' > /tmp/xint.spad
)abbrev domain XINT XInteger
XInteger(sy: Symbol): IntegerNumberSystem == Integer add
  coerce(x: %): OutputForm ==
    o := sy :: OutputForm
    one? x => o
    i: Integer := x pretend Integer
    x >= 0 => o^(i::OutputForm)
    o^paren(i::OutputForm)
EOF

Now we compile that file. Since FriCAS resets certain variables during compilation, we must reset the output format to MathJax.

In [30]:
)compile /tmp/xint.spad
setFormat!(FormatMathJax)$JFriCASSupport
Compiling FriCAS source code from file /tmp/xint.spad using old system 
compiler.
 XINT abbreviates domain XInteger 
Adding (Symbol) modemaps
------------------------------------------------------------------------
   initializing NRLIB XINT for XInteger 
   compiling into NRLIB XINT 
Adding $ modemaps
Adding (OutputForm) modemaps
   compiling exported coerce : $ -> OutputForm
Adding (Boolean) modemaps
Adding (Integer) modemaps
Time: 0.02 SEC.
(time taken in buildFunctor:  2363)
;;;     ***       |XInteger| REDEFINED
;;;     ***       |XInteger| REDEFINED
Time: 0.01 SEC.
   Cumulative Statistics for Constructor XInteger
      Time: 0.03 seconds
--------------non extending category----------------------
.. XInteger(#1) of cat 
(|IntegerNumberSystem|)   has no 
(|LinearlyExplicitOver| (|Integer|))    finalizing NRLIB XINT 
   Processing XInteger for Browser database:
--->-->XInteger(): Missing Description
; compiling file "/home/hemmecke/backup/git/fricas-notebooks/tmp/XINT.NRLIB/XINT.lsp" (written 03 APR 2021 01:13:45 AM):
; wrote /home/hemmecke/backup/git/fricas-notebooks/tmp/XINT.NRLIB/XINT.fasl
; compilation finished in 0:00:00.024
------------------------------------------------------------------------
 XInteger is now explicitly exposed in frame initial 
XInteger will be automatically loaded when needed from 
/home/hemmecke/backup/git/fricas-notebooks/tmp/XINT.NRLIB/XINT
Out[30]:

Using XInteger instead of Integer, the output looks more pleasing.

In [35]:
QZ ==> PolynomialRing(Q, XInteger("x"::Symbol))
qx := monomial(1, 1)$QZ
iqx := monomial(1, -1)$QZ
l := iqx^3 + 5*iqx + 7 + qx^2
l^2
Out[35]:
Out[35]:
\[ x \]
Out[35]:
\[ {x}^{\left(-1\right)} \]
Out[35]:
\[ {x}^{2}+7\, {x}^{0}+5\, {x}^{\left(-1\right)}+{x}^{\left(-3\right)} \]
Out[35]:
\[ {x}^{4}+14\, {x}^{2}+49\, {x}^{0}+35\, {x}^{\left(-1\right)}+7\, {x}^{\left(-3\right)} \]

Truncation of the above Laurent series at degree 3 goes as follows.

In [36]:
tr(s: S, n: Z): QZ ==
  qz: QZ := 0
  while (e := order(s, n+1)) <= n repeat
    qz := qz + monomial(leadingCoefficient s, e)$QZ
    s := reductum s
  qz
Function declaration tr : (UnivariateLaurentSeries(Fraction(Integer),x,0), 
Integer) -> PolynomialRing(Fraction(Integer),XInteger(x)) has been added to 
workspace.
Out[36]:
In [37]:
tr(s,3)
Compiling function tr with type (UnivariateLaurentSeries(Fraction(Integer),x,
0), Integer) -> PolynomialRing(Fraction(Integer),XInteger(x)) 
Out[37]:
\[ 63\, {x}^{3}-3\, {x}^{2}+21\, {x}^{0}-{x}^{\left(-1\right)}+7\, {x}^{\left(-3\right)} \]

Or construct the Laurent polynomial like this. Note that we must use the complete function to expand the (finite) stream such that it is recognized as finite by the entries function.

In [39]:
mons := [monomial(r.c,r.k)$QZ for r in terms s while r.k < 4];
reduce(+, entries complete mons)
Out[39]:
\[ 63\, {x}^{3}-3\, {x}^{2}+21\, {x}^{0}-{x}^{\left(-1\right)}+7\, {x}^{\left(-3\right)} \]