Compiling external C++ Libraries on Colab and bringing the nnue-probe library to Colab

Roger Lorenz
5 min readNov 15, 2023

Nowadays, there are many tools, that allow you to use your browser as your development environment. You edit your code in the browser and then execute and test it directly there. This saves the effort of having to set up your own environment and makes it possible to share your own code with others. Google Colab is certainly the best-known variant for this approach.

Normally you use Python as a programming language in Colab. If Python libraries are missing, you can quickly install them with the !pip command. But what do you do if you want to use a library in Colab that is not implemented in Python? What if this library is implemented in C++? I recently faced exactly this challenge and it took me some time, to come up with a solution. Since this solution might also be interesting for others, I have documented my approach in this article.

The Challenge

I am currently planning a new project developing a simple chess engine that uses a neural network for position evaluation (instead of e.g. counting material). To keep things simple, I would like to use an existing network. Training my own new network would be too time-consuming for me at the moment. Also I would like to run the engine in a Colab notebook to be able to share the code and make it easy for others to play against my engine.

During my research I came across a library that makes it possible to use a Stockfish net (Stockfish nets are also called nnue, see here for a detailed description). The nnue-probe library (https://github.com/dshawul/nnue-probe) by Daniel Shawul offers exactly the functionality I need. After loading the net you can evaluate each chess position represented by a FEN-string.

Using the library in Python would look like this:

from __future__ import print_function
from ctypes import *
nnue = cdll.LoadLibrary("libnnueprobe.so")
nnue.nnue_init(b"nn-04cf2b4ed1da.nnue")
score = nnue.nnue_evaluate_fen(b"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
print("Score = ", score)

And the result would be (may vary if a different nnue file is used):

Loading NNUE : nn-04cf2b4ed1da.nnue
NNUE loaded !
Score = 42

Looks great, but still one problem remains. The library is not available on Colab and — since it is implemented in C++ — cannot be easily installed either.

The Solution

There will certainly be different solutions to my problem like cross compiling. But I focused on translating the library directly in Colab. So the first step was downloading the code to colab. This can be done in this way:

!git clone https://github.com/dshawul/nnue-probe ./nnue_li

As a result of this operation, there is now a folder nnue_lib in your Colab environment. Keep in mind, that this only temporarily. After restarting the Colab environment the folder will be gone.

Navigating to the new folder is easy.

!cd nnue_lib;pwd;ls -al

/content/nnue_lib
total 56
drwxr-xr-x 4 root root 4096 Nov 15 15:07 .
drwxr-xr-x 1 root root 4096 Nov 15 15:07 ..
drwxr-xr-x 8 root root 4096 Nov 15 15:07 .git
-rw-r--r-- 1 root root 35141 Nov 15 15:07 LICENSE
-rw-r--r-- 1 root root 724 Nov 15 15:07 README.md
drwxr-xr-x 2 root root 4096 Nov 15 15:07 src

And compiling the library is also a one line command.

!cd nnue_lib/src; make

make libnnueprobe.so strip
make[1]: Entering directory '/content/nnue_lib/src'
g++ -Wall -fstrict-aliasing -fno-exceptions -fno-rtti -Wno-unused-variable -Wno-unused-result -Wno-unused-but-set-variable -Wno-maybe-uninitialized -std=c++11 -Ofast -fomit-frame-pointer -DIS_64BIT -DUSE_AVX2 -mavx2 -DUSE_SSE41 -msse4.1 -DUSE_SSE3 -msse3 -DUSE_SSE2 -msse2 -DUSE_SSE -msse -c -fPIC -o misc.o misc.cpp
g++ -Wall -fstrict-aliasing -fno-exceptions -fno-rtti -Wno-unused-variable -Wno-unused-result -Wno-unused-but-set-variable -Wno-maybe-uninitialized -std=c++11 -Ofast -fomit-frame-pointer -DIS_64BIT -DUSE_AVX2 -mavx2 -DUSE_SSE41 -msse4.1 -DUSE_SSE3 -msse3 -DUSE_SSE2 -msse2 -DUSE_SSE -msse -c -fPIC -o nnue.o nnue.cpp
g++ -o libnnueprobe.so misc.o nnue.o -lm -ldl -shared
strip libnnueprobe.so
make[1]: Leaving directory '/content/nnue_lib/src'

!ls -al nnue_lib/src/*.so

-rwxr-xr-x 1 root root 22904 Nov 15 15:14 nnue_lib/src/libnnueprobe.so

Now only the nnue file is missing. This can be downloaded from the stockfish neural network repository. But there is a catch. The nnue_lib only works with older nnue files. These can be identified — unfortunately only after the download — by the fact that they are approximately 21 MB in size. But you can search for a specific nnue file (like “nn-04cf2b4ed1da.nnue”, which is from October 2020).

Downloading a file to Colab is easy and can be done like this:

!wget /content/nnue_lib/nn-04cf2b4ed1da.nnue https://tests.stockfishchess.org/api/nn/nn-04cf2b4ed1da.nnue -P /content/nnue_lib

Now we can test the probing code (after adaption of the paths for the library and the nnue file):

from __future__ import print_function
from ctypes import *
nnue = cdll.LoadLibrary("/content/nnue_lib/src/libnnueprobe.so")
nnue.nnue_init(b"/content/nnue_lib/nn-04cf2b4ed1da.nnue")
score = nnue.nnue_evaluate_fen(b"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
print("Score = ", score)

Output:

Score =  42

Voila. Mission accomplished. If you want, you can find the Colab notebook I used for compiling and testing here.

One final note to the score value of 42. According to the Stockfish Evaluation Guide this value is equivalent to an engine evaluation of 0.20. So I guess, that you have to divide the score result by 210 to be able to compare the evaluation to other chess engines.

Summary and next Steps

In the beginning I struggled with finding the right approach for the solution. But after I realized that Colab is not only an environment for executing notebooks, but provides a complete Linux environment with all the usefull tools, the solution was straight forward. Just set up Colab in the same way you would set up your Linux machine. But don’t forget, that all changes to a Colab environment are temporarily. If you or Google restarts the environment, all changes are gone. So you have to automize everything.

My next step will now be to develop a simple chess engine which uses the nnue_probe library to evaluate the positions. I will post my findings on Medium shortly.

If anyone knows other approaches how to reuse existing evaluation networks in a Python chess engine, please let me know.

--

--

Roger Lorenz

MSC in Computer Science, Chess Player, Chess Historian