21
$\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$
  • $\begingroup$ Anything wrong with UpperTriangularize[arg, 1] + LowerTriangularize[arg, -1]? $\endgroup$ – ciao Aug 31 '15 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 '15 at 7:36
  • $\begingroup$ Done - I was surprised at speed... $\endgroup$ – ciao Aug 31 '15 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 '15 at 7:53
  • $\begingroup$ Well, It seem I have beat that. :) $\endgroup$ – yode Jul 13 '17 at 20:55
18
$\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$
  • $\begingroup$ @ciao...very nice (as usual) +1 :) $\endgroup$ – ubpdqn Aug 31 '15 at 7:46
8
$\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$
  • 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 '15 at 7:26
  • $\begingroup$ OK now I see what u mean by "packed". $\endgroup$ – thils Aug 31 '15 at 7:29
  • 2
    $\begingroup$ Packed arrays are described here.. $\endgroup$ – Szabolcs Aug 31 '15 at 9:26
5
$\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$
  • $\begingroup$ cool method +1 ! $\endgroup$ – Ali Hashmi Jul 13 '17 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
    $\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$ – Alexey Popkov Jul 14 '17 at 7:44

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.