Skip to content

Commit dacb8c6

Browse files
committed
Merge commit 'ac82dbc298fe0ed0d64561d7af7bc41d8ac4cc0a'
2 parents 90c78e7 + 72e7d0c commit dacb8c6

File tree

1 file changed

+171
-28
lines changed

1 file changed

+171
-28
lines changed

MappedTensor.m

Lines changed: 171 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
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
@@ -47,6 +49,13 @@
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);
@@ -69,6 +78,13 @@
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.
@@ -149,7 +165,7 @@
149165
% fixes.
150166

151167
classdef 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?
@@ -215,6 +231,52 @@
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', ...
@@ -261,7 +323,7 @@
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.');
@@ -305,6 +367,11 @@
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
13751518
end

0 commit comments

Comments
 (0)