Skip to content

fix(analysis): grade CSR/PB confidence on periodicity strength - #410

Merged
wpfleger96 merged 2 commits into
mainfrom
wpfleger96/fix-pattern-confidence-floor
Oct 10, 2026
Merged

wpfleger96 merged 2 commits into
mainfrom
wpfleger96/fix-pattern-confidence-floor

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

CSR and periodic-breathing confidence had a fixed floor. _find_dominant_cycle only searches autocorrelation lags inside the cycle range, so the "cycle length in range" bonus in _calculate_csr_confidence (+0.2) and _calculate_periodic_confidence (+0.1) was earned on every detection. CSR confidence never fell below 0.7 and periodic breathing never below 0.6, so that part of the score carried no information.

The bonus is replaced with a graded periodicity term, _periodicity_strength. It measures how far the autocorrelation peak that picked the dominant cycle clears the detection gate (autocorr_threshold), scaled to 0–1 and weighted 0.2 for CSR and 0.1 for periodic breathing on a 0.5 base. Two details:

  • Bias-corrected peak: the autocorrelation is the biased estimator, so even a perfect cycle peaks near (N - lag) / N, and long cycles would grade weaker than short ones. _find_dominant_cycle returns the peak rescaled by N / (N - lag) for grading. Detection still gates on the raw autocorrelation, so which windows are detected doesn't change.
  • Exact range: both scores are 0.5 + weight * periodicity_strength + bonus * earned, clipped to 1.0, so all bonuses earned gives exactly 1.0. A non-finite strength grades as 0.

Gating, cycle lengths, episode boundaries and merging are unchanged, so only confidence values move, and none go up. periodic_breathing_pct is computed from episode durations and doesn't change. On seeded synthetic waxing/waning signals, compared with main:

Field Old range New range
csr_detection 0.80–1.00 0.61–1.00
csr_episodes 0.90–1.00 0.74–1.00
periodic_breathing 0.60–1.00 0.51–1.00
periodic_breathing_episodes 0.60–1.00 0.51–1.00

On the recorded fixtures (all three detection modes), the full AnalysisResult is identical to main; none of those sessions produces a CSR or periodic-breathing detection.

Also in pattern_detector.py and constants.py, in the spirit of #354: the module and class docstrings no longer claim clustering or positional-event detection, the class example checks for None, and the unused CLUSTER_THRESHOLD_SECONDS, MIN_EVENTS_FOR_POSITIONAL and MIN_CLUSTER_SIZE constants are removed.

Release note: stored confidences change, so AlgorithmIdentity gains a pattern_detector key (PATTERN_DETECTOR_ALGO_VERSION = "v1") and format_version goes from 4 to 5. This marks every stored analysis STALE_VERSION. After deploy, re-run analysis per profile with snore analysis run --from 2000-01-01 --user <email> --profile <name>. pattern_detector is not in CROSS_VERSION_REFUSAL_KEYS, because CSR/PB confidence feeds no cross-epoch distribution.

Closes #354

_find_dominant_cycle only searches lags inside the cycle range, so the
cycle-range confidence bonus was always earned: CSR confidence never fell
below 0.7 and periodic breathing never below 0.6. Replace it with the
autocorrelation peak's strength above the detection gate.

Stored confidences change, so a new pattern_detector identity key and a
format_version bump to 5 mark existing analysis rows stale.
The biased autocorrelation peaks near (N - lag) / N even for a perfect
cycle, so the periodicity grade favoured short cycles; rescale the peak
before grading (detection gating still uses the raw autocorrelation).
The periodic-breathing detector's use of the grade had no test, and the
pattern detector still described clustering it never did.
@wpfleger96
wpfleger96 merged commit f408961 into main Oct 10, 2026
9 checks passed
@wpfleger96
wpfleger96 deleted the wpfleger96/fix-pattern-confidence-floor branch October 10, 2026 03:34
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.

chore(analysis): remove dead and misleading constants and docstrings

1 participant