Skip to content

C++ search#210

Draft
ms609 wants to merge 918 commits into
mainfrom
cpp-search
Draft

C++ search#210
ms609 wants to merge 918 commits into
mainfrom
cpp-search

Conversation

@ms609

@ms609 ms609 commented Mar 19, 2026

Copy link
Copy Markdown
Owner
  • other optimizations + features

Manual testing underway; shiny app in particular has some usability issues.

@ms609
ms609 marked this pull request as draft March 25, 2026 14:21
ms609 added a commit that referenced this pull request Mar 28, 2026
ms609 added a commit that referenced this pull request May 18, 2026
In R CMD check, R runs as a non-interactive subprocess with captured
stdout. R_FlushConsole() calls fflush() on that pipe; when the buffer
fills the call blocks indefinitely, causing the 6 h GHA timeout seen
on every ubuntu runner for PR #210.

Gate the \r-overwrite progress line and the flush behind R_Interactive
(FALSE in batch/check contexts). Interactive sessions are unchanged.
At verbosity >= 2 in batch mode, emit plain \n-terminated lines so
diagnostic logs still carry progress detail without the flush risk.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ms609 added a commit that referenced this pull request May 18, 2026
In R CMD check, R runs as a non-interactive subprocess with captured
stdout. R_FlushConsole() calls fflush() on that pipe; when the buffer
fills the call blocks indefinitely, causing the 6 h GHA timeout seen
on every ubuntu runner for PR #210.

Gate the \r-overwrite progress line and the flush behind R_Interactive
(FALSE in batch/check contexts). Interactive sessions are unchanged.
At verbosity >= 2 in batch mode, emit plain \n-terminated lines so
diagnostic logs still carry progress detail without the flush risk.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ms609 and others added 23 commits June 8, 2026 07:59
WideSample() now selects topologically spread trees by solving the Max-Min
Diversity Problem with the MaxMin package, dispatching by a quality tier
auto-chosen from length(trees):

- exact node-packing IP (highs) for very small sets,
- DropAdd-TS while the dense distance matrix is affordable,
- matrix-free anchored Gonzalez (on-demand distance-column oracle) beyond
  that, so the largest tree sets never materialise an N x N matrix.

- Imports: MaxMin (Remotes: ms609/MaxMin) instead of FurthestPoint; the
  GonzalezColumn / DropAddTS / ExactMaxMin calls dispatch to MaxMin.
- Add Porumbel (2011) and Sayyady & Fathi (2016) references.
- dev/smoke_40k.R: 40,000-tree matrix-free smoke test.
- test-WideSample: 16/16 green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Until available on CRAN
Resize n0/n1 only when a new character has more states than current
buffer capacity (once per character rather than once per taxon × split).
Also compute max_state in the existing char-column pass (no extra loop).

Adds dev/benchmarks/bench_quartet_concordance.R for timing at
small/medium/large scales and profvis flame-graph support.

Benchmarks (Windows, R 4.5): small (<1ms), medium (14ms), large (186ms)
per call at 25/100/300 taxa respectively.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…n test (#247)

* T-303: document css_search HSJ/XFORM safety; add sectorial HSJ regression test

build_reduced_dataset() omits hierarchy_blocks/tip_labels/n_orig_chars/
hsj_alpha/sankoff_* fields, so rss_search/xss_search are already guarded to
fall back under HSJ/XFORM (commit e5ff294, same class as the T-275 guard).

css_search needs no guard: it never builds a reduced dataset — it runs
tbr_search() with a sector_mask against the full ds, so score_tree() dispatches
hsj_score()/Sankoff with complete data and the sector-internal heuristic is
correct for every scoring mode. Documented this with an in-code comment so it
is not re-flagged.

Approach (b) (copy the fields into the reduced dataset) is not tractable: the
HTU pseudo-tip is a Fitch from_above state-set with no valid HSJ tip_labels or
Sankoff tip_costs representation, so the reduced dataset cannot be made correct
for those modes without new from-above machinery in both scoring kernels.

Adds a Tier-2 regression test driving the full HSJ + sectorial pipeline
(rss/xss guarded, css on full ds) and asserting it completes, stays
self-consistent, and is deterministic across identical-seed runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* paramrename

* -progress

* Suggest `highs` for MaxMin solver

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
ms609 and others added 30 commits July 16, 2026 06:49
…rift/sector opt-in + findings (#273)

* docs(profiling): settle union-of-finals exactness gate (empirical)

Fresh rooted union-of-finals is NOT an exact EW-Fitch insertion cost, under
any rooting or on binary data. Three-way empirical verdict over 6066
reconnection candidates vs TreeLength full-rescore ground truth:

- Per-child edge set E(D)=combine(prelim(D),up(D)): EXACT (6066/6066).
- Union of TRUE edge sets E(A)|E(D): sound LOWER bound (under-counts, never exact).
- Union of the DEPLOYED simplified final_ (fitch_indirect_length): OVER-counts
  (unsafe as a lower bound), because uppass_node's simplified rule never unions
  so final_ is a strict subset of prelim. Fresh finals => formula limitation,
  not staleness. The code comment claiming it under-counts is wrong for the
  deployed path.

=> "root like TNT -> cheap exact union-of-finals" refactor is a mirage.

Adds dev/profiling/exactness-gate.md (verdict) + exactness-gate.R (harness).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(drift,spr): opt-in exact directional EW scorer + scan-error census

The DRIFT phase (ts_drift.cpp) and standalone spr_search (ts_search.cpp) still
scored pure-EW reconnection candidates with the union-of-finals approximation
(fitch_indirect_length_bounded, reading the incrementally-maintained
tree.final_), never migrated to the exact directional edge set that main TBR
adopted in 2b299e4.

Severity (dev/profiling/drift-exactness-gate.md): no inexact score is ever
leaked (every accept is drift_full_rescore'd / full_rescore-verified), but on
the deployed incremental path the union error is TWO-SIDED (over- AND
under-counts up to ~100 steps, not the strict over-count the exactness gate
measured with fresh finals). argmin selection then suffers an optimizer's curse
(picks moves the scan is optimistic about), so drift admits moves outside its
AFD envelope (env_violation 52-62% on Zhu2013/Zanol2014) and spr_search
converges prematurely (union 810 vs exact 639).

Fix (opt-in, union stays default; NA/IW untouched, gate !has_na && !use_iw):
- TS_DRIFT_EXACT routes drift's SPR + reroot EW scans to
  fitch_indirect_length_cached over a per-clip compute_insertion_edge_sets,
  mirroring tbr_search.
- TS_SPR_EXACT does the same for spr_search.
- TS_DRIFT_SCANCHK: two-sided scan-error + decision census (select_flip,
  env_violation, applied-move predicted-vs-full_rescore; cf. TS_IW_SCANCHK).

Validation: exact path applied_mismatch == 0 across all applied moves incl.
reroots; default (flags-off) path byte-identical (drift/SPR/char-ordering tests
pass). Local matched-wall pilot: frontier CROSSOVER -- exact wins the auto/
wall-race regime on all 3, union wins the thorough/saturated regime on 2/3 ->
land opt-in, do not flip the default. spr_search is an unambiguous reach win.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ci(hamilton): drift-exactness matched-wall gate build + array scripts

Dedicated build (lib-driftexact, does not clobber the shared cpp-search lib)
fetching claude/happy-jennings-e7a165, and a 3-task array (one per dataset)
running dev/profiling/drift-exactness-gate-bench.R at 30 seeds x nCycles
{8,16,32,64,128,256}, union vs TS_DRIFT_EXACT, to resolve the matched-wall
plateau-vs-climb crossover the local pilot showed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ci(hamilton): resolve gate deps from shared lib (R_LIBS_USER)

lib-driftexact holds only the branch's TreeSearch; point R_LIBS_USER at the
shared TreeSearch/lib so Rcpp/TreeTools/ape resolve at build+run time while the
harness loads our TreeSearch via lib.loc=GATE_LIB.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(drift): close matched-wall gate with 30-seed Hamilton result

Hamilton array (17870087, 30 seeds x nCycles {8..256}, 3 datasets) confirms the
8-seed pilot's matched-wall CROSSOVER with clean monotone gaps: exact wins the
auto/wall-race regime on all three (~2.7-3x faster, no time-to-optimum
regression), union wins the thorough/saturated regime on 2/3 (Zhu2013, Zanol2014
- exact plateaus, union over-diversifies deeper); Dikow2009 exact dominates every
budget. best-of-30 equal on all three (reliability effect, not reachability).

Verdict: LAND OPT-IN (TS_DRIFT_EXACT), do not flip the drift default. Adds the
per-dataset raw CSVs and the frontier reanalysis script.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(drift): soften saturated-tail claim (nCycles-cap censoring)

The nc<=256 gate compared a censored exact tail (exact ~3x faster, so frozen at
its nc256 score with unspent wall) against union's fuller curve; exact was still
descending nc128->256, not plateaued. So "union wins thorough on 2/3" is not
established. auto/wall-race win stands. Extension (exact+union nc {512,1024} on
Zhu2013+Zanol2014) running to settle at matched wall. Opt-in landing unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(drift): resolve saturated tail — crossover is real (nc1024 check)

Extended exact+union to nc {512,1024} on Zhu2013/Zanol2014 (30 seeds) to remove
the nCycles-cap censoring. Exact does NOT plateau (Zhu 624.80->624.40) but
descends slower than union; at genuinely matched wall union stays ahead through
the saturated tail (Zhu floor 624.0 vs exact ~624.4; Zanol narrower ~0.13). So
the auto/thorough crossover is real, not an artifact: exact wins the wall-race
regime on all three, union wins the thorough regime on 2/3.

Verdict CLOSED: land TS_DRIFT_EXACT opt-in, do not flip the drift default. Adds
the extension CSVs and the combined-frontier reanalysis script.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(bench): sector-godrift isolation gate harness + Hamilton array (route B)

Full "thorough" search with driftCycles=0 (main-tree drift off; verified gd=0 =>
0 drift calls, gd=25 => all-sectoral) isolates the in-sector godrift scorer.
Three arms none/union/exact, maxReplicates knob, matched-wall frontier. Array
adds mbank_X30754 (180t) as the larger/harder case where sector drift is most
active. Reuses lib-driftexact (no rebuild).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(spr): make exact directional the default EW scorer for spr_search

spr_search is a pure hill-climb with no exact-TBR phase to launder union's
over-count, so union converges catastrophically short (multistart best-of-25:
Zhu2013 650 vs exact 625; Zanol2014 1292 vs 1265 — systematic, not variance),
and exact is also faster per start. No crossover, unlike drift. Flip default to
exact; TS_SPR_UNION restores the old scorer (kill switch). Tests green
(SPR/spr-nni-opt/spr-state-restore/drift). Reach today = standalone
ts_spr_search (sprFirst OFF in presets), but this makes an SPR warmup viable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(bench): route-3 marginal-value experiment (drift vs ratchet at matched wall)

From a shared per-seed local optimum, compare where a wall-second is best spent:
incumbent ratchet vs exact-drift vs union-drift (control). If exact-drift's
anytime frontier beats ratchet's over some wall range and dominates union-drift,
drift earns a place in wall-limited recipes (route 3) -> worth re-tuning.
mbank_X30754 (180t) is the large/hard anchor. Reuses lib-driftexact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(bench): route-3 follow-ons — +replicate gap-closer + training-corpus recipe test

arm A (+replicate): is a wall-second better on exact-drift than on the project's
real incumbent lever, more RAS+TBR replicates? Same datasets as route3 for
frontier comparison.
arm B' (recipe): whole-search thorough on TRAINING-split MorphoBank (EW-recoded,
one key/task, sector drift off), arms none/union/exact main-tree drift, to see
if the drift scorer choice moves a wall-limited recipe. Both reuse lib-driftexact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(drift): route-3 recipe verdict — NO-GO (marginal win, ensemble-redundant)

Marginal value (from a shared local-opt): exact-drift is the best single
continuation, beating ratchet + union-drift + replicate. But the whole-search
recipe test on 8 training-split MorphoBank keys (EW, sector-drift off,
sequestered) shows main-tree drift is REDUNDANT in the multistart+ratchet+
sectorial+TBR ensemble at both saturated (none ties + faster) and tight-budget
ends (exact beats none only 1/4 keys). So the isolated win doesn't survive ->
no recipe surgery; exact-drift stays opt-in (TS_DRIFT_EXACT), D preset-refactor
not worth it. Adds companion note + raw CSVs + analysis. (spr_search exact
default stands as the real landed win from this line.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
… Scheme 1)

Exact incremental replacement for the per-clip from-scratch
compute_insertion_edge_sets recompute, gated to the plain directional EW/IW
TBR regime (TS_L3B_INCREMENTAL, default OFF). Patch-from-full "Scheme 1":
maintain a pristine per-pass intact-tree base; per clip patch only the changed
frontier (worklist incremental up-pass, value-based attenuation stop), score,
then restore base over the changed-node list. Shares ts_fitch_combine with the
from-scratch path so patched words are byte-identical.

Chose Scheme 1 over the design doc's Euler "Config C": the 482t evidence that
reopened the lever (fp_ref) supports Scheme 1; the Euler descend-delta was
killed at small N and never re-measured; the clip loop already restores per
clip (Scheme-1-native) and keeps the shuffle => bit-identical C-vs-A. Advisor
concurred. Rationale + results in dev/profiling/l3b-land.md.

Correctness: per-clip oracle (edge_set == from-scratch, full array) mismatch=0
across random EW (64 configs), real multi-state/ambiguous panel + project5432
subsamples, EW+IW, single-rooting and do_reroot; C-vs-A bit-identical; tbr
completeness oracles clean. do_reroot supported (not a footprint penalty).

Wall (raw tbr_search, real project5432, bit-identical trajectory): wash at
<=75t, 1.10x@120t, 1.39x@240t, 1.36-1.38x@482t (both unrooted) — the predicted
large-N ceiling. Open: end-to-end MaximizeParsimony at shipped ratchet on
Hamilton (sector share is gated out, so end-to-end is diluted).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Full MaximizeParsimony C-vs-A on project5432 (EW): 1.01x wash at both 120t and
240t, despite raw tbr_search being 1.39x at 240t. Coverage confirmed (l3b fires
in driven+ratchet; sector ~30% gated out) and the per-clip oracle is clean in
the ratchet upweight/collapsed context. The raw-TBR win is diluted to nothing
end-to-end by the sector exclusion + per-pass base-recompute amortization across
ratchet's short passes. Verdict: exact/correct large-N raw-TBR lever, NOT a net
mission-wall lever as built -> land default-OFF; the unlock is incremental base
update after accept. Replaces the earlier ~1.2x end-to-end estimate with measured
numbers; marks the Hamilton 482t full-search run not-worth-running until the
base-update follow-on lands.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ran the cheap discriminator (MP 240t, TS_L3B_STATS+ORACLE): in-search fp_changed
= 0.244, essentially the clean 240t footprint (0.243) -> the per-clip patch win is
INTACT, refuting "in-search fp elevated -> win structurally small". So the wash is
sector-exclusion (predicts ~1.24x) + the per-pass O(n_node) base recompute, which
MP's short ratchet passes amortize poorly. Confirms base-incremental-update as the
plausible unlock; notes it is itself a correctness-critical kernel (TBR-accept, two
dirty paths + reroot, harder than the clip patch).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… honest verdict

Replaces the per-pass full compute_insertion_edge_sets base recompute with an
O(footprint) in-place update after each accepted SPR move
(update_base_after_spr_move): the move decomposes into a remove-leg at nz + a
single-site insert-leg at above; nx gets a fresh up so A1-path up is recomputed
(not assumed invariant). Gated SPR-only (TBR-reroot/reroot/first-pass fall back
to full recompute); pass-top recompute skipped when l3b_base_current; buffers
synced over the changed set; kill switch TS_L3B_NOBASEINCR. Base oracle
(base == from-scratch after each SPR accept) clean across EW+IW tbr_oracle
(to 120t x18) and full ratchet MaximizeParsimony on real Zhu2013/Zanol2014;
first-version root-child seed bug caught by the oracle and fixed.

Measured (project5432, EW): base-incr removes the per-pass recompute, converting
plain-l3b's small MP regression (0.99x @240t) into a clean wash (1.00x), and
preserves raw tbr_search 1.39x@482t / 0.97x@37t. But it yields NO net MP win =>
the base recompute was NOT the bottleneck. The MP wash is structural: short
ratchet-recovery passes (few clips/pass) + sector ~30% excluded. Verdict
unchanged and now complete: exact large-N raw-TBR lever, not a mission-wall
lever; land default-OFF. STOP (advisor gate: fixed cost removed, still no win).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The L3b incremental edge-set patch is EXACT (bit-identical trajectory) and
1.36-1.38x faster on raw large-N TBR (dev/profiling/l3b-land.md); end-to-end
MaximizeParsimony is a clean wash (neutral, with the base-incremental follow-on).
Per objective (ii) be-as-fast-as-possible, take the free large-N win: default-ON
for n_tip>=150; small trees (where the raw-TBR win washes out) pay zero overhead.

Gate: l3b_active = !TS_L3B_OFF && (TS_L3B_INCREMENTAL || n_tip>=150) && use_directional && ...
  TS_L3B_OFF          kill-switch (full disable)
  TS_L3B_INCREMENTAL  force-on regardless of size (A/B + small-tree testing)

Verified on the merged+flipped build (CCACHE_DISABLE --preclean; header changed):
force-ON == force-OFF byte-identical on the panel (Zhu/Zanol x2 seeds:
score+n_evaluated+passes), l3b fires (patch_clips=3909), n_tip<150 default inert
(== pre-#6 #7 baseline). The n_tip>=150 exact path was validated at 482t by the
chip's per-clip oracle + C-vs-A.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The single-seed assertion (seed 1001) that zero-mode and upweight-mode
ratchet produce different search statistics coincidentally held equal on
macOS after d8c5999 (homoplasy character ordering) shifted the RNG-seeded
trajectory under libc++'s std::sort tie-break. On a 15x10 binary matrix the
two modes coincide on ~3-7% of seeds; which seeds coincide is platform
dependent, so a single fixed seed is fragile.

Assert instead that the modes diverge on >=1 of several seeds (early break),
so an incidental single-seed coincidence no longer fails while a genuine
collapse (upweight silently == zero) still does. The deterministic
regime-distinctness guarantee is already covered by test-ts-na-evcache.R.

Validated green on a clean committed-HEAD build (CCACHE_DISABLE=1 --preclean):
all 9 blocks pass, no crash.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…liverable

ratchetPerturbMaxMoves 5->auto/scaled corpus anytime A/B (Hamilton job
17872154, 25 training matrices x 3 arms, validation sequestered) => NO broad
wall benefit: 14/25 tie exactly; clean large/xlarge reach the optimum in the
same replicate (rep2hit ratio 1.000); narrow reach regression on a degenerate
4062t giant; marginal noisy +reach on one hard multi-basin matrix. Verdict:
keep the default at 5; the kick is an opt-in hard-tail lever, not a default.

Rewrite dev/plans/2026-07-11-...: retire the FALSIFIED clade-generation thesis
(restore238 shows TS's TBR rebuilds 1943 from within ~8-12 TBR moves), reframe
to cold-start basin capture, correct the numbers, fold in the kick result.

Adds kick_anytime.R (harness), kick_anytime_hamilton.sh (SLURM array),
kick_analyze.R (analysis), kick_anytime_FINDINGS.md (write-up).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…from 1943)

Mission A angle #2 (push the reweighting kick as a basin-HOPPING schedule to
cross the last steps past the ~1945 TS floor on project5432).

Mechanism: src/ts_ratchet.cpp is a MONOTONE strict-descent ILS (accept-only-if-
strictly-better, else reset-to-best), so strength/schedule/multi-kick all stay
inside a monotone frame; the only escape lever is the acceptance frame
(accept-worse: drift, or reweighting with accept-worse between cycles).

Gate (buildless, dev/benchmarks/basin_hop_gate.R): ordered-char re-score by label
(self-check L(1943 floor)==1943) + TBRDist(exact=FALSE, hard lower bounds) to the
1943 floor. Every near-optimal TS tree from EVERY generation method (kick 1945 /
strongperturb / cold / random / largefirst / invweight, 1945-1958) is tbr_min >=97
TBR from 1943 (min 97, best kick only 2 score-steps above 1943). Corroborated by
basin_5432 (descent 0/5 by d~20) and geom_full (~109). No accept-worse schedule
walks ~100 rearrangements to a narrow distant basin, so the afd-sweep probe and the
accept-worse build were NOT run.

Scope: refutes route #2 only (escape the floor). Angle #1 (cold-start constructor)
and #3 (structured breadth) are untouched - they generate a start rather than walk
from a floor. Kick is not useless (CID 0.22 vs cold 0.38: a better LOCAL basin).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…gating [focus-test: ts-ratchet-opt]

R-CMD-check gates the macOS `full` job behind the ubuntu/windows `core` wave
(full needs core needs sense-check), so a flake elsewhere stops the macOS run
from ever building. focus-test runs ONE testthat file on ONE OS (default
macOS-latest) with no full R CMD check -- via the Actions tab (workflow_dispatch)
or a [focus-test] / [focus-test: <filter>] marker in a commit message.

This commit's own [focus-test: ts-ratchet-opt] marker triggers a macOS run of
the de-flaked ratchet test as a live check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Auto-memory is now organised as campaign hubs indexed at the top of MEMORY.md; record the write-side rule (advance a campaign by updating its hub the same turn) on the always-loaded project-instructions channel so future agents follow it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…o diameter build)

Zero-code precondition for a TNT-style dss (diameter-limited local sector) build.
The dss benefit (local sector -> cheaper escape) is driven by TIP-COUNT, which the
existing sectorMaxSize knob controls; rev5's missiongate sweep never varied it small
(held maxS>=0.65n). This probe sweeps small maxS {12,20,30} x ras=3 from the fixed
TNT T0, vs a large-sector anchor and ratchet, on the missiongate protocol.

Local pilot (Zanol s1, Zhu s1, n=3) + Zanol reach test (n=16 restarts, up to r=256):
- steps/Mcand APPEARS to favour small sectors (-2.4 vs ratchet -1.5) but this is a
  candidate-COUNT artifact: small-sector candidates cost ~2x ratchet's per wall
  (per-pick reduced-dataset build + 3x RAS overhead), so on the WALL axis (the true
  mission axis) ratchet dominates the hard target (-29 vs -24 steps/s, reaches deeper).
- REACH CEILING: small sectors PLATEAU at -12 on Zanol at ANY budget; ratchet reaches
  the -13 target. Whole-tree-barrier prior (rev2): a sector <= maxS tips cannot emit
  the split(s) for the -13 reorganization.

=> dss/diameter-limiting is strictly MORE local -> shallower reach -> cannot help.
No C++ build. Confirms + extends rev5 (sectorial Pareto-dominated by ratchet) to the
small-maxS regime it never measured. Hamilton n=15 confirmation scripts included.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-a source dir gone on Hamilton)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…losed

