refactor(MultiTapeTM): Make the deterministic machine a nondeterministic one - #821
Draft
barni120400 wants to merge 18 commits into
Draft
refactor(MultiTapeTM): Make the deterministic machine a nondeterministic one#821barni120400 wants to merge 18 commits into
barni120400 wants to merge 18 commits into
Conversation
barni120400
marked this pull request as draft
August 20, 2026 08:58
barni120400
force-pushed
the
multitape/ntm-deterministic
branch
3 times, most recently
from
August 20, 2026 15:44
ca7bec3 to
a03c9d4
Compare
A nondeterministic machine replaces the transition function by a transition relation. Configurations and the effect of an action move to a shared `MultiTape/Configuration.lean`; no existing statement changes meaning. A computation path is the list of configurations it passes through together with a proof that they form a chain of steps from the initial configuration to the one it ends at, so `List.IsChainFromTo` carries the whole validity condition. `MultiTapeTM.toNTM` embeds the deterministic machine and `toNTM_computes` shows the embedding preserves computation in bounded time and space. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eterministic one `MultiTapeTM` now extends `MultiTapeNTM` with a transition function and the requirement that the permitted transitions are exactly the ones it prescribes. A deterministic machine is therefore literally a nondeterministic one rather than something translated into one, and `DeterministicToNondeterministic.lean` is removed. Everything stated of the general machine now applies unchanged: `initCfg` and `ComputesInTimeAndSpace` are no longer defined here, and `Step`, `ComputationPath`, `Computes`, `ComputesInExactTime`, `ComputesInExactSpace` and `ComputesInExactTimeAndSpace` are available on a `MultiTapeTM` directly. What is left is what follows from there being no choice to make. `step_iff` identifies the inherited `Step` with the graph of `step`; from it a computation path can only follow `runFrom`, which is one induction on the path index rather than one per measure, since the output tape lives in the configuration and a path carries no labels. `runPath` supplies a path of every length, and together they give `computesInExactTimeAndSpace_iff_runFrom`, an equivalence where `toNTM_computes` was a one-way implication. `ofTr` builds a machine from an initial state and a transition function and keeps it computable: relation to function needs choice, so `tr` stays data and `Tr_iff` records that the two agree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…function `Tr` is stored but fully determined by `tr`, so making `Tr_iff` a simp lemma lets it normalise away wherever it appears. `step_iff` no longer names it, and `Tr` is now absent from the deterministic file outside the structure itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ralise the rest Now that a deterministic machine is a nondeterministic one, most of what `Deterministic.lean` carried is either unused or true of any machine. Removed as unreachable: `runFrom_add`, `runFrom_output_eq_of_halt`, `spaceUsedByTape_le_spaceUsed`, `spaceUsed_zero_tapes_eq_zero`, `outputSymbol_of_halt`, `step_output`, `haltsAtStep`, `halting_step_unique` and `not_halts_of_repeat_nonhalt`. Nothing in the library referred to them. `TransitionRelation` was the graph of `step`, which is what the inherited `Step` already is, so it is dropped and `relatesInSteps_iff_runFrom_eq` is stated over `Step`. The space bounds turn out to mention no machine at all: their proofs are cardinality arguments about a list of configurations. `spaceUsedOfCfgs_le` and `spaceUsedOfCfgs_mono` move to `Configuration.lean`, which gives `ComputationPath.space_le` for nondeterministic machines, a bound the library did not have, and leaves `spaceUsed_linear` a corollary rather than its own proof. `visitedByTapeHead` becomes `visitedOfCfgs` at a step index, so there is now one notion of which cells a computation touches, and `spaceUsed_eq_spaceUsedOfCfgs` holds by `rfl`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… core `step` and `Step` each carried their own copy of the same case analysis: a halted configuration idles, a running one applies the chosen action. Only the choosing differed, by a function in one case and by a relation in the other. `Cfg.stepWith` and `Cfg.StepWith` move that to `Configuration.lean`, which mentions no machine, and `Cfg.stepWith_iff` states there once that choosing from the graph of a function is choosing its value. Both machines now define stepping in a line, and `step_iff` is that lemma applied. `outputSymbol` is removed. The output tape is part of a configuration, so the symbol a step emits is already recorded in `Cfg.output` and nothing referred to it, leaving the case analysis in exactly one place. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lding Marking `Cfg.stepWith` a simp definition, as `Action.apply` beside it already is, lets its two cases fire from a hypothesis on the state. The proofs that reasoned about a step no longer name the internals: `unfold step Cfg.stepWith` becomes `simp [step, hstate]`. Only the function is marked. The relation stays opaque so that `Step` continues to normalise through `step_iff` instead of unfolding into its cases. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
barni120400
force-pushed
the
multitape/ntm-deterministic
branch
from
August 29, 2026 13:02
a03c9d4 to
ef26fcc
Compare
…its run `spaceUsed` described the same thing as `ComputationPath.space` and was defined separately. It is now that space, read off the machine's own run. `ComputationPath` gains the configuration it starts from. It was pinned to `initCfg`, but `spaceUsed` and the results about it speak of a run from any configuration, so the path could not express them. `IsChainFromTo` already takes both endpoints; only the start was being fixed. `runPath cfg t` is then the run from `cfg`, `spaceUsed cfg t` is its space, and `visitedByTapeHead` is the cells its configurations touch. The three views of space agree by `rfl`: as a path's space, as a sum over the tapes, and as `spaceUsedOfCfgs` of the configurations passed through. `spaceUsed_linear` and `spaceUsed_mono` follow from the results about any run, `ComputationPath.space_le` and `spaceUsedOfCfgs_mono`, rather than being proved again for the deterministic case. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…th ends read off it The path designated one end and not the other: `start` was a parameter and `last` a field, so the two ends of the same list were described in two different ways. Neither is designated now. A path is a non-empty list of configurations whose consecutive elements are joined by a step, and `start` and `last` are its head and last element. `IsChainFromTo` is no longer needed, since fixing the endpoints was the only thing it added over `IsChain` and non-emptiness. `ComputesSuchThat` asks that a path start at the initial configuration, which the type used to carry. The deterministic side gains `runPath_start` beside `runPath_last`, and the results about a path speak of `p.start` where they spoke of a parameter. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…has steps `workTapePos_step_le` and `step_workTapes_eq_of_ne` were proved for the deterministic machine, but neither uses determinism: a head moves by at most one cell and no cell but the one under it changes because that is what applying an action does. Both are now stated of `Action.apply` in `Configuration.lean` and of `Step` in `Nondeterministic.lean`, so they hold of any machine. The deterministic machine gets them by its own step being one of those steps, which is a line each rather than a proof. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tself `step_of_halt` was proved twice, once of `Step` and once of `step`. The deterministic one is now the nondeterministic one applied to the machine's own step, so `step_iff` moves up to sit with `step`, where it belongs: it is the fact the rest of the deterministic development rests on. Also restores two `@[simp]` attributes, on `step_of_halt` and `runFrom_of_halt`, that were dropped by mistake when neighbouring declarations were removed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`workTapePos_step_le` and `step_workTapes_eq_of_ne` were restated for the deterministic machine, a line each, only to supply `tm.Step c (tm.step c)` to the general result. That fact is now stated once, as `step_step`, and the general results are applied to it where they are used. A further such result costs nothing. `path_length` was `length_cfgs` under another name and is dropped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`runFrom` iterated `step` through `Function.iterate`, so reaching the configuration after one more step took a lemma. Defining it by recursion makes `runFrom_zero` and `runFrom_succ_eq_step'` hold by `rfl`, and `runFrom_succ_eq_step`, which peeled a step off the front, is unused and gone. `step_step` was `step_iff.mpr rfl` under a name, and is inlined at its three uses. `step_of_halt` stays. The nondeterministic one is an equivalence about the relation, `Step c c' ↔ c' = c`, which cannot rewrite the term `step cfg`; the deterministic one is the equation that can, which is what makes it useful to `simp`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s about `runFrom` goes back to iterating `step` with `^[·]`. The deterministic file carried four lemmas saying a computation path follows `runFrom`, all in service of `computesInExactTimeAndSpace_iff_runFrom`. What they rest on is not `runFrom` but the uniqueness of a step, so it is stated of `MultiTapeNTM`: a machine whose steps are unique has one run of each length from a given configuration, `ComputationPath.cfgs_eq`. The deterministic file supplies that uniqueness, which `step_iff` gives in a line, and reads the measures off its own run. `path_getElem`, `path_cfgs`, `path_last` and `path_space` are gone. `ComputesInExactTimeAndSpace` was never redefined here: it is the nondeterministic notion, and the theorem states it by step index. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A run is now built rather than described. `ComputationPath.single` is the run of no steps and `ComputationPath.concat` extends one by a step, so the machine's own run is a recursion on the number of steps, and `runFrom` is the configuration it ends in. `spaceUsed` was already the run's space, so both measures are now read off the same object. Uniqueness is stated of the general machine and as an equality of runs, not just of their configurations: a machine whose steps are unique has exactly one run of each length from each configuration, `ComputationPath.eq_of_start_of_time`. That is what removes the existential. `ComputesInExactTimeAndSpace` quantifies over runs, as it must for a machine with a choice; for one without, the run in question is the machine's own, and `computesInExactTimeAndSpace_iff_runFrom` says so with nothing existential left. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…fast one Building the run says what running is; iterating `step` says how to do it. `runFrom_eq_iterate` states they agree, so the `Function.iterate` API is available on `runFrom` again, and a `csimp` lemma has the compiler evaluate it by iteration rather than by building a list of configurations. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Appending a configuration leaves one thing to check, that the run's last configuration steps to it, which is the hypothesis. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A run is a list, so proving something of every run meant reasoning about indices. `ComputationPath.induction` gives it the induction of an inductive definition instead: a run is the run of no steps, or one more step on a shorter run. The representation is unchanged, so the list API that `space` and uniqueness rest on still applies; only the way to reason about a run is added. `ComputationPath.reflTransGen`, that a run reaches its last configuration from its first, is the first thing proved that way. `IsChainFromTo` supplied it before the endpoints stopped being part of a run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…bound bounds `workTapePos_step_le` and `workTapes_step_eq_of_ne` are lemmas about what a step does to a tape, so they join the others in `TapeLemmas`, stated of any machine. `space_le` becomes `space_le_linear`, matching `spaceUsed_linear`, which it now proves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds on #820. GitHub shows that PR's commit too, since its branch is not on this repository and cannot be used as a base. Only the commits after
3e67a9bbelong here, and they are exactly:MultiTapeTMnow extendsMultiTapeNTM, permitting exactly the transition its transition function prescribes, withofTrbuilding one from a transition function. Everything defined for the nondeterministic machine therefore applies to it directly:initCfgandComputesInTimeAndSpaceare no longer defined for it,Step,ComputationPathand theComputesnotions are available on it unchanged, and the separate embedding file is gone.What is left is what follows from there being no choice to make.
step_iffidentifies the inheritedStepwith the graph ofstep; a computation path can then only followrunFrom, andrunPathsupplies one of every length, givingcomputesInExactTimeAndSpace_iff_runFrom. The previous embedding proved one direction of that; it is now an equivalence, sorunFrom,spaceUsedand theTapeLemmasresults keep their meaning.Then several things move out of the deterministic file, because none of them were deterministic:
stepandStepeach carried the same case analysis, differing only in choosing an action by a function or by a relation.Cfg.stepWith,Cfg.StepWithandCfg.stepWith_iffstate that once inConfiguration.lean, which mentions no machine. Both machines then define a step in a line, andstep_iffis that lemma applied.workTapePos_step_leandstep_workTapes_eq_of_nehold because of what applying an action does, not because of determinism. They are now stated ofAction.applyand ofStep, and the deterministic machine gets them by its own step being one of those steps.spaceUsedOfCfgs_leandspaceUsedOfCfgs_monogiveComputationPath.space_le, a bound for nondeterministic machines that the library did not have, and leavespaceUsed_linearandspaceUsed_monoas corollaries.spaceUsedwas describing whatComputationPath.spacealready described, so it is now that: the space of the machine's own run,(tm.runPath cfg t).space. The three views agree byrfl— a path's space, a sum over the tapes, andspaceUsedOfCfgsof the configurations passed through.Two changes to
Nondeterministic.leanthat a reader of #820 should see called out:ComputationPathgains nothing and loses its endpoints. It designated one end and not the other, so the two ends of one list were described in two different ways. It is now a non-empty list whose consecutive elements are joined by a step, withstartandlastread off it. Fixing the endpoints was the only thingIsChainFromToadded overIsChainand non-emptiness, so it is no longer used here;ComputesSuchThatasks that a path start at the initial configuration, which the type used to carry.spaceUsedand the results about it be read off one.Also removes declarations nothing referred to (
runFrom_add,runFrom_output_eq_of_halt,spaceUsedByTape_le_spaceUsed,spaceUsed_zero_tapes_eq_zero,outputSymbol_of_halt,step_output,outputSymbol,haltsAtStep,halting_step_unique,not_halts_of_repeat_nonhalt) andTransitionRelation, which was the graph ofstepand so is the inheritedStep;relatesInSteps_iff_runFrom_eqis stated overStep.NB: claude was used heavily throughout.