Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/SIL.Machine.Morphology.HermitCrab/Morpher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -413,13 +413,18 @@ private IEnumerable<Word> SynthesizeParallel(string word, ConcurrentQueue<Word>
return matches.Distinct(FreezableEqualityComparer<Word>.Default);
}

internal IEnumerable<Word> SynthesizeWord(Word input)
{
return _synthesisRule.Apply(input).Where(IsWordValid);
}

internal IEnumerable<RootAllomorph> SearchRootAllomorphs(Stratum stratum, Shape shape)
{
RootAllomorphTrie alloSearcher = _allomorphTries[stratum];
return alloSearcher.Search(shape).Distinct();
}

private IEnumerable<Word> LexicalLookup(Word input)
internal IEnumerable<Word> LexicalLookup(Word input)
{
if (_traceManager.IsTracing)
_traceManager.LexicalLookup(input.Stratum, input);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ out FeatureStruct syntacticFS
: FinalTemplateState.None;
}
}
outWord.MorphologicalRuleUnapplied(_rule);
outWord.MorphologicalRuleUnapplied(_rule, _rule.Allomorphs[i]);
outWord.Freeze();
if (_morpher.TraceManager.IsTracing)
_morpher.TraceManager.MorphologicalRuleUnapplied(_rule, i, input, outWord);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,21 +196,24 @@ public IEnumerable<Word> Apply(Word input)
outWord.MorphologicalRuleApplied(_rule, appliedAllomorphIndices);
appliedAllomorphIndices.Add(i);

if (_rule.Blockable && outWord.CheckBlocking(out Word newWord))
if (input.IsAllomorphApplicable(allo))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: F6. An allomorph that matches but was not used in analysis is dropped with no trace event, neither MorphologicalRuleApplied nor MorphologicalRuleNotApplied. In a Try-a-Word trace it just disappears, which hides cases like F1.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

F6 still applies at bb7211a: the reworked block at SynthesisAffixProcessRule.cs:199 still drops a matched but unused allomorph with no trace event, and the MPR failures for unused allomorphs are now untraced too.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

F6 still applies at f7aea42: the MPR failures at SynthesisAffixProcessRule.cs:150 and :167 are traced again, but an allomorph that matches and was not used in analysis (the IsAllomorphApplicable check at line 199) still produces neither MorphologicalRuleApplied nor MorphologicalRuleNotApplied, so it vanishes from a trace.

{
if (_rule.Blockable && outWord.CheckBlocking(out Word newWord))
{
if (_morpher.TraceManager.IsTracing)
_morpher.TraceManager.Blocked(_rule, newWord);
outWord = newWord;
}
else
{
outWord.Freeze();
}

if (_morpher.TraceManager.IsTracing)
_morpher.TraceManager.Blocked(_rule, newWord);
outWord = newWord;
}
else
{
outWord.Freeze();
_morpher.TraceManager.MorphologicalRuleApplied(_rule, i, input, outWord);
output.Add(outWord);
}

if (_morpher.TraceManager.IsTracing)
_morpher.TraceManager.MorphologicalRuleApplied(_rule, i, input, outWord);
output.Add(outWord);

