Java tutorial
/* * CPAchecker is a tool for configurable software verification. * This file is part of CPAchecker. * * Copyright (C) 2007-2014 Dirk Beyer * All rights reserved. * * 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. * * * CPAchecker web page: * http://cpachecker.sosy-lab.org */ package org.sosy_lab.cpachecker.cpa.predicate; import static com.google.common.base.Preconditions.*; import static org.sosy_lab.cpachecker.cpa.predicate.PredicateAbstractState.getPredicateState; import static org.sosy_lab.cpachecker.cpa.predicate.PredicatePrecision.*; import static org.sosy_lab.cpachecker.util.AbstractStates.extractLocation; import java.io.IOException; import java.io.PrintStream; import java.io.Writer; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Set; import java.util.logging.Level; import org.sosy_lab.common.Pair; import org.sosy_lab.common.configuration.Configuration; import org.sosy_lab.common.configuration.FileOption; import org.sosy_lab.common.configuration.FileOption.Type; import org.sosy_lab.common.configuration.IntegerOption; import org.sosy_lab.common.configuration.InvalidConfigurationException; import org.sosy_lab.common.configuration.Option; import org.sosy_lab.common.configuration.Options; import org.sosy_lab.common.io.Files; import org.sosy_lab.common.io.Path; import org.sosy_lab.common.io.PathTemplate; import org.sosy_lab.common.log.LogManager; import org.sosy_lab.cpachecker.cfa.model.CFANode; import org.sosy_lab.cpachecker.core.CPAcheckerResult.Result; import org.sosy_lab.cpachecker.core.ShutdownNotifier; import org.sosy_lab.cpachecker.core.defaults.VariableTrackingPrecision; import org.sosy_lab.cpachecker.core.interfaces.Precision; import org.sosy_lab.cpachecker.core.interfaces.Statistics; import org.sosy_lab.cpachecker.core.reachedset.ReachedSet; import org.sosy_lab.cpachecker.core.reachedset.UnmodifiableReachedSet; import org.sosy_lab.cpachecker.cpa.arg.ARGReachedSet; import org.sosy_lab.cpachecker.cpa.arg.ARGState; import org.sosy_lab.cpachecker.cpa.predicate.persistence.PredicateMapWriter; import org.sosy_lab.cpachecker.cpa.value.ValueAnalysisCPA; import org.sosy_lab.cpachecker.exceptions.CPAException; import org.sosy_lab.cpachecker.exceptions.CPATransferException; import org.sosy_lab.cpachecker.exceptions.RefinementFailedException; import org.sosy_lab.cpachecker.exceptions.SolverException; import org.sosy_lab.cpachecker.util.AbstractStates; import org.sosy_lab.cpachecker.util.Precisions; import org.sosy_lab.cpachecker.util.predicates.AbstractionPredicate; import org.sosy_lab.cpachecker.util.predicates.FormulaMeasuring; import org.sosy_lab.cpachecker.util.predicates.FormulaMeasuring.FormulaMeasures; import org.sosy_lab.cpachecker.util.predicates.Solver; import org.sosy_lab.cpachecker.util.predicates.interfaces.BooleanFormula; import org.sosy_lab.cpachecker.util.predicates.interfaces.view.BooleanFormulaManagerView; import org.sosy_lab.cpachecker.util.predicates.interfaces.view.FormulaManagerView; import org.sosy_lab.cpachecker.util.predicates.pathformula.PathFormula; import org.sosy_lab.cpachecker.util.statistics.StatCounter; import org.sosy_lab.cpachecker.util.statistics.StatInt; import org.sosy_lab.cpachecker.util.statistics.StatKind; import org.sosy_lab.cpachecker.util.statistics.StatTimer; import org.sosy_lab.cpachecker.util.statistics.StatisticsWriter; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Iterables; import com.google.common.collect.ListMultimap; import com.google.common.collect.Multimaps; import com.google.common.collect.Sets; /** * This class provides the refinement strategy for the classical predicate * abstraction (adding the predicates from the interpolant to the precision * and removing the relevant parts of the ARG). */ @Options(prefix = "cpa.predicate") public class PredicateAbstractionRefinementStrategy extends RefinementStrategy { @Option(secure = true, name = "refinement.atomicPredicates", description = "use only the atoms from the interpolants as predicates, " + "and not the whole interpolant") private boolean atomicPredicates = true; @Option(secure = true, name = "precision.sharing", description = "Where to apply the found predicates to?") private PredicateSharing predicateSharing = PredicateSharing.LOCATION; private static enum PredicateSharing { GLOBAL, // at all locations FUNCTION, // at all locations in the respective function LOCATION, // at all occurrences of the respective location LOCATION_INSTANCE, // at the n-th occurrence of the respective location in each path ; } @Option(secure = true, name = "refinement.keepAllPredicates", description = "During refinement, keep predicates from all removed parts " + "of the ARG. Otherwise, only predicates from the error path are kept.") private boolean keepAllPredicates = false; @Option(secure = true, name = "refinement.restartAfterRefinements", description = "Do a complete restart (clearing the reached set) " + "after N refinements. 0 to disable, 1 for always.") @IntegerOption(min = 0) private int restartAfterRefinements = 0; @Option(secure = true, name = "refinement.sharePredicates", description = "During refinement, add all new predicates to the precisions " + "of all abstract states in the reached set.") private boolean sharePredicates = false; @Option(secure = true, name = "refinement.useBddInterpolantSimplification", description = "Use BDDs to simplify interpolants " + "(removing irrelevant predicates)") private boolean useBddInterpolantSimplification = false; @Option(secure = true, name = "refinement.dumpPredicates", description = "After each refinement, dump the newly found predicates.") private boolean dumpPredicates = false; @Option(secure = true, name = "refinement.dumpPredicatesFile", description = "File name for the predicates dumped after refinements.") @FileOption(Type.OUTPUT_FILE) private PathTemplate dumpPredicatesFile = PathTemplate.ofFormatString("refinement%04d-predicates.prec"); private int refinementCount = 0; // this is modulo restartAfterRefinements private int heuristicsCount = 0; private boolean lastRefinementUsedHeuristics = false; protected final LogManager logger; private final ShutdownNotifier shutdownNotifier; private final FormulaManagerView fmgr; private final BooleanFormulaManagerView bfmgr; private final PredicateAbstractionManager predAbsMgr; private final PredicateStaticRefiner staticRefiner; private final FormulaMeasuring formulaMeasuring; private final PredicateMapWriter precisionWriter; // statistics private StatCounter numberOfRefinementsWithStrategy2 = new StatCounter( "Number of refs with location-based cutoff"); private StatInt irrelevantPredsInItp = new StatInt(StatKind.SUM, "Number of irrelevant preds in interpolants"); private StatTimer predicateCreation = new StatTimer(StatKind.SUM, "Predicate creation"); private StatTimer precisionUpdate = new StatTimer(StatKind.SUM, "Precision update"); private StatTimer argUpdate = new StatTimer(StatKind.SUM, "ARG update"); private StatTimer itpSimplification = new StatTimer(StatKind.SUM, "Itp simplification with BDDs"); private StatInt simplifyDeltaConjunctions = new StatInt(StatKind.SUM, "Conjunctions Delta"); private StatInt simplifyDeltaDisjunctions = new StatInt(StatKind.SUM, "Disjunctions Delta"); private StatInt simplifyDeltaNegations = new StatInt(StatKind.SUM, "Negations Delta"); private StatInt simplifyDeltaAtoms = new StatInt(StatKind.SUM, "Atoms Delta"); private StatInt simplifyDeltaVariables = new StatInt(StatKind.SUM, "Variables Delta"); private StatInt simplifyVariablesBefore = new StatInt(StatKind.SUM, "Variables Before"); private StatInt simplifyVariablesAfter = new StatInt(StatKind.SUM, "Variables After"); private class Stats implements Statistics { @Override public String getName() { return "Predicate-Abstraction Refiner"; } @Override public void printStatistics(PrintStream out, Result pResult, ReachedSet pReached) { StatisticsWriter w0 = StatisticsWriter.writingStatisticsTo(out); StatisticsWriter w1 = w0.beginLevel(); w1.put(predicateCreation).ifUpdatedAtLeastOnce(itpSimplification).put(itpSimplification).beginLevel() .put(simplifyDeltaConjunctions).put(simplifyDeltaDisjunctions).put(simplifyDeltaNegations) .put(simplifyDeltaAtoms).put(simplifyDeltaVariables).put(simplifyVariablesBefore) .put(simplifyVariablesAfter); w1.put(precisionUpdate).put(argUpdate).spacer(); basicRefinementStatistics.printStatistics(out, pResult, pReached); w0.put(numberOfRefinementsWithStrategy2).ifUpdatedAtLeastOnce(itpSimplification) .put(irrelevantPredsInItp); } } public PredicateAbstractionRefinementStrategy(final Configuration config, final LogManager pLogger, final ShutdownNotifier pShutdownNotifier, final PredicateAbstractionManager pPredAbsMgr, final PredicateStaticRefiner pStaticRefiner, final Solver pSolver) throws CPAException, InvalidConfigurationException { super(pSolver); config.inject(this, PredicateAbstractionRefinementStrategy.class); logger = pLogger; shutdownNotifier = pShutdownNotifier; fmgr = pSolver.getFormulaManager(); bfmgr = fmgr.getBooleanFormulaManager(); predAbsMgr = pPredAbsMgr; staticRefiner = pStaticRefiner; formulaMeasuring = new FormulaMeasuring(fmgr); if (dumpPredicates && dumpPredicatesFile != null) { precisionWriter = new PredicateMapWriter(config, fmgr); } else { precisionWriter = null; } } private ListMultimap<Pair<CFANode, Integer>, AbstractionPredicate> newPredicates; @Override public boolean needsInterpolants() { return !useStaticRefinement(); } private boolean useStaticRefinement() { return (staticRefiner != null) && (heuristicsCount == 0); } @Override public void performRefinement(ARGReachedSet pReached, List<ARGState> abstractionStatesTrace, List<BooleanFormula> pInterpolants, boolean pRepeatedCounterexample) throws CPAException, InterruptedException { pRepeatedCounterexample = !lastRefinementUsedHeuristics && pRepeatedCounterexample; if (useStaticRefinement()) { UnmodifiableReachedSet reached = pReached.asReachedSet(); ARGState root = (ARGState) reached.getFirstState(); ARGState refinementRoot = Iterables.getLast(root.getChildren()); PredicatePrecision heuristicPrecision; try { heuristicPrecision = staticRefiner.extractPrecisionFromCfa(pReached.asReachedSet(), abstractionStatesTrace, atomicPredicates); } catch (CPATransferException | SolverException e) { logger.logUserException(Level.WARNING, e, "Static refinement failed"); lastRefinementUsedHeuristics = false; super.performRefinement(pReached, abstractionStatesTrace, pInterpolants, pRepeatedCounterexample); return; } shutdownNotifier.shutdownIfNecessary(); pReached.removeSubtree(refinementRoot, heuristicPrecision, Predicates.instanceOf(PredicatePrecision.class)); heuristicsCount++; lastRefinementUsedHeuristics = true; } else { lastRefinementUsedHeuristics = false; super.performRefinement(pReached, abstractionStatesTrace, pInterpolants, pRepeatedCounterexample); } } @Override public void startRefinementOfPath() { checkState(newPredicates == null); // needs to be a fully deterministic data structure, // thus a Multimap based on a LinkedHashMap // (we iterate over the keys) newPredicates = Multimaps.newListMultimap( new LinkedHashMap<Pair<CFANode, Integer>, Collection<AbstractionPredicate>>(), new Supplier<List<AbstractionPredicate>>() { @Override public List<AbstractionPredicate> get() { return new ArrayList<>(); } }); } @Override public boolean performRefinementForState(BooleanFormula pInterpolant, ARGState interpolationPoint) { checkState(newPredicates != null); checkArgument(!bfmgr.isTrue(pInterpolant)); predicateCreation.start(); PredicateAbstractState predicateState = getPredicateState(interpolationPoint); PathFormula blockFormula = predicateState.getAbstractionFormula().getBlockFormula(); Collection<AbstractionPredicate> localPreds = convertInterpolant(pInterpolant, blockFormula); CFANode loc = AbstractStates.extractLocation(interpolationPoint); int locInstance = predicateState.getAbstractionLocationsOnPath().get(loc); newPredicates.putAll(Pair.of(loc, locInstance), localPreds); predicateCreation.stop(); return false; } /** * Get the predicates out of an interpolant. * @param interpolant The interpolant formula. * @return A set of predicates. */ protected final Collection<AbstractionPredicate> convertInterpolant(final BooleanFormula pInterpolant, PathFormula blockFormula) { BooleanFormula interpolant = pInterpolant; if (bfmgr.isTrue(interpolant)) { return Collections.<AbstractionPredicate>emptySet(); } Collection<AbstractionPredicate> preds; int allPredsCount = 0; if (useBddInterpolantSimplification) { FormulaMeasures itpBeforeSimple = formulaMeasuring.measure(interpolant); itpSimplification.start(); // need to call extractPredicates() for registering all predicates allPredsCount = predAbsMgr.extractPredicates(interpolant).size(); interpolant = predAbsMgr.buildAbstraction(fmgr.uninstantiate(interpolant), blockFormula) .asInstantiatedFormula(); itpSimplification.stop(); FormulaMeasures itpAfterSimple = formulaMeasuring.measure(interpolant); simplifyDeltaAtoms.setNextValue(itpAfterSimple.getAtoms() - itpBeforeSimple.getAtoms()); simplifyDeltaDisjunctions .setNextValue(itpAfterSimple.getDisjunctions() - itpBeforeSimple.getDisjunctions()); simplifyDeltaConjunctions .setNextValue(itpAfterSimple.getConjunctions() - itpBeforeSimple.getConjunctions()); simplifyDeltaNegations.setNextValue(itpAfterSimple.getNegations() - itpBeforeSimple.getNegations()); simplifyDeltaVariables .setNextValue(itpAfterSimple.getVariables().size() - itpBeforeSimple.getVariables().size()); simplifyVariablesBefore.setNextValue(itpBeforeSimple.getVariables().size()); simplifyVariablesAfter.setNextValue(itpAfterSimple.getVariables().size()); } if (atomicPredicates) { preds = predAbsMgr.extractPredicates(interpolant); if (useBddInterpolantSimplification) { irrelevantPredsInItp.setNextValue(allPredsCount - preds.size()); } } else { preds = ImmutableList.of(predAbsMgr.createPredicateFor(fmgr.uninstantiate(interpolant))); } assert !preds.isEmpty() : "Interpolant without relevant predicates: " + pInterpolant + "; simplified to " + interpolant; logger.log(Level.FINEST, "Got predicates", preds); return preds; } @Override public void finishRefinementOfPath(ARGState pUnreachableState, List<ARGState> pAffectedStates, ARGReachedSet pReached, boolean pRepeatedCounterexample) throws CPAException { { // Add predicate "false" to unreachable location CFANode loc = extractLocation(pUnreachableState); int locInstance = getPredicateState(pUnreachableState).getAbstractionLocationsOnPath().get(loc); newPredicates.put(Pair.of(loc, locInstance), predAbsMgr.createPredicateFor(bfmgr.makeBoolean(false))); pAffectedStates.add(pUnreachableState); } // We have two different strategies for the refinement root: set it to // the first interpolation point or set it to highest location in the ARG // where the same CFANode appears. // Both work, so this is a heuristics question to get the best performance. // My benchmark showed, that at least for the benchmarks-lbe examples it is // best to use strategy one iff newPredicatesFound. // get previous precision UnmodifiableReachedSet reached = pReached.asReachedSet(); PredicatePrecision targetStatePrecision = extractPredicatePrecision( reached.getPrecision(reached.getLastState())); ARGState refinementRoot = getRefinementRoot(pAffectedStates, targetStatePrecision, pRepeatedCounterexample); logger.log(Level.FINEST, "Removing everything below", refinementRoot, "from ARG."); // check whether we should restart refinementCount++; if (restartAfterRefinements > 0 && refinementCount >= restartAfterRefinements) { ARGState root = (ARGState) reached.getFirstState(); // we have to use the child as the refinementRoot assert root.getChildren().size() == 1 : "ARG root should have exactly one child"; refinementRoot = Iterables.getLast(root.getChildren()); logger.log(Level.FINEST, "Restarting analysis after", refinementCount, "refinements by clearing the ARG."); refinementCount = 0; } // now create new precision precisionUpdate.start(); PredicatePrecision basePrecision; if (keepAllPredicates) { basePrecision = findAllPredicatesFromSubgraph(refinementRoot, reached); } else { basePrecision = targetStatePrecision; } logger.log(Level.ALL, "Old predicate map is", basePrecision); logger.log(Level.ALL, "New predicates are", newPredicates); PredicatePrecision newPrecision; switch (predicateSharing) { case GLOBAL: newPrecision = basePrecision.addGlobalPredicates(newPredicates.values()); break; case FUNCTION: newPrecision = basePrecision.addFunctionPredicates(mergePredicatesPerFunction(newPredicates)); break; case LOCATION: newPrecision = basePrecision.addLocalPredicates(mergePredicatesPerLocation(newPredicates)); break; case LOCATION_INSTANCE: newPrecision = basePrecision.addLocationInstancePredicates(newPredicates); break; default: throw new AssertionError(); } logger.log(Level.ALL, "Predicate map now is", newPrecision); assert basePrecision.calculateDifferenceTo(newPrecision) == 0 : "We forgot predicates during refinement!"; assert targetStatePrecision .calculateDifferenceTo(newPrecision) == 0 : "We forgot predicates during refinement!"; if (dumpPredicates && dumpPredicatesFile != null) { Path precFile = dumpPredicatesFile.getPath(precisionUpdate.getUpdateCount()); try (Writer w = Files.openOutputFile(precFile)) { precisionWriter.writePredicateMap(ImmutableSetMultimap.copyOf(newPredicates), ImmutableSetMultimap.<CFANode, AbstractionPredicate>of(), ImmutableSetMultimap.<String, AbstractionPredicate>of(), ImmutableSet.<AbstractionPredicate>of(), newPredicates.values(), w); } catch (IOException e) { logger.logUserException(Level.WARNING, e, "Could not dump precision to file"); } } precisionUpdate.stop(); argUpdate.start(); List<Precision> precisions = new ArrayList<>(2); List<Predicate<? super Precision>> precisionTypes = new ArrayList<>(2); precisions.add(newPrecision); precisionTypes.add(Predicates.instanceOf(PredicatePrecision.class)); if (isValuePrecisionAvailable(pReached, refinementRoot)) { precisions.add(mergeAllValuePrecisionsFromSubgraph(refinementRoot, reached)); precisionTypes.add(VariableTrackingPrecision.isMatchingCPAClass(ValueAnalysisCPA.class)); } pReached.removeSubtree(refinementRoot, precisions, precisionTypes); assert (refinementCount > 0) || reached.size() == 1; if (sharePredicates) { pReached.updatePrecisionGlobally(newPrecision, Predicates.instanceOf(PredicatePrecision.class)); } argUpdate.stop(); newPredicates = null; } protected final PredicatePrecision extractPredicatePrecision(Precision oldPrecision) throws IllegalStateException { PredicatePrecision oldPredicatePrecision = Precisions.extractPrecisionByType(oldPrecision, PredicatePrecision.class); if (oldPredicatePrecision == null) { throw new IllegalStateException("Could not find the PredicatePrecision for the error element"); } return oldPredicatePrecision; } private ARGState getRefinementRoot(List<ARGState> pAffectedStates, PredicatePrecision targetStatePrecision, boolean pRepeatedCounterexample) throws RefinementFailedException { boolean newPredicatesFound = !targetStatePrecision.getLocalPredicates().entries() .containsAll(newPredicates.entries()); ARGState firstInterpolationPoint = pAffectedStates.get(0); if (!newPredicatesFound) { if (pRepeatedCounterexample) { throw new RefinementFailedException(RefinementFailedException.Reason.RepeatedCounterexample, null); } numberOfRefinementsWithStrategy2.inc(); CFANode firstInterpolationPointLocation = AbstractStates.extractLocation(firstInterpolationPoint); logger.log(Level.FINEST, "Found spurious counterexample,", "trying strategy 2: remove everything below node", firstInterpolationPointLocation, "from ARG."); // find top-most element in path with location == firstInterpolationPointLocation, // this is not necessary equal to firstInterpolationPoint ARGState current = firstInterpolationPoint; while (!current.getParents().isEmpty()) { current = Iterables.get(current.getParents(), 0); if (getPredicateState(current).isAbstractionState()) { CFANode loc = AbstractStates.extractLocation(current); if (loc.equals(firstInterpolationPointLocation)) { firstInterpolationPoint = current; } } } } return firstInterpolationPoint; } /** * Collect all precisions in the subgraph below refinementRoot and merge * their predicates. * @return a new precision with all these predicates. */ private PredicatePrecision findAllPredicatesFromSubgraph(ARGState refinementRoot, UnmodifiableReachedSet reached) { PredicatePrecision newPrecision = PredicatePrecision.empty(); // find all distinct precisions to merge them Set<Precision> precisions = Sets.newIdentityHashSet(); for (ARGState state : refinementRoot.getSubgraph()) { if (!state.isCovered()) { // covered states are not in reached set precisions.add(reached.getPrecision(state)); } } for (Precision prec : precisions) { newPrecision = newPrecision.mergeWith(extractPredicatePrecision(prec)); } return newPrecision; } private boolean isValuePrecisionAvailable(final ARGReachedSet pReached, ARGState root) { if (!pReached.asReachedSet().contains(root)) { return false; } return Precisions.extractPrecisionByType(pReached.asReachedSet().getPrecision(root), VariableTrackingPrecision.class) != null; } private VariableTrackingPrecision mergeAllValuePrecisionsFromSubgraph(ARGState refinementRoot, UnmodifiableReachedSet reached) { VariableTrackingPrecision rootPrecision = Precisions .extractPrecisionByType(reached.getPrecision(refinementRoot), VariableTrackingPrecision.class); // find all distinct precisions to merge them Set<Precision> precisions = Sets.newIdentityHashSet(); for (ARGState state : refinementRoot.getSubgraph()) { if (!state.isCovered()) { // covered states are not in reached set precisions.add(reached.getPrecision(state)); } } for (Precision prec : precisions) { rootPrecision = rootPrecision .join(Precisions.extractPrecisionByType(prec, VariableTrackingPrecision.class)); } return rootPrecision; } @Override public Statistics getStatistics() { return new Stats(); } }