Berlin Tech Meetup: The Future of Relational Foundation Models, Systems, and Real-World Applications

Register now:
PyG/Use Case11 min read

Inventory Optimization: GNN on Supply-Demand Graphs

Excess inventory ties up $1.1 trillion in working capital globally. Out-of-stocks cost $634B in lost sales. Here is how to build a GNN that jointly optimizes stock levels across the supply-demand network.

PyTorch Geometric

TL;DR

  • 1Inventory optimization is a network problem. Stock levels at warehouses, DCs, and stores are interdependent. GNNs jointly optimize across the supply-demand graph, reducing total inventory while improving service levels.
  • 2SAGEConv propagates demand signals upstream and supply constraints downstream through the multi-echelon network for joint optimization.
  • 3On demand forecasting benchmarks, GNNs reduce WMAPE by 15-25% vs independent per-SKU models. Network-level optimization captures cross-location and cross-product substitution effects.
  • 4The GNN predicts demand distributions; safety stock calculations use these predictions instead of simple historical averages.
  • 5KumoRFM predicts demand per SKU-location with one PQL query, feeding directly into safety stock calculations and inventory optimization models.

The business problem

Retailers and manufacturers hold $1.1 trillion in excess inventory globally while simultaneously losing $634 billion to out-of-stocks. This paradox exists because inventory is optimized per SKU-location independently, ignoring the network effects: inventory at a nearby warehouse can cover stockouts at a store, substitute products can absorb demand, and supplier lead time variability affects the entire downstream network.

Why flat ML fails

  • Independent optimization: Each SKU-location gets its own safety stock formula. No cross-location pooling or substitution effects are captured.
  • No network effects: A stockout at Store A drives customers to Store B, increasing B's demand. Flat models cannot anticipate this demand transfer.
  • No substitution modeling: When Product A is out of stock, demand shifts to substitute Product B. Joint inventory planning across substitutes reduces total stock needed.
  • Static lead times: Lead times vary based on supplier load, which depends on all customers' orders. The supply graph captures these dependencies.

The relational schema

schema.txt
Node types:
  Product    (id, category, unit_cost, shelf_life)
  Location   (id, type, capacity, geo_lat, geo_lon)
  Supplier   (id, lead_time_mean, lead_time_var, moq)

Edge types:
  Location  --[supplies]-->      Location (transit_time, cost)
  Product   --[substitute_of]--> Product  (substitution_rate)
  Supplier  --[provides]-->      Product  (lead_time, cost)
  Product   --[stocked_at]-->    Location (on_hand, demand_rate)

Multi-echelon supply chain: suppliers feed warehouses, warehouses feed stores. Substitute products share demand.

PyG architecture: SAGEConv for network optimization

inventory_model.py
import torch
import torch.nn.functional as F
from torch_geometric.nn import SAGEConv, HeteroConv, Linear

class InventoryGNN(torch.nn.Module):
    def __init__(self, hidden_dim=128):
        super().__init__()
        self.product_lin = Linear(-1, hidden_dim)
        self.location_lin = Linear(-1, hidden_dim)
        self.supplier_lin = Linear(-1, hidden_dim)

        self.conv1 = HeteroConv({
            ('location', 'supplies', 'location'): SAGEConv(
                hidden_dim, hidden_dim),
            ('product', 'substitute_of', 'product'): SAGEConv(
                hidden_dim, hidden_dim),
            ('supplier', 'provides', 'product'): SAGEConv(
                hidden_dim, hidden_dim),
            ('product', 'stocked_at', 'location'): SAGEConv(
                hidden_dim, hidden_dim),
        }, aggr='mean')

        self.conv2 = HeteroConv({
            ('location', 'supplies', 'location'): SAGEConv(
                hidden_dim, hidden_dim),
            ('product', 'substitute_of', 'product'): SAGEConv(
                hidden_dim, hidden_dim),
            ('product', 'stocked_at', 'location'): SAGEConv(
                hidden_dim, hidden_dim),
        }, aggr='mean')

        # Predict demand distribution parameters (mean, std)
        self.demand_head = torch.nn.Sequential(
            Linear(hidden_dim, 64),
            torch.nn.ReLU(),
            Linear(64, 2),  # mean and std of demand
        )

    def forward(self, x_dict, edge_index_dict):
        x_dict['product'] = self.product_lin(x_dict['product'])
        x_dict['location'] = self.location_lin(
            x_dict['location'])
        x_dict['supplier'] = self.supplier_lin(
            x_dict['supplier'])

        x_dict = {k: F.relu(v) for k, v in
                  self.conv1(x_dict, edge_index_dict).items()}
        x_dict = self.conv2(x_dict, edge_index_dict)

        # Output demand distribution per product-location
        params = self.demand_head(x_dict['product'])
        mean = F.softplus(params[:, 0])
        std = F.softplus(params[:, 1])
        return mean, std

The GNN predicts demand distribution parameters (mean, std) per product-location, accounting for substitution, network supply, and lead time effects. Feed these into safety stock calculations.

Expected performance

Inventory optimization is measured by demand forecast accuracy (WMAPE) and resulting service level improvements:

  • EOQ formula (heuristic): ~20% WMAPE, baseline service levels
  • LightGBM (independent demand): ~14% WMAPE
  • GNN (network demand): ~10-12% WMAPE
  • KumoRFM (zero-shot): ~10% WMAPE

Or use KumoRFM in one line

KumoRFM PQL
PREDICT weekly_demand FOR product, location
USING product, location, supplier, sales_history

One PQL query. KumoRFM predicts demand per SKU-location, capturing substitution and network effects for inventory planning.

Frequently asked questions

How do GNNs improve inventory optimization over traditional methods?

Traditional methods (EOQ, safety stock formulas) optimize each SKU-location independently. GNNs model the supply-demand network: inventory at one warehouse affects service levels at downstream stores, substitute product availability affects demand, and supplier lead times propagate through the network. Joint optimization across the graph reduces total inventory while improving service levels.

What graph structure represents inventory planning?

Warehouses, stores, products, and suppliers form a network. Edges represent supply routes (warehouse-to-store), product substitutability, and supplier-to-warehouse flows. The graph captures the full supply chain topology, enabling joint optimization of safety stock levels across the network.

Can GNNs handle multi-echelon inventory optimization?

Yes. Multi-echelon optimization (central warehouse plus regional DCs plus stores) is naturally a graph problem. The GNN propagates demand signals upstream and supply constraints downstream through the echelon graph, jointly optimizing stock levels at every node in the network.

How do you combine GNN predictions with safety stock calculations?

The GNN predicts demand and lead time distributions at each node, accounting for network effects. These predictions feed into modified safety stock formulas: safety_stock = z * sigma_demand * sqrt(lead_time), where sigma_demand comes from the GNN rather than historical averages.

Can KumoRFM optimize inventory levels?

KumoRFM predicts demand per SKU-location with one PQL query, capturing cross-product and cross-location effects. These predictions feed directly into inventory optimization models for safety stock and reorder point calculations.

Learn more about graph ML

PyTorch Geometric is the open-source foundation for graph neural networks. Explore more layers, concepts, and production patterns.