Interface to FriCAS

Todo

  • some conversions in sage.functions are still missing and all should be checked and tested

FriCAS is a free GPL-compatible (modified BSD license) general purpose computer algebra system based on Axiom. The FriCAS website can be found at http://fricas.sourceforge.net/.

AUTHORS:

  • Mike Hansen (2009-02): Split off the FriCAS interface from the Axiom interface.
  • Martin Rubey, Bill Page (2016-08): Completely separate from Axiom, implement more complete translation from FriCAS to SageMath types.

EXAMPLES:

sage: fricas('3 * 5')                                                       # optional - fricas
15
sage: a = fricas(3) * fricas(5); a                                          # optional - fricas
15

The type of a is FriCASElement, i.e., an element of the FriCAS interpreter:

sage: type(a)                                                               # optional - fricas
<class 'sage.interfaces.fricas.FriCASElement'>
sage: a.parent()                                                            # optional - fricas
FriCAS

The underlying FriCAS type of a is also available, via the type method:

sage: a.typeOf()                                                            # optional - fricas
PositiveInteger

FriCAS objects are normally displayed using “ASCII art”:

sage: fricas(2/3)                                                           # optional - fricas
  2
  -
  3
sage: fricas('x^2 + 3/7')                                                   # optional - fricas
   2   3
  x  + -
       7

Functions defined in FriCAS are available as methods of the fricas object:

sage: F = fricas.factor('x^5 - y^5'); F                                     # optional - fricas
           4      3    2 2    3     4
- (y - x)(y  + x y  + x y  + x y + x )
sage: type(F)                                                               # optional - fricas
<class 'sage.interfaces.fricas.FriCASElement'>
sage: F.typeOf()                                                            # optional - fricas
Factored(Polynomial(Integer))

We can also create a FriCAS polynomial and apply the function factor from FriCAS. The notation f.factor() is consistent with how the rest of SageMath works:

sage: f = fricas('x^5 - y^5')                                               # optional - fricas
sage: f^2                                                                   # optional - fricas
 10      5 5    10
y   - 2 x y  + x
sage: f.factor()                                                            # optional - fricas
           4      3    2 2    3     4
- (y - x)(y  + x y  + x y  + x y + x )

For many FriCAS types, translation to an appropriate SageMath type is available:

sage: f.factor().sage()                                                     # optional - fricas
(y - x) * (y^4 + y^3*x + y^2*x^2 + y*x^3 + x^4)

Control-C interruption works well with the FriCAS interface. For example, try the following sum but with a much bigger range, and hit control-C:

sage:  f = fricas('(x^5 - y^5)^10000')                                      # not tested - fricas
Interrupting FriCAS...
...
KeyboardInterrupt: Ctrl-c pressed while running FriCAS

Let us demonstrate some features of FriCAS. FriCAS can guess a differential equation for the generating function for integer partitions:

sage: fricas("guessADE([partition n for n in 0..40], homogeneous==4)")      # optional - fricas
[
  [
      n
    [x ]f(x):
           2    3 (iv)           2    2 ,              3  ,,,
          x f(x) f    (x) + (20 x f(x) f (x) + 5 x f(x) )f   (x)

        +
                2    2 ,,   2
          - 39 x f(x) f  (x)

        +
               2     ,   2            2 ,            3  ,,         2 ,   4
          (12 x f(x)f (x)  - 15 x f(x) f (x) + 4 f(x) )f  (x) + 6 x f (x)

        +
                    ,   3          2 ,   2
          10 x f(x)f (x)  - 16 f(x) f (x)

      =
        0
    ,
                     2      3      4
   f(x) = 1 + x + 2 x  + 3 x  + O(x )]
  ]

FriCAS can solve linear ordinary differential equations:

sage: fricas.set("y", "operator y")                                         # optional - fricas
sage: fricas.set("deq", "x^3*D(y x, x, 3) + x^2*D(y x, x, 2) - 2*x*D(y x, x) + 2*y x - 2*x^4")  # optional - fricas
sage: fricas.set("sol", "solve(deq, y, x)"); fricas("sol")                  # optional - fricas
               5       3       2
              x  - 10 x  + 20 x  + 4
[particular = ----------------------,
                       15 x
             3      2       3       3      2
          2 x  - 3 x  + 1  x  - 1  x  - 3 x  - 1
 basis = [---------------, ------, -------------]]
                 x            x          x

