In addition to the answers given (ArrayFlatten
), there may be cases when the SparseArray
-s need to be accumulated one-by-one, and keeping them all in memory at the same time may be too expensive. I've written a generic function to gradually accumulate SparseArray
-s. Here is the code:
This is the low-level API for SparseArray construction / deconstruction, explained in details here (where the code slightly differs):
ClearAll[spart, getIC, getJR, getSparseData, getDefaultElement,
makeSparseArray];
HoldPattern[spart[SparseArray[s___], p_]] := {s}[[p]];
getIC[s_SparseArray] := spart[s, 4][[2, 1]];
getJR[s_SparseArray] := spart[s, 4][[2, 2]];
getSparseData[s_SparseArray] := spart[s, 4][[3]];
getDefaultElement[s_SparseArray] := spart[s, 3];
makeSparseArray[dims_List, jc_List, ir_List, data_List, defElem_: 0] :=
SparseArray @@ {Automatic, dims, defElem, {1, {jc, ir}, data}};
This is the function itself. It accepts the code which is supposed to generate the next piece, wrapped in Hold
.
Clear[accumulateSparseArray];
accumulateSparseArray[Hold[getDataChunkCode_]] :=
Module[{start, ic, jr, sparseData, dims, dataChunk},
start = getDataChunkCode;
ic = getIC[start];
jr = getJR[start];
sparseData = getSparseData[start];
dims = Dimensions[start];
While[True,
dataChunk = getDataChunkCode;
If[dataChunk === {}, Break[]];
ic = Join[ic, Rest@getIC[dataChunk] + Last@ic];
jr = Join[jr, getJR[dataChunk]];
sparseData = Join[sparseData, getSparseData[dataChunk]];
dims[[1]] += First[Dimensions[dataChunk]];
];
makeSparseArray[dims, ic, jr, sparseData]];
Based on this function, it is not difficult to write a function that will do what you need:
Clear[sparseArrayFlatten]
sparseArrayFlatten[m_?(MatrixQ[#, MatchQ[#, _SparseArray] &] &)] :=
Module[{joinRow, code},
joinRow[row_List] :=
Module[{i = 1},
With[{l = Append[row, {{}}]}, code := l[[i++]]];
accumulateSparseArray[Hold[Transpose[code]]]
];
joinRow[joinRow /@ m]]
Now:
In[109]:=
sparseArrayFlatten[{{sa11,sa12},{sa21,sa22}}]==ArrayFlatten[{{sa11,sa12},{sa21,sa22}}]
Out[109]= True
Note that my solution will only pay off if you do have a situation with a huge number of SparseArray
-s, which you would like to process lazily (or, which are created one by one). One of situations when this happens is during the data transfer, as explained in my post here. I was also able to significantly improve efficiency by using this technique for efficient initialization of a certain sparse array, in my answer to this question. I consider this to be a rather general technique.