Inside the network of Coherent 0.2.13
Listen to this post9 min
Created by AI from the text of this post and read by a synthetic voice; not a word-for-word version, and it may contain errors.
Loading…
Created by AI from the text of this post and read by a synthetic voice; not a word-for-word version, and it may contain errors.Position and speed stay in this browser and are sent nowhere.
The recording could not be played. Open the audio file
This entry describes the neural network that evaluates positions in Coherent version 0.2.13, as it stands on the main branch on 2026-10-12. The network itself arrived on 2026-10-11, together with the version change from 0.2.12 to 0.2.13; the oracle adopted it in a run on that network. Every number below comes from the source code at that state, from the commits and from the register.
What the network sees
The network gets nothing but the pieces and their squares. It looks at the board from both sides; each of these is called a view. For each view there are 768 binary features: 2 colours, own or opponent's, times 6 piece types, kings included, times 64 squares. 1 feature is active per piece, so in a regular position at most 32.
On top of this, the square of the own king selects 1 of 4 king buckets, each with its own set of 768 features. That makes 3,072 inputs per view. If the own king stands on files e to h, the whole board of that view is mirrored a ↔ h. Black sees the board flipped vertically and with the colours swapped. As a result, swapping the colours of all pieces and the side to move while flipping the ranks 1 ↔ 8 leaves the inputs, in their order, and so the value unchanged.
Whose move it is enters only through the order of the 2 views: the view of the side to move comes first. The network is not told castling rights, en passant, the halfmove clock or repetitions. The search handles the halfmove clock and repetitions outside the network.
Layers and size
| Layer | In → out | Weights | Biases | Computation |
|---|---|---|---|---|
| First layer, per view, same weights for both | 3,072 → 768 | 2,359,296 (int16) | 768 (int16) | sum of the rows of the active features plus bias, then clamped to 0…255 |
| Joining both views | 2 × 768 → 1,536 | – | – | side to move first |
| Hidden layer | 1,536 → 16 | 24,576 (int16) | 16 (int32) | clamp((bias + Σ a·w) >> 14, 0, 255) |
| Hidden layer | 16 → 32 | 512 (int16) | 32 (int32) | clamp((bias + Σ a·w) >> 14, 0, 255) |
| Output | 32 → 1 | 32 (int16) | 1 (int32) | (bias + Σ h·w) × 159 / (255 · 16,384), in centipawns |
In total the network has 2,385,233 parameters, 2,384,416 weights and 817 biases. 98.9 per cent of them sit in the first layer. The data take 4,770,564 bytes, about 4.8 MB. There is 1 output for all positions; the network does not switch between several output heads.
Integers throughout
The network computes only with integers. Activations lie between 0 and 255, where 255 stands for 1.0. The output weights carry a factor of 16,384, and both hidden layers shift their sums right by 14 bits before clamping. The output sums in 32 bits; the scaling is computed in 64 bits, divided by 255 · 16,384 = 4,177,920 and cut off towards zero.
The sums of the first layer are kept in 16-bit words, where an overflow wraps around in a defined way. For the layers after it, checks at compile time prove from the actual weights that no sum leaves the 32-bit range. Because integer addition does not depend on order, the plain path and the vector paths for AVX2 and AVX-512 give the same result; the engine picks a path by the features of the processor.
The first layer is not recomputed for every position. 2 accumulators, 1 per view, are updated move by move: the rows of the pieces that arrive are added, those of the pieces that leave are subtracted. If the king bucket of a view or its mirroring changes, that view is rebuilt from a cache with 8 entries per view, 1 for each bucket with and without mirroring.
What happens after the network
The value of the network is then scaled by the material on the board: value + value · 80 · (material − 3,370) / 1,000,000. Material counts both sides in centipawns: pawn 110, knight 320, bishop 330, rook 500, queen 900, kings 0. In the starting position the material is 8,160 and the factor 1.3832. Then the value is limited to just below the range of mate scores.
In the search, outside the network, a correction from 6 table entries in 5 groups is added, which the search keeps updating as it goes. Then the value is damped with the halfmove clock, value · (208 − min(clock, 100)) / 208, and limited again. The longer the game goes without a pawn move or a capture, the closer the value is pulled to a draw.
From network value to final value: 1. Network value scaled by material on the board; 2. Value limited to just below the mate-score range; 3. Search adds a correction (6 table entries, 5 groups); 4. Value damped with the halfmove clock; 5. Value limited again: final value.
Where the network comes from
The starting point was the previous main network, trained for 32 epochs. It was fine-tuned on 150 million positions from our own self-play games, a part of the training data of that network, labelled by a deeper search of our own: Coherent 0.2.8 with the previous network at 20,000 nodes per position, 1 thread and Hash=16, a table budget of 16 MiB. Positions from our test sets were filtered out of the data beforehand. The recipe: learning rate 1e-5, falling per step to 0, 4 epochs, a WDL weight of 0.5, an output scale of 159 instead of 157 for the previous network. No labels or weights from outside went into it.
In the oracle, the run played this network against its predecessor and passed both stages: short, at 8+0.08 seconds, +24.60 ± 8.58 Elo Strength difference to the opponent, estimated from the games, with a 95-percent half-width where the oracle reported one. Never an absolute rating. after 1,542 games; long, at 40+0.4 seconds, +31.43 ± 9.51 Elo after 1,186 games. The values are conditional logistic Elo estimates against the predecessor in epoch 10, with Book A in the short and Book B in the long stage; ± gives the approximate 95 per cent interval. Both stages reached the acceptance bound of their sequential test, which decides in nElo Normalised Elo: the Elo difference divided by the spread of the game results, so that the SPRT bounds mean the same at different time controls.. They stopped after few games; the values are rough estimates.
How this was checked
The facts come from a report that one of the project's agents wrote at the owner's request; an agent of another model family checked it against the same source tree. For this entry every number was checked again against the source code, the commit messages of the network changes and the register entry of the run, and the calculations were reproduced in Python; an agent of another model family checked the text against the same sources.
Some things were not checked: the hash of the network file was taken as recorded in the source code, not computed again from the file. That the vector paths give the same result was argued from the code, not tested by running them. The origin of the network is documented in the commit, not reproduced by training it again.