diff --git a/IncrementalMinimization/src/Main.java b/IncrementalMinimization/src/Main.java index 6d869282..1d409a6d 100644 --- a/IncrementalMinimization/src/Main.java +++ b/IncrementalMinimization/src/Main.java @@ -1,8 +1,10 @@ +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; +import java.util.List; import java.util.Set; import org.apache.commons.lang3.NotImplementedException; @@ -19,44 +21,116 @@ public class Main { private static class EquivTest { - private SFA aut; - private HashSet equiv; - private HashSet path; + private final SFA aut; + private final BooleanAlgebra ba; + private final DisjointSets equivClasses; - public EquivTest(SFA aut, HashSet equiv, HashSet path) + private HashSet> neq; + private HashSet> equiv; + private HashSet> path; + + public EquivTest(SFA aut, BooleanAlgebra ba, DisjointSets equivClasses, + HashSet> neq, HashSet> equiv, HashSet> path) { this.aut = aut; + this.ba = ba; + this.equivClasses = equivClasses; + this.neq = neq; this.equiv = equiv; this.path = path; } - public boolean isEquiv(Integer p, Integer q) throws NotImplementedException + private List> findNonDisjointMoves(Collection> outp, Collection> outq) throws TimeoutException + { + //TODO: look into local minterm generation, can be more efficient? + assert(!outp.isEmpty() && !outq.isEmpty()); //TODO: remove assertions + SFAInputMove pMove = outp.iterator().next(); + P pGuard = pMove.guard; + for(SFAInputMove qMove : outq) + { + P qGuard = qMove.guard; + P pqAnd = ba.MkAnd(pGuard, qGuard); + if(ba.IsSatisfiable(pqAnd)) + { + return Arrays.asList(pMove,qMove); + } + } + return null; + } + + public boolean isEquiv(Integer p, Integer q) throws TimeoutException { - //TODO - throw new NotImplementedException("Incremental Equivalence Not Implemented Yet"); + List pair = Arrays.asList(p, q); //Should already be normalized + if (neq.contains(pair)) + { + return false; + } + if (path.contains(pair)) + { + return true; + } + path.add(pair); + Collection> outp = new ArrayList>(aut.getInputMovesFrom(p)); + Collection> outq = new ArrayList>(aut.getInputMovesFrom(q)); + while (!outp.isEmpty() && !outq.isEmpty()) + { + List> nonDisjointGuards = findNonDisjointMoves(outp,outq); + SFAInputMove pMove = nonDisjointGuards.get(0); + SFAInputMove qMove = nonDisjointGuards.get(1); + //note: we don't actually need to generate a witness, only need to know pMove,qMove are non-disjoint + Integer pNextClass = equivClasses.find(pMove.to); + Integer qNextClass = equivClasses.find(qMove.to); + List nextPair = normalize(pNextClass, qNextClass); + if ( !pNextClass.equals(qNextClass) && !equiv.contains(nextPair)) + { + equiv.add(nextPair); + if(!isEquiv(pNextClass, qNextClass)) + { + return false; + } + else + { + path.remove(nextPair); + } + } + outp.remove(pMove); + outq.remove(qMove); + P newPGuard = ba.MkAnd(pMove.guard, ba.MkNot(qMove.guard)); + if (ba.IsSatisfiable(newPGuard)) + { + outp.add(new SFAInputMove(pMove.from, pMove.to, newPGuard)); + } + P newQGuard = ba.MkAnd(qMove.guard, ba.MkNot(pMove.guard)); + if (ba.IsSatisfiable(newQGuard)) + { + outq.add(new SFAInputMove(qMove.from, qMove.to, newQGuard)); + } + } + equiv.add(pair); + return true; } - public HashSet getEquiv() + public HashSet> getEquiv() { return equiv; } - public HashSet getPath() + public HashSet> getPath() { return path; } } - private static Integer[] normalize(Integer p, Integer q) + private static List normalize(Integer p, Integer q) { - Integer[] pair; + List pair; if(p SFA incrementalMinimize(SFA aut, BooleanAlgebra< { return SFA.getEmptySFA(ba); } - aut = aut.determinize(ba).mkTotal(ba); + aut = aut.determinize(ba).mkTotal(ba); //TODO: normalizes? DisjointSets equivClasses = new DisjointSets(); for(Integer q : aut.getStates()) { equivClasses.make(q); } Integer num_pairs = aut.getStates().size() * aut.getStates().size(); //n^2 where n is # of states - HashSet neq = new HashSet(num_pairs, 1); //set is given initial capacity of n^2, won't exceed this + HashSet> neq = new HashSet>(num_pairs, 1); //set is given initial capacity of n^2, won't exceed this for(Integer p : aut.getFinalStates()) { for(Integer q : aut.getNonFinalStates()) @@ -91,10 +165,9 @@ public static SFA incrementalMinimize(SFA aut, BooleanAlgebra< { continue; } - Integer[] pair = new Integer[]{p,q}; + List pair = Arrays.asList(p,q); if(neq.contains(pair)) { - //Already know p,q not equivalent continue; } else if(equivClasses.find(p) == equivClasses.find(q)) @@ -104,21 +177,21 @@ else if(equivClasses.find(p) == equivClasses.find(q)) } //TODO: look into efficiency of HashSet operations, ideally should be O(1) for searching, inserting, removing - HashSet equiv = new HashSet(num_pairs,1); - HashSet path = new HashSet(num_pairs,1); - EquivTest pEquiv = new EquivTest(aut, equiv, path); + HashSet> equiv = new HashSet>(num_pairs,1); + HashSet> path = new HashSet>(num_pairs,1); + EquivTest pEquiv = new EquivTest(aut, ba, equivClasses, neq, equiv, path); if(pEquiv.isEquiv(p,q)) { //p,q found equivalent. Other pairs may be found equivalent. - for(Integer[] equivPair : pEquiv.getEquiv()) + for(List equivPair : pEquiv.getEquiv()) { - equivClasses.union(equivPair[0], equivPair[1]); + equivClasses.union(equivPair.get(0), equivPair.get(1)); } } else { //p,q found non-equivalent. Other pairs may be found non-equivalent. - for(Integer[] pathPair : pEquiv.getPath()) + for(List pathPair : pEquiv.getPath()) { neq.add(pathPair); } @@ -203,5 +276,7 @@ public static void main(String[] args) throws TimeoutException SFA minAut = incrementalMinimize(aut, ba); + System.out.println(minAut.getStates().size()); + System.out.println(aut.minimize(ba).getStates().size()); } }