Skip to content

Commit

Permalink
Re #1791 refactored concatenate method of unique_objects_container.m
Browse files Browse the repository at this point in the history
  • Loading branch information
abuts committed Jan 23, 2025
1 parent 54bc691 commit 9fc0625
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 50 deletions.
64 changes: 64 additions & 0 deletions _test/test_object_containers/test_unique_objects.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,70 @@ function test_two_fields_constructor(~)
assertEqual(uoc.n_unique,2)
assertEqual(uoc.idx,[1,2]);
end
%------------------------------------------------------------------
function test_concatenate_some_different(~)
uoc = unique_objects_container('char');
uoc(1)='aa';
uoc(2)='bb';
uoc(3)='cc';
uoc(4)='aa';
uoc2 = uoc;
uoc2(3) = 'dd';
uoc2(4) = 'ee';

conc = unique_objects_container.concatenate({uoc,uoc2});
assertEqual(conc.n_objects,8)
assertEqual(conc.n_unique,5)
assertEqual(conc.unique_objects,{'aa','bb','cc','dd','ee'});
assertEqual(conc.n_duplicates,[3,2,1,1,1]) ;
assertEqual(conc.idx,[uoc.idx,uoc.idx(1:2),[4,5]]);
end

function test_concatenate_same(~)
uoc = unique_objects_container('char');
uoc(1)='aa';
uoc(2)='bb';
uoc(3)='cc';
uoc(4)='aa';

conc = unique_objects_container.concatenate({uoc,uoc});
assertEqual(conc.n_objects,8)
assertEqual(conc.n_unique,3)
assertEqual(conc.unique_objects,{'aa','bb','cc'});
assertEqual(conc.n_duplicates,[4,2,2]) ;
assertEqual(conc.idx,[uoc.idx,uoc.idx]);
end
function test_concatenate_containers_not_implemented(~)
data = 'aabbccaa';

me = assertExceptionThrown(@()unique_objects_container.concatenate(data), ...
'HERBERT:unique_objects_container:invalid_argument');
assertEqual(me.message(1:83), ...
'Input for this method may be cellarray or array of unique_objects_container classes')
end


function test_concatenate_cellarrays_only_fails(~)
data = {'aa','bb','cc','aa'};


me = assertExceptionThrown(@()unique_objects_container.concatenate({data,data}), ...
'HERBERT:unique_objects_container:invalid_argument');
assertEqual(me.message(1:65), ...
'Input cellarray may contain unique_objects_container members only')
end

function test_concatenate_different_fails(~)
uoc = unique_objects_container('char');
data = {'aa','bb','cc','aa'};
uoc = uoc.add(data);