sage: fricas("sol.particular").sage()                                       # optional - fricas
1/15*(x^5 - 10*x^3 + 20*x^2 + 4)/x
sage: fricas("sol.basis").sage()                                            # optional - fricas
[(2*x^3 - 3*x^2 + 1)/x, (x^3 - 1)/x, (x^3 - 3*x^2 - 1)/x]
sage: fricas.eval(")clear values y deq sol")                                # optional - fricas
''

FriCAS can expand expressions into series:

sage: x = var('x'); ex = sqrt(cos(x)); a = fricas(ex).series(x=0); a        # optional - fricas
    1  2    1  4    19   6     559   8     29161    10      11
1 - - x  - -- x  - ---- x  - ------ x  - --------- x   + O(x  )
    4      96      5760      645120      116121600

sage: a.coefficients()[38].sage()                                           # optional - fricas
-29472026335337227150423659490832640468979/274214482066329363682430667508979749984665600000000

sage: ex = sqrt(atan(x)); a = fricas(ex).series(x=0); a                     # optional - fricas
 1      5        9
 -      -        -
 2   1  2    31  2      6
x  - - x  + --- x  + O(x )
     6      360

sage: a.coefficient(9/2).sage()                                             # optional - fricas
31/360

sage: x = fricas("x::TaylorSeries Fraction Integer")                        # optional - fricas
sage: y = fricas("y::TaylorSeries Fraction Integer")                        # optional - fricas
sage: 2*(1+2*x+sqrt(1-4*x)-2*x*y).recip()                                   # optional - fricas
              2       3     2 2      3       4        4        5
  1 + (x y + x ) + 2 x  + (x y  + 2 x y + 6 x ) + (4 x y + 18 x )
+
    3 3      4 2       5        6        5 2       6         7
  (x y  + 3 x y  + 13 x y + 57 x ) + (6 x y  + 40 x y + 186 x )
+
    4 4      5 3       6 2        7         8
  (x y  + 4 x y  + 21 x y  + 130 x y + 622 x )
+
      6 3       7 2        8          9
  (8 x y  + 66 x y  + 432 x y + 2120 x )
+
    5 5      6 4       7 3        8 2         9          10
  (x y  + 5 x y  + 30 x y  + 220 x y  + 1466 x y + 7338 x  ) + O(11)

FriCAS does some limits right:

sage: x = var('x'); ex = x^2*exp(-x)*Ei(x) - x; fricas(ex).limit(x=oo)      # optional - fricas
1
class sage.interfaces.fricas.FriCAS(name='fricas', command='fricas -nosman', script_subdirectory=None, logfile=None, server=None, server_tmpdir=None)

Bases: sage.interfaces.tab_completion.ExtraTabCompletion, sage.interfaces.expect.Expect

Interface to a FriCAS interpreter.

console()

Spawn a new FriCAS command-line session.

EXAMPLES:

sage: fricas.console()                                              # not tested
                 FriCAS (AXIOM fork) Computer Algebra System
                        Version: FriCAS 1.0.5
         Timestamp: Thursday February 19, 2009 at 06:57:33
-----------------------------------------------------------------------------
   Issue )copyright to view copyright notices.
   Issue )summary for a summary of useful system commands.
   Issue )quit to leave AXIOM and return to shell.
-----------------------------------------------------------------------------
eval(code, strip=True, synchronize=False, locals=None, allow_use_file=True, split_lines='nofile', reformat=True, **kwds)

Evaluate code using FriCAS.

Except reformat, all arguments are passed to sage.interfaces.expect.Expect.eval().

INPUT:

  • reformat – bool; remove the output markers when True.

This can also be used to pass system commands to FriCAS.

EXAMPLES:

sage: fricas.set("x", "1783"); fricas("x")                          # optional - fricas
1783
sage: fricas.eval(")cl val x");                                     # optional - fricas
''
sage: fricas("x")                                                   # optional - fricas
x
get(var)

Get the string representation of the value (more precisely, the OutputForm) of a variable or expression in FriCAS.

If FriCAS cannot evaluate \(var\) an error is raised.

EXAMPLES:

sage: fricas.set('xx', '2')                                         # optional - fricas
sage: fricas.get('xx')                                              # optional - fricas
'2'
sage: a = fricas('(1 + sqrt(2))^5')                                 # optional - fricas
sage: fricas.get(a.name())                                          # optional - fricas
'    +-+\n29 \\|2  + 41'
sage: fricas.get('(1 + sqrt(2))^5')                                 # optional - fricas
'    +-+\n29 \\|2  + 41'
sage: fricas.new('(1 + sqrt(2))^5')                                 # optional - fricas
    +-+
29 \|2  + 41
get_InputForm(var)

Return the InputForm as a string.

get_boolean(var)

