Equations
- «term_∘ᵣ₁_» = Lean.ParserDescr.trailingNode `«term_∘ᵣ₁_» 140 141 (Lean.ParserDescr.binary `andthen (Lean.ParserDescr.symbol " ∘ᵣ₁ ") (Lean.ParserDescr.cat `term 140))
Instances For
Equations
- «term_∘ᵣ₂_» = Lean.ParserDescr.trailingNode `«term_∘ᵣ₂_» 140 141 (Lean.ParserDescr.binary `andthen (Lean.ParserDescr.symbol " ∘ᵣ₂ ") (Lean.ParserDescr.cat `term 140))
Instances For
The idle transition: no state change, empty trace. Unit of both compositions above
(lcomp₁.left_id_eq, lcomp₂.left_id_eq/.right_id_eq), base case of a statement list's semantics
(GuardedPlusCal.Block.listReducing), semantics of a branch with no precondition
(AtomicBranch.reducing), and the reflexive half of Relation.starFun.
Exposed for the same reason the two compositions are: proofs destructure membership directly with
rintro ⟨rfl, rfl⟩.
Instances For
Set.union_subset_union restated at ≤. Mathlib tags only the ⊆ form for gcongr, while
the two composition lemmas above are tagged at ≤, so a goal mixing a union with a composition —
which is every monotonicity obligation of a semantic functional — matches at neither relation and
gcongr reports no progress. Registering the ≤ form is what lets it descend through both.
Refolding a two-step run's aborting set: the abort that happens after both steps can be attached
to the second step alone. Read left to right it is how a cons of aborting semantics is taken apart;
read right to left it is how the tail of an induction is put back together.
One step of an "aborting commutes past" induction. Q is the statement being moved leftwards
and R what it is moved past; Qa/Ra are their aborting sets, Q'/Qa' what Q becomes on the
far side.
The three hypotheses are the three things such a step ever needs: that the reducing relations
commute (hcomm), that Q's own aborts are covered once it has crossed R (hhead), and that the
rest of the run is covered (htail). hmid absorbs a preceding inclusion — the induction hypothesis,
where there is one, and le_rfl where the run is already in this shape.
Stated on bare relations because every user is the same algebra over different statements: a guard substituted into, a guard whose index was bumped, and a whole walk.
Infinite iteration #
R^∞: the executions that take infinitely many R-steps. Defined directly, from a sequence
of states and a sequence of emitted traces, rather than as the greatest fixed point of
X ↦ R ∘ᵣ₁ X.
The gfp is the wrong denotation, in a way that has nothing to do with how hard it is to reason
about. A step emitting the empty trace makes that functional non-contractive — R ∘ᵣ₁ x ⊇ x — so
at R = {(σ, 1, σ)} it is the identity, whose greatest fixed point is ⊤: every trace
whatsoever, paired with a state that merely diverges silently. R^∞ gives that execution the
trace 1, which is what it actually emits. The two agree only when R has no infinite chain of
empty-trace steps, which Algebra.step certainly does (while TRUE { x := x + 1 }).
e 0 * ⋯ * e (n-1), and 1 when n = 0.
Equations
- Monoid.partialProd e 0 = 1
- Monoid.partialProd e n.succ = Monoid.partialProd e n * e n
Instances For
The same product peeled from the left instead of the right. partialProd folds right-to-left,
but a run built forwards from a starting state produces its factors left-to-right, so relating the
two is what lets a prefix of a run be recognised as a partialProd.
A product splits wherever its index does. What lets the trace of two runs concatenated be read as the two runs' traces multiplied.
Skipping a stretch of ones. Extending a product past factors that are all 1 does not
change it — the gap-splitting fact a reindexed product needs, since deleting 1s from a sequence is
exactly refusing to extend across them. partialProd_add does the splitting; this says the second
factor is trivial.
A monoid in which an infinite sequence of factors has a well-behaved product. A mixin over
Monoid rather than an extension of it, so that the existing [Monoid ε] binders throughout the
refinement framework are untouched and no instance diamond arises. Carries the five laws refinement
proofs consume, so that they read them from the instance instead of threading them as explicit
hypotheses.
- ωProd : (ℕ → ε) → ε
The product of infinitely many factors.
Every finite prefix of an infinite product divides it.
The first factor of an infinite product comes out in front.
- productLimit (e r : ℕ → ε) (x : ε) : (∀ (n : ℕ), x = Monoid.partialProd e n * r n) → (∀ (n : ℕ), ∃ (m : ℕ), n ≤ m ∧ e m ≠ 1) → x = ωProd e
An element having every partial product as a left factor is the infinite product, provided the sequence keeps contributing.
- ωProd_comp (e : ℕ → ε) (n : ℕ → ℕ) : StrictMono n → (∀ (i : ℕ), (∀ (j : ℕ), n j ≠ i) → e i = 1) → ωProd e = ωProd (e ∘ n)
Deleting factors that are
1does not change the product.
Instances
Deleting idle steps from an infinite run. A run in which every index either steps or stands
still — emitting 1 when it stands still — is a run of the stepping relation alone, provided it
steps cofinally often.
This is what a stuttering simulation needs and cannot get from Relation.omega.mono:
Relation.omega (R ∪ Idle) ≤ Relation.omega R is false outright, since standing still forever is a
witness of the left and of nothing on the right. Cofinality is exactly the missing side condition,
and a caller supplies it from whatever well-founded measure forbids an infinite idle tail.
The compressed run is indexed by the moving indices, so its product is the original's with the
idle factors deleted; ωMonoid.ωProd_comp handles that.
Dropping the first step of an infinite run leaves an infinite run. Every proof that
destructures a Relation.omega membership and then has to put the tail back together needs this,
so it is stated once here rather than re-instantiated at each site.
Finite iteration #
R*. Stated in the same ℕ-indexed shape as Relation.omega rather than reusing
Relation.TraceReflTransGen (VerifiedCompiler/Relation.lean), which is Prop-valued. Sharing the
shape is what lets the two refinement lemmas — one for R*, one for R^∞ — be proved by the same
kind of induction over the index.
R* — finitely many R-steps, with the concatenated trace.
Equations
Instances For
A run is either empty or a step followed by a run. The eliminator the closed form below needs,
since Relation.star is indexed by a length rather than defined inductively.
A step in front of a run-then-Y is again a run-then-Y.
This is the absorption side condition that the aborting and diverging refinements both need in order
to place an abort reached after n steps in the aborting set itself rather than in
semⁿ ∘ᵣ₁ sem'. Stated at the closed form it is a theorem; at an arbitrary aborting semantics it
has to be assumed, which is what Diverging.omega's and Diverging.star's abs binder is.
Two runs end to end. Proved by peeling the first step of the left-hand run rather than by
concatenating the two index-wise: Relation.star.head already knows how to put a step in front, so
the induction only has to keep the trace's factors in the same order — which is
Monoid.partialProd_succ'.
Runs of runs are runs. What lets a refinement whose source side already absorbs a whole run
per target step be lifted to the whole iteration: instantiating StrongRefinement.Terminating.star
at Relation.star R produces R** on the source, and this collapses it back.
A whole run in front of a run-then-Y is again a run-then-Y. The absorption law at the
shape a refinement whose source absorbs a run per target step produces
(StrongRefinement.Terminating.starStutter): there the side condition arrives with Relation.star R
where lcomp₁_absorb has R.
lcomp₁_absorb at R := Relation.star R, with star_eq collapsing the R** it leaves behind.