Számos kereskedelmi (Mathematica, Maple) számítógépes algebrai csomag létezik már, melyek bonyolult analitikusformula-manipulációs készséggel rendelkeznek. A sympy
csomag egy ingyenes nyílt forráskódú python
modul, ami használható alternatívát kínál kereskedelmi vetélytársaival szemben. Ebben a notebookban néhány sympy
függvénnyel fogunk megismerkedni.
Mielőtt fejest ugranánk a modul ismertetésébe, álljon itt az alábbi két kép figyelmeztetésül:
vs. |
Általában ugyanis igaz az, hogy a hétköznapi felhasználók (alapvetően diákok) hajlamosak túlságosan megbízni ezekben a formulamanipulációs programcsomagokban. Sokszor hallani oktatási intézmények folyosóin, hogy: 'Még a Mathematica se tudta kiszámolni!' Ezek a programcsomagok nem mindentudó orákulumok! Alapvetően inkább mankóként tekintsünk rájuk, amit a saját készségeinkhez próbálunk igazítani, s nem mint egy mindent tudó feketedobozra amely minden kérdésre tévedhetetlenül a pontos választ adja.
Fontos megjegyezni, hogy mivel van pár, a sympy
függvényeihez hasonló nevű függvény a pylab
parancs által importált modulokban, ezért a kellemetlenségek elkerülése végett célszerű - legalábbis a házi feladatok megoldása során - mindig külön notebookot indítani a sympy
-os problémák megoldásánál!
Ezen felül a már megszokott %pylab inline
helyett %matplotlib inline
-al kezdjük a notebookokat, hogy az esetlegesen megjeleníteni kívánt ábrák jól működjenek!
# Figyelem az alábbi parancs csak az ábrázolásra készíti fel a környezetet
%matplotlib inline
# Ez a két parancs pedig a szimbolikus számításra
from sympy import * # a sympy csomag rutinjainak betöltése
init_printing() # szép kimenet
Ahhoz, hogy változókat szimbolikusan is manipulálni tudjunk, meg kell mondanunk a Pythonnak, hogy ezentúl tekintsen a változónkra mint valamilyen matematikai formulában előforduló szimbólumra. Ezt a legegyszerűbben így tehetjük meg:
x=symbols('x')
Ezek után az $x$ változót használhatjuk szimbolikus számításokra. Oldjuk meg például a következő egyszerű egyenletet: $$3x=5$$
Ezt a solve
függvény segítségével tehetjük meg. A solve
függvény első bemenete a megoldandó egyenlet 0-ra rendezve, a második pedig a keresett változó:
solve(3*x-5,x)
Definiáljunk néhány további változót is!
y,z,a,b,c=symbols('y,z,a,b,c') # Így definiálunk egyszerre több változót
k,l=symbols('k,l',integer=True) # Így specifikáljuk hogy a defiiált változók egész számok
p,q=symbols('p,q',real=True) # Így pedig hogy valósak
Oldjuk meg most az $$a x+b=y$$ egyenletet $x$ -re!
solve(a*x+b-y,x)
Ha több megoldása is van az egyenletnek, akkor a sympy
solve
függvénye lehetőség szerint mind a kettőt megtalálja. Jól mutatja ezt a másodfokú egyenlet megoldásának "megtalálása":
solve(a*x**2+b*x+c,x)
Természetesen a megoldás nem feltétlenül valós szám! A komplex egységgyököt a sympy
I
-vel jelöli:
I**2
A solve
függvényt egyenletrendszerek megoldására is lehet használni. Ilyenkor a nullára rendezett egyenleteket listába foglaljuk. A keresett változókat szintúgy. Oldjuk meg a következő egyenletrendszert az $x$ és $y$ változókra:
$$y=x^2+ax-4b$$
$$y=x-b$$
Mivel $x$-ben másodrendű az első egyenlet, ezért két megoldáspárt várunk (A parabola legfeljebb két helyen metszi az egyenest...)
solve([x**2 + a*x -4*b - y, x - y - b],[x,y]) # Így kell egyenletrendszert megoldani
A solve
függvény kimenete most egy lista, amelynek az elemei a megfelelő $(x,y)$ párok.
Sokszor előfordul, hogy analitikus számítások során a képletek egyszerűsítése végett egy-egy gyakran ismétlődő kifejezésre az ember bevezet egy rövidítést. A számolás befejeztével ezeket a rövidítéseket vissza kell helyettesíteni az eredeti megfelelőikkel a végeredménybe. Ilyen és ehhez hasonló helyettesítésekre alkalmazható a subs()
metódus, mely a sympy
minden kifejezés objektumának metódusa. Lássunk erre egy példát! Legyen egy számolás eredménye $x^2+ax-y$, és legyen ez az eredmény a valami
változóban eltárolva:
valami=x**2 + a*x - y
valami
Tegyük most fel, hogy korábban az $y$-t az $x^3$ kifejezés helyettesítésére vezettük be, és most vissza akarjuk vezetni. Ekkor a subs
metódus az alábbiak szerint alkalmazható:
valami.subs(y,x**3)
A számítások végrehajtásának utolsó lépése, hogy numerikusan kiértékeljük az eredményeket. Ezt a sympy
-ban az evalf()
metódus segítségével tehetjük meg. Például:
ize=sqrt(8)
ize.evalf()
Az evalf
metódust egyetlen egész számú argumentummal meghívva tudjuk az eredmény elvárt numerikus precizióját befolyásolni:
ize.evalf(40)
Ha a kifejezésünk több ismeretlent tartalmaz, akkor az evalf
-nak a subs
kulcsszavas argumentumába szótárszerűen felsorolhatjuk a kívánt numerikus behelyettesítési értékeket:
ize2=sin(2*x)+cos(y)
ize2.evalf(12,subs={x:0.5,y:0.3})
A sympy
egyik legnagyobb előnye, hogy a Python
nyelven belül lehetővé teszi egyszerűbb analízisbeli feladatok elvégzését. Alább a teljesség igénye nélkül összefoglalunk néhány egyszerű függvényt. Vizsgáljuk meg a következő két határértéket: $$\lim_{x\rightarrow 0}\frac{\sin x}{x}=? $$ illetve $$\lim_{x\rightarrow \infty}\frac{1}{1+\mathrm{e}^{-x}}=? $$ A határértéket a limit
függvény segítségével tudjuk meghatározni:
limit(sin(x)/x,x,0)# sin(x)/x határértéke az x=0 pontban.
Ha a végtelenben vagyunk kíváncsiak a határértékre, akkor azt az oo
-szimbólummal tudjuk elérni!
limit(1/(1+exp(-x)),x,oo)
Egy kifejezés deriváltjait a diff
függvény segítségével tudjuk meghatározni. Például a $\sin$ függvény első $x$-szerinti deriváltja:
diff(sin(x),x)
A második deriváltat vagy így
diff(sin(x),x,x)
vagy így, (talán egy kicsit átláthatóbban) írjuk.
diff(sin(x),x,2)
Természetesen parciális deriváltak elvégzésére is van mód:
diff(sin(x)*cos(y),x,y)
A magasabb rendű parciális deriváltak legyártása az egyszerű deriváltak általánosításán alapszik:
diff(sin(x)*cos(y),x,2,y,3)
Az integrate
függvény segítségével határozott és határozattlan integrálokat tudunk elvégezni.
Határozzuk meg először az $x^2$ primitív függvényét:
integrate(x**2,x)
Az $$\int_0^3 x^2\mathrm{d}x$$ határozott integrált pedig az alábbi módon értékelhetjük ki.
integrate(x**2,(x,0,3))
Természetesen az integrálás során szerepelhetnek a kifejezésben más paraméterek is:
integrate(x**2+y**3,(x,0,3))
Többváltozós integrált egyszerűen a változók (és ha határozott integrálról van szó, akkor az integrálási határok) egymás után írásával értékelhetünk ki:
A $$\int x^2+y^3 \mathrm{d}x\mathrm{d}y $$ határozatlan integrál:
integrate(x**2+y**3,x,y)
A $$\int_0^3\int_{-3}^{5} x^2+y^3 \mathrm{d}x\mathrm{d}y $$ határozott integrál:
integrate(x**2+y**3,(x,0,3),(y,-3,5))
A sympy
modul segítségével a meghatározott analitikus függvényeket ábrázolni is tudjuk. Erre a sympy a már korábban megismert matplotlib
csomag függvényeit használja, DE egy kicsit MÁS SZINTAKTIKÁT követve! Az alapvető különbség a két csomag között, hogy míg a matplotlib
szigorúan csak numerikus adatokat tud kezelni, a mintavételezésről és a függvény kiértékeléséről a felhasználó gondoskodik, addig a sympy
-nak elég magát az analitikus kifelyezést megadni, és maga gondoskodik az ábrázolás létrehozásához szükséges numerikus értékeket tartalmazó struktúrák létrehozásáról.
Az alábbiakban a sympy
modul plotting
almodulja segítségével síkbeli és térbeli analitikus görbék és alakzatok megjelenítésére látunk néhány példát:
from sympy.plotting import * # A modul betöltése
Az plot
függvény egyváltozós skalár kifejezések megjelenítését teszi lehetővé.
plot(1/(exp(x)+1),(x,-10,10))
A plot_parametric
síkbeli általános parametrikus görbék ábrázolására szolgál
plot_parametric(sin(x),cos(x),(x,0,2*pi))
Térbeli görbék a plot3d_parametric_line
függvénnyel jeleníthetők meg.
plot3d_parametric_line(cos(3*x),sin(3*x),x,(x,0,2*pi))
Térbeli felületek a ábrázolására a plot3d
és plot3d_parametric_surface
függvények szolgálnak:
plot3d(sin(x)*cos(y),(x,-pi,pi),(y,-pi,pi))
u, v = symbols('u v')
plot3d_parametric_surface(sin(v)*cos(u),sin(v)*sin(u),cos(v), (u, 0, 2*pi), (v, 0, pi))
simplify
fügvény¶A legáltalánosabb egyszerűsítő függvény a simplify
, mely különösebb specifikációk nélkül arra törekszik, hogy a hasában szereplő kifejezést a lehető legtömörebb alakra hozza. Lássunk erre néhány példát:
simplify(sin(x)**2 + cos(x)**2)
simplify((x**3 + x**2 - x - 1)/(x**2 + 2*x + 1))
Láttuk, hogy a fenti két példában valóban tömörebbre sikerült hozni egy-egy bonyolultabb kifejezést, lássunk egy ellenpéldát is! A simplify
az alábbi, talán sokak számára könnyen egyszerűsíthető kifejezést nem egyszerűsíti tovább:
simplify(x**2 + 2*x +1)
A fenti jelenség oka az, hogy algoritmikusan nehéz jól definiálni az 'egyszerűsítés' fogalmát. Ha kicsit jobban kifejtjük, hogy milyen egyszerűsítésre gondolunk, akkor már könnyebb dolga van a sympy
-nak is. Az alábbiakban megismerkedünk néhány olyan függvénnyel, ami kifejezések specifikus egyszerűsítésére vagy éppen kibontására ad lehetőséget.
Az expand()
függvény segítségével legegyszerűbb esetben polinomiális kifejezéseket bonthatunk ki:
expand((x+y)**3)
A factor()
függvény talán egy kicsit az expand()
függvény ellenkezője. A factor
függvény a kapott kifejezést a lehető legtömörebb szorzatalakba próbálja hozni. Így például a simplify
által nem egyszerűsített kifejezés a factor()
segítségével:
factor(x**2 + 2*x +1)
a várt egyszerűbb alakra alakul.
Általánosabb többváltozós polinomiális kifejezések manipulálása során előfordul, hogy egy bizonyos változó szerint szeretnénk a polinomot rendezni. Erre való a collect()
függvény. Lássunk erre is egy példát:
kifejezes=x*y + x - 3 + 2*x**2 - z*x**2 + x**3
collect(kifejezes,x)
xek=collect(kifejezes,x)
A coeff()
metódussal pedig megkaphatjuk a polinom megfelelő hatványkitevőihez tartozó együtthatókat:
xek.coeff(x,2)
A cancel()
függvény racionális (polinom/polinom) kifejezések egyszerűsítésére szolgál:
cancel((x*y**2 - 2*x*y*z + x*z**2 + y**2 - 2*y*z + z**2)/(x**2 - 1))
Az apart()
a parciális törtekre bontást végző függvény:
expr = (4*x**3 + 21*x**2 + 10*x + 12)/(x**4 + 5*x**3 + 5*x**2 + 4*x)
expr
apart(expr)
Trigonometrikus függvényeket tartalmazó kifejezések kezelésére két metódus szolgál:
A trigsimp()
trigonometrikus azonosságokat figyelembe véve a lehető legtömörebb kifejezés előállítására törekszik:
expr=sin(x)**4 - 2*cos(x)**2*sin(x)**2 + cos(x)**4
expr
trigsimp(expr)
A expand_trig()
függvény pedig olyan formulákká alakítja a trigonometrikus függvényeket tartalmazó kifejezéseket melyek argumentumai a lehető legegyszerűbbek... cserébe természetesen a végeredmény hosszabb lesz.
expand_trig(cos(4*x))
A rewrite
metódus a sympy
kifejezések általánosabb átírását teszi lehetővé.
Alkalmazható például trigonometrikus kifejezések boncolgatására:
tan(x).rewrite(sin)
vagy trigonometrikus és exponenciális függvényeket tartalmazó kifejezések egymásba alakítására:
sin(x).rewrite(exp)
de más a sympy
-ban implementált függvény azonosságokat kihasználó formulamanipulációra is:
factorial(x).rewrite(gamma) # a faktoriális és a gamma függvény kapcsolata
binomial(x,y).rewrite(factorial) # a binomiális eggyütthatók és a faktoriális kapcsolata
A fentiekben a sympy
formula manipulációs képességeit csak nagyon felületesen vizsgáltuk meg. Bővebb információért érdemes a megfelelő függvények dokumentációit, illetve magát a sympy
documentációt böngészni!
A sympy
segítségével paraméteres lineáris algebrai problémákat is megoldhatunk. Ilyen problémák kezelésére a sympy
modul a Matrix
osztályt vezeti be. Figyelem, nem összekeverendő ez a modul a numpy
numerikus matrix
, illetve array
osztályaival!
Az alábbiakban egy pár példán keresztül illusztráljuk a szimbolikus mátrixok létrehozását és a rajtuk végrehajtható műveletek viselkedését.
Hozzunk létre két egyszerű 2x2-es mátrixot!
M=Matrix([[0, -x*exp(I*y)],
[-x*exp(-I*y), 0]])
M
B=Matrix([[1,2],
[4,5]])
B
Figyeljük meg, hogy az M
mátrix elemei maguk is tartalmaznak sympy
változókat, míg a B
mátrix csak numerikus értékeket tartalmaz.
A két mátrix a várható módon viselkedik, például össze tudjuk őket adni:
M+B
vagy össze tudjuk őket szorozni:
M*B
Természetesen a szorzás eredménye függ a sorrendtől!
B*M
Mátrixokra a hatványozás művelet nem elemenként, hanem mátrixhatványozásként hat!
M**2
Mátrix inverzét is meg tudjuk határozni a **
-operátorral:
B**(-1)
Létezik néhány függvény, ami a mátrixok létrehozását szolgálja. Az eye()
tetszőleges méretű egységmátrixot hoz létre:
eye(3)
A diag()
függvény diagonális struktúrájú mátrixok gyártására való:
diag(sin(x),y,z)
Ahhoz, hogy néhány alapvető lineáris algebrai feladatot meg tudjunk oldani, szükség lesz oszlop- illetve sorvektorokra is. Ezeket is le tudjuk gyártani a Matrix
osztály segítségével:
v=Matrix([[1,2]])
v
w=Matrix([[1],[2]])
w
A mátrix-vektor szorzás a már megszokott módon történik:
B*w
v*B
Mátrixokkal és vektorokkal megadott lineáris egyenletrendszerek megoldására kínál lehetőséget a Matrix
osztály solve()
metódusa. Például a fent definiált B
és w
változók által meghatározott $$Bx=w$$ lineáris problémát az alábbi módon oldhatjuk meg:
B.solve(w)
Egy mátrix determinánsa a det
, a nyoma a trace
metódussal számítható:
M.det()
B.trace()
A mátrix transzponáltja, illetve adjungáltja a .T
, illetve .H
konstrukcióval érhető el:
M.T
M.H
A fenti eredményben a változók fölé húzott vonal a komplex konjugáltat jelöli.
Egy mátrix sajátérték problémájának vizsgálatára az eigenvals()
és eigenvects()
metódusok szolgálnak.
Az eigenvals()
egy dict-tel tér vissza, mely a sajátértékeket kulcsszóként tartalmazza, a kulcsokhoz tartozó érték pedig a sajátérték degenerációjának fokát jelzi:
M.eigenvals()
Az eigenvects()
a sajátértékeket, a multiplicitásukat és a sajátértékhez tartozó sajátvektorokat adja vissza:
M.eigenvects()