# Why linear-size RAFs disappear in the sparse polymer model

Runnable companion to paper 12, **Linear-size autocatalytic sets are
asymptotically absent in the sparse binary polymer model**. The paper proves
that, for fixed food horizon t, intensity lambda and linear budget C, the
probability of a nonempty RAF with at most C*n reversible channels tends to
zero. A fixed number of catalysts also becomes insufficient, even without a
size restriction. This does not mean that RAFs disappear altogether.

The example provides a literal random-network model, exact construction
encoding and scalar theorem-bound evaluator. It distinguishes small-n
observations from the asymptotic statement rather than fitting a trend and
calling it evidence for the theorem.

Manuscript SHA-256:
`3e04e1ca8aaafd81a2695e0b21b7a6c7b726e3016d80c00a355ab9e9c57ccc3a`.
The original manuscript is unchanged. No Lean compilation is claimed.

## Run and change inputs

Python 3.11 or newer:

```sh
python -m venv .venv
# Windows PowerShell: .venv\Scripts\Activate.ps1
# macOS/Linux: source .venv/bin/activate
python -m pip install -r requirements.txt
python example.py --output outputs
python -m unittest -v
```

Editable inputs are at the top of `example.py`. All parameters are
dimensionless: this is a structural random chemistry, not a kinetic reactor.
The defaults use t=2, lambda=1, lengths 4 through 8, 64 trials per length, and
seed 20260922. The model does not attach empirical physical meaning to the
choice lambda=1. Keep t, lambda and C fixed when exploring the stated theorem.

The catalogue has 2^(n+1)-2 molecules and (n-2)*2^(n+1)+4 channels. Simulation
memory and time therefore grow exponentially in n. The large n values in the
bound settings evaluate scalar formulas only; they never enumerate molecules.
The default run takes roughly ten seconds on the development machine.

## Literal reversible chemistry

`Channel(word, split)` stores a product and its split position. Its two
orientations are u+v -> uv and uv -> u+v. Distinct split positions remain
distinct even when the unordered factors agree: (000,1) and (000,2) are two
channels. Equal factors are permitted. A single catalysis coordinate applies
to **both** orientations of a channel.

`PolymerModel` constructs this catalogue, supplies all words of length at most
t as food, and computes reversible closure. Closure ignores catalysis while
testing food generation, as RAF semantics requires. A catalyst can be produced
by a reaction set; it need not be present before that reaction first fires.

`CatalysedNetwork.max_raf()` repeatedly prunes channels that lack either food
generation or a reachable catalyst. `single_catalyst_witnesses()` exactly tests
the self-construction event for every active molecule. A witness includes
cleavages and may be a food-only RAF. The separately named `exact_minimum()`
exhausts subsets for small networks and refuses searches beyond its configured
limit. Maximum RAF size is **not** minimum RAF size, nor does one irreducible
RAF automatically give the minimum. The default simulation never claims to
measure that minimum.

```python
import numpy as np
from example import PolymerModel, SparseCatalysisLaw
model = PolymerModel(n=7, food_horizon=2)
law = SparseCatalysisLaw(intensity=1.0)
network = law.sample(model, np.random.default_rng(123))
maximum = network.max_raf()
self_constructing = network.single_catalyst_witnesses()
```

## Sample the two-level law correctly

`SparseCatalysisLaw` first activates each molecule independently with
p=min(1,lambda*n^2/R_n). For each active molecule, it then draws independent
channel bits with q=1/n. Inactive molecules catalyse no channels. A binomial
count followed by a uniformly selected subset implements independent Bernoulli
membership exactly without generating a dense molecule-by-channel array.

Activity is sampled once per molecule. Two different channels catalysed by
the same molecule have joint probability p*q^2, not (p*q)^2. Replacing this
shared activity with independent marginal edges changes the model. The tests
include a seeded distribution check separating those two probabilities.

Clipping p to 1 is explicit in the output. When clipping occurs, the expected
number of catalysed channels per molecule is no longer lambda*n; the actual
expectation is reported. The defaults are unclipped. The scalar theorem
calculations also account for clipping at small n.

## Productive extraction and the factorial saving

`saturated_program(channels)` fires only enabled channels that add a new
molecule, until no further addition is possible. Its final state equals the
full closure of the supplied set. Therefore every original reachable catalyst
is retained. The extracted support can be empty even when the original set is
a RAF: a catalysed channel entirely inside the food is a real example, handled
separately by the active-food term in the paper's probability bound.

`ProductiveProgram` validates each firing. `DependencyCodec` encodes its
support with arbitrary channel labels. Each instruction references either
food or an endpoint of another labelled channel, and records ligation inputs
or a cleavage product and split. Decoding resolves the acyclic references;
cyclic or unresolved codes are rejected.

