org.sosy_lab.cpachecker.util.predicates.interpolation.CexTraceAnalysisDirection.java Source code

Java tutorial

Introduction

Here is the source code for org.sosy_lab.cpachecker.util.predicates.interpolation.CexTraceAnalysisDirection.java

Source

/*
 *  CPAchecker is a tool for configurable software verification.
 *  This file is part of CPAchecker.
 *
 *  Copyright (C) 2007-2015  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.util.predicates.interpolation;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.FluentIterable.from;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.sosy_lab.common.Pair;
import org.sosy_lab.common.Triple;
import org.sosy_lab.cpachecker.cfa.model.CFANode;
import org.sosy_lab.cpachecker.core.interfaces.AbstractState;
import org.sosy_lab.cpachecker.cpa.arg.ARGState;
import org.sosy_lab.cpachecker.util.AbstractStates;
import org.sosy_lab.cpachecker.util.LoopStructure;
import org.sosy_lab.cpachecker.util.LoopStructure.Loop;
import org.sosy_lab.cpachecker.util.VariableClassification;
import org.sosy_lab.solver.api.BooleanFormula;
import org.sosy_lab.cpachecker.util.predicates.interfaces.view.FormulaManagerView;

import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.TreeMultimap;

enum CexTraceAnalysisDirection {
    /**
     * Just the trace as it is
     */
    FORWARDS {
        @Override
        public ImmutableList<Triple<BooleanFormula, AbstractState, Integer>> orderFormulas(
                List<BooleanFormula> traceFormulas, List<AbstractState> abstractionStates,
                VariableClassification pVariableClassification, LoopStructure pLoopStructure,
                FormulaManagerView pFmgr) {
            Builder<Triple<BooleanFormula, AbstractState, Integer>> orderedFormulas = ImmutableList.builder();
            for (int i = 0; i < traceFormulas.size(); i++) {
                orderedFormulas.add(Triple.of(traceFormulas.get(i), abstractionStates.get(i), i));
            }
            return orderedFormulas.build();
        }
    },

    /**
     * The trace when traversed backwards
     */
    BACKWARDS {
        @Override
        public ImmutableList<Triple<BooleanFormula, AbstractState, Integer>> orderFormulas(
                List<BooleanFormula> traceFormulas, List<AbstractState> abstractionStates,
                VariableClassification pVariableClassification, LoopStructure pLoopStructure,
                FormulaManagerView pFmgr) {
            Builder<Triple<BooleanFormula, AbstractState, Integer>> orderedFormulas = ImmutableList.builder();
            for (int i = traceFormulas.size() - 1; i >= 0; i--) {
                orderedFormulas.add(Triple.of(traceFormulas.get(i), abstractionStates.get(i), i));
            }
            return orderedFormulas.build();
        }
    },

    /**
     * Takes alternatingly one element from the front of the trace and one of
     * the back
     */
    ZIGZAG {
        @Override
        public ImmutableList<Triple<BooleanFormula, AbstractState, Integer>> orderFormulas(
                List<BooleanFormula> traceFormulas, List<AbstractState> abstractionStates,
                VariableClassification pVariableClassification, LoopStructure pLoopStructure,
                FormulaManagerView pFmgr) {
            Builder<Triple<BooleanFormula, AbstractState, Integer>> orderedFormulas = ImmutableList.builder();
            int e = traceFormulas.size() - 1;
            int s = 0;
            boolean fromStart = false;
            while (s <= e) {
                int i = fromStart ? s++ : e--;
                fromStart = !fromStart;

                orderedFormulas.add(Triple.of(traceFormulas.get(i), abstractionStates.get(i), i));
            }
            return orderedFormulas.build();
        }
    },

    /**
     * Those parts of the trace that are in no loops or in less loops than
     * others are sorted to the front
     */
    LOOP_FREE_FIRST {
        @Override
        public ImmutableList<Triple<BooleanFormula, AbstractState, Integer>> orderFormulas(
                List<BooleanFormula> traceFormulas, List<AbstractState> abstractionStates,
                VariableClassification pVariableClassification, LoopStructure pLoopStructure,
                FormulaManagerView pFmgr) {
            Builder<Triple<BooleanFormula, AbstractState, Integer>> orderedFormulas = ImmutableList.builder();
            Multimap<Integer, AbstractState> stateOrdering = LinkedHashMultimap.create();
            createLoopDrivenStateOrdering(abstractionStates, stateOrdering, new ArrayDeque<CFANode>(),
                    checkNotNull(pLoopStructure));

            for (int i = 0; stateOrdering.containsKey(i); i++) {
                Collection<AbstractState> states = stateOrdering.get(i);
                for (AbstractState state : states) {
                    int id = abstractionStates.indexOf(state);
                    orderedFormulas.add(Triple.of(traceFormulas.get(id), state, id));
                }
            }
            return orderedFormulas.build();
        }
    },

    /**
     * A random order of the trace
     */
    RANDOM {
        @Override
        public ImmutableList<Triple<BooleanFormula, AbstractState, Integer>> orderFormulas(
                List<BooleanFormula> traceFormulas, List<AbstractState> abstractionStates,
                VariableClassification pVariableClassification, LoopStructure pLoopStructure,
                FormulaManagerView pFmgr) {
            Builder<Triple<BooleanFormula, AbstractState, Integer>> orderedFormulas = ImmutableList.builder();
            List<AbstractState> stateList = new ArrayList<>(abstractionStates);
            Collections.shuffle(stateList);

            for (int i = 0; i < traceFormulas.size(); i++) {
                AbstractState state = stateList.get(i);
                int oldIndex = abstractionStates.indexOf(state);
                orderedFormulas.add(Triple.of(traceFormulas.get(oldIndex), state, oldIndex));
            }
            return orderedFormulas.build();
        }
    },

    /**
     * Formulas with the lowest average score for their variables according
     * to some calculations in the VariableClassification are sorted to the front
     */
    LOWEST_AVG_SCORE {
        @Override
        public ImmutableList<Triple<BooleanFormula, AbstractState, Integer>> orderFormulas(
                List<BooleanFormula> traceFormulas, List<AbstractState> abstractionStates,
                VariableClassification pVariableClassification, LoopStructure pLoopStructure,
                FormulaManagerView pFmgr) {
            Builder<Triple<BooleanFormula, AbstractState, Integer>> orderedFormulas = ImmutableList.builder();
            Multimap<Double, Integer> sortedFormulas = TreeMultimap.create();

            for (BooleanFormula formula : traceFormulas) {
                if (!sortedFormulas.put(getAVGScoreForVariables(formula, checkNotNull(pVariableClassification),
                        checkNotNull(pFmgr), checkNotNull(pLoopStructure)), traceFormulas.indexOf(formula))) {
                    throw new AssertionError("Bug in creation of sorted formulas.");
                }
            }

            for (Integer index : sortedFormulas.values()) {
                orderedFormulas.add(Triple.of(traceFormulas.get(index), abstractionStates.get(index), index));
            }
            return orderedFormulas.build();
        }
    },

    /**
     * Formulas with the highest average score for their variables according
     * to some calculations in the VariableClassification are sorted to the front
     */
    HIGHEST_AVG_SCORE {
        @Override
        public ImmutableList<Triple<BooleanFormula, AbstractState, Integer>> orderFormulas(
                List<BooleanFormula> traceFormulas, List<AbstractState> abstractionStates,
                VariableClassification pVariableClassification, LoopStructure pLoopStructure,
                FormulaManagerView pFmgr) {
            Builder<Triple<BooleanFormula, AbstractState, Integer>> orderedFormulas = ImmutableList.builder();
            Multimap<Double, Integer> sortedFormulas = TreeMultimap.create();

            for (BooleanFormula formula : traceFormulas) {
                if (!sortedFormulas.put(
                        Double.MAX_VALUE - getAVGScoreForVariables(formula, checkNotNull(pVariableClassification),
                                checkNotNull(pFmgr), checkNotNull(pLoopStructure)),
                        traceFormulas.indexOf(formula))) {
                    throw new AssertionError("Bug in creation of sorted formulas.");
                }
            }

            for (Integer index : sortedFormulas.values()) {
                orderedFormulas.add(Triple.of(traceFormulas.get(index), abstractionStates.get(index), index));
            }
            return orderedFormulas.build();
        }
    },

    /**
     * Combination of loop free first and backwards, for each loop level, we iterate
     * backwards through the found formulas to have those that are closest to
     * the error location at first.
     */
    LOOP_FREE_FIRST_BACKWARDS {
        @Override
        public ImmutableList<Triple<BooleanFormula, AbstractState, Integer>> orderFormulas(
                List<BooleanFormula> traceFormulas, List<AbstractState> abstractionStates,
                VariableClassification pVariableClassification, LoopStructure pLoopStructure,
                FormulaManagerView pFmgr) {
            Builder<Triple<BooleanFormula, AbstractState, Integer>> orderedFormulas = ImmutableList.builder();
            Multimap<Integer, AbstractState> stateOrdering = LinkedHashMultimap.create();
            createLoopDrivenStateOrdering(abstractionStates, stateOrdering, new ArrayDeque<CFANode>(),
                    checkNotNull(pLoopStructure));

            for (int i = 0; stateOrdering.containsKey(i); i++) {
                Collection<AbstractState> stateSet = stateOrdering.get(i);
                AbstractState[] stateArray = new AbstractState[stateSet.size()];
                stateSet.toArray(stateArray);
                for (int j = stateArray.length - 1; j >= 0; j--) {
                    int id = abstractionStates.indexOf(stateArray[j]);
                    orderedFormulas.add(Triple.of(traceFormulas.get(id), stateArray[j], id));
                }
            }
            return orderedFormulas.build();
        }
    };

    /**
     * This method reorders the given formulas such that their order applies to the
     * name of the given enum constant.
     * @return The reordered formulas
     */
    public abstract ImmutableList<Triple<BooleanFormula, AbstractState, Integer>> orderFormulas(
            List<BooleanFormula> traceFormulas, List<AbstractState> abstractionStates,
            VariableClassification variableClassification, LoopStructure loopStructure, FormulaManagerView fmgr);

    /**
     * This method computes a score for a set of variables regarding the domain
     * types of these variables.
     * @param variableNames the variables that should be scored
     * @return the average score over all given variables
     */
    private static double getAVGScoreForVariables(BooleanFormula formula,
            VariableClassification variableClassification, FormulaManagerView fmgr, LoopStructure loopStructure) {

        Set<String> varNames = from(fmgr.extractVariableNames(formula)).transform(new Function<String, String>() {
            @Override
            public String apply(String pInput) {
                Pair<String, Integer> name = FormulaManagerView.parseName(pInput);

                // we want only variables to be in our set, and ignore everything without SSA index
                if (name.getSecond() != null) {
                    return name.getFirst();
                } else {
                    return null;
                }
            }
        }).filter(Predicates.notNull()).toSet();

        double currentScore = 0;
        for (String variableName : varNames) {

            // best, easy variables
            if (variableClassification.getIntBoolVars().contains(variableName)) {
                currentScore += 2;

                // little harder but still good variables
            } else if (variableClassification.getIntEqualVars().contains(variableName)) {
                currentScore += 4;

                // unknown type, potentially much harder than other variables
            } else {
                currentScore += 16;
            }

            // a loop counter variables, really bad for interpolants
            if (loopStructure.getLoopIncDecVariables().contains(variableName)) {
                currentScore += 100;
            }

            // check for overflow
            if (currentScore < 0) {
                return Double.MAX_VALUE / varNames.size();
            }
        }

        // this is a true or false formula, return 0 as this is the easiest formula
        // we can encounter
        if (varNames.size() == 0) {
            return 0;
        } else {
            return currentScore / varNames.size();
        }
    }

    private static void createLoopDrivenStateOrdering(final List<AbstractState> pAbstractionStates,
            final Multimap<Integer, AbstractState> loopLevelsToStatesMap, Deque<CFANode> actLevelStack,
            LoopStructure loopStructure) {
        ImmutableSet<CFANode> loopHeads = loopStructure.getAllLoopHeads();

        // in the nodeLoopLevel map there has to be for every seen ARGState one
        // key-value pair therefore we can use this as our index
        int actARGState = loopLevelsToStatesMap.size();

        AbstractState actState = null;
        CFANode actCFANode = null;

        boolean isCFANodeALoopHead = false;

        // move on as long as there occurs no loop-head in the ARG path
        while (!isCFANodeALoopHead && actLevelStack.isEmpty() && actARGState < pAbstractionStates.size()) {

            actState = pAbstractionStates.get(actARGState);
            actCFANode = AbstractStates.EXTRACT_LOCATION.apply(actState);

            loopLevelsToStatesMap.put(0, actState);

            isCFANodeALoopHead = loopHeads.contains(actCFANode);

            actARGState++;
        }

        // when not finished with computing the node levels
        if (actARGState != pAbstractionStates.size()) {
            actLevelStack.push(actCFANode);
            createLoopDrivenStateOrdering0(pAbstractionStates, loopLevelsToStatesMap, actLevelStack, loopStructure);
        }
    }

    private static void createLoopDrivenStateOrdering0(final List<AbstractState> pAbstractionStates,
            final Multimap<Integer, AbstractState> loopLevelsToStatesMap, Deque<CFANode> actLevelStack,
            LoopStructure loopStructure) {

        // we are finished with the computation
        if (loopLevelsToStatesMap.size() == pAbstractionStates.size()) {
            return;
        }

        AbstractState lastState = pAbstractionStates.get(loopLevelsToStatesMap.size() - 1);
        AbstractState actState = pAbstractionStates.get(loopLevelsToStatesMap.size());
        CFANode actCFANode = AbstractStates.EXTRACT_LOCATION.apply(actState);

        Iterator<CFANode> it = actLevelStack.descendingIterator();
        while (it.hasNext()) {
            CFANode lastLoopNode = it.next();

            // check if the functions match, if yes we can simply check if the node
            // is in the loop on this level, if not we have to check the functions entry
            // point, in order to know if the current node is in the loop on this
            // level or on a lower one
            if (actCFANode.getFunctionName().equals(lastLoopNode.getFunctionName())) {
                actCFANode = getPrevFunctionNode((ARGState) actState, (ARGState) lastState,
                        lastLoopNode.getFunctionName());
            }

            // the lastLoopNode cannot be reached from the actState
            // so decrease the actLevelStack
            if (actCFANode == null || !isNodePartOfLoop(lastLoopNode, actCFANode, loopStructure)) {
                it.remove();
                continue;

                // we have a valid path to the function of the lastLoopNode
            } else {
                loopLevelsToStatesMap.put(actLevelStack.size(), actState);

                // node itself is a loophead, too, so add it also to the levels stack
                if (loopStructure.getAllLoopHeads().contains(actCFANode)) {
                    actLevelStack.push(actCFANode);
                }
                createLoopDrivenStateOrdering0(pAbstractionStates, loopLevelsToStatesMap, actLevelStack,
                        loopStructure);
                return;
            }
        }

        // coming here is possible only if the stack is empty and no matching
        // loop for the current node was found
        createLoopDrivenStateOrdering(pAbstractionStates, loopLevelsToStatesMap, actLevelStack, loopStructure);
    }

    private static boolean isNodePartOfLoop(CFANode loopHead, CFANode potentialLoopNode,
            LoopStructure loopStructure) {
        for (Loop loop : loopStructure.getLoopsForLoopHead(loopHead)) {
            if (loop.getLoopNodes().contains(potentialLoopNode)) {
                return true;
            }
        }
        return false;
    }

    private static CFANode getPrevFunctionNode(ARGState argState, ARGState lastState, String wantedFunction) {
        CFANode returnNode = AbstractStates.EXTRACT_LOCATION.apply(argState);
        while (!returnNode.getFunctionName().equals(wantedFunction)) {
            argState = argState.getParents().iterator().next();

            // the function does not return to the wanted function we can skip the search
            // here
            if (argState == lastState.getParents().iterator().next()) {
                return null;
            }

            returnNode = AbstractStates.EXTRACT_LOCATION.apply(argState);
        }

        return returnNode;
    }

}