org.sosy_lab.cpachecker.cpa.bdd.BDDPartitionOrderer.java Source code

Java tutorial

Introduction

Here is the source code for org.sosy_lab.cpachecker.cpa.bdd.BDDPartitionOrderer.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.cpa.bdd;

import static org.sosy_lab.cpachecker.util.CFAUtils.leavingEdges;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.sosy_lab.cpachecker.cfa.CFA;
import org.sosy_lab.cpachecker.cfa.model.CFAEdge;
import org.sosy_lab.cpachecker.cfa.model.CFAEdgeType;
import org.sosy_lab.cpachecker.cfa.model.CFANode;
import org.sosy_lab.cpachecker.cfa.model.FunctionCallEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CAssumeEdge;
import org.sosy_lab.cpachecker.util.CFATraversal;
import org.sosy_lab.cpachecker.util.CFATraversal.CFAVisitor;
import org.sosy_lab.cpachecker.util.CFATraversal.DefaultCFAVisitor;
import org.sosy_lab.cpachecker.util.CFATraversal.TraversalProcess;
import org.sosy_lab.cpachecker.util.VariableClassification;
import org.sosy_lab.cpachecker.util.VariableClassification.Partition;

import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;

/** This Visitor collects a graph of dependencies of partitions.
 * They can be used for ordering variables in the BDD. */
public class BDDPartitionOrderer {

    /** this graph contains all dependencies between partitions */
    private Multimap<Partition, Partition> graph = LinkedHashMultimap.create();

    private VariableClassification varClass;

    public BDDPartitionOrderer(CFA cfa) {
        assert cfa.getVarClassification().isPresent();
        this.varClass = cfa.getVarClassification().get();

        // collect assumption, they are split-points
        CFAAssumptionCollector aCol = new CFAAssumptionCollector();
        CFATraversal.dfs().ignoreSummaryEdges().traverseOnce(cfa.getMainFunction(), aCol);
        Collection<CAssumeEdge> assumptions = aCol.getAssumptions();

        for (CAssumeEdge ass : assumptions) {
            collectDependentPartitions(ass);
        }
    }

    /** This function collects some edges, that are dependent from the assumption,
     * and puts them into the graph. */
    private void collectDependentPartitions(CAssumeEdge assumption) {
        CFANode root = assumption.getPredecessor();
        assert root.getNumLeavingEdges() == 2 : "assumption must have 2 branches.";

        CFAEdge ass1 = root.getLeavingEdge(0);
        CFAEdge ass2 = root.getLeavingEdge(1);
        assert ass1 == assumption || ass2 == assumption;

        Partition assPartition = varClass.getPartitionForEdge(ass1);
        assert varClass.getPartitionForEdge(ass2) == assPartition;

        if (assPartition == null) {
            return;
        } // assumption is like "3==4"

        // left branch
        CFAUntilSplitCollector fCol1 = new CFAUntilSplitCollector();
        CFATraversal.dfs().ignoreSummaryEdges().traverseOnce(ass1.getSuccessor(), fCol1);
        Set<CFAEdge> reachable1 = fCol1.getEdges();

        // right branch
        CFAUntilSplitCollector fCol2 = new CFAUntilSplitCollector();
        CFATraversal.dfs().ignoreSummaryEdges().traverseOnce(ass2.getSuccessor(), fCol2);
        Set<CFAEdge> reachable2 = fCol2.getEdges();

        // get edges, that are either in left or in right branch.
        // edges, that are reachable from both branches, are independent from the assumption
        SetView<CFAEdge> distinctEdges = Sets.symmetricDifference(reachable1, reachable2);
        for (CFAEdge edge : distinctEdges) {
            if (edge instanceof FunctionCallEdge) {
                final FunctionCallEdge funcCall = (FunctionCallEdge) edge;
                for (int i = 0; i < funcCall.getArguments().size(); i++) {
                    final Partition part = varClass.getPartitionForParameterOfEdge(funcCall, i);
                    if (part != null) {
                        graph.put(assPartition, part);
                    }
                }
            } else {
                final Partition part = varClass.getPartitionForEdge(edge);
                if (part != null) {
                    graph.put(assPartition, part);
                }
            }
        }
    }

    /** returns a ordered list of partitions, so that the BDD stays small. */
    public List<Partition> getOrderedPartitions() {

        // TODO use some "Minimum Linear Arrangement Algorithm"?

        Collection<Partition> partitions = Sets.newLinkedHashSet();

        for (Partition p : graph.keySet()) {
            addToPartitions(p, partitions);
        }

        List<Partition> orderedPartitions = Lists.newLinkedList(partitions);

        // add partitions, that are not dependent, in front of all other partitions
        for (Partition p : varClass.getPartitions()) {
            if (!partitions.contains(p)) {
                orderedPartitions.add(0, p);
            }
        }
        return orderedPartitions;
    }

    /** adds the father and all his children to the partitions,if not done before */
    private void addToPartitions(Partition father, Collection<Partition> partitions) {
        if (!partitions.contains(father)) {
            partitions.add(father);
            for (Partition child : graph.get(father)) {
                addToPartitions(child, partitions);
            }
        }
    }

    /** This Visitor collects all edges reachable from the a node
     * until a split-point (assumption) is reached.
     * The leaving edges of the splitpoint are cllected, too. */
    private static class CFAUntilSplitCollector implements CFAVisitor {

        private Set<CFAEdge> edges = new LinkedHashSet<>();

        public Set<CFAEdge> getEdges() {
            return edges;
        }

        @Override
        public TraversalProcess visitEdge(CFAEdge pEdge) {
            edges.add(pEdge);
            return TraversalProcess.CONTINUE;
        }

        @Override
        public TraversalProcess visitNode(CFANode pNode) {
            int numChildren = pNode.getNumLeavingEdges();
            if (numChildren > 1) { // split-point
                leavingEdges(pNode).copyInto(edges);
                return TraversalProcess.SKIP;

            } else {
                return TraversalProcess.CONTINUE;
            }
        }
    }

    /** This Visitor collects all assumptionEdges from the CFA.
     * For each assumption only the true-edge is collected. */
    private class CFAAssumptionCollector extends DefaultCFAVisitor {

        private Collection<CAssumeEdge> assumptions = new ArrayList<>();

        public Collection<CAssumeEdge> getAssumptions() {
            return assumptions;
        }

        @Override
        public TraversalProcess visitEdge(CFAEdge pEdge) {

            // graph splits into branches
            if (pEdge.getEdgeType() == CFAEdgeType.AssumeEdge) {
                CAssumeEdge assumption = (CAssumeEdge) pEdge;
                if (assumption.getTruthAssumption()) { // true-branch
                    assumptions.add(assumption);
                }
            }

            return TraversalProcess.CONTINUE;
        }
    }
}