Skip to content

Require slot literals in order and at reachable edges in the template look-ahead - #576

Open
johnml1135 wants to merge 1 commit into
perf/hc-required-slot-lookaheadfrom
perf/hc-ordered-slot-lookahead
Open

johnml1135 wants to merge 1 commit into
perf/hc-required-slot-lookaheadfrom
perf/hc-ordered-slot-lookahead

Conversation

@johnml1135

@johnml1135 johnml1135 commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Start here: src/SIL.Machine.Morphology.HermitCrab/RequiredSlotMaterial.cs -- the class remarks (Presence, Order) hold the soundness argument; the rest of the PR is wiring.

Status: stacked on #575 (open); merge #575 first. All CI jobs pass.

The affix-template look-ahead from #575 now also requires a rule's inserted segments to appear in the order the rule inserts them, a slot that inserts only before (or after) its copied parts to find that material near the word edge once the outer slots have taken theirs, and the material of required slots to lie in slot order. Parses are unchanged; slots that can reorder, repeat or remove from the middle switch these tests off for the slots behind them.

Worth knowing before reading: it is a tightening of the #575 prune, not a new pass. The diff against #575 is RequiredSlotMaterial.cs, 4 changed lines in AnalysisAffixTemplateRule.cs, and a new test class; the question worth your time is whether the order and edge conditions can wrongly prune a real parse.

Where to look

  • Soundness: IsOrdered is the per-rule order-preserving flag; the SlotWalk Lo/Hi window bounds where later material may sit.
  • AnalysisAffixTemplateOrderTests: cases go red when the condition is removed for copies out of order, a part copied twice, an infix, optional slots in the left and right reach, circumfix overlap, and the Lo/Hi window (deleted or inverted).
  • Not pinned: the variable-constraint wildcard (inherited from Prune affix template partials whose required slot cannot find its material #575; removing it changes nothing observable) and the null-allomorph outer slot (guard test, not shown to go red).
  • Morpher.RequiredSlotShadow and RequiredSlotCheckObserver are unchanged and null by default; no Interlocked was added.

Not here: Mbugwe's object-marker and subject slots are impure (vowel-fusion allomorphs restore segments), so the walk still stops testing at them. That needs a purity extension, not more ordering.

Verification: the HermitCrab test project (Release), shadow runs, the conformance runner, dotnet csharpier check . and pwsh ./scripts/comment-hygiene.ps1 were run when this was written; none was re-run for this description. Conformance runner: same 4 known failures (truncate-morphotactic family) on base and branch. Nothing red. Work figures are in steps, in Evidence.

Next: review RequiredSlotMaterial.cs after #575 lands, or tell me which condition to split out.


Reading this a year from now -- start here

#575 prunes an affix-template partial when a required slot cannot find its material anywhere. This PR makes that test stricter: material must be in the right order and near the right word edge. The measured gain over #575 is below; the unification counters that produced it were never merged and live only on the research branch research/hc-ordered-census @ 4bb687f0. Issue: #567.

Evidence -- shadow, parity, work counts
  • Shadow over conformance, Indonesian, Aweti, Amharic, Mbugwe, Sena and the Bantu stress grammar: 0 violations, 0 completions under a fired check; per-word parses equal to master in traced and merged modes.
  • Conformance runner, base vs branch: the same 4 failures in both (the known truncate-morphotactic family), per-fixture verdicts identical.
  • Work counted in feature-structure unification calls over the whole parse (look-ahead included), extra savings over Prune affix template partials whose required slot cannot find its material #575: Sena 3.7 points less work, Aweti 4.8, Amharic 3.4 (net more work under Prune affix template partials whose required slot cannot find its material #575, now 2.75% less than master), stress grammar 34.1, conformance 1.1, Mbugwe 1.5, Indonesian unchanged.
  • In events plus synthesis steps against master: Mbugwe 7.3% less, Sena 40.9%, Aweti 11.0%, Amharic 11.9%, conformance 1.4%, stress grammar 86.1%, Indonesian unchanged.
  • Figures are from the earlier description; the research branch was not re-run for this edit.
Surprising findings -- the cost side

Aweti check-only unifications (the look-ahead's own calls) rose 3.5% against #575 (232,302 vs 224,347), though its total unification work fell 5.0%. Check-only counts fell on Mbugwe, Sena, Amharic, conformance and the stress grammar.

Deferred -- impure slots

Mbugwe's object-marker and subject slots stay untested because vowel-fusion allomorphs restore segments. Unblocking needs a purity extension to the walk.

🤖 Generated with Claude Code


This change is Reviewable

…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>
}
else
}
if (segment && !node.Annotation.Optional)

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.

Minor: F1. Untested soundness rule: no test puts an optional node (an unapplied deletion) in the shape, yet before/after leave such nodes out of the reach. If that exclusion breaks, a required prefix behind an optional node is pruned in On mode and a valid parse is lost; every fixture in AnalysisAffixTemplateOrderTests uses plain Segment(form) shapes, so nothing would fail. Add an Off/On/Shadow equality case with a deletion rule ahead of the template.

return false;
any = true;
newLo = Math.Min(newLo, allomorph.Pre > 0 ? prefixEnd : walk.Lo);
newHi = Math.Max(newHi, allomorph.Post > 0 ? suffixStart : walk.Hi);

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: F2. Only the left side of the window is tested. Apply_InnerMaterialOnlyInsideTheOuterPrefix_PrunesAtEntry covers Lo, but no case has an outer required suffix narrow Hi, or exercises the backward pass stopping at forwardEnd/walk.Lo (line 234). An off-by-one there would go unnoticed. The mirror case, inner material only inside the outer suffix, would cover it.

