feat: combine two cutsat proof steps (#7371)

This PR combines two cutsat proof steps that often appear together.
This commit is contained in:
Leonardo de Moura 2025-03-06 15:28:49 -08:00 committed by GitHub
parent 11aff52fb1
commit 00a4503c4f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 32 additions and 2 deletions

View file

@ -846,6 +846,26 @@ theorem le_combine (ctx : Context) (p₁ p₂ p₃ : Poly)
· rw [← Int.zero_mul (Poly.denote ctx p₂)]; apply Int.mul_le_mul_of_nonpos_right <;> simp [*]
· rw [← Int.zero_mul (Poly.denote ctx p₁)]; apply Int.mul_le_mul_of_nonpos_right <;> simp [*]
def le_combine_coeff_cert (p₁ p₂ p₃ : Poly) (k : Int) : Bool :=
let a₁ := p₁.leadCoeff.natAbs
let a₂ := p₂.leadCoeff.natAbs
let p := p₁.mul a₂ |>.combine (p₂.mul a₁)
k > 0 && (p.divCoeffs k && p₃ == p.div k)
theorem le_combine_coeff (ctx : Context) (p₁ p₂ p₃ : Poly) (k : Int)
: le_combine_coeff_cert p₁ p₂ p₃ k → p₁.denote' ctx ≤ 0 → p₂.denote' ctx ≤ 0 → p₃.denote' ctx ≤ 0 := by
simp only [le_combine_coeff_cert, gt_iff_lt, Bool.and_eq_true, decide_eq_true_eq, beq_iff_eq, and_imp]
let a₁ := p₁.leadCoeff.natAbs
let a₂ := p₂.leadCoeff.natAbs
generalize h : (p₁.mul a₂ |>.combine (p₂.mul a₁)) = p
intro h₁ h₂ h₃ h₄ h₅
have := le_combine ctx p₁ p₂ p
simp only [le_combine_cert, beq_iff_eq] at this
have aux₁ := this h.symm h₄ h₅
have := le_coeff ctx p p₃ k
simp only [le_coeff_cert, gt_iff_lt, Bool.and_eq_true, decide_eq_true_eq, beq_iff_eq, and_imp] at this
exact this h₁ h₂ h₃ aux₁
theorem le_unsat (ctx : Context) (p : Poly) : p.isUnsatLe → p.denote' ctx ≤ 0 → False := by
simp [Poly.isUnsatLe]; split <;> simp

View file

@ -105,6 +105,11 @@ partial def LeCnstr.toExprProof (c' : LeCnstr) : ProofM Expr := caching c' do
(← getContext) (← mkPolyDecl c₁.p) (← mkPolyDecl c₂.p) (← mkPolyDecl c'.p)
reflBoolTrue
(← c₁.toExprProof) (← c₂.toExprProof)
| .combineDivCoeffs c₁ c₂ k =>
return mkApp8 (mkConst ``Int.Linear.le_combine_coeff)
(← getContext) (← mkPolyDecl c₁.p) (← mkPolyDecl c₂.p) (← mkPolyDecl c'.p) (toExpr k)
reflBoolTrue
(← c₁.toExprProof) (← c₂.toExprProof)
| .subst x c₁ c₂ =>
let a := c₁.p.coeff x
let thm := if a ≥ 0 then
@ -285,7 +290,7 @@ partial def LeCnstr.collectDecVars (c' : LeCnstr) : CollectDecVarsM Unit := do u
| .expr h => collectExpr h
| .notExpr .. => return () -- This kind of proof is used for connecting with the `grind` core.
| .cooper c | .norm c | .divCoeffs c => c.collectDecVars
| .combine c₁ c₂ | .subst _ c₁ c₂ | .ofLeDiseq c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
| .combine c₁ c₂ | .combineDivCoeffs c₁ c₂ _ | .subst _ c₁ c₂ | .ofLeDiseq c₁ c₂ => c₁.collectDecVars; c₂.collectDecVars
| .ofDiseqSplit (decVars := decVars) .. => decVars.forM markAsFound
partial def DiseqCnstr.collectDecVars (c' : DiseqCnstr) : CollectDecVarsM Unit := do unless (← alreadyVisited c') do

View file

@ -275,7 +275,11 @@ def resolveRealLowerUpperConflict (c₁ c₂ : LeCnstr) : GoalM Bool := do
trace[grind.cutsat.conflict] "not resolved"
return false
else
let c := { p, h := .combine c₁ c₂ : LeCnstr }
let k := p.gcdCoeffs'
let c := if k == 1 then
{ p, h := .combine c₁ c₂ : LeCnstr }
else
{ p := p.div k, h := .combineDivCoeffs c₁ c₂ k : LeCnstr }
trace[grind.cutsat.conflict] "resolved: {← c.pp}"
c.assert
return true

View file

@ -153,6 +153,7 @@ inductive LeCnstrProof where
| norm (c : LeCnstr)
| divCoeffs (c : LeCnstr)
| combine (c₁ c₂ : LeCnstr)
| combineDivCoeffs (c₁ c₂ : LeCnstr) (k : Int)
| subst (x : Var) (c₁ : EqCnstr) (c₂ : LeCnstr)
| ofLeDiseq (c₁ : LeCnstr) (c₂ : DiseqCnstr)
| ofDiseqSplit (c₁ : DiseqCnstr) (decVar : FVarId) (h : UnsatProof) (decVars : Array FVarId)