org.sosy_lab.cpachecker.util.predicates.pathformula.PathFormulaManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.sosy_lab.cpachecker.util.predicates.pathformula.PathFormulaManagerImpl.java

Source

/*
 *  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.util.predicates.pathformula;

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

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.regex.Pattern;

import javax.annotation.Nullable;

import org.sosy_lab.common.Pair;
import org.sosy_lab.common.Triple;
import org.sosy_lab.common.configuration.Configuration;
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.log.LogManager;
import org.sosy_lab.cpachecker.cfa.CFA;
import org.sosy_lab.cpachecker.cfa.ast.c.CIdExpression;
import org.sosy_lab.cpachecker.cfa.model.AssumeEdge;
import org.sosy_lab.cpachecker.cfa.model.CFAEdge;
import org.sosy_lab.cpachecker.cfa.types.MachineModel;
import org.sosy_lab.cpachecker.cfa.types.c.CNumericTypes;
import org.sosy_lab.cpachecker.cfa.types.c.CType;
import org.sosy_lab.cpachecker.core.AnalysisDirection;
import org.sosy_lab.cpachecker.core.ShutdownNotifier;
import org.sosy_lab.cpachecker.core.counterexample.Model;
import org.sosy_lab.cpachecker.core.counterexample.Model.AssignableTerm;
import org.sosy_lab.cpachecker.core.counterexample.Model.TermType;
import org.sosy_lab.cpachecker.cpa.arg.ARGState;
import org.sosy_lab.cpachecker.cpa.predicate.PredicateAbstractState;
import org.sosy_lab.cpachecker.exceptions.CPATransferException;
import org.sosy_lab.cpachecker.exceptions.UnrecognizedCCodeException;
import org.sosy_lab.cpachecker.util.AbstractStates;
import org.sosy_lab.cpachecker.util.VariableClassification;
import org.sosy_lab.cpachecker.util.predicates.interfaces.BooleanFormula;
import org.sosy_lab.cpachecker.util.predicates.interfaces.Formula;
import org.sosy_lab.cpachecker.util.predicates.interfaces.FormulaType;
import org.sosy_lab.cpachecker.util.predicates.interfaces.PathFormulaManager;
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.interfaces.view.FunctionFormulaManagerView;
import org.sosy_lab.cpachecker.util.predicates.pathformula.SSAMap.SSAMapBuilder;
import org.sosy_lab.cpachecker.util.predicates.pathformula.ctoformula.CtoFormulaConverter;
import org.sosy_lab.cpachecker.util.predicates.pathformula.ctoformula.CtoFormulaTypeHandler;
import org.sosy_lab.cpachecker.util.predicates.pathformula.ctoformula.FormulaEncodingOptions;
import org.sosy_lab.cpachecker.util.predicates.pathformula.pointeraliasing.CToFormulaConverterWithPointerAliasing;
import org.sosy_lab.cpachecker.util.predicates.pathformula.pointeraliasing.FormulaEncodingWithPointerAliasingOptions;
import org.sosy_lab.cpachecker.util.predicates.pathformula.pointeraliasing.PointerTarget;
import org.sosy_lab.cpachecker.util.predicates.pathformula.pointeraliasing.PointerTargetSet;
import org.sosy_lab.cpachecker.util.predicates.pathformula.pointeraliasing.PointerTargetSetManager;
import org.sosy_lab.cpachecker.util.predicates.pathformula.pointeraliasing.TypeHandlerWithPointerAliasing;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Maps;

/**
 * Class implementing the FormulaManager interface,
 * providing some commonly used stuff which is independent from specific libraries.
 *
 * This class inherits from CtoFormulaConverter to import the stuff there.
 */
@Options(prefix = "cpa.predicate")
public class PathFormulaManagerImpl implements PathFormulaManager {

    @Option(secure = true, description = "Handle aliasing of pointers. "
            + "This adds disjunctions to the formulas, so be careful when using cartesian abstraction.")
    private boolean handlePointerAliasing = true;

