org.sosy_lab.cpachecker.util.predicates.matching.SmtAstMatcherImpl.java Source code

Java tutorial

Introduction

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

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nullable;

import org.sosy_lab.solver.api.BooleanFormula;
import org.sosy_lab.solver.api.BooleanFormulaManager;
import org.sosy_lab.solver.api.Formula;
import org.sosy_lab.solver.api.FormulaManager;
import org.sosy_lab.solver.api.QuantifiedFormulaManager;
import org.sosy_lab.solver.api.UnsafeFormulaManager;
import org.sosy_lab.cpachecker.util.predicates.matching.SmtAstPatternSelection.LogicalConnection;
import org.sosy_lab.cpachecker.util.predicates.matching.SmtQuantificationPattern.QuantifierType;

import com.google.common.base.Optional;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;

public class SmtAstMatcherImpl implements SmtAstMatcher {

    private final UnsafeFormulaManager ufmgr;
    private final BooleanFormulaManager bfmgr;
    private final QuantifiedFormulaManager qfmgr;
    private final FormulaManager fm;

    protected final Multimap<Comparable<?>, Comparable<?>> functionAliases = HashMultimap.create();
    protected final Multimap<Comparable<?>, Comparable<?>> functionImpliedBy = HashMultimap.create();
    protected final Map<Comparable<?>, Comparable<?>> functionRotations = Maps.newHashMap();
    protected final Set<Comparable<?>> commutativeFunctions = Sets.newTreeSet();

    public SmtAstMatcherImpl(UnsafeFormulaManager pUfmgr, BooleanFormulaManager pBfmgr,
            QuantifiedFormulaManager pQfmgr, FormulaManager pFm) {
        ufmgr = pUfmgr;
        bfmgr = pBfmgr;
        qfmgr = pQfmgr;
        fm = pFm;

        defineRotations(">=", "<="); // IMPORTANT: This should NOT define a NEGATION!
        defineRotations(">", "<");

        defineCommutative("=");
        defineCommutative("+");
        defineCommutative("*");
        defineCommutative("and"); // used in the arguments of a quantified predicate

        defineFunctionAliases("*", Sets.newHashSet("Integer__*_", "Real__*_"));
        defineFunctionAliases("-", Sets.newHashSet("Integer__-_", "Real__-_"));
        defineFunctionAliases("+", Sets.newHashSet("Integer__+_", "Real__+_"));
    }

    protected SmtAstMatchResult newMatchFailedResult(Object... pDescription) {
        return SmtAstMatchResult.NOMATCH_RESULT;
    }

    protected SmtAstMatchResult wrapPositiveMatchResult(SmtAstMatchResult pResult, Object... pDescription) {
        //debugLogFromUnitTesting(ObjectArrays.concat("MATCH SUCCESS", pDescription));
        return pResult;
    }

    @Override
    public SmtAstMatchResult perform(SmtAstPatternSelection pPatternSelection, @Nullable Formula pParent,
            Formula pF, Optional<Multimap<String, Formula>> bBindingRestrictions) {

        return matchSelectionOnOneFormula(pParent, pF, pPatternSelection, bBindingRestrictions);
    }

    private SmtAstMatchResult matchSelectionOnOneFormula(final Formula pParentFormula, final Formula pF,
            final SmtAstPatternSelection pPatternSelection,
            final Optional<Multimap<String, Formula>> pBindingRestrictions) {
        // TODO: Cache the match result

        int matches = 0;
        SmtAstMatchResultImpl aggregatedResult = new SmtAstMatchResultImpl();

        for (SmtAstPatternSelectionElement p : pPatternSelection) {
            final SmtAstMatchResult r;
            if (p instanceof SmtAstPattern) {
                SmtAstPattern asp = (SmtAstPattern) p;
                r = internalPerform(pParentFormula, pF, asp, pBindingRestrictions);
            } else if (p instanceof SmtAstPatternSelection) {
                r = matchSelectionOnOneFormula(pParentFormula, pF, (SmtAstPatternSelection) p,
                        pBindingRestrictions);
            } else {
                throw new UnsupportedOperationException("Unknown Ast Pattern Type!");
            }

            matches = matches + (r.matches() ? 1 : 0);

            if (r.matches() && pPatternSelection.getRelationship().isNone()) {
                return newMatchFailedResult("Match but NONE should!");
            }

            if (!r.matches() && pPatternSelection.getRelationship().isAnd()) {
                return newMatchFailedResult("No match but ALL should!");
            }

            if (r.matches() && pPatternSelection.getRelationship().isOr()) {
                return wrapPositiveMatchResult(r, "OR logic");
            }

            if (r.matches()) {
                aggregatedResult.setMatchingRootFormula(pF);
                aggregatedResult.putMatchingArgumentFormula(p, pF); // TODO: Refactor/code duplication
                for (String boundVar : r.getBoundVariables()) {
                    for (Formula varBinding : r.getVariableBindings(boundVar)) {
                        aggregatedResult.putBoundVaribale(boundVar, varBinding);
                    }
                }
            }
        }

        if (matches == 0 && pPatternSelection.getRelationship().isOr()) {
            return newMatchFailedResult("No match but ANY should!");
        }

        return wrapPositiveMatchResult(aggregatedResult, "Late return");
    }

    protected SmtAstMatchResult handleFunctionApplication(final Formula pFunctionRootFormula,
            final SmtAstMatchResultImpl result, final SmtFunctionApplicationPattern fp, final String functionSymbol,
            final ArrayList<Formula> pFunctionArguments,
            final Optional<Multimap<String, Formula>> pBindingRestrictions) {

        // ---------------------------------------------
        // We might consider the reversion version of the function
        boolean considerArgumentsInReverse = false; // TODO: Add support for cases where NOT fp.function.isPresent()
        if (fp.function.isPresent()) {
            boolean isExpectedFunctApp = isExpectedFunctionSymbol(fp.function.get(), functionSymbol);

            if (!isExpectedFunctApp) {
                Comparable<?> functionSymbolRotated = functionRotations.get(functionSymbol);
                if (functionSymbolRotated != null) {
                    if (isExpectedFunctionSymbol(fp.function.get(), functionSymbolRotated)) {
                        isExpectedFunctApp = true;
                        considerArgumentsInReverse = true;
                    }
                }
            }

            if (!isExpectedFunctApp) {
                return newMatchFailedResult("Function missmatch!", fp.function.get(), functionSymbol);
            }
        }

        // ---------------------------------------------
        // in case of AND, the number of arguments must match
        if (fp.getArgumentsLogic().isAnd()) {
            if (fp.getArgumentPatternCount() != pFunctionArguments.size()) {
                return SmtAstMatchResult.NOMATCH_RESULT;
            }
        }

        boolean initialReverseMatching = considerArgumentsInReverse;

        SmtAstMatchResult argumentMatchingResult = matchFormulaChildrenInSequence(pFunctionRootFormula,
                pFunctionArguments, fp.argumentPatterns, pBindingRestrictions, initialReverseMatching);

        if (!argumentMatchingResult.matches() && isCommutative(functionSymbol)) {
            argumentMatchingResult = matchFormulaChildrenInSequence(pFunctionRootFormula, pFunctionArguments,
                    fp.argumentPatterns, pBindingRestrictions, !initialReverseMatching);
        }

        if (argumentMatchingResult.matches()) {
            result.addSubResults(argumentMatchingResult);
            return result;
        } else {
            // Encodes the reason for the failure
            return argumentMatchingResult;
        }
    }

