/// <summary> /// Recursively builds the projection of a given automaton onto this transducer. /// The projected automaton must be epsilon-free. /// </summary> /// <param name="destAutomaton">The projection being built.</param> /// <param name="mappingState">The currently traversed state of the transducer.</param> /// <param name="srcState">The currently traversed state of the automaton being projected.</param> /// <param name="destStateCache">The cache of the created projection states.</param> /// <returns>The state of the projection corresponding to the given mapping state and the position in the projected sequence.</returns> private Automaton <TDestSequence, TDestElement, TDestElementDistribution, TDestSequenceManipulator, TDestAutomaton> .State BuildProjectionOfAutomaton( TDestAutomaton destAutomaton, PairListAutomaton.State mappingState, Automaton <TSrcSequence, TSrcElement, TSrcElementDistribution, TSrcSequenceManipulator, TSrcAutomaton> .State srcState, Dictionary <IntPair, Automaton <TDestSequence, TDestElement, TDestElementDistribution, TDestSequenceManipulator, TDestAutomaton> .State> destStateCache) { Debug.Assert(mappingState != null && srcState != null, "Valid states must be provided."); Debug.Assert(!ReferenceEquals(srcState.Owner, destAutomaton), "Cannot build a projection in place."); //// The code of this method has a lot in common with the code of Automaton<>.BuildProduct. //// Unfortunately, it's not clear how to avoid the duplication in the current design. // State already exists, return its index var statePair = new IntPair(mappingState.Index, srcState.Index); Automaton <TDestSequence, TDestElement, TDestElementDistribution, TDestSequenceManipulator, TDestAutomaton> .State destState; if (destStateCache.TryGetValue(statePair, out destState)) { return(destState); } destState = destAutomaton.AddState(); destStateCache.Add(statePair, destState); // Iterate over transitions from mappingState for (int mappingTransitionIndex = 0; mappingTransitionIndex < mappingState.TransitionCount; mappingTransitionIndex++) { var mappingTransition = mappingState.GetTransition(mappingTransitionIndex); var childMappingState = mappingState.Owner.States[mappingTransition.DestinationStateIndex]; // Epsilon transition case if (IsSrcEpsilon(mappingTransition)) { TDestElementDistribution destElementDistribution = mappingTransition.ElementDistribution == null ? null : mappingTransition.ElementDistribution.Second; var childDestState = this.BuildProjectionOfAutomaton(destAutomaton, childMappingState, srcState, destStateCache); destState.AddTransition(destElementDistribution, mappingTransition.Weight, childDestState, mappingTransition.Group); continue; } // Iterate over states and transitions in the closure of srcState for (int srcTransitionIndex = 0; srcTransitionIndex < srcState.TransitionCount; srcTransitionIndex++) { var srcTransition = srcState.GetTransition(srcTransitionIndex); Debug.Assert(!srcTransition.IsEpsilon, "The automaton being projected must be epsilon-free."); var srcChildState = srcState.Owner.States[srcTransition.DestinationStateIndex]; TDestElementDistribution destElementDistribution; double projectionLogScale = mappingTransition.ElementDistribution.ProjectFirst( srcTransition.ElementDistribution, out destElementDistribution); if (double.IsNegativeInfinity(projectionLogScale)) { continue; } Weight destWeight = Weight.Product(mappingTransition.Weight, srcTransition.Weight, Weight.FromLogValue(projectionLogScale)); var childDestState = this.BuildProjectionOfAutomaton(destAutomaton, childMappingState, srcChildState, destStateCache); destState.AddTransition(destElementDistribution, destWeight, childDestState, mappingTransition.Group); } } destState.EndWeight = Weight.Product(mappingState.EndWeight, srcState.EndWeight); return(destState); }