Contents
Where this sits
The core takes the Python reference (through its traces) and the four arithmetic tracks, and feeds the simulation page.
Live parity check
This page fetches the reference’s seven trace files and replays every one through the core loaded on this page. Exact fields must match exactly; floats within \(10^{-12}\) relative; Sinkhorn plans within \(10^{-5}\), since both sides stop at a convergence threshold.
Every line of the report
| Trace | What is replayed | Match |
|---|---|---|
| prng.json | The published xoshiro128** vector, splitmix32 seeding, floats (bit for bit), bounded draws, a sample, rationals, an exact Cayley rotation | Exact |
| geometry.json | Ghost map, exp at a point, distance, Klein chart on 12 cases; exact ghost points and boosts | Exact and \(10^{-12}\) |
| step_b1.json | 24 steps of one channel on a 9-vertex 3-colouring: block, exact rotation, exact corners, costs, snapped states, Helix B; weights | Exact and \(10^{-12}\) |
| channels_b1.json | Three channels for two registers with the membrane: 60 zero-point memory words with their SHA-256 chain, both registers’ pump identity, all final exact states | Exact |
| maxsat.json | polystep’s own MAX-SAT generator, rebuilt through a port of Python’s Mersenne Twister, and the shared-generator variant | Exact |
| tree.json | 40 norm-5 tree words: quaternions, right-division, anchored decoding | Exact |
| sinkhorn.json | Four Sinkhorn plans from polystep’s solver | \(10^{-5}\) |
Yes. Each fault below was injected into a copy of the core, and the check failed exactly where it should:
- a Cayley map with its sign flipped failed 85 of 169 lines;
- a softmax temperature off by one part in \(10^7\) failed 25 (the snapped states move);
- one changed character in the memory hash’s format failed 60.
Rounding halves to even instead of up changed nothing. That is expected: the two rules differ only on an exact tie, and none occurs in these traces.
How two languages agree
Python and JavaScript both use IEEE doubles, but agreeing to the last bit, and exactly on every rational, takes a few rules. Both implementations follow them.
| Rule | Why |
|---|---|
Exact to float is correctly rounded. Q.toNumber divides with a 65-bit quotient and a sticky bit, then rounds once. | Python’s float(Fraction) rounds once. Dividing two rounded numbers, the obvious way, can be one unit off, and every float the search sees comes from an exact point. |
Float to exact is exact. Q.fromDouble reads the double’s bits. | The snap measures its residual against Fraction(float), which is exact. |
Halves round up, floor(x + 1/2): in the snap, the exact step length t and B1’s edge count. | Python’s round rounds halves to even; JavaScript’s Math.round rounds them up. The reference was changed to match (Amendment 1). |
| One generator, written twice: xoshiro128** with splitmix32 seeding, plus a port of Python’s Mersenne Twister for polystep’s own instance generator. | Neither torch’s nor the browser’s generator can be reproduced in the other language. |
| Sums run left to right, in the order the reference writes them. | Floating-point addition is not associative. |
| The traced path avoids transcendental functions. It uses rational step lengths and exact Cayley rotations. | exp, cosh and their kin may differ in the last bit between maths libraries. Where they remain (the weights), a tolerance is stated. |
Run three channels
The core running on its own in this browser: three channels on the zero-point clock attack a planted 3-colouring (B1), with Helix B certifying at every zero point. The settings are untuned, so this shows that the core runs; it is not a benchmark.
What is in the core
One file with no dependencies. It loads as window.HPCore in a page and through require in Node. Each part mirrors a Python module.
| Part | Mirrors | Track | Names |
|---|---|---|---|
| Exact rationals | fractions.Fraction | B | Q: add sub mul div cmp toNumber fromDouble parse |
| Generators | prng.py, Python’s random | — | Rng (xoshiro128**), PyRandom (Mersenne Twister) |
| Exact geometry | exact.py | B, C | X.cayley ghost boost pointAt snap displacement pump pumpHolds |
| Float geometry | hyperbolic.py | A | H.lorentz dist expAt logOrigin lorentzCentroid toKlein |
| The step | step.py | A, B, C | PolyStepH, stepConfig, softmaxWeights, sinkhornWeights |
| Problems | problems.py | A, B | ThreeColouring MaxSat TreeAddress HardThresholdNet |
| Channels | channels.py | A, B | TriChannel, ON, OFF, scheduleFacts, memoryWord (SHA-256) |
Using it
<script src="hyperbolic_core.js"></script> // or in Node: const HPCore = require('./hyperbolic_core.js') const { Rng, ThreeColouring, PolyStepH, TriChannel, objective } = HPCore; const prob = new ThreeColouring(60, 4.4, new Rng(1000)); const solvers = [], states = []; for (let j = 0; j < 3; j++) { const s = new PolyStepH(objective(prob), prob.m, 2, { epsilon: 0.5, stepRadius: 0.6, block: 8 }, new Rng(10 + j)); states.push(s.initState(prob.initPoints(new Rng(20 + j), 4))); solvers.push(s); } const tc = new TriChannel(solvers, states, prob, { clutch: 'membrane', mu: 0.3 }).run({ budget: 24000 }); console.log(tc.memory.at(-1)); // { F, costs, Y, best, hash }
# the same parity check, in Node, from the Hyperbolic PolyStep folder node tests/parity.cjs # prints each file's count and the total; -v prints every line
Limits
- No Haar rotations. polystep’s Haar rotations come from torch’s generator and LAPACK, which the browser cannot reproduce. The core uses Cayley rotations from the shared generator, float or exact. The reference offers both.
- Sinkhorn agrees only approximately (within \(10^{-5}\)). Both sides iterate to a threshold, and the softmax solver is the one traced exactly.
- B4’s data are not traced. They come from Box–Muller, whose logarithms and cosines may differ in the last bit. The browser’s B4 runs are its own, and benchmark decisions come from the reference.
- It is not fast. Exact arithmetic is BigInt and the search is plain JavaScript, so it suits the sizes the simulation page shows (tens to hundreds of variables), not the reference’s largest cells.