Prescriptive Reasoning for Supply Chain Resilience with RelationalAI
Every disruption in a supply chain is a data problem waiting to be solved. Whether it’s a port delay, raw material shortage, or unexpected demand spike, each event triggers a cascade of decisions—what to reroute, where to produce, how much to ship. Traditional systems struggle with this complexity. That’s where RelationalAI’s Prescriptive Reasoning comes in: a way to model, reason, and optimize supply chain decisions at scale—right inside Snowflake.
RelationalAI provides the only relational knowledge graph fully integrated and native to the Snowflake AI Data Cloud. This architecture enables organizations to apply advanced reasoning techniques to infer, discover, predict, and optimize all aspects of their business–unlocking a new class of intelligent applications and deeply aligned GenAI workflows. At the Snowflake Summit, we announced several major product enhancements designed to help developers and enterprises build intelligent applications. One of the most exciting announcements was the preview of Prescriptive Reasoning.
One key problem for organizations is the ability to solve complex intelligent problems around business constraints. For any system that needs to solve these complex, domain-specific problems, one of the most effective approaches is to model the business as a relational knowledge graph–defining key concepts, relationships, constraints, and rules. Once the domain is modeled it can be used as inputs to mathematical optimization techniques, offering context and deeper insights that can significantly improve the performance and decision-making capabilities that reflect the real-world trade-offs and goals of the business. RelationalAI’s platform is purpose-built to support this end-to-end flow—from modeling to reasoning to decision-making–natively within the Snowflake ecosystem.
With this new capability, developers can now embed mathematical optimization into their applications–defining constraints, specifying objectives, and computing optimal decisions directly from within the knowledge graph. This bridges the gap between predictive insight and operational action.
Let us look at a real world example of prescriptive reasoning used in supply chain planning.
Modern supply chains are massive and interconnected. A typical enterprise may rely on thousands of suppliers, operate dozens of manufacturing plants, and manage a global logistics network. Each product has its own sourcing, production, and delivery path–and when one link in the chain breaks, the effects cascade across the system.
Even under normal conditions, managing this complexity is a challenge. But supply chains don’t operate in a vacuum. Disruptions such as bad weather, a factory delay, port congestion, a strike, or even a sudden spike in demand or tariffs–can throw operations off course quickly. Many companies run lean to save costs, leaving little margin for error which results in small problems leading to big delays very quickly.
This is where enterprises leverage mathematical optimizations as a tool to address these disruptions by helping organizations take the right decisions from countless options and trade-offs. In supply chain contexts, optimization is used to compute the best possible decision across a range of trade-offs
How much to produce and where? Where to place inventory to balance cost and service levels? Which suppliers to prioritize when capacity is constrained? How to route shipments or schedule shifts to meet fluctuating demand?
RelationalAI’s approach
RelationalAI makes it natural to model a supply chain as a knowledge graph, using structure and logic defined in Python through RAI rules. This knowledge graph is constructed directly over base tables residing in Snowflake, allowing users to build solutions that are fully integrated with their enterprise data. The result is a Snowflake-native, knowledge graph that connects business logic with operational data. Below is an example that illustrates how to define core supply chain concepts and their relationships using this approach.
# Sets up a supply chain model
model = Model("SupplyChainNetwork")
# Defines basic items: products, places, and connections
Product = Concept("Product") # Things like Widgets
Facility = Concept("Facility") # Places like suppliers or stores
Connection = Concept("Connection") # Ways to move goods like trains
# Describes how items and places are linked
Facility.distributes = Relationship("{Facility} distributes {Product} to {Facility} in quantity {qty:Int}")
Connection.between = Relationship("{Facility} is connected to {Facility}")
# Adds specific items and links for our supply chain
define(
sp := Facility.new(name="SupplierA"),
wh := Facility.new(name="MainWarehouse"),
st := Facility.new(name="StoreX"),
p := Product.new(name="Widget"), # Creates a new product called "Widgets"
c1 := Connection.new(name="Train"),
c2 := Connection.new(name="Truck"),
sp.distributes(p, wh, 100), # SupplierA sends 100 Widgets to MainWarehouse
wh.distributes(p, st, 50), # MainWarehouse sends 50 Widgets to StoreX
c1.between(sp, wh), # Train links SupplierA to MainWarehouse
c2.between(wh, st) # Truck links MainWarehouse to StoreX
)
Once the model for the supply chain is ready, we can use one of the supported Reasoners–Graph, Rules, Predictive and Prescriptive Reasoners–to solve the business problem. The Prescriptive Reasoner is particularly well-aligned with the RelationalAI platform due to its declarative nature—users define the constraints and specify what makes one solution preferable over others, without needing to outline how the solution should be computed. RAI Prescriptive Reasoner features a solver-agnostic design; with just a single line change, users can switch between solvers best suited for the task—ranging from free solvers like HiGHS for relatively simple problems to commercial mixed integer programming (MIP) solvers such as Gurobi, IBM CPLEX, or FICO Xpress. The platform supports a broad spectrum of solver types, including mathematical optimization, constraint satisfaction, and SAT solvers.
Supply Chain Network Representation
The supply chain network can be modeled as a Directed Acyclic Graph (DAG)[^1].
Nodes with only outgoing edges represent suppliers or plants—these are referred to as source nodes. Nodes with only incoming edges represent demand points or stores or customers—referred to as sink nodes. All other nodes, which typically represent warehouses, hubs, or distribution centers, are classified as intermediate nodes. Each edge in the graph typically captures flow of goods or materials between two nodes.
This graph-based representation provides a clear and structured way to analyze and optimize the flow of goods, identify bottlenecks, and evaluate the impact of decisions across the entire supply chain network.
The example below illustrates such a network, highlighting a route from source node S3 to sink node D5 via intermediate node WH5.

