Ik gebruik MATLAB al meer dan 25 jaar. (En daarvoor gebruikte ik zelfs MATRIXx, een late, onbetreurde poging tot een spin-off, of misschien een ripoff.) Het is niet de eerste taal waarin ik heb leren programmeren, maar het is wel de taal waarmee ik wiskundig volwassen ben geworden. MATLAB kennen is erg goed geweest voor mijn carrière.
Hoe dan ook, het is onmogelijk om de opkomst van Python in de wetenschappelijke informatica te negeren. MathWorks moet hetzelfde voelen: niet alleen hebben ze de mogelijkheid toegevoegd om Python direct vanuit MATLAB aan te roepen, maar ze hebben ook een aantal van de taaleigenschappen overgenomen, zoals agressievere broadcasting voor operanden van binaire operatoren.
Het heeft een punt bereikt waarop ik mijn voortdurende gebruik van MATLAB in zowel onderzoek als onderwijs in twijfel heb getrokken. Toch komt zo veel gemakkelijk voor mij daar, en ik heb zo veel geïnvesteerd in materialen voor het, dat het moeilijk was om motivatie te verzamelen om echt iets nieuws te leren.
Enter het MATLAB-gebaseerde leerboek dat ik heb mede-geschreven voor inleidende computationele wiskunde. Het boek heeft meer dan 40 functies en 160 rekenvoorbeelden, en het behandelt wat ik denk dat een grondige basis is in het gebruik van MATLAB voor numerieke wetenschappelijke berekeningen. Dus deels als zelfverbetering, en deels om de bruikbaarheid van het boek te vergroten, ben ik dit jaar begonnen met het vertalen van de codes naar Julia en Python. Deze ervaring heeft mij geleid tot een bijzondere kijk op de drie talen in relatie tot wetenschappelijk rekenen, die ik hieronder probeer weer te geven.
Ik zal de kwesties van kosten en openheid grotendeels terzijde schuiven. MATLAB is, in tegenstelling tot Python en Julia, noch biervrij noch spraakvrij. Dit is inderdaad een groot onderscheid – voor sommigen een beslissend onderscheid – maar ik wil het hebben over de technische verdiensten. Jarenlang was MATLAB op een aantal zeer nuttige manieren veel beter dan welk gratis product dan ook, en als je productief wilde zijn, dan waren de kosten niet van belang. Dat staat los van de Platonische aantrekkingskracht van een taal en een ecosysteem.
Als je de kosten opzij zet, is een nuttig kader voor veel van de verschillen tussen deze talen te vinden in hun oorsprong. MATLAB, de oudste van de pogingen, gaf voorrang aan wiskunde, in het bijzonder numeriek georiënteerde wiskunde. Python, dat eind jaren tachtig serieus van start ging, stelde informatica centraal. Julia, dat in 2009 van start ging, probeerde een beter evenwicht tussen deze twee te vinden.
MATLAB
Oorspronkelijk was elke waarde in MATLAB een array van double-precision floating point getallen. Beide aspecten van deze keuze, arrays en drijvende komma, waren geïnspireerde ontwerpbeslissingen.
De IEEE 754 standaard voor drijvende komma’s werd zelfs pas in 1985 aangenomen, en geheugen werd gemeten in K, niet in G. Drijvende komma’s waren niet de meest efficiënte manier om tekens of gehele getallen weer te geven, maar het was wat wetenschappers, ingenieurs, en in toenemende mate, wiskundigen het meest van de tijd wilden gebruiken. Bovendien hoefden variabelen niet te worden gedeclareerd en hoefde geheugen niet expliciet te worden toegewezen. Door die taken aan de computer over te laten, en datatypes uit de weg te ruimen, kon je beter nadenken over de algoritmen die op de gegevens zouden werken.
Arrays waren belangrijk omdat numerieke algoritmen in lineaire algebra hun intrede deden, in de vorm van LINPACK en EISPACK. Maar om ze te benaderen met de standaard drager in wetenschappelijke berekeningen, FORTRAN 77, was een proces van meerdere stappen, waarbij variabelen moesten worden gedeclareerd, cryptisch genoemde routines moesten worden aangeroepen, code moest worden gecompileerd, en vervolgens gegevens en uitvoerbestanden moesten worden onderzocht. Het schrijven van een matrix vermenigvuldiging als A*B
en het antwoord meteen afgedrukt krijgen was een spel-veranderaar.
MATLAB maakte grafieken ook eenvoudig en veel toegankelijker. Geen lastige machine-specifieke bibliotheken met aanroepen op laag niveau, gewoon plot(x,y)
en je zag vrijwel hetzelfde als ieder ander met MATLAB zou zien. Er waren meer innovaties, zoals ingebouwde complexe getallen, sparse matrices, gereedschappen om cross-platform grafische gebruikersinterfaces te bouwen, en een toonaangevende suite van ODE oplossers, die van MATLAB de plek maakten om wetenschappelijke berekeningen te doen met de snelheid van het denken.
Het ontwerp dat ideaal was voor interactieve berekeningen, zelfs lange, was echter niet altijd bevorderlijk voor het schrijven van goede en goed presterende software. Het verplaatsen van gegevens tussen vele functies vereiste jongleren met veel variabelen en veelvuldig raadplegen van documentatie over invoer- en uitvoerargumenten. Eén functie per disk file in een platte namespace was verfrissend eenvoudig voor een klein project, maar een hoofdpijn voor een groot project. Bepaalde programmeerpatronen (vectorisatie, geheugen preallocatie) moesten worden toegepast als je snelheidsknelpunten wilde vermijden. Wetenschappelijk rekenen werd nu toegepast op veel meer domeinen, met enorme hoeveelheden van verschillende native soorten gegevens. Enz.
MathWorks reageerde door te blijven innoveren binnen MATLAB: inline functies, geneste functies, variabele afsluitingen, talrijke datatypes, object-georiënteerde functies, unit testing raamwerken, en ga zo maar door. Elke innovatie was waarschijnlijk de oplossing voor een belangrijk probleem. Maar de opeenstapeling van 40 jaar van deze veranderingen heeft als neveneffect gehad dat de eenvoud en eenheid van concept is uitgehold. In 2009 schreef ik een boek dat in minder dan 100 pagina’s vrij goed behandelde wat ik beschouwde als de essentie van MATLAB. Voor zover ik weet, zijn al die dingen nog steeds beschikbaar. Maar je moet nu veel meer weten om jezelf vaardig te noemen.
Python
In zekere zin lijkt de geschiedenis van Python bijna een spiegelbeeld van die van MATLAB te zijn. Beiden hadden een interactieve opdrachtregel (nu algemeen een REPL genoemd, voor “read-eval-print loop”) en vrijheid van variabele declaraties en compilatie. Maar MATLAB was gemaakt als een speeltuin voor numerieke analisten, terwijl Python was gemaakt met hackers in het achterhoofd. Elk groeide vervolgens naar het andere publiek toe door middel van revisies en uitbreidingen.
Op het oog mist Python nog steeds mathematische aantrekkingskracht. Je hebt lelijkheid en kleine ergernissen zoals **
in plaats van ^
, @
voor matrixvermenigvuldiging (een recente innovatie!), een shape
in plaats van de grootte van een matrix, rij-georiënteerde opslag, enz. Als u denkt dat V.conj().T@D**3@V
een elegante manier is om $V^*D^3V$ te schrijven, dan moet u misschien naar de dokter. En er is de nul-indexering (in tegenstelling tot indexen die beginnen bij 1). Ik heb de argumenten gelezen, en ik vind ze niet doorslaggevend. Het is duidelijk een kwestie van voorkeur – het onderwerp van online heilige oorlogen – want je kunt voor beide conventies onhandige voorbeelden aanhalen. Wat ik doorslaggevend vind is dat we tientallen jaren wiskundige praktijk hebben om vectoren en matrices te indexeren vanaf één, en de meeste pseudocode maakt die aanname.
Afgezien van de kleine ergernissen, vind ik het Python+NumPy+SciPy ecosysteem kludgy en inconsistent. Bewijsstuk A is het feit dat, ondanks dat de taal nogal toegewijd is aan object oriëntatie, er een matrix klasse bestaat, en toch wordt het gebruik ervan ontmoedigd en zal het worden afgeschreven. Misschien heeft MATLAB mij gewoon corrupt gemaakt, maar ik vind matrices een belangrijk genoeg type object om te behouden en te promoten. Is het niet een belangrijk verkoopargument van OOP dat je *
verschillende dingen kunt laten doen voor arrays en matrices? Er zijn vele andere ongerijmdheden in dit verband. (Waarom heb ik een commando nodig dat spsolve heet? Kan ik niet gewoon solve
aanroepen op een sparse matrix? En ga zo maar door.)
Er zijn ook plaatsen waar het numerieke ecosysteem mij wat dunnetjes overkomt. Bijvoorbeeld, de kwadratuur en ODE oplossers zien eruit als een minimale set in 2019. AFAICT er zijn geen methoden voor DAEs, DDEs, symplectische solvers, of impliciete solvers die innerlijke Krylov iteraties toestaan. Kijk eens naar de referenties voor deze functies; ze zijn meestal 30 of meer jaar oud – nog steeds goed, maar verre van compleet. Het Matplotlib pakket is een geweldig stukje werk, en een tijdje zag het er beter uit dan MATLAB, maar ik vind het nog steeds behoorlijk tekortschieten in 3D.
Sommige experts beweren dat er diepgaande redenen zijn waarom Python code moeite heeft om de uitvoeringssnelheid bij te houden van gecompileerde talen. Ik ben geamuseerd door de resultaten van het zoeken naar “python is te traag”. De voorvechters van Python maken veel van dezelfde argumenten/verontschuldigingen die mensen vroeger voor MATLAB deden. Dat wil niet zeggen dat ze ongelijk hebben, maar er is meer dan alleen een perceptie probleem.
Ik denk dat ik begrijp waarom Python zo opwindend is geweest voor veel mensen in de wetenschappelijke informatica. Het heeft een MATLAB-achtige syntaxis en kracht, beschikbaar via een REPL. Het heeft geweldige gereedschappen en speelt goed samen met andere talen en gebieden van de informatica. Het biedt dat aan zonder kosten en met een veel betere reproduceerbaarheid op lange termijn. Het is duidelijk dat het goed werkt voor veel mensen die waarschijnlijk weinig reden zien om te veranderen.
Maar voor de dingen die ik weet hoe te doen in de wetenschappelijke informatica, voelt Python veel meer als een karwei om te leren en te gebruiken dan ik gewend ben. Het zal nog wel even duren voordat we weten of het de gemeenschap blijft overspoelen of dat het zijn hoogtepunt al heeft bereikt. Ik heb geen speciale voorspellende gaven, maar ik ben bearish.
Julia
Julia heeft de voor- en nadelen van een laatkomer te zijn. Ik juich het toe dat de makers van Julia denken dat ze het beter kunnen:
We willen een taal die open source is, met een liberale licentie. We willen de snelheid van C met de dynamiek van Ruby. We willen een taal die homoiconisch is, met echte macro’s zoals Lisp, maar met een voor de hand liggende, vertrouwde wiskundige notatie zoals Matlab. We willen iets dat net zo bruikbaar is voor algemeen programmeren als Python, net zo makkelijk voor statistiek als R, net zo natuurlijk voor string processing als Perl, net zo krachtig voor lineaire algebra als Matlab, net zo goed in het aan elkaar lijmen van programma’s als de shell. Iets dat eenvoudig te leren is, en toch de meest serieuze hackers tevreden houdt. We willen het interactief en we willen het gecompileerd.
Voor een groot deel geloof ik dat ze daarin geslaagd zijn. Laat op de weg naar versie 1.0 leken ze de REPL een beetje te bagatelliseren, en er waren enkele bijna gratuite uitstapjes weg van MATLAB. (Hoe precies is LinRange
beter dan linspace
?) Dit zijn echter maar kleine puntjes.
Dit is de eerste taal die ik heb gebruikt die verder gaat dan ASCII. Ik krijg nog steeds een onredelijke hoeveelheid voldoening van het gebruik van variabelen als ϕ
en operatoren als ≈
. Het is meer dan cosmetisch; in staat zijn om meer te lijken op de wiskundige expressies die we schrijven is echt een pluspunt, hoewel het het onderwijs en de documentatie een beetje bemoeilijkt.
Werken in Julia heeft me laten zien dat ik sommige programmeergewoonten heb opgepikt door de keuzes van MATLAB, niet door de inherente superioriteit. Vectorisatie is niet natuurlijk voor veel dingen. Het is een openbaring om in Julia te ontdekken dat je elke functie kunt vectoriseren door een punt aan de naam toe te voegen. Het construeren van een matrix door middel van een comprehension doet geneste lussen (of meshgrid
trucs) lijken op buggy zweepslagen in vergelijking, en het vermijden van een matrix via een generator voor een eenvoudige optelling voelt als iets voor niets krijgen. (Ik ben me ervan bewust dat Python vergelijkbare taaleigenschappen heeft.)
De grote eigenschap van multiple dispatch maakt sommige dingen een stuk eenvoudiger en duidelijker dan object oriëntatie dat doet. Bijvoorbeeld, stel je hebt Muur en Bal klassen in een traditionele object-georiënteerde taal. Welke klasse moet een botsing van een Bal met een Muur detecteren? Of heb je een Room klasse nodig om scheidsrechter te spelen? Dit soort vragen kan mij tot afleiding drijven. Met multiple dispatch wordt data verpakt in object types, maar de methodes die de data bewerken zijn niet gebonden aan een klasse. Dus
function detect_collision(B::Ball,W::Wall)
weet van de types, maar is onafhankelijk ervan gedefinieerd. Het heeft me heel wat programmeerwerk gekost voordat ik inzag hoe interessant en potentieel belangrijk de notie van multiple dispatch is voor het uitbreiden van de taal.
Het numerieke ecosysteem heeft zich snel ontwikkeld. Mijn nummer één voorbeeld is DifferentialEquations.jl, geschreven door de geweldige Chris Rackauckas. Als deze software niet snel de Wilkinson prijs wint, is het systeem kapot. Ga gewoon naar de site en bereid je voor om bekeerd te worden.
Ik moet de grote snelheidswinst ten opzichte van MATLAB die Julia belooft nog zien. Deels is dat mijn relatieve onervarenheid en het soort taken dat ik doe, maar het is ook deels omdat MathWorks een ongelooflijk werk heeft gedaan met het automatisch optimaliseren van code. Het is trouwens niet een aspect van coderen waar ik me meestal op concentreer.
Programmeren in Julia heeft me een tijdje gekost om me er comfortabel bij te voelen (misschien word ik gewoon oud en gekristalliseerd). Het laat me meer nadenken over datatypes dan ik zou willen, en er is altijd het sluipende vermoeden dat ik de juiste manier gemist heb om iets te doen. Maar voor dagelijks gebruik wend ik me nu ongeveer net zo snel tot Julia als tot MATLAB.
De bottom line
MATLAB is de bedrijfsoplossing, vooral voor engineering. Het is waarschijnlijk nog steeds het gemakkelijkst te leren voor numerieke basistaken. Nauwkeurige documentatie en decennia van bijgedragen leermiddelen zijn zeker van belang.
MATLAB is de BMW sedan van de wetenschappelijke computerwereld. Het is duur, en dan heb ik het nog niet eens over de accessoires (toolboxes). Je betaalt voor een rotsvaste, soepele prestatie en service. Het trekt ook een onevenredige hoeveelheid haat aan.
Python is een Ford pick-up. Het is alomtegenwoordig en geliefd door velen (in de VS). Het kan alles doen wat je wilt, en het is gebouwd om sommige dingen te doen die andere voertuigen niet kunnen. De kans is groot dat je er af en toe een wilt lenen. Maar het biedt geen geweldige pure rijervaring.
Julia is een Tesla. Het is gebouwd met een gewaagd doel van het veranderen van de toekomst, en het zou kunnen. Het kan ook slechts een voetnoot worden. Maar in de tussentijd kom je waar je heen gaat in stijl, en met kracht om te sparen.