Skip to content

Commit

Permalink
Add new examples in the API doc
Browse files Browse the repository at this point in the history
  • Loading branch information
mbaudin47 committed Dec 8, 2024
1 parent 1a658c6 commit 112b3ba
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 38 deletions.
20 changes: 20 additions & 0 deletions numericalderivative/_DerivativeBenchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,26 @@ def __init__(
This can be useful for benchmarking on several points.
We must have interval[0] <= interval[1].
Examples
--------
The next example creates a benchmark problem.
>>> import numericalderivative as nd
>>> problem = nd.ExponentialProblem()
>>> x = problem.get_x()
>>> function = problem.get_function()
>>> first_derivative = problem.get_first_derivative()
The next example creates a benchmark experiment.
>>> import numericalderivative as nd
>>> benchmark = nd.BuildBenchmark()
>>> number_of_problems = len(benchmark)
>>> for i in range(number_of_problems):
>>> problem = benchmark[i]
>>> name = problem.get_name()
>>> print(f"Problem #{i}: {name}")
References
----------
- Dumontet, J., & Vignes, J. (1977). Détermination du pas optimal dans le calcul des dérivées sur ordinateur. RAIRO. Analyse numérique, 11 (1), 13-25.
Expand Down
45 changes: 33 additions & 12 deletions numericalderivative/_FiniteDifferenceFormula.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,20 @@ def __init__(self, function, x, args=None):
-------
None.
Examples
--------
>>> import numericalderivative as nd
>>> import numpy as np
>>>
>>> def scaled_exp(x):
>>> alpha = 1.e6
>>> return np.exp(-x / alpha)
>>>
>>> x = 1.0
>>> formula = nd.FirstDerivativeForward(scaled_exp, x)
>>> function = formula.get_function()
>>> x = formula.get_x()
"""
self.function = nd.FunctionWithArguments(function, args)
self.x = x
Expand All @@ -54,7 +68,6 @@ def get_function(self):
"""
return self.function


def get_x(self):
"""
Return the input point
Expand All @@ -66,11 +79,12 @@ def get_x(self):
"""
return self.x


class FirstDerivativeForward(FiniteDifferenceFormula):
"""Compute the first derivative using forward finite difference formula"""

@staticmethod
def compute_error(step, second_derivative_value, absolute_precision=1.0e-16):
def compute_error(step, second_derivative_value=1.0, absolute_precision=1.0e-16):
r"""
Compute the total error for forward finite difference for f'.
Expand Down Expand Up @@ -112,7 +126,7 @@ def compute_error(step, second_derivative_value, absolute_precision=1.0e-16):
return total_error

@staticmethod
def compute_step(second_derivative_value, absolute_precision=1.0e-16):
def compute_step(second_derivative_value=1.0, absolute_precision=1.0e-16):
r"""
Compute the exact optimal step for forward finite difference for f'.
Expand Down Expand Up @@ -185,19 +199,26 @@ def __init__(self, function, x, args=None):
Examples
--------
>>> import numericalderivative as nd
>>> import numpy as np
>>>
>>> def scaled_exp(x):
>>> alpha = 1.e6
>>> return np.exp(-x / alpha)
>>>
>>> x = 1.0
>>> formula = FirstDerivativeForward(scaled_exp, x)
>>> formula = nd.FirstDerivativeForward(scaled_exp, x)
>>> step = 1.0e-3 # A first guess
>>> f_prime_approx = finite_difference.compute(step)
>>> # Compute the step using an educated guess
>>> f_prime_approx = formula.compute(step)
Compute the step using an educated guess.
>>> second_derivative_value = 1.0 # A guess
>>> step, absolute_error = finite_difference.compute_step(second_derivative_value)
>>> f_prime_approx = finite_difference.compute(step)
>>> step, absolute_error = formula.compute_step(second_derivative_value)
>>> f_prime_approx = formula.compute(step)
Compute the absolute error from a given step.
>>> absolute_error = formula.compute_error(step, second_derivative_value)
"""
super().__init__(function, x, args)

Expand Down Expand Up @@ -242,7 +263,7 @@ class FirstDerivativeCentral(FiniteDifferenceFormula):
"""Compute the first derivative using central finite difference formula"""

@staticmethod
def compute_error(step, third_derivative_value, absolute_precision=1.0e-16):
def compute_error(step, third_derivative_value=1.0, absolute_precision=1.0e-16):
r"""
Compute the total error for central finite difference for f'
Expand Down Expand Up @@ -284,7 +305,7 @@ def compute_error(step, third_derivative_value, absolute_precision=1.0e-16):
return total_error

@staticmethod
def compute_step(third_derivative_value, absolute_precision=1.0e-16):
def compute_step(third_derivative_value=1.0, absolute_precision=1.0e-16):
r"""
Compute the exact optimal step for central finite difference for f'.
Expand Down Expand Up @@ -398,7 +419,7 @@ class SecondDerivativeCentral(FiniteDifferenceFormula):
"""Compute the second derivative using central finite difference formula"""

@staticmethod
def compute_error(step, fourth_derivative_value, absolute_precision=1.0e-16):
def compute_error(step, fourth_derivative_value=1.0, absolute_precision=1.0e-16):
r"""
Compute the total error for central finite difference for f''
Expand Down Expand Up @@ -440,7 +461,7 @@ def compute_error(step, fourth_derivative_value, absolute_precision=1.0e-16):
return total_error

@staticmethod
def compute_step(fourth_derivative_value, absolute_precision=1.0e-16):
def compute_step(fourth_derivative_value=1.0, absolute_precision=1.0e-16):
r"""
Compute the optimal step for the finite difference for f''.
Expand Down
50 changes: 46 additions & 4 deletions numericalderivative/_GeneralFiniteDifference.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,13 +502,55 @@ def compute(self, step):
Examples
--------
The next example computes an approximate third derivative of the sin function.
To do so, we use a central F.D. formula of order 2.
>>> import numericalderivative as nd
>>> import numpy as np
>>> x = 1.0
>>> differentiation_order = 3 # Compute f'''
>>> formula_accuracy = 2 # Use differentiation_order 2 precision
>>> y = nd.GeneralFiniteDifference(np.sin, x, differentiation_order, formula_accuracy).finite_differences()
>>> y = nd.GeneralFiniteDifference(np.sin, x, differentiation_order, formula_accuracy, "forward").finite_differences()
>>> formula_accuracy = 2 # Use order 2 precision
>>> formula = nd.GeneralFiniteDifference(
>>> np.sin, x, differentiation_order, formula_accuracy
>>> )
>>> step = 1.0 # A first guess
>>> third_derivative = formula.compute(step)
We can use either forward, backward or central finite differences.
>>> formula = nd.GeneralFiniteDifference(
>>> np.sin,
>>> x,
>>> differentiation_order,
>>> formula_accuracy,
>>> "forward"
>>> )
We can compute the step provided the derivative of order 5 is known.
A first guess of this value can be set, or the default value (equal to 1).
Then the step can be used to compute the derivative.
>>> formula = nd.GeneralFiniteDifference(
>>> np.sin,
>>> x,
>>> differentiation_order,
>>> formula_accuracy,
>>> "central"
>>> )
>>> step, absolute_error = formula.compute_step()
>>> third_derivative = formula.compute(step)
>>> # Set the fifth derivative, if known
>>> fifth_derivative_value = 1.0 # This may work
>>> step, absolute_error = formula.compute_step(fifth_derivative_value)
>>> # Set the absolute error of the function evaluation
>>> absolute_precision = 1.0e-14
>>> step, absolute_error = formula.compute_step(
>>> fifth_derivative_value, absolute_precision
>>> )
Given the step, we can compute the absolute error.
>>> absolute_error = formula.compute_error(step)
"""
# Compute the function values
y = np.zeros((self.differentiation_order + self.formula_accuracy))
Expand Down
8 changes: 7 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@

setup(
name="numericalderivative",
keywords=["Numerical Differentiation", "First Derivative", "Finite Difference", "Optimal Step Size", "Numerical Analysis"],
keywords=[
"Numerical Differentiation",
"First Derivative",
"Finite Difference",
"Optimal Step Size",
"Numerical Analysis",
],
version="0.1",
packages=find_packages(),
install_requires=["numpy"],
Expand Down
46 changes: 27 additions & 19 deletions tests/test_finitedifferenceformula.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,23 +68,25 @@ def test_first_derivative_forward_error(self):
# Check that the step actually minimises the total error
number_of_points = 10000
step_array = np.logspace(-10.0, 5.0, number_of_points)
model_error = [nd.FirstDerivativeForward.compute_error(
model_error = [
nd.FirstDerivativeForward.compute_error(
step, second_derivative_value, absolute_precision
) for step in step_array]
)
for step in step_array
]
index = np.argmin(model_error)
reference_optimal_step = step_array[index]
reference_optimal_error = model_error[index]
print(f"Optimal index = {index}")
print(f"reference_optimal_step = {reference_optimal_step}, "
f"reference_optimal_error = {reference_optimal_error}")
np.testing.assert_allclose(
step, reference_optimal_step, rtol=1.0e-3
print(
f"reference_optimal_step = {reference_optimal_step}, "
f"reference_optimal_error = {reference_optimal_error}"
)
np.testing.assert_allclose(step, reference_optimal_step, rtol=1.0e-3)
np.testing.assert_allclose(
computed_absolute_error, reference_optimal_error, rtol=1.0e-3
)


def test_first_derivative_central(self):
print("+ test_first_derivative_central")
# Check FirstDerivativeCentral.compute()
Expand Down Expand Up @@ -117,18 +119,21 @@ def test_first_derivative_central_error(self):
# Check that the step actually minimises the total error
number_of_points = 10000
step_array = np.logspace(-10.0, 5.0, number_of_points)
model_error = [nd.FirstDerivativeCentral.compute_error(
model_error = [
nd.FirstDerivativeCentral.compute_error(
step, third_derivative_value, absolute_precision
) for step in step_array]
)
for step in step_array
]
index = np.argmin(model_error)
reference_optimal_step = step_array[index]
reference_optimal_error = model_error[index]
print(f"Optimal index = {index}")
print(f"reference_optimal_step = {reference_optimal_step}, "
f"reference_optimal_error = {reference_optimal_error}")
np.testing.assert_allclose(
step, reference_optimal_step, rtol=2.0e-3
print(
f"reference_optimal_step = {reference_optimal_step}, "
f"reference_optimal_error = {reference_optimal_error}"
)
np.testing.assert_allclose(step, reference_optimal_step, rtol=2.0e-3)
np.testing.assert_allclose(
computed_absolute_error, reference_optimal_error, rtol=2.0e-3
)
Expand Down Expand Up @@ -165,18 +170,21 @@ def test_second_derivative_step(self):
# Check that the step actually minimises the total error
number_of_points = 10000
step_array = np.logspace(-10.0, 5.0, number_of_points)
model_error = [nd.SecondDerivativeCentral.compute_error(
model_error = [
nd.SecondDerivativeCentral.compute_error(
step, fourth_derivative_value, absolute_precision
) for step in step_array]
)
for step in step_array
]
index = np.argmin(model_error)
reference_optimal_step = step_array[index]
reference_optimal_error = model_error[index]
print(f"Optimal index = {index}")
print(f"reference_optimal_step = {reference_optimal_step}, "
f"reference_optimal_error = {reference_optimal_error}")
np.testing.assert_allclose(
step, reference_optimal_step, rtol=1.0e-3
print(
f"reference_optimal_step = {reference_optimal_step}, "
f"reference_optimal_error = {reference_optimal_error}"
)
np.testing.assert_allclose(step, reference_optimal_step, rtol=1.0e-3)
np.testing.assert_allclose(
computed_absolute_error, reference_optimal_error, rtol=1.0e-3
)
Expand Down
3 changes: 1 addition & 2 deletions tests/test_generalfinitedifference.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ def test_finite_differences(self):
f_third_derivative_approx, f_third_derivative_exact, rtol=1.0e-5
)


def test_first_forward(self):
print("+ test_first_forward")
# Evaluate f'(x) with f(x)= sin(x) using forward F.D.
Expand Down Expand Up @@ -162,7 +161,6 @@ def test_first_central(self):
computed_absolute_error, exact_absolute_error, rtol=1.0e-5
)


def test_second_central_coefficients(self):
print("+ test_second_central")
# Evaluate f''(x) with f(x)= sin(x) using central F.D.
Expand Down Expand Up @@ -240,5 +238,6 @@ def test_second_central_coefficients(self):
computed_absolute_error, exact_absolute_error, rtol=1.0e-5
)


if __name__ == "__main__":
unittest.main()

0 comments on commit 112b3ba

Please sign in to comment.