A fast and robust root-finding library written in C for Python, using the Modified Anderson-Bjork method:
Ganchovski, N.; Smith, O.; Rackauckas, C.; Tomov, L.; Traykov, A.
Improvements to the Modified Anderson–Björck(modAB) Root-Finding Algorithm.
Algorithms 2026, 19, 332. https://doi.org/10.3390/a19050332
It finds the root of a single nonlinear equation f(x) = 0 within the specified interval [x1, x2].
Installation
pip install pymodab
Usage
import math
from pymodab import find_root, get_evaluation_count
# Find the root of cos(x) - x = 0 in [0, 1]
root = find_root(lambda x: math.cos(x) - x, 0, 1, 1e-3, 1e-3, 10)
print(f"Root: {root}") # 0.7390851332086904
# Get the number of function evaluations
print(f"Evaluations: {get_evaluation_count()}")
print(f"Error: {math.cos(root) - root}")
# Using default tolerances
root = find_root(lambda x: x**2 - 2, 1, 2)
print(f"sqrt(2) = {root}") # 1.414213562373095
# Get the number of function evaluations
print(f"Evaluations: {get_evaluation_count()}")
print(f"Error: {root**2 - 2}")
API
find_root(f, x1, x2, atol=1e-14, rtol=1e-14, max_iter=200)
Find the root of f(x) = 0 within the interval [x1, x2].
Parameters:
f: A continuous function of one variable, or the address (int) of a compiled C functiondouble f(double)x1,x2: Bracket interval endpoints (must satisfyf(x1) * f(x2) < 0)atol: Absolute tolerance (default: 1e-14)rtol: Relative tolerance (default: 1e-14)max_iter: Maximum iterations (default: 200)
Returns: The root, or NaN if not found. Exceptions raised by f are propagated to the caller. This holds for the native extension only: with the ctypes fallback (pymodab.NATIVE is False), an exception in f is only printed to stderr, the search goes on with an undefined value of f(x), and the result cannot be trusted; it may be NaN or a wrong root.
Since version 1.0.6, find_root is a native CPython extension that calls f directly, which is about 2x faster than the previous ctypes wrapper. Version 1.0.7 ships it as a wheel for Windows, Linux (x86_64 and aarch64, glibc and musl) and macOS (Intel and Apple Silicon); one wheel serves every CPython from 3.8 up. On any other platform pymodab falls back to ctypes automatically; pymodab.NATIVE tells which one is used.
Version 1.0.8 improves the fallback from secant to bisection steps: a secant step that does not shrink the bracket fast enough is still kept if it at least halves the best residual of the bracket. This avoids needless bisection steps when the secant converges from one side, and falls back sooner when it stalls. get_evaluation_count() now returns the exact number of calls to f. Version 1.0.9 ships that work as wheels for every supported platform.
Version 1.0.10 adds overflow and NaN safeguards to the arithmetic. The midpoint and the secant point are computed without forming x1 + x2 or x1*y2 - x2*y1, so they stay finite and inside the bracket even for endpoints or residuals near the floating-point limits. A NaN from f stops the search with NaN instead of corrupting the bracket. The Anderson-Björck scaling never lets an auxiliary ordinate underflow to zero. The new secant formula rounds differently, so single problems may need a few evaluations more or less; the 100 benchmark problems below need 1895 in total instead of 1897.
Version 1.1.0 simplifies these safeguards without weakening them. The test that switches from bisection to secant steps is now computed inline as |ym - y3| < k·|ym| + k·|y3|, which cannot overflow, and it is skipped while f(x2) - f(x1) overflows, so the search keeps bisecting until the residuals shrink. The bracket is updated from the sign of the true residuals instead of the Anderson-Björck auxiliary ordinates, so an auxiliary ordinate that underflows to zero can no longer steer the update, and the extra underflow guard of 1.0.10 is no longer needed. The 100 benchmark problems give the same roots and evaluation counts as with 1.0.10.
Version 1.1.1 separates the bisection and Anderson-Björck steps in the solver loop. A bisection step now updates only the bracket and the true residuals. The Anderson-Björck corrections and the fallback test run only on secant steps, and the auxiliary ordinates start from the true residuals whenever the search switches to secant steps. It also caps the residual exception of 1.0.8: a secant step that does not shrink the bracket fast enough is kept on the strength of halving the residual for at most three consecutive steps (MAX_RESIDUAL_STEPS), after which the search falls back to bisection anyway. Without the cap, f(x) = -x*x for x < 0 and 7*x*x for x >= 0 on [-π, 1], a counterexample by L. Tomov, keeps halving the residual while the bracket never gets narrower than 1, and the search runs out of iterations; with the cap it converges in 76 evaluations. This function is now problem f101 of the benchmark. The other 100 problems give the same roots and evaluation counts as with 1.1.0; all 101 need 1971 evaluations in total.
For maximum speed, pass a compiled function, e.g. from numba. It is then called from C with no Python overhead:
import math
from numba import cfunc
from pymodab import find_root
@cfunc("float64(float64)")
def f(x):
return math.cos(x) - x
root = find_root(f.address, 0, 1)
get_evaluation_count()
Returns the number of function evaluations from the last root-finding call.
Algorithm
Modified Anderson-Björck's method is a new robust and efficient bracketing root-finding algorithm. It combines bisection with Anderson-Björk's method to achieve both fast performance and worst-case optimality.
References:
Ganchovski N.; Traykov A. Modified Anderson-Björck's method for solving non-linear equations in structural mechanics. IOP Conference Series: Materials Science and Engineering 2023, 1276 (1) 012010, IOP Publishing.
https://iopscience.iop.org/article/10.1088/1757-899X/1276/1/012010/pdf
Ganchovski, N.; Smith, O.; Rackauckas, C.; Tomov, L.; Traykov, A. Improvements to the Modified Anderson–Björck (modAB) Root-Finding Algorithm. Algorithms 2026, 19, 332. https://doi.org/10.3390/a19050332
License
MIT License
Benchmark results
The modAB algorithm is benchmarked against the available algorithms in Python/SciPy in respect to number of evaluations and execution times:
bisect- Bisection methodbrentq- Brent’s method (van Wijngaarden–Dekker–Brent, 1973)brenth- Brent–Dekker variant (hyperbolic extrapolation variant, 1975)ridder- Ridder’s method (1979)toms748- Alefeld–Potra–Shi method (1995 - TOMS Algorithm 748)chandr- Chandrupatla's method (1997) -scipy.optimize.elementwise.find_rootcybrentq- Cython implementation of brentq by Gledis CaushajmodAB_SG- Safeguarded Modified Anderson Bjork's method (Ganchovski & Traykov, 2023; improved 2026 by L.Tomov and N.Ganchovski)modAB_ct- the old version of pymodab 1.0.5 implemented with ctypes
Function evaluations
| Func | bisect | brentq | brenth | ridder | chandr | cybrentq | modAB_ct | modAB_SG |
|---|---|---|---|---|---|---|---|---|
| SUM | 4883 | 2810 | 2769 | 3425 | 2112 | 2810 | 1993 | 1971 |
| AVG | 48 | 28 | 27 | 34 | 21 | 28 | 20 | 20 |
| MEDIAN | 49 | 13 | 12 | 16 | 12 | 13 | 13 | 13 |
| MIN | 3 | 4 | 4 | 3 | 3 | 4 | 3 | 3 |
| MAX | 53 | 102 | 102 | 202 | 70 | 102 | 78 | 76 |
| FACTOR | 2.477x | 1.426x | 1.405x | 1.738x | 1.072x | 1.426x | 1.011x | 1.000x |
Execution times (ms per problem, 200 iterations)
| Func | bisect | brentq | brenth | ridder | chandr | cybrentq | modAB_ct | modAB_SG |
|---|---|---|---|---|---|---|---|---|
| SUM | 1385.66 | 841.73 | 814.23 | 976.14 | 57729.06 | 114.57 | 165.90 | 87.27 |
| AVG | 13.7194 | 8.3340 | 8.0617 | 9.6647 | 571.5749 | 1.1344 | 1.6426 | 0.8641 |
| MEDIAN | 13.9073 | 4.3587 | 4.2225 | 5.4357 | 336.8105 | 0.6225 | 1.3061 | 0.6450 |
| MIN | 1.4121 | 1.6385 | 1.7789 | 1.3487 | 64.7960 | 0.1986 | 0.5643 | 0.1897 |
| MAX | 17.2491 | 31.5796 | 32.1298 | 53.2028 | 2002.6788 | 5.4763 | 4.3412 | 2.7929 |
| FACTOR | 15.877x | 9.645x | 9.330x | 11.185x | 661.479x | 1.313x | 1.901x | 1.000x |
Notes:
Last Run on: 29.09.2026
Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz (1.50 GHz) with 16.0 GB RAM
Windows 11 Home
Python Version: 3.14.7
numpy Version: 2.4.6
scipy Version: 1.18.0
cybrentq Version: 0.1.5 - by Gledis Caushaj (https://github.com/gledi-ai/cybrentq)
modAB_ct: pymodab version 1.0.5 from PyPI - the previous implementation with ctypes
modAB_SG: pymodab version 1.1.1 - the latest implementation of the safeguarded modab as native C extension
Metadata
Release files for pymodab 1.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pymodab-1.1.1.tar.gz | 120.6 kB | Without provenance | Details for pymodab-1.1.1.tar.gz |
Built distributions (wheels)
Total release size: 348.5 kB
Release files / pymodab-1.1.1.tar.gz
| Download URL | pymodab-1.1.1.tar.gz |
|---|---|
| Size | 120.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9ad331bd2af2f4ca6af5746384d0892d2a3e86335af38e159f1e88d7c64b65ba
|
|
BLAKE2b-256 checksum How to use checksums |
16a0cd523e590cf636e7fa74d99505f7876f1a2fedced0da9dfe1fe56988b9f7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.7
|
Release files / pymodab-1.1.1-py3-none-any.whl
| Download URL | pymodab-1.1.1-py3-none-any.whl |
|---|---|
| Size | 110.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b6cd03d1920f94d59a7cc4c32539621a9bfa14c208bc9cbc5d4de881731d9198
|
|
BLAKE2b-256 checksum How to use checksums |
810928af14f92c553c6c471cc9df53e24965b51b9f1d96ad0a266f5428927b62
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.7
|
Release files / pymodab-1.1.1-cp38-abi3-win_amd64.whl
| Download URL | pymodab-1.1.1-cp38-abi3-win_amd64.whl |
|---|---|
| Size | 15.3 kB |
| Tags | CPython 3.8 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
c03d0c5f3bdedc2ec4b193cf5f7577d169082161e765126a2e9f1e66a6091cec
|
|
BLAKE2b-256 checksum How to use checksums |
e406a869eff14786555603b9e2b088aa5f708ff574842f0eea31dc1452ede569
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.7
|
Release files / pymodab-1.1.1-cp38-abi3-musllinux_1_2_x86_64.whl
| Download URL | pymodab-1.1.1-cp38-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 21.3 kB |
| Tags | CPython 3.8 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
e190e4fe50d5ed304446181059d62632073570365027c1f85fa54187d7a43d58
|
|
BLAKE2b-256 checksum How to use checksums |
dcb3febb7624917bdaca6864f18bb8ff7dee7707766fcadb0fa693b7d7f6592a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.7
|
Release files / pymodab-1.1.1-cp38-abi3-musllinux_1_2_aarch64.whl
| Download URL | pymodab-1.1.1-cp38-abi3-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 21.6 kB |
| Tags | CPython 3.8 Linux musl 1.2+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
f7f7a48174239dae339d51b9050973af655c19b4d352ef8237d329a98209cf9d
|
|
BLAKE2b-256 checksum How to use checksums |
85c17626e1380a29202aa5c2ffb3d941ed322ef22cbeee6548c362d256971420
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.7
|
Release files / pymodab-1.1.1-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
| Download URL | pymodab-1.1.1-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl |
|---|---|
| Size | 21.4 kB |
| Tags | CPython 3.8 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
ae08a589fbfea79fb6351f8832e5221ee5f7694754c5aacf3aac8e6aac007a2f
|
|
BLAKE2b-256 checksum How to use checksums |
b9ed69d819e24e4e7c3fda94a9dd33ee2afdc336d70407bce793ed30fbc498a4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.7
|
Release files / pymodab-1.1.1-cp38-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl
| Download URL | pymodab-1.1.1-cp38-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl |
|---|---|
| Size | 20.6 kB |
| Tags | CPython 3.8 Linux glibc 2.28+ x86-64 Linux glibc 2.5+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
57be8d1728f9ac66021728306e49d854c3346931ab42a076f0151f4c4878c678
|
|
BLAKE2b-256 checksum How to use checksums |
78cc91ad135f50f8cdf6e4d8bbad46c6aa7d93669f74e318698e622cdbd84f86
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.7
|
Release files / pymodab-1.1.1-cp38-abi3-macosx_10_9_universal2.whl
| Download URL | pymodab-1.1.1-cp38-abi3-macosx_10_9_universal2.whl |
|---|---|
| Size | 17.6 kB |
| Tags | CPython 3.8 abi3 macOS 10.9+ universal2 (ARM64, x86-64) |
|
SHA-256 checksum How to use checksums |
768822fc02f0aa30db8c19984488a2f434349f4dc23694183669d3db6589da41
|
|
BLAKE2b-256 checksum How to use checksums |
f9c594de2e13fff597c9c285f7230522a4ecf6eb6bef9161b65a47d06d19eb0e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.7
|
Security for pymodab 1.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.