    private static final String BRANCHING_PREDICATE_NAME = "__ART__";
    private static final Pattern BRANCHING_PREDICATE_NAME_PATTERN = Pattern
            .compile("^.*" + BRANCHING_PREDICATE_NAME + "(?=\\d+$)");

    private static final String NONDET_VARIABLE = "__nondet__";
    private static final String NONDET_FLAG_VARIABLE = NONDET_VARIABLE + "flag__";
    private static final CType NONDET_TYPE = CNumericTypes.INT;
    private final FormulaType<?> NONDET_FORMULA_TYPE;

    private final FormulaManagerView fmgr;
    private final BooleanFormulaManagerView bfmgr;
    private final FunctionFormulaManagerView ffmgr;
    private final CtoFormulaConverter converter;
    private final CtoFormulaTypeHandler typeHandler;
    private final @Nullable PointerTargetSetManager ptsManager;
    private final LogManager logger;
    private final ShutdownNotifier shutdownNotifier;

    @Option(secure = true, description = "add special information to formulas about non-deterministic functions")
    private boolean useNondetFlags = false;

    private final AnalysisDirection direction;

    @Deprecated
    public PathFormulaManagerImpl(FormulaManagerView pFmgr, Configuration config, LogManager pLogger,
            ShutdownNotifier pShutdownNotifier, MachineModel pMachineModel, AnalysisDirection pDirection)
            throws InvalidConfigurationException {

        this(pFmgr, config, pLogger, pShutdownNotifier, pMachineModel, Optional.<VariableClassification>absent(),
                pDirection);
    }

    public PathFormulaManagerImpl(FormulaManagerView pFmgr, Configuration config, LogManager pLogger,
            ShutdownNotifier pShutdownNotifier, CFA pCfa, AnalysisDirection pDirection)
            throws InvalidConfigurationException {

        this(pFmgr, config, pLogger, pShutdownNotifier, pCfa.getMachineModel(), pCfa.getVarClassification(),
                pDirection);
    }

    @VisibleForTesting
    PathFormulaManagerImpl(FormulaManagerView pFmgr, Configuration config, LogManager pLogger,
            ShutdownNotifier pShutdownNotifier, MachineModel pMachineModel,
            Optional<VariableClassification> pVariableClassification, AnalysisDirection pDirection)
            throws InvalidConfigurationException {

        config.inject(this, PathFormulaManagerImpl.class);

        fmgr = pFmgr;
        bfmgr = fmgr.getBooleanFormulaManager();
        ffmgr = fmgr.getFunctionFormulaManager();
        logger = pLogger;
        shutdownNotifier = pShutdownNotifier;

        direction = pDirection;

        if (handlePointerAliasing) {
            final FormulaEncodingWithPointerAliasingOptions options = new FormulaEncodingWithPointerAliasingOptions(
                    config);
            TypeHandlerWithPointerAliasing aliasingTypeHandler = new TypeHandlerWithPointerAliasing(pLogger,
                    pMachineModel, pFmgr, options);
            typeHandler = aliasingTypeHandler;
            ptsManager = new PointerTargetSetManager(options, fmgr, aliasingTypeHandler, shutdownNotifier);
            converter = createCToFormulaConverterWithPointerAliasing(options, pMachineModel, ptsManager,
                    pVariableClassification, aliasingTypeHandler);

        } else {
            final FormulaEncodingOptions options = new FormulaEncodingOptions(config);
            typeHandler = new CtoFormulaTypeHandler(pLogger, options, pMachineModel, pFmgr);
            converter = createCtoFormulaConverter(options, pMachineModel, pVariableClassification, typeHandler);
            ptsManager = null;

            logger.log(Level.WARNING,
                    "Handling of pointer aliasing is disabled, analysis is unsound if aliased pointers exist.");
        }

        NONDET_FORMULA_TYPE = converter.getFormulaTypeFromCType(NONDET_TYPE);
    }

