Nie za krótkie wprowadzenie do wxMaxima

Matematyka jest piękna. Układy równań różniczkowych rządzą światem, począwszy od prostego wahadła po znajdowanie równowagi pomiędzy populacją wilków a królików. Epidemia choroby równie grzecznie poddaje się prawom matematyki, jak o dziwo kurs walutowy lub postrzeganie harmonii. Oczywiście, to tylko modele teoretyczne, ale o ile przyjemniej żyje się w świecie, który chociażby twierdzimy, że rozumiemy (;. 

Niestety, radość odkrywania tajemnic niejednokrotnie rozbija się o kartkę papieru i ołówek, który łamie się w najmniej oczekiwanym momencie kolejnego układu równań. Całka liczona po raz pierwszy może być wyzwaniem (o ile nie jest to kolokwium lub egzamin), ale któryś kolejny identyczny problem optymalizacyjny jest zwyczajnie nudny.

W takich chwilach z pomocą może przyjść program komputerowy typu CAS (computer algebra system) umożliwiający liczenie nie tylko numeryczne (jak zwykły kalkulator), ale również i symboliczne (znane nam ze szkoły – litera jako reprezentacja liczby). Na rynku dostępne są programy obsługujące tę funkcjonalność (m.in. Mathematica), są jednak drogie (i zamknięte). Istnieje też ich odpowiednik o otwartych źródłach – Maxima (oraz jej graficzna nakładka wxMaxima), którą postanowiłam przedstawić w tym artykule.

Podstawowe informacje

Korzenie Maximy sięgają lat 60′ XX wieku. Wtedy w Massachusetts Institute of Technology na zlecenie Departamentu Energii USA opracowano program Macsyma. Zajmujący się od 1982 roku projektem William Schelter uzyskał w 1998 roku od Departamentu Energii zezwolenie na uwolnienie kodu (napisanego w Lispie) na licencji GPL. Źródła można znaleźć na sourceforge.net. Program można uruchomić za pomocą terminala (wpisujemy maxima i już możemy zacząć liczyć), jak i w edytorze Emacs. Opracowany został również wieloplatformowy interfejs wxMaxima.

Instalacja

Używam systemu Debian 6 sid, więc mam dosyć aktualną wersję omawianych pakietów. Zauważyłam jednak, że w repo dla stabilnych wydań niekiedy mogą być ich starsze wersje. Jeśli chcesz być jak najbardziej na czasie, prawdopodobnie nie obejdzie się bez kompilacji ze źródła. Tutaj przedstawię najprostszą instalację.

W terminalu wpisujemy

sudo apt-get install maxima

co zainstaluje nam silnik programu. Następnie potrzebujemy prostego interfejsu graficznego:

sudo apt-get install xmaxima

Pakiet jest istotny, ponieważ cześć wyników zwracanych przez aplikację pojawia się w osobnych okienkach – bez niego otrzymamy jedynie błąd. Dzieje się tak np. przy wykresach fazowych równań różniczkowych (osobiście moja ulubiona funkcjonalność).

Tutaj można zakończyć proces instalacji, o ile nie przeraża Cię praca w trybie tekstowym lub masz doświadczenie np. z pakietem R. Osobiście polecam jednak zainstalowanie nakładki graficznej – wersja konsolowa przynajmniej dla mnie nie zachowywała się zgodnie z regułą najmniejszego zaskoczenia (;.

sudo apt-get install wxmaxima

i zwolennicy klikalnych aplikacji również nie muszą się już bać matematyki.

Startujemy!

Mod-4 (klawisz windows) i wpisujemy wxmaxima (w GNOME3), ewentualnie w terminalu lub innym starterze wxmaxima. ukazuje się coś takiego:

Główne okno wxmaxima

Zupełnie dla zabawy możemy zacząć wpisywać jakieś działania, np. 2*3/4+15; (średnik wyjaśnię za chwilę). Zapytanie zatwierdzamy kombinacją shift+Enter (jeśli komuś strasznie przeszkadza, może zmienić to w Edycja-> Preferencje-> zaznaczenie klawisz Enter wykonuje komórki). Program działa w trybie input- output, co oznacza, że instrukcje (input) i wyniki (output) są numerowane i można się do nich odwoływać za pomocą %i i %o. Warto pamiętać, że wykona się zawartość całej komórki. Ale przecież nie instalowaliśmy pakietu po to, żeby używać go jak prostego kalkulatora. Wpiszmy zatem a+ b/c;. Co otrzymamy? Postać doskonale znaną ze szkoły – to właśnie siła Maximy. Wszystko może być tutaj symbolem – nawet liczba!

Średniki? $? Komentarze?

O ile w wxmaxima program dopisze nam brakujący średnik, konsolowa wersja nakrzyczy na nas. Na końcu linii zawsze musi pojawić się jeden ze znaczników:

; – wynik zostanie wyświetlony

$ – wynik nie zostanie wyświetlony (ale zostanie wykonany!).

Oczywiście druga opcja jest użyteczna w przypadku wklejania większych bloków kodu.  Nie zawsze interesuje nas to, co dzieje się pomiędzy kluczowymi momentami rozwiązania. Rozwiązując problem optymalizacyjny chcemy otrzymać wynik końcowy, w mniejszym stopniu będziemy chcieli oglądać postać warunków pierwszego rzędu (pochodna może przecież mieć bardzo nieprzyjemną postać analityczną). Bez zapisu jednak o liczeniu gradientu program nie policzy nam tego, co chcemy.

Porównajmy tutaj dwa kody:

a+ b/c;

oraz

a+ b/c$
disp("Pokaż się!");

W pierwszym wypadku otrzymamy od razu wynik. W drugim gdybyśmy nie wpisali czegoś „widzialnego”  (moje disp()), nie zauważylibyśmy nic. Po prawej stronie paska dolnego zobaczylibyśmy tylko „oczekiwanie na wejście”.

Komentarze mogą być zapisane w /* */. Muszą zmieścić się w jednej linii i NIE MOGĄ być ostatnie w komórce.

Uwaga również na nawiasy: o ile tego nie zmienimy w preferencjach, będą pojawiać się oba jednocześnie.

To zależy… więc załóżmy…. (;

Podczas korzystania z maximy jako prostego kalkulatora nie będzie to prawdopodobnie potrzebne. Załóżmy (; jednak, że zechcesz rozwiązywać, np. zadania z ekonomii lub fizyki. Wtedy nagle może okazać się, iż program nie jest taki mądry jak Ty i nie wie, na jakim przedziale liczb ma operować. Z pomocą przychodzi tutaj instrukcja assume. Maxima jest bardzo wrażliwa na tym punkcie. Jeśli nadal lekceważysz założenia początkowe, proponuję wywołać następujący kod:

kill(all);
load (newton1);
newton (x^2 - a^2, x, a/2, a^2/100);
ev (x^2 - a^2, x = %);

gratulacje, właśnie uruchomiłeś nieskończoną pętlę. Wciśnij stop na pasku narzędzi i popraw kod.

kill(all);
load (newton1);
assume (a > 0);
newton (x^2 - a^2, x, a/2, a^2/100);
''(x^2 - a^2, x = %);

Lepiej prawda? Warto tutaj zaznaczyć, że w obrębie assume można użyć tylko <, <=, > lub >=.

Niekiedy jednak Maximie nie wystarcza samo określenie przedziałów zmiennej. Potrzebuje wiedzieć, jaki jest jej typ. W takim przypadku przerwie wykonywanie kodu i zapyta nas o to. Jeśli licząc na to, iż program sobie sam policzy, wyszliśmy zrobić sobie herbaty, po powrocie możemy być niemile zaskoczeni. Żeby nie rozmawiać z aplikacją, warto wpisać na początku declare(x_i, b_i…);

gdzie

x_i – nasze zmienne, które deklarujemy

b_i – właściwości przypisywane zmiennym. Podawane PO PRZECINKU PO ZMIENNEJ, której dotyczą. Przykładowe wartości:

- constant (stała),
- even (parzysta),
- odd (nieparzysta),
- real (rzeczywista),
- complex (zespolona),
- integer (całkowita).

Totalna zagłada na dobry początek

W części dotyczącej założeń na początku kodu pojawiła się instrukcja kill(all); . To wezwanie do masowej destrukcji… czyści pamięć z niepotrzebnych rzeczy. Istotność tej deklaracji widoczna jest w poniższym przykładzie:

rownanko: 4 = x^2 +x -2;
solve(rownanko,x);
x:rhs(%[2]);

liczymy tutaj proste równanie kwadratowe i następnie za x podstawiamy drugi wynik z listy (wspomniałam, że Maxima jest napisana w Lispie – lubi tworzyć listy, dlatego nie powinno dziwić, że wyniki również zwracane są w takiej postaci). Kiedy wywołamy kod kilka razy, okaże się, iż program zwraca nam dziwne wyniki- pamięta podstawienie z pierwszego uruchomienia kodu. Z kill(all); umieszczonym na początku wynik jest powtarzalny i prawidłowy.

Pracując z różnymi kodami, bardziej lub mniej długimi na tyle przyzwyczaiłam się do tej komendy, że obecnie pierwsza moja myśl na hasło Maxima to właśnie „kill(all);” (:.

Słowo o operatorach

Pisząc kod możemy napotkać na 3 rodzaje operatorów przypisania:

: nadaje wartość zmiennej (a:3; b:4;)

= deklaruje równanie (eq1: y= ax+b;)

:= dla funkcji (mojafunkcja(x1,x2):=a*x1*b*x2; )

Jednak jeśli wywołamy po sobie np. deklarację a i b oraz deklarację mojafunkcja(x1,x2) okaże się, iż w tej ostatniej nadal będzie widoczne a i b (chociaż wcześniej je zadeklarowaliśmy). Maxima nie zawsze przypisuje domyślne wartości, niekiedy musimy ją do tego zmusić. Robimy to za pomocą ”(wyrażenie). Komenda wyglądać będzie wtedy:

mojafunkcja(x1,x2):=''(a*x1*b*x2);

Usprawiedliwienie lenistwa, czyli ostatni wynik

% stanowi wygodny sposób przypisywania zmiennej. Dzięki niemu możemy ominąć deklaracje niektórych nowych zmiennych. To tak, jakbyśmy programowi powiedzieli „weź wynik z ostatniej linijki”.

kill(all);
assume(x>0);
declare (a, noninteger);
2=x^a;
solve(%,x); /* wpisanie równania z poprzedniej linii*/
disp("Pokaż mi moje rozwiazanie", %)$

Listy i wyciąganie potrzebnych danych

Często wyniki dostaniemy zwrócone w postaci listy. I jest to wygodne, jeśli na tym chcieliśmy poprzestać. Gorzej, jeśli wynik działania przesyłany jest do kolejnej instrukcji. Listy maximowe mogą zawierać wszystko: liczby, zmienne, funkcje, inne listy. Poszczególne elementy oddzielamy przecinkiem, a odwołujemy się przez nazwa_listy[element]. Najprościej listę zadeklarować za pomocą operatora przypisania „:”. Poniżej paskudny, rozdęty i przesadzony kod, który jednak pomaga zrozumieć mechanizm odwoływania się do elementów listy (jeśli interesuje nas odwołanie do lewej/prawej strony równania korzystamy z komendy lhs/rhs)

kill(all)$
funkcja(x):=[const=x^a]$
a:3$
6$
b:[const,%,''funkcja(x)]$
d:[%,"wat?"]$
d[1]$
e:%[3]$
/* prawa strona*/
rhs(%[1])$
disp("To jest prawa strona równania ",%)$
/* lewa strona*/
lhs(e[1])$
disp("To jest lewa strona równania ",%)$

Czyścimy pamięć z niepotrzebnych rzeczy. Deklarujemy funkcję „funkcja(x)”. Ustalamy wielkość parametru a. Wklejamy sobie liczbę tylko po to, żeby odwołać się do niej w następnej linijce (omawiany wcześniej %). Nasze b jest listą zawierającą po kolei stałą const, naszą liczbę z linijki wcześniej (6) oraz uzupełnioną o zadeklarowany parametr a funkcję. Lista d składa się z listy b (operator %) i tekstu. Wyciągamy z niej pierwszy element – czyli listę b. Deklarujemy e – chcemy wyciągnąć równanie z listy b (które było na 3 jej miejscu) – używamy 3 miejsca z wyniku z poprzedniej linijki (czy wspominałam, że ten kod jest rozdęty?). Teraz wyciągnięcie jednej lub drugiej strony równania nie wydaje się trudne. Warto zauważyć, iż przy wyciąganiu lewej strony odwołaliśmy się już do zmiennej e – użycie % odwołałoby nas do wyniku komendy rhs.

Świetnie, ale po co to wszystko?

Od podstaw trzeba zacząć. Z obserwacji mogę powiedzieć, iż często zlekceważenie dobrego zrozumienia bazowych elementów składni Maximy wywołuje niechęć do jej użycia. Zdaję sobie sprawę, że czytelnik po dobrnięciu do tego punktu może czuć się rozczarowany (dużo programowania, wyjątków i co najgorsze myślenia!) tym bardziej, iż instalując nakładkę graficzną, chcąc nie chcąc, liczymy na klikalność. Owszem, można większość rzeczy prostych w ten sposób rozwiązać. Zamierzając jednak użyć programu do bardziej wyrafinowanych celów, wygodniej samodzielnie napisać kod.

Na osłodę napiszę, że następny artykuł poświęcę temu, co w Maximie jest najładniejsze. Wykresom. Na zachętę proponuję wywołać następujące kody:

kill(all);
plotdf([(y), (-x + x^3 -y)], [x,y], [x,-1.5,1.5], [y,-1.5,1.5]);

popatrz jaki jestem piękny

oraz

kill(all);
load(draw);
draw2d(
color=red,implicit(sqrt(1-(abs(x)-1)^2)=y, x, -2,2, y,-3.5,1.5),
color=red,implicit(tan(abs(x)-1)-1.7=y, x, -2,2, y,-3.5,1.5))$

Komentarze są prywatnymi opiniami dodających je osób. Prosimy o zachowanie kultury wypowiedzi. Komentarze obraźliwe oraz obniżające poziom serwisu będą usuwane. Więcej w regulaminie komentowania.

7 komentarzy

  1. Franek 15 marca 2012 o godz. 20:28 #

    Interesujący wpis ;>

  2. rtc 16 marca 2012 o godz. 1:20 #

    no

  3. zyli 16 marca 2012 o godz. 12:25 #

    Bardzo dobry program.
    Koniecznie zerknąć do dokumentacji/podręczników/tutoriali: http://maxima.sourceforge.net/documentation.html
    Nie bać się języków – matematyka jest wszędzie ta sama.
    Nawiasem mówiąc mnie przypadł do gustu podręcznik hiszpański, choć po hiszpańsku u mnie ani w ząb (sic!): http://riotorto.users.sourceforge.net/maxima/max….
    Polecam

  4. etmoon 16 marca 2012 o godz. 19:41 #

    Bardzo dobrze napisany artykuł. Sam używam Maximy do rozwiązywania problemów symbolicznych. Kilka rad z tego artykułu na pewno się przyda. Pozdrawiam autorkę.

  5. Marcin 8 maja 2012 o godz. 1:40 #

    WOW!
    Chyba zacznę się uczyć równań różniczkowych (na polibudzie mnie zniechęcili do matmy :/ )

  6. franq57 24 października 2013 o godz. 22:40 #

    A jak w maximie wyswietlic spirale, np archimedesa?

(wymagane)
URI
Uwaga! Niektóre komentarze, m.in. te dodane przez niezalogowanych i nowych użytkowników, są ręcznie moderowane. Jeśli Twój komentarz nie ukaże się od razu, nie dodawaj go ponownie, tylko cierpliwie poczekaj na akceptację.

Literówki najlepiej zgłaszać mailem: sirmacik@jakilinux.org!

W komentarzach możesz używać prostych znaczników HTML. Przykłady:
  • Link: <a href="jaklinux.org">Linux dla każdego</a>,
  • Wytłuszczenie: <strong>tekst pogrubiony</strong>,
  • Kursywa: <em>tekst pochylony</em>,
  • Przekreślenie: <del>tekst przekreślony</del>,
  • Kod: <code>printf("blok kodu");</code>,
  • Cytat: <blockquote>cytat</blockquote>
Uwaga: jeśli dodasz nieznany znacznik, będzie on niewidoczny, gdyż system filtruje takie znaczniki.