1%% Example: Multi-Class Reward Analysis
2% This example demonstrates reward-based CTMC analysis in a multi-
class queueing
3% network, showing how to define per-
class metrics and analyze class-specific behavior.
5% Copyright (c) 2012-2026, Imperial College London
11% Create an M/M/2 queue with two job
classes (prioritized service)
13model = Network(
'MultiClassRewardExample');
16source = Source(model,
'Source');
17queue = Queue(model,
'Queue', SchedStrategy.PS); % Processor Sharing
18sink = Sink(model,
'Sink');
20% Server capacity and buffer limit
21queue.setNumberOfServers(2);
22queue.setCapacity(6); % Limit state space
24% Job
classes with different characteristics
25class_interactive = OpenClass(model,
'Interactive');
26class_batch = OpenClass(model,
'Batch');
28% Different arrival rates
29source.setArrival(class_interactive, Exp(2.0)); % Interactive: λ = 2.0
30source.setArrival(class_batch, Exp(1.5)); % Batch: λ = 1.5
32% Different service time requirements
33queue.setService(class_interactive, Exp(0.5)); % Interactive: μ = 2.0 (fast)
34queue.setService(class_batch, Exp(1.0)); % Batch: μ = 1.0 (slow)
36% Topology (same
for both
classes)
37P = model.initRoutingMatrix;
38P{class_interactive} = Network.serialRouting(source, queue, sink);
39P{class_batch} = Network.serialRouting(source, queue, sink);
42%% Define Per-Class Rewards
43fprintf(
'Defining per-class reward metrics:\n\n');
45% === Interactive Class Metrics ===
47% Jobs of Interactive
class
48model.setReward(
'Interactive_QLen', @(state) state.at(queue, class_interactive));
49fprintf(
' ✓ Interactive_QLen = state.at(queue, Interactive)\n');
51% Utilization contributed by Interactive
class
52model.setReward(
'Interactive_Util', Reward.utilization(queue, class_interactive));
53fprintf(
' ✓ Interactive_Util = Reward.utilization(queue, Interactive)\n');
55% === Batch Class Metrics ===
58model.setReward(
'Batch_QLen', @(state) state.at(queue, class_batch));
59fprintf(
' ✓ Batch_QLen = state.at(queue, Batch)\n');
61% Utilization contributed by Batch
class
62model.setReward(
'Batch_Util', Reward.utilization(queue, class_batch));
63fprintf(
' ✓ Batch_Util = Reward.utilization(queue, Batch)\n');
65% === Comparative Metrics ===
67% Total queue length (both
classes)
68model.setReward(
'Total_QLen', @(state) state.at(queue).total());
69fprintf(
' ✓ Total_QLen = state.at(queue).total()\n');
71% Total system utilization
72model.setReward(
'Total_Util', Reward.utilization(queue));
73fprintf(
' ✓ Total_Util = Reward.utilization(queue)\n');
75% Ratio of Interactive to Batch jobs
76model.setReward(
'Interactive_Ratio', @(state) ...
77 state.at(queue, class_interactive) / ...
78 max(state.at(queue, class_batch), 0.001)); % Avoid division by zero
79fprintf(
' ✓ Interactive_Ratio = Interactive / Batch\n');
81% === Priority-Aware Metrics ===
83% Weighted response time cost (Interactive weighted 3x, Batch weighted 1x)
84% Approximates weighted system delay
85model.setReward(
'Weighted_Cost', @(state) ...
86 3.0 * state.at(queue, class_interactive) + ...
87 1.0 * state.at(queue, class_batch));
88fprintf(
' ✓ Weighted_Cost = 3.0*Interactive + 1.0*Batch\n');
90% Service fairness indicator: 1
if both
classes present, 0 otherwise
91model.setReward(
'Fairness', @(state) ...
92 double(state.at(queue, class_interactive) > 0 && ...
93 state.at(queue, class_batch) > 0));
94fprintf(
' ✓ Fairness = (Interactive > 0) AND (Batch > 0)\n');
96%% Solve with CTMC Solver
97fprintf(
'\nSolving with CTMC solver...\n\n');
99options = Solver.defaultOptions;
102solver = CTMC(model, options);
104%% Get Steady-State Expected Rewards
105[R, names] = solver.getAvgReward();
107fprintf(
'=== Steady-State Expected Rewards (Multi-Class) ===\n\n');
109fprintf(
'Interactive Class:\n');
110fprintf(
' %-25s: %10.6f (jobs)\n',
'Interactive_QLen', R(1));
111fprintf(
' %-25s: %10.6f (server capacity)\n',
'Interactive_Util', R(2));
113fprintf(
'\nBatch Class:\n');
114fprintf(
' %-25s: %10.6f (jobs)\n',
'Batch_QLen', R(3));
115fprintf(
' %-25s: %10.6f (server capacity)\n',
'Batch_Util', R(4));
117fprintf(
'\nComparative Metrics:\n');
118fprintf(
' %-25s: %10.6f (both classes)\n',
'Total_QLen', R(5));
119fprintf(
' %-25s: %10.6f (2 servers)\n',
'Total_Util', R(6));
120fprintf(
' %-25s: %10.6f (ratio)\n',
'Interactive_Ratio', R(7));
122fprintf(
'\nPriority-Aware Metrics:\n');
123fprintf(
' %-25s: %10.6f (weighted cost)\n',
'Weighted_Cost', R(8));
124fprintf(
' %-25s: %10.6f (fraction of time both present)\n', ...
127%% Analytical Validation
128fprintf(
'\n=== Analysis ===\n');
134c = 2; % Number of servers
136rho_int = lambda_int / (c * mu_int); % 2 / (2*2) = 0.5
137rho_batch = lambda_batch / (c * mu_batch); % 1.5 / (2*1) = 0.75
138rho_total = rho_int + rho_batch; % 1.25
140fprintf(
'System Characteristics:\n');
141fprintf(
' Interactive: λ=%.1f, μ=%.1f per server\n', lambda_int, mu_int);
142fprintf(
' Batch: λ=%.1f, μ=%.1f per server\n', lambda_batch, mu_batch);
143fprintf(
' Servers: %d\n', c);
144fprintf(
' Total utilization: %.3f (%.1f%% capacity)\n', ...
145 rho_total, 100*rho_total);
150 pct_int = 100 * R(1) / total_qlen;
151 pct_batch = 100 * R(3) / total_qlen;
152 fprintf(
'\nQueue Composition:\n');
153 fprintf(
' Interactive jobs: %.2f%% of queue\n', pct_int);
154 fprintf(
' Batch jobs: %.2f%% of queue\n', pct_batch);
157% Response time estimation (Little
's Law)
158total_arrival = lambda_int + lambda_batch;
159est_resp_time_int = R(1) / lambda_int;
160est_resp_time_batch = R(3) / lambda_batch;
162fprintf('\nEstimated Response Times (Little
''s Law):\n
');
163fprintf(' Interactive: %.6f time units\n
', est_resp_time_int);
164fprintf(' Batch: %.6f time units\n
', est_resp_time_batch);
166fprintf('\n✓ Example completed successfully.\n
');