Sage: Otwarty pakiet matematyczny (worksheet na OpenAcademy 2011)

456 days ago by giniu

Zaczynamy

Sage to pakiet matematyczny. Większość wbudowanych weń elementów służy do obliczeń symbolicznych, jednak posiada on również moduły do obliczeń numerycznych. To czyni go konkurentem pakietów takich jak Mathematica, Maple, Magma czy Matlab. Pakiet Sage jest owocem działania projektu, który został zapoczątkowany przez prof. Williama Steina z Uniwersytetu Washingtona w Seattle w 2005 roku. Jest to stosunkowo młody projekt, ale funkcjonalność pakietu jest już bardzo wysoka. Jest to możliwe dzięki połączonej sile działań wielu ludzi i wykorzystaniu istniejących i sprawdzonych rozwiązań oraz dofinansowaniu gigantów takich jak Microsoft, Google, Sun czy Departament Obrony Stanów Zjednoczonych. Obecnie Sage rozwijany jest przez ponad 220 osób z ponad 150 krajów i łączy w sobie ponad 100 wyspecjalizowanych pakietów matematycznych.

Sage może być widziany z różnych ujęć. Po pierwsze jest to dystrybucja Pythona oraz wielu innych pakietów, na przykład Maxima czy R. Po drugie Sage to ujednolicony interfejs programistyczny i graficzny do tych pakietów, pozwalający na wymianę danych i przekazywanie wyników pomiędzy pakietami. Ale Sage to nie tylko opakowanie tego co już gotowe, to również setki tysięcy nowych linii kodu implementującego to, co nie jest dostępne w innych pakietach. Często kod ten powstaje specjalnie dla pakietu Sage po to, by rozszerzyć jego możliwości lub przyśpieszyć pewne funkcje, czy też jako przykłady implementacji zawarte w publikacjach naukowych.

Postaram się dzisiaj przybliżyć nieco pakiet Sage. Prezentację, którą teraz Państwo widzicie można śledzić w internecie pod adresem http://sage.im.pwr.wroc.pl/home/pub/8/, http://www.sagenb.org/home/pub/2687 lub http://alpha.sagenb.org/home/pub/72/. Zanim przejdziemy do poznania pakietu zapoznajmy się jeszcze z pewnym stwierdzeniem, które było jednym z bodźców do stworzenia tego pakietu, otóż

"płatne pakiety matematyczne nie są matematyczne".

Coraz więcej osób zaczyna podzielać opinię wygłaszaną już jakiś czas przez dr. Joachima Neubüsera, którą jako pierwszy zaczął otwarcie głosić już w 1993 roku. Zamknięte pakiety matematyczne tworzone przez setki ludzi w wielkich firmach sprzedawane są za ogromne pieniądze aby sfinansować dalsze prace. Aby chronić pracę tych ludzi klient kupujący pakiet (zwykle w postaci licencji użytkowania na rok) otrzymuje produkt, w którym nie ma dostępu do kodu, ani informacji o użytych algorytmach. To narusza dwie podstawowe zasady napędzające rozwój matematyki - to, że każde twierdzenie posiada towarzyszący dowód, który możemy zweryfikować i to, że raz czytając o danej metodzie i sprawdzając dowód, przez resztę życia możemy z niej korzystać za darmo. Pakiet matematyczny, tak samo jak twierdzenie, jest narzędziem matematyka - bezkrytyczne ufanie narzędziu, którego poprawności działania nie możemy zweryfikować, lub na które może nas nie być stać za rok, może okazać się poważnym błędem.

Podstawy budowy pakietu

Rozwojowi pakietu Sage przyświeca między innymi jeden cel - "zbudowanie samochodu, zamiast ponownego wynalezienia koła". Między innymi z tego powodu na potrzeby pakietu nie został stworzony nowy język, jak to miało miejsce w przypadku pakietu Mathematica czy Matlab. Twórca pakietu Sage zdecydował, że o wiele więcej osiągnie wykorzystując już istniejący, znany, uniwersalny i sprawdzony język. Po krótkiej analizie dostępnych opcji wybór padł na język Python.

Pakiet Sage posiada dwa interfejsy. Po pierwsze, dysponujemy interfejsem z linii komend. Możemy go wywołać i wewnątrz używać pakietu z poziomu interpretera zbliżonego do interpretera IPython czy tekstowych interfejsów innych pakietów matematycznych. Drugi interfejs, to interfejs graficzny przypominający zasadą działania Notebooki znane z pakietów Mathematica czy Maple. Nie dysponujemy tu jednak napisaną na cel pakietu aplikacją. Wybór padł na technologie internetowe - architekturę klient-serwer oraz interfejs oparty o technologie HTML, CSS, JS, \TeX, AJAX, JAVA i wiele pokrewnych. Otwiera to wiele nowych możliwości związanych z centralnymi instalacjami pakietu, nie blokując instalacji i uruchomienia serwera lokalnie dla uzyskania lepszej wydajności.

Ale czy Python to dobry wybór? Spójrzmy na kilka prostych obliczeń.

Czy jest to Python czy nie Python?

  • Możliwość doinstalowania dowolnego
print "Hello World!" 
       
Hello World!
Hello World!
2+2 
       
4
4
2^3 
       
8
8
1/2 
       
1/2
1/2

Jak widać, nie do końca. Po pierwsze, w Pythonie operator "^" nie służy do potęgowania, używamy zamiast niego operatora "**". Po drugie, 1/2 w języku Python to 0. Twórcy widząc te wady zapisu (z punktu widzenia matematyka) mieli do wyboru trzy wyjścia - zrezygnować z Pythona, zmodyfikować Pythona albo dodać warstwę pomiędzy interfejsem a Pytonem. Oczywisty wybór padł na ostatnią opcję. Wszystko co wpisujemy w komórce jest dodatkowo przetwarzane zanim zostanie wykonane. Możemy przyjrzeć się takiemu zapisowi:

preparse("2^3") 
       
'Integer(2)**Integer(3)'
'Integer(2)**Integer(3)'
preparse("1/2") 
       
'Integer(1)/Integer(2)'
'Integer(1)/Integer(2)'

Jak widać liczby, które wpisujemy są tłumaczone na obiekty klasy Integer zanim zostają zapamiętane. Również operator jest przekształcony. Spójrzmy jeszcze jaki jest typ różnych wartości:

type(2^3) 
       
<type 'sage.rings.integer.Integer'>
<type 'sage.rings.integer.Integer'>
type(1/2) 
       
<type 'sage.rings.rational.Rational'>
<type 'sage.rings.rational.Rational'>

Parser, o którym mowa pozwala również na zaoszczędzenie dużej ilości czasu przy pisaniu bardziej skomplikowanych programów. Spróbujmy wykonać wykres funkcji f(x)=sin(x^2) na przedziale (-\pi,\pi). Możemy to zrobić na przykład tak:

