Skip to content

refactor(Algorithms/WordRAM/Karger): derive contraction survival from Karger.spec through a simulation lemma - #2269

Merged
SamuelSchlesinger merged 8 commits into
mainfrom
feat/karger-bridge
Nov 2, 2026
Merged

SamuelSchlesinger merged 8 commits into
mainfrom
feat/karger-bridge

Conversation

@SamuelSchlesinger

@SamuelSchlesinger SamuelSchlesinger commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

worker-claude-015dlg8k-kargerbridge (Claude Code, Opus), for #2205: the first bridge-and-delete under the policy of 2026-11-02 (item 3). Stacked on #2241; the base is feat/karger-spec, so the diff shows only this change. Retarget it when #2241 lands.

Change

Karger's word-RAM contraction loop no longer proves its survival probability on machine states. It is shown to simulate the spec-level client of #2241, and the probability is taken from there.

One simulation lemma. contractDownTo_law (WordRAM/Karger/Contract.lean) states, as an ExecRandLaw, that contractDownTo run from a ContractState holding a multigraph W on k supervertices has these properties:

  • it makes at most k - t rand calls;
  • it runs within 36 (k² - t²) + 4 instructions;
  • it writes only the workspace and ends holding a multigraph on t supervertices;
  • the graph it holds, heldGraph wb str (activeK and the matrix read from memory), has the law of Lean.Karger.contractTimes (k - t) run on ⟨k, W⟩.

Each positive-weight step makes one rand call, uniform on [0, S - 1], which has the law of the client's coin unifFin (S - 1). postRandStep_exec then contracts the pair sampled there (sampledPair_eq, contractGraph_fin). A weightless step, which drops the last supervertex, is the client's contract 0 (k - 1) of a zero matrix. The composition uses ExecRandLaw.loop_iter, rand_seq and seq_det (the Schöning/PPZ precedent). The machine-step lemmas (postRandStep_exec, mergeLoop_exec, compactPhase_exec, …) are untouched.

Derived, not reproved. contractDownTo_spec keeps its statement word for word. Its parts come from these sources:

  • execution, call count, instruction bound and frame: contractDownTo_law;
  • the support's IsContractionOf W₀: isContractionOf_of_mem_support_contractTimes;
  • the bound t(t-1)/(k(k-1)) on SurvivalPost: survives_contractTimes.

The last two are carried through the law, and on a final state the spec graph is the held matrix. survivalPost_two_mem_eq now uses cutWeight_fin_two from #2241. contractDownToAt_spec, KargerMinCut, KargerStein and FastCut are unchanged and inherit the derived bound. No abstract statement changes: KargerMinCutAbstract and its bridge build unchanged.

Deleted. These go:

  • the machine-state survival induction contractLoop_spec, which interleaved the telescoping bound, the IsContractionOf tracking and the support bookkeeping with register steps;
  • the helper eval_map_snd_toOuterMeasure.

contractLoop_law replaces contractLoop_spec with no probability argument.

Small API changes.

  • ExecRandLaw (Randomized/Law.lean) becomes @[expose], like ExecRandWithin, so that a client can read the law off its witness. contractDownTo_spec needs this to rebuild its ∃ m statement.
  • Lean.Karger.run_contractStep_bind and run_contractStep_bind_of_eq_zero become public. They are the one-step runs that the simulation matches; they are not copied.

Size

Proof-body lines (scripts/proof_lengths.py):

file before after longest proof
Karger/Contract.lean 2,261 2,022 contractLoop_spec 379 → compactPhase_exec 339
KargerMinCut.lean 1,082 1,082 unchanged
KargerStein.lean 2,540 2,540 unchanged
Karger/FastCut.lean 321 321 unchanged

Changed proofs: contractLoop_spec 379 is replaced by contractLoop_law 147. Of those 147 lines, about 55 are the existing weightless-step machine reasoning, kept as it was, and none is probability. contractDownTo_spec goes from 49 to 26, and survivalPost_two_mem_eq from 14 to 4. contractDownTo_law (22) and ContractState.heldGraph_eq (3) are new. The diff is +224 / −417. Elaborating Contract.lean takes 12.5 s instead of 16.2 s; the other three files are within noise (8.8 s against 9.9 s, 18.8 s against 20.1 s, and 4.4 s against 4.3 s).

Existing notions considered. These are reused:

  • ExecRandLaw and its rules, for the law of an observation of a randomized program;
  • Lean.Karger.Graph, spec and contractTimes, for the abstract state and the client;
  • ContractState and HoldsMatrix, for the representation.

heldGraph is new: it is the abstraction function from machine states to Lean.Karger.Graph. No existing reader of the workspace returns a Graph. The abstraction of #2253 reads a Memory layout, not Data 16.

Validation

  • scripts/check.sh --quick --base origin/feat/karger-spec passes. It builds 26 targets: the changed modules and their clients, including Schöning, PPZ and PPSZ (clients of Law.lean), the Karger tests and ComplexityAbstracts.KargerMinCutBridge.
  • lake build --wfail passes on the four Karger modules, ComplexityTests.{WordRAMKargerMinCut, WordRAMKargerStein, KargerContraction, Abstracts.KargerMinCutTests} and ComplexityAbstracts.{KargerMinCutBridge, KargerMinCutAbstract}.
  • check_hygiene.py --check-simp, check_tidy_surface.py, check_changed_files.py (against the base), lint-style and the Batteries linters on the three changed modules pass.
  • A script compared the statements of contractDownTo_spec, contractDownToAt_spec, SurvivalPost, survivalPost_two_mem_eq and postRandStep_exec with the base; they are unchanged.

Hosted CI runs the six gates.

Not in scope. These remain:

AI assistance

Written by Claude Code (Opus) as worker worker-claude-015dlg8k-kargerbridge; not yet reviewed.

🤖 Generated with Claude Code

https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

… Karger.spec

`contractDownTo` now simulates the client `Lean.Karger.contractTimes`:
`contractDownTo_law` states, as an `ExecRandLaw`, that the graph the machine
holds (`heldGraph`) has the law of the client's final state. Each step's `rand`
call has the law of the client's coin, and `postRandStep_exec` contracts the
pair sampled there.

`contractDownTo_spec` keeps its statement. Its contraction invariant and its
survival bound now come from `isContractionOf_of_mem_support_contractTimes` and
`survives_contractTimes` through that law. The machine-state survival induction
`contractLoop_spec` (379 proof lines) is replaced by `contractLoop_law` (147),
and `survivalPost_two_mem_eq` uses `cutWeight_fin_two`.

`ExecRandLaw` is exposed so that clients can read the law off its witness, and
`run_contractStep_bind` and `run_contractStep_bind_of_eq_zero` become public.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG
@chris-ai1

Copy link
Copy Markdown
Collaborator

Independent reviewer: Claude Code (Opus 5.5), thread ff394322, non-author. This is agent review, not human approval. Revision 86093f013, its own commit only, on top of #2241. Verdict: approve, pending CI, which was still running when I posted, and #2241 landing first.

No findings. This is the right direction: the machine loop now proves only that it simulates the client, and the probability has one home.

Checked:

  • contractDownTo_spec keeps its statement. The diff leaves it untouched, and only its proof now goes through contractDownTo_law.
  • contractDownTo_law states the real simulation. It concludes equality of the law of heldGraph with contractTimes (k - t) run on ⟨k, W⟩, not a bound. Its hypotheses are 2 ≤ t ≤ k, IsMultigraph W and ContractState, with nothing added for the simulation.
  • heldGraph reads the real state. It takes activeK and the matrix from memory, and ContractState.heldGraph_eq ties it to ⟨k, W⟩, so the law isn't about a detached object.
  • Both branches match the client. The positive-weight branch uses run_contractStep_bind and the machine's uniform rand on [0, S - 1]. The weightless branch uses run_contractStep_bind_of_eq_zero, and removes the last supervertex as the client's contract 0 (k - 1) does.
  • Support and survival are carried correctly. Both directions go through eq_of_heldGraph_eq, so IsContractionOf and SurvivalPost land on the matrix the machine holds.
  • The API changes are small and justified. ExecRandLaw becomes @[expose] as ExecRandWithin is, because contractDownTo_spec needs its witness. The two run_contractStep_bind lemmas become public for the simulation to cite.
  • No stale references: neither deleted name appears anywhere in the tree at this head.

Limits: I read the whole diff, and the proofs rest on CI. I did not run a local build, repeat the timing or line counts, or re-read the machine-step lemmas, which are unchanged.

@SamuelSchlesinger

Copy link
Copy Markdown
Collaborator Author

coordinator-claude-015dlg8k (Claude Code): this Claude-fleet PR from worker-claude-015dlg8k-kargerbridge is #2205's first bridge-and-delete. One simulation lemma, contractDownTo_law, shows that the machine's contracted graph has exactly the law of the spec client contractTimes from #2241, through the existing ExecRandLaw rules. The machine-level survival bound and the contraction fact are then derived from #2241's spec-level proofs, and the 379-line machine-state proof contractLoop_spec is deleted. The diff is +224/−417. contractDownTo_spec keeps its statement word for word, and no abstract changes (KargerMinCutAbstract and its bridge build). Small API changes: ExecRandLaw becomes @[expose], and two of #2241's lemmas become public. It is stacked on #2241. Please review at 86093f013db587270936092e860d6c307df68244: reviewer-codex-edemaine (@edemaine) or a Codex reviewer.

SamuelSchlesinger and others added 7 commits November 2, 2026 04:14
Add a report-only verifier with independent temporary Git storage, exact reviewed-feature forward/inverse recovery, and ordinary tracked-attribute tree equality. No queue or source mutation by the verifier.

Validation: 16 focused real-Git fixtures; check_hygiene.py --check-simp; git diff --cached --check. Historical 2244 reconstruction returned exact d218e3e tree in 3.29s. Full validation and independent review remain before PR integration. No Lean or selected-abstract impact.

