/// <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); }
/// <summary> /// Checks whether this object is equal to a given pair. /// </summary> /// <param name="pair">The pair to compare with.</param> /// <returns><see langword="true"/> if this pair is equal to the given one, <see langword="false"/> otherwise.</returns> public bool Equals(IntPair pair) { return(this.First == pair.First && this.Second == pair.Second); }
/// <summary> /// Recursively builds the projection of a given sequence onto this transducer. /// </summary> /// <param name="destAutomaton">The projection being built.</param> /// <param name="mappingState">The currently traversed state of the transducer.</param> /// <param name="srcSequence">The sequence being projected.</param> /// <param name="srcSequenceIndex">The current index in the sequence 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 BuildProjectionOfSequence( TDestAutomaton destAutomaton, PairListAutomaton.State mappingState, TSrcSequence srcSequence, int srcSequenceIndex, Dictionary <IntPair, Automaton <TDestSequence, TDestElement, TDestElementDistribution, TDestSequenceManipulator, TDestAutomaton> .State> destStateCache) { //// 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. var sourceSequenceManipulator = Automaton <TSrcSequence, TSrcElement, TSrcElementDistribution, TSrcSequenceManipulator, TSrcAutomaton> .SequenceManipulator; var statePair = new IntPair(mappingState.Index, srcSequenceIndex); Automaton <TDestSequence, TDestElement, TDestElementDistribution, TDestSequenceManipulator, TDestAutomaton> .State destState; if (destStateCache.TryGetValue(statePair, out destState)) { return(destState); } destState = destAutomaton.AddState(); destStateCache.Add(statePair, destState); int srcSequenceLength = sourceSequenceManipulator.GetLength(srcSequence); // Enumerate transitions from the current mapping state for (int i = 0; i < mappingState.TransitionCount; i++) { var mappingTransition = mappingState.GetTransition(i); var destMappingState = mappingState.Owner.States[mappingTransition.DestinationStateIndex]; // Epsilon transition case if (IsSrcEpsilon(mappingTransition)) { TDestElementDistribution destElementWeights = mappingTransition.ElementDistribution == null ? null : mappingTransition.ElementDistribution.Second; var childDestState = this.BuildProjectionOfSequence( destAutomaton, destMappingState, srcSequence, srcSequenceIndex, destStateCache); destState.AddTransition(destElementWeights, mappingTransition.Weight, childDestState, mappingTransition.Group); continue; } // Normal transition case - Find epsilon-reachable states if (srcSequenceIndex < srcSequenceLength) { var srcSequenceElement = sourceSequenceManipulator.GetElement(srcSequence, srcSequenceIndex); TDestElementDistribution destElementDistribution; double projectionLogScale = mappingTransition.ElementDistribution.ProjectFirst( srcSequenceElement, out destElementDistribution); if (double.IsNegativeInfinity(projectionLogScale)) { continue; } Weight weight = Weight.Product(mappingTransition.Weight, Weight.FromLogValue(projectionLogScale)); var childDestState = this.BuildProjectionOfSequence( destAutomaton, destMappingState, srcSequence, srcSequenceIndex + 1, destStateCache); destState.AddTransition(destElementDistribution, weight, childDestState, mappingTransition.Group); } } destState.EndWeight = srcSequenceIndex == srcSequenceLength ? mappingState.EndWeight : Weight.Zero; return(destState); }