9
$\begingroup$

I am interested in replacing all elements in a diagonal of a sparse matrix with zero. Currently, I do it in the following manner:

spA = SparseArray[{{1, 1} -> 1, {2, 2} -> 2, {3, 3} -> 3, {1, 3} -> 4}]
withOutDiagonalELements = DeleteCases[ArrayRules[spA], {a_, a_} -> _];
spAwd =  SparseArray@Append[withOutDiagonalELements, {_, _} -> 0.0];

Any suggestions on how to do it more efficiently?

$\endgroup$
5
$\begingroup$
removeDiagonal = 
  # SparseArray[SparseArray`SparseArrayRemoveDiagonal[#]["NonzeroPositions"] -> 1, 
   Dimensions[#]] &;

removeDiagonal[spA]

SparseArray[<4>,{3,3}]

removeDiagonal[spA] // MatrixForm // TeXForm

$\left( \begin{array}{ccc} 0 & 0 & 4 \\ 0 & 0 & 0 \\ 0 & 0 & 0 \\ \end{array} \right)$

removeDiagonal[spA]["NonzeroPositions"]

{{1,3}}

Some timings:

f1 = removeDiagonal;
f2 = SparseArray[# - DiagonalMatrix[Diagonal[#, 0], 0,  
      Dimensions[#]]] &; (* Henrik Shumacher *)
f3 = With[{zeros = 1 - IdentityMatrix[Dimensions@#, SparseArray]}, #*
     zeros] &; (* Mr. Wizard *)
f4 = ReplacePart[#, {i_, i_} :> 0] &; (* tomd *)

m1 = SparseArray[Tuples[RandomSample[Range[101], 100], {2}] -> 1, {101, 101}];
m2 = SparseArray[Tuples[RandomSample[Range[100000], 100], {2}] -> 1, {100000, 100000}];

m = m1;
t11 = First[RepeatedTiming[r11 = f1 @ m ;]];
t21 = First[RepeatedTiming[r21 = f2 @ m;]];
t31 = First[RepeatedTiming[ r31 = f3 @ m;]];
t41 = First[RepeatedTiming[ r41 = f4 @ m;]];
r11 == r21 == r31 == r41

True

m = m2;
t12 = First[RepeatedTiming[r12 = f1 @ m ;]];
t22 = First[RepeatedTiming[r22 = f2 @ m;]];
t32 = First[RepeatedTiming[ r32 = f3 @ m;]];
t42 = "n/a"; (* b/c computation exceeded the limitation of free plan on  Wolfram Cloud *)
r12 == r22 == r32

True

{{"dimensions", "non-zero elements", "f1", "f2", "f3", "f4"},
 {{101, 101}, 100, t11, t21, t31, t41}, 
 {{100000, 100000}, 100, t12, t22, t32, t42}} // Grid[#, Dividers -> All] &  // TeXForm

$\small\begin{array}{|c|c|c|c|c|c|} \hline \text{dimensions} & \text{non-zero elements} & \text{f1} & \text{f2} & \text{f3} & \text{f4} \\ \hline \{101,101\} & 100 & 0.000322 & 0.000279 & 0.000131 & 0.00487 \\ \hline \{100000,100000\} & 100 & 0.0017 & 0.0053 & 0.004 & \text{n/a} \\ \hline \end{array}$

$\endgroup$
  • $\begingroup$ I don't believe I've seen SparseArrayRemoveDiagonal before. Have you been using it or did you discover it only now? $\endgroup$ – Mr.Wizard Jul 21 '18 at 2:24
  • $\begingroup$ @Mr.Wizard, I bumped into it some time ago. This is probably the second time I have used it. $\endgroup$ – kglr Jul 21 '18 at 2:59
8
$\begingroup$

You can multiply a binary matrix by your Sparse Array, and that matrix may itself be a SparseArray.

spA = SparseArray[{{1, 1} -> 1, {2, 2} -> 2, {3, 3} -> 3, {1, 3} -> 4}];

zeros = 1 - IdentityMatrix[Dimensions @ spA, SparseArray];

spA*zeros // Grid

$\begin{array}{ccc} 0 & 0 & 4 \\ 0 & 0 & 0 \\ 0 & 0 & 0 \\ \end{array}$

See also Band and DiagonalMatrix for other tools to construct the zeros array.

$\endgroup$
5
$\begingroup$

Just subtract the diagonal matrix:

SparseArray[spA - DiagonalMatrix[Diagonal[spA]]]

As Mr. Wizard pointed out, if spA is not a square matrix, one can use

k = 0;
SparseArray[spA - DiagonalMatrix[Diagonal[spA, k], k, Dimensions[spA]]]

Here, k stands for the number of the diagonal you what to delete. k = 0 stands for the main diagonal, k = 1 for the first diagonal above the main diagonal, k = -1 for the first diagonal below the main diagonal etc.

$\endgroup$
  • $\begingroup$ I think the SparseArray wrapper is superfluous. $\endgroup$ – Carl Woll Jul 20 '18 at 15:36
  • 2
    $\begingroup$ Yes and no. Wrapping with SparseArray removes the diagonal entries from the list of `"NonzeroPositions". There are both reasons for and against this... $\endgroup$ – Henrik Schumacher Jul 20 '18 at 16:11
  • 1
    $\begingroup$ @Mr.Wizard You mean nonsquare matrices? Good point! Fixed it. $\endgroup$ – Henrik Schumacher Jul 20 '18 at 20:48
  • $\begingroup$ Sorry, yes of course I meant non-square. You already had my +1. $\endgroup$ – Mr.Wizard Jul 20 '18 at 22:33
2
$\begingroup$
ReplacePart[spA, {i_, i_} :> 0]

and

Diagonal[ReplacePart[spA, {i_, i_} :> 0]] // Normal

{0, 0, 0}

In general:

ReplacePart[spA, {i_, i_} :> {a, b, c}[[i]]]
$\endgroup$
  • $\begingroup$ Unfortunately this can be very slow. $\endgroup$ – Mr.Wizard Jul 20 '18 at 18:52
2
$\begingroup$

Two methods to add a list to the diagonal of a sparse matrix are given as below. The performance varies greatly.

n = 100000;
a = RandomReal[1, n];
Ksp = SparseArray[{{i_, i_} :> a[[i]]}, Length[a]];
Ksp2 = Ksp;

Method 1

t1 = Timing[Do[Ksp[[i, i]] += a[[i]], {i, Length[a]}]][[1]]

33.7742

Method 2

t2 = Timing[Ksp2 += SparseArray[{{i_, i_} :> a[[i]]}, Length[a]]][[1]]

0.0312002

Compare:

t1/t2

1082.5

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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