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