LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
Source.m
1classdef Source < Station
2 % Source External job arrival node for open queueing networks
3 %
4 % Source represents an external arrival node that generates jobs for open
5 % classes according to specified arrival processes. It serves as the entry
6 % point for jobs entering the network from the external environment, with
7 % configurable arrival rates and distributions for each job class.
8 %
9 % @brief External arrival node generating jobs for open queueing networks
10 %
11 % Key characteristics:
12 % - External job generation for open classes
13 % - Class-dependent arrival processes
14 % - Infinite capacity job source
15 % - Configurable inter-arrival time distributions
16 % - Integration with network routing
17 %
18 % Source node features:
19 % - Multiple job class support
20 % - Flexible arrival process specification
21 % - Poisson, MAP, and general arrival processes
22 % - Arrival rate configuration per class
23 % - Disabled arrival capability for specific classes
24 %
25 % Source is used for:
26 % - Web server client arrivals
27 % - Manufacturing job arrivals
28 % - Call center customer generation
29 % - Network packet injection
30 % - Open system workload modeling
31 %
32 % Example:
33 % @code
34 % model = Network('WebServer');
35 % source = Source(model, 'ClientArrivals');
36 % webClass = OpenClass(model, 'WebRequests', 1);
37 % source.setArrival(webClass, Exp(2.0)); % Poisson arrivals, rate 2
38 % @endcode
39 %
40 % Copyright (c) 2012-2026, Imperial College London
41 % All rights reserved.
42
43 properties
44 schedStrategy;
45 arrivalProcess;
46 markedProcess; % shared MarkedMAP driving the marked classes (empty if none)
47 markedClasses; % (1,K) class indexes bound to marks 1..K of markedProcess
48 arrivalBatch; % (1,K) cell of batch-size DiscreteDistribution per class (empty = single arrivals)
49 end
50
51 methods
52 %Constructor
53 function self = Source(model, name)
54 % SOURCE Create an external arrival source node
55 %
56 % @brief Creates a Source node for external job generation
57 % @param model Network model to add the source node to
58 % @param name String identifier for the source node
59 % @return self Source instance ready for arrival process configuration
60 self@Station(name);
61 if model.isMatlabNative()
62 self.numberOfServers = 1;
63 if(model ~= 0)
64 classes = model.getClasses();
65 self.classCap = Inf*ones(1,length(classes));
66 self.output = Dispatcher(classes);
67 self.server = ServiceTunnel();
68 self.input = RandomSource(classes);
69 self.schedStrategy = SchedStrategy.EXT;
70 self.setModel(model);
71 model.addNode(self);
72 end
73 elseif model.isJavaNative()
74 self.setModel(model);
75 self.obj=jline.lang.nodes.Source(model.obj, name);
76 end
77 end
78
79 function setArrivalBatch(self, class, batchSize)
80 % SETARRIVALBATCH(CLASS, BATCHSIZE)
81 %
82 % Turns each arrival epoch of CLASS into the simultaneous release of
83 % a batch of jobs. The interarrival distribution set by SETARRIVAL
84 % keeps spacing the epochs; this decides how many jobs each epoch
85 % releases. Geometric interarrivals with a Geometric batch size is
86 % the Geo^X arrival stream, whose analytical counterpart is
87 % QSYS_GEOXGEO1.
88 %
89 % The batch size must be supported on {1,2,...}: an epoch that
90 % releases no job is not an arrival epoch, so a law that can return
91 % zero is rejected rather than clamped. Pass [] to restore single
92 % arrivals.
93 if isempty(batchSize)
94 if numel(self.arrivalBatch) >= class.index
95 self.arrivalBatch{1, class.index} = [];
96 end
97 return
98 end
99 if ~isa(batchSize, 'DiscreteDistribution')
100 line_error(mfilename, 'arrival batch size must be a DiscreteDistribution');
101 end
102 if batchSize.getMean() < 1
103 line_error(mfilename, sprintf(['arrival batch size for class ''%s'' has mean %g; ' ...
104 'a batch must carry at least one job, so its support must be {1,2,...}'], ...
105 class.name, batchSize.getMean()));
106 end
107 if isempty(self.obj)
108 self.arrivalBatch{1, class.index} = batchSize;
109 else
110 self.obj.setArrivalBatch(class.obj, batchSize.obj);
111 end
112 end
113
114 function batchSize = getArrivalBatch(self, class)
115 % BATCHSIZE = GETARRIVALBATCH(CLASS)
116 % Returns the batch-size law bound to CLASS, or [] for single arrivals.
117 batchSize = [];
118 idx = class;
119 if isobject(class) && isprop(class, 'index')
120 idx = class.index;
121 end
122 if numel(self.arrivalBatch) >= idx
123 batchSize = self.arrivalBatch{1, idx};
124 end
125 end
126
127 function setArrival(self, class, distribution)
128 % SETARRIVAL(CLASS, DISTRIBUTION)
129 % distribution can be a Distribution object or a Workflow object
130
131 % If Workflow, convert to PH distribution
132 if isa(distribution, 'Workflow')
133 distribution = distribution.toPH();
134 end
135
136 if isempty(self.obj)
137 % Check if arrival was already configured
138 if length(self.input.sourceClasses) >= class.index && ~isempty(self.input.sourceClasses{1, class.index})
139 % Note: We no longer invalidate hasStruct here as it causes severe performance
140 % issues in iterative solvers like LN. The refreshRates/refreshProcesses methods
141 % called during solver post-iteration phase handle updating procid appropriately.
142 self.model.setInitialized(false);
143 end
144 self.input.sourceClasses{1, class.index}{2} = ServiceStrategy.LI;
145 self.input.sourceClasses{1, class.index}{3} = distribution;
146 self.arrivalProcess{1,class.index} = distribution;
147 if distribution.isDisabled()
148 self.classCap(class.index) = 0;
149 else
150 self.classCap(class.index) = Inf;
151 end
152 % Update cached procid if struct exists to avoid stale values
153 % This is needed because we don't invalidate hasStruct for performance
154 if self.model.hasStruct && ~isempty(self.model.sn)
155 ist = self.model.getStationIndex(self);
156 c = class.index;
157 procTypeId = ProcessType.toId(ProcessType.fromText(builtin('class', distribution)));
158 self.model.sn.procid(ist, c) = procTypeId;
159 end
160 else
161 self.obj.setArrival(class.obj, distribution.obj);
162 % Also update MATLAB-side storage to keep in sync with Java object
163 % This ensures getArrivalProcess returns the correct distribution
164 % Check if arrival was already configured
165 if length(self.input.sourceClasses) >= class.index && ~isempty(self.input.sourceClasses{1, class.index})
166 % Note: We no longer invalidate hasStruct here as it causes severe performance
167 % issues in iterative solvers like LN. The refreshRates/refreshProcesses methods
168 % called during solver post-iteration phase handle updating procid appropriately.
169 self.model.setInitialized(false);
170 end
171 self.input.sourceClasses{1, class.index}{2} = ServiceStrategy.LI;
172 self.input.sourceClasses{1, class.index}{3} = distribution;
173 self.arrivalProcess{1,class.index} = distribution;
174 if distribution.isDisabled()
175 self.classCap(class.index) = 0;
176 else
177 self.classCap(class.index) = Inf;
178 end
179 % Update cached procid if struct exists to avoid stale values
180 if self.model.hasStruct && ~isempty(self.model.sn)
181 ist = self.model.getStationIndex(self);
182 c = class.index;
183 procTypeId = ProcessType.toId(ProcessType.fromText(builtin('class', distribution)));
184 self.model.sn.procid(ist, c) = procTypeId;
185 end
186 end
187 end
188
189 function distrib = getArrivalProcess(self, oclass)
190 distrib = self.arrivalProcess{oclass};
191 end
192
193 function setMarkedArrival(self, mmap, classes)
194 % SETMARKEDARRIVAL(MMAP, CLASSES)
195 %
196 % Bind a MarkedMAP with K marks to K open classes: mark k emits
197 % jobs of class CLASSES{k}, with all marks driven by one shared
198 % modulating chain. CLASSES is a cell array or vector of K
199 % distinct OpenClass handles, ordered by mark index.
200 if ~isa(mmap, 'MarkedMAP')
201 line_error(mfilename, 'setMarkedArrival requires a MarkedMAP/MMAP arrival process.');
202 end
203 if ~isempty(self.obj)
204 line_error(mfilename, 'setMarkedArrival is not yet supported with the Java backend (lang=''java'').');
205 end
206 K = mmap.getNumberOfTypes;
207 if iscell(classes)
208 classList = classes;
209 else
210 classList = num2cell(classes);
211 end
212 if numel(classList) ~= K
213 line_error(mfilename, sprintf('The MarkedMAP has %d types but %d classes were supplied.', K, numel(classList)));
214 end
215 markClasses = zeros(1, K);
216 for k = 1:K
217 cls = classList{k};
218 if ~isa(cls, 'OpenClass')
219 line_error(mfilename, 'setMarkedArrival requires open classes.');
220 end
221 markClasses(k) = cls.index;
222 end
223 if numel(unique(markClasses)) ~= K
224 line_error(mfilename, 'setMarkedArrival requires distinct classes for the marks.');
225 end
226 % Per-class binding: every marked class stores the shared MarkedMAP
227 % object, so procid resolves to MMAP and rates/SCV are derived from
228 % the per-mark marginal in the refresh layer.
229 for k = 1:K
230 self.setArrival(classList{k}, mmap);
231 end
232 self.markedProcess = mmap;
233 self.markedClasses = markClasses;
234 end
235
236 end
237
238end
Definition Station.m:245