Hamilton array 17898332 (build 17898331, HEAD 023b038), 12 cells x n=15
(3 TNT-T0 seeds x 5 reps), corrected wall+reach aggregate.

VERDICT 0/4: no small-sector config reaches ratchet's median depth at <= its wall.
On the hard Zanol target ratchet dominates EVERY axis incl the biased steps/Mcand
(-1.90 vs best smallmax -1.78). The n=3 local-pilot "lead" (maxS=12 r=1 = -2.39
st/Mcand; a Zhu wall win) was noise + metric bias: at n=15 it falls to -1.19 and the
Zhu win vanishes (ratchet med -3 vs smallmax -1).

steps/Mcand is a BIASED axis here: small-sector candidates cost ~2x ratchet's per
wall (reduced-dataset build + 3x RAS overhead over fewer candidates), so it flatters
small sectors. Adjudicated on WALL + reach instead.

=> dss/diameter-limiting is strictly MORE local -> shallower/less-competitive ->
cannot help. No C++ build. Confirms + EXTENDS rev5 (sectorial Pareto-dominated by
ratchet) to the small-maxS regime rev5 never measured. Also refutes rev5's handoff
nuance (small maxS + ras=3 vs single ratchet start on Zhu/Giles): smallmax median
<= ratchet median on all cells. Full writeup: DSS_FINDINGS.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… retested

