/// <summary> /// Recursively increases the value of this automaton on <paramref name="sequence"/> by <paramref name="weight"/>. /// </summary> /// <param name="stateIndex">Index of currently traversed state.</param> /// <param name="isNewState">Indicates whether state <paramref name="stateIndex"/> was just created.</param> /// <param name="selfLoopAlreadyMatched">Indicates whether self-loop on state <paramref name="stateIndex"/> was just matched.</param> /// <param name="firstAllowedStateIndex">The minimum index of an existing state that can be used for the sequence.</param> /// <param name="currentSequencePos">The current position in the generalized sequence.</param> /// <param name="sequence">The generalized sequence.</param> /// <param name="weight">The weight of the sequence.</param> /// <returns> /// <see langword="true"/> if the subsequence starting at <paramref name="currentSequencePos"/> has been successfully merged in, /// <see langword="false"/> otherwise. /// </returns> /// <remarks> /// This function attempts to add as few new states and transitions as possible. /// Its implementation is conceptually similar to adding string to a trie. /// </remarks> private bool DoAddGeneralizedSequence( int stateIndex, bool isNewState, bool selfLoopAlreadyMatched, int firstAllowedStateIndex, int currentSequencePos, GeneralizedSequence sequence, Weight weight) { bool success; var builder = this.builder; var state = builder[stateIndex]; if (currentSequencePos == sequence.Count) { if (!selfLoopAlreadyMatched) { // We can't finish in a state with a self-loop for (var iterator = state.TransitionIterator; iterator.Ok; iterator.Next()) { if (iterator.Value.DestinationStateIndex == state.Index) { return(false); } } } state.SetEndWeight(Weight.Sum(state.EndWeight, weight)); return(true); } var element = sequence[currentSequencePos]; // Treat self-loops elements separately if (element.LoopWeight.HasValue) { if (selfLoopAlreadyMatched) { // Previous element was also a self-loop, we should try to find an espilon transition for (var iterator = state.TransitionIterator; iterator.Ok; iterator.Next()) { var transition = iterator.Value; if (transition.DestinationStateIndex != state.Index && transition.IsEpsilon && transition.DestinationStateIndex >= firstAllowedStateIndex) { if (this.DoAddGeneralizedSequence( transition.DestinationStateIndex, false, false, firstAllowedStateIndex, currentSequencePos, sequence, Weight.Product(weight, Weight.Inverse(transition.Weight)))) { return(true); } } } // Epsilon transition not found, let's create a new one var destination = state.AddEpsilonTransition(Weight.One); success = this.DoAddGeneralizedSequence( destination.Index, true, false, firstAllowedStateIndex, currentSequencePos, sequence, weight); Debug.Assert(success, "This call must always succeed."); return(true); } // Find a matching self-loop for (var iterator = state.TransitionIterator; iterator.Ok; iterator.Next()) { var transition = iterator.Value; if (transition.IsEpsilon && transition.DestinationStateIndex != state.Index && transition.DestinationStateIndex >= firstAllowedStateIndex) { // Try this epsilon transition if (this.DoAddGeneralizedSequence( transition.DestinationStateIndex, false, false, firstAllowedStateIndex, currentSequencePos, sequence, weight)) { return(true); } } // Is it a self-loop? if (transition.DestinationStateIndex == state.Index) { // Do self-loops match? if ((transition.Weight == element.LoopWeight.Value) && (element.Group == transition.Group) && ((transition.IsEpsilon && element.IsEpsilonSelfLoop) || (!transition.IsEpsilon && !element.IsEpsilonSelfLoop && transition.ElementDistribution.Equals(element.ElementDistribution)))) { // Skip the element in the sequence, remain in the same state success = this.DoAddGeneralizedSequence( stateIndex, false, true, firstAllowedStateIndex, currentSequencePos + 1, sequence, weight); Debug.Assert(success, "This call must always succeed."); return(true); } // StateIndex also has a self-loop, but the two doesn't match return(false); } } if (!isNewState) { // Can't add self-loop to an existing state, it will change the language accepted by the state return(false); } // Add a new self-loop state.AddTransition(element.ElementDistribution, element.LoopWeight.Value, stateIndex, element.Group); success = this.DoAddGeneralizedSequence(stateIndex, false, true, firstAllowedStateIndex, currentSequencePos + 1, sequence, weight); Debug.Assert(success, "This call must always succeed."); return(true); } // Try to find a transition for the element for (var iterator = state.TransitionIterator; iterator.Ok; iterator.Next()) { var transition = iterator.Value; if (transition.IsEpsilon && transition.DestinationStateIndex != state.Index && transition.DestinationStateIndex >= firstAllowedStateIndex) { // Try this epsilon transition if (this.DoAddGeneralizedSequence( transition.DestinationStateIndex, false, false, firstAllowedStateIndex, currentSequencePos, sequence, weight)) { return(true); } } // Is it a self-loop? if (transition.DestinationStateIndex == state.Index) { if (selfLoopAlreadyMatched) { // The self-loop was checked or added by the caller continue; } // Can't go through an existing self-loop, it will allow undesired sequences to be accepted return(false); } if (transition.DestinationStateIndex < firstAllowedStateIndex || element.Group != transition.Group || !element.ElementDistribution.Equals(transition.ElementDistribution)) { continue; } // Skip the element in the sequence, move to the destination state // Weight of the existing transition must be taken into account // This case can fail if the next element is a self-loop and the destination state already has a different one if (this.DoAddGeneralizedSequence( transition.DestinationStateIndex, false, false, firstAllowedStateIndex, currentSequencePos + 1, sequence, Weight.Product(weight, Weight.Inverse(transition.Weight)))) { return(true); } } // Add a new transition var newChild = state.AddTransition(element.ElementDistribution, Weight.One, null, element.Group); success = this.DoAddGeneralizedSequence( newChild.Index, true, false, firstAllowedStateIndex, currentSequencePos + 1, sequence, weight); Debug.Assert(success, "This call must always succeed."); return(true); }
/// <summary> /// Initializes a new instance of the <see cref="WeightedSequence"/> struct. /// </summary> /// <param name="sequence">The <see cref="GeneralizedSequence"/></param> /// <param name="weight">The <see cref="Weight"/> for the specified sequence</param> public WeightedSequence(GeneralizedSequence sequence, Weight weight) : this() { this.Sequence = sequence; this.Weight = weight; }
/// <summary> /// Recursively builds a complete list of generalized sequences accepted by the simplifiable part of the automaton. /// </summary> /// <param name="stateIndex">The currently traversed state.</param> /// <param name="generalizedTreeNodes">The state labels obtained from <see cref="FindGeneralizedTrees"/>.</param> /// <param name="weightedSequences">The sequence list being built.</param> /// <param name="currentSequenceElements">The list of elements of the sequence currently being built.</param> /// <param name="currentWeight">The weight of the sequence currently being built.</param> private void DoBuildAcceptedSequenceList( int stateIndex, bool[] generalizedTreeNodes, List <WeightedSequence> weightedSequences, List <GeneralizedElement> currentSequenceElements, Weight currentWeight) { var stack = new Stack <StackItem>(); stack.Push(new StateWeight(stateIndex, currentWeight)); while (stack.Count > 0) { var stackItem = stack.Pop(); if (stackItem is ElementItem elementItem) { if (elementItem.Element != null) { currentSequenceElements.Add(elementItem.Element.Value); } else { currentSequenceElements.RemoveAt(currentSequenceElements.Count - 1); } continue; } var stateAndWeight = stackItem as StateWeight; stateIndex = stateAndWeight.StateIndex; var state = this.builder[stateIndex]; currentWeight = stateAndWeight.Weight; // Find a non-epsilon self-loop if there is one Transition?selfLoop = null; for (var iterator = state.TransitionIterator; iterator.Ok; iterator.Next()) { var transition = iterator.Value; if (transition.DestinationStateIndex == stateIndex) { Debug.Assert( selfLoop == null, "Multiple self-loops should have been merged by MergeParallelTransitions()"); selfLoop = transition; } } // Push the found self-loop to the end of the current sequence if (selfLoop != null) { currentSequenceElements.Add(new GeneralizedElement( selfLoop.Value.ElementDistribution, selfLoop.Value.Group, selfLoop.Value.Weight)); stack.Push(new ElementItem(null)); } // Can this state produce a sequence? if (state.CanEnd && generalizedTreeNodes[stateIndex]) { var sequence = new GeneralizedSequence(currentSequenceElements); // TODO: use immutable data structure instead of copying sequences weightedSequences.Add(new WeightedSequence(sequence, Weight.Product(currentWeight, state.EndWeight))); } // Traverse the outgoing transitions for (var iterator = state.TransitionIterator; iterator.Ok; iterator.Next()) { var transition = iterator.Value; // Skip self-loops & disallowed states if (transition.DestinationStateIndex == stateIndex || !generalizedTreeNodes[transition.DestinationStateIndex]) { continue; } if (!transition.IsEpsilon) { // Non-epsilon transitions contribute to the sequence stack.Push(new ElementItem(null)); } stack.Push( new StateWeight( transition.DestinationStateIndex, Weight.Product(currentWeight, transition.Weight))); if (!transition.IsEpsilon) { stack.Push( new ElementItem( new GeneralizedElement(transition.ElementDistribution, transition.Group, null))); } } } }