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 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
function Cout = GB_spec_matrix (Cin, identity)
%GB_SPEC_MATRIX a mimic that conforms a matrix to the GraphBLAS spec
%
% Cout = GB_spec_matrix (Cin)
% Cout = GB_spec_matrix (Cin, identity)
%
% Conforms an input matrix to the GraphBLAS spec, for use in GB_spec_* only.
%
% The identity argument is the addititive identity of the semiring that will be
% used on the matrix (default is zero). It represents the value of the
% implicit 'zeros' that are not present in a sparse matrix.
%
% The input Cin is either a matrix or a struct.
%
% If Cin is a struct, it must have a field Cin.matrix, and it can have two
% optional fields, Cin.pattern and Cin.class. Let X = Cin.matrix. The type
% of X is given by Cin.class, if present, or type(X) otherwise. For a dense
% X, type(X) and Cin.class, if present, must match. If present, the pattern
% of X is given by Cin.pattern; otherwise the pattern for a sparse X is
% GB_spones_mex(X) and entries outside the pattern are assumed to be equal to
% identity. For a dense X, with no Cin.pattern present the pattern of X is
% all true.
%
% If Cin is a matrix, then its type is given by GB_spec_type (Cin). If the
% matrix is sparse, its pattern is GB_spones_mex(Cin) and entries not in the
% pattern are assumed equal to identity. Otherwise the pattern of Cin is
% all true.
%
% The output Cout is a struct with all three fields present (matrix, pattern,
% and type). Cout.matrix is dense, and it has been typecast into the type
% given by Cout.class. Entries not in the pattern of X are explicitly set to
% identity.
%
% The matrix is typecast to Cout.class using the typecasting GraphBLAS
% typecasting rules.
%
% Converting a matrix from sparse to full is not part of the GraphBLAS spec, of
% course. Neither is the identity value a part of the matrix structure in
% GraphBLAS. A matrix in GraphBLAS is always sparse, and entries not in the
% pattern are always implicitly equal to whatever semiring is used on the
% sparse matrix. As a result, GraphBLAS never needs to know what the identity
% value is until it operates on the matrix, and the identity is not part of the
% matrix itself. Using a different semiring immediately changes the value of
% the implicit zero, with a change to the GraphBLAS data structure for the
% matrix.
%
% However, built-in methods only support logical, double, and double complex
% sparse matrices, so to the mimic functions GB_spec_* operate only on dense
% matrices. When a built-in sparse matrix is converted into a dense matrix,
% the entries not in the pattern must be set to an explicit value: the
% addititive identity.
%
% The mimic routines, GB_spec_* are written almost purely in M, and
% do not call GraphBLAS functions (with the exception of typecasting and
% operators). They always return a stuct C with a dense C.matrix, a
% pattern C.pattern and type C.class.
%
% GraphBLAS GB_mex_* functions are direct interfaces to the C functions in
% GraphBLAS, and they always return a sparse struct; otherwise large problems
% could not be solved. The struct contains just C.matrix and C.class. The
% pattern of C.matrix is GB_spones_mex(C.matrix). To compare the output C0 of
% a GB_mex_* function with C1 of a GrapBLAS_spec_* function, the struct C0
% must first be passed to this function, C0=GB_spec_matrix(C0,identity) and
% then C0 and C1 should be identical.
% SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2022, All Rights Reserved.
% SPDX-License-Identifier: Apache-2.0
% get the semiring addititive identity, if present
if (nargin < 2)
identity = 0 ; % default is zero
end
% get the matrix
if (isstruct (Cin))
X = Cin.matrix ;
else
X = Cin ;
end
% get the type
if (isstruct (Cin))
if (isfield (Cin, 'class'))
% get the type of X from Cin.class
xtype = Cin.class ;
if (~issparse (X) && ~isequal (xtype, GB_spec_type (X)))
% for a dense X, GB_spec_type (X) and Cin.class must match
X = GB_mex_cast (X, xtype) ;
end
else
% no Cin.class present, so get the type from X itself
xtype = GB_spec_type (X) ;
end
else
% Cin is a matrix, so get the type from X itself
xtype = GB_spec_type (X) ;
end
% get the pattern
if (isstruct (Cin) && isfield (Cin, 'pattern'))
xpattern = Cin.pattern ;
else
% must be done before typecasting since it can introduce zeros
if (issparse (X))
% For a sparse matrix, use the actual pattern. Entries not in the
% the pattern are assumed to be equal to the addititve identity.
xpattern = GB_mex_cast (full (GB_spones_mex (X)), 'logical') ;
else
xpattern = true (size (X)) ;
% xpattern = (X ~= identity) ;
end
end
% get the uncasted values, if present
if (isstruct (Cin) && isfield (Cin, 'values') && ...
(isequal (xtype, 'int64') || isequal (xtype, 'uint64')))
% Cin.matrix is sparse, either double or logical. But it was typecasted
% from a GraphBLAS matrix that was not double or logical. Typecasting
% to int64 or uint64 can lead to loss of precision. So in this case,
% use Cin.values instead.
X = GB_spec_zeros (size (Cin.matrix), xtype) ;
[I, J, ~] = find (xpattern) ;
Cx = Cin.values ;
assert (length (I) == length (Cx)) ;
for k = 1:length (Cin.values)
X (I (k), J (k)) = Cx (k) ;
end
else
% typecast X to the requested type and make it dense
X = GB_mex_cast (full (X), xtype) ;
end
% in the dense X, entries not in the xpattern must be set to the identity
X (~xpattern) = GB_mex_cast (identity, xtype) ;
if (~isequal (xtype, GB_spec_type (X)))
% if X is complex, it may have been downgraded to real, if
% its imaginary part is zero
X = GB_mex_cast (X, xtype) ;
end
% return the output struct
Cout.matrix = X ;
Cout.pattern = xpattern ;
Cout.class = xtype ;
% The output is now a struct with all 3 fields present, and Cout.matrix
% is always dense. Cout.class always matches type(Cout.matrix).
assert (isstruct (Cout)) ;
assert (~issparse (Cout.matrix)) ;
% handle the iso case
if (isstruct (Cin) && isfield (Cin, 'iso'))
iso = Cin.iso ;
if (iso)
% get the first entry in the pattern
[I,J] = ind2sub (size (xpattern), find (xpattern)) ;
if (isempty (I))
xiso = 0 ;
else
xiso = X (I (1), J (1)) ;
end
Cout.matrix (xpattern) = xiso ;
end
Cout.iso = iso ;
end
|