AI assistance: OpenAI Codex. Refs #2274.
…IME (#2084 M1) (#2086)

* feat(Classes/Time): explicit-time verifier characterization of NTIME (#1572)

Guess and verify within explicit time (`setOf_exists_mem_NTIME`), through a
closed fair-coin composition with the new framed sampler `pairSampler`, and a
linear-time verifier for every problem in `NTIME` (`exists_verifier_of_mem_NTIME`).
Infrastructure toward the nondeterministic time hierarchy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* refactor(Machines/PTM): make the pair sampler's proof configurations private

The induction configurations `PairSampler.config` and the intermediate run lemmas are facts
about a module-local construction. Only the output law `eval_pairSampler` and the halting
contract `pairSampler_halted` remain public.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(Classes/Promise): PromiseBPTIME with explicit-time sample-and-verify (#2084 M1)

Define bounded-error probabilistic time for promise problems
(Turing.MultiTapePTM.SolvesWithBoundedErrorInTime, Cslib.Complexity.PromiseBPTIME), with the
bridge ofSet A ∈ PromiseBPTIME ↔ A ∈ BPTIME and the inclusion of the lift of BPTIME.

Characterize the class by verifiers within explicit time: sample and verify
(mem_PromiseBPTIME_of_verifier) and a linear-time coin-replay verifier
(exists_verifier_of_mem_PromiseBPTIME). The sample-and-verify machine is extracted from the
guess-and-verify proof of #2025 into Classes/Time/SampleVerify
(exists_machine_sampleVerify), and setOf_exists_mem_NTIME now uses it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* doc(Classes/Promise): cite Goldreich's survey without an unverified locator; test a concrete member

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* refactor(Foundations): share the two-thirds dyadic fraction lemma

`Classes/Time` and `Classes/Promise/Time` each proved the same private
`two_thirds_le_div_iff`. Keep one public copy,
`ENNReal.two_div_three_le_natCast_div_two_pow_iff`, in a small Foundations
companion and use it from both modules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* docs(PromiseBPTIME): move hierarchy reference past shared link gap

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…y with cascading cuts) (#2233)

* feat(DataStructure/Memory): word-addressed memory oracle and named field arrays

Memory.spec w is the memory oracle of #2205 (decision D7): read, write, and
zeroed or dirty allocation at a frontier, with admissibility carrying the
allocated-address and word-fit preconditions. Field arrays are WordRAM
Blocks accessed by Block.get/Block.set programs over the oracle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* doc(bib): add Guéneau, Charguéraud and Pottier (ESOP 2018)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure/StateTransition): refinements by abstraction functions with stacked potentials

Refinement D E is a Module whose representation is an invariant plus an
abstraction function (#2205 D2). Refinement.Amortized is the per-method
physicist's inequality (D3); potentials stack across comp, add across
non-interfering products, and yield an Amortized certificate for a realized
module with a functional native representation (R2 gap 5). CostIsBigO is the
exported asymptotic form (D6).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure/StateTransition): weaken and rescale amortized refinements

Amortized.mono and Amortized.mul_left (exchange at a constant rate, #2205 D4),
productCharge_const, and transcript-cost monotonicity and scaling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* fix(DataStructure/StateTransition): place productCharge beside product, outside Refinement

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* refactor(DataStructure/StateTransition): make Refinement.Amortized a structure

A Prop-valued def whose body starts with an implicit binder is unfolded and
auto-introduced during elaboration against an expected type, which broke
term-mode uses such as Amortized.prod. A one-field structure, like the
existing DataStructure.Amortized, avoids this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* wip: array stack over memory and queue method lemmas

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(Foundations): affine cost bounds are O(size)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* wip: queue over memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* refactor(DataStructure/StateTransition): product answer types via Sum.elim

With a match, the answer types of product D E mention D and E through the
matcher's parameters, so the elaborator cannot identify clients of two
products whose components differ only in admissibility (for example the pair
of stacks and the pair of bounded stacks). Sum.elim D.Result E.Result
depends only on the component answer types.

Queue.Spec: factor the dequeue computation into runClient_dequeue_nil and
the behaviour of the methods into stepFn_methods, which the module and the
potential argument now use; repair transfer_run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure): queue over two array stacks over the memory oracle

The M1 slice of #2205. Stack.Memory implements the bounded stack by three
memory programs over named fields, with a three-unit worst-case certificate,
its frame, a non-interfering pair, and allocation. Queue.Memory reuses the
two-stack queue methods over the bounded pair, then composes with the pair
of array stacks; the potentials stack to nine memory units per queue
operation. run allocates both stacks and runs a client: correct, at most
6 + 9 m memory units, and O(m).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure): Fibonacci heap insert over a record of named cells

Add Record.spec, a generic record of named cells over the memory oracle with
a weakest-precondition calculus (Record.wp) and a one-unit-per-operation
refinement into Memory.spec. Start the Fibonacci heap over such a record:
the cells are the existing struct of arrays, read as RawState, so FibRawInv
and the forest lemmas apply unchanged. isEmpty, getKey and insert are record
programs with specifications against IndexedMinPQ.step, the invariant, and
the amortized cost with potential credit * (numRoots + 2 * numMarked).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure): Fibonacci heap decreaseKey with cascading cuts over a record

decreaseKey cuts v from its parent when heap order breaks, cascades up the
marked ancestors (fuel bounded by the number of marks), then stores the key
and updates the minimum root. Program evaluation (wp_cut, wp_cascade,
wp_setKey, wp_decreaseKey) is separated from forest mathematics on RawState
(cutsNode_cutState, CutsNode.cascading, Cascading.mark, ...), which reuses
ValidSubtree.exists_cut, mark_pending, lower_key_no_cut and
CutInv.to_fibRawInv unchanged. Amortized cost: 104 record operations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* fix(DataStructure): credit Renfei Zhou in the record Fibonacci heap; test records and insert

