Change transitions range representation in Automaton.StateData (#140)

* Pair (FirstTranstiionIndex, LastTransitionIndex) changed to pair
  (FirstTransitionIndex, TransitionsCount).
* Introduced Automaton.Builder.LinkedStateData struct which
  mirrors Automaton.StateData but represents transitions as linked list.
  Previously Automaton.StateData was reused for this purpose.
  That was confusing.
This commit is contained in:
Ivan Korostelev 2019-03-26 19:20:27 +00:00 коммит произвёл GitHub
Родитель 26c093e17f
Коммит 9ab76c6b7c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 63 добавлений и 44 удалений

Просмотреть файл

@ -20,7 +20,7 @@ namespace Microsoft.ML.Probabilistic.Distributions.Automata
/// <summary>
/// States created so far.
/// </summary>
private readonly List<StateData> states;
private readonly List<LinkedStateData> states;
/// <summary>
/// Transitions created so far.
@ -29,8 +29,7 @@ namespace Microsoft.ML.Probabilistic.Distributions.Automata
/// Unlike in <see cref="StateCollection.transitions"/>, transitions
/// for single entity are not represented by contiguous segment of array, but rather as a linked
/// list. It is done this way, because transitions can be added at any moment and inserting
/// transition into a middle of array is not feasible. <see cref="StateData.FirstTransition"/>
/// references head of list and <see cref="StateData.LastTransition"/> references last element of list.
/// transition into a middle of array is not feasible.
/// </remarks>
private readonly List<LinkedTransitionNode> transitions;
@ -45,7 +44,7 @@ namespace Microsoft.ML.Probabilistic.Distributions.Automata
/// </summary>
public Builder(int startStateCount = 1)
{
this.states = new List<StateData>();
this.states = new List<LinkedStateData>();
this.transitions = new List<LinkedTransitionNode>();
this.AddStates(startStateCount);
}
@ -120,10 +119,10 @@ namespace Microsoft.ML.Probabilistic.Distributions.Automata
var index = this.states.Count;
this.states.Add(
new StateData
new LinkedStateData
{
FirstTransition = -1,
LastTransition = -1,
FirstTransitionIndex = -1,
LastTransitionIndex = -1,
EndWeight = Weight.Zero,
});
return new StateBuilder(this, index);
@ -357,7 +356,7 @@ namespace Microsoft.ML.Probabilistic.Distributions.Automata
for (var i = 0; i < oldStateCount; ++i)
{
var state = this.states[i];
if (state.CanEnd && state.FirstTransition != -1)
if (!state.EndWeight.IsZero && state.FirstTransitionIndex != -1)
{
return false;
}
@ -397,9 +396,8 @@ namespace Microsoft.ML.Probabilistic.Distributions.Automata
for (var i = 0; i < resultStates.Length; ++i)
{
var state = this.states[i];
var transitionIndex = state.FirstTransition;
state.FirstTransition = nextResultTransitionIndex;
var firstResultTransitionIndex = nextResultTransitionIndex;
var transitionIndex = this.states[i].FirstTransitionIndex;
while (transitionIndex != -1)
{
var node = this.transitions[transitionIndex];
@ -415,8 +413,10 @@ namespace Microsoft.ML.Probabilistic.Distributions.Automata
transitionIndex = node.Next;
}
state.LastTransition = nextResultTransitionIndex;
resultStates[i] = state;
resultStates[i] = new StateData(
firstResultTransitionIndex,
nextResultTransitionIndex - firstResultTransitionIndex,
this.states[i].EndWeight);
}
Debug.Assert(
@ -470,7 +470,7 @@ namespace Microsoft.ML.Probabilistic.Distributions.Automata
/// <summary>
/// Gets a value indicating whether the ending weight of this state is greater than zero.
/// </summary>
public bool CanEnd => this.builder.states[this.Index].CanEnd;
public bool CanEnd => !this.builder.states[this.Index].EndWeight.IsZero;
/// <summary>
/// Gets or sets the ending weight of the state.
@ -486,7 +486,7 @@ namespace Microsoft.ML.Probabilistic.Distributions.Automata
/// Gets <see cref="TransitionIterator"/> over transitions of this state.
/// </summary>
public TransitionIterator TransitionIterator =>
new TransitionIterator(this.builder, this.Index, this.builder.states[this.Index].FirstTransition);
new TransitionIterator(this.builder, this.Index, this.builder.states[this.Index].FirstTransitionIndex);
/// <summary>
/// Initializes a new instance of <see cref="StateBuilder"/> struct.
@ -525,22 +525,22 @@ namespace Microsoft.ML.Probabilistic.Distributions.Automata
{
Transition = transition,
Next = -1,
Prev = state.LastTransition,
Prev = state.LastTransitionIndex,
});
if (state.LastTransition != -1)
if (state.LastTransitionIndex != -1)
{
// update "next" field in old tail
var oldTail = this.builder.transitions[state.LastTransition];
var oldTail = this.builder.transitions[state.LastTransitionIndex];
oldTail.Next = transitionIndex;
this.builder.transitions[state.LastTransition] = oldTail;
this.builder.transitions[state.LastTransitionIndex] = oldTail;
}
else
{
state.FirstTransition = transitionIndex;
state.FirstTransitionIndex = transitionIndex;
}
state.LastTransition = transitionIndex;
state.LastTransitionIndex = transitionIndex;
this.builder.states[this.Index] = state;
@ -759,16 +759,16 @@ namespace Microsoft.ML.Probabilistic.Distributions.Automata
}
// update references in state
if (state.FirstTransition == this.index || state.LastTransition == this.index)
if (state.FirstTransitionIndex == this.index || state.LastTransitionIndex == this.index)
{
if (state.FirstTransition == this.index)
if (state.FirstTransitionIndex == this.index)
{
state.FirstTransition = node.Next;
state.FirstTransitionIndex = node.Next;
}
if (state.LastTransition == this.index)
if (state.LastTransitionIndex == this.index)
{
state.LastTransition = node.Prev;
state.LastTransitionIndex = node.Prev;
}
this.builder.states[this.stateIndex] = state;
@ -793,6 +793,29 @@ namespace Microsoft.ML.Probabilistic.Distributions.Automata
}
/// <summary>
/// Version of <see cref="StateData"/> that is used during automaton constructions. Unlike
/// regular <see cref="StateData"/> transitions are stored as a linked list over
/// <see cref="Builder.transitions"/> array.
/// </summary>
private struct LinkedStateData
{
/// <summary>
/// Index of the head of transitions list in <see cref="Builder.transitions"/>.
/// </summary>
public int FirstTransitionIndex { get; internal set; }
/// <summary>
/// Index of the tail of transitions list in <see cref="Builder.transitions"/>.
/// </summary>
public int LastTransitionIndex { get; internal set; }
/// <summary>
/// Ending weight of the state.
/// </summary>
public Weight EndWeight { get; internal set; }
}
/// <summary>
/// Linked list node for representing transitions for state.
/// </summary>

Просмотреть файл

@ -108,12 +108,13 @@ namespace Microsoft.ML.Probabilistic.Distributions.Automata
foreach (var state in this.States)
{
if (state.FirstTransition < 0 || state.LastTransition > this.Transitions.Count)
var lastTransitionIndex = state.FirstTransitionIndex + state.TransitionsCount;
if (state.FirstTransitionIndex < 0 || lastTransitionIndex > this.Transitions.Count)
{
return false;
}
for (var i = state.FirstTransition; i < state.LastTransition; ++i)
for (var i = state.FirstTransitionIndex; i < lastTransitionIndex; ++i)
{
var transition = this.Transitions[i];
if (transition.DestinationStateIndex < 0 ||

Просмотреть файл

@ -67,8 +67,8 @@ namespace Microsoft.ML.Probabilistic.Distributions.Automata
public ReadOnlyArraySegment<Transition> Transitions =>
new ReadOnlyArraySegment<Transition>(
this.transitions,
this.Data.FirstTransition,
this.Data.LastTransition - this.Data.FirstTransition);
this.Data.FirstTransitionIndex,
this.Data.TransitionsCount);
internal StateData Data => this.states[this.Index];

Просмотреть файл

@ -29,20 +29,15 @@ namespace Microsoft.ML.Probabilistic.Distributions.Automata
/// this property contains index of the head of the linked-list of transitions.
/// </remarks>
[DataMember]
public int FirstTransition { get; internal set; }
public int FirstTransitionIndex { get; internal set; }
/// <summary>
/// Gets or sets index of the first transition in <see cref="DataContainer.Transitions"/> after
/// <see cref="FirstTransition"/> which does not belong to this state. All transitions for
/// Gets or sets count of transition in <see cref="DataContainer.Transitions"/> after
/// <see cref="FirstTransitionIndex"/> which belong to this state. All transitions for
/// the same state are stored as a contiguous block.
/// </summary>
/// <remarks>
/// During automaton construction <see cref="Automaton{TSequence,TElement,TElementDistribution,TSequenceManipulator,TThis}.Builder"/>
/// stores transitions as linked-list instead of contiguous block. So, during construction
/// this property contains index of the tail of the linked-list of transitions.
/// </remarks>
[DataMember]
public int LastTransition { get; internal set; }
public int TransitionsCount { get; internal set; }
/// <summary>
/// Gets or sets ending weight of the state.
@ -53,11 +48,11 @@ namespace Microsoft.ML.Probabilistic.Distributions.Automata
/// <summary>
/// Initializes a new instance of the <see cref="StateData"/> struct.
/// </summary>
[Construction("FirstTransition", "LastTransition", "EndWeight")]
public StateData(int firstTransition, int lastTransition, Weight endWeight)
[Construction("FirstTransitionIndex", "TransitionsCount", "EndWeight")]
public StateData(int firstTransitionIndex, int transitionsCount, Weight endWeight)
{
this.FirstTransition = firstTransition;
this.LastTransition = lastTransition;
this.FirstTransitionIndex = firstTransitionIndex;
this.TransitionsCount = transitionsCount;
this.EndWeight = endWeight;
}

Просмотреть файл

@ -864,7 +864,7 @@ namespace Microsoft.ML.Probabilistic.Tests
new[]
{
new StringAutomaton.StateData(0, 1, Weight.One),
new StringAutomaton.StateData(1, 1, Weight.One),
new StringAutomaton.StateData(1, 0, Weight.One),
},
new[] { new StringAutomaton.Transition(DiscreteChar.PointMass('a'), Weight.One, 1) }));