This module implements a model-based decision procedure for linear integer arithmetic, inspired by Section 4 of "Cutting to the Chase: Solving Linear Integer Arithmetic". Our implementation includes several enhancements and modifications: Key Features:
- Extended constraint support (equality and disequality)
- Optimized encoding of
Cooper-Left
rule using "big"-disjunction instead of fresh variables - Decision variable tracking for case splits (disequalities,
Cooper-Left
,Cooper-Right
)
Constraint Types: We handle four categories of linear polynomial constraints (where p is a linear polynomial):
Implementation Details:
- Polynomials use
Int.Linear.Poly
with sorted linear monomials (leading monomial contains max variable) - Equalities are eliminated eagerly
- Divisibility constraints are maintained in solved form (one constraint per variable) using
Div-Solve
Model Construction:
The procedure builds a model incrementally, resolving conflicts through constraint generation.
For example:
Given a partial model {x := 1}
and constraint 3 ∣ 3*y + x + 1
:
- Cannot extend to
y
because3 ∣ 3*y + 2
is unsatisfiable - Generate implied constraint
3 ∣ x + 1
- Force model update for
x
Variable Assignment:
When assigning a variable y
, we consider:
- Best upper and lower bounds (inequalities)
- Divisibility constraint
- Disequality constraints
Cooper-Left
andCooper-Right
rules handle the combination of inequalities and divisibility. For unsatisfiable disequalities p ≠ 0, we generate case split:p + 1 ≤ 0 ∨ -p + 1 ≤ 0
Contradiction Handling:
- Check dependency on decision variables
- If independent, use contradiction to close current grind goal
- Otherwise, trigger backtracking
Optimization: We employ rational approximation for model construction:
- Continue with rational solutions when integer solutions aren't immediately found
- Helps identify simpler unsatisfiability proofs before full integer model construction
- core0
(a zero : Expr)
: EqCnstrProof
An equality
a = 0
coming from the core. - core
(a b : Expr)
(p₁ p₂ : Poly)
: EqCnstrProof
An equality
a = b
coming from the core.p₁
andp₂
are the polynomials corresponding toa
andb
. - coreNat (a b : Expr) (lhs rhs : Int.OfNat.Expr) (lhs' rhs' : Int.Linear.Expr) : EqCnstrProof
- defn
(e : Expr)
(p : Poly)
: EqCnstrProof
e
isp
- defnNat (e : Int.OfNat.Expr) (x : Var) (e' : Int.Linear.Expr) : EqCnstrProof
- norm (c : EqCnstr) : EqCnstrProof
- divCoeffs (c : EqCnstr) : EqCnstrProof
- subst (x : Var) (c₁ c₂ : EqCnstr) : EqCnstrProof
- ofLeGe (c₁ c₂ : LeCnstr) : EqCnstrProof
Instances For
The predicate of type Nat → Prop
, which serves as the conclusion of the
cooper_left
, cooper_right
, cooper_dvd_left
, and cooper_dvd_right
theorems.
The specific predicate used is determined as follows:
cooper_left_split
(ifleft
istrue
andc₃?
isnone
)cooper_right_split
(ifleft
isfalse
andc₃?
isnone
)cooper_dvd_left_split
(ifleft
istrue
andc₃?
issome
)cooper_dvd_right_split
(ifleft
isfalse
andc₃?
issome
)
See CooperSplit
Instances For
An instance of the CooperSplitPred
at k
.
- pred : CooperSplitPred
- k : Nat
- h : CooperSplitProof
Instances For
The cooper_left
, cooper_right
, cooper_dvd_left
, and cooper_dvd_right
theorems have a resulting type
that is a big-or of the form OrOver n (cooper_*_split ...)
. The predicate (cooper_*_split ...)
has type Nat → Prop
.
The cutsat
procedure performs case splitting on (cooper_*_split ... (n-1))
down to (cooper_*_split ... 1)
.
If it derives False
from each case, it uses orOver_resolve
and orOver_one
to deduce the final case,
which has type (cooper_*_split ... 0)
.
- dec
(h : FVarId)
: CooperSplitProof
The first
n-1
cases are decisions (aka case-splits). - last
(hs : Array (FVarId × UnsatProof))
(decVars : Array FVarId)
: CooperSplitProof
The last case which has type
(cooper_*_split ... 0)
Instances For
- core (e : Expr) : DvdCnstrProof
- coreNat (e : Expr) (d : Nat) (b : Int.OfNat.Expr) (b' : Int.Linear.Expr) : DvdCnstrProof
- norm (c : DvdCnstr) : DvdCnstrProof
- divCoeffs (c : DvdCnstr) : DvdCnstrProof
- solveCombine (c₁ c₂ : DvdCnstr) : DvdCnstrProof
- solveElim (c₁ c₂ : DvdCnstr) : DvdCnstrProof
- elim (c : DvdCnstr) : DvdCnstrProof
- ofEq (x : Var) (c : EqCnstr) : DvdCnstrProof
- subst (x : Var) (c₁ : EqCnstr) (c₂ : DvdCnstr) : DvdCnstrProof
- cooper₁ (c : CooperSplit) : DvdCnstrProof
- cooper₂
(c : CooperSplit)
: DvdCnstrProof
c.c₃?
must besome
Instances For
- core (e : Expr) : LeCnstrProof
- coreNeg (e : Expr) (p : Poly) : LeCnstrProof
- coreNat (e : Expr) (lhs rhs : Int.OfNat.Expr) (lhs' rhs' : Int.Linear.Expr) : LeCnstrProof
- coreNatNeg (e : Expr) (lhs rhs : Int.OfNat.Expr) (lhs' rhs' : Int.Linear.Expr) : LeCnstrProof
- denoteAsIntNonneg (rhs : Int.OfNat.Expr) (rhs' : Int.Linear.Expr) : LeCnstrProof
- dec (h : FVarId) : LeCnstrProof
- norm (c : LeCnstr) : LeCnstrProof
- divCoeffs (c : LeCnstr) : LeCnstrProof
- combine (c₁ c₂ : LeCnstr) : LeCnstrProof
- combineDivCoeffs (c₁ c₂ : LeCnstr) (k : Int) : LeCnstrProof
- subst (x : Var) (c₁ : EqCnstr) (c₂ : LeCnstr) : LeCnstrProof
- ofLeDiseq (c₁ : LeCnstr) (c₂ : DiseqCnstr) : LeCnstrProof
- ofDiseqSplit (c₁ : DiseqCnstr) (decVar : FVarId) (h : UnsatProof) (decVars : Array FVarId) : LeCnstrProof
- cooper (c : CooperSplit) : LeCnstrProof
- dvdTight (c₁ : DvdCnstr) (c₂ : LeCnstr) : LeCnstrProof
- negDvdTight (c₁ : DvdCnstr) (c₂ : LeCnstr) : LeCnstrProof
Instances For
- core0
(a zero : Expr)
: DiseqCnstrProof
An disequality
a != 0
coming from the core. That is,(a = 0) = False
in the core. - core
(a b : Expr)
(p₁ p₂ : Poly)
: DiseqCnstrProof
An disequality
a ≠ b
coming from the core. That is,(a = b) = False
in the core.p₁
andp₂
are the polynomials corresponding toa
andb
. - coreNat (a b : Expr) (lhs rhs : Int.OfNat.Expr) (lhs' rhs' : Int.Linear.Expr) : DiseqCnstrProof
- norm (c : DiseqCnstr) : DiseqCnstrProof
- divCoeffs (c : DiseqCnstr) : DiseqCnstrProof
- neg (c : DiseqCnstr) : DiseqCnstrProof
- subst (x : Var) (c₁ : EqCnstr) (c₂ : DiseqCnstr) : DiseqCnstrProof
Instances For
A proof of False
.
Remark: We will later add support for a backtraking search inside of cutsat.
- dvd (c : DvdCnstr) : UnsatProof
- le (c : LeCnstr) : UnsatProof
- eq (c : EqCnstr) : UnsatProof
- diseq (c : DiseqCnstr) : UnsatProof
- cooper (c₁ c₂ : LeCnstr) (c₃ : DvdCnstr) : UnsatProof
Instances For
Equations
- Lean.Meta.Grind.Arith.Cutsat.instInhabitedLeCnstr = { default := { p := Int.Linear.Poly.num 0, h := Lean.Meta.Grind.Arith.Cutsat.LeCnstrProof.core default } }
Equations
- Lean.Meta.Grind.Arith.Cutsat.instInhabitedDvdCnstr = { default := { d := 0, p := Int.Linear.Poly.num 0, h := Lean.Meta.Grind.Arith.Cutsat.DvdCnstrProof.core default } }
Equations
- Lean.Meta.Grind.Arith.Cutsat.instInhabitedCooperSplit = { default := { pred := default, k := 0, h := Lean.Meta.Grind.Arith.Cutsat.CooperSplitProof.dec default } }
Instances For
State of the cutsat procedure.
Mapping from variables to their denotations.
Mapping from
Expr
to a variable representing it.- foreignVarMap : PHashMap ENodeKey (Var × ForeignType)
Mapping from foreign terms to their variable and type (e.g.,
Nat
). They are also marked usingmarkAsCutsatTerm
. - foreignVars : PHashMap ForeignType (PArray Expr)
Some foreign variables encode nested terms such as
b+1
. This is a mapping from this kind of variable to the integer variable representingnatCast (b+1)
.Mapping from variables to divisibility constraints. Recall that we keep the divisibility constraint in solved form. Thus, we have at most one divisibility per variable.
Mapping from variables to their "lower" bounds. We say a relational constraint
c
is a lower bound for a variablex
ifx
is the maximal variable inc
, andx
coefficient inc
is negative.Mapping from variables to their "upper" bounds. We say a relational constraint
c
is a upper bound for a variablex
ifx
is the maximal variable inc
, andx
coefficient inc
is positive.- diseqs : PArray (PArray DiseqCnstr)
Mapping from variables to their disequalities. We say a disequality constraint
c
is a disequality for a variablex
ifx
is the maximal variable inc
. Mapping from variable to occurrences. For example, an entry
x ↦ {y, z}
means thatx
may occur indvdCnstrs
,lowers
, oruppers
of variablesy
andz
. Ifx
occurs indvdCnstrs[y]
,lowers[y]
, oruppers[y]
, theny
is inoccurs[x]
, but the reverse is not true. Ifx
is inelimStack
, thenoccurs[x]
is the empty set.Partial assignment being constructed by cutsat.
- nextCnstrId : Nat
Next unique id for a constraint.
- caseSplits : Bool
caseSplits
istrue
if cutsat is searching for model and already performed case splits. This information is used to decide whether a conflict should immediately close the currentgrind
goal or not. - conflict? : Option UnsatProof
conflict?
issome ..
if a contradictory constraint was derived. This field is only set whencaseSplits
istrue
. Otherwise, we can convertUnsatProof
into a Lean term and close the currentgrind
goal. Cache decision variables used when splitting on disequalities. This is necessary because the same disequality may be in different conflicts.
Pairs
(x, n)
s.t. we have expanded the theorems
Instances For
Equations
- One or more equations did not get rendered due to their size.