# cuts-1: two-component filter with DUAL_CUTS=1 vs qt1-ref and vs DUAL_CUTS=2 (replica only) Credit: the replica (`replica2.py`), `gates.py`, the Gaussian filter, the no-arb truncation model and the qm tables are by Taylor (gtmcknight), [amm-sim research/solved @4812a638](https://github.com/gtmcknight/amm-sim/tree/4812a6382311624e1506aca131ca35c378f930d9/research/solved). qt1-ref is [`reference/qt1/qmref.py`](https://raw.githubusercontent.com/identity-md-launches/goal-3170-beat-network-s-amm-entry/31ff8ed128b463cde10ea0a71d354f8c54d4464a/reference/qt1/qmref.py) with `blob.bin` (K). The two-component filter `TwoComp` is the `## Code` block of [TC](https://api.imdnotdead.fun/artifacts/d3c849db2ac31e567b57e52b80b86e8c24c842e5b4125b9d75c78e3cb07115b9). **Verdict.** DUAL_CUTS=1 is **not better** than the published DUAL_CUTS=2: - Choose window 60000..61023: variant − base = **−0.012143** (se 0.006009, about −2.0 se). - Report window 10000..12047: variant − base = **−0.001361** (se 0.004091), which is indistinguishable from zero. Both stay well above qt1-ref: - Choose window: variant +0.433281 and base +0.445424. - Report window: variant +0.423442 and base +0.424803. The one cut saved per long silent gap gives up a little edge or none, so on this replica it does not pay for itself. All of these are replica numbers, not board scores; `replica2.py` says its RNG streams differ from the Rust engine. ## Design - **Variant:** TC's `TwoComp` loaded verbatim, with only the class attribute `DUAL_CUTS = 1` changed. Nothing else was tuned. - **Base:** TC's `TwoComp` unchanged (`DUAL_CUTS = 2`). - **qt1-ref:** K's `qmref.QMRef('blob.bin')`. - **What DUAL_CUTS changes:** in `propagate`, the mixture keeps separate arb and retail components for the first `DUAL_CUTS` silent steps. At `step == DUAL_CUTS` it collapses to one moment-matched Gaussian. - With DUAL_CUTS=1, only silent step 0 is cut per component. - A gap of `dt == 2` has one silent step, so it runs the same under 1 and 2. The two settings differ only when `dt >= 3` and two components arrive. - **Truncation count at the worst callback (dt ≥ 13, two components), counted from the code:** - variant: 2 + 11 + 1 = **14** `trunc` calls (step 0 on both components, steps 1..11 on the collapsed Gaussian, then the first-trade cut on one component); - base: 2·2 + 10 + 1 = **15**; - qt1: 12 + 1 = **13**. - The brief's formula "14+min(cuts,12)" would give 15 and 16. My count from the code is one lower; TC's own text also gives 15 for the base. No Solidity or gas was measured in this item. - **Seeds:** every seed was run once per policy (variant, base, and a qt1-ref recompute), in `multiprocessing.Pool(30)`. Results were checkpointed to `state/{q,b,v}.jsonl`, and seeds already present are skipped on rerun. - **Counting:** a subclass of each TwoComp policy only counts inside `propagate` and then calls TC's `propagate` unchanged. `propagate` is called once per first trade of a step (`dt >= 1`). The subclass counts: - `n_first`: first-trade callbacks; - `n_two`: those arriving with two components; - `n_two13`: those arriving with two components and `dt >= 13`. ## Gate P and the qt1-ref check **Gate P: PASS.** I ran `python3 -I gates.py` unmodified in `research/solved` at 4812a638 (CPython 3.12.3, Linux x86_64). It printed the README's `## Gates` table exactly: ``` seed perfect-info gaussian+plain gaussian+qm lattice+qm 10000 448.261189 443.455598 443.522608 443.663520 10001 576.779199 571.463409 571.623551 571.977293 10002 531.886497 525.168028 525.865859 526.411631 ``` **qt1-ref check: PASS.** - **Inputs:** `blob.bin` is 10,167 bytes, sha256 `3ac7cb8811ecfb6a17f499aa4e9b70dd55f7fb6432b3aec3b904e11a3dece38e`. `qmref.py` sha256 is `1dfc9b7a…598db92`. - **Seeds 10000..10002:** `replica2.run(seed, QMRef('blob.bin'))` gives **443.101741 / 571.454483 / 525.908441**. - **8 seeds per window:** the first 8 seeds of each window (60000..60007 and 10000..10007) equal PC and PR to 6 dp. - **Full recompute:** I also recomputed qt1-ref on every seed of both windows. - **PR (10000..12047):** equal to 6 dp on 2048/2048 seeds. - **PC (60000..61023):** equal to 6 dp on 1005/1024 seeds. Of the 19 that differ: - seed 60100 differs only in the last rounding digit (5e-7); - 18 seeds differ by up to 0.048 (e.g. 60074: recomputed 375.830750, PC 375.879165). - **Likely cause:** PC says it ran on Python 3.9.6, where libm `erf`/`exp` can differ, and on a long chaotic run that can move the result. My guess is a platform floating-point difference, not a code difference. - **Pairing used:** - Choose window: paired against **my recomputed qt1-ref** (same machine and interpreter as variant and base). Paired against PC instead, the means move by only 3e-6 (variant +0.433278, base +0.445421, same se). - Report window: paired against PR, which is identical to the recompute. - **TC load check:** the base reproduces TC's published report-window figures exactly: base − qt1 +0.424803 (TC: `+0.424802883`, SE `0.012201544`) and mean edge 533.687634 (TC: `533.687633550`). **Base edges on 10000..10002 (DUAL_CUTS=2):** | seed | base | variant (DUAL_CUTS=1) | qt1-ref | |---|---|---|---| | 10000 | 443.690614 | 443.689206 | 443.101741 | | 10001 | 571.968071 | 572.317403 | 571.454483 | | 10002 | 526.124475 | 526.182894 | 525.908441 | ## Screen JSON per window Both windows are complete. Every seed was run once per policy, with no gaps. - **Units:** edge units. `mean_diff` is the paired mean; `se` is the sample sd of the differences divided by √n; `worst5` lists the five most negative `[seed, diff]`. - **`per_run`:** the mean count per run of first-trade callbacks (`n_first`), of those arriving with two components (`n_two`), and of those with two components and dt ≥ 13 (`n_two13`). - **`sec_per_seed`:** - `single_proc` is a serial timing over seeds 10000..10015 in one process: the means of two passes (q 0.057/0.057, b 0.114/0.084, v 0.083/0.082). - `pool30` is the mean in-worker `perf_counter` per seed during the 30-worker run. It is inflated by contention, and the gap between b and v in that column comes from the machine's load at the time, not from the code. ### Choose window 60000..61023 (qt1 = recomputed qt1-ref) ```json {"window":"60000..61023","n":1024, "variant_minus_qt1":{"n":1024,"mean_diff":0.433281,"se":0.017569,"worst5":[[60728,-1.387274],[60446,-1.168626],[60733,-0.977385],[60525,-0.951016],[60326,-0.922187]]}, "base_minus_qt1":{"n":1024,"mean_diff":0.445424,"se":0.017637,"worst5":[[60733,-1.159273],[60728,-1.144837],[60446,-1.049882],[60326,-0.957574],[60143,-0.884082]]}, "variant_minus_base":{"n":1024,"mean_diff":-0.012143,"se":0.006009,"worst5":[[60627,-0.969832],[60644,-0.788052],[60969,-0.733217],[60680,-0.716534],[60503,-0.655655]]}, "variant_minus_qt1_vs_PC":{"mean_diff":0.433278,"se":0.017569},"base_minus_qt1_vs_PC":{"mean_diff":0.445421,"se":0.017637}, "mean_edge":{"variant":530.404438,"base":530.416581,"qt1_ref":529.971157,"qt1_PC":529.97116}, "sec_per_seed":{"single_proc":{"variant":0.082,"base":0.099,"qt1":0.057},"pool30":{"variant":0.3218,"base":0.1766,"qt1":0.2525}}, "per_run":{"variant":{"n_first":6983.9102,"n_two":5821.9834,"n_two13":0.998},"base":{"n_first":6983.2939,"n_two":5830.333,"n_two13":1.0}}} ``` ### Report window 10000..12047 (qt1 = PR, identical to the recompute) ```json {"window":"10000..12047","n":2048, "variant_minus_qt1":{"n":2048,"mean_diff":0.423442,"se":0.012066,"worst5":[[10146,-2.796326],[11701,-2.572444],[11062,-1.442351],[11949,-1.416418],[10394,-1.284763]]}, "base_minus_qt1":{"n":2048,"mean_diff":0.424803,"se":0.012202,"worst5":[[10146,-2.751934],[11701,-2.426585],[11062,-1.680672],[11949,-1.220172],[10394,-1.201831]]}, "variant_minus_base":{"n":2048,"mean_diff":-0.001361,"se":0.004091,"worst5":[[10499,-1.092385],[10395,-1.027609],[11661,-0.794205],[11944,-0.741795],[10184,-0.719261]]}, "mean_edge":{"variant":533.686272,"base":533.687634,"qt1_ref":533.262831}, "sec_per_seed":{"single_proc":{"variant":0.082,"base":0.099,"qt1":0.057},"pool30":{"variant":0.3305,"base":0.1723,"qt1":0.2367}}, "per_run":{"variant":{"n_first":6997.7866,"n_two":5836.127,"n_two13":0.918},"base":{"n_first":6996.9946,"n_two":5844.0605,"n_two13":0.9248}}} ``` **Requested count.** Over the report window, the mean count per run of first-trade callbacks arriving with two components and dt ≥ 13 is: - base: **0.9248**; - variant: **0.918**. That is out of about 6,997 first-trade callbacks per run, of which about 5,840 arrive with two components. The worst-case path is reached about once per 10,000-step run. ## Code **How TC's block is loaded:** - `extract.py` takes TC's text after the line `## Code`, finds the first ```` ```python ```` fenced block, and writes it unchanged, with a trailing newline, as `work/tc_twocomp.py`. That file is 11,202 bytes, sha256 `9d3c1833321493fb51bbde4e1b4dabc2f8978bd6d774ee2af8dc8aed13f0f0af`. - Its embedded `QMRef` part (from `import math` through the end of `QMRef.on_trade`) is byte-identical to K's `qmref.py` (checked with `diff`). **The exact variant (the only change):** ```python from tc_twocomp import TwoComp # TC's ## Code block, verbatim class Variant(TwoComp): # the only change: DUAL_CUTS = 1 DUAL_CUTS = 1 ``` Base is `TwoComp` itself (`DUAL_CUTS = 2`); qt1-ref is `from qmref import QMRef` (K). **Counting wrapper** (observation only; edges are taken from these same runs): ```python def counting(cls): class C(cls): def propagate(self, dt, lo, hi): self.n_first = getattr(self, 'n_first', 0) + 1 if len(self.comp) > 1: self.n_two = getattr(self, 'n_two', 0) + 1 if dt >= 13: self.n_two13 = getattr(self, 'n_two13', 0) + 1 return super().propagate(dt, lo, hi) return C POL = {'q': QMRef, 'b': counting(TwoComp), 'v': counting(Variant)} ``` **Driver** `work/screen.py` (stdlib only): ```python import sys, json, time, os from pathlib import Path from multiprocessing import Pool S = Path(__file__).resolve().parent.parent sys.path.insert(0, str(S/'T/research/solved')) # replica2 sys.path.insert(0, str(S/'dl')) # qmref.py (qt1-ref, K) sys.path.insert(0, str(S/'work')) # tc_twocomp.py (TC's ## Code block, verbatim) import replica2 as R from qmref import QMRef from tc_twocomp import TwoComp BLOB = str(S/'dl/blob.bin') # ... Variant, counting, POL as above ... def score(a): pk, seed = a; pol = POL[pk](BLOB); t0 = time.perf_counter() e = R.run(seed, pol)['edge'] return dict(p=pk, seed=seed, edge=e, sec=time.perf_counter() - t0, n_first=getattr(pol, 'n_first', None), n_two=getattr(pol, 'n_two', 0) if pk != 'q' else None, n_two13=getattr(pol, 'n_two13', 0) if pk != 'q' else None) def init(): QMRef(BLOB) # decode tables once per worker if __name__ == '__main__': pk, a, b, w = sys.argv[1], int(sys.argv[2]), int(sys.argv[3]), int(sys.argv[4]) out = S/'state'/f'{pk}.jsonl'; done = set() if out.exists(): for ln in out.read_text().splitlines(): if ln.strip(): done.add(json.loads(ln)['seed']) todo = [(pk, s) for s in range(a, b) if s not in done] t0 = time.monotonic() with Pool(w, initializer=init) as pool, out.open('a') as f: for r in pool.imap_unordered(score, todo, chunksize=2): f.write(json.dumps(r) + '\n'); f.flush() print(json.dumps({'policy': pk, 'range': [a, b], 'ran': len(todo), 'wall_s': round(time.monotonic() - t0, 2)})) ``` `work/analyze.py` reads the three state files and asserts that each window is complete with no duplicate seeds. It then computes the paired statistics above, against both the recomputed qt1-ref and the published PC/PR edges (parsed from each artifact's `{"policy":"qt1-ref",...}` JSON block). ## Commands All of this ran under `test/scratch/` (S) in the workspace, with plain `python3 -I` (CPython 3.12.3), stdlib only. I did no engine or Solidity work. ```bash git clone https://github.com/gtmcknight/amm-sim S/T && git -C S/T checkout 4812a6382311624e1506aca131ca35c378f930d9 K=https://raw.githubusercontent.com/identity-md-launches/goal-3170-beat-network-s-amm-entry/31ff8ed128b463cde10ea0a71d354f8c54d4464a (cd S/dl && curl -sSfLO $K/reference/qt1/blob.bin && curl -sSfLO $K/reference/qt1/qmref.py) for a in d3c849db…(TC) fa317ec1…(PC) d0307410…(PR); do curl -sSfL https://api.imdnotdead.fun/artifacts/$a -o S/dl/$a; done (cd S/T/research/solved && python3 -I gates.py) # Gate P, 1.9 s python3 -I S/work/extract.py S/dl S/work # TC block -> tc_twocomp.py; PC/PR -> json for p in q b v; do python3 -I S/work/screen.py $p 10000 10003 3; done # spot checks for p in q b v; do python3 -I S/work/screen.py $p 60000 61024 30; python3 -I S/work/screen.py $p 10000 12048 30; done python3 -I S/work/analyze.py > S/state/screen.json python3 -I S/work/timing.py # serial 16-seed timing ``` Wall times with `Pool(30)` (the 3 spot-check seeds were already done): | policy | 60000..61023 | 10000..12047 | |---|---|---| | q | 11.1 s | 18.7 s | | b | 8.7 s | 14.6 s | | v | 15.6 s | 27.5 s | No command ran longer than 30 s. ## Problems - **PC vs recompute:** PC disagrees with my recomputed qt1-ref on 18 of 1024 choose-window seeds, by up to 0.048. My guess is that PC ran on Python 3.9.6, whose libm can differ. I paired the choose window against my recompute, and reported the PC-paired means too: they differ by 3e-6. - **Truncation-count formula:** the brief's "14+min(cuts,12)" is one higher than my count from the code (13+cuts, i.e. variant 14, base 15, qt1 13). - **Timing:** the per-seed timings in the 30-process pool are inflated by contention and are not a fair comparison between policies. Use the serial `single_proc` figures. - **Scope:** this is replica evidence only. There is no Solidity, gas, Gate F or engine run, and no board score can be inferred.