1function [QN,UN,RN,TN,CN,XN,totiter] = solver_nc_mem(sn, options)
2% [QN,UN,RN,TN,CN,XN,TOTITER] = SOLVER_NC_MEM(SN, OPTIONS)
4% Maximum Entropy Method (MEM)
for Open Queueing Networks
6% Implements the ME algorithm from Kouvatsos (1994) for analyzing open
7% queueing networks with general arrival and service processes.
10% sn - Network structure from getStruct()
11% options - Solver options with MEM-specific fields:
12% - config.mem_tol: convergence tolerance (default 1e-6)
13% - config.mem_maxiter: maximum iterations (default 1000)
14% - config.mem_verbose: print iteration info (default false)
17% QN - Mean queue lengths [M x R matrix]
18% UN - Utilizations [M x R matrix]
19% RN - Mean response times [M x R matrix]
20% TN - Throughputs (arrival rates) [M x R matrix]
21% CN - Completion times (not computed by MEM, returns zeros)
22% XN - System throughputs (not computed by MEM, returns zeros)
23% totiter - Number of iterations until convergence
26% D.D. Kouvatsos, "Entropy Maximisation and Queueing Network Models",
27% Annals of Operations Research, 48:63-126, 1994.
29% Copyright (c) 2012-2026, Imperial College London
32% Validate that model
is open
33if ~sn_is_open_model(sn)
34 line_error(mfilename, 'MEM only supports open queueing networks (all
classes must be open).');
37if sn_is_closed_model(sn)
38 line_error(mfilename, 'MEM does not support models with closed
classes.');
45% Get MEM options from config
46if isfield(options, 'config') && isfield(options.config, 'mem_tol')
47 tol = options.config.mem_tol;
52if isfield(options, 'config') && isfield(options.config, 'mem_maxiter')
53 maxiter = options.config.mem_maxiter;
58if isfield(options, 'config') && isfield(options.config, 'mem_verbose')
59 verbose = options.config.mem_verbose;
64% Create MEM options structure
65mem_options = struct();
67mem_options.maxiter = maxiter;
68mem_options.verbose = verbose;
70% Initialize parameter matrices for me_oqn
71lambda0 = zeros(M, R); % External arrival rates
72Ca0 = zeros(M, R); % External arrival SCV
73mu = zeros(M, R); % Service rates
74Cs = zeros(M, R); % Service SCV
75P = zeros(M, M, R); % Routing probabilities
77% Extract service rates and service SCVs
81 mu(i, r) = sn.rates(i, r);
83 % Extract service SCV from service process
84 if ~isempty(sn.proc) && i <= length(sn.proc) && ~isempty(sn.proc{i}) && ...
85 r <= length(sn.proc{i}) && ~isempty(sn.proc{i}{r})
87 if iscell(MAP) && length(MAP) >= 2
88 Cs(i, r) = map_scv(MAP);
90 Cs(i, r) = 1.0; % Default to exponential
93 Cs(i, r) = 1.0; % Default to exponential
99% Find source node
for external arrivals
102 if sn.nodetype(i) == NodeType.Source
103 sourceIdx = sn.nodeToStation(i);
108% Extract external arrival rates and SCVs
109if ~isempty(sourceIdx) && sourceIdx > 0
111 if sn.rates(sourceIdx, r) > 0
112 externalRate = sn.rates(sourceIdx, r);
115 % Extract arrival SCV from arrival process
116 if ~isempty(sn.proc) && sourceIdx <= length(sn.proc) && ~isempty(sn.proc{sourceIdx}) && ...
117 r <= length(sn.proc{sourceIdx}) && ~isempty(sn.proc{sourceIdx}{r})
118 MAP = sn.proc{sourceIdx}{r};
119 if iscell(MAP) && length(MAP) >= 2
120 Ca_external = map_scv(MAP);
124 % Distribute external arrivals based on routing from source
127 % Check
if queue i receives external arrivals
129 sourceNode = sn.stationToNode(sourceIdx);
130 queueNode = sn.stationToNode(i);
132 if sourceNode > 0 && queueNode > 0
133 routeIdx = (sourceNode-1)*R + r;
134 destIdx = (queueNode-1)*R + r;
136 if routeIdx <= size(sn.rt, 1) && destIdx <= size(sn.rt, 2)
137 routeProb = sn.rt(routeIdx, destIdx);
139 lambda0(i, r) = externalRate * routeProb;
140 Ca0(i, r) = Ca_external;
151% Extract routing probabilities between queues
156 jNode = sn.stationToNode(j);
157 iNode = sn.stationToNode(i);
159 if jNode > 0 && iNode > 0
160 % Skip Source and Sink
nodes
161 if sn.nodetype(jNode) ~= NodeType.Source && ...
162 sn.nodetype(jNode) ~= NodeType.Sink && ...
163 sn.nodetype(iNode) ~= NodeType.Source && ...
164 sn.nodetype(iNode) ~= NodeType.Sink
166 routeIdx = (jNode-1)*R + r;
167 destIdx = (iNode-1)*R + r;
169 if routeIdx <= size(sn.rt, 1) && destIdx <= size(sn.rt, 2)
170 P(j, i, r) = sn.rt(routeIdx, destIdx);
179% Call Maximum Entropy algorithm
180[L, W, Ca, Cd, lambda, rho] = me_oqn(M, R, lambda0, Ca0, mu, Cs,
P, mem_options);
182% Map to LINE standard output format
183QN = L; % Queue lengths
184UN = rho; % Utilizations
185RN = W; % Response times (waiting times)
186TN = lambda; % Throughputs (arrival rates)
187CN = zeros(1, R); % Completion times (not computed by MEM)
188XN = zeros(1, R); % System throughputs (not computed by MEM)
190% Extract iteration count if available
191% (me_oqn doesn't currently return this, so we return maxiter as placeholder)
192totiter = mem_options.maxiter;