Evolutionary Substrate¶
Open-ended evolution of stochastic-computing neural networks: self-replicating
organisms whose genomes encode topology, neuron kinetics, and plasticity
parameters, mutate and recombine under safety invariants, speciate by
genomic distance, migrate between islands, and deploy onto FPGA tiles. The
fitness function accepts either a pure-software proxy or the closed-loop
wet-lab MEA hook exposed by
:func:sc_neurocore.bioware.bioware.mea_fitness_hook.
from sc_neurocore.evo_substrate.evo_substrate import (
Genome, NeuronGene, TopologyGene, PlasticityGene,
MutationEngine, CrossoverEngine, FitnessEvaluator,
ReplicationEngine, OrganismEmitter, SafetyBounds,
TileDeploymentTracker, HallOfFame, IslandModel,
NoveltyArchive, FormalSafetyGuard, ParetoFront,
CPPNGenome, ComplexityTracker, BloatPenalizer,
ExtinctionDetector, LineageTracker, AgeRegulator,
TournamentSelector, EvoStatisticsTracker,
HWFitnessCollector, CoevolutionArena,
assign_species, population_diversity, genomic_distance,
dominates, shared_fitness, compute_bloat, genome_complexity,
genome_diff,
)
1. Mathematical formalism¶
1.1 Genome as a fixed-length vector¶
A :class:Genome serialises to a vector
$\mathbf{g} \in \mathbb{R}^{19}$ via
$$ \mathbf{g} = \bigl[\;\mathbf{t}\;|\;\mathbf{n}\;|\;\mathbf{p}\;\bigr], $$
where $\mathbf{t} \in \mathbb{R}^{5}$ is the
:class:TopologyGene block
$(N_{\text{neurons}},\, N_{\text{layers}},\, c,\, r_{\text{rec}},\, L_{\text{bits}})$,
$\mathbf{n} \in \mathbb{R}^{8}$ is the
:class:NeuronGene block
$(\tau_{\text{fast}},\, \tau_{\text{work}},\, \tau_{\text{deep}},\,
\theta,\, \gamma,\, \delta_{\text{conf}},\, \kappa,\, w_{\text{inh}})$,
and $\mathbf{p} \in \mathbb{R}^{6}$ is the
:class:PlasticityGene block
$(\eta_{\text{STDP}},\, \tau_{+},\, \tau_{-},\, U_{\text{STP}},\,
\eta_{\text{hom}},\, s_{\text{meta}})$.
The :meth:Genome.compute_id fingerprint is the first 12 hex digits of
SHA-256 over the raw bytes of $\mathbf{g}$, giving a collision-safe
content-addressable id. Round-trip is exact via
:meth:Genome.from_vector.
1.2 Point mutation (Gaussian, multiplicative)¶
For each coordinate $i$, with probability $p_{\text{point}}$ (default 0.2),
$$ g_i \leftarrow g_i + \mathcal{N}(0,\, \sigma_{\text{point}}^{2}) \cdot \bigl(|g_i| + \varepsilon\bigr), $$
where $\sigma_{\text{point}} = 0.05$ and $\varepsilon = 10^{-8}$. The multiplicative coupling keeps the relative step size constant across parameters with very different magnitudes (e.g. $\tau_{\text{deep}}=10^{4}$ vs $\gamma=0.2$).
1.3 Structural / duplication / swap mutations¶
- Structural. $N_{\text{neurons}} \leftarrow N_{\text{neurons}} + \delta$ with $\delta \in {-2, -1, 1, 2}$, clamped to $[N_{\min},\,N_{\max}] = [4,\,1024]$; connectivity $c$ receives a small Gaussian kick and is clamped to $[0.01,\,1]$.
- Duplication. Layer count increases by 1 (capped at 10), neuron count scaled by $1.5$ (capped at $N_{\max}$). This models whole-gene duplication — the dominant driver of complexity growth in biological evolution.
- Swap. $\tau_{\text{fast}}$ and $\tau_{\text{work}}$ are swapped, a simple inversion-like operator that probes time-scale re-assignment without changing the vector's L2 norm.
Mutation type is drawn via cumulative-probability selection using the
rates in :class:MutationConfig (structural 0.05, duplication 0.01,
swap 0.02, else point).
1.4 Uniform crossover¶
Two parents $\mathbf{a},\mathbf{b} \in \mathbb{R}^{19}$ produce a child $\mathbf{c}$ by coordinate-wise Bernoulli selection:
$$ c_i = \begin{cases} a_i & \text{if } u_i < 0.5 \ b_i & \text{otherwise} \end{cases}, \quad u_i \sim \mathcal{U}(0,1). $$
This is the standard Syswerda uniform operator (Syswerda, 1989); gene-block boundaries (topology | neuron | plasticity) are respected because each block occupies a contiguous slice of the vector.
1.5 Genomic distance (Adam-like normalised L1)¶
$$ d(\mathbf{a},\mathbf{b}) = \frac{1}{D} \sum_{i=1}^{D} \frac{|a_i - b_i|}{|a_i| + |b_i| + \varepsilon}, \qquad D = 19. $$
This normalised metric is scale-invariant, which is crucial because $\tau_{\text{deep}}$ and $\gamma$ differ by five orders of magnitude. $d=0$ means clones; $d$ approaches 1 for maximally different genomes.
1.6 NEAT-style speciation¶
:func:assign_species partitions the population greedily:
$$ \text{species}(o) = \begin{cases} k, & \min_k d\bigl(\mathbf{g}o,\, \mathbf{g} \ k_{\text{new}}, & \text{otherwise} \end{cases} $$}\bigr) < \theta_{\text{sp}
where $r_k$ is the representative genome of species $k$ and $\theta_{\text{sp}}$ is the speciation threshold (default 0.3). The first organism placed in a species becomes its representative — this matches Stanley & Miikkulainen's NEAT algorithm (Stanley, 2002).
1.7 Composite fitness¶
:meth:FitnessResult.compute_composite combines three terms:
$$ F = w_{\text{acc}} \cdot A \;+\; w_{\text{en}} \cdot E \;+\; w_{\text{lat}} \cdot L, $$
with default weights $(0.5,\,0.3,\,0.2)$, where $A$ is the metrics-fn accuracy score and $E$, $L$ are hardware-cost proxies derived from the topology gene:
$$ E = \max!\left(0,\; 1 - 0.5 \cdot \tfrac{N_{\text{neurons}}}{1024} - 0.5 \cdot \tfrac{L_{\text{bits}}}{1024}\right), \qquad L = \max!\left(0,\; 1 - \tfrac{N_{\text{layers}}}{10}\right). $$
1.8 Pareto dominance for multi-objective selection¶
One fitness result dominates another iff it is at least as good on every objective and strictly better on at least one:
$$ \mathbf{f}a \succ \mathbf{f}_b \;\Leftrightarrow\; \bigl(\forall i:\, f\bigr) \wedge \bigl(\exists j:\, f_{a,j} > f_{b,j}\bigr). $$} \geq f_{b,i
:class:ParetoFront maintains the set of non-dominated organisms across
generations, exposed through :func:dominates.
1.9 Bloat-aware fitness penalty¶
Complexity combines the Shannon entropy of the normalised absolute genome coordinates with a topology term:
$$ C(g) = H!\left(\frac{|\mathbf{g}|}{\lVert\mathbf{g}\rVert_1}\right) + \log_2!\left(1 + N_{\text{neurons}}N_{\text{layers}}c\right). $$
:class:BloatPenalizer subtracts a parsimony term from the composite score:
$$ F_{\text{penalised}} = F - \lambda \cdot \max!\left(0,\; \mathrm{complexity}(g) - \mathrm{complexity}_{\text{baseline}}\right), $$
defaulting to $\lambda = 0.01$.
1.10 Fitness sharing (niche preservation)¶
:func:shared_fitness divides an organism's raw fitness by a niche count,
yielding:
$$ F_{\text{shared}}(o_i) = \frac{F(o_i)} {\sum_j \max!\bigl(0,\;1 - d(g_i, g_j)/\sigma_{\text{share}}\bigr)}. $$
This is the Goldberg & Richardson (1987) fitness-sharing operator; it prevents a single dominant lineage from erasing weaker but diverse niches.
1.11 CPPN developmental encoding¶
Instead of storing weights directly, :class:CPPNGenome stores a small
network of CPPN nodes with activations drawn from
${\sin,\, \tanh,\, \text{Gaussian},\, \text{sigmoid}}$. The connection
weight between post-synaptic neuron at coordinate $\mathbf{x}$ and
pre-synaptic neuron at coordinate $\mathbf{y}$ is obtained by a forward
pass $w = \mathrm{CPPN}(\mathbf{x}, \mathbf{y})$. This matches Stanley's
HyperNEAT formulation (Stanley et al., 2009) and exploits spatial
symmetries — a mutation in one CPPN edge reshapes the entire weight
matrix coherently.
2. Theory (why this particular design)¶
2.1 Genotype–phenotype map is lossy but hardware-closed¶
The 19-D genome does not encode specific weights — those are
deterministic from weight_seed — nor specific spike trains. Instead,
the genome encodes control points of the phenotype (time constants,
connection probability, plasticity rates) that stay inside the envelope
the hardware FPGA tile can realise. This is deliberate: the evolutionary
search operates in a space where every point is constructible on the
target substrate, so crossing a fitness gradient cannot yield an organism
that fails to instantiate.
2.2 Why 19 coordinates, not 100s¶
Open-ended evolution typically gets more powerful with higher-dimensional
genomes, but each added dimension multiplies the search volume. SC-NeuroCore
fixes a small, physically motivated 19-D genome and lets
:class:CPPNGenome provide the escape hatch for high-dimensional weight
searches when needed. This follows the same principle as PicBreeder
(Secretan, 2011) — small active genome, large effective phenotype via
developmental indirection.
2.3 Formal safety as a hard filter¶
Every proposed genome passes through
:class:FormalSafetyGuard before it enters the population. The guard
checks three invariants:
- Time-constant positivity. $\tau_{\text{fast}},\tau_{\text{work}},\tau_{\text{deep}} > 0$ and $\tau_{\text{fast}} < \tau_{\text{work}} < \tau_{\text{deep}}$.
- Connectivity bounds. $c \in [0.01,\,1]$ and $N_{\text{neurons}} \in [4,\,1024]$.
- Lyapunov-bounded plasticity. $\eta_{\text{STDP}} \cdot \max(\tau_{+}, \tau_{-}) < C_{\text{lyap}}$ where $C_{\text{lyap}}$ is a pre-computed bound that keeps the STDP update map contractive under worst-case rate inputs.
Invariant 3 is checked by
:meth:FormalSafetyGuard.check rather than proved on-the-fly; see
docs/api/formal.md §6 for the matching Lean 4 theorem (axiomatised,
with a Mathlib proof roadmap).
2.4 Extinction as a diversity reset¶
Real evolution periodically resets via mass-extinction events (Raup, 1991).
:class:ExtinctionDetector mirrors this: when the best fitness has not
improved for stagnation_gens=10 generations, a fraction
(kill_fraction=0.9) of the population is culled and reseeded from the
:class:HallOfFame. This is not just ergodicity theatre — it breaks local
maxima that incremental mutation cannot escape.
2.5 Island model with periodic migration¶
:class:IslandModel runs N independent sub-populations with migration
every $M$ generations. Each island has its own RNG seed and mutation
pressure; diverse islands explore different basins. Migration copies the
top-$k$ organisms between adjacent islands, propagating discoveries
without erasing sub-population identity. This is the textbook Whitley
distributed GA (Whitley, 1999) adapted to hardware-aware selection.
3. Position in the pipeline¶
+------------------+ +-------------------+ +------------------+
| ArcaneZenith | | evo_substrate | | bioware |
| cognitive core |<---| (this module) |--->| MEA closed-loop |
+------------------+ +-------------------+ +------------------+
^ | ^
| | |
seeds | | | deploys
| v |
+----------+ +------------+
| hdl_gen | | FPGA tile |
| verilog | | allocation |
+----------+ +------------+
- Upstream inputs.
ArcaneZenith.step_from_genomeseeds its time-constants from :class:NeuronGene;sc_scopeandsc_doctor(seedebug.md) observe phenotype behaviour. - Outputs. :class:
OrganismEmitterconverts winning genomes to NIR graph or Verilog forhdl_gen/verilog_generator.py; resource budget checks go through :class:SafetyBoundsand :class:TileDeploymentTracker. - Closed loop. Fitness metrics come from either a software proxy
or :func:
bioware.mea_fitness_hook; the loop does not leave the substrate.
4. Features¶
- Content-addressable genomes (SHA-256 ids, 12 hex chars).
- 5 mutation operators (point, structural, duplication, swap, identity).
- Uniform crossover with gene-block alignment.
- Normalised L1 genomic distance (scale-invariant).
- NEAT-style greedy speciation, fitness sharing, novelty archive.
- Tournament + elitist + age-regulated selection
(:class:
TournamentSelector+ :class:AgeRegulator). - Industrial-mode :class:
ReplicationEnginewith 9 co-operating guards. - Multi-objective Pareto front (:class:
ParetoFront, :func:dominates). - Bloat penalty, complexity tracking, extinction detector, hall of fame.
- CPPN developmental encoding (:class:
CPPNGenome) for high-dim weight searches. - Island model (:class:
IslandModel) with migration. - Hardware-side gating (:class:
SafetyBounds, :class:ResourceBudget, :class:TileDeploymentTracker). - NIR + Verilog emission (:class:
OrganismEmitter). - Co-evolution arena (:class:
CoevolutionArena) for predator/prey or critic/actor dynamics. - Full lineage graph (:class:
LineageTracker) — every child records its parent and mutation type, giving a reconstructable phylogeny.
5. Usage — end-to-end generation¶
from sc_neurocore.evo_substrate.evo_substrate import (
Genome, ReplicationEngine, MutationEngine, MutationConfig,
)
def metrics_fn(genome):
# Plug your closed-loop MEA hook, or a software proxy:
return {"accuracy": 0.5 + 0.01 * genome.topology.num_neurons / 32}
cfg = MutationConfig(
point_rate=0.2,
point_sigma=0.05,
structural_rate=0.05,
duplication_rate=0.01,
swap_rate=0.02,
)
engine = ReplicationEngine(
mutation_engine=MutationEngine(cfg, rng_seed=7),
max_population=32,
elitism=1,
industrial_mode=True,
)
for i in range(16):
g = Genome()
g.compute_id()
engine.seed(g)
engine.evaluate_all(metrics_fn)
for gen in range(20):
stats = engine.evolve_generation(metrics_fn)
print(
f"gen {stats['generation']:>3} "
f"pop={stats['population_size']:>2} "
f"best={stats['best_fitness']:.3f} "
f"diversity={stats['diversity']:.3f}"
)
Sample output from a real run (industrial_mode=True, 16-organism
seed, 20 generations, rng_seed=7):
gen 1 pop=16 best=0.042 diversity=0.006
gen 5 pop=16 best=0.044 diversity=0.008
gen 10 pop=16 best=0.044 diversity=0.008
gen 20 pop=16 best=0.044 diversity=0.007
Low best_fitness values reflect the demonstration metrics_fn above (returns
0.5 + 0.01 · num_neurons/32, penalised by hardware-cost and
bloat terms); replace with a real evaluator to see non-trivial
selection pressure. Population stays at 16 in this run because
tournament selection + safety-guard rejection keep new organisms
below the max_population=32 cap when the selected parents are
similar.
6. API reference¶
6.1 Gene blocks¶
| Class | Fields (with defaults) |
|---|---|
:class:TopologyGene |
num_neurons=16, num_layers=2, connectivity=0.3, recurrent_fraction=0.1, bitstream_length=256 |
:class:NeuronGene |
tau_fast=5, tau_work=200, tau_deep=10000, theta=1, gamma=0.2, delta_conf=0.3, kappa=5, w_inh=0.3 |
:class:PlasticityGene |
stdp_lr=0.01, stdp_tau_plus=20, stdp_tau_minus=20, stp_u_base=0.5, homeostatic_rate=0.001, meta_sensitivity=1 |
6.2 Mutation + crossover¶
| Symbol | Purpose |
|---|---|
:class:MutationType |
enum: POINT, STRUCTURAL, DUPLICATION, SWAP, IDENTITY |
:class:MutationConfig |
per-type rates, Gaussian σ, structural intensity bounds |
:class:MutationEngine |
deterministic under rng_seed; mutate(g) returns (child, op) |
:class:CrossoverEngine |
uniform crossover, gene-block-aligned |
:func:genomic_distance |
scale-invariant L1 |
:func:assign_species |
NEAT-style speciation |
:func:population_diversity |
mean pairwise distance |
6.3 Fitness + selection¶
| Symbol | Purpose |
|---|---|
:class:FitnessType |
ACCURACY, ENERGY, LATENCY, COMPOSITE |
:class:FitnessResult |
(accuracy, energy_score, latency_score, composite) |
:class:FitnessEvaluator |
scorer over population; accepts metrics_fn |
:class:TournamentSelector |
$k$-way tournament with optional elitism |
:class:AgeRegulator |
ages out organisms past max_age |
:class:ParetoFront |
non-dominated front |
:func:dominates |
Pareto relation $\succ$ |
:func:shared_fitness |
Goldberg–Richardson niching |
TournamentSelector.select() fails closed on empty populations before RNG
sampling, and select_n() propagates the same validation boundary for batched
selection.
6.4 Population control¶
| Symbol | Purpose |
|---|---|
:class:IslandModel |
N sub-populations + periodic migration |
:class:NoveltyArchive |
sparse archive of behaviourally distinct genomes |
:class:HallOfFame |
top-K elites across generations |
:class:BloatPenalizer |
parsimony penalty on composite fitness |
:class:ComplexityTracker |
structural complexity over time |
:class:ExtinctionDetector |
mass-extinction trigger on stagnation |
:class:CoevolutionArena |
predator/prey or critic/actor co-evolution |
:class:EvoStatisticsTracker |
per-generation :class:GenerationStats log |
6.5 Safety + hardware¶
| Symbol | Purpose |
|---|---|
:class:FormalSafetyGuard |
genome-side invariants (tau positivity, c bounds, Lyapunov plast.) |
:class:SafetyBounds |
hardware-side limits (V, I, routing length) |
:class:ResourceBudget |
tracks (power_mw, area_um2, latency_ns) |
:class:TileAllocation |
which FPGA tile a genome occupies |
:class:TileDeploymentTracker |
live map of tile occupancy; handles replication + extinction |
:class:HWFitnessReport |
post-silicon metrics feedback |
:class:HWFitnessCollector |
aggregates :class:HWFitnessReport into a fitness proxy |
6.6 Indirect encoding (CPPN)¶
| Symbol | Purpose |
|---|---|
:class:ActivationFunc |
SINE, TANH, GAUSSIAN, SIGMOID |
:class:CPPNNode |
one activation node |
:class:CPPNEdge |
one weighted edge |
:class:CPPNGenome |
NEAT-like CPPN; expands to weight matrix via forward pass |
6.7 Lineage + diff¶
| Symbol | Purpose |
|---|---|
:class:LineageRecord |
(genome_id, parent_id, generation, mutation_type, fitness) |
:class:LineageTracker |
records all records; walk ancestry via get_ancestors(genome_id) |
:class:GenomeDiff |
neuron/layer/connectivity/time-constant deltas + changed-parameter count |
:func:genome_diff |
structural deltas and vector coordinates changed above $10^{-8}$ |
:func:genome_complexity |
coordinate entropy + $\log_2(1 + NLC)$ topology term |
6.8 Emission¶
| Symbol | Purpose |
|---|---|
:class:OrganismEmitter |
genome → NIR graph or Verilog |
:class:GenomeSerializer |
JSON / binary round-trip |
7. Verified diagnostic benchmarks¶
The committed schema-v2 artefact was captured on 2026-07-12 with CPython
3.12.3 and NumPy 2.2.6 on an Intel i5-11600K workstation. Each value below is
the median of 30 samples after two warmups. The process was pinned to one CPU,
but the host had no kernel-reserved isolated cores, used the powersave
governor, and had load averages near 22. These timings are local regression
context only, not publishable throughput claims.
| Operation | Median latency | Diagnostic rate |
|---|---|---|
MutationEngine.mutate |
217.10 µs | 4 606 ops/s |
CrossoverEngine.crossover |
111.70 µs | 8 953 ops/s |
genomic_distance (19-D) |
23.08 µs | 43 330 ops/s |
FormalSafetyGuard.check |
2.00 µs | 500 541 ops/s |
assign_species (n=64, $\theta=0.3$) |
1.81 ms | 552 ops/s |
ReplicationEngine.evolve_generation (pop=32, industrial) |
16.28 ms | 61 gen/s |
benchmarks/bench_evo_substrate.py records all raw samples, summary
statistics, source-tree SHA-256, runtime versions, affinity, governor,
frequency, and host-load evidence in
benchmarks/results/bench_evo_substrate.json. Rerun on reserved isolated
cores before using the numbers in a release or publication.
7.1 Determinism + reproducibility¶
All RNGs in the module are numpy.random.default_rng seeded through
explicit constructor arguments (MutationEngine(rng_seed=…),
CrossoverEngine(rng_seed=…), :class:ReplicationEngine's internal
self.mutator.rng). Two consequences:
- A given
(config, seeds, metrics_fn)triple is bit-reproducible: re-running the 20-generation demo above yields the same lineage tree, same :class:HallOfFameentries, and the same :class:ParetoFront. - Islands in :class:
IslandModeltake independent seeds derived from a master seed, so experiments can be re-run with different master seeds to bound Monte-Carlo noise on any reported figure.
The lineage tracker (:class:LineageTracker) also lets you replay any
subtree: given a surviving genome_id, :meth:get_ancestors returns
the exact mutation chain from seed to present, which is what
:class:OrganismEmitter serialises alongside the Verilog blob for
audit trails on the FPGA tile side.
7.2 Multi-language kernel comparison¶
The four compute hot paths are also mirrored in Rust, Julia, Go, and
Mojo for honest cross-language measurement. Python orchestration
(ReplicationEngine, lineage, hall-of-fame, island model, safety
guards — 40+ classes) stays authoritative in Python; only
genomic_distance, crossover_uniform, point_mutation, and
population_diversity are mirrored elsewhere.
The source-bound schema-v2 comparison was captured on 2026-07-12 via
benchmarks/bench_evo_substrate_multilang.py. It interleaves 30 samples per
backend after two warmups; each sample executes 100 000 calls over 19-D
Float64 vectors. All five required backends completed. The same non-exclusive,
loaded-host caveat applies, so the medians below are diagnostic only.
| Kernel (ns/call, dim=19) | Rust | Julia | Go | Mojo | Python |
|---|---|---|---|---|---|
genomic_distance |
692.8 | 36.0 | 80.0 | 34.8 | 21 347.4 |
crossover_uniform |
1 508.1 | 131.3 | 161.7 | 323.8 | 3 705.1 |
point_mutation |
1 205.3 | 707.2 | 176.8 | 328.3 | 12 373.0 |
How to read this. Rust includes the PyO3 boundary. Julia, Go, and Mojo are
standalone kernel processes and are parity references rather than Python-callable
inner-loop backends. The raw samples, toolchain context, source digest, and
empty unavailable set are committed in
benchmarks/results/bench_evo_substrate_multilang.json.
From the Python orchestration's perspective, Rust is the only directly accessible compiled kernel because Julia, Go, and Mojo require subprocess dispatch. In this loaded diagnostic run, the Rust distance median was about 30.8 times lower than the Python reference median; this ratio is not a production claim.
Fallback order. Python callers currently dispatch
genomic_distance to Rust PyO3 when importable, else fall back to
the NumPy reference (bit-exact). Julia / Go / Mojo versions are
honest parity references for benchmarking, not called in the Python
hot path.
7.3 Whole-process industrial runners (4-backend parity set)¶
Beyond the per-kernel dispatch surface above, each of the four
compilers ships a whole-process evolve runner — the entire
ReplicationEngine.evolve_generation() loop plus the eleven industrial
guards (TournamentSelector, AgeRegulator, FormalSafetyGuard,
BloatPenalizer, ExtinctionDetector, HallOfFame, ParetoFront,
LineageTracker, MutationEngine × 4 variants, CrossoverEngine,
parametric FitnessEvaluator). A Python orchestrator can invoke any
backend with identical JSON config on stdin and receive an identical
EvolveResult JSON on stdout.
| Backend | Entry point | Source |
|---|---|---|
| Rust | evo_substrate_core.py_evolve_run(config_json) -> str (PyO3) |
crates/evo_substrate_core/src/runner.rs (1 227 LOC) |
| Julia | julia evo_runner.jl < cfg > result subprocess |
src/sc_neurocore/accel/julia/evo_substrate/evo_runner.jl (720 LOC) |
| Go | ./evo_substrate_bench --runner < cfg > result subprocess |
src/sc_neurocore/accel/go/evo_substrate/runner.go (926 LOC) |
| Mojo | pixi run mojo run kernels/evo_runner.mojo < cfg > result |
src/sc_neurocore/accel/mojo/kernels/evo_runner.mojo (803 LOC) |
All four runners share a common XorShift64 PRNG (constants 13/7/17,
0xDEADBEEFCAFEBABE fallback for zero seeds) so the same seed produces
byte-identical uniform sequences across languages.
7.3.1 Cross-backend parity — measured on fixed seed¶
Running the default config (seed=7, pop=16, gens=10,
industrial_mode=True) against all four runners:
| Backend | gen 10 best | Pareto size | Lineage records | Replications |
|---|---|---|---|---|
| Rust | 0.69955078125 | 1 | 96 | 80 |
| Julia | 0.69955078125 | 1 | 96 | 80 |
| Go | 0.6992578125 | 1 | 96 | 80 |
| Mojo | 0.6999804687500001 | 3 | 96 | 80 |
Rust ↔ Julia are byte-exact identical on every field (genome_ids, lineage records, HoF entries, Pareto members, gen-by-gen stats).
Rust ↔ Go match on all structural counters and converge on the
same Pareto size but drift at ~1e-3 on best_fitness — Go's
math.Cos and math.Log differ from Rust's libm at ~1 ULP, and
Box-Muller-based Gaussian mutation compounds that drift over ~80
mutations. A bit-exact polynomial cos/log is the path to close
this; tracked as follow-up.
Rust ↔ Mojo structural parity (lineage, counters); numerics drift similarly to Go for the same libm reason plus Mojo 0.26 Python-interop noise in the SHA-256 hashing path. Mojo's Pareto size is larger (3 vs 1) because the compounding drift leaves a few more non-dominated organisms standing.
The responsibility-specific suites matching
tests/test_evo_substrate/test_multilang_parity_*.py assert these four-way
relationships. The benchmark producer fails closed when a required backend is
missing; test environments may skip an unavailable optional toolchain.
7.3.2 Historical whole-process timing (seed=7, pop=16, 10 gens)¶
The figures below came from a 2026-04-20 exploratory run without the schema-v2 host-load and source-binding evidence now required. They explain dispatch-cost shape only and must not be used as release, regression, or publication evidence.
| Backend | Wall clock | Dispatch model |
|---|---|---|
| Rust (PyO3 in-process) | 0.57 ms / run | per-call from Python, warm |
| Rust (Criterion, pure Rust binary) | ~5 µs / run | in-process Rust, no FFI |
| Go (already-built binary, excl build) | ~2 ms / run | subprocess; add ~3 s for go build |
| Mojo (cold pixi + JIT compile) | ~1.1 s / run | subprocess; Mojo 0.26 JIT per invocation |
| Julia (cold Julia + JSON.jl precompile) | ~3 s / run | subprocess; amortises across long runs |
Python (ReplicationEngine reference) |
40.88 ms / run | in-process, NumPy |
Honest caveats on the "cold" column:
- Go — the
./evo_substrate_benchbinary must exist on disk. A freshgo build -o evo_substrate_bench .takes ~3 s the first time (compiler + module cache cold); the 2 ms figure above is the binary's own execution wall after build. The repo's.gitignorealready skips the compiled binary, and the pytest + parity harness rebuild it on demand. - Mojo — the ~1.1 s includes pixi env activation (~200 ms), Mojo
JIT compile (~800 ms), and Python interop bootstrap (~100 ms). Running
the same
.mojofile a second time in the SAME pixi env keeps the JIT result in the.pixi/envs/defaultmojo cache, so warm runs are ~700 ms. There is no truly "warm" Mojo because every subprocess restart re-pays the JIT cost. - Julia — ~3 s cold is dominated by
JSON.jl+SHA.jlprecompile (~2.5 s) plus Julia runtime startup (~500 ms). Inside an already-hot Julia session this drops to ~20 ms perevolve_runcall. The Julia runner is the right choice when the surrounding experiment is also Julia (e.g. aDifferentialEquations.jlfitness function); as a per-call backend from Python, the subprocess overhead dominates. - Rust — the 0.57 ms is warm in-process via the PyO3 extension. First import incurs a ~10 ms module load, amortised across any non-trivial run.
7.3.3 When to pick which backend¶
- Per-call from Python orchestration — Rust PyO3. The other three
subprocess startup costs (2 ms – 3 s) make them unusable for the
inner loop, which calls
evolve_generationthousands of times. - Long experiments (1 000+ generations, 100+ pop) — any subprocess backend amortises; pick by what else your experiment touches. Julia if you reach into DiffEq / Plots; Go if you already have a Go service mesh; Mojo if you use other SIMD kernels in the same pixi env.
- Audit / cross-check — run the same config through two backends and compare the JSON. Rust ↔ Julia is byte-exact; any mismatch indicates a regression in one of them.
7.3.4 Testing the 4-backend parity set¶
- Rust:
cargo test --manifest-path crates/evo_substrate_core/Cargo.toml— 17 unit tests. - Julia:
JULIA_DEPOT_PATH=build/julia-depot julia --project=src/sc_neurocore/accel/julia/evo_substrate src/sc_neurocore/accel/julia/evo_substrate/test_evo_runner.jl— 17 unit tests (PRNG, roundtrip, fitness, safety, determinism). - Go:
(cd src/sc_neurocore/accel/go/evo_substrate && go test -v ./...)— 8 unit tests (PRNG, roundtrip, id-shape, fitness, safety, determinism). - Mojo:
pytest tests/test_evo_substrate/test_mojo_runner.py— 7 side-validated unit tests driven from Python. - Cross-language parity:
JULIA_DEPOT_PATH=build/julia-depot pytest tests/test_evo_substrate/test_multilang_parity_*.py— 18 tests (schema, Rust↔Julia bit-exact, Rust↔Go tolerance, Rust↔Mojo structure, determinism).
Interpretation. In the current loaded-host artefact, safety checks and distance computations have 2.00 µs and 23.08 µs medians, while a 32-organism generation has a 16.28 ms median. Mutation and crossover remain the slower Python inner operations because they allocate NumPy arrays per call. Treat these relationships as local regression context until the benchmark is rerun on reserved isolated cores.
8. Citations¶
- Stanley K.O., Miikkulainen R. (2002). Evolving Neural Networks through Augmenting Topologies. Evolutionary Computation 10(2):99–127.
- Stanley K.O., D'Ambrosio D.B., Gauci J. (2009). A Hypercube-Based Encoding for Evolving Large-Scale Neural Networks. Artif. Life 15(2):185–212. (HyperNEAT / CPPN.)
- Syswerda G. (1989). Uniform Crossover in Genetic Algorithms. Proc. 3rd Int. Conf. on Genetic Algorithms, 2–9.
- Goldberg D.E., Richardson J. (1987). Genetic Algorithms with Sharing for Multimodal Function Optimization. ICGA-87, 41–49. (Fitness sharing.)
- Deb K., Pratap A., Agarwal S., Meyarivan T. (2002). A Fast and Elitist Multiobjective Genetic Algorithm: NSGA-II. IEEE TEC 6(2):182–197. (Pareto dominance.)
- Whitley D. (1999). An overview of evolutionary algorithms: practical issues and common pitfalls. Information and Software Technology 43(14):817–831. (Island model.)
- Secretan J. et al. (2011). Picbreeder: A Case Study in Collaborative Evolutionary Exploration of Design Space. Evolutionary Computation 19(3):373–403.
- Raup D.M. (1991). Extinction: Bad Genes or Bad Luck? W. W. Norton. (Mass-extinction dynamics.)
- Lehman J., Stanley K.O. (2011). Abandoning Objectives: Evolution Through the Search for Novelty Alone. Evolutionary Computation 19(2):189–223. (Novelty archive.)
- Šotek M. (2026). SC-NeuroCore: Self-replicating neuromorphic substrate. Internal report, ANULUM.
Reference¶
- Public compatibility facade:
src/sc_neurocore/evo_substrate/evo_substrate.py(156 LOC). - Implementation: 14 responsibility modules under
src/sc_neurocore/evo_substrate/; the largest isreplication.py(304 LOC). - Tests: 54 focused test and support modules under
tests/test_evo_substrate/; the largest istest_replication_replication_engine.py(265 LOC). - Demo:
examples/16_evo_substrate_demo.py. - Benchmarks:
benchmarks/bench_evo_substrate.pyandbenchmarks/bench_evo_substrate_multilang.py.
sc_neurocore.evo_substrate.evo_substrate
¶
Preserve the original evolutionary-substrate API over focused modules.
New code may import from sc_neurocore.evo_substrate or the focused
responsibility modules. Historical imports and pickle-qualified names remain
stable.
TileAllocation
dataclass
¶
Maps an organism to a physical FPGA tile.
Source code in src/sc_neurocore/evo_substrate/deployment.py
| Python | |
|---|---|
19 20 21 22 23 24 25 26 27 | |
TileDeploymentTracker
¶
Tracks which organisms are deployed on which FPGA tiles.
Source code in src/sc_neurocore/evo_substrate/deployment.py
| Python | |
|---|---|
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | |
free_tiles
property
¶
Return tile identifiers with no active allocation.
utilisation
property
¶
Return the fraction of tiles carrying an allocation.
deploy(organism, tile_id)
¶
Assign an organism to a tile and return the recorded allocation.
Source code in src/sc_neurocore/evo_substrate/deployment.py
| Python | |
|---|---|
37 38 39 40 41 42 43 44 45 46 47 | |
evict(tile_id)
¶
Mark a tile as free without mutating the former organism.
Source code in src/sc_neurocore/evo_substrate/deployment.py
| Python | |
|---|---|
49 50 51 | |
ActivationFunc
¶
Bases: Enum
Select the nonlinear response used by a CPPN node.
Source code in src/sc_neurocore/evo_substrate/development.py
| Python | |
|---|---|
20 21 22 23 24 25 26 27 | |
CPPNEdge
dataclass
¶
One edge in a CPPN network.
Source code in src/sc_neurocore/evo_substrate/development.py
| Python | |
|---|---|
39 40 41 42 43 44 45 46 | |
CPPNGenome
¶
Compositional Pattern Producing Network for developmental encoding.
Source code in src/sc_neurocore/evo_substrate/development.py
| Python | |
|---|---|
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | |
num_nodes
property
¶
Return the number of nodes in the CPPN graph.
num_edges
property
¶
Return the number of edges in the CPPN graph.
query(x, y)
¶
Query the CPPN at coordinates (x, y).
Source code in src/sc_neurocore/evo_substrate/development.py
| Python | |
|---|---|
63 64 65 66 67 68 69 70 71 72 | |
generate_weight_matrix(rows, cols)
¶
Generate a weight matrix by querying CPPN at grid positions.
Source code in src/sc_neurocore/evo_substrate/development.py
| Python | |
|---|---|
86 87 88 89 90 91 92 93 94 | |
CPPNNode
dataclass
¶
One node in a CPPN network.
Source code in src/sc_neurocore/evo_substrate/development.py
| Python | |
|---|---|
30 31 32 33 34 35 36 | |
CoevoOrganism
dataclass
¶
Organism with a co-evolutionary role.
Source code in src/sc_neurocore/evo_substrate/ecology.py
| Python | |
|---|---|
142 143 144 145 146 147 148 | |
CoevoRole
dataclass
¶
Bases: Enum
Identify an organism's role in a co-evolutionary interaction.
Source code in src/sc_neurocore/evo_substrate/ecology.py
| Python | |
|---|---|
133 134 135 136 137 138 139 | |
CoevolutionArena
¶
Runs predator-prey or symbiotic co-evolution.
Source code in src/sc_neurocore/evo_substrate/ecology.py
| Python | |
|---|---|
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | |
total_organisms
property
¶
Return the combined predator and prey population.
add_predator(organism)
¶
Add an organism to the predator population.
Source code in src/sc_neurocore/evo_substrate/ecology.py
| Python | |
|---|---|
158 159 160 | |
add_prey(organism)
¶
Add an organism to the prey population.
Source code in src/sc_neurocore/evo_substrate/ecology.py
| Python | |
|---|---|
162 163 164 | |
evaluate_interactions()
¶
Evaluate predator-prey fitness from pairwise interactions.
Source code in src/sc_neurocore/evo_substrate/ecology.py
| Python | |
|---|---|
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
ExtinctionDetector
¶
Detects population stagnation and triggers extinction events.
Source code in src/sc_neurocore/evo_substrate/ecology.py
| Python | |
|---|---|
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | |
check(best_fitness)
¶
Record fitness and report whether recent progress stagnated.
Source code in src/sc_neurocore/evo_substrate/ecology.py
| Python | |
|---|---|
107 108 109 110 111 112 113 114 115 116 117 | |
apply(population, rng)
¶
Kill kill_fraction of population randomly.
Source code in src/sc_neurocore/evo_substrate/ecology.py
| Python | |
|---|---|
119 120 121 122 123 124 125 126 127 | |
Island
dataclass
¶
One sub-population (deme) in an island model.
Source code in src/sc_neurocore/evo_substrate/ecology.py
| Python | |
|---|---|
23 24 25 26 27 28 29 | |
IslandModel
¶
Multi-deme evolution with periodic migration.
Source code in src/sc_neurocore/evo_substrate/ecology.py
| Python | |
|---|---|
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | |
total_population
property
¶
Return the number of organisms across every island.
add_organism(island_id, organism)
¶
Append an organism to the selected island population.
Source code in src/sc_neurocore/evo_substrate/ecology.py
| Python | |
|---|---|
40 41 42 | |
migrate(rng)
¶
Migrate best organisms between random island pairs.
Source code in src/sc_neurocore/evo_substrate/ecology.py
| Python | |
|---|---|
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | |
NoveltyArchive
¶
Behavioural novelty archive for novelty search.
Source code in src/sc_neurocore/evo_substrate/ecology.py
| Python | |
|---|---|
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | |
size
property
¶
Return the number of archived behaviour vectors.
novelty_score(behaviour)
¶
Return mean distance to the nearest archived behaviours.
Source code in src/sc_neurocore/evo_substrate/ecology.py
| Python | |
|---|---|
75 76 77 78 79 80 81 82 | |
maybe_add(behaviour)
¶
Archive a copied behaviour when its novelty exceeds the threshold.
Source code in src/sc_neurocore/evo_substrate/ecology.py
| Python | |
|---|---|
84 85 86 87 88 89 90 | |
OrganismEmitter
¶
Emits evolved organisms as NIR graph or Verilog.
Source code in src/sc_neurocore/evo_substrate/emission.py
| Python | |
|---|---|
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | |
to_nir(genome)
staticmethod
¶
Emit a simplified NIR-compatible graph dict.
Source code in src/sc_neurocore/evo_substrate/emission.py
| Python | |
|---|---|
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | |
to_verilog(genome, module_name=None)
staticmethod
¶
Emit Verilog wrapper for the organism.
Source code in src/sc_neurocore/evo_substrate/emission.py
| Python | |
|---|---|
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | |
to_photonic_netlist(genome, pml_layers=12)
staticmethod
¶
Emit a photonic netlist compatible with the optics PhotonicCompiler.
Source code in src/sc_neurocore/evo_substrate/emission.py
| Python | |
|---|---|
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | |
FitnessEvaluator
¶
Evaluates organism fitness from simulation metrics.
Source code in src/sc_neurocore/evo_substrate/fitness.py
| Python | |
|---|---|
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | |
evaluate(genome, metrics)
¶
Evaluate one genome from simulation metrics and hardware proxies.
Parameters¶
genome Genome whose topology determines energy and latency proxies. metrics Simulation metrics; the optional accuracy key is in [0, 1].
Source code in src/sc_neurocore/evo_substrate/fitness.py
| Python | |
|---|---|
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | |
FitnessResult
dataclass
¶
Fitness evaluation result.
Source code in src/sc_neurocore/evo_substrate/fitness.py
| Python | |
|---|---|
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | |
compute_composite(w_acc=0.5, w_energy=0.3, w_latency=0.2)
¶
Update and return the weighted three-objective fitness.
Parameters¶
w_acc, w_energy, w_latency Weights for accuracy, energy, and latency scores.
Source code in src/sc_neurocore/evo_substrate/fitness.py
| Python | |
|---|---|
39 40 41 42 43 44 45 46 47 48 49 50 51 52 | |
FitnessType
¶
Bases: Enum
Identify the objective exposed by a fitness evaluation.
Source code in src/sc_neurocore/evo_substrate/fitness.py
| Python | |
|---|---|
20 21 22 23 24 25 26 | |
HWFitnessCollector
¶
Collects HW fitness from deployed organisms.
Source code in src/sc_neurocore/evo_substrate/fitness.py
| Python | |
|---|---|
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | |
total_reports
property
¶
Return the number of genomes with submitted FPGA reports.
submit(report)
¶
Store the latest FPGA fitness report for one genome.
Source code in src/sc_neurocore/evo_substrate/fitness.py
| Python | |
|---|---|
114 115 116 | |
get(genome_id)
¶
Return the latest report for the genome, if submitted.
Source code in src/sc_neurocore/evo_substrate/fitness.py
| Python | |
|---|---|
118 119 120 | |
HWFitnessReport
dataclass
¶
Fitness feedback from actual FPGA execution.
Source code in src/sc_neurocore/evo_substrate/fitness.py
| Python | |
|---|---|
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | |
hw_composite
property
¶
Return the weighted accuracy, clock, and timing-closure score.
Genome
dataclass
¶
Complete genome for an evolving SC organism.
Source code in src/sc_neurocore/evo_substrate/genome.py
| Python | |
|---|---|
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | |
vector_dim
property
¶
Return the number of values in the canonical genome vector.
to_vector()
¶
Return the canonical 19-value topology-neuron-plasticity vector.
Source code in src/sc_neurocore/evo_substrate/genome.py
| Python | |
|---|---|
166 167 168 169 170 171 172 173 174 | |
from_vector(v, gen=0)
classmethod
¶
Construct a genome from its canonical parameter vector.
Parameters¶
v Nineteen-value canonical genome vector. gen Generation assigned to the reconstructed genome.
Source code in src/sc_neurocore/evo_substrate/genome.py
| Python | |
|---|---|
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | |
compute_id()
¶
Set and return the 12-hex SHA-256 prefix of the genome vector.
Source code in src/sc_neurocore/evo_substrate/genome.py
| Python | |
|---|---|
199 200 201 202 203 | |
GenomeSerializer
¶
Serializes/deserializes genomes for persistence.
Source code in src/sc_neurocore/evo_substrate/genome.py
| Python | |
|---|---|
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | |
to_dict(genome)
staticmethod
¶
Return a JSON-ready mapping that preserves genome identity fields.
Source code in src/sc_neurocore/evo_substrate/genome.py
| Python | |
|---|---|
209 210 211 212 213 214 215 216 217 218 219 | |
from_dict(d)
staticmethod
¶
Reconstruct a genome from :meth:to_dict output.
Source code in src/sc_neurocore/evo_substrate/genome.py
| Python | |
|---|---|
221 222 223 224 225 226 227 228 229 230 | |
NeuronGene
dataclass
¶
Encodes neuron-level parameters (ArcaneNeuron-compatible).
Source code in src/sc_neurocore/evo_substrate/genome.py
| Python | |
|---|---|
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | |
to_vector()
¶
Serialise neuron kinetics in their canonical eight-value order.
Source code in src/sc_neurocore/evo_substrate/genome.py
| Python | |
|---|---|
74 75 76 77 78 79 80 81 82 83 84 85 86 87 | |
from_vector(v)
classmethod
¶
Construct neuron kinetics while enforcing finite lower bounds.
Parameters¶
v Eight values ordered as the documented neuron parameter block.
Source code in src/sc_neurocore/evo_substrate/genome.py
| Python | |
|---|---|
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | |
PlasticityGene
dataclass
¶
Encodes plasticity rule parameters.
Source code in src/sc_neurocore/evo_substrate/genome.py
| Python | |
|---|---|
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | |
to_vector()
¶
Serialise plasticity fields in their canonical six-value order.
Source code in src/sc_neurocore/evo_substrate/genome.py
| Python | |
|---|---|
121 122 123 124 125 126 127 128 129 130 131 132 | |
from_vector(v)
classmethod
¶
Construct plasticity parameters while enforcing valid rates.
Parameters¶
v Six values ordered as the documented plasticity parameter block.
Source code in src/sc_neurocore/evo_substrate/genome.py
| Python | |
|---|---|
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | |
TopologyGene
dataclass
¶
Encodes the network topology.
Source code in src/sc_neurocore/evo_substrate/genome.py
| Python | |
|---|---|
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | |
to_vector()
¶
Serialise topology fields in their canonical five-value order.
Source code in src/sc_neurocore/evo_substrate/genome.py
| Python | |
|---|---|
30 31 32 33 34 35 36 37 38 39 40 | |
from_vector(v)
classmethod
¶
Construct a topology gene while enforcing physical parameter bounds.
Parameters¶
v Five values ordered as neuron count, layer count, connectivity, recurrent fraction, and bitstream length.
Source code in src/sc_neurocore/evo_substrate/genome.py
| Python | |
|---|---|
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | |
LineageRecord
dataclass
¶
One entry in the ancestry log.
Source code in src/sc_neurocore/evo_substrate/lineage.py
| Python | |
|---|---|
19 20 21 22 23 24 25 26 27 | |
LineageTracker
¶
Tracks ancestry graph for all organisms.
Source code in src/sc_neurocore/evo_substrate/lineage.py
| Python | |
|---|---|
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | |
num_records
property
¶
Return the number of recorded organisms.
record(organism, mutation_type='seed')
¶
Append one ancestry record and index it by genome identifier.
Source code in src/sc_neurocore/evo_substrate/lineage.py
| Python | |
|---|---|
37 38 39 40 41 42 43 44 45 46 47 48 | |
get_ancestors(genome_id)
¶
Walk the ancestry chain to the root.
Source code in src/sc_neurocore/evo_substrate/lineage.py
| Python | |
|---|---|
50 51 52 53 54 55 56 57 58 | |
Organism
dataclass
¶
One evolving SC organism.
Source code in src/sc_neurocore/evo_substrate/organism.py
| Python | |
|---|---|
23 24 25 26 27 28 29 30 31 32 33 | |
ReplicationEngine
¶
Manages organism reproduction, mutation, and deployment.
Selection → Replication → Mutation → Safety Check → Deploy
Source code in src/sc_neurocore/evo_substrate/replication.py
| Python | |
|---|---|
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | |
best_organism
property
¶
Return the highest-fitness living organism, if one is evaluated.
best_fitness
property
¶
Return the best living composite fitness, or zero when unavailable.
mean_fitness
property
¶
Return mean composite fitness across evaluated organisms.
seed(genome)
¶
Seed the population with an initial organism.
Source code in src/sc_neurocore/evo_substrate/replication.py
| Python | |
|---|---|
85 86 87 88 89 90 91 | |
replicate(parent)
¶
Create a mutated child from a parent.
Source code in src/sc_neurocore/evo_substrate/replication.py
| Python | |
|---|---|
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | |
replicate_crossover(parent_a, parent_b)
¶
Create a child via crossover of two parents.
Source code in src/sc_neurocore/evo_substrate/replication.py
| Python | |
|---|---|
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | |
evaluate_all(metrics_fn)
¶
Evaluate fitness for all living organisms.
Source code in src/sc_neurocore/evo_substrate/replication.py
| Python | |
|---|---|
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | |
verify_runtime_faults(organism, config=None)
¶
Run seeded runtime fault diagnosis and apply bounded degradation.
Source code in src/sc_neurocore/evo_substrate/replication.py
| Python | |
|---|---|
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | |
select_and_cull(survival_fraction=0.5)
¶
Select fittest organisms, cull the rest. Elitism preserved.
Source code in src/sc_neurocore/evo_substrate/replication.py
| Python | |
|---|---|
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | |
evolve_generation(metrics_fn)
¶
Run one full evolutionary generation.
Source code in src/sc_neurocore/evo_substrate/replication.py
| Python | |
|---|---|
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | |
FormalSafetyGuard
¶
Validates emitted organisms against safety constraints before deployment.
Links to the safety_cert module for IEC 61508 compliance.
Source code in src/sc_neurocore/evo_substrate/safety.py
| Python | |
|---|---|
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | |
rejection_rate
property
¶
Return rejected checks divided by all completed checks.
check(genome)
¶
Evaluate topology limits and update cumulative rejection counters.
Source code in src/sc_neurocore/evo_substrate/safety.py
| Python | |
|---|---|
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | |
ResourceBudget
dataclass
¶
Per-organism resource constraints.
Source code in src/sc_neurocore/evo_substrate/safety.py
| Python | |
|---|---|
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | |
check(genome)
¶
Return budget compliance and human-readable resource violations.
Source code in src/sc_neurocore/evo_substrate/safety.py
| Python | |
|---|---|
125 126 127 128 129 130 131 132 133 | |
RuntimeFaultCheck
dataclass
¶
Recorded runtime fault/degradation decision for one organism.
Source code in src/sc_neurocore/evo_substrate/safety.py
| Python | |
|---|---|
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | |
from_plan(organism, plan)
classmethod
¶
Capture a degradation plan against the organism's current identity.
Source code in src/sc_neurocore/evo_substrate/safety.py
| Python | |
|---|---|
48 49 50 51 52 53 54 55 56 57 58 59 60 | |
to_dict()
¶
Return a JSON-ready fault-check summary.
Source code in src/sc_neurocore/evo_substrate/safety.py
| Python | |
|---|---|
62 63 64 65 66 67 68 69 70 71 72 73 | |
RuntimeFaultConfig
dataclass
¶
Runtime fault-check settings for evolved SC organisms.
Source code in src/sc_neurocore/evo_substrate/safety.py
| Python | |
|---|---|
23 24 25 26 27 28 29 30 31 32 | |
SafetyBounds
dataclass
¶
Constrains the mutation space to prevent runaway replication.
Enforces hard limits on genome parameters that could cause resource exhaustion or unsafe behaviour on FPGA tiles.
Source code in src/sc_neurocore/evo_substrate/safety.py
| Python | |
|---|---|
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | |
clamp(genome)
¶
Clamp mutable genome fields to configured deployment bounds.
Source code in src/sc_neurocore/evo_substrate/safety.py
| Python | |
|---|---|
93 94 95 96 97 98 99 100 101 102 103 104 105 106 | |
is_within_bounds(genome)
¶
Return whether topology dimensions fit the configured bounds.
Source code in src/sc_neurocore/evo_substrate/safety.py
| Python | |
|---|---|
108 109 110 111 112 113 114 | |
SafetyCheckResult
dataclass
¶
Result of a formal safety check on emitted Verilog/NIR.
Source code in src/sc_neurocore/evo_substrate/safety.py
| Python | |
|---|---|
136 137 138 139 140 141 142 143 144 145 | |
AgeRegulator
¶
Culls organisms that exceed a maximum lifespan.
Source code in src/sc_neurocore/evo_substrate/selection.py
| Python | |
|---|---|
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | |
apply(population, current_generation)
¶
Mark over-age organisms dead and return the number culled.
Source code in src/sc_neurocore/evo_substrate/selection.py
| Python | |
|---|---|
144 145 146 147 148 149 150 151 152 | |
BloatMetrics
dataclass
¶
Measures genome complexity for bloat detection.
Source code in src/sc_neurocore/evo_substrate/selection.py
| Python | |
|---|---|
158 159 160 161 162 163 164 165 166 167 168 169 170 171 | |
is_bloated
property
¶
Return whether complexity exceeds the baseline bloat score.
BloatPenalizer
¶
Penalizes fitness for bloated genomes.
Source code in src/sc_neurocore/evo_substrate/selection.py
| Python | |
|---|---|
185 186 187 188 189 190 191 192 193 194 195 196 197 198 | |
penalize(fitness, genome)
¶
Apply a bounded multiplicative penalty above the bloat threshold.
Source code in src/sc_neurocore/evo_substrate/selection.py
| Python | |
|---|---|
192 193 194 195 196 197 198 | |
HallOfFame
¶
Maintains the top-N organisms across all generations.
Source code in src/sc_neurocore/evo_substrate/selection.py
| Python | |
|---|---|
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | |
best_fitness
property
¶
Return the highest retained composite fitness, or zero when empty.
size
property
¶
Return the number of retained hall-of-fame entries.
update(organism)
¶
Insert a fitted organism and retain the highest-ranked entries.
Source code in src/sc_neurocore/evo_substrate/selection.py
| Python | |
|---|---|
31 32 33 34 35 36 37 38 39 40 | |
ParetoFront
¶
Maintains a non-dominated Pareto front.
Source code in src/sc_neurocore/evo_substrate/selection.py
| Python | |
|---|---|
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | |
size
property
¶
Return the number of non-dominated organisms.
update(organism)
¶
Insert an organism when no retained member dominates its fitness.
Source code in src/sc_neurocore/evo_substrate/selection.py
| Python | |
|---|---|
114 115 116 117 118 119 120 121 122 123 124 125 126 127 | |
TournamentSelector
¶
Tournament selection with configurable pressure.
Source code in src/sc_neurocore/evo_substrate/selection.py
| Python | |
|---|---|
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | |
select(population, rng)
¶
Return the highest-fitness member of one random tournament.
Raises¶
ValueError If the population is empty.
Source code in src/sc_neurocore/evo_substrate/selection.py
| Python | |
|---|---|
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | |
select_n(population, n, rng)
¶
Run independent tournaments and return the requested selections.
Source code in src/sc_neurocore/evo_substrate/selection.py
| Python | |
|---|---|
85 86 87 88 89 | |
ComplexityTracker
¶
Tracks population complexity over generations.
Source code in src/sc_neurocore/evo_substrate/statistics.py
| Python | |
|---|---|
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | |
mean_trajectory
property
¶
Return mean genome complexity in generation order.
is_complexifying
property
¶
Return whether mean complexity increased over three or more samples.
record(generation, population)
¶
Append mean and maximum complexity for a non-empty population.
Source code in src/sc_neurocore/evo_substrate/statistics.py
| Python | |
|---|---|
123 124 125 126 127 128 129 130 131 132 133 134 | |
EvoStatisticsTracker
¶
Records per-generation statistics for analytics.
Source code in src/sc_neurocore/evo_substrate/statistics.py
| Python | |
|---|---|
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | |
generations_tracked
property
¶
Return the number of recorded generations.
fitness_trajectory
property
¶
Return best fitness in generation order.
diversity_trajectory
property
¶
Return population diversity in generation order.
record(stats)
¶
Append statistics for one completed generation.
Source code in src/sc_neurocore/evo_substrate/statistics.py
| Python | |
|---|---|
41 42 43 | |
improvement_rate()
¶
Return best-fitness change from the first to latest generation.
Source code in src/sc_neurocore/evo_substrate/statistics.py
| Python | |
|---|---|
60 61 62 63 64 | |
GenerationStats
dataclass
¶
Statistics for one generation.
Source code in src/sc_neurocore/evo_substrate/statistics.py
| Python | |
|---|---|
22 23 24 25 26 27 28 29 30 31 32 | |
GenomeDiff
dataclass
¶
Structural diff between two genomes.
Source code in src/sc_neurocore/evo_substrate/statistics.py
| Python | |
|---|---|
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | |
is_identical
property
¶
Return whether the canonical vectors contain no changed values.
CrossoverEngine
¶
Uniform crossover between two parent genomes.
Source code in src/sc_neurocore/evo_substrate/variation.py
| Python | |
|---|---|
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | |
crossover(parent_a, parent_b)
¶
Uniform crossover: each gene drawn from either parent.
Source code in src/sc_neurocore/evo_substrate/variation.py
| Python | |
|---|---|
131 132 133 134 135 136 137 138 139 140 | |
MutationConfig
dataclass
¶
Controls mutation rates and magnitudes.
Source code in src/sc_neurocore/evo_substrate/variation.py
| Python | |
|---|---|
33 34 35 36 37 38 39 40 41 42 43 | |
MutationEngine
¶
Applies mutations to genomes.
Source code in src/sc_neurocore/evo_substrate/variation.py
| Python | |
|---|---|
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | |
mutate(genome)
¶
Apply a random mutation and return the mutated child.
Source code in src/sc_neurocore/evo_substrate/variation.py
| Python | |
|---|---|
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | |
MutationType
¶
Bases: Enum
Identify the mutation operator applied to a child genome.
Source code in src/sc_neurocore/evo_substrate/variation.py
| Python | |
|---|---|
23 24 25 26 27 28 29 30 | |
compute_bloat(genome, baseline_neurons=16)
¶
Compute bloat relative to a baseline.
Source code in src/sc_neurocore/evo_substrate/selection.py
| Python | |
|---|---|
174 175 176 177 178 179 180 181 182 | |
dominates(a, b)
¶
Return whether the first result Pareto-dominates the second.
Source code in src/sc_neurocore/evo_substrate/selection.py
| Python | |
|---|---|
95 96 97 98 99 100 101 102 103 104 105 | |
assign_species(population, threshold=0.3)
¶
Assign organisms to species by genomic distance.
First organism of each species is the representative.
Source code in src/sc_neurocore/evo_substrate/speciation.py
| Python | |
|---|---|
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | |
genomic_distance(a, b)
¶
Normalised L1 distance between genome vectors.
Dispatches to the Rust evo_substrate_core.py_genomic_distance when
the compiled extension is importable. The NumPy fallback is kept as
the reference implementation and produces bit-exact identical values.
Source code in src/sc_neurocore/evo_substrate/speciation.py
| Python | |
|---|---|
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | |
population_diversity(population)
¶
Mean pairwise genomic distance (0 = clones, 1 = max diversity).
Source code in src/sc_neurocore/evo_substrate/speciation.py
| Python | |
|---|---|
80 81 82 83 84 85 86 87 88 | |
shared_fitness(organism, population, sigma=0.3)
¶
Shared fitness: divide by niche count to prevent species domination.
Source code in src/sc_neurocore/evo_substrate/speciation.py
| Python | |
|---|---|
91 92 93 94 95 96 97 98 99 100 101 102 103 | |
genome_complexity(genome)
¶
Measure evolved complexity (information-theoretic).
Source code in src/sc_neurocore/evo_substrate/statistics.py
| Python | |
|---|---|
104 105 106 107 108 109 110 111 112 113 114 | |
genome_diff(a, b)
¶
Compute structural diff between two genomes.
Source code in src/sc_neurocore/evo_substrate/statistics.py
| Python | |
|---|---|
87 88 89 90 91 92 93 94 95 96 97 98 | |