This notebook is licenced under CC BY-SA 4.0.

FriCAS Tutorial (First Steps)

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"

General remarks

FriCAS is a typed computer algebra system.

In this notebook we evaluate certain expressions. Each expression is evaluated and yields a value that belongs to a certain type. This type is shown at the right hand side after the value.

Note that this type is a hyperlink that leads to its description.

By convention, types in FriCAS begin with a capital letter. Functions and variables are usually start with a small letter.

FriCAS as a Pocket Calculator

Type an expression into a cell and press SHIFT-ENTER.

In [2]:
1+1
Out[2]:
\[ 2 \]
In [3]:
1-1
Out[3]:
\[ 0 \]
In [4]:
-1
Out[4]:
\[ -1 \]

Everything in FriCAS has a type.

The FriCAS interpreter guesses the most appropriate type. For simple things types do not matter much.

Apart from the above integer domains, there are rational numbers and floating point numbers, complex integers, complex floats, etc.

In [5]:
2/3
Out[5]:
\[ \frac{2}{3} \]
In [6]:
3/6
Out[6]:
\[ \frac{1}{2} \]
In [7]:
2/3 + 1/5 * (- 2/11)
Out[7]:
\[ \frac{104}{165} \]
In [8]:
2^10
Out[8]:
\[ 1024 \]

FriCAS can compute with arbitrarily big numerical values. Only memory and time set the limits.

In [9]:
factorial(100)
Out[9]:
\[ 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 \]

By default 20 digits are used for floating point computation.

In [10]:
2/3 + 1.0
Out[10]:
\[ 1.6666666666666666667 \]

By using the digits function, one can query and set the number of digits that are used during the computation.

By default, a fraction is expressed in the most appropriate domain, which would be Fraction(Integer). Coercion into the appropriate type is done via the :: operator.

In [14]:
digits 40
22/7 :: Float
digits(20)
22/7 :: Float
Out[14]:
\[ 20 \]
Out[14]:
\[ 3.142857142857142857142857142857142857143 \]
Out[14]:
\[ 40 \]
Out[14]:
\[ 3.1428571428571428571 \]

A number that contains a dot, is automatically a member of the floating point domain. So the following fraction is computed within the respective domain, namely, Float.

In [15]:
22/7.0
Out[15]:
\[ 3.1428571428571428571 \]
In [16]:
sqrt(2.0)
Out[16]:
\[ 1.4142135623730950488 \]

For integer arguments, it is not clear what the user actually wants. So the expression remains unevaluated. Note that we get AlgebraicNumber as yet another number type.

In [17]:
sqrt(3)
Out[17]:
\[ \sqrt{3} \]

In fact, AlgebraicNumber is a step into the direction of computing with symbols rather than just numbers.

In [18]:
sqrt(2)+sqrt(3)
Out[18]:
\[ \sqrt{3}+\sqrt{2} \]

FriCAS can also deal with complex numbers. The symbol %i denotes the imaginary unit. Here we have Gaussian integers.

In [19]:
2+3*%i
Out[19]:
\[ 2+3\, i \]

As with integers, the resulting type is appropriately chosen, i.e. "complex rational numbers".

In [20]:
(2 + 3*%i) / (3 + 5*%i)
Out[20]:
\[ \frac{21}{34}-\frac{1}{34}\, i \]

As with real numbers where Float is the corresponding domain in FriCAS, complex numbers can only be modelled approximately on a computer.

In [21]:
sqrt(2.0) * (1 + 2.0*%i)
Out[21]:
\[ 1.4142135623730950488+2.8284271247461900976\, i \]

Not everything can be done with floating point numbers. FriCAS does not automatically extend the number domain from real numbers to complex numbers. It rather considers Float as a domain with partial operations, i.e. operations that might fail on certain input.

In [22]:
sqrt(5.0)
Out[22]:
\[ 2.2360679774997896964 \]
In [21]:
sqrt(-5.0)
   >> Error detected within library code:
   sqrt: negative argument: -5.0

But we can convert an algebraic number into a complex floating point number.

In [24]:
sqrt(-5)
sqrt(-5) :: Complex(Float)
Out[24]:
\[ \sqrt{-5} \]
Out[24]:
\[ 2.2360679774997896964\, i \]

There are a lot of functions that can be used with floating point numbers.

