/* * 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.util.Arrays; import java.util.BitSet; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import de.uni_freiburg.informatik.ultimate.logic.FunctionSymbol; import de.uni_freiburg.informatik.ultimate.logic.Term; import de.uni_freiburg.informatik.ultimate.logic.Theory; import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.Clause; 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.cclosure.CCAnnotation; import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.cclosure.CCAppTerm; import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.cclosure.CCBaseTerm; import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.cclosure.CCEquality; import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.cclosure.CCTerm; import de.uni_freiburg.informatik.ultimate.smtinterpol.util.Coercion; import de.uni_freiburg.informatik.ultimate.smtinterpol.util.SymmetricPair; public class CCInterpolator { Interpolator mInterpolator; HashMap<SymmetricPair<CCTerm>, CCEquality> mEqualities; HashMap<SymmetricPair<CCTerm>, PathInfo> mPaths; Theory mTheory; int mNumInterpolants; Set<Term>[] mInterpolants; /** * Compute the parent partition. This is the next partition, whose * subtree includes color. */ private int getParent(int color) { int parent = color + 1; while (mInterpolator.mStartOfSubtrees[parent] > color) { parent++; } return parent; } /** * Compute the A-local child partition. This is the child, that is * A-local to the occurence. This function returns -1 if all childs * are in B. */ private int getChild(int color, Occurrence occur) { /* find A-local child of m_Color */ int child = color - 1; while (child >= mInterpolator.mStartOfSubtrees[color]) { if (occur.isALocal(child)) { return child; } child = mInterpolator.mStartOfSubtrees[child] - 1; } return -1; } class PathInfo { CCTerm[] mPath; /** * The set of partitions for which there is an AB-shared path * from start to end. */ BitSet mHasABPath; /** * The first partition for which the path from start to end is * A-local. This is m_numInterpolants, if there is no such * partition. If m_hasABPath is not empty, this value is undefined; * we set it to the root of the m_hasABPath tree, which equals the * two mColor of the head and tail node. */ int mMaxColor; PathEnd mHead, mTail; /* max color is the maximum of all firstColor of all literals on the * path. * * Head color is the lastColor of the first literal before the first * path change. If head color >= max color, then there is no path * change. * */ boolean mComputed; class PathEnd { /** * The first partition for which there is an A-local prefix of * the path. If m_hasABPath is non-empty, this is the first * partition that is not in m_hasABPath, i.e. the first for which * only a continuous A-path but not a continuous B-path exists. */ int mColor; /** * For each partition on the path from m_Color to m_MaxColor this * gives the term that ends the first A-local chain of equalities. */ Term[] mTerm; /** * For each partition on the path from m_Color to the root * (m_numInterpolants) this gives B-summaries that are used to * gap congruence proofs, where both ends are A-local. */ Set<Term>[] mPre; @SuppressWarnings("unchecked") public PathEnd() { mColor = mNumInterpolants; mTerm = new Term[mNumInterpolants]; mPre = new Set[mNumInterpolants]; } /** * Close the A path for partition color. This is called when we * add a term to the chain that is B-local for the current * mColor. We set mColor to the parent node. We also close the * open path on mColor or open a new one and increment mMaxColor * if such a path was not yet open. * @param other the other PathEnd * @param boundaryTerm the boundary term for opening/closing the * path. */ public void closeSingleAPath(PathEnd other, Term boundaryTerm) { // this should be empty now, since we anded it with // occur.mInA and the occurrence is not in A for color. assert mHasABPath.isEmpty(); final int color = mColor; mColor = getParent(color); if (color < mMaxColor) { addPre(color, Coercion.buildEq(boundaryTerm, mTerm[color])); addInterpolantClause(color, mPre[color]); mPre[color] = null; mTerm[color] = null; } else { assert (mMaxColor == color); other.mTerm[color] = boundaryTerm; if (mPre[color] != null) { other.mPre[color] = mPre[color]; mPre[color] = null; } mMaxColor = getParent(color); } } /** * Open a new A path. This is called when a term is added that * is A local in child, where child is a child of mColor. We * start a new A path on child. If we have still slack, since * mHasABPath contains child, we don't have to open the path and * just set mMaxColor to child. * @param other the other path end. * @param boundaryTerm the term that starts the new A path. * @param child the child of mColor for which the added term is * A local. */ public void openSingleAPath( PathEnd other, Term boundaryTerm, int child) { if (mHasABPath.get(child)) { mMaxColor = other.mColor = mColor = child; // compute all nodes below child excluding child itself final BitSet subtree = new BitSet(); subtree.set(mInterpolator.mStartOfSubtrees[child], child); // keep only those below the current child. mHasABPath.and(subtree); } else { /* open a new A-path. */ mTerm[child] = boundaryTerm; mColor = child; } } /** * */ public void closeAPath( PathEnd other, Term boundaryTerm, Occurrence occur) { assert(other.mColor <= mMaxColor); mHasABPath.and(occur.mInA); while (mColor < mNumInterpolants && occur.isBLocal(mColor)) { closeSingleAPath(other, boundaryTerm); } } public void openAPath( PathEnd other, Term boundaryTerm, Occurrence occur) { while (true) { final int child = getChild(mColor, occur); /* if there is no A-local child, we are done. */ if (child < 0) { break; } assert occur.isALocal(child); openSingleAPath(other, boundaryTerm, child); } } public Term getBoundTerm(int color) { if (color < mColor) { final CCTerm first = this == mHead ? mPath[0] : mPath[mPath.length - 1]; return first.toSMTTerm(mTheory); } else if (color < mMaxColor) { return mTerm[color]; } else { final CCTerm last = this == mTail ? mPath[0] : mPath[mPath.length - 1]; return last.toSMTTerm(mTheory); } } public void addPre(int color, Term pre) { if (mPre[color] == null) { mPre[color] = new HashSet<Term>(); } mPre[color].add(pre); } public void addAllPre(int color, PathEnd src) { final Set<Term> pre = src.mPre[color]; if (pre == null) { return; } if (mPre[color] == null) { mPre[color] = new HashSet<Term>(); } mPre[color].addAll(pre); } private void mergeCongPath( PathEnd other, CCAppTerm start, CCAppTerm end) { CCTerm term = start.getFunc(); while (term instanceof CCAppTerm) { term = ((CCAppTerm) term).getFunc(); } final int rightColor = mInterpolator .getOccurrence(end.getFlatTerm()).getALocalColor(); final Occurrence rightOccur = mInterpolator.new Occurrence(); rightOccur.occursIn(rightColor); final Occurrence leftOccur = mInterpolator.new Occurrence(); leftOccur.occursIn(mColor); final FunctionSymbol func = ((CCBaseTerm)term).getFunctionSymbol(); final int numArgs = func.getParameterSorts().length; final PathInfo[] argPaths = new PathInfo[numArgs]; final PathEnd[] head = new PathEnd[numArgs]; final PathEnd[] tail = new PathEnd[numArgs]; final boolean[] isReverse = new boolean[numArgs]; int arg = numArgs; while (true) { arg--; argPaths[arg] = start.getArg() == end.getArg() ? new PathInfo(start.getArg()) : mPaths.get(new SymmetricPair<CCTerm>(start.getArg(), end.getArg())); argPaths[arg].interpolatePathInfo(); isReverse[arg] = (start.getArg() != argPaths[arg].mPath[0]); head[arg] = isReverse[arg] ? argPaths[arg].mTail : argPaths[arg].mHead; tail[arg] = isReverse[arg] ? argPaths[arg].mHead : argPaths[arg].mTail; final Term startTerm = start.getArg().toSMTTerm(mTheory); head[arg].closeAPath(tail[arg], startTerm, leftOccur); head[arg].openAPath(tail[arg], startTerm, leftOccur); final Term endTerm = end.getArg().toSMTTerm(mTheory); tail[arg].closeAPath(head[arg], endTerm, rightOccur); tail[arg].openAPath(head[arg], endTerm, rightOccur); if (arg == 0) { break; } start = (CCAppTerm) start.getFunc(); end = (CCAppTerm) end.getFunc(); } mHasABPath.and(rightOccur.mInA); while (rightOccur.isBLocal(mColor)) { final Term[] boundaryParams = new Term[numArgs]; for (int i = 0; i < numArgs; i++) { boundaryParams[i] = head[i].getBoundTerm(mColor); addAllPre(mColor, tail[i]); } final Term boundaryTerm = Coercion.buildApp(func, boundaryParams); closeSingleAPath(other, boundaryTerm); } final int highColor = mColor; while (true) { /* find A-local child of m_Color */ final int child = getChild(mColor, rightOccur); if (child < 0) { break; } final Term[] boundaryParams = new Term[numArgs]; for (int i = 0; i < numArgs; i++) { boundaryParams[i] = tail[i].getBoundTerm(child); addAllPre(child, tail[i]); } final Term boundaryTerm = Coercion.buildApp(func, boundaryParams); openSingleAPath(other, boundaryTerm, child); } assert (mColor == rightColor); for (int color = highColor; color < mNumInterpolants; color = getParent(color)) { for (int i = 0; i < numArgs; i++) { if (color < argPaths[i].mMaxColor) { addPre(color, Coercion.buildDistinct( head[i].getBoundTerm(color), tail[i].getBoundTerm(color))); } addAllPre(color, head[i]); addAllPre(color, tail[i]); } } } @Override public String toString() { final StringBuilder sb = new StringBuilder(); String comma = ""; sb.append(mColor).append(":["); for (int i = mColor; i < mMaxColor; i++) { sb.append(comma); if (mPre[i] != null) { sb.append(mPre[i]).append(" or "); } sb.append(mTerm[i]); comma = ","; } comma = "|"; for (int i = mMaxColor; i < mNumInterpolants; i++) { if (mPre[i] != null) { sb.append(comma).append("pre").append(i).append(':'); sb.append(mPre[i]); comma = ","; } } sb.append(']'); return sb.toString(); } } /* invariants: * HeadTerm[p] != null exactly for p in [m_HeadColor, m_MaxColor-1] * HeadPre[p] != null only for p in [m_HeadColor, numInterpolants] * HeadColor is in between first and last color of head term. * likewise for Tail. * MaxColor is maximum of all first of all terms and literals * involved in current path (but there may be bigger literals in * congruence paths that were added to headPre/tailPre). * * The partial interpolant of the current path is * m_Interpolants && * HeadPre ==> Lits[0] == m_HeadTerm && * TailPre ==> m_TailTerm == lits[n] * where HeadTerm = Lits[0] for partitions < HeadColor and * TailTerm = Lits[n] for partitions < TailColor. * * For partitions >= maxColor, everything so far was in A, so * the partial interpolant of the current path is * m_Interpolants && * TailPre ==> Lits[0] == lits[n] */ public PathInfo(CCTerm[] path) { mPath = path; mHasABPath = new BitSet(mNumInterpolants); mHasABPath.set(0, mNumInterpolants); mMaxColor = mNumInterpolants; } public PathInfo(CCTerm arg) { this(new CCTerm[] { arg }); } public void interpolatePathInfo() { if (mComputed) { return; } final Occurrence headOccur = mInterpolator.getOccurrence(mPath[0].getFlatTerm()); mHead = new PathEnd(); mTail = new PathEnd(); mTail.closeAPath(mHead, null, headOccur); mTail.openAPath(mHead, null, headOccur); for (int i = 0; i < mPath.length - 1; i++) { final CCTerm left = mPath[i]; final CCTerm right = mPath[i + 1]; final CCEquality lit = mEqualities.get(new SymmetricPair<CCTerm>(left, right)); if (lit == null) { mTail.mergeCongPath(mHead, (CCAppTerm) left, (CCAppTerm) right); } else { final LitInfo info = mInterpolator.getLiteralInfo(lit); Term boundaryTerm; boundaryTerm = mPath[i].toSMTTerm(mTheory); if (info.getMixedVar() == null) { mTail.closeAPath(mHead, boundaryTerm, info); mTail.openAPath(mHead, boundaryTerm, info); } else { mTail.closeAPath(mHead, boundaryTerm, info); mTail.openAPath(mHead, boundaryTerm, info); final Occurrence occ = mInterpolator.getOccurrence( mPath[i + 1].getFlatTerm()); boundaryTerm = info.getMixedVar(); mTail.closeAPath(mHead, boundaryTerm, occ); mTail.openAPath(mHead, boundaryTerm, occ); } } } mComputed = true; } /** * Build an interpolant clause and add it to the interpolant set. * @param pre The disequalities summarizing the requirements from * the B-part in skipped argument paths. * @param lhsTerm The end of the A-equality chain. * @param rhsTerm The start of the A-equality chain. * @param isNegated True, if there is a disequality in the chain. */ private void addInterpolantClause(int color, Set<Term> pre) { final Term clause = pre == null ? mTheory.mFalse : mTheory.or(pre.toArray(new Term[pre.size()])); mInterpolants[color].add(clause); } @Override public String toString() { return "PathInfo[" + Arrays.toString(mPath) + "," + mHead + ',' + mTail + "," + mMaxColor + "]"; } public void addDiseq(CCEquality diseq) { final LitInfo info = mInterpolator.getLiteralInfo(diseq); Term boundaryTailTerm, boundaryHeadTerm; boundaryHeadTerm = mPath[0].toSMTTerm(mTheory); boundaryTailTerm = mPath[mPath.length - 1].toSMTTerm(mTheory); if (info.getMixedVar() == null) { mTail.closeAPath(mHead, boundaryTailTerm, info); mTail.openAPath(mHead, boundaryTailTerm, info); mHead.closeAPath(mTail, boundaryHeadTerm, info); mHead.openAPath(mTail, boundaryHeadTerm, info); } else { final Occurrence occHead = mInterpolator.getOccurrence( mPath[0].getFlatTerm()); mHead.closeAPath(mTail, boundaryHeadTerm, info); mHead.openAPath(mTail, boundaryHeadTerm, info); final Occurrence occTail = mInterpolator.getOccurrence( mPath[mPath.length - 1].getFlatTerm()); mTail.closeAPath(mHead, boundaryTailTerm, info); mTail.openAPath(mHead, boundaryTailTerm, info); mHead.closeAPath(mTail, info.getMixedVar(), occTail); mTail.closeAPath(mHead, info.getMixedVar(), occHead); } } public void close() { while (mHead.mColor < mNumInterpolants || mTail.mColor < mNumInterpolants) { if (mHead.mColor < mTail.mColor) { mHead.addPre(mHead.mColor, Coercion.buildEq( mHead.getBoundTerm(mHead.mColor), mTail.getBoundTerm(mMaxColor))); addInterpolantClause(mHead.mColor, mHead.mPre[mHead.mColor]); int parent = mHead.mColor + 1; while (mInterpolator.mStartOfSubtrees[parent] > mHead.mColor) { parent++; } mHead.mColor = parent; } else if (mHead.mColor == mTail.mColor) { mHead.addAllPre(mHead.mColor, mTail); mTail.mPre[mTail.mColor] = null; if (mHead.mColor < mMaxColor) { mHead.addPre(mHead.mColor, Coercion.buildDistinct( mHead.getBoundTerm(mHead.mColor), mTail.getBoundTerm(mHead.mColor))); } addInterpolantClause(mHead.mColor, mHead.mPre[mHead.mColor]); int parent = mHead.mColor + 1; while (mInterpolator.mStartOfSubtrees[parent] > mHead.mColor) { parent++; } mHead.mColor = parent; mTail.mColor = parent; } else { mTail.addPre(mTail.mColor, Coercion.buildEq( mHead.getBoundTerm(mMaxColor), mTail.getBoundTerm(mTail.mColor))); addInterpolantClause(mTail.mColor, mTail.mPre[mTail.mColor]); int parent = mTail.mColor + 1; while (mInterpolator.mStartOfSubtrees[parent] > mTail.mColor) { parent++; } mTail.mColor = parent; } } } } @SuppressWarnings("unchecked") public CCInterpolator(Interpolator ipolator) { mInterpolator = ipolator; mNumInterpolants = ipolator.mNumInterpolants; mTheory = ipolator.mTheory; mPaths = new HashMap<SymmetricPair<CCTerm>, PathInfo>(); mInterpolants = new Set[mNumInterpolants]; for (int i = 0; i < mNumInterpolants; i++) { mInterpolants[i] = new HashSet<Term>(); } } public Term[] computeInterpolants(Clause cl, CCAnnotation annot) { mEqualities = new HashMap<SymmetricPair<CCTerm>, CCEquality>(); for (int i = 0; i < cl.getSize(); i++) { final Literal lit = cl.getLiteral(i); if (lit.negate() instanceof CCEquality) { final CCEquality eq = (CCEquality) lit.negate(); mEqualities.put( new SymmetricPair<CCTerm>(eq.getLhs(), eq.getRhs()), eq); } } PathInfo mainPath = null; final CCTerm[][] paths = annot.getPaths(); for (int i = 0; i < paths.length; i++) { final CCTerm first = paths[i][0]; final CCTerm last = paths[i][paths[i].length - 1]; final PathInfo pathInfo = new PathInfo(paths[i]); mPaths.put(new SymmetricPair<CCTerm>(first, last), pathInfo); if (i == 0) { mainPath = pathInfo; } } mainPath.interpolatePathInfo(); final CCEquality diseq = annot.getDiseq(); if (diseq != null) { mainPath.addDiseq(diseq); } mainPath.close(); final Term[] interpolants = new Term[mNumInterpolants]; for (int i = 0; i < mNumInterpolants; i++) { interpolants[i] = mTheory.and(mInterpolants[i].toArray( new Term[mInterpolants[i].size()])); } return interpolants; } @Override public String toString() { return Arrays.toString(mInterpolants); } }