### Install All Jraphx Example Dependencies Source: https://github.com/dbraun/jraphx/blob/main/examples/README.md Install all dependencies required for Jraphx examples from the root of the jraphx project. This command ensures all example-specific packages are installed. ```bash pip install -e ".[examples]" ``` -------------------------------- ### Install Development Dependencies Source: https://github.com/dbraun/jraphx/blob/main/CLAUDE.md Install the package with development dependencies and set up pre-commit hooks. ```bash make install-dev ``` -------------------------------- ### Run Basic Examples Source: https://github.com/dbraun/jraphx/blob/main/docs/source/tutorial/examples.md Execute the Python script containing basic JraphX examples. Ensure you have copied the code into a Python file or Jupyter notebook first. ```bash python basic_examples.py ``` -------------------------------- ### Install in Development Mode Source: https://github.com/dbraun/jraphx/blob/main/CLAUDE.md Install the package in editable mode with development dependencies. ```bash pip install -e ".[dev]" ``` -------------------------------- ### Development Installation from Source Source: https://github.com/dbraun/jraphx/blob/main/docs/source/install/installation.md Clone the JraphX repository and install it in development mode for the latest features or contributions. ```bash git clone https://github.com/DBraun/jraphx.git cd jraphx pip install -e . ``` -------------------------------- ### Install Sphinx Read the Docs Theme Source: https://github.com/dbraun/jraphx/blob/main/docs/CLAUDE.md Install the Sphinx Read the Docs theme using pip. This theme is commonly used for project documentation. ```bash pip install sphinx-rtd-theme ``` -------------------------------- ### Generate HTML Documentation Source: https://github.com/dbraun/jraphx/blob/main/docs/CLAUDE.md Navigate to the docs directory and run the make html command to generate the documentation. This requires Sphinx to be installed. ```bash cd docs make html ``` -------------------------------- ### Install JAX AI Stack and JraphX Source: https://github.com/dbraun/jraphx/blob/main/docs/source/install/installation.md Install the JAX AI Stack first, then add JraphX. This method ensures compatibility with other JAX ecosystem libraries. ```bash pip install jax-ai-stack pip install jraphx ``` -------------------------------- ### Development Installation with JAX AI Stack Source: https://github.com/dbraun/jraphx/blob/main/docs/source/install/installation.md Combine the JAX AI Stack installation with a development installation of JraphX from its source repository. ```bash pip install jax-ai-stack git clone https://github.com/DBraun/jraphx.git cd jraphx pip install -e . ``` -------------------------------- ### MLP Instantiation with Configuration Source: https://github.com/dbraun/jraphx/blob/main/docs/source/modules/models.md Example of creating an MLP instance with specified channel list, normalization, bias, dropout rate, and activation function. ```python from jraphx.nn.models import MLP import flax.nnx as nnx # Using channel list mlp = MLP( channel_list=[16, 64, 64, 32, 10], norm="layer_norm", bias=True, dropout_rate=0.5, act="relu", rngs=nnx.Rngs(0) ) ``` -------------------------------- ### Node Classification Example with Synthetic Data Source: https://github.com/dbraun/jraphx/blob/main/docs/source/tutorial/examples.md Provides a complete example for node classification, including synthetic data generation, model initialization, training, and evaluation using JraphX and Optax. This is a good starting point for graph-based classification problems. ```python import jax import optax from jraphx.data import Data # Create synthetic data def create_synthetic_data(num_nodes=100, num_features=16, num_classes=4): # Use modern Flax NNX Rngs shorthand methods rngs = nnx.Rngs(42) # Random features x = rngs.normal((num_nodes, num_features)) # Random edges (Erdős-Rényi graph) prob = 0.1 adj = rngs.bernoulli(prob, (num_nodes, num_nodes)) edge_index = jnp.array(jnp.where(adj)).astype(jnp.int32) # Random labels y = rngs.randint((num_nodes,), 0, num_classes) # Train/val/test splits using indices (JIT-friendly) indices = rngs.permutation(jnp.arange(num_nodes)) train_size = int(0.6 * num_nodes) val_size = int(0.8 * num_nodes) train_indices = indices[:train_size] val_indices = indices[train_size:val_size] test_indices = indices[val_size:] # Create basic data object data = Data(x=x, edge_index=edge_index, y=y) return data, train_indices, val_indices, test_indices # Create data data, train_indices, val_indices, test_indices = create_synthetic_data() # Initialize model and optimizer model = BasicGCN(16, 32, 4, rngs=nnx.Rngs(0)) optimizer = nnx.Optimizer(model, optax.adam(0.01), wrt=nnx.Param) # Training function @nnx.jit def train_step(model, optimizer, data, train_indices): # Ensure model is in training mode model.train() def loss_fn(model): logits = model(data.x, data.edge_index) loss = optax.softmax_cross_entropy_with_integer_labels( logits[train_indices], data.y[train_indices] ).mean() return loss loss, grads = nnx.value_and_grad(loss_fn)(model) optimizer.update(model, grads) return loss # Evaluation function @nnx.jit def evaluate(model, data, indices): # Create evaluation model that shares weights eval_model = nnx.merge(*nnx.split(model)) eval_model.eval() logits = eval_model(data.x, data.edge_index) preds = jnp.argmax(logits, axis=-1) accuracy = (preds[indices] == data.y[indices]).mean() return accuracy ``` -------------------------------- ### Install PyTorch Geometric Dependencies Source: https://github.com/dbraun/jraphx/blob/main/examples/README.md Install PyTorch Geometric for use with Jraphx examples. This command installs the necessary packages for PyTorch Geometric integration. ```bash pip install torch-geometric ``` -------------------------------- ### Multi-Graph Training Example Source: https://github.com/dbraun/jraphx/blob/main/docs/source/tutorial/gnn_design.md Demonstrates efficient multi-graph training using Jraphx, Flax NNX, and JAX primitives. Includes graph creation, batching, and a training loop. ```python import jax import jax.numpy as jnp from flax import nnx from jraphx.data import Data, Batch from jraphx.nn.pool import global_mean_pool import optax # Dummy GCN model for demonstration class GCN(nnx.Module): def __init__(self, in_features, hidden_features, out_features, *, rngs: nnx.Rngs): self.layer1 = nnx.Linear(in_features, hidden_features, rngs=rngs) self.layer2 = nnx.Linear(hidden_features, out_features, rngs=rngs) def __call__(self, x): x = self.layer1(x) x = jax.nn.relu(x) x = self.layer2(x) return x # Create multiple training graphs using new Rngs shorthand methods rngs = nnx.Rngs(0, params=1) # Separate keys for different purposes train_graphs = [] num_nodes = 0 for i in range(100): # Use Rngs shorthand methods (Flax NNX feature) n_nodes = rngs.randint((), 10, 50) # Much cleaner than random.randint! x = rngs.params.normal((n_nodes, 16)) # Use params key for features # Create random edges (simplified) n_edges = n_nodes - 1 edge_index = jnp.stack([ jnp.arange(n_edges), jnp.roll(jnp.arange(n_edges), 1) ]) train_graphs.append(Data(x=x, edge_index=edge_index)) # Batch training function @nnx.jit def train_on_batch(model, optimizer, graphs, targets): batch = Batch.from_data_list(graphs) def loss_fn(model): predictions = model(batch.x) # Assuming model takes node features directly # Global pooling to get graph-level predictions graph_preds = global_mean_pool(predictions, batch.batch) return jnp.mean((graph_preds - targets) ** 2) loss, grads = nnx.value_and_grad(loss_fn)(model) optimizer.update(model, grads) return loss # Training loop model_rngs = nnx.Rngs(42) # For model initialization model = GCN(16, 32, 7, rngs=model_rngs) optimizer = nnx.Optimizer(model, optax.adam(0.01), wrt=nnx.Param) target_rngs = nnx.Rngs(100) # Separate Rngs for targets for epoch in range(50): # Sample batch of graphs batch_graphs = train_graphs[:32] # Batch size 32 batch_targets = target_rngs.normal((32, 7)) # Shorthand method! loss = train_on_batch(model, optimizer, batch_graphs, batch_targets) if epoch % 10 == 0: print(f'Epoch {epoch}, Loss: {loss:.4f}') ``` -------------------------------- ### Setup Optimizer and Training Loop Source: https://github.com/dbraun/jraphx/blob/main/docs/source/get_started/introduction.md Initializes an Adam optimizer for the GCN model and defines a JIT-compiled training step function. The loop trains the model for a specified number of epochs. ```python from flax import nnx import optax # Setup optimizer optimizer = nnx.Optimizer(model, optax.adam(0.01), wrt=nnx.Param) # Training loop train_mask = jnp.array([True, True, False, False]) # First 2 nodes for training test_mask = jnp.array([False, False, True, True]) # Last 2 nodes for testing @nnx.jit def train_step(model, optimizer, data, train_mask): def loss_fn_inner(model): return loss_fn(model, data, train_mask) loss, grads = nnx.value_and_grad(loss_fn_inner)(model) optimizer.update(model, grads) return loss # Train for a few epochs model.train() for epoch in range(200): loss = train_step(model, optimizer, data, train_mask) if epoch % 50 == 0: print(f'Epoch {epoch}, Loss: {loss:.4f}') ``` -------------------------------- ### Understanding JAX Batch Output Source: https://github.com/dbraun/jraphx/blob/main/docs/source/get_started/introduction.md This example shows the typical output structure when printing a JAX batch object, indicating the sizes of different graph components. ```python print(batch) >>> Batch(batch=[1082], edge_index=[2, 4066], x=[1082, 21], y=[32]) ``` -------------------------------- ### Create Example Graph Data Source: https://github.com/dbraun/jraphx/blob/main/docs/source/get_started/introduction.md Initializes a graph with node features, edge indices, and node labels for use in GNN models. Ensure `jraphx.data.Data` and `jax.numpy` are imported. ```python import jax.numpy as jnp from jraphx.data import Data # Create a simple graph with 4 nodes, 3 features per node, 3 classes x = jnp.array([[1.0, 0.5, 0.2], [0.8, 1.0, 0.1], [0.3, 0.7, 1.0], [0.9, 0.2, 0.8]], dtype=jnp.float32) edge_index = jnp.array([[0, 1, 1, 2, 2, 3], [1, 0, 2, 1, 3, 2]], dtype=jnp.int32) # Undirected edges y = jnp.array([0, 0, 1, 1], dtype=jnp.int32) # Node labels data = Data(x=x, edge_index=edge_index, y=y) print(f"Graph: {data.num_nodes} nodes, {data.num_edges} edges") ``` -------------------------------- ### Install JraphX from PyPI Source: https://github.com/dbraun/jraphx/blob/main/docs/source/install/installation.md Use this command to install the latest stable version of JraphX and its dependencies from the Python Package Index. ```bash pip install jraphx ``` -------------------------------- ### Generate PyTorch Geometric Installation Command Source: https://github.com/dbraun/jraphx/blob/main/docs/source/install/quick-start.html This snippet dynamically generates the installation command for PyTorch Geometric based on selected options for PyTorch version, OS, package manager, and CUDA version. It also displays compatibility warnings for unsupported combinations. ```javascript var torchList = [ ['torch-2.8.0', 'PyTorch 2.8.*'], ['torch-2.7.0', 'PyTorch 2.7.*'], ['torch-2.6.0', 'PyTorch 2.6.*'], ]; var osList = [ ['linux', 'Linux'], ['mac', 'Mac'], ['windows', 'Windows'], ]; var packageList = [ ['pip', 'Pip'], ['conda', 'Conda'], ]; var cudaList = [ ['cu118', '11.8'], ['cu121', '12.1'], ['cu124', '12.4'], ['cu126', '12.6'], ['cu128', '12.8'], ['cu129', '12.9'], ['cpu', 'CPU'], ]; torchList.forEach(x => $("#torch").append(\`
${x[1]}
\`)); osList.forEach(x => $("#os").append(\`
${x[1]}
\`)); packageList.forEach(x => $("#package").append(\`
${x[1]}
\`)); cudaList.forEach(x => $("#cuda").append(\`
${x[1]}
\`)); function updateCommand() { var torch = $("#command").attr("torch"); var os = $("#command").attr("os"); var package = $("#command").attr("package"); var cuda = $("#command").attr("cuda"); if (os == "mac" && cuda != "cpu") { $("#command pre").text('# macOS binaries do not support CUDA'); } else if (torch == "torch-2.6.0" && cuda == "cu121") { $("#command pre").text('# PyTorch version does not support CUDA 12.1'); } else if (torch == "torch-2.6.0" && cuda == "cu128") { $("#command pre").text('# PyTorch version does not support CUDA 12.8'); } else if (torch == "torch-2.7.0" && cuda == "cu121") { $("#command pre").text('# PyTorch version does not support CUDA 12.1'); } else if (torch == "torch-2.7.0" && cuda == "cu124") { $("#command pre").text('# PyTorch version does not support CUDA 12.4'); } else if (torch == "torch-2.8.0" && cuda == "cu118") { $("#command pre").text('# PyTorch version does not support CUDA 11.8'); } else if (torch == "torch-2.8.0" && cuda == "cu121") { $("#command pre").text('# PyTorch version does not support CUDA 11.8'); } else if (torch == "torch-2.8.0" && cuda == "cu124") { $("#command pre").text('# PyTorch version does not support CUDA 12.4'); } else if (package == "conda") { $("#command pre").text('# Conda packages are no longer available since PyTorch >2.5.0. Please use pip instead.'); } else { $("#command pre").text(\`pip install torch_geometric\n\n# Optional dependencies:\npip install pyg_lib torch_scatter torch_sparse torch_cluster torch_spline_conv -f https://data.pyg.org/whl/${$("#command").attr("torch")}+${$("#command").attr("cuda")}.html ylic`); } } $(".quick-start .content-column .row div").click(function() { $(this).parent().children().removeClass("selected"); $(this).addClass("selected"); $("#command").attr($(this).parent().attr("id"), $(this).attr("id")); updateCommand(); }); $("#torch").children().get(0).click(); $("#linux").click(); $("#pip").click(); $("#cpu").click(); ``` -------------------------------- ### Node Degree Calculation Example Source: https://github.com/dbraun/jraphx/blob/main/docs/source/modules/utils.md Demonstrates calculating node degrees using a simple index array. The `num_nodes` parameter can be used to specify the total number of nodes. ```python import jax.numpy as jnp from jraphx.utils import degree row = jnp.array([0, 1, 0, 2, 0]) print(degree(row, dtype=jnp.int32)) ``` -------------------------------- ### Serve Documentation Locally Source: https://github.com/dbraun/jraphx/blob/main/docs/CLAUDE.md Launch a local HTTP server to view the generated documentation. The -d build/html flag specifies the directory containing the HTML files. ```bash python -m http.server -d build/html ``` -------------------------------- ### JIT-Compile a Training Step Source: https://github.com/dbraun/jraphx/blob/main/docs/source/advanced/jit.md Shows how to JIT compile an entire training step for optimal performance. It includes loss calculation, gradient computation, and optimizer updates. Uses concrete indices for JIT compatibility. ```python import optax # Setup optimizer optimizer = nnx.Optimizer(model, optax.adam(0.01), wrt=nnx.Param) @jax.jit def train_step(model, optimizer, x, edge_index, targets, train_indices): """JIT-compiled training step.""" def loss_fn(model): predictions = model(x, edge_index) # Use concrete indices instead of boolean mask for JIT compatibility train_predictions = predictions[train_indices] train_targets = targets[train_indices] return jnp.mean(optax.softmax_cross_entropy_with_integer_labels( train_predictions, train_targets )) loss, grads = nnx.value_and_grad(loss_fn)(model) optimizer.update(model, grads) return loss # Training loop with JIT compilation targets = jnp.array([0, 1, 2, 0, 1, 2, 0] * 14 + [0, 1, 2]) # 100 targets train_indices = jnp.arange(80) # First 80 nodes for training (concrete indices) for epoch in range(100): loss = train_step(model, optimizer, data.x, data.edge_index, targets, train_indices) if epoch % 20 == 0: print(f'Epoch {epoch}, Loss: {loss:.4f}') ``` -------------------------------- ### Verify JraphX Installation Source: https://github.com/dbraun/jraphx/blob/main/docs/source/install/installation.md Run this Python script to check installed versions of JAX, JraphX, and test basic graph processing functionality. ```python import jax import jax.numpy as jnp from flax import nnx import jraphx print(f"JAX version: {jax.__version__}") print(f"JAX backend: {jax.default_backend()}") print(f"JraphX version: {jraphx.__version__}") # Test basic functionality from jraphx.data import Data from jraphx.nn.conv import GCNConv # Create a simple graph data = Data( x=jnp.ones((3, 4)), edge_index=jnp.array([[0, 1, 2], [1, 2, 0]]) ) # Create and use a GNN layer layer = GCNConv(4, 8, rngs=nnx.Rngs(42)) output = layer(data.x, data.edge_index) print(f"Successfully processed graph: {output.shape}") ``` -------------------------------- ### Initializing and Calling GCNConv Layer Source: https://github.com/dbraun/jraphx/blob/main/docs/source/notes/create_gnn.md Demonstrates how to instantiate and use the GCNConv layer with sample node features and edge indices. ```python conv = GCNConv(16, 32, rngs=nnx.Rngs(42)) output = conv(x, edge_index) ``` -------------------------------- ### Basic Graph Batching with JraphX Source: https://github.com/dbraun/jraphx/blob/main/docs/source/advanced/batching.md Demonstrates how to create individual graphs and then batch them together using the `jraphx.data.Batch` class. Shows how to inspect the properties of the created batch. ```python import jax.numpy as jnp from jraphx.data import Data, Batch # Create individual graphs graph1 = Data( x=jnp.array([[1.0, 2.0], [3.0, 4.0]]), # 2 nodes edge_index=jnp.array([[0, 1], [1, 0]]) # 2 edges ) graph2 = Data( x=jnp.array([[5.0, 6.0], [7.0, 8.0], [9.0, 10.0]]), # 3 nodes edge_index=jnp.array([[0, 1, 2], [1, 2, 0]]) # 3 edges ) # Create batch batch = Batch.from_data_list([graph1, graph2]) print(f"Batch info:") print(f" Total nodes: {batch.num_nodes}") # 5 nodes total print(f" Total edges: {batch.num_edges}") # 5 edges total print(f" Num graphs: {batch.num_graphs}") # 2 graphs print(f" Node features: {batch.x.shape}") # [5, 2] print(f" Edge indices: {batch.edge_index.shape}") # [2, 5] print(f" Batch vector: {batch.batch}") # [0, 0, 1, 1, 1] ``` -------------------------------- ### Basic Graph Preprocessing Source: https://github.com/dbraun/jraphx/blob/main/docs/source/notes/introduction.md Applies a default preprocessing function to graph data. This is a starting point for data preparation. ```python import jraphx as jraphx from jraphx.data import Data original_data = Data(x=jnp.ones((3, 2)), edge_index=jnp.array([[0, 1], [1, 2]])) processed_data = preprocess_graph(original_data) ``` -------------------------------- ### JAX JIT Compilation and Vectorization for GNNs Source: https://github.com/dbraun/jraphx/blob/main/docs/source/cheatsheet/gnn_cheatsheet.md Illustrates how to leverage JAX's `@jax.jit` for JIT compilation and `nnx.vmap` for vectorizing GNN inference over multiple graphs. Also shows integration with Optax for optimization. ```python import jax # JIT compile for speed @jax.jit def fast_gnn_inference(model, x, edge_index): return model(x, edge_index) # Vectorize over multiple graphs (fixed-size) @nnx.vmap def batch_gnn_inference(x_batch, edge_index_batch): return model(x_batch, edge_index_batch) # Use with optimization libraries import optax optimizer = nnx.Optimizer(model, optax.adam(0.01), wrt=nnx.Param) @jax.jit def train_step(model, optimizer, data, targets): def loss_fn(model): preds = model(data.x, data.edge_index) return jnp.mean((preds - targets) ** 2) loss, grads = nnx.value_and_grad(loss_fn)(model) optimizer.update(model, grads) return loss ``` -------------------------------- ### LayerNorm Source: https://github.com/dbraun/jraphx/blob/main/docs/source/modules/norm.md Applies layer normalization over each individual example in a batch of node features. Supports both node-wise and graph-wise normalization modes. ```APIDOC ## LayerNorm ### Description Applies layer normalization over each individual example in a batch of node features. ### Parameters * **num_features** (*int* *or* *list*) – Size of each input sample, or list of dimensions to normalize. * **eps** (*float* *,* *optional*) – A value added to the denominator for numerical stability. (default: `1e-5`) * **elementwise_affine** (*bool* *,* *optional*) – If set to `True`, this module has learnable affine parameters $\gamma$ and $\beta$. (default: `True`) * **mode** (*str* *,* *optional*) – The normalization mode to use for layer normalization (`"graph"` or `"node"`). (default: `"node"`) * **dtype** – The dtype of the result (default: infer from input and params). * **param_dtype** – The dtype passed to parameter initializers (default: float32). * **use_bias** (*bool* *,* *optional*) – If True, bias (beta) is added. (default: `True`) * **use_scale** (*bool* *,* *optional*) – If True, multiply by scale (gamma). (default: `True`) * **bias_init** – Initializer for bias, by default, zero. * **scale_init** – Initializer for scale, by default, one. * **reduction_axes** – Axes for computing normalization statistics. * **feature_axes** – Feature axes for learned bias and scaling. * **axis_name** – The axis name used to combine batch statistics from multiple devices. * **axis_index_groups** – Groups of axis indices within that named axis. * **use_fast_variance** – If true, use faster, but less numerically stable variance calculation. * **rngs** – Random number generators for initialization. ### Example ```python from jraphx.nn.norm import LayerNorm import flax.nnx as nnx # Node-wise normalization norm = LayerNorm( num_features=64, mode="node", eps=1e-5, elementwise_affine=True, rngs=nnx.Rngs(0) ) x_normalized = norm(x) # Graph-wise normalization (requires batch index) norm_graph = LayerNorm( num_features=64, mode="graph", rngs=nnx.Rngs(0) ) x_normalized = norm_graph(x, batch=batch) ``` ``` -------------------------------- ### Custom GNN Subclassing Source: https://github.com/dbraun/jraphx/blob/main/docs/source/modules/models.md Example of subclassing the abstract BasicGNN class to define a custom GNN model by implementing the init_conv method. ```python from jraphx.nn.models import BasicGNN from jraphx.nn.conv import MessagePassing class MyCustomGNN(BasicGNN): def init_conv(self, in_features, out_features, rngs=None, **kwargs): # Return your custom message passing layer return MyCustomConv(in_features, out_features, rngs=rngs, **kwargs) ``` -------------------------------- ### Simple GNN Training Loop with Optax Optimizer Source: https://github.com/dbraun/jraphx/blob/main/docs/source/get_started/introduction.md Implement a basic training step using `nnx.jit` and `nnx.value_and_grad`. This snippet sets up an Adam optimizer and defines a loss function for training. ```python import optax from jraphx.data import DataLoader # Create optimizer optimizer = nnx.Optimizer(model, optax.adam(learning_rate=0.01), wrt=nnx.Param) @nnx.jit def train_step(model, optimizer, data, labels): # Ensure model is in training mode model.train() def loss_fn(model): logits = model(data.x, data.edge_index) loss = optax.softmax_cross_entropy(logits, labels).mean() return loss loss, grads = nnx.value_and_grad(loss_fn)(model) optimizer.update(model, grads) return loss # Training loop for epoch in range(100): loss = train_step(model, optimizer, data, labels) if epoch % 10 == 0: print(f"Epoch {epoch}, Loss: {loss:.4f}") ``` -------------------------------- ### Custom GNN with GraphNorm Integration Source: https://github.com/dbraun/jraphx/blob/main/docs/source/modules/norm.md Example of integrating GraphNorm within a custom GNN module for node feature transformation and normalization. ```python from jraphx.nn.conv import GCNConv from jraphx.nn.norm import GraphNorm import flax.nnx as nnx class CustomGNN(nnx.Module): def __init__(self, in_features, out_features, rngs): self.conv = GCNConv(in_features, 64, rngs=rngs) self.norm = GraphNorm(64, rngs=rngs) self.linear = nnx.Linear(64, out_features, rngs=rngs) def __call__(self, x, edge_index, batch=None): x = self.conv(x, edge_index) x = self.norm(x, batch) x = nnx.relu(x) return self.linear(x) ``` -------------------------------- ### Initialize and Use GCN, GAT, and SAGE Convolution Layers Source: https://github.com/dbraun/jraphx/blob/main/docs/source/get_started/introduction.md Demonstrates the initialization and basic usage of GCNConv, GATConv, and SAGEConv layers from JraphX for graph convolution operations. ```python import flax.nnx as nnx from jraphx.nn.conv import GCNConv, GATConv, SAGEConv # Initialize random number generator rngs = nnx.Rngs(42) # Graph Convolutional Network (GCN) gcn = GCNConv(in_features=3, out_features=16, rngs=rngs) out = gcn(data.x, data.edge_index) # Graph Attention Network (GAT) gat = GATConv(in_features=3, out_features=16, heads=4, rngs=rngs) out = gat(data.x, data.edge_index) # GraphSAGE sage = SAGEConv(in_features=3, out_features=16, rngs=rngs) out = sage(data.x, data.edge_index) ``` -------------------------------- ### Run Pre-commit Hooks Source: https://github.com/dbraun/jraphx/blob/main/CLAUDE.md Execute all configured pre-commit hooks to verify code quality before committing. ```bash make pre-commit ``` -------------------------------- ### TopKPooling Layer Initialization and Application Source: https://github.com/dbraun/jraphx/blob/main/docs/source/modules/pooling.md Initializes and applies the TopKPooling layer to select the top 50% of nodes based on learnable scores. Ensure 'x', 'edge_index', 'edge_attr', and 'batch' are defined before use. ```python from jraphx.nn.pool import TopKPooling import flax.nnx as nnx # Select top 50% of nodes pool = TopKPooling( in_features=64, ratio=0.5, min_score=None, # Optional minimum score threshold multiplier=1.0, # Score multiplier rngs=nnx.Rngs(0) ) # Apply pooling x_pool, edge_index_pool, edge_attr_pool, batch_pool, perm = pool( x, edge_index, edge_attr=edge_attr, batch=batch ) # perm contains indices of selected nodes ``` -------------------------------- ### Custom Aggregation in MessagePassing Source: https://github.com/dbraun/jraphx/blob/main/docs/source/modules/conv.md Implement custom aggregation logic by subclassing MessagePassing and overriding the aggregate method. This example demonstrates using scatter_mean for custom aggregation. ```python class CustomConv(MessagePassing): def __init__(self, in_features, out_features): # Custom aggregation function super().__init__(aggr='add') def aggregate(self, inputs, index, dim_size=None): # Override for custom aggregation return scatter_mean(inputs, index, dim=0, dim_size=dim_size) ``` -------------------------------- ### JIT Compile Graph Processing Function Source: https://github.com/dbraun/jraphx/blob/main/docs/source/cheatsheet/data_cheatsheet.md Compiles a graph processing function using JAX's JIT for performance. This example adds self-loops to a graph. ```python import jax @jax.jit def process_graph(data): from jraphx.utils import add_self_loops edge_index, _ = add_self_loops(data.edge_index, data.x.shape[0]) return edge_index processed = process_graph(data) ``` -------------------------------- ### JAX JIT Compilation for Batched Graph Processing Source: https://github.com/dbraun/jraphx/blob/main/docs/source/advanced/batching.md Shows how to use JAX's JIT compilation with a GCNConv model to efficiently process batched graph data. Includes a helper function for processing lists of graphs. ```python import jax from jraphx.nn.conv import GCNConv from flax import nnx # Create model model = GCNConv(2, 8, rngs=nnx.Rngs(42)) # Process batch with JIT compilation (extract arrays first) @jax.jit def process_batch(model, x, edge_index): return model(x, edge_index) # Efficient batch processing batch_output = process_batch(model, batch.x, batch.edge_index) print(f"Batch output shape: {batch_output.shape}") # [5, 8] # For multiple batches, process arrays directly def process_graph_list(model, graph_list): """Process a list of graphs efficiently.""" batch = Batch.from_data_list(graph_list) return process_batch(model, batch.x, batch.edge_index) ``` -------------------------------- ### Batch Processing with Graph-Level Targets Source: https://github.com/dbraun/jraphx/blob/main/docs/source/advanced/batching.md Demonstrates how to create graphs with graph-level targets and process them in batches using `global_mean_pool` for aggregation. ```python from jraphx.nn.pool import global_mean_pool # Create graphs with graph-level targets graphs_with_targets = [] rngs = nnx.Rngs(0, targets=1) # Use Flax NNX shorthand for i in range(10): x = rngs.normal((5, 16)) # Node features edge_index = jnp.array([[0, 1, 2], [1, 2, 0]]) # Simple cycle target = rngs.targets.normal((7,)) # Graph-level target graph = Data(x=x, edge_index=edge_index) graphs_with_targets.append((graph, target)) # Batch processing with graph-level targets def process_graph_batch_with_targets(model, graphs_and_targets): graphs, targets = zip(*graphs_and_targets) # Create batch for graphs batch = Batch.from_data_list(graphs) # Process batch node_embeddings = model(batch.x, batch.edge_index) # Pool to graph-level graph_embeddings = global_mean_pool(node_embeddings, batch.batch) # Stack targets to create [num_graphs, target_dim] targets_array = jnp.stack(targets) return graph_embeddings, targets_array ``` -------------------------------- ### JraphX Model Compilation with JIT Source: https://github.com/dbraun/jraphx/blob/main/docs/source/advanced/compile.md Demonstrates how to create a JraphX model and use JAX's JIT (Just-In-Time) compilation to optimize the forward pass for performance. XLA optimization is triggered automatically by JIT. ```python import jax import jax.numpy as jnp from jraphx.nn.models import GCN from flax import nnx # Create model - XLA will optimize this automatically when JIT-compiled model = GCN( in_features=64, hidden_features=128, out_features=32, num_layers=4, rngs=nnx.Rngs(42) ) # JIT compilation triggers XLA optimization @nnx.jit def optimized_forward(model, x, edge_index): return model(x, edge_index) # XLA optimizes the entire computation graph x = jnp.ones((1000, 64)) edge_index = jnp.array([[0, 1, 2], [1, 2, 0]]) output = optimized_forward(model, x, edge_index) ``` -------------------------------- ### Memory-Efficient Deep GNN with nnx.scan Source: https://github.com/dbraun/jraphx/blob/main/docs/source/tutorial/gnn_design.md Implement a deep GNN using nnx.scan for memory-efficient sequential processing of layers. This example defines a DeepGNN module with multiple hidden blocks. ```python from jraphx.nn.conv import GCNConv class HiddenBlock(nnx.Module): """Single hidden layer block for scanning.""" def __init__(self, hidden_features: int, rngs: nnx.Rngs): self.conv = GCNConv(hidden_features, hidden_features, rngs=rngs) def __call__(self, x, edge_index): x = self.conv(x, edge_index) x = nnx.relu(x) return x # Return only x, no second output needed class DeepGNN(nnx.Module): def __init__(self, in_features: int, hidden_features: int, out_features: int, num_layers: int, rngs: nnx.Rngs): # Create input and output layers self.input_layer = GCNConv(in_features, hidden_features, rngs=rngs) self.output_layer = GCNConv(hidden_features, out_features, rngs=rngs) # Create multiple hidden layers using vmap num_hidden = num_layers - 2 self.num_hidden = num_hidden if num_hidden > 0: @nnx.split_rngs(splits=num_hidden) @nnx.vmap(in_axes=(0,), out_axes=0) def create_hidden_block(rngs: nnx.Rngs): return HiddenBlock(hidden_features, rngs=rngs) self.hidden_blocks = create_hidden_block(rngs) else: self.hidden_blocks = None def __call__(self, data): x, edge_index = data.x, data.edge_index # Input layer x = self.input_layer(x, edge_index) x = nnx.relu(x) # Hidden layers with scan (only if we have hidden layers) if self.num_hidden > 0: @nnx.scan(in_axes=(nnx.Carry, 0), out_axes=nnx.Carry) def forward_hidden(x, block): x = block(x, edge_index) return x x = forward_hidden(x, self.hidden_blocks) # Output layer return self.output_layer(x, edge_index) # Create and use deep network deep_model = DeepGNN(16, 64, 7, 10, rngs=nnx.Rngs(42)) deep_predictions = deep_model(data) ``` -------------------------------- ### Model Initialization: PyTorch Geometric vs JraphX Source: https://github.com/dbraun/jraphx/blob/main/docs/source/missing_tests.md Demonstrates the difference in initializing a GCNConv layer between PyTorch Geometric and JraphX, highlighting the need for RNGs in JraphX. ```python # PyTorch Geometric conv = GCNConv(16, 32) # JraphX conv = GCNConv(16, 32, rngs=nnx.Rngs(0)) ``` -------------------------------- ### Edge-Conditioned Convolution Implementation Source: https://github.com/dbraun/jraphx/blob/main/docs/source/advanced/techniques.md Implement custom message passing layers, such as edge-conditioned convolutions, by subclassing MessagePassing. This example defines how messages are constructed using node and edge features. ```python from jraphx.nn.conv import MessagePassing import flax.nnx as nnx class EdgeConditionedConv(MessagePassing): """Message passing with edge features.""" def __init__(self, in_features, out_features, edge_dim, rngs): super().__().__init__(aggr='mean') self.node_mlp = nnx.Sequential( nnx.Linear(in_features * 2 + edge_dim, out_features, rngs=rngs), nnx.relu, nnx.Linear(out_features, out_features, rngs=rngs) ) def message(self, x_i, x_j, edge_attr): # Concatenate source, target, and edge features msg = jnp.concatenate([x_i, x_j, edge_attr], axis=-1) return self.node_mlp(msg) def __call__(self, x, edge_index, edge_attr): return self.propagate( edge_index, x=x, edge_attr=edge_attr ) ``` -------------------------------- ### SAGEConv Initialization and Usage Source: https://github.com/dbraun/jraphx/blob/main/docs/source/modules/conv.md Initializes and uses the SAGEConv layer with mean and LSTM aggregation. Supports normalization and requires RNGs for initialization. ```python from jraphx.nn.conv import SAGEConv import flax.nnx as nnx # Mean aggregation (most common) conv = SAGEConv( in_features=16, out_features=32, aggr='mean', normalize=True, rngs=nnx.Rngs(0) ) # LSTM aggregation conv_lstm = SAGEConv( in_features=16, out_features=32, aggr='lstm', rngs=nnx.Rngs(0) ) out = conv(x, edge_index) ``` -------------------------------- ### Process Multiple Graphs with JAX vmap Source: https://github.com/dbraun/jraphx/blob/main/docs/source/get_started/introduction.md Demonstrates how to prepare for processing multiple graphs in parallel using jax.vmap. This requires proper batching of graph data. ```python # Create multiple graphs graphs = [Data(x=jnp.ones((3, 2)), edge_index=jnp.array([[0, 1], [1, 0]])) for _ in range(5)] # Process multiple graphs in parallel def process_single_graph(data): return jnp.sum(data.x) # vmap over a batch of graphs batched_process = jax.vmap(process_single_graph) # results = batched_process(graph_batch) # Requires proper batching ``` -------------------------------- ### Build a Complete GNN Model with JraphX Layers Source: https://github.com/dbraun/jraphx/blob/main/docs/source/get_started/introduction.md Combine GCNConv and global_mean_pool layers to construct a full GNN model. This example demonstrates a typical architecture for graph-level prediction tasks. ```python import jax import flax.nnx as nnx from jraphx.nn.conv import GCNConv from jraphx.nn.pool import global_mean_pool class GNN(nnx.Module): def __init__(self, in_features, hidden_features, out_features, rngs): self.conv1 = GCNConv(in_features, hidden_features, rngs=rngs) self.conv2 = GCNConv(hidden_features, hidden_features, rngs=rngs) self.conv3 = GCNConv(hidden_features, out_features, rngs=rngs) self.dropout = nnx.Dropout(rate=0.5, rngs=rngs) def __call__(self, x, edge_index, batch=None): # First GCN layer x = self.conv1(x, edge_index) x = nnx.relu(x) x = self.dropout(x) # Second GCN layer x = self.conv2(x, edge_index) x = nnx.relu(x) x = self.dropout(x) # Third GCN layer x = self.conv3(x, edge_index) # Global pooling (for graph-level prediction) if batch is not None: x = global_mean_pool(x, batch) return x # Create model model = GNN(in_features=3, hidden_features=64, out_features=10, rngs=nnx.Rngs(42)) # Forward pass output = model(data.x, data.edge_index) ``` -------------------------------- ### Gradual Pooling for Better Gradients Source: https://github.com/dbraun/jraphx/blob/main/docs/source/modules/pooling.md Illustrates a strategy of applying pooling gradually over multiple layers, starting with a higher ratio and decreasing it in subsequent layers. This approach can help maintain gradient flow. ```python # Gradual pooling for better gradients pool1 = TopKPooling(64, ratio=0.8, rngs=nnx.Rngs(42)) # First layer: 80% pool2 = TopKPooling(64, ratio=0.6, rngs=nnx.Rngs(42)) # Second layer: 60% ``` -------------------------------- ### Tensor Operations: PyTorch vs JAX Source: https://github.com/dbraun/jraphx/blob/main/docs/source/missing_tests.md Illustrates the conversion of tensor creation and comparison operations between PyTorch and JAX. ```python # PyTorch x = torch.randn(4, 16) assert torch.allclose(x, x) # JAX x = random.normal(random.key(0), (4, 16)) assert jnp.allclose(x, x) ``` -------------------------------- ### Manage Train/Eval Modes for GNNs with Dropout Source: https://github.com/dbraun/jraphx/blob/main/docs/source/get_started/introduction.md Set models to training or evaluation mode using `model.train()` and `model.eval()`. This example shows how dropout is enabled during training and disabled during evaluation, while weights remain synchronized. ```python from jraphx.nn.models import GraphSAGE # Create model with dropout model = GraphSAGE(in_features=16, hidden_features=32, out_features=8, num_layers=2, dropout_rate=0.5, rngs=nnx.Rngs(42)) model.train() # Set to training mode # Create evaluation model that shares weights eval_model = nnx.merge(*nnx.split(model)) # Same weights, different behavior eval_model.eval() # Set to evaluation mode # Both models share weights but behave differently train_out = model(x, edge_index) # Uses dropout eval_out = eval_model(x, edge_index) # No dropout # Weights stay synchronized automatically - no copying needed! print("Weights shared:", jnp.allclose( model.convs[0].linear.kernel.value, eval_model.convs[0].linear.kernel.value )) >>> Weights shared: True ``` -------------------------------- ### Custom vmap Pattern for Graph Aggregation Source: https://github.com/dbraun/jraphx/blob/main/docs/source/advanced/techniques.md Implement custom vmap patterns for specific graph processing tasks, such as aggregating features across a batch of graphs. This example defines a per-graph operation and then vectorizes it. ```python def custom_vmap_aggregation(graphs, model): """Custom vmap pattern for graph aggregation.""" # Define per-graph operation def per_graph_op(graph): node_features = model(graph.x, graph.edge_index) # Custom aggregation graph_feature = node_features.mean(axis=0) return graph_feature # Vectorize and apply vmapped_op = nnx.vmap(per_graph_op) graph_features = vmapped_op(graphs) # Further processing on all graphs return graph_features.mean(axis=0) ``` -------------------------------- ### Debugging Compiled Code with Intermediate Prints Source: https://github.com/dbraun/jraphx/blob/main/docs/source/advanced/compile.md Provides an example of using `jax.debug.print` within a function to inspect intermediate values and shapes during execution. Note that this specific method for printing only works in eager mode. ```python # Print intermediate values (only works in eager mode) def debug_forward(model, x, edge_index): x = model.layers[0](x, edge_index) jax.debug.print("After layer 0: {}", x.shape) x = model.layers[1](x, edge_index) jax.debug.print("After layer 1: {}", x.shape) return x ``` -------------------------------- ### Create a JIT-Compatible Custom GNN Layer Source: https://github.com/dbraun/jraphx/blob/main/docs/source/advanced/jit.md Illustrates how to create a custom JraphX layer that is JIT-compatible. Emphasizes using only JAX operations, static shapes, and pure functions. ```python from jraphx.nn.conv import MessagePassing class CustomGNNLayer(MessagePassing): def __init__(self, in_features, out_features, *, rngs: nnx.Rngs): super().__init__(aggr='mean') self.linear = nnx.Linear(in_features, out_features, rngs=rngs) def __call__(self, x, edge_index): # All operations here must be JAX-compatible x = self.linear(x) # Use JAX operations for conditionals x = jnp.where(x > 0, x, 0.0) # ReLU activation # Standard message passing return self.propagate(edge_index, x) # This layer is automatically JIT-compatible @jax.jit def forward_with_custom_layer(x, edge_index): layer = CustomGNNLayer(16, 32, rngs=nnx.Rngs(42)) return layer(x, edge_index) ``` -------------------------------- ### GINConv Initialization with MLP Source: https://github.com/dbraun/jraphx/blob/main/docs/source/modules/conv.md Initializes the GINConv layer, which uses a provided MLP for transformation. The epsilon parameter can be fixed or trainable. ```python from jraphx.nn.conv import GINConv from jraphx.nn.models import MLP import flax.nnx as nnx ```