你上世紀寫的代碼現在還work嗎?挑戰者:我需要讀磁帶的機器
代碼找不到、硬件已過時、文檔也缺失…… 幾十年前的代碼復現起來沒那么容易。

「敢不敢把你十年前寫的代碼翻出來看看還能不能運行?」在最近的一篇社論文章中,《Nature》介紹了兩位法國科學家發起的一項挑戰。
這項挑戰名為「Ten Years Reproducibility Challenge」,由法國國家數字科學技術研究所的計算機神經科學家 Nicolas Rougier 與法國國家科學研究中心的理論生物物理學家 Konrad Hinsen 共同發起,旨在鼓勵各個領域的研究者翻出自己十年前(或更早)的代碼,看看這些代碼到底還能不能運行,研究能否復現。

Rougier 認為,盡管計算在科學研究中占有越來越重要的比重,但研究者很少披露自己的底層代碼。即使他們給出了代碼,別人也很難去執行,就連作者本人有時也會遇到麻煩。而且,隨著編程語言、計算環境的演化,現在還能運行的代碼過段時間可能就不行了。
因此,「『十年代碼復現挑戰』的宗旨是找出那些十年之后仍然能夠 work 的代碼編寫和發布技術,」Hinsen 表示。
這項比賽共吸引了 35 名參賽者。他們提出要復現 43 篇論文,其中 28 篇形成了可復現報告。這些論文涉及的語言包括 C、R、Mathematica 和 Pascal 等等,還有一位參賽者復現的不是代碼,而是一個用 SBML(系統生物學標記語言)編寫的分子模型。
當然,這條復現之路并沒有想象中那么簡單,有人代碼找不到了,有人找到代碼也不知道怎么運行。最后,他們通過這項比賽總結出了一些提高代碼可復現性的技巧,可以為現在的工作提供借鑒。
老代碼沒那么容易 work
你的代碼還在嗎?
要完成這個挑戰,首先你得找到自己當年的代碼,有人在這一步就被卡住了。
Roberto DiCosmo 是法國國家信息與自動化研究所的一位計算機科學家,他在 1998 年的一篇論文中提到了一個名為「OcamlP3l」的并行編程系統。但在找遍自己和合著者的硬盤、備份之后,他也沒能找到 OcamlP3l 系統的代碼。
不過幸運的是,一個名為「Software Heritage」的源代碼歸檔網站為他提供了一份備份。

Software Heritage 會定期抓取 GitHub 等開源代碼網站,有點像定期抓取網頁的互聯網檔案館(Internet Archive)。開發者也可以請求 Software Heritage 抓取自己的庫留作存檔。
起初,DiCosmo 并沒有考慮去 Software Heritage 找自己的代碼,因為在他開發 OcamlP3l 的時候,Software Heritage 還沒出現。他猜測,一定是有人將他的代碼傳到了 Gitorious 托管平臺,而 Software Heritage 又在該平臺關停之前抓取了上述代碼。
你的文檔還在嗎?
「在一個組織良好的項目里,文檔的行數超過代碼行數不是什么稀罕事兒,」加州大學伯克利分校的一位計算可復現性倡導者表示,「你要保留盡可能多的信息,對分析的結構有更廣泛的描述,比如數據從哪兒來,數據、代碼的一些元信息等,這些是復現的關鍵。」
Melanie Stefan 是愛丁堡大學的一位神經科學家,她想復現一個用 SBML 寫的計算模型。盡管模型都在,但她卻找不到自己當年用的參數值(如分子濃度),也沒有很好地記錄數據標準化的關鍵細節。
因此,Stefen 無法復現她的部分研究。「即使對于同一個人來說,很多十幾年前再明顯不過的模型細節現在也不明顯了,真是令人始料未及!」她面無表情地說道。
你有運行代碼的硬件嗎?
作為比賽的組織者,Rougier 也參加了這次挑戰。他重現的代碼是 Apple II 中的一個圖像放大器,這是整個挑戰賽中最古老的代碼。這段代碼寫于 32 年前,當時寫的時候 Rougier 才 16 歲,還發表在了一本名為《Tremplin Micro》的雜志上(已倒閉)。
如今,即使拿著神秘的 AppleSoft BASIC 語言說明,他也不記得代碼是怎么運行的了。「真是見了鬼了,這可是我自己寫的,」Rougier 惆悵地說道。
但是,他可以在網上找到這段代碼并使其在一個網頁版 Apple II 模擬器上運行。要做到這點并不難,Rougier 表示,最難的部分是讓它在一個真正的 Apple II 上運行。
對于 Rougier 來說,硬件不是問題,因為他辦公室就有一臺 Apple II,是一位同事在清理辦公室時搶救出來的。但由于這款 Apple II 的年齡比 USB 線和互聯網都大,而且當前的計算機無法與它的老式磁盤驅動相連。因此,在運行代碼之前,Rougier 需要某種定制的硬件以及一盒老式磁盤。
他在亞馬遜上找到了一些帶有「New」字樣的磁盤,但日期是 1993 年的。在對他的數據進行三次寫入以確保比特穩定之后,磁盤開始運行了。
活動的發起者 Hinsen 也遇到了硬件方面的麻煩。他把自己 90 年代寫的代碼有條不紊地存到了磁帶里,但現在,他已經沒有能讀取磁帶的工具了。
你的計算環境過時了嗎?
過時的計算環境也是壓死參賽者的一根稻草。Sabino Maggi 是意大利的一名計算機物理學家,1996 年,他用 Fortran 語言建模了一個超導裝置,并用 Microsoft Visual Basic 來處理結果。二十多年過去了,Fortran 并沒有發生太大變化,因此 Maggi 只做了些微的調整就實現了代碼的順利編譯。但始料未及的是,Visual Basic 給他出了一個難題。
Maggi 在報告中寫道,「Visual Basic 是一門死掉的語言,早就被 Visual Basic.NET 取代了。」所以,為了運行二十多年前的 Visual Basic 代碼,Maggi 不得不使用從網上找到的安裝盤在自己的 Mac 電腦上重建了一個十年前的 Windows 虛擬機。
在安裝之前,Maggi 遇到了一個問題:他根本不記得自己 96 年用的是哪個版本。這些年,微軟發布了該語言的多個版本,而且并不總是向后兼容的。

