Source code for flexsolve.open_solvers

# -*- coding: utf-8 -*-
"""
Created on Tue Nov 19 22:50:26 2019

@author: yoelr
"""
from .exceptions import SolverError
import numpy as np
from . import utils

__all__ = ('secant', 'wegstein_secant', 'aitken_secant')

def secant(f, x0, x1=None, xtol=1e-8, ytol=5e-8, args=(), maxiter=50):
    """Secant solver."""
    if x1 is None: x1 = x0 + xtol
    y0 = f(x0, *args)
    if np.abs(y0) < ytol: return x0
    dx = x1-x0 
    for iter in range(maxiter): 
        y1 = f(x1, *args)
        x1 = x0 - y1*dx/(y1-y0)
        dx = x1-x0 
        if np.abs(dx) < xtol or np.abs(y1) < ytol: return x1
        x0 = x1
        y0 = y1
    raise SolverError(maxiter, x1)
    
def wegstein_secant(f, x0, x1=None, xtol=1e-8, ytol=5e-8, args=(), maxiter=50):
    """Secant solver with Wegstein acceleration."""
    if x1 is None: x1 = x0 + xtol
    y0 = f(x0, *args)
    if np.abs(y0) < ytol: return x0
    y1 = f(x1, *args)
    if np.abs(y1) < ytol: return x0
    g0 = x1 - y1*(x1-x0)/(y1-y0)
    y0 = y1
    dx = g0-x1
    x1 = g0
    wegstein_iter = utils.scalar_wegstein_iter
    for iter in range(maxiter):
        y1 = f(x1, *args)
        g1 = x1 - y1*dx/(y1-y0)
        x0 = x1
        x1 = wegstein_iter(x1, dx, g1, g0)
        dx = x1-x0
        if np.abs(dx) < xtol or np.abs(y1) < ytol: return x1
        y0 = y1
        g0 = g1
    raise SolverError(maxiter, x1)
    
def aitken_secant(f, x0, x1=None, xtol=1e-8, ytol=5e-8, args=(), maxiter=50):
    """Secant solver with Aitken acceleration."""
    if x1 is None: x1 = x0 + xtol
    y0 = f(x0, *args)
    if np.abs(y0) < ytol: return x0
    dx = x1-x0
    aitken_iter = utils.scalar_aitken_iter
    for iter in range(maxiter):
        y1 = f(x1, *args)
        if y1 == y0: return x1
        x0 = x1 - y1*dx/(y1-y0) # x0 = g
        dx = x0-x1
        if np.abs(dx) < xtol or np.abs(y1) < ytol: return x0
        y0 = y1
        y1 = f(x0, *args)
        if y1 == y0: return x0
        x2 = x0 - y1*dx/(y1-y0) # x2 = gg
        if np.abs(dx) < xtol or np.abs(y1) < ytol: return x2
        dx = x1 - x0 # x - g
        x1 = aitken_iter(x1, x2, dx, x2 - x0)
        dx = x1 - x0
        y0 = y1
    raise SolverError(maxiter, x2)