Skip to content

Commit

Permalink
Merge pull request #1 from zafer-esen/ghost-vars-refactor
Browse files Browse the repository at this point in the history
Extended quantifiers refactor
  • Loading branch information
zafer-esen authored Feb 26, 2024
2 parents 21332ac + 46c1a5c commit 22a712e
Show file tree
Hide file tree
Showing 20 changed files with 2,205 additions and 846 deletions.
6 changes: 3 additions & 3 deletions src/main/scala/lazabs/ast/ASTree.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
package lazabs.ast

import lazabs.types._
import ap.theories.{ADT, Heap, Theory, TheoryCollector, TheoryRegistry}
import lazabs.horn.extendedquantifiers.ExtendedQuantifier
import ap.theories.{ADT, Heap}
import lazabs.horn.extendedquantifiers.theories.AbstractExtendedQuantifier


object ASTree {
Expand Down Expand Up @@ -111,7 +111,7 @@ object ASTree {
exprList: Seq[Expression]) extends Expression

// Extended quantifiers
case class ExtQuantifierFun(extQuantifier: ExtendedQuantifier,
case class ExtQuantifierFun(extQuantifier: AbstractExtendedQuantifier,
exprList: Seq[Expression]) extends Expression

// Bit-vectors
Expand Down
794 changes: 345 additions & 449 deletions src/main/scala/lazabs/horn/extendedquantifiers/ClauseInstrumenter.scala

Large diffs are not rendered by default.

297 changes: 107 additions & 190 deletions src/main/scala/lazabs/horn/extendedquantifiers/GhostVariableAdder.scala

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023 Jesper Amilon, Zafer Esen, Philipp Ruemmer.
* Copyright (c) 2024 Jesper Amilon, Zafer Esen, Philipp Ruemmer.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -30,17 +30,22 @@

package lazabs.horn.extendedquantifiers

import ap.parser.IExpression.{Predicate, _}
import ap.parser.IExpression._
import ap.parser.IFormula
import lazabs.horn.bottomup.HornClauses.Clause
import lazabs.horn.extendedquantifiers.Util.ExtendedQuantifierInfo
import lazabs.horn.extendedquantifiers.Util.ExtendedQuantifierApp
import lazabs.horn.extendedquantifiers.GhostVariableAdder._
import lazabs.horn.preprocessor.HornPreprocessor._
import lazabs.horn.extendedquantifiers.theories.AbstractExtendedQuantifier
import lazabs.horn.preprocessor._
import lazabs.prover.PrincessWrapper.expr2Formula
import lazabs.horn.preprocessor.HornPreprocessor._

/**
* Initializes ghost variables added by [[GhostVariableAdder]] using
* the initializers [[InstrumentationOperator.ghostVarInitialValues]].
*/
class GhostVariableInitializer(
ghostVarInds : Map[ExtendedQuantifierInfo, Map[Predicate, Seq[GhostVariableInds]]])
predToGhostVarIndsPerExqApp : Map[ExtendedQuantifierApp, Map[Predicate, GhostVariableInds]],
exqToInstrumentationOperator : Map[AbstractExtendedQuantifier, InstrumentationOperator])
extends HornPreprocessor {
override val name: String = "Initializing ghost variables"

Expand All @@ -53,17 +58,16 @@ class GhostVariableInitializer(
val newClauses = for (clause <- clauses) yield {
if (entryClauses contains clause) {
val newConjuncts = new collection.mutable.ArrayBuffer[IFormula]
for ((exq, predToGhostVars) <- ghostVarInds) {
val ghostVars = predToGhostVars(clause.head.pred)
for (GhostVariableInds(lo, hi, res, arr, alienInds) <- ghostVars) {
newConjuncts +=
clause.head.args(lo) === 0 &&& clause.head.args(hi) === 0 &&&
exq.exTheory.identity === clause.head.args(res)
for (AlienGhostVariableInds(_, vSet) <- alienInds) {
// ghost alien vars are initially not set
newConjuncts += !expr2Formula(clause.head.args(vSet))
}
}
for ((exqApp, predToGhostVarInds) <- predToGhostVarIndsPerExqApp) {
val instOp = exqToInstrumentationOperator(exqApp.exTheory)

for (ghostVarToInd <- predToGhostVarInds(clause.head.pred))
for ((ghostVar, ghostInd) <- ghostVarToInd)
instOp.ghostVarInitialValues get ghostVar match {
case Some(initialValue) =>
newConjuncts += clause.head.args(ghostInd) === initialValue
case None => // no initialization needed
}
}
val newConstraint = clause.constraint &&&
newConjuncts.fold(i(true))((c1, c2) => c1 &&& c2)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023 Jesper Amilon, Zafer Esen, Philipp Ruemmer.
* Copyright (c) 2024 Jesper Amilon, Zafer Esen, Philipp Ruemmer.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023 Jesper Amilon, Zafer Esen, Philipp Ruemmer.
* Copyright (c) 2024 Jesper Amilon, Zafer Esen, Philipp Ruemmer.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -30,22 +30,22 @@

package lazabs.horn.extendedquantifiers

import ap.parser.{IAtom, IExpression, IFormula, IIntLit}
import ap.parser.{IAtom, IExpression, IFormula}
import ap.terfor.conjunctions.Conjunction
import ap.terfor.preds.Predicate
import ap.util.Timeout
import lazabs.GlobalParameters
import lazabs.horn.abstractions.AbstractionRecord.AbstractionMap
import lazabs.horn.abstractions.{AbstractionRecord, StaticAbstractionBuilder}
import lazabs.horn.abstractions.VerificationHints.VerifHintElement
import lazabs.horn.bottomup.HornClauses.Clause
import lazabs.horn.bottomup.{DagInterpolator, IncrementalHornPredAbs, NormClause, PredicateStore, TemplateInterpolator}
import lazabs.horn.bottomup.{DagInterpolator, IncrementalHornPredAbs, PredicateStore, TemplateInterpolator}
import lazabs.horn.bottomup.Util.{Dag, DagEmpty, DagNode}
import lazabs.horn.preprocessor.{DefaultPreprocessor, PreStagePreprocessor}
import lazabs.horn.extendedquantifiers.theories.AbstractExtendedQuantifier
import lazabs.horn.preprocessor.DefaultPreprocessor
import lazabs.horn.preprocessor.HornPreprocessor.{BackTranslator, Clauses, ComposedBackTranslator, VerificationHints}

import scala.collection.immutable.Map
import scala.collection.{immutable, mutable}
import scala.collection.mutable.{ArrayBuffer, HashMap => MHashMap, HashSet => MHashSet}
import scala.collection.mutable.{ArrayBuffer, Buffer => MBuffer, HashMap => MHashMap, HashSet => MHashSet}
import scala.util.Random

object InstrumentationLoop {
Expand All @@ -55,14 +55,17 @@ object InstrumentationLoop {
case object Inconclusive extends Result
}

class InstrumentationLoop (clauses : Clauses,
hints : VerificationHints,
templateBasedInterpolation : Boolean = false,
templateBasedInterpolationTimeout : Long = 2000,
templateBasedInterpolationType :
StaticAbstractionBuilder.AbstractionType.Value =
StaticAbstractionBuilder.AbstractionType.RelationalEqs,
globalPredicateTemplates: Map[Predicate, Seq[VerifHintElement]] = Map()) {
class InstrumentationLoop (
clauses : Clauses,
hints : VerificationHints,
extendedQuantifierToInstOp : Map[AbstractExtendedQuantifier, InstrumentationOperator],
templateBasedInterpolation : Boolean = false,
templateBasedInterpolationTimeout : Long = 2000,
templateBasedInterpolationType :
StaticAbstractionBuilder.AbstractionType.Value =
StaticAbstractionBuilder.AbstractionType.RelationalEqs,
globalPredicateTemplates :
Map[Predicate, Seq[VerifHintElement]] = Map()) {
import InstrumentationLoop._

private val backTranslators = new ArrayBuffer[BackTranslator]
Expand All @@ -81,7 +84,11 @@ class InstrumentationLoop (clauses : Clauses,
private var curHints : VerificationHints = hints
private val (simpClauses, newHints1, backTranslator1) =
Console.withErr(Console.out) {
preprocessor.process(clauses, curHints)
val slicingPre = GlobalParameters.get.slicing
GlobalParameters.get.slicing = false
val res = preprocessor.process(clauses, curHints)
GlobalParameters.get.slicing = slicingPre
res
}
curHints = newHints1
backTranslators += backTranslator1
Expand All @@ -92,20 +99,20 @@ class InstrumentationLoop (clauses : Clauses,
// clauses.foreach(clause => println(clause.toPrologString))
// println("="*80 + "\n")

private val ghostVarRanges: mutable.Buffer[Int] = (1 to 2).toBuffer
private val ghostVarRanges: MBuffer[Int] = (1 to 2).toBuffer
private var rawResult : Result = Inconclusive
private val searchSpaceSizePerNumGhostRanges = new MHashMap[Int, Int]
private val searchStepsPerNumGhostRanges = new MHashMap[Int, Int]
private var lastSolver : IncrementalHornPredAbs[Clause] = _
private var lastInstrumenter : SimpleExtendedQuantifierInstrumenter = _
private var lastSolver : IncrementalHornPredAbs[Clause] = _
private var lastInstrumenter : InstrumentationOperatorApplier = _

while (ghostVarRanges.nonEmpty && rawResult == Inconclusive) {
val numRanges = ghostVarRanges.head
ghostVarRanges -= numRanges

println("# ghost variable ranges: " + numRanges)
val instrumenter = new SimpleExtendedQuantifierInstrumenter(
simpClauses, curHints, Set.empty, numRanges)
val instrumenter = new InstrumentationOperatorApplier(
simpClauses, curHints, Set.empty, extendedQuantifierToInstOp, numRanges)
lastInstrumenter = instrumenter

curHints = instrumenter.newHints
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/**
* Copyright (c) 2024 Jesper Amilon, Zafer Esen, Philipp Ruemmer.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the authors nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package lazabs.horn.extendedquantifiers

import ap.parser._
import ap.types.Sort
import lazabs.horn.extendedquantifiers.Util._
import lazabs.horn.extendedquantifiers.theories.AbstractExtendedQuantifier

object RewriteRules {
/**
* The result of a rewrite rule. Let `c` be the original conjunct that the
* rewrite rule is applied to. Then the new formula will be:
* `subst(c &&& newConjunct, rewriteFormulas)` where `subst` is some method
* that rewrites all formulas appearing in the keys of `rewriteFormulas` with
* their values.
* Each formula in `assertions` will also be asserted.
*/
case class Result(newConjunct : IFormula,
rewriteFormulas : Map[IFormula, IFormula],
assertions : Seq[IFormula])
}

trait RewriteRules {
// These rules are hardcoded as the array operations, but with some effort
// the methods could also be generalized to a map from theory functions to
// rewrite rules.

import InstrumentationOperator.GhostVar

/**
* `oldGhostTerms` and `newGhostTerms` are the old and new values of ghost
* variables defined in [[InstrumentationOperator.ghostVars]].
* `otherConstants` contains other terms that might be relevant for the
* rewriting, for instance alien terms which can appear in predicates.
* All rewrite rules return a sequence of [[RewriteRules.Result]], which
* contains information about which conjuncts to add, rewrite, and assert.
*/
def rewriteStore(oldGhostTerms : Map[GhostVar, ITerm],
newGhostTerms : Map[GhostVar, ITerm],
otherConstants : Set[IConstant],
storeInfo : StoreInfo) : Seq[RewriteRules.Result]
def rewriteSelect(oldGhostTerms : Map[GhostVar, ITerm],
newGhostTerms : Map[GhostVar, ITerm],
otherConstants : Set[IConstant],
selectInfo : SelectInfo) : Seq[RewriteRules.Result]
def rewriteConst(oldGhostTerms : Map[GhostVar, ITerm],
newGhostTerms : Map[GhostVar, ITerm],
otherConstants : Set[IConstant],
constInfo : ConstInfo) : Seq[RewriteRules.Result]

/**
* The rule for rewriting applications of [[AbstractExtendedQuantifier.morphism]].
* @param ghostTerms A collection of [[InstrumentationOperator.ghostVars]].
* There will be
* [[InstrumentationOperatorApplier.numGhostRanges]] such
* collections.
* @param exqInfo Information extracted from the application of
* [[AbstractExtendedQuantifier.morphism]].
*/
def rewriteAggregate(
ghostTerms : Seq[Map[GhostVar, ITerm]],
exqInfo : ExtendedQuantifierApp) : Seq[RewriteRules.Result]
}

object InstrumentationOperator {
class GhostVar(val sort : Sort, val name : String) {
override def toString : String = name
}
}

abstract class InstrumentationOperator(val exq : AbstractExtendedQuantifier)
extends RewriteRules {
import InstrumentationOperator.GhostVar

/**
* These are the ghost variables of the extended quantifier.
* There will be [[InstrumentationOperatorApplier.numGhostRanges]] such
* sequences of ghost variables.
* Sometimes we might want additional ghost variables that are not directly
* related to the extended quantifier (such as alien terms). For simplicity,
* these should be declared as part of below ghostVars. As a result, these
* terms will be needlessly duplicated when `numGhostRanges > 1`.
*
* @todo Check if Eldarica's other preprocessors slice away such copies.
*/
val ghostVars : Seq[GhostVar]

/**
* Initial values for `ghostVars`.
* If an initial value is not found for a GhostVar, it is left uninitialized.
*/
val ghostVarInitialValues : Map[GhostVar, ITerm]

/**
* Each instrumentation operator is initially assigned one set of ghost
* variables. If `maxSupportedGhostVarRanges` is set to a value other than 1,
* if the result is `Inconclusive` the number of ghost variable sets is
* incremented by one, at most until `maxSupportedGhostVarRanges`.
* If a value other than `1` is specified,
* [[InstrumentationOperator.rewriteAggregate]] should handle the combining
* values coming from multiple sets of ghost variables. Other rewrite rules
* do not require special treatment, as they each rule will automatically be
* applied once per ghost variable set, per extended quantifier application.
* @todo This is currently ignored, refactor to consider it.
*/
val maxSupportedGhostVarRanges : Int = 1

/**
* Given a map from ghost variables to terms, this should construct a formula
* using those terms and [[exq.morphism]]. This is later used when
* back-translating solutions in [[GhostVariableAdder]] to eliminate
* ghost variables.
*/
def ghostVarsToAggregateFormula(ghostTerms : Map[GhostVar, ITerm]) : IFormula
}



Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023 Jesper Amilon, Zafer Esen, Philipp Ruemmer.
* Copyright (c) 2024 Jesper Amilon, Zafer Esen, Philipp Ruemmer.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -35,18 +35,25 @@ import lazabs.horn.preprocessor.HornPreprocessor
import HornPreprocessor.{Clauses, VerificationHints, _}
import Util._
import InstrumentingPreprocessor._
import lazabs.horn.extendedquantifiers.theories.AbstractExtendedQuantifier

class SimpleExtendedQuantifierInstrumenter(clauses : Clauses,
hints : VerificationHints,
frozenPredicates : Set[Predicate],
numGhostRanges : Int) {
/**
* A preprocessor that applies instrumentation operators to `clauses`.
* @param numGhostRanges There will be this many collections of
* [[InstrumentationOperator.ghostVars]] per
* instrumentation operator.
*/
class InstrumentationOperatorApplier(
clauses : Clauses,
hints : VerificationHints,
frozenPredicates : Set[Predicate],
extendedQuantifierToInstOp : Map[AbstractExtendedQuantifier, InstrumentationOperator],
numGhostRanges : Int) {
val exqApps = gatherExtQuans(clauses)
val exqs = exqApps.map(_.exTheory).toSet
private val clauseInstrumenters = (exqs.map(exq =>
(exq, new SimpleClauseInstrumenter(exq)))).toMap
private val instrumentingPreprocessor =
new InstrumentingPreprocessor(clauses, hints, frozenPredicates,
clauseInstrumenters, numGhostRanges)
extendedQuantifierToInstOp, numGhostRanges)
val (InstrumentationResult(instrumentedClauses, branchPredicates, searchSpace),
newHints, backTranslator) = instrumentingPreprocessor.process
}
Loading

0 comments on commit 22a712e

Please sign in to comment.