Skip to content

Commit b2469a8

Browse files
committed
Princess: improve visiting quantified formulas.
The user should never come across a situation without variables, and internally, we avoid this, too.
1 parent 4f4bad4 commit b2469a8

File tree

3 files changed

+58
-30
lines changed

3 files changed

+58
-30
lines changed

src/org/sosy_lab/java_smt/solvers/princess/PrincessFormulaCreator.java

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import static org.sosy_lab.java_smt.solvers.princess.PrincessEnvironment.toSeq;
1515
import static scala.collection.JavaConverters.asJava;
1616
import static scala.collection.JavaConverters.asJavaCollection;
17+
import static scala.collection.JavaConverters.asScala;
1718

1819
import ap.basetypes.IdealInt;
1920
import ap.parser.IAtom;
@@ -38,7 +39,6 @@
3839
import ap.parser.ITermITE;
3940
import ap.parser.ITimes;
4041
import ap.parser.IVariable;
41-
import ap.terfor.conjunctions.Quantifier;
4242
import ap.terfor.preds.Predicate;
4343
import ap.theories.arrays.ExtArray;
4444
import ap.theories.bitvectors.ModuloArithmetic;
@@ -53,11 +53,11 @@
5353
import com.google.common.collect.ImmutableSet;
5454
import com.google.common.collect.Table;
5555
import java.lang.reflect.InvocationTargetException;
56-
import java.util.ArrayList;
5756
import java.util.HashMap;
5857
import java.util.List;
5958
import java.util.Map;
6059
import java.util.Set;
60+
import org.sosy_lab.common.UniqueIdGenerator;
6161
import org.sosy_lab.common.rationals.Rational;
6262
import org.sosy_lab.java_smt.api.ArrayFormula;
6363
import org.sosy_lab.java_smt.api.BitvectorFormula;
@@ -66,6 +66,7 @@
6666
import org.sosy_lab.java_smt.api.FormulaType;
6767
import org.sosy_lab.java_smt.api.FunctionDeclarationKind;
6868
import org.sosy_lab.java_smt.api.NumeralFormula.IntegerFormula;
69+
import org.sosy_lab.java_smt.api.QuantifiedFormulaManager.Quantifier;
6970
import org.sosy_lab.java_smt.api.visitors.FormulaVisitor;
7071
import org.sosy_lab.java_smt.basicimpl.FormulaCreator;
7172
import org.sosy_lab.java_smt.basicimpl.FunctionDeclarationImpl;
@@ -184,6 +185,9 @@ class PrincessFormulaCreator
184185
*/
185186
private final Table<Sort, Sort, Sort> arraySortCache = HashBasedTable.create();
186187