Note that in the following list % is an abbreviation of "this domain", i.e. Float in our case. The question mark is just used as a placeholder for an argument.

In [23]:
)show Float
 Float is a domain constructor.
 Abbreviation for Float is FLOAT 
 This constructor is exposed in this frame.
 127 Names for 171 Operations in this Domain.
------------------------------- Operations --------------------------------
 ?*? : (Integer, %) -> %               ?*? : (PositiveInteger, %) -> %
 ?*? : (%, %) -> %                     ?+? : (%, %) -> %
 ?-? : (%, %) -> %                     -? : % -> %
 ?/? : (%, Integer) -> %               ?/? : (%, %) -> %
 ?<? : (%, %) -> Boolean               ?<=? : (%, %) -> Boolean
 ?=? : (%, %) -> Boolean               ?>? : (%, %) -> Boolean
 ?>=? : (%, %) -> Boolean              D : (%, NonNegativeInteger) -> %
 D : % -> %                            OMwrite : (%, Boolean) -> String
 OMwrite : % -> String                 1 : () -> %
 0 : () -> %                           ?^? : (%, Integer) -> %
 ?^? : (%, PositiveInteger) -> %       ?^? : (%, %) -> %
 abs : % -> %                          acos : % -> %
 acosh : % -> %                        acot : % -> %
 acoth : % -> %                        acsc : % -> %
 acsch : % -> %                        annihilate? : (%, %) -> Boolean
 antiCommutator : (%, %) -> %          asec : % -> %
 asech : % -> %                        asin : % -> %
 asinh : % -> %                        associates? : (%, %) -> Boolean
 associator : (%, %, %) -> %           atan : (%, %) -> %
 atan : % -> %                         atanh : % -> %
 base : () -> PositiveInteger          bits : () -> PositiveInteger
 ceiling : % -> %                      coerce : % -> DoubleFloat
 coerce : % -> OutputForm              coerce : Fraction(Integer) -> %
 coerce : Integer -> %                 coerce : % -> %
 commutator : (%, %) -> %              convert : % -> DoubleFloat
 convert : % -> Float                  convert : % -> InputForm
 convert : % -> Pattern(Float)         convert : % -> String
 convert : DoubleFloat -> %            cos : % -> %
 cosh : % -> %                         cot : % -> %
 coth : % -> %                         csc : % -> %
 csch : % -> %                         differentiate : % -> %
 digits : () -> PositiveInteger        exp : % -> %
 exp1 : () -> %                        exponent : % -> Integer
 factor : % -> Factored(%)             float : (Integer, Integer) -> %
 floor : % -> %                        fractionPart : % -> %
 gcd : List(%) -> %                    gcd : (%, %) -> %
 hash : % -> SingleInteger             inv : % -> %
 latex : % -> String                   lcm : List(%) -> %
 lcm : (%, %) -> %                     log : % -> %
 log10 : % -> %                        log10 : () -> %
 log2 : % -> %                         log2 : () -> %
 mantissa : % -> Integer               max : (%, %) -> %
 min : (%, %) -> %                     negative? : % -> Boolean
 norm : % -> %                         normalize : % -> %
 nthRoot : (%, Integer) -> %           one? : % -> Boolean
 opposite? : (%, %) -> Boolean         order : % -> Integer
 outputFixed : () -> Void              outputFloating : () -> Void
 outputGeneral : () -> Void            pi : () -> %
 positive? : % -> Boolean              precision : () -> PositiveInteger
 prime? : % -> Boolean                 ?quo? : (%, %) -> %
 recip : % -> Union(%,"failed")        relerror : (%, %) -> %
 ?rem? : (%, %) -> %                   retract : % -> Fraction(Integer)
 retract : % -> Integer                round : % -> %
 sample : () -> %                      sec : % -> %
 sech : % -> %                         shift : (%, Integer) -> %
 sign : % -> Integer                   sin : % -> %
 sinh : % -> %                         sizeLess? : (%, %) -> Boolean
 smaller? : (%, %) -> Boolean          sqrt : % -> %
 squareFree : % -> Factored(%)         squareFreePart : % -> %
 tan : % -> %                          tanh : % -> %
 toString : % -> String                truncate : % -> %
 unit? : % -> Boolean                  unitCanonical : % -> %
 wholePart : % -> Integer              zero? : % -> Boolean
 ?~=? : (%, %) -> Boolean             
 ?*? : (Fraction(Integer), %) -> %
 ?*? : (NonNegativeInteger, %) -> %
 ?*? : (%, Fraction(Integer)) -> %
 OMwrite : (OpenMathDevice, %, Boolean) -> Void
 OMwrite : (OpenMathDevice, %) -> Void
 ?^? : (%, Fraction(Integer)) -> %
 ?^? : (%, NonNegativeInteger) -> %
 bits : PositiveInteger -> PositiveInteger
 characteristic : () -> NonNegativeInteger
 decreasePrecision : Integer -> PositiveInteger
 differentiate : (%, NonNegativeInteger) -> %
 digits : PositiveInteger -> PositiveInteger
 divide : (%, %) -> Record(quotient: %,remainder: %)
 euclideanSize : % -> NonNegativeInteger
 expressIdealMember : (List(%), %) -> Union(List(%),"failed")
 exquo : (%, %) -> Union(%,"failed")
 extendedEuclidean : (%, %) -> Record(coef1: %,coef2: %,generator: %)
 extendedEuclidean : (%, %, %) -> Union(Record(coef1: %,coef2: %),"failed")
 float : (Integer, Integer, PositiveInteger) -> %
 gcdPolynomial : (SparseUnivariatePolynomial(%), SparseUnivariatePolynomial(%)) -> SparseUnivariatePolynomial(%)
 hashUpdate! : (HashState, %) -> HashState
 increasePrecision : Integer -> PositiveInteger
 lcmCoef : (%, %) -> Record(llcm_res: %,coeff1: %,coeff2: %)
 leftPower : (%, NonNegativeInteger) -> %
 leftPower : (%, PositiveInteger) -> %
 leftRecip : % -> Union(%,"failed")
 multiEuclidean : (List(%), %) -> Union(List(%),"failed")
 outputFixed : NonNegativeInteger -> Void
 outputFloating : NonNegativeInteger -> Void
 outputGeneral : NonNegativeInteger -> Void
 outputSpacing : NonNegativeInteger -> Void
 patternMatch : (%, Pattern(Float), PatternMatchResult(Float,%)) -> PatternMatchResult(Float,%)
 precision : PositiveInteger -> PositiveInteger
 principalIdeal : List(%) -> Record(coef: List(%),generator: %)
 rationalApproximation : (%, NonNegativeInteger, NonNegativeInteger) -> Fraction(Integer)
 rationalApproximation : (%, NonNegativeInteger) -> Fraction(Integer)
 retractIfCan : % -> Union(Fraction(Integer),"failed")
 retractIfCan : % -> Union(Integer,"failed")
 rightPower : (%, NonNegativeInteger) -> %
 rightPower : (%, PositiveInteger) -> %
 rightRecip : % -> Union(%,"failed")
 subtractIfCan : (%, %) -> Union(%,"failed")
 toString : (%, NonNegativeInteger) -> String
 unitNormal : % -> Record(unit: %,canonical: %,associate: %)

FriCAS can be used as a numerical engine.

In [25]:
tan(sin(1.0)*exp(3.5))
Out[25]:
\[ -0.43301751741640407036 \]
In [26]:
log(abs(-3.0*sin(2*3.1415)))
Out[26]:
\[ -7.4948833967339587459 \]

Computation with symbols

Entering variables is as easy as in most other CAS.

In [27]:
p:=4*x^2+4*x+1
Out[27]:
\[ 4\, {x}^{2}+4\, x+1 \]

Note that the resulting domain of the factor operation is not Polynomial(Integer).

In [28]:
factor(p)
Out[28]:
\[ {\left(2\, x+1\right)}^{2} \]

The domain Factored(X) keeps its elements in a factored form as long as possible.

In [29]:
f:=factor(x^2-1)
Out[29]:
\[ \left(x-1\right)\, \left(x+1\right) \]
In [30]:
g:=f*(x-1)
Out[30]:
\[ {\left(x-1\right)}^{2}\, \left(x+1\right) \]
In [31]:
h:=g+1
Out[31]:
\[ {x}^{3}-{x}^{2}-x+2 \]
In [32]:
h-1
Out[32]:
\[ {\left(x-1\right)}^{2}\, \left(x+1\right) \]

Since in a rational function field any element is a unit, the concept of factorization does not do anything. But there is a function factorFraction that factors the numerator and denominator separately.

