LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
IndexedTable.m
1classdef IndexedTable < handle
2 % INDEXEDTABLE Generic enhanced table wrapper with flexible object-based filtering
3 %
4 % This class wraps any MATLAB table to enable filtering using Station, Node,
5 % JobClass, and/or Chain objects, while maintaining full backward compatibility
6 % with standard table operations.
7 %
8 % Supports all NetworkSolver result tables:
9 % - AvgTable (Station + JobClass)
10 % - AvgNodeTable (Node + JobClass)
11 % - AvgChainTable (Station + Chain)
12 % - AvgNodeChainTable (Node + Chain)
13 % - AvgSysTable (Chain only)
14 % - Any custom table with Station/Node/JobClass/Chain columns
15 %
16 % Usage:
17 % % Create from any MATLAB table
18 % t_native = solver.avgTable();
19 % t = IndexedTable(t_native);
20 %
21 % % Standard table operations (works as normal)
22 % rows = t.data(1:5, :)
23 % value = t.data.QLen(1)
24 %
25 % % Object-based filtering (four equivalent syntaxes)
26 % rows = t(station_obj) % Direct indexing
27 % rows = t.filterBy(station_obj) % filterBy method
28 % rows = t.get(station_obj) % get alias (shorthand)
29 % rows = t.tget(station_obj) % tget alias
30 %
31 % % Filter by class object
32 % rows = t(class_obj)
33 % rows = t.filterBy(class_obj)
34 % rows = t.get(class_obj)
35 % rows = t.tget(class_obj)
36 %
37 % % Filter by both station/node and class/chain (any order)
38 % rows = t(station_obj, class_obj)
39 % rows = t.filterBy(station_obj, class_obj)
40 % rows = t.get(station_obj, class_obj)
41 % rows = t.tget(station_obj, class_obj)
42 %
43 % Example (AvgTable):
44 % solver = SolverJMT(model);
45 % solver.runAnalyzer();
46 % avgTable = IndexedTable(solver.avgTable());
47 % metrics = avgTable(queue, jobclass);
48 %
49 % Example (AvgNodeTable):
50 % nodeTable = IndexedTable(solver.avgNodeTable());
51 % metrics = nodeTable(sink, jobclass);
52 %
53 % Example (AvgChainTable):
54 % chainTable = IndexedTable(solver.avgChainTable());
55 % metrics = chainTable(queue, chain);
56 %
57 % Example (AvgSysTable):
58 % sysTable = IndexedTable(solver.avgSysTable());
59 % metrics = sysTable(chain);
60
61 properties
62 data % Underlying MATLAB table
63 end
64
65 methods
66 function obj = IndexedTable(table_data)
67 % INDEXEDTABLE(table_data) Create wrapper around any MATLAB table
68 %
69 % Args:
70 % table_data: A MATLAB table object with object-filterable columns
71
72 if ~istable(table_data)
73 error('IndexedTable requires a MATLAB table as input');
74 end
75 obj.data = table_data;
76 end
77
78 function varargout = subsref(obj, s)
79 % SUBSREF Support both standard indexing and object-based filtering
80 %
81 % Intelligently detects object types and filters appropriate columns
82
83 if isempty(s)
84 varargout = {obj};
85 return;
86 end
87
88 switch s(1).type
89 case '()'
90 % Check if this is object-based indexing
91 indices = s(1).subs;
92
93 % Check if first argument is a Station/Node/JobClass/Chain object
94 isObjArg = isa(indices{1}, 'Station') || isa(indices{1}, 'Node') || ...
95 isa(indices{1}, 'JobClass') || isa(indices{1}, 'Chain');
96
97 if isObjArg
98 % Object-based indexing - use filterBy
99 if length(indices) == 1
100 result = obj.filterBy(indices{1});
101 elseif length(indices) == 2
102 result = obj.filterBy(indices{1}, indices{2});
103 else
104 error('Too many arguments for object-based indexing');
105 end
106 else
107 % Standard numeric/logical indexing - pass to underlying table
108 varargout{1} = subsref(obj.data, s);
109 return;
110 end
111
112 % Handle chained indexing
113 if length(s) > 1
114 varargout{1} = subsref(result, s(2:end));
115 return;
116 end
117
118 varargout{1} = result;
119
120 case '.'
121 % Handle method calls and property access
122 if ~isempty(s(1).subs)
123 methodName = s(1).subs;
124 % Check if this is a call to our alias methods
125 if strcmp(methodName, 'get') && length(s) > 1 && strcmp(s(2).type, '()')
126 result = obj.get(s(2).subs{:});
127 if length(s) > 2
128 varargout{1} = subsref(result, s(3:end));
129 return;
130 end
131 varargout{1} = result;
132 return;
133 elseif strcmp(methodName, 'tget') && length(s) > 1 && strcmp(s(2).type, '()')
134 result = obj.tget(s(2).subs{:});
135 if length(s) > 2
136 varargout{1} = subsref(result, s(3:end));
137 return;
138 end
139 varargout{1} = result;
140 return;
141 elseif strcmp(methodName, 'filterBy') && length(s) > 1 && strcmp(s(2).type, '()')
142 result = obj.filterBy(s(2).subs{:});
143 if length(s) > 2
144 varargout{1} = subsref(result, s(3:end));
145 return;
146 end
147 varargout{1} = result;
148 return;
149 end
150 % Check if this is a table column name - delegate to underlying data
151 if ismember(methodName, obj.data.Properties.VariableNames)
152 varargout{1} = subsref(obj.data, s);
153 return;
154 end
155 end
156 % For other properties/methods, delegate to builtin subsref
157 try
158 varargout{1} = builtin('subsref', obj, s);
159 catch ME
160 if strcmp(ME.identifier, 'MATLAB:maxlhs')
161 builtin('subsref', obj, s);
162 varargout = {};
163 else
164 rethrow(ME);
165 end
166 end
167
168 case '{}'
169 % Cell indexing - delegate to underlying table
170 varargout{1} = subsref(obj.data, s);
171
172 otherwise
173 error('Invalid indexing type');
174 end
175 end
176
177 function result = filterBy(obj, varargin)
178 % FILTERBY Filter table by Station/Node and/or JobClass/Chain objects
179 %
180 % Intelligently filters based on table structure and object types
181 %
182 % Usage:
183 % rows = t.filterBy(station_obj)
184 % rows = t.filterBy(class_obj)
185 % rows = t.filterBy(station_obj, class_obj)
186 %
187 % Args:
188 % varargin: 1 or 2 arguments - Station/Node/JobClass/Chain objects
189 %
190 % Returns:
191 % result: Filtered MATLAB table
192
193 if nargin == 2
194 % Single argument
195 arg1 = varargin{1};
196 if isa(arg1, 'Station') || isa(arg1, 'Node')
197 result = obj.filterByFirstDimension(arg1);
198 elseif isa(arg1, 'JobClass') || isa(arg1, 'Chain')
199 result = obj.filterBySecondDimension(arg1);
200 else
201 error('Invalid argument type. Expected Station, Node, JobClass, or Chain.');
202 end
203
204 elseif nargin == 3
205 % Two arguments
206 arg1 = varargin{1};
207 arg2 = varargin{2};
208
209 result = obj.data;
210
211 % Determine order: could be (station, class) or (class, station)
212 if (isa(arg1, 'Station') || isa(arg1, 'Node')) && ...
213 (isa(arg2, 'JobClass') || isa(arg2, 'Chain'))
214 % First arg is station/node, second is class/chain
215 result = obj.filterByFirstDimensionInternal(result, arg1);
216 result = obj.filterBySecondDimensionInternal(result, arg2);
217
218 elseif (isa(arg1, 'JobClass') || isa(arg1, 'Chain')) && ...
219 (isa(arg2, 'Station') || isa(arg2, 'Node'))
220 % First arg is class/chain, second is station/node
221 result = obj.filterByFirstDimensionInternal(result, arg2);
222 result = obj.filterBySecondDimensionInternal(result, arg1);
223
224 else
225 error('Expected one Station/Node and one JobClass/Chain object.');
226 end
227
228 else
229 error('filterBy expects 1 or 2 arguments');
230 end
231 end
232
233 function result = tget(obj, varargin)
234 % TGET Alias for filterBy - backward compatibility
235 result = obj.filterBy(varargin{:});
236 end
237
238 function result = get(obj, varargin)
239 % GET Alias for filterBy - convenient shorthand
240 result = obj.filterBy(varargin{:});
241 end
242
243 function n = numel(obj, varargin)
244 % NUMEL Support numel() function
245 % When called with extra arguments (during indexing like obj(a,b)),
246 % return 1 to tell MATLAB we expect a single output from subsref.
247 % When called directly as numel(obj), return actual element count.
248 if nargin == 1
249 n = numel(obj.data);
250 else
251 n = 1;
252 end
253 end
254
255 function n = numArgumentsFromSubscript(obj, s, indexingContext)
256 % NUMARGUMENTSFROMSUBSCRIPT Specify number of outputs for indexing
257 % This tells MATLAB to always expect exactly 1 output from subsref
258 % operations, which is appropriate for table data access.
259 n = 1;
260 end
261
262 function n = size(obj, varargin)
263 % SIZE Support size() function
264 n = size(obj.data, varargin{:});
265 end
266
267 function n = height(obj)
268 % HEIGHT Get number of rows
269 n = height(obj.data);
270 end
271
272 function n = width(obj)
273 % WIDTH Get number of columns
274 n = width(obj.data);
275 end
276
277 function disp(obj)
278 % DISP Display the table
279 disp(obj.data);
280 end
281
282 function summary(obj)
283 % SUMMARY Display table summary
284 summary(obj.data);
285 end
286
287 function t = head(obj, n)
288 % HEAD Get first n rows
289 if nargin < 2
290 n = 5;
291 end
292 t = head(obj.data, n);
293 end
294
295 function t = tail(obj, n)
296 % TAIL Get last n rows
297 if nargin < 2
298 n = 5;
299 end
300 t = tail(obj.data, n);
301 end
302
303 function t = getTable(obj)
304 % GETTABLE Return the native MATLAB table
305 t = obj.data;
306 end
307
308 function result = varfun(func, obj, varargin)
309 % VARFUN Apply function to table variables
310 % Delegates to the underlying MATLAB table
311 result = varfun(func, obj.data, varargin{:});
312 end
313
314 function result = table2array(obj)
315 % TABLE2ARRAY Convert IndexedTable to array
316 % Delegates to the underlying MATLAB table
317 result = table2array(obj.data);
318 end
319 end
320
321 methods (Access = private)
322 function result = filterByFirstDimension(obj, first_dim_obj)
323 % Filter table by first dimension object (Station or Node)
324 result = obj.filterByFirstDimensionInternal(obj.data, first_dim_obj);
325 end
326
327 function result = filterByFirstDimensionInternal(~, tbl, first_dim_obj)
328 % Internal method for filtering by first dimension
329 colNames = tbl.Properties.VariableNames;
330
331 % Try Station first, then Node
332 if ismember('Station', colNames)
333 col = tbl.Station;
334 if iscategorical(col)
335 result = tbl(col == first_dim_obj.name, :);
336 else
337 result = tbl(strcmp(col, first_dim_obj.name), :);
338 end
339 elseif ismember('Node', colNames)
340 col = tbl.Node;
341 if iscategorical(col)
342 result = tbl(col == first_dim_obj.name, :);
343 else
344 result = tbl(strcmp(col, first_dim_obj.name), :);
345 end
346 else
347 result = tbl;
348 end
349 end
350
351 function result = filterBySecondDimension(obj, second_dim_obj)
352 % Filter table by second dimension object (JobClass or Chain)
353 result = obj.filterBySecondDimensionInternal(obj.data, second_dim_obj);
354 end
355
356 function result = filterBySecondDimensionInternal(~, tbl, second_dim_obj)
357 % Internal method for filtering by second dimension
358 colNames = tbl.Properties.VariableNames;
359
360 % Try JobClass first, then Chain
361 if ismember('JobClass', colNames)
362 col = tbl.JobClass;
363 if iscategorical(col)
364 result = tbl(col == second_dim_obj.name, :);
365 else
366 result = tbl(strcmp(col, second_dim_obj.name), :);
367 end
368 elseif ismember('Chain', colNames)
369 col = tbl.Chain;
370 if iscategorical(col)
371 result = tbl(col == second_dim_obj.name, :);
372 else
373 result = tbl(strcmp(col, second_dim_obj.name), :);
374 end
375 else
376 result = tbl;
377 end
378 end
379 end
380end