/// <summary> /// Initializes a new instance of the <see cref="Condensation"/> class. /// </summary> /// <param name="automaton">The automaton.</param> /// <param name="root">The root of the condensation DAG.</param> /// <param name="transitionFilter"> /// A function specifying whether the transition should be treated as an edge /// of the automaton graph while building the condensation. /// </param> /// <param name="useApproximateClosure"> /// Specifies whether <see cref="Weight.ApproximateClosure"/> should be used /// instead of <see cref="Weight.Closure"/> in semiring computations. /// </param> internal Condensation( Automaton <TSequence, TElement, TElementDistribution, TSequenceManipulator, TThis> automaton, State root, Func <Transition, bool> transitionFilter, bool useApproximateClosure) { Debug.Assert(transitionFilter != null, "A valid transition filter must be provided."); this.automaton = automaton; this.Root = root; this.transitionFilter = transitionFilter; this.useApproximateClosure = useApproximateClosure; this.components = this.FindStronglyConnectedComponents(); this.stateInfo = PreallocatedAutomataObjects.LeaseComputeCondensationState(); for (int i = 0; i < this.components.Count; ++i) { StronglyConnectedComponent component = this.components[i]; for (int j = 0; j < component.Size; ++j) { this.stateInfo.Add(component.GetStateByIndex(j).Index, CondensationStateInfo.Default); } } }
/// <summary> /// Optimizes the automaton by removing all states which can't reach end states. /// </summary> public bool RemoveDeadStates() { var builder = this.builder; var(edgesStart, edges) = BuildReversedGraph(); //// Now run a depth-first search to label all reachable nodes var(stack, visited) = PreallocatedAutomataObjects.LeaseRemoveDeadStatesState(builder.StatesCount); for (var i = 0; i < builder.StatesCount; ++i) { if (!visited[i] && builder[i].CanEnd) { visited[i] = true; stack.Push(i); while (stack.Count != 0) { var stateIndex = stack.Pop(); for (var j = edgesStart[stateIndex]; j < edgesStart[stateIndex + 1]; ++j) { var destinationIndex = edges[j]; if (!visited[destinationIndex]) { visited[destinationIndex] = true; stack.Push(destinationIndex); } } } } } if (!visited[builder.StartStateIndex]) { builder.Clear(); builder.StartStateIndex = builder.AddState().Index; return(true); } return(this.builder.RemoveStates(visited, false) > 0); (int[] edgesStart, int[] edges) BuildReversedGraph() { // [edgesStart[i]; edgesStart[i+1]) is a range `edges` array which corresponds to incoming edges for state `i` // edges1 is incoming edges for state. Represented as index of source state var(edgesStart1, edges1) = PreallocatedAutomataObjects.LeaseBuildReversedGraphState( builder.StatesCount + 1, builder.TransitionsCount); // first populate edges for (var i = 0; i < builder.StatesCount; ++i) { for (var iterator = builder[i].TransitionIterator; iterator.Ok; iterator.Next()) { ++edgesStart1[iterator.Value.DestinationStateIndex]; } } // calculate commutative sums. Now edgesStart[i] contains end of range for (var i = 0; i < builder.StatesCount; ++i) { edgesStart1[i + 1] += edgesStart1[i]; } // Fill ranges and adjust start indices. Now edgesStart[i] contains begining of the range for (var i = 0; i < builder.StatesCount; ++i) { for (var iterator = builder[i].TransitionIterator; iterator.Ok; iterator.Next()) { var index = --edgesStart1[iterator.Value.DestinationStateIndex]; edges1[index] = i; } } return(edgesStart1, edges1); } }
/// <summary> /// Implements <a href="http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm">Tarjan's algorithm</a> /// for finding the strongly connected components of the automaton graph. /// </summary> private List <StronglyConnectedComponent> FindStronglyConnectedComponents() { var components = new List <StronglyConnectedComponent>(); var states = this.automaton.States; int traversalIndex = 0; var(stateIdStack, stateInfo, traversalStack, generation) = PreallocatedAutomataObjects.LeaseFindStronglyConnectedComponentsState(states.Count); traversalStack.Push(new IntPair(this.Root.Index, 0)); while (traversalStack.Count > 0) { var(currentStateIndex, currentTransitionIndex) = traversalStack.Pop(); var currentState = states[currentStateIndex]; var transitions = currentState.Transitions; if (stateInfo[currentStateIndex].Generation != generation) { ++traversalIndex; // Entered into processing of this state for the first time: create Tarjan info for it // and push the state onto Tarjan stack. stateInfo[currentStateIndex] = new TarjanStateInfo(generation, traversalIndex); stateIdStack.Push(currentStateIndex); } else { // Just returned from recursion into 'currentTransitionIndex': update Lowlink // and advance to next transition. var lastDestination = transitions[currentTransitionIndex].DestinationStateIndex; stateInfo[currentStateIndex].Lowlink = Math.Min( stateInfo[currentStateIndex].Lowlink, stateInfo[lastDestination].Lowlink); ++currentTransitionIndex; } for (; currentTransitionIndex < transitions.Count; ++currentTransitionIndex) { var transition = transitions[currentTransitionIndex]; if (!this.transitionFilter(transition)) { continue; } var destinationStateIndex = transition.DestinationStateIndex; if (stateInfo[destinationStateIndex].Generation != generation) { // Return to this (state/transition) after destinationState is processed. // Processing will resume from currentTransitionIndex. traversalStack.Push(new IntPair(currentStateIndex, currentTransitionIndex)); // Process destination state. traversalStack.Push(new IntPair(destinationStateIndex, 0)); // Do not process other transitions until destination state is processed. break; } if (stateInfo[destinationStateIndex].InStack) { stateInfo[currentStateIndex].Lowlink = Math.Min( stateInfo[currentStateIndex].Lowlink, stateInfo[destinationStateIndex].TraversalIndex); } } if (currentTransitionIndex < transitions.Count) { // Not all states processed: continue processing according to stack. Current state // is already pushed onto it -> will be processed again. continue; } if (stateInfo[currentStateIndex].Lowlink == stateInfo[currentStateIndex].TraversalIndex) { var statesInComponent = new List <State>(); while (true) { var stateIndex = stateIdStack.Pop(); stateInfo[stateIndex].InStack = false; statesInComponent.Add(states[stateIndex]); if (stateIndex == currentStateIndex) { break; } } components.Add(new StronglyConnectedComponent( this.automaton, this.transitionFilter, statesInComponent, this.useApproximateClosure)); this.TotalStatesCount += statesInComponent.Count; } } return(components); }
public void Dispose() { PreallocatedAutomataObjects.ReleaseComputeCondensationState(this.stateInfo); }
/// <summary> /// Removes a set of states from the automaton where the set is defined by labels not matching /// the <paramref name="removeLabel"/>. /// </summary> /// <param name="labels">State labels</param> /// <param name="removeLabel">Label which marks states which should be deleted</param> public int RemoveStates(bool[] labels, bool removeLabel) { var oldToNewStateIdMapping = PreallocatedAutomataObjects.LeaseRemoveStatesState(this.states.Count); var newStateId = 0; var deadStateCount = 0; for (var stateId = 0; stateId < this.states.Count; ++stateId) { if (labels[stateId] != removeLabel) { oldToNewStateIdMapping[stateId] = newStateId++; } else { oldToNewStateIdMapping[stateId] = -1; ++deadStateCount; } } if (deadStateCount == 0) { return(0); } this.StartStateIndex = oldToNewStateIdMapping[this.StartStateIndex]; // Caller must ensure that it doesn't try to delete start state. Because it will lead to // invalid automaton. Debug.Assert(this.StartStateIndex != -1); for (var i = 0; i < this.states.Count; ++i) { var newId = oldToNewStateIdMapping[i]; if (newId == -1) { // remove all transitions for (var iterator = this[i].TransitionIterator; iterator.Ok; iterator.Next()) { iterator.Remove(); } continue; } Debug.Assert(newId <= i); this.states[newId] = this.states[i]; // Remap transitions for (var iterator = this[newId].TransitionIterator; iterator.Ok; iterator.Next()) { var transition = iterator.Value; var destinationStateIndex = oldToNewStateIdMapping[transition.DestinationStateIndex]; if (destinationStateIndex == -1) { iterator.Remove(); } else { iterator.Value = transition.With(destinationStateIndex: destinationStateIndex); } } } this.states.RemoveRange(newStateId, this.states.Count - newStateId); return(deadStateCount); }