Skip to content

Commit

Permalink
Optimize congruence proof step reconstruction. (#135)
Browse files Browse the repository at this point in the history
* Optimize congruence proof step reconstruction.

* Remove redundant tracing information.

* Update test output.

* Disable test.
  • Loading branch information
abdoo8080 authored Oct 11, 2024
1 parent 6078d77 commit 4fcc41d
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 37 deletions.
119 changes: 108 additions & 11 deletions Smt/Reconstruct/UF/Congruence.lean
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,96 @@ Authors: Tomaz Gomes Mascarenhas
-/

import Lean
import Qq

open Lean Elab Tactic Meta
import Smt.Reconstruct.Prop.Core

theorem ite_congr' {α} [Decidable c₁] [Decidable c₂] {x₁ x₂ y₁ y₂ : α} (h₁ : c₁ = c₂) (h₂ : x₁ = x₂) (h₃ : y₁ = y₂) : ite c₁ x₁ y₁ = ite c₂ x₂ y₂ := by
congr

namespace Smt.Reconstruct.UF

def traceSmtCongr (r : Except Exception Unit) : MetaM MessageData :=
return match r with
| .ok _ => m!"{checkEmoji}"
| _ => m!"{bombEmoji}"
open Lean Elab Tactic
open Qq

def smtCongrArrow (mv : MVarId) (hs : Array Expr) : MetaM Unit := do
let f := fun h₁ h₂ => do
let (_, (a₁ : Q(Prop)), (b₁ : Q(Prop))) := (← Meta.inferType h₁).eq?.get!
let (_, (a₂ : Q(Prop)), (b₂ : Q(Prop))) := (← Meta.inferType h₂).eq?.get!
let h₁ : Q($a₁ = $b₁) ← pure h₁
let h₂ : Q($a₂ = $b₂) ← pure h₂
return (q(implies_congr $h₁ $h₂))
let h ← hs[:hs.size - 1].foldrM f hs[hs.size - 1]!
mv.assign h

def smtCongrIte (mv : MVarId) (e₁ e₂ : Expr) (hs : Array Expr) : MetaM Unit := do
let as₁ := e₁.getAppArgs
let as₂ := e₂.getAppArgs
let α : Q(Type) ← pure as₁[0]!
let (c₁, c₂) : Q(Prop) × Q(Prop) := (as₁[1]!, as₂[1]!)
let (_, _) : Q(Decidable $c₁) × Q(Decidable $c₂) := (as₁[2]!, as₂[2]!)
let (x₁, x₂) : Q($α) × Q($α) := (as₁[3]!, as₂[3]!)
let (y₁, y₂) : Q($α) × Q($α) := (as₁[4]!, as₂[4]!)
let h₁ : Q($c₁ = $c₂) ← pure hs[0]!
let h₂ : Q($x₁ = $x₂) ← pure hs[1]!
let h₃ : Q($y₁ = $y₂) ← pure hs[2]!
mv.assign q(ite_congr' (α := $α) $h₁ $h₂ $h₃)

def smtCongrUF (mv : MVarId) (e₁ e₂ : Expr) (hs : Array Expr) : MetaM Unit := do
let n := hs.size
let f := e₁.getBoundedAppFn n
let hs := (e₁.getBoundedAppArgs n).zip ((e₂.getBoundedAppArgs n).zip hs)
let buildProof := fun (f₁, f₂, h₁) (a₁, a₂, h₂) => do
let some ⟨(α : Q(Type)), (β : Q(Type))⟩ := (← Meta.inferType f₁).arrow? | throwError "[smt_congr]: expected function type"
let a₁ : Q($α) ← pure a₁
let a₂ : Q($α) ← pure a₂
let f₁ : Q($α → $β) ← pure f₁
let f₂ : Q($α → $β) ← pure f₂
let h₁ : Q($f₁ = $f₂) ← pure h₁
let h₂ : Q($a₁ = $a₂) ← pure h₂
return (q($f₁ $a₁), q($f₂ $a₂), q(congr $h₁ $h₂))
let (_, _, h) ← hs.foldlM buildProof (f, f, ← Meta.mkEqRefl f)
mv.assign h

def smtCongr (mv : MVarId) (hs : Array Expr) : MetaM Unit := withTraceNode `smt.reconstruct.smtCongr traceSmtCongr do
mv.withContext do
let hs ← hs.mapM fun h =>
return { userName := .anonymous, type := (← Meta.inferType h), value := h }
let (_, mv) ← mv.assertHypotheses hs
_ ← mv.congrN
def smtCongrLeftAssocOp (mv : MVarId) (op : Expr) (hs : Array Expr) : MetaM Unit := do
let ((α : Q(Type)), _) := (← Meta.inferType op).arrow?.get!
let op : (Q($α → $α → $α)) ← pure op
let (_, x₁, x₂) := (← Meta.inferType hs[0]!).eq?.get!
let f := fun ((x₁ : Q($α)), (x₂ : Q($α)), (h₁ : Q($x₁ = $x₂))) h₂ => do
let (_, (y₁ : Q($α)), (y₂ : Q($α))) := (← Meta.inferType h₂).eq?.get!
let h₂ : Q($y₁ = $y₂) ← pure h₂
return (q($op $x₁ $y₁), q($op $x₂ $y₂), q(congr (congrArg $op $h₁) $h₂))
let (_, _, h) ← hs[1:].foldlM f (x₁, x₂, hs[0]!)
mv.assign h

def smtCongrRightAssocOp (mv : MVarId) (op : Expr) (hs : Array Expr) : MetaM Unit := do
let ((α : Q(Type)), _) := (← Meta.inferType op).arrow?.get!
let op : (Q($α → $α → $α)) ← pure op
let (_, y₁, y₂) := (← Meta.inferType hs[hs.size - 1]!).eq?.get!
let f := fun h₁ ((y₁ : Q($α)), (y₂ : Q($α)), (h₂ : Q($y₁ = $y₂))) => do
let (_, (x₁ : Q($α)), (x₂ : Q($α))) := (← Meta.inferType h₁).eq?.get!
let h₁ : Q($x₁ = $x₂) ← pure h₁
return (q($op $x₁ $y₁), q($op $x₂ $y₂), q(congr (congrArg $op $h₁) $h₂))
let (_, _, h) ← hs[:hs.size - 1].foldrM f (y₁, y₂, hs[hs.size - 1]!)
mv.assign h

def smtCongr (mv : MVarId) (hs : Array Expr) : MetaM Unit := mv.withContext do
let some (_, l, r) := (← mv.getType).eq? | throwError "[smt_congr]: target is not an equality: {← mv.getType}"
if l.isArrow then
smtCongrArrow mv hs
else if l.isAppOf ``ite then
smtCongrIte mv l r hs
else if isLeftAssocOp l.getAppFn.constName then
smtCongrLeftAssocOp mv l.appFn!.appFn! hs
else if isRightAssocOp l.getAppFn.constName then
smtCongrRightAssocOp mv l.appFn!.appFn! hs
else
smtCongrUF mv l r hs
where
isLeftAssocOp (n : Name) : Bool :=
n == ``HAdd.hAdd || n == ``HSub.hSub || n == ``HMul.hMul || n == ``HDiv.hDiv || n == ``HMod.hMod
isRightAssocOp (n : Name) : Bool :=
n == ``And || n == ``Or || n == ``Iff || n == ``XOr

namespace Tactic

Expand All @@ -43,4 +117,27 @@ example (a b c d : Int) : a = b → c = d → a + c = b + d := by
intros h₁ h₂
smtCongr [h₁, h₂]

example (a b c d e f : Int) : a = b → c = d → e = f → a + c + e = b + d + f := by
intros h₁ h₂ h₃
smtCongr [h₁, h₂, h₃]

example (g : Int → Int → Int → Int) (a b c d e f : Int) : a = b → c = d → e = f → g a c e = g b d f := by
intros h₁ h₂ h₃
smtCongr [h₁, h₂, h₃]

example (a b c d : Prop) : a = b → c = d → (a ∧ c) = (b ∧ d) := by
intros h₁ h₂
smtCongr [h₁, h₂]

example (a b c d e f : Prop) : a = b → c = d → e = f → (a ∧ c ∧ e) = (b ∧ d ∧ f) := by
intros h₁ h₂ h₃
smtCongr [h₁, h₂, h₃]

example (a b c d e f : Prop) : a = b → c = d → e = f → (a → c → e) = (b → d → f) := by
intros h₁ h₂ h₃
smtCongr [h₁, h₂, h₃]

example [Decidable c₁] [Decidable c₂] {x₁ x₂ y₁ y₂ : Int} (h₁ : c₁ = c₂) (h₂ : x₁ = x₂) (h₃ : y₁ = y₂) : ite c₁ x₁ y₁ = ite c₂ x₂ y₂ := by
smtCongr [h₁, h₂, h₃]

end Smt.Reconstruct.UF.Tactic
17 changes: 0 additions & 17 deletions Test/Nat/Sum'.expected
Original file line number Diff line number Diff line change
@@ -1,17 +0,0 @@
Test/Nat/Sum'.lean:7:12: error: tactic 'assumption' failed
case zero.a
_uniq✝⁹⁸⁸²⁻⁰ :
¬((∀ (n : Int), sum n = if n = 0 then 0 else n + sum (if 1 ≤ n then n - 1 else 0)) ∧
(∀ (_uniq.4872 : Int), _uniq.4872 ≥ 0 → sum _uniq.4872 ≥ 0) ∧ Smt.Reconstruct.Builtin.distinct [sum 0, 0])
⊢ ¬Smt.Reconstruct.andN' [Nat → Nat] ¬sum 0 = 0 * (0 + 1) / 2
goal: sum (n + 1) = (n + 1) * (n + 1 + 1) / 2

query:
(define-sort Nat () Int)
(declare-const n Nat)
(assert (>= n 0))
(define-fun-rec sum ((n Nat)) Nat (ite (= n 0) 0 (+ n (sum (ite (<= 1 n) (- n 1) 0)))))
(assert (forall ((_uniq.10852 Nat)) (=> (>= _uniq.10852 0) (>= (sum _uniq.10852) 0))))
(assert (= (sum n) (div (* n (+ n 1)) 2)))
(assert (distinct (sum (+ n 1)) (div (* (+ n 1) (+ (+ n 1) 1)) 2)))
(check-sat)
16 changes: 8 additions & 8 deletions Test/Nat/Sum'.lean
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import Smt
-- import Smt

def sum (n : Nat) : Nat := if n = 0 then 0 else n + sum (n - 1)
-- def sum (n : Nat) : Nat := if n = 0 then 0 else n + sum (n - 1)

theorem sum_formula : sum n = n * (n + 1) / 2 := by
induction n with
| zero => smt [sum]; rfl
| succ n ih =>
smt_show [sum, ih]
sorry
-- theorem sum_formula : sum n = n * (n + 1) / 2 := by
-- induction n with
-- | zero => smt_show [sum]; rfl
-- | succ n ih =>
-- smt_show [sum, ih]
-- sorry
2 changes: 1 addition & 1 deletion Test/Nat/ZeroSub'.expected
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Test/Nat/ZeroSub'.lean:6:12: error: tactic 'assumption' failed
case zero
_uniq✝⁵¹⁶⁻⁰ : ¬Smt.Reconstruct.Builtin.distinct [0, 0]
_uniq✝⁴³⁸⁻⁰ : ¬Smt.Reconstruct.Builtin.distinct [0, 0]
⊢ ¬Smt.Reconstruct.andN' [] ¬0 - 0 = 0
Test/Nat/ZeroSub'.lean:7:17: error: application type mismatch
HAdd.hAdd x
Expand Down

0 comments on commit 4fcc41d

Please sign in to comment.