From 695b8416db34a0067127f2db740c42cdc053f31e Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Wed, 15 May 2024 15:03:12 -0700 Subject: [PATCH 1/8] Add basic infrastructure for matrix conversions, COO -> CSR --- include/binsparse/binsparse.h | 1 + include/binsparse/convert_matrix.h | 82 +++++++++++++++++++ include/binsparse/detail/declamp_values.h | 29 +++++-- .../matrix_market/matrix_market_read.h | 18 +--- include/binsparse/types.h | 14 ++++ 5 files changed, 124 insertions(+), 20 deletions(-) create mode 100644 include/binsparse/convert_matrix.h diff --git a/include/binsparse/binsparse.h b/include/binsparse/binsparse.h index 2594127..7fa58e2 100644 --- a/include/binsparse/binsparse.h +++ b/include/binsparse/binsparse.h @@ -3,6 +3,7 @@ #define BINSPARSE_VERSION "0.1" #include +#include #include #include #include diff --git a/include/binsparse/convert_matrix.h b/include/binsparse/convert_matrix.h new file mode 100644 index 0000000..fc18dc2 --- /dev/null +++ b/include/binsparse/convert_matrix.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include + +bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, + bsp_matrix_format_t format) { + // If already in desired format, do nothing. + if (matrix.format == format) { + return matrix; + } + + if (format == BSP_COOR) { + // *Convert to COO* from another format. + assert(false); + } else { + // Convert to any another format. + + // Currently only support COOR -> X. + // If matrix is not COOR, convert to COOR. + if (matrix.format != BSP_COOR) { + bsp_matrix_t intermediate = bsp_convert_matrix(matrix, BSP_COOR); + bsp_matrix_t result = bsp_convert_matrix(intermediate, format); + bsp_destroy_matrix_t(intermediate); + return result; + } else { + if (format == BSP_CSR) { + // Convert COOR -> CSR + + bsp_matrix_t result = bsp_construct_default_matrix_t(); + + // TODO: consider whether to produce files with varying integer types + // for row indices, column indices, and offsets. + + size_t max_dim = + (matrix.nrows > matrix.ncols) ? matrix.nrows : matrix.ncols; + + size_t max_value = + (max_dim > matrix.values.size) ? max_dim : matrix.values.size; + + bsp_type_t value_type = matrix.values.type; + bsp_type_t index_type = bsp_pick_integer_type(max_value); + + result.values = bsp_construct_array_t(matrix.nnz, value_type); + result.indices_0 = bsp_construct_array_t(matrix.nnz, index_type); + result.pointers_to_1 = + bsp_construct_array_t(matrix.nrows + 1, index_type); + + bsp_array_t values = result.values; + bsp_array_t colind = result.indices_0; + bsp_array_t rowptr = result.pointers_to_1; + + bsp_array_write(rowptr, 0, 0); + + size_t r = 0; + size_t c = 0; + for (size_t c = 0; c < matrix.nnz; c++) { + bsp_array_awrite(values, c, matrix.values, c); + bsp_array_awrite(colind, c, matrix.indices_1, c); + + size_t j; + bsp_array_read(matrix.indices_0, c, j); + + while (r < j) { + assert(r + 1 <= matrix.nrows); + + bsp_array_write(rowptr, r + 1, c); + r++; + } + } + + for (; r < matrix.nrows; r++) { + bsp_array_write(rowptr, r + 1, matrix.nnz); + } + + return result; + } else { + assert(false); + } + } + } +} diff --git a/include/binsparse/detail/declamp_values.h b/include/binsparse/detail/declamp_values.h index 764603b..ca257ec 100644 --- a/include/binsparse/detail/declamp_values.h +++ b/include/binsparse/detail/declamp_values.h @@ -3,6 +3,17 @@ #include #include +double bsp_suitesparse_declamp_impl_(double value) { + const double HUGE_DOUBLE = 1e308; + if (value >= HUGE_DOUBLE) { + return INFINITY; + } else if (value <= -HUGE_DOUBLE) { + return -INFINITY; + } else { + return value; + } +} + // "Declamp" a matrix from the SuiteSparse Matrix Collection. // SuiteSparse Matrix Collection clamps values at 1e308 and -1e308 // for printing. These are almost all infinities or negative infinites. @@ -10,16 +21,22 @@ // This allows the `bsp_matrix_minimize_values` to properly minimize // matrices that have infinity values. void bsp_matrix_declamp_values(bsp_matrix_t matrix) { - const double HUGE_DOUBLE = 1e308; if (matrix.values.type == BSP_FLOAT64) { double* values = (double*) matrix.values.data; for (size_t i = 0; i < matrix.values.size; i++) { - if (values[i] >= HUGE_DOUBLE) { - values[i] = INFINITY; - } else if (values[i] <= -HUGE_DOUBLE) { - values[i] = -INFINITY; - } + values[i] = bsp_suitesparse_declamp_impl_(values[i]); + } + } else if (matrix.values.type == BSP_COMPLEX_FLOAT64) { + double _Complex* values = (double _Complex*) matrix.values.data; + + for (size_t i = 0; i < matrix.values.size; i++) { + double real_value = __real__ values[i]; + double imaginary_value = __imag__ values[i]; + real_value = bsp_suitesparse_declamp_impl_(real_value); + imaginary_value = bsp_suitesparse_declamp_impl_(imaginary_value); + + values[i] = real_value + 1j * imaginary_value; } } } diff --git a/include/binsparse/matrix_market/matrix_market_read.h b/include/binsparse/matrix_market/matrix_market_read.h index 2626ecc..a302c45 100644 --- a/include/binsparse/matrix_market/matrix_market_read.h +++ b/include/binsparse/matrix_market/matrix_market_read.h @@ -78,10 +78,10 @@ bsp_matrix_t bsp_mmread_explicit_array(char* file_path, bsp_type_t value_type, bsp_array_write(matrix.values, i * matrix.nrows + j, value); } else if (mm_type == BSP_MM_COMPLEX) { - double real_value, complex_value; - sscanf(buf, "%lf %lf", &real_value, &complex_value); + double real_value, imaginary_value; + sscanf(buf, "%lf %lf", &real_value, &imaginary_value); - double _Complex value = real_value + 1j * complex_value; + double _Complex value = real_value + 1j * imaginary_value; size_t i = count % matrix.ncols; size_t j = count / matrix.ncols; @@ -284,20 +284,10 @@ bsp_matrix_t bsp_mmread(char* file_path) { assert(false); } - bsp_type_t index_type; - size_t max_dim = (metadata.nrows > metadata.ncols) ? metadata.nrows : metadata.ncols; - if (max_dim < (size_t) UINT8_MAX) { - index_type = BSP_UINT8; - } else if (max_dim < (size_t) UINT16_MAX) { - index_type = BSP_UINT16; - } else if (max_dim < (size_t) UINT32_MAX) { - index_type = BSP_UINT32; - } else { - index_type = BSP_UINT64; - } + bsp_type_t index_type = bsp_pick_integer_type(max_dim); return bsp_mmread_explicit(file_path, value_type, index_type); } diff --git a/include/binsparse/types.h b/include/binsparse/types.h index e4bb886..8578957 100644 --- a/include/binsparse/types.h +++ b/include/binsparse/types.h @@ -280,3 +280,17 @@ hid_t bsp_get_hdf5_native_type(bsp_type_t type) { return H5I_INVALID_HID; } } + +// Given the maximum value `max_value` that must be stored, +// pick an unsigned integer type for indices. +bsp_type_t bsp_pick_integer_type(size_t max_value) { + if (max_value < (size_t) UINT8_MAX) { + return BSP_UINT8; + } else if (max_value < (size_t) UINT16_MAX) { + return BSP_UINT16; + } else if (max_value < (size_t) UINT32_MAX) { + return BSP_UINT32; + } else { + return BSP_UINT64; + } +} From fd94fd1da09c18b454918bfbd56da118f9474c55 Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Wed, 15 May 2024 16:49:20 -0700 Subject: [PATCH 2/8] Update `mtx2bsp` to support multiple formats --- examples/check_equivalence.c | 33 ----------- examples/mtx2bsp.c | 70 ++++++++++++++++++++++-- include/binsparse/convert_matrix.h | 4 +- include/binsparse/detail/detail.h | 1 + include/binsparse/detail/parse_dataset.h | 46 ++++++++++++++++ 5 files changed, 114 insertions(+), 40 deletions(-) create mode 100644 include/binsparse/detail/parse_dataset.h diff --git a/examples/check_equivalence.c b/examples/check_equivalence.c index 1174c52..3ab96ac 100644 --- a/examples/check_equivalence.c +++ b/examples/check_equivalence.c @@ -80,39 +80,6 @@ int check_array_equivalence(bsp_array_t array1, bsp_array_t array2) { return 0; } -typedef struct { - char* fname; - char* dataset; -} bsp_fdataset_info_t; - -bsp_fdataset_info_t bsp_parse_fdataset_string(char* str) { - size_t len = strlen(str); - - int split = -1; - for (int i = len - 1; i >= 0; i--) { - if (str[i] == ':') { - split = i; - break; - } - } - - if (split == -1) { - bsp_fdataset_info_t info; - info.fname = (char*) malloc(sizeof(char) * (len + 1)); - strcpy(info.fname, str); - info.dataset = NULL; - return info; - } else { - bsp_fdataset_info_t info; - info.fname = (char*) malloc(sizeof(char) * (split + 1)); - strncpy(info.fname, str, split); - info.fname[split] = '\0'; - info.dataset = (char*) malloc(sizeof(char) * (len - split)); - strcpy(info.dataset, &str[split + 1]); - return info; - } -} - int main(int argc, char** argv) { if (argc < 3) { printf( diff --git a/examples/mtx2bsp.c b/examples/mtx2bsp.c index aecc812..6b18639 100644 --- a/examples/mtx2bsp.c +++ b/examples/mtx2bsp.c @@ -4,8 +4,31 @@ int main(int argc, char** argv) { if (argc < 3) { - printf("usage: ./mtx2bsp [inputfile_name.mtx] [outputfile_name.bsp.hdf5] " - "[optional: dataset]\n"); + printf("usage: ./mtx2bsp [input.mtx] [output.bsp.h5]:[optional: group] " + "[optional: format]\n"); + printf("\n"); + printf("Description: Convert a Matrix Market file to a Binsparse HDF5 " + "file.\n"); + printf(" Users can optionally provide an HDF5 group to store " + "the\n"); + printf(" file in as well as a specific format. The default " + "format\n"); + printf(" is row-sorted COO (COOR).\n"); + printf("\n"); + printf("example: ./mtx2bsp chesapeake.mtx chesapeake.bsp.h5\n"); + printf(" - Convert Matrix Market file `chesapeake.mtx` to Binsparse " + "HDF5 file `chesapeake.bsp.h5`.\n"); + printf(" - Matrix will be stored in root group.\n"); + printf(" - Matrix will be stored in COOR format.\n"); + printf("\n"); + printf("example: ./mtx2bsp chesapeake.mtx chesapeake.bsp.h5:chesapeake\n"); + printf(" - Same as previous example, but matrix will be stored in " + "HDF5 group `chesapeake`.\n"); + printf("\n"); + printf( + "example: ./mtx2bsp chesapeake.mtx chesapeake.bsp.h5:chesapeake CSR\n"); + printf(" - Same as previous example, but matrix will use CSR " + "format.\n"); return 1; } @@ -15,16 +38,47 @@ int main(int argc, char** argv) { bool perform_suitesparse_declamping = true; char* input_fname = argv[1]; - char* output_fname = argv[2]; - char* group_name = NULL; + bsp_fdataset_info_t info2 = bsp_parse_fdataset_string(argv[2]); + char* output_fname = info2.fname; + char* group_name = info2.dataset; + + char* format_name = NULL; if (argc >= 4) { - group_name = argv[3]; + format_name = argv[3]; + } + + char* input_file_extension = bsp_get_file_extension(input_fname); + char* output_file_extension = bsp_get_file_extension(output_fname); + + if (input_file_extension == NULL || + strcmp(input_file_extension, ".mtx") != 0) { + fprintf(stderr, + "error: input file \"%s\" is not a Matrix Market file. " + "(Its extension is not '.mtx'.)\n", + input_fname); + return 1; + } + + if (output_file_extension == NULL || + (strcmp(output_file_extension, ".h5") != 0 && + strcmp(output_file_extension, ".hdf5") != 0)) { + fprintf(stderr, + "error: output file \"%s\" is not an HDF5 file. " + "(Its extension is not '.h5' or '.hdf5'.)\n", + output_fname); + return 1; } bsp_mm_metadata m = bsp_mmread_metadata(input_fname); + bsp_matrix_format_t format = BSP_COOR; + if (format_name != NULL) { + format = bsp_get_matrix_format(format_name); + assert(format != 0); + } + printf("%lu x %lu matrix with %lu nonzeros.\n", m.nrows, m.ncols, m.nnz); printf( "Matrix Market format is \"%s\" with type \"%s\" and structure \"%s\"\n", @@ -52,6 +106,12 @@ int main(int argc, char** argv) { matrix = bsp_matrix_minimize_values(matrix); + if (format != BSP_COOR) { + bsp_matrix_t converted_matrix = bsp_convert_matrix(matrix, format); + bsp_destroy_matrix_t(matrix); + matrix = converted_matrix; + } + bsp_print_matrix_info(matrix); printf(" === Writing to %s... ===\n", output_fname); diff --git a/include/binsparse/convert_matrix.h b/include/binsparse/convert_matrix.h index fc18dc2..3523a2f 100644 --- a/include/binsparse/convert_matrix.h +++ b/include/binsparse/convert_matrix.h @@ -5,9 +5,9 @@ bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, bsp_matrix_format_t format) { - // If already in desired format, do nothing. + // Throw an error if matrix already in desired format. if (matrix.format == format) { - return matrix; + assert(false); } if (format == BSP_COOR) { diff --git a/include/binsparse/detail/detail.h b/include/binsparse/detail/detail.h index 776a8d6..4cebf43 100644 --- a/include/binsparse/detail/detail.h +++ b/include/binsparse/detail/detail.h @@ -1,3 +1,4 @@ #pragma once #include +#include diff --git a/include/binsparse/detail/parse_dataset.h b/include/binsparse/detail/parse_dataset.h new file mode 100644 index 0000000..434c821 --- /dev/null +++ b/include/binsparse/detail/parse_dataset.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +typedef struct { + char* fname; + char* dataset; +} bsp_fdataset_info_t; + +bsp_fdataset_info_t bsp_parse_fdataset_string(char* str) { + size_t len = strlen(str); + + int split = -1; + for (int i = len - 1; i >= 0; i--) { + if (str[i] == ':') { + split = i; + break; + } + } + + if (split == -1) { + bsp_fdataset_info_t info; + info.fname = (char*) malloc(sizeof(char) * (len + 1)); + strcpy(info.fname, str); + info.dataset = NULL; + return info; + } else { + bsp_fdataset_info_t info; + info.fname = (char*) malloc(sizeof(char) * (split + 1)); + strncpy(info.fname, str, split); + info.fname[split] = '\0'; + info.dataset = (char*) malloc(sizeof(char) * (len - split)); + strcpy(info.dataset, &str[split + 1]); + return info; + } +} + +char* bsp_get_file_extension(char* file_name) { + int64_t len = strlen(file_name); + for (int64_t i = len - 1; i >= 0; i--) { + if (file_name[i] == '.') { + return &file_name[i]; + } + } + return NULL; +} From f182c839faaa77f30d250a9e08f67bc03bff46d6 Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Wed, 15 May 2024 17:34:29 -0700 Subject: [PATCH 3/8] Update `check_equivalence` to support conversion if necessary --- examples/check_equivalence.c | 15 ++++++ include/binsparse/convert_matrix.h | 80 +++++++++++++++++++++++++++--- 2 files changed, 87 insertions(+), 8 deletions(-) diff --git a/examples/check_equivalence.c b/examples/check_equivalence.c index 3ab96ac..97c7ed7 100644 --- a/examples/check_equivalence.c +++ b/examples/check_equivalence.c @@ -101,6 +101,21 @@ int main(int argc, char** argv) { bsp_matrix_t matrix1 = bsp_read_matrix(info1.fname, info1.dataset); bsp_matrix_t matrix2 = bsp_read_matrix(info2.fname, info2.dataset); + // If matrices are not the same format, try to convert. + if (matrix1.format != matrix2.format) { + if (matrix1.format != BSP_COOR) { + bsp_matrix_t intermediate = bsp_convert_matrix(matrix1, BSP_COOR); + bsp_destroy_matrix_t(matrix1); + matrix1 = intermediate; + } + + if (matrix2.format != BSP_COOR) { + bsp_matrix_t intermediate = bsp_convert_matrix(matrix2, BSP_COOR); + bsp_destroy_matrix_t(matrix2); + matrix2 = intermediate; + } + } + if (matrix1.format != matrix2.format) { fprintf(stderr, "Formats do not match. (%s != %s)\n", bsp_get_matrix_format_string(matrix1.format), diff --git a/include/binsparse/convert_matrix.h b/include/binsparse/convert_matrix.h index 3523a2f..cb7f480 100644 --- a/include/binsparse/convert_matrix.h +++ b/include/binsparse/convert_matrix.h @@ -12,7 +12,54 @@ bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, if (format == BSP_COOR) { // *Convert to COO* from another format. - assert(false); + if (matrix.format == BSP_CSR) { + // Convert CSR -> COOR + bsp_matrix_t result = bsp_construct_default_matrix_t(); + + result.format = BSP_COOR; + + // Inherit NNZ, nrows, ncols, ISO-ness, and structure directly from + // original matrix. + result.nnz = matrix.nnz; + result.nrows = matrix.nrows; + result.ncols = matrix.ncols; + result.is_iso = matrix.is_iso; + result.structure = matrix.structure; + + size_t max_dim = + (matrix.nrows > matrix.ncols) ? matrix.nrows : matrix.ncols; + + bsp_type_t index_type = bsp_pick_integer_type(max_dim); + + result.values = bsp_copy_construct_array_t(matrix.values); + + // There is a corner case with tall and skinny matrices where we need a + // higher width for rowind. In order to keep rowind/colind the same type, + // we might upcast. + + if (index_type == matrix.indices_0.type) { + result.indices_1 = bsp_copy_construct_array_t(matrix.indices_0); + } else { + result.indices_1 = bsp_construct_array_t(matrix.nnz, index_type); + for (size_t i = 0; i < matrix.nnz; i++) { + bsp_array_awrite(result.indices_1, i, matrix.indices_0, i); + } + } + + result.indices_0 = bsp_construct_array_t(matrix.nnz, index_type); + + for (size_t i = 0; i < matrix.nrows; i++) { + size_t row_begin, row_end; + bsp_array_read(matrix.pointers_to_1, i, row_begin); + bsp_array_read(matrix.pointers_to_1, i + 1, row_end); + for (size_t j_ptr = row_begin; j_ptr < row_end; j_ptr++) { + bsp_array_write(result.indices_0, j_ptr, i); + } + } + return result; + } else { + assert(false); + } } else { // Convert to any another format. @@ -29,6 +76,14 @@ bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, bsp_matrix_t result = bsp_construct_default_matrix_t(); + result.format = BSP_CSR; + + result.nrows = matrix.nrows; + result.ncols = matrix.ncols; + result.nnz = matrix.nnz; + result.is_iso = matrix.is_iso; + result.structure = matrix.structure; + // TODO: consider whether to produce files with varying integer types // for row indices, column indices, and offsets. @@ -41,13 +96,25 @@ bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, bsp_type_t value_type = matrix.values.type; bsp_type_t index_type = bsp_pick_integer_type(max_value); - result.values = bsp_construct_array_t(matrix.nnz, value_type); - result.indices_0 = bsp_construct_array_t(matrix.nnz, index_type); + // Since COOR is sorted by rows and then by columns, values and column + // indices can be copied exactly. Values' type will not change, but + // column indices might, thus the extra branch. + + result.values = bsp_copy_construct_array_t(matrix.values); + + if (index_type == matrix.indices_1.type) { + result.indices_0 = bsp_copy_construct_array_t(matrix.indices_1); + } else { + result.indices_0 = bsp_construct_array_t(matrix.nnz, index_type); + + for (size_t i = 0; i < matrix.nnz; i++) { + bsp_array_awrite(result.indices_0, i, matrix.indices_1, i); + } + } + result.pointers_to_1 = bsp_construct_array_t(matrix.nrows + 1, index_type); - bsp_array_t values = result.values; - bsp_array_t colind = result.indices_0; bsp_array_t rowptr = result.pointers_to_1; bsp_array_write(rowptr, 0, 0); @@ -55,9 +122,6 @@ bsp_matrix_t bsp_convert_matrix(bsp_matrix_t matrix, size_t r = 0; size_t c = 0; for (size_t c = 0; c < matrix.nnz; c++) { - bsp_array_awrite(values, c, matrix.values, c); - bsp_array_awrite(colind, c, matrix.indices_1, c); - size_t j; bsp_array_read(matrix.indices_0, c, j); From 8e3cb3ef5f9ebdf60857b3285b6140c9a87c50fe Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Thu, 16 May 2024 13:32:17 -0700 Subject: [PATCH 4/8] Perform de-clamping inside `check_equivalence` --- examples/check_equivalence.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/examples/check_equivalence.c b/examples/check_equivalence.c index 97c7ed7..e7dfb62 100644 --- a/examples/check_equivalence.c +++ b/examples/check_equivalence.c @@ -101,6 +101,17 @@ int main(int argc, char** argv) { bsp_matrix_t matrix1 = bsp_read_matrix(info1.fname, info1.dataset); bsp_matrix_t matrix2 = bsp_read_matrix(info2.fname, info2.dataset); + bool perform_suitesparse_declamping = true; + if (perform_suitesparse_declamping && + strcmp(bsp_get_file_extension(file1), ".mtx") == 0) { + bsp_matrix_declamp_values(matrix1); + } + + if (perform_suitesparse_declamping && + strcmp(bsp_get_file_extension(file2), ".mtx") == 0) { + bsp_matrix_declamp_values(matrix2); + } + // If matrices are not the same format, try to convert. if (matrix1.format != matrix2.format) { if (matrix1.format != BSP_COOR) { From b0f06572ec98bbb26fc92d0cd169a969d6213fdc Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Thu, 16 May 2024 13:34:24 -0700 Subject: [PATCH 5/8] Update `bsp-ls` --- examples/bsp-ls.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/bsp-ls.c b/examples/bsp-ls.c index 1901cab..86a8cbb 100644 --- a/examples/bsp-ls.c +++ b/examples/bsp-ls.c @@ -35,7 +35,8 @@ void print_group_info(hid_t g, const char* name) { assert(format_ != NULL); char* format_string = cJSON_GetStringValue(format_); - cJSON* nnz_ = cJSON_GetObjectItemCaseSensitive(binsparse, "nnz"); + cJSON* nnz_ = + cJSON_GetObjectItemCaseSensitive(binsparse, "number_of_stored_values"); assert(nnz_ != NULL); size_t nnz = cJSON_GetNumberValue(nnz_); From a5da4a3884eb23e2b2713a0efa27606075b80e58 Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Thu, 16 May 2024 14:00:55 -0700 Subject: [PATCH 6/8] Update `bsp-ls` to print types --- examples/bsp-ls.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/bsp-ls.c b/examples/bsp-ls.c index 86a8cbb..7ed4bf3 100644 --- a/examples/bsp-ls.c +++ b/examples/bsp-ls.c @@ -60,6 +60,15 @@ void print_group_info(hid_t g, const char* name) { printf("Group \"%s\": Version %s Binsparse matrix. Format %s, %zu x %zu.\n", full_group_path, version_string, format_string, nrows, ncols); + + cJSON* data_types = + cJSON_GetObjectItemCaseSensitive(binsparse, "data_types"); + assert(data_types != NULL); + + cJSON* item; + cJSON_ArrayForEach(item, data_types) { + printf(" %s: %s\n", item->string, cJSON_Print(item)); + } } H5Literate(g, H5_INDEX_NAME, H5_ITER_INC, NULL, visit_group, NULL); From 6ed460a96ca53d7e0d7fa135bfd16a1a66fef31d Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Fri, 17 May 2024 12:34:09 -0700 Subject: [PATCH 7/8] Create attributes as scalars, fix MatrixMarket FP printing. --- examples/check_equivalence.c | 9 +++++++-- include/binsparse/hdf5_wrapper.h | 3 +-- include/binsparse/matrix_market/matrix_market_write.h | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/examples/check_equivalence.c b/examples/check_equivalence.c index e7dfb62..2a50204 100644 --- a/examples/check_equivalence.c +++ b/examples/check_equivalence.c @@ -53,7 +53,8 @@ int check_array_equivalence(bsp_array_t array1, bsp_array_t array2) { bsp_array_read(array2, i, value2); if (value1 != value2) { - fprintf(stderr, "Array values are not equal.\n"); + fprintf(stderr, "Array values are not equal. (%zu != %zu)\n", value1, + value2); return 4; } } else if (mm_type1 == BSP_MM_REAL) { @@ -62,7 +63,11 @@ int check_array_equivalence(bsp_array_t array1, bsp_array_t array2) { bsp_array_read(array2, i, value2); if (value1 != value2) { - fprintf(stderr, "Array values are not equal.\n"); + fprintf(stderr, + "Array values are not equal. (%.17lg + i%.17lg != %.17lg + " + "i%.17lg)\n", + __real__ value1, __imag__ value1, __real__ value2, + __imag__ value2); return 4; } } else if (mm_type1 == BSP_MM_COMPLEX) { diff --git a/include/binsparse/hdf5_wrapper.h b/include/binsparse/hdf5_wrapper.h index 7a69b11..f9b675c 100644 --- a/include/binsparse/hdf5_wrapper.h +++ b/include/binsparse/hdf5_wrapper.h @@ -96,8 +96,7 @@ void bsp_write_attribute(hid_t f, char* label, char* string) { hid_t strtype = H5Tcopy(H5T_C_S1); H5Tset_size(strtype, strlen(string)); H5Tset_cset(strtype, H5T_CSET_UTF8); - hsize_t size = 1; - hid_t dataspace = H5Screate_simple(1, &size, H5P_DEFAULT); + hid_t dataspace = H5Screate(H5S_SCALAR); hid_t attribute = H5Acreate2(f, label, strtype, dataspace, H5P_DEFAULT, H5P_DEFAULT); diff --git a/include/binsparse/matrix_market/matrix_market_write.h b/include/binsparse/matrix_market/matrix_market_write.h index 41502e3..238ee78 100644 --- a/include/binsparse/matrix_market/matrix_market_write.h +++ b/include/binsparse/matrix_market/matrix_market_write.h @@ -76,8 +76,10 @@ void bsp_mmwrite(char* file_path, bsp_matrix_t matrix) { bsp_array_read(matrix.indices_0, count, i); bsp_array_read(matrix.indices_1, count, j); bsp_array_read(matrix.values, count, value); - fprintf(f, "%zu %zu %lf\n", i + 1, j + 1, value); + fprintf(f, "%zu %zu %.17lg\n", i + 1, j + 1, value); } else if (mm_type == BSP_MM_COMPLEX) { + // TODO: use Tim Davis' trick from SuiteSparse to limit the number of + // digits printed without reducing accuracy. size_t i, j; double _Complex value; bsp_array_read(matrix.indices_0, count, i); @@ -85,7 +87,7 @@ void bsp_mmwrite(char* file_path, bsp_matrix_t matrix) { bsp_array_read(matrix.values, count, value); double real_value = 1.0 * value; double complex_value = 1j * value; - fprintf(f, "%zu %zu %lf %lf\n", i + 1, j + 1, real_value, + fprintf(f, "%zu %zu %.17lg %.17lg\n", i + 1, j + 1, real_value, complex_value); } else { assert(false); From 59aef62cff9df436e5599ae83b3963dbdf84d611 Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Mon, 20 May 2024 12:09:12 -0700 Subject: [PATCH 8/8] Fix printing of non-matching real/complex values in `check_equivalence` --- examples/check_equivalence.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/check_equivalence.c b/examples/check_equivalence.c index 2a50204..0706d45 100644 --- a/examples/check_equivalence.c +++ b/examples/check_equivalence.c @@ -63,11 +63,8 @@ int check_array_equivalence(bsp_array_t array1, bsp_array_t array2) { bsp_array_read(array2, i, value2); if (value1 != value2) { - fprintf(stderr, - "Array values are not equal. (%.17lg + i%.17lg != %.17lg + " - "i%.17lg)\n", - __real__ value1, __imag__ value1, __real__ value2, - __imag__ value2); + fprintf(stderr, "Array values are not equal. (%.17lg != %.17lg)\n", + value1, value2); return 4; } } else if (mm_type1 == BSP_MM_COMPLEX) { @@ -76,7 +73,11 @@ int check_array_equivalence(bsp_array_t array1, bsp_array_t array2) { bsp_array_read(array2, i, value2); if (value1 != value2) { - fprintf(stderr, "Array values are not equal.\n"); + fprintf(stderr, + "Array values are not equal. (%.17lg + i%.17lg != %.17lg + " + "i%.17lg)\n", + __real__ value1, __imag__ value1, __real__ value2, + __imag__ value2); return 4; } }