Files
agda-spa/lean/Spa/Analysis/Constant.lean
Danila Fedorin b16f14fdfd Lean migration: typeclass-based parameter passing, as in the Agda original
The port had flattened Agda's instance arguments ({{flA}}, {{evaluator}},
{{latticeInterpretation}}, {{validEvaluator}}) into explicitly threaded
values (fhL, E, I, hE). Restore them as typeclasses:

- Spa.FiniteHeightLattice: now actually used — Fixedpoint takes the
  instance instead of a FixedHeight value; FiniteMap gets the missing
  instance (height = ks.length * height B), so varsFixedHeight /
  statesFixedHeight / signFixedHeight / constFixedHeight plumbing
  disappears (instance bottoms are defeq to the old ones)
- Spa.Analysis.Forward.Evaluation: StmtEvaluator/ExprEvaluator become
  classes; the Valid* Props become Prop-classes, as in Agda
- Spa.Analysis.Forward.Adapters: the expr→stmt adapter and its validity
  are instances (Agda: the ExprToStmtAdapter instances)
- LatticeInterpretation is a class; sign/const interpretations,
  evaluators and validity proofs are instances; use sites read like the
  Agda module applications: result SignLattice prog

Proof simplifications (same theorems, proofs factored):

- Spa.Lattice.AboveBelow.monotone₂_of_strict: any ⊥-strict/⊤-dominated
  operation on a flat lattice is monotone — replaces the four near-
  identical case bashes per analysis (postulates in Agda)
- Spa.Lattice.AboveBelow.interp_sup_of/interp_inf_of: the shared flat-
  lattice interpretation case analysis, making interpSign_sup/inf and
  interpConst_sup/inf one-liners

lake build green with zero warnings; lake exe spa output verified
byte-identical (diff) to the previous, Agda-verified output.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 23:32:38 -07:00

