/*
* 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.proof;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
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.proof.ResolutionNode.Antecedent;
/**
* Fix a proof tree by replacing deleted nodes. We mark for the result of every
* ordered binary hyperresolution (aka {@link ResolutionNode}) which literals
* can be removed from this resolution. If this list contains a literal on the
* main path (i.e., the negation of a pivot stored in some resolution), it is
* assumed to contain all literals from that point upwards the hyperresolution
* chain.
* @author Juergen Christ
*/
public class FixProofDAG {
/**
* A work unit in the fixing process.
* @author Juergen Christ
*/
private static interface Worker {
public void process(FixProofDAG engine);
}
/**
* Collect the transformed resolution proof for a clause.
* @author Juergen Christ
*/
private static class CollectClause implements Worker {
/**
* The clause to collect.
*/
private final Clause mCls;
/**
* Set the clause to collect.
* @param cls The clause to collect.
*/
public CollectClause(Clause cls) {
mCls = cls;
}
@Override
public void process(FixProofDAG engine) {
final ResolutionNode rn = (ResolutionNode) mCls.getProof();
// Simplify rn
final Set<Literal> removed = engine.mDeletedNodes.get(mCls);
final Antecedent[] antes = rn.getAntecedents();
final ArrayDeque<Antecedent> newAntes =
new ArrayDeque<ResolutionNode.Antecedent>();
boolean deleted = false;
boolean changed = false;
Clause primary = null;
newprimary:
{
for (int i = antes.length - 1; i >= 0; --i) {
if (removed != null) {
if (removed.contains(antes[i].mPivot)) {
// Antecedent has been removed
changed = true;
continue;
}
deleted = removed.contains(antes[i].mPivot.negate());
}
final Clause cls = engine.mTransformed.get(antes[i].mAntecedent);
if (deleted || !cls.contains(antes[i].mPivot)) {
primary = cls;
changed = true;
break newprimary;
}
if (cls == antes[i].mAntecedent) {
newAntes.addFirst(antes[i]);
} else {
newAntes.addFirst(new Antecedent(antes[i].mPivot, cls));
changed = true;
}
}
primary = engine.mTransformed.get(rn.getPrimary());
changed |= primary != rn.getPrimary();
}
if (changed) {
// recompute clause
final HashSet<Literal> lits = new HashSet<Literal>();
for (int i = 0; i < primary.getSize(); ++i) {
lits.add(primary.getLiteral(i));
}
for (final Iterator<Antecedent> it = newAntes.iterator(); it.hasNext(); ) {
final Antecedent a = it.next();
final boolean resolutionStepUsed = lits.remove(a.mPivot.negate());
if (!resolutionStepUsed) {
it.remove();
continue;
}
for (int j = 0; j < a.mAntecedent.getSize(); ++j) {
final Literal lit = a.mAntecedent.getLiteral(j);
if (lit != a.mPivot) {
lits.add(lit);
}
}
}
Clause result;
if (newAntes.isEmpty()) {
result = primary;
} else {
final Antecedent[] nantes = newAntes.toArray(
new Antecedent[newAntes.size()]);
result = new Clause(lits.toArray(new Literal[lits.size()]),
new ResolutionNode(primary, nantes));
}
engine.mTransformed.put(mCls, result);
} else {
engine.mTransformed.put(mCls, mCls);
}
}
@Override
public String toString() {
return "Collect: " + mCls.toString();
}
}
/**
* Expands the node in the proof DAG represented by a given clause.
* @author Juergen Christ
*/
private static class ExpandClause implements Worker {
/**
* The clause to expand.
*/
private final Clause mCls;
/**
* Set the clause to expand.
* @param cls The clause to expand.
*/
public ExpandClause(Clause cls) {
mCls = cls;
}
@Override
public void process(FixProofDAG engine) {
if (engine.mTransformed.containsKey(mCls)) {
return;
}
final Set<Literal> removed = engine.mDeletedNodes.get(mCls);
final ProofNode pn = mCls.getProof();
if (pn.isLeaf()) {
// m_Cls stays unchanged
engine.mTransformed.put(mCls, mCls);
} else {
final ResolutionNode rn = (ResolutionNode) pn;
engine.mTodo.push(new CollectClause(mCls));
final Antecedent[] antes = rn.getAntecedents();
boolean deleted = false;
for (int i = antes.length - 1; !deleted && i >= 0; --i) {
if (removed != null) {
if (removed.contains(antes[i].mPivot)) {
// Antecedent has been removed
continue;
}
deleted = removed.contains(antes[i].mPivot.negate());
}
engine.mTodo.push(new ExpandClause(antes[i].mAntecedent));
}
if (!deleted) {
engine.mTodo.push(new ExpandClause(rn.getPrimary()));
}
}
}
@Override
public String toString() {
return "Expand: " + mCls.toString();
}
}
/**
* The todo stack.
*/
private final Deque<Worker> mTodo = new ArrayDeque<Worker>();
/**
* Mapping for input to replaced clauses.
*/
private HashMap<Clause, Clause> mTransformed =
new HashMap<Clause, Clause>();
/**
* The set of deleted nodes. This can either be clauses or antecedents
* which are used to represent the result of a resolution step.
*/
private Map<Clause, Set<Literal>> mDeletedNodes;
/**
* Clear the transformation cache.
*/
public void reset() {
mTransformed = new HashMap<Clause, Clause>();
}
/**
* Fix a proof tree rooted at <code>rt</code>. Note that <code>rt</code>
* cannot be deleted for this algorithm to work properly.
* @param rt The root of the proof tree to fix.
* @param deletedNodes The deleted nodes of the proof tree.
* @return A new proof tree with all removed nodes replaced.
*/
public Clause fix(Clause rt, Map<Clause, Set<Literal>> deletedNodes) {
mDeletedNodes = deletedNodes;
mTodo.push(new ExpandClause(rt));
run();
return mTransformed.get(rt);
}
/**
* Non-recursive walk over the proof tree and process the nodes.
*/
private final void run() {
while (!mTodo.isEmpty()) {
final Worker w = mTodo.pop();
w.process(this);
}
}
}