rbmatlab  1.16.09
 All Classes Namespaces Files Functions Variables Modules Pages
IdMapNode.m
1 classdef IdMapNode < DataTree.DefaultNode
2  % Data Tree element which can be filtered by 'ids'
3  %
4  % The children branches of this data tree node are all tagged with one or
5  % several "Id"s.
6  %
7  % The branches can then be accessed via the 'id' argument of the get_index()
8  % method.
9  %
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.
12 
13  properties (SetAccess = private)
14  % cell array of "Id" strings
15  idmap;
16  end
17 
18  properties (Constant)
19  % Data node type identifier
20  data_node_type = 'IdMap';
21  end
22 
23  properties (Access = private)
24  % struct mapping between "Id"s and indices of children.
25  invmap;
26  end
27 
28  methods
29 
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.
33  %
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"
37  % string.
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"
40  % string.
41  %
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.
44  %
45  % @sa test_indexed_node() for example usage of this constructor.
46  %
47  % @note that the values given by 'initvalues' are wrapped by a
48  % DataTree.DummyLeafNode if they are not derived from a DataTree.INode.
49  %
50  % Parameters:
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
58  % -# a scalar,
59  % -# a cell array,
60  % -# a struct or
61  % -# an IdMapNode
62  % .
63  % - In the first case every child of this node is set to
64  % this scalar.
65  % - In the second case the 'i'-th child is set to
66  % 'initvalues{i}'.
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,
71  % too.
72  % .
73  % mergefun: This is a function pointer to a function expecting a cell
74  % array of values as an argument and returning an arbitrary
75  % value.
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.
84 
85  ic.idmap = idmap;
86  ic.values = cell(1, length(idmap));
87 
88  if nargin == 2 && ~iscell(initvalues)
89  might_be_recursive = true;
90  else
91  might_be_recursive = false;
92  end
93  recursive = false;
94 
95  ic.invmap = [];
96 
97  for i = 1:length(idmap)
98  if iscell(idmap{i})
99  if might_be_recursive
100  ic.values{i} = DataTree.IdMapNode(idmap{i}, initvalues);
101  recursive = true;
102  end
103 
104  idmapi = DataTree.IdMapNode.flatten_cell_array(idmap{i});
105  for j = 1:length(idmapi)
106  ic.invmap.(idmapi{j}) = i;
107  end
108  else
109  ic.invmap.(idmap{i}) = i;
110  end
111  end
112 
113  % do we have initial values?
114  if nargin >= 2
115  if ~recursive
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};
121  else
122  ic.values{i} = DataTree.DummyLeafNode(initvalues{i});
123  end
124  end
125  else
126  if nargin <= 2
127  mergefun = [];
128  end
129  if ~isempty(initvalues)
130  set_values(ic, initvalues, mergefun);
131  end
132  end
133  else
134  set_values(ic, initvalues);
135  end
136  end
137 
138  end
139 
140  function data = get(this, index)
141  % function data = get(this, index)
142  % @copybrief DataTreeINode.get()
143  %
144  % @copydetails DataTreeINode.get()
145  if length(index) > 1
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);
150  else
151  data = this.values{index};
152  end
153  else
154  data = [];
155  end
156  end
157 
158  % Interface methods
159 
160  function index = get_index(this, id, mu, nt)
161  % function index = get_index(this, id, mu, nt)
162  % @copybrief DataTree.INode.get_index()
163  %
164  % Parameters:
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.
170  %
171  % Return values:
172  % index: index vector for the element which is filter by the argument
173  % triple. If none such element exists, empty vector.
174  if nargin <=1
175  id = [];
176  end
177  if nargin <=2
178  mu = [];
179  end
180  if nargin <=3
181  nt = [];
182  end
183  if isfield(this.invmap, id)
184  invid = this.invmap.(id);
185  index = [invid, get_index(this.values{invid}, id, mu, nt)];
186  else
187  index = [];
188  end
189  end
190 
191 % function path = get_path_index(this, id, mu, nt)
192 % % function path = get_path_index(this, id, mu, nt)
193 % %
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) ];
197 % end
198 % end
199 
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()
203  %
204  % @copydetails DataTree.INode.create_tree()
205  %
206  % Calls DataTree.Creator.create_idmap_node() to build new elements.
207 
208  if nargin < 3
209  ids = [];
210  end
211  if nargin < 4
212  mu_cube = [];
213  end
214  if nargin < 5
215  tslice = [];
216  end
217  if nargin < 6
218  basepath = [];
219  end
220 
221  % get possible indices for traversal
222  if isempty(ids)
223  ids = fieldnames(this.invmap);
224  indices = 1:length(this);
225  else
226  ids=intersect(fieldnames(this.invmap), ids);
227  if isempty(ids)
228  ids = fieldnames(this.invmap);
229  end
230  indices = unique(cellfun(@(x) this.invmap.(x), ids));
231  % indices = unique(cellfun(@(x) get_index(this, x, [], []), ids));
232  end
233 
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);
240  id_map{i} = new_ids;
241 
242  initvalues{i} = create_tree(get(this, indices(i)), creator, ...
243  new_ids, mu_cube, tslice, ...
244  [basepath, indices(i)]);
245  end
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!');
250  else
251  tree = create_idmap_node(creator, id_map, initvalues);
252  end
253  end
254 
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
258  %
259  % @note only direct children can be set.
260  %
261  % Parameters:
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
269  % nodes.
270  %
271  % Return values:
272  % this: handle to the changed IdMapNode.
273  if isempty(i)
274  if nargin <= 3
275  this = set_values(this, value);
276  else
277  this = set_values(this, value, mergefun);
278  end
279  return
280  elseif length(i) > 1
281  if nargin <= 3
282  set(this.values{i(1)}, i(2:end), value)
283  else
284  set(this.values{i(1)}, i(2:end), value, mergefun)
285  end
286  elseif iscell(this.idmap{i})
287  if nargin <= 3
288  this = set_values(this.values{i}, value);
289  else
290  this = set_values(this.values{i}, value, mergefun);
291  end
292  else
293  if isa(value, 'DataTree.INode')
294  this.values{i} = value;
295  else
296  this.values{i} = DataTree.DummyLeafNode(value);
297  end
298  end
299  end
300 
301  function scalar = scalar(this, mergefun)
302  % function scalar = scalar(this, mergefun)
303  % reduces the data tree values with help of the mergefun
304  %
305  % Parameters:
306  % mergefun: This is a function pointer to a function expecting a cell
307  % array of values as an argument and returning an arbitrary
308  % value.
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.
318  if nargin == 1
319  mergefun = @(x) x{1};
320  end
321 
322  tmpcurvals = get_flat_struct(this);
323 
324  scalar = DataTree.IdMapNode.reduce(this.idmap, tmpcurvals, mergefun);
325  end
326 
327 
328  %% Other Methods
329 
330  function ids = get_all_ids(this)
331  % function ids = get_all_ids(this)
332  % returns all "Id" strings in this IdMapNode.
333  %
334  % @note that a flat cell array is returned, such that this might unequal
335  % to the idmap.
336  %
337  % Return values:
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));
340  end
341 
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"
345  % names.
346  %
347  % Return values:
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);
352 
353  stcell = reshape([ids;vals], 1, 2*length(ids));
354  st = struct(stcell{:});
355  end
356 
357  function str = num2str(this)
358  % function str = num2str(this)
359  % converts the data tree into a string
360  %
361  % Return values:
362  % str: a string representation of the data tree
363  fs = get_flat_struct(this);
364  fns = fieldnames(fs);
365  str = '[';
366  for i = 1:length(fns)
367  str = [ str, fns{i}, ': ', num2str(fs.(fns{i})) ];
368  if i < length(fns)
369  str = [ str, ', ' ];
370  end
371  end
372  str = [ str, ']' ];
373  end
374 
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.
378  %
379  % This function assumes, that the IdMapNode has only leaf children.
380  %
381  % Parameters:
382  % scalar: The scalar to substract from the leaf values.
383  %
384  % Return values:
385  % this: handle to the changed IdMapNode.
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);
389  end
390  end
391 
392 
393  function display(this)
394  % function display(this)
395  % prints out a string representation of the data tree.
396  disp(get_flat_struct(this));
397  end
398  end
399 
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.
404  %
405  % Parameters:
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.
410  % Return values:
411  % this: handle to the current IdMapNode.
412  if nargin == 3
413  recursive = false;
414  if isempty(mergefun)
415  mergefun = @(x) x{1};
416  end
417  else
418  recursive = true;
419  end
420 
421  if isa(values, 'DataTree.IdMapNode')
422  values = get_flat_struct(values);
423  end
424  if isstruct(values)
425  for i = 1:length(this.idmap)
426  fn = this.idmap{i};
427  if iscell(this.idmap{i})
428  if recursive
429  set_values(this.values{i}, values);
430  else
431  try
432  this.values{i} = DataTree.DummyLeafNode(...
433  DataTree.IdMapNode.reduce(this.idmap{i}, values, mergefun)...
434  );
435  catch exception
436  error(['Could not set values. Exception:, ' getReport(exception)]);
437  end
438  end
439  elseif isfield(values, fn)
440  val = values.(fn);
441  if isa(val, 'DataTree.INode')
442  this.values{i} = val;
443  else
444  this.values{i} = DataTree.DummyLeafNode(val);
445  end
446  end
447  end
448  elseif isscalar(values)
449  for i = 1:length(this.values)
450  this.values{i} = DataTree.DummyLeafNode(values);
451  end
452  elseif isempty(values)
453  warning('RBmatlab:Logic', 'Assigned nothing, because values argument is empty!');
454  else
455  error('Invalid assignment argument in method ''set_values''.');
456  end
457  end
458  end
459 
460  methods(Static, Access = public)
461 
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.
466  %
467  % Parameters:
468  % ca: a cell array
469  %
470  % Return values:
471  % flatca: the flattended cell array
472  for i = 1:length(ca)
473  if iscell(ca{i})
474  ca{i} = DataTree.IdMapNode.flatten_cell_array(ca{i});
475  else
476  ca{i} = ca(i);
477  end
478  end
479  flatca = [ca{:}];
480  end
481  end
482 
483  methods(Static, Access = private)
484 
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
488  %
489  % This function recursively reduces the values.
490  %
491  % Parameters:
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
495  % cell argument.
496  % Return values:
497  % value: scalar return by the 'mergefun'
498  if nargin == 2
499  mergefun = @(x) x{1};
500  end
501  tmpvals = cell(1, length(idmap));
502  for i = 1:length(idmap)
503  if iscell(idmap{i})
504  tmpvals{i} = DataTree.IdMapNode.reduce(idmap{i}, values, mergefun);
505  else
506  if ~isfield(values, idmap{i})
507  throw(MException('RBmatlab:IdMapNode:internal',['Values are missing the field: ', idmap{i}]));
508  end
509  val = values.(idmap{i});
510  if isa(val, 'DataTree.DummyLeafNode')
511  val = get(val, 1);
512  end
513  tmpvals{i} = val;
514  end
515  end
516 
517  value = mergefun(tmpvals);
518 
519  end
520  end
521  methods(Static)
522  function idname = name_from_idmap(idmap)
523  % function idname = name_from_idmap(idmap)
524  % creates a string representation of an "Id"-map
525  %
526  % Parameters:
527  % idmap: a cell array of strings
528  %
529  % Return values:
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{:});
534  end
535  end
536 end
537 
Data Tree element which can be filtered by ids
Definition: IdMapNode.m:18
Dummy implementation for a DataTree.ILeafNode that stores a single data.
Definition: DummyLeafNode.m:18
Interface for a node in a DataTree.
Definition: INode.m:18
virtual function DataTree.INode tree = create_tree(DataTree.ICreator creator, ids, mu_cube, tslice, basepath)
Creates a new tree from a subtree specified by ids, parameter and time index regions.
Default implementation for a DataTree.INode.
Definition: DefaultNode.m:18
virtual function index = get_index(id, mu, nt)
Obtains the leaf index vector that best fits the child description given by the three arguments...
Definition: leaf.m:17
Data Tree element which can be filtered by time instants.
Definition: TpartNode.m:18
static function flatca = flatten_cell_array(ca)
flattens a cell array, i.e. returns a cell without cell entries which are cell arrays by itself...
Definition: IdMapNode.m:531