Disruptions in a supply chain network can occur at one or more nodes, edges, or a combination of both. In the example shown below, a disruption has occurred at node WH5, rendering the route S3 → WH5 → D5 unavailable. The network provides four viable alternative paths to reach the sink node D5, originating from two different source nodes:
S3 → WH6 → D5 S3 → WH8 → D5 S4 → WH6 → D5 S4 → WH8 → D5

To efficiently manage such disruptions at scale—where many suppliers, warehouses, demand points and products may be involved—it is essential to formulate this problem as an optimization model.
DAG Path Optimization Formulation
Sets and Parameters:

Decision Variables:

Constraints:

The objective function will be specified later, adapted to the requirements of different use cases.
Strategies for Planning Alternative Routes
We will now outline several approaches that planners can use with the Prescriptive reasoning capability to evaluate and identify alternative routes.
Scenario 1–Determining the optimal route
This method employs mathematical optimization to identify the best route from all feasible alternatives by evaluating against defined business objectives. Key optimization criteria include:
Cost Efficiency: Minimize total transportation cost Service Quality: Maximize on-time delivery probability Speed Optimization: Minimize total transit time Sustainability: Minimize carbon emissions or environmental impact
The RelationalAI Prescriptive Reasoner identifies the optimal path based on defined planning priorities, guided by objectives such as minimizing distance. For instance, the RelationalAI Reasoner can set an objective function to minimize distance and then invoke a solver like HiGHS (or another commercial solver if needed), typically specifying a time limit (e.g., 100 seconds).[^2] Once the optimal route is defined and computed, this can become just another relationship in the knowledge graph derived through mathematical optimization.
# an example of an objective function: minimize distance
total_dist = sum(Connection.x_edge * Connection.distance)
# Solve model
s.minimize(total_dist)
s.solve(Solver("highs"), time_limit_sec=100)
Scenario 2–Enumerating all routes
Generating a comprehensive list of all alternative routes, not just the optimal one, offers planners greater flexibility. This approach, similar to how Google Maps provides various route options, allows for decisions based on practical considerations that a model's constraints may not fully capture. It empowers planners to factor in elements like risk, preferences, or real-world limitations that a solver might not directly incorporate.
To achieve this, we omit the objective function and utilize a solver like MiniZinc, designed for constraint satisfaction and combinatorial optimization problems, instead of a MIP solver.
# Objective function is not specified
# Solve model, retrieve all solutions
s.solve(Solver("minizinc"), time_limit_sec=100)
Scenario 3–Generating High Quality Routes
In large-scale supply chains, where thousands or even tens of thousands of potential routes exist, particularly with multiple sink nodes, an exhaustive listing of all routes is often impractical. Nevertheless, planners frequently seek a diverse set of viable options rather than a singular solution optimized for a specific objective.
To address this, the RelationalAI Prescriptive Reasoner utilizes an advanced feature called the Solution Pool. This solver feature, supported by commercial solvers such as Gurobi, CPLEX, or Xpress, provides multiple optimal or near-optimal solutions. The Solution Pool requires an objective function and delivers a predefined number of top-scoring solutions. This method effectively merges the advantages of both the single-optimum strategy and the exhaustive enumeration approach.
# an example of an objective function: minimize distance
total_dist = sum(Connection.x_edge * Connection.distance)
# Solve model, retrieve the top 100 shortest routes
s.minimize(total_dist)
s.solve(Solver("gurobi"), time_limit_sec=100, solution_pool = TRUE, solution_pool_size = 100)
Conclusion
For people who are familiar with other mathematical optimization frameworks, RelationalAI Knowledge Graph and Prescriptive Reasoner offer a distinct approach. It adopts a declarative, relational paradigm with a rule-based modeling style, in contrast to the procedural methods used in modeling platforms like Pyomo and PuLP. Prescriptive Reasoner access to solvers is abstracted, allowing use of a variety of commercial and open-source solvers without changing the model structure. While some modeling environments like GurobiPy offer more concise formulations, RelationalAI Reasoner syntax provides richer context, enhancing model clarity and interpretability. We’ll delve deeper into these differences in an upcoming blog post.
Appendix : DAG Path Optimization in RelationalAI Syntax
For the graph below, which includes distances between Facilities, the code determines the shortest route from either source (S3 or S4) to the sink (D5). We use the concept Facility as a general term used to describe any physical location or entity involved in the flow of goods, materials, or information such as Suppliers, Warehouses, Manufacturing Pants or Distribution Centers.

