Solution (source code)

= Solution

Starting with the <identity matrix>, multiply by $M$ once per iteration and test the resulting power. Using integer objects keeps the calculation exact and avoids fixed-width integer overflow accidentally turning a nonzero power into zero:

``
def kzero(matrix,kmax=100):
    a=np.asarray(matrix,dtype=object)
    if a.ndim!=2 or a.shape[0]!=a.shape[1]:raise ValueError('square matrix required')
    power=np.eye(len(a),dtype=object)
    for k in range(1,kmax+1):
        power=power@a
        if np.equal(power,0).all():return k
    return None

print([kzero(rand_triu(10)) for _ in range(5)])
``

Continuing the random-number stream from the displayed example gives \b[$[5,4,6,6,8]$]. These are the least positive zero powers, rather than the number of nonzero powers; in particular, a zero matrix has <nilpotency index> one.

For a <strictly upper triangular matrix>, a nonzero entry of $M^k$ can only lie at least $k$ positions above the diagonal. Thus $M^n=0$, and every one of these $10\times10$ examples must return an integer at most ten. The code interprets “up to” as including `kmax`; if the final strict inequality in the question is intended instead, replace `range(1,kmax+1)` by `range(1,kmax)`.