1%% GraphBLAS: graph algorithms in the language of linear algebra 2% GraphBLAS is a library for creating graph algorithms based on sparse 3% linear algebraic operations over semirings. Visit http://graphblas.org 4% for more details and resources. See also the SuiteSparse:GraphBLAS 5% User Guide in this package. 6% 7% http://faculty.cse.tamu.edu/davis 8% 9% SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2021, All Rights Reserved. 10% SPDX-License-Identifier: GPL-3.0-or-later 11 12%% GraphBLAS: faster and more general sparse matrices for MATLAB 13% GraphBLAS is not only useful for creating graph algorithms; it also 14% supports a wide range of sparse matrix data types and operations. 15% MATLAB can compute C=A*B with just two semirings: 'plus.times.double' 16% and 'plus.times.complex' for complex matrices. GraphBLAS has 2,518 17% built-in semirings, such as 'max.plus' 18% (https://en.wikipedia.org/wiki/Tropical_semiring). These semirings can 19% be used to construct a wide variety of graph algorithms, based on 20% operations on sparse adjacency matrices. 21% 22% MATLAB and GraphBLAS both provide sparse matrices of type double, 23% logical, and double complex. GraphBLAS adds sparse matrices of type: 24% single, int8, int16, int32, int64, uint8, uint16, uint32, uint64, and 25% single complex (with MATLAB matrices, these types can only be held in 26% full matrices). 27 28% reset to the default number of threads 29clear all 30maxNumCompThreads ('automatic') ; 31GrB.clear ; 32fprintf ('\n# of threads used by GraphBLAS: %d\n', GrB.threads) ; 33 34format compact 35rng ('default') ; 36X = 100 * rand (2) ; 37G = GrB (X) % GraphBLAS copy of a matrix X, same type 38 39%% Sparse integer matrices 40% Here's an int8 version of the same matrix: 41 42S = int8 (G) % convert G to a full MATLAB int8 matrix 43S (1,1) = 0 % add an explicit zero to S 44G = GrB (X, 'int8') % a GraphBLAS full int8 matrix 45G (1,1) = 0 % add an explicit zero to G 46G = GrB.prune (G) % a GraphBLAS sparse int8 matrix 47 48try 49 S = sparse (S) ; % MATLAB can't create sparse int8 matrices 50catch me 51 display (me) 52end 53 54%% Sparse single-precision matrices 55% Matrix operations in GraphBLAS are typically as fast, or faster than 56% MATLAB. Here's an unfair comparison: computing X*X with MATLAB in 57% double precision and with GraphBLAS in single precision. You would 58% naturally expect GraphBLAS to be faster. 59% 60% CAVEAT: MATLAB R2021a uses SuiteSparse:GraphBLAS v3.3.3 for C=A*B, 61% so on that version of MATLAB, we're comparing 2 versions of GraphBLAS 62% by the same author. 63% 64% Please wait ... 65 66n = 1e5 ; 67X = spdiags (rand (n, 201), -100:100, n, n) ; 68G = GrB (X, 'single') ; 69tic 70G2 = G*G ; 71gb_time = toc ; 72tic 73X2 = X*X ; 74matlab_time = toc ; 75fprintf ('\nGraphBLAS time: %g sec (in single)\n', gb_time) ; 76fprintf ('MATLAB time: %g sec (in double)\n', matlab_time) ; 77fprintf ('Speedup of GraphBLAS over MATLAB: %g\n', ... 78 matlab_time / gb_time) ; 79fprintf ('\n# of threads used by GraphBLAS: %d\n', GrB.threads) ; 80 81%% Mixing MATLAB and GraphBLAS matrices 82% The error in the last computation is about eps('single') since 83% GraphBLAS did its computation in single precision, while MATLAB used 84% double precision. MATLAB and GraphBLAS matrices can be easily 85% combined, as in X2-G2. The sparse single precision matrices take less 86% memory space. 87 88err = norm (X2 - G2, 1) / norm (X2,1) 89eps ('single') 90whos G G2 X X2 91 92%% Faster matrix operations 93% But even with standard double precision sparse matrices, GraphBLAS is 94% typically faster than the built-in MATLAB methods. Here's a fair 95% comparison (caveat: these both use GraphBLAS in MATLAB R2021a): 96 97G = GrB (X) ; 98tic 99G2 = G*G ; 100gb_time = toc ; 101err = norm (X2 - G2, 1) / norm (X2,1) 102fprintf ('\nGraphBLAS time: %g sec (in double)\n', gb_time) ; 103fprintf ('MATLAB time: %g sec (in double)\n', matlab_time) ; 104fprintf ('Speedup of GraphBLAS over MATLAB: %g\n', ... 105 matlab_time / gb_time) ; 106fprintf ('\n# of threads used by GraphBLAS: %d\n', GrB.threads) ; 107 108%% A wide range of semirings 109% MATLAB can only compute C=A*B using the standard '+.*.double' and 110% '+.*.complex' semirings. A semiring is defined in terms of a string, 111% 'add.mult.type', where 'add' is a monoid that takes the place of the 112% additive operator, 'mult' is the multiplicative operator, and 'type' is 113% the data type for the two inputs to the mult operator. 114% 115% In the standard semiring, C=A*B is defined as: 116% 117% C(i,j) = sum (A(i,:).' .* B(:,j)) 118% 119% using 'plus' as the monoid and 'times' as the multiplicative operator. 120% But in a more general semiring, 'sum' can be any monoid, which is an 121% associative and commutative operator that has an identity value. For 122% example, in the 'max.plus' tropical algebra, C(i,j) for C=A*B is 123% defined as: 124% 125% C(i,j) = max (A(i,:).' + B(:,j)) 126 127%% 128% This can be computed in GraphBLAS with: 129% 130% C = GrB.mxm ('max.+', A, B) 131 132n = 3 ; 133A = rand (n) ; 134B = rand (n) ; 135C = zeros (n) ; 136for i = 1:n 137 for j = 1:n 138 C(i,j) = max (A (i,:).' + B (:,j)) ; 139 end 140end 141C2 = GrB.mxm ('max.+', A, B) ; 142fprintf ('\nerr = norm (C-C2,1) = %g\n', norm (C-C2,1)) ; 143 144%% The max.plus tropical semiring 145% Here are details of the "max.plus" tropical semiring. The identity 146% value is -inf since max(x,-inf) = max (-inf,x) = x for any x. 147% The identity for the conventional "plus.times" semiring is zero, 148% since x+0 = 0+x = x for any x. 149 150GrB.semiringinfo ('max.+.double') ; 151 152%% A boolean semiring 153% MATLAB cannot multiply two logical matrices. MATLAB R2019a converts 154% them to double and uses the conventional +.*.double semiring instead. 155% In GraphBLAS, this is the common Boolean 'or.and.logical' semiring, 156% which is widely used in linear algebraic graph algorithms. 157 158GrB.semiringinfo ('|.&.logical') ; 159 160%% 161clear 162A = sparse (rand (3) > 0.5) 163B = sparse (rand (3) > 0.2) 164 165%% 166try 167 % MATLAB R2019a does this by casting A and B to double 168 C1 = A*B 169catch 170 % MATLAB R2018a throws an error 171 fprintf ('MATLAB R2019a required for C=A*B with logical\n') ; 172 fprintf ('matrices. Explicitly converting to double:\n') ; 173 C1 = double (A) * double (B) 174end 175C2 = GrB (A) * GrB (B) 176 177%% 178% Note that C1 is a MATLAB sparse double matrix, and contains non-binary 179% values. C2 is a GraphBLAS logical matrix. 180whos 181GrB.type (C2) 182 183%% GraphBLAS operators, monoids, and semirings 184% The C interface for SuiteSparse:GraphBLAS allows for arbitrary types 185% and operators to be constructed. However, the MATLAB interface to 186% SuiteSparse:GraphBLAS is restricted to pre-defined types and operators: 187% a mere 13 types, 212 unary operators, 401 binary operators, 77 monoids, 188% 22 select operators (each of which can be used for all 13 types), 189% and 2,518 semirings. 190% 191% That gives you a lot of tools to create all kinds of interesting 192% graph algorithms. For example: 193% 194% GrB.bfs % breadth-first search 195% GrB.dnn % sparse deep neural network (http://graphchallenge.org) 196% GrB.mis % maximal independent set 197% 198% See 'help GrB.binopinfo' for a list of the binary operators, and 199% 'help GrB.monoidinfo' for the ones that can be used as the additive 200% monoid in a semiring. 'help GrB.unopinfo' lists the unary operators. 201% 'help GrB.semiringinfo' describes the semirings. 202 203%% 204help GrB.binopinfo 205 206%% 207help GrB.monoidinfo 208 209%% 210help GrB.unopinfo 211 212%% 213help GrB.semiringinfo 214 215%% Element-wise operations 216% Binary operators can be used in element-wise matrix operations, like 217% C=A+B and C=A.*B. For the matrix addition C=A+B, the pattern of C is 218% the set union of A and B, and the '+' operator is applied for entries 219% in the intersection. Entries in A but not B, or in B but not A, are 220% assigned to C without using the operator. The '+' operator is used for 221% C=A+B but any operator can be used with GrB.eadd. 222 223%% 224A = GrB (sprand (3, 3, 0.5)) ; 225B = GrB (sprand (3, 3, 0.5)) ; 226C1 = A + B 227C2 = GrB.eadd ('+', A, B) 228err = norm (C1-C2,1) 229 230%% Subtracting two matrices 231% A-B and GrB.eadd ('-', A, B) are not the same thing, since the '-' 232% operator is not applied to an entry that is in B but not A. 233 234C1 = A-B 235C2 = GrB.eadd ('-', A, B) 236 237%% 238% But these give the same result 239 240C1 = A-B 241C2 = GrB.eadd ('+', A, GrB.apply ('-', B)) 242err = norm (C1-C2,1) 243 244%% Element-wise 'multiplication' 245% For C = A.*B, the result C is the set intersection of the pattern of A 246% and B. The operator is applied to entries in both A and B. Entries in 247% A but not B, or B but not A, do not appear in the result C. 248 249C1 = A.*B 250C2 = GrB.emult ('*', A, B) 251C3 = double (A) .* double (B) 252 253%% 254% Just as in GrB.eadd, any operator can be used in GrB.emult: 255 256A 257B 258C2 = GrB.emult ('max', A, B) 259 260%% Overloaded operators 261% The following operators all work as you would expect for any matrix. 262% The matrices A and B can be GraphBLAS matrices, or MATLAB sparse or 263% dense matrices, in any combination, or scalars where appropriate, 264% The matrix M is logical (MATLAB or GraphBLAS): 265% 266% A+B A-B A*B A.*B A./B A.\B A.^b A/b C=A(I,J) C(M)=A 267% -A +A ~A A' A.' A&B A|B b\A C(I,J)=A C=A(M) 268% A~=B A>B A==B A<=B A>=B A<B [A,B] [A;B] C(A) 269% A(1:end,1:end) 270% 271% For A^b, b must be a non-negative integer. 272 273C1 = [A B] ; 274C2 = [double(A) double(B)] ; 275assert (isequal (double (C1), C2)) 276 277%% 278C1 = A^2 279C2 = double (A)^2 ; 280err = norm (C1 - C2, 1) 281assert (err < 1e-12) 282 283%% 284C1 = A (1:2,2:end) 285A = double (A) ; 286C2 = A (1:2,2:end) ; 287assert (isequal (double (C1), C2)) 288 289%% Overloaded functions 290% Many MATLAB built-in functions can be used with GraphBLAS matrices: 291% 292% A few differences with the built-in functions: 293% 294% S = sparse (G) % converts G to sparse/hypersparse 295% F = full (G) % adds explicit zeros, so numel(F)==nnz(F) 296% F = full (G,type,id) % adds explicit identity values to a GrB matrix 297% disp (G, level) % display a GrB matrix G; level=2 is the default. 298% 299% In the list below, the first set of Methods are overloaded built-in 300% methods. They are used as-is on GraphBLAS matrices, such as C=abs(G). 301% The Static methods are prefixed with "GrB.", as in C = GrB.apply ( ... ). 302 303%% 304 305methods GrB 306 307%% Zeros are handled differently 308% Explicit zeros cannot be automatically dropped from a GraphBLAS matrix, 309% like they are in MATLAB sparse matrices. In a shortest-path problem, 310% for example, an edge A(i,j) that is missing has an infinite weight, 311% (the monoid identity of min(x,y) is +inf). A zero edge weight A(i,j)=0 312% is very different from an entry that is not present in A. However, if 313% a GraphBLAS matrix is converted into a MATLAB sparse matrix, explicit 314% zeros are dropped, which is the convention for a MATLAB sparse matrix. 315% They can also be dropped from a GraphBLAS matrix using the GrB.select 316% method. 317 318%% 319 320G = GrB (magic (2)) ; 321G (1,1) = 0 % G(1,1) still appears as an explicit entry 322A = double (G) % but it's dropped when converted to MATLAB sparse 323H = GrB.select ('nonzero', G) % drops the explicit zeros from G 324fprintf ('nnz (G): %d nnz (A): %g nnz (H): %g\n', ... 325 nnz (G), nnz (A), nnz (H)) ; 326fprintf ('num entries in G: %d\n', GrB.entries (G)) ; 327 328%% Displaying contents of a GraphBLAS matrix 329% Unlike MATLAB, the default is to display just a few entries of a GrB matrix. 330% Here are all 100 entries of a 10-by-10 matrix, using a non-default disp(G,3): 331 332%% 333G = GrB (rand (10)) ; 334% display everything: 335disp (G,3) 336 337%% 338% That was disp(G,3), so every entry was printed. It's a little long, so 339% the default is not to print everything. 340 341%% 342% With the default display (level = 2): 343G 344 345%% 346% That was disp(G,2) or just display(G), which is what is printed by a 347% MATLAB statement that doesn't have a trailing semicolon. With 348% level = 1, disp(G,1) gives just a terse summary: 349disp (G,1) 350 351%% Storing a matrix by row or by column 352% MATLAB stores its sparse matrices by column, refered to as 'sparse by 353% col' in SuiteSparse:GraphBLAS. In the 'sparse by col' format, each 354% column of the matrix is stored as a list of entries, with their value 355% and row index. In the 'sparse by row' format, each row is stored as a 356% list of values and their column indices. GraphBLAS uses both 'by row' 357% and 'by col', and the two formats can be intermixed arbitrarily. In 358% its C interface, the default format is 'by row'. However, for better 359% compatibility with MATLAB, the SuiteSparse:GraphBLAS MATLAB interface 360% uses 'by col' by default instead. 361 362%% 363rng ('default') ; 364GrB.clear ; % clear prior GraphBLAS settings 365fprintf ('the default format is: %s\n', GrB.format) ; 366C = sparse (rand (2)) 367G = GrB (C) 368GrB.format (G) 369 370%% 371% Many graph algorithms work better in 'by row' format, with matrices 372% stored by row. For example, it is common to use A(i,j) for the edge 373% (i,j), and many graph algorithms need to access the out-adjacencies of 374% nodes, which is the row A(i,;) for node i. If the 'by row' format is 375% desired, GrB.format ('by row') tells GraphBLAS to create all subsequent 376% matrices in the 'by row' format. Converting from a MATLAB sparse matrix 377% (in standard 'by col' format) takes a little more time (requiring a 378% transpose), but subsequent graph algorithms can be faster. 379 380%% 381G = GrB (C, 'by row') 382fprintf ('the format of G is: %s\n', GrB.format (G)) ; 383H = GrB (C) 384fprintf ('the format of H is: %s\n', GrB.format (H)) ; 385err = norm (H-G,1) 386 387%% Hypersparse, sparse, bitmap, and full matrices 388% SuiteSparse:GraphBLAS can use four kinds of sparse matrix data 389% structures: hypersparse, sparse, bitmap, and full, in both 'by col' and 390% 'by row' formats, for a total of eight different combinations. In the 391% 'sparse by col' that MATLAB uses for its sparse matrices, an m-by-n 392% matrix A takes O(n+nnz(A)) space. MATLAB can create huge column 393% vectors, but not huge matrices (when n is huge). 394 395clear 396[c, huge] = computer ; 397C = sparse (huge, 1) % MATLAB can create a huge-by-1 sparse column 398try 399 C = sparse (huge, huge) % but this fails 400catch me 401 error_expected = me 402end 403 404%% 405% In a GraphBLAS hypersparse matrix, an m-by-n matrix A takes only 406% O(nnz(A)) space. The difference can be huge if nnz (A) << n. 407 408clear 409[c, huge] = computer ; 410G = GrB (huge, 1) % no problem for GraphBLAS 411H = GrB (huge, huge) % this works in GraphBLAS too 412 413%% 414% Operations on huge hypersparse matrices are very fast; no component of 415% the time or space complexity is Omega(n). 416 417I = randperm (huge, 2) ; 418J = randperm (huge, 2) ; 419H (I,J) = magic (2) ; % add 4 nonzeros to random locations in H 420H (I,I) = 10 * [1 2 ; 3 4] ; % so H^2 is not all zero 421H = H^2 ; % square H 422H = (H' * 2) ; % transpose H and double the entries 423K = pi * spones (H) ; 424H = H + K % add pi to each entry in H 425 426%% numel uses vpa if the matrix is really huge 427e1 = numel (G) % this is huge, but still a flint 428e2 = numel (H) % this is huge^2, which needs vpa 429whos e1 e2 430 431%% 432% All of these matrices take very little memory space: 433whos C G H K 434 435%% The mask and accumulator 436% When not used in overloaded operators or built-in functions, many 437% GraphBLAS methods of the form GrB.method ( ... ) can optionally use a 438% mask and/or an accumulator operator. If the accumulator is '+' in 439% GrB.mxm, for example, then C = C + A*B is computed. The mask acts much 440% like logical indexing in MATLAB. With a logical mask matrix M, 441% C<M>=A*B allows only part of C to be assigned. If M(i,j) is true, then 442% C(i,j) can be modified. If false, then C(i,j) is not modified. 443% 444% For example, to set all values in C that are greater than 0.5 to 3: 445 446%% 447A = rand (3) 448C = GrB.assign (A, A > 0.5, 3) ; % in GraphBLAS 449C1 = GrB (A) ; C1 (A > .5) = 3 % also in GraphBLAS 450C2 = A ; C2 (A > .5) = 3 % in MATLAB 451err = norm (C - C1, 1) 452err = norm (C - C2, 1) 453 454%% The descriptor 455% Most GraphBLAS functions of the form GrB.method ( ... ) take an optional 456% last argument, called the descriptor. It is a MATLAB struct that can 457% modify the computations performed by the method. 'help 458% GrB.descriptorinfo' gives all the details. The following is a short 459% summary of the primary settings: 460% 461% d.out = 'default' or 'replace', clears C after the accum op is used. 462% 463% d.mask = 'default' or 'complement', to use M or ~M as the mask matrix; 464% 'structural', or 'structural complement', to use the pattern 465% of M or ~M. 466% 467% d.in0 = 'default' or 'transpose', to transpose A for C=A*B, C=A+B, etc. 468% 469% d.in1 = 'default' or 'transpose', to transpose B for C=A*B, C=A+B, etc. 470% 471% d.kind = 'default', 'GrB', 'sparse', or 'full'; the output of GrB.method. 472 473A = sparse (rand (2)) ; 474B = sparse (rand (2)) ; 475C1 = A'*B ; 476C2 = GrB.mxm ('+.*', A, B, struct ('in0', 'transpose')) ; 477err = norm (C1-C2,1) 478 479%% Integer arithmetic is different in GraphBLAS 480% MATLAB supports integer arithmetic on its full matrices, using int8, 481% int16, int32, int64, uint8, uint16, uint32, or uint64 data types. None 482% of these integer data types can be used to construct a MATLAB sparse 483% matrix, which can only be double, double complex, or logical. 484% Furthermore, C=A*B is not defined for integer types in MATLAB, except 485% when A and/or B are scalars. 486% 487% GraphBLAS supports all of those types for all of its matrices (hyper, 488% sparse, bitmap, or full). All operations are supported, including C=A*B 489% when A or B are any integer type, in 1000s of semirings. 490% 491% However, integer arithmetic differs in GraphBLAS and MATLAB. In MATLAB, 492% integer values saturate if they exceed their maximum value. In 493% GraphBLAS, integer operators act in a modular fashion. The latter is 494% essential when computing C=A*B over a semiring. A saturating integer 495% operator cannot be used as a monoid since it is not associative. 496 497%% 498C = uint8 (magic (3)) ; 499G = GrB (C) ; 500C1 = C * 40 501C2 = G * uint8 (40) 502S = double (C1 < 255) ; 503assert (isequal (double (C1).*S, double (C2).*S)) 504 505%% An example graph algorithm: breadth-first search 506% The breadth-first search of a graph finds all nodes reachable from the 507% source node, and their level, v. v=GrB.bfs(A,s) or v=bfs_matlab(A,s) 508% compute the same thing, but GrB.bfs uses GraphBLAS matrices and 509% operations, while bfs_matlab uses pure MATLAB operations. v is defined 510% as v(s) = 1 for the source node, v(i) = 2 for nodes adjacent to the 511% source, and so on. 512 513clear 514rng ('default') ; 515n = 1e5 ; 516A = logical (sprandn (n, n, 1e-3)) ; 517 518tic 519v1 = GrB.bfs (A, 1) ; 520gb_time = toc ; 521 522tic 523v2 = bfs_matlab (A, 1) ; 524matlab_time = toc ; 525 526assert (isequal (double (v1'), v2)) 527fprintf ('\nnodes reached: %d of %d\n', nnz (v2), n) ; 528fprintf ('GraphBLAS time: %g sec\n', gb_time) ; 529fprintf ('MATLAB time: %g sec\n', matlab_time) ; 530fprintf ('Speedup of GraphBLAS over MATLAB: %g\n', ... 531 matlab_time / gb_time) ; 532fprintf ('\n# of threads used by GraphBLAS: %d\n', GrB.threads) ; 533 534%% Example graph algorithm: Luby's method in GraphBLAS 535% The GrB.mis function is variant of Luby's randomized algorithm [Luby 536% 1985]. It is a parallel method for finding an maximal independent set 537% of nodes, where no two nodes are adjacent. See the GraphBLAS/@GrB/mis.m 538% function for details. The graph must be symmetric with a zero-free 539% diagonal, so A is symmetrized first and any diagonal entries are removed. 540 541A = GrB (A) ; 542A = GrB.offdiag (A|A') ; 543 544tic 545s = GrB.mis (A) ; 546toc 547fprintf ('# nodes in the graph: %g\n', size (A,1)) ; 548fprintf ('# edges: : %g\n', GrB.entries (A) / 2) ; 549fprintf ('size of maximal independent set found: %g\n', ... 550 full (double (sum (s)))) ; 551 552% make sure it's independent 553p = find (s) ; 554S = A (p,p) ; 555assert (GrB.entries (S) == 0) 556 557% make sure it's maximal 558notp = find (s == 0) ; 559S = A (notp, p) ; 560deg = GrB.vreduce ('+.int64', S) ; 561assert (logical (all (deg > 0))) 562 563%% Sparse deep neural network 564% The 2019 MIT GraphChallenge (see http://graphchallenge.org) is to solve 565% a set of large sparse deep neural network problems. In this demo, the 566% MATLAB reference solution is compared with a solution using GraphBLAS, 567% for a randomly constructed neural network. See the GrB.dnn and 568% dnn_matlab.m functions for details. 569 570clear 571rng ('default') ; 572nlayers = 16 ; 573nneurons = 4096 ; 574nfeatures = 30000 ; 575fprintf ('# layers: %d\n', nlayers) ; 576fprintf ('# neurons: %d\n', nneurons) ; 577fprintf ('# features: %d\n', nfeatures) ; 578fprintf ('# of threads used: %d\n', GrB.threads) ; 579 580tic 581Y0 = sprand (nfeatures, nneurons, 0.1) ; 582for layer = 1:nlayers 583 W {layer} = sprand (nneurons, nneurons, 0.01) * 0.2 ; 584 bias {layer} = -0.2 * ones (1, nneurons) ; 585end 586t_setup = toc ; 587fprintf ('construct problem time: %g sec\n', t_setup) ; 588 589% convert the problem from MATLAB to GraphBLAS 590t = tic ; 591[W_gb, bias_gb, Y0_gb] = dnn_mat2gb (W, bias, Y0) ; 592t = toc (t) ; 593fprintf ('setup time: %g sec\n', t) ; 594 595%% Solving the sparse deep neural network problem with GraphbLAS 596% Please wait ... 597 598tic 599Y1 = GrB.dnn (W_gb, bias_gb, Y0_gb) ; 600gb_time = toc ; 601fprintf ('total time in GraphBLAS: %g sec\n', gb_time) ; 602 603%% Solving the sparse deep neural network problem with MATLAB 604% Please wait ... 605 606tic 607Y2 = dnn_matlab (W, bias, Y0) ; 608matlab_time = toc ; 609fprintf ('total time in MATLAB: %g sec\n', matlab_time) ; 610fprintf ('Speedup of GraphBLAS over MATLAB: %g\n', ... 611 matlab_time / gb_time) ; 612fprintf ('\n# of threads used by GraphBLAS: %d\n', GrB.threads) ; 613 614err = norm (Y1-Y2,1) 615 616%% For objects, GraphBLAS has better colon notation than MATLAB 617% The MATLAB notation C = A (start:inc:fini) is very handy, and 618% it works great if A is a MATLAB matrix. But for objects like 619% the GraphBLAS matrix, MATLAB starts by creating the explicit 620% index vector I = start:inc:fini. That's fine if the matrix is 621% modest in size, but GraphBLAS can construct huge matrices. 622% The problem is that 1:n cannot be explicitly constructed when n 623% is huge. 624% 625% The C API for GraphBLAS can represent the colon notation 626% start:inc:fini in an implicit manner, so it can do the indexing 627% without actually forming the explicit list I = start:inc:fini. 628% But there is no access to this method using the MATLAB notation 629% start:inc:fini. 630% 631% Thus, to compute C = A (start:inc:fini) for very huge matrices, 632% you need to use use a cell array to represent the colon notation, 633% as { start, inc, fini }, instead of start:inc:fini. See 634% 'help GrB.extract', 'help GrB.assign' for the functional form. 635% For the overloaded syntax C(I,J)=A and C=A(I,J), see 636% 'help GrB/subsasgn' and 'help GrB/subsref'. The cell array 637% syntax isn't conventional, but it is far faster than the MATLAB 638% colon notation for objects, and takes far less memory when I is huge. 639 640%% 641n = 1e14 ; 642H = GrB (n, n) ; % a huge empty matrix 643I = [1 1e9 1e12 1e14] ; 644M = magic (4) 645H (I,I) = M ; 646J = {1, 1e13} ; % represents 1:1e13 colon notation 647C1 = H (J, J) % computes C1 = H (1:e13,1:1e13) 648c = nonzeros (C1) ; 649m = nonzeros (M (1:3, 1:3)) ; 650assert (isequal (c, m)) ; 651 652%% 653try 654 % try to compute the same thing with colon 655 % notation (1:1e13), but this fails: 656 C2 = H (1:1e13, 1:1e13) 657catch me 658 error_expected = me 659end 660 661%% Iterative solvers work as-is 662% Many built-in functions work with GraphBLAS matrices unmodified. 663 664A = sparse (rand (4)) ; 665b = sparse (rand (4,1)) ; 666x = gmres (A,b) 667norm (A*x-b) 668x = gmres (GrB(A), GrB(b)) 669norm (A*x-b) 670 671%% ... even in single precision 672x = gmres (GrB(A,'single'), GrB(b,'single')) 673norm (A*x-b) 674 675%% 676% Both of the following uses of minres (A,b) fail to converge because A 677% is not symmetric, as the method requires. Both failures are correctly 678% reported, and both the MATLAB version and the GraphBLAS version return 679% the same incorrect vector x. 680 681x = minres (A, b) 682x = minres (GrB(A), GrB(b)) 683 684%% 685% With a proper symmetric matrix 686 687A = A+A' ; 688x = minres (A, b) 689norm (A*x-b) 690x = minres (GrB(A), GrB(b)) 691norm (A*x-b) 692 693%% Extreme performance differences between GraphBLAS and MATLAB. 694% The GraphBLAS operations used so far are perhaps 2x to 50x faster than 695% the corresponding MATLAB operations, depending on how many cores your 696% computer has. To run a demo illustrating a 500x or more speedup versus 697% MATLAB, run this demo: 698% 699% gbdemo2 700% 701% It will illustrate an assignment C(I,J)=A that can take under a second 702% in GraphBLAS but several minutes in MATLAB. To make the comparsion 703% even more dramatic, try: 704% 705% gbdemo2 (20000) 706% 707% assuming you have enough memory. 708 709%% Sparse logical indexing is much, much faster in GraphBLAS 710% The mask in GraphBLAS acts much like logical indexing in MATLAB, but it 711% is not quite the same. MATLAB logical indexing takes the form: 712% 713% C (M) = A (M) 714% 715% which computes the same thing as the GraphBLAS statement: 716% 717% C = GrB.assign (C, M, A) 718% 719% The GrB.assign statement computes C(M)=A(M), and it is vastly faster 720% than C(M)=A(M) for MATLAB sparse matrices, even if the time to convert 721% the GrB matrix back to a MATLAB sparse matrix is included. 722% 723% GraphBLAS can also compute C(M)=A(M) using overloaded operators for 724% subsref and subsasgn, but C = GrB.assign (C, M, A) is a bit faster. 725% 726% Here are both methods in GraphBLAS (both are very fast). Setting up: 727 728clear 729n = 4000 ; 730tic 731C = sprand (n, n, 0.1) ; 732A = 100 * sprand (n, n, 0.1) ; 733M = (C > 0.5) ; 734t_setup = toc ; 735fprintf ('nnz(C): %g, nnz(M): %g, nnz(A): %g\n', ... 736 nnz(C), nnz(M), nnz(A)) ; 737fprintf ('\nsetup time: %g sec\n', t_setup) ; 738 739%% First method in GraphBLAS, with GrB.assign 740% Including the time to convert C1 from a GraphBLAS 741% matrix to a MATLAB sparse matrix: 742tic 743C1 = GrB.assign (C, M, A) ; 744C1 = double (C1) ; 745gb_time = toc ; 746fprintf ('\nGraphBLAS time: %g sec for GrB.assign\n', gb_time) ; 747 748%% Second method in GraphBLAS, with C(M)=A(M) 749% now using overloaded operators, also include the time to 750% convert back to a MATLAB sparse matrix, for good measure: 751A2 = GrB (A) ; 752C2 = GrB (C) ; 753tic 754C2 (M) = A2 (M) ; 755C2 = double (C2) ; 756gb_time2 = toc ; 757fprintf ('\nGraphBLAS time: %g sec for C(M)=A(M)\n', gb_time2) ; 758 759%% Now with MATLAB matrices, with C(M)=A(M) 760% Please wait, this will take about 10 minutes or so ... 761 762tic 763C (M) = A (M) ; 764matlab_time = toc ; 765 766fprintf ('\nGraphBLAS time: %g sec (GrB.assign)\n', gb_time) ; 767fprintf ('GraphBLAS time: %g sec (overloading)\n', gb_time2) ; 768fprintf ('MATLAB time: %g sec\n', matlab_time) ; 769fprintf ('Speedup of GraphBLAS (overloading) over MATLAB: %g\n', ... 770 matlab_time / gb_time2) ; 771fprintf ('Speedup of GraphBLAS (GrB.assign) over MATLAB: %g\n', ... 772 matlab_time / gb_time) ; 773fprintf ('\n# of threads used by GraphBLAS: %d\n', GrB.threads) ; 774 775assert (isequal (C1, C)) 776assert (isequal (C2, C)) 777fprintf ('Results of GrB and MATLAB match perfectly.\n') 778 779%% Limitations and their future solutions 780% The MATLAB interface for SuiteSparse:GraphBLAS is a work-in-progress. 781% It has some limitations, most of which will be resolved over time. 782% 783% (1) Nonblocking mode: 784% 785% GraphBLAS has a 'non-blocking' mode, in which operations can be left 786% pending and completed later. SuiteSparse:GraphBLAS uses the 787% non-blocking mode to speed up a sequence of assignment operations, such 788% as C(I,J)=A. However, in its MATLAB interface, this would require a 789% MATLAB mexFunction to modify its inputs. That breaks the MATLAB API 790% standard, so it cannot be safely done. As a result, using GraphBLAS 791% via its MATLAB interface can be slower than when using its C API. 792 793%% 794% (2) Integer element-wise operations: 795% 796% Integer operations in MATLAB saturate, so that uint8(255)+1 is 255. To 797% allow for integer monoids, GraphBLAS uses modular arithmetic instead. 798% This is the only way that C=A*B can be defined for integer semirings. 799% However, saturating integer operators could be added in the future, so 800% that element- wise integer operations on GraphBLAS sparse integer 801% matrices could work just the same as their MATLAB counterparts. 802% 803% So in the future, you could perhaps write this, for both sparse and 804% dense integer matrices A and B: 805% 806% C = GrB.eadd ('+saturate.int8', A, B) 807% 808% to compute the same thing as C=A+B in MATLAB for its full int8 809% matrices. Note that MATLAB can do this only for dense integer 810% matrices, since it doesn't support sparse integer matrices. 811 812%% 813% (3) Faster methods: 814% 815% Most methods in this MATLAB interface are based on efficient parallel C 816% functions in GraphBLAS itself, and are typically as fast or faster than 817% the equivalent built-in operators and functions in MATLAB. 818% 819% There are few notable exceptions; these will be addressed in the future. 820% These include bandwidth, istriu, istril, isdiag, reshape, issymmetric, 821% and ishermitian, all of which should be faster in a future release. 822 823%% 824% Here is an example that illustrates the performance of istril. 825A = sparse (rand (2000)) ; 826tic 827c1 = istril (A) ; 828matlab_time = toc ; 829A = GrB (A) ; 830tic 831c2 = istril (A) ; 832gb_time = toc ; 833fprintf ('\nMATLAB: %g sec, GraphBLAS: %g sec\n', ... 834 matlab_time, gb_time) ; 835if (gb_time > matlab_time) 836 fprintf ('GraphBLAS is slower by a factor of %g\n', ... 837 gb_time / matlab_time) ; 838end 839 840%% 841% (4) Linear indexing: 842% 843% If A is an m-by-n 2D MATLAB matrix, with n > 1, A(:) is a column vector 844% of length m*n. The index operation A(i) accesses the ith entry in the 845% vector A(:). This is called linear indexing in MATLAB. It is not yet 846% available for GraphBLAS matrices in this MATLAB interface to GraphBLAS, 847% but will be added in the future. 848 849%% 850% (5) Implicit singleton dimension expansion 851% 852% In MATLAB C=A+B where A is m-by-n and B is a 1-by-n row vector 853% implicitly expands B to a matrix, computing C(i,j)=A(i,j)+B(j). This 854% implicit expansion is not yet suported in GraphBLAS with C=A+B. 855% However, it can be done with C = GrB.mxm ('+.+', A, diag(GrB(B))). 856% That's a nice example of the power of semirings, but it's not 857% immediately obvious, and not as clear a syntax as C=A+B. The 858% GraphBLAS/@GrB/dnn.m function uses this 'plus.plus' semiring to 859% apply the bias to each neuron. 860 861A = magic (3) 862B = 1000:1000:3000 863C1 = A + B 864C2 = GrB.mxm ('+.+', A, diag (GrB (B))) 865err = norm (C1-C2,1) 866 867%% 868% (6) MATLAB object overhead. 869% 870% The GrB matrix is a MATLAB object, and there are some cases where 871% performance issues can arise as a result. Extracting the contents of 872% a MATLAB object (G.field) takes much more time than for a MATLAB struct 873% with % the same syntax, and building an object has similar issues. The 874% difference is small, and it does not affect large problems. But if you 875% have many calls to GrB operations with a small amount of work, then the 876% time can be dominated by the MATLAB object-oriented overhead. 877% 878% There is no solution or workaround to this issue. 879 880A = rand (3,4) ; 881G = GrB (A) ; 882tic 883for k = 1:100000 884 [m, n] = size (A) ; 885end 886toc 887tic 888for k = 1:100000 889 [m, n] = size (G) ; 890end 891toc 892 893%% GraphBLAS operations 894% In addition to the overloaded operators (such as C=A*B) and overloaded 895% functions (such as L=tril(A)), GraphBLAS also has methods of the form 896% GrB.method. Most of them take an optional input matrix Cin, which is 897% the initial value of the matrix C for the expression below, an optional 898% mask matrix M, and an optional accumulator operator. 899% 900% in GrB syntax: C<#M,replace> = accum (C, A*B) 901% 902% in @GrB MATLAB: C = GrB.mxm (Cin, M, accum, semiring, A, B, desc) ; 903% 904% In the above expression, #M is either empty (no mask), M (with a mask 905% matrix) or ~M (with a complemented mask matrix), as determined by the 906% descriptor (desc). 'replace' can be used to clear C after it is used 907% in accum(C,T) but before it is assigned with C<...> = Z, where 908% Z=accum(C,T). The matrix T is the result of some operation, such as 909% T=A*B for GrB.mxm, or T=op(A,B) for GrB.eadd. 910% 911% For a complete list of GraphBLAS overloaded operators and methods, type: 912% 913% help GrB 914% 915% Thanks for watching! 916% 917% Tim Davis, Texas A&M University, http://faculty.cse.tamu.edu/davis, 918% https://twitter.com/DocSparse 919 920