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