345 lines
12 KiB
Text
345 lines
12 KiB
Text
/-
|
||
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Dany Fabian
|
||
-/
|
||
|
||
module
|
||
|
||
prelude
|
||
public import Init.ByCases
|
||
|
||
@[expose] public section
|
||
|
||
namespace Lean.Data.AC
|
||
inductive Expr
|
||
| var (x : Nat)
|
||
| op (lhs rhs : Expr)
|
||
deriving Inhabited, Repr, BEq
|
||
|
||
open Std
|
||
|
||
structure Variable {α : Sort u} (op : α → α → α) : Type u where
|
||
value : α
|
||
neutral : Option $ PLift (LawfulIdentity op value)
|
||
|
||
structure Context (α : Sort u) where
|
||
op : α → α → α
|
||
assoc : Associative op
|
||
comm : Option $ PLift $ Commutative op
|
||
idem : Option $ PLift $ IdempotentOp op
|
||
vars : List (Variable op)
|
||
arbitrary : α
|
||
|
||
class ContextInformation (α : Sort u) where
|
||
isNeutral : α → Nat → Bool
|
||
isComm : α → Bool
|
||
isIdem : α → Bool
|
||
|
||
class EvalInformation (α : Sort u) (β : Sort v) where
|
||
arbitrary : α → β
|
||
evalOp : α → β → β → β
|
||
evalVar : α → Nat → β
|
||
|
||
def Context.var (ctx : Context α) (idx : Nat) : Variable ctx.op :=
|
||
ctx.vars[idx]?.getD ⟨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 (β : Sort 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 (β : Sort 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 :: xs =>
|
||
match loop (x :: xs) with
|
||
| [] => [x]
|
||
| ys => ys
|
||
| [] => []
|
||
where loop : List Nat → List Nat
|
||
| x :: xs =>
|
||
match info.isNeutral ctx x with
|
||
| true => loop xs
|
||
| false => x :: loop 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
|
||
|
||
noncomputable def 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 : IdempotentOp ctx.op := by
|
||
simp [ContextInformation.isIdem, Option.isSome] at h;
|
||
match h₂ : ctx.idem with
|
||
| none =>
|
||
simp [h₂] at h
|
||
| some val =>
|
||
simp [h₂] at h
|
||
exact val.down
|
||
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
|
||
next h₂ => simp [evalList, h₂, h.1, EvalInformation.evalOp]
|
||
next => rfl
|
||
| cons z zs =>
|
||
by_cases h₂ : x = y
|
||
case pos =>
|
||
rw [h₂, mergeIdem_head, ih]
|
||
simp [evalList, ←ctx.assoc.1, h.1, EvalInformation.evalOp]
|
||
case neg =>
|
||
rw [mergeIdem_head2]
|
||
by_cases h₃ : y = z
|
||
case pos =>
|
||
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 neg =>
|
||
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 _ => 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 _ ih => simp [sort.loop]; apply ih; apply insert_nonEmpty
|
||
|
||
theorem Context.evalList_insert
|
||
(ctx : Context α)
|
||
(h : Commutative 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
|
||
next => rfl
|
||
next =>
|
||
split
|
||
next => simp [evalList, EvalInformation.evalOp]; rw [h.1, ctx.assoc.1, h.1 (evalList _ _ _)]
|
||
next => simp_all [evalList, EvalInformation.evalOp]; rw [h.1, ctx.assoc.1, h.1 (evalList _ _ _)]
|
||
|
||
theorem Context.evalList_sort_congr
|
||
(ctx : Context α)
|
||
(h : Commutative 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 _ ih =>
|
||
simp [sort.loop]; apply ih; simp [evalList_insert ctx h]
|
||
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 : Commutative 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 _ =>
|
||
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 : Commutative 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 : Commutative ctx.op := by
|
||
simp [ContextInformation.isComm, Option.isSome] at h
|
||
match h₂ : ctx.comm with
|
||
| none =>
|
||
simp [h₂] at h
|
||
| some val =>
|
||
simp [h₂] at h
|
||
exact val.down
|
||
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₁ _ =>
|
||
simp [Expr.toList]
|
||
cases h : l.toList with
|
||
| nil => contradiction
|
||
| cons => simp
|
||
|
||
theorem Context.unwrap_isNeutral
|
||
{ctx : Context α}
|
||
{x : Nat}
|
||
: ContextInformation.isNeutral ctx x = true → LawfulIdentity (EvalInformation.evalOp ctx) (EvalInformation.evalVar (β := α) ctx x) := by
|
||
simp [ContextInformation.isNeutral, Option.isSome, EvalInformation.evalOp, EvalInformation.evalVar]
|
||
match (var ctx x).neutral with
|
||
| some hn =>
|
||
intro
|
||
exact hn.down
|
||
| none => intro; contradiction
|
||
|
||
theorem Context.evalList_removeNeutrals (ctx : Context α) (e : List Nat) : evalList α ctx (removeNeutrals ctx e) = evalList α ctx e := by
|
||
induction e using List.two_step_induction with
|
||
| empty => rfl
|
||
| single =>
|
||
simp [removeNeutrals, removeNeutrals.loop]; split
|
||
case h_1 => rfl
|
||
case h_2 h => split at h <;> simp_all
|
||
| step x y ys ih =>
|
||
cases h₁ : ContextInformation.isNeutral ctx x <;>
|
||
cases h₂ : ContextInformation.isNeutral ctx y <;>
|
||
cases h₃ : removeNeutrals.loop ctx ys
|
||
<;> simp [removeNeutrals, removeNeutrals.loop, h₁, h₂, h₃, evalList, ←ih]
|
||
<;> (try simp [unwrap_isNeutral h₂ |>.right_id])
|
||
<;> (try simp [unwrap_isNeutral h₁ |>.left_id])
|
||
|
||
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 =>
|
||
cases r
|
||
. simp at h₂
|
||
. 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 [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, 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) (eq_of_beq h)
|
||
rw [eval_norm, eval_norm] at h
|
||
assumption
|
||
|
||
end Lean.Data.AC
|