Repository navigation
HermitCrab: keep final-template pruning on when only lexical entries are partial #553
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
base: master
Are you sure you want to change the base?
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 |
|---|---|---|
|
|
@@ -62,7 +62,7 @@ public Morpher(ITraceManager traceManager, Language lang, int maxDegreeOfParalle | |
| RuleSelector = rule => true; | ||
|
|
||
| _morphemes = new ReadOnlyObservableCollection<Morpheme>(morphemes); | ||
| IsPartial = GetPartialMorphemes().Count() > 0; | ||
| HasPartialRules = GetPartialMorphemes().Any(morpheme => !(morpheme is LexEntry)); | ||
| } | ||
|
|
||
| public IEnumerable<Morpheme> GetPartialMorphemes() | ||
|
|
@@ -98,9 +98,13 @@ public ITraceManager TraceManager | |
| public bool MergeEquivalentAnalyses { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// A Morpher is partial if any of the elements are partial. | ||
| /// True if any morpheme other than a <see cref="LexEntry"/> is partial. Analysis may prune a final | ||
| /// template that follows a non-template rule only when this is false. Partial lexical entries do not | ||
| /// count: <see cref="SynthesisAffixTemplatesRule"/> applies no template to a word whose root is partial, | ||
| /// and when it substitutes an inflectional-family relative for the root, the relative's word is | ||
| /// non-partial, so a following non-partial rule is blocked either way. | ||
| /// </summary> | ||
| public bool IsPartial { get; } | ||
| public bool HasPartialRules { get; } | ||
|
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. Removing
Collaborator
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. FieldWorks doesn't read it. I think that nothing further needs to be done. |
||
|
|
||
| /// <summary> | ||
| /// Enforce final templates even if some of the morphemes were partial. | ||
|
|
@@ -491,8 +495,6 @@ private IEnumerable<Word> LexicalGuess(Word input) | |
| SyntacticFeatureStruct = input.SyntacticFeatureStruct, | ||
| Stratum = input.Stratum, | ||
| }; | ||
| // Don't update Morpher.IsPartial even if lexEntry.IsPartial is true. | ||
| // We don't mind if final templates are filtered during analysis in this case. | ||
| lexEntry.Allomorphs.Add(root); | ||
| // Point the root allomorph to the lexical pattern in FieldWorks. | ||
| if (lexicalPattern.Morpheme != null) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -473,7 +473,7 @@ public void EarlyPruningOfFinalTemplate() | |
|
|
||
| // Verify early pruning of final template. | ||
| var morpher = new Morpher(TraceManager, Language); | ||
| Assert.That(morpher.IsPartial, Is.True); | ||
| Assert.That(morpher.GetPartialMorphemes(), Is.Not.Empty); | ||
| morpher.AlwaysEnforceFinalTemplates = true; | ||
|
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. No test covers the analysis-side |
||
| TraceManager.IsTracing = true; | ||
| AssertMorphsEqual(morpher.ParseWord("sagdv", out object trace)); | ||
|
|
@@ -501,6 +501,79 @@ public void EarlyPruningOfFinalTemplate() | |
| AssertMorphsEqual(morpher.ParseWord("sagdmis"), "32 PAST 53 PL"); | ||
| } | ||
|
|
||
| [Test] | ||
| public void PartialLexicalEntryDoesNotDisableEarlyPruningOfFinalTemplate() | ||
| { | ||
| var any = FeatureStruct.New().Symbol(HCFeatureSystem.Segment).Value; | ||
| var verb = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("V").Value; | ||
|
|
||
| AffixProcessRule CreateSuffix(string name, string gloss, string suffix) | ||
| { | ||
| var rule = new AffixProcessRule | ||
| { | ||
| Name = name, | ||
| Gloss = gloss, | ||
| RequiredSyntacticFeatureStruct = verb.Clone(), | ||
| }; | ||
| rule.Allomorphs.Add( | ||
| new AffixProcessAllomorph | ||
| { | ||
| Lhs = { Pattern<Word, ShapeNode>.New("1").Annotation(any).OneOrMore.Value }, | ||
| Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, suffix) }, | ||
| } | ||
| ); | ||
| return rule; | ||
| } | ||
|
|
||
| var edSuffix = CreateSuffix("ed_suffix", "PAST", "d"); | ||
| var verbTemplate = new AffixTemplate { Name = "verb", RequiredSyntacticFeatureStruct = verb.Clone() }; | ||
| verbTemplate.Slots.Add(new AffixTemplateSlot(edSuffix)); | ||
| Morphophonemic.AffixTemplates.Add(verbTemplate); | ||
| var nominalizer = CreateSuffix("nominalizer", "NOM", "v"); | ||
| Morphophonemic.MorphologicalRules.Add(nominalizer); | ||
| var partialRoot = AddEntry("partial_root", verb.Clone(), Morphophonemic, "sab"); | ||
| partialRoot.IsPartial = true; | ||
| try | ||
| { | ||
| // The test grammar already has a partial entry, so only the rules decide the prune. | ||
| var morpher = new Morpher(TraceManager, Language); | ||
| Assert.That(morpher.GetPartialMorphemes(), Is.Not.Empty); | ||
| Assert.That(morpher.HasPartialRules, Is.False); | ||
| Assert.That(morpher.AlwaysEnforceFinalTemplates, Is.False); | ||
| TraceManager.IsTracing = true; | ||
| AssertMorphsEqual(morpher.ParseWord("sagdv", out object trace)); | ||
| Assert.That( | ||
| GetFailureDepth((Trace)trace, FailureReason.NonPartialRuleProhibitedAfterFinalTemplate), | ||
| Is.EqualTo(2) | ||
| ); | ||
| TraceManager.IsTracing = false; | ||
| AssertMorphsEqual(morpher.ParseWord("sagd"), "32 PAST"); | ||
|
|
||
| // No template applies to a partial root, so pruning loses nothing for it. | ||
| AssertMorphsEqual(morpher.ParseWord("sab"), "partial_root"); | ||
| AssertMorphsEqual(morpher.ParseWord("sabd")); | ||
| AssertMorphsEqual(morpher.ParseWord("sabdv")); | ||
| AssertMorphsEqual(morpher.ParseWord("sabv"), "partial_root NOM"); | ||
|
|
||
| // A partial rule still turns the prune off, and what it licenses after a final template survives. | ||
| nominalizer.IsPartial = true; | ||
| morpher = new Morpher(TraceManager, Language); | ||
| Assert.That(morpher.HasPartialRules, Is.True); | ||
| TraceManager.IsTracing = true; | ||
| AssertMorphsEqual(morpher.ParseWord("sagdv", out trace), "32 PAST NOM"); | ||
| Assert.That( | ||
| GetFailureDepth((Trace)trace, FailureReason.NonPartialRuleProhibitedAfterFinalTemplate), | ||
| Is.EqualTo(0) | ||
| ); | ||
| TraceManager.IsTracing = false; | ||
| } | ||
| finally | ||
| { | ||
| Morphophonemic.Entries.Remove(partialRoot); | ||
| Entries.Remove("partial_root"); | ||
| } | ||
| } | ||
|
|
||
| private static int GetFailureDepth(Trace trace, FailureReason reason) | ||
| { | ||
| if (trace == null) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -228,7 +228,11 @@ public void PartialMorphemesParseTheSameWithAndWithoutPruning(bool alwaysEnforce | |
| string[] Parse(bool pruneCopies, string word) | ||
| { | ||
| Morpher morpher = CreateMorpher(pruneCopies); | ||
| Assert.That(morpher.IsPartial, Is.True, "entry 54 has an empty syntactic feature structure"); | ||
| Assert.That( | ||
| morpher.GetPartialMorphemes(), | ||
| Is.Not.Empty, | ||
| "entry 54 has an empty syntactic feature structure" | ||
| ); | ||
| morpher.AlwaysEnforceFinalTemplates = alwaysEnforceFinalTemplates; | ||
|
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: F6. The two |
||
| return ParseGlosses(morpher, word); | ||
| } | ||
|
|
||
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.
FYI: F3. The summary's reasoning leaves out blocking. A blockable template slot rule can call
Word.CheckBlocking(SynthesisAffixProcessRule.cs:199), which swaps in a partial family relative.SynthesisAffixTemplatesRule.cs:45then marks that word final, and a non-partial rule may follow it becauseinput.IsPartial(SynthesisAffixProcessRule.cs:67). The output matches the relative analyzed directly as a root, so I expect no parse is lost. That is Unverified. Consider cutting the summary to its first sentence.