2
$\begingroup$

Let's say I have a "small" SparseArray of any size, in the example $2\times2$

MatrixForm[
    m2 = SparseArray[{{i_,i_}->1,{1,2}->a},2]
]

enter image description here

I want to expand it to a "Large"m any size larger than the previous, for example, $4\times4$ SparseArray without rewriting it.

let's say

m4test = SparseArray[{{i_,i_}->1,{1,2}->a, {3,4}->b },4]

enter image description here

How do I efficiently (not expanding and contracting) do I transform m2 into m4 by increasing the size and adding new terms?

Due diligence

I didn't find a similar question on the site, and ChatGPT doesn't seem to understand how to program for general cases.

I have tried this,

extendSparseArray[sa_, rules_] := SparseArray[
    Join[rules, ArrayRules[sa]]
    , Max[Join[Dimensions[sa],Flatten@rules[[All,1]] ] ]
]

MatrixForm[
    extendSparseArray[m2, {{3,4}->b}]
]

enter image description here

Which does part of the job. Notice it conserves the default value, because

ArrayRules[SparseArray[{{i_,i_}->1}, 4, x]]
(* {{1, 1} -> 1, {2, 2} -> 1, {3, 3} -> 1, {4, 4} -> 1, {_, _} -> x} *)

which includes the rule {_, _} -> x. Unfortunately, it misses the other general rule {i_,i_}->1.

Another problem is that it doesn't work with further patterns. This fails, because Max[4, i_] doesn't make sense.

extendSparseArray[m2, {{i_,i_}->1,  {3,4}->b}]

Furthermore, it all seems too cumbersome, and I'm hoping I'm missing something more fundamental.

Perhaps it's not reasonable to expect to recover the pattern rule, in that case at least the default value (in this case zero, but not necessarily) should be conserved.

What are simple and idiomatic ways to achieve this and which one performs the best?

The examples are meant to be minimal to facilitate the discussion, I will use this with larger and more complex SparseArray expressions.


It seems from the comments that I'm not explaining myself clearly.

SparseArray[SparseArray[{},n],m] with $m>n$ does part of the job, it manipulates a small SparseArray to make it larger, but the issue is how do I add the rules that define some of the new terms in a short robust and idiomatic form?

$\endgroup$
3
  • 1
    $\begingroup$ ArrayReshape? $\endgroup$
    – Domen
    Commented Aug 15 at 15:51
  • $\begingroup$ @Domen Thanks for your comment, ArrayReshape[m2, {4,4}] does return a SparseArray, but it puts all the items in the first row. and doesn't take arguments to fill the new values i.e {{3,4}->b. How do you propose I use it? $\endgroup$
    – rhermans
    Commented Aug 15 at 15:59
  • $\begingroup$ Can you give us an example that is, say, 3x3 (presumably expanding to 6x6)? And another example where the "rule to be preserved" isn't just the long diagonal? Based on simple example you've given, I can't imagine why you wouldn't use one of the suggestions already given. They are much better than trying to manipulate the sparse array rules directly. So, there must be a reason why those aren't working for you, but without seeing these suggestions "break" I don't really have any idea what to suggest. $\endgroup$
    – lericr
    Commented Aug 16 at 15:02

2 Answers 2

1
$\begingroup$

I think ArrayFlatten is your best friend here.

A = SparseArray[{{i_, i_} -> 1, {1, 2} -> a}, 2];
B = SparseArray[{{i_, i_} -> 1, {1, 2} -> b}, 2];

AB = ArrayFlatten[{{A,0},{0,B}}]

$$ \left( \begin{array}{cccc} 1 & a & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & b \\ 0 & 0 & 0 & 1 \\ \end{array} \right) $$

In this case, ArrayFlatten is even clever enough to replace the two 0s by the according 0-block matrices. This is not always possible, e.g., when you want to stack A and B on top of each other and if you still want to get a $4 \times 4$ matrix. Then you have to give at least one 0-block explicitly:

ArrayFlatten[{{A, SparseArray[{}, {2, 2}]}, {B, 0}}]

Beware that all sparse matrices that show up here should be SparseArrays; otherwise ArrayFlatten will produce a dense matrix. Moreover, all the default values of the SparseArrays have to coincide, too (and 0 is considered different from 0. in this regards); otherwise, a dense matrix is creates.

$\endgroup$
4
  • $\begingroup$ I was expecting something like extendSparseArray[sparseaarray_, rules_] , two arguments, a SparseArray and a list of rules. With your approach if I try to extend with something like {i_,i_-1} -> 2, I will need to put that rule in three sub-matrices. $\endgroup$
    – rhermans
    Commented Aug 16 at 15:04
  • $\begingroup$ Hm. The problem with rules is that this would require to unpack the CRS data from the SparseArray. If the rules don't index into the existing matrix, then this unpacking is not really needed. $\endgroup$ Commented Aug 16 at 15:18
  • $\begingroup$ Do you like SparseArray[ Join[{{i_, i_} -> 1, {3, 4} -> b}, ArrayRules[m2]] ] better? The rules are processed FIFO, so this might be what you are looking for. $\endgroup$ Commented Aug 16 at 15:19
  • $\begingroup$ Note that this way duplicate entries will be merged such that the first occurence is taken. (In particular, no additive assembly.) $\endgroup$ Commented Aug 16 at 18:56
0
$\begingroup$

I'd probably create a helper function and then use the built in matrix-related functions.

specialMatrix[val_] := SparseArray[{{i_, i_} -> 1, {1, 2} -> val}, 2];
BlockDiagonalMatrix[{specialMatrix[a], specialMatrix[b]}]

In "normal" form this gives

{{1, a, 0, 0}, 
 {0, 1, 0, 0}, 
 {0, 0, 1, b}, 
 {0, 0, 0, 1}}

The basic problem with your approach is that, while the SparseArray constructor takes pattern arguments, the SparseArray structure that results uses the concrete rules that the patterns specified. So you can't just take the ArrayRules and expect them to be still patterns.

You might also be able to use DiagonalMatrix and/or IdentityMatrix. DiagonalMatrix lets you specify the values for any particular diagonal, so if your rules can be formulated rigorously (I'm assuming that the simple a and b aren't totally faithful to your actual problem) it might work nicely.

$\endgroup$
7
  • $\begingroup$ Thanks for your answer. Some issues pending on it are the fact that you are expanding the matrix and leaving the "Sparse" representation, your helper function is ad-hoc this particular example, not a general solution, and lastly, the new matrix doesn't inherit the default values of the original SparseArray. $\endgroup$
    – rhermans
    Commented Aug 15 at 16:20
  • $\begingroup$ About SparseArray structure that results uses the concrete rules, that is true, except for the default value, See my edit and the output of ArrayRules does return {_, _} -> x. $\endgroup$
    – rhermans
    Commented Aug 15 at 16:35
  • $\begingroup$ Yeah, I figured my exact solution was too tailored to a toy version of the problem, but I'd think the idea is sound. But if what you think you really need is a helper method to do extension from a starting array, then explain the extension rules more clearly. Given a NxN array the rule for extending it to a MxM array (or an N+k x N+k array, or a 2N x 2N array, or whatever) is <insert rule here>. $\endgroup$
    – lericr
    Commented Aug 15 at 17:18
  • $\begingroup$ You implied that 2x2 was just an example and that you'd actually start with larger matrices, but 2x2 is so special that I don't know how to extend your example to, say, 3x3. Is it full upper triangular? Is it just full in the diagonal adjacent to the main diagonal? Are the entries all the same value? Is there some pattern to them? Or are they just arbitrary, and a wasn't really intended to describe anything at all? $\endgroup$
    – lericr
    Commented Aug 15 at 17:21
  • $\begingroup$ Why can't you just start with an identity matrix and update according to your new rules? Or is your question really about how to manipulate existing sparse array rules in arbitrary ways? $\endgroup$
    – lericr
    Commented Aug 15 at 17:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.