Zulip Chat Archive

Stream: new members

Topic: Correct use of lean-auto


Mike Whalen (Nov 21 2023 at 14:40):

Hello,

I'm a new user of lean and I am trying out lean-auto on a simple model:

import Auto
import Std.Data.BitVec
set_option auto.smt true
set_option auto.smt.trust true
set_option trace.auto.smt.printCommands true
set_option trace.auto.smt.result true

inductive Zone where
  | Z1 | Z2 | Z3 | Z4
  -- Ask Lean to automatically show that type is not empty, has a representation function, and
  -- equality is decidable
  deriving Inhabited, Repr, DecidableEq

abbrev Area : Type := Int

def Zone.MinArea1 : Zone  Area
  | .Z1    => 10000
  | .Z2    => 5000
  | .Z3     => 3500
  | .Z4     => 2500

def Zone.MinArea2 : Zone  Area
  | .Z1    => 12000
  | .Z2    => 7000
  | .Z3     => 4000
  | .Z4     => 3000

example (x : Zone) : x.MinArea1 <= x.MinArea2 := by
  cases x <;> simp

example (x : Zone) : x.MinArea1 <= x.MinArea2 := by
  cases x <;> auto

Simp succeeds but auto fails here. Looking at the generated SMT it looks like MinArea1 and MinArea2 are treated as uninterpreted functions. How do I tell lean-auto to use these definitions?

Mario Carneiro (Nov 21 2023 at 16:57):

cc: @Yicheng Qian

Yicheng Qian (Nov 21 2023 at 17:14):

Currently lean-auto does not have good support for match expressions, but that might change in the near future.

Yicheng Qian (Nov 21 2023 at 17:15):

@Mario Carneiro Is there already code in Lean to generate/obtain equational theorems for aux constants like ?.match_1?

Mario Carneiro (Nov 21 2023 at 17:48):

I thought it did, but getEqnsFor? only seems to return some if the definition is recursive, and it returns it only for the main recursive definition, not the internal match splitters

Mario Carneiro (Nov 21 2023 at 17:56):

oh, there is a special function for matchers:

import Lean

def foo : Nat  Nat
  | 0 => 1
  | n+1 => 2

#eval Lean.Meta.Match.getEquationsFor ``foo.match_1

#print foo.match_1
#print foo.match_1.eq_1
#print foo.match_1.eq_2
#print foo.match_1.splitter
def foo.match_1.{u_1} : (motive : Nat  Sort u_1) 
  (x : Nat)  (Unit  motive 0)  ((n : Nat)  motive (Nat.succ n))  motive x :=
fun motive x h_1 h_2  Nat.casesOn x (h_1 ()) fun n  h_2 n

private theorem foo.match_1.eq_1.{u_1} :  (motive : Nat  Sort u_1) (h_1 : Unit  motive 0)
  (h_2 : (n : Nat)  motive (Nat.succ n)),
  (match 0 with
    | 0 => h_1 ()
    | Nat.succ n => h_2 n) =
    h_1 () :=
fun motive h_1 h_2  id (id (Eq.refl (h_1 ())))

private theorem foo.match_1.eq_2.{u_1} :  (motive : Nat  Sort u_1) (n : Nat) (h_1 : Unit  motive 0)
  (h_2 : (n : Nat)  motive (Nat.succ n)),
  (match Nat.succ n with
    | 0 => h_1 ()
    | Nat.succ n => h_2 n) =
    h_2 n :=
fun motive n h_1 h_2  id (id (Eq.refl (h_2 n)))

private def foo.match_1.splitter.{u_1} : (motive : Nat  Sort u_1) 
  (x : Nat)  motive 0  ((n : Nat)  motive (Nat.succ n))  motive x :=
fun motive x h_1 h_2  Nat.casesOn x h_1 fun n  h_2 n

Mike Whalen (Nov 21 2023 at 19:26):

(Hopefully I am replying correctly - still my first post) - I also tried using auto u[Zone.MinArea1, Zone.MinArea2] but got some errors, e.g. lamTerm2STerm :: Unexpected head term Auto.Embedding.Lam.LamTerm.lam (.atom 1) (.app (.base (.nat)) (.base (.icst (.iofNat))) (.base (.ncst (.natVal 10000))))

Yicheng Qian (Nov 22 2023 at 01:24):

Mario Carneiro said:

oh, there is a special function for matchers:

import Lean

def foo : Nat  Nat
  | 0 => 1
  | n+1 => 2

#eval Lean.Meta.Match.getEquationsFor ``foo.match_1

#print foo.match_1
#print foo.match_1.eq_1
#print foo.match_1.eq_2
#print foo.match_1.splitter

def foo.match_1.{u_1} : (motive : Nat  Sort u_1) 
  (x : Nat)  (Unit  motive 0)  ((n : Nat)  motive (Nat.succ n))  motive x :=
fun motive x h_1 h_2  Nat.casesOn x (h_1 ()) fun n  h_2 n

private theorem foo.match_1.eq_1.{u_1} :  (motive : Nat  Sort u_1) (h_1 : Unit  motive 0)
  (h_2 : (n : Nat)  motive (Nat.succ n)),
  (match 0 with
    | 0 => h_1 ()
    | Nat.succ n => h_2 n) =
    h_1 () :=
fun motive h_1 h_2  id (id (Eq.refl (h_1 ())))

private theorem foo.match_1.eq_2.{u_1} :  (motive : Nat  Sort u_1) (n : Nat) (h_1 : Unit  motive 0)
  (h_2 : (n : Nat)  motive (Nat.succ n)),
  (match Nat.succ n with
    | 0 => h_1 ()
    | Nat.succ n => h_2 n) =
    h_2 n :=
fun motive n h_1 h_2  id (id (Eq.refl (h_2 n)))

private def foo.match_1.splitter.{u_1} : (motive : Nat  Sort u_1) 
  (x : Nat)  motive 0  ((n : Nat)  motive (Nat.succ n))  motive x :=
fun motive x h_1 h_2  Nat.casesOn x h_1 fun n  h_2 n

Ah, this seems to be what I've been missing! I was only calling getEqnsFor? before.

Yicheng Qian (Nov 22 2023 at 01:27):

Mike Whalen said:

(Hopefully I am replying correctly - still my first post) - I also tried using auto u[Zone.MinArea1, Zone.MinArea2] but got some errors, e.g. lamTerm2STerm :: Unexpected head term Auto.Embedding.Lam.LamTerm.lam (.atom 1) (.app (.base (.nat)) (.base (.icst (.iofNat))) (.base (.ncst (.natVal 10000))))

Yes, that's another issue in auto. If we unfold these match-expressions, there will be dependently typed terms that auto can't handle. The expected usage is auto d[Zone.MinArea1, Zone.MinArea2], but that doesn't work right now. The first issue is recorded in TODO.md, and the second issue is recorded in Test/SmtTranslation/Inductive.lean (I'll add it to TODO.md).

Yicheng Qian (Nov 22 2023 at 01:32):

So that's it. Lean-auto is only 4 months old and I'm the only developer of it, so it's pretty rudimentary. It do work on some mathlib examples when used together with duper.

Mike Whalen (Nov 22 2023 at 03:54):

Sure, no problem. Thank you for your quick response!

Yicheng Qian (Nov 25 2023 at 01:48):

@Mike Whalen p.s: Recently I've been busy with other stuff and have no time for lean-auto, but it will get better after January 7th next year. Sorry about this.
Issues on github are still welcome.


Last updated: Dec 20 2023 at 11:08 UTC