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