Skip to content

Commit 8bb7f4c

Browse files
committed
concept-persistent-ir audit, six fixes. the chapter was presenting the engine's internal Rust nucleotide struct as if it were a Python-side per-base object, with attribute names that don't exist on the Python API and a fictional MUTATED flag. (1) "A base, unpacked" card now uses the real internal field names (base, germline, germline_pos, segment, flags) and is explicitly labelled "Internal nucleotide struct (Rust)" so readers understand it's the engine's data model, not a Python handle they can dereference. (2) The fictional MUTATED flag is gone; the card now lists the real NucFlags (P_NUC, N_NUC, JUNCTION, INVERTED, INDEL_INSERTED) and notes that the discriminator for "edited base" is base != germline (no flag needed). (3) the "Why per-base provenance" pseudo-Python snippet (nuc.current_base = "G" etc., which has no real handle) is replaced with a working Python snippet that shows the actual aggregate accessors users will use: sim.bases() / sim.germline_bases() / sim.germline_position(i), labelled "Reading provenance from Python". (4) "One nucleotide, fully described" card relabelled "One nucleotide in the internal pool (illustrative)", drops the fictional extras (codon_phase, region_offset, edit_history) and the per-base allele_id, and notes that allele names resolve via sim.v_allele_id() + refdata.v_allele(...).name. (5) outcome.revision(-1).bases() — verified to raise OverflowError because negative indexing isn't supported — replaced with the explicit idiom last = outcome.revision_count() - 1; outcome.revision(last).bases(). (6) the fictional allele name 'IGHV3-23*01' (doesn't exist in current shipped configs) replaced with 'IGHVF10-G38*04', which is what the integer AlleleId 146 actually resolves to via refdata. all three rewritten code blocks smoke-tested end-to-end against the v1.1 engine: provenance read works (10 edited bases out of 388 at seed=42 with count=(5,15)), revision_count() is 14 and revision(13).bases() matches sim.bases() exactly, v_allele_id 146 maps to IGHVF10-G38*04.
1 parent a9cc50d commit 8bb7f4c

1 file changed

Lines changed: 35 additions & 31 deletions

File tree

website/concept-persistent-ir.html

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -93,48 +93,51 @@ <h2>The Nucleotide Pool — every base carries a passport.</h2>
9393
</div>
9494

9595
<div class="ph-grid">
96-
<!-- What every base knows -->
96+
<!-- Internal Rust struct — describes the engine's data model -->
9797
<div class="ph-card">
98-
<div class="ph-card-head">A base, unpacked</div>
98+
<div class="ph-card-head">Internal nucleotide struct (Rust)</div>
9999
<ul class="pass-list">
100-
<li><span class="pass-name">current_base</span><span class="pass-note">A · C · G · T · N — the base as it stands today</span></li>
101-
<li><span class="pass-name">germline_base</span><span class="pass-note">the original base before any mutation or error</span></li>
102-
<li><span class="pass-name">germline_position</span><span class="pass-note">where this base lived in its source allele</span></li>
100+
<li><span class="pass-name">base</span><span class="pass-note">A · C · G · T · N — the base as it stands today</span></li>
101+
<li><span class="pass-name">germline</span><span class="pass-note">the original base before any mutation; <code>base != germline</code> means "edited"</span></li>
102+
<li><span class="pass-name">germline_pos</span><span class="pass-note">where this base lived in its source allele; <code>NO_GERMLINE_POS</code> for indel-inserted bases</span></li>
103103
<li><span class="pass-name">segment</span><span class="pass-note">V · D · J · NP1 · NP2 — the structural role</span></li>
104-
<li><span class="pass-name">flags</span><span class="pass-note">MUTATED · INDEL_INS · N_CORRUPTION · </span></li>
104+
<li><span class="pass-name">flags</span><span class="pass-note">bitset of <code>P_NUC · N_NUC · JUNCTION · INVERTED · INDEL_INSERTED</code></span></li>
105105
</ul>
106+
<p class="phr-note" style="margin: 10px 0 0;">The fields above describe the <em>internal</em> Rust representation. The Python side surfaces this via aggregate accessors — see the next card.</p>
106107
</div>
107108