    private CtoFormulaConverter createCtoFormulaConverter(FormulaEncodingOptions pOptions,
            MachineModel pMachineModel, Optional<VariableClassification> pVariableClassification,
            CtoFormulaTypeHandler pTypeHandler) {

        return new CtoFormulaConverter(pOptions, fmgr, pMachineModel, pVariableClassification, logger,
                shutdownNotifier, pTypeHandler, direction);
    }

    private CtoFormulaConverter createCToFormulaConverterWithPointerAliasing(
            FormulaEncodingWithPointerAliasingOptions pOptions, MachineModel pMachineModel,
            PointerTargetSetManager pPtsManager, Optional<VariableClassification> pVariableClassification,
            TypeHandlerWithPointerAliasing pAliasingTypeHandler) throws InvalidConfigurationException {

        return new CToFormulaConverterWithPointerAliasing(pOptions, fmgr, pMachineModel, pPtsManager,
                pVariableClassification, logger, shutdownNotifier, pAliasingTypeHandler, direction);
    }

    @Override
    public Pair<PathFormula, ErrorConditions> makeAndWithErrorConditions(PathFormula pOldFormula,
            final CFAEdge pEdge) throws CPATransferException, InterruptedException {
        ErrorConditions errorConditions = new ErrorConditions(bfmgr);
        PathFormula pf = makeAnd(pOldFormula, pEdge, errorConditions);

        return Pair.of(pf, errorConditions);
    }

    private PathFormula makeAnd(PathFormula pOldFormula, final CFAEdge pEdge, ErrorConditions errorConditions)
            throws CPATransferException, InterruptedException {
        PathFormula pf = converter.makeAnd(pOldFormula, pEdge, errorConditions);

        if (useNondetFlags) {
            SSAMapBuilder ssa = pf.getSsa().builder();

            int lNondetIndex = ssa.getIndex(NONDET_VARIABLE);
            int lFlagIndex = ssa.getIndex(NONDET_FLAG_VARIABLE);

            if (lNondetIndex != lFlagIndex) {
                if (lFlagIndex < 0) {
                    lFlagIndex = 1; // ssa indices start with 2, so next flag that is generated also uses index 2
                }

                BooleanFormula edgeFormula = pf.getFormula();

                for (int lIndex = lFlagIndex + 1; lIndex <= lNondetIndex; lIndex++) {
                    Formula nondetVar = fmgr.makeVariable(NONDET_FORMULA_TYPE, NONDET_FLAG_VARIABLE, lIndex);
                    BooleanFormula lAssignment = fmgr.assignment(nondetVar,
                            fmgr.makeNumber(NONDET_FORMULA_TYPE, 1));
                    edgeFormula = bfmgr.and(edgeFormula, lAssignment);
                }

                // update ssa index of nondet flag
                //setSsaIndex(ssa, Variable.create(NONDET_FLAG_VARIABLE, getNondetType()), lNondetIndex);
                ssa.setIndex(NONDET_FLAG_VARIABLE, NONDET_TYPE, lNondetIndex);

                pf = new PathFormula(edgeFormula, ssa.build(), pf.getPointerTargetSet(), pf.getLength());
            }
        }
        return pf;
    }

    @Override
    public PathFormula makeAnd(PathFormula pOldFormula, CFAEdge pEdge)
            throws CPATransferException, InterruptedException {
        ErrorConditions errorConditions = ErrorConditions.dummyInstance(bfmgr);
        return makeAnd(pOldFormula, pEdge, errorConditions);
    }

    @Override
    public PathFormula makeEmptyPathFormula() {
        return new PathFormula(bfmgr.makeBoolean(true), SSAMap.emptySSAMap(),
                PointerTargetSet.emptyPointerTargetSet(), 0);
    }

    @Override
    public PathFormula makeEmptyPathFormula(PathFormula oldFormula) {
        return new PathFormula(bfmgr.makeBoolean(true), oldFormula.getSsa(), oldFormula.getPointerTargetSet(), 0);
    }

    @Override
    public PathFormula makeNewPathFormula(PathFormula oldFormula, SSAMap m) {
        return new PathFormula(oldFormula.getFormula(), m, oldFormula.getPointerTargetSet(),
                oldFormula.getLength());
    }

