diff --git a/research/signed-leibniz-replay/MEASUREMENT.json b/research/signed-leibniz-replay/MEASUREMENT.json new file mode 100644 index 0000000000..314bc9621e --- /dev/null +++ b/research/signed-leibniz-replay/MEASUREMENT.json @@ -0,0 +1,87 @@ +{ + "cpu_count": 18, + "errors": 0, + "load_average": [ + 3.1376953125, + 3.8837890625, + 2.8388671875 + ], + "platform": "macOS-27.0.1-arm64-arm-64bit", + "python": "3.12.15 (main, Oct 14 2026, 00:55:37) [Clang 23.1.3 ]", + "reduction": 0.09829787936083867, + "samples": [ + { + "elapsed_ns": 142122583, + "engine": "reference", + "stdout_sha256": "d0ee4265d505e6433c2eb13cc54edfe144167c5f61d0a4add50a6e513c4febb5" + }, + { + "elapsed_ns": 121716458, + "engine": "scheduled", + "stdout_sha256": "d0ee4265d505e6433c2eb13cc54edfe144167c5f61d0a4add50a6e513c4febb5" + }, + { + "elapsed_ns": 130969667, + "engine": "reference", + "stdout_sha256": "d0ee4265d505e6433c2eb13cc54edfe144167c5f61d0a4add50a6e513c4febb5" + }, + { + "elapsed_ns": 118456500, + "engine": "scheduled", + "stdout_sha256": "d0ee4265d505e6433c2eb13cc54edfe144167c5f61d0a4add50a6e513c4febb5" + }, + { + "elapsed_ns": 131713167, + "engine": "reference", + "stdout_sha256": "d0ee4265d505e6433c2eb13cc54edfe144167c5f61d0a4add50a6e513c4febb5" + }, + { + "elapsed_ns": 119808625, + "engine": "scheduled", + "stdout_sha256": "d0ee4265d505e6433c2eb13cc54edfe144167c5f61d0a4add50a6e513c4febb5" + }, + { + "elapsed_ns": 131461458, + "engine": "reference", + "stdout_sha256": "d0ee4265d505e6433c2eb13cc54edfe144167c5f61d0a4add50a6e513c4febb5" + }, + { + "elapsed_ns": 118718833, + "engine": "scheduled", + "stdout_sha256": "d0ee4265d505e6433c2eb13cc54edfe144167c5f61d0a4add50a6e513c4febb5" + }, + { + "elapsed_ns": 134855917, + "engine": "reference", + "stdout_sha256": "d0ee4265d505e6433c2eb13cc54edfe144167c5f61d0a4add50a6e513c4febb5" + }, + { + "elapsed_ns": 118766042, + "engine": "scheduled", + "stdout_sha256": "d0ee4265d505e6433c2eb13cc54edfe144167c5f61d0a4add50a6e513c4febb5" + } + ], + "source_hashes": { + "research/dyadic-helper-channel-capacity/RESULT.json": "31975ba943d585843b185bc32ea6a5bff20d9d8f1cca2c041be6e8517c7f91ec", + "research/dyadic-helper-channel-capacity/SOURCES.json": "f480b7e6f7021278faad4bdc6eb0e3909ef673d88c4d9cc836636feacedb7dea", + "research/dyadic-helper-channel-capacity/capacity.py": "1cd82b0beab7983d955cfc1d1e562af490f90bcaad6e3ec7d07e0c5a33608834", + "research/dyadic-helper-channel-capacity/verify.py": "d008dcf6de6d76c160989126453371b4cff09beeaab5c523e57ad6d8c6009af7", + "research/p104-coupled-helper-bases/PROOF.md": "d9049aa75f065c3c21b0f55246c433c643584bca8969f2f382a83c425c4a2927", + "research/signed-leibniz-replay/SOURCES.json": "7d57b2617574606e98c45b20c7211ce9df7e1cea00360100751d8b553a6322f6", + "research/signed-leibniz-replay/adapter.py": "b37d88e4972a9d6af8e7b6e63d156935d6ad58a5e5f513b81509fd8aa67ad5ce", + "research/signed-leibniz-replay/benchmark.py": "f9653d5eb1d0eda9fe1360daca3bfcd902ca5a8a577d959261f1d8aa1e2dfc66", + "research/signed-leibniz-replay/kernel.py": "12d88b1657496699a8da76b94052a4ce8755ae586985d06207adaf6d4d8d93f9" + }, + "summary": { + "reference": { + "max_ns": 142122583, + "median_ns": 131713167, + "min_ns": 130969667 + }, + "scheduled": { + "max_ns": 121716458, + "median_ns": 118766042, + "min_ns": 118456500 + } + } +} diff --git a/research/signed-leibniz-replay/PROOF.md b/research/signed-leibniz-replay/PROOF.md new file mode 100644 index 0000000000..7af2f1911c --- /dev/null +++ b/research/signed-leibniz-replay/PROOF.md @@ -0,0 +1,102 @@ +# Exact evaluator and complete-caller preservation + +Let A be a square matrix over the integers or rationals, of dimension +0 <= n <= 5. The reference determinant enumerates every permutation p and +evaluates + +```text +sum_p (-1)^(number of inversions of p) product_i A[i,p[i]]. +``` + +The replacement returns A[0,0] for n=1 and +A[0,0]A[1,1]-A[0,1]A[1,0] for n=2. These are exactly the one and two +Leibniz terms. For every other allowed dimension it uses the same permutation +enumeration, exact products and sum, taking the sign from a table initialized +by the same inversion formula. The empty product and single empty permutation +give determinant one at n=0. Thus the evaluators agree on the entire stated +arithmetic and dimension domain, including singular and negative determinants. +No division, rounding, rank assumption or nonzero-pivot assumption enters. + +The table contains sum(n!, n=0..5)=154 permutation/sign pairs. It depends only +on dimension. Matrix values are read again at every call; a changed matrix +cannot reuse an earlier determinant. A separate cycle-decomposition check +uses sign(p)=(-1)^(n-number_of_cycles) and verifies table completeness and +uniqueness. It is a conformance check, not the basis of the equality proof. + +The source-bound complete caller has no determinant request outside sizes +one through five. Comparison mode evaluates both functions on every actual +request, checks exact equality, and hashes the ordered matrix/value stream. +Its 19,654 calls include every determinant requested by both the unimodular +certificate checks and the independent gcd of every minor. All remaining +functions and all control flow execute from the original authenticated source. + +Consequently the changed caller computes the same unimodular conditions, +Smith identities, diagonal and divisibility conditions, independent minor gcds, +constructive unit blocks, nonunit obstructions and corrupted-certificate +decisions. It performs all original work with a cheaper exact evaluator. +This implication preserves the source caller's checks; it does not independently +prove the mathematical capacity algorithm or its physical applicability. + +# Measurement contract + +The benchmark launches five alternating reference/scheduled pairs, each as a +fresh CPython process. The reference arm is the original complete script. +The scheduled arm is the isolated adapter, including its source authentication +and additional imports. The timer encloses process creation, full execution, +captured output and successful termination. Timeout is thirty seconds. Every +arm must exit successfully, emit no stderr, reach the original success marker +and produce identical complete stdout. No scientific output or result is cached. + +Bytecode-cache namespaces are fresh for every process. The adapter additionally +loads the two authenticated predecessor modules directly from source. Both +arms use the same interpreter, warnings and disabled cache-write settings. +There is no baseline-only profiler, tracer, extra guard or repetition inside +the timed region. Measurement summaries and source hashes are generated after +timing. Profile runs are separate and never included in the ten samples. + +The diagnostic reference profile put roughly 22% of its total profiled time +inside determinant evaluation. The scheduled evaluator reduces that work; +unchanged Fraction arithmetic, matrix products and certificate checks limit +the complete gain. The measured 9.83% reduction is below that optimistic +removable share. This is a local observation, not an asymptotic or portable +speed guarantee. No performance threshold is asserted by CI. + +# Implementation bounds and deviations + +New loops are finite. Schedule initialization has six dimensions and 154 +permutations, with at most ten inversion comparisons each. A scheduled +determinant has at most 120 terms of at most five factors. Conformance grids +have 1,248 values; source controls have eight copied files and five separately +mutated inputs. The manifest is limited to sixteen rows and each input to +one megabyte. The production caller is fixed by five input hashes. +The benchmark has exactly ten subprocesses, with thirty-second limits. +The normal and optimized registered checks each have a 120-second limit. + +Functions are shorter than sixty physical lines. New control flow is iterative, +with explicit exceptions active under optimization. Temporary exports and +bytecode namespaces use context-managed cleanup. The source loader restores +the original capacity module binding on success and failure. Neither a kernel +exception nor a source mismatch is turned into scientific success. + +Power of Ten deviations: + +- Rule 2, inherited Smith reduction: the frozen predecessor has an unbounded + Euclidean loop. Preserving its exact source and checks prevents changing it + here. The caller's fixed finite input domain is unchanged, and each executed + subprocess and registered check has a wall-clock timeout. This limits a + stalled replay but is not a newly proved iteration bound. A separate change + to that source needs its own bound and preservation evidence. +- Rule 3, managed allocation: CPython integers, Fractions, tuples and captured + stdout allocate dynamically. The fixed grids, five-row matrices, 154-entry + table and bounded source files limit live data in these scripts. No service + loop or unbounded cache exists. Reassess storage before enlarging dimensions + or accepting external matrices. +- Rules 8 and 9, source imports and callable replacement: the adapter uses the + standard source loader for exactly two authenticated module paths and replaces + one determinant function with one of three explicit engines. It bypasses + bytecode. Comparison and corruption controls exercise the alternate targets; + no arbitrary plugin, code generator or runtime expression is accepted. + +Warning-strict normal and optimized execution, source corruption checks, +committed-byte/source-history preflight and AST inspection are the available +diagnostics. No external static analyzer or formal proof kernel is claimed. diff --git a/research/signed-leibniz-replay/README.md b/research/signed-leibniz-replay/README.md new file mode 100644 index 0000000000..e18e6834d7 --- /dev/null +++ b/research/signed-leibniz-replay/README.md @@ -0,0 +1,113 @@ +# Faster complete dyadic-capacity replay + +This contribution was produced by Nick Petalas. Apache-2.0. + +Claim type: scoped implementation-preservation proof and local performance +result. The complete [Ali Romman PR5615](https://github.com/CrocSwap/integer-mult-bounds/pull/5615) +capacity replay takes **9.83% less median wall time** with a bounded exact +Leibniz evaluator. Five alternating fresh-process pairs measured +0.131713167 s to 0.118766042 s. Reference range is 0.130969667 to +0.142122583 s; scheduled range is 0.118456500 to 0.121716458 s. +All ten complete scientific outputs are identical, with zero errors. + +The change evaluates sizes one and two directly and initializes the 154 signed +permutations through size five once. It caches no matrix entries, determinants, +certificate decisions or scientific outputs. The original capacity algorithm, +independent minor oracle, source checks and complete caller remain unchanged. +This is an isolated opt-in adapter; no shared default command is changed. + +The first signed-schedule-only experiment was inconclusive after adapter +overhead. Profiling then identified the 18,778 size-one and size-two requests. +Direct evaluation supplied the first clear improvement; optimization stopped. +The final generated [measurement](MEASUREMENT.json) binds the executing source +and all five declared inputs. It includes process startup, imports, source +authentication and the complete caller, rather than only the determinant loop. +Its final five-pair rerun records the numbers above. Timing is descriptive and +does not gate CI or promise a speedup on another host. + +## Evidence + +[PROOF.md](PROOF.md) shows exact equality of the evaluator for square integer +and rational matrices of sizes zero through five, and why replacing that one +function preserves each original certificate predicate. + +The standalone verifier checks, in each normal or optimized run: + +- All 19,654 requested determinants against the original evaluator, with an + ordered matrix/value transcript. The dimension counts are + `[0, 10331, 8447, 705, 138, 33]`. +- The complete source caller in reference, scheduled and comparison modes, + retaining its 2,401 integer matrices, 75 rectangular dyadic matrices, causal + and joint witnesses, and five original rejection controls. +- 154 signed schedules with a separate cycle-parity check; 1,248 explicit + conformance values, including rational, large-integer and mutable inputs. +- Six domain or evaluator corruption controls, plus five actual corrupted-input + executions. Every declared input is corrupted separately in a disposable + Git-free export and rejected before scientific execution. The unchanged + export also completes with an empty PATH. +- All ten recorded benchmark outputs, the source hashes, sample order and + summary arithmetic. Receipts are generated by the scripts, never handwritten. + +The complete determinant transcript has SHA256 +`868f3a5d2c37364d7a93986027182bc08b620f98d2ddb6daf7ec945875edea39`. +No predecessor mathematical theorem is re-audited as this contribution. + +## Reproduce + +From the repository root, with CPython 3.12 and the standard library: + +```sh +/Users/nick/git/integer-mult-bounds/.venv/bin/python3.12 -I -W error -B research/signed-leibniz-replay/verify.py +/Users/nick/git/integer-mult-bounds/.venv/bin/python3.12 -I -O -W error -B research/signed-leibniz-replay/verify.py +/Users/nick/git/integer-mult-bounds/.venv/bin/python3.12 -B scripts/research_package.py --package research/signed-leibniz-replay +/Users/nick/git/integer-mult-bounds/.venv/bin/python3.12 -B scripts/check_research_commit.py --package research/signed-leibniz-replay --check-source-history +``` + +Another CPython 3.12 path may replace the local path. Direct checks print +`PASS_SIGNED_LEIBNIZ_REPLAY` in JSON. The registered launcher prints +`PASS_REGISTERED_RESEARCH_PACKAGE`. Both registered checks have 120-second +limits and take less than five seconds locally. They write no package files. +Set TMPDIR to a chosen scratch directory to place disposable exports there. + +To measure a different host, supply a fresh path outside the checkout: + +```sh +/Users/nick/git/integer-mult-bounds/.venv/bin/python3.12 -W error -B research/signed-leibniz-replay/benchmark.py --output /absolute/scratch/measurement.json +``` + +The reference arm runs the original script directly. The scheduled arm runs +`adapter.py --engine scheduled`. Each process has a private empty bytecode +namespace; both use warning-strict execution and disabled bytecode writes. +Source imports in the adapter explicitly bypass cached bytecode. +The benchmark never overwrites an existing output. + +## Scope and credit + +The measured host is macOS arm64, CPython 3.12.15, with eighteen logical CPUs. +Other work was running; alternating arms expose both to that load. See the +generated receipt for environment and load details. This is one fixed complete +caller and a small local runtime improvement. It is neither a new capacity +theorem nor an independent scientific acceptance of PR5615. + +Credit Ali Romman for PR5615's algorithm, independent minor oracle, complete +caller and fixtures; PR5602/5599 and the retained helper, native and compiler +lineage. PR5615 also credits PR5609/5611 and Nick Petalas's earlier PR5115 +row preparation. Leibniz expansion and permutation parity are classical. +All predecessor sources and credits retain their bytes. + +No new kappa, physical channel, native supplier, selected-frontier change or +complete multiplication theorem is claimed. Native realization, dirty +restoration, caller costs, compiler closure, precision, storage and the five +selected framework assumptions retain their status. No selected cold replay, +Lean or Docker run is required or performed. Independent human and formal +proof-kernel review are unclaimed. + +## Open next questions + +Can another complete source caller use the same fixed schedules without +reducing its determinant domain? First profile that distinct caller and derive +its exact dimension and arithmetic contract. No broader caller or performance +claim is supplied here. A larger determinant domain needs a new storage and +runtime bound before reuse. + +[Advance claim](https://github.com/CrocSwap/integer-mult-bounds/discussions/1166#discussioncomment-19299774). diff --git a/research/signed-leibniz-replay/SOURCES.json b/research/signed-leibniz-replay/SOURCES.json new file mode 100644 index 0000000000..b9bf0f2402 --- /dev/null +++ b/research/signed-leibniz-replay/SOURCES.json @@ -0,0 +1,48 @@ +{ + "schema_version": 1, + "base_commit": "2485805a6d47f36ea83ddde6327d453ac82d44dc", + "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/dyadic-helper-channel-capacity/RESULT.json", + "usage": "input", + "git_blob": "a602ab1c755d5d245ed948c9b38d444c5b691c8d", + "sha256": "31975ba943d585843b185bc32ea6a5bff20d9d8f1cca2c041be6e8517c7f91ec" + }, + { + "path": "research/dyadic-helper-channel-capacity/SOURCES.json", + "usage": "input", + "git_blob": "187717b4e0d62a8c9101fc3dcf0b85995f67b310", + "sha256": "f480b7e6f7021278faad4bdc6eb0e3909ef673d88c4d9cc836636feacedb7dea" + }, + { + "path": "research/dyadic-helper-channel-capacity/capacity.py", + "usage": "input", + "git_blob": "d882eb6d049125c67d4c0d47dced60bf0e181268", + "sha256": "1cd82b0beab7983d955cfc1d1e562af490f90bcaad6e3ec7d07e0c5a33608834" + }, + { + "path": "research/dyadic-helper-channel-capacity/verify.py", + "usage": "input", + "git_blob": "dad6052e1c791883e7266c6ad5ec2d6ccc07db77", + "sha256": "d008dcf6de6d76c160989126453371b4cff09beeaab5c523e57ad6d8c6009af7" + }, + { + "path": "research/p104-coupled-helper-bases/PROOF.md", + "usage": "input", + "git_blob": "168733f6c486d689e58de15c6c34ce1b5ae6b12d", + "sha256": "d9049aa75f065c3c21b0f55246c433c643584bca8969f2f382a83c425c4a2927" + } + ] +} diff --git a/research/signed-leibniz-replay/TASK.json b/research/signed-leibniz-replay/TASK.json new file mode 100644 index 0000000000..1f612f1872 --- /dev/null +++ b/research/signed-leibniz-replay/TASK.json @@ -0,0 +1,73 @@ +{ + "schema_version": 1, + "task_id": "replay-runtime", + "priority_revision": 8, + "base_commit": "2485805a6d47f36ea83ddde6327d453ac82d44dc", + "original_card": { + "id": "replay-runtime", + "question": "Can one measured replay bottleneck be sped up without dropping coverage?", + "starting_points": [ + "scripts/run_isolated_tests.py", + "scripts/verify_replay_coverage.py", + "certificates/selected-result.json" + ], + "methods": [ + "Profile one isolated replay and optimize the measured bottleneck.", + "Design source-bound reuse with explicit dependency invalidation and negative controls." + ], + "acceptance_gate": "Report before/after wall time on the same fixture, retained checks and invalidation behavior.", + "first_step": { + "command": [ + "python3", + "-B", + "scripts/research_selected_baseline.py" + ], + "expected": "PASS_RESEARCH_BASELINE_BINDINGS_ONLY", + "inspect": "Resolve the current selected review, separate complex/ordinary BIT/admitted BIT/outer budgets, and compare the card against live handoffs before starting. Historical ceilings require exact source/model equivalence.", + "scope": "Source-binding orientation only; not a scientific replay or all-size proof. Materialize selected inputs if this checkout is sparse." + }, + "first_experiment": "Measure one unchanged fixture, preserve check coverage, and test one optimization with invalidation controls.", + "bottleneck_link": "Faster trustworthy replays increase review throughput and let mechanism candidates be checked sooner.", + "allowed_variations": [ + "Profile one stage or design one source-bound cache boundary." + ], + "scope_change_triggers": [ + "Skipping coverage or comparing different fixtures to claim speedup." + ], + "overlap_terms": [ + "replay runtime", + "cache invalidation", + "isolated tests" + ] + }, + "scope_status": "refined", + "actual_scope": "Reduce complete PR5615 replay time with direct size-one and size-two Leibniz formulas and immutable signed permutation schedules through size five.", + "scope_change_reason": "Fix one complete caller and one evaluator; preserve every original certificate predicate and all scientific outputs.", + "bottleneck_link": "A faster complete exact-capacity oracle reduces replay cost without changing the helper criterion or admitting any supplier.", + "budget_minutes": 60, + "stop_rule": "Stop at the first clear complete-command improvement in five alternating fresh-process pairs, or hand off a failed approach.", + "outcome": "completed", + "evidence": "PROOF.md, MEASUREMENT.json, kernel.py, adapter.py, verify.py", + "author": "Nick Petalas", + "ai_assistance": "Independent human and formal proof-kernel review are unclaimed.", + "open_questions": [ + { + "question": "Can another complete source caller use the same fixed signed schedules without reducing its determinant domain?", + "why_it_matters": "Reuse could save verification work while retaining independent minor arithmetic.", + "first_step": "Profile one distinct caller and derive its exact dimension and arithmetic contracts before proposing reuse.", + "blocked_by": "No broader caller or performance claim is supplied here." + } + ], + "coordination": { + "claim": "https://github.com/CrocSwap/integer-mult-bounds/discussions/1166#discussioncomment-19299774", + "main": "2485805a6d47f36ea83ddde6327d453ac82d44dc", + "priority_revision": 8, + "source": "original", + "author": "original", + "overlap": "Live complete open-PR and Discussion snapshots, recent account PRs, all-main README terms and merged searches; sparse local note inventory incomplete. No matching performance target found." + }, + "preserved_worktree": { + "branch": "research/w13-1030b-frontier", + "head": "cd2404bfcacaa98fe1281946d0a549c11f396614" + } +} diff --git a/research/signed-leibniz-replay/VERIFY.json b/research/signed-leibniz-replay/VERIFY.json new file mode 100644 index 0000000000..163e1692f2 --- /dev/null +++ b/research/signed-leibniz-replay/VERIFY.json @@ -0,0 +1,40 @@ +{ + "schema_version": 1, + "status": "ready", + "python": "3.12", + "requirements": [], + "inputs": [ + "research/dyadic-helper-channel-capacity/RESULT.json", + "research/dyadic-helper-channel-capacity/SOURCES.json", + "research/dyadic-helper-channel-capacity/capacity.py", + "research/dyadic-helper-channel-capacity/verify.py", + "research/p104-coupled-helper-bases/PROOF.md" + ], + "commands": [ + { + "argv": [ + "{python}", + "-I", + "-W", + "error", + "-B", + "{package}/verify.py" + ], + "timeout_seconds": 120, + "checks": "Complete unchanged caller equivalence, all 19654 determinant requests, bounded formula conformance, schedule and source corruptions, Git-free empty-PATH replay and generated measurement integrity." + }, + { + "argv": [ + "{python}", + "-I", + "-W", + "error", + "-B", + "-O", + "{package}/verify.py" + ], + "timeout_seconds": 120, + "checks": "Complete unchanged caller equivalence, all 19654 determinant requests, bounded formula conformance, schedule and source corruptions, Git-free empty-PATH replay and generated measurement integrity." + } + ] +} diff --git a/research/signed-leibniz-replay/adapter.py b/research/signed-leibniz-replay/adapter.py new file mode 100644 index 0000000000..8875ef4ae7 --- /dev/null +++ b/research/signed-leibniz-replay/adapter.py @@ -0,0 +1,104 @@ +"""Run the complete pinned capacity caller, changing only its determinant evaluator.""" + +import contextlib +import hashlib +import importlib.util +from importlib.machinery import SourceFileLoader +import io +import json +import sys +from pathlib import Path + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parents[1] +SOURCE = ROOT / "research/dyadic-helper-channel-capacity" +sys.path.insert(0, str(HERE)) +from kernel import determinant + + +def authenticate(): + manifest = json.loads((HERE / "SOURCES.json").read_text()) + if len(manifest["files"]) > 16: + raise ValueError("source manifest exceeds its fixed capacity") + for pin in manifest["files"]: + if pin["usage"] != "input": + continue + path = ROOT / pin["path"] + if path.stat().st_size > 1_000_000: + raise ValueError("source input exceeds its fixed capacity") + if hashlib.sha256(path.read_bytes()).hexdigest() != pin["sha256"]: + raise ValueError("source hash mismatch: " + pin["path"]) + + +class SourceOnlyLoader(SourceFileLoader): + """Authenticate source rather than executing an unrelated cached bytecode file.""" + + def get_code(self, fullname): + path = self.get_filename(fullname) + return self.source_to_code(self.get_data(path), path) + + +def load_source(name, path): + spec = importlib.util.spec_from_file_location(name, path, loader=SourceOnlyLoader(name, str(path))) + if spec is None or spec.loader is None: + raise ValueError("missing caller source loader") + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def load_caller(): + """Read both authenticated modules directly from source, bypassing bytecode.""" + authenticate() + old_capacity = sys.modules.pop("capacity", None) + try: + sys.modules["capacity"] = load_source("capacity", SOURCE / "capacity.py") + module = load_source("capacity_caller", SOURCE / "verify.py") + finally: + sys.modules.pop("capacity", None) + if old_capacity is not None: + sys.modules["capacity"] = old_capacity + return module + + +def run(engine): + caller = load_caller() + reference = caller.det + digest = hashlib.sha256() + dimensions = [0] * 6 + + def compared(matrix): + expected = reference(matrix) + actual = determinant(matrix) + if actual != expected: + raise ValueError("determinant disagreement") + n = len(matrix) + dimensions[n] += 1 + digest.update((repr((matrix, actual)) + "\n").encode()) + return actual + + if engine == "scheduled": + caller.det = determinant + elif engine == "compare": + caller.det = compared + elif engine != "reference": + raise ValueError("unknown determinant engine") + output = io.StringIO() + with contextlib.redirect_stdout(output): + caller.main() + return output.getvalue(), {"dimensions": dimensions, "sha256": digest.hexdigest()} + + +def main(): + args = sys.argv[1:] + if args not in ([], ["--engine", "reference"], ["--engine", "scheduled"], ["--engine", "compare"]): + raise ValueError("usage: adapter.py [--engine reference|scheduled|compare]") + engine = args[1] if args else "scheduled" + output, trace = run(engine) + print(output, end="") + if engine == "compare": + print(json.dumps(trace, sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/research/signed-leibniz-replay/benchmark.py b/research/signed-leibniz-replay/benchmark.py new file mode 100644 index 0000000000..ed6f084f29 --- /dev/null +++ b/research/signed-leibniz-replay/benchmark.py @@ -0,0 +1,68 @@ +"""Five alternating complete fresh-process pairs; timing never gates CI.""" + +import argparse +import hashlib +import json +import os +import platform +import statistics +import subprocess +import sys +import tempfile +from pathlib import Path +from time import perf_counter_ns + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parents[1] + + +def command(engine, cache): + argv = [sys.executable, "-X", "pycache_prefix=" + cache, "-W", "error", "-B"] + if engine == "reference": + return argv + [str(ROOT / "research/dyadic-helper-channel-capacity/verify.py")] + return argv + [str(HERE / "adapter.py"), "--engine", "scheduled"] + + +def measure(engine): + with tempfile.TemporaryDirectory(prefix="leibniz-bench-") as cache: + argv = command(engine, cache) + start = perf_counter_ns() + result = subprocess.run(argv, cwd=ROOT, capture_output=True, timeout=30, check=False) + elapsed = perf_counter_ns() - start + if result.returncode != 0 or result.stderr: + raise ValueError("complete replay failed: " + result.stderr.decode(errors="replace")) + if not result.stdout.endswith(b"PASS_DYADIC_HELPER_CHANNEL_CAPACITY\n"): + raise ValueError("complete caller did not reach its success predicate") + return {"engine": engine, "elapsed_ns": elapsed, "stdout_sha256": hashlib.sha256(result.stdout).hexdigest()} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--output", required=True, type=Path) + args = parser.parse_args() + samples = [] + for _ in range(5): + for engine in ("reference", "scheduled"): + samples.append(measure(engine)) + if len({row["stdout_sha256"] for row in samples}) != 1: + raise ValueError("complete scientific outputs differ") + summaries = {} + for engine in ("reference", "scheduled"): + values = [row["elapsed_ns"] for row in samples if row["engine"] == engine] + summaries[engine] = {"median_ns": statistics.median(values), "min_ns": min(values), "max_ns": max(values)} + manifest = json.loads((HERE / "SOURCES.json").read_text()) + paths = [HERE / name for name in ("kernel.py", "adapter.py", "benchmark.py", "SOURCES.json")] + paths += [ROOT / pin["path"] for pin in manifest["files"] if pin["usage"] == "input"] + sources = {str(path.relative_to(ROOT)): hashlib.sha256(path.read_bytes()).hexdigest() for path in paths} + receipt = {"python": sys.version, "platform": platform.platform(), "cpu_count": os.cpu_count(), + "load_average": os.getloadavg(), "samples": samples, "summary": summaries, + "errors": 0, "source_hashes": sources, + "reduction": 1 - summaries["scheduled"]["median_ns"] / summaries["reference"]["median_ns"]} + with args.output.open("x") as stream: + json.dump(receipt, stream, indent=2, sort_keys=True) + stream.write("\n") + print(json.dumps({"summary": summaries, "reduction": receipt["reduction"], "errors": 0}, sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/research/signed-leibniz-replay/kernel.py b/research/signed-leibniz-replay/kernel.py new file mode 100644 index 0000000000..4546f2a22e --- /dev/null +++ b/research/signed-leibniz-replay/kernel.py @@ -0,0 +1,33 @@ +"""Dimension-only schedules for the exact Leibniz determinant, through size five.""" + +from itertools import permutations +from math import prod + + +def make_schedules(): + """Initialize the bounded immutable table; matrix values never enter it.""" + return tuple( + tuple( + (p, (-1) ** sum(p[i] > p[j] for i in range(n) for j in range(i + 1, n))) + for p in permutations(range(n)) + ) + for n in range(6) + ) + + +SCHEDULES = make_schedules() + + +def determinant(matrix): + """Evaluate every Leibniz term with its precomputed sign, without division.""" + n = len(matrix) + if n == 1 and len(matrix[0]) == 1: + return matrix[0][0] + if n == 2 and len(matrix[0]) == len(matrix[1]) == 2: + return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0] + if n > 5 or any(len(row) != n for row in matrix): + raise ValueError("square determinant dimension must be between zero and five") + return sum( + sign * prod(matrix[i][p[i]] for i in range(n)) + for p, sign in SCHEDULES[n] + ) diff --git a/research/signed-leibniz-replay/verify.py b/research/signed-leibniz-replay/verify.py new file mode 100644 index 0000000000..88ed194e4d --- /dev/null +++ b/research/signed-leibniz-replay/verify.py @@ -0,0 +1,184 @@ +"""Check the bounded evaluator, unchanged complete caller and source invalidation.""" + +import hashlib +import json +import os +import shutil +import statistics +import subprocess +import sys +import tempfile +from fractions import Fraction +from itertools import product +from math import factorial, prod +from pathlib import Path + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parents[1] +sys.path.insert(0, str(HERE)) +import adapter +import kernel + + +def need(condition, message): + if not condition: + raise ValueError(message) + + +def check_schedules(): + count = 0 + for n in range(6): + schedule = kernel.SCHEDULES[n] + need(len(schedule) == factorial(n), "schedule size") + need(len({p for p, _ in schedule}) == factorial(n), "duplicate permutation") + for permutation, sign in schedule: + need(sorted(permutation) == list(range(n)), "invalid permutation") + visited = [False] * n + cycles = 0 + for start in range(n): + if visited[start]: + continue + cycles += 1 + index = start + for _ in range(n): + if visited[index]: + break + visited[index] = True + index = permutation[index] + need(visited[index], "cycle did not close") + need(sign == (-1) ** (n - cycles), "independent cycle parity") + count += 1 + return count + + +def check_values(reference): + count = 0 + for n, alphabet in ((1, range(-3, 4)), (2, range(-2, 3)), (3, range(2))): + for entries in product(alphabet, repeat=n * n): + matrix = [entries[i * n:(i + 1) * n] for i in range(n)] + need(kernel.determinant(matrix) == reference(matrix), "finite determinant conformance") + count += 1 + for n in range(6): + for case in range(16): + matrix = [[Fraction((case + 3 * i - 5 * j) ** 2 - 7, 2 ** ((case + i + j) % 3)) + for j in range(n)] for i in range(n)] + need(kernel.determinant(matrix) == reference(matrix), "rational determinant conformance") + count += 1 + triangular = [[2 ** (65 + i) if i == j else int(i < j) for j in range(n)] for i in range(n)] + need(kernel.determinant(triangular) == prod(2 ** (65 + i) for i in range(n)), "large integer determinant") + count += 1 + matrix = [[2, 3], [5, 7]] + need(kernel.determinant(matrix) == -1, "initial mutable matrix") + matrix[0][0] = 4 + need(kernel.determinant(matrix) == 13, "stale matrix result reused") + return count + 2 + + +def check_rejections(): + rejected = 0 + for matrix in ([[1, 2]], [[1, 2, 3], [4, 5, 6]], [[0] * 6 for _ in range(6)]): + try: + kernel.determinant(matrix) + except ValueError: + rejected += 1 + else: + raise ValueError("invalid determinant domain accepted") + original = kernel.SCHEDULES + matrix = [[1, 2, 4], [3, 7, 5], [11, 13, 17]] + expected = kernel.determinant(matrix) + try: + for broken in (original[3][:-1], tuple((p, -s) for p, s in original[3])): + kernel.SCHEDULES = original[:3] + (broken,) + original[4:] + need(kernel.determinant(matrix) != expected, "schedule corruption undetected") + rejected += 1 + finally: + kernel.SCHEDULES = original + original_det = adapter.determinant + try: + adapter.determinant = lambda matrix: -original_det(matrix) + try: + adapter.run("compare") + except ValueError as error: + need(str(error) == "determinant disagreement", "wrong comparison rejection") + rejected += 1 + else: + raise ValueError("corrupt determinant accepted by complete comparison") + finally: + adapter.determinant = original_det + return rejected + + +def exported_source_controls(expected_output): + manifest = json.loads((HERE / "SOURCES.json").read_text()) + inputs = [pin["path"] for pin in manifest["files"] if pin["usage"] == "input"] + need(len(inputs) == 5, "source-control capacity") + with tempfile.TemporaryDirectory(prefix="leibniz-controls-") as temporary: + root = Path(temporary) + paths = inputs + [str((HERE / name).relative_to(ROOT)) for name in ("adapter.py", "kernel.py", "SOURCES.json")] + for relative in paths: + target = root / relative + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(ROOT / relative, target) + argv = [sys.executable, "-I", "-W", "error", "-B"] + if sys.flags.optimize: + argv.append("-O") + argv.append(str(root / HERE.relative_to(ROOT) / "adapter.py")) + environment = dict(os.environ, PATH="") + result = subprocess.run(argv, cwd=root, env=environment, capture_output=True, timeout=30, check=False) + need(result.returncode == 0 and not result.stderr and result.stdout.decode() == expected_output, + "Git-free complete export replay") + for relative in inputs: + target = root / relative + original = target.read_bytes() + target.write_bytes(original + b"\n") + result = subprocess.run(argv, cwd=root, env=environment, capture_output=True, timeout=30, check=False) + need(result.returncode != 0 and b"source hash mismatch" in result.stderr and not result.stdout, + "actual corrupted-source execution accepted") + target.write_bytes(original) + return len(inputs) + + +def check_measurement(output): + receipt = json.loads((HERE / "MEASUREMENT.json").read_text()) + samples = receipt["samples"] + need(len(samples) == 10 and receipt["errors"] == 0, "benchmark completion") + need([row["engine"] for row in samples] == ["reference", "scheduled"] * 5, "benchmark order") + need({row["stdout_sha256"] for row in samples} == {hashlib.sha256(output.encode()).hexdigest()}, + "benchmark scientific output") + need(len(receipt["source_hashes"]) == 9, "benchmark input capacity") + for path, digest in receipt["source_hashes"].items(): + need(hashlib.sha256((ROOT / path).read_bytes()).hexdigest() == digest, "benchmark source changed") + for engine in ("reference", "scheduled"): + values = [row["elapsed_ns"] for row in samples if row["engine"] == engine] + need(all(type(value) is int and 0 < value < 30_000_000_000 for value in values), "timing domain") + need(receipt["summary"][engine] == {"median_ns": statistics.median(values), "min_ns": min(values), "max_ns": max(values)}, + "benchmark summary") + a = receipt["summary"]["reference"]["median_ns"] + b = receipt["summary"]["scheduled"]["median_ns"] + need(receipt["reduction"] == 1 - b / a, "benchmark arithmetic") + + +def main(): + reference = adapter.load_caller().det + schedules = check_schedules() + values = check_values(reference) + controls = check_rejections() + original, _ = adapter.run("reference") + scheduled, _ = adapter.run("scheduled") + compared, trace = adapter.run("compare") + need(original == scheduled == compared, "complete scientific output differs") + need(trace == {"dimensions": [0, 10331, 8447, 705, 138, 33], + "sha256": "868f3a5d2c37364d7a93986027182bc08b620f98d2ddb6daf7ec945875edea39"}, + "complete determinant transcript differs") + corruptions = exported_source_controls(original) + check_measurement(original) + hashes = {path.name: hashlib.sha256(path.read_bytes()).hexdigest() + for path in sorted(HERE.iterdir()) if path.is_file()} + receipt = {"pass": "PASS_SIGNED_LEIBNIZ_REPLAY", "schedules": schedules, "conformance_values": values, + "complete_determinants": sum(trace["dimensions"]), "trace": trace, + "rejecting_controls": controls, "actual_source_corruptions": corruptions, "package_sha256": hashes} + print(json.dumps(receipt, sort_keys=True)) + + +if __name__ == "__main__": + main()