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.value.refiner.utils; import static com.google.common.collect.Iterables.skip; import java.io.IOException; import java.util.ArrayDeque; import java.util.Deque; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import org.sosy_lab.common.Pair; import org.sosy_lab.common.Triple; import org.sosy_lab.common.configuration.InvalidConfigurationException; import org.sosy_lab.common.io.Files; import org.sosy_lab.common.io.Paths; import org.sosy_lab.cpachecker.cfa.ast.FileLocation; import org.sosy_lab.cpachecker.cfa.model.BlankEdge; import org.sosy_lab.cpachecker.cfa.model.CFAEdge; import org.sosy_lab.cpachecker.cfa.model.CFAEdgeType; import org.sosy_lab.cpachecker.cpa.arg.ARGPath; import org.sosy_lab.cpachecker.cpa.arg.ARGState; import org.sosy_lab.cpachecker.cpa.arg.MutableARGPath; import org.sosy_lab.cpachecker.util.LoopStructure; import org.sosy_lab.cpachecker.util.VariableClassification; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.collect.Iterables; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.SetMultimap; public class ErrorPathClassifier { private static final int BOOLEAN_VAR = 2; private static final int INTEQUAL_VAR = 4; private static final int UNKNOWN_VAR = 16; private static final int MAX_PREFIX_LENGTH = 1000; private static final String PREFIX_REPLACEMENT = ErrorPathClassifier.class.getSimpleName() + " replaced this assume edge in prefix"; private static final String SUFFIX_REPLACEMENT = ErrorPathClassifier.class.getSimpleName() + " replaced this assume edge in suffix"; private static int invocationCounter = 0; private final Optional<VariableClassification> classification; private final Optional<LoopStructure> loopStructure; public static enum ErrorPathPrefixPreference { // returns the original error path DEFAULT(), // sensible alternative options SHORTEST(), LONGEST(), // heuristics based on approximating cost via variable domain types DOMAIN_BEST_SHALLOW(FIRST_LOWEST_SCORE), DOMAIN_BEST_BOUNDED(FINAL_LOWEST_SCORE_BOUNDED), DOMAIN_BEST_DEEP( FINAL_LOWEST_SCORE), // heuristics based on approximating the depth of the refinement root REFINE_SHALLOW(FIRST_HIGHEST_SCORE), REFINE_DEEP(FINAL_LOWEST_SCORE), // use these only if you are feeling lucky RANDOM(), MEDIAN(), MIDDLE(), // use these if you want to go for a coffee or ten DOMAIN_WORST_SHALLOW(FIRST_HIGHEST_SCORE), DOMAIN_WORST_DEEP(FINAL_HIGHEST_SCORE); private ErrorPathPrefixPreference() { } private ErrorPathPrefixPreference(Function<Triple<Long, Long, Integer>, Boolean> scorer) { this.scorer = scorer; } private Function<Triple<Long, Long, Integer>, Boolean> scorer = INDIFFERENT_SCOREKEEPER; } private static final Function<Triple<Long, Long, Integer>, Boolean> INDIFFERENT_SCOREKEEPER = new Function<Triple<Long, Long, Integer>, Boolean>() { @Override public Boolean apply(Triple<Long, Long, Integer> prefixParameters) { return Boolean.TRUE; } }; private static final Function<Triple<Long, Long, Integer>, Boolean> FIRST_HIGHEST_SCORE = new Function<Triple<Long, Long, Integer>, Boolean>() { @Override public Boolean apply(Triple<Long, Long, Integer> prefixParameters) { return prefixParameters.getSecond() == null || prefixParameters.getFirst() > prefixParameters.getSecond(); } }; private static final Function<Triple<Long, Long, Integer>, Boolean> FINAL_HIGHEST_SCORE = new Function<Triple<Long, Long, Integer>, Boolean>() { @Override public Boolean apply(Triple<Long, Long, Integer> prefixParameters) { return prefixParameters.getSecond() == null || prefixParameters.getFirst() >= prefixParameters.getSecond(); } }; private static final Function<Triple<Long, Long, Integer>, Boolean> FIRST_LOWEST_SCORE = new Function<Triple<Long, Long, Integer>, Boolean>() { @Override public Boolean apply(Triple<Long, Long, Integer> prefixParameters) { return prefixParameters.getSecond() == null || prefixParameters.getFirst() < prefixParameters.getSecond(); } }; private static final Function<Triple<Long, Long, Integer>, Boolean> FINAL_LOWEST_SCORE = new Function<Triple<Long, Long, Integer>, Boolean>() { @Override public Boolean apply(Triple<Long, Long, Integer> prefixParameters) { return prefixParameters.getSecond() == null || prefixParameters.getFirst() <= prefixParameters.getSecond(); } }; private static final Function<Triple<Long, Long, Integer>, Boolean> FINAL_LOWEST_SCORE_BOUNDED = new Function<Triple<Long, Long, Integer>, Boolean>() { @Override public Boolean apply(Triple<Long, Long, Integer> prefixParameters) { if (prefixParameters.getSecond() == null) { return true; } else if (prefixParameters.getThird() < MAX_PREFIX_LENGTH) { return prefixParameters.getFirst() <= prefixParameters.getSecond(); } else { return prefixParameters.getFirst() < prefixParameters.getSecond(); } } }; public ErrorPathClassifier(Optional<VariableClassification> pClassification, Optional<LoopStructure> pLoopStructure) throws InvalidConfigurationException { classification = pClassification; loopStructure = pLoopStructure; } public ARGPath obtainPrefix(ErrorPathPrefixPreference preference, ARGPath errorPath, List<ARGPath> pPrefixes) { switch (preference) { case SHORTEST: return obtainShortestPrefix(pPrefixes, errorPath); case LONGEST: return obtainLongestPrefix(pPrefixes, errorPath); case DOMAIN_BEST_SHALLOW: case DOMAIN_BEST_BOUNDED: case DOMAIN_BEST_DEEP: case DOMAIN_WORST_SHALLOW: case DOMAIN_WORST_DEEP: return obtainDomainTypeHeuristicBasedPrefix(pPrefixes, preference, errorPath); case REFINE_SHALLOW: case REFINE_DEEP: return obtainRefinementRootHeuristicBasedPrefix(pPrefixes, preference, errorPath); case RANDOM: return obtainRandomPrefix(pPrefixes, errorPath); case MEDIAN: return obtainMedianPrefix(pPrefixes, errorPath); case MIDDLE: return obtainMiddlePrefix(pPrefixes, errorPath); default: return errorPath; } } private ARGPath obtainShortestPrefix(List<ARGPath> pPrefixes, ARGPath originalErrorPath) { return buildPath(0, pPrefixes, originalErrorPath); } private ARGPath obtainLongestPrefix(List<ARGPath> pPrefixes, ARGPath originalErrorPath) { return buildPath(pPrefixes.size() - 1, pPrefixes, originalErrorPath); } private ARGPath obtainDomainTypeHeuristicBasedPrefix(List<ARGPath> pPrefixes, ErrorPathPrefixPreference preference, ARGPath originalErrorPath) { if (!classification.isPresent()) { return concatPrefixes(pPrefixes); } MutableARGPath currentErrorPath = new MutableARGPath(); Long bestScore = null; int bestIndex = 0; for (ARGPath currentPrefix : pPrefixes) { assert (Iterables.getLast(currentPrefix.asEdgesList()).getEdgeType() == CFAEdgeType.AssumeEdge); currentErrorPath.addAll(pathToList(currentPrefix)); Set<String> useDefinitionInformation = obtainUseDefInformationOfErrorPath(currentErrorPath); Long score = obtainDomainTypeScoreForVariables(useDefinitionInformation); if (preference.scorer.apply(Triple.of(score, bestScore, currentErrorPath.size()))) { bestScore = score; bestIndex = pPrefixes.indexOf(currentPrefix); } } return buildPath(bestIndex, pPrefixes, originalErrorPath); } private ARGPath obtainRefinementRootHeuristicBasedPrefix(List<ARGPath> pPrefixes, ErrorPathPrefixPreference preference, ARGPath originalErrorPath) { if (!classification.isPresent()) { return concatPrefixes(pPrefixes); } MutableARGPath currentErrorPath = new MutableARGPath(); Long bestScore = null; int bestIndex = 0; for (ARGPath currentPrefix : pPrefixes) { assert (Iterables.getLast(currentPrefix.asEdgesList()).getEdgeType() == CFAEdgeType.AssumeEdge); currentErrorPath.addAll(pathToList(currentPrefix)); // gets the score for the prefix of how "local" it is AssumptionUseDefinitionCollector collector = new InitialAssumptionUseDefinitionCollector(); collector.obtainUseDefInformation(currentErrorPath); Long score = Long.valueOf(collector.getDependenciesResolvedOffset() * (-1)); if (preference.scorer.apply(Triple.of(score, bestScore, currentErrorPath.size()))) { bestScore = score; bestIndex = pPrefixes.indexOf(currentPrefix); } } return buildPath(bestIndex, pPrefixes, originalErrorPath); } // not really a sensible heuristic at all, just here for comparison reasons private ARGPath obtainRandomPrefix(List<ARGPath> pPrefixes, ARGPath originalErrorPath) { return buildPath(new Random().nextInt(pPrefixes.size()), pPrefixes, originalErrorPath); } // not really a sensible heuristic at all, just here for comparison reasons private ARGPath obtainMedianPrefix(List<ARGPath> pPrefixes, ARGPath originalErrorPath) { return buildPath(pPrefixes.size() / 2, pPrefixes, originalErrorPath); } // not really a sensible heuristic at all, just here for comparison reasons private ARGPath obtainMiddlePrefix(List<ARGPath> pPrefixes, ARGPath originalErrorPath) { int totalLength = 0; for (ARGPath p : pPrefixes) { totalLength += p.size(); } int length = 0; int index = 0; for (ARGPath p : pPrefixes) { length += p.size(); if (length > totalLength / 2) { break; } index++; } return buildPath(index, pPrefixes, originalErrorPath); } private Set<String> obtainUseDefInformationOfErrorPath(MutableARGPath currentErrorPath) { return new InitialAssumptionUseDefinitionCollector().obtainUseDefInformation(currentErrorPath); } private Long obtainDomainTypeScoreForVariables(Set<String> useDefinitionInformation) { Long domainTypeScore = 1L; for (String variableName : useDefinitionInformation) { int factor = UNKNOWN_VAR; if (classification.get().getIntBoolVars().contains(variableName)) { factor = BOOLEAN_VAR; } else if (classification.get().getIntEqualVars().contains(variableName)) { factor = INTEQUAL_VAR; } domainTypeScore = domainTypeScore * factor; if (loopStructure.isPresent() && loopStructure.get().getLoopIncDecVariables().contains(variableName)) { domainTypeScore = domainTypeScore + Integer.MAX_VALUE; } } return domainTypeScore; } /** * This methods builds a new path from the given prefixes. It makes all * contradicting assume edge but the last ineffective, so that only the last * assumption leads to a contradiction. * * @param bestIndex the index of the prefix with the best score * @param pPrefixes the list of prefixes * @param originalErrorPath the original error path * @return a new path with the last assumption leading to a contradiction */ private ARGPath buildPath(final int bestIndex, final List<ARGPath> pPrefixes, final ARGPath originalErrorPath) { MutableARGPath errorPath = new MutableARGPath(); for (int j = 0; j <= bestIndex; j++) { errorPath.addAll(pathToList(pPrefixes.get(j))); if (j != bestIndex) { replaceAssumeEdgeWithBlankEdge(errorPath); } } // append the feasible suffix; nevertheless, replace assume edges, because they // might contradict in other domains, e.g. for octagons, or predicate analysis for (Pair<ARGState, CFAEdge> element : skip(pathToList(originalErrorPath), errorPath.size())) { if (element.getSecond().getEdgeType() == CFAEdgeType.AssumeEdge && element.getFirst() != originalErrorPath.getLastState()) { errorPath.add(Pair.<ARGState, CFAEdge>of(element.getFirst(), new BlankEdge("", FileLocation.DUMMY, element.getSecond().getPredecessor(), element.getSecond().getSuccessor(), SUFFIX_REPLACEMENT))); } else { errorPath.add(element); } } return errorPath.immutableCopy(); } public static List<Pair<ARGState, CFAEdge>> pathToList(ARGPath path) { return Pair.zipList(path.asStatesList(), path.asEdgesList()); } /** * This method replaces the final (assume) edge of each prefix, except for the * last, with a blank edge, and as such, avoiding a contradiction along that * path at the removed assumptions. * * @param pErrorPath the error path from which to remove the final assume edge */ private void replaceAssumeEdgeWithBlankEdge(final MutableARGPath pErrorPath) { Pair<ARGState, CFAEdge> assumeState = pErrorPath.removeLast(); assert (assumeState.getSecond().getEdgeType() == CFAEdgeType.AssumeEdge); pErrorPath.add(Pair.<ARGState, CFAEdge>of(assumeState.getFirst(), new BlankEdge("", FileLocation.DUMMY, assumeState.getSecond().getPredecessor(), assumeState.getSecond().getSuccessor(), PREFIX_REPLACEMENT))); } private ARGPath concatPrefixes(List<ARGPath> pPrefixes) { MutableARGPath errorPath = new MutableARGPath(); for (ARGPath currentPrefix : pPrefixes) { errorPath.addAll(pathToList(currentPrefix)); } return errorPath.immutableCopy(); } /** * This method export the current error path, visualizing the individual prefixes. * * @param errorPath the original error path * @param pPrefixes the list of prefixes */ @SuppressWarnings("unused") private void exportToDot(MutableARGPath errorPath, List<MutableARGPath> pPrefixes) { SetMultimap<ARGState, ARGState> successorRelation = buildSuccessorRelation(errorPath.getLast().getFirst()); Set<ARGState> failingStates = new HashSet<>(); for (MutableARGPath path : pPrefixes) { failingStates.add(path.getLast().getFirst()); } int assertFailCnt = failingStates.size(); StringBuilder result = new StringBuilder().append("digraph tree {" + "\n"); for (Map.Entry<ARGState, ARGState> current : successorRelation.entries()) { result.append( current.getKey().getStateId() + " [label=\"" + current.getKey().getStateId() + "\"]" + "\n"); result.append(current.getKey().getStateId() + " -> " + current.getValue().getStateId() + "\n"); CFAEdge edge = current.getKey().getEdgeToChild(current.getValue()); if (failingStates.contains(current.getValue())) { result.append( current.getKey().getStateId() + " [shape=diamond, style=filled, fillcolor=\"red\"]" + "\n"); result.append(current.getKey().getStateId() + " -> stop" + assertFailCnt + "\n"); result.append("stop" + assertFailCnt + " [shape=point]\n"); assertFailCnt--; } else if (edge.getEdgeType() == CFAEdgeType.AssumeEdge) { result.append(current.getKey().getStateId() + " [shape=diamond]" + "\n"); } assert (!current.getKey().isTarget()); } result.append("}"); try { Files.writeFile(Paths.get("output/itpPaths" + (invocationCounter++) + ".dot"), result.toString()); } catch (IOException e) { throw new IllegalArgumentException(); } } /** * This method creates a successor relation from the root to the target state. * * @param target the state to which the successor relation should be built. * @return the successor relation from the root state to the given target state */ private SetMultimap<ARGState, ARGState> buildSuccessorRelation(ARGState target) { Deque<ARGState> todo = new ArrayDeque<>(); todo.add(target); ARGState itpTreeRoot = null; SetMultimap<ARGState, ARGState> successorRelation = LinkedHashMultimap.create(); // build the tree, bottom-up, starting from the target states while (!todo.isEmpty()) { final ARGState currentState = todo.removeFirst(); if (currentState.getParents().iterator().hasNext()) { ARGState parentState = currentState.getParents().iterator().next(); todo.add(parentState); successorRelation.put(parentState, currentState); } else if (itpTreeRoot == null) { itpTreeRoot = currentState; } } return successorRelation; } }