Skip to content

Commit 43ee0ac

Browse files
committed
Add LAGraph_Matrix_Sum utility
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.
1 parent e2539e2 commit 43ee0ac

4 files changed

Lines changed: 526 additions & 0 deletions

File tree

Config/LAGraph.h.in

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,6 +1572,47 @@ int LAGraph_Matrix_Structure
15721572
char *msg
15731573
) ;
15741574

1575+
//------------------------------------------------------------------------------
1576+
// LAGraph_Matrix_Sum: sum an array of matrices with a binary operator
1577+
//------------------------------------------------------------------------------
1578+
1579+
/** LAGraph_Matrix_Sum: combines an array of matrices into a single matrix C.
1580+
* The tuples of all input matrices are concatenated into a single buffer and
1581+
* passed to GrB_Matrix_build, using the binary operator dup to combine any
1582+
* duplicate (i,j) entries. With dup = GrB_PLUS_FP64 (for example) this
1583+
* computes the element-wise sum of all the matrices.
1584+
*
1585+
* All input matrices must have identical dimensions and identical built-in
1586+
* type; C is created with that same type and dimensions.
1587+
*
1588+
* @param[out] C the resulting matrix.
1589+
* @param[in] Matrices array of nmatrices input matrices.
1590+
* @param[in] nmatrices number of matrices in Matrices (must be >= 1).
1591+
* @param[in] dup binary operator to combine duplicate (i,j) entries;
1592+
* if NULL, duplicates are handled per GrB_Matrix_build.
1593+
* @param[in,out] msg any error messages.
1594+
*
1595+
* @retval GrB_SUCCESS if successful.
1596+
* @retval GrB_NULL_POINTER if C, Matrices, or any Matrices[k] is NULL.
1597+
* @retval GrB_INVALID_VALUE if nmatrices is zero.
1598+
* @retval GrB_DIMENSION_MISMATCH if the inputs do not all share dimensions.
1599+
* @retval GrB_DOMAIN_MISMATCH if the inputs do not all share a type.
1600+
* @retval GrB_NOT_IMPLEMENTED if the matrix type is user-defined.
1601+
* @returns any GraphBLAS errors that may have been encountered.
1602+
*/
1603+
1604+
LAGRAPH_PUBLIC
1605+
int LAGraph_Matrix_Sum
1606+
(
1607+
// output:
1608+
GrB_Matrix *C, // result = combination of all input matrices
1609+
// input:
1610+
GrB_Matrix *Matrices, // array of nmatrices input matrices
1611+
GrB_Index nmatrices, // number of matrices in the array (must be >= 1)
1612+
GrB_BinaryOp dup, // operator to combine duplicate (i,j) entries
1613+
char *msg
1614+
) ;
1615+
15751616
//------------------------------------------------------------------------------
15761617
// LAGraph_Vector_Structure: return the structure of a vector
15771618
//------------------------------------------------------------------------------

include/LAGraph.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,6 +1572,47 @@ int LAGraph_Matrix_Structure
15721572
char *msg
15731573
) ;
15741574

1575+
//------------------------------------------------------------------------------
1576+
// LAGraph_Matrix_Sum: sum an array of matrices with a binary operator
1577+
//------------------------------------------------------------------------------
1578+
1579+
/** LAGraph_Matrix_Sum: combines an array of matrices into a single matrix C.
1580+
* The tuples of all input matrices are concatenated into a single buffer and
1581+
* passed to GrB_Matrix_build, using the binary operator dup to combine any
1582+
* duplicate (i,j) entries. With dup = GrB_PLUS_FP64 (for example) this
1583+
* computes the element-wise sum of all the matrices.
1584+
*
1585+
* All input matrices must have identical dimensions and identical built-in
1586+
* type; C is created with that same type and dimensions.
1587+
*
1588+
* @param[out] C the resulting matrix.
1589+
* @param[in] Matrices array of nmatrices input matrices.
1590+
* @param[in] nmatrices number of matrices in Matrices (must be >= 1).
1591+
* @param[in] dup binary operator to combine duplicate (i,j) entries;
1592+
* if NULL, duplicates are handled per GrB_Matrix_build.
1593+
* @param[in,out] msg any error messages.
1594+
*
1595+
* @retval GrB_SUCCESS if successful.
1596+
* @retval GrB_NULL_POINTER if C, Matrices, or any Matrices[k] is NULL.
1597+
* @retval GrB_INVALID_VALUE if nmatrices is zero.
1598+
* @retval GrB_DIMENSION_MISMATCH if the inputs do not all share dimensions.
1599+
* @retval GrB_DOMAIN_MISMATCH if the inputs do not all share a type.
1600+
* @retval GrB_NOT_IMPLEMENTED if the matrix type is user-defined.
1601+
* @returns any GraphBLAS errors that may have been encountered.
1602+
*/
1603+
1604+
LAGRAPH_PUBLIC
1605+
int LAGraph_Matrix_Sum
1606+
(
1607+
// output:
1608+
GrB_Matrix *C, // result = combination of all input matrices
1609+
// input:
1610+
GrB_Matrix *Matrices, // array of nmatrices input matrices
1611+
GrB_Index nmatrices, // number of matrices in the array (must be >= 1)
1612+
GrB_BinaryOp dup, // operator to combine duplicate (i,j) entries
1613+
char *msg
1614+
) ;
1615+
15751616
//------------------------------------------------------------------------------
15761617
// LAGraph_Vector_Structure: return the structure of a vector
15771618
//------------------------------------------------------------------------------

src/test/test_Matrix_Sum.c

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
//------------------------------------------------------------------------------
2+
// LAGraph/src/test/test_Matrix_Sum.c: test LAGraph_Matrix_Sum
3+
//------------------------------------------------------------------------------
4+
5+
// LAGraph, (c) 2019-2025 by The LAGraph Contributors, All Rights Reserved.
6+
// SPDX-License-Identifier: BSD-2-Clause
7+
//
8+
// For additional details (including references to third party source code and
9+
// other files) see the LICENSE file or contact permission@sei.cmu.edu. See
10+
// Contributors.txt for a full list of contributors. Created, in part, with
11+
// funding and support from the U.S. Government (see Acknowledgments.txt file).
12+
// DM22-0790
13+
14+
// Contributed by Michel Pelletier.
15+
16+
//------------------------------------------------------------------------------
17+
18+
#include "LAGraph_test.h"
19+
#include "LG_internal.h"
20+
21+
//------------------------------------------------------------------------------
22+
// global variables
23+
//------------------------------------------------------------------------------
24+
25+
char msg [LAGRAPH_MSG_LEN] ;
26+
GrB_Matrix A = NULL, B = NULL, C = NULL, Expected = NULL ;
27+
28+
//------------------------------------------------------------------------------
29+
// setup and teardown
30+
//------------------------------------------------------------------------------
31+
32+
void setup (void)
33+
{
34+
OK (LAGraph_Init (msg)) ;
35+
}
36+
37+
void teardown (void)
38+
{
39+
OK (LAGraph_Finalize (msg)) ;
40+
}
41+
42+
//------------------------------------------------------------------------------
43+
// make_A, make_B: construct two 4x4 FP64 matrices with overlapping and
44+
// distinct (i,j) coordinates
45+
//------------------------------------------------------------------------------
46+
47+
static void make_AB (void)
48+
{
49+
// A has entries at (0,0), (1,1), (2,2), (0,3)
50+
GrB_Index Ai [ ] = { 0, 1, 2, 0 } ;
51+
GrB_Index Aj [ ] = { 0, 1, 2, 3 } ;
52+
double Ax [ ] = { 1, 2, 3, 4 } ;
53+
OK (GrB_Matrix_new (&A, GrB_FP64, 4, 4)) ;
54+
OK (GrB_Matrix_build_FP64 (A, Ai, Aj, Ax, 4, NULL)) ;
55+
56+
// B has entries at (0,0), (1,1), (3,3), (2,0)
57+
// shares (0,0) and (1,1) with A, distinct elsewhere
58+
GrB_Index Bi [ ] = { 0, 1, 3, 2 } ;
59+
GrB_Index Bj [ ] = { 0, 1, 3, 0 } ;
60+
double Bx [ ] = { 10, 20, 30, 40 } ;
61+
OK (GrB_Matrix_new (&B, GrB_FP64, 4, 4)) ;
62+
OK (GrB_Matrix_build_FP64 (B, Bi, Bj, Bx, 4, NULL)) ;
63+
}
64+
65+
//------------------------------------------------------------------------------
66+
// test_Matrix_Sum: basic correctness
67+
//------------------------------------------------------------------------------
68+
69+
void test_Matrix_Sum (void)
70+
{
71+
setup ( ) ;
72+
73+
make_AB ( ) ;
74+
75+
// Expected = A + B (element-wise add, set union)
76+
OK (GrB_Matrix_new (&Expected, GrB_FP64, 4, 4)) ;
77+
OK (GrB_eWiseAdd (Expected, NULL, NULL, GrB_PLUS_FP64, A, B, NULL)) ;
78+
79+
// C = sum of {A, B} using GrB_PLUS_FP64 for duplicates
80+
GrB_Matrix Mats [2] = { A, B } ;
81+
OK (LAGraph_Matrix_Sum (&C, Mats, 2, GrB_PLUS_FP64, msg)) ;
82+
83+
bool ok ;
84+
OK (LAGraph_Matrix_IsEqual (&ok, C, Expected, msg)) ;
85+
TEST_CHECK (ok) ;
86+
TEST_MSG ("sum of {A,B} did not equal A+B") ;
87+
OK (GrB_free (&C)) ;
88+
89+
// single-matrix array: result must be a copy of A
90+
GrB_Matrix One [1] = { A } ;
91+
OK (LAGraph_Matrix_Sum (&C, One, 1, GrB_PLUS_FP64, msg)) ;
92+
OK (LAGraph_Matrix_IsEqual (&ok, C, A, msg)) ;
93+
TEST_CHECK (ok) ;
94+
TEST_MSG ("sum of {A} did not equal A") ;
95+
OK (GrB_free (&C)) ;
96+
97+
// an empty matrix in the array contributes nothing
98+
GrB_Matrix Empty = NULL ;
99+
OK (GrB_Matrix_new (&Empty, GrB_FP64, 4, 4)) ;
100+
GrB_Matrix WithEmpty [3] = { A, Empty, B } ;
101+
OK (LAGraph_Matrix_Sum (&C, WithEmpty, 3, GrB_PLUS_FP64, msg)) ;
102+
OK (LAGraph_Matrix_IsEqual (&ok, C, Expected, msg)) ;
103+
TEST_CHECK (ok) ;
104+
TEST_MSG ("sum of {A,Empty,B} did not equal A+B") ;
105+
OK (GrB_free (&Empty)) ;
106+
OK (GrB_free (&C)) ;
107+
108+
OK (GrB_free (&A)) ;
109+
OK (GrB_free (&B)) ;
110+
OK (GrB_free (&Expected)) ;
111+
112+
teardown ( ) ;
113+
}
114+
115+
//------------------------------------------------------------------------------
116+
// test_Matrix_Sum_types: exercise every built-in type branch
117+
//------------------------------------------------------------------------------
118+
119+
void test_Matrix_Sum_types (void)
120+
{
121+
setup ( ) ;
122+
123+
// (type, GrB_PLUS operator) pairs, one per built-in type branch
124+
GrB_Type types [ ] = { GrB_BOOL, GrB_INT8, GrB_INT16, GrB_INT32,
125+
GrB_INT64, GrB_UINT8, GrB_UINT16, GrB_UINT32, GrB_UINT64,
126+
GrB_FP32, GrB_FP64 } ;
127+
GrB_BinaryOp ops [ ] = { GrB_LOR, GrB_PLUS_INT8, GrB_PLUS_INT16,
128+
GrB_PLUS_INT32, GrB_PLUS_INT64, GrB_PLUS_UINT8, GrB_PLUS_UINT16,
129+
GrB_PLUS_UINT32, GrB_PLUS_UINT64, GrB_PLUS_FP32, GrB_PLUS_FP64 } ;
130+
int ntypes = sizeof (types) / sizeof (types [0]) ;
131+
132+
GrB_Index Ai [ ] = { 0, 1, 2 } ;
133+
GrB_Index Aj [ ] = { 0, 1, 0 } ;
134+
GrB_Index Bi [ ] = { 0, 1, 0 } ;
135+
GrB_Index Bj [ ] = { 0, 1, 2 } ;
136+
137+
for (int t = 0 ; t < ntypes ; t++)
138+
{
139+
OK (GrB_Matrix_new (&A, types [t], 3, 3)) ;
140+
OK (GrB_Matrix_new (&B, types [t], 3, 3)) ;
141+
// build with INT32 values; GraphBLAS typecasts to the matrix type
142+
int32_t Ax [ ] = { 1, 1, 1 } ;
143+
int32_t Bx [ ] = { 1, 1, 1 } ;
144+
OK (GrB_Matrix_build_INT32 (A, Ai, Aj, Ax, 3, NULL)) ;
145+
OK (GrB_Matrix_build_INT32 (B, Bi, Bj, Bx, 3, NULL)) ;
146+
147+
OK (GrB_Matrix_new (&Expected, types [t], 3, 3)) ;
148+
OK (GrB_eWiseAdd (Expected, NULL, NULL, ops [t], A, B, NULL)) ;
149+
150+
GrB_Matrix Mats [2] = { A, B } ;
151+
OK (LAGraph_Matrix_Sum (&C, Mats, 2, ops [t], msg)) ;
152+
153+
bool ok ;
154+
OK (LAGraph_Matrix_IsEqual (&ok, C, Expected, msg)) ;
155+
TEST_CHECK (ok) ;
156+
TEST_MSG ("type index %d failed", t) ;
157+
158+
OK (GrB_free (&A)) ;
159+
OK (GrB_free (&B)) ;
160+
OK (GrB_free (&C)) ;
161+
OK (GrB_free (&Expected)) ;
162+
}
163+
164+
teardown ( ) ;
165+
}
166+
167+
//------------------------------------------------------------------------------
168+
// test_Matrix_Sum_brutal
169+
//------------------------------------------------------------------------------
170+
171+
#if LG_BRUTAL_TESTS
172+
void test_Matrix_Sum_brutal (void)
173+
{
174+
OK (LG_brutal_setup (msg)) ;
175+
176+
make_AB ( ) ;
177+
OK (GrB_Matrix_new (&Expected, GrB_FP64, 4, 4)) ;
178+
OK (GrB_eWiseAdd (Expected, NULL, NULL, GrB_PLUS_FP64, A, B, NULL)) ;
179+
180+
GrB_Matrix Mats [2] = { A, B } ;
181+
LG_BRUTAL (LAGraph_Matrix_Sum (&C, Mats, 2, GrB_PLUS_FP64, msg)) ;
182+
183+
bool ok ;
184+
OK (LAGraph_Matrix_IsEqual (&ok, C, Expected, msg)) ;
185+
TEST_CHECK (ok) ;
186+
187+
OK (GrB_free (&A)) ;
188+
OK (GrB_free (&B)) ;
189+
OK (GrB_free (&C)) ;
190+
OK (GrB_free (&Expected)) ;
191+
192+
OK (LG_brutal_teardown (msg)) ;
193+
}
194+
#endif
195+
196+
//------------------------------------------------------------------------------
197+
// test_Matrix_Sum_failures: test error handling
198+
//------------------------------------------------------------------------------
199+
200+
void test_Matrix_Sum_failures (void)
201+
{
202+
setup ( ) ;
203+
204+
make_AB ( ) ;
205+
GrB_Matrix Mats [2] = { A, B } ;
206+
int result ;
207+
208+
// NULL output
209+
result = LAGraph_Matrix_Sum (NULL, Mats, 2, GrB_PLUS_FP64, msg) ;
210+
TEST_CHECK (result == GrB_NULL_POINTER) ;
211+
212+
// NULL Matrices array
213+
C = NULL ;
214+
result = LAGraph_Matrix_Sum (&C, NULL, 2, GrB_PLUS_FP64, msg) ;
215+
TEST_CHECK (result == GrB_NULL_POINTER) ;
216+
TEST_CHECK (C == NULL) ;
217+
218+
// nmatrices == 0
219+
result = LAGraph_Matrix_Sum (&C, Mats, 0, GrB_PLUS_FP64, msg) ;
220+
TEST_CHECK (result == GrB_INVALID_VALUE) ;
221+
222+
// NULL entry in the array
223+
GrB_Matrix WithNull [2] = { A, NULL } ;
224+
result = LAGraph_Matrix_Sum (&C, WithNull, 2, GrB_PLUS_FP64, msg) ;
225+
TEST_CHECK (result == GrB_NULL_POINTER) ;
226+
227+
// dimension mismatch
228+
GrB_Matrix D = NULL ;
229+
OK (GrB_Matrix_new (&D, GrB_FP64, 5, 5)) ;
230+
GrB_Matrix BadDim [2] = { A, D } ;
231+
result = LAGraph_Matrix_Sum (&C, BadDim, 2, GrB_PLUS_FP64, msg) ;
232+
TEST_CHECK (result == GrB_DIMENSION_MISMATCH) ;
233+
OK (GrB_free (&D)) ;
234+
235+
// type mismatch
236+
GrB_Matrix E = NULL ;
237+
OK (GrB_Matrix_new (&E, GrB_INT32, 4, 4)) ;
238+
GrB_Matrix BadType [2] = { A, E } ;
239+
result = LAGraph_Matrix_Sum (&C, BadType, 2, GrB_PLUS_FP64, msg) ;
240+
TEST_CHECK (result == GrB_DOMAIN_MISMATCH) ;
241+
OK (GrB_free (&E)) ;
242+
243+
// user-defined type not supported
244+
typedef struct { double x, y ; } udt_t ;
245+
GrB_Type UDT = NULL ;
246+
OK (GrB_Type_new (&UDT, sizeof (udt_t))) ;
247+
GrB_Matrix U = NULL ;
248+
OK (GrB_Matrix_new (&U, UDT, 4, 4)) ;
249+
GrB_Matrix Udt [1] = { U } ;
250+
result = LAGraph_Matrix_Sum (&C, Udt, 1, NULL, msg) ;
251+
TEST_CHECK (result == GrB_NOT_IMPLEMENTED) ;
252+
OK (GrB_free (&U)) ;
253+
OK (GrB_free (&UDT)) ;
254+
255+
OK (GrB_free (&A)) ;
256+
OK (GrB_free (&B)) ;
257+
258+
teardown ( ) ;
259+
}
260+
261+
//------------------------------------------------------------------------------
262+
// TEST_LIST: the list of tasks for this entire test
263+
//------------------------------------------------------------------------------
264+
265+
TEST_LIST =
266+
{
267+
{ "Matrix_Sum", test_Matrix_Sum },
268+
{ "Matrix_Sum_types", test_Matrix_Sum_types },
269+
{ "Matrix_Sum_failures", test_Matrix_Sum_failures },
270+
#if LG_BRUTAL_TESTS
271+
{ "Matrix_Sum_brutal", test_Matrix_Sum_brutal },
272+
#endif
273+
{ NULL, NULL }
274+
} ;

0 commit comments

Comments
 (0)