hu.bme.mit.theta.sts.cli.StsCli Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of theta-sts-cli Show documentation
Show all versions of theta-sts-cli Show documentation
Sts Cli subproject in the Theta model checking framework
/*
* Copyright 2024 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.sts.cli;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.google.common.base.Stopwatch;
import hu.bme.mit.theta.analysis.Trace;
import hu.bme.mit.theta.analysis.algorithm.SafetyResult;
import hu.bme.mit.theta.analysis.algorithm.arg.ARG;
import hu.bme.mit.theta.analysis.algorithm.bounded.BoundedChecker;
import hu.bme.mit.theta.analysis.algorithm.bounded.BoundedCheckerBuilderKt;
import hu.bme.mit.theta.analysis.algorithm.bounded.MonolithicExpr;
import hu.bme.mit.theta.analysis.algorithm.cegar.CegarStatistics;
import hu.bme.mit.theta.analysis.expr.ExprState;
import hu.bme.mit.theta.analysis.expr.refinement.PruneStrategy;
import hu.bme.mit.theta.common.CliUtils;
import hu.bme.mit.theta.common.Utils;
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;
import hu.bme.mit.theta.common.logging.NullLogger;
import hu.bme.mit.theta.common.table.BasicTableWriter;
import hu.bme.mit.theta.common.table.TableWriter;
import hu.bme.mit.theta.core.model.Valuation;
import hu.bme.mit.theta.core.type.booltype.BoolExprs;
import hu.bme.mit.theta.core.utils.ExprUtils;
import hu.bme.mit.theta.solver.SolverFactory;
import hu.bme.mit.theta.solver.z3legacy.Z3LegacySolverFactory;
import hu.bme.mit.theta.sts.STS;
import hu.bme.mit.theta.sts.StsUtils;
import hu.bme.mit.theta.sts.aiger.AigerParser;
import hu.bme.mit.theta.sts.aiger.AigerToSts;
import hu.bme.mit.theta.sts.aiger.elements.AigerSystem;
import hu.bme.mit.theta.sts.aiger.utils.AigerCoi;
import hu.bme.mit.theta.sts.analysis.StsAction;
import hu.bme.mit.theta.sts.analysis.StsToMonolithicExprKt;
import hu.bme.mit.theta.sts.analysis.StsTraceConcretizer;
import hu.bme.mit.theta.sts.analysis.config.StsConfig;
import hu.bme.mit.theta.sts.analysis.config.StsConfigBuilder;
import hu.bme.mit.theta.sts.analysis.config.StsConfigBuilder.*;
import hu.bme.mit.theta.sts.dsl.StsDslManager;
import hu.bme.mit.theta.sts.dsl.StsSpec;
import java.io.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
/** A command line interface for running a CEGAR configuration on an STS. */
public class StsCli {
private static final String JAR_NAME = "theta-sts-cli.jar";
private final String[] args;
private final TableWriter writer;
enum Algorithm {
CEGAR,
BMC,
KINDUCTION,
IMC
}
@Parameter(
names = {"--domain"},
description = "Abstract domain")
Domain domain = Domain.PRED_CART;
@Parameter(
names = {"--algorithm"},
description = "Algorithm")
Algorithm algorithm = Algorithm.CEGAR;
@Parameter(
names = {"--refinement"},
description = "Refinement strategy")
Refinement refinement = Refinement.SEQ_ITP;
@Parameter(
names = {"--search"},
description = "Search strategy")
Search search = Search.BFS;
@Parameter(
names = {"--predsplit"},
description = "Predicate splitting")
PredSplit predSplit = PredSplit.WHOLE;
@Parameter(
names = {"--model"},
description = "Path of the input STS model",
required = true)
String model;
@Parameter(
names = {"--initprec"},
description = "Initial precision")
InitPrec initPrec = InitPrec.EMPTY;
@Parameter(
names = "--prunestrategy",
description = "Strategy for pruning the ARG after refinement")
PruneStrategy pruneStrategy = PruneStrategy.LAZY;
@Parameter(
names = {"--loglevel"},
description = "Detailedness of logging")
Logger.Level logLevel = Level.SUBSTEP;
@Parameter(
names = {"--benchmark"},
description = "Benchmark mode (only print metrics)")
Boolean benchmarkMode = false;
@Parameter(names = "--cex", description = "Write concrete counterexample to a file")
String cexfile = null;
@Parameter(
names = {"--header"},
description = "Print only a header (for benchmarks)",
help = true)
boolean headerOnly = false;
@Parameter(names = "--stacktrace", description = "Print full stack trace in case of exception")
boolean stacktrace = false;
@Parameter(names = "--version", description = "Display version", help = true)
boolean versionInfo = false;
private Logger logger;
public StsCli(final String[] args) {
this.args = args;
writer = new BasicTableWriter(System.out, ",", "\"", "\"");
}
public static void main(final String[] args) {
final StsCli mainApp = new StsCli(args);
mainApp.run();
}
private void run() {
try {
JCommander.newBuilder().addObject(this).programName(JAR_NAME).build().parse(args);
logger = benchmarkMode ? NullLogger.getInstance() : new ConsoleLogger(logLevel);
} catch (final ParameterException ex) {
System.out.println("Invalid parameters, details:");
System.out.println(ex.getMessage());
ex.usage();
return;
}
if (headerOnly) {
printHeader();
return;
}
if (versionInfo) {
CliUtils.printVersion(System.out);
return;
}
try {
final Stopwatch sw = Stopwatch.createStarted();
final STS sts = loadModel();
SafetyResult, ? extends Trace, ?>> status = null;
if (algorithm.equals(Algorithm.CEGAR)) {
final StsConfig, ?, ?> configuration = buildConfiguration(sts);
status = check(configuration);
} else if (algorithm == Algorithm.BMC
|| algorithm == Algorithm.KINDUCTION
|| algorithm == Algorithm.IMC) {
final BoundedChecker, ?> checker =
buildBoundedChecker(sts, Z3LegacySolverFactory.getInstance());
status = checker.check(null);
} else {
throw new UnsupportedOperationException(
"Algorithm " + algorithm + " not supported");
}
sw.stop();
printResult(status, sts, sw.elapsed(TimeUnit.MILLISECONDS));
if (status.isUnsafe() && cexfile != null) {
writeCex(sts, status.asUnsafe());
}
} catch (final Throwable ex) {
printError(ex);
System.exit(1);
}
}
private SafetyResult extends ARG, ?>, ? extends Trace, ?>> check(
StsConfig, ?, ?> configuration) throws Exception {
try {
return configuration.check();
} catch (final Exception ex) {
String message = ex.getMessage() == null ? "(no message)" : ex.getMessage();
throw new Exception(
"Error while running algorithm: "
+ ex.getClass().getSimpleName()
+ " "
+ message,
ex);
}
}
private void printHeader() {
Stream.of(
"Result",
"TimeMs",
"AlgoTimeMs",
"AbsTimeMs",
"RefTimeMs",
"Iterations",
"ArgSize",
"ArgDepth",
"ArgMeanBranchFactor",
"CexLen",
"Vars",
"Size")
.forEach(writer::cell);
writer.newRow();
}
private STS loadModel() throws Exception {
try {
if (model.endsWith(".aag")) {
final AigerSystem aigerSystem = AigerParser.parse(model);
AigerCoi.apply(aigerSystem);
return AigerToSts.createSts(aigerSystem);
} else {
try (InputStream inputStream = new FileInputStream(model)) {
final StsSpec spec = StsDslManager.createStsSpec(inputStream);
if (spec.getAllSts().size() != 1) {
throw new UnsupportedOperationException(
"STS contains multiple properties.");
}
return StsUtils.eliminateIte(Utils.singleElementOf(spec.getAllSts()));
}
}
} catch (Exception ex) {
throw new Exception("Could not parse STS: " + ex.getMessage(), ex);
}
}
private StsConfig, ?, ?> buildConfiguration(final STS sts) throws Exception {
try {
return new StsConfigBuilder(domain, refinement, Z3LegacySolverFactory.getInstance())
.initPrec(initPrec)
.search(search)
.predSplit(predSplit)
.pruneStrategy(pruneStrategy)
.logger(logger)
.build(sts);
} catch (final Exception ex) {
throw new Exception("Could not create configuration: " + ex.getMessage(), ex);
}
}
private BoundedChecker, ?> buildBoundedChecker(
final STS sts, final SolverFactory abstractionSolverFactory) {
final MonolithicExpr monolithicExpr = StsToMonolithicExprKt.toMonolithicExpr(sts);
final BoundedChecker, ?> checker;
switch (algorithm) {
case BMC ->
checker =
BoundedCheckerBuilderKt.buildBMC(
monolithicExpr,
abstractionSolverFactory.createSolver(),
val -> StsToMonolithicExprKt.valToState(sts, val),
(val1, val2) ->
StsToMonolithicExprKt.valToAction(sts, val1, val2),
logger);
case KINDUCTION ->
checker =
BoundedCheckerBuilderKt.buildKIND(
monolithicExpr,
abstractionSolverFactory.createSolver(),
abstractionSolverFactory.createSolver(),
val -> StsToMonolithicExprKt.valToState(sts, val),
(val1, val2) ->
StsToMonolithicExprKt.valToAction(sts, val1, val2),
logger);
case IMC ->
checker =
BoundedCheckerBuilderKt.buildIMC(
monolithicExpr,
abstractionSolverFactory.createSolver(),
abstractionSolverFactory.createItpSolver(),
val -> StsToMonolithicExprKt.valToState(sts, val),
(val1, val2) ->
StsToMonolithicExprKt.valToAction(sts, val1, val2),
logger);
default ->
throw new UnsupportedOperationException(
"Algorithm " + algorithm + " not supported");
}
return checker;
}
private void printResult(
final SafetyResult, ? extends Trace, ?>> status,
final STS sts,
final long totalTimeMs) {
final CegarStatistics stats =
(CegarStatistics) status.getStats().orElse(new CegarStatistics(0, 0, 0, 0));
if (benchmarkMode) {
writer.cell(status.isSafe());
writer.cell(totalTimeMs);
writer.cell(stats.getAlgorithmTimeMs());
writer.cell(stats.getAbstractorTimeMs());
writer.cell(stats.getRefinerTimeMs());
writer.cell(stats.getIterations());
if (status.getProof() instanceof ARG, ?> arg) {
writer.cell(arg.size());
writer.cell(arg.getDepth());
writer.cell(arg.getMeanBranchingFactor());
} else {
writer.cell("");
writer.cell("");
writer.cell("");
}
if (status.isUnsafe()) {
writer.cell(status.asUnsafe().getCex().length() + "");
} else {
writer.cell("");
}
writer.cell(sts.getVars().size());
writer.cell(ExprUtils.nodeCountSize(BoolExprs.And(sts.getInit(), sts.getTrans())));
writer.newRow();
}
}
private void printError(final Throwable ex) {
final String message = ex.getMessage() == null ? "" : ex.getMessage();
if (benchmarkMode) {
writer.cell("[EX] " + ex.getClass().getSimpleName() + ": " + message);
writer.newRow();
} else {
logger.write(
Level.RESULT,
"%s occurred, message: %s%n",
ex.getClass().getSimpleName(),
message);
if (stacktrace) {
final StringWriter errors = new StringWriter();
ex.printStackTrace(new PrintWriter(errors));
logger.write(Level.RESULT, "Trace:%n%s%n", errors.toString());
} else {
logger.write(Level.RESULT, "Use --stacktrace for stack trace%n");
}
}
}
private void writeCex(final STS sts, final SafetyResult.Unsafe, ? extends Trace, ?>> status)
throws FileNotFoundException {
@SuppressWarnings("unchecked")
final Trace trace = (Trace) status.getCex();
final Trace concrTrace =
StsTraceConcretizer.concretize(sts, trace, Z3LegacySolverFactory.getInstance());
final File file = new File(cexfile);
PrintWriter printWriter = null;
try {
printWriter = new PrintWriter(file);
for (Valuation state : concrTrace.getStates()) {
printWriter.println(state.toString());
}
} finally {
if (printWriter != null) {
printWriter.close();
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy