1function test20(fulltest)
2%TEST20 test GrB_mxm, mxv, and vxm
3
4% SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2021, All Rights Reserved.
5% SPDX-License-Identifier: Apache-2.0
6
7[binops, ~, add_ops, types, ~, ~] = GB_spec_opsall ;
8mult_ops = binops.all ;
9types = types.all ;
10
11tic
12
13if (nargin < 1)
14    fulltest = 0 ;
15end
16
17if (fulltest == 2)
18    fprintf ('test20: lengthy tests of GrB_mxm, mxv, and vxm\n') ;
19    n_semirings_max = inf ;
20else
21    fprintf ('test20: quick test of GrB_mxm, mxv, and vxm\n') ;
22    n_semirings_max = 1 ;
23end
24
25% types to test:
26kk = 1 ;
27% ops to test
28aa = 1 ;
29
30if (fulltest > 0)
31    k1_list = 1:length(mult_ops) ;
32    k2_list = 1:length(add_ops) ;
33    k3_list = 1:length(types) ;
34else
35    k1_list = [ 9 ] ;   % times
36    k2_list = [ 3 ] ;   % plus
37    k3_list = [ 11 ] ;  % double
38end
39
40kk = min (kk, length (types)) ;
41
42dnn = struct ;
43dtn = struct ( 'inp0', 'tran' ) ;
44dnt = struct ( 'inp1', 'tran' ) ;
45dtt = struct ( 'inp0', 'tran', 'inp1', 'tran' ) ;
46
47n_semirings = 0 ;
48ntrials = 0 ;
49
50for k1 = k1_list % 1:length(mult_ops)
51    mulop = mult_ops {k1} ;
52    % if (~GB_spec_is_positional (mulop))
53    %     continue ;
54    % end
55
56    for k2 = k2_list % 1:length(add_ops)
57
58    addop = add_ops {k2} ;
59    fprintf ('\nsemiring %s:%s ', addop, mulop) ;
60
61    for k3 = k3_list % 1:length (types)
62
63    rng ('default') ;
64    type = types {k3} ;
65
66    semiring.multiply = mulop ;
67    semiring.add = addop ;
68    semiring.class = type ;
69    if (n_semirings_max == 1)
70        semiring
71    end
72
73    % create the semiring.  some are not valid because the or,and,xor
74    % monoids can only be used when z is boolean for z=mult(x,y).
75    try
76        [mult_op add_op id] = GB_spec_semiring (semiring) ;
77        [mult_opname mult_optype ztype] = GB_spec_operator (mult_op);
78        [ add_opname  add_optype] = GB_spec_operator (add_op) ;
79        identity = GB_spec_identity (semiring.add, add_optype) ;
80    catch
81        continue
82    end
83
84    if (n_semirings+1 > n_semirings_max)
85        fprintf ('\ntest20: all quick tests passed\n') ;
86        return ;
87    end
88
89    % fprintf ('\n%4d ', n_semirings) ;
90    % fprintf ('%12.2f mxm semiring %s:%s:%s ', toc,addop,mulop,type) ;
91    % fprintf (' id: %g ', double (identity)) ;
92    n_semirings = n_semirings + 1 ;
93
94    % for k4 = [0 5 11 15] % 0:length(mult_ops)
95    for k4 = [ 0 randperm(length(mult_ops), aa)]
96
97    if (k4 == 0)
98        accum_op = '' ;
99        ntypes = 1 ;
100    else
101        accum_op = mult_ops {k4} ;
102        % ntypes = [1 2 8 ] ; % length (types) ;
103        % ntypes = 1:length (types) ;
104        ntypes = randperm (length (types),kk) ;
105    end
106
107    for k5 = ntypes
108
109    clear accum
110    if (~isempty (accum_op))
111        accum_type = types {k5} ;
112        accum.opname = accum_op ;
113        accum.optype = accum_type ;
114    else
115        accum = '' ;
116        accum_type = '' ;
117    end
118
119    if (GB_spec_is_positional (accum))
120        continue ;
121    end
122
123    try
124        GB_spec_operator (accum) ;
125    catch
126        continue
127    end
128
129    for Mask_complement = [false true]
130
131    if (Mask_complement)
132        dnn.mask = 'complement' ;
133        dtn.mask = 'complement' ;
134        dnt.mask = 'complement' ;
135        dtt.mask = 'complement' ;
136    else
137        dnn.mask = 'default' ;
138        dtn.mask = 'default' ;
139        dnt.mask = 'default' ;
140        dtt.mask = 'default' ;
141    end
142
143    for C_replace = [false true]
144
145    if (C_replace)
146        dnn.outp = 'replace' ;
147        dtn.outp = 'replace' ;
148        dnt.outp = 'replace' ;
149        dtt.outp = 'replace' ;
150    else
151        dnn.outp = 'default' ;
152        dtn.outp = 'default' ;
153        dnt.outp = 'default' ;
154        dtt.outp = 'default' ;
155    end
156
157    % pick a random class, and int32
158    atypes = randperm(length(types),kk) ;
159    %  1:length (types)
160    atypes = unique ([atypes 6]) ;
161
162    % try all matrix types, to test casting
163    for k6 = atypes
164
165    aclas = types {k6} ;
166
167    if (isequal (aclas, 'int32') && ...
168        mod (n_semirings, 100) == 1)
169        % single or double would lead to
170        % different roundoff errors
171        hyper_range = 0:1 ;
172        csc_range   = 0:1 ;
173    else
174        hyper_range = 0 ;
175        csc_range   = 1 ;
176    end
177
178    % try some matrices
179    for m = 8 % [1 5 10 ]
180
181    for n = 5 % [ 1 5 10 ]
182
183    for s = 4 % [ 1 5 10 ]
184    for density = [0.1 0.2 0.3 0.5]
185
186    % try all combinations of hyper/
187    % non-hyper and CSR/CSC
188    for A_is_hyper = hyper_range
189    for A_is_csc   = csc_range
190    for B_is_hyper = hyper_range
191    for B_is_csc   = csc_range
192    for C_is_hyper = hyper_range
193    for C_is_csc   = csc_range
194    for M_is_hyper = hyper_range
195    for M_is_csc   = csc_range
196
197    if (mod (ntrials, 23) == 0)
198        fprintf ('.') ;
199    end
200
201    %-----------------------------------
202    % A*B
203    %-----------------------------------
204
205    A = GB_spec_random (m,s,density,100,aclas, A_is_csc, A_is_hyper) ;
206    B = GB_spec_random (s,n,density,100,aclas, B_is_csc, B_is_hyper) ;
207    C = GB_spec_random (m,n,density,100,aclas, C_is_csc, C_is_hyper) ;
208
209    % C = A*B, no mask
210    Mask = [ ] ;
211    C0 = GB_spec_mxm (C, [ ], accum, semiring, A, B, dnn);
212    C1 = GB_mex_mxm  (C, [ ], accum, semiring, A, B, dnn);
213    GB_spec_compare (C0, C1, identity) ;
214
215    % w = A*u, no mask
216    w = GB_spec_random (m,1,density,100,type) ;
217    u = GB_spec_random (s,1,density,100,type) ;
218
219    w0 = GB_spec_mxv (w, [ ], accum, semiring, A, u, dnn);
220    w1 = GB_mex_mxv  (w, [ ], accum, semiring, A, u, dnn);
221    GB_spec_compare (w0, w1, identity) ;
222
223    % w' = u'*A' no mask
224    w0 = GB_spec_vxm (w, [ ], accum, semiring, u, A, dnt);
225    w1 = GB_mex_vxm  (w, [ ], accum, semiring, u, A, dnt);
226    GB_spec_compare (w0, w1, identity) ;
227
228    % C = A*B with mask
229    % Mask = sprandn (m,n,0.2) ~= 0 ;
230    Mask = GB_random_mask(m,n,0.2, M_is_csc, M_is_hyper) ;
231    C0 = GB_spec_mxm (C, Mask, accum, semiring, A, B, dnn);
232    C1 = GB_mex_mxm  (C, Mask, accum, semiring, A, B, dnn);
233    GB_spec_compare (C0, C1, identity) ;
234
235    % C = A*B with mask (with explicit zero entries)
236    Mask1 = sprandn (m,n,0.2) ~= 0 ;
237    Mask2 = Mask1 .* spones (sprandn (m,n,0.5)) ;
238    S = sparse (m,n) ;
239    Mask3 = GB_mex_Matrix_eWiseAdd (S,[ ],[ ],'minus',Mask1,Mask2) ;
240    clear Mask
241    Mask.matrix = Mask3.matrix ;
242    Mask.is_csc = M_is_csc ;
243    Mask.is_hyper = M_is_hyper ;
244    clear Mask1 Mask2 Mask3
245    % the Mask matrix will not pass GB_spok(Mask) test since
246    % it will have explicit zeros
247
248    C0 = GB_spec_mxm (C, Mask, accum, semiring, A, B, dnn);
249    C1 = GB_mex_mxm  (C, Mask, accum, semiring, A, B, dnn);
250    GB_spec_compare (C0, C1, identity) ;
251
252    % w = A*u with mask
253    % mask = sprandn (m,1,0.2) ~= 0 ;
254    mask = GB_random_mask (m,1,0.2) ;
255    w0 = GB_spec_mxv (w, mask, accum, semiring, A, u, dnn);
256    w1 = GB_mex_mxv  (w, mask, accum, semiring, A, u, dnn);
257    GB_spec_compare (w0, w1, identity) ;
258
259    % w' = u'*A' with mask
260    w0 = GB_spec_vxm (w, mask, accum, semiring, u, A, dnt) ;
261    w1 = GB_mex_vxm  (w, mask, accum, semiring, u, A, dnt) ;
262    GB_spec_compare (w0, w1, identity) ;
263
264    %-----------------------------------
265    % A'*B
266    %-----------------------------------
267
268    A = GB_spec_random (s,m,density,100,aclas, A_is_csc, A_is_hyper) ;
269    B = GB_spec_random (s,n,density,100,aclas, B_is_csc, B_is_hyper) ;
270    C = GB_spec_random (m,n,density,100,aclas, C_is_csc, C_is_hyper) ;
271
272    % C = A'*B, no Mask
273    C0 = GB_spec_mxm (C, [ ], accum, semiring, A, B, dtn);
274    C1 = GB_mex_mxm  (C, [ ], accum, semiring, A, B, dtn);
275    GB_spec_compare (C0, C1, identity) ;
276
277    % w = A'*u, no mask
278
279    w0 = GB_spec_mxv (w, [ ], accum, semiring, A, u, dtn);
280    w1 = GB_mex_mxv  (w, [ ], accum, semiring, A, u, dtn);
281    GB_spec_compare (w0, w1, identity) ;
282
283    % w' = u'*A, no mask
284    w0 = GB_spec_vxm (w, [ ], accum, semiring, u, A, dnn);
285    w1 = GB_mex_vxm  (w, [ ], accum, semiring, u, A, dnn);
286    GB_spec_compare (w0, w1, identity) ;
287
288    % C = A'*B with mask
289    % Mask = sprandn (m,n,0.2) ~= 0 ;
290    Mask = GB_random_mask (m,n,0.2, M_is_csc, M_is_hyper) ;
291    C0 = GB_spec_mxm (C, Mask, accum, semiring, A, B, dtn);
292    C1 = GB_mex_mxm  (C, Mask, accum, semiring, A, B, dtn);
293    GB_spec_compare (C0, C1, identity) ;
294
295    % C = A'*B with mask
296    Mask1 = sprandn (m,n,0.2) ~= 0 ;
297    Mask2 = Mask1 .* spones (sprandn (m,n,0.5)) ;
298    S = sparse (m,n) ;
299    Mask3 = GB_mex_Matrix_eWiseAdd (S,[ ],[ ],'minus',Mask1,Mask2) ;
300    clear Mask
301    Mask.matrix = Mask3.matrix ;
302    Mask.is_csc = M_is_csc ;
303    Mask.is_hyper = M_is_hyper ;
304    clear Mask1 Mask2 Mask3
305    % the Mask matrix will not pass GB_spok(Mask) test since
306    % it will have explicit zeros
307    C0 = GB_spec_mxm (C, Mask, accum, semiring, A, B, dtn);
308    C1 = GB_mex_mxm  (C, Mask, accum, semiring, A, B, dtn);
309    GB_spec_compare (C0, C1, identity) ;
310
311    % w = A'*u, with mask
312    % mask = sprandn (m,1,0.2) ~= 0 ;
313    mask = GB_random_mask (m,1,0.2) ;
314    w0 = GB_spec_mxv (w, mask, accum, semiring, A, u, dtn);
315    w1 = GB_mex_mxv  (w, mask, accum, semiring, A, u, dtn);
316    GB_spec_compare (w0, w1, identity) ;
317
318    % w' = u'*A, with mask
319    w0 = GB_spec_vxm (w, mask, accum, semiring, u, A, dnn);
320    w1 = GB_mex_vxm  (w, mask, accum, semiring, u, A, dnn);
321    GB_spec_compare (w0, w1, identity) ;
322
323    %-----------------------------------
324    % A*B'
325    %-----------------------------------
326
327    % no mask
328
329    A = GB_spec_random (m,s,density,100,aclas, A_is_csc, A_is_hyper) ;
330    B = GB_spec_random (n,s,density,100,aclas, B_is_csc, B_is_hyper) ;
331    C = GB_spec_random (m,n,density,100,aclas, C_is_csc, C_is_hyper) ;
332
333    C0 = GB_spec_mxm (C, [ ], accum, semiring, A, B, dnt);
334    C1 = GB_mex_mxm  (C, [ ], accum, semiring, A, B, dnt);
335    GB_spec_compare (C0, C1, identity) ;
336
337    % with mask
338    % Mask = sprandn (m,n,0.2) ~= 0 ;
339    Mask = GB_random_mask (m,n,0.2, M_is_csc, M_is_hyper) ;
340    C0 = GB_spec_mxm (C, Mask, accum, semiring, A, B, dnt);
341    C1 = GB_mex_mxm  (C, Mask, accum, semiring, A, B, dnt);
342    GB_spec_compare (C0, C1, identity) ;
343
344    %-----------------------------------
345    % A'*B'
346    %-----------------------------------
347
348    % no Mask
349
350    A = GB_spec_random (s,m,density,100,aclas, A_is_csc, A_is_hyper) ;
351    B = GB_spec_random (n,s,density,100,aclas, B_is_csc, B_is_hyper) ;
352    C = GB_spec_random (m,n,density,100,aclas, C_is_csc, C_is_hyper) ;
353
354    C0 = GB_spec_mxm (C, [ ], accum, semiring, A, B, dtt);
355    C1 = GB_mex_mxm  (C, [ ], accum, semiring, A, B, dtt);
356    GB_spec_compare (C0, C1, identity) ;
357
358
359    % A'*B', with mask
360    % Mask = sprandn (m,n,0.2) ~= 0 ;
361    Mask = GB_random_mask (m,n,0.2, M_is_csc, M_is_hyper) ;
362
363    C0 = GB_spec_mxm (C, Mask, accum, semiring, A, B, dtt);
364    C1 = GB_mex_mxm  (C, Mask, accum, semiring, A, B, dtt);
365    GB_spec_compare (C0, C1, identity) ;
366
367    ntrials = ntrials + 1 ;
368
369
370end
371end
372end
373end
374end
375end
376end
377end
378end
379end
380end
381end
382end
383end
384end
385end
386end
387end
388end
389end
390
391fprintf ('semirings tested: %d\n', n_semirings) ;
392fprintf ('\ntest20: all tests passed\n') ;
393
394