diff --git a/README.md b/README.md index 366b0bec62..0e9ed7e108 100644 --- a/README.md +++ b/README.md @@ -35,32 +35,32 @@ Currently, the following tools are available (follow the links for more informat Theta can be divided into the following four layers. * **Formalisms**: The core elements of Theta are the formalisms, which represent models of real life systems (e.g., software, hardware, protocols). -Formalisms are usually low level, mathematical representations based on first order logic expressions and graph like structures. -Formalisms can also support higher level languages that can be mapped to that particular formalism by a language front-end (consisting of a specific parser and possibly reductions for simplification of the model). -The common features of the different formalisms reside in the [`core`](subprojects/common/core) project (e.g., expressions and statements) and each formalism has its own project. -Currently, the following formalisms are supported: (extended) symbolic transition systems ([`sts`](subprojects/sts/sts) / [`xsts`](subprojects/xsts/xsts)), control-flow automata ([`cfa`](subprojects/cfa/cfa)) and timed automata ([`xta`](subprojects/xta/xta)). + Formalisms are usually low level, mathematical representations based on first order logic expressions and graph like structures. + Formalisms can also support higher level languages that can be mapped to that particular formalism by a language front-end (consisting of a specific parser and possibly reductions for simplification of the model). + The common features of the different formalisms reside in the [`core`](subprojects/common/core) project (e.g., expressions and statements) and each formalism has its own project. + Currently, the following formalisms are supported: (extended) symbolic transition systems ([`sts`](subprojects/sts/sts) / [`xsts`](subprojects/xsts/xsts)), control-flow automata ([`cfa`](subprojects/cfa/cfa)) and timed automata ([`xta`](subprojects/xta/xta)). * **Analysis back-end**: The analysis back-end provides the verification algorithms that can formally prove whether a model meets certain requirements. -There is an interpreter for each formalism, providing a common interface towards the algorithms (e.g., calculating initial states and successors). -This ensures that most components of the algorithms work for all formalisms (as long as they provide the interpreter). -The verification algorithms are mostly based on abstraction. -The analysis back-end defines various abstract domains (e.g., predicates, explicit values, zones), strategies for performing abstraction and refinement (e.g., interpolation), and algorithms built from these components. -The common components reside in the [`analysis`](subprojects/common/analysis) project (e.g., CEGAR loop) and the formalism-specific modules (e.g., the interpreters) are implemented in separate analysis projects (with suffix `-analysis`) for each formalism. + There is an interpreter for each formalism, providing a common interface towards the algorithms (e.g., calculating initial states and successors). + This ensures that most components of the algorithms work for all formalisms (as long as they provide the interpreter). + The verification algorithms are mostly based on abstraction. + The analysis back-end defines various abstract domains (e.g., predicates, explicit values, zones), strategies for performing abstraction and refinement (e.g., interpolation), and algorithms built from these components. + The common components reside in the [`analysis`](subprojects/common/analysis) project (e.g., CEGAR loop) and the formalism-specific modules (e.g., the interpreters) are implemented in separate analysis projects (with suffix `-analysis`) for each formalism. * **SMT solver interface and SMT solvers**: Many components of the algorithms rely on satisfiability modulo theories (SMT) solvers. -The framework provides a general SMT solver interface in the project [`solver`](subprojects/common/solver) that supports incremental solving, unsat cores, and the generation of binary and sequence interpolants. -Currently, the interface is implemented by the [Z3](https://github.com/Z3Prover/z3) SMT solver in the project [`solver-z3`](subprojects/common/solver-z3), but it can easily be extended with new solvers. + The framework provides a general SMT solver interface in the project [`solver`](subprojects/common/solver) that supports incremental solving, unsat cores, and the generation of binary and sequence interpolants. + Currently, the interface is implemented by the [Z3](https://github.com/Z3Prover/z3) SMT solver in the project [`solver-z3`](subprojects/common/solver-z3), but it can easily be extended with new solvers. * **Tools**: Tools are command line applications that can be compiled into a runnable jar file. -Tools usually read some input and then instantiate and run the algorithms. -Tools are implemented in separate projects, currently with the `-cli` suffix. + Tools usually read some input and then instantiate and run the algorithms. + Tools are implemented in separate projects, currently with the `-cli` suffix. The table below shows the architecture and the projects. Each project contains a README.md in its root directory describing its purpose in more detail. | | Common | CFA | STS | XTA | XSTS | |--|--|--|--|--|--| -| **Tools** | | [`cfa-cli`](subprojects/cfa/cfa-cli) | [`sts-cli`](subprojects/sts/sts-cli) | [`xta-cli`](subprojects/xta/xta-cli) | [`xsts-cli`](subprojects/xsts/xsts-cli) | +| **Tools** | [`solver-smtlib-cli`](subprojects/solver/solver-smtlib-cli) | [`cfa-cli`](subprojects/cfa/cfa-cli) | [`sts-cli`](subprojects/sts/sts-cli) | [`xta-cli`](subprojects/xta/xta-cli) | [`xsts-cli`](subprojects/xsts/xsts-cli) | | **Analyses** | [`analysis`](subprojects/common/analysis) | [`cfa-analysis`](subprojects/cfa/cfa-analysis) | [`sts-analysis`](subprojects/sts/sts-analysis) | [`xta-analysis`](subprojects/xta/xta-analysis) | [`xsts-analysis`](subprojects/xsts/xsts-analysis) | | **Formalisms** | [`core`](subprojects/common/core), [`common`](subprojects/common/common) | [`cfa`](subprojects/cfa/cfa) | [`sts`](subprojects/sts/sts) | [`xta`](subprojects/xta/xta) | [`xsts`](subprojects/xsts/xsts) | -| **SMT solvers** | [`solver`](subprojects/common/solver), [`solver-z3`](subprojects/common/solver-z3) | +| **SMT solvers** | [`solver`](subprojects/solver/solver), [`solver-z3`](subprojects/solver/solver-z3), [`solver-smtlib`](subprojects/solver/solver-smtlib) | ## Extend Theta diff --git a/build.gradle.kts b/build.gradle.kts index 3dc1baea67..282a38fe25 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,7 +10,7 @@ buildscript { allprojects { group = "hu.bme.mit.inf.theta" - version = "2.23.0" + version = "3.0.0" apply(from = rootDir.resolve("gradle/shared-with-buildSrc/mirrors.gradle.kts")) } diff --git a/doc/CEGAR-algorithms.md b/doc/CEGAR-algorithms.md index 3adb2d5e04..478946b09d 100644 --- a/doc/CEGAR-algorithms.md +++ b/doc/CEGAR-algorithms.md @@ -141,3 +141,12 @@ The pruning strategy controls which portion of the abstract state space is disca * `LAZY`: The ARG is only pruned back to the first point where refinement was applied. (See [Lazy abstraction](https://dl.acm.org/doi/10.1145/565816.503279).) It is recommended to first try `LAZY` and fall back to `FULL` if there is no refinement progress (seemingly infinite iterations with the same counterexample). + +### `--solver`, `--abstraction-solver`, `--refinement-solver` + +Available for CFA. The SMT-solver to use during verification. The `--abstraction-solver` specifies the solver to use during the abstraction phase, while `--refinement-solver` specifies the solver to use during the refinement phase. The option `--solver` sets both the abstraction and the refinement solver to be the same. Possible values: + +* `Z3`: The native integration of Microsoft's Z3 solver. (See subproject [solver-z3](../subprojects/solver/solver-z3) for more details.) +* `:`: An installed SMT-LIB solver. (See subprojects [solver-smtlib](../subprojects/solver/solver-smtlib) and [solver-smtlib-cli](../subprojects/solver/solver-smtlib-cli) for more details.) + +It is recommended to stick with the default `Z3` option at first, and only use the SMT-LIB based solvers, if some required features are not supported by `Z3` (e.g. interpolating with bitvectors, floating points). \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 856b94e54f..04f191d083 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,8 +4,6 @@ include( "common/analysis", "common/common", "common/core", - "common/solver", - "common/solver-z3", "cfa/cfa", "cfa/cfa-analysis", @@ -21,7 +19,12 @@ include( "xsts/xsts", "xsts/xsts-analysis", - "xsts/xsts-cli" + "xsts/xsts-cli", + + "solver/solver", + "solver/solver-z3", + "solver/solver-smtlib", + "solver/solver-smtlib-cli" ) for (project in rootProject.children) { diff --git a/subprojects/cfa/cfa-analysis/build.gradle.kts b/subprojects/cfa/cfa-analysis/build.gradle.kts index fbfc58a323..6ba5f600e6 100644 --- a/subprojects/cfa/cfa-analysis/build.gradle.kts +++ b/subprojects/cfa/cfa-analysis/build.gradle.kts @@ -8,4 +8,5 @@ dependencies { compile(project(":theta-common")) compile(project(":theta-core")) testImplementation(project(":theta-solver-z3")) + testImplementation(project(":theta-solver-smtlib")) } diff --git a/subprojects/cfa/cfa-analysis/src/main/java/hu/bme/mit/theta/cfa/analysis/config/CfaConfigBuilder.java b/subprojects/cfa/cfa-analysis/src/main/java/hu/bme/mit/theta/cfa/analysis/config/CfaConfigBuilder.java index 08ead09daf..7a3d402a47 100644 --- a/subprojects/cfa/cfa-analysis/src/main/java/hu/bme/mit/theta/cfa/analysis/config/CfaConfigBuilder.java +++ b/subprojects/cfa/cfa-analysis/src/main/java/hu/bme/mit/theta/cfa/analysis/config/CfaConfigBuilder.java @@ -15,8 +15,6 @@ */ package hu.bme.mit.theta.cfa.analysis.config; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.True; - import hu.bme.mit.theta.analysis.Action; import hu.bme.mit.theta.analysis.Analysis; import hu.bme.mit.theta.analysis.Prec; @@ -36,7 +34,20 @@ import hu.bme.mit.theta.analysis.expl.ItpRefToExplPrec; import hu.bme.mit.theta.analysis.expl.VarsRefToExplPrec; import hu.bme.mit.theta.analysis.expr.ExprState; -import hu.bme.mit.theta.analysis.expr.refinement.*; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceBwBinItpChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceFwBinItpChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceNewtonChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceSeqItpChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceUCBChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceUnsatCoreChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ItpRefutation; +import hu.bme.mit.theta.analysis.expr.refinement.MultiExprTraceRefiner; +import hu.bme.mit.theta.analysis.expr.refinement.PrecRefiner; +import hu.bme.mit.theta.analysis.expr.refinement.PruneStrategy; +import hu.bme.mit.theta.analysis.expr.refinement.Refutation; +import hu.bme.mit.theta.analysis.expr.refinement.RefutationToPrec; +import hu.bme.mit.theta.analysis.expr.refinement.SingleExprTraceRefiner; import hu.bme.mit.theta.analysis.pred.ExprSplitters; import hu.bme.mit.theta.analysis.pred.ExprSplitters.ExprSplitter; import hu.bme.mit.theta.analysis.pred.ItpRefToPredPrec; @@ -47,7 +58,12 @@ import hu.bme.mit.theta.analysis.pred.PredState; import hu.bme.mit.theta.analysis.waitlist.PriorityWaitlist; import hu.bme.mit.theta.cfa.CFA; -import hu.bme.mit.theta.cfa.analysis.*; +import hu.bme.mit.theta.cfa.analysis.CfaAction; +import hu.bme.mit.theta.cfa.analysis.CfaAnalysis; +import hu.bme.mit.theta.cfa.analysis.CfaInitPrecs; +import hu.bme.mit.theta.cfa.analysis.CfaPrec; +import hu.bme.mit.theta.cfa.analysis.CfaState; +import hu.bme.mit.theta.cfa.analysis.DistToErrComparator; import hu.bme.mit.theta.cfa.analysis.lts.CfaCachedLts; import hu.bme.mit.theta.cfa.analysis.lts.CfaLbeLts; import hu.bme.mit.theta.cfa.analysis.lts.CfaLts; @@ -58,9 +74,11 @@ import hu.bme.mit.theta.cfa.analysis.prec.LocalCfaPrecRefiner; import hu.bme.mit.theta.common.logging.Logger; import hu.bme.mit.theta.common.logging.NullLogger; -import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.SolverFactory; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.True; + public class CfaConfigBuilder { public enum Domain { EXPL, PRED_BOOL, PRED_CART, PRED_SPLIT @@ -120,7 +138,7 @@ public

CfaPrec

createPrec(final P innerPrec) { @Override public PrecRefiner, A, CfaPrec

, R> createRefiner( - final RefutationToPrec refToPrec) { + final RefutationToPrec refToPrec) { return GlobalCfaPrecRefiner.create(refToPrec); } }, @@ -133,7 +151,7 @@ public

CfaPrec

createPrec(final P innerPrec) { @Override public PrecRefiner, A, CfaPrec

, R> createRefiner( - final RefutationToPrec refToPrec) { + final RefutationToPrec refToPrec) { return LocalCfaPrecRefiner.create(refToPrec); } }; @@ -141,7 +159,7 @@ public CfaPrec

createPrec(P innerPrec); public abstract PrecRefiner, A, CfaPrec

, R> createRefiner( - RefutationToPrec refToPrec); + RefutationToPrec refToPrec); } public enum Encoding { @@ -167,7 +185,8 @@ public enum InitPrec { } private Logger logger = NullLogger.getInstance(); - private final SolverFactory solverFactory; + private final SolverFactory abstractionSolverFactory; + private final SolverFactory refinementSolverFactory; private final Domain domain; private final Refinement refinement; private Search search = Search.BFS; @@ -181,7 +200,15 @@ public enum InitPrec { public CfaConfigBuilder(final Domain domain, final Refinement refinement, final SolverFactory solverFactory) { this.domain = domain; this.refinement = refinement; - this.solverFactory = solverFactory; + this.abstractionSolverFactory = solverFactory; + this.refinementSolverFactory = solverFactory; + } + + public CfaConfigBuilder(final Domain domain, final Refinement refinement, final SolverFactory abstractionSolverFactory, final SolverFactory refinementSolverFactory) { + this.domain = domain; + this.refinement = refinement; + this.abstractionSolverFactory = abstractionSolverFactory; + this.refinementSolverFactory = refinementSolverFactory; } public CfaConfigBuilder logger(final Logger logger) { @@ -225,50 +252,49 @@ public CfaConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { } public CfaConfig build(final CFA cfa, final CFA.Loc errLoc) { - final ItpSolver solver = solverFactory.createItpSolver(); final CfaLts lts = encoding.getLts(errLoc); if (domain == Domain.EXPL) { final Analysis, CfaAction, CfaPrec> analysis = CfaAnalysis - .create(cfa.getInitLoc(), ExplStmtAnalysis.create(solver, True(), maxEnum)); + .create(cfa.getInitLoc(), ExplStmtAnalysis.create(abstractionSolverFactory.createSolver(), True(), maxEnum)); final ArgBuilder, CfaAction, CfaPrec> argBuilder = ArgBuilder.create(lts, - analysis, s -> s.getLoc().equals(errLoc), true); + analysis, s -> s.getLoc().equals(errLoc), true); final Abstractor, CfaAction, CfaPrec> abstractor = BasicAbstractor - .builder(argBuilder).projection(CfaState::getLoc) - .waitlist(PriorityWaitlist.create(search.getComp(cfa, errLoc))) - .stopCriterion(refinement == Refinement.MULTI_SEQ ? StopCriterions.fullExploration() - : StopCriterions.firstCex()).logger(logger).build(); + .builder(argBuilder).projection(CfaState::getLoc) + .waitlist(PriorityWaitlist.create(search.getComp(cfa, errLoc))) + .stopCriterion(refinement == Refinement.MULTI_SEQ ? StopCriterions.fullExploration() + : StopCriterions.firstCex()).logger(logger).build(); Refiner, CfaAction, CfaPrec> refiner; switch (refinement) { case FW_BIN_ITP: - refiner = SingleExprTraceRefiner.create(ExprTraceFwBinItpChecker.create(True(), True(), solver), - precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger); + refiner = SingleExprTraceRefiner.create(ExprTraceFwBinItpChecker.create(True(), True(), refinementSolverFactory.createItpSolver()), + precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger); break; case BW_BIN_ITP: - refiner = SingleExprTraceRefiner.create(ExprTraceBwBinItpChecker.create(True(), True(), solver), - precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger); + refiner = SingleExprTraceRefiner.create(ExprTraceBwBinItpChecker.create(True(), True(), refinementSolverFactory.createItpSolver()), + precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger); break; case SEQ_ITP: - refiner = SingleExprTraceRefiner.create(ExprTraceSeqItpChecker.create(True(), True(), solver), - precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger); + refiner = SingleExprTraceRefiner.create(ExprTraceSeqItpChecker.create(True(), True(), refinementSolverFactory.createItpSolver()), + precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger); break; case MULTI_SEQ: - refiner = MultiExprTraceRefiner.create(ExprTraceSeqItpChecker.create(True(), True(), solver), - precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger); + refiner = MultiExprTraceRefiner.create(ExprTraceSeqItpChecker.create(True(), True(), refinementSolverFactory.createItpSolver()), + precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger); break; case UNSAT_CORE: - refiner = SingleExprTraceRefiner.create(ExprTraceUnsatCoreChecker.create(True(), True(), solver), - precGranularity.createRefiner(new VarsRefToExplPrec()), pruneStrategy, logger); + refiner = SingleExprTraceRefiner.create(ExprTraceUnsatCoreChecker.create(True(), True(), refinementSolverFactory.createUCSolver()), + precGranularity.createRefiner(new VarsRefToExplPrec()), pruneStrategy, logger); break; case UCB: - refiner = SingleExprTraceRefiner.create(ExprTraceUCBChecker.create(True(), True(), solver), - precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger); + refiner = SingleExprTraceRefiner.create(ExprTraceUCBChecker.create(True(), True(), refinementSolverFactory.createUCSolver()), + precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger); break; case NWT_SP: refiner = SingleExprTraceRefiner.create( - ExprTraceNewtonChecker.create(True(), True(), solver).withoutIT().withSP().withoutLV(), + ExprTraceNewtonChecker.create(True(), True(), refinementSolverFactory.createUCSolver()).withoutIT().withSP().withoutLV(), precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger @@ -276,7 +302,7 @@ public CfaConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { break; case NWT_WP: refiner = SingleExprTraceRefiner.create( - ExprTraceNewtonChecker.create(True(), True(), solver).withoutIT().withWP().withoutLV(), + ExprTraceNewtonChecker.create(True(), True(), refinementSolverFactory.createUCSolver()).withoutIT().withWP().withoutLV(), precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger @@ -284,7 +310,7 @@ public CfaConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { break; case NWT_SP_LV: refiner = SingleExprTraceRefiner.create( - ExprTraceNewtonChecker.create(True(), True(), solver).withoutIT().withSP().withLV(), + ExprTraceNewtonChecker.create(True(), True(), refinementSolverFactory.createUCSolver()).withoutIT().withSP().withLV(), precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger @@ -292,7 +318,7 @@ public CfaConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { break; case NWT_WP_LV: refiner = SingleExprTraceRefiner.create( - ExprTraceNewtonChecker.create(True(), True(), solver).withoutIT().withWP().withLV(), + ExprTraceNewtonChecker.create(True(), True(), refinementSolverFactory.createUCSolver()).withoutIT().withWP().withLV(), precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger @@ -300,7 +326,7 @@ public CfaConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { break; case NWT_IT_SP: refiner = SingleExprTraceRefiner.create( - ExprTraceNewtonChecker.create(True(), True(), solver).withIT().withSP().withoutLV(), + ExprTraceNewtonChecker.create(True(), True(), refinementSolverFactory.createUCSolver()).withIT().withSP().withoutLV(), precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger @@ -308,7 +334,7 @@ public CfaConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { break; case NWT_IT_WP: refiner = SingleExprTraceRefiner.create( - ExprTraceNewtonChecker.create(True(), True(), solver).withIT().withWP().withoutLV(), + ExprTraceNewtonChecker.create(True(), True(), refinementSolverFactory.createUCSolver()).withIT().withWP().withoutLV(), precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger @@ -316,7 +342,7 @@ public CfaConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { break; case NWT_IT_SP_LV: refiner = SingleExprTraceRefiner.create( - ExprTraceNewtonChecker.create(True(), True(), solver).withIT().withSP().withLV(), + ExprTraceNewtonChecker.create(True(), True(), refinementSolverFactory.createUCSolver()).withIT().withSP().withLV(), precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger @@ -324,7 +350,7 @@ public CfaConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { break; case NWT_IT_WP_LV: refiner = SingleExprTraceRefiner.create( - ExprTraceNewtonChecker.create(True(), True(), solver).withIT().withWP().withLV(), + ExprTraceNewtonChecker.create(True(), True(), refinementSolverFactory.createUCSolver()).withIT().withWP().withLV(), precGranularity.createRefiner(new ItpRefToExplPrec()), pruneStrategy, logger @@ -332,11 +358,11 @@ public CfaConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { break; default: throw new UnsupportedOperationException( - domain + " domain does not support " + refinement + " refinement."); + domain + " domain does not support " + refinement + " refinement."); } final SafetyChecker, CfaAction, CfaPrec> checker = CegarChecker - .create(abstractor, refiner, logger); + .create(abstractor, refiner, logger); CfaPrec prec; @@ -349,94 +375,95 @@ public CfaConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { break; default: throw new UnsupportedOperationException(initPrec + " initial precision is not supported with " + - domain + " domain"); + domain + " domain"); } return CfaConfig.create(checker, prec); } else if (domain == Domain.PRED_BOOL || domain == Domain.PRED_CART || domain == Domain.PRED_SPLIT) { + final Solver analysisSolver = abstractionSolverFactory.createSolver(); PredAbstractor predAbstractor; switch (domain) { case PRED_BOOL: - predAbstractor = PredAbstractors.booleanAbstractor(solver); + predAbstractor = PredAbstractors.booleanAbstractor(analysisSolver); break; case PRED_SPLIT: - predAbstractor = PredAbstractors.booleanSplitAbstractor(solver); + predAbstractor = PredAbstractors.booleanSplitAbstractor(analysisSolver); break; case PRED_CART: - predAbstractor = PredAbstractors.cartesianAbstractor(solver); + predAbstractor = PredAbstractors.cartesianAbstractor(analysisSolver); break; default: throw new UnsupportedOperationException(domain + " domain is not supported."); } final Analysis, CfaAction, CfaPrec> analysis = CfaAnalysis - .create(cfa.getInitLoc(), PredAnalysis.create(solver, predAbstractor, True())); + .create(cfa.getInitLoc(), PredAnalysis.create(analysisSolver, predAbstractor, True())); final ArgBuilder, CfaAction, CfaPrec> argBuilder = ArgBuilder.create(lts, - analysis, s -> s.getLoc().equals(errLoc), true); + analysis, s -> s.getLoc().equals(errLoc), true); final Abstractor, CfaAction, CfaPrec> abstractor = BasicAbstractor - .builder(argBuilder).projection(CfaState::getLoc) - .waitlist(PriorityWaitlist.create(search.getComp(cfa, errLoc))) - .stopCriterion(refinement == Refinement.MULTI_SEQ ? StopCriterions.fullExploration() - : StopCriterions.firstCex()).logger(logger).build(); + .builder(argBuilder).projection(CfaState::getLoc) + .waitlist(PriorityWaitlist.create(search.getComp(cfa, errLoc))) + .stopCriterion(refinement == Refinement.MULTI_SEQ ? StopCriterions.fullExploration() + : StopCriterions.firstCex()).logger(logger).build(); ExprTraceChecker exprTraceChecker; switch (refinement) { case FW_BIN_ITP: - exprTraceChecker = ExprTraceFwBinItpChecker.create(True(), True(), solver); + exprTraceChecker = ExprTraceFwBinItpChecker.create(True(), True(), refinementSolverFactory.createItpSolver()); break; case BW_BIN_ITP: - exprTraceChecker = ExprTraceBwBinItpChecker.create(True(), True(), solver); + exprTraceChecker = ExprTraceBwBinItpChecker.create(True(), True(), refinementSolverFactory.createItpSolver()); break; case SEQ_ITP: - exprTraceChecker = ExprTraceSeqItpChecker.create(True(), True(), solver); + exprTraceChecker = ExprTraceSeqItpChecker.create(True(), True(), refinementSolverFactory.createItpSolver()); break; case MULTI_SEQ: - exprTraceChecker = ExprTraceSeqItpChecker.create(True(), True(), solver); + exprTraceChecker = ExprTraceSeqItpChecker.create(True(), True(), refinementSolverFactory.createItpSolver()); break; case UCB: - exprTraceChecker = ExprTraceUCBChecker.create(True(), True(), solver); + exprTraceChecker = ExprTraceUCBChecker.create(True(), True(), refinementSolverFactory.createUCSolver()); break; case NWT_SP: - exprTraceChecker = ExprTraceNewtonChecker.create(True(), True(), solver).withoutIT().withSP().withoutLV(); + exprTraceChecker = ExprTraceNewtonChecker.create(True(), True(), refinementSolverFactory.createUCSolver()).withoutIT().withSP().withoutLV(); break; case NWT_WP: - exprTraceChecker = ExprTraceNewtonChecker.create(True(), True(), solver).withoutIT().withWP().withoutLV(); + exprTraceChecker = ExprTraceNewtonChecker.create(True(), True(), refinementSolverFactory.createUCSolver()).withoutIT().withWP().withoutLV(); break; case NWT_SP_LV: - exprTraceChecker = ExprTraceNewtonChecker.create(True(), True(), solver).withoutIT().withSP().withLV(); + exprTraceChecker = ExprTraceNewtonChecker.create(True(), True(), refinementSolverFactory.createUCSolver()).withoutIT().withSP().withLV(); break; case NWT_WP_LV: - exprTraceChecker = ExprTraceNewtonChecker.create(True(), True(), solver).withoutIT().withWP().withLV(); + exprTraceChecker = ExprTraceNewtonChecker.create(True(), True(), refinementSolverFactory.createUCSolver()).withoutIT().withWP().withLV(); break; case NWT_IT_SP: - exprTraceChecker = ExprTraceNewtonChecker.create(True(), True(), solver).withIT().withSP().withoutLV(); + exprTraceChecker = ExprTraceNewtonChecker.create(True(), True(), refinementSolverFactory.createUCSolver()).withIT().withSP().withoutLV(); break; case NWT_IT_WP: - exprTraceChecker = ExprTraceNewtonChecker.create(True(), True(), solver).withIT().withWP().withoutLV(); + exprTraceChecker = ExprTraceNewtonChecker.create(True(), True(), refinementSolverFactory.createUCSolver()).withIT().withWP().withoutLV(); break; case NWT_IT_SP_LV: - exprTraceChecker = ExprTraceNewtonChecker.create(True(), True(), solver).withIT().withSP().withLV(); + exprTraceChecker = ExprTraceNewtonChecker.create(True(), True(), refinementSolverFactory.createUCSolver()).withIT().withSP().withLV(); break; case NWT_IT_WP_LV: - exprTraceChecker = ExprTraceNewtonChecker.create(True(), True(), solver).withIT().withWP().withLV(); + exprTraceChecker = ExprTraceNewtonChecker.create(True(), True(), refinementSolverFactory.createUCSolver()).withIT().withWP().withLV(); break; default: throw new UnsupportedOperationException( - domain + " domain does not support " + refinement + " refinement."); + domain + " domain does not support " + refinement + " refinement."); } final ItpRefToPredPrec refToPrec = new ItpRefToPredPrec(predSplit.splitter); Refiner, CfaAction, CfaPrec> refiner; if (refinement == Refinement.MULTI_SEQ) { refiner = MultiExprTraceRefiner.create(exprTraceChecker, - precGranularity.createRefiner(refToPrec), pruneStrategy, logger); + precGranularity.createRefiner(refToPrec), pruneStrategy, logger); } else { refiner = SingleExprTraceRefiner.create(exprTraceChecker, - precGranularity.createRefiner(refToPrec), pruneStrategy, logger); + precGranularity.createRefiner(refToPrec), pruneStrategy, logger); } final SafetyChecker, CfaAction, CfaPrec> checker = CegarChecker - .create(abstractor, refiner, logger); + .create(abstractor, refiner, logger); CfaPrec prec; @@ -454,12 +481,12 @@ public CfaConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { break; default: throw new UnsupportedOperationException(precGranularity + - " precision granularity is not supported with " + domain + " domain"); + " precision granularity is not supported with " + domain + " domain"); } break; default: throw new UnsupportedOperationException(initPrec + " initial precision is not supported with " + - domain + " domain"); + domain + " domain"); } return CfaConfig.create(checker, prec); @@ -468,4 +495,4 @@ public CfaConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { throw new UnsupportedOperationException(domain + " domain is not supported."); } } -} +} \ No newline at end of file diff --git a/subprojects/cfa/cfa-analysis/src/main/java/hu/bme/mit/theta/cfa/analysis/impact/PredImpactChecker.java b/subprojects/cfa/cfa-analysis/src/main/java/hu/bme/mit/theta/cfa/analysis/impact/PredImpactChecker.java index d4fee50573..88b2114529 100644 --- a/subprojects/cfa/cfa-analysis/src/main/java/hu/bme/mit/theta/cfa/analysis/impact/PredImpactChecker.java +++ b/subprojects/cfa/cfa-analysis/src/main/java/hu/bme/mit/theta/cfa/analysis/impact/PredImpactChecker.java @@ -40,19 +40,22 @@ import hu.bme.mit.theta.cfa.analysis.CfaState; import hu.bme.mit.theta.cfa.analysis.prec.GlobalCfaPrec; import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.Solver; public final class PredImpactChecker implements SafetyChecker, CfaAction, UnitPrec> { private final ImpactChecker, CfaAction, UnitPrec> checker; private PredImpactChecker(final LTS, ? extends CfaAction> lts, final Loc initLoc, - final Predicate targetLocs, final ItpSolver solver) { + final Predicate targetLocs, + final Solver abstractionSolver, final ItpSolver refinementSolver) { checkNotNull(lts); checkNotNull(initLoc); - checkNotNull(solver); + checkNotNull(abstractionSolver); + checkNotNull(refinementSolver); - final Analysis predAnalysis = PredAnalysis.create(solver, - PredAbstractors.booleanSplitAbstractor(solver), True()); + final Analysis predAnalysis = PredAnalysis.create(abstractionSolver, + PredAbstractors.booleanSplitAbstractor(abstractionSolver), True()); final CfaPrec fixedPrec = GlobalCfaPrec.create(PredPrec.of(emptySet())); @@ -67,14 +70,15 @@ private PredImpactChecker(final LTS, ? extends CfaAc final ArgBuilder, CfaAction, UnitPrec> argBuilder = ArgBuilder.create(lts, analysis, target); - final ImpactRefiner, CfaAction> refiner = PredImpactRefiner.create(solver); + final ImpactRefiner, CfaAction> refiner = PredImpactRefiner.create(refinementSolver); checker = ImpactChecker.create(argBuilder, refiner, CfaState::getLoc); } public static PredImpactChecker create(final LTS, ? extends CfaAction> lts, - final Loc initLoc, final Predicate targetLocs, final ItpSolver solver) { - return new PredImpactChecker(lts, initLoc, targetLocs, solver); + final Loc initLoc, final Predicate targetLocs, + final Solver abstractionSolver, final ItpSolver refinementSolver) { + return new PredImpactChecker(lts, initLoc, targetLocs, abstractionSolver, refinementSolver); } @Override diff --git a/subprojects/cfa/cfa-analysis/src/test/java/hu/bme/mit/theta/cfa/analysis/CfaTest.java b/subprojects/cfa/cfa-analysis/src/test/java/hu/bme/mit/theta/cfa/analysis/CfaTest.java index 25935c9a41..96bc0e767a 100644 --- a/subprojects/cfa/cfa-analysis/src/test/java/hu/bme/mit/theta/cfa/analysis/CfaTest.java +++ b/subprojects/cfa/cfa-analysis/src/test/java/hu/bme/mit/theta/cfa/analysis/CfaTest.java @@ -25,19 +25,29 @@ import hu.bme.mit.theta.cfa.analysis.config.CfaConfig; import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder; import hu.bme.mit.theta.cfa.dsl.CfaDslManager; -import hu.bme.mit.theta.solver.z3.Z3SolverFactory; +import hu.bme.mit.theta.common.OsHelper; +import hu.bme.mit.theta.common.logging.NullLogger; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.SolverManager; +import hu.bme.mit.theta.solver.smtlib.SmtLibSolverManager; +import hu.bme.mit.theta.solver.z3.Z3SolverManager; import org.junit.Assert; +import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.io.FileInputStream; -import java.io.IOException; import java.util.Arrays; import java.util.Collection; -import static hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Domain.*; -import static hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Refinement.*; +import static hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Domain.EXPL; +import static hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Domain.PRED_BOOL; +import static hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Domain.PRED_CART; +import static hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Refinement.BW_BIN_ITP; +import static hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Refinement.NWT_IT_WP; +import static hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Refinement.SEQ_ITP; +import static hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Refinement.UCB; @RunWith(value = Parameterized.class) public class CfaTest { @@ -56,89 +66,121 @@ public class CfaTest { @Parameterized.Parameter(value = 4) public int cexLength; - @Parameterized.Parameters(name = "{index}: {0}, {1}, {2}, {3}, {4}") + @Parameterized.Parameter(value = 5) + public String solver; + + @Parameterized.Parameters(name = "{index}: {0}, {1}, {2}, {3}, {4}, {5}") public static Collection data() { return Arrays.asList(new Object[][] { - { "src/test/resources/arithmetic-bool00.cfa", PRED_CART, SEQ_ITP, false, 15 }, + { "src/test/resources/arithmetic-bool00.cfa", PRED_CART, SEQ_ITP, false, 15, "Z3" }, + + { "src/test/resources/arithmetic-bool00.cfa", PRED_BOOL, BW_BIN_ITP, false, 15, "Z3" }, + + { "src/test/resources/arithmetic-bool00.cfa", EXPL, SEQ_ITP, false, 15, "Z3" }, + + { "src/test/resources/arithmetic-bool01.cfa", PRED_CART, SEQ_ITP, false, 15, "Z3" }, + + { "src/test/resources/arithmetic-bool01.cfa", PRED_BOOL, BW_BIN_ITP, false, 15, "Z3" }, + + { "src/test/resources/arithmetic-bool01.cfa", EXPL, SEQ_ITP, false, 15, "Z3" }, - { "src/test/resources/arithmetic-bool00.cfa", PRED_BOOL, BW_BIN_ITP, false, 15 }, + { "src/test/resources/arithmetic-bool10.cfa", PRED_BOOL, SEQ_ITP, false, 15, "Z3" }, - { "src/test/resources/arithmetic-bool00.cfa", EXPL, SEQ_ITP, false, 15 }, + { "src/test/resources/arithmetic-bool10.cfa", PRED_CART, BW_BIN_ITP, false, 15, "Z3" }, - { "src/test/resources/arithmetic-bool01.cfa", PRED_CART, SEQ_ITP, false, 15 }, + { "src/test/resources/arithmetic-bool10.cfa", EXPL, SEQ_ITP, false, 15, "Z3" }, - { "src/test/resources/arithmetic-bool01.cfa", PRED_BOOL, BW_BIN_ITP, false, 15 }, + { "src/test/resources/arithmetic-bool11.cfa", PRED_CART, SEQ_ITP, false, 15, "Z3" }, - { "src/test/resources/arithmetic-bool01.cfa", EXPL, SEQ_ITP, false, 15 }, + { "src/test/resources/arithmetic-bool11.cfa", PRED_BOOL, BW_BIN_ITP, false, 15, "Z3" }, - { "src/test/resources/arithmetic-bool10.cfa", PRED_BOOL, SEQ_ITP, false, 15 }, + { "src/test/resources/arithmetic-bool11.cfa", EXPL, SEQ_ITP, false, 15, "Z3" }, - { "src/test/resources/arithmetic-bool10.cfa", PRED_CART, BW_BIN_ITP, false, 15 }, + { "src/test/resources/arithmetic-int.cfa", PRED_CART, SEQ_ITP, false, 13, "Z3" }, - { "src/test/resources/arithmetic-bool10.cfa", EXPL, SEQ_ITP, false, 15 }, + { "src/test/resources/arithmetic-int.cfa", PRED_BOOL, BW_BIN_ITP, false, 13, "Z3" }, - { "src/test/resources/arithmetic-bool11.cfa", PRED_CART, SEQ_ITP, false, 15 }, + { "src/test/resources/arithmetic-int.cfa", EXPL, SEQ_ITP, false, 13, "Z3" }, - { "src/test/resources/arithmetic-bool11.cfa", PRED_BOOL, BW_BIN_ITP, false, 15 }, + { "src/test/resources/arithmetic-mod.cfa", PRED_CART, SEQ_ITP, true, 0, "Z3" }, - { "src/test/resources/arithmetic-bool11.cfa", EXPL, SEQ_ITP, false, 15 }, + { "src/test/resources/arithmetic-mod.cfa", EXPL, BW_BIN_ITP, true, 0, "Z3" }, - { "src/test/resources/arithmetic-int.cfa", PRED_CART, SEQ_ITP, false, 13 }, + { "src/test/resources/arrays.cfa", PRED_CART, SEQ_ITP, false, 8, "Z3" }, - { "src/test/resources/arithmetic-int.cfa", PRED_BOOL, BW_BIN_ITP, false, 13 }, + { "src/test/resources/arrays.cfa", PRED_BOOL, BW_BIN_ITP, false, 8, "Z3" }, - { "src/test/resources/arithmetic-int.cfa", EXPL, SEQ_ITP, false, 13 }, + { "src/test/resources/arrayinit.cfa", PRED_CART, BW_BIN_ITP, false, 3, "Z3" }, - { "src/test/resources/arithmetic-mod.cfa", PRED_CART, SEQ_ITP, true, 0 }, + { "src/test/resources/arrays.cfa", EXPL, SEQ_ITP, false, 8, "Z3" }, - { "src/test/resources/arithmetic-mod.cfa", EXPL, BW_BIN_ITP, true, 0 }, + { "src/test/resources/counter5_true.cfa", PRED_BOOL, SEQ_ITP, true, 0, "Z3" }, - { "src/test/resources/arrays.cfa", PRED_CART, SEQ_ITP, false, 8 }, + { "src/test/resources/counter5_true.cfa", PRED_CART, BW_BIN_ITP, true, 0, "Z3" }, - { "src/test/resources/arrays.cfa", PRED_BOOL, BW_BIN_ITP, false, 8 }, + { "src/test/resources/counter5_true.cfa", EXPL, SEQ_ITP, true, 0, "Z3" }, - { "src/test/resources/arrayinit.cfa", PRED_CART, BW_BIN_ITP, false, 3 }, + { "src/test/resources/counter_bv_true.cfa", EXPL, NWT_IT_WP, true, 0, "Z3" }, - { "src/test/resources/arrays.cfa", EXPL, SEQ_ITP, false, 8 }, + { "src/test/resources/counter_bv_false.cfa", EXPL, NWT_IT_WP, false, 13, "Z3" }, - { "src/test/resources/counter5_true.cfa", PRED_BOOL, SEQ_ITP, true, 0 }, + { "src/test/resources/counter_bv_true.cfa", PRED_CART, NWT_IT_WP, true, 0, "Z3" }, - { "src/test/resources/counter5_true.cfa", PRED_CART, BW_BIN_ITP, true, 0 }, + { "src/test/resources/counter_bv_false.cfa", PRED_CART, UCB, false, 13, "Z3" }, - { "src/test/resources/counter5_true.cfa", EXPL, SEQ_ITP, true, 0 }, + { "src/test/resources/counter_bv_true.cfa", EXPL, SEQ_ITP, true, 0, "mathsat:latest" }, - { "src/test/resources/counter_bv_true.cfa", EXPL, NWT_IT_WP, true, 0 }, + { "src/test/resources/counter_bv_false.cfa", EXPL, SEQ_ITP, false, 13, "mathsat:latest" }, - { "src/test/resources/counter_bv_false.cfa", EXPL, NWT_IT_WP, false, 13 }, + { "src/test/resources/fp1.cfa", PRED_CART, NWT_IT_WP, true, 0, "Z3" }, - { "src/test/resources/counter_bv_true.cfa", PRED_CART, NWT_IT_WP, true, 0 }, + { "src/test/resources/fp2.cfa", PRED_CART, NWT_IT_WP, false, 5, "Z3" }, - { "src/test/resources/counter_bv_false.cfa", PRED_CART, UCB, false, 13 }, + { "src/test/resources/counter_fp_true.cfa", EXPL, NWT_IT_WP, true, 0, "Z3" }, - { "src/test/resources/ifelse.cfa", PRED_CART, SEQ_ITP, false, 3 }, + { "src/test/resources/ifelse.cfa", PRED_CART, SEQ_ITP, false, 3, "Z3" }, - { "src/test/resources/ifelse.cfa", PRED_BOOL, BW_BIN_ITP, false, 3 }, + { "src/test/resources/ifelse.cfa", PRED_BOOL, BW_BIN_ITP, false, 3, "Z3" }, - { "src/test/resources/ifelse.cfa", EXPL, SEQ_ITP, false, 3 }, + { "src/test/resources/ifelse.cfa", EXPL, SEQ_ITP, false, 3, "Z3" }, - { "src/test/resources/locking.cfa", PRED_CART, SEQ_ITP, true, 0 }, + { "src/test/resources/locking.cfa", PRED_CART, SEQ_ITP, true, 0, "Z3" }, }); } @Test - public void test() throws IOException { - CFA cfa = CfaDslManager.createCfa(new FileInputStream(filePath)); - CfaConfig config - = new CfaConfigBuilder(domain, refinement, Z3SolverFactory.getInstance()).build(cfa, cfa.getErrorLoc().get()); - SafetyResult result = config.check(); - Assert.assertEquals(isSafe, result.isSafe()); - if (result.isUnsafe()) { - Trace, CfaAction> trace = CfaTraceConcretizer.concretize( + public void test() throws Exception { + SolverManager.registerSolverManager(Z3SolverManager.create()); + if(OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)) { + SolverManager.registerSolverManager(SmtLibSolverManager.create(SmtLibSolverManager.HOME, NullLogger.getInstance())); + } + + final SolverFactory solverFactory; + try { + solverFactory = SolverManager.resolveSolverFactory(solver); + } + catch (Exception e) { + Assume.assumeNoException(e); + return; + } + + try { + CFA cfa = CfaDslManager.createCfa(new FileInputStream(filePath)); + CfaConfig config + = new CfaConfigBuilder(domain, refinement, solverFactory).build(cfa, cfa.getErrorLoc().get()); + SafetyResult result = config.check(); + Assert.assertEquals(isSafe, result.isSafe()); + if (result.isUnsafe()) { + Trace, CfaAction> trace = CfaTraceConcretizer.concretize( (Trace, CfaAction>) result.asUnsafe().getTrace(), - Z3SolverFactory.getInstance()); - Assert.assertEquals(cexLength, trace.length()); + solverFactory); + Assert.assertEquals(cexLength, trace.length()); + } + } + finally { + SolverManager.closeAll(); } } diff --git a/subprojects/cfa/cfa-analysis/src/test/java/hu/bme/mit/theta/cfa/analysis/impact/CfaPredImpactCheckerTest.java b/subprojects/cfa/cfa-analysis/src/test/java/hu/bme/mit/theta/cfa/analysis/impact/CfaPredImpactCheckerTest.java index 6bf6f0275e..3ff359bc93 100644 --- a/subprojects/cfa/cfa-analysis/src/test/java/hu/bme/mit/theta/cfa/analysis/impact/CfaPredImpactCheckerTest.java +++ b/subprojects/cfa/cfa-analysis/src/test/java/hu/bme/mit/theta/cfa/analysis/impact/CfaPredImpactCheckerTest.java @@ -21,6 +21,7 @@ import java.io.FileNotFoundException; import java.io.IOException; +import hu.bme.mit.theta.solver.Solver; import org.junit.Test; import hu.bme.mit.theta.analysis.algorithm.ARG; @@ -44,10 +45,11 @@ public void test() throws FileNotFoundException, IOException { // Arrange final CFA cfa = CfaDslManager.createCfa(new FileInputStream("src/test/resources/counter5_true.cfa")); - final ItpSolver solver = Z3SolverFactory.getInstance().createItpSolver(); + final Solver abstractionSolver = Z3SolverFactory.getInstance().createSolver(); + final ItpSolver refinementSolver = Z3SolverFactory.getInstance().createItpSolver(); final PredImpactChecker checker = PredImpactChecker.create(CfaLbeLts.of(cfa.getErrorLoc().get()), cfa.getInitLoc(), - l -> l.equals(cfa.getErrorLoc().get()), solver); + l -> l.equals(cfa.getErrorLoc().get()), abstractionSolver, refinementSolver); // Act final SafetyResult status = checker.check(UnitPrec.getInstance()); @@ -58,9 +60,9 @@ public void test() throws FileNotFoundException, IOException { final ARG arg = status.getArg(); arg.minimize(); - final ArgChecker argChecker = ArgChecker.create(solver); + final ArgChecker argChecker = ArgChecker.create(abstractionSolver); assertTrue(argChecker.isWellLabeled(arg)); System.out.println(GraphvizWriter.getInstance().writeString(ArgVisualizer.getDefault().visualize(arg))); } -} +} \ No newline at end of file diff --git a/subprojects/cfa/cfa-analysis/src/test/resources/counter_fp_true.cfa b/subprojects/cfa/cfa-analysis/src/test/resources/counter_fp_true.cfa new file mode 100644 index 0000000000..bb8b90ba84 --- /dev/null +++ b/subprojects/cfa/cfa-analysis/src/test/resources/counter_fp_true.cfa @@ -0,0 +1,17 @@ +main process cfa { + var x : fp[8:24] + + init loc L0 + loc L1 + loc L2 + loc L3 + final loc END + error loc ERR + + L0 -> L1 { x := 8'd0.23'd0 } + L1 -> L2 { assume x < 8'b10000001.23'b01000000000000000000000 } + L1 -> L3 { assume not (x < 8'b10000001.23'b01000000000000000000000) } + L2 -> L1 { x := (x fpadd[RNE] 8'b01111111.23'd0) } + L3 -> END { assume x >= 8'b10000001.23'b01000000000000000000000 } + L3 -> ERR { assume not (x >= 8'b10000001.23'b01000000000000000000000) } +} \ No newline at end of file diff --git a/subprojects/cfa/cfa-analysis/src/test/resources/fp1.cfa b/subprojects/cfa/cfa-analysis/src/test/resources/fp1.cfa new file mode 100644 index 0000000000..ee192546d0 --- /dev/null +++ b/subprojects/cfa/cfa-analysis/src/test/resources/fp1.cfa @@ -0,0 +1,21 @@ +main process cfa { + var f2 : fp[5:11] + var f3 : fp[11:53] + var f4 : fp[11:53] + var b1 : bv[4] + + init loc L1 + loc L2 + loc L3 + loc L4 + loc L5 + final loc END + error loc ERR + + L1 -> L2 { f2 := 5'b10010.10'd0 } + L2 -> L3 { f3 := fptofp[11:53][RNE] f2 } + L3 -> L4 { b1 := 4'd8 } + L4 -> L5 { f4 := fpfrombv[11:53][u][RNE] b1 } + L5 -> END { assume f3 = f4 } + L5 -> ERR { assume not (f3 = f4) } +} \ No newline at end of file diff --git a/subprojects/cfa/cfa-analysis/src/test/resources/fp2.cfa b/subprojects/cfa/cfa-analysis/src/test/resources/fp2.cfa new file mode 100644 index 0000000000..27a6d8b8b7 --- /dev/null +++ b/subprojects/cfa/cfa-analysis/src/test/resources/fp2.cfa @@ -0,0 +1,21 @@ +main process cfa { + var f2 : fp[2:3] + var f3 : fp[2:3] + var f4 : fp[2:3] + var f5 : fp[2:3] + + init loc L1 + loc L2 + loc L3 + loc L4 + loc L5 + final loc END + error loc ERR + + L1 -> L2 { f2 := 2'b01.2'b00 } + L2 -> L3 { f3 := 2'b10.2'b11 } + L3 -> L4 { f4 := f2 fpdiv[RTP] f3 } + L4 -> L5 { f5 := f2 / f3 } + L5 -> END { assume (f4 = f5) } + L5 -> ERR { assume not (f4 = f5) } +} \ No newline at end of file diff --git a/subprojects/cfa/cfa-cli/build.gradle.kts b/subprojects/cfa/cfa-cli/build.gradle.kts index 2d7a900bde..81d0bb182c 100644 --- a/subprojects/cfa/cfa-cli/build.gradle.kts +++ b/subprojects/cfa/cfa-cli/build.gradle.kts @@ -7,6 +7,7 @@ dependencies { compile(project(":theta-cfa")) compile(project(":theta-cfa-analysis")) compile(project(":theta-solver-z3")) + compile(project(":theta-solver-smtlib")) } application { diff --git a/subprojects/cfa/cfa-cli/src/main/java/hu/bme/mit/theta/cfa/cli/CfaCli.java b/subprojects/cfa/cfa-cli/src/main/java/hu/bme/mit/theta/cfa/cli/CfaCli.java index 8e1bc8e738..7d30c2eeaa 100644 --- a/subprojects/cfa/cfa-cli/src/main/java/hu/bme/mit/theta/cfa/cli/CfaCli.java +++ b/subprojects/cfa/cfa-cli/src/main/java/hu/bme/mit/theta/cfa/cli/CfaCli.java @@ -21,6 +21,7 @@ import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; +import java.nio.file.Path; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; @@ -51,6 +52,7 @@ import hu.bme.mit.theta.cfa.analysis.utils.CfaVisualizer; import hu.bme.mit.theta.cfa.dsl.CfaDslManager; import hu.bme.mit.theta.common.CliUtils; +import hu.bme.mit.theta.common.OsHelper; import hu.bme.mit.theta.common.logging.ConsoleLogger; import hu.bme.mit.theta.common.logging.Logger; import hu.bme.mit.theta.common.logging.Logger.Level; @@ -59,7 +61,11 @@ import hu.bme.mit.theta.common.table.TableWriter; import hu.bme.mit.theta.common.visualization.Graph; import hu.bme.mit.theta.common.visualization.writer.GraphvizWriter; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.SolverManager; +import hu.bme.mit.theta.solver.smtlib.SmtLibSolverManager; import hu.bme.mit.theta.solver.z3.Z3SolverFactory; +import hu.bme.mit.theta.solver.z3.Z3SolverManager; import static com.google.common.base.Preconditions.checkNotNull; @@ -83,6 +89,18 @@ public class CfaCli { @Parameter(names = "--predsplit", description = "Predicate splitting (for predicate abstraction)") PredSplit predSplit = PredSplit.WHOLE; + @Parameter(names = "--solver", description = "Sets the underlying SMT solver to use for both the abstraction and the refinement process. Enter in format :, see theta-smtlib-cli.jar for more details. Enter \"Z3\" to use the legacy z3 solver.") + String solver = "Z3"; + + @Parameter(names = "--abstraction-solver", description = "Sets the underlying SMT solver to use for the abstraction process. Enter in format :, see theta-smtlib-cli.jar for more details. Enter \"Z3\" to use the legacy z3 solver.") + String abstractionSolver; + + @Parameter(names = "--refinement-solver", description = "Sets the underlying SMT solver to use for the refinement process. Enter in format :, see theta-smtlib-cli.jar for more details. Enter \"Z3\" to use the legacy z3 solver.") + String refinementSolver; + + @Parameter(names = "--home", description = "The path of the solver registry") + String home = SmtLibSolverManager.HOME.toAbsolutePath().toString(); + @Parameter(names = "--model", description = "Path of the input CFA model", required = true) String model; @@ -162,6 +180,13 @@ private void run() { } try { + SolverManager.registerSolverManager(Z3SolverManager.create()); + if(OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)) { + final var homePath = Path.of(home); + final var smtLibSolverManager = SmtLibSolverManager.create(homePath, logger); + SolverManager.registerSolverManager(smtLibSolverManager); + } + final Stopwatch sw = Stopwatch.createStarted(); final CFA cfa = loadModel(); @@ -189,9 +214,26 @@ private void run() { } checkNotNull(errLoc, "Location '" + errorLoc + "' not found in CFA"); } - checkNotNull(errLoc, "Error location must be specified in CFA or as argument"); - final CfaConfig configuration = buildConfiguration(cfa, errLoc); + + final SolverFactory abstractionSolverFactory; + if(abstractionSolver != null) { + abstractionSolverFactory = SolverManager.resolveSolverFactory(abstractionSolver); + } + else { + abstractionSolverFactory = SolverManager.resolveSolverFactory(solver); + } + + + final SolverFactory refinementSolverFactory; + if(refinementSolver != null) { + refinementSolverFactory = SolverManager.resolveSolverFactory(refinementSolver); + } + else { + refinementSolverFactory = SolverManager.resolveSolverFactory(solver); + } + + final CfaConfig configuration = buildConfiguration(cfa, errLoc, abstractionSolverFactory, refinementSolverFactory); final SafetyResult status = check(configuration); sw.stop(); printResult(status, sw.elapsed(TimeUnit.MILLISECONDS)); @@ -220,9 +262,9 @@ private CFA loadModel() throws Exception { } } - private CfaConfig buildConfiguration(final CFA cfa, final CFA.Loc errLoc) throws Exception { + private CfaConfig buildConfiguration(final CFA cfa, final CFA.Loc errLoc, final SolverFactory abstractionSolverFactory, final SolverFactory refinementSolverFactory) throws Exception { try { - return new CfaConfigBuilder(domain, refinement, Z3SolverFactory.getInstance()) + return new CfaConfigBuilder(domain, refinement, abstractionSolverFactory, refinementSolverFactory) .precGranularity(precGranularity).search(search) .predSplit(predSplit).encoding(encoding).maxEnum(maxEnum).initPrec(initPrec) .pruneStrategy(pruneStrategy).logger(logger).build(cfa, errLoc); diff --git a/subprojects/cfa/cfa/src/main/antlr/CfaDsl.g4 b/subprojects/cfa/cfa/src/main/antlr/CfaDsl.g4 index 15bfce3af0..29bb06c56a 100644 --- a/subprojects/cfa/cfa/src/main/antlr/CfaDsl.g4 +++ b/subprojects/cfa/cfa/src/main/antlr/CfaDsl.g4 @@ -541,15 +541,15 @@ FP_IS_NAN ; FPMAX - : 'fpmax' FP_ROUNDINGMODE? + : 'fpmax' ; FPMIN - : 'fpmin' FP_ROUNDINGMODE? + : 'fpmin' ; FPREM - : 'fprem' FP_ROUNDINGMODE? + : 'fprem' ; FPROUNDTOINT diff --git a/subprojects/cfa/cfa/src/main/java/hu/bme/mit/theta/cfa/dsl/CfaExpression.java b/subprojects/cfa/cfa/src/main/java/hu/bme/mit/theta/cfa/dsl/CfaExpression.java index 6ffe416e3c..e7e53b4c5f 100644 --- a/subprojects/cfa/cfa/src/main/java/hu/bme/mit/theta/cfa/dsl/CfaExpression.java +++ b/subprojects/cfa/cfa/src/main/java/hu/bme/mit/theta/cfa/dsl/CfaExpression.java @@ -128,6 +128,7 @@ import static hu.bme.mit.theta.core.utils.ExprUtils.simplify; import static hu.bme.mit.theta.core.utils.TypeUtils.cast; import static hu.bme.mit.theta.core.utils.TypeUtils.castBv; +import static hu.bme.mit.theta.core.utils.TypeUtils.castFp; import static java.util.stream.Collectors.toList; final class CfaExpression { @@ -392,9 +393,9 @@ public Expr visitFpFuncExpr(FpFuncExprContext ctx){ switch (ctx.oper.getType()) { case FPMAX: - return Max(getRoundingMode(ctx.oper.getText()), leftOp, rightOp); + return Max(leftOp, rightOp); case FPMIN: - return Min(getRoundingMode(ctx.oper.getText()), leftOp, rightOp); + return Min(leftOp, rightOp); default: throw new ParseException(ctx, "Unknown operator"); } @@ -538,6 +539,12 @@ private Expr createAdditiveSubExpr(final Expr leftOp, final Expr rightO case BV_SUB: return createBvSubExpr(castBv(leftOp), castBv(rightOp)); + case FPADD: + return FpExprs.Add(getRoundingMode(oper.getText()), List.of(castFp(leftOp), castFp(rightOp))); + + case FPSUB: + return FpExprs.Sub(getRoundingMode(oper.getText()), castFp(leftOp), castFp(rightOp)); + default: throw new ParseException(ctx, "Unknown operator '" + oper.getText() + "'"); } @@ -646,7 +653,7 @@ private Expr createMultiplicativeSubExpr(final Expr leftOp, final Expr return createBvSRemExpr(castBv(leftOp), castBv(rightOp)); case FPREM: - return FpExprs.Rem(getRoundingMode(oper.getText()), (Expr) leftOp, (Expr) rightOp); + return FpExprs.Rem((Expr) leftOp, (Expr) rightOp); case FPMUL: return FpExprs.Mul(getRoundingMode(oper.getText()), List.of((Expr) leftOp, (Expr) rightOp)); diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/ExprTraceNewtonChecker.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/ExprTraceNewtonChecker.java index 7dff233ca1..6f31cceae3 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/ExprTraceNewtonChecker.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/ExprTraceNewtonChecker.java @@ -13,7 +13,17 @@ import hu.bme.mit.theta.core.model.BasicSubstitution; import hu.bme.mit.theta.core.model.ImmutableValuation; import hu.bme.mit.theta.core.model.Valuation; -import hu.bme.mit.theta.core.stmt.*; +import hu.bme.mit.theta.core.stmt.AssignStmt; +import hu.bme.mit.theta.core.stmt.AssumeStmt; +import hu.bme.mit.theta.core.stmt.HavocStmt; +import hu.bme.mit.theta.core.stmt.IfStmt; +import hu.bme.mit.theta.core.stmt.LoopStmt; +import hu.bme.mit.theta.core.stmt.NonDetStmt; +import hu.bme.mit.theta.core.stmt.OrtStmt; +import hu.bme.mit.theta.core.stmt.SequenceStmt; +import hu.bme.mit.theta.core.stmt.SkipStmt; +import hu.bme.mit.theta.core.stmt.Stmt; +import hu.bme.mit.theta.core.stmt.StmtVisitor; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.Type; import hu.bme.mit.theta.core.type.booltype.BoolType; @@ -24,13 +34,14 @@ import hu.bme.mit.theta.core.utils.StmtUtils; import hu.bme.mit.theta.core.utils.VarIndexing; import hu.bme.mit.theta.core.utils.WpState; -import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.UCSolver; import hu.bme.mit.theta.solver.utils.WithPushPop; -import java.util.*; - -import hu.bme.mit.theta.common.container.Containers; - +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -53,7 +64,7 @@ public class ExprTraceNewtonChecker implements ExprTraceChecker { private enum AssertionGeneratorMethod { SP, WP } - private final Solver solver; + private final UCSolver solver; private final Expr init; private final Expr target; @@ -62,7 +73,7 @@ private enum AssertionGeneratorMethod { SP, WP } private final boolean LV; // Whether to project the assertions to live variables private ExprTraceNewtonChecker( - final Expr init, final Expr target, final Solver solver, + final Expr init, final Expr target, final UCSolver solver, boolean it, AssertionGeneratorMethod sPorWP, boolean lv ) { this.solver = checkNotNull(solver); @@ -74,7 +85,7 @@ private ExprTraceNewtonChecker( } public static ExprTraceNewtonCheckerITBuilder create( - final Expr init, final Expr target, final Solver solver + final Expr init, final Expr target, final UCSolver solver ) { return new ExprTraceNewtonCheckerITBuilder(solver, init, target); } @@ -145,8 +156,8 @@ public String toString() { for(var i = 1; i < stateCount; i++) { var initStream = (i == 1) - ? ExprUtils.getConjuncts(init).stream().map(AssumeStmt::of) - : Stream.empty(); + ? ExprUtils.getConjuncts(init).stream().map(AssumeStmt::of) + : Stream.empty(); var stateStream = ExprUtils.getConjuncts(trace.getState(i - 1).toExpr()).stream().map(AssumeStmt::of); @@ -154,11 +165,11 @@ public String toString() { var targetStream = (i == stateCount - 1) - ? Stream.concat( + ? Stream.concat( ExprUtils.getConjuncts(trace.getState(i).toExpr()).stream().map(AssumeStmt::of), ExprUtils.getConjuncts(target).stream().map(AssumeStmt::of) ) - : Stream.empty(); + : Stream.empty(); flattenedActions.add( NewtonAction.of( @@ -355,7 +366,7 @@ private List> computeAssertionsFromTraceWithWeakestPrecondition( */ private Collection> collectVariablesInTrace(final Trace trace) { - Set> variables = Containers.createSet(); + var variables = new HashSet>(); for(var state : trace.getStates()) { ExprUtils.collectVars(state.toExpr(), variables); @@ -405,10 +416,14 @@ public Collection> visit(OrtStmt stmt, Void param) { } @Override - public Collection> visit(LoopStmt stmt, Void param) { throw new UnsupportedOperationException(); } + public Collection> visit(LoopStmt stmt, Void param) { + throw new UnsupportedOperationException(); + } @Override - public Collection> visit(IfStmt stmt, Void param) { throw new UnsupportedOperationException(); } + public Collection> visit(IfStmt stmt, Void param) { + throw new UnsupportedOperationException(); + } }, null); } @@ -450,10 +465,14 @@ public Collection> visit(OrtStmt stmt, Void param) { } @Override - public Collection> visit(LoopStmt stmt, Void param) { throw new UnsupportedOperationException(); } + public Collection> visit(LoopStmt stmt, Void param) { + throw new UnsupportedOperationException(); + } @Override - public Collection> visit(IfStmt stmt, Void param) { throw new UnsupportedOperationException(); } + public Collection> visit(IfStmt stmt, Void param) { + throw new UnsupportedOperationException(); + } }, null); } @@ -495,10 +514,14 @@ public Collection> visit(OrtStmt stmt, Void param) { } @Override - public Collection> visit(LoopStmt stmt, Void param) { throw new UnsupportedOperationException(); } + public Collection> visit(LoopStmt stmt, Void param) { + throw new UnsupportedOperationException(); + } @Override - public Collection> visit(IfStmt stmt, Void param) { throw new UnsupportedOperationException(); } + public Collection> visit(IfStmt stmt, Void param) { + throw new UnsupportedOperationException(); + } }, null); } @@ -520,7 +543,7 @@ private List>> collectFutureLiveVariablesForTrace(final Tr futureLiveVariables.set(stateCount - 1, Collections.emptySet()); for(var i = stateCount - 2; i >= 0; i--) { - var vars = Containers.createSet(futureLiveVariables.get(i + 1)); + var vars = new HashSet<>(futureLiveVariables.get(i + 1)); vars.addAll(actionReadsVariables(trace.getAction(i))); vars.removeAll(actionWritesVariables(trace.getAction(i))); vars.removeAll(actionHavocsVariables(trace.getAction(i))); @@ -536,7 +559,7 @@ private List>> collectPastLiveVariablesForTrace(final Trac pastLiveVariables.set(0, Collections.emptySet()); for(var i = 1; i < stateCount; i++) { - var vars = Containers.createSet(pastLiveVariables.get(i - 1)); + var vars = new HashSet<>(pastLiveVariables.get(i - 1)); vars.addAll(actionReadsVariables(trace.getAction(i - 1))); vars.addAll(actionWritesVariables(trace.getAction(i - 1))); vars.removeAll(actionHavocsVariables(trace.getAction(i - 1))); @@ -580,8 +603,8 @@ private Expr universalProjection( .collect(Collectors.toUnmodifiableSet()); var substitution = BasicSubstitution.builder() - .putAll(params.stream().collect(toUnmodifiableMap(Tuple2::get1, e -> e.get2().getRef()))) - .build(); + .putAll(params.stream().collect(toUnmodifiableMap(Tuple2::get1, e -> e.get2().getRef()))) + .build(); return params.size() > 0 ? Forall(params.stream().map(Tuple2::get2).collect(toList()), substitution.apply(expr)) @@ -593,11 +616,11 @@ private Expr universalProjection( */ public static class ExprTraceNewtonCheckerITBuilder { - private final Solver solver; + private final UCSolver solver; private final Expr init; private final Expr target; - public ExprTraceNewtonCheckerITBuilder(Solver solver, Expr init, Expr target) { + public ExprTraceNewtonCheckerITBuilder(UCSolver solver, Expr init, Expr target) { this.solver = solver; this.init = init; this.target = target; @@ -613,13 +636,13 @@ public ExprTraceNewtonCheckerAssertBuilder withoutIT() { } public static class ExprTraceNewtonCheckerAssertBuilder { - private final Solver solver; + private final UCSolver solver; private final Expr init; private final Expr target; private final boolean IT; - public ExprTraceNewtonCheckerAssertBuilder(Solver solver, Expr init, Expr target, boolean it) { + public ExprTraceNewtonCheckerAssertBuilder(UCSolver solver, Expr init, Expr target, boolean it) { this.solver = solver; this.init = init; this.target = target; @@ -636,14 +659,14 @@ public ExprTraceNewtonCheckerLVBuilder withWP() { } public static class ExprTraceNewtonCheckerLVBuilder { - private final Solver solver; + private final UCSolver solver; private final Expr init; private final Expr target; private final boolean IT; private final AssertionGeneratorMethod SPorWP; - public ExprTraceNewtonCheckerLVBuilder(Solver solver, Expr init, Expr target, boolean it, AssertionGeneratorMethod sPorWP) { + public ExprTraceNewtonCheckerLVBuilder(UCSolver solver, Expr init, Expr target, boolean it, AssertionGeneratorMethod sPorWP) { this.solver = solver; this.init = init; this.target = target; diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/ExprTraceUCBChecker.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/ExprTraceUCBChecker.java index e1a9737537..74941b1a2f 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/ExprTraceUCBChecker.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/ExprTraceUCBChecker.java @@ -20,13 +20,13 @@ import hu.bme.mit.theta.core.utils.SpState; import hu.bme.mit.theta.core.utils.VarIndexing; import hu.bme.mit.theta.core.utils.WpState; -import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.UCSolver; import hu.bme.mit.theta.solver.utils.WithPushPop; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import hu.bme.mit.theta.common.container.Containers; +import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -45,18 +45,18 @@ */ public class ExprTraceUCBChecker implements ExprTraceChecker { - private final Solver solver; + private final UCSolver solver; private final Expr init; private final Expr target; - private ExprTraceUCBChecker(final Expr init, final Expr target, final Solver solver) { + private ExprTraceUCBChecker(final Expr init, final Expr target, final UCSolver solver) { this.solver = checkNotNull(solver); this.init = checkNotNull(init); this.target = checkNotNull(target); } public static ExprTraceUCBChecker create( - final Expr init, final Expr target, final Solver solver + final Expr init, final Expr target, final UCSolver solver ) { return new ExprTraceUCBChecker(init, target, solver); } @@ -73,7 +73,7 @@ public ExprTraceStatus check(final Trace check2(final Trace trace) { + private ExprTraceStatus check2(final Trace trace) { final var ftrace = flattenTrace(trace); final int stateCount = trace.getStates().size(); @@ -180,7 +180,7 @@ private ExprTraceStatus.Infeasible createRefinement( /* Add the negated of the above expression as the new predicate */ predicates.add( ExprSimplifier.simplify( - Not(And(Containers.createSet(predicate))), + Not(And(new HashSet<>(predicate))), ImmutableValuation.empty() ) ); @@ -224,8 +224,8 @@ private List> calculateWpStates( for(var i = 1; i < stateCount; i++) { var initStream = (i == 1) - ? ExprUtils.getConjuncts(init).stream().map(AssumeStmt::of) - : Stream.empty(); + ? ExprUtils.getConjuncts(init).stream().map(AssumeStmt::of) + : Stream.empty(); var stateStream = ExprUtils.getConjuncts(trace.getState(i - 1).toExpr()).stream().map(AssumeStmt::of); @@ -233,11 +233,11 @@ private List> calculateWpStates( var targetStream = (i == stateCount - 1) - ? Stream.concat( + ? Stream.concat( ExprUtils.getConjuncts(trace.getState(i).toExpr()).stream().map(AssumeStmt::of), ExprUtils.getConjuncts(target).stream().map(AssumeStmt::of) ) - : Stream.empty(); + : Stream.empty(); flattenedActions.add( UCBAction.of( diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/ExprTraceUnsatCoreChecker.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/ExprTraceUnsatCoreChecker.java index 60360a1f25..1595565a74 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/ExprTraceUnsatCoreChecker.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/ExprTraceUnsatCoreChecker.java @@ -33,7 +33,7 @@ import hu.bme.mit.theta.core.utils.IndexedVars; import hu.bme.mit.theta.core.utils.PathUtils; import hu.bme.mit.theta.core.utils.VarIndexing; -import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.UCSolver; import hu.bme.mit.theta.solver.utils.WithPushPop; /** @@ -42,18 +42,18 @@ */ public final class ExprTraceUnsatCoreChecker implements ExprTraceChecker { - private final Solver solver; + private final UCSolver solver; private final Expr init; private final Expr target; - private ExprTraceUnsatCoreChecker(final Expr init, final Expr target, final Solver solver) { + private ExprTraceUnsatCoreChecker(final Expr init, final Expr target, final UCSolver solver) { this.solver = checkNotNull(solver); this.init = checkNotNull(init); this.target = checkNotNull(target); } public static ExprTraceUnsatCoreChecker create(final Expr init, final Expr target, - final Solver solver) { + final UCSolver solver) { return new ExprTraceUnsatCoreChecker(init, target, solver); } diff --git a/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/expr/ExprTraceCheckersTest.java b/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/expr/ExprTraceCheckersTest.java index 1eeff7ace9..567c90491d 100644 --- a/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/expr/ExprTraceCheckersTest.java +++ b/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/expr/ExprTraceCheckersTest.java @@ -33,6 +33,7 @@ import java.util.Collection; import java.util.List; +import hu.bme.mit.theta.solver.UCSolver; import org.junit.Before; import org.junit.Test; @@ -55,12 +56,13 @@ public final class ExprTraceCheckersTest { @Before public void before() { - final ItpSolver solver = Z3SolverFactory.getInstance().createItpSolver(); + final ItpSolver itpSolver = Z3SolverFactory.getInstance().createItpSolver(); + final UCSolver ucSolver = Z3SolverFactory.getInstance().createUCSolver(); traceCheckers = new ArrayList<>(); - traceCheckers.add(ExprTraceSeqItpChecker.create(True(), True(), solver)); - traceCheckers.add(ExprTraceFwBinItpChecker.create(True(), True(), solver)); - traceCheckers.add(ExprTraceBwBinItpChecker.create(True(), True(), solver)); - traceCheckers.add(ExprTraceUnsatCoreChecker.create(True(), True(), solver)); + traceCheckers.add(ExprTraceSeqItpChecker.create(True(), True(), itpSolver)); + traceCheckers.add(ExprTraceFwBinItpChecker.create(True(), True(), itpSolver)); + traceCheckers.add(ExprTraceBwBinItpChecker.create(True(), True(), itpSolver)); + traceCheckers.add(ExprTraceUnsatCoreChecker.create(True(), True(), ucSolver)); } @Test diff --git a/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/OsHelper.java b/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/OsHelper.java index 16320df513..062b089a1b 100644 --- a/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/OsHelper.java +++ b/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/OsHelper.java @@ -24,7 +24,7 @@ private OsHelper() { } public enum OperatingSystem { - WINDOWS, LINUX + WINDOWS, LINUX, MAC } public static OperatingSystem getOs() { @@ -35,12 +35,31 @@ public static OperatingSystem getOs() { return OperatingSystem.LINUX; } else if (os.toLowerCase().startsWith("windows")) { return OperatingSystem.WINDOWS; + } else if ((os.toLowerCase().contains("mac")) || (os.toLowerCase().contains("darwin"))) { + return OperatingSystem.MAC; } else { throw new UnsupportedOperationException("Operating system \"" + os + "\" not supported."); } } + public enum Architecture { + X86, X64 + } + + public static Architecture getArch() { + final String arch = StandardSystemProperty.OS_ARCH.value(); + + if (arch.equalsIgnoreCase("x86")) { + return Architecture.X86; + } else if (arch.equalsIgnoreCase("amd64")) { + return Architecture.X64; + } else { + throw new UnsupportedOperationException("Architecture \"" + arch + "\" not supported."); + } + } + public static void main(final String[] args) { System.out.println(getOs()); + System.out.println(getArch()); } } diff --git a/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/QuadFunction.java b/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/QuadFunction.java new file mode 100644 index 0000000000..416b77bb74 --- /dev/null +++ b/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/QuadFunction.java @@ -0,0 +1,16 @@ +package hu.bme.mit.theta.common; + +import java.util.function.Function; + +import static com.google.common.base.Preconditions.checkNotNull; + +@FunctionalInterface +public interface QuadFunction { + + R apply(T t, U u, V v, W w); + + default QuadFunction andThen(final Function after) { + checkNotNull(after); + return (final T t, final U u, final V v, final W w) -> after.apply(apply(t, u, v, w)); + } +} diff --git a/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/QuintFunction.java b/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/QuintFunction.java new file mode 100644 index 0000000000..548b9fc3e3 --- /dev/null +++ b/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/QuintFunction.java @@ -0,0 +1,16 @@ +package hu.bme.mit.theta.common; + +import java.util.function.Function; + +import static com.google.common.base.Preconditions.checkNotNull; + +@FunctionalInterface +public interface QuintFunction { + + R apply(T t, U u, V v, W w, X x); + + default QuintFunction andThen(final Function after) { + checkNotNull(after); + return (final T t, final U u, final V v, final W w, final X x) -> after.apply(apply(t, u, v, w, x)); + } +} diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpExprs.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpExprs.java index 421054d600..591369e05f 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpExprs.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpExprs.java @@ -4,6 +4,8 @@ import hu.bme.mit.theta.core.type.bvtype.BvLitExpr; import hu.bme.mit.theta.core.type.bvtype.BvType; +import java.util.Arrays; + public final class FpExprs { private FpExprs() { } @@ -16,6 +18,51 @@ public static FpLitExpr Fp(boolean hidden, BvLitExpr exponent, BvLitExpr signifi return FpLitExpr.of(hidden, exponent, significand); } + public static FpLitExpr NaN(final FpType fpType) { + final var exponent = new boolean[fpType.getExponent()]; + Arrays.fill(exponent, true); + final var significand = new boolean[fpType.getSignificand() - 1]; + Arrays.fill(significand, true); + + return Fp(false, BvLitExpr.of(exponent), BvLitExpr.of(significand)); + } + + public static FpLitExpr PositiveInfinity(final FpType fpType) { + final var exponent = new boolean[fpType.getExponent()]; + Arrays.fill(exponent, true); + final var significand = new boolean[fpType.getSignificand() - 1]; + Arrays.fill(significand, false); + + return Fp(false, BvLitExpr.of(exponent), BvLitExpr.of(significand)); + } + + public static FpLitExpr NegativeInfinity(final FpType fpType) { + final var exponent = new boolean[fpType.getExponent()]; + Arrays.fill(exponent, true); + final var significand = new boolean[fpType.getSignificand() - 1]; + Arrays.fill(significand, false); + + return Fp(true, BvLitExpr.of(exponent), BvLitExpr.of(significand)); + } + + public static FpLitExpr PositiveZero(final FpType fpType) { + final var exponent = new boolean[fpType.getExponent()]; + Arrays.fill(exponent, false); + final var significand = new boolean[fpType.getSignificand() - 1]; + Arrays.fill(significand, false); + + return Fp(false, BvLitExpr.of(exponent), BvLitExpr.of(significand)); + } + + public static FpLitExpr NegativeZero(final FpType fpType) { + final var exponent = new boolean[fpType.getExponent()]; + Arrays.fill(exponent, false); + final var significand = new boolean[fpType.getSignificand() - 1]; + Arrays.fill(significand, false); + + return Fp(true, BvLitExpr.of(exponent), BvLitExpr.of(significand)); + } + public static FpAddExpr Add(final FpRoundingMode roundingMode, final Iterable> ops) { return FpAddExpr.of(roundingMode, ops); } @@ -40,8 +87,8 @@ public static FpDivExpr Div(final FpRoundingMode roundingMode, final Expr leftOp, final Expr rightOp) { - return FpRemExpr.of(roundingMode, leftOp, rightOp); + public static FpRemExpr Rem(final Expr leftOp, final Expr rightOp) { + return FpRemExpr.of(leftOp, rightOp); } public static FpAbsExpr Abs(final Expr op) { @@ -88,12 +135,12 @@ public static FpSqrtExpr Sqrt(final FpRoundingMode roundingMode, final Expr leftOp, final Expr rightOp) { - return FpMaxExpr.of(roundingMode, leftOp, rightOp); + public static FpMaxExpr Max(final Expr leftOp, final Expr rightOp) { + return FpMaxExpr.of(leftOp, rightOp); } - public static FpMinExpr Min(final FpRoundingMode roundingMode, final Expr leftOp, final Expr rightOp) { - return FpMinExpr.of(roundingMode, leftOp, rightOp); + public static FpMinExpr Min(final Expr leftOp, final Expr rightOp) { + return FpMinExpr.of(leftOp, rightOp); } public static FpToBvExpr ToBv(final FpRoundingMode roundingMode, final Expr op, final int size, final boolean sgn) { diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpLitExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpLitExpr.java index f3886937d4..716a52d9c6 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpLitExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpLitExpr.java @@ -7,8 +7,6 @@ import hu.bme.mit.theta.core.type.booltype.BoolLitExpr; import hu.bme.mit.theta.core.type.bvtype.BvLitExpr; -import java.util.Arrays; - import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static hu.bme.mit.theta.core.utils.FpUtils.bigFloatToFpLitExpr; @@ -35,33 +33,6 @@ public static FpLitExpr of(final boolean hidden, final BvLitExpr exponent, final return new FpLitExpr(hidden, exponent, significand); } - public static FpLitExpr NaN(final FpType fpType) { - final var exponent = new boolean[fpType.getExponent()]; - Arrays.fill(exponent, true); - final var significand = new boolean[fpType.getSignificand() - 1]; - Arrays.fill(significand, true); - - return new FpLitExpr(false, BvLitExpr.of(exponent), BvLitExpr.of(significand)); - } - - public static FpLitExpr PositiveInfinity(final FpType fpType) { - final var exponent = new boolean[fpType.getExponent()]; - Arrays.fill(exponent, true); - final var significand = new boolean[fpType.getSignificand() - 1]; - Arrays.fill(significand, false); - - return new FpLitExpr(false, BvLitExpr.of(exponent), BvLitExpr.of(significand)); - } - - public static FpLitExpr NegativeInfinity(final FpType fpType) { - final var exponent = new boolean[fpType.getExponent()]; - Arrays.fill(exponent, true); - final var significand = new boolean[fpType.getSignificand() - 1]; - Arrays.fill(significand, false); - - return new FpLitExpr(true, BvLitExpr.of(exponent), BvLitExpr.of(significand)); - } - public boolean getHidden() { return hidden; } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpMaxExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpMaxExpr.java index 7bc37d3af4..c16d77b84a 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpMaxExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpMaxExpr.java @@ -14,24 +14,20 @@ public class FpMaxExpr extends BinaryExpr { private static final int HASH_SEED = 6668; private static final String OPERATOR_LABEL = "fpmax"; - private final FpRoundingMode roundingMode; - - private FpMaxExpr(final FpRoundingMode roundingMode, final Expr leftOp, final Expr rightOp) { + private FpMaxExpr(final Expr leftOp, final Expr rightOp) { super(leftOp, rightOp); checkAllTypesEqual(leftOp, rightOp); - checkNotNull(roundingMode); - this.roundingMode = roundingMode; } - public static FpMaxExpr of(final FpRoundingMode roundingMode, final Expr leftOp, final Expr rightOp) { - return new FpMaxExpr(roundingMode, leftOp, rightOp); + public static FpMaxExpr of(final Expr leftOp, final Expr rightOp) { + return new FpMaxExpr(leftOp, rightOp); } - public static FpMaxExpr create(final FpRoundingMode roundingMode, final Expr leftOp, final Expr rightOp) { + public static FpMaxExpr create(final Expr leftOp, final Expr rightOp) { checkNotNull(leftOp, rightOp); final Expr newLeftOp = castFp(leftOp); final Expr newRightOp = castFp(rightOp); - return FpMaxExpr.of(roundingMode, newLeftOp, newRightOp); + return FpMaxExpr.of(newLeftOp, newRightOp); } @Override @@ -39,7 +35,7 @@ public BinaryExpr with(Expr leftOp, Expr rightOp if (leftOp == getLeftOp() && rightOp == getRightOp()) { return this; } else { - return FpMaxExpr.of(roundingMode, leftOp, rightOp); + return FpMaxExpr.of(leftOp, rightOp); } } @@ -74,7 +70,7 @@ public boolean equals(final Object obj) { return true; } else if (obj instanceof FpMaxExpr) { final FpMaxExpr that = (FpMaxExpr) obj; - return this.getLeftOp().equals(that.getLeftOp()) && this.getRightOp().equals(that.getRightOp()) && roundingMode == that.roundingMode; + return this.getLeftOp().equals(that.getLeftOp()) && this.getRightOp().equals(that.getRightOp()); } else { return false; } @@ -83,7 +79,7 @@ public boolean equals(final Object obj) { public LitExpr eval(Valuation val) { final FpLitExpr leftOpVal = (FpLitExpr) getLeftOp().eval(val); final FpLitExpr rightOpVal = (FpLitExpr) getRightOp().eval(val); - if (FpUtils.fpLitExprToBigFloat(roundingMode, leftOpVal).greaterThan(FpUtils.fpLitExprToBigFloat(roundingMode, rightOpVal))) { + if (FpUtils.fpLitExprToBigFloat(null, leftOpVal).greaterThan(FpUtils.fpLitExprToBigFloat(null, rightOpVal))) { return leftOpVal; } else { return rightOpVal; diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpMinExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpMinExpr.java index f5010af3eb..8be8693110 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpMinExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpMinExpr.java @@ -14,24 +14,20 @@ public class FpMinExpr extends BinaryExpr { private static final int HASH_SEED = 6667; private static final String OPERATOR_LABEL = "fpmin"; - private final FpRoundingMode roundingMode; - - private FpMinExpr(final FpRoundingMode roundingMode, final Expr leftOp, final Expr rightOp) { + private FpMinExpr(final Expr leftOp, final Expr rightOp) { super(leftOp, rightOp); checkAllTypesEqual(leftOp, rightOp); - checkNotNull(roundingMode); - this.roundingMode = roundingMode; } - public static FpMinExpr of(final FpRoundingMode roundingMode, final Expr leftOp, final Expr rightOp) { - return new FpMinExpr(roundingMode, leftOp, rightOp); + public static FpMinExpr of(final Expr leftOp, final Expr rightOp) { + return new FpMinExpr(leftOp, rightOp); } - public static FpMinExpr create(final FpRoundingMode roundingMode, final Expr leftOp, final Expr rightOp) { + public static FpMinExpr create(final Expr leftOp, final Expr rightOp) { checkNotNull(leftOp, rightOp); final Expr newLeftOp = castFp(leftOp); final Expr newRightOp = castFp(rightOp); - return FpMinExpr.of(roundingMode, newLeftOp, newRightOp); + return FpMinExpr.of(newLeftOp, newRightOp); } @Override @@ -39,7 +35,7 @@ public BinaryExpr with(Expr leftOp, Expr rightOp if (leftOp == getLeftOp() && rightOp == getRightOp()) { return this; } else { - return FpMinExpr.of(roundingMode, leftOp, rightOp); + return FpMinExpr.of(leftOp, rightOp); } } @@ -74,7 +70,7 @@ public boolean equals(final Object obj) { return true; } else if (obj instanceof FpMinExpr) { final FpMinExpr that = (FpMinExpr) obj; - return this.getLeftOp().equals(that.getLeftOp()) && this.getRightOp().equals(that.getRightOp()) && roundingMode == that.roundingMode; + return this.getLeftOp().equals(that.getLeftOp()) && this.getRightOp().equals(that.getRightOp()); } else { return false; } @@ -83,7 +79,7 @@ public boolean equals(final Object obj) { public LitExpr eval(Valuation val) { final FpLitExpr leftOpVal = (FpLitExpr) getLeftOp().eval(val); final FpLitExpr rightOpVal = (FpLitExpr) getRightOp().eval(val); - if (FpUtils.fpLitExprToBigFloat(roundingMode, leftOpVal).lessThan(FpUtils.fpLitExprToBigFloat(roundingMode, rightOpVal))) { + if (FpUtils.fpLitExprToBigFloat(null, leftOpVal).lessThan(FpUtils.fpLitExprToBigFloat(null, rightOpVal))) { return leftOpVal; } else { return rightOpVal; diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpRemExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpRemExpr.java index 9699a7cd9a..7762aa874b 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpRemExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpRemExpr.java @@ -21,7 +21,6 @@ import hu.bme.mit.theta.core.utils.FpUtils; import org.kframework.mpfr.BigFloat; -import static com.google.common.base.Preconditions.checkNotNull; import static hu.bme.mit.theta.core.utils.TypeUtils.castFp; import static hu.bme.mit.theta.core.utils.TypeUtils.checkAllTypesEqual; @@ -31,27 +30,19 @@ public final class FpRemExpr extends BinaryExpr { private static final String OPERATOR_LABEL = "fprem"; - private final FpRoundingMode roundingMode; - - private FpRemExpr(final FpRoundingMode roundingMode, final Expr leftOp, final Expr rightOp) { + private FpRemExpr(final Expr leftOp, final Expr rightOp) { super(leftOp, rightOp); checkAllTypesEqual(leftOp, rightOp); - checkNotNull(roundingMode); - this.roundingMode = roundingMode; } - public static FpRemExpr of(final FpRoundingMode roundingMode, final Expr leftOp, final Expr rightOp) { - return new FpRemExpr(roundingMode, leftOp, rightOp); + public static FpRemExpr of(final Expr leftOp, final Expr rightOp) { + return new FpRemExpr(leftOp, rightOp); } - public static FpRemExpr create(final FpRoundingMode roundingMode, final Expr leftOp, final Expr rightOp) { + public static FpRemExpr create(final Expr leftOp, final Expr rightOp) { final Expr newLeftOp = castFp(leftOp); final Expr newRightOp = castFp(rightOp); - return FpRemExpr.of(roundingMode, newLeftOp, newRightOp); - } - - public FpRoundingMode getRoundingMode() { - return roundingMode; + return FpRemExpr.of(newLeftOp, newRightOp); } @Override @@ -63,9 +54,9 @@ public FpType getType() { public FpLitExpr eval(final Valuation val) { final FpLitExpr leftOpVal = (FpLitExpr) getLeftOp().eval(val); final FpLitExpr rightOpVal = (FpLitExpr) getRightOp().eval(val); - BigFloat leftFloat = FpUtils.fpLitExprToBigFloat(roundingMode, leftOpVal); - BigFloat rightFloat = FpUtils.fpLitExprToBigFloat(roundingMode, rightOpVal); - BigFloat remainder = leftFloat.remainder(rightFloat, FpUtils.getMathContext(this.getType(), roundingMode)); + BigFloat leftFloat = FpUtils.fpLitExprToBigFloat(null, leftOpVal); + BigFloat rightFloat = FpUtils.fpLitExprToBigFloat(null, rightOpVal); + BigFloat remainder = leftFloat.remainder(rightFloat, FpUtils.getMathContext(this.getType(), null)); return FpUtils.bigFloatToFpLitExpr(remainder, this.getType()); } @@ -77,7 +68,7 @@ public FpRemExpr with(final Expr leftOp, final Expr rightOp) { if (leftOp == getLeftOp() && rightOp == getRightOp()) { return this; } else { - return FpRemExpr.of(roundingMode, leftOp, rightOp); + return FpRemExpr.of(leftOp, rightOp); } } @@ -97,7 +88,7 @@ public boolean equals(final Object obj) { return true; } else if (obj instanceof FpRemExpr) { final FpRemExpr that = (FpRemExpr) obj; - return this.getLeftOp().equals(that.getLeftOp()) && this.getRightOp().equals(that.getRightOp()) && roundingMode == that.roundingMode; + return this.getLeftOp().equals(that.getLeftOp()) && this.getRightOp().equals(that.getRightOp()); } else { return false; } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java index 0ef4a97117..ac3bbf7345 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprUtils.java @@ -21,11 +21,14 @@ import java.util.Collection; import java.util.Collections; import hu.bme.mit.theta.common.container.Containers; + +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import hu.bme.mit.theta.core.decl.ConstDecl; import hu.bme.mit.theta.core.decl.Decl; import hu.bme.mit.theta.core.decl.ParamDecl; import hu.bme.mit.theta.core.decl.VarDecl; @@ -160,6 +163,59 @@ public static Set> getVars(final Iterable> exprs) { return vars; } + /** + * Collect constants of an expression into a given collection. + * + * @param expr Expression + * @param collectTo Collection where the constants should be put + */ + public static void collectConstants(final Expr expr, final Collection> collectTo) { + if (expr instanceof RefExpr) { + final RefExpr refExpr = (RefExpr) expr; + final Decl decl = refExpr.getDecl(); + if (decl instanceof ConstDecl) { + final ConstDecl constDecl = (ConstDecl) decl; + collectTo.add(constDecl); + return; + } + } + expr.getOps().forEach(op -> collectConstants(op, collectTo)); + } + + /** + * Collect constants from expressions into a given collection. + * + * @param exprs Expressions + * @param collectTo Collection where the constants should be put + */ + public static void collectConstants(final Iterable> exprs, final Collection> collectTo) { + exprs.forEach(e -> collectConstants(e, collectTo)); + } + + /** + * Get constants of an expression. + * + * @param expr Expression + * @return Set of constants appearing in the expression + */ + public static Set> getConstants(final Expr expr) { + final Set> consts = new HashSet<>(); + collectConstants(expr, consts); + return consts; + } + + /** + * Get constants of expressions. + * + * @param exprs Expressions + * @return Set of constants appearing in the expressions + */ + public static Set> getConstants(final Iterable> exprs) { + final Set> consts = new HashSet<>(); + collectConstants(exprs, consts); + return consts; + } + /** * Get indexed variables of an expression. * diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/FpUtils.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/FpUtils.java index f45b5a01e1..666cf09f69 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/FpUtils.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/FpUtils.java @@ -9,6 +9,10 @@ import java.math.BigInteger; import java.math.RoundingMode; +import static hu.bme.mit.theta.core.type.fptype.FpExprs.NaN; +import static hu.bme.mit.theta.core.type.fptype.FpExprs.NegativeInfinity; +import static hu.bme.mit.theta.core.type.fptype.FpExprs.PositiveInfinity; + public final class FpUtils { private FpUtils() { } @@ -36,11 +40,11 @@ public static BigFloat fpLitExprToBigFloat(final FpRoundingMode roundingMode, fi public static FpLitExpr bigFloatToFpLitExpr(final BigFloat bigFloat, final FpType type) { if (bigFloat.isNaN()) { - return FpLitExpr.NaN(type); + return NaN(type); } else if (bigFloat.isInfinite() && bigFloat.greaterThan(BigFloat.zero(type.getSignificand()))) { - return FpLitExpr.PositiveInfinity(type); + return PositiveInfinity(type); } else if (bigFloat.isInfinite() && bigFloat.lessThan(BigFloat.zero(type.getSignificand()))) { - return FpLitExpr.NegativeInfinity(type); + return NegativeInfinity(type); } else { final var minExponent = -(1L << (type.getExponent() - 1)) + 2; final var maxExponent = (1L << (type.getExponent() - 1)) - 1; @@ -58,6 +62,10 @@ public static FpLitExpr bigFloatToFpLitExpr(final BigFloat bigFloat, final FpTyp } public static RoundingMode getMathContextRoundingMode(final FpRoundingMode roundingMode) { + if(roundingMode == null) { + return RoundingMode.UNNECESSARY; + } + switch (roundingMode) { case RNE: return RoundingMode.HALF_EVEN; diff --git a/subprojects/common/core/src/testFixtures/java/hu/bme/mit/theta/core/utils/FpTestUtils.java b/subprojects/common/core/src/testFixtures/java/hu/bme/mit/theta/core/utils/FpTestUtils.java index a8cf72172c..783b844dc5 100644 --- a/subprojects/common/core/src/testFixtures/java/hu/bme/mit/theta/core/utils/FpTestUtils.java +++ b/subprojects/common/core/src/testFixtures/java/hu/bme/mit/theta/core/utils/FpTestUtils.java @@ -114,14 +114,14 @@ private static Collection BasicOperations() { {FpLtExpr.class, Bool(true), Lt(Fp16("7.14"), Fp16("7.15"))}, {FpLtExpr.class, Bool(false), Lt(Fp16("7.14"), Fp16("7.14"))}, {FpLtExpr.class, Bool(false), Lt(Fp16("-7.14"), Fp16("-7.15"))}, - {FpMaxExpr.class, Fp16("2.1"), Max(RNE, Fp16("-2.1"), Fp16("2.1"))}, - {FpMaxExpr.class, Fp16("2.1"), Max(RNE, Fp16("1.9"), Fp16("2.1"))}, - {FpMinExpr.class, Fp16("-2.1"), Min(RNE, Fp16("-2.1"), Fp16("2.1"))}, - {FpMinExpr.class, Fp16("1.9"), Min(RNE, Fp16("1.9"), Fp16("2.1"))}, - {FpRemExpr.class, Fp16("0.1"), Rem(RNE, Fp16("4.3"), Fp16("2.1"))}, - {FpRemExpr.class, Fp16("-0.1"), Rem(RNE, Fp16("-4.3"), Fp16("2.1"))}, - {FpRemExpr.class, Fp16("0.1"), Rem(RNE, Fp16("4.3"), Fp16("-2.1"))}, - {FpRemExpr.class, Fp16("-0.1"), Rem(RNE, Fp16("-4.3"), Fp16("-2.1"))}, + {FpMaxExpr.class, Fp16("2.1"), Max(Fp16("-2.1"), Fp16("2.1"))}, + {FpMaxExpr.class, Fp16("2.1"), Max(Fp16("1.9"), Fp16("2.1"))}, + {FpMinExpr.class, Fp16("-2.1"), Min(Fp16("-2.1"), Fp16("2.1"))}, + {FpMinExpr.class, Fp16("1.9"), Min(Fp16("1.9"), Fp16("2.1"))}, + {FpRemExpr.class, Fp16("0.1"), Rem(Fp16("4.3"), Fp16("2.1"))}, + {FpRemExpr.class, Fp16("-0.1"), Rem(Fp16("-4.3"), Fp16("2.1"))}, + {FpRemExpr.class, Fp16("0.1"), Rem(Fp16("4.3"), Fp16("-2.1"))}, + {FpRemExpr.class, Fp16("-0.1"), Rem(Fp16("-4.3"), Fp16("-2.1"))}, {FpRoundToIntegralExpr.class, Fp16("2.0"), RoundToIntegral(RNE, Fp16("2.49"))}, {FpRoundToIntegralExpr.class, Fp16("-10.0"), RoundToIntegral(RNE, Fp16("-10.49"))}, {FpSqrtExpr.class, Fp16("2.1"), Sqrt(RNE, Fp16("4.41"))}, diff --git a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/ItpPattern.java b/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/ItpPattern.java deleted file mode 100644 index 5d908b626c..0000000000 --- a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/ItpPattern.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 Budapest University of Technology and Economics - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package hu.bme.mit.theta.solver; - -import java.util.Collection; - -/** - * Interface for an element of an interpolation pattern. - * For example, in sequence interpolation the patterns form a linear chain. - */ -public interface ItpPattern { - - /** - * Get the current marker. - * - * @return Marker - */ - ItpMarker getMarker(); - - /** - * Get the parent pattern. - * - * @return Parent - */ - ItpPattern getParent(); - - /** - * Get child patterns. - * - * @return Children - */ - Collection getChildren(); - - /** - * Create a child for the current pattern with a given marker. - * - * @param marker Marker - * @return Child - */ - ItpPattern createChild(final ItpMarker marker); - -} diff --git a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/Solver.java b/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/Solver.java deleted file mode 100644 index 35eba40709..0000000000 --- a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/Solver.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2017 Budapest University of Technology and Economics - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package hu.bme.mit.theta.solver; - -import java.util.Collection; - -import hu.bme.mit.theta.core.model.Valuation; -import hu.bme.mit.theta.core.type.Expr; -import hu.bme.mit.theta.core.type.booltype.BoolType; - -/** - * Common interface for SMT solvers. - * - * Use the {@link #add(Expr)} or {@link #track(Expr)} methods to add expressions to the solver. - * Then use {@link #check()} method to check their satisfiability. The result can be queried by - * {@link #getStatus()}. If the expressions are satisfiable, a satisfying assignment can be - * obtained by {@link #getModel()}. - * - * The solver can also support incremental solving by {@link #push()} and {@link #pop()}. - */ -public interface Solver { - - /** - * Add an expression to the solver. - * - * @param assertion Expression to be added - */ - void add(Expr assertion); - - /** - * Add a collection of expressions to the solver. - * - * @param assertions Expressions to be added - */ - default void add(final Iterable> assertions) { - for (final Expr assertion : assertions) { - add(assertion); - } - } - - /** - * Add and track an expression. Required to calculate unsat cores. - * If you don't need unsat cores you can simply use {@link #add(Expr)}. - * - * @param assertion Expression to be tracked - */ - void track(Expr assertion); - - /** - * Add and track a collection of expressions. - * - * @param assertions Expressions to be tracked - */ - default void track(final Iterable> assertions) { - for (final Expr assertion : assertions) { - track(assertion); - } - } - - /** - * Check if the currently added expressions are satisfiable. - * - * @return Status - */ - SolverStatus check(); - - /** - * Push the current solver state. When calling {@link #pop()}, all expressions added after - * the last push will be removed. - */ - void push(); - - /** - * Remove expressions added after the previous n {@link #push()} calls. - * - * @param n - */ - void pop(final int n); - - /** - * Remove expressions added after the previous {@link #push()} call. - */ - default void pop() { - pop(1); - } - - /** - * Reset the solver state. It should be only used as a last resort. Try using - * {@link #push()} and {@link #pop()} instead. See also {@link hu.bme.mit.theta.solver.utils.WithPushPop} - * which implements try with resources pattern. - */ - void reset(); - - /** - * Get the current status of the solver. - * - * @return Status - */ - SolverStatus getStatus(); - - /** - * Get the satisfying assignment for the currently added expressions. - * Should only be called if {@link #check()} was already called and - * the result is SAT. - * - * @return Satisfying assignment - */ - Valuation getModel(); - - /** - * Get an unsat core, i.e., a (not necessarily) minimal subset of the - * expressions that are already unsatisfiable. It only works if expressions - * were added by {@link #track(Expr)} or {@link #track(Iterable)} instead of - * {@link #add(Expr)} or {@link #add(Iterable)}. Furthermore, it should only - * be called if {@link #check()} was already called and the result is UNSAT. - * - * @return Unsat core - */ - Collection> getUnsatCore(); - - /** - * Get the currently added expressions. - * - * @return Expressions - */ - Collection> getAssertions(); -} diff --git a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/impl/ItpPatternImpl.java b/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/impl/ItpPatternImpl.java deleted file mode 100644 index 7f91ffb180..0000000000 --- a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/impl/ItpPatternImpl.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Budapest University of Technology and Economics - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package hu.bme.mit.theta.solver.impl; - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -import hu.bme.mit.theta.solver.ItpMarker; -import hu.bme.mit.theta.solver.ItpPattern; - -public class ItpPatternImpl implements ItpPattern { - - private ItpPattern parent; - private final List children; - - private final ItpMarker marker; - - public ItpPatternImpl(final ItpMarker marker) { - this.marker = checkNotNull(marker); - children = new LinkedList<>(); - } - - @Override - public ItpMarker getMarker() { - return marker; - } - - @Override - public ItpPattern getParent() { - return parent; - } - - @Override - public Collection getChildren() { - return Collections.unmodifiableCollection(children); - } - - @Override - public ItpPattern createChild(final ItpMarker marker) { - checkNotNull(marker); - final ItpPatternImpl child = new ItpPatternImpl(marker); - children.add(child); - child.parent = this; - return child; - } - -} diff --git a/subprojects/solver/solver-smtlib-cli/README.md b/subprojects/solver/solver-smtlib-cli/README.md new file mode 100644 index 0000000000..7754883937 --- /dev/null +++ b/subprojects/solver/solver-smtlib-cli/README.md @@ -0,0 +1,82 @@ +## Overview + +**This project is in alpha state. Expect breaking changes in the near future!** + +The `solver-smtlib-cli` project is an executable (command line) tool for managing the SMT-LIB compatible solvers. +For more information about the SMT-LIB compatibility in Theta, take a look at the [`solver-smtlib`](../solver-smtlib/README.md) project. + +### Related projects + +* [`solver`](../solver/README.md): Contains the generic utilities and solver interface of Theta. +* [`solver-smtlib`](../solver-smtlib/README.md): Implements Theta's solver interface to support SMT-LIB compatible solvers. + +## Using the tool + +1. First, get the tool. + * The easiest way is to download a [pre-built release](https://github.com/ftsrg/theta/releases). + * You can also [build](../../../doc/Build.md) the tool yourself. The runnable jar file will appear under _build/libs/_ with the name _theta-solver-smtlib-cli-\-all.jar_, you can simply rename it to _theta-solver-smtlib-cli.jar_.. +2. Running the tool requires Java (JRE) 11. +3. The tool can be executed with `java -jar theta-solver-smtlib-cli.jar [MAIN ARGUMENTS] [COMMAND] [ARGUMENTS]`. + * If no arguments are given, a help screen is displayed about the arguments and their possible values. + More information can also be found below. + +## Arguments + +The tool supports the following main arguments. These are all optional: +* `--home`: Sets the path of the solver registry. It defaults to a folder named _.theta_ in the home folder of the current user. +* `--help`: Prints the help message. + +The tool supports the following commands and their arguments: + +* `install :`: Installs a solver with the given name and version to the current solver registry. The __ identifies the driver (a.k.a. the type) of the solver, while the __ identifies the version of the solver to install. (See the list of supported solvers and their versions with `list-supported`) + * `--name`: Install the solver version under this custom name (:), instead of the default (:) + * `--solver-path`: The path of the solver to install. The solver will not be downloaded, instead the binary on this path will be used. Caveat emptor: the version must be specified correctly, there is no automatic detection. + * `--tempt-murphy`: Enables the installation of unsupported solver versions. If you enable this, you can expect things to break, as these solvers were not tested with Theta at all! +* `install-generic`: Installs an SMT-LIB compatible solver with the generic driver. For more information see the [Generic driver](#Generic-driver) section. + * `--solver-path` **(required)**: Denotes the path to the binary of the solver. + * `--solver-args`: The command line arguments to pass to the generic solver. + * `--name` **(required)**: Install the solver under this custom name (generic:) +* `uninstall :`: Uninstalls a solver with the given name and version from the current solver registry. (See the list of installed solvers and their versions with `list-installed`) +* `rename :`: Renames one installed solver version. (See the list of installed solvers and their versions with `list-installed`) + * `--name` **(required)**: Rename the solver version to this custom name (:). +* `get-info :`: Gets the stored information about the solver with the given name and version in the current solver registry. (See the list of installed solvers and their versions with `list-installed`) +* `edit-args :`: Edits the command line arguments of the solver with the given name and version. The command opens a txt file that stores the command line arguments to edit with the default editor, or prints the path of the file to edit if the default editor is not applicable (e.g. CLI). (See the list of installed solvers and their versions with `list-installed`) + * `--print`: If given, the path of the file to edit will be printed instead of opening. +* `list-installed []`: Lists the installed solvers and their versions. If the __ is given, only the versions of that solver will be listed. +* `list-supported []`: Lists the supported solvers and their versions. If the __ is given, only the versions of that solver will be listed. + +### For developer usage + +The following main arguments are targeted for developers: + +| Flag | Description | +|---|---| +| `--stacktrace` | Print full stack trace for exceptions. | +| `--loglevel` | `--loglevel`: Detailedness of logging. Possible values (from the least to the most detailed): `RESULT`, `MAINSTEP`, `SUBSTEP` (default), `INFO`, `DETAIL`, `VERBOSE` | + +## Generic driver + +It is possible to integrate any fully SMT-LIB compatible solvers with Theta using the generic driver. To do this, the following information is needed: + +* The path to the binary of the solver. If the solver does not have a binary (e.g. it is Java based), then a bash script should be created that serves as an entry point for the solver. +* The command line arguments to pass to the solver. These arguments should configure the solver for the following behavior: + * It must read its input from the standard input. + * It must write its output to the standard output. Moreover, it should flush the standard output after every line. + * It **must not** write anything else to the standard output that is not part of the SMT-LIB standard. The first command the driver issues is the `(set-option :print-success true)`, so the first characters the binary outputs should be the result of this command! + +For more information see the default configurations of the supported solvers. + +## Supported solvers + +Currently, the following solvers are supported. + +* Boolector +* CVC4 +* MathSAT5: Supported with interpolation +* Princess: Supported with interpolation +* SMTInterpol: Supported with interpolation +* Yices2: _Partial, error-prone support_ +* Z3 + * `4.4.0` - `4.6.0`: Supported with interpolation. + * `4.7.1` - : Supported without interpolation. Interpolation was removed in `4.7.0`. +* Generic: Any other solver that can communicate with SMT-LIBv2 standard. See section Generic driver for more details. \ No newline at end of file diff --git a/subprojects/common/solver-z3/bin/.gitignore b/subprojects/solver/solver-smtlib-cli/bin/.gitignore similarity index 100% rename from subprojects/common/solver-z3/bin/.gitignore rename to subprojects/solver/solver-smtlib-cli/bin/.gitignore diff --git a/subprojects/solver/solver-smtlib-cli/build.gradle.kts b/subprojects/solver/solver-smtlib-cli/build.gradle.kts new file mode 100644 index 0000000000..ccc506edcb --- /dev/null +++ b/subprojects/solver/solver-smtlib-cli/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + id("java-common") + id("cli-tool") +} + +dependencies { + compile(project(":theta-solver-smtlib")) +} + +application { + mainClassName = "hu.bme.mit.theta.solver.smtlib.cli.SmtLibCli" +} diff --git a/subprojects/solver/solver-smtlib-cli/src/main/java/hu/bme/mit/theta/solver/smtlib/cli/SmtLibCli.java b/subprojects/solver/solver-smtlib-cli/src/main/java/hu/bme/mit/theta/solver/smtlib/cli/SmtLibCli.java new file mode 100644 index 0000000000..ba65077f26 --- /dev/null +++ b/subprojects/solver/solver-smtlib-cli/src/main/java/hu/bme/mit/theta/solver/smtlib/cli/SmtLibCli.java @@ -0,0 +1,397 @@ +package hu.bme.mit.theta.solver.smtlib.cli; + +import com.beust.jcommander.IParameterValidator; +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.Parameters; +import hu.bme.mit.theta.common.Tuple2; +import hu.bme.mit.theta.common.logging.ConsoleLogger; +import hu.bme.mit.theta.common.logging.Logger; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException; +import hu.bme.mit.theta.solver.smtlib.SmtLibSolverManager; + +import java.awt.Desktop; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +public class SmtLibCli { + private static final String JAR_NAME = "theta-solver-smtlib-cli.jar"; + private final String[] args; + + private Logger logger; + + static class MainParams { + @Parameter(names = "--home", description = "The path of the solver registry") + String home = SmtLibSolverManager.HOME.toAbsolutePath().toString(); + + @Parameter(names = "--loglevel", description = "Detailedness of logging") + Logger.Level logLevel = Logger.Level.MAINSTEP; + + @Parameter(names = "--stacktrace", description = "Prints the stacktrace in case of an error") + private boolean stacktrace = false; + + @Parameter(names = "--help", help = true, description = "Prints this help message") + private boolean help = false; + } + + interface Command { + String getCommand(); + void handle(SmtLibSolverManager smtLibSolverManager, Logger logger) throws SmtLibSolverInstallerException; + } + + @Parameters(commandDescription = "Installs the solver") + static class InstallCommand implements Command { + static final String COMMAND = "install"; + + @Parameter(description = "The solver to install (:)", validateWith = SolverNameAndVersionValidator.class, required = true) + String solver; + + @Parameter(names = "--name", description = "Install the solver version under this custom name (:), instead of the default (:)") + String name; + + @Parameter(names = "--solver-path", description = "The path of the solver to install. The solver will not be downloaded, instead the binary on this path will be used. Caveat emptor: the version must be specified correctly, there is no automatic detection.") + String solverPath; + + @Parameter(names = "--tempt-murphy", description = "Allows the installation of unsupported solver version") + boolean temptMurphy = false; + + @Override + public String getCommand() { + return COMMAND; + } + + @Override + public void handle(final SmtLibSolverManager smtLibSolverManager, final Logger logger) throws SmtLibSolverInstallerException{ + final var solver = decodeVersionString(this.solver); + + if(solver.get1().equals(smtLibSolverManager.getGenericInstallerName())) { + logger.write(Logger.Level.RESULT, "To install a generic solver, use the \"%s\" command", InstallGenericCommand.COMMAND); + return; + } + + if(name != null) { + smtLibSolverManager.install(solver.get1(), solver.get2(), name, solverPath != null ? Path.of(solverPath) : null, temptMurphy); + } + else { + smtLibSolverManager.install(solver.get1(), solver.get2(), solver.get2(), solverPath != null ? Path.of(solverPath) : null, temptMurphy); + } + } + } + + @Parameters(commandDescription = "Installs a generic solver") + static class InstallGenericCommand implements Command { + static final String COMMAND = "install-generic"; + + @Parameter(names = "--solver-path", description = "The path of the generic solver to install", required = true) + String solverPath; + + @Parameter(names = "--solver-args", description = "The arguments of the generic solver to invoke with") + String solverArgs; + + @Parameter(names = "--name", description = "Install the solver version under this custom name (:), instead of the default (:)", required = true) + String name; + + @Override + public String getCommand() { + return COMMAND; + } + + @Override + public void handle(final SmtLibSolverManager smtLibSolverManager, final Logger logger) throws SmtLibSolverInstallerException { + smtLibSolverManager.installGeneric( + name, + Path.of(solverPath), + (solverArgs == null ? "" : solverArgs).split(" ") + ); + } + } + + @Parameters(commandDescription = "Uninstalls the solver") + static class UninstallCommand implements Command { + static final String COMMAND = "uninstall"; + + @Parameter(description = "The solver to uninstall (:)", validateWith = SolverNameAndVersionValidator.class, required = true) + String solver; + + @Override + public String getCommand() { + return COMMAND; + } + + @Override + public void handle(SmtLibSolverManager smtLibSolverManager, Logger logger) throws SmtLibSolverInstallerException { + final var solver = decodeVersionString(this.solver); + smtLibSolverManager.uninstall(solver.get1(), solver.get2()); + } + } + + @Parameters(commandDescription = "Renames one installed solver version") + static class RenameCommand implements Command { + static final String COMMAND = "rename"; + + @Parameter(description = "The solver to reinstall (:)", validateWith = SolverNameAndVersionValidator.class, required = true) + String solver; + + @Parameter(names = "--name", description = "Rename the solver version to this custom name (:).", required = true) + String name; + + @Override + public String getCommand() { + return COMMAND; + } + + @Override + public void handle(SmtLibSolverManager smtLibSolverManager, Logger logger) throws SmtLibSolverInstallerException { + final var solver = decodeVersionString(this.solver); + smtLibSolverManager.rename(solver.get1(), solver.get2(), name); + } + } + + @Parameters(commandDescription = "Prints info about the solver") + static class GetInfoCommand implements Command { + static final String COMMAND = "get-info"; + + @Parameter(description = "The solver to print info about (:)", validateWith = SolverNameAndVersionValidator.class, required = true) + String solver; + + @Override + public String getCommand() { + return COMMAND; + } + + @Override + public void handle(SmtLibSolverManager smtLibSolverManager, Logger logger) throws SmtLibSolverInstallerException { + final var solver = decodeVersionString(this.solver); + final var info = smtLibSolverManager.getInfo(solver.get1(), solver.get2()); + logger.write(Logger.Level.RESULT, "%s\n", info); + } + } + + @Parameters(commandDescription = "Edits the runtime arguments passed to the solver") + static class EditArgsCommand implements Command { + static final String COMMAND = "edit-args"; + + @Parameter(description = "The solver, whose runtime arguments are to be edited (:)", validateWith = SolverNameAndVersionValidator.class, required = true) + String solver; + + @Parameter(names = "--print", description = "Print the path instead of opening it for editing") + boolean print = false; + + @Override + public String getCommand() { + return COMMAND; + } + + @Override + public void handle(SmtLibSolverManager smtLibSolverManager, Logger logger) throws SmtLibSolverInstallerException { + final var solver = decodeVersionString(this.solver); + final var argsFilePath = smtLibSolverManager.getArgsFile(solver.get1(), solver.get2()); + + if(print) { + logger.write(Logger.Level.RESULT, String.format("%s\n", argsFilePath.toAbsolutePath())); + } + else if(Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.EDIT)) { + try { + Desktop.getDesktop().edit(argsFilePath.toFile()); + } catch (IOException e) { + throw new SmtLibSolverInstallerException(e); + } + } + else { + logger.write(Logger.Level.MAINSTEP, "Open the following text file in your favourite editor, and edit the content:\n"); + logger.write(Logger.Level.RESULT, String.format("%s\n", argsFilePath.toAbsolutePath())); + } + } + } + + @Parameters(commandDescription = "Lists installed solvers and their versions") + static class ListInstalledCommand implements Command { + static final String COMMAND = "list-installed"; + + @Parameter(description = "The solver, whose installed versions are to be listed ()", validateWith = SolverNameValidator.class) + String solver; + + @Override + public String getCommand() { + return COMMAND; + } + + @Override + public void handle(SmtLibSolverManager smtLibSolverManager, Logger logger) throws SmtLibSolverInstallerException { + if(solver != null) { + logger.write(Logger.Level.MAINSTEP, "The currently installed versions of solver %s are: \n", solver); + smtLibSolverManager.getInstalledVersions(solver).forEach(version -> { + logger.write(Logger.Level.RESULT, "\t%s:%s\n", solver, version); + }); + } + else { + logger.write(Logger.Level.MAINSTEP, "The currently installed solvers are: \n"); + smtLibSolverManager.getInstalledVersions().forEach(solver -> { + solver.get2().forEach(version -> { + logger.write(Logger.Level.RESULT, "\t%s:%s\n", solver.get1(), version); + }); + }); + } + } + } + + @Parameters(commandDescription = "Lists supported solvers and their versions") + static class ListSupportedCommand implements Command { + static final String COMMAND = "list-supported"; + + @Parameter(description = "The solver, whose supported versions are to be listed ()", validateWith = SolverNameValidator.class) + String solver; + + @Override + public String getCommand() { + return COMMAND; + } + + @Override + public void handle(SmtLibSolverManager smtLibSolverManager, Logger logger) throws SmtLibSolverInstallerException { + if(solver != null) { + logger.write(Logger.Level.MAINSTEP, "The currently supported versions of solver %s are: \n", solver); + smtLibSolverManager.getSupportedVersions(solver).forEach(version -> { + logger.write(Logger.Level.RESULT, "\t%s:%s\n", solver, version); + }); + } + else { + logger.write(Logger.Level.MAINSTEP, "The currently supported solvers are: \n"); + smtLibSolverManager.getSupportedVersions().forEach(solver -> { + solver.get2().forEach(version -> { + logger.write(Logger.Level.RESULT, "\t%s:%s\n", solver.get1(), version); + }); + }); + } + } + } + + public SmtLibCli(final String[] args) { + this.args = args; + } + + public static void main(final String[] args) { + final SmtLibCli mainApp = new SmtLibCli(args); + mainApp.run(); + } + + private void run() { + final var mainParams = new MainParams(); + List commands = List.of( + new InstallCommand(), + new InstallGenericCommand(), + new UninstallCommand(), + new RenameCommand(), + new GetInfoCommand(), + new EditArgsCommand(), + new ListInstalledCommand(), + new ListSupportedCommand() + ); + + final var jcBuilder = JCommander.newBuilder().addObject(mainParams); + commands.forEach(command -> jcBuilder.addCommand(command.getCommand(), command)); + final var jc = jcBuilder.programName(JAR_NAME).build(); + + try { + jc.parse(args); + logger = new ConsoleLogger(mainParams.logLevel); + } catch (final ParameterException ex) { + System.out.println("Invalid parameters, details:"); + System.out.println(ex.getMessage()); + ex.usage(); + return; + } + + if(mainParams.help) { + jc.usage(); + return; + } + + try { + final var homePath = createIfNotExists(Path.of(mainParams.home)); + final var smtLibSolverManager = SmtLibSolverManager.create(homePath, logger); + + if(jc.getParsedCommand() == null) { + logger.write(Logger.Level.RESULT, "Missing command\n"); + jc.usage(); + return; + } + + final var parsedCommand = jc.getParsedCommand(); + for(final var command : commands) { + if(command.getCommand().equals(parsedCommand)) { + command.handle(smtLibSolverManager, logger); + return; + } + } + logger.write(Logger.Level.RESULT, "Unknown command\n"); + jc.usage(); + } + catch (SmtLibSolverInstallerException e) { + logger.write(Logger.Level.RESULT, "%s\n", e.getMessage()); + if(mainParams.stacktrace) { + printError(e, true); + } + } + catch (IOException e) { + printError(e, mainParams.stacktrace); + } + } + + private static Tuple2 decodeVersionString(final String version) { + return Tuple2.of(SmtLibSolverManager.getSolverName(version), SmtLibSolverManager.getSolverVersion(version)); + } + + private Path createIfNotExists(final Path path) throws IOException { + if(!Files.exists(path)) { + Files.createDirectory(path); + } + return path; + } + + private void printError(final Throwable ex, final boolean printStackTrace) { + final String message = ex.getMessage() == null ? "" : ex.getMessage(); + logger.write(Logger.Level.RESULT, "%s occurred, message: %s%n", ex.getClass().getSimpleName(), message); + if (printStackTrace) { + final StringWriter errors = new StringWriter(); + ex.printStackTrace(new PrintWriter(errors)); + logger.write(Logger.Level.RESULT, "Trace:%n%s%n", errors.toString()); + } + else { + logger.write(Logger.Level.RESULT, "Use --stacktrace for stack trace%n"); + } + } + + public static class SolverNameValidator implements IParameterValidator { + @Override + public void validate(String name, String value) throws ParameterException { + if(!value.matches("[a-zA-Z0-9]+")) { + throw new ParameterException( + String.format("Invalid solver name in parameter %s", name) + ); + } + } + } + + public static class SolverNameAndVersionValidator implements IParameterValidator { + @Override + public void validate(String name, String value) throws ParameterException { + final var versionArr = value.split(":"); + + if(versionArr.length != 2) { + throw new ParameterException(String.format("Invalid version string %s in parameter %s", value, name)); + } + + if(!versionArr[0].matches("[a-zA-Z0-9]+") || !versionArr[1].matches("[a-zA-Z0-9-._]+")) { + throw new ParameterException( + String.format("Invalid version string %s in parameter %s", value, name) + ); + } + } + } +} diff --git a/subprojects/solver/solver-smtlib/README.md b/subprojects/solver/solver-smtlib/README.md new file mode 100644 index 0000000000..dbde95da63 --- /dev/null +++ b/subprojects/solver/solver-smtlib/README.md @@ -0,0 +1,61 @@ +## Overview + +**This project is in alpha state. Expect breaking changes in the near future!** + +This project wraps a generic solver that supports the SMT-LIB2 interface into our common interfaces for solvers (located in the [solver](..) project). + +### Related projects + +* [`solver`](../solver/README.md): Contains the generic utilities and solver interface of Theta. +* [`solver-smtlib-cli`](../solver-smtlib-cli/README.md): Command line tool for managing the SMT-LIB compatible solvers. + +## SMT-LIB2 + +The [SMT-LIB2 standard](http://smtlib.cs.uiowa.edu/) is an international initiative, whose goal is to partly provide a common input and output language for SMT solvers. The standard is adapted by many SMT solvers, such as Z3, MathSAT, CVC4, etc... + +However, there are some limitation to this standard, both in terms of its content and its practical application by the solvers that support it: + +* There are no standard way of denoting an array literal in SMT-LIB. Each solver that support the said theory used to come up with a solution of their own in this matter. This lead to the creation of the `as-array` (Z3), and the `as const` (CVC4, MathSAT) constructs. Nowadays, solvers tend to support the `as const` construct (event the newer versions of Z3). +* There are no standard way of denoting interpolation. Solvers that support interpolation (Z3, SMTInterpol, MathSAT) have their own extension added to SMT-LIB to support this feature. +* The support for the standard tends to be loose in some solvers. For example, MathSAT expects a numeric argument to `push` and `pop`, does not default to 1, like the standard. Moreover, MathSAT uses a custom output language when outputting models, that is not part of the standard. + +This list is unfortunately far from being complete, so one has to be careful when integrating a new solver with SMT-LIB support. + +## Architecture + +The issues of compatibility described in the earlier section warranted to be considered in the architecture. The component is designed to be a composite of subcomponents along well-defined interfaced that make it possible to replace complete subcomponents if for the sake of a solver. + +### Generic architecture interface + +In general, to have an SMT-LIB supporting solver, components implementing the following interfaces has to be developed: + +* `SmtLibTransformationManager`: Transforms anything in Theta to their SMT-LIB counterpart. It uses further components for this feature: + * `SmtLibTypeTransformer`: Transforms a Theta type to and SMT-LIB type. + * `SmtLibDeclTransformer`: Transforms a Theta declaration to an SMT-LIB declaration. + * `SmtLibExprTransformer`: Transforms a Theta expression to an SMT-LIB expression. + * `SmtLibSymbolTable`: Caches the Theta declarations and their SMT-LIB counterpart. +* `SmtLibTermTransformer`: Transforms an SMT-LIB expression to a Theta expression. It provides methods that ensure type-safety. +* `SmtLibSolverBinary`: Provides an interface to communicate with the binary of the solver. +* `SmtLibSolverInstaller`: An interface to support installation scripts and solver management. + +The SMT-LIB grammar that the component supports is defined using ANTLR. The ANTLR grammar can be found in the source folder (`SMTLIBv2.g4`). + +The components above are integrated into a working solver implementation with Theta's solver interface by the following classes: +* `SmtLibSolver`: Provides a solver implementing Theta's `Solver` and `UCSolver` interface supporting basic satisfiability checks, model querying and unsat core querying. +* `BaseSmtLibItpSolver`: Provides a solver implementing Theta's `ItpSolver` interface supporting interpolating solvers. **Note**: As interpolation is not part of the SMT-LIB standard, this class is abstract. Each solver supporting interpolation has to extend this class and configure it properly. + +### Generic interface implementation + +The interfaces above have a default, generic implementation that works with solvers that follow the SMT-LIB standard fully. These generic classes can be found in the `hu.bme.mit.theta.solver.smtlib.impl.generic` package. + +### Solver specific implementations + +Right now, the solver-smtlib subproject supports the following solvers. Each package contains the specialization of the interfaces above that communicate with the said solver: + +* **Boolector** (`hu.bme.mit.theta.solver.smtlib.impl.boolector`): The solver specializes in bitvector and array theories, but lacks support for others. It cannot interpolate or produce unsat cores, so it can only be used for abstraction. +* **CVC4** (`hu.bme.mit.theta.solver.smtlib.impl.cvc4`): The solver supports basic satisfiability problems for numerous theories (integers, rationals, quantifiers, arrays, bitvectors). +* **Mathsat5** (`hu.bme.mit.theta.solver.smtlib.impl.mathsat`): The solver supports basic satisfiability problems for numerous theories (integers, rationals, quantifiers, arrays, bitvectors, floating points). It also supports interpolation (binary, sequential and tree interpolation) with many of them. +* **Princess** (`hu.bme.mit.theta.solver.smtlib.impl.princess`): The solver supports basic satisfiability problems for numerous theories (integers, rationals, quantifiers, arrays, bitvectors). It also supports interpolation (binary, sequential and tree interpolation) with many of them. +* **SMTInterpol** (`hu.bme.mit.theta.solver.smtlib.impl.smtinterpol`): The solver specializes in integer and rational theories. It also supports interpolation (binary, sequential and tree interpolation) with them. +* **Yices2** (`hu.bme.mit.theta.solver.smtlib.impl.yices2`): The solver supports basic satisfiability problems for numerous theories (integers, rationals, quantifiers, arrays, bitvectors). +* **Z3** (`hu.bme.mit.theta.solver.smtlib.impl.z3`): The solver supports basic satisfiability problems for numerous theories (integers, rationals, quantifiers, arrays, bitvectors, functions), and supports interpolation as well (binary, sequential and tree interpolation) up to version 4.7. \ No newline at end of file diff --git a/subprojects/common/solver/bin/.gitignore b/subprojects/solver/solver-smtlib/bin/.gitignore similarity index 100% rename from subprojects/common/solver/bin/.gitignore rename to subprojects/solver/solver-smtlib/bin/.gitignore diff --git a/subprojects/solver/solver-smtlib/build.gradle.kts b/subprojects/solver/solver-smtlib/build.gradle.kts new file mode 100644 index 0000000000..ea9f63f693 --- /dev/null +++ b/subprojects/solver/solver-smtlib/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + id("java-common") + id("antlr-grammar") +} + +dependencies { + compile(project(":theta-common")) + compile(project(":theta-core")) + compile(project(":theta-solver")) + compile("org.apache.commons:commons-compress:1.20") + testImplementation(testFixtures(project(":theta-core"))) +} diff --git a/subprojects/solver/solver-smtlib/src/main/antlr/SMTLIBv2.g4 b/subprojects/solver/solver-smtlib/src/main/antlr/SMTLIBv2.g4 new file mode 100644 index 0000000000..0e5543397d --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/antlr/SMTLIBv2.g4 @@ -0,0 +1,777 @@ +/** + * SMT-LIB (v2.6) grammar + * + * Grammar is baesd on the following specification: + * http://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.6-r2017-07-18.pdf + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Julian Thome + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + **/ + +grammar SMTLIBv2; + +// Parser Rules Start + +response + : general_response_success + | general_response_unsupported + | general_response_error + | specific_success_response + ; + +general_response_success + : PS_Success + ; + +general_response_unsupported + : PS_Unsupported + ; + +general_response_error + : ParOpen PS_Error reason=String ParClose + ; + +specific_success_response + : check_sat_response + | get_unsat_core_response + | get_model_response + | get_interpolants_response_smtinterpol + ; + +check_sat_response + : value=PS_Sat + | value=PS_Unsat + | value=PS_Unknown + ; + +get_unsat_core_response + : ParOpen symbol* ParClose + ; + +get_model_response + : ParOpen PS_Model? model_response* ParClose + ; + +model_response + : model_response_fun + | model_response_fun_rec + | model_response_funs_rec + | model_response_mathsat + ; + +model_response_fun + : ParOpen CMD_DefineFun function_def ParClose + ; + +model_response_fun_rec + : ParOpen CMD_DefineFunRec function_def ParClose + ; + +model_response_funs_rec + : ParOpen CMD_DefineFunsRec ParOpen function_dec+ ParClose ParOpen term+ ParClose ParClose + ; + +model_response_mathsat + : ParOpen symbol term ParClose + ; + +function_def + : symbol ParOpen sorted_var* ParClose sort term + ; + +function_dec + : ParOpen symbol ParOpen sorted_var* ParClose sort ParClose + ; + +get_interpolants_response_smtinterpol + : ParOpen term* ParClose + ; + +// Parser Rules End + +// Parser Rules Start + +// Starting rule(s) + +simpleSymbol + : predefSymbol + | UndefinedSymbol + ; + +quotedSymbol + : QuotedSymbol + ; + +predefSymbol + : PS_Not + | PS_Bool + | PS_ContinuedExecution + | PS_Error + | PS_False + | PS_ImmediateExit + | PS_Incomplete + | PS_Logic + | PS_Memout + | PS_Sat + | PS_Success + | PS_Theory + | PS_True + | PS_Unknown + | PS_Unsupported + | PS_Unsat + ; + +predefKeyword + : PK_AllStatistics + | PK_AssertionStackLevels + | PK_Authors + | PK_Category + | PK_Chainable + | PK_Definition + | PK_DiagnosticOutputChannel + | PK_ErrorBehaviour + | PK_Extension + | PK_Funs + | PK_FunsDescription + | PK_GlobalDeclarations + | PK_InteractiveMode + | PK_Language + | PK_LeftAssoc + | PK_License + | PK_Named + | PK_Name + | PK_Notes + | PK_Pattern + | PK_PrintSuccess + | PK_ProduceAssertions + | PK_ProduceAssignments + | PK_ProduceModels + | PK_ProduceProofs + | PK_ProduceUnsatAssumptions + | PK_ProduceUnsatCores + | PK_RandomSeed + | PK_ReasonUnknown + | PK_RegularOutputChannel + | PK_ReproducibleResourceLimit + | PK_RightAssoc + | PK_SmtLibVersion + | PK_Sorts + | PK_SortsDescription + | PK_Source + | PK_Status + | PK_Theories + | PK_Values + | PK_Verbosity + | PK_Version + ; + +symbol + : simpleSymbol + | quotedSymbol + ; + +numeral + : Numeral + ; + +decimal + : Decimal + ; + +hexadecimal + : HexDecimal + ; + +binary + : Binary + ; + +string + : String + ; + +keyword + : predefKeyword + | Colon simpleSymbol + ; + +// S-expression + +spec_constant + : numeral + | decimal + | hexadecimal + | binary + | string + ; + + +s_expr + : spec_constant + | symbol + | keyword + | ParOpen s_expr* ParClose + ; + +// Identifiers + +index + : numeral + | symbol + ; + +identifier + : symbol + | ParOpen GRW_Underscore symbol index+ ParClose + ; + +// Attributes + +attribute_value + : spec_constant + | symbol + | ParOpen s_expr* ParClose + ; + +attribute + : keyword + | keyword attribute_value + ; + +// Sorts + +sort + : identifier + | ParOpen identifier sort+ ParClose + ; + + +// Terms and Formulas + +qual_identifier + : identifier + | ParOpen GRW_As identifier sort ParClose + ; + +var_binding + : ParOpen symbol term ParClose + ; + +sorted_var + : ParOpen symbol sort ParClose + ; + +pattern + : symbol + | ParOpen symbol symbol+ ParClose + ; + +match_case + : ParOpen pattern term ParClose + ; + +term + : spec_constant + | qual_identifier + | generic_term + | let_term + | forall_term + | exists_term + | match_term + | annotate_term + ; + +generic_term + : ParOpen qual_identifier term+ ParClose + ; + +let_term + : ParOpen GRW_Let ParOpen var_binding+ ParClose term ParClose + ; + +forall_term + : ParOpen GRW_Forall ParOpen sorted_var+ ParClose term ParClose + ; + +exists_term + : ParOpen GRW_Exists ParOpen sorted_var+ ParClose term ParClose + ; + +match_term + : ParOpen GRW_Match term ParOpen match_case+ ParClose ParClose + ; + +annotate_term + : ParOpen GRW_Exclamation term attribute+ ParClose + ; + +// Parser Rules End + +// Lexer Rules Start + +Comment + : Semicolon ~[\r\n]* -> skip + ; + +ParOpen + : '(' + ; + +ParClose + : ')' + ; + +Semicolon + : ';' + ; + +String + : '"' (PrintableCharNoDquote | WhiteSpaceChar)+ '"' + ; + +QuotedSymbol: + '|' (PrintableCharNoBackslash | WhiteSpaceChar)+ '|' + ; + + +// Predefined Symbols + +PS_Not + : 'not' + ; +PS_Bool + : 'Bool' + ; +PS_ContinuedExecution + : 'continued-execution' + ; +PS_Error + : 'error' + ; +PS_False + : 'false' + ; +PS_ImmediateExit + : 'immediate-exit' + ; +PS_Incomplete + : 'incomplete' + ; +PS_Logic + : 'logic' + ; +PS_Memout + : 'memout' + ; +PS_Model + : 'model' + ; +PS_Sat + : 'sat' + ; +PS_Success + : 'success' + ; +PS_Theory + : 'theory' + ; +PS_True + : 'true' + ; +PS_Unknown + : 'unknown' + ; +PS_Unsupported + : 'unsupported' + ; +PS_Unsat + : 'unsat' + ; + +// RESERVED Words + +// Command names + + +CMD_Assert + : 'assert' + ; +CMD_CheckSat + : 'check-sat' + ; +CMD_CheckSatAssuming + : 'check-sat-assuming' + ; +CMD_DeclareConst + : 'declare-const' + ; +CMD_DeclareDatatype + : 'declare-datatype' + ; +CMD_DeclareDatatypes + : 'declare-datatypes' + ; +CMD_DeclareFun + : 'declare-fun' + ; +CMD_DeclareSort + : 'declare-sort' + ; +CMD_DefineFun + : 'define-fun' + ; +CMD_DefineFunRec + : 'define-fun-rec' + ; +CMD_DefineFunsRec + : 'define-funs-rec' + ; +CMD_DefineSort + : 'define-sort' + ; +CMD_Echo + : 'echo' + ; +CMD_Exit + : 'exit' + ; +CMD_GetAssertions + : 'get-assertions' + ; +CMD_GetAssignment + : 'get-assignment' + ; +CMD_GetInfo + : 'get-info' + ; +CMD_GetModel + : 'get-model' + ; +CMD_GetOption + : 'get-option' + ; +CMD_GetProof + : 'get-proof' + ; +CMD_GetUnsatAssumptions + : 'get-unsat-assumptions' + ; +CMD_GetUnsatCore + : 'get-unsat-core' + ; +CMD_GetValue + : 'get-value' + ; +CMD_Pop + : 'pop' + ; +CMD_Push + : 'push' + ; +CMD_Reset + : 'reset' + ; +CMD_ResetAssertions + : 'reset-assertions' + ; +CMD_SetInfo + : 'set-info' + ; +CMD_SetLogic + : 'set-logic' + ; +CMD_SetOption + : 'set-option' + ; + + + + +// General reserved words + +GRW_Exclamation + : '!' + ; +GRW_Underscore + : '_' + ; +GRW_As + : 'as' + ; +GRW_Binary + : 'BINARY' + ; +GRW_Decimal + : 'DECIMAL' + ; +GRW_Exists + : 'exists' + ; +GRW_Hexadecimal + : 'HEXADECIMAL' + ; +GRW_Forall + : 'forall' + ; +GRW_Let + : 'let' + ; +GRW_Match + : 'match' + ; +GRW_Numeral + : 'NUMERAL' + ; +GRW_Par + : 'par' + ; +GRW_String + : 'string' + ; + +Numeral + : '0' + | [1-9] Digit* + ; + +Binary + : '#b' BinaryDigit+ + ; + +HexDecimal + : '#x' HexDigit+ + ; + +Decimal + : Numeral '.' '0'* Numeral + ; + + + +fragment HexDigit + : '0' .. '9' | 'a' .. 'f' | 'A' .. 'F' + ; + + +Colon + : ':' + ; + +fragment Digit + : [0-9] + ; + +fragment Sym + : 'a'..'z' + | 'A' .. 'Z' + | '+' + | '=' + | '/' + | '*' + | '%' + | '?' + | '!' + | '$' + | '-' + | '_' + | '~' + | '&' + | '^' + | '<' + | '>' + | '@' + | '.' + ; + + + +fragment BinaryDigit + : [01] + ; + +fragment PrintableChar + : '\u0020' .. '\u007E' + | '\u0080' .. '\uffff' + | EscapedSpace + ; + +fragment PrintableCharNoDquote + : '\u0020' .. '\u0021' + | '\u0023' .. '\u007E' + | '\u0080' .. '\uffff' + | EscapedSpace + ; + +fragment PrintableCharNoBackslash + : '\u0020' .. '\u005B' + | '\u005D' .. '\u007B' + | '\u007D' .. '\u007E' + | '\u0080' .. '\uffff' + | EscapedSpace + ; + +fragment EscapedSpace + : '""' + ; + +fragment WhiteSpaceChar + : '\u0009' + | '\u000A' + | '\u000D' + | '\u0020' + ; + +// Lexer Rules End + +// Predefined Keywords + + + +PK_AllStatistics + : ':all-statistics' + ; +PK_AssertionStackLevels + : ':assertion-stack-levels' + ; +PK_Authors + : ':authors' + ; +PK_Category + : ':category' + ; +PK_Chainable + : ':chainable' + ; +PK_Definition + : ':definition' + ; +PK_DiagnosticOutputChannel + : ':diagnostic-output-channel' + ; +PK_ErrorBehaviour + : ':error-behavior' + ; +PK_Extension + : ':extensions' + ; +PK_Funs + : ':funs' + ; +PK_FunsDescription + : ':funs-description' + ; +PK_GlobalDeclarations + : ':global-declarations' + ; +PK_InteractiveMode + : ':interactive-mode' + ; +PK_Language + : ':language' + ; +PK_LeftAssoc + : ':left-assoc' + ; +PK_License + : ':license' + ; +PK_Named + : ':named' + ; +PK_Name + : ':name' + ; +PK_Notes + : ':notes' + ; +PK_Pattern + : ':pattern' + ; +PK_PrintSuccess + : ':print-success' + ; +PK_ProduceAssertions + : ':produce-assertions' + ; +PK_ProduceAssignments + : ':produce-assignments' + ; +PK_ProduceModels + : ':produce-models' + ; +PK_ProduceProofs + : ':produce-proofs' + ; +PK_ProduceUnsatAssumptions + : ':produce-unsat-assumptions' + ; +PK_ProduceUnsatCores + : ':produce-unsat-cores' + ; +PK_RandomSeed + : ':random-seed' + ; +PK_ReasonUnknown + : ':reason-unknown' + ; +PK_RegularOutputChannel + : ':regular-output-channel' + ; +PK_ReproducibleResourceLimit + : ':reproducible-resource-limit' + ; +PK_RightAssoc + : ':right-assoc' + ; +PK_SmtLibVersion + : ':smt-lib-version' + ; +PK_Sorts + : ':sorts' + ; +PK_SortsDescription + : ':sorts-description' + ; +PK_Source + : ':source' + ; +PK_Status + : ':status' + ; +PK_Theories + : ':theories' + ; +PK_Values + : ':values' + ; +PK_Verbosity + : ':verbosity' + ; +PK_Version + : ':version' + ; + +UndefinedSymbol: + Sym (Digit | Sym)*; + +WS : [ \t\r\n]+ -> skip + ; \ No newline at end of file diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverManager.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverManager.java new file mode 100644 index 0000000000..3095b25ad9 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverManager.java @@ -0,0 +1,337 @@ +package hu.bme.mit.theta.solver.smtlib; + +import com.google.common.collect.ImmutableList; +import hu.bme.mit.theta.common.Tuple2; +import hu.bme.mit.theta.common.logging.Logger; +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.SolverBase; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.SolverManager; +import hu.bme.mit.theta.solver.UCSolver; +import hu.bme.mit.theta.solver.smtlib.impl.boolector.BoolectorSmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.impl.princess.PrincessSmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.impl.smtinterpol.SMTInterpolSmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.impl.yices2.Yices2SmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException; +import hu.bme.mit.theta.solver.smtlib.impl.cvc4.CVC4SmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.impl.mathsat.MathSATSmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.impl.z3.Z3SmtLibSolverInstaller; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +public final class SmtLibSolverManager extends SolverManager { + public static final Path HOME = Path.of(System.getProperty("user.home"), ".theta"); + + private static final Map> installerDeclarations = new HashMap<>(); + private static Tuple2> genericInstallerDeclaration; + + public static void registerInstaller(final String name, final Class decl) { + installerDeclarations.put(name, decl); + } + + public static void registerGenericInstaller(final String name, final Class decl) { + checkState(genericInstallerDeclaration == null); + genericInstallerDeclaration = Tuple2.of(name, decl); + } + + static { + registerInstaller("z3", Z3SmtLibSolverInstaller.class); + registerInstaller("cvc4", CVC4SmtLibSolverInstaller.class); + registerInstaller("mathsat", MathSATSmtLibSolverInstaller.class); + registerInstaller("yices2", Yices2SmtLibSolverInstaller.class); + registerInstaller("boolector", BoolectorSmtLibSolverInstaller.class); + registerInstaller("smtinterpol", SMTInterpolSmtLibSolverInstaller.class); + registerInstaller("princess", PrincessSmtLibSolverInstaller.class); + registerGenericInstaller("generic", GenericSmtLibSolverInstaller.class); + } + + public static String getSolverName(final String name) { + final var solverName = decodeSolverName(name, 0); + if(solverName != null) { + return solverName; + } + else { + throw new IllegalArgumentException("Invalid version string: " + name); + } + } + + public static String getSolverVersion(final String name) { + final var solverVersion = decodeSolverName(name, 1); + if(solverVersion != null) { + return solverVersion; + } + else { + throw new IllegalArgumentException("Invalid version string: " + name); + } + } + + private static String decodeSolverName(final String name, final int part) { + final var versionArr = name.split(":"); + + if(versionArr.length != 2) { + return null; + } + + return versionArr[part]; + } + + private final Path home; + private final Logger logger; + + private final Map installers; + private final Tuple2 genericInstaller; + + private boolean closed = false; + private final Set instantiatedSolvers; + + private SmtLibSolverManager(final Path home, final Logger logger) { + this.logger = logger; + checkNotNull(home); + checkArgument(Files.exists(home), "Home directory does not exist"); + + this.home = home; + + try { + this.genericInstaller = Tuple2.of( + genericInstallerDeclaration.get1(), + genericInstallerDeclaration.get2().getDeclaredConstructor(Logger.class).newInstance(logger) + ); + + this.installers = Stream.concat( + Stream.of(this.genericInstaller), + installerDeclarations.entrySet().stream() + .map(p -> { + try { + return Tuple2.of(p.getKey(), p.getValue().getDeclaredConstructor(Logger.class).newInstance(logger)); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + }) + ).collect(Collectors.toUnmodifiableMap(Tuple2::get1, Tuple2::get2)); + } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { + throw new RuntimeException(e); + } + + this.instantiatedSolvers = new HashSet<>(); + } + + public static SmtLibSolverManager create(final Path home, final Logger logger) throws IOException { + createIfNotExists(home); + return new SmtLibSolverManager(home, logger); + } + + public String getGenericInstallerName() { + return genericInstaller.get1(); + } + + @Override + public boolean managesSolver(final String name) { + final var solverName = decodeSolverName(name, 0); + return solverName != null && installers.containsKey(solverName); + } + + public void install(final String solver, final String version, final String name, final Path solverPath, final boolean installUnsupported) throws SmtLibSolverInstallerException { + checkArgument(!solver.equals(genericInstaller.get1())); + + if(!installers.containsKey(solver)) { + throw new SmtLibSolverInstallerException(String.format("Unknown solver: %s", solver)); + } + else if(!getSupportedVersions(solver).contains(getVersionString(solver, version, false)) && !installUnsupported) { + throw new SmtLibSolverInstallerException("Installing unsupported solvers is not enabled"); + } + + final var installDir = home.resolve(solver); + try { + if (!Files.exists(installDir)) { + Files.createDirectory(installDir); + } + } catch (IOException e) { + throw new SmtLibSolverInstallerException(e); + } + + if(solverPath != null) { + installers.get(solver).install(installDir, getVersionString(solver, version, false), getVersionString(solver, name, false), solverPath); + } + else { + installers.get(solver).install(installDir, getVersionString(solver, version, false), getVersionString(solver, name, false)); + } + } + + public void installGeneric(final String version, final Path solverPath, final String[] args) throws SmtLibSolverInstallerException { + final var installDir = home.resolve(genericInstaller.get1()); + try { + if (!Files.exists(installDir)) { + Files.createDirectory(installDir); + } + } catch (IOException e) { + throw new SmtLibSolverInstallerException(e); + } + genericInstaller.get2().install(installDir, version, solverPath, args); + } + + public void uninstall(final String solver, final String version) throws SmtLibSolverInstallerException { + if(!installers.containsKey(solver)) { + throw new SmtLibSolverInstallerException(String.format("Unknown solver: %s", solver)); + } + + installers.get(solver).uninstall(home.resolve(solver), getVersionString(solver, version, true)); + } + + public void rename(final String solver, final String version, final String name) throws SmtLibSolverInstallerException { + if(!installers.containsKey(solver)) { + throw new SmtLibSolverInstallerException(String.format("Unknown solver: %s", solver)); + } + + installers.get(solver).rename(home.resolve(solver), getVersionString(solver, version, true), name); + } + + public String getInfo(final String solver, final String version) throws SmtLibSolverInstallerException { + if(!installers.containsKey(solver)) { + throw new SmtLibSolverInstallerException(String.format("Unknown solver: %s", solver)); + } + + return installers.get(solver).getInfo(home.resolve(solver), getVersionString(solver, version, true)); + } + + public Path getArgsFile(final String solver, final String version) throws SmtLibSolverInstallerException { + if(!installers.containsKey(solver)) { + throw new SmtLibSolverInstallerException(String.format("Unknown solver: %s", solver)); + } + + return installers.get(solver).getArgsFile(home.resolve(solver), getVersionString(solver, version, true)); + } + + @Override + public SolverFactory getSolverFactory(final String name) throws SmtLibSolverInstallerException { + checkArgument(managesSolver(name)); + return getSolverFactory(getSolverName(name), getSolverVersion(name)); + } + + public SolverFactory getSolverFactory(final String solver, final String version) throws SmtLibSolverInstallerException { + if(!installers.containsKey(solver)) { + throw new SmtLibSolverInstallerException(String.format("Unknown solver: %s", solver)); + } + + return new ManagedFactory(installers.get(solver).getSolverFactory(home.resolve(solver), getVersionString(solver, version, true))); + } + + public List getSupportedSolvers() { + return installers.keySet().stream().collect(Collectors.toUnmodifiableList()); + } + + public List>> getSupportedVersions() throws SmtLibSolverInstallerException { + final var builder = ImmutableList.>>builder(); + + for(final var solver : getSupportedSolvers()) { + builder.add(Tuple2.of(solver, getSupportedVersions(solver))); + } + + return builder.build(); + } + + public List getSupportedVersions(final String solver) throws SmtLibSolverInstallerException { + if(!installers.containsKey(solver)) { + throw new SmtLibSolverInstallerException(String.format("Unknown solver: %s", solver)); + } + + return installers.get(solver).getSupportedVersions(); + } + + public List>> getInstalledVersions() throws SmtLibSolverInstallerException { + final var builder = ImmutableList.>>builder(); + + for(final var solver : getSupportedSolvers()) { + builder.add(Tuple2.of(solver, getInstalledVersions(solver))); + } + + return builder.build(); + } + + public List getInstalledVersions(final String solver) throws SmtLibSolverInstallerException { + if(!installers.containsKey(solver)) { + throw new SmtLibSolverInstallerException(String.format("Unknown solver: %s", solver)); + } + + return installers.get(solver).getInstalledVersions(home.resolve(solver)); + } + + private String getVersionString(final String solver, final String version, final boolean installed) throws SmtLibSolverInstallerException { + if(!version.equals("latest")) { + return version; + } + else { + final var supportedVersions = getSupportedVersions(solver); + final var versions = installed ? getInstalledVersions(solver).stream().filter(supportedVersions::contains).collect(Collectors.toList()) : supportedVersions; + if(versions.size() > 0) { + return versions.get(0); + } + else { + throw new SmtLibSolverInstallerException(String.format("There are no %s versions of solver: %s", installed ? "installed" : "supported", solver)); + } + } + } + + private static Path createIfNotExists(final Path path) throws IOException { + if(!Files.exists(path)) { + Files.createDirectory(path); + } + return path; + } + + @Override + public void close() throws Exception { + for(final var solver : instantiatedSolvers) { + solver.close(); + } + closed = true; + } + + private final class ManagedFactory implements SolverFactory { + private final SolverFactory solverFactory; + + private ManagedFactory(final SolverFactory solverFactory) { + this.solverFactory = solverFactory; + } + + @Override + public Solver createSolver() { + checkState(!closed, "Solver manager was closed"); + final var solver = solverFactory.createSolver(); + instantiatedSolvers.add(solver); + return solver; + } + + @Override + public UCSolver createUCSolver() { + checkState(!closed, "Solver manager was closed"); + final var solver = solverFactory.createUCSolver(); + instantiatedSolvers.add(solver); + return solver; + } + + @Override + public ItpSolver createItpSolver() { + checkState(!closed, "Solver manager was closed"); + final var solver = solverFactory.createItpSolver(); + instantiatedSolvers.add(solver); + return solver; + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/boolector/BoolectorSmtLibSolverFactory.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/boolector/BoolectorSmtLibSolverFactory.java new file mode 100644 index 0000000000..6150264263 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/boolector/BoolectorSmtLibSolverFactory.java @@ -0,0 +1,27 @@ +package hu.bme.mit.theta.solver.smtlib.impl.boolector; + +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.UCSolver; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverFactory; + +import java.nio.file.Path; + +public class BoolectorSmtLibSolverFactory extends GenericSmtLibSolverFactory { + private BoolectorSmtLibSolverFactory(Path solverPath, String[] args) { + super(solverPath, args); + } + + public static BoolectorSmtLibSolverFactory create(Path solverPath, String[] args) { + return new BoolectorSmtLibSolverFactory(solverPath, args); + } + + @Override + public UCSolver createUCSolver() { + throw new UnsupportedOperationException("Boolector does not support unsat cores"); + } + + @Override + public ItpSolver createItpSolver() { + throw new UnsupportedOperationException("Boolector does not support interpolation"); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/boolector/BoolectorSmtLibSolverInstaller.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/boolector/BoolectorSmtLibSolverInstaller.java new file mode 100644 index 0000000000..a033be0c45 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/boolector/BoolectorSmtLibSolverInstaller.java @@ -0,0 +1,122 @@ +package hu.bme.mit.theta.solver.smtlib.impl.boolector; + +import hu.bme.mit.theta.common.OsHelper; +import hu.bme.mit.theta.common.logging.Logger; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException; +import hu.bme.mit.theta.solver.smtlib.utils.Compress; +import hu.bme.mit.theta.solver.smtlib.utils.SemVer; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; + +public class BoolectorSmtLibSolverInstaller extends SmtLibSolverInstaller.Default { + + public BoolectorSmtLibSolverInstaller(final Logger logger) { + super(logger); + } + + @Override + protected String getSolverName() { + return "boolector"; + } + + @Override + protected void installSolver(final Path installDir, final String version) throws SmtLibSolverInstallerException { + final var downloadUrl = URI.create(String.format( + "https://github.com/Boolector/boolector/archive/refs/tags/%s.tar.gz", + version + )); + + logger.write(Logger.Level.MAINSTEP, "Starting download (%s)...\n", downloadUrl.toString()); + + try(final var inputStream = downloadUrl.toURL().openStream()) { + Compress.extract(inputStream, installDir, Compress.CompressionType.TARGZ); + } + catch (IOException e) { + throw new SmtLibSolverInstallerException(e); + } + + logger.write(Logger.Level.MAINSTEP, "Download finished\n"); + + logger.write(Logger.Level.MAINSTEP, "Starting compilation\n"); + + installDir.resolve("contrib").resolve("setup-lingeling.sh").toFile().setExecutable(true, true); + executeCommand(installDir, "alias nproc=\"echo 1\" && ./contrib/setup-lingeling.sh"); + + installDir.resolve("contrib").resolve("setup-btor2tools.sh").toFile().setExecutable(true, true); + executeCommand(installDir, "alias nproc=\"echo 1\" && ./contrib/setup-btor2tools.sh"); + + installDir.resolve("configure.sh").toFile().setExecutable(true, true); + executeCommand(installDir, "./configure.sh"); + executeCommand(installDir.resolve("build"), "make"); + installDir.resolve("build").resolve("bin").resolve(getSolverBinaryName()).toFile().setExecutable(true, true); + + logger.write(Logger.Level.MAINSTEP, "Finished compilation\n"); + } + + @Override + protected void uninstallSolver(final Path installDir, final String version) { + // Default uninstall is suitable + } + + @Override + protected String[] getDefaultSolverArgs(String version) { + if(SemVer.of(version).compareTo(SemVer.of("3.2.2")) >= 0) { + return new String[]{ + "--smt2", + "-i" + }; + } + else { + return new String[]{ + "--smt2", + "--smt2-model", + "-i" + }; + } + } + + @Override + public SolverFactory getSolverFactory(final Path installDir, final String version, final Path solverPath, final String[] solverArgs) throws SmtLibSolverInstallerException { + final var solverFilePath = solverPath != null ? solverPath : installDir.resolve("build").resolve("bin").resolve(getSolverBinaryName()); + return BoolectorSmtLibSolverFactory.create(solverFilePath, solverArgs); + } + + @Override + public List getSupportedVersions() { + return Arrays.asList("3.2.2", "3.2.1", "3.2.0", "3.1.0", "3.0.0"); + } + + private String getSolverBinaryName() { + switch(OsHelper.getOs()) { + case LINUX: + return "boolector"; + default: + throw new AssertionError(); + } + } + + private void executeCommand(final Path workingPath, final String command) throws SmtLibSolverInstallerException { + try { + logger.write(Logger.Level.SUBSTEP, "Execute command: %s\n", command); + final var process = new ProcessBuilder() + .command("bash", "-c", command) + .directory(workingPath.toFile()) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .redirectError(ProcessBuilder.Redirect.INHERIT) + .start(); + + if(process.waitFor() != 0) { + throw new SmtLibSolverInstallerException(String.format("Error executing command: %s", command)); + } + } + catch (IOException | InterruptedException e) { + throw new SmtLibSolverInstallerException(String.format("Error executing command: %s", command), e); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/cvc4/CVC4SmtLibSolverFactory.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/cvc4/CVC4SmtLibSolverFactory.java new file mode 100644 index 0000000000..c540d3653a --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/cvc4/CVC4SmtLibSolverFactory.java @@ -0,0 +1,21 @@ +package hu.bme.mit.theta.solver.smtlib.impl.cvc4; + +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverFactory; + +import java.nio.file.Path; + +public class CVC4SmtLibSolverFactory extends GenericSmtLibSolverFactory { + private CVC4SmtLibSolverFactory(Path solverPath, String[] args) { + super(solverPath, args); + } + + public static CVC4SmtLibSolverFactory create(Path solverPath, String[] args) { + return new CVC4SmtLibSolverFactory(solverPath, args); + } + + @Override + public ItpSolver createItpSolver() { + throw new UnsupportedOperationException("CVC4 does not support interpolation"); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/cvc4/CVC4SmtLibSolverInstaller.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/cvc4/CVC4SmtLibSolverInstaller.java new file mode 100644 index 0000000000..789c0821e4 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/cvc4/CVC4SmtLibSolverInstaller.java @@ -0,0 +1,121 @@ +package hu.bme.mit.theta.solver.smtlib.impl.cvc4; + +import hu.bme.mit.theta.common.OsHelper; +import hu.bme.mit.theta.common.logging.Logger; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException; +import hu.bme.mit.theta.solver.smtlib.utils.SemVer; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.nio.channels.Channels; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; + +import static hu.bme.mit.theta.common.OsHelper.Architecture.X64; +import static hu.bme.mit.theta.common.OsHelper.OperatingSystem.LINUX; +import static hu.bme.mit.theta.common.OsHelper.OperatingSystem.WINDOWS; + +public class CVC4SmtLibSolverInstaller extends SmtLibSolverInstaller.Default { + + public CVC4SmtLibSolverInstaller(final Logger logger) { + super(logger); + } + + @Override + protected String getSolverName() { + return "cvc4"; + } + + @Override + protected void installSolver(final Path installDir, final String version) throws SmtLibSolverInstallerException { + + try( + final var inputChannel = Channels.newChannel(getDownloadUrl(version).openStream()); + final var outputChannel = new FileOutputStream(installDir.resolve(getSolverBinaryName()).toAbsolutePath().toString()).getChannel() + ) { + logger.write(Logger.Level.MAINSTEP, "Starting download (%s)...\n", getDownloadUrl(version).toString()); + outputChannel.transferFrom(inputChannel, 0, Long.MAX_VALUE); + installDir.resolve(getSolverBinaryName()).toFile().setExecutable(true, true); + } + catch (IOException e) { + throw new SmtLibSolverInstallerException(e); + } + + logger.write(Logger.Level.MAINSTEP, "Download finished\n"); + } + + @Override + protected void uninstallSolver(final Path installDir, final String version) { + // Default uninstall is suitable + } + + @Override + protected String[] getDefaultSolverArgs(String version) { + return new String[] { + "--lang", "smt2", + "--output-lang", "smt2", + "--quiet", + "--incremental" + }; + } + + @Override + public SolverFactory getSolverFactory(final Path installDir, final String version, final Path solverPath, final String[] solverArgs) throws SmtLibSolverInstallerException { + final var solverFilePath = solverPath != null ? solverPath : installDir.resolve(getSolverBinaryName()); + return CVC4SmtLibSolverFactory.create(solverFilePath, solverArgs); + } + + @Override + public List getSupportedVersions() { + return Arrays.asList("1.8", "1.7", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"); + } + + private URL getDownloadUrl(final String version) throws SmtLibSolverInstallerException, MalformedURLException { + final var os = OsHelper.getOs(); + final var arch = OsHelper.getArch(); + + final String archString; + final String platformExtension; + if(arch != X64) { + throw new SmtLibSolverInstallerException("cvc4 is available only for x64 architecture"); + } + else if(os != LINUX && os != WINDOWS) { + throw new SmtLibSolverInstallerException("cvc4 is available only for Windows and Linux"); + } + else if(os == LINUX) { + archString = "x86_64-linux-opt"; + platformExtension = ""; + } + else /* if(os == WINDOWS) */ { + if(SemVer.of(version).compareTo(SemVer.of("1.6")) >= 0) { + archString = "win64-opt"; + platformExtension = ".exe"; + } + else { + throw new SmtLibSolverInstallerException("Windows platform is only supported for version 1.6 and forward"); + } + } + + return URI.create(String.format( + "https://cvc4.cs.stanford.edu/downloads/builds/%s/cvc4-%s-%s%s", + archString, version, archString, platformExtension + )).toURL(); + } + + private String getSolverBinaryName() { + switch(OsHelper.getOs()) { + case WINDOWS: + return "cvc4.exe"; + case LINUX: + return "cvc4"; + default: + throw new AssertionError(); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibDeclTransformer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibDeclTransformer.java new file mode 100644 index 0000000000..310bd869bc --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibDeclTransformer.java @@ -0,0 +1,100 @@ +package hu.bme.mit.theta.solver.smtlib.impl.generic; + +import com.google.common.collect.ImmutableList; +import hu.bme.mit.theta.common.Tuple2; +import hu.bme.mit.theta.core.decl.ConstDecl; +import hu.bme.mit.theta.core.decl.Decl; +import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.functype.FuncType; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibDeclTransformer; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; + +import java.util.List; + +import static com.google.common.base.Preconditions.checkArgument; + +public class GenericSmtLibDeclTransformer implements SmtLibDeclTransformer { + private final SmtLibTransformationManager transformer; + private final SmtLibSymbolTable symbolTable; + + private int symbolCount; + + public GenericSmtLibDeclTransformer(final SmtLibTransformationManager transformer, final SmtLibSymbolTable symbolTable) { + this.transformer = transformer; + this.symbolTable = symbolTable; + + symbolCount = 0; + } + + @Override + public String toSymbol(final Decl decl) { + if (decl instanceof ConstDecl) { + final ConstDecl cdecl = (ConstDecl) decl; + if(!symbolTable.definesConst(cdecl)) { + transformConst(cdecl); + } + return symbolTable.getSymbol(cdecl); + } else { + throw new UnsupportedOperationException("Cannot transform declaration: " + decl); + } + } + + @Override + public String toDeclaration(final Decl decl) { + if (decl instanceof ConstDecl) { + final ConstDecl cdecl = (ConstDecl) decl; + if(!symbolTable.definesConst(cdecl)) { + transformConst(cdecl); + } + return symbolTable.getDeclaration(cdecl); + } else { + throw new UnsupportedOperationException("Cannot transform declaration: " + decl); + } + } + + private void transformConst(final ConstDecl decl) { + final Type type = decl.getType(); + + final Tuple2, Type> extractedTypes = extractTypes(type); + final List paramTypes = extractedTypes.get1(); + final Type returnType = extractedTypes.get2(); + + final String returnSort = transformer.toSort(returnType); + final String[] paramSorts = paramTypes.stream().map(transformer::toSort) + .toArray(String[]::new); + + final String symbolName = symbolNameFor(decl); + final String symbolDeclaration = String.format( + "(declare-fun %s (%s) %s)", + symbolName, String.join(" ", paramSorts), returnSort + ); + symbolTable.put(decl, symbolName, symbolDeclaration); + } + + private Tuple2, Type> extractTypes(final Type type) { + if (type instanceof FuncType) { + final FuncType funcType = (FuncType) type; + + final Type paramType = funcType.getParamType(); + final Type resultType = funcType.getResultType(); + + checkArgument(!(paramType instanceof FuncType)); + + final Tuple2, Type> subResult = extractTypes(resultType); + final List paramTypes = subResult.get1(); + final Type newResultType = subResult.get2(); + final List newParamTypes = ImmutableList.builder().add(paramType).addAll(paramTypes).build(); + final Tuple2, Type> result = Tuple2.of(newParamTypes, newResultType); + + return result; + } else { + return Tuple2.of(ImmutableList.of(), type); + } + } + + private String symbolNameFor(final Decl decl) { + return String.format("%s_%d", decl.getName(), symbolCount++); + } + +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibExprTransformer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibExprTransformer.java new file mode 100644 index 0000000000..cf2d868045 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibExprTransformer.java @@ -0,0 +1,1123 @@ +package hu.bme.mit.theta.solver.smtlib.impl.generic; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.ImmutableList; +import hu.bme.mit.theta.common.DispatchTable; +import hu.bme.mit.theta.common.Tuple2; +import hu.bme.mit.theta.common.dsl.Env; +import hu.bme.mit.theta.core.decl.ConstDecl; +import hu.bme.mit.theta.core.decl.Decl; +import hu.bme.mit.theta.core.decl.ParamDecl; +import hu.bme.mit.theta.core.dsl.DeclSymbol; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.anytype.IteExpr; +import hu.bme.mit.theta.core.type.anytype.RefExpr; +import hu.bme.mit.theta.core.type.arraytype.ArrayEqExpr; +import hu.bme.mit.theta.core.type.arraytype.ArrayInitExpr; +import hu.bme.mit.theta.core.type.arraytype.ArrayLitExpr; +import hu.bme.mit.theta.core.type.arraytype.ArrayNeqExpr; +import hu.bme.mit.theta.core.type.arraytype.ArrayReadExpr; +import hu.bme.mit.theta.core.type.arraytype.ArrayWriteExpr; +import hu.bme.mit.theta.core.type.booltype.AndExpr; +import hu.bme.mit.theta.core.type.booltype.ExistsExpr; +import hu.bme.mit.theta.core.type.booltype.FalseExpr; +import hu.bme.mit.theta.core.type.booltype.ForallExpr; +import hu.bme.mit.theta.core.type.booltype.IffExpr; +import hu.bme.mit.theta.core.type.booltype.ImplyExpr; +import hu.bme.mit.theta.core.type.booltype.NotExpr; +import hu.bme.mit.theta.core.type.booltype.OrExpr; +import hu.bme.mit.theta.core.type.booltype.TrueExpr; +import hu.bme.mit.theta.core.type.booltype.XorExpr; +import hu.bme.mit.theta.core.type.bvtype.BvAddExpr; +import hu.bme.mit.theta.core.type.bvtype.BvAndExpr; +import hu.bme.mit.theta.core.type.bvtype.BvArithShiftRightExpr; +import hu.bme.mit.theta.core.type.bvtype.BvConcatExpr; +import hu.bme.mit.theta.core.type.bvtype.BvEqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvExtractExpr; +import hu.bme.mit.theta.core.type.bvtype.BvLitExpr; +import hu.bme.mit.theta.core.type.bvtype.BvLogicShiftRightExpr; +import hu.bme.mit.theta.core.type.bvtype.BvMulExpr; +import hu.bme.mit.theta.core.type.bvtype.BvNegExpr; +import hu.bme.mit.theta.core.type.bvtype.BvNeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvNotExpr; +import hu.bme.mit.theta.core.type.bvtype.BvOrExpr; +import hu.bme.mit.theta.core.type.bvtype.BvPosExpr; +import hu.bme.mit.theta.core.type.bvtype.BvRotateLeftExpr; +import hu.bme.mit.theta.core.type.bvtype.BvRotateRightExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSDivExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSExtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSGeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSGtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSLeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSLtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSModExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSRemExpr; +import hu.bme.mit.theta.core.type.bvtype.BvShiftLeftExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSubExpr; +import hu.bme.mit.theta.core.type.bvtype.BvUDivExpr; +import hu.bme.mit.theta.core.type.bvtype.BvUGeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvUGtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvULeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvULtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvURemExpr; +import hu.bme.mit.theta.core.type.bvtype.BvXorExpr; +import hu.bme.mit.theta.core.type.bvtype.BvZExtExpr; +import hu.bme.mit.theta.core.type.fptype.FpAbsExpr; +import hu.bme.mit.theta.core.type.fptype.FpAddExpr; +import hu.bme.mit.theta.core.type.fptype.FpDivExpr; +import hu.bme.mit.theta.core.type.fptype.FpEqExpr; +import hu.bme.mit.theta.core.type.fptype.FpFromBvExpr; +import hu.bme.mit.theta.core.type.fptype.FpGeqExpr; +import hu.bme.mit.theta.core.type.fptype.FpGtExpr; +import hu.bme.mit.theta.core.type.fptype.FpIsNanExpr; +import hu.bme.mit.theta.core.type.fptype.FpLeqExpr; +import hu.bme.mit.theta.core.type.fptype.FpLitExpr; +import hu.bme.mit.theta.core.type.fptype.FpLtExpr; +import hu.bme.mit.theta.core.type.fptype.FpMaxExpr; +import hu.bme.mit.theta.core.type.fptype.FpMinExpr; +import hu.bme.mit.theta.core.type.fptype.FpMulExpr; +import hu.bme.mit.theta.core.type.fptype.FpNegExpr; +import hu.bme.mit.theta.core.type.fptype.FpNeqExpr; +import hu.bme.mit.theta.core.type.fptype.FpPosExpr; +import hu.bme.mit.theta.core.type.fptype.FpRemExpr; +import hu.bme.mit.theta.core.type.fptype.FpRoundToIntegralExpr; +import hu.bme.mit.theta.core.type.fptype.FpRoundingMode; +import hu.bme.mit.theta.core.type.fptype.FpSqrtExpr; +import hu.bme.mit.theta.core.type.fptype.FpSubExpr; +import hu.bme.mit.theta.core.type.fptype.FpToBvExpr; +import hu.bme.mit.theta.core.type.fptype.FpToFpExpr; +import hu.bme.mit.theta.core.type.functype.FuncAppExpr; +import hu.bme.mit.theta.core.type.functype.FuncType; +import hu.bme.mit.theta.core.type.inttype.IntAddExpr; +import hu.bme.mit.theta.core.type.inttype.IntDivExpr; +import hu.bme.mit.theta.core.type.inttype.IntEqExpr; +import hu.bme.mit.theta.core.type.inttype.IntGeqExpr; +import hu.bme.mit.theta.core.type.inttype.IntGtExpr; +import hu.bme.mit.theta.core.type.inttype.IntLeqExpr; +import hu.bme.mit.theta.core.type.inttype.IntLitExpr; +import hu.bme.mit.theta.core.type.inttype.IntLtExpr; +import hu.bme.mit.theta.core.type.inttype.IntModExpr; +import hu.bme.mit.theta.core.type.inttype.IntMulExpr; +import hu.bme.mit.theta.core.type.inttype.IntNegExpr; +import hu.bme.mit.theta.core.type.inttype.IntNeqExpr; +import hu.bme.mit.theta.core.type.inttype.IntPosExpr; +import hu.bme.mit.theta.core.type.inttype.IntRemExpr; +import hu.bme.mit.theta.core.type.inttype.IntSubExpr; +import hu.bme.mit.theta.core.type.inttype.IntToRatExpr; +import hu.bme.mit.theta.core.type.rattype.RatAddExpr; +import hu.bme.mit.theta.core.type.rattype.RatDivExpr; +import hu.bme.mit.theta.core.type.rattype.RatEqExpr; +import hu.bme.mit.theta.core.type.rattype.RatGeqExpr; +import hu.bme.mit.theta.core.type.rattype.RatGtExpr; +import hu.bme.mit.theta.core.type.rattype.RatLeqExpr; +import hu.bme.mit.theta.core.type.rattype.RatLitExpr; +import hu.bme.mit.theta.core.type.rattype.RatLtExpr; +import hu.bme.mit.theta.core.type.rattype.RatMulExpr; +import hu.bme.mit.theta.core.type.rattype.RatNegExpr; +import hu.bme.mit.theta.core.type.rattype.RatNeqExpr; +import hu.bme.mit.theta.core.type.rattype.RatPosExpr; +import hu.bme.mit.theta.core.type.rattype.RatSubExpr; +import hu.bme.mit.theta.core.utils.BvUtils; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibExprTransformer; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; + +import java.math.BigInteger; +import java.util.List; +import java.util.concurrent.ExecutionException; + +public class GenericSmtLibExprTransformer implements SmtLibExprTransformer { + private static final int CACHE_SIZE = 1000; + + private final SmtLibTransformationManager transformer; + + private final Cache, String> exprToTerm; + private final DispatchTable table; + private final Env env; + + public GenericSmtLibExprTransformer(final SmtLibTransformationManager transformer) { + this.transformer = transformer; + this.env = new Env(); + + this.exprToTerm = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).build(); + + this.table = DispatchTable.builder() + + // General + + .addCase(RefExpr.class, this::transformRef) + + .addCase(IteExpr.class, this::transformIte) + + // Boolean + + .addCase(FalseExpr.class, this::transformFalse) + + .addCase(TrueExpr.class, this::transformTrue) + + .addCase(NotExpr.class, this::transformNot) + + .addCase(ImplyExpr.class, this::transformImply) + + .addCase(IffExpr.class, this::transformIff) + + .addCase(XorExpr.class, this::transformXor) + + .addCase(AndExpr.class, this::transformAnd) + + .addCase(OrExpr.class, this::transformOr) + + .addCase(ExistsExpr.class, this::transformExists) + + .addCase(ForallExpr.class, this::transformForall) + + // Rationals + + .addCase(RatLitExpr.class, this::transformRatLit) + + .addCase(RatAddExpr.class, this::transformRatAdd) + + .addCase(RatSubExpr.class, this::transformRatSub) + + .addCase(RatPosExpr.class, this::transformRatPos) + + .addCase(RatNegExpr.class, this::transformRatNeg) + + .addCase(RatMulExpr.class, this::transformRatMul) + + .addCase(RatDivExpr.class, this::transformRatDiv) + + .addCase(RatEqExpr.class, this::transformRatEq) + + .addCase(RatNeqExpr.class, this::transformRatNeq) + + .addCase(RatGeqExpr.class, this::transformRatGeq) + + .addCase(RatGtExpr.class, this::transformRatGt) + + .addCase(RatLeqExpr.class, this::transformRatLeq) + + .addCase(RatLtExpr.class, this::transformRatLt) + + // Integers + + .addCase(IntLitExpr.class, this::transformIntLit) + + .addCase(IntAddExpr.class, this::transformIntAdd) + + .addCase(IntSubExpr.class, this::transformIntSub) + + .addCase(IntPosExpr.class, this::transformIntPos) + + .addCase(IntNegExpr.class, this::transformIntNeg) + + .addCase(IntMulExpr.class, this::transformIntMul) + + .addCase(IntDivExpr.class, this::transformIntDiv) + + .addCase(IntModExpr.class, this::transformIntMod) + + .addCase(IntRemExpr.class, this::transformIntRem) + + .addCase(IntEqExpr.class, this::transformIntEq) + + .addCase(IntNeqExpr.class, this::transformIntNeq) + + .addCase(IntGeqExpr.class, this::transformIntGeq) + + .addCase(IntGtExpr.class, this::transformIntGt) + + .addCase(IntLeqExpr.class, this::transformIntLeq) + + .addCase(IntLtExpr.class, this::transformIntLt) + + .addCase(IntToRatExpr.class, this::transformIntToRat) + + // Bitvectors + + .addCase(BvLitExpr.class, this::transformBvLit) + + .addCase(BvConcatExpr.class, this::transformBvConcat) + + .addCase(BvExtractExpr.class, this::transformBvExtract) + + .addCase(BvZExtExpr.class, this::transformBvZExt) + + .addCase(BvSExtExpr.class, this::transformBvSExt) + + .addCase(BvAddExpr.class, this::transformBvAdd) + + .addCase(BvSubExpr.class, this::transformBvSub) + + .addCase(BvPosExpr.class, this::transformBvPos) + + .addCase(BvNegExpr.class, this::transformBvNeg) + + .addCase(BvMulExpr.class, this::transformBvMul) + + .addCase(BvUDivExpr.class, this::transformBvUDiv) + + .addCase(BvSDivExpr.class, this::transformBvSDiv) + + .addCase(BvSModExpr.class, this::transformBvSMod) + + .addCase(BvURemExpr.class, this::transformBvURem) + + .addCase(BvSRemExpr.class, this::transformBvSRem) + + .addCase(BvAndExpr.class, this::transformBvAnd) + + .addCase(BvOrExpr.class, this::transformBvOr) + + .addCase(BvXorExpr.class, this::transformBvXor) + + .addCase(BvNotExpr.class, this::transformBvNot) + + .addCase(BvShiftLeftExpr.class, this::transformBvShiftLeft) + + .addCase(BvArithShiftRightExpr.class, this::transformBvArithShiftRight) + + .addCase(BvLogicShiftRightExpr.class, this::transformBvLogicShiftRight) + + .addCase(BvRotateLeftExpr.class, this::transformBvRotateLeft) + + .addCase(BvRotateRightExpr.class, this::transformBvRotateRight) + + .addCase(BvEqExpr.class, this::transformBvEq) + + .addCase(BvNeqExpr.class, this::transformBvNeq) + + .addCase(BvUGeqExpr.class, this::transformBvUGeq) + + .addCase(BvUGtExpr.class, this::transformBvUGt) + + .addCase(BvULeqExpr.class, this::transformBvULeq) + + .addCase(BvULtExpr.class, this::transformBvULt) + + .addCase(BvSGeqExpr.class, this::transformBvSGeq) + + .addCase(BvSGtExpr.class, this::transformBvSGt) + + .addCase(BvSLeqExpr.class, this::transformBvSLeq) + + .addCase(BvSLtExpr.class, this::transformBvSLt) + + // Floating points + + .addCase(FpLitExpr.class, this::transformFpLit) + + .addCase(FpAddExpr.class, this::transformFpAdd) + + .addCase(FpSubExpr.class, this::transformFpSub) + + .addCase(FpPosExpr.class, this::transformFpPos) + + .addCase(FpNegExpr.class, this::transformFpNeg) + + .addCase(FpMulExpr.class, this::transformFpMul) + + .addCase(FpDivExpr.class, this::transformFpDiv) + + .addCase(FpEqExpr.class, this::transformFpEq) + + .addCase(FpGeqExpr.class, this::transformFpGeq) + + .addCase(FpLeqExpr.class, this::transformFpLeq) + + .addCase(FpGtExpr.class, this::transformFpGt) + + .addCase(FpLtExpr.class, this::transformFpLt) + + .addCase(FpNeqExpr.class, this::transformFpNeq) + + .addCase(FpAbsExpr.class, this::transformFpAbs) + + .addCase(FpRoundToIntegralExpr.class, this::transformFpRoundToIntegral) + + .addCase(FpMaxExpr.class, this::transformFpMax) + + .addCase(FpMinExpr.class, this::transformFpMin) + + .addCase(FpSqrtExpr.class, this::transformFpSqrt) + + .addCase(FpRemExpr.class, this::transformFpRem) + + .addCase(FpIsNanExpr.class, this::transformFpIsNaN) + + .addCase(FpFromBvExpr.class, this::transformFpFromBv) + + .addCase(FpToBvExpr.class, this::transformFpToBv) + + .addCase(FpToFpExpr.class, this::transformFpToFp) + + // Functions + + .addCase(FuncAppExpr.class, this::transformFuncApp) + + // Arrays + + .addCase(ArrayReadExpr.class, this::transformArrayRead) + + .addCase(ArrayWriteExpr.class, this::transformArrayWrite) + + .addCase(ArrayEqExpr.class, this::transformArrayEq) + + .addCase(ArrayNeqExpr.class, this::transformArrayNeq) + + .addCase(ArrayLitExpr.class, this::transformArrayLit) + + .addCase(ArrayInitExpr.class, this::transformArrayInit) + + .build(); + } + + @Override + public final String toTerm(final Expr expr) { + try { + return exprToTerm.get(expr, () -> table.dispatch(expr)); + } catch (final ExecutionException e) { + throw new AssertionError(); + } + } + + //// + + /* + * General + */ + + protected String transformRef(final RefExpr expr) { + final Decl decl = expr.getDecl(); + if (decl instanceof ConstDecl) { + return transformer.toSymbol(decl); + } else if (decl instanceof ParamDecl) { + return (String) env.eval(DeclSymbol.of(decl)); + } else { + throw new UnsupportedOperationException("Cannot transform reference for declaration: " + decl); + } + } + + protected String transformIte(final IteExpr expr) { + final String condTerm = toTerm(expr.getCond()); + final String thenTerm = toTerm(expr.getThen()); + final String elzeTerm = toTerm(expr.getElse()); + return String.format("(ite %s %s %s)", condTerm, thenTerm, elzeTerm); + } + + /* + * Booleans + */ + + protected String transformFalse(final FalseExpr expr) { + return "false"; + } + + protected String transformTrue(final TrueExpr expr) { + return "true"; + } + + protected String transformNot(final NotExpr expr) { + return String.format("(not %s)", toTerm(expr.getOp())); + } + + protected String transformImply(final ImplyExpr expr) { + return String.format("(=> %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformIff(final IffExpr expr) { + return String.format("(= %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformXor(final XorExpr expr) { + return String.format("(xor %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformAnd(final AndExpr expr) { + if(expr.getArity() == 1) { + return toTerm(expr.getOps().get(0)); + } + else if(expr.getArity() == 0) { + return "true"; + } + else { + final String[] opTerms = expr.getOps().stream() + .map(this::toTerm) + .toArray(String[]::new); + + return String.format("(and %s)", String.join(" ", opTerms)); + } + } + + protected String transformOr(final OrExpr expr) { + if(expr.getArity() == 1) { + return toTerm(expr.getOps().get(0)); + } + else if(expr.getArity() == 0) { + return "false"; + } + else { + final String[] opTerms = expr.getOps().stream() + .map(this::toTerm) + .toArray(String[]::new); + + return String.format("(or %s)", String.join(" ", opTerms)); + } + } + + protected String transformExists(final ExistsExpr expr) { + env.push(); + final String[] paramTerms = transformParamDecls(expr.getParamDecls()); + final String opTerm = toTerm(expr.getOp()); + final String result = String.format("(exists (%s) %s)", String.join(" ", paramTerms), opTerm); + env.pop(); + return result; + } + + protected String transformForall(final ForallExpr expr) { + env.push(); + final String[] paramTerms = transformParamDecls(expr.getParamDecls()); + final String opTerm = toTerm(expr.getOp()); + final String result = String.format("(forall (%s) %s)", String.join(" ", paramTerms), opTerm); + env.pop(); + return result; + } + + private String[] transformParamDecls(final List> paramDecls) { + final String[] paramTerms = new String[paramDecls.size()]; + int i = 0; + for (final ParamDecl paramDecl : paramDecls) { + final String paramSymbol = transformParamDecl(paramDecl); + paramTerms[i] = paramSymbol; + env.define(DeclSymbol.of(paramDecl), paramDecl.getName()); + i++; + } + return paramTerms; + } + + private String transformParamDecl(final ParamDecl paramDecl) { + final Type type = paramDecl.getType(); + if (type instanceof FuncType) { + throw new UnsupportedOperationException("Only simple types are supported"); + } else { + return String.format("(%s %s)", paramDecl.getName(), transformer.toSort(type)); + } + } + + /* + * Rationals + */ + + protected String transformRatLit(final RatLitExpr expr) { + return String.format("(/ %d.0 %d.0)", expr.getNum(), expr.getDenom()); + } + + protected String transformRatAdd(final RatAddExpr expr) { + if(expr.getArity() == 1) { + return toTerm(expr.getOps().get(0)); + } + else if(expr.getArity() == 0) { + return "0.0"; + } + else { + final String[] opTerms = expr.getOps().stream() + .map(this::toTerm) + .toArray(String[]::new); + + return String.format("(+ %s)", String.join(" ", opTerms)); + } + } + + protected String transformRatSub(final RatSubExpr expr) { + return String.format("(- %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformRatPos(final RatPosExpr expr) { + return toTerm(expr.getOp()); + } + + protected String transformRatNeg(final RatNegExpr expr) { + return String.format("(- %s)", toTerm(expr.getOp())); + } + + protected String transformRatMul(final RatMulExpr expr) { + if(expr.getArity() == 1) { + return toTerm(expr.getOps().get(0)); + } + else if(expr.getArity() == 0) { + return "1.0"; + } + else { + final String[] opTerms = expr.getOps().stream() + .map(this::toTerm) + .toArray(String[]::new); + + return String.format("(* %s)", String.join(" ", opTerms)); + } + } + + protected String transformRatDiv(final RatDivExpr expr) { + return String.format("(/ %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformRatEq(final RatEqExpr expr) { + return String.format("(= %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformRatNeq(final RatNeqExpr expr) { + return String.format("(not (= %s %s))", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformRatGeq(final RatGeqExpr expr) { + return String.format("(>= %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformRatGt(final RatGtExpr expr) { + return String.format("(> %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformRatLeq(final RatLeqExpr expr) { + return String.format("(<= %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformRatLt(final RatLtExpr expr) { + return String.format("(< %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + /* + * Integers + */ + + protected String transformIntLit(final IntLitExpr expr) { + if(expr.getValue().compareTo(BigInteger.ZERO) < 0) { + return String.format("(- %s)", expr.getValue().abs()); + } + else { + return expr.getValue().toString(); + } + } + + protected String transformIntAdd(final IntAddExpr expr) { + if(expr.getArity() == 1) { + return toTerm(expr.getOps().get(0)); + } + else if(expr.getArity() == 0) { + return "0"; + } + else { + final String[] opTerms = expr.getOps().stream() + .map(this::toTerm) + .toArray(String[]::new); + + return String.format("(+ %s)", String.join(" ", opTerms)); + } + } + + protected String transformIntSub(final IntSubExpr expr) { + return String.format("(- %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformIntPos(final IntPosExpr expr) { + return toTerm(expr.getOp()); + } + + protected String transformIntNeg(final IntNegExpr expr) { + return String.format("(- %s)", toTerm(expr.getOp())); + } + + protected String transformIntMul(final IntMulExpr expr) { + if(expr.getArity() == 1) { + return toTerm(expr.getOps().get(0)); + } + else if(expr.getArity() == 0) { + return "1"; + } + else { + final String[] opTerms = expr.getOps().stream() + .map(this::toTerm) + .toArray(String[]::new); + + return String.format("(* %s)", String.join(" ", opTerms)); + } + } + + protected String transformIntDiv(final IntDivExpr expr) { + return String.format("(div %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformIntMod(final IntModExpr expr) { + return String.format("(mod %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformIntRem(final IntRemExpr expr) { + return String.format("(rem %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformIntEq(final IntEqExpr expr) { + return String.format("(= %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformIntNeq(final IntNeqExpr expr) { + return String.format("(not (= %s %s))", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformIntGeq(final IntGeqExpr expr) { + return String.format("(>= %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformIntGt(final IntGtExpr expr) { + return String.format("(> %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformIntLeq(final IntLeqExpr expr) { + return String.format("(<= %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformIntLt(final IntLtExpr expr) { + return String.format("(< %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformIntToRat(final IntToRatExpr expr) { + return String.format("(to_real %s)", toTerm(expr.getOp())); + } + + /* + * Bitvectors + */ + + protected String transformBvLit(final BvLitExpr expr) { + final StringBuilder sb = new StringBuilder(expr.getType().getSize() + 1); + + for(boolean value : expr.getValue()) { + sb.append(value ? "1" : "0"); + } + + return String.format("#b%s", sb); + } + + protected String transformBvConcat(final BvConcatExpr expr) { + final String[] opTerms = expr.getOps().stream() + .map(this::toTerm) + .toArray(String[]::new); + + return String.format("(concat %s)", String.join(" ", opTerms)); + } + + protected String transformBvExtract(final BvExtractExpr expr) { + final var until = expr.getUntil().getValue().subtract(BigInteger.ONE); + final var from = expr.getFrom().getValue(); + + return String.format("((_ extract %s %s) %s)", until, from, toTerm(expr.getBitvec())); + } + + protected String transformBvZExt(final BvZExtExpr expr) { + final var extendWith = expr.getExtendType().getSize() - expr.getOp().getType().getSize(); + return String.format("((_ zero_extend %d) %s)", extendWith, toTerm(expr.getOp())); + } + + protected String transformBvSExt(final BvSExtExpr expr) { + final var extendWith = expr.getExtendType().getSize() - expr.getOp().getType().getSize(); + return String.format("((_ sign_extend %d) %s)", extendWith, toTerm(expr.getOp())); + } + + protected String transformBvAdd(final BvAddExpr expr) { + if(expr.getArity() == 1) { + return toTerm(expr.getOps().get(0)); + } + else if(expr.getArity() == 0) { + return toTerm(BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, expr.getType().getSize())); + } + else { + final String[] opTerms = expr.getOps().stream() + .map(this::toTerm) + .toArray(String[]::new); + + return String.format("(bvadd %s)", String.join(" ", opTerms)); + } + } + + protected String transformBvSub(final BvSubExpr expr) { + return String.format("(bvsub %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvPos(final BvPosExpr expr) { + return toTerm(expr.getOp()); + } + + protected String transformBvNeg(final BvNegExpr expr) { + return String.format("(bvneg %s)", toTerm(expr.getOp())); + } + + protected String transformBvMul(final BvMulExpr expr) { + if(expr.getArity() == 1) { + return toTerm(expr.getOps().get(0)); + } + else if(expr.getArity() == 0) { + return toTerm(BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ONE, expr.getType().getSize())); + } + else { + final String[] opTerms = expr.getOps().stream() + .map(this::toTerm) + .toArray(String[]::new); + + return String.format("(bvmul %s)", String.join(" ", opTerms)); + } + } + + protected String transformBvUDiv(final BvUDivExpr expr) { + return String.format("(bvudiv %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvSDiv(final BvSDivExpr expr) { + return String.format("(bvsdiv %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvSMod(final BvSModExpr expr) { + return String.format("(bvsmod %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvURem(final BvURemExpr expr) { + return String.format("(bvurem %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvSRem(final BvSRemExpr expr) { + return String.format("(bvsrem %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvAnd(final BvAndExpr expr) { + if(expr.getArity() == 1) { + return toTerm(expr.getOps().get(0)); + } + else if(expr.getArity() == 0) { + return toTerm(BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.TWO.pow(expr.getType().getSize()).subtract(BigInteger.ONE), expr.getType().getSize())); + } + else { + final String[] opTerms = expr.getOps().stream() + .map(this::toTerm) + .toArray(String[]::new); + + return String.format("(bvand %s)", String.join(" ", opTerms)); + } + } + + protected String transformBvOr(final BvOrExpr expr) { + if(expr.getArity() == 1) { + return toTerm(expr.getOps().get(0)); + } + else if(expr.getArity() == 0) { + return toTerm(BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, expr.getType().getSize())); + } + else { + final String[] opTerms = expr.getOps().stream() + .map(this::toTerm) + .toArray(String[]::new); + + return String.format("(bvor %s)", String.join(" ", opTerms)); + } + } + + protected String transformBvXor(final BvXorExpr expr) { + if(expr.getArity() == 1) { + return toTerm(expr.getOps().get(0)); + } + else if(expr.getArity() == 0) { + return toTerm(BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, expr.getType().getSize())); + } + else { + final String[] opTerms = expr.getOps().stream() + .map(this::toTerm) + .toArray(String[]::new); + + return String.format("(bvxor %s)", String.join(" ", opTerms)); + } + } + + protected String transformBvNot(final BvNotExpr expr) { + return String.format("(bvnot %s)", toTerm(expr.getOp())); + } + + protected String transformBvShiftLeft(final BvShiftLeftExpr expr) { + return String.format("(bvshl %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvArithShiftRight(final BvArithShiftRightExpr expr) { + return String.format("(bvashr %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvLogicShiftRight(final BvLogicShiftRightExpr expr) { + return String.format("(bvlshr %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvRotateLeft(final BvRotateLeftExpr expr) { + final var toRotate = toTerm(expr.getLeftOp()); + final var rotateWith = toTerm(expr.getRightOp()); + final var size = toTerm(BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.valueOf(expr.getType().getSize()), expr.getType().getSize())); + return String.format("(bvor (bvshl %s %s) (bvlshr %s (bvsub %s %s)))", toRotate, rotateWith, toRotate, size, rotateWith); + } + + protected String transformBvRotateRight(final BvRotateRightExpr expr) { + final var toRotate = toTerm(expr.getLeftOp()); + final var rotateWith = toTerm(expr.getRightOp()); + final var size = toTerm(BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.valueOf(expr.getType().getSize()), expr.getType().getSize())); + return String.format("(bvor (bvlshr %s %s) (bvshl %s (bvsub %s %s)))", toRotate, rotateWith, toRotate, size, rotateWith); + } + + protected String transformBvEq(final BvEqExpr expr) { + return String.format("(= %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvNeq(final BvNeqExpr expr) { + return String.format("(not (= %s %s))", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvUGeq(final BvUGeqExpr expr) { + return String.format("(bvuge %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvUGt(final BvUGtExpr expr) { + return String.format("(bvugt %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvULeq(final BvULeqExpr expr) { + return String.format("(bvule %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvULt(final BvULtExpr expr) { + return String.format("(bvult %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvSGeq(final BvSGeqExpr expr) { + return String.format("(bvsge %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvSGt(final BvSGtExpr expr) { + return String.format("(bvsgt %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvSLeq(final BvSLeqExpr expr) { + return String.format("(bvsle %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformBvSLt(final BvSLtExpr expr) { + return String.format("(bvslt %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + /* + * Floating points + */ + + protected String transformFpLit(final FpLitExpr expr) { + return String.format("(fp #b%s #b%s #b%s)", + expr.getHidden() ? "1" : "0", + transformBvLit(expr.getExponent()).substring(2), + transformBvLit(expr.getSignificand()).substring(2) + ); + } + + protected String transformFpAdd(final FpAddExpr expr) { + if(expr.getArity() == 1) { + return toTerm(expr.getOps().get(0)); + } + else if(expr.getArity() == 0) { + return String.format("(_ +zero %d %d)", expr.getType().getExponent(), expr.getType().getSignificand()); + } + else { + final String[] opTerms = expr.getOps().stream() + .map(this::toTerm) + .toArray(String[]::new); + + return String.format("(fp.add %s %s)", transformFpRoundingMode(expr.getRoundingMode()), String.join(" ", opTerms)); + } + } + + protected String transformFpSub(final FpSubExpr expr) { + return String.format("(fp.sub %s %s %s)", transformFpRoundingMode(expr.getRoundingMode()), toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformFpPos(final FpPosExpr expr) { + return toTerm(expr.getOp()); + } + + protected String transformFpNeg(final FpNegExpr expr) { + return String.format("(fp.neg %s)", toTerm(expr.getOp())); + } + + protected String transformFpMul(final FpMulExpr expr) { + if(expr.getArity() == 1) { + return toTerm(expr.getOps().get(0)); + } + else if(expr.getArity() == 0) { + return String.format("(fp #b0 #b0%s #b%s)", "1".repeat(expr.getType().getExponent()-1), "0".repeat(expr.getType().getSignificand())); + } + else { + final String[] opTerms = expr.getOps().stream() + .map(this::toTerm) + .toArray(String[]::new); + + return String.format("(fp.mul %s %s)", transformFpRoundingMode(expr.getRoundingMode()), String.join(" ", opTerms)); + } + } + + protected String transformFpDiv(final FpDivExpr expr) { + return String.format("(fp.div %s %s %s)", transformFpRoundingMode(expr.getRoundingMode()), toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformFpEq(final FpEqExpr expr) { + return String.format("(fp.eq %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformFpNeq(final FpNeqExpr expr) { + return String.format("(not (fp.eq %s %s))", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformFpGeq(final FpGeqExpr expr) { + return String.format("(fp.geq %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformFpGt(final FpGtExpr expr) { + return String.format("(fp.gt %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformFpLeq(final FpLeqExpr expr) { + return String.format("(fp.leq %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformFpLt(final FpLtExpr expr) { + return String.format("(fp.lt %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformFpAbs(final FpAbsExpr expr) { + return String.format("(fp.abs %s)", toTerm(expr.getOp())); + } + + protected String transformFpRoundToIntegral(final FpRoundToIntegralExpr expr) { + return String.format("(fp.roundToIntegral %s %s)", transformFpRoundingMode(expr.getRoundingMode()), toTerm(expr.getOp())); + } + + protected String transformFpMin(final FpMinExpr expr) { + return String.format("(fp.min %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformFpMax(final FpMaxExpr expr) { + return String.format("(fp.max %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformFpSqrt(final FpSqrtExpr expr) { + return String.format("(fp.sqrt %s %s)", transformFpRoundingMode(expr.getRoundingMode()), toTerm(expr.getOp())); + } + + protected String transformFpRem(final FpRemExpr expr) { + return String.format("(fp.rem %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformFpIsNaN(final FpIsNanExpr expr) { + return String.format("(fp.isNaN %s)", toTerm(expr.getOp())); + } + + protected String transformFpFromBv(final FpFromBvExpr expr) { + if(expr.isSigned()) { + return String.format("((_ to_fp %d %d) %s %s)", expr.getType().getExponent(), expr.getType().getSignificand(), transformFpRoundingMode(expr.getRoundingMode()), toTerm(expr.getOp())); + } + else { + return String.format("((_ to_fp_unsigned %d %d) %s %s)", expr.getType().getExponent(), expr.getType().getSignificand(), transformFpRoundingMode(expr.getRoundingMode()), toTerm(expr.getOp())); + } + } + + protected String transformFpToBv(final FpToBvExpr expr) { + if(expr.getSgn()) { + return String.format("((_ fp.to_sbv %d) %s %s) ", expr.getType().getSize(), transformFpRoundingMode(expr.getRoundingMode()), toTerm(expr.getOp())); + } + else { + return String.format("((_ fp.to_ubv %d) %s %s) ", expr.getType().getSize(), transformFpRoundingMode(expr.getRoundingMode()), toTerm(expr.getOp())); + } + } + + protected String transformFpToFp(final FpToFpExpr expr) { + return String.format("((_ to_fp %d %d) %s %s)", expr.getType().getExponent(), expr.getType().getSignificand(), transformFpRoundingMode(expr.getRoundingMode()), toTerm(expr.getOp())); + } + + private String transformFpRoundingMode(FpRoundingMode roundingMode) { + switch (roundingMode) { + case RNE: return "RNE"; + case RNA: return "RNA"; + case RTP: return "RTP"; + case RTN: return "RTN"; + case RTZ: return "RTZ"; + default: throw new UnsupportedOperationException(); + } + } + + /* + * Functions + */ + + protected String transformFuncApp(final FuncAppExpr expr) { + final Tuple2, List>> funcAndArgs = extractFuncAndArgs(expr); + final Expr func = funcAndArgs.get1(); + if (func instanceof RefExpr) { + final RefExpr ref = (RefExpr) func; + final Decl decl = ref.getDecl(); + final String funcDecl = transformer.toSymbol(decl); + final List> args = funcAndArgs.get2(); + final String[] argTerms = args.stream() + .map(this::toTerm) + .toArray(String[]::new); + + return String.format("(%s %s)", funcDecl, String.join(" ", argTerms)); + } else { + throw new UnsupportedOperationException("Higher order functions are not supported: " + func); + } + } + + private static Tuple2, List>> extractFuncAndArgs(final FuncAppExpr expr) { + final Expr func = expr.getFunc(); + final Expr arg = expr.getParam(); + if (func instanceof FuncAppExpr) { + final FuncAppExpr funcApp = (FuncAppExpr) func; + final Tuple2, List>> funcAndArgs = extractFuncAndArgs(funcApp); + final Expr resFunc = funcAndArgs.get1(); + final List> args = funcAndArgs.get2(); + final List> resArgs = ImmutableList.>builder().addAll(args).add(arg).build(); + return Tuple2.of(resFunc, resArgs); + } else { + return Tuple2.of(func, ImmutableList.of(arg)); + } + } + + /* + * Arrays + */ + + protected String transformArrayRead(final ArrayReadExpr expr) { + return String.format("(select %s %s)", toTerm(expr.getArray()), toTerm(expr.getIndex())); + } + + protected String transformArrayWrite(final ArrayWriteExpr expr) { + return String.format("(store %s %s %s)", toTerm(expr.getArray()), toTerm(expr.getIndex()), toTerm(expr.getElem())); + } + + protected String transformArrayEq(final ArrayEqExpr expr) { + return String.format("(= %s %s)", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformArrayNeq(final ArrayNeqExpr expr) { + return String.format("(not (= %s %s))", toTerm(expr.getLeftOp()), toTerm(expr.getRightOp())); + } + + protected String transformArrayLit(final ArrayLitExpr expr) { + String running = String.format("((as const %s) %s)", transformer.toSort(expr.getType()), toTerm(expr.getElseElem())); + for (Tuple2, ? extends Expr> elem : expr.getElements()) { + running = String.format("(store %s %s %s)", running, toTerm(elem.get1()), toTerm(elem.get2())); + } + return running; + } + + protected String transformArrayInit(final ArrayInitExpr expr) { + String running = String.format("((as const %s) %s)", transformer.toSort(expr.getType()), toTerm(expr.getElseElem())); + for (Tuple2, ? extends Expr> elem : expr.getElements()) { + running = String.format("(store %s %s %s)", running, toTerm(elem.get1()), toTerm(elem.get2())); + } + return running; + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverBinary.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverBinary.java new file mode 100644 index 0000000000..30bf700ec7 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverBinary.java @@ -0,0 +1,134 @@ +package hu.bme.mit.theta.solver.smtlib.impl.generic; + +import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinaryException; +import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; + +import static com.google.common.base.Preconditions.checkState; + +public final class GenericSmtLibSolverBinary implements SmtLibSolverBinary { + + private final Process solverProcess; + private final PrintWriter solverInput; + private final Reader solverOutput; + + public GenericSmtLibSolverBinary(final Path solverPath, final String[] args) { + final var processCmd = new ArrayList(); + processCmd.add(solverPath.toAbsolutePath().toString()); + processCmd.addAll(Arrays.asList(args)); + try { + solverProcess = new ProcessBuilder(processCmd) + .redirectError(ProcessBuilder.Redirect.INHERIT) + .start(); + solverInput = new PrintWriter(solverProcess.getOutputStream(), true, StandardCharsets.US_ASCII); + solverOutput = new InputStreamReader(solverProcess.getInputStream(), StandardCharsets.US_ASCII); + checkState(solverProcess.isAlive()); + } catch (IOException e) { + throw new SmtLibSolverBinaryException(e); + } + } + + @Override + public void issueCommand(final String command) { + checkState(solverProcess.isAlive()); + solverInput.println(command); + } + + @Override + public String readResponse() { + checkState(solverProcess.isAlive()); + final var sb = new StringBuilder(256); + final var readProcessor = new ReadProcessor(); + while (sb.length() == 0 || !readProcessor.isReady()) { + Thread.yield(); + if (!solverProcess.isAlive()) { + throw new SmtLibSolverBinaryException("Solver process terminated early"); + } + try { + while (solverOutput.ready() && !readProcessor.isReady()) { + readProcessor.step(sb, (char) solverOutput.read()); + } + } catch (IOException e) { + throw new SmtLibSolverBinaryException(e); + } + } + return sb.toString().trim(); + } + + @Override + public void close() { + solverProcess.destroyForcibly(); + } + + private static final class ReadProcessor { + private enum ReadStatus { + INIT, LINE, PARENTHESES, STRING, COMMENT, READY + } + + private ReadProcessor.ReadStatus status = ReadProcessor.ReadStatus.INIT; + private int level = 0; + + public void step(final StringBuilder sb, final char c) { + switch (status) { + case INIT: + if (c == '(') { + level++; + sb.append(c); + status = ReadProcessor.ReadStatus.PARENTHESES; + } else if (c == ';') { + status = ReadProcessor.ReadStatus.COMMENT; + } else if (Character.isAlphabetic(c)) { + sb.append(c); + status = ReadProcessor.ReadStatus.LINE; + } + break; + case LINE: + if (c == '\n') { + status = ReadProcessor.ReadStatus.READY; + } else { + sb.append(c); + } + break; + case PARENTHESES: + sb.append(c); + if (c == '(') { + level++; + } else if (c == ')') { + level--; + if (level == 0) { + status = ReadProcessor.ReadStatus.READY; + } + } else if (c == '"') { + status = ReadProcessor.ReadStatus.STRING; + } + break; + case STRING: + sb.append(c); + if (c == '"') { + status = ReadProcessor.ReadStatus.PARENTHESES; + } + break; + case COMMENT: + if (c == '\n') { + status = ReadProcessor.ReadStatus.INIT; + } + break; + case READY: + default: + throw new AssertionError(); + } + } + + public boolean isReady() { + return status == ReadProcessor.ReadStatus.READY; + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverFactory.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverFactory.java new file mode 100644 index 0000000000..370ec3b1e2 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverFactory.java @@ -0,0 +1,48 @@ +package hu.bme.mit.theta.solver.smtlib.impl.generic; + +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.UCSolver; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolver; + +import java.nio.file.Path; + +public class GenericSmtLibSolverFactory implements SolverFactory { + protected final Path solverPath; + protected final String[] args; + + protected GenericSmtLibSolverFactory(Path solverPath, String[] args) { + this.solverPath = solverPath; + this.args = args; + } + + public static GenericSmtLibSolverFactory create(Path solverPath, String[] args) { + return new GenericSmtLibSolverFactory(solverPath, args); + } + + @Override + public Solver createSolver() { + final var symbolTable = new GenericSmtLibSymbolTable(); + final var transformationManager = new GenericSmtLibTransformationManager(symbolTable); + final var termTransformer = new GenericSmtLibTermTransformer(symbolTable); + final var solverBinary = new GenericSmtLibSolverBinary(solverPath, args); + + return new SmtLibSolver(symbolTable, transformationManager, termTransformer, solverBinary, false); + } + + @Override + public UCSolver createUCSolver() { + final var symbolTable = new GenericSmtLibSymbolTable(); + final var transformationManager = new GenericSmtLibTransformationManager(symbolTable); + final var termTransformer = new GenericSmtLibTermTransformer(symbolTable); + final var solverBinary = new GenericSmtLibSolverBinary(solverPath, args); + + return new SmtLibSolver(symbolTable, transformationManager, termTransformer, solverBinary, true); + } + + @Override + public ItpSolver createItpSolver() { + throw new UnsupportedOperationException("The generic driver does not support interpolation"); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverInstaller.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverInstaller.java new file mode 100644 index 0000000000..1ccc548242 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSolverInstaller.java @@ -0,0 +1,90 @@ +package hu.bme.mit.theta.solver.smtlib.impl.generic; + +import hu.bme.mit.theta.common.logging.Logger; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; + +import static com.google.common.base.Preconditions.checkState; + +public final class GenericSmtLibSolverInstaller extends SmtLibSolverInstaller.Default { + private Path solverPath; + private String[] solverArgs; + + public GenericSmtLibSolverInstaller(final Logger logger) { + super(logger); + } + + @Override + protected String getSolverName() { + return "generic"; + } + + public void install(final Path home, final String version, final Path solverPath, final String[] solverArgs) throws SmtLibSolverInstallerException { + this.solverPath = solverPath; + this.solverArgs = solverArgs; + super.install(home, version, version, solverPath); + } + + @Override + protected void installSolver(Path installDir, String version) throws SmtLibSolverInstallerException { + checkState(solverPath != null); + try { + final var solverFilePath = solverFile(installDir); + Files.writeString(solverFilePath, solverPath.toAbsolutePath().toString(), StandardCharsets.UTF_8); + + final var solverInfoPath = infoFile(installDir); + final var info = Files.readString(solverInfoPath, StandardCharsets.UTF_8); + Files.writeString( + solverInfoPath, + info + String.format("binary=%s\n", solverPath.toAbsolutePath().toString()), + StandardCharsets.UTF_8 + ); + + solverPath = null; + } + catch (IOException e) { + throw new SmtLibSolverInstallerException(String.format("Error: %s", e.getMessage()), e); + } + } + + @Override + protected void uninstallSolver(Path installDir, String version) throws SmtLibSolverInstallerException { + try { + final var solverFilePath = solverFile(installDir); + Files.delete(solverFilePath); + } + catch (IOException e) { + throw new SmtLibSolverInstallerException(String.format("Error: %s", e.getMessage()), e); + } + } + + @Override + public SolverFactory getSolverFactory(final Path installDir, final String version, final Path solverPath, final String[] solverArgs) { + return GenericSmtLibSolverFactory.create(solverPath, solverArgs); + } + + @Override + public List getSupportedVersions() { + return Collections.emptyList(); + } + + @Override + protected String[] getDefaultSolverArgs(String version) { + checkState(solverArgs != null); + final var tmp = solverArgs; + solverArgs = null; + return tmp; + } + + private Path solverFile(final Path installDir) { + return installDir.resolve("solver.txt"); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSymbolTable.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSymbolTable.java new file mode 100644 index 0000000000..d0903ccf00 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSymbolTable.java @@ -0,0 +1,62 @@ +package hu.bme.mit.theta.solver.smtlib.impl.generic; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Maps; +import hu.bme.mit.theta.core.decl.ConstDecl; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +public class GenericSmtLibSymbolTable implements SmtLibSymbolTable { + private static final String problematicCharactersRegex = ":"; + private static final String problematicCharactersReplacement = "\\$"; + + private final BiMap, String> constToSymbol; + private final BiMap, String> constToDeclaration; + + public GenericSmtLibSymbolTable() { + constToSymbol = Maps.synchronizedBiMap(HashBiMap.create()); + constToDeclaration = Maps.synchronizedBiMap(HashBiMap.create()); + } + + @Override + public boolean definesConst(final ConstDecl constDecl) { + return constToSymbol.containsKey(constDecl); + } + + @Override + public boolean definesSymbol(final String symbol) { + return constToSymbol.inverse().containsKey(symbol.replaceAll(problematicCharactersRegex, problematicCharactersReplacement)); + } + + @Override + public String getSymbol(final ConstDecl constDecl) { + checkArgument(definesConst(constDecl)); + return constToSymbol.get(constDecl); + } + + @Override + public String getDeclaration(final ConstDecl constDecl) { + checkArgument(definesConst(constDecl)); + return constToDeclaration.get(constDecl); + } + + @Override + public ConstDecl getConst(final String symbol) { + checkArgument(definesSymbol(symbol)); + return constToSymbol.inverse().get(symbol); + } + + @Override + public void put(final ConstDecl constDecl, final String symbol, final String declaration) { + checkNotNull(constDecl); + checkNotNull(symbol); + checkNotNull(declaration); + checkState(!constToSymbol.containsKey(constDecl), "Constant not found."); + constToSymbol.put(constDecl, symbol.replaceAll(problematicCharactersRegex, problematicCharactersReplacement)); + constToDeclaration.put(constDecl, declaration.replaceAll(problematicCharactersRegex, problematicCharactersReplacement)); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java new file mode 100644 index 0000000000..3cb7e5c0c3 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java @@ -0,0 +1,861 @@ +package hu.bme.mit.theta.solver.smtlib.impl.generic; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.ImmutableList; +import hu.bme.mit.theta.common.QuadFunction; +import hu.bme.mit.theta.common.TernaryOperator; +import hu.bme.mit.theta.common.TriFunction; +import hu.bme.mit.theta.common.Tuple2; +import hu.bme.mit.theta.core.decl.Decl; +import hu.bme.mit.theta.core.decl.ParamDecl; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.LitExpr; +import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.abstracttype.AddExpr; +import hu.bme.mit.theta.core.type.abstracttype.DivExpr; +import hu.bme.mit.theta.core.type.abstracttype.EqExpr; +import hu.bme.mit.theta.core.type.abstracttype.GeqExpr; +import hu.bme.mit.theta.core.type.abstracttype.GtExpr; +import hu.bme.mit.theta.core.type.abstracttype.LeqExpr; +import hu.bme.mit.theta.core.type.abstracttype.LtExpr; +import hu.bme.mit.theta.core.type.abstracttype.ModExpr; +import hu.bme.mit.theta.core.type.abstracttype.MulExpr; +import hu.bme.mit.theta.core.type.abstracttype.NegExpr; +import hu.bme.mit.theta.core.type.abstracttype.RemExpr; +import hu.bme.mit.theta.core.type.abstracttype.SubExpr; +import hu.bme.mit.theta.core.type.anytype.IteExpr; +import hu.bme.mit.theta.core.type.arraytype.ArrayReadExpr; +import hu.bme.mit.theta.core.type.arraytype.ArrayType; +import hu.bme.mit.theta.core.type.arraytype.ArrayWriteExpr; +import hu.bme.mit.theta.core.type.booltype.AndExpr; +import hu.bme.mit.theta.core.type.booltype.BoolExprs; +import hu.bme.mit.theta.core.type.booltype.IffExpr; +import hu.bme.mit.theta.core.type.booltype.ImplyExpr; +import hu.bme.mit.theta.core.type.booltype.NotExpr; +import hu.bme.mit.theta.core.type.booltype.OrExpr; +import hu.bme.mit.theta.core.type.booltype.XorExpr; +import hu.bme.mit.theta.core.type.bvtype.BvAddExpr; +import hu.bme.mit.theta.core.type.bvtype.BvAndExpr; +import hu.bme.mit.theta.core.type.bvtype.BvArithShiftRightExpr; +import hu.bme.mit.theta.core.type.bvtype.BvConcatExpr; +import hu.bme.mit.theta.core.type.bvtype.BvExprs; +import hu.bme.mit.theta.core.type.bvtype.BvExtractExpr; +import hu.bme.mit.theta.core.type.bvtype.BvLitExpr; +import hu.bme.mit.theta.core.type.bvtype.BvLogicShiftRightExpr; +import hu.bme.mit.theta.core.type.bvtype.BvNegExpr; +import hu.bme.mit.theta.core.type.bvtype.BvNotExpr; +import hu.bme.mit.theta.core.type.bvtype.BvOrExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSDivExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSExtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSGeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSGtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSLeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSLtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSModExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSRemExpr; +import hu.bme.mit.theta.core.type.bvtype.BvShiftLeftExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSubExpr; +import hu.bme.mit.theta.core.type.bvtype.BvType; +import hu.bme.mit.theta.core.type.bvtype.BvUDivExpr; +import hu.bme.mit.theta.core.type.bvtype.BvUGeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvUGtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvULeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvULtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvURemExpr; +import hu.bme.mit.theta.core.type.bvtype.BvXorExpr; +import hu.bme.mit.theta.core.type.bvtype.BvZExtExpr; +import hu.bme.mit.theta.core.type.fptype.FpAbsExpr; +import hu.bme.mit.theta.core.type.fptype.FpAddExpr; +import hu.bme.mit.theta.core.type.fptype.FpDivExpr; +import hu.bme.mit.theta.core.type.fptype.FpEqExpr; +import hu.bme.mit.theta.core.type.fptype.FpExprs; +import hu.bme.mit.theta.core.type.fptype.FpGeqExpr; +import hu.bme.mit.theta.core.type.fptype.FpGtExpr; +import hu.bme.mit.theta.core.type.fptype.FpIsNanExpr; +import hu.bme.mit.theta.core.type.fptype.FpLeqExpr; +import hu.bme.mit.theta.core.type.fptype.FpLtExpr; +import hu.bme.mit.theta.core.type.fptype.FpMaxExpr; +import hu.bme.mit.theta.core.type.fptype.FpMinExpr; +import hu.bme.mit.theta.core.type.fptype.FpMulExpr; +import hu.bme.mit.theta.core.type.fptype.FpNegExpr; +import hu.bme.mit.theta.core.type.fptype.FpRemExpr; +import hu.bme.mit.theta.core.type.fptype.FpRoundToIntegralExpr; +import hu.bme.mit.theta.core.type.fptype.FpRoundingMode; +import hu.bme.mit.theta.core.type.fptype.FpSqrtExpr; +import hu.bme.mit.theta.core.type.fptype.FpSubExpr; +import hu.bme.mit.theta.core.type.functype.FuncExprs; +import hu.bme.mit.theta.core.type.functype.FuncLitExpr; +import hu.bme.mit.theta.core.type.functype.FuncType; +import hu.bme.mit.theta.core.type.inttype.IntToRatExpr; +import hu.bme.mit.theta.core.utils.BvUtils; +import hu.bme.mit.theta.core.utils.ExprUtils; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Lexer; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolverException; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibModel; +import hu.bme.mit.theta.solver.smtlib.solver.parser.ThrowExceptionErrorListener; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTermTransformer; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static hu.bme.mit.theta.core.decl.Decls.Param; +import static hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Array; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Exists; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Forall; +import static hu.bme.mit.theta.core.type.functype.FuncExprs.Func; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; +import static hu.bme.mit.theta.core.type.rattype.RatExprs.Rat; +import static hu.bme.mit.theta.core.utils.TypeUtils.cast; +import static hu.bme.mit.theta.core.utils.TypeUtils.castBv; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.BinaryContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.DecimalContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Exists_termContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Forall_termContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Generic_termContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.HexadecimalContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.IdentifierContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.IndexContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.NumeralContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Qual_identifierContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.SortContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Spec_constantContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.SymbolContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.TermContext; +import static java.util.stream.Collectors.toList; + +public class GenericSmtLibTermTransformer implements SmtLibTermTransformer { + protected final SmtLibSymbolTable symbolTable; + protected final Map funAppTransformer; + + public GenericSmtLibTermTransformer(final SmtLibSymbolTable symbolTable) { + this.symbolTable = symbolTable; + this.funAppTransformer = new HashMap<>() {{ + // Generic + put("ite", exprIteOperator()); + + // Abstract + put("=", exprRelationalOperator(EqExpr::create2)); + put("<=", exprRelationalOperator(LeqExpr::create2)); + put("<", exprRelationalOperator(LtExpr::create2)); + put(">=", exprRelationalOperator(GeqExpr::create2)); + put(">", exprRelationalOperator(GtExpr::create2)); + put("+", exprMultiaryOperator(AddExpr::create2)); + put("-", exprMinusOperator()); + put("*", exprMultiaryOperator(MulExpr::create2)); + put("div", exprBinaryOperator(DivExpr::create2)); + put("/", exprBinaryOperator(DivExpr::create2)); + put("mod", exprBinaryOperator(ModExpr::create2)); + put("rem", exprBinaryOperator(RemExpr::create2)); + + // Booleal + put("not", exprUnaryOperator(NotExpr::create)); + put("or", exprMultiaryOperator(OrExpr::create)); + put("and", exprMultiaryOperator(AndExpr::create)); + put("xor", exprBinaryOperator(XorExpr::create)); + put("iff", exprBinaryOperator(IffExpr::create)); + put("=>", exprBinaryOperator(ImplyExpr::create)); + + // Integer + put("to_real", exprUnaryOperator(IntToRatExpr::create)); + + // Rational + + // Bitvector + put("concat", exprMultiaryOperator(BvConcatExpr::create)); + put("extract", exprBvExtractOperator()); + put("zero_extend", exprBvExtendOperator(BvZExtExpr::create)); + put("sign_extend", exprBvExtendOperator(BvSExtExpr::create)); + put("bvadd", exprMultiaryOperator(BvAddExpr::create)); + put("bvsub", exprBinaryOperator(BvSubExpr::create)); + put("bvneg", exprUnaryOperator(BvNegExpr::create)); + put("bvmul", exprMultiaryOperator(BvAddExpr::create)); + put("bvudiv", exprBinaryOperator(BvUDivExpr::create)); + put("bvsdiv", exprBinaryOperator(BvSDivExpr::create)); + put("bvsmod", exprBinaryOperator(BvSModExpr::create)); + put("bvsrem", exprBinaryOperator(BvURemExpr::create)); + put("bvurem", exprBinaryOperator(BvSRemExpr::create)); + put("bvand", exprMultiaryOperator(BvAndExpr::create)); + put("bvor", exprMultiaryOperator(BvOrExpr::create)); + put("bvxor", exprMultiaryOperator(BvXorExpr::create)); + put("bvnot", exprUnaryOperator(BvNotExpr::create)); + put("bvshl", exprBinaryOperator(BvShiftLeftExpr::create)); + put("bvashr", exprBinaryOperator(BvArithShiftRightExpr::create)); + put("bvlshr", exprBinaryOperator(BvLogicShiftRightExpr::create)); + put("bvult", exprBinaryOperator(BvULtExpr::create)); + put("bvslt", exprBinaryOperator(BvSLtExpr::create)); + put("bvule", exprBinaryOperator(BvULeqExpr::create)); + put("bvsle", exprBinaryOperator(BvSLeqExpr::create)); + put("bvugt", exprBinaryOperator(BvUGtExpr::create)); + put("bvsgt", exprBinaryOperator(BvSGtExpr::create)); + put("bvuge", exprBinaryOperator(BvUGeqExpr::create)); + put("bvsge", exprBinaryOperator(BvSGeqExpr::create)); + + // Floating point + + put("fp", exprFpLit()); + put("fp.add", exprFpMultiaryOperator(FpAddExpr::create)); + put("fp.sub", exprFpBinaryOperator(FpSubExpr::create)); + put("fp.neg", exprUnaryOperator(FpNegExpr::create)); + put("fp.mul", exprFpMultiaryOperator(FpMulExpr::create)); + put("fp.div", exprFpBinaryOperator(FpDivExpr::create)); + put("fp.eq", exprBinaryOperator(FpEqExpr::create)); + put("fp.geq", exprBinaryOperator(FpGeqExpr::create)); + put("fp.gt", exprBinaryOperator(FpGtExpr::create)); + put("fp.leq", exprBinaryOperator(FpLeqExpr::create)); + put("fp.lt", exprBinaryOperator(FpLtExpr::create)); + put("fp.abs", exprUnaryOperator(FpAbsExpr::create)); + put("fp.roundToIntegral", exprFpUnaryOperator(FpRoundToIntegralExpr::create)); + put("fp.min", exprBinaryOperator(FpMinExpr::create)); + put("fp.max", exprBinaryOperator(FpMaxExpr::create)); + put("fp.sqrt", exprFpUnaryOperator(FpSqrtExpr::create)); + put("fp.rem", exprBinaryOperator(FpRemExpr::create)); + put("fp.isNaN", exprUnaryOperator(FpIsNanExpr::create)); + + // Array + put("select", exprArrayReadOperator()); + put("store", exprArrayWriteOperator()); + }}; + } + + /* Public interface */ + + @Override + public

LitExpr> toFuncLitExpr(final String funcLitImpl, final FuncType type, final SmtLibModel model) { + final var litExpr = toFuncLitExpr(funcLitImpl, model); + if(litExpr == null) { + return null; + } + else if(litExpr instanceof LitExpr) { + return (LitExpr>) cast(litExpr, type); + } + else { + return (LitExpr>) cast(ExprUtils.simplify(litExpr), type); + } + } + + private Expr toFuncLitExpr(final String funcLitImpl, final SmtLibModel model) { + final var lexer = new SMTLIBv2Lexer(CharStreams.fromString(funcLitImpl)); + final var parser = new SMTLIBv2Parser(new CommonTokenStream(lexer)); + lexer.removeErrorListeners(); + lexer.addErrorListener(new ThrowExceptionErrorListener()); + parser.removeErrorListeners(); + parser.addErrorListener(new ThrowExceptionErrorListener()); + + return transformFuncDef(parser.function_def(), model, HashBiMap.create()); + } + + @Override + public Expr toExpr(final String term, final T type, final SmtLibModel model) { + final var expr = toExpr(term, model); + return cast(expr, type); + } + + private Expr toExpr(final String term, final SmtLibModel model) { + final var lexer = new SMTLIBv2Lexer(CharStreams.fromString(term)); + final var parser = new SMTLIBv2Parser(new CommonTokenStream(lexer)); + lexer.removeErrorListeners(); + lexer.addErrorListener(new ThrowExceptionErrorListener()); + parser.removeErrorListeners(); + parser.addErrorListener(new ThrowExceptionErrorListener()); + + return transformTerm(parser.term(), model, HashBiMap.create()); + } + + @Override + public LitExpr toLitExpr(final String litImpl, final T type, final SmtLibModel model) { + final var litExpr = toLitExpr(litImpl, model); + + if(litExpr == null) { + return null; + } + else if(litExpr instanceof LitExpr) { + return (LitExpr) cast(litExpr, type); + } + else { + return (LitExpr) cast(ExprUtils.simplify(litExpr), type); + } + } + + private Expr toLitExpr(final String litImpl, final SmtLibModel model) { + final var lexer = new SMTLIBv2Lexer(CharStreams.fromString(litImpl)); + final var parser = new SMTLIBv2Parser(new CommonTokenStream(lexer)); + lexer.removeErrorListeners(); + lexer.addErrorListener(new ThrowExceptionErrorListener()); + parser.removeErrorListeners(); + parser.addErrorListener(new ThrowExceptionErrorListener()); + + final var funcDef = parser.function_def(); + final var paramDecls = funcDef.sorted_var().stream() + .map(sv -> Param(sv.symbol().getText(), transformSort(sv.sort()))) + .collect(toList()); + + final var vars = HashBiMap., String>create(); + pushParams(paramDecls, vars); + final var expr = transformTerm(funcDef.term(), model, vars); + popParams(paramDecls, vars); + + return expr; + } + + @Override + @SuppressWarnings("unchecked") + public LitExpr> toArrayLitExpr(final String arrayLitImpl, final ArrayType type, final SmtLibModel model) { + final var arrayLitExpr = toLitExpr(arrayLitImpl, model); + + if(arrayLitExpr == null) { + return null; + } + else if(arrayLitExpr instanceof IteExpr) { + final var entryExprsBuilder = new ImmutableList.Builder, ? extends Expr>>(); + var iteExpr = (IteExpr) arrayLitExpr; + while (true) { + entryExprsBuilder.add(Tuple2.of((Expr) iteExpr.getCond().getOps().get(1), iteExpr.getThen())); + if (iteExpr.getElse() instanceof IteExpr) { + iteExpr = (IteExpr) iteExpr.getElse(); + } else { + return Array(entryExprsBuilder.build(), iteExpr.getElse(), type); + } + } + } + else { + return (LitExpr>) cast(ExprUtils.simplify(arrayLitExpr), type); + } + } + + @Override + public LitExpr toBvLitExpr(final String bvLitImpl, final BvType type, final SmtLibModel model) { + final var bvLitExpr = toLitExpr(bvLitImpl, model); + + if(bvLitExpr == null) { + return null; + } + else if(bvLitExpr instanceof BvLitExpr) { + return (BvLitExpr) bvLitExpr; + } + else { + return (LitExpr) cast(ExprUtils.simplify(bvLitExpr), type); + } + } + + /* End of public interface */ + + /* Visitor implementation */ + + protected Expr transformFuncDef(final SMTLIBv2Parser.Function_defContext ctx, final SmtLibModel model, final BiMap, String> vars) { + assert model != null; + assert vars != null; + + final var paramDecls = ctx.sorted_var().stream() + .map(sv -> Param(sv.symbol().getText(), transformSort(sv.sort()))) + .collect(toList()); + checkArgument(paramDecls.size() == 1, "Only unary functions are supported"); + + pushParams(paramDecls, vars); + final var op = transformTerm(ctx.term(), model, vars); + popParams(paramDecls, vars); + + return Func(paramDecls.get(0), op); + } + + protected Expr transformTerm(final TermContext ctx, final SmtLibModel model, final BiMap, String> vars) { + assert model != null; + assert vars != null; + + if(ctx.spec_constant() != null) { + return transformSpecConstant(ctx.spec_constant(), model, vars); + } + else if(ctx.qual_identifier() != null) { + return transformQualIdentifier(ctx.qual_identifier(), model, vars); + } + else if(ctx.generic_term() != null) { + return transformGenericTerm(ctx.generic_term(), model, vars); + } + else if(ctx.let_term() != null) { + throw new UnsupportedOperationException(); + } + else if(ctx.forall_term() != null) { + return transformForallTerm(ctx.forall_term(), model, vars); + } + else if(ctx.exists_term() != null) { + return transformExistsTerm(ctx.exists_term(), model, vars); + } + else if(ctx.match_term() != null) { + throw new UnsupportedOperationException(); + } + else if(ctx.annotate_term() != null) { + return transformTerm(ctx.annotate_term().term(), model, vars); + } + else { + throw new SmtLibSolverException("Invalid input"); + } + } + + protected Expr transformSpecConstant(final Spec_constantContext ctx, final SmtLibModel model, final BiMap, String> vars) { + assert model != null; + assert vars != null; + + if(ctx.numeral() != null) { + return transformNumeral(ctx.numeral(), model, vars); + } + else if(ctx.decimal() != null) { + return transformDecimal(ctx.decimal(), model, vars); + } + else if(ctx.hexadecimal() != null) { + return transformHexadecimal(ctx.hexadecimal(), model, vars); + } + else if(ctx.binary() != null) { + return transformBinary(ctx.binary(), model, vars); + } + else if(ctx.string() != null) { + throw new UnsupportedOperationException(); + } + else { + throw new SmtLibSolverException("Invalid input"); + } + } + + protected Expr transformQualIdentifier(final Qual_identifierContext ctx, final SmtLibModel model, final BiMap, String> vars) { + assert model != null; + assert vars != null; + + return transformIdentifier(ctx.identifier(), model, vars); + } + + protected Expr transformGenericTerm(final Generic_termContext ctx, final SmtLibModel model, final BiMap, String> vars) { + assert model != null; + assert vars != null; + + final var funName = ctx.qual_identifier().identifier().symbol().getText(); + + final var funParams = ctx.qual_identifier().identifier().index(); + final var funAppParams = ctx.term(); + + if (funName.equals("const")) { // as const construct + final var constType = transformSort(ctx.qual_identifier().sort()); + if (constType instanceof ArrayType) { + checkArgument(funAppParams.size() == 1, "Invalid as const construct"); + + final var arrayType = (ArrayType) constType; + final var expr = transformTerm(funAppParams.get(0), model, vars); + return createArrayLitExpr(expr, arrayType); + } + else { + throw new UnsupportedOperationException(); + } + } else if (funAppTransformer.containsKey(funName)) { // known function application + return funAppTransformer.get(funName).apply(funParams, funAppParams, model, vars); + } else { // custom function application + checkArgument(funParams.size() == 0, "Custom unary function application cannot have parameter"); + checkArgument(funAppParams.size() == 1, "Only unary functions are supported"); + + return createFuncAppExpr(funName, funAppParams.get(0), model, vars); + } + } + + @SuppressWarnings("unchecked") + private Expr createArrayLitExpr(final Expr elze, final ArrayType type) { + return Array(Collections.emptyList(), (Expr) elze, type); + } + + private

Expr createFuncAppExpr(final String funName, final TermContext funAppParam, final SmtLibModel model, final BiMap, String> vars) { + final Expr funcExpr; + if (symbolTable.definesSymbol(funName)) { + funcExpr = checkNotNull(symbolTable.getConst(funName).getRef()); + } else { + final var funDefImpl = model.getTerm(funName); + funcExpr = toFuncLitExpr(funDefImpl, model); + } + + assert funcExpr.getType() instanceof FuncType; + @SuppressWarnings("unchecked") final var funType = (FuncType) funcExpr.getType(); + final var paramExpr = transformTerm(funAppParam, model, vars); + + return FuncExprs.App(cast(funcExpr, funType), cast(paramExpr, funType.getParamType())); + } + + protected Expr transformForallTerm(final Forall_termContext ctx, final SmtLibModel model, final BiMap, String> vars) { + assert model != null; + assert vars != null; + + final var paramDecls = ctx.sorted_var().stream() + .map(sv -> Param(sv.symbol().getText(), transformSort(sv.sort()))) + .collect(toList()); + + pushParams(paramDecls, vars); + final var op = transformTerm(ctx.term(), model, vars); + popParams(paramDecls, vars); + + assert op != null; + return Forall(paramDecls, cast(op, Bool())); + } + + protected Expr transformExistsTerm(final Exists_termContext ctx, final SmtLibModel model, final BiMap, String> vars) { + assert model != null; + assert vars != null; + + final var paramDecls = ctx.sorted_var().stream() + .map(sv -> Param(sv.symbol().getText(), transformSort(sv.sort()))) + .collect(toList()); + + pushParams(paramDecls, vars); + final var op = transformTerm(ctx.term(), model, vars); + popParams(paramDecls, vars); + + assert op != null; + return Exists(paramDecls, cast(op, Bool())); + } + + protected Expr transformIdentifier(final IdentifierContext ctx, final SmtLibModel model, final BiMap, String> vars) { + assert model != null; + assert vars != null; + + if(ctx.symbol().getText().equals("as-array")) { + final var name = ctx.index().get(0).getText(); + final var funcLit = (FuncLitExpr) toFuncLitExpr(model.getTerm(name), model); + return funcLit.getResult(); + } + else if(ctx.symbol().getText().startsWith("bv")) { + final var value = ctx.symbol().getText().substring(2); + final var bvSize = Integer.parseInt(ctx.index().get(0).getText()); + return BvUtils.bigIntegerToNeutralBvLitExpr(new BigInteger(value), bvSize); + } + else if(ctx.symbol().getText().equals("+oo")) { + final var eb = Integer.parseInt(ctx.index().get(0).getText()); + final var sb = Integer.parseInt(ctx.index().get(1).getText()); + return FpExprs.PositiveInfinity(FpExprs.FpType(eb, sb)); + } + else if(ctx.symbol().getText().equals("-oo")) { + final var eb = Integer.parseInt(ctx.index().get(0).getText()); + final var sb = Integer.parseInt(ctx.index().get(1).getText()); + return FpExprs.NegativeInfinity(FpExprs.FpType(eb, sb)); + } + else if(ctx.symbol().getText().equals("+zero")) { + final var eb = Integer.parseInt(ctx.index().get(0).getText()); + final var sb = Integer.parseInt(ctx.index().get(1).getText()); + return FpExprs.PositiveZero(FpExprs.FpType(eb, sb)); + } + else if(ctx.symbol().getText().equals("-zero")) { + final var eb = Integer.parseInt(ctx.index().get(0).getText()); + final var sb = Integer.parseInt(ctx.index().get(1).getText()); + return FpExprs.NegativeZero(FpExprs.FpType(eb, sb)); + } + else { + return transformSymbol(ctx.symbol(), model, vars); + } + } + + protected Expr transformSymbol(final SymbolContext ctx, final SmtLibModel model, final BiMap, String> vars) { + assert model != null; + assert vars != null; + + final var value = ctx.getText(); + switch (value) { + case "true": + return BoolExprs.True(); + case "false": + return BoolExprs.False(); + default: + if(vars.containsValue(value)) { + final var decl = vars.inverse().get(value); + return decl.getRef(); + } + else if(symbolTable.definesSymbol(value)) { + return symbolTable.getConst(value).getRef(); + } + else { + throw new UnsupportedOperationException(); + } + } + } + + protected Expr transformNumeral(final NumeralContext ctx, final SmtLibModel model, final BiMap, String> vars) { + assert model != null; + assert vars != null; + + return Int(ctx.getText()); + } + + protected Expr transformDecimal(final DecimalContext ctx, final SmtLibModel model, final BiMap, String> vars) { + assert model != null; + assert vars != null; + + final var decimal = new BigDecimal(ctx.getText()); + if(decimal.scale() <= 0) { + return Rat(decimal.unscaledValue(), BigInteger.ONE); + } + else { + return Rat(decimal.unscaledValue(), BigInteger.TEN.pow(decimal.scale())); + } + } + + protected Expr transformHexadecimal(final HexadecimalContext ctx, final SmtLibModel model, final BiMap, String> vars) { + assert model != null; + assert vars != null; + + final var numStr = ctx.getText().substring(2); + final var num = new BigInteger(numStr, 16); + return BvUtils.bigIntegerToNeutralBvLitExpr(num, numStr.length() * 4); + } + + protected Expr transformBinary(final BinaryContext ctx, final SmtLibModel model, final BiMap, String> vars) { + assert model != null; + assert vars != null; + + final var numStr = ctx.getText().substring(2); + final var num = new BigInteger(numStr, 2); + return BvUtils.bigIntegerToNeutralBvLitExpr(num, numStr.length()); + } + + protected Type transformSort(final SortContext ctx) { + final var name = ctx.identifier().symbol().getText(); + switch(name) { + case "Bool": + return Bool(); + case "Int": + return Int(); + case "Real": + return Rat(); + case "BitVec": + assert ctx.identifier().index().size() == 1; + return BvExprs.BvType(Integer.parseInt(ctx.identifier().index().get(0).getText())); + case "Array": + assert ctx.sort().size() == 2; + return Array(transformSort(ctx.sort().get(0)), transformSort(ctx.sort().get(1))); + default: + throw new UnsupportedOperationException(); + } + } + + /* End of visitor implementation */ + + /* Variable scope handling */ + + protected void pushParams(final List> paramDecls, BiMap, String> vars) { + vars.putAll(paramDecls.stream().collect(Collectors.toUnmodifiableMap(Function.identity(), Decl::getName))); + } + + protected void popParams(final List> paramDecls, BiMap, String> vars) { + for (final var paramDecl : paramDecls) { + vars.remove(paramDecl, paramDecl.getName()); + } + } + + /* Utilities */ + + @SuppressWarnings("unused") + private OperatorCreatorFunction exprNullaryOperator(final Supplier> function) { + return (params, ops, model, vars) -> { + checkArgument(params.size() == 0, "No parameters expected"); + checkArgument(ops.size() == 0, "Nullary operator expected"); + return function.get(); + }; + } + + private OperatorCreatorFunction exprUnaryOperator(final UnaryOperator> function) { + return (params, ops, model, vars) -> { + checkArgument(params.size() == 0, "No parameters expected"); + checkArgument(ops.size() == 1, "Unary operator expected"); + + final var op = transformTerm(ops.get(0), model, vars); + return function.apply(op); + }; + } + + private OperatorCreatorFunction exprBvExtractOperator() { + return (params, ops, model, vars) -> { + checkArgument(params.size() == 2, "Two parameters expected"); + checkArgument(ops.size() == 1, "Unary operator expected"); + + final var until = Integer.parseInt(params.get(0).numeral().getText()) + 1; + final var from = Integer.parseInt(params.get(1).numeral().getText()); + final var extractFrom = castBv(transformTerm(ops.get(0), model, vars)); + return BvExtractExpr.create(extractFrom, Int(from), Int(until)); + }; + } + + private OperatorCreatorFunction exprBvExtendOperator(final BiFunction, BvType, Expr> function) { + return (params, ops, model, vars) -> { + checkArgument(params.size() == 1, "One parameters expected"); + checkArgument(ops.size() == 1, "Unary operator expected"); + + final var extendWith = Integer.parseInt(params.get(0).numeral().getText()); + final var toExtend = castBv(transformTerm(ops.get(0), model, vars)); + return function.apply(toExtend, BvType.of(toExtend.getType().getSize() + extendWith)); + }; + } + + private OperatorCreatorFunction exprBinaryOperator(final BinaryOperator> function) { + return (params, ops, model, vars) -> { + checkArgument(params.size() == 0, "No parameters expected"); + checkArgument(ops.size() == 2, "Binary operator expected"); + + final var op1 = transformTerm(ops.get(0), model, vars); + final var op2 = transformTerm(ops.get(1), model, vars); + return function.apply(op1, op2); + }; + } + + private OperatorCreatorFunction exprRelationalOperator(final BinaryOperator> function) { + return (params, ops, model, vars) -> { + checkArgument(params.size() == 0, "No parameters expected"); + checkArgument(ops.size() == 2, "Binary operator expected"); + + final var op1 = transformTerm(ops.get(0), model, vars); + final var op2 = transformTerm(ops.get(1), model, vars); + return function.apply(op1, op2); + }; + } + + private OperatorCreatorFunction exprArrayReadOperator() { + return (params, ops, model, vars) -> { + checkArgument(params.size() == 0, "No parameters expected"); + checkArgument(ops.size() == 2, "Binary operator expected"); + + final var op1 = transformTerm(ops.get(0), model, vars); + final var op2 = transformTerm(ops.get(1), model, vars); + return ArrayReadExpr.create(op1, op2); + }; + } + + private OperatorCreatorFunction exprMinusOperator() { + return (params, ops, model, vars) -> { + checkArgument(params.size() == 0, "No parameters expected"); + checkArgument(ops.size() == 1 || ops.size() == 2, "Unary or binary operator expected"); + if(ops.size() == 2) { + return exprBinaryOperator(SubExpr::create2).apply(params, ops, model, vars); + } + else { + return exprUnaryOperator(NegExpr::create2).apply(params, ops, model, vars); + } + }; + } + + @SuppressWarnings("unused") + private OperatorCreatorFunction exprTernaryOperator(final TernaryOperator> function) { + return (params, ops, model, vars) -> { + checkArgument(params.size() == 0, "No parameters expected"); + checkArgument(ops.size() == 3, "Ternary operator expected"); + final Expr op1 = transformTerm(ops.get(0), model, vars); + final Expr op2 = transformTerm(ops.get(1), model, vars); + final Expr op3 = transformTerm(ops.get(2), model, vars); + return function.apply(op1, op2, op3); + }; + } + + private OperatorCreatorFunction exprIteOperator() { + return (params, ops, model, vars) -> { + checkArgument(params.size() == 0, "No parameters expected"); + checkArgument(ops.size() == 3, "Ternary operator expected"); + + final var op1 = transformTerm(ops.get(0), model, vars); + final var op2 = transformTerm(ops.get(1), model, vars); + final var op3 = transformTerm(ops.get(2), model, vars); + return IteExpr.create(op1, op2, op3); + }; + } + + private OperatorCreatorFunction exprArrayWriteOperator() { + return (params, ops, model, vars) -> { + checkArgument(params.size() == 0, "No parameters expected"); + checkArgument(ops.size() == 3, "Ternary operator expected"); + + final var op1 = transformTerm(ops.get(0), model, vars); + final var op2 = transformTerm(ops.get(1), model, vars); + final var op3 = transformTerm(ops.get(2), model, vars); + return ArrayWriteExpr.create(op1, op2, op3); + }; + } + + private OperatorCreatorFunction exprMultiaryOperator(final Function>, Expr> function) { + return (params, ops, model, vars) -> { + checkArgument(params.size() == 0, "No parameters expected"); + + return function.apply(ops.stream().map(op -> transformTerm(op, model, vars)).collect(Collectors.toUnmodifiableList())); + }; + } + + private OperatorCreatorFunction exprFpLit() { + return (params, ops, model, vars) -> { + checkArgument(params.size() == 0, "No parameters expected"); + checkArgument(ops.size() == 3, "Three operators expected"); + + final var hidden = (BvLitExpr) transformTerm(ops.get(1), model, vars); + final var exponent = (BvLitExpr) transformTerm(ops.get(1), model, vars); + final var significand = (BvLitExpr) transformTerm(ops.get(2), model, vars); + + return FpExprs.Fp(hidden.getValue()[0], exponent, significand); + }; + } + + private OperatorCreatorFunction exprFpUnaryOperator(final BiFunction, Expr> function) { + return (params, ops, model, vars) -> { + checkArgument(params.size() == 0, "No parameters expected"); + checkArgument(ops.size() == 2, "Unary floating point operator expected"); + + final var roundingMode = fpOperatorRoundingMode(ops.get(0)); + final var op = transformTerm(ops.get(1), model, vars); + return function.apply(roundingMode, op); + }; + } + + private OperatorCreatorFunction exprFpBinaryOperator(final TriFunction, Expr, Expr> function) { + return (params, ops, model, vars) -> { + checkArgument(params.size() == 0, "No parameters expected"); + checkArgument(ops.size() == 3, "Binary floating point operator expected"); + + final var roundingMode = fpOperatorRoundingMode(ops.get(0)); + final var op1 = transformTerm(ops.get(1), model, vars); + final var op2 = transformTerm(ops.get(2), model, vars); + return function.apply(roundingMode, op1, op2); + }; + } + + private OperatorCreatorFunction exprFpMultiaryOperator(final BiFunction>, Expr> function) { + return (params, ops, model, vars) -> { + checkArgument(params.size() == 0, "No parameters expected"); + checkArgument(ops.size() >= 1); + + return function.apply( + fpOperatorRoundingMode(ops.get(0)), + ops.stream().skip(1).map(op -> transformTerm(op, model, vars)).collect(Collectors.toUnmodifiableList()) + ); + }; + } + + private FpRoundingMode fpOperatorRoundingMode(final TermContext term) { + switch(term.getText()) { + case "RNE": return FpRoundingMode.RNE; + case "RNA": return FpRoundingMode.RNA; + case "RTP": return FpRoundingMode.RTP; + case "RTN": return FpRoundingMode.RTN; + case "RTZ": return FpRoundingMode.RTZ; + default: throw new UnsupportedOperationException(); + } + } + + private interface OperatorCreatorFunction extends QuadFunction< + List, // Parameters + List, // Operands + SmtLibModel, // The model + BiMap, String>, // The variable (param) store + Expr // Return type + > {} +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTransformationManager.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTransformationManager.java new file mode 100644 index 0000000000..6c2cb0ff42 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTransformationManager.java @@ -0,0 +1,51 @@ +package hu.bme.mit.theta.solver.smtlib.impl.generic; + +import hu.bme.mit.theta.core.decl.Decl; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibDeclTransformer; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibExprTransformer; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTypeTransformer; + +public class GenericSmtLibTransformationManager implements SmtLibTransformationManager { + private final SmtLibTypeTransformer typeTransformer; + private final SmtLibDeclTransformer declTransformer; + private final SmtLibExprTransformer exprTransformer; + + public GenericSmtLibTransformationManager(final SmtLibSymbolTable symbolTable) { + this.typeTransformer = instantiateTypeTransformer(this); + this.declTransformer = instantiateDeclTransformer(this, symbolTable); + this.exprTransformer = instantiateExprTransformer(this); + } + + @Override + public final String toSort(final Type type) { + return typeTransformer.toSort(type); + } + + @Override + public final String toSymbol(final Decl decl) { + return declTransformer.toSymbol(decl); + } + + @Override + public final String toTerm(final Expr expr) { + return exprTransformer.toTerm(expr); + } + + protected SmtLibTypeTransformer instantiateTypeTransformer(final SmtLibTransformationManager transformer) { + return new GenericSmtLibTypeTransformer(transformer); + } + + protected SmtLibDeclTransformer instantiateDeclTransformer( + final SmtLibTransformationManager transformer, final SmtLibSymbolTable symbolTable + ) { + return new GenericSmtLibDeclTransformer(transformer, symbolTable); + } + + protected SmtLibExprTransformer instantiateExprTransformer(final SmtLibTransformationManager transformer) { + return new GenericSmtLibExprTransformer(transformer); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTypeTransformer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTypeTransformer.java new file mode 100644 index 0000000000..eeb94773c4 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTypeTransformer.java @@ -0,0 +1,74 @@ +package hu.bme.mit.theta.solver.smtlib.impl.generic; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import hu.bme.mit.theta.common.DispatchTable; +import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.arraytype.ArrayType; +import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.core.type.bvtype.BvType; +import hu.bme.mit.theta.core.type.fptype.FpType; +import hu.bme.mit.theta.core.type.inttype.IntType; +import hu.bme.mit.theta.core.type.rattype.RatType; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTypeTransformer; + +import java.util.concurrent.ExecutionException; + +public class GenericSmtLibTypeTransformer implements SmtLibTypeTransformer { + private static final int CACHE_SIZE = 1000; + + @SuppressWarnings("unused") + private final SmtLibTransformationManager transformer; + + private final Cache typeToSmtLib; + private final DispatchTable table; + + public GenericSmtLibTypeTransformer(final SmtLibTransformationManager transformer) { + this.transformer = transformer; + + typeToSmtLib = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).build(); + + table = DispatchTable.builder() + .addCase(BoolType.class, this::boolType) + .addCase(IntType.class, this::intType) + .addCase(RatType.class, this::ratType) + .addCase(BvType.class, this::bvType) + .addCase(FpType.class, this::fpType) + .addCase(ArrayType.class, this::arrayType) + .build(); + } + + @Override + public final String toSort(final Type type) { + try { + return typeToSmtLib.get(type, () -> table.dispatch(type)); + } catch (final ExecutionException e) { + throw new AssertionError(); + } + } + + protected String boolType(final BoolType type) { + return "Bool"; + } + + protected String intType(final IntType type) { + return "Int"; + } + + protected String ratType(final RatType type) { + return "Real"; + } + + protected String bvType(final BvType type) { + return String.format("(_ BitVec %d)", type.getSize()); + } + + protected String fpType(final FpType type) { + return String.format("(_ FloatingPoint %d %d)", type.getExponent(), type.getSignificand()); + } + + protected String arrayType(final ArrayType type) { + return String.format("(Array %s %s)", toSort(type.getIndexType()), toSort(type.getElemType())); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/mathsat/MathSATSmtLibItpMarker.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/mathsat/MathSATSmtLibItpMarker.java new file mode 100644 index 0000000000..1d1a8bc94e --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/mathsat/MathSATSmtLibItpMarker.java @@ -0,0 +1,23 @@ +package hu.bme.mit.theta.solver.smtlib.impl.mathsat; + +import hu.bme.mit.theta.solver.smtlib.solver.interpolation.SmtLibItpMarker; + +public class MathSATSmtLibItpMarker extends SmtLibItpMarker { + private static final String markerPattern = "_mathsat_marker_%d"; + private static long markerCount = 0; + + static void resetMarkerCount() { + markerCount = 0; + } + + private final String markerName; + + public MathSATSmtLibItpMarker() { + super(); + markerName = String.format(markerPattern, markerCount++); + } + + public String getMarkerName() { + return markerName; + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/mathsat/MathSATSmtLibItpSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/mathsat/MathSATSmtLibItpSolver.java new file mode 100644 index 0000000000..d407919f31 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/mathsat/MathSATSmtLibItpSolver.java @@ -0,0 +1,125 @@ +package hu.bme.mit.theta.solver.smtlib.impl.mathsat; + +import hu.bme.mit.theta.core.decl.ConstDecl; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.BoolExprs; +import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.solver.Interpolant; +import hu.bme.mit.theta.solver.ItpMarker; +import hu.bme.mit.theta.solver.ItpMarkerTree; +import hu.bme.mit.theta.solver.ItpPattern; +import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibItpSolver; +import hu.bme.mit.theta.solver.smtlib.solver.interpolation.SmtLibInterpolant; +import hu.bme.mit.theta.solver.smtlib.solver.interpolation.SmtLibItpPattern; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibModel; +import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolverException; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Lexer; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser; +import hu.bme.mit.theta.solver.smtlib.solver.parser.ThrowExceptionErrorListener; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.misc.Interval; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +public class MathSATSmtLibItpSolver extends SmtLibItpSolver { + + public MathSATSmtLibItpSolver( + final SmtLibSymbolTable symbolTable, final SmtLibTransformationManager transformationManager, + final SmtLibTermTransformer termTransformer, final SmtLibSolverBinary solverBinary + ) { + super(symbolTable, transformationManager, termTransformer, solverBinary); + } + + @Override + public ItpPattern createTreePattern(final ItpMarkerTree root) { + checkNotNull(root); + return SmtLibItpPattern.of(root); + } + + @Override + public MathSATSmtLibItpMarker createMarker() { + final var marker = new MathSATSmtLibItpMarker(); + markers.add(marker); + return marker; + } + + @Override + protected void add(final MathSATSmtLibItpMarker marker, final Expr assertion, final Set> consts, final String term) { + consts.stream().map(symbolTable::getDeclaration).forEach(this::issueGeneralCommand); + issueGeneralCommand(String.format("(assert (! %s :interpolation-group %s))", term, marker.getMarkerName())); + } + + @Override + public Interpolant getInterpolant(final ItpPattern pattern) { + checkState(getStatus() == SolverStatus.UNSAT, "Cannot get interpolant if status is not UNSAT."); + checkArgument(pattern instanceof SmtLibItpPattern); + @SuppressWarnings("unchecked") + final var mathsatItpPattern = (SmtLibItpPattern) pattern; + + final Map> itpMap = new HashMap<>(); + buildItpMapFromTree(mathsatItpPattern.getRoot(), itpMap); + + return new SmtLibInterpolant(itpMap); + } + + @Override + protected void init() { + super.init(); + issueGeneralCommand("(set-option :produce-interpolants true)"); + } + + private List buildItpMapFromTree(final ItpMarkerTree pattern, final Map> itpMap) { + final List markers = new ArrayList<>(); + for(final var child : pattern.getChildren()) { + markers.addAll(buildItpMapFromTree(child, itpMap)); + } + markers.add(pattern.getMarker()); + + solverBinary.issueCommand(String.format("(get-interpolant (%s))", markers.stream().map(MathSATSmtLibItpMarker::getMarkerName).collect(Collectors.joining(" ")))); + final var res = parseItpResponse(solverBinary.readResponse()); + itpMap.put(pattern.getMarker(), termTransformer.toExpr(res, BoolExprs.Bool(), new SmtLibModel(Collections.emptyMap()))); + + return markers; + } + + private String parseItpResponse(final String response) { + final var lexer = new SMTLIBv2Lexer(CharStreams.fromString(response)); + final var parser = new SMTLIBv2Parser(new CommonTokenStream(lexer)); + try { + lexer.removeErrorListeners(); + lexer.addErrorListener(new ThrowExceptionErrorListener()); + parser.removeErrorListeners(); + parser.addErrorListener(new ThrowExceptionErrorListener()); + return extractString(parser.term()); + } + catch (Exception e) { + try { + throw new SmtLibSolverException(parser.response().general_response_error().reason.getText()); + } + catch(Exception ex) { + throw new SmtLibSolverException("Could not parse solver output: " + response, e); + } + } + } + + private static String extractString(final ParserRuleContext ctx) { + return ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex())); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/mathsat/MathSATSmtLibSolverFactory.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/mathsat/MathSATSmtLibSolverFactory.java new file mode 100644 index 0000000000..91f4101bb1 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/mathsat/MathSATSmtLibSolverFactory.java @@ -0,0 +1,38 @@ +package hu.bme.mit.theta.solver.smtlib.impl.mathsat; + +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverBinary; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverFactory; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTransformationManager; + +import java.nio.file.Path; + +public class MathSATSmtLibSolverFactory extends GenericSmtLibSolverFactory { + private final boolean itpSupported; + + private MathSATSmtLibSolverFactory(Path solverPath, String[] args, boolean itpSupported) { + super(solverPath, args); + this.itpSupported = itpSupported; + } + + public static MathSATSmtLibSolverFactory create(Path solverPath, String[] args, boolean itpSupported) { + return new MathSATSmtLibSolverFactory(solverPath, args, itpSupported); + } + + @Override + public ItpSolver createItpSolver() { + if(itpSupported) { + final var symbolTable = new GenericSmtLibSymbolTable(); + final var transformationManager = new GenericSmtLibTransformationManager(symbolTable); + final var termTransformer = new GenericSmtLibTermTransformer(symbolTable); + final var solverBinary = new GenericSmtLibSolverBinary(solverPath, args); + + return new MathSATSmtLibItpSolver(symbolTable, transformationManager, termTransformer, solverBinary); + } + else { + throw new UnsupportedOperationException("MathSAT interpolation supported above 5.4.0"); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/mathsat/MathSATSmtLibSolverInstaller.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/mathsat/MathSATSmtLibSolverInstaller.java new file mode 100644 index 0000000000..88298c3d1b --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/mathsat/MathSATSmtLibSolverInstaller.java @@ -0,0 +1,165 @@ +package hu.bme.mit.theta.solver.smtlib.impl.mathsat; + +import hu.bme.mit.theta.common.OsHelper; +import hu.bme.mit.theta.common.logging.Logger; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException; +import hu.bme.mit.theta.solver.smtlib.utils.Compress; +import hu.bme.mit.theta.solver.smtlib.utils.SemVer; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static hu.bme.mit.theta.common.OsHelper.Architecture.X64; +import static hu.bme.mit.theta.common.OsHelper.Architecture.X86; +import static hu.bme.mit.theta.common.OsHelper.OperatingSystem.LINUX; +import static hu.bme.mit.theta.common.OsHelper.OperatingSystem.MAC; +import static hu.bme.mit.theta.common.OsHelper.OperatingSystem.WINDOWS; + +public class MathSATSmtLibSolverInstaller extends SmtLibSolverInstaller.Default { + private final List versions; + + public MathSATSmtLibSolverInstaller(final Logger logger) { + super(logger); + + versions = new ArrayList<>(); + versions.add(SemVer.VersionDecoder.create(SemVer.of("5.5.0")) + .addString(LINUX, X64, "linux-x86_64") + .addString(MAC, X64, "darwin-libcxx-x86_64") + .addString(WINDOWS, X64, "win64-msvc") + .build() + ); + versions.add(SemVer.VersionDecoder.create(SemVer.of("5.3.6")) + .addString(LINUX, X64, "linux-x86_64") + .addString(LINUX, X86, "linux-x86") + .addString(MAC, X64, "darwin-libcxx-x86_64") + .addString(WINDOWS, X64, "win64-msvc") + .addString(WINDOWS, X86, "win32-msvc") + .build() + ); + versions.add(SemVer.VersionDecoder.create(SemVer.of("5.2.12")) + .addString(LINUX, X64, "linux-x86_64") + .addString(LINUX, X86, "linux-x86") + .addString(MAC, X64, "darwin-libcxx-x86_64") + .addString(WINDOWS, X64, "win64-msvc18") + .addString(WINDOWS, X86, "win32-msvc18") + .build() + ); + versions.add(SemVer.VersionDecoder.create(SemVer.of("5.2.9")) + .addString(LINUX, X64, "linux-x86_64") + .addString(LINUX, X86, "linux-x86") + .addString(MAC, X64, "darwin-libcxx-x86_64") + .addString(WINDOWS, X64, "win64-msvc") + .addString(WINDOWS, X86, "win32-msvc") + .build() + ); + versions.add(SemVer.VersionDecoder.create(SemVer.of("5.2.6")) + .addString(LINUX, X64, "linux-x86_64") + .addString(LINUX, X86, "linux-x86") + .addString(MAC, X64, "darwin-x86_64") + .addString(WINDOWS, X64, "win64-msvc") + .addString(WINDOWS, X86, "win32-msvc") + .build() + ); + versions.add(SemVer.VersionDecoder.create(SemVer.of("5.1.10")) + .addString(LINUX, X64, "linux-x86_64") + .addString(LINUX, X86, "linux-x86") + .addString(MAC, X64, "darwin-x86_64") + .build() + ); + versions.add(SemVer.VersionDecoder.create(SemVer.of("5.1.1")) + .addString(LINUX, X64, "linux-x86_64") + .addString(LINUX, X86, "linux-x86") + .build() + ); + } + + @Override + protected String getSolverName() { + return "mathsat"; + } + + @Override + protected void installSolver(Path installDir, String version) throws SmtLibSolverInstallerException { + final var semVer = SemVer.of(version); + String archStr = null; + + for(final var versionDecoder : versions) { + if(semVer.compareTo(versionDecoder.getVersion()) >= 0) { + archStr = versionDecoder.getOsArchString(OsHelper.getOs(), OsHelper.getArch()); + break; + } + } + if(archStr == null) { + throw new SmtLibSolverInstallerException(String.format("MathSAT on operating system %s and architecture %s is not supported", OsHelper.getOs(), OsHelper.getArch())); + } + + final var downloadUrl = URI.create(String.format( + "https://mathsat.fbk.eu/download.php?file=mathsat-%s-%s.%s", + version, archStr, OsHelper.getOs().equals(WINDOWS) ? "zip" : "tar.gz" + )); + + logger.write(Logger.Level.MAINSTEP, "Starting download (%s)...\n", downloadUrl.toString()); + + try(final var inputStream = downloadUrl.toURL().openStream()) { + if(OsHelper.getOs().equals(WINDOWS)) { + Compress.extract(inputStream, installDir, Compress.CompressionType.ZIP); + } + else { + Compress.extract(inputStream, installDir, Compress.CompressionType.TARGZ); + } + installDir.resolve("bin").resolve(getSolverBinaryName()).toFile().setExecutable(true, true); + } + catch (IOException e) { + throw new SmtLibSolverInstallerException(e); + } + + logger.write(Logger.Level.MAINSTEP, "Download finished\n"); + } + + @Override + protected void uninstallSolver(Path installDir, String version) { + // Default uninstall is suitable + } + + @Override + protected String[] getDefaultSolverArgs(String version) throws SmtLibSolverInstallerException { + return new String[] { "-theory.bv.eager=false", "-theory.fp.mode=2" }; + } + + @Override + public SolverFactory getSolverFactory(final Path installDir, final String version, final Path solverPath, final String[] solverArgs) throws SmtLibSolverInstallerException { + final var solverFilePath = solverPath != null ? solverPath : installDir.resolve("bin").resolve(getSolverBinaryName()); + return MathSATSmtLibSolverFactory.create(solverFilePath, solverArgs, SemVer.of(version).compareTo(SemVer.of("5.4.0")) >= 0); + } + + @Override + public List getSupportedVersions() { + return Arrays.asList( + "5.6.6", "5.6.5", "5.6.4", "5.6.3", "5.6.2", "5.6.1", "5.6.0", + "5.5.4", "5.5.3", "5.5.2", "5.5.1", "5.5.0", + "5.4.1", "5.4.0", + "5.3.14", "5.3.13", "5.3.12", "5.3.11", "5.3.10", "5.3.9", "5.3.8", "5.3.7", "5.3.6", "5.3.5", "5.3.4", "5.3.3", "5.3.2", "5.3.1", + "5.2.12", "5.2.11", "5.2.10", "5.2.9", "5.2.8", "5.2.7", "5.2.6", "5.2.5", "5.2.4", "5.2.3", "5.2.2", "5.2.1", + "5.1.12", "5.1.11", "5.1.10", "5.1.9", "5.1.8", "5.1.7", "5.1.6", "5.1.5", "5.1.4", "5.1.3" + ); + } + + private String getSolverBinaryName() { + switch(OsHelper.getOs()) { + case WINDOWS: + return "mathsat.exe"; + case LINUX: + return "mathsat"; + case MAC: + return "mathsat.dmg"; + default: + throw new AssertionError(); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/princess/PrincessSmtLibItpMarker.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/princess/PrincessSmtLibItpMarker.java new file mode 100644 index 0000000000..141ecdf2bb --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/princess/PrincessSmtLibItpMarker.java @@ -0,0 +1,6 @@ +package hu.bme.mit.theta.solver.smtlib.impl.princess; + +import hu.bme.mit.theta.solver.smtlib.solver.interpolation.SmtLibItpMarker; + +public class PrincessSmtLibItpMarker extends SmtLibItpMarker { +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/princess/PrincessSmtLibItpSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/princess/PrincessSmtLibItpSolver.java new file mode 100644 index 0000000000..bec80fa9ef --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/princess/PrincessSmtLibItpSolver.java @@ -0,0 +1,173 @@ +package hu.bme.mit.theta.solver.smtlib.impl.princess; + +import hu.bme.mit.theta.core.decl.ConstDecl; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.BoolExprs; +import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.solver.Interpolant; +import hu.bme.mit.theta.solver.ItpMarker; +import hu.bme.mit.theta.solver.ItpMarkerTree; +import hu.bme.mit.theta.solver.ItpPattern; +import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Lexer; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibItpSolver; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolverException; +import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; +import hu.bme.mit.theta.solver.smtlib.solver.interpolation.SmtLibInterpolant; +import hu.bme.mit.theta.solver.smtlib.solver.interpolation.SmtLibItpPattern; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibModel; +import hu.bme.mit.theta.solver.smtlib.solver.parser.ThrowExceptionErrorListener; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.misc.Interval; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.False; + +public final class PrincessSmtLibItpSolver extends SmtLibItpSolver { + private final Map, String> assertionNames = new HashMap<>(); + private static final String assertionNamePattern = "_smtinterpol_assertion_%d"; + private static long assertionCount = 0; + + public PrincessSmtLibItpSolver( + final SmtLibSymbolTable symbolTable, final SmtLibTransformationManager transformationManager, + final SmtLibTermTransformer termTransformer, final SmtLibSolverBinary solverBinary + ) { + super(symbolTable, transformationManager, termTransformer, solverBinary); + } + + @Override + public ItpPattern createTreePattern(final ItpMarkerTree root) { + checkNotNull(root); + return SmtLibItpPattern.of(root); + } + + @Override + public PrincessSmtLibItpMarker createMarker() { + final var marker = new PrincessSmtLibItpMarker(); + markers.add(marker); + return marker; + } + + @Override + protected void add(final PrincessSmtLibItpMarker marker, final Expr assertion, final Set> consts, final String term) { + consts.stream().map(symbolTable::getDeclaration).forEach(this::issueGeneralCommand); + + final var name = String.format(assertionNamePattern, assertionCount++); + assertionNames.put(assertion, name); + issueGeneralCommand(String.format("(assert (! %s :named %s))", term, name)); + } + + @Override + public Interpolant getInterpolant(final ItpPattern pattern) { + checkState(getStatus() == SolverStatus.UNSAT, "Cannot get interpolant if status is not UNSAT."); + checkArgument(pattern instanceof SmtLibItpPattern); + @SuppressWarnings("unchecked") + final var princessItpPattern = (SmtLibItpPattern) pattern; + + final var term = patternToTerm(princessItpPattern.getRoot()); + + final List> itpList = new LinkedList<>(); + + solverBinary.issueCommand(String.format("(get-interpolants %s)", term)); + for(final var itp : parseItpResponse(solverBinary.readResponse())) { + itpList.add(termTransformer.toExpr(itp, BoolExprs.Bool(), new SmtLibModel(Collections.emptyMap()))); + } + itpList.add(False()); + + final Map> itpMap = new HashMap<>(); + buildItpMapFormList(princessItpPattern.getRoot(), itpList, itpMap); + + return new SmtLibInterpolant(itpMap); + } + + private String patternToTerm(final ItpMarkerTree markerTree) { + String term; + + final var marker = markerTree.getMarker(); + final var terms = marker.getTerms(); + if(terms.size() == 1) { + term = assertionNames.get(terms.iterator().next().get1()); + } + else { + term = String.format("(and %s)", terms.stream().map(t -> assertionNames.get(t.get1())).collect(Collectors.joining(" "))); + } + + final var children = markerTree.getChildren(); + for(var i = children.size() - 1; i >= 0; i--) { + if(i == 0) { + term = String.format("%s %s", patternToTerm(children.get(i)), term); + } + else { + term = String.format("(%s) %s", patternToTerm(children.get(i)), term); + } + } + + return term; + } + + private void buildItpMapFormList(final ItpMarkerTree pattern, final List> itpList, final Map> itpMap) { + for (final ItpMarkerTree child : pattern.getChildren()) { + buildItpMapFormList(child, itpList, itpMap); + } + final ItpMarker marker = pattern.getMarker(); + final Expr itpExpr = itpList.get(0); + itpMap.put(marker, itpExpr); + itpList.remove(0); + } + + @Override + protected void init() { + issueGeneralCommand("(set-option :print-success true)"); + issueGeneralCommand("(set-option :produce-models true)"); + issueGeneralCommand("(set-option :produce-interpolants true)"); + issueGeneralCommand("(set-logic ALL)"); + } + + private List parseItpResponse(final String response) { + final var lexer = new SMTLIBv2Lexer(CharStreams.fromString(response)); + final var parser = new SMTLIBv2Parser(new CommonTokenStream(lexer)); + try { + final var interpolants = new ArrayList(); + + lexer.removeErrorListeners(); + lexer.addErrorListener(new ThrowExceptionErrorListener()); + parser.removeErrorListeners(); + parser.addErrorListener(new ThrowExceptionErrorListener()); + + for(final var term : parser.get_interpolants_response_smtinterpol().term()) { + interpolants.add(extractString(term)); + } + + return interpolants; + } + catch (Exception e) { + try { + throw new SmtLibSolverException(parser.response().general_response_error().reason.getText()); + } + catch(Exception ex) { + throw new SmtLibSolverException("Could not parse solver output: " + response, e); + } + } + } + + private static String extractString(final ParserRuleContext ctx) { + return ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex())); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/princess/PrincessSmtLibSolverFactory.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/princess/PrincessSmtLibSolverFactory.java new file mode 100644 index 0000000000..5d61eb15c2 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/princess/PrincessSmtLibSolverFactory.java @@ -0,0 +1,30 @@ +package hu.bme.mit.theta.solver.smtlib.impl.princess; + +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverBinary; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverFactory; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTransformationManager; + +import java.nio.file.Path; + +public class PrincessSmtLibSolverFactory extends GenericSmtLibSolverFactory { + private PrincessSmtLibSolverFactory(Path solverPath, String[] args) { + super(solverPath, args); + } + + public static PrincessSmtLibSolverFactory create(Path solverPath, String[] args) { + return new PrincessSmtLibSolverFactory(solverPath, args); + } + + @Override + public ItpSolver createItpSolver() { + final var symbolTable = new GenericSmtLibSymbolTable(); + final var transformationManager = new GenericSmtLibTransformationManager(symbolTable); + final var termTransformer = new GenericSmtLibTermTransformer(symbolTable); + final var solverBinary = new GenericSmtLibSolverBinary(solverPath, args); + + return new PrincessSmtLibItpSolver(symbolTable, transformationManager, termTransformer, solverBinary); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/princess/PrincessSmtLibSolverInstaller.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/princess/PrincessSmtLibSolverInstaller.java new file mode 100644 index 0000000000..b6c0dad3a7 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/princess/PrincessSmtLibSolverInstaller.java @@ -0,0 +1,87 @@ +package hu.bme.mit.theta.solver.smtlib.impl.princess; + +import hu.bme.mit.theta.common.OsHelper; +import hu.bme.mit.theta.common.logging.Logger; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException; +import hu.bme.mit.theta.solver.smtlib.utils.Compress; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; + +public class PrincessSmtLibSolverInstaller extends SmtLibSolverInstaller.Default { + + public PrincessSmtLibSolverInstaller(final Logger logger) { + super(logger); + } + + @Override + protected String getSolverName() { + return "princess"; + } + + @Override + protected void installSolver(final Path installDir, final String version) throws SmtLibSolverInstallerException { + + final var downloadUrl = URI.create(String.format( + "http://www.philipp.ruemmer.org/princess/princess-bin-%s.zip", + version + )); + + logger.write(Logger.Level.MAINSTEP, "Starting download (%s)...\n", downloadUrl.toString()); + + try(final var inputStream = downloadUrl.toURL().openStream()) { + Compress.extract(inputStream, installDir, Compress.CompressionType.ZIP); + installDir.resolve(getSolverBinaryName()).toFile().setExecutable(true, true); + } + catch (IOException e) { + throw new SmtLibSolverInstallerException(e); + } + + logger.write(Logger.Level.MAINSTEP, "Download finished\n"); + } + + @Override + protected void uninstallSolver(final Path installDir, final String version) { + // Default uninstall is suitable + } + + @Override + protected String[] getDefaultSolverArgs(String version) { + return new String[] { + "+stdin", + "+incremental", + "+quiet" + }; + } + + @Override + public SolverFactory getSolverFactory(final Path installDir, final String version, final Path solverPath, final String[] solverArgs) throws SmtLibSolverInstallerException { + final var solverFilePath = solverPath != null ? solverPath : installDir.resolve(getSolverBinaryName()); + return PrincessSmtLibSolverFactory.create(solverFilePath, solverArgs); + } + + @Override + public List getSupportedVersions() { + return Arrays.asList( + "2021-05-10", "2021-03-10", "2020-03-12", "2019-10-02", "2019-07-24", + "2018-10-26", "2018-05-25", "2018-01-27", "2017-12-06", "2017-07-17" + ); + } + + private String getSolverBinaryName() { + switch(OsHelper.getOs()) { + case WINDOWS: + return "princess.bat"; + case MAC: + case LINUX: + return "princess"; + default: + throw new AssertionError(); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/smtinterpol/SMTInterpolSmtLibItpMarker.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/smtinterpol/SMTInterpolSmtLibItpMarker.java new file mode 100644 index 0000000000..04fbe9a68a --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/smtinterpol/SMTInterpolSmtLibItpMarker.java @@ -0,0 +1,6 @@ +package hu.bme.mit.theta.solver.smtlib.impl.smtinterpol; + +import hu.bme.mit.theta.solver.smtlib.solver.interpolation.SmtLibItpMarker; + +public class SMTInterpolSmtLibItpMarker extends SmtLibItpMarker { +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/smtinterpol/SMTInterpolSmtLibItpSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/smtinterpol/SMTInterpolSmtLibItpSolver.java new file mode 100644 index 0000000000..b02fd7aec8 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/smtinterpol/SMTInterpolSmtLibItpSolver.java @@ -0,0 +1,173 @@ +package hu.bme.mit.theta.solver.smtlib.impl.smtinterpol; + +import hu.bme.mit.theta.core.decl.ConstDecl; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.BoolExprs; +import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.solver.Interpolant; +import hu.bme.mit.theta.solver.ItpMarker; +import hu.bme.mit.theta.solver.ItpMarkerTree; +import hu.bme.mit.theta.solver.ItpPattern; +import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Lexer; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibItpSolver; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolverException; +import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; +import hu.bme.mit.theta.solver.smtlib.solver.interpolation.SmtLibInterpolant; +import hu.bme.mit.theta.solver.smtlib.solver.interpolation.SmtLibItpPattern; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibModel; +import hu.bme.mit.theta.solver.smtlib.solver.parser.ThrowExceptionErrorListener; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.misc.Interval; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.False; + +public final class SMTInterpolSmtLibItpSolver extends SmtLibItpSolver { + private final Map, String> assertionNames = new HashMap<>(); + private static final String assertionNamePattern = "_smtinterpol_assertion_%d"; + private static long assertionCount = 0; + + public SMTInterpolSmtLibItpSolver( + final SmtLibSymbolTable symbolTable, final SmtLibTransformationManager transformationManager, + final SmtLibTermTransformer termTransformer, final SmtLibSolverBinary solverBinary + ) { + super(symbolTable, transformationManager, termTransformer, solverBinary); + } + + @Override + public ItpPattern createTreePattern(final ItpMarkerTree root) { + checkNotNull(root); + return SmtLibItpPattern.of(root); + } + + @Override + public SMTInterpolSmtLibItpMarker createMarker() { + final var marker = new SMTInterpolSmtLibItpMarker(); + markers.add(marker); + return marker; + } + + @Override + protected void add(final SMTInterpolSmtLibItpMarker marker, final Expr assertion, final Set> consts, final String term) { + consts.stream().map(symbolTable::getDeclaration).forEach(this::issueGeneralCommand); + + final var name = String.format(assertionNamePattern, assertionCount++); + assertionNames.put(assertion, name); + issueGeneralCommand(String.format("(assert (! %s :named %s))", term, name)); + } + + @Override + public Interpolant getInterpolant(final ItpPattern pattern) { + checkState(getStatus() == SolverStatus.UNSAT, "Cannot get interpolant if status is not UNSAT."); + checkArgument(pattern instanceof SmtLibItpPattern); + @SuppressWarnings("unchecked") + final var smtInterpolItpPattern = (SmtLibItpPattern) pattern; + + final var term = patternToTerm(smtInterpolItpPattern.getRoot()); + + final List> itpList = new LinkedList<>(); + + solverBinary.issueCommand(String.format("(get-interpolants %s)", term)); + for(final var itp : parseItpResponse(solverBinary.readResponse())) { + itpList.add(termTransformer.toExpr(itp, BoolExprs.Bool(), new SmtLibModel(Collections.emptyMap()))); + } + itpList.add(False()); + + final Map> itpMap = new HashMap<>(); + buildItpMapFormList(smtInterpolItpPattern.getRoot(), itpList, itpMap); + + return new SmtLibInterpolant(itpMap); + } + + private String patternToTerm(final ItpMarkerTree markerTree) { + String term; + + final var marker = markerTree.getMarker(); + final var terms = marker.getTerms(); + if(terms.size() == 1) { + term = assertionNames.get(terms.iterator().next().get1()); + } + else { + term = String.format("(and %s)", terms.stream().map(t -> assertionNames.get(t.get1())).collect(Collectors.joining(" "))); + } + + final var children = markerTree.getChildren(); + for(var i = children.size() - 1; i >= 0; i--) { + if(i == 0) { + term = String.format("%s %s", patternToTerm(children.get(i)), term); + } + else { + term = String.format("(%s) %s", patternToTerm(children.get(i)), term); + } + } + + return term; + } + + private void buildItpMapFormList(final ItpMarkerTree pattern, final List> itpList, final Map> itpMap) { + for (final ItpMarkerTree child : pattern.getChildren()) { + buildItpMapFormList(child, itpList, itpMap); + } + final ItpMarker marker = pattern.getMarker(); + final Expr itpExpr = itpList.get(0); + itpMap.put(marker, itpExpr); + itpList.remove(0); + } + + @Override + protected void init() { + issueGeneralCommand("(set-option :print-success true)"); + issueGeneralCommand("(set-option :produce-models true)"); + issueGeneralCommand("(set-option :produce-interpolants true)"); + issueGeneralCommand("(set-logic ALL)"); + } + + private List parseItpResponse(final String response) { + final var lexer = new SMTLIBv2Lexer(CharStreams.fromString(response)); + final var parser = new SMTLIBv2Parser(new CommonTokenStream(lexer)); + try { + final var interpolants = new ArrayList(); + + lexer.removeErrorListeners(); + lexer.addErrorListener(new ThrowExceptionErrorListener()); + parser.removeErrorListeners(); + parser.addErrorListener(new ThrowExceptionErrorListener()); + + for(final var term : parser.get_interpolants_response_smtinterpol().term()) { + interpolants.add(extractString(term)); + } + + return interpolants; + } + catch (Exception e) { + try { + throw new SmtLibSolverException(parser.response().general_response_error().reason.getText()); + } + catch(Exception ex) { + throw new SmtLibSolverException("Could not parse solver output: " + response, e); + } + } + } + + private static String extractString(final ParserRuleContext ctx) { + return ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex())); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/smtinterpol/SMTInterpolSmtLibSolverFactory.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/smtinterpol/SMTInterpolSmtLibSolverFactory.java new file mode 100644 index 0000000000..ea4fd088ea --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/smtinterpol/SMTInterpolSmtLibSolverFactory.java @@ -0,0 +1,71 @@ +package hu.bme.mit.theta.solver.smtlib.impl.smtinterpol; + +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.UCSolver; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverBinary; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTransformationManager; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolver; + +import java.nio.file.Path; + +public class SMTInterpolSmtLibSolverFactory implements SolverFactory { + private final Path solverPath; + private final String[] args; + + private SMTInterpolSmtLibSolverFactory(Path solverPath, String[] args) { + this.solverPath = solverPath; + this.args = args; + } + + public static SMTInterpolSmtLibSolverFactory create(Path solverPath, String[] args) { + return new SMTInterpolSmtLibSolverFactory(solverPath, args); + } + + @Override + public Solver createSolver() { + final var symbolTable = new GenericSmtLibSymbolTable(); + final var transformationManager = new GenericSmtLibTransformationManager(symbolTable); + final var termTransformer = new GenericSmtLibTermTransformer(symbolTable); + final var solverBinary = new GenericSmtLibSolverBinary(getJavaBinary(), getSolverArgs()); + + return new SmtLibSolver(symbolTable, transformationManager, termTransformer, solverBinary, false); + } + + @Override + public UCSolver createUCSolver() { + final var symbolTable = new GenericSmtLibSymbolTable(); + final var transformationManager = new GenericSmtLibTransformationManager(symbolTable); + final var termTransformer = new GenericSmtLibTermTransformer(symbolTable); + final var solverBinary = new GenericSmtLibSolverBinary(getJavaBinary(), getSolverArgs()); + + return new SmtLibSolver(symbolTable, transformationManager, termTransformer, solverBinary, true); + } + + @Override + public ItpSolver createItpSolver() { + final var symbolTable = new GenericSmtLibSymbolTable(); + final var transformationManager = new GenericSmtLibTransformationManager(symbolTable); + final var termTransformer = new GenericSmtLibTermTransformer(symbolTable); + final var solverBinary = new GenericSmtLibSolverBinary(getJavaBinary(), getSolverArgs()); + + return new SMTInterpolSmtLibItpSolver(symbolTable, transformationManager, termTransformer, solverBinary); + } + + private Path getJavaBinary() { + return Path.of(System.getProperty("java.home")).resolve("bin").resolve("java"); + } + + private String[] getSolverArgs() { + final var solverArgs = new String[args.length + 2]; + solverArgs[0] = "-jar"; + solverArgs[1] = solverPath.toAbsolutePath().toString(); + for(var i = 0; i < args.length; i++) { + solverArgs[i + 2] = args[i]; + } + return solverArgs; + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/smtinterpol/SMTInterpolSmtLibSolverInstaller.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/smtinterpol/SMTInterpolSmtLibSolverInstaller.java new file mode 100644 index 0000000000..57ce9c5867 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/smtinterpol/SMTInterpolSmtLibSolverInstaller.java @@ -0,0 +1,86 @@ +package hu.bme.mit.theta.solver.smtlib.impl.smtinterpol; + +import hu.bme.mit.theta.common.logging.Logger; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.nio.channels.Channels; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; + +public class SMTInterpolSmtLibSolverInstaller extends SmtLibSolverInstaller.Default { + + public SMTInterpolSmtLibSolverInstaller(final Logger logger) { + super(logger); + } + + @Override + protected String getSolverName() { + return "smtinterpol"; + } + + @Override + protected void installSolver(final Path installDir, final String version) throws SmtLibSolverInstallerException { + + try( + final var inputChannel = Channels.newChannel(getDownloadUrl(version).openStream()); + final var outputChannel = new FileOutputStream(installDir.resolve(getSolverBinaryName(version)).toAbsolutePath().toString()).getChannel() + ) { + logger.write(Logger.Level.MAINSTEP, "Starting download (%s)...\n", getDownloadUrl(version).toString()); + outputChannel.transferFrom(inputChannel, 0, Long.MAX_VALUE); + } + catch (IOException e) { + throw new SmtLibSolverInstallerException(e); + } + + logger.write(Logger.Level.MAINSTEP, "Download finished\n"); + } + + @Override + protected void uninstallSolver(final Path installDir, final String version) { + // Default uninstall is suitable + } + + @Override + protected String[] getDefaultSolverArgs(String version) { + return new String[] { "-smt2" }; + } + + @Override + public SolverFactory getSolverFactory(final Path installDir, final String version, final Path solverPath, final String[] solverArgs) throws SmtLibSolverInstallerException { + final var solverFilePath = solverPath != null ? solverPath : installDir.resolve(getSolverBinaryName(version)); + return SMTInterpolSmtLibSolverFactory.create(solverFilePath, solverArgs); + } + + @Override + public List getSupportedVersions() { + return Arrays.asList("2.5-916", "2.5-663", "2.5-479", "2.5-7"); + } + + private URL getDownloadUrl(final String version) throws SmtLibSolverInstallerException, MalformedURLException { + final String fileName; + switch (version) { + case "2.5-916": fileName = "2.5-916-ga5843d8b"; break; + case "2.5-663": fileName = "2.5-663-gf15aa217"; break; + case "2.5-479": fileName = "2.5-479-ga49e50b1"; break; + case "2.5-7": fileName = "2.5-7-g64ec65d"; break; + default: throw new SmtLibSolverInstallerException("Unsupported solver version."); + } + + return URI.create(String.format( + "https://ultimate.informatik.uni-freiburg.de/smtinterpol/smtinterpol-%s.jar", + fileName + )).toURL(); + } + + private String getSolverBinaryName(final String version) { + return String.format("smtinterpol-%s.jar", version); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/yices2/Yices2SmtLibSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/yices2/Yices2SmtLibSolver.java new file mode 100644 index 0000000000..7b84ca093e --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/yices2/Yices2SmtLibSolver.java @@ -0,0 +1,92 @@ +package hu.bme.mit.theta.solver.smtlib.impl.yices2; + +import hu.bme.mit.theta.common.Tuple2; +import hu.bme.mit.theta.core.model.Valuation; +import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Lexer; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolver; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolverException; +import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibModel; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibValuation; +import hu.bme.mit.theta.solver.smtlib.solver.parser.GetModelResponse; +import hu.bme.mit.theta.solver.smtlib.solver.parser.ThrowExceptionErrorListener; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; + +import java.util.HashMap; + +import static com.google.common.base.Preconditions.checkState; + +public class Yices2SmtLibSolver extends SmtLibSolver { + private Valuation model; + private SolverStatus status; + + public Yices2SmtLibSolver( + final SmtLibSymbolTable symbolTable, final SmtLibTransformationManager transformationManager, + final SmtLibTermTransformer termTransformer, final SmtLibSolverBinary solverBinary, + final boolean unsatCoreEnabled + ) { + super(symbolTable, transformationManager, termTransformer, solverBinary, unsatCoreEnabled); + } + + @Override + public SolverStatus check() { + return status = super.check(); + } + + @Override + public Valuation getModel() { + checkState(status == SolverStatus.SAT, "Cannot get model if status is not SAT."); + + if (model == null) { + model = extractModel(); + } + + return model; + } + + private Valuation extractModel() { + assert status == SolverStatus.SAT; + assert model == null; + + solverBinary.issueCommand("(get-model)"); + + final var modelValues = new HashMap(); + for(final var ignored : declarationStack.toCollection()) { + final var value = parseModelResponse(solverBinary.readResponse()); + modelValues.put(value.get1(), value.get2()); + } + + return new SmtLibValuation(symbolTable, transformationManager, termTransformer, new SmtLibModel(modelValues)); + } + + private Tuple2 parseModelResponse(final String response) { + try { + final var lexer = new SMTLIBv2Lexer(CharStreams.fromString(response)); + final var parser = new SMTLIBv2Parser(new CommonTokenStream(lexer)); + lexer.removeErrorListeners(); + lexer.addErrorListener(new ThrowExceptionErrorListener()); + parser.removeErrorListeners(); + parser.addErrorListener(new ThrowExceptionErrorListener()); + + final var term = parser.term().generic_term(); + final var id = GetModelResponse.extractString(term.qual_identifier()); + final var variable = GetModelResponse.extractString(term.term(0)); + final var value = GetModelResponse.extractString(term.term(1)); + + if(id.equals("=")) { + return Tuple2.of(variable, String.format("%s () (_ theta_type unknown) %s", variable, value)); + } + + throw new SmtLibSolverException("Could not parse solver output: " + response); + } + catch (Exception e) { + throw new SmtLibSolverException("Could not parse solver output: " + response, e); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/yices2/Yices2SmtLibSolverFactory.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/yices2/Yices2SmtLibSolverFactory.java new file mode 100644 index 0000000000..b2c1712ecb --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/yices2/Yices2SmtLibSolverFactory.java @@ -0,0 +1,22 @@ +package hu.bme.mit.theta.solver.smtlib.impl.yices2; + +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverFactory; + +import java.nio.file.Path; + +public class Yices2SmtLibSolverFactory extends GenericSmtLibSolverFactory { + + private Yices2SmtLibSolverFactory(Path solverPath, String[] args) { + super(solverPath, args); + } + + public static Yices2SmtLibSolverFactory create(Path solverPath, String[] args) { + return new Yices2SmtLibSolverFactory(solverPath, args); + } + + @Override + public ItpSolver createItpSolver() { + throw new UnsupportedOperationException("Yices2 does not support interpolation"); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/yices2/Yices2SmtLibSolverInstaller.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/yices2/Yices2SmtLibSolverInstaller.java new file mode 100644 index 0000000000..4483dc8222 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/yices2/Yices2SmtLibSolverInstaller.java @@ -0,0 +1,125 @@ +package hu.bme.mit.theta.solver.smtlib.impl.yices2; + +import hu.bme.mit.theta.common.OsHelper; +import hu.bme.mit.theta.common.logging.Logger; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException; +import hu.bme.mit.theta.solver.smtlib.utils.Compress; +import hu.bme.mit.theta.solver.smtlib.utils.SemVer; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static hu.bme.mit.theta.common.OsHelper.Architecture.X64; +import static hu.bme.mit.theta.common.OsHelper.OperatingSystem.LINUX; +import static hu.bme.mit.theta.common.OsHelper.OperatingSystem.MAC; +import static hu.bme.mit.theta.common.OsHelper.OperatingSystem.WINDOWS; + +public class Yices2SmtLibSolverInstaller extends SmtLibSolverInstaller.Default { + private final List versions; + + public Yices2SmtLibSolverInstaller(final Logger logger) { + super(logger); + + versions = new ArrayList<>(); + versions.add(SemVer.VersionDecoder.create(SemVer.of("2.6.2")) + .addString(LINUX, X64, "x86_64-pc-linux-gnu-static-gmp") + .addString(MAC, X64, "x86_64-apple-darwin18.7.0-static-gmp") + .addString(WINDOWS, X64, "x86_64-pc-mingw32-static-gmp") + .build() + ); + versions.add(SemVer.VersionDecoder.create(SemVer.of("2.5.4")) + .addString(LINUX, X64, "x86_64-pc-linux-gnu-static-gmp") + .addString(MAC, X64, "x86_64-apple-darwin16.7.0-static-gmp") + .addString(WINDOWS, X64, "x86_64-pc-mingw32-static-gmp") + .build() + ); + versions.add(SemVer.VersionDecoder.create(SemVer.of("2.5.3")) + .addString(LINUX, X64, "x86_64-unknown-linux-gnu-static-gmp") + .addString(MAC, X64, "x86_64-apple-darwin16.7.0-static-gmp") + .addString(WINDOWS, X64, "x86_64-pc-mingw32-static-gmp") + .build() + ); + } + + @Override + protected String getSolverName() { + return "yices2"; + } + + @Override + protected void installSolver(Path installDir, String version) throws SmtLibSolverInstallerException { + final var semVer = SemVer.of(version); + String archStr = null; + + for(final var versionDecoder : versions) { + if(semVer.compareTo(versionDecoder.getVersion()) >= 0) { + archStr = versionDecoder.getOsArchString(OsHelper.getOs(), OsHelper.getArch()); + break; + } + } + if(archStr == null) { + throw new SmtLibSolverInstallerException(String.format("Yices2 on operating system %s and architecture %s is not supported", OsHelper.getOs(), OsHelper.getArch())); + } + + final var downloadUrl = URI.create(String.format( + "https://yices.csl.sri.com/releases/%s/yices-%s-%s.%s", + version, version, archStr, OsHelper.getOs().equals(WINDOWS) ? "zip" : "tar.gz" + )); + + logger.write(Logger.Level.MAINSTEP, "Starting download (%s)...\n", downloadUrl.toString()); + + try(final var inputStream = downloadUrl.toURL().openStream()) { + if(OsHelper.getOs().equals(WINDOWS)) { + Compress.extract(inputStream, installDir, Compress.CompressionType.ZIP); + } + else { + Compress.extract(inputStream, installDir, Compress.CompressionType.TARGZ); + } + installDir.resolve("bin").resolve(getSolverBinaryName()).toFile().setExecutable(true, true); + } + catch (IOException e) { + throw new SmtLibSolverInstallerException(e); + } + + logger.write(Logger.Level.MAINSTEP, "Download finished\n"); + } + + @Override + protected void uninstallSolver(Path installDir, String version) { } + + @Override + protected String[] getDefaultSolverArgs(String version) { + return new String[] { "--incremental" }; + } + + @Override + public SolverFactory getSolverFactory(final Path installDir, final String version, final Path solverPath, final String[] solverArgs) throws SmtLibSolverInstallerException { + final var solverFilePath = solverPath != null ? solverPath : installDir.resolve("bin").resolve(getSolverBinaryName()); + return Yices2SmtLibSolverFactory.create(solverFilePath, solverArgs); + } + + @Override + public List getSupportedVersions() { + return Arrays.asList( + "2.6.2", "2.6.1", "2.6.0" + ); + } + + private String getSolverBinaryName() { + switch(OsHelper.getOs()) { + case WINDOWS: + return "yices-smt2.exe"; + case LINUX: + case MAC: + return "yices-smt2"; + default: + throw new AssertionError(); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/z3/Z3SmtLibItpMarker.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/z3/Z3SmtLibItpMarker.java new file mode 100644 index 0000000000..55acc34080 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/z3/Z3SmtLibItpMarker.java @@ -0,0 +1,23 @@ +package hu.bme.mit.theta.solver.smtlib.impl.z3; + +import hu.bme.mit.theta.solver.smtlib.solver.interpolation.SmtLibItpMarker; + +public class Z3SmtLibItpMarker extends SmtLibItpMarker { + private static final String markerPattern = "_z3_marker_%d"; + private static long markerCount = 0; + + static void resetMarkerCount() { + markerCount = 0; + } + + private final String markerName; + + public Z3SmtLibItpMarker() { + super(); + markerName = String.format(markerPattern, markerCount++); + } + + public String getMarkerName() { + return markerName; + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/z3/Z3SmtLibItpSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/z3/Z3SmtLibItpSolver.java new file mode 100644 index 0000000000..b5f5187ee4 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/z3/Z3SmtLibItpSolver.java @@ -0,0 +1,210 @@ +package hu.bme.mit.theta.solver.smtlib.impl.z3; + +import hu.bme.mit.theta.common.Tuple2; +import hu.bme.mit.theta.core.decl.ConstDecl; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.BoolExprs; +import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.solver.Interpolant; +import hu.bme.mit.theta.solver.ItpMarker; +import hu.bme.mit.theta.solver.ItpMarkerTree; +import hu.bme.mit.theta.solver.ItpPattern; +import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Lexer; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibItpSolver; +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolverException; +import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; +import hu.bme.mit.theta.solver.smtlib.solver.interpolation.SmtLibInterpolant; +import hu.bme.mit.theta.solver.smtlib.solver.interpolation.SmtLibItpPattern; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibModel; +import hu.bme.mit.theta.solver.smtlib.solver.parser.ThrowExceptionErrorListener; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.misc.Interval; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +public final class Z3SmtLibItpSolver extends SmtLibItpSolver { + private boolean topMostContainsAssertions = false; + + public Z3SmtLibItpSolver( + final SmtLibSymbolTable symbolTable, final SmtLibTransformationManager transformationManager, + final SmtLibTermTransformer termTransformer, final SmtLibSolverBinary solverBinary + ) { + super(symbolTable, transformationManager, termTransformer, solverBinary); + } + + @Override + public ItpPattern createTreePattern(final ItpMarkerTree root) { + checkNotNull(root); + return SmtLibItpPattern.of(root); + } + + @Override + public Z3SmtLibItpMarker createMarker() { + final var marker = new Z3SmtLibItpMarker(); + markers.add(marker); + return marker; + } + + + + @Override + public void add(ItpMarker marker, Expr assertion) { + if(topMostContainsAssertions) { + issueGeneralCommand("(pop 1)"); // Topmost frame contains marker assertions + topMostContainsAssertions = false; + } + super.add(marker, assertion); + } + + @Override + protected void add(final Z3SmtLibItpMarker marker, final Expr assertion, final Set> consts, final String term) { + consts.stream().map(symbolTable::getDeclaration).forEach(this::issueGeneralCommand); + } + + @Override + public SolverStatus check() { + if(topMostContainsAssertions) { + issueGeneralCommand("(pop 1)"); // Topmost frame contains marker assertions + topMostContainsAssertions = false; + } + Z3SmtLibItpMarker.resetMarkerCount(); + issueGeneralCommand("(push 1)"); // Topmost frame contains marker assertions + topMostContainsAssertions = true; + + for(final var marker : markers.toCollection()) { + final var terms = marker.getTerms(); + if(terms.size() == 0) { + issueGeneralCommand(String.format("(assert (! true :named %s))", marker.getMarkerName())); + } + else { + final var term = String.format("(and %s)", String.join(" ", marker.getTerms().stream().map(Tuple2::get2).collect(Collectors.toUnmodifiableList()))); + + issueGeneralCommand(String.format("(assert (! %s :named %s))", term, marker.getMarkerName())); + } + } + + return super.check(); + } + + @Override + public void push() { + if(topMostContainsAssertions) { + issueGeneralCommand("(pop 1)"); // Topmost frame contains marker assertions + topMostContainsAssertions = false; + } + super.push(); // Topmost frame contains marker assertions + } + + @Override + public void pop(int n) { + if(topMostContainsAssertions) { + issueGeneralCommand("(pop 1)"); // Topmost frame contains marker assertions + topMostContainsAssertions = false; + } + super.pop(n); // Topmost frame contains marker assertions + } + + @Override + public Interpolant getInterpolant(final ItpPattern pattern) { + checkState(getStatus() == SolverStatus.UNSAT, "Cannot get interpolant if status is not UNSAT."); + checkArgument(pattern instanceof SmtLibItpPattern); + @SuppressWarnings("unchecked") + final var z3ItpPattern = (SmtLibItpPattern) pattern; + + final var term = patternToTerm(z3ItpPattern.getRoot()); + final var markerCount = getMarkerCount(z3ItpPattern.getRoot()); + + final List> itpList = new LinkedList<>(); + + solverBinary.issueCommand(String.format("(get-interpolant %s)", term)); + for(var i = 0; i < markerCount; i++) { + final var res = parseItpResponse(solverBinary.readResponse()); + itpList.add(termTransformer.toExpr(res, BoolExprs.Bool(), new SmtLibModel(Collections.emptyMap()))); + } + // itpList.add(False()); + + final Map> itpMap = new HashMap<>(); + buildItpMapFormList(z3ItpPattern.getRoot(), itpList, itpMap); + + return new SmtLibInterpolant(itpMap); + } + + private String patternToTerm(final ItpMarkerTree markerTree) { + final Collection opTerms = new LinkedList<>(); + + final Z3SmtLibItpMarker marker = markerTree.getMarker(); + opTerms.add(marker.getMarkerName()); + + for (final var child : markerTree.getChildren()) { + final var childTerm = patternToTerm(child); + opTerms.add(childTerm); + } + + return String.format("(interp (and %s))", String.join(" ", opTerms)); + } + + private void buildItpMapFormList(final ItpMarkerTree pattern, final List> itpList, + final Map> itpMap) { + for (final ItpMarkerTree child : pattern.getChildren()) { + buildItpMapFormList(child, itpList, itpMap); + } + final ItpMarker marker = pattern.getMarker(); + final Expr itpExpr = itpList.get(0); + itpMap.put(marker, itpExpr); + itpList.remove(0); + } + + private int getMarkerCount(final ItpMarkerTree markerTree) { + return 1 + markerTree.getChildren().stream().mapToInt(this::getMarkerCount).sum(); + } + + @Override + protected void init() { + issueGeneralCommand("(set-option :print-success true)"); + issueGeneralCommand("(set-option :produce-interpolants true)"); + super.init(); + issueGeneralCommand("(push 1)"); // Topmost frame contains marker assertions + } + + private String parseItpResponse(final String response) { + final var lexer = new SMTLIBv2Lexer(CharStreams.fromString(response)); + final var parser = new SMTLIBv2Parser(new CommonTokenStream(lexer)); + try { + lexer.removeErrorListeners(); + lexer.addErrorListener(new ThrowExceptionErrorListener()); + parser.removeErrorListeners(); + parser.addErrorListener(new ThrowExceptionErrorListener()); + return extractString(parser.term()); + } + catch (Exception e) { + try { + throw new SmtLibSolverException(parser.response().general_response_error().reason.getText()); + } + catch(Exception ex) { + throw new SmtLibSolverException("Could not parse solver output: " + response, e); + } + } + } + + private static String extractString(final ParserRuleContext ctx) { + return ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex())); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/z3/Z3SmtLibSolverFactory.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/z3/Z3SmtLibSolverFactory.java new file mode 100644 index 0000000000..ec580ed951 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/z3/Z3SmtLibSolverFactory.java @@ -0,0 +1,38 @@ +package hu.bme.mit.theta.solver.smtlib.impl.z3; + +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverBinary; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSolverFactory; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTransformationManager; + +import java.nio.file.Path; + +public class Z3SmtLibSolverFactory extends GenericSmtLibSolverFactory { + private final boolean itpSupported; + + private Z3SmtLibSolverFactory(Path solverPath, String[] args, boolean itpSupported) { + super(solverPath, args); + this.itpSupported = itpSupported; + } + + public static Z3SmtLibSolverFactory create(Path solverPath, String[] args, boolean itpSupported) { + return new Z3SmtLibSolverFactory(solverPath, args, itpSupported); + } + + @Override + public ItpSolver createItpSolver() { + if(itpSupported) { + final var symbolTable = new GenericSmtLibSymbolTable(); + final var transformationManager = new GenericSmtLibTransformationManager(symbolTable); + final var termTransformer = new GenericSmtLibTermTransformer(symbolTable); + final var solverBinary = new GenericSmtLibSolverBinary(solverPath, args); + + return new Z3SmtLibItpSolver(symbolTable, transformationManager, termTransformer, solverBinary); + } + else { + throw new UnsupportedOperationException("Z3 interpolation supported below 4.5.0"); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/z3/Z3SmtLibSolverInstaller.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/z3/Z3SmtLibSolverInstaller.java new file mode 100644 index 0000000000..b69d66af09 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/z3/Z3SmtLibSolverInstaller.java @@ -0,0 +1,168 @@ +package hu.bme.mit.theta.solver.smtlib.impl.z3; + +import hu.bme.mit.theta.common.OsHelper; +import hu.bme.mit.theta.common.logging.Logger; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstaller; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException; +import hu.bme.mit.theta.solver.smtlib.utils.Compress; +import hu.bme.mit.theta.solver.smtlib.utils.SemVer; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static hu.bme.mit.theta.common.OsHelper.Architecture.X64; +import static hu.bme.mit.theta.common.OsHelper.Architecture.X86; +import static hu.bme.mit.theta.common.OsHelper.OperatingSystem.LINUX; +import static hu.bme.mit.theta.common.OsHelper.OperatingSystem.MAC; +import static hu.bme.mit.theta.common.OsHelper.OperatingSystem.WINDOWS; + +public class Z3SmtLibSolverInstaller extends SmtLibSolverInstaller.Default { + private final List versions; + + public Z3SmtLibSolverInstaller(final Logger logger) { + super(logger); + + versions = new ArrayList<>(); + versions.add(SemVer.VersionDecoder.create(SemVer.of("4.8.12")) + .addString(LINUX, X64, "x64-glibc-2.31") + .addString(WINDOWS, X64, "x64-win") + .addString(WINDOWS, X86, "x86-win") + .addString(MAC, X64, "x64-osx-10.15.7") + .build() + ); + versions.add(SemVer.VersionDecoder.create(SemVer.of("4.8.10")) + .addString(LINUX, X64, "x64-ubuntu-18.04") + .addString(WINDOWS, X64, "x64-win") + .addString(WINDOWS, X86, "x86-win") + .addString(MAC, X64, "x64-osx-10.15.7") + .build() + ); + versions.add(SemVer.VersionDecoder.create(SemVer.of("4.8.5")) + .addString(LINUX, X64, "x64-ubuntu-16.04") + .addString(WINDOWS, X64, "x64-win") + .addString(WINDOWS, X86, "x86-win") + .addString(MAC, X64, "x64-osx-10.14.6") + .build() + ); + versions.add(SemVer.VersionDecoder.create(SemVer.of("4.8.5")) + .addString(LINUX, X64, "x64-ubuntu-16.04") + .addString(WINDOWS, X64, "x64-win") + .addString(WINDOWS, X86, "x86-win") + .addString(MAC, X64, "x64-osx-10.14.2") + .build() + ); + versions.add(SemVer.VersionDecoder.create(SemVer.of("4.8.4")) + .addString(LINUX, X64, "x64-ubuntu-16.04") + .addString(WINDOWS, X64, "x64-win") + .addString(WINDOWS, X86, "x86-win") + .addString(MAC, X64, "x64-osx-10.14.1") + .build() + ); + versions.add(SemVer.VersionDecoder.create(SemVer.of("4.8.3")) + .addString(LINUX, X64, "x64-ubuntu-16.04") + .addString(WINDOWS, X64, "x64-win") + .addString(WINDOWS, X86, "x86-win") + .addString(MAC, X64, "x64-osx-10.13.6") + .build() + ); + versions.add(SemVer.VersionDecoder.create(SemVer.of("4.6.0")) + .addString(LINUX, X64, "x64-ubuntu-16.04") + .addString(WINDOWS, X64, "x64-win") + .addString(WINDOWS, X86, "x86-win") + .addString(MAC, X64, "x64-osx-10.11.6") + .build() + ); + versions.add(SemVer.VersionDecoder.create(SemVer.of("4.4.0")) + .addString(LINUX, X64, "x64-ubuntu-14.04") + .addString(LINUX, X86, "x86-ubuntu-14.04") + .addString(WINDOWS, X64, "x64-win") + .addString(WINDOWS, X86, "x86-win") + .addString(MAC, X64, "x64-osx-10.11.6") + .build() + ); + versions.add(SemVer.VersionDecoder.create(SemVer.of("4.3.2")) + .addString(WINDOWS, X64, "x64-win") + .addString(WINDOWS, X86, "x86-win") + .build() + ); + } + + @Override + protected String getSolverName() { + return "z3"; + } + + @Override + protected void installSolver(final Path installDir, final String version) throws SmtLibSolverInstallerException { + final var semVer = SemVer.of(version); + String archStr = null; + + for(final var versionDecoder : versions) { + if(semVer.compareTo(versionDecoder.getVersion()) >= 0) { + archStr = versionDecoder.getOsArchString(OsHelper.getOs(), OsHelper.getArch()); + break; + } + } + if(archStr == null) { + throw new SmtLibSolverInstallerException(String.format("z3 on operating system %s and architecture %s is not supported", OsHelper.getOs(), OsHelper.getArch())); + } + + final var downloadUrl = URI.create(String.format( + "https://github.com/Z3Prover/z3/releases/download/z3-%s/z3-%s-%s.zip", + version, version, archStr + )); + + logger.write(Logger.Level.MAINSTEP, "Starting download (%s)...\n", downloadUrl.toString()); + try(final var inputStream = downloadUrl.toURL().openStream()) { + Compress.extract(inputStream, installDir, Compress.CompressionType.ZIP); + installDir.resolve("bin").resolve(getSolverBinaryName()).toFile().setExecutable(true, true); + } + catch (IOException e) { + throw new SmtLibSolverInstallerException(e); + } + + logger.write(Logger.Level.MAINSTEP, "Download finished\n"); + } + + @Override + protected void uninstallSolver(Path installDir, String version) { + // Default uninstall is suitable + } + + @Override + protected String[] getDefaultSolverArgs(String version) { + return new String[] { "-smt2", "-in" }; + } + + @Override + public SolverFactory getSolverFactory(final Path installDir, final String version, final Path solverPath, final String[] solverArgs) throws SmtLibSolverInstallerException { + final var solverFilePath = solverPath != null ? solverPath : installDir.resolve("bin").resolve(getSolverBinaryName()); + return Z3SmtLibSolverFactory.create(solverFilePath, solverArgs, SemVer.of(version).compareTo(SemVer.of("4.5.0")) <= 0); + } + + @Override + public List getSupportedVersions() { + return Arrays.asList( + "4.8.12", "4.8.11", "3.8.10", "4.8.9", "4.8.8", "4.8.7", "4.8.6", "4.8.5", "4.8.4", "4.8.3", + "4.8.2", "4.8.1", "4.7.1", "4.6.0", "4.5.0", "4.4.1", "4.4.0", "4.3.2" + ); + } + + private String getSolverBinaryName() { + switch(OsHelper.getOs()) { + case WINDOWS: + return "z3.exe"; + case LINUX: + return "z3"; + case MAC: + return "z3.dmg"; + default: + throw new AssertionError(); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibItpSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibItpSolver.java new file mode 100644 index 0000000000..c691f0d6d1 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibItpSolver.java @@ -0,0 +1,242 @@ +package hu.bme.mit.theta.solver.smtlib.solver; + +import hu.bme.mit.theta.core.decl.ConstDecl; +import hu.bme.mit.theta.core.model.Valuation; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.core.utils.ExprUtils; +import hu.bme.mit.theta.solver.Interpolant; +import hu.bme.mit.theta.solver.ItpMarker; +import hu.bme.mit.theta.solver.ItpMarkerTree; +import hu.bme.mit.theta.solver.ItpPattern; +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.Stack; +import hu.bme.mit.theta.solver.UnknownSolverStatusException; +import hu.bme.mit.theta.solver.impl.StackImpl; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Lexer; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser; +import hu.bme.mit.theta.solver.smtlib.solver.parser.CheckSatResponse; +import hu.bme.mit.theta.solver.smtlib.solver.parser.GeneralResponse; +import hu.bme.mit.theta.solver.smtlib.solver.parser.GetModelResponse; +import hu.bme.mit.theta.solver.smtlib.solver.parser.ThrowExceptionErrorListener; +import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; +import hu.bme.mit.theta.solver.smtlib.solver.interpolation.SmtLibItpMarker; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibValuation; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; + +import java.util.Collection; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +public abstract class SmtLibItpSolver implements ItpSolver { + protected final SmtLibSymbolTable symbolTable; + protected final SmtLibTransformationManager transformationManager; + protected final SmtLibTermTransformer termTransformer; + + protected final SmtLibSolverBinary solverBinary; + + protected final Stack> assertions; + protected final Stack markers; + protected final Stack> declarationStack; + + private Valuation model; + private SolverStatus status; + + + public SmtLibItpSolver( + final SmtLibSymbolTable symbolTable, final SmtLibTransformationManager transformationManager, + final SmtLibTermTransformer termTransformer, final SmtLibSolverBinary solverBinary + ) { + this.symbolTable = symbolTable; + this.transformationManager = transformationManager; + this.termTransformer = termTransformer; + + this.solverBinary = solverBinary; + + this.assertions = new StackImpl<>(); + this.markers = new StackImpl<>(); + this.declarationStack = new StackImpl<>(); + + init(); + } + + @Override + public abstract ItpPattern createTreePattern(ItpMarkerTree root); + + @Override + public abstract T createMarker(); + + @Override + public Collection getMarkers() { + return markers.toCollection(); + } + + @SuppressWarnings("unchecked") + @Override + public void add(final ItpMarker marker, final Expr assertion) { + checkNotNull(marker); + checkNotNull(assertion); + checkNotNull((T) marker); + checkArgument(markers.toCollection().contains(marker)); + + final var consts = ExprUtils.getConstants(assertion); + consts.removeAll(declarationStack.toCollection()); + declarationStack.add(consts); + + final var itpMarker = (T) marker; + final var term = transformationManager.toTerm(assertion); + itpMarker.add(assertion, term); + + add(itpMarker, assertion, consts, term); + + clearState(); + } + + protected abstract void add(final T marker, final Expr assertion, final Set> consts, final String term); + + @Override + public SolverStatus check() { + solverBinary.issueCommand("(check-sat)"); + var res = parseResponse(solverBinary.readResponse()); + if(res.isError()) { + throw new SmtLibSolverException(res.getReason()); + } + else if(res.isSpecific()) { + final CheckSatResponse checkSatResponse = res.asSpecific(); + if(checkSatResponse.isSat()) { + status = SolverStatus.SAT; + } + else if(checkSatResponse.isUnsat()) { + status = SolverStatus.UNSAT; + } + else { + throw new UnknownSolverStatusException(); + } + } + else { + throw new AssertionError(); + } + + return status; + } + + @Override + public void push() { + markers.push(); + for (final var marker : markers) { + marker.push(); + } + assertions.push(); + declarationStack.push(); + issueGeneralCommand("(push 1)"); + } + + @Override + public void pop(final int n) { + markers.pop(n); + for (final var marker : markers) { + marker.pop(n); + } + assertions.pop(n); + declarationStack.pop(n); + issueGeneralCommand(String.format("(pop %d)", n)); + clearState(); + } + + @Override + public void reset() { + issueGeneralCommand("(reset)"); + clearState(); + init(); + } + + @Override + public SolverStatus getStatus() { + checkState(status != null, "Solver status is unknown."); + return status; + } + + @Override + public Valuation getModel() { + checkState(status == SolverStatus.SAT, "Cannot get model if status is not SAT."); + + if (model == null) { + model = extractModel(); + } + + return model; + } + + private Valuation extractModel() { + assert status == SolverStatus.SAT; + assert model == null; + + solverBinary.issueCommand("(get-model)"); + final var res = parseResponse(solverBinary.readResponse()); + if(res.isError()) { + throw new SmtLibSolverException(res.getReason()); + } + else if(res.isSpecific()) { + final GetModelResponse getModelResponse = res.asSpecific(); + return new SmtLibValuation(symbolTable, transformationManager, termTransformer, getModelResponse.getModel()); + } + else { + throw new AssertionError(); + } + } + + @Override + public abstract Interpolant getInterpolant(ItpPattern pattern); + + @Override + public Collection> getAssertions() { + return assertions.toCollection(); + } + + @Override + public void close() throws Exception { + solverBinary.close(); + } + + protected void clearState() { + status = null; + model = null; + } + + protected void init() { + issueGeneralCommand("(set-option :print-success true)"); + issueGeneralCommand("(set-option :produce-models true)"); + issueGeneralCommand("(set-logic ALL)"); + } + + protected final void issueGeneralCommand(String command) { + solverBinary.issueCommand(command); + var res = parseResponse(solverBinary.readResponse()); + if(res.isError()) { + throw new SmtLibSolverException(res.getReason()); + } + } + + protected final GeneralResponse parseResponse(final String response) { + try { + final var lexer = new SMTLIBv2Lexer(CharStreams.fromString(response)); + final var parser = new SMTLIBv2Parser(new CommonTokenStream(lexer)); + lexer.removeErrorListeners(); + lexer.addErrorListener(new ThrowExceptionErrorListener()); + parser.removeErrorListeners(); + parser.addErrorListener(new ThrowExceptionErrorListener()); + return GeneralResponse.fromContext(parser.response()); + } + catch (Exception e) { + throw new SmtLibSolverException("Could not parse solver output: " + response, e); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java new file mode 100644 index 0000000000..f01184a717 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolver.java @@ -0,0 +1,285 @@ +package hu.bme.mit.theta.solver.smtlib.solver; + +import hu.bme.mit.theta.core.decl.ConstDecl; +import hu.bme.mit.theta.core.model.Valuation; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.core.utils.ExprUtils; +import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.Stack; +import hu.bme.mit.theta.solver.UCSolver; +import hu.bme.mit.theta.solver.UnknownSolverStatusException; +import hu.bme.mit.theta.solver.impl.StackImpl; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Lexer; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser; +import hu.bme.mit.theta.solver.smtlib.solver.binary.SmtLibSolverBinary; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibValuation; +import hu.bme.mit.theta.solver.smtlib.solver.parser.CheckSatResponse; +import hu.bme.mit.theta.solver.smtlib.solver.parser.GeneralResponse; +import hu.bme.mit.theta.solver.smtlib.solver.parser.GetModelResponse; +import hu.bme.mit.theta.solver.smtlib.solver.parser.GetUnsatCoreResponse; +import hu.bme.mit.theta.solver.smtlib.solver.parser.ThrowExceptionErrorListener; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkState; + +public class SmtLibSolver implements UCSolver, Solver { + protected final SmtLibSymbolTable symbolTable; + protected final SmtLibTransformationManager transformationManager; + protected final SmtLibTermTransformer termTransformer; + + protected final SmtLibSolverBinary solverBinary; + private final boolean unsatCoreEnabled; + + protected final Stack> assertions; + protected final Map> assumptions; + protected final Stack> declarationStack; + + private static final String ASSUMPTION_LABEL = "_LABEL_%d"; + private int labelNum = 0; + + private Valuation model; + private Collection> unsatCore; + private SolverStatus status; + + public SmtLibSolver( + final SmtLibSymbolTable symbolTable, final SmtLibTransformationManager transformationManager, + final SmtLibTermTransformer termTransformer, final SmtLibSolverBinary solverBinary, boolean unsatCoreEnabled + ) { + this.solverBinary = solverBinary; + this.symbolTable = symbolTable; + this.transformationManager = transformationManager; + this.termTransformer = termTransformer; + + this.unsatCoreEnabled = unsatCoreEnabled; + + assertions = new StackImpl<>(); + assumptions = new HashMap<>(); + declarationStack = new StackImpl<>(); + + init(); + } + + @Override + public void add(Expr assertion) { + final var consts = ExprUtils.getConstants(assertion); + consts.removeAll(declarationStack.toCollection()); + declarationStack.add(consts); + + final var term = transformationManager.toTerm(assertion); + + consts.stream().map(symbolTable::getDeclaration).forEach(this::issueGeneralCommand); + issueGeneralCommand(String.format("(assert %s)", term)); + + clearState(); + } + + public void add(final Expr assertion, final String term) { + final var consts = ExprUtils.getConstants(assertion); + consts.removeAll(declarationStack.toCollection()); + declarationStack.add(consts); + + consts.stream().map(symbolTable::getDeclaration).forEach(this::issueGeneralCommand); + issueGeneralCommand(String.format("(assert %s)", term)); + + clearState(); + } + + @Override + public void track(Expr assertion) { + final var consts = ExprUtils.getConstants(assertion); + consts.removeAll(declarationStack.toCollection()); + declarationStack.add(consts); + + final var term = transformationManager.toTerm(assertion); + final var label = String.format(ASSUMPTION_LABEL, labelNum++); + assumptions.put(label, assertion); + + consts.stream().map(symbolTable::getDeclaration).forEach(this::issueGeneralCommand); + issueGeneralCommand(String.format("(assert (! %s :named %s))", term, label)); + + clearState(); + } + + @Override + public SolverStatus check() { + solverBinary.issueCommand("(check-sat)"); + var res = parseResponse(solverBinary.readResponse()); + if(res.isError()) { + throw new SmtLibSolverException(res.getReason()); + } + else if(res.isSpecific()) { + final CheckSatResponse checkSatResponse = res.asSpecific(); + if(checkSatResponse.isSat()) { + status = SolverStatus.SAT; + } + else if(checkSatResponse.isUnsat()) { + status = SolverStatus.UNSAT; + } + else { + throw new UnknownSolverStatusException(); + } + } + else { + throw new AssertionError(); + } + + return status; + } + + @Override + public void push() { + assertions.push(); + declarationStack.push(); + issueGeneralCommand("(push 1)"); + } + + @Override + public void pop(int n) { + assertions.pop(n); + declarationStack.pop(n); + issueGeneralCommand("(pop 1)"); + clearState(); + } + + @Override + public void reset() { + issueGeneralCommand("(reset)"); + clearState(); + init(); + } + + @Override + public SolverStatus getStatus() { + checkState(status != null, "Solver status is unknown."); + return status; + } + + @Override + public Valuation getModel() { + checkState(status == SolverStatus.SAT, "Cannot get model if status is not SAT."); + + if (model == null) { + model = extractModel(); + } + + return model; + } + + private Valuation extractModel() { + assert status == SolverStatus.SAT; + assert model == null; + + solverBinary.issueCommand("(get-model)"); + final var res = parseResponse(solverBinary.readResponse()); + if(res.isError()) { + throw new SmtLibSolverException(res.getReason()); + } + else if(res.isSpecific()) { + final GetModelResponse getModelResponse = res.asSpecific(); + return new SmtLibValuation(symbolTable, transformationManager, termTransformer, getModelResponse.getModel()); + } + else { + throw new AssertionError(); + } + } + + @Override + public Collection> getUnsatCore() { + checkState(status == SolverStatus.UNSAT, "Cannot get unsat core if status is not UNSAT"); + + if (unsatCore == null) { + unsatCore = extractUnsatCore(); + } + + return Collections.unmodifiableCollection(unsatCore); + } + + private Collection> extractUnsatCore() { + assert status == SolverStatus.UNSAT; + assert unsatCore == null; + + final Collection> unsatCore = new LinkedList<>(); + final Collection unsatCoreLabels; + + solverBinary.issueCommand("(get-unsat-core)"); + final var res = parseResponse(solverBinary.readResponse()); + if(res.isError()) { + throw new SmtLibSolverException(res.getReason()); + } + else if(res.isSpecific()) { + final GetUnsatCoreResponse getUnsatCoreResponse = res.asSpecific(); + unsatCoreLabels = getUnsatCoreResponse.getLabels(); + } + else { + throw new AssertionError(); + } + + for(final var label : unsatCoreLabels) { + final Expr assumption = assumptions.get(label); + assert assumption != null; + unsatCore.add(assumption); + } + + return unsatCore; + } + + @Override + public Collection> getAssertions() { + return assertions.toCollection(); + } + + @Override + public void close() throws Exception { + solverBinary.close(); + } + + private void init() { + issueGeneralCommand("(set-option :print-success true)"); + issueGeneralCommand("(set-option :produce-models true)"); + if(unsatCoreEnabled) { + issueGeneralCommand("(set-option :produce-unsat-cores true)"); + } + issueGeneralCommand("(set-logic ALL)"); + } + + protected void clearState() { + status = null; + model = null; + unsatCore = null; + } + + protected final void issueGeneralCommand(String command) { + solverBinary.issueCommand(command); + var res = parseResponse(solverBinary.readResponse()); + if(res.isError()) { + throw new SmtLibSolverException(res.getReason()); + } + } + + protected final GeneralResponse parseResponse(final String response) { + try { + final var lexer = new SMTLIBv2Lexer(CharStreams.fromString(response)); + final var parser = new SMTLIBv2Parser(new CommonTokenStream(lexer)); + lexer.removeErrorListeners(); + lexer.addErrorListener(new ThrowExceptionErrorListener()); + parser.removeErrorListeners(); + parser.addErrorListener(new ThrowExceptionErrorListener()); + return GeneralResponse.fromContext(parser.response()); + } + catch (Exception e) { + throw new SmtLibSolverException("Could not parse solver output: " + response, e); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolverException.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolverException.java new file mode 100644 index 0000000000..fecf6a7d83 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/SmtLibSolverException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2017 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.solver.smtlib.solver; + +public class SmtLibSolverException extends RuntimeException { + private static final long serialVersionUID = -7472824180590829943L; + + public SmtLibSolverException(Exception e) { + super(e); + } + + public SmtLibSolverException(String e) { + super(e); + } + + public SmtLibSolverException(String e, Throwable c) { + super(e, c); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/binary/SmtLibSolverBinary.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/binary/SmtLibSolverBinary.java new file mode 100644 index 0000000000..61091d5ac8 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/binary/SmtLibSolverBinary.java @@ -0,0 +1,6 @@ +package hu.bme.mit.theta.solver.smtlib.solver.binary; + +public interface SmtLibSolverBinary extends AutoCloseable { + void issueCommand(String command); + String readResponse(); +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/binary/SmtLibSolverBinaryException.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/binary/SmtLibSolverBinaryException.java new file mode 100644 index 0000000000..dbb9506aa7 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/binary/SmtLibSolverBinaryException.java @@ -0,0 +1,30 @@ +/* + * Copyright 2017 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.solver.smtlib.solver.binary; + +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolverException; + +public class SmtLibSolverBinaryException extends SmtLibSolverException { + private static final long serialVersionUID = -7472824180590829943L; + + public SmtLibSolverBinaryException(Exception e) { + super(e); + } + + public SmtLibSolverBinaryException(String e) { + super(e); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/installer/SmtLibSolverInstaller.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/installer/SmtLibSolverInstaller.java new file mode 100644 index 0000000000..b662892029 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/installer/SmtLibSolverInstaller.java @@ -0,0 +1,290 @@ +package hu.bme.mit.theta.solver.smtlib.solver.installer; + +import hu.bme.mit.theta.common.logging.Logger; +import hu.bme.mit.theta.solver.SolverFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +public interface SmtLibSolverInstaller { + + void install(Path home, String version, String name) throws SmtLibSolverInstallerException; + + void install(Path home, String version, String name, Path solverPath) throws SmtLibSolverInstallerException; + + void uninstall(Path home, String version) throws SmtLibSolverInstallerException; + + void rename(Path home, String version, String name) throws SmtLibSolverInstallerException; + + String getInfo(Path home, String version) throws SmtLibSolverInstallerException; + + Path getArgsFile(Path home, String version) throws SmtLibSolverInstallerException; + + SolverFactory getSolverFactory(Path home, String version) throws SmtLibSolverInstallerException; + + List getSupportedVersions() throws SmtLibSolverInstallerException; + + List getInstalledVersions(Path home) throws SmtLibSolverInstallerException; + + abstract class Default implements SmtLibSolverInstaller { + protected final Logger logger; + + public Default(final Logger logger) { + this.logger = logger; + } + + @Override + public final void install(final Path home, final String version, final String name) throws SmtLibSolverInstallerException { + checkNotNull(home); + checkArgument(Files.exists(home)); + checkVersion(version); + checkName(name); + + doInstall(home, version, name, null); + } + + @Override + public final void install(final Path home, final String version, final String name, final Path solverPath) throws SmtLibSolverInstallerException { + checkNotNull(home); + checkArgument(Files.exists(home)); + checkVersion(version); + checkName(name); + checkNotNull(solverPath); + + doInstall(home, version, name, solverPath); + } + + private void doInstall(final Path home, final String version, final String name, final Path solverPath) throws SmtLibSolverInstallerException { + final var installDir = getInstallDir(home, name); + if(Files.exists(installDir)) { + throw new SmtLibSolverInstallerException("There is already a solver instance with this name installed"); + } + + try { + logger.write(Logger.Level.MAINSTEP, "Beginning installation...\n"); + + Files.createDirectory(installDir); + + if(solverPath != null) { + final var solverFilePath = solverFile(installDir); + Files.writeString(solverFilePath, solverPath.toAbsolutePath().toString(), StandardCharsets.UTF_8); + } + + final var solverArgsPath = argsFile(installDir); + Files.writeString(solverArgsPath, String.join("\n", getDefaultSolverArgs(version)), StandardCharsets.UTF_8); + + final var solverInfoPath = infoFile(installDir); + Files.writeString( + solverInfoPath, + String.format("solver=%s\n", getSolverName()) + + String.format("version=%s\n", version) + + String.format("name=%s\n", name) + + (solverPath != null ? String.format("binary=%s\n", solverPath.toAbsolutePath().toString()) : ""), + StandardCharsets.UTF_8 + ); + + if(solverPath == null) { + installSolver(installDir, version); + } + + logger.write(Logger.Level.MAINSTEP, "Installation finished\n"); + } + catch (SmtLibSolverInstallerException e) { + uninstall(home, version); + throw e; + } + catch (IOException e) { + throw new SmtLibSolverInstallerException(String.format("Error: %s", e.getMessage()), e); + } + } + + @Override + public final void uninstall(Path home, String version) throws SmtLibSolverInstallerException { + checkNotNull(home); + checkArgument(Files.exists(home)); + checkName(version); + + final var installDir = getInstallDir(home, version); + if(!Files.exists(installDir)) { + throw new SmtLibSolverInstallerException("The version is not installed"); + } + + try { + logger.write(Logger.Level.MAINSTEP, "Beginning uninstallation...\n"); + + uninstallSolver(installDir, version); + + deleteDirectory(installDir.toFile()); + + logger.write(Logger.Level.MAINSTEP, "Uninstallation finished\n"); + } + catch (IOException e) { + throw new SmtLibSolverInstallerException(String.format("Error: %s", e.getMessage()), e); + } + } + + @Override + public void rename(final Path home, final String version, final String name) throws SmtLibSolverInstallerException { + checkNotNull(home); + checkArgument(Files.exists(home)); + checkVersion(version); + checkName(name); + + final var installDir = getInstallDir(home, version); + if(!Files.exists(installDir)) { + throw new SmtLibSolverInstallerException("The version is not installed"); + } + + final var newInstallDir = getInstallDir(home, name); + if(Files.exists(newInstallDir)) { + throw new SmtLibSolverInstallerException("The chosen name is already used"); + } + + try { + Files.move(installDir, newInstallDir); + } + catch (IOException e) { + throw new SmtLibSolverInstallerException(String.format("Error renaming solver: %s", e.getMessage()), e); + } + } + + @Override + public final SolverFactory getSolverFactory(final Path home, final String version) throws SmtLibSolverInstallerException { + checkNotNull(home); + checkArgument(Files.exists(home)); + checkVersion(version); + + final var installDir = home.resolve(version); + if(!Files.exists(installDir)) { + throw new SmtLibSolverInstallerException("The version is not installed"); + } + + try { + final Path solverPath; + final var solverFilePath = solverFile(installDir); + if(Files.exists(solverFilePath)) { + solverPath = Path.of(Files.readAllLines(solverFilePath, StandardCharsets.UTF_8).get(0)); + } + else { + solverPath = null; + } + + final var solverArgsPath = argsFile(installDir); + final var solverArgs = Files.readAllLines(solverArgsPath, StandardCharsets.UTF_8).toArray(String[]::new); + + return getSolverFactory(installDir, version, solverPath, solverArgs); + } + catch (IOException e) { + throw new SmtLibSolverInstallerException(String.format("Error: %s", e.getMessage()), e); + } + } + + @Override + public final String getInfo(Path home, String version) throws SmtLibSolverInstallerException { + checkNotNull(home); + checkArgument(Files.exists(home)); + checkName(version); + + final var installDir = getInstallDir(home, version); + if(!Files.exists(installDir)) { + throw new SmtLibSolverInstallerException("The version is not installed"); + } + + try { + final var solverInfoPath = infoFile(installDir); + final var solverInfoStr = Files.readString(solverInfoPath, StandardCharsets.UTF_8); + final var solverArgsPath = argsFile(installDir); + final var solverArgs = Files.readAllLines(solverArgsPath, StandardCharsets.UTF_8).toArray(String[]::new); + + return solverInfoStr + String.format("args=%s\n", String.join(" ", solverArgs)); + } + catch (IOException e) { + throw new SmtLibSolverInstallerException(String.format("Error: %s", e.getMessage()), e); + } + } + + @Override + public final Path getArgsFile(Path home, String version) throws SmtLibSolverInstallerException { + checkNotNull(home); + checkArgument(Files.exists(home)); + checkName(version); + + final var installDir = home.resolve(version); + if(!Files.exists(installDir)) { + throw new SmtLibSolverInstallerException("The version is not installed"); + } + + return argsFile(installDir); + } + + @Override + public final List getInstalledVersions(Path home) { + checkNotNull(home); + + if(Files.exists(home)) { + final var installedDirs = home.toFile() + .list((current, name) -> new File(current, name).isDirectory()); + + assert installedDirs != null; + return Arrays.asList(installedDirs); + } + else { + return Collections.emptyList(); + } + } + + protected abstract String getSolverName() throws SmtLibSolverInstallerException; + protected abstract void installSolver(final Path installDir, final String version) throws SmtLibSolverInstallerException; + protected abstract void uninstallSolver(final Path installDir, final String version) throws SmtLibSolverInstallerException; + protected abstract SolverFactory getSolverFactory(final Path installDir, final String version, final Path solverPath, final String[] args) throws SmtLibSolverInstallerException; + protected abstract String[] getDefaultSolverArgs(final String version) throws SmtLibSolverInstallerException; + + protected final void checkName(final String name) throws SmtLibSolverInstallerException { + if(!name.matches("^[a-zA-Z0-9_.-]+$") || name.matches("latest")) { + throw new SmtLibSolverInstallerException("Unsupported name format: " + name); + } + } + + protected final void checkVersion(final String version) throws SmtLibSolverInstallerException { + if(!version.matches("^[a-zA-Z0-9_.-]+$")) { + throw new SmtLibSolverInstallerException("Unsupported version format: " + version); + } + } + + protected final Path getInstallDir(final Path home, final String version) { + return home.resolve(version); + } + + private Path solverFile(final Path installDir) { + return installDir.resolve("solver.txt"); + } + + protected final Path argsFile(final Path installDir) { + return installDir.resolve("solver-args.txt"); + } + + protected final Path infoFile(final Path installDir) { + return installDir.resolve("solver-info.txt"); + } + + private void deleteDirectory(File directoryToBeDeleted) throws IOException { + File[] allContents = directoryToBeDeleted.listFiles(); + if (allContents != null) { + for (File file : allContents) { + deleteDirectory(file); + } + } + + Files.delete(directoryToBeDeleted.toPath()); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/installer/SmtLibSolverInstallerException.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/installer/SmtLibSolverInstallerException.java new file mode 100644 index 0000000000..734344a284 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/installer/SmtLibSolverInstallerException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2017 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.solver.smtlib.solver.installer; + +public class SmtLibSolverInstallerException extends Exception { + private static final long serialVersionUID = -7472824180590329943L; + + public SmtLibSolverInstallerException(Exception e) { + super(e); + } + + public SmtLibSolverInstallerException(String e) { + super(e); + } + + public SmtLibSolverInstallerException(String e, Throwable c) { + super(e, c); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/interpolation/SmtLibInterpolant.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/interpolation/SmtLibInterpolant.java new file mode 100644 index 0000000000..38681d69c5 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/interpolation/SmtLibInterpolant.java @@ -0,0 +1,43 @@ +/* + * Copyright 2017 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.solver.smtlib.solver.interpolation; + +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.solver.Interpolant; +import hu.bme.mit.theta.solver.ItpMarker; + +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class SmtLibInterpolant implements Interpolant { + + private final Map> itpMap; + + public SmtLibInterpolant(final Map> itpMap) { + this.itpMap = itpMap; + } + + @Override + public Expr eval(final ItpMarker marker) { + checkNotNull(marker); + final Expr itpExpr = itpMap.get(marker); + checkNotNull(itpExpr); + return itpExpr; + } + +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/interpolation/SmtLibItpMarker.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/interpolation/SmtLibItpMarker.java new file mode 100644 index 0000000000..0ca2a1f7eb --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/interpolation/SmtLibItpMarker.java @@ -0,0 +1,36 @@ +package hu.bme.mit.theta.solver.smtlib.solver.interpolation; + +import hu.bme.mit.theta.common.Tuple2; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.solver.ItpMarker; +import hu.bme.mit.theta.solver.Stack; +import hu.bme.mit.theta.solver.impl.StackImpl; + +import java.util.Collection; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class SmtLibItpMarker implements ItpMarker { + private final Stack, String>> terms; + + public SmtLibItpMarker() { + terms = new StackImpl<>(); + } + + public void add(final Expr assertion, final String term) { + terms.add(Tuple2.of(checkNotNull(assertion), checkNotNull(term))); + } + + public void push() { + terms.push(); + } + + public void pop(final int n) { + terms.pop(n); + } + + public Collection, String>> getTerms() { + return terms.toCollection(); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/interpolation/SmtLibItpPattern.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/interpolation/SmtLibItpPattern.java new file mode 100644 index 0000000000..07908bcd2f --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/interpolation/SmtLibItpPattern.java @@ -0,0 +1,94 @@ +package hu.bme.mit.theta.solver.smtlib.solver.interpolation; + +import com.google.common.collect.Lists; +import hu.bme.mit.theta.solver.ItpMarker; +import hu.bme.mit.theta.solver.ItpMarkerTree; +import hu.bme.mit.theta.solver.ItpPattern; + +import java.util.ArrayList; +import java.util.List; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; + +public class SmtLibItpPattern implements ItpPattern.Binary, ItpPattern.Sequence, ItpPattern.Tree { + final ItpMarkerTree markerTree; + + SmtLibItpPattern(final ItpMarkerTree markerTree) { + this.markerTree = markerTree; + } + + @SuppressWarnings("unchecked") + public static SmtLibItpPattern of(final ItpMarkerTree markerTree) { + final var list = new ArrayList>(); + list.add(markerTree); + while(!list.isEmpty()) { + final var node = list.get(0); + list.remove(0); + + checkArgument(node.getMarker() instanceof SmtLibItpMarker); + + list.addAll(node.getChildren()); + } + + return new SmtLibItpPattern<>((ItpMarkerTree) markerTree); + } + + @Override + public T getA() { + checkState(isBinary()); + return markerTree.getChild(0).getMarker(); + } + + @Override + public T getB() { + checkState(isBinary()); + return markerTree.getMarker(); + } + + @Override + public List getSequence() { + checkState(isSequence()); + final var markerList = new ArrayList(); + + var current = markerTree; + while(current.getChildrenNumber() > 0) { + markerList.add(current.getMarker()); + current = current.getChild(0); + } + markerList.add(current.getMarker()); + + return Lists.reverse(markerList); + } + + @Override + public ItpMarkerTree getRoot() { + return markerTree; + } + + @Override + public E visit(ItpPatternVisitor visitor) { + return visitor.visitTreePattern(this); + } + + private boolean isBinary() { + return + markerTree != null && + markerTree.getChildrenNumber() == 1 && + markerTree.getChild(0) != null && + markerTree.getChild(0).getChildrenNumber() == 0; + } + + private boolean isSequence() { + var current = markerTree; + while(current.getChildrenNumber() > 0) { + if(current.getChildrenNumber() > 1) { + return false; + } + else { + current = current.getChild(0); + } + } + return true; + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/model/SmtLibModel.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/model/SmtLibModel.java new file mode 100644 index 0000000000..c9f5d2028a --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/model/SmtLibModel.java @@ -0,0 +1,20 @@ +package hu.bme.mit.theta.solver.smtlib.solver.model; + +import java.util.Collection; +import java.util.Map; + +public class SmtLibModel { + protected final Map values; + + public SmtLibModel(final Map values) { + this.values = values; + } + + public Collection getDecls() { + return values.keySet(); + } + + public String getTerm(final String symbol) { + return values.get(symbol); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/model/SmtLibValuation.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/model/SmtLibValuation.java new file mode 100644 index 0000000000..a6647dbccc --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/model/SmtLibValuation.java @@ -0,0 +1,143 @@ +package hu.bme.mit.theta.solver.smtlib.solver.model; + +import com.google.common.collect.ImmutableList; +import hu.bme.mit.theta.core.decl.ConstDecl; +import hu.bme.mit.theta.core.decl.Decl; +import hu.bme.mit.theta.core.model.Valuation; +import hu.bme.mit.theta.core.type.LitExpr; +import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.arraytype.ArrayType; +import hu.bme.mit.theta.core.type.bvtype.BvType; +import hu.bme.mit.theta.core.type.functype.FuncType; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.solver.transformer.SmtLibTransformationManager; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import static com.google.common.base.Preconditions.checkNotNull; + +public final class SmtLibValuation extends Valuation { + private final SmtLibSymbolTable symbolTable; + private final SmtLibTransformationManager transformationManager; + private final SmtLibTermTransformer termTransformer; + + private final SmtLibModel model; + private final Map, LitExpr> constToExpr; + private volatile Collection> constDecls = null; + + public SmtLibValuation( + final SmtLibSymbolTable symbolTable, final SmtLibTransformationManager transformationManager, + final SmtLibTermTransformer termTransformer, final SmtLibModel model + ) { + this.symbolTable = symbolTable; + this.transformationManager = transformationManager; + this.termTransformer = termTransformer; + this.model = model; + constToExpr = new HashMap<>(); + } + + @Override + public Collection> getDecls() { + Collection> result = constDecls; + if (result == null) { + result = constDeclsOf(model); + constDecls = result; + } + return result; + } + + @Override + public Optional> eval(Decl decl) { + checkNotNull(decl); + + if (!(decl instanceof ConstDecl)) { + return Optional.empty(); + } + + final ConstDecl constDecl = (ConstDecl) decl; + + LitExpr val = constToExpr.get(constDecl); + if (val == null) { + val = extractLiteral(constDecl); + if (val != null) { + constToExpr.put(constDecl, val); + } + } + + @SuppressWarnings("unchecked") final LitExpr tVal = (LitExpr) val; + return Optional.ofNullable(tVal); + } + + private LitExpr extractLiteral(final ConstDecl decl) { + final String symbol = transformationManager.toSymbol(decl); + final Type type = decl.getType(); + + if (type instanceof FuncType) { + return extractFuncLiteral(symbol, (FuncType) type); + } else if (type instanceof ArrayType) { + return extractArrayLiteral(symbol, (ArrayType) type); + } else if (type instanceof BvType) { + return extractBvConstLiteral(symbol, (BvType) type); + } else { + return extractConstLiteral(symbol, type); + } + } + + private LitExpr extractFuncLiteral(final String symbol, final FuncType type) { + final String term = model.getTerm(symbol); + if (term == null) { + return null; + } else { + return checkNotNull(termTransformer.toFuncLitExpr(term, type, model)); + } + } + + private LitExpr extractArrayLiteral(final String symbol, final ArrayType type) { + final String term = model.getTerm(symbol); + if (term == null) { + return null; + } else { + return checkNotNull(termTransformer.toArrayLitExpr(term, type, model)); + } + } + + private LitExpr extractBvConstLiteral(final String symbol, final BvType type) { + final String term = model.getTerm(symbol); + if (term == null) { + return null; + } else { + return checkNotNull(termTransformer.toBvLitExpr(term, type, model)); + } + } + + private LitExpr extractConstLiteral(final String symbol, final Type type) { + final String term = model.getTerm(symbol); + if (term == null) { + return null; + } else { + return checkNotNull(termTransformer.toLitExpr(term, type, model)); + } + } + + @Override + public Map, LitExpr> toMap() { + getDecls().forEach(this::eval); + return Collections.unmodifiableMap(constToExpr); + } + + private Collection> constDeclsOf(final SmtLibModel model) { + final ImmutableList.Builder> builder = ImmutableList.builder(); + for (final var symbol : model.getDecls()) { + if (symbolTable.definesSymbol(symbol)) { + final ConstDecl constDecl = symbolTable.getConst(symbol); + builder.add(constDecl); + } + } + return builder.build(); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/CheckSatResponse.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/CheckSatResponse.java new file mode 100644 index 0000000000..5ad50589d9 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/CheckSatResponse.java @@ -0,0 +1,46 @@ +package hu.bme.mit.theta.solver.smtlib.solver.parser; + +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolverException; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Check_sat_responseContext; + +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.PS_Sat; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.PS_Unknown; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.PS_Unsat; + +public class CheckSatResponse implements SpecificResponse { + private enum Status { + SAT, UNSAT, UNKNOWN + } + + private final Status status; + + private CheckSatResponse(Status status) { + this.status = status; + } + + public static CheckSatResponse fromContext(final Check_sat_responseContext ctx) { + switch (ctx.value.getType()) { + case PS_Sat: + return new CheckSatResponse(Status.SAT); + case PS_Unsat: + return new CheckSatResponse(Status.UNSAT); + case PS_Unknown: + return new CheckSatResponse(Status.UNKNOWN); + default: + throw new SmtLibSolverException("Invalid interface"); + + } + } + + public boolean isSat() { + return status == Status.SAT; + } + + public boolean isUnsat() { + return status == Status.UNSAT; + } + + public boolean isUnknown() { + return status == Status.UNKNOWN; + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GeneralResponse.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GeneralResponse.java new file mode 100644 index 0000000000..c4383125dc --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GeneralResponse.java @@ -0,0 +1,65 @@ +package hu.bme.mit.theta.solver.smtlib.solver.parser; + +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2BaseVisitor; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.General_response_errorContext; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.General_response_successContext; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.General_response_unsupportedContext; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.ResponseContext; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Specific_success_responseContext; + +public class GeneralResponse { + private final boolean successful; + private final String reason; + private final SpecificResponse specificResponse; + + private GeneralResponse(boolean successful, String reason, SpecificResponse specificResponse) { + this.successful = successful; + this.reason = reason; + this.specificResponse = specificResponse; + } + + public static GeneralResponse fromContext(final ResponseContext ctx) { + return ctx.accept(new SMTLIBv2BaseVisitor<>() { + @Override + public GeneralResponse visitGeneral_response_success(General_response_successContext ctx) { + return new GeneralResponse(true, null, null); + } + + @Override + public GeneralResponse visitGeneral_response_unsupported(General_response_unsupportedContext ctx) { + return new GeneralResponse(false, "Unsupported", null); + } + + @Override + public GeneralResponse visitSpecific_success_response(Specific_success_responseContext ctx) { + return new GeneralResponse(true, null, SpecificResponse.fromContext(ctx)); + } + + @Override + public GeneralResponse visitGeneral_response_error(General_response_errorContext ctx) { + return new GeneralResponse(false, ctx.reason.getText(), null); + } + }); + } + + public boolean isSuccessful() { + return successful && specificResponse == null; + } + + public boolean isError() { + return !successful; + } + + public String getReason() { + return reason; + } + + public boolean isSpecific() { + return successful && specificResponse != null; + } + + @SuppressWarnings("unchecked") + public T asSpecific() { + return (T) specificResponse; + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetModelResponse.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetModelResponse.java new file mode 100644 index 0000000000..e2d7d604f3 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetModelResponse.java @@ -0,0 +1,61 @@ +package hu.bme.mit.theta.solver.smtlib.solver.parser; + +import hu.bme.mit.theta.common.Tuple2; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibModel; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2BaseVisitor; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Model_response_mathsatContext; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.misc.Interval; + +import java.util.Map; +import java.util.stream.Collectors; + +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Get_model_responseContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Model_response_funContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Model_response_fun_recContext; +import static hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Model_response_funs_recContext; + +public class GetModelResponse implements SpecificResponse { + private final SmtLibModel model; + + private GetModelResponse(final Map values) { + model = new SmtLibModel(values); + } + + public static GetModelResponse fromContext(final Get_model_responseContext ctx) { + return new GetModelResponse(ctx.model_response().stream().map(member -> member.accept(new SMTLIBv2BaseVisitor>() { + @Override + public Tuple2 visitModel_response_fun(Model_response_funContext ctx) { + return Tuple2.of(extractString(ctx.function_def().symbol()), extractString(ctx.function_def())); + } + + @Override + public Tuple2 visitModel_response_mathsat(Model_response_mathsatContext ctx) { + final var functionDef = String.format( + "%s () (_ theta_type unknown) %s", + extractString(ctx.symbol()), + extractString(ctx.term()) + ); + return Tuple2.of(extractString(ctx.symbol()), functionDef); + } + + @Override + public Tuple2 visitModel_response_fun_rec(Model_response_fun_recContext ctx) { + throw new UnsupportedOperationException(); + } + + @Override + public Tuple2 visitModel_response_funs_rec(Model_response_funs_recContext ctx) { + throw new UnsupportedOperationException(); + } + })).collect(Collectors.toUnmodifiableMap(Tuple2::get1, Tuple2::get2))); + } + + public SmtLibModel getModel() { + return model; + } + + public static String extractString(final ParserRuleContext ctx) { + return ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex())); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetUnsatCoreResponse.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetUnsatCoreResponse.java new file mode 100644 index 0000000000..fc691de4ec --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/GetUnsatCoreResponse.java @@ -0,0 +1,25 @@ +package hu.bme.mit.theta.solver.smtlib.solver.parser; + +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Get_unsat_core_responseContext; +import org.antlr.v4.runtime.RuleContext; + +import java.util.Collection; +import java.util.stream.Collectors; + +public class GetUnsatCoreResponse implements SpecificResponse { + private final Collection labels; + + private GetUnsatCoreResponse(Collection labels) { + this.labels = labels; + } + + public static GetUnsatCoreResponse fromContext(Get_unsat_core_responseContext ctx) { + return new GetUnsatCoreResponse( + ctx.symbol().stream().map(RuleContext::getText).collect(Collectors.toUnmodifiableSet()) + ); + } + + public Collection getLabels() { + return labels; + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/SpecificResponse.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/SpecificResponse.java new file mode 100644 index 0000000000..997efe55f4 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/SpecificResponse.java @@ -0,0 +1,26 @@ +package hu.bme.mit.theta.solver.smtlib.solver.parser; + +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2BaseVisitor; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser; +import hu.bme.mit.theta.solver.smtlib.dsl.gen.SMTLIBv2Parser.Specific_success_responseContext; + +public interface SpecificResponse { + static SpecificResponse fromContext(final Specific_success_responseContext ctx) { + return ctx.accept(new SMTLIBv2BaseVisitor<>() { + @Override + public SpecificResponse visitCheck_sat_response(SMTLIBv2Parser.Check_sat_responseContext ctx) { + return CheckSatResponse.fromContext(ctx); + } + + @Override + public SpecificResponse visitGet_unsat_core_response(SMTLIBv2Parser.Get_unsat_core_responseContext ctx) { + return GetUnsatCoreResponse.fromContext(ctx); + } + + @Override + public SpecificResponse visitGet_model_response(SMTLIBv2Parser.Get_model_responseContext ctx) { + return GetModelResponse.fromContext(ctx); + } + }); + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/ThrowExceptionErrorListener.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/ThrowExceptionErrorListener.java new file mode 100644 index 0000000000..69b5de7641 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/parser/ThrowExceptionErrorListener.java @@ -0,0 +1,14 @@ +package hu.bme.mit.theta.solver.smtlib.solver.parser; + +import hu.bme.mit.theta.solver.smtlib.solver.SmtLibSolverException; +import org.antlr.v4.runtime.ANTLRErrorListener; +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; + +public class ThrowExceptionErrorListener extends BaseErrorListener implements ANTLRErrorListener { + @Override + public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { + throw new SmtLibSolverException(String.format("Invalid Expression: %s", msg), e); + } +} \ No newline at end of file diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibDeclTransformer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibDeclTransformer.java new file mode 100644 index 0000000000..b6bc85ffce --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibDeclTransformer.java @@ -0,0 +1,9 @@ +package hu.bme.mit.theta.solver.smtlib.solver.transformer; + +import hu.bme.mit.theta.core.decl.Decl; + +public interface SmtLibDeclTransformer { + String toSymbol(Decl decl); + + String toDeclaration(Decl decl); +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibExprTransformer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibExprTransformer.java new file mode 100644 index 0000000000..2ac8b5cbbc --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibExprTransformer.java @@ -0,0 +1,7 @@ +package hu.bme.mit.theta.solver.smtlib.solver.transformer; + +import hu.bme.mit.theta.core.type.Expr; + +public interface SmtLibExprTransformer { + String toTerm(Expr expr); +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibSymbolTable.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibSymbolTable.java new file mode 100644 index 0000000000..5095bd13f4 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibSymbolTable.java @@ -0,0 +1,17 @@ +package hu.bme.mit.theta.solver.smtlib.solver.transformer; + +import hu.bme.mit.theta.core.decl.ConstDecl; + +public interface SmtLibSymbolTable { + boolean definesConst(ConstDecl constDecl); + + boolean definesSymbol(String symbol); + + String getSymbol(ConstDecl constDecl); + + String getDeclaration(ConstDecl constDecl); + + ConstDecl getConst(String symbol); + + void put(ConstDecl constDecl, String symbol, String declaration); +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibTermTransformer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibTermTransformer.java new file mode 100644 index 0000000000..faa52597b0 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibTermTransformer.java @@ -0,0 +1,21 @@ +package hu.bme.mit.theta.solver.smtlib.solver.transformer; + +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.LitExpr; +import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.arraytype.ArrayType; +import hu.bme.mit.theta.core.type.bvtype.BvType; +import hu.bme.mit.theta.core.type.functype.FuncType; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibModel; + +public interface SmtLibTermTransformer { +

LitExpr> toFuncLitExpr(String funcLitImpl, FuncType type, SmtLibModel model); + + Expr toExpr(String term, T type, SmtLibModel model); + + LitExpr toLitExpr(String litImpl, T type, SmtLibModel model); + + LitExpr> toArrayLitExpr(String arrayLitImpl, ArrayType type, SmtLibModel model); + + LitExpr toBvLitExpr(String bvLitImpl, BvType type, SmtLibModel model); +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibTransformationManager.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibTransformationManager.java new file mode 100644 index 0000000000..400f991354 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibTransformationManager.java @@ -0,0 +1,13 @@ +package hu.bme.mit.theta.solver.smtlib.solver.transformer; + +import hu.bme.mit.theta.core.decl.Decl; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.Type; + +public interface SmtLibTransformationManager { + String toSort(Type type); + + String toSymbol(Decl decl); + + String toTerm(Expr expr); +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibTypeTransformer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibTypeTransformer.java new file mode 100644 index 0000000000..7e07025adb --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/solver/transformer/SmtLibTypeTransformer.java @@ -0,0 +1,7 @@ +package hu.bme.mit.theta.solver.smtlib.solver.transformer; + +import hu.bme.mit.theta.core.type.Type; + +public interface SmtLibTypeTransformer { + String toSort(Type type); +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/utils/Compress.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/utils/Compress.java new file mode 100644 index 0000000000..6dbff73c27 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/utils/Compress.java @@ -0,0 +1,51 @@ +package hu.bme.mit.theta.solver.smtlib.utils; + +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveInputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; +import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +public class Compress { + private Compress() {} + + public enum CompressionType { + ZIP, TARGZ + } + + public static void extract(final InputStream inputStream, final Path extractDir, final CompressionType compressionType) throws IOException { + switch (compressionType) { + case ZIP: + extract(new ZipArchiveInputStream(inputStream), extractDir); + break; + case TARGZ: + extract(new TarArchiveInputStream(new GzipCompressorInputStream(new BufferedInputStream(inputStream))), extractDir); + break; + default: + throw new AssertionError(); + } + } + + private static void extract(final ArchiveInputStream archiveInputStream, final Path extractDir) throws IOException { + for(ArchiveEntry entry = archiveInputStream.getNextEntry(); entry != null; entry = archiveInputStream.getNextEntry()) { + final var entryPath = Path.of(entry.getName()); + if(entry.isDirectory()) { + if(entryPath.getNameCount() > 1) { + final var entryResolvedPath = extractDir.resolve(entryPath.subpath(1, entryPath.getNameCount())); + Files.createDirectories(entryResolvedPath); + } + } + else { + final var entryResolvedPath = extractDir.resolve(entryPath.subpath(1, entryPath.getNameCount())); + Files.createDirectories(entryResolvedPath.getParent()); + Files.copy(archiveInputStream, entryResolvedPath); + } + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/utils/SemVer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/utils/SemVer.java new file mode 100644 index 0000000000..7b15a060d3 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/utils/SemVer.java @@ -0,0 +1,142 @@ +package hu.bme.mit.theta.solver.smtlib.utils; + +import hu.bme.mit.theta.common.OsHelper; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +public class SemVer implements Comparable { + private final int[] version; + + private SemVer(final String version) { + checkNotNull(version); + checkArgument(version.matches("[0-9]+(\\.[0-9]+)*")); + + this.version = Arrays.stream(version.split("\\.")).mapToInt(Integer::parseInt).toArray(); + } + + public static SemVer of(final String version) { + return new SemVer(version); + } + + public boolean hasMajor() { + return version.length > 0; + } + + public int getMajor() { + return version[0]; + } + + public boolean hasMinor() { + return version.length > 1; + } + + public int getMinor() { + return version[1]; + } + + public boolean hasPatch() { + return version.length > 2; + } + + public int getPatch() { + return version[2]; + } + + public int[] getAll() { + return version; + } + + @Override + public int compareTo(final SemVer that) { + if(that == null) { + return 1; + } + + for(int i = 0; i < Math.max(this.version.length, that.version.length); i++) { + final var thisVer = i < this.version.length ? this.version[i] : 0; + final var thatVer = i < that.version.length ? that.version[i] : 0; + + if(thisVer < thatVer) { + return -1; + } + else if(thisVer > thatVer) { + return 1; + } + else { + continue; + } + } + + return 0; + } + + @Override + public boolean equals(Object obj) { + if(obj == null) { + return false; + } + else if(obj instanceof SemVer) { + return this.compareTo((SemVer) obj) == 0; + } + else { + return false; + } + } + + public static class VersionDecoder { + private final SemVer version; + private final Map> string; + + private VersionDecoder(final SemVer version, final Map> string) { + this.version = version; + this.string = string; + } + + public static VersionDecoder.Builder create(final SemVer version) { + return new VersionDecoder.Builder(version); + } + + public SemVer getVersion() { + return version; + } + + public String getOsArchString(final OsHelper.OperatingSystem os, final OsHelper.Architecture arch) { + if(!string.containsKey(os)) { + throw new UnsupportedOperationException(String.format("Operating system %s is not supported by z3", os)); + } + else if(!string.get(os).containsKey(arch)) { + throw new UnsupportedOperationException(String.format("Architecture %s on operating system %s is not supported by z3", arch, os)); + } + else { + return string.get(os).get(arch); + } + } + + public static class Builder { + private final SemVer version; + private final Map> string; + + private Builder(final SemVer version) { + this.version = version; + this.string = new HashMap<>(); + } + + public VersionDecoder.Builder addString(final OsHelper.OperatingSystem os, final OsHelper.Architecture arch, final String string) { + if(!this.string.containsKey(os)) { + this.string.put(os, new HashMap<>()); + } + this.string.get(os).put(arch, string); + return this; + } + + public VersionDecoder build() { + return new VersionDecoder(version, string); + } + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibItpSolverTest.java b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibItpSolverTest.java new file mode 100644 index 0000000000..f8351c3899 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibItpSolverTest.java @@ -0,0 +1,287 @@ +/* + * Copyright 2017 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.solver.smtlib; + +import com.google.common.collect.ImmutableList; +import hu.bme.mit.theta.common.OsHelper; +import hu.bme.mit.theta.common.logging.NullLogger; +import hu.bme.mit.theta.core.decl.ConstDecl; +import hu.bme.mit.theta.core.decl.ParamDecl; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.core.type.functype.FuncType; +import hu.bme.mit.theta.core.type.inttype.IntType; +import hu.bme.mit.theta.core.utils.ExprUtils; +import hu.bme.mit.theta.solver.Interpolant; +import hu.bme.mit.theta.solver.ItpMarker; +import hu.bme.mit.theta.solver.ItpPattern; +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Path; + +import static hu.bme.mit.theta.core.decl.Decls.Const; +import static hu.bme.mit.theta.core.decl.Decls.Param; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Forall; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Imply; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Not; +import static hu.bme.mit.theta.core.type.functype.FuncExprs.App; +import static hu.bme.mit.theta.core.type.functype.FuncExprs.Func; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Add; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Eq; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Mul; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Neq; +import static hu.bme.mit.theta.solver.ItpMarkerTree.Leaf; +import static hu.bme.mit.theta.solver.ItpMarkerTree.Subtree; +import static hu.bme.mit.theta.solver.ItpMarkerTree.Tree; + +public final class SmtLibItpSolverTest { + private static boolean solverInstalled = false; + private static SmtLibSolverManager solverManager; + private static SolverFactory solverFactory; + + @BeforeClass + public static void init() throws SmtLibSolverInstallerException, IOException { + if(OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)) { + Path home = SmtLibSolverManager.HOME; + + solverManager = SmtLibSolverManager.create(home, NullLogger.getInstance()); + try { + solverManager.install("z3", "4.5.0", "4.5.0", null, false); + solverInstalled = true; + } catch (SmtLibSolverInstallerException e) { + } + + solverFactory = solverManager.getSolverFactory("z3", "4.5.0"); + } + } + + @AfterClass + public static void destroy() throws SmtLibSolverInstallerException { + if(solverInstalled) solverManager.uninstall("z3", "4.5.0"); + } + + ItpSolver solver; + + Expr a; + Expr b; + Expr c; + Expr d; + Expr e; + Expr> f; + Expr> g; + + @Before + public void initialize() { + Assume.assumeTrue(OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)); + + solver = solverFactory.createItpSolver(); + + final ConstDecl ad = Const("a", Int()); + final ConstDecl bd = Const("b", Int()); + final ConstDecl cd = Const("c", Int()); + final ConstDecl dd = Const("d", Int()); + final ConstDecl ed = Const("e", Int()); + final ConstDecl> fd = Const("f", Func(Int(), Int())); + final ConstDecl> gd = Const("g", Func(Int(), Int())); + + a = ad.getRef(); + b = bd.getRef(); + c = cd.getRef(); + d = dd.getRef(); + e = ed.getRef(); + f = fd.getRef(); + g = gd.getRef(); + } + + @Test + public void testBinaryInterpolation() { + final ItpMarker A = solver.createMarker(); + final ItpMarker B = solver.createMarker(); + final ItpPattern pattern = solver.createBinPattern(A, B); + + solver.add(A, Eq(a, b)); + solver.add(A, Eq(a, c)); + solver.add(B, Eq(b, d)); + solver.add(B, Neq(c, d)); + + solver.check(); + Assert.assertEquals(SolverStatus.UNSAT, solver.getStatus()); + final Interpolant itp = solver.getInterpolant(pattern); + + System.out.println(itp.eval(A)); + System.out.println("----------"); + Assert.assertTrue(ExprUtils.getVars(itp.eval(A)).size() <= 3); + } + + @Test + public void testSequenceInterpolation() { + final ItpMarker I1 = solver.createMarker(); + final ItpMarker I2 = solver.createMarker(); + final ItpMarker I3 = solver.createMarker(); + final ItpMarker I4 = solver.createMarker(); + final ItpMarker I5 = solver.createMarker(); + final ItpPattern pattern = solver.createSeqPattern(ImmutableList.of(I1, I2, I3, I4, I5)); + + solver.add(I1, Eq(a, Int(0))); + solver.add(I2, Eq(a, b)); + solver.add(I3, Eq(c, d)); + solver.add(I4, Eq(d, Int(1))); + solver.add(I5, Eq(b, c)); + + solver.check(); + Assert.assertEquals(SolverStatus.UNSAT, solver.getStatus()); + final Interpolant itp = solver.getInterpolant(pattern); + + System.out.println(itp.eval(I1)); + System.out.println(itp.eval(I2)); + System.out.println(itp.eval(I3)); + System.out.println(itp.eval(I4)); + System.out.println(itp.eval(I5)); + System.out.println("----------"); + } + + @Test + public void testTreeInterpolation() { + final ItpMarker I1 = solver.createMarker(); + final ItpMarker I2 = solver.createMarker(); + final ItpMarker I3 = solver.createMarker(); + final ItpMarker I4 = solver.createMarker(); + final ItpMarker I5 = solver.createMarker(); + final ItpPattern pattern = solver.createTreePattern(Tree(I3, Subtree(I1, Leaf(I4), Leaf(I5)), Leaf(I2))); + + solver.add(I1, Eq(a, Int(0))); + solver.add(I2, Eq(a, b)); + solver.add(I3, Eq(c, d)); + solver.add(I4, Eq(d, Int(1))); + solver.add(I5, Eq(b, c)); + + solver.check(); + Assert.assertEquals(SolverStatus.UNSAT, solver.getStatus()); + final Interpolant itp = solver.getInterpolant(pattern); + + System.out.println(itp.eval(I1)); + System.out.println(itp.eval(I2)); + System.out.println(itp.eval(I3)); + System.out.println(itp.eval(I4)); + System.out.println(itp.eval(I5)); + System.out.println("----------"); + } + + @Test + public void testEUF() { + final ItpMarker A = solver.createMarker(); + final ItpMarker B = solver.createMarker(); + final ItpPattern pattern = solver.createBinPattern(A, B); + + solver.add(A, Eq(App(f, a), c)); + solver.add(A, Eq(App(f, b), d)); + solver.add(B, Eq(a, b)); + solver.add(B, Neq(App(g, c), App(g, d))); + + solver.check(); + Assert.assertEquals(SolverStatus.UNSAT, solver.getStatus()); + final Interpolant itp = solver.getInterpolant(pattern); + + System.out.println(itp.eval(A)); + System.out.println("----------"); + } + + @Test + public void testLIA() { + final ItpMarker A = solver.createMarker(); + final ItpMarker B = solver.createMarker(); + final ItpPattern pattern = solver.createBinPattern(A, B); + + solver.add(A, Eq(b, Mul(ImmutableList.of(Int(2), a)))); + solver.add(B, Eq(b, Add(ImmutableList.of(Mul(ImmutableList.of(Int(2), c)), Int(1))))); + + solver.check(); + Assert.assertEquals(SolverStatus.UNSAT, solver.getStatus()); + final Interpolant itp = solver.getInterpolant(pattern); + + System.out.println(itp.eval(A)); + System.out.println("----------"); + } + + @Test + public void testQuantifiers() { + final ItpMarker A = solver.createMarker(); + final ItpMarker B = solver.createMarker(); + final ItpPattern pattern = solver.createBinPattern(A, B); + + final ConstDecl id = Const("i", Int()); + final ConstDecl> pd = Const("p", Func(Int(), Bool())); + final ConstDecl> qd = Const("q", Func(Int(), Bool())); + final ParamDecl x1d = Param("x", Int()); + final ParamDecl x2d = Param("x", Int()); + + final Expr i = id.getRef(); + final Expr> p = pd.getRef(); + final Expr> q = qd.getRef(); + final Expr x1 = x1d.getRef(); + final Expr x2 = x2d.getRef(); + + solver.add(A, Forall(ImmutableList.of(x1d), Imply(App(q, x1), App(p, x1)))); + solver.add(A, Forall(ImmutableList.of(x2d), Not(App(p, x2)))); + solver.add(B, App(q, i)); + + solver.check(); + Assert.assertEquals(SolverStatus.UNSAT, solver.getStatus()); + final Interpolant itp = solver.getInterpolant(pattern); + + System.out.println(itp.eval(A)); + System.out.println("----------"); + } + + @Test + public void testPushPop() { + final ItpMarker A = solver.createMarker(); + final ItpMarker B = solver.createMarker(); + final ItpPattern pattern = solver.createBinPattern(A, B); + + solver.add(A, Eq(a, b)); + solver.add(B, Eq(b, c)); + + solver.push(); + + solver.add(A, Neq(a, c)); + solver.check(); + Assert.assertEquals(SolverStatus.UNSAT, solver.getStatus()); + + solver.pop(); + + solver.add(B, Neq(a, c)); + solver.check(); + Assert.assertEquals(SolverStatus.UNSAT, solver.getStatus()); + final Interpolant itp = solver.getInterpolant(pattern); + + System.out.println(itp.eval(A)); + System.out.println("----------"); + } + +} diff --git a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverBVTest.java b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverBVTest.java new file mode 100644 index 0000000000..c1b3125947 --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverBVTest.java @@ -0,0 +1,105 @@ +package hu.bme.mit.theta.solver.smtlib; + +import hu.bme.mit.theta.common.OsHelper; +import hu.bme.mit.theta.common.logging.NullLogger; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.abstracttype.EqExpr; +import hu.bme.mit.theta.core.utils.BvTestUtils; +import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException; +import org.junit.AfterClass; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Collection; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class SmtLibSolverBVTest { + private static final String SOLVER = "z3"; + private static final String VERSION = "4.5.0"; + private static boolean solverInstalled = false; + private static SmtLibSolverManager solverManager; + + @Parameterized.Parameter(0) + public Class exprType; + + @Parameterized.Parameter(1) + public Expr expected; + + @Parameterized.Parameter(2) + public Expr actual; + + @BeforeClass + public static void init() throws SmtLibSolverInstallerException, IOException { + if(OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)) { + Path home = SmtLibSolverManager.HOME; + + solverManager = SmtLibSolverManager.create(home, NullLogger.getInstance()); + try { + solverManager.install(SOLVER, VERSION, VERSION, null, false); + solverInstalled = true; + } catch (SmtLibSolverInstallerException e) { + } + } + } + + @AfterClass + public static void destroy() throws SmtLibSolverInstallerException { + if(solverInstalled) solverManager.uninstall(SOLVER, VERSION); + } + + @Parameters(name = "expr: {0}, expected: {1}, actual: {2}") + public static Collection operations() { + return Stream.concat( + BvTestUtils.BasicOperations().stream(), + Stream.concat( + BvTestUtils.BitvectorOperations().stream(), + BvTestUtils.RelationalOperations().stream() + ) + ).collect(Collectors.toUnmodifiableList()); + } + + @Test + public void testOperationEquals() throws Exception { + Assume.assumeTrue(OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)); + + // Sanity check + assertNotNull(exprType); + assertNotNull(expected); + assertNotNull(actual); + + // Type checks + assertTrue( + "The type of actual is " + actual.getClass().getName() + " instead of " + exprType.getName(), + exprType.isInstance(actual) + ); + assertEquals( + "The type of expected (" + expected.getType() + ") must match the type of actual (" + actual.getType() + ")", + expected.getType(), + actual.getType() + ); + + // Equality check + try(final Solver solver = solverManager.getSolverFactory(SOLVER, VERSION).createSolver()) { + solver.push(); + + solver.add(EqExpr.create2(expected, actual)); + + SolverStatus status = solver.check(); + assertTrue(status.isSat()); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverFPTest.java b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverFPTest.java new file mode 100644 index 0000000000..f6879af48c --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverFPTest.java @@ -0,0 +1,122 @@ +package hu.bme.mit.theta.solver.smtlib; + +import hu.bme.mit.theta.common.OsHelper; +import hu.bme.mit.theta.common.logging.NullLogger; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.abstracttype.EqExpr; +import hu.bme.mit.theta.core.type.fptype.FpLeqExpr; +import hu.bme.mit.theta.core.type.fptype.FpLitExpr; +import hu.bme.mit.theta.core.type.fptype.FpType; +import hu.bme.mit.theta.core.utils.FpTestUtils; +import hu.bme.mit.theta.core.utils.FpUtils; +import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException; +import org.junit.AfterClass; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.kframework.mpfr.BigFloat; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Collection; +import java.util.stream.Collectors; + +import static hu.bme.mit.theta.core.type.fptype.FpExprs.Abs; +import static hu.bme.mit.theta.core.type.fptype.FpExprs.IsNan; +import static hu.bme.mit.theta.core.type.fptype.FpExprs.Leq; +import static hu.bme.mit.theta.core.type.fptype.FpExprs.Sub; +import static hu.bme.mit.theta.core.type.fptype.FpRoundingMode.RNE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class SmtLibSolverFPTest { + private static boolean solverInstalled = false; + private static SmtLibSolverManager solverManager; + + @Parameterized.Parameter(0) + public Class exprType; + + @Parameterized.Parameter(1) + public Expr expected; + + @Parameterized.Parameter(2) + public Expr actual; + + @BeforeClass + public static void init() throws SmtLibSolverInstallerException, IOException { + if(OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)) { + Path home = SmtLibSolverManager.HOME; + + solverManager = SmtLibSolverManager.create(home, NullLogger.getInstance()); + try { + solverManager.install("z3", "4.5.0", "4.5.0", null, false); + solverInstalled = true; + } catch (SmtLibSolverInstallerException e) { + } + } + } + + @AfterClass + public static void destroy() throws SmtLibSolverInstallerException { + if(solverInstalled) solverManager.uninstall("z3", "4.5.0"); + } + + @Parameters(name = "expr: {0}, expected: {1}, actual: {2}") + public static Collection operations() { + return FpTestUtils.GetOperations().collect(Collectors.toUnmodifiableList()); + } + + @Test + public void testOperationEquals() throws Exception { + Assume.assumeTrue(OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)); + + // Sanity check + assertNotNull(exprType); + assertNotNull(expected); + assertNotNull(actual); + + // Type checks + assertTrue( + "The type of actual is " + actual.getClass().getName() + " instead of " + exprType.getName(), + exprType.isInstance(actual) + ); + assertEquals( + "The type of expected (" + expected.getType() + ") must match the type of actual (" + actual.getType() + ")", + expected.getType(), + actual.getType() + ); + + // Equality check + try(final Solver solver = solverManager.getSolverFactory("z3", "latest").createSolver()) { + solver.push(); + + if (expected instanceof FpLitExpr && actual.getType() instanceof FpType) { + if (((FpLitExpr) expected).isNaN()) { + //noinspection unchecked + solver.add(IsNan((Expr) actual)); + } else if (((FpLitExpr) expected).isNegativeInfinity()) { + solver.add(EqExpr.create2(expected, actual)); + } else if (((FpLitExpr) expected).isPositiveInfinity()) { + solver.add(EqExpr.create2(expected, actual)); + } else { + //noinspection unchecked + FpLeqExpr leq = Leq(Abs(Sub(RNE, (FpLitExpr) expected, (Expr) actual)), + FpUtils.bigFloatToFpLitExpr(new BigFloat("1e-2", FpUtils.getMathContext((FpType) actual.getType(), RNE)), (FpType) actual.getType())); + solver.add(leq); + } + } else { + solver.add(EqExpr.create2(expected, actual)); + } + + SolverStatus status = solver.check(); + assertTrue(status.isSat()); + } + } +} diff --git a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverTest.java b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverTest.java new file mode 100644 index 0000000000..ecac1d4e4e --- /dev/null +++ b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/SmtLibSolverTest.java @@ -0,0 +1,500 @@ +package hu.bme.mit.theta.solver.smtlib; + +import hu.bme.mit.theta.common.OsHelper; +import hu.bme.mit.theta.common.logging.NullLogger; +import hu.bme.mit.theta.core.decl.ConstDecl; +import hu.bme.mit.theta.core.decl.ParamDecl; +import hu.bme.mit.theta.core.model.ImmutableValuation; +import hu.bme.mit.theta.core.model.Valuation; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.LitExpr; +import hu.bme.mit.theta.core.type.arraytype.ArrayExprs; +import hu.bme.mit.theta.core.type.arraytype.ArrayLitExpr; +import hu.bme.mit.theta.core.type.arraytype.ArrayType; +import hu.bme.mit.theta.core.type.booltype.BoolExprs; +import hu.bme.mit.theta.core.type.booltype.BoolType; +import hu.bme.mit.theta.core.type.booltype.ForallExpr; +import hu.bme.mit.theta.core.type.bvtype.BvExprs; +import hu.bme.mit.theta.core.type.bvtype.BvType; +import hu.bme.mit.theta.core.type.functype.FuncType; +import hu.bme.mit.theta.core.type.inttype.IntExprs; +import hu.bme.mit.theta.core.type.inttype.IntType; +import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.UCSolver; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSymbolTable; +import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTermTransformer; +import hu.bme.mit.theta.solver.smtlib.solver.installer.SmtLibSolverInstallerException; +import hu.bme.mit.theta.solver.smtlib.solver.model.SmtLibModel; +import org.junit.AfterClass; +import org.junit.Assume; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static com.google.common.collect.ImmutableList.of; +import static hu.bme.mit.theta.core.decl.Decls.Const; +import static hu.bme.mit.theta.core.decl.Decls.Param; +import static hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Array; +import static hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Read; +import static hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Write; +import static hu.bme.mit.theta.core.type.bvtype.BvExprs.Bv; +import static hu.bme.mit.theta.core.type.functype.FuncExprs.App; +import static hu.bme.mit.theta.core.type.functype.FuncExprs.Func; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public final class SmtLibSolverTest { + private static boolean solverInstalled = false; + private static SmtLibSolverManager solverManager; + private static SolverFactory solverFactory; + + @BeforeClass + public static void init() throws SmtLibSolverInstallerException, IOException { + if(OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)) { + Path home = SmtLibSolverManager.HOME; + + solverManager = SmtLibSolverManager.create(home, NullLogger.getInstance()); + try { + solverManager.install("z3", "4.5.0", "4.5.0", null, false); + solverInstalled = true; + } catch (SmtLibSolverInstallerException e) { + } + + solverFactory = solverManager.getSolverFactory("z3", "4.5.0"); + } + } + + @AfterClass + public static void destroy() throws SmtLibSolverInstallerException { + if(solverInstalled) solverManager.uninstall("z3", "4.5.0"); + } + + @Before + public void before() { + Assume.assumeTrue(OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)); + } + + @Test + public void test() { + final var symbolTable = new GenericSmtLibSymbolTable(); + final var termTransformer = new GenericSmtLibTermTransformer(symbolTable); + + final var x = Const("x", BvExprs.BvType(4)); + symbolTable.put(x, "x", "(declare-fun x () (_ BitVec 4))"); + + final var expr = termTransformer.toExpr( + "(forall ((y (Array (_ BitVec 4) Int))) (= (select y x) 0))", + BoolExprs.Bool(), new SmtLibModel(Map.of()) + ); + assertNotNull(expr); + assertTrue(expr instanceof ForallExpr); + assertEquals(Array(x.getType(), IntExprs.Int()), ((ForallExpr) expr).getParamDecls().get(0).getType()); + } + + @Test + public void testSimple() { + final Solver solver = solverFactory.createSolver(); + + // Create two integer constants x and y + final ConstDecl cx = Const("x", IntExprs.Int()); + final ConstDecl cy = Const("y", IntExprs.Int()); + + // Add x == y + 1 to the solver + solver.add(IntExprs.Eq(cx.getRef(), IntExprs.Add(cy.getRef(), IntExprs.Int(1)))); + + // Check, the expression should be satisfiable + SolverStatus status = solver.check(); + assertTrue(status.isSat()); + + // Add x < y to the solver + solver.add(IntExprs.Lt(cx.getRef(), cy.getRef())); + + // Check, the expression should be unsatisfiable + status = solver.check(); + assertTrue(status.isUnsat()); + } + + @Test + public void testTrack() { + final UCSolver solver = solverFactory.createUCSolver(); + + final ConstDecl ca = Const("a", BoolExprs.Bool()); + final Expr expr = BoolExprs.And(ca.getRef(), BoolExprs.True()); + + solver.push(); + solver.track(expr); + + final SolverStatus status = solver.check(); + + assertTrue(status.isSat()); + + solver.pop(); + } + + @Test + public void testUnsatCore() { + final UCSolver solver = solverFactory.createUCSolver(); + + final ConstDecl x = Const("x", IntExprs.Int()); + final ConstDecl y = Const("y", IntExprs.Int()); + final Expr expr1 = IntExprs.Eq(x.getRef(), IntExprs.Int(0)); + final Expr expr2 = IntExprs.Eq(x.getRef(), IntExprs.Int(1)); + final Expr expr3 = IntExprs.Eq(y.getRef(), IntExprs.Int(1)); + + solver.track(expr1); + solver.track(expr2); + solver.track(expr3); + + final SolverStatus status = solver.check(); + assertTrue(status.isUnsat()); + + final var uc = solver.getUnsatCore(); + assertTrue(uc.contains(expr1)); + assertTrue(uc.contains(expr2)); + assertEquals(2, uc.size()); + } + + @Test + public void testModel() { + final UCSolver solver = solverFactory.createUCSolver(); + + final ConstDecl x = Const("x", IntExprs.Int()); + final ConstDecl y = Const("y", IntExprs.Int()); + final ConstDecl z = Const("z", IntExprs.Int()); + final ConstDecl u = Const("u", BoolExprs.Bool()); + final Expr expr1 = IntExprs.Eq(x.getRef(), IntExprs.Int(2)); + final Expr expr2 = IntExprs.Eq(y.getRef(), IntExprs.Int(3)); + final Expr expr3 = IntExprs.Eq(z.getRef(), IntExprs.Add(x.getRef(), y.getRef())); + final Expr expr4 = BoolExprs.Iff(u.getRef(), BoolExprs.True()); + + solver.track(expr1); + solver.track(expr2); + solver.track(expr3); + solver.track(expr4); + + final SolverStatus status = solver.check(); + assertTrue(status.isSat()); + + final var model = solver.getModel(); + assertEquals(IntExprs.Int(2), model.eval(x).orElseThrow()); + assertEquals(IntExprs.Int(3), model.eval(y).orElseThrow()); + assertEquals(IntExprs.Int(5), model.eval(z).orElseThrow()); + assertEquals(BoolExprs.Bool(true), model.eval(u).orElseThrow()); + } + + @Test + public void testFunc() { + // Arrange + final Solver solver = solverFactory.createSolver(); + final ConstDecl> ca = Const("a", Func(IntExprs.Int(), IntExprs.Int())); + final Expr> a = ca.getRef(); + final ParamDecl px = Param("x", IntExprs.Int()); + final Expr x = px.getRef(); + + solver.add(BoolExprs.Forall(of(px), BoolExprs.Imply(IntExprs.Leq(x, IntExprs.Int(0)), IntExprs.Eq(App(a, x), IntExprs.Int(0))))); + solver.add(BoolExprs.Forall(of(px), BoolExprs.Imply(IntExprs.Geq(x, IntExprs.Int(1)), IntExprs.Eq(App(a, x), IntExprs.Int(1))))); + + // Act + final SolverStatus status = solver.check(); + assertTrue(status.isSat()); + final Valuation model = solver.getModel(); + final Optional>> optVal = model.eval(ca); + final Expr> val = optVal.get(); + + // Assert + assertEquals(ca.getType(), val.getType()); + } + + @Test + public void testArray() { + final Solver solver = solverFactory.createSolver(); + + final ConstDecl> arr = Const("arr", Array(IntExprs.Int(), IntExprs.Int())); + + solver.add(ArrayExprs.Eq(Write(arr.getRef(), IntExprs.Int(0), IntExprs.Int(1)), arr.getRef())); + solver.add(ArrayExprs.Eq(Write(arr.getRef(), IntExprs.Int(1), IntExprs.Int(2)), arr.getRef())); + + // Check, the expression should be satisfiable + SolverStatus status = solver.check(); + assertTrue(status.isSat()); + + Valuation valuation = solver.getModel(); + final Optional>> optVal = valuation.eval(arr); + final Expr> val = optVal.get(); + assertTrue(val instanceof ArrayLitExpr); + var valLit = (ArrayLitExpr)val; + assertTrue(2 <= valLit.getElements().size()); + assertEquals(IntExprs.Int(1), Read(valLit, IntExprs.Int(0)).eval(ImmutableValuation.empty())); + assertEquals(IntExprs.Int(2), Read(valLit, IntExprs.Int(1)).eval(ImmutableValuation.empty())); + } + + @Test + public void testBV1() { + final Solver solver = solverFactory.createSolver(); + + final ConstDecl cx = Const("x", BvExprs.BvType(4)); + final ConstDecl cy = Const("y", BvExprs.BvType(4)); + + solver.push(); + + solver.add(BvExprs.Eq(cx.getRef(), Bv(new boolean[]{false, false, true, false}))); + solver.add(BvExprs.Eq(cy.getRef(), Bv(new boolean[]{false, false, true, false}))); + solver.add(BvExprs.Eq(cx.getRef(), cy.getRef())); + + SolverStatus status = solver.check(); + assertTrue(status.isSat()); + + solver.pop(); + } + + @Test + public void testBV2() { + final Solver solver = solverFactory.createSolver(); + + final ConstDecl cx = Const("x", BvExprs.BvType(4)); + final ConstDecl cz = Const("z", BvExprs.BvType(4)); + + solver.push(); + + solver.add(BvExprs.Eq(cx.getRef(), Bv(new boolean[]{false, false, false, false}))); + solver.add(BvExprs.Neq(cx.getRef(), cz.getRef())); + + SolverStatus status = solver.check(); + assertTrue(status.isSat()); + + Valuation model = solver.getModel(); + assertNotNull(model); + assertNotNull(model.toMap()); + + solver.pop(); + } + + @Test + public void testBV3() { + final Solver solver = solverFactory.createSolver(); + + final ConstDecl cx = Const("x", BvExprs.BvType(4)); + final ConstDecl cy = Const("y", BvExprs.BvType(4)); + + solver.push(); + + solver.add(BvExprs.Eq(cx.getRef(), Bv(new boolean[] {false, false, false, false}))); + solver.add(BvExprs.Eq(cy.getRef(), BvExprs.Add(List.of(cx.getRef(), Bv(new boolean[] {false, false, false, true}))))); + + SolverStatus status = solver.check(); + assertTrue(status.isSat()); + + Valuation model = solver.getModel(); + assertNotNull(model); + assertNotNull(model.toMap()); + + solver.pop(); + } + + @Test + public void testBV4() { + final Solver solver = solverFactory.createSolver(); + + final ConstDecl cx = Const("x", BvExprs.BvType(4)); + final ConstDecl cy = Const("y", BvExprs.BvType(4)); + + solver.push(); + + solver.add(BvExprs.Eq(cx.getRef(), Bv(new boolean[] {false, false, true, false}))); + solver.add(BvExprs.Eq(cy.getRef(), BvExprs.Sub(cx.getRef(), Bv(new boolean[] {false, false, false, true})))); + + SolverStatus status = solver.check(); + assertTrue(status.isSat()); + + Valuation model = solver.getModel(); + assertNotNull(model); + assertNotNull(model.toMap()); + + solver.pop(); + } + + @Test + public void testBV5() { + final Solver solver = solverFactory.createSolver(); + + final ConstDecl cx = Const("x", BvExprs.BvType(4)); + final ConstDecl cy = Const("y", BvExprs.BvType(4)); + + solver.push(); + + solver.add(BvExprs.Eq(cx.getRef(), Bv(new boolean[] {false, false, true, false}))); + solver.add(BvExprs.Eq(cy.getRef(), BvExprs.Neg(cx.getRef()))); + + SolverStatus status = solver.check(); + assertTrue(status.isSat()); + + Valuation model = solver.getModel(); + assertNotNull(model); + assertNotNull(model.toMap()); + + solver.pop(); + } + + @Test + public void testBV6() { + final Solver solver = solverFactory.createSolver(); + + final ConstDecl cx = Const("x", BvExprs.BvType(4)); + final ConstDecl cy = Const("y", BvExprs.BvType(4)); + + solver.push(); + + solver.add(BvExprs.Eq(cx.getRef(), Bv(new boolean[] {false, false, true, false}))); + solver.add(BvExprs.Eq(cy.getRef(), BvExprs.Mul(List.of(cx.getRef(), Bv(new boolean[] {false, false, true, false}))))); + + SolverStatus status = solver.check(); + assertTrue(status.isSat()); + + Valuation model = solver.getModel(); + assertNotNull(model); + assertNotNull(model.toMap()); + + solver.pop(); + } + + @Test + public void testBV7() { + final Solver solver = solverFactory.createSolver(); + + final ConstDecl cx = Const("x", BvExprs.BvType(4)); + final ConstDecl cy = Const("y", BvExprs.BvType(4)); + + solver.push(); + + solver.add(BvExprs.ULt(cx.getRef(), Bv(new boolean[] {true, true, true, true}))); + solver.add(BvExprs.ULt(cy.getRef(), Bv(new boolean[] {true, true, true, true}))); + + SolverStatus status = solver.check(); + assertTrue(status.isSat()); + + Valuation model = solver.getModel(); + assertNotNull(model); + assertNotNull(model.toMap()); + + solver.pop(); + } + + @Test + public void testBV8() { + final Solver solver = solverFactory.createSolver(); + + final ConstDecl cx = Const("x", BvExprs.BvType(4)); + final ConstDecl cy = Const("y", BvExprs.BvType(4)); + + solver.push(); + + solver.add(BvExprs.Eq(cx.getRef(), Bv(new boolean[] {true, false, true, false}))); + solver.add(BvExprs.Eq(cy.getRef(), BvExprs.SMod(cx.getRef(), Bv(new boolean[] {false, true, false, false})))); + + SolverStatus status = solver.check(); + assertTrue(status.isSat()); + + Valuation model = solver.getModel(); + assertNotNull(model); + assertNotNull(model.toMap()); + + solver.pop(); + } + + @Test + public void testBV9() { + final Solver solver = solverFactory.createSolver(); + + final ConstDecl cx = Const("x", BvExprs.BvType(4)); + final ConstDecl cy = Const("y", BvExprs.BvType(4)); + + solver.push(); + + solver.add(BvExprs.Eq(cy.getRef(), Bv(new boolean[] {false, true, false, false}))); + solver.add(BvExprs.Eq(BvExprs.Or(List.of(cx.getRef(), cy.getRef())), Bv(new boolean[] {true, true, false, false}))); + + SolverStatus status = solver.check(); + assertTrue(status.isSat()); + + Valuation model = solver.getModel(); + assertNotNull(model); + assertNotNull(model.toMap()); + + solver.pop(); + } + + @Test + public void testBV10() { + final Solver solver = solverFactory.createSolver(); + + final ConstDecl cx = Const("x", BvExprs.BvType(4)); + final ConstDecl cy = Const("y", BvExprs.BvType(4)); + + solver.push(); + + solver.add(BvExprs.Eq(cy.getRef(), Bv(new boolean[] {false, true, false, false}))); + solver.add(BvExprs.Eq(BvExprs.And(List.of(cx.getRef(), cy.getRef())), Bv(new boolean[] {false, true, false, false}))); + + SolverStatus status = solver.check(); + assertTrue(status.isSat()); + + Valuation model = solver.getModel(); + assertNotNull(model); + assertNotNull(model.toMap()); + + solver.pop(); + } + + @Test + public void testBV11() { + final Solver solver = solverFactory.createSolver(); + + final ConstDecl cx = Const("x", BvExprs.BvType(4)); + final ConstDecl cy = Const("y", BvExprs.BvType(4)); + + solver.push(); + + solver.add(BvExprs.Eq(cy.getRef(), Bv(new boolean[] {false, true, false, false}))); + solver.add(BvExprs.Eq(BvExprs.Xor(List.of(cx.getRef(), cy.getRef())), Bv(new boolean[] {false, true, false, false}))); + + SolverStatus status = solver.check(); + assertTrue(status.isSat()); + + Valuation model = solver.getModel(); + assertNotNull(model); + assertNotNull(model.toMap()); + + solver.pop(); + } + + @Test + public void testBV12() { + final Solver solver = solverFactory.createSolver(); + + final ConstDecl cx = Const("x", BvExprs.BvType(4)); + final ConstDecl cy = Const("y", BvExprs.BvType(4)); + + solver.push(); + + solver.add(BvExprs.Eq(cy.getRef(), Bv(new boolean[] {false, true, false, false}))); + solver.add(BvExprs.Eq(BvExprs.ArithShiftRight(cy.getRef(), Bv(new boolean[] {false, false, false, true})), cx.getRef())); + + SolverStatus status = solver.check(); + assertTrue(status.isSat()); + + Valuation model = solver.getModel(); + assertNotNull(model); + assertNotNull(model.toMap()); + + solver.pop(); + } +} diff --git a/subprojects/common/solver-z3/README.md b/subprojects/solver/solver-z3/README.md similarity index 100% rename from subprojects/common/solver-z3/README.md rename to subprojects/solver/solver-z3/README.md diff --git a/subprojects/solver/solver-z3/bin/.gitignore b/subprojects/solver/solver-z3/bin/.gitignore new file mode 100644 index 0000000000..7eed456bec --- /dev/null +++ b/subprojects/solver/solver-z3/bin/.gitignore @@ -0,0 +1,2 @@ +/main/ +/test/ diff --git a/subprojects/common/solver-z3/build.gradle.kts b/subprojects/solver/solver-z3/build.gradle.kts similarity index 100% rename from subprojects/common/solver-z3/build.gradle.kts rename to subprojects/solver/solver-z3/build.gradle.kts diff --git a/subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3DeclTransformer.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3DeclTransformer.java similarity index 100% rename from subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3DeclTransformer.java rename to subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3DeclTransformer.java diff --git a/subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ExprTransformer.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ExprTransformer.java similarity index 100% rename from subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ExprTransformer.java rename to subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ExprTransformer.java diff --git a/subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Interpolant.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Interpolant.java similarity index 100% rename from subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Interpolant.java rename to subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Interpolant.java diff --git a/subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ItpMarker.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ItpMarker.java similarity index 100% rename from subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ItpMarker.java rename to subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ItpMarker.java diff --git a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ItpPattern.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ItpPattern.java new file mode 100644 index 0000000000..133e4d90f9 --- /dev/null +++ b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ItpPattern.java @@ -0,0 +1,94 @@ +package hu.bme.mit.theta.solver.z3; + +import com.google.common.collect.Lists; +import hu.bme.mit.theta.solver.ItpMarker; +import hu.bme.mit.theta.solver.ItpMarkerTree; +import hu.bme.mit.theta.solver.ItpPattern; + +import java.util.ArrayList; +import java.util.List; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; + +public class Z3ItpPattern implements ItpPattern.Binary, ItpPattern.Sequence, ItpPattern.Tree { + final ItpMarkerTree markerTree; + + Z3ItpPattern(final ItpMarkerTree markerTree) { + this.markerTree = markerTree; + } + + @SuppressWarnings("unchecked") + static Z3ItpPattern of(final ItpMarkerTree markerTree) { + final var list = new ArrayList>(); + list.add(markerTree); + while(!list.isEmpty()) { + final var node = list.get(0); + list.remove(0); + + checkArgument(node.getMarker() instanceof Z3ItpMarker); + + list.addAll(node.getChildren()); + } + + return new Z3ItpPattern((ItpMarkerTree) markerTree); + } + + @Override + public Z3ItpMarker getA() { + checkState(isBinary()); + return markerTree.getChild(0).getMarker(); + } + + @Override + public Z3ItpMarker getB() { + checkState(isBinary()); + return markerTree.getMarker(); + } + + @Override + public List getSequence() { + checkState(isSequence()); + final var markerList = new ArrayList(); + + var current = markerTree; + while(current.getChildrenNumber() > 0) { + markerList.add(current.getMarker()); + current = current.getChild(0); + } + markerList.add(current.getMarker()); + + return Lists.reverse(markerList); + } + + @Override + public ItpMarkerTree getRoot() { + return markerTree; + } + + @Override + public E visit(ItpPatternVisitor visitor) { + return visitor.visitTreePattern(this); + } + + private boolean isBinary() { + return + markerTree != null && + markerTree.getChildrenNumber() == 1 && + markerTree.getChild(0) != null && + markerTree.getChild(0).getChildrenNumber() == 0; + } + + private boolean isSequence() { + var current = markerTree; + while(current.getChildrenNumber() > 0) { + if(current.getChildrenNumber() > 1) { + return false; + } + else { + current = current.getChild(0); + } + } + return true; + } +} diff --git a/subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ItpSolver.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ItpSolver.java similarity index 82% rename from subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ItpSolver.java rename to subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ItpSolver.java index 7e392db51e..ee2f798be3 100644 --- a/subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ItpSolver.java +++ b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3ItpSolver.java @@ -15,29 +15,30 @@ */ package hu.bme.mit.theta.solver.z3; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; - -import java.util.Collection; import hu.bme.mit.theta.common.container.Containers; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - import hu.bme.mit.theta.core.model.Valuation; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.booltype.BoolType; import hu.bme.mit.theta.solver.Interpolant; import hu.bme.mit.theta.solver.ItpMarker; +import hu.bme.mit.theta.solver.ItpMarkerTree; import hu.bme.mit.theta.solver.ItpPattern; import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.SolverStatus; import hu.bme.mit.theta.solver.Stack; -import hu.bme.mit.theta.solver.impl.ItpPatternImpl; import hu.bme.mit.theta.solver.impl.StackImpl; -final class Z3ItpSolver implements ItpSolver { +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +final class Z3ItpSolver implements ItpSolver, Solver { private final Z3TransformationManager transformationManager; private final Z3TermTransformer termTransformer; @@ -63,13 +64,13 @@ public Z3ItpSolver(final Z3SymbolTable symbolTable, final Z3TransformationManage } @Override - public ItpPattern createPattern(final ItpMarker marker) { - checkNotNull(marker); - return new ItpPatternImpl(marker); + public ItpPattern createTreePattern(final ItpMarkerTree root) { + checkNotNull(root); + return Z3ItpPattern.of(root); } @Override - public ItpMarker createMarker() { + public Z3ItpMarker createMarker() { final Z3ItpMarker marker = new Z3ItpMarker(); markers.add(marker); return marker; @@ -89,9 +90,11 @@ public void add(final ItpMarker marker, final Expr assertion) { @Override public Interpolant getInterpolant(final ItpPattern pattern) { checkState(solver.getStatus() == SolverStatus.UNSAT, "Cannot get interpolant if status is not UNSAT."); + checkArgument(pattern instanceof Z3ItpPattern); + final Z3ItpPattern z3ItpPattern = (Z3ItpPattern) pattern; final com.microsoft.z3.Expr proof = z3Solver.getProof(); - final com.microsoft.z3.Expr term = patternToTerm(pattern); + final com.microsoft.z3.Expr term = patternToTerm(z3ItpPattern.getRoot()); final com.microsoft.z3.Params params = z3Context.mkParams(); final com.microsoft.z3.BoolExpr[] itpArray = z3Context.GetInterpolant(proof, term, params); @@ -103,31 +106,31 @@ public Interpolant getInterpolant(final ItpPattern pattern) { } final Map> itpMap = Containers.createMap(); - buildItpMapFormList(pattern, itpList, itpMap); + buildItpMapFormList(z3ItpPattern.getRoot(), itpList, itpMap); return new Z3Interpolant(itpMap); } - private com.microsoft.z3.BoolExpr patternToTerm(final ItpPattern pattern) { + private com.microsoft.z3.BoolExpr patternToTerm(final ItpMarkerTree markerTree) { final Collection opTerms = new LinkedList<>(); - final Z3ItpMarker marker = (Z3ItpMarker) pattern.getMarker(); + final Z3ItpMarker marker = (Z3ItpMarker) markerTree.getMarker(); opTerms.addAll(marker.getTerms()); - for (final ItpPattern child : pattern.getChildren()) { + for (final ItpMarkerTree child : markerTree.getChildren()) { final com.microsoft.z3.BoolExpr childTerm = patternToTerm(child); opTerms.add(childTerm); } final com.microsoft.z3.BoolExpr andTerm = z3Context - .mkAnd(opTerms.toArray(new com.microsoft.z3.BoolExpr[opTerms.size()])); + .mkAnd(opTerms.toArray(new com.microsoft.z3.BoolExpr[opTerms.size()])); final com.microsoft.z3.BoolExpr term = z3Context.MkInterpolant(andTerm); return term; } - private void buildItpMapFormList(final ItpPattern pattern, final List> itpList, + private void buildItpMapFormList(final ItpMarkerTree pattern, final List> itpList, final Map> itpMap) { - for (final ItpPattern child : pattern.getChildren()) { + for (final ItpMarkerTree child : pattern.getChildren()) { buildItpMapFormList(child, itpList, itpMap); } final ItpMarker marker = pattern.getMarker(); @@ -149,12 +152,6 @@ public void add(final Expr assertion) { solver.add(assertion); } - @Override - public void track(final Expr assertion) { - checkNotNull(assertion); - solver.track(assertion); - } - @Override public SolverStatus check() { return solver.check(); @@ -193,14 +190,13 @@ public Valuation getModel() { return solver.getModel(); } - @Override - public Collection> getUnsatCore() { - return solver.getUnsatCore(); - } - @Override public Collection> getAssertions() { return solver.getAssertions(); } -} + @Override + public void close() { + solver.close(); + } +} \ No newline at end of file diff --git a/subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java similarity index 97% rename from subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java rename to subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java index 6a58baccba..37dea628cd 100644 --- a/subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java +++ b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java @@ -44,10 +44,11 @@ import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.SolverStatus; import hu.bme.mit.theta.solver.Stack; +import hu.bme.mit.theta.solver.UCSolver; import hu.bme.mit.theta.solver.UnknownSolverStatusException; import hu.bme.mit.theta.solver.impl.StackImpl; -final class Z3Solver implements Solver { +final class Z3Solver implements UCSolver, Solver { private final Z3SymbolTable symbolTable; private final Z3TransformationManager transformationManager; @@ -225,6 +226,11 @@ private void clearState() { unsatCore = null; } + @Override + public void close() { + z3Context.close(); + } + //// private final class Z3Model extends Valuation { @@ -277,7 +283,7 @@ private LitExpr extractLiteral(final ConstDecl extractArrayLiteral(final FuncDecl funcDecl) { return (LitExpr) expr; } - private LitExpr extractBvConstLiteral(final FuncDecl funcDecl, final BvType type) { + private LitExpr extractBvConstLiteral(final FuncDecl funcDecl) { final com.microsoft.z3.Expr term = z3Model.getConstInterp(funcDecl); if (term == null) { return null; @@ -325,11 +331,12 @@ private Collection> constDeclsOf(final com.microsoft.z3.Model z3Mod if (symbolTable.definesSymbol(symbol)) { final ConstDecl constDecl = symbolTable.getConst(symbol); builder.add(constDecl); - } else { + } + /* else { if (!assumptions.containsKey(symbol.getName().toString())) { // Quantifier? } - } + } */ } return builder.build(); } diff --git a/subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverFactory.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverFactory.java similarity index 81% rename from subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverFactory.java rename to subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverFactory.java index 305ec9af22..26e0e72674 100644 --- a/subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverFactory.java +++ b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverFactory.java @@ -21,6 +21,7 @@ import hu.bme.mit.theta.solver.ItpSolver; import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.UCSolver; public final class Z3SolverFactory implements SolverFactory { @@ -63,6 +64,18 @@ public Solver createSolver() { return new Z3Solver(symbolTable, transformationManager, termTransformer, z3Context, z3Solver); } + @Override + public UCSolver createUCSolver() { + final com.microsoft.z3.Context z3Context = new com.microsoft.z3.Context(); + final com.microsoft.z3.Solver z3Solver = z3Context.mkSimpleSolver(); + + final Z3SymbolTable symbolTable = new Z3SymbolTable(); + final Z3TransformationManager transformationManager = new Z3TransformationManager(symbolTable, z3Context); + final Z3TermTransformer termTransformer = new Z3TermTransformer(symbolTable); + + return new Z3Solver(symbolTable, transformationManager, termTransformer, z3Context, z3Solver); + } + @Override public ItpSolver createItpSolver() { final InterpolationContext z3Context = InterpolationContext.mkContext(); diff --git a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverManager.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverManager.java new file mode 100644 index 0000000000..d891156fda --- /dev/null +++ b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SolverManager.java @@ -0,0 +1,78 @@ +package hu.bme.mit.theta.solver.z3; + +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.SolverBase; +import hu.bme.mit.theta.solver.SolverFactory; +import hu.bme.mit.theta.solver.SolverManager; +import hu.bme.mit.theta.solver.UCSolver; + +import java.util.HashSet; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; + +public final class Z3SolverManager extends SolverManager { + private static final String NAME = "Z3"; + + private boolean closed = false; + private final Set instantiatedSolvers = new HashSet<>(); + + private Z3SolverManager() {} + + public static Z3SolverManager create() { + return new Z3SolverManager(); + } + + @Override + public boolean managesSolver(final String name) { + return NAME.equals(name); + } + + @Override + public SolverFactory getSolverFactory(final String name) { + checkArgument(NAME.equals(name)); + return new ManagedFactory(Z3SolverFactory.getInstance()); + } + + @Override + public synchronized void close() throws Exception { + for(final var solver : instantiatedSolvers) { + solver.close(); + } + closed = true; + } + + private final class ManagedFactory implements SolverFactory { + private final SolverFactory solverFactory; + + private ManagedFactory(final SolverFactory solverFactory) { + this.solverFactory = solverFactory; + } + + @Override + public Solver createSolver() { + checkState(!closed, "Solver manager was closed"); + final var solver = solverFactory.createSolver(); + instantiatedSolvers.add(solver); + return solver; + } + + @Override + public UCSolver createUCSolver() { + checkState(!closed, "Solver manager was closed"); + final var solver = solverFactory.createUCSolver(); + instantiatedSolvers.add(solver); + return solver; + } + + @Override + public ItpSolver createItpSolver() { + checkState(!closed, "Solver manager was closed"); + final var solver = solverFactory.createItpSolver(); + instantiatedSolvers.add(solver); + return solver; + } + } +} diff --git a/subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SymbolTable.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SymbolTable.java similarity index 100% rename from subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SymbolTable.java rename to subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3SymbolTable.java diff --git a/subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TermTransformer.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TermTransformer.java similarity index 100% rename from subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TermTransformer.java rename to subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TermTransformer.java diff --git a/subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TransformationManager.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TransformationManager.java similarity index 100% rename from subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TransformationManager.java rename to subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TransformationManager.java diff --git a/subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TypeTransformer.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TypeTransformer.java similarity index 100% rename from subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TypeTransformer.java rename to subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3TypeTransformer.java diff --git a/subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/package-info.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/package-info.java similarity index 100% rename from subprojects/common/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/package-info.java rename to subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/package-info.java diff --git a/subprojects/common/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/SolverUtilsTest.java b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/SolverUtilsTest.java similarity index 100% rename from subprojects/common/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/SolverUtilsTest.java rename to subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/SolverUtilsTest.java diff --git a/subprojects/common/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3ItpSolverTest.java b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3ItpSolverTest.java similarity index 88% rename from subprojects/common/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3ItpSolverTest.java rename to subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3ItpSolverTest.java index b162ade10e..529eec7f06 100644 --- a/subprojects/common/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3ItpSolverTest.java +++ b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3ItpSolverTest.java @@ -28,6 +28,9 @@ import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Mul; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Neq; +import static hu.bme.mit.theta.solver.ItpMarkerTree.Leaf; +import static hu.bme.mit.theta.solver.ItpMarkerTree.Subtree; +import static hu.bme.mit.theta.solver.ItpMarkerTree.Tree; import hu.bme.mit.theta.core.utils.ExprUtils; import hu.bme.mit.theta.solver.*; @@ -102,13 +105,15 @@ public void testSequenceInterpolation() { final ItpMarker I1 = solver.createMarker(); final ItpMarker I2 = solver.createMarker(); final ItpMarker I3 = solver.createMarker(); - final ItpPattern pattern = solver.createSeqPattern(ImmutableList.of(I1, I2, I3)); + final ItpMarker I4 = solver.createMarker(); + final ItpMarker I5 = solver.createMarker(); + final ItpPattern pattern = solver.createSeqPattern(ImmutableList.of(I1, I2, I3, I4, I5)); - solver.add(I1, Eq(a, b)); - solver.add(I1, Eq(a, c)); - solver.add(I2, Eq(c, d)); - solver.add(I3, Eq(b, e)); - solver.add(I3, Neq(d, e)); + solver.add(I1, Eq(a, Int(0))); + solver.add(I2, Eq(a, b)); + solver.add(I3, Eq(c, d)); + solver.add(I4, Eq(d, Int(1))); + solver.add(I5, Eq(b, c)); solver.check(); Assert.assertEquals(SolverStatus.UNSAT, solver.getStatus()); @@ -116,6 +121,9 @@ public void testSequenceInterpolation() { System.out.println(itp.eval(I1)); System.out.println(itp.eval(I2)); + System.out.println(itp.eval(I3)); + System.out.println(itp.eval(I4)); + System.out.println(itp.eval(I5)); System.out.println("----------"); } @@ -124,15 +132,15 @@ public void testTreeInterpolation() { final ItpMarker I1 = solver.createMarker(); final ItpMarker I2 = solver.createMarker(); final ItpMarker I3 = solver.createMarker(); - final ItpPattern pattern = solver.createPattern(I3); - pattern.createChild(I1); - pattern.createChild(I2); + final ItpMarker I4 = solver.createMarker(); + final ItpMarker I5 = solver.createMarker(); + final ItpPattern pattern = solver.createTreePattern(Tree(I3, Subtree(I1, Leaf(I4), Leaf(I5)), Leaf(I2))); solver.add(I1, Eq(a, Int(0))); - solver.add(I1, Eq(a, b)); - solver.add(I2, Eq(c, d)); - solver.add(I2, Eq(d, Int(1))); - solver.add(I3, Eq(b, c)); + solver.add(I2, Eq(a, b)); + solver.add(I3, Eq(c, d)); + solver.add(I4, Eq(d, Int(1))); + solver.add(I5, Eq(b, c)); solver.check(); Assert.assertEquals(SolverStatus.UNSAT, solver.getStatus()); @@ -140,6 +148,9 @@ public void testTreeInterpolation() { System.out.println(itp.eval(I1)); System.out.println(itp.eval(I2)); + System.out.println(itp.eval(I3)); + System.out.println(itp.eval(I4)); + System.out.println(itp.eval(I5)); System.out.println("----------"); } diff --git a/subprojects/common/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3ModelTest.java b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3ModelTest.java similarity index 100% rename from subprojects/common/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3ModelTest.java rename to subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3ModelTest.java diff --git a/subprojects/common/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3SolverBVTest.java b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3SolverBVTest.java similarity index 100% rename from subprojects/common/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3SolverBVTest.java rename to subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3SolverBVTest.java diff --git a/subprojects/common/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3SolverFPTest.java b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3SolverFPTest.java similarity index 100% rename from subprojects/common/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3SolverFPTest.java rename to subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3SolverFPTest.java diff --git a/subprojects/common/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3SolverTest.java b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3SolverTest.java similarity index 97% rename from subprojects/common/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3SolverTest.java rename to subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3SolverTest.java index c4b6651c86..a5d650d667 100644 --- a/subprojects/common/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3SolverTest.java +++ b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3SolverTest.java @@ -37,6 +37,7 @@ import hu.bme.mit.theta.core.utils.BvUtils; import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.SolverStatus; +import hu.bme.mit.theta.solver.UCSolver; import org.junit.Before; import org.junit.Test; @@ -79,7 +80,7 @@ public void testSimple() { final ConstDecl cy = Const("y", Int()); // Add x == y + 1 to the solver - solver.add(IntExprs.Eq(cx.getRef(), IntExprs.Add(cy.getRef(), Int(1)))); + solver.add(Eq(cx.getRef(), Add(cy.getRef(), Int(1)))); // Check, the expression should be satisfiable SolverStatus status = solver.check(); @@ -95,6 +96,7 @@ public void testSimple() { @Test public void testTrack() { + final UCSolver solver = Z3SolverFactory.getInstance().createUCSolver(); final ConstDecl ca = Const("a", BoolExprs.Bool()); final Expr expr = BoolExprs.And(ca.getRef(), True()); @@ -120,8 +122,8 @@ public void testFunc() { final ParamDecl px = Param("x", Int()); final Expr x = px.getRef(); - solver.add(BoolExprs.Forall(of(px), BoolExprs.Imply(IntExprs.Leq(x, Int(0)), IntExprs.Eq(App(a, x), Int(0))))); - solver.add(BoolExprs.Forall(of(px), BoolExprs.Imply(IntExprs.Geq(x, Int(1)), IntExprs.Eq(App(a, x), Int(1))))); + solver.add(BoolExprs.Forall(of(px), BoolExprs.Imply(IntExprs.Leq(x, Int(0)), Eq(App(a, x), Int(0))))); + solver.add(BoolExprs.Forall(of(px), BoolExprs.Imply(IntExprs.Geq(x, Int(1)), Eq(App(a, x), Int(1))))); // Act final SolverStatus status = solver.check(); @@ -191,7 +193,7 @@ public void testReset() { final ConstDecl cx = Const("x", Int()); final ConstDecl cy = Const("y", Int()); - IntEqExpr eqExpr = IntExprs.Eq(cx.getRef(), IntExprs.Add(cy.getRef(), Int(1))); + IntEqExpr eqExpr = Eq(cx.getRef(), Add(cy.getRef(), Int(1))); solver.add(eqExpr); SolverStatus status = solver.check(); assertTrue(status.isSat()); @@ -453,9 +455,4 @@ public void testBV12() { solver.pop(); } - - private static BvLitExpr uint16ToBvLitExpr(int value) { - return BvUtils.bigIntegerToUnsignedBvLitExpr(BigInteger.valueOf(value), 16); - } - } diff --git a/subprojects/common/solver/README.md b/subprojects/solver/solver/README.md similarity index 100% rename from subprojects/common/solver/README.md rename to subprojects/solver/solver/README.md diff --git a/subprojects/solver/solver/bin/.gitignore b/subprojects/solver/solver/bin/.gitignore new file mode 100644 index 0000000000..7eed456bec --- /dev/null +++ b/subprojects/solver/solver/bin/.gitignore @@ -0,0 +1,2 @@ +/main/ +/test/ diff --git a/subprojects/common/solver/build.gradle.kts b/subprojects/solver/solver/build.gradle.kts similarity index 100% rename from subprojects/common/solver/build.gradle.kts rename to subprojects/solver/solver/build.gradle.kts diff --git a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/Interpolant.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/Interpolant.java similarity index 100% rename from subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/Interpolant.java rename to subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/Interpolant.java diff --git a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/ItpMarker.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ItpMarker.java similarity index 100% rename from subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/ItpMarker.java rename to subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ItpMarker.java diff --git a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ItpMarkerTree.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ItpMarkerTree.java new file mode 100644 index 0000000000..cec9093f15 --- /dev/null +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ItpMarkerTree.java @@ -0,0 +1,44 @@ +package hu.bme.mit.theta.solver; + +import java.util.Arrays; +import java.util.List; + +public final class ItpMarkerTree { + private final T marker; + private final List> children; + + private ItpMarkerTree(final T marker, final List> children) { + this.marker = marker; + this.children = children; + } + + public T getMarker() { + return marker; + } + + public List> getChildren() { + return children; + } + + public ItpMarkerTree getChild(int i) { + return children.get(i); + } + + public int getChildrenNumber() { + return children.size(); + } + + @SafeVarargs + public static ItpMarkerTree Tree(final T marker, final ItpMarkerTree ...subtrees) { + return new ItpMarkerTree<>(marker, Arrays.asList(subtrees)); + } + + @SafeVarargs + public static ItpMarkerTree Subtree(final T marker, final ItpMarkerTree ...subtrees) { + return Tree(marker, subtrees); + } + + public static ItpMarkerTree Leaf(final T marker) { + return Tree(marker); + } +} diff --git a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ItpPattern.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ItpPattern.java new file mode 100644 index 0000000000..044c03a645 --- /dev/null +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ItpPattern.java @@ -0,0 +1,76 @@ +/* + * Copyright 2017 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.solver; + +import java.util.List; + +/** + * Interface for an element of an interpolation pattern. + */ +public interface ItpPattern { + + /** + * ItpPatter visitor function + * @param visitor The visitor + * @param The return type of the visitor + * @return Returns the result of the visitor + */ + E visit(final ItpPatternVisitor visitor); + + /** + * Interface for a binary interpolation pattern + */ + interface Binary extends ItpPattern { + T getA(); + T getB(); + + @Override + default E visit(final ItpPatternVisitor visitor) { + return visitor.visitBinaryPattern(this); + } + } + + /** + * Interface for a sequence interpolation pattern + */ + interface Sequence extends ItpPattern { + List getSequence(); + + @Override + default E visit(final ItpPatternVisitor visitor) { + return visitor.visitSequencePattern(this); + } + } + + /** + * Interface for a tree interpolation pattern + */ + interface Tree extends ItpPattern { + ItpMarkerTree getRoot(); + + @Override + default E visit(final ItpPatternVisitor visitor) { + return visitor.visitTreePattern(this); + } + } + + interface ItpPatternVisitor { + E visitBinaryPattern(final Binary binaryPattern); + E visitSequencePattern(final Sequence sequencePattern); + E visitTreePattern(final Tree treePattern); + } + +} diff --git a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/ItpSolver.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ItpSolver.java similarity index 86% rename from subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/ItpSolver.java rename to subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ItpSolver.java index 460d133e94..21abc34101 100644 --- a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/ItpSolver.java +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/ItpSolver.java @@ -15,17 +15,15 @@ */ package hu.bme.mit.theta.solver; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.BoolType; import java.util.Arrays; import java.util.Collection; import java.util.List; -import com.google.common.collect.Lists; - -import hu.bme.mit.theta.core.type.Expr; -import hu.bme.mit.theta.core.type.booltype.BoolType; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; /** * An extension of the {@link Solver} interface, which also supports interpolation. @@ -35,16 +33,7 @@ * expressions are unsatisfiable, an interpolant can be calculated with * {@link #getInterpolant(ItpPattern)}. */ -public interface ItpSolver extends Solver { - - /** - * Create a pattern for a given marker. - * - * @param marker Marker - * @return Pattern - */ - ItpPattern createPattern(final ItpMarker marker); - +public interface ItpSolver extends SolverBase { /** * Create a binary pattern, which is a sequence of two markers: A and B. * @@ -69,20 +58,26 @@ default ItpPattern createSeqPattern(final List markers) { checkNotNull(markers); checkArgument(!markers.isEmpty()); - ItpPattern result = null; - ItpPattern current = null; + ItpMarkerTree current = null; - for (final ItpMarker marker : Lists.reverse(markers)) { - if (result == null) { - current = createPattern(marker); - result = current; + for (final var marker : markers) { + if (current == null) { + current = ItpMarkerTree.Leaf(marker); } else { - current = current.createChild(marker); + current = ItpMarkerTree.Tree(marker, current); } } - return result; + return createTreePattern(current); } + /** + * Create a tree pattern, in which each node can have multiple children + * + * @param root Root of the marker tree + * @return Tree interpolant pattern + */ + ItpPattern createTreePattern(final ItpMarkerTree root); + /** * Create a new marker. * diff --git a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/Solver.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/Solver.java new file mode 100644 index 0000000000..bfc9496ca6 --- /dev/null +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/Solver.java @@ -0,0 +1,50 @@ +/* + * Copyright 2017 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.solver; + +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.BoolType; + +/** + * Common interface for SMT solvers. + * + * Use the {@link #add(Expr)} methods to add expressions to the solver. + * Then use {@link #check()} method to check their satisfiability. The result can be queried by + * {@link #getStatus()}. If the expressions are satisfiable, a satisfying assignment can be + * obtained by {@link #getModel()}. + * + * The solver can also support incremental solving by {@link #push()} and {@link #pop()}. + */ +public interface Solver extends SolverBase { + + /** + * Add an expression to the solver. + * + * @param assertion Expression to be added + */ + void add(Expr assertion); + + /** + * Add a collection of expressions to the solver. + * + * @param assertions Expressions to be added + */ + default void add(final Iterable> assertions) { + for (final Expr assertion : assertions) { + add(assertion); + } + } +} diff --git a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverBase.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverBase.java new file mode 100644 index 0000000000..0d3326be68 --- /dev/null +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverBase.java @@ -0,0 +1,66 @@ +package hu.bme.mit.theta.solver; + +import hu.bme.mit.theta.core.model.Valuation; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.BoolType; + +import java.util.Collection; + +public interface SolverBase extends AutoCloseable { + + + /** + * Check if the currently added expressions are satisfiable. + * + * @return Status + */ + SolverStatus check(); + + /** + * Push the current solver state. When calling {@link #pop()}, all expressions added after + * the last push will be removed. + */ + void push(); + + /** + * Remove expressions added after the previous n {@link #push()} calls. + * + * @param n + */ + void pop(final int n); + + /** + * Remove expressions added after the previous {@link #push()} call. + */ + default void pop() { + pop(1); + } + + /** + * Reset the solver state. + */ + void reset(); + + /** + * Get the current status of the solver. + * + * @return Status + */ + SolverStatus getStatus(); + + /** + * Get the satisfying assignment for the currently added expressions. + * Should only be called if {@link #check()} was already called and + * the result is SAT. + * + * @return Satisfying assignment + */ + Valuation getModel(); + + /** + * Get the currently added expressions. + * + * @return Expressions + */ + Collection> getAssertions(); +} diff --git a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/SolverFactory.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverFactory.java similarity index 82% rename from subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/SolverFactory.java rename to subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverFactory.java index 0c74e0d3ef..62e0bd62da 100644 --- a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/SolverFactory.java +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverFactory.java @@ -17,6 +17,7 @@ /** * Interface for solver factories that can instantiate solvers. + * Stores a configuration of a solver, and creates instances with that configuration. */ public interface SolverFactory { @@ -26,6 +27,12 @@ public interface SolverFactory { */ Solver createSolver(); + /** + * Create a solver that is capable of producing unsat cores. + * @return Solver instance + */ + UCSolver createUCSolver(); + /** * Create a solver that is capable of interpolation. * @return Solver instance diff --git a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverManager.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverManager.java new file mode 100644 index 0000000000..34a5db8996 --- /dev/null +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverManager.java @@ -0,0 +1,39 @@ +package hu.bme.mit.theta.solver; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Owns the lifecycle of the Solvers created by the SolverFactory instances returned by resolveSolverFactory + */ +public abstract class SolverManager implements AutoCloseable { + private static final Collection solverManagers = new ArrayList<>(); + + public static void registerSolverManager(final SolverManager solverManager) { + solverManagers.add(solverManager); + } + + public static SolverFactory resolveSolverFactory(final String name) throws Exception { + for(final SolverManager solverManager : solverManagers) { + if(solverManager.managesSolver(name)) { + return solverManager.getSolverFactory(name); + } + } + + throw new UnsupportedOperationException("Solver " + name + " not supported"); + } + + /** + * Closes all SolverManager instances registered + */ + public static void closeAll() throws Exception { + for(final var solverManager : solverManagers) { + solverManager.close(); + } + solverManagers.clear(); + } + + public abstract boolean managesSolver(final String name); + public abstract SolverFactory getSolverFactory(final String name) throws Exception; + +} diff --git a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/SolverStatus.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverStatus.java similarity index 100% rename from subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/SolverStatus.java rename to subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverStatus.java diff --git a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/Stack.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/Stack.java similarity index 100% rename from subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/Stack.java rename to subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/Stack.java diff --git a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/UCSolver.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/UCSolver.java new file mode 100644 index 0000000000..68a4b3a690 --- /dev/null +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/UCSolver.java @@ -0,0 +1,48 @@ +package hu.bme.mit.theta.solver; + +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.booltype.BoolType; + +import java.util.Collection; + +/** + * Common interface for SMT solvers with unsat core capabilities. + * + * Use the {@link #track(Expr)} method to add expressions to the solver. + * Then use {@link #check()} method to check their satisfiability. The result can be queried by + * {@link #getStatus()}. If the expressions are satisfiable, a satisfying assignment can be + * obtained by {@link #getModel()}. If the expressions are not satisfiable, use {@link #getUnsatCore()} + * to obtain the unsat core. + * + * The solver can also support incremental solving by {@link #push()} and {@link #pop()}. + */ +public interface UCSolver extends SolverBase { + + /** + * Add and track an expression. Required to calculate unsat cores. + * + * @param assertion Expression to be tracked + */ + void track(Expr assertion); + + /** + * Add and track a collection of expressions. + * + * @param assertions Expressions to be tracked + */ + default void track(final Iterable> assertions) { + for (final Expr assertion : assertions) { + track(assertion); + } + } + + /** + * Get an unsat core, i.e., a (not necessarily) minimal subset of the + * expressions that are already unsatisfiable. It only works if expressions + * were added by {@link #track(Expr)} or {@link #track(Iterable)}. Furthermore, it should only + * be called if {@link #check()} was already called and the result is UNSAT. + * + * @return Unsat core + */ + Collection> getUnsatCore(); +} diff --git a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/UnknownSolverStatusException.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/UnknownSolverStatusException.java similarity index 100% rename from subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/UnknownSolverStatusException.java rename to subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/UnknownSolverStatusException.java diff --git a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/impl/NullSolver.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/impl/NullSolver.java similarity index 92% rename from subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/impl/NullSolver.java rename to subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/impl/NullSolver.java index bd355cb0a5..2b52d5eead 100644 --- a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/impl/NullSolver.java +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/impl/NullSolver.java @@ -41,11 +41,6 @@ public void add(final Expr assertion) { throw new UnsupportedOperationException(); } - @Override - public void track(final Expr assertion) { - throw new UnsupportedOperationException(); - } - @Override public SolverStatus check() { throw new UnsupportedOperationException(); @@ -77,12 +72,12 @@ public Valuation getModel() { } @Override - public Collection> getUnsatCore() { + public Collection> getAssertions() { throw new UnsupportedOperationException(); } @Override - public Collection> getAssertions() { + public void close() throws Exception { throw new UnsupportedOperationException(); } diff --git a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/impl/StackImpl.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/impl/StackImpl.java similarity index 100% rename from subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/impl/StackImpl.java rename to subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/impl/StackImpl.java diff --git a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/package-info.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/package-info.java similarity index 100% rename from subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/package-info.java rename to subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/package-info.java diff --git a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/utils/SolverUtils.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/utils/SolverUtils.java similarity index 100% rename from subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/utils/SolverUtils.java rename to subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/utils/SolverUtils.java diff --git a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/utils/WithPushPop.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/utils/WithPushPop.java similarity index 88% rename from subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/utils/WithPushPop.java rename to subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/utils/WithPushPop.java index c46c183ea1..88d4371038 100644 --- a/subprojects/common/solver/src/main/java/hu/bme/mit/theta/solver/utils/WithPushPop.java +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/utils/WithPushPop.java @@ -15,9 +15,9 @@ */ package hu.bme.mit.theta.solver.utils; -import java.io.Closeable; +import hu.bme.mit.theta.solver.SolverBase; -import hu.bme.mit.theta.solver.Solver; +import java.io.Closeable; /** * A helper class for automatic push and pop for solvers using the @@ -25,9 +25,9 @@ */ public class WithPushPop implements Closeable { - private final Solver solver; + private final SolverBase solver; - public WithPushPop(final Solver solver) { + public WithPushPop(final SolverBase solver) { this.solver = solver; solver.push(); } diff --git a/subprojects/common/solver/src/test/java/hu/bme/mit/theta/solver/SolverStub.java b/subprojects/solver/solver/src/test/java/hu/bme/mit/theta/solver/SolverStub.java similarity index 91% rename from subprojects/common/solver/src/test/java/hu/bme/mit/theta/solver/SolverStub.java rename to subprojects/solver/solver/src/test/java/hu/bme/mit/theta/solver/SolverStub.java index 1ce4403da7..6b1979dba3 100644 --- a/subprojects/common/solver/src/test/java/hu/bme/mit/theta/solver/SolverStub.java +++ b/subprojects/solver/solver/src/test/java/hu/bme/mit/theta/solver/SolverStub.java @@ -31,10 +31,7 @@ public SolverStub() { @Override public void add(final Expr assertion) { - } - - @Override - public void track(final Expr assertion) { + // Stub } @Override @@ -54,6 +51,7 @@ public void pop(final int n) { @Override public void reset() { + // Stub } @Override @@ -67,13 +65,12 @@ public Valuation getModel() { } @Override - public Collection> getUnsatCore() { + public Collection> getAssertions() { return null; } @Override - public Collection> getAssertions() { - return null; + public void close() { + // Nothing to close } - } diff --git a/subprojects/common/solver/src/test/java/hu/bme/mit/theta/solver/StackTest.java b/subprojects/solver/solver/src/test/java/hu/bme/mit/theta/solver/StackTest.java similarity index 100% rename from subprojects/common/solver/src/test/java/hu/bme/mit/theta/solver/StackTest.java rename to subprojects/solver/solver/src/test/java/hu/bme/mit/theta/solver/StackTest.java diff --git a/subprojects/common/solver/src/test/java/hu/bme/mit/theta/solver/WithPushPopTest.java b/subprojects/solver/solver/src/test/java/hu/bme/mit/theta/solver/WithPushPopTest.java similarity index 100% rename from subprojects/common/solver/src/test/java/hu/bme/mit/theta/solver/WithPushPopTest.java rename to subprojects/solver/solver/src/test/java/hu/bme/mit/theta/solver/WithPushPopTest.java diff --git a/subprojects/sts/sts-analysis/src/main/java/hu/bme/mit/theta/sts/analysis/config/StsConfigBuilder.java b/subprojects/sts/sts-analysis/src/main/java/hu/bme/mit/theta/sts/analysis/config/StsConfigBuilder.java index 622f322c96..fd3dbc2b3f 100644 --- a/subprojects/sts/sts-analysis/src/main/java/hu/bme/mit/theta/sts/analysis/config/StsConfigBuilder.java +++ b/subprojects/sts/sts-analysis/src/main/java/hu/bme/mit/theta/sts/analysis/config/StsConfigBuilder.java @@ -15,10 +15,6 @@ */ package hu.bme.mit.theta.sts.analysis.config; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Not; - -import java.util.function.Predicate; - import hu.bme.mit.theta.analysis.Action; import hu.bme.mit.theta.analysis.Analysis; import hu.bme.mit.theta.analysis.LTS; @@ -42,7 +38,16 @@ import hu.bme.mit.theta.analysis.expr.ExprAction; import hu.bme.mit.theta.analysis.expr.ExprState; import hu.bme.mit.theta.analysis.expr.ExprStatePredicate; -import hu.bme.mit.theta.analysis.expr.refinement.*; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceBwBinItpChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceFwBinItpChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceSeqItpChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceUnsatCoreChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ItpRefutation; +import hu.bme.mit.theta.analysis.expr.refinement.JoiningPrecRefiner; +import hu.bme.mit.theta.analysis.expr.refinement.MultiExprTraceRefiner; +import hu.bme.mit.theta.analysis.expr.refinement.PruneStrategy; +import hu.bme.mit.theta.analysis.expr.refinement.SingleExprTraceRefiner; import hu.bme.mit.theta.analysis.pred.ExprSplitters; import hu.bme.mit.theta.analysis.pred.ExprSplitters.ExprSplitter; import hu.bme.mit.theta.analysis.pred.ItpRefToPredPrec; @@ -56,7 +61,7 @@ import hu.bme.mit.theta.common.logging.NullLogger; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.booltype.BoolType; -import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.SolverFactory; import hu.bme.mit.theta.sts.STS; import hu.bme.mit.theta.sts.analysis.StsAction; @@ -65,6 +70,10 @@ import hu.bme.mit.theta.sts.analysis.initprec.StsInitPrec; import hu.bme.mit.theta.sts.analysis.initprec.StsPropInitPrec; +import java.util.function.Predicate; + +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Not; + public final class StsConfigBuilder { public enum Domain { @@ -154,14 +163,14 @@ public StsConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { } public StsConfig build(final STS sts) { - final ItpSolver solver = solverFactory.createItpSolver(); final LTS lts = StsLts.create(sts); final Expr init = sts.getInit(); final Expr negProp = Not(sts.getProp()); if (domain == Domain.EXPL) { - final Predicate target = new ExplStatePredicate(negProp, solver); - final Analysis analysis = ExplAnalysis.create(solver, init); + final Solver analysisSolver = solverFactory.createSolver(); + final Predicate target = new ExplStatePredicate(negProp, analysisSolver); + final Analysis analysis = ExplAnalysis.create(analysisSolver, init); final ArgBuilder argBuilder = ArgBuilder.create(lts, analysis, target, true); final Abstractor abstractor = BasicAbstractor.builder(argBuilder) @@ -174,23 +183,23 @@ public StsConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { switch (refinement) { case FW_BIN_ITP: - refiner = SingleExprTraceRefiner.create(ExprTraceFwBinItpChecker.create(init, negProp, solver), + refiner = SingleExprTraceRefiner.create(ExprTraceFwBinItpChecker.create(init, negProp, solverFactory.createItpSolver()), JoiningPrecRefiner.create(new ItpRefToExplPrec()), pruneStrategy, logger); break; case BW_BIN_ITP: - refiner = SingleExprTraceRefiner.create(ExprTraceBwBinItpChecker.create(init, negProp, solver), + refiner = SingleExprTraceRefiner.create(ExprTraceBwBinItpChecker.create(init, negProp, solverFactory.createItpSolver()), JoiningPrecRefiner.create(new ItpRefToExplPrec()), pruneStrategy, logger); break; case SEQ_ITP: - refiner = SingleExprTraceRefiner.create(ExprTraceSeqItpChecker.create(init, negProp, solver), + refiner = SingleExprTraceRefiner.create(ExprTraceSeqItpChecker.create(init, negProp, solverFactory.createItpSolver()), JoiningPrecRefiner.create(new ItpRefToExplPrec()), pruneStrategy, logger); break; case MULTI_SEQ: - refiner = MultiExprTraceRefiner.create(ExprTraceSeqItpChecker.create(init, negProp, solver), + refiner = MultiExprTraceRefiner.create(ExprTraceSeqItpChecker.create(init, negProp, solverFactory.createItpSolver()), JoiningPrecRefiner.create(new ItpRefToExplPrec()), pruneStrategy, logger); break; case UNSAT_CORE: - refiner = SingleExprTraceRefiner.create(ExprTraceUnsatCoreChecker.create(init, negProp, solver), + refiner = SingleExprTraceRefiner.create(ExprTraceUnsatCoreChecker.create(init, negProp, solverFactory.createUCSolver()), JoiningPrecRefiner.create(new VarsRefToExplPrec()), pruneStrategy, logger); break; default: @@ -204,22 +213,23 @@ public StsConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { return StsConfig.create(checker, prec); } else if (domain == Domain.PRED_BOOL || domain == Domain.PRED_CART || domain == Domain.PRED_SPLIT) { + final Solver analysisSolver = solverFactory.createSolver(); PredAbstractor predAbstractor = null; switch (domain) { case PRED_BOOL: - predAbstractor = PredAbstractors.booleanAbstractor(solver); + predAbstractor = PredAbstractors.booleanAbstractor(analysisSolver); break; case PRED_SPLIT: - predAbstractor = PredAbstractors.booleanSplitAbstractor(solver); + predAbstractor = PredAbstractors.booleanSplitAbstractor(analysisSolver); break; case PRED_CART: - predAbstractor = PredAbstractors.cartesianAbstractor(solver); + predAbstractor = PredAbstractors.cartesianAbstractor(analysisSolver); break; default: throw new UnsupportedOperationException(domain + " domain is not supported."); } - final Predicate target = new ExprStatePredicate(negProp, solver); - final Analysis analysis = PredAnalysis.create(solver, predAbstractor, + final Predicate target = new ExprStatePredicate(negProp, analysisSolver); + final Analysis analysis = PredAnalysis.create(analysisSolver, predAbstractor, init); final ArgBuilder argBuilder = ArgBuilder.create(lts, analysis, target, true); @@ -232,16 +242,16 @@ public StsConfigBuilder pruneStrategy(final PruneStrategy pruneStrategy) { ExprTraceChecker exprTraceChecker = null; switch (refinement) { case FW_BIN_ITP: - exprTraceChecker = ExprTraceFwBinItpChecker.create(init, negProp, solver); + exprTraceChecker = ExprTraceFwBinItpChecker.create(init, negProp, solverFactory.createItpSolver()); break; case BW_BIN_ITP: - exprTraceChecker = ExprTraceBwBinItpChecker.create(init, negProp, solver); + exprTraceChecker = ExprTraceBwBinItpChecker.create(init, negProp, solverFactory.createItpSolver()); break; case SEQ_ITP: - exprTraceChecker = ExprTraceSeqItpChecker.create(init, negProp, solver); + exprTraceChecker = ExprTraceSeqItpChecker.create(init, negProp, solverFactory.createItpSolver()); break; case MULTI_SEQ: - exprTraceChecker = ExprTraceSeqItpChecker.create(init, negProp, solver); + exprTraceChecker = ExprTraceSeqItpChecker.create(init, negProp, solverFactory.createItpSolver()); break; default: throw new UnsupportedOperationException( diff --git a/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsExplTest.java b/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsExplTest.java index de35443f5d..acbc09b188 100644 --- a/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsExplTest.java +++ b/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsExplTest.java @@ -15,25 +15,6 @@ */ package hu.bme.mit.theta.sts.analysis; -import static hu.bme.mit.theta.analysis.algorithm.ArgUtils.isWellLabeled; -import static hu.bme.mit.theta.core.decl.Decls.Var; -import static hu.bme.mit.theta.core.type.anytype.Exprs.Prime; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.And; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Imply; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Not; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Add; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Eq; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Geq; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Lt; -import static org.junit.Assert.assertTrue; - -import java.util.Collections; -import java.util.function.Predicate; - -import hu.bme.mit.theta.analysis.expr.refinement.*; -import org.junit.Test; - import hu.bme.mit.theta.analysis.Analysis; import hu.bme.mit.theta.analysis.LTS; import hu.bme.mit.theta.analysis.State; @@ -52,6 +33,12 @@ import hu.bme.mit.theta.analysis.expr.ExprAction; import hu.bme.mit.theta.analysis.expr.ExprState; import hu.bme.mit.theta.analysis.expr.ExprStatePredicate; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceUnsatCoreChecker; +import hu.bme.mit.theta.analysis.expr.refinement.JoiningPrecRefiner; +import hu.bme.mit.theta.analysis.expr.refinement.PruneStrategy; +import hu.bme.mit.theta.analysis.expr.refinement.SingleExprTraceRefiner; +import hu.bme.mit.theta.analysis.expr.refinement.VarsRefutation; import hu.bme.mit.theta.analysis.waitlist.PriorityWaitlist; import hu.bme.mit.theta.common.logging.ConsoleLogger; import hu.bme.mit.theta.common.logging.Logger; @@ -59,10 +46,28 @@ import hu.bme.mit.theta.core.decl.VarDecl; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.inttype.IntType; -import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.Solver; +import hu.bme.mit.theta.solver.UCSolver; import hu.bme.mit.theta.solver.z3.Z3SolverFactory; import hu.bme.mit.theta.sts.STS; import hu.bme.mit.theta.sts.STS.Builder; +import org.junit.Test; + +import java.util.Collections; +import java.util.function.Predicate; + +import static hu.bme.mit.theta.analysis.algorithm.ArgUtils.isWellLabeled; +import static hu.bme.mit.theta.core.decl.Decls.Var; +import static hu.bme.mit.theta.core.type.anytype.Exprs.Prime; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.And; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Imply; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Not; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Add; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Eq; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Geq; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Lt; +import static org.junit.Assert.assertTrue; public class StsExplTest { @@ -89,10 +94,11 @@ public void test() { final STS sts = builder.build(); - final ItpSolver solver = Z3SolverFactory.getInstance().createItpSolver(); + final Solver abstractionSolver = Z3SolverFactory.getInstance().createSolver(); + final UCSolver refinementSolver = Z3SolverFactory.getInstance().createUCSolver(); - final Analysis analysis = ExplAnalysis.create(solver, sts.getInit()); - final Predicate target = new ExprStatePredicate(Not(sts.getProp()), solver); + final Analysis analysis = ExplAnalysis.create(abstractionSolver, sts.getInit()); + final Predicate target = new ExprStatePredicate(Not(sts.getProp()), abstractionSolver); final ExplPrec prec = ExplPrec.of(Collections.singleton(vy)); @@ -104,7 +110,7 @@ public void test() { .waitlist(PriorityWaitlist.create(ArgNodeComparators.bfs())).logger(logger).build(); final ExprTraceChecker exprTraceChecker = ExprTraceUnsatCoreChecker.create(sts.getInit(), - Not(sts.getProp()), solver); + Not(sts.getProp()), refinementSolver); final SingleExprTraceRefiner refiner = SingleExprTraceRefiner .create(exprTraceChecker, JoiningPrecRefiner.create(new VarsRefToExplPrec()), PruneStrategy.LAZY, logger); @@ -114,7 +120,7 @@ public void test() { final SafetyResult safetyStatus = checker.check(prec); final ARG arg = safetyStatus.getArg(); - assertTrue(isWellLabeled(arg, solver)); + assertTrue(isWellLabeled(arg, abstractionSolver)); // System.out.println(new // GraphvizWriter().writeString(ArgVisualizer.visualize(arg))); diff --git a/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsPredTest.java b/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsPredTest.java index bf338f813c..2712527c1b 100644 --- a/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsPredTest.java +++ b/subprojects/sts/sts-analysis/src/test/java/hu/bme/mit/theta/sts/analysis/StsPredTest.java @@ -31,6 +31,8 @@ import java.util.function.Predicate; import hu.bme.mit.theta.analysis.expr.refinement.*; +import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.Solver; import org.junit.Before; import org.junit.Test; @@ -59,14 +61,14 @@ import hu.bme.mit.theta.core.decl.VarDecl; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.inttype.IntType; -import hu.bme.mit.theta.solver.ItpSolver; import hu.bme.mit.theta.solver.z3.Z3SolverFactory; import hu.bme.mit.theta.sts.STS; import hu.bme.mit.theta.sts.STS.Builder; public class StsPredTest { final Logger logger = new ConsoleLogger(Level.VERBOSE); - final ItpSolver solver = Z3SolverFactory.getInstance().createItpSolver(); + final Solver abstractionSolver = Z3SolverFactory.getInstance().createSolver(); + final ItpSolver refinementSolver = Z3SolverFactory.getInstance().createItpSolver(); STS sts = null; @Before @@ -87,9 +89,9 @@ public void setUp() { @Test public void testPredPrec() { - final Analysis analysis = PredAnalysis.create(solver, - PredAbstractors.booleanSplitAbstractor(solver), sts.getInit()); - final Predicate target = new ExprStatePredicate(Not(sts.getProp()), solver); + final Analysis analysis = PredAnalysis.create(abstractionSolver, + PredAbstractors.booleanSplitAbstractor(abstractionSolver), sts.getInit()); + final Predicate target = new ExprStatePredicate(Not(sts.getProp()), abstractionSolver); final PredPrec prec = PredPrec.of(); @@ -101,7 +103,7 @@ public void testPredPrec() { .build(); final ExprTraceChecker exprTraceChecker = ExprTraceFwBinItpChecker.create(sts.getInit(), - Not(sts.getProp()), solver); + Not(sts.getProp()), refinementSolver); final SingleExprTraceRefiner refiner = SingleExprTraceRefiner .create(exprTraceChecker, JoiningPrecRefiner.create(new ItpRefToPredPrec(ExprSplitters.atoms())), @@ -113,7 +115,7 @@ public void testPredPrec() { System.out.println(safetyStatus); final ARG arg = safetyStatus.getArg(); - assertTrue(isWellLabeled(arg, solver)); + assertTrue(isWellLabeled(arg, abstractionSolver)); // System.out.println(new // GraphvizWriter().writeString(ArgVisualizer.visualize(arg))); diff --git a/subprojects/xsts/xsts-analysis/src/main/java/hu/bme/mit/theta/xsts/analysis/config/XstsConfigBuilder.java b/subprojects/xsts/xsts-analysis/src/main/java/hu/bme/mit/theta/xsts/analysis/config/XstsConfigBuilder.java index 2091f97ed6..8bea2a8fa6 100644 --- a/subprojects/xsts/xsts-analysis/src/main/java/hu/bme/mit/theta/xsts/analysis/config/XstsConfigBuilder.java +++ b/subprojects/xsts/xsts-analysis/src/main/java/hu/bme/mit/theta/xsts/analysis/config/XstsConfigBuilder.java @@ -1,6 +1,10 @@ package hu.bme.mit.theta.xsts.analysis.config; -import hu.bme.mit.theta.analysis.*; +import hu.bme.mit.theta.analysis.Action; +import hu.bme.mit.theta.analysis.Analysis; +import hu.bme.mit.theta.analysis.LTS; +import hu.bme.mit.theta.analysis.Prec; +import hu.bme.mit.theta.analysis.State; import hu.bme.mit.theta.analysis.algorithm.ArgBuilder; import hu.bme.mit.theta.analysis.algorithm.ArgNodeComparators; import hu.bme.mit.theta.analysis.algorithm.SafetyChecker; @@ -9,10 +13,33 @@ import hu.bme.mit.theta.analysis.algorithm.cegar.CegarChecker; import hu.bme.mit.theta.analysis.algorithm.cegar.Refiner; import hu.bme.mit.theta.analysis.algorithm.cegar.abstractor.StopCriterions; -import hu.bme.mit.theta.analysis.expl.*; +import hu.bme.mit.theta.analysis.expl.ExplAnalysis; +import hu.bme.mit.theta.analysis.expl.ExplPrec; +import hu.bme.mit.theta.analysis.expl.ExplState; +import hu.bme.mit.theta.analysis.expl.ExplStatePredicate; +import hu.bme.mit.theta.analysis.expl.ExplStmtAnalysis; +import hu.bme.mit.theta.analysis.expl.ExplStmtOptimizer; +import hu.bme.mit.theta.analysis.expl.ItpRefToExplPrec; +import hu.bme.mit.theta.analysis.expl.VarsRefToExplPrec; import hu.bme.mit.theta.analysis.expr.ExprStatePredicate; -import hu.bme.mit.theta.analysis.expr.refinement.*; -import hu.bme.mit.theta.analysis.pred.*; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceBwBinItpChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceFwBinItpChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceSeqItpChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceUnsatCoreChecker; +import hu.bme.mit.theta.analysis.expr.refinement.ItpRefutation; +import hu.bme.mit.theta.analysis.expr.refinement.JoiningPrecRefiner; +import hu.bme.mit.theta.analysis.expr.refinement.MultiExprTraceRefiner; +import hu.bme.mit.theta.analysis.expr.refinement.PruneStrategy; +import hu.bme.mit.theta.analysis.expr.refinement.RefutationToPrec; +import hu.bme.mit.theta.analysis.expr.refinement.SingleExprTraceRefiner; +import hu.bme.mit.theta.analysis.pred.ExprSplitters; +import hu.bme.mit.theta.analysis.pred.ItpRefToPredPrec; +import hu.bme.mit.theta.analysis.pred.PredAbstractors; +import hu.bme.mit.theta.analysis.pred.PredAnalysis; +import hu.bme.mit.theta.analysis.pred.PredPrec; +import hu.bme.mit.theta.analysis.pred.PredState; +import hu.bme.mit.theta.analysis.pred.PredStmtOptimizer; import hu.bme.mit.theta.analysis.prod2.Prod2Analysis; import hu.bme.mit.theta.analysis.prod2.Prod2Prec; import hu.bme.mit.theta.analysis.prod2.Prod2State; @@ -24,12 +51,24 @@ import hu.bme.mit.theta.core.decl.VarDecl; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.booltype.BoolType; -import hu.bme.mit.theta.solver.ItpSolver; +import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.SolverFactory; import hu.bme.mit.theta.xsts.XSTS; -import hu.bme.mit.theta.xsts.analysis.*; -import hu.bme.mit.theta.xsts.analysis.autoexpl.*; -import hu.bme.mit.theta.xsts.analysis.initprec.*; +import hu.bme.mit.theta.xsts.analysis.XstsAction; +import hu.bme.mit.theta.xsts.analysis.XstsAnalysis; +import hu.bme.mit.theta.xsts.analysis.XstsLts; +import hu.bme.mit.theta.xsts.analysis.XstsState; +import hu.bme.mit.theta.xsts.analysis.XstsStatePredicate; +import hu.bme.mit.theta.xsts.analysis.XstsStmtOptimizer; +import hu.bme.mit.theta.xsts.analysis.autoexpl.XstsAutoExpl; +import hu.bme.mit.theta.xsts.analysis.autoexpl.XstsNewAtomsAutoExpl; +import hu.bme.mit.theta.xsts.analysis.autoexpl.XstsNewOperandsAutoExpl; +import hu.bme.mit.theta.xsts.analysis.autoexpl.XstsStaticAutoExpl; +import hu.bme.mit.theta.xsts.analysis.initprec.XstsAllVarsInitPrec; +import hu.bme.mit.theta.xsts.analysis.initprec.XstsCtrlInitPrec; +import hu.bme.mit.theta.xsts.analysis.initprec.XstsEmptyInitPrec; +import hu.bme.mit.theta.xsts.analysis.initprec.XstsInitPrec; +import hu.bme.mit.theta.xsts.analysis.initprec.XstsPropInitPrec; import java.util.Set; import java.util.function.Predicate; @@ -165,56 +204,56 @@ public XstsConfigBuilder autoExpl(final AutoExpl autoExpl) { } public XstsConfig build(final XSTS xsts) { - final ItpSolver solver = solverFactory.createItpSolver(); + final Solver abstractionSolver = solverFactory.createSolver(); final Expr negProp = Not(xsts.getProp()); if (domain == Domain.EXPL) { final LTS, XstsAction> lts; if(optimizeStmts == OptimizeStmts.ON){ - lts = XstsLts.create(xsts,XstsStmtOptimizer.create(ExplStmtOptimizer.getInstance())); + lts = XstsLts.create(xsts, XstsStmtOptimizer.create(ExplStmtOptimizer.getInstance())); } else { lts = XstsLts.create(xsts, XstsStmtOptimizer.create(DefaultStmtOptimizer.create())); } - final Predicate> target = new XstsStatePredicate(new ExplStatePredicate(negProp, solver)); - final Analysis, XstsAction, ExplPrec> analysis = XstsAnalysis.create(ExplStmtAnalysis.create(solver, xsts.getInitFormula(), maxEnum)); + final Predicate> target = new XstsStatePredicate(new ExplStatePredicate(negProp, abstractionSolver)); + final Analysis, XstsAction, ExplPrec> analysis = XstsAnalysis.create(ExplStmtAnalysis.create(abstractionSolver, xsts.getInitFormula(), maxEnum)); final ArgBuilder, XstsAction, ExplPrec> argBuilder = ArgBuilder.create(lts, analysis, target, - true); + true); final Abstractor, XstsAction, ExplPrec> abstractor = BasicAbstractor.builder(argBuilder) - .waitlist(PriorityWaitlist.create(search.comparator)) - .stopCriterion(refinement == Refinement.MULTI_SEQ ? StopCriterions.fullExploration() - : StopCriterions.firstCex()) - .logger(logger).build(); + .waitlist(PriorityWaitlist.create(search.comparator)) + .stopCriterion(refinement == Refinement.MULTI_SEQ ? StopCriterions.fullExploration() + : StopCriterions.firstCex()) + .logger(logger).build(); Refiner, XstsAction, ExplPrec> refiner = null; switch (refinement) { case FW_BIN_ITP: - refiner = SingleExprTraceRefiner.create(ExprTraceFwBinItpChecker.create(xsts.getInitFormula(), negProp, solver), - JoiningPrecRefiner.create(new ItpRefToExplPrec()), pruneStrategy, logger); + refiner = SingleExprTraceRefiner.create(ExprTraceFwBinItpChecker.create(xsts.getInitFormula(), negProp, solverFactory.createItpSolver()), + JoiningPrecRefiner.create(new ItpRefToExplPrec()), pruneStrategy, logger); break; case BW_BIN_ITP: - refiner = SingleExprTraceRefiner.create(ExprTraceBwBinItpChecker.create(xsts.getInitFormula(), negProp, solver), - JoiningPrecRefiner.create(new ItpRefToExplPrec()), pruneStrategy, logger); + refiner = SingleExprTraceRefiner.create(ExprTraceBwBinItpChecker.create(xsts.getInitFormula(), negProp, solverFactory.createItpSolver()), + JoiningPrecRefiner.create(new ItpRefToExplPrec()), pruneStrategy, logger); break; case SEQ_ITP: - refiner = SingleExprTraceRefiner.create(ExprTraceSeqItpChecker.create(xsts.getInitFormula(), negProp, solver), - JoiningPrecRefiner.create(new ItpRefToExplPrec()), pruneStrategy, logger); + refiner = SingleExprTraceRefiner.create(ExprTraceSeqItpChecker.create(xsts.getInitFormula(), negProp, solverFactory.createItpSolver()), + JoiningPrecRefiner.create(new ItpRefToExplPrec()), pruneStrategy, logger); break; case MULTI_SEQ: - refiner = MultiExprTraceRefiner.create(ExprTraceSeqItpChecker.create(xsts.getInitFormula(), negProp, solver), - JoiningPrecRefiner.create(new ItpRefToExplPrec()), pruneStrategy, logger); + refiner = MultiExprTraceRefiner.create(ExprTraceSeqItpChecker.create(xsts.getInitFormula(), negProp, solverFactory.createItpSolver()), + JoiningPrecRefiner.create(new ItpRefToExplPrec()), pruneStrategy, logger); break; case UNSAT_CORE: - refiner = SingleExprTraceRefiner.create(ExprTraceUnsatCoreChecker.create(xsts.getInitFormula(), negProp, solver), - JoiningPrecRefiner.create(new VarsRefToExplPrec()), pruneStrategy, logger); + refiner = SingleExprTraceRefiner.create(ExprTraceUnsatCoreChecker.create(xsts.getInitFormula(), negProp, solverFactory.createUCSolver()), + JoiningPrecRefiner.create(new VarsRefToExplPrec()), pruneStrategy, logger); break; default: throw new UnsupportedOperationException(domain + " domain does not support " + refinement + " refinement."); } final SafetyChecker, XstsAction, ExplPrec> checker = CegarChecker.create(abstractor, refiner, - logger); + logger); final ExplPrec prec = initPrec.builder.createExpl(xsts); return XstsConfig.create(checker, prec); @@ -222,13 +261,13 @@ public XstsConfigBuilder autoExpl(final AutoExpl autoExpl) { PredAbstractors.PredAbstractor predAbstractor = null; switch (domain) { case PRED_BOOL: - predAbstractor = PredAbstractors.booleanAbstractor(solver); + predAbstractor = PredAbstractors.booleanAbstractor(abstractionSolver); break; case PRED_SPLIT: - predAbstractor = PredAbstractors.booleanSplitAbstractor(solver); + predAbstractor = PredAbstractors.booleanSplitAbstractor(abstractionSolver); break; case PRED_CART: - predAbstractor = PredAbstractors.cartesianAbstractor(solver); + predAbstractor = PredAbstractors.cartesianAbstractor(abstractionSolver); break; default: throw new UnsupportedOperationException(domain + " domain is not supported."); @@ -241,46 +280,46 @@ public XstsConfigBuilder autoExpl(final AutoExpl autoExpl) { lts = XstsLts.create(xsts, XstsStmtOptimizer.create(DefaultStmtOptimizer.create())); } - final Predicate> target = new XstsStatePredicate(new ExprStatePredicate(negProp, solver)); - final Analysis, XstsAction, PredPrec> analysis = XstsAnalysis.create(PredAnalysis.create(solver, predAbstractor, - xsts.getInitFormula())); + final Predicate> target = new XstsStatePredicate(new ExprStatePredicate(negProp, abstractionSolver)); + final Analysis, XstsAction, PredPrec> analysis = XstsAnalysis.create(PredAnalysis.create(abstractionSolver, predAbstractor, + xsts.getInitFormula())); final ArgBuilder, XstsAction, PredPrec> argBuilder = ArgBuilder.create(lts, analysis, target, - true); + true); final Abstractor, XstsAction, PredPrec> abstractor = BasicAbstractor.builder(argBuilder) - .waitlist(PriorityWaitlist.create(search.comparator)) - .stopCriterion(refinement == Refinement.MULTI_SEQ ? StopCriterions.fullExploration() - : StopCriterions.firstCex()) - .logger(logger).build(); + .waitlist(PriorityWaitlist.create(search.comparator)) + .stopCriterion(refinement == Refinement.MULTI_SEQ ? StopCriterions.fullExploration() + : StopCriterions.firstCex()) + .logger(logger).build(); ExprTraceChecker exprTraceChecker = null; switch (refinement) { case FW_BIN_ITP: - exprTraceChecker = ExprTraceFwBinItpChecker.create(xsts.getInitFormula(), negProp, solver); + exprTraceChecker = ExprTraceFwBinItpChecker.create(xsts.getInitFormula(), negProp, solverFactory.createItpSolver()); break; case BW_BIN_ITP: - exprTraceChecker = ExprTraceBwBinItpChecker.create(xsts.getInitFormula(), negProp, solver); + exprTraceChecker = ExprTraceBwBinItpChecker.create(xsts.getInitFormula(), negProp, solverFactory.createItpSolver()); break; case SEQ_ITP: - exprTraceChecker = ExprTraceSeqItpChecker.create(xsts.getInitFormula(), negProp, solver); + exprTraceChecker = ExprTraceSeqItpChecker.create(xsts.getInitFormula(), negProp, solverFactory.createItpSolver()); break; case MULTI_SEQ: - exprTraceChecker = ExprTraceSeqItpChecker.create(xsts.getInitFormula(), negProp, solver); + exprTraceChecker = ExprTraceSeqItpChecker.create(xsts.getInitFormula(), negProp, solverFactory.createItpSolver()); break; default: throw new UnsupportedOperationException( - domain + " domain does not support " + refinement + " refinement."); + domain + " domain does not support " + refinement + " refinement."); } Refiner, XstsAction, PredPrec> refiner; if (refinement == Refinement.MULTI_SEQ) { refiner = MultiExprTraceRefiner.create(exprTraceChecker, - JoiningPrecRefiner.create(new ItpRefToPredPrec(predSplit.splitter)), pruneStrategy, logger); + JoiningPrecRefiner.create(new ItpRefToPredPrec(predSplit.splitter)), pruneStrategy, logger); } else { refiner = SingleExprTraceRefiner.create(exprTraceChecker, - JoiningPrecRefiner.create(new ItpRefToPredPrec(predSplit.splitter)), pruneStrategy, logger); + JoiningPrecRefiner.create(new ItpRefToPredPrec(predSplit.splitter)), pruneStrategy, logger); } final SafetyChecker, XstsAction, PredPrec> checker = CegarChecker.create(abstractor, refiner, - logger); + logger); final PredPrec prec = initPrec.builder.createPred(xsts); return XstsConfig.create(checker, prec); @@ -288,52 +327,52 @@ public XstsConfigBuilder autoExpl(final AutoExpl autoExpl) { final LTS>, XstsAction> lts; if(optimizeStmts == OptimizeStmts.ON){ lts = XstsLts.create(xsts,XstsStmtOptimizer.create( - Prod2ExplPredStmtOptimizer.create( - ExplStmtOptimizer.getInstance() - ))); + Prod2ExplPredStmtOptimizer.create( + ExplStmtOptimizer.getInstance() + ))); } else { lts = XstsLts.create(xsts, XstsStmtOptimizer.create(DefaultStmtOptimizer.create())); } final Analysis,XstsAction,Prod2Prec> prod2Analysis; - final Predicate>> target = new XstsStatePredicate>(new ExprStatePredicate(negProp, solver)); + final Predicate>> target = new XstsStatePredicate>(new ExprStatePredicate(negProp, abstractionSolver)); if(domain == Domain.EXPL_PRED_BOOL || domain == Domain.EXPL_PRED_CART || domain == Domain.EXPL_PRED_SPLIT){ final PredAbstractors.PredAbstractor predAbstractor; switch (domain) { case EXPL_PRED_BOOL: - predAbstractor = PredAbstractors.booleanAbstractor(solver); + predAbstractor = PredAbstractors.booleanAbstractor(abstractionSolver); break; case EXPL_PRED_SPLIT: - predAbstractor = PredAbstractors.booleanSplitAbstractor(solver); + predAbstractor = PredAbstractors.booleanSplitAbstractor(abstractionSolver); break; case EXPL_PRED_CART: - predAbstractor = PredAbstractors.cartesianAbstractor(solver); + predAbstractor = PredAbstractors.cartesianAbstractor(abstractionSolver); break; default: throw new UnsupportedOperationException(domain + " domain is not supported."); } prod2Analysis = Prod2Analysis.create( - ExplStmtAnalysis.create(solver, xsts.getInitFormula(), maxEnum), - PredAnalysis.create(solver, predAbstractor, xsts.getInitFormula()), - Prod2ExplPredPreStrengtheningOperator.create(), - Prod2ExplPredStrengtheningOperator.create(solver)); + ExplStmtAnalysis.create(abstractionSolver, xsts.getInitFormula(), maxEnum), + PredAnalysis.create(abstractionSolver, predAbstractor, xsts.getInitFormula()), + Prod2ExplPredPreStrengtheningOperator.create(), + Prod2ExplPredStrengtheningOperator.create(abstractionSolver)); } else { - final Prod2ExplPredAbstractors.Prod2ExplPredAbstractor prodAbstractor = Prod2ExplPredAbstractors.booleanAbstractor(solver); + final Prod2ExplPredAbstractors.Prod2ExplPredAbstractor prodAbstractor = Prod2ExplPredAbstractors.booleanAbstractor(abstractionSolver); prod2Analysis = Prod2ExplPredAnalysis.create( - ExplAnalysis.create(solver, xsts.getInitFormula()), - PredAnalysis.create(solver, PredAbstractors.booleanAbstractor(solver), xsts.getInitFormula()), - Prod2ExplPredStrengtheningOperator.create(solver), - prodAbstractor); + ExplAnalysis.create(abstractionSolver, xsts.getInitFormula()), + PredAnalysis.create(abstractionSolver, PredAbstractors.booleanAbstractor(abstractionSolver), xsts.getInitFormula()), + Prod2ExplPredStrengtheningOperator.create(abstractionSolver), + prodAbstractor); } final Analysis>, XstsAction, Prod2Prec> analysis = XstsAnalysis.create(prod2Analysis); final ArgBuilder>, XstsAction, Prod2Prec> argBuilder = ArgBuilder.create(lts, analysis, target, - true); + true); final Abstractor>, XstsAction, Prod2Prec> abstractor = BasicAbstractor.builder(argBuilder) - .waitlist(PriorityWaitlist.create(search.comparator)) - .stopCriterion(refinement == Refinement.MULTI_SEQ ? StopCriterions.fullExploration() - : StopCriterions.firstCex()) - .logger(logger).build(); + .waitlist(PriorityWaitlist.create(search.comparator)) + .stopCriterion(refinement == Refinement.MULTI_SEQ ? StopCriterions.fullExploration() + : StopCriterions.firstCex()) + .logger(logger).build(); Refiner>, XstsAction, Prod2Prec> refiner = null; @@ -342,28 +381,28 @@ public XstsConfigBuilder autoExpl(final AutoExpl autoExpl) { switch (refinement) { case FW_BIN_ITP: - refiner = SingleExprTraceRefiner.create(ExprTraceFwBinItpChecker.create(xsts.getInitFormula(), negProp, solver), - JoiningPrecRefiner.create(precRefiner), pruneStrategy, logger); + refiner = SingleExprTraceRefiner.create(ExprTraceFwBinItpChecker.create(xsts.getInitFormula(), negProp, solverFactory.createItpSolver()), + JoiningPrecRefiner.create(precRefiner), pruneStrategy, logger); break; case BW_BIN_ITP: - refiner = SingleExprTraceRefiner.create(ExprTraceBwBinItpChecker.create(xsts.getInitFormula(), negProp, solver), - JoiningPrecRefiner.create(precRefiner), pruneStrategy, logger); + refiner = SingleExprTraceRefiner.create(ExprTraceBwBinItpChecker.create(xsts.getInitFormula(), negProp, solverFactory.createItpSolver()), + JoiningPrecRefiner.create(precRefiner), pruneStrategy, logger); break; case SEQ_ITP: - refiner = SingleExprTraceRefiner.create(ExprTraceSeqItpChecker.create(xsts.getInitFormula(), negProp, solver), - JoiningPrecRefiner.create(precRefiner), pruneStrategy, logger); + refiner = SingleExprTraceRefiner.create(ExprTraceSeqItpChecker.create(xsts.getInitFormula(), negProp, solverFactory.createItpSolver()), + JoiningPrecRefiner.create(precRefiner), pruneStrategy, logger); break; case MULTI_SEQ: - refiner = MultiExprTraceRefiner.create(ExprTraceSeqItpChecker.create(xsts.getInitFormula(), negProp, solver), - JoiningPrecRefiner.create(precRefiner), pruneStrategy, logger); + refiner = MultiExprTraceRefiner.create(ExprTraceSeqItpChecker.create(xsts.getInitFormula(), negProp, solverFactory.createItpSolver()), + JoiningPrecRefiner.create(precRefiner), pruneStrategy, logger); break; default: throw new UnsupportedOperationException( - domain + " domain does not support " + refinement + " refinement."); + domain + " domain does not support " + refinement + " refinement."); } final SafetyChecker>, XstsAction, Prod2Prec> checker = CegarChecker.create(abstractor, refiner, - logger); + logger); final Prod2Prec prec = initPrec.builder.createProd2ExplPred(xsts); return XstsConfig.create(checker, prec); } else {