Repository navigation
Keep the final-template prune on in grammars with partial rules wherever no partial word can be involved #644
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
aa3c04a
919484d
0dfee20
8d06d06
9934032
71ce0fe
f6c0b84
2db0929
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 |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using SIL.Machine.FeatureModel; | ||
| using SIL.Machine.Morphology.HermitCrab.MorphologicalRules; | ||
|
|
||
| namespace SIL.Machine.Morphology.HermitCrab | ||
| { | ||
| /// <summary> | ||
| /// Finds the affix templates that a partial word can reach in synthesis. A word is partial at a template only | ||
| /// if a partial affix-process rule that is neither a template-slot nor a clitic rule applied before it (a partial root takes no template), in the template's | ||
|
Check warning on line 10 in src/SIL.Machine.Morphology.HermitCrab/FinalTemplatePartialReachability.cs
|
||
| /// stratum or an earlier one. Its part of speech is then whatever that rule leaves, changed by any rules | ||
| /// applied since. The search over-approximates: it tracks only the part-of-speech feature, treats an | ||
| /// unspecified or variable part of speech as every part of speech, ignores application counts, rule order, | ||
| /// template gating and every other feature, and lets every rule of a stratum apply any number of times. A | ||
| /// template it does not return can therefore never see a partial word. | ||
| /// </summary> | ||
| internal static class FinalTemplatePartialReachability | ||
| { | ||
| public static HashSet<AffixTemplate> Compute(Language lang) | ||
| { | ||
| var result = new HashSet<AffixTemplate>(); | ||
| SymbolicFeature posFeature = lang.SyntacticFeatureSystem.PartOfSpeechFeature; | ||
| HashSet<FeatureSymbol> all = | ||
| posFeature == null | ||
| ? new HashSet<FeatureSymbol>() | ||
| : new HashSet<FeatureSymbol>(posFeature.PossibleSymbols); | ||
|
|
||
| // Part-of-speech values a partial word may carry on leaving the strata processed so far. | ||
| var reach = new HashSet<FeatureSymbol>(); | ||
| bool anyPartial = false; | ||
| foreach (Stratum stratum in lang.Strata) // synthesis order | ||
| { | ||
| List<IMorphologicalRule> rules = stratum | ||
| .MorphologicalRules.Concat(stratum.AffixTemplates.SelectMany(t => t.Slots).SelectMany(s => s.Rules)) | ||
| .Distinct() | ||
| .ToList(); | ||
|
|
||
| foreach (AffixProcessRule rule in rules.OfType<AffixProcessRule>().Where(IsSynthesisPartial)) | ||
| { | ||
| anyPartial = true; | ||
| reach.UnionWith( | ||
| Apply(all, all, rule.RequiredSyntacticFeatureStruct, rule.OutSyntacticFeatureStruct) | ||
| ); | ||
| } | ||
| if (!anyPartial) | ||
| continue; | ||
| if (all.Count == 0) | ||
| { | ||
| // No parts of speech to tell templates apart: every later template is reachable. | ||
| result.UnionWith(stratum.AffixTemplates); | ||
| continue; | ||
| } | ||
|
|
||
| // Close the set under every rule of this stratum. | ||
| bool changed = true; | ||
| while (changed) | ||
| { | ||
| changed = false; | ||
| foreach (IMorphologicalRule rule in rules) | ||
| { | ||
| if (reach.Count == 0) | ||
| break; | ||
| IEnumerable<FeatureSymbol> next = ApplyRule(all, reach, rule); | ||
| foreach (FeatureSymbol symbol in next.ToList()) | ||
| changed |= reach.Add(symbol); | ||
| } | ||
| } | ||
|
|
||
| foreach (AffixTemplate template in stratum.AffixTemplates) | ||
| { | ||
| if (reach.Count == 0) | ||
| continue; | ||
| HashSet<FeatureSymbol> required = Constraint(all, template.RequiredSyntacticFeatureStruct); | ||
| if (required == null || required.Overlaps(reach)) | ||
| result.Add(template); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| // Synthesis marks a word partial only for a non-template, non-clitic partial rule | ||
| // (SynthesisAffixProcessRule), and a slot or clitic rule is exempt from the final-template check. | ||
| internal static bool IsSynthesisPartial(AffixProcessRule rule) | ||
| { | ||
| return rule.IsPartial && !rule.IsTemplateRule && !rule.GetIsCliticRule(); | ||
| } | ||
|
|
||
| private static IEnumerable<FeatureSymbol> ApplyRule( | ||
| HashSet<FeatureSymbol> all, | ||
| HashSet<FeatureSymbol> input, | ||
| IMorphologicalRule rule | ||
| ) | ||
| { | ||
| switch (rule) | ||
| { | ||
| case AffixProcessRule apr: | ||
| return Apply(all, input, apr.RequiredSyntacticFeatureStruct, apr.OutSyntacticFeatureStruct); | ||
| case CompoundingRule cr: | ||
| return Apply(all, input, cr.HeadRequiredSyntacticFeatureStruct, cr.OutSyntacticFeatureStruct); | ||
| case RealizationalAffixProcessRule rr: | ||
| return Apply(all, input, rr.RequiredSyntacticFeatureStruct, rr.RealizationalFeatureStruct); | ||
| default: | ||
| // Unknown rule kind: assume it can produce any part of speech. | ||
| return all; | ||
| } | ||
| } | ||
|
|
||
| /// <summary>Unify with <paramref name="required"/>, then priority-union <paramref name="output"/>.</summary> | ||
| private static IEnumerable<FeatureSymbol> Apply( | ||
| HashSet<FeatureSymbol> all, | ||
| HashSet<FeatureSymbol> input, | ||
| FeatureStruct required, | ||
| FeatureStruct output | ||
| ) | ||
| { | ||
| HashSet<FeatureSymbol> req = Constraint(all, required); | ||
| bool applicable = req == null ? input.Count > 0 : req.Overlaps(input); | ||
| if (!applicable) | ||
| return Enumerable.Empty<FeatureSymbol>(); | ||
| HashSet<FeatureSymbol> outPos = Constraint(all, output); | ||
| if (outPos != null) | ||
| return outPos; | ||
| if (req == null) | ||
| return input; | ||
| return input.Where(req.Contains); | ||
| } | ||
|
|
||
| /// <summary>The part-of-speech values a feature structure allows, or null if it leaves them open.</summary> | ||
| private static HashSet<FeatureSymbol> Constraint(HashSet<FeatureSymbol> all, FeatureStruct fs) | ||
| { | ||
| if (fs == null) | ||
| return null; | ||
| if (!fs.TryGetValue(SyntacticFeatureSystem.PartOfSpeechID, out SymbolicFeatureValue value)) | ||
| return null; | ||
| if (value.IsVariable) | ||
| return null; | ||
| var symbols = new HashSet<FeatureSymbol>(value.Values); | ||
| // A value that names no known symbol is treated as unconstrained, to stay conservative. | ||
| return symbols.Count == 0 ? null : symbols; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,7 @@ public enum FailureReason | |
| MaxApplicationCount, | ||
| AllomorphNotApplicable, | ||
| LexicalLookupFeatures, | ||
| OwedPartialCannotUnapply, | ||
| } | ||
|
|
||
| public interface ITraceManager | ||
|
|
@@ -45,7 +46,13 @@ public interface ITraceManager | |
| void PhonologicalRuleNotUnapplied(IPhonologicalRule rule, int subruleIndex, Word input); | ||
|
|
||
| void BeginUnapplyTemplate(AffixTemplate template, Word input); | ||
| void EndUnapplyTemplate(AffixTemplate template, Word output, bool unapplied, FailureReason reason); | ||
| void EndUnapplyTemplate( | ||
|
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. Major: F11. This breaks FieldWorks: a fifth parameter on the public |
||
| AffixTemplate template, | ||
| Word output, | ||
| bool unapplied, | ||
| FailureReason reason, | ||
| object failureObj | ||
| ); | ||
|
|
||
| void MorphologicalRuleUnapplied(IMorphologicalRule rule, int subruleIndex, Word input, Word output); | ||
| void MorphologicalRuleNotUnapplied(IMorphologicalRule rule, int subruleIndex, Word input); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.