Skip to content

Add LAGraph_Matrix_Sum and LAGraph_Matrix_Binary_Sum utilities - #407

Merged
michelp merged 5 commits into
v1.3.xfrom
matrix-sum
Jul 21, 2026
Merged

Add LAGraph_Matrix_Sum and LAGraph_Matrix_Binary_Sum utilities#407
michelp merged 5 commits into
v1.3.xfrom
matrix-sum

Conversation

@michelp

@michelp michelp commented Jun 12, 2026

Copy link
Copy Markdown
Member

Summary

Adds two experimental/utility/ functions that combine an array of GrB_Matrix objects into a single matrix, using a binary operator to resolve duplicate (i,j) entries. They compute the same result via two different techniques, so they can be compared:

  • LAGraph_Matrix_Sum — concatenate-and-build: extract every matrix's tuples into one shared buffer, then a single GrB_Matrix_build.
  • LAGraph_Matrix_Binary_Sum — pairwise binary reduction tree of GrB_eWiseAdd (per the GraphChallenge "Anonymized Network Sensing" paper, Fig. 3 "Binary Summation of Traffic Matrices").

With dup = GrB_PLUS_FP64 (for example) either computes the element-wise sum of all the matrices; other operators generalize it (max, times, etc.). All input matrices must have identical dimensions and the same built-in type; C is created with that type and dimensions. User-defined types return GrB_NOT_IMPLEMENTED.

LAGraph_Matrix_Sum (concatenate + build)

  1. Compute the total nvals across all inputs and a per-matrix prefix-sum offset.
  2. Allocate one tuple buffer (I, J, X) large enough for every entry.
  3. Extract each matrix's tuples into its disjoint buffer region — parallelized across LG_nthreads_outer threads, since the regions never overlap.
  4. GrB_Matrix_build with the provided dup operator to combine duplicate coordinates.

LAGraph_Matrix_Binary_Sum (pairwise binary reduction)

At each level the matrices are summed in disjoint adjacent pairs with GrB_eWiseAdd using dup; an unpaired (odd) trailing matrix is carried up to the next level unchanged, until a single matrix remains. The independent pair-sums within a level run concurrently across LG_nthreads_outer threads. Working on the smaller intermediate matrices of the tree keeps the active data in faster memory: adding two matrices each with N entries yields a matrix with fewer than 2N entries, so the relative work shrinks as the matrices grow.

All intermediate matrices are tracked in a single pre-NULLed pool with deterministic per-pair slots, so cleanup is a simple sweep that is correct on any failure path. Unlike LAGraph_Matrix_Sum, dup must be non-NULL, since GrB_eWiseAdd requires a binary operator (NULL dup returns GrB_NULL_POINTER).

Changes

  • experimental/utility/LAGraph_Matrix_Sum.c — concatenate/build implementation.
  • experimental/utility/LAGraph_Matrix_Binary_Sum.c — binary-reduction implementation.
  • include/LAGraphX.h — public declarations (experimental LAGraphX API).
  • experimental/test/test_Matrix_Sum.c, experimental/test/test_Matrix_Binary_Sum.c — tests.

Testing

ctest -R LAGraphX_Matrix_Sum and ctest -R LAGraphX_Matrix_Binary_Sum pass; clean builds with OpenMP on and off. The test binaries cover:

  • correctness (vs GrB_eWiseAdd, single-matrix copy, empty-matrix inclusion)
  • every built-in type branch
  • odd counts exercising the binary-reduction carry path
  • parallel reduction/extraction with multiple outer threads
  • error handling (NULL args, nmatrices == 0, NULL array entry, dimension/type mismatch, UDT, and NULL dup for the binary variant)
  • brutal malloc-failure variants exercising the cleanup paths

@michelp
michelp marked this pull request as draft June 12, 2026 01:26
@michelp michelp changed the title Add LAGraph_Matrix_Sum utility Add LAGraph_Matrix_Sum and LAGraph_Matrix_Binary_Sum utilities Jun 25, 2026
// funding and support from the U.S. Government (see Acknowledgments.txt file).
// DM22-0790

// Contributed by Michel Pelletier.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Original idea by Tim Davis.

@DrTimothyAldenDavis

Copy link
Copy Markdown
Member

Can you change this to be a PR to the 1.3.x branch? That's the current development branch.

@DrTimothyAldenDavis DrTimothyAldenDavis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

Can you revise the PR to go to the v1.3.x branch instead of the stable branch? And can you put these in the experimental folder instead of src? Also put the header files in LAGraphX.h.

We have to talk in the LAGraph committee about what to add move from experimental to src, before anything can go into the src folder.

For the two utilities: would it be better to have a single method, LAGraph_Matrix_sum, which took in a parameter that told what method to use? That's something can can be discussed later, after these methods are merged into the experimental/utility folder.

