pymodab 1.1.2__tar.gz → 1.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pymodab
3
- Version: 1.1.2
3
+ Version: 1.1.3
4
4
  Summary: Fast and robust root-finding library using the Modified Anderson-Bjork method
5
5
  Author: Ganchovski, N.; Smith, O.; Rackauckas, C.; Tomov, L.; Traykov, A.
6
6
  License-Expression: MIT
@@ -90,6 +90,8 @@ Version 1.1.1 separates the bisection and Anderson-Björck steps in the solver l
90
90
 
91
91
  Version 1.1.2 streamlines the solver loop. The bracket width and the difference of the true residuals are computed once per step and reused. The exits on a zero and on a `NaN` residual share a single test on the hot path. The best true residual of the bracket, which the fallback test needs, is now computed only when a secant step fails to shrink the bracket fast enough, instead of on every secant step, and the test is written as `2·|y3| >= ymin` instead of `|y3| >= 0.5·ymin`. The 101 benchmark problems give the same roots and evaluation counts as with 1.1.1.
92
92
 
93
+ Version 1.1.3 replaces the capped residual exception of 1.1.1 with a stricter test and removes the `MAX_RESIDUAL_STEPS` counter. A secant step that does not shrink the bracket fast enough is now kept only if it reduces the residual more than 42 times, that is `42·|y3| < ymin`, where `ymin` is the smaller true residual of the bracket. The factor comes from 2^(1074/200) ≈ 41.4: at this rate a residual that starts near 1 underflows to zero within the default limit of 200 iterations, because the smallest positive double is 2^-1074. A search that stays on secant steps this way therefore ends by itself, without a counter. The factor is a fixed constant and does not follow `max_iter`, so raising the limit does not weaken the test. On the benchmark, problem f101 now needs 65 evaluations instead of 76, and the other 100 problems give the same roots and evaluation counts as with 1.1.2; all 101 need 1960 evaluations in total instead of 1971.
94
+
93
95
  For maximum speed, pass a compiled function, e.g. from [numba](https://numba.pydata.org/). It is then called from C with no Python overhead:
94
96
 
95
97
  ```python
@@ -142,27 +144,27 @@ The modAB algorithm is benchmarked against the available algorithms in Python/Sc
142
144
 
143
145
  Func| bisect| brentq| brenth| ridder| chandr| cybrentq| modAB_ct| <mark>**modAB_SG**</mark>|
144
146
  ----- | ------: | ------: | ------: | ------: | ------: | ------: | ------: | ------: |
145
- SUM| 4883| 2810| 2769| 3425| 2112| 2810| 1993| <mark>**1971**</mark>|
146
- AVG| 48| 28| 27| 34| 21| 28| 20| <mark>**20**</mark>|
147
+ SUM| 4883| 2810| 2769| 3425| 2112| 2810| 1993| <mark>**1960**</mark>|
148
+ AVG| 48| 28| 27| 34| 21| 28| 20| <mark>**19**</mark>|
147
149
  MEDIAN| 49| 13| 12| 16| 12| 13| 13| <mark>**13**</mark>|
148
150
  MIN| 3| 4| 4| 3| 3| 4| 3| <mark>**3**</mark>|
149
- MAX| 53| 102| 102| 202| 70| 102| 78| <mark>**76**</mark>|
150
- FACTOR| 2.477x| 1.426x| 1.405x| 1.738x| 1.072x| 1.426x| 1.011x| <mark>**1.000x**</mark>|
151
+ MAX| 53| 102| 102| 202| 70| 102| 78| <mark>**65**</mark>|
152
+ FACTOR| 2.491x| 1.434x| 1.413x| 1.747x| 1.078x| 1.434x| 1.017x| <mark>**1.000x**</mark>|
151
153
 
152
154
  #### Execution times (ms per problem, 200 iterations)
153
155
 
154
156
  Func| bisect| brentq| brenth| ridder| chandr| cybrentq| modAB_ct| <mark>**modAB_SG**</mark>|
155
157
  ----- | ------: | ------: | ------: | ------: | ------: | ------: | ------: | ------: |
156
- SUM| 1497.81| 888.81| 878.13| 1103.39| 62435.64| 118.77| 176.71|<mark>**90.70**</mark>|
157
- AVG| 14.8298| 8.8001| 8.6944| 10.9246| 618.1747| 1.1759| 1.7496|<mark>**0.8981**</mark>|
158
- MEDIAN| 14.1046| 4.5237| 4.4239| 5.5108| 366.5141| 0.6629| 1.3385|<mark>**0.6881**</mark>|
159
- MIN| 1.6523| 1.6333| 1.8243| 1.4128| 68.0820| 0.1926| 0.5485|<mark>**0.1625**</mark>|
160
- MAX| 39.1626| 36.7848| 31.6445| 121.3956| 2159.4610| 5.8503| 5.9590|<mark>**3.2718**</mark>|
161
- FACTOR| 16.513x| 9.799x| 9.681x| 12.165x| 688.344x| 1.309x| 1.948x|<mark>**1.000x**</mark>|
158
+ SUM| 1535.42| 931.31| 878.87| 1047.95| 58524.74| 115.55| 175.60| <mark>**91.23**</mark>|
159
+ AVG| 15.2021| 9.2209| 8.7017| 10.3758| 579.4528| 1.1440| 1.7386| <mark>**0.9033**</mark>|
160
+ MEDIAN| 14.7569| 5.1910| 4.9383| 6.4764| 345.7968| 0.5879| 1.3410| <mark>**0.6511**</mark>|
161
+ MIN| 1.7242| 1.8918| 1.8852| 2.0780| 73.8264| 0.2071| 0.5655| <mark>**0.1677**</mark>|
162
+ MAX| 70.1038| 32.1594| 41.2759| 53.5572|1979.9811| 6.4304| 5.1045| <mark>**3.8393**</mark>|
163
+ FACTOR| 16.829x| 10.208x| 9.633x| 11.486x| 641.480x| 1.266x| 1.925x| <mark>**1.000x**</mark>|
162
164
 
163
165
  #### Notes:
164
166
 
165
- Last Run on: 06.10.2026
167
+ Last Run on: 01.11.2026
166
168
  Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz (1.50 GHz) with 16.0 GB RAM
167
169
  Windows 11 Home
168
170
  Python Version: 3.14.7
@@ -170,4 +172,4 @@ numpy Version: 2.4.6
170
172
  scipy Version: 1.18.0
171
173
  cybrentq Version: 0.1.5 - by Gledis Caushaj (https://github.com/gledi-ai/cybrentq)
172
174
  modAB_ct: pymodab version 1.0.5 from PyPI - the previous implementation with ctypes
173
- modAB_SG: pymodab version 1.1.2 - the latest implementation of the safeguarded modab as native C extension
175
+ modAB_SG: pymodab version 1.1.3 - the latest implementation of the safeguarded modab as native C extension
@@ -60,6 +60,8 @@ Version 1.1.1 separates the bisection and Anderson-Björck steps in the solver l
60
60
 
61
61
  Version 1.1.2 streamlines the solver loop. The bracket width and the difference of the true residuals are computed once per step and reused. The exits on a zero and on a `NaN` residual share a single test on the hot path. The best true residual of the bracket, which the fallback test needs, is now computed only when a secant step fails to shrink the bracket fast enough, instead of on every secant step, and the test is written as `2·|y3| >= ymin` instead of `|y3| >= 0.5·ymin`. The 101 benchmark problems give the same roots and evaluation counts as with 1.1.1.
62
62
 
63
+ Version 1.1.3 replaces the capped residual exception of 1.1.1 with a stricter test and removes the `MAX_RESIDUAL_STEPS` counter. A secant step that does not shrink the bracket fast enough is now kept only if it reduces the residual more than 42 times, that is `42·|y3| < ymin`, where `ymin` is the smaller true residual of the bracket. The factor comes from 2^(1074/200) ≈ 41.4: at this rate a residual that starts near 1 underflows to zero within the default limit of 200 iterations, because the smallest positive double is 2^-1074. A search that stays on secant steps this way therefore ends by itself, without a counter. The factor is a fixed constant and does not follow `max_iter`, so raising the limit does not weaken the test. On the benchmark, problem f101 now needs 65 evaluations instead of 76, and the other 100 problems give the same roots and evaluation counts as with 1.1.2; all 101 need 1960 evaluations in total instead of 1971.
64
+
63
65
  For maximum speed, pass a compiled function, e.g. from [numba](https://numba.pydata.org/). It is then called from C with no Python overhead:
64
66
 
65
67
  ```python
@@ -112,27 +114,27 @@ The modAB algorithm is benchmarked against the available algorithms in Python/Sc
112
114
 
113
115
  Func| bisect| brentq| brenth| ridder| chandr| cybrentq| modAB_ct| <mark>**modAB_SG**</mark>|
114
116
  ----- | ------: | ------: | ------: | ------: | ------: | ------: | ------: | ------: |
115
- SUM| 4883| 2810| 2769| 3425| 2112| 2810| 1993| <mark>**1971**</mark>|
116
- AVG| 48| 28| 27| 34| 21| 28| 20| <mark>**20**</mark>|
117
+ SUM| 4883| 2810| 2769| 3425| 2112| 2810| 1993| <mark>**1960**</mark>|
118
+ AVG| 48| 28| 27| 34| 21| 28| 20| <mark>**19**</mark>|
117
119
  MEDIAN| 49| 13| 12| 16| 12| 13| 13| <mark>**13**</mark>|
118
120
  MIN| 3| 4| 4| 3| 3| 4| 3| <mark>**3**</mark>|
119
- MAX| 53| 102| 102| 202| 70| 102| 78| <mark>**76**</mark>|
120
- FACTOR| 2.477x| 1.426x| 1.405x| 1.738x| 1.072x| 1.426x| 1.011x| <mark>**1.000x**</mark>|
121
+ MAX| 53| 102| 102| 202| 70| 102| 78| <mark>**65**</mark>|
122
+ FACTOR| 2.491x| 1.434x| 1.413x| 1.747x| 1.078x| 1.434x| 1.017x| <mark>**1.000x**</mark>|
121
123
 
122
124
  #### Execution times (ms per problem, 200 iterations)
123
125
 
124
126
  Func| bisect| brentq| brenth| ridder| chandr| cybrentq| modAB_ct| <mark>**modAB_SG**</mark>|
125
127
  ----- | ------: | ------: | ------: | ------: | ------: | ------: | ------: | ------: |
126
- SUM| 1497.81| 888.81| 878.13| 1103.39| 62435.64| 118.77| 176.71|<mark>**90.70**</mark>|
127
- AVG| 14.8298| 8.8001| 8.6944| 10.9246| 618.1747| 1.1759| 1.7496|<mark>**0.8981**</mark>|
128
- MEDIAN| 14.1046| 4.5237| 4.4239| 5.5108| 366.5141| 0.6629| 1.3385|<mark>**0.6881**</mark>|
129
- MIN| 1.6523| 1.6333| 1.8243| 1.4128| 68.0820| 0.1926| 0.5485|<mark>**0.1625**</mark>|
130
- MAX| 39.1626| 36.7848| 31.6445| 121.3956| 2159.4610| 5.8503| 5.9590|<mark>**3.2718**</mark>|
131
- FACTOR| 16.513x| 9.799x| 9.681x| 12.165x| 688.344x| 1.309x| 1.948x|<mark>**1.000x**</mark>|
128
+ SUM| 1535.42| 931.31| 878.87| 1047.95| 58524.74| 115.55| 175.60| <mark>**91.23**</mark>|
129
+ AVG| 15.2021| 9.2209| 8.7017| 10.3758| 579.4528| 1.1440| 1.7386| <mark>**0.9033**</mark>|
130
+ MEDIAN| 14.7569| 5.1910| 4.9383| 6.4764| 345.7968| 0.5879| 1.3410| <mark>**0.6511**</mark>|
131
+ MIN| 1.7242| 1.8918| 1.8852| 2.0780| 73.8264| 0.2071| 0.5655| <mark>**0.1677**</mark>|
132
+ MAX| 70.1038| 32.1594| 41.2759| 53.5572|1979.9811| 6.4304| 5.1045| <mark>**3.8393**</mark>|
133
+ FACTOR| 16.829x| 10.208x| 9.633x| 11.486x| 641.480x| 1.266x| 1.925x| <mark>**1.000x**</mark>|
132
134
 
133
135
  #### Notes:
134
136
 
135
- Last Run on: 06.10.2026
137
+ Last Run on: 01.11.2026
136
138
  Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz (1.50 GHz) with 16.0 GB RAM
137
139
  Windows 11 Home
138
140
  Python Version: 3.14.7
@@ -140,4 +142,4 @@ numpy Version: 2.4.6
140
142
  scipy Version: 1.18.0
141
143
  cybrentq Version: 0.1.5 - by Gledis Caushaj (https://github.com/gledi-ai/cybrentq)
142
144
  modAB_ct: pymodab version 1.0.5 from PyPI - the previous implementation with ctypes
143
- modAB_SG: pymodab version 1.1.2 - the latest implementation of the safeguarded modab as native C extension
145
+ modAB_SG: pymodab version 1.1.3 - the latest implementation of the safeguarded modab as native C extension
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pymodab"
7
- version = "1.1.2"
7
+ version = "1.1.3"
8
8
  description = "Fast and robust root-finding library using the Modified Anderson-Bjork method"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -11,5 +11,5 @@ Preprints 2026, 2026032190. https://doi.org/10.20944/preprints202603.2190.v1
11
11
 
12
12
  from .modab import find_root, get_evaluation_count, NATIVE
13
13
 
14
- __version__ = "1.1.2"
14
+ __version__ = "1.1.3"
15
15
  __all__ = ["find_root", "get_evaluation_count"]
@@ -148,8 +148,6 @@ static double modab_core(eval_fn f, void *ctx, double x1, double x2,
148
148
  double threshold = x2 - x1;
149
149
  double f1 = y1, f2 = y2; /* True residuals, kept unmodified by A&B corrections */
150
150
  const double C = 2.0;
151
- const int MAX_RESIDUAL_STEPS = 3; /* Max consecutive A&B steps kept by the residual test alone */
152
- int residual_steps = 0;
153
151
  for (int i = 1; i <= maxIter; ++i) {
154
152
  double x3 = bisection ? safe_midpoint(x1, x2) : safe_secant(x1, y1, x2, y2);
155
153
  double dx = x2 - x1; /* Bracket width */
@@ -169,7 +167,6 @@ static double modab_core(eval_fn f, void *ctx, double x1, double x2,
169
167
  if (fabs(ym - y3) < k * fabs(ym) + k * fabs(y3)) {
170
168
  bisection = 0;
171
169
  threshold = C * dx;
172
- residual_steps = 0;
173
170
  y1 = f1; y2 = f2; /* A&B starts from the true residuals */
174
171
  }
175
172
  }
@@ -213,17 +210,12 @@ static double modab_core(eval_fn f, void *ctx, double x1, double x2,
213
210
  side = -1;
214
211
  x2 = x3; f2 = y2 = y3;
215
212
  }
216
- /* Fallback if AB fails to reduce the bracket width, unless it still halves the residual,
217
- but for no more than MAX_RESIDUAL_STEPS consecutive steps */
218
- if (x2 - x1 > threshold) {
219
- double ymin = fmin(fabs(yl), fabs(yr)); /* Best true residual of the bracket */
220
- if (residual_steps >= MAX_RESIDUAL_STEPS || 2.0 * fabs(y3) >= ymin) {
221
- bisection = 1;
222
- side = 0;
223
- } else
224
- ++residual_steps;
225
- } else
226
- residual_steps = 0;
213
+ /* Fallback if AB fails to reduce the bracket width, unless it still shrinks the residual more than 42 times
214
+ (2^(1074/200) = 41.4: at this rate the residual underflows within 200 steps from |y| = 1) */
215
+ if (x2 - x1 > threshold && 42.0 * fabs(y3) >= fmin(fabs(yl), fabs(yr))) {
216
+ bisection = 1;
217
+ side = 0;
218
+ }
227
219
  }
228
220
  }
229
221
  return NAN;
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pymodab
3
- Version: 1.1.2
3
+ Version: 1.1.3
4
4
  Summary: Fast and robust root-finding library using the Modified Anderson-Bjork method
5
5
  Author: Ganchovski, N.; Smith, O.; Rackauckas, C.; Tomov, L.; Traykov, A.
6
6
  License-Expression: MIT
@@ -90,6 +90,8 @@ Version 1.1.1 separates the bisection and Anderson-Björck steps in the solver l
90
90
 
91
91
  Version 1.1.2 streamlines the solver loop. The bracket width and the difference of the true residuals are computed once per step and reused. The exits on a zero and on a `NaN` residual share a single test on the hot path. The best true residual of the bracket, which the fallback test needs, is now computed only when a secant step fails to shrink the bracket fast enough, instead of on every secant step, and the test is written as `2·|y3| >= ymin` instead of `|y3| >= 0.5·ymin`. The 101 benchmark problems give the same roots and evaluation counts as with 1.1.1.
92
92
 
93
+ Version 1.1.3 replaces the capped residual exception of 1.1.1 with a stricter test and removes the `MAX_RESIDUAL_STEPS` counter. A secant step that does not shrink the bracket fast enough is now kept only if it reduces the residual more than 42 times, that is `42·|y3| < ymin`, where `ymin` is the smaller true residual of the bracket. The factor comes from 2^(1074/200) ≈ 41.4: at this rate a residual that starts near 1 underflows to zero within the default limit of 200 iterations, because the smallest positive double is 2^-1074. A search that stays on secant steps this way therefore ends by itself, without a counter. The factor is a fixed constant and does not follow `max_iter`, so raising the limit does not weaken the test. On the benchmark, problem f101 now needs 65 evaluations instead of 76, and the other 100 problems give the same roots and evaluation counts as with 1.1.2; all 101 need 1960 evaluations in total instead of 1971.
94
+
93
95
  For maximum speed, pass a compiled function, e.g. from [numba](https://numba.pydata.org/). It is then called from C with no Python overhead:
94
96
 
95
97
  ```python
@@ -142,27 +144,27 @@ The modAB algorithm is benchmarked against the available algorithms in Python/Sc
142
144
 
143
145
  Func| bisect| brentq| brenth| ridder| chandr| cybrentq| modAB_ct| <mark>**modAB_SG**</mark>|
144
146
  ----- | ------: | ------: | ------: | ------: | ------: | ------: | ------: | ------: |
145
- SUM| 4883| 2810| 2769| 3425| 2112| 2810| 1993| <mark>**1971**</mark>|
146
- AVG| 48| 28| 27| 34| 21| 28| 20| <mark>**20**</mark>|
147
+ SUM| 4883| 2810| 2769| 3425| 2112| 2810| 1993| <mark>**1960**</mark>|
148
+ AVG| 48| 28| 27| 34| 21| 28| 20| <mark>**19**</mark>|
147
149
  MEDIAN| 49| 13| 12| 16| 12| 13| 13| <mark>**13**</mark>|
148
150
  MIN| 3| 4| 4| 3| 3| 4| 3| <mark>**3**</mark>|
149
- MAX| 53| 102| 102| 202| 70| 102| 78| <mark>**76**</mark>|
150
- FACTOR| 2.477x| 1.426x| 1.405x| 1.738x| 1.072x| 1.426x| 1.011x| <mark>**1.000x**</mark>|
151
+ MAX| 53| 102| 102| 202| 70| 102| 78| <mark>**65**</mark>|
152
+ FACTOR| 2.491x| 1.434x| 1.413x| 1.747x| 1.078x| 1.434x| 1.017x| <mark>**1.000x**</mark>|
151
153
 
152
154
  #### Execution times (ms per problem, 200 iterations)
153
155
 
154
156
  Func| bisect| brentq| brenth| ridder| chandr| cybrentq| modAB_ct| <mark>**modAB_SG**</mark>|
155
157
  ----- | ------: | ------: | ------: | ------: | ------: | ------: | ------: | ------: |
156
- SUM| 1497.81| 888.81| 878.13| 1103.39| 62435.64| 118.77| 176.71|<mark>**90.70**</mark>|
157
- AVG| 14.8298| 8.8001| 8.6944| 10.9246| 618.1747| 1.1759| 1.7496|<mark>**0.8981**</mark>|
158
- MEDIAN| 14.1046| 4.5237| 4.4239| 5.5108| 366.5141| 0.6629| 1.3385|<mark>**0.6881**</mark>|
159
- MIN| 1.6523| 1.6333| 1.8243| 1.4128| 68.0820| 0.1926| 0.5485|<mark>**0.1625**</mark>|
160
- MAX| 39.1626| 36.7848| 31.6445| 121.3956| 2159.4610| 5.8503| 5.9590|<mark>**3.2718**</mark>|
161
- FACTOR| 16.513x| 9.799x| 9.681x| 12.165x| 688.344x| 1.309x| 1.948x|<mark>**1.000x**</mark>|
158
+ SUM| 1535.42| 931.31| 878.87| 1047.95| 58524.74| 115.55| 175.60| <mark>**91.23**</mark>|
159
+ AVG| 15.2021| 9.2209| 8.7017| 10.3758| 579.4528| 1.1440| 1.7386| <mark>**0.9033**</mark>|
160
+ MEDIAN| 14.7569| 5.1910| 4.9383| 6.4764| 345.7968| 0.5879| 1.3410| <mark>**0.6511**</mark>|
161
+ MIN| 1.7242| 1.8918| 1.8852| 2.0780| 73.8264| 0.2071| 0.5655| <mark>**0.1677**</mark>|
162
+ MAX| 70.1038| 32.1594| 41.2759| 53.5572|1979.9811| 6.4304| 5.1045| <mark>**3.8393**</mark>|
163
+ FACTOR| 16.829x| 10.208x| 9.633x| 11.486x| 641.480x| 1.266x| 1.925x| <mark>**1.000x**</mark>|
162
164
 
163
165
  #### Notes:
164
166
 
165
- Last Run on: 06.10.2026
167
+ Last Run on: 01.11.2026
166
168
  Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz (1.50 GHz) with 16.0 GB RAM
167
169
  Windows 11 Home
168
170
  Python Version: 3.14.7
@@ -170,4 +172,4 @@ numpy Version: 2.4.6
170
172
  scipy Version: 1.18.0
171
173
  cybrentq Version: 0.1.5 - by Gledis Caushaj (https://github.com/gledi-ai/cybrentq)
172
174
  modAB_ct: pymodab version 1.0.5 from PyPI - the previous implementation with ctypes
173
- modAB_SG: pymodab version 1.1.2 - the latest implementation of the safeguarded modab as native C extension
175
+ modAB_SG: pymodab version 1.1.3 - the latest implementation of the safeguarded modab as native C extension
Binary file
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes