Skip to content

Commit 90c78e7

Browse files
committed
Merge commit 'ca6d4f1175cd0b051472ef9f47b4fca06badf0ed'
2 parents 83cf420 + b181f4a commit 90c78e7

File tree

6 files changed

+172
-49
lines changed

6 files changed

+172
-49
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.mexmaci64

MappedTensor.m

Lines changed: 170 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@
7575
%
7676
% Dot referencing ('.') is not supported.
7777
%
78-
% sum(mtVar <, nDimension>) is supported, to avoid importing the entire tensor
79-
% into memory.
78+
% sum(mtVar <, nDimension, strOutType>) is supported, to avoid importing
79+
% the entire tensor into memory.
8080
%
8181
% Convenience functions:
8282
% SliceFunction: Execute a function on the entire tensor, by slicing it along
@@ -144,6 +144,9 @@
144144

145145
% Author: Dylan Muir <[email protected]>
146146
% Created: 19th November, 2010
147+
%
148+
% Thanks to @marcsous (https://github.com/marcsous) for bug reports and
149+
% fixes.
147150

148151
classdef MappedTensor < handle
149152
properties (SetAccess = private, GetAccess = private)
@@ -175,6 +178,13 @@
175178
methods
176179
%% MappedTensor - CONSTRUCTOR
177180
function [mtVar] = MappedTensor(varargin)
181+
182+
% - Get a handle to the appropriate shim function (should be done
183+
% before any errors are thrown)
184+
[mtVar.hShimFunc, ...
185+
mtVar.hRepSumFunc, ...
186+
mtVar.hChunkLengthFunc] = GetMexFunctionHandles;
187+
178188
% - Filter arguments for properties
179189
vbKeepArg = true(numel(varargin), 1);
180190
nArg = 2;
@@ -208,7 +218,7 @@
208218
otherwise
209219
% - No other properties are supported
210220
error('MappedTensor:InvalidProperty', ...
211-
'*** MappedTensor: ''%s'' is not a valid property. Use the ''Class'' keyword to specify the tensor class.', varargin{nArg});
221+
'*** MappedTensor: ''%s'' is not a valid property.', varargin{nArg});
212222
end
213223
end
214224

@@ -233,32 +243,35 @@
233243
% - Should we map a file on disk, or create a temporary file?
234244
if (ischar(varargin{1}))
235245
% - Open an existing file
236-
vnTensorSize = [varargin{2:end}];
246+
vnTensorSize = double([varargin{2:end}]);
237247
mtVar.strRealFilename = varargin{1};
238248
mtVar.bTemporary = false;
239249

240250
else
241251
% - Create a temporary file
242252
mtVar.bTemporary = true;
243-
vnTensorSize = [varargin{:}];
253+
vnTensorSize = double([varargin{:}]);
244254
end
245255

246256
% - If only one dimension was provided, assume the matrix is
247257
% square (Matlab default semantics)
248258
if (isscalar(vnTensorSize))
249259
vnTensorSize = vnTensorSize * [1 1];
250260
end
251-
261+
262+
% - Validate tensor size argument
263+
try
264+
validateattributes(vnTensorSize, {'numeric'}, {'positive', 'integer'});
265+
catch
266+
error('MappedTensor:Arguments', ...
267+
'*** MappedTensor: Error: ''vnTensorSize'' must be a positive integer vector.');
268+
end
269+
252270
% - Make enough space for a temporary tensor
253271
if (mtVar.bTemporary)
254272
mtVar.strRealFilename = create_temp_file(prod(vnTensorSize) * mtVar.nClassSize + mtVar.nHeaderBytes);
255273
end
256274

257-
% - Get a handle to the appropriate shim function
258-
[mtVar.hShimFunc, ...
259-
mtVar.hRepSumFunc, ...
260-
mtVar.hChunkLengthFunc] = GetMexFunctionHandles;
261-
262275
% - Open the file
263276
if (isempty(mtVar.strMachineFormat))
264277
[mtVar.hRealContent, mtVar.strMachineFormat] = mtVar.hShimFunc('open', mtVar.strRealFilename);
@@ -279,14 +292,19 @@
279292
'*** MappedTensor: Error: only ''ieee-be'' and ''ieee-le'' machine formats are supported.');
280293
end
281294

295+
% - Record the original tensor size, remove trailing unitary dimensions
296+
if (vnTensorSize(end) == 1) && (numel(vnTensorSize) > 2)
297+
nLastNonUnitary = max(2, find(vnTensorSize ~= 1, 1, 'last'));
298+
vnTensorSize = vnTensorSize(1:nLastNonUnitary);
299+
end
300+
301+
mtVar.vnOriginalSize = vnTensorSize;
302+
282303
% - Initialise dimension order
283304
mtVar.vnDimensionOrder = 1:numel(vnTensorSize);
284305