In [34]:
r := (x^2+2*x+1)/(x^2-9)
factor r
Out[34]:
\[ \frac{{x}^{2}+2\, x+1}{{x}^{2}-9} \]
Out[34]:
\[ \frac{{x}^{2}+2\, x+1}{{x}^{2}-9} \]
In [35]:
factorFraction r
Out[35]:
\[ \frac{{\left(x+1\right)}^{2}}{\left(x-3\right)\, \left(x+3\right)} \]

FriCAS knows about the standard mathematical functions and can do computations with them.

In [36]:
sin(x)*exp(x)
Out[36]:
\[ {e}^{x}\, \sin\left(x\right) \]

Some mathematical constants and their properties are built-in.

In [37]:
%e
Out[37]:
\[ e \]
In [38]:
%pi
Out[38]:
\[ \pi \]
In [39]:
%e^(%pi * %i)
Out[39]:
\[ -1 \]

Expressions vs. specific types

Eagerness of evaluation of an expression depends on its type. In fact, each type defines a certain normal form and the expression is simply stored in this form.

In [40]:
(x^100 + 1)*(x^100-1)
Out[40]:
\[ {x}^{200}-1 \]
In [41]:
(x^50-1)/(x-1)
Out[41]:
\[ {x}^{49}+{x}^{48}+{x}^{47}+{x}^{46}+{x}^{45}+{x}^{44}+{x}^{43}+{x}^{42}+{x}^{41}+{x}^{40}+{x}^{39}+{x}^{38}+{x}^{37}+{x}^{36}+{x}^{35}+{x}^{34}+{x}^{33}+{x}^{32}+{x}^{31}+{x}^{30}+{x}^{29}+{x}^{28}+{x}^{27}+{x}^{26}+{x}^{25}+{x}^{24}+{x}^{23}+{x}^{22}+{x}^{21}+{x}^{20}+{x}^{19}+{x}^{18}+{x}^{17}+{x}^{16}+{x}^{15}+{x}^{14}+{x}^{13}+{x}^{12}+{x}^{11}+{x}^{10}+{x}^{9}+{x}^{8}+{x}^{7}+{x}^{6}+{x}^{5}+{x}^{4}+{x}^{3}+{x}^{2}+x+1 \]

The domain Expression(Integer) is similar to the way expressions are stored in other computer algebra systems.

If FriCAS does not find a more appropriate interpretation for an expresssion, the expression often ends up in being of type Expression(Integer).

In [42]:
sin(%pi/3)
Out[42]:
\[ \frac{\sqrt{3}}{2} \]
In [43]:
e := sin(x)^2 + cos(x)^2
Out[43]:
\[ {\left(\sin\left(x\right)\right)}^{2}+{\left(\cos\left(x\right)\right)}^{2} \]

Some expressions are not automatically simplified.

In [44]:
simplify(e)
Out[44]:
\[ 1 \]

In some cases, it is simply not clear what a "simpler expression" actually means.

In [45]:
simplify(sin(2*x))
Out[45]:
\[ \sin\left(2\, x\right) \]
In [46]:
simplify(2*sin(x)*cos(x))
Out[46]:
\[ 2\, \cos\left(x\right)\, \sin\left(x\right) \]

Variables, Assignments, Equations, Declarations

In addition to accessing previoiusly computed values via the %%(n) mechanism, intermediate results can also be stored in variables.

In [47]:
a := 1/3 + 22/7
Out[47]:
\[ \frac{73}{21} \]
In [48]:
a
Out[48]:
\[ \frac{73}{21} \]

Assignment is done via :=. A simple = sign just creates an equation.

In [49]:
eq := A = 1/3 + 22/7
Out[49]:
\[ A=\frac{73}{21} \]

FriCAS is case-sensitive.

In [50]:
A
Out[50]:
\[ A \]
In [51]:
A = lhs(eq)
Out[51]:
\[ A=A \]

In order to see whether an equation is true or false, we must turn it into a boolean.

In [52]:
(A=lhs(eq))::Boolean
Out[52]:
\[ \texttt{true} \]
In [53]:
(A=10)::Boolean
Out[53]:
\[ \texttt{false} \]

