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: 4 additions & 3 deletions src/SIL.Machine.Morphology.HermitCrab/AnalysisLanguageRule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ internal class AnalysisLanguageRule : IRule<Word, ShapeNode>
{
private readonly Morpher _morpher;
private readonly List<Stratum> _strata;
private readonly List<IRule<Word, ShapeNode>> _rules;
private readonly List<AnalysisStratumRule> _rules;

public AnalysisLanguageRule(Morpher morpher, Language language)
{
_morpher = morpher;
_strata = language.Strata.Reverse().ToList();
_rules = _strata.Select(stratum => stratum.CompileAnalysisRule(morpher)).ToList();
_rules = _strata.Select(stratum => new AnalysisStratumRule(morpher, stratum)).ToList();
}

public IEnumerable<Word> Apply(Word input)
Expand All @@ -31,10 +31,11 @@ public IEnumerable<Word> Apply(Word input)

HashSet<Word> outputSet = tempSet;
outputSet.Clear();
int alternativeCount = 0;

foreach (Word inData in inputSet)
{
foreach (Word outData in _rules[i].Apply(inData))
foreach (Word outData in _rules[i].CappedApply(inData, ref alternativeCount))
{
outputSet.Add(outData);
results.Add(outData);
Expand Down
21 changes: 19 additions & 2 deletions src/SIL.Machine.Morphology.HermitCrab/AnalysisStratumRule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ private IRule<Word, ShapeNode> CompilePhonologicalRule(IPhonologicalRule prule,
}

public IEnumerable<Word> Apply(Word input)
{
int alternativeCount = 0;
return CappedApply(input, ref alternativeCount);
}

internal IEnumerable<Word> CappedApply(Word input, ref int alternativeCount)
{
if (_morpher.TraceManager.IsTracing)
_morpher.TraceManager.BeginUnapplyStratum(_stratum, input);
Expand All @@ -108,6 +114,11 @@ public IEnumerable<Word> Apply(Word input)
input = input.Clone();
input.Stratum = _stratum;

if (_mrulesRule is ParallelCombinationRuleCascade<Word, ShapeNode> parallelMRulesRule)
{
parallelMRulesRule.SetMaxAlternatives(_morpher.MaxAlternatives);
}

_prulesRule.Apply(input);
input.Freeze();
IDictionary<Shape, Word> shapeWord = null;
Expand All @@ -125,6 +136,14 @@ public IEnumerable<Word> Apply(Word input)
_morpher.TraceManager.EndUnapplyStratum(_stratum, input);
foreach (Word mruleOutWord in mruleOutWords)
{
alternativeCount++;
if (_morpher.MaxAlternatives > 0 && alternativeCount >= _morpher.MaxAlternatives)
{
// Not literally a timeout, but serves the same purpose.
// (A literal timeout would produce different results on different machines.)
// Stops before full enumeration because ApplyTemplates and ApplyMorphologicalRules use yield return.
throw new TimeoutException("MaxAlternatives exceeded");
}
// Skip intermediate sources from phonological rules, templates, and morphological rules.
mruleOutWord.Source = origInput;
if (mergeEquivalentAnalyses)
Expand All @@ -141,8 +160,6 @@ public IEnumerable<Word> Apply(Word input)
output.Add(mruleOutWord);
if (_morpher.TraceManager.IsTracing)
_morpher.TraceManager.EndUnapplyStratum(_stratum, mruleOutWord);
if (_morpher.MaxUnapplications > 0 && output.Count >= _morpher.MaxUnapplications)
break;
}
return output;
}
Expand Down
4 changes: 2 additions & 2 deletions src/SIL.Machine.Morphology.HermitCrab/Morpher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public Morpher(ITraceManager traceManager, Language lang)
_analysisRule = lang.CompileAnalysisRule(this);
_synthesisRule = lang.CompileSynthesisRule(this);
MaxStemCount = 2;
MaxUnapplications = 0;
MaxAlternatives = 0;
MergeEquivalentAnalyses = true;
LexEntrySelector = entry => true;
RuleSelector = rule => true;
Expand All @@ -76,7 +76,7 @@ public ITraceManager TraceManager
/// to make it possible to debug words that take 30 minutes to parse
/// because there are too many unapplications.
/// </summary>
public int MaxUnapplications { get; set; }
public int MaxAlternatives { get; set; }

/// <summary>
/// Merge analyses that have equivalent shapes.
Expand Down
11 changes: 11 additions & 0 deletions src/SIL.Machine/Rules/ParallelCombinationRuleCascade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ IEqualityComparer<TData> comparer
)
: base(rules, multiApp, comparer) { }

private int _maxAlternatives = 0;

public void SetMaxAlternatives(int maxAlternatives)
{
_maxAlternatives = maxAlternatives;
}

public override IEnumerable<TData> Apply(TData input)
{
var output = new ConcurrentStack<TData>();
Expand All @@ -50,6 +57,10 @@ public override IEnumerable<TData> Apply(TData input)
if (results.Length > 0)
{
output.PushRange(results);
if (_maxAlternatives > 0 && output.Count > _maxAlternatives)
{
throw new TimeoutException("MaxAlternatives exceeded");
}

Tuple<TData, HashSet<int>>[] workItems = results
.Where(res => !Comparer.Equals(work.Item1, res))
Expand Down
27 changes: 27 additions & 0 deletions tests/SIL.Machine.Morphology.HermitCrab.Tests/MorpherTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,33 @@ public void AnalyzeWord_CanAnalyze_ReturnsCorrectAnalysis()
);
}

[Test]
public void AnalyzeWord_MaxAlternatives()
{
var any = FeatureStruct.New().Symbol(HCFeatureSystem.Segment).Value;

var edSuffix = new AffixProcessRule
{
Id = "PAST",
Name = "ed_suffix",
Gloss = "PAST",
RequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("V").Value,
};
edSuffix.Allomorphs.Add(
new AffixProcessAllomorph
{
Lhs = { Pattern<Word, ShapeNode>.New("1").Annotation(any).OneOrMore.Value },
Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "+d") },
}
);
Morphophonemic.MorphologicalRules.Add(edSuffix);

var morpher = new Morpher(TraceManager, Language);
morpher.MaxAlternatives = 1;

Assert.Throws<TimeoutException>(() => morpher.AnalyzeWord("sagd"));
}

[Test]
public void AnalyzeWord_CanAnalyzeLinear_ReturnsCorrectAnalysis()
{
Expand Down
Loading