org.sosy_lab.cpachecker.util.predicates.AbstractionManager.java Source code

Java tutorial

Introduction

Here is the source code for org.sosy_lab.cpachecker.util.predicates.AbstractionManager.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;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;

import org.sosy_lab.common.AbstractMBean;
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.core.ShutdownNotifier;
import org.sosy_lab.cpachecker.exceptions.SolverException;
import org.sosy_lab.cpachecker.util.predicates.interfaces.BooleanFormula;
import org.sosy_lab.cpachecker.util.predicates.interfaces.Region;
import org.sosy_lab.cpachecker.util.predicates.interfaces.RegionManager;
import org.sosy_lab.cpachecker.util.predicates.interfaces.RegionManager.RegionBuilder;
import org.sosy_lab.cpachecker.util.predicates.interfaces.view.BooleanFormulaManagerView;
import org.sosy_lab.cpachecker.util.predicates.interfaces.view.FormulaManagerView;

import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Joiner;
import com.google.common.collect.Maps;

/**
 * This class stores a mapping between abstract regions and the corresponding
 * symbolic formula. It is therefore the bridge between the abstract and the
 * symbolic "worlds".
 * It is also responsible for the creation of {@link AbstractionPredicate}s.
 */
@Options(prefix = "cpa.predicate")
public final class AbstractionManager {

    public static interface AbstractionPredicatesMXBean {

        int getNumberOfPredicates();

        String getPredicates();
    }

    private class AbstractionPredicatesMBean extends AbstractMBean implements AbstractionPredicatesMXBean {

        public AbstractionPredicatesMBean() {
            super("org.sosy_lab.cpachecker:type=predicate,name=AbstractionPredicates", logger);
            register();
        }

        @Override
        public int getNumberOfPredicates() {
            return numberOfPredicates;
        }

        @Override
        public String getPredicates() {
            // TODO this may run into a ConcurrentModificationException
            return Joiner.on('\n').join(absVarToPredicate.values());
        }
    }

    private volatile int numberOfPredicates = 0;

    private final LogManager logger;
    private final RegionManager rmgr;
    private final FormulaManagerView fmgr;

    // Here we keep the mapping abstract predicate variable -> predicate
    private final Map<Region, AbstractionPredicate> absVarToPredicate = Maps.newHashMap();
    // and the mapping symbolic variable -> predicate
    private final Map<BooleanFormula, AbstractionPredicate> symbVarToPredicate = Maps.newHashMap();
    // and the mapping atom -> predicate
    private final Map<BooleanFormula, AbstractionPredicate> atomToPredicate = Maps.newHashMap();

    @Option(secure = true, name = "abs.useCache", description = "use caching of region to formula conversions")
    private boolean useCache = true;

    private final Map<Region, BooleanFormula> toConcreteCache;

    private BooleanFormulaManagerView bfmgr;

    public AbstractionManager(RegionManager pRmgr, FormulaManagerView pFmgr, Configuration config,
            LogManager pLogger) throws InvalidConfigurationException {
        config.inject(this, AbstractionManager.class);
        logger = pLogger;
        rmgr = pRmgr;
        fmgr = pFmgr;
        bfmgr = pFmgr.getBooleanFormulaManager();

        if (useCache) {
            toConcreteCache = new HashMap<>();
        } else {
            toConcreteCache = null;
        }

        new AbstractionPredicatesMBean(); // don't store it, we wouldn't know when to unregister anyway
    }

    public int getNumberOfPredicates() {
        return numberOfPredicates;
    }

    /**
     * creates a Predicate from the Boolean symbolic variable (var) and
     * the atom that defines it
     */
    public AbstractionPredicate makePredicate(BooleanFormula atom) {
        AbstractionPredicate result = atomToPredicate.get(atom);
        if (result == null) {
            BooleanFormula symbVar = fmgr.createPredicateVariable("PRED" + numberOfPredicates++);
            Region absVar = rmgr.createPredicate();

            logger.log(Level.FINEST, "Created predicate", absVar, "from variable", symbVar, "and atom", atom);

            result = new AbstractionPredicate(absVar, symbVar, atom);
            symbVarToPredicate.put(symbVar, result);
            absVarToPredicate.put(absVar, result);
            atomToPredicate.put(atom, result);
        }
        return result;
    }

    /**
     * creates a Predicate that represents "false"
     */
    public AbstractionPredicate makeFalsePredicate() {
        return makePredicate(bfmgr.makeBoolean(false));
    }

    /**
     * Get predicate corresponding to a variable.
     * @param var A symbolic formula representing the variable. The same formula has to been passed to makePredicate earlier.
     * @return a Predicate
     */
    private AbstractionPredicate getPredicate(BooleanFormula var) {
        AbstractionPredicate result = symbVarToPredicate.get(var);
        if (result == null) {
            throw new IllegalArgumentException(
                    var + " seems not to be a formula corresponding to a single predicate variable.");
        }
        return result;
    }

    /**
     * Given an abstract formula (which is a BDD over the predicates), build
     * its concrete representation (which is a symbolic formula corresponding
     * to the BDD, in which each predicate is replaced with its definition)
     */
    public BooleanFormula toConcrete(Region af) {
        if (rmgr instanceof SymbolicRegionManager) {
            // optimization shortcut
            return ((SymbolicRegionManager) rmgr).toFormula(af);
        }

        Map<Region, BooleanFormula> cache;
        if (useCache) {
            cache = toConcreteCache;
        } else {
            cache = new HashMap<>();
        }
        Deque<Region> toProcess = new ArrayDeque<>();

        cache.put(rmgr.makeTrue(), bfmgr.makeBoolean(true));
        cache.put(rmgr.makeFalse(), bfmgr.makeBoolean(false));

        toProcess.push(af);
        while (!toProcess.isEmpty()) {
            Region n = toProcess.peek();
            if (cache.containsKey(n)) {
                toProcess.pop();
                continue;
            }
            boolean childrenDone = true;
            BooleanFormula m1 = null;
            BooleanFormula m2 = null;

            Triple<Region, Region, Region> parts = rmgr.getIfThenElse(n);
            Region c1 = parts.getSecond();
            Region c2 = parts.getThird();
            if (!cache.containsKey(c1)) {
                toProcess.push(c1);
                childrenDone = false;
            } else {
                m1 = cache.get(c1);
            }
            if (!cache.containsKey(c2)) {
                toProcess.push(c2);
                childrenDone = false;
            } else {
                m2 = cache.get(c2);
            }
            if (childrenDone) {
                assert m1 != null;
                assert m2 != null;

                toProcess.pop();
                Region var = parts.getFirst();

                AbstractionPredicate pred = absVarToPredicate.get(var);
                assert pred != null;
                BooleanFormula atom = pred.getSymbolicAtom();

                if (bfmgr.isTrue(m1)) {
                    if (bfmgr.isFalse(m2)) {
                        // ITE(atom, true, false) <==> atom
                        cache.put(n, atom);
                    } else {
                        // ITE(atom, true, m2) <==> (atom || m2)
                        cache.put(n, bfmgr.or(atom, m2));
                    }
                } else if (bfmgr.isFalse(m1)) {
                    if (bfmgr.isTrue(m2)) {
                        // ITE(atom, false, true) <==> !atom
                        cache.put(n, bfmgr.not(atom));
                    } else {
                        // ITE(atom, false, m2) <==> (!atom && m2)
                        cache.put(n, bfmgr.and(bfmgr.not(atom), m2));
                    }
                } else {
                    if (bfmgr.isTrue(m2)) {
                        // ITE(atom, m1, true) <==> (!atom || m1)
                        cache.put(n, bfmgr.or(bfmgr.not(atom), m1));
                    } else if (bfmgr.isFalse(m2)) {
                        // ITE(atom, m1, false) <==> (atom && m1)
                        cache.put(n, bfmgr.and(atom, m1));
                    } else {
                        // ITE(atom, m1, m2)
                        cache.put(n, bfmgr.ifThenElse(atom, m1, m2));
                    }
                }
            }
        }

        BooleanFormula result = cache.get(af);
        assert result != null;

        return result;
    }

    /**
     * checks whether the data region represented by f1
     * is a subset of that represented by f2
     * @param f1 an AbstractFormula
     * @param f2 an AbstractFormula
     * @return true if (f1 => f2), false otherwise
     */
    public boolean entails(Region f1, Region f2) throws SolverException, InterruptedException {
        return rmgr.entails(f1, f2);
    }

    /**
     * Return the set of predicates that occur in a a region.
     * In some cases, this method also returns the predicate 'false'
     * in the set.
     */
    public Set<AbstractionPredicate> extractPredicates(Region af) {
        Set<AbstractionPredicate> vars = new HashSet<>();

        Deque<Region> toProcess = new ArrayDeque<>();
        toProcess.push(af);
        while (!toProcess.isEmpty()) {
            Region n = toProcess.pop();

            if (n.isTrue() || n.isFalse()) {
                vars.add(this.makeFalsePredicate());
                continue;
            }

            AbstractionPredicate pred = absVarToPredicate.get(n);

            if (pred == null) {
                Triple<Region, Region, Region> parts = rmgr.getIfThenElse(n);

                Region var = parts.getFirst();
                pred = absVarToPredicate.get(var);
                assert pred != null;

                toProcess.push(parts.getSecond());
                toProcess.push(parts.getThird());
            }

            vars.add(pred);
        }

        return vars;
    }

    public Region buildRegionFromFormula(BooleanFormula pF) {
        return rmgr.fromFormula(pF, fmgr, Functions.compose(new Function<AbstractionPredicate, Region>() {
            @Override
            public Region apply(AbstractionPredicate pInput) {
                return pInput.getAbstractVariable();
            }
        }, Functions.forMap(atomToPredicate)));
    }

    public RegionCreator getRegionCreator() {
        return new RegionCreator();
    }

    public class RegionCreator {

        public RegionBuilder newRegionBuilder(ShutdownNotifier pShutdownNotifier) {
            return rmgr.builder(pShutdownNotifier);
        }

        /**
         * @return a representation of logical truth
         */
        public Region makeTrue() {
            return rmgr.makeTrue();
        }

        /**
         * @return a representation of logical falseness
         */
        public Region makeFalse() {
            return rmgr.makeFalse();
        }

        /**
         * Creates a region representing a negation of the argument
         * @param f an AbstractFormula
         * @return (!f1)
         */
        public Region makeNot(Region f) {
            return rmgr.makeNot(f);
        }

        /**
         * Creates a region representing an AND of the two argument
         * @param f1 an AbstractFormula
         * @param f2 an AbstractFormula
         * @return (f1 & f2)
         */
        public Region makeAnd(Region f1, Region f2) {
            return rmgr.makeAnd(f1, f2);
        }

        /**
         * Creates a region representing an OR of the two argument
         * @param f1 an AbstractFormula
         * @param f2 an AbstractFormula
         * @return (f1 | f2)
         */
        public Region makeOr(Region f1, Region f2) {
            return rmgr.makeOr(f1, f2);
        }

        /**
         * Creates a region representing an equality (bi-implication) of the two argument
         * @param f1 an AbstractFormula
         * @param f2 an AbstractFormula
         * @return (f1 <=> f2)
         */
        public Region makeEqual(Region f1, Region f2) {
            return rmgr.makeEqual(f1, f2);
        }

        /**
         * Creates a region representing an if then else construct of the three arguments
         * @param f1 an AbstractFormula
         * @param f2 an AbstractFormula
         * @param f3 an AbstractFormula
         * @return (if f1 then f2 else f3)
         */
        public Region makeIte(Region f1, Region f2, Region f3) {
            return rmgr.makeIte(f1, f2, f3);
        }

        /**
         * Creates a region representing an existential quantification of the two argument
         * @param f1 an AbstractFormula
         * @param f2 an AbstractFormula
         * @return (\exists f2: f1)
         */
        public Region makeExists(Region f1, Region f2) {
            return rmgr.makeExists(f1, f2);
        }

        public Region getPredicate(BooleanFormula var) {
            return AbstractionManager.this.getPredicate(var).getAbstractVariable();
        }
    }
}