| name | reinforcement-learning-supply-chain |
| description | When the user wants to apply reinforcement learning to supply chain problems, learn optimal policies, or solve sequential decision-making under uncertainty. Also use when the user mentions "reinforcement learning," "Q-learning," "deep Q-networks," "policy gradient," "actor-critic," "RL for inventory," "dynamic pricing with RL," "warehouse robot control," or "sequential optimization." For forecasting, see neural-networks-forecasting. For static optimization, see optimization-modeling. |
Reinforcement Learning for Supply Chain
You are an expert in applying reinforcement learning to supply chain sequential decision-making problems. Your goal is to help design, train, and deploy RL agents that learn optimal policies for inventory control, pricing, routing, and resource allocation through interaction with environments.
Initial Assessment
- Problem Type: Sequential decisions? (inventory orders, pricing adjustments, routing)
- State Space: What information available? (inventory levels, demand, prices)
- Action Space: What decisions? (order quantities, prices, routes)
- Reward Function: How measure performance? (profit, service level, cost)
- Environment: Simulator available or real system?
RL Fundamentals
Markov Decision Process (MDP):
- States (S): system conditions
- Actions (A): available decisions
- Transitions (P): state dynamics
- Rewards (R): immediate feedback
- Policy (π): state → action mapping
Goal: Learn policy π that maximizes expected cumulative reward
Q-Learning for Inventory Control
import numpy as np
import matplotlib.pyplot as plt
from collections import defaultdict
class InventoryEnvironment:
"""
Inventory control environment
State: current inventory level
Action: order quantity
Reward: -holding_cost - backorder_cost + revenue
"""
def __init__(self,
max_inventory=50,
holding_cost=1.0,
backorder_cost=10.0,
order_cost=2.0,
price=15.0):
self.max_inventory = max_inventory
self.h_cost = holding_cost
self.b_cost = backorder_cost
self.o_cost = order_cost
self.price = price
self.mean_demand = 10
self.state = 20
def reset(self):
"""Reset environment"""
self.state = 20
return self.state
def step(self, action):
"""
Take action (order quantity), observe demand, get reward
Returns: next_state, reward, done
"""
inventory_after_order = min(self.state + action, self.max_inventory)
demand = np.random.poisson(self.mean_demand)
sales = min(inventory_after_order, demand)
backorder = max(0, demand - inventory_after_order)
next_inventory = inventory_after_order - sales
revenue = self.price * sales
holding = self.h_cost * next_inventory
backorder_penalty = self.b_cost * backorder
ordering = self.o_cost * action
reward = revenue - holding - backorder_penalty - ordering
self.state = next_inventory
done = False
return next_inventory, reward, done
class QLearningAgent:
"""
Q-Learning agent for inventory control
"""
def __init__(self,
state_space,
action_space,
learning_rate=0.1,
discount_factor=0.95,
epsilon=0.1):
self.states = state_space
self.actions = action_space
self.lr = learning_rate
self.gamma = discount_factor
self.epsilon = epsilon
self.Q = defaultdict(lambda: defaultdict(float))
def select_action(self, state):
"""
Epsilon-greedy action selection
"""
if np.random.random() < self.epsilon:
return np.random.choice(self.actions)
else:
q_values = [self.Q[state][a] for a in self.actions]
best_action = self.actions[np.argmax(q_values)]
return best_action
def update(self, state, action, reward, next_state):
"""
Q-learning update rule
Q(s,a) ← Q(s,a) + α[r + γ max_a' Q(s',a') - Q(s,a)]
"""
current_q = self.Q[state][action]
next_q_values = [self.Q[next_state][a] for a in self.actions]
max_next_q = max(next_q_values)
target = reward + self.gamma * max_next_q
self.Q[state][action] = current_q + self.lr * (target - current_q)
def get_policy(self):
"""Extract greedy policy from Q-values"""
policy = {}
for state in self.states:
q_values = [self.Q[state][a] for a in self.actions]
best_action = self.actions[np.argmax(q_values)]
policy[state] = best_action
return policy
env = InventoryEnvironment()
agent = QLearningAgent(
state_space=list(range(51)),
action_space=list(range(21)),
learning_rate=0.1,
discount_factor=0.95,
epsilon=0.1
)
n_episodes = 10000
episode_rewards = []
for episode in range(n_episodes):
state = env.reset()
total_reward = 0
for t in range(30):
action = agent.select_action(state)
next_state, reward, done = env.step(action)
agent.update(state, action, reward, next_state)
total_reward += reward
state = next_state
if done:
break
episode_rewards.append(total_reward)
if (episode + 1) % 1000 == 0:
avg_reward = np.mean(episode_rewards[-100:])
print(f"Episode {episode+1}: Avg Reward = {avg_reward:.2f}")
policy = agent.get_policy()
print("\nLearned Policy (Inventory → Order Quantity):")
for inventory in range(0, 51, 5):
order = policy.get(inventory, 0)
print(f" Inventory {inventory}: Order {order}")
Deep Q-Network (DQN) for Complex States
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
from collections import deque
import random
class DQN(nn.Module):
"""
Deep Q-Network
"""
def __init__(self, state_dim, action_dim, hidden_dim=128):
super(DQN, self).__init__()
self.network = nn.Sequential(
nn.Linear(state_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, action_dim)
)
def forward(self, state):
return self.network(state)
class DQNAgent:
"""
DQN Agent with Experience Replay and Target Network
"""
def __init__(self, state_dim, action_dim, lr=0.001, gamma=0.99):
self.state_dim = state_dim
self.action_dim = action_dim
self.gamma = gamma
self.q_network = DQN(state_dim, action_dim)
self.target_network = DQN(state_dim, action_dim)
self.target_network.load_state_dict(.q_network.state_dict())
.optimizer = optim.Adam(.q_network.parameters(), lr=lr)
.loss_fn = nn.MSELoss()
.memory = deque(maxlen=)
.batch_size =
():
random.random() < epsilon:
random.randint(, .action_dim - )
torch.no_grad():
state_tensor = torch.FloatTensor(state).unsqueeze()
q_values = .q_network(state_tensor)
q_values.argmax().item()
():
.memory.append((state, action, reward, next_state, done))
():
(.memory) < .batch_size:
batch = random.sample(.memory, .batch_size)
states = torch.FloatTensor([t[] t batch])
actions = torch.LongTensor([t[] t batch])
rewards = torch.FloatTensor([t[] t batch])
next_states = torch.FloatTensor([t[] t batch])
dones = torch.FloatTensor([t[] t batch])
q_values = .q_network(states).gather(, actions.unsqueeze())
torch.no_grad():
next_q_values = .target_network(next_states).()[]
targets = rewards + .gamma * next_q_values * ( - dones)
loss = .loss_fn(q_values.squeeze(), targets)
.optimizer.zero_grad()
loss.backward()
.optimizer.step()
():
.target_network.load_state_dict(.q_network.state_dict())
Policy Gradient for Continuous Actions
class PolicyNetwork(nn.Module):
"""
Policy network for continuous actions
"""
def __init__(self, state_dim, action_dim, hidden_dim=128):
super(PolicyNetwork, self).__init__()
self.network = nn.Sequential(
nn.Linear(state_dim, hidden_dim),
nn.Tanh(),
nn.Linear(hidden_dim, hidden_dim),
nn.Tanh()
)
self.mean_layer = nn.Linear(hidden_dim, action_dim)
self.log_std_layer = nn.Linear(hidden_dim, action_dim)
def forward(self, state):
features = self.network(state)
mean = self.mean_layer(features)
log_std = self.log_std_layer(features)
std = torch.exp(log_std)
return mean, std
class PolicyGradientAgent:
"""
REINFORCE algorithm for policy gradient
"""
def __init__(self, state_dim, action_dim, lr=0.001, gamma=0.99):
self.policy = PolicyNetwork(state_dim, action_dim)
self.optimizer = optim.Adam(self.policy.parameters(), lr=lr)
self.gamma = gamma
self.saved_log_probs = []
self.rewards = []
def select_action(self, state):
state_tensor = torch.FloatTensor(state).unsqueeze()
mean, std = .policy(state_tensor)
dist = torch.distributions.Normal(mean, std)
action = dist.sample()
log_prob = dist.log_prob(action).()
.saved_log_probs.append(log_prob)
action.numpy()[]
():
returns = []
R =
r (.rewards):
R = r + .gamma * R
returns.insert(, R)
returns = torch.tensor(returns)
returns = (returns - returns.mean()) / (returns.std() + )
policy_loss = []
log_prob, R (.saved_log_probs, returns):
policy_loss.append(-log_prob * R)
policy_loss = torch.stack(policy_loss).()
.optimizer.zero_grad()
policy_loss.backward()
.optimizer.step()
.saved_log_probs = []
.rewards = []
Applications
1. Dynamic Pricing
2. Warehouse Robot Control
3. Supply Chain Network Optimization
4. Order Fulfillment
Tools & Libraries
Python RL:
stable-baselines3: RL algorithms
Ray RLlib: Distributed RL
TensorFlow Agents: TF-based RL
PyTorch: Custom implementations
Simulation:
SimPy: Discrete-event simulation
Gym: RL environments
- Custom simulators
Related Skills
- optimization-modeling: traditional optimization
- optimization-ml-hybrid: RL + optimization
- **dynamic-pricing`: pricing applications
- inventory-optimization: inventory control
- **route-optimization`: VRP with RL