f(x)=sin(x^2) show(f) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}x \ {\mapsto}\ \sin\left(x^{2}\right)
\newcommand{\Bold}[1]{\mathbf{#1}}x \ {\mapsto}\ \sin\left(x^{2}\right)
plot(f,(-pi,pi)) 
       

Osoby znające Pythona zauważą, że na pewno nie jest to składnia do jakiej są przyzwyczajeni. Jest to jednak składnia logiczniejsza w zastosowaniu w pakiecie. Po pierwsze, warto zwrócić uwagę na fakt, że po lewej stronie przypisania występuje zapis przypominający wywołanie funkcji oraz nie związana zmienna x. To co się dzieje stanie się jasne, gdy popatrzymy na to, co właściwie zostało wykonane:

preparse("f(x)=sin(x^2)") 
       
'__tmp__=var("x"); f =
symbolic_expression(sin(x**Integer(2))).function(x)'
'__tmp__=var("x"); f = symbolic_expression(sin(x**Integer(2))).function(x)'

Jak widać, zmienna x została zdefiniowana jako zmienna symboliczna. Następnie zostało zbudowane wyrażenie symboliczne od niej zależne i przekształcone w funkcję. Spójrzmy na typ zmiennej x:

type(x) 
       
<type 'sage.symbolic.expression.Expression'>
<type 'sage.symbolic.expression.Expression'>

Jak widać x nie jest tutaj zmienną w rozumieniu zmiennej języka Python. W pakiecie można też używać innych typów niż zmienna Pythona lub zmienna symboliczna, np.:

R.<x> = ZZ[] 
       

Od wystąpienia takiej deklaracji x jest pewnego rodzaju zmienną, będącą generatorem pierścienia R, tutaj R to:

       
Univariate Polynomial Ring in x over Integer Ring
Univariate Polynomial Ring in x over Integer Ring

a x to:

type(x) 
       
<type
'sage.rings.polynomial.polynomial_integer_dense_flint.Polynomial_integer\
_dense_flint'>
<type 'sage.rings.polynomial.polynomial_integer_dense_flint.Polynomial_integer_dense_flint'>

Tutaj x jest zmienną należącego do wielomianów całkowitych. Typ zawiera dodatkowo informację w jakim pakiecie wielomiany te są zaimplementowane (tutaj Flint). Zwykle jeśli taka informacja towarzyszy typowi znaczy to, że mamy do wyboru kilka implementacji. Pomińmy teraz cukier syntaktyczny i stwórzmy S.<y> = ZZ[] ale w innej implementacji:

S=PolynomialRing(ZZ,'y',implementation='NTL'); (y,)=S._first_ngens(1);S 
       
Univariate Polynomial Ring in y over Integer Ring (using NTL)
Univariate Polynomial Ring in y over Integer Ring (using NTL)
type(y) 
       
<type
'sage.rings.polynomial.polynomial_integer_dense_ntl.Polynomial_integer_d\
ense_ntl'>
<type 'sage.rings.polynomial.polynomial_integer_dense_ntl.Polynomial_integer_dense_ntl'>

Informacja ta jest oczywiście respektowana przez pakiet. Spójrzmy na miejsce zerowe dwóch wielomianów:

(x^2-1).roots() 
       
[(1, 1), (-1, 1)]
[(1, 1), (-1, 1)]
(x^2-2).roots() 
       
[]
[]

W pierwszym przypadku miejscem zerowym jest całkowite 1 oraz -1, w drugim brak miejsc zerowych. Jeśli teraz zdefiniujemy:

R.<x> = RR[] R 
       
Univariate Polynomial Ring in x over Real Field with 53 bits of
precision
Univariate Polynomial Ring in x over Real Field with 53 bits of precision
(x^2-1).roots() 
       
[(-1.00000000000000, 1), (1.00000000000000, 1)]
[(-1.00000000000000, 1), (1.00000000000000, 1)]
(x^2-2).roots() 
       
[(-1.41421356237310, 1), (1.41421356237310, 1)]
[(-1.41421356237310, 1), (1.41421356237310, 1)]

Uzyskamy inne wyniki, przedstawione w postaci liczb zmiennoprzecinkowych z 53 bitami precyzji. 

Rozgraniczenie takie pozwala często na implementację o wiele bardziej wydajnego algorytmu, ponieważ zmienna zawiera w sobie dużo informacji o tym, do czego jej użyjemy. Przejdźmy teraz do praktyczniejszych przykładów zastosowań.

Sage w przykładach

Na początek coś prostego. Znajdźmy miejsca zerowe równania kwadratowego a x^2+b x+c. Najpierw upewnijmy się, że wszystkie użyte oznaczenia są zmiennymi symbolicznymi:

var("a,b,c,x") 
       
(a, b, c, x)
(a, b, c, x)

Teraz określmy równanie:

rown = a*x^2+b*x+c==0 show(rown) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}a x^{2} + b x + c = 0
\newcommand{\Bold}[1]{\mathbf{#1}}a x^{2} + b x + c = 0

Pozostaje je rozwiązać. Możemy użyć funkcji solve:

rozw = solve(rown, x) show(rozw) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}\left[x = -\frac{b + \sqrt{-4 \, a c + b^{2}}}{2 \, a}, x = -\frac{b - \sqrt{-4 \, a c + b^{2}}}{2 \, a}\right]
\newcommand{\Bold}[1]{\mathbf{#1}}\left[x = -\frac{b + \sqrt{-4 \, a c + b^{2}}}{2 \, a}, x = -\frac{b - \sqrt{-4 \, a c + b^{2}}}{2 \, a}\right]

Lub równoważnie metody solve:

show(rown.solve(x)) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}\left[x = -\frac{b + \sqrt{-4 \, a c + b^{2}}}{2 \, a}, x = -\frac{b - \sqrt{-4 \, a c + b^{2}}}{2 \, a}\right]
\newcommand{\Bold}[1]{\mathbf{#1}}\left[x = -\frac{b + \sqrt{-4 \, a c + b^{2}}}{2 \, a}, x = -\frac{b - \sqrt{-4 \, a c + b^{2}}}{2 \, a}\right]

Zapis taki jak powyżej uzyskujemy dzięki funkcji show, w rzeczywistości wynik jest zwykłą listą:

rozw 
       
[x == -1/2*(b + sqrt(-4*a*c + b^2))/a, x == -1/2*(b - sqrt(-4*a*c +
b^2))/a]
[x == -1/2*(b + sqrt(-4*a*c + b^2))/a, x == -1/2*(b - sqrt(-4*a*c + b^2))/a]

Możemy też sprawdzić, czy rozwiązanie jest prawidłowe. W tym celu podstawimy jedno z nich do równania:

rown.subs(rozw[0]) 
       
1/4*(b + sqrt(-4*a*c + b^2))^2/a - 1/2*(b + sqrt(-4*a*c + b^2))*b/a + c
== 0
1/4*(b + sqrt(-4*a*c + b^2))^2/a - 1/2*(b + sqrt(-4*a*c + b^2))*b/a + c == 0

Podobnie jak wiele innych pakietów, Sage sam nie upraszcza obliczeń symbolicznych. Jest to operacja kosztowna, która może okazać się niepotrzebna. Możemy oczywiście zażądać pełnego uproszczenia wyniku:

rown2=rown.subs(rozw[0]) rown2.full_simplify() 
       
0 == 0
0 == 0

Jeśli jednak jesteśmy pewni, że chcemy zastosować tylko pewne reguły upraszczania, możemy użyć prostszej implementacji:

rown2.simplify_rational() 
       
0 == 0
0 == 0

Dokumentacja zawiera dokładny opis, spójrzmy na dokumentację metody simplify_rational:

rown2.simplify_rational? 
       

File: /usr/local/sage/sage-4.6.1/devel/sage/sage/symbolic/expression.pyx

Type: <type ‘instancemethod’>

Definition: rown2.simplify_rational(*args, **kwds)

Docstring:

Simplify rational expressions.

INPUT:

  • self - symbolic expression
  • algorithm - (default: ‘full’) string which switches the algorithm for simplifications. Possible values are
    • ‘simple’ (simplify rational functions into quotient of two polynomials),
    • ‘full’ (apply repeatedly, if necessary)
    • ‘noexpand’ (convert to commmon denominator and add)
  • map - (default: False) if True, the result is an expression whose leading operator is the same as that of the expression self but whose subparts are the results of applying simplification rules to the corresponding subparts of the expressions.

ALIAS: rational_simplify() and simplify_rational() are the same

DETAILS: We call Maxima functions ratsimp, fullratsimp and xthru. If each part of the expression has to be simplified separately, we use Maxima function map.

EXAMPLES:

sage: f = sin(x/(x^2 + x))
sage: f
sin(x/(x^2 + x))
sage: f.simplify_rational()
sin(1/(x + 1))
sage: f = ((x - 1)^(3/2) - (x + 1)*sqrt(x - 1))/sqrt((x - 1)*(x + 1)); f
((x - 1)^(3/2) - sqrt(x - 1)*(x + 1))/sqrt((x - 1)*(x + 1))
sage: f.simplify_rational()
-2*sqrt(x - 1)/sqrt(x^2 - 1)

With map=True each term in a sum is simplified separately and thus the resuls are shorter for functions which are combination of rational and nonrational funtions. In the following example, we use this option if we want not to combine logarithm and the rational function into one fraction:

sage: f=(x^2-1)/(x+1)-ln(x)/(x+2)
sage: f.simplify_rational()
(x^2 + x - log(x) - 2)/(x + 2)
sage: f.simplify_rational(map=True)
x - log(x)/(x + 2) - 1

Here is an example from the Maxima documentation of where algorithm='simple' produces an (possibly useful) intermediate step:

sage: y = var('y')
sage: g = (x^(y/2) + 1)^2*(x^(y/2) - 1)^2/(x^y - 1)
sage: g.simplify_rational(algorithm='simple')
-(2*x^y - x^(2*y) - 1)/(x^y - 1)
sage: g.simplify_rational()
x^y - 1

With option algorithm='noexpand' we only convert to common denominators and add. No expansion of products is performed:

sage: f=1/(x+1)+x/(x+2)^2
sage: f.simplify_rational()
(2*x^2 + 5*x + 4)/(x^3 + 5*x^2 + 8*x + 4)
sage: f.simplify_rational(algorithm='noexpand')
((x + 1)*x + (x + 2)^2)/((x + 1)*(x + 2)^2)

File: /usr/local/sage/sage-4.6.1/devel/sage/sage/symbolic/expression.pyx

Type: <type ‘instancemethod’>

Definition: rown2.simplify_rational(*args, **kwds)

Docstring:

Simplify rational expressions.

INPUT:

  • self - symbolic expression
  • algorithm - (default: ‘full’) string which switches the algorithm for simplifications. Possible values are
    • ‘simple’ (simplify rational functions into quotient of two polynomials),
    • ‘full’ (apply repeatedly, if necessary)
    • ‘noexpand’ (convert to commmon denominator and add)
  • map - (default: False) if True, the result is an expression whose leading operator is the same as that of the expression self but whose subparts are the results of applying simplification rules to the corresponding subparts of the expressions.

ALIAS: rational_simplify() and simplify_rational() are the same

DETAILS: We call Maxima functions ratsimp, fullratsimp and xthru. If each part of the expression has to be simplified separately, we use Maxima function map.

EXAMPLES:

sage: f = sin(x/(x^2 + x))
sage: f
sin(x/(x^2 + x))
sage: f.simplify_rational()
sin(1/(x + 1))
sage: f = ((x - 1)^(3/2) - (x + 1)*sqrt(x - 1))/sqrt((x - 1)*(x + 1)); f
((x - 1)^(3/2) - sqrt(x - 1)*(x + 1))/sqrt((x - 1)*(x + 1))
sage: f.simplify_rational()
-2*sqrt(x - 1)/sqrt(x^2 - 1)

With map=True each term in a sum is simplified separately and thus the resuls are shorter for functions which are combination of rational and nonrational funtions. In the following example, we use this option if we want not to combine logarithm and the rational function into one fraction:

sage: f=(x^2-1)/(x+1)-ln(x)/(x+2)
sage: f.simplify_rational()
(x^2 + x - log(x) - 2)/(x + 2)
sage: f.simplify_rational(map=True)
x - log(x)/(x + 2) - 1

Here is an example from the Maxima documentation of where algorithm='simple' produces an (possibly useful) intermediate step:

sage: y = var('y')
sage: g = (x^(y/2) + 1)^2*(x^(y/2) - 1)^2/(x^y - 1)
sage: g.simplify_rational(algorithm='simple')
-(2*x^y - x^(2*y) - 1)/(x^y - 1)
sage: g.simplify_rational()
x^y - 1

With option algorithm='noexpand' we only convert to common denominators and add. No expansion of products is performed:

sage: f=1/(x+1)+x/(x+2)^2
sage: f.simplify_rational()
(2*x^2 + 5*x + 4)/(x^3 + 5*x^2 + 8*x + 4)
sage: f.simplify_rational(algorithm='noexpand')
((x + 1)*x + (x + 2)^2)/((x + 1)*(x + 2)^2)

Możemy nawet sprawdzić jej kod, choć w tym przypadku jest to tylko przekazanie obliczeń do pakietu Maxima:

rown2.simplify_rational?? 
       
def simplify_rational(self,algorithm=’full’, map=False):

r”“” Simplify rational expressions.

INPUT:

  • self - symbolic expression
  • algorithm - (default: ‘full’) string which switches the algorithm for simplifications. Possible values are
    • ‘simple’ (simplify rational functions into quotient of two polynomials),
    • ‘full’ (apply repeatedly, if necessary)
    • ‘noexpand’ (convert to commmon denominator and add)
  • map - (default: False) if True, the result is an expression whose leading operator is the same as that of the expression self but whose subparts are the results of applying simplification rules to the corresponding subparts of the expressions.

ALIAS: rational_simplify() and simplify_rational() are the same

DETAILS: We call Maxima functions ratsimp, fullratsimp and xthru. If each part of the expression has to be simplified separately, we use Maxima function map.

EXAMPLES:

sage: f = sin(x/(x^2 + x))
sage: f
sin(x/(x^2 + x))
sage: f.simplify_rational()
sin(1/(x + 1))
sage: f = ((x - 1)^(3/2) - (x + 1)*sqrt(x - 1))/sqrt((x - 1)*(x + 1)); f
((x - 1)^(3/2) - sqrt(x - 1)*(x + 1))/sqrt((x - 1)*(x + 1))
sage: f.simplify_rational()
-2*sqrt(x - 1)/sqrt(x^2 - 1)

With map=True each term in a sum is simplified separately and thus the resuls are shorter for functions which are combination of rational and nonrational funtions. In the following example, we use this option if we want not to combine logarithm and the rational function into one fraction:

sage: f=(x^2-1)/(x+1)-ln(x)/(x+2)
sage: f.simplify_rational()
(x^2 + x - log(x) - 2)/(x + 2)
sage: f.simplify_rational(map=True)
x - log(x)/(x + 2) - 1

Here is an example from the Maxima documentation of where algorithm='simple' produces an (possibly useful) intermediate step:

sage: y = var('y')
sage: g = (x^(y/2) + 1)^2*(x^(y/2) - 1)^2/(x^y - 1)
sage: g.simplify_rational(algorithm='simple')
-(2*x^y - x^(2*y) - 1)/(x^y - 1)
sage: g.simplify_rational()
x^y - 1

With option algorithm='noexpand' we only convert to common denominators and add. No expansion of products is performed:

sage: f=1/(x+1)+x/(x+2)^2
sage: f.simplify_rational()
(2*x^2 + 5*x + 4)/(x^3 + 5*x^2 + 8*x + 4)
sage: f.simplify_rational(algorithm='noexpand')
((x + 1)*x + (x + 2)^2)/((x + 1)*(x + 2)^2)

“”” self_m = self._maxima_() if algorithm == ‘full’:

maxima_method = ‘fullratsimp’
elif algorithm == ‘simple’:
maxima_method = ‘ratsimp’
elif algorithm == ‘noexpand’:
maxima_method = ‘xthru’
else:
raise NotImplementedError, “unknown algorithm, see the help for available algorithms”

P = self_m.parent() self_str=self_m.str() if map:

cmd = “if atom(%s) then %s(%s) else map(%s,%s)”%(self_str,maxima_method,self_str,maxima_method,self_str)
else:
cmd = “%s(%s)”%(maxima_method,self_m.str())

res = P(cmd) return self.parent()(res)

def simplify_rational(self,algorithm=’full’, map=False):

r”“” Simplify rational expressions.

INPUT:

  • self - symbolic expression
  • algorithm - (default: ‘full’) string which switches the algorithm for simplifications. Possible values are
    • ‘simple’ (simplify rational functions into quotient of two polynomials),
    • ‘full’ (apply repeatedly, if necessary)
    • ‘noexpand’ (convert to commmon denominator and add)
  • map - (default: False) if True, the result is an expression whose leading operator is the same as that of the expression self but whose subparts are the results of applying simplification rules to the corresponding subparts of the expressions.

ALIAS: rational_simplify() and simplify_rational() are the same

DETAILS: We call Maxima functions ratsimp, fullratsimp and xthru. If each part of the expression has to be simplified separately, we use Maxima function map.

EXAMPLES:

sage: f = sin(x/(x^2 + x))
sage: f
sin(x/(x^2 + x))
sage: f.simplify_rational()
sin(1/(x + 1))
sage: f = ((x - 1)^(3/2) - (x + 1)*sqrt(x - 1))/sqrt((x - 1)*(x + 1)); f
((x - 1)^(3/2) - sqrt(x - 1)*(x + 1))/sqrt((x - 1)*(x + 1))
sage: f.simplify_rational()
-2*sqrt(x - 1)/sqrt(x^2 - 1)

With map=True each term in a sum is simplified separately and thus the resuls are shorter for functions which are combination of rational and nonrational funtions. In the following example, we use this option if we want not to combine logarithm and the rational function into one fraction:

sage: f=(x^2-1)/(x+1)-ln(x)/(x+2)
sage: f.simplify_rational()
(x^2 + x - log(x) - 2)/(x + 2)
sage: f.simplify_rational(map=True)
x - log(x)/(x + 2) - 1

Here is an example from the Maxima documentation of where algorithm='simple' produces an (possibly useful) intermediate step:

sage: y = var('y')
sage: g = (x^(y/2) + 1)^2*(x^(y/2) - 1)^2/(x^y - 1)
sage: g.simplify_rational(algorithm='simple')
-(2*x^y - x^(2*y) - 1)/(x^y - 1)
sage: g.simplify_rational()
x^y - 1

With option algorithm='noexpand' we only convert to common denominators and add. No expansion of products is performed:

sage: f=1/(x+1)+x/(x+2)^2
sage: f.simplify_rational()
(2*x^2 + 5*x + 4)/(x^3 + 5*x^2 + 8*x + 4)
sage: f.simplify_rational(algorithm='noexpand')
((x + 1)*x + (x + 2)^2)/((x + 1)*(x + 2)^2)

“”” self_m = self._maxima_() if algorithm == ‘full’:

maxima_method = ‘fullratsimp’
elif algorithm == ‘simple’:
maxima_method = ‘ratsimp’
elif algorithm == ‘noexpand’:
maxima_method = ‘xthru’
else:
raise NotImplementedError, “unknown algorithm, see the help for available algorithms”

P = self_m.parent() self_str=self_m.str() if map:

cmd = “if atom(%s) then %s(%s) else map(%s,%s)”%(self_str,maxima_method,self_str,maxima_method,self_str)
else:
cmd = “%s(%s)”%(maxima_method,self_m.str())

res = P(cmd) return self.parent()(res)

Niestety, pakiety do obliczeń symbolicznych nie zawsze sobie poradzą. Łatwo znaleźć prosto wyglądające przykłady, na których zarazem Sage, Mathematica i inne pakiety po prostu odmawiają podania wyniku. Przeanalizujmy zadanie znalezienia rozwiązania równania x=sin(x)^2+2 cos(x)^2.

rown = x==sin(x)^2+2*cos(x)^2 show(rown) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}x = \sin\left(x\right)^{2} + 2 \, \cos\left(x\right)^{2}
\newcommand{\Bold}[1]{\mathbf{#1}}x = \sin\left(x\right)^{2} + 2 \, \cos\left(x\right)^{2}
solve(rown,x) 
       
[x == sin(x)^2 + 2*cos(x)^2]
[x == sin(x)^2 + 2*cos(x)^2]

Jak widać nie udało się znaleźć rozwiązania. Spróbujmy najpierw uprościć równanie:

rown2 = rown.full_simplify() # rown.trig_simplify() show(rown2) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}x = \cos\left(x\right)^{2} + 1
\newcommand{\Bold}[1]{\mathbf{#1}}x = \cos\left(x\right)^{2} + 1
rown2.solve(x) 
       
[x == cos(x)^2 + 1]
[x == cos(x)^2 + 1]

Niestety, dalej nie udało nam się znaleźć odpowiedzi. Spróbujmy jednak zadowolić się rozwiązaniem numerycznym. Przekształćmy nasze równanie na wyrażenie, którego miejsca zerowego będziemy szukać:

wyr = rown2.lhs() - rown2.rhs() show(wyr) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}-\cos\left(x\right)^{2} + x - 1
\newcommand{\Bold}[1]{\mathbf{#1}}-\cos\left(x\right)^{2} + x - 1

Jest to prawie funkcja liniowa, ale występuje tutaj też czynnik oscylujący - cos(x)^2. Zastąpmy go minimalną i maksymalną wartością, po czym rozwiążmy powstałe równania:

wyr.subs({cos(x)^2:0}).solve(x) 
       
[x == 1]
[x == 1]
wyr.subs({cos(x)^2:1}).solve(x) 
       
[x == 2]
[x == 2]

Możemy więc wnioskować, że rozwiązanie jest na odcinku (1,2). Spróbujmy znaleźć je numerycznie:

rozw=wyr.find_root(1,2) rozw 
       
1.1596952618411804
1.1596952618411804

Sprawdźmy na rysunku

wykres = plot(wyr,(x,-1,4)) punkt = point((rozw,0),pointsize=25,rgbcolor=(1,0,0)) show(wykres+punkt) 
       

Wiemy już, że Sage radzi sobie z równaniami. Spróbujmy teraz policzyć pochodną funkcji

f(x) = sin(log(x^2)) show(f) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}x \ {\mapsto}\ \sin\left(\log\left(x^{2}\right)\right)
\newcommand{\Bold}[1]{\mathbf{#1}}x \ {\mapsto}\ \sin\left(\log\left(x^{2}\right)\right)

Możemy to wykonać na kilka sposobów, zwróćmy uwagę na różnice pomiędzy funkcją a wyrażeniem:

f.diff() 
       
x |--> 2*cos(log(x^2))/x
x |--> 2*cos(log(x^2))/x
f(x).diff() 
       
2*cos(log(x^2))/x
2*cos(log(x^2))/x

Możemy policzyć również drugą pochodną:

f.diff(2) 
       
x |--> -4*sin(log(x^2))/x^2 - 2*cos(log(x^2))/x^2
x |--> -4*sin(log(x^2))/x^2 - 2*cos(log(x^2))/x^2

zapis ten jest równoważny kilku innym formom:

f.diff(x,2) 
       
x |--> -4*sin(log(x^2))/x^2 - 2*cos(log(x^2))/x^2
x |--> -4*sin(log(x^2))/x^2 - 2*cos(log(x^2))/x^2
diff(f,2) 
       
x |--> -4*sin(log(x^2))/x^2 - 2*cos(log(x^2))/x^2
x |--> -4*sin(log(x^2))/x^2 - 2*cos(log(x^2))/x^2
diff(f,x,2) 
       
x |--> -4*sin(log(x^2))/x^2 - 2*cos(log(x^2))/x^2
x |--> -4*sin(log(x^2))/x^2 - 2*cos(log(x^2))/x^2

To, której formy użyjemy w przypadku funkcji lub wyrażeń jednej zmiennej zależy wyłącznie od nas. W przypadku wielu zmiennych należy podać również zmienne, po której liczymy pochodne, inaczej uzyskamy gradient funkcji. Popatrzmy na kilka obliczeń dla funkcji:

g(x,y) = sin(x)+cos(y) show(g) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}\left( x, y \right) \ {\mapsto} \ \sin\left(x\right) + \cos\left(y\right)
\newcommand{\Bold}[1]{\mathbf{#1}}\left( x, y \right) \ {\mapsto} \ \sin\left(x\right) + \cos\left(y\right)
g.diff() 
       
(x, y) |--> (cos(x), -sin(y))
(x, y) |--> (cos(x), -sin(y))
g.diff(x) 
       
(x, y) |--> cos(x)
(x, y) |--> cos(x)
g.diff(y) 
       
(x, y) |--> -sin(y)
(x, y) |--> -sin(y)
g.gradient() 
       
(x, y) |--> (cos(x), -sin(y))
(x, y) |--> (cos(x), -sin(y))
g.diff(2) 
       
[(x, y) |--> -sin(x)       (x, y) |--> 0]
[      (x, y) |--> 0 (x, y) |--> -cos(y)]
[(x, y) |--> -sin(x)       (x, y) |--> 0]
[      (x, y) |--> 0 (x, y) |--> -cos(y)]
g.diff(2).det() 
       
(x, y) |--> sin(x)*cos(y)
(x, y) |--> sin(x)*cos(y)
plot3d(g,(-4*pi,4*pi),(-4*pi,4*pi)) 
       

Wróćmy teraz do funkcji f:

show(f) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}x \ {\mapsto}\ \sin\left(\log\left(x^{2}\right)\right)
\newcommand{\Bold}[1]{\mathbf{#1}}x \ {\mapsto}\ \sin\left(\log\left(x^{2}\right)\right)

Spróbujmy policzyć całkę:

calka=f.integrate(x) show(calka) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}x \ {\mapsto}\ \frac{1}{5} \, x \sin\left(2 \, \log\left(x\right)\right) - \frac{2}{5} \, x \cos\left(2 \, \log\left(x\right)\right)
\newcommand{\Bold}[1]{\mathbf{#1}}x \ {\mapsto}\ \frac{1}{5} \, x \sin\left(2 \, \log\left(x\right)\right) - \frac{2}{5} \, x \cos\left(2 \, \log\left(x\right)\right)

Sprawdźmy teraz, czy obliczenia są prawidłowe:

calka.diff() 
       
x |--> sin(2*log(x))
x |--> sin(2*log(x))
wyr = calka(x).diff()-f(x) wyr 
       
sin(2*log(x)) - sin(log(x^2))
sin(2*log(x)) - sin(log(x^2))
wyr.simplify_log() 
       
0
0

Spróbujmy obliczyć teraz całkę oznaczoną:

var('t') h(t) = integrate(f(x), (x,0,t)) 
       
Traceback (click to the left of this block for traceback)
...
Is  t  positive, negative, or zero?
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "_sage_input_87.py", line 10, in <module>
    exec compile(u'open("___code___.py","w").write("# -*- coding: utf-8 -*-\\n" + _support_.preparse_worksheet_cell(base64.b64decode("dmFyKCd0JykKaCh0KSA9IGludGVncmF0ZShmKHgpLCAoeCwwLHQpKQ=="),globals())+"\\n"); execfile(os.path.abspath("___code___.py"))
  File "", line 1, in <module>
    
  File "/tmp/tmpnRNECm/___code___.py", line 4, in <module>
    exec compile(u'__tmp__=var("t"); h = symbolic_expression(integrate(f(x), (x,_sage_const_0 ,t))).function(t)
  File "", line 1, in <module>
    
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/misc/functional.py", line 718, in integral
    return x.integral(*args, **kwds)
  File "expression.pyx", line 8147, in sage.symbolic.expression.Expression.integral (sage/symbolic/expression.cpp:30866)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/symbolic/integration/integral.py", line 603, in integrate
    return definite_integral(expression, v, a, b)
  File "function.pyx", line 414, in sage.symbolic.function.Function.__call__ (sage/symbolic/function.cpp:4424)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/symbolic/integration/integral.py", line 174, in _eval_
    return integrator(*args)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/symbolic/integration/external.py", line 21, in maxima_integrator
    result = expression._maxima_().integrate(v, a, b)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/interfaces/maxima.py", line 2140, in integral
    return I(var, min, max)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/interfaces/expect.py", line 1479, in __call__
    return self._obj.parent().function_call(self._name, [self._obj] + list(args), kwds)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/interfaces/expect.py", line 1378, in function_call
    return self.new(s)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/interfaces/expect.py", line 1159, in new
    return self(code)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/interfaces/expect.py", line 1095, in __call__
    return cls(self, x, name=name)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/interfaces/expect.py", line 1522, in __init__
    raise TypeError, x
TypeError: Computation failed since Maxima requested additional constraints (try the command 'assume(t>0)' before integral or limit evaluation, for example):
Is  t  positive, negative, or zero?

Co się stało? Sage zdecydował się nie podawać wyniku ogólnego, ponieważ jego postać zależy od znaku zmiennej t. Załóżmy, że t>0:

assume(t>0) 
       
h(t) = integrate(f(x), (x,0,t)) show(h) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}t \ {\mapsto}\ \frac{1}{5} \, t \sin\left(2 \, \log\left(t\right)\right) - \frac{2}{5} \, t \cos\left(2 \, \log\left(t\right)\right)
\newcommand{\Bold}[1]{\mathbf{#1}}t \ {\mapsto}\ \frac{1}{5} \, t \sin\left(2 \, \log\left(t\right)\right) - \frac{2}{5} \, t \cos\left(2 \, \log\left(t\right)\right)

Założeń również możemy się pozbywać:

forget(t>0) 
       
integrate(f(x), (x,0,t)) 
       
Traceback (click to the left of this block for traceback)
...
Is  t  positive, negative, or zero?
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "_sage_input_91.py", line 10, in <module>
    exec compile(u'open("___code___.py","w").write("# -*- coding: utf-8 -*-\\n" + _support_.preparse_worksheet_cell(base64.b64decode("aW50ZWdyYXRlKGYoeCksICh4LDAsdCkp"),globals())+"\\n"); execfile(os.path.abspath("___code___.py"))
  File "", line 1, in <module>
    
  File "/tmp/tmptElfvc/___code___.py", line 3, in <module>
    exec compile(u'integrate(f(x), (x,_sage_const_0 ,t))
  File "", line 1, in <module>
    
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/misc/functional.py", line 718, in integral
    return x.integral(*args, **kwds)
  File "expression.pyx", line 8147, in sage.symbolic.expression.Expression.integral (sage/symbolic/expression.cpp:30866)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/symbolic/integration/integral.py", line 603, in integrate
    return definite_integral(expression, v, a, b)
  File "function.pyx", line 414, in sage.symbolic.function.Function.__call__ (sage/symbolic/function.cpp:4424)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/symbolic/integration/integral.py", line 174, in _eval_
    return integrator(*args)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/symbolic/integration/external.py", line 21, in maxima_integrator
    result = expression._maxima_().integrate(v, a, b)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/interfaces/maxima.py", line 2140, in integral
    return I(var, min, max)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/interfaces/expect.py", line 1479, in __call__
    return self._obj.parent().function_call(self._name, [self._obj] + list(args), kwds)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/interfaces/expect.py", line 1378, in function_call
    return self.new(s)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/interfaces/expect.py", line 1159, in new
    return self(code)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/interfaces/expect.py", line 1095, in __call__
    return cls(self, x, name=name)
  File "/usr/local/sage/sage-4.6.1/local/lib/python2.6/site-packages/sage/interfaces/expect.py", line 1522, in __init__
    raise TypeError, x
TypeError: Computation failed since Maxima requested additional constraints (try the command 'assume(t>0)' before integral or limit evaluation, for example):
Is  t  positive, negative, or zero?

Ale to co obliczyliśmy przy konkretnych założeniach nie ulega zmianie:

show(h) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}t \ {\mapsto}\ \frac{1}{5} \, t \sin\left(2 \, \log\left(t\right)\right) - \frac{2}{5} \, t \cos\left(2 \, \log\left(t\right)\right)
\newcommand{\Bold}[1]{\mathbf{#1}}t \ {\mapsto}\ \frac{1}{5} \, t \sin\left(2 \, \log\left(t\right)\right) - \frac{2}{5} \, t \cos\left(2 \, \log\left(t\right)\right)

Przy okazji, wyniki możemy prezentować w różnej formie. W formie złożonej (show), w formie \TeX-owej (latex) oraz podstawowej.

latex(h) 
       
t \ {\mapsto}\ \frac{1}{5} \, t \sin\left(2 \, \log\left(t\right)\right)
- \frac{2}{5} \, t \cos\left(2 \, \log\left(t\right)\right)
t \ {\mapsto}\ \frac{1}{5} \, t \sin\left(2 \, \log\left(t\right)\right) - \frac{2}{5} \, t \cos\left(2 \, \log\left(t\right)\right)
h(2) 
       
2/5*sin(2*log(2)) - 4/5*cos(2*log(2))
2/5*sin(2*log(2)) - 4/5*cos(2*log(2))

Wartość taką jak powyżej możemy chcieć przybliżyć numerycznie, by mieć pojęcie o wymiarze wielkości:

n(h(2)) 
       
0.246445516369856
0.246445516369856
n(h(2),digits=100) 
       
0.2464455163698561467625129506897031774298775383654785101319790237781924\
188652689097225052514345319593
0.2464455163698561467625129506897031774298775383654785101319790237781924188652689097225052514345319593
time cyfry_pi=n(pi, digits=100000) 
       
Time: CPU 1.57 s, Wall: 1.57 s
Time: CPU 1.57 s, Wall: 1.57 s
len(str(cyfry_pi)) 
       
100001
100001

Przyjrzyjmy się teraz trochę bardziej skomplikowanym obliczeniom, powiedzmy pakietowi Sage, że x jest funkcją czasu i rozwiążmy proste równanie różniczkowe x'(t)+x(t)-1=0.

var('t') assume(t>0) x=function('x',t) show(x) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}x\left(t\right)
\newcommand{\Bold}[1]{\mathbf{#1}}x\left(t\right)
rown = x.diff() + x - 1 show(rown) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}x\left(t\right) + D[0]\left(x\right)\left(t\right) - 1
\newcommand{\Bold}[1]{\mathbf{#1}}x\left(t\right) + D[0]\left(x\right)\left(t\right) - 1
rozw = desolve(rown, x) show(rozw) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}{\left(c + e^{t}\right)} e^{\left(-t\right)}
\newcommand{\Bold}[1]{\mathbf{#1}}{\left(c + e^{t}\right)} e^{\left(-t\right)}

Spróbujmy teraz to powtórzyć, ale z warunkiem początkowym x(0)=0.1:

rozw2 = desolve(rown, x, ics=[0,0.1]) show(rozw2) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}\frac{1}{10} \, {\left(10 \, e^{t} - 9\right)} e^{\left(-t\right)}
\newcommand{\Bold}[1]{\mathbf{#1}}\frac{1}{10} \, {\left(10 \, e^{t} - 9\right)} e^{\left(-t\right)}

Oczywiście, korzystając z warunku początkowego i rozwiązania ogólnego możemy wyznaczyć parametr c.

solve(rozw(t=0)==0.1, c) 
       
[c == (-9/10)]
[c == (-9/10)]
rozw.variables() 
       
(c, t)
(c, t)
solve(rozw(t=0)==0.1,(rozw(t=0).variables()[0])) 
       
[c == (-9/10)]
[c == (-9/10)]
c0 = solve(rozw(t=0)==0.1,(rozw(t=0).variables()[0]))[0].rhs() c0 
       
-9/10
-9/10
show(rozw(c=c0)) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}\frac{1}{10} \, {\left(10 \, e^{t} - 9\right)} e^{\left(-t\right)}
\newcommand{\Bold}[1]{\mathbf{#1}}\frac{1}{10} \, {\left(10 \, e^{t} - 9\right)} e^{\left(-t\right)}

Przejdźmy teraz do klasy obliczeń, które mogłoby się wydawać, że wymagają większej siły obliczeniowej:

%time factorial(100) 
       
933262154439441526816992388562667004907159682643816214685929638952175999\
932299156089414639761565182862536979208272237582511852109168640000000000\
00000000000000
CPU time: 0.00 s,  Wall time: 0.00 s
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
CPU time: 0.00 s,  Wall time: 0.00 s
%time factor(factorial(1000)) 
       
2^994 * 3^498 * 5^249 * 7^164 * 11^98 * 13^81 * 17^61 * 19^54 * 23^44 *
29^35 * 31^33 * 37^27 * 41^24 * 43^23 * 47^21 * 53^18 * 59^16 * 61^16 *
67^14 * 71^14 * 73^13 * 79^12 * 83^12 * 89^11 * 97^10 * 101^9 * 103^9 *
107^9 * 109^9 * 113^8 * 127^7 * 131^7 * 137^7 * 139^7 * 149^6 * 151^6 *
157^6 * 163^6 * 167^5 * 173^5 * 179^5 * 181^5 * 191^5 * 193^5 * 197^5 *
199^5 * 211^4 * 223^4 * 227^4 * 229^4 * 233^4 * 239^4 * 241^4 * 251^3 *
257^3 * 263^3 * 269^3 * 271^3 * 277^3 * 281^3 * 283^3 * 293^3 * 307^3 *
311^3 * 313^3 * 317^3 * 331^3 * 337^2 * 347^2 * 349^2 * 353^2 * 359^2 *
367^2 * 373^2 * 379^2 * 383^2 * 389^2 * 397^2 * 401^2 * 409^2 * 419^2 *
421^2 * 431^2 * 433^2 * 439^2 * 443^2 * 449^2 * 457^2 * 461^2 * 463^2 *
467^2 * 479^2 * 487^2 * 491^2 * 499^2 * 503 * 509 * 521 * 523 * 541 *
547 * 557 * 563 * 569 * 571 * 577 * 587 * 593 * 599 * 601 * 607 * 613 *
617 * 619 * 631 * 641 * 643 * 647 * 653 * 659 * 661 * 673 * 677 * 683 *
691 * 701 * 709 * 719 * 727 * 733 * 739 * 743 * 751 * 757 * 761 * 769 *
773 * 787 * 797 * 809 * 811 * 821 * 823 * 827 * 829 * 839 * 853 * 857 *
859 * 863 * 877 * 881 * 883 * 887 * 907 * 911 * 919 * 929 * 937 * 941 *
947 * 953 * 967 * 971 * 977 * 983 * 991 * 997
CPU time: 0.03 s,  Wall time: 0.03 s
2^994 * 3^498 * 5^249 * 7^164 * 11^98 * 13^81 * 17^61 * 19^54 * 23^44 * 29^35 * 31^33 * 37^27 * 41^24 * 43^23 * 47^21 * 53^18 * 59^16 * 61^16 * 67^14 * 71^14 * 73^13 * 79^12 * 83^12 * 89^11 * 97^10 * 101^9 * 103^9 * 107^9 * 109^9 * 113^8 * 127^7 * 131^7 * 137^7 * 139^7 * 149^6 * 151^6 * 157^6 * 163^6 * 167^5 * 173^5 * 179^5 * 181^5 * 191^5 * 193^5 * 197^5 * 199^5 * 211^4 * 223^4 * 227^4 * 229^4 * 233^4 * 239^4 * 241^4 * 251^3 * 257^3 * 263^3 * 269^3 * 271^3 * 277^3 * 281^3 * 283^3 * 293^3 * 307^3 * 311^3 * 313^3 * 317^3 * 331^3 * 337^2 * 347^2 * 349^2 * 353^2 * 359^2 * 367^2 * 373^2 * 379^2 * 383^2 * 389^2 * 397^2 * 401^2 * 409^2 * 419^2 * 421^2 * 431^2 * 433^2 * 439^2 * 443^2 * 449^2 * 457^2 * 461^2 * 463^2 * 467^2 * 479^2 * 487^2 * 491^2 * 499^2 * 503 * 509 * 521 * 523 * 541 * 547 * 557 * 563 * 569 * 571 * 577 * 587 * 593 * 599 * 601 * 607 * 613 * 617 * 619 * 631 * 641 * 643 * 647 * 653 * 659 * 661 * 673 * 677 * 683 * 691 * 701 * 709 * 719 * 727 * 733 * 739 * 743 * 751 * 757 * 761 * 769 * 773 * 787 * 797 * 809 * 811 * 821 * 823 * 827 * 829 * 839 * 853 * 857 * 859 * 863 * 877 * 881 * 883 * 887 * 907 * 911 * 919 * 929 * 937 * 941 * 947 * 953 * 967 * 971 * 977 * 983 * 991 * 997
CPU time: 0.03 s,  Wall time: 0.03 s
%time m = random_matrix(RDF,500) eig = m.eigenvalues() w = [(i, abs(eig[i])) for i in range(len(eig))] show(points(w)) 
       

CPU time: 20.66 s,  Wall time: 20.68 s

CPU time: 20.66 s,  Wall time: 20.68 s
%time Partitions(10^7).cardinality() 
       
920271755026045466855962781668256054307294052810239793953285763517412985\
262323501978822916547103339332198764311128926694429965192014469337180574\
258854255101965669713692722439368861237049443900118466267242229358838809\
496460215546742114497122936318794382420922229797018587870350451317915617\
184990942766797810155029441933075045772129188981041614489343545384206438\
995186836592262593125170223430127680062490663477743842242002004914231357\
899486287124678626100600066102278733540933447719703464029124680195026177\
412964857500689657276787365748796835192363570613191348609145244276270764\
465804777408575949440508551447566418811489630464191115045309280131652547\
731782793747141150484980319367430611463990946023472819466195667158678183\
681130408875817996838721729445775753916663228712954510481120704923859087\
275241592392223665876910286300131474621294645735699401736284697581755151\
900016413451408993670931908590802671856117921704294651978579681174352991\
410791557056622496173369118595092855785844134417338169649142692589187435\
301742615221458864914330678814703958326474085781955660425664604942849123\
726120485051272439872545976606903238042522371480831216853001064737946906\
898017475046619462994720059814429480949267645638531717278315386109140567\
421507374975384275021240249642090800336572787686941682199463463099066866\
702204042926855402821066676615841472011557402435218180206292340119241672\
141006748199826744593298451761269206891815323035747468238859771982766512\
554781263236924319788525197851803361705413947686093663891147724061366833\
494127467087092245969359401102159299184733830143890656327648052676575908\
655135939782442443116808542177355365928232190011995697704315727188881084\
528176142957772556227153624081222658008489322761962675453544265567233476\
857358975517683731089773908740330164186182660688614126800469264172995743\
679921180655438244719647115856821466186517301168907732792274321379544884\
573350740004120158204881150331945483918135267242867162717503464701181877\
399958004314416087296985163324282563458624844066236399562733354725457411\
347627828347256593033627186674151973687980402192233716371343312564266298\
874305195901960277032311918990723575237081597193774377829718464719281814\
341332773477437283460627207869478917449115162489982657581564116298023459\
732495270075351700956243598913730840133454971118265085585706676644886436\
331834764264219387054191767502487300255270790939231320204783038516420542\
008888313103784838110884255487585083733240938734797302734376936793420810\
867020185209718779670927269908884747550412550772822753523086679809536245\
524746452957745548238178302717520159051754876562006809869627325035982116\
186671112647935832871470323287962444698515988848621901187755088113589185\
457016501136471407349024158013999313263656227842088073986822774216895311\
418550769982898380687461265543773217506522881285332749712463895138180510\
378178497047690889118184846592767949868064656342862993393444237184906674\
722873849811294966439997475566061223014020274110963671727713854372115721\
224489228469570767118124546455721433201289716309063821673453815865659403\
167483160827068775845409484961883982333993500194238336824233008140594931\
590134175918949793856506343243088419472771218165592793359389269115106835\
490432906902871027335713152227618464826154317860618134546336344159741794\
139420247061299867257344715523461677386135094707607583386378705799210071\
685144173415484815139532963734550586141746926780137597372467246969311252\
404574068882891540550303875935489428805492623836212595940806996986432453\
55453826567378500963781681659096276126857969078217677288980
CPU time: 1.47 s,  Wall time: 1.47 s
92027175502604546685596278166825605430729405281023979395328576351741298526232350197882291654710333933219876431112892669442996519201446933718057425885425510196566971369272243936886123704944390011846626724222935883880949646021554674211449712293631879438242092222979701858787035045131791561718499094276679781015502944193307504577212918898104161448934354538420643899518683659226259312517022343012768006249066347774384224200200491423135789948628712467862610060006610227873354093344771970346402912468019502617741296485750068965727678736574879683519236357061319134860914524427627076446580477740857594944050855144756641881148963046419111504530928013165254773178279374714115048498031936743061146399094602347281946619566715867818368113040887581799683872172944577575391666322871295451048112070492385908727524159239222366587691028630013147462129464573569940173628469758175515190001641345140899367093190859080267185611792170429465197857968117435299141079155705662249617336911859509285578584413441733816964914269258918743530174261522145886491433067881470395832647408578195566042566460494284912372612048505127243987254597660690323804252237148083121685300106473794690689801747504661946299472005981442948094926764563853171727831538610914056742150737497538427502124024964209080033657278768694168219946346309906686670220404292685540282106667661584147201155740243521818020629234011924167214100674819982674459329845176126920689181532303574746823885977198276651255478126323692431978852519785180336170541394768609366389114772406136683349412746708709224596935940110215929918473383014389065632764805267657590865513593978244244311680854217735536592823219001199569770431572718888108452817614295777255622715362408122265800848932276196267545354426556723347685735897551768373108977390874033016418618266068861412680046926417299574367992118065543824471964711585682146618651730116890773279227432137954488457335074000412015820488115033194548391813526724286716271750346470118187739995800431441608729698516332428256345862484406623639956273335472545741134762782834725659303362718667415197368798040219223371637134331256426629887430519590196027703231191899072357523708159719377437782971846471928181434133277347743728346062720786947891744911516248998265758156411629802345973249527007535170095624359891373084013345497111826508558570667664488643633183476426421938705419176750248730025527079093923132020478303851642054200888831310378483811088425548758508373324093873479730273437693679342081086702018520971877967092726990888474755041255077282275352308667980953624552474645295774554823817830271752015905175487656200680986962732503598211618667111264793583287147032328796244469851598884862190118775508811358918545701650113647140734902415801399931326365622784208807398682277421689531141855076998289838068746126554377321750652288128533274971246389513818051037817849704769088911818484659276794986806465634286299339344423718490667472287384981129496643999747556606122301402027411096367172771385437211572122448922846957076711812454645572143320128971630906382167345381586565940316748316082706877584540948496188398233399350019423833682423300814059493159013417591894979385650634324308841947277121816559279335938926911510683549043290690287102733571315222761846482615431786061813454633634415974179413942024706129986725734471552346167738613509470760758338637870579921007168514417341548481513953296373455058614174692678013759737246724696931125240457406888289154055030387593548942880549262383621259594080699698643245355453826567378500963781681659096276126857969078217677288980
CPU time: 1.47 s,  Wall time: 1.47 s

Jak widać te skomplikowane na pierwszy rzut oka obliczenia nie stanowią problemu. Przejdźmy teraz do kilku wizualizacji. Widzieliśmy już kilka trików związanych z grafiką. Mamy oczywiście dużo możliwości kontroli opcji wyświetlania:

var('x') s = plot(sin(x), (x,-pi,pi), rgbcolor='red', fill='min',fillcolor='red',fillalpha=0.1,thickness=2,linestyle='dashed') c = plot(cos(x), (x,-pi,pi), rgbcolor='blue', fill='min',fillcolor='blue',fillalpha=0.1,thickness=2,linestyle='dashdot') t = text("$\sin(x)$ i $\cos(x)$", (-3/4*pi,0.5), rgbcolor='black', fontsize=12) show(s+c+t, frame=True, axes=False, aspect_ratio=1, figsize=8) 
       
%time a = animate([cos(x+k)+x+1 for k in srange(0,2*pi,0.3)], xmin=0, xmax=2*pi,ymin=0,ymax=9) a.show() 
       
CPU time: 27.16 s,  Wall time: 40.05 s
CPU time: 27.16 s,  Wall time: 40.05 s
@interact def _(g=sin(x)*e^(-x), x0=0, order=(2,(1..20))): p = plot(g,(x,-1,5), thickness=2) dot= point((x0,g.subs(x=x0)),pointsize=80,rgbcolor=(1,0,0)) gt = g.taylor(x,x0,order) pt = plot(gt,(x,-1, 5), color='green', thickness=2) html('funkcja: $g(x)\;=\;%s$'%latex(g)) html('dopasowanie: $\hat{g}(x;%s)\;=\;%s+\mathcal{O}(x^{%s})$'%(x0,latex(gt),order+1)) show(dot + p + pt, ymin = -.5, ymax = 1) 
       

Click to the left again to hide and once more to show the dynamic interactive window

W = plot3d(sin(pi*(x^2+y^2))/2,(x,-1,1),(y,-1,1), frame=False, color='purple', opacity=0.8) S = sphere((0,0,0), size=0.3, color='red', aspect_ratio=[1,1,1]) show(W + S, figsize=4) 
       
region_plot(sin(x^2 + y^2)/(1+y+x*y) > 0, (x,-5,5), (y,-5,5), incol='#ffff7f', outcol='#7f7fff', bordercol='black', plot_points=300).show(aspect_ratio=1) 
       
density_plot(sin(x^2 + y^2) * cos(x+y^2) * sin(y), (x,-4, 4), (y,-4, 4), cmap='jet', plot_points=100).show(figsize=(6,6), frame=True) 
       
C = cube(color=['red', 'green', 'blue'], aspect_ratio=[1,1,1], viewer='tachyon') + sphere((1,0,0),0.2) @interact def example(theta=(0,2*pi), phi=(0,2*pi), zoom=(1,(1,4))): show(C.rotate((0,0,1), theta).rotate((0,1,0),phi), zoom=zoom) 
       

Click to the left again to hide and once more to show the dynamic interactive window

G = Graph() G.add_vertices(range(10)) G.add_cycle(range(20)[10:20]) G.add_cycle(range(10)) G.add_edge(10,6) show(G) P=graphs.PetersenGraph() show(P) 
       


Jak widać, mamy do dyspozycji sporo możliwości. Wiele osób zarzuca językowi Python, że jest wolny i może nie nadawać się do trudnych obliczeń. Autorzy pakietu to rozumieją i dostarczają zintegrowane metody optymalizacji. Najpopularniejszą jest Cython. Cython to modyfikacja rozwiązania PyRex, tłumacząca kod składniowo zbliżony do kodu Pythona na C i podłączająca wynik w locie. Poniżej przedstawiony jest przykład ilustrujący różnicę wydajności pomiędzy Pythonem zawartym w Sageu a Cythonem. W przypadku tym obliczamy sumę częściową szeregu geometrycznego, naiwnie wykonując sumowanie

def sage_one(n,a = 0.5): r = 0.0 for i in range(n+1): r = r+a^i return r 
       
%cython def cython_one(int n, float a = 0.5): cdef float r r = 0.0 for i in range(n+1): r = r+a**i return r 
time _ = sage_one(10^6) 
       
Time: CPU 20.26 s, Wall: 20.31 s
Time: CPU 20.26 s, Wall: 20.31 s
time _ = cython_one(10^6) 
       
Time: CPU 1.84 s, Wall: 1.84 s
Time: CPU 1.84 s, Wall: 1.84 s

Dodatkowo, jeśli nasze obliczenia mogą być wywołane niezależne dla różnych elementów listy, w bardzo prosty sposób możemy wykorzystać obliczenia wielowątkowe:

@parallel(ncpus=2) def f_two(n): return n*n 
       
pgen=f_two([1,2,3,4]); pgen 
       
<generator object __call__ at 0xcbbfe14>
<generator object __call__ at 0xcbbfe14>
w=list(pgen);w 
       
[(((1,), {}), 1), (((2,), {}), 4), (((3,), {}), 9), (((4,), {}), 16)]
[(((1,), {}), 1), (((2,), {}), 4), (((3,), {}), 9), (((4,), {}), 16)]
map(lambda x: x[1], sorted(w)) 
       
[1, 4, 9, 16]
[1, 4, 9, 16]
list(pgen) 
       
[]
[]

Dzięki dostępnym możliwościom optymalizacji znanych jest wiele przykładów, w których Sage jest o wiele szybszy niż konkurencja:

  • Wymnożenie liczb ({12345}^{678900} - 1) \cdot ({67890}^{123456} - 1), Sage 278ms, Mathematica 368ms
  • Silnia  10000000!, Sage 13.79s, Mathematica 19.29s
  • Rozkład na czynniki pierwsze 2^{512} - 1, Sage 92s, Mathematica 346s
  • Mnożenie macierzy 2000\times 2000, Sage 3.28s, Mathematica 5.95s, Matlab 5.68s
  • SVD macierzy 1000\times 1000, Sage 2.38s, Matlab 9.66s

Te różnice w szybkości przekładają się bezpośrednio na jeszcze większe różnice w przypadku operacji złożonych.

Ważnym pakietu Sage jest integracja z innymi pakietami. Zacznijmy od pakietu statystycznego R. Postaramy się stworzyć funkcję lm wyznaczającą krzywą regresji na płaszczyźnie na podstawie danych punktów.

wektor=r.rnorm(10); wektor 
       
 [1]  0.59047450  0.79568355 -1.14404062  0.28586726  0.01249535 
0.10794379  0.90248881 -2.16837694
 [9]  0.82525428  2.00835592
 [1]  0.59047450  0.79568355 -1.14404062  0.28586726  0.01249535  0.10794379  0.90248881 -2.16837694
 [9]  0.82525428  2.00835592
type(wektor) 
       
<class 'sage.interfaces.r.RElement'>
<class 'sage.interfaces.r.RElement'>
wektor2 = wektor._sage_() wektor2 
       
[0.59047449999511503, 0.79568355018906001, -1.14404061847399,
0.28586726491841202, 0.0124953462070257, 0.10794379455711101,
0.90248881154446503, -2.1683769384016101, 0.82525427922093797,
2.00835591894915]
[0.59047449999511503, 0.79568355018906001, -1.14404061847399, 0.28586726491841202, 0.0124953462070257, 0.10794379455711101, 0.90248881154446503, -2.1683769384016101, 0.82525427922093797, 2.00835591894915]
X=sageobj(r.rnorm(100)) Y=r.rnorm(100)._sage_() plot(points(zip(X,Y))) 
       
r.png() r.plot(X,Y, xlab='"x"', ylab="'y'") _ = r.dev_off() 
       
%r x <- rnorm(100) y <- rnorm(100, mean=5)*x+rnorm(100, mean=0.1) png() plot(y ~ x) abline(lm(y ~ x), lwd=2) .silent.me. <- dev.off() 
       
X=r("x")._sage_(); Y=r("y")._sage_(); 
       
data=plot(points(zip(X,Y))); data 
       
reg=r.lm("y~x"); reg 
       
Call:
lm(formula = sage179)

Coefficients:
(Intercept)            x  
    0.04433      4.96566  
Call:
lm(formula = sage179)

Coefficients:
(Intercept)            x  
    0.04433      4.96566  
reg.coefficients()._sage_() 
       
{'_Names': ['(Intercept)', 'x'], 'DATA': [0.0443253366825773,
4.9656562440455296]}
{'_Names': ['(Intercept)', 'x'], 'DATA': [0.0443253366825773, 4.9656562440455296]}
coefficients = reg.coefficients()._sage_()['DATA']; est(x) = coefficients[1]*x+coefficients[0] show(est) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}x \ {\mapsto}\ 4.96565624405 \, x + 0.0443253366826
\newcommand{\Bold}[1]{\mathbf{#1}}x \ {\mapsto}\ 4.96565624405 \, x + 0.0443253366826
estimated = plot(est(x),(-4,4),rgbcolor=(0,0,0)) show(data+estimated) 
       

Jesteśmy gotowi do stworzenia naszej funkcji. Ale jak zauważyliśmy całość zmian wykonywana jest w jednym interpreterze R. Jeśli kilka funkcji by z niego korzystało, moglibyśmy wpaść w konflikty. W tym celu stworzymy nowy interpreter. Niestety, przez przypadek nadpisaliśmy ważną dla pakietu R instrukcję:

type(R) 
       
<class
'sage.rings.polynomial.polynomial_ring.PolynomialRing_field'>
<class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_field'>

Całe szczęście nic straconego:

restore('R') inny_r = R() type(inny_r) 
       
<class 'sage.interfaces.r.R'>
<class 'sage.interfaces.r.R'>
def lm(x,y): """ simple linear regression in R, returns lambda """ inny_r('x <- c('+str(map(n,x))[1:-1]+')') inny_r('y <- c('+str(map(n,y))[1:-1]+')') c = inny_r.lm("y~x").coefficients()._sage_()['DATA'] return lambda x: (c[1]*x+c[0]) 
       
lm([1,2,3],[3,5,7])(x) 
       
2*x + 0.999999999999999
2*x + 0.999999999999999

Spróbujmy teraz wykorzystać naszą funkcję dla danych losowanych poza pakietem R. Skorzystamy z biblioteki Numpy:

from numpy.random import normal 
       
X = normal(0,1,100) Y = normal(5,1,100)*X+normal(0.1,1,100) fit(x) = lm(X,Y)(x) show(fit) plot(points(zip(X,Y))) + plot(fit,(min(X)-0.1,max(X)+0.1),rgbcolor=(0,0,0)) 
       
\newcommand{\Bold}[1]{\mathbf{#1}}x \ {\mapsto}\ 5.21383649003 \, x + 0.132870801998
\newcommand{\Bold}[1]{\mathbf{#1}}x \ {\mapsto}\ 5.21383649003 \, x + 0.132870801998

W wersji 4.6.1 występuje niewielki błąd powodujący, że wyświetlanie dokumentacji pakietu R nie jest możliwe, ale patch to naprawiający (#10860) czeka już na weryfikację. Na potrzeby tej prezentacji pakiet został uzupełniony o wyżej wymienioną poprawkę. Aby zastosować poprawkę wystarczy wykonać operację patch -p1 < ../(#ticket ID).patch wewnątrz katalogu sage/devel, a następnie wywołać komendę sage -b, które powoduje uaktualnienie wszystkich plików do odpowiednich wersji. W pakiecie Sage jest wiele innych elementów ułatwiających jego modyfikację, między innymi zintegrowany jest z pakietem Mercurial.

Niestety, błąd ten jest naprawiony tylko na serwerze http://sage.im.pwr.wroc.pl/.

r.lm? 
       

Traceback (click to the left of this block for traceback)
...
Traceback (most recent call last): File "", line 1, in File "_sage_input_2.py", line 10, in exec compile(u'print _support_.docstring("r.lm", globals(), system="sage") File "", line 1, in File "/home/sage/sage_install/sage-alpha/devel/sagenb/sagenb/misc/support.py", line 263, in docstring return html_markup(s) File "/home/sage/sage_install/sage-alpha/devel/sagenb/sagenb/misc/support.py", line 288, in html_markup return '
' + preamble + '
' + s + '
' UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 116: ordinal not in range(128)

Jeśli na komputerze, na którym zainstalowany jest Sage dysponujemy licencją na dany pakiet (np.: Mathematica, Matlab, Maple), możemy używać go z poziomu tego samego interfejsu i w taki sam sposób jak pakiety dołączone standardowo do Sagea. Spójrzmy na przykład wykorzystujący pakiet Mathematica:

%mathematica Integrate[Sin[x^2],{x,0,1}] 
       
             Pi                2
        Sqrt[--] FresnelS[Sqrt[--]]
             2                 Pi
             Pi                2
        Sqrt[--] FresnelS[Sqrt[--]]
             2                 Pi
var('x') wynik=mathematica.Integrate(sin(x),(x,0,1)); wynik 
       
1 - Cos[1]
1 - Cos[1]
type(wynik) 
       
<class 'sage.interfaces.mathematica.MathematicaElement'>
<class 'sage.interfaces.mathematica.MathematicaElement'>
wynik2=wynik._sage_() wynik2 
       
-cos(1) + 1
-cos(1) + 1
type(wynik2) 
       
<type 'sage.symbolic.expression.Expression'>
<type 'sage.symbolic.expression.Expression'>

Jak widać możliwości są ogromne. Na koniec małe podsumowanie

możliwości pakietu

  • Notebook, interaktywne środowisko obliczeń działające poprzez przeglądarkę.
  • Wizualizacja
    • wykresy dwu- oraz trój-wymiarowe, statyczne, animowane oraz interaktywne
    • wykresy klasyczne, parametryczne, słupkowe, konturowe, gęstości, obszarów, pól wektorowych
    • możliwość wrysowania dodatkowych elementów jak wektorów, linii, opisów, dysków, brył platońskich do wykresu poprzez operator dodawania
  • Obliczenia symboliczne
    • wyrażenia (w tym szeregi nieskończone, stałe, funkcje zdefiniowane przedziałami i funkcje specjalne) oraz relacje, rozwiązywanie równań i nierówności i operacje na nich, w tym dodawanie, mnożenie, dzielenie stronami, podstawianie, wymnażanie lub grupowanie w nawiasy, grupowanie do postaci wielomianowej, rozbijanie na czynniki, wyszukiwanie największego wspólnego dzielnika, itp
    • wprowadzanie oraz usuwanie założeń i sprawdzanie czy są spełnione
    • badanie rzędu wyrażeń (jak O(wyrażenie))
    • rozróżnianie i konwersja pomiędzy jednostkami miary, pojemności, siły, itp
    • różniczkowanie oraz całkowanie wyrażeń, wyliczanie gradientu, hesjan, transformat Laplace'a, szeregów potęgowych, wyliczanie granic
    • wyszukiwanie wzorców w wyrażeniach i stosowanie dla nich niestandardowych reguł zamiany
    • upraszczanie wyrażeń i relacji, stosując wzory trygonometryczne, postaci wielomianowe oraz wykładnicze i więcej
    • możliwość pracy większości metod nad dowolnym podanym przez użytkownika ciałem, lub gdzie jest to rozsądne pierścieniem czy inną strukturą
    • wyszukiwanie ekstremów oraz miejsc zerowych
    • numeryczne operacje na wyrażeniach symbolicznych
    • sporządzanie wykresów z automatycznym rozróżnieniem punktów nieciągłości
  • Teoria grafów
    • wiele możliwości wprowadzania oraz importowania grafów, również z zewnętrznych baz danych
    • grafy zorientowane, niezorientowane, z wagami, możliwość dodawania etykiet
    • dwu- i trój-wymiarowa wizualizacja grafu, starająca się automatycznie znaleźć najlepszą dwuwymiarową reprezentację
    • wiele standardowych, opisanych w literaturze grafów posiada swoje konstruktory (grafy dwudzielne, pełne, grafy Petersena, gwiazdy, siatki i wiele innych, łącznie kilkadziesiąt konstruktorów)
    • metody do kolorowania grafów, wyszukiwania klik, wyliczania różnych miar centralności, liczb i wielomianów chromatycznych, cykli Eulera i Hamiltona, różne metody wybierania podgrafów spełniających zadane warunki w tym drzew rozpinających, konwersja do relacji i ich badanie, różne metody przechodzenia grafów, wyszukiwania ścieżek i operacji na nich, badanie planarności i innych cech, sprawdzanie czy dwa grafy są izomorficzne i wiele innych, również zaawansowanych metod.
  • Kombinatoryka i symboliczny rachunek zdań
    • badanie spełnialności, sprzeczności, równoważności i tautologii, operacje na rachunku zdań
    • znane ciągi: liczby Bella, Bernoulliego, Catalana, Fibonacciego, Sterlinga, Lucasa, Eulera
    • operacje na zbiorach i multizbiorach, badanie rang, kardynalności
    • kombinacje, permutacje, kompozycje, iloczyny kartezjańskie i inne podstawowe narzędzia
    • kryształy, zbiory częściowo uporządkowane, funkcje symetryczne, ciała kombinatoryczne, alfabety, q-odpowiedniki, kwadraty łacińskie, typy, wstęgi, korzenie i operacje na nich i wielu innych strukturach.
  • Prawdopodobieństwo i statystyka
    • możliwość pracy w terminologii zmiennych losowych i przestrzeni, podstawowe statystyki opisowe
    • zoptymalizowane metody dla szeregów czasowych
    • dostęp do wszystkich metod i pakietów środowiska R, najpopularniejszego środowiska statystycznego, które jest jednym ze zintegrowanych pakietów
    • wciąż wiele do zrobienia, w pakiecie Sage w oficjalnym wydaniu dopiero od 2 lat i w testach od 3 lat (projekt "Statystyka i Sage" Koła Naukowego Statystyki Matematycznej Gauss przy Instytucie Matematyki i Informatyki Politechnice Wrocławskiej ma na celu załatać tę lukę).
  • Kryptografia, teoria kategorii, liczby p-adyczne, geometria kombinatoryczna, funkcje L, krzywe eliptyczne i hipereliptyczne, optymalizacja, teoria kodowania, podgrupy arytmetyczne, formy i wiele innych specjalistycznych działów
  • Możliwość doinstalowania dowolnej biblioteki do języka Python spośród dostępnych w sieci.
  • Wiele wiele więcej. Dowolny dział, o ile tylko znajdzie się specjalista z danej dziedziny chcący zaimplementować nowe metody, Sage rozwija się dzięki ochotnikom. Ponad 90 opublikowanych prac cytuje lub zawiera kod napisany w pakiecie Sage, co rok przyznawana jest nagroda dla osoby, która ma największy wkład w rozwój pakietu w danym roku.

Dziękuję za uwagę, może jakieś pytania?