Skip to content

Add burst segmentation to dcio.analysis (M1) - #1

Merged
remislp merged 2 commits into
masterfrom
bursts
Sep 1, 2026
Merged

remislp merged 2 commits into
masterfrom
bursts

Conversation

@remislp

@remislp remislp commented Sep 1, 2026

Copy link
Copy Markdown
Member

First of the four moves in the DCPROGS record-layer proposal. It puts burst
splitting below SCALCS, EKDIST and HJCFIT instead of beside them.

This is additive. Nothing is removed from SCALCS; scsim.extract_bursts
and friends keep working exactly as they do today. Turning them into
re-exports is M2, and should wait until the TIO manuscript is out, since the
bursts branch imports scsim directly.

What moves

dcio/analysis/bursts.py — extract_bursts, extract_burst_intervals, and
the _burst_segments they share, taken from scalcs/scsim.py. SCALCS's
implementation was chosen over EKDIST's because it is the one with flag
handling, the one with a test suite, and the one that reproduces the 2017
segmentation exactly.

Verified identical to SCALCS over 500 random records in both flag modes, and
on examples/scn/glyr_experimental.scn at tcrit = 2, 4 and 10 ms.

What is new

bursts_from_record(record, tcrit) segments a SingleChannelRecord and
supplies the flags itself, so a caller cannot forget them.

It segments the record's periods, not its resolved intervals, and that
turned out to matter. impose_resolution emits a fresh open interval at every
change of fitted amplitude, so a record idealised with sub-conductance levels
does not alternate open/shut. The shipped experimental example has 1770
consecutive open–open pairs in 3232 resolved intervals
, and segmenting those
directly gives 30 of 63 bursts that end on a shut — sequences the missed-events
likelihood cannot consume. set_periods merges them.

segmenting bursts mean length openings
resolved intervals 63 98.629 ms 2501
periods 63 98.629 ms 731

Same bursts, same lengths; openings counted once rather than once per sub-level.

Worth knowing

Nothing is currently broken by this. Every SCN record shipped with HJCFIT —
CH82, CO, CCO, and all six Burzomato glydemo files — has zero
open–open adjacencies, which is why read_idealized_bursts reproduces the 2017
numbers exactly while passing resolved intervals straight through. The problem
is latent, not live: it would appear on the first record idealised with
sub-levels. Worth pointing read_idealized_bursts at bursts_from_record when
M2 lands.

One correction to the proposal. It listed resolution imposition as
triplicated. That is too blunt: dcio's and EKDIST's are the same full
experimental algorithm (both from dcpyps — flags, sub-level handling, weighted
mean amplitude), while SCALCS's is a simplified simulated-record path already
covered by dcio's record_type="simulated". Two full implementations plus a
convenience, not three of the same thing. It does not change what M1 does.

Tests

18 new, in tests/test_bursts.py. The convention cases carry over from SCALCS
— first opening starts a burst, shut exactly equal to tcrit stays within one,
unusable interval ends one — plus golden values for the shipped experimental
record and the periods/intervals comparison above.

Full suite: 233 passed, 2 skipped.

🤖 Generated with Claude Code

@remislp remislp mentioned this pull request Sep 1, 2026
remislp added a commit that referenced this pull request Sep 1, 2026
The import list named the burst functions from #1, which is not on this
branch. Those go in when M1 merges.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
remislp added a commit that referenced this pull request Sep 1, 2026
* Add CI

dcio has never had CI. That was tolerable while it was one person's file
reader; it is not now that it is becoming the record layer under SCALCS,
EKDIST and HJCFIT, where a regression reaches three other projects.

The suite runs in under two seconds, so the matrix covers every supported
Python (3.11-3.13) and all three platforms rather than one representative
build. Reading and writing SCN means struct packing and endianness, which is
exactly the kind of thing that is fine on the machine it was written on.

Three gates beyond running pytest:

Example records present. Several tests take a legacy SCN from examples/ and
skip when it is absent. A skip is a silent pass: if those files ever stopped
being checked out, the golden burst counts and the round-trip checks against
real records would quietly stop running while CI stayed green.

Oldest dependencies. numpy>=2.0 is declared but nothing has ever run against
2.0 itself, so the floor is an assertion rather than a tested fact. That job
pins Python 3.11, since numpy 2.0 has no wheel for 3.13. Verified locally that
the suite passes on numpy 2.1, the oldest available for this machine's 3.13.

Wheel and console scripts. An editable install resolves imports and entry
points from the source tree, so it cannot catch an undeclared package or an
entry point that does not resolve. The three dcio-* commands have never been
run from an installed wheel; all three answer --help locally.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Check only the API master has in the wheel job

The import list named the burst functions from #1, which is not on this
branch. Those go in when M1 merges.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
remislp and others added 2 commits September 1, 2026 13:46
Move burst splitting into the record layer, so the one implementation sits
below SCALCS, EKDIST and HJCFIT rather than beside them. This is additive:
nothing is removed from SCALCS, whose scsim functions keep working unchanged
until they can be turned into re-exports.

The implementation is SCALCS's, chosen over EKDIST's because it is the one
with flag handling, the one with tests, and the one that reproduces the 2017
segmentation exactly. Verified identical to it over 500 random records in both
flag modes, and on examples/scn/glyr_experimental.scn at three critical times.

Adds a record-level entry point, bursts_from_record, which segments the
record's periods rather than its resolved intervals. impose_resolution emits a
fresh open interval at every change of fitted amplitude, so a record idealised
with sub-conductance levels does not alternate open/shut: the shipped
experimental example has 1770 consecutive open-open pairs in 3232 resolved
intervals, and segmenting those gives 30 of 63 bursts that end on a shut. The
missed-events likelihood needs alternating sequences. Burst count and lengths
are unaffected; openings per burst are 731 rather than 2501, counting an
opening once instead of once per sub-level.

No HJCFIT sample record contains a sub-conductance level, so this fixes
nothing currently broken there -- it removes a way for it to break later.

Tests carry over the convention cases that pinned this behaviour in SCALCS,
and add golden values for the shipped experimental record.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deferred from #2, which was based on a master that did not have it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@remislp
remislp merged commit 93f0e3e into master Sep 1, 2026
7 checks passed
@remislp
remislp deleted the bursts branch September 1, 2026 12:48
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