    @Override
    public PathFormula makeOr(final PathFormula pathFormula1, final PathFormula pathFormula2)
            throws InterruptedException {

        final BooleanFormula formula1 = pathFormula1.getFormula();
        final BooleanFormula formula2 = pathFormula2.getFormula();
        final SSAMap ssa1 = pathFormula1.getSsa();
        final SSAMap ssa2 = pathFormula2.getSsa();

        final PointerTargetSet pts1 = pathFormula1.getPointerTargetSet();
        final PointerTargetSet pts2 = pathFormula2.getPointerTargetSet();

        final MergeResult<SSAMap> mergeSSAResult = mergeSSAMaps(ssa1, pts1, ssa2, pts2);
        final SSAMap newSSA = mergeSSAResult.getResult();

        final MergeResult<PointerTargetSet> mergePtsResult;
        if (ptsManager != null) {
            mergePtsResult = ptsManager.mergePointerTargetSets(pts1, pts2, newSSA);
        } else {
            mergePtsResult = MergeResult.trivial(pts1, bfmgr);
        }

        // (?) Do not swap these two lines, that makes a huge difference in performance (?) !
        final BooleanFormula newFormula1 = bfmgr.and(formula1,
                bfmgr.and(mergeSSAResult.getLeftConjunct(), mergePtsResult.getLeftConjunct()));
        final BooleanFormula newFormula2 = bfmgr.and(formula2,
                bfmgr.and(mergeSSAResult.getRightConjunct(), mergePtsResult.getRightConjunct()));
        final BooleanFormula newFormula = bfmgr.and(bfmgr.or(newFormula1, newFormula2),
                bfmgr.and(mergeSSAResult.getFinalConjunct(), mergePtsResult.getFinalConjunct()));
        final PointerTargetSet newPTS = mergePtsResult.getResult();
        final int newLength = Math.max(pathFormula1.getLength(), pathFormula2.getLength());

        return new PathFormula(newFormula, newSSA, newPTS, newLength);
    }

    @Override
    public PathFormula makeAnd(PathFormula pPathFormula, BooleanFormula pOtherFormula) {
        SSAMap ssa = pPathFormula.getSsa();
        BooleanFormula otherFormula = fmgr.instantiate(pOtherFormula, ssa);
        BooleanFormula resultFormula = bfmgr.and(pPathFormula.getFormula(), otherFormula);
        final PointerTargetSet pts = pPathFormula.getPointerTargetSet();
        return new PathFormula(resultFormula, ssa, pts, pPathFormula.getLength());
    }

    /**
     * Class representing the result of the operation of merging (disjuncting)
     * additional parts of {@link PathFormula}s beyond the actual formula.
     */
    public static class MergeResult<T> {

        private final BooleanFormula leftConjunct;
        private final BooleanFormula rightConjunct;
        private final BooleanFormula finalConjunct;

        private final T result;

        public MergeResult(T pResult, BooleanFormula pLeftConjunct, BooleanFormula pRightConjunct,
                BooleanFormula pFinalConjunct) {
            result = checkNotNull(pResult);
            leftConjunct = checkNotNull(pLeftConjunct);
            rightConjunct = checkNotNull(pRightConjunct);
            finalConjunct = checkNotNull(pFinalConjunct);
        }

        public static <T> MergeResult<T> trivial(T result, BooleanFormulaManagerView bfmgr) {
            BooleanFormula trueFormula = bfmgr.makeBoolean(true);
            return new MergeResult<>(result, trueFormula, trueFormula, trueFormula);
        }

        /**
         * This is a formula that needs to be conjuncted to the left formula
         * before it is used in the disjunction.
         */
        BooleanFormula getLeftConjunct() {
            return leftConjunct;
        }

        /**
         * This is a formula that needs to be conjuncted to the right formula
         * before it is used in the disjunction.
         */
        BooleanFormula getRightConjunct() {
            return rightConjunct;
        }

        /**
         * This is a formula that needs to be conjuncted to the result of the disjunction.
         */
        BooleanFormula getFinalConjunct() {
            return finalConjunct;
        }

        T getResult() {
            return result;
        }
    }

    /**
     * builds a formula that represents the necessary variable assignments
     * to "merge" the two ssa maps. That is, for every variable X that has two
     * different ssa indices i and j in the maps, creates a new formula
     * (X_k = X_i) | (X_k = X_j), where k is a fresh ssa index.
     * Returns the formula described above, plus a new SSAMap that is the merge
     * of the two.
     *
     * @param ssa1 an SSAMap
     * @param pts1 the PointerTargetSet for ssa1
     * @param ssa2 an SSAMap
     * @param pts2 the PointerTargetSet for ssa1
     * @return The new SSAMap and the formulas that need to be added to the path formulas before disjuncting them.
     */
    private MergeResult<SSAMap> mergeSSAMaps(final SSAMap ssa1, final PointerTargetSet pts1, final SSAMap ssa2,
            final PointerTargetSet pts2) throws InterruptedException {
        final Pair<SSAMap, List<Triple<String, Integer, Integer>>> ssaMergeResult = SSAMap.merge(ssa1, ssa2);
        final SSAMap resultSSA = ssaMergeResult.getFirst();
        final List<Triple<String, Integer, Integer>> symbolDifferences = ssaMergeResult.getSecond();

        BooleanFormula mergeFormula1 = bfmgr.makeBoolean(true);
        BooleanFormula mergeFormula2 = bfmgr.makeBoolean(true);

        for (final Triple<String, Integer, Integer> symbolDifference : symbolDifferences) {
            shutdownNotifier.shutdownIfNecessary();
            final String symbolName = symbolDifference.getFirst();
            final CType symbolType = resultSSA.getType(symbolName);
            final int index1 = firstNonNull(symbolDifference.getSecond(), 1);
            final int index2 = firstNonNull(symbolDifference.getThird(), 1);

            assert symbolName != null;
            if (index1 > index2 && index1 > 1) {
                // i2:smaller, i1:bigger
                // => need correction term for i2
                BooleanFormula mergeFormula = makeSsaMerger(symbolName, symbolType, index2, index1, pts2);

                mergeFormula2 = bfmgr.and(mergeFormula2, mergeFormula);

            } else if (index2 > 1) {
                assert index1 < index2;
                // i1:smaller, i2:bigger
                // => need correction term for i1
                BooleanFormula mergeFormula = makeSsaMerger(symbolName, symbolType, index1, index2, pts1);

                mergeFormula1 = bfmgr.and(mergeFormula1, mergeFormula);
            }
        }

        return new MergeResult<>(resultSSA, mergeFormula1, mergeFormula2, bfmgr.makeBoolean(true));
    }

    /**
     * Create the necessary equivalence terms for adjusting the SSA indices
     * of a given symbol (of any type) from oldIndex to newIndex.
     */
    private BooleanFormula makeSsaMerger(final String symbolName, final CType symbolType, final int oldIndex,
            final int newIndex, final PointerTargetSet oldPts) throws InterruptedException {
        assert oldIndex > 0;
        assert newIndex > oldIndex;

        // Important note:
        // we need to use fmgr.assignment in these methods,
        // because fmgr.equal has undesired semantics for floating points.

        if (useNondetFlags && symbolName.equals(NONDET_FLAG_VARIABLE)) {
            return makeSsaNondetFlagMerger(oldIndex, newIndex);

        } else if (CToFormulaConverterWithPointerAliasing.isUF(symbolName)) {
            assert symbolName.equals(CToFormulaConverterWithPointerAliasing.getUFName(symbolType));
            return makeSsaUFMerger(symbolName, symbolType, oldIndex, newIndex, oldPts);

        } else {
            return makeSsaVariableMerger(symbolName, symbolType, oldIndex, newIndex);
        }
    }

