On this page

The idea

A search that looks a few moves ahead stops at thousands of boards it cannot see past, and it needs a number for every one of them: how good is this position? The function that supplies that number is the leaf evaluator, and it is called more often than anything else in the program. The reference search on this site uses a hand-written sum of terms. A neural network could recognise patterns a sum of terms cannot, but an ordinary network reads the whole board through large matrix multiplications every call, and a leaf has about a microsecond to spend.

An NNUE ("efficiently updatable neural network", a design from computer shogi and chess) is built around two facts about boards. First, a board is sparse: describe it as a long list of yes/no questions and only a few dozen answers are yes. Second, one move changes only a few cells, so the answers barely change from one board to the next. A network whose first layer is a lookup table turns both facts into speed.

A small example

Take a board of nine lights in a three-by-three grid, each on or off, and ask for one number that says how good the arrangement is.

  1. Describe the board as nine yes/no features, one per light. Only the lit ones matter.
  2. Make the first layer a table with one row per feature, each row a short vector of learned numbers. The board's hidden vector is the sum of the rows for the lights that are on. Five lights on, five rows gathered and added. No multiplication happens.
  3. Pass that short vector through two small dense layers to get the output.
the boardthe table: one row per lightsumtwo small layers9 lights, 5 onrow 1row 2row 3row 4row 5row 6row 7row 8row 95 rows addedthe hidden vectortiny dense layershow goodone number
The toy board has nine lights and five are on. The first layer is a table with one row per light; the board's hidden vector is the sum of the five lit rows, gathered and added with no multiplication. Two small dense layers then turn that short vector into one number.
  1. Flip one light on. Exactly one row joins the sum, so add it to the running total instead of rebuilding the vector. Flip it off and subtract the row. That is the "efficiently updatable" part: the expensive layer is maintained by adding and subtracting rows as the board changes.
one light flipsthis light: on, off, onrow 1row 2row 3row 4row 5row 6row 7row 8row 9row 6+ one rowthe restis untouchedrow 6 joins or leaves the sumthe same update on a Drop7 board645one drop lands: 1 cell changed, 1 row added48 cells unchanged: nothing recomputedengine output: the rules page's drop scenario
On the left, the light in the second row and third column switches on and off; only row 6 of the table lights with it, and only one segment joins the sum. On the right, the same update on a Drop7 board: the engine's drop scenario from the rules page lands one disc, so one cell changes and one row is added, and the other 48 cells are not recomputed.
  1. Make the features richer than single lights: add one for each pair of adjacent lights, so a run of two is recognised as one thing. The table grows, but the number of features on for any board stays small.
  2. Count the cost: a few dozen row additions and two small matrix products. That is microseconds, which is what a function called hundreds of thousands of times per decision can afford.

How it works

The network has three parts. A feature set turns a board into the list of features currently on; most of the game knowledge goes into choosing that list. An accumulator holds the sum of the active rows of the first-layer table and is updated incrementally when a move changes the board. A small head of one or two dense layers turns the accumulator into a single number.

Training is ordinary gradient descent on targets. The targets can be a slower search's values or a measured outcome such as how long the game lasted, and the weights can later be adjusted by another method, as the evolution primer describes. What the architecture fixes is the budget. Almost every weight lives in the first table, which is read sparsely, so the cost of a call is set by the number of active features and the width of the head, and barely grows with the table.

Two things follow. A feature that never appeared in training has no useful row to gather, so the network generalises only through features that recur. And because the head is small, its opinion stays close to a weighted sum of feature rows: a step up from a hand-written sum of terms, and a long way from a deep convolutional network.

In Drop7

An NNUE here reads exactly what a player can see. The learned-leaf page describes the feature set: 8,902 binary features, of which exactly 135 are active for any board, including 84 adjacent-pair features, because a disc clears when its number matches the length of the run it lands in and a bag of single-cell features cannot see a run (A learned survival estimate inside the reference search). The output is in units of a row rise, 17,000 points, so the search can add it to points it has already counted.

615,090 boards scored per decisionfour-move fair search, five chance samples; 2,271,280 with seven1 µs10 µs100 µs1 ms10 mstime to score one board (log scale)NNUE-shaped student: 1.33 µs572,367 parameters, held-out correlation 0.8564convolutional network: 4,122 µs3,006,543 parameters, held-out correlation 0.8646One decision: 0.887 s with the reference leaf; 2,535 s with the convolutional network, a factor of 2,860.Numbers as recorded on the learned-leaf page; the scale is drawn from them.
Time per board on a logarithmic scale, as recorded on the learned-leaf page: the NNUE-shaped student at 1.33 microseconds and the convolutional network it replaced at 4,122 microseconds. Above the scale is the number of boards one four-move decision scores, measured over 30 real decisions.

The budget is why the shape was chosen. A four-move search scores 615,090 boards per decision with five chance samples, measured over 30 real decisions, and 2,271,280 with seven. The convolutional network first specified for that leaf costs 4,122 microseconds per board; the NNUE-shaped student that replaced it costs 1.33 (the same page).

Pages that use one:

What it cannot do

The first limit is arithmetic. The three-million-parameter convolutional network originally specified as the leaf would have taken 2,535 seconds per decision at 4,122 microseconds a board, a factor of 2,860 over the reference's 0.887-second decision (A learned survival estimate inside the reference search). The NNUE-shaped student gave up 0.008 of held-out lifetime correlation (0.8564 against the convolutional network's 0.8646) to run in 1.33 microseconds.

The second limit is fidelity of another kind. Fitting a teacher's numbers closely does not mean ranking moves the way the teacher would. The NNUE warm start on the evolution page matched a depth-5 teacher's values to a validation Pearson correlation of 0.95 and then, placed inside the real depth-3 search, picked the teacher's column only 44% of the time, about what a leaf that returns zero manages (An evolved NNUE leaf, distilled from a depth-5 teacher). A leaf is judged by the order it puts sibling moves in, and a small error in value can swap two of them; the concept page on the sibling trap is about that problem.