me = assertExceptionThrown(@()uoc.concatenate({uoc,data}), ...
'HERBERT:unique_objects_container:invalid_argument');
assertEqual(me.message(1:65), ...
'Input cellarray may contain unique_objects_container members only')
end
%
%
%------------------------------------------------------------------
function test_find_in_container_object(obj)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
properties (Access=protected)
stored_hashes_ = cell(1,0); % their hashes are stored
% (default respecified in constructor inputParser)
n_duplicates_ = zeros(1,0); % number of duplicated
n_duplicates_ = zeros(1,0); % number of duplicated
end
properties(Dependent,Hidden)
% property containing list of stored hashes for unique objects for
Expand Down Expand Up @@ -135,10 +135,10 @@
n_objects = 1;
end
if ~isnumeric(n_objects) || ~isscalar(n_objects)|| n_objects<=0
error('HERBERT:unique_objects_container:invalid_argument',[...
error('HERBERT:unique_objects_container:invalid_argument',[...
'n_objects have to be numeric positive scalar.\n' ...
'It is: %s of class %s'],...
num2str(n_objects),class(n_objects));
num2str(n_objects),class(n_objects));
end
if obj.n_unique ~= 1
error('HERBERT:unique_objects_container:invalid_argument',[...
Expand All @@ -162,7 +162,7 @@
% - hash : the hash of the object from hashify
%
% - obj : input object. If hashable, contains calculated hash
% value, if this value have not been there initially
% value, if this value have not been there initially
%
[obj,hash] = build_hash(obj);
if isempty(self.stored_hashes_)
Expand Down Expand Up @@ -391,7 +391,7 @@
obj = loadobj@serializable(S,obj);
end

function out = concatenate(objs, type)
function out = concatenate(objs, varargin)
%CONCATENATE takes the unique_object and idx (index) arrays from
% an array of one or more unique_object_containers and concatenates
% separately the unique objects and the indices to single outputs
Expand All @@ -400,51 +400,44 @@
% Input
% -----
% - objs - one cell or array of one or more unique_object_containers
% - type - '{}' if objs is a cell; anything else (but by
% convention '()') if objs is an array
%
% Outputs
% -------
% - out - single unique_objects_container combining the contents of
% the elements of the input array objs

if isempty(objs)
error('HERBERT:unique_objects_container:invalid_input', ...
error('HERBERT:unique_objects_container:invalid_argument', ...
'at least one object must be supplied to concatenate');
end

concat_cells = strcmp(type,'{}');

if numel(objs)==1
if concat_cells
out = objs{1};
else
out = objs;
if iscell(objs)
is_uoc = cellfun(@(x)isa(x,'unique_objects_container'),objs);
is_cell = true;
if ~all(is_uoc)
nuoc = sum(~is_uoc);
error('HERBERT:unique_objects_container:invalid_argument',[ ...
'Input cellarray may contain unique_objects_container members only.\n' ...
'There are %d out of %d non-complying objects in input cellarray'],nuoc,numel(objs));
end
elseif ~isa(objs,'unique_objects_container')
error('HERBERT:unique_objects_container:invalid_argument', ...
'Input for this method may be cellarray or array of unique_objects_container classes. It is: "%s"',...
class(objs))
else
if concat_cells
out = objs{1};
for ii=2:numel(objs)
for jj=1:objs{ii}.n_runs
out_obj = objs{ii}.get(jj);
%out_hsh = build_hash(out_obj);
%[~,index] = ismember(out_hsh, out.stored_hashes_);
%if index==0, index = []; end
%out = out.add_single_(out_obj,index); %( objs{ii}.get(jj) );
end
end
is_cell = false;
end
if is_cell
out = objs{1};
else
out = objs(1);
end
for i=2:numel(objs)
if is_cell
add_obj = objs{i};
else
out = objs(1);
for ii=2:numel(objs)
for jj=1:objs(ii).n_runs
out_obj = objs(ii).get(jj);
out_hsh = build_hash(out_obj);
[~,index] = ismember(out_hsh, out.stored_hashes_);
if index==0, index = []; end
out = out.add_single_(out_obj,index); %( objs{ii}.get(jj) );
end
end
add_obj = objs(i);
end
out = out.add(add_obj);
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
end

% call find_in_container to get position and hash of the object
[lidx,hash,obj] = self.find_in_container(obj);
[lidx,hash,obj] = self.find_in_container(obj,false);

% If the object is not in the container add it to the container.
if isempty(lidx) % means obj not in container and should be added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,21 @@
%UNQUE_ONLY_OBJ_CONTAINER contains only unique objects providing
%permanent references (pointers) to these objects
%
% Despite this is still serializable object, there are no situation
% when this object is expected to be serialized or saved to hdd.
% contrary to unique_objects_container, it also contains and
% exposed only unique addresses of the objects it holds.
%
% There are two types of addresses of this object: local indices (lidx)
% -- continuous array of numbers with size, equal to number of objects
% container holds and global indices (gidx, exposed as idx to adhere to
% common containers interface) -- the array of constant indices(numbers)
% used to address container's unique objects from out
% unique
% objects can be addressed from outside
%
%
% Despite this is still serializable object by inheritance, there are
% no situation when this object is expected to be serialized or saved
% to hdd, so searilizable features of this object are disabled.
%
properties (Access=protected)
n_unique_ = 0; % number of unique objects stored in
Expand All @@ -29,15 +42,15 @@
lidx % access to internal local indices of the container,
% controlling distribution of information within the container
end
%properties(Constant,Access= protected)
properties(Access=protected)
% typical experiment may contain ~200 runs. The configuration is
% usually unique, but in special cases may differ between runs.
% let's select number which minimize memory storage but from another,
% do not reallocate memory too often to accommodate all runs which
% may become unique. If these assumptions are insufficient, we may
% always introduce more complex algorithm (e.g. doubling each
% allocation size. see e.g. C++ stl algorithms for vector) in a future.
% let's select number which minimize memory storage but from other
% side, do not reallocate memory too often to accommodate all runs
% which may become unique. If these assumptions are insufficient,
% we may always introduce more complex algorithm (e.g. doubling each
% allocation size. see e.g. C++ stl algorithms for the vector)
% in a future.
mem_expansion_chunk_ = 100;
end
methods(Access=protected)
Expand Down Expand Up @@ -228,11 +241,13 @@
'This funciton is pissible but does not make sence on unique_only_obj_container')
end
%
function val = hash(self,index)
function val = hash(self,lidx)
% accessor for the stored hashes looping over container indices
%
% Indices are accessed through their local indices in container
%
% confusing test function.
val = self.stored_hashes_{ self.lidx_(index) };
val = self.stored_hashes_{ self.lidx_(lidx) };
end
end
%----------------------------------------------------------------------
Expand Down Expand Up @@ -263,6 +278,8 @@
function check_if_range_allowed(self,nuix,varargin)
% Validates if input non-unique index is in the range of indices
% allowed for current state of the container
%
%
if nargin==3
upper_range = self.max_obj_idx_+1;
if any(nuix == upper_range)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
% unique objects container. if false, return index of
% the object used in this container
% Output:
% - ix : the index of the input object in list of local indices
% - idx : the index of the input object in list of local indices
% self.idx_ if return_global is false, or
% unique index of the object in the global storage
% If object is not stored, return emtpy []
Expand All @@ -199,7 +199,7 @@

function sset = get_subset(self, indices)
% retrieve set of objects, defined by input indices
% and arrange then into appropriate unique references container
% and arrange then into appropriate unique objects container
sset = unique_objects_container('baseclass', self.baseclass);
for i=indices
item = self.get(i);
Expand Down

0 comments on commit 9fc0625

Please sign in to comment.