模擬 1994 年的 Windows 計算機運行 Microsoft Visual Basic 的 Mac。
同樣受到計算環境問題困擾的還有 Ludovic Courtès,他是法國國家信息與自動化研究所的一名研究工程師。在這次挑戰賽中,他復現了 2006 年一篇比較數據壓縮策略的論文,代碼是用 C 語言寫的。由于 API 發生了變化,他的代碼無法用現有軟件庫進行編譯。為了解決這一問題,他不得不將 6 個計算組件回滾到很老的版本。
如今,研究者們可以用 Docker 和 Conda 虛擬環境來打包計算環境,以備不時之需。但有幾位參賽者選擇了其他方法,比如 Guix(一個 Linux 包管理器)。它可以保證環境直到最后一位都是可復現的,并且構建環境的代碼版本是透明的。
「環境和整篇論文都可以檢查,可以從源代碼構建,」Courtès 表示。Hinsen 認為,Guix 可能是這個比賽「目前最好的可復現研究工具」。
違反直覺的是,很多參賽者發現,用一些比較古老的語言寫的代碼反而是最容易復現的。新語言快速變化的 API 和對第三方庫的依賴使得它們很難復現。從這個意義上來說,今年剛剛停止支持的 Python 2.7 倒是一個不錯的機會,它既是一門高級編程語言,又不會再進行更新。
如何提高代碼可復現性?
在經歷了復現代碼的艱辛之后,相信每位參賽者都意識到了自己當年寫代碼時埋下的一些「隱患」,比如存儲介質、所選語言、備份平臺等。
那么,如何提高論文代碼的可復現性呢?《Nature》文章的作者在文中給出了一個 checklist:
1. 代碼。基于即點即擊(point-and-click)界面的工作流(如 Excel)是不可復現的。你要將計算和數據操作保存在代碼中;
2. 文件。使用注釋、計算筆記本、README 文件等解釋你的代碼如何運行,定義期望的參數和所需的計算環境;
3. 記錄。記下關鍵參數,如用于啟動隨機數生成器的「seed」值。這樣的記錄可以幫你重新運行代碼、跟蹤 bug 以及意外的結果;
4. 測試。創建一套測試函數。使用 positive 和 negative 控制數據集來確保你得到預期的結果,并在開發過程中運行這些測試,在 bug 出現時及時清除;
5. 保存。GitHub 是一個流行但并不永久的在線存儲庫。長期來看,Zenodo、Figshare 和 Software Heritage 等歸檔服務可能更加穩定;
6. 跟蹤。使用 Git 等版本控制工具來記錄你的項目歷史,記下產生每個結果所用到的版本;
7. 打包。利用容器化工具(Docker、Singularity 等)、網頁服務(Code Ocean、Gigantum、Binder)、虛擬環境管理器(Conda)等創建隨時可用的計算環境;
8. 自動化。使用 Travis CI 等持續集成服務在不同的計算環境中定期自動測試你的代碼;
9. 簡化。避免使用會使后續利用復雜化的小眾或難以安裝的第三方代碼庫;
10. 檢查。通過在一系列計算環境中運行代碼來檢查代碼的可移植性。
此外,曼徹斯特大學的計算機科學家 Carole Goble 指出,將自己的代碼開源也是一種提高可復現性的方式,這樣別人就有機會在你的代碼基礎上進行修改,以保持其活力。
如果你也有十幾、二十幾年前寫的代碼,可以拿出來試試還能不能運行。