# Check if rows and columns of matrices have more than one non-zero element?

I am given a list of square matrices (with non numeric entries but abstract symbols which are assumed to take real values) and I want to write a function that returns True if the following condition is satisfied:

• All rows and columns of each matrix have at most one nonzero element.

In other words, a row or column can either have all elements zero, or all but one. Otherwise, the function should return False.

I could of course write a triple loop that goes through each matrix in the list, then each row and column, and counts non-zero elements, but I am wondering what would be the most clever and efficient way to do it.

Note that even if only one matrix in that list has a row or column with more than one non-zero elements, then there would be no need to check for the rest.

ClearAll[f]
f[mat_?(MatrixQ[#, NumericQ]&)] :=
FreeQ[
Through[{Total, Map[Total]} @ Unitize[mat]],
_?(# > 1 &)
]


The code uses Unitize to turn any non-zero number into 1. Total sums the columns of the unitized matrix; Map[Total] sums the rows. The sums are therefore counts of non-zero elements in each column vs. row. FreeQ checks whether any of those counts is higher than 1, which indicates that there was more than one non-zero element in that column vs. row.

Let's make some matrices to test:

SeedRandom
MatrixForm /@
(list =
RandomChoice[{200, 10, 10, 10} -> {0, 1, 2, 3}, {10, 5, 5}]) and apply our test function f to each:

f /@ list

(* Out: {False, True, False, False, False, False, False, True, False, True} *)


Following the comment and edit mentioning the presence of symbols rather than numbers, here is an alternative function:

ClearAll[fSym]
fSym[mat_] :=
FreeQ[
Through[{Total, Map[Total]}@mat],
_Plus,
-1
]


Same test, but with symbolic variables:

SeedRandom
MatrixForm /@ (listSym =
RandomChoice[{200, 10, 10, 10} -> {0, a, b, c}, {10, 5, 5}]) fSym /@ listSym

(* Out: {False, True, False, False, False, False, False, True, False, True} *)

• Thank you for your answer. However, there is the following issue. My matrices do not have numeric elements but abstract symbols (that take real values). E.g. I would have something like {{a,0,0},{b,c,0},{0,0,0}}. In this case, your code does not work Dec 4, 2020 at 6:52
• @AG1123 I added an edit to the answer to take that into account. Dec 4, 2020 at 7:04
• Great, thanks! That works as needed Dec 4, 2020 at 7:17
• How would I modify the code if I wanted the property to hold only for rows (or columns) instead of both? Thanks @MarcoB Dec 6, 2020 at 5:50
• @AG1123 As mentioned in the description, Total sums the columns of the matrix; Map[Total] sums the rows. So instead of Through[{Total, Map[Total]}@mat] you can just have one of those only: e.g. Total @ mat will do the check over the columns; whereas Total /@ mat will do it over the rows (note the difference between @ and /@). Dec 6, 2020 at 20:43
m = {{0, a, 0}, {b, 0, 0}, {0, 0, c}};
f[m_] := AllTrue[
Count[#, 0] & /@ Join[m, Transpose[m]], # >= Length@m - 1 &]


 True

Original

f[m_] := Nor @@ (AllTrue[#, EqualTo] & /@ Join[m, Transpose[m]]);
m = {{0, 0, 1}, {1, 0, 1}, {2, 0, 0}};
n = {{1, 1, 1}, {0, 0, 1}, {0, 1, 0}};
f[m]
f[n]


False

True

• Thanks but your code does not return the correct result. Both example matrices should return False, since they both have a row or column with more than one non zero elements. Dec 4, 2020 at 6:49
• @AG1123 Thanks! I have fixed it. Dec 4, 2020 at 9:11
• Thanks! I like how concise your solution is, even though I don't really understand it. Dec 4, 2020 at 10:33