The manuscript's three channels are:

```
(010,2):   01 + 0 <-> 010
(01011,3): 010 + 11 <-> 01011
(01011,4): 0101 + 1 <-> 01011
```

Starting from t=2 food, only that order is productive: two ligations followed
by a cleavage producing 0101. Nevertheless there are six arbitrary labellings,
each with a distinct dependency code. The factorial m! counts labels, not
productive firing orders. Exact enumeration at n=3, t=1 finds 4, 22 and 86
productive supports of sizes 1, 2 and 3; their 4, 44 and 516 labelled assignments
all satisfy the bound

```
number_of_supports * m! <= ((f+3m)^2 + n*(f+3m))^m.
```

Small enumeration checks the mechanism; it does not prove the universal count.

## Evaluate the two proof mechanisms

`TheoremBounds` keeps the literal constants. For depth d, it computes
b_j=(f+3j)^2+(f+3j)*t*2^j, P_d=product(b_j), and
h_d=sum(P_j*(f+3j)). At fixed rank k, d=k+1 gives

```
Pr(rank-k reachable cover exists)
    <= h_d*p + N_n^k * P_d * k^d * p^k / n^d.
```

The second term is P_(k+1)*k^(k+1)*(N_n*p/n)^k/n. Its prefactor approaches a
constant, explaining the eventual 1/n decrease. Stable logarithmic evaluation
avoids subtracting two enormous, nearly equal values when computing N_n*p.
The plotted raw bounds may exceed one; only values below one are informative
probability bounds. CSV supplies both raw logarithms and bounds capped at one.

For large catalyst ranks the factorial support bound controls the number of
possible constructions. `cutoff(C)` conservatively replaces e by 3 in D and
finds a fixed K0 with (D*k)^C <= (4/3)^k for all k>K0. Integer powers check the
first valid rank, and monotonicity beyond k>=4*C covers the remaining ranks.
`linear_bound(n,C)` then checks the required activity condition
(f+3*C*n)*p <= (9/16)^n before assembling the low-rank terms, active-food term
and C*n*(f+3*C*n)*(3/4)^n tail. If that premise fails, it returns `applicable:
false`, not a purported full bound. Logarithmic evaluations are ordinary
floating-point evaluations of analytic inequalities, not interval certificates.

For t=2, lambda=1 and C=1, the conservative cutoff is 31. At n=1000 the premise
holds, but the raw assembled bound is approximately 10^258.19, so its capped
value is just 1. This explicitly demonstrates the manuscript's warning that
the proved rate is not a practical moderate-size estimate. A separate method
evaluates the simpler explicit single-catalyst bound, including cleavage.

```python
from example import TheoremBounds
bounds = TheoremBounds(food_horizon=2, intensity=1.0)
report = bounds.linear_bound(n=1000, budget=1)
log_probability_bound = bounds.log_rank_bound(n=10**12, k=2)
```

## Read the results without overstating them

For n=4,...,8 the saved run finds any RAF in 63,64,64,64,64 of the 64 trials
and a single-catalyst RAF in 62,64,64,63,63. These small systems frequently
satisfy the event that ultimately becomes rare. The sampling figure is a
finite-size observation with pointwise 95% Wilson intervals, not a fitted
asymptotic law or simultaneous confidence band. Zero observed occurrences in
a different run would not prove absence.

The theorem does not identify the true minimum-size scaling between linear
and the cited quadratic upper budget. It does not establish a quadratic lower
bound, a little-o(n^2) upper scale, or a growing-intensity theorem. A conditional
absence conclusion on RAF existence needs a positive lower bound on the
probability of RAF existence; this code does not assert that hypothesis from
these small simulations.

## Outputs and validation

`trials.csv` records sample active counts, explicitly labelled maximum RAF
sizes and the number of single-catalyst witnesses. `results.json` records
sampling summaries, exact construction counts, all six codes and their states,
and the conservative full-bound report. `rank_bounds.csv`, two PNG/SVG figure
pairs, console output and metadata complete the package. Metadata records
inputs, seed, environment and source/manuscript/output hashes. With pinned
dependencies, scientific data replays deterministically; SVG creation metadata
may vary. Zero-bound logarithms use null in JSON and -inf in CSV.

Seven test groups check exact catalogue counts, reversible cleavage, maximum
and minimum RAFs against an independent subset oracle, all selected-subset
closure extractions, the empty extraction case, unique productive order versus
six labels, exhaustive small-support code injectivity, the two-level sampling
law, rational probability formulas, the cutoff inequality and large-n numerical
stability. Tests are finite checks, not replacements for the Lean proof.

## License

MIT is proposed for this original example code, pending the owner's license
choice. No license grant is asserted here. This does not change the license of
the manuscript or any third-party dependency.
