1function test23(fulltest)
2%TEST23 test GrB_*_build
3
4% SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2021, All Rights Reserved.
5% SPDX-License-Identifier: Apache-2.0
6
7[~, ~, ~, types, ~, ~] = GB_spec_opsall ;
8types = types.all ;
9
10if (nargin < 1)
11    % do a short test, by default
12    fulltest = 0 ;
13end
14
15ops = {
16'first',  0, % z = x
17'second', 0, % z = y
18'pair',   1, % z = 1
19'any',    1, % z = pick x or y
20'min',    1, % z = min(x,y)
21'max',    1, % z = max(x,y)
22'plus',   1, % z = x + y
23'times',  1, % z = x * y
24'iseq',   1, % z = x == y
25'or',     1, % z = x || y
26'and',    1, % z = x && y
27'xor'     1, % z = x != y
28} ;
29
30
31if (fulltest)
32    fprintf ('\n==== exhaustive test for GrB_Matrix_build and GrB_Vector_build:\n');
33    problems = [
34        10,    8,   40,  -5, 100
35        100, 200, 1000, -99, 200
36         50,  50,  500,  -2, 3
37        ] ;
38else
39    fprintf ('\n==== quick test for GrB_Matrix_build and GrB_Vector_build:\n');
40    problems = [
41        10,    8,   40,  -5, 100
42        ] ;
43end
44
45% try several problems
46for k0 = 1:size (problems,1) ;
47
48    % create nnz triplets for a matrix of size nrows-by-nrows
49    nrows = problems (k0,1) ;
50    ncols = problems (k0,2) ;
51    nnz = problems (k0,3) ;
52    y1 = problems (k0,4) ;
53    y2 = problems (k0,5) ;
54
55    rng ('default') ;
56    I = irand (0, nrows-1, nnz, 1) ;
57    J = irand (0, ncols-1, nnz, 1) ;
58    Y = y2 * rand (nnz, 1) + y1 ;
59
60    fprintf ('\nnrows: %d ncols %d nnz %d ymin %g ymax %g\n', ...
61        nrows, ncols, nnz, min (Y), max (Y)) ;
62
63    % try every operator
64    for k1 = 1:size (ops,1)
65        op.opname = ops {k1,1} ;
66        is_associative = ops {k1,2} ;
67        op_is_any = isequal (op.opname, 'any') ;
68
69        fprintf ('\n%-14s ', op.opname) ;
70
71        % try every operator type
72        for k2 = 1:length (types)
73            op.optype = types {k2} ;
74            z = GB_mex_cast (1, op.optype) ;
75            opint = isinteger (z) || islogical (z) ;
76
77            try
78                GB_spec_operator (op) ;
79            catch
80                continue
81            end
82
83            % the non-boolean logical operators are not associative
84            if (isequal (op.opname, 'or')  || ...
85                isequal (op.opname, 'and')  || ...
86                isequal (op.opname, 'iseq')  || ...
87                isequal (op.opname, 'xor'))
88                if (~isequal (op.optype, 'logical'))
89                    is_associative = false ;
90                end
91            end
92
93            if (contains (op.optype, 'single'))
94                epsilon = 1e-5 ;
95            elseif (contains (op.optype, 'double'))
96                epsilon = 1e-12 ;
97            else
98                epsilon = 0 ;
99            end
100
101            if (fulltest)
102                k3list = 1:length(types) ;
103            else
104                k3list = unique ([k2 randperm(11,2)]) ;
105            end
106
107            % try every type for X
108            for k3 = k3list % 1:length (types)
109                xtype = types {k3} ;
110                X = GB_mex_cast (Y, xtype) ;
111                fprintf ('.') ;
112
113                if (fulltest)
114                    k4list = 1:length(types) ;
115                else
116                    k4list = unique ([k3 randperm(11,2)]) ;
117                end
118
119                % try every type for the result
120                for k4 = k4list % 1:length (types)
121                    ctype = types {k4} ;
122
123                    % build the matrix in the natural order
124                    % fprintf ('\n-------------------------------op: %s ', ...
125                    % op.opname) ;
126                    % fprintf ('optype: %s ', op.optype) ;
127                    % fprintf ('xtype: %s ', xtype) ;
128
129                    for A_is_csc   = 0:1
130
131                        A = GB_mex_Matrix_build (I, J, X, nrows, ncols, op, ...
132                            ctype, A_is_csc) ;
133                        % A is sparse but may have explicit zeros
134                        if (~GB_spok (A.matrix*1))
135                            fprintf ('test failure: invalid sparse matrix\n') ;
136                            assert (false) ;
137                        end
138                        A.matrix = full (double (A.matrix)) ;
139                        if (~op_is_any)
140                            S = GB_spec_build (I, J, X, nrows, ncols, op, 'natural', ctype) ;
141                            if (~isequalwithequalnans (A.matrix, double (S.matrix))) ;
142                                fprintf ('test failure: does not match spec\n') ;
143                                assert (false) ;
144                            end
145                            assert (isequal (S.class, A.class)) ;
146                        end
147
148                        % build in random order, for associative operators.
149                        if (is_associative)
150                            [S2 p] = GB_spec_build (I, J, X, nrows, ncols, ...
151                                op, 'random', ctype) ;
152                            if (op_is_any)
153                                % 'any' reduction
154                            elseif (opint)
155                                % integers are perfectly associative
156                                if (~isequal (A.matrix, double (S2.matrix)))
157                                    fprintf ('fail: int non-associative\n') ;
158                                    assert (false) ;
159                                end
160                            else
161                                % floating point is approximately associative
162                                tol = norm (double (S2.matrix)) * epsilon ;
163                                ok = isequal (isnan (A.matrix), isnan (S2.matrix)) ;
164                                A.matrix (isnan (A.matrix)) = 0 ;
165                                S2.matrix (isnan (S2.matrix)) = 0 ;
166                                ok = ok & (norm (double (A.matrix - double (S2.matrix))) < tol) ;
167                                if (~ok)
168                                    fprintf ('fail: float non-associative\n') ;
169                                    assert (false) ;
170                                end
171                            end
172                        end
173                    end
174
175                    % build a vector in the natural order (discard J)
176                    % fprintf ('\n-------------------------------op: %s ', op) ;
177                    % fprintf ('optype: %s ', optype) ;
178                    % fprintf ('xtype: %s\n', xtype) ;
179                    A = GB_mex_Vector_build (I, X, nrows, op, ctype) ;
180                    % A is sparse but may have explicit zeros
181                    if (~GB_spok (A.matrix*1))
182                        fprintf ('test failure: invalid sparse matrix\n') ;
183                        assert (false) ;
184                    end
185                    if (~op_is_any)
186                        A.matrix = full (double (A.matrix)) ;
187                        S = GB_spec_build (I, [ ], X, nrows, 1, op, 'natural', ctype) ;
188                        if (~isequalwithequalnans (A.matrix, double (S.matrix))) ;
189                            fprintf ('test failure: does not match spec\n') ;
190                            assert (false) ;
191                        end
192                    end
193                end
194            end
195        end
196    end
197end
198
199fprintf ('\ntest23: all tests passed\n') ;
200
201