285306
% - Record number of total elements
286307
mtVar.nNumElements = prod(vnTensorSize);
287-
288-
% - Record the original tensor size
289-
mtVar.vnOriginalSize = vnTensorSize;
290308
end
291309

292310
% delete - DESTRUCTOR
@@ -475,7 +493,7 @@ function delete(mtVar)
475493
% - Permute input data
476494
tfData = ipermute(tfData, mtVar.vnDimensionOrder);
477495

478-
if (~isreal(tfData))
496+
if (~isreal(tfData)) || (~isreal(mtVar))
479497
% - Assign to both real and complex parts
480498
mt_write_data(mtVar.hShimFunc, mtVar.hRealContent, subs, mtVar.vnOriginalSize, mtVar.strClass, mtVar.nHeaderBytes, real(tfData) ./ mtVar.fRealFactor, mtVar.bBigEndian, mtVar.hRepSumFunc, mtVar.hChunkLengthFunc);
481499
mt_write_data(mtVar.hShimFunc, mtVar.hCmplxContent, subs, mtVar.vnOriginalSize, mtVar.strClass, mtVar.nHeaderBytes, imag(tfData) ./ mtVar.fComplexFactor, mtVar.bBigEndian, mtVar.hRepSumFunc, mtVar.hChunkLengthFunc);
@@ -541,6 +559,18 @@ function delete(mtVar)
541559
end
542560
end
543561

562+
% ndims - METHOD Overloaded ndims function
563+
function [nDim] = ndims(mtVar, varargin)
564+
% - If varargin contains anything, a cell reference "{}" was attempted
565+
if (~isempty(varargin))
566+
error('MappedTensor:cellRefFromNonCell', ...
567+
'*** MappedTensor: Cell contents reference from non-cell obejct.');
568+
end
569+
570+
% - Return the total number of dimensions in the tensor
571+
nDim = length(size(mtVar));
572+
end
573+
544574
% numel - METHOD Overloaded numel function
545575
function [nNumElem] = numel(mtVar, varargin)
546576
% - If varargin contains anything, a cell reference "{}" was attempted
@@ -805,34 +835,69 @@ function disp(mtVar)
805835
'*** MappedTensor: Concatenation is not supported for MappedTensor objects.');
806836
end
807837

808-
%% sum - METHOD Overloaded sum function for usage "sum(mtVar <, dim>)"
838+
%% sum - METHOD Overloaded sum function for usage "sum(mtVar <, dim, outtype>)"
809839
function [tFinalSum] = sum(mtVar, varargin)
810840
% - Get tensor size
811841
vnTensorSize = size(mtVar);
812842

813-
if (exist('varargin', 'var') && ~isempty(varargin))
814-
% - Check varargin for string parameters and discard
815-
vbIsString = cellfun(@ischar, varargin);
816-
varargin = varargin(~vbIsString);
817-
818-
% - Too many arguments?
819-
if (numel(varargin) > 1)
820-
error('MappedTensor:sum:InvalidArguments', ...
821-
'*** MappedTensor/sum: Too many arguments were supplied.');
843+
% - By default, sum along first non-singleton dimension
844+
DEF_nDim = find(vnTensorSize > 1, 1, 'first');
845+
846+
% - By default, accumulate in a double tensor
847+
DEF_strReturnClass = 'double';
848+
849+
% - Check arguments and apply defaults
850+
if (nargin > 3)
851+
error('MappedTensor:sum:InvalidArguments', ...
852+
'*** MappedTensor/sum: Too many arguments were supplied.');
853+
854+
elseif (nargin == 3)
855+
856+
elseif (nargin == 2)
857+
if (ischar(varargin{1}))
858+
varargin{2} = varargin{1};
859+
varargin{1} = DEF_nDim;
860+
861+
else
862+
varargin{2} = DEF_strReturnClass;
822863
end
823-
824-
% - Was a dimension specified?
825-
if (~isnumeric(varargin{1}) || numel(varargin{1}) > 1)
826-
error('MappedTensor:sum:InvalidArguments', ...
827-
'*** MappedTensor/sum: ''dim'' must be supplied as a scalar number.');
864+
865+
elseif (nargin == 1)
866+
varargin{1} = DEF_nDim;
867+
varargin{2} = DEF_strReturnClass;
868+
end
869+
870+
% - Was a valid dimension specified?
871+
try
872+
validateattributes(varargin{1}, {'numeric'}, {'positive', 'integer', 'scalar'});
873+
catch
874+
error('MappedTensor:sum:InvalidArguments', ...
875+
'*** MappedTensor/sum: ''dim'' must be supplied as a positive scalar number.');
876+
end
877+
nDim = varargin{1};
878+
879+
% - Was a valid output argument type specified?
880+
try
881+
strReturnClass = validatestring(lower(varargin{2}), {'native', 'double', 'default'});
882+
catch
883+
error('MappedTensor:sum:InvalidArguments', ...
884+
'*** MappedTensor/sum: ''outtype'' must be one of {''double'', ''native'', ''default''}.');
885+
end
886+
887+
% - Get the class for the summation matrix
888+
if (strcmp(strReturnClass, 'native'))
889+
% - Logicals are always summed in a double tensor
890+
if (islogical(mtVar))
891+
strOutputClass = 'double';
892+
else
893+
strOutputClass = mtVar.strClass;
828894
end
895+
896+
elseif (strcmp(strReturnClass, 'default'))
897+
strOutputClass = DEF_strReturnClass;
829898

830-
% - Record dimension to sum along
831-
nDim = varargin{1};
832-
833-
else
834-
% - By default, sum along first non-singleton dimension
835-
nDim = find(vnTensorSize > 1, 1, 'first');
899+
else %if (strcmp(strReturnClass, 'double'))
900+
strOutputClass = strReturnClass;
836901
end
837902

838903
% -- Sum in chunks to avoid allocating full tensor
@@ -857,7 +922,7 @@ function disp(mtVar)
857922
end
858923

859924
% -- Perform sum by taking dimensions in turn
860-
tFinalSum = zeros(vnSumSize);
925+
tFinalSum = zeros(vnSumSize, strOutputClass);
861926

862927
% - Construct referencing structures
863928
sSourceRef = substruct('()', ':');
@@ -877,7 +942,7 @@ function disp(mtVar)
877942
% - Call subsasgn, subsref and sum to process data
878943
sSourceRef.subs = cellTheseSourceIndices;
879944
sDestRef.subs = cellTheseDestIndices;
880-
tFinalSum = subsasgn(tFinalSum, sDestRef, subsref(tFinalSum, sDestRef) + sum(subsref(mtVar, sSourceRef), nDim));
945+
tFinalSum = subsasgn(tFinalSum, sDestRef, subsref(tFinalSum, sDestRef) + sum(subsref(mtVar, sSourceRef), nDim, strReturnClass));
881946

882947
% - Increment first non-max index
883948
nIncrementDim = find(vnSplitIndices <= vnNumDivisions, 1, 'first');
@@ -1241,7 +1306,7 @@ function make_complex(mtVar)
12411306
mtVar.strCmplxFilename = create_temp_file(mtVar.nNumElements * mtVar.nClassSize + mtVar.nHeaderBytes);
12421307

12431308
% - open the file
1244-
mtVar.hCmplxContent = mtVar.hShimFunc('open', mtVar.strCmplxFilename);
1309+
mtVar.hCmplxContent = mtVar.hShimFunc('open', mtVar.strCmplxFilename, mtVar.strMachineFormat);
12451310

12461311
% - record that the tensor has a complex part
12471312
mtVar.bIsComplex = true;
@@ -1315,12 +1380,21 @@ function make_complex(mtVar)
13151380
% - Get the name of a temporary file
13161381
strFilename = tempname;
13171382

1318-
% - Create the file
1319-
hFile = fopen(strFilename, 'w+');
1320-
1321-
% - Allocate enough space
1322-
fwrite(hFile, 0, 'uint8', nNumEntries-1);
1323-
fclose(hFile);
1383+
% - Attempt fast allocation on some platforms
1384+
if (ispc)
1385+
[bFailed, ~] = system(sprintf('fsutil file createnew %s %i', strFilename, nNumEntries));
1386+
elseif (ismac || isunix)
1387+
[bFailed, ~] = system(sprintf('fallocate -l %i %s', nNumEntries, strFilename));
1388+
else
1389+
bFailed = true;
1390+
end
1391+
1392+
% - Slow fallback -- use Matlab to write zero data directly
1393+
if (bFailed)
1394+
hFile = fopen(strFilename, 'w+');
1395+
fwrite(hFile, 0, 'uint8', nNumEntries-1);
1396+
fclose(hFile);
1397+
end
13241398
end
13251399

13261400
function [nBytes, strStorageClass] = ClassSize(strClass)
@@ -1587,7 +1661,7 @@ function isvalidsubscript(oRefs)
15871661

15881662
else
15891663
% - Test for normal indexing
1590-
validateattributes(oRefs, {'single', 'double'}, {'integer', 'real', 'positive'});
1664+
validateattributes(oRefs, {'numeric'}, {'integer', 'real', 'positive'});
15911665
end
15921666

15931667
catch
@@ -1720,8 +1794,47 @@ function mt_write_data_chunks(hDataFile, mnFileChunkIndices, vnUniqueDataIndices
17201794
end
17211795
end
17221796

1797+
% mt_read_all - FUNCTION Read the entire stack
1798+
function [tData] = mt_read_all(hDataFile, vnTensorSize, strClass, nHeaderBytes, ~)
1799+
% - Allocate data
1800+
[~, strStorageClass] = ClassSize(strClass);
1801+
% tData = zeros(vnTensorSize, strStorageClass);
1802+
1803+
% - Seek file to beginning of data
1804+
fseek(hDataFile, nHeaderBytes, 'bof');
1805+
1806+
% - Normal forward read
1807+
tData = fread(hDataFile, prod(vnTensorSize), [strStorageClass '=>' strClass], 0);
1808+
end
1809+
1810+
% mt_write_all - FUNCTION Write the entire stack
1811+
function mt_write_all(hDataFile, vnTensorSize, strClass, nHeaderBytes, tData, ~)
1812+
1813+
% - Do we need to replicate the data?
1814+
if (isscalar(tData) && prod(vnTensorSize) > 1)
1815+
tData = repmat(tData, prod(vnTensorSize), 1);
1816+
1817+
elseif (numel(tData) ~= prod(vnTensorSize))
1818+
% - The was a mismatch in the sizes of the left and right sides
1819+
error('MappedTensor:index_assign_element_count_mismatch', ...
1820+
'*** MappedTensor: In an assignment A(I) = B, the number of elements in B and I must be the same.');
1821+
end
1822+
1823+
% - Get storage class
1824+
[~, strStorageClass] = ClassSize(strClass);
1825+
1826+
% - Seek file to beginning of data
1827+
fseek(hDataFile, nHeaderBytes, 'bof');
1828+
1829+
% - Normal forward write of data
1830+
fwrite(hDataFile, tData, strStorageClass, 0);
1831+
end
1832+
17231833
% ConvertColonCheckLims - FUNCTION Convert colon referencing to subscript indices; check index limits
17241834
function [vnLinearIndices, vnDataSize] = ConvertColonsCheckLims(cRefs, vnLims, hRepSumFunc)
1835+
% - Fill trailing referenced dimension limits
1836+
vnLims(end+1:numel(cRefs)) = 1;
1837+
17251838
% - Handle linear indexing
17261839
if (numel(cRefs) == 1)
17271840
vnLims = prod(vnLims);
@@ -1749,8 +1862,8 @@ function mt_write_data_chunks(hDataFile, mnFileChunkIndices, vnUniqueDataIndices
17491862
'*** MappedTensor: Index exceeds matrix dimensions.');
17501863

17511864
else
1752-
% - This dimension was ok
1753-
cCheckedRefs{nRefDim} = cRefs{nRefDim};
1865+
% - This dimension was ok, convert to double
1866+
cCheckedRefs{nRefDim} = double(cRefs{nRefDim});
17541867
end
17551868
end
17561869

@@ -1768,6 +1881,9 @@ function mt_write_data_chunks(hDataFile, mnFileChunkIndices, vnUniqueDataIndices
17681881
% - Find colon references
17691882
vbIsColon = cellfun(@iscolon, cRefs);
17701883

1884+
% - Fill trailing referenced dimension limits
1885+
vnLims(end+1:numel(cRefs)) = 1;
1886+
17711887
% - Catch "reference whole stack" condition
17721888
if (all(vbIsColon))
17731889
vnLinearIndices = 1:prod(vnLims);
@@ -1895,6 +2011,12 @@ function mt_write_data_chunks(hDataFile, mnFileChunkIndices, vnUniqueDataIndices
18952011

18962012
case 'write_chunks'
18972013
mt_write_data_chunks(varargin{1:7});
2014+
2015+
case 'read_all'
2016+
varargout{1} = mt_read_all(varargin{1:5});
2017+
2018+
case 'write_all'
2019+
varargout{1} = mt_write_all(varargin{1:6});
18982020
end
18992021
end
19002022

-10.5 KB
Binary file not shown.
-10.5 KB
Binary file not shown.

private/mapped_tensor_shim.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
#define bool uint8_t
3232
#define true 1
3333
#define false 0
34-
#define UINT64_C(c) c ## ULL
34+
#define UINT64_C(c) c ## i64
3535
#else
3636
#define FILE_FORCE_BINARY
3737
#endif

private/mapped_tensor_shim.mexw64

-24.5 KB
Binary file not shown.

0 commit comments

Comments
 (0)