You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Build the Python/NumPy port of the biosim4 simulator, golden-tested against David Miller's C++ — vectorised over the population. This is SPEC §7's M1, with one correction: SPEC's M1 gate is wrong. It reads "golden-output test: matches TS port step-for-step under fixed seed", but the conformance audit (#26) proved the TS port diverges from Miller in 23 verified ways across all 14 modules. Golden-testing against it would faithfully reproduce a broken reference. Miller's C++ is the ground truth. (ISSUES.md §6 half-anticipated this: "the golden test may need to reproduce upstream, not the port.")
Context — why port instead of patch
The full-sweep audit (#26, data/audit/cpp_conformance_audit.md) found 23 distinct new defects: 4 CRITICAL, 10 MAJOR, 8 MINOR. Zero of 14 modules clean. Its recommendation is PORT, argued from three facts:
Patching's usual benefit is void. D3/D4/D10/D11/D12/D21 each change RNG draw counts or drawn values → every published baseline must be re-run under any option. The choice is not "cheap patch vs expensive rewrite"; it is "re-run on a substrate with 23 known defects and an unmeasured defect floor, vs re-run on one with dtypes and golden tests."
The dominant defect class is eliminated by a port, and only by a port.7 of 23 defects share one root cause: JavaScript has no integer types. The port transliterated C++ arithmetic without modelling C++ types. A typed port makes D3, D4, D6, D9, D12, D15, D20 unrepresentable, not merely fixed. Patching leaves the generator running — bug #4 M0 predictability harness → GATE: FAIL #4's "fix" changed which bitrandomBitFlip flips but left the int16_t store semantics that give bit 15 its meaning; D3 is its unfixed half.
The defect floor is NOT established. The single worst defect (D0 — 100% of GENETIC_SIM_FWD similarity scores wrong, live in both challenges, zero tests) survived a 14-module sweep with 28 adversarial skeptics, and was found only by asking "which files did nobody open?"SignalBeacon.ts, BeaconTrace.ts and HandGenome.ts (627 LOC, including the entire SIGNAL_BEACON challenge) still have zero coverage.
SPEC §4.2 independently requires this port: the TS sim's throughput "cannot support the experiment," and "the brain has to be a torch model anyway, so the JS/Python boundary is a problem regardless." Issue #25 is measuring the actual numbers.
Not STOP: the audit's own framing — "the science is sound; the substrate is not."
The six non-negotiables (from the audit, §5)
Port Miller's unit tests as golden tests FIRST. They are already written and sitting in the vendored tree — 389 LOC of free oracles, and the audit's own skeptics used them to find defects:
unitTestGridVisitNeighborhood.cpp (35 LOC) — pins D12 in 7 cases
unitTestBasicTypes.cpp (312 LOC) — pins D17
unitTestConnectNeuralNetWiringFromGenome.cpp (42 LOC) — pins the decode/wiring
Golden-test jaro_winkler_distance against the C++ on generated genome pairs. D0 is proof that an untested comparison function silently returns garbage forever. Reference artifact: data/audit/jw_verify.js (20,000/20,000 pairs diverge).
Adopt Miller's 1-based indexing with a reserved slot 0. Verified in the C++: peeps.cpp:21 — // Index 0 is reserved, so add one: — and simulator.cpp:149 — // multithreaded loop: index 0 is reserved, start at 1. The port's 0-based choice is the root of three findings (D10, D11, D21 — including agent 0 is never stepped yet still breeds).
Decide D2 explicitly and document it. Miller's neuron-array construction is itself buggy: he indexes by original key with std::map::operator[] default-insertion, pinning a subset of hidden neurons at a constant 0.5. The TS "fix" for known-bug #2 Seeded PRNG throughout #2overshot — Miller does not drive them all; 57.5% of default brains differ in driven flags. Bug-for-bug fidelity means deliberately re-introducing frozen neurons. This directly touches the M0 question (how much usable recurrent state an agent has). Either choice is defensible; silence is not.
State the determinism invariant once, globally. Miller-stream identity is unreachable (seeded mulberry32 vs his thread-private Jenkins) — say so explicitly, and audit self-determinism per seed instead. Then cost appendEpochLog's ~1100 undrawn RNG calls/generation (called from insidespawnNewGeneration.cpp:177, before initializeNewGeneration) against that invariant. The audit found every auditor asked only "does my function draw RNG?" — locally correct, globally useless.
Re-run everything. Not optional under any option.
Typed-dtype requirements (the structural fix)
These are what make the dominant defect class unrepresentable:
np.int16 connection weights — restores bit 15 as the sign bit (D3: sign currently frozen for life; |w| reaches 8.0 vs Miller's 4.0)
makeRandomWeight over the full 0xffff range, not 0xefff (D4: birth weights currently 53.3% negative and capped at +3.4998)
// integer division everywhere the C++ uses int division or (int) casts (D6, D12, D15, D20) — including visitNeighborhood's two (int) truncations and Coord.length()'s (int)sqrt
Scope
Port, vectorised over the population where it pays: basicTypes, Grid (+ createBarrier, visitNeighborhood), Signals, Peeps, Genome (+ decode, mutation, reproduction), GenomeCompare, Individual (+ feedForward, getSensor), Sensor, ExecuteActions, SurvivalCriteria, SpawnNewGeneration, params, and the sim loop (simulator / endOfSimStep / endOfGeneration).
P4 — loop:Peeps, SpawnNewGeneration, the step/generation loop, throughput.
Reference: the C++ is vendored (davidrmiller/biosim4 @ 45e808c); ~5,055 LOC C++ / ~3,595 LOC TS. Clone it locally rather than fetching per-file (a prior agent run died because WebFetch depends on a permission classifier that was unavailable).
Do NOT port the TS port's bugs. Read the C++ for each function; use the TS only to understand slsim's deliberate additions.
Preserve slsim's deliberate additions as first-class, and port them too: the seeded PRNG, env-var parameterisation, sensor-stream CSV logging, and the experiment levers (barrier dynamics, killEnable, the SIGNAL_BEACON challenge + beacon trace/scatter, genome injection). Note these are the 627 LOC the audit never reviewed — port them from their intent, and test them.
Keep the TS port in-tree as a historical reference; it is no longer the source of truth. Do not delete it.
Do NOT build the Brain interface / TransformerBrain / MLPBrain (M3/M4). This is the substrate only.
Do NOT change the SPEC §2 gate criterion.
Acceptance criteria
Miller's unitTestGridVisitNeighborhood.cpp, unitTestBasicTypes.cpp and unitTestConnectNeuralNetWiringFromGenome.cpp ported and passing against the Python port.
jaro_winkler_distance golden-tested against the C++ on generated genome pairs — 0% divergence (vs the TS port's 100%).
Each of the 23 audited defects is either structurally unrepresentable (state which dtype/design makes it so) or covered by a test pinning the C++ behaviour. A short table mapping D0–D22 → unrepresentable / tested / deliberately divergent (with rationale).
This is a large, multi-session port; land it in phases, each with its golden tests green. Read the C++ for every function you port — do not translate the TS. The whole reason this issue exists is that transliterating without modelling types produced an open-ended bug generator; the antidote is types first, oracles first, then code. When the C++ itself looks wrong (D2), stop and flag it rather than silently choosing.
Task
Build the Python/NumPy port of the biosim4 simulator, golden-tested against David Miller's C++ — vectorised over the population. This is SPEC §7's M1, with one correction: SPEC's M1 gate is wrong. It reads "golden-output test: matches TS port step-for-step under fixed seed", but the conformance audit (#26) proved the TS port diverges from Miller in 23 verified ways across all 14 modules. Golden-testing against it would faithfully reproduce a broken reference. Miller's C++ is the ground truth. (ISSUES.md §6 half-anticipated this: "the golden test may need to reproduce upstream, not the port.")
Context — why port instead of patch
The full-sweep audit (#26,
data/audit/cpp_conformance_audit.md) found 23 distinct new defects: 4 CRITICAL, 10 MAJOR, 8 MINOR. Zero of 14 modules clean. Its recommendation is PORT, argued from three facts:randomBitFlipflips but left theint16_tstore semantics that give bit 15 its meaning; D3 is its unfixed half.GENETIC_SIM_FWDsimilarity scores wrong, live in both challenges, zero tests) survived a 14-module sweep with 28 adversarial skeptics, and was found only by asking "which files did nobody open?"SignalBeacon.ts,BeaconTrace.tsandHandGenome.ts(627 LOC, including the entire SIGNAL_BEACON challenge) still have zero coverage.SPEC §4.2 independently requires this port: the TS sim's throughput "cannot support the experiment," and "the brain has to be a torch model anyway, so the JS/Python boundary is a problem regardless." Issue #25 is measuring the actual numbers.
Not STOP: the audit's own framing — "the science is sound; the substrate is not."
The six non-negotiables (from the audit, §5)
unitTestGridVisitNeighborhood.cpp(35 LOC) — pins D12 in 7 casesunitTestBasicTypes.cpp(312 LOC) — pins D17unitTestConnectNeuralNetWiringFromGenome.cpp(42 LOC) — pins the decode/wiringjaro_winkler_distanceagainst the C++ on generated genome pairs. D0 is proof that an untested comparison function silently returns garbage forever. Reference artifact:data/audit/jw_verify.js(20,000/20,000 pairs diverge).peeps.cpp:21—// Index 0 is reserved, so add one:— andsimulator.cpp:149—// multithreaded loop: index 0 is reserved, start at 1. The port's 0-based choice is the root of three findings (D10, D11, D21 — including agent 0 is never stepped yet still breeds).std::map::operator[]default-insertion, pinning a subset of hidden neurons at a constant 0.5. The TS "fix" for known-bug #2 Seeded PRNG throughout #2 overshot — Miller does not drive them all; 57.5% of default brains differ indrivenflags. Bug-for-bug fidelity means deliberately re-introducing frozen neurons. This directly touches the M0 question (how much usable recurrent state an agent has). Either choice is defensible; silence is not.appendEpochLog's ~1100 undrawn RNG calls/generation (called from insidespawnNewGeneration.cpp:177, beforeinitializeNewGeneration) against that invariant. The audit found every auditor asked only "does my function draw RNG?" — locally correct, globally useless.Typed-dtype requirements (the structural fix)
These are what make the dominant defect class unrepresentable:
np.int16connection weights — restores bit 15 as the sign bit (D3: sign currently frozen for life; |w| reaches 8.0 vs Miller's 4.0)makeRandomWeightover the full0xffffrange, not0xefff(D4: birth weights currently 53.3% negative and capped at +3.4998)sourceNum/sinkNumgene fields, 0..127 (D9)uint16grid cells (the0xffffbarrier sentinel)//integer division everywhere the C++ uses int division or(int)casts (D6, D12, D15, D20) — includingvisitNeighborhood's two(int)truncations andCoord.length()'s(int)sqrtScope
Port, vectorised over the population where it pays:
basicTypes,Grid(+createBarrier,visitNeighborhood),Signals,Peeps,Genome(+ decode, mutation, reproduction),GenomeCompare,Individual(+feedForward,getSensor),Sensor,ExecuteActions,SurvivalCriteria,SpawnNewGeneration,params, and the sim loop (simulator/endOfSimStep/endOfGeneration).Suggested phasing (each phase independently landable):
basicTypes,Grid,Signals+ port Miller's 3 unit tests. Highest oracle density; pins D12/D17 immediately.GenomeCompare+ the jaro-winkler golden test. Most CRITICALs live here (D0, D3, D4).Individual,feedForward, sensors, actions, survival.Peeps,SpawnNewGeneration, the step/generation loop, throughput.Reference: the C++ is vendored (
davidrmiller/biosim4 @ 45e808c); ~5,055 LOC C++ / ~3,595 LOC TS. Clone it locally rather than fetching per-file (a prior agent run died becauseWebFetchdepends on a permission classifier that was unavailable).Constraints
killEnable, the SIGNAL_BEACON challenge + beacon trace/scatter, genome injection). Note these are the 627 LOC the audit never reviewed — port them from their intent, and test them.Braininterface /TransformerBrain/MLPBrain(M3/M4). This is the substrate only.Acceptance criteria
unitTestGridVisitNeighborhood.cpp,unitTestBasicTypes.cppandunitTestConnectNeuralNetWiringFromGenome.cppported and passing against the Python port.jaro_winkler_distancegolden-tested against the C++ on generated genome pairs — 0% divergence (vs the TS port's 100%).RESULTS.mdupdated to state that every pre-port result is superseded and why.Out of scope
Braininterface,TransformerBrain,MLPBrain, ES training, lifetime learning (M3/M4/M5).Reasoning guidance
This is a large, multi-session port; land it in phases, each with its golden tests green. Read the C++ for every function you port — do not translate the TS. The whole reason this issue exists is that transliterating without modelling types produced an open-ended bug generator; the antidote is types first, oracles first, then code. When the C++ itself looks wrong (D2), stop and flag it rather than silently choosing.
Cross-references
data/audit/cpp_conformance_audit.mdLabels
enhancement