Backends¶
edge2torch can compile the same architecture edgelist into different
backend-specific sparse PyTorch model classes.
Each backend answers a slightly different question:
- how should graph structure be translated into neural-network computation?
- which graph properties are allowed?
- what internal execution structure should be created?
The goal of edge2torch is to stay minimally opinionated. A backend
defines only the structural semantics required for compilation. Broader
modeling choices such as activations, heads, losses, optimizers, and training
loops remain the user's responsibility.
Overview¶
The currently implemented backends are:
feedforwardrecurrentgraphnn
All three backends share the same high-level interface:
- they are created with
compile_graph(...) - they return a normal PyTorch
nn.Module - they return a
CompileArtifactthat preserves information needed for alignment, inspection, and interpretation
Across all backends, input and output nodes are inferred from graph structure:
- input nodes are nodes with no incoming edges
- output nodes are nodes with no outgoing edges
The current implementation enforces sparse graph-derived connectivity with masked dense PyTorch layers. These masks constrain trainable edge weights: a source-to-target weighted connection can contribute only where the compiled graph contains the corresponding directed edge.
Compiled layers include bias terms by default. Biases are node-level offsets,
not graph edges, and are not constrained by the edge mask. Set bias=False in
compile_graph() to remove these offsets so node updates depend only on
graph-defined weighted inputs.
The use of masked dense layers should not be interpreted as sparse tensor acceleration, which is planned as a future addition.
Common input format¶
All backends currently start from the same edgelist representation.
The edgelist must contain two required columns:
sourcetarget
For example:
import pandas as pd
edgelist = pd.DataFrame(
{
"source": ["feature_a", "feature_b", "hidden"],
"target": ["hidden", "hidden", "prediction"],
}
)
Each row defines a directed edge from one named node to another. The edgelist may come from domain knowledge, a manually designed sparse architecture, a discovered graph, or any other source that can be represented as directed connections between named nodes.
The backend changes the compiled execution semantics, not the graph input format.
Feedforward backend¶
Meaning¶
The feedforward backend compiles the graph into a strictly layer-wise sparse
PyTorch model.
This backend assumes that the graph can be organized into successive layers. If the graph contains skip edges, these are expanded internally using pseudo nodes so that the final compiled computation remains strictly adjacent layer-to-layer.
Structural properties¶
The feedforward backend:
- expects an acyclic, layerable directed graph
- compiles computation into successive layer blocks
- uses masks to enforce graph-derived connectivity between adjacent layers
- expands skip edges internally through pseudo nodes when needed
Internal execution pattern¶
Conceptually, the feedforward backend behaves like a sparse multilayer
perceptron whose connectivity pattern is derived from the edgelist.
The compiled model contains one computation block per adjacent layer pair.
Each block applies a masked linear transformation so that only graph-defined
weighted connections contribute through edge weights. If bias=True, target
nodes may also have learned node-level offsets. Set bias=False to remove
these offsets.
Pseudo nodes¶
Pseudo nodes are internal compiler-generated nodes used by the feedforward
backend to represent skip edges in a strictly layer-wise form.
They are stored in the compilation artifact for internal bookkeeping, but they are hidden from user-facing node attribution outputs. User-facing node interpretation reports named graph nodes rather than compiler-generated pseudo nodes.
When to use it¶
Use feedforward when:
- the graph is naturally hierarchical or approximately hierarchical
- you want a sparse layered architecture
Recurrent backend¶
Meaning¶
The recurrent backend compiles the graph into a recurrent node-state model.
Instead of layering the graph into a sequence of feedforward blocks, this backend keeps the original graph topology and applies repeated state updates over the graph for a configurable number of steps.
Structural properties¶
The recurrent backend:
- allows cyclic graphs
- keeps the original graph topology
- requires at least one inferred input node
- requires at least one inferred output node
- requires every node that can influence an output node to be reachable from at least one inferred input node
- updates node states repeatedly over multiple steps
- uses masks to enforce graph-defined recurrent connectivity
- re-injects input-node values after each recurrent update step
Cycles are allowed, but disconnected components that can influence an output are rejected because their contributions cannot depend on the provided input features.
Internal execution pattern¶
Conceptually, the recurrent backend behaves like a sparse recurrent neural
system over graph nodes.
Each node has a position in a global node-state vector. At each step, the model updates node states using the graph-defined connectivity mask. Input nodes are then re-injected so that external inputs remain anchored across recurrent updates.
The steps argument to compile_graph() controls how many recurrent update
steps are applied during each forward pass. It is not a training epoch count
and does not represent a sequence length in the input data.
Pseudo nodes¶
Pseudo nodes are not used by the recurrent backend.
They are not required by the current recurrent compilation strategy because the backend does not need to force computation into adjacent feedforward layers.
When to use it¶
Use recurrent when:
- the graph contains cycles
- repeated state updates are a natural representation of the system
- you want a graph-structured recurrent model rather than a layered one
GraphNN backend¶
Meaning¶
The graphnn backend compiles the graph into a minimal graph-oriented
state-update model over named node states.
Like the recurrent backend, it keeps the original graph topology instead of
forcing the graph into a layer-wise feedforward structure. In the current
implementation, graphnn is intentionally close to recurrent: both use a
single node-state vector, masked dense updates, configurable fixed-step
iteration, and input-node re-injection.
The graphnn backend should therefore be understood as a lightweight
message-passing-style interface and extension point, not as a full-featured
graph neural network library backend.
Structural properties¶
The graphnn backend:
- allows cyclic or non-layerable graphs
- keeps the original graph structure
- requires at least one inferred input node
- requires at least one inferred output node
- requires every node that can influence an output node to be reachable from at least one inferred input node
- performs repeated graph-defined node-state updates
- uses masks to enforce graph-derived connectivity
- re-injects input-node values after each update step
- currently uses the same minimal masked-update primitive as the recurrent backend
Cycles are allowed, but disconnected components that can influence an output are rejected because their contributions cannot depend on the provided input features.
Internal execution pattern¶
Conceptually, the current graphnn backend applies a configurable number of
message-passing-style updates over scalar node states.
It provides a conservative graph-oriented backend that fits the same compile/train/interpret interface as the other backends, while leaving room for future backend-specific extensions such as richer message functions, aggregation choices, normalization, residual updates, or edge features.
The steps argument to compile_graph() controls how many graph update steps
are applied during each forward pass. It is not a training epoch count and does
not represent a sequence length in the input data.
Pseudo nodes¶
Pseudo nodes are not used by the graphnn backend.
GraphNN-style models operate directly on the original graph topology, so pseudo-node expansion is not used as a default mechanism for this backend.
When to use it¶
Use graphnn when:
- you want to preserve the original graph topology
- the graph is not naturally feedforward
- you want a minimal graph-oriented state-update backend
- you want a backend that can serve as a future extension point for message-passing-style models
Summary of backend behavior¶
| Backend | Layered execution | Cycles allowed | Pseudo nodes | Main internal idea |
|---|---|---|---|---|
feedforward |
yes | no | yes | sparse layer-wise computation |
recurrent |
no | yes | no | sparse recurrent node-state updates |
graphnn |
no | yes | no | minimal message-passing-style node updates |
Interpretation support¶
Node and feature interpretation are available for all implemented backends.
| Backend | Feature attribution | Node attribution |
|---|---|---|
feedforward |
yes | yes |
recurrent |
yes | yes |
graphnn |
yes | yes |
Feature attribution is available through feature-level Captum methods such as
IntegratedGradients, Saliency, and DeepLift.
Node attribution is available through layer-level Captum methods such as
LayerConductance and LayerIntegratedGradients.
Interpretation sites¶
Node attribution is computed at interpretation sites inside the compiled model:
feedforward: one site per non-input layer (layer_1,layer_2, ...)recurrentandgraphnn: one site per unrolled state-update step (step_1,step_2, ...)
Pseudo nodes used internally by the feedforward backend are never exposed in user-facing node interpretation output.
Summary vs per-site node results¶
interpret_model(..., target="nodes") supports two detail levels:
level |
Return type | Meaning |
|---|---|---|
"summary" (default) |
pandas.DataFrame |
One node-importance table per sample |
"sites" |
dict[str, pandas.DataFrame] |
One table per interpretation site |
Use the nodes parameter to filter which graph nodes appear in the result:
nodes |
Included nodes |
|---|---|
"hidden" (default) |
Internal graph nodes only |
"non_input" |
All nodes except inputs (includes outputs) |
"all" |
All visible graph nodes |
For recurrent and graphnn backends, summary results aggregate repeated node
columns across steps. Control this with site_aggregation:
site_aggregation |
Behavior |
|---|---|
"max_abs" (default) |
Keep the step value with largest absolute magnitude |
"mean_abs" |
Average absolute values across steps |
"last" |
Use the final step only |
Feedforward summary results merge disjoint site columns. site_aggregation is
ignored for feedforward summary output and for level="sites".
Models returned by customize_model() support node interpretation when the
wrapped compiled model remains accessible for interpretation-site lookup.
Adding a custom output head can change output dimensionality and may require
Captum attribute_kwargs such as target for multi-output graphs.
See Scope and limitations for what each backend is designed to cover.