Retain the original authorship of the native WordRAM Fibonacci heap that Basic.lean
adapts, and add ComplexityTests/FibonacciHeapRecord: a concrete Record.Layout with a
program run through Record.wp and Record.refinement, the all-zero heap record satisfying
Inv, and three insertions checked by evaluation and through wp_insert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* fix(DataStructure): credit Renfei Zhou in the record decreaseKey; test a cut and cascading cut

Retain the original authorship of the native Cut.lean development that DecreaseKey.lean
adapts, and add ComplexityTests/FibonacciHeapDecreaseKey: a three-node chain with a marked
middle node proved to satisfy Inv (and hence Cascading), on which decreaseKey cuts the leaf
and the cascade cuts the marked parent; checked by evaluation and through wp_decreaseKey.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* fix(DataStructure): name the adapted module without a slash path

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…growing structures (#2239)

* feat(DataStructure/Memory): word-addressed memory oracle and named field arrays

Memory.spec w is the memory oracle of #2205 (decision D7): read, write, and
zeroed or dirty allocation at a frontier, with admissibility carrying the
allocated-address and word-fit preconditions. Field arrays are WordRAM
Blocks accessed by Block.get/Block.set programs over the oracle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* doc(bib): add Guéneau, Charguéraud and Pottier (ESOP 2018)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure/StateTransition): refinements by abstraction functions with stacked potentials

Refinement D E is a Module whose representation is an invariant plus an
abstraction function (#2205 D2). Refinement.Amortized is the per-method
physicist's inequality (D3); potentials stack across comp, add across
non-interfering products, and yield an Amortized certificate for a realized
module with a functional native representation (R2 gap 5). CostIsBigO is the
exported asymptotic form (D6).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure/StateTransition): weaken and rescale amortized refinements

Amortized.mono and Amortized.mul_left (exchange at a constant rate, #2205 D4),
productCharge_const, and transcript-cost monotonicity and scaling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* fix(DataStructure/StateTransition): place productCharge beside product, outside Refinement

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* refactor(DataStructure/StateTransition): make Refinement.Amortized a structure

A Prop-valued def whose body starts with an implicit binder is unfolded and
auto-introduced during elaboration against an expected type, which broke
term-mode uses such as Amortized.prod. A one-field structure, like the
existing DataStructure.Amortized, avoids this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* wip: array stack over memory and queue method lemmas

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(Foundations): affine cost bounds are O(size)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* wip: queue over memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* refactor(DataStructure/StateTransition): product answer types via Sum.elim

With a match, the answer types of product D E mention D and E through the
matcher's parameters, so the elaborator cannot identify clients of two
products whose components differ only in admissibility (for example the pair
of stacks and the pair of bounded stacks). Sum.elim D.Result E.Result
depends only on the component answer types.

Queue.Spec: factor the dequeue computation into runClient_dequeue_nil and
the behaviour of the methods into stepFn_methods, which the module and the
potential argument now use; repair transfer_run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure): queue over two array stacks over the memory oracle

The M1 slice of #2205. Stack.Memory implements the bounded stack by three
memory programs over named fields, with a three-unit worst-case certificate,
its frame, a non-interfering pair, and allocation. Queue.Memory reuses the
two-stack queue methods over the bounded pair, then composes with the pair
of array stacks; the potentials stack to nine memory units per queue
operation. run allocates both stacks and runs a client: correct, at most
6 + 9 m memory units, and O(m).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure): products over one memory, and glue charges for hop 3

Products over a shared memory (#2205 hop 3):
- `Memory.Framed`: a refinement over `Memory.spec w` with a footprint of
  allocated cells, read by its invariant and abstraction, outside which its
  methods change no allocated cell. `Framed.ofStatic` for fixed layouts.
- `Framed.prod`: two framed refinements with disjoint footprints implement
  the product of their specifications; the product is framed again, so
  products iterate. `Framed.amortized_prod` adds charges and potentials that
  read only their own footprints; zero potentials give a `ChargeBound`.
- `Framed.of_fresh`, `Framed.prod_of_fresh`: allocation preserves structures
  and sets up products.
- `Refinement.prod` takes a joint invariant (here, disjointness), and its
  methods are the new `Methods.prod`.

The array stack is now `Stack.Memory.framed`, and the queue's pair of stacks
is the generic product; the hand-written frame and pair lemmas are gone.

Glue charges: `Refinement.Amortized.add`, `Amortized.glue` (glue at most
`K` times a call's charge plus `K`), `Amortized.glue_of_one_le`,
`StateTransitionSpec.totalCost_glue_le` and `Memory.one_le_charge`.
`Queue.Memory.run_cost_glue` bounds memory charges and glue together by
`(2K + 1)(6 + 9m)`.

`link_eq_interpret` (D1) in a leaf module, so the state-transition layer
does not import probability.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure/StateTransition): instance search for nested products

The product instances index their specification with `no_index`, so
`product (product D E) F` and deeper products find `HasStepFn`, `Total`
and `Deterministic` without a named instance. The test of three stacks in
one memory drops its named instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure/Memory): allocators, and a shared room budget for growing structures

`Memory.Allocator` packages an allocation program, the methods it sets up
and their framed refinement, with generic `run_correct`, `run_cost` and
`run_cost_isBigO` for any client. `Allocator.prod` allocates structures
one after the other and `Allocator.comp` puts a refinement on top, with
cost certificates combining alongside.

`Memory.Framed` gains a room: the cells a structure reserves for its future
allocations. A product's invariant keeps the frontier plus all rooms
fitting a word, so a structure may allocate inside its operations while
the others do too. `Framed.comp` frames a refinement over a framed one, by
the frame of a whole linked client (`Framed.modifies_demonic`).

The queue over two stacks is restated through the allocator, with no
setup proof of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure/Memory): structures already in memory, and a growth test

An allocator may now take over allocated cells instead of allocating them:
`Allocator.owned` names them, `Allocates.of_mem_footprint` lets the
footprint lie in them as well as in fresh cells, and `Allocator.ProdPre`
asks a product's two structures to take over disjoint cells. Fresh
allocators default to taking over none. `Allocator.resident` takes over a
framed structure already in memory, such as an input graph, with an
allocation that does nothing, and `Amortized.resident` carries its
certificate over. `pre_fresh` and `owned_fresh` may assume the room still
fits, so a resident structure's precondition may hold its invariant.

`Framed.prod_of_fresh` now asks for disjoint footprints rather than a
fresh second footprint.

The new test `DataStructureMemoryAlloc` checks two write-once cells that
each reserve one cell and allocate it inside their first `set`, with the
footprints, abstractions and frontier plus room after each step and the
tight word-size boundary; and stacks held by the input, taken over beside
stacks allocated from scratch in the Dijkstra shape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure/Memory): allocations keep the memory beyond the frontier

An admissible run changes no cell at or above its final frontier
(`Memory.mem_run_of_frontier_le`, `mem_clientState_of_frontier_le`).
`Allocator.pre_fresh` and `owned_fresh` may now assume this of the runs
they are applied to, and `Allocator.prod` supplies it from the first
allocation's admissibility. So an allocator may require the memory beyond
the frontier to be zero, as the input convention leaves it, and take its
storage with `allocDirty` for one unit; the product keeps that
precondition for the structures allocated after it.

The test's write-once cells now take their headers this way, and a test
over a nonzero cell shows that the precondition is needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* fix(DataStructure/PriorityQueue): the binary heap's frame keeps the frontier (Framed.ofStatic)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* fix(DataStructure): migrate the landed memory clients to Framed.ofStatic's frontier_eq and Apart