    private BooleanFormula makeSsaVariableMerger(final String variableName, final CType variableType,
            final int oldIndex, final int newIndex) {
        assert oldIndex < newIndex;

        // TODO Previously we called makeMerger,
        // which creates the terms (var@oldIndex = var@oldIndex+1; ...; var@oldIndex = var@newIndex).
        // Now we only create a single term (var@oldIndex = var@newIndex).
        // This should not make a difference except maybe for the model,
        // but this could be investigated to be sure.

        final FormulaType<?> variableFormulaType = converter.getFormulaTypeFromCType(variableType);
        final Formula oldVariable = fmgr.makeVariable(variableFormulaType, variableName, oldIndex);
        final Formula newVariable = fmgr.makeVariable(variableFormulaType, variableName, newIndex);

        return fmgr.assignment(newVariable, oldVariable);
    }

    private BooleanFormula makeSsaUFMerger(final String functionName, final CType returnType, final int oldIndex,
            final int newIndex, final PointerTargetSet pts) throws InterruptedException {
        assert oldIndex < newIndex;

        final FormulaType<?> returnFormulaType = converter.getFormulaTypeFromCType(returnType);
        BooleanFormula result = bfmgr.makeBoolean(true);
        for (final PointerTarget target : pts.getAllTargets(returnType)) {
            shutdownNotifier.shutdownIfNecessary();
            final Formula targetAddress = fmgr.makePlus(
                    fmgr.makeVariable(typeHandler.getPointerType(), target.getBaseName()),
                    fmgr.makeNumber(typeHandler.getPointerType(), target.getOffset()));

            final BooleanFormula retention = fmgr.assignment(
                    ffmgr.declareAndCallUninterpretedFunction(functionName, newIndex, returnFormulaType,
                            targetAddress),
                    ffmgr.declareAndCallUninterpretedFunction(functionName, oldIndex, returnFormulaType,
                            targetAddress));
            result = fmgr.makeAnd(result, retention);
        }

        return result;
    }

    private BooleanFormula makeSsaNondetFlagMerger(int iSmaller, int iBigger) {
        Formula pInitialValue = fmgr.makeNumber(NONDET_FORMULA_TYPE, 0);
        assert iSmaller < iBigger;

        BooleanFormula lResult = bfmgr.makeBoolean(true);
        FormulaType<Formula> type = fmgr.getFormulaType(pInitialValue);

        for (int i = iSmaller + 1; i <= iBigger; ++i) {
            Formula currentVar = fmgr.makeVariable(type, NONDET_FLAG_VARIABLE, i);
            BooleanFormula e = fmgr.assignment(currentVar, pInitialValue);
            lResult = bfmgr.and(lResult, e);
        }

        return lResult;
    }

    @Override
    public PathFormula makeFormulaForPath(List<CFAEdge> pPath) throws CPATransferException, InterruptedException {
        PathFormula pathFormula = makeEmptyPathFormula();
        for (CFAEdge edge : pPath) {
            pathFormula = makeAnd(pathFormula, edge);
        }
        return pathFormula;
    }

