try:
import executable_engineering as exe
except ImportError:
%pip install -q executable_engineering
import executable_engineering as exe
import numpy as npStability and Uncertainty¶
Previously, we discussed how roundoff and truncation errors introduce uncertainty into numbers stored on a computer. The conditioning of a function relates how uncertainty in the inputs propagates to uncertainty in the outputs.
For a general function :
Well-conditioned: Small changes in inputs () lead to proportionally small changes in outputs ().
Ill-conditioned: Small changes in inputs could result in massive, explosive changes in the outputs, and vice-versa.
The condition of a function is a fundamental property of the mathematical transformation itself, not just the specific input/output values.
Recall however that numerical methods have inherent uncertainty in numbers due to finite precision representation. Ill-conditioned functions can catastrophically disrupt finding solutions of linear systems!
Approaching Singularity¶
In the solvability section, we saw two strict cases based on the determinant :
and the system has a unique exact solution.
and the system is unsolvable (parallel lines).
But what happens in the “grey area” when ?
Let’s adjust the price of tables in our fundraising problem to be .
The determinant is . Let’s plot this when is small!
# Change epsilon to see how wildly the solution point swings!
epsilon = 10
A = [[20, 20 - epsilon],
[1, 1]]
b = [700, 20]
fig = exe.visual_solve_2d(A, b, method='exact')
fig.show()The Consequence of Small ¶
If we solve this algebraically by substituting into the first equation:
Notice what happens as gets smaller:
| -10 | 30 | -10 |
| 1 | -300 | 320 |
| 0.1 | -3000 | 3020 |
| 1e-15 | -3e+17 | 3e+17 |
This should sound alarm bells! Technically, for any , the lines intersect and the system is invertible. However, the solution point swings wildly toward infinity as the lines become nearly parallel.
This is simple example of an ill-conditioned system (matrix). If is ill-conditioned, the transformation depends highly on precision. When numerical methods are applied to such systems, they inherantly add round-off error which makes the solution unstable (and also interferes with algorithms that check the interations for convergence).
Distortion, not Amplification¶
A common misconception is that ill-conditioning is just “amplification” of error. But if a function simply amplified error, its inverse would reduce error.
This is not the case! The conditioning of a forward function and its inverse are quantifiably the same. Conditioning represents the maximum distortion or stretching caused by the transformation in any direction.
Well-conditioned matrix distortion¶
For a linear transformation , we can visualize this distortion by applying the matrix (and its inverse ) to a perfect unit circle.
Let’s look at a well-conditioned matrix:
Notice how the circle is scaled but remains perfectly proportional. The inverse simply shrinks it back. The mapping is smooth and nowhere would two values of overlap.
A_well = [[2, 0],
[0, 2]]
print('Condition number: ', np.linalg.cond(A_well))
fig1 = exe.visualize_conditioning(A_well)
fig1.show() Ill-conditioned matrix distortion¶
Now, let’s look at an ill-conditioned matrix where the rows are nearly parallel:
A_ill = [[1, 1],
[1, 1.1]]
print('Condition number: ', np.linalg.cond(A_ill))
fig2 = exe.visualize_conditioning(A_ill)
fig2.show()The Condition Number ()¶
The severe stretching seen above is quantified by the Condition Number (). It is the maximum ratio of the relative error in to the relative error in .
Let’s perturb our inputs and outputs by a small error and :
Derivation¶
Substitute the perturbed vectors into our linear system :
Since we know , these terms cancel out, leaving us with a direct relationship between the uncertainties:
Ratio of Relative Errors¶
We want to find the ratio of the relative error in the input to the relative error in the output using the norm (magnitude) of the vectors:
The maximum possible value of this ratio across all possible vectors is defined as the condition number :
NB: This result introduces the concept of the norm (magnitude) of a matrix (defined below) and an identity .
Application¶
From this definition, it is obvious that the condition number is identical for both the forward () and backward () transformations!
It guarantees bounds on the relative error in both directions:
If is large (ill-conditioned), small errors in your data can result in massive, unpredictable errors in your computed solution and due to finite precision, small errors will always be present!!
Calculation via Eigenvalues¶
The formal definition requires calculating the inverse , which is exactly what we are trying to avoid doing blindly! We want to know the condition number before attempting to solve the system.
Geometrically, the maximal stretching of a matrix is defined by its largest eigenvalue (). The maximal stretching of the inverse is the smallest eigenvalue of the original matrix ().
Therefore, for symmetric matrices, we can calculate using the eigenvalues:
In numpy this is calculated using the function.