From 8489f90acf167d54124112db7b7891314aeeb8ea Mon Sep 17 00:00:00 2001 From: debesh Date: Thu, 2 Oct 2025 00:14:31 +0530 Subject: [PATCH 1/6] Add Brent's Method for root finding (numerical analysis) --- maths/brent_method.py | 115 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 maths/brent_method.py diff --git a/maths/brent_method.py b/maths/brent_method.py new file mode 100644 index 000000000000..cdf52a8b7d49 --- /dev/null +++ b/maths/brent_method.py @@ -0,0 +1,115 @@ +from typing import Callable + +def brent_method( + f: Callable[[float], float], + a: float, + b: float, + tol: float = 1e-8, + max_iter: int = 100 +) -> float: + """ + Find the root of function f in the interval [a, b] using Brent's Method. + + Brent's Method combines bisection, secant, and inverse quadratic interpolation. + + Parameters + ---------- + f : Callable[[float], float] + Function for which to find the root. + a : float + Left endpoint of interval. + b : float + Right endpoint of interval. + tol : float + Tolerance for convergence (default 1e-8). + max_iter : int + Maximum number of iterations (default 100). + + Returns + ------- + float + Approximate root of f in [a, b]. + + Raises + ------ + ValueError + If f(a) and f(b) do not have opposite signs. + + Examples + -------- + >>> def func(x): return x**3 - x - 2 + >>> round(brent_method(func, 1, 2), 5) + 1.52138 + + >>> def func2(x): return x**2 + 1 + >>> brent_method(func2, 0, 1) + Traceback (most recent call last): + ... + ValueError: f(a) and f(b) must have opposite signs + """ + fa = f(a) + fb = f(b) + + if fa * fb >= 0: + raise ValueError("f(a) and f(b) must have opposite signs") + + if abs(fa) < abs(fb): + a, b = b, a + fa, fb = fb, fa + + c = a + fc = fa + d = e = b - a + + for iteration in range(max_iter): + if fb == 0: + return b + + if fa != fc and fb != fc: + # Inverse quadratic interpolation + s = ( + a * fb * fc / ((fa - fb) * (fa - fc)) + + b * fa * fc / ((fb - fa) * (fb - fc)) + + c * fa * fb / ((fc - fa) * (fc - fb)) + ) + else: + # Secant method + s = b - fb * (b - a) / (fb - fa) + + conditions = [ + not ((3 * a + b) / 4 < s < b) if b > a else not (b < s < (3 * a + b) / 4), + iteration > 1 and abs(s - b) >= abs(b - c) / 2, + iteration <= 1 and abs(s - b) >= abs(c - d) / 2, + iteration > 1 and abs(b - c) < tol, + iteration <= 1 and abs(c - d) < tol, + ] + + if any(conditions): + # Bisection fallback + s = (a + b) / 2 + d = e = b - a + + fs = f(s) + d, c = c, b + fc = fb + + if fa * fs < 0: + b = s + fb = fs + else: + a = s + fa = fs + + if abs(fa) < abs(fb): + a, b = b, a + fa, fb = fb, fa + + if abs(b - a) < tol: + return b + + # If we reach max iterations + return b +if __name__ == "__main__": + import doctest + doctest.testmod(verbose=True) + From a14bceb042d78be7e9b46a2eae8447392b6128fa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 18:53:20 +0000 Subject: [PATCH 2/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/brent_method.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/maths/brent_method.py b/maths/brent_method.py index cdf52a8b7d49..02e42c291fab 100644 --- a/maths/brent_method.py +++ b/maths/brent_method.py @@ -1,17 +1,18 @@ from typing import Callable + def brent_method( f: Callable[[float], float], a: float, b: float, tol: float = 1e-8, - max_iter: int = 100 + max_iter: int = 100, ) -> float: """ Find the root of function f in the interval [a, b] using Brent's Method. - + Brent's Method combines bisection, secant, and inverse quadratic interpolation. - + Parameters ---------- f : Callable[[float], float] @@ -24,17 +25,17 @@ def brent_method( Tolerance for convergence (default 1e-8). max_iter : int Maximum number of iterations (default 100). - + Returns ------- float Approximate root of f in [a, b]. - + Raises ------ ValueError If f(a) and f(b) do not have opposite signs. - + Examples -------- >>> def func(x): return x**3 - x - 2 @@ -109,7 +110,9 @@ def brent_method( # If we reach max iterations return b + + if __name__ == "__main__": import doctest - doctest.testmod(verbose=True) + doctest.testmod(verbose=True) From cdaeabe343567eaa7c35ad52a30c42976951c0c6 Mon Sep 17 00:00:00 2001 From: debesh Date: Thu, 2 Oct 2025 00:33:06 +0530 Subject: [PATCH 3/6] Fix parameter names and lint issues in Brent's Method --- maths/brent_method.py | 105 +++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/maths/brent_method.py b/maths/brent_method.py index 02e42c291fab..dddd46ff3501 100644 --- a/maths/brent_method.py +++ b/maths/brent_method.py @@ -2,114 +2,115 @@ def brent_method( - f: Callable[[float], float], - a: float, - b: float, + func: Callable[[float], float], + left: float, + right: float, tol: float = 1e-8, max_iter: int = 100, ) -> float: """ - Find the root of function f in the interval [a, b] using Brent's Method. + Find the root of function func in the interval [left, right] using Brent's Method. Brent's Method combines bisection, secant, and inverse quadratic interpolation. + Parameters ---------- - f : Callable[[float], float] + func : Callable[[float], float] Function for which to find the root. - a : float + left : float Left endpoint of interval. - b : float + right : float Right endpoint of interval. tol : float Tolerance for convergence (default 1e-8). max_iter : int Maximum number of iterations (default 100). + Returns ------- float - Approximate root of f in [a, b]. + Approximate root of func in [left, right]. Raises ------ ValueError - If f(a) and f(b) do not have opposite signs. + If func(left) and func(right) do not have opposite signs. Examples -------- - >>> def func(x): return x**3 - x - 2 - >>> round(brent_method(func, 1, 2), 5) + >>> def f(x): return x**3 - x - 2 + >>> round(brent_method(f, 1, 2), 5) 1.52138 - >>> def func2(x): return x**2 + 1 - >>> brent_method(func2, 0, 1) + >>> def f2(x): return x**2 + 1 + >>> brent_method(f2, 0, 1) Traceback (most recent call last): ... - ValueError: f(a) and f(b) must have opposite signs + ValueError: func(left) and func(right) must have opposite signs """ - fa = f(a) - fb = f(b) + fl = func(left) + fr = func(right) - if fa * fb >= 0: - raise ValueError("f(a) and f(b) must have opposite signs") + if fl * fr >= 0: + raise ValueError("func(left) and func(right) must have opposite signs") - if abs(fa) < abs(fb): - a, b = b, a - fa, fb = fb, fa + if abs(fl) < abs(fr): + left, right = right, left + fl, fr = fr, fl - c = a - fc = fa - d = e = b - a + c = left + fc = fl + d = right - left for iteration in range(max_iter): - if fb == 0: - return b + if fr == 0: + return right - if fa != fc and fb != fc: + if fc not in (fl, fr): # Inverse quadratic interpolation s = ( - a * fb * fc / ((fa - fb) * (fa - fc)) - + b * fa * fc / ((fb - fa) * (fb - fc)) - + c * fa * fb / ((fc - fa) * (fc - fb)) + left * fr * fc / ((fl - fr) * (fl - fc)) + + right * fl * fc / ((fr - fl) * (fr - fc)) + + c * fl * fr / ((fc - fl) * (fc - fr)) ) else: # Secant method - s = b - fb * (b - a) / (fb - fa) + s = right - fr * (right - left) / (fr - fl) conditions = [ - not ((3 * a + b) / 4 < s < b) if b > a else not (b < s < (3 * a + b) / 4), - iteration > 1 and abs(s - b) >= abs(b - c) / 2, - iteration <= 1 and abs(s - b) >= abs(c - d) / 2, - iteration > 1 and abs(b - c) < tol, + not ((3 * left + right) / 4 < s < right) if right > left else not (right < s < (3 * left + right) / 4), + iteration > 1 and abs(s - right) >= abs(right - c) / 2, + iteration <= 1 and abs(s - right) >= abs(c - d) / 2, + iteration > 1 and abs(right - c) < tol, iteration <= 1 and abs(c - d) < tol, ] if any(conditions): # Bisection fallback - s = (a + b) / 2 - d = e = b - a + s = (left + right) / 2 + d = right - left - fs = f(s) - d, c = c, b - fc = fb + fs = func(s) + d, c = c, right + fc = fr - if fa * fs < 0: - b = s - fb = fs + if fl * fs < 0: + right = s + fr = fs else: - a = s - fa = fs + left = s + fl = fs - if abs(fa) < abs(fb): - a, b = b, a - fa, fb = fb, fa + if abs(fl) < abs(fr): + left, right = right, left + fl, fr = fr, fl - if abs(b - a) < tol: - return b + if abs(right - left) < tol: + return right - # If we reach max iterations - return b + return right if __name__ == "__main__": From c623ce6629e63ddda3b731543388d65a113c234f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:06:15 +0000 Subject: [PATCH 4/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/brent_method.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/maths/brent_method.py b/maths/brent_method.py index dddd46ff3501..a84b595a69ed 100644 --- a/maths/brent_method.py +++ b/maths/brent_method.py @@ -80,7 +80,9 @@ def brent_method( s = right - fr * (right - left) / (fr - fl) conditions = [ - not ((3 * left + right) / 4 < s < right) if right > left else not (right < s < (3 * left + right) / 4), + not ((3 * left + right) / 4 < s < right) + if right > left + else not (right < s < (3 * left + right) / 4), iteration > 1 and abs(s - right) >= abs(right - c) / 2, iteration <= 1 and abs(s - right) >= abs(c - d) / 2, iteration > 1 and abs(right - c) < tol, From a531af57e865e46ac44c315c0242bb782286743b Mon Sep 17 00:00:00 2001 From: debesh Date: Thu, 2 Oct 2025 00:39:50 +0530 Subject: [PATCH 5/6] Fix import name --- maths/brent_method.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/maths/brent_method.py b/maths/brent_method.py index dddd46ff3501..5e26e30ec9af 100644 --- a/maths/brent_method.py +++ b/maths/brent_method.py @@ -1,4 +1,5 @@ -from typing import Callable +from collections.abc import Callable + def brent_method( From b6902519f962cf315d62953f4077e5682e324777 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:10:27 +0000 Subject: [PATCH 6/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- maths/brent_method.py | 1 - 1 file changed, 1 deletion(-) diff --git a/maths/brent_method.py b/maths/brent_method.py index 7e3acfe27cac..8cefe564632f 100644 --- a/maths/brent_method.py +++ b/maths/brent_method.py @@ -1,7 +1,6 @@ from collections.abc import Callable - def brent_method( func: Callable[[float], float], left: float,