How to create a sparse vector of class objects

Dear all!
My aim is to write a class to handle locations in 3-d space (let's call it 'CoordinateClass'). Therefore I need to store the three coordinates (along with - at the moment - three other values) in an array. Each set of coordinate must have a unique ID. These IDs range from 0 to let's say 1e6 and are chosen by the user. The current solution is to use a sparse array. The coordinates are stored in the row corresponding to the ID.
What is the best way to get this scenario into a class? I could define a sparse array as the class property and work with that. But it is not as easy as to use a vector of objects like
C(1) = CoordinateClass([1 1 1 0 0 0]) % 3-d point at (1,1,1) with ID 1
C(100) = CoordinateClass([100 2 1 0 0 0]) % 3-d point at (100,2,1) with ID 100
The index is the user-defined ID. The problem is the memory usage. If I use large ID values all objects below are allocated as well. Is there a way to implement some kind of "sparse object" to decrease memory usage?
Thanks in advance

 Respuesta aceptada

Matt J
Matt J el 26 de Ag. de 2013
Editada: Matt J el 26 de Ag. de 2013
You don't want to have a separate C(i) for each coordinate. That will allocate memory very inefficiently. You want a single object which holds your coordinate and other data as an array, e.g.,
classdef CoordinateClass
properties
coordinates;
IDs;
ThreeOtherValues;
end
end
and now you would have
C.coordinates=[1 1 1; 100 2 1; etc...]
C.IDs=[1;100; etc...]
C.ThreeOtherValues=[0 0 0; 0 0 0; etc...]

11 comentarios

Hi!
Thank you for your answer! This solution is of course easy to develop and efficient. But it is(in my opinion) not very user-friendly. Suppose in your example the user wants to change the coordinate with the ID 100.
ind = find(C.IDs==100);
C.coordinates(ind, 1:3) = [100 200 1];
If we had an array C(i) we could just say
C(100).coordinates = [100 200 1];
The same applies e.g. if we want to transfor the cartesian coordinates to polar ones, we might just say:
C(100).cart2pol
Or we add two coordinates (with the overloaded 'plus' function) simply with
C(200) = C(100) + C(1);
instead of
ind1 = find(C.IDs==1);
ind100 = find(C.IDs==100);
ind200 = find(C.IDs==200);
C.coordinates(ind200, 1:3) = C.coordinates(ind100, 1:3) + C.coordinates(ind1, 1:3);
(and we don't have to worry for an array C(i) if C(200) exists already as in 'find(C.IDs==200)'). At the moment I'm using a method 'sumcoordinates' that does the work. The same applies for the above example where I use something like
C = C.changecoordinate(100, [100 200 1]);
It makes things a lot easier to use C(i) in my opinion, but the memory problem ... But maybe there is another solution I don't see. Do you have any hints? Thank you very much!
Matt J
Matt J el 27 de Ag. de 2013
Editada: Matt J el 27 de Ag. de 2013
You can overload the class' SUBSREF method to enable any indexing syntax you want. In any case, you would not use FIND. You would do
ind1=(C.IDs==1);
we don't have to worry for an array C(i) if C(200) exists already
If you're not worrying about this, it means you're not pre-allocating = BAD.
Hi Matt!
Thank you for your assistance! I understood that I can 'emulate' some kind of 'sparse behaviour' by using subsref and subsasgn, like you proposed. Anything else is not usable because a large amount of memory is used if I want to use an array of my class with - let's say - 1e7 elements. Even with pre-allocation it even takes quite some time to work with it.
Nevertheless, I don't know how to cope with my class correctly. I have problems calling the class' methods (even after reading e.g. http://www.mathworks.de/de/help/matlab/ref/subsref.html, http://www.mathworks.de/de/help/matlab/matlab_oop/indexed-reference-and-assignment.html#br09ho8, http://www.mathworks.de/matlabcentral/answers/73430, ...)
Here is the clss code
classdef CoordinateClass
properties
Coord;
IDs;
CoordNum;
end
methods
function obj = CoordinateClass(val)
if ((nargin > 0)&& isa(val, 'numeric') && (length(val) == 1))
% we take the input as the maximum number of coordinates to store
CoordMax = val;
else
% default of 10 coordinates
CoordMax = 10;
end
% pre-allocate arrays
obj.Coord = zeros(CoordMax, 3);
obj.IDs = zeros(CoordMax, 1);
obj.CoordNum = 0;
end
function obj = cart2pol(obj, ind)
fprintf('transforming IDs: %s\n', mat2str(obj.IDs(ind)))
val = obj.Coord(ind, 1:3);
[THETA,RHO,Z] = cart2pol(val(:,1), val(:,2), val(:,3));
obj.Coord(ind, 1:3) = [THETA,RHO,Z];
end
function sref = subsref(obj,s)
switch s(1).type
case '.'
% default dot notation
sref = builtin('subsref',obj,s);
case '()'
% index into coordinate array
if length(s) == 1
% find desired coordinate in array of IDs
[~, ind] = intersect(obj.IDs, s.subs{1});
if ~isempty(ind)
% found -> output as double array
sref = obj.Coord(ind, 1:3);
else
% not found -> output empty array
sref = [];
end
return
elseif length(s) == 2
if strcmp(s(2).type, '.') && strcmp(s(2).subs, 'cart2pol')
% we want to transform some coordinates
% find desired coordinate in array of IDs
[~, ind] = intersect(obj.IDs, s(1).subs{1});
if ~isempty(ind)
% transform found coordinates
obj = cart2pol(obj, ind);
end
sref = obj;
return
end
else
sref = builtin('subsref',obj,s);
end
otherwise
sref = [];
end
end
function obj = subsasgn(obj,s, val)
switch s(1).type
case '.'
% default dot notation
obj = builtin('subsasgn',obj,s);
case '()'
if length(s)<2
% find desired coordinate in array of IDs
[~, ind] = intersect(obj.IDs, s.subs{1});
if any(ind)
% found -> coordinate exists -> change
obj.Coord(ind, 1:3) = val;
else
% not found -> create new coordinate
obj.CoordNum = obj.CoordNum + 1;
obj.Coord(obj.CoordNum, 1:3) = val;
obj.IDs(obj.CoordNum) = s.subs{1};
end
return
else
obj = builtin('subsasgn',obj,s);
end
end
end
end
end
And the test script is
clear all
clc
% create object with a maximum of 10 entries
C = CoordinateClass(10);
% set coordinates
C(1) = [1 1 3];
C(100) = [100 1 3];
C(2) = [2 1 2];
% redefine coordinates
C([1 100]) = [1 2 3; 100 2 3];
% does not work -> transformed coordinates are not returned
C(1:2).cart2pol
% does not work because of missing input argument
C.cart2pol
The second problem seems to be easy to solve by improving the subsref function for all possible calls to the class' object. Is this the best solution or is there something I don't see at the moment?
But I don't know where the first problem comes from. Do you have a hint for me?
Thanks again for your help!
I'm not seeing a problem with C(1:2).cart2pol. The object returned clearly has transformed coordinate data.
>> B=C(1:2).cart2pol; B.Coord, C.Coord
transforming IDs: [1;2]
ans =
1.1071 2.2361 3.0000
100.0000 2.0000 3.0000
0.4636 2.2361 2.0000
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
ans =
1 2 3
100 2 3
2 1 2
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
As for C.cart2pol, it is as you say -- you just need to be doing smarter parsing.
Hi Matt!
I thought about setting the new (transformed) object property implicitly, but I see I have to use
C = C(1:2).cart2pol;
to catch the result of the transformation.
I'll try my best to "invent smarter parsing" for my class. Let's see what happens ... ;-)
Thanks again for your help
Matt J
Matt J el 29 de Ag. de 2013
I thought about setting the new (transformed) object property implicitly, but I see I have to use C = C(1:2).cart2pol; to catch the result of the transformation.
No, you can make the class a Handle Class
Simon
Simon el 29 de Ag. de 2013
Editada: Simon el 29 de Ag. de 2013
Yes, that's possible. Then, I need a copy method like proposed here: http://www.mathworks.com/matlabcentral/newsreader/view_thread/257925
I will think about this approach.
But, is it true that e.g. with overloaded subsasgn a property set method is not called via
obj = builtin('subsasgn',obj,s, val)
I have to take care of every property set method via a switch/case statement in the '.'-section of subsasgn?
Matt J
Matt J el 29 de Ag. de 2013
Editada: Matt J el 29 de Ag. de 2013
No you never have to explicitly call a property set method. A property's set method will be called implicitly whenever an attempt is made to modify the property apart from exceptions mentioned here
That's true whether the modification is made inside subsasgn() or any other class method.
Simon
Simon el 30 de Ag. de 2013
Editada: Simon el 30 de Ag. de 2013
Ok, I understood this point.
But what I can't figure out is how to do "correct indexing". I changed subsref/subsasgn to extract the index like in the example above. But in the example I call the cart2pol function explicitly, mainly because I didn't find a way to call it via
builtin('subsref', obj, s)
with an additional argument (the extracted index).
Another possibility I found is to store the extracted index in a separate property of the object, use builtin(...) and extract the index in the called method.
The question is: what is the best/prefered way to do this?
EDIT: Why does C.cart2pol go through subsref and cart2pol(C) not?
Matt J
Matt J el 30 de Ag. de 2013
Editada: Matt J el 30 de Ag. de 2013
Why does C.cart2pol go through subsref and cart2pol(C) not?
Because C.cart2pol is an indexing expression and cart2pol(C) is not. I assume you're referring to situations when these expressions are executed outside the class. Inside a class method, neither expression goes through subsref.
Yes, your right, I execute the command from my test script. But inside the class the expression goes through subsref if I call it directly
sref = subsref(obj, s,)
(assumed the overloaded subsref handles this correctly) and not with
sref = builtin('subsref',obj,s);
That's ok.

Iniciar sesión para comentar.

Más respuestas (0)

Categorías

Más información sobre Class Introspection and Metadata en Centro de ayuda y File Exchange.

Etiquetas

Preguntada:

el 26 de Ag. de 2013

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by