Return the value of a FriCAS boolean as a boolean, without checking that it is a boolean.

get_integer(var)

Return the value of a FriCAS integer as an integer, without checking that it is an integer.

get_string(var)

Return the value of a FriCAS string as a string, without checking that it is a string.

get_unparsed_InputForm(var)

Return the unparsed InputForm as a string.

Todo

  • catch errors, especially when InputForm is not available:

    • for example when integration returns "failed"
    • UnivariatePolynomial
  • should we provide workarounds, too?

set(var, value)

Set a variable to a value in FriCAS.

INPUT:

  • var, value: strings, the first representing a valid FriCAS variable identifier, the second a FriCAS expression.

OUTPUT: None

EXAMPLES:

sage: fricas.set('xx', '2')                                         # optional - fricas
sage: fricas.get('xx')                                              # optional - fricas
'2'
class sage.interfaces.fricas.FriCASElement(parent, value, is_name=False, name=None)

Bases: sage.interfaces.expect.ExpectElement

Instances of this class represent objects in FriCAS.

Using the method sage() we can translate some of them to SageMath objects:

_sage_()

Convert self to a Sage object.

EXAMPLES:

Floats:

sage: fricas(2.1234).sage()                                         # optional - fricas
2.12340000000000
sage: _.parent()                                                    # optional - fricas
Real Field with 53 bits of precision
sage: a = RealField(100)(pi)                                        # optional - fricas
sage: fricas(a).sage()                                              # optional - fricas
3.1415926535897932384626433833
sage: _.parent()                                                    # optional - fricas
Real Field with 100 bits of precision
sage: fricas(a).sage() == a                                         # optional - fricas
True
sage: fricas(2.0).sage()                                            # optional - fricas
2.00000000000000
sage: _.parent()                                                    # optional - fricas
Real Field with 53 bits of precision

Algebraic numbers:

sage: a = fricas('(1 + sqrt(2))^5'); a                              # optional - fricas
    +-+
29 \|2  + 41
sage: b = a.sage(); b                                               # optional - fricas
82.0121933088198?
sage: b.radical_expression()                                        # optional - fricas
29*sqrt(2) + 41

Integers modulo n:

sage: fricas("((42^17)^1783)::IntegerMod(5^(5^5))").sage() == Integers(5^(5^5))((42^17)^1783) # optional - fricas
True

Matrices over a prime field:

sage: fricas("matrix [[1::PF 3, 2],[2, 0]]").sage().parent()        # optional - fricas
Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 3

We can also convert FriCAS’s polynomials to Sage polynomials:

sage: a = fricas(x^2 + 1); a.typeOf()                               # optional - fricas
Polynomial(Integer)
sage: a.sage()                                                      # optional - fricas
x^2 + 1
sage: _.parent()                                                    # optional - fricas
Univariate Polynomial Ring in x over Integer Ring
sage: fricas('x^2 + y^2 + 1/2').sage()                              # optional - fricas
y^2 + x^2 + 1/2
sage: _.parent()                                                    # optional - fricas
Multivariate Polynomial Ring in y, x over Rational Field

sage: fricas("1$Polynomial Integer").sage()                         # optional - fricas
1

sage: fricas("x^2/2").sage()                                        # optional - fricas
1/2*x^2

sage: x = polygen(QQ, 'x')
sage: fricas(x+3).sage()                                            # optional - fricas
x + 3
sage: fricas(x+3).domainOf()                                        # optional - fricas
Polynomial(Integer())

sage: fricas(matrix([[2,3],[4,x+5]])).diagonal().sage()             # optional - fricas
(2, x + 5)

sage: f = fricas("(y^2+3)::UP(y, INT)").sage(); f                   # optional - fricas
y^2 + 3
sage: f.parent()                                                    # optional - fricas
Univariate Polynomial Ring in y over Integer Ring

sage: fricas("(y^2+sqrt 3)::UP(y, AN)").sage()                      # optional - fricas
y^2 + 1.732050807568878?

Rational functions:

sage: fricas("x^2 + 1/z").sage()                                    # optional - fricas
x^2 + 1/z

Expressions:

sage: fricas(pi).sage()                                             # optional - fricas
pi

sage: fricas("sin(x+y)/exp(z)*log(1+%e)").sage()                    # optional - fricas
e^(-z)*log(e + 1)*sin(x + y)

sage: fricas("factorial(n)").sage()                                 # optional - fricas
factorial(n)

sage: fricas("integrate(sin(x+y), x=0..1)").sage()                  # optional - fricas
-cos(y + 1) + cos(y)

