Octave

Az octave a matlab ingyenes utánzata. Egyaránt telepíthető linuxra, windowsra, és használható a jupyter rendszerben is. A python-hoz hasonlóan nagy előnye, hogy egy utasításban tud műveleteket hatékonyan végezni adatsorok, vektorok illetve mátrixok összes elemével.

Néhány hasznos link:

Alapok

A octave-ot a python-hoz hasonlóan használhatjuk számológépként:

In [1]:
1+2
ans =  3
In [2]:
3*4.0
ans =  12

octave-ban a "^"-al hatványozunk:

In [3]:
2^3 # Figyelem itt így hatványozunk!
ans =  8

Komplex számokat az i-betűvel jelöljük:

In [4]:
1i*1i
ans = -1

Változókat itt is az =-jellel deklarálunk:

In [5]:
x=7/4
x+1/x
x =  1.7500
ans =  2.3214

A változók itt is lehetnek számok vagy karakterláncok:

In [6]:
x=1/55 # ez egy szám
s='Hy' # ez egy string
x =  0.018182
s = Hy

Fontos különbség a python-hoz képest hogy bool típusú változók külön nincsenek definiálva. A 0 és 1 egész számok reprezentálják a True és False értékeket:

In [7]:
2*2==4
ans =  1

Ahogy azt a fenti példák mutatjk az octave minden sor után produkál kimenetet. Egy sor kimenetét akkor nem írja ki az interpreter ha ";"-vel zárjuk. Egyébként pedig egy sorbeli utasítások vesszővel és pontosvesszővel is elválaszthatók:

In [8]:
x=4, y=3^2, 5*5
p=1/2; q=2*3;
x =  4
y =  9
ans =  25

Az értékadás nélküli kifejezés az 'ans' speciális változónak való értékadást generál, amit következő utasításokban felhsználhatunk. A 'who' paranccsal pedig lekérdezhetjük a már létrehozott változókat:

In [9]:
who
Variables in the current scope:

ans  p    q    s    x    y

Range és for

A python-beli rang() függvényt az octave-ban az eleje:vege konstrukció implementálja:

In [10]:
(1:5)
ans =

   1   2   3   4   5

A beadott értékek persze nem csak egészek lehetnek. Lépésközt 3 elemű ":" kifejezés középső elemében adhatjuk meg. ilyenkor a sorozat utolsó eleme az lesz, ami még nem lépett túl a limitként megadott értéken:

In [11]:
x = 2.48:-0.111:2
x =

    2.4800    2.3690    2.2580    2.1470    2.0360

For ciklust írhatjuk egy vagy több sorba. A tabulálás itt nem számít, de hasznos. Itt is a ';'-t használjuk, ha csak a végeredményt akarjuk látni. Példaként számoljuk ki első néhány egész hatványát, összegüket és szorzatukat:

In [12]:
for i=1:5 i^3 endfor
ans =  1
ans =  8
ans =  27
ans =  64
ans =  125
In [13]:
s=0;
p=1;
for i=1:10
  s=s+i;
  p=p*i;
endfor
s
p
s =  55
p =  3628800

Számtani és mértani sorozatok pedig a linspace és logspace függvényekkel is állíthatók elő, a pythonnal azonos módon:

In [14]:
ari=linspace(0.5,3,5)
geo=logspace(0.5,3,5)
ari =

   0.50000   1.12500   1.75000   2.37500   3.00000

geo =

      3.1623     13.3352     56.2341    237.1374   1000.0000

if

Az octave-ban sok más programnyelvhez hasonlóan az if konstrukció segítségével állítjuk döntések elé a számítógépet. Az alábbi példa illusztrálja az if- elseif-else konstrukció legálltalánosabb alakját, ha nincs szükség az else illetve az elseif ágakra akkor azok természetesen elhagyhatóak.

In [15]:
x=6
if (rem (x, 2) == 0)
  printf ("x is even\n");
elseif (rem (x, 3) == 0)
  printf ("x is odd and divisible by 3\n");
else
  printf ("x is odd\n");
endif
x =  6
x is even

Tömbök

Számokból vagy stringekből itt is alkothatunk tömböket, de fontos eltérés, hogy nincs külön list, tuple, array, matrix típus, amikre bizonyos műveletek a pythonban másképp működnek. Itt a műveleti jel elé tett ponttal lehet kérni az adott művelet elemenkénti változatát (ld. később). Másik eltérés, hogy itt egy tömbben egyszerre vagy csak számok, vagy csak stringek lehetnek. De ehhez nézzük előbb, hogyan lehet létrehozni elemeik felsorolásával:

Sorvektor, vagy adatsor az adatok közé tett ','-vel vagy spaceszel is képezhető.

In [16]:
t=[ 1, 3, 5, 7, 9]
u=[ 2 4 6 8]
t =

   1   3   5   7   9

u =

   2   4   6   8

Mátrix sorait ';'-vel vagy sortördeléssel kell elválasztani (extra [ ] nem szükséges).

In [17]:
M=[ 1, 2, 3; 2,3,2; 3 2 1]
R=[ 1  2  3
    4  5 -6]
M =

   1   2   3
   2   3   2
   3   2   1

R =

   1   2   3
   4   5  -6

Adjungálást (transzponálás konjugálással) az aposztróf jellel végezhetünk, így oszlopvektort 3-féleképp tudunk létrehozni legegyszerűbben:

In [18]:
v=[1;2;-2]
w=[3,-4,5]'
b=[-5
  -12
   13]
v =

   1
   2
  -2

w =

   3
  -4
   5

b =

   -5
  -12
   13

In [19]:
[1i 2 3]'   #figyelem itt van konjugalas
ans =

   0 - 1i
   2 - 0i
   3 - 0i

In [20]:
transpose([1i 2 3]) # ez csak sima transzponalas!
ans =

   0 + 1i
   2 + 0i
   3 + 0i

A tömbök méretét a size és length függvényekkel kérdezhetjük le.

In [21]:
size(R), size(v), length(v)
ans =

   2   3

ans =

   3   1

ans =  3

Azonos méretű tömbök egymással való összeadása, kivonása itt természetesen az elemenkénti művelet. Tömbnek számmal való szorzása, osztása is elemenkénti (érdekesség, hogy szám hozzáadása is).

A szorzások és hatványozások viszont a vektorszámításban tanult értelmezés szerintiek, láthatóan különböznek az elemenkénti művelettől, amit a *,/,^ jelek elé tett ponntal tudunk kérni:

In [22]:
M
M =

   1   2   3
   2   3   2
   3   2   1

In [23]:
#Ez matrix szorzás!!
M*M
ans =

   14   14   10
   14   17   14
   10   14   14

In [24]:
# Ez elemenkénti szorzás!!
M.*M
ans =

   1   4   9
   4   9   4
   9   4   1

In [25]:
# ez matrix hatvanyozas
M^3
# ez elemenkenti hatvanyozas
M.^3
ans =

    72    90    80
    90   107    90
    80    90    72

ans =

    1    8   27
    8   27    8
   27    8    1

In [26]:
2^M, 2.^M
ans =

   24.760   28.304   24.510
   28.304   35.117   28.304
   24.510   28.304   24.760

ans =

   2   4   8
   4   8   4
   8   4   2

Vektorok skaláris és vektoriális szorzatára pedig van külön függvény, a dot és a cross.

Nagyon fontos 3 eltérés:

  • a tömbök indexe nem 0-val, hanem 1-gyel kezdődik
  • tömbök eleminek kiolvasásánál kerek zárójel kell
In [27]:
z=v(3), c=M(1,3)
z = -2
c =  3
  • Itt is lehet szeletelni, azonban a : utáni szám az index tényleges utolsó értékét jelöli:
In [28]:
M, bc=M(1,2:3), row12=M(1:2,:)
M =

   1   2   3
   2   3   2
   3   2   1

bc =

   2   3

row12 =

   1   2   3
   2   3   2

Itt is kereshetünk bennük minimumot, maximumot. Ha két eredményt kérünk, akkor megkapjuk a szélsőértékhez tartozó indexet is. Octaveban ez úgy lehetséges, hogy listát alkotunk azokból a változókból, amelyekbe az eredményeket akarjuk tenni. A szögletes zárójel tehát itt, az egyenlőségjeltől balra más jelentéssel bír, mint jobbra, amikor tömböt jelölt.

In [29]:
minbc=min(bc)
[maxbc,imaxbc]=max(bc)
minbc =  2
maxbc =  3
imaxbc =  2

