org.evosuite.symbolic.solver.cvc4.CVC4Solver Maven / Gradle / Ivy
The newest version!
/**
* Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite
* contributors
*
* This file is part of EvoSuite.
*
* EvoSuite is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* EvoSuite is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with EvoSuite. If not, see .
*/
package org.evosuite.symbolic.solver.cvc4;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.evosuite.Properties;
import org.evosuite.symbolic.expr.Constraint;
import org.evosuite.symbolic.expr.Variable;
import org.evosuite.symbolic.solver.SmtExprBuilder;
import org.evosuite.symbolic.solver.SolverEmptyQueryException;
import org.evosuite.symbolic.solver.SolverErrorException;
import org.evosuite.symbolic.solver.SolverParseException;
import org.evosuite.symbolic.solver.SolverResult;
import org.evosuite.symbolic.solver.SolverTimeoutException;
import org.evosuite.symbolic.solver.SubProcessSolver;
import org.evosuite.symbolic.solver.smt.SmtAssertion;
import org.evosuite.symbolic.solver.smt.SmtCheckSatQuery;
import org.evosuite.symbolic.solver.smt.SmtConstantDeclaration;
import org.evosuite.symbolic.solver.smt.SmtExpr;
import org.evosuite.symbolic.solver.smt.SmtFunctionDeclaration;
import org.evosuite.symbolic.solver.smt.SmtFunctionDefinition;
import org.evosuite.symbolic.solver.smt.SmtIntVariable;
import org.evosuite.symbolic.solver.smt.SmtOperation;
import org.evosuite.symbolic.solver.smt.SmtOperation.Operator;
import org.evosuite.symbolic.solver.smt.SmtOperatorCollector;
import org.evosuite.symbolic.solver.smt.SmtRealVariable;
import org.evosuite.symbolic.solver.smt.SmtStringVariable;
import org.evosuite.symbolic.solver.smt.SmtVariable;
import org.evosuite.symbolic.solver.smt.SmtVariableCollector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class CVC4Solver extends SubProcessSolver {
private boolean reWriteNonLinearConstraints = false;
/**
* If enabled the translation will approximate non-linear constraints with
* concrete values
*
* @param rewrite
*/
public void setRewriteNonLinearConstraints(boolean rewrite) {
reWriteNonLinearConstraints = rewrite;
}
static Logger logger = LoggerFactory.getLogger(CVC4Solver.class);
public CVC4Solver(boolean addMissingValues) {
super(addMissingValues);
}
public CVC4Solver() {
super();
}
@Override
public SolverResult solve(Collection> constraints) throws SolverTimeoutException,
SolverEmptyQueryException, SolverErrorException, SolverParseException, IOException {
if (Properties.CVC4_PATH == null) {
String errMsg = "Property CVC4_PATH should be setted in order to use the CVC4 Solver!";
logger.error(errMsg);
throw new IllegalStateException(errMsg);
}
// CVC4 has very little support for non-linear arithemtics
// In fact, it cannot even produce models for non-linear theories
if (!reWriteNonLinearConstraints && hasNonLinearConstraints(constraints)) {
logger.debug("Skipping query due to (unsupported) non-linear constraints");
throw new SolverEmptyQueryException("Skipping query due to (unsupported) non-linear constraints");
}
long cvcTimeout = Properties.DSE_CONSTRAINT_SOLVER_TIMEOUT_MILLIS;
Set> variables = new HashSet>();
for (Constraint> c : constraints) {
Set> c_variables = c.getVariables();
variables.addAll(c_variables);
}
SmtCheckSatQuery smtQuery = buildSmtCheckSatQuery(constraints);
if (smtQuery == null) {
logger.debug("No variables found during the creation of the SMT query.");
throw new SolverEmptyQueryException("No variables found during the creation of the SMT query.");
}
if (smtQuery.getAssertions().isEmpty()) {
Map emptySolution = new HashMap();
SolverResult emptySAT = SolverResult.newSAT(emptySolution);
return emptySAT;
}
CVC4QueryPrinter printer = new CVC4QueryPrinter();
String smtQueryStr = printer.print(smtQuery);
if (smtQueryStr == null) {
logger.debug("No variables found during constraint solving.");
throw new SolverEmptyQueryException("No variables found during constraint solving.");
}
logger.debug("CVC4 Query:");
logger.debug(smtQueryStr);
String cvc4Cmd = buildCVC4cmd(cvcTimeout);
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
try {
launchNewProcess(cvc4Cmd, smtQueryStr, (int) cvcTimeout, stdout);
String cvc4ResultStr = stdout.toString("UTF-8");
if (cvc4ResultStr.startsWith("unsat") && cvc4ResultStr.contains(
"(error \"Cannot get the current model unless immediately preceded by SAT/INVALID or UNKNOWN response.\")")) {
// UNSAT
SolverResult unsatResult = SolverResult.newUNSAT();
return unsatResult;
}
if (cvc4ResultStr.contains("error")) {
String errMsg = "An error occurred while executing CVC4!";
logger.error(errMsg);
throw new SolverErrorException(errMsg);
}
// parse solution
Map initialValues = getConcreteValues(variables);
CVC4ResultParser resultParser;
if (addMissingVariables()) {
resultParser = new CVC4ResultParser(initialValues);
} else {
resultParser = new CVC4ResultParser();
}
SolverResult solverResult = resultParser.parse(cvc4ResultStr);
if (solverResult.isSAT()) {
// check if the found solution is useful
boolean check = checkSAT(constraints, solverResult);
if (!check) {
logger.debug("CVC4 solution does not solve the original constraint system. ");
SolverResult unsatResult = SolverResult.newUNSAT();
return unsatResult;
}
}
return solverResult;
} catch (IOException e) {
if (e.getMessage().contains("Permission denied")) {
logger.error("No permissions for running CVC4 binary");
} else {
logger.error("IO Exception during launching of CVC4 command");
}
throw e;
}
}
private static SmtCheckSatQuery buildSmtCheckSatQuery(Collection> constraints) {
ConstraintToCVC4Visitor v = new ConstraintToCVC4Visitor(true);
SmtVariableCollector varCollector = new SmtVariableCollector();
SmtOperatorCollector funCollector = new SmtOperatorCollector();
List smtAssertions = new LinkedList();
for (Constraint> c : constraints) {
SmtExpr smtExpr = c.accept(v, null);
if (smtExpr != null) {
SmtAssertion smtAssertion = new SmtAssertion(smtExpr);
smtAssertions.add(smtAssertion);
smtExpr.accept(varCollector, null);
smtExpr.accept(funCollector, null);
}
}
Set variables = varCollector.getSmtVariables();
if (variables.isEmpty()) {
return null; // no variables, constraint system is trivial
}
List functionDefinitions = new LinkedList();
final boolean addCharToInt = funCollector.getOperators().contains(Operator.CHAR_TO_INT);
if (addCharToInt) {
String charToIntFunction = buildCharToIntFunction();
SmtFunctionDefinition funcDefinition = new SmtFunctionDefinition(charToIntFunction);
functionDefinitions.add(funcDefinition);
}
final boolean addIntToChar = funCollector.getOperators().contains(Operator.INT_TO_CHAR);
if (addIntToChar) {
String intToCharFunction = buildIntToCharFunction();
SmtFunctionDefinition funcDefinition = new SmtFunctionDefinition(intToCharFunction);
functionDefinitions.add(funcDefinition);
}
List functionDeclarations = new LinkedList();
for (SmtVariable var : variables) {
String varName = var.getName();
if (var instanceof SmtIntVariable) {
SmtFunctionDeclaration intVar = SmtExprBuilder.mkIntFunctionDeclaration(varName);
functionDeclarations.add(intVar);
} else if (var instanceof SmtRealVariable) {
SmtFunctionDeclaration realVar = SmtExprBuilder.mkRealFunctionDeclaration(varName);
functionDeclarations.add(realVar);
} else if (var instanceof SmtStringVariable) {
SmtFunctionDeclaration stringVar = SmtExprBuilder.mkStringFunctionDeclaration(varName);
functionDeclarations.add(stringVar);
} else {
throw new RuntimeException("Unknown variable type " + var.getClass().getCanonicalName());
}
}
SmtCheckSatQuery smtQuery = new SmtCheckSatQuery(new LinkedList(), functionDeclarations,
functionDefinitions, smtAssertions);
return smtQuery;
}
private static String buildCVC4cmd(long cvcTimeout) {
String cmd = Properties.CVC4_PATH;
cmd += " --rewrite-divk"; // rewrite-divk rewrites division (or
// modulus) by a constant value
cmd += " --lang smt"; // query language is SMT-LIB
cmd += " --finite-model-find";
/**
* Option --finite-model-find has two effects:
*
* 1. It extends the ground UF solver to
* "UF + finite cardinality constraints", as described in this paper:
* http://homepage.cs.uiowa.edu/~ajreynol/cav13.pdf This is used to
* minimize the size of the interpretation of uninterpreted sorts.
*
* 2. It modifies the quantifier instantiation heuristics, as described
* in this paper: http://homepage.cs.uiowa.edu/~ajreynol/cade24.pdf In
* particular, it disables E-matching and enables
* "model-based quantifier instantiation", which is the strategy that
* enables CVC4 to answer "SAT" in the presence of universally
* quantified formulas.
*
* More details on both of these points can be found in Sections 5.2 -
* 5.4 of http://homepage.cs.uiowa.edu/~ajreynol/thesis.pdf.
*/
cmd += " --tlimit=" + cvcTimeout; // set timeout to cvcTimeout
return cmd;
}
private static boolean hasNonLinearConstraints(Collection> constraints) {
NonLinearConstraintVisitor v = new NonLinearConstraintVisitor();
for (Constraint> constraint : constraints) {
Boolean ret_val = constraint.accept(v, null);
if (ret_val) {
return true;
}
}
return false;
}
// private final static int ASCII_TABLE_LENGTH = 256;
private final static int ASCII_TABLE_LENGTH = 256;
private static String buildIntToCharFunction() {
StringBuffer buff = new StringBuffer();
buff.append(SmtOperation.Operator.INT_TO_CHAR + "((!x Int)) String");
buff.append("\n");
for (int i = 0; i < ASCII_TABLE_LENGTH; i++) {
String hexStr;
if (i < 16) {
hexStr = "0" + Integer.toHexString(i);
} else {
hexStr = Integer.toHexString(i);
}
String escapedHexStr = "\\x" + hexStr;
if (i < ASCII_TABLE_LENGTH - 1) {
String iteStr = String.format("(ite (= !x %s) \"%s\"", i, escapedHexStr);
buff.append(iteStr);
buff.append("\n");
} else {
buff.append(String.format("\"%s\"", escapedHexStr));
}
}
for (int i = 0; i < ASCII_TABLE_LENGTH - 1; i++) {
buff.append(")");
}
return buff.toString();
}
private static String buildCharToIntFunction() {
StringBuffer buff = new StringBuffer();
buff.append(SmtOperation.Operator.CHAR_TO_INT + "((!x String)) Int");
buff.append("\n");
for (int i = 0; i < ASCII_TABLE_LENGTH; i++) {
String hexStr;
if (i < 16) {
hexStr = "0" + Integer.toHexString(i);
} else {
hexStr = Integer.toHexString(i);
}
String escapedHexStr = "\\x" + hexStr;
if (i < ASCII_TABLE_LENGTH - 1) {
String iteStr = String.format("(ite (= !x \"%s\") %s", escapedHexStr, i);
buff.append(iteStr);
buff.append("\n");
} else {
buff.append(i);
}
}
for (int i = 0; i < ASCII_TABLE_LENGTH - 1; i++) {
buff.append(")");
}
return buff.toString();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy