2222% mtVariable = MappedTensor(..., 'Class', strClassName)
2323% mtVariable = MappedTensor(..., 'HeaderBytes', nHeaderBytesToSkip)
2424% mtVariable = MappedTensor(..., 'MachineFormat', strMachineFormat)
25+ % mtVariable = MappedTensor(tExistingTensor, 'Convert')
26+ % mtVariable = MappedTensor(..., 'Like', tExistingTensor)
2527%
2628% 'vnTensorSize', or [nDim1 nDim2 nDim3 ...] defines the desired size of the
2729% variable. By default, a new binary temporary file will be generated, and
4749% ('ieee-be') or little-endian ('ieee-le') formats for data storage and
4850% reading. If not specified, the machine-native format will be used.
4951%
52+ % The optional argument 'Convert' allows you to convert an existing matlab
53+ % tensor 'tExistingTensor' into a MappedTensor, of the appropriate class.
54+ %
55+ % The optional argument 'Like' allows you to create a MappedTensor with the
56+ % same class and complexity (i.e. real or complex) of 'tExistingTensor'.
57+ % Note that sparse MappedTensors are not supported.
58+ %
5059% Usage: size(mtVariable)
5160% mtVariable(:) = rand(100, 100, 100);
5261% mfData = mtVariable(:, :, 34, 2);
6978% times (A*B, A.*B) as long as one of A or B is a scalar. Divide (A/B,
7079% A./B, B\A, B.\A) is supported, as long as B is a scalar.
7180%
81+ % Transparent casting to other classes is supported in O(1) time. Note that
82+ % due to transparent casting and tranparent O(1) scaling, rounding may
83+ % occur in a different class to the returned data, and therefore may not
84+ % match matlab rounding precisely. If this is an issue, index the tensor
85+ % and then scale the returned values rather than rely on O(1) scaling of
86+ % the entire tensor.
87+ %
7288% Save and load is minimally supported -- data is NOT saved, but on load a new
7389% mapped tensor will be generated and filled with zeros. Both save and load
7490% generate warnings.
149165% fixes.
150166
151167classdef MappedTensor < handle
152- properties (SetAccess = private , GetAccess = private )
168+ properties (SetAccess = private , GetAccess = public )
153169 strRealFilename ; % Binary data file on disk (real part of tensor)
154170 strCmplxFilename ; % Binary data file on disk (complex part of tensor)
155171 bReadOnly = false ; % Should the data be protected from writing?
215231 vbKeepArg(nArg : nArg + 1 ) = false ;
216232 nArg = nArg + 1 ;
217233
234+ case {' convert' }
235+ % - Convert an existing tensor into a MappedTensor
236+ if (nargin > 2 )
237+ error(' MappedTensor:Usage' , ...
238+ ' *** MappedTensor: Only a single input argument is required when using '' Convert'' .' );
239+ end
240+
241+ % - Do we already have a MappedTensor?
242+ if (isa(varargin{1 }, ' MappedTensor' ))
243+ % - Just return it
244+ mtVar = varargin{1 };
245+ return ;
246+
247+ else
248+ % - Get the
249+ tfSourceTensor = varargin{1 };
250+
251+ % - Check the size of the incoming tensor
252+ if (numel(tfSourceTensor ) == 0 )
253+ error(' MappedTensor:Arguments' , ...
254+ ' *** MappedTensor: A zero-sized tensor cannot be converted to a MappedTensor.' );
255+ end
256+
257+ % - Remove 'Convert' arguments from varargin
258+ varargin([1 nArg ]) = [];
259+
260+ % - Create a MappedTensor
261+ mtVar = MappedTensor(size(tfSourceTensor ), varargin{: }, ' Like' , tfSourceTensor );
262+
263+ % - Copy the data
264+ subsasgn(mtVar , substruct(' ()' , {' :' }), tfSourceTensor );
265+ return ;
266+ end
267+
268+ case {' like' }
269+ % - Set the class property accordingly
270+ mtVar.strClass = class(varargin{nArg + 1 });
271+
272+ % - Set the complexity (real or complex) accordingly
273+ if (~isreal(varargin{nArg + 1 }))
274+ mtVar.bIsComplex = true ;
275+ end
276+
277+ vbKeepArg(nArg : nArg + 1 ) = false ;
278+ nArg = nArg + 1 ;
279+
218280 otherwise
219281 % - No other properties are supported
220282 error(' MappedTensor:InvalidProperty' , ...
261323
262324 % - Validate tensor size argument
263325 try
264- validateattributes(vnTensorSize , {' numeric' }, {' positive' , ' integer' });
326+ validateattributes(vnTensorSize , {' numeric' }, {' positive' , ' integer' , ' nonempty ' });
265327 catch
266328 error(' MappedTensor:Arguments' , ...
267329 ' *** MappedTensor: Error: '' vnTensorSize'' must be a positive integer vector.' );
305367
306368 % - Record number of total elements
307369 mtVar.nNumElements = prod(vnTensorSize );
370+
371+ % - Set complexity
372+ if (mtVar .bIsComplex )
373+ make_complex(mtVar );
374+ end
308375 end
309376
310377 % delete - DESTRUCTOR
@@ -442,13 +509,33 @@ function delete(mtVar)
442509 end
443510
444511 % - Reference the tensor data element
512+ tfData = mt_read_data(mtVar .hShimFunc , mtVar .hRealContent , S , vnReferencedTensorSize , mtVar .strStorageClass , mtVar .nHeaderBytes , mtVar .bBigEndian , mtVar .hRepSumFunc , mtVar .hChunkLengthFunc );
513+
445514 if (mtVar .bIsComplex )
446- % - Get the real and complex parts
447- tfData = complex(mtVar .fRealFactor .* mt_read_data(mtVar .hShimFunc , mtVar .hRealContent , S , vnReferencedTensorSize , mtVar .strClass , mtVar .nHeaderBytes , mtVar .bBigEndian , mtVar .hRepSumFunc , mtVar .hChunkLengthFunc ), ...
448- mtVar .fComplexFactor .* mt_read_data(mtVar .hShimFunc , mtVar .hCmplxContent , S , vnReferencedTensorSize , mtVar .strClass , mtVar .nHeaderBytes , mtVar .bBigEndian , mtVar .hRepSumFunc , mtVar .hChunkLengthFunc ));
515+ tfImagData = mt_read_data(mtVar .hShimFunc , mtVar .hCmplxContent , S , vnReferencedTensorSize , mtVar .strStorageClass , mtVar .nHeaderBytes , mtVar .bBigEndian , mtVar .hRepSumFunc , mtVar .hChunkLengthFunc );
516+ end
517+
518+ % - Cast data, if required
519+ if (mtVar .bMustCast )
520+ tfData = cast(tfData , mtVar .strClass );
521+
522+ if (mtVar .bIsComplex )
523+ tfImagData = cast(tfImagData , mtVar .strClass );
524+ end
525+ end
526+
527+ % - Apply scaling factors
528+ if (mtVar .bIsComplex )
529+ tfData = complex(mtVar .fRealFactor .* tfData , ...
530+ mtVar .fComplexFactor .* tfImagData );
449531 else
450- % - Just return the real part
451- tfData = mtVar .fRealFactor .* mt_read_data(mtVar .hShimFunc , mtVar .hRealContent , S , vnReferencedTensorSize , mtVar .strClass , mtVar .nHeaderBytes , mtVar .bBigEndian , mtVar .hRepSumFunc , mtVar .hChunkLengthFunc );
532+ tfData = mtVar .fRealFactor .* tfData ;
533+ end
534+
535+ % - Recast data, if required, to take into account scaling which
536+ % can occur in another class
537+ if (mtVar .bMustCast )
538+ tfData = cast(tfData , mtVar .strClass );
452539 end
453540
454541 % - Permute dimensions
@@ -463,15 +550,13 @@ function delete(mtVar)
463550 cnSize = num2cell(size(tfData ));
464551 tfData = reshape(tfData , cnSize{1 : nNumDims - 1 }, []);
465552 end
466-
467- % - Cast data, if required
468- if (mtVar .bMustCast )
469- tfData = cast(tfData , mtVar .strClass );
470- end
471553 end
472554
473555 % subsasgn - METHOD Overloaded subsasgn
474- function [mtVar ] = subsasgn(mtVar , subs , tfData )
556+ function [mtVar ] = subsasgn(mtVar , S , tfData )
557+ % - Test for valid subscripts
558+ cellfun(@isvalidsubscript , S .subs );
559+
475560 % - Test read-only status if tensor
476561 if (mtVar .bReadOnly )
477562 error(' MappedTensor:ReadProtect' , ' *** MappedTensor: Attempted write to a read-only tensor.' );
@@ -487,20 +572,26 @@ function delete(mtVar)
487572
488573 % - Cast data, if required
489574 if (mtVar .bMustCast )
490- tfData = cast(tfData , mtVar .strStorageClass );
575+ if (mtVar .bIsComplex )
576+ tfData = complex(cast(real(tfData ) ./ mtVar .fRealFactor , mtVar .strStorageClass ), ...
577+ cast(imag(tfData ) ./ mtVar .fComplexFactor , mtVar .strStorageClass ));
578+
579+ else
580+ tfData = cast(tfData ./ mtVar .fRealFactor , mtVar .strStorageClass );
581+ end
491582 end
492583
493584 % - Permute input data
494585 tfData = ipermute(tfData , mtVar .vnDimensionOrder );
495586
496587 if (~isreal(tfData )) || (~isreal(mtVar ))
497588 % - Assign to both real and complex parts
498- mt_write_data(mtVar .hShimFunc , mtVar .hRealContent , subs , mtVar .vnOriginalSize , mtVar .strClass , mtVar .nHeaderBytes , real(tfData ) ./ mtVar . fRealFactor , mtVar .bBigEndian , mtVar .hRepSumFunc , mtVar .hChunkLengthFunc );
499- mt_write_data(mtVar .hShimFunc , mtVar .hCmplxContent , subs , mtVar .vnOriginalSize , mtVar .strClass , mtVar .nHeaderBytes , imag(tfData ) ./ mtVar . fComplexFactor , mtVar .bBigEndian , mtVar .hRepSumFunc , mtVar .hChunkLengthFunc );
589+ mt_write_data(mtVar .hShimFunc , mtVar .hRealContent , S , mtVar .vnOriginalSize , mtVar .strStorageClass , mtVar .nHeaderBytes , real(tfData ), mtVar .bBigEndian , mtVar .hRepSumFunc , mtVar .hChunkLengthFunc );
590+ mt_write_data(mtVar .hShimFunc , mtVar .hCmplxContent , S , mtVar .vnOriginalSize , mtVar .strStorageClass , mtVar .nHeaderBytes , imag(tfData ), mtVar .bBigEndian , mtVar .hRepSumFunc , mtVar .hChunkLengthFunc );
500591
501592 else
502593 % - Assign only real part
503- mt_write_data(mtVar .hShimFunc , mtVar .hRealContent , subs , mtVar .vnOriginalSize , mtVar .strClass , mtVar .nHeaderBytes , tfData ./ mtVar . fRealFactor , mtVar .bBigEndian , mtVar .hRepSumFunc , mtVar .hChunkLengthFunc );
594+ mt_write_data(mtVar .hShimFunc , mtVar .hRealContent , S , mtVar .vnOriginalSize , mtVar .strStorageClass , mtVar .nHeaderBytes , tfData , mtVar .bBigEndian , mtVar .hRepSumFunc , mtVar .hChunkLengthFunc );
504595 end
505596 end
506597
@@ -514,7 +605,7 @@ function delete(mtVar)
514605 end
515606 end
516607
517- %% Overloaded methods (size, numel, permute, ipermute, ctranspose, transpose, isreal)
608+ %% Overloaded methods (size, numel, ndims, length, permute, ipermute, ctranspose, transpose, isreal)
518609 % size - METHOD Overloaded size function
519610 function [varargout ] = size(mtVar , vnDimensions )
520611 % - Get original tensor size, and extend dimensions if necessary
@@ -583,6 +674,11 @@ function delete(mtVar)
583674 nNumElem = mtVar .nNumElements ;
584675 end
585676
677+ % length - METHOD Overloaded length function
678+ function [nLength ] = length(mtVar )
679+ nLength = max(size(mtVar ));
680+ end
681+
586682 % permute - METHOD Overloaded permute function
587683 function [mtVar ] = permute(mtVar , vnNewOrder )
588684 vnCurrentOrder = mtVar .vnDimensionOrder ;
@@ -628,7 +724,7 @@ function delete(mtVar)
628724
629725 % isnumeric - METHOD Overloaded isnumeric function
630726 function [bIsNumeric ] = isnumeric(mtVar )
631- bIsNumeric = ~islogical(mtVar );
727+ bIsNumeric = ~islogical(mtVar ) && ~ischar( mtVar ) ;
632728 end
633729
634730 % isscalar - METHOD Overloaded isscalar function
@@ -647,8 +743,25 @@ function delete(mtVar)
647743 end
648744
649745 % isnan - METHOD Overloaded isnan function
650- function [bIsNan ] = isnan(mtVar ) % #ok<MANU>
651- bIsNan = false ;
746+ function [tbIsNan ] = isnan(mtVar )
747+ tbIsNan = reshape(isnan(mtVar(: )), size(mtVar ));
748+ end
749+
750+ % isfloat - METHOD Overloaded isfloat function
751+ function [bIsFloat ] = isfloat(mtVar )
752+ bIsFloat = isequal(mtVar .strClass , ' single' ) || isequal(mtVar .strClass , ' double' );
753+ end
754+
755+ % isinteger - METHOD Overloaded isinteger function
756+ function [bIsInteger ] = isinteger(mtVar )
757+ bIsInteger = ~isfloat(mtVar ) & ~islogical(mtVar ) & ~ischar(mtVar );
758+ end
759+
760+ % strfind - METHOD Overloaded strfind function
761+ function [nLoc ] = strfind(mtVar , varargin ) % #ok<INUSD>
762+ warning(' MappedTensor:Unsupported' , ...
763+ ' --- MappedTensor/strfind: Warning: strfind is not supported.' );
764+ nLoc = [];
652765 end
653766
654767 %% Overloaded methods (uminus, uplus, times, mtimes, ldivide, rdivide, mldivide, mrdivide)
@@ -694,7 +807,7 @@ function delete(mtVar)
694807 mtVar.fRealFactor = mtVar .fRealFactor .* fScalar ;
695808
696809 if (mtVar .bIsComplex )
697- mtVar.fCmplxFactor = mtVar .fCmplxFactor .* fScalar ;
810+ mtVar.fComplexFactor = mtVar .fComplexFactor .* fScalar ;
698811 end
699812 end
700813
@@ -1364,12 +1477,42 @@ function make_complex(mtVar)
13641477 tfData = cast(mtVar , ' double' );
13651478 end
13661479
1367- function tfData = cast(mtVar , strClass )
1368- warning(' MappedTensor:WholeTensor' , ...
1369- ' --- MappedTensor: Warning: This command will allocate memory for the entire tensor!' );
1370-
1371- sSubs = substruct(' ()' , repmat({' :' }, numel(size(mtVar )), 1 ));
1372- tfData = builtin(' cast' , subsref(mtVar , sSubs ), strClass );
1480+ % cast - Overloaded "cast" method.
1481+ %
1482+ % Usage: tfData = cast(mtVar, strClass <, bForce>)
1483+ %
1484+ % See the documentation for the built-in "cast" function.
1485+ %
1486+ % The optional parameter 'bForce'
1487+ function tfData = cast(mtVar , strClass , bForce )
1488+ % - Should we really cast the entire tensor?
1489+ if (~exist(' bForce' , ' var' ) || isempty(bForce ))
1490+ bForce = false ;
1491+ end
1492+
1493+ % - Validate the class
1494+ try
1495+ sSubs = substruct(' ()' , {1 });
1496+ cast(subsref(mtVar , sSubs ), strClass );
1497+ catch
1498+ error(' MappedTensor:cast:UnsupportedCLass' , ...
1499+ ' *** MappedTensor: Error: Unsupported data type for conversion: ''%s'' ' , strClass );
1500+ end
1501+
1502+ % - Cast the object
1503+ if (bForce )
1504+ warning(' MappedTensor:WholeTensor' , ...
1505+ ' --- MappedTensor: Warning: This command will allocate memory for the entire tensor!' );
1506+
1507+ sSubs = substruct(' ()' , repmat({' :' }, numel(size(mtVar )), 1 ));
1508+ tfData = builtin(' cast' , subsref(mtVar , sSubs ), strClass );
1509+
1510+ else
1511+ % - Do a transparent cast
1512+ tfData = mtVar ;
1513+ mtVar.bMustCast = true ;
1514+ mtVar.strClass = strClass ;
1515+ end
13731516 end
13741517 end
13751518end
0 commit comments