Skip to content

Commit

Permalink
DecElim
Browse files Browse the repository at this point in the history
  • Loading branch information
JOSHCLUNE committed Nov 29, 2023
1 parent 15ae9bc commit e02955c
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 22 deletions.
99 changes: 99 additions & 0 deletions Duper/Rules/DecElim.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import Duper.Simp
import Duper.Util.ProofReconstruction
import Lean.Meta.Basic

namespace Duper
open RuleM
open SimpResult
open Lean
open Meta

initialize Lean.registerTraceClass `Rule.decElim

/-- Checks whether a literal is decidable, and if it is, uses mkDecide to return whether the literal is
decidably true or false. If the literal is not decidable, returns none. -/
def decideLiteral (lit : Lit) : MetaM (Option Bool) := do
try
let d ← mkDecide lit.toExpr
let d ← instantiateMVars d
let r ← withDefault <| whnf d
if r.isConstOf ``false then return some false
else if r.isConstOf ``true then return some true
else return none
catch _ =>
return none

def mkDecElimProof (refs : List (Option Nat)) (premises : List Expr) (parents : List ProofParent)
(transferExprs : Array Expr) (c : Clause) : MetaM Expr :=
Meta.forallTelescope c.toForallExpr fun xs body => do
let cLits := c.lits.map (fun l => l.map (fun e => e.instantiateRev xs))
let (parentsLits, appliedPremises, transferExprs) ← instantiatePremises parents premises xs transferExprs
let parentLits := parentsLits[0]!
let appliedPremise := appliedPremises[0]!

if refs.length != parentLits.size then throwError "mkDecElimProof error: {refs} and {parentLits} have different sizes"

let mut caseProofs := Array.mkEmpty parentLits.size
for i in [:parentLits.size] do
let lit := parentLits[i]!
match refs[i]! with
| none =>
-- This is adapted from the internals of `decide`
let expectedType := lit.toExpr
trace[Rule.decElim] "Trying to decide {expectedType}"
let d ← mkDecide expectedType
let d ← instantiateMVars d
let r ← withDefault <| whnf d
unless r.isConstOf ``false do
throwError "mkDecElimProof: Failed to reduce to 'false'{indentExpr r}"
trace[Rule.decElim] "{d} is false"
let s := d.appArg! -- get instance from `d`
let rflPrf ← mkEqRefl (toExpr false)
let proofCase := mkApp3 (Lean.mkConst ``of_decide_eq_false) expectedType s rflPrf
trace[Rule.decElim] "Built {proofCase} proving {d} is false"
let pr ← Meta.withLocalDeclD `h lit.toExpr fun h => do
let proofCase := mkApp proofCase h
let proofCase := mkApp2 (mkConst ``False.elim [levelZero]) body proofCase
Meta.mkLambdaFVars #[h] proofCase
caseProofs := caseProofs.push pr
| some j =>
-- need proof of `L_i → L_1 ∨ ... ∨ L_n`
let pr ← Meta.withLocalDeclD `h lit.toExpr fun h => do
let idx := j
Meta.mkLambdaFVars #[h] $ ← orIntro (cLits.map Lit.toExpr) idx h
caseProofs := caseProofs.push pr
let r ← orCases (parentLits.map Lit.toExpr) caseProofs
Meta.mkLambdaFVars xs $ mkApp r appliedPremise

/-- If there are any literals in `c` that are decidably true, then `decElim` removes `c`. Otherwise, if there are
any literals in `c` that are decidably false, then `decElim` yields the clause obtained by removing all such literals. -/
def decElim : MSimpRule := fun c => do
let c ← loadClause c
/-
Spec for newLits and refs:
If c.lits[i] is decidably false, then refs[i] = none
If c.lits[i] isn't decidably false, then refs[i] = some j where newLits[j] = c.lits[i]
-/
let mut newLits : List Lit := []
let mut refs : List (Option Nat) := []
for lit in c.lits do
match ← decideLiteral lit with
| some true => -- Remove the entire clause because it is decidably true
return some #[]
| some false => -- Remove the decidably false literal from the clause
refs := none :: refs
| none =>
refs := (some newLits.length) :: refs
newLits := lit :: newLits
-- To achieve the desired spec for newLits and refs, I must reverse them
newLits := newLits.reverse
refs := refs.reverse
trace[Rule.decElim] "newLits: {newLits}, refs: {refs}"
if (newLits.length = c.lits.size) then
return none
else
let resultClause ← yieldClause (MClause.mk newLits.toArray) "decidable false elimination"
(some (mkDecElimProof refs))
return some #[resultClause]

end Duper
65 changes: 44 additions & 21 deletions Duper/Saturate.lean
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Duper.Rules.BetaEtaReduction
import Duper.Rules.BoolSimp
import Duper.Rules.Clausification
import Duper.Rules.ClausifyPropEq
import Duper.Rules.DecElim
import Duper.Rules.ElimDupLit
import Duper.Rules.ElimResolvedLit
import Duper.Rules.EqualityFactoring
Expand Down Expand Up @@ -54,27 +55,49 @@ open SimpResult

def forwardSimpRules : ProverM (Array SimpRule) := do
let subsumptionTrie ← getSubsumptionTrie
return #[
betaEtaReduction.toSimpRule,
clausificationStep.toSimpRule,
syntacticTautologyDeletion1.toSimpRule,
syntacticTautologyDeletion2.toSimpRule,
boolSimp.toSimpRule,
syntacticTautologyDeletion3.toSimpRule,
elimDupLit.toSimpRule,
elimResolvedLit.toSimpRule,
destructiveEqualityResolution.toSimpRule,
identPropFalseElim.toSimpRule,
identBoolFalseElim.toSimpRule,
(forwardDemodulation (← getDemodSidePremiseIdx)).toSimpRule,
(forwardClauseSubsumption subsumptionTrie).toSimpRule,
(forwardEqualitySubsumption subsumptionTrie).toSimpRule,
(forwardContextualLiteralCutting subsumptionTrie).toSimpRule,
(forwardPositiveSimplifyReflect subsumptionTrie).toSimpRule,
(forwardNegativeSimplifyReflect subsumptionTrie).toSimpRule,
-- Higher order rules
identBoolHoist.toSimpRule
]
if ← getIncludeExpensiveRulesM then
return #[
betaEtaReduction.toSimpRule,
clausificationStep.toSimpRule,
syntacticTautologyDeletion1.toSimpRule,
syntacticTautologyDeletion2.toSimpRule,
boolSimp.toSimpRule,
syntacticTautologyDeletion3.toSimpRule,
elimDupLit.toSimpRule,
elimResolvedLit.toSimpRule,
destructiveEqualityResolution.toSimpRule,
decElim.toSimpRule, -- decElim subsumes identPropFalseElim and identBoolFalseElim
(forwardDemodulation (← getDemodSidePremiseIdx)).toSimpRule,
(forwardClauseSubsumption subsumptionTrie).toSimpRule,
(forwardEqualitySubsumption subsumptionTrie).toSimpRule,
(forwardContextualLiteralCutting subsumptionTrie).toSimpRule,
(forwardPositiveSimplifyReflect subsumptionTrie).toSimpRule,
(forwardNegativeSimplifyReflect subsumptionTrie).toSimpRule,
-- Higher order rules
identBoolHoist.toSimpRule
]
else
return #[
betaEtaReduction.toSimpRule,
clausificationStep.toSimpRule,
syntacticTautologyDeletion1.toSimpRule,
syntacticTautologyDeletion2.toSimpRule,
boolSimp.toSimpRule,
syntacticTautologyDeletion3.toSimpRule,
elimDupLit.toSimpRule,
elimResolvedLit.toSimpRule,
destructiveEqualityResolution.toSimpRule,
identPropFalseElim.toSimpRule,
identBoolFalseElim.toSimpRule,
(forwardDemodulation (← getDemodSidePremiseIdx)).toSimpRule,
(forwardClauseSubsumption subsumptionTrie).toSimpRule,
(forwardEqualitySubsumption subsumptionTrie).toSimpRule,
(forwardContextualLiteralCutting subsumptionTrie).toSimpRule,
(forwardPositiveSimplifyReflect subsumptionTrie).toSimpRule,
(forwardNegativeSimplifyReflect subsumptionTrie).toSimpRule,
-- Higher order rules
identBoolHoist.toSimpRule
]

-- The first `Clause` is the given clause
-- The second `MClause` is a loaded clause
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Each of the `options` supplied to Duper have the form `option := value` and are
- `portfolioMode`: Can be set to `true` or `false` and is set to `true` by default. If portfolioMode is enabled, then Duper will call 3-4 instances of itself with different option configurations, and if portfolioMode is disabled, then Duper will only call a single instance of itself. The benefit of enabling portfolioMode is that different option configurations are better suited to different problems, while the downside is that if portfolioMode is enabled, each instance of Duper is given less time to run before timing out. Generally, we recommend users keep portfolioMode enabled by default and use `set_option maxHeartbeats` to control how long they would like each Duper instance to run before giving up (if `maxHeartbeats` is set to `n`, each instance will be allocated approximately `n/3` heartbeats).
- `preprocessing`: Can be set to `full`, `monomorphization`, or `no_preprocessing`. This option will control the extent to which lemmas are preprocessed before being given to Duper. When portfolioMode is enabled and the preprocessing option is not specified, 3-4 instances will have full preprocessing and 1 instance will have no preprocessing. But when the preprocessing option is enabled, all attempted instances will use the specified preprocessing option. Generally, we recommend users do not specify the preprocessing option unless specifically prompted to by Duper.
- `inhabitationReasoning`: Can be set to `true` or `false`. If the goal or any provided lemmas include potentially empty types, Duper has to perform additional reasoning to guarantee the correctness of its proofs. If the user is confident that all types in the problem are provably nonempty, even outside the context of the current problem, then setting inhabitationReasoning to false can help Duper reason more efficiently (though during proof reconstruction, Duper will still verify that any assumptions made about type inhabitation are valid). Likewise, if the user is confident that there is at least one type in the problem that is either empty or can only be verified as nonempty using facts in the current context, then setting inhabitationReasoning to true will ensure that all Duper instances perform the necessary type inhabitation reasoning from the getgo.
- `includeExpensiveRules`: Can be set to `true` or `false`. Some of the rules that Duper uses to reason about higher-order problems can significantly worsen Duper's performance. If the user believes the problem they are attempting to solve only requires first-order reasoning, then setting includeExpensiveRules to false can make Duper's search more efficient.
- `includeExpensiveRules`: Can be set to `true` or `false`. Some of the rules that Duper uses to reason about higher-order problems can significantly worsen Duper's performance. Additionally, one of Duper's rules evaluates decidability instances, which can be prohibitively expensive for some problems. Setting includeExpensiveRules to false can make Duper's search more efficient by skipping these rules that have the potential to consume a significant portion of Duper's runtime.

### Proof Scripts

Expand Down

0 comments on commit e02955c

Please sign in to comment.