Zulip Chat Archive

Stream: general

Topic: porting leaneuclid to use lean auto


Frederick Pu (Apr 30 2025 at 01:22):

I'm trying to port lean euclid to use lean-auto. Namely, I'm making a tactic eauto that wraps lean auto by passing all lemmas tagged with the specified attribute tag. For example eauto euclid [*] would call auto with all of the lemmas tagged with @[euclid].

import Lean
import Qq
import Auto

import SystemE.Tactic.Attr

open Lean Elab Tactic Meta Simp

namespace Auto

syntax (name := eauto) "eauto" ident autoinstr hints (uord)* : tactic

#check InputHints
def collectAllLemmas'
  (inputHints : InputHints) (unfoldInfos : Array Prep.ConstUnfoldInfo) (defeqNames : Array Name) (ngoalAndBinders : Array FVarId) :
  -- The first `Array Lemma` are `Prop` lemmas
  -- The second `Array Lemma` are Inhabitation facts
  TacticM (Array Lemma × Array Lemma) := do
  let unfoldInfos  Prep.topoSortUnfolds unfoldInfos
  let startTime  IO.monoMsNow
  let lctxLemmas  collectLctxLemmas inputHints.lctxhyps ngoalAndBinders
  let lctxLemmas  lctxLemmas.mapM (m:=MetaM) (unfoldConstAndPreprocessLemma unfoldInfos)
  traceLemmas `auto.printLemmas "Lemmas collected from local context:" lctxLemmas
  checkDuplicatedFact inputHints.terms
  checkDuplicatedLemmaDB inputHints.lemdbs
  let userLemmas := ( collectUserLemmas inputHints.terms) ++ ( collectHintDBLemmas inputHints.lemdbs)
  let userLemmas  userLemmas.mapM (m:=MetaM) (unfoldConstAndPreprocessLemma unfoldInfos)
  traceLemmas `auto.printLemmas "Lemmas collected from user-provided terms:" userLemmas
  let defeqLemmas  collectDefeqLemmas defeqNames
  let defeqLemmas  defeqLemmas.mapM (m:=MetaM) (unfoldConstAndprepReduceDefeq unfoldInfos)
  traceLemmas `auto.printLemmas "Lemmas collected from user-provided defeq hints:" defeqLemmas
  trace[auto.tactic] "Preprocessing took {(← IO.monoMsNow) - startTime}ms"
  let inhFacts  Inhabitation.getInhFactsFromLCtx
  let inhFacts  inhFacts.mapM (m:=MetaM) (unfoldConstAndPreprocessLemma unfoldInfos)
  traceLemmas `auto.printLemmas "Inhabitation lemmas :" inhFacts
  return (lctxLemmas ++ userLemmas ++ defeqLemmas, inhFacts)

@[tactic eauto]
def evalEAuto : Tactic
| `(eauto | eauto $attr $instr $hints $[$uords]*) => withMainContext do
  -- Suppose the goal is `∀ (x₁ x₂ ⋯ xₙ), G`
  -- First, apply `intros` to put `x₁ x₂ ⋯ xₙ` into the local context,
  --   now the goal is just `G`
  let (goalBinders, newGoal)  ( getMainGoal).intros
  let [nngoal]  newGoal.apply (.const ``Classical.byContradiction [])
    | throwError "{decl_name%} :: Unexpected result after applying Classical.byContradiction"
  let (ngoal, absurd)  MVarId.intro1 nngoal
  replaceMainGoal [absurd]
  withMainContext do
    let instr  parseInstr instr
    match instr with
    | .none =>
      let ext :=
        match attr.getId with
        | `euclid => euclidExtension
        | `diag => diagExtension
        | `metric => metricExtension
        | `super => superExtension
        | `transfer => transferExtension
        | _ => euclidExtension
      let axioms := ext.getState ( getEnv)
      let mut inputHints  parseHints hints
      for x in axioms do
        inputHints := { inputHints with terms := inputHints.terms.push (mkIdent x)}
      let (unfoldInfos, defeqNames)  parseUOrDs uords
      let (lemmas, inhFacts)  collectAllLemmas' inputHints unfoldInfos defeqNames (goalBinders.push ngoal)
      let declName?  Elab.Term.getDeclName?
      let proof  runAuto declName? lemmas inhFacts
      absurd.assign proof
    | .useSorry =>
      let proof  Meta.mkAppM ``sorryAx #[Expr.const ``False [], Expr.const ``false []]
      absurd.assign proof
| _ => throwUnsupportedSyntax