sage: fricas("integrate(x*sin(1/x), x=0..1)").sage()                # optional - fricas
'failed'

sage: fricas("integrate(sin((x^2+1)/x),x)").sage()                  # optional - fricas
integral(sin((x^2 + 1)/x), x)

Todo

  • Converting matrices and lists takes much too long.

Matrices:

sage: fricas("matrix [[x^n/2^m for n in 0..5] for m in 0..3]").sage()         # optional - fricas, long time
[      1       x     x^2     x^3     x^4     x^5]
[    1/2   1/2*x 1/2*x^2 1/2*x^3 1/2*x^4 1/2*x^5]
[    1/4   1/4*x 1/4*x^2 1/4*x^3 1/4*x^4 1/4*x^5]
[    1/8   1/8*x 1/8*x^2 1/8*x^3 1/8*x^4 1/8*x^5]

Lists:

sage: fricas("[2^n/x^n for n in 0..5]").sage()                      # optional - fricas, long time
[1, 2/x, 4/x^2, 8/x^3, 16/x^4, 32/x^5]

sage: fricas("[matrix [[i for i in 1..n]] for n in 0..5]").sage()   # optional - fricas, long time
[[], [1], [1 2], [1 2 3], [1 2 3 4], [1 2 3 4 5]]

Error handling:

sage: s = fricas.guessPade("[fibonacci i for i in 0..10]"); s       # optional - fricas
    n        x
[[[x ]- ----------]]
         2
        x  + x - 1
sage: s.sage()                                                      # optional - fricas
Traceback (most recent call last):
...
NotImplementedError: the translation of the FriCAS Expression 'rootOfADE' to sage is not yet implemented

sage: s = fricas("series(sqrt(1+x), x=0)"); s                       # optional - fricas
      1     1  2    1  3    5   4    7   5    21   6    33   7    429   8
  1 + - x - - x  + -- x  - --- x  + --- x  - ---- x  + ---- x  - ----- x
      2     8      16      128      256      1024      2048      32768
+
   715   9    2431   10      11
  ----- x  - ------ x   + O(x  )
  65536      262144

sage: s.sage()                                                      # optional - fricas
Traceback (most recent call last):
...
NotImplementedError: the translation of the FriCAS object

      1     1  2    1  3    5   4    7   5    21   6    33   7    429   8
  1 + - x - - x  + -- x  - --- x  + --- x  - ---- x  + ---- x  - ----- x
      2     8      16      128      256      1024      2048      32768
+
   715   9    2431   10      11
  ----- x  - ------ x   + O(x  )
  65536      262144

to sage is not yet implemented:
An error occurred when FriCAS evaluated 'unparse(...::InputForm)':

   Cannot convert the value from type Any to InputForm .
bool()

Coerce the expression into a boolean.

EXAMPLES:

sage: fricas("1=1").bool()                                          # optional - fricas
True
sage: fricas("1~=1").bool()                                         # optional - fricas
False
gen(n)

Return an error, since the n-th generator in FriCAS is not well defined.

class sage.interfaces.fricas.FriCASExpectFunction(parent, name)

Bases: sage.interfaces.expect.ExpectFunction

Translate the pythonized function identifier back to a FriCAS operation name.

class sage.interfaces.fricas.FriCASFunctionElement(object, name)

Bases: sage.interfaces.expect.FunctionElement

Make FriCAS operation names valid python function identifiers.

sage.interfaces.fricas.fricas_console()

Spawn a new FriCAS command-line session.

EXAMPLES:

sage: fricas_console()                                                  # not tested
                 FriCAS (AXIOM fork) Computer Algebra System
                            Version: FriCAS 1.0.5
             Timestamp: Thursday February 19, 2009 at 06:57:33
-----------------------------------------------------------------------------
   Issue )copyright to view copyright notices.
   Issue )summary for a summary of useful system commands.
   Issue )quit to leave AXIOM and return to shell.
-----------------------------------------------------------------------------
sage.interfaces.fricas.is_FriCASElement(x)

Return True if x is of type FriCASElement.

EXAMPLES:

sage: from sage.interfaces.fricas import is_FriCASElement               # optional - fricas
sage: is_FriCASElement(fricas(2))                                       # optional - fricas
True
sage: is_FriCASElement(2)                                               # optional - fricas
False
sage.interfaces.fricas.reduce_load_fricas()

Returns the FriCAS interface object defined in :sage.interfaces.fricas.

EXAMPLES:

sage: from sage.interfaces.fricas import reduce_load_fricas             # optional - fricas
sage: reduce_load_fricas()                                              # optional - fricas
FriCAS