    protected SmtAstMatchResult matchFormulaChildrenInSequence(final Formula pRootFormula,
            final List<? extends Formula> pChildFormulas, final SmtAstPatternSelection pChildPatterns,
            final Optional<Multimap<String, Formula>> pBindingRestrictions, boolean pConsiderPatternsInReverse) {

        final LogicalConnection logic = pChildPatterns.getRelationship();
        final Iterator<SmtAstPatternSelectionElement> pItPatternsInSequence;
        if (pConsiderPatternsInReverse) {
            pItPatternsInSequence = Lists.reverse(pChildPatterns.getPatterns()).iterator();
        } else {
            pItPatternsInSequence = pChildPatterns.iterator();
        }

        SmtAstMatchResultImpl result = new SmtAstMatchResultImpl();
        result.setMatchingRootFormula(pRootFormula);

        if (logic.isDontCare()) {
            return wrapPositiveMatchResult(result, "Don't care");
        }

        // Perform the matching recursively on the arguments
        Set<SmtAstPatternSelectionElement> argPatternsMatched = Sets.newHashSet();

        for (Formula childFormula : pChildFormulas) {
            if (!pItPatternsInSequence.hasNext()) {
                break;
            }

            final SmtAstPatternSelectionElement argPattern = pItPatternsInSequence.next();
            final SmtAstMatchResult functionArgumentResult;

            if (argPattern instanceof SmtAstPattern) {
                functionArgumentResult = internalPerform(pRootFormula, childFormula, (SmtAstPattern) argPattern,
                        pBindingRestrictions);
            } else {
                functionArgumentResult = matchSelectionOnOneFormula(pRootFormula, childFormula,
                        (SmtAstPatternSelection) argPattern, pBindingRestrictions);
            }

            if (functionArgumentResult.matches()) {
                argPatternsMatched.add(argPattern);
                result.putMatchingArgumentFormula(argPattern, childFormula);
                for (String boundVar : functionArgumentResult.getBoundVariables()) {
                    for (Formula varBinding : functionArgumentResult.getVariableBindings(boundVar)) {
                        result.putBoundVaribale(boundVar, varBinding);
                    }
                }

                if (logic.isNone()) {
                    return newMatchFailedResult("Match but NONE should!");
                }

            } else if (logic.isAnd()) {
                return newMatchFailedResult("No match but ALL should!");
            }
        }

        if (argPatternsMatched.isEmpty() && logic.isOr()) {
            return newMatchFailedResult("No match but ANY should!");
        }

        if (argPatternsMatched.size() != pChildPatterns.getPatterns().size() && logic.isAnd()) {
            // assert false; // might be dead code
            return newMatchFailedResult("No match but ALL should!");
        }

        return wrapPositiveMatchResult(result, "Last in matchFormulaChildrenInSequence");
    }

    protected boolean isExpectedFunctionSymbol(Comparable<?> pExpectedSymbol, Comparable<?> pFound) {
        // Either it is equivalent...
        boolean result = pExpectedSymbol.equals(pFound);

        // Or there is a symmetric alias...
        if (!result) {
            Collection<Comparable<?>> definedAliase = functionAliases.get(pExpectedSymbol);
            for (Comparable<?> alias : definedAliase) {
                if (alias.equals(pFound)) {
                    return true;
                }
            }
        }

        // Or the operator implies (or is implied by) another operator...
        if (!result) {
            // Example:
            //  pExpectedSymbol == "="
            //      pFound == ">="
            //  or  pFound == "<="
            //
            if (functionImpliedBy.get(pExpectedSymbol).contains(pFound)) {
                return true;
            }
        }

        return result;
    }

    protected boolean isCommutative(final String pFunctionName) {
        return commutativeFunctions.contains(pFunctionName);
    }

    @Override
    public void defineCommutative(String pFunctionName) {
        commutativeFunctions.add(pFunctionName);
    }

    @Override
    public void defineRotations(String pFunctionName, String pRotationFunctionName) {
        functionRotations.put(pFunctionName, pRotationFunctionName);
        functionRotations.put(pRotationFunctionName, pFunctionName);
    }

    @Override
    public void defineFunctionAliases(String pFunctionName, Set<String> pAliases) {
        functionAliases.putAll(pFunctionName, pAliases);
    }

    @Override
    public SmtAstMatchResult perform(SmtAstPatternSelection pPatternSelection, Formula pF) {
        return perform(pPatternSelection, null, pF, Optional.<Multimap<String, Formula>>absent());
    }

    protected SmtAstMatchResult internalPerform(final Formula pParentFormula, final Formula pRootFormula,
            final SmtAstPattern pP, Optional<Multimap<String, Formula>> pBindingRestrictions) {

        final SmtAstMatchResultImpl result = new SmtAstMatchResultImpl();
        result.setMatchingRootFormula(pRootFormula);

        if (pP.getBindMatchTo().isPresent()) {
            final String bindMatchTo = pP.getBindMatchTo().get();
            result.putBoundVaribale(bindMatchTo, pRootFormula);
            result.putBoundVaribale(bindMatchTo + ".parent", pParentFormula);

            if (!bindMatchTo.contains("?")) {
                if (pBindingRestrictions.isPresent()) {
                    Collection<Formula> variableAlreadyBoundTo = pBindingRestrictions.get().get(bindMatchTo);
                    assert variableAlreadyBoundTo.size() <= 1;
                    if (variableAlreadyBoundTo.size() > 0) {
                        if (!variableAlreadyBoundTo.contains(pRootFormula)) {
                            return newMatchFailedResult(
                                    String.format("Binding of variable %s does not match!", bindMatchTo));
                        }
                    }
                }
            }
        }

        if (pRootFormula instanceof BooleanFormula && qfmgr.isQuantifier((BooleanFormula) pRootFormula)) {
            BooleanFormula booleanFormula = (BooleanFormula) pRootFormula;

            if (!(pP instanceof SmtQuantificationPattern)) {
                return newMatchFailedResult("No function application!");
            }
            SmtQuantificationPattern qp = (SmtQuantificationPattern) pP;

            SmtQuantificationPattern.QuantifierType quantifierType = qfmgr.isForall(booleanFormula)
                    ? QuantifierType.FORALL
                    : QuantifierType.EXISTS;

            if (qp.matchQuantificationWithType.isPresent()) {
                if (!qp.matchQuantificationWithType.get().equals(quantifierType)) {
                    return newMatchFailedResult("Different quantifier!");
                }
            }

            BooleanFormula bodyFormula = qfmgr.getQuantifierBody(booleanFormula);

            return handleQuantification(pRootFormula, result, qp, bodyFormula, pBindingRestrictions);

        } else {
            if (!(pP instanceof SmtFunctionApplicationPattern)) {
                return newMatchFailedResult("Got unexpected function application!");
            }

            // todo: check that it is always applicable.
            SmtFunctionApplicationPattern fp = (SmtFunctionApplicationPattern) pP;

            if (fp.customFormulaMatcher.isPresent()) {
                if (!fp.customFormulaMatcher.get().formulaMatches(fm, pRootFormula)) {
                    return newMatchFailedResult("Custom matcher not matched!");
                }
            }

            final String functionSymbol;
            final int functionParameterCount;

            functionParameterCount = ufmgr.getArity(pRootFormula);
            functionSymbol = ufmgr.getName(pRootFormula);

            if (functionParameterCount == 0) {
                if (pP.getBindMatchTo().isPresent()) {
                    final String bindMatchTo = pP.getBindMatchTo().get();
                    if (bindMatchTo.startsWith(".") && !qfmgr.isBoundByQuantifier(pRootFormula)) {

                        // No match if we are on a variable, that was not bound by a
                        // quantifier, but it is expected to be so.
                        return newMatchFailedResult("Variable not quantified, " + "quantification expected");
                    }
                }
            }

            final ArrayList<Formula> functionArguments = Lists.newArrayList();
            for (int i = 0; i < functionParameterCount; i++) {
                final Formula argFormula = ufmgr.getArg(pRootFormula, i);
                functionArguments.add(argFormula);
            }

            return handleFunctionApplication(pRootFormula, result, fp, functionSymbol, functionArguments,
                    pBindingRestrictions);
        }
    }

    private SmtAstMatchResult handleQuantification(final Formula pRootFormula, final SmtAstMatchResultImpl pResult,
            final SmtQuantificationPattern pQp, final BooleanFormula pBodyFormula,
            final Optional<Multimap<String, Formula>> bBindingRestrictions) {

        final List<BooleanFormula> bodyConjuncts = extractConjuncts(pBodyFormula, false);

        SmtAstMatchResult bodyMatchingResult = matchFormulaChildrenInSequence(pRootFormula, bodyConjuncts,
                pQp.quantorBodyMatchers, bBindingRestrictions, false);

        if (!bodyMatchingResult.matches() && bodyConjuncts.size() > 0) {
            bodyMatchingResult = matchFormulaChildrenInSequence(pRootFormula, bodyConjuncts,
                    pQp.quantorBodyMatchers, bBindingRestrictions, true);
        }

        if (bodyMatchingResult.matches()) {
            pResult.addSubResults(bodyMatchingResult);
            return pResult;
        } else {
            // Encodes the reason for the failure
            return bodyMatchingResult;
        }
    }

    private List<BooleanFormula> extractConjuncts(BooleanFormula pFormula, boolean pRecursive) {
        List<BooleanFormula> result = Lists.newArrayList();
        if (bfmgr.isAnd(pFormula)) {
            for (int i = 0; i < ufmgr.getArity(pFormula); i++) {
                BooleanFormula child = (BooleanFormula) ufmgr.getArg(pFormula, i);
                if (pRecursive) {
                    result.addAll(extractConjuncts(child, true));
                } else {
                    result.add(child);
                }
            }
        }
        return result;
    }

    @Override
    public <T1 extends Formula, T2 extends Formula> T1 substitute(T1 f, Map<T2, T2> fromToMapping) {
        return ufmgr.substitute(f, fromToMapping);
    }

}