The type of a variable can be declared in advance. Its type is then fixed for the whole session.

In [54]:
v: PositiveInteger
Out[54]:

If a variable has been declared, it is no longer automatically converted to an indeterminate that can be used in an expression.

In [53]:
5*v
 v is declared as being in PositiveInteger but has not been given a value.

One would have to prepend an apostroph to refer to a symbol of that name.

In [55]:
5 * 'v
Out[55]:
\[ 5\, v \]

Only values that belong to the respective type can be assigned to the variable.

In [55]:
v := -1
Compiling function G843 with type Integer -> Boolean 
Cannot convert right-hand side of assignment
- 1

to an object of the type PositiveInteger of the left-hand side.
In [56]:
v := 1
Out[56]:
\[ 1 \]

Functions

Functions, are first class objects, they can be assigned to variables and can be used as arguments of functions.

The operator +-> is used to created anonymous functions, i.e. lambda expressions.

In [57]:
f := x +-> sin(x)*exp(x)
Out[57]:
\[ x\mapsto \sin\left(x\right)\, \operatorname{exp}\left(x\right) \]
In [58]:
f(%pi/3)
Out[58]:
\[ \frac{\sqrt{3}\, {e}^{\frac{\pi }{3}}}{2} \]

Simply using an expression in a functional way makes no sense.

In [59]:
g := sin(x)*exp(x)
Out[59]:
\[ {e}^{x}\, \sin\left(x\right) \]
In [60]:
g(%pi/2)
There are no library operations named g 
Use HyperDoc Browse or issue
                                 )what op g
to learn if there is any operation containing " g " in its name.
Cannot find a definition or applicable library operation named g with 
argument type(s) 
                                     Pi
 
Perhaps you should use "@" to indicate the required return type, or "$" to 
specify which version of the function you need.
In [60]:
eval(g, x=%pi/2)
Out[60]:
\[ {e}^{\frac{\pi }{2}} \]

Declaring the type of a function explicitly, restricts the way the function can be used.

In [61]:
f1: Integer -> Integer
Out[61]:
In [62]:
f1 := x +-> gcd(x,x+6)
Out[62]:
\[ \operatorname{theMap}(anonymousFunction) \]
In [63]:
f1(18)
Out[63]:
\[ 6 \]

Since the functions has been declared for integer arguments, it cannot be used with floating point arguments.

In [65]:
f1(2.3)
There are no library operations named f1 
Use HyperDoc Browse or issue
                                 )what op f1
to learn if there is any operation containing " f1 " in its name.
Cannot find a definition or applicable library operation named f1 with 
argument type(s) 
                                    Float
 
Perhaps you should use "@" to indicate the required return type, or "$" to 
specify which version of the function you need.

Functions can also be defined via the "delayed assignment" using ==.

In [64]:
f2(x: Integer): String == reverse(string(x))
Function declaration f2 : Integer -> String has been added to workspace.
Out[64]:

Functions will automatically be compiled to machine code, the first time they are used.

In [65]:
f2(123453)
Compiling function f2 with type Integer -> String 
Out[65]:
\[ \texttt{"354321"} \]

The function cannot be applied to strings, because that would require the string to be converted into an integer.

In [68]:
f2("FriCAS")
Conversion failed in the compiled user function f2 .
Cannot convert the value from type String to Integer .

Multivariate function definitions are no problem.

In [66]:
f3(x,y,z) == (x+2*y)*z
Out[66]:
In [67]:
f3(3,4,2)
Compiling function f3 with type (PositiveInteger, PositiveInteger, 
PositiveInteger) -> PositiveInteger 
Out[67]:
\[ 22 \]
In [68]:
f3(3.2,4.1,2)
Compiling function f3 with type (Float, Float, PositiveInteger) -> Float 
Out[68]:
\[ 22.8 \]

Note that by not specifying the types in the definition of f3 above, FriCAS will use the definition as a pattern to define functions with appropriate types when the function is first called. These functions have the same name, but due to different input/output type are, in fact, different. Not so different here, because the follow the same pattern. However, in the programming language SPAD that comes with FriCAS, it is, allowed and common to have functions with the same name/identifier that have different input/output types. That is known as function overloading.