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')
15
sage: a = fricas(3) * fricas(5); a
15

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

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

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

sage: a.typeOf()
PositiveInteger

FriCAS objects are normally displayed using “ASCII art”:

sage: fricas(2/3)
  2
  -
  3
sage: fricas('x^2 + 3/7')
   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
           4      3    2 2    3     4
- (y - x)(y  + x y  + x y  + x y + x )
sage: type(F)
<class 'sage.interfaces.fricas.FriCASElement'>
sage: F.typeOf()
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')
sage: f^2
 10      5 5    10
y   - 2 x y  + x
sage: f.factor()
           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()
(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
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)")
[
  [
      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")
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")
sage: fricas.set("sol", "solve(deq, y, x)"); fricas("sol")
               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()
1/15*(x^5 - 10*x^3 + 20*x^2 + 4)/x
sage: fricas("sol.basis").sage()
[(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")
''

FriCAS can expand expressions into series:

sage: x = var('x'); ex = sqrt(cos(x)); a = fricas(ex).series(x=0); a
    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()
-29472026335337227150423659490832640468979/274214482066329363682430667508979749984665600000000

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

sage: a.coefficient(9/2).sage()
31/360

sage: x = fricas("x::TaylorSeries Fraction Integer")
sage: y = fricas("y::TaylorSeries Fraction Integer")
sage: 2*(1+2*x+sqrt(1-4*x)-2*x*y).recip()
              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)
1
class sage.interfaces.fricas.FriCAS(name='fricas', command=None, script_subdirectory=None, logfile=None, server=None, server_tmpdir=None)#

Bases: ExtraTabCompletion, 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")
1783
sage: fricas.eval(")cl val x");
''
sage: fricas("x")
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')
sage: fricas.get('xx')
'2'
sage: a = fricas('(1 + sqrt(2))^5')
sage: fricas.get(a.name())
'    +-+\n29 \\|2  + 41'
sage: fricas.get('(1 + sqrt(2))^5')
'    +-+\n29 \\|2  + 41'
sage: fricas.new('(1 + sqrt(2))^5')
    +-+
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')
sage: fricas.get('xx')
'2'
class sage.interfaces.fricas.FriCASElement(parent, value, is_name=False, name=None)#

Bases: ExpectElement, FriCASElement

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()
2.12340000000000
sage: _.parent()
Real Field with 53 bits of precision
sage: a = RealField(100)(pi)
sage: fricas(a).sage()
3.1415926535897932384626433833
sage: _.parent()
Real Field with 100 bits of precision
sage: fricas(a).sage() == a
True
sage: fricas(2.0).sage()
2.00000000000000
sage: _.parent()
Real Field with 53 bits of precision

Algebraic numbers:

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

Integers modulo n:

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

Matrices over a prime field:

sage: fricas("matrix [[1::PF 3, 2],[2, 0]]").sage().parent()
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()
Polynomial(Integer)
sage: a.sage()
x^2 + 1
sage: _.parent()
Univariate Polynomial Ring in x over Integer Ring
sage: fricas('x^2 + y^2 + 1/2').sage()
y^2 + x^2 + 1/2
sage: _.parent()
Multivariate Polynomial Ring in y, x over Rational Field

sage: fricas("1$Polynomial Integer").sage()
1

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

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

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

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

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

Rational functions:

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

Expressions:

sage: fricas(pi).sage()
pi

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

sage: fricas("factorial(n)").sage()
factorial(n)

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

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

sage: fricas("integrate(sin((x^2+1)/x),x)").sage()
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()   # 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()                      # 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()   # 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
    n        x
[[[x ]- ----------]]
         2
        x  + x - 1
sage: s.sage()
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
      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()
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()
True
sage: fricas("1~=1").bool()
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: ExpectFunction

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

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

Bases: 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
sage: is_FriCASElement(2)
doctest:...: DeprecationWarning: the function is_FriCASElement is deprecated; use isinstance(x, sage.interfaces.abc.FriCASElement) instead
See https://github.com/sagemath/sage/issues/34804 for details.
False
sage: is_FriCASElement(fricas(2))
True
sage.interfaces.fricas.reduce_load_fricas()#

Return the FriCAS interface object defined in sage.interfaces.fricas.

EXAMPLES:

sage: from sage.interfaces.fricas import reduce_load_fricas
sage: reduce_load_fricas()
FriCAS