/// <summary> /// For each state of the component, computes the total weight of all paths starting at the root /// and ending at that state. Ending weights are not taken into account. /// </summary> /// <remarks>The weights are computed using dynamic programming, going down from the root to leafs.</remarks> private void ComputeWeightsFromRoot() { CondensationStateInfo rootInfo = this.stateIdToInfo[this.Root.Index]; rootInfo.UpwardWeightFromRoot = Weight.One; this.stateIdToInfo[this.Root.Index] = rootInfo; // Iterate in the topological order for (int currentComponentIndex = this.components.Count - 1; currentComponentIndex >= 0; --currentComponentIndex) { StronglyConnectedComponent currentComponent = this.components[currentComponentIndex]; // Propagate weights inside the component for (int srcStateIndex = 0; srcStateIndex < currentComponent.Size; ++srcStateIndex) { State srcState = currentComponent.GetStateByIndex(srcStateIndex); CondensationStateInfo srcStateInfo = this.stateIdToInfo[srcState.Index]; if (srcStateInfo.UpwardWeightFromRoot.IsZero) { continue; } for (int destStateIndex = 0; destStateIndex < currentComponent.Size; ++destStateIndex) { State destState = currentComponent.GetStateByIndex(destStateIndex); CondensationStateInfo destStateInfo = this.stateIdToInfo[destState.Index]; destStateInfo.WeightFromRoot += srcStateInfo.UpwardWeightFromRoot * currentComponent.GetWeight(srcStateIndex, destStateIndex); this.stateIdToInfo[destState.Index] = destStateInfo; } } // Compute weight contributions to downward components for (int srcStateIndex = 0; srcStateIndex < currentComponent.Size; ++srcStateIndex) { State srcState = currentComponent.GetStateByIndex(srcStateIndex); CondensationStateInfo srcStateInfo = this.stateIdToInfo[srcState.Index]; if (srcStateInfo.WeightFromRoot.IsZero) { continue; } // Aggregate weights of all the outgoing transitions from this state foreach (var transition in srcState.Transitions) { State destState = this.automaton.States[transition.DestinationStateIndex]; if (this.transitionFilter(transition) && !currentComponent.HasState(destState)) { CondensationStateInfo destStateInfo = this.stateIdToInfo[destState.Index]; destStateInfo.UpwardWeightFromRoot += srcStateInfo.WeightFromRoot * transition.Weight; this.stateIdToInfo[transition.DestinationStateIndex] = destStateInfo; } } } } this.weightsFromRootComputed = true; }
/// <summary> /// Initializes a new instance of the <see cref="Condensation"/> class. /// </summary> /// <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(State root, Func <Transition, bool> transitionFilter, bool useApproximateClosure) { Debug.Assert(root != null, "A valid root node must be provided."); Debug.Assert(transitionFilter != null, "A valid transition filter must be provided."); this.Root = root; this.transitionFilter = transitionFilter; this.useApproximateClosure = useApproximateClosure; this.components = new List <StronglyConnectedComponent>(); var stateIdStack = new Stack <State>(); var stateIdToTarjanInfo = new Dictionary <int, TarjanStateInfo>(); int traversalIndex = 0; this.FindStronglyConnectedComponents(this.Root, ref traversalIndex, stateIdToTarjanInfo, stateIdStack); this.stateIdToInfo = new Dictionary <int, CondensationStateInfo>(stateIdToTarjanInfo.Count); for (int i = 0; i < this.components.Count; ++i) { StronglyConnectedComponent component = this.components[i]; for (int j = 0; j < component.Size; ++j) { this.stateIdToInfo.Add(component.GetStateByIndex(j).Index, CondensationStateInfo.Default); } } }
/// <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> /// For each state of the component, computes the total weight of all paths starting at that state. /// Ending weights are taken into account. /// </summary> /// <remarks>The weights are computed using dynamic programming, going up from leafs to the root.</remarks> private void ComputeWeightsToEnd() { // Iterate in the reverse topological order for (int currentComponentIndex = 0; currentComponentIndex < this.components.Count; ++currentComponentIndex) { StronglyConnectedComponent currentComponent = this.components[currentComponentIndex]; // Update end weights in this component based on outgoing transitions to downward components for (int stateIndex = 0; stateIndex < currentComponent.Size; ++stateIndex) { State state = currentComponent.GetStateByIndex(stateIndex); // Aggregate weights of all the outgoing transitions from this state Weight weightToAdd = state.EndWeight; for (int transitionIndex = 0; transitionIndex < state.TransitionCount; ++transitionIndex) { Transition transition = state.GetTransition(transitionIndex); State destState = state.Owner.states[transition.DestinationStateIndex]; if (this.transitionFilter(transition) && !currentComponent.HasState(destState)) { weightToAdd = Weight.Sum( weightToAdd, Weight.Product(transition.Weight, this.stateIdToInfo[transition.DestinationStateIndex].WeightToEnd)); } } // We can go from any state of the component to the current state if (!weightToAdd.IsZero) { for (int updatedStateIndex = 0; updatedStateIndex < currentComponent.Size; ++updatedStateIndex) { State updatedState = currentComponent.GetStateByIndex(updatedStateIndex); CondensationStateInfo updatedStateInfo = this.stateIdToInfo[updatedState.Index]; updatedStateInfo.WeightToEnd = Weight.Sum( updatedStateInfo.WeightToEnd, Weight.Product(currentComponent.GetWeight(updatedStateIndex, stateIndex), weightToAdd)); this.stateIdToInfo[updatedState.Index] = updatedStateInfo; } } } } this.weightsToEndComputed = true; }
/// <summary> /// For each state of the component, computes the total weight of all paths starting at that state. /// Ending weights are taken into account. /// </summary> /// <remarks>The weights are computed using dynamic programming, going up from leafs to the root.</remarks> private void ComputeWeightsToEnd() { // Iterate in the reverse topological order for (int currentComponentIndex = 0; currentComponentIndex < this.components.Count; ++currentComponentIndex) { StronglyConnectedComponent currentComponent = this.components[currentComponentIndex]; // Update end weights in this component based on outgoing transitions to downward components for (int stateIndex = 0; stateIndex < currentComponent.Size; ++stateIndex) { State state = currentComponent.GetStateByIndex(stateIndex); // Aggregate weights of all the outgoing transitions from this state Weight weightToAdd = state.EndWeight; foreach (var transition in state.Transitions) { State destState = this.automaton.States[transition.DestinationStateIndex]; if (this.transitionFilter(transition) && !currentComponent.HasState(destState)) { weightToAdd += transition.Weight * this.stateInfo[transition.DestinationStateIndex].WeightToEnd; } } // We can go from any state of the component to the current state if (!weightToAdd.IsZero) { for (int updatedStateIndex = 0; updatedStateIndex < currentComponent.Size; ++updatedStateIndex) { State updatedState = currentComponent.GetStateByIndex(updatedStateIndex); CondensationStateInfo updatedStateInfo = this.stateInfo[updatedState.Index]; updatedStateInfo.WeightToEnd += currentComponent.GetWeight(updatedStateIndex, stateIndex) * weightToAdd; this.stateInfo.Update(updatedState.Index, updatedStateInfo); } } } } this.weightsToEndComputed = true; }
/// <summary> /// Initializes a new instance of the <see cref="EpsilonClosure"/> class. /// </summary> /// <param name="automaton">The automaton from to which <paramref name="state"/> belongs.</param> /// <param name="state">The state, which epsilon closure this instance will represent.</param> internal EpsilonClosure( Automaton <TSequence, TElement, TElementDistribution, TSequenceManipulator, TThis> automaton, State state) { this.weightedStates = new List <(State, Weight)>(DefaultStateListCapacity); // Optimize for a very common case: a single-node closure bool singleNodeClosure = true; Weight selfLoopWeight = Weight.Zero; foreach (var transition in state.Transitions) { if (transition.IsEpsilon) { if (transition.DestinationStateIndex != state.Index) { singleNodeClosure = false; break; } selfLoopWeight += transition.Weight; } } if (singleNodeClosure) { Weight stateWeight = Weight.ApproximateClosure(selfLoopWeight); this.weightedStates.Add((state, stateWeight)); this.EndWeight = stateWeight * state.EndWeight; } else { Condensation condensation = automaton.ComputeCondensation(state, tr => tr.IsEpsilon, true); for (int i = 0; i < condensation.ComponentCount; ++i) { StronglyConnectedComponent component = condensation.GetComponent(i); for (int j = 0; j < component.Size; ++j) { State componentState = component.GetStateByIndex(j); this.weightedStates.Add((componentState, condensation.GetWeightFromRoot(componentState))); } } this.EndWeight = condensation.GetWeightToEnd(state.Index); } }
/// <summary> /// Initializes a new instance of the <see cref="EpsilonClosure"/> class. /// </summary> /// <param name="state">The state, which epsilon closure this instance will represent.</param> internal EpsilonClosure(State state) { Argument.CheckIfValid(!state.IsNull, nameof(state)); // Optimize for a very common case: a single-node closure bool singleNodeClosure = true; Weight selfLoopWeight = Weight.Zero; for (int i = 0; i < state.TransitionCount; ++i) { Transition transition = state.GetTransition(i); if (transition.IsEpsilon) { if (transition.DestinationStateIndex != state.Index) { singleNodeClosure = false; break; } selfLoopWeight = Weight.Sum(selfLoopWeight, transition.Weight); } } if (singleNodeClosure) { Weight stateWeight = Weight.ApproximateClosure(selfLoopWeight); this.weightedStates.Add(Pair.Create(state, stateWeight)); this.EndWeight = Weight.Product(stateWeight, state.EndWeight); } else { Condensation condensation = state.Owner.ComputeCondensation(state, tr => tr.IsEpsilon, true); for (int i = 0; i < condensation.ComponentCount; ++i) { StronglyConnectedComponent component = condensation.GetComponent(i); for (int j = 0; j < component.Size; ++j) { State componentState = component.GetStateByIndex(j); this.weightedStates.Add(Pair.Create(componentState, condensation.GetWeightFromRoot(componentState))); } } this.EndWeight = condensation.GetWeightToEnd(state); } }
/// <summary> /// Initializes a new instance of the <see cref="Condensation"/> class. /// </summary> /// <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(State root, Func <Transition, bool> transitionFilter, bool useApproximateClosure) { Debug.Assert(transitionFilter != null, "A valid transition filter must be provided."); this.Root = root; this.transitionFilter = transitionFilter; this.useApproximateClosure = useApproximateClosure; this.components = this.FindStronglyConnectedComponents(); this.stateIdToInfo = new Dictionary <int, CondensationStateInfo>(); for (int i = 0; i < this.components.Count; ++i) { StronglyConnectedComponent component = this.components[i]; for (int j = 0; j < component.Size; ++j) { this.stateIdToInfo.Add(component.GetStateByIndex(j).Index, CondensationStateInfo.Default); } } }