1 //------------------------------------------------------------------------------
2 // GrB_transpose: transpose a sparse matrix
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,A') or accum (C,A)
11
12 #include "GB_transpose.h"
13 #include "GB_accum_mask.h"
14
15 #define GB_FREE_ALL ;
16
GrB_transpose(GrB_Matrix C,const GrB_Matrix M,const GrB_BinaryOp accum,const GrB_Matrix A,const GrB_Descriptor desc)17 GrB_Info GrB_transpose // C<M> = accum(C,A') or accum(C,A)
18 (
19 GrB_Matrix C, // input/output matrix for results
20 const GrB_Matrix M, // optional mask for C, unused if NULL
21 const GrB_BinaryOp accum, // optional accum for Z=accum(C,T)
22 const GrB_Matrix A, // first input: matrix A
23 const GrB_Descriptor desc // descriptor for C, M, and A
24 )
25 {
26
27 //--------------------------------------------------------------------------
28 // check inputs
29 //--------------------------------------------------------------------------
30
31 struct GB_Matrix_opaque T_header ;
32 GrB_Matrix T = GB_clear_static_header (&T_header) ;
33
34 // C may be aliased with M and/or A
35
36 GB_WHERE (C, "GrB_transpose (C, M, accum, A, desc)") ;
37 GB_BURBLE_START ("GrB_transpose") ;
38 GB_RETURN_IF_NULL_OR_FAULTY (C) ;
39 GB_RETURN_IF_FAULTY (M) ;
40 GB_RETURN_IF_FAULTY_OR_POSITIONAL (accum) ;
41 GB_RETURN_IF_NULL_OR_FAULTY (A) ;
42
43 ASSERT_MATRIX_OK (C, "C input for GrB_transpose", GB0) ;
44 ASSERT_MATRIX_OK_OR_NULL (M, "M for GrB_transpose", GB0) ;
45 ASSERT_BINARYOP_OK_OR_NULL (accum, "accum for GrB_transpose", GB0) ;
46 ASSERT_MATRIX_OK (A, "A input for GrB_transpose", GB0) ;
47
48 // get the descriptor
49 GB_GET_DESCRIPTOR (info, desc, C_replace, Mask_comp, Mask_struct,
50 A_transpose, xx1, xx2, xx7) ;
51
52 // check domains and dimensions for C<M> = accum (C,T)
53 GB_OK (GB_compatible (C->type, C, M, Mask_struct, accum, A->type, Context));
54
55 // check the dimensions
56 int64_t tnrows = (!A_transpose) ? GB_NCOLS (A) : GB_NROWS (A) ;
57 int64_t tncols = (!A_transpose) ? GB_NROWS (A) : GB_NCOLS (A) ;
58 if (GB_NROWS (C) != tnrows || GB_NCOLS (C) != tncols)
59 {
60 GB_ERROR (GrB_DIMENSION_MISMATCH,
61 "Dimensions not compatible:\n"
62 "output is " GBd "-by-" GBd "\n"
63 "input is " GBd "-by-" GBd "%s",
64 GB_NROWS (C), GB_NCOLS (C),
65 tnrows, tncols, (!A_transpose) ? " (transposed)" : "") ;
66 }
67
68 // quick return if an empty mask is complemented
69 GB_RETURN_IF_QUICK_MASK (C, C_replace, M, Mask_comp, Mask_struct) ;
70
71 //--------------------------------------------------------------------------
72 // T = A or A', where T can have the type of C or the type of A
73 //--------------------------------------------------------------------------
74
75 bool C_is_csc = C->is_csc ;
76 if (C_is_csc != A->is_csc)
77 {
78 // Flip the sense of A_transpose
79 A_transpose = !A_transpose ;
80 }
81
82 if (!A_transpose)
83 {
84
85 // T = A', the default behavior. This step may seem counter-intuitive,
86 // but method computes C<M>=A' by default when A_transpose is false.
87
88 // Precasting:
89 if (accum == NULL)
90 {
91 // If there is no accum operator, T is transplanted into Z and
92 // typecasted into the C->type during the transpose.
93 // transpose: typecast, no op, not in-place
94 GB_OK (GB_transpose (&T, C->type, C_is_csc, A, // T static
95 NULL, NULL, NULL, false, Context)) ;
96 }
97 else
98 {
99 // If the accum operator is present, entries in the intersection of
100 // T and C are typecasted into the accum->ytype, while entries in T
101 // but not C are typecasted directly into C->type. Thus, the
102 // typecast of T (if any) must wait, and be done in call to GB_add
103 // in GB_accum_mask.
104 // transpose: no typecast, no op, not in-place
105 GB_OK (GB_transpose (&T, A->type, C_is_csc, A, // T static
106 NULL, NULL, NULL, false, Context)) ;
107 }
108
109 // no operator; typecasting done if accum is NULL
110 }
111 else
112 {
113
114 // T = A, a pure shallow copy; nothing at all is allocated. No
115 // typecasting is done since the types of T and A are the same. If the
116 // A_transpose descriptor is true, A is viewed as transposed first.
117 // The method transposes A again, giving T=A''=A. This is a double
118 // transpose, so C<M>=A is computed, and no transpose is done. T is
119 // typecasted eventually, into the type of C if the types of T and C
120 // differ. That can be postponed at no cost since the following step
121 // is free.
122 GBURBLE ("(cheap) ") ;
123 GB_OK (GB_shallow_copy (T, C_is_csc, A, Context)) ;
124 }
125
126 ASSERT (T->is_csc == C->is_csc) ;
127 ASSERT_MATRIX_OK (T, "T for GrB_transpose", GB0) ;
128 ASSERT_MATRIX_OK (C, "C for GrB_transpose", GB0) ;
129
130 //--------------------------------------------------------------------------
131 // C<M> = accum (C,T): accumulate the results into C via the mask M
132 //--------------------------------------------------------------------------
133
134 info = GB_accum_mask (C, M, NULL, accum, &T, C_replace, Mask_comp,
135 Mask_struct, Context) ;
136 if (info == GrB_SUCCESS)
137 {
138 ASSERT_MATRIX_OK (C, "final C for GrB_transpose", GB0) ;
139 }
140
141 GB_BURBLE_END ;
142 return (info) ;
143 }
144
145