/*
* Copyright (C) 2012-2013 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.delta;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import de.uni_freiburg.informatik.ultimate.logic.AnnotatedTerm;
import de.uni_freiburg.informatik.ultimate.logic.Annotation;
import de.uni_freiburg.informatik.ultimate.logic.ApplicationTerm;
import de.uni_freiburg.informatik.ultimate.logic.Term;
import de.uni_freiburg.informatik.ultimate.logic.TermTransformer;
import de.uni_freiburg.informatik.ultimate.smtinterpol.delta.TermSimplifier.Mode;
import de.uni_freiburg.informatik.ultimate.smtinterpol.smtlib2.ParseEnvironment;
import de.uni_freiburg.informatik.ultimate.util.datastructures.ScopedHashMap;
public class Minimizer {
private static class OutputReaper extends Thread implements Runnable {
private final static int CHUNK_SIZE = 1024;
private InputStream mToReap;
public OutputReaper() {
setDaemon(true);
}
@Override
public void run() {
final byte[] chunk = new byte[CHUNK_SIZE];
while (true) {
try {
synchronized(this) {
wait();
}
} catch (final InterruptedException eie) {
// Interrupted while waiting for something to do => terminate
return;
}
if (mToReap == null) {
// woken up without anything to do => terminate thread
return;
}
try {
while (mToReap.read(chunk) != -1) {
;
}
} catch (final IOException ignored) {
// Ignore exception and wait since process died...
}
mToReap = null;
}
}
public synchronized void setToReap(InputStream toReap) {
mToReap = toReap;
notifyAll();
}
}
private class DeactivateCmds implements BinSearch.Driver<Cmd> {
@Override
public Boolean prepare(List<Cmd> sublist) {
if (mVerbosity > 1) {
System.err.println("Trying " + sublist);
}
for (final Cmd cmd : sublist) {
cmd.deactivate();
}
return null;
}
@Override
public void failure(List<Cmd> sublist) {
for (final Cmd cmd : sublist) {
cmd.activate();
}
}
@Override
public void success(List<Cmd> sublist) {
// Commands remain deactivated. shrinkCmdList will do the cleanup.
}
}
private class SimplifyTerms implements BinSearch.Driver<Substitution> {
private final AbstractOneTermCmd mCmd;
private final SubstitutionManager mMgr;
private final List<Substitution> mSubsts;
private final HashMap<Term, Boolean> mSeen;
private List<Cmd> mPres;
public SimplifyTerms(AbstractOneTermCmd cmd, SubstitutionManager mgr,
List<Substitution> substs, HashMap<Term, Boolean> cache) {
mCmd = cmd;
mMgr = mgr;
mSubsts = substs;
mSeen = cache;
}
@Override
public Boolean prepare(List<Substitution> sublist) {
final SubstitutionApplier applier = new SubstitutionApplier();
for (final Substitution subst : sublist) {
subst.activate();
}
if (mVerbosity > 1) {
System.err.println("Active substs: " + sublist);
}
applier.init(mMgr.getDepth(), mSubsts);
final Term simp = applier.apply(mCmd.getTerm(mUnletRelet));
if (mVerbosity > 1) {
System.err.println("simp = " + simp);
}
final Boolean res = mSeen.get(simp);
if (res != null && !res.booleanValue()) {
for (final Substitution s : sublist) {
s.deactivate();
}
return res;
}
mCmd.setTerm(simp, mUnletRelet);
mPres = applier.getAdds();
mCmd.appendPreCmds(mPres);
if (res != null && res.booleanValue()) {
success(sublist);
}
return res;
}
@Override
public void failure(List<Substitution> sublist) {
mSeen.put(mCmd.getTerm(mUnletRelet), Boolean.FALSE);
mCmd.removePreCmds(mPres);
mCmd.failure();
for (final Substitution subst : sublist) {
subst.deactivate();
}
mPres = null;
}
@Override
public void success(List<Substitution> sublist) {
mSeen.put(mCmd.getTerm(mUnletRelet), Boolean.TRUE);
for (final Substitution s : sublist) {
s.success();
}
mCmd.success();
mPres = null;
}
}
private static class RemoveScopes implements BinSearch.Driver<Scope> {
private final List<Cmd> mCmds;
public RemoveScopes(List<Cmd> cmds) {
mCmds = cmds;
}
@Override
public Boolean prepare(List<Scope> sublist) {
for (final Scope s : sublist) {
for (int i = s.mFirst; i < s.mLast; ++i) {
mCmds.get(i).deactivate();
}
final ScopeCmd sc = (ScopeCmd) mCmds.get(s.mLast);
final int remScopes = sc.getNumScopes() - s.mReduce;
if (remScopes == 0) {
sc.deactivate();
} else {
sc.tryNumScopes(remScopes);
}
}
return null;
}
@Override
public void failure(List<Scope> sublist) {
for (final Scope s : sublist) {
for (int i = s.mFirst; i < s.mLast; ++i) {
mCmds.get(i).activate();
}
final ScopeCmd sc = (ScopeCmd) mCmds.get(s.mLast);
if (sc.isActive()) {
sc.reset();
} else {
sc.activate();
}
}
}
@Override
public void success(List<Scope> sublist) {
for (final Scope s : sublist) {
s.mDeactivated = true;
}
}
}
private final class RemoveNeutrals implements BinSearch.Driver<Neutral> {
private final AbstractOneTermCmd mCmd;
public RemoveNeutrals(AbstractOneTermCmd cmd) {
mCmd = cmd;
}
@Override
public Boolean prepare(List<Neutral> sublist) {
if (mVerbosity > 1) {
System.err.println("Trying " + sublist);
}
final Term rem = new NeutralRemover(sublist).removeNeutrals(mCmd.getTerm(mUnletRelet));
if (mVerbosity > 1) {
System.err.println("Result: " + rem);
}
mCmd.setTerm(rem, mUnletRelet);
return null;
}
@Override
public void failure(List<Neutral> sublist) {
mCmd.failure();
}
@Override
public void success(List<Neutral> sublist) {
mCmd.success();
}
}
private List<Cmd> mCmds;
private final int mGoldenExit;
private final File mTmpFile, mResultFile;
private final String mSolver;
private int mTestCtr = 0, mSuccTestCtr = 0;
/**
* The verbosity level for the delta debugger. Values are:
* <th><td>level</td><td>meaning</td></th>
* <tr><td>0</td><td>only final statistics</td></tr>
* <tr><td>1</td><td>print current and successful phases</td></tr>
* <tr><td>2</td><td>print also debugging information for phases</td></tr>
* <tr><td>>2</td><td>print also debugging information about tests</td></tr>
*/
private final int mVerbosity;
private final OutputReaper mOut, mErr;
private final boolean mUnletRelet;
public Minimizer(List<Cmd> cmds, int goldenExit,
File tmpFile, File resultFile, String solver, int verbosity,
OutputReaper out, OutputReaper err, boolean unletRelet) {
mCmds = cmds;
mGoldenExit = goldenExit;
mTmpFile = tmpFile;
mResultFile = resultFile;
mSolver = solver;
mVerbosity = verbosity;
mOut = out;
mErr = err;
mUnletRelet = unletRelet;
}
public boolean deltaDebug() throws IOException, InterruptedException {
if (mVerbosity > 0) {
System.err.println("# commands: " + mCmds.size());
}
int numRounds = 0;
boolean cmds = false, terms = false, bindings = false, neutrals = false,
lists = false, ips = false, decls = false;
final boolean scopes = removeScopes();
do {
cmds = removeCmds();
terms = simplifyTerms(true);
decls = removeDecls();
shrinkCmdList();
terms = simplifyTerms(false);
bindings = removeBindings();
neutrals = removeNeutrals();
lists = simplifyTermListCmds();
ips = simplifyGetInterpolants();
decls = removeDecls() || decls;
// Not needed anymore since I don't do further tests...
// shrinkCmdList();
++numRounds;
if (mVerbosity > 0) {
if (cmds) {
System.err.println("Removed commands");
}
if (terms) {
System.err.println("Simplified terms");
}
if (bindings) {
System.err.println("Removed bindings");
}
if (neutrals) {
System.err.println("Removed neutrals");
}
if (lists) {
System.err.println("Simplifed term command lists");
}
if (ips) {
System.err.println("Simplified get-interpolants");
}
if (decls) {
System.err.println("Removed declarations");
}
}
} while (
cmds || terms || bindings || neutrals || lists || ips || decls);
final boolean features = removeFeatures();
System.err.println("# tests: " + mTestCtr);
System.err.println("# successful tests: " + mSuccTestCtr);
System.err.println("# rounds: " + numRounds);
return scopes || numRounds > 1 || features;
}
private static class Scope {
int mFirst;
int mLast;
int mReduce;
List<Scope> mNested;
boolean mDeactivated = false;
public Scope(int f) {
mFirst = f;
}
public void nest(Scope s) {
if (mNested == null) {
mNested = new ArrayList<Scope>();
}
mNested.add(s);
}
}
private List<Scope> detectScopes() {
final ArrayDeque<Scope> ppStack = new ArrayDeque<Scope>();
// All toplevel scopes.
final List<Scope> res = new ArrayList<Scope>();
for (int i = 0; i < mCmds.size(); ++i) {
final Cmd cmd = mCmds.get(i);
if (!cmd.isActive()) {
continue;
}
if (cmd instanceof ScopeCmd) {
final ScopeCmd sc = (ScopeCmd) cmd;
if (sc.isScopeStart()) {
if (mVerbosity > 1) {
System.err.println("Found scope start at " + i);
}
final Scope s = new Scope(i);
for (int n = 0; n < sc.getNumScopes(); ++n) {
ppStack.push(s);
}
} else {
if (mVerbosity > 1) {
System.err.println("Found scope end at " + i);
}
for (int n = 0; n < sc.getNumScopes(); ++n) {
final Scope last = ppStack.pop();
final Scope next = ppStack.peek();
// We have found a scope end...
last.mLast = i;
last.mReduce = n + 1;
if (next == null) {
// toplevel scope
res.add(last);
} else if (last != next) {
next.nest(last);
}
}
}
}
}
return res;
}
private boolean removeScopes() throws IOException, InterruptedException {
if (mVerbosity > 0) {
System.err.println("Removing scopes...");
}
boolean res = false;
final ArrayDeque<List<Scope>> todo = new ArrayDeque<List<Scope>>();
todo.push(detectScopes());
while (!todo.isEmpty()) {
final List<Scope> scopes = todo.pop();
final BinSearch<Scope> bs = new BinSearch<Scope>(
scopes, new RemoveScopes(mCmds));
res |= bs.run(this);
for (final Scope s : scopes) {
if (!s.mDeactivated && s.mNested != null) {
todo.push(s.mNested);
}
}
}
if (mVerbosity > 0) {
System.err.println("...done");
}
return res;
}
private boolean removeCmds() throws IOException, InterruptedException {
if (mVerbosity > 0) {
System.err.println("Removing commands...");
}
final List<Cmd> cmds = new ArrayList<Cmd>();
for (int i = 0; i < mCmds.size(); ++i) {
final Cmd cmd = mCmds.get(i);
if (!cmd.isActive()) {
continue;
}
if (cmd.canBeRemoved() && !cmd.hasDefinitions()) {
cmds.add(cmd);
}
}
final boolean res = deactivateCmds(cmds);
if (mVerbosity > 0) {
System.err.println("...done");
}
return res;
}
private boolean deactivateCmds(List<Cmd> toDeactivate)
throws IOException, InterruptedException {
final DeactivateCmds driver = new DeactivateCmds();
final BinSearch<Cmd> bs = new BinSearch<Cmd>(toDeactivate, driver);
return bs.run(this);
}
private boolean removeDecls() throws IOException, InterruptedException {
if (mVerbosity > 0) {
System.err.println("Removing unused declarations...");
}
// Collect used definitions
ScopedHashMap<String, Cmd> definitions =
new ScopedHashMap<String, Cmd>();
final Set<Cmd> usedDefs = new HashSet<Cmd>();
for (final Cmd cmd : mCmds) {
if (!cmd.isActive()) {
continue;
}
cmd.addUsedDefinitions(definitions, usedDefs);
if (cmd.hasDefinitions()) {
cmd.insertDefinitions(definitions);
}
if (cmd instanceof ScopeCmd) {
final ScopeCmd scope = (ScopeCmd) cmd;
if (scope.isScopeStart()) {
for (int i = 0; i < scope.getNumScopes(); ++i) {
definitions.beginScope();
}
} else {
for (int i = 0; i < scope.getNumScopes(); ++i) {
definitions.endScope();
}
}
}
}
// Free some space...
definitions = null;
// Collect unused definitions
final List<Cmd> unusedDefs = new ArrayList<Cmd>();
for (final Cmd cmd : mCmds) {
if (!cmd.isActive()) {
continue;
}
if (cmd.hasDefinitions() && !usedDefs.contains(cmd)) {
unusedDefs.add(cmd);
}
if (cmd instanceof AbstractOneTermCmd) {
for (final Cmd pre : ((AbstractOneTermCmd) cmd).getPreCmds()) {
if (pre.isActive() && pre.hasDefinitions()
&& !usedDefs.contains(pre)) {
unusedDefs.add(pre);
}
}
}
}
boolean res = deactivateCmds(unusedDefs);
// Now, we have deactivated all unused definitions that can be
// removed completely from the input. But unfortunately some of these
// definitions might be :named annotations and we still need the term!
// Try to only remove the annotation.
for (final Iterator<Cmd> it = unusedDefs.iterator(); it.hasNext(); ) {
final Cmd next = it.next();
if (!next.isActive() || !isNamedAssert(next)) {
it.remove();
}
}
final BinSearch<Cmd> bs = new BinSearch<Cmd>(
unusedDefs, new BinSearch.Driver<Cmd>() {
@Override
public Boolean prepare(List<Cmd> sublist) {
for (final Cmd cmd : sublist) {
final OneTermCmd tcmd = (OneTermCmd) cmd;
final Term stripped = new TermTransformer() {
@Override
public void postConvertAnnotation(AnnotatedTerm old,
Annotation[] newAnnots, Term newBody) {
final ArrayList<Annotation> noNames =
new ArrayList<Annotation>(newAnnots.length);
for (final Annotation a : newAnnots) {
if (!a.getKey().equals(":named")) {
noNames.add(a);
}
}
setResult(noNames.isEmpty() ? newBody
: old.getTheory().annotatedTerm(noNames.toArray(
new Annotation[noNames.size()]),
newBody));
}
}.transform(tcmd.getTerm(mUnletRelet));// NOCHECKSTYLE
tcmd.setTerm(stripped, mUnletRelet);
}
return null;
}
@Override
public void failure(List<Cmd> sublist) {
for (final Cmd cmd : sublist) {
((OneTermCmd) cmd).failure();
}
}
@Override
public void success(List<Cmd> sublist) {
for (final Cmd cmd : sublist) {
((OneTermCmd) cmd).success();
}
}
});// NOCHECKSTYLE
res |= bs.run(this);
if (mVerbosity > 0) {
System.err.println("...done");
}
return res;
}
private boolean isUnnamedAssert(AbstractOneTermCmd cmd) {
return (cmd instanceof OneTermCmd)
&& ((OneTermCmd) cmd).getCmd().equals("assert")
&& !cmd.hasDefinitions();
}
private boolean isNamedAssert(Cmd cmd) {
if (cmd instanceof OneTermCmd) {
final OneTermCmd tcmd = (OneTermCmd) cmd;
if (tcmd.getCmd().equals("assert")
&& tcmd.getTerm(false) instanceof AnnotatedTerm) {
for (final Annotation a : ((AnnotatedTerm) tcmd.getTerm(false)).
getAnnotations()) {
if (a.getKey().equals(":named")) {
return true;
}
}
}
}
return false;
}
private boolean removeUnusedCore(Mode mode)
throws IOException, InterruptedException {
boolean res = false;
final TermSimplifier simp = new TermSimplifier(mode);
for (final Cmd cmd : mCmds) {
if (!cmd.isActive() || !(cmd instanceof AbstractOneTermCmd)) {
continue;
}
final AbstractOneTermCmd tcmd = (AbstractOneTermCmd) cmd;
final Term s = simp.transform(tcmd.getTerm(mUnletRelet));
if (s != tcmd.getTerm(mUnletRelet)) {
tcmd.setTerm(s, mUnletRelet);
if (test()) {
res = true;
tcmd.success();
} else {
tcmd.failure();
}
}
}
return res;
}
private boolean removeBindings() throws IOException, InterruptedException {
return removeUnusedCore(Mode.BINDINGS);
}
private boolean removeNeutrals() throws IOException, InterruptedException {
// return removeUnusedCore(Mode.NEUTRALS);
boolean result = false;
for (final Cmd cmd : mCmds) {
if (!cmd.isActive() || !(cmd instanceof AbstractOneTermCmd)) {
continue;
}
final AbstractOneTermCmd tcmd = (AbstractOneTermCmd) cmd;
final List<Neutral> neutrals = new NeutralDetector().detect(tcmd.getTerm(mUnletRelet));
if (neutrals.isEmpty()) {
continue;
}
final RemoveNeutrals driver = new RemoveNeutrals(tcmd);
final BinSearch<Neutral> bs = new BinSearch<Neutral>(neutrals, driver);
result |= bs.run(this);
}
return result;
}
private boolean simplifyTerms(boolean simpAsserts) throws IOException, InterruptedException {
if (mVerbosity > 0) {
System.err.println("Simplifying terms...");
}
boolean res = false;
for (final Cmd cmd : mCmds) {
if (!cmd.isActive() || !(cmd instanceof AbstractOneTermCmd)
|| (simpAsserts ^ (cmd instanceof OneTermCmd && ((OneTermCmd) cmd).getCmd().equals("assert")))) {
continue;
}
boolean localres = false;
final AbstractOneTermCmd tcmd = (AbstractOneTermCmd) cmd;
final SubstitutionManager substmgr =
new SubstitutionManager(tcmd, mUnletRelet);
// Try to simplify this one command...
if (isUnnamedAssert(tcmd)) {
// We should not substitute the top level
substmgr.deepen();
}
final HashMap<Term, Boolean> testCache = new HashMap<Term, Boolean>();
deepen: while (substmgr.deepen()) {// NOCHECKSTYLE
List<Substitution> substs;
do {
substs = substmgr.getSubstitutions();
if (substs.isEmpty()) {
continue deepen;
}
if (mVerbosity > 1) {
System.err.println("Term: " + tcmd.getTerm(false));
System.err.println("Substs: " + substs);
}
final SimplifyTerms driver = new SimplifyTerms(
tcmd, substmgr, substs, testCache);
final BinSearch<Substitution> bs =
new BinSearch<Substitution>(substs, driver);
localres |= bs.run(this);
} while (substmgr.failed());
}
res |= localres;
} // Cmd-loop
if (mVerbosity > 0) {
System.err.println("...done");
}
return res;
}
private Term unfoldAnd(Term p1, Term p2) {
final ArrayList<Term> conjuncts = new ArrayList<Term>();
ApplicationTerm at = (ApplicationTerm) p1;
if (at.getFunction() == at.getTheory().mAnd) {
conjuncts.addAll(Arrays.asList(at.getParameters()));
} else {
conjuncts.add(p1);
}
at = (ApplicationTerm) p2;
if (at.getFunction() == at.getTheory().mAnd) {
conjuncts.addAll(Arrays.asList(at.getParameters()));
} else {
conjuncts.add(p2);
}
return p1.getTheory().term("and",
conjuncts.toArray(new Term[conjuncts.size()]));
}
private int numChildren(int[] sos, int parent) {
int numChildren = 0;
int child = parent - 1;
while (child >= sos[parent]) {
++numChildren;
child = sos[child] - 1;
}
return numChildren;
}
private void mergeWithChild(GetInterpolants gi, int parent, int child) {
final int[] sos = gi.getStartOfSubtree();
int childidx = parent - 1;
for (/* Nothing */ ; child > 0; --child) {
childidx = sos[childidx];
}
final Term[] partition = gi.getPartition();
final Term[] newPartition = new Term[partition.length - 1];
final int[] newSos = new int[sos.length - 1];
int diff = 0;
for (int i = 0; i < partition.length; ++i) {
if (i == childidx) {
diff = 1;
} else if (i == parent) {
newPartition[i - diff] =
unfoldAnd(partition[childidx], partition[parent]);
newSos[i - diff] = Math.max(sos[i] - 1, 0);
} else {
newPartition[i - diff] = partition[i];
newSos[i - diff] = Math.max(sos[i] - 1, 0);
}
}
gi.setNewPartition(newPartition);
gi.setNewStartOfSubtree(newSos);
}
private boolean mergeTree(GetInterpolants gi)
throws IOException, InterruptedException {
boolean res = false;
int[] sos = gi.getStartOfSubtree();
int n = sos.length;
for (int i = 1; i < n; ++i) {
//@ invariant n == gi.getPartition().length && 0<= i <= n
// invariant n >= 2 is hidden in assumption about interpolation tree
int children = numChildren(sos, i);
for (int child = 0; child < children; /*Nothing*/) {
//@ invariant 0 <= child <= children
//@ invariant old(children) >= children
//@ invariant n == gi.getPartition().length
//@ invariant i <= n
// invariant n >= 2 see above
if (n == 2) {
// No further merge possible!
return res;
}
mergeWithChild(gi, i, child);
if (test()) {
res = true;
gi.success();
sos = gi.getStartOfSubtree();
--i;
--n;
--children;
} else {
gi.failure();
++child;
}
}
}
return res;
}
private boolean isAnd(Term t) {
return ((ApplicationTerm) t).getFunction() == t.getTheory().mAnd;
}
private boolean simplifyAndParition(GetInterpolants gi, int idx) throws IOException, InterruptedException {
Term[] partition = gi.getPartition();
Term[] conjs =
((ApplicationTerm) partition[idx]).getParameters();
int c = 0;
boolean res = false;
while (c < conjs.length) {
final ArrayList<Term> newcs =
new ArrayList<Term>(conjs.length - 1);
for (int j = 0; j < conjs.length; ++j) {
if (j != c) {
newcs.add(conjs[j]);
}
}
final Term[] newPartition = partition.clone();
newPartition[idx] = buildAnd(newcs);
gi.setNewPartition(newPartition);
gi.setNewStartOfSubtree(gi.getStartOfSubtree());
if (test()) {
gi.success();
conjs = ((ApplicationTerm) newPartition[idx]).
getParameters();
partition = newPartition;
res = true;
// Don't increment c since we shifted elements
} else {
gi.failure();
++c;
}
}
return res;
}
private boolean simplifyInterpolantPartitions(GetInterpolants gi)
throws IOException, InterruptedException {
boolean res = false;
Term[] partition = gi.getPartition();
if (partition.length == 2) {
if (isAnd(partition[0])) {
res |= simplifyAndParition(gi, 0);
}
if (isAnd(partition[1])) {
res |= simplifyAndParition(gi, 1);
}
return res;
}
int i = 0;
while (i < partition.length) {
// Try to remove partition i
// 1. complete
final int newlength = partition.length - 1;
if (newlength < 2) {
// We cannot remove anything anymore!!!
return res;
}
final Term[] newPartition = new Term[newlength];
final int[] newSos = new int[newlength];
final int[] sos = gi.getStartOfSubtree();
int diff = 0;
for (int j = 0; j < partition.length; ++j) {
if (j == i) {
diff = 1;
} else {
newPartition[j - diff] = partition[j];
newSos[j - diff] = Math.max(0, sos[j] - diff);
}
}
gi.setNewPartition(newPartition);
gi.setNewStartOfSubtree(newSos);
if (test()) {
gi.success();
partition = newPartition;
res = true;
// Don't increment i since we shifted a new element here
} else {
gi.failure();
// 2. If conjunctive partition, try to simplify conjunction
if (isAnd(partition[i])) {
res |= simplifyAndParition(gi, i);
}
++i;
}
}
return res;
}
private static ApplicationTerm buildAnd(List<Term> conjs) {
if (conjs.isEmpty()) {
return null;
}
if (conjs.size() == 1) {
return (ApplicationTerm) conjs.get(0);
}
return conjs.get(0).getTheory().term(
"and", conjs.toArray(new Term[conjs.size()]));
}
private boolean simplifyGetInterpolants()
throws IOException, InterruptedException {
if (mVerbosity > 0) {
System.err.println("Simplifying get-interpolants...");
}
boolean res = false;
final Map<Term, Term> actualNames = new HashMap<Term, Term>();
for (final Cmd cmd : mCmds) {
if (!cmd.isActive()) {
continue;
}
if (cmd instanceof GetInterpolants) {
final GetInterpolants gi = (GetInterpolants) cmd;
res |= simplifyInterpolantPartitions(gi);
// This should be superseded by simplifyInterpolantPartitions
// res |= removeTruePartitions(gi, actualNames);
// This should be superseded by mergeTree
// res |= mergeSequential(gi);
res |= mergeTree(gi);
} else if (isNamedAssert(cmd)) {
final AbstractOneTermCmd tcmd = (AbstractOneTermCmd) cmd;
final AnnotatedTerm t = (AnnotatedTerm) tcmd.getTerm(false);
final Term v = t.getSubterm();
for (final Annotation a : t.getAnnotations()) {
if (a.getKey().equals(":named")) {
actualNames.put(
t.getTheory().term(a.getValue().toString()), v);
}
}
}
}
if (mVerbosity > 0) {
System.err.println("...done");
}
return res;
}
private boolean simplifyTermListCmds()
throws IOException, InterruptedException {
if (mVerbosity > 0) {
System.err.println("Simplifying term list commands...");
}
final List<TermListCmd> cmds = new ArrayList<TermListCmd>();
for (final Cmd cmd : mCmds) {
if (!cmd.isActive()) {
continue;
}
if (cmd instanceof TermListCmd) {
final TermListCmd tcmd = (TermListCmd) cmd;
if (tcmd.getTerms().length > 1) {
cmds.add(tcmd);
}
}
}
if (cmds.isEmpty()) {
if (mVerbosity > 0) {
System.err.println("...done");
}
return false;
}
// Try to reduce number of terms in the list
// First try to reduce all cmds to their lower half terms.
boolean goon = true;
boolean res = false;
while (goon) {
goon = false;
for (final TermListCmd cmd : cmds) {
final Term[] terms = cmd.getTerms();
final Term[] newTerms = new Term[terms.length / 2];
System.arraycopy(terms, 0, newTerms, 0, newTerms.length);
cmd.setNewTerms(newTerms);
}
if (test()) {
for (final TermListCmd cmd : cmds) {
cmd.success();
}
res = true;
goon = true;
} else {
// We had a failure => Try to reduce to the other half
for (final TermListCmd cmd : cmds) {
cmd.failure();
final Term[] terms = cmd.getTerms();
final int len = terms.length - terms.length / 2;
final Term[] newTerms = new Term[len];
System.arraycopy(terms, terms.length / 2, newTerms, 0,
newTerms.length);
cmd.setNewTerms(newTerms);
}
if (test()) {
for (final TermListCmd cmd : cmds) {
cmd.success();
}
res = true;
goon = true;
} else {
for (final TermListCmd cmd : cmds) {
cmd.failure();
}
// Both reductions failed => give up
if (mVerbosity > 0) {
System.err.println("...done");
}
return res;
}
}
}
// Actually dead code, but required by the java compiler
return res;
}
private void shrinkCmdList() {
if (mVerbosity > 0) {
System.err.println("Shrinking command list...");
}
int newsize = 0;
for (final Iterator<Cmd> it = mCmds.iterator(); it.hasNext(); ) {
if (it.next().isActive()) {
++newsize;
}
}
if (mVerbosity > 1) {
System.err.println(mCmds.size() + " -> " + newsize);
}
final List<Cmd> tmp = new ArrayList<Cmd>(newsize);
for (final Iterator<Cmd> it = mCmds.iterator(); it.hasNext(); ) {
final Cmd cmd = it.next();
if (cmd.isActive()) {
tmp.add(cmd);
}
}
mCmds = tmp;
if (mVerbosity > 0) {
System.err.println("...done");
}
}
private boolean removeFeatures() throws IOException, InterruptedException {
final Map<String, Cmd> features = new HashMap<String, Cmd>();
for (final Cmd cmd : mCmds) {
if (cmd.isActive()) {
final String feature = cmd.provideFeature();
if (feature != null) {
if (mVerbosity > 1) {
System.err.println("Found feature " + feature);
}
features.put(feature, cmd);
}
}
}
for (final Cmd cmd : mCmds) {
if (cmd.isActive()) {
cmd.checkFeature(features);
}
}
final List<Cmd> featureProvider = new ArrayList<Cmd>(features.values());
if (mVerbosity > 1) {
System.err.println("Trying to remove features " + featureProvider);
}
return deactivateCmds(featureProvider);
}
/**
* Test a modified input script for error reproduction.
* @return Did the error still occur?
* @throws IOException
* @throws InterruptedException
*/
boolean test() throws IOException, InterruptedException {
++mTestCtr;
if (mVerbosity > 2) {
System.err.println("Dumping...");
}
dumpCmds();
if (mVerbosity > 2) {
System.err.println("Testing...");
}
final Process p = Runtime.getRuntime().exec(mSolver);
mOut.setToReap(p.getInputStream());
mErr.setToReap(p.getErrorStream());
final int exitVal = p.waitFor();
if (exitVal == mGoldenExit) {
++mSuccTestCtr;
if (mVerbosity > 2) {
System.err.println("Success");
}
Files.copy(mTmpFile.toPath(), mResultFile.toPath(),
StandardCopyOption.REPLACE_EXISTING);
return true;
}
if (mVerbosity > 2) {
System.err.println("Failure");
}
return false;
}
private void dumpCmds() throws FileNotFoundException {
final PrintWriter out = new PrintWriter(mTmpFile);
for (final Cmd cmd : mCmds) {
if (cmd.isActive()) {
cmd.dump(out);
}
}
out.flush();
out.close();
}
public static void usage() {
System.err.println(
"Usage: Minimizer <infile> <outfile> [-v] [-golden <num>] <command> <args>");
System.err.println("where");
System.err.println(" infile is the original input file");
System.err.println(" outfile is the desired output file");
System.err.println(" command is the command to start the solver");
System.err.println(" -golden <num> sets expected exit code to \"num\" and safes initial test");
System.err.println(" -v make output more verbose (can be repeated)");
System.err.println(" -u forces all terms to be unlet and (potentially) reletted");
System.err.println(" args are optional arguments to \"command\"");
System.exit(0);
}
public static void main(String[] args) {
if (args.length < 3) {
usage();
}
int goldenExit = 0;
final String infile = args[0];
final String outfile = args[1];
int cmdstart = 2;
int arg = 2;
boolean foundArg = true;
int verbosity = 0;
boolean unletReletMode = false;
while (foundArg && arg < args.length) {
foundArg = false;
if (args[arg].equals("-v")) {
++verbosity;
++arg;
foundArg = true;
} else if (args[arg].equals("-golden")) {
if (++arg == args.length) {
usage();
}
try {
goldenExit = Integer.parseInt(args[arg]);
} catch (final NumberFormatException eNAN) {
usage();
}
foundArg = true;
++arg;
} else if (args[arg].equals("-u")) {
unletReletMode = true;
++arg;
foundArg = true;
}
}
cmdstart = arg;
StringBuilder command = new StringBuilder();
if (cmdstart >= args.length) {
usage();
}
for (int i = cmdstart; i < args.length; ++i) {
command.append(args[i]).append(' ');
}
final File resultFile = new File(outfile);
try {
final File tmpFile = File.createTempFile("minimize", ".smt2");
tmpFile.deleteOnExit();
final File input = new File(infile);
command.append(tmpFile.getAbsolutePath());
final String solver = command.toString();
// Free space
command = null;
// Start the output reapers
final OutputReaper out = new OutputReaper();
final OutputReaper err = new OutputReaper();
out.start();
err.start();
if (goldenExit == 0) {
Files.copy(input.toPath(), tmpFile.toPath(),
StandardCopyOption.REPLACE_EXISTING);
if (verbosity > 2) {
System.err.println("Starting " + solver);
}
Process p = Runtime.getRuntime().exec(solver);
out.setToReap(p.getInputStream());
err.setToReap(p.getErrorStream());
goldenExit = p.waitFor();
// Free space
p = null;
}
if (verbosity > 0) {
System.err.println("Got golden exit code: " + goldenExit);
}
ParseScript ps = new ParseScript();
ParseEnvironment pe = new ParseEnvironment(ps, null) {
@Override
public void printSuccess() {
// Disable output
}
@Override
public void printValues(Map<Term, Term> values) {
// Disable output
}
@Override
public void printResponse(Object response) {
// Disable output
}
@Override
public void printInfoResponse(String info, Object response) {
// Disable output
}
@Override
public void printTermResponse(Term[] response) {
// Disable output
}
};
if (verbosity > 0) {
System.err.println("Begin parsing");
}
pe.parseScript(infile);
// Free space
pe = null;
if (verbosity > 0) {
System.err.println("Parsing done");
}
final Minimizer mini = new Minimizer(
ps.getCmds(), goldenExit, tmpFile, resultFile, solver,
verbosity, out, err, unletReletMode);
// Free space
ps = null;
if (!mini.deltaDebug()) {
System.err.println("Failed to minimize");
}
// Gracefully terminate our threads.
out.setToReap(null);
err.setToReap(null);
out.join();
err.join();
} catch (final IOException e) {
e.printStackTrace();
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
}