/* * Copyright (C) 2009-2012 University of Freiburg * * This file is part of SMTInterpol. * * SMTInterpol is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SMTInterpol is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with SMTInterpol. If not, see <http://www.gnu.org/licenses/>. */ package de.uni_freiburg.informatik.ultimate.smtinterpol.interpolate; import java.math.BigInteger; import java.util.ArrayList; import java.util.BitSet; import java.util.HashMap; import java.util.Map.Entry; import de.uni_freiburg.informatik.ultimate.logic.Rational; import de.uni_freiburg.informatik.ultimate.logic.Sort; import de.uni_freiburg.informatik.ultimate.logic.Term; import de.uni_freiburg.informatik.ultimate.logic.TermVariable; import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.Literal; import de.uni_freiburg.informatik.ultimate.smtinterpol.interpolate.Interpolator.LitInfo; import de.uni_freiburg.informatik.ultimate.smtinterpol.interpolate.Interpolator.Occurrence; import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.linar.BoundConstraint; import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.linar.InfinitNumber; import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.linar.LAAnnotation; import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.linar.LAEquality; import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.linar.LinVar; import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.linar.MutableAffinTerm; /** * The Interpolator for linear arithmetic. This computes the interpolants * with the algorithm described in "Proof Tree Preserving Interpolation" * in the version "newtechreport.pdf" in this repository. * * In particular we need to compute leaf interpolants for trichotomy * <pre>a < b \/ a == b \/ a > b</pre>, * for simple conflicts with Farkas coefficients, and for resolution steps. * * If we have a more complex LAAnnotation we internally break it down to * resolution steps on simple leaf clauses. * * @author Jochen Hoenicke, Alexander Nutz */ public class LAInterpolator { Interpolator mInterpolator; /** * The annotation of the clause for which we compute an interpolant. */ LAAnnotation mAnnotation; /** * A hash map mapping to each subannotation of mAnnotation the partial * interpolant and some auxiliary information. */ HashMap<LAAnnotation, AnnotInfo> mAuxInfos = new HashMap<LAAnnotation, AnnotInfo>(); /** * This class stores partial interpolants and auxiliary information * for each sub-annotation. * * This extends Occurence, i.e., it also knows if it is A local, * B local, or mixed in every partition. This occurence is the * occurence of the "proved literal" <code>mSum <= 0</code>. */ class AnnotInfo extends Interpolator.Occurrence { /** * The annotation for which the auxiliary information is stored * in this class. */ LAAnnotation mMyAnnotation; /** * The pseudo literal that this sub annotation proves. Basically * a sub-annotation is a resolution proof that proves a clause * where this pseudo literal occurs positively. It is used by its * parent annotation negatively, so there is a hidden resolution * step combining the parent annotation and the subannotation. * * The literal is stored as a mutable affine term, i.e., the literal * is mSum <= 0. It is equivalent to * <pre>mMyAnnotation.getLinVar() <= mMyAnnotation.getBound()</pre> */ private MutableAffinTerm mSum; /** * For each partition, this stores the partial interpolant. * This is the partial interpolant of the clause this * sub-annotation proves. */ Interpolant[] mInterpolants; /** * For each partition, this stores the mixed part of mSum. * This is the sum of the A part of mSum, i.e., the A part of the * literal <code>mSum <= 0</code>. It is <code>null</null> if \ * the literal is not mixed in that partition. */ InterpolatorAffineTerm[] mMixedSums; /** * If the literal mSum <= 0 is mixed, this denotes the auxiliary * variable our algorithm introduces for this literal. */ TermVariable mAuxVar; /** * This is 1, if mSum is an integer, eps otherwise. */ InfinitNumber mEpsilon; /** * The default constructor. This computes all the necessary fields * except for the partial interpolants. The partial interpolants * are computed by the LAAnnotation. * @param auxAnnot The annotation for which we compute partial * interpolants and auxiliary information. */ public AnnotInfo(LAAnnotation auxAnnot) { mInterpolator.super(); mMyAnnotation = auxAnnot; //we only need sum and stuff for subannotations if (!auxAnnot.equals(mAnnotation)) { computeSum(); color(); computeMixedSums(); } } /** * Compute mSum and mEpsilon. */ private void computeSum() { mSum = new MutableAffinTerm(); mSum.add(Rational.ONE, mMyAnnotation.getLinVar()); mSum.add(mMyAnnotation.getBound().negate()); mEpsilon = mMyAnnotation.getLinVar().getEpsilon(); } /** * Compute the occurrence. */ private void color() { boolean isFirst = true; for (final LinVar lv : mSum.getSummands().keySet()) { final Interpolator.Occurrence occ = mInterpolator.getOccurrence(lv.getSharedTerm()); assert (occ != null); if (isFirst) { mInA.or(occ.mInA); mInB.or(occ.mInB); isFirst = false; } else { mInA.and(occ.mInA); mInB.and(occ.mInB); } } } /** * Compute mMixedSums and set mAuxVar. */ private void computeMixedSums() { final BitSet shared = new BitSet(); shared.or(mInA); shared.or(mInB); if (shared.nextClearBit(0) == mInterpolator.mNumInterpolants) { return; } final Sort sort = mInterpolator.mTheory.getSort( mMyAnnotation.getLinVar().isInt() ? "Int" : "Real"); mMixedSums = new InterpolatorAffineTerm[mInterpolator.mNumInterpolants]; mAuxVar = mInterpolator.mTheory.createFreshTermVariable("msaux", sort); for (int part = 0; part < mInterpolator.mNumInterpolants; part++) { if (isMixed(part)) { final InterpolatorAffineTerm sumApart = new InterpolatorAffineTerm(); final LinVar lv = mMyAnnotation.getLinVar(); for (final Entry<LinVar, BigInteger> en : lv.getLinTerm().entrySet()) { final Occurrence occ = mInterpolator.getOccurrence( en.getKey().getSharedTerm()); if (occ.isALocal(part)) { final Rational coeff = Rational.valueOf(en.getValue(), BigInteger.ONE); sumApart.add(coeff, en.getKey()); } } sumApart.add(Rational.MONE, mAuxVar); mMixedSums[part] = sumApart; } } } /** * This returns the proved literal <code>sum</code>. The proved * literal is really <code>sum <= 0</code>. This is the literal * that occurs positively in the clause proved by this subannotation. * @return the "proved literal". */ private MutableAffinTerm getSum() { return mSum; } /** * Return the A part of <code>getSum() <= 0</code>. The literal * must be mixed in the partition. * @param part the partition for which to compute the A part. * @return the A part. */ InterpolatorAffineTerm getMixedSum(int part) { return mMixedSums[part]; } /** * Return the epsilon. This is 1 for integer constraints, eps for * rational constraints. * @return the epsilon. */ public InfinitNumber getEpsilon() { return mEpsilon; } } /** * Create a new linear arithmetic interpolator for a root LAAnnotation. * @param interpolator the global interpolator. * @param theoryAnnotation the annotation of a leaf clause. This must * be a root LAAnnoation, i.e., parent must be <code>null</code>. */ public LAInterpolator(Interpolator interpolator, LAAnnotation theoryAnnotation) { mInterpolator = interpolator; mAnnotation = theoryAnnotation; } /** * Compute the summary, aux variable and interpolants for a annotation * or sub-annotation. This function caches the information, so that * it is only computed once. This is where the partial interpolants * are computed. * @param auxAnnot The annotation of which the information should be * computed. * @return An AnnotInfo containing all the information. */ private AnnotInfo computeAuxAnnotations(LAAnnotation auxAnnot) { AnnotInfo result = mAuxInfos.get(auxAnnot); if (result != null) { return result; } result = new AnnotInfo(auxAnnot); result.mInterpolants = new Interpolant[mInterpolator.mNumInterpolants]; for (int i = 0; i < mInterpolator.mNumInterpolants; i++) { result.mInterpolants[i] = new Interpolant(); } /* Compute the partial interpolants etc. for all child annotations. */ for (final LAAnnotation annot : auxAnnot.getAuxAnnotations().keySet()) { computeAuxAnnotations(annot); } /* Compute the partial interpolants of this annotation without * child annotations. This is a leaf in the final resolution tree. */ interpolateLeaf(auxAnnot, result); /* Resolve the partial interpolants with each child annotation. */ interpolateInnerNode(auxAnnot, result); /* Cache the result for the future. */ mAuxInfos.put(auxAnnot, result); return result; } /** * Interpolate a leaf node that explains the sub-proof auxAnnot. * This sub-proof explains how the normalized and rounded summary of * auxAnnot is implied by its literals and sub-annotations. Note that * this is not an interpolant of the sub annotation, since it does * not yet contain the interpolant for its own sub-annotations. * You have to call interpolateInnerNode afterwards. * * Normally, the interpolant is computed by summing up the A-part of all * sub-annotations, literals, and the negated summary of this annotation. * * For trichotomy clauses we have to return the special trichotomy * interpolant, * <pre>LA(x1 + x2 <= 0, 0, x1 + x2 <= 0 and * (x1 + x2 < 0 or EQ(x, x1)))</pre> * in the mixed case. * * @param auxAnnot the sub-proof that is interpolated. * @param result the normalized and rounded summary of auxAnnot. */ private void interpolateLeaf(LAAnnotation auxAnnot, AnnotInfo result) { final InterpolatorAffineTerm[] ipl = new InterpolatorAffineTerm[mInterpolator.mNumInterpolants + 1]; for (int part = 0; part < ipl.length; part++) { ipl[part] = new InterpolatorAffineTerm(); } @SuppressWarnings("unchecked") final ArrayList<TermVariable>[] auxVars = new ArrayList[mInterpolator.mNumInterpolants]; /* these variables are used for trichotomy clauses. * The inequalityInfo will remember the information for * one of the inequalities to get the aux literal. * The equality will remember the equality literal and * equalityInfo will remember its info. */ Literal equality = null; LitInfo equalityInfo = null; Interpolator.Occurrence inequalityInfo = null; /* if this leaf is a sub-annotation, i.e. we have to add the * negated summary to get a conflict clause. * * To compute the interpolant we have to add the A-part * of the negated summary. */ if (auxAnnot != mAnnotation) { // Sum A parts of negated auxAnnot. int part = result.mInB.nextClearBit(0); while (part < ipl.length) { final Rational coeff = auxAnnot.isUpper() ? Rational.MONE : Rational.ONE; if (result.isMixed(part)) { ipl[part].add(coeff, result.getMixedSum(part)); if (auxVars[part] == null) { auxVars[part] = new ArrayList<TermVariable>(); } auxVars[part].add(result.mAuxVar); } if (result.isALocal(part)) { ipl[part].add(coeff, result.getSum()); ipl[part].add(result.getEpsilon()); } part++; } } /* Add the A-part of the summary for all used sub-annotations. */ for (final Entry<LAAnnotation, Rational> entry : auxAnnot.getAuxAnnotations().entrySet()) { final AnnotInfo info = mAuxInfos.get(entry.getKey()); final Rational coeff = entry.getValue(); // Sum A parts of info. int part = info.mInB.nextClearBit(0); while (part < ipl.length) { if (info.isMixed(part)) { ipl[part].add(coeff, info.getMixedSum(part)); if (auxVars[part] == null) { auxVars[part] = new ArrayList<TermVariable>(); } auxVars[part].add(info.mAuxVar); } if (info.isALocal(part)) { ipl[part].add(coeff, info.getSum()); } part++; } inequalityInfo = info; } /* Add the A-part of the literals in this annotation. */ for (final Entry<Literal, Rational> entry : auxAnnot.getCoefficients().entrySet()) { final Literal lit = entry.getKey().negate(); final Rational factor = entry.getValue(); if (lit.getAtom() instanceof BoundConstraint || lit instanceof LAEquality) { InfinitNumber bound; LinVar lv; if (lit.getAtom() instanceof BoundConstraint) { assert factor.signum() == lit.getSign(); final BoundConstraint bc = (BoundConstraint) lit.getAtom(); bound = lit.getSign() > 0 ? bc.getBound() : bc.getInverseBound(); lv = bc.getVar(); } else { assert lit instanceof LAEquality; final LAEquality eq = (LAEquality) lit; lv = eq.getVar(); bound = new InfinitNumber(eq.getBound(), 0); } final LitInfo info = mInterpolator.getLiteralInfo(lit.getAtom()); inequalityInfo = info; int part = info.mInB.nextClearBit(0); while (part < ipl.length) { if (info.isMixed(part)) { /* ab-mixed interpolation */ assert (info.mMixedVar != null); ipl[part].add(factor, info.getAPart(part)); ipl[part].add(factor.negate(), info.mMixedVar); if (auxVars[part] == null) { auxVars[part] = new ArrayList<TermVariable>(); } auxVars[part].add(info.mMixedVar); } if (info.isALocal(part)) { /* Literal in A: add to sum */ ipl[part].add(factor, lv); ipl[part].add(bound.negate().mul(factor)); } part++; } } else if (lit.negate() instanceof LAEquality) { //we have a Trichotomy Clause equality = lit.negate(); final LAEquality eq = (LAEquality) equality; //a trichotomy clause must contain exactly three parts assert auxAnnot.getAuxAnnotations().size() + auxAnnot.getCoefficients().size() + (auxAnnot == mAnnotation ? 0 : 1) == 3;// NOCHECKSTYLE assert equalityInfo == null; equalityInfo = mInterpolator.getLiteralInfo(eq); assert factor.abs() == Rational.ONE; int part = equalityInfo.mInB.nextClearBit(0); while (part < ipl.length) { if (equalityInfo.isALocal(part)) { /* Literal in A: add epsilon to sum */ ipl[part].add(eq.getVar().getEpsilon()); } part++; } } } assert (ipl[ipl.length - 1].isConstant() && InfinitNumber.ZERO.less(ipl[ipl.length - 1].getConstant())); /* * Save the interpolants computed for this leaf into the result array. */ for (int part = 0; part < auxVars.length; part++) { final Rational normFactor = ipl[part].isConstant() ? Rational.ONE : ipl[part].getGCD().inverse().abs(); ipl[part].mul(normFactor); /* Round up the (negated) constant if all terms in the interpolant * are known to be integer. This is sound since * x <= 0 is equivalent to ceil(x) <= 0. */ if (ipl[part].isInt()) { ipl[part].mConstant = ipl[part].getConstant().ceil(); } if (auxVars[part] != null) { // NOPMD /* This is a mixed interpolant with auxiliary variables. * Prepare an LATerm that wraps the interpolant. */ InfinitNumber k; Term F; if (equalityInfo != null) { // NOPMD /* This is a mixed trichotomy clause. This requires a * very special interpolant. */ assert equalityInfo.isMixed(part); assert auxVars[part].size() == 2; assert normFactor == Rational.ONE; final InterpolatorAffineTerm less = new InterpolatorAffineTerm(ipl[part]).add( InfinitNumber.EPSILON); k = InfinitNumber.ZERO; F = mInterpolator.mTheory.and( ipl[part].toLeq0(mInterpolator.mTheory), mInterpolator.mTheory.or(less.toLeq0(mInterpolator.mTheory), mInterpolator.mTheory.equals( equalityInfo.getMixedVar(), auxVars[part].iterator().next()))); } else { /* Just the inequalities are mixed. */ if (ipl[part].isInt()) { k = InfinitNumber.ONE.negate(); } else { k = InfinitNumber.EPSILON.negate(); } F = ipl[part].toLeq0(mInterpolator.mTheory); } final LATerm laTerm = new LATerm(ipl[part], k, F); result.mInterpolants[part].mTerm = laTerm; } else { assert (equalityInfo == null || !equalityInfo.isMixed(part)); if (equalityInfo != null && ipl[part].isConstant() && equalityInfo.isALocal(part) != inequalityInfo.isALocal(part)) { /* special case: Nelson-Oppen conflict, a < b and b < a in * one partition, a != b in the other. * If a != b is in A, the interpolant is simply a != b. * If a != b is in B, the interpolant is simply a == b. */ final Literal thisIpl = equalityInfo.isALocal(part) ? equality.negate() : equality; result.mInterpolants[part].mTerm = thisIpl.getSMTFormula(mInterpolator.mTheory); } else { result.mInterpolants[part].mTerm = ipl[part].toLeq0(mInterpolator.mTheory); } } } } /** * This function computes the complete interpolant for an annotation * by resolving it with all interpolants of its sub-annotations. * * @param auxAnnot the annotation for which the interpolant should * be computed. * @param result The info where the interpolants are stored. On call * it must contain the interpolants computed by interpolateLeaf. */ private void interpolateInnerNode(LAAnnotation auxAnnot, AnnotInfo result) { for (final Entry<LAAnnotation, Rational> auxAnn : auxAnnot.getAuxAnnotations().entrySet()) { final LAAnnotation annot = auxAnn.getKey(); final AnnotInfo subInfo = computeAuxAnnotations(annot); for (int part = 0; part < mInterpolator.mNumInterpolants; part++) { if (subInfo.isBLocal(part)) { /* Literal in B: and */ result.mInterpolants[part].mTerm = mInterpolator.mTheory. and(result.mInterpolants[part].mTerm, subInfo.mInterpolants[part].mTerm); } else if (subInfo.isMixed(part)) { final TermVariable mixedVar = subInfo.mAuxVar; result.mInterpolants[part] = mInterpolator.mixedPivotLA( result.mInterpolants[part], subInfo.mInterpolants[part], mixedVar); } else if (subInfo.isAB(part)) { /* Literal is shared: ite */ final MutableAffinTerm mat = new MutableAffinTerm(); final Rational coeff = annot.isUpper() ? Rational.ONE : Rational.MONE; mat.add(coeff, subInfo.getSum()); result.mInterpolants[part].mTerm = mInterpolator.mTheory.ifthenelse( mat.toSMTLibLeq0(mInterpolator.mTheory, false), result.mInterpolants[part].mTerm, subInfo.mInterpolants[part].mTerm); } else { /* Literal in A: or */ result.mInterpolants[part].mTerm = mInterpolator.mTheory. or(result.mInterpolants[part].mTerm, subInfo.mInterpolants[part].mTerm); } } } } /** * Computes partial interpolants for the clause proved by the root * LAAnnotation. * @return an array containing the partial tree interpolants. */ public Interpolant[] computeInterpolants() { final AnnotInfo annotInfo = computeAuxAnnotations(mAnnotation); return annotInfo.mInterpolants; } }