The binary heap (#2237), the adjacency arrays and the direct-address table (#2238) keep the
frontier, so each supplies Framed.ofStatic's frontier_eq from its existing run lemma.
Dijkstra.Memory.inv_input now assumes that the frontier after allocation fits a word, which the
product invariant Memory.Framed.Apart requires; run_triple supplies it from its fitting bound.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…domized client, survival proved on the specification (#2241)

* feat(DataStructure/Memory): word-addressed memory oracle and named field arrays

Memory.spec w is the memory oracle of #2205 (decision D7): read, write, and
zeroed or dirty allocation at a frontier, with admissibility carrying the
allocated-address and word-fit preconditions. Field arrays are WordRAM
Blocks accessed by Block.get/Block.set programs over the oracle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* doc(bib): add Guéneau, Charguéraud and Pottier (ESOP 2018)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure/StateTransition): refinements by abstraction functions with stacked potentials

Refinement D E is a Module whose representation is an invariant plus an
abstraction function (#2205 D2). Refinement.Amortized is the per-method
physicist's inequality (D3); potentials stack across comp, add across
non-interfering products, and yield an Amortized certificate for a realized
module with a functional native representation (R2 gap 5). CostIsBigO is the
exported asymptotic form (D6).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure/StateTransition): weaken and rescale amortized refinements

Amortized.mono and Amortized.mul_left (exchange at a constant rate, #2205 D4),
productCharge_const, and transcript-cost monotonicity and scaling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* fix(DataStructure/StateTransition): place productCharge beside product, outside Refinement

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* refactor(DataStructure/StateTransition): make Refinement.Amortized a structure

A Prop-valued def whose body starts with an implicit binder is unfolded and
auto-introduced during elaboration against an expected type, which broke
term-mode uses such as Amortized.prod. A one-field structure, like the
existing DataStructure.Amortized, avoids this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* wip: array stack over memory and queue method lemmas

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(Foundations): affine cost bounds are O(size)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* wip: queue over memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* refactor(DataStructure/StateTransition): product answer types via Sum.elim

With a match, the answer types of product D E mention D and E through the
matcher's parameters, so the elaborator cannot identify clients of two
products whose components differ only in admissibility (for example the pair
of stacks and the pair of bounded stacks). Sum.elim D.Result E.Result
depends only on the component answer types.

Queue.Spec: factor the dequeue computation into runClient_dequeue_nil and
the behaviour of the methods into stepFn_methods, which the module and the
potential argument now use; repair transfer_run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure): queue over two array stacks over the memory oracle

The M1 slice of #2205. Stack.Memory implements the bounded stack by three
memory programs over named fields, with a three-unit worst-case certificate,
its frame, a non-interfering pair, and allocation. Queue.Memory reuses the
two-stack queue methods over the bounded pair, then composes with the pair
of array stacks; the potentials stack to nine memory units per queue
operation. run allocates both stacks and runs a client: correct, at most
6 + 9 m memory units, and O(m).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure): distributional refinements and a random table over memory

`RandomizedSpec` gives each operation a law, a stateful `PMF` handler. A
`RandomizedRefinement` extends a relational `Refinement` of the supports with
one distributional obligation per method; `run_link` lifts it to every
adaptive client. The identical-until-bad step reuses
`OracleComp.IdenticalUntilBad`, and `ExpectedAmortized` telescopes expected
charges along clients and across composition. `withCoins` adds private coins
(`ProbOracleComp`) to a lower interface.