188+
// Keeps track of the bound/free variables created when visiting quantified terms.
189+
private final UniqueIdGenerator boundVariablesId = new UniqueIdGenerator();
190+
187191
PrincessFormulaCreator(PrincessEnvironment pEnv) {
188192
super(
189193
pEnv,
@@ -398,19 +402,7 @@ public <R> R visit(FormulaVisitor<R> visitor, final Formula f, final IExpression
398402
return visitor.visitConstant(f, convertValue(input));
399403

400404
} else if (input instanceof IQuantified) {
401-
// this is a quantifier
402-
403-
BooleanFormula body = encapsulateBoolean(((IQuantified) input).subformula());
404-
return visitor.visitQuantifier(
405-
(BooleanFormula) f,
406-
((IQuantified) input).quan().equals(Quantifier.apply(true)) ? FORALL : EXISTS,
407-
408-
// Princess does not hold any metadata about bound variables,
409-
// so we can't get meaningful list here.
410-
// HOWEVER, passing this list to QuantifiedFormulaManager#mkQuantifier
411-
// works as expected.
412-
new ArrayList<>(),
413-
body);
405+
return visitQuantifier(visitor, (BooleanFormula) f, (IQuantified) input);
414406

415407
} else if (input instanceof IVariable) {
416408
// variable bound by a quantifier
@@ -539,6 +531,29 @@ public <R> R visit(FormulaVisitor<R> visitor, final Formula f, final IExpression
539531
getName(input), kind, argTypes.build(), getFormulaType(f), solverDeclaration));
540532
}
541533

534+
private <R> R visitQuantifier(FormulaVisitor<R> visitor, BooleanFormula f, IQuantified input) {
535+
Quantifier quantifier =
536+
input.quan().equals(ap.terfor.conjunctions.Quantifier.apply(true)) ? FORALL : EXISTS;
537+
IFormula body = input.subformula();
538+
539+
// Princess uses de-Bruijn indices, so we have index 0 here for the most outer quantified scope
540+
IVariable boundVariable = input.sort().boundVariable(0);
541+
String boundVariableName = "__JAVASMT__BOUND_VARIABLE_" + boundVariablesId.getFreshId();
542+
// Currently, Princess supports only non-boolean bound variables, so we can cast to ITerm.
543+
ITerm substitutionVariable = (ITerm) makeVariable(boundVariable.sort(), boundVariableName);
544+
545+
// substitute the bound variable with a new variable,
546+
// and un-shift the remaining de-Bruijn indices.
547+
IFormula substitutedBody =
548+
IFormula.subst(body, asScala(List.of(substitutionVariable)).toList(), -1);
549+
550+
return visitor.visitQuantifier(
551+
f,
552+
quantifier,
553+
List.of(encapsulateWithTypeOf(substitutionVariable)),
554+
encapsulateBoolean(substitutedBody));
555+
}
556+
542557
private boolean isBitvectorOperationWithAdditionalArgument(FunctionDeclarationKind kind) {
543558
switch (kind) {
544559
case BV_NOT:

src/org/sosy_lab/java_smt/solvers/princess/PrincessQuantifiedFormulaManager.java

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@
1414
import ap.parser.IConstant;
1515
import ap.parser.IExpression;
1616
import ap.parser.IFormula;
17-
import ap.parser.ISortedQuantified;
1817
import ap.terfor.ConstantTerm;
1918
import ap.terfor.conjunctions.Quantifier.ALL$;
2019
import ap.terfor.conjunctions.Quantifier.EX$;
2120
import ap.types.Sort;
22-
import java.util.ArrayList;
21+
import com.google.common.collect.Lists;
2322
import java.util.List;
2423
import org.sosy_lab.java_smt.api.SolverException;
2524
import org.sosy_lab.java_smt.basicimpl.AbstractQuantifiedFormulaManager;
@@ -40,24 +39,17 @@ class PrincessQuantifiedFormulaManager
4039

4140
@Override
4241
public IExpression mkQuantifier(Quantifier q, List<IExpression> vars, IExpression body) {
42+
checkArgument(!vars.isEmpty(), "Missing variables for quantifier '%s' and body '%s'.", q, body);
43+
4344
checkArgument(body instanceof IFormula);
4445
ap.terfor.conjunctions.Quantifier pq = (q == Quantifier.FORALL) ? ALL$.MODULE$ : EX$.MODULE$;
45-
if (vars.isEmpty()) {
4646

47-
// Body already contains bound variables.
48-
return new ISortedQuantified(pq, PrincessEnvironment.INTEGER_SORT, (IFormula) body);
49-
} else {
50-
// TODO: add support for boolean quantification!
51-
return IExpression.quanConsts(pq, asScala(toConstantTerm(vars)), (IFormula) body);
52-
}
47+
// TODO: add support for boolean quantification!
48+
return IExpression.quanConsts(pq, asScala(toConstantTerms(vars)), (IFormula) body);
5349
}
5450

55-
private List<ConstantTerm> toConstantTerm(List<IExpression> lst) {
56-
List<ConstantTerm> retVal = new ArrayList<>(lst.size());
57-
for (IExpression f : lst) {
58-
retVal.add(((IConstant) f).c());
59-
}
60-
return retVal;
51+
private List<ConstantTerm> toConstantTerms(List<IExpression> lst) {
52+
return Lists.transform(lst, f -> ((IConstant) f).c());
6153
}
6254

6355
@Override

src/org/sosy_lab/java_smt/test/FormulaClassifierTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,27 @@ public void test_LRA() {
136136
assertThat(classifier.toString()).isEqualTo("LRA");
137137
}
138138

139+
@Test
140+
public void test_LRA_2() {
141+
requireParser();
142+
requireRationals();
143+
requireQuantifiers();
144+
requireRationals();
145+
String query =
146+
NUMERAL_VARS
147+
+ "(assert (and "
148+
+ "(exists ((a Real) (b Real)) (= (+ y y) (+ a b))) "
149+
+ "(exists ((a Real) (b Real)) (= (+ y y) (- a b))) "
150+
+ "))";
151+
classifier.visit(mgr.parse(query));
152+
if (solverToUse() == Solvers.PRINCESS) {
153+
// Princess rewrites the formula and uses '-1' for the negation -> Integer arithmetic
154+
assertThat(classifier.toString()).isEqualTo("LIRA");
155+
} else {
156+
assertThat(classifier.toString()).isEqualTo("LRA");
157+
}
158+
}
159+
139160
@Test
140161
public void test_ABVIRA() {
141162
requireParser();

0 commit comments

Comments
 (0)