thermoc-erasure 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. thermoc_erasure-0.2.0/LICENSE +10 -0
  2. thermoc_erasure-0.2.0/MANIFEST.in +7 -0
  3. thermoc_erasure-0.2.0/MANUAL.md +546 -0
  4. thermoc_erasure-0.2.0/PKG-INFO +125 -0
  5. thermoc_erasure-0.2.0/README.md +92 -0
  6. thermoc_erasure-0.2.0/examples/item2_adapter.py +82 -0
  7. thermoc_erasure-0.2.0/examples/profile_gpt2_block.py +20 -0
  8. thermoc_erasure-0.2.0/examples/profile_item2_chain.py +22 -0
  9. thermoc_erasure-0.2.0/examples/profile_resnet50.py +21 -0
  10. thermoc_erasure-0.2.0/integration/ENERGYIR_INTEGRATION.md +81 -0
  11. thermoc_erasure-0.2.0/pyproject.toml +44 -0
  12. thermoc_erasure-0.2.0/pytest.ini +3 -0
  13. thermoc_erasure-0.2.0/setup.cfg +4 -0
  14. thermoc_erasure-0.2.0/src/thermoc/__init__.py +3 -0
  15. thermoc_erasure-0.2.0/src/thermoc/__main__.py +4 -0
  16. thermoc_erasure-0.2.0/src/thermoc/_vendor/__init__.py +1 -0
  17. thermoc_erasure-0.2.0/src/thermoc/_vendor/thermoir/__init__.py +15 -0
  18. thermoc_erasure-0.2.0/src/thermoc/_vendor/thermoir/backend.py +76 -0
  19. thermoc_erasure-0.2.0/src/thermoc/_vendor/thermoir/ir.py +71 -0
  20. thermoc_erasure-0.2.0/src/thermoc/_vendor/thermoir/passes.py +108 -0
  21. thermoc_erasure-0.2.0/src/thermoc/allocate_time.py +193 -0
  22. thermoc_erasure-0.2.0/src/thermoc/cli.py +65 -0
  23. thermoc_erasure-0.2.0/src/thermoc/erasure/__init__.py +19 -0
  24. thermoc_erasure-0.2.0/src/thermoc/erasure/attribution.py +114 -0
  25. thermoc_erasure-0.2.0/src/thermoc/erasure/backends/capability.py +67 -0
  26. thermoc_erasure-0.2.0/src/thermoc/erasure/backends/simulated_adiabatic.py +60 -0
  27. thermoc_erasure-0.2.0/src/thermoc/erasure/benchmarks.py +119 -0
  28. thermoc_erasure-0.2.0/src/thermoc/erasure/budget.py +100 -0
  29. thermoc_erasure-0.2.0/src/thermoc/erasure/certify.py +135 -0
  30. thermoc_erasure-0.2.0/src/thermoc/erasure/cli_impl.py +150 -0
  31. thermoc_erasure-0.2.0/src/thermoc/erasure/constants.py +27 -0
  32. thermoc_erasure-0.2.0/src/thermoc/erasure/demos.py +75 -0
  33. thermoc_erasure-0.2.0/src/thermoc/erasure/estimators/base.py +38 -0
  34. thermoc_erasure-0.2.0/src/thermoc/erasure/estimators/empirical.py +231 -0
  35. thermoc_erasure-0.2.0/src/thermoc/erasure/estimators/glauber.py +203 -0
  36. thermoc_erasure-0.2.0/src/thermoc/erasure/estimators/worstcase.py +187 -0
  37. thermoc_erasure-0.2.0/src/thermoc/erasure/frontends/__init__.py +29 -0
  38. thermoc_erasure-0.2.0/src/thermoc/erasure/frontends/stablehlo.py +23 -0
  39. thermoc_erasure-0.2.0/src/thermoc/erasure/frontends/thermoir_frontend.py +60 -0
  40. thermoc_erasure-0.2.0/src/thermoc/erasure/frontends/torch_fx.py +211 -0
  41. thermoc_erasure-0.2.0/src/thermoc/erasure/html.py +163 -0
  42. thermoc_erasure-0.2.0/src/thermoc/erasure/ir.py +134 -0
  43. thermoc_erasure-0.2.0/src/thermoc/erasure/ir_protocols.py +33 -0
  44. thermoc_erasure-0.2.0/src/thermoc/erasure/licensing.py +162 -0
  45. thermoc_erasure-0.2.0/src/thermoc/erasure/measurement.py +46 -0
  46. thermoc_erasure-0.2.0/src/thermoc/erasure/pebble.py +214 -0
  47. thermoc_erasure-0.2.0/src/thermoc/erasure/plan_format.py +66 -0
  48. thermoc_erasure-0.2.0/src/thermoc/erasure/planner.py +129 -0
  49. thermoc_erasure-0.2.0/src/thermoc/erasure/profile_api.py +85 -0
  50. thermoc_erasure-0.2.0/src/thermoc/erasure/report.py +89 -0
  51. thermoc_erasure-0.2.0/src/thermoc/erasure/rewrite/__init__.py +61 -0
  52. thermoc_erasure-0.2.0/src/thermoc/erasure/rewrite/framework.py +131 -0
  53. thermoc_erasure-0.2.0/src/thermoc/erasure/rewrite/substitutions.py +146 -0
  54. thermoc_erasure-0.2.0/src/thermoc/erasure/rewrite/uncompute.py +187 -0
  55. thermoc_erasure-0.2.0/src/thermoc/erasure/taxonomy/__init__.py +35 -0
  56. thermoc_erasure-0.2.0/src/thermoc/erasure/taxonomy/registry.py +101 -0
  57. thermoc_erasure-0.2.0/src/thermoc/erasure/taxonomy/seed_irrev.py +332 -0
  58. thermoc_erasure-0.2.0/src/thermoc/erasure/taxonomy/seed_rev.py +204 -0
  59. thermoc_erasure-0.2.0/src/thermoc/erasure/taxonomy/seed_stoch.py +97 -0
  60. thermoc_erasure-0.2.0/src/thermoc/erasure/trace.py +124 -0
  61. thermoc_erasure-0.2.0/src/thermoc_erasure.egg-info/PKG-INFO +125 -0
  62. thermoc_erasure-0.2.0/src/thermoc_erasure.egg-info/SOURCES.txt +83 -0
  63. thermoc_erasure-0.2.0/src/thermoc_erasure.egg-info/dependency_links.txt +1 -0
  64. thermoc_erasure-0.2.0/src/thermoc_erasure.egg-info/entry_points.txt +2 -0
  65. thermoc_erasure-0.2.0/src/thermoc_erasure.egg-info/requires.txt +14 -0
  66. thermoc_erasure-0.2.0/src/thermoc_erasure.egg-info/top_level.txt +1 -0
  67. thermoc_erasure-0.2.0/tests/conftest.py +82 -0
  68. thermoc_erasure-0.2.0/tests/erasure_testlib.py +100 -0
  69. thermoc_erasure-0.2.0/tests/test_acceptance_fr1.py +59 -0
  70. thermoc_erasure-0.2.0/tests/test_allocate_time.py +101 -0
  71. thermoc_erasure-0.2.0/tests/test_budget.py +45 -0
  72. thermoc_erasure-0.2.0/tests/test_constants.py +33 -0
  73. thermoc_erasure-0.2.0/tests/test_empirical.py +130 -0
  74. thermoc_erasure-0.2.0/tests/test_glauber_sigma.py +100 -0
  75. thermoc_erasure-0.2.0/tests/test_ir_frontends.py +162 -0
  76. thermoc_erasure-0.2.0/tests/test_licensing.py +142 -0
  77. thermoc_erasure-0.2.0/tests/test_m3_end_to_end.py +87 -0
  78. thermoc_erasure-0.2.0/tests/test_pebble.py +133 -0
  79. thermoc_erasure-0.2.0/tests/test_planner.py +101 -0
  80. thermoc_erasure-0.2.0/tests/test_report_certify.py +119 -0
  81. thermoc_erasure-0.2.0/tests/test_substitutions.py +93 -0
  82. thermoc_erasure-0.2.0/tests/test_taxonomy.py +117 -0
  83. thermoc_erasure-0.2.0/tests/test_uncompute.py +76 -0
  84. thermoc_erasure-0.2.0/tests/test_vendor.py +65 -0
  85. thermoc_erasure-0.2.0/tests/test_worstcase.py +168 -0
