12. Computação simbólica

12.1. SymPy

Nesta seção, apresentamos algumas funcionalidades básicas da biblioteca SymPy (SYMbolic Python). Em contraste com a computação numérica (envolvendo números), no cálculo simbólico estamos processando e transformando variáveis genéricas.

A página inicial do SymPy (http://sympy.org/) fornece a documentação completa (e atualizada) para esta biblioteca.

A computação simbólica é muito lento em comparação com as operações em ponto flutuante e, assim, geralmente não muito adequada para simulação direta. No entanto, é uma ferramenta poderosa no suporte à preparação do código e trabalho simbólico. Ocasionalmente, usamos operações simbólicas em simulações para elaborar o código numérico mais eficiente, antes que seja executado.

12.1.1. Saída

Antes de começarmos a usar o SymPy, chamaremos init_printing. Isto diz ao SymPy para mostrar as expressões em um formato mais conveniente.

import sympy
sympy.init_printing()

12.1.2. Símbolos

Antes de começarmos a executar qualquer operação simbólica, precisamos criar variáveis simbólicas usando a função Symbol do SymPy:

from sympy import Symbol
x = Symbol('x')
type(x)
sympy.core.symbol.Symbol
y = Symbol('y')
2 * x - x
_images/12-computacao-simbolica_4_0.png
x + y + x + 10*y
_images/12-computacao-simbolica_5_0.png
y + x - y + 10
_images/12-computacao-simbolica_6_0.png

Podemos abreviar a criação de múltiplas variáveis simbólicas usando a função symbols. Por exemplo, para criar as variáveis simbólicas x, y e z, podemos usar

import sympy
x, y, z = sympy.symbols('x,y,z')
x + 2*y + 3*z - x
_images/12-computacao-simbolica_8_0.png

Uma vez que terminarmos a manipulação dos termos, às vezes desejamos inserir números para as variáveis. Isso pode ser feito usando o método subs.

from sympy import symbols
x, y = symbols('x,y')
x + 2*y
_images/12-computacao-simbolica_10_0.png
x + 2*y.subs(x, 10)
_images/12-computacao-simbolica_11_0.png
(x + 2*y).subs(x, 10).subs(y, 3)
_images/12-computacao-simbolica_12_0.png
(x + 2*y).subs({x:10, y:3})
_images/12-computacao-simbolica_13_0.png

Também podemos substituir uma variável simbólica por outra, como neste exemplo, em que y é substituído por x antes de substituirmos x pelo número 2.

termo = 3*x + y**2
termo
_images/12-computacao-simbolica_15_0.png
termo.subs(x, y)
_images/12-computacao-simbolica_16_0.png
termo.subs(x, y).subs(y, 2)
_images/12-computacao-simbolica_17_0.png

A partir deste ponto, alguns fragmentos de código e exemplos que apresentamos assumirão que os símbolos necessários já foram definidos. Se você tentar um exemplo e o SymPy der uma mensagem como NameError: name 'x' is not defined, é porque você precisa definir o símbolo usando um dos métodos acima.

12.1.3. isympy

O executável isympy é um wrapper em torno do ipython que cria as variáveis simbólicas (reais) x, y e z, as variáveis inteiras simbólicas k, m e n, e as variáveis de função simbólica f, g e h, e importa todos os objetos do SymPy.

Isto é conveniente para descobrir novos recursos ou fazer experimentos de forma interativa

$> isympy Python 2.6.5 console for SymPy 0.6.7

These commands were executed:
>>> from __future__ import division
>>> from sympy import *
>>> x, y, z = symbols('xyz')
>>> k, m, n = symbols('kmn', integer=True)
>>> f, g, h = map(Function, 'fgh')

Documentation can be found at http://sympy.org/

In [1]: 

12.1.4. Tipos numéricos

O SymPy tem os tipos numéricos Rational e RealNumber. A classe Rational representa um número racional como um par de dois inteiros: o numerador e o denominador, então Rational(1,2) representa 1/2, Rational(5,2) representa 5/2, e assim por diante.

from sympy import Rational
a = Rational(1, 10)
a
_images/12-computacao-simbolica_22_0.png
b = Rational(45, 67)
b
_images/12-computacao-simbolica_23_0.png
a * b
_images/12-computacao-simbolica_24_0.png
a - b
_images/12-computacao-simbolica_25_0.png
a + b
_images/12-computacao-simbolica_26_0.png

Note que a classe Rational funciona com expressões racionais exatas. Isto contrasta com o tipo de dado float, padrão do Python, que usa a representação em ponto flutuante para aproximar números racionais.

Podemos converter o tipo sympy.Rational em uma variável de ponto flutuante no Python usando float ou o método evalf do objeto Rational. O método evalf pode levar um argumento que especifica quantos dígitos devem ser calculados para a aproximação de ponto flutuante (nem todos podem ser usados pelo tipo de ponto flutuante do Python, evidentemente).

c = Rational(2, 3)
c
_images/12-computacao-simbolica_28_0.png
float(c)
_images/12-computacao-simbolica_29_0.png
c.evalf()
_images/12-computacao-simbolica_30_0.png
c.evalf(50)
_images/12-computacao-simbolica_31_0.png

12.1.5. Diferenciação e Integração

O SymPy é capaz de executar a diferenciação e integração de muitas funções:

from sympy import Symbol, exp, sin, sqrt, diff
x = Symbol('x')
y = Symbol('y')
diff(sin(x), x)
_images/12-computacao-simbolica_33_0.png
diff(sin(x), y)
_images/12-computacao-simbolica_34_0.png
diff(10 + 3*x + 4*y + 10*x**2 + x**9, x)
_images/12-computacao-simbolica_35_0.png
diff(10 + 3*x + 4*y + 10*x**2 + x**9, y)
_images/12-computacao-simbolica_36_0.png
diff(10 + 3*x + 4*y + 10*x**2 + x**9, x).subs(x,1)
_images/12-computacao-simbolica_37_0.png
diff(10 + 3*x + 4*y + 10*x**2 + x**9, x).subs(x,1.5)
_images/12-computacao-simbolica_38_0.png
diff(exp(x), x)
_images/12-computacao-simbolica_39_0.png
diff(exp(-x ** 2 / 2), x)
_images/12-computacao-simbolica_40_0.png

A função SymPy diff() usa no mínimo dois argumentos: a função a ser diferenciada e a variável em relação à qual a diferenciação é realizada. Derivadas de ordem superior podem ser calculadas pela especificação de variáveis adicionais, ou pela adição de um argumento inteiro opcional:

diff(3*x**4, x)
_images/12-computacao-simbolica_42_0.png
diff(3*x**4, x, x, x)
_images/12-computacao-simbolica_43_0.png
diff(3*x**4, x, 3)
_images/12-computacao-simbolica_44_0.png
diff(3*x**4*y**7, x, 2, y, 2)
_images/12-computacao-simbolica_45_0.png
diff(diff(3*x**4*y**7, x, x), y, y)
_images/12-computacao-simbolica_46_0.png

Às vezes, o SymPy pode retornar um resultado de uma forma pouco familiar. Se, por exemplo, você desejar usar o SymPy para verificar se você diferenciou algo corretamente, uma técnica que pode ser útil é subtrair o resultado do SymPy do seu resultado e verificar se a resposta é zero.

Tomando o exemplo simples de uma função de base radial multiquádrica, \(\phi(r) = \sqrt{r^2 + \sigma^2}\) com \(r = \sqrt{x^2 + y^2}\) e \(\sigma\) uma constante, podemos verificar se a primeira derivada em \(x\) é \(\frac{\partial \phi}{\partial x} = \frac{x}{\sqrt{r^2 + \sigma^ 2}}\).

Neste exemplo, primeiro pedimos que o SymPy imprima a derivada. Veja que ela é impressa de forma diferente da nossa derivada de teste, mas a subtração verifica que elas são idênticas:

r = sqrt(x**2 + y**2)
sigma = Symbol('σ')
def phi(x,y,sigma):
    return sqrt(x**2 + y**2 + sigma**2)

minha_dfdx= x/sqrt(r**2 + sigma**2)
print(diff(phi(x, y, sigma), x))
x/sqrt(x**2 + y**2 + σ**2)
print(minha_dfdx - diff(phi(x, y, sigma), x))
0

Aqui é trivial dizer que as expressões são idênticas sem a ajuda do SymPy, mas, em exemplos mais complicados, pode haver muitos outros termos, tornando-se cada vez mais difícil, demorado e propenso a erros tentar reorganizar nossa derivada de teste e a resposta do SymPy na mesma forma. São nesses casos que esta técnica de subtração é de maior utilidade. A integração usa uma sintaxe semelhante. Para o caso indefinido, especifique a função e a variável em relação à qual a integração é realizada:

from sympy import integrate
integrate(x**2, x)
_images/12-computacao-simbolica_51_0.png
integrate(x**2, y)
_images/12-computacao-simbolica_52_0.png
integrate(sin(x), y)
_images/12-computacao-simbolica_53_0.png
integrate(sin(x), x)
_images/12-computacao-simbolica_54_0.png
integrate(-x*exp(-x**2/2), x)
_images/12-computacao-simbolica_55_0.png

Podemos calcular integrais definidas fornecendo integrate() com uma tupla contendo a variável de interesse, os limites inferior e superior. Se várias variáveis forem especificadas, a integração múltipla é realizada. Quando o SymPy retorna um resultado na classe Rational, é possível avaliá-lo em uma representação de ponto flutuante com qualquer precisão desejada.

integrate(x*2, (x, 0, 1))
_images/12-computacao-simbolica_57_0.png
integrate(x**2, x)
_images/12-computacao-simbolica_58_0.png
integrate(x**2, x, x)
_images/12-computacao-simbolica_59_0.png
integrate(x**2, x, x, y)
_images/12-computacao-simbolica_60_0.png
integrate(x**2, (x, 0, 2))
_images/12-computacao-simbolica_61_0.png
integrate(x**2, (x, 0, 2), (x, 0, 2), (y, 0, 1))
_images/12-computacao-simbolica_62_0.png
float(integrate(x**2, (x, 0, 2)))
_images/12-computacao-simbolica_63_0.png
type(integrate(x**2, (x, 0, 2)))
sympy.core.numbers.Rational
resultado_racional=integrate(x**2, (x, 0, 2))
resultado_racional.evalf()
_images/12-computacao-simbolica_65_0.png
resultado_racional.evalf(50)
_images/12-computacao-simbolica_66_0.png

12.1.6. Equações diferenciais ordinárias

O SymPy tem suporte incorporado para resolver vários tipos de equações diferenciais ordinárias através do comando dsolve. Precisamos configurar a EDO e passá-la como o primeiro argumento, eq. O segundo argumento é a função f(x) a resolver. Um terceiro argumento opcional, hint, influencia o método que dsolve usa: alguns métodos são mais adequados a certas classes de EDOs ou expressarão a solução de forma mais simples do que outros.

Para configurar o solucionador de EDO, precisamos de alguma maneira, fazer referencia à função incógnita que estamos resolvendo, bem como às suas derivadas. As classes Function e Derivative facilitam isso:

from sympy import Symbol, dsolve, Function, Derivative, Eq
y = Function("y")
x = Symbol('x')
y_ = Derivative(y(x), x)
dsolve(y_ + 5*y(x), y(x))
_images/12-computacao-simbolica_68_0.png

Observe como dsolve introduziu uma constante de integração, C1. Esta função introduzirá tantas constantes quantas forem necessárias, e todas serão chamadas Cn, onde n é um número inteiro. Observe também que o primeiro argumento para dsolve é considerado igual a zero, a menos que usemos a função Eq() para especificar o contrário:

dsolve(y_ + 5*y(x), y(x))
_images/12-computacao-simbolica_70_0.png
dsolve(Eq(y_ + 5*y(x), 0), y(x))
_images/12-computacao-simbolica_71_0.png
dsolve(Eq(y_ + 5*y(x), 12), y(x))
_images/12-computacao-simbolica_72_0.png

Os resultados de dsolve são uma instância da classe Equality. Isto tem consequências quando desejamos avaliar numericamente a função e usar o resultado em outro lugar (e.g. se quisermos plotar y(x) por x), porque mesmo depois de usar subs() e evalf(), ainda temos uma Equality, e não um escalar. A maneira de avaliar a função para um número é através do atributo rhs da Equality. Note que, aqui, usamos z para armazenar a Equality retornada por dsolve, ainda que seja uma expressão para uma função chamada y(x), enfatizando a distinção entre Equality e os dados que ela contém.

z = dsolve(y_ + 5*y(x), y(x))
z
_images/12-computacao-simbolica_74_0.png
type(z)
sympy.core.relational.Equality
z.rhs
_images/12-computacao-simbolica_76_0.png
C1=Symbol('C1')
y3 = z.subs({C1:2, x:3})
y3
_images/12-computacao-simbolica_77_0.png
y3.evalf(10)
_images/12-computacao-simbolica_78_0.png
y3.rhs
_images/12-computacao-simbolica_79_0.png
y3.evalf(10)
_images/12-computacao-simbolica_80_0.png
y3.rhs
_images/12-computacao-simbolica_81_0.png
y3.rhs.evalf(10)
_images/12-computacao-simbolica_82_0.png
z.rhs.subs({C1:2, x:4}).evalf(10)
_images/12-computacao-simbolica_83_0.png
z.rhs.subs({C1:2, x:5}).evalf(10)
_images/12-computacao-simbolica_84_0.png
type(z.rhs.subs({C1:2, x:5}).evalf(10))
sympy.core.numbers.Float

Às vezes, dsolve pode retornar uma solução muito geral. Um exemplo é quando existe a possibilidade de que alguns coeficientes sejam complexos. Se soubermos que, por exemplo, eles são sempre reais e positivos, podemos passar esta informação para dsolve e evitar que a solução se torne desnecessariamente complicada:

from sympy import *
a, x = symbols('a,x')
f = Function('f')
dsolve(Derivative(f(x), x, 2) + a**4*f(x), f(x))
_images/12-computacao-simbolica_87_0.png
a=Symbol('a',real=True,positive=True)
dsolve(Derivative(f(x), x, 2)+a**4*f(x), f(x))
_images/12-computacao-simbolica_88_0.png

12.1.7. Expansões em série e plotagens

É possível expandir muitas expressões do SymPy em série de Taylor. O método series torna isso direto. No mínimo, devemos especificar a expressão e a variável a ser expandida. Opcionalmente, também podemos especificar o ponto em torno do qual expandir, o número máximo de termos e a direção da expansão (tente help(Basic.series) para mais informações).

from sympy import *
x = Symbol('x')
sin(x).series(x, 0)
_images/12-computacao-simbolica_90_0.png
series(sin(x), x, 0)
_images/12-computacao-simbolica_91_0.png
cos(x).series(x, 0.5, 10)
_images/12-computacao-simbolica_92_0.png

Em alguns casos, especialmente para avaliação numérica e plotagem dos resultados, é necessário remover o termo final O (n):

cos(x).series(x, 0.5, 10).removeO()
_images/12-computacao-simbolica_94_0.png

O SymPy fornece duas funções de plotagem, plot(), do módulo sympy.plotting, e plot do módulo sympy.mpmath.visualization. No momento da escrita, essas funções careciam de algumas capacidades de plotagem, o que significa que não são adequadas para a maioria de nossas necessidades. Ainda assim, caso você deseje utilizá-las, a função help() delas é útil.

Para a maioria dos nossos propósitos, Matplotlib deve ser a ferramenta de plotagem a ser escolhida. Aqui, fornecemos apenas um exemplo de como plotar os resultados de uma computação SymPy.

%matplotlib inline
from sympy import sin,series,Symbol
import pylab
x = Symbol('x')
s10 = sin(x).series(x,0,10).removeO()
s20 = sin(x).series(x,0,20).removeO()
s = sin(x)
xx = []
y10 = []
y20 = []
y = []
for i in range(1000):
  xx.append(i / 100.0)
  y10.append(float(s10.subs({x:i/100.0})))
  y20.append(float(s20.subs({x:i/100.0})))
  y.append(float(s.subs({x:i/100.0})))

pylab.figure()
<matplotlib.figure.Figure at 0x1140d6c50>
<matplotlib.figure.Figure at 0x1140d6c50>
pylab.plot(xx, y10, label='O(10)')
pylab.plot(xx, y20, label='O(20)')
pylab.plot(xx, y, label='sin(x)')

pylab.axis([0, 10, -4, 4])
pylab.xlabel('x')
pylab.ylabel('f(x)')

pylab.legend()
<matplotlib.legend.Legend at 0x1143f4a58>
_images/12-computacao-simbolica_98_1.png

12.1.8. Equações lineares e inversão de matrizes

O SymPy possui uma classe Matrix e funções associadas que permitem a solução simbólica de sistemas de equações lineares (e, claro, podemos obter respostas numéricas com subs() e evalf()). Considerararemos o exemplo do seguinte par de equações lineares simples:

\[\begin{split} \begin{aligned} 3x + 7y & = 12z \\ 4x - 2y & = 5z \end {aligned} \end{split}\]

Podemos escrever este sistema na forma \(\textbf{A} \textbf{x} = \textbf{b}\) (multiplique \(\textbf{A}\) por \(\textbf{x}\) caso queira verificar se as equações originais são recuperadas), onde

\[\begin{split}{\bf A} = \left( \begin{array}{cc} 3 & 7 \\ 4 & -2 \end{array} \right), \qquad \textbf{x} = \left( \begin{array}{c} x \\ y \end{array} \right), \qquad \textbf{b} = \left( \begin{array}{c} 12z \\ 5z \end{array} \right).\end{split}\]

Aqui, incluímos um símbolo, \(z\), no lado direito para demonstrar que os símbolos serão propagados na solução. Em muitos casos, teríamos \(z = 1\), mas ainda pode haver benefício ao se usar SymPy como um solucionador numérico, mesmo quando a solução não contenha símbolos, devido à sua capacidade de retornar frações exatas em vez de floats aproximados.

Uma estratégia para resolver o sistema para \(\textbf{x}\) é inverter a matriz \(\textbf{A}\) e pré-multiplicar a equação pela matriz inversa, i.e. \(\textbf{A}^{-1} \textbf{A} \textbf{x} = \textbf{x} = \textbf{A}^{-1} \textbf{b}\). A classe Matrix do SymPy possui um método inv() que nos permite encontrar a inversa e * realiza a multiplicação da matriz para nós, quando apropriado:

from sympy import symbols,Matrix
x, y, z = symbols('x,y,z')
A = Matrix(([3, 7], [4, -2]))
A
_images/12-computacao-simbolica_100_0.png
A.inv()
_images/12-computacao-simbolica_101_0.png
b = Matrix(( 12*z,5*z  ))
b
_images/12-computacao-simbolica_102_0.png
x = A.inv()*b
x
_images/12-computacao-simbolica_103_0.png
x.subs({z:3.3}).evalf(4)
_images/12-computacao-simbolica_104_0.png
type(x)
sympy.matrices.dense.MutableDenseMatrix

Um método alternativo para resolver o mesmo problema é construir o sistema como uma matriz aumentada. Essa é a forma obtida dispondo juntas as colunas (no nosso exemplo) de \(\textbf{A}\) e o vetor \(\textbf{b}\). A matriz aumentada é [1]:

\[\begin{split} (\textbf{A} \, | \, \textbf{b} ) = \left( \begin{array}{cc|c} 3 & 7 & 12z \\ 4 & -2 & 5z \end{array} \right), \end{split}\]

e, como antes, construímos isto como um objeto SymPy Matrix, mas, neste caso, passamos para a função solve_linear_system():

from sympy import Matrix, symbols, solve_linear_system
x, y, z = symbols('x,y,z')
system = Matrix(([3, 7, 12*z],[4, -2, 5*z]))
system
_images/12-computacao-simbolica_107_0.png
sol = solve_linear_system(system,x,y)
sol
_images/12-computacao-simbolica_108_0.png
type(sol)
dict
for k in sol.keys():
    print(k,'=',sol[k].subs({z:3.3}).evalf(4))
x = 5.726
y = 3.203

Uma terceira opção é o método solve(), cujos argumentos incluem as equações simbólicas individuais, em vez de matrizes. Como dsolve(), solve() espera ou expressões que ela assumirá como iguais a zero, ou objetos Equality, que podemos criar convenientemente com Eq():

from sympy import symbols,solve,Eq
x, y, z = symbols('x,y,z')
solve((Eq(3*x+7*y,12*z), Eq(4*x-2*y,5*z)), x, y)
_images/12-computacao-simbolica_112_0.png
solve((3*x+7*y-12*z, 4*x-2*y-5*z), x, y)
_images/12-computacao-simbolica_113_0.png

Para obter mais informações, consulte help(solve) e help(solve_linear_system).

12.1.9. Equações não-lineares

Vamos resolver uma equação simples como \(x = x^2\). Existem duas soluções óbvias: \(x = 0\) e \(x = 1\). Como podemos pedir ao SymPy para calcular isso para nós?

import sympy
x, y, z = sympy.symbols('x, y, z')        # cria alguns símbolos
eq = x - x ** 2                           # define a equação
sympy.solve(eq, x)                        # resolve eq = 0
_images/12-computacao-simbolica_116_0.png

A função solve() espera uma expressão que deve ser resolvida com o zero isolado. Para o nosso exemplo, reescrevemos \(x = x^2\) como \(x - x^2 = 0\) e, em seguida, a passamos para a função solve().

Vamos repetir o mesmo para a equação \(x = x^3\) e resolver

eq = x - x ** 3                           # define a equação
sympy.solve(eq, x)                        # resolve eq = 0
_images/12-computacao-simbolica_118_0.png

12.1.10. Saída: interface com LaTeX e impressão elegante

Como é o caso de muitos sistemas algébricos computacionais, o SymPy tem a capacidade de formatar sua saída como código LaTeX para facilitar sua inclusão em documentos.

No início deste capítulo, chamamos:

sympy.init_printing()

O SymPy detectou que estava em Jupyter e habilitou a saída em LaTeX. O Jupyter Notebook suporta (um pouco de) LaTeX e isso é o que nos proporciona a saída elegantemente formatada vista acima. Também podemos ver a saída em texto simples a partir do SymPy, e o código LaTeX puro que pode ser gerado:

print(series(1/(x+y), y, 0, 3))
y**2/x**3 - y/x**2 + 1/x + O(y**3)
print(latex(series(1/(x+y), y, 0, 3)))
\frac{y^{2}}{x^{3}} - \frac{y}{x^{2}} + \frac{1}{x} + \mathcal{O}\left(y^{3}\right)
print(latex(series(1/(x+y), y, 0, 3), mode='inline'))
$\frac{y^{2}}{x^{3}} - \frac{y}{x^{2}} + 1 / x + \mathcal{O}\left(y^{3}\right)$

Esteja ciente de que em seu modo padrão, o código de saída produzido por latex() requer o pacote amsmath, a ser carregado através do comando \usepackage amsmath} no preâmbulo do documento.

