Skip to content

Filter synthesis by the allomorphs used - #548

Merged
jtmaxwell3 merged 7 commits into
masterfrom
filter-synthesis-by-allomorph
Oct 27, 2026
Merged

jtmaxwell3 merged 7 commits into
masterfrom
filter-synthesis-by-allomorph

Conversation

@jtmaxwell3

@jtmaxwell3 jtmaxwell3 commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Parsing "kanna" in the DolganShared project takes forever to synthesize because many of the morphemes have lots of unconstrained allomorphs. For instance, the -LA: morpheme has more than 30 unconstrained allomorphs. The synthesizer tries all possible combinations of allomorphs in order to find the combination that produces the input word. One analysis of "kanna" had 5 morphemes that together had ~10,000 allomorph combinations. This was just one of 315 analyses for "kanna".

This pull request solves the problem by recording the allomorphs used during analysis and restricting the synthesizer to only use those allomorphs. This reduces the number of syntheses for "kanna" by four orders of magnitude.

Filtering by allomorphs wasn't as simple as you might imaging because some allomorphs block other free fluctuation allomorphs, so I couldn't just filter allomorphs at the top of the loop.


This change is Reviewable

@jtmaxwell3
jtmaxwell3 marked this pull request as draft October 22, 2026 16:29
Comment thread src/SIL.Machine.Morphology.HermitCrab/Word.cs
Comment thread src/SIL.Machine.Morphology.HermitCrab/Word.cs
Comment thread src/SIL.Machine.Morphology.HermitCrab/Word.cs
Comment thread src/SIL.Machine.Morphology.HermitCrab/Word.cs
@claude

This comment has been minimized.

@codecov-commenter

codecov-commenter commented Oct 23, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 59.45946% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.57%. Comparing base (74ee09b) to head (c74dfc7).

Files with missing lines Patch % Lines
...ab/MorphologicalRules/SynthesisAffixProcessRule.cs 40.00% 9 Missing and 3 partials ⚠️
src/SIL.Machine.Morphology.HermitCrab/Word.cs 76.92% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #548      +/-   ##
==========================================
- Coverage   74.57%   74.57%   -0.01%     
==========================================
  Files         453      453              
  Lines       38314    38342      +28     
  Branches     5280     5284       +4     
==========================================
+ Hits        28573    28593      +20     
- Misses       8576     8583       +7     
- Partials     1165     1166       +1     

☔ 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.

@claude

This comment has been minimized.

@claude

This comment has been minimized.

@claude

claude Bot commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

Approve. The new head traces an allomorph that synthesis drops because analysis did not use it, which closes the last open finding.

  1. Most important: nothing is open. F6 is fixed at SynthesisAffixProcessRule.cs:216.
  2. Counts: 0 Critical, 0 Important, 0 Low open. No new findings.
  3. Ran: gh pr checks 548. Build on ubuntu-22.04 passes. Build on windows-latest and claude-review are pending. Comment hygiene passes, but that check is advisory. Diffed f7aea42..57a81e0, which changes only the trace. Ran dotnet test filtered to AffixProcessRuleTests on the merge commit: 22 passed, 0 failed.
  4. Not verified: no fixture covers the F1 case (the disjunctive break), the F2 equality case, or the new trace event. I did not check the DolganShared speedup, or whether a FieldWorks trace consumer handles the new FailureReason value.
  5. Design: the earlier suggestion still applies. A single list of (rule, allomorph) pairs would keep _allomorphApps in step with _mruleApps.

Public API, target framework, package, or sillsdev/machine.py parity changed: public enum FailureReason (ITraceManager.cs:29) gains AllomorphNotApplicable. It is appended, so existing values keep their numbers. machine.py has no HermitCrab.

Findings: F1 addressed; F2 addressed; F3 withdrawn; F4 addressed; F5 addressed; F6 addressed.

Reviewed at 57a81e0

@johnml1135 johnml1135 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good to me, with two questions and a couple of nits. The filter sits after appliedAllomorphIndices.Add(i) and before the disjunctive break, so an unused allomorph still blocks later ones exactly as on master. I ran #480's conformance suite (140 fixtures) with and without this PR. Nothing changed outcome except the known truncate-morphotactic noise (#506), and the filter fired in eight different rules, so the result means something.

One heads-up for #480: its FieldworksProducibilityLedgerTests lists every FailureReason, and with this PR it fails with Missing (1): "AllomorphNotApplicable". Whichever of #480 and #548 lands second needs to regenerate that ledger row.

docs/review/hermitcrab.md asks for a reproducible measurement when a change claims a speedup. Could you add the before/after synthesis count or wall time for "kanna" in DolganShared? A design question too: synthesis used to try every allomorph, so it could make up for an analysis path that went through the wrong allomorph. With this PR it can't. I found no case in the conformance suite where that matters, but it's worth knowing that analysis now has to find each allomorph's path on its own.

This review was assisted by Claude Opus 5.5.

&& _nonHeadAppIndex == other._nonHeadAppIndex
&& _stratum == other._stratum
&& _rootAllomorph == other._rootAllomorph
&& _allomorphApps.SequenceEqual(other._allomorphApps)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Since _allomorphApps is now part of equality, analyses that differ only in which same-form allomorph was unapplied no longer merge, so each one counts against MaxAlternatives. In a probe test (one suffix, three identical +d allomorphs, parse sagd), the smallest MaxAlternatives that parses went from 1 to 3, with merging on or off. The results are the same. Is that intended? FieldWorks defaults the limit to 0, so only projects that set it are affected, but it would be worth a line in the description.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I hadn't thought of that, but I'd like to keep it as is. One of my goals is that synthesis not do any search. If the morphemes are underspecified, then synthesis of one Word could be exponential in the number of morphemes it has. I want it to be linear in the number of morphemes, if possible (or polynomial at worst).

_rule,
i,
input,
FailureReason.AllomorphNotApplicable,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

FieldWorks' FwXmlTraceManager.MorphologicalRuleNotApplied switches on FailureReason and hits default: return; for unknown values. So in Try a Word this allomorph still disappears from the trace. Do you plan a FieldWorks follow-up (a case plus text in FormatHCTrace.xsl), or is hiding it acceptable there?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I plan to add FailureReason.AllmorphNotApplicable to FieldWorks.

}

[Test]
public void IsAllomorphApplicable()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This test builds the analysis Word by hand, so it doesn't check that ParseWord hands the right allomorph to synthesis. It also doesn't cover the cases fixed in review: the disjunctive break, same-form allomorphs differing by MPR feature, and the merged ExpandAlternatives path. Could you add one ParseWord-level test, e.g. -s requiring MPR feature F plus an elsewhere -s, with merging on and off? It would also let SynthesizeWord and LexicalLookup (Morpher.cs:416, :427) stay private. MergeEquivalentAnalyses = true on line 2063 is already the default.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I'm not sure that I understand what you want. I'm happy for you to upgrade the test after I merge it if you want.

@jtmaxwell3
jtmaxwell3 marked this pull request as ready for review October 27, 2026 01:29
@jtmaxwell3
jtmaxwell3 merged commit c245476 into master Oct 27, 2026
7 of 9 checks passed
@jtmaxwell3
jtmaxwell3 deleted the filter-synthesis-by-allomorph branch October 27, 2026 01:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants