org.sosy_lab.java_smt.solvers.z3.Z3Model.java Source code

Java tutorial

Introduction

Here is the source code for org.sosy_lab.java_smt.solvers.z3.Z3Model.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.solvers.z3;

import com.google.common.base.Preconditions;
import com.google.common.base.Verify;
import com.google.common.base.VerifyException;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.Lists;
import com.microsoft.z3.Native;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.sosy_lab.java_smt.api.Formula;
import org.sosy_lab.java_smt.basicimpl.AbstractModel.CachingAbstractModel;

class Z3Model extends CachingAbstractModel<Long, Long, Long> {

    private final long model;
    private final long z3context;
    private static final Pattern Z3_IRRELEVANT_MODEL_TERM_PATTERN = Pattern.compile(".*![0-9]+");

    @SuppressWarnings("hiding")
    private final Z3FormulaCreator creator;

    private Z3Model(long z3context, long z3model, Z3FormulaCreator pCreator) {
        super(pCreator);
        Native.modelIncRef(z3context, z3model);
        model = z3model;
        this.z3context = z3context;
        creator = pCreator;
    }

    static Z3Model create(long z3context, long z3model, Z3FormulaCreator pCreator) {
        return new Z3Model(z3context, z3model, pCreator);
    }

    @Nullable
    @Override
    public Object evaluateImpl(Long f) {
        Native.LongPtr out = new Native.LongPtr();
        boolean status = Native.modelEval(z3context, model, f, false, out);
        Verify.verify(status, "Error during model evaluation");
        long outValue = out.value;

        if (creator.isConstant(outValue)) {
            return creator.convertValue(outValue);
        }

        // Z3 does not give us a direct API to query for "irrelevant" ASTs during evaluation.
        // The only hint we get is that the input AST is not simplified down to a constant:
        // thus, it is assumed to be irrelevant.
        return null;
    }

    @Override
    protected ImmutableList<ValueAssignment> modelToList() {
        Builder<ValueAssignment> out = ImmutableList.builder();

        // Iterate through constants.
        for (int constIdx = 0; constIdx < Native.modelGetNumConsts(z3context, model); constIdx++) {
            long keyDecl = Native.modelGetConstDecl(z3context, model, constIdx);
            Native.incRef(z3context, keyDecl);
            out.addAll(getConstAssignments(keyDecl));
            Native.decRef(z3context, keyDecl);
        }

        // Iterate through function applications.
        for (int funcIdx = 0; funcIdx < Native.modelGetNumFuncs(z3context, model); funcIdx++) {
            long funcDecl = Native.modelGetFuncDecl(z3context, model, funcIdx);
            Native.incRef(z3context, funcDecl);
            if (!isInternalSymbol(funcDecl)) {
                String functionName = creator.symbolToString(Native.getDeclName(z3context, funcDecl));
                out.addAll(getFunctionAssignments(funcDecl, funcDecl, functionName));
            }
            Native.decRef(z3context, funcDecl);
        }

        return out.build();
    }

    /**
     * The symbol "!" is part of temporary symbols used for quantified formulas or aliases. This
     * method is only a heuristic, because the user can also create a symbol containing "!".
     */
    private boolean isInternalSymbol(long funcDecl) {
        return Z3_IRRELEVANT_MODEL_TERM_PATTERN
                .matcher(creator.symbolToString(Native.getDeclName(z3context, funcDecl))).matches();
    }

    /** @return ValueAssignments for a constant declaration in the model */
    private Collection<ValueAssignment> getConstAssignments(long keyDecl) {
        Preconditions.checkArgument(Native.getArity(z3context, keyDecl) == 0, "Declaration is not a constant");

        long var = Native.mkApp(z3context, keyDecl, 0, new long[] {});
        Formula key = creator.encapsulateWithTypeOf(var);

        long value = Native.modelGetConstInterp(z3context, model, keyDecl);
        checkReturnValue(value, keyDecl);
        Native.incRef(z3context, value);

        try {
            long symbol = Native.getDeclName(z3context, keyDecl);
            if (creator.isConstant(value)) {
                return Collections.singletonList(new ValueAssignment(key, creator.symbolToString(symbol),
                        creator.convertValue(value), ImmutableList.of()));

            } else if (Native.isAsArray(z3context, value)) {
                long arrayFormula = Native.mkConst(z3context, symbol, Native.getSort(z3context, value));
                Native.incRef(z3context, arrayFormula);
                return getArrayAssignments(symbol, arrayFormula, value, Collections.emptyList());

            } else {
                throw new UnsupportedOperationException(
                        "unknown model evaluation: " + Native.astToString(z3context, value));
            }

        } finally {
            // cleanup outdated data
            Native.decRef(z3context, value);
        }
    }

    /**
     * Z3 models an array as an uninterpreted function.
     *
     * @return a list of assignments {@code a[1]=0; a[2]=0; a[5]=0}.
     */
    private Collection<ValueAssignment> getArrayAssignments(long arraySymbol, long arrayFormula, long value,
            List<Object> upperIndices) {
        long evalDecl = Native.getAsArrayFuncDecl(z3context, value);
        Native.incRef(z3context, evalDecl);
        long interp = Native.modelGetFuncInterp(z3context, model, evalDecl);
        checkReturnValue(interp, evalDecl);
        Native.funcInterpIncRef(z3context, interp);

        Collection<ValueAssignment> lst = new ArrayList<>();

        // get all assignments for the array
        int numInterpretations = Native.funcInterpGetNumEntries(z3context, interp);
        for (int interpIdx = 0; interpIdx < numInterpretations; interpIdx++) {
            long entry = Native.funcInterpGetEntry(z3context, interp, interpIdx);
            Native.funcEntryIncRef(z3context, entry);
            long arrayValue = Native.funcEntryGetValue(z3context, entry);
            Native.incRef(z3context, arrayValue);
            int noArgs = Native.funcEntryGetNumArgs(z3context, entry);
            assert noArgs == 1 : "array modelled as UF is expected to have only one parameter, aka index";
            long arrayIndex = Native.funcEntryGetArg(z3context, entry, 0);
            Native.incRef(z3context, arrayIndex);
            long select = Native.mkSelect(z3context, arrayFormula, arrayIndex);
            Native.incRef(z3context, select);

            List<Object> innerIndices = Lists.newArrayList(upperIndices);
            innerIndices.add(evaluateImpl(arrayIndex));

            if (creator.isConstant(arrayValue)) {
                lst.add(new ValueAssignment(creator.encapsulateWithTypeOf(select),
                        creator.symbolToString(arraySymbol), creator.convertValue(arrayValue), innerIndices));

            } else if (Native.isAsArray(z3context, arrayValue)) {
                lst.addAll(getArrayAssignments(arraySymbol, select, arrayValue, innerIndices));
            }

            Native.decRef(z3context, arrayIndex);
            Native.funcEntryDecRef(z3context, entry);
        }

        Native.funcInterpDecRef(z3context, interp);
        Native.decRef(z3context, evalDecl);
        return lst;
    }

    private void checkReturnValue(long value, long funcDecl) {
        if (value == 0) {
            throw new VerifyException("Z3 unexpectedly claims that the value of "
                    + Native.funcDeclToString(z3context, funcDecl) + " does not matter in model.");
        }
    }

    /**
     * get all ValueAssignments for a function declaration in the model
     *
     * @param evalDecl function declaration where the evaluation comes from
     * @param funcDecl function declaration where the function name comes from
     * @param functionName the name of the funcDecl
     */
    private Collection<ValueAssignment> getFunctionAssignments(long evalDecl, long funcDecl, String functionName) {
        long interp = Native.modelGetFuncInterp(z3context, model, evalDecl);
        checkReturnValue(interp, evalDecl);
        Native.funcInterpIncRef(z3context, interp);

        List<ValueAssignment> lst = new ArrayList<>();

        int numInterpretations = Native.funcInterpGetNumEntries(z3context, interp);

        if (numInterpretations == 0) {
            // we found an alias in the model, follow the alias
            long elseInterp = Native.funcInterpGetElse(z3context, interp);
            Native.incRef(z3context, elseInterp);
            long aliasDecl = Native.getAppDecl(z3context, elseInterp);
            Native.incRef(z3context, aliasDecl);
            if (isInternalSymbol(aliasDecl)) {
                lst.addAll(getFunctionAssignments(aliasDecl, funcDecl, functionName));
                // TODO Can we guarantee termination of this recursive call?
                //      A chain of aliases should end after several steps.
            } else {
                // ignore functionDeclarations like "ite", "and",...
            }
            Native.decRef(z3context, aliasDecl);
            Native.decRef(z3context, elseInterp);

        } else {
            for (int interpIdx = 0; interpIdx < numInterpretations; interpIdx++) {
                long entry = Native.funcInterpGetEntry(z3context, interp, interpIdx);
                Native.funcEntryIncRef(z3context, entry);
                long entryValue = Native.funcEntryGetValue(z3context, entry);
                if (creator.isConstant(entryValue)) {
                    lst.add(getFunctionAssignment(functionName, funcDecl, entry, entryValue));
                } else {
                    // ignore values of complex types, e.g. Arrays
                }
                Native.funcEntryDecRef(z3context, entry);
            }
        }

        Native.funcInterpDecRef(z3context, interp);
        return lst;
    }

    /**
     * @return ValueAssignment for an entry (one evaluation) of an uninterpreted function in the
     *     model.
     */
    private ValueAssignment getFunctionAssignment(String functionName, long funcDecl, long entry, long entryValue) {
        Object value = creator.convertValue(entryValue);
        int noArgs = Native.funcEntryGetNumArgs(z3context, entry);
        long[] args = new long[noArgs];
        List<Object> argumentInterpretation = new ArrayList<>();

        for (int k = 0; k < noArgs; k++) {
            long arg = Native.funcEntryGetArg(z3context, entry, k);
            Native.incRef(z3context, arg);
            argumentInterpretation.add(creator.convertValue(arg));
            args[k] = arg;
        }
        Formula formula = creator.encapsulateWithTypeOf(Native.mkApp(z3context, funcDecl, args.length, args));

        // Clean up memory.
        for (long arg : args) {
            Native.decRef(z3context, arg);
        }

        return new ValueAssignment(formula, functionName, value, argumentInterpretation);
    }

    @Override
    public String toString() {
        return Native.modelToString(z3context, model);
    }

    @Override
    public void close() {
        Native.modelDecRef(z3context, model);
    }
}