The selectem_large anchor used auto picks (~3/round), under-provisioning rev5's
selectem (explicit pk 5-20): my Zhu selectem_large median -1 vs rev5's -6. So it is
NOT a faithful rev5 reproduction and cannot speak to rev5's handoff (large-band selectem
being lower-VARIANCE than a single ratchet start on Zhu/Giles — a variance question never
measured here). Retract "refutes rev5's handoff"; that stays OPEN as rev5 left it.

Core verdict UNAFFECTED: the smallmax arm used auto picks too, but for small bands that
gives MANY picks (~16/round at maxS=12) = over-provisioned, so its 0/4 loss vs ratchet
(esc_med-vs-ratchet-at-wall, same pooled seeds) is decisive. Also de-emphasised the
cross-seed-pooled reach flag as non-load-bearing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
TS_PACK_LOCAL packs each 64-char block to its own applicable-state alphabet
rather than the global one, cutting Fitch planes scanned per candidate on
low-state morphological data (~1.1x small -> ~1.5x char-rich, climbing with
nChar). Score-EXACT per tree; now default-ON (set TS_PACK_LOCAL=0 to force off).

- ts_data.cpp/.h: default-ON flip; add CharBlock::plane_state[] mapping each
  packed plane back to its global applicable label (identity in the unpacked
  layout), populated in build_dataset by inverting loc[] through state_remap.
- ts_rcpp.cpp: ts_na_debug_char reconstruction consumes plane_state[] so packed
  output prints the global state label, not the plane index.
- test-ts-pack-local.R (new): score-exactness (TreeLength ON vs OFF) + a
  hand-built non-contiguous-alphabet fixture that exercises plane_state[] -- the
  only gate that does, since the scorer is index-agnostic.
- reeval pack_{gate,reach,mpt_gate}.R: OFF-arm switched to TS_PACK_LOCAL=0
  (unset now means ON).

Validation: pack_mpt_gate Sansom2010 EW/IW/NA byte-identical ON vs OFF (score +
ntree + candidates_evaluated, 3 seeds; NA reproduced the 66-tree MPT set); full
testthat default-ON 0 failures (11337 pass); corpus 18/18 reach-neutral.
ts_na_debug_char is package-internal (no exported surface changed) and
PlotCharacter is pure-R, so no user-visible reconstruction changes. The packed
ambiguous-set content differs (local vs global alphabet) -- correct, not a
regression: phantom states never appear in an MPR, proven by identical
score+candidates_evaluated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Give the 4-wide flat reroot batch kernels (fitch_indirect_cached_flat_x4 /
indirect_iw_cached_flat_x4 + NA) the same next-row software prefetch the scalar
reroot path already has. The x4 batches issued 4 cold demand loads to scattered
vroot_cache rows per batch with no hint; the in-batch MLP can't hide the
batch-boundary head latency (cutoff early-break is a control dependency).

