From d5ce3d7cf21ec4b52cb1fd87bd258016c996fdb2 Mon Sep 17 00:00:00 2001 From: Iahn Cajigas Date: Sun, 22 Mar 2026 12:32:13 -0400 Subject: [PATCH 1/2] refactor: nspikeTrain constructor takes sampleRate instead of binwidth Change the 3rd positional argument of nspikeTrain.__init__ from binwidth (seconds) to sampleRate (Hz), matching the user-facing mental model. The default changes from 0.001 to 1000.0 (same effective rate: 1/0.001 = 1000). This fixes several silent bugs where callers passed sampleRate-like values (10.0, 20.0) as the 3rd arg, which were misinterpreted as binwidth (giving sampleRate=0.1 or 0.05 Hz instead of intended 10 or 20 Hz). Changes: - nstat/core.py: Constructor signature, nstCopy, fromStructure - nstat/trial.py: nstColl collapsed train construction - 8 test files: Convert explicit binwidth values to sampleRate - 2 notebook builder tools: Convert delta to 1.0/delta Co-Authored-By: Claude Opus 4.6 (1M context) --- nstat/core.py | 9 ++++----- nstat/trial.py | 2 +- tests/test_expanded_coverage.py | 20 +++++++++---------- tests/test_matlab_gold_fixtures.py | 14 ++++++------- tests/test_matlab_reference.py | 2 +- tests/test_nspiketrain_fidelity.py | 12 +++++------ tests/test_trial_fidelity.py | 4 ++-- tests/test_workflow_fidelity.py | 4 ++-- .../build_decoding_fidelity_notebooks.py | 2 +- .../build_helpfile_fidelity_notebooks.py | 4 ++-- 10 files changed, 36 insertions(+), 37 deletions(-) diff --git a/nstat/core.py b/nstat/core.py index b494030f..f2b0fd99 100644 --- a/nstat/core.py +++ b/nstat/core.py @@ -2100,7 +2100,7 @@ def __init__( self, spikeTimes, name: str = "", - binwidth: float = 0.001, + sampleRate: float = 1000.0, minTime: float | None = None, maxTime: float | None = None, xlabelval: str = "time", @@ -2115,7 +2115,7 @@ def __init__( self.spikeTimes = np.sort(spikes) self.originalSpikeTimes = self.spikeTimes.copy() self.name = str(name) - self.sampleRate = float(1.0 / float(binwidth)) + self.sampleRate = float(sampleRate) self.originalSampleRate = float(self.sampleRate) if minTime is None: minTime = float(np.min(self.spikeTimes)) if self.spikeTimes.size else 0.0 @@ -2654,7 +2654,7 @@ def nstCopy(self) -> "nspikeTrain": return nspikeTrain( self.spikeTimes.copy(), self.name, - 1.0 / self.sampleRate if self.sampleRate > 0 else 0.001, + self.sampleRate if self.sampleRate > 0 else 1000.0, self.minTime, self.maxTime, self.xlabelval, @@ -2688,11 +2688,10 @@ def toStructure(self) -> dict[str, Any]: def fromStructure(structure: dict[str, Any]) -> "nspikeTrain": """Reconstruct an ``nspikeTrain`` from a dict.""" sampleRate = float(structure.get("sampleRate", 1000.0)) - binwidth = 1.0 / sampleRate if sampleRate > 0 else 0.001 return nspikeTrain( structure.get("spikeTimes", []), structure.get("name", ""), - binwidth, + sampleRate if sampleRate > 0 else 1000.0, structure.get("minTime"), structure.get("maxTime"), structure.get("xlabelval", "time"), diff --git a/nstat/trial.py b/nstat/trial.py index c4eddac4..b990942d 100644 --- a/nstat/trial.py +++ b/nstat/trial.py @@ -966,7 +966,7 @@ def toSpikeTrain( if np.asarray(train.spikeTimes).size: spike_times.extend((np.asarray(train.spikeTimes, dtype=float).reshape(-1) * delta_tw + local_min).tolist()) - collapsed = nspikeTrain(spike_times, name, delta, minTime, float(maxTime) * len(selector), "time", "s", "", "", -1) + collapsed = nspikeTrain(spike_times, name, 1.0 / delta, minTime, float(maxTime) * len(selector), "time", "s", "", "", -1) collapsed.setName(name) collapsed.setMinTime(float(minTime)) collapsed.setMaxTime(float(maxTime) * len(selector)) diff --git a/tests/test_expanded_coverage.py b/tests/test_expanded_coverage.py index e56b1dce..dd03a62b 100644 --- a/tests/test_expanded_coverage.py +++ b/tests/test_expanded_coverage.py @@ -51,8 +51,8 @@ def simple_trial(): np.random.seed(42) spikes1 = np.sort(np.random.uniform(0, 1, 30)) spikes2 = np.sort(np.random.uniform(0, 1, 25)) - n1 = nspikeTrain(spikes1, "n1", 0.001, 0.0, 1.0, "time", "s", "spikes", "spk", -1) - n2 = nspikeTrain(spikes2, "n2", 0.001, 0.0, 1.0, "time", "s", "spikes", "spk", -1) + n1 = nspikeTrain(spikes1, "n1", 1000.0, 0.0, 1.0, "time", "s", "spikes", "spk", -1) + n2 = nspikeTrain(spikes2, "n2", 1000.0, 0.0, 1.0, "time", "s", "spikes", "spk", -1) trial = Trial(nstColl([n1, n2]), CovColl([cov])) return trial @@ -79,14 +79,14 @@ def fit_summary(fit_result): class TestEdgeCases: def test_empty_spike_train_statistics(self) -> None: """Empty spike train should not error on basic statistics.""" - nst = nspikeTrain([], "empty", 0.001, 0.0, 1.0, "time", "s", "spikes", "spk", -1) + nst = nspikeTrain([], "empty", 1000.0, 0.0, 1.0, "time", "s", "spikes", "spk", -1) assert nst.n_spikes == 0 isis = nst.getISIs() assert len(isis) == 0 def test_single_spike_train_collection(self) -> None: """nstColl with one spike train should work without error.""" - nst = nspikeTrain([0.1, 0.5, 0.9], "only", 0.001, 0.0, 1.0, "time", "s", "spikes", "spk", -1) + nst = nspikeTrain([0.1, 0.5, 0.9], "only", 1000.0, 0.0, 1.0, "time", "s", "spikes", "spk", -1) coll = nstColl([nst]) assert coll.numSpikeTrains == 1 assert coll.getNST(1).name == "only" @@ -95,7 +95,7 @@ def test_single_spike_train_collection(self) -> None: def test_single_spike_train_psth(self) -> None: """PSTH on single-neuron collection should still work.""" - nst = nspikeTrain([0.1, 0.5, 0.9], "n1", 0.001, 0.0, 1.0, "time", "s", "spikes", "spk", -1) + nst = nspikeTrain([0.1, 0.5, 0.9], "n1", 1000.0, 0.0, 1.0, "time", "s", "spikes", "spk", -1) coll = nstColl([nst]) psth = coll.psth(0.1, [1], 0.0, 1.0) # nstColl.psth returns nstat.core.Covariate; check by class name to avoid module alias issues @@ -104,7 +104,7 @@ def test_single_spike_train_psth(self) -> None: def test_spike_train_with_one_spike(self) -> None: """Spike train with exactly one spike should compute statistics safely.""" - nst = nspikeTrain([0.5], "one", 0.001, 0.0, 1.0, "time", "s", "spikes", "spk", -1) + nst = nspikeTrain([0.5], "one", 1000.0, 0.0, 1.0, "time", "s", "spikes", "spk", -1) assert nst.n_spikes == 1 assert len(nst.getISIs()) == 0 @@ -115,7 +115,7 @@ def test_covariate_collection_empty(self) -> None: def test_trial_with_no_covariates(self) -> None: """Trial requires CovColl — verify clear error when omitted.""" - nst = nspikeTrain([0.1], "n1", 0.001, 0.0, 1.0, "time", "s", "spikes", "spk", -1) + nst = nspikeTrain([0.1], "n1", 1000.0, 0.0, 1.0, "time", "s", "spikes", "spk", -1) with pytest.raises(ValueError, match="CovColl is a required argument"): Trial(nstColl([nst])) @@ -175,8 +175,8 @@ def test_fitsummary_tostructure_fromstructure(self, fit_summary) -> None: def test_nstcoll_tostructure_fromstructure(self) -> None: """nstColl should survive toStructure/fromStructure round-trip.""" - n1 = nspikeTrain([0.1, 0.5], "n1", 0.001, 0.0, 1.0, "time", "s", "spikes", "spk", -1) - n2 = nspikeTrain([0.2, 0.8], "n2", 0.001, 0.0, 1.0, "time", "s", "spikes", "spk", -1) + n1 = nspikeTrain([0.1, 0.5], "n1", 1000.0, 0.0, 1.0, "time", "s", "spikes", "spk", -1) + n2 = nspikeTrain([0.2, 0.8], "n2", 1000.0, 0.0, 1.0, "time", "s", "spikes", "spk", -1) coll = nstColl([n1, n2]) structure = coll.toStructure() restored = nstColl.fromStructure(structure) @@ -336,7 +336,7 @@ def test_spikeTrigAvg_basic(self, simple_trial) -> None: def test_psth_function(self) -> None: """Analysis.psth should return counts and centers.""" - spikes = [nspikeTrain([0.1, 0.3, 0.5, 0.7, 0.9], "n1", 0.001, 0.0, 1.0)] + spikes = [nspikeTrain([0.1, 0.3, 0.5, 0.7, 0.9], "n1", 1000.0, 0.0, 1.0)] bins = np.arange(0.0, 1.1, 0.2) counts, centers = Analysis.psth(spikes, bins) assert counts.shape[0] == len(bins) - 1 diff --git a/tests/test_matlab_gold_fixtures.py b/tests/test_matlab_gold_fixtures.py index 025dc0f2..86084512 100644 --- a/tests/test_matlab_gold_fixtures.py +++ b/tests/test_matlab_gold_fixtures.py @@ -135,7 +135,7 @@ def test_nspiketrain_matches_matlab_gold_fixture() -> None: np.testing.assert_allclose(parts.getNST(1).spikeTimes, _vector(payload, "part1_spikes"), rtol=1e-8, atol=1e-10) np.testing.assert_allclose(parts.getNST(2).spikeTimes, _vector(payload, "part2_spikes"), rtol=1e-8, atol=1e-10) - restore_train = nspikeTrain(_vector(payload, "spikeTimes"), "restore", 0.2, -0.1, 0.8, "time", "s", "spikes", "spk", -1) + restore_train = nspikeTrain(_vector(payload, "spikeTimes"), "restore", 5.0, -0.1, 0.8, "time", "s", "spikes", "spk", -1) restore_train.setSigRep(0.1, -0.1, 0.8) restore_train.setMinTime(-0.3) restore_train.setMaxTime(1.1) @@ -144,7 +144,7 @@ def test_nspiketrain_matches_matlab_gold_fixture() -> None: np.testing.assert_allclose(float(restore_train.minTime), _scalar(payload, "restore_min_time"), rtol=1e-12, atol=1e-12) np.testing.assert_allclose(float(restore_train.maxTime), _scalar(payload, "restore_max_time"), rtol=1e-12, atol=1e-12) - burst_train = nspikeTrain([0.0, 0.001, 0.002, 0.007, 0.507, 0.508, 0.509, 0.514], "bursting", 0.001, 0.0, 0.6, "time", "s", "spikes", "spk", 0) + burst_train = nspikeTrain([0.0, 0.001, 0.002, 0.007, 0.507, 0.508, 0.509, 0.514], "bursting", 1000.0, 0.0, 0.6, "time", "s", "spikes", "spk", 0) np.testing.assert_allclose(float(burst_train.avgSpikesPerBurst), _scalar(payload, "burst_avgSpikesPerBurst"), rtol=1e-8, atol=1e-10) np.testing.assert_allclose(float(burst_train.stdSpikesPerBurst), _scalar(payload, "burst_stdSpikesPerBurst"), rtol=1e-8, atol=1e-10) np.testing.assert_allclose(float(burst_train.numBursts), _scalar(payload, "burst_numBursts"), rtol=1e-8, atol=1e-10) @@ -582,7 +582,7 @@ def test_analysis_fit_surface_matches_matlab_gold_fixture() -> None: sample_rate = _scalar(payload, "sample_rate") stim = Covariate(time, stim_data, "Stimulus", "time", "s", "", ["stim"]) - spike_train = nspikeTrain(spike_times, "1", 0.1, 0.0, 1.0, "time", "s", "", "", -1) + spike_train = nspikeTrain(spike_times, "1", 10.0, 0.0, 1.0, "time", "s", "", "", -1) trial = Trial(nstColl([spike_train]), CovColl([stim])) cfg = TrialConfig([["Stimulus", "stim"]], sample_rate, [], [], name="stim") fit = Analysis.RunAnalysisForNeuron(trial, 1, ConfigColl([cfg])) @@ -612,8 +612,8 @@ def test_analysis_multineuron_surface_matches_matlab_gold_fixture() -> None: time = _vector(payload, "time") stim_data = _vector(payload, "stim_data") stim = Covariate(time, stim_data, "Stimulus", "time", "s", "", ["stim"]) - spike_train_1 = nspikeTrain(_vector(payload, "spike_times_1"), "1", 0.1, 0.0, 1.0, "time", "s", "", "", -1) - spike_train_2 = nspikeTrain(_vector(payload, "spike_times_2"), "2", 0.1, 0.0, 1.0, "time", "s", "", "", -1) + spike_train_1 = nspikeTrain(_vector(payload, "spike_times_1"), "1", 10.0, 0.0, 1.0, "time", "s", "", "", -1) + spike_train_2 = nspikeTrain(_vector(payload, "spike_times_2"), "2", 10.0, 0.0, 1.0, "time", "s", "", "", -1) trial = Trial(nstColl([spike_train_1, spike_train_2]), CovColl([stim])) cfg = TrialConfig([["Stimulus", "stim"]], 10, [], [], name="stim") fits = Analysis.RunAnalysisForAllNeurons(trial, ConfigColl([cfg]), makePlot=0) @@ -703,8 +703,8 @@ def test_fit_summary_matches_matlab_gold_fixture() -> None: "Hz", ["stim", "stim_hist"], ) - st1 = nspikeTrain([0.1, 0.4, 0.7], "1", 0.1, 0.0, 1.0, "time", "s", "", "", -1) - st2 = nspikeTrain([0.2, 0.5, 0.8], "2", 0.1, 0.0, 1.0, "time", "s", "", "", -1) + st1 = nspikeTrain([0.1, 0.4, 0.7], "1", 10.0, 0.0, 1.0, "time", "s", "", "", -1) + st2 = nspikeTrain([0.2, 0.5, 0.8], "2", 10.0, 0.0, 1.0, "time", "s", "", "", -1) stim_cfg = TrialConfig([["Stimulus", "stim"]], 10.0, [], [], name="stim") stim_hist_cfg = TrialConfig([["Stimulus", "stim"]], 10.0, [0.0, 0.1, 0.2], [], name="stim_hist") config_coll = ConfigColl([stim_cfg, stim_hist_cfg]) diff --git a/tests/test_matlab_reference.py b/tests/test_matlab_reference.py index a0ead9c9..40e075ca 100644 --- a/tests/test_matlab_reference.py +++ b/tests/test_matlab_reference.py @@ -54,7 +54,7 @@ def test_analysis_fixture_remains_consumable_without_matlab_runtime() -> None: payload = _load_fixture("analysis_exactness.mat") time = np.arange(0.0, 1.0 + 0.1, 0.1) stim = Covariate(time, np.sin(2 * np.pi * time), "Stimulus", "time", "s", "", ["stim"]) - spike_train = nspikeTrain([0.1, 0.4, 0.7], "1", 0.1, 0.0, 1.0, "time", "s", "", "", -1) + spike_train = nspikeTrain([0.1, 0.4, 0.7], "1", 10.0, 0.0, 1.0, "time", "s", "", "", -1) trial = Trial(nstColl([spike_train]), CovColl([stim])) cfg = TrialConfig([["Stimulus", "stim"]], 10.0, [], [], name="stim") fit = Analysis.RunAnalysisForNeuron(trial, 1, ConfigColl([cfg])) diff --git a/tests/test_nspiketrain_fidelity.py b/tests/test_nspiketrain_fidelity.py index 3afdfd70..abca9c06 100644 --- a/tests/test_nspiketrain_fidelity.py +++ b/tests/test_nspiketrain_fidelity.py @@ -15,7 +15,7 @@ def test_nspiketrain_constructor_runs_statistics_without_numpy_mode_error() -> N def test_nspiketrain_sigrep_uses_matlab_style_centers_and_inclusive_last_bin() -> None: - train = nspikeTrain([0.0, 0.5, 1.0], "neuron", 0.5, 0.0, 1.0, makePlots=-1) + train = nspikeTrain([0.0, 0.5, 1.0], "neuron", 2.0, 0.0, 1.0, makePlots=-1) sig = train.getSigRep() @@ -33,7 +33,7 @@ def test_nspiketrain_windowing_and_binary_limit_follow_matlab_semantics() -> Non def test_nspiketrain_partition_and_min_isi_follow_matlab_semantics() -> None: - train = nspikeTrain([0.1, 0.4, 0.6, 1.1], "neuron", 0.1, 0.0, 1.2, makePlots=-1) + train = nspikeTrain([0.1, 0.4, 0.6, 1.1], "neuron", 10.0, 0.0, 1.2, makePlots=-1) np.testing.assert_allclose(train.getMinISI(), 0.2) parts = train.partitionNST([0.0, 0.5, 1.2], normalizeTime=0) @@ -49,7 +49,7 @@ def test_nspiketrain_partition_and_min_isi_follow_matlab_semantics() -> None: def test_nspiketrain_setsigrep_restore_and_field_access_match_matlab_surface() -> None: - train = nspikeTrain([0.2, 0.6], "neuron", 0.2, 0.0, 1.0, makePlots=-1) + train = nspikeTrain([0.2, 0.6], "neuron", 5.0, 0.0, 1.0, makePlots=-1) train.setSigRep(0.1, 0.0, 1.0) assert train.sampleRate == 10.0 @@ -66,7 +66,7 @@ def test_nspiketrain_setsigrep_restore_and_field_access_match_matlab_surface() - def test_nspiketrain_compute_statistics_matches_matlab_style_burst_metrics() -> None: - train = nspikeTrain([0.0, 0.001, 0.002, 0.007, 0.507, 0.508, 0.509, 0.514], "bursting", 0.001, 0.0, 0.6, makePlots=0) + train = nspikeTrain([0.0, 0.001, 0.002, 0.007, 0.507, 0.508, 0.509, 0.514], "bursting", 1000.0, 0.0, 0.6, makePlots=0) assert np.isfinite(train.B) assert np.isfinite(train.An) @@ -78,7 +78,7 @@ def test_nspiketrain_compute_statistics_matches_matlab_style_burst_metrics() -> def test_nspiketrain_partition_rounds_windows_and_uses_matlab_constructor_defaults() -> None: - train = nspikeTrain([0.0004, 0.0014, 0.0096], "neuron", 0.001, 0.0, 0.01, makePlots=-1) + train = nspikeTrain([0.0004, 0.0014, 0.0096], "neuron", 1000.0, 0.0, 0.01, makePlots=-1) parts = train.partitionNST([0.00049, 0.00151, 0.0101], normalizeTime=0) @@ -89,7 +89,7 @@ def test_nspiketrain_partition_rounds_windows_and_uses_matlab_constructor_defaul def test_nspiketrain_isi_plot_helpers_execute_and_return_matplotlib_objects() -> None: - train = nspikeTrain([0.1, 0.12, 0.15, 0.5, 0.8], "neuron", 0.001, 0.0, 1.0, makePlots=0) + train = nspikeTrain([0.1, 0.12, 0.15, 0.5, 0.8], "neuron", 1000.0, 0.0, 1.0, makePlots=0) line = train.plotISISpectrumFunction() joint_ax = train.plotJointISIHistogram() diff --git a/tests/test_trial_fidelity.py b/tests/test_trial_fidelity.py index a9258637..71fff10f 100644 --- a/tests/test_trial_fidelity.py +++ b/tests/test_trial_fidelity.py @@ -18,8 +18,8 @@ def _make_covariates() -> tuple[Covariate, Covariate]: def _make_spikes() -> tuple[nspikeTrain, nspikeTrain]: - n1 = nspikeTrain([0.0, 0.5, 1.0], "n1", 0.5, 0.0, 1.0, makePlots=-1) - n2 = nspikeTrain([0.25, 0.75], "n2", 0.5, 0.0, 1.0, makePlots=-1) + n1 = nspikeTrain([0.0, 0.5, 1.0], "n1", 2.0, 0.0, 1.0, makePlots=-1) + n2 = nspikeTrain([0.25, 0.75], "n2", 2.0, 0.0, 1.0, makePlots=-1) return n1, n2 diff --git a/tests/test_workflow_fidelity.py b/tests/test_workflow_fidelity.py index 908eff77..67d42f93 100644 --- a/tests/test_workflow_fidelity.py +++ b/tests/test_workflow_fidelity.py @@ -22,8 +22,8 @@ def _build_trial() -> Trial: vel = Covariate(time, np.cos(2 * np.pi * time), "Velocity", "time", "s", "", ["vel"]) spikes = nstColl( [ - nspikeTrain([0.1, 0.3, 0.7], "1", 0.1, 0.0, 0.9, makePlots=-1), - nspikeTrain([0.2, 0.5, 0.8], "2", 0.1, 0.0, 0.9, makePlots=-1), + nspikeTrain([0.1, 0.3, 0.7], "1", 10.0, 0.0, 0.9, makePlots=-1), + nspikeTrain([0.2, 0.5, 0.8], "2", 10.0, 0.0, 0.9, makePlots=-1), ] ) return Trial(spikes, CovColl([stim, vel]), Events([0.2], ["cue"]), History([0.0, 0.1, 0.2])) diff --git a/tools/notebooks/build_decoding_fidelity_notebooks.py b/tools/notebooks/build_decoding_fidelity_notebooks.py index 74336a5a..5efff3aa 100644 --- a/tools/notebooks/build_decoding_fidelity_notebooks.py +++ b/tools/notebooks/build_decoding_fidelity_notebooks.py @@ -317,7 +317,7 @@ def _simulate_history_spike_train(time, stim_data, baseline, hist_coeffs, window trains = [] for idx in range(numRealizations): spikes = _simulate_history_spike_train(time, stimData, b0, histCoeffs, windowTimes) - trains.append(nspikeTrain(spikes, str(idx + 1), delta, 0.0, Tmax, makePlots=-1)) + trains.append(nspikeTrain(spikes, str(idx + 1), 1.0 / delta, 0.0, Tmax, makePlots=-1)) sC = nstColl(trains) fig = _prepare_figure("figure", figsize=(8.0, 5.5)) diff --git a/tools/notebooks/build_helpfile_fidelity_notebooks.py b/tools/notebooks/build_helpfile_fidelity_notebooks.py index 1a26eb15..38d94345 100644 --- a/tools/notebooks/build_helpfile_fidelity_notebooks.py +++ b/tools/notebooks/build_helpfile_fidelity_notebooks.py @@ -334,7 +334,7 @@ def _simulate_constant_case(seed=0, *, p=0.01, n_samples=None, delta=0.001): for idx in range(2): spike_mask = rng.random(n_samples) < p spike_times = time[spike_mask] - train = nspikeTrain(spike_times, str(idx + 1), delta, 0.0, total_time, makePlots=-1) + train = nspikeTrain(spike_times, str(idx + 1), 1.0 / delta, 0.0, total_time, makePlots=-1) trains.append(train) spike_coll = nstColl(trains) cov = Covariate(time, np.ones((time.shape[0], 1), dtype=float), "Baseline", "time", "s", "", ["mu"]) @@ -368,7 +368,7 @@ def _simulate_piecewise_case(seed=1, *, p1=0.001, p2=0.01, n1=None, n2=None, del spikes1 = t1[:-1][rng.random(n1) < p1] spikes2 = t2[rng.random(n2) < p2] spike_times = np.concatenate([spikes1, spikes2]) - train = nspikeTrain(spike_times, str(idx + 1), delta, 0.0, total_time, makePlots=-1) + train = nspikeTrain(spike_times, str(idx + 1), 1.0 / delta, 0.0, total_time, makePlots=-1) trains.append(train) time = np.concatenate([t1[:-1], t2]) cov_data = np.column_stack( From 16c63434d1735877de6bd13adecd68ff844dc63b Mon Sep 17 00:00:00 2001 From: Iahn Cajigas Date: Sun, 22 Mar 2026 12:48:31 -0400 Subject: [PATCH 2/2] fix: convert remaining gold fixture binwidth values to sampleRate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MATLAB gold fixtures were generated with binwidth as 3rd arg. Values 10.0 and 2.0 in nstcoll/history tests were MATLAB binwidths (not sampleRate), needing conversion: 10.0→0.1, 2.0→0.5. Also convert fixture-loaded binwidth via 1.0/binwidth. Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/test_matlab_gold_fixtures.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_matlab_gold_fixtures.py b/tests/test_matlab_gold_fixtures.py index 86084512..36baa2f7 100644 --- a/tests/test_matlab_gold_fixtures.py +++ b/tests/test_matlab_gold_fixtures.py @@ -112,7 +112,7 @@ def test_nspiketrain_matches_matlab_gold_fixture() -> None: nst = nspikeTrain( _vector(payload, "spikeTimes"), "nst", - _scalar(payload, "binwidth"), + 1.0 / _scalar(payload, "binwidth"), _scalar(payload, "minTime"), _scalar(payload, "maxTime"), "time", @@ -320,8 +320,8 @@ def test_confidence_interval_matches_matlab_gold_fixture() -> None: def test_nstcoll_matches_matlab_gold_fixture() -> None: payload = _load_fixture("nstcoll_exactness.mat") - n1 = nspikeTrain(_vector(payload, "firstSpikeTimes"), "1", 10.0, 0.0, 0.5, "time", "s", "spikes", "spk", -1) - n2 = nspikeTrain(_vector(payload, "secondSpikeTimes"), "2", 10.0, 0.0, 0.5, "time", "s", "spikes", "spk", -1) + n1 = nspikeTrain(_vector(payload, "firstSpikeTimes"), "1", 0.1, 0.0, 0.5, "time", "s", "spikes", "spk", -1) + n2 = nspikeTrain(_vector(payload, "secondSpikeTimes"), "2", 0.1, 0.0, 0.5, "time", "s", "spikes", "spk", -1) coll = nstColl([n1, n2]) collapsed = coll.toSpikeTrain() coll.setNeighbors() @@ -394,8 +394,8 @@ def test_trialconfig_and_configcoll_match_matlab_gold_fixture() -> None: time = np.array([0.0, 0.5, 1.0], dtype=float) position = Covariate(time, np.column_stack([[0.0, 1.0, 2.0], [10.0, 11.0, 12.0]]), "Position", "time", "s", "", ["x", "y"]) stimulus = Covariate(time, [5.0, 6.0, 7.0], "Stimulus", "time", "s", "a.u.", ["stim"]) - n1 = nspikeTrain([0.0, 0.5, 1.0], "n1", 2.0, 0.0, 1.0, "time", "s", "spikes", "spk", -1) - n2 = nspikeTrain([0.25, 0.75], "n2", 2.0, 0.0, 1.0, "time", "s", "spikes", "spk", -1) + n1 = nspikeTrain([0.0, 0.5, 1.0], "n1", 0.5, 0.0, 1.0, "time", "s", "spikes", "spk", -1) + n2 = nspikeTrain([0.25, 0.75], "n2", 0.5, 0.0, 1.0, "time", "s", "spikes", "spk", -1) cfg_applied = TrialConfig([["Position", "x"], ["Stimulus"]], 4.0, [0.0, 0.5, 1.0], [0.0, 0.5, 1.0], [[0, 1], [1, 0]], 0.25, "stim_pos") trial = Trial(nstColl([n1, n2]), CovColl([position, stimulus])) @@ -510,8 +510,8 @@ def test_history_matches_matlab_gold_fixture() -> None: np.testing.assert_allclose(float(rebuilt.minTime), _scalar(payload, "roundtrip_minTime"), rtol=1e-12, atol=1e-12) np.testing.assert_allclose(float(rebuilt.maxTime), _scalar(payload, "roundtrip_maxTime"), rtol=1e-12, atol=1e-12) - n1 = nspikeTrain([0.0, 0.5, 1.0], "n1", 2.0, 0.0, 1.0, "time", "s", "spikes", "spk", -1) - n2 = nspikeTrain([0.25, 0.75], "n2", 2.0, 0.0, 1.0, "time", "s", "spikes", "spk", -1) + n1 = nspikeTrain([0.0, 0.5, 1.0], "n1", 0.5, 0.0, 1.0, "time", "s", "spikes", "spk", -1) + n2 = nspikeTrain([0.25, 0.75], "n2", 0.5, 0.0, 1.0, "time", "s", "spikes", "spk", -1) coll = nstColl([n1, n2]) single_cov = history.computeHistory(n1, 1) @@ -1051,13 +1051,13 @@ def test_cif_gamma_scaled_evals_match_matlab_gold_fixture() -> None: window_times = _vector(payload, "window_times") spike_times = _vector(payload, "spike_times") # Fixture stores binwidth (Δt in seconds). Python nspikeTrain takes - # binwidth as its 3rd arg and stores sampleRate = 1/binwidth. + # sampleRate (Hz) as its 3rd arg. binwidth = _scalar(payload, "binwidth") # Construct CIF with history — must provide histCoeffs + historyObj # at construction time so gamma function handles are compiled. hist = History(window_times, 0.0, 1.0) - nst = nspikeTrain_cls(spike_times, "n1", binwidth, 0.0, 1.0) + nst = nspikeTrain_cls(spike_times, "n1", 1.0 / binwidth, 0.0, 1.0) cif = CIF( beta=beta_vec, Xnames=["stim1", "stim2"],