@@ -395,6 +395,161 @@ def block_parent_eid(self, block_id):
395
395
assert F .asnumpy (F .sum (ret == - 1 , 0 )) == 0 , "The eid in the parent graph is invalid."
396
396
return ret
397
397
398
+ def block_edges (self , block_id ):
399
+ """Return the edges in a block.
400
+
401
+ Parameters
402
+ ----------
403
+ block_id : int
404
+ The specified block to return the edges.
405
+
406
+ Returns
407
+ -------
408
+ Tensor
409
+ The src nodes.
410
+ Tensor
411
+ The dst nodes.
412
+ Tensor
413
+ The edge ids.
414
+ """
415
+ layer0_size = self ._layer_offsets [block_id + 1 ] - self ._layer_offsets [block_id ]
416
+ rst = _CAPI_NodeFlowGetBlockAdj (self ._graph ._handle , "coo" , layer0_size ,
417
+ self ._layer_offsets [block_id + 1 ],
418
+ self ._layer_offsets [block_id + 2 ])
419
+ idx = utils .toindex (rst (0 )).tousertensor ()
420
+ eid = utils .toindex (rst (1 ))
421
+ num_edges = int (len (idx ) / 2 )
422
+ assert len (eid ) == num_edges
423
+ return idx [num_edges :len (idx )], idx [0 :num_edges ], eid .tousertensor ()
424
+
425
+ def block_adjacency_matrix (self , block_id , ctx ):
426
+ """Return the adjacency matrix representation for a specific block in a NodeFlow.
427
+
428
+ A row of the returned adjacency matrix represents the destination
429
+ of an edge and the column represents the source.
430
+
431
+ Parameters
432
+ ----------
433
+ block_id : int
434
+ The specified block to return the adjacency matrix.
435
+ ctx : context
436
+ The context of the returned matrix.
437
+
438
+ Returns
439
+ -------
440
+ SparseTensor
441
+ The adjacency matrix.
442
+ Tensor
443
+ A index for data shuffling due to sparse format change. Return None
444
+ if shuffle is not required.
445
+ """
446
+ fmt = F .get_preferred_sparse_format ()
447
+ # We need to extract two layers.
448
+ layer0_size = self ._layer_offsets [block_id + 1 ] - self ._layer_offsets [block_id ]
449
+ rst = _CAPI_NodeFlowGetBlockAdj (self ._graph ._handle , fmt , layer0_size ,
450
+ self ._layer_offsets [block_id + 1 ],
451
+ self ._layer_offsets [block_id + 2 ])
452
+ num_rows = self .layer_size (block_id + 1 )
453
+ num_cols = self .layer_size (block_id )
454
+
455
+ if fmt == "csr" :
456
+ indptr = F .copy_to (utils .toindex (rst (0 )).tousertensor (), ctx )
457
+ indices = F .copy_to (utils .toindex (rst (1 )).tousertensor (), ctx )
458
+ shuffle = utils .toindex (rst (2 ))
459
+ dat = F .ones (indices .shape , dtype = F .float32 , ctx = ctx )
460
+ return F .sparse_matrix (dat , ('csr' , indices , indptr ),
461
+ (num_rows , num_cols ))[0 ], shuffle .tousertensor ()
462
+ elif fmt == "coo" :
463
+ ## FIXME(minjie): data type
464
+ idx = F .copy_to (utils .toindex (rst (0 )).tousertensor (), ctx )
465
+ m = self .block_size (block_id )
466
+ idx = F .reshape (idx , (2 , m ))
467
+ dat = F .ones ((m ,), dtype = F .float32 , ctx = ctx )
468
+ adj , shuffle_idx = F .sparse_matrix (dat , ('coo' , idx ), (num_rows , num_cols ))
469
+ return adj , shuffle_idx
470
+ else :
471
+ raise Exception ("unknown format" )
472
+
473
+ def block_incidence_matrix (self , block_id , typestr , ctx ):
474
+ """Return the incidence matrix representation of the block.
475
+
476
+ An incidence matrix is an n x m sparse matrix, where n is
477
+ the number of nodes and m is the number of edges. Each nnz
478
+ value indicating whether the edge is incident to the node
479
+ or not.
480
+
481
+ There are three types of an incidence matrix `I`:
482
+ * "in":
483
+ - I[v, e] = 1 if e is the in-edge of v (or v is the dst node of e);
484
+ - I[v, e] = 0 otherwise.
485
+ * "out":
486
+ - I[v, e] = 1 if e is the out-edge of v (or v is the src node of e);
487
+ - I[v, e] = 0 otherwise.
488
+ * "both":
489
+ - I[v, e] = 1 if e is the in-edge of v;
490
+ - I[v, e] = -1 if e is the out-edge of v;
491
+ - I[v, e] = 0 otherwise (including self-loop).
492
+
493
+ Parameters
494
+ ----------
495
+ block_id : int
496
+ The specified block to return the incidence matrix.
497
+ typestr : str
498
+ Can be either "in", "out" or "both"
499
+ ctx : context
500
+ The context of returned incidence matrix.
501
+
502
+ Returns
503
+ -------
504
+ SparseTensor
505
+ The incidence matrix.
506
+ Tensor
507
+ A index for data shuffling due to sparse format change. Return None
508
+ if shuffle is not required.
509
+ """
510
+ src , dst , eid = self .block_edges (block_id )
511
+ src = F .copy_to (src , ctx ) # the index of the ctx will be cached
512
+ dst = F .copy_to (dst , ctx ) # the index of the ctx will be cached
513
+ eid = F .copy_to (eid , ctx ) # the index of the ctx will be cached
514
+ if typestr == 'in' :
515
+ n = self .layer_size (block_id + 1 )
516
+ m = self .block_size (block_id )
517
+ row = F .unsqueeze (dst , 0 )
518
+ col = F .unsqueeze (eid , 0 )
519
+ idx = F .cat ([row , col ], dim = 0 )
520
+ # FIXME(minjie): data type
521
+ dat = F .ones ((m ,), dtype = F .float32 , ctx = ctx )
522
+ inc , shuffle_idx = F .sparse_matrix (dat , ('coo' , idx ), (n , m ))
523
+ elif typestr == 'out' :
524
+ n = self .layer_size (block_id )
525
+ m = self .block_size (block_id )
526
+ row = F .unsqueeze (src , 0 )
527
+ col = F .unsqueeze (eid , 0 )
528
+ idx = F .cat ([row , col ], dim = 0 )
529
+ # FIXME(minjie): data type
530
+ dat = F .ones ((m ,), dtype = F .float32 , ctx = ctx )
531
+ inc , shuffle_idx = F .sparse_matrix (dat , ('coo' , idx ), (n , m ))
532
+ elif typestr == 'both' :
533
+ # TODO does it work for bipartite graph?
534
+ # first remove entries for self loops
535
+ mask = F .logical_not (F .equal (src , dst ))
536
+ src = F .boolean_mask (src , mask )
537
+ dst = F .boolean_mask (dst , mask )
538
+ eid = F .boolean_mask (eid , mask )
539
+ n_entries = F .shape (src )[0 ]
540
+ # create index
541
+ row = F .unsqueeze (F .cat ([src , dst ], dim = 0 ), 0 )
542
+ col = F .unsqueeze (F .cat ([eid , eid ], dim = 0 ), 0 )
543
+ idx = F .cat ([row , col ], dim = 0 )
544
+ # FIXME(minjie): data type
545
+ x = - F .ones ((n_entries ,), dtype = F .float32 , ctx = ctx )
546
+ y = F .ones ((n_entries ,), dtype = F .float32 , ctx = ctx )
547
+ dat = F .cat ([x , y ], dim = 0 )
548
+ inc , shuffle_idx = F .sparse_matrix (dat , ('coo' , idx ), (n , m ))
549
+ else :
550
+ raise DGLError ('Invalid incidence matrix type: %s' % str (typestr ))
551
+ return inc , shuffle_idx
552
+
398
553
def set_n_initializer (self , initializer , layer_id = ALL , field = None ):
399
554
"""Set the initializer for empty node features.
400
555
@@ -651,12 +806,13 @@ def block_compute(self, block_id, message_func="default", reduce_func="default",
651
806
assert reduce_func is not None
652
807
653
808
if is_all (v ):
654
- dest_nodes = utils .toindex (self .layer_nid (block_id + 1 ))
655
- u , v , _ = self ._graph .in_edges (dest_nodes )
656
- u = utils .toindex (self ._glb2lcl_nid (u .tousertensor (), block_id ))
657
- v = utils .toindex (self ._glb2lcl_nid (v .tousertensor (), block_id + 1 ))
658
- dest_nodes = utils .toindex (F .arange (0 , self .layer_size (block_id + 1 )))
659
- eid = utils .toindex (F .arange (0 , self .block_size (block_id )))
809
+ with ir .prog () as prog :
810
+ scheduler .schedule_nodeflow_update_all (graph = self ,
811
+ block_id = block_id ,
812
+ message_func = message_func ,
813
+ reduce_func = reduce_func ,
814
+ apply_func = apply_node_func )
815
+ Runtime .run (prog )
660
816
else :
661
817
dest_nodes = utils .toindex (v )
662
818
u , v , eid = self ._graph .in_edges (dest_nodes )
@@ -667,18 +823,18 @@ def block_compute(self, block_id, message_func="default", reduce_func="default",
667
823
block_id + 1 ))
668
824
eid = utils .toindex (self ._glb2lcl_eid (eid .tousertensor (), block_id ))
669
825
670
- with ir .prog () as prog :
671
- scheduler .schedule_nodeflow_compute (graph = self ,
672
- block_id = block_id ,
673
- u = u ,
674
- v = v ,
675
- eid = eid ,
676
- dest_nodes = dest_nodes ,
677
- message_func = message_func ,
678
- reduce_func = reduce_func ,
679
- apply_func = apply_node_func ,
680
- inplace = inplace )
681
- Runtime .run (prog )
826
+ with ir .prog () as prog :
827
+ scheduler .schedule_nodeflow_compute (graph = self ,
828
+ block_id = block_id ,
829
+ u = u ,
830
+ v = v ,
831
+ eid = eid ,
832
+ dest_nodes = dest_nodes ,
833
+ message_func = message_func ,
834
+ reduce_func = reduce_func ,
835
+ apply_func = apply_node_func ,
836
+ inplace = inplace )
837
+ Runtime .run (prog )
682
838
683
839
def prop_flow (self , message_funcs = "default" , reduce_funcs = "default" ,
684
840
apply_node_funcs = "default" , flow_range = ALL , inplace = False ):
0 commit comments