Repository navigation
Prune analysis rule outputs that contain no root when no later rule can restore stem material #572
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -93,7 +93,8 @@ out FeatureStruct syntacticFS | |
| outWord.Freeze(); | ||
| if (_morpher.TraceManager.IsTracing) | ||
| _morpher.TraceManager.MorphologicalRuleUnapplied(_rule, i, input, outWord); | ||
| output.Add(outWord); | ||
| if (_morpher.MayContainRoot(input, outWord, nameof(AnalysisAffixProcessRule))) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor: F1. Try-a-Word loses the lexical lookup that tells a user which root is missing. The rule output is traced as |
||
| output.Add(outWord); | ||
| unapplied = true; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,8 @@ internal class RootAllomorphTrie | |
| private readonly Fst<Shape, ShapeNode> _fsa; | ||
| private readonly Func<Annotation<ShapeNode>, bool> _filter; | ||
| private int _shapeCount; | ||
| private bool _hasEmptyAllomorph; | ||
| private readonly Dictionary<string, int> _rootLengths = new Dictionary<string, int>(); | ||
| private readonly Dictionary<string, RootAllomorph> _allomorphs; | ||
|
|
||
| public RootAllomorphTrie(Func<Annotation<ShapeNode>, bool> filter) | ||
|
|
@@ -26,11 +28,17 @@ public RootAllomorphTrie(Func<Annotation<ShapeNode>, bool> filter) | |
| public void Add(RootAllomorph allomorph) | ||
| { | ||
| _allomorphs[allomorph.ID] = allomorph; | ||
| _rootLengths[allomorph.ID] = allomorph.Segments.Shape.Count(n => _filter(n.Annotation)); | ||
| ShapeNode first = allomorph.Segments.Shape.GetFirst(n => _filter(n.Annotation)); | ||
| if (first == allomorph.Segments.Shape.End) | ||
| { | ||
| _hasEmptyAllomorph = true; | ||
| _fsa.StartState.Arcs.Add(_fsa.CreateAcceptingState(allomorph.ID, (s, match) => true, _shapeCount)); | ||
| } | ||
| else | ||
| { | ||
| AddNode(first, _fsa.StartState, allomorph.ID); | ||
| } | ||
| _shapeCount++; | ||
| } | ||
|
|
||
|
|
@@ -67,6 +75,44 @@ private void AddNode(ShapeNode node, State<Shape, ShapeNode> state, string id) | |
| AddNode(nextNode, nextState, id); | ||
| } | ||
|
|
||
| public bool IsEmpty => _allomorphs.Count == 0; | ||
|
|
||
| /// <summary> | ||
| /// Whether any root occurs as a contiguous run of the shape's segments, starting anywhere and ending | ||
| /// anywhere. Stops at the first accepting path. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI: F4. The doc comment says "Stops at the first accepting path", but that is not what happens. |
||
| /// </summary> | ||
| public bool ContainsRootSubstring(Shape shape) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI: F5. |
||
| { | ||
| if (_hasEmptyAllomorph) | ||
| return true; | ||
| Annotation<ShapeNode> startAnn = shape.Annotations.GetFirst(_filter); | ||
| return _fsa.Transduce(shape, startAnn, null, false, false, false, out FstResult<Shape, ShapeNode> _); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Finds the first root run by trying each start segment in turn, which costs the same arcs as | ||
| /// <see cref="ContainsRootSubstring"/> and also says where the run is, in segment indices. | ||
| /// </summary> | ||
| public bool TryFindRootRun(Shape shape, out RootRun run) | ||
| { | ||
| run = null; | ||
| if (_hasEmptyAllomorph) | ||
| return true; | ||
| int index = 0; | ||
| foreach (Annotation<ShapeNode> ann in shape.Annotations) | ||
| { | ||
| if (!_filter(ann)) | ||
| continue; | ||
| if (_fsa.Transduce(shape, ann, null, true, false, false, out FstResult<Shape, ShapeNode> result)) | ||
| { | ||
| run = new RootRun(index, index + _rootLengths[result.ID]); | ||
| return true; | ||
| } | ||
| index++; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| public IEnumerable<RootAllomorph> Search(Shape shape) | ||
| { | ||
| Annotation<ShapeNode> startAnn = shape.Annotations.GetFirst(_filter); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| namespace SIL.Machine.Morphology.HermitCrab | ||
| { | ||
| /// <summary>A root found as a run of segments [Start, End) of an analysis shape.</summary> | ||
| internal sealed class RootRun | ||
| { | ||
| public RootRun(int start, int end) | ||
| { | ||
| Start = start; | ||
| End = end; | ||
| } | ||
|
|
||
| /// <summary>Stands for no usable location: nothing lies inside it.</summary> | ||
| public static readonly RootRun None = new RootRun(-1, -1); | ||
|
|
||
| public int Start { get; } | ||
| public int End { get; } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: F2. Callers cannot turn this off. The prune is on by default and changes which analyses come out, but its switch is
internal, so FLEx and othernetstandard2.0consumers have no way back if it misprunes a grammar. The comparable switchesMergeEquivalentAnalyses(line 100) andPruneDisagreeingCopies(line 117) are publicbools. Expose a publicbooland keepShadowinternal.