@@ -0,0 +1,10 @@
1
+ Copyright (c) 2026 David Johnson / EnergyIR. All rights reserved.
2
+
3
+ This software and its accompanying documentation are proprietary.
4
+ No permission is granted to use, copy, modify, distribute, or create
5
+ derivative works of this software, in whole or in part, without prior
6
+ written agreement from the copyright holder.
7
+
8
+ The vendored ThermoIR snapshot under src/thermoc/_vendor/thermoir/ is a
9
+ frozen copy of the author's own EnergyIR research-stack prototype and is
10
+ covered by the same terms.
@@ -0,0 +1,7 @@
1
+ include LICENSE
2
+ include README.md
3
+ include MANUAL.md
4
+ include pytest.ini
5
+ recursive-include integration *.md
6
+ recursive-include tests *.py
7
+ recursive-include examples *.py
@@ -0,0 +1,546 @@
1
+ # ThermoC `erasure` — User Manual
2
+
3
+ **Version 0.1.0 · July 2026 · EnergyIR**
4
+ Reversibility analysis, Landauer erased-bit accounting, erasure-minimizing
5
+ graph rewrites, and adiabatic execution planning for computation graphs.
6
+
7
+ This manual covers the complete end-to-end workflow: profile a model →
8
+ read the report → certify the floor → rewrite the graph → emit and
9
+ simulate an adiabatic execution plan.
10
+
11
+ ---
12
+
13
+ ## 1. What this compiler does, in one page
14
+
15
+ Every logically **irreversible** operation — every bit overwritten, rounded
16
+ away, or discarded — has a hard physical minimum energy cost:
17
+
18
+ > **Landauer bound.** Erasing ΔS bits into a bath at temperature T
19
+ > dissipates heat Q ≥ k_B·T·ln2·ΔS.
20
+ > At 300 K that is **2.87098 × 10⁻²¹ joules per bit** — a law of physics,
21
+ > not an engineering estimate.
22
+
23
+ Every logically **reversible** operation has no such floor: on
24
+ adiabatic-capable hardware its dissipation can be driven toward zero by
25
+ running it more slowly (E ≈ a/τ).
26
+
27
+ `thermoc.erasure` is the compiler subsystem that exploits this:
28
+
29
+ | Phase | What it does | Command |
30
+ |---|---|---|
31
+ | **A — Measure** | Counts the erased bits of every op in your graph, attributes a certified joule floor per op, ranks hotspots | `erasure profile` |
32
+ | **A — Certify** | Emits a signed physics certificate of the floor | `erasure certify` |
33
+ | **B — Restructure** | Removes erasures via substitutions, uncomputation, and Bennett pebble scheduling, under your memory/latency budgets | `rewrite()` |
34
+ | **B — Plan** | Emits an adiabatic execution plan with provably optimal per-op time allocation; refuses non-adiabatic backends | `erasure plan` |
35
+
36
+ **The honest line (PRD P4/N1), stated up front:** floors and headroom are
37
+ physics *reference lines*. Actual joule savings are realized **only** on
38
+ adiabatic-capable hardware via Phase B plans. The tool never claims energy
39
+ savings on GPUs or conventional CMOS — it refuses, with an explanation.
40
+
41
+ ---
42
+
43
+ ## 2. Installation & environment
44
+
45
+ The project is self-contained at:
46
+
47
+ ```
48
+ /Users/davidjohnson/Desktop/Spectral/EnergyIR/thermoc_erasure/
49
+ ```
50
+
51
+ It lives inside the production EnergyIR repository **without touching it**
52
+ (vendored ThermoIR snapshot, read-only borrows only).
53
+
54
+ **Requirements:** Python ≥ 3.11 with numpy. Optional: `torch` +
55
+ `transformers` (for the PyTorch frontend and neural-net demos),
56
+ `hypothesis` + `scipy` (dev/tests). On this machine everything is already
57
+ present in `/opt/anaconda3/bin/python`.
58
+
59
+ **No install needed** — run from the project folder:
60
+
61
+ ```sh
62
+ cd "/Users/davidjohnson/Desktop/Spectral/EnergyIR/thermoc_erasure"
63
+
64
+ # full test suite (150 tests, ~75 s; the physics gates live here)
65
+ /opt/anaconda3/bin/python -m pytest
66
+
67
+ # CLI without installing
68
+ PYTHONPATH=src /opt/anaconda3/bin/python -m thermoc --help
69
+ ```
70
+
71
+ Optionally, in your own venv: `pip install -e .` gives you the `thermoc`
72
+ console command directly.
73
+
74
+ Throughout this manual, `thermoc` means
75
+ `PYTHONPATH=src /opt/anaconda3/bin/python -m thermoc` run from the project
76
+ folder.
77
+
78
+ ---
79
+
80
+ ## 3. Quickstart: profile a model in 60 seconds
81
+
82
+ ### 3.1 Builtin demo targets
83
+
84
+ ```sh
85
+ thermoc erasure profile resnet50 --mode empirical --out out_resnet
86
+ thermoc erasure profile gpt2-block --mode worstcase --out out_gpt2
87
+ thermoc erasure profile item2-chain --out out_item2
88
+ ```
89
+
90
+ Example output (GPT-2 block, worstcase):
91
+
92
+ ```
93
+ graph: GPT2Block ops: 27 mode: worstcase
94
+ Landauer floor: 8.1470e-14 J erased bits: 2.8377e+07 sigma_env: 0 nats
95
+ top hotspots:
96
+ 3.613e-14 J 44.3% gelu torch_fx:0025:act
97
+ 2.709e-14 J 33.3% linear torch_fx:0026:mlp_proj
98
+ 1.411e-16 J 0.2% softmax torch_fx:0016:softmax
99
+ ...
100
+ wrote json: out_gpt2/erasure_report.json
101
+ wrote html: out_gpt2/erasure_report.html
102
+ ```
103
+
104
+ Open `erasure_report.html` in any browser — it is fully self-contained
105
+ (no CDNs): headline cards, hotspot chart, sortable per-op table,
106
+ warnings, unclassified-op panel.
107
+
108
+ ### 3.2 Your own PyTorch model
109
+
110
+ ```sh
111
+ thermoc erasure profile path/to/my_model.py --mode empirical --out out/
112
+ ```
113
+
114
+ `my_model.py` must expose:
115
+
116
+ ```python
117
+ def get_model(): # returns an nn.Module (eval mode recommended)
118
+ ...
119
+ def get_example_inputs(): # returns a tensor / tuple of tensors
120
+ ...
121
+ ```
122
+
123
+ or pass inputs from a file: `--inputs batch.npz` (first array is used).
124
+
125
+ ### 3.3 Modes: `worstcase` vs `empirical`
126
+
127
+ - **`worstcase`** — no execution. Distribution-free preimage arithmetic:
128
+ an *upper* attribution per op ("at most this many bits"), plus the
129
+ uniform-ensemble column used for certificates. This is the only mode
130
+ whose certificate is a valid bound.
131
+ - **`empirical`** — runs your model on the example inputs, streams
132
+ activation histograms (never stores raw activations), and estimates the
133
+ bits actually erased *on your workload*, with bootstrap confidence
134
+ intervals. Deterministic for a fixed `--seed` (bin edges are frozen on
135
+ the first batch). Never certifiable; always ≤ the worstcase column.
136
+
137
+ Useful flags:
138
+
139
+ ```
140
+ --temperature 300 bath temperature in K (floor scales linearly)
141
+ --seed 0 --bins 64 empirical estimator settings
142
+ --top-k 10 hotspot count
143
+ --measured-json m.json attach measured energy => headroom column
144
+ --no-html JSON only
145
+ ```
146
+
147
+ `m.json` schema: `{"total_J": 12.3, "per_op": {...}?, "source": "nvml"}` —
148
+ any measurement source works; v1 deliberately does not bind NVML/RAPL.
149
+
150
+ ---
151
+
152
+ ## 4. Reading the report
153
+
154
+ Every op row carries **three erased-bit columns** — they answer different
155
+ questions and are deliberately kept separate:
156
+
157
+ | Column | Meaning | Use |
158
+ |---|---|---|
159
+ | `delta_S_worstcase_bits` | max over inputs of log₂\|preimage\| — an upper bound for **every** input distribution. **= 0 ⇔ op is logically reversible.** | reversibility certification, rewrite targeting |
160
+ | `delta_S_uniform_bits` | erased bits under a uniform ensemble over the dtype domain | the certified floor (Form B) |
161
+ | `delta_S_empirical_bits` | erased bits measured on your traced workload (± CI) | profiling truth |
162
+
163
+ Ordering is always `empirical ≤ uniform ≤ worstcase` (tested).
164
+
165
+ **Classes:**
166
+ - `REV` — reversible (ΔS_wc = 0). Examples: transpose/reshape, XOR,
167
+ integer add, LeakyReLU(α≠0), coupling layers, RevNet blocks, RoPE, FFT,
168
+ *numerically full-rank square* linear (checked at runtime via SVD — a
169
+ singular or ill-conditioned weight is demoted with its σ-spectrum
170
+ recorded; condition number literally becomes a bit count).
171
+ - `IRREV` — information-destroying, with a derived formula. E.g. ReLU
172
+ erases exactly p(x≤0)·H(x|x≤0); softmax erases exactly one scalar (the
173
+ mean logit) per row; quantization erases bits_in − bits_out per element.
174
+ - `STOCH` — sampling ops. Accounted by **entropy production σ** (nats),
175
+ not deterministic erasure: heat ≥ k_B·T·σ. For Gibbs/Glauber sweeps σ is
176
+ computed exactly from the trajectory ledger.
177
+ - `UNKNOWN` — unclassified kind. **Soundness rule:** treated as full-input
178
+ erasure (worstcase), logged, and listed in the report's
179
+ `unclassified ops` panel. If you see UNKNOWN inflating your floor, add a
180
+ spec (§9).
181
+
182
+ **Headroom** = measured_J / floor_J. Expect 10⁵–10⁶× on today's hardware —
183
+ that gap is the industry's total remaining prize, not a promise this tool
184
+ saves it on your GPU.
185
+
186
+ **Sanity anchors** (what a healthy report looks like): ResNet-50 —
187
+ conv/relu/pool dominate; GPT-2 block — GELU and the non-square MLP
188
+ projection dominate, softmax is tiny (exactly b bits per attention row);
189
+ item-2 chain — the first stage dissipates (+σ), later noising stages
190
+ absorb heat (−σ, zero floor).
191
+
192
+ ---
193
+
194
+ ## 5. Certification
195
+
196
+ ```sh
197
+ thermoc erasure certify out_gpt2/erasure_report.json --out cert.json
198
+ ```
199
+
200
+ The certificate contains **two claim forms** (this is a deliberate,
201
+ documented soundness correction to the PRD's original FR6 wording — flag
202
+ for legal review):
203
+
204
+ - **Form A — reversibility (distribution-free, fully sound).** Lists the
205
+ ops with ΔS_wc = 0: *"these contribute no Landauer floor under any input
206
+ distribution."* Valid because worstcase preimage counting upper-bounds
207
+ the erased entropy for every distribution.
208
+ - **Form B — floor (ensemble-bound).** *"Under the declared input ensemble
209
+ (default: uniform over the dtype domain), the **average** dissipation of
210
+ any physical device executing this exact graph at T is ≥ X J."*
211
+ A distribution-free nonzero floor does not exist (a point-mass input
212
+ erases nothing), so the ensemble must be declared — and is.
213
+
214
+ Guard rails baked into the artifact:
215
+ - empirical-mode reports always produce `claim_valid: false` and a
216
+ `[NOT A BOUND — empirical mode]` prefix;
217
+ - the claim binds to *this graph's semantics* — a different algorithm for
218
+ the same task may have a lower floor;
219
+ - Landauer is an ensemble-average bound (single-shot fluctuations can
220
+ undershoot it — Jarzynski); the wording says "average".
221
+
222
+ **Signing:** set `THERMOC_SIGNING_KEY` (and optionally
223
+ `THERMOC_SIGNING_KEY_ID`) in the environment → HMAC-SHA256 over canonical
224
+ JSON. Unset → `alg: none` with an explicit warning. Verify in Python:
225
+
226
+ ```python
227
+ import json
228
+ from thermoc.erasure.certify import verify
229
+ assert verify(json.load(open("cert.json")))
230
+ ```
231
+
232
+ ---
233
+
234
+ ## 6. Python API (end-to-end)
235
+
236
+ ```python
237
+ import sys
238
+ sys.path.insert(0, "/Users/davidjohnson/Desktop/Spectral/EnergyIR/thermoc_erasure/src")
239
+
240
+ from thermoc.erasure import profile, rewrite, plan, Budget
241
+
242
+ # ---- Phase A: profile -------------------------------------------------
243
+ import torch
244
+ model = MyModel().eval()
245
+ x = torch.randn(8, 128)
246
+
247
+ rep = profile(model, x, mode="empirical", temperature_K=300.0, seed=0)
248
+ print(rep.total_floor_J) # graph Landauer floor, joules
249
+ print(rep.hotspots[:5]) # ranked irreversibility hotspots
250
+ rep.save("out/") # erasure_report.json + .html
251
+
252
+ # ---- Phase B: rewrite under budgets ------------------------------------
253
+ g = rep.graph # the live erasure-IR graph
254
+ g2, rw = rewrite(g, Budget(mem="4x", latency="2x"))
255
+ print(rw.erased_bits_before, "->", rw.erased_bits_after)
256
+ for cert in rw.certificates: # every rewrite carries a certificate
257
+ print(cert.rule, cert.delta_bits, cert.equivalence)
258
+
259
+ # ---- Phase B: plan + simulate ------------------------------------------
260
+ xp = plan(g2, "phal:pb64", latency_budget_s=1.0)
261
+ xp.save("execution_plan.json")
262
+
263
+ from thermoc.erasure.backends.simulated_adiabatic import SimulatedAdiabaticBackend
264
+ sim = SimulatedAdiabaticBackend("phal:pb64").run(xp)
265
+ print(xp.E_total_pred_J, sim.E_total_J) # agree within 2% (M3 gate)
266
+ ```
267
+
268
+ `profile()` accepts an `nn.Module` (+ example inputs), an
269
+ `fx.GraphModule`, a ThermoIR `Program`, or an `EGraph`.
270
+
271
+ ### 6.1 ThermoIR / p-bit programs and σ accounting
272
+
273
+ ```python
274
+ from thermoc.erasure.demos import item2_chain
275
+ prog, _ = item2_chain(T=6, L=16) # item-2 DTM forward chain
276
+ rep = profile(prog, mode="empirical", seed=0)
277
+ ```
278
+
279
+ Empirical mode executes the chain with the **σ ledger**: per Glauber
280
+ update, the exact pathwise entropy flow Δs_env = β·f_i·(Δs_i) = β·Q. Per
281
+ stage you get `sigma_env_nats` and a stage floor
282
+ kT·max(σ,0). The cheap production estimator
283
+ (σ̂ = β·Σ f·(tanh(βf) − s), the Rao-Blackwellized conditional expectation)
284
+ is validated ≤ 5% against the exact ledger, and the whole machinery passes
285
+ an integral-fluctuation-theorem test (⟨e^(−Δs_tot)⟩ = 1) against an
286
+ exactly-enumerated 4×4 ensemble.
287
+
288
+ To instrument your own program:
289
+
290
+ ```python
291
+ from thermoc.erasure.trace import run_program_logged
292
+ import numpy as np
293
+ s, ledger = run_program_logged(prog, np.random.default_rng(0))
294
+ print(ledger.sigma_env, ledger.sigma_rb) # nats; per-op in ledger.per_op
295
+ ```
296
+
297
+ Trajectories are bit-identical to the vendored ThermoIR backend at equal
298
+ seeds — logging adds zero RNG draws.
299
+
300
+ ---
301
+
302
+ ## 7. The rewrite engine (Phase B, part 1)
303
+
304
+ `rewrite(graph, budget, opt_in=None)` runs three passes in order — R1
305
+ creates the reversible regions that R2/R3 then exploit:
306
+
307
+ **R1 — substitutions**
308
+ - `relu → leaky_relu` fold-back. *Exact* when every consumer is another
309
+ relu-family op (relu∘leaky = relu); otherwise requires
310
+ `opt_in={"relu_leaky": {"alpha": 0.01}}` and the certificate is marked
311
+ `approx`.
312
+ - `quantize-later`: sinks a quantize past exactly-commuting relabelings
313
+ (transpose/reshape/flip/roll), growing the upstream REV region.
314
+ - `inplace → out-of-place`: overwrites stop erasing H(old|new), charged
315
+ against the memory budget by erased-bits-per-byte density.
316
+
317
+ **R2 — uncomputation.** Explicit `erase` nodes whose value was produced by
318
+ an invertible cone are replaced by the cone's **inverse chain** (generated
319
+ from the taxonomy's registered inverses, the way autodiff generates
320
+ gradients from a tape). Cost: ~2× that region's time + pinned anchor
321
+ memory. A copy of a still-live source (e.g. a graph input) is cancelled
322
+ reversibly (retained-operand convention). Selection is greedy by erased
323
+ bits per extra time, until your latency budget is spent.
324
+
325
+ **R3 — Bennett pebble scheduling.** For long reversible chains where
326
+ memory can't hold every checkpoint: the exact checkpoint/uncompute
327
+ recursion with branching factor k. Erasure drops **N·b → b** (only the
328
+ forced final erasure survives) at time × (2−1/k)ⁿ = N^ε and
329
+ n(k−1)+1 checkpoints. The scheduler generates and *simulates* the exact
330
+ move list (game invariant asserted); k is chosen under your budgets, and
331
+ infeasible budgets raise `BudgetInfeasible` carrying the exact
332
+ (k, time×, space, erasure) Pareto frontier so you can pick a point.
333
+
334
+ **Budgets.** `Budget(mem="2x", latency="1.5x")` — multipliers over the
335
+ baseline graph's peak liveness / total time, or absolute values
336
+ (bytes / time units). Rough guide: `mem="8x", latency="3x"` lets the full
337
+ pipeline strip a reversible-heavy graph to its forced erasures.
338
+
339
+ **Certificates.** Every application emits
340
+ `{rule, nodes, delta_bits, equivalence: tested|by-construction|approx}`.
341
+ Semantic equivalence is checked by executing before/after graphs on
342
+ identical inputs (bit-equal for integers).
343
+
344
+ ---
345
+
346
+ ## 8. The adiabatic planner (Phase B, part 2)
347
+
348
+ ```sh
349
+ thermoc erasure plan coupling_mlp --latency 10 --mem 8x --out plan.json
350
+ ```
351
+
352
+ ```
353
+ rewrite: 1.188e+05 -> 4160 erased bits (7 certificates)
354
+ plan: 30 stages on phal:pb64, T = 10.0 s
355
+ E_pred = 5.7743e-17 J (landauer residue 1.194e-17 J, sigma heat 0.000e+00 J)
356
+ simulated = 5.7743e-17 J (pred error 0.00%)
357
+ ```
358
+
359
+ **The physics the planner implements:**
360
+ - Per-op adiabatic energy E(τ) = a/τ + c·τ (RC-ramp dissipation +
361
+ leakage). With leakage, "slower is always better" is false — each op
362
+ has an optimum τ* = √(a/c), and the planner will deliberately *not*
363
+ spend your whole latency budget past it.
364
+ - Optimal allocation (P6): τ_i\* ∝ √a_i, E\* = (Σ√a_i)²/T. The gain over
365
+ uniform allocation is exactly the heterogeneity of the a_i
366
+ (Cauchy–Schwarz), reported as `gain_vs_uniform`.
367
+ - Total prediction: driving dissipation + kT·ln2·(residual erased bits) +
368
+ kT·(Σσ for sampling stages).
369
+ - The same `thermoc.allocate_time` engine will drive the geodesic
370
+ (thermodynamic-length) scheduler: a stage of thermodynamic length L is
371
+ just a_i = kT·L_i², giving constant thermodynamic speed.
372
+
373
+ **Backends.** Builtins: `phal:pb64` (simulated adiabatic p-bit fabric,
374
+ projected constants) and `phal:cmos-ref` (refusal reference). Describe
375
+ real hardware in JSON and pass `BackendCapability.from_json(path)`:
376
+
377
+ ```json
378
+ {"name": "my-adiabatic-chip", "adiabatic": true,
379
+ "a_per_unit": {"*": 2e-21}, "leak_per_unit": {"*": 1e-21},
380
+ "tau_min_s": 1e-8, "ramp_granularity_s": 1e-6}
381
+ ```
382
+
383
+ **Refusal path (P4/N1).** Planning onto a non-adiabatic backend raises
384
+ `NonAdiabaticBackendError` with the full physics explanation and emits
385
+ **no plan and no savings claim**:
386
+
387
+ ```
388
+ Backend 'phal:cmos-ref' declares adiabatic=false: it dissipates a fixed
389
+ switching energy (~1/2 C V^2) per operation irrespective of logical
390
+ reversibility ... the residual Landauer floor remains a physics reference
391
+ line only.
392
+ ```
393
+
394
+ **Plan format (v1)** reserves the pb64 hardware-integration fields per
395
+ stage (FR7): `ramp_profile`, `erase_sites`, `telemetry.q_channel` — the
396
+ simulated backend fills telemetry; real hardware will.
397
+
398
+ **Simulated backend.** `SimulatedAdiabaticBackend(cap).run(plan,
399
+ jitter=0.05)` is an independent code path (own snapping, own capability
400
+ read); the M3 gate holds predicted-vs-simulated ≤ 2% across the benchmark
401
+ suite, and the reversible-heavy benchmarks achieve **≥ 5× dissipation
402
+ reduction at 2× latency** (rev_chain 6.5×, inplace_accum 20.7×,
403
+ fft_pipeline 11.2×).
404
+
405
+ ---
406
+
407
+ ## 9. Extending the taxonomy
408
+
409
+ 63+ op specs ship in `src/thermoc/erasure/taxonomy/seed_{rev,irrev,stoch}.py`.
410
+ Inspect them:
411
+
412
+ ```sh
413
+ thermoc erasure taxonomy --list
414
+ thermoc erasure taxonomy --show softmax
415
+ ```
416
+
417
+ To add an op, register an `OpSpec`:
418
+
419
+ ```python
420
+ from thermoc.erasure.taxonomy import default_registry
421
+ from thermoc.erasure.taxonomy.registry import OpSpec
422
+ from thermoc.erasure.ir import OpClass
423
+
424
+ default_registry().register(OpSpec(
425
+ name="my_op", klass=OpClass.IRREV,
426
+ matches=("my_op", "aten.my_op*"), # exact names or globs
427
+ worstcase_bits=lambda node: node.numel_in() * 8.0,
428
+ unif_bits=lambda node: node.numel_in() * 4.0,
429
+ inverse=None, # or "my_op_inv" to enable R2
430
+ reference_impl=lambda x, **a: ..., # numpy semantics (equiv tests)
431
+ notes="what the formula means and why it is sound"))
432
+ ```
433
+
434
+ Rules of the game:
435
+ - `worstcase_bits` must upper-bound H(X|f(X)) for *every* distribution;
436
+ - `unif_bits` must not exceed the true uniform-ensemble value (floors must
437
+ never overstate); when unsure, use `n·b − log2|image|` or 0;
438
+ - unregistered kinds are safe by construction: UNKNOWN ⇒ full-input
439
+ erasure, logged and surfaced in the report;
440
+ - registering `inverse` + `reference_impl` is what makes an op eligible
441
+ for uncomputation and equivalence testing.
442
+
443
+ Fine-precision helpers you can reuse in formulas
444
+ (`estimators/worstcase.py`): `smooth_monotone_bits` (elasticity formula
445
+ for sigmoid-like saturation), `spectral_erasure_bits`
446
+ (Σ log₂(1/σᵢ) over singular values < 1), `linear_rank_bits`.
447
+
448
+ ---
449
+
450
+ ## 10. Physics reference card
451
+
452
+ | Law | Statement | Where implemented |
453
+ |---|---|---|
454
+ | Landauer (F1) | Q ≥ kT·ln2·ΔS, ensemble-average | `constants.kT_ln2` |
455
+ | Erased info (F2) | ΔS(p) = H(X\|f(X)); ΔS_wc ≥ ΔS(p) ∀p; wc=0 ⇔ reversible | `estimators/worstcase.py` |
456
+ | Finite precision (F3) | alphabet = dtype; δ(x) ≈ max(0, −log₂\|x f′/f\|) | `smooth_monotone_bits` |
457
+ | Spectral erasure (F4) | ΔS_num = Σ_{σ<1} log₂(1/σ) | `spectral_erasure_bits` |
458
+ | Entropy production (F5) | Δs_env = ln[P(s→s′)/P(s′→s)] = βQ, exact pathwise | `trace.SigmaLedger` |
459
+ | IFT gate (F5) | ⟨e^(−Δs_tot)⟩ = 1 (exact 4×4 ensemble) | `estimators/glauber.py` |
460
+ | Estimator stats (F6) | frozen quantile bins, Miller–Madow, bootstrap CI | `estimators/empirical.py` |
461
+ | Bennett (F7) | N·b → b at time N^ε, ε = ln(2−1/k)/ln k, space n(k−1)+1 | `pebble.py` |
462
+ | Uncompute (F8) | inverse-mode transformation over the taxonomy tape | `rewrite/uncompute.py` |
463
+ | Time allocation (F9) | τ\* ∝ √a; E(τ)=a/τ+cτ, τ_opt=√(a/c); geodesic a=kT·L² | `thermoc/allocate_time.py` |
464
+ | Certificates (F10) | Form A distribution-free; Form B ensemble-bound | `certify.py` |
465
+ | Plan total (F11) | Σa/τ + cτ + kT·ln2·bits + kT·σ; refuse non-adiabatic | `planner.py` |
466
+ | TUR hook (F12) | σ ≥ 2/ε² reserved (`precision_certificate: null`) | report/plan schemas |
467
+
468
+ ---
469
+
470
+ ## 11. Honest limits (read before quoting numbers externally)
471
+
472
+ 1. **No savings on today's GPUs.** Phase A is measurement/certification;
473
+ Phase B savings are demonstrated in simulation against declared E(τ)
474
+ curves. `phal:pb64` constants are projections.
475
+ 2. **Empirical estimates are workload-conditional** and carry binning
476
+ bias (documented per-op in `notes`); they never enter certificates.
477
+ 3. **Worstcase attributions can be loose** (that is their job — they are
478
+ upper bounds). Use the uniform column for floors, empirical for
479
+ profiling.
480
+ 4. **Certificates bind to the graph, not the task.** A different
481
+ algorithm may have a lower floor. The wording enforces this.
482
+ 5. **fx tracing limits:** dynamic control flow won't trace; shapes are
483
+ concretized at the example inputs and recorded in the report; the HF
484
+ GPT-2 tracer is incompatible with this torch/transformers pairing
485
+ (the demo uses a structure-identical hand-written block).
486
+ 6. **UNKNOWN ops inflate floors by design** (soundness). Check the
487
+ report's unclassified panel and add specs.
488
+
489
+ ## 12. Licensing & the PRO tier
490
+
491
+ thermoc-erasure is OSS-first and **fails open to the free tier**: with no
492
+ license (or an invalid/expired one) everything in §3–§5 keeps working
493
+ except signing. The enterprise surfaces are PRO-gated using the same
494
+ offline license mechanism as the rest of the EnergyIR suite:
495
+
496
+ - **Free:** `profile` (both modes), JSON/HTML reports, headroom,
497
+ taxonomy inspection, certificates *emitted but unsigned*.
498
+ - **PRO:** `rewrite()` (R1/R2/R3), `plan()` (adiabatic planner), and
499
+ signed certificates. Calling a PRO surface without a license raises
500
+ `PermissionError` (CLI exit code 4) with an upgrade hint.
501
+
502
+ **How to activate:** obtain the license token from the EnergyIR portal
503
+ (the same token that activates ThermoIR — one entitlement covers both),
504
+ then either
505
+
506
+ ```sh
507
+ export ENERGYIR_LICENSE="<token>" # token literal
508
+ export ENERGYIR_LICENSE="@/path/to/license" # or a file reference
509
+ # or place the token at ~/.energyir/license
510
+ export ENERGYIR_LICENSE_PUBKEY="<publicKeyHex from the portal>" # if instructed
511
+ ```
512
+
513
+ Verification is offline ed25519 (`cryptography` package — installed with
514
+ `pip install "thermoc-erasure[pro]"`); there is no phone-home. If the
515
+ `energyir` suite is installed, verification delegates to
516
+ `energy.intelligence.licensing` so one license serves the whole suite.
517
+
518
+ ## 13. Test map (where each guarantee is enforced)
519
+
520
+ ```
521
+ test_constants.py kT ln2 pinned; T scaling
522
+ test_vendor.py vendored ThermoIR ≡ upstream snapshot
523
+ test_ir_frontends.py graph hash, fx/thermoir mapping, in-place detection
524
+ test_taxonomy.py ≥60 specs, normative seeds, UNKNOWN path
525
+ test_worstcase.py every F4 formula; emp ≤ unif ≤ wc; SVD demotion
526
+ test_empirical.py ReLU closed form; frozen bins; CI; determinism
527
+ test_glauber_sigma.py bit-exact mirror; telescoping <1e-9; σ̂ ≤5%; IFT=1
528
+ test_report_certify.py schema; headroom; Form A/B; HMAC tamper detection
529
+ test_acceptance_fr1.py ResNet-50 / GPT-2 / item-2: <5 s, all resolve, <1%
530
+ test_allocate_time.py P6 closed form; Cauchy–Schwarz; leakage √(a/c)
531
+ test_budget.py budgets + liveness baselines
532
+ test_substitutions.py R1a/b/c with certificates
533
+ test_uncompute.py R2 legality, inverse synthesis, equivalence
534
+ test_pebble.py N·b → b property test (hypothesis); Pareto
535
+ test_planner.py plan schema; FR7 fields; refusal wording; σ heat
536
+ test_m3_end_to_end.py ≤2% pred-vs-sim; ≥5× @ 2× latency
537
+ test_licensing.py offline verifier; fail-open OSS; PRO gates; unsigned certs
538
+ ```
539
+
540
+ Run any single gate: `/opt/anaconda3/bin/python -m pytest tests/test_glauber_sigma.py -v`
541
+
542
+ ---
543
+
544
+ *Generated as part of the M0–M3 build of PRD_thermoc_erasure.md
545
+ (physics-deep edition). The compiler's objective function is a physical
546
+ quantity — and the certificate says exactly when that quantity is a law.*
@@ -0,0 +1,125 @@
1
+ Metadata-Version: 2.4
2
+ Name: thermoc-erasure
3
+ Version: 0.2.0
4
+ Summary: ThermoC `erasure`: reversibility analysis, Landauer erased-bit accounting, Bennett scheduling, and adiabatic execution planning for computation graphs
5
+ Author-email: David Johnson <sportsmedicineuae@gmail.com>
6
+ License: Proprietary
7
+ Project-URL: Repository, https://github.com/dmjdxb/thermoc-erasure
8
+ Project-URL: Documentation, https://github.com/dmjdxb/thermoc-erasure/blob/main/MANUAL.md
9
+ Keywords: thermodynamic-computing,landauer,reversible-computing,energy,compiler,p-bit,entropy-production
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: License :: Other/Proprietary License
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Scientific/Engineering :: Physics
17
+ Classifier: Topic :: Software Development :: Compilers
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: numpy>=1.26
22
+ Provides-Extra: torch
23
+ Requires-Dist: torch<2.7,>=2.4; extra == "torch"
24
+ Requires-Dist: transformers<4.60,>=4.50; extra == "torch"
25
+ Provides-Extra: pro
26
+ Requires-Dist: cryptography>=42; extra == "pro"
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=8.0; extra == "dev"
29
+ Requires-Dist: hypothesis>=6.100; extra == "dev"
30
+ Requires-Dist: scipy>=1.12; extra == "dev"
31
+ Requires-Dist: cryptography>=42; extra == "dev"
32
+ Dynamic: license-file
33
+
34
+ # thermoc-erasure
35
+
36
+ **ThermoC `erasure`** — reversibility analysis, Landauer erased-bit
37
+ accounting, erasure-minimizing graph rewrites (uncomputation + Bennett
38
+ pebble scheduling), and adiabatic execution planning for computation
39
+ graphs. Part of the EnergyIR compiler suite.
40
+
41
+ Every logically irreversible operation has a hard physical minimum energy
42
+ cost of k_B·T·ln2 per erased bit (Landauer, 1961). This compiler:
43
+
44
+ - **Phase A — measure & certify:** counts the erased bits of every op in a
45
+ PyTorch or ThermoIR graph, attributes a certified joule floor per op,
46
+ ranks irreversibility hotspots, and emits signed physics certificates
47
+ (distribution-free reversibility claims + ensemble-bound floor claims).
48
+ - **Phase B — restructure & plan:** removes erasures via reversible
49
+ substitutions, autodiff-style uncomputation, and exact Bennett pebble
50
+ scheduling under user memory/latency budgets, then emits adiabatic
51
+ execution plans with provably optimal per-op time allocation
52
+ (τ\* ∝ √a, Sivak–Crooks-compatible shared engine). Non-adiabatic
53
+ backends are refused, with the physics explanation — no false savings
54
+ claims.
55
+
56
+ ## Install
57
+
58
+ ```sh
59
+ pip install . # numpy-only core: worstcase mode, ThermoIR, planner
60
+ pip install ".[torch]" # + PyTorch fx frontend (ResNet/GPT-style models)
61
+ pip install ".[dev]" # + pytest, hypothesis, scipy (test suite)
62
+ ```
63
+
64
+ ## Quickstart
65
+
66
+ ```sh
67
+ thermoc erasure profile resnet50 --mode empirical --out out/ # needs [torch]
68
+ thermoc erasure certify out/erasure_report.json
69
+ thermoc erasure plan coupling_mlp --latency 10 --mem 8x # numpy-only
70
+ thermoc erasure taxonomy --list
71
+ ```
72
+
73
+ ```python
74
+ from thermoc.erasure import profile, rewrite, plan, Budget
75
+
76
+ rep = profile(model, example_inputs, mode="empirical") # Landauer floor + hotspots
77
+ g2, rw = rewrite(rep.graph, Budget(mem="4x", latency="2x"))
78
+ xp = plan(g2, "phal:pb64", latency_budget_s=1.0) # adiabatic execution plan
79
+ ```
80
+
81
+ See **[MANUAL.md](MANUAL.md)** for the complete end-to-end guide and the
82
+ physics reference card (every formula is mapped to the function that
83
+ implements it and the test that pins it).
84
+
85
+ ## Guarantees enforced by the test suite (150 tests)
86
+
87
+ - Glauber entropy-production ledger is exact: telescoping identity to
88
+ machine precision, and the **integral fluctuation theorem**
89
+ ⟨e^(−Δs_tot)⟩ = 1 verified against an exactly-enumerated 4×4 ensemble.
90
+ - Bennett pebbling drops chain erasure **N·b → b** at the exactly
91
+ predicted time/space cost (property-based).
92
+ - Planner prediction matches an independent simulated adiabatic backend
93
+ within 2%; reversible-heavy benchmarks reach ≥5× dissipation reduction
94
+ at 2× latency.
95
+ - Certificates are only ever marked valid in distribution-sound modes.
96
+
97
+ ## Licensing & PRO tier
98
+
99
+ OSS-first, fail-open: **profiling, reports, taxonomy, and unsigned
100
+ certificates are free** and never require a license. The enterprise
101
+ surfaces are PRO-gated with the same offline ed25519 license the EnergyIR
102
+ suite uses (`ENERGYIR_LICENSE` env var or `~/.energyir/license`; no
103
+ network, no phone-home):
104
+
105
+ | Surface | Tier |
106
+ |---|---|
107
+ | `profile` (worstcase + empirical), reports, HTML, headroom | free |
108
+ | certificates (emitted, unsigned) | free |
109
+ | **signed** certificates | PRO |
110
+ | `rewrite()` — R1 substitutions / R2 uncompute / R3 Bennett pebbling | PRO |
111
+ | `plan()` — adiabatic execution planning | PRO |
112
+
113
+ A PRO license minted by the EnergyIR portal for ThermoIR unlocks erasure
114
+ too — same token, same entitlement. When the `energyir` package is
115
+ installed alongside, license verification delegates to it
116
+ (`energy.intelligence.licensing`) so there is exactly one code path.
117
+ See `integration/ENERGYIR_INTEGRATION.md` for the (tiny) suite-side hookup.
118
+
119
+ ## Provenance
120
+
121
+ This project is standalone: ThermoIR (the EnergyIR item-4 prototype) is
122
+ vendored as a frozen snapshot under `src/thermoc/_vendor/thermoir/`; no
123
+ production EnergyIR code paths are imported or modified.
124
+
125
+ License: proprietary — see [LICENSE](LICENSE).