Als allererste Zutat für symblische Ausdrücke müssen Variablen definiert werden.
Hierfür gibt es entweder die einfache from sympy.abc import x
Variante,
oder über die sympy.symbols
Funktion werden Name(n) und eventuell spezielle Abschätzungen ("ganzzahlig", ...) festgelegt.
from __future__ import division
import sympy as sy
from sympy.abc import x, y
type(x)
sympy.core.symbol.Symbol
Ein einfacher, erster Ausdruck:
x + y
x + y
... und dessen Typ ist (ein wenig überraschend?) Add
.
type(x + y)
sympy.core.add.Add
Das liegt daran, dass ein symbolischer Ausdruck ein Baum von Operationen mit Argumenten ist. Die Wurzel ist in diesem Fall die Addition, und die beiden Variablen sind die Argumente der Addition.
Zusammensetzung eines anderen, größeren Ausdrucks:
(2*x**2 + 3) / (y + 5)
(2*x**2 + 3)/(y + 5)
Zuweisung an eine Variable und Auswertung durch Substitution:
ex1 = (2*x**2 + 3) / (y + 5)
ex1.subs(x, 21)
885/(y + 5)
ex1.subs({x : 11, y : 3.14})
30.0982800982801
.evalf()
wertet Ausdrücke numerisch aus:
from sympy import pi, sin, sqrt
ex2 = sqrt(2 * sin(x)) / pi
ex2
sqrt(2)*sqrt(sin(x))/pi
ex2.evalf(subs = {x : 1})
0.412937855327941
ex2.evalf(100, subs = {x : 1})
0.4129378553279408383569413918993854348289426830297614114255099495993100683862339505790006113904633237
Sympy's "srepr
" Funktion zeigt den Aufbau des Ausdrucks als Komposition aller Funktionen an:
sy.srepr(ex1)
"Mul(Add(Mul(Integer(2), Pow(Symbol('x'), Integer(2))), Integer(3)), Pow(Add(Symbol('y'), Integer(5)), Integer(-1)))"
sy.pprint(ex1)
2 2⋅x + 3 ──────── y + 5
Sympy's latex()
Funktion konvertiert den Ausdruck zu einem entsprechenden LaTeX-Formel string.
Anschließend zeigt die Math()
Funktion von IPython diese LaTeX Formel hier im Notebook an.
sy.latex(ex1)
'\\frac{2 x^{2} + 3}{y + 5}'
from IPython.display import Math
Math(sy.latex(ex1))
Durch einmaliges Ausführen von init_printing()
werden alle SymPy Ausdrücke automatisch in $\LaTeX{}$ dargestellt.
from sympy import init_printing
init_printing()
ex1
Den Baum des symbolischen Ausdruckt kann man auch grafisch Darstellen.
Zuerst konvertiert dotprint
den Ausdruck in das dot
-Format zur Visualisierung von Graphen,
und dann wird es mit dem passenden Linux Utility gerendert und dargestellt.
def expr_to_dotgraph(name, expr):
from sympy.printing.dot import dotprint
# input and output filenames
import os
fname = os.path.join("res", name)
fname_dot = fname + ".dot"
fname_svg = fname + ".svg"
# writing the output of dotprint to the given file
with open(fname_dot, "w") as dot_file:
dot_file.write(dotprint(expr))
# rendering the *.dot to *.svg
! dot -Tsvg -o $fname_svg $fname_dot
# showing the resulting *.svg file here in the notebook
from IPython.display import SVG
return SVG(filename = fname_svg)
expr_to_dotgraph("sympy-ex1", ex1)
Ganzzahlig: $i \in \mathbb{Z}$:
i = sy.symbols("i", integer=True)
i.is_integer
True
(3*i + 1).is_integer
True
print(i.is_nonnegative)
None
Reel und nicht negativ: $t \in\mathbb{R}\;\bigwedge\;t \geq 0$
t = sy.symbols("t", real=True, nonnegative=True)
print((3*t).is_nonnegative)
True
print(t.is_real)
True
print((i * t).is_integer)
None
# squared value plus non-negative variable is non-negative
print((i**2 + t).is_nonnegative)
True
Hiervon gibt es verschiedene Varianten, je nach dem welche Art der Vereinfachung vorgenommen werden soll.
.simplify()
: probiert intern einige Varianten, bewertet die Verbesserungen (wenn vorhanden), und sucht die beste aus..expand()
: Ausmultiplizieren, ....collect(<variable>)
: Herausziehen eines oder mehrerer Faktoren bzw. Subausdrücke.factor()
: Faktorisieren.cancel()
: Kürzen.trigsimp()
: Trigonometrische Formelnex2 = (1 - x) * (4 + 5 * x) * (1 + x)
ex2
ex3 = ex2.expand()
ex3
ex3.factor()
ex5 = sy.sin(1 + x)**2 / sy.cos(1+x)**2
ex5
ex5.trigsimp()
a, b = sy.symbols('a b', positive=True)
ex6 = sy.log(a) + sy.log(b) + sy.log(1 + a) - sy.log(1/b)
ex6
ex6.simplify()
ex7 = (3*x**2 + x) / x
ex7
ex7.cancel()
ex8 = a*sy.log(x) + b*sy.cos(3*x) + t * sy.log(x)
ex8
ex8.collect(sy.log(x))
Die Gegenüberstellung zweier Ausdrücke ergibt eine Gleichung.
SymPy ist hier etwas unfreundlich und verlangt die Verwendung des sympy.Eq
Operators.
eq1 = sy.Eq(3 * x, 9 * x**2 - 2*x - 1)
eq1
Linke- und Rechte-Seite mittels .lhs
und .rhs
:
eq1.rhs
... was wieder einen Ausdruck (siehe oben) liefert, in dem z.B. Substituiert werden kann.
eq1.lhs.subs({x : 42})
solve
¶Eine der Standardanwendungen symbolischer Software is das symbolische Lösen von Gleichungen bzw. Gleichungssystemen.
eq1_sol = sy.solve(eq1)
eq1_sol
Lösung 1 einsetzen:
eq1_sol1 = eq1.subs({x:eq1_sol[0]})
eq1_sol1
Ein ganzes Gleichungssystem lösen.
(Die rechte Seite nach links bringen erspart den Aufruf von Eq( ... )
)
from sympy.abc import x, y, z
eqn3 = [2 * x**2 - y - 2,
3 * x - y - z + 4,
x + y - z + 8]
sol3 = sy.solve(eqn3)
for sol in sol3:
subsol = [e.subs(sol).simplify() for e in eqn3]
print("%-30s -> eingesetzt: %s" % (sol, subsol))
{x: 0, z: 6, y: -2} -> eingesetzt: [0, 0, 0] {x: 1/2, z: 7, y: -3/2} -> eingesetzt: [0, 0, 0]
Der fundamentale Unterschied zu normalen Gleichungen ist,
dass es symbolische Variablen für Funktionen gibt: $f$, $g$, ...
Diese werden mit der symbols
Funktion erzeugt,
deren cls
(Klasse) auf Function
gesetzt wird,
bzw. das Argument function = True
angegeben wird.
Dadurch lässt sich eine Funktion $f(x)$ ganz allgemein in Python durch f(x)
konstruieren.
from sympy import symbols, Function, Eq
f = symbols("f", cls=Function)
f(x)
dgl1 = Eq(f(x) + f(x).diff(x), y)
dgl1
from sympy import dsolve
dsolve(dgl1, f(x))
dgl2 = f(x).diff(x, x) + f(x)
dgl2
dsolve(dgl2, f(x))
Ähnlich zu Gleichungen, können auch Ungleichungen behandelt werden.
from sympy.solvers.inequalities import solve_univariate_inequality
from sympy import symbols
x = symbols("x", real=True)
solve_univariate_inequality(x**2 + 1 >= 3, x)
solve_univariate_inequality(x**2 + 1 >= 3, x, relational=False)
from sympy import sin
solve_univariate_inequality(sin(x) > 0.5, x, relational=False)
from sympy.solvers.diophantine import diophantine
from sympy import symbols
a, b = symbols("a, b", integer=True)
diophantine(2 * a + b)
diophantine(2 * a + 3 * b**2 - 10)
Die Definition des Limes:
$$\lim_{x\to a}f(x)=L \; \text{iff} \; \forall\; \epsilon>0\; \exists\; \delta \; \text{such that} \; \forall\; x: |x-a|<\delta \Rightarrow |f(x)-L|\leq \epsilon $$from sympy import limit
from sympy.abc import x
f = (3 * x**3) / (1 - 2*x**2 + x**3)
Numerische Approximation
f.subs(<variable>, <wert>)
substituiert hier die angegebene Variable mit dem gegebenen Wert;
und .evalf()
wertet den Ausdruck aus.
for x_val in range(1000, 20000, 1000):
fx = f.subs(x, x_val).evalf()
print("%7d -> %14.11f" % (x_val, fx))
1000 -> 3.00601202104 2000 -> 3.00300300263 3000 -> 3.00200133411 4000 -> 3.00150075033 5000 -> 3.00120048017 6000 -> 3.00100033343 7000 -> 3.00085738782 8000 -> 3.00075018754 9000 -> 3.00066681484 10000 -> 3.00060012002 11000 -> 3.00054555373 12000 -> 3.00050008335 13000 -> 3.00046160948 14000 -> 3.00042863266 15000 -> 3.00040005334 16000 -> 3.00037504688 17000 -> 3.00035298270 18000 -> 3.00033337037 19000 -> 3.00031582272
Bemerkung: SymPy's oo
steht für $\infty$
from sympy import oo
limit(f, x, oo)
limit(f, x, -oo)
limit(f, x, 0)
Dies ist auch mit mehrstelligen Funktionen machbar:
from sympy import sin
from sympy.abc import x, y
f2 = (sin(x) + x**2 * y**2 - x**3 * y**3) / (1 - x - 2 * (x + 2 * y)**3)
limit(f2, x, oo)
limit(f2, y, oo)
Bei Polstellen ist es wichtig, von welcher Seite der Limes angegangen wird:
f_pol = 1 / (-27 + x**3)
limit(f_pol, x, 3, dir="+")
limit(f_pol, x, 3, dir="-")
Ein Ausdruck wird an einem bestimmten Punkt entwickelt.
from sympy import sin
from sympy.abc import x
f3 = sin(x) * x
f3
An der Stelle $x_0 = 0$ mit 10 Gliedern:
f3_series_0 = f3.series(x = x, x0 = 0, n = 10)
f3_series_0
An der Stelle $x_0 = \pi$ mit 5 Gliedern:
f3_series_pi = f3.series(x = x, x0 = sy.pi, n = 5)
f3_series_pi
f3_series_pi.expand()
Sympy kann bestimmte und unbestimmte Integrale berechnen.
from sympy import integrate, cos, pi, exp, sqrt
from sympy.abc import x
integrate((1 - x**2) * cos(x), x)
from sympy.abc import t
integrate((1 - x**2) * cos(x), (x, -pi, pi))
from sympy.abc import y
exp2 = exp(-x**2)*exp(-y**2)
exp2.simplify()
exp2
Integral der Funktion $\operatorname{exp2}(x,\,y)$ nach $y$
integrate(exp2, y)
Bestimmtes Integral:
$$\int_{-\infty}^{\infty}\!\int_{-\infty}^{\infty}\!\!\operatorname{exp2}(x,\,y)\,\mathrm{d}x = ?$$from sympy import oo
integrate(exp2, (x, -oo, oo), (y, -oo, oo))
Viele der bislang vorgestellten Funktionen haben äquivalent dazu Kontruktoren für Ausdrücke.
Diese können wie gehabt dargestellt werden,
es sind Substitutionen möglich,
und werden mit der .doit()
Methode ausgewertet.
from sympy import exp, sin, Limit, latex, oo
from sympy.abc import x
limit_expr = Limit(1 - exp(1 / x), x, oo)
limit_expr
limit_expr.doit()
from sympy import Integral
from sympy.abc import y
from IPython.display import Math
int_expr = Integral((1 + x + y) / (4-y**2) , x)
int_expr2 = int_expr.subs(y, 1+x)
Math(latex(int_expr2) + "=" + latex(int_expr2.doit()))
int_expr_xy = Integral(int_expr, y)
int_expr_xy
int_expr_xy.doit()
Extrahieren des symbolischen Ausdrucks aus dem nicht-evaluierten Integral int_expr
via .args[0]
.
Daraus wird ein bestimmtes Integral über x
und y
zusammengebaut:
int_expr_i2 = Integral(int_expr.args[0], (x, -1, 1), (y, -1, 1))
int_expr_i2
int_expr_i2.doit()
int_expr_i2.doit().evalf()
Der interne Graph des Integralausdrucks int_expr
:
expr_to_dotgraph("sympy-int", int_expr)