1function Cout = GB_spec_matrix (Cin, identity) 2%GB_SPEC_MATRIX a MATLAB mimic that conforms a matrix to the GraphBLAS spec 3% 4% Cout = GB_spec_matrix (Cin) 5% Cout = GB_spec_matrix (Cin, identity) 6% 7% Conforms an input matrix to the GraphBLAS spec, for use in GB_spec_* only. 8% 9% The identity argument is the addititive identity of the semiring that will be 10% used on the matrix (default is zero). It represents the value of the 11% implicit 'zeros' that are not present in a sparse matrix. 12% 13% The input Cin is either a matrix or a struct. 14% 15% If Cin is a struct, it must have a field Cin.matrix, and it can have two 16% optional fields, Cin.pattern and Cin.class. Let X = Cin.matrix. The type 17% of X is given by Cin.class, if present, or type(X) otherwise. For a dense 18% X, type(X) and Cin.class, if present, must match. If present, the pattern 19% of X is given by Cin.pattern; otherwise the pattern for a sparse X is 20% GB_spones_mex(X) and entries outside the pattern are assumed to be equal to 21% identity. For a dense X, with no Cin.pattern present the pattern of X is 22% all true. 23% 24% If Cin is a matrix, then its type is given by GB_spec_type (Cin). If the 25% matrix is sparse, its pattern is GB_spones_mex(Cin) and entries not in the 26% pattern are assumed equal to identity. Otherwise the pattern of Cin is 27% all true. 28% 29% The output Cout is a struct with all three fields present (matrix, pattern, 30% and type). Cout.matrix is dense, and it has been typecast into the type 31% given by Cout.class. Entries not in the pattern of X are explicitly set to 32% identity. 33% 34% The matrix is typecast to Cout.class using the typecasting GraphBLAS 35% typecasting rules, which differ from MATLAB's rules, particular for integer 36% types. Since MATLAB can only represent 'logical' and 'double' sparse 37% matries, the matrix is converted to full. 38% 39% Converting a matrix from sparse to full is not part of the GraphBLAS spec, of 40% course. Neither is the identity value a part of the matrix structure in 41% GraphBLAS. A matrix in GraphBLAS is always sparse, and entries not in the 42% pattern are always implicitly equal to whatever semiring is used on the 43% sparse matrix. As a result, GraphBLAS never needs to know what the identity 44% value is until it operates on the matrix, and the identity is not part of the 45% matrix itself. Using a different semiring immediately changes the value of 46% the implicit zero, with a change to the GraphBLAS data structure for the 47% matrix. 48% 49% However, MATLAB only supports logical, double, and double complex 50% sparse matrices, so to the MATLAB mimic functions GB_spec_* operate only on 51% dense matrices. When a MATLAB sparse matrix is converted into a dense matrix, 52% the entries not in the pattern must be set to an explicit value: the 53% addititive identity. 54% 55% The MATLAB mimic routines, GB_spec_* are written almost purely in M, and 56% do not call GraphBLAS functions (with the exception of typecasting and 57% operators). They always return a stuct C with a dense C.matrix, a 58% pattern C.pattern and type C.class. 59% 60% GraphBLAS GB_mex_* functions are direct interfaces to the C functions in 61% GraphBLAS, and they always return a sparse struct; otherwise large problems 62% could not be solved. The struct contains just C.matrix and C.class. The 63% pattern of C.matrix is GB_spones_mex(C.matrix). To compare the output C0 of 64% a GB_mex_* function with C1 of a GrapBLAS_spec_* function, the struct C0 65% must first be passed to this function, C0=GB_spec_matrix(C0,identity) and 66% then C0 and C1 should be identical. 67 68% SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2021, All Rights Reserved. 69% SPDX-License-Identifier: Apache-2.0 70 71% get the semiring addititive identity, if present 72if (nargin < 2) 73 identity = 0 ; % default is zero 74end 75 76% get the matrix 77if (isstruct (Cin)) 78 X = Cin.matrix ; 79else 80 X = Cin ; 81end 82 83% get the type 84if (isstruct (Cin)) 85 if (isfield (Cin, 'class')) 86 % get the type of X from Cin.class 87 xtype = Cin.class ; 88 if (~issparse (X) && ~isequal (xtype, GB_spec_type (X))) 89 % for a dense X, GB_spec_type (X) and Cin.class must match 90 X = GB_mex_cast (X, xtype) ; 91 end 92 else 93 % no Cin.class present, so get the type from X itself 94 xtype = GB_spec_type (X) ; 95 end 96else 97 % Cin is a matrix, so get the type from X itself 98 xtype = GB_spec_type (X) ; 99end 100 101% get the pattern 102if (isstruct (Cin) && isfield (Cin, 'pattern')) 103 xpattern = Cin.pattern ; 104else 105 % must be done before typecasting since it can introduce zeros 106 if (issparse (X)) 107 % For a sparse matrix, use the actual pattern. Entries not in the 108 % the pattern are assumed to be equal to the addititve identity. 109 xpattern = GB_mex_cast (full (GB_spones_mex (X)), 'logical') ; 110 else 111 xpattern = true (size (X)) ; 112 % xpattern = (X ~= identity) ; 113 end 114end 115 116% get the uncasted values, if present 117if (isstruct (Cin) && isfield (Cin, 'values') && ... 118 (isequal (xtype, 'int64') || isequal (xtype, 'uint64'))) 119 % Cin.matrix is sparse, either double or logical. But it was typecasted 120 % from a GraphBLAS matrix that was not double or logical. Typecasting 121 % to int64 or uint64 can lead to loss of precision. So in this case, 122 % use Cin.values instead. 123 X = GB_spec_zeros (size (Cin.matrix), xtype) ; 124 [I J ~] = find (xpattern) ; 125 Cx = Cin.values ; 126 assert (length (I) == length (Cx)) ; 127 for k = 1:length (Cin.values) 128 X (I (k), J (k)) = Cx (k) ; 129 end 130else 131 % typecast X to the requested type and make it dense 132 X = GB_mex_cast (full (X), xtype) ; 133end 134 135% in the dense X, entries not in the xpattern must be set to the identity 136X (~xpattern) = GB_mex_cast (identity, xtype) ; 137 138if (~isequal (xtype, GB_spec_type (X))) 139 % if X is complex, it may have been downgraded to real, if 140 % its imaginary part is zero 141 X = GB_mex_cast (X, xtype) ; 142end 143 144% return the output struct 145Cout.matrix = X ; 146Cout.pattern = xpattern ; 147Cout.class = xtype ; 148 149% The output is now a struct with all 3 fields present, and Cout.matrix 150% is always dense. Cout.class always matches type(Cout.matrix). 151assert (isstruct (Cout)) ; 152assert (~issparse (Cout.matrix)) ; 153 154