foreach (int[] requirements in summary.Allomorphs)
// The window is only narrowed for a slot that has slots after it in the walk, and that needs every
// passing allomorph, not the first.
bool narrow = walk.Ordered && summary.IsOrdered && slot > 0;

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: F3. Every ordered required slot above 0 now runs Embeds over all its allomorphs, not just the first that passes, even when no required, unfired slot behind it will read the new Lo/Hi (all optional, fired, or past an impure slot). That may be part of the 3.5% rise in Aweti check-only unifications noted in the description. Narrowing only when a later slot will be tested would avoid it.


// Once a slot that can reorder nodes has been walked the order of the shape says nothing about the order of
// the shape the slot unapplies to, so each constraint is only looked for on its own.
private static bool AllPresent(AllomorphSummary allomorph, Shape shape, ref long unifications)

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: F4. The per-call cache of distinct constraints (known/present over summary.Distinct) is gone. So AllPresent rescans the shape for the same constraint once per allomorph that carries it, where before it scanned once per call. The cache still applies on this path, because order plays no part in it.

return false;
}

// The ordinals are those of the shape, margins excluded. A node is counted against the reach only when it

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: F5. Embeds restates the analysis matcher settings it relies on: anchored at both ends, a segment-only filter, optional nodes skippable. Nothing ties it to AnalysisAffixProcessRule and AnalysisRealizationalAffixProcessRule (AnchoredToStart/AnchoredToEnd/Filter). If those change, the edge test silently drops parses. A test asserting those settings, or a <see cref> to them here, would keep the two together.

AddRequirement(requirements, insertContext.SimpleContext.FeatureStruct);
}
int added = sequence.Count - before;
if (first < 0 || (i > first && i < last))

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 insert between copies that adds no segment constraint (for example a boundary-only InsertSegments, added == 0) still sets infix. That makes the reach Unbounded and switches off the edge tests for this slot and every slot behind it. This is sound, but it gives up pruning for no reason. Guard with added > 0.

@claude

claude Bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Verdict: approve with fixes. I found no defect that loses a parse. Earliest-match from the left and latest-match from the right give sound Lo/Hi bounds, and the reach sums are upper bounds.

Most important: F1. Nothing tests the rule that keeps optional nodes out of the edge reach (src/SIL.Machine.Morphology.HermitCrab/RequiredSlotMaterial.cs:220). If that rule broke, On mode would silently drop parses.

Counts: 0 Critical, 1 Important, 5 Low.

Ran: dotnet test tests/SIL.Machine.Morphology.HermitCrab.Tests -c Release: 183 passed, 0 failed. gh pr checks 576: both builds and comment hygiene pass; NuGet packaging was still pending.

Not verified: the corpus figures (Sena, Aweti, Amharic, Mbugwe, stress grammar) and the shadow runs, which depend on the research/hc-ordered-census counters. I did not run --agent-strict, and the advisory hygiene tick does not stand in for it.

Public API / target framework / package / machine.py parity: None verified. Every change is internal to HermitCrab.

Findings: F1 new, F2 new, F3 new, F4 new, F5 new, F6 new.

Reviewed at 28cbee1

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.30769% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.64%. Comparing base (0bf9e64) to head (28cbee1).

Files with missing lines Patch % Lines
...hine.Morphology.HermitCrab/RequiredSlotMaterial.cs 92.19% 6 Missing and 10 partials ⚠️
Additional details and impacted files
@@                         Coverage Diff                         @@
##           perf/hc-required-slot-lookahead     #576      +/-   ##
===================================================================
- Coverage                            74.65%   74.64%   -0.01%     
===================================================================
  Files                                  454      454              
  Lines                                38573    38716     +143     
  Branches                              5338     5370      +32     
===================================================================
+ Hits                                 28796    28900     +104     
- Misses                                8601     8634      +33     
- Partials                              1176     1182       +6     

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

johnml1135 added a commit that referenced this pull request Oct 28, 2026
Rows 43 and 44 move from in progress to done. R1 on #546 is draft PR #577
(commit bbb81a0, shadow 0 violations); R3 ordered look-ahead is draft
PR #576 (shadow 0 violations). Adds the round-doc section for both and
the PanGloss port issues (sillsdev/PanGloss #65, #66).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
johnml1135 added a commit that referenced this pull request Oct 28, 2026
…for the impure-slot look-ahead stacked on #576.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
johnml1135 added a commit that referenced this pull request Oct 28, 2026
R1 called a slot impure when a copied part may match nothing. An
unbounded run (max=-1) restores no segment in Untruncate, so Mbugwe's
vowel-fusion slots were treated as impure and the look-ahead stopped
testing behind them. PR #579 (stacked on #576) makes the purity test
exact against Untruncate. Row 48 of the ledger and a section in the
2026-10-27 round record the result: Mbugwe events plus synthesis steps
8.19% less -> 40.85% less than master, unification 1.83% less ->
22.55% less, matcher 7.65% less -> 30.47% less. Parses are equal to
master; shadow shows 0 violations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@johnml1135
johnml1135 requested a review from jtmaxwell3 October 28, 2026 18:08
johnml1135 added a commit that referenced this pull request Oct 29, 2026
…for the impure-slot look-ahead stacked on #576.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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