Skip to content

Fix bug in MergeEquivalentAnalyses - #493

Open
jtmaxwell3 wants to merge 1 commit into
masterfrom
fix-merge-equivalent-analyses
Open

Fix bug in MergeEquivalentAnalyses#493
jtmaxwell3 wants to merge 1 commit into
masterfrom
fix-merge-equivalent-analyses

Conversation

@jtmaxwell3

@jtmaxwell3 jtmaxwell3 commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Comparing MergeEquivalentAnalyses to John Lambert's memoization code, I noticed that it was not computing equivalence correctly. So I borrowed AnalysisStateKey from the memoization code to fix the problem.


This change is Reviewable

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 73.51%. Comparing base (d734722) to head (4562502).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #493   +/-   ##
=======================================
  Coverage   73.51%   73.51%           
=======================================
  Files         451      451           
  Lines       37692    37692           
  Branches     5183     5183           
=======================================
  Hits        27708    27708           
  Misses       8845     8845           
  Partials     1139     1139           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ddaspit ddaspit left a comment

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.

@ddaspit reviewed 1 file and all commit messages, and made 1 comment.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on jtmaxwell3).


src/SIL.Machine.Morphology.HermitCrab/AnalysisStratumRule.cs line 155 at r1 (raw file):

                if (mergeEquivalentAnalyses)
                {
                    var key = AnalysisStateKey.PinAndKey(mruleOutWord);

wordCache and output don't agree on identity. Word.ValueEquals ignores SyntacticFeatureStruct, which the key includes. So analyses differing only in SyntacticFeatureStruct now get distinct keys, skip the merge, and are then dropped by output.Add.

Also, wordCache[key] = mruleOutWord runs before output.Add accepts it, so a rejected word stays canonical and swallows every later word with that key.

@ddaspit ddaspit left a comment

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.

@ddaspit made 1 comment.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on jtmaxwell3).


src/SIL.Machine.Morphology.HermitCrab/AnalysisStratumRule.cs line 155 at r1 (raw file):

Previously, ddaspit (Damien Daspit) wrote…

wordCache and output don't agree on identity. Word.ValueEquals ignores SyntacticFeatureStruct, which the key includes. So analyses differing only in SyntacticFeatureStruct now get distinct keys, skip the merge, and are then dropped by output.Add.

Also, wordCache[key] = mruleOutWord runs before output.Add accepts it, so a rejected word stays canonical and swallows every later word with that key.

You could do something like this:

var output = new Dictionary<Word, Word>(FreezableEqualityComparer<Word>.Default) { [input] = input };
...
if (mergeEquivalentAnalyses)
{
    AnalysisStateKey key = AnalysisStateKey.PinAndKey(mruleOutWord);
    if (wordCache.TryGetValue(key, out Word canonicalWord))
    {
        canonicalWord.Alternatives.Add(mruleOutWord);
        continue;
    }
    // Distinct key, but Word.ValueEquals ignores SyntacticFeatureStruct, so the output
    // may already hold an equal word. Keep this analysis as that word's alternative
    // rather than letting the output silently drop it.
    if (output.TryGetValue(mruleOutWord, out Word equivalentWord))
    {
        equivalentWord.Alternatives.Add(mruleOutWord);
        continue;
    }
    // Registered only once the word is certain to reach the output: a canonical that
    // never gets returned would swallow every later word with the same key.
    wordCache[key] = mruleOutWord;
}
if (!output.ContainsKey(mruleOutWord))
    output.Add(mruleOutWord, mruleOutWord);
...
return output.Keys;

@jtmaxwell3

jtmaxwell3 commented Sep 9, 2026 via email

Copy link
Copy Markdown
Collaborator Author

@johnml1135

Copy link
Copy Markdown
Collaborator

Heads-up from #494. Your suggested fix parks an analysis with different features behind one that stays active, and the parser keeps working on the active one only. If the active one has stricter features, the parked one's valid parses get filtered out and lost. That is exactly what happens under PriorityUnion in #494. The fix there is one line: when merging, loosen the active analysis's features to cover both. Whichever PR lands first should carry it.

(Claude helped).

@ddaspit ddaspit left a comment

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.

It is less than ideal that Word.ValueEquals and AnalysisStateKey disagree, but I would prefer to leave Word.ValueEquals alone. Changing it will have larger downstream effects that I would rather not deal with. We should just make the local change to deal with the discrepancy in this case.

@ddaspit made 1 comment.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on jtmaxwell3).

@jtmaxwell3

Copy link
Copy Markdown
Collaborator Author

@ddaspit: which local change do you have in mind? My change, which can drop solutions in output.Add, your change, which can lose valid solutions if the equivalent word fails but the alternative would succeed, or the change that John proposed in 3be1eb9?

@jtmaxwell3

Copy link
Copy Markdown
Collaborator Author

BTW, I still think that there is a bug in output.Add that is independent of MergeEquivalentAnalyses: if there are two words that are equivalent except for SyntacticFeatureStruct then one of them will be dropped. I think that this hasn't been a problem so far because SyntacticFeatureStruct is mostly determined by the morphemes (including the morphemes that are in templates). But if there were an inflection morpheme that could be in two different templates with different feature structures, then you could get two different words that are equivalent except for SyntaticFeatureStruct.

@ddaspit ddaspit left a comment

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.

@jtmaxwell3 This bug potentially changes things a bit, since it impacts the question of whether SyntacticFeatureStruct should be compared in Word.ValueEquals. This is what I would suggest:

  1. Fix the bug in output.Add by including SyntacticFeatureStruct in Word.ValueEquals. This is a larger change, but it should fix incorrectly dropped analyses and make this PR simpler.
  2. Merge this PR.
  3. Merge PR #494.

@ddaspit made 1 comment.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on jtmaxwell3).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants