Fix KSPlot, MATLAB parity (Examples 03-05), and regenerate figures#61
Merged
iahncajigas merged 4 commits intomainfrom Mar 13, 2026
Merged
Fix KSPlot, MATLAB parity (Examples 03-05), and regenerate figures#61iahncajigas merged 4 commits intomainfrom
iahncajigas merged 4 commits intomainfrom
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR brings full MATLAB behavioral parity for Examples 03-05 and fixes several issues:
KSPlot fix
KSPlot()defaults to plotting all model KS curves overlaid; Python now matches this behaviorExample 03 parity (psthGLM)
batchMode=1)Example 04 parity (place cells) — 29/29 ALL PASS
Example 05 parity (PPAF/PPHF decoding) — 11/11 ALL PASS
Figures
Test plan
🤖 Generated with Claude Code