2.1 Built-in Functions and Message Passing APIs¶
In DGL, message function takes a single argument edges
,
which is an EdgeBatch
instance. During message passing,
DGL generates it internally to represent a batch of edges. It has three
members src
, dst
and data
to access features of source nodes,
destination nodes, and edges, respectively.
reduce function takes a single argument nodes
, which is a
NodeBatch
instance. During message passing,
DGL generates it internally to represent a batch of nodes. It has member
mailbox
to access the messages received for the nodes in the batch.
Some of the most common reduce operations include sum
, max
, min
, etc.
update function takes a single argument nodes
as described above.
This function operates on the aggregation result from reduce function
, typically
combining it with a node’s original feature at the the last step and saving the result
as a node feature.
DGL has implemented commonly used message functions and reduce functions
as built-in in the namespace dgl.function
. In general, DGL
suggests using built-in functions whenever possible since they are
heavily optimized and automatically handle dimension broadcasting.
If your message passing functions cannot be implemented with built-ins, you can implement user-defined message/reduce function (aka. UDF).
Built-in message functions can be unary or binary. DGL supports copy
for unary. For binary funcs, DGL supports add
, sub
, mul
, div
,
dot
. The naming convention for message built-in funcs is that u
represents src
nodes, v
represents dst
nodes, and e
represents edges
.
The parameters for those functions are strings indicating the input and output field names for
the corresponding nodes and edges. The list of supported built-in functions
can be found in DGL Built-in Function. For example, to add the hu
feature from src
nodes and hv
feature from dst nodes then save the result on the edge
at he
field, one can use built-in function dgl.function.u_add_v('hu', 'hv', 'he')
.
This is equivalent to the Message UDF:
def message_func(edges):
return {'he': edges.src['hu'] + edges.dst['hv']}
Built-in reduce functions support operations sum
, max
, min
,
and mean
. Reduce functions usually have two parameters, one
for field name in mailbox
, one for field name in node features, both
are strings. For example, dgl.function.sum('m', 'h')
is equivalent
to the Reduce UDF that sums up the message m
:
import torch
def reduce_func(nodes):
return {'h': torch.sum(nodes.mailbox['m'], dim=1)}
For advanced usage of UDF, see User-defined Functions.
It is also possible to invoke only edge-wise computation by apply_edges()
without invoking message passing. apply_edges()
takes a message function
for parameter and by default updates the features of all edges. For example:
import dgl.function as fn
graph.apply_edges(fn.u_add_v('el', 'er', 'e'))
For message passing, update_all()
is a high-level
API that merges message generation, message aggregation and node update
in a single call, which leaves room for optimization as a whole.
The parameters for update_all()
are a message function, a
reduce function and an update function. One can call update function outside of
update_all
and not specify it in invoking update_all()
.
DGL recommends this approach since the update function can usually be
written as pure tensor operations to make the code concise. For
example:
def update_all_example(graph):
# store the result in graph.ndata['ft']
graph.update_all(fn.u_mul_e('ft', 'a', 'm'),
fn.sum('m', 'ft'))
# Call update function outside of update_all
final_ft = graph.ndata['ft'] * 2
return final_ft
This call will generate the messages m
by multiply src node features
ft
and edge features a
, sum up the messages m
to update node
features ft
, and finally multiply ft
by 2 to get the result
final_ft
. After the call, DGL will clean the intermediate messages m
.
The math formula for the above function is:
DGL’s built-in functions support floating point data types, i.e. the feature must
be half
(float16
) /float
/double
tensors.
float16
data type support is disabled by default as it has a minimum GPU
compute capacity requirement of sm_53
(Pascal, Volta, Turing and Ampere
architectures).
User can enable float16 for mixed precision training by compiling DGL from source (see Mixed Precision Training tutorial for details).