1 classdef IdMapNode < DataTree.DefaultNode
2 % Data Tree element which can be filtered by
'ids'
4 % The children branches of
this data tree node are all tagged with one or
7 % The branches can then be accessed via the
'id' argument of the get_index()
10 % Note: If one branch is tagged with a list of several
"Id"s, any of these
11 %
"Id"s in the last can be used to access the tagged branch.
13 properties (SetAccess =
private)
14 % cell array of
"Id" strings
19 % Data node type identifier
20 data_node_type =
'IdMap';
23 properties (Access =
private)
24 %
struct mapping between "Id"s and indices of children.
30 function ic =
IdMapNode(idmap, initvalues, mergefun)
31 %
function ic =
IdMapNode(idmap, initvalues, mergefun)
32 % constructor
for an
IdMapNode mapping the children nodes with
"Id" tags.
34 % This constructor can be run in two modes:
35 % -# @em Flat-mode: This creates exactly one IdMapNode, such that
36 % it is possible that one child is tagged with more than one "Id"
38 % -# @em Recursive-mode: This creates a tree of IdMapNode nodes,
39 % such that the leafs of this tree are tagges with exactly one "Id"
42 % The constructor runs in @em Flat-mode, if a 'mergefun' is given or if
43 % 'initvalues' is a cell array. Otherwise, it runs in @em Recursive-mode.
45 % @sa test_indexed_node() for example usage of this constructor.
47 % @note that the values given by 'initvalues' are wrapped by a
48 % DataTree.DummyLeafNode if they are not derived from a DataTree.INode.
51 % idmap: a cell array of "Id" strings that are tagged to the
52 % children of this node. These "Id"s can also be nested,
53 % which either leads to a child tagged with more than one
54 % "Id"s or a recursively build tree of IdMapNode nodes.
55 % See the explanation of @em Flat-mode and @em
56 % Recursive-Mode for details.
57 % initvalues: This can be
63 % - In the first case every child of this node is set to
65 % - In the second case the 'i'-th child is set to
67 % - In the third and fourth case, a child tagged with
68 % 'id' is set to 'initvalues.(id)
' respectively
69 % 'get(initvalues, get_index(initvalues,
id, [], []))
'.
70 % In @em Flat-mode, the 'mergefun
' might apply here,
73 % mergefun: This is a function pointer to a function expecting a cell
74 % array of values as an argument and returning an arbitrary
76 % This function pointer is used, in case the constructor runs
77 % in @em Flat-mode and the initvalues are given as an
78 % IdMapNode or a struct. For children tagged with several
79 % "Id"s 'id1, ..., idn
', mergefun can be used to compute a
80 % value for this child from the cell array
81 % '{initvalues.id1,..., initvalues.idn}
'.
82 % If 'mergefun
' is set to '[]
', a default function
83 % @code @(x) x{1}; @endcode is used.
86 ic.values = cell(1, length(idmap));
88 if nargin == 2 && ~iscell(initvalues)
89 might_be_recursive = true;
91 might_be_recursive = false;
97 for i = 1:length(idmap)
100 ic.values{i} = DataTree.IdMapNode(idmap{i}, initvalues);
104 idmapi = DataTree.IdMapNode.flatten_cell_array(idmap{i});
105 for j = 1:length(idmapi)
106 ic.invmap.(idmapi{j}) = i;
109 ic.invmap.(idmap{i}) = i;
113 % do we have initial values?
116 if iscell(initvalues)
117 assert(length(initvalues) == length(ic.values));
118 for i = 1:length(initvalues)
119 if isa(initvalues{i}, 'DataTree.INode
')
120 ic.values{i} = initvalues{i};
122 ic.values{i} = DataTree.DummyLeafNode(initvalues{i});
129 if ~isempty(initvalues)
130 set_values(ic, initvalues, mergefun);
134 set_values(ic, initvalues);
140 function data = get(this, index)
141 % function data = get(this, index)
142 % @copybrief DataTree.INode.get()
144 % @copydetails DataTree.INode.get()
146 data = get(this.values{index(1)}, index(2:end));
147 elseif length(index) == 1
148 if isa(this.values{index}, 'DataTree.DummyLeafNode
')
149 data = get(this.values{index}, 1);
151 data = this.values{index};
160 function index = get_index(this, id, mu, nt)
161 % function index = get_index(this, id, mu, nt)
162 % @copybrief DataTree.INode.get_index()
165 % id: id string filtered by this node or another IdMapNode instance.
166 % mu: a parameter vector filtered through a DataTree.PpartNode instance in
167 % the tree hierarchy.
168 % nt: an integer corresponding to a time step index filtered through a
169 % DataTree.TpartNode instance in the tree hierarchy.
172 % index: index vector for the element which is filter by the argument
173 % triple. If none such element exists, empty vector.
183 if isfield(this.invmap, id)
184 invid = this.invmap.(id);
185 index = [invid, get_index(this.values{invid}, id, mu, nt)];
191 % function path = get_path_index(this, id, mu, nt)
192 % % function path = get_path_index(this, id, mu, nt)
194 % path = get_index(this, id, mu, nt);
195 % if ~this.recursive || iscell(this.idmap{path})
196 % path = [ path, get_path_index(this.values{path}, id, mu, nt) ];
200 function tree = create_tree(this, creator, ids, mu_cube, tslice, basepath)
201 % function tree = create_tree(this, creator, ids, mu_cube, tslice, basepath)
202 % @copybrief DataTree.INode.create_tree()
204 % @copydetails DataTree.INode.create_tree()
206 % Calls DataTree.Creator.create_idmap_node() to build new elements.
221 % get possible indices for traversal
223 ids = fieldnames(this.invmap);
224 indices = 1:length(this);
226 ids=intersect(fieldnames(this.invmap), ids);
228 ids = fieldnames(this.invmap);
230 indices = unique(cellfun(@(x) this.invmap.(x), ids));
231 % indices = unique(cellfun(@(x) get_index(this, x, [], []), ids));
234 initvalues = cell(1, length(indices));
235 id_map = cell(1, length(indices));
236 for i=1:length(indices)
237 % compute id intersection
238 new_id_extract = cellfun(@(x) this.invmap.(x)==indices(i), ids);
239 new_ids = ids(new_id_extract);
242 initvalues{i} = create_tree(get(this, indices(i)), creator, ...
243 new_ids, mu_cube, tslice, ...
244 [basepath, indices(i)]);
246 if length(indices) == 1
247 tree = initvalues{1};
248 elseif isempty(indices)
249 throw('Merge of trees failed, because given
id region was not found!
');
251 tree = create_idmap_node(creator, id_map, initvalues);
255 function this = set(this, i, value, mergefun)
256 % function this = set(this, i, value[, mergefun])
257 % sets a new value at a specific child branch
259 % @note only direct children can be set.
262 % i: scalar index of the child to be set or vector of indices.
263 % In the latter case, set() is called recursively.
264 % value: a scalar, a struct or a tree of IdMapNode nodes as
265 % described in the constructor IdMapNode.IdMapNode().
266 % mergefun: (optional) If given, the function runs in @em Flat-mode as
267 % described in the constructor description IdMapNode.IdMapNode().
268 % Then 'value
' needs to be a struct or a tree of IdMapNode
272 % this: handle to the changed IdMapNode.
275 this = set_values(this, value);
277 this = set_values(this, value, mergefun);
282 set(this.values{i(1)}, i(2:end), value)
284 set(this.values{i(1)}, i(2:end), value, mergefun)
286 elseif iscell(this.idmap{i})
288 this = set_values(this.values{i}, value);
290 this = set_values(this.values{i}, value, mergefun);
293 if isa(value, 'DataTree.INode
')
294 this.values{i} = value;
296 this.values{i} = DataTree.DummyLeafNode(value);
301 function scalar = scalar(this, mergefun)
302 % function scalar = scalar(this, mergefun)
303 % reduces the data tree values with help of the mergefun
306 % mergefun: This is a function pointer to a function expecting a cell
307 % array of values as an argument and returning an arbitrary
309 % For children tagged with several "Id"s 'id1, ..., idn
',
310 % mergefun can be used to compute a single value for all thes
311 % children values. It gathers the values
312 % '{ values1, ..., valuesn }
' in a cell array and applies the
313 % 'mergefun
' on it. The result is returned. The algorithm is
314 % run recursively, such that the leaf-level is reduced first,
315 % and the upper level's values are reduced.
316 % If
'mergefun' is
set to
'[]', a
default function
317 % @code @(x) x{1};
@endcode is used.
319 mergefun = @(x) x{1};
322 tmpcurvals = get_flat_struct(
this);
324 scalar = DataTree.IdMapNode.reduce(this.idmap, tmpcurvals, mergefun);
330 function ids = get_all_ids(
this)
331 %
function ids = get_all_ids(
this)
332 % returns all
"Id" strings in
this IdMapNode.
334 % @note that a flat cell array is returned, such that
this might unequal
338 % ids: a cell array of strings with the
"Id"s in
this data tree.
339 ids = unique(DataTree.IdMapNode.flatten_cell_array(
this.idmap));
342 function st = get_flat_struct(
this)
343 %
function st = get_flat_struct(
this)
344 % makes a
struct from the data tree with field names equal to the "Id"
348 % st: flat
struct as described above
349 ids = get_all_ids(
this);
351 vals = cellfun(@(x)
get(
this, get_index(
this, x, [], [])), ids,
'UniformOutput',
false);
353 stcell = reshape([ids;vals], 1, 2*length(ids));
354 st =
struct(stcell{:});
357 function str = num2str(
this)
358 %
function str = num2str(
this)
359 % converts the data tree into a
string
362 % str: a
string representation of the data tree
363 fs = get_flat_struct(
this);
364 fns = fieldnames(fs);
366 for i = 1:length(fns)
367 str = [ str, fns{i},
': ', num2str(fs.(fns{i})) ];
375 function this = minus(this, scalar)
376 % function this = minus(this, scalar)
377 % substracts a scalar from each of the values in the data tree.
379 % This function assumes, that the IdMapNode has only
leaf children.
382 % scalar: The scalar to substract from the
leaf values.
386 for i = 1:length(this.values)
387 assert(isa(this.values{i},
'DataTree.ILeafNode'));
388 this.values{i} = DataTree.DummyLeafNode(
get(this.values{i},1) - scalar);
393 function display(
this)
394 %
function display(
this)
395 % prints out a
string representation of the data tree.
396 disp(get_flat_struct(
this));
400 methods(Access =
private)
401 function this = set_values(this, values, mergefun)
402 % function this = set_values(this, values, mergefun)
403 % sets new values in the data tree.
406 % values: the new values. This can be either
407 % -
# a scalar substituting all values by this scalar,
408 % -# another tree of
IdMapNode nodes or a struct, indicating which
409 % "Id" mapped values shall be set.
411 % this: handle to the current IdMapNode.
415 mergefun = @(x) x{1};
421 if isa(values,
'DataTree.IdMapNode')
422 values = get_flat_struct(values);
425 for i = 1:length(this.idmap)
427 if iscell(this.idmap{i})
429 set_values(this.values{i}, values);
432 this.values{i} = DataTree.DummyLeafNode(...
433 DataTree.IdMapNode.reduce(
this.idmap{i}, values, mergefun)...
436 error([
'Could not set values. Exception:, ' getReport(exception)]);
439 elseif isfield(values, fn)
441 if isa(val, 'DataTree.
INode')
442 this.values{i} = val;
444 this.values{i} = DataTree.DummyLeafNode(val);
448 elseif isscalar(values)
449 for i = 1:length(this.values)
450 this.values{i} = DataTree.DummyLeafNode(values);
452 elseif isempty(values)
453 warning('RBmatlab:Logic', 'Assigned nothing, because values argument is empty!');
455 error('Invalid assignment argument in method ''set_values''.');
460 methods(Static, Access = public)
462 function flatca = flatten_cell_array(ca)
463 % function flatca = flatten_cell_array(ca)
464 % flattens a cell array, i.e. returns a cell without cell entries which
465 % are cell arrays by itself.
471 % flatca: the flattended cell array
474 ca{i} = DataTree.IdMapNode.flatten_cell_array(ca{i});
483 methods(Static, Access =
private)
485 function value = reduce(idmap, values, mergefun)
486 % function value = reduce(idmap, values, mergefun)
487 % Recursively reduces the
leaf values of the data tree to a scalar value
489 % This function recursively reduces the values.
492 % values: struct of values mapping "Id"s to values
493 % mergefun: function ptr assuming a cell of values as an argument.
494 % By default this is set to a function that returns the first
497 % value: scalar return by the 'mergefun'
499 mergefun = @(x) x{1};
501 tmpvals = cell(1, length(idmap));
502 for i = 1:length(idmap)
504 tmpvals{i} = DataTree.IdMapNode.reduce(idmap{i}, values, mergefun);
506 if ~isfield(values, idmap{i})
507 throw(MException(
'RBmatlab:IdMapNode:internal',[
'Values are missing the field: ', idmap{i}]));
509 val = values.(idmap{i});
510 if isa(val,
'DataTree.DummyLeafNode')
517 value = mergefun(tmpvals);
522 function idname = name_from_idmap(idmap)
523 % function idname = name_from_idmap(idmap)
524 % creates a
string representation of an "Id"-map
527 % idmap: a cell array of strings
530 % idname: a
string of all "Id"s concatenated with the '_' character.
532 tmp = unique(DataTree.
IdMapNode.flatten_cell_array(idmap));
533 idname = sprintf('%s_', tmp{:});