# model and concepts
model = Model("SupplyChainNetwork")
Concept, Relationship = model.Concept, model.Relationship
Facility = Concept("Facility")
Connection = Concept("Connection")
# Define the combined relationship capturing connections between facilities and their distances
Connection.from_to = Relationship("{Connection} connects {Facility} to {Facility} dist {d:float}")
# Define the network with 6 nodes and 9 connections
define(
# Sources (origin points)
s3 := Facility.new(name="S3"),
s4 := Facility.new(name="S4"),
# Intermediate Warehouses
wh6 := Facility.new(name="WH6"),
wh8 := Facility.new(name="WH8"),
# Final destination (sink)
d5 := Facility.new(name="D5"),
# Define each connection (edge) by name
c2 := Connection.new(name="S3_WH6"),
c3 := Connection.new(name="S3_WH8"),
c4 := Connection.new(name="S4_WH6"),
c5 := Connection.new(name="S4_WH8"),
c8 := Connection.new(name="WH6_D5"),
c9 := Connection.new(name="WH8_D5"),
# Define all connections using from_to with source, destination, and distance
Connection.from_to(c2, s3, wh6, 20.0),
Connection.from_to(c3, s3, wh8, 25.0),
Connection.from_to(c4, s4, wh6, 18.0),
Connection.from_to(c5, s4, wh8, 22.0),
Connection.from_to(c8, wh6, d5, 10.0),
Connection.from_to(c9, wh8, d5, 14.0),
)
# Identify sources and sink for routing
Sources = [s3, s4]
Sink = d5
intermediate_nodes = [wh6, wh8]
# Initialize the solver and declare a binary decision variable on connections
s = SolverModel(model)
Connection.x = Relationship("{Connection} is selected if {x}")
s.solve_for(Connection.x, type="bin")
# Compute out-degree (edges leaving a node) and in-degree (edges entering a node)
out_deg = per(Connection.from_to).sum(Connection.x).group_by(u)
in_deg = per(Connection.from_to).sum(Connection.x).group_by(v)
# Constraint 1: Each source must must not receive any flow
# it may or may not send flow
for source in Sources:
s.satisfy(require(in_deg == 0).where(v == source))
# Constraint 2: Sink must receive exactly one flow and not send any
s.satisfy(require(in_deg == 1).where(v == Sink))
s.satisfy(require(out_deg == 0).where(u == Sink))
# Constraint 3: For each intermediate node, flow in must equal flow out (i.e., conservation of flow)
intermediate_nodes = [wh5, wh6, wh8]
for node in intermediate_nodes:
s.satisfy(
require(
per(Connection.from_to).sum(Connection.x).where(u == node) ==
per(Connection.from_to).sum(Connection.x).where(v == node)
)
)
# Objective: minimize the total distance of all selected connections
s.minimize(sum(Connection.x * d for (conn, u, v, d) in Connection.from_to))
# Solve the model using HiGHS solver and a time limit
s.solve(Solver("highs"), time_limit_sec=10)
s.summarize_result()
# Print the selected connections that form the optimal path(s)
print("Selected path:")
selected_connections = []
for (conn_name, u_name, v_name, d) in select(Connection.name, u.name, v.name, d).from_(Connection.from_to).where(Connection.x >= 0.5):
selected_connections.append((u_name, v_name, d))
print(f"{u_name}->{v_name}: {d}")
Note 1: This does not always hold —product returns and similar reverse flows can introduce cycles into the network.
Note 2: The complete code can be found in the appendix.