Skip to content

Commit d808f78

Browse files
authored
Merge pull request #396 from sosy-lab/371-better-performance-in-floatingpointmanagertest
Bitwuzla: Fix performance issues in FloatingPointFormulaManagerTest
2 parents e697ed6 + 467459e commit d808f78

9 files changed

+182
-93
lines changed

src/org/sosy_lab/java_smt/api/FunctionDeclarationKind.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,9 @@ public enum FunctionDeclarationKind {
231231
/** Division over floating points. */
232232
FP_DIV,
233233

234+
/** Remainder of the floating point division. */
235+
FP_REM,
236+
234237
/** Multiplication over floating points. */
235238
FP_MUL,
236239

src/org/sosy_lab/java_smt/solvers/bitwuzla/BitwuzlaFloatingPointManager.java

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public class BitwuzlaFloatingPointManager
2828
private final TermManager termManager;
2929
private final Term roundingMode;
3030

31+
// Keeps track of the temporary variables that are created for fp-to-bv casts
32+
private static int counter = 0;
33+
3134
protected BitwuzlaFloatingPointManager(
3235
BitwuzlaFormulaCreator pCreator, FloatingPointRoundingMode pFloatingPointRoundingMode) {
3336
super(pCreator);
@@ -200,20 +203,32 @@ protected Term fromIeeeBitvectorImpl(Term pNumber, FloatingPointType pTargetType
200203

201204
@Override
202205
protected Term toIeeeBitvectorImpl(Term pNumber) {
203-
// FIXME: We should use a reserved symbol for the fresh variables
204206
int sizeExp = pNumber.sort().fp_exp_size();
205207
int sizeSig = pNumber.sort().fp_sig_size();
206208

207209
Sort bvSort = termManager.mk_bv_sort(sizeExp + sizeSig);
208210

211+
// The following code creates a new variable that is returned as result.
212+
// Additionally, we track constraints about the equality of the new variable and the FP number,
213+
// which is added onto the prover stack whenever the new variable is used as assertion.
214+
215+
// TODO This internal implementation is a technical dept and should be removed.
216+
// The additional constraints are not transparent in all cases, e.g., when visiting a
217+
// formula, creating a model, or transferring the assertions onto another prover stack.
218+
// A better way would be a direct implementation of this in Bitwuzla, without interfering
219+
// with JavaSMT.
220+
209221
// Note that NaN is handled as a special case in this method. This is not strictly necessary,
210222
// but if we just use "fpTerm = to_fp(bvVar)" the NaN will be given a random payload (and
211223
// sign). Since NaN payloads are not preserved here anyway we might as well pick a canonical
212-
// representation.
213-
Term bvNaN =
214-
termManager.mk_bv_value(bvSort, "0" + "1".repeat(sizeExp + 1) + "0".repeat(sizeSig - 2));
215-
216-
Term bvVar = termManager.mk_const(bvSort, pNumber.symbol() + "_toIeeeBitvector");
224+
// representation, e.g., which is "0 11111111 10000000000000000000000" for single precision.
225+
String nanRepr = "0" + "1".repeat(sizeExp + 1) + "0".repeat(sizeSig - 2);
226+
Term bvNaN = termManager.mk_bv_value(bvSort, nanRepr);
227+
228+
// TODO creating our own utility variables might eb unexpected from the user.
229+
// We might need to exclude such variables in models and formula traversal.
230+
String newVariable = "__JAVASMT__CAST_FROM_BV_" + counter++;
231+
Term bvVar = termManager.mk_const(bvSort, newVariable);
217232
Term equal =
218233
termManager.mk_term(
219234
Kind.ITE,
@@ -224,7 +239,7 @@ protected Term toIeeeBitvectorImpl(Term pNumber) {
224239
termManager.mk_term(Kind.FP_TO_FP_FROM_BV, bvVar, sizeExp, sizeSig),
225240
pNumber));
226241

227-
bitwuzlaCreator.addVariableCast(equal);
242+
bitwuzlaCreator.addConstraintForVariable(newVariable, equal);
228243
return bvVar;
229244
}
230245

src/org/sosy_lab/java_smt/solvers/bitwuzla/BitwuzlaFormulaCreator.java

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,22 @@
99
package org.sosy_lab.java_smt.solvers.bitwuzla;
1010

1111
import static com.google.common.base.Preconditions.checkArgument;
12+
import static org.sosy_lab.common.collect.Collections3.transformedImmutableSetCopy;
1213

1314
import com.google.common.base.Preconditions;
1415
import com.google.common.collect.HashBasedTable;
1516
import com.google.common.collect.ImmutableList;
1617
import com.google.common.collect.Table;
1718
import java.math.BigInteger;
19+
import java.util.ArrayDeque;
1820
import java.util.ArrayList;
19-
import java.util.HashSet;
21+
import java.util.Collection;
22+
import java.util.Deque;
23+
import java.util.HashMap;
2024
import java.util.Iterator;
25+
import java.util.LinkedHashSet;
2126
import java.util.List;
27+
import java.util.Map;
2228
import java.util.Map.Entry;
2329
import java.util.Optional;
2430
import java.util.Set;
@@ -55,7 +61,21 @@ public class BitwuzlaFormulaCreator extends FormulaCreator<Term, Sort, Void, Bit
5561

5662
private final Table<String, Sort, Term> formulaCache = HashBasedTable.create();
5763

58-
private final Set<Term> variableCasts = new HashSet<>();
64+
/**
65+
* This mapping stores symbols and their constraints, such as from fp-to-bv casts with their
66+
* defining equation.
67+
*
68+
* <p>Bitwuzla does not support casts from floating-point to bitvector natively. The reason given
69+
* is that the value is undefined for NaN and that the SMT-LIB standard also does not include such
70+
* an operation. We try to work around this limitation by introducing a fresh variable <code>
71+
* __CAST_FROM_BV_XXX</code>for the result and then adding the constraint <code>
72+
* fp.to_fp(__CAST_FROM_BV_XXX) = &lt;float-term&gt;</code> as a side-condition. This is also what
73+
* is recommended by the SMT-LIB2 standard. The map <code>variableCasts</code> is used to store
74+
* these side-conditions so that they can later be added as assertions. The keys of the map are
75+
* the newly introduced variable symbols and the values are the defining equations as mentioned
76+
* above.
77+
*/
78+
private final Map<String, Term> constraintsForVariables = new HashMap<>();
5979

6080
protected BitwuzlaFormulaCreator(TermManager pTermManager) {
6181
super(null, pTermManager.mk_bool_sort(), null, null, null, null);
@@ -221,6 +241,8 @@ private FunctionDeclarationKind getDeclarationKind(Term term) {
221241
return FunctionDeclarationKind.BV_SGT;
222242
} else if (kind.equals(Kind.BV_SHL)) {
223243
return FunctionDeclarationKind.BV_SHL;
244+
} else if (kind.equals(Kind.BV_SHR)) {
245+
return FunctionDeclarationKind.BV_LSHR;
224246
} else if (kind.equals(Kind.BV_SLE)) {
225247
return FunctionDeclarationKind.BV_SLE;
226248
} else if (kind.equals(Kind.BV_SLT)) {
@@ -283,6 +305,8 @@ private FunctionDeclarationKind getDeclarationKind(Term term) {
283305
return FunctionDeclarationKind.FP_MIN;
284306
} else if (kind.equals(Kind.FP_MUL)) {
285307
return FunctionDeclarationKind.FP_MUL;
308+
} else if (kind.equals(Kind.FP_REM)) {
309+
return FunctionDeclarationKind.FP_REM;
286310
} else if (kind.equals(Kind.FP_NEG)) {
287311
return FunctionDeclarationKind.FP_NEG;
288312
} else if (kind.equals(Kind.FP_RTI)) {
@@ -572,11 +596,34 @@ public Object convertValue(Term term) {
572596
throw new AssertionError("Unknown value type.");
573597
}
574598

575-
public void addVariableCast(Term equal) {
576-
variableCasts.add(equal);
599+
/** Add a constraint that is pushed onto the prover stack whenever the variable is used. */
600+
public void addConstraintForVariable(String variable, Term constraint) {
601+
constraintsForVariables.put(variable, constraint);
577602
}
578603

579-
public Iterable<Term> getVariableCasts() {
580-
return variableCasts;
604+
/**
605+
* Returns a set of additional constraints (side-conditions) that are needed to use some variables
606+
* from the given term, such as utility variables from casts.
607+
*
608+
* <p>Bitwuzla does not support fp-to-bv conversion natively. We have to use side-conditions as a
609+
* workaround. When a term containing fp-to-bv casts is added to the assertion stack these
610+
* side-conditions need to be collected by calling this method and then also adding them to the
611+
* assertion stack.
612+
*/
613+
public Collection<Term> getConstraintsForTerm(Term pTerm) {
614+
final Set<String> usedConstraintVariables = new LinkedHashSet<>();
615+
final Deque<String> waitlist = new ArrayDeque<>(extractVariablesAndUFs(pTerm, false).keySet());
616+
while (!waitlist.isEmpty()) {
617+
String current = waitlist.pop();
618+
if (constraintsForVariables.containsKey(current)) { // ignore variables without constraints
619+
if (usedConstraintVariables.add(current)) {
620+
// if we found a new variable with constraint, get transitive variables from constraint
621+
Term constraint = constraintsForVariables.get(current);
622+
waitlist.addAll(extractVariablesAndUFs(constraint, false).keySet());
623+
}
624+
}
625+
}
626+
627+
return transformedImmutableSetCopy(usedConstraintVariables, constraintsForVariables::get);
581628
}
582629
}

src/org/sosy_lab/java_smt/solvers/bitwuzla/BitwuzlaFormulaManager.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,12 @@ public Appender dumpFormula(Term pTerm) {
186186
return new Appenders.AbstractAppender() {
187187
@Override
188188
public void appendTo(Appendable out) throws IOException {
189+
if (pTerm.is_value()) {
190+
out.append("(assert " + pTerm + ")");
191+
return;
192+
}
189193
Bitwuzla bitwuzla = new Bitwuzla(creator.getTermManager());
190-
for (Term t : creator.getVariableCasts()) {
194+
for (Term t : creator.getConstraintsForTerm(pTerm)) {
191195
bitwuzla.assert_formula(t);
192196
}
193197
bitwuzla.assert_formula(pTerm);

src/org/sosy_lab/java_smt/solvers/bitwuzla/BitwuzlaTheoremProver.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.google.common.collect.Collections2;
1313
import java.util.ArrayList;
1414
import java.util.Collection;
15+
import java.util.LinkedHashSet;
1516
import java.util.List;
1617
import java.util.Optional;
1718
import java.util.Set;
@@ -24,7 +25,6 @@
2425
import org.sosy_lab.java_smt.api.SolverException;
2526
import org.sosy_lab.java_smt.basicimpl.AbstractProverWithAllSat;
2627
import org.sosy_lab.java_smt.basicimpl.CachingModel;
27-
import org.sosy_lab.java_smt.solvers.bitwuzla.BitwuzlaFormula.BitwuzlaBooleanFormula;
2828
import org.sosy_lab.java_smt.solvers.bitwuzla.api.Bitwuzla;
2929
import org.sosy_lab.java_smt.solvers.bitwuzla.api.Option;
3030
import org.sosy_lab.java_smt.solvers.bitwuzla.api.Options;
@@ -93,7 +93,11 @@ public void popImpl() {
9393
@Override
9494
public @Nullable Void addConstraintImpl(BooleanFormula constraint) throws InterruptedException {
9595
wasLastSatCheckSat = false;
96-
env.assert_formula(((BitwuzlaBooleanFormula) constraint).getTerm());
96+
Term formula = creator.extractInfo(constraint);
97+
env.assert_formula(formula);
98+
for (Term t : creator.getConstraintsForTerm(formula)) {
99+
env.assert_formula(t);
100+
}
97101
return null;
98102
}
99103

@@ -127,7 +131,7 @@ private boolean readSATResult(Result resultValue) throws SolverException, Interr
127131
public boolean isUnsat() throws SolverException, InterruptedException {
128132
Preconditions.checkState(!closed);
129133
wasLastSatCheckSat = false;
130-
final Result result = env.check_sat(new Vector_Term(creator.getVariableCasts()));
134+
final Result result = env.check_sat();
131135
return readSATResult(result);
132136
}
133137

@@ -142,12 +146,15 @@ public boolean isUnsatWithAssumptions(Collection<BooleanFormula> assumptions)
142146
throws SolverException, InterruptedException {
143147
Preconditions.checkState(!closed);
144148
wasLastSatCheckSat = false;
145-
Vector_Term ass = new Vector_Term(creator.getVariableCasts());
149+
150+
Collection<Term> newAssumptions = new LinkedHashSet<>();
146151
for (BooleanFormula formula : assumptions) {
147-
BitwuzlaBooleanFormula bitwuzlaFormula = (BitwuzlaBooleanFormula) formula;
148-
ass.add(bitwuzlaFormula.getTerm());
152+
Term term = creator.extractInfo(formula);
153+
newAssumptions.add(term);
154+
newAssumptions.addAll(creator.getConstraintsForTerm(term));
149155
}
150-
final Result result = env.check_sat(ass);
156+
157+
final Result result = env.check_sat(new Vector_Term(newAssumptions));
151158
return readSATResult(result);
152159
}
153160

src/org/sosy_lab/java_smt/solvers/cvc4/CVC4FormulaCreator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,7 @@ private Expr normalize(Expr operator) {
489489
.put(Kind.FLOATINGPOINT_SUB, FunctionDeclarationKind.FP_SUB)
490490
.put(Kind.FLOATINGPOINT_MULT, FunctionDeclarationKind.FP_MUL)
491491
.put(Kind.FLOATINGPOINT_DIV, FunctionDeclarationKind.FP_DIV)
492+
.put(Kind.FLOATINGPOINT_REM, FunctionDeclarationKind.FP_REM)
492493
.put(Kind.FLOATINGPOINT_LT, FunctionDeclarationKind.FP_LT)
493494
.put(Kind.FLOATINGPOINT_LEQ, FunctionDeclarationKind.FP_LE)
494495
.put(Kind.FLOATINGPOINT_GT, FunctionDeclarationKind.FP_GT)

src/org/sosy_lab/java_smt/solvers/cvc5/CVC5FormulaCreator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,7 @@ private Term normalize(Term operator) {
602602
.put(Kind.FLOATINGPOINT_ADD, FunctionDeclarationKind.FP_ADD)
603603
.put(Kind.FLOATINGPOINT_SUB, FunctionDeclarationKind.FP_SUB)
604604
.put(Kind.FLOATINGPOINT_MULT, FunctionDeclarationKind.FP_MUL)
605+
.put(Kind.FLOATINGPOINT_REM, FunctionDeclarationKind.FP_REM)
605606
.put(Kind.FLOATINGPOINT_DIV, FunctionDeclarationKind.FP_DIV)
606607
.put(Kind.FLOATINGPOINT_NEG, FunctionDeclarationKind.FP_NEG)
607608
.put(Kind.FLOATINGPOINT_LT, FunctionDeclarationKind.FP_LT)

src/org/sosy_lab/java_smt/solvers/z3/Z3FormulaCreator.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,8 @@ private FunctionDeclarationKind getDeclarationKind(long f) {
734734
return FunctionDeclarationKind.FP_DIV;
735735
case Z3_OP_FPA_MUL:
736736
return FunctionDeclarationKind.FP_MUL;
737+
case Z3_OP_FPA_REM:
738+
return FunctionDeclarationKind.FP_REM;
737739
case Z3_OP_FPA_LT:
738740
return FunctionDeclarationKind.FP_LT;
739741
case Z3_OP_FPA_LE:

0 commit comments

Comments
 (0)