Skip to content

[Bug]: nwb.units.toTable() fails when the number of total units exceeds the number of samples in each waveform #772

@maxdougherty

Description

@maxdougherty

What happened?

Version: 2.9.0

When the number of total spike events across all units exceeds the total length of the samples in each waveform, the following error occurs. This error does not occur if the number of samples in the waveform is greater-than or equal-to the total number of spike events.

The error within a recursive call to the select function in getRow.m at line 200.

for iRange = 1:length(matInd)
        startInd = startInds(iRange);  
        stopInd = stopInds(iRange);
        selected{iRange} = select(DynamicTable,...
            colIndStack(1:(end-1)),...
            startInd:stopInd);
end
Image

This screenshot shows that for the 4th simulated unit, the waveform arrays being populated have 110 "timepoints" rather than the specified 109. This 110 corresponds with the total number of spike events across all units. The error should be reproducible with the code below.

There is no tutorial or documentation demonstrating the use of the waveform_index and waveform_index_index parameters, so perhaps this could be attributed to user error?

Steps to Reproduce

nwb = NwbFile;
nwb.session_description = 'Example session with single-unit waveforms';
nwb.identifier          = 'example-waveforms-nwb';
nwb.session_start_time  = datetime('now', 'TimeZone', 'local');

%% --- Define synthetic spike waveform data per unit ---
%   single electrode per spike, each waveform has nSamples points

unitSpikeCounts = [20 30 15 45];   % number of spikes per unit
%unitSpikeCounts = [2 3];
nUnits          = numel(unitSpikeCounts);
%nSamples        = 40;      % points per waveform
nSamples = 109; %CHANGING THIS TO >= 110 PREVENTS THE ERROR
totalSpikes     = sum(unitSpikeCounts);

% Preallocate
waveforms_all          = zeros(totalSpikes, nSamples);     % [nSpikes x nSamples]
waveforms_index_data   = zeros(totalSpikes, 1, 'int64');   % one entry per spike event
waveforms_index_index_data = zeros(nUnits, 1, 'int64');    % one entry per unit

row   = 0;   % index into waveforms_all (rows)
spike = 0;   % global spike index (over all units)

for u = 1:nUnits
    for s = 1:unitSpikeCounts(u)
        row   = row + 1;
        spike = spike + 1;

        % Example waveform: random noise
        wf = randn(1, nSamples);     % 1 electrode x nSamples
        waveforms_all(row, :) = wf;

        % waveforms_index: index into rows of waveforms_all for each spike event
        % With exactly 1 waveform per spike event:
        waveforms_index_data(spike) = int64(row);
    end

    % waveforms_index_index: cumulative count of spike events for each unit
    waveforms_index_index_data(u) = int64(spike);
end

%% --- Build the Units table with waveforms columns ---

%% waveforms: VectorData containing all waveforms for all spikes
waveforms = types.hdmf_common.VectorData( ...
    'data',        waveforms_all, ...
    'description', 'Spike waveforms; each row is one spike (single electrode)' ...
    );

%% waveforms_index: VectorIndex into waveforms
waveforms_index = types.hdmf_common.VectorIndex( ...
    'data',        waveforms_index_data, ...
    'target',      types.untyped.ObjectView(waveforms), ...
    'description', 'Index into waveforms; one value per spike event' ...
    );

%% waveforms_index_index: VectorIndex into waveforms_index
waveforms_index_index = types.hdmf_common.VectorIndex( ...
    'data',        waveforms_index_index_data, ...
    'target',      types.untyped.ObjectView(waveforms_index), ...
    'description', 'Index into waveforms_index; one value per unit' ...
    );

%% --- Attach units to NWB file ---
units = types.core.Units( ...
    'colnames', {'waveforms', 'waveforms_index', 'waveforms_index_index'}, ...
    'description', 'Example units table with single-unit spike waveforms', ...
    'waveforms', waveforms, ...
    'waveforms_index', waveforms_index, ...
    'waveforms_index_index', waveforms_index_index ...
    );

nwb.units = units;
nwb.units.toTable();

Error Message

Index in position 2 exceeds array bounds. Index must not exceed 109.

Error in types.util.dynamictable.getRow>select (line 168)
        selected = Vector.data(selectInd{:});
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
Error in types.util.dynamictable.getRow>select (line 203)
        selected{iRange} = select(DynamicTable,...
                           ^^^^^^^^^^^^^^^^^^^^^^^
Error in types.util.dynamictable.getRow>select (line 203)
        selected{iRange} = select(DynamicTable,...
                           ^^^^^^^^^^^^^^^^^^^^^^^
Error in types.util.dynamictable.getRow (line 46)
    row{i} = select(DynamicTable, indexNames, ind);
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in types.hdmf_common.DynamicTable/getRow (line 113)
        row = types.util.dynamictable.getRow(obj, id, varargin{:});
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in types.util.dynamictable.nwbToTable (line 83)
matlabTable = [matlabTable DynamicTable.getRow( ...
                           ^^^^^^^^^^^^^^^^^^^^^^^^
Error in types.hdmf_common.DynamicTable/toTable (line 117)
        table = types.util.dynamictable.nwbToTable(obj, varargin{:});
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in nwb_waveform_testing (line 89)
nwb.units.toTable();
^^^^^^^^^^^^^^^^^^^

Operating System

Windows

Matlab Version

R2024b

Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions