/*
* Copyright (C) 2024 Matthias Heizmann (heizmann@informatik.uni-freiburg.de)
* Copyright (C) 2024 University of Freiburg
*
* This file is part of the ULTIMATE ModelCheckerUtils Library.
*
* The ULTIMATE ModelCheckerUtils Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ULTIMATE ModelCheckerUtils Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the ULTIMATE ModelCheckerUtils Library. If not, see .
*
* Additional permission under GNU GPL version 3 section 7:
* If you modify the ULTIMATE ModelCheckerUtils Library, or any covered work, by linking
* or combining it with Eclipse RCP (or a modified version of Eclipse RCP),
* containing parts covered by the terms of the Eclipse Public License, the
* licensors of the ULTIMATE ModelCheckerUtils Library grant you additional permission
* to convey the resulting work.
*/
package de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import de.uni_freiburg.informatik.ultimate.core.model.services.ILogger;
import de.uni_freiburg.informatik.ultimate.core.model.services.IUltimateServiceProvider;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.IcfgUtils;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.structure.IIcfg;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.structure.IIcfgCallTransition;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.structure.IIcfgInternalTransition;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.structure.IIcfgReturnTransition;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.structure.IIcfgSummaryTransition;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.structure.IcfgEdge;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.structure.IcfgLocation;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.structure.debugidentifiers.DebugIdentifier;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.transitions.TransFormula;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.cfg.variables.IProgramNonOldVar;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.smt.IncrementalImplicationChecker;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.smt.predicates.BasicPredicate;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.smt.predicates.BasicPredicateFactory;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.smt.predicates.IPredicate;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.smt.predicates.PredicateTransformer;
import de.uni_freiburg.informatik.ultimate.lib.modelcheckerutils.smt.predicates.TermDomainOperationProvider;
import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.IncrementalPlicationChecker.Validity;
import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.ManagedScript;
import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.SmtUtils;
import de.uni_freiburg.informatik.ultimate.lib.smtlibutils.quantifier.QuantifierUtils;
import de.uni_freiburg.informatik.ultimate.logic.Term;
import de.uni_freiburg.informatik.ultimate.util.datastructures.DataStructureUtils;
/**
* This class provides an implementation of abstract interpretation whose focus
* is not speed and precision but simplicity. The fixpoint iteration will never
* split nodes hence invariants are typically conjunctions.
* Warning: The interface of this class will change.
*
* @author Matthias Heizmann (heizmann@informatik.uni-freiburg.de)
*/
public class ConjunctiveAbstractInterpretationUtils {
public static enum Widening {
INTERSECTION, SMT_SOLVER, POLY_PAC,
}
private ConjunctiveAbstractInterpretationUtils() {
// do not instantiate
}
public static Map computeInvariants(
final IUltimateServiceProvider services, final IIcfg icfg, final Widening widening) {
final BasicPredicateFactory predFac = new BasicPredicateFactory(services,
icfg.getCfgSmtToolkit().getManagedScript(), icfg.getCfgSmtToolkit().getSymbolTable());
final Map result = new HashMap<>();
final ArrayDeque worklist = new ArrayDeque<>();
initializeMapAndWorklist(icfg, worklist, predFac, result);
doFixpointIteration(services, icfg, predFac, widening, worklist, result);
final ILogger logger = services.getLoggingService().getLogger(ConjunctiveAbstractInterpretationUtils.class);
if (logger.isInfoEnabled()) {
logInvariantInformation(logger, icfg, result);
}
return result;
}
private static void logInvariantInformation(final ILogger logger, final IIcfg icfg,
final Map result) {
int trueInvariants = 0;
int falseInvariants = 0;
int errorLocations = 0;
int errorLocationsLabelledByFalse = 0;
for (final Entry entry : result.entrySet()) {
if (IcfgUtils.isErrorLocation(icfg, entry.getKey())) {
errorLocations++;
if (SmtUtils.isFalseLiteral(entry.getValue().getFormula())) {
errorLocationsLabelledByFalse++;
}
}
if (SmtUtils.isTrueLiteral(entry.getValue().getFormula())) {
trueInvariants++;
} else if (SmtUtils.isFalseLiteral(entry.getValue().getFormula())) {
falseInvariants++;
}
}
logger.info(String.format(
"Applied ConjunctiveAbstractInterpretation to ICFG %s: %s locations (thereof %s times true, %s times false). ICFG has %s error locations (thereof %s labelled by false).",
icfg.getIdentifier(), result.size(), trueInvariants, falseInvariants, errorLocations,
errorLocationsLabelledByFalse));
}
private static void initializeMapAndWorklist(final IIcfg icfg,
final ArrayDeque worklist, final BasicPredicateFactory predFac,
final Map result) {
final IPredicate truePredicate = constructTruePredicate(icfg, predFac);
final IPredicate falsePredicate = constructFalsePredicate(icfg, predFac);
for (final Entry> procToPps : icfg.getProgramPoints().entrySet()) {
for (final Entry entry : procToPps.getValue().entrySet()) {
final LOC pp = entry.getValue();
if (icfg.getInitialNodes().contains(pp)) {
// initialize initial nodes with empty set (which represents true)
result.put(pp, truePredicate);
worklist.add(pp);
} else {
// initialize all other nodes with false
result.put(pp, falsePredicate);
}
}
}
}
private static IPredicate constructFalsePredicate(final IIcfg icfg,
final BasicPredicateFactory predFac) {
return predFac.newPredicate(icfg.getCfgSmtToolkit().getManagedScript().getScript().term("false"));
}
private static IPredicate constructTruePredicate(final IIcfg icfg,
final BasicPredicateFactory predFac) {
return predFac.newPredicate(icfg.getCfgSmtToolkit().getManagedScript().getScript().term("true"));
}
private static void doFixpointIteration(final IUltimateServiceProvider services,
final IIcfg icfg, final BasicPredicateFactory predFac, final Widening widening,
final ArrayDeque worklist, final Map result) {
final ManagedScript mgdScript = icfg.getCfgSmtToolkit().getManagedScript();
final PredicateTransformer pt = new PredicateTransformer<>(mgdScript,
new TermDomainOperationProvider(services, mgdScript));
long iterations = 0;
while (!worklist.isEmpty()) {
final LOC src = worklist.removeFirst();
final IPredicate srcPred = result.get(src);
for (final IcfgEdge edge : src.getOutgoingEdges()) {
final Term postcondition;
if (edge instanceof IIcfgInternalTransition) {
postcondition = pt.strongestPostcondition(srcPred, edge.getTransformula());
} else if (edge instanceof IIcfgSummaryTransition) {
if (((IIcfgSummaryTransition) edge).calledProcedureHasImplementation()) {
// do nothing, ignore this edge
continue;
} else {
// handle like an internal edge
postcondition = pt.strongestPostcondition(srcPred, edge.getTransformula());
}
} else if (edge instanceof IIcfgCallTransition) {
final String callee = edge.getSucceedingProcedure();
final TransFormula localVarAssignment = edge.getTransformula();
final TransFormula globalVarAssignments = icfg.getCfgSmtToolkit().getOldVarsAssignmentCache()
.getGlobalVarsAssignment(callee);
final TransFormula oldVarAssignments = icfg.getCfgSmtToolkit().getOldVarsAssignmentCache()
.getGlobalVarsAssignment(callee);
final Set modifiableGlobalsOfCalledProcedure = icfg.getCfgSmtToolkit()
.getModifiableGlobalsTable().getModifiedBoogieVars(callee);
postcondition = pt.strongestPostconditionCall(srcPred, localVarAssignment, globalVarAssignments,
oldVarAssignments, modifiableGlobalsOfCalledProcedure);
} else if (edge instanceof IIcfgReturnTransition) {
final String callee = edge.getPrecedingProcedure();
final IIcfgCallTransition call = ((IIcfgReturnTransition) edge).getCorrespondingCall();
final TransFormula oldVarAssignments = icfg.getCfgSmtToolkit().getOldVarsAssignmentCache()
.getGlobalVarsAssignment(callee);
final Set modifiableGlobalsOfCalledProcedure = icfg.getCfgSmtToolkit()
.getModifiableGlobalsTable().getModifiedBoogieVars(callee);
final IPredicate callerPred = result.get(((IIcfgReturnTransition) edge).getCallerProgramPoint());
postcondition = pt.strongestPostconditionReturn(srcPred, callerPred, edge.getTransformula(),
call.getLocalVarsAssignment(), oldVarAssignments, modifiableGlobalsOfCalledProcedure);
} else {
throw new AssertionError("Yet unsupported: " + edge.getClass().getSimpleName());
}
final LOC target = (LOC) edge.getTarget();
final IPredicate oldTargetPredicate = result.get(target);
final IPredicate newTargetPredicate = widen(services, icfg, predFac, widening, oldTargetPredicate,
postcondition);
if (newTargetPredicate != null) {
result.put(target, newTargetPredicate);
worklist.add(target);
}
}
iterations++;
}
System.out.println("Iterations " + iterations);
}
private static IPredicate widen(final IUltimateServiceProvider services,
final IIcfg icfg, final BasicPredicateFactory predFac, final Widening widen,
final IPredicate oldTargetPredicate, final Term postcondition) {
final Set oldConjuncts = new HashSet<>(
Arrays.asList(SmtUtils.getConjuncts(oldTargetPredicate.getFormula())));
final Set postconditionConjuncts = new HashSet<>(Arrays.asList(SmtUtils.getConjuncts(postcondition)));
final Term newConjunction;
if (isFalse(oldConjuncts)) {
newConjunction = postcondition;
} else {
final Set newConjuncts;
switch (widen) {
case INTERSECTION:
newConjuncts = widenNaively(oldConjuncts, postconditionConjuncts);
break;
case POLY_PAC:
throw new AssertionError("Not yet implemented.");
case SMT_SOLVER:
newConjuncts = widenBySolver(services, icfg.getCfgSmtToolkit().getManagedScript(), predFac,
oldTargetPredicate, postcondition, oldConjuncts, postconditionConjuncts);
break;
default:
throw new AssertionError("Unknown value");
}
newConjunction = SmtUtils.and(icfg.getCfgSmtToolkit().getManagedScript().getScript(), newConjuncts);
}
if (newConjunction.equals(oldTargetPredicate.getFormula())) {
return null;
} else {
return predFac.newPredicate(newConjunction);
}
}
public static Set widenNaively(final Set oldConjuncts, final Set postconditionConjuncts) {
return DataStructureUtils.intersection(postconditionConjuncts, oldConjuncts);
}
public static Set widenBySolver(final IUltimateServiceProvider services, final ManagedScript managedScript,
final BasicPredicateFactory predFac, final IPredicate oldPredicate, final Term postcondition,
final Set oldConjuncts, final Set postconditionConjuncts) {
final Set resultConjuncts = new HashSet<>();
final IPredicate postcondPred = predFac.newPredicate(postcondition);
addImplied(services, managedScript, predFac, oldPredicate, oldConjuncts, postconditionConjuncts,
resultConjuncts);
addImplied(services, managedScript, predFac, postcondPred, postconditionConjuncts, oldConjuncts,
resultConjuncts);
return resultConjuncts;
}
public static void addImplied(final IUltimateServiceProvider services, final ManagedScript managedScript,
final BasicPredicateFactory predFac, final IPredicate antecedent, final Set antecedentConjuncts,
final Set candidates, final Set result) {
final IncrementalImplicationChecker ipc = new IncrementalImplicationChecker(services, managedScript);
for (final Term candidate : candidates) {
if (antecedentConjuncts.contains(candidate)) {
result.add(candidate);
continue;
}
if (!QuantifierUtils.isQuantifierFree(candidate)) {
continue;
}
final BasicPredicate candidatePred = predFac.newPredicate(candidate);
final Validity val = ipc.checkImplication(antecedent, candidatePred);
if (val == Validity.VALID) {
result.add(candidate);
}
}
ipc.releaseLock();
}
private static boolean isFalse(final Set conjuncts) {
return conjuncts.size() == 1 && SmtUtils.isFalseLiteral(conjuncts.iterator().next());
}
}