function colamd_test %COLAMD_TEST test colamd2 and symamd2 % Example: % colamd_test % % COLAMD and SYMAMD testing function. Here we try to give colamd2 and symamd2 % every possible type of matrix and erroneous input that they may encounter. % We want either a valid permutation returned or we want them to fail % gracefully. % % You are prompted as to whether or not the colamd2 and symand routines and % the test mexFunctions are to be compiled. % % See also colamd2, symamd2 % COLAMD, Copyright (c) 1998-2022, Timothy A. Davis, and Stefan Larimore. % SPDX-License-Identifier: BSD-3-clause % Developed in collaboration with J. Gilbert and E. Ng. % Acknowledgements: This work was supported by the National Science Foundation, % under grants DMS-9504974 and DMS-9803599. help colamd_test fprintf ('Compiling colamd2, symamd2, and test mexFunctions.\n') ; colamd_make ; d = '' ; if (~isempty (strfind (computer, '64'))) d = '-largeArrayDims' ; end cmd = sprintf (... 'mex -O %s -I../../SuiteSparse_config -I../Include ', d) ; src = '../Source/colamd_l.c ../../SuiteSparse_config/SuiteSparse_config.c' ; if (~(ispc || ismac)) % for POSIX timing routine src = [src ' -lrt'] ; end eval ([cmd 'colamdtestmex.c ' src]) ; eval ([cmd 'symamdtestmex.c ' src]) ; fprintf ('Done compiling.\n') ; fprintf ('\nThe following codes will be tested:\n') ; which colamd2 which symamd2 which colamd2mex which symamd2mex which colamdtestmex which symamdtestmex fprintf ('\nStarting the tests. Please be patient.\n') ; h = waitbar (0, 'COLAMD test') ; rand ('state', 0) ; randn ('state', 0) ; A = sprandn (500,500,0.4) ; p = colamd2 (A, [10 10 1]) ; check_perm (p, A) ; p = colamd2 (A, [2 7 1]) ; check_perm (p, A) ; p = symamd2 (A, [10 1]) ; check_perm (p, A) ; p = symamd2 (A, [7 1]) ; check_perm (p, A) ; p = symamd2 (A, [4 1]) ; check_perm (p, A) ; fprintf ('Null matrices') ; A = zeros (0,0) ; A = sparse (A) ; [p, stats] = colamd2 (A, [10 10 0]) ; %#ok check_perm (p, A) ; [p, stats] = symamd2 (A, [10 0]) ; %#ok check_perm (p, A) ; A = zeros (0, 100) ; A = sparse (A) ; [p, stats] = colamd2 (A, [10 10 0]) ; %#ok check_perm (p, A) ; A = zeros (100, 0) ; A = sparse (A) ; [p, stats] = colamd2 (A, [10 10 0]) ; check_perm (p, A) ; fprintf (' OK\n') ; fprintf ('Matrices with a few dense row/cols\n') ; for trial = 1:20 waitbar (trial/20, h, 'COLAMD: with dense rows/cols') ; fprintf ('.') ; % random square unsymmetric matrix A = rand_matrix (1000, 1000, 1, 10, 20) ; for tol = [0:.1:2 3:20 1e6] [p, stats] = colamd2 (A, [tol tol 0]) ; %#ok check_perm (p, A) ; B = A + A' ; [p, stats] = symamd2 (B, [tol 0]) ; %#ok check_perm (p, A) ; [p, stats] = colamd2 (A, [tol 1 0]) ; %#ok check_perm (p, A) ; [p, stats] = colamd2 (A, [1 tol 0]) ; %#ok check_perm (p, A) ; end end fprintf (' OK\n') ; fprintf ('General matrices\n') ; for trial = 1:400 waitbar (trial/400, h, 'COLAMD: general') ; fprintf ('.') ; % matrix of random mtype mtype = irand (3) ; A = rand_matrix (2000, 2000, mtype, 0, 0) ; p = colamd2 (A) ; check_perm (p, A) ; if (mtype == 3) p = symamd2 (A) ; check_perm (p, A) ; end end fprintf (' OK\n') ; fprintf ('Test error handling with invalid inputs\n') ; % Check different erroneous input. for trial = 1:30 waitbar (trial/30, h, 'COLAMD: error handling') ; fprintf ('.') ; A = rand_matrix (1000, 1000, 2, 0, 0) ; [m n] = size (A) ; for err = 1:13 p = Tcolamd (A, [n n 0 0 err]) ; if (p ~= -1) %#ok check_perm (p, A) ; end if (err == 1) % check different (valid) input args to colamd2 p = Acolamd (A) ; p2 = Acolamd (A, [10 10 0 0 0]) ; if (any (p ~= p2)) error ('colamd2: mismatch 1!') ; end [p2 stats] = Acolamd (A) ; %#ok if (any (p ~= p2)) error ('colamd2: mismatch 2!') ; end [p2 stats] = Acolamd (A, [10 10 0 0 0]) ; if (any (p ~= p2)) error ('colamd2: mismatch 3!') ; end end B = A'*A ; p = Tsymamd (B, [n 0 err]) ; if (p ~= -1) %#ok check_perm (p, A) ; end if (err == 1) % check different (valid) input args to symamd2 p = Asymamd (B) ; check_perm (p, A) ; p2 = Asymamd (B, [10 0 0]) ; if (any (p ~= p2)) error ('symamd2: mismatch 1!') ; end [p2 stats] = Asymamd (B) ; %#ok if (any (p ~= p2)) error ('symamd2: mismatch 2!') ; end [p2 stats] = Asymamd (B, [10 0 0]) ; %#ok if (any (p ~= p2)) error ('symamd2: mismatch 3!') ; end end end end fprintf (' OK\n') ; fprintf ('Matrices with a few empty columns\n') ; for trial = 1:400 % some are square, some are rectangular n = 0 ; while (n < 5) A = rand_matrix (1000, 1000, irand (2), 0, 0) ; [m n] = size (A) ; end % Add 5 null columns at random locations. null_col = randperm (n) ; null_col = sort (null_col (1:5)) ; A (:, null_col) = 0 ; % Order the matrix and make sure that the null columns are ordered last. [p, stats] = colamd2 (A, [1e6 1e6 0]) ; check_perm (p, A) ; % if (stats (2) ~= 5) % stats (2) % error ('colamd2: wrong number of null columns') ; % end % find all null columns in A null_col = find (sum (spones (A), 1) == 0) ; nnull = length (null_col) ; %#ok if (any (null_col ~= p ((n-4):n))) error ('colamd2: Null cols are not ordered last in natural order') ; end end fprintf (' OK\n') ; fprintf ('Matrices with a few empty rows and columns\n') ; for trial = 1:400 waitbar (trial/400, h, 'COLAMD: with empty rows/cols') ; fprintf ('.') ; % symmetric matrices n = 0 ; while (n < 5) A = rand_matrix (1000, 1000, 3, 0, 0) ; [m n] = size (A) ; end % Add 5 null columns and rows at random locations. null_col = randperm (n) ; null_col = sort (null_col (1:5)) ; A (:, null_col) = 0 ; A (null_col, :) = 0 ; % Order the matrix and make sure that the null rows/cols are ordered last. [p,stats] = symamd2 (A, [10 0]) ; check_perm (p, A) ; % find actual number of null rows and columns Alo = tril (A, -1) ; nnull = length (find (sum (Alo') == 0 & sum (Alo) == 0)) ; %#ok if (stats (2) ~= nnull | nnull < 5) %#ok error ('symamd2: wrong number of null columns') ; end if (any (null_col ~= p ((n-4):n))) error ('symamd2: Null cols are not ordered last in natural order') ; end end fprintf (' OK\n') ; fprintf ('Matrices with a few empty rows\n') ; % Test matrices with null rows inserted. for trial = 1:400 waitbar (trial/400, h, 'COLAMD: with null rows') ; fprintf ('.') ; m = 0 ; while (m < 5) A = rand_matrix (1000, 1000, 2, 0, 0) ; [m n] = size (A) ; %#ok end % Add 5 null rows at random locations. null_row = randperm (m) ; null_row = sort (null_row (1:5)) ; A (null_row, :) = 0 ; p = colamd2 (A, [10 10 0]) ; check_perm (p, A) ; if (stats (1) ~= 5) error ('colamd2: wrong number of null rows') ; end end fprintf (' OK\n') ; fprintf ('\ncolamd2 and symamd2: all tests passed\n\n') ; close (h) ; %------------------------------------------------------------------------------- function [p,stats] = Acolamd (S, knobs) % Acolamd: compare colamd2 and Tcolamd results if (nargin < 3) if (nargout == 1) [p] = colamd2 (S) ; [p1] = Tcolamd (S, [10 10 0 0 0]) ; else [p, stats] = colamd2 (S) ; [p1, stats1] = Tcolamd (S, [10 10 0 0 0]) ; %#ok end else if (nargout == 1) [p] = colamd2 (S, knobs (1:3)) ; [p1] = Tcolamd (S, knobs) ; else [p, stats] = colamd2 (S, knobs (1:3)) ; [p1, stats1] = Tcolamd (S, knobs) ; %#ok end end check_perm (p, S) ; check_perm (p1, S) ; if (any (p1 ~= p)) error ('Acolamd mismatch!') ; end %------------------------------------------------------------------------------- function [p,stats] = Asymamd (S, knobs) % Asymamd: compare symamd2 and Tsymamd results if (nargin < 3) if (nargout == 1) [p] = symamd2 (S) ; [p1] = Tsymamd (S, [10 0 0]) ; else [p, stats] = symamd2 (S) ; [p1, stats1] = Tsymamd (S, [10 0 0]) ; %#ok end else if (nargout == 1) [p] = symamd2 (S, knobs (1:2)) ; [p1] = Tsymamd (S, knobs) ; else [p, stats] = symamd2 (S, knobs (1:2)) ; [p1, stats1] = Tsymamd (S, knobs) ; %#ok end end if (any (p1 ~= p)) error ('Asymamd mismatch!') ; end %------------------------------------------------------------------------------- function check_perm (p, A) % check_perm: check for a valid permutation vector if (isempty (A) & isempty (p)) %#ok % empty permutation vectors of empty matrices are OK return end if (isempty (p)) error ('bad permutation: cannot be empty') ; end [m n] = size (A) ; [pm pn] = size (p) ; if (pn == 1) % force p to be a row vector p = p' ; [pm pn] = size (p) ; end if (n ~= pn) error ('bad permutation: wrong size') ; end if (pm ~= 1) ; % p must be a vector error ('bad permutation: not a vector') ; else if (any (sort (p) - (1:pn))) error ('bad permutation') ; end end %------------------------------------------------------------------------------- function i = irand (n) % irand: return a random integer between 1 and n i = min (n, 1 + floor (rand * n)) ; %------------------------------------------------------------------------------- function A = rand_matrix (nmax, mmax, mtype, drows, dcols) % rand_matrix: return a random sparse matrix % % A = rand_matrix (nmax, mmax, mtype, drows, dcols) % % A binary matrix of random size, at most nmax-by-mmax, with drows dense rows % and dcols dense columns. % % mtype 1: square unsymmetric (mmax is ignored) % mtype 2: rectangular % mtype 3: symmetric (mmax is ignored) n = irand (nmax) ; if (mtype ~= 2) % square m = n ; else m = irand (mmax) ; end A = sprand (m, n, 10 / max (m,n)) ; if (drows > 0) % add dense rows for k = 1:drows i = irand (m) ; nz = irand (n) ; p = randperm (n) ; p = p (1:nz) ; A (i,p) = 1 ; end end if (dcols > 0) % add dense cols for k = 1:dcols j = irand (n) ; nz = irand (m) ; p = randperm (m) ; p = p (1:nz) ; A (p,j) = 1 ; end end A = spones (A) ; % ensure that there are no empty columns d = find (full (sum (A)) == 0) ; %#ok A (m,d) = 1 ; %#ok % ensure that there are no empty rows d = find (full (sum (A,2)) == 0) ; %#ok A (d,n) = 1 ; %#ok if (mtype == 3) % symmetric A = A + A' + speye (n) ; end A = spones (A) ; %------------------------------------------------------------------------------- function [p,stats] = Tcolamd (S, knobs) % Tcolamd: run colamd2 in a testing mode if (nargout <= 1 & nargin == 1) %#ok p = colamdtestmex (S) ; elseif (nargout <= 1 & nargin == 2) %#ok p = colamdtestmex (S, knobs) ; elseif (nargout == 2 & nargin == 1) %#ok [p, stats] = colamdtestmex (S) ; elseif (nargout == 2 & nargin == 2) %#ok [p, stats] = colamdtestmex (S, knobs) ; else error ('colamd2: incorrect number of input and/or output arguments') ; end if (p (1) ~= -1) [ignore, q] = etree (S (:,p), 'col') ; p = p (q) ; check_perm (p, S) ; end %------------------------------------------------------------------------------- function [p, stats] = Tsymamd (S, knobs) % Tsymamd: run symamd2 in a testing mode if (nargout <= 1 & nargin == 1) %#ok p = symamdtestmex (S) ; elseif (nargout <= 1 & nargin == 2) %#ok p = symamdtestmex (S, knobs) ; elseif (nargout == 2 & nargin == 1) %#ok [p, stats] = symamdtestmex (S) ; elseif (nargout == 2 & nargin == 2) %#ok [p, stats] = symamdtestmex (S, knobs) ; else error ('symamd2: incorrect number of input and/or output arguments') ; end if (p (1) ~= -1) [ignore, q] = etree (S (p,p)) ; p = p (q) ; check_perm (p, S) ; end