michelp added 5 commits July 21, 2026 09:11
LAGraph_Matrix_Sum combines an array of GrB_Matrix objects into a single
matrix using a binary operator to resolve duplicate entries.  It computes
the total number of entries across all inputs, allocates one shared tuple
buffer (I, J, X), extracts the tuples of each input matrix into the buffer,
and calls GrB_Matrix_build with the dup operator to combine duplicate (i,j)
coordinates.  With dup = GrB_PLUS_FP64 (for example) this computes the
element-wise sum of all the matrices.

All input matrices must have identical dimensions and the same built-in
type; user-defined types return GrB_NOT_IMPLEMENTED.

Includes a test (src/test/test_Matrix_Sum.c) covering correctness, all
built-in type branches, error handling, and a brutal malloc-failure variant.
The per-matrix extraction loop precomputes an offset (prefix-sum) array so
each matrix's tuples occupy a disjoint region of the shared (I, J, X) buffer.
That removes the loop-carried offset dependency and lets the extraction run
across LG_nthreads_outer threads with OpenMP; each GrB_Matrix_extractTuples
is still parallelized internally by GraphBLAS with LG_nthreads_inner threads
(the two-level nested model).  The public signature is unchanged: the thread
count follows the usual LAGraph convention via LAGraph_SetNumThreads.

Because GRB_TRY cannot return out of an OpenMP region, the first extraction
error is captured under a critical section and checked after the loop.

Adds test_Matrix_Sum_parallel, which sums many overlapping matrices with
multiple outer threads and compares against an independently accumulated
result.
Adds LAGraph_Matrix_Binary_Sum, which combines an array of matrices into
a single matrix with the same interface and result as LAGraph_Matrix_Sum
but a different technique: a pairwise binary reduction tree built from
GrB_eWiseAdd (per the GraphChallenge "Anonymized Network Sensing" paper,
"Binary Summation of Traffic Matrices"), rather than one large
extract/build.

At each level the matrices are summed in disjoint adjacent pairs using
the dup operator; an unpaired (odd) trailing matrix is carried up to the
next level unchanged, until a single matrix remains. The independent
pair-sums within a level are parallelized across LG_nthreads_outer
threads. Working on the smaller intermediate matrices of the tree keeps
the active data in faster memory.

All intermediate matrices are tracked in a single pre-NULLed Pool array
with deterministic per-pair slots, so cleanup is a simple sweep that is
correct on any failure path. Unlike LAGraph_Matrix_Sum, dup must be
non-NULL since GrB_eWiseAdd requires a binary operator.

Includes tests for correctness, odd counts (carry path), all built-in
types, parallel reduction with multiple outer threads, error handling,
and a brutal malloc-failure variant.
Replace the Pool design (which held all n-1 intermediate matrices
simultaneously) with a double-buffered ownership model using per-slot
bool flags (ownedW/ownedN). Each pair's two inputs are freed inside the
parallel loop the moment their sum is built, so only the active merge
frontier is ever resident. The Pool approach peaked at O(S * log n) of
working memory; the eager-free model peaks at O(S), where S is the
size of the current level. On synthetic data with 22% entry overlap the
reduction is 0.43x and with 4.4% overlap it reaches 0.28x versus
LAGraph_Matrix_Sum. Results are identical in all tested configurations.
Relocate LAGraph_Matrix_Sum and LAGraph_Matrix_Binary_Sum out of the
stable src/ tree into the experimental tree, and move their prototypes
from the stable public header (Config/LAGraph.h.in / include/LAGraph.h)
to include/LAGraphX.h:

- src/utility/LAGraph_Matrix_Sum.c        -> experimental/utility/
- src/utility/LAGraph_Matrix_Binary_Sum.c -> experimental/utility/
- src/test/test_Matrix_Sum.c              -> experimental/test/
- src/test/test_Matrix_Binary_Sum.c       -> experimental/test/

Each source and test now includes LAGraphX.h, and the two prototypes use
the LAGRAPHX_PUBLIC export macro. The tests register as ctests
LAGraphX_Matrix_Sum and LAGraphX_Matrix_Binary_Sum. No CMake changes are
needed: the experimental library and test globs pick up the moved files.
@michelp
michelp changed the base branch from stable to v1.3.x July 21, 2026 16:23
@michelp

michelp commented Jul 21, 2026

Copy link
Copy Markdown
Member Author

The branch is now rebased on v1.3.x.

@DrTimothyAldenDavis DrTimothyAldenDavis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great to me. A future extension could add support for user-define types, assuming the dup operator is also user-defined. But that's not essential for now.

@DrTimothyAldenDavis
DrTimothyAldenDavis marked this pull request as ready for review July 21, 2026 17:30
@DrTimothyAldenDavis

Copy link
Copy Markdown
Member

Shall I merge it in?

@michelp
michelp merged commit 94573af into v1.3.x Jul 21, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants