feat: add theory for ac normalization.
This lets us implement an AC reflexivity tactic.
This commit is contained in:
parent
4320a61f4d
commit
1114dfac6c
6 changed files with 355 additions and 0 deletions
|
|
@ -1112,4 +1112,17 @@ constant reduceNat (n : Nat) : Nat := n
|
|||
axiom ofReduceBool (a b : Bool) (h : reduceBool a = b) : a = b
|
||||
axiom ofReduceNat (a b : Nat) (h : reduceNat a = b) : a = b
|
||||
|
||||
class IsAssociative (op : α → α → α) where
|
||||
assoc : (a b c : α) → op (op a b) c = op a (op b c)
|
||||
|
||||
class IsCommutative (op : α → α → α) where
|
||||
comm : (a b : α) → op a b = op b a
|
||||
|
||||
class IsIdempotent (op : α → α → α) where
|
||||
idempotent : (x : α) → op x x = x
|
||||
|
||||
class IsNeutral (op : α → α → α) (neutral : α) where
|
||||
left_neutral : (a : α) → op neutral a = a
|
||||
right_neutral : (a : α) → op a neutral = a
|
||||
|
||||
end Lean
|
||||
|
|
|
|||
|
|
@ -26,3 +26,4 @@ import Lean.Meta.Tactic.Cleanup
|
|||
import Lean.Meta.Tactic.Unfold
|
||||
import Lean.Meta.Tactic.Rename
|
||||
import Lean.Meta.Tactic.LinearArith
|
||||
import Lean.Meta.Tactic.AC
|
||||
|
|
|
|||
8
src/Lean/Meta/Tactic/AC.lean
Normal file
8
src/Lean/Meta/Tactic/AC.lean
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/-
|
||||
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Dany Fabian
|
||||
-/
|
||||
import Lean.Meta.Tactic.AC.Main
|
||||
import Lean.Meta.Tactic.AC.Basic
|
||||
import Lean.Meta.Tactic.AC.Theory
|
||||
99
src/Lean/Meta/Tactic/AC/Basic.lean
Normal file
99
src/Lean/Meta/Tactic/AC/Basic.lean
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/-
|
||||
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Dany Fabian
|
||||
-/
|
||||
import Init.Core
|
||||
|
||||
namespace Lean.Meta.AC
|
||||
inductive Expr
|
||||
| var (x : Nat)
|
||||
| op (lhs rhs : Expr)
|
||||
deriving Inhabited, Repr, BEq
|
||||
|
||||
structure Variable (op : α → α → α) where
|
||||
value : α
|
||||
neutral : Option $ IsNeutral op value
|
||||
|
||||
structure Context (α : Type u) where
|
||||
op : α → α → α
|
||||
assoc : IsAssociative op
|
||||
comm : Option $ IsCommutative op
|
||||
idem : Option $ IsIdempotent op
|
||||
vars : List (Variable op)
|
||||
arbitrary : α
|
||||
|
||||
class ContextInformation (α : Type u) where
|
||||
isNeutral : α → Nat → Bool
|
||||
isComm : α → Bool
|
||||
isIdem : α → Bool
|
||||
|
||||
class EvalInformation (α : Type u) (β : Type v) where
|
||||
arbitrary : α → β
|
||||
evalOp : α → β → β → β
|
||||
evalVar : α → Nat → β
|
||||
|
||||
def Context.var (ctx : Context α) (idx : Nat) : Variable ctx.op :=
|
||||
ctx.vars.getD idx ⟨ctx.arbitrary, none⟩
|
||||
|
||||
instance : ContextInformation (Context α) where
|
||||
isNeutral ctx x := ctx.var x |>.neutral.isSome
|
||||
isComm ctx := ctx.comm.isSome
|
||||
isIdem ctx := ctx.idem.isSome
|
||||
|
||||
instance : EvalInformation (Context α) α where
|
||||
arbitrary ctx := ctx.arbitrary
|
||||
evalOp ctx := ctx.op
|
||||
evalVar ctx idx := ctx.var idx |>.value
|
||||
|
||||
def eval (β : Type u) [EvalInformation α β] (ctx : α) : (ex : Expr) → β
|
||||
| Expr.var idx => EvalInformation.evalVar ctx idx
|
||||
| Expr.op l r => EvalInformation.evalOp ctx (eval β ctx l) (eval β ctx r)
|
||||
|
||||
def Expr.toList : Expr → List Nat
|
||||
| Expr.var idx => [idx]
|
||||
| Expr.op l r => l.toList.append r.toList
|
||||
|
||||
def evalList (β : Type u) [EvalInformation α β] (ctx : α) : List Nat → β
|
||||
| [] => EvalInformation.arbitrary ctx
|
||||
| [x] => EvalInformation.evalVar ctx x
|
||||
| x :: xs => EvalInformation.evalOp ctx (EvalInformation.evalVar ctx x) (evalList β ctx xs)
|
||||
|
||||
def insert (x : Nat) : List Nat → List Nat
|
||||
| [] => [x]
|
||||
| a :: as => if x < a then x :: a :: as else a :: insert x as
|
||||
|
||||
def sort (xs : List Nat) : List Nat :=
|
||||
let rec loop : List Nat → List Nat → List Nat
|
||||
| acc, [] => acc
|
||||
| acc, x :: xs => loop (insert x acc) xs
|
||||
loop [] xs
|
||||
|
||||
def mergeIdem (xs : List Nat) : List Nat :=
|
||||
let rec loop : Nat → List Nat → List Nat
|
||||
| curr, next :: rest =>
|
||||
if curr = next then
|
||||
loop curr rest
|
||||
else
|
||||
curr :: loop next rest
|
||||
| curr, [] => [curr]
|
||||
|
||||
match xs with
|
||||
| [] => []
|
||||
| x :: xs => loop x xs
|
||||
|
||||
def removeNeutrals [info : ContextInformation α] (ctx : α) : List Nat → List Nat
|
||||
| [] => []
|
||||
| [x] => [x]
|
||||
| x :: xs =>
|
||||
match info.isNeutral ctx x with
|
||||
| true => removeNeutrals ctx xs
|
||||
| false => x :: removeNeutrals ctx xs
|
||||
|
||||
def norm [info : ContextInformation α] (ctx : α) (e : Expr) : List Nat :=
|
||||
let xs := e.toList
|
||||
let xs := removeNeutrals ctx xs
|
||||
let xs := if info.isComm ctx then sort xs else xs
|
||||
if info.isIdem ctx then mergeIdem xs else xs
|
||||
|
||||
end Lean.Meta.AC
|
||||
10
src/Lean/Meta/Tactic/AC/Main.lean
Normal file
10
src/Lean/Meta/Tactic/AC/Main.lean
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/-
|
||||
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Dany Fabian
|
||||
-/
|
||||
import Lean.Meta.Tactic.AC.Basic
|
||||
|
||||
namespace Lean.Meta.AC
|
||||
|
||||
end Lean.Meta.AC
|
||||
224
src/Lean/Meta/Tactic/AC/Theory.lean
Normal file
224
src/Lean/Meta/Tactic/AC/Theory.lean
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
/-
|
||||
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Dany Fabian
|
||||
-/
|
||||
import Lean.Meta.Tactic.AC.Basic
|
||||
|
||||
namespace Lean.Meta.AC.Theory
|
||||
open Lean.Meta.AC
|
||||
|
||||
theorem List.two_step_induction
|
||||
{motive : List Nat → Sort u}
|
||||
(l : List Nat)
|
||||
(empty : motive [])
|
||||
(single : ∀ a, motive [a])
|
||||
(step : ∀ a b l, motive (b :: l) → motive (a :: b :: l))
|
||||
: motive l := by
|
||||
induction l with
|
||||
| nil => assumption
|
||||
| cons a l => cases l; apply single; apply step; assumption
|
||||
|
||||
theorem Context.mergeIdem_nonEmpty (e : List Nat) (h : e ≠ []) : mergeIdem e ≠ [] := by
|
||||
induction e using List.two_step_induction with
|
||||
| empty => simp_all
|
||||
| single => simp [mergeIdem, mergeIdem.loop]
|
||||
| step => simp [mergeIdem, mergeIdem.loop] at *; split <;> simp_all
|
||||
|
||||
theorem Context.mergeIdem_head : mergeIdem (x :: x :: xs) = mergeIdem (x :: xs) := by
|
||||
simp [mergeIdem, mergeIdem.loop]
|
||||
|
||||
theorem Context.mergeIdem_head2 (h : x ≠ y) : mergeIdem (x :: y :: ys) = x :: mergeIdem (y :: ys) := by
|
||||
simp [mergeIdem, mergeIdem.loop, h]
|
||||
|
||||
theorem Context.evalList_mergeIdem (ctx : Context α) (h : ContextInformation.isIdem ctx) (e : List Nat) : evalList α ctx (mergeIdem e) = evalList α ctx e := by
|
||||
have h : IsIdempotent ctx.op := by simp [ContextInformation.isIdem, Option.isSome] at h; cases h₂ : ctx.idem <;> simp [h₂] at h; assumption
|
||||
induction e using List.two_step_induction with
|
||||
| empty => rfl
|
||||
| single => rfl
|
||||
| step x y ys ih =>
|
||||
cases ys with
|
||||
| nil =>
|
||||
simp [mergeIdem, mergeIdem.loop]
|
||||
split
|
||||
case inl h₂ => simp [evalList, h₂, h.1, EvalInformation.evalOp]
|
||||
rfl
|
||||
| cons z zs =>
|
||||
by_cases h₂ : x = y
|
||||
case inl =>
|
||||
rw [h₂, mergeIdem_head, ih]
|
||||
simp [evalList, ←ctx.assoc.1, h.1, EvalInformation.evalOp]
|
||||
case inr =>
|
||||
rw [mergeIdem_head2]
|
||||
by_cases h₃ : y = z
|
||||
case inl =>
|
||||
simp [mergeIdem_head, h₃, evalList]
|
||||
cases h₄ : mergeIdem (z :: zs) with
|
||||
| nil => apply absurd h₄; apply mergeIdem_nonEmpty; simp
|
||||
| cons u us => simp_all [mergeIdem, mergeIdem.loop, evalList]
|
||||
case inr =>
|
||||
simp [mergeIdem_head2, h₃, evalList] at *
|
||||
rw [ih]
|
||||
assumption
|
||||
|
||||
theorem insert_nonEmpty : insert x xs ≠ [] := by
|
||||
induction xs with
|
||||
| nil => simp [insert]
|
||||
| cons x xs ih => simp [insert]; split <;> simp
|
||||
|
||||
theorem Context.sort_loop_nonEmpty (xs : List Nat) (h : xs ≠ []) : sort.loop xs ys ≠ [] := by
|
||||
induction ys generalizing xs with
|
||||
| nil => simp [sort.loop]; assumption
|
||||
| cons y ys ih => simp [sort.loop]; apply ih; apply insert_nonEmpty
|
||||
|
||||
theorem Context.evalList_insert
|
||||
(ctx : Context α)
|
||||
(h : IsCommutative ctx.op)
|
||||
(x : Nat)
|
||||
(xs : List Nat)
|
||||
: evalList α ctx (insert x xs) = evalList α ctx (x::xs) := by
|
||||
induction xs using List.two_step_induction with
|
||||
| empty => rfl
|
||||
| single =>
|
||||
simp [insert]
|
||||
split
|
||||
. rfl
|
||||
. simp [evalList, h.1, EvalInformation.evalOp]
|
||||
| step y z zs ih =>
|
||||
simp [insert] at *; split
|
||||
case inl => rfl
|
||||
case inr =>
|
||||
split
|
||||
case inl => simp [evalList, EvalInformation.evalOp]; rw [h.1, ctx.assoc.1, h.1 (evalList _ _ _)]
|
||||
case inr => simp_all [ih, evalList, EvalInformation.evalOp]; rw [h.1, ctx.assoc.1, h.1 (evalList _ _ _)]
|
||||
|
||||
theorem Context.evalList_sort_congr
|
||||
(ctx : Context α)
|
||||
(h : IsCommutative ctx.op)
|
||||
(h₂ : evalList α ctx a = evalList α ctx b)
|
||||
(h₃ : a ≠ [])
|
||||
(h₄ : b ≠ [])
|
||||
: evalList α ctx (sort.loop a c) = evalList α ctx (sort.loop b c) := by
|
||||
induction c generalizing a b with
|
||||
| nil => simp [sort.loop, h₂]
|
||||
| cons c cs ih =>
|
||||
simp [sort.loop]; apply ih; simp [evalList_insert ctx h, evalList]
|
||||
cases a with
|
||||
| nil => apply absurd h₃; simp
|
||||
| cons a as =>
|
||||
cases b with
|
||||
| nil => apply absurd h₄; simp
|
||||
| cons b bs => simp [evalList, h₂]
|
||||
all_goals apply insert_nonEmpty
|
||||
|
||||
theorem Context.evalList_sort_loop_swap
|
||||
(ctx : Context α)
|
||||
(h : IsCommutative ctx.op)
|
||||
(xs ys : List Nat)
|
||||
: evalList α ctx (sort.loop xs (y::ys)) = evalList α ctx (sort.loop (y::xs) ys) := by
|
||||
induction ys generalizing y xs with
|
||||
| nil => simp [sort.loop, evalList_insert ctx h]
|
||||
| cons z zs ih =>
|
||||
simp [sort.loop]; apply evalList_sort_congr ctx h
|
||||
simp [evalList_insert ctx h]
|
||||
cases h₂ : insert y xs
|
||||
. apply absurd h₂; simp [insert_nonEmpty]
|
||||
. simp [evalList, ←h₂, evalList_insert ctx h]
|
||||
all_goals simp [insert_nonEmpty]
|
||||
|
||||
theorem Context.evalList_sort_cons
|
||||
(ctx : Context α)
|
||||
(h : IsCommutative ctx.op)
|
||||
(x : Nat)
|
||||
(xs : List Nat)
|
||||
: evalList α ctx (sort (x :: xs)) = evalList α ctx (x :: sort xs) := by
|
||||
simp [sort, sort.loop]
|
||||
generalize [] = ys
|
||||
induction xs generalizing x ys with
|
||||
| nil => simp [sort.loop, evalList_insert ctx h]
|
||||
| cons z zs ih =>
|
||||
rw [evalList_sort_loop_swap ctx h]; simp [sort.loop, ←ih]; apply evalList_sort_congr ctx h; rw [evalList_insert ctx h]
|
||||
cases h₂ : insert x ys with
|
||||
| nil => apply absurd h₂; simp [insert_nonEmpty]
|
||||
| cons u us =>
|
||||
cases h₃ : insert z ys with
|
||||
| nil => apply absurd h₃; simp [insert_nonEmpty]
|
||||
| cons v vs =>
|
||||
simp [evalList, ←h₂, ←h₃, evalList_insert ctx h]
|
||||
cases ys
|
||||
. simp [evalList, h.1, EvalInformation.evalOp]
|
||||
. simp [evalList, EvalInformation.evalOp]; rw [h.1, ctx.assoc.1, h.1 (evalList _ _ _)]
|
||||
all_goals simp [insert_nonEmpty]
|
||||
|
||||
theorem Context.evalList_sort (ctx : Context α) (h : ContextInformation.isComm ctx) (e : List Nat) : evalList α ctx (sort e) = evalList α ctx e := by
|
||||
have h : IsCommutative ctx.op := by simp [ContextInformation.isComm, Option.isSome] at h; cases h₂ : ctx.comm <;> simp [h₂] at h; assumption
|
||||
induction e using List.two_step_induction with
|
||||
| empty => rfl
|
||||
| single => rfl
|
||||
| step x y ys ih =>
|
||||
simp [evalList_sort_cons ctx h]
|
||||
cases h₂ : sort (y :: ys) with
|
||||
| nil => simp [sort, sort.loop] at *; apply absurd h₂; apply sort_loop_nonEmpty; apply insert_nonEmpty
|
||||
| cons z zs => simp [evalList, ←h₂, ih]
|
||||
|
||||
theorem Context.toList_nonEmpty (e : Expr) : e.toList ≠ [] := by
|
||||
induction e with
|
||||
| var => simp [Expr.toList]
|
||||
| op l r ih₁ ih₂ =>
|
||||
simp [Expr.toList]
|
||||
cases h : l.toList with
|
||||
| nil => contradiction
|
||||
| cons => simp [List.append]
|
||||
|
||||
theorem Context.removeNeutrals_nonEmpty (ctx : Context α) (e : List Nat) (h : e ≠ []) : removeNeutrals ctx e ≠ [] := by
|
||||
induction e using List.two_step_induction with
|
||||
| empty => simp_all
|
||||
| single x => simp [removeNeutrals]
|
||||
| step x y ys => simp [removeNeutrals] split <;> simp_all
|
||||
|
||||
theorem Context.evalList_removeNeutrals (ctx : Context α) (e : List Nat) (h : e ≠ []) : evalList α ctx (removeNeutrals ctx e) = evalList α ctx e := by
|
||||
induction e using List.two_step_induction with
|
||||
| empty => simp_all
|
||||
| single => simp [removeNeutrals]
|
||||
| step x y ys ih =>
|
||||
simp [removeNeutrals, ContextInformation.isNeutral, Option.isSome] at *
|
||||
split
|
||||
case h_1 h h₂ =>
|
||||
split at h₂
|
||||
case h_1 hn _ => simp [evalList, ih, hn.1, EvalInformation.evalOp, EvalInformation.evalVar]
|
||||
case h_2 => cases h₂
|
||||
case h_2 =>
|
||||
cases h : removeNeutrals ctx (y :: ys) with
|
||||
| nil => apply absurd h; simp [removeNeutrals_nonEmpty]
|
||||
| cons z zs =>
|
||||
simp_all [evalList]
|
||||
|
||||
theorem Context.evalList_append
|
||||
(ctx : Context α)
|
||||
(l r : List Nat)
|
||||
(h₁ : l ≠ [])
|
||||
(h₂ : r ≠ [])
|
||||
: evalList α ctx (l.append r) = ctx.op (evalList α ctx l) (evalList α ctx r) := by
|
||||
induction l using List.two_step_induction with
|
||||
| empty => simp_all
|
||||
| single x => simp [List.append, evalList, EvalInformation.evalOp]
|
||||
| step x y ys ih => simp [List.append, evalList, EvalInformation.evalOp] at *; rw [ih]; simp [ctx.assoc.1]
|
||||
|
||||
theorem Context.eval_toList (ctx : Context α) (e : Expr) : evalList α ctx e.toList = eval α ctx e := by
|
||||
induction e with
|
||||
| var x => rfl
|
||||
| op l r ih₁ ih₂ =>
|
||||
simp [evalList, Expr.toList, eval, ←ih₁, ←ih₂]
|
||||
apply evalList_append <;> apply toList_nonEmpty
|
||||
|
||||
theorem Context.eval_norm (ctx : Context α) (e : Expr) : evalList α ctx (norm ctx e) = eval α ctx e := by
|
||||
simp [norm]
|
||||
cases h₁ : ContextInformation.isIdem ctx <;> cases h₂ : ContextInformation.isComm ctx <;>
|
||||
simp_all [evalList_removeNeutrals, eval_toList, toList_nonEmpty, evalList_mergeIdem, evalList_sort]
|
||||
|
||||
theorem Context.eq_of_norm (ctx : Context α) (a b : Expr) (h : norm ctx a = norm ctx b) : eval α ctx a = eval α ctx b := by
|
||||
have h := congrArg (evalList α ctx) h
|
||||
rw [eval_norm, eval_norm] at h
|
||||
assumption
|
||||
|
||||
end Lean.Meta.AC.Theory
|
||||
Loading…
Add table
Reference in a new issue