O SymPy também suporta uma rotina de “impressão elegante” com a função pprint() (abreviatura de “pretty print”), que produz saída de texto com melhor formatação do que a rotina de impressão padrão. Observe os recursos de notação, tais como subíndices para elementos de um array cujos nomes são da forma T_n, a constante italicizada \(e\), pontos centralizados verticalmente para a multiplicação, bordas de matrizes e frações.

Finalmente, o SymPy oferece a função preview(), que exibe a saída renderizada na tela (verifique help(preview) para obter detalhes).

12.1.11. Geração automática de código em C

Um ponto forte de muitas bibliotecas simbólicas é que elas podem converter as expressões simbólicas em código C (ou outro código), o qual, posteriormente, pode ser compilado para execução em alta velocidade. Aqui está um exemplo que demonstra isso:

from sympy import *                                                                                    
from sympy.utilities.codegen import codegen                                                                            
x = Symbol('x')                                                                                                          
sin(x).series(x, 0, 6)
_images/12-computacao-simbolica_125_0.png
print(codegen(("taylor_sine",sin(x).series(x,0,6)), language='C')[0][1])
/******************************************************************************
 *                       Code generated with sympy 1.0                        *
 *                                                                            *
 *              See http://www.sympy.org/ for more information.               *
 *                                                                            *
 *                       This file is part of 'project'                       *
 ******************************************************************************/
#include "taylor_sine.h"
#include <math.h>

double taylor_sine(double x) {

   double taylor_sine_result;
   taylor_sine_result = x - 1.0L/6.0L*pow(x, 3) + (1.0L/120.0L)*pow(x, 5) + O(x**6);
   return taylor_sine_result;

}

12.2. Ferramentas relacionadas

Vale a pena conferir a iniciativa SAGE (http://www.sagemath.org/), que está tentando “criar uma alternativa de fonte aberta livre e viável para Magma, Maple, Mathematica e Matlab” e inclui a biblioteca SymPy e muitas outras. Suas capacidades simbólicas são mais poderosas do que as do SymPy e o SAGE, à exceção dos recursos do SymPy, já cobrirá muitas das necessidades emergentes nos campos das ciências e engenharias. O SAGE inclui o sistema algébrico computacional Maxima, que também está disponível de forma autônoma em http://maxima.sourceforge.net/.