Layer 1.5: architecture-general neural existence proof
Re-scopes Layer 2 into a cheaper, architecture-general neural collapse proof
before the LLM rung. Realises the same Wright–Fisher abstractions in real trained
generative models on a fully-synthetic sandbox with an exact oracle, reusing
knowledge.metrics/truth/seeding and the output contract so neural curves overlay
the Layer-1 analytic curves.
- src/neural/: synthetic token-grammar sandbox (lossless identity + stochastic
style), ExactOracle, HistogramModel bridge, generation loop, experiment runner
- HARD GATE passed: histogram lineage reproduces Layer 1 exactly (neutral decay,
exact H_eq, tracks run_lineage) — tests/test_neural_validation.py
- torch models: autoregressive RNN + MLP (VAE implemented, not yet fidelity-
passing); determinism seeding derived from the SeedSequence stream
- N0 bridge (neural g*=0.047 ≈ Layer-1 0.048), N1 collapse-in-weights, N2 phase
boundary, N5 architecture-generality (collapse + grounding-rescue in histogram
+ RNN + MLP). Manifests/configs committed; parquet gitignored, hashes tracked
- additive backward-compatible save_artifacts extension; Makefile neural targets
Finding: neural smoothing partially resists H-collapse, so forward-KL and tail
survival are the sharp neural collapse metrics (H is smooth, per Layer 1).
92 tests green. Remaining (tasks/todo.md): N4 merge, N2 refine, N3/N6, VAE
fidelity, MNIST tier, figures. LLM/LoRA rung and C3 deferred.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1721d047fa
commit
840b6b00b3
35 changed files with 3679 additions and 23 deletions
13
.gitignore
vendored
13
.gitignore
vendored
|
|
@ -1,13 +1,8 @@
|
||||||
# Environments
|
# Reproducibility: results artifacts are regenerable from committed configs + seeds; only
|
||||||
|
# their hashes (in each run's manifest.json) are tracked, per the open-science contract.
|
||||||
.venv/
|
.venv/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
|
results/**/results.parquet
|
||||||
# Results: the large data artifact (results.parquet) is regenerable and gitignored;
|
models/
|
||||||
# the reproducibility metadata (manifest.json with content hashes, resolved_config.yaml)
|
|
||||||
# and the figures (PNG/PDF) are tracked so the paper's figures live in the repo.
|
|
||||||
results/**/*.parquet
|
|
||||||
|
|
||||||
# OS / editor
|
|
||||||
.DS_Store
|
|
||||||
|
|
|
||||||
24
CLAUDE.md
24
CLAUDE.md
|
|
@ -2,12 +2,28 @@
|
||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
## Current state: greenfield
|
## Current state: Layer 1 complete; Layer 1.5 (neural) in progress
|
||||||
|
|
||||||
This repository currently contains **only two design documents and no code**. The task is to implement the study those documents specify.
|
- **Layer 1** (`src/knowledge/`) — **complete and validated.** All six experiments E1–E6, the
|
||||||
|
closed-form scientific-validation tests, figures, and reproducibility harness exist. Headline:
|
||||||
|
critical grounding `g* = 0.048 ≪ 1`; the E4 finding that mean-mixture distillation conserves
|
||||||
|
collapse while only a union-preserving max-merge realises the recombination benefit.
|
||||||
|
- **Layer 1.5** (`src/neural/`) — **in progress.** An architecture-general neural existence proof
|
||||||
|
(re-scoped Layer 2): the same Wright–Fisher abstractions realised in *real trained generative
|
||||||
|
models* (histogram bridge + RNN + MLP; VAE implemented but not fidelity-passing) on a
|
||||||
|
fully-synthetic sandbox with an exact oracle, plus real MNIST as a later secondary tier. See
|
||||||
|
`tasks/todo.md` for status and `~/.claude/plans/we-are-going-to-cheerful-fog.md` for the plan.
|
||||||
|
**Done:** scaffold, the histogram bridge gate (reproduces Layer 1 exactly), N0 (bridge, neural
|
||||||
|
g*=0.047 ≈ Layer 1), N1 (collapse in RNN weights), N2 (neural phase boundary), N5
|
||||||
|
(architecture-generality). **Remaining:** N4 (mean-vs-max-merge, load-bearing), N3, N6, figures,
|
||||||
|
the MNIST tier. The LLM/LoRA rung and the C3 vertical claim are deferred.
|
||||||
|
|
||||||
- `the-lamarckian-society-v4.md` — the *perspective paper*. The conceptual thesis: a multigenerational architecture of continual-learning agents that mature, teach, and evolve. Read this for the "why."
|
The two design documents are the source of truth for intent:
|
||||||
- `lamarckian-society-technical-blueprint-v1.md` — the *technical blueprint*. A build specification written to be handed to a coding agent. Read this for the "what" and "how." **It is normative**: module names, function signatures, config schema, experiment IDs, and directory layout in it are the contract to implement against.
|
|
||||||
|
- `paper/the-lamarckian-society-v4.md` — the *perspective paper* (the "why").
|
||||||
|
- `paper/blueprint.md` — the *technical blueprint* (the "what"/"how"). **It is normative** for
|
||||||
|
Layer 1 and the LLM Layer 2; Layer 1.5 is a cost-staged intermediate the blueprint does not
|
||||||
|
cover, designed to preserve the same §1 abstractions.
|
||||||
|
|
||||||
Everything below summarizes the blueprint so you can orient fast, but the blueprint is the source of truth. When they conflict, the blueprint wins; when the blueprint is silent, minimize decisions and match its established patterns.
|
Everything below summarizes the blueprint so you can orient fast, but the blueprint is the source of truth. When they conflict, the blueprint wins; when the blueprint is silent, minimize decisions and match its established patterns.
|
||||||
|
|
||||||
|
|
|
||||||
13
Makefile
13
Makefile
|
|
@ -1,19 +1,28 @@
|
||||||
# Layer 1 automation. The uv venv (built from the committed uv.lock) is the
|
# Layer 1 + Layer 1.5 automation. The uv venv (built from the committed uv.lock) is the
|
||||||
# reproducibility source of truth; every target runs inside it via `uv run`.
|
# reproducibility source of truth; every target runs inside it via `uv run`.
|
||||||
|
|
||||||
.PHONY: env test layer1 figures clean
|
.PHONY: env env-neural test layer1 layer2 neural figures clean
|
||||||
|
|
||||||
env: ## build .venv from the committed lockfile
|
env: ## build .venv from the committed lockfile
|
||||||
uv sync --extra dev
|
uv sync --extra dev
|
||||||
|
|
||||||
|
env-neural: ## add the Layer 1.5 torch stack (GPU; Stage C onward)
|
||||||
|
uv sync --extra dev --extra neural
|
||||||
|
|
||||||
test: ## correctness tests + scientific-validation tests (the spine of trust)
|
test: ## correctness tests + scientific-validation tests (the spine of trust)
|
||||||
uv run pytest
|
uv run pytest
|
||||||
|
|
||||||
layer1: ## run experiments E1-E6
|
layer1: ## run experiments E1-E6
|
||||||
for e in E1 E2 E3 E4 E5 E6; do uv run python -m knowledge.experiment configs/layer1/$$e.yaml; done
|
for e in E1 E2 E3 E4 E5 E6; do uv run python -m knowledge.experiment configs/layer1/$$e.yaml; done
|
||||||
|
|
||||||
|
neural: ## run Layer 1.5 neural experiments (N-series)
|
||||||
|
for c in configs/neural/*.yaml; do uv run python -m neural.experiment $$c; done
|
||||||
|
|
||||||
|
layer2: neural ## alias: Layer 1.5 is the current Layer-2 deliverable (LLM rung deferred)
|
||||||
|
|
||||||
figures: ## regenerate figures from committed results
|
figures: ## regenerate figures from committed results
|
||||||
for e in E1 E2 E3 E4 E5 E6; do MPLBACKEND=Agg uv run python figures/plot_$$e.py; done
|
for e in E1 E2 E3 E4 E5 E6; do MPLBACKEND=Agg uv run python figures/plot_$$e.py; done
|
||||||
|
for p in figures/plot_N*.py; do [ -e "$$p" ] && MPLBACKEND=Agg uv run python "$$p"; done
|
||||||
|
|
||||||
clean: ## remove caches and generated results (keeps committed manifests)
|
clean: ## remove caches and generated results (keeps committed manifests)
|
||||||
rm -rf .pytest_cache **/__pycache__
|
rm -rf .pytest_cache **/__pycache__
|
||||||
|
|
|
||||||
42
configs/neural/N0.yaml
Normal file
42
configs/neural/N0.yaml
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
experiment: N0_bridge_histogram
|
||||||
|
kind: gen_lineage
|
||||||
|
seed: 20260704
|
||||||
|
n_replicates: 60
|
||||||
|
generations: 200
|
||||||
|
|
||||||
|
# Bridge / harness-faithfulness check (Layer 1.5 build-order Stage B): run the E2 grounding
|
||||||
|
# phase-boundary sweep through the NEURAL runner with the histogram model, which is exactly
|
||||||
|
# neutral Wright-Fisher drift with immigration. Stationary H must track the closed form
|
||||||
|
# H_eq = H* * m(2n+m-1)/(n+2nm+m^2) and reproduce a critical g* << 1 — i.e. the neural
|
||||||
|
# plumbing reproduces the analytic core before any real network is trained. R=1 so every
|
||||||
|
# grounding policy reduces to the proportional immigration model H_eq is derived for.
|
||||||
|
synthetic:
|
||||||
|
K: 200
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.1
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 1.0e-3
|
||||||
|
init: truth
|
||||||
|
style_len: 2
|
||||||
|
style_vocab: 4
|
||||||
|
id_base: 2
|
||||||
|
|
||||||
|
model:
|
||||||
|
kind: histogram
|
||||||
|
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding: {m: 0, policy: proportional} # m is overwritten per g by the sweep
|
||||||
|
remint: {enabled: false, period: null, H_gate: null}
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-9
|
||||||
|
support_eps: 1.0e-9
|
||||||
|
|
||||||
|
sweep:
|
||||||
|
- param: g
|
||||||
|
values: [0.0, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.4]
|
||||||
|
|
||||||
|
output:
|
||||||
|
dir: results/N0
|
||||||
50
configs/neural/N1.yaml
Normal file
50
configs/neural/N1.yaml
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
experiment: N1_collapse_in_weights
|
||||||
|
kind: gen_lineage
|
||||||
|
seed: 20260704
|
||||||
|
n_replicates: 5
|
||||||
|
|
||||||
|
# N1 (Layer 1.5, maps to Layer-1 E1 / blueprint C1): does model collapse appear in REAL
|
||||||
|
# trained weights under dry recursive self-training, and does a little grounding arrest it?
|
||||||
|
# An autoregressive RNN is retrained each generation on n samples drawn from the previous
|
||||||
|
# generation's RNN (drift), optionally mixed with m verifier-grounded samples. Expect: the
|
||||||
|
# dry arm (g=0) loses diversity (H falls) and forgets the tail (forward-KL to truth rises,
|
||||||
|
# tail_mass shrinks); grounded arms hold. Falsifier: dry inheritance does not degrade in
|
||||||
|
# real weights -> the neural collapse claim is unsupported at this scale.
|
||||||
|
generations: 25
|
||||||
|
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 1.0e-3
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 128
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 2.0e-3
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding: {m: 0, policy: proportional} # m overwritten per g by the sweep
|
||||||
|
remint: {enabled: false, period: null, H_gate: null}
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-9
|
||||||
|
support_eps: 1.0e-9
|
||||||
|
|
||||||
|
sweep:
|
||||||
|
- param: g
|
||||||
|
values: [0.0, 0.02, 0.05, 0.1]
|
||||||
|
|
||||||
|
output:
|
||||||
|
dir: results/N1
|
||||||
50
configs/neural/N2.yaml
Normal file
50
configs/neural/N2.yaml
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
experiment: N2_grounding_phase_boundary_neural
|
||||||
|
kind: gen_lineage
|
||||||
|
seed: 20260704
|
||||||
|
n_replicates: 5
|
||||||
|
|
||||||
|
# N2 (Layer 1.5 headline, maps to Layer-1 E2): the grounding phase boundary in REAL weights.
|
||||||
|
# Sweep the grounding fraction g = m/(n+m) and locate the neural critical g* at which
|
||||||
|
# stationary diversity is restored. Layer 1 found g* = 0.048 << 1. The neural regime (finite
|
||||||
|
# model capacity, a smaller K so gen-0 fidelity holds) will not reproduce that value exactly
|
||||||
|
# -- the claim is directional (blueprint 3.5): a critical g* << 1 exists in trained weights,
|
||||||
|
# i.e. a little grounding protects most of the diversity. Falsifier: stationary H flat in g,
|
||||||
|
# or only restored as g -> 1.
|
||||||
|
generations: 30
|
||||||
|
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 1.0e-3
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 128
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 2.0e-3
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding: {m: 0, policy: proportional} # m overwritten per g by the sweep
|
||||||
|
remint: {enabled: false, period: null, H_gate: null}
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-9
|
||||||
|
support_eps: 1.0e-9
|
||||||
|
|
||||||
|
sweep:
|
||||||
|
- param: g
|
||||||
|
values: [0.0, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2]
|
||||||
|
|
||||||
|
output:
|
||||||
|
dir: results/N2
|
||||||
54
configs/neural/N5.yaml
Normal file
54
configs/neural/N5.yaml
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
experiment: N5_architecture_generality
|
||||||
|
kind: gen_lineage
|
||||||
|
seed: 20260704
|
||||||
|
n_replicates: 5
|
||||||
|
|
||||||
|
# N5 (a new Layer-1.5 axis, no Layer-1 counterpart): is collapse ARCHITECTURE-GENERAL?
|
||||||
|
# Run the same dry-vs-grounded protocol across three genuinely different learners that
|
||||||
|
# share only the generative-collapse operator: the exact histogram (= Wright-Fisher, the
|
||||||
|
# analytic anchor), an autoregressive GRU (recurrent), and an autoregressive MLP (feed-
|
||||||
|
# forward). Expect the same SIGN in all: dry (g=0) loses diversity / forgets the tail;
|
||||||
|
# grounding arrests it. Falsifier: the signs appear only for the histogram -> real neural
|
||||||
|
# inductive biases break the Wright-Fisher mapping. (The sequence VAE is implemented but
|
||||||
|
# excluded here: it does not clear the gen-0 fidelity gate on the Zipf-codeword task, so
|
||||||
|
# its collapse would be confounded with underfitting; see tasks/todo.md.)
|
||||||
|
generations: 22
|
||||||
|
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 1.0e-3
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
|
||||||
|
model:
|
||||||
|
kind: rnn # overwritten per arm by the model.kind sweep
|
||||||
|
hidden: 192
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 2.0e-3
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding: {m: 0, policy: proportional} # m overwritten per g by the sweep
|
||||||
|
remint: {enabled: false, period: null, H_gate: null}
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-9
|
||||||
|
support_eps: 1.0e-9
|
||||||
|
|
||||||
|
sweep:
|
||||||
|
- param: model.kind
|
||||||
|
values: [histogram, rnn, mlp]
|
||||||
|
- param: g
|
||||||
|
values: [0.0, 0.05]
|
||||||
|
|
||||||
|
output:
|
||||||
|
dir: results/N5
|
||||||
208
paper/layer1-summary.md
Normal file
208
paper/layer1-summary.md
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
# Layer 1 — Summary of results
|
||||||
|
|
||||||
|
*The Lamarckian Society, analytical core. Two summaries of the same work: one technical,
|
||||||
|
one accessible to ML engineers and neuroscientists with no population-genetics background.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## A. Technical summary
|
||||||
|
|
||||||
|
### What was built
|
||||||
|
|
||||||
|
Layer 1 is a parametric model of generational knowledge transmission, built on the
|
||||||
|
observation that the generational step — *sample from the parent distribution, optionally
|
||||||
|
mix in fresh real samples, refit* — is **literally a Wright–Fisher process with
|
||||||
|
immigration**, not merely analogous to one. Knowledge is a distribution `p_t` over `K`
|
||||||
|
discrete items on the simplex; a fixed true distribution `p*` carries a deliberate heavy
|
||||||
|
(Zipf) tail; "model collapse" is the loss of rare alleles under drift. Each safeguard
|
||||||
|
from the perspective paper is one operator on the step:
|
||||||
|
|
||||||
|
- **grounding** `g = m/(n+m)` — immigration of `m` real samples per `n` inherited (mutation supply);
|
||||||
|
- **region-matched grounding** — immigration structured by locus;
|
||||||
|
- **multi-teacher distillation** — recombination across lineages;
|
||||||
|
- **selection** — directional (`greedy`) vs. balancing/novelty (`qd`);
|
||||||
|
- **re-minting** — a founder event that freezes `p_t` as the new reference and discards `p*`.
|
||||||
|
|
||||||
|
Because the process is Wright–Fisher, it inherits **closed-form validation targets**, which
|
||||||
|
are enforced as `test_scientific_validation.py` assertions (the "spine of trust"):
|
||||||
|
|
||||||
|
1. neutral heterozygosity decay `E[H_t] = H₀(1−1/n)^t`;
|
||||||
|
2. fixation probability = initial frequency;
|
||||||
|
3. **exact** mutation–drift equilibrium `H_eq = H*·m(2n+m−1)/(n+2nm+m²)` (not the textbook `θ/(1+θ)` approximation);
|
||||||
|
4. tail-persistence threshold `m·p*_i ≳ 1`;
|
||||||
|
5. recombination union coverage `U(K_T,ρ,q) = T[ρq + (1−ρ)(1−(1−q)^{K_T})]`, with teachers built by a shared-switch exchangeable-Bernoulli construction giving *exact* marginal retention `q` and pairwise correlation `ρ`.
|
||||||
|
|
||||||
|
The simulator matches (1), (3), (5) to `<0.5%` and (2), (4) statistically. 71 tests pass.
|
||||||
|
|
||||||
|
### Findings (E1–E6)
|
||||||
|
|
||||||
|
- **E1 — collapse (null).** Neutral drift reproduces the geometric `H` decay to within
|
||||||
|
Monte-Carlo error; support collapses `K→1`; forward KL to truth diverges. Tail *items*
|
||||||
|
go extinct ≈10× faster than head items. **Subtlety:** aggregate tail *mass* is a drift
|
||||||
|
martingale (mean-conserved), so it is a misleading collapse metric; tail-*item* survival
|
||||||
|
is the honest one.
|
||||||
|
|
||||||
|
- **E2 — grounding phase boundary (headline).** Stationary `H` tracks the exact `H_eq`
|
||||||
|
across the sweep. An operational critical grounding `g* = 0.048` (95% bootstrap CI
|
||||||
|
[0.047, 0.050]) marks where `H` reaches 95% of `H*`; **g* ≪ 1** — as little as `m=1`
|
||||||
|
real sample against `n=200` inherited (`g=0.005`) restores 68% of the truth's diversity;
|
||||||
|
`g=0.05` reaches 96%. The phase boundary in `H` is *smooth* (H is continuous in `m`); the
|
||||||
|
sharp threshold lives in discrete tail-item survival. Per-rarity-band analysis makes the
|
||||||
|
`m·p*_i ≳ 1` law visible: at feasible grounding the **deep tail is unrescuable** — diversity
|
||||||
|
is cheap to protect, but the rarest items require grounding budgets that scale as `1/p_min`.
|
||||||
|
|
||||||
|
- **E3 — region-matched grounding.** At fixed total budget, `matched` grounding preserves
|
||||||
|
the exercised region's tail (survival 0.49) where `uniform` spreads thin and lets it
|
||||||
|
collapse (0.07). Grounding protects only what it overlaps. (Per-region `H` is confounded
|
||||||
|
by region mass under matched grounding; tail-item survival is the clean metric.)
|
||||||
|
|
||||||
|
- **E4 — multi-teacher recombination.** Union coverage matches `U(K_T,ρ,q)` exactly
|
||||||
|
(recombination *supplies* the tail). **Principal finding:** under the blueprint's
|
||||||
|
mean-mixture distillation, surviving tail coverage is **flat in `K_T`** — a conservation
|
||||||
|
law, since averaging preserves expected pupil tail mass at `q·(tail mass of p*)`
|
||||||
|
regardless of `K_T`, and in the rare-tail (linear-survival) regime the `1/K_T` dilution
|
||||||
|
*exactly cancels* the union gain. The recombination benefit is realised only under a
|
||||||
|
**union-preserving merge** (`max` over teachers, à la M2N2 model-merging), where surviving
|
||||||
|
coverage rises with `K_T` and with decorrelation `(1−ρ)`. E4 reports both operators.
|
||||||
|
|
||||||
|
- **E5 — QD vs. greedy.** At matched grounding, greedy (directional) selection drives
|
||||||
|
fixation (`H≈0.01`); quality-diversity selection (`w_i ∝ f_i·p_i^{−α}`) holds `H` at a
|
||||||
|
positive plateau (0.48–0.88, rising with the novelty exponent α). qd ≫ greedy.
|
||||||
|
|
||||||
|
- **E6 — re-minting gate.** Re-minting a *collapsed* lineage discards the original truth and
|
||||||
|
makes forward KL to the original **diverge** (irreversible lock-in), and even accelerates
|
||||||
|
the `H` collapse (grounding now reinforces the surviving few). A diversity gate
|
||||||
|
(`H ≥ H_gate`) refuses to re-mint while collapsed and keeps KL bounded; re-minting a
|
||||||
|
healthy lineage is harmless.
|
||||||
|
|
||||||
|
### Implications
|
||||||
|
|
||||||
|
1. **The economic bet holds for diversity, not the deep tail.** The architecture's central
|
||||||
|
claim — "a little grounding protects a lot of inheritance" — is confirmed *for overall
|
||||||
|
diversity* (`g* ≪ 1`). But the deepest tail cannot be held by grounding at any feasible
|
||||||
|
budget (`m* ∼ 1/p_min`). Preserving the deep tail is therefore *not* grounding's job — it
|
||||||
|
is recombination's, which sets up E4 and the paper's multi-teacher argument.
|
||||||
|
|
||||||
|
2. **Naive multi-teacher distillation does not prevent tail collapse; merging does.** This is
|
||||||
|
the sharpest new result. The paper's recombination benefit is real at the *supply* (union)
|
||||||
|
level but is annihilated by mean-mixture averaging at matched budget. The benefit survives
|
||||||
|
into the pupil only under a union-preserving merge operator. The paper's recombination
|
||||||
|
claim should therefore rest on **model-merging (M2N2)**, not on averaging distillation —
|
||||||
|
a concrete, falsifiable design constraint carried into Layer 2 (contrast C4).
|
||||||
|
|
||||||
|
3. **Re-minting is a one-way door and must be gated.** Assimilating soft inheritance into a
|
||||||
|
new base while the lineage has narrowed locks in the collapse irreversibly. A cheap
|
||||||
|
diversity gate suffices to prevent it.
|
||||||
|
|
||||||
|
4. **Everything is anchored to closed forms.** Three of the five predictions are exact, so
|
||||||
|
the simulator is *validated*, not merely plausible — the headline curves sit on analytic
|
||||||
|
targets. The study is bitwise-reproducible from a seed (uv-locked environment).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## B. Accessible summary (for ML engineers and neuroscientists)
|
||||||
|
|
||||||
|
### The question
|
||||||
|
|
||||||
|
Modern AI is trained once and frozen; it cannot keep learning without *catastrophically
|
||||||
|
forgetting*. The Lamarckian Society proposes an alternative: **generations** of bounded
|
||||||
|
agents that learn through a working life, then *teach* a fresh pupil, who inherits the
|
||||||
|
compressed knowledge and starts ahead — a cultural ratchet. The danger is well known to ML
|
||||||
|
engineers under a different name: train a model on the previous model's outputs, generation
|
||||||
|
after generation, and it suffers **model collapse** — the rare, improbable cases (the *tail*)
|
||||||
|
vanish first and the model drifts to its own mode. The teaching step in this architecture *is*
|
||||||
|
that collapse operation. So the whole scheme lives or dies on one question: **under what
|
||||||
|
conditions does generational teaching accumulate knowledge instead of degrading it?** Layer 1
|
||||||
|
answers that quantitatively, before any GPUs are involved.
|
||||||
|
|
||||||
|
### The one idea that makes it rigorous
|
||||||
|
|
||||||
|
Represent a model's knowledge as a probability distribution over discrete "items"
|
||||||
|
(capabilities, facts, behaviours). One generation = *draw a finite sample of size `n` from the
|
||||||
|
teacher, and refit the pupil to it.* That finite-sampling step is **mathematically identical**
|
||||||
|
to genetic drift in a finite population — the century-old **Wright–Fisher** process. That is
|
||||||
|
not a metaphor; it is the same equations. The payoff: population genetics already has **exact
|
||||||
|
formulas** for how diversity decays, what survives, and how "immigration" of fresh individuals
|
||||||
|
holds a population together. We inherit those formulas as **ground truth to check the simulator
|
||||||
|
against** — so the results below are *provably correct*, not just plausible-looking curves.
|
||||||
|
|
||||||
|
A small dictionary:
|
||||||
|
|
||||||
|
| in this model | ML reading | neuroscience reading |
|
||||||
|
|---|---|---|
|
||||||
|
| knowledge item | a capability / mode of the model | a memory / stored pattern |
|
||||||
|
| sample size `n` | how much data the student distils from | consolidation bandwidth |
|
||||||
|
| the tail | rare capabilities / long-tail inputs | rare episodic detail |
|
||||||
|
| grounding `g` | fraction of fresh **verified** real data in the training mix | new lived experience replenishing memory |
|
||||||
|
| heterozygosity `H` | diversity of the model's knowledge | richness / non-degeneracy of memory |
|
||||||
|
| collapse | mode-seeking / catastrophic forgetting | memory degradation, loss of the improbable |
|
||||||
|
|
||||||
|
### What we found, in plain terms
|
||||||
|
|
||||||
|
1. **Without fresh data, teaching collapses — and the rare stuff goes first, fast.** Pure
|
||||||
|
generation-on-generation distillation loses diversity exponentially, at a rate set by how
|
||||||
|
much data the student sees. Rare items go extinct roughly 10× faster than common ones.
|
||||||
|
(This reproduces, exactly, the known math of drift.)
|
||||||
|
|
||||||
|
2. **A little fresh grounded data rescues almost all the diversity — this is the headline.**
|
||||||
|
Mixing in even ~5% verified real data (in the extreme, *one* real sample against 200
|
||||||
|
inherited) restores ~70–96% of the model's diversity and holds it there indefinitely.
|
||||||
|
Grounding is cheap and it works. **But** there is a hard limit: the *very rarest*
|
||||||
|
capabilities still cannot be saved by grounding alone — protecting an item of rarity `p`
|
||||||
|
needs a real-data budget that grows like `1/p`. So grounding rescues *diversity* cheaply,
|
||||||
|
but not the deepest tail. (That is a feature, not a bug — it tells us what the other
|
||||||
|
mechanisms are for.)
|
||||||
|
|
||||||
|
3. **Grounding only protects what it overlaps.** Spreading a fixed amount of fresh data thinly
|
||||||
|
across everything fails to protect any specific area; you must ground the *specific* region
|
||||||
|
you want to keep. "Don't inherit dry, region by region" is literally true.
|
||||||
|
|
||||||
|
4. **Learning from several diverse teachers can preserve rare knowledge one teacher would
|
||||||
|
lose — but only if you combine them correctly. This is the surprising, important one.**
|
||||||
|
Multiple decorrelated teachers *collectively* retain far more of the tail than any one of
|
||||||
|
them (we verified this against an exact formula). But whether the *pupil* keeps that
|
||||||
|
depends entirely on **how you merge the teachers**. The standard approach — averaging their
|
||||||
|
outputs (ordinary multi-teacher distillation) — **mathematically cancels the benefit**: the
|
||||||
|
averaging dilutes each teacher's rare knowledge by exactly the factor by which more teachers
|
||||||
|
would have helped. A **"keep-the-strongest-teacher-per-item" merge** (the style of model
|
||||||
|
*merging*, e.g. Sakana's M2N2) *does* realise the benefit — rare-capability retention rises
|
||||||
|
with the number and diversity of teachers. **Design lesson: to fight tail collapse with
|
||||||
|
multiple teachers, merge their weights; don't average their outputs.**
|
||||||
|
|
||||||
|
5. **Optimising for "quality" alone collapses diversity; rewarding novelty too keeps it
|
||||||
|
alive.** Selecting for fitness drives everything to the single best item (fixation);
|
||||||
|
rewarding rareness alongside fitness (quality-diversity selection) maintains a rich,
|
||||||
|
diverse population. (Familiar to anyone who has watched a population-based or RLHF pipeline
|
||||||
|
mode-collapse.)
|
||||||
|
|
||||||
|
6. **"Baking in" accumulated knowledge into a new base model is a one-way door.** Periodically
|
||||||
|
consolidating soft inheritance into fresh base weights lets the system grow without bound —
|
||||||
|
but if you do it *after* the model has already narrowed, you lock in the damage
|
||||||
|
**permanently** (the original, uncollapsed reference is gone). A cheap check — only
|
||||||
|
consolidate while diversity is still high — prevents the irreversible mistake.
|
||||||
|
|
||||||
|
### Why it is novel and why it matters
|
||||||
|
|
||||||
|
- **It turns a hand-wavy debate into exact, falsifiable science.** "Does generational
|
||||||
|
distillation ratchet up or collapse?" was an argument by analogy. Casting it as
|
||||||
|
Wright–Fisher makes it a set of equations with closed-form answers, and the simulator is
|
||||||
|
validated against them — so the headline curves *sit on analytic targets*, not on
|
||||||
|
eyeballing.
|
||||||
|
|
||||||
|
- **It quantifies the feasibility of the whole architecture.** The result that a *tiny*
|
||||||
|
grounding fraction protects most of the diversity (`g* ≪ 1`) is what makes a
|
||||||
|
continually-teaching society economically plausible rather than a data-hungry fantasy.
|
||||||
|
|
||||||
|
- **It corrects how the field should build multi-teacher systems.** The finding that ordinary
|
||||||
|
averaging distillation gives *no* protection against tail collapse — while weight-merging
|
||||||
|
does — is a concrete, testable design constraint that most current multi-agent/distillation
|
||||||
|
setups get wrong by default.
|
||||||
|
|
||||||
|
- **It gives an operational safety rule for self-improving systems.** "Consolidate only while
|
||||||
|
diversity is high" is a simple, measurable gate against a failure mode (irreversible
|
||||||
|
collapse-in-place) that self-distilling systems are otherwise prone to.
|
||||||
|
|
||||||
|
All of this is at the level of *distributions and dynamics*, deliberately upstream of neural
|
||||||
|
networks — Layer 2 then checks that the same three signs (grounded inheritance holds where dry
|
||||||
|
inheritance degrades; complementary teachers preserve what one sheds; general capability climbs
|
||||||
|
while each specialty is re-earned) appear in real LoRA-adapted language models.
|
||||||
|
|
@ -16,15 +16,22 @@ dependencies = [
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = ["pytest>=8.0"]
|
dev = ["pytest>=8.0"]
|
||||||
|
# Layer 1.5 neural existence proof. Torch is only needed from Stage C (RNN/VAE/MLP);
|
||||||
|
# Stages A-B (synthetic sandbox + histogram bridge) are pure NumPy and run in the base env.
|
||||||
|
# The default PyPI torch wheel is CUDA-enabled (cu13, matching the RTX A4000 driver).
|
||||||
|
# Install with `uv sync --extra neural`.
|
||||||
|
neural = ["torch>=2.2"]
|
||||||
|
# The real-MNIST secondary-confirmation tier only. Install with `uv sync --extra mnist`.
|
||||||
|
mnist = ["torchvision>=0.17"]
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["hatchling"]
|
requires = ["hatchling"]
|
||||||
build-backend = "hatchling.build"
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
# src-layout: src/knowledge/ is importable as `knowledge` (the normative package
|
# src-layout: src/knowledge/ is importable as `knowledge` (the normative package
|
||||||
# name the scientific-validation conformance tests import).
|
# name the scientific-validation conformance tests import). src/neural/ is Layer 1.5.
|
||||||
[tool.hatch.build.targets.wheel]
|
[tool.hatch.build.targets.wheel]
|
||||||
packages = ["src/knowledge"]
|
packages = ["src/knowledge", "src/neural"]
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
testpaths = ["tests"]
|
testpaths = ["tests"]
|
||||||
|
|
|
||||||
16
results/N0/manifest.json
Normal file
16
results/N0/manifest.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"experiment": "N0_bridge_histogram",
|
||||||
|
"master_seed": 20260704,
|
||||||
|
"git_commit": null,
|
||||||
|
"python": "3.14.5",
|
||||||
|
"libraries": {
|
||||||
|
"numpy": "2.5.0",
|
||||||
|
"scipy": "1.18.0",
|
||||||
|
"pandas": "3.0.3",
|
||||||
|
"pyarrow": "24.0.0"
|
||||||
|
},
|
||||||
|
"rows": 96480,
|
||||||
|
"results_sha256": "445fd5165fc69edbca87f79c0cf669b5879025637f4d36ddc769e940fb02f114",
|
||||||
|
"layer": "1.5",
|
||||||
|
"model_kind": "histogram"
|
||||||
|
}
|
||||||
288
results/N0/resolved_config.yaml
Normal file
288
results/N0/resolved_config.yaml
Normal file
|
|
@ -0,0 +1,288 @@
|
||||||
|
experiment: N0_bridge_histogram
|
||||||
|
seed: 20260704
|
||||||
|
n_replicates: 60
|
||||||
|
source_config:
|
||||||
|
experiment: N0_bridge_histogram
|
||||||
|
kind: gen_lineage
|
||||||
|
seed: 20260704
|
||||||
|
n_replicates: 60
|
||||||
|
generations: 200
|
||||||
|
synthetic:
|
||||||
|
K: 200
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.1
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 2
|
||||||
|
style_vocab: 4
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: histogram
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 0
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
sweep:
|
||||||
|
- param: g
|
||||||
|
values:
|
||||||
|
- 0.0
|
||||||
|
- 0.005
|
||||||
|
- 0.01
|
||||||
|
- 0.02
|
||||||
|
- 0.05
|
||||||
|
- 0.1
|
||||||
|
- 0.2
|
||||||
|
- 0.4
|
||||||
|
output:
|
||||||
|
dir: results/N0
|
||||||
|
grid:
|
||||||
|
- label:
|
||||||
|
g: 0.0
|
||||||
|
m: 0
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 200
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.1
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 2
|
||||||
|
style_vocab: 4
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: histogram
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 0
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 200
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
g: 0.005
|
||||||
|
m: 1
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 200
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.1
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 2
|
||||||
|
style_vocab: 4
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: histogram
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 1
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 200
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
g: 0.01
|
||||||
|
m: 2
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 200
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.1
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 2
|
||||||
|
style_vocab: 4
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: histogram
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 2
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 200
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
g: 0.02
|
||||||
|
m: 4
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 200
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.1
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 2
|
||||||
|
style_vocab: 4
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: histogram
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 4
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 200
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
g: 0.05
|
||||||
|
m: 11
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 200
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.1
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 2
|
||||||
|
style_vocab: 4
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: histogram
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 11
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 200
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
g: 0.1
|
||||||
|
m: 22
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 200
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.1
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 2
|
||||||
|
style_vocab: 4
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: histogram
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 22
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 200
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
g: 0.2
|
||||||
|
m: 50
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 200
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.1
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 2
|
||||||
|
style_vocab: 4
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: histogram
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 50
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 200
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
g: 0.4
|
||||||
|
m: 133
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 200
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.1
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 2
|
||||||
|
style_vocab: 4
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: histogram
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 133
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 200
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
17
results/N1/manifest.json
Normal file
17
results/N1/manifest.json
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"experiment": "N1_collapse_in_weights",
|
||||||
|
"master_seed": 20260704,
|
||||||
|
"git_commit": null,
|
||||||
|
"python": "3.14.5",
|
||||||
|
"libraries": {
|
||||||
|
"numpy": "2.5.0",
|
||||||
|
"scipy": "1.18.0",
|
||||||
|
"pandas": "3.0.3",
|
||||||
|
"pyarrow": "24.0.0",
|
||||||
|
"torch": "2.12.1"
|
||||||
|
},
|
||||||
|
"rows": 520,
|
||||||
|
"results_sha256": "27438db70af3240524855e59c4a14d3fb63bf6fca2d2002cdcb1ac354559909b",
|
||||||
|
"layer": "1.5",
|
||||||
|
"model_kind": "rnn"
|
||||||
|
}
|
||||||
194
results/N1/resolved_config.yaml
Normal file
194
results/N1/resolved_config.yaml
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
experiment: N1_collapse_in_weights
|
||||||
|
seed: 20260704
|
||||||
|
n_replicates: 5
|
||||||
|
source_config:
|
||||||
|
experiment: N1_collapse_in_weights
|
||||||
|
kind: gen_lineage
|
||||||
|
seed: 20260704
|
||||||
|
n_replicates: 5
|
||||||
|
generations: 25
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 128
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 0
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
sweep:
|
||||||
|
- param: g
|
||||||
|
values:
|
||||||
|
- 0.0
|
||||||
|
- 0.02
|
||||||
|
- 0.05
|
||||||
|
- 0.1
|
||||||
|
output:
|
||||||
|
dir: results/N1
|
||||||
|
grid:
|
||||||
|
- label:
|
||||||
|
g: 0.0
|
||||||
|
m: 0
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 128
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 0
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 25
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
g: 0.02
|
||||||
|
m: 4
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 128
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 4
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 25
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
g: 0.05
|
||||||
|
m: 11
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 128
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 11
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 25
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
g: 0.1
|
||||||
|
m: 22
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 128
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 22
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 25
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
17
results/N2/manifest.json
Normal file
17
results/N2/manifest.json
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"experiment": "N2_grounding_phase_boundary_neural",
|
||||||
|
"master_seed": 20260704,
|
||||||
|
"git_commit": null,
|
||||||
|
"python": "3.14.5",
|
||||||
|
"libraries": {
|
||||||
|
"numpy": "2.5.0",
|
||||||
|
"scipy": "1.18.0",
|
||||||
|
"pandas": "3.0.3",
|
||||||
|
"pyarrow": "24.0.0",
|
||||||
|
"torch": "2.12.1"
|
||||||
|
},
|
||||||
|
"rows": 1085,
|
||||||
|
"results_sha256": "93590677221955189814ccf044b9dc8aee36b026e350c9dea7e8d23b137e514b",
|
||||||
|
"layer": "1.5",
|
||||||
|
"model_kind": "rnn"
|
||||||
|
}
|
||||||
305
results/N2/resolved_config.yaml
Normal file
305
results/N2/resolved_config.yaml
Normal file
|
|
@ -0,0 +1,305 @@
|
||||||
|
experiment: N2_grounding_phase_boundary_neural
|
||||||
|
seed: 20260704
|
||||||
|
n_replicates: 5
|
||||||
|
source_config:
|
||||||
|
experiment: N2_grounding_phase_boundary_neural
|
||||||
|
kind: gen_lineage
|
||||||
|
seed: 20260704
|
||||||
|
n_replicates: 5
|
||||||
|
generations: 30
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 128
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 0
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
sweep:
|
||||||
|
- param: g
|
||||||
|
values:
|
||||||
|
- 0.0
|
||||||
|
- 0.005
|
||||||
|
- 0.01
|
||||||
|
- 0.02
|
||||||
|
- 0.05
|
||||||
|
- 0.1
|
||||||
|
- 0.2
|
||||||
|
output:
|
||||||
|
dir: results/N2
|
||||||
|
grid:
|
||||||
|
- label:
|
||||||
|
g: 0.0
|
||||||
|
m: 0
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 128
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 0
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 30
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
g: 0.005
|
||||||
|
m: 1
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 128
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 1
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 30
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
g: 0.01
|
||||||
|
m: 2
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 128
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 2
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 30
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
g: 0.02
|
||||||
|
m: 4
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 128
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 4
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 30
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
g: 0.05
|
||||||
|
m: 11
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 128
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 11
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 30
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
g: 0.1
|
||||||
|
m: 22
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 128
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 22
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 30
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
g: 0.2
|
||||||
|
m: 50
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 128
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 50
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 30
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
17
results/N5/manifest.json
Normal file
17
results/N5/manifest.json
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"experiment": "N5_architecture_generality",
|
||||||
|
"master_seed": 20260704,
|
||||||
|
"git_commit": null,
|
||||||
|
"python": "3.14.5",
|
||||||
|
"libraries": {
|
||||||
|
"numpy": "2.5.0",
|
||||||
|
"scipy": "1.18.0",
|
||||||
|
"pandas": "3.0.3",
|
||||||
|
"pyarrow": "24.0.0",
|
||||||
|
"torch": "2.12.1"
|
||||||
|
},
|
||||||
|
"rows": 690,
|
||||||
|
"results_sha256": "4e8d7931e791493aa35ea7b114d81b90a3a58eabd92d8b1dbd87cf0a9e200ba1",
|
||||||
|
"layer": "1.5",
|
||||||
|
"model_kind": "rnn"
|
||||||
|
}
|
||||||
275
results/N5/resolved_config.yaml
Normal file
275
results/N5/resolved_config.yaml
Normal file
|
|
@ -0,0 +1,275 @@
|
||||||
|
experiment: N5_architecture_generality
|
||||||
|
seed: 20260704
|
||||||
|
n_replicates: 5
|
||||||
|
source_config:
|
||||||
|
experiment: N5_architecture_generality
|
||||||
|
kind: gen_lineage
|
||||||
|
seed: 20260704
|
||||||
|
n_replicates: 5
|
||||||
|
generations: 22
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 192
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 0
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
sweep:
|
||||||
|
- param: model.kind
|
||||||
|
values:
|
||||||
|
- histogram
|
||||||
|
- rnn
|
||||||
|
- mlp
|
||||||
|
- param: g
|
||||||
|
values:
|
||||||
|
- 0.0
|
||||||
|
- 0.05
|
||||||
|
output:
|
||||||
|
dir: results/N5
|
||||||
|
grid:
|
||||||
|
- label:
|
||||||
|
kind: histogram
|
||||||
|
g: 0.0
|
||||||
|
m: 0
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: histogram
|
||||||
|
hidden: 192
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 0
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 22
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
kind: histogram
|
||||||
|
g: 0.05
|
||||||
|
m: 11
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: histogram
|
||||||
|
hidden: 192
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 11
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 22
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
kind: rnn
|
||||||
|
g: 0.0
|
||||||
|
m: 0
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 192
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 0
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 22
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
kind: rnn
|
||||||
|
g: 0.05
|
||||||
|
m: 11
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: rnn
|
||||||
|
hidden: 192
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 11
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 22
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
kind: mlp
|
||||||
|
g: 0.0
|
||||||
|
m: 0
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: mlp
|
||||||
|
hidden: 192
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 0
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 22
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
- label:
|
||||||
|
kind: mlp
|
||||||
|
g: 0.05
|
||||||
|
m: 11
|
||||||
|
neural_cfg:
|
||||||
|
synthetic:
|
||||||
|
K: 256
|
||||||
|
R: 1
|
||||||
|
tail: zipf
|
||||||
|
zipf_s: 1.3
|
||||||
|
tail_frac: 0.5
|
||||||
|
tail_threshold: 0.001
|
||||||
|
init: truth
|
||||||
|
style_len: 3
|
||||||
|
style_vocab: 5
|
||||||
|
id_base: 2
|
||||||
|
model:
|
||||||
|
kind: mlp
|
||||||
|
hidden: 192
|
||||||
|
embed: 24
|
||||||
|
epochs: 25
|
||||||
|
lr: 0.002
|
||||||
|
batch_size: 256
|
||||||
|
n_eval: 12000
|
||||||
|
dynamics:
|
||||||
|
n: 200
|
||||||
|
grounding:
|
||||||
|
m: 11
|
||||||
|
policy: proportional
|
||||||
|
remint:
|
||||||
|
enabled: false
|
||||||
|
period: null
|
||||||
|
H_gate: null
|
||||||
|
generations: 22
|
||||||
|
metrics:
|
||||||
|
kl_floor: 1.0e-09
|
||||||
|
support_eps: 1.0e-09
|
||||||
|
|
@ -211,11 +211,28 @@ def _content_hash(path: Path) -> str:
|
||||||
return h.hexdigest()
|
return h.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def save_artifacts(cfg: dict, df: pd.DataFrame, out_dir: Path) -> None:
|
def save_artifacts(cfg: dict, df: pd.DataFrame, out_dir: Path,
|
||||||
|
extra_libs: tuple[str, ...] = (),
|
||||||
|
extra_manifest: dict | None = None,
|
||||||
|
grid: list | None = None) -> None:
|
||||||
"""Write the reproducibility output contract (blueprint 2.7 / 4).
|
"""Write the reproducibility output contract (blueprint 2.7 / 4).
|
||||||
|
|
||||||
Writes ``results.parquet``, ``resolved_config.yaml`` (the fully-expanded config), and
|
Writes ``results.parquet``, ``resolved_config.yaml`` (the fully-expanded config), and
|
||||||
``manifest.json`` (library versions, master seed, git commit, content hash).
|
``manifest.json`` (library versions, master seed, git commit, content hash).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cfg (dict): The parsed experiment config.
|
||||||
|
df (pd.DataFrame): The long-form results.
|
||||||
|
out_dir (Path): Output directory.
|
||||||
|
extra_libs (tuple[str, ...]): Extra library names to record versions for (e.g.
|
||||||
|
``torch``, ``torchvision`` for Layer 1.5). Missing libraries are skipped, so a
|
||||||
|
caller can pass optional deps unconditionally.
|
||||||
|
extra_manifest (dict | None): Extra key/value pairs to merge into the manifest
|
||||||
|
(e.g. model architecture, oracle checkpoint hash, determinism flags).
|
||||||
|
grid (list | None): Pre-expanded ``[{label, lineage_cfg}, ...]`` to record in the
|
||||||
|
resolved config. If None, it is computed via ``expand_sweeps`` for the Layer-1
|
||||||
|
``lineage`` kind (a caller with a different schema, e.g. Layer 1.5, passes its
|
||||||
|
own expanded grid here).
|
||||||
"""
|
"""
|
||||||
out_dir.mkdir(parents=True, exist_ok=True)
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||||||
results_path = out_dir / "results.parquet"
|
results_path = out_dir / "results.parquet"
|
||||||
|
|
@ -227,24 +244,32 @@ def save_artifacts(cfg: dict, df: pd.DataFrame, out_dir: Path) -> None:
|
||||||
"n_replicates": cfg["n_replicates"],
|
"n_replicates": cfg["n_replicates"],
|
||||||
"source_config": cfg,
|
"source_config": cfg,
|
||||||
}
|
}
|
||||||
if cfg.get("kind", "lineage") == "lineage":
|
if grid is not None:
|
||||||
|
resolved["grid"] = grid
|
||||||
|
elif cfg.get("kind", "lineage") == "lineage":
|
||||||
resolved["grid"] = [
|
resolved["grid"] = [
|
||||||
{"label": label, "lineage_cfg": lineage_cfg}
|
{"label": label, "lineage_cfg": lineage_cfg}
|
||||||
for label, lineage_cfg in expand_sweeps(cfg)
|
for label, lineage_cfg in expand_sweeps(cfg)
|
||||||
]
|
]
|
||||||
(out_dir / "resolved_config.yaml").write_text(yaml.safe_dump(resolved, sort_keys=False))
|
(out_dir / "resolved_config.yaml").write_text(yaml.safe_dump(resolved, sort_keys=False))
|
||||||
|
|
||||||
|
libraries: dict[str, str] = {}
|
||||||
|
for lib in ("numpy", "scipy", "pandas", "pyarrow") + tuple(extra_libs):
|
||||||
|
try:
|
||||||
|
libraries[lib] = version(lib)
|
||||||
|
except Exception: # optional dep not installed -> omit rather than crash
|
||||||
|
pass
|
||||||
manifest = {
|
manifest = {
|
||||||
"experiment": cfg["experiment"],
|
"experiment": cfg["experiment"],
|
||||||
"master_seed": cfg["seed"],
|
"master_seed": cfg["seed"],
|
||||||
"git_commit": _git_commit(),
|
"git_commit": _git_commit(),
|
||||||
"python": sys.version.split()[0],
|
"python": sys.version.split()[0],
|
||||||
"libraries": {
|
"libraries": libraries,
|
||||||
lib: version(lib) for lib in ("numpy", "scipy", "pandas", "pyarrow")
|
|
||||||
},
|
|
||||||
"rows": int(len(df)),
|
"rows": int(len(df)),
|
||||||
"results_sha256": _content_hash(results_path),
|
"results_sha256": _content_hash(results_path),
|
||||||
}
|
}
|
||||||
|
if extra_manifest:
|
||||||
|
manifest.update(extra_manifest)
|
||||||
(out_dir / "manifest.json").write_text(json.dumps(manifest, indent=2))
|
(out_dir / "manifest.json").write_text(json.dumps(manifest, indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
13
src/neural/__init__.py
Normal file
13
src/neural/__init__.py
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
"""Layer 1.5 — the architecture-general neural existence proof.
|
||||||
|
|
||||||
|
Realises the Layer-1 (``knowledge``) Wright-Fisher abstractions in *real trained
|
||||||
|
generative models* on a fully-synthetic sandbox whose ground-truth ``p*`` is known
|
||||||
|
exactly. A model's knowledge is measured as its output distribution over ``K`` discrete
|
||||||
|
*modes* (via an oracle), so the same metrics (``knowledge.metrics``), the same closed
|
||||||
|
forms, and the same experiments carry over — a neural collapse curve can be overlaid on
|
||||||
|
a Layer-1 analytic curve.
|
||||||
|
|
||||||
|
The package is staged by cost: the histogram model (pure NumPy) reduces this layer
|
||||||
|
*exactly* to Layer 1 and is the validation bridge; the RNN/VAE/MLP models (torch, added
|
||||||
|
from Stage C) show that collapse is architecture-general.
|
||||||
|
"""
|
||||||
124
src/neural/config.py
Normal file
124
src/neural/config.py
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
"""Resolved run configuration for a neural (Layer 1.5) lineage.
|
||||||
|
|
||||||
|
Mirrors the ``knowledge.config`` idiom exactly: frozen dataclasses with a ``from_dict``
|
||||||
|
that fills defaults and rejects unknown keys via ``knowledge.config._sub``. The grounding,
|
||||||
|
re-mint, and metrics blocks are *reused verbatim* from ``knowledge.config`` so the neural
|
||||||
|
runner speaks the same schema as Layer 1 (grounding ``m``, the ``g -> m`` conversion, the
|
||||||
|
re-mint gate, and the KL/support floors are all identical). Only the data source
|
||||||
|
(``synthetic``) and the model (``model``) are neural-specific.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import math
|
||||||
|
from dataclasses import dataclass, field, replace
|
||||||
|
from typing import Any, Mapping
|
||||||
|
|
||||||
|
from knowledge.config import GroundingCfg, MetricsCfg, RemintCfg, _sub
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class SyntheticCfg:
|
||||||
|
"""The fully-synthetic mode-truth and observation grammar.
|
||||||
|
|
||||||
|
The first seven fields are the Layer-1 ``TruthCfg`` knobs (they build ``p*`` over the
|
||||||
|
``K`` modes via ``knowledge.truth.make_true_distribution``). The remaining fields
|
||||||
|
define how a mode is rendered to a categorical token sequence: an *identity* segment
|
||||||
|
that encodes the mode losslessly (read by the exact oracle) followed by a *style*
|
||||||
|
segment of within-mode stochastic tokens (so a real generative model has a
|
||||||
|
distribution to learn, not just a lookup table).
|
||||||
|
"""
|
||||||
|
|
||||||
|
K: int
|
||||||
|
R: int = 1
|
||||||
|
tail: str = "zipf"
|
||||||
|
zipf_s: float = 1.1
|
||||||
|
tail_frac: float = 0.5
|
||||||
|
tail_threshold: float = 1e-3
|
||||||
|
init: str = "uniform" # initial p_0 over modes: {uniform, truth}
|
||||||
|
style_len: int = 4 # style-segment length (within-mode entropy)
|
||||||
|
style_vocab: int = 6 # style token alphabet size
|
||||||
|
id_base: int = 2 # identity segment encodes the mode in this base
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id_len(self) -> int:
|
||||||
|
"""Identity-segment length: fewest base-``id_base`` digits to index ``K`` modes."""
|
||||||
|
if self.K <= 1:
|
||||||
|
return 1
|
||||||
|
return max(1, math.ceil(math.log(self.K, self.id_base)))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vocab(self) -> int:
|
||||||
|
"""Token alphabet size (shared by identity and style segments)."""
|
||||||
|
return max(self.id_base, self.style_vocab)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def seq_len(self) -> int:
|
||||||
|
"""Total observation length in tokens."""
|
||||||
|
return self.id_len + self.style_len
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ModelCfg:
|
||||||
|
"""The generative learner. ``kind`` selects the architecture behind a thin adapter.
|
||||||
|
|
||||||
|
Neural hyperparameters are ignored by the ``histogram`` bridge model.
|
||||||
|
"""
|
||||||
|
|
||||||
|
kind: str = "histogram" # {histogram, rnn, vae, mlp}
|
||||||
|
hidden: int = 64
|
||||||
|
embed: int = 16
|
||||||
|
epochs: int = 30
|
||||||
|
lr: float = 1.0e-3
|
||||||
|
batch_size: int = 256
|
||||||
|
device: str = "auto" # {auto, cpu, cuda}
|
||||||
|
latent: int = 16 # VAE latent dimension (VAE only)
|
||||||
|
beta: float = 1.0 # VAE KL weight (VAE only)
|
||||||
|
# Samples used to estimate a neural model's mode distribution by generate-and-classify
|
||||||
|
# (ignored by the exact histogram bridge). Larger -> less measurement noise on p_hat.
|
||||||
|
n_eval: int = 8000
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class NeuralDynamicsCfg:
|
||||||
|
"""Generational dynamics: drift strength ``n`` + reused grounding/re-mint blocks."""
|
||||||
|
|
||||||
|
n: int = 4000 # pupil training-sample size (drift strength ~ 1/n)
|
||||||
|
grounding: GroundingCfg = field(default_factory=GroundingCfg)
|
||||||
|
remint: RemintCfg = field(default_factory=RemintCfg)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class NeuralLineageCfg:
|
||||||
|
"""A fully-resolved neural lineage configuration."""
|
||||||
|
|
||||||
|
synthetic: SyntheticCfg
|
||||||
|
model: ModelCfg = field(default_factory=ModelCfg)
|
||||||
|
dynamics: NeuralDynamicsCfg = field(default_factory=NeuralDynamicsCfg)
|
||||||
|
generations: int = 30
|
||||||
|
metrics: MetricsCfg = field(default_factory=MetricsCfg)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_dict(cfg: Mapping[str, Any]) -> "NeuralLineageCfg":
|
||||||
|
"""Build a validated NeuralLineageCfg from a nested mapping, filling defaults."""
|
||||||
|
if isinstance(cfg, NeuralLineageCfg):
|
||||||
|
return cfg
|
||||||
|
synthetic = _sub(cfg.get("synthetic", {}), SyntheticCfg)
|
||||||
|
model = _sub(cfg.get("model", {}), ModelCfg)
|
||||||
|
dyn_raw = dict(cfg.get("dynamics", {}))
|
||||||
|
dynamics = NeuralDynamicsCfg(
|
||||||
|
n=dyn_raw.get("n", NeuralDynamicsCfg.n),
|
||||||
|
grounding=_sub(dyn_raw.get("grounding", {}), GroundingCfg),
|
||||||
|
remint=_sub(dyn_raw.get("remint", {}), RemintCfg),
|
||||||
|
)
|
||||||
|
metrics = _sub(cfg.get("metrics", {}), MetricsCfg)
|
||||||
|
return NeuralLineageCfg(
|
||||||
|
synthetic=synthetic,
|
||||||
|
model=model,
|
||||||
|
dynamics=dynamics,
|
||||||
|
generations=int(cfg.get("generations", NeuralLineageCfg.generations)),
|
||||||
|
metrics=metrics,
|
||||||
|
)
|
||||||
|
|
||||||
|
def replace(self, **kw) -> "NeuralLineageCfg":
|
||||||
|
return replace(self, **kw)
|
||||||
77
src/neural/evaluate.py
Normal file
77
src/neural/evaluate.py
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
"""Metrics for a neural lineage — the *same* row schema as ``knowledge.lineage``.
|
||||||
|
|
||||||
|
``measure_metrics`` takes a model's oracle-measured mode distribution ``p_hat`` and emits a
|
||||||
|
row with exactly the columns Layer 1 logs per generation (``knowledge.lineage.record``),
|
||||||
|
computed with the *same* ``knowledge.metrics`` functions. Identical columns are what let a
|
||||||
|
neural collapse curve be plotted on top of an analytic one, and let the same figure and
|
||||||
|
analysis code (``knowledge.analysis``) run unchanged.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from knowledge.config import MetricsCfg
|
||||||
|
from knowledge.lineage import N_BANDS
|
||||||
|
from knowledge.metrics import (
|
||||||
|
forward_kl,
|
||||||
|
heterozygosity,
|
||||||
|
per_region,
|
||||||
|
support_size,
|
||||||
|
tail_band_metrics,
|
||||||
|
tail_mass,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def measure_metrics(p: np.ndarray, p_star_orig: np.ndarray, tail_mask: np.ndarray,
|
||||||
|
regions: np.ndarray, R: int, metrics_cfg: MetricsCfg) -> dict:
|
||||||
|
"""Compute every per-generation metric for a measured mode distribution.
|
||||||
|
|
||||||
|
Mirrors ``knowledge.lineage.record`` field-for-field. ``forward_kl`` and the tail set
|
||||||
|
are always measured against the *original* truth, so a re-minted lineage that has lost
|
||||||
|
tails is penalised exactly as in Layer 1's E6.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
p (np.ndarray): The model's measured mode distribution ``p_hat`` (length ``K``).
|
||||||
|
p_star_orig (np.ndarray): The original true distribution over modes.
|
||||||
|
tail_mask (np.ndarray): Boolean tail mask on the original truth.
|
||||||
|
regions (np.ndarray): Length-``K`` region index per mode.
|
||||||
|
R (int): Number of regions.
|
||||||
|
metrics_cfg (MetricsCfg): KL floor and support epsilon.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: One row of metrics (no ``generation``/label columns; the runner adds those).
|
||||||
|
"""
|
||||||
|
eps = metrics_cfg.support_eps
|
||||||
|
kl_floor = metrics_cfg.kl_floor
|
||||||
|
head_mask = ~tail_mask
|
||||||
|
n_tail = int(tail_mask.sum())
|
||||||
|
n_head = int(head_mask.sum())
|
||||||
|
|
||||||
|
row = {
|
||||||
|
"heterozygosity": heterozygosity(p),
|
||||||
|
"forward_kl": forward_kl(p_star_orig, p, kl_floor),
|
||||||
|
"tail_mass": tail_mass(p, tail_mask),
|
||||||
|
"support_size": support_size(p, eps),
|
||||||
|
"tail_support": int(np.sum(p[tail_mask] > eps)),
|
||||||
|
"head_support": int(np.sum(p[head_mask] > eps)),
|
||||||
|
"tail_frac_alive": (float(np.mean(p[tail_mask] > eps)) if n_tail else 0.0),
|
||||||
|
"head_frac_alive": (float(np.mean(p[head_mask] > eps)) if n_head else 0.0),
|
||||||
|
"tail_truth_mass_alive": (
|
||||||
|
float(p_star_orig[tail_mask][p[tail_mask] > eps].sum()
|
||||||
|
/ p_star_orig[tail_mask].sum()) if n_tail else 0.0),
|
||||||
|
}
|
||||||
|
if n_tail >= N_BANDS:
|
||||||
|
fa, _ = tail_band_metrics(p, p_star_orig, tail_mask, n_bands=N_BANDS, alive_eps=eps)
|
||||||
|
for b in range(N_BANDS):
|
||||||
|
row[f"band{b}_alive"] = fa[b]
|
||||||
|
if R > 1:
|
||||||
|
for r, v in per_region(heterozygosity, p, regions).items():
|
||||||
|
row[f"H_region_{r}"] = v
|
||||||
|
for r, v in per_region(tail_mass, p, regions, tail_mask).items():
|
||||||
|
row[f"tail_region_{r}"] = v
|
||||||
|
for r in range(R):
|
||||||
|
region_tail = (regions == r) & tail_mask
|
||||||
|
row[f"tailalive_region_{r}"] = (
|
||||||
|
float(np.mean(p[region_tail] > eps)) if region_tail.any() else 0.0)
|
||||||
|
return row
|
||||||
124
src/neural/experiment.py
Normal file
124
src/neural/experiment.py
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
"""Neural (Layer 1.5) experiment runner: sweep a grid x replicates, write artifacts.
|
||||||
|
|
||||||
|
Mirrors ``knowledge.experiment`` and reuses its sweep-expansion primitives
|
||||||
|
(``_apply_param`` — including the ``g -> m`` conversion — and ``_set_by_path``), its
|
||||||
|
provenance helpers, and its output contract (``save_artifacts``). Only the per-run call and
|
||||||
|
the config key set differ: a neural run trains generative models rather than resampling a
|
||||||
|
frequency vector, and its config groups are ``synthetic``/``model``/``dynamics``/... .
|
||||||
|
|
||||||
|
CLI: python -m neural.experiment configs/neural/N0.yaml
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import copy
|
||||||
|
import itertools
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from knowledge.experiment import _apply_param, save_artifacts
|
||||||
|
from knowledge.seeding import spawn_seeds
|
||||||
|
|
||||||
|
from .generation_loop import run_generative_lineage
|
||||||
|
|
||||||
|
# Config groups that make up a single neural lineage (everything else is experiment-level).
|
||||||
|
_NEURAL_KEYS = ("synthetic", "model", "dynamics", "generations", "metrics", "n_eval")
|
||||||
|
|
||||||
|
# Libraries recorded in the manifest on top of the Layer-1 core set (skipped if absent).
|
||||||
|
_EXTRA_LIBS = ("torch", "torchvision")
|
||||||
|
|
||||||
|
|
||||||
|
def expand_sweeps(cfg: dict) -> list[tuple[dict, dict]]:
|
||||||
|
"""Expand the sweep grid into (label, resolved_neural_cfg) pairs.
|
||||||
|
|
||||||
|
Identical semantics to ``knowledge.experiment.expand_sweeps`` (Cartesian product of the
|
||||||
|
declared ``{param, values}`` entries, reusing ``_apply_param`` for the ``g -> m`` and
|
||||||
|
``arm`` special cases) but assembling the base from the neural config groups.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[tuple[dict, dict]]: One (label-columns, neural-config) pair per grid point.
|
||||||
|
"""
|
||||||
|
base = {k: copy.deepcopy(cfg[k]) for k in _NEURAL_KEYS if k in cfg}
|
||||||
|
sweeps = cfg.get("sweep", [])
|
||||||
|
if isinstance(sweeps, dict):
|
||||||
|
sweeps = [sweeps]
|
||||||
|
if not sweeps:
|
||||||
|
return [({}, base)]
|
||||||
|
params = [s["param"] for s in sweeps]
|
||||||
|
value_lists = [list(s["values"]) for s in sweeps]
|
||||||
|
combos: list[tuple[dict, dict]] = []
|
||||||
|
for values in itertools.product(*value_lists):
|
||||||
|
lin = copy.deepcopy(base)
|
||||||
|
label: dict = {}
|
||||||
|
for param, val in zip(params, values):
|
||||||
|
label.update(_apply_param(lin, param, val))
|
||||||
|
combos.append((label, lin))
|
||||||
|
return combos
|
||||||
|
|
||||||
|
|
||||||
|
def run_experiment(cfg: dict) -> pd.DataFrame:
|
||||||
|
"""Run every grid point x every replicate; return long-form results.
|
||||||
|
|
||||||
|
Replicate seeds are derived once from the master seed and reused across grid points, so
|
||||||
|
comparisons across sweep values are paired (shared drift noise) — as in Layer 1.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cfg (dict): Parsed experiment YAML.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
pd.DataFrame: One row per (combo, replicate, generation).
|
||||||
|
"""
|
||||||
|
name = cfg["experiment"]
|
||||||
|
master = int(cfg["seed"])
|
||||||
|
n_rep = int(cfg["n_replicates"])
|
||||||
|
combos = expand_sweeps(cfg)
|
||||||
|
seeds = spawn_seeds(master, n_rep)
|
||||||
|
|
||||||
|
frames: list[pd.DataFrame] = []
|
||||||
|
for label, neural_cfg in combos:
|
||||||
|
for rep, ss in enumerate(seeds):
|
||||||
|
df = run_generative_lineage(neural_cfg, int(ss.generate_state(1)[0]))
|
||||||
|
for col, val in label.items():
|
||||||
|
df[col] = val
|
||||||
|
df["replicate"] = rep
|
||||||
|
frames.append(df)
|
||||||
|
out = pd.concat(frames, ignore_index=True)
|
||||||
|
out.insert(0, "experiment", name)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def run_and_save(config_path: str | Path) -> Path:
|
||||||
|
"""Load a neural experiment YAML, run it, and write artifacts. Returns the output dir."""
|
||||||
|
config_path = Path(config_path)
|
||||||
|
cfg = yaml.safe_load(config_path.read_text())
|
||||||
|
out_dir = Path(cfg.get("output", {}).get("dir", f"results/{cfg['experiment']}"))
|
||||||
|
kind = cfg.get("kind", "gen_lineage")
|
||||||
|
if kind == "recombination":
|
||||||
|
from .recombine import run_recombination # Stage C (N4); imported lazily
|
||||||
|
df = run_recombination(cfg)
|
||||||
|
grid = None
|
||||||
|
elif kind == "gen_lineage":
|
||||||
|
df = run_experiment(cfg)
|
||||||
|
grid = [{"label": label, "neural_cfg": c} for label, c in expand_sweeps(cfg)]
|
||||||
|
else:
|
||||||
|
raise ValueError(f"unknown neural experiment kind {kind!r}")
|
||||||
|
model_kind = cfg.get("model", {}).get("kind", "histogram")
|
||||||
|
save_artifacts(cfg, df, out_dir, extra_libs=_EXTRA_LIBS,
|
||||||
|
extra_manifest={"layer": "1.5", "model_kind": model_kind}, grid=grid)
|
||||||
|
return out_dir
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: list[str] | None = None) -> None:
|
||||||
|
parser = argparse.ArgumentParser(description="Run a Layer-1.5 neural experiment from a YAML config.")
|
||||||
|
parser.add_argument("config", help="Path to configs/neural/NX.yaml")
|
||||||
|
args = parser.parse_args(argv)
|
||||||
|
out_dir = run_and_save(args.config)
|
||||||
|
print(f"wrote artifacts to {out_dir}/")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
113
src/neural/generation_loop.py
Normal file
113
src/neural/generation_loop.py
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
"""The neural analogue of ``knowledge.lineage.run_lineage``.
|
||||||
|
|
||||||
|
Runs ``T`` generations of *train-a-model-on-the-previous-model's-samples*, the neural
|
||||||
|
image of the Wright-Fisher generational step. Each generation the pupil is trained on a
|
||||||
|
pool of (i) ``n`` observations drawn from the parent model (drift) and (ii) ``m`` fresh
|
||||||
|
observations drawn from the grounding reference (immigration, ``g = m/(n+m)``), then its
|
||||||
|
oracle-measured mode distribution is logged with the *same* metric schema Layer 1 uses.
|
||||||
|
Grounding structure (proportional / uniform / matched over regions) and the re-mint gate
|
||||||
|
reuse ``knowledge.step`` and mirror ``run_lineage`` exactly, so a histogram-model lineage
|
||||||
|
reproduces the analytic core and a neural-model lineage tests whether the same signs hold
|
||||||
|
in real weights.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Mapping
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from knowledge.metrics import heterozygosity
|
||||||
|
from knowledge.step import allocate_m, structured_multinomial
|
||||||
|
from knowledge.truth import uniform_init
|
||||||
|
|
||||||
|
from .config import NeuralLineageCfg
|
||||||
|
from .evaluate import measure_metrics
|
||||||
|
from .models import make_model
|
||||||
|
from .oracle import ExactOracle
|
||||||
|
from .synthetic import id_codewords, make_mode_truth, render_modes
|
||||||
|
|
||||||
|
|
||||||
|
def _counts_to_observations(counts: np.ndarray, cfg, rng, codewords) -> np.ndarray:
|
||||||
|
"""Expand a per-mode count vector into rendered token sequences."""
|
||||||
|
modes = np.repeat(np.arange(counts.size), counts)
|
||||||
|
return render_modes(modes, cfg, rng, codewords)
|
||||||
|
|
||||||
|
|
||||||
|
def run_generative_lineage(cfg: Mapping[str, Any] | NeuralLineageCfg,
|
||||||
|
seed: int) -> pd.DataFrame:
|
||||||
|
"""Run one neural lineage and return per-generation metrics.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cfg (Mapping | NeuralLineageCfg): Resolved neural-lineage configuration.
|
||||||
|
seed (int): Seed for this replicate; the run is a pure function of (cfg, seed) for
|
||||||
|
the histogram model (statistically reproducible for torch models).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
pd.DataFrame: One row per generation 0..T with the same metric columns as
|
||||||
|
``knowledge.lineage.run_lineage``.
|
||||||
|
"""
|
||||||
|
cfg = NeuralLineageCfg.from_dict(cfg)
|
||||||
|
syn = cfg.synthetic
|
||||||
|
td = make_mode_truth(syn)
|
||||||
|
p_star_orig = td.p_star # forward_kl is always vs the original truth
|
||||||
|
regions = td.regions
|
||||||
|
tail_mask = td.tail_mask
|
||||||
|
R = syn.R
|
||||||
|
|
||||||
|
rng = np.random.default_rng(seed)
|
||||||
|
oracle = ExactOracle(syn)
|
||||||
|
codewords = id_codewords(syn)
|
||||||
|
|
||||||
|
# Initial distribution over modes (exact, like Layer 1).
|
||||||
|
if syn.init == "uniform":
|
||||||
|
p0 = uniform_init(syn.K)
|
||||||
|
elif syn.init == "truth":
|
||||||
|
p0 = p_star_orig.copy()
|
||||||
|
else:
|
||||||
|
raise ValueError(f"unknown init {syn.init!r} (expected uniform|truth)")
|
||||||
|
|
||||||
|
# Grounding wiring (reused verbatim from Layer 1).
|
||||||
|
grounding = cfg.dynamics.grounding
|
||||||
|
exercised = np.asarray(grounding.exercised) if grounding.exercised is not None else None
|
||||||
|
m_vector = allocate_m(grounding.m, R, grounding.policy, exercised)
|
||||||
|
p_star_eff = p_star_orig.copy() # grounding reference; may be re-minted (N6)
|
||||||
|
remint = cfg.dynamics.remint
|
||||||
|
n = cfg.dynamics.n
|
||||||
|
|
||||||
|
model = make_model(cfg.model, syn, oracle)
|
||||||
|
model.initialise(p0, rng)
|
||||||
|
|
||||||
|
rows: list[dict] = []
|
||||||
|
|
||||||
|
def record(t: int, p: np.ndarray) -> None:
|
||||||
|
row = {"generation": t}
|
||||||
|
row.update(measure_metrics(p, p_star_orig, tail_mask, regions, R, cfg.metrics))
|
||||||
|
rows.append(row)
|
||||||
|
|
||||||
|
record(0, model.mode_distribution(rng))
|
||||||
|
|
||||||
|
for t in range(1, cfg.generations + 1):
|
||||||
|
X_syn = model.sample(n, rng) # drift: n from the parent
|
||||||
|
if m_vector is not None: # immigration: m grounded samples
|
||||||
|
counts_real = structured_multinomial(
|
||||||
|
m_vector, p_star_eff, regions, grounding.policy, rng)
|
||||||
|
X_real = _counts_to_observations(counts_real, syn, rng, codewords)
|
||||||
|
pool = np.concatenate([X_syn, X_real], axis=0)
|
||||||
|
else:
|
||||||
|
pool = X_syn
|
||||||
|
|
||||||
|
pupil = make_model(cfg.model, syn, oracle)
|
||||||
|
pupil.fit(pool, rng)
|
||||||
|
model = pupil
|
||||||
|
p = model.mode_distribution(rng)
|
||||||
|
|
||||||
|
if remint.enabled and remint.period and t % remint.period == 0:
|
||||||
|
# Founder event: current distribution becomes the new grounding reference and
|
||||||
|
# the original truth is discarded for grounding. Gated on diversity (N6).
|
||||||
|
if remint.H_gate is None or heterozygosity(p) >= remint.H_gate:
|
||||||
|
p_star_eff = p.copy()
|
||||||
|
record(t, p)
|
||||||
|
|
||||||
|
return pd.DataFrame(rows)
|
||||||
119
src/neural/models.py
Normal file
119
src/neural/models.py
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
"""Generative models behind a thin adapter, so architecture is a config switch.
|
||||||
|
|
||||||
|
Every model implements the same three-method protocol: ``fit`` on a batch of token
|
||||||
|
sequences, ``sample`` fresh token sequences, and report its ``mode_distribution`` (the
|
||||||
|
model's ``p_t``). Keeping the interface identical is what makes "collapse is
|
||||||
|
architecture-general" (experiment N5) a single sweep over ``model.kind``.
|
||||||
|
|
||||||
|
``HistogramModel`` is the bridge: its ``fit`` is a maximum-likelihood mode histogram and
|
||||||
|
its ``sample`` is a multinomial draw, so a lineage of histogram models is *exactly*
|
||||||
|
neutral Wright-Fisher drift with immigration — the analytic core in disguise. The
|
||||||
|
torch-backed RNN/VAE/MLP models are added in Stage C and reuse this same protocol.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Protocol, runtime_checkable
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from .config import ModelCfg, SyntheticCfg
|
||||||
|
from .oracle import Oracle, measure_distribution
|
||||||
|
from .synthetic import id_codewords, render_modes
|
||||||
|
|
||||||
|
|
||||||
|
@runtime_checkable
|
||||||
|
class GenerativeModel(Protocol):
|
||||||
|
"""A learner of ``p(x)`` over the synthetic observation space."""
|
||||||
|
|
||||||
|
def initialise(self, p0: np.ndarray, rng: np.random.Generator) -> None:
|
||||||
|
"""Initialise generation 0 to represent the mode distribution ``p0``.
|
||||||
|
|
||||||
|
The histogram bridge sets ``p0`` exactly (matching Layer 1's exact ``p_0`` start);
|
||||||
|
a neural model trains on a sample drawn from ``p0`` (its gen-0 fidelity is checked
|
||||||
|
by the Stage-C fidelity gate).
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def fit(self, X: np.ndarray, rng: np.random.Generator) -> None:
|
||||||
|
"""Train (from scratch) on a batch of token sequences ``X``."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def sample(self, n: int, rng: np.random.Generator) -> np.ndarray:
|
||||||
|
"""Draw ``n`` fresh token sequences of shape ``(n, seq_len)``."""
|
||||||
|
...
|
||||||
|
|
||||||
|
def mode_distribution(self, rng: np.random.Generator) -> np.ndarray:
|
||||||
|
"""Return the model's length-``K`` distribution over modes (its ``p_t``)."""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class HistogramModel:
|
||||||
|
"""MLE mode-histogram generator — reduces Layer 1.5 exactly to Layer 1.
|
||||||
|
|
||||||
|
``fit`` counts oracle-labelled modes in the training pool and stores the empirical
|
||||||
|
distribution; ``sample`` draws modes multinomially and renders them; the stored
|
||||||
|
distribution *is* the model's ``p_t`` (read exactly, no eval-sampling noise). Composed
|
||||||
|
over generations this is neutral Wright-Fisher drift with immigration.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cfg (SyntheticCfg): The synthetic grammar (for ``K`` and rendering).
|
||||||
|
oracle (Oracle): The mode adjudicator used to label the training pool.
|
||||||
|
model_cfg (ModelCfg): Present for interface symmetry; unused by the histogram.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, cfg: SyntheticCfg, oracle: Oracle,
|
||||||
|
model_cfg: ModelCfg | None = None) -> None:
|
||||||
|
self.cfg = cfg
|
||||||
|
self.oracle = oracle
|
||||||
|
self._codewords = id_codewords(cfg)
|
||||||
|
self._p: np.ndarray | None = None
|
||||||
|
|
||||||
|
def initialise(self, p0: np.ndarray, rng: np.random.Generator) -> None:
|
||||||
|
"""Set the stored distribution to ``p0`` exactly (no gen-0 sampling noise)."""
|
||||||
|
p0 = np.asarray(p0, dtype=float)
|
||||||
|
self._p = p0 / p0.sum()
|
||||||
|
|
||||||
|
def fit(self, X: np.ndarray, rng: np.random.Generator) -> None:
|
||||||
|
"""Store the empirical mode distribution of the (oracle-labelled) pool ``X``."""
|
||||||
|
self._p = measure_distribution(X, self.oracle, self.cfg.K)
|
||||||
|
|
||||||
|
def sample(self, n: int, rng: np.random.Generator) -> np.ndarray:
|
||||||
|
"""Draw ``n`` observations whose modes follow the stored distribution."""
|
||||||
|
if self._p is None:
|
||||||
|
raise RuntimeError("HistogramModel.sample called before fit")
|
||||||
|
counts = rng.multinomial(n, self._p)
|
||||||
|
modes = np.repeat(np.arange(self.cfg.K), counts)
|
||||||
|
return render_modes(modes, self.cfg, rng, self._codewords)
|
||||||
|
|
||||||
|
def mode_distribution(self, rng: np.random.Generator) -> np.ndarray:
|
||||||
|
"""Return the stored mode distribution (exact; no eval sampling)."""
|
||||||
|
if self._p is None:
|
||||||
|
raise RuntimeError("HistogramModel.mode_distribution called before fit")
|
||||||
|
return self._p.copy()
|
||||||
|
|
||||||
|
|
||||||
|
def make_model(model_cfg: ModelCfg, cfg: SyntheticCfg, oracle: Oracle) -> GenerativeModel:
|
||||||
|
"""Construct a generative model of the requested ``kind``.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model_cfg (ModelCfg): Selects the architecture and its hyperparameters.
|
||||||
|
cfg (SyntheticCfg): The synthetic grammar.
|
||||||
|
oracle (Oracle): The mode adjudicator (needed by the histogram bridge; the neural
|
||||||
|
models estimate their mode distribution by generate-and-classify).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
GenerativeModel: A fresh, untrained model.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If ``kind`` is unknown.
|
||||||
|
"""
|
||||||
|
kind = model_cfg.kind
|
||||||
|
if kind == "histogram":
|
||||||
|
return HistogramModel(cfg, oracle, model_cfg)
|
||||||
|
if kind in ("rnn", "vae", "mlp"):
|
||||||
|
# Torch-backed models arrive in Stage C; imported lazily so Stages A-B need no GPU.
|
||||||
|
from .torch_models import make_torch_model # noqa: PLC0415
|
||||||
|
|
||||||
|
return make_torch_model(model_cfg, cfg, oracle)
|
||||||
|
raise ValueError(f"unknown model kind {kind!r} (expected histogram|rnn|vae|mlp)")
|
||||||
82
src/neural/oracle.py
Normal file
82
src/neural/oracle.py
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
"""The oracle — the neural analogue of Layer 1's "reality's no".
|
||||||
|
|
||||||
|
An oracle maps an observation to the mode it belongs to. For the fully-synthetic sandbox
|
||||||
|
the oracle is **exact** (it decodes the lossless identity segment), so the measured mode
|
||||||
|
distribution ``p_hat`` carries zero measurement noise — this is what lets a trained model's
|
||||||
|
collapse be read directly against the known ``p*``. (The MNIST tier will add a
|
||||||
|
``ClassifierOracle`` wrapping a frozen network plus its confusion matrix; that arrives with
|
||||||
|
Stage C and is confirmation-only.)
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Protocol, runtime_checkable
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from .config import SyntheticCfg
|
||||||
|
|
||||||
|
|
||||||
|
@runtime_checkable
|
||||||
|
class Oracle(Protocol):
|
||||||
|
"""Adjudicates which mode an observation belongs to."""
|
||||||
|
|
||||||
|
def classify(self, X: np.ndarray) -> np.ndarray:
|
||||||
|
"""Return the length-``n`` mode index for each row of ``X``."""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class ExactOracle:
|
||||||
|
"""Zero-error oracle for the synthetic sandbox: decodes the identity segment.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cfg (SyntheticCfg): The synthetic configuration whose grammar produced ``X``.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, cfg: SyntheticCfg) -> None:
|
||||||
|
self.cfg = cfg
|
||||||
|
self.id_len = cfg.id_len
|
||||||
|
self.base = cfg.id_base
|
||||||
|
self.K = cfg.K
|
||||||
|
# Positional weights for base-`base` decoding, most-significant digit first.
|
||||||
|
self._weights = self.base ** np.arange(self.id_len - 1, -1, -1, dtype=np.int64)
|
||||||
|
|
||||||
|
def classify(self, X: np.ndarray) -> np.ndarray:
|
||||||
|
"""Decode mode indices from the identity segment of each observation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
X (np.ndarray): Token sequences of shape ``(n, seq_len)``.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
np.ndarray: Length-``n`` decoded indices. Grammar-valid data always decodes into
|
||||||
|
``[0, K)``; a *neural* model may emit an invalid codeword that decodes to
|
||||||
|
``>= K`` — such samples are dropped by :func:`measure_distribution` rather than
|
||||||
|
being clipped onto a real mode (which would bias ``p_hat``).
|
||||||
|
"""
|
||||||
|
X = np.asarray(X, dtype=np.int64)
|
||||||
|
ident = X[:, : self.id_len]
|
||||||
|
return ident @ self._weights
|
||||||
|
|
||||||
|
|
||||||
|
def measure_distribution(X: np.ndarray, oracle: Oracle, K: int) -> np.ndarray:
|
||||||
|
"""Measure the empirical mode distribution ``p_hat`` of a sample.
|
||||||
|
|
||||||
|
This is the neural readout of ``p_t``: classify every observation and normalise the
|
||||||
|
mode histogram. Modes absent from ``X`` receive zero mass (support shrinks exactly as
|
||||||
|
in Layer 1). Invalid codewords (decoded index ``>= K``, only producible by a neural
|
||||||
|
model) are dropped, so ``p_hat`` is renormalised over grammar-valid samples.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
X (np.ndarray): Token sequences of shape ``(n, seq_len)``.
|
||||||
|
oracle (Oracle): The mode adjudicator.
|
||||||
|
K (int): Number of modes.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
np.ndarray: Length-``K`` probability vector summing to 1.
|
||||||
|
"""
|
||||||
|
modes = oracle.classify(X)
|
||||||
|
counts = np.bincount(modes, minlength=K)[:K].astype(float)
|
||||||
|
total = counts.sum()
|
||||||
|
if total <= 0:
|
||||||
|
raise ValueError("measure_distribution received an empty sample")
|
||||||
|
return counts / total
|
||||||
109
src/neural/synthetic.py
Normal file
109
src/neural/synthetic.py
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
"""The fully-synthetic sandbox: a known ``p*`` over modes + a lossless observation grammar.
|
||||||
|
|
||||||
|
The mode-truth (``p*``, regions, tail mask) comes straight from Layer 1's
|
||||||
|
``knowledge.truth.make_true_distribution`` — so "mode", "region", and "tail" are *the same
|
||||||
|
objects* as in the analytic core. Each mode is rendered to a categorical token sequence:
|
||||||
|
|
||||||
|
* an **identity** segment of ``id_len`` base-``id_base`` digits that encodes the mode
|
||||||
|
index exactly (the exact oracle reads these back with zero error), and
|
||||||
|
* a **style** segment of ``style_len`` tokens drawn uniformly at random, giving genuine
|
||||||
|
within-mode entropy so a real generative model must learn a *distribution* ``p(x|mode)``
|
||||||
|
rather than memorise ``K`` fixed strings.
|
||||||
|
|
||||||
|
Because the identity segment is lossless, the measured mode distribution ``p_hat`` is a
|
||||||
|
noise-free readout of the model's output — the property that lets the histogram model
|
||||||
|
reduce this layer exactly to Wright-Fisher drift.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from knowledge.truth import TrueDist, make_true_distribution
|
||||||
|
|
||||||
|
from .config import SyntheticCfg
|
||||||
|
|
||||||
|
|
||||||
|
def make_mode_truth(cfg: SyntheticCfg) -> TrueDist:
|
||||||
|
"""Build the true distribution over modes (thin wrapper over Layer 1's truth).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cfg (SyntheticCfg): The synthetic configuration (its first seven fields are the
|
||||||
|
Layer-1 ``TruthCfg`` knobs).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
TrueDist: ``p_star`` (length ``K``), ``regions``, and ``tail_mask`` over modes.
|
||||||
|
"""
|
||||||
|
return make_true_distribution(
|
||||||
|
cfg.K, cfg.R, cfg.tail, cfg.tail_frac, cfg.zipf_s, 0,
|
||||||
|
tail_threshold=cfg.tail_threshold,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def id_codewords(cfg: SyntheticCfg) -> np.ndarray:
|
||||||
|
"""Return the ``(K, id_len)`` matrix of base-``id_base`` identity codewords.
|
||||||
|
|
||||||
|
Codeword of mode ``k`` is ``k`` written in base ``id_base``, most-significant digit
|
||||||
|
first, zero-padded to ``id_len``. Deterministic and invertible.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cfg (SyntheticCfg): The synthetic configuration.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
np.ndarray: Integer array of shape ``(K, id_len)`` with tokens in
|
||||||
|
``[0, id_base)``.
|
||||||
|
"""
|
||||||
|
k = np.arange(cfg.K, dtype=np.int64)
|
||||||
|
id_len, base = cfg.id_len, cfg.id_base
|
||||||
|
digits = np.empty((cfg.K, id_len), dtype=np.int64)
|
||||||
|
for pos in range(id_len - 1, -1, -1): # least-significant digit last
|
||||||
|
digits[:, pos] = k % base
|
||||||
|
k //= base
|
||||||
|
return digits
|
||||||
|
|
||||||
|
|
||||||
|
def render_modes(modes: np.ndarray, cfg: SyntheticCfg, rng: np.random.Generator,
|
||||||
|
codewords: np.ndarray | None = None) -> np.ndarray:
|
||||||
|
"""Render an array of mode indices to token sequences.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
modes (np.ndarray): Length-``n`` integer array of mode indices in ``[0, K)``.
|
||||||
|
cfg (SyntheticCfg): The synthetic configuration.
|
||||||
|
rng (np.random.Generator): Random source for the style segment.
|
||||||
|
codewords (np.ndarray | None): Optional precomputed identity codewords.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
np.ndarray: Integer array of shape ``(n, seq_len)`` — identity segment followed by
|
||||||
|
a freshly-sampled style segment.
|
||||||
|
"""
|
||||||
|
modes = np.asarray(modes, dtype=np.int64)
|
||||||
|
if codewords is None:
|
||||||
|
codewords = id_codewords(cfg)
|
||||||
|
ident = codewords[modes] # (n, id_len)
|
||||||
|
style = rng.integers(0, cfg.style_vocab, size=(modes.shape[0], cfg.style_len))
|
||||||
|
return np.concatenate([ident, style], axis=1)
|
||||||
|
|
||||||
|
|
||||||
|
def sample_synthetic(p_over_modes: np.ndarray, n: int, cfg: SyntheticCfg,
|
||||||
|
rng: np.random.Generator,
|
||||||
|
codewords: np.ndarray | None = None) -> tuple[np.ndarray, np.ndarray]:
|
||||||
|
"""Draw ``n`` observations whose modes follow ``p_over_modes``.
|
||||||
|
|
||||||
|
This is the *grounding* generator (draw from a fixed distribution and render) and the
|
||||||
|
reference sampler used to seed generation 0.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
p_over_modes (np.ndarray): Distribution over the ``K`` modes to sample from.
|
||||||
|
n (int): Number of observations.
|
||||||
|
cfg (SyntheticCfg): The synthetic configuration.
|
||||||
|
rng (np.random.Generator): Random source.
|
||||||
|
codewords (np.ndarray | None): Optional precomputed identity codewords.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple[np.ndarray, np.ndarray]: ``(X, modes)`` — token sequences of shape
|
||||||
|
``(n, seq_len)`` and the length-``n`` true mode indices.
|
||||||
|
"""
|
||||||
|
p = np.asarray(p_over_modes, dtype=float)
|
||||||
|
modes = rng.choice(cfg.K, size=n, p=p / p.sum())
|
||||||
|
X = render_modes(modes, cfg, rng, codewords)
|
||||||
|
return X, modes
|
||||||
109
src/neural/torch_mlp.py
Normal file
109
src/neural/torch_mlp.py
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
"""Autoregressive MLP generative model (Stage C, for the N5 architecture-generality axis).
|
||||||
|
|
||||||
|
A causal feed-forward next-token model: token ``i`` is predicted from the concatenated
|
||||||
|
(causally-masked) embeddings of all earlier tokens. Deliberately a *different* inductive
|
||||||
|
bias from the GRU — if collapse appears here too, it is a property of the transmission
|
||||||
|
operator, not of any one architecture.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from .config import ModelCfg, SyntheticCfg
|
||||||
|
from .oracle import Oracle
|
||||||
|
from .torch_models import _BaseTorchGenerator
|
||||||
|
from .train import device_generator, seed_everything
|
||||||
|
|
||||||
|
|
||||||
|
def _make_mlp_net(V: int, L: int, embed: int, hidden: int):
|
||||||
|
import torch
|
||||||
|
import torch.nn as nn
|
||||||
|
|
||||||
|
class MLPNet(nn.Module):
|
||||||
|
"""Predict every position from a causally-masked flatten of prior embeddings."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.V, self.L, self.E = V, L, embed
|
||||||
|
self.bos = V
|
||||||
|
self.embed = nn.Embedding(V + 1, embed)
|
||||||
|
self.net = nn.Sequential(
|
||||||
|
nn.Linear(L * embed, hidden), nn.ReLU(),
|
||||||
|
nn.Linear(hidden, hidden), nn.ReLU(),
|
||||||
|
nn.Linear(hidden, V),
|
||||||
|
)
|
||||||
|
# lower-triangular INCLUSIVE mask over input positions: position i sees inputs
|
||||||
|
# 0..i (the input is already shifted by one, so this is strictly causal on x).
|
||||||
|
mask = torch.tril(torch.ones(L, L))
|
||||||
|
self.register_buffer("mask", mask)
|
||||||
|
|
||||||
|
def _context(self, inp): # inp: (B, L) input tokens
|
||||||
|
B = inp.shape[0]
|
||||||
|
emb = self.embed(inp) # (B, L, E)
|
||||||
|
m = self.mask.to(emb.dtype) # (L, L)
|
||||||
|
# ctx[b, i] = concat_j ( emb[b, j] * mask[i, j] ) -> (B, L, L*E)
|
||||||
|
ctx = emb.unsqueeze(1) * m.unsqueeze(0).unsqueeze(-1) # (B, L, L, E)
|
||||||
|
return ctx.reshape(B, self.L, self.L * self.E)
|
||||||
|
|
||||||
|
def forward(self, x): # x: (B, L) targets
|
||||||
|
B = x.shape[0]
|
||||||
|
bos = torch.full((B, 1), self.bos, dtype=torch.long, device=x.device)
|
||||||
|
inp = torch.cat([bos, x[:, :-1]], dim=1)
|
||||||
|
ctx = self._context(inp)
|
||||||
|
return self.net(ctx) # (B, L, V)
|
||||||
|
|
||||||
|
def step_logits(self, prefix): # prefix: (B, pos) tokens so far
|
||||||
|
"""Logits for the next token given the tokens generated so far."""
|
||||||
|
B, pos = prefix.shape
|
||||||
|
bos = torch.full((B, 1), self.bos, dtype=torch.long, device=prefix.device)
|
||||||
|
inp = torch.cat([bos, prefix], dim=1)[:, : self.L] # (B, <=L)
|
||||||
|
if inp.shape[1] < self.L:
|
||||||
|
pad = torch.zeros((B, self.L - inp.shape[1]), dtype=torch.long,
|
||||||
|
device=prefix.device)
|
||||||
|
inp = torch.cat([inp, pad], dim=1)
|
||||||
|
ctx = self._context(inp) # (B, L, L*E)
|
||||||
|
return self.net(ctx[:, pos, :]) # logits at position `pos`
|
||||||
|
|
||||||
|
return MLPNet()
|
||||||
|
|
||||||
|
|
||||||
|
class MLPGenerator(_BaseTorchGenerator):
|
||||||
|
"""Autoregressive feed-forward generative model over token sequences."""
|
||||||
|
|
||||||
|
def fit(self, X: np.ndarray, rng: np.random.Generator) -> None:
|
||||||
|
import torch
|
||||||
|
|
||||||
|
g = seed_everything(int(rng.integers(2 ** 31)))
|
||||||
|
net = _make_mlp_net(self.V, self.L, self.mcfg.embed, self.mcfg.hidden).to(self.device)
|
||||||
|
net.train()
|
||||||
|
opt = torch.optim.Adam(net.parameters(), lr=self.mcfg.lr)
|
||||||
|
loss_fn = torch.nn.CrossEntropyLoss()
|
||||||
|
data = torch.as_tensor(np.asarray(X), dtype=torch.long, device=self.device)
|
||||||
|
n, bs = data.shape[0], self.mcfg.batch_size
|
||||||
|
for _ in range(self.mcfg.epochs):
|
||||||
|
perm = torch.randperm(n, generator=g).to(self.device)
|
||||||
|
for i in range(0, n, bs):
|
||||||
|
batch = data[perm[i:i + bs]]
|
||||||
|
logits = net(batch)
|
||||||
|
loss = loss_fn(logits.reshape(-1, self.V), batch.reshape(-1))
|
||||||
|
opt.zero_grad()
|
||||||
|
loss.backward()
|
||||||
|
opt.step()
|
||||||
|
net.eval()
|
||||||
|
self.net = net
|
||||||
|
|
||||||
|
def sample(self, n: int, rng: np.random.Generator) -> np.ndarray:
|
||||||
|
import torch
|
||||||
|
|
||||||
|
if self.net is None:
|
||||||
|
raise RuntimeError("MLPGenerator.sample called before fit")
|
||||||
|
g = device_generator(int(rng.integers(2 ** 31)), self.device)
|
||||||
|
prefix = torch.empty((n, 0), dtype=torch.long, device=self.device)
|
||||||
|
with torch.no_grad():
|
||||||
|
for pos in range(self.L):
|
||||||
|
logits = self.net.step_logits(prefix)
|
||||||
|
probs = torch.softmax(logits, dim=-1)
|
||||||
|
tok = torch.multinomial(probs, 1, generator=g)
|
||||||
|
prefix = torch.cat([prefix, tok], dim=1)
|
||||||
|
return prefix.cpu().numpy()
|
||||||
146
src/neural/torch_models.py
Normal file
146
src/neural/torch_models.py
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
"""Torch-backed generative models over the synthetic token grammar (Stage C).
|
||||||
|
|
||||||
|
Each model implements the same ``GenerativeModel`` protocol as the histogram bridge
|
||||||
|
(``initialise`` / ``fit`` / ``sample`` / ``mode_distribution``), so a lineage is
|
||||||
|
architecture-agnostic and N5 is a single sweep over ``model.kind``. Unlike the histogram
|
||||||
|
model, a neural model's ``mode_distribution`` is *estimated* by generate-and-classify
|
||||||
|
(``n_eval`` samples), which is the honest, slightly-noisy neural readout of ``p_t``.
|
||||||
|
|
||||||
|
Implemented so far: ``RNNGenerator`` (autoregressive GRU). ``VAEGenerator`` and
|
||||||
|
``MLPGenerator`` follow and reuse the shared measure/initialise helpers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from .config import ModelCfg, SyntheticCfg
|
||||||
|
from .oracle import Oracle, measure_distribution
|
||||||
|
from .synthetic import sample_synthetic
|
||||||
|
from .train import device_generator, resolve_device, seed_everything, set_determinism
|
||||||
|
|
||||||
|
|
||||||
|
def _measure(model, rng: np.random.Generator, oracle: Oracle, K: int, n_eval: int) -> np.ndarray:
|
||||||
|
"""Estimate a model's mode distribution by generate-and-classify."""
|
||||||
|
X = model.sample(n_eval, rng)
|
||||||
|
return measure_distribution(X, oracle, K)
|
||||||
|
|
||||||
|
|
||||||
|
class _BaseTorchGenerator:
|
||||||
|
"""Shared plumbing: device, gen-0 initialisation, and mode measurement."""
|
||||||
|
|
||||||
|
def __init__(self, cfg: SyntheticCfg, model_cfg: ModelCfg, oracle: Oracle) -> None:
|
||||||
|
self.cfg = cfg
|
||||||
|
self.mcfg = model_cfg
|
||||||
|
self.oracle = oracle
|
||||||
|
self.device = resolve_device(model_cfg.device)
|
||||||
|
self.V = cfg.vocab
|
||||||
|
self.L = cfg.seq_len
|
||||||
|
self.net = None
|
||||||
|
set_determinism()
|
||||||
|
|
||||||
|
def initialise(self, p0: np.ndarray, rng: np.random.Generator) -> None:
|
||||||
|
"""Train generation 0 on a sample drawn from ``p0`` (fidelity-gated in Stage C)."""
|
||||||
|
n_init = max(self.mcfg.n_eval, 4000)
|
||||||
|
X, _ = sample_synthetic(p0, n_init, self.cfg, rng)
|
||||||
|
self.fit(X, rng)
|
||||||
|
|
||||||
|
def mode_distribution(self, rng: np.random.Generator) -> np.ndarray:
|
||||||
|
return _measure(self, rng, self.oracle, self.cfg.K, self.mcfg.n_eval)
|
||||||
|
|
||||||
|
|
||||||
|
# --- autoregressive GRU -----------------------------------------------------------------
|
||||||
|
|
||||||
|
def _make_ar_net(V: int, embed: int, hidden: int):
|
||||||
|
"""Build an autoregressive GRU next-token network (built lazily to avoid a torch import
|
||||||
|
at module load)."""
|
||||||
|
import torch.nn as nn
|
||||||
|
|
||||||
|
class ARNet(nn.Module):
|
||||||
|
"""Predict token ``i`` from tokens ``0..i-1`` via a GRU (BOS-prefixed)."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.bos = V # extra input id for the start token
|
||||||
|
self.embed = nn.Embedding(V + 1, embed)
|
||||||
|
self.gru = nn.GRU(embed, hidden, batch_first=True)
|
||||||
|
self.out = nn.Linear(hidden, V)
|
||||||
|
|
||||||
|
def forward(self, x): # x: (B, L) target tokens
|
||||||
|
import torch
|
||||||
|
|
||||||
|
B = x.shape[0]
|
||||||
|
bos = torch.full((B, 1), self.bos, dtype=torch.long, device=x.device)
|
||||||
|
inp = torch.cat([bos, x[:, :-1]], dim=1) # teacher forcing
|
||||||
|
h, _ = self.gru(self.embed(inp))
|
||||||
|
return self.out(h) # (B, L, V)
|
||||||
|
|
||||||
|
return ARNet()
|
||||||
|
|
||||||
|
|
||||||
|
class RNNGenerator(_BaseTorchGenerator):
|
||||||
|
"""Autoregressive GRU generative model over token sequences."""
|
||||||
|
|
||||||
|
def _train_net(self, X, rng: np.random.Generator):
|
||||||
|
import torch
|
||||||
|
|
||||||
|
g = seed_everything(int(rng.integers(2 ** 31)))
|
||||||
|
net = _make_ar_net(self.V, self.mcfg.embed, self.mcfg.hidden).to(self.device)
|
||||||
|
net.train()
|
||||||
|
opt = torch.optim.Adam(net.parameters(), lr=self.mcfg.lr)
|
||||||
|
loss_fn = torch.nn.CrossEntropyLoss()
|
||||||
|
data = torch.as_tensor(np.asarray(X), dtype=torch.long, device=self.device)
|
||||||
|
n = data.shape[0]
|
||||||
|
bs = self.mcfg.batch_size
|
||||||
|
for _ in range(self.mcfg.epochs):
|
||||||
|
perm = torch.randperm(n, generator=g).to(self.device)
|
||||||
|
for i in range(0, n, bs):
|
||||||
|
idx = perm[i:i + bs]
|
||||||
|
batch = data[idx]
|
||||||
|
logits = net(batch) # (b, L, V)
|
||||||
|
loss = loss_fn(logits.reshape(-1, self.V), batch.reshape(-1))
|
||||||
|
opt.zero_grad()
|
||||||
|
loss.backward()
|
||||||
|
opt.step()
|
||||||
|
net.eval()
|
||||||
|
return net
|
||||||
|
|
||||||
|
def fit(self, X: np.ndarray, rng: np.random.Generator) -> None:
|
||||||
|
self.net = self._train_net(X, rng)
|
||||||
|
|
||||||
|
def sample(self, n: int, rng: np.random.Generator) -> np.ndarray:
|
||||||
|
import torch
|
||||||
|
|
||||||
|
if self.net is None:
|
||||||
|
raise RuntimeError("RNNGenerator.sample called before fit")
|
||||||
|
g = device_generator(int(rng.integers(2 ** 31)), self.device)
|
||||||
|
out = torch.empty((n, self.L), dtype=torch.long, device=self.device)
|
||||||
|
tok = torch.full((n, 1), self.V, dtype=torch.long, device=self.device) # BOS
|
||||||
|
h = None
|
||||||
|
with torch.no_grad():
|
||||||
|
for pos in range(self.L):
|
||||||
|
emb = self.net.embed(tok)
|
||||||
|
hid, h = self.net.gru(emb, h)
|
||||||
|
logits = self.net.out(hid[:, -1, :]) # (n, V)
|
||||||
|
probs = torch.softmax(logits, dim=-1)
|
||||||
|
tok = torch.multinomial(probs, 1, generator=g)
|
||||||
|
out[:, pos] = tok[:, 0]
|
||||||
|
return out.cpu().numpy()
|
||||||
|
|
||||||
|
|
||||||
|
# --- dispatch ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def make_torch_model(model_cfg: ModelCfg, cfg: SyntheticCfg, oracle: Oracle):
|
||||||
|
"""Construct a torch generative model of the requested ``kind``."""
|
||||||
|
kind = model_cfg.kind
|
||||||
|
if kind == "rnn":
|
||||||
|
return RNNGenerator(cfg, model_cfg, oracle)
|
||||||
|
if kind == "vae":
|
||||||
|
from .torch_vae import VAEGenerator # noqa: PLC0415
|
||||||
|
|
||||||
|
return VAEGenerator(cfg, model_cfg, oracle)
|
||||||
|
if kind == "mlp":
|
||||||
|
from .torch_mlp import MLPGenerator # noqa: PLC0415
|
||||||
|
|
||||||
|
return MLPGenerator(cfg, model_cfg, oracle)
|
||||||
|
raise ValueError(f"unknown torch model kind {kind!r}")
|
||||||
104
src/neural/torch_vae.py
Normal file
104
src/neural/torch_vae.py
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
"""Sequence VAE generative model (Stage C, for the N5 architecture-generality axis).
|
||||||
|
|
||||||
|
A GRU encoder maps a token sequence to a Gaussian latent ``z``; a GRU decoder (its initial
|
||||||
|
hidden state projected from ``z``) reconstructs the sequence. Trained by the ELBO
|
||||||
|
(reconstruction CE + ``beta`` * KL). A latent-variable generator is a third, distinct
|
||||||
|
inductive bias — and the canonical model in which generative collapse was first studied — so
|
||||||
|
its collapse under dry self-training is strong evidence the effect is operator-driven.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from .config import ModelCfg, SyntheticCfg
|
||||||
|
from .oracle import Oracle
|
||||||
|
from .torch_models import _BaseTorchGenerator
|
||||||
|
from .train import device_generator, seed_everything
|
||||||
|
|
||||||
|
|
||||||
|
def _make_vae(V: int, L: int, embed: int, hidden: int, latent: int):
|
||||||
|
import torch
|
||||||
|
import torch.nn as nn
|
||||||
|
|
||||||
|
class SeqVAE(nn.Module):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.V, self.L, self.bos = V, L, V
|
||||||
|
self.embed = nn.Embedding(V + 1, embed)
|
||||||
|
self.enc = nn.GRU(embed, hidden, batch_first=True)
|
||||||
|
self.to_mu = nn.Linear(hidden, latent)
|
||||||
|
self.to_lv = nn.Linear(hidden, latent)
|
||||||
|
self.z_to_h = nn.Linear(latent, hidden)
|
||||||
|
self.dec = nn.GRU(embed, hidden, batch_first=True)
|
||||||
|
self.out = nn.Linear(hidden, V)
|
||||||
|
|
||||||
|
def encode(self, x):
|
||||||
|
_, h = self.enc(self.embed(x)) # h: (1, B, H)
|
||||||
|
h = h[-1]
|
||||||
|
return self.to_mu(h), self.to_lv(h)
|
||||||
|
|
||||||
|
def decode_logits(self, z, x): # teacher forcing
|
||||||
|
B = x.shape[0]
|
||||||
|
bos = torch.full((B, 1), self.bos, dtype=torch.long, device=x.device)
|
||||||
|
inp = torch.cat([bos, x[:, :-1]], dim=1)
|
||||||
|
h0 = torch.tanh(self.z_to_h(z)).unsqueeze(0) # (1, B, H)
|
||||||
|
out, _ = self.dec(self.embed(inp), h0)
|
||||||
|
return self.out(out)
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
mu, lv = self.encode(x)
|
||||||
|
std = torch.exp(0.5 * lv)
|
||||||
|
z = mu + std * torch.randn_like(std)
|
||||||
|
logits = self.decode_logits(z, x)
|
||||||
|
kl = -0.5 * torch.sum(1 + lv - mu.pow(2) - lv.exp(), dim=1).mean()
|
||||||
|
return logits, kl
|
||||||
|
|
||||||
|
return SeqVAE()
|
||||||
|
|
||||||
|
|
||||||
|
class VAEGenerator(_BaseTorchGenerator):
|
||||||
|
"""Sequence VAE generative model over token sequences."""
|
||||||
|
|
||||||
|
def fit(self, X: np.ndarray, rng: np.random.Generator) -> None:
|
||||||
|
import torch
|
||||||
|
|
||||||
|
g = seed_everything(int(rng.integers(2 ** 31)))
|
||||||
|
net = _make_vae(self.V, self.L, self.mcfg.embed, self.mcfg.hidden,
|
||||||
|
self.mcfg.latent).to(self.device)
|
||||||
|
net.train()
|
||||||
|
opt = torch.optim.Adam(net.parameters(), lr=self.mcfg.lr)
|
||||||
|
ce = torch.nn.CrossEntropyLoss()
|
||||||
|
data = torch.as_tensor(np.asarray(X), dtype=torch.long, device=self.device)
|
||||||
|
n, bs, beta = data.shape[0], self.mcfg.batch_size, self.mcfg.beta
|
||||||
|
for _ in range(self.mcfg.epochs):
|
||||||
|
perm = torch.randperm(n, generator=g).to(self.device)
|
||||||
|
for i in range(0, n, bs):
|
||||||
|
batch = data[perm[i:i + bs]]
|
||||||
|
logits, kl = net(batch)
|
||||||
|
recon = ce(logits.reshape(-1, self.V), batch.reshape(-1))
|
||||||
|
loss = recon + beta * kl / batch.shape[0]
|
||||||
|
opt.zero_grad()
|
||||||
|
loss.backward()
|
||||||
|
opt.step()
|
||||||
|
net.eval()
|
||||||
|
self.net = net
|
||||||
|
|
||||||
|
def sample(self, n: int, rng: np.random.Generator) -> np.ndarray:
|
||||||
|
import torch
|
||||||
|
|
||||||
|
if self.net is None:
|
||||||
|
raise RuntimeError("VAEGenerator.sample called before fit")
|
||||||
|
g = device_generator(int(rng.integers(2 ** 31)), self.device)
|
||||||
|
z = torch.randn(n, self.mcfg.latent, generator=g, device=self.device)
|
||||||
|
h = torch.tanh(self.net.z_to_h(z)).unsqueeze(0) # (1, n, H)
|
||||||
|
tok = torch.full((n, 1), self.net.bos, dtype=torch.long, device=self.device)
|
||||||
|
out = torch.empty((n, self.L), dtype=torch.long, device=self.device)
|
||||||
|
with torch.no_grad():
|
||||||
|
for pos in range(self.L):
|
||||||
|
dec_out, h = self.net.dec(self.net.embed(tok), h)
|
||||||
|
logits = self.net.out(dec_out[:, -1, :])
|
||||||
|
probs = torch.softmax(logits, dim=-1)
|
||||||
|
tok = torch.multinomial(probs, 1, generator=g)
|
||||||
|
out[:, pos] = tok[:, 0]
|
||||||
|
return out.cpu().numpy()
|
||||||
79
src/neural/train.py
Normal file
79
src/neural/train.py
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
"""Torch determinism, seeding, and device helpers for the neural (Stage C) models.
|
||||||
|
|
||||||
|
Layer 1.5's neural tiers are *statistically* reproducible, not bitwise (blueprint 4 rider):
|
||||||
|
we derive every torch seed from the same ``SeedSequence`` stream the rest of the study uses,
|
||||||
|
set all available determinism flags, and report per-seed points. ``CUBLAS_WORKSPACE_CONFIG``
|
||||||
|
must be set before the first CUDA op, so it is set at import time.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
os.environ.setdefault("CUBLAS_WORKSPACE_CONFIG", ":4096:8")
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
_DETERMINISM_SET = False
|
||||||
|
|
||||||
|
|
||||||
|
def torch_seed_from(seed) -> int:
|
||||||
|
"""Reduce an int or ``np.random.SeedSequence`` to a 32-bit torch seed."""
|
||||||
|
if isinstance(seed, np.random.SeedSequence):
|
||||||
|
return int(seed.generate_state(1)[0])
|
||||||
|
return int(seed) & 0xFFFFFFFF
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_device(device: str):
|
||||||
|
"""Resolve ``{auto, cpu, cuda}`` to a concrete ``torch.device``."""
|
||||||
|
import torch
|
||||||
|
|
||||||
|
if device == "auto":
|
||||||
|
device = "cuda" if torch.cuda.is_available() else "cpu"
|
||||||
|
return torch.device(device)
|
||||||
|
|
||||||
|
|
||||||
|
def set_determinism() -> None:
|
||||||
|
"""Set torch/cuDNN determinism flags once per process (best-effort)."""
|
||||||
|
global _DETERMINISM_SET
|
||||||
|
if _DETERMINISM_SET:
|
||||||
|
return
|
||||||
|
import torch
|
||||||
|
|
||||||
|
torch.use_deterministic_algorithms(True, warn_only=True)
|
||||||
|
torch.backends.cudnn.deterministic = True
|
||||||
|
torch.backends.cudnn.benchmark = False
|
||||||
|
_DETERMINISM_SET = True
|
||||||
|
|
||||||
|
|
||||||
|
def seed_everything(seed) -> "object":
|
||||||
|
"""Seed torch (CPU+CUDA) from ``seed`` and return a seeded CPU ``torch.Generator``.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
seed: An int or ``np.random.SeedSequence`` from the study's seed stream.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
torch.Generator: A CPU generator seeded for sampling ops (e.g. ``randperm``).
|
||||||
|
"""
|
||||||
|
import torch
|
||||||
|
|
||||||
|
s = torch_seed_from(seed)
|
||||||
|
torch.manual_seed(s)
|
||||||
|
if torch.cuda.is_available():
|
||||||
|
torch.cuda.manual_seed_all(s)
|
||||||
|
g = torch.Generator()
|
||||||
|
g.manual_seed(s)
|
||||||
|
return g
|
||||||
|
|
||||||
|
|
||||||
|
def device_generator(seed, device) -> "object":
|
||||||
|
"""Return a ``torch.Generator`` on ``device`` seeded from ``seed``.
|
||||||
|
|
||||||
|
``torch.multinomial`` requires the generator to live on the same device as the
|
||||||
|
probabilities, so sampling ops use this rather than the CPU generator.
|
||||||
|
"""
|
||||||
|
import torch
|
||||||
|
|
||||||
|
g = torch.Generator(device=device)
|
||||||
|
g.manual_seed(torch_seed_from(seed))
|
||||||
|
return g
|
||||||
|
|
@ -123,6 +123,79 @@ Design decisions #1 (dataclasses now / pydantic at YAML layer), #2 (fitness `f_i
|
||||||
- **E2 analysis add-ons** (companion work order `tasks/workorder-E2-analysis-addons.md`, verified): new `analysis.py` (`reduce_to_stationary`, `critical_grounding` bootstrap CI) — real E2 **g*=0.048, CI [0.047,0.050]**; `metrics.tail_band_metrics` + per-band lineage logging; `tests/test_analysis.py` reproduces the work order's verified numbers exactly. E2 figure rebuilt 2×2. **Deviation:** used truth-mass-weighted tail coverage instead of raw `tail_mass` (a drift martingale).
|
- **E2 analysis add-ons** (companion work order `tasks/workorder-E2-analysis-addons.md`, verified): new `analysis.py` (`reduce_to_stationary`, `critical_grounding` bootstrap CI) — real E2 **g*=0.048, CI [0.047,0.050]**; `metrics.tail_band_metrics` + per-band lineage logging; `tests/test_analysis.py` reproduces the work order's verified numbers exactly. E2 figure rebuilt 2×2. **Deviation:** used truth-mass-weighted tail coverage instead of raw `tail_mass` (a drift martingale).
|
||||||
- All six figures regenerate via `make figures`; **71 tests green**.
|
- All six figures regenerate via `make figures`; **71 tests green**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Layer 1.5 — Architecture-general neural existence proof (RNN/VAE/MLP + synthetic/MNIST)
|
||||||
|
|
||||||
|
*Created 2026-07-04. Plan: `~/.claude/plans/we-are-going-to-cheerful-fog.md`. Re-scopes Layer 2:
|
||||||
|
build a cheap, architecture-general neural collapse proof in real trained weights on a
|
||||||
|
fully-synthetic sandbox (exact known `p*`) before the LLM rung. Locked decisions: exact-oracle
|
||||||
|
categorical token sequences; Histogram+RNN+VAE+MLP; real MNIST as secondary confirmation; LLM +
|
||||||
|
C3 vertical claim deferred.*
|
||||||
|
|
||||||
|
## Progress log
|
||||||
|
|
||||||
|
**2026-07-04 — Stages A, B, plumbing complete.**
|
||||||
|
|
||||||
|
- **Env:** installed `uv` 0.11.26 (`~/.local/bin`); `/home` was 100% full — GG approved clearing
|
||||||
|
pip/yay/browser caches (~10 GB freed). Base venv synced; 71 Layer-1 tests green.
|
||||||
|
- **Stage A (scaffold, pure NumPy):** `src/neural/` — `config.py` (frozen dataclasses reusing
|
||||||
|
`knowledge.config` GroundingCfg/RemintCfg/MetricsCfg/_sub), `synthetic.py` (mode-truth via
|
||||||
|
`make_true_distribution`; lossless identity + stochastic style token grammar), `oracle.py`
|
||||||
|
(`ExactOracle` zero-error + `measure_distribution`), `models.py` (`GenerativeModel` protocol +
|
||||||
|
`HistogramModel` bridge), `evaluate.py` (reuses `knowledge.metrics`, Layer-1 row schema),
|
||||||
|
`generation_loop.py` (`run_generative_lineage`, reuses `allocate_m`/`structured_multinomial`).
|
||||||
|
15 correctness tests green.
|
||||||
|
- **Stage B — HARD GATE PASSED:** `tests/test_neural_validation.py` — histogram lineage reproduces
|
||||||
|
Pred. 1 (neutral decay, <3% rel err), Pred. 3 (exact `H_eq`, <5%), and tracks Layer-1
|
||||||
|
`run_lineage` directly (<3%). The neural plumbing reproduces the analytic core.
|
||||||
|
- **Plumbing:** `neural/experiment.py` (`run_and_save` dispatch on `kind`, reuses `_apply_param`
|
||||||
|
g→m, paired seeds); extended `knowledge.experiment.save_artifacts` (optional `extra_libs`,
|
||||||
|
`extra_manifest`, injectable `grid`; skips missing libs — backward compatible). `configs/neural/N0.yaml`,
|
||||||
|
Makefile `neural`/`env-neural`/`layer2` targets, `.gitignore`.
|
||||||
|
- **N0 result (bridge, 17s):** neural **g\* = 0.0474, CI [0.045, 0.052]** — reproduces Layer-1 E2's
|
||||||
|
g\*=0.048 essentially exactly (g=0.005→67% of H*, g=0.05→96%). **89 tests green.**
|
||||||
|
|
||||||
|
**2026-07-04 — Stage C: torch models + N1/N2/N5.**
|
||||||
|
|
||||||
|
- **Env:** torch **2.12.1+cu130** (default PyPI wheel ships CUDA 13, matches RTX A4000 driver;
|
||||||
|
no custom index needed, cp314 wheels exist). `--extra neural` = torch only; `--extra mnist` =
|
||||||
|
torchvision (later). `UV_CACHE_DIR=/tmp` during install (RAM-backed) to spare `/home`.
|
||||||
|
- **Models:** `torch_models.py` (RNNGenerator, autoregressive GRU), `torch_mlp.py` (autoregressive
|
||||||
|
MLP, causal-masked), `torch_vae.py` (sequence VAE), `train.py` (determinism flags + device/seed
|
||||||
|
helpers derived from the SeedSequence stream). `tests/test_neural_torch.py` (torch-gated): gen-0
|
||||||
|
fidelity (rnn+mlp) + dry-collapse/grounded-holds. **92 tests green.**
|
||||||
|
- **Validated regime:** K=256, n=200, zipf_s=1.3, RNN hidden=128/epochs=25. RNN gen-0 fidelity
|
||||||
|
KL(p*‖p̂)=0.008, 64/64 (or 256/256) modes recovered. MLP fidelity KL=0.011. **VAE does NOT clear
|
||||||
|
the gen-0 gate** on the Zipf-codeword task (KL≈0.8; prior-hole mismatch — sampling z~N(0,I) misses
|
||||||
|
the aggregate posterior) → excluded from N5 to avoid confounding collapse with underfitting.
|
||||||
|
- **N1 (collapse in weights):** dry RNN lineage collapses — forward-KL rises to ~2.2 vs grounded
|
||||||
|
~1.4; grounding lifts tail survival (tailalive 0.31 dry → 0.50 at g=0.02). Sign confirmed.
|
||||||
|
- **N2 (neural phase boundary):** stationary H hovers 80–91% of H* and is **noisy / non-monotonic**
|
||||||
|
at 5 reps — no crisp g*. **KEY FINDING:** the neural models' smoothing inductive bias *partially
|
||||||
|
resists* H-collapse (dry H stays ~83% of H*), so **forward-KL and tail survival are the sharp
|
||||||
|
neural collapse metrics, not H** (mirrors Layer-1's "H is smooth; the threshold lives in tail
|
||||||
|
survival"). N2 needs (a) forward-KL as the phase metric, (b) more reps (≥10), and/or (c) a
|
||||||
|
stronger-collapse regime for a clean neural g*.
|
||||||
|
- **N5 (architecture-generality) — clean result:** collapse + grounding-rescue appear in ALL three
|
||||||
|
model classes (dry→grounded forward-KL: histogram 6.2→4.6, MLP 4.8→1.3, RNN 3.8→1.1; tailalive
|
||||||
|
RNN 0.41→0.64, MLP 0.07→0.20). The WF operator is architecture-general. Bonus: neural smoothing
|
||||||
|
lets RNN/MLP retain *more* tail than the exact histogram under grounding (they generalise to
|
||||||
|
unseen codewords) — an inductive-bias finding worth the write-up.
|
||||||
|
|
||||||
|
## Remaining
|
||||||
|
|
||||||
|
- [ ] **N2 refinement:** re-run with forward-KL as the phase metric + ≥10 reps (and/or smaller n)
|
||||||
|
for a clean neural g*. Pin the falsifier ("g* ≪ 1 exists") before re-running.
|
||||||
|
- [ ] **N4 (load-bearing):** `recombine.py` — mean-mixture vs union-preserving merge. The neural
|
||||||
|
merge MUST be **oracle-guided mixture sampling** (sample from the teacher strongest on each mode),
|
||||||
|
NOT weight-averaging of recurrent nets (flag #7). Reproduce the E4 "mean flat, max rises" finding.
|
||||||
|
- [ ] **N3** region-matched grounding (R>1), **N6** re-mint gate (optional).
|
||||||
|
- [ ] **VAE fidelity:** fix the prior-hole mismatch (KL-annealing / free-bits / larger latent) so it
|
||||||
|
clears the gen-0 gate, then add to N5. Or document as a known limitation.
|
||||||
|
- [ ] Real-MNIST secondary tier (`ClassifierOracle` + confusion matrix; `--extra mnist`).
|
||||||
|
- [ ] `figures/plot_N*.py` (reuse `figures/_figlib.py`); wire into `make figures`.
|
||||||
|
|
||||||
## Discovered during work
|
## Discovered during work
|
||||||
|
|
||||||
- **E2 grounding policy vs. the analytic H_eq:** Pred. 3's closed form is derived for *plain* immigration `Multinomial(m, p*)`. Implemented as `policy="proportional"`, and every policy reduces to it at `R=1`. E2 should therefore run at `R=1` (or `proportional`) so the phase-boundary sweep tracks the exact `H_eq`; region structure is E3's concern. Decide E2's `init` (uniform vs truth) when building its config.
|
- **E2 grounding policy vs. the analytic H_eq:** Pred. 3's closed form is derived for *plain* immigration `Multinomial(m, p*)`. Implemented as `policy="proportional"`, and every policy reduces to it at `R=1`. E2 should therefore run at `R=1` (or `proportional`) so the phase-boundary sweep tracks the exact `H_eq`; region structure is E3's concern. Decide E2's `init` (uniform vs truth) when building its config.
|
||||||
|
|
|
||||||
174
tests/test_neural_correctness.py
Normal file
174
tests/test_neural_correctness.py
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
"""Correctness tests for the Layer 1.5 neural scaffold (Stage A).
|
||||||
|
|
||||||
|
Pure-NumPy checks (no torch): the synthetic grammar is lossless, the exact oracle has zero
|
||||||
|
error, the histogram model reduces to a mode-frequency estimator, and the generation loop
|
||||||
|
produces the Layer-1 row schema deterministically.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from neural.config import NeuralLineageCfg, SyntheticCfg
|
||||||
|
from neural.generation_loop import run_generative_lineage
|
||||||
|
from neural.models import HistogramModel, make_model
|
||||||
|
from neural.oracle import ExactOracle, measure_distribution
|
||||||
|
from neural.synthetic import id_codewords, make_mode_truth, render_modes, sample_synthetic
|
||||||
|
|
||||||
|
|
||||||
|
def _syn(**over) -> SyntheticCfg:
|
||||||
|
base = dict(K=64, R=1, zipf_s=1.1, tail_threshold=1e-3, style_len=3, style_vocab=5,
|
||||||
|
id_base=2)
|
||||||
|
base.update(over)
|
||||||
|
return SyntheticCfg(**base)
|
||||||
|
|
||||||
|
|
||||||
|
# --- synthetic grammar ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_id_len_covers_all_modes():
|
||||||
|
syn = _syn(K=100, id_base=2)
|
||||||
|
assert syn.id_base ** syn.id_len >= syn.K
|
||||||
|
assert syn.id_base ** (syn.id_len - 1) < syn.K
|
||||||
|
|
||||||
|
|
||||||
|
def test_seq_len_and_vocab():
|
||||||
|
syn = _syn(K=64, id_base=2, style_len=3, style_vocab=5)
|
||||||
|
assert syn.id_len == 6 # 2**6 = 64
|
||||||
|
assert syn.seq_len == syn.id_len + syn.style_len
|
||||||
|
assert syn.vocab == max(syn.id_base, syn.style_vocab)
|
||||||
|
|
||||||
|
|
||||||
|
def test_codewords_are_unique_and_invertible():
|
||||||
|
syn = _syn(K=64)
|
||||||
|
cw = id_codewords(syn)
|
||||||
|
assert cw.shape == (syn.K, syn.id_len)
|
||||||
|
assert cw.max() < syn.id_base
|
||||||
|
# each mode's codeword is distinct
|
||||||
|
assert len({tuple(r) for r in cw}) == syn.K
|
||||||
|
|
||||||
|
|
||||||
|
def test_render_shapes_and_token_ranges():
|
||||||
|
syn = _syn(K=32, style_len=4, style_vocab=7)
|
||||||
|
rng = np.random.default_rng(0)
|
||||||
|
modes = np.arange(syn.K)
|
||||||
|
X = render_modes(modes, syn, rng)
|
||||||
|
assert X.shape == (syn.K, syn.seq_len)
|
||||||
|
assert X[:, : syn.id_len].max() < syn.id_base
|
||||||
|
assert X[:, syn.id_len :].max() < syn.style_vocab
|
||||||
|
|
||||||
|
|
||||||
|
# --- exact oracle -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_exact_oracle_zero_error_on_all_modes():
|
||||||
|
syn = _syn(K=100)
|
||||||
|
rng = np.random.default_rng(1)
|
||||||
|
modes = np.repeat(np.arange(syn.K), 5) # every mode, many style draws
|
||||||
|
X = render_modes(modes, syn, rng)
|
||||||
|
recovered = ExactOracle(syn).classify(X)
|
||||||
|
assert np.array_equal(recovered, modes) # zero measurement error
|
||||||
|
|
||||||
|
|
||||||
|
def test_measure_distribution_recovers_frequencies():
|
||||||
|
syn = _syn(K=16)
|
||||||
|
rng = np.random.default_rng(2)
|
||||||
|
p = np.array([0.5] + [0.5 / 15] * 15)
|
||||||
|
X, _ = sample_synthetic(p, 200_000, syn, rng)
|
||||||
|
p_hat = measure_distribution(X, ExactOracle(syn), syn.K)
|
||||||
|
assert p_hat.shape == (syn.K,)
|
||||||
|
assert np.isclose(p_hat.sum(), 1.0)
|
||||||
|
assert abs(p_hat[0] - 0.5) < 0.01
|
||||||
|
|
||||||
|
|
||||||
|
# --- histogram model --------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_histogram_initialise_is_exact():
|
||||||
|
syn = _syn(K=32)
|
||||||
|
m = HistogramModel(syn, ExactOracle(syn))
|
||||||
|
p0 = np.full(syn.K, 1.0 / syn.K)
|
||||||
|
m.initialise(p0, np.random.default_rng(0))
|
||||||
|
assert np.allclose(m.mode_distribution(np.random.default_rng(0)), p0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_histogram_fit_then_sample_roundtrip():
|
||||||
|
syn = _syn(K=16)
|
||||||
|
rng = np.random.default_rng(3)
|
||||||
|
m = HistogramModel(syn, ExactOracle(syn))
|
||||||
|
p = np.array([0.4, 0.3, 0.2] + [0.1 / 13] * 13)
|
||||||
|
X, _ = sample_synthetic(p, 100_000, syn, rng)
|
||||||
|
m.fit(X, rng)
|
||||||
|
drawn = m.sample(100_000, rng)
|
||||||
|
p_hat = measure_distribution(drawn, ExactOracle(syn), syn.K)
|
||||||
|
assert np.allclose(p_hat, m.mode_distribution(rng), atol=0.01)
|
||||||
|
|
||||||
|
|
||||||
|
def test_make_model_histogram():
|
||||||
|
syn = _syn()
|
||||||
|
from neural.config import ModelCfg
|
||||||
|
model = make_model(ModelCfg(kind="histogram"), syn, ExactOracle(syn))
|
||||||
|
assert isinstance(model, HistogramModel)
|
||||||
|
|
||||||
|
|
||||||
|
def test_make_model_rejects_unknown_kind():
|
||||||
|
syn = _syn()
|
||||||
|
from neural.config import ModelCfg
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
make_model(ModelCfg(kind="nope"), syn, ExactOracle(syn))
|
||||||
|
|
||||||
|
|
||||||
|
# --- mode truth reuses Layer 1 ----------------------------------------------------------
|
||||||
|
|
||||||
|
def test_mode_truth_is_layer1_truth():
|
||||||
|
syn = _syn(K=100, R=10)
|
||||||
|
td = make_mode_truth(syn)
|
||||||
|
assert td.p_star.shape == (syn.K,)
|
||||||
|
assert np.isclose(td.p_star.sum(), 1.0)
|
||||||
|
assert td.tail_mask.dtype == bool
|
||||||
|
assert len(np.unique(td.regions)) == syn.R
|
||||||
|
|
||||||
|
|
||||||
|
# --- generation loop: schema + determinism ---------------------------------------------
|
||||||
|
|
||||||
|
def _cfg(**over) -> dict:
|
||||||
|
base = {
|
||||||
|
"synthetic": {"K": 64, "R": 1, "zipf_s": 1.1, "init": "truth",
|
||||||
|
"style_len": 2, "style_vocab": 4, "id_base": 2},
|
||||||
|
"model": {"kind": "histogram"},
|
||||||
|
"dynamics": {"n": 200, "grounding": {"m": 0}},
|
||||||
|
"generations": 5,
|
||||||
|
}
|
||||||
|
base.update(over)
|
||||||
|
return base
|
||||||
|
|
||||||
|
|
||||||
|
def test_lineage_returns_layer1_schema():
|
||||||
|
df = run_generative_lineage(_cfg(), seed=0)
|
||||||
|
assert isinstance(df, pd.DataFrame)
|
||||||
|
assert list(df["generation"]) == [0, 1, 2, 3, 4, 5]
|
||||||
|
for col in ("heterozygosity", "forward_kl", "tail_mass", "support_size",
|
||||||
|
"tail_frac_alive", "head_frac_alive", "tail_truth_mass_alive"):
|
||||||
|
assert col in df.columns
|
||||||
|
|
||||||
|
|
||||||
|
def test_lineage_deterministic_given_seed():
|
||||||
|
a = run_generative_lineage(_cfg(), seed=7)
|
||||||
|
b = run_generative_lineage(_cfg(), seed=7)
|
||||||
|
pd.testing.assert_frame_equal(a, b)
|
||||||
|
|
||||||
|
|
||||||
|
def test_lineage_h0_is_truth_heterozygosity():
|
||||||
|
# init='truth' -> gen-0 H equals H* of the truth exactly (histogram is exact at gen 0)
|
||||||
|
syn = SyntheticCfg(K=64, R=1, zipf_s=1.1, init="truth", style_len=2, style_vocab=4)
|
||||||
|
td = make_mode_truth(syn)
|
||||||
|
h_star = 1.0 - np.sum(td.p_star ** 2)
|
||||||
|
df = run_generative_lineage(_cfg(), seed=1)
|
||||||
|
assert abs(df.loc[df["generation"] == 0, "heterozygosity"].iloc[0] - h_star) < 1e-12
|
||||||
|
|
||||||
|
|
||||||
|
def test_dry_lineage_collapses():
|
||||||
|
# m=0, small n -> heterozygosity must fall over generations (collapse)
|
||||||
|
df = run_generative_lineage(_cfg(generations=40, dynamics={"n": 50,
|
||||||
|
"grounding": {"m": 0}}), seed=2)
|
||||||
|
h = df["heterozygosity"].to_numpy()
|
||||||
|
assert h[-1] < h[0] - 0.1
|
||||||
60
tests/test_neural_torch.py
Normal file
60
tests/test_neural_torch.py
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
"""Stage C torch-model tests (skipped when torch is absent).
|
||||||
|
|
||||||
|
Small, fast sign checks — the neural tiers are statistically reproducible and directional,
|
||||||
|
not exact, so these assert the *sign* of each effect (blueprint 3.5): gen-0 fidelity, dry
|
||||||
|
collapse, and grounding arresting it. They gate the RNN before the N-series experiments.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
pytest.importorskip("torch")
|
||||||
|
|
||||||
|
from knowledge.metrics import forward_kl, heterozygosity # noqa: E402
|
||||||
|
from neural.config import ModelCfg, SyntheticCfg # noqa: E402
|
||||||
|
from neural.generation_loop import run_generative_lineage # noqa: E402
|
||||||
|
from neural.models import make_model # noqa: E402
|
||||||
|
from neural.oracle import ExactOracle # noqa: E402
|
||||||
|
from neural.synthetic import make_mode_truth # noqa: E402
|
||||||
|
|
||||||
|
_SYN = dict(K=256, R=1, zipf_s=1.3, init="truth", style_len=3, style_vocab=5, id_base=2,
|
||||||
|
tail_threshold=1e-3)
|
||||||
|
# hidden/epochs high enough that the RNN sharpens (an underfit RNN smooths and resists
|
||||||
|
# collapse); with n=200 K=256 the dry lineage collapses robustly across seeds.
|
||||||
|
_MODEL = dict(kind="rnn", hidden=128, embed=24, epochs=25, lr=2e-3, batch_size=256, n_eval=10000)
|
||||||
|
|
||||||
|
|
||||||
|
def _lineage_cfg(g: float, n: int, gens: int) -> dict:
|
||||||
|
m = 0 if g == 0 else round(n * g / (1 - g))
|
||||||
|
return {
|
||||||
|
"synthetic": dict(_SYN),
|
||||||
|
"model": dict(_MODEL),
|
||||||
|
"dynamics": {"n": n, "grounding": {"m": m, "policy": "proportional"}},
|
||||||
|
"generations": gens,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("kind", ["rnn", "mlp"])
|
||||||
|
def test_gen0_fidelity(kind):
|
||||||
|
# A trained gen-0 model must recover p* (else "collapse" would be underfitting). Checked
|
||||||
|
# for the RNN and MLP; the VAE does not clear this gate on the codeword task (see todo).
|
||||||
|
syn = SyntheticCfg(**_SYN)
|
||||||
|
td = make_mode_truth(syn)
|
||||||
|
model = make_model(ModelCfg(**{**_MODEL, "kind": kind}), syn, ExactOracle(syn))
|
||||||
|
model.initialise(td.p_star, np.random.default_rng(0))
|
||||||
|
p_hat = model.mode_distribution(np.random.default_rng(1))
|
||||||
|
assert forward_kl(td.p_star, p_hat, 1e-9) < 0.25 # close to truth
|
||||||
|
assert (p_hat > 1e-9).sum() >= 0.9 * syn.K # most modes represented
|
||||||
|
|
||||||
|
|
||||||
|
def test_rnn_dry_collapses_grounded_holds():
|
||||||
|
# gens=20 gives clean dry-vs-grounded separation (KL ~2+ vs ~0.3); big margins survive
|
||||||
|
# GPU non-determinism. Directional per blueprint 3.5.
|
||||||
|
dry = run_generative_lineage(_lineage_cfg(0.0, 200, 25), seed=0)
|
||||||
|
grd = run_generative_lineage(_lineage_cfg(0.05, 200, 25), seed=0)
|
||||||
|
assert dry["heterozygosity"].iloc[-1] < dry["heterozygosity"].iloc[0] - 0.10
|
||||||
|
assert dry["forward_kl"].iloc[-1] > 1.5 # tail forgotten
|
||||||
|
assert grd["forward_kl"].iloc[-1] < dry["forward_kl"].iloc[-1] # grounding closer to truth
|
||||||
|
assert grd["heterozygosity"].iloc[-1] > dry["heterozygosity"].iloc[-1]
|
||||||
100
tests/test_neural_validation.py
Normal file
100
tests/test_neural_validation.py
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
"""Stage B — the HARD GATE: the neural runner reproduces the Layer-1 analytic core.
|
||||||
|
|
||||||
|
With ``model.kind == "histogram"`` the neural generational step (train-on-parent's-samples
|
||||||
|
+ grounding) is *exactly* neutral Wright-Fisher drift with immigration. This module asserts
|
||||||
|
that the neural runner reproduces the two closed forms Layer 1 is validated against
|
||||||
|
(blueprint 2.4-1 neutral heterozygosity decay, 2.4-3 exact mutation-drift equilibrium) and
|
||||||
|
that its mean H-trajectory tracks ``knowledge.lineage.run_lineage`` directly. If any of
|
||||||
|
these fail the neural plumbing is wrong — no real network should be trained until they pass.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from knowledge.lineage import run_lineage
|
||||||
|
from knowledge.seeding import spawn_seeds
|
||||||
|
from neural.generation_loop import run_generative_lineage
|
||||||
|
from neural.synthetic import make_mode_truth
|
||||||
|
from neural.config import SyntheticCfg
|
||||||
|
|
||||||
|
|
||||||
|
def theory_decay(H0: float, n: int, t: np.ndarray) -> np.ndarray:
|
||||||
|
return H0 * (1.0 - 1.0 / n) ** np.asarray(t, dtype=float)
|
||||||
|
|
||||||
|
|
||||||
|
def theory_H_eq(n: int, m: int, H_star: float) -> float:
|
||||||
|
return H_star * m * (2 * n + m - 1) / (n + 2 * n * m + m * m)
|
||||||
|
|
||||||
|
|
||||||
|
def _mean_H(cfg: dict, n_rep: int, master: int = 20260704) -> np.ndarray:
|
||||||
|
"""Mean heterozygosity trajectory over ``n_rep`` histogram-model replicates."""
|
||||||
|
seeds = spawn_seeds(master, n_rep)
|
||||||
|
Hs = [run_generative_lineage(cfg, int(s.generate_state(1)[0]))["heterozygosity"].to_numpy()
|
||||||
|
for s in seeds]
|
||||||
|
return np.mean(np.stack(Hs), axis=0)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Pred. 1: neutral heterozygosity decay ----------------------------------------------
|
||||||
|
|
||||||
|
def test_bridge_neutral_decay_matches_theory():
|
||||||
|
K, n, gens, reps = 50, 100, 20, 800
|
||||||
|
cfg = {
|
||||||
|
"synthetic": {"K": K, "R": 1, "zipf_s": 1.1, "init": "uniform",
|
||||||
|
"style_len": 2, "style_vocab": 4, "id_base": 2},
|
||||||
|
"model": {"kind": "histogram"},
|
||||||
|
"dynamics": {"n": n, "grounding": {"m": 0}},
|
||||||
|
"generations": gens,
|
||||||
|
}
|
||||||
|
H_sim = _mean_H(cfg, reps)
|
||||||
|
t = np.arange(gens + 1)
|
||||||
|
H_theory = theory_decay(1.0 - 1.0 / K, n, t)
|
||||||
|
rel_err = np.abs(H_sim - H_theory) / H_theory
|
||||||
|
assert rel_err.max() < 0.03, f"max rel err {rel_err.max():.4f} exceeds 0.03"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Pred. 3: exact mutation-drift equilibrium under grounding ---------------------------
|
||||||
|
|
||||||
|
def test_bridge_grounded_equilibrium_matches_theory():
|
||||||
|
K, n, m, gens, reps = 80, 100, 8, 220, 300
|
||||||
|
cfg = {
|
||||||
|
"synthetic": {"K": K, "R": 1, "zipf_s": 1.1, "init": "uniform",
|
||||||
|
"style_len": 2, "style_vocab": 4, "id_base": 2},
|
||||||
|
"model": {"kind": "histogram"},
|
||||||
|
"dynamics": {"n": n, "grounding": {"m": m}}, # R=1 -> proportional immigration
|
||||||
|
"generations": gens,
|
||||||
|
}
|
||||||
|
H_sim_traj = _mean_H(cfg, reps)
|
||||||
|
H_sim = float(H_sim_traj[-60:].mean()) # stationary average
|
||||||
|
td = make_mode_truth(SyntheticCfg(K=K, R=1, zipf_s=1.1))
|
||||||
|
H_star = 1.0 - float(np.sum(td.p_star ** 2))
|
||||||
|
H_eq = theory_H_eq(n, m, H_star)
|
||||||
|
assert H_sim == pytest.approx(H_eq, rel=0.05), f"sim {H_sim:.4f} vs theory {H_eq:.4f}"
|
||||||
|
|
||||||
|
|
||||||
|
# --- Direct bridge: histogram lineage tracks Layer-1 run_lineage -------------------------
|
||||||
|
|
||||||
|
def test_bridge_tracks_layer1_trajectory():
|
||||||
|
K, n, m, gens, reps = 60, 120, 6, 40, 400
|
||||||
|
neural_cfg = {
|
||||||
|
"synthetic": {"K": K, "R": 1, "zipf_s": 1.1, "init": "uniform",
|
||||||
|
"style_len": 2, "style_vocab": 4, "id_base": 2},
|
||||||
|
"model": {"kind": "histogram"},
|
||||||
|
"dynamics": {"n": n, "grounding": {"m": m}},
|
||||||
|
"generations": gens,
|
||||||
|
}
|
||||||
|
layer1_cfg = {
|
||||||
|
"truth": {"K": K, "R": 1, "zipf_s": 1.1, "init": "uniform"},
|
||||||
|
"dynamics": {"n": n, "grounding": {"m": m, "policy": "proportional"}},
|
||||||
|
"generations": gens,
|
||||||
|
}
|
||||||
|
seeds = spawn_seeds(20260704, reps)
|
||||||
|
H_neural = np.mean(np.stack([
|
||||||
|
run_generative_lineage(neural_cfg, int(s.generate_state(1)[0]))["heterozygosity"].to_numpy()
|
||||||
|
for s in seeds]), axis=0)
|
||||||
|
H_layer1 = np.mean(np.stack([
|
||||||
|
run_lineage(layer1_cfg, int(s.generate_state(1)[0]))["heterozygosity"].to_numpy()
|
||||||
|
for s in seeds]), axis=0)
|
||||||
|
rel_err = np.abs(H_neural - H_layer1) / H_layer1
|
||||||
|
assert rel_err.max() < 0.03, f"neural vs Layer-1 max rel err {rel_err.max():.4f}"
|
||||||
467
uv.lock
generated
467
uv.lock
generated
|
|
@ -114,6 +114,74 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" },
|
{ url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cuda-bindings"
|
||||||
|
version = "13.3.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "cuda-pathfinder" },
|
||||||
|
]
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/51/6b/457ca12dad3ee9bfcc9a545cfd6b64b359ba49de40f776f6e028e678f262/cuda_bindings-13.3.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5879712accf6e14bb01aa5e67440eb84998b8d104b509cc7a6dc0b8f656a474", size = 6053539, upload-time = "2026-05-29T23:11:43.19Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/95/7a/c5e3c34a409b148f5c0f5a4ea374158f95d488862c1dffedf9aa5c639df9/cuda_bindings-13.3.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:04436a9364059c84b8f9636f359eccda1cf814341f5b670c71d80d2f79dbc708", size = 6674166, upload-time = "2026-05-29T23:11:45.478Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ce/67/5e7dba1ba576dd73da5dee894ca076ca5e959450dfff66d6d510a255d1f7/cuda_bindings-13.3.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7855c4868aabc0cfae28abbe83d56734bdfbd08f08fc234ac1912a12858bf49", size = 6025351, upload-time = "2026-05-29T23:11:49.685Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/39/2a/6d2e9047d1fb243dbaa364b01e0297534b9ed7fd27dba1c9f361519cf69b/cuda_bindings-13.3.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e32d08f71ebcdf00f0f41eab2eb37e8da94c8ed411cc9f7f7a019ce6b34abe3a", size = 6657965, upload-time = "2026-05-29T23:11:52.227Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cc/6e/2394f8163360f8391f8f1b7e72d300a82724edb81a7b7084c799fbd4c91f/cuda_bindings-13.3.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9efb21c1ee64981e184b9e0ba5eb3179e5ba3d4b51665a6cb52b8ef3d01a7cbf", size = 5920504, upload-time = "2026-05-29T23:11:56.883Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/34/c2/ef9b6a63f7dc432712a462c816662e662e00d38caa9b861c8c2588195d03/cuda_bindings-13.3.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2732904099e0a4d4db774a5fc6d91ee95fae065b4d2ecabb4968c5fe2406c9d7", size = 6476660, upload-time = "2026-05-29T23:11:59.188Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b1/81/bff68ce829999c1e4209c761bbf903b1c06ec570416ddb25020864ad5907/cuda_bindings-13.3.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ab2f74ed65bfef4163ba07a8db16f1085e0729291db12a2423aff84ee8278b8", size = 6013639, upload-time = "2026-05-29T23:12:03.509Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d4/e0/c8a1f0c8f9ffdea4f5fe6dbab89b326cef4d85caf489dad39e209da89416/cuda_bindings-13.3.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd4c814d311ec08c981f6dded1dbe7d4b371067ee4f6c14cccec4bde9590f80", size = 6534419, upload-time = "2026-05-29T23:12:05.633Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/52/b8/83b1f563925b290f2d11a01a77a84013ba56052fe3653a5bef3ccfbb43d6/cuda_bindings-13.3.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3c772dfff49681541d59630c90f858e173ac926b9c593a2b7123f2a1043cc76", size = 5809771, upload-time = "2026-05-29T23:12:10.422Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/12/20/e79b4bfe98f075195afb6343d41c498f9dbd2d161d7021d4d28bceb83581/cuda_bindings-13.3.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36febb7c1079d68a981dbbd8d5a67235b399802b82075c9388624719607e52b9", size = 6358584, upload-time = "2026-05-29T23:12:12.767Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cuda-pathfinder"
|
||||||
|
version = "1.5.6"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d2/53/8fc9b0cdc5b7f62746e6a01b85b6461e5ae27f871010a5fcf8fa6950766d/cuda_pathfinder-1.5.6-py3-none-any.whl", hash = "sha256:7e4c07c117b78ba1fb35dac4c444d21f3677b1b1ff56175c53a8e3025c5b43c0", size = 52972, upload-time = "2026-06-30T00:58:04.34Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cuda-toolkit"
|
||||||
|
version = "13.0.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364, upload-time = "2025-12-19T23:24:07.328Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.optional-dependencies]
|
||||||
|
cudart = [
|
||||||
|
{ name = "nvidia-cuda-runtime" },
|
||||||
|
]
|
||||||
|
cufft = [
|
||||||
|
{ name = "nvidia-cufft" },
|
||||||
|
]
|
||||||
|
cufile = [
|
||||||
|
{ name = "nvidia-cufile" },
|
||||||
|
]
|
||||||
|
cupti = [
|
||||||
|
{ name = "nvidia-cuda-cupti" },
|
||||||
|
]
|
||||||
|
curand = [
|
||||||
|
{ name = "nvidia-curand" },
|
||||||
|
]
|
||||||
|
cusolver = [
|
||||||
|
{ name = "nvidia-cusolver" },
|
||||||
|
]
|
||||||
|
cusparse = [
|
||||||
|
{ name = "nvidia-cusparse" },
|
||||||
|
]
|
||||||
|
nvjitlink = [
|
||||||
|
{ name = "nvidia-nvjitlink" },
|
||||||
|
]
|
||||||
|
nvrtc = [
|
||||||
|
{ name = "nvidia-cuda-nvrtc" },
|
||||||
|
]
|
||||||
|
nvtx = [
|
||||||
|
{ name = "nvidia-nvtx" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cycler"
|
name = "cycler"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
|
|
@ -123,6 +191,15 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" },
|
{ url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filelock"
|
||||||
|
version = "3.29.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/e3/ee/29c668c50888588c432a702f7c2e8ee8a0c9e5286028d91f170308d6b2e9/filelock-3.29.5.tar.gz", hash = "sha256:6e6034c57a00a020e767f2614a5539863f056de7e7991d6d1473aef7ff73f156", size = 68927, upload-time = "2026-07-03T03:50:31.818Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4a/e3/f1fae3647d170919c2cf2a898e77e7d1a4e5c7cae0aed7bb4bd3f5ebff6f/filelock-3.29.5-py3-none-any.whl", hash = "sha256:8af830889ba3a0ffcefbd6c7d2af8a54012058103771f2e10848222f476a1693", size = 45073, upload-time = "2026-07-03T03:50:30.445Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fonttools"
|
name = "fonttools"
|
||||||
version = "4.63.0"
|
version = "4.63.0"
|
||||||
|
|
@ -172,6 +249,15 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/2c/47/c99d5268f354002ce80f8d029cd9d7d872969da1de8b93d32de4dc56d6f4/fonttools-4.63.0-py3-none-any.whl", hash = "sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d", size = 1164562, upload-time = "2026-05-14T12:04:29.092Z" },
|
{ url = "https://files.pythonhosted.org/packages/2c/47/c99d5268f354002ce80f8d029cd9d7d872969da1de8b93d32de4dc56d6f4/fonttools-4.63.0-py3-none-any.whl", hash = "sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d", size = 1164562, upload-time = "2026-05-14T12:04:29.092Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fsspec"
|
||||||
|
version = "2026.6.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/10/a1/ae4e3e5003468d6391d2c77b6fa1cd73bd5d13511d81c642d7b28ac90ed4/fsspec-2026.6.0.tar.gz", hash = "sha256:f5bac145310fe30e16e1471bd6840b2d990d609e872251d7e674241822abf01a", size = 313646, upload-time = "2026-06-16T01:57:28.105Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e5/22/4222d7ddf3da30f363edaa98e329c2bce6c65497c9cb2810931c8b2c0fbc/fsspec-2026.6.0-py3-none-any.whl", hash = "sha256:02e0b71817df9b2169dc30a16832045764def1191b43dcff5bb85bdee212d2a1", size = 203949, upload-time = "2026-06-16T01:57:26.358Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iniconfig"
|
name = "iniconfig"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
|
|
@ -181,6 +267,18 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jinja2"
|
||||||
|
version = "3.1.6"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "markupsafe" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kiwisolver"
|
name = "kiwisolver"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
|
@ -307,6 +405,12 @@ dependencies = [
|
||||||
dev = [
|
dev = [
|
||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
]
|
]
|
||||||
|
mnist = [
|
||||||
|
{ name = "torchvision" },
|
||||||
|
]
|
||||||
|
neural = [
|
||||||
|
{ name = "torch" },
|
||||||
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
|
|
@ -318,8 +422,84 @@ requires-dist = [
|
||||||
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" },
|
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" },
|
||||||
{ name = "pyyaml", specifier = ">=6.0" },
|
{ name = "pyyaml", specifier = ">=6.0" },
|
||||||
{ name = "scipy", specifier = ">=1.11" },
|
{ name = "scipy", specifier = ">=1.11" },
|
||||||
|
{ name = "torch", marker = "extra == 'neural'", specifier = ">=2.2" },
|
||||||
|
{ name = "torchvision", marker = "extra == 'mnist'", specifier = ">=0.17" },
|
||||||
|
]
|
||||||
|
provides-extras = ["dev", "neural", "mnist"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "markupsafe"
|
||||||
|
version = "3.0.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
|
||||||
]
|
]
|
||||||
provides-extras = ["dev"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matplotlib"
|
name = "matplotlib"
|
||||||
|
|
@ -386,6 +566,24 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/0c/d8/c4ecab06b7ea36a570c4f3bd2d48d1799fd5d9174470e45c2194199431e7/matplotlib-3.11.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cf662e5ac5707658cb931e19972c4bd99f7b4f8b7bf79d3c821d239fa6b71e64", size = 10015653, upload-time = "2026-06-12T02:29:13.251Z" },
|
{ url = "https://files.pythonhosted.org/packages/0c/d8/c4ecab06b7ea36a570c4f3bd2d48d1799fd5d9174470e45c2194199431e7/matplotlib-3.11.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cf662e5ac5707658cb931e19972c4bd99f7b4f8b7bf79d3c821d239fa6b71e64", size = 10015653, upload-time = "2026-06-12T02:29:13.251Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mpmath"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "networkx"
|
||||||
|
version = "3.6.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "numpy"
|
name = "numpy"
|
||||||
version = "2.4.6"
|
version = "2.4.6"
|
||||||
|
|
@ -529,6 +727,158 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/fd/6a/d3a169aaf8536cf228d56a09e04bcb713a2fe4410d4e2105b9419b5a9c89/numpy-2.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:016623417bb330d719d579daf2d6b9a01ddc52e41a9ed61a47f39fde46dcd865", size = 10686451, upload-time = "2026-06-21T20:57:49.313Z" },
|
{ url = "https://files.pythonhosted.org/packages/fd/6a/d3a169aaf8536cf228d56a09e04bcb713a2fe4410d4e2105b9419b5a9c89/numpy-2.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:016623417bb330d719d579daf2d6b9a01ddc52e41a9ed61a47f39fde46dcd865", size = 10686451, upload-time = "2026-06-21T20:57:49.313Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nvidia-cublas"
|
||||||
|
version = "13.1.1.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "nvidia-cuda-nvrtc" },
|
||||||
|
]
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a7/a1/0bd24ee8c8d03adac032fd2909426a00c88f8c57961b1277ded97f91119f/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b7a210458267ac818974c53038fbec2e969d5c99f305ab15c72522fa9f001dd5", size = 542848918, upload-time = "2026-04-08T18:46:22.985Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3b/cd/154ca20c38269e05eff77c1464e6c1da89f50a6390b565e9d82e06bc11e1/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:37936a16db8fe4ac1f065c2139360608a543a09275cb1a1af612e08cfa065436", size = 423138758, upload-time = "2026-04-08T18:46:58.655Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nvidia-cuda-cupti"
|
||||||
|
version = "13.0.85"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827, upload-time = "2025-09-04T08:26:42.012Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597, upload-time = "2025-09-04T08:26:51.312Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nvidia-cuda-nvrtc"
|
||||||
|
version = "13.0.88"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200, upload-time = "2025-09-04T08:28:44.204Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nvidia-cuda-runtime"
|
||||||
|
version = "13.0.96"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060, upload-time = "2025-10-09T08:55:15.78Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nvidia-cudnn-cu13"
|
||||||
|
version = "9.20.0.48"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "nvidia-cublas" },
|
||||||
|
]
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/56/c5/83384d846b2fd17c44bd499b36c75a45ed4f095fbbb2252294e89cea5c5c/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:e31454ae00094b0c55319d9d15b6fa2fc50a9e1c0f5c8c80fb75258234e731e1", size = 444574296, upload-time = "2026-03-09T19:28:27.751Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6e/5e/edb9c0ae051602c3ccaffe424256463636d639e27d7f302dde9975ef9e7a/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0c45dd8eeb50b603f07995b1b300c62ffe6a1980482b82b3bcf94a4ca9d49304", size = 366173588, upload-time = "2026-03-09T19:29:34.474Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nvidia-cufft"
|
||||||
|
version = "12.0.0.61"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "nvidia-nvjitlink" },
|
||||||
|
]
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489, upload-time = "2025-09-04T08:31:56.044Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nvidia-cufile"
|
||||||
|
version = "1.15.1.6"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672, upload-time = "2025-09-04T08:32:22.779Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992, upload-time = "2025-09-04T08:32:14.119Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nvidia-curand"
|
||||||
|
version = "10.4.0.35"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106, upload-time = "2025-08-04T10:21:41.128Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258, upload-time = "2025-08-04T10:22:03.992Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nvidia-cusolver"
|
||||||
|
version = "12.0.4.66"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "nvidia-cublas" },
|
||||||
|
{ name = "nvidia-cusparse" },
|
||||||
|
{ name = "nvidia-nvjitlink" },
|
||||||
|
]
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980, upload-time = "2025-09-04T08:33:22.767Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nvidia-cusparse"
|
||||||
|
version = "12.6.3.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "nvidia-nvjitlink" },
|
||||||
|
]
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937, upload-time = "2025-09-04T08:33:58.029Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nvidia-cusparselt-cu13"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/46/e1/cdc1797eadf82d3a9a575a19b33fdc871a97edbec42c00b5b5e914f4aff4/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4dca476c50bf4780d46cd0bfbd82e2bc10a08e4fef7950917ce8d7578d22a23f", size = 221051344, upload-time = "2025-09-05T18:49:51.289Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/34/7d/2661f2fb3ac4302f3a246f5fc030213ac60c1fe0bce84f9783dbd831dbb7/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:786ce87568c303fadb5afcc7102d454cd3040d75f6f8626f5db460d1871f4dd0", size = 170148586, upload-time = "2025-09-05T18:50:50.248Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nvidia-nccl-cu13"
|
||||||
|
version = "2.29.7"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/72/0d/daf50d44177ee0cbc7ff0a0c91eb5ff676c82be42f9a970bc7597f440c3a/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:674a12383e3c38a1bcccae7d4f3633b37852230b6047883cb2f4c2d1b36d9bf5", size = 206014712, upload-time = "2026-03-03T05:34:20.843Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/67/f4/58e4e91b6919367c7aafb8e36fce9aad1a3047e536bf7e2fd560927d3a4c/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:edd81538446786ec3b73972543e53bb43bcaf0bfc8ef76cb679fcc390ffe136d", size = 205976000, upload-time = "2026-03-03T05:36:24.472Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nvidia-nvjitlink"
|
||||||
|
version = "13.0.88"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933, upload-time = "2025-09-04T08:35:43.553Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748, upload-time = "2025-09-04T08:35:20.008Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nvidia-nvshmem-cu13"
|
||||||
|
version = "3.4.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947, upload-time = "2025-09-06T00:32:20.022Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546, upload-time = "2025-09-06T00:32:41.564Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nvidia-nvtx"
|
||||||
|
version = "13.0.85"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047, upload-time = "2025-09-04T08:29:01.761Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "packaging"
|
name = "packaging"
|
||||||
version = "26.2"
|
version = "26.2"
|
||||||
|
|
@ -1096,6 +1446,15 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/d5/19/969dc072906c84dd0a3b05dcf57ea750936087d7873549e408b35cfc3f97/scipy-1.18.0-cp314-cp314t-win_arm64.whl", hash = "sha256:368e0a705903c466aa5f08eefb39e6b1b6b2d659e7352a31fd9e2438365be0f8", size = 25279661, upload-time = "2026-06-19T15:01:40.817Z" },
|
{ url = "https://files.pythonhosted.org/packages/d5/19/969dc072906c84dd0a3b05dcf57ea750936087d7873549e408b35cfc3f97/scipy-1.18.0-cp314-cp314t-win_arm64.whl", hash = "sha256:368e0a705903c466aa5f08eefb39e6b1b6b2d659e7352a31fd9e2438365be0f8", size = 25279661, upload-time = "2026-06-19T15:01:40.817Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "setuptools"
|
||||||
|
version = "81.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "six"
|
name = "six"
|
||||||
version = "1.17.0"
|
version = "1.17.0"
|
||||||
|
|
@ -1105,6 +1464,112 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sympy"
|
||||||
|
version = "1.14.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "mpmath" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "torch"
|
||||||
|
version = "2.12.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "cuda-bindings", marker = "sys_platform == 'linux'" },
|
||||||
|
{ name = "cuda-toolkit", extra = ["cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" },
|
||||||
|
{ name = "filelock" },
|
||||||
|
{ name = "fsspec" },
|
||||||
|
{ name = "jinja2" },
|
||||||
|
{ name = "networkx" },
|
||||||
|
{ name = "nvidia-cublas", marker = "sys_platform == 'linux'" },
|
||||||
|
{ name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" },
|
||||||
|
{ name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" },
|
||||||
|
{ name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" },
|
||||||
|
{ name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" },
|
||||||
|
{ name = "setuptools" },
|
||||||
|
{ name = "sympy" },
|
||||||
|
{ name = "triton", marker = "sys_platform == 'linux'" },
|
||||||
|
{ name = "typing-extensions" },
|
||||||
|
]
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/59/38/7028d3be540f1dcdf41660a2b01d0c51d2cb73915fe370d84e4d277a6d47/torch-2.12.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:ef81f503912effea2ce3d9b12a2e3a6ed488943e91271c90c7a829f60baf6aa2", size = 87975425, upload-time = "2026-06-17T21:08:34.094Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5a/e3/750b3e3548635ceac03ba255daa26dbc7ed66ca3484dc4b4d955ab7f4501/torch-2.12.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:107df6888624bdea41508f9aeb6149d9333c737a5530ceecb56c904e811369ae", size = 426379894, upload-time = "2026-06-17T21:06:55.077Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dc/ca/ed24783da629ff3e640ba3f70a7639e9045d3d88b93ee6bc47b8a28a1f2c/torch-2.12.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:6e29e7e74d05bda7d955c75e99459f878ebd970ef851b4057edbd3b34a5eb4a3", size = 532169264, upload-time = "2026-06-17T21:08:17.65Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/46/61/c63f0158446f3a98ea672b004d761b848911eba567ea4a624c7db5aadc04/torch-2.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:a513506cfda3c1c78dabeb6574c1597538c0254b3d39af174dde35d8177f4ce3", size = 122953086, upload-time = "2026-06-17T21:08:27.69Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f0/54/efb7ebca77970012b0cc21687a55d70eb2ba514b2c2b8e18d9fb1222f3be/torch-2.12.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:d2dd0f2c5f7ccbddaf34cade0deaf476808368f902b9cdb7f36a2ab42301bc0e", size = 87991951, upload-time = "2026-06-17T21:07:49.309Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1e/00/4210d76ca7424981f04033ebe7e48816ab83287a62538747a58825db770c/torch-2.12.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:2de4e19b88a481482c6c75291f2d6a52eda3ce51f311b29aa9b68499c830c07c", size = 426382721, upload-time = "2026-06-17T21:06:41.842Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/76/1f/bc9f5a5aa569307076365f25afcebacb22e9c754b1bcfbaaa146627c7fda/torch-2.12.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:649e4ced014ba646f76f8cb9c9726735a6323eb321b7919f942790a923f90921", size = 532261322, upload-time = "2026-06-17T21:06:06.673Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9e/49/c549461daa008159d006a76a991fbc2f26fa8bac27a4030c858463dcb20f/torch-2.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:e86550597877fb272ddc52db2f85b82cb601ea7bd932576a0340152cae2200b3", size = 122988095, upload-time = "2026-06-17T21:07:44.9Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ff/4a/0300261818e1560d72cc160ac826005507e8b7ca0a35788b591436d05b4a/torch-2.12.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:c75e93173c700bccd6bfcc4a9d19ce242ab6dacd1f1781483027a16239b9e650", size = 87992358, upload-time = "2026-06-17T21:07:40.299Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/30/a7/874a5ca05e8f159211dca7921060f7057acc1adb26431e119fd150623efc/torch-2.12.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:fcb61ccd20784b62bdd78ec84238a5cfb383b4994902e03bac95505ab360884c", size = 426386134, upload-time = "2026-06-17T21:07:31.481Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e1/75/20bb8fe9c1ad6538cce8cd0391b51927ae5af0b17ed1eab44b8824465dc1/torch-2.12.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:f4afc8083dff08719edbea346644476e3cec0cf40ebe256be0ee5d5b7c7e8c0d", size = 532268019, upload-time = "2026-06-17T21:05:37.925Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d1/fa/824ddb662af55b2eabc0dbb7b57c7c0b1bcd93693754a2b8509ec4d16490/torch-2.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:f92609e3b3ce72f25e2eb780d043ced2480c1a86c47c852604fc7a9108648386", size = 122987777, upload-time = "2026-06-17T21:07:09.49Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/63/b7/1b49fe7086ea36839cc80abc43174c43d0ab6f676c0891c871c162f44fe3/torch-2.12.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:e9b6f7d2dd66ea87a3ae620069d31335d594c06effb1a383bdd21cfe61e44ece", size = 88010025, upload-time = "2026-06-17T21:07:03.934Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d7/06/5b44063a6545036dcc680d2d303b137d9176cfb2cc1e1863e3ef94abeb52/torch-2.12.1-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:7973ccd3d2cd35c74449213f7bded199bec6c6247e705cbeda7407af79703d91", size = 426392891, upload-time = "2026-06-17T21:05:52.261Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f8/dd/c9ce9a4b0eb3c5bb92d9ea56766e2c22559f0b45171149188494edcce80f/torch-2.12.1-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:c64ac4aac16be5e296dcd912305605804b203333c690bf98c55bc09494ee92ad", size = 532272494, upload-time = "2026-06-17T21:06:22.72Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/21/7c/f3a601fc1b1f663ff269bfe553654e638651939aa6563e8daa7167c33098/torch-2.12.1-cp314-cp314-win_amd64.whl", hash = "sha256:f6dc4caf7eb4adb38a2d9f536b51db56310fdd1254e69a2d96767e1367c892b3", size = 122987254, upload-time = "2026-06-17T21:06:33.199Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e6/8c/b8087556cf81ddd808dbeb34afb8396d7ae7a1694ab489f08b1a0004e7d0/torch-2.12.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:2afbb2bdaa8a95040e733f05492ddf133c3967c9b7ce0abd218d704b6cab437d", size = 88303173, upload-time = "2026-06-17T21:05:06.603Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4a/07/fe09d1699fbed2afa10ebc692ff2b99d113f2605b6748cea633989e2789a/torch-2.12.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:97eba061fcb042fed191400b15568990073d67eaacaa6ee9b7ca01dd8b790fe9", size = 426404009, upload-time = "2026-06-17T21:04:57.557Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2e/f7/0ce4f6c1962c60ded7270e0a9eb560fb615c92b89d332cf9e3dff36d5ecc/torch-2.12.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:3867b861391701012adb2df93360efb88494dca245a185e3bb7624495cfe3f33", size = 532184292, upload-time = "2026-06-17T21:05:17.526Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/70/db/e384c12aba30320ca92aaaf557456cbcb26f04b4df307728bb8f019f5000/torch-2.12.1-cp314-cp314t-win_amd64.whl", hash = "sha256:dd15595f8fc764cffde8c6361a3beb6ef69a028c851b1b3e70e077f615980d4e", size = 123231142, upload-time = "2026-06-17T21:05:27.061Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "torchvision"
|
||||||
|
version = "0.27.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
|
||||||
|
{ name = "numpy", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
||||||
|
{ name = "pillow" },
|
||||||
|
{ name = "torch" },
|
||||||
|
]
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/64/46/bc0ebd93282aeedc1759f054a252c6fadf14b42a0535db3233c85cce4ae5/torchvision-0.27.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:ad8743a9c12c8c124ad0a1491e54c3ca0c749e91e374e3d92136060b22c9e0f4", size = 1852118, upload-time = "2026-06-17T21:09:32.448Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b2/00/752adc57b6aa8bb833f5b0672acb9538aa5535d64998b9d8dd48ee51fa80/torchvision-0.27.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a726707e4cbe438fcc507d787af7acf6bca52de30bf4b03579f1dfc0675da829", size = 7831256, upload-time = "2026-06-17T21:09:26.767Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fa/8a/c474fb27faba02e84dc40e0ac9ea1aa828d6d3557a378f7d0a22468bb2a3/torchvision-0.27.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a1d6a123009af59ad288459f579f67a65cbe8f59372dc7b97e41bc01a6a9b767", size = 7659995, upload-time = "2026-06-17T21:09:25.325Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/eb/7c/e254f8e242a921adc2cc62c11674fa8a16d33e0a1b6c6f5436cb91628ee7/torchvision-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:f3b57a984283896f15c9698562418282f828332886c77315bf269936e6ba0280", size = 3807497, upload-time = "2026-06-17T21:09:31.234Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/88/82/2e8fdc19e4f0bbe31d403a55d78318bcea4afcd3083e1e4700ef61ebb893/torchvision-0.27.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:448abfc3baba984da4577f737209e445da6be93e3b5f4799d90162bf61e3f485", size = 1852105, upload-time = "2026-06-17T21:09:33.695Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/43/42/103fa8f9366cfd1329fe449d6b1a25a640c0c17862ed48f21c4af94af322/torchvision-0.27.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9edfb5a549fc2f30ccadb24eca907901e92e426c91a59316be6703a9360e5098", size = 7830902, upload-time = "2026-06-17T21:09:29.739Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/97/70/fa6052a42110a3657fc94073648da6171220469f4bf9f27e6a0b9378075c/torchvision-0.27.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ae3d49e57c4abc8eafc1a1971f80fc4948a6268fa69340737ca4466936def080", size = 7664211, upload-time = "2026-06-17T21:09:17.206Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d0/95/27aca854da7e536a339f46bab1ef67823ac2ac97c59ab2b3203b373d46cf/torchvision-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:0b6e3aa98b7433506bbce1d0d05cb13ec787fc6eb8c5fbd998b26ce05f047543", size = 4079076, upload-time = "2026-06-17T21:09:15.907Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/32/bb/b21e0f598ca191bb2a9e9fda2fee37c06ad113313b43c6769dbefa0e921d/torchvision-0.27.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:d60311a6d08df905f9656a3a312f0a8f55f0d46321bc737bad30a8dec9644309", size = 1852110, upload-time = "2026-06-17T21:09:22.577Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2f/90/d61171daa5d6cd5f9315f84f9ef947b047a9fdf283d53241327045a8dd6d/torchvision-0.27.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:08aa33bc8e062cca32aefa90ac714916c5a855cbe1ab4c6148fc0453eb40ca5a", size = 7789476, upload-time = "2026-06-17T21:09:13.105Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b8/dc/b21d7801562c23a770e7037989814582f22ca4db479204293561de4b62e8/torchvision-0.27.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:916448be4b19676677b0dbf47d08f68b7955ea0abec7fc79340c31e217a824ba", size = 7664256, upload-time = "2026-06-17T21:09:07.549Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b9/b3/4386976ff77eda55f0aed504a288564f3ff8d170b6db49ee22e172eddfac/torchvision-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:18bc906235bfa901c135acd239f05b8c8ab90d502830cf1ef2cba3301e1f8a23", size = 4150710, upload-time = "2026-06-17T21:09:14.457Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ff/74/1d237c61f665bf46d02e15f67c9d40be42b1b634f87164b9cefd257450e7/torchvision-0.27.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:9f5ef59ad60e695796eca6b64e97cb9b21b9d5463cac5ac0ef86cfb72b6e5db9", size = 1852112, upload-time = "2026-06-17T21:09:21.445Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/24/84/f0d772e7ed85891f084755bd5d7f6f7fd279992a02652c653c1c8429dd84/torchvision-0.27.1-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:ab2f8047c2da5bf6742fec6da86840e5feaeb0cea76930d0536f3520df31e166", size = 7789751, upload-time = "2026-06-17T21:09:11.51Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/76/68/3febd41b6eef453a83fb7a0178446334fbb0405eb4b0c40b00efaf99a2dc/torchvision-0.27.1-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b44ef28ad1963f8cba5bf82f3564c454c74be300df9f79efa43f773312d17d6c", size = 7664350, upload-time = "2026-06-17T21:09:04.486Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/52/49/a23e199faf29e42a90f7d6b76437ade5d17e3185da3c64d368973ba8243e/torchvision-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:b3e9bc71854fddbf94ddb69ed8d88983945f3f28f78ee104214b0088669af66a", size = 4177297, upload-time = "2026-06-17T21:09:10.273Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ba/48/b3240eaf0fe3676dcf677ce8930ef477fe77d7f69ebe58ca8d0941384952/torchvision-0.27.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:c2fd9902f23b56b6ac667213171672fb6c89287ff011918b04af053852a2c4eb", size = 1852118, upload-time = "2026-06-17T21:09:20.223Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/73/01/6c8f3158994a9e5bb0c7b1bacc361d60e015ad79487af88fa4d7ce72c2b6/torchvision-0.27.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:8abb6d5cacd56486ca2240e5580750e53ac559412e472ea6a3cee83231a77ca7", size = 7791242, upload-time = "2026-06-17T21:09:02.062Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1b/e6/f66733fc411a9ce070c0d899c1ae562ff11654a0bc708511e23efe9d6872/torchvision-0.27.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d11da1ce8a5cc7fc527f2d5e0fe25efba93687897fe9339382b593910b1d1c6e", size = 7664934, upload-time = "2026-06-17T21:09:06.221Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/90/aa/d6179812ec52b70a7a8f5e99fe7937895d28c535106df1ca0d03f5f51425/torchvision-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:12deaee20d0d9dec6302025d3f93354266befeb692f5c50bca0137b395598b9e", size = 4284412, upload-time = "2026-06-17T21:09:08.989Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "triton"
|
||||||
|
version = "3.7.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7b/f9/19d842d06a08559534fa1eaab6ca551b1bcf40f06620bddec1babaa2772d/triton-3.7.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4a0e1cd4c4a76370ed74a8432a53cea28716827d19e40ffc732233e35ceb3f6", size = 184664887, upload-time = "2026-06-17T20:03:42.913Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cd/5e/fce69606f7f240297f163e25539906732b199530d486ce67ae319877e821/triton-3.7.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6744957e9fd610a29680ec2346057d0c86948ed3812468670719f391e94b44a5", size = 197701306, upload-time = "2026-06-17T19:53:13.673Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/94/fa/f856e24deb462d5f18bd4b5a746957862ab9b6ee5834bda60605ec348366/triton-3.7.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9497f2e696ee368862a181a90b2dcc03ca978cc4f602abd67c7d81022a6988e1", size = 184692359, upload-time = "2026-06-17T20:03:48.288Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c4/6f/fb96d15db6f36d6eae4cafb998c2e0353bf59d7c4ea1662d7497f269134a/triton-3.7.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e40869937a68206ec70d7f25bb7ec6433cb083f9135e1f36dbd318dc449a728", size = 197719725, upload-time = "2026-06-17T19:53:20.419Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/42/c5089d4d9327fcd1e862c599cc2927f39418f84dd11a84cb2ccff9d4787a/triton-3.7.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cdbfc09d9ec58bc5e68321525653220de7515c199e7a8097a97c85e62b52cd0a", size = 184694629, upload-time = "2026-06-17T20:03:53.444Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/07/42/2c3ac59253ae8892b6f307875263dd23dc875cdf732d3aea40d6d41fb7cb/triton-3.7.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:58c0e131da05134a2a4788ccbcc0c1105cf0f54c8e98f19e34cd465396dc15eb", size = 197729241, upload-time = "2026-06-17T19:53:27.801Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/40/71/e01aa7ad573883ed9456f130226babdec70b005e098c4d6226a6238e761b/triton-3.7.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe4ea396a06171f1f1f58cbd39c70b09294398f7dd7c620939bab54ad6f934fa", size = 184705764, upload-time = "2026-06-17T20:03:59.064Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a4/09/5683146fda6a2b569deb78ccfd8fbfea8bfe55f726b081c0a6bb18dd6f28/triton-3.7.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2020153b08280415ec0da6607834e79166442147e78e144df06b508c75b186d2", size = 197729537, upload-time = "2026-06-17T19:53:35.516Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e9/f8/448220c3092019f9fdfab39ec47985968181d67da34b44f6a7f6280a5cbb/triton-3.7.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c58e4c61f0c73b5dba3b5d19b4a7093c32f90dc18b2a7f121a7c16ccd31107b7", size = 184814760, upload-time = "2026-06-17T20:04:04.984Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f0/ac/229b7d4589d2e5937310e72c6d46e89599d16a4a12b479ffa1499fee8eb8/triton-3.7.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10ba85fa2cca4a2fbdeb36bf1cb082f2c252bda55bf9fccd74f65ec5bc647e68", size = 197824404, upload-time = "2026-06-17T19:53:42.772Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typing-extensions"
|
name = "typing-extensions"
|
||||||
version = "4.16.0"
|
version = "4.16.0"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue