Compound stimulus - #16
Merged
Merged
Conversation
The exporter decided a stimulus was compound by looking for an underscore in the class name. Plugin classes are named TonesGrating and TonesPanda, so they took the single-stimulus branch and only the first modality reached the file. No error was raised: the result was a valid NWB file missing the other modality's parameters. Components are now read back from the database instead of parsed out of the name. A stimulus writes one row per cond_table under the trial's stim_hash, so the components are the stimulus tables holding conditions for that session's trials. This drops the naming requirement and works for both naming styles, while leaving single stimuli whose names contain words that look like table names (PsychoGrating, VROdors) intact. Three further defects surfaced once the compound path ran: - Only the first component's conditions were written. Each component now gets its own Conditions/Stimulus_<Component> table; a simple stimulus keeps the single Stimulus table. - Conditions were deduplicated on the hash alone, which discarded the rows of part tables that extend the primary key. Panda.Object adds obj_id and Panda.Light adds light_idx, so a two-object two-light condition kept 1 of its 4 combinations. Dedup is now on the primary key of the joined table. - Those same part tables repeated each presentation once per combination, giving 16 identical timing rows for 4 trials. The presentation timing is collapsed back to one row per trial. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S7gfyfdvHFBvXP5RNMPJ5c
Explains how to combine several modalities into one stimulus class, which is done by inheritance rather than composition: subclass the dominant modality, then extend cond_tables, required_fields and default_key. Includes a minimal Tones + Grating example and the task file that runs it. The bulk of the guide is the pitfalls, most of which fail silently rather than raising. Stimulus.__init__ wipes cond_tables, required_fields and default_key, so a subclass that sets them as class attributes gets an empty contract and every condition collapses to the same stim_hash. A missing required field skips a whole condition table. A component decorated with the stimulus schema registers a second time. Logging a trial in both parents duplicates it. Also distinguishes a compound stimulus, where the modalities share one stim_hash, from stim_periods, which sequences them within a trial. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S7gfyfdvHFBvXP5RNMPJ5c
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.
Description
Compound (multimodal) stimuli were exported based on their class name: the exporter split
stimulus_classon underscores and treated each piece as a component table. That only worked for classes named after their tables, and a freely named class such asTonesGratingexported nothing usable.Components are now resolved from the data instead. For a session, the exporter looks up which stimulus tables actually hold conditions for the trials of that stimulus class, so any naming scheme works and
cond_tablesis the single source of truth.Two related export bugs are fixed along the way:
Stimulus_<Component>, and a simple stimulus keeps the singleStimulustable.Panda.Objectaddsobj_id,Panda.Lightaddslight_idx) produce several rows per hash, and collapsing on the hash kept only one of them. The repeated presentation timing rows are collapsed instead.Docs: a new guide on building a compound stimulus, plus an update to the NWB export docs covering component resolution and stimuli with part tables.
How Has This Been Tested?
Exported sessions with
export_to_nwb(animal_id=..., session_id=...)and inspectedthe resulting file:
nwbfile.stimulusand as aStimulus_<Component>table in theConditionsmoduleStimulustable is unchangedChecklist: