A discrepancy can originate in the mathematical problem, the selected algorithm, or its implementation. This tutorial uses controlled examples to separate problem sensitivity from algorithmic error and to show why residual and forward error answer different questions.
Perturb two related linear systems in high precision.
Explore the roles of \(\delta\) and \(\eta\) in an interactive line plot.
Estimate observed amplification as the systems become nearly dependent.
Compare two algorithms for the same quadratic root.
Measure forward and coefficient backward error.
Construct a small-residual, large-forward-error candidate.
Record the diagnosis and limitations.
Set up the sensitivity problem
This is a concrete instance of the condition measure from Module 4. For each fixed positive \(\delta\), define the map \(f_\delta:b\mapsto x\) by the dimensionless system
\[
x_1+x_2=b_1,
\qquad
x_1+(1+\delta)x_2=b_2.
\]
The input is the right-hand-side vector \(b=(b_1,b_2)\) and the output is the exact solution \(x=(x_1,x_2)\). The parameter \(\delta\) selects the map and is held fixed while its input is perturbed. The baseline input \(b_\delta=(2,2+\delta)\) produces \(x=(1,1)\). The experiment adds \(\Delta b=(0,\eta)\) and observes
\[
\Delta x=(-\eta/\delta,\eta/\delta).
\]
With the maximum norm, the relative RHS-input change is \(|\eta|/(2+\delta)\), the relative solution-output change is \(|\eta|/\delta\), and their ratio is \(\kappa_\mathrm{obs}=(2+\delta)/\delta\). The code uses 80-digit decimal arithmetic so that this amplification is not an accidental binary64 effect.
from decimal import Decimal, getcontext, localcontextimport mathgetcontext().prec =80D = Decimaldef max_norm(values):"""Return the maximum absolute component."""returnmax(abs(value) for value in values)def solve_two_equation_system(delta, second_rhs_perturbation=D(0)):"""Return the solution output for the constructed RHS input.""" first_rhs = D(2) second_rhs = D(2) + delta + second_rhs_perturbation x2 = (second_rhs - first_rhs) / delta x1 = first_rhs - x2return [x1, x2]def sensitivity_diagnostics(delta, perturbation):"""Treat the RHS as input and report its solution-output sensitivity.""" base_rhs = [D(2), D(2) + delta] input_change = [D(0), perturbation] base_solution = solve_two_equation_system(delta) perturbed_solution = solve_two_equation_system(delta, perturbation) output_change = [ perturbed - basefor perturbed, base inzip(perturbed_solution, base_solution) ] relative_input_change = max_norm(input_change) / max_norm(base_rhs) relative_output_change = max_norm(output_change) / max_norm(base_solution) observed_amplification = relative_output_change / relative_input_changereturn {"base_rhs": base_rhs,"input_change": input_change,"base_solution": base_solution,"perturbed_solution": perturbed_solution,"output_change": output_change,"relative_input_change": relative_input_change,"relative_output_change": relative_output_change,"observed_amplification": observed_amplification, }input_perturbation = D("1e-16")print(f"decimal precision: {getcontext().prec} digits")print(f"second right-hand-side perturbation: {input_perturbation:.1E}")
decimal precision: 80 digits
second right-hand-side perturbation: 1.0E-16
Compare separated and nearly dependent systems
Predict how the same right-hand-side perturbation affects the solution when \(\delta=1\) and when \(\delta=10^{-12}\).
The separated map amplifies this RHS direction by about 3. The nearly dependent map amplifies it by about \(2\times10^{12}\): a relative RHS-input change near \(5\times10^{-17}\) produces a relative solution-output change of \(10^{-4}\). For each result, \(\delta\) was held fixed while \(b\) changed. This is directional problem sensitivity observed in high precision, not a full condition number.
Explore \(\delta\) and \(\eta\) interactively
The controls below use base-10 exponents because the informative values span many orders of magnitude. Before moving them, predict separately what will happen when you:
hold \(\eta\) fixed and decrease \(\delta\);
hold \(\delta\) fixed and increase \(\eta\).
The plot always uses the same linear axes, \(0\leq x_1,x_2\leq2\). It does not zoom to make almost coincident lines look separated. The displayed geometry is therefore directly comparable between settings, while the diagnostics retain the small quantities that a screen cannot resolve.
The published activity page is static, so it cannot run Python callbacks. The reviewed figure below is the non-interactive fallback. Download the executable notebook or open it in Colab to use the live \(\delta\) and \(\eta\) controls.
Static fallback showing the two-equation geometry for delta equal to one and ten to the minus twelve.
Exercise: vary the separation
Change exercise_delta and predict the amplification before running the cell. Try powers of ten between 1 and \(10^{-12}\). Which quantity in the equations suggests the trend?
The exact solution change contains the ratio \(\eta/\delta\). The sweep below checks how that factor appears in the relative amplification. One direction is being sampled; this is not a complete worst-case condition-number calculation.
For a unique nonzero exact solution \(x\), a common relative normwise choice is \(\rho_X=\|\hat{x}-x\|_X/\|x\|_X\). With multiple solutions, \(S(d)\) must represent the acceptable solutions or the intended branch. If \(x\) is replaced by a justified numerical reference, the measured value is an estimate of the unknown exact forward error.
The backward error instead measures distance in the data space:
This definition asks for the infimum of the sizes of admissible input changes that make the computed result exact. It is incomplete unless the allowed changes, preserved structure, norm, and scaling in \(\rho_D\) are stated. For \(Ax=b\) with \(A\) fixed and \(r=b-A\hat{x}\), choosing \(\rho_D=\|\Delta b\|/\|b\|\) gives \(\eta_b=\|r\|/\|b\|\), because \(\hat{x}\) exactly solves \(A\hat{x}=b-r\).
Hold the problem fixed and change the algorithm
Now consider the dimensionless polynomial
\[
p(x)=x^2-10^8x+1.
\]
The direct formula subtracts the square root from \(10^8\) to obtain the small root. The reformulation computes the large root using addition, then uses the fact that the product of the roots is one. Both solve the same mathematical problem in the same binary64 format.
The high-precision reference is recomputed with 80 and 100 decimal digits. Their relative difference checks that reference precision is far beyond what this comparison needs.
The direct calculation has roughly 25% forward error and requires a large coefficient perturbation under the stated backward measure. The reformulated calculation has forward and backward errors near binary64 rounding scale. This is evidence about the algorithms because the polynomial, input coefficients, arithmetic format, reference, and metrics are held fixed.
Compare residual with forward error
Return to the nearly dependent system with \(\delta=10^{-12}\). Its exact solution is \((1,1)\). The candidate \((0,2)\) is visibly far away, but calculate its residual and the right-hand-side perturbation that would make it exact.
The relative backward error is about \(5\times10^{-13}\), but the relative forward error is one. The candidate exactly solves a nearby system, and this ill-conditioned problem amplifies that small input change. A small residual is therefore evidence of a nearby solved problem, not necessarily of proximity to the desired solution.
Evidence record
A diagnosis should record which object was tested, which metric was used, and what remains unproven. This prevents “ill-conditioned” or “unstable” from becoming an unsupported label.
evidence = {"conditioning_experiment": {"arithmetic": "Decimal with 80 digits of precision","map": "For fixed delta, right-hand side b maps to exact solution x","input": "right-hand-side vector b","output": "solution vector x","fixed_parameter": "delta","norm": "maximum norm on dimensionless quantities","input_perturbation": input_perturbation,"observed_amplification_delta_1": ( conditioning_results["1"]["observed_amplification"] ),"observed_amplification_delta_1e_12": observed_conditioning,"limitation": "One perturbation direction, not a full condition number.", },"algorithm_experiment": {"problem": "Small root of x**2 - 1e8*x + 1","reference_check_relative_difference": reference_difference,"direct_forward_error": quadratic_results["direct"]["forward_error"],"direct_backward_error": quadratic_results["direct"]["backward_error"],"reformulated_forward_error": ( quadratic_results["reformulated"]["forward_error"] ),"reformulated_backward_error": ( quadratic_results["reformulated"]["backward_error"] ),"limitation": "One coefficient set, not a general stability proof.", },"residual_experiment": {"relative_forward_error": relative_forward_error,"relative_rhs_backward_error": relative_rhs_backward_error,"conclusion": ("A small scaled residual does not imply small forward error ""for this ill-conditioned system." ), },}evidence
{'conditioning_experiment': {'arithmetic': 'Decimal with 80 digits of precision',
'map': 'For fixed delta, right-hand side b maps to exact solution x',
'input': 'right-hand-side vector b',
'output': 'solution vector x',
'fixed_parameter': 'delta',
'norm': 'maximum norm on dimensionless quantities',
'input_perturbation': Decimal('1E-16'),
'observed_amplification_delta_1': Decimal('3.0000000000000000000000000000000000000000000000000000000000000000000000000000000'),
'observed_amplification_delta_1e_12': Decimal('2000000000001.0000000000000000000000000000000000000000000000000000000000000000000'),
'limitation': 'One perturbation direction, not a full condition number.'},
'algorithm_experiment': {'problem': 'Small root of x**2 - 1e8*x + 1',
'reference_check_relative_difference': Decimal('4.1999999999999995799999999999999579999999999999915999999999999978999999999999995E-79'),
'direct_forward_error': Decimal('0.2549419403076172620058059692382887005805969238296151161193847659975290298461915105581283569335266948'),
'direct_backward_error': Decimal('0.1460936722945242059648786141111309714162763770152708971046628882161945422366802853447337934745734873'),
'reformulated_forward_error': Decimal('7.907743916987153941692944892356164065364028626385689635114354499370422999748222689215609611052530019E-17'),
'reformulated_backward_error': Decimal('3.953871958493576336403115389080986758609425472278465196290785691824888572980141881324657977789542996E-17'),
'limitation': 'One coefficient set, not a general stability proof.'},
'residual_experiment': {'relative_forward_error': Decimal('1'),
'relative_rhs_backward_error': Decimal('4.9999999999975000000000012499999999993750000000003124999999998437500000000781250E-13'),
'conclusion': 'A small scaled residual does not imply small forward error for this ill-conditioned system.'}}
Pitfalls and optional extensions
Do not call a problem ill-conditioned because one low-precision algorithm failed; perturb the mathematical inputs independently of that algorithm.
Do not call an algorithm stable because it returned a small residual on one ill-conditioned problem.
Do not treat one observed perturbation direction as the full condition number.
Try smaller and larger \(\delta\) values while ensuring the decimal precision remains sufficient for the selected perturbation.
Change \(B\) in the quadratic and identify where the direct formula’s errors become consequential.
Compare another norm, but state its scaling and explain which scientific quantity it represents.