229 lines
8.8 KiB
Lean4
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/-
Port of `Analysis/Constant.agda`.
Correspondence:
showable, ≡-equiv, ≡-Decidable- ↦ (mathlib/derived instances)
ConstLattice (AboveBelow ) ↦ ConstLattice
AB.Plain (+ 0) ↦ the AboveBelow FiniteHeightLattice instance,
seeded by `Inhabited ` (default `0`)
plus, minus ↦ plus, minus
plus-Monoˡ/ʳ, minus-Monoˡ/ʳ (postulates in Agda!)
↦ plus_mono_left/right, minus_mono_left/right
— now actually proved, via
AboveBelow.monotone₂_of_strict
plus-Mono₂, minus-Mono₂ ↦ plus_mono₂, minus_mono₂
⟦_⟧ᶜ ↦ interpConst
⟦⟧ᶜ-respects-≈ᶜ ↦ (trivial with `=`)
⟦⟧ᶜ-⊔ᶜ-, ⟦⟧ᶜ-⊓ᶜ-∧ ↦ interpConst_sup, interpConst_inf
s₁≢s₂⇒¬s₁∧s₂ ↦ interpConst_mk_disjoint
latticeInterpretationᶜ ↦ constInterpretation
WithProg.eval, eval-Monoʳ ↦ ConstAnalysis.eval, eval_mono
ConstEval ↦ ConstAnalysis.exprEvaluator
plus-valid, minus-valid ↦ plus_valid, minus_valid
eval-valid, ConstEvalValid ↦ eval_valid
output ↦ ConstAnalysis.output
analyze-correct ↦ ConstAnalysis.analyze_correct
-/
import Spa.Analysis.Forward
import Spa.Analysis.Utils
import Spa.Showable
namespace Spa
abbrev ConstLattice : Type := AboveBelow
namespace ConstAnalysis
open AboveBelow in
/-- Agda: `plus`. -/
def plus : ConstLattice ConstLattice ConstLattice
| bot, _ => bot
| _, bot => bot
| top, _ => top
| _, top => top
| mk z₁, mk z₂ => mk (z₁ + z₂)
open AboveBelow in
/-- Agda: `minus`. -/
def minus : ConstLattice ConstLattice ConstLattice
| bot, _ => bot
| _, bot => bot
| top, _ => top
| _, top => top
| mk z₁, mk z₂ => mk (z₁ - z₂)
/-- Agda: `plus-Mono₂` (its components were postulates in Agda; `plus` is a
strict operation on the flat lattice, so monotonicity holds regardless of the
constant table). -/
theorem plus_mono₂ : Monotone₂ plus :=
AboveBelow.monotone₂_of_strict plus
(fun y => by cases y <;> rfl) (fun x => by cases x <;> rfl)
(fun y hy => by cases y <;> first | exact absurd rfl hy | rfl)
(fun x hx => by cases x <;> first | exact absurd rfl hx | rfl)
/-- Agda: `plus-Monoˡ` — a postulate there, a theorem here. -/
theorem plus_mono_left (s₂ : ConstLattice) : Monotone (plus · s₂) := plus_mono₂.1 s₂
/-- Agda: `plus-Monoʳ` — a postulate there, a theorem here. -/
theorem plus_mono_right (s₁ : ConstLattice) : Monotone (plus s₁) := plus_mono₂.2 s₁
/-- Agda: `minus-Mono₂` (likewise from strictness of `minus`). -/
theorem minus_mono₂ : Monotone₂ minus :=
AboveBelow.monotone₂_of_strict minus
(fun y => by cases y <;> rfl) (fun x => by cases x <;> rfl)
(fun y hy => by cases y <;> first | exact absurd rfl hy | rfl)
(fun x hx => by cases x <;> first | exact absurd rfl hx | rfl)
/-- Agda: `minus-Monoˡ` — a postulate there, a theorem here. -/
theorem minus_mono_left (s₂ : ConstLattice) : Monotone (minus · s₂) := minus_mono₂.1 s₂
/-- Agda: `minus-Monoʳ` — a postulate there, a theorem here. -/
theorem minus_mono_right (s₁ : ConstLattice) : Monotone (minus s₁) := minus_mono₂.2 s₁
/-- Agda: `⟦_⟧ᶜ`. -/
def interpConst : ConstLattice Value Prop
| .bot, _ => False
| .top, _ => True
| .mk z, v => v = .int z
/-- Agda: `s₁≢s₂⇒¬s₁∧s₂`. -/
theorem interpConst_mk_disjoint {z₁ z₂ : } (hne : z₁ z₂) {v : Value} :
¬(interpConst (.mk z₁) v interpConst (.mk z₂) v) := by
rintro h₁, h₂
rw [h₁] at h₂
injection h₂ with hz
exact hne hz
/-- Agda: `⟦⟧ᶜ-⊔ᶜ-` (via the factored flat-lattice lemma). -/
theorem interpConst_sup {s₁ s₂ : ConstLattice} (v : Value)
(h : interpConst s₁ v interpConst s₂ v) : interpConst (s₁ s₂) v :=
AboveBelow.interp_sup_of (fun _ h => h) (fun _ => trivial) v h
/-- Agda: `⟦⟧ᶜ-⊓ᶜ-∧` (via the factored flat-lattice lemma). -/
theorem interpConst_inf {s₁ s₂ : ConstLattice} (v : Value)
(h : interpConst s₁ v interpConst s₂ v) : interpConst (s₁ s₂) v :=
AboveBelow.interp_inf_of (fun hne _ => interpConst_mk_disjoint hne) v h
/-- Agda: `latticeInterpretationᶜ` (an instance there too). -/
instance constInterpretation : LatticeInterpretation ConstLattice where
interp := interpConst
interp_sup := fun {l₁ l₂} v h => interpConst_sup (s₁ := l₁) (s₂ := l₂) v h
interp_inf := fun {l₁ l₂} v h => interpConst_inf (s₁ := l₁) (s₂ := l₂) v h
variable (prog : Program)
/-- Agda: `WithProg.eval`. -/
def eval : Expr VariableValues ConstLattice prog ConstLattice
| .add e₁ e₂, vs => plus (eval e₁ vs) (eval e₂ vs)
| .sub e₁ e₂, vs => minus (eval e₁ vs) (eval e₂ vs)
| .var k, vs =>
if h : FiniteMap.MemKey k vs then (FiniteMap.locate h).1 else .top
| .num n, _ => .mk n
/-- Agda: `WithProg.eval-Monoʳ`. -/
theorem eval_mono (e : Expr) : Monotone (eval prog e) := by
induction e with
| add e₁ e₂ ih₁ ih₂ =>
intro vs₁ vs₂ h
exact eval_combine₂ plus_mono₂ (ih₁ h) (ih₂ h)
| sub e₁ e₂ ih₁ ih₂ =>
intro vs₁ vs₂ h
exact eval_combine₂ minus_mono₂ (ih₁ h) (ih₂ h)
| var k =>
intro vs₁ vs₂ h
simp only [eval]
by_cases hk : k prog.vars
· rw [dif_pos (FiniteMap.memKey_iff.mpr hk),
dif_pos (FiniteMap.memKey_iff.mpr hk)]
exact FiniteMap.le_of_mem_mem prog.vars_nodup h
(FiniteMap.locate _).2 (FiniteMap.locate _).2
· rw [dif_neg (fun hm => hk (FiniteMap.memKey_iff.mp hm)),
dif_neg (fun hm => hk (FiniteMap.memKey_iff.mp hm))]
| num n =>
intro vs₁ vs₂ _
exact le_refl _
/-- Agda: the `ConstEval` instance. -/
instance exprEvaluator : ExprEvaluator ConstLattice prog :=
eval prog, eval_mono prog
/-- Agda: `WithProg.result`/`output`. -/
def output : String :=
show' (result ConstLattice prog)
/-- Agda: `plus-valid`. -/
theorem plus_valid {g₁ g₂ : ConstLattice} {z₁ z₂ : }
(h₁ : interpConst g₁ (.int z₁)) (h₂ : interpConst g₂ (.int z₂)) :
interpConst (plus g₁ g₂) (.int (z₁ + z₂)) := by
rcases g₁ with _ | _ | c₁
· exact h₁.elim
· rcases g₂ with _ | _ | c₂
· exact h₂.elim
· exact trivial
· exact trivial
· rcases g₂ with _ | _ | c₂
· exact h₂.elim
· exact trivial
· injection h₁ with hz₁
injection h₂ with hz₂
show Value.int (z₁ + z₂) = Value.int (c₁ + c₂)
rw [hz₁, hz₂]
/-- Agda: `minus-valid`. -/
theorem minus_valid {g₁ g₂ : ConstLattice} {z₁ z₂ : }
(h₁ : interpConst g₁ (.int z₁)) (h₂ : interpConst g₂ (.int z₂)) :
interpConst (minus g₁ g₂) (.int (z₁ - z₂)) := by
rcases g₁ with _ | _ | c₁
· exact h₁.elim
· rcases g₂ with _ | _ | c₂
· exact h₂.elim
· exact trivial
· exact trivial
· rcases g₂ with _ | _ | c₂
· exact h₂.elim
· exact trivial
· injection h₁ with hz₁
injection h₂ with hz₂
show Value.int (z₁ - z₂) = Value.int (c₁ - c₂)
rw [hz₁, hz₂]
/-- Agda: `eval-valid` / the `ConstEvalValid` instance. -/
instance eval_valid : ValidExprEvaluator ConstLattice prog := by
constructor
intro vs ρ e v hev
induction hev with
| num n =>
intro _
show interpConst (eval prog (.num n) vs) (.int n)
rfl
| var x v hxv =>
intro hvs
show interpConst (eval prog (.var x) vs) v
simp only [eval]
by_cases hk : FiniteMap.MemKey x vs
· rw [dif_pos hk]
exact hvs _ _ (FiniteMap.locate hk).2 _ hxv
· rw [dif_neg hk]
exact trivial
| add e₁ e₂ z₁ z₂ _ _ ih₁ ih₂ =>
intro hvs
have h₁ : interpConst (eval prog e₁ vs) (.int z₁) := ih₁ hvs
have h₂ : interpConst (eval prog e₂ vs) (.int z₂) := ih₂ hvs
show interpConst (eval prog (.add e₁ e₂) vs) (.int (z₁ + z₂))
exact plus_valid h₁ h₂
| sub e₁ e₂ z₁ z₂ _ _ ih₁ ih₂ =>
intro hvs
have h₁ : interpConst (eval prog e₁ vs) (.int z₁) := ih₁ hvs
have h₂ : interpConst (eval prog e₂ vs) (.int z₂) := ih₂ hvs
show interpConst (eval prog (.sub e₁ e₂) vs) (.int (z₁ - z₂))
exact minus_valid h₁ h₂
/-- Agda: `WithProg.analyze-correct`. -/
theorem analyze_correct {ρ : Env} (hrun : EvalStmt [] prog.rootStmt ρ) :
interpV (variablesAt prog.finalState (result ConstLattice prog)) ρ :=
Spa.analyze_correct ConstLattice prog hrun
end ConstAnalysis
end Spa