Skip to content

Support sparse matrices - #2

Open
bendichter wants to merge 1 commit into
complex-compoundfrom
sparse
Open

Support sparse matrices#2
bendichter wants to merge 1 commit into
complex-compoundfrom
sparse

Conversation

@bendichter

Copy link
Copy Markdown
Contributor

Stacked on #1 (targets complex-compound, since the complex work is a prerequisite for sparse matrices with complex values). Retarget to main once #1 merges.

v7.3 stores a sparse matrix as a group of three ordinary numeric datasets in compressed-column form: jc holds a running nonzero count per column plus a terminator, ir the 0-based row indices, and data the values. The indexer already handled all three (and complex data, now that #1 adds the compound path), so this is a reader-side change only: one new materializeSparse in File.m.

The two details worth knowing

The row count is not recoverable from the three datasets, since ir only names rows that actually hold a nonzero. MATLAB records it in the MATLAB_sparse attribute; the column count is numel(jc) - 1.

A matrix with no nonzeros omits data and ir entirely, leaving a group with a single jc dataset. That is how both an all-zero matrix and sparse(0, 0) arrive.

Eager, not lazy

Sparse values are materialized eagerly, like cells and structs. Zarr has no sparse concept to hang laziness on, and the compressed-column form has to be read in full to reconstruct any part of the matrix anyway.

Testing

Both storage layouts (chunked + deflate, and contiguous via -nocompression) across the degenerate shapes: all-zero, 0x0, 0xN, single column, scalar, logical, logical all-zero, complex, and sparse nested inside a struct and a cell.

Worth flagging: isequaln does not distinguish a sparse matrix from a full one with the same values, so the test asserts issparse and the class separately. Without that it would pass on a reader that quietly returned dense matrices.

Two sparse variables also join the shared round-trip matrix (now 21 types), which puts them through the HTTP and request-count tests. 14/14 pass locally on R2024b.

Survey

tools/make_survey_corpus.m pairs complex and sparse in signals.mat as deliberate clean-error cases. Both now read, so the corpus surveys clean apart from the legacy v7 file:

[1/5] crcns_ephys.mat (0 MB) ... OK (3 vars)
[2/5] imaging_big.mat (3 MB) ... OK (1 vars)
[3/5] legacy_v7.mat (0 MB) ... not v7.3 (pre-HDF5 .mat); matzarr targets v7.3
[4/5] nested.mat (0 MB) ... OK (1 vars)
[5/5] signals.mat (0 MB) ... OK (2 vars)

The stale comments calling those paths clean-error cases are updated.

🤖 Generated with Claude Code

https://claude.ai/code/session_016nBFt7vk88uDoPpw2QQHFW

v7.3 stores a sparse matrix as a group of three ordinary numeric datasets in
compressed-column form: jc holds a running nonzero count per column plus a
terminator, ir the 0-based row indices, and data the values. The indexer
already handled all three (and complex data now that the compound path
exists), so this is a reader-side change only.

Two details are easy to get wrong and are what the tests aim at. The row
count is not recoverable from the three datasets, since ir only names rows
that actually hold a nonzero, so MATLAB records it in the MATLAB_sparse
attribute; the column count is numel(jc) - 1. And a matrix with no nonzeros
omits data and ir entirely, leaving a group with a single jc dataset, which
is how both an all-zero matrix and sparse(0, 0) arrive.

Sparse values are materialized eagerly, like cells and structs, rather than
returned as a lazy zarr.Array. Zarr has no sparse concept to hang laziness
on, and the compressed-column form has to be read in full to reconstruct any
part of the matrix anyway.

Testing covers both storage layouts (chunked + deflate, and contiguous via
-nocompression) across the degenerate shapes: all-zero, 0x0, 0xN, single
column, scalar, logical, logical all-zero, complex, and sparse nested inside
a struct and a cell. Note that isequaln does not distinguish a sparse matrix
from a full one with the same values, so the test asserts issparse and the
class separately; without that it would pass on a reader that quietly
returned dense matrices. Two sparse variables also join the shared
round-trip matrix (now 21 types), which puts them through the HTTP and
request-count tests.

The synthetic survey corpus pairs complex and sparse in signals.mat as
deliberate clean-error cases. Both now read, so the corpus surveys clean
apart from the legacy v7 file, and the stale comments saying otherwise are
updated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nBFt7vk88uDoPpw2QQHFW
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