Skip to content

z3py interface backed by grind + managed project support#1

Open
kiranandcode wants to merge 5 commits into
mainfrom
iterate
Open

z3py interface backed by grind + managed project support#1
kiranandcode wants to merge 5 commits into
mainfrom
iterate

Conversation

@kiranandcode
Copy link
Copy Markdown
Collaborator

@kiranandcode kiranandcode commented May 20, 2026

Philip tried lean.py, bounced off the setup friction. Loading Batteries/Mathlib means creating a Lake project with the right deps wired up — lakefile, moreLinkObjs, the whole dance. He also wanted a programmatic interface to grind that looks like z3py. This branch ships both.

lean_py.z3 — z3py vocabulary backed by grind

Same Int, Bools, ForAll, Solver, prove. Expressions build Lean syntax under the hood; free variables are tracked and universally quantified at proof time. The solver tries grind → omega → decide → simp_all in order.

Covers the z3py surface that knuckledragger uses: propositional logic, integer/nat/real arithmetic, bit-vectors (BitVec n with &&&/|||/^^^/<<</>>>), arrays (function types + if-then-else store), algebraic datatypes (uninterpreted sorts + constructor functions), quantifiers, uninterpreted sorts and functions, Solver with push/pop.

Lean is a proof checker, not an SMT solver. check() returns unsat (negation proved) or unknown — never sat. No model extraction.

from lean_py.z3 import *
x, y = Ints('x y')
prove(Implies(And(x > 0, y > 0), x + y > 0))  # proved

lean_py.project.ManagedProject — zero-config Lake projects

Creates and caches a Lake project under ~/.lean_py/managed/ so users can import Batteries or import Mathlib without touching a lakefile. Deps pinned to the active toolchain version (batteries@v4.29.1 for leanprover/lean4:v4.29.1). Tested end-to-end with Batteries — 126k decls loaded, proofs discharged through the managed kernel.

from lean_py.project import ManagedProject
from lean_py.z3 import *
mp = ManagedProject.get(deps=("batteries",))
set_kernel(mp.kernel())
prove(Int('x') + 0 == Int('x'))

What ships

New files:

  • lean_py/z3/core.py — expression AST, sorts, operators, quantifiers, bitvecs, arrays, datatypes
  • lean_py/z3/solver.py — Solver, prove(), solve(), simplify()
  • lean_py/project.py — ManagedProject
  • tests/test_z3_compat.py — 51 tests
  • examples/07_z3py_drop_in/ — worked example

Modified: lean_py/utils.py (toolchain version helper), README.md (z3py + managed project docs)

Tests

182 total (175 passed, 7 skipped for optional deps). 51 new z3 tests covering: expression building, goal string construction, arithmetic/boolean/quantifier proofs, solver UNSAT, push/pop/context manager, Socrates syllogism, 2-queens, bitvec idempotence, array store-select, constant arrays, datatype constructors.

Two features that remove the setup friction for using lean.py as a
proving backend:

lean_py.z3 — z3py-compatible expression AST and solver.  Expressions
build Lean syntax strings under the hood; free variables are tracked
and universally quantified at proof time.  The solver tries grind,
omega, decide, simp_all in order.  Supports Int/Nat/Real/Bool sorts,
arithmetic operators, quantifiers, uninterpreted sorts and functions,
Distinct, push/pop, context manager protocol.

lean_py.project.ManagedProject — creates and caches Lake projects
under ~/.lean_py/managed/<hash>/ so users can load Batteries or Mathlib
without maintaining a lakefile.  Deps are pinned to the active toolchain
version (e.g. batteries@v4.29.1 for leanprover/lean4:v4.29.1).

New files:
  lean_py/z3/core.py      expression AST (~500 lines)
  lean_py/z3/solver.py    Solver, prove(), solve() (~250 lines)
  lean_py/z3/__init__.py   re-exports for `from lean_py.z3 import *`
  lean_py/project.py       ManagedProject (~210 lines)
  tests/test_z3_compat.py  31 tests (expr building, proving, solver, syllogism, n-queens)
  examples/07_z3py_drop_in/  worked example showing the z3py vocabulary
                             as a drop-in for knuckledragger-style proofs

Modified:
  lean_py/utils.py   add lean_toolchain_version()
  README.md          z3py prover section, managed project docs, updated layout
Add BitVecSortRef/BitVecRef with Lean's BitVec n operators (<<<, >>>,
&&&, |||, ^^^, ~~~).  Add ArraySortRef/ArrayRef encoding arrays as
Lean function types, Store as if-then-else lambda, Select as application,
K as constant function.  Add Datatype builder (uninterpreted sort +
constructor functions).

Remove the ModelRef placeholder class entirely -- model extraction is
out of scope (Lean is a proof checker).

51 z3 tests pass, full regression green (131 + 7 skipped).
Replace string-based z3 expression building with a typed AST that
compiles to Lean.Expr via MetaM. The Lean side (LeanPy/Z3.lean) defines
Z3Sort/Z3BinOp/Z3UnOp/Z3Expr inductives with a compileExpr that uses
mkAppM for automatic instance resolution. Python builds pure AST nodes
(_ast.py), marshals them at proof time, and calls z3_compile +
z3_goal_create_expr to get a GoalState.

Fix the marshaller to correctly handle scalar fields (Bool, enum
inductives, numeric types) per the Lean ABI: pointer fields first via
lean_ctor_get/set, then scalars at byte offsets via lean_ctor_get/set_uintN.
Each TypeWrapper carries its own ctor_scalar_size and conversion functions
so encode/decode is type-agnostic. Fix cache key collision between opaque
and named TypeReprs with identical names.
…edicates

Fix __pow__ (was MUL), add __bool__ guard, simplify() returns ExprRef,
empty Solver.check() returns sat. Add reverse operators, BV functions
(Extract, Concat, ZeroExt, SignExt, LShR, BV2Int, Int2BV), arithmetic
helpers (Abs, ToReal, ToInt, Sum, Product, Lambda), 21 predicates,
And/Or list support. Lean compiler: pow with Nat exponent coercion,
concat, bv2int, lambda, extract/zeroExt/signExt/int2bv, toReal/toInt.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant