Używam MATLABa od ponad 25 lat. (A wcześniej używałem nawet MATRIXxa, późnej, nieodżałowanej próby spinoffu, a może ripoffu). Nie jest to pierwszy język, w którym nauczyłem się programować, ale jest to język, z którym zżyłem się matematycznie. Znajomość MATLABa była bardzo dobra dla mojej kariery.
Jednakże nie można zignorować wzrostu popularności Pythona w obliczeniach naukowych. MathWorks musi czuć to samo: nie tylko dodali możliwość wywoływania Pythona bezpośrednio z MATLABa, ale także zapożyczyli niektóre z jego cech językowych, takich jak bardziej agresywne nadawanie operandów operatorów binarnych.
Dotarło to do punktu, w którym kwestionuję moje dalsze używanie MATLABa zarówno w badaniach jak i nauczaniu. Jednak tak wiele przychodzi mi tam z łatwością i tak wiele zainwestowałem w materiały do niego, że trudno było zebrać motywację, aby naprawdę nauczyć się czegoś nowego.
Wejdź do opartego na MATLABie podręcznika, który współtworzyłem dla wprowadzającej matematyki obliczeniowej. Książka ma ponad 40 funkcji i 160 przykładów obliczeniowych, i obejmuje to, co uważam za gruntowne podstawy w użyciu MATLABa do numerycznych obliczeń naukowych. Tak więc częściowo w ramach samodoskonalenia, a częściowo w celu zwiększenia użyteczności książki, postanowiłem w tym roku przetłumaczyć kody na języki Julia i Python. To doświadczenie doprowadziło mnie do szczególnego spojrzenia na te trzy języki w odniesieniu do obliczeń naukowych, które staram się uchwycić poniżej.
Będę głównie odkładał na bok kwestie kosztów i otwartości. MATLAB, w przeciwieństwie do Pythona i Julii, nie jest ani wolny od piwa, ani od mowy. Jest to rzeczywiście ogromne rozróżnienie – dla niektórych rozstrzygające – ale ja chcę rozważyć zalety techniczne. Przez wiele lat MATLAB był znacznie lepszy niż jakikolwiek darmowy produkt pod wieloma bardzo użytecznymi względami i jeśli chciałeś być produktywny, to koszty powinny być przeklęte. To oddzielne rozważania od platońskiego uroku języka i ekosystemu.
Gdy odłożysz koszty na bok, użyteczna rama dla wielu różnic między tymi językami leży w ich pochodzeniu. MATLAB, najstarszy z nich, kładzie nacisk na matematykę, szczególnie matematykę zorientowaną numerycznie. Python, który zaczął się na dobre w późnych latach 80-tych, skupił się na informatyce. Julia, która rozpoczęła się w 2009 roku, postanowiła osiągnąć równowagę między tymi stronami.
MATLAB
Początkowo każda wartość w MATLABie była tablicą liczb zmiennoprzecinkowych podwójnej precyzji. Oba aspekty tego wyboru, tablice i zmiennoprzecinkowe, były inspirowanymi decyzjami projektowymi.
Standard IEEE 754 dla liczb zmiennoprzecinkowych nie został przyjęty aż do 1985 roku, a pamięć była mierzona w K, a nie G. Podwójne liczby zmiennoprzecinkowe nie były najbardziej wydajnym sposobem reprezentowania znaków lub liczb całkowitych, ale były tym, czego naukowcy, inżynierowie i coraz częściej matematycy chcieli używać przez większość czasu. Co więcej, zmienne nie musiały być deklarowane, a pamięć nie musiała być jawnie alokowana. Pozwalając komputerowi zająć się tymi zadaniami i usuwając typy danych z drogi, uwolniło twój mózg od myślenia o algorytmach, które miały operować na danych.
Tarcze były ważne, ponieważ algorytmy numeryczne w algebrze liniowej wchodziły w życie, w postaci LINPACK i EISPACK. Ale dostęp do nich za pomocą standardowego nośnika obliczeń naukowych, FORTRAN 77, był procesem wieloetapowym, który wymagał deklarowania zmiennych, wywoływania krypto nazwanych procedur, kompilowania kodu, a następnie sprawdzania danych i plików wyjściowych. Zapisanie mnożenia macierzowego jako A*B
i otrzymanie od razu wydrukowanej odpowiedzi było zmianą w grze.
MATLAB sprawił również, że grafika stała się łatwa i o wiele bardziej dostępna. Żadnych skomplikowanych, specyficznych dla maszyny bibliotek z niskopoziomowymi wywołaniami, po prostu plot(x,y)
i widziałeś prawie to samo, co każdy inny użytkownik MATLABa. Było więcej innowacji, takich jak wbudowane liczby zespolone, rzadkie macierze, narzędzia do budowania wieloplatformowych graficznych interfejsów użytkownika i wiodący zestaw rozwiązań ODE, które sprawiły, że MATLAB stał się miejscem do obliczeń naukowych z prędkością myśli.
Jednakże projekt, który był idealny do interaktywnych obliczeń, nawet tych długich, nie zawsze sprzyjał pisaniu dobrego i wydajnego oprogramowania. Przenoszenie danych pomiędzy wieloma funkcjami wymagało żonglowania mnóstwem zmiennych i częstego sprawdzania dokumentacji dotyczącej argumentów wejściowych i wyjściowych. Jedna funkcja na plik dyskowy w płaskiej przestrzeni nazw była orzeźwiająco prosta w przypadku małego projektu, ale przyprawiała o ból głowy w przypadku dużego. Pewne wzorce programowania (wektoryzacja, prealokacja pamięci) musiały być stosowane, jeśli chciało się uniknąć wąskich gardeł prędkości. Obliczenia naukowe były teraz stosowane w znacznie większej liczbie dziedzin, z ogromną ilością różnych natywnych typów danych. Etc.
MathWorks odpowiedziało kontynuując innowacje w MATLABie: funkcje inline, funkcje zagnieżdżone, zamknięcia zmiennych, liczne typy danych, funkcje zorientowane obiektowo, frameworki testów jednostkowych, i tak dalej i dalej. Każda innowacja była prawdopodobnie rozwiązaniem ważnego problemu. Ale kumulacja 40 lat tych zmian miała efekt uboczny w postaci erozji prostoty i jedności koncepcji. W 2009 roku napisałem książkę, która w mniej niż 100 stronach dość dobrze opisywała to, co uważałem za najważniejsze w MATLABie. O ile mi wiadomo, wszystkie te rzeczy są nadal dostępne. Ale teraz trzeba wiedzieć o wiele więcej, aby nazwać siebie biegłym.
Python
W pewnym sensie historia Pythona wydaje się być niemal lustrzanym odbiciem historii MATLABa. Oba charakteryzowały się interaktywną linią poleceń (obecnie powszechnie nazywaną REPL, dla „read-eval-print loop”) i wolnością od deklaracji zmiennych i kompilacji. Jednak MATLAB został stworzony jako plac zabaw dla analityków numerycznych, podczas gdy Python został stworzony z myślą o hakerach. Każdy z nich rozwijał się w kierunku innej publiczności poprzez korekty i rozszerzenia.
Na moje oko, Pythonowi wciąż brakuje matematycznego uroku. Masz brzydotę i małe przykrości, takie jak **
zamiast ^
, @
dla mnożenia macierzy (niedawna innowacja!), shape
zamiast rozmiaru macierzy, przechowywania zorientowanego na wiersze itp. Jeśli uważasz, że V.conj().T@D**3@V
jest eleganckim sposobem na napisanie $V^*D^3V$, to możesz potrzebować wizyty u lekarza. I jest zero-indeksowanie (w przeciwieństwie do indeksów, które zaczynają się od 1). Czytałem te argumenty i nie uważam ich za decydujące. Jest to oczywiście kwestia preferencji – rzecz internetowych świętych wojen – ponieważ można przytoczyć nieciekawe przykłady dla każdej z konwencji. Uważam, że decydujące jest to, że mamy dekady matematycznej praktyki indeksowania wektorów i macierzy od jednego, a większość pseudokodu przyjmuje takie założenie.
Poza drobnymi przykrościami, uważam, że ekosystem Python+NumPy+SciPy jest kludgy i niespójny. Przykładem A jest fakt, że pomimo tego, że język jest raczej poświęcony orientacji obiektowej, istnieje klasa macierzy, a mimo to jej użycie jest odradzane i zostanie zdeprecjonowane. Być może MATLAB po prostu mnie skorumpował, ale uważam, że macierze są na tyle ważnym typem obiektu, że warto je zachować i promować. Czy nie jest to główny punkt sprzedaży OOP, że możesz mieć *
robiący różne rzeczy dla tablic i macierzy? Istnieje wiele innych nieścisłości w tym względzie. (Dlaczego potrzebuję polecenia o nazwie spsolve? Czy nie mogę po prostu wywołać solve
na macierzy rzadkiej? I tak dalej, i tak dalej.)
Są też miejsca, gdzie ekosystem numeryczny wygląda dla mnie trochę cienko. Na przykład quadrature i ODE solvers wyglądają jak minimalny zestaw w 2019 roku. AFAICT nie ma metod dla DAEs, DDEs, symplektycznych solverów lub implicite solverów, które pozwalają na wewnętrzne iteracje Krylov. Spójrz na referencje dla tych funkcji; są one w większości 30 lub więcej lat – wciąż dobre, ale bardzo dalekie od kompletności. Pakiet Matplotlib jest niesamowitym dziełem i przez pewien czas wyglądał lepiej niż MATLAB, ale uważam, że brakuje mu jeszcze 3D.
Niektórzy eksperci twierdzą, że istnieją głębokie powody, dla których kod Pythona ma problemy z utrzymaniem prędkości wykonywania z językami skompilowanymi. Jestem rozbawiony wynikami wyszukiwania „python jest zbyt wolny”. Mistrzowie Pythona wysuwają wiele z tych samych argumentów/apologii, które ludzie robili dla MATLABa w tamtych czasach. To nie znaczy, że się mylą, ale jest coś więcej niż tylko problem z percepcją.
Myślę, że rozumiem, dlaczego Python był tak ekscytujący dla wielu ludzi w obliczeniach naukowych. Ma składnię i moc podobną do MATLABa, dostępną z REPL. Ma świetne narzędzia wokół niego i gra dobrze z innymi językami i obszarami informatyki. Oferował to bez żadnych kosztów i z dużo lepszą długoterminową odtwarzalnością. Najwyraźniej, działa dobrze dla wielu ludzi, którzy prawdopodobnie nie widzą powodu do zmiany.
Ale dla rzeczy, które wiem jak robić w obliczeniach naukowych, Python czuje się o wiele bardziej jak uciążliwy do nauki i użytkowania niż jestem przyzwyczajony. Przez jakiś czas nie będziemy wiedzieli, czy będzie to kontynuacja zamiatania przez społeczność, czy już zbliżyła się do swojego szczytu. Nie mam specjalnych zdolności przewidywania, ale jestem niedźwiedziowaty.
Julia
Julia ma zalety i wady bycia spóźnialskim. Pochwalam twórców Julii za to, że myśleli, że mogą zrobić coś lepszego:
Chcemy języka, który jest open source, z liberalną licencją. Chcemy szybkości C z dynamizmem Ruby. Chcemy języka, który jest homoiconic, z prawdziwymi makrami jak Lisp, ale z oczywistą, znajomą notacją matematyczną jak Matlab. Chcemy czegoś tak użytecznego do programowania ogólnego jak Python, tak łatwego do statystyki jak R, tak naturalnego do przetwarzania ciągów znaków jak Perl, tak potężnego do algebry liniowej jak Matlab, tak dobrego w sklejaniu programów jak shell. Coś, co jest brudne, proste do nauczenia, a jednocześnie sprawia, że najbardziej poważni hakerzy są zadowoleni. Chcemy go interaktywnego i chcemy go skompilować.
W dużej mierze, wierzę, że im się udało. Późno po drodze do wersji 1.0 wydawało się, że trochę zbagatelizowali REPL, i było kilka prawie darmowych odchyleń od MATLABa. (Jak dokładnie LinRange
jest lepsze niż linspace
?) To są drobiazgi, chociaż.
To jest pierwszy język, którego używam, który wykracza poza ASCII. Nadal czerpię nieuzasadnioną satysfakcję z używania zmiennych takich jak ϕ
i operatorów takich jak ≈
. To coś więcej niż tylko kosmetyka; możliwość upodobnienia się do wyrażeń matematycznych, które piszemy, jest prawdziwym plusem, choć nieco komplikuje nauczanie i dokumentację.
Praca w Julii pokazała mi, że nabrałem pewnych nawyków programistycznych z powodu wyborów MATLABa, a nie z powodu jego wrodzonej wyższości. Wektoryzacja nie jest naturalna dla wielu rzeczy. Odkryciem w Julii jest to, że można zwektoryzować dowolną funkcję przez dodanie kropki do jej nazwy. Konstruowanie macierzy poprzez comprehension sprawia, że zagnieżdżone pętle (lub sztuczki meshgrid
) wyglądają w porównaniu z tym jak bicze, a całkowite unikanie macierzy poprzez generator dla prostego sumowania czuje się jak dostawanie czegoś za nic. (Jestem świadomy, że Python ma podobne cechy językowe.)
Duża cecha wielokrotnego wysyłania sprawia, że niektóre rzeczy są o wiele łatwiejsze i jaśniejsze niż orientacja obiektowa. Na przykład załóżmy, że masz klasy Wall i Ball w tradycyjnym języku zorientowanym obiektowo. Która z nich powinna wykryć kolizję kuli z murem? A może potrzebujesz klasy Room, która będzie pełniła rolę sędziego? Tego typu pytania potrafią doprowadzić mnie do rozpaczy. Z wielokrotną wysyłką, dane są pakowane do typów obiektów, ale metody, które operują na danych nie są związane z klasą. Więc
function detect_collision(B::Ball,W::Wall)
wie o typach, ale jest zdefiniowany niezależnie od nich. Zajęło mi to całkiem sporo programowania, aby docenić, jak interesujące i potencjalnie ważne jest pojęcie wielokrotnej wysyłki dla rozszerzenia języka.
Ekosystem numeryczny szybko się rozwija. Moim przykładem numer jeden jest DifferentialEquations.jl, napisany przez niesamowitego Chrisa Rackauckasa. Jeśli to oprogramowanie nie wygra wkrótce nagrody Wilkinsona, to znaczy, że system jest zepsuty. Po prostu wejdź na stronę i przygotuj się na konwersję.
Jeszcze nie widziałem dużego przyrostu prędkości w stosunku do MATLABa, który obiecuje Julia. Częściowo jest to mój względny brak doświadczenia i rodzaje zadań, które wykonuję, ale częściowo jest to również spowodowane tym, że MathWorks wykonał niesamowitą pracę automatycznie optymalizując kod. To nie jest aspekt kodowania, na którym skupiam się przez większość czasu, w każdym razie.
Programowanie w Julii zajęło mi trochę czasu, aby poczuć się komfortowo (być może po prostu starzeję się i krystalizuję). Zmusza mnie do myślenia o typach danych bardziej, niż bym chciał, i zawsze jest to ukradkowe podejrzenie, że przegapiłem właściwą drogę, aby coś zrobić. Ale do codziennego użytku, jestem tak samo skłonny zwrócić się do Julii, jak do MATLABa teraz.
Podsumowanie
MATLAB jest rozwiązaniem korporacyjnym, szczególnie dla inżynierii. Prawdopodobnie nadal jest najłatwiejszy do nauczenia się dla podstawowych zadań numerycznych. Skrupulatna dokumentacja i dziesiątki lat narzędzi do nauki zdecydowanie mają znaczenie.
MATLAB jest BMW sedan świata obliczeń naukowych. Jest drogi, i to zanim zaczniesz mówić o akcesoriach (skrzynki z narzędziami). Płacisz za solidną jak skała, płynną wydajność i obsługę. Przyciąga też nieproporcjonalnie dużo nienawiści.
Python jest jak pickup Forda. Jest wszechobecny i uwielbiany przez wielu (w USA). Może zrobić wszystko, co chcesz, i jest zbudowany, aby zrobić kilka rzeczy, których inne pojazdy nie mogą. Jest szansa, że będziesz chciał go pożyczyć od czasu do czasu. Ale nie oferuje wspaniałych, czystych wrażeń z jazdy.
Julia to Tesla. Została zbudowana w zuchwałym celu zmiany przyszłości i może to zrobić. Może też stać się tylko przypisem. Ale w międzyczasie dojedziesz tam, dokąd zmierzasz, w dobrym stylu i z zapasem mocy.