diff --git a/prism/src/acceptance/AcceptanceGeneric.java b/prism/src/acceptance/AcceptanceGeneric.java index 2ac902ea..febba209 100644 --- a/prism/src/acceptance/AcceptanceGeneric.java +++ b/prism/src/acceptance/AcceptanceGeneric.java @@ -32,7 +32,10 @@ import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; import java.util.List; +import java.util.Stack; +import acceptance.AcceptanceRabin.RabinPair; +import acceptance.AcceptanceStreett.StreettPair; import prism.PrismException; import prism.PrismNotSupportedException; import jdd.JDDVars; @@ -366,6 +369,369 @@ public class AcceptanceGeneric implements AcceptanceOmega { return this.clone(); } + /** + * Attempt to convert this generic acceptance condition + * to a Rabin condition. If this condition does not have + * the required form (top-level disjunctions with conjunctive pairs), + * returns {@code null}. + *
+ * If the condition does not syntactically have the required + * from but can be easily converted to one (e.g., by converting + * a single Inf(S) to a Fin(emptyset) & Inf(S) etc) or by swapping + * Inf and Fin, this will be done. + *
+ * The BitSets for the state sets are cloned, i.e., modifications of + * the state sets of the returned acceptance condition will not affect + * the original generic acceptance condition. + * + * @param numStates the number of states in the underlying model/automaton (for generating state sets for true) + * @return the generated AcceptanceRabin condition, or false on failure + */ + public AcceptanceRabin toAcceptanceRabin(int numStates) + { + AcceptanceRabin result = new AcceptanceRabin(); + List disjuncts = extractDisjuncts(); + + for (AcceptanceGeneric term : disjuncts) { + RabinPair pair = term.toAcceptanceRabinPair(numStates); + if (pair == null) return null; + result.add(pair); + } + + return result; + } + + /** + * Attempt to convert this generic acceptance to a Rabin pair. + * + * @param numStates the number of states in the underlying model/automaton (for generating state sets for true) + * @return the generated RabinPair, or false on failure + */ + private RabinPair toAcceptanceRabinPair(int numStates) + { + BitSet L = null, K = null; + switch (getKind()) { + case TRUE: + L = new BitSet(); + K = new BitSet(); + K.set(0, numStates); + break; + case FALSE: + L = new BitSet(); + L.set(0, numStates); + K = new BitSet(); + break; + case AND: { + AcceptanceGeneric left = getLeft(); + AcceptanceGeneric right = getRight(); + + if (left.getKind() == ElementType.INF || left.getKind() == ElementType.INF_NOT) { + // swap + AcceptanceGeneric tmp = left; + left = right; + right = tmp; + } + + switch (left.getKind()) { + case AND: + case OR: + case FALSE: + case TRUE: + case INF: + case INF_NOT: + // not a Rabin condition + return null; + case FIN: + // Fin(A) <=> <>[]!A + // L = A + L = (BitSet) left.getStates().clone(); + break; + case FIN_NOT: + // Fin(!A) <=> <>[]A + // L = !A + L = (BitSet) left.getStates().clone(); + L.flip(0, numStates); + break; + } + + switch (right.getKind()) { + case AND: + case OR: + case FALSE: + case TRUE: + case FIN: + case FIN_NOT: + // not a Rabin condition + return null; + case INF: + // Inf(A) <=> []<>A + // K = A + K = (BitSet) right.getStates().clone(); + break; + case INF_NOT: + // Inf(!A) <=> []<>!A + // K = !A + K = (BitSet) right.getStates().clone(); + K.flip(0, numStates); + break; + } + + break; + } + case FIN: + // Fin(A) <=> <>[]!A & []<> true <=> RabinPair(A, true) + // L = A + L = (BitSet) getStates().clone(); + // K = true + K = new BitSet(); + K.set(0, numStates); + break; + case FIN_NOT: + // Fin(!A) <=> <>[]A & []<> true <=> RabinPair(!A, true) + // L = !A + L = (BitSet) getStates().clone(); + L.flip(0, numStates); + // K = true + K = new BitSet(); + K.set(0, numStates); + break; + case INF: + // Inf(A) <=> <>[]!false & []<> A <=> RabinPair(false, A) + // L = false + L = new BitSet(); + // K = A + K = (BitSet) getStates().clone(); + break; + case INF_NOT: + // Inf(!A) <=> <>[]!false & []<> !A <=> RabinPair(false, !A) + // L = false + L = new BitSet(); + // K = !A + K = (BitSet) getStates().clone(); + K.flip(0, numStates); + break; + case OR: + // not a Rabin pair + return null; + } + + return new AcceptanceRabin.RabinPair(L, K); + } + + /** + * Attempt to convert this generic acceptance condition + * to a Streett condition. If this condition does not have + * the required form (top-level conjunction with disjunctive pairs), + * returns {@code null}. + *
+ * If the condition does not syntactically have the required + * from but can be easily converted to one (e.g., by converting + * a single Inf(S) to a Fin(true) | Inf(S) etc) or by swapping + * Inf and Fin, this will be done. + *
+ * The BitSets for the state sets are cloned, i.e., modifications of + * the state sets of the returned acceptance condition will not affect + * the original generic acceptance condition. + * + * @param numStates the number of states in the underlying model/automaton (for generating state sets for true) + * @return the generated AcceptanceRabin condition, or false on failure + */ + public AcceptanceStreett toAcceptanceStreett(int numStates) + { + AcceptanceStreett result = new AcceptanceStreett(); + List conjuncts = extractConjuncts(); + + for (AcceptanceGeneric term : conjuncts) { + StreettPair pair = term.toAcceptanceStreettPair(numStates); + if (pair == null) return null; + result.add(pair); + } + + return result; + } + + /** + * Attempt to convert this generic acceptance to a Streett pair. + * + * @param numStates the number of states in the underlying model/automaton (for generating state sets for true) + * @return the generated RabinPair, or false on failure + */ + private StreettPair toAcceptanceStreettPair(int numStates) + { + BitSet R = null, G = null; + switch (getKind()) { + case TRUE: + // true = []<> false -> []<> false + R = new BitSet(); + G = new BitSet(); + break; + case FALSE: + // false = []<> true -> []<> false + R = new BitSet(); + R.set(0, numStates); + G = new BitSet(); + break; + case OR: { + AcceptanceGeneric left = getLeft(); + AcceptanceGeneric right = getRight(); + + if (left.getKind() == ElementType.INF || left.getKind() == ElementType.INF_NOT) { + // swap + AcceptanceGeneric tmp = left; + left = right; + right = tmp; + } + + switch (left.getKind()) { + case AND: + case OR: + case FALSE: + case TRUE: + case INF: + case INF_NOT: + // not a Streett condition + return null; + case FIN: + // Fin(A) -> R = A + R = (BitSet) left.getStates().clone(); + break; + case FIN_NOT: + // Fin(!A) -> R = !A + R = (BitSet) left.getStates().clone(); + R.flip(0, numStates); + break; + } + + switch (right.getKind()) { + case AND: + case OR: + case FALSE: + case TRUE: + case FIN: + case FIN_NOT: + // not a Streett condition + return null; + case INF: + // Inf(A) -> G = A + G = (BitSet) right.getStates().clone(); + break; + case INF_NOT: + // Inf(!A) -> G = !A + G = (BitSet) right.getStates().clone(); + G.flip(0, numStates); + break; + } + + break; + } + case FIN: + // Fin(A) <=> []<>A -> []<>false + // R = A + R = (BitSet) getStates().clone(); + // G = false + G = new BitSet(); + break; + case FIN_NOT: + // Fin(!A) <=> []<>!A -> []<>false + // R = !A + R = (BitSet) getStates().clone(); + R.flip(0, numStates); + // G = false + G = new BitSet(); + break; + case INF: + // Inf(A) <=> []<>true -> []<>A + // R = true + R = new BitSet(); + R.set(0, numStates); + // G = A + G = (BitSet) getStates().clone(); + break; + case INF_NOT: + // Inf(!A) <=> []<>true -> []<>!A + // R = true + R = new BitSet(); + R.set(0, numStates); + // G = !A + G = (BitSet) getStates().clone(); + G.flip(0, numStates); + break; + case AND: + // not a Streett pair + return null; + } + + return new AcceptanceStreett.StreettPair(R,G); + } + + /** + * Extract the operands of the top-level disjunctions, e.g., + * for (A & B) | (C | D)) would return the list [A&B,C,D]. + */ + public List extractDisjuncts() + { + ArrayList result = new ArrayList(); + Stack todo = new Stack(); + + todo.add(this); + while (!todo.isEmpty()) { + AcceptanceGeneric current = todo.pop(); + switch (current.getKind()) { + case AND: + case FALSE: + case TRUE: + case FIN: + case FIN_NOT: + case INF: + case INF_NOT: + // not a top level disjunction, add to list + result.add(current); + break; + case OR: + // still a top level disjunction, recurse + todo.push(current.getRight()); + todo.push(current.getLeft()); + break; + } + } + + return result; + } + + /** + * Extract the operands of the top-level conjunctions, e.g., + * for (A | B) & (C & D)) would return the list [A|B,C,D]. + */ + public List extractConjuncts() + { + ArrayList result = new ArrayList(); + Stack todo = new Stack(); + + todo.add(this); + while (!todo.isEmpty()) { + AcceptanceGeneric current = todo.pop(); + switch (current.getKind()) { + case OR: + case FALSE: + case TRUE: + case FIN: + case FIN_NOT: + case INF: + case INF_NOT: + // not a top level conjunction, add to list + result.add(current); + break; + case AND: + // still a top level conjunction, recurse + todo.push(current.getRight()); + todo.push(current.getLeft()); + break; + } + } + + return result; + } + @Override public String toString() { switch(kind) {