From ff4b3de2dba5a89b33bba6f489ed906e31ddbd12 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 20 Oct 2026 02:50:29 +0000 Subject: [PATCH] Price one (N-P) tensor M consumer at 199680 unit additions. The straight-line shear program matches (N-P)XM on the pinned P104 fixture. Its bill exceeds PR3996's two-pair Gram saving of 110592 additions, so that gap is not yet a paid saving. No new kappa. Co-authored-by: unpaidmoon --- research/p104-omitted-side-consumer/README.md | 119 +++++++++ .../p104-omitted-side-consumer/SOURCES.json | 24 ++ research/p104-omitted-side-consumer/TASK.json | 94 +++++++ .../p104-omitted-side-consumer/VERIFY.json | 29 +++ .../p104-omitted-side-consumer/test_verify.py | 53 ++++ research/p104-omitted-side-consumer/verify.py | 232 ++++++++++++++++++ 6 files changed, 551 insertions(+) create mode 100644 research/p104-omitted-side-consumer/README.md create mode 100644 research/p104-omitted-side-consumer/SOURCES.json create mode 100644 research/p104-omitted-side-consumer/TASK.json create mode 100644 research/p104-omitted-side-consumer/VERIFY.json create mode 100644 research/p104-omitted-side-consumer/test_verify.py create mode 100644 research/p104-omitted-side-consumer/verify.py diff --git a/research/p104-omitted-side-consumer/README.md b/research/p104-omitted-side-consumer/README.md new file mode 100644 index 0000000000..69b984785c --- /dev/null +++ b/research/p104-omitted-side-consumer/README.md @@ -0,0 +1,119 @@ +# One straight-line consumer of `(N-P) ⊗ M` + +**Partial scalar bill. No new κ.** Selected κ stays `0.001672660197076918`. + +Discussion comment 19056819 asked for a price of one repeated consumer of +`(N-P) ⊗ M` before the Gram addition saving is treated as paid. This package +gives that price for one 104×104 bank, in the unit-addition model of PR #3996. +It is not a minimum, a native rank bill, or a supplier. + +## Scope + +The card `p104-side-correction` asks for a chronological endpoint correction. +Scope is **changed**. The object here is the omitted identity side named in +PR #3996's proof, not a flag program and not a replay of its 763,392 events. + +On the canonical fixture +`research/p104-native-channel/inputs/P104-FIXTURE.json` +(SHA256 `516c06f6ec3cb84be588c546108de897650288576dd5fbf5fe44b9c5546ad8a4`), +the ordered ports give an 8×96 matrix `B2` whose ASCII digest is +`88e9a726e8c8aec055bfd7cd024883b5c8ecbda0cd6471e591a08c43a108a535`. +Every entry is in `{0, ±1}`, there are 384 nonzeros, each row has weight 48, +each column has weight 4, and `B2 B2^t = 48 I`. Write `B = B2/2`, +`U = [I, B; 0, I]`, `A = U^t P U`, `M = U U^t`, and +`N - P = U^{-t}(I - A)U^{-1}`. The checker re-derives `A^2 = 13A`. + +The index convention of PR #3996 sends `(N-P) ⊗ M` to the bank map +`(N-P) X M`, because `M` is symmetric. One straight-line program computes it +by the shears: + +- right-multiply by `M = U U^t` with two `B2` matvecs per row (768 additions); +- left-multiply by `N-P` with three `B2` matvecs per column (1152 additions). + +The closed form used for the second step is + +```text +(N-P)(v1, v2) = (-B v2, v2 - B^t v1 + B^t B v2). +``` + +`B v2` is stored once and reused. Each matvec applies every nonzero of `B2` +once, as a `±1` multiple of one coordinate. The program is straight-line: +every bank pays the same count, + +```text +104 * (2 + 3) * 384 = 199680 +``` + +unit additions. Dyadic divisions by 2 and 4 restore the scale. They are not +included in this count, and the half/double column of PR #3996 is not +re-derived. The same program matches the integer operators `4(N-P)` and `4M` +on the all-ones bank, a single-spike bank, and one dense modular bank. +Dropping `B^t B v2` breaks that identity. The zero bank is the zero bank and +still executes 199680 additions. + +## Comparison with the published Gram gap + +PR #3996's unit-addition invoices are entry plus exit `319488`, one coded +Gram pair `26240`, and one specified reference pair `241280`. For `k` +uncorrected pairs the gap is + +```text +241280 k - 319488 - 26240 k = 215040 k - 319488. +``` + +That is `-104448` at one pair and **`110592` at two pairs**. Paying this +consumer once against the two-pair saving leaves + +```text +110592 - 199680 = -89088. +``` + +At three pairs the same one-time payment leaves `+125952`. The other omitted +side `M ⊗ (N-P)` has the same straight-line count by swapping axes. Charging +both, once each, costs `399360` additions. One coded pair plus both sides is +`425600` additions, which is already above one reference pair (`241280`), +before the entry/exit cost. + +This does **not** prove that every program for `(N-P) ⊗ M` costs at least +`199680`, and it does not price native children, owners, or frames. A cheaper +program remains the first open question. Until one exists, the two-pair Gram +gap is not a paid saving. + +## Reproduce + +Python 3.12, standard library, from the repository root: + +```sh +python3.12 -B research/p104-omitted-side-consumer/verify.py +python3.12 -B research/p104-omitted-side-consumer/test_verify.py +python3.12 -B scripts/research_package.py --package research/p104-omitted-side-consumer +``` + +Expected marker: + +```text +PASS_P104_OMITTED_SIDE_CONSUMER +``` + +with `adds_one_side` 199680, `k2_net_after_one_side` -89088, and +`new_kappa` false. + +## Open next questions + +1. Is there a straight-line program for one bank of `(N-P) ⊗ M` with fewer + than 110592 unit additions in this `B2` model? The number above is one + program, not a lower bound. +2. Can the two omitted sides share shears? Independent charging may + double-count, which matters for the comparison with one 241280-addition + reference pair. + +## Credit + +The omitted-side formulas, the index, and the 110592-addition gap are ToikY's +PR #3996 audit of Chris R / Clear1nsight's PR #3979. The fixture and root +normalization are the retained P104 native channel (Rohan Arun, Chafik +Boukhalfa, Adam Gall, and the native/E8 lineage named there). The request to +price the consumer is discussion comment 19056819. + +Prepared by unpaidmoon with substantial Cursor Grok assistance. Apache-2.0. +No independent human review is claimed. diff --git a/research/p104-omitted-side-consumer/SOURCES.json b/research/p104-omitted-side-consumer/SOURCES.json new file mode 100644 index 0000000000..6441450d9e --- /dev/null +++ b/research/p104-omitted-side-consumer/SOURCES.json @@ -0,0 +1,24 @@ +{ + "schema_version": 1, + "base_commit": "bb4bccbf42844d2801487450727c2f63e10d1d0f", + "files": [ + { + "path": "certificates/selected-result.json", + "usage": "provenance", + "git_blob": "b1fea0c94436b236cc2b7d41a7be032811b94c81", + "sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129" + }, + { + "path": "docs/research/agent-task-catalog.json", + "usage": "provenance", + "git_blob": "67f085acd86acd7d4e03922d9cf4b26bca66fed5", + "sha256": "47b42433f5a85809a3aadd29c2c339da2c6ed728e4f7762df6b88bccb7874cf0" + }, + { + "path": "research/p104-native-channel/inputs/P104-FIXTURE.json", + "usage": "input", + "git_blob": "2e16f99b295bb386fd13bd8fbd57b42ef54143a9", + "sha256": "516c06f6ec3cb84be588c546108de897650288576dd5fbf5fe44b9c5546ad8a4" + } + ] +} diff --git a/research/p104-omitted-side-consumer/TASK.json b/research/p104-omitted-side-consumer/TASK.json new file mode 100644 index 0000000000..460d733db6 --- /dev/null +++ b/research/p104-omitted-side-consumer/TASK.json @@ -0,0 +1,94 @@ +{ + "schema_version": 1, + "task_id": "p104-side-correction", + "priority_revision": 6, + "base_commit": "bb4bccbf42844d2801487450727c2f63e10d1d0f", + "original_card": { + "id": "p104-side-correction", + "question": "Can the proposed P104 joint endpoint flags be realized by a chronological correction program with coefficients, dirty restoration and an affordable complete bill?", + "starting_points": [ + "docs/research/research-family-status.md", + "research/p104-native-channel/README.md", + "research/p104-native-channel/PROOF.md", + "docs/research/community-efficient1000-review.md", + "research/p104-joint-unit-flags/README.md" + ], + "methods": [ + "Construct one side-correction word at an explicit chronological cut with the needed frames still available.", + "Derive a lower bound on the side correction and hosting costs that decides whether this channel can beat the selected budget." + ], + "acceptance_gate": "An exact arbitrary-input identity, source/target frame availability and restoration at the chosen cut, plus the complete channel+correction+idle/hosting bill or an explicit favorable bound. The native channel alone is not a supplier. A scoped local result may be retained without a complete supplier. Expanding it into further construction rounds requires the family-level budget and obligation gate in research-family-status.md; an optimistic passing relaxation alone is not acceptance.", + "first_step": { + "command": [ + "python3", + "-B", + "scripts/research_baseline.py" + ], + "expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY", + "inspect": "Read the revision-6 family record and #2981 static joint flags, alongside the reviewed #1991 channel. #2981 is an author-checked source archive, not an independently validated executable correction or a refutation of program-level lifetime floors.", + "scope": "Source-bound orientation only. Read the named package and review before relying on its local evidence; this command is not its replay or supplier admission." + }, + "first_experiment": "After checking live handoffs, pin one named collection from #2981 and attach acquisition/delivery times, source dependencies and coefficient requirements. Test legal access and arbitrary dirty restoration; price any completion against the full channel/endpoint/hosting allowance. Stop with the exact missing implication or a scoped counterexample rather than expanding the static cover census.", + "bottleneck_link": "Completing an already realized changed channel tests a concrete construction opportunity, instead of refining the incumbent packing. A positive local result still needs global and all-size admission.", + "allowed_variations": [ + "One correction factorization, one chronological cut, or one explicit hosting obstruction.", + "A changed correction schedule with an updated full bill and native frame contract." + ], + "scope_change_triggers": [ + "Recompiling the already certified low-rank channel without closing a new obligation.", + "Treating a pending ancestor, local histogram or unpriced side correction as an admitted global supplier.", + "Repeating a correction or physical conversion already supplied by a pending follow-up without adding independent coverage or a different unresolved obligation.", + "Repeating the 208-flag static matching or calling a static nested cover a chronological correction program. Independent audit of that certificate is distinct and remains useful." + ], + "overlap_terms": [ + "P104", + "I-G/2", + "side correction", + "native channel", + "1972", + "1991", + "2586", + "endpoint transport", + "physical dual", + "relay composition", + "2981", + "joint unit flags", + "acquisition delivery", + "static nesting" + ], + "existing_checks": [ + { + "scope": "PR #2981 reports static joint endpoint flags and a matching of 208 departure and arrival flags; archival intake is not independent verification.", + "evidence": "research/p104-joint-unit-flags/README.md", + "remaining": "Independent flag check and a chronological coefficient program with dirty restoration and a complete cost.", + "status": "author-checked", + "source_commit": "3d24b92d8ba057b57cb2d480170ec407520e6882", + "selected_record_sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129" + } + ] + }, + "scope_status": "changed", + "actual_scope": "Price one straight-line consumer of (N-P) tensor M on one 104 by 104 bank, in the PR3996 unit-addition model, and compare that bill with the two-pair uncorrected Gram saving.", + "scope_change_reason": "The catalogue card asks for a chronological endpoint-flag correction with a complete channel bill. The open question left by discussion comment 19056819 is narrower: the two-pair Gram addition saving is not a paid saving until some consumer of the omitted side (N-P) tensor M is priced. This package prices that one side. It does not build the flag program.", + "bottleneck_link": "A repeated Gram saving changes the complex supplier only if the omitted identity side fits inside it. The straight-line bill shows that one application costs more unit additions than the two-pair saving, so that saving is not yet a paid profile improvement.", + "budget_minutes": 60, + "stop_rule": "Stop after the straight-line B2 program matches (N-P)XM on the pinned fixture and the addition comparison is checked. Do not replay the 763392 events or start a native supplier.", + "outcome": "partial", + "evidence": "PASS_P104_OMITTED_SIDE_CONSUMER: 199680 B2 additions for one side, two-pair Gram saving 110592, net -89088 at two pairs. Both sides together cost 399360 additions, above one uncoded reference pair.", + "author": "unpaidmoon", + "ai_assistance": "Cursor Grok 4.7 wrote the checker and the comparison. The geometry and the 110592 saving are from the credited predecessors.", + "open_questions": [ + { + "question": "Is there a straight-line program for one bank of (N-P) tensor M that uses fewer than 110592 unit additions in this B2 coefficient model?", + "why_it_matters": "199680 is the cost of this factorization, not a proved minimum. A cheaper program could still fit inside the two-pair saving.", + "first_step": "Replace either the two M shears or the three N-P matvecs by a program whose addition count is below 110592 and check it on the same three banks.", + "blocked_by": "none" + }, + { + "question": "Can the two omitted sides share shears so that charging 2 times 199680 double-counts work?", + "why_it_matters": "PR3996's identity needs both (N-P) tensor M and M tensor (N-P). Shared work would change the both-sides comparison with the 241280-addition reference pair.", + "first_step": "Write one program that emits both sides on the same bank and count additions that are used by both outputs.", + "blocked_by": "none" + } + ] +} diff --git a/research/p104-omitted-side-consumer/VERIFY.json b/research/p104-omitted-side-consumer/VERIFY.json new file mode 100644 index 0000000000..f6be8d4d75 --- /dev/null +++ b/research/p104-omitted-side-consumer/VERIFY.json @@ -0,0 +1,29 @@ +{ + "schema_version": 1, + "status": "ready", + "python": "3.12", + "requirements": [], + "inputs": [ + "research/p104-native-channel/inputs/P104-FIXTURE.json" + ], + "commands": [ + { + "argv": [ + "{python}", + "-B", + "{package}/verify.py" + ], + "timeout_seconds": 120, + "checks": "B2 digest and weights, A^2=13A, three banks equal (N-P)XM, straight-line count 199680, two-pair net -89088, dropped B^t B term rejected." + }, + { + "argv": [ + "{python}", + "-B", + "{package}/test_verify.py" + ], + "timeout_seconds": 120, + "checks": "Zero bank still pays 199680, one flipped B2 entry fails the Gram identity, omitting B^t B breaks the product, and the two-pair saving stays negative after one side." + } + ] +} diff --git a/research/p104-omitted-side-consumer/test_verify.py b/research/p104-omitted-side-consumer/test_verify.py new file mode 100644 index 0000000000..9298c6a68e --- /dev/null +++ b/research/p104-omitted-side-consumer/test_verify.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +"""Failure controls for the omitted-side addition bill.""" +from pathlib import Path +import sys +import unittest + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +import verify + + +class ConsumerBillTests(unittest.TestCase): + def setUp(self): + self.b2 = verify.load_b2() + verify.check_b2(self.b2) + self.side, self.moment = verify.scaled_operators(self.b2) + + def test_three_banks_match_and_share_one_bill(self): + counts = [] + for matrix in verify.banks(): + output, adds = verify.consume_bank(self.b2, matrix) + self.assertEqual(output, verify.reference_bank(self.side, self.moment, matrix)) + counts.append(adds) + self.assertEqual(counts, [199680, 199680, 199680]) + + def test_zero_bank_is_zero_and_still_pays_the_straight_line(self): + zero = [[0 for _ in range(verify.N)] for _ in range(verify.N)] + output, adds = verify.consume_bank(self.b2, zero) + self.assertEqual(output, verify.reference_bank(self.side, self.moment, zero)) + self.assertTrue(all(entry == 0 for row in output for entry in row)) + self.assertEqual(adds, 199680) + + def test_omitting_btb_breaks_the_identity(self): + matrix = verify.banks()[0] + output, adds = verify.consume_bank(self.b2, matrix, omit_btb=True) + reference = verify.reference_bank(self.side, self.moment, matrix) + self.assertNotEqual(output[8][0], reference[8][0]) + self.assertEqual(adds, 199680 - verify.N * 384) + + def test_flipping_one_b2_entry_breaks_the_gram_identity(self): + mutated = [row[:] for row in self.b2] + mutated[0][0] = 0 if mutated[0][0] else 1 + with self.assertRaises(SystemExit): + verify.check_b2(mutated) + + def test_two_pair_saving_does_not_pay_for_one_side(self): + self.assertEqual(verify.gram_add_saving(2), 110592) + self.assertLess(verify.gram_add_saving(2) - 199680, 0) + self.assertGreater(verify.gram_add_saving(3) - 199680, 0) + self.assertGreater(verify.CODED_PAIR_ADDS + 2 * 199680, verify.REFERENCE_PAIR_ADDS) + + +if __name__ == "__main__": + unittest.main() diff --git a/research/p104-omitted-side-consumer/verify.py b/research/p104-omitted-side-consumer/verify.py new file mode 100644 index 0000000000..8d6caad4e9 --- /dev/null +++ b/research/p104-omitted-side-consumer/verify.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python3 +"""Straight-line unit-addition bill for one bank of (N-P) tensor M. + +The canonical P104 fixture determines B2. PR #3996 states +N-P = U^{-t}(I-A)U^{-1} and M = UU^t, and the two-pair uncorrected Gram +saving of 110592 unit additions. This checker builds the shear program for +one application of (N-P) tensor M and counts its B2 coefficient additions. +It does not replay the 763392 literal events and does not claim a minimum. +""" +from __future__ import annotations + +import hashlib +import itertools +import json +from fractions import Fraction +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +FIXTURE = ROOT / "research/p104-native-channel/inputs/P104-FIXTURE.json" +FIXTURE_SHA256 = "516c06f6ec3cb84be588c546108de897650288576dd5fbf5fe44b9c5546ad8a4" +B2_SHA256 = "88e9a726e8c8aec055bfd7cd024883b5c8ecbda0cd6471e591a08c43a108a535" +N = 104 +PIVOTS = (0, 1, 3, 6, 22, 31, 93, 103) +ENTRY_ADDS = 319488 +CODED_PAIR_ADDS = 26240 +REFERENCE_PAIR_ADDS = 241280 +MARGINAL_ADDS = REFERENCE_PAIR_ADDS - CODED_PAIR_ADDS # 215040 + + +def require(condition, message): + if not condition: + raise SystemExit("FAIL: " + message) + + +def load_b2(): + raw = FIXTURE.read_bytes() + require(hashlib.sha256(raw).hexdigest() == FIXTURE_SHA256, "canonical P104 fixture") + section = json.loads(raw)["section4_explicit_P104"] + roots = set() + for coords in itertools.product((-2, 0, 2), repeat=8): + support = [value for value in coords if value] + if len(support) == 2 and support[0] > 0: + roots.add(coords) + for coords in itertools.product((-1, 1), repeat=8): + if coords[0] == 1 and coords.count(-1) % 2 == 0: + roots.add(coords) + removed = {tuple(row) for row in section["F0"] + section["C"]} + require(len(roots) == 120 and len(removed) == 16 and removed <= roots, "E8 root domain") + original = sorted(roots - removed) + order = list(PIVOTS) + [index for index in range(N) if index not in PIVOTS] + ports = [original[index] for index in order] + gram = [[sum(left * right for left, right in zip(row, other)) for other in ports] for row in ports] + b2 = [[gram[i][8 + j] // 4 for j in range(96)] for i in range(8)] + digest = hashlib.sha256(json.dumps(b2, separators=(",", ":")).encode("ascii")).hexdigest() + require(digest == B2_SHA256, "ordered B2 digest") + return b2 + + +def check_b2(b2): + values = {entry for row in b2 for entry in row} + require(values <= {0, 1, -1}, "B2 entries are not in {0, ±1}") + nonzero = sum(entry != 0 for row in b2 for entry in row) + require(nonzero == 384, "B2 nonzero count") + require(all(sum(entry != 0 for entry in row) == 48 for row in b2), "B2 row weight") + require(all(sum(b2[i][j] != 0 for i in range(8)) == 4 for j in range(96)), "B2 column weight") + for i in range(8): + for j in range(8): + product = sum(b2[i][k] * b2[j][k] for k in range(96)) + require(product == 48 * int(i == j), "B2 B2^t = 48 I") + return nonzero + + +def scaled_operators(b2): + """side = 4(N-P) and moment = 4M, with M = UU^t and B = B2/2.""" + upper = [[2 if i == j else 0 for j in range(N)] for i in range(N)] + inverse = [[2 if i == j else 0 for j in range(N)] for i in range(N)] + for i in range(8): + for j in range(96): + upper[i][8 + j] = b2[i][j] + inverse[i][8 + j] = -b2[i][j] + pivot = [[1 if i == j < 8 else 0 for j in range(N)] for i in range(N)] + + def matmul(left, right): + columns = list(zip(*right)) + return [[sum(x * y for x, y in zip(row, column)) for column in columns] for row in left] + + def transpose(matrix): + return [list(row) for row in zip(*matrix)] + + gram_scale = matmul(matmul(transpose(upper), pivot), upper) # 4A + squared = matmul(gram_scale, gram_scale) + require(squared == [[52 * entry for entry in row] for row in gram_scale], "A^2 = 13A") + left = [[4 * int(i == j) - gram_scale[i][j] for j in range(N)] for i in range(N)] + sixteen = matmul(matmul(transpose(inverse), left), inverse) # 16(N-P) + require(all(entry % 4 == 0 for row in sixteen for entry in row), "N-P scale") + side = [[entry // 4 for entry in row] for row in sixteen] + moment = matmul(upper, transpose(upper)) # 4M + return side, moment + + +def b2_times(b2, vector, counter): + output = [0] * 8 + for i in range(8): + total = 0 + for j in range(96): + coefficient = b2[i][j] + if coefficient: + total += coefficient * vector[j] + counter[0] += 1 + output[i] = total + return output + + +def b2_transpose_times(b2, vector, counter): + output = [0] * 96 + for j in range(96): + total = 0 + for i in range(8): + coefficient = b2[i][j] + if coefficient: + total += coefficient * vector[i] + counter[0] += 1 + output[j] = total + return output + + +def apply_m_to_row(b2, row, counter): + """Right-multiply one row by M = UU^t. Two shears, 768 B2 additions.""" + pivot = [Fraction(entry) for entry in row[:8]] + rest = [Fraction(entry) for entry in row[8:]] + mixed = b2_transpose_times(b2, pivot, counter) + sheared = [rest[j] + Fraction(mixed[j], 2) for j in range(96)] + back = b2_times(b2, sheared, counter) + return [pivot[i] + Fraction(back[i], 2) for i in range(8)] + sheared + + +def apply_np_to_column(b2, column, counter, omit_btb=False): + """Left-multiply one column by N-P. Three B2 matvecs, 1152 additions.""" + pivot = [Fraction(entry) for entry in column[:8]] + rest = [Fraction(entry) for entry in column[8:]] + image = b2_times(b2, rest, counter) # B2 rest = 2 B rest + composed = [0] * 96 if omit_btb else b2_transpose_times(b2, image, counter) + cross = b2_transpose_times(b2, pivot, counter) + out_pivot = [Fraction(-image[i], 2) for i in range(8)] + out_rest = [rest[j] - Fraction(cross[j], 2) + Fraction(composed[j], 4) for j in range(96)] + return out_pivot + out_rest + + +def consume_bank(b2, matrix, omit_btb=False): + """Apply (N-P) X M and return (matrix, B2-addition count).""" + counter = [0] + right = [apply_m_to_row(b2, row, counter) for row in matrix] + columns = [] + for j in range(N): + columns.append(apply_np_to_column( + b2, [right[i][j] for i in range(N)], counter, omit_btb=omit_btb)) + output = [[columns[j][i] for j in range(N)] for i in range(N)] + return output, counter[0] + + +def reference_bank(side, moment, matrix): + """(N-P) X M = side (X moment) / 16.""" + width = len(matrix) + moment_columns = list(zip(*moment)) + scaled = [] + for row in matrix: + scaled.append([sum(entry * moment_columns[column][k] + for k, entry in enumerate(row)) + for column in range(width)]) + product = [] + for i in range(width): + product.append([sum(side[i][k] * scaled[k][j] for k in range(width)) + for j in range(width)]) + return [[Fraction(entry, 16) for entry in row] for row in product] + + +def gram_add_saving(pairs): + """PR #3996 unit-ADD gap: reference pairs minus entry/exit minus coded pairs.""" + require(type(pairs) is int and pairs >= 0, "pair count") + return REFERENCE_PAIR_ADDS * pairs - ENTRY_ADDS - CODED_PAIR_ADDS * pairs + + +def banks(): + ones = [[1 for _ in range(N)] for _ in range(N)] + spike = [[0 for _ in range(N)] for _ in range(N)] + spike[0][0] = 1 + modular = [[(3 * i + 5 * j + 1) % 7 - 3 for j in range(N)] for i in range(N)] + return ones, spike, modular + + +def main(): + b2 = load_b2() + nonzero = check_b2(b2) + side, moment = scaled_operators(b2) + counts = [] + for matrix in banks(): + output, adds = consume_bank(b2, matrix) + require(output == reference_bank(side, moment, matrix), "consumer differs from (N-P)XM") + counts.append(adds) + require(counts == [199680, 199680, 199680], "addition count depends on the bank") + one_side = counts[0] + require(one_side == N * (2 * nonzero + 3 * nonzero), "5 matvecs times 104 times nnz") + require(gram_add_saving(1) == -104448, "one-pair ADD gap") + require(gram_add_saving(2) == 110592, "two-pair ADD saving") + require(MARGINAL_ADDS == 215040, "marginal ADD saving") + net_two = gram_add_saving(2) - one_side + net_three = gram_add_saving(3) - one_side + require(net_two == -89088 and net_three == 125952, "net after one side") + both_sides = 2 * one_side + require(CODED_PAIR_ADDS + both_sides > REFERENCE_PAIR_ADDS, "both sides versus one reference pair") + broken, broken_adds = consume_bank(b2, banks()[0], omit_btb=True) + require(broken != reference_bank(side, moment, banks()[0]), "dropped B^t B term still matched") + require(broken_adds == one_side - N * nonzero, "dropped term was not one matvec per column") + receipt = { + "adds_one_side": one_side, + "adds_m_right_multiply": N * 2 * nonzero, + "adds_np_left_multiply": N * 3 * nonzero, + "b2_nonzero": nonzero, + "banks_checked": 3, + "both_sides_adds": both_sides, + "both_sides_exceed_one_reference_pair": True, + "k2_gram_add_saving": gram_add_saving(2), + "k2_net_after_one_side": net_two, + "k3_net_after_one_side": net_three, + "minimum_claimed": False, + "new_kappa": False, + } + print("PASS_P104_OMITTED_SIDE_CONSUMER " + json.dumps(receipt, sort_keys=True)) + + +if __name__ == "__main__": + main()