From fc0b2859d9b1fa3984464b6680207b2b4c480272 Mon Sep 17 00:00:00 2001 From: Qianqian Fang Date: Sun, 31 May 2020 01:08:54 -0400 Subject: [PATCH] adding support to _ArrayShape_ to record special matrices --- examples/demo_jsonlab_basic.m | 27 ++++++++++++ jdatadecode.m | 82 +++++++++++++++++++++++++++++++++-- jdataencode.m | 72 +++++++++++++++++++++++++++--- 3 files changed, 172 insertions(+), 9 deletions(-) diff --git a/examples/demo_jsonlab_basic.m b/examples/demo_jsonlab_basic.m index 16b84d8..062384f 100644 --- a/examples/demo_jsonlab_basic.m +++ b/examples/demo_jsonlab_basic.m @@ -366,6 +366,33 @@ json2data=loadjson(ans) end +if(exist('bandwidth')) + fprintf(1,'\n%%=================================================\n') + fprintf(1,'%% use _ArrayShape_ \n') + fprintf(1,'%%=================================================\n\n') + + lband=2; + uband=3; + data2json=spdiags(true(8,lband+uband+1),-uband:lband,5,8); + data2json=full(double(data2json)); + data2json(data2json~=0)=find(data2json) + + savejson('',data2json,'usearrayshape',1) + json2data=loadjson(ans,'fullarrayshape',1) + + savejson('',tril(data2json),'usearrayshape',1) + json2data=loadjson(ans,'fullarrayshape',1) + + savejson('',triu(data2json+1i*data2json),'usearrayshape',1) + json2data=loadjson(ans,'fullarrayshape',1) + + savejson('',tril(triu(int8(data2json))),'usearrayshape',1) + json2data=loadjson(ans,'fullarrayshape',1) + + savejson('',data2json(:,1:5)+data2json(:,1:5)','usearrayshape',1) + json2data=loadjson(ans,'fullarrayshape',1) +end + try val=zlibencode('test'); fprintf(1,'\n%%=================================================\n') diff --git a/jdatadecode.m b/jdatadecode.m index cf65279..8509e20 100644 --- a/jdatadecode.m +++ b/jdatadecode.m @@ -33,6 +33,8 @@ % jsondecode(), the prefix is 'x'; this function % attempts to automatically determine the prefix; % for octave, the default value is an empty string ''. +% FullArrayShape: [0|1] if set to 1, converting _ArrayShape_ +% objects to full matrices, otherwise, stay sparse % FormatVersion: [2|float]: set the JSONLab output version; % since v2.0, JSONLab uses JData specification Draft 1 % for output format, it is incompatible with all @@ -61,6 +63,7 @@ elseif(nargin>2) opt=varargin2struct(varargin{:}); end + opt.fullarrayshape=jsonopt('FullArrayShape',0,opt); %% process non-structure inputs if(~isstruct(data)) @@ -128,6 +131,9 @@ error('compression method is not supported'); end else + if(isstruct(data(j).(N_('_ArrayData_'))) && isfield(data(j).(N_('_ArrayData_')),N_('_ArrayType_'))) + data(j).(N_('_ArrayData_'))=jdatadecode(data(j).(N_('_ArrayData_')),varargin{:}); + end if(iscell(data(j).(N_('_ArrayData_')))) data(j).(N_('_ArrayData_'))=cell2mat(cellfun(@(x) double(x(:)),data(j).(N_('_ArrayData_')),'uniformoutput',0)).'; end @@ -142,10 +148,8 @@ ndata=permute(ndata,ndims(ndata):-1:1); end iscpx=0; - if(isfield(data,N_('_ArrayIsComplex_'))) - if(data(j).(N_('_ArrayIsComplex_'))) - iscpx=1; - end + if(isfield(data,N_('_ArrayIsComplex_')) && data(j).(N_('_ArrayIsComplex_')) ) + iscpx=1; end if(isfield(data,N_('_ArrayIsSparse_')) && data(j).(N_('_ArrayIsSparse_'))) if(isfield(data,N_('_ArraySize_'))) @@ -175,6 +179,76 @@ end ndata=sparse(ndata(1,:),ndata(2,:),ndata(3,:)); end + elseif(isfield(data,N_('_ArrayShape_'))) + if(iscpx) + if(size(ndata,1)==2) + dim=size(ndata); + dim(end+1)=1; + arraydata=reshape(complex(ndata(1,:),ndata(2,:)),dim(2:end)).'; + else + error('The first dimension must be 2 for complex-valued arrays'); + end + else + arraydata=data.(N_('_ArrayData_')); + end + shapeid=data.(N_('_ArrayShape_')); + if(isfield(data,N_('_ArrayZipSize_'))) + datasize=data.(N_('_ArrayZipSize_')); + if(iscpx) + datasize=datasize(2:end); + end + else + datasize=size(arraydata); + end + arraysize=data.(N_('_ArraySize_')); + if(ischar(shapeid)) + shapeid={shapeid}; + end + if(strcmp(shapeid{1},'diag')) + ndata=spdiags(arraydata(:),0,arraysize(1),arraysize(2)); + elseif(strcmp(shapeid{1},'upper') || strcmp(shapeid{1},'uppersymm')) + ndata=zeros(arraysize); + ndata(triu(true(size(ndata)))')=arraydata(:); + if(strcmp(shapeid{1},'uppersymm')) + ndata(triu(true(size(ndata))))=arraydata(:); + end + ndata=ndata.'; + elseif(strcmp(shapeid{1},'lower') || strcmp(shapeid{1},'lowersymm')) + ndata=zeros(arraysize); + ndata(tril(true(size(ndata)))')=arraydata(:); + if(strcmp(shapeid{1},'lowersymm')) + ndata(tril(true(size(ndata))))=arraydata(:); + end + ndata=ndata.'; + elseif(strcmp(shapeid{1},'upperband') || strcmp(shapeid{1},'uppersymmband')) + if(length(shapeid)>1 && isvector(arraydata)) + datasize=[shapeid{2}+1, prod(datasize)/(shapeid{2}+1)]; + end + ndata=spdiags(reshape(arraydata,min(arraysize),datasize(1)),-datasize(1)+1:0,arraysize(2),arraysize(1)).'; + if(strcmp(shapeid{1},'uppersymmband')) + diagonal=diag(ndata); + ndata=ndata+ndata.'; + ndata(1:arraysize(1)+1:end)=diagonal; + end + elseif(strcmp(shapeid{1},'lowerband') || strcmp(shapeid{1},'lowersymmband')) + if(length(shapeid)>1 && isvector(arraydata)) + datasize=[shapeid{2}+1, prod(datasize)/(shapeid{2}+1)]; + end + ndata=spdiags(reshape(arraydata,min(arraysize),datasize(1)),0:datasize(1)-1,arraysize(2),arraysize(1)).'; + if(strcmp(shapeid{1},'lowersymmband')) + diagonal=diag(ndata); + ndata=ndata+ndata.'; + ndata(1:arraysize(1)+1:end)=diagonal; + end + elseif(strcmp(shapeid{1},'band')) + if(length(shapeid)>1 && isvector(arraydata)) + datasize=[shapeid{2}+shapeid{3}+1, prod(datasize)/(shapeid{2}+shapeid{3}+1)]; + end + ndata=spdiags(reshape(arraydata,min(arraysize),datasize(1)),shapeid{2}:-1:-shapeid{3},arraysize(1),arraysize(2)); + end + if(opt.fullarrayshape && issparse(ndata)) + ndata=cast(full(ndata),data(j).(N_('_ArrayType_'))); + end elseif(isfield(data,N_('_ArraySize_'))) if(iscpx) ndata=complex(ndata(1,:),ndata(2,:)); diff --git a/jdataencode.m b/jdataencode.m index 980e063..84885a1 100644 --- a/jdataencode.m +++ b/jdataencode.m @@ -32,6 +32,9 @@ % the original data stored in _ArrayData_, and then flaten % _ArrayData_ into a row vector using row-major % order; if set to 0, a 2D _ArrayData_ will be used +% UseArrayShape: [0|1] if set to 1, a matrix will be tested by +% to determine if it is diagonal, triangular, banded or +% toeplitz, and use _ArrayShape_ to encode the matrix % MapAsStruct: [0|1] if set to 1, convert containers.Map into % struct; otherwise, keep it as map % Compression: ['zlib'|'gzip','lzma','lz4','lz4hc'] - use zlib method @@ -73,6 +76,7 @@ opt.mapasstruct=jsonopt('MapAsStruct',0,opt); opt.usearrayzipsize=jsonopt('UseArrayZipSize',1,opt); opt.messagepack=jsonopt('MessagePack',0,opt); +opt.usearrayshape=jsonopt('UseArrayShape',0,opt) && exist('bandwidth'); jdata=obj2jd(data,opt); @@ -156,12 +160,72 @@ %%------------------------------------------------------------------------- function newitem=mat2jd(item,varargin) +N=@(x) N_(x,varargin{:}); + +% no encoding for char arrays or non-sparse real vectors if(isempty(item) || isa(item,'string') || ischar(item) || varargin{1}.nestarray || ... - ((isvector(item) || ndims(item)==2) && isreal(item) && ~issparse(item))) + (isvector(item) && isreal(item) && ~issparse(item))) newitem=item; - if(~(varargin{1}.messagepack && size(item,1)>1)) - return; + return; +% 2d numerical (real/complex/sparse) arrays with _ArrayShape_ encoding enabled +elseif(varargin{1}.usearrayshape && ndims(item)==2 && ~isvector(item)) + newitem=struct(N('_ArrayType_'),class(item),N('_ArraySize_'),size(item)); + newitem.(N('_ArrayIsComplex_'))=~isreal(item); + symmtag=''; + if(isreal(item) && issymmetric(double(item))) + symmtag='symm'; + item=tril(item); + elseif(~isreal(item) && ishermitian(double(item))) + symmtag='herm'; + item=tril(item); + end + [lband,uband]=bandwidth(double(item)); + newitem.(N('_ArrayZipSize_'))=[lband+uband+1, min(size(item,1),size(item,2))]; + if(lband+uband==0) % isdiag + newitem.(N('_ArrayShape_'))='diag'; + newitem.(N('_ArrayData_'))=diag(item).'; + elseif(uband==0 && lband==size(item,1)-1) % lower triangular + newitem.(N('_ArrayShape_'))=['lower' symmtag]; + item=item.'; + newitem.(N('_ArrayData_'))=item(triu(true(size(item)))).'; + elseif(lband==0 && uband==size(item,2)-1) % upper triangular + newitem.(N('_ArrayShape_'))='upper'; + item=item.'; + newitem.(N('_ArrayData_'))=item(tril(true(size(item)))).'; + elseif(lband==0) % upper band + newitem.(N('_ArrayShape_'))={'upperband',uband}; + newitem.(N('_ArrayData_'))=spdiags(item.',-uband:lband).'; + elseif(uband==0) % lower band + newitem.(N('_ArrayShape_'))={sprintf('lower%sband',symmtag),lband}; + newitem.(N('_ArrayData_'))=spdiags(item.',-uband:lband).'; + elseif(uband