From f13ea4165bd1724bf94290dc4404cd289f8ca1e0 Mon Sep 17 00:00:00 2001 From: kaghi Date: Wed, 8 Jul 2026 15:49:46 -0700 Subject: [PATCH] =?UTF-8?q?max(peak-time,=20FWHM)=20shows=20inconsistences?= =?UTF-8?q?=20in=20iterations=20it=20stops=20at=20with=20some=20issues=20w?= =?UTF-8?q?ith=20spike=20inference=20quality.=20Instead,=20testing=20FWHM?= =?UTF-8?q?=20alone=20as=20a=20stop=20criterion=20leads=20to=20consistence?= =?UTF-8?q?=20convergence=20of=20CaDecon=20around=207=E2=80=9312=20iters?= =?UTF-8?q?=20with=20same=20or=20greater=20spike=20inference=20quality=20a?= =?UTF-8?q?nd=20tighter=20variance=20across=20multiple=20seeds.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cadecon/src/lib/iteration-manager.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/apps/cadecon/src/lib/iteration-manager.ts b/apps/cadecon/src/lib/iteration-manager.ts index 8c6a30d..e7fa266 100644 --- a/apps/cadecon/src/lib/iteration-manager.ts +++ b/apps/cadecon/src/lib/iteration-manager.ts @@ -758,13 +758,20 @@ export async function startRun(): Promise { tauR = median(tauRises); tauD = median(tauDecays); - // Convergence coordinate: kernel shape (peak time + FWHM). + // Convergence coordinate: kernel shape. Peak time (RTP) is rise-weighted and inherits + // tau_rise's persistent ~2-3%/iter jitter, so it never reliably settles; gating on it + // (max(dPeak,dFwhm) < convTol) stalls convergence — runs converge very late or hit + // maxIter. Convergence is therefore tested on FWHM alone (the decay-dominated width, + // which does settle), mirroring the tau_d-only rationale of the legacy stability net. + // shapeDelta (peak + FWHM) is still computed and exported as a dashboard diagnostic. const shape = tauToShape(tauR, tauD); - let shapeDelta: number | null = null; + let shapeDelta: number | null = null; // exported diagnostic: max(peak-time, FWHM) change + let fwhmDelta: number | null = null; // convergence test signal: FWHM change only if (shape && prevShape) { const dPeak = Math.abs(shape.tPeak - prevShape.tPeak) / (prevShape.tPeak + SHAPE_EPS); const dFwhm = Math.abs(shape.fwhm - prevShape.fwhm) / (prevShape.fwhm + SHAPE_EPS); shapeDelta = Math.max(dPeak, dFwhm); + fwhmDelta = dFwhm; } const riseUnresolved = tauR <= tauRiseFloor * RISE_FLOOR_MARGIN; if (shape) { @@ -821,11 +828,11 @@ export async function startRun(): Promise { }); }); - // Step 4: Convergence check in shape space. An iteration is "stable" when - // both peak time and FWHM change less than convTol; convergence is declared - // after `patience` consecutive stable iterations, once past `minIters`. A + // Step 4: Convergence check in shape space. An iteration is "stable" when the kernel + // FWHM changes less than convTol (peak time excluded — see note above); convergence is + // declared after `patience` consecutive stable iterations, once past `minIters`. A // degenerate (null) shape resets the streak — it can never count as stable. - if (shape && shapeDelta !== null && iter + 1 >= minIters && shapeDelta < convTol) { + if (shape && fwhmDelta !== null && iter + 1 >= minIters && fwhmDelta < convTol) { if (stableCount === 0) firstStableIter = iter + 1; stableCount++; } else {