I agree that dtrtri
should be more visible, so I wrote an example.
# Import.
import timeit
import numpy as np
from scipy.linalg.lapack import dtrtri
# Make a random upper triangular matrix.
rng = np.random.default_rng(12345)
n = 15
mat = np.triu(rng.random(size=(n, n)))
# The condition number is high, and grows quickly with n.
print('Condition number: ', np.linalg.cond(mat))
# Time the generic matrix inverse routine and the ad hoc one.
print('Time inv: ', timeit.timeit(lambda: np.linalg.inv(mat), number=10000))
print('Time dtrtri: ', timeit.timeit(lambda: dtrtri(mat, lower=0), number=10000))
# Check the error.
inv_mat1 = np.linalg.inv(mat)
inv_mat2, _ = dtrtri(mat, lower=0)
print('Error inv: ', np.max(np.abs(inv_mat1 @ mat - np.eye(n))))
print('Error dtrtri: ', np.max(np.abs(inv_mat2 @ mat - np.eye(n))))
At least for this simple example we get:
Condition number: 227524.1404212523
Time inv: 0.1151930999999422
Time dtrtri: 0.03039009999974951
Error inv: 7.883022033401421e-12
Error dtrtri: 7.65486099651801e-13
Which shows that dtrtri()
is both faster and accurate than inv()
. In this case, both inv()
and dtrtri()
compute a matrix that is exactly upper triangular. However, this is not the case for a lower triangular matrix, where small entries above the diagonal pollute the result of inv()
.