// return all word syntheses that match subrules that are constrained by environments,
// HC violates the disjunctive property of allomorphs here because it cannot check the
// environmental constraints until it has a surface form, we will enforce the disjunctive
Expand Down
23 changes: 20 additions & 3 deletions src/SIL.Machine.Morphology.HermitCrab/Word.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class Word : Freezable<Word>, IAnnotatedData<ShapeNode>, ICloneable<Word>
private RootAllomorph _rootAllomorph;
private Shape _shape;
private readonly List<IMorphologicalRule> _mruleApps;
private readonly List<Allomorph> _allomorphApps;
Comment thread
claude[bot] marked this conversation as resolved.
private int _mruleAppIndex = -1;
private readonly Dictionary<IMorphologicalRule, int> _mrulesUnapplied;
private readonly Dictionary<IMorphologicalRule, int> _mrulesApplied;
Expand All @@ -42,6 +43,7 @@ public Word(RootAllomorph rootAllomorph, FeatureStruct realizationalFS)
ResetDirty();
SetRootAllomorph(rootAllomorph);
RealizationalFeatureStruct = realizationalFS;
_allomorphApps = new List<Allomorph>();
_mruleApps = new List<IMorphologicalRule>();
_mrulesUnapplied = new Dictionary<IMorphologicalRule, int>();
_mrulesApplied = new Dictionary<IMorphologicalRule, int>();
Expand All @@ -61,6 +63,7 @@ public Word(Stratum stratum, Shape shape)
SyntacticFeatureStruct = new FeatureStruct();
RealizationalFeatureStruct = new FeatureStruct();
_mprFeatures = new MprFeatureSet();
_allomorphApps = new List<Allomorph>();
_mruleApps = new List<IMorphologicalRule>();
_mrulesUnapplied = new Dictionary<IMorphologicalRule, int>();
_mrulesApplied = new Dictionary<IMorphologicalRule, int>();
Expand All @@ -83,6 +86,7 @@ protected Word(Word word)
SyntacticFeatureStruct = word.SyntacticFeatureStruct.Clone();
RealizationalFeatureStruct = word.RealizationalFeatureStruct.Clone();
_mprFeatures = word.MprFeatures.Clone();
_allomorphApps = new List<Allomorph>(word._allomorphApps);
Comment thread
claude[bot] marked this conversation as resolved.
_mruleApps = new List<IMorphologicalRule>(word._mruleApps);
_mruleAppIndex = word._mruleAppIndex;
_mrulesUnapplied = new Dictionary<IMorphologicalRule, int>(word._mrulesUnapplied);
Expand Down Expand Up @@ -247,6 +251,15 @@ internal bool IsMorphologicalRuleApplicable(IMorphologicalRule rule)
return curRule == rule || (curRule == null && rule is CompoundingRule);
}

internal bool IsAllomorphApplicable(Allomorph allomorph)
Comment thread
claude[bot] marked this conversation as resolved.
{
if (_mruleAppIndex < 0)
return false;

Allomorph curAllomorph = _allomorphApps[_mruleAppIndex];
return curAllomorph == null || curAllomorph == allomorph;
}

internal bool HasRemainingRulesFromStratum(Stratum stratum)
{
if (_mruleAppIndex < 0)
Expand Down Expand Up @@ -313,19 +326,21 @@ internal void RemoveMorph(Annotation<ShapeNode> morphAnn)
}

/// <summary>
/// Notifies this word that the specified morphological rule was unapplied. Null
/// Notifies this word that the specified morphological rule and allomorph were unapplied.
/// A null allomorph means that any allomorph is acceptable. A null morphological rule
/// indicates that an unknown compounding rule was unapplied. This is used when
/// generating a compound word, because the compounding rule is usually not known just
/// the non-head allomorph.
/// </summary>
internal void MorphologicalRuleUnapplied(IMorphologicalRule mrule)
internal void MorphologicalRuleUnapplied(IMorphologicalRule mrule, Allomorph allomorph = null)
Comment thread
claude[bot] marked this conversation as resolved.
{
CheckFrozen();
if (mrule != null)
_mrulesUnapplied.UpdateValue(mrule, () => 0, count => count + 1);
if (!(mrule is RealizationalAffixProcessRule))
{
_mruleApps.Add(mrule);
_allomorphApps.Add(allomorph);
_mruleAppIndex++;
}
}
Expand Down Expand Up @@ -441,7 +456,7 @@ internal IList<Word> ExpandAlternatives()
// Add new rules to alternative.
int m_start = Source == null ? 0 : Source._mruleApps.Count();
for (int i = m_start; i < _mruleApps.Count(); i++)
alternative.MorphologicalRuleUnapplied(_mruleApps[i]);
alternative.MorphologicalRuleUnapplied(_mruleApps[i], _allomorphApps[i]);
int nh_start = Source == null ? 0 : Source._nonHeadApps.Count();
for (int i = nh_start; i < _nonHeadApps.Count(); i++)
alternative.NonHeadUnapplied(_nonHeadApps[i]);
Expand Down Expand Up @@ -536,6 +551,7 @@ protected override int FreezeImpl()
code = code * 31 + _nonHeadAppIndex.GetHashCode();
code = code * 31 + _stratum.GetHashCode();
code = code * 31 + (_rootAllomorph == null ? 0 : _rootAllomorph.GetHashCode());
code = code * 31 + _allomorphApps.GetSequenceHashCode();
code = code * 31 + _mruleApps.GetSequenceHashCode();
code = code * 31 + _mruleAppIndex.GetHashCode();
code = code * 31 + _isLastAppliedRuleFinal.GetHashCode();
Expand All @@ -560,6 +576,7 @@ public override bool ValueEquals(Word other)
&& _nonHeadAppIndex == other._nonHeadAppIndex
&& _stratum == other._stratum
&& _rootAllomorph == other._rootAllomorph
&& _allomorphApps.SequenceEqual(other._allomorphApps)
&& _mruleApps.SequenceEqual(other._mruleApps)
&& _mruleAppIndex == other._mruleAppIndex
&& _isLastAppliedRuleFinal == other._isLastAppliedRuleFinal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2031,4 +2031,61 @@ public void NonContiguousRules()
var morpher = new Morpher(TraceManager, Language);
AssertMorphsEqual(morpher.ParseWord("katibɯd"), "49 PER.ACT");
}

[Test]
public void IsAllomorphApplicable()
{
FeatureStruct any = FeatureStruct.New().Symbol(HCFeatureSystem.Segment).Value;
var sSuffix = new AffixProcessRule
{
Name = "s_suffix",
Gloss = "3SG",
RequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("V").Value,
OutSyntacticFeatureStruct = FeatureStruct
.New(Language.SyntacticFeatureSystem)
.Feature(Head)
.EqualTo(head => head.Feature("pers").EqualTo("3"))
.Value,
};
var allomorph1 = new AffixProcessAllomorph
{
Lhs = { Pattern<Word, ShapeNode>.New("1").Annotation(any).OneOrMore.Value },
Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "s") },
};
var allomorph2 = new AffixProcessAllomorph
{
Lhs = { Pattern<Word, ShapeNode>.New("1").Annotation(any).OneOrMore.Value },
Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "z") },
};
sSuffix.Allomorphs.Add(allomorph1);
sSuffix.Allomorphs.Add(allomorph2);
Morphophonemic.MorphologicalRules.Add(sSuffix);
var morpher = new Morpher(TraceManager, Language) { MergeEquivalentAnalyses = true };

Shape shape = Morphophonemic.CharacterDefinitionTable.Segment("sag");
var input = new Word(Morphophonemic, shape);
input.MorphologicalRuleUnapplied(sSuffix);
input.Freeze();
Word word = morpher.LexicalLookup(input).First();
IEnumerable<Word> output = morpher.SynthesizeWord(word);
Assert.That(output.Count(), Is.EqualTo(2));

shape = Morphophonemic.CharacterDefinitionTable.Segment("sag");
input = new Word(Morphophonemic, shape);
input.MorphologicalRuleUnapplied(sSuffix, allomorph1);
input.Freeze();
word = morpher.LexicalLookup(input).First();
output = morpher.SynthesizeWord(word);
Assert.That(output.Count(), Is.EqualTo(1));
Assert.That(output.First().ToString(), Is.EqualTo("sags"));

shape = Morphophonemic.CharacterDefinitionTable.Segment("sag");
input = new Word(Morphophonemic, shape);
input.MorphologicalRuleUnapplied(sSuffix, allomorph2);
input.Freeze();
word = morpher.LexicalLookup(input).First();
output = morpher.SynthesizeWord(word);
Assert.That(output.Count(), Is.EqualTo(1));
Assert.That(output.First().ToString(), Is.EqualTo("sagz"));
}
}
Loading