However, I'm getting significantly worse performance than the original LeanEuclid smt solver command. Is it possible that Point is defined as multiple different sorts in the generated cvc5 smt query?

Frederick Pu (Apr 30 2025 at 01:24):

for reference this is what some of my queries look like:

 query:
    (set-logic ALL)
    (declare-sort _exTy_0 0)
    (declare-sort _exTy_1 0)
    (declare-fun e!_15 (_exTy_0 _exTy_0 _exTy_1) Bool)
    (declare-fun e!_14 (_exTy_0 _exTy_0 _exTy_0) Bool)
    (declare-sort _exTy_3 0)
    (declare-fun e!_6 (_exTy_0 _exTy_0) _exTy_3)
    (declare-sort _exTy_2 0)
    (declare-fun e!_5 (_exTy_3) _exTy_2)
    (declare-sort _exTy_5 0)
    (declare-fun e!_21 (_exTy_5) _exTy_2)
    (declare-fun e!_0 (_exTy_0 _exTy_1)
...

It seems like the constants such as Point and Circle are showing up as their metavariable id's instead of their actual names. Does lean-smt have issues with handling constants?

Frederick Pu (Apr 30 2025 at 01:38):

actually it seems that lean auto is unable to infer stuff about real numbers. You need to use the smt command for it to actually understand that the mathlib version of Real corresponds to the smt version

import SystemE
import Smt
import Smt.Real
import Smt.Auto
import Book.Prop02


set_option maxHeartbeats 0
set_option auto.mono.mode "fol"
set_option trace.smt true
set_option auto.mono.ciInstDefEq.mode "reducible"
set_option auto.mono.termLikeDefEq.mode "reducible"

set_option auto.native true

attribute [rebind Auto.Native.solverFunc] Smt.smtSolverFunc

theorem womp (x : ) : x + x = 2 * x := by
  /-
    [smt] goal: False

[smt]
    query:
    (set-logic ALL)
    (declare-sort _exTy_0 0)
    (declare-const e!_3 _exTy_0)
    (declare-fun e!_0 (_exTy_0 _exTy_0) _exTy_0)
    (declare-const e!_1 _exTy_0)
    (declare-fun e!_2 (_exTy_0 _exTy_0) _exTy_0)
    (assert (distinct (e!_0 e!_1 e!_1) (e!_2 e!_3 e!_1)))
    (assert (not false))
    (check-sat)

[smt]
    error reason:
    cvc5.Error.error "Expected unsat, got sat"
  -/
  auto

Frederick Pu (Apr 30 2025 at 01:39):

is there anyway to include axioms and other constants as hints for the smt tactic?

Josh Clune (Apr 30 2025 at 18:50):

Currently, lean-smt has special support for the reals but lean-auto doesn't. Because of this, running smt on its own can yield a translation that involves smt reals, but running auto with lean-smt as a backend won't (because lean-auto's preprocessing procedure will get rid of reals before lean-smt has a chance to see them).

Frederick Pu (Apr 30 2025 at 19:00):

Here's my attempt to get lean-smt command to support axioms tagged with @[euclid]:

import Lean

import Smt.Tactic.Smt
import SystemE.Tactic.Attr

namespace Smt

open Lean hiding Command
open Elab Tactic Qq
open Smt Translate Query Reconstruct Util

namespace Tactic

syntax (name := esmt) "esmt" smtHints smtTimeout : tactic

@[tactic esmt]
def evalESmt : Tactic := fun stx => withMainContext do
  let axioms := euclidExtension.getState ( getEnv)
  let hintExprs : List Expr := ( axioms.toList.mapM (fun x => do
    let decl  getConstInfo x
    return decl.type
  )) ++ ( parseHints stx[1])
  let mvs  Smt.smt ( Tactic.getMainGoal) (hintExprs) ( parseTimeout stx[2])
  Tactic.replaceMainGoal mvs

But I still get the following error when I try to run the tactic:

/-
  trying to define _uniq✝⁸⁴⁵⁻⁰ but it has no equational definition and is not a let-decl
-/

Frederick Pu (Apr 30 2025 at 19:00):

any ideas what's going wrong?

Josh Clune (Apr 30 2025 at 19:23):

I'm not able to sit down properly and fully debug this at the moment, but off the cuff, it looks like in hintExprs, you're concatenating the list of decl types with the output of parseHints? But parseHints elaborates terms which are supposed to be proofs of relevant facts (not the fact itself). So I'm wondering if your current code is doing something analogous to creating a call like by smt [1 + 1 = 2]


Last updated: May 02 2025 at 03:31 UTC