diff --git a/research/cubic-three-query-arities/CERTIFICATES.json b/research/cubic-three-query-arities/CERTIFICATES.json new file mode 100644 index 0000000000..ae72c7ef71 --- /dev/null +++ b/research/cubic-three-query-arities/CERTIFICATES.json @@ -0,0 +1,352 @@ +{ + "schema_version": 1, + "search": { + "max_r": 160, + "width": 160, + "domain": "integers c1 2` needs at least three dyadic cube queries, even with +nonmonic forms and general dyadic frames. Before this package, three queries +were known for these arities: + +| Source | Arities with three queries | +|---|---| +| PR6212 (apoursh) | `r = 2 mod 3`, `r = 3`, `r = 4` | +| PR6215 (apoursh), original-b family | `r mod 9` in `{0,1}` (and `{2,5,8}`) | +| PR6246 (AAAmirinu) | `r = 2^s` | +| PR6250 (CubeJerry) | `r = 2^s + 1` | + +**Result.** At each of the 17 arities + +```text +6, 7, 12, 13, 21, 25, 42, 51, 52, 76, 85, 93, 97, 114, 156, 157, 160 +``` + +none of which lies in a class above, three integer-direction cube queries with +dyadic weights and integer source shears repair the program exactly. With the +PR6212 lower bound, the exact dyadic minimum at each of these arities is +**three**. Every certificate uses only the PR6212 alphabet: integer shears +`a += k*b` and weighted queries `y += q*a^3` with dyadic `q`. + +Example, `r = 6`, starting from the actual deferred source `a = A + 6B`: + +```text +a -= 8*b # A - 2B +y += 1*a^3 +a += 4*b # A + 2B +y += 10*a^3 +a += b # A + 3B +y -= 11*a^3 +a -= 3*b # A, restored +``` + +Example, `r = 7`: directions `0, 1, 5` with defect weights `-7, 7/2, 7/2`. +All 17 direction triples and weights are in [CERTIFICATES.json](CERTIFICATES.json). + +## Proof + +Let `S_k = sum_{i sum w_j p(c_j)` sends `(x-c_k)(x-c_l)` to +`S2 - (c_k+c_l)S1`. The `B^3` coefficient needs `sum w_j c_j^3 = S3`. Since +`c_j^3 = e1 c_j^2 - e2 c_j + e3` at each node, the left side is +`e1*S2 - e2*S1 + e3*0`. So the condition is `S1^2 = S1(m e1 - e2)`, and +`S1 > 0` for `r >= 2`. ∎ + +**Schedule.** Given an integer node triple with dyadic weights, start from +`a = A + rB` after the last retained child. For each `j`, shear `a` to +`A + c_j B` with `a += (c_j - previous)*b`, then apply `y += (-w_j)*a^3`. A +final shear `a -= c_3*b` restores `a = A`. `b` is never written. The appended +repair has exactly three queries and at most four shears. The final output is +`y + sum_i (A+iB)^3 - D_r = y + r*A^3`, for arbitrary dirty `y,A,B`. + +For each listed arity, the node condition and dyadic weights are finite exact +facts that the verifier checks. The lower bound of three is PR6212's +theorem, cited and not re-proved here. + +## Evidence and reproduction + +From the repository root, with CPython 3.12 and only the standard library: + +```sh +python3.12 -B scripts/research_package.py --package research/cubic-three-query-arities +python3.12 -I -W error -B research/cubic-three-query-arities/verify.py +python3.12 -I -W error -B research/cubic-three-query-arities/search.py --check research/cubic-three-query-arities/CERTIFICATES.json +``` + +- `verify.py` does not use the closed weight formula to accept certificates. + It solves the first three coefficient equations by exact Gaussian + elimination and requires the `B^3` residual to vanish. It also checks that + every weight is nonzero and dyadic, and that each recorded weight matches. + It runs each literal program (all `r` children, then the repair) on 12 + dirty integer and rational inputs. +- It corroborates the node lemma's residual identity + `S1*(m e1 - e2 - S1)` and the closed weights on random rational triples. +- It recomputes the uncovered-arity list independently and anchors the + predecessors. It reproduces PR6250's r=33 weights, and it finds PR6215's + residue table and PR6212's two-query statement in the pinned README bytes. +- Six rejection controls must fail: a perturbed weight, an omitted restoring + shear, a query moved before its shear, a wrong arity, an omitted query, and + the non-node triple `0,1,5` at `r = 10`. +- `search.py` regenerates `CERTIFICATES.json` byte for byte. It takes the + lexicographically first integer triple with `c1 < c2 < c3` and + `c1,c2` in `[-160, r+160)`, for every uncovered `r <= 160`. + +## Limitations + +- **Search failure, not obstruction:** 50 of the 67 uncovered arities + `r <= 160` have no integer-direction certificate in the box. They are listed + in `no_certificate_in_box`. Nothing here proves that four queries are needed + at any of them. Dyadic non-integer directions and general dyadic frames + (as in PR6215) were not searched. +- This is a local coefficient and restoration result. No query, shear, + preparation, routing, precision or storage price is paid. No physical + backend, complete invoice or supplier admission is claimed. The selected + result and its five framework assumptions are unchanged. +- No independent human or formal proof-kernel review is claimed. + +## Open next questions + +1. **Finish the classification.** Do `r = 15, 22, 24, 30, 31, ...` admit + three queries with dyadic non-integer directions or general frames, or is + there a four-query obstruction? A first step is to write + `v_j = 6c_j - (2r-1)`. The node condition becomes + `e2(v) = (9 - 3(2r-1)^2)/2`. Then test odd-prime valuations of the weight + numerators and denominators at `r = 15`. +2. **Infinite family.** The certificates at `r = 7` and `r = 97` share the + weight shape `(-r, r/2, r/2)` on directions `(0,d,e)`. Imposing + `w_2 = w_3` in the closed formula gives a binary quadratic. Does it have a + Pell-type recurrence that yields infinitely many new arities? +3. **Price.** Does the `r = 6` or `r = 7` repair beat `r` eager inverse shears + under one pinned backend? Use the complete inequality in PR6250's PROOF.md. + +## Sources and credit + +Produced by Nick Petalas. New work is Apache-2.0. + +- The question is CubeJerry's PR6250 (`research/cubic-equal-step-classification`). +- The interface, dyadic lower bound of three and the `r = 2 mod 3`, `3`, `4` + constructions are apoursh's PR6212 (`research/dyadic-cubic-repair`). +- The original-b classification is apoursh's PR6215 + (`research/cubic-pure-b-boundary`). +- The power-of-two branch is AAAmirinu's PR6246. +- The fixed cubic reader and its restoration interface are from PR6200. + +The three predecessor READMEs are pinned live inputs in [SOURCES.json](SOURCES.json). +No predecessor code is imported or modified. Three-node quadrature and +Lagrange interpolation are classical; no literature priority is claimed. diff --git a/research/cubic-three-query-arities/SOURCES.json b/research/cubic-three-query-arities/SOURCES.json new file mode 100644 index 0000000000..b2089903f5 --- /dev/null +++ b/research/cubic-three-query-arities/SOURCES.json @@ -0,0 +1,36 @@ +{ + "schema_version": 1, + "base_commit": "7c49fe32c55de02c2c873655c804b16e4c3fef0a", + "files": [ + { + "path": "certificates/selected-result.json", + "usage": "provenance", + "git_blob": "d59b5035e7b4cbaa9d688a1d32dd0d6eb79c466a", + "sha256": "9b8d0eea95b3bf0dfcb7938a732a6439b87a2685dec2a5ab9e525b3b34debbfa" + }, + { + "path": "docs/research/agent-task-catalog.json", + "usage": "provenance", + "git_blob": "3404fbdd5e7529a0674eef26114b5071e4586cec", + "sha256": "ead6cdc9d0af6d58e8a49744d8ec8c7c15e13d2ba93c276c434bcf9b51b93a81" + }, + { + "path": "research/cubic-equal-step-classification/README.md", + "usage": "input", + "git_blob": "d31b3bbb08d2e41f373f0a49a70f00e14b6e6d24", + "sha256": "429e4dae8329ae85172483043dfd3c76020ae37e72d0095cb02f193b7b940c2c" + }, + { + "path": "research/cubic-pure-b-boundary/README.md", + "usage": "input", + "git_blob": "a3bb812a0655abe16fd34bb0e0deef8857d757aa", + "sha256": "3d5b7d0d117f9ddf49b295c9291d3b55b984e27bac1ee78eaca66afbc7b72960" + }, + { + "path": "research/dyadic-cubic-repair/README.md", + "usage": "input", + "git_blob": "de6b0954b428d7b4d3421c97c29f4bc6517f04d2", + "sha256": "2a291420d43c4ab0157c23ab3adfc7494b90b6cf11ab7bb6842c99ece7a0594d" + } + ] +} diff --git a/research/cubic-three-query-arities/TASK.json b/research/cubic-three-query-arities/TASK.json new file mode 100644 index 0000000000..1f144ec0cf --- /dev/null +++ b/research/cubic-three-query-arities/TASK.json @@ -0,0 +1,96 @@ +{ + "schema_version": 1, + "task_id": "joint-query-controller", + "priority_revision": 8, + "base_commit": "7c49fe32c55de02c2c873655c804b16e4c3fef0a", + "original_card": { + "id": "joint-query-controller", + "question": "Can a changed query demand or caller remove real work while satisfying native chronology and restoration?", + "starting_points": [ + "docs/research/current-priorities.md", + "docs/research/research-family-status.md", + "certificates/selected-result.json", + "docs/research/pr-intake-evidence/pr5599-completion/PROOF-REVIEW.md" + ], + "methods": [ + "Construct and independently check one source-bound instance of the stated first experiment.", + "Seek an exact counterexample or target-specific complete-budget obstruction to that same proposed change." + ], + "acceptance_gate": "A source-bound arbitrary-input identity and legal producer/consumer boundary with the complete paid and productive target invoice, including restoration, ownership, native alignment and extra charges. Price under the actual current model; historical ceiling.py assumptions must be established before use. A favorable optimistic relaxation keeps a route open but is not a completed supplier. A scoped local result remains useful; expansion requires the family-level budget gate.", + "first_step": { + "command": [ + "python3", + "-B", + "scripts/research_selected_baseline.py" + ], + "expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY", + "inspect": "Read revision 8, the selected review and live exact-source handoffs. Then follow this card's bounded first_experiment.", + "scope": "Source-bound orientation only, not a supplier replay or theorem proof." + }, + "first_experiment": "Choose an exact caller and a new demand/interface hypothesis. Read the scoped #5617/#5631/#5649/#5747 and #5720/#5745 objections before rebuilding an excluded schedule. Compare the complete matched source/consumer protocols including all retained helper outputs. For an active-mode injection, first check #5745: its canonical delivery claim repays the eight removed centre jobs. Audit that exact claim or change its channel/feedback hypothesis; do not repeat the same free-injection search. Stop if the full favorable budget fails or the required restored output is unobservable.", + "bottleneck_link": "Judge the actual current supplier contract and complete signed target/paid bill; frozen-profile ceilings require an applicability proof.", + "allowed_variations": [ + "One exact source head, helper/cut region or quantified proposition and changed hypothesis; check live handoffs first. Different seeds are not different scopes." + ], + "scope_change_triggers": [ + "Changing source head, helper/cut domain, operation alphabet or caller boundary without recording the new scope.", + "Treating an archived proposal or local numerical pass as complete supplier admission." + ], + "overlap_terms": [ + "query demand", + "active mode", + "5720", + "5745", + "5747", + "caller" + ], + "existing_checks": [ + { + "scope": "PR #4166: author-checked installed producer/service/return/bridge connection; native-component comparison loses.", + "evidence": "research/qq16-caller-install/README.md", + "remaining": "Complete matched physical protocol bill, or changed source-compatible demand/circuit; no affordable supplier yet.", + "status": "author-checked", + "source_commit": "0b082d22a0e9a442d264ab0633e35ef26c790630", + "selected_record_sha256": "bb2eaa58647b353f88d0aec2eafb9d30df1be53589231b23e12ceef3e4bad129" + }, + { + "scope": "Selected PR5599: complete helper-subspace construction and independent caller replay; five framework assumptions remain.", + "evidence": "docs/research/pr-intake-evidence/pr5599-completion/PROOF-REVIEW.md", + "remaining": "The next experiment must identify uncovered source-specific work. Successor archive intake does not transfer this admission.", + "status": "reviewed", + "source_commit": "09dd0965711ca7eeb169057a420abab1c9052137", + "selected_record_sha256": "2a0220e866417774606e0f991e41b9c1d26b1f43b97857238cf478dbc7d330b5" + } + ] + }, + "scope_status": "changed", + "actual_scope": "Open next question 1 of CubeJerry's PR6250: which arities r>=3 not covered by PR6212 (r=2 mod 3, r=3,4), PR6215 (r mod 9 in {0,1}), PR6246 (r=2^s) or PR6250 (r=2^s+1) admit a three-query dyadic repair of PR6200's fixed cubic reader with all r children retained and arbitrary incoming sources restored.", + "scope_change_reason": "Same coefficient-feasibility side question as PR6212/PR6250 (recorded there as a changed scope of this card). The deliverable is a local coefficient and restoration result for new arities, not a caller-bound supplier or complete invoice.", + "bottleneck_link": "Side result. It settles the exact dyadic query count (three, by PR6212's lower bound) at 17 further arities; event prices, a physical backend and complete invoice remain unpaid.", + "budget_minutes": 60, + "stop_rule": "Stop after the first verified batch of new arities from the node-condition reduction; report arities without a certificate in the bounded box as a search failure only.", + "outcome": "partial", + "evidence": "README.md (written reduction and schedule), verify.py (independent exact elimination, literal dirty execution, controls), search.py and CERTIFICATES.json (bounded search, byte-reproduced).", + "author": "Nick Petalas", + "ai_assistance": "Independent human and formal proof-kernel review are unclaimed.", + "open_questions": [ + { + "question": "Do the 50 uncovered arities r<=160 without an integer-direction certificate in the box (first: 15, 22, 24, 30, 31) admit three queries with dyadic non-integer directions or general dyadic frames, or is there a four-query obstruction?", + "why_it_matters": "This would finish the three-versus-four classification for the fixed cubic reader.", + "first_step": "At r=15, write v_j=6c_j-(2r-1) so the node condition becomes e2(v)=(9-3(2r-1)^2)/2, then test odd-prime valuations of the weight numerators and denominators for a local obstruction before widening the search.", + "blocked_by": "A bounded integer-direction search failure is not a lower bound." + }, + { + "question": "Is there an infinite arity family beyond the known classes? The certificates at r=7, 97 have weights (-r, r/2, r/2) on directions (0,d,e).", + "why_it_matters": "An infinite family would replace finite certificates with a uniform construction.", + "first_step": "Impose w_2=w_3 in the closed weight formula, reduce to a binary quadratic in (d,e) and look for a Pell-type recurrence through r=7 and r=97.", + "blocked_by": "None known." + }, + { + "question": "Does any of these repairs beat r eager inverse shears in a complete physical invoice?", + "why_it_matters": "Three weighted cube queries and up to four shears may cost more than the restoration they replace.", + "first_step": "Price the r=6 or r=7 schedule with the inequality of PR6250's PROOF.md under one pinned backend.", + "blocked_by": "No pinned physical backend or favorable recurrence." + } + ] +} diff --git a/research/cubic-three-query-arities/VERIFY.json b/research/cubic-three-query-arities/VERIFY.json new file mode 100644 index 0000000000..aaa2721080 --- /dev/null +++ b/research/cubic-three-query-arities/VERIFY.json @@ -0,0 +1,28 @@ +{ + "schema_version": 1, + "status": "ready", + "python": "3.12", + "requirements": [], + "inputs": [ + "research/cubic-equal-step-classification/README.md", + "research/cubic-pure-b-boundary/README.md", + "research/dyadic-cubic-repair/README.md" + ], + "commands": [ + { + "argv": ["{python}", "-I", "-W", "error", "-B", "{package}/verify.py"], + "timeout_seconds": 120, + "checks": "Node-condition identity on random rational triples; PR6250/PR6215/PR6212 anchors; 17 certificates by independent elimination, dyadic weights and literal dirty execution; six rejection controls." + }, + { + "argv": ["{python}", "-I", "-O", "-W", "error", "-B", "{package}/verify.py"], + "timeout_seconds": 120, + "checks": "Same checks under optimized Python." + }, + { + "argv": ["{python}", "-I", "-W", "error", "-B", "{package}/search.py", "--check", "{package}/CERTIFICATES.json"], + "timeout_seconds": 300, + "checks": "Regenerates the bounded search (r<=160, width 160) and requires byte equality with CERTIFICATES.json, including the no-certificate-in-box list." + } + ] +} diff --git a/research/cubic-three-query-arities/search.py b/research/cubic-three-query-arities/search.py new file mode 100644 index 0000000000..1ae529dabe --- /dev/null +++ b/research/cubic-three-query-arities/search.py @@ -0,0 +1,130 @@ +"""Bounded search for three-query integer-direction dyadic cubic repairs. + +For each arity r in the uncovered set U(N) (see `uncovered`), search all +integer direction triples c1 < c2 < c3 with c1, c2 in [-W, r + W) that +satisfy the node condition e2 = m*e1 - S1, and keep those whose unique +weights are dyadic. Writes the lexicographically first certificate per +arity plus the list of arities with no certificate in the box. + +Usage: search.py OUT.json [--max-r N] [--width W] + search.py --check FILE.json (regenerate and compare bytes) + +Nick Petalas. Apache-2.0. +""" +from __future__ import annotations + +import json +import sys +from fractions import Fraction + +MAX_R = 160 +WIDTH = 160 + + +def is_power_of_two(x: int) -> bool: + return x >= 1 and x & (x - 1) == 0 + + +def covered_by_predecessors(r: int) -> str | None: + """Return the predecessor family that already gives three queries, if any.""" + if r % 3 == 2: + return "PR6212: r = 2 mod 3" + if r in (3, 4): + return "PR6212: r in {3,4}" + if r % 9 in (0, 1): + return "PR6215: original-b family, r mod 9 in {0,1}" + if is_power_of_two(r): + return "PR6246: r = 2^s" + if is_power_of_two(r - 1): + return "PR6250: r = 2^s + 1" + return None + + +def uncovered(max_r: int) -> list[int]: + return [r for r in range(3, max_r + 1) if covered_by_predecessors(r) is None] + + +def is_dyadic(x: Fraction) -> bool: + d = x.denominator + return d & (d - 1) == 0 + + +def weights(r: int, c: tuple[Fraction, Fraction, Fraction]) -> list[Fraction]: + s1 = Fraction(r * (r - 1), 2) + m = Fraction(2 * r - 1, 3) + out = [] + for j in range(3): + k, l = (i for i in range(3) if i != j) + out.append(s1 * (m - c[k] - c[l]) / ((c[j] - c[k]) * (c[j] - c[l]))) + return out + + +def first_certificate(r: int, width: int) -> list[int] | None: + s1 = Fraction(r * (r - 1), 2) + m = Fraction(2 * r - 1, 3) + for c1 in range(-width, r + width): + for c2 in range(c1 + 1, r + width): + den = (c1 + c2) - m + c3 = (m * (c1 + c2) - s1 - c1 * c2) / den # den != 0: m is not an integer + if c3.denominator != 1 or c3 <= c2: + continue + c = (Fraction(c1), Fraction(c2), c3) + if all(is_dyadic(w) for w in weights(r, c)): + return [c1, c2, int(c3)] + return None + + +def build(max_r: int, width: int) -> dict: + certs, missing = [], [] + for r in uncovered(max_r): + found = first_certificate(r, width) + if found is None: + missing.append(r) + else: + w = weights(r, tuple(Fraction(x) for x in found)) + certs.append({"r": r, "directions": found, "weights": [str(x) for x in w]}) + return { + "schema_version": 1, + "search": {"max_r": max_r, "width": width, + "domain": "integers c1 str: + return json.dumps(data, indent=1) + "\n" + + +def main(argv: list[str]) -> int: + if len(argv) == 3 and argv[1] == "--check": + with open(argv[2], encoding="utf-8") as handle: + committed = handle.read() + old = json.loads(committed)["search"] + fresh = dump(build(old["max_r"], old["width"])) + if fresh != committed: + print("FAIL_SEARCH_MISMATCH") + return 1 + print("PASS_SEARCH_REPRODUCED") + return 0 + if len(argv) not in (2, 4, 6): + print(__doc__) + return 2 + max_r, width = MAX_R, WIDTH + args = argv[2:] + for flag, value in zip(args[::2], args[1::2]): + if flag == "--max-r": + max_r = int(value) + elif flag == "--width": + width = int(value) + else: + print(__doc__) + return 2 + with open(argv[1], "w", encoding="utf-8") as handle: + handle.write(dump(build(max_r, width))) + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/research/cubic-three-query-arities/verify.py b/research/cubic-three-query-arities/verify.py new file mode 100644 index 0000000000..515536095f --- /dev/null +++ b/research/cubic-three-query-arities/verify.py @@ -0,0 +1,196 @@ +"""Exact checker for three-query dyadic cubic repairs at new arities. + +Independent of search.py: weights are obtained by exact Gaussian elimination +on the first three coefficient equations, not by the closed form. Each +certificate is checked by coefficient comparison and by literal execution of +the retained children followed by the appended repair on dirty integer and +rational inputs. Rejection controls must fail. Standard library only. + +Nick Petalas. Apache-2.0. +""" +from __future__ import annotations + +import json +import random +import sys +from fractions import Fraction +from pathlib import Path + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parent.parent + + +def fail(msg: str) -> None: + print("FAIL", msg) + sys.exit(1) + + +def power_sums(r: int) -> tuple[int, int, int]: + return (sum(range(r)), sum(i * i for i in range(r)), sum(i ** 3 for i in range(r))) + + +def solve3(mat: list[list[Fraction]], rhs: list[Fraction]) -> list[Fraction]: + a = [row[:] + [v] for row, v in zip(mat, rhs)] + for col in range(3): + piv = next(i for i in range(col, 3) if a[i][col] != 0) + a[col], a[piv] = a[piv], a[col] + for i in range(3): + if i != col and a[i][col] != 0: + f = a[i][col] / a[col][col] + a[i] = [x - f * y for x, y in zip(a[i], a[col])] + return [a[i][3] / a[i][i] for i in range(3)] + + +def solved_weights(r: int, c: list[Fraction]) -> tuple[list[Fraction], Fraction]: + """Weights from A^3, A^2B, AB^2 equations; return them and the B^3 residual.""" + s1, s2, s3 = power_sums(r) + w = solve3([[Fraction(1)] * 3, c, [x * x for x in c]], + [Fraction(0), Fraction(s1), Fraction(s2)]) + return w, sum(wj * cj ** 3 for wj, cj in zip(w, c)) - s3 + + +def is_dyadic(x: Fraction) -> bool: + d = x.denominator + return d & (d - 1) == 0 + + +def run_program(r: int, prog: list[tuple[str, Fraction]], y, a, b): + """Retained children y += a^3; a += b (r times), then the appended repair.""" + for _ in range(r): + y += a ** 3 + a += b + for op, k in prog: + if op == "shear": + a += k * b + else: + y += k * a ** 3 + return y, a, b + + +def schedule(r: int, c: list[int], w: list[Fraction]) -> list[tuple[str, Fraction]]: + prog, pos = [], r + for cj, wj in zip(c, w): + if cj != pos: + prog.append(("shear", Fraction(cj - pos))) + prog.append(("query", -wj)) + pos = cj + if pos != 0: + prog.append(("shear", Fraction(-pos))) + return prog + + +def program_ok(r: int, prog, rng: random.Random) -> bool: + for trial in range(12): + if trial < 6: + y, a, b = (Fraction(rng.randint(-10**6, 10**6)) for _ in range(3)) + else: + y, a, b = (Fraction(rng.randint(-999, 999), rng.randint(1, 97)) for _ in range(3)) + got = run_program(r, prog, y, a, b) + if got != (y + r * a ** 3, a, b): + return False + return True + + +def is_power_of_two(x: int) -> bool: + return x >= 1 and x & (x - 1) == 0 + + +def covered(r: int) -> bool: + return (r % 3 == 2 or r in (3, 4) or r % 9 in (0, 1) + or is_power_of_two(r) or is_power_of_two(r - 1)) + + +def main() -> None: + rng = random.Random(20261103) + + # 1. Node-condition identity (written proof, corroborated on random triples): + # B^3 residual = S1 * (m*e1 - e2 - S1) and closed-form weights agree. + for _ in range(400): + r = rng.randint(3, 300) + c = [Fraction(rng.randint(-500, 500), rng.randint(1, 9)) for _ in range(3)] + if len(set(c)) < 3: + continue + s1 = Fraction(r * (r - 1), 2) + m = Fraction(2 * r - 1, 3) + e1 = sum(c) + e2 = c[0] * c[1] + c[0] * c[2] + c[1] * c[2] + w, resid = solved_weights(r, c) + if resid != s1 * (m * e1 - e2 - s1): + fail("node-condition identity") + for j in range(3): + k, l = (i for i in range(3) if i != j) + if w[j] != s1 * (m - c[k] - c[l]) / ((c[j] - c[k]) * (c[j] - c[l])): + fail("closed-form weight") + print("PASS node condition and weight formula on random rational triples") + + # 2. Positive anchor: PR6250's published r=33 weights. + w33, res33 = solved_weights(33, [Fraction(0), Fraction(16), Fraction(32)]) + if res33 != 0 or w33 != [Fraction(-869, 32), Fraction(341, 16), Fraction(187, 32)]: + fail("r=33 anchor") + readme = (ROOT / "research/cubic-equal-step-classification/README.md").read_text("utf-8") + if "(187/32)(A+32B)^3 + (341/16)(A+16B)^3 - (869/32)A^3" not in readme: + fail("PR6250 r=33 text not found in pinned input") + pure_b = (ROOT / "research/cubic-pure-b-boundary/README.md").read_text("utf-8") + if "| 0, 1, 2, 5, 8 | 3 |" not in pure_b or "| 3, 4, 6, 7 | 4 |" not in pure_b: + fail("PR6215 residue table not found in pinned input") + dyadic = (ROOT / "research/dyadic-cubic-repair/README.md").read_text("utf-8") + if "two queries suffice **only for r=2**" not in dyadic: + fail("PR6212 two-query statement not found in pinned input") + print("PASS predecessor anchors (r=33 weights, PR6215 table, PR6212 two-query bound)") + + # 3. Certificates. + data = json.loads((HERE / "CERTIFICATES.json").read_text("utf-8")) + certs = data["certificates"] + expect_unc = [r for r in range(3, data["search"]["max_r"] + 1) if not covered(r)] + if data["uncovered_arities"] != expect_unc: + fail("uncovered arity list") + seen = set() + for cert in certs: + r, c = cert["r"], cert["directions"] + if covered(r) or r in seen or not (c[0] < c[1] < c[2]): + fail(f"certificate domain r={r}") + seen.add(r) + cf = [Fraction(x) for x in c] + w, resid = solved_weights(r, cf) + if resid != 0: + fail(f"B^3 coefficient r={r}") + if [str(x) for x in w] != cert["weights"]: + fail(f"recorded weights r={r}") + if any(x == 0 or not is_dyadic(x) for x in w): + fail(f"weights not nonzero dyadic r={r}") + prog = schedule(r, c, w) + if sum(op == "query" for op, _ in prog) != 3: + fail(f"query count r={r}") + if not program_ok(r, prog, rng): + fail(f"literal execution r={r}") + if sorted(seen | set(data["no_certificate_in_box"])) != expect_unc: + fail("certificate/no-certificate partition") + print(f"PASS {len(certs)} certificates: exact identity, dyadic weights, " + f"literal dirty execution; arities {sorted(seen)}") + + # 4. Rejection controls (each must be rejected). + r, c = certs[0]["r"], certs[0]["directions"] + w, _ = solved_weights(r, [Fraction(x) for x in c]) + good = schedule(r, c, w) + controls = { + "perturbed weight": [(op, k + (Fraction(1, 2) if i == 1 else 0)) for i, (op, k) in enumerate(good)], + "omitted restoring shear": good[:-1] if good[-1][0] == "shear" else good + [("shear", Fraction(1))], + "query before first shear": [good[1], good[0]] + good[2:], + "wrong arity": None, + "omitted query": [step for i, step in enumerate(good) if i != next(j for j, s in enumerate(good) if s[0] == "query")], + } + for name, prog in controls.items(): + if name == "wrong arity": + rejected = not program_ok(r + 1, good, rng) + else: + rejected = not program_ok(r, prog, rng) + if not rejected: + fail(f"control accepted: {name}") + if solved_weights(10, [Fraction(0), Fraction(1), Fraction(5)])[1] == 0: + fail("control accepted: r=10 non-node") + print(f"PASS {len(controls) + 1} rejection controls") + print("PASS_CUBIC_THREE_QUERY_ARITIES") + + +if __name__ == "__main__": + main()