# Replace diagonal elements in sparse matrix

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?

removeDiagonal =
# SparseArray[SparseArraySparseArrayRemoveDiagonal[#]["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}$

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

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.

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.

• I think the SparseArray wrapper is superfluous. – Carl Woll Jul 20 '18 at 15:36
• Yes and no. Wrapping with SparseArray removes the diagonal entries from the list of "NonzeroPositions". There are both reasons for and against this... – Henrik Schumacher Jul 20 '18 at 16:11
• @Mr.Wizard You mean nonsquare matrices? Good point! Fixed it. – Henrik Schumacher Jul 20 '18 at 20:48
• Sorry, yes of course I meant non-square. You already had my +1. – Mr.Wizard Jul 20 '18 at 22:33
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]]]

• Unfortunately this can be very slow. – Mr.Wizard Jul 20 '18 at 18:52

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