1 //------------------------------------------------------------------------------
2 // GB_reduce_to_vector: reduce a matrix to a vector using a monoid
3 //------------------------------------------------------------------------------
4 
5 // SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2021, All Rights Reserved.
6 // SPDX-License-Identifier: Apache-2.0
7 
8 //------------------------------------------------------------------------------
9 
10 // C<M> = accum (C,reduce(A)) where C is n-by-1.  Reduces a matrix A or A'
11 // to a vector.
12 
13 #include "GB_reduce.h"
14 #include "GB_binop.h"
15 #include "GB_mxm.h"
16 
17 #define GB_FREE_ALL ;
18 
GB_reduce_to_vector(GrB_Matrix C,const GrB_Matrix M,const GrB_BinaryOp accum,const GrB_Monoid monoid,const GrB_Matrix A,const GrB_Descriptor desc,GB_Context Context)19 GrB_Info GB_reduce_to_vector        // C<M> = accum (C,reduce(A))
20 (
21     GrB_Matrix C,                   // input/output for results, size n-by-1
22     const GrB_Matrix M,             // optional M for C, unused if NULL
23     const GrB_BinaryOp accum,       // optional accum for z=accum(C,T)
24     const GrB_Monoid monoid,        // reduce monoid for T=reduce(A)
25     const GrB_Matrix A,             // first input:  matrix A
26     const GrB_Descriptor desc,      // descriptor for C, M, and A
27     GB_Context Context
28 )
29 {
30 
31     //--------------------------------------------------------------------------
32     // check inputs
33     //--------------------------------------------------------------------------
34 
35     struct GB_Matrix_opaque B_header ;
36     GrB_Matrix B = GB_clear_static_header (&B_header) ;
37 
38     // C may be aliased with M and/or A
39     GB_RETURN_IF_NULL_OR_FAULTY (C) ;
40     GB_RETURN_IF_FAULTY (M) ;
41     GB_RETURN_IF_FAULTY_OR_POSITIONAL (accum) ;
42     GB_RETURN_IF_NULL_OR_FAULTY (monoid) ;
43     GB_RETURN_IF_NULL_OR_FAULTY (A) ;
44     GB_RETURN_IF_FAULTY (desc) ;
45 
46     ASSERT_MATRIX_OK (C, "C input for reduce-to-vector", GB0) ;
47     ASSERT_MATRIX_OK_OR_NULL (M, "M for reduce-to-vector", GB0) ;
48     ASSERT_BINARYOP_OK_OR_NULL (accum, "accum for reduce-to-vector", GB0) ;
49     ASSERT_MONOID_OK (monoid, "monoid for reduce-to-vector", GB0) ;
50     ASSERT_MATRIX_OK (A, "A input for reduce-to-vector", GB0) ;
51     ASSERT_DESCRIPTOR_OK_OR_NULL (desc, "desc for reduce-to-vector", GB0) ;
52 
53     // get the descriptor
54     GB_GET_DESCRIPTOR (info, desc, C_replace, Mask_comp, Mask_struct,
55         A_transpose, xx1, xx2, do_sort) ;
56 
57     // C and M are n-by-1 GrB_Vector objects, typecasted to GrB_Matrix
58     ASSERT (GB_VECTOR_OK (C)) ;
59     ASSERT (GB_IMPLIES (M != NULL, GB_VECTOR_OK (M))) ;
60 
61     // check domains and dimensions for C<M> = accum (C,T)
62     GrB_Type ztype = monoid->op->ztype ;
63     GB_OK (GB_compatible (C->type, C, M, Mask_struct, accum, ztype, Context)) ;
64 
65     // T = reduce (T,A) must be compatible
66     if (!GB_Type_compatible (A->type, ztype))
67     {
68         GB_ERROR (GrB_DOMAIN_MISMATCH,
69             "Incompatible type for reduction monoid z=%s(x,y):\n"
70             "input matrix A of type [%s]\n"
71             "cannot be typecast to reduction monoid of type [%s]",
72             monoid->op->name, A->type->name, ztype->name) ;
73     }
74 
75     // check the dimensions
76     int64_t n = GB_NROWS (C) ;
77     if (A_transpose)
78     {
79         if (n != GB_NCOLS (A))
80         {
81             GB_ERROR (GrB_DIMENSION_MISMATCH,
82                 "w=reduce(A'):  length of w is " GBd ";\n"
83                 "it must match the number of columns of A, which is " GBd ".",
84                 n, GB_NCOLS (A)) ;
85         }
86     }
87     else
88     {
89         if (n != GB_NROWS(A))
90         {
91             GB_ERROR (GrB_DIMENSION_MISMATCH,
92                 "w=reduce(A):  length of w is " GBd ";\n"
93                 "it must match the number of rows of A, which is " GBd ".",
94                 n, GB_NROWS (A)) ;
95         }
96     }
97 
98     // quick return if an empty mask is complemented
99     GB_RETURN_IF_QUICK_MASK (C, C_replace, M, Mask_comp, Mask_struct) ;
100 
101     //--------------------------------------------------------------------------
102     // create B as full vector but with B->x of NULL
103     //--------------------------------------------------------------------------
104 
105     // B is constructed with a static header in O(1) time and space, even
106     // though it is m-by-1.  It contains no dynamically-allocated content and
107     // does not need to be freed.
108     int64_t m = A_transpose ? GB_NROWS (A) : GB_NCOLS (A) ;
109     info = GB_new (&B, true,  // full, static header
110         ztype, m, 1, GB_Ap_null, true, GxB_FULL, GB_NEVER_HYPER, 1, Context) ;
111     ASSERT (info == GrB_SUCCESS) ;
112     B->nzmax = m ;
113     B->magic = GB_MAGIC ;
114     ASSERT_MATRIX_OK (B, "B for reduce-to-vector", GB0) ;
115 
116     //--------------------------------------------------------------------------
117     // create the FIRST_ZTYPE binary operator
118     //--------------------------------------------------------------------------
119 
120     // note the function pointer is NULL; it is not needed by FIRST
121     struct GB_BinaryOp_opaque op_header ;
122     GrB_BinaryOp op = &op_header ;
123     GB_binop_new (op, NULL, ztype, ztype, ztype, NULL, GB_FIRST_opcode) ;
124     ASSERT_BINARYOP_OK (op, "op for reduce-to-vector", GB0) ;
125 
126     //--------------------------------------------------------------------------
127     // create the REDUCE_FIRST_ZTYPE semiring
128     //--------------------------------------------------------------------------
129 
130     struct GB_Semiring_opaque semiring_header ;
131     GrB_Semiring semiring = &semiring_header ;
132     semiring->header_size = 0 ;
133     info = GB_Semiring_new (semiring, monoid, op) ;
134     ASSERT (info == GrB_SUCCESS) ;
135     ASSERT_SEMIRING_OK (semiring, "semiring for reduce-to-vector", GB0) ;
136 
137     //--------------------------------------------------------------------------
138     // reduce the matrix to a vector via C<M> = accum (C, A*B)
139     //--------------------------------------------------------------------------
140 
141     return (GB_mxm (C, C_replace, M, Mask_comp, Mask_struct, accum,
142         semiring, A, A_transpose, B, false, false, GxB_DEFAULT, do_sort,
143         Context)) ;
144 }
145 
146