Repository navigation
Prune affix template partials with a required-slot look-ahead - #575
johnml1135 wants to merge 4 commits into
Conversation
…erial An analysis template partial now stops as soon as a required slot can no longer find the segments its affix inserts, instead of paying for every optional slot before the required one. The test is used only while every slot walked since is a pure copy-and-insert rule, so parses do not change. A count-only shadow mode and a null-by-default check observer let a harness verify soundness and cost on real grammars. Closes #561 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #575 +/- ##
==========================================
+ Coverage 74.57% 74.76% +0.19%
==========================================
Files 453 454 +1
Lines 38342 38743 +401
Branches 5284 5375 +91
==========================================
+ Hits 28593 28967 +374
- Misses 8583 8595 +12
- Partials 1166 1181 +15 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This comment has been minimized.
This comment has been minimized.
…lking a template The required-slot look-ahead now needs each rule's inserted segments to match distinct segments in the order the rule inserts them, a slot that inserts only before or after its copied parts to find that material within the reach of the outer slots' own edge material, and the material of a required ordered slot to lie after the prefix and before the suffix of the required slots that unapply first. Slots that can reorder, repeat or remove from the middle switch the order and edge tests off for the slots behind them. Closes #567 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hed parts are unbounded runs, which restore no segments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
…y rules conservatively - Morpher.PruneRequiredSlots turns the prune off (default on); set it before parsing. - Tracing runs the slot rules without pruning, so failure reasons stay. - An allomorph with an untyped constraint is impure and unprunable, so a grammar that compiled before cannot throw. - A boundary-only insertion no longer counts as an infix. - Drop the redundant HasSegmentConstraint guard. - Tests for the optional-node rule, the suffix-side window, empty and deleted unbounded runs, and ordered copies.
| private bool RequiredSlotCannotUnapply(Word word, int lastSlot, ref int[] fired) | ||
| { | ||
| RequiredSlotLookaheadMode mode = _morpher.RequiredSlotLookahead; | ||
| if (mode == RequiredSlotLookaheadMode.Off || _morpher.TraceManager.IsTracing) |
There was a problem hiding this comment.
FYI: F8. Dead trace branches: this method now returns false whenever TraceManager.IsTracing is set, so the IsTracing blocks that trace a prune at lines 69-70, 152-153, and 200-206 can never run. They suggest pruned partials still show up in traces, but they don't. Remove them.
| || allomorph.Rhs.Where(IsInsertion).SelectMany(InsertedConstraints).Any(HasNoType) | ||
| ) | ||
| { | ||
| summary.MayAlwaysUnapply = true; |
There was a problem hiding this comment.
FYI: F9. Duplicated block: these five lines, which make a slot opaque, are the same as lines 306-310 for an unknown rule type. A field added to SlotSummary later has to be set in both places, and missing one makes the test unsound. Move them into one helper.
|
Verdict: approve. F1-F3 and F6-F7 are fixed in 91ee3da. F4 and F5 are deferred to #598. Most important: F8 (Low), Counts: 0 Critical, 0 Important, 2 new Low (F8, F9). Ran: Not verified: I did not run the new tests locally. I did not re-measure the pruning cost (F5, now #598). Public API changed: Findings: F1 addressed, F2 addressed, F3 addressed, F4 accepted (#598), F5 accepted (#598, unverified), F6 addressed, F7 addressed, F8 new, F9 new. Reviewed at 91ee3da |
Start here:
src/SIL.Machine.Morphology.HermitCrab/RequiredSlotMaterial.cs-- the class remarks (Presence, Order) give the soundness argument for dropping a partial; the rest is wiring.Status: open, review required; head 91ee3da. CI was still running when this was written. #576 and #579 are folded into this branch (both show as merged); #580 (draft) is parked on top and #577 (draft) is parked.
HermitCrab's affix-template walk now drops a partial as soon as a required slot can no longer find the segments its affix inserts. Parses are unchanged; doomed partials stop paying for every optional slot between them and the required one. The slot's literals must appear in the order the rule inserts them and, for prefix-only or suffix-only slots, near the word edge; slots whose unmatched parts are unbounded runs keep being tested. Pruning is on by default;
Morpher.PruneRequiredSlots = falseturns it off (set it before parsing), and tracing never prunes, so failure reasons stay.The question a reviewer arrives with is "does it lose a parse?". Tests assert identical parses with pruning off and on for each hard case, and a shadow pass found 0 violations (carried over, see Evidence). A slot is tested only while every slot walked since is pure, ordered where order is used, and typed; untyped constraints and boundary-only inserts are handled conservatively. The question worth your time is the cost side: the look-ahead has its own unification cost, so whether it pays on real grammars.
Where to look
Apply_ImpureSlotBetween_DoesNotPruneForTheInnerRequiredSlot(purity gate),AnalysisAffixTemplateOrderTests(order and edge windowSlotWalk.Lo/Hi),AnalysisAffixTemplateFusionTests(unbounded runs).RequiredSlotMaterial.RestoredSegmentsagainstAnalysisMorphologicalTransform.Untruncate: an unbounded run must restore nothing, or the purity test is unsound.Apply_Parallel_ReturnsTheSameParsesAsSequential,Apply_Tracing_RunsTheRulesForDetailedFailureInformation.Apply_PublicPruningSwitch_ControlsRuleAttempts,Compile_UntypedConstraints_DisablesTheSummaryConservatively,Apply_BoundaryOnlyInfix_DoesNotMakeReachUnbounded.Not here: the performance follow-ups (repeated shape scans, narrowing, keeping shadow mode) are tracked in #598; #580 is parked; the stack was not re-measured on top of #569, which may reduce its value.
Verification at 91ee3da: HermitCrab tests 199/199 pass (Release);
dotnet csharpierand comment hygiene clean; the HermitCrab project builds with 0 warnings. The fulllocal_check.shdid not complete (cmake is missing for SentencePiece). The measurements below were not re-run at this head.Next: review
RequiredSlotMaterial.cs, and say whether the cost side justifies merging before #569 is measured.Reading this a year from now -- start here
This branch is four commits on master c245476: 0bf9e64 (the required-slot prune, issue #561), 28cbee1 (was PR #576: slot literals in order and at reachable edges, issue #567), faa2499 (was PR #579: keep testing behind slots whose unmatched parts are unbounded runs) and 91ee3da (review fixes). #576 and #579 were separate stacked PRs and were folded in so the look-ahead is reviewed once. All of it runs under the internal
RequiredSlotLookaheadMode(Off,Shadow,On);Morpher.PruneRequiredSlotsis the public on/off view of it. The unification and census counters that produced the numbers below are not in this PR; they live on the research branchesresearch/hc-rsl-census@6ad11702andresearch/hc-ordered-census@4bb687f0.Decisions, and why
RequiredSlotCheckObserver(null by default) are kept for the soundness tests; whether to keep them is part of HermitCrab analysis: measure the required-slot look-ahead's repeated scans, narrowing and shadow diagnostics in steps #598.Untruncatewrites nothing for it, so a slot whose unmatched parts are unbounded runs stays pure. Bounded runs, bare restored segments, alternations with an optional branch andModifyFromInputstay impure.Paths not taken
Evidence (carried over, not re-run at 91ee3da)
All figures are from the bodies of #575, #576 and #579 before the fold, measured on those PRs' own heads (before the review-fix commit; the #579 figures also before #522 removed the analysis memo). They are not from 91ee3da.
Deferred, and what would unblock it
🤖 Generated with Claude Code
This change is