Assuming this is feasible in your case, you could go for a classical strategy of trading time for space.
(I am assuming you are using an OO language - the idea holds for non-OO ones too, but will require more effort to ensure that the different representation of the same matrix are kept in synch).
Instead of representing the matrix as an array (or along with that representation) what about keeping it as a set of non-zero values?
So what you are representing as:
|5 6 9 2|
|1 8 4 0|
|5 8 0 0|
|7 0 0 0|
would become (or stored also as)
1,1=5
1,2=6
1,3=9
...
4,1=7
if this is doable, you could also split this set in two (upper and lower diagonal)
so in the case of your first matrix:
|5 6 9 2|
|1 8 4 9|
|5 8 1 6|
|7 6 3 2|
would be:
UpperMap -
1,1=5
1,2=6
1,3=9
...
4,1=7
Lower Map-
2,4=9
3,3=1
...
4,4=2
In this case, your test would be "asking if the lower hashmap is empty".
As mentioned above, if you need to do more "traditional" operations on the matrix you could store it as an array along with the 2-maps, and in case the matrixes are not immutable, you will have to provide methods to change the "cell" values.
The other trade off (apart from space) is that creating a new matrix requires more cpu time. But could be worth it if you create and modify these infrequently and have to test for the lower diagonal often.
A more extreme approach:
For each matrix build a bitmap representation (non-zero cells are 1, zero cells get a 0) and use logical operations to check for "emptiness" of a section.