Skip to content

Ingest: write the epoch's data, ingest by segment in parallel, and compare tool (NDI-matlab#1089, #1090, #1091) - #346

Merged
stevevanhooser merged 3 commits into
mainfrom
claude/ingest-within-epoch-progress
Oct 31, 2026
Merged

stevevanhooser merged 3 commits into
mainfrom
claude/ingest-within-epoch-progress

Conversation

@stevevanhooser

@stevevanhooser stevevanhooser commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Ports VH-Lab/NDI-matlab#1089, #1090 and #1091, all of which are merged. CI checks against NDI-matlab main and installs ndi-compress from NDI-compress-python main, which now has Waltham-Data-Science/NDI-compress-python#4.

Why

Python ingestion made each epoch's documents without any data. After a Python ingest, reading an ingested epoch failed with channel_list.bin not found. NDI-matlab leads, so this ports three things from it: its writer, its segment-level parallel scheduler, and its raw-vs-ingested comparison tool.

What

  • ndi_daq_reader_mfdaq ingestion (NDI-matlab 6ff0264e5, 569fdab67):
    • writes the channel list (channel_list.bin, with each channel's sample rate, offset and scale);
    • writes the data in compressed segments: 1e6 samples for analog/time, 1e7 for digital;
    • stores analog data in the reader's underlying type, and stores event/marker/text channels too;
    • is split, as in MATLAB, into ingest_epochfiles_plan, ingest_segment and the static ingest_attach_segments. ingest_epochfiles(..., ProgressFcn=) runs the three in sequence.
  • ingestEpochDocuments (b192ae605, 569fdab67):
    • On workers, every epoch is planned in the calling process. Each segment, and each epoch's metadata readers, is one task for a spawn ProcessPoolExecutor, so every worker stays busy even with one long epoch.
    • Segments are attached in plan order, so the documents match a serial ingest.
    • ProgressFcn's numDone is fractional, counted in the calling process from the plan weights.
  • ndi.test.ingest.compare / mfdaq_compare (#1091) compare a raw session with an ingested copy of it:
    • checks every epoch, the channel list, every sample of every continuous type (with RelativeTolerance and TimeTolerance), and every event;
    • returns a report with one entry per check and never stops on a mismatch.
  • Intan reader:
    • underlying_datatype gives uint16, as in MATLAB;
    • aux inputs are read at their own rate;
    • digital channels are requested as NDR numbers them (before, digital reads always failed);
    • reads past the end of the file are no longer padded.
  • Metadata readers store their parameters as data.bin, and readmetadata_ingested reads them back.
  • mfdaq_epoch_channel.writeToFile writes MATLAB's saveStructArray format.

Differences from MATLAB (in the bridge decision logs)

  • Digital-output segments are written as do_group*. MATLAB writes di_group*, which collides with digital inputs of the same group; this looks like a MATLAB bug.
  • Float underlying data are stored as floats. MATLAB's datatypestring rejects 'float64'.
  • The time channel computes its own sample range.
  • In the compare tool, a channel-list field is compared only when the raw reader set it. Python's ChannelInfo always has every field, whereas MATLAB compares only the fields both lists share.

Bridge

Testing

  • tests/test_ingest_data_writer.py (10 tests) ingests the example Intan session and reads every channel type back, comparing against the raw reads. This includes epochs split across several segments and windows that cross segment boundaries.
  • tests/test_ingest_compare.py (8 tests):
    • mirrors NDI-matlab's ingestCompareTest;
    • a corrupted sample must be reported;
    • a two-worker parallel ingest with many segments must compare equal;
    • mirrors MATLAB's plan/segment/attach equivalence test.
  • Full suite locally after merging main, against NDI-matlab main: 6525 passed, 352 skipped, 0 failed. CI on 21716f8 is green, bridge completeness included.

🤖 Generated with Claude Code

https://claude.ai/code/session_011v7R5rnKYwncJkrNZRXhtw

…ogress)

Ingestion made each epoch's documents without any data, so nothing
could be read from an ingested epoch ("channel_list.bin not found").

- ndi_daq_reader_mfdaq.ingest_epochfiles (NDI-matlab 6ff0264e5, #1089):
  the channel list (channel_list.bin, MATLAB's saveStructArray format)
  and the data in compressed segments -- analog in the reader's
  underlying type, digital, time, event/marker/text -- plus ProgressFcn,
  the fraction of the epoch done after each segment.
- ingestEpochDocuments (b192ae605): fractional ProgressFcn while an
  epoch is underway, also from worker processes (Manager queue).
- Intan reader: underlying_datatype (uint16, as MATLAB); aux inputs read
  at their own sample rate; digital channels as NDR numbers them (16
  were requested and NDR refused); reads past the end are not padded.
- Metadata readers store readmetadata's parameters (data.bin) and
  readmetadata_ingested reads them back.
- mfdaq_epoch_channel.writeToFile writes MATLAB's format; readFromFile
  still reads the older JSON files.

Needs ndi-compress's compress_ephys format options
(Waltham-Data-Science/NDI-compress-python#4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011v7R5rnKYwncJkrNZRXhtw

Copy link
Copy Markdown
Contributor Author

MATLAB/Python bridge completeness fails here on the same four files as #345: +ndi/+gui/+nav/updateDialog.m and three under +ndi/+update (packageStatus.m, private/findCalculators.m, private/pullRepo.m). This PR doesn't touch them; see the comment on #345. Every entry this PR adds or changes passes, including against NDI-matlab's paired claude/ingest-within-epoch-progress branch. No fix exists yet, and it belongs in its own small bridge PR.


Generated by Claude Code

stevevanhooser commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor Author

test (3.11), test (3.12) and MATLAB ↔ Python symmetry tests fail with one error: compress_ephys() got an unexpected keyword argument 'output_size_bits'.

Once #4 merges, re-running CI here should leave only the bridge-completeness failures, which #347 fixes.


Generated by Claude Code

ndi_daq_reader_mfdaq.ingest_epochfiles is split into ingest_epochfiles_plan,
ingest_segment and the static ingest_attach_segments, as in MATLAB, and
ingest_epochfiles runs them in sequence. ingestEpochDocuments now plans each
epoch in the caller and hands single segments (and each epoch's metadata
readers) to the worker processes, so the pool stays busy however unequal the
epochs are; progress is counted in the caller from the plan weights, which
retires the Manager queue.

New ndi.test.ingest.compare / mfdaq_compare compare a raw session with an
ingested copy: epochs, channel lists, every sample of every continuous type
(RelativeTolerance / TimeTolerance) and every event, with a report of every
check. tests/test_ingest_compare.py mirrors ingestCompareTest, plus a
parallel many-segment ingest that must compare equal, a corrupted sample
that must be reported, and MATLAB's plan/segment/attach equivalence test.

Bridge: mfdaq_reader -> 5c5784c6a with the three new methods,
ingestEpochDocuments -> 569fdab67; fun/epoch (9821a12e6) and setup
(fd7ddbf06) bumped with notes, as nothing there needs porting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011v7R5rnKYwncJkrNZRXhtw
@stevevanhooser stevevanhooser changed the title Ingest: write the epoch's data, as NDI-matlab does (+ within-epoch progress, NDI-matlab#1089) Ingest: write the epoch's data, ingest by segment in parallel, and compare tool (NDI-matlab#1089, #1090, #1091) Oct 31, 2026

Copy link
Copy Markdown
Contributor Author

MATLAB/Python bridge completeness is red on 30435c0 for reasons outside this PR. The checks for every entry this PR touches (daq, daq/fun, fun, setup) pass.

The two failures are entries missing for MATLAB's update dialog (VH-Lab/NDI-matlab 19b8ab573 and 749b1a266):

  • +ndi/+gui/+nav/updateDialog.m
  • +ndi/+update/packageStatus.m, private/findCalculators.m and private/pullRepo.m

main fails the same way against NDI-matlab main. The fix is #347: it ports the update dialog itself, adds these entries, and brings the +ndi/+update and ndiPane hashes up to 749b1a266. I haven't copied that port into this ingestion PR, because it's a separate feature. This check goes green once #347 merges and this branch takes in its base. I haven't re-run the job, since a re-run would fail the same way.


Generated by Claude Code

…branch

#347 already re-examined the ndi.fun.epoch and ndi.setup bridge entries
against NDI-matlab main, so its versions of those two files are kept and
this branch's duplicate notes are dropped. CHANGELOG keeps every entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011v7R5rnKYwncJkrNZRXhtw
Base automatically changed from claude/ingest-progress-parallel to main October 31, 2026 18:46
@stevevanhooser
stevevanhooser merged commit 7f0c15a into main Oct 31, 2026
15 checks passed
@stevevanhooser
stevevanhooser deleted the claude/ingest-within-epoch-progress branch October 31, 2026 18:51
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.

2 participants