Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions maths/numerical_analysis/brents_method.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"""
Brent's Method for Root Finding

Find a root of a function in a bracketing interval using Brent's method.

Brent's method combines bisection, secant, and inverse quadratic interpolation to efficiently and robustly find a root of a continuous function. It is guaranteed to converge as long as the root is bracketed.

Check failure on line 6 in maths/numerical_analysis/brents_method.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

maths/numerical_analysis/brents_method.py:6:89: E501 Line too long (207 > 88)

Check failure on line 6 in maths/numerical_analysis/brents_method.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

maths/numerical_analysis/brents_method.py:6:89: E501 Line too long (207 > 88)

Check failure on line 6 in maths/numerical_analysis/brents_method.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

maths/numerical_analysis/brents_method.py:6:89: E501 Line too long (207 > 88)

See: https://en.wikipedia.org/wiki/Brent%27s_method

Author: [Aryan Singh (2nd year LNMIIT)]
"""

from collections.abc import Callable

Check failure on line 13 in maths/numerical_analysis/brents_method.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (I001)

maths/numerical_analysis/brents_method.py:13:1: I001 Import block is un-sorted or un-formatted

Check failure on line 13 in maths/numerical_analysis/brents_method.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (I001)

maths/numerical_analysis/brents_method.py:13:1: I001 Import block is un-sorted or un-formatted

Check failure on line 13 in maths/numerical_analysis/brents_method.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (I001)

maths/numerical_analysis/brents_method.py:13:1: I001 Import block is un-sorted or un-formatted

def brent_method(
function: Callable[[float], float],
lower: float,
upper: float,
tolerance: float = 1e-14,
max_iterations: int = 100,
) -> float:
"""
Find a root of a function in a bracketing interval using Brent's method.

Args:
function: A continuous function for which the root is sought.
lower: One end of the bracketing interval.
upper: The other end of the bracketing interval.
tolerance: The tolerance for convergence (default 1e-14).
max_iterations: Maximum number of iterations (default 100).

Returns:
A float value approximating the root.

Raises:
ValueError: If the root is not bracketed in [lower, upper].

Examples:
>>> brent_method(lambda x: x**3 - 1, -5, 5)
1.0
>>> brent_method(lambda x: x**2 - 4*x + 3, 0, 2)
1.0
>>> brent_method(lambda x: x**2 - 4*x + 3, 2, 4)
3.0
>>> brent_method(lambda x: x**2 - 4*x + 3, 4, 1000)
Traceback (most recent call last):
...
ValueError: Root is not bracketed in the interval [4, 1000].
"""
fa = function(lower)
fb = function(upper)
if fa * fb >= 0:
raise ValueError(f"Root is not bracketed in the interval [{lower}, {upper}].")

Check failure on line 53 in maths/numerical_analysis/brents_method.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (EM102)

maths/numerical_analysis/brents_method.py:53:26: EM102 Exception must not use an f-string literal, assign to variable first

Check failure on line 53 in maths/numerical_analysis/brents_method.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (EM102)

maths/numerical_analysis/brents_method.py:53:26: EM102 Exception must not use an f-string literal, assign to variable first

Check failure on line 53 in maths/numerical_analysis/brents_method.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (EM102)

maths/numerical_analysis/brents_method.py:53:26: EM102 Exception must not use an f-string literal, assign to variable first

if abs(fa) < abs(fb):
lower, upper = upper, lower
fa, fb = fb, fa

c = lower
fc = fa
d = upper - lower
mflag = True

for _ in range(max_iterations):
if fb == 0:
return upper
if fc not in {fa, fb}:
# Inverse quadratic interpolation
s = (
lower * fb * fc / ((fa - fb) * (fa - fc))
+ upper * fa * fc / ((fb - fa) * (fb - fc))
+ c * fa * fb / ((fc - fa) * (fc - fb))
)
else:
# Secant method
s = upper - fb * (upper - lower) / (fb - fa)

conditions = [
not ((3 * lower + upper) / 4 < s < upper if upper > lower else upper < s < (3 * lower + upper) / 4),

Check failure on line 79 in maths/numerical_analysis/brents_method.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

maths/numerical_analysis/brents_method.py:79:89: E501 Line too long (112 > 88)

Check failure on line 79 in maths/numerical_analysis/brents_method.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

maths/numerical_analysis/brents_method.py:79:89: E501 Line too long (112 > 88)

Check failure on line 79 in maths/numerical_analysis/brents_method.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

maths/numerical_analysis/brents_method.py:79:89: E501 Line too long (112 > 88)
mflag and abs(s - upper) >= abs(upper - c) / 2,
not mflag and abs(s - upper) >= abs(c - d) / 2,
mflag and abs(upper - c) < tolerance,
not mflag and abs(c - d) < tolerance,
]
if any(conditions):
s = (lower + upper) / 2
mflag = True
else:
mflag = False

fs = function(s)
d, c = c, upper
fc = fb

if fa * fs < 0:
upper = s
fb = fs
else:
lower = s
fa = fs

if abs(fa) < abs(fb):
lower, upper = upper, lower
fa, fb = fb, fa

if abs(upper - lower) < tolerance or fb == 0:
return upper

return upper


if __name__ == "__main__":
import doctest
doctest.testmod()

Check failure on line 114 in maths/numerical_analysis/brents_method.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W292)

maths/numerical_analysis/brents_method.py:114:22: W292 No newline at end of file

Check failure on line 114 in maths/numerical_analysis/brents_method.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W292)

maths/numerical_analysis/brents_method.py:114:22: W292 No newline at end of file

Check failure on line 114 in maths/numerical_analysis/brents_method.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W292)

maths/numerical_analysis/brents_method.py:114:22: W292 No newline at end of file
Loading