IterationError | Iteration has failed for some reason. |
NoDescentError | No downhill step could be found. |
line_search(g, g0[, g1, trust_dg, ...]) | Return (l, g(l)) where g(l) decreases sufficiently. |
Inheritance diagram for mmf.solve.solver_utils:
Solver Utilities.
This module provides some utilities used by various solvers.
Bases: exceptions.Exception
Iteration has failed for some reason.
x.__init__(...) initializes x; see help(type(x)) for signature
Bases: mmf.solve.solver_interface.IterationError
No downhill step could be found.
x.__init__(...) initializes x; see help(type(x)) for signature
Return (l, g(l)) where g(l) decreases sufficiently.
The assumption idea here is that one is trying to minimize a function g(x) where x = x0 + l*dx and dx is known to be a local downhill direction. Taking a large step l=1 could work well if the function is linear, but if the function fluctuates on length scales comparable to |dx|, then one needs to take a shorter step.
The step size is monotonically reduced and a cubic interpolation scheme is used to find the optimal step using a minimal number of steps.
The standard line search algorithm can use this with g(l) = g(x0 + l*dx) but more general search algorithms can also be used.
Parameters : | g : function
g0 : float
g1 : float (optional)
trust_dg : bool, optional
allow_backstep : bool, optional
mesgs : IMesg
allow_bad_step : bool, optional
|
---|---|
Other Parameters: | |
avg_descent_factor : float
min_step : float
min_scale_factor, max_scale_factor : float
max_steps : int
use_cubic_factor : float
|
|
Raises : | :class:`NoDescentError` :
|
Notes
If the functional form is justified, then we know the slope of so we can use two points to fit a cubic polynomial in order to find the optimal :
If we only have one point, then we set .
If the functional form is not justified (for example, during a Broyden algorithm where the inverse Jacobian is approximate or incorrect) then this may fail because the slope is incorrect (possibly even with an incorrect sign). In this case, we fit a quadratic to the previous two points:
A test is made to see if . If so, then the cubic approximation is used, otherwise the quadratic approximation is assume (which may allow ).
Examples
Here is a bad problem. The initial step is 500 but a step of about 1 is required, so l is about 1/500 = 0.002.
>>> f = lambda x: x**2 - 1
>>> df = lambda x: 2*x
>>> x0 = 0.001
>>> dx = - f(x0)/df(x0)
>>> def g(l):
... print l
... return abs(f(x0 + l*dx))**2
>>> l, g_l = line_search(g, g0=g(0), mesgs=_MESGS)
0
1
-0.5
-0.166...
-0.076...
-0.033...
-0.014...
-0.006...
-0.002...
-0.001...
There are quite a few steps here because l must become small. The number of steps can be reduced by reducing the *_scale_factor parameters, but this runs the risk of overshooting and producing extremely small steps. (If the Jacobian is correct, then small enough steps will always be accepted unless at a local minimum).
Note that one can still get hung up at local extrema if not sufficiently close to the solution. In this case, the program should raise an exception.
>>> f = lambda x: (x - 3)*(x*x + 1)
>>> df = lambda x: 2*x**2 - 6*x
>>> x0 = 1 - 6**(1/2)/3
>>> dx = 0.1 # Correct direction
>>> def g(l):
... return abs(f(x0 + l*dx))**2
>>> l, g_l = line_search(g, g0=g(0), mesgs=_MESGS)
Traceback (most recent call last):
...
NoDescentError: Minimum step-size reached l = ... < 1e-05, err = 2.9...
Perhaps a local minimum has been found?