EW x4 prefetches kept_ei[ki..ki+3] (next batch). IW x4 tracks a parallel iw_pf
cursor into kept_ei -- valid because its skip predicate is identical to the
kept_ei construction (use_collapsed == !collapsed.empty(), ts_tbr.cpp:1532), so
kept_ei is the same candidate sequence in the same order. Purely additive and
flag-gated (opt-in TS_TBR_PREFETCH=1), so flag-OFF is byte-identical to prior
committed code by construction; a prefetch hint cannot change a score anyway.

Byte-identity validated: ts_tbr_diagnostics ON vs OFF on project970, EW + IW,
identical score + n_candidates_evaluated (38.9M/44.8M) + n_clips + final edge.
Local Windows/MinGW A/B flat (project970 60.0->60.2, project510 57.1->57.0, no
regression) -- expected, since the memory-latency win only surfaces in the EPYC
L3-pressure regime. Landed default-OFF as a validated building block per the
lever-6 precedent; the win/wash is settled on Hamilton EPYC raw-TBR + full
MaximizeParsimony wall before any default flip. See dev/profiling/kernel-prefetch-x4.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the +1 slack on the EW/NA integer bail cutoff (best_candidate - divided_length
+ 1 -> + 0) at the EW sites: spr_scan_plain_ew template, SPR general, reroot x4,
reroot scalar. The scorers bail on extra_steps >= cutoff; the +1 let an
equal-length, non-improving candidate accumulate its full extra before rejection
(scanning every block on the near-optimal plateau), whereas +0 bails it the moment
its running extra reaches the current minimum.