    /**
     * Build a formula containing a predicate for all branching situations in the
     * ARG. If a satisfying assignment is created for this formula, it can be used
     * to find out which paths in the ARG are feasible.
     *
     * This method may be called with an empty set, in which case it does nothing
     * and returns the formula "true".
     *
     * @param elementsOnPath The ARG states that should be considered.
     * @return A formula containing a predicate for each branching.
     */
    @Override
    public BooleanFormula buildBranchingFormula(Iterable<ARGState> elementsOnPath)
            throws CPATransferException, InterruptedException {
        // build the branching formula that will help us find the real error path
        BooleanFormula branchingFormula = bfmgr.makeBoolean(true);
        for (final ARGState pathElement : elementsOnPath) {

            if (pathElement.getChildren().size() > 1) {
                if (pathElement.getChildren().size() > 2) {
                    // can't create branching formula
                    if (from(pathElement.getChildren()).anyMatch(AbstractStates.IS_TARGET_STATE)) {
                        // We expect this situation of one of the children is a target state created by PredicateCPA.
                        continue;
                    } else {
                        logger.log(Level.WARNING, "ARG branching with more than two outgoing edges at ARG node "
                                + pathElement.getStateId() + ".");
                        return bfmgr.makeBoolean(true);
                    }
                }

                FluentIterable<CFAEdge> outgoingEdges = from(pathElement.getChildren())
                        .transform(new Function<ARGState, CFAEdge>() {
                            @Override
                            public CFAEdge apply(ARGState child) {
                                return pathElement.getEdgeToChild(child);
                            }
                        });
                if (!outgoingEdges.allMatch(Predicates.instanceOf(AssumeEdge.class))) {
                    if (from(pathElement.getChildren()).anyMatch(AbstractStates.IS_TARGET_STATE)) {
                        // We expect this situation of one of the children is a target state created by PredicateCPA.
                        continue;
                    } else {
                        logger.log(Level.WARNING,
                                "ARG branching without AssumeEdge at ARG node " + pathElement.getStateId() + ".");
                        return bfmgr.makeBoolean(true);
                    }
                }

                AssumeEdge edge = null;
                for (CFAEdge currentEdge : outgoingEdges) {
                    if (((AssumeEdge) currentEdge).getTruthAssumption()) {
                        edge = (AssumeEdge) currentEdge;
                        break;
                    }
                }
                assert edge != null;
                BooleanFormula pred = bfmgr.makeVariable(BRANCHING_PREDICATE_NAME + pathElement.getStateId(), 0);

                // create formula by edge, be sure to use the correct SSA indices!
                // TODO the class PathFormulaManagerImpl should not depend on PredicateAbstractState,
                // it is used without PredicateCPA as well.
                PathFormula pf;
                PredicateAbstractState pe = AbstractStates.extractStateByType(pathElement,
                        PredicateAbstractState.class);
                if (pe == null) {
                    logger.log(Level.WARNING, "Cannot find precise error path information without PredicateCPA");
                    return bfmgr.makeBoolean(true);
                } else {
                    pf = pe.getPathFormula();
                }
                pf = this.makeEmptyPathFormula(pf); // reset everything except SSAMap
                pf = this.makeAnd(pf, edge); // conjunct with edge

                BooleanFormula equiv = bfmgr.equivalence(pred, pf.getFormula());
                branchingFormula = bfmgr.and(branchingFormula, equiv);
            }
        }
        return branchingFormula;
    }

    /**
     * Extract the information about the branching predicates created by
     * {@link #buildBranchingFormula(Set)} from a satisfying assignment.
     *
     * A map is created that stores for each ARGState (using its element id as
     * the map key) which edge was taken (the positive or the negated one).
     *
     * @param model A satisfying assignment that should contain values for branching predicates.
     * @return A map from ARG state id to a boolean value indicating direction.
     */
    @Override
    public Map<Integer, Boolean> getBranchingPredicateValuesFromModel(Model model) {
        if (model.isEmpty()) {
            logger.log(Level.WARNING, "No satisfying assignment given by solver!");
            return Collections.emptyMap();
        }

        Map<Integer, Boolean> preds = Maps.newHashMap();
        for (Map.Entry<AssignableTerm, Object> entry : model.entrySet()) {
            AssignableTerm a = entry.getKey();
            if (a instanceof Model.Variable && a.getType() == TermType.Boolean) {

                String name = BRANCHING_PREDICATE_NAME_PATTERN.matcher(a.getName()).replaceFirst("");
                if (!name.equals(a.getName())) {
                    // pattern matched, so it's a variable with __ART__ in it

                    // no NumberFormatException because of RegExp match earlier
                    Integer nodeId = Integer.parseInt(name);

                    assert !preds.containsKey(nodeId);

                    preds.put(nodeId, (Boolean) entry.getValue());
                }
            }
        }
        return preds;
    }

    @Override
    public Formula expressionToFormula(PathFormula pFormula, CIdExpression expr, CFAEdge edge)
            throws UnrecognizedCCodeException {
        return converter.buildTermFromPathFormula(pFormula, expr, edge);
    }

}