org.sosy_lab.java_smt.utils.UfElimination.java Source code

Java tutorial

Introduction

Here is the source code for org.sosy_lab.java_smt.utils.UfElimination.java

Source

/*
 *  JavaSMT is an API wrapper for a collection of SMT solvers.
 *  This file is part of JavaSMT.
 *
 *  Copyright (C) 2007-2016  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.
 */
package org.sosy_lab.java_smt.utils;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableMultimap.copyOf;
import static com.google.common.collect.Maps.difference;

import com.google.auto.value.AutoValue;
import com.google.common.base.Verify;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import javax.annotation.CheckReturnValue;
import org.sosy_lab.common.UniqueIdGenerator;
import org.sosy_lab.java_smt.api.ArrayFormula;
import org.sosy_lab.java_smt.api.BitvectorFormula;
import org.sosy_lab.java_smt.api.BooleanFormula;
import org.sosy_lab.java_smt.api.BooleanFormulaManager;
import org.sosy_lab.java_smt.api.FloatingPointFormula;
import org.sosy_lab.java_smt.api.FloatingPointFormulaManager;
import org.sosy_lab.java_smt.api.Formula;
import org.sosy_lab.java_smt.api.FormulaManager;
import org.sosy_lab.java_smt.api.FormulaType;
import org.sosy_lab.java_smt.api.FunctionDeclaration;
import org.sosy_lab.java_smt.api.FunctionDeclarationKind;
import org.sosy_lab.java_smt.api.NumeralFormula;
import org.sosy_lab.java_smt.api.NumeralFormula.IntegerFormula;
import org.sosy_lab.java_smt.api.QuantifiedFormulaManager;
import org.sosy_lab.java_smt.api.QuantifiedFormulaManager.Quantifier;
import org.sosy_lab.java_smt.api.visitors.DefaultFormulaVisitor;
import org.sosy_lab.java_smt.api.visitors.TraversalProcess;

/**
 * UfElimination replaces UFs by fresh variables and adds constraints to enforce the functional
 * consistency.
 */
public class UfElimination {

    public static class Result {

        private final BooleanFormula formula;
        private final BooleanFormula constraints;
        private final ImmutableMap<Formula, Formula> substitutions;
        private final ImmutableMultimap<FunctionDeclaration<?>, UninterpretedFunctionApplication> ufs;

        public static Result empty(FormulaManager pFormulaManager) {
            BooleanFormula trueFormula = pFormulaManager.getBooleanFormulaManager().makeTrue();
            return new Result(trueFormula, trueFormula, ImmutableMap.of(), ImmutableMultimap.of());
        }

        Result(BooleanFormula pFormula, BooleanFormula pConstraints, ImmutableMap<Formula, Formula> pSubstitutions,
                ImmutableMultimap<FunctionDeclaration<?>, UninterpretedFunctionApplication> pUfs) {
            formula = checkNotNull(pFormula);
            constraints = checkNotNull(pConstraints);
            substitutions = checkNotNull(pSubstitutions);
            ufs = checkNotNull(pUfs);
        }

        /** @return the new {@link Formula} without UFs */
        public BooleanFormula getFormula() {
            return formula;
        }

        /** @return the constraints enforcing the functional consistency. */
        public BooleanFormula getConstraints() {
            return constraints;
        }

        /** @return the substitution used to replace UFs */
        public Map<Formula, Formula> getSubstitution() {
            return substitutions;
        }

        /** @return all eliminated application of Ufs */
        Multimap<FunctionDeclaration<?>, UninterpretedFunctionApplication> getUfs() {
            return ufs;
        }
    }

    private static final UniqueIdGenerator UNIQUE_ID_GENERATOR = new UniqueIdGenerator();

    private static final String prefix = "__UF_fresh_";

    private final BooleanFormulaManager bfmgr;
    private final FormulaManager fmgr;

    UfElimination(FormulaManager pFmgr) {
        bfmgr = pFmgr.getBooleanFormulaManager();
        fmgr = pFmgr;
    }

    /**
     * Applies the Ackermann transformation to the given {@link Formula}. Quantified formulas are not
     * supported.
     *
     * @param f the {@link Formula} to remove all Ufs from
     * @return the new {@link Formula} and the substitution done during transformation
     */
    public BooleanFormula eliminateUfs(BooleanFormula f) {
        Result result = eliminateUfs(f, Result.empty(fmgr));
        return fmgr.getBooleanFormulaManager().and(result.getFormula(), result.getConstraints());
    }