Byte-identical: acceptance is strict < everywhere, so a bailed candidate (extra >=
cutoff = best-divided) is rejected identically, a true improver never reaches the
tighter cutoff, and an equal candidate bails one block earlier but is still
rejected. The +1 was not protecting acceptEqual (decided post-scan on the recorded
best_candidate), and the drift path already ships slack 0. Gated by a cached
tbr_cutoff_slack() (default 0; TS_TBR_LOOSE_CUTOFF=1 reverts to the historical +1
for the A/B). Left the +1 on spr_scan_plain_iw (cutoff dead on the IW path -- IW
bounds on the float best_candidate), the IW-float cutoff, and the INT_MAX init.

Validated byte-identical (score + n_candidates_evaluated + n_clips + final edge)
tight vs loose on project970 EW + IW + Sansom2010 NA. Chosen by the post-packing
lever-hunt workflow with 3/3 adversarial verifiers confirming exactness + non-wash.
Local ns A/B is flat (blocks-scanned is hardware-independent, so representative):
the exact-equal-full-scan population is small in practice (dedup skips equivalent
rerootings; worse candidates already bail early). Landed default-on regardless --
byte-identical, guaranteed non-negative, the correct tighter bound; may pay in the
raw-TBR @482t plateau regime, to be checked on the EPYC A/B. See dev/profiling/kernel-cutoff-tighten.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ened it)

Whole-program -mavx2 is 8-9% faster per candidate than the package -msse2 build
(project970 60.6->55.2, project2668 34.7->31.9) because the target(avx2) reduce
helper is a non-inlined call boundary and packing (ns 2-4) made that a big fraction
of the block body. Pre-packing this was -1.2% (T-P5f); packing reopened it.

The portable source capture (target(avx2) scorer clone + cpu_has_avx2 dispatch) was
flat locally and reverted: cpu_has_avx2() is almost certainly false on this
MinGW/Windows box (__builtin_cpu_supports unreliable there), so the shipped Windows
binary likely runs SSE2 -- a separate portable win. On Linux/EPYC the runtime check
works, so the baseline already uses the AVX2 helpers and the clone (boundary kill)
is the right lever there. Resolution deferred to a Hamilton EPYC -msse2-vs-mavx2 A/B
(after the 5432 packbuild job frees the lib). See dev/profiling/kernel-avx2-reduce-gate.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…0% end-to-end

EPYC A/B (job 17908514): the shipped -msse2 build pays a per-block AVX2-call
boundary (cpu_has_avx2 true on Linux, but the target(avx2) helper can't inline
into the -msse2 scorer). -mavx2 removes it: 1.67x/1.38x/1.26x per candidate on
project970/510/2668, scaling with block count. Windows masked this (cpu_has_avx2
false there -> inline SSE2 -> gap looked like 9%).

But end-to-end (job 17908602, fixed-seed maxReplicates=4, identical trajectory):
project970 1.01x, project510 1.10x. Amdahl: scoring is only ~3% (970) / ~34%
(510) of MaximizeParsimony wall; the rest is non-scoring machinery (ratchet/
sectorial/tree-ops). The per-candidate win does not propagate -- same dilution
that sank lever-6, and why packing (shrinks total_words pervasively) beat AVX2
(reduce compute only).

Banked as a -march=native Hamilton deployment build (dev/profiling/tsmarch_build.sh
-> /nobackup/pjjg18/tsmarch/lib): free ~1-10% for EPYC runs, machine-specific,
NOT for CRAN (breaks non-AVX2 CPUs; release keeps -msse2 + runtime dispatch). The
portable target(avx2) clone is not worth it (small, dataset-dependent end-to-end).
Confirms the mission: time-to-optimum is bounded by search machinery/stopping,
not the per-move kernel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t=off

Verified the -march=native deployment lib gives byte-identical EW+IW scores vs
-msse2 (project970, 8 trees, IW k=10, 12dp). Add -ffp-contract=off so byte-identity
is guaranteed (not just empirical): keeps the AVX2-inline win (boundary-kill, not
FMA) but forbids FMA fusion of the IW/profile float accumulators, which could
otherwise shift IW ties -> trajectory drift in a race. tsmarch/lib is thus a
transparent speedup, safe for IW-tie-sensitive race runs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…full-breadth control

