Storbrittannien ändrade radikalt sin covid-19-strategi efter en serie rapporter från Imperial College London, särskilt https://www.imperial.ac.uk/media/imperial-college/medicine/mrc-gida/2020-03-16-COVID19-Report-9.pdf. Rapporterna baserades till stor del på en modell vars källkod nu finns tillgänglig https://github.com/mrc-ide/covid-sim/. Jag har läst koden.
Precis som många andra programmerare som har tittat på koden var min första reaktion olika chockerade kraftuttryck. “Hemskt”, “omg”, “herregud”, “wtf”. Men det är givetvis orättvist att komma med den sortens kritik utan att förklara och nyansera. Först lite bakgrund.
Koden implementerar en iterativ (stegvis) stokastisk (slumpmässig) spatiell (platsbaserad) modell. Den använder sig av ett stort antal parametrar för att styra sina antaganden, men det är inte egentligen någon svår statistik inblandad, bara en ovanligt detaljerad simulering.
Vissa har liknat det här vid Sim City, det är en grov förenkling, men leder tankarna rätt. Modellen börjar med att lägga ut människor, bostäder, resvägar, sjukhus, flygplatser, hotell m.m. på ett stort rutnät.
Sedan inför man ett antal smittade personer, varefter man steg för steg simulerar hur smittan sprider sig. Om modellen är bra beskriver den ett möjligt förlopp för smittan.
Genom att sätta olika parametrar i koden kan man påverka hur den utvecklar sig. Hur dessa parametrar sätts är väldigt viktigt för hur realistisk modellen är. För att modellen ska matcha verkligheten måste flera saker stämma samtidigt.
Startvillkoren måste vara lika verkligheten.
Modellen måste tillräckligt realistiskt sätt simulera vad som händer i varje steg
Parametrarna som styr simuleringen måste vara satta så att de liknar omständigheterna som finns i verkligheten.
Alla dessa saker kan gå fel, var och en för sig, men eftersom det är en iterativ modell kan små fel leda till stora skillnader i slutresultatet.
Tänk dig att du följer en felritad karta slaviskt. Om avståndet mellan två städer är lite fel så hamnar du lite fel. Men om du sedan fortsätter mellan städer bara baserat på kartan hamnar du till slut väldigt långt ifrån där du tänkt.
Parametrarna som används i just denna modell finns här, totalt ca ett hundratal: https://github.com/mrc-ide/covid-sim/blob/c56666279a4de0337bd6847efc1a3c76b78f2e10/src/Param.h Vissa används inte i alla simuleringstyper, vissa har standardvärden osv.
De faktiska parametervärden som användes för att producera rapporterna finns tyvärr inte tillgängliga, men det finnns exempeldata för UK här https://github.com/mrc-ide/covid-sim/blob/master/data/param_files/preUK_R0%3D2.0.txt
Observera att det alltså inte är dessa parametrar som faktiskt använts, det är bara ett exempel.
Experter och epidemiologer försöker på bästa möjliga sätt ta reda på hur dessa parametrar är i verkligheten. Ett exempel är den omdiskuterade R0 (hur många personer en smittad person förväntas smitta). Men i denna simulation finns många fler parametrar som påverkar utfallet
För att vi ska kunna ha en bra vetenskaplig diskussion om den här modellen behöver vi alltså kunna förstå dessa parametrar och hur de används, så att vi kan utvärdera i vilken utsträckning det som görs är rimligt.
Som programmerare är det svårt att utvärdera hur realistiskt parametrarna som är valda är uppmätta. Men det viktigaste modellen gör är att den använder parametrarna för att räkna ut nästa steg i modellen. För att göra det måste man förstå vad koden gör. Det här kan jag utvärdera.
Till koden. Några initiala observationer. Koden var ursprungligen i en enda fil på 15000 rader kod. Koden i detta repo är en uppstädad version, där olika filer gör olika saker. Trots det är många av funktionerna är flera hundra rader långa och flera skärmbredder breda.
Den kanske viktigaste funktionen är funktionen InfectSweep, som modellerar själva infektionsspridningen, den finns här: https://github.com/mrc-ide/covid-sim/blob/master/src/Sweep.cpp#L267. Den är ca 500 rader lång och på sina ställen nästan 200 tecken bred. Den är också djupt nästad, tio nivåer på vissa ställen.
Tänk dig ett stycke text som har en bisats med 10 nivåers djup. Heideggers prosa är lättläst i jämförelse. Programmerare är förvisso tränade att hantera sånt här, men det är ändå mycket svårläst.
Här finns datastrukturen som beskriver nuvarande tillstånd för en enskild person i modellen:
https://github.com/mrc-ide/covid-sim/blob/c56666279a4de0337bd6847efc1a3c76b78f2e10/src/Model.h#L16 Och här är det globala tillståndet i modellen https://github.com/mrc-ide/covid-sim/blob/c56666279a4de0337bd6847efc1a3c76b78f2e10/src/Model.h#L90 Notera kommentaren “TODO: Detailed explanation” :)
Här är koden som implementerar logik för inrikes flygresor, notera kommentaren “Not used for COVID-19 right now.[...]. Slows the simulation.”. Synd, hoppas det inte är så viktigt. : https://github.com/mrc-ide/covid-sim/blob/c56666279a4de0337bd6847efc1a3c76b78f2e10/src/Model.h#L231
Som programmerare läser man kod ungefär som om det var vanlig text. Man måste kunna hålla kontext i huvudet. När en funktion är flera sidor lång är det precis som att läsa ett stycke som täcker flera sidor, utan skiljetecken. Svårbegripligt.
Den här funktionen implementerar grundtillståndet. https://github.com/mrc-ide/covid-sim/blob/master/src/SetupModel.cpp#L26 Den är ca 600 rader lång. Det är mycket långt och svårt att förstå. Fattas någon parameter? Att felsöka detta är som att korrläsa någon av Hegels mer obskyra verk.
Ett bra datorprogram är precis som en bra vetenskaplig text kortfattad, korrekt och tydlig. När kod är lång, bred, har många konstiga namn och komplicerade funktioner är det ett dåligt tecken. Code smell kallar vi det i branschen. Dålig lukt.
Jag körde med hjälp av @lewisporter koden genom en automatisk kodgranskare, i det här fallet SonarQube. Det är ungefär som en stavningskontroll för kod. Ibland får du rött för att du har skrivit ett ord som inte finns i ordboken. Men oftast är det du som har stavat fel.
Här är resultatet. 1.4k i en såhär liten kodbas är relativt mycket. I vanliga fall strävar man efter att ha så få defekter av den här typen, helst inga, eftersom de är så lätta att hitta automatiskt.
För att testa att programkod fungerar brukar man även skriva tester, små testprogram som kontrollerar att delarna av programmet fungerar som det är tänkt.
Det finns några tester i den här kodbasen, här: https://github.com/mrc-ide/covid-sim/tree/master/tests Men dessa tester är så kallade regressionstest. Testet jämför resultatet av en körning med en känd mängd data, parametrar och ett visst slumpfrö med ett känt tidigareresultat av körningen.
Ett regressionstest visar alltså inte egentligen att koden är rätt, det den gör är att den jämför med tidigare resultat av koden. Om det inte stämmer överens är det en regression, ett fel. Om den var rätt från början alltså.
Det här kan vara bra för att inte oavsiktligt ha sönder något, men om modellen var fel till en början kan dessa tester inte visa det. De kan inte heller hjälpa oss att förstå hur de olika delarna är tänkta att fungera.
Ett bättre sätt är att testa så små bitar som möjligt av koden. När man vet att alla enskilda delar fungerar kan man sätta ihop dom till en större struktur som man också kan testa. Det här kallas enhetstestning, unit testing. Dela och erövra.
För att man ska kunna göra detta måste koden vara organiserad så att enheterna är oberoende av varandra. Exempelvis kanske man vill förstå koden som simulerar hur social distansiering påverkar modellen baserat på parametern SocDistChangeDelay.
Men i den här kodbasen är den här biten av koden inte isolerad alls, allt är ihopslaget i en megastor funktion med alla detaljer synliga samtidigt. Det är omöjligt att köra bara den biten av koden eftersom den är inblandad med allt annat.
Det saknas alltså inte bara tester, det är omöjligt att skriva dom som den är organiserad. Det är för mig väldigt oklart hur man har förstå om man exempelvis har lyckats modellera spridningen på sjukhus på ett bra sätt. Utvecklingen måste ha gjorts ad-hoc och med små inkrement.
Det är klart en text ibland kan fungera även med massor av felstavade ord, man “fattar ju vad det betyder”. Den kan ändå innehålla värdefulla insikter.
Men det finns ingen anledning till att publicera vetenskapliga artiklar med mängder av stavfel. Det skulle göra dina forskarkollegor tokiga.
När jag läser den här koden ser det för mig ut som en uppsats som gjorts helt utan stavningskontroll, radbrytningar och satt i Comic sans storlek 18. Jag blir tokig.
Det svåra sitter såklart i detaljerna. Finns det någonstans i koden ett fatalt misstag i hur regeln om hur smitta sprids på sjukhus uppdateras? Räknar den med alla kontaktvägar? Dubbelräknar den vissa hotell? Mycket svårt att svara på när koden ser ut som som den är skriven.
Jag är väldigt van att läsa sån här kod, med ett tränat öga och välskriven kod är det i vanliga fall relativt lätt att svara på sådana frågor. Men i den här koden är det nästan omöjligt att vara säker.
Jag skulle aldrig lita på att den här koden genererar korrekt utdata, för den är så snårigt skriven. Jag skulle bli förvånad om det inte finns åtminstone några ställen där fel indexvariabel har använts, eller där man glömt att uppdatera värden baserat på någon parameter.
Samtidigt är det väldigt bra att vi äntligen får läsa den här koden. För det skulle kunna vara så mycket bättre. Koden skulle kunna skrivas om så att den är lätt att förstå och granska.
Programmerare över hela världen skulle kunna hjälpas åt, som vi redan gör i tusentals öppen-källkodsprojekt.
Akademisk kod har i nuläget andra (lägre) kvalitetskrav än kod som körs i industrin eller öppen källkodsrörelsen, som jag är van vid.
För mig är det här obegripligt, om något borde kraven snarare vara högre. I industrin förlorar man (oftast) bara pengar om det inte fungerar som den ska. I akademin kan vi i värsta fall missförstå världen. Eller följa en felaktig strategi i en global pandemi.
All akademisk kod, särskilt sådan som används för att fatta viktiga beslut, borde vara fritt tillgänglig så att den kan granskas och förbättras!
You can follow @joakimlundborg.
Tip: mention @twtextapp on a Twitter thread with the keyword “unroll” to get a link to it.

Latest Threads Unrolled: