From 8f21d2f9bd2cf76b04ebe989d6d947dc34a18cd7 Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Thu, 23 Jul 2026 18:34:04 +0000 Subject: [PATCH 1/5] Remove redundant SSMC writer MEX --- bindings/matlab/Contents.m | 2 - bindings/matlab/README.md | 14 +-- bindings/matlab/build_matlab_bindings.m | 3 +- bindings/matlab/build_octave_bindings.m | 3 +- bindings/matlab/compile_octave.sh | 2 +- .../matlab/test_write_binsparse_from_matlab.m | 102 --------------- bindings/matlab/write_binsparse_from_matlab.c | 116 ------------------ 7 files changed, 9 insertions(+), 233 deletions(-) delete mode 100644 bindings/matlab/test_write_binsparse_from_matlab.m delete mode 100644 bindings/matlab/write_binsparse_from_matlab.c diff --git a/bindings/matlab/Contents.m b/bindings/matlab/Contents.m index 015bf6b..2e9970d 100644 --- a/bindings/matlab/Contents.m +++ b/bindings/matlab/Contents.m @@ -6,7 +6,6 @@ % binsparse_from_ssmc - convert SSMC A+Zeros to a Binsparse matrix struct % binsparse_minimize_types - minimize value/index types in a Binsparse struct % binsparse_write_string_dataset - write an HDF5 UTF-8 string dataset -% write_binsparse_from_matlab - write an SSMC Problem struct (delegates to generate_bsp_from_ssmc) % % MATLAB helpers: % generate_bsp_from_ssmc - write an SSMC Problem to a Binsparse file @@ -27,7 +26,6 @@ % test_bsp_matrix_struct - tests for the Binsparse matrix struct helpers % test_convert_to_problem_struct - tests for Binsparse Problem conversion % test_generate_bsp_from_ssmc - end-to-end test for generate_bsp_from_ssmc -% test_write_binsparse_from_matlab - end-to-end test for the SSMC writer MEX % test_binsparse_roundtrip_dir - round-trip every .h5 file in a directory % SPDX-FileCopyrightText: 2024 Binsparse Developers diff --git a/bindings/matlab/README.md b/bindings/matlab/README.md index e09a708..6a40a25 100644 --- a/bindings/matlab/README.md +++ b/bindings/matlab/README.md @@ -57,7 +57,7 @@ mkoctfile --version ```matlab test_binsparse_read() test_binsparse_write() - test_write_binsparse_from_matlab() + test_generate_bsp_from_ssmc() ``` #### Option 2: Octave (from within Octave) @@ -76,7 +76,7 @@ mkoctfile --version ```octave test_binsparse_read() test_binsparse_write() - test_write_binsparse_from_matlab() + test_generate_bsp_from_ssmc() ``` #### Option 3: Octave (from command line) @@ -95,7 +95,7 @@ mkoctfile --version ```bash octave --eval "test_binsparse_read()" octave --eval "test_binsparse_write()" - octave --eval "test_write_binsparse_from_matlab()" + octave --eval "test_generate_bsp_from_ssmc()" ``` ## Usage Examples @@ -157,13 +157,13 @@ Problem.title = 'Test Matrix'; Problem.kind = 'artificial/test'; % Write directly from SuiteSparse format to Binsparse -write_binsparse_from_matlab(Problem, 'output.bsp.h5'); +generate_bsp_from_ssmc(Problem, 'output.bsp.h5'); % Write with an explicit sparse format ('COO', 'COOR', 'CSC', or 'CSR') -write_binsparse_from_matlab(Problem, 'output.bsp.h5', 'CSC'); +generate_bsp_from_ssmc(Problem, 'output.bsp.h5', 'CSC'); % Write with a format and gzip compression level (0-9) -write_binsparse_from_matlab(Problem, 'output.bsp.h5', 'COO', [], 6); +generate_bsp_from_ssmc(Problem, 'output.bsp.h5', 'COO', 6); ``` ### Error Handling @@ -187,7 +187,6 @@ end | `binsparse_from_ssmc.c` | MEX function converting SuiteSparse A+Zeros to a Binsparse struct | | `binsparse_minimize_types.c` | MEX function minimizing value/index types in a Binsparse struct | | `binsparse_write_string_dataset.c` | MEX function writing HDF5 UTF-8 string datasets | -| `write_binsparse_from_matlab.c` | MEX entry point for writing an SSMC Problem (delegates to `generate_bsp_from_ssmc.m`) | | `matlab_bsp_helpers.h` | Shared MATLAB/Binsparse conversion helpers for the MEX sources | | `generate_bsp_from_ssmc.m` | Write a full SSMC Problem struct to one Binsparse file | | `convert_to_problem_struct.m` | Convert Binsparse data back to an SSMC Problem struct | @@ -203,7 +202,6 @@ end | `test_bsp_matrix_struct.m` | Test script for the matrix struct helpers | | `test_convert_to_problem_struct.m` | Test script for Problem conversion | | `test_generate_bsp_from_ssmc.m` | End-to-end test for the SSMC writer | -| `test_write_binsparse_from_matlab.m` | Test script for the SSMC writer MEX | | `test_binsparse_roundtrip_dir.m` | Round-trip every .h5 file in a directory | | `Contents.m` | Directory listing for MATLAB's `help` | | `README.md` | This documentation file | diff --git a/bindings/matlab/build_matlab_bindings.m b/bindings/matlab/build_matlab_bindings.m index 34408ca..e31b6dd 100644 --- a/bindings/matlab/build_matlab_bindings.m +++ b/bindings/matlab/build_matlab_bindings.m @@ -56,7 +56,7 @@ function build_matlab_bindings(varargin) fprintf('Run the test functions to verify the installation:\n'); fprintf(' test_binsparse_read()\n'); fprintf(' test_binsparse_write()\n'); -fprintf(' test_write_binsparse_from_matlab()\n\n'); +fprintf(' test_generate_bsp_from_ssmc()\n\n'); end @@ -107,7 +107,6 @@ function compile_mex_functions(paths, verbose) % List of MEX functions to compile mex_files = {'binsparse_read.c', 'binsparse_write.c', ... 'binsparse_from_ssmc.c', 'binsparse_minimize_types.c', ... - 'write_binsparse_from_matlab.c', ... 'binsparse_write_string_dataset.c'}; fprintf('Compiling MEX functions...\n'); diff --git a/bindings/matlab/build_octave_bindings.m b/bindings/matlab/build_octave_bindings.m index 5031ac1..8ac8f60 100644 --- a/bindings/matlab/build_octave_bindings.m +++ b/bindings/matlab/build_octave_bindings.m @@ -64,7 +64,7 @@ function build_octave_bindings(varargin) fprintf('Run the test functions to verify the installation:\n'); fprintf(' test_binsparse_read()\n'); fprintf(' test_binsparse_write()\n'); -fprintf(' test_write_binsparse_from_matlab()\n\n'); +fprintf(' test_generate_bsp_from_ssmc()\n\n'); end @@ -119,7 +119,6 @@ function compile_octave_functions(paths, verbose) % List of MEX functions to compile mex_files = {'binsparse_read.c', 'binsparse_write.c', ... 'binsparse_from_ssmc.c', 'binsparse_minimize_types.c', ... - 'write_binsparse_from_matlab.c', ... 'binsparse_write_string_dataset.c'}; fprintf('Compiling MEX functions with mkoctfile...\n'); diff --git a/bindings/matlab/compile_octave.sh b/bindings/matlab/compile_octave.sh index 4e429f2..7a32e7b 100755 --- a/bindings/matlab/compile_octave.sh +++ b/bindings/matlab/compile_octave.sh @@ -148,7 +148,7 @@ if [ "$CLEAN" = true ]; then fi # List of MEX files to compile -MEX_FILES=("binsparse_read.c" "binsparse_write.c" "binsparse_from_ssmc.c" "binsparse_minimize_types.c" "write_binsparse_from_matlab.c" "binsparse_write_string_dataset.c") +MEX_FILES=("binsparse_read.c" "binsparse_write.c" "binsparse_from_ssmc.c" "binsparse_minimize_types.c" "binsparse_write_string_dataset.c") print_info "Compiling MEX functions..." diff --git a/bindings/matlab/test_write_binsparse_from_matlab.m b/bindings/matlab/test_write_binsparse_from_matlab.m deleted file mode 100644 index a663910..0000000 --- a/bindings/matlab/test_write_binsparse_from_matlab.m +++ /dev/null @@ -1,102 +0,0 @@ -function test_write_binsparse_from_matlab() -% TEST_WRITE_BINSPARSE_FROM_MATLAB - End-to-end test for the SSMC writer MEX - -% SPDX-FileCopyrightText: 2024 Binsparse Developers -% -% SPDX-License-Identifier: BSD-3-Clause - -fprintf('=== Testing write_binsparse_from_matlab MEX function ===\n\n'); - -required = {'write_binsparse_from_matlab', 'binsparse_read'}; -for i = 1:numel(required) - if exist(required{i}, 'file') ~= 3 - error('%s MEX function not found. Please compile it first.', required{i}); - end -end - -try - write_binsparse_from_matlab(); - error('Expected insufficient-argument error'); -catch ME - assert(strcmp(ME.identifier, 'BinSparse:InvalidArgs'), ... - 'Unexpected identifier: %s', ME.identifier); -end - -try - write_binsparse_from_matlab(42, 'invalid.bsp.h5'); - error('Expected invalid-problem error'); -catch ME - assert(strcmp(ME.identifier, 'BinSparse:InvalidProblem'), ... - 'Unexpected identifier: %s', ME.identifier); -end - -Problem = struct(); -Problem.name = 'test_basic_matrix'; -Problem.title = 'Basic Test Matrix'; -Problem.kind = 'test matrix'; -Problem.A = sparse([1 3], [1 2], [5 7], 3, 3); -Problem.Zeros = sparse(2, 3, 1, 3, 3); -Problem.b = [1; 2; 3]; -Problem.aux = struct(); -Problem.aux.D = [10 20; 30 40]; - -out_file = [tempname() '.bsp.h5']; -cleanup_file = onCleanup(@() delete_if_exists(out_file)); - -write_binsparse_from_matlab(struct('Problem', Problem), out_file, 'COO', [], 0); - -primary = bsp_to_matlab(binsparse_read(out_file)); -assert(isequal(double(primary), full(Problem.A)), 'Primary matrix mismatch'); - -b = bsp_to_matlab(binsparse_read(out_file, 'b')); -assert(isequal(double(b), Problem.b), 'b vector mismatch'); - -D = bsp_to_matlab(binsparse_read(out_file, 'D')); -assert(isequal(double(D), Problem.aux.D), 'aux.D mismatch'); - -legacy_out_file = [tempname() '.bsp.h5']; -cleanup_legacy = onCleanup(@() delete_if_exists(legacy_out_file)); -write_binsparse_from_matlab(Problem, legacy_out_file, 'legacy_group', '{}', 0); -assert(exist(legacy_out_file, 'file') == 2, 'Legacy call did not create file'); - -fprintf('Test passed.\n'); - -end - -function mat = bsp_to_matlab(bsp) - fmt = upper(bsp.format); - switch fmt - case 'COO' - rows = double(bsp.indices_0) + 1; - cols = double(bsp.indices_1) + 1; - vals = bsp.values; - mat = full(sparse(rows, cols, vals, bsp.nrows, bsp.ncols)); - case 'CSC' - colptr = double(bsp.pointers_to_1); - rowind = double(bsp.indices_1); - rows = []; - cols = []; - vals = []; - for j = 1:bsp.ncols - idx = (colptr(j) + 1):colptr(j + 1); - rows = [rows; rowind(idx) + 1]; - cols = [cols; j * ones(numel(idx), 1)]; - vals = [vals; bsp.values(idx)]; - end - mat = full(sparse(rows, cols, vals, bsp.nrows, bsp.ncols)); - case 'DMAT' - mat = reshape(bsp.values, [bsp.ncols, bsp.nrows]).'; - case 'DMATC' - mat = reshape(bsp.values, [bsp.nrows, bsp.ncols]); - case 'DVEC' - mat = reshape(bsp.values, [bsp.nrows, 1]); - otherwise - error('Unsupported format in test: %s', fmt); - end -end - -function delete_if_exists(filename) -if exist(filename, 'file') - delete(filename); -end -end diff --git a/bindings/matlab/write_binsparse_from_matlab.c b/bindings/matlab/write_binsparse_from_matlab.c deleted file mode 100644 index 69951b8..0000000 --- a/bindings/matlab/write_binsparse_from_matlab.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 Binsparse Developers - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -/** - * write_binsparse_from_matlab.c - Write a SuiteSparse Matrix Collection - * problem struct to Binsparse format. - * - * This MEX entry point intentionally delegates SSMC-specific handling to the - * MATLAB implementation in generate_bsp_from_ssmc.m. That path writes the - * primary matrix, explicit zeros, metadata, aux matrices, b, and x. - * - * Usage in MATLAB/Octave: - * write_binsparse_from_matlab(problem_struct, filename) - * write_binsparse_from_matlab(problem_struct, filename, format) - * write_binsparse_from_matlab(problem_struct, filename, format, [], - * compression) - * - * For compatibility with the older scaffold, a non-format third string is - * accepted and ignored as an HDF5 group name; generate_bsp_from_ssmc writes - * SSMC files at the root with aux entries in named groups. - */ - -#include "mex.h" -#include -#include - -static bool is_supported_sparse_format(const char* text) { - return strcasecmp(text, "CSC") == 0 || strcasecmp(text, "CSR") == 0 || - strcasecmp(text, "COO") == 0 || strcasecmp(text, "COOR") == 0; -} - -static mxArray* make_default_format(void) { - return mxCreateString("COO"); -} - -void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { - (void) plhs; - - if (nrhs < 2 || nrhs > 5) { - mexErrMsgIdAndTxt( - "BinSparse:InvalidArgs", - "Usage: write_binsparse_from_matlab(problem_struct, filename [, format " - "[, json_metadata [, compression_level]]])"); - } - - if (nlhs > 0) { - mexErrMsgIdAndTxt("BinSparse:TooManyOutputs", - "No output arguments expected"); - } - - if (!mxIsStruct(prhs[0])) { - mexErrMsgIdAndTxt("BinSparse:InvalidProblem", - "First argument must be a struct"); - } - - if (!mxIsChar(prhs[1])) { - mexErrMsgIdAndTxt("BinSparse:InvalidFilename", - "Filename must be a character vector"); - } - - mxArray* format = NULL; - if (nrhs >= 3 && !mxIsEmpty(prhs[2])) { - if (!mxIsChar(prhs[2])) { - mexErrMsgIdAndTxt("BinSparse:InvalidFormat", - "Format must be a character vector"); - } - - char* format_text = mxArrayToString(prhs[2]); - if (!format_text) { - mexErrMsgIdAndTxt("BinSparse:MemoryError", - "Failed to read format string"); - } - - if (is_supported_sparse_format(format_text)) { - format = mxCreateString(format_text); - } - mxFree(format_text); - } - - if (!format) { - format = make_default_format(); - } - - mxArray* compression = NULL; - if (nrhs >= 5 && !mxIsEmpty(prhs[4])) { - if (!mxIsNumeric(prhs[4]) || mxIsComplex(prhs[4]) || - mxGetNumberOfElements(prhs[4]) != 1) { - mxDestroyArray(format); - mexErrMsgIdAndTxt("BinSparse:InvalidCompression", - "Compression level must be a real numeric scalar"); - } - compression = mxDuplicateArray(prhs[4]); - } else if (nrhs >= 4 && mxIsNumeric(prhs[3]) && !mxIsEmpty(prhs[3])) { - compression = mxDuplicateArray(prhs[3]); - } else { - compression = mxCreateDoubleScalar(0.0); - } - - mxArray* rhs[4]; - rhs[0] = (mxArray*) prhs[0]; - rhs[1] = (mxArray*) prhs[1]; - rhs[2] = format; - rhs[3] = compression; - - int status = mexCallMATLAB(0, NULL, 4, rhs, "generate_bsp_from_ssmc"); - - mxDestroyArray(format); - mxDestroyArray(compression); - - if (status != 0) { - mexErrMsgIdAndTxt("BinSparse:WriteFailed", "generate_bsp_from_ssmc failed"); - } -} From 914e7bb3897f4c3a79e6dcaaa1b3d97e0db600aa Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Thu, 23 Jul 2026 18:34:11 +0000 Subject: [PATCH 2/5] Validate SSMC writer inputs --- bindings/matlab/generate_bsp_from_ssmc.m | 37 +++++++++++++++++-- bindings/matlab/test_generate_bsp_from_ssmc.m | 31 +++++++++++++++- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/bindings/matlab/generate_bsp_from_ssmc.m b/bindings/matlab/generate_bsp_from_ssmc.m index a7ac027..1a3873b 100644 --- a/bindings/matlab/generate_bsp_from_ssmc.m +++ b/bindings/matlab/generate_bsp_from_ssmc.m @@ -15,19 +15,45 @@ function generate_bsp_from_ssmc(problem, output_filename, format, compression_le if nargin < 2 error('generate_bsp_from_ssmc:InvalidArgs', ... - 'Usage: generate_bsp_from_ssmc(problem, output_filename [, format [, compression_level]])'); + ['Usage: generate_bsp_from_ssmc(problem, output_filename ' ... + '[, format [, compression_level]])']); +end + +if ~isstruct(problem) || ~isscalar(problem) + error('generate_bsp_from_ssmc:InvalidProblem', ... + 'Problem must be a scalar struct'); +end + +if ~ischar(output_filename) || size(output_filename, 1) ~= 1 || ... + isempty(output_filename) + error('generate_bsp_from_ssmc:InvalidFilename', ... + 'Output filename must be a nonempty character vector'); end if nargin < 3 || isempty(format) format = 'COO'; end +if ~ischar(format) || size(format, 1) ~= 1 + error('generate_bsp_from_ssmc:InvalidFormat', ... + 'Format must be a character vector'); +end +format = upper(format); +if ~any(strcmp(format, {'COO', 'COOR', 'CSC', 'CSR'})) + error('generate_bsp_from_ssmc:InvalidFormat', ... + 'Unsupported sparse format: %s', format); +end + if nargin < 4 || isempty(compression_level) compression_level = 0; end -if ~isstruct(problem) - error('generate_bsp_from_ssmc:InvalidProblem', 'Problem must be a struct'); +if ~isnumeric(compression_level) || ~isreal(compression_level) || ... + ~isscalar(compression_level) || ~isfinite(compression_level) || ... + compression_level ~= fix(compression_level) || ... + compression_level < 0 || compression_level > 9 + error('generate_bsp_from_ssmc:InvalidCompression', ... + 'Compression level must be an integer from 0 to 9'); end if isfield(problem, 'Problem') @@ -36,6 +62,11 @@ function generate_bsp_from_ssmc(problem, output_filename, format, compression_le P = problem; end +if ~isstruct(P) || ~isscalar(P) + error('generate_bsp_from_ssmc:InvalidProblem', ... + 'Problem must contain a scalar Problem struct'); +end + if ~isfield(P, 'A') error('generate_bsp_from_ssmc:MissingMatrix', 'Problem.A is required'); end diff --git a/bindings/matlab/test_generate_bsp_from_ssmc.m b/bindings/matlab/test_generate_bsp_from_ssmc.m index 9771b24..6f64f13 100644 --- a/bindings/matlab/test_generate_bsp_from_ssmc.m +++ b/bindings/matlab/test_generate_bsp_from_ssmc.m @@ -12,10 +12,28 @@ function test_generate_bsp_from_ssmc() 'binsparse_write_string_dataset', 'generate_bsp_from_ssmc'}; for i = 1:numel(required) if exist(required{i}, 'file') ~= 3 && exist(required{i}, 'file') ~= 2 - error('%s not found. Please compile MEX functions and ensure the .m file is on path.', required{i}); + error(['%s not found. Please compile MEX functions and ensure ' ... + 'the .m file is on path.'], required{i}); end end +% Check validation performed by the public SSMC writer. +minimal_problem = struct('A', sparse(1)); +expect_error(@() generate_bsp_from_ssmc(), ... + 'generate_bsp_from_ssmc:InvalidArgs'); +expect_error(@() generate_bsp_from_ssmc(42, 'invalid.bsp.h5'), ... + 'generate_bsp_from_ssmc:InvalidProblem'); +expect_error(@() generate_bsp_from_ssmc(minimal_problem, 42), ... + 'generate_bsp_from_ssmc:InvalidFilename'); +expect_error(@() generate_bsp_from_ssmc( ... + minimal_problem, 'invalid.bsp.h5', 'bad'), ... + 'generate_bsp_from_ssmc:InvalidFormat'); +expect_error(@() generate_bsp_from_ssmc(minimal_problem, ... + 'invalid.bsp.h5', 'COO', 10), ... + 'generate_bsp_from_ssmc:InvalidCompression'); +expect_error(@() generate_bsp_from_ssmc(struct(), 'invalid.bsp.h5'), ... + 'generate_bsp_from_ssmc:MissingMatrix'); + % Build synthetic problem Problem = struct(); Problem.name = 'Test/Small'; @@ -90,6 +108,17 @@ function check_string_dataset(filename, name, expected) assert(isequal(actual, expected), 'String dataset "%s" mismatch', name); end +function expect_error(action, identifier) + try + action(); + catch exception + assert(strcmp(exception.identifier, identifier), ... + 'Expected %s, received %s', identifier, exception.identifier); + return; + end + error('Expected error %s', identifier); +end + function value = as_cellstr(value) if iscell(value) value = value(:); From 620a883d72b7061839ad79eef772261cf6e434d3 Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Thu, 23 Jul 2026 19:00:59 +0000 Subject: [PATCH 3/5] Write vector shapes as one-dimensional --- CMakeLists.txt | 2 + bindings/matlab/test_generate_bsp_from_ssmc.m | 9 ++ examples/bsp-ls.c | 25 ++-- include/binsparse/matrix_formats.h | 6 + src/read_matrix.c | 86 ++++++++------ src/write_matrix.c | 17 ++- test/CMakeLists.txt | 1 + test/c/CMakeLists.txt | 10 ++ test/c/dvec_shape_test.c | 110 ++++++++++++++++++ 9 files changed, 216 insertions(+), 50 deletions(-) create mode 100644 test/c/CMakeLists.txt create mode 100644 test/c/dvec_shape_test.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 91247b2..1776473 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,6 +120,8 @@ install(FILES DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/binsparse) if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + enable_testing() + if (ENABLE_SANITIZERS) set(SANITIZER_FLAGS "-fsanitize=address,undefined") target_compile_options(binsparse INTERFACE ${SANITIZER_FLAGS} -g -O1 -fno-omit-frame-pointer) diff --git a/bindings/matlab/test_generate_bsp_from_ssmc.m b/bindings/matlab/test_generate_bsp_from_ssmc.m index 6f64f13..2f607b4 100644 --- a/bindings/matlab/test_generate_bsp_from_ssmc.m +++ b/bindings/matlab/test_generate_bsp_from_ssmc.m @@ -73,8 +73,10 @@ function test_generate_bsp_from_ssmc() % Read aux and x/b check_dense_group(out_file, 'b', Problem.b(:)); +check_vector_shape(out_file, 'b', numel(Problem.b)); check_dense_group(out_file, 'x', Problem.x); check_dense_group(out_file, 'c', Problem.aux.c(:)); +check_vector_shape(out_file, 'c', numel(Problem.aux.c)); check_dense_group(out_file, 'D', Problem.aux.D); aux_sparse = binsparse_read(out_file, 'S'); @@ -102,6 +104,13 @@ function check_dense_group(filename, group, expected) 'Group "%s" mismatch', group); end +function check_vector_shape(filename, group, expected) + json = h5readatt(filename, ['/' group], 'binsparse'); + pattern = sprintf('"shape"\s*:\s*\[\s*%d\s*\]', expected); + assert(~isempty(regexp(json, pattern, 'once')), ... + 'Group "%s" does not have a one-dimensional shape', group); +end + function check_string_dataset(filename, name, expected) actual = h5read(filename, ['/' name]); actual = as_cellstr(actual); diff --git a/examples/bsp-ls.c b/examples/bsp-ls.c index 7fb0870..caf4ab5 100644 --- a/examples/bsp-ls.c +++ b/examples/bsp-ls.c @@ -55,24 +55,31 @@ void print_group_info(hid_t g, const char* name) { cJSON* shape_ = cJSON_GetObjectItemCaseSensitive(binsparse, "shape"); assert(shape_ != NULL); - assert(cJSON_GetArraySize(shape_) == 2); + bool is_vector = bsp_matrix_format_is_vector( + bsp_get_matrix_format(format_string)); + assert(cJSON_GetArraySize(shape_) == (is_vector ? 1 : 2)); cJSON* nrows_ = cJSON_GetArrayItem(shape_, 0); assert(nrows_ != NULL); size_t nrows = cJSON_GetNumberValue(nrows_); - cJSON* ncols_ = cJSON_GetArrayItem(shape_, 1); - assert(ncols_ != NULL); - - size_t ncols = cJSON_GetNumberValue(ncols_); - char full_group_path[2048]; size_t size = H5Iget_name(g, full_group_path, 2048); - printf("Group \"%s\": Version %s Binsparse matrix. Format %s, %zu x %zu. " - "%zu stored values.\n", - full_group_path, version_string, format_string, nrows, ncols, nnz); + if (is_vector) { + printf("Group \"%s\": Version %s Binsparse vector. Format %s, length " + "%zu. %zu stored values.\n", + full_group_path, version_string, format_string, nrows, nnz); + } else { + cJSON* ncols_ = cJSON_GetArrayItem(shape_, 1); + assert(ncols_ != NULL); + size_t ncols = cJSON_GetNumberValue(ncols_); + printf("Group \"%s\": Version %s Binsparse matrix. Format %s, %zu x " + "%zu. %zu stored values.\n", + full_group_path, version_string, format_string, nrows, ncols, + nnz); + } cJSON* data_types = cJSON_GetObjectItemCaseSensitive(binsparse, "data_types"); diff --git a/include/binsparse/matrix_formats.h b/include/binsparse/matrix_formats.h index 367f199..78cdb07 100644 --- a/include/binsparse/matrix_formats.h +++ b/include/binsparse/matrix_formats.h @@ -6,6 +6,7 @@ #pragma once +#include #include typedef enum bsp_matrix_format_t { @@ -24,6 +25,11 @@ typedef enum bsp_matrix_format_t { BSP_INVALID_FORMAT = 21 } bsp_matrix_format_t; +static inline bool +bsp_matrix_format_is_vector(bsp_matrix_format_t format) { + return format == BSP_DVEC || format == BSP_CVEC; +} + static inline char* bsp_get_matrix_format_string(bsp_matrix_format_t format) { if (format == BSP_DVEC) { return (char*) "DVEC"; diff --git a/src/read_matrix.c b/src/read_matrix.c index 103851b..1fc3877 100644 --- a/src/read_matrix.c +++ b/src/read_matrix.c @@ -12,6 +12,7 @@ #include #include #include +#include #include static void bsp_prepare_hdf5_runtime(void) { @@ -22,6 +23,44 @@ static void bsp_prepare_hdf5_runtime(void) { } } +static bool bsp_parse_dimension(const cJSON* item, size_t* dimension) { + if (item == NULL || !cJSON_IsNumber(item)) { + return false; + } + + double value = cJSON_GetNumberValue(item); + if (!isfinite(value) || value < 0 || floor(value) != value || + value >= (double) SIZE_MAX) { + return false; + } + + *dimension = (size_t) value; + return true; +} + +static bsp_error_t bsp_parse_matrix_shape(const cJSON* binsparse, + bsp_matrix_format_t format, + size_t* nrows, size_t* ncols) { + cJSON* shape = cJSON_GetObjectItemCaseSensitive(binsparse, "shape"); + if (shape == NULL || !cJSON_IsArray(shape)) { + return BSP_ERROR_FORMAT; + } + + int expected_rank = bsp_matrix_format_is_vector(format) ? 1 : 2; + if (cJSON_GetArraySize(shape) != expected_rank || + !bsp_parse_dimension(cJSON_GetArrayItem(shape, 0), nrows)) { + return BSP_ERROR_FORMAT; + } + + if (expected_rank == 1) { + *ncols = 1; + } else if (!bsp_parse_dimension(cJSON_GetArrayItem(shape, 1), ncols)) { + return BSP_ERROR_FORMAT; + } + + return BSP_SUCCESS; +} + #if __STDC_VERSION__ >= 201112L bsp_error_t bsp_read_matrix_from_group_parallel(bsp_matrix_t* matrix, hid_t f, int num_threads) { @@ -69,20 +108,14 @@ bsp_error_t bsp_read_matrix_from_group_parallel(bsp_matrix_t* matrix, hid_t f, assert(nnz_ != NULL); size_t nnz = cJSON_GetNumberValue(nnz_); - cJSON* shape_ = cJSON_GetObjectItemCaseSensitive(binsparse, "shape"); - assert(shape_ != NULL); - - assert(cJSON_GetArraySize(shape_) == 2); - - cJSON* nrows_ = cJSON_GetArrayItem(shape_, 0); - assert(nrows_ != NULL); - - size_t nrows = cJSON_GetNumberValue(nrows_); - - cJSON* ncols_ = cJSON_GetArrayItem(shape_, 1); - assert(ncols_ != NULL); - - size_t ncols = cJSON_GetNumberValue(ncols_); + size_t nrows; + size_t ncols; + error = bsp_parse_matrix_shape(binsparse, format, &nrows, &ncols); + if (error != BSP_SUCCESS || (format == BSP_DVEC && nnz != nrows)) { + cJSON_Delete(j); + free(json_string); + return BSP_ERROR_FORMAT; + } matrix->nrows = nrows; matrix->ncols = ncols; @@ -231,32 +264,15 @@ bsp_error_t bsp_read_matrix_from_group_allocator(bsp_matrix_t* matrix, hid_t f, } size_t nnz = cJSON_GetNumberValue(nnz_); - cJSON* shape_ = cJSON_GetObjectItemCaseSensitive(binsparse, "shape"); - if (shape_ == NULL || !cJSON_IsArray(shape_) || - cJSON_GetArraySize(shape_) != 2) { + size_t nrows; + size_t ncols; + error = bsp_parse_matrix_shape(binsparse, format, &nrows, &ncols); + if (error != BSP_SUCCESS || (format == BSP_DVEC && nnz != nrows)) { cJSON_Delete(j); allocator.free(json_string); return BSP_ERROR_FORMAT; } - cJSON* nrows_ = cJSON_GetArrayItem(shape_, 0); - if (nrows_ == NULL || !cJSON_IsNumber(nrows_)) { - cJSON_Delete(j); - allocator.free(json_string); - return BSP_ERROR_FORMAT; - } - - size_t nrows = cJSON_GetNumberValue(nrows_); - - cJSON* ncols_ = cJSON_GetArrayItem(shape_, 1); - if (ncols_ == NULL || !cJSON_IsNumber(ncols_)) { - cJSON_Delete(j); - allocator.free(json_string); - return BSP_ERROR_FORMAT; - } - - size_t ncols = cJSON_GetNumberValue(ncols_); - matrix->nrows = nrows; matrix->ncols = ncols; matrix->nnz = nnz; diff --git a/src/write_matrix.c b/src/write_matrix.c index 38e38a6..fa1a26a 100644 --- a/src/write_matrix.c +++ b/src/write_matrix.c @@ -43,12 +43,10 @@ char* bsp_generate_json(bsp_matrix_t matrix, cJSON* user_json) { bsp_get_matrix_format_string(matrix.format)); cJSON* shape = cJSON_AddArrayToObject(binsparse, "shape"); - - cJSON* nrows = cJSON_CreateNumber(matrix.nrows); - cJSON* ncols = cJSON_CreateNumber(matrix.ncols); - - cJSON_AddItemToArray(shape, nrows); - cJSON_AddItemToArray(shape, ncols); + cJSON_AddItemToArray(shape, cJSON_CreateNumber(matrix.nrows)); + if (!bsp_matrix_format_is_vector(matrix.format)) { + cJSON_AddItemToArray(shape, cJSON_CreateNumber(matrix.ncols)); + } cJSON_AddNumberToObject(binsparse, "number_of_stored_values", matrix.nnz); @@ -101,6 +99,13 @@ char* bsp_generate_json(bsp_matrix_t matrix, cJSON* user_json) { bsp_error_t bsp_write_matrix_to_group_cjson(hid_t f, bsp_matrix_t matrix, cJSON* user_json, int compression_level) { + if (bsp_matrix_format_is_vector(matrix.format) && matrix.ncols != 1) { + return BSP_ERROR_INVALID_INPUT; + } + if (matrix.format == BSP_DVEC && matrix.nnz != matrix.nrows) { + return BSP_ERROR_INVALID_INPUT; + } + bsp_error_t error = bsp_write_array(f, (char*) "values", matrix.values, compression_level); if (error != BSP_SUCCESS) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 45ff378..bc81d56 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,4 +3,5 @@ # SPDX-License-Identifier: BSD-3-Clause add_subdirectory(bash) +add_subdirectory(c) add_subdirectory(julia) diff --git a/test/c/CMakeLists.txt b/test/c/CMakeLists.txt new file mode 100644 index 0000000..dfa65da --- /dev/null +++ b/test/c/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2026 Binsparse Developers +# +# SPDX-License-Identifier: BSD-3-Clause + +add_executable(dvec_shape_test dvec_shape_test.c) +target_link_libraries(dvec_shape_test binsparse) + +add_test( + NAME unit.dvec_shape + COMMAND dvec_shape_test ${CMAKE_CURRENT_BINARY_DIR}/dvec_shape_test.h5) diff --git a/test/c/dvec_shape_test.c b/test/c/dvec_shape_test.c new file mode 100644 index 0000000..1265bb8 --- /dev/null +++ b/test/c/dvec_shape_test.c @@ -0,0 +1,110 @@ +/* + * SPDX-FileCopyrightText: 2026 Binsparse Developers + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +static void check_shape(const char* filename) { + hid_t file = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); + assert(file >= 0); + + char* json_string = NULL; + assert(bsp_read_attribute(&json_string, file, "binsparse") == BSP_SUCCESS); + H5Fclose(file); + + cJSON* json = cJSON_Parse(json_string); + assert(json != NULL); + cJSON* binsparse = cJSON_GetObjectItemCaseSensitive(json, "binsparse"); + assert(cJSON_IsObject(binsparse)); + cJSON* shape = cJSON_GetObjectItemCaseSensitive(binsparse, "shape"); + assert(cJSON_IsArray(shape)); + assert(cJSON_GetArraySize(shape) == 1); + cJSON* dimension = cJSON_GetArrayItem(shape, 0); + assert(cJSON_IsNumber(dimension)); + assert(cJSON_GetNumberValue(dimension) == 3); + + cJSON_Delete(json); + free(json_string); +} + +static void check_read(const char* filename, bool parallel) { + bsp_matrix_t matrix; + bsp_error_t error; + if (parallel) { + error = bsp_read_matrix_parallel(&matrix, filename, NULL, 2); + } else { + error = bsp_read_matrix(&matrix, filename, NULL); + } + assert(error == BSP_SUCCESS); + assert(matrix.format == BSP_DVEC); + assert(matrix.nrows == 3); + assert(matrix.ncols == 1); + assert(matrix.nnz == 3); + assert(matrix.values.size == 3); + bsp_destroy_matrix_t(&matrix); +} + +static void create_invalid_shape(const char* filename) { + static const char* json = + "{\"binsparse\":{\"version\":\"0.1\",\"format\":\"DVEC\"," + "\"shape\":[3,1],\"number_of_stored_values\":3," + "\"data_types\":{\"values\":\"float64\"}}}"; + + hid_t file = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); + assert(file >= 0); + assert(bsp_write_attribute(file, "binsparse", json) == BSP_SUCCESS); + H5Fclose(file); +} + +int main(int argc, char** argv) { + assert(argc == 2); + const char* filename = argv[1]; + char invalid_filename[4096]; + int length = snprintf(invalid_filename, sizeof(invalid_filename), + "%s.invalid.h5", filename); + assert(length > 0 && (size_t) length < sizeof(invalid_filename)); + unlink(filename); + unlink(invalid_filename); + + bsp_matrix_t matrix; + bsp_construct_default_matrix_t(&matrix); + matrix.format = BSP_DVEC; + matrix.structure = BSP_GENERAL; + matrix.nrows = 3; + matrix.ncols = 1; + matrix.nnz = 3; + assert(bsp_construct_array_t(&matrix.values, 3, BSP_FLOAT64) == BSP_SUCCESS); + double* values = matrix.values.data; + values[0] = 1; + values[1] = 2; + values[2] = 3; + + assert(bsp_write_matrix(filename, matrix, NULL, NULL, 0) == BSP_SUCCESS); + check_shape(filename); + check_read(filename, false); + check_read(filename, true); + + matrix.ncols = 2; + assert(bsp_write_matrix(invalid_filename, matrix, NULL, NULL, 0) == + BSP_ERROR_INVALID_INPUT); + matrix.ncols = 1; + unlink(invalid_filename); + + create_invalid_shape(invalid_filename); + bsp_matrix_t invalid; + assert(bsp_read_matrix(&invalid, invalid_filename, NULL) == + BSP_ERROR_FORMAT); + assert(bsp_read_matrix_parallel(&invalid, invalid_filename, NULL, 2) == + BSP_ERROR_FORMAT); + + unlink(filename); + unlink(invalid_filename); + bsp_destroy_matrix_t(&matrix); + return 0; +} From 67a774acbd7e5f504ab58a090abb5afd8e21f759 Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Thu, 23 Jul 2026 20:41:42 +0000 Subject: [PATCH 4/5] Regularize MATLAB binding names --- bindings/matlab/Contents.m | 18 ++++----- bindings/matlab/README.md | 40 +++++++++---------- ...gs.m => binsparse_build_matlab_bindings.m} | 12 +++--- ...gs.m => binsparse_build_octave_bindings.m} | 15 +++---- ...rix_create.m => binsparse_create_struct.m} | 15 +++---- bindings/matlab/binsparse_from_ssmc.m | 6 ++- .../{bsp_matrix_info.m => binsparse_info.m} | 8 ++-- bindings/matlab/binsparse_read.c | 2 +- bindings/matlab/binsparse_read.m | 9 +++-- ...m_struct.m => binsparse_to_ssmc_problem.m} | 6 +-- bindings/matlab/binsparse_write.c | 3 +- bindings/matlab/binsparse_write.m | 9 +++-- ..._ssmc.m => binsparse_write_ssmc_problem.m} | 30 +++++++------- ...atrix_struct.m => test_binsparse_struct.m} | 18 ++++----- ...uct.m => test_binsparse_to_ssmc_problem.m} | 34 ++++++++-------- ....m => test_binsparse_write_ssmc_problem.m} | 34 ++++++++-------- 16 files changed, 133 insertions(+), 126 deletions(-) rename bindings/matlab/{build_matlab_bindings.m => binsparse_build_matlab_bindings.m} (93%) rename bindings/matlab/{build_octave_bindings.m => binsparse_build_octave_bindings.m} (92%) rename bindings/matlab/{bsp_matrix_create.m => binsparse_create_struct.m} (81%) rename bindings/matlab/{bsp_matrix_info.m => binsparse_info.m} (84%) rename bindings/matlab/{convert_to_problem_struct.m => binsparse_to_ssmc_problem.m} (98%) rename bindings/matlab/{generate_bsp_from_ssmc.m => binsparse_write_ssmc_problem.m} (86%) rename bindings/matlab/{test_bsp_matrix_struct.m => test_binsparse_struct.m} (81%) rename bindings/matlab/{test_convert_to_problem_struct.m => test_binsparse_to_ssmc_problem.m} (87%) rename bindings/matlab/{test_generate_bsp_from_ssmc.m => test_binsparse_write_ssmc_problem.m} (85%) diff --git a/bindings/matlab/Contents.m b/bindings/matlab/Contents.m index 2e9970d..f44becc 100644 --- a/bindings/matlab/Contents.m +++ b/bindings/matlab/Contents.m @@ -8,14 +8,14 @@ % binsparse_write_string_dataset - write an HDF5 UTF-8 string dataset % % MATLAB helpers: -% generate_bsp_from_ssmc - write an SSMC Problem to a Binsparse file -% convert_to_problem_struct - convert a Binsparse problem to an SSMC Problem -% bsp_matrix_create - create a Binsparse matrix struct -% bsp_matrix_info - display information about a Binsparse matrix struct +% binsparse_write_ssmc_problem - write an SSMC Problem to a Binsparse file +% binsparse_to_ssmc_problem - convert a Binsparse problem to an SSMC Problem +% binsparse_create_struct - create a Binsparse vector or matrix struct +% binsparse_info - display information about a Binsparse struct % % Build scripts: -% build_matlab_bindings - build all MEX functions with MATLAB's mex -% build_octave_bindings - build all MEX functions with Octave's mkoctfile +% binsparse_build_matlab_bindings - build all MEX functions with MATLAB's mex +% binsparse_build_octave_bindings - build all MEX functions with Octave's mkoctfile % compile_octave.sh - build the Octave MEX functions from the shell % % Tests: @@ -23,9 +23,9 @@ % test_binsparse_write - write and round-trip tests for binsparse_write % test_binsparse_from_ssmc - basic test for binsparse_from_ssmc % test_binsparse_minimize_roundtrip - SSMC conversion + type minimization test -% test_bsp_matrix_struct - tests for the Binsparse matrix struct helpers -% test_convert_to_problem_struct - tests for Binsparse Problem conversion -% test_generate_bsp_from_ssmc - end-to-end test for generate_bsp_from_ssmc +% test_binsparse_struct - tests for the Binsparse struct helpers +% test_binsparse_to_ssmc_problem - tests for Binsparse Problem conversion +% test_binsparse_write_ssmc_problem - end-to-end SSMC writer test % test_binsparse_roundtrip_dir - round-trip every .h5 file in a directory % SPDX-FileCopyrightText: 2024 Binsparse Developers diff --git a/bindings/matlab/README.md b/bindings/matlab/README.md index 6a40a25..b0aee5d 100644 --- a/bindings/matlab/README.md +++ b/bindings/matlab/README.md @@ -50,14 +50,14 @@ mkoctfile --version 2. Build the MEX functions: ```matlab - build_matlab_bindings() + binsparse_build_matlab_bindings() ``` 3. Test the installation: ```matlab test_binsparse_read() test_binsparse_write() - test_generate_bsp_from_ssmc() + test_binsparse_write_ssmc_problem() ``` #### Option 2: Octave (from within Octave) @@ -69,14 +69,14 @@ mkoctfile --version 2. Build the MEX functions: ```octave - build_octave_bindings() + binsparse_build_octave_bindings() ``` 3. Test the installation: ```octave test_binsparse_read() test_binsparse_write() - test_generate_bsp_from_ssmc() + test_binsparse_write_ssmc_problem() ``` #### Option 3: Octave (from command line) @@ -95,7 +95,7 @@ mkoctfile --version ```bash octave --eval "test_binsparse_read()" octave --eval "test_binsparse_write()" - octave --eval "test_generate_bsp_from_ssmc()" + octave --eval "test_binsparse_write_ssmc_problem()" ``` ## Usage Examples @@ -157,13 +157,13 @@ Problem.title = 'Test Matrix'; Problem.kind = 'artificial/test'; % Write directly from SuiteSparse format to Binsparse -generate_bsp_from_ssmc(Problem, 'output.bsp.h5'); +binsparse_write_ssmc_problem(Problem, 'output.bsp.h5'); % Write with an explicit sparse format ('COO', 'COOR', 'CSC', or 'CSR') -generate_bsp_from_ssmc(Problem, 'output.bsp.h5', 'CSC'); +binsparse_write_ssmc_problem(Problem, 'output.bsp.h5', 'CSC'); % Write with a format and gzip compression level (0-9) -generate_bsp_from_ssmc(Problem, 'output.bsp.h5', 'COO', 6); +binsparse_write_ssmc_problem(Problem, 'output.bsp.h5', 'COO', 6); ``` ### Error Handling @@ -188,20 +188,20 @@ end | `binsparse_minimize_types.c` | MEX function minimizing value/index types in a Binsparse struct | | `binsparse_write_string_dataset.c` | MEX function writing HDF5 UTF-8 string datasets | | `matlab_bsp_helpers.h` | Shared MATLAB/Binsparse conversion helpers for the MEX sources | -| `generate_bsp_from_ssmc.m` | Write a full SSMC Problem struct to one Binsparse file | -| `convert_to_problem_struct.m` | Convert Binsparse data back to an SSMC Problem struct | -| `bsp_matrix_create.m` | Utility function for creating matrix structs | -| `bsp_matrix_info.m` | Utility function for displaying matrix information | -| `build_matlab_bindings.m` | Build script for all MATLAB MEX functions | -| `build_octave_bindings.m` | Build script for all Octave MEX functions | +| `binsparse_write_ssmc_problem.m` | Write a full SSMC Problem struct to one Binsparse file | +| `binsparse_to_ssmc_problem.m` | Convert Binsparse data back to an SSMC Problem struct | +| `binsparse_create_struct.m` | Utility function for creating Binsparse structs | +| `binsparse_info.m` | Utility function for displaying Binsparse information | +| `binsparse_build_matlab_bindings.m` | Build script for all MATLAB MEX functions | +| `binsparse_build_octave_bindings.m` | Build script for all Octave MEX functions | | `compile_octave.sh` | Shell script for building Octave MEX functions | | `test_binsparse_read.m` | Test script for read functionality | | `test_binsparse_write.m` | Test script for write functionality | | `test_binsparse_from_ssmc.m` | Test script for SSMC conversion | | `test_binsparse_minimize_roundtrip.m` | Test script for type minimization | -| `test_bsp_matrix_struct.m` | Test script for the matrix struct helpers | -| `test_convert_to_problem_struct.m` | Test script for Problem conversion | -| `test_generate_bsp_from_ssmc.m` | End-to-end test for the SSMC writer | +| `test_binsparse_struct.m` | Test script for the struct helpers | +| `test_binsparse_to_ssmc_problem.m` | Test script for Problem conversion | +| `test_binsparse_write_ssmc_problem.m` | End-to-end test for the SSMC writer | | `test_binsparse_roundtrip_dir.m` | Round-trip every .h5 file in a directory | | `Contents.m` | Directory listing for MATLAB's `help` | | `README.md` | This documentation file | @@ -240,7 +240,7 @@ To add new Binsparse functionality: 1. Create a new `.c` file with MEX function structure 2. Include `` and relevant headers -3. Add the filename to `mex_files` list in `build_matlab_bindings.m` +3. Add the filename to `mex_files` list in `binsparse_build_matlab_bindings.m` 4. Create corresponding test functions ### Example MEX Function Template @@ -282,8 +282,8 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { - Check that `../../include/binsparse/binsparse.h` exists 3. **Compilation errors** - - **MATLAB**: Try building with verbose output: `build_matlab_bindings('verbose')` - - **Octave**: Try building with verbose output: `build_octave_bindings('verbose')` or `./compile_octave.sh --verbose` + - **MATLAB**: Try building with verbose output: `binsparse_build_matlab_bindings('verbose')` + - **Octave**: Try building with verbose output: `binsparse_build_octave_bindings('verbose')` or `./compile_octave.sh --verbose` - Check compiler compatibility with your MATLAB/Octave version ### Platform-Specific Notes diff --git a/bindings/matlab/build_matlab_bindings.m b/bindings/matlab/binsparse_build_matlab_bindings.m similarity index 93% rename from bindings/matlab/build_matlab_bindings.m rename to bindings/matlab/binsparse_build_matlab_bindings.m index e31b6dd..1bed03f 100644 --- a/bindings/matlab/build_matlab_bindings.m +++ b/bindings/matlab/binsparse_build_matlab_bindings.m @@ -1,14 +1,14 @@ -function build_matlab_bindings(varargin) -% BUILD_MATLAB_BINDINGS - Build Binsparse MATLAB MEX functions +function binsparse_build_matlab_bindings(varargin) +% BINSPARSE_BUILD_MATLAB_BINDINGS - Build Binsparse MATLAB MEX functions % % This script provides a simple interface to build MATLAB bindings % for the Binsparse library. It automatically detects include paths % and sets up the compilation environment. % % Usage: -% build_matlab_bindings() % Build all available MEX functions -% build_matlab_bindings('verbose') % Build with verbose output -% build_matlab_bindings('clean') % Clean compiled MEX files +% binsparse_build_matlab_bindings() % Build all available MEX functions +% binsparse_build_matlab_bindings('verbose') % Build with verbose output +% binsparse_build_matlab_bindings('clean') % Clean compiled MEX files % % Prerequisites: % - MATLAB with working MEX compiler (run 'mex -setup' if needed) @@ -56,7 +56,7 @@ function build_matlab_bindings(varargin) fprintf('Run the test functions to verify the installation:\n'); fprintf(' test_binsparse_read()\n'); fprintf(' test_binsparse_write()\n'); -fprintf(' test_generate_bsp_from_ssmc()\n\n'); +fprintf(' test_binsparse_write_ssmc_problem()\n\n'); end diff --git a/bindings/matlab/build_octave_bindings.m b/bindings/matlab/binsparse_build_octave_bindings.m similarity index 92% rename from bindings/matlab/build_octave_bindings.m rename to bindings/matlab/binsparse_build_octave_bindings.m index 8ac8f60..d376b66 100644 --- a/bindings/matlab/build_octave_bindings.m +++ b/bindings/matlab/binsparse_build_octave_bindings.m @@ -1,14 +1,14 @@ -function build_octave_bindings(varargin) -% BUILD_OCTAVE_BINDINGS - Build Binsparse Octave MEX functions +function binsparse_build_octave_bindings(varargin) +% BINSPARSE_BUILD_OCTAVE_BINDINGS - Build Binsparse Octave MEX functions % % This script provides a simple interface to build Octave bindings % for the Binsparse library using mkoctfile. It automatically detects % include paths and sets up the compilation environment. % % Usage: -% build_octave_bindings() % Build all available MEX functions -% build_octave_bindings('verbose') % Build with verbose output -% build_octave_bindings('clean') % Clean compiled MEX files +% binsparse_build_octave_bindings() % Build all available MEX functions +% binsparse_build_octave_bindings('verbose') % Build with verbose output +% binsparse_build_octave_bindings('clean') % Clean compiled MEX files % % Prerequisites: % - GNU Octave with mkoctfile (usually included with Octave) @@ -36,7 +36,8 @@ function build_octave_bindings(varargin) % Check if we're running in Octave if ~is_octave() - warning('This script is designed for Octave. For MATLAB, use build_matlab_bindings.m'); + warning(['This script is designed for Octave. For MATLAB, use ' ... + 'binsparse_build_matlab_bindings.m']); end % Check mkoctfile availability @@ -64,7 +65,7 @@ function build_octave_bindings(varargin) fprintf('Run the test functions to verify the installation:\n'); fprintf(' test_binsparse_read()\n'); fprintf(' test_binsparse_write()\n'); -fprintf(' test_generate_bsp_from_ssmc()\n\n'); +fprintf(' test_binsparse_write_ssmc_problem()\n\n'); end diff --git a/bindings/matlab/bsp_matrix_create.m b/bindings/matlab/binsparse_create_struct.m similarity index 81% rename from bindings/matlab/bsp_matrix_create.m rename to bindings/matlab/binsparse_create_struct.m index dc6f609..c4749c9 100644 --- a/bindings/matlab/bsp_matrix_create.m +++ b/bindings/matlab/binsparse_create_struct.m @@ -1,12 +1,12 @@ -function matrix = bsp_matrix_create(varargin) -% BSP_MATRIX_CREATE - Create a Binsparse matrix struct +function matrix = binsparse_create_struct(varargin) +% BINSPARSE_CREATE_STRUCT - Create a Binsparse vector or matrix struct % % Creates a MATLAB struct analogous to the C bsp_matrix_t structure. % The struct contains four native MATLAB arrays and metadata fields. % % Usage: -% matrix = bsp_matrix_create() % Empty matrix -% matrix = bsp_matrix_create(values, indices_0, indices_1, pointers_to_1, ... +% matrix = binsparse_create_struct() % Empty struct +% matrix = binsparse_create_struct(values, indices_0, indices_1, pointers_to_1, ... % nrows, ncols, nnz, is_iso, format, structure) % % Fields: @@ -23,13 +23,14 @@ % % Example: % % Create empty matrix -% matrix = bsp_matrix_create(); +% matrix = binsparse_create_struct(); % % % Create COO matrix % values = [1.0, 2.0, 3.0]; % rows = [1, 2, 3]; % cols = [1, 2, 3]; -% matrix = bsp_matrix_create(values, rows, cols, [], 3, 3, 3, false, 'COO', 'general'); +% matrix = binsparse_create_struct(values, rows, cols, [], ... +% 3, 3, 3, false, 'COO', 'general'); % SPDX-FileCopyrightText: 2024 Binsparse Developers % @@ -64,7 +65,7 @@ 'structure', char(varargin{10})); else - error('bsp_matrix_create:InvalidArgs', ... + error('binsparse_create_struct:InvalidArgs', ... 'Expected 0 or 10 arguments, got %d', nargin); end diff --git a/bindings/matlab/binsparse_from_ssmc.m b/bindings/matlab/binsparse_from_ssmc.m index 31bce1c..d0f54ea 100644 --- a/bindings/matlab/binsparse_from_ssmc.m +++ b/bindings/matlab/binsparse_from_ssmc.m @@ -20,7 +20,8 @@ % Zeros = sparse (2, 3, 1, 3, 3) ; % matrix = binsparse_from_ssmc (A, Zeros, 'CSC') ; % -% See also binsparse_write, binsparse_minimize_types, generate_bsp_from_ssmc. +% See also binsparse_write, binsparse_minimize_types, +% binsparse_write_ssmc_problem. % SPDX-FileCopyrightText: 2024 Binsparse Developers % @@ -29,4 +30,5 @@ % This .m file provides the help text for the binsparse_from_ssmc MEX % function. -error('binsparse_from_ssmc mexFunction not found; compile with build_matlab_bindings first'); +error(['binsparse_from_ssmc mexFunction not found; compile with ' ... + 'binsparse_build_matlab_bindings first']); diff --git a/bindings/matlab/bsp_matrix_info.m b/bindings/matlab/binsparse_info.m similarity index 84% rename from bindings/matlab/bsp_matrix_info.m rename to bindings/matlab/binsparse_info.m index c2f0ed6..3490463 100644 --- a/bindings/matlab/bsp_matrix_info.m +++ b/bindings/matlab/binsparse_info.m @@ -1,8 +1,8 @@ -function bsp_matrix_info(matrix) -% BSP_MATRIX_INFO - Display information about a Binsparse matrix struct +function binsparse_info(matrix) +% BINSPARSE_INFO - Display information about a Binsparse vector or matrix struct % % Usage: -% bsp_matrix_info(matrix) +% binsparse_info(matrix) % % Displays: % - Matrix dimensions and number of non-zeros @@ -14,7 +14,7 @@ function bsp_matrix_info(matrix) % SPDX-License-Identifier: BSD-3-Clause if ~isstruct(matrix) - error('bsp_matrix_info:InvalidInput', 'Input must be a struct'); + error('binsparse_info:InvalidInput', 'Input must be a struct'); end % Display basic matrix information diff --git a/bindings/matlab/binsparse_read.c b/bindings/matlab/binsparse_read.c index 6740797..107b8e6 100644 --- a/bindings/matlab/binsparse_read.c +++ b/bindings/matlab/binsparse_read.c @@ -8,7 +8,7 @@ * binsparse_read.c - Read Binsparse matrices into MATLAB * * This MEX function reads Binsparse format matrices and returns them - * as MATLAB structs compatible with bsp_matrix_create. + * as MATLAB structs compatible with binsparse_create_struct. * * Usage in MATLAB/Octave: * matrix = binsparse_read(filename) diff --git a/bindings/matlab/binsparse_read.m b/bindings/matlab/binsparse_read.m index aba9650..6903a11 100644 --- a/bindings/matlab/binsparse_read.m +++ b/bindings/matlab/binsparse_read.m @@ -9,16 +9,16 @@ % group optionally names an HDF5 group within the file that holds a % Binsparse matrix. The result is a raw Binsparse matrix struct with the % fields values, indices_0, indices_1, pointers_to_1, nrows, ncols, nnz, -% is_iso, format, and structure, as documented in bsp_matrix_create. +% is_iso, format, and structure, as documented in binsparse_create_struct. % Value and index arrays keep the types stored in the file; indices are -% 0-based. Use convert_to_problem_struct to build a MATLAB-native +% 0-based. Use binsparse_to_ssmc_problem to build a MATLAB-native % SuiteSparse Problem struct from Binsparse data. % % Example: % matrix = binsparse_read ('west0067.bsp.h5') ; % b = binsparse_read ('west0067.bsp.h5', 'b') ; % -% See also binsparse_write, bsp_matrix_create, convert_to_problem_struct. +% See also binsparse_write, binsparse_create_struct, binsparse_to_ssmc_problem. % SPDX-FileCopyrightText: 2024 Binsparse Developers % @@ -26,4 +26,5 @@ % This .m file provides the help text for the binsparse_read MEX function. -error ('binsparse_read mexFunction not found; compile with build_matlab_bindings first') ; +error (['binsparse_read mexFunction not found; compile with ' ... + 'binsparse_build_matlab_bindings first']) ; diff --git a/bindings/matlab/convert_to_problem_struct.m b/bindings/matlab/binsparse_to_ssmc_problem.m similarity index 98% rename from bindings/matlab/convert_to_problem_struct.m rename to bindings/matlab/binsparse_to_ssmc_problem.m index 4779cbf..3309216 100644 --- a/bindings/matlab/convert_to_problem_struct.m +++ b/bindings/matlab/binsparse_to_ssmc_problem.m @@ -1,7 +1,7 @@ -function Problem = convert_to_problem_struct(bsp_problem) -%CONVERT_TO_PROBLEM_STRUCT convert a Binsparse problem to an SSMC Problem +function Problem = binsparse_to_ssmc_problem(bsp_problem) +%BINSPARSE_TO_SSMC_PROBLEM convert a Binsparse problem to an SSMC Problem % -% Problem = convert_to_problem_struct(bsp_problem) +% Problem = binsparse_to_ssmc_problem(bsp_problem) % % bsp_problem is an in-memory representation of one SuiteSparse Matrix % Collection problem. Its A, b, x, and aux numeric entries are raw structs diff --git a/bindings/matlab/binsparse_write.c b/bindings/matlab/binsparse_write.c index e3d839d..14da9a8 100644 --- a/bindings/matlab/binsparse_write.c +++ b/bindings/matlab/binsparse_write.c @@ -7,7 +7,8 @@ /** * binsparse_write.c - Write MATLAB structs to Binsparse format * - * This MEX function writes MATLAB structs (compatible with bsp_matrix_create) + * This MEX function writes MATLAB structs (compatible with + * binsparse_create_struct) * to Binsparse format files. * * Usage in MATLAB/Octave: diff --git a/bindings/matlab/binsparse_write.m b/bindings/matlab/binsparse_write.m index c494ff1..7d1c661 100644 --- a/bindings/matlab/binsparse_write.m +++ b/bindings/matlab/binsparse_write.m @@ -9,7 +9,7 @@ function binsparse_write(filename, matrix, group, json_string, compression_level % % filename is the path of the Binsparse HDF5 file to create (typically % *.bsp.h5), and matrix is a Binsparse matrix struct as returned by -% binsparse_read, binsparse_from_ssmc, or bsp_matrix_create. The optional +% binsparse_read, binsparse_from_ssmc, or binsparse_create_struct. The optional % group names an HDF5 group to write into ('' or [ ] writes to the file % root). The optional json_string is a JSON object whose keys are merged % into the Binsparse descriptor as user metadata. The optional @@ -21,12 +21,13 @@ function binsparse_write(filename, matrix, group, json_string, compression_level % binsparse_write ('example.bsp.h5', matrix) ; % binsparse_write ('example.bsp.h5', matrix, 'b', '{"role": "b"}', 9) ; % -% See also binsparse_read, binsparse_from_ssmc, bsp_matrix_create, -% generate_bsp_from_ssmc. +% See also binsparse_read, binsparse_from_ssmc, binsparse_create_struct, +% binsparse_write_ssmc_problem. % SPDX-FileCopyrightText: 2024 Binsparse Developers % SPDX-License-Identifier: BSD-3-Clause % This .m file provides the help text for the binsparse_write MEX function. -error ('binsparse_write mexFunction not found; compile with build_matlab_bindings first') ; +error (['binsparse_write mexFunction not found; compile with ' ... + 'binsparse_build_matlab_bindings first']) ; diff --git a/bindings/matlab/generate_bsp_from_ssmc.m b/bindings/matlab/binsparse_write_ssmc_problem.m similarity index 86% rename from bindings/matlab/generate_bsp_from_ssmc.m rename to bindings/matlab/binsparse_write_ssmc_problem.m index 1a3873b..bb1b11e 100644 --- a/bindings/matlab/generate_bsp_from_ssmc.m +++ b/bindings/matlab/binsparse_write_ssmc_problem.m @@ -1,9 +1,9 @@ -function generate_bsp_from_ssmc(problem, output_filename, format, compression_level) -% GENERATE_BSP_FROM_SSMC - Generate a Binsparse file from an SSMC problem +function binsparse_write_ssmc_problem(problem, output_filename, format, compression_level) +% BINSPARSE_WRITE_SSMC_PROBLEM - Write an SSMC problem to a Binsparse file % % Usage: -% generate_bsp_from_ssmc(problem, output_filename) -% generate_bsp_from_ssmc(problem, output_filename, format, compression_level) +% binsparse_write_ssmc_problem(problem, output_filename) +% binsparse_write_ssmc_problem(problem, output_filename, format, compression_level) % % Defaults: % format = 'COO' @@ -14,19 +14,19 @@ function generate_bsp_from_ssmc(problem, output_filename, format, compression_le % SPDX-License-Identifier: BSD-3-Clause if nargin < 2 - error('generate_bsp_from_ssmc:InvalidArgs', ... - ['Usage: generate_bsp_from_ssmc(problem, output_filename ' ... + error('binsparse_write_ssmc_problem:InvalidArgs', ... + ['Usage: binsparse_write_ssmc_problem(problem, output_filename ' ... '[, format [, compression_level]])']); end if ~isstruct(problem) || ~isscalar(problem) - error('generate_bsp_from_ssmc:InvalidProblem', ... + error('binsparse_write_ssmc_problem:InvalidProblem', ... 'Problem must be a scalar struct'); end if ~ischar(output_filename) || size(output_filename, 1) ~= 1 || ... isempty(output_filename) - error('generate_bsp_from_ssmc:InvalidFilename', ... + error('binsparse_write_ssmc_problem:InvalidFilename', ... 'Output filename must be a nonempty character vector'); end @@ -35,12 +35,12 @@ function generate_bsp_from_ssmc(problem, output_filename, format, compression_le end if ~ischar(format) || size(format, 1) ~= 1 - error('generate_bsp_from_ssmc:InvalidFormat', ... + error('binsparse_write_ssmc_problem:InvalidFormat', ... 'Format must be a character vector'); end format = upper(format); if ~any(strcmp(format, {'COO', 'COOR', 'CSC', 'CSR'})) - error('generate_bsp_from_ssmc:InvalidFormat', ... + error('binsparse_write_ssmc_problem:InvalidFormat', ... 'Unsupported sparse format: %s', format); end @@ -52,7 +52,7 @@ function generate_bsp_from_ssmc(problem, output_filename, format, compression_le ~isscalar(compression_level) || ~isfinite(compression_level) || ... compression_level ~= fix(compression_level) || ... compression_level < 0 || compression_level > 9 - error('generate_bsp_from_ssmc:InvalidCompression', ... + error('binsparse_write_ssmc_problem:InvalidCompression', ... 'Compression level must be an integer from 0 to 9'); end @@ -63,12 +63,12 @@ function generate_bsp_from_ssmc(problem, output_filename, format, compression_le end if ~isstruct(P) || ~isscalar(P) - error('generate_bsp_from_ssmc:InvalidProblem', ... + error('binsparse_write_ssmc_problem:InvalidProblem', ... 'Problem must contain a scalar Problem struct'); end if ~isfield(P, 'A') - error('generate_bsp_from_ssmc:MissingMatrix', 'Problem.A is required'); + error('binsparse_write_ssmc_problem:MissingMatrix', 'Problem.A is required'); end % Primary matrix @@ -181,7 +181,7 @@ function handle_aux_entry(name, value, output_filename, format, compression_leve function write_string_dataset(output_filename, name, value) if exist('binsparse_write_string_dataset', 'file') ~= 3 - error('generate_bsp_from_ssmc:MissingStringWriter', ... + error('binsparse_write_ssmc_problem:MissingStringWriter', ... 'BSP text output requires binsparse_write_string_dataset on the path'); end @@ -200,7 +200,7 @@ function write_string_dataset(output_filename, name, value) elseif iscellstr(value) value = value(:); else - error('generate_bsp_from_ssmc:InvalidStringValue', ... + error('binsparse_write_ssmc_problem:InvalidStringValue', ... 'Text aux value must be char, string, or cellstr'); end diff --git a/bindings/matlab/test_bsp_matrix_struct.m b/bindings/matlab/test_binsparse_struct.m similarity index 81% rename from bindings/matlab/test_bsp_matrix_struct.m rename to bindings/matlab/test_binsparse_struct.m index a129593..3ce06a3 100644 --- a/bindings/matlab/test_bsp_matrix_struct.m +++ b/bindings/matlab/test_binsparse_struct.m @@ -1,5 +1,5 @@ -function test_bsp_matrix_struct() -% TEST_BSP_MATRIX_STRUCT - Test the Binsparse matrix struct functionality +function test_binsparse_struct() +% TEST_BINSPARSE_STRUCT - Test the Binsparse struct functionality % % This function demonstrates and tests the basic MATLAB struct % that mirrors the C bsp_matrix_t structure. @@ -13,9 +13,9 @@ function test_bsp_matrix_struct() try % Test 1: Create empty matrix fprintf('Test 1: Creating empty matrix\n'); - empty_matrix = bsp_matrix_create(); + empty_matrix = binsparse_create_struct(); fprintf('Empty matrix created successfully\n'); - bsp_matrix_info(empty_matrix); + binsparse_info(empty_matrix); fprintf('\n'); % Test 2: Create simple COO matrix @@ -26,10 +26,10 @@ function test_bsp_matrix_struct() cols = uint64([0, 1, 2]); % 0-based indexing like C pointers = uint64([]); % Empty for COO format - coo_matrix = bsp_matrix_create(values, rows, cols, pointers, ... + coo_matrix = binsparse_create_struct(values, rows, cols, pointers, ... 3, 3, 3, false, 'COO', 'general'); fprintf('COO matrix created successfully\n'); - bsp_matrix_info(coo_matrix); + binsparse_info(coo_matrix); fprintf('\n'); % Test 3: Create CSR matrix @@ -40,10 +40,10 @@ function test_bsp_matrix_struct() csr_rows = uint64([]); % Not used in CSR csr_ptrs = uint64([0, 1, 2, 3]); % Row pointers - csr_matrix = bsp_matrix_create(csr_values, csr_rows, csr_cols, csr_ptrs, ... + csr_matrix = binsparse_create_struct(csr_values, csr_rows, csr_cols, csr_ptrs, ... 3, 3, 3, false, 'CSR', 'general'); fprintf('CSR matrix created successfully\n'); - bsp_matrix_info(csr_matrix); + binsparse_info(csr_matrix); fprintf('\n'); % Test 4: Test field access @@ -57,7 +57,7 @@ function test_bsp_matrix_struct() % Test 5: Test error handling fprintf('Test 5: Testing error handling\n'); try - invalid_matrix = bsp_matrix_create(1, 2, 3); % Wrong number of args + invalid_matrix = binsparse_create_struct(1, 2, 3); % Wrong number of args fprintf('FAILED - Should have thrown error\n'); catch ME fprintf('Successfully caught error: %s\n', ME.message); diff --git a/bindings/matlab/test_convert_to_problem_struct.m b/bindings/matlab/test_binsparse_to_ssmc_problem.m similarity index 87% rename from bindings/matlab/test_convert_to_problem_struct.m rename to bindings/matlab/test_binsparse_to_ssmc_problem.m index 46b8092..6f57293 100644 --- a/bindings/matlab/test_convert_to_problem_struct.m +++ b/bindings/matlab/test_binsparse_to_ssmc_problem.m @@ -1,5 +1,5 @@ -function test_convert_to_problem_struct -%TEST_CONVERT_TO_PROBLEM_STRUCT test in-memory Binsparse Problem conversion +function test_binsparse_to_ssmc_problem +%TEST_BINSPARSE_TO_SSMC_PROBLEM test in-memory Binsparse Problem conversion % SPDX-FileCopyrightText: 2026 Binsparse Developers % @@ -19,7 +19,7 @@ formats = matrix_formats(rows, cols, values, 3, 3); for k = 1:numel(formats) raw = struct('metadata', metadata, 'A', formats{k}); - Problem = convert_to_problem_struct(raw); + Problem = binsparse_to_ssmc_problem(raw); assert(isequal(Problem.A, expected), ... 'A mismatch for %s', formats{k}.format); assert(isequal(Problem.Zeros, expected_zeros), ... @@ -29,34 +29,34 @@ raw = struct('metadata', metadata, ... 'A', make_matrix(0, rows, cols, [], 3, 3, 'COO', true, 'general')); -Problem = convert_to_problem_struct(raw); +Problem = binsparse_to_ssmc_problem(raw); assert(nnz(Problem.A) == 0); assert(isequal(Problem.Zeros, sparse(rows + 1, cols + 1, 1, 3, 3))); lower = make_matrix([1; 2; 0; 3], [0; 1; 1; 2], [0; 0; 1; 2], ... [], 3, 3, 'COO', false, 'symmetric_lower'); raw = struct('metadata', metadata, 'A', lower); -Problem = convert_to_problem_struct(raw); +Problem = binsparse_to_ssmc_problem(raw); assert(isequal(Problem.A, sparse([1 2 1 3], [1 1 2 3], [1 2 2 3], 3, 3))); assert(isequal(Problem.Zeros, sparse(2, 2, 1, 3, 3))); hermitian = make_matrix([1; 2+3i; 4], [0; 1; 1], [0; 0; 1], ... [], 2, 2, 'COO', false, 'hermitian_lower'); raw = struct('metadata', metadata, 'A', hermitian); -Problem = convert_to_problem_struct(raw); +Problem = binsparse_to_ssmc_problem(raw); assert(isequal(Problem.A, sparse([1 2-3i; 2+3i 4]))); skew = make_matrix([5; 0], [1; 1], [0; 1], ... [], 2, 2, 'COO', false, 'skew_symmetric_lower'); raw = struct('metadata', metadata, 'A', skew); -Problem = convert_to_problem_struct(raw); +Problem = binsparse_to_ssmc_problem(raw); assert(isequal(Problem.A, sparse([0 -5; 5 0]))); assert(isequal(Problem.Zeros, sparse(2, 2, 1, 2, 2))); cvec = make_matrix([8; 0], [0; 2], [], [], ... 3, 1, 'CVEC', false, 'general'); raw = struct('metadata', metadata, 'A', cvec); -Problem = convert_to_problem_struct(raw); +Problem = binsparse_to_ssmc_problem(raw); assert(isequal(Problem.A, sparse(1, 1, 8, 3, 1))); assert(isequal(Problem.Zeros, sparse(3, 1, 1, 3, 1))); @@ -67,7 +67,7 @@ raw.aux.seq_1 = dense_matrix([5; 6], 2, 1, 'DVEC'); raw.aux.seq_2 = dense_matrix([7; 8], 2, 1, 'DVEC'); raw.aux.label = {'abc'; 'def'}; -Problem = convert_to_problem_struct(raw); +Problem = binsparse_to_ssmc_problem(raw); assert(isequal(Problem.b, [10; 20; 30])); assert(isequal(Problem.x, [1 2; 3 4])); assert(iscell(Problem.aux.seq) && numel(Problem.aux.seq) == 2); @@ -76,39 +76,39 @@ dmat = dense_matrix([1; 2; 3; 4; 5; 6], 2, 3, 'DMAT'); raw = struct('metadata', metadata, 'A', formats{1}, 'b', dmat); -Problem = convert_to_problem_struct(raw); +Problem = binsparse_to_ssmc_problem(raw); assert(isequal(Problem.b, [1 2 3; 4 5 6])); bad = formats{1}; bad.indices_1([1 2]) = bad.indices_1([2 1]); -assert_throws(@() convert_to_problem_struct( ... +assert_throws(@() binsparse_to_ssmc_problem( ... struct('metadata', metadata, 'A', bad)), 'BinSparse:InvalidMatrix'); bad = formats{3}; bad.pointers_to_1 = uint64([0; 3; 2; 5]); -assert_throws(@() convert_to_problem_struct( ... +assert_throws(@() binsparse_to_ssmc_problem( ... struct('metadata', metadata, 'A', bad)), 'BinSparse:InvalidMatrix'); bad = formats{5}; bad.pointers_to_1 = uint64([0; 2; 2; 5]); -assert_throws(@() convert_to_problem_struct( ... +assert_throws(@() binsparse_to_ssmc_problem( ... struct('metadata', metadata, 'A', bad)), 'BinSparse:InvalidMatrix'); bad = formats{1}; bad.format = 'CUSTOM'; -assert_throws(@() convert_to_problem_struct( ... +assert_throws(@() binsparse_to_ssmc_problem( ... struct('metadata', metadata, 'A', bad)), 'BinSparse:UnsupportedFormat'); bad = dense_matrix((1:6).', 2, 3, 'DMATC'); -assert_throws(@() convert_to_problem_struct( ... +assert_throws(@() binsparse_to_ssmc_problem( ... struct('metadata', metadata, 'A', bad)), 'BinSparse:InvalidProblem'); bad = make_matrix(uint64(9007199254740992) + uint64(1), 0, 0, [], ... 1, 1, 'COO', false, 'general'); -assert_throws(@() convert_to_problem_struct( ... +assert_throws(@() binsparse_to_ssmc_problem( ... struct('metadata', metadata, 'A', bad)), 'BinSparse:InexactValue'); -fprintf('test_convert_to_problem_struct: all tests passed\n'); +fprintf('test_binsparse_to_ssmc_problem: all tests passed\n'); end diff --git a/bindings/matlab/test_generate_bsp_from_ssmc.m b/bindings/matlab/test_binsparse_write_ssmc_problem.m similarity index 85% rename from bindings/matlab/test_generate_bsp_from_ssmc.m rename to bindings/matlab/test_binsparse_write_ssmc_problem.m index 2f607b4..7b6824d 100644 --- a/bindings/matlab/test_generate_bsp_from_ssmc.m +++ b/bindings/matlab/test_binsparse_write_ssmc_problem.m @@ -1,15 +1,15 @@ -function test_generate_bsp_from_ssmc() -% TEST_GENERATE_BSP_FROM_SSMC - End-to-end test for generate_bsp_from_ssmc +function test_binsparse_write_ssmc_problem() +% TEST_BINSPARSE_WRITE_SSMC_PROBLEM - Test binsparse_write_ssmc_problem % SPDX-FileCopyrightText: 2024 Binsparse Developers % % SPDX-License-Identifier: BSD-3-Clause -fprintf('=== Testing generate_bsp_from_ssmc ===\n\n'); +fprintf('=== Testing binsparse_write_ssmc_problem ===\n\n'); required = {'binsparse_from_ssmc', 'binsparse_minimize_types', ... 'binsparse_write', 'binsparse_read', ... - 'binsparse_write_string_dataset', 'generate_bsp_from_ssmc'}; + 'binsparse_write_string_dataset', 'binsparse_write_ssmc_problem'}; for i = 1:numel(required) if exist(required{i}, 'file') ~= 3 && exist(required{i}, 'file') ~= 2 error(['%s not found. Please compile MEX functions and ensure ' ... @@ -19,20 +19,20 @@ function test_generate_bsp_from_ssmc() % Check validation performed by the public SSMC writer. minimal_problem = struct('A', sparse(1)); -expect_error(@() generate_bsp_from_ssmc(), ... - 'generate_bsp_from_ssmc:InvalidArgs'); -expect_error(@() generate_bsp_from_ssmc(42, 'invalid.bsp.h5'), ... - 'generate_bsp_from_ssmc:InvalidProblem'); -expect_error(@() generate_bsp_from_ssmc(minimal_problem, 42), ... - 'generate_bsp_from_ssmc:InvalidFilename'); -expect_error(@() generate_bsp_from_ssmc( ... +expect_error(@() binsparse_write_ssmc_problem(), ... + 'binsparse_write_ssmc_problem:InvalidArgs'); +expect_error(@() binsparse_write_ssmc_problem(42, 'invalid.bsp.h5'), ... + 'binsparse_write_ssmc_problem:InvalidProblem'); +expect_error(@() binsparse_write_ssmc_problem(minimal_problem, 42), ... + 'binsparse_write_ssmc_problem:InvalidFilename'); +expect_error(@() binsparse_write_ssmc_problem( ... minimal_problem, 'invalid.bsp.h5', 'bad'), ... - 'generate_bsp_from_ssmc:InvalidFormat'); -expect_error(@() generate_bsp_from_ssmc(minimal_problem, ... + 'binsparse_write_ssmc_problem:InvalidFormat'); +expect_error(@() binsparse_write_ssmc_problem(minimal_problem, ... 'invalid.bsp.h5', 'COO', 10), ... - 'generate_bsp_from_ssmc:InvalidCompression'); -expect_error(@() generate_bsp_from_ssmc(struct(), 'invalid.bsp.h5'), ... - 'generate_bsp_from_ssmc:MissingMatrix'); + 'binsparse_write_ssmc_problem:InvalidCompression'); +expect_error(@() binsparse_write_ssmc_problem(struct(), 'invalid.bsp.h5'), ... + 'binsparse_write_ssmc_problem:MissingMatrix'); % Build synthetic problem Problem = struct(); @@ -63,7 +63,7 @@ function test_generate_bsp_from_ssmc() format = 'COO'; compression_level = 0; -generate_bsp_from_ssmc(problem, out_file, format, compression_level); +binsparse_write_ssmc_problem(problem, out_file, format, compression_level); % Read primary primary_bsp = binsparse_read(out_file); From a832f01b0192ea2af609c9b9cdeec1a84fcf534d Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Thu, 23 Jul 2026 21:09:13 +0000 Subject: [PATCH 5/5] Apply pre-commit formatting --- examples/bsp-ls.c | 7 +++---- include/binsparse/matrix_formats.h | 3 +-- test/c/dvec_shape_test.c | 3 +-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/examples/bsp-ls.c b/examples/bsp-ls.c index caf4ab5..8c30e5d 100644 --- a/examples/bsp-ls.c +++ b/examples/bsp-ls.c @@ -55,8 +55,8 @@ void print_group_info(hid_t g, const char* name) { cJSON* shape_ = cJSON_GetObjectItemCaseSensitive(binsparse, "shape"); assert(shape_ != NULL); - bool is_vector = bsp_matrix_format_is_vector( - bsp_get_matrix_format(format_string)); + bool is_vector = + bsp_matrix_format_is_vector(bsp_get_matrix_format(format_string)); assert(cJSON_GetArraySize(shape_) == (is_vector ? 1 : 2)); cJSON* nrows_ = cJSON_GetArrayItem(shape_, 0); @@ -77,8 +77,7 @@ void print_group_info(hid_t g, const char* name) { size_t ncols = cJSON_GetNumberValue(ncols_); printf("Group \"%s\": Version %s Binsparse matrix. Format %s, %zu x " "%zu. %zu stored values.\n", - full_group_path, version_string, format_string, nrows, ncols, - nnz); + full_group_path, version_string, format_string, nrows, ncols, nnz); } cJSON* data_types = diff --git a/include/binsparse/matrix_formats.h b/include/binsparse/matrix_formats.h index 78cdb07..3ac83e8 100644 --- a/include/binsparse/matrix_formats.h +++ b/include/binsparse/matrix_formats.h @@ -25,8 +25,7 @@ typedef enum bsp_matrix_format_t { BSP_INVALID_FORMAT = 21 } bsp_matrix_format_t; -static inline bool -bsp_matrix_format_is_vector(bsp_matrix_format_t format) { +static inline bool bsp_matrix_format_is_vector(bsp_matrix_format_t format) { return format == BSP_DVEC || format == BSP_CVEC; } diff --git a/test/c/dvec_shape_test.c b/test/c/dvec_shape_test.c index 1265bb8..1e0914a 100644 --- a/test/c/dvec_shape_test.c +++ b/test/c/dvec_shape_test.c @@ -98,8 +98,7 @@ int main(int argc, char** argv) { create_invalid_shape(invalid_filename); bsp_matrix_t invalid; - assert(bsp_read_matrix(&invalid, invalid_filename, NULL) == - BSP_ERROR_FORMAT); + assert(bsp_read_matrix(&invalid, invalid_filename, NULL) == BSP_ERROR_FORMAT); assert(bsp_read_matrix_parallel(&invalid, invalid_filename, NULL, 2) == BSP_ERROR_FORMAT);