10
$\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 Answers 5

6
$\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$
2
  • $\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
    Commented Jul 21, 2018 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
    Commented Jul 21, 2018 at 2:59
9
$\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$
7
$\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$
4
  • $\begingroup$ I think the SparseArray wrapper is superfluous. $\endgroup$
    – Carl Woll
    Commented Jul 20, 2018 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$ Commented Jul 20, 2018 at 16:11
  • 1
    $\begingroup$ @Mr.Wizard You mean nonsquare matrices? Good point! Fixed it. $\endgroup$ Commented Jul 20, 2018 at 20:48
  • $\begingroup$ Sorry, yes of course I meant non-square. You already had my +1. $\endgroup$
    – Mr.Wizard
    Commented Jul 20, 2018 at 22:33
3
$\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$
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$
1
  • $\begingroup$ Unfortunately this can be very slow. $\endgroup$
    – Mr.Wizard
    Commented Jul 20, 2018 at 18:52

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.