Graph-Level Tensors: Pooling and Readout for Structured Node States
Many tasks ask a question about a whole graph, not its individual nodes: is this molecule toxic, does this scene contain a particular configuration, what class is this object graph? Answering requires collapsing a variable-sized set of node states into one fixed-size graph-level representation — the readout. When nodes are flat vectors this is routine. When nodes are [C, H, W] tensors, the readout has to respect two structures at once, and the order in which you collapse them matters. This note works through graph-level pooling in TGraphX, grounded in tgraphx/layers/pooling.py.
It assumes the shape-algebra view of how axes flow through a model.
The readout must be permutation-invariant
A graph has no node ordering, so a graph-level readout R must give the same answer regardless of how nodes are listed:
z_G = R( { h_v : v ∈ V } ) with R(π·set) = R(set) for any permutation π
The standard way to guarantee this is to use a symmetric reduction over nodes. TGraphX provides the three canonical ones in tgraphx/layers/pooling.py, each taking node features x and the batch vector that says which nodes belong to which graph:
| Function | Reduction | Character |
|---|---|---|
global_sum_pool(x, batch) |
Σ_{v∈Gᵢ} h_v |
size-sensitive; preserves totals |
global_mean_pool(x, batch) |
(1/|Gᵢ|) Σ h_v |
size-normalised; average node |
global_max_pool(x, batch) |
element-wise max | salience; "is feature present" |
Each is permutation-invariant by construction — summation, averaging, and maximum do not depend on node order — so any of them yields a valid readout. The choice encodes a prior: sum keeps magnitude and graph-size information, mean removes size, max emphasises the strongest activation. The batch vector is what makes this work for a GraphBatch: it lets one call pool a disjoint union of many graphs into [B, …] rows, as described in the batching internals.
Order of operations for tensor nodes
With [C, H, W] nodes there are two axes to collapse — the spatial axes inside each node and the node axis across the graph — and the usual, well-behaved order is spatial first, then graph:
[N, C, H, W] --spatial reduce--> [N, C] --global_*_pool(·, batch)--> [B, C] --head--> [B, classes]
Reducing spatial dimensions inside each node first turns every node into a vector, after which the global pool produces a per-graph vector. Doing it the other way (pooling nodes while they are still feature maps) is possible when all nodes share spatial dimensions, but it couples the two reductions in ways that are easy to misread. The clean separation keeps the shape algebra legible and the permutation-invariance argument intact. GraphClassifier in tgraphx/models/graph_classifier.py wires a message-passing stack, a readout, and a classification head into this pattern.
Graph-level inputs vs graph-level targets
A subtle but important data-model point: TGraphX distinguishes a graph-level input feature from a graph-level label. The api-stability contract notes that graph_features= was added in v1.0.2 as a distinct field, explicitly not aliased to graph_label. So a graph can carry side information about itself (a global descriptor) separately from its supervision target. Conflating those two is a classic source of subtle leakage — the model accidentally reads the answer from an "input" that is really the label — and keeping them separate at the data-structure level prevents it.
Honest framing
Three caveats keep this accurate. First, sum/mean/max are simple global readouts; they discard a lot of structure, and tasks needing hierarchical or attention-based pooling would require building that on top — TGraphX does not present global pooling as a universal solution. Second, the spatial reduction choice (mean vs flatten-then-project) is a modelling decision with accuracy consequences you should validate rather than assume. Third, max-pooling is permutation-invariant but not differentiable everywhere; this is standard and rarely a problem in practice, but worth knowing.
How to describe this accurately
A safe summary: TGraphX produces graph-level representations with permutation-invariant global_sum_pool / global_mean_pool / global_max_pool over the batch vector; for tensor-valued nodes, spatial dimensions are typically reduced before graph pooling, and a distinct graph_features field carries graph-level inputs separately from labels. That maps directly to tgraphx/layers/pooling.py and the api-stability contract.
When to reach for mean, sum, or max
The three readouts encode different priors, and the choice is rarely arbitrary. Use global_mean_pool when graph size should not affect the prediction — averaging makes a 10-node and a 1000-node graph comparable. Use global_sum_pool when totals matter and size is informative, for instance when "more of feature X" should push the output regardless of normalisation; sum is also the readout most aligned with the expressiveness arguments for injective aggregation. Use global_max_pool when the task is about presence — does any node exhibit a salient feature — rather than about averages.
A common, slightly stronger pattern is to concatenate two readouts (mean and max, say) so the head sees both the typical and the extreme node, at the cost of doubling the readout width. None of these is universally preferable; the right choice depends on whether your label is driven by averages, totals, or salient outliers, which is an empirical question worth a quick ablation rather than a default.
Related guides
- Shape Algebra Through GNN Layers
- Inside Tensor Message Passing
- Tensor-Valued Nodes in Graph Neural Networks
Conclusion
Turning a graph of tensor-valued nodes into one vector means choosing a permutation-invariant readout and collapsing the spatial axis before the node axis. TGraphX's global_*_pool functions provide the invariant reductions, GraphClassifier wires the standard pattern, and the separate graph_features field keeps graph-level inputs from contaminating labels. Get the order and the invariance right and graph-level prediction over tensor nodes is no harder than the vector case.