import numpy as np
import plotly.graph_objects as go
try:
import executable_engineering as exe
except ImportError:
%pip install -q executable_engineering
import executable_engineering as exeTrue Absolute Error¶
The True Absolute Error is the absolute difference between the true value and an approximation.
This metric is only useful when the true value is known.
Example: What is the derivative of at ?¶
Recall the definition of a derivative:
The true value of the derivative of is .
value_true = 2*np.cos(0)
print(f"The true value is: {value_true}")The true value is: 2.0
Approximating the derivative¶
We can define a function to approximate the derivative using a finite , and use it with , to find the approximate value and the true absolute error:
def approx_derivative(delta_x):
return (2*np.sin(0 + delta_x) - 2*np.sin(0)) / delta_x
value_approx = approx_derivative(delta_x=0.1)
print(f"The approximate value is: {value_approx}")
E_t = np.abs(value_true - value_approx)
print(f"The true absolute error is: {E_t}")The approximate value is: 1.996668332936563
The true absolute error is: 0.003331667063436905
True Relative Error¶
Often, the absolute error is not as useful as the True Relative Error, because it does not account for the magnitude of the value. For example, a 1-meter error in GPS is insignificant for a long road trip but critical for a self-driving car.
The true relative error is defined as:
or as a percentage:
Example: What is the relative error from the previous calculation?¶
eps_t = E_t / value_true
print(f"The true relative error is: {eps_t}")The true relative error is: 0.0016658335317184525
Approximate Absolute and Relative Error¶
What if we don’t know the true value? Numerical methods often have a tunable parameter that controls accuracy (like above). We can estimate the error by comparing sequential approximations, using the better approximation in place of the true value.
Example: Use a smaller step size to find the approximate errors.¶
This is the typical use-case you will encounter in numerical methods since the true function is not usually known.
Except for root finders because we are looking for for which !
Commonly the difference is disregarded and the meaning has to be taken in context.
approx_1 = approx_derivative(0.1)
approx_2 = approx_derivative(0.01)
E_a = np.abs(approx_2 - approx_1)
epsilon_a = E_a / approx_2
print(f"The approximate absolute error is: {E_a}")
print(f"The approximate relative error is: {epsilon_a}")
print(f"Recall, The true absolute and relative errors were: {E_t}, and {eps_t}")The approximate absolute error is: 0.0032983338967698117
The approximate relative error is: 0.001649194434821387
Recall, The true absolute and relative errors were: 0.003331667063436905, and 0.0016658335317184525
Tolerance¶
So how can we know when we have an answer that is good enough? We can’t really (unless we know what the ‘true’ answer is!). Instead, the best we can do is compare our error indicators to a tolerance:
Absolute Tolerance (): The threshold below which the absolute error is considered acceptable.
Relative Tolerance (): The threshold below which the relative error is considered acceptable.
For approximate errors that are based on successive guesses, satisfying the tolerances doesn’t actually indicate we’ve found the right solution, just that our esimate is no longer improving.
Pseudocode Concept¶
parameter = initial_value
while True:
result = run_algorithm(parameter)
error = calculate_error(result, previous_result)
if error < tolerance:
break
parameter = reduce_parameter(parameter)Exploring and as a function of ¶
Lets see how the errors behave with decreasing step size. One might expect the error to approach zero as .
Is this what you observe? What happens at ? Why? Checking the avlue of the function evaulations might shed some light on this!
delta_x = np.logspace(0, -10, 11)
E_a = np.zeros(delta_x.size)
epsilon_a = np.zeros(delta_x.size)
for i, dx in enumerate(delta_x):
# Note: We are comparing the approximation at dx with the one at dx/10
E_a[i] = np.abs(approx_derivative(dx/10) - approx_derivative(dx))
epsilon_a[i] = E_a[i] / approx_derivative(dx/10)
fig = go.Figure()
fig.add_trace(go.Scatter(x=delta_x, y=np.abs(E_a), mode='lines+markers', name='E_a'))
fig.add_trace(go.Scatter(x=delta_x, y=np.abs(epsilon_a), mode='lines+markers', name='epsilon_a'))
fig.update_xaxes(type='log', title='Delta x')
fig.update_yaxes(type='log', title='Error')
fig.show()