/** * Returns true if the language of this automaton is finite. The * automaton must not have any dead states. */ public static boolean isFinite(Automaton a) { if (a.getNumStates() == 0) { return true; } return isFinite(new Transition(), a, 0, new BitSet(a.getNumStates()), new BitSet(a.getNumStates()), 0); }
/** Returns true if this automaton has any states that cannot * be reached from the initial state or cannot reach an accept state. * Cost is O(numTransitions+numStates). */ public static boolean hasDeadStates(Automaton a) { BitSet liveStates = getLiveStates(a); int numLive = liveStates.cardinality(); int numStates = a.getNumStates(); assert numLive <= numStates: "numLive=" + numLive + " numStates=" + numStates + " " + liveStates; return numLive < numStates; }
private static Set<Integer> toSet(Automaton a, int offset) { int numStates = a.getNumStates(); BitSet isAccept = a.getAcceptStates(); Set<Integer> result = new HashSet<Integer>(); int upto = 0; while (upto < numStates && (upto = isAccept.nextSetBit(upto)) != -1) { result.add(offset+upto); upto++; } return result; }
/** Use this constructor when the automaton failed to determinize. */ public TooComplexToDeterminizeException(Automaton automaton, int maxDeterminizedStates) { super("Determinizing automaton with " + automaton.getNumStates() + " states and " + automaton.getNumTransitions() + " transitions would result in more than " + maxDeterminizedStates + " states."); this.automaton = automaton; this.regExp = null; this.maxDeterminizedStates = maxDeterminizedStates; }
/** Set or clear this state as an accept state. */ public void setAccept(int state, boolean accept) { FutureObjects.checkIndex(state, getNumStates()); isAccept.set(state, accept); }
/** Returns the topological sort of all states reachable from * the initial state. Behavior is undefined if this * automaton has cycles. CPU cost is O(numTransitions), * and the implementation is recursive so an automaton * matching long strings may exhaust the java stack. */ public static int[] topoSortStates(Automaton a) { if (a.getNumStates() == 0) { return new int[0]; } int numStates = a.getNumStates(); int[] states = new int[numStates]; final BitSet visited = new BitSet(numStates); int upto = topoSortStatesRecurse(a, visited, states, 0, 0, 0); if (upto < states.length) { // There were dead states int[] newStates = new int[upto]; System.arraycopy(states, 0, newStates, 0, upto); states = newStates; } // Reverse the order: for(int i=0;i<states.length/2;i++) { int s = states[i]; states[i] = states[states.length-1-i]; states[states.length-1-i] = s; } return states; }
/** * Returns the articulation points (or cut vertices) of the graph: * https://en.wikipedia.org/wiki/Biconnected_component */ public int[] articulationPoints() { if (det.getNumStates() == 0) { return new int[0]; } // Automaton.Builder undirect = new Automaton.Builder(); undirect.copy(det); for (int i = 0; i < det.getNumStates(); i++) { int numT = det.initTransition(i, transition); for (int j = 0; j < numT; j++) { det.getNextTransition(transition); undirect.addTransition(transition.dest, i, transition.min); } } int numStates = det.getNumStates(); BitSet visited = new BitSet(numStates); int[] depth = new int[det.getNumStates()]; int[] low = new int[det.getNumStates()]; int[] parent = new int[det.getNumStates()]; Arrays.fill(parent, -1); List<Integer> points = new ArrayList<>(); articulationPointsRecurse(undirect.finish(), 0, 0, depth, low, parent, visited, points); Collections.reverse(points); return points.stream().mapToInt(p -> p).toArray(); }
/** Returns bitset marking states reachable from the initial state. */ private static BitSet getLiveStatesFromInitial(Automaton a) { int numStates = a.getNumStates(); BitSet live = new BitSet(numStates); if (numStates == 0) { return live; } ArrayDeque<Integer> workList = new ArrayDeque<>(); live.set(0); workList.add(0); Transition t = new Transition(); while (workList.isEmpty() == false) { int s = workList.removeFirst(); int count = a.initTransition(s, t); for(int i=0;i<count;i++) { a.getNextTransition(t); if (live.get(t.dest) == false) { live.set(t.dest); workList.add(t.dest); } } } return live; }
int stateOffset = getNumStates(); states = ArrayUtil.grow(states, nextState + other.nextState); System.arraycopy(other.states, 0, states, nextState, other.nextState); int otherNumStates = other.getNumStates(); BitSet otherAcceptStates = other.getAcceptStates(); int state = 0;
/** Copies over all states from other. */ public void copyStates(Automaton other) { int otherNumStates = other.getNumStates(); for (int s = 0; s < otherNumStates; s++) { int newState = createState(); setAccept(newState, other.isAccept(s)); } } }
/** Sugar to get all transitions for all states. This is * object-heavy; it's better to iterate state by state instead. */ public Transition[][] getSortedTransitions() { int numStates = getNumStates(); Transition[][] transitions = new Transition[numStates][]; for(int s=0;s<numStates;s++) { int numTransitions = getNumTransitions(s); transitions[s] = new Transition[numTransitions]; for(int t=0;t<numTransitions;t++) { Transition transition = new Transition(); getTransition(s, t, transition); transitions[s][t] = transition; } } return transitions; }
/** * Returns an automaton that accepts the union of the languages of the given * automata. * <p> * Complexity: linear in number of states. */ public static Automaton union(Collection<Automaton> l) { Automaton result = new Automaton(); // Create initial state: result.createState(); // Copy over all automata for(Automaton a : l) { result.copy(a); } // Add epsilon transition from new initial state int stateOffset = 1; for(Automaton a : l) { if (a.getNumStates() == 0) { continue; } result.addEpsilon(0, stateOffset); stateOffset += a.getNumStates(); } result.finishState(); return removeDeadStates(result); }
/** * Constructor. * * @param a Automaton to create finite string from. * @param startState The starting state for each path. * @param endState The state where each path should stop or -1 if only accepted states should be final. */ public FiniteStringsIterator(Automaton a, int startState, int endState) { this.a = a; this.endState = endState; this.nodes = new PathNode[16]; for (int i = 0, end = nodes.length; i < end; i++) { nodes[i] = new PathNode(); } this.string = new IntsRefBuilder(); this.pathStates = new BitSet(a.getNumStates()); this.string.setLength(0); this.emitEmptyString = a.isAccept(0); // Start iteration with node startState. if (a.getNumTransitions(startState) > 0) { pathStates.set(startState); nodes[0].resetState(a, startState); string.append(startState); } }
/** Returns sink state, if present, else -1. */ private static int findSinkState(Automaton automaton) { int numStates = automaton.getNumStates(); Transition t = new Transition(); int foundState = -1; for (int s=0;s<numStates;s++) { if (automaton.isAccept(s)) { int count = automaton.initTransition(s, t); boolean isSinkState = false; for(int i=0;i<count;i++) { automaton.getNextTransition(t); if (t.dest == s && t.min == 0 && t.max == 0xff) { isSinkState = true; break; } } if (isSinkState) { foundState = s; break; } } } return foundState; }
/** * Returns a (deterministic) automaton that accepts the complement of the * language of the given automaton. * <p> * Complexity: linear in number of states if already deterministic and * exponential otherwise. * @param maxDeterminizedStates maximum number of states determinizing the * automaton can result in. Set higher to allow more complex queries and * lower to prevent memory exhaustion. */ static public Automaton complement(Automaton a, int maxDeterminizedStates) { a = totalize(determinize(a, maxDeterminizedStates)); int numStates = a.getNumStates(); for (int p=0;p<numStates;p++) { a.setAccept(p, !a.isAccept(p)); } return removeDeadStates(a); }
/** Copies over all states/transitions from other. */ public void copy(Automaton other) { int offset = getNumStates(); int otherNumStates = other.getNumStates(); // Copy all states copyStates(other); // Copy all transitions Transition t = new Transition(); for(int s=0;s<otherNumStates;s++) { int count = other.initTransition(s, t); for(int i=0;i<count;i++) { other.getNextTransition(t); addTransition(offset + s, offset + t.dest, t.min, t.max); } } }
/** * Returns an automaton that accepts the union of the empty string and the * language of the given automaton. This may create a dead state. * <p> * Complexity: linear in number of states. */ static public Automaton optional(Automaton a) { Automaton result = new Automaton(); result.createState(); result.setAccept(0, true); if (a.getNumStates() > 0) { result.copy(a); result.addEpsilon(0, 1); } result.finishState(); return result; }
int numStates = a.getNumStates(); BitSet liveSet = getLiveStates(a);
int numStates = a.getNumStates();
int numStates = a.getNumStates(); for(int i=0;i<numStates;i++) { result.createState();