Skip to content

Commit 3b2ebdc

Browse files
committed
Add Traub method in Newton solver
1 parent d2370de commit 3b2ebdc

4 files changed

Lines changed: 38 additions & 19 deletions

File tree

coloraide/algebra.py

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -511,13 +511,13 @@ def solve_newton(
511511
maxiter: int = 50,
512512
rtol: float = RTOL,
513513
atol: float = ATOL,
514-
ostrowski: bool = False,
514+
order: int = 2,
515515
bounds: tuple[float, float] | None = None,
516516
) -> tuple[float, bool | None]:
517517
"""
518518
Solve equation using Newton's method.
519519
520-
If the second derivative is given, Halley's method will be used as an additional step.
520+
If order <= 3 and second derivative is given, Halley's method will be used as an additional step.
521521
Newton provides 2nd order convergence and Halley provides 3rd order convergence.
522522
523523
```
@@ -535,7 +535,16 @@ def solve_newton(
535535
Step4: halley = xn - yn / (1 - 0.5 * yn * f''(xn) / f'(xn))
536536
```
537537
538-
If Ostrowski method is enabled, only one derivative is needed, but you can get 4th order convergence.
538+
If order == 3 and 2nd derivative is not provided, we can use Traub's method which gives 3rd order convergence
539+
without a second derivative.
540+
541+
```
542+
yn = xn - f(xn) / f'(xn)
543+
traub = yn - f(yn) / f'(xn)
544+
```
545+
546+
If order >= 3, we can use Ostrowski method where only one derivative is needed, but you can get 4th order
547+
convergence.
539548
540549
```
541550
yn = xn - f(xn) / f'(xn)
@@ -545,6 +554,9 @@ def solve_newton(
545554
Return result along with True if converged, False if did not converge, None if could not converge.
546555
"""
547556

557+
if dx2 is not None and order < 3:
558+
order = 3
559+
548560
if bounds is not None:
549561
lo, hi = bounds
550562
bracketed = True
@@ -579,22 +591,26 @@ def solve_newton(
579591
return x0, None
580592

581593
# Newton step
582-
newton = fx / d1
583-
584-
# If second derivative is provided, apply the Halley's method step: 3rd order convergence.
585-
# The Newton step has been factored out of Halley's such that we can apply the rest to make
586-
# it Halley's, and if we can't, or shouldn't apply it, it remains a Newton step.
587-
if dx2 is not None and not ostrowski:
588-
d2 = dx2(x0, *args) if args else dx2(x0)
589-
denom = 1 - (0.5 * newton * d2) / d1
590-
if abs(denom) >= ATOL:
591-
newton /= denom
594+
newton = fx / d1
595+
596+
if order == 3:
597+
# Halley's method: 3rd order convergence.
598+
if dx2 is not None:
599+
d2 = dx2(x0, *args) if args else dx2(x0)
600+
denom = 1 - (0.5 * newton * d2) / d1
601+
if abs(denom) >= ATOL:
602+
newton /= denom
603+
604+
# Traub's method: 3rd order convergence
605+
else:
606+
fy = f0(x0 - newton, *args) if args else f0(x0 - newton)
607+
newton -= fy / d1
592608

593609
# If change is under our epsilon, we can consider the result converged.
594610
x0 -= newton
595611

596-
# Use Ostrowski method: 4th order convergence
597-
if ostrowski:
612+
# Ostrowski's method: 4th order convergence
613+
if order == 4:
598614
fy = f0(x0, *args) if args else f0(x0)
599615
denom = fx - 2 * fy
600616
if abs(denom) >= ATOL:

docs/src/dictionary/en-custom.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ TODO
232232
TORTIOUS
233233
Tetradic
234234
Tokenize
235+
Traub
235236
Triadic
236237
Trichromacy
237238
Trilinear

docs/src/markdown/colors/ipt.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ The IPT color appearance model excels at providing a formulation for hue where a
3131
perceived hue independent of the values of lightness and chroma (which is the general ideal for any color appearance
3232
model, but hard to achieve). It is therefore well-suited for gamut mapping implementations.
3333

34-
[Learn more](https://www.researchgate.net/publication/21677980_Development_and_Testing_of_a_Color_Space_IPT_with_Improved_Hue_Uniformity.).
34+
[Learn more](https://www.researchgate.net/publication/21677980_Development_and_Testing_of_a_Color_Space_IPT_with_Improved_Hue_Uniformity).
3535
///
3636

3737
## Channel Aliases

tests/test_algebra.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2851,8 +2851,8 @@ def test_sign(self):
28512851
self.assertEqual(alg.sign(float('inf')), 1)
28522852
self.assertEqual(alg.sign(float('-inf')), -1)
28532853

2854-
def test_solve_newton_and_halley(self):
2855-
"""Test solving with Newton."""
2854+
def test_solve_newton_variants(self):
2855+
"""Test solving with Newton variants."""
28562856

28572857
def f0(x):
28582858
return 2 * x ** 3 - 3 * x ** 2 - 3 * x + 2
@@ -2869,8 +2869,10 @@ def dx2(x):
28692869
self.assertEqual(alg.solve_newton(1, f0, dx), (0.5, True))
28702870
# Halley
28712871
self.assertEqual(alg.solve_newton(1, f0, dx, dx2), (0.5, True))
2872+
# Traub
2873+
self.assertEqual(alg.solve_newton(1, f0, dx, order=3), (0.5, True))
28722874
# Ostrowski
2873-
self.assertEqual(alg.solve_newton(1, f0, dx, ostrowski=True), (0.5, True))
2875+
self.assertEqual(alg.solve_newton(1, f0, dx, order=4), (0.5, True))
28742876

28752877

28762878
def test_solve_newton_bracketed(self):

0 commit comments

Comments
 (0)