chore: cleanup of grind if-then-else example (#8326)

This commit is contained in:
Kim Morrison 2025-05-14 07:01:34 +08:00 committed by GitHub
parent ff85acedb9
commit b5bf0b1d05
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,4 +1,5 @@
import Std.Data.HashMap.Lemmas
import Std.Data.TreeMap.Lemmas
/-!
# If normalization
@ -115,11 +116,8 @@ set_option grind.warning false
namespace IfExpr
-- We tell `grind` it can unfold our definitions above.
attribute [grind] normalized hasNestedIf hasConstantIf hasRedundantIf disjoint vars eval
-- I'd prefer to use `TreeMap` here, but its `getElem?_insert` lemma is not useful.
attribute [grind] Std.HashMap.getElem?_insert
-- We tell `grind` to unfold our definitions above.
attribute [local grind] normalized hasNestedIf hasConstantIf hasRedundantIf disjoint vars eval
/--
Custom size function for if-expressions, used for proving termination.
@ -132,6 +130,10 @@ we are allowed to increase the size of the branches by one, and still be smaller
| var _ => 1
| .ite i t e => 2 * normSize i + max (normSize t) (normSize e) + 1
-- TODO: ensure that we can use `TreeMap` here instead.
-- Currently we rely on `HashMap.getElem?_insert`, but the corresponding `TreeMap` lemma is harder to use
-- and will require some amount of `LawfulOrd` machinery.
def normalize (assign : Std.HashMap Nat Bool) : IfExpr → IfExpr
| lit b => lit b
| var v =>
@ -150,13 +152,37 @@ def normalize (assign : Std.HashMap Nat Bool) : IfExpr → IfExpr
| some b => normalize assign (ite (lit b) t e)
termination_by e => e.normSize
-- TODO: add these to the library.
attribute [grind] getElem?_eq_none_iff
attribute [grind] Std.HashMap.contains_iff_mem
example (m : Std.HashMap Nat Bool) (v w : Nat) (h : m.contains v) (_ : m[w]? = none) (_ : v = w) : False := by
grind
-- TODO: ungrind `List.mem_append_left`/`right`?
theorem normalize_spec (assign : Std.HashMap Nat Bool) (e : IfExpr) :
(normalize assign e).normalized
∧ (∀ f, (normalize assign e).eval f = e.eval fun w => assign[w]?.getD (f w))
∧ ∀ (v : Nat), v ∈ vars (normalize assign e) → assign[v]? = none := by
fun_induction normalize <;> grind (gen := 8) (splits := 8)
∧ ∀ (v : Nat), v ∈ vars (normalize assign e) → ¬ v ∈ assign := by
fun_induction normalize <;> grind (gen := 7)
/-
-- We can also prove other variations, where we spell "`v` is not in `assign`"
-- different ways, and `grind` doesn't mind.
example (assign : Std.HashMap Nat Bool) (e : IfExpr) :
(normalize assign e).normalized
∧ (∀ f, (normalize assign e).eval f = e.eval fun w => assign[w]?.getD (f w))
∧ ∀ (v : Nat), v ∈ vars (normalize assign e) → assign.contains v = false := by
fun_induction normalize <;> grind (gen := 7)
example (assign : Std.HashMap Nat Bool) (e : IfExpr) :
(normalize assign e).normalized
∧ (∀ f, (normalize assign e).eval f = e.eval fun w => assign[w]?.getD (f w))
∧ ∀ (v : Nat), v ∈ vars (normalize assign e) → assign[v]? = none := by
fun_induction normalize <;> grind (gen := 8)
/--
We recall the statement of the if-normalization problem.
We want a function from if-expressions to if-expressions,