1%% Example: Using setReward
for reward-based CTMC analysis
2% This example demonstrates the setReward feature
for defining custom
3% reward functions on a queueing network model and computing steady-state
4% expected rewards
using the CTMC solver.
6% Copyright (c) 2012-2026, Imperial College London
12% Create a simple M/M/1/K queue with finite buffer
14model = Network(
'RewardExample');
17source = Source(model,
'Source');
18queue = Queue(model,
'Queue', SchedStrategy.FCFS);
19sink = Sink(model,
'Sink');
21% Set finite buffer capacity
22queue.setNumberOfServers(1);
23queue.setCapacity(3); % Maximum jobs in the system
26oclass = OpenClass(model,
'Class1');
27source.setArrival(oclass, Exp(2)); % Arrival rate = 2
28queue.setService(oclass, Exp(3)); % Service rate = 3 (utilization ~ 0.67)
31model.link(Network.serialRouting(source,queue,sink));
33%% Define Reward Functions
34% setReward(name, rewardFn) where rewardFn uses RewardState
for clear state access
35% The
new API provides intuitive methods: state.at(node,
class), state.at(node).total(), etc.
37% Reward 1: Queue length (number of jobs in the queue)
38% Old way: state(2) - unclear what state(2) means
39% New way: state.at(queue, oclass) - clear reference to queue and class
40model.setReward('QueueLength', @(state) state.at(queue, oclass));
42% Reward 2: Utilization (1 if server busy, 0 if idle)
43% Using Reward template for clarity
44model.setReward('Utilization', Reward.utilization(queue, oclass));
46% Reward 3: Blocking indicator (1 if buffer full, 0 otherwise)
47% Using Reward template for common metric
48model.setReward('BlockingProb', Reward.blocking(queue));
50% Reward 4: Weighted queue cost (quadratic penalty for
long queues)
51% Custom reward using state accessor
52model.setReward('QueueCost', @(state) state.at(queue, oclass)^2);
54%% Solve with CTMC Solver
55fprintf('Solving with CTMC solver...\n\n');
57options = Solver.defaultOptions;
60solver = CTMC(model, options);
62%% Get Steady-State Expected Rewards
63[R, names] = solver.getAvgReward();
65fprintf('\n=== Steady-State Expected Rewards ===\n');
66for i = 1:length(names)
67 fprintf('%15s: %.6f\n', names{i}, R(i));
70%% Get Transient Reward Analysis
71% Get full reward trajectories over time
72[t, V, names, stateSpace] = solver.getReward();
74fprintf(
'\n=== State Space ===\n');
75fprintf(
'Number of states: %d\n', size(stateSpace, 1));
76fprintf(
'State dimensions: %d\n', size(stateSpace, 2));
78%% Plot Reward Convergence
79figure(
'Name',
'Reward Convergence',
'Position', [100 100 800 600]);
81nRewards = length(names);
85 % Plot mean reward rate over time (averaged over all initial states)
86 meanReward = mean(V{r}, 2); % Average over states
88 % Compute reward rate (reward per unit time)
89 rewardRate = zeros(length(t), 1);
91 rewardRate(k) = meanReward(k) / t(k);
94 plot(t(2:end), rewardRate(2:end),
'LineWidth', 1.5);
96 yline(R(r),
'r--',
'LineWidth', 1.5);
100 ylabel(
'Expected Reward Rate');
101 title(sprintf(
'%s (Steady-state: %.4f)', names{r}, R(r)));
102 legend(
'Transient',
'Steady-state',
'Location',
'best');
106sgtitle(
'Convergence of Reward Functions to Steady-State');
108%% Compare with Analytical Results
for M/M/1/K
109fprintf(
'\n=== Comparison with M/M/1/K Analytical Results ===\n');
111lambda = 2; % Arrival rate
112mu = 3; % Service rate
114K = 10; % Buffer capacity
116% For M/M/1/K queue, steady-state probabilities:
117% pi(n) = (1-rho) * rho^n / (1 - rho^(K+1))
121 pi(n+1) = (1 - rho) * rho^n / (1 - rho^(K+1));
124 pi = ones(K+1, 1) / (K+1);
127% Analytical expected queue length
128L_analytical = sum((0:K)
' .* pi);
130% Analytical utilization (P(server busy) = 1 - pi(0))
131U_analytical = 1 - pi(1);
133% Analytical blocking probability = pi(K)
134B_analytical = pi(end);
136% Analytical queue cost (E[N^2])
137Cost_analytical = sum(((0:K).^2)' .* pi);
139fprintf(
'%15s: LINE = %.6f, Analytical = %.6f, Error = %.2e\n', ...
140 'QueueLength', R(1), L_analytical, abs(R(1) - L_analytical));
141fprintf(
'%15s: LINE = %.6f, Analytical = %.6f, Error = %.2e\n', ...
142 'Utilization', R(2), U_analytical, abs(R(2) - U_analytical));
143fprintf(
'%15s: LINE = %.6f, Analytical = %.6f, Error = %.2e\n', ...
144 'BlockingProb', R(3), B_analytical, abs(R(3) - B_analytical));
145fprintf(
'%15s: LINE = %.6f, Analytical = %.6f, Error = %.2e\n', ...
146 'QueueCost', R(4), Cost_analytical, abs(R(4) - Cost_analytical));
148fprintf(
'\nExample completed successfully.\n');