Mátrix generáló függvények

Az eye, zeros, ones, rand, diag függvények hasonlóan működnek a pythonhoz:

In [30]:
E=eye(3), L=ones(1,5), rand(1,3), D=diag([1,10,100]), diagpart=diag(M)
E =

Diagonal Matrix

   1   0   0
   0   1   0
   0   0   1

L =

   1   1   1   1   1

ans =

   0.40971   0.16158   0.67305

D =

Diagonal Matrix

     1     0     0
     0    10     0
     0     0   100

diagpart =

   1
   3
   1

Így például előállíthatjuk a kvantummechanikai $\hat a^+$ keltő operátor mátrixalakját, amiben a pozitív egészek négyzetgyökei vannak elhelyezve átlós vonalban (természetesen a végtelen méretű mátrix véges részletét hozzuk létre):

In [31]:
n=7; aplus = diag( sqrt(1:n-1), 1)
aplus =

   0.00000   1.00000   0.00000   0.00000   0.00000   0.00000   0.00000
   0.00000   0.00000   1.41421   0.00000   0.00000   0.00000   0.00000
   0.00000   0.00000   0.00000   1.73205   0.00000   0.00000   0.00000
   0.00000   0.00000   0.00000   0.00000   2.00000   0.00000   0.00000
   0.00000   0.00000   0.00000   0.00000   0.00000   2.23607   0.00000
   0.00000   0.00000   0.00000   0.00000   0.00000   0.00000   2.44949
   0.00000   0.00000   0.00000   0.00000   0.00000   0.00000   0.00000

Még hasznos ez a függvény, ami Töplitz típusú mátrixot hoz létre, melynek az átlós vonalak mentén azonosak az elemei. A lineáris algebra anyagrészben láttunk ilyen szerkezetű mátrixot, ennek előálítását megkönnyíti. (E függvénnyel nemszimmetrikus mátrix is létrehozható, ha 2 vektort adunk be).

In [32]:
toeplitz( [ 2.5, 2, 1.5, 1, 0.5] )
ans =

   2.50000   2.00000   1.50000   1.00000   0.50000
   2.00000   2.50000   2.00000   1.50000   1.00000
   1.50000   2.00000   2.50000   2.00000   1.50000
   1.00000   1.50000   2.00000   2.50000   2.00000
   0.50000   1.00000   1.50000   2.00000   2.50000

Saját függvények

Írjunk például olyan függvényt, ami egy kisbolygó földközelponti és földtávolponti távolsága függvényében megadja a földközelponti pályára állításhoz szükséges sebességet. Függvények definálása a pythontól annyiban különbözik, hogy más kulcsszóval kell kezdeni, a visszatérési értéknek megfelelő változót meg kell adni az első sorban az = jel előtt, és a kezdő sor végére nem szabad kettőspontot tenni. Ezen felül azonban figyelni kell, hogy az értékadásokat ';'-vel zárjuk le, hacsak nem akarjuk, hogy a belső értékadás eredménye már a függvény végrehajtása közben kiíródjon (ilyen kiíratás persze tesztelés közben hasznos, így példaként meghagytam a félnagytengely $a$ hossszának kiírtását):

In [33]:
function z=v1(r1,r2)
  gamma=6.67408e-11;
  M=5.97237e24;

  a=(r1+r2)/2
  c=(r2-r1)/2;
  b=sqrt(a^2-c^2);
  z=sqrt(gamma*M/a)*b/r1;
end

Így kapjuk pl. a körpályához szükséges sebességet:

In [34]:
v1(6.371e6,6.37e6)
a =  6370500
ans =  7909.5

Így pedig a szökési sebességet:

In [35]:
v1(6.371e6,1e11)
a =    5.0003e+10
ans =    1.1186e+04

Egyenletrendszer megoldása

Mint láttuk, ${\bf M} x = v$ alakú, azaz lineáris egyenletrendszer megoldását pythonban a solve függvény végzi el, itt ennek megfelelője az alábbi művelet:

In [36]:
M\v
ans =

  -2.12500
   2.50000
  -0.62500

Sajátérték-probléma

A mátrix sajátértékeit pedig így kapjuk meg:

In [37]:
Lambda=eig(M)
Lambda =

  -2.00000
   0.62772
   6.37228

Ha a sajátvektorokat is meg akarjuk kapni, akkor két mennyiséget kell a függvénynek kiadnia. Octaveban ez úgy lehetséges, hogy listát alkotunk azokból a változókból, amelyekbe a sajátvektorokat és sajátértékeket akarjuk tenni. A szögletes zárójel tehát itt, az egyenlőségjeltől balra más jelentéssel bír, mint jobbra, amikor tömböt jelölt.

In [38]:
[V,D]=eig(M)
V =

   7.0711e-01   4.5440e-01   5.4177e-01
   2.0123e-16  -7.6618e-01   6.4262e-01
  -7.0711e-01   4.5440e-01   5.4177e-01

D =

Diagonal Matrix

  -2.00000         0         0
         0   0.62772         0
         0         0   6.37228

A pythontól eltérően az első mátrixban vannak a sajátvektorok, a másodikban a sajátértékek. A sajátvektorok itt is az oszlopokat alkotják, a sajátértékek pedig a vektoroknak megfelelő sorrendben következnek a második mátrix átlójában.

Szinguláris-érték dekompozíció

Ez egy hasonló probléma a sajátérték-kereséshez. Pl. arra vagyunk kíváncsiak, hogy egy mátrix legfeljebb milyen hosszúra tud nyújtani egy $v$ egységvektort, ha ${\bf M} v$ szorzás szerint hat rá. Ennek a szemléletes jelentését látjuk, ha felrajzoljuk a síkban az összes egységvektor végpontját, az az egységkör (3D-ben gömb). Minden pontjának vesszük a képét az előbbi szorzással véve, ezek általánosságban ferde tengelyű ellipszist alkotnak (3D-ben ellipszoidot). Tehát a legjobban megnyújtott vektor az, ami az ellipszis nagytengelyének végébe képződött, a nyújtás aránya pedig az ellipszis fél nagytengelye. A különbség a sajátvektor-kereséshez képest, hogy most nem kötöttük ki, hogy a vektor az irányát megtartsa. Könnyen belátható, hogy ez a nyújtási arány éppen az ${\bf M}^+{\bf M}$ mátrix legnagyobb sajátértékének négyzetgyöke, a keresett vektor pedig a hozzá tartozó sajátvektor. Általánosabban pedig a legkisebb, legnagyobb nyújtási arányokat (és 2-nél több dimenzióban az aránynak az irány függvényében található nyeregpontjait) e mátrix sajátértékeinek négyzetgyökei adják, a hozzájuk tartozó szinguláris vektorok pedig a megfelelő sajátvektorok. Ezeket adja meg nekünk az svd függvény, a szinguláris értékek az S mátrixba kerülnek, a szinguláris vektorok pedig a V oszlopaiba (egyúttal a mátrix ${\bf M}={\bf U}{\bf S}{\bf V}^+$ szorzatra bontódik):

In [39]:
[U,S,V]=svd(M)
U =

  -5.4177e-01  -7.0711e-01  -4.5440e-01
  -6.4262e-01  -4.1061e-16   7.6618e-01
  -5.4177e-01   7.0711e-01  -4.5440e-01

S =

Diagonal Matrix

   6.37228         0         0
         0   2.00000         0
         0         0   0.62772

V =

  -5.4177e-01   7.0711e-01  -4.5440e-01
  -6.4262e-01  -1.1059e-16   7.6618e-01
  -5.4177e-01  -7.0711e-01  -4.5440e-01

Közel szinguláris mátrixok

Próbáljunk ki valamit octaveban, amit persze pythonban is kiszámolhattunk volna. Ha kimértük egy potenciál értékét $x_1,x_2,\ldots x_n$ pontokban, arra pl. interpolálás, extrapolálás érdekében illeszthetünk n-1 fokú polinomot. Ez persze megy a polyfit v. curvefit függvénnyel, de hogy a probléma lényegét lássuk, írjuk fel, hogy ez a következő egyenletrendszer megoldását jelenti: $$ \sum{j=1}^n a_j x_i^j = v_i, \;\; i=1,2,\ldots n$$ Ez ${\bf M}\cdot a=v$ egyenletrendszer alakjába írható, ahol az ismeretlenek most az $aj$ polinomegyütthatók, az egyenletrendszer együtthatómátrixának elemei pedig: $M{ij}=x_i^j$. Ha pl. $x_i=1,2,\ldots 5$, és a mért értékeke az alábbiak, akkor így állíthatjuk elő az egyenletrendszer mátrixát:

In [40]:
n=5; X=(1:n)'*ones(1,n); P=ones(n,1)*(0:n-1); M=X.^P
M =

     1     1     1     1     1
     1     2     4     8    16
     1     3     9    27    81
     1     4    16    64   256
     1     5    25   125   625

In [41]:
# gyártunk egy példa adatsort ilyen módon:
v=M*[1,1/2,1/3,1/4,1/5]';

Az ${\bf M}\cdot a=v$ egyenletrendszer megoldását pedig így kapjuk az itt szereplő mért $v_i$ értékekhez:

In [42]:
v=[2.2833;8.5333;28.4500;75.5333;168.0833];
a = M\v; a'
ans =

   1.00030   0.49935   0.33374   0.24990   0.20001

Ha azonban az egyik v értéket csupán 0.03 hibával mérjük, jelentős eltérést kapunk az eredményben:

In [43]:
a = M \ ( v + [ 0; 0; 0.03; 0; 0] ); a'
ans =

   1.300300  -0.085650   0.701242   0.159900   0.207508

Ez a mátrix közel szinguláris jellegének köszönhető, amit könyebb megérteni 2x2 mátrix esetére. Készítsünk belőlük olyat, aminek a 2. oszlopa kis eltéréssel az 1. oszlop számszorosa:

In [44]:
M2=[ 2, 4
    3, 6.01]
M2 =

   2.0000   4.0000
   3.0000   6.0100

Az $\left(\begin{array}{r}1\\0\end{array}\right)$, $\left(\begin{array}{r}0\\1\end{array}\right)$ bázisvektorok képei pedig épp a mátrix két oszlopa. Így a korábbi ábra jobb felső ellipszisében a nyilak közel egyirányúak. Emiatt az ellipszis nagyon megnyúlt, azaz a két szinguláris érték hányadosa nagy. Az $\bf{M}\cdot x=v$ egyenletrendszer $x$ megoldását keressük, és $v$, pl. mérési hiba miatt megváltozik $v+dv$-re. és a megváltozott megoldást $x+dx$-szel jelöljük, akkor nyilván $\underline M\cdot \underline dx=\underline dv$ is igaz. Ha $v$ közel van az ábra egyik nyilának irányához, akkor láthatóan kis megváltozással viszonylag közel kerülhet a másik nyílhoz, így a hozzá tartozó $x$ iránya viszonylag sokat változik. Tehát a hiba lényegesen megnő.

Általánosságban is próbáljuk megérteni! Ha a megoldást megváltozását $dx$-szel jelöljük, akkor nyilván $\bf{M}\cdot dx=dv$ is igaz. Az $x$ben tapasztal hibát, pontosabban annak $|dx|/|x|$ relatív mértékét akarjuk viszonyítani a mért értékek $|dv|/|v|$ relatív hibájához. Az arány nyilván akkor a legnagyobb, ha $|dv|/|dx|$ a lehető legkisebb, és $|v/|x|$ a lehető legnagyobb, azaz $x$ és $dx$ a megfelelő szinguláris vektorok irányához közel állnak, s az utóbbi arányokat ekkor a szinguláris értékek adják. Így a relatív hiba növekedésének maximális aránya a maximális és minimális szinguláris érték hányadosa. Ezt hívjuk kondíciós számnak, $${\rm cond}(\bf{M})=s_{max}/s_{min}$$ Így ez az ${\bf M}^+{\bf M}$ sajátértékeivel is kifejezhető. Nagy számérték itt is azt jelenti, hogy a mátrix oszlopai között közelítőleg teljesül egy lineáris kapcsolat. Az octaveban egyenletrendszer megoldásakor érdemes ellenőrizni nagyságát, hogy az eredmény pontosságára következtessünk:

In [45]:
cond(M2)
ans =  3256.0

A polinom-illesztést leíró mátrixra szintén nagy szám adódik, sőt ez a mátrix méretével gyorsan növekszik:

In [46]:
cond(M)
ans =    2.6170e+04