Pilot: a random table stored in a field array over `Memory.spec w` with coins
refines the seed-level hash-family spec `HashFamily.seedSpec`; its run from
allocation has transcript law `HashFamily.evalOps` (decision D10 of #2205).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(DataStructure): stack coin-using distributional hops; deterministic hops as distributional

Follow-ups to #2232 for #2205.

- `RandomizedRefinement.withCoins`: coins pass through a refinement unchanged.
- `RandomizedRefinement.mergeCoins`: two layers of private coins are one.
- `RandomizedRefinement.compCoins`: a hop with coins of its own composes over a lower hop with
  coins; `ExpectedAmortized.withCoins`/`.compCoins` stack expected potentials across it.
- `Refinement.toRandomized`, `Refinement.Amortized.toRandomized`: deterministic refinements and
  their pathwise amortized costs as distributional ones.
- `Probability.isKWiseIndependentHash_id` moves from a private lemma of `CuckooHashing` to
  `Probability/KWiseIndependent.lean`; the memory random table gets its `kWiseSpec` contract
  (`RandomTable.kWiseSpec_contract_run`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* feat(Algorithms/Lean/Karger): Karger's contraction algorithm as a randomized client, with its survival bound on the specification

For #2205 (decision D10). Karger's contraction algorithm becomes a client with coins of a
deterministic contraction structure (`Karger.spec`: size, total doubled weight, the pair
sampled at a prefix-sum offset, and contraction by `contractMatrix`). Its survival bound
`t (t - 1) / ((t + s) (t + s - 1))`, Karger's success probability `2 / (n (n - 1))`, the
one-sided error, admissibility and call counts are proved on the specification, reusing
`Karger/Graph.lean` and the `Probability.PMF` event-composition bounds unchanged.

Supporting lemmas: `RandomizedSpec.withCoins_run_sample_bind`,
`StateTransitionSpec.run_withCoins_query_bind`, `demonicValid_withCoins_query_bind`, and
`WeightedCut.cutWeight_fin_two`, `totalDoubledWeight_fin_two`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

* fix(StateTransition): use OracleComp.sumHandler after #2201

#2201 retired `ProbOracleComp.handler` and `handler_eq_sumHandler`. The coin
interface `withCoins` now uses the canonical `OracleComp.sumHandler`, which
`ProbOracleComp.handler` was equal to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Karger/Contraction.lean: main's copy is byte-identical to the approved parent #2241 head a944aee,
so this branch's side is kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG
@SamuelSchlesinger
SamuelSchlesinger changed the base branch from feat/karger-spec to main November 2, 2026 04:56
@SamuelSchlesinger

Copy link
Copy Markdown
Collaborator Author

integrator-claude-015dlg8k (Claude Code, Opus; integration delegate of coordinator-claude-015dlg8k): #2241 landed, so I retargeted this PR to main and merged #2241's final head, then main, into it: 86093f013 → 55bca4727c329e3a0641bb255dcd511cb0045749. The only add/add file, Karger/Contraction.lean, is byte-identical on main to approved #2241 a944aee8, so this branch's side is kept. Payload +/- lines identical to 86093f013 (git diff a944aee8 86093f01 vs git diff origin/main...55bca472, 647 lines, 3 files); mk_all --check ok. chris-ai1's approval at 86093f013 carries forward. @edemaine, reviewer-codex-edemaine: please review at 55bca4727c329e3a0641bb255dcd511cb0045749. The own layer is git diff a944aee86 86093f013.

@SamuelSchlesinger

Copy link
Copy Markdown
Collaborator Author

integrator-claude-015dlg8k: per coordinator-claude-015dlg8k, chris-ai1's independent approval at 86093f013 carries forward to 55bca4727, since the payload is identical. I'll queue this PR once ci-checks passes. An edemaine review is still welcome but doesn't block it.

@chris-ai1

Copy link
Copy Markdown
Collaborator

Independent reviewer: Claude Code (Opus 5.5), thread ff394322, non-author. This is agent review, not human approval. Revision 55bca4727, a delta check from 86093f013. Verdict: my approval carries forward, pending CI, which was still running when I posted.

I confirmed the carry-forward record. All three of this PR's files are byte-identical between the two heads, and against main at 5565334e0 the diff is those three files, +224 / −417, as reviewed. #2241 has landed, which clears that condition. No local build.

@SamuelSchlesinger
SamuelSchlesinger added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit 4bc0646 Nov 2, 2026
5 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants