Skip to content

Fix KSPlot, MATLAB parity (Examples 03-05), and regenerate figures#61

Merged
iahncajigas merged 4 commits intomainfrom
fix/ksplot-all-models
Mar 13, 2026
Merged

Fix KSPlot, MATLAB parity (Examples 03-05), and regenerate figures#61
iahncajigas merged 4 commits intomainfrom
fix/ksplot-all-models

Conversation

@iahncajigas
Copy link
Copy Markdown
Contributor

@iahncajigas iahncajigas commented Mar 12, 2026

Summary

This PR brings full MATLAB behavioral parity for Examples 03-05 and fixes several issues:

KSPlot fix

  • MATLAB's KSPlot() defaults to plotting all model KS curves overlaid; Python now matches this behavior

Example 03 parity (psthGLM)

  • Fixed psthGLM to use batch fitting across trials (MATLAB batchMode=1)
  • Fixed GLM spike binning, xcov normalization, KS stats, and history slicing

Example 04 parity (place cells) — 29/29 ALL PASS

  • Cross-language parity test comparing FitSummary stats (AIC, BIC, KS), design matrices, and computed place fields
  • Tolerance-aware comparison for FP precision differences in sampleRate computation

Example 05 parity (PPAF/PPHF decoding) — 11/11 ALL PASS

  • Fixed goal-directed PPDecodeFilterLinear: Added missing x0/Pi0 fusion step (Srinivasan 2006 prior update) and corrected PPDecode_predict index selection in forward loop
  • Cross-language parity test for 1-D, 4-D free, 4-D goal-directed, and hybrid filter — all at machine precision (max Δ ≤ 1.78e-15)

Figures

  • Regenerated all 24 paper example figures from the updated codebase
  • Cleaned up stale old-format figure files

Test plan

  • 265 tests pass, 3 skipped (no regressions)
  • Example 04 parity: 29/29 ALL PASS
  • Example 05 parity: 11/11 ALL PASS
  • Paper figure asset tests: 4/4 pass
  • Smoke-group notebooks: 10/10 pass

🤖 Generated with Claude Code

Iahn Cajigas and others added 4 commits March 12, 2026 09:12
MATLAB's KSPlot() defaults to fitNum=1:numResults, plotting all model
KS curves overlaid with colors and a legend. Python's KSPlot defaulted
to fit_num=1, showing only the first model.

- KSPlot now accepts fit_num=None (default) to plot all models
- Uses MATLAB color cycle (b, g, r, c, m, y, k) for model curves
- Adds legend with model names from lambda dataLabels
- plotResults passes fit_num=None to KSPlot for all-model KS panel
- Title matches MATLAB: "KS Plot of Rescaled ISIs / with 95% CIs"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nd history slicing

Four fixes achieving exact MATLAB-Python numerical parity for Examples 01 and 02:

1. analysis.py: Use getSpikeVector() instead of to_binned_counts() in GLMFit
   to match MATLAB's spike vector construction. np.histogram bin edges assign
   spikes to adjacent bins on floating-point boundaries, causing systematic
   deviance offsets (2*ln(4) ≈ 2.77).

2. core.py: Change xcov() default scaleOpt from "biased" to "none" to match
   MATLAB's built-in xcov() which returns unscaled cross-covariance.

3. fit.py + analysis.py: Compute KS statistics for ALL fits in a multi-config
   result (not just fit 1), and write each stat to the correct KSStats index.
   MATLAB processes all lambda columns at once; Python now loops over fits.

4. example02: Fix selectedHistory off-by-one — windowTimes[:windowIndex] gives
   one fewer element than MATLAB's windowTimes(1:windowIndex) due to 0-based
   vs 1-based indexing. Fixed to windowTimes[:windowIndex + 1].

Also includes example01 fixes from prior session: _matlab_colon() for exact
MATLAB colon operator parity, and searchsorted(side='left') for epoch boundaries.

Parity results: Example 01 25/25, Example 02 30/30 (KS stats within 0.05 due
to ksdiscrete DT correction random draws). Full test suite: 265 passed, 3 skipped.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rity)

MATLAB's nstColl.psthGLM calls RunAnalysisForAllNeurons with batchMode=1,
which pools all trials of the same neuron into a single GLM fit. Python
previously used RunAnalysisForAllNeurons without batchMode, fitting each
trial separately — producing single-trial coefficients instead of
across-trial pooled ones. This caused max Δ ~49 Hz in psthGLM outputs.

Changes:
- psthGLM now manually concatenates X and y across all trials and performs
  a single pooled Poisson (or binomial) GLM fit, matching MATLAB exactly
- Standard errors computed from Fisher information (Hessian inverse)
- FitResult populated with batch-fit coefficients and SEs
- Fix dN transpose in example03 (Python dataToMatrix returns (T,K) not (K,T))
- Add tests/test_example03_parity.py: 29-check cross-language parity test
  with boundary-aware psthGLM comparison and advisory Monte Carlo checks

Remaining psthGLM per-bin max Δ (0.05-0.09 Hz) is inherent: MATLAB's
colon operator uses a proprietary compensated-sum algorithm producing
different floating-point basis boundaries than np.arange at ~8/2001 points.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cross-language tests

The non-augmented target branch of PPDecodeFilterLinear had two parity
issues vs MATLAB:

1. Missing x0/Pi0 fusion step (Srinivasan 2006 prior update) — MATLAB
   lines 547-558 fuse the initial state estimate with backward target
   information before the forward pass; Python skipped this entirely.

2. PPDecode_predict index mismatch in the forward loop — MATLAB uses
   Amat(:,:,min(size(A,3),n)) which resolves to B[:,:,0] (since A is
   2-D with singleton 3rd dim) and Qmat(:,:,min(size(Qmat,3))) which
   resolves to QT[:,:,N-1] at every time step. Python was varying these
   indices with time, causing accumulating divergence.

Both fixes achieve machine-precision parity (max Δ = 7.77e-16).

Also adds cross-language parity test scripts for Examples 04 and 05:
- test_example04_parity.py: 29/29 tests (place fields, FitSummary, design matrices)
- test_example05_parity.py: 11/11 tests (1-D/4-D PPAF, goal-directed, hybrid filter)

Regenerated all 24 paper example figures from the updated codebase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@iahncajigas iahncajigas changed the title Fix KSPlot to show all models by default Fix KSPlot, MATLAB parity (Examples 03-05), and regenerate figures Mar 13, 2026
@iahncajigas iahncajigas merged commit 0cf8bd5 into main Mar 13, 2026
13 checks passed
@iahncajigas iahncajigas deleted the fix/ksplot-all-models branch March 13, 2026 04:09
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