108-
<!-- Why -->
109+
<!-- Python aggregate accessors — what users actually call -->
109110
<div class="ph-card ph-card-code">
110-
<div class="ph-card-head">Why per-base provenance</div>
111-
<pre class="code"><code><span class="c-c"># A mutation arrives. The base flips A → G.</span>
112-
nuc.current_base = <span class="c-s">"G"</span> <span class="c-c"># new value</span>
113-
nuc.germline_base = <span class="c-s">"A"</span> <span class="c-c"># preserved</span>
114-
nuc.flags |= MUTATED <span class="c-c"># marked</span>
115-
116-
<span class="c-c"># Later, an indel inserts a base nearby.</span>
117-
inserted.germline_base = <span class="c-s">"\0"</span> <span class="c-c"># no germline origin</span>
118-
inserted.flags |= INDEL_INS
119-
120-
<span class="c-c"># A coordinate query at the end still works:</span>
121-
<span class="c-c"># "where did v_germline_end land after all this?"</span>
122-
<span class="c-c"># the pool answers — no manual bookkeeping needed.</span></code></pre>
111+
<div class="ph-card-head">Reading provenance from Python</div>
112+
<pre class="code"><code><span class="c-k">import</span> GenAIRR <span class="c-k">as</span> ga
113+
114+
out = ga.Experiment.<span class="c-f">on</span>(<span class="c-s">"human_igh"</span>).<span class="c-f">recombine</span>()\
115+
.<span class="c-f">mutate</span>(model=<span class="c-s">"s5f"</span>, count=(<span class="c-n">5</span>, <span class="c-n">15</span>))\
116+
.<span class="c-f">run</span>(n=<span class="c-n">1</span>, seed=<span class="c-n">42</span>)[<span class="c-n">0</span>]
117+
sim = out.<span class="c-f">final_simulation</span>()
118+
119+
<span class="c-c"># current sequence and pre-mutation germline, byte for byte</span>
120+
sim.<span class="c-f">bases</span>() <span class="c-c"># b'gag...gtg'</span>
121+
sim.<span class="c-f">germline_bases</span>() <span class="c-c"># b'gag...gtg' — pre-mutation</span>
122+
123+
<span class="c-c"># source-allele position for any pool index</span>
124+
sim.<span class="c-f">germline_position</span>(<span class="c-n">187</span>) <span class="c-c"># 187 (or NO_GERMLINE_POS for indel-inserts)</span>
125+
126+
<span class="c-c"># a base is "edited" iff sim.bases()[i] != sim.germline_bases()[i]</span></code></pre>
123127
</div>
124128
</div>
125129

126-
<!-- Visual: a single base, expanded -->
130+
<!-- Visual: a single base, expanded — labelled as internal model -->
127131
<div class="ph-recorded">
128-
<div class="phr-head">One nucleotide, fully described</div>
132+
<div class="phr-head">One nucleotide in the internal pool (illustrative)</div>
129133
<div class="phr-fields">
130134
<span class="phr-field"><span class="phr-k">pool_index</span> <span class="phr-v">187</span></span>
131-
<span class="phr-field"><span class="phr-k">current_base</span> <span class="phr-v">G</span></span>
132-
<span class="phr-field"><span class="phr-k">germline_base</span> <span class="phr-v">A</span></span>
133-
<span class="phr-field"><span class="phr-k">germline_position</span> <span class="phr-v">187</span></span>
135+
<span class="phr-field"><span class="phr-k">base</span> <span class="phr-v">G</span></span>
136+
<span class="phr-field"><span class="phr-k">germline</span> <span class="phr-v">A</span></span>
137+
<span class="phr-field"><span class="phr-k">germline_pos</span> <span class="phr-v">187</span></span>
134138
<span class="phr-field"><span class="phr-k">segment</span> <span class="phr-v">V</span></span>
135-
<span class="phr-field"><span class="phr-k">allele_id</span> <span class="phr-v">IGHV3-23*01</span></span>
136-
<span class="phr-field"><span class="phr-k">flags</span> <span class="phr-v">MUTATED</span></span>
137-
<span class="phr-field phr-more">+ codon_phase, region_offset, edit_history</span>
139+
<span class="phr-field"><span class="phr-k">flags</span> <span class="phr-v">P_NUC</span></span>
140+
<span class="phr-field phr-more">edited bit derived from <code>base != germline</code>; allele-name resolved via <code>sim.v_allele_id()</code> + <code>refdata.v_allele(...)</code></span>
138141
</div>
139142
</div>
140143
</section>
@@ -176,7 +179,7 @@ <h2>Structure is separate from content.</h2>
176179
<span class="c-c"># ['V', 'NP1', 'D', 'NP2', 'J']</span>
177180

178181
<span class="c-c"># layer 3 — assignments (identity)</span>
179-
sim.v_allele_id() <span class="c-c"># 146 → 'IGHV3-23*01'</span>
182+
sim.<span class="c-f">v_allele_id</span>() <span class="c-c"># 146 → 'IGHVF10-G38*04' via refdata.v_allele(146).name</span>
180183

181184
<span class="c-c"># layer 4 — trace (history)</span>
182185
outcome.trace().find(<span class="c-s">"sample_allele.v"</span>).value
@@ -218,8 +221,9 @@ <h2>Every pass writes a new revision.</h2>
218221
<span class="c-c"># 'trim.v_3', 'generate_np.np1', 'assemble.v', ..., 'mutate.s5f']</span>
219222

220223
<span class="c-c"># the IR snapshot after the i-th pass</span>
221-
outcome.revision(<span class="c-n">5</span>).bases() <span class="c-c"># mid-recombination state</span>
222-
outcome.revision(-<span class="c-n">1</span>).bases() <span class="c-c"># final state (alias)</span>
224+
outcome.<span class="c-f">revision</span>(<span class="c-n">5</span>).<span class="c-f">bases</span>() <span class="c-c"># mid-recombination state</span>
225+
last = outcome.<span class="c-f">revision_count</span>() - <span class="c-n">1</span>
226+
outcome.<span class="c-f">revision</span>(last).<span class="c-f">bases</span>() <span class="c-c"># final-revision bases</span>
223227

224228
<span class="c-c"># the first snapshot a named pass produced</span>
225229
outcome.revision_after(<span class="c-s">"mutate.s5f"</span>)

0 commit comments

Comments
 (0)