§ 03

§ 03 · Matrix math

Integers and quantization

Small whole numbers in, exact 32-bit sums out

Pearl’s mining multiplies matrices of small whole numbers, between −64 and 64. Not fractions, not big numbers.

That choice does three jobs at once. Whole numbers make the arithmetic exact, so everyone agrees on every bit. Small ones fit in a single byte, even after noise is added, so GPUs can use their fastest circuits. And their sums fit comfortably in 32 bits, so nothing ever overflows.

Why whole numbers and not fractions?

Because a mining ticket must come out identical on every computer that checks it.

Computers store fractions as floating-point numbers, which round after every operation. Add the same numbers in a different order and the last digits can differ. A GPU, a CPU and a proof circuit would sometimes disagree, and the ticket, a hash, would change completely.

Integer arithmetic has no rounding. 3 × (−4) + 7 is −5 on every machine, in any order. So Pearl’s rules define the work in integers: small inputs that fit in a byte, 32-bit running sums. The format is named in every certificate: Int7 × Int7 → Int32.

What are the ranges, exactly?

−128127int8 byte−6464A, B entries−6363noise E−127127A′ = A + E−128−64064127
The ranges on one number line. Entries from −64 to 64 plus noise from −63 to 63 can reach ±127, which still fits a signed byte (−128 to 127).
Numbers Range Stored as
Entries of A and B [−64, 64], 129 values one signed byte each
Noise factors (the “thin” part) [−32, 31] 6-bit random numbers
Noise added to A and B [−63, 63] one signed byte
Noisy entries A′ = A + E, B′ = B + F [−127, 127] one signed byte (int8)
Running sums of products far inside ±2³¹ 32-bit signed integer (int32)

The first row is enforced twice. The plain check a pool runs on a winning share rejects any revealed entry outside [−64, 64], and the zero-knowledge circuit that every node verifies has the same range check built in. The noise ranges come from how the noise is built, on the rank and noise page.

Why can’t the sums overflow?

A 32-bit signed integer holds values up to 2,147,483,647. Check the worst case:

|Ail′⋅Blj′|≤127×127=16,129|A'_{il} \cdot B'_{lj}| \le 127 \times 127 = 16{,}129
|∑l=0k−1Ail′Blj′|≤16,129×65,536=1,057,030,144<231−1\Big|\sum_{l=0}^{k-1} A'_{il} B'_{lj}\Big| \;\le\; 16{,}129 \times 65{,}536 \;=\; 1{,}057{,}030{,}144 \;<\; 2^{31} - 1
k is at most 2¹⁶ = 65,536 by consensus rule, so even the most extreme sum uses less than half of int32's range.

So the GPU can accumulate a whole dot product in 32-bit registers without ever checking for overflow. This is one reason k has an upper limit at all.

Make a guess

If entries could go all the way to ±127 before noise, what range would the noisy entries need?

Think of your answer first, then check it.

Answer ±190, which no longer fits in a byte.

127 + 63 = 190, beyond int8’s 127. The noisy matrices would need a wider format, and the GPU would lose its fast 8-bit integer mode. Capping entries at 64 keeps the noisy matrices in one byte.

What is quantization?

AI models are trained with fractional numbers, usually 16-bit floating point. Quantization squeezes them into a few integer levels, with a scale factor to convert back.

The common symmetric method: find the largest magnitude in a row, divide it into 63 steps, and round each number to the nearest step.

Pearl’s official miner does this inside vLLM. In mining mode, each token’s activations are quantized to 7 bits, [−63, 63], with one scale per token. The weights were quantized ahead of time, with one scale per output channel. After the integer multiplication and denoising, the result is multiplied by both scales and written back as 16-bit floating point, the layer’s normal output.

Pearl Research Labs describe this setup as W7A7 (7-bit weights and activations), prepared with the GPTQ and SmoothQuant methods. Their whitepaper reports Llama-3.3-70B accuracy scores (MMLU) of 0.8198 for the original 16-bit model and 0.8190 to 0.8198 for Pearl-certified runs: little or no loss on that test.

How does the ticket read a negative sum?

As raw bits. Every 128 steps the miner XORs a tile’s running sums together into one 32-bit word, and XOR works on bits, not on values. A negative sum is read in two’s complement.

In Pearl’s toy example, a tile’s first four running sums are 6, 1, −66 and 10:

Sum 32-bit pattern (hex)
6 00000006
1 00000001
−66 ffffffbe
10 0000000a
XOR of all four ffffffb3

That word is what goes into the ticket’s diary. See tickets and target.

Code: the range check and the casts

The plain check of a share, which a pool or gateway runs, rejects any revealed entry outside the range in verify.rs (zk-pow/src/api/verify.rs:113-118 at v1.5.2, on GitHub) (the zero-knowledge circuit that nodes verify enforces the same range):

zk-pow/src/api/verify.rs Rust
// Verify all strip values are in [-64, 64] (matching the ZK circuit's IRANGE7P1 range check)
for strip in private_params.s_a.iter().chain(private_params.s_b.iter()) {
    for &val in strip {
        ensure!((-64..=64).contains(&val), "Matrix value {} out of range [-64, 64]", val);
    }
}

In the tile loop, entries are widened to i32 before multiplying, and the sums are reinterpreted as u32 for the XOR (x as u32), exactly the two’s-complement reading above.

The codex

Suggested

Up and down arrows move through the results, Enter opens one, Escape closes. Anywhere on the site, slash or Command K opens this search, and the square brackets [ and ] go to the previous and next page.