5
$\begingroup$

I have a given SparseArray, let's call it A.

I want to construct another SparseArray, let's call it B, which has the same "shape" as A. Right now, I construct B by applying a certain defining function on the positions of the elements of A. So something like

B = SparseArray[ Map[ some_function[], A["ExplicitPositions"] ] ]

It works OK by it turns out to be quite slow for large SparseArray...

Is there another more efficient method maybe ?

Thanks in advance.

$\endgroup$

2 Answers 2

5
$\begingroup$

What makes building a SparseArray from triples {i,j,a} (explicit positions and explicit values) so expensive is the sorting of the indices. But once the matrix is constructed, then the indices are sorted. You can exploit this by using an undocumented constructor that allows to initialize a matrix only by the "RowPointers", "ColumnInidces" and , "ExplicitValues". See this example, where I use a Kirchoff matrix of a random matrix as initial sparse matrix.

A = KirchhoffMatrix[RandomGraph[{1000, 2000}]];

B = With[{
    data = {Automatic, A["Dimensions"], A["Background"],
      {1, {A["RowPointers"], A["ColumnIndices"]}, A["ExplicitValues"]}}
    },
   SparseArray @@ data
   ];

A == B

True

So in your application you might want to try

B = With[{
    data = {Automatic, A["Dimensions"], A["Background"],
      {1, {A["RowPointers"], A["ColumnIndices"]}, 
       Map[f, A["ExplicitPositions"]]}}
    },
   SparseArray @@ data
   ];
$\endgroup$
2
  • $\begingroup$ Thank you very much for your answer. In my case, all of the time was spend in the function defining the elements so there was only a marginal gain of time constructing the SparseArray from triples {i,j,a} or from {RowPointers,ColumnIndices}. But this discussion was nevertheless very useful for me :) $\endgroup$
    – coussin
    Dec 19, 2023 at 11:10
  • $\begingroup$ You're welcome! $\endgroup$ Dec 19, 2023 at 12:01
3
$\begingroup$

Is this even necessary to improve? Let's define a sparse array $A$,

A = SparseArray[{3, 2} -> 6, {10, 20}];

which has rules

ArrayRules[A]
(*    {{3, 2} -> 6, {_, _} -> 0}    *)

Now define a function that we want to map on the elements of $A$, defining it so that it logs every call:

f[x_] := Echo[g[x]]

Map it onto the elements of $A$:

B = Map[f, A, {2}];

(*    g[6]    *)
(*    g[0]    *)

Note that $f$ was only called twice. The result is

ArrayRules[B]
(*    {{3, 2} -> g[6], {_, _} -> g[0]}    *)

There's no indication that "too much work" was done in constructing $B$.

$\endgroup$
3
  • $\begingroup$ Yeah, when I say "it's quite slow" I guess it's just the time to call the defining function times the length of A["ExplicitPositions"]... I guess I was expecting some kind of internal vectorization by using another method which do not map[] blindly over the list of ExplicitPositions... $\endgroup$
    – coussin
    Dec 18, 2023 at 11:42
  • $\begingroup$ Can you be more specific about the kind of magic you expect? $\endgroup$
    – Roman
    Dec 18, 2023 at 12:19
  • $\begingroup$ Huh, what I rote this morning was totally unreadable. Must have been due to jetlag... So here the correct version: Roman, I think the issue here is that user coussin wants to compute new "ExplicitValues" from "ExplicitPositions". In your example, the new "ExplicitValues" are just a function of the original "ExplicitValues", so the function can be simply mapped over the whole SparseArray. $\endgroup$ Dec 18, 2023 at 21:41

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.