Expands the @param targetHits one-liner into explicit guidance for the two goals a user brings to a search: a small value for a single tree one can be reasonably confident is an MPT, a large value + high maxReplicates to represent the full range of MPTs. States the mechanism (targetHits is shared by every strategy preset; presets differ in per-replicate effort, not stop policy) and the honest caveat that full-breadth completeness cannot be auto-detected. Regenerates man/MaximizeParsimony.Rd. Outcome of the Lever-B over-search closure.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…stop curves)

Hamilton harnesses behind the Lever-B closure: lever_b_curve.R captures the per-rep pool-vs-rep breadth curve (uncapped pool + never-stop = distinct-MPT count); lever_b_{stop,disc,regime}.sh drive the mode-(i) targetHits sweep, the discovery-rate probe, and the mode-(ii) intermediate-regime probe. Referenced by the lever-b-oversearch memory.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Goloboff-1996 union-construct screen refutation: lower-bound proof note (unsound with collapsed uppass finals) and the gate script that exercises it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Kernel speed-baseline / reslice-plan / goloboff-gates notes plus the disc_nstates and corpus_state_heterogeneity characterisation scripts (+ small .rds caches). The TS_AUDIT_PROBE bail-depth instrumentation from the same original commit is intentionally OMITTED here: origin/cpp-search rewrote ts_fitch.cpp (+264 lines, spr-drift/L3b) so the probe's call-sites no longer map; it remains preserved on branch claude/mission-b-kernel-goloboff-6f5c0e for a future manual port if needed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…#2/#3) + harnesses

project5432 Mission A: all cold-start-basin-capture angles refuted by measurement.
- #1 constructor CLOSED five ways: per-split detectability (anti-detect), absolute
  synapomorphy count (chance), joint clique recovery (36th pctile, count-matched),
  scaffold-sufficiency (all-deep-fixed 1/12), clique-START routability (0/60, worse
  than RAS), clique-proportional reweight (~uniform, wrong-skew).
- #2 basin-hop schedule CLOSED (all near-optimal TS trees >=97 TBR from 1943).
- #3 structured breadth CLOSED (random resample lands farther, 0/400).
Capstone: the 1943 deep backbone is an emergent aggregate (~4/183 clean-supporting
chars), so no per-char/compatibility/reweight start-generator can bias toward it;
reach is search-side only (not a hard limit -- TNT ~1/3). Adds dev/benchmarks harnesses.
Updates the deliverable's open-angles + status so the next 1939 phase does not re-run these.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e skips them

The mission brief still listed constructor/schedule/breadth as 'none yet refuted' — the
exact re-investigation trap. Now points to the closure record + capstone (emergent-aggregate
backbone => search-side only, not a constructor).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t-in)

Recovered from a stale GitHub Desktop auto-stash. POOL_RESEED
(TS_POOL_RESEED) reseeds a fraction of replicates from a retained
best-score pool tree instead of a fresh Wagner start, giving the
diverse equal-length pool its own sectorial re-solve pass. Terminal
fuse (TS_TERMINAL_FUSE=1) runs a final fuse over a few best-score
anchors after the loop converges, recombining complementary clades
the in-loop fuse never reached. Both default-off; deployed behaviour
is byte-identical. Also carries dev/profiling driver scripts used
during that exploration and a stray citation-syntax fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…result

Commit the score-verified best-known artifacts for the Mission-A epitome
instance project5432, so they can never go missing again (the lesson of the
retracted "1939" trace phantom):

- dev/benchmarks/hardtail/project5432_best_1942_tnt.tre  (1942, verified)
- dev/benchmarks/hardtail/project5432_ts_reach_1943.tre  (1943, verified)
- dev/benchmarks/hardtail/README.md  (regime, provenance, config, re-run footnote)

Result (Hamilton array 17943743, 16 seeds x ~48h): TreeSearch's aggressive
perturbation swarm reaches 1943 (6/16 seeds) — the new best TS-generated score,
one step below the prior TS-side best of 1944. Recorded observationally:
block-1 cold still floors ~1945-1947; sustained iteration under the perturbation
stack descends to 1943.

Reframing: 1943 is a SHARED TS/TNT floor (28h heavy TNT also floored at 1943
across all seeds; 1942 = one lucky regen; 1939 = phantom). The residual
1943->1942 gap is a rarity/luck-and-speed gap, not a TS pathology. Updates the
Mission-A plan doc's dated result-note + artifact pointers; CLOSED sections
left intact. Swarm ran on Hamilton lib TreeSearch 2.0.0 (lags cpp-search);
a current-engine re-run to settle whether TS reaches 1942 is held, not launched.

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

1 participant