    /**
     * Applies the Ackermann transformation to the given {@link Formula} with respect to the {@link
     * Result} of another formula. Quantified formulas are not supported.
     *
     * @param pF the {@link Formula} to remove all Ufs from
     * @param pOtherResult result of eliminating Ufs in another {@link BooleanFormula}
     * @return the {@link Result} of the Ackermannization
     */
    public Result eliminateUfs(BooleanFormula pF, Result pOtherResult) {
        checkArgument(!isQuantified(pF));
        BooleanFormula f;
        if (!pOtherResult.getSubstitution().isEmpty()) {
            f = fmgr.substitute(pF, pOtherResult.getSubstitution());
        } else {
            f = pF;
        }

        int depth = getNestingDepthOfUfs(f);
        Multimap<FunctionDeclaration<?>, UninterpretedFunctionApplication> ufs = findUFs(f);
        ufs = merge(ufs, pOtherResult);

        ImmutableMap.Builder<Formula, Formula> substitutionsBuilder = ImmutableMap.builder();
        List<BooleanFormula> extraConstraints = new ArrayList<>();

        for (FunctionDeclaration<?> function : ufs.keySet()) {
            List<UninterpretedFunctionApplication> applications = new ArrayList<>(ufs.get(function));
            for (int idx1 = 0; idx1 < applications.size(); idx1++) {
                UninterpretedFunctionApplication application = applications.get(idx1);

                Formula uf = application.getFormula();
                List<Formula> args = application.getArguments();

                Formula substitution = application.getSubstitution();
                substitutionsBuilder.put(uf, substitution);

                for (int idx2 = idx1 + 1; idx2 < applications.size(); idx2++) {
                    UninterpretedFunctionApplication application2 = applications.get(idx2);
                    List<Formula> otherArgs = application2.getArguments();

                    /*
                     * Add constraints to enforce functional consistency.
                     */
                    Verify.verify(args.size() == otherArgs.size());
                    Collection<BooleanFormula> argumentEquality = new ArrayList<>(args.size());
                    for (int i = 0; i < args.size(); i++) {
                        Formula arg1 = args.get(i);
                        Formula arg2 = otherArgs.get(i);
                        argumentEquality.add(makeEqual(arg1, arg2));
                    }

                    BooleanFormula functionEquality = makeEqual(substitution, application2.getSubstitution());
                    extraConstraints.add(bfmgr.implication(bfmgr.and(argumentEquality), functionEquality));
                }
            }
        }

        // Get rid of UFs.
        ImmutableMap<Formula, Formula> substitutions = substitutionsBuilder.build();
        BooleanFormula formulaWithoutUFs = fmgr.substitute(f, substitutions);

        // substitute all UFs in the additional constraints,
        // required if UFs are arguments of UFs, e.g. uf(uf(1, 2), 2)
        for (int i = 0; i < depth; i++) {
            extraConstraints = extraConstraints.stream().map(c -> fmgr.substitute(c, substitutions))
                    .collect(Collectors.toList());
        }

        Map<Formula, Formula> otherSubstitution = difference(pOtherResult.getSubstitution(), substitutions)
                .entriesOnlyOnLeft();
        substitutionsBuilder.putAll(otherSubstitution);
        ImmutableMap<Formula, Formula> allSubstitutions = substitutionsBuilder.build();
        BooleanFormula constraints = bfmgr.and(extraConstraints);
        return new Result(formulaWithoutUFs, constraints, allSubstitutions, copyOf(ufs));
    }

    private Multimap<FunctionDeclaration<?>, UninterpretedFunctionApplication> merge(
            Multimap<FunctionDeclaration<?>, UninterpretedFunctionApplication> pUfs, Result pPreviousResult) {
        for (Entry<FunctionDeclaration<?>, UninterpretedFunctionApplication> ufInOtherFormula : pPreviousResult
                .getUfs().entries()) {
            if (pUfs.containsKey(ufInOtherFormula.getKey())) {
                pUfs.put(ufInOtherFormula.getKey(), ufInOtherFormula.getValue());
            }
        }
        return pUfs;
    }

    @SuppressWarnings("unchecked")
    @CheckReturnValue
    private BooleanFormula makeEqual(Formula pLhs, Formula pRhs) {
        BooleanFormula t;
        if (pLhs instanceof BooleanFormula && pRhs instanceof BooleanFormula) {
            t = bfmgr.equivalence((BooleanFormula) pLhs, (BooleanFormula) pRhs);
        } else if (pLhs instanceof IntegerFormula && pRhs instanceof IntegerFormula) {
            t = fmgr.getIntegerFormulaManager().equal((IntegerFormula) pLhs, (IntegerFormula) pRhs);
        } else if (pLhs instanceof NumeralFormula && pRhs instanceof NumeralFormula) {
            t = fmgr.getRationalFormulaManager().equal((NumeralFormula) pLhs, (NumeralFormula) pRhs);
        } else if (pLhs instanceof BitvectorFormula) {
            t = fmgr.getBitvectorFormulaManager().equal((BitvectorFormula) pLhs, (BitvectorFormula) pRhs);
        } else if (pLhs instanceof FloatingPointFormula && pRhs instanceof FloatingPointFormula) {
            FloatingPointFormulaManager fpfmgr = fmgr.getFloatingPointFormulaManager();
            t = fpfmgr.equalWithFPSemantics((FloatingPointFormula) pLhs, (FloatingPointFormula) pRhs);
        } else if (pLhs instanceof ArrayFormula<?, ?> && pRhs instanceof ArrayFormula<?, ?>) {
            ArrayFormula<?, ?> lhs = (ArrayFormula<?, ?>) pLhs;
            @SuppressWarnings("rawtypes")
            ArrayFormula rhs = (ArrayFormula) pRhs;
            t = fmgr.getArrayFormulaManager().equivalence(lhs, rhs);
        } else {
            throw new IllegalArgumentException("Not supported interface");
        }

        return t;
    }

    private boolean isQuantified(Formula f) {
        AtomicBoolean result = new AtomicBoolean();
        fmgr.visitRecursively(f, new DefaultFormulaVisitor<TraversalProcess>() {

            @Override
            protected TraversalProcess visitDefault(Formula pF) {
                return TraversalProcess.CONTINUE;
            }

            @Override
            public TraversalProcess visitQuantifier(BooleanFormula pF, Quantifier pQ, List<Formula> pBoundVariables,
                    BooleanFormula pBody) {
                result.set(true);
                return TraversalProcess.ABORT;
            }
        });

        return result.get();
    }

    private int getNestingDepthOfUfs(Formula f) {
        return fmgr.visit(f, new DefaultFormulaVisitor<Integer>() {

            @Override
            protected Integer visitDefault(Formula pF) {
                return 0;
            }

            @Override
            public Integer visitFunction(Formula pF, List<Formula> pArgs,
                    FunctionDeclaration<?> pFunctionDeclaration) {
                int depthOfArgs = pArgs.stream().mapToInt(f -> fmgr.visit(f, this)).max().orElse(0);

                // count only UFs
                if (pFunctionDeclaration.getKind() == FunctionDeclarationKind.UF) {
                    return depthOfArgs + 1;
                } else {
                    return depthOfArgs;
                }
            }

            @Override
            public Integer visitQuantifier(BooleanFormula pF, QuantifiedFormulaManager.Quantifier pQ,
                    List<Formula> pBoundVariables, BooleanFormula pBody) {
                return fmgr.visit(pBody, this);
            }
        });
    }

    private Multimap<FunctionDeclaration<?>, UninterpretedFunctionApplication> findUFs(Formula f) {
        Multimap<FunctionDeclaration<?>, UninterpretedFunctionApplication> ufs = HashMultimap.create();

        fmgr.visitRecursively(f, new DefaultFormulaVisitor<TraversalProcess>() {

            @Override
            protected TraversalProcess visitDefault(Formula f) {
                return TraversalProcess.CONTINUE;
            }

            @Override
            public TraversalProcess visitFunction(Formula f, List<Formula> args, FunctionDeclaration<?> decl) {
                if (decl.getKind() == FunctionDeclarationKind.UF) {
                    Formula substitution = freshUfReplaceVariable(decl.getType());
                    ufs.put(decl, UninterpretedFunctionApplication.create(f, args, substitution));
                }
                return TraversalProcess.CONTINUE;
            }
        });

        return ufs;
    }

    private Formula freshUfReplaceVariable(FormulaType<?> pType) {
        return fmgr.makeVariable(pType, prefix + UNIQUE_ID_GENERATOR.getFreshId());
    }

    @AutoValue
    abstract static class UninterpretedFunctionApplication {

        static UninterpretedFunctionApplication create(Formula pF, List<Formula> pArguments,
                Formula pSubstitution) {
            return new AutoValue_UfElimination_UninterpretedFunctionApplication(pF, pArguments, pSubstitution);
        }

        abstract Formula getFormula();

        abstract List<Formula> getArguments();

        abstract Formula getSubstitution();
    }
}