|
""" |
|
Unit tests for optimization routines from _root.py. |
|
""" |
|
from numpy.testing import assert_, assert_equal |
|
import pytest |
|
from pytest import raises as assert_raises, warns as assert_warns |
|
import numpy as np |
|
|
|
from scipy.optimize import root |
|
|
|
|
|
class TestRoot: |
|
def test_tol_parameter(self): |
|
|
|
def func(z): |
|
x, y = z |
|
return np.array([x**3 - 1, y**3 - 1]) |
|
|
|
def dfunc(z): |
|
x, y = z |
|
return np.array([[3*x**2, 0], [0, 3*y**2]]) |
|
|
|
for method in ['hybr', 'lm', 'broyden1', 'broyden2', 'anderson', |
|
'diagbroyden', 'krylov']: |
|
if method in ('linearmixing', 'excitingmixing'): |
|
|
|
continue |
|
|
|
if method in ('hybr', 'lm'): |
|
jac = dfunc |
|
else: |
|
jac = None |
|
|
|
sol1 = root(func, [1.1,1.1], jac=jac, tol=1e-4, method=method) |
|
sol2 = root(func, [1.1,1.1], jac=jac, tol=0.5, method=method) |
|
msg = f"{method}: {func(sol1.x)} vs. {func(sol2.x)}" |
|
assert_(sol1.success, msg) |
|
assert_(sol2.success, msg) |
|
assert_(abs(func(sol1.x)).max() < abs(func(sol2.x)).max(), |
|
msg) |
|
|
|
def test_tol_norm(self): |
|
|
|
def norm(x): |
|
return abs(x[0]) |
|
|
|
for method in ['excitingmixing', |
|
'diagbroyden', |
|
'linearmixing', |
|
'anderson', |
|
'broyden1', |
|
'broyden2', |
|
'krylov']: |
|
|
|
root(np.zeros_like, np.zeros(2), method=method, |
|
options={"tol_norm": norm}) |
|
|
|
def test_minimize_scalar_coerce_args_param(self): |
|
|
|
def func(z, f=1): |
|
x, y = z |
|
return np.array([x**3 - 1, y**3 - f]) |
|
root(func, [1.1, 1.1], args=1.5) |
|
|
|
def test_f_size(self): |
|
|
|
|
|
|
|
class fun: |
|
def __init__(self): |
|
self.count = 0 |
|
|
|
def __call__(self, x): |
|
self.count += 1 |
|
|
|
if not (self.count % 5): |
|
ret = x[0] + 0.5 * (x[0] - x[1]) ** 3 - 1.0 |
|
else: |
|
ret = ([x[0] + 0.5 * (x[0] - x[1]) ** 3 - 1.0, |
|
0.5 * (x[1] - x[0]) ** 3 + x[1]]) |
|
|
|
return ret |
|
|
|
F = fun() |
|
with assert_raises(ValueError): |
|
root(F, [0.1, 0.0], method='lm') |
|
|
|
@pytest.mark.thread_unsafe |
|
def test_gh_10370(self): |
|
|
|
|
|
|
|
def fun(x, ignored): |
|
return [3*x[0] - 0.25*x[1]**2 + 10, 0.1*x[0]**2 + 5*x[1] - 2] |
|
|
|
def grad(x, ignored): |
|
return [[3, 0.5 * x[1]], [0.2 * x[0], 5]] |
|
|
|
def fun_grad(x, ignored): |
|
return fun(x, ignored), grad(x, ignored) |
|
|
|
x0 = np.zeros(2) |
|
|
|
ref = root(fun, x0, args=(1,), method='krylov') |
|
message = 'Method krylov does not use the jacobian' |
|
with assert_warns(RuntimeWarning, match=message): |
|
res1 = root(fun, x0, args=(1,), method='krylov', jac=grad) |
|
with assert_warns(RuntimeWarning, match=message): |
|
res2 = root(fun_grad, x0, args=(1,), method='krylov', jac=True) |
|
|
|
assert_equal(res1.x, ref.x) |
|
assert_equal(res2.x, ref.x) |
|
assert res1.success is res2.success is ref.success is True |
|
|
|
@pytest.mark.parametrize("method", ["hybr", "lm", "broyden1", "broyden2", |
|
"anderson", "linearmixing", |
|
"diagbroyden", "excitingmixing", |
|
"krylov", "df-sane"]) |
|
def test_method_in_result(self, method): |
|
def func(x): |
|
return x - 1 |
|
|
|
res = root(func, x0=[1], method=method) |
|
assert res.method == method |
|
|