25
$\begingroup$

Zeroing the diagonal of a square matrix is an operation I need frequently, but somehow I still haven't managed to find an elegant solution that satisfies all these requirements:

  • works with both numeric and symbolic matrices
  • doesn't get tripped up by Infinity or Indeterminate
  • doesn't unpack packed arrays
  • performs reasonably

So what do you use to zero out your diagonals? Is there any concise implementation for this at all?

Notes: Arithmetic with infinities tends to give indeterminates and DiagonalMatrix strangely doesn't work with inifinities at all (even though it works with symbols). Writing an integer 0 into a packed array of reals unpacks it (or generally, trying to write an un-matching type of integer, real, complex).

$\endgroup$
5
  • 1
    $\begingroup$ Anything wrong with UpperTriangularize[arg, 1] + LowerTriangularize[arg, -1]? $\endgroup$
    – ciao
    Aug 31, 2015 at 7:21
  • 1
    $\begingroup$ @ciao Looks like it might be exactly the thing I need. Can you post it as an answer? $\endgroup$
    – Szabolcs
    Aug 31, 2015 at 7:36
  • $\begingroup$ Done - I was surprised at speed... $\endgroup$
    – ciao
    Aug 31, 2015 at 7:40
  • $\begingroup$ @ciao Thanks! I think this will be very hard to beat, but I'll wait until tomorrow with the accept. And I know the solution looks trivial, but I never thought of this specific one, and all the other (more obvious) ones have problems. $\endgroup$
    – Szabolcs
    Aug 31, 2015 at 7:53
  • $\begingroup$ Well, It seem I have beat that. :) $\endgroup$
    – yode
    Jul 13, 2017 at 20:55

5 Answers 5

20
$\begingroup$

As requested, posting my comment as an answer:

UpperTriangularize[arg, 1] + LowerTriangularize[arg, -1]

seems to meet all the criteria, quite quick (surprisingly so to me).

$\endgroup$
1
  • 1
    $\begingroup$ @ciao...very nice (as usual) +1 :) $\endgroup$
    – ubpdqn
    Aug 31, 2015 at 7:46
10
$\begingroup$

The following works for a numeric matrix, should be OK for symbolic ones

exmat = {{0, 5, 2, 3, 1, 0}, {4, 3, 2, 5, 1, 3}, {4, 1, 3, 5, 3, 2}, 
       {4, 4, 1, 1, 1, 5}, {3, 4, 4, 5, 3, 3}, {5, 1, 4, 5, 2, 0}}; 

MatrixForm[ReplacePart[exmat, {i_, i_} -> 0]]



(*   0   5   2   3   1   0

    4   0   2   5   1   3

    4   1   0   5   3   2

    4   4   1   0   1   5

    3   4   4   5   0   3

    5   1   4   5   2   0 *)
$\endgroup$
3
  • 2
    $\begingroup$ This is a good start, so +1, but it fails on one count: if the array is packed, I'd need to explicitly check whether its type is real, integer or complex, and replace with 0., 0 or 0. + 0. I accordingly. Otherwise it unpacks. $\endgroup$
    – Szabolcs
    Aug 31, 2015 at 7:26
  • $\begingroup$ OK now I see what u mean by "packed". $\endgroup$
    – thils
    Aug 31, 2015 at 7:29
  • 2
    $\begingroup$ Packed arrays are described here.. $\endgroup$
    – Szabolcs
    Aug 31, 2015 at 9:26
7
$\begingroup$

Method one:LinearAlgebra`SetMatrixDiagonal

mat = RandomReal[10, {3, 3}];
LinearAlgebra`SetMatrixDiagonal[mat,Array[0 &, Length[mat]]] // MatrixForm

Developer`PackedArrayQ[mat]

True

Method two:LinearAlgebra`AddVectorToMatrixDiagonal

mat = RandomReal[10, {3, 3}];
LinearAlgebra`AddVectorToMatrixDiagonal[mat, -Diagonal[mat]] // MatrixForm

Developer`PackedArrayQ[mat]

True

ps:Note this two method will change the original mat.

Compare with ciao's answer here

arg = RandomReal[10, {10^4, 10^4}];
LinearAlgebra`SetMatrixDiagonal[arg,Array[0 &, Length[arg]]]; // AbsoluteTiming

{0.406152, Null}

arg = RandomReal[10, {10^4, 10^4}];
AbsoluteTiming[LinearAlgebra`AddVectorToMatrixDiagonal[arg, -Diagonal[arg]];]

{0.356579, Null}

arg = RandomReal[10, {10^4, 10^4}];
AbsoluteTiming[UpperTriangularize[arg, 1] + LowerTriangularize[arg, -1];]

{1.41651, Null}

$\endgroup$
1
  • $\begingroup$ cool method +1 ! $\endgroup$
    – Ali Hashmi
    Jul 13, 2017 at 20:46
4
$\begingroup$

Just to play with no great idea to solve all issues... and looking forward to answers. This respects packed arrays but does not deal with Infinity or Indeterminate diagonal entries...

mat = RandomReal[1, {100, 100}];
sa = SparseArray[mat];
zr = 1 - SparseArray[IdentityMatrix[100]]
res = zr sa

enter image description here

$\endgroup$
1
$\begingroup$

Although perhaps this unpacks but it seems to be fast:

replaceDiagonal[m_] := Module[{mat = m},
Scan[(mat[[Sequence @@ #]] = 0) &, Table[{i, i}, {i, Length@m}]];
mat];

checking performance:

mat = RandomReal[1, {1000, 1000}];
replaceDiagonal[mat]; // RepeatedTiming

(* {0.04, Null} *)

with a bigger matrix:

mat = RandomReal[10, {10000, 10000}];
replaceDiagonal[mat]; // AbsoluteTiming
(* {2.57671, Null} *)

How does it compare to other methods posted above for matrix of size 1000 x 1000:

@thils method

ReplacePart[mat, {i_, i_} -> 0]; // RepeatedTiming
(* {0.32, Null} *)

@ciao's method

UpperTriangularize[mat, 1] + LowerTriangularize[mat, -1]; // RepeatedTiming
(* {0.01, Null} *)

All the methods give the same result for an input matrix

$\endgroup$
1
  • 1
    $\begingroup$ Why not simply use Do[mat[[i, i]] = 0., {i, Length@m}]? Also see this comment about condition when this method doesn't unpack. $\endgroup$ Jul 14, 2017 at 7:44

Your Answer

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

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