using for loop to install conda package
This commit is contained in:
0
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__init__.py
vendored
Normal file
0
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__init__.py
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/__init__.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/__init__.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test__basinhopping.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test__basinhopping.cpython-311.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test__numdiff.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test__numdiff.cpython-311.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test__root.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test__root.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test__shgo.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test__shgo.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test__spectral.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test__spectral.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_cobyla.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_cobyla.cpython-311.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_constraints.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_constraints.cpython-311.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_direct.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_direct.cpython-311.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_lbfgsb_hessinv.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_lbfgsb_hessinv.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_lbfgsb_setulb.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_lbfgsb_setulb.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_least_squares.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_least_squares.cpython-311.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_linesearch.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_linesearch.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_linprog.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_linprog.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_lsq_common.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_lsq_common.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_lsq_linear.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_lsq_linear.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_milp.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_milp.cpython-311.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_minpack.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_minpack.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_nnls.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_nnls.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_nonlin.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_nonlin.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_optimize.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_optimize.cpython-311.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_regression.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_regression.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_slsqp.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_slsqp.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_tnc.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_tnc.cpython-311.pyc
vendored
Normal file
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_trustregion.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_trustregion.cpython-311.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_zeros.cpython-311.pyc
vendored
Normal file
BIN
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/__pycache__/test_zeros.cpython-311.pyc
vendored
Normal file
Binary file not shown.
480
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__basinhopping.py
vendored
Normal file
480
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__basinhopping.py
vendored
Normal file
@@ -0,0 +1,480 @@
|
||||
"""
|
||||
Unit tests for the basin hopping global minimization algorithm.
|
||||
"""
|
||||
import copy
|
||||
|
||||
from numpy.testing import assert_almost_equal, assert_equal, assert_
|
||||
import pytest
|
||||
from pytest import raises as assert_raises
|
||||
import numpy as np
|
||||
from numpy import cos, sin
|
||||
|
||||
from scipy.optimize import basinhopping, OptimizeResult
|
||||
from scipy.optimize._basinhopping import (
|
||||
Storage, RandomDisplacement, Metropolis, AdaptiveStepsize)
|
||||
from scipy._lib._pep440 import Version
|
||||
|
||||
|
||||
def func1d(x):
|
||||
f = cos(14.5 * x - 0.3) + (x + 0.2) * x
|
||||
df = np.array(-14.5 * sin(14.5 * x - 0.3) + 2. * x + 0.2)
|
||||
return f, df
|
||||
|
||||
|
||||
def func2d_nograd(x):
|
||||
f = cos(14.5 * x[0] - 0.3) + (x[1] + 0.2) * x[1] + (x[0] + 0.2) * x[0]
|
||||
return f
|
||||
|
||||
|
||||
def func2d(x):
|
||||
f = cos(14.5 * x[0] - 0.3) + (x[1] + 0.2) * x[1] + (x[0] + 0.2) * x[0]
|
||||
df = np.zeros(2)
|
||||
df[0] = -14.5 * sin(14.5 * x[0] - 0.3) + 2. * x[0] + 0.2
|
||||
df[1] = 2. * x[1] + 0.2
|
||||
return f, df
|
||||
|
||||
|
||||
def func2d_easyderiv(x):
|
||||
f = 2.0*x[0]**2 + 2.0*x[0]*x[1] + 2.0*x[1]**2 - 6.0*x[0]
|
||||
df = np.zeros(2)
|
||||
df[0] = 4.0*x[0] + 2.0*x[1] - 6.0
|
||||
df[1] = 2.0*x[0] + 4.0*x[1]
|
||||
|
||||
return f, df
|
||||
|
||||
|
||||
class MyTakeStep1(RandomDisplacement):
|
||||
"""use a copy of displace, but have it set a special parameter to
|
||||
make sure it's actually being used."""
|
||||
def __init__(self):
|
||||
self.been_called = False
|
||||
super().__init__()
|
||||
|
||||
def __call__(self, x):
|
||||
self.been_called = True
|
||||
return super().__call__(x)
|
||||
|
||||
|
||||
def myTakeStep2(x):
|
||||
"""redo RandomDisplacement in function form without the attribute stepsize
|
||||
to make sure everything still works ok
|
||||
"""
|
||||
s = 0.5
|
||||
x += np.random.uniform(-s, s, np.shape(x))
|
||||
return x
|
||||
|
||||
|
||||
class MyAcceptTest:
|
||||
"""pass a custom accept test
|
||||
|
||||
This does nothing but make sure it's being used and ensure all the
|
||||
possible return values are accepted
|
||||
"""
|
||||
def __init__(self):
|
||||
self.been_called = False
|
||||
self.ncalls = 0
|
||||
self.testres = [False, 'force accept', True, np.bool_(True),
|
||||
np.bool_(False), [], {}, 0, 1]
|
||||
|
||||
def __call__(self, **kwargs):
|
||||
self.been_called = True
|
||||
self.ncalls += 1
|
||||
if self.ncalls - 1 < len(self.testres):
|
||||
return self.testres[self.ncalls - 1]
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
class MyCallBack:
|
||||
"""pass a custom callback function
|
||||
|
||||
This makes sure it's being used. It also returns True after 10
|
||||
steps to ensure that it's stopping early.
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
self.been_called = False
|
||||
self.ncalls = 0
|
||||
|
||||
def __call__(self, x, f, accepted):
|
||||
self.been_called = True
|
||||
self.ncalls += 1
|
||||
if self.ncalls == 10:
|
||||
return True
|
||||
|
||||
|
||||
class TestBasinHopping:
|
||||
|
||||
def setup_method(self):
|
||||
""" Tests setup.
|
||||
|
||||
Run tests based on the 1-D and 2-D functions described above.
|
||||
"""
|
||||
self.x0 = (1.0, [1.0, 1.0])
|
||||
self.sol = (-0.195, np.array([-0.195, -0.1]))
|
||||
|
||||
self.tol = 3 # number of decimal places
|
||||
|
||||
self.niter = 100
|
||||
self.disp = False
|
||||
|
||||
# fix random seed
|
||||
np.random.seed(1234)
|
||||
|
||||
self.kwargs = {"method": "L-BFGS-B", "jac": True}
|
||||
self.kwargs_nograd = {"method": "L-BFGS-B"}
|
||||
|
||||
def test_TypeError(self):
|
||||
# test the TypeErrors are raised on bad input
|
||||
i = 1
|
||||
# if take_step is passed, it must be callable
|
||||
assert_raises(TypeError, basinhopping, func2d, self.x0[i],
|
||||
take_step=1)
|
||||
# if accept_test is passed, it must be callable
|
||||
assert_raises(TypeError, basinhopping, func2d, self.x0[i],
|
||||
accept_test=1)
|
||||
|
||||
def test_input_validation(self):
|
||||
msg = 'target_accept_rate has to be in range \\(0, 1\\)'
|
||||
with assert_raises(ValueError, match=msg):
|
||||
basinhopping(func1d, self.x0[0], target_accept_rate=0.)
|
||||
with assert_raises(ValueError, match=msg):
|
||||
basinhopping(func1d, self.x0[0], target_accept_rate=1.)
|
||||
|
||||
msg = 'stepwise_factor has to be in range \\(0, 1\\)'
|
||||
with assert_raises(ValueError, match=msg):
|
||||
basinhopping(func1d, self.x0[0], stepwise_factor=0.)
|
||||
with assert_raises(ValueError, match=msg):
|
||||
basinhopping(func1d, self.x0[0], stepwise_factor=1.)
|
||||
|
||||
def test_1d_grad(self):
|
||||
# test 1-D minimizations with gradient
|
||||
i = 0
|
||||
res = basinhopping(func1d, self.x0[i], minimizer_kwargs=self.kwargs,
|
||||
niter=self.niter, disp=self.disp)
|
||||
assert_almost_equal(res.x, self.sol[i], self.tol)
|
||||
|
||||
def test_2d(self):
|
||||
# test 2d minimizations with gradient
|
||||
i = 1
|
||||
res = basinhopping(func2d, self.x0[i], minimizer_kwargs=self.kwargs,
|
||||
niter=self.niter, disp=self.disp)
|
||||
assert_almost_equal(res.x, self.sol[i], self.tol)
|
||||
assert_(res.nfev > 0)
|
||||
|
||||
def test_njev(self):
|
||||
# test njev is returned correctly
|
||||
i = 1
|
||||
minimizer_kwargs = self.kwargs.copy()
|
||||
# L-BFGS-B doesn't use njev, but BFGS does
|
||||
minimizer_kwargs["method"] = "BFGS"
|
||||
res = basinhopping(func2d, self.x0[i],
|
||||
minimizer_kwargs=minimizer_kwargs, niter=self.niter,
|
||||
disp=self.disp)
|
||||
assert_(res.nfev > 0)
|
||||
assert_equal(res.nfev, res.njev)
|
||||
|
||||
def test_jac(self):
|
||||
# test Jacobian returned
|
||||
minimizer_kwargs = self.kwargs.copy()
|
||||
# BFGS returns a Jacobian
|
||||
minimizer_kwargs["method"] = "BFGS"
|
||||
|
||||
res = basinhopping(func2d_easyderiv, [0.0, 0.0],
|
||||
minimizer_kwargs=minimizer_kwargs, niter=self.niter,
|
||||
disp=self.disp)
|
||||
|
||||
assert_(hasattr(res.lowest_optimization_result, "jac"))
|
||||
|
||||
# in this case, the Jacobian is just [df/dx, df/dy]
|
||||
_, jacobian = func2d_easyderiv(res.x)
|
||||
assert_almost_equal(res.lowest_optimization_result.jac, jacobian,
|
||||
self.tol)
|
||||
|
||||
def test_2d_nograd(self):
|
||||
# test 2-D minimizations without gradient
|
||||
i = 1
|
||||
res = basinhopping(func2d_nograd, self.x0[i],
|
||||
minimizer_kwargs=self.kwargs_nograd,
|
||||
niter=self.niter, disp=self.disp)
|
||||
assert_almost_equal(res.x, self.sol[i], self.tol)
|
||||
|
||||
def test_all_minimizers(self):
|
||||
# Test 2-D minimizations with gradient. Nelder-Mead, Powell, and COBYLA
|
||||
# don't accept jac=True, so aren't included here.
|
||||
i = 1
|
||||
methods = ['CG', 'BFGS', 'Newton-CG', 'L-BFGS-B', 'TNC', 'SLSQP']
|
||||
minimizer_kwargs = copy.copy(self.kwargs)
|
||||
for method in methods:
|
||||
minimizer_kwargs["method"] = method
|
||||
res = basinhopping(func2d, self.x0[i],
|
||||
minimizer_kwargs=minimizer_kwargs,
|
||||
niter=self.niter, disp=self.disp)
|
||||
assert_almost_equal(res.x, self.sol[i], self.tol)
|
||||
|
||||
def test_all_nograd_minimizers(self):
|
||||
# Test 2-D minimizations without gradient. Newton-CG requires jac=True,
|
||||
# so not included here.
|
||||
i = 1
|
||||
methods = ['CG', 'BFGS', 'L-BFGS-B', 'TNC', 'SLSQP',
|
||||
'Nelder-Mead', 'Powell', 'COBYLA']
|
||||
minimizer_kwargs = copy.copy(self.kwargs_nograd)
|
||||
for method in methods:
|
||||
minimizer_kwargs["method"] = method
|
||||
res = basinhopping(func2d_nograd, self.x0[i],
|
||||
minimizer_kwargs=minimizer_kwargs,
|
||||
niter=self.niter, disp=self.disp)
|
||||
tol = self.tol
|
||||
if method == 'COBYLA':
|
||||
tol = 2
|
||||
assert_almost_equal(res.x, self.sol[i], decimal=tol)
|
||||
|
||||
def test_pass_takestep(self):
|
||||
# test that passing a custom takestep works
|
||||
# also test that the stepsize is being adjusted
|
||||
takestep = MyTakeStep1()
|
||||
initial_step_size = takestep.stepsize
|
||||
i = 1
|
||||
res = basinhopping(func2d, self.x0[i], minimizer_kwargs=self.kwargs,
|
||||
niter=self.niter, disp=self.disp,
|
||||
take_step=takestep)
|
||||
assert_almost_equal(res.x, self.sol[i], self.tol)
|
||||
assert_(takestep.been_called)
|
||||
# make sure that the build in adaptive step size has been used
|
||||
assert_(initial_step_size != takestep.stepsize)
|
||||
|
||||
def test_pass_simple_takestep(self):
|
||||
# test that passing a custom takestep without attribute stepsize
|
||||
takestep = myTakeStep2
|
||||
i = 1
|
||||
res = basinhopping(func2d_nograd, self.x0[i],
|
||||
minimizer_kwargs=self.kwargs_nograd,
|
||||
niter=self.niter, disp=self.disp,
|
||||
take_step=takestep)
|
||||
assert_almost_equal(res.x, self.sol[i], self.tol)
|
||||
|
||||
def test_pass_accept_test(self):
|
||||
# test passing a custom accept test
|
||||
# makes sure it's being used and ensures all the possible return values
|
||||
# are accepted.
|
||||
accept_test = MyAcceptTest()
|
||||
i = 1
|
||||
# there's no point in running it more than a few steps.
|
||||
basinhopping(func2d, self.x0[i], minimizer_kwargs=self.kwargs,
|
||||
niter=10, disp=self.disp, accept_test=accept_test)
|
||||
assert_(accept_test.been_called)
|
||||
|
||||
def test_pass_callback(self):
|
||||
# test passing a custom callback function
|
||||
# This makes sure it's being used. It also returns True after 10 steps
|
||||
# to ensure that it's stopping early.
|
||||
callback = MyCallBack()
|
||||
i = 1
|
||||
# there's no point in running it more than a few steps.
|
||||
res = basinhopping(func2d, self.x0[i], minimizer_kwargs=self.kwargs,
|
||||
niter=30, disp=self.disp, callback=callback)
|
||||
assert_(callback.been_called)
|
||||
assert_("callback" in res.message[0])
|
||||
# One of the calls of MyCallBack is during BasinHoppingRunner
|
||||
# construction, so there are only 9 remaining before MyCallBack stops
|
||||
# the minimization.
|
||||
assert_equal(res.nit, 9)
|
||||
|
||||
def test_minimizer_fail(self):
|
||||
# test if a minimizer fails
|
||||
i = 1
|
||||
self.kwargs["options"] = dict(maxiter=0)
|
||||
self.niter = 10
|
||||
res = basinhopping(func2d, self.x0[i], minimizer_kwargs=self.kwargs,
|
||||
niter=self.niter, disp=self.disp)
|
||||
# the number of failed minimizations should be the number of
|
||||
# iterations + 1
|
||||
assert_equal(res.nit + 1, res.minimization_failures)
|
||||
|
||||
def test_niter_zero(self):
|
||||
# gh5915, what happens if you call basinhopping with niter=0
|
||||
i = 0
|
||||
basinhopping(func1d, self.x0[i], minimizer_kwargs=self.kwargs,
|
||||
niter=0, disp=self.disp)
|
||||
|
||||
def test_seed_reproducibility(self):
|
||||
# seed should ensure reproducibility between runs
|
||||
minimizer_kwargs = {"method": "L-BFGS-B", "jac": True}
|
||||
|
||||
f_1 = []
|
||||
|
||||
def callback(x, f, accepted):
|
||||
f_1.append(f)
|
||||
|
||||
basinhopping(func2d, [1.0, 1.0], minimizer_kwargs=minimizer_kwargs,
|
||||
niter=10, callback=callback, seed=10)
|
||||
|
||||
f_2 = []
|
||||
|
||||
def callback2(x, f, accepted):
|
||||
f_2.append(f)
|
||||
|
||||
basinhopping(func2d, [1.0, 1.0], minimizer_kwargs=minimizer_kwargs,
|
||||
niter=10, callback=callback2, seed=10)
|
||||
assert_equal(np.array(f_1), np.array(f_2))
|
||||
|
||||
def test_random_gen(self):
|
||||
# check that np.random.Generator can be used (numpy >= 1.17)
|
||||
rng = np.random.default_rng(1)
|
||||
|
||||
minimizer_kwargs = {"method": "L-BFGS-B", "jac": True}
|
||||
|
||||
res1 = basinhopping(func2d, [1.0, 1.0],
|
||||
minimizer_kwargs=minimizer_kwargs,
|
||||
niter=10, seed=rng)
|
||||
|
||||
rng = np.random.default_rng(1)
|
||||
res2 = basinhopping(func2d, [1.0, 1.0],
|
||||
minimizer_kwargs=minimizer_kwargs,
|
||||
niter=10, seed=rng)
|
||||
assert_equal(res1.x, res2.x)
|
||||
|
||||
def test_monotonic_basin_hopping(self):
|
||||
# test 1-D minimizations with gradient and T=0
|
||||
i = 0
|
||||
res = basinhopping(func1d, self.x0[i], minimizer_kwargs=self.kwargs,
|
||||
niter=self.niter, disp=self.disp, T=0)
|
||||
assert_almost_equal(res.x, self.sol[i], self.tol)
|
||||
|
||||
|
||||
class Test_Storage:
|
||||
def setup_method(self):
|
||||
self.x0 = np.array(1)
|
||||
self.f0 = 0
|
||||
|
||||
minres = OptimizeResult()
|
||||
minres.x = self.x0
|
||||
minres.fun = self.f0
|
||||
|
||||
self.storage = Storage(minres)
|
||||
|
||||
def test_higher_f_rejected(self):
|
||||
new_minres = OptimizeResult()
|
||||
new_minres.x = self.x0 + 1
|
||||
new_minres.fun = self.f0 + 1
|
||||
|
||||
ret = self.storage.update(new_minres)
|
||||
minres = self.storage.get_lowest()
|
||||
assert_equal(self.x0, minres.x)
|
||||
assert_equal(self.f0, minres.fun)
|
||||
assert_(not ret)
|
||||
|
||||
def test_lower_f_accepted(self):
|
||||
new_minres = OptimizeResult()
|
||||
new_minres.x = self.x0 + 1
|
||||
new_minres.fun = self.f0 - 1
|
||||
|
||||
ret = self.storage.update(new_minres)
|
||||
minres = self.storage.get_lowest()
|
||||
assert_(self.x0 != minres.x)
|
||||
assert_(self.f0 != minres.fun)
|
||||
assert_(ret)
|
||||
|
||||
|
||||
class Test_RandomDisplacement:
|
||||
def setup_method(self):
|
||||
self.stepsize = 1.0
|
||||
self.displace = RandomDisplacement(stepsize=self.stepsize)
|
||||
self.N = 300000
|
||||
self.x0 = np.zeros([self.N])
|
||||
|
||||
def test_random(self):
|
||||
# the mean should be 0
|
||||
# the variance should be (2*stepsize)**2 / 12
|
||||
# note these tests are random, they will fail from time to time
|
||||
x = self.displace(self.x0)
|
||||
v = (2. * self.stepsize) ** 2 / 12
|
||||
assert_almost_equal(np.mean(x), 0., 1)
|
||||
assert_almost_equal(np.var(x), v, 1)
|
||||
|
||||
|
||||
class Test_Metropolis:
|
||||
def setup_method(self):
|
||||
self.T = 2.
|
||||
self.met = Metropolis(self.T)
|
||||
|
||||
def test_boolean_return(self):
|
||||
# the return must be a bool, else an error will be raised in
|
||||
# basinhopping
|
||||
ret = self.met(f_new=0., f_old=1.)
|
||||
assert isinstance(ret, bool)
|
||||
|
||||
def test_lower_f_accepted(self):
|
||||
assert_(self.met(f_new=0., f_old=1.))
|
||||
|
||||
def test_KeyError(self):
|
||||
# should raise KeyError if kwargs f_old or f_new is not passed
|
||||
assert_raises(KeyError, self.met, f_old=1.)
|
||||
assert_raises(KeyError, self.met, f_new=1.)
|
||||
|
||||
def test_accept(self):
|
||||
# test that steps are randomly accepted for f_new > f_old
|
||||
one_accept = False
|
||||
one_reject = False
|
||||
for i in range(1000):
|
||||
if one_accept and one_reject:
|
||||
break
|
||||
ret = self.met(f_new=1., f_old=0.5)
|
||||
if ret:
|
||||
one_accept = True
|
||||
else:
|
||||
one_reject = True
|
||||
assert_(one_accept)
|
||||
assert_(one_reject)
|
||||
|
||||
def test_GH7495(self):
|
||||
# an overflow in exp was producing a RuntimeWarning
|
||||
# create own object here in case someone changes self.T
|
||||
met = Metropolis(2)
|
||||
with np.errstate(over='raise'):
|
||||
met.accept_reject(0, 2000)
|
||||
|
||||
|
||||
class Test_AdaptiveStepsize:
|
||||
def setup_method(self):
|
||||
self.stepsize = 1.
|
||||
self.ts = RandomDisplacement(stepsize=self.stepsize)
|
||||
self.target_accept_rate = 0.5
|
||||
self.takestep = AdaptiveStepsize(takestep=self.ts, verbose=False,
|
||||
accept_rate=self.target_accept_rate)
|
||||
|
||||
def test_adaptive_increase(self):
|
||||
# if few steps are rejected, the stepsize should increase
|
||||
x = 0.
|
||||
self.takestep(x)
|
||||
self.takestep.report(False)
|
||||
for i in range(self.takestep.interval):
|
||||
self.takestep(x)
|
||||
self.takestep.report(True)
|
||||
assert_(self.ts.stepsize > self.stepsize)
|
||||
|
||||
def test_adaptive_decrease(self):
|
||||
# if few steps are rejected, the stepsize should increase
|
||||
x = 0.
|
||||
self.takestep(x)
|
||||
self.takestep.report(True)
|
||||
for i in range(self.takestep.interval):
|
||||
self.takestep(x)
|
||||
self.takestep.report(False)
|
||||
assert_(self.ts.stepsize < self.stepsize)
|
||||
|
||||
def test_all_accepted(self):
|
||||
# test that everything works OK if all steps were accepted
|
||||
x = 0.
|
||||
for i in range(self.takestep.interval + 1):
|
||||
self.takestep(x)
|
||||
self.takestep.report(True)
|
||||
assert_(self.ts.stepsize > self.stepsize)
|
||||
|
||||
def test_all_rejected(self):
|
||||
# test that everything works OK if all steps were rejected
|
||||
x = 0.
|
||||
for i in range(self.takestep.interval + 1):
|
||||
self.takestep(x)
|
||||
self.takestep.report(False)
|
||||
assert_(self.ts.stepsize < self.stepsize)
|
||||
1485
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__differential_evolution.py
vendored
Normal file
1485
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__differential_evolution.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
360
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__dual_annealing.py
vendored
Normal file
360
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__dual_annealing.py
vendored
Normal file
@@ -0,0 +1,360 @@
|
||||
# Dual annealing unit tests implementation.
|
||||
# Copyright (c) 2018 Sylvain Gubian <sylvain.gubian@pmi.com>,
|
||||
# Yang Xiang <yang.xiang@pmi.com>
|
||||
# Author: Sylvain Gubian, PMP S.A.
|
||||
"""
|
||||
Unit tests for the dual annealing global optimizer
|
||||
"""
|
||||
from scipy.optimize import dual_annealing, Bounds
|
||||
|
||||
from scipy.optimize._dual_annealing import EnergyState
|
||||
from scipy.optimize._dual_annealing import LocalSearchWrapper
|
||||
from scipy.optimize._dual_annealing import ObjectiveFunWrapper
|
||||
from scipy.optimize._dual_annealing import StrategyChain
|
||||
from scipy.optimize._dual_annealing import VisitingDistribution
|
||||
from scipy.optimize import rosen, rosen_der
|
||||
import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import assert_equal, assert_allclose, assert_array_less
|
||||
from pytest import raises as assert_raises
|
||||
from scipy._lib._util import check_random_state
|
||||
from scipy._lib._pep440 import Version
|
||||
|
||||
|
||||
class TestDualAnnealing:
|
||||
|
||||
def setup_method(self):
|
||||
# A function that returns always infinity for initialization tests
|
||||
self.weirdfunc = lambda x: np.inf
|
||||
# 2-D bounds for testing function
|
||||
self.ld_bounds = [(-5.12, 5.12)] * 2
|
||||
# 4-D bounds for testing function
|
||||
self.hd_bounds = self.ld_bounds * 4
|
||||
# Number of values to be generated for testing visit function
|
||||
self.nbtestvalues = 5000
|
||||
self.high_temperature = 5230
|
||||
self.low_temperature = 0.1
|
||||
self.qv = 2.62
|
||||
self.seed = 1234
|
||||
self.rs = check_random_state(self.seed)
|
||||
self.nb_fun_call = 0
|
||||
self.ngev = 0
|
||||
|
||||
def callback(self, x, f, context):
|
||||
# For testing callback mechanism. Should stop for e <= 1 as
|
||||
# the callback function returns True
|
||||
if f <= 1.0:
|
||||
return True
|
||||
|
||||
def func(self, x, args=()):
|
||||
# Using Rastrigin function for performing tests
|
||||
if args:
|
||||
shift = args
|
||||
else:
|
||||
shift = 0
|
||||
y = np.sum((x - shift) ** 2 - 10 * np.cos(2 * np.pi * (
|
||||
x - shift))) + 10 * np.size(x) + shift
|
||||
self.nb_fun_call += 1
|
||||
return y
|
||||
|
||||
def rosen_der_wrapper(self, x, args=()):
|
||||
self.ngev += 1
|
||||
return rosen_der(x, *args)
|
||||
|
||||
# FIXME: there are some discontinuities in behaviour as a function of `qv`,
|
||||
# this needs investigating - see gh-12384
|
||||
@pytest.mark.parametrize('qv', [1.1, 1.41, 2, 2.62, 2.9])
|
||||
def test_visiting_stepping(self, qv):
|
||||
lu = list(zip(*self.ld_bounds))
|
||||
lower = np.array(lu[0])
|
||||
upper = np.array(lu[1])
|
||||
dim = lower.size
|
||||
vd = VisitingDistribution(lower, upper, qv, self.rs)
|
||||
values = np.zeros(dim)
|
||||
x_step_low = vd.visiting(values, 0, self.high_temperature)
|
||||
# Make sure that only the first component is changed
|
||||
assert_equal(np.not_equal(x_step_low, 0), True)
|
||||
values = np.zeros(dim)
|
||||
x_step_high = vd.visiting(values, dim, self.high_temperature)
|
||||
# Make sure that component other than at dim has changed
|
||||
assert_equal(np.not_equal(x_step_high[0], 0), True)
|
||||
|
||||
@pytest.mark.parametrize('qv', [2.25, 2.62, 2.9])
|
||||
def test_visiting_dist_high_temperature(self, qv):
|
||||
lu = list(zip(*self.ld_bounds))
|
||||
lower = np.array(lu[0])
|
||||
upper = np.array(lu[1])
|
||||
vd = VisitingDistribution(lower, upper, qv, self.rs)
|
||||
# values = np.zeros(self.nbtestvalues)
|
||||
# for i in np.arange(self.nbtestvalues):
|
||||
# values[i] = vd.visit_fn(self.high_temperature)
|
||||
values = vd.visit_fn(self.high_temperature, self.nbtestvalues)
|
||||
|
||||
# Visiting distribution is a distorted version of Cauchy-Lorentz
|
||||
# distribution, and as no 1st and higher moments (no mean defined,
|
||||
# no variance defined).
|
||||
# Check that big tails values are generated
|
||||
assert_array_less(np.min(values), 1e-10)
|
||||
assert_array_less(1e+10, np.max(values))
|
||||
|
||||
def test_reset(self):
|
||||
owf = ObjectiveFunWrapper(self.weirdfunc)
|
||||
lu = list(zip(*self.ld_bounds))
|
||||
lower = np.array(lu[0])
|
||||
upper = np.array(lu[1])
|
||||
es = EnergyState(lower, upper)
|
||||
assert_raises(ValueError, es.reset, owf, check_random_state(None))
|
||||
|
||||
def test_low_dim(self):
|
||||
ret = dual_annealing(
|
||||
self.func, self.ld_bounds, seed=self.seed)
|
||||
assert_allclose(ret.fun, 0., atol=1e-12)
|
||||
assert ret.success
|
||||
|
||||
def test_high_dim(self):
|
||||
ret = dual_annealing(self.func, self.hd_bounds, seed=self.seed)
|
||||
assert_allclose(ret.fun, 0., atol=1e-12)
|
||||
assert ret.success
|
||||
|
||||
def test_low_dim_no_ls(self):
|
||||
ret = dual_annealing(self.func, self.ld_bounds,
|
||||
no_local_search=True, seed=self.seed)
|
||||
assert_allclose(ret.fun, 0., atol=1e-4)
|
||||
|
||||
def test_high_dim_no_ls(self):
|
||||
ret = dual_annealing(self.func, self.hd_bounds,
|
||||
no_local_search=True, seed=self.seed)
|
||||
assert_allclose(ret.fun, 0., atol=1e-4)
|
||||
|
||||
def test_nb_fun_call(self):
|
||||
ret = dual_annealing(self.func, self.ld_bounds, seed=self.seed)
|
||||
assert_equal(self.nb_fun_call, ret.nfev)
|
||||
|
||||
def test_nb_fun_call_no_ls(self):
|
||||
ret = dual_annealing(self.func, self.ld_bounds,
|
||||
no_local_search=True, seed=self.seed)
|
||||
assert_equal(self.nb_fun_call, ret.nfev)
|
||||
|
||||
def test_max_reinit(self):
|
||||
assert_raises(ValueError, dual_annealing, self.weirdfunc,
|
||||
self.ld_bounds)
|
||||
|
||||
def test_reproduce(self):
|
||||
res1 = dual_annealing(self.func, self.ld_bounds, seed=self.seed)
|
||||
res2 = dual_annealing(self.func, self.ld_bounds, seed=self.seed)
|
||||
res3 = dual_annealing(self.func, self.ld_bounds, seed=self.seed)
|
||||
# If we have reproducible results, x components found has to
|
||||
# be exactly the same, which is not the case with no seeding
|
||||
assert_equal(res1.x, res2.x)
|
||||
assert_equal(res1.x, res3.x)
|
||||
|
||||
def test_rand_gen(self):
|
||||
# check that np.random.Generator can be used (numpy >= 1.17)
|
||||
# obtain a np.random.Generator object
|
||||
rng = np.random.default_rng(1)
|
||||
|
||||
res1 = dual_annealing(self.func, self.ld_bounds, seed=rng)
|
||||
# seed again
|
||||
rng = np.random.default_rng(1)
|
||||
res2 = dual_annealing(self.func, self.ld_bounds, seed=rng)
|
||||
# If we have reproducible results, x components found has to
|
||||
# be exactly the same, which is not the case with no seeding
|
||||
assert_equal(res1.x, res2.x)
|
||||
|
||||
def test_bounds_integrity(self):
|
||||
wrong_bounds = [(-5.12, 5.12), (1, 0), (5.12, 5.12)]
|
||||
assert_raises(ValueError, dual_annealing, self.func,
|
||||
wrong_bounds)
|
||||
|
||||
def test_bound_validity(self):
|
||||
invalid_bounds = [(-5, 5), (-np.inf, 0), (-5, 5)]
|
||||
assert_raises(ValueError, dual_annealing, self.func,
|
||||
invalid_bounds)
|
||||
invalid_bounds = [(-5, 5), (0, np.inf), (-5, 5)]
|
||||
assert_raises(ValueError, dual_annealing, self.func,
|
||||
invalid_bounds)
|
||||
invalid_bounds = [(-5, 5), (0, np.nan), (-5, 5)]
|
||||
assert_raises(ValueError, dual_annealing, self.func,
|
||||
invalid_bounds)
|
||||
|
||||
def test_deprecated_local_search_options_bounds(self):
|
||||
func = lambda x: np.sum((x-5) * (x-1))
|
||||
bounds = list(zip([-6, -5], [6, 5]))
|
||||
# Test bounds can be passed (see gh-10831)
|
||||
|
||||
with pytest.warns(RuntimeWarning, match=r"Method CG cannot handle "):
|
||||
dual_annealing(
|
||||
func,
|
||||
bounds=bounds,
|
||||
minimizer_kwargs={"method": "CG", "bounds": bounds})
|
||||
|
||||
def test_minimizer_kwargs_bounds(self):
|
||||
func = lambda x: np.sum((x-5) * (x-1))
|
||||
bounds = list(zip([-6, -5], [6, 5]))
|
||||
# Test bounds can be passed (see gh-10831)
|
||||
dual_annealing(
|
||||
func,
|
||||
bounds=bounds,
|
||||
minimizer_kwargs={"method": "SLSQP", "bounds": bounds})
|
||||
|
||||
with pytest.warns(RuntimeWarning, match=r"Method CG cannot handle "):
|
||||
dual_annealing(
|
||||
func,
|
||||
bounds=bounds,
|
||||
minimizer_kwargs={"method": "CG", "bounds": bounds})
|
||||
|
||||
def test_max_fun_ls(self):
|
||||
ret = dual_annealing(self.func, self.ld_bounds, maxfun=100,
|
||||
seed=self.seed)
|
||||
|
||||
ls_max_iter = min(max(
|
||||
len(self.ld_bounds) * LocalSearchWrapper.LS_MAXITER_RATIO,
|
||||
LocalSearchWrapper.LS_MAXITER_MIN),
|
||||
LocalSearchWrapper.LS_MAXITER_MAX)
|
||||
assert ret.nfev <= 100 + ls_max_iter
|
||||
assert not ret.success
|
||||
|
||||
def test_max_fun_no_ls(self):
|
||||
ret = dual_annealing(self.func, self.ld_bounds,
|
||||
no_local_search=True, maxfun=500, seed=self.seed)
|
||||
assert ret.nfev <= 500
|
||||
assert not ret.success
|
||||
|
||||
def test_maxiter(self):
|
||||
ret = dual_annealing(self.func, self.ld_bounds, maxiter=700,
|
||||
seed=self.seed)
|
||||
assert ret.nit <= 700
|
||||
|
||||
# Testing that args are passed correctly for dual_annealing
|
||||
def test_fun_args_ls(self):
|
||||
ret = dual_annealing(self.func, self.ld_bounds,
|
||||
args=((3.14159,)), seed=self.seed)
|
||||
assert_allclose(ret.fun, 3.14159, atol=1e-6)
|
||||
|
||||
# Testing that args are passed correctly for pure simulated annealing
|
||||
def test_fun_args_no_ls(self):
|
||||
ret = dual_annealing(self.func, self.ld_bounds,
|
||||
args=((3.14159, )), no_local_search=True,
|
||||
seed=self.seed)
|
||||
assert_allclose(ret.fun, 3.14159, atol=1e-4)
|
||||
|
||||
def test_callback_stop(self):
|
||||
# Testing that callback make the algorithm stop for
|
||||
# fun value <= 1.0 (see callback method)
|
||||
ret = dual_annealing(self.func, self.ld_bounds,
|
||||
callback=self.callback, seed=self.seed)
|
||||
assert ret.fun <= 1.0
|
||||
assert 'stop early' in ret.message[0]
|
||||
assert not ret.success
|
||||
|
||||
@pytest.mark.parametrize('method, atol', [
|
||||
('Nelder-Mead', 2e-5),
|
||||
('COBYLA', 1e-5),
|
||||
('Powell', 1e-8),
|
||||
('CG', 1e-8),
|
||||
('BFGS', 1e-8),
|
||||
('TNC', 1e-8),
|
||||
('SLSQP', 2e-7),
|
||||
])
|
||||
def test_multi_ls_minimizer(self, method, atol):
|
||||
ret = dual_annealing(self.func, self.ld_bounds,
|
||||
minimizer_kwargs=dict(method=method),
|
||||
seed=self.seed)
|
||||
assert_allclose(ret.fun, 0., atol=atol)
|
||||
|
||||
def test_wrong_restart_temp(self):
|
||||
assert_raises(ValueError, dual_annealing, self.func,
|
||||
self.ld_bounds, restart_temp_ratio=1)
|
||||
assert_raises(ValueError, dual_annealing, self.func,
|
||||
self.ld_bounds, restart_temp_ratio=0)
|
||||
|
||||
def test_gradient_gnev(self):
|
||||
minimizer_opts = {
|
||||
'jac': self.rosen_der_wrapper,
|
||||
}
|
||||
ret = dual_annealing(rosen, self.ld_bounds,
|
||||
minimizer_kwargs=minimizer_opts,
|
||||
seed=self.seed)
|
||||
assert ret.njev == self.ngev
|
||||
|
||||
def test_from_docstring(self):
|
||||
func = lambda x: np.sum(x * x - 10 * np.cos(2 * np.pi * x)) + 10 * np.size(x)
|
||||
lw = [-5.12] * 10
|
||||
up = [5.12] * 10
|
||||
ret = dual_annealing(func, bounds=list(zip(lw, up)), seed=1234)
|
||||
assert_allclose(ret.x,
|
||||
[-4.26437714e-09, -3.91699361e-09, -1.86149218e-09,
|
||||
-3.97165720e-09, -6.29151648e-09, -6.53145322e-09,
|
||||
-3.93616815e-09, -6.55623025e-09, -6.05775280e-09,
|
||||
-5.00668935e-09], atol=4e-8)
|
||||
assert_allclose(ret.fun, 0.000000, atol=5e-13)
|
||||
|
||||
@pytest.mark.parametrize('new_e, temp_step, accepted, accept_rate', [
|
||||
(0, 100, 1000, 1.0097587941791923),
|
||||
(0, 2, 1000, 1.2599210498948732),
|
||||
(10, 100, 878, 0.8786035869128718),
|
||||
(10, 60, 695, 0.6812920690579612),
|
||||
(2, 100, 990, 0.9897404249173424),
|
||||
])
|
||||
def test_accept_reject_probabilistic(
|
||||
self, new_e, temp_step, accepted, accept_rate):
|
||||
# Test accepts unconditionally with e < current_energy and
|
||||
# probabilistically with e > current_energy
|
||||
|
||||
rs = check_random_state(123)
|
||||
|
||||
count_accepted = 0
|
||||
iterations = 1000
|
||||
|
||||
accept_param = -5
|
||||
current_energy = 1
|
||||
for _ in range(iterations):
|
||||
energy_state = EnergyState(lower=None, upper=None)
|
||||
# Set energy state with current_energy, any location.
|
||||
energy_state.update_current(current_energy, [0])
|
||||
|
||||
chain = StrategyChain(
|
||||
accept_param, None, None, None, rs, energy_state)
|
||||
# Normally this is set in run()
|
||||
chain.temperature_step = temp_step
|
||||
|
||||
# Check if update is accepted.
|
||||
chain.accept_reject(j=1, e=new_e, x_visit=[2])
|
||||
if energy_state.current_energy == new_e:
|
||||
count_accepted += 1
|
||||
|
||||
assert count_accepted == accepted
|
||||
|
||||
# Check accept rate
|
||||
pqv = 1 - (1 - accept_param) * (new_e - current_energy) / temp_step
|
||||
rate = 0 if pqv <= 0 else np.exp(np.log(pqv) / (1 - accept_param))
|
||||
|
||||
assert_allclose(rate, accept_rate)
|
||||
|
||||
def test_bounds_class(self):
|
||||
# test that result does not depend on the bounds type
|
||||
def func(x):
|
||||
f = np.sum(x * x - 10 * np.cos(2 * np.pi * x)) + 10 * np.size(x)
|
||||
return f
|
||||
lw = [-5.12] * 5
|
||||
up = [5.12] * 5
|
||||
|
||||
# Unbounded global minimum is all zeros. Most bounds below will force
|
||||
# a DV away from unbounded minimum and be active at solution.
|
||||
up[0] = -2.0
|
||||
up[1] = -1.0
|
||||
lw[3] = 1.0
|
||||
lw[4] = 2.0
|
||||
|
||||
# run optimizations
|
||||
bounds = Bounds(lw, up)
|
||||
ret_bounds_class = dual_annealing(func, bounds=bounds, seed=1234)
|
||||
|
||||
bounds_old = list(zip(lw, up))
|
||||
ret_bounds_list = dual_annealing(func, bounds=bounds_old, seed=1234)
|
||||
|
||||
# test that found minima, function evaluations and iterations match
|
||||
assert_allclose(ret_bounds_class.x, ret_bounds_list.x, atol=1e-8)
|
||||
assert_allclose(ret_bounds_class.x, np.arange(-2, 3), atol=1e-7)
|
||||
assert_allclose(ret_bounds_list.fun, ret_bounds_class.fun, atol=1e-9)
|
||||
assert ret_bounds_list.nfev == ret_bounds_class.nfev
|
||||
297
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__linprog_clean_inputs.py
vendored
Normal file
297
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__linprog_clean_inputs.py
vendored
Normal file
@@ -0,0 +1,297 @@
|
||||
"""
|
||||
Unit test for Linear Programming via Simplex Algorithm.
|
||||
"""
|
||||
import numpy as np
|
||||
from numpy.testing import assert_, assert_allclose, assert_equal
|
||||
from pytest import raises as assert_raises
|
||||
from scipy.optimize._linprog_util import _clean_inputs, _LPProblem
|
||||
from copy import deepcopy
|
||||
from datetime import date
|
||||
|
||||
|
||||
def test_aliasing():
|
||||
"""
|
||||
Test for ensuring that no objects referred to by `lp` attributes,
|
||||
`c`, `A_ub`, `b_ub`, `A_eq`, `b_eq`, `bounds`, have been modified
|
||||
by `_clean_inputs` as a side effect.
|
||||
"""
|
||||
lp = _LPProblem(
|
||||
c=1,
|
||||
A_ub=[[1]],
|
||||
b_ub=[1],
|
||||
A_eq=[[1]],
|
||||
b_eq=[1],
|
||||
bounds=(-np.inf, np.inf)
|
||||
)
|
||||
lp_copy = deepcopy(lp)
|
||||
|
||||
_clean_inputs(lp)
|
||||
|
||||
assert_(lp.c == lp_copy.c, "c modified by _clean_inputs")
|
||||
assert_(lp.A_ub == lp_copy.A_ub, "A_ub modified by _clean_inputs")
|
||||
assert_(lp.b_ub == lp_copy.b_ub, "b_ub modified by _clean_inputs")
|
||||
assert_(lp.A_eq == lp_copy.A_eq, "A_eq modified by _clean_inputs")
|
||||
assert_(lp.b_eq == lp_copy.b_eq, "b_eq modified by _clean_inputs")
|
||||
assert_(lp.bounds == lp_copy.bounds, "bounds modified by _clean_inputs")
|
||||
|
||||
|
||||
def test_aliasing2():
|
||||
"""
|
||||
Similar purpose as `test_aliasing` above.
|
||||
"""
|
||||
lp = _LPProblem(
|
||||
c=np.array([1, 1]),
|
||||
A_ub=np.array([[1, 1], [2, 2]]),
|
||||
b_ub=np.array([[1], [1]]),
|
||||
A_eq=np.array([[1, 1]]),
|
||||
b_eq=np.array([1]),
|
||||
bounds=[(-np.inf, np.inf), (None, 1)]
|
||||
)
|
||||
lp_copy = deepcopy(lp)
|
||||
|
||||
_clean_inputs(lp)
|
||||
|
||||
assert_allclose(lp.c, lp_copy.c, err_msg="c modified by _clean_inputs")
|
||||
assert_allclose(lp.A_ub, lp_copy.A_ub, err_msg="A_ub modified by _clean_inputs")
|
||||
assert_allclose(lp.b_ub, lp_copy.b_ub, err_msg="b_ub modified by _clean_inputs")
|
||||
assert_allclose(lp.A_eq, lp_copy.A_eq, err_msg="A_eq modified by _clean_inputs")
|
||||
assert_allclose(lp.b_eq, lp_copy.b_eq, err_msg="b_eq modified by _clean_inputs")
|
||||
assert_(lp.bounds == lp_copy.bounds, "bounds modified by _clean_inputs")
|
||||
|
||||
|
||||
def test_missing_inputs():
|
||||
c = [1, 2]
|
||||
A_ub = np.array([[1, 1], [2, 2]])
|
||||
b_ub = np.array([1, 1])
|
||||
A_eq = np.array([[1, 1], [2, 2]])
|
||||
b_eq = np.array([1, 1])
|
||||
|
||||
assert_raises(TypeError, _clean_inputs)
|
||||
assert_raises(TypeError, _clean_inputs, _LPProblem(c=None))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=c, A_ub=A_ub))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=c, A_ub=A_ub, b_ub=None))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=c, b_ub=b_ub))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=c, A_ub=None, b_ub=b_ub))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=c, A_eq=A_eq))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=c, A_eq=A_eq, b_eq=None))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=c, b_eq=b_eq))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=c, A_eq=None, b_eq=b_eq))
|
||||
|
||||
|
||||
def test_too_many_dimensions():
|
||||
cb = [1, 2, 3, 4]
|
||||
A = np.random.rand(4, 4)
|
||||
bad2D = [[1, 2], [3, 4]]
|
||||
bad3D = np.random.rand(4, 4, 4)
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=bad2D, A_ub=A, b_ub=cb))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=cb, A_ub=bad3D, b_ub=cb))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=cb, A_ub=A, b_ub=bad2D))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=cb, A_eq=bad3D, b_eq=cb))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=cb, A_eq=A, b_eq=bad2D))
|
||||
|
||||
|
||||
def test_too_few_dimensions():
|
||||
bad = np.random.rand(4, 4).ravel()
|
||||
cb = np.random.rand(4)
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=cb, A_ub=bad, b_ub=cb))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=cb, A_eq=bad, b_eq=cb))
|
||||
|
||||
|
||||
def test_inconsistent_dimensions():
|
||||
m = 2
|
||||
n = 4
|
||||
c = [1, 2, 3, 4]
|
||||
|
||||
Agood = np.random.rand(m, n)
|
||||
Abad = np.random.rand(m, n + 1)
|
||||
bgood = np.random.rand(m)
|
||||
bbad = np.random.rand(m + 1)
|
||||
boundsbad = [(0, 1)] * (n + 1)
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=c, A_ub=Abad, b_ub=bgood))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=c, A_ub=Agood, b_ub=bbad))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=c, A_eq=Abad, b_eq=bgood))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=c, A_eq=Agood, b_eq=bbad))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=c, bounds=boundsbad))
|
||||
assert_raises(ValueError, _clean_inputs, _LPProblem(c=c, bounds=[[1, 2], [2, 3], [3, 4], [4, 5, 6]]))
|
||||
|
||||
|
||||
def test_type_errors():
|
||||
lp = _LPProblem(
|
||||
c=[1, 2],
|
||||
A_ub=np.array([[1, 1], [2, 2]]),
|
||||
b_ub=np.array([1, 1]),
|
||||
A_eq=np.array([[1, 1], [2, 2]]),
|
||||
b_eq=np.array([1, 1]),
|
||||
bounds=[(0, 1)]
|
||||
)
|
||||
bad = "hello"
|
||||
|
||||
assert_raises(TypeError, _clean_inputs, lp._replace(c=bad))
|
||||
assert_raises(TypeError, _clean_inputs, lp._replace(A_ub=bad))
|
||||
assert_raises(TypeError, _clean_inputs, lp._replace(b_ub=bad))
|
||||
assert_raises(TypeError, _clean_inputs, lp._replace(A_eq=bad))
|
||||
assert_raises(TypeError, _clean_inputs, lp._replace(b_eq=bad))
|
||||
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(bounds=bad))
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(bounds="hi"))
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(bounds=["hi"]))
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(bounds=[("hi")]))
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(bounds=[(1, "")]))
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(bounds=[(1, 2), (1, "")]))
|
||||
assert_raises(TypeError, _clean_inputs, lp._replace(bounds=[(1, date(2020, 2, 29))]))
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(bounds=[[[1, 2]]]))
|
||||
|
||||
|
||||
def test_non_finite_errors():
|
||||
lp = _LPProblem(
|
||||
c=[1, 2],
|
||||
A_ub=np.array([[1, 1], [2, 2]]),
|
||||
b_ub=np.array([1, 1]),
|
||||
A_eq=np.array([[1, 1], [2, 2]]),
|
||||
b_eq=np.array([1, 1]),
|
||||
bounds=[(0, 1)]
|
||||
)
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(c=[0, None]))
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(c=[np.inf, 0]))
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(c=[0, -np.inf]))
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(c=[np.nan, 0]))
|
||||
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(A_ub=[[1, 2], [None, 1]]))
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(b_ub=[np.inf, 1]))
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(A_eq=[[1, 2], [1, -np.inf]]))
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(b_eq=[1, np.nan]))
|
||||
|
||||
|
||||
def test__clean_inputs1():
|
||||
lp = _LPProblem(
|
||||
c=[1, 2],
|
||||
A_ub=[[1, 1], [2, 2]],
|
||||
b_ub=[1, 1],
|
||||
A_eq=[[1, 1], [2, 2]],
|
||||
b_eq=[1, 1],
|
||||
bounds=None
|
||||
)
|
||||
|
||||
lp_cleaned = _clean_inputs(lp)
|
||||
|
||||
assert_allclose(lp_cleaned.c, np.array(lp.c))
|
||||
assert_allclose(lp_cleaned.A_ub, np.array(lp.A_ub))
|
||||
assert_allclose(lp_cleaned.b_ub, np.array(lp.b_ub))
|
||||
assert_allclose(lp_cleaned.A_eq, np.array(lp.A_eq))
|
||||
assert_allclose(lp_cleaned.b_eq, np.array(lp.b_eq))
|
||||
assert_equal(lp_cleaned.bounds, [(0, np.inf)] * 2)
|
||||
|
||||
assert_(lp_cleaned.c.shape == (2,), "")
|
||||
assert_(lp_cleaned.A_ub.shape == (2, 2), "")
|
||||
assert_(lp_cleaned.b_ub.shape == (2,), "")
|
||||
assert_(lp_cleaned.A_eq.shape == (2, 2), "")
|
||||
assert_(lp_cleaned.b_eq.shape == (2,), "")
|
||||
|
||||
|
||||
def test__clean_inputs2():
|
||||
lp = _LPProblem(
|
||||
c=1,
|
||||
A_ub=[[1]],
|
||||
b_ub=1,
|
||||
A_eq=[[1]],
|
||||
b_eq=1,
|
||||
bounds=(0, 1)
|
||||
)
|
||||
|
||||
lp_cleaned = _clean_inputs(lp)
|
||||
|
||||
assert_allclose(lp_cleaned.c, np.array(lp.c))
|
||||
assert_allclose(lp_cleaned.A_ub, np.array(lp.A_ub))
|
||||
assert_allclose(lp_cleaned.b_ub, np.array(lp.b_ub))
|
||||
assert_allclose(lp_cleaned.A_eq, np.array(lp.A_eq))
|
||||
assert_allclose(lp_cleaned.b_eq, np.array(lp.b_eq))
|
||||
assert_equal(lp_cleaned.bounds, [(0, 1)])
|
||||
|
||||
assert_(lp_cleaned.c.shape == (1,), "")
|
||||
assert_(lp_cleaned.A_ub.shape == (1, 1), "")
|
||||
assert_(lp_cleaned.b_ub.shape == (1,), "")
|
||||
assert_(lp_cleaned.A_eq.shape == (1, 1), "")
|
||||
assert_(lp_cleaned.b_eq.shape == (1,), "")
|
||||
|
||||
|
||||
def test__clean_inputs3():
|
||||
lp = _LPProblem(
|
||||
c=[[1, 2]],
|
||||
A_ub=np.random.rand(2, 2),
|
||||
b_ub=[[1], [2]],
|
||||
A_eq=np.random.rand(2, 2),
|
||||
b_eq=[[1], [2]],
|
||||
bounds=[(0, 1)]
|
||||
)
|
||||
|
||||
lp_cleaned = _clean_inputs(lp)
|
||||
|
||||
assert_allclose(lp_cleaned.c, np.array([1, 2]))
|
||||
assert_allclose(lp_cleaned.b_ub, np.array([1, 2]))
|
||||
assert_allclose(lp_cleaned.b_eq, np.array([1, 2]))
|
||||
assert_equal(lp_cleaned.bounds, [(0, 1)] * 2)
|
||||
|
||||
assert_(lp_cleaned.c.shape == (2,), "")
|
||||
assert_(lp_cleaned.b_ub.shape == (2,), "")
|
||||
assert_(lp_cleaned.b_eq.shape == (2,), "")
|
||||
|
||||
|
||||
def test_bad_bounds():
|
||||
lp = _LPProblem(c=[1, 2])
|
||||
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(bounds=(1, 2, 2)))
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(bounds=[(1, 2, 2)]))
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(bounds=[(1, 2), (1, 2, 2)]))
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(bounds=[(1, 2), (1, 2), (1, 2)]))
|
||||
|
||||
lp = _LPProblem(c=[1, 2, 3, 4])
|
||||
|
||||
assert_raises(ValueError, _clean_inputs, lp._replace(bounds=[(1, 2, 3, 4), (1, 2, 3, 4)]))
|
||||
|
||||
|
||||
def test_good_bounds():
|
||||
lp = _LPProblem(c=[1, 2])
|
||||
|
||||
lp_cleaned = _clean_inputs(lp) # lp.bounds is None by default
|
||||
assert_equal(lp_cleaned.bounds, [(0, np.inf)] * 2)
|
||||
|
||||
lp_cleaned = _clean_inputs(lp._replace(bounds=[]))
|
||||
assert_equal(lp_cleaned.bounds, [(0, np.inf)] * 2)
|
||||
|
||||
lp_cleaned = _clean_inputs(lp._replace(bounds=[[]]))
|
||||
assert_equal(lp_cleaned.bounds, [(0, np.inf)] * 2)
|
||||
|
||||
lp_cleaned = _clean_inputs(lp._replace(bounds=(1, 2)))
|
||||
assert_equal(lp_cleaned.bounds, [(1, 2)] * 2)
|
||||
|
||||
lp_cleaned = _clean_inputs(lp._replace(bounds=[(1, 2)]))
|
||||
assert_equal(lp_cleaned.bounds, [(1, 2)] * 2)
|
||||
|
||||
lp_cleaned = _clean_inputs(lp._replace(bounds=[(1, None)]))
|
||||
assert_equal(lp_cleaned.bounds, [(1, np.inf)] * 2)
|
||||
|
||||
lp_cleaned = _clean_inputs(lp._replace(bounds=[(None, 1)]))
|
||||
assert_equal(lp_cleaned.bounds, [(-np.inf, 1)] * 2)
|
||||
|
||||
lp_cleaned = _clean_inputs(lp._replace(bounds=[(None, None), (-np.inf, None)]))
|
||||
assert_equal(lp_cleaned.bounds, [(-np.inf, np.inf)] * 2)
|
||||
|
||||
lp = _LPProblem(c=[1, 2, 3, 4])
|
||||
|
||||
lp_cleaned = _clean_inputs(lp) # lp.bounds is None by default
|
||||
assert_equal(lp_cleaned.bounds, [(0, np.inf)] * 4)
|
||||
|
||||
lp_cleaned = _clean_inputs(lp._replace(bounds=(1, 2)))
|
||||
assert_equal(lp_cleaned.bounds, [(1, 2)] * 4)
|
||||
|
||||
lp_cleaned = _clean_inputs(lp._replace(bounds=[(1, 2)]))
|
||||
assert_equal(lp_cleaned.bounds, [(1, 2)] * 4)
|
||||
|
||||
lp_cleaned = _clean_inputs(lp._replace(bounds=[(1, None)]))
|
||||
assert_equal(lp_cleaned.bounds, [(1, np.inf)] * 4)
|
||||
|
||||
lp_cleaned = _clean_inputs(lp._replace(bounds=[(None, 1)]))
|
||||
assert_equal(lp_cleaned.bounds, [(-np.inf, 1)] * 4)
|
||||
|
||||
lp_cleaned = _clean_inputs(lp._replace(bounds=[(None, None), (-np.inf, None), (None, np.inf), (-np.inf, np.inf)]))
|
||||
assert_equal(lp_cleaned.bounds, [(-np.inf, np.inf)] * 4)
|
||||
813
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__numdiff.py
vendored
Normal file
813
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__numdiff.py
vendored
Normal file
@@ -0,0 +1,813 @@
|
||||
import math
|
||||
from itertools import product
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose, assert_equal, assert_
|
||||
from pytest import raises as assert_raises
|
||||
|
||||
from scipy.sparse import csr_matrix, csc_matrix, lil_matrix
|
||||
|
||||
from scipy.optimize._numdiff import (
|
||||
_adjust_scheme_to_bounds, approx_derivative, check_derivative,
|
||||
group_columns, _eps_for_method, _compute_absolute_step)
|
||||
|
||||
|
||||
def test_group_columns():
|
||||
structure = [
|
||||
[1, 1, 0, 0, 0, 0],
|
||||
[1, 1, 1, 0, 0, 0],
|
||||
[0, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 0],
|
||||
[0, 0, 0, 1, 1, 1],
|
||||
[0, 0, 0, 0, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0]
|
||||
]
|
||||
for transform in [np.asarray, csr_matrix, csc_matrix, lil_matrix]:
|
||||
A = transform(structure)
|
||||
order = np.arange(6)
|
||||
groups_true = np.array([0, 1, 2, 0, 1, 2])
|
||||
groups = group_columns(A, order)
|
||||
assert_equal(groups, groups_true)
|
||||
|
||||
order = [1, 2, 4, 3, 5, 0]
|
||||
groups_true = np.array([2, 0, 1, 2, 0, 1])
|
||||
groups = group_columns(A, order)
|
||||
assert_equal(groups, groups_true)
|
||||
|
||||
# Test repeatability.
|
||||
groups_1 = group_columns(A)
|
||||
groups_2 = group_columns(A)
|
||||
assert_equal(groups_1, groups_2)
|
||||
|
||||
|
||||
def test_correct_fp_eps():
|
||||
# check that relative step size is correct for FP size
|
||||
EPS = np.finfo(np.float64).eps
|
||||
relative_step = {"2-point": EPS**0.5,
|
||||
"3-point": EPS**(1/3),
|
||||
"cs": EPS**0.5}
|
||||
for method in ['2-point', '3-point', 'cs']:
|
||||
assert_allclose(
|
||||
_eps_for_method(np.float64, np.float64, method),
|
||||
relative_step[method])
|
||||
assert_allclose(
|
||||
_eps_for_method(np.complex128, np.complex128, method),
|
||||
relative_step[method]
|
||||
)
|
||||
|
||||
# check another FP size
|
||||
EPS = np.finfo(np.float32).eps
|
||||
relative_step = {"2-point": EPS**0.5,
|
||||
"3-point": EPS**(1/3),
|
||||
"cs": EPS**0.5}
|
||||
|
||||
for method in ['2-point', '3-point', 'cs']:
|
||||
assert_allclose(
|
||||
_eps_for_method(np.float64, np.float32, method),
|
||||
relative_step[method]
|
||||
)
|
||||
assert_allclose(
|
||||
_eps_for_method(np.float32, np.float64, method),
|
||||
relative_step[method]
|
||||
)
|
||||
assert_allclose(
|
||||
_eps_for_method(np.float32, np.float32, method),
|
||||
relative_step[method]
|
||||
)
|
||||
|
||||
|
||||
class TestAdjustSchemeToBounds:
|
||||
def test_no_bounds(self):
|
||||
x0 = np.zeros(3)
|
||||
h = np.full(3, 1e-2)
|
||||
inf_lower = np.empty_like(x0)
|
||||
inf_upper = np.empty_like(x0)
|
||||
inf_lower.fill(-np.inf)
|
||||
inf_upper.fill(np.inf)
|
||||
|
||||
h_adjusted, one_sided = _adjust_scheme_to_bounds(
|
||||
x0, h, 1, '1-sided', inf_lower, inf_upper)
|
||||
assert_allclose(h_adjusted, h)
|
||||
assert_(np.all(one_sided))
|
||||
|
||||
h_adjusted, one_sided = _adjust_scheme_to_bounds(
|
||||
x0, h, 2, '1-sided', inf_lower, inf_upper)
|
||||
assert_allclose(h_adjusted, h)
|
||||
assert_(np.all(one_sided))
|
||||
|
||||
h_adjusted, one_sided = _adjust_scheme_to_bounds(
|
||||
x0, h, 1, '2-sided', inf_lower, inf_upper)
|
||||
assert_allclose(h_adjusted, h)
|
||||
assert_(np.all(~one_sided))
|
||||
|
||||
h_adjusted, one_sided = _adjust_scheme_to_bounds(
|
||||
x0, h, 2, '2-sided', inf_lower, inf_upper)
|
||||
assert_allclose(h_adjusted, h)
|
||||
assert_(np.all(~one_sided))
|
||||
|
||||
def test_with_bound(self):
|
||||
x0 = np.array([0.0, 0.85, -0.85])
|
||||
lb = -np.ones(3)
|
||||
ub = np.ones(3)
|
||||
h = np.array([1, 1, -1]) * 1e-1
|
||||
|
||||
h_adjusted, _ = _adjust_scheme_to_bounds(x0, h, 1, '1-sided', lb, ub)
|
||||
assert_allclose(h_adjusted, h)
|
||||
|
||||
h_adjusted, _ = _adjust_scheme_to_bounds(x0, h, 2, '1-sided', lb, ub)
|
||||
assert_allclose(h_adjusted, np.array([1, -1, 1]) * 1e-1)
|
||||
|
||||
h_adjusted, one_sided = _adjust_scheme_to_bounds(
|
||||
x0, h, 1, '2-sided', lb, ub)
|
||||
assert_allclose(h_adjusted, np.abs(h))
|
||||
assert_(np.all(~one_sided))
|
||||
|
||||
h_adjusted, one_sided = _adjust_scheme_to_bounds(
|
||||
x0, h, 2, '2-sided', lb, ub)
|
||||
assert_allclose(h_adjusted, np.array([1, -1, 1]) * 1e-1)
|
||||
assert_equal(one_sided, np.array([False, True, True]))
|
||||
|
||||
def test_tight_bounds(self):
|
||||
lb = np.array([-0.03, -0.03])
|
||||
ub = np.array([0.05, 0.05])
|
||||
x0 = np.array([0.0, 0.03])
|
||||
h = np.array([-0.1, -0.1])
|
||||
|
||||
h_adjusted, _ = _adjust_scheme_to_bounds(x0, h, 1, '1-sided', lb, ub)
|
||||
assert_allclose(h_adjusted, np.array([0.05, -0.06]))
|
||||
|
||||
h_adjusted, _ = _adjust_scheme_to_bounds(x0, h, 2, '1-sided', lb, ub)
|
||||
assert_allclose(h_adjusted, np.array([0.025, -0.03]))
|
||||
|
||||
h_adjusted, one_sided = _adjust_scheme_to_bounds(
|
||||
x0, h, 1, '2-sided', lb, ub)
|
||||
assert_allclose(h_adjusted, np.array([0.03, -0.03]))
|
||||
assert_equal(one_sided, np.array([False, True]))
|
||||
|
||||
h_adjusted, one_sided = _adjust_scheme_to_bounds(
|
||||
x0, h, 2, '2-sided', lb, ub)
|
||||
assert_allclose(h_adjusted, np.array([0.015, -0.015]))
|
||||
assert_equal(one_sided, np.array([False, True]))
|
||||
|
||||
|
||||
class TestApproxDerivativesDense:
|
||||
def fun_scalar_scalar(self, x):
|
||||
return np.sinh(x)
|
||||
|
||||
def jac_scalar_scalar(self, x):
|
||||
return np.cosh(x)
|
||||
|
||||
def fun_scalar_vector(self, x):
|
||||
return np.array([x[0]**2, np.tan(x[0]), np.exp(x[0])])
|
||||
|
||||
def jac_scalar_vector(self, x):
|
||||
return np.array(
|
||||
[2 * x[0], np.cos(x[0]) ** -2, np.exp(x[0])]).reshape(-1, 1)
|
||||
|
||||
def fun_vector_scalar(self, x):
|
||||
return np.sin(x[0] * x[1]) * np.log(x[0])
|
||||
|
||||
def wrong_dimensions_fun(self, x):
|
||||
return np.array([x**2, np.tan(x), np.exp(x)])
|
||||
|
||||
def jac_vector_scalar(self, x):
|
||||
return np.array([
|
||||
x[1] * np.cos(x[0] * x[1]) * np.log(x[0]) +
|
||||
np.sin(x[0] * x[1]) / x[0],
|
||||
x[0] * np.cos(x[0] * x[1]) * np.log(x[0])
|
||||
])
|
||||
|
||||
def fun_vector_vector(self, x):
|
||||
return np.array([
|
||||
x[0] * np.sin(x[1]),
|
||||
x[1] * np.cos(x[0]),
|
||||
x[0] ** 3 * x[1] ** -0.5
|
||||
])
|
||||
|
||||
def jac_vector_vector(self, x):
|
||||
return np.array([
|
||||
[np.sin(x[1]), x[0] * np.cos(x[1])],
|
||||
[-x[1] * np.sin(x[0]), np.cos(x[0])],
|
||||
[3 * x[0] ** 2 * x[1] ** -0.5, -0.5 * x[0] ** 3 * x[1] ** -1.5]
|
||||
])
|
||||
|
||||
def fun_parametrized(self, x, c0, c1=1.0):
|
||||
return np.array([np.exp(c0 * x[0]), np.exp(c1 * x[1])])
|
||||
|
||||
def jac_parametrized(self, x, c0, c1=0.1):
|
||||
return np.array([
|
||||
[c0 * np.exp(c0 * x[0]), 0],
|
||||
[0, c1 * np.exp(c1 * x[1])]
|
||||
])
|
||||
|
||||
def fun_with_nan(self, x):
|
||||
return x if np.abs(x) <= 1e-8 else np.nan
|
||||
|
||||
def jac_with_nan(self, x):
|
||||
return 1.0 if np.abs(x) <= 1e-8 else np.nan
|
||||
|
||||
def fun_zero_jacobian(self, x):
|
||||
return np.array([x[0] * x[1], np.cos(x[0] * x[1])])
|
||||
|
||||
def jac_zero_jacobian(self, x):
|
||||
return np.array([
|
||||
[x[1], x[0]],
|
||||
[-x[1] * np.sin(x[0] * x[1]), -x[0] * np.sin(x[0] * x[1])]
|
||||
])
|
||||
|
||||
def fun_non_numpy(self, x):
|
||||
return math.exp(x)
|
||||
|
||||
def jac_non_numpy(self, x):
|
||||
# x can be a scalar or an array [val].
|
||||
# Cast to true scalar before handing over to math.exp
|
||||
xp = np.asarray(x).item()
|
||||
return math.exp(xp)
|
||||
|
||||
def test_scalar_scalar(self):
|
||||
x0 = 1.0
|
||||
jac_diff_2 = approx_derivative(self.fun_scalar_scalar, x0,
|
||||
method='2-point')
|
||||
jac_diff_3 = approx_derivative(self.fun_scalar_scalar, x0)
|
||||
jac_diff_4 = approx_derivative(self.fun_scalar_scalar, x0,
|
||||
method='cs')
|
||||
jac_true = self.jac_scalar_scalar(x0)
|
||||
assert_allclose(jac_diff_2, jac_true, rtol=1e-6)
|
||||
assert_allclose(jac_diff_3, jac_true, rtol=1e-9)
|
||||
assert_allclose(jac_diff_4, jac_true, rtol=1e-12)
|
||||
|
||||
def test_scalar_scalar_abs_step(self):
|
||||
# can approx_derivative use abs_step?
|
||||
x0 = 1.0
|
||||
jac_diff_2 = approx_derivative(self.fun_scalar_scalar, x0,
|
||||
method='2-point', abs_step=1.49e-8)
|
||||
jac_diff_3 = approx_derivative(self.fun_scalar_scalar, x0,
|
||||
abs_step=1.49e-8)
|
||||
jac_diff_4 = approx_derivative(self.fun_scalar_scalar, x0,
|
||||
method='cs', abs_step=1.49e-8)
|
||||
jac_true = self.jac_scalar_scalar(x0)
|
||||
assert_allclose(jac_diff_2, jac_true, rtol=1e-6)
|
||||
assert_allclose(jac_diff_3, jac_true, rtol=1e-9)
|
||||
assert_allclose(jac_diff_4, jac_true, rtol=1e-12)
|
||||
|
||||
def test_scalar_vector(self):
|
||||
x0 = 0.5
|
||||
jac_diff_2 = approx_derivative(self.fun_scalar_vector, x0,
|
||||
method='2-point')
|
||||
jac_diff_3 = approx_derivative(self.fun_scalar_vector, x0)
|
||||
jac_diff_4 = approx_derivative(self.fun_scalar_vector, x0,
|
||||
method='cs')
|
||||
jac_true = self.jac_scalar_vector(np.atleast_1d(x0))
|
||||
assert_allclose(jac_diff_2, jac_true, rtol=1e-6)
|
||||
assert_allclose(jac_diff_3, jac_true, rtol=1e-9)
|
||||
assert_allclose(jac_diff_4, jac_true, rtol=1e-12)
|
||||
|
||||
def test_vector_scalar(self):
|
||||
x0 = np.array([100.0, -0.5])
|
||||
jac_diff_2 = approx_derivative(self.fun_vector_scalar, x0,
|
||||
method='2-point')
|
||||
jac_diff_3 = approx_derivative(self.fun_vector_scalar, x0)
|
||||
jac_diff_4 = approx_derivative(self.fun_vector_scalar, x0,
|
||||
method='cs')
|
||||
jac_true = self.jac_vector_scalar(x0)
|
||||
assert_allclose(jac_diff_2, jac_true, rtol=1e-6)
|
||||
assert_allclose(jac_diff_3, jac_true, rtol=1e-7)
|
||||
assert_allclose(jac_diff_4, jac_true, rtol=1e-12)
|
||||
|
||||
def test_vector_scalar_abs_step(self):
|
||||
# can approx_derivative use abs_step?
|
||||
x0 = np.array([100.0, -0.5])
|
||||
jac_diff_2 = approx_derivative(self.fun_vector_scalar, x0,
|
||||
method='2-point', abs_step=1.49e-8)
|
||||
jac_diff_3 = approx_derivative(self.fun_vector_scalar, x0,
|
||||
abs_step=1.49e-8, rel_step=np.inf)
|
||||
jac_diff_4 = approx_derivative(self.fun_vector_scalar, x0,
|
||||
method='cs', abs_step=1.49e-8)
|
||||
jac_true = self.jac_vector_scalar(x0)
|
||||
assert_allclose(jac_diff_2, jac_true, rtol=1e-6)
|
||||
assert_allclose(jac_diff_3, jac_true, rtol=3e-9)
|
||||
assert_allclose(jac_diff_4, jac_true, rtol=1e-12)
|
||||
|
||||
def test_vector_vector(self):
|
||||
x0 = np.array([-100.0, 0.2])
|
||||
jac_diff_2 = approx_derivative(self.fun_vector_vector, x0,
|
||||
method='2-point')
|
||||
jac_diff_3 = approx_derivative(self.fun_vector_vector, x0)
|
||||
jac_diff_4 = approx_derivative(self.fun_vector_vector, x0,
|
||||
method='cs')
|
||||
jac_true = self.jac_vector_vector(x0)
|
||||
assert_allclose(jac_diff_2, jac_true, rtol=1e-5)
|
||||
assert_allclose(jac_diff_3, jac_true, rtol=1e-6)
|
||||
assert_allclose(jac_diff_4, jac_true, rtol=1e-12)
|
||||
|
||||
def test_wrong_dimensions(self):
|
||||
x0 = 1.0
|
||||
assert_raises(RuntimeError, approx_derivative,
|
||||
self.wrong_dimensions_fun, x0)
|
||||
f0 = self.wrong_dimensions_fun(np.atleast_1d(x0))
|
||||
assert_raises(ValueError, approx_derivative,
|
||||
self.wrong_dimensions_fun, x0, f0=f0)
|
||||
|
||||
def test_custom_rel_step(self):
|
||||
x0 = np.array([-0.1, 0.1])
|
||||
jac_diff_2 = approx_derivative(self.fun_vector_vector, x0,
|
||||
method='2-point', rel_step=1e-4)
|
||||
jac_diff_3 = approx_derivative(self.fun_vector_vector, x0,
|
||||
rel_step=1e-4)
|
||||
jac_true = self.jac_vector_vector(x0)
|
||||
assert_allclose(jac_diff_2, jac_true, rtol=1e-2)
|
||||
assert_allclose(jac_diff_3, jac_true, rtol=1e-4)
|
||||
|
||||
def test_options(self):
|
||||
x0 = np.array([1.0, 1.0])
|
||||
c0 = -1.0
|
||||
c1 = 1.0
|
||||
lb = 0.0
|
||||
ub = 2.0
|
||||
f0 = self.fun_parametrized(x0, c0, c1=c1)
|
||||
rel_step = np.array([-1e-6, 1e-7])
|
||||
jac_true = self.jac_parametrized(x0, c0, c1)
|
||||
jac_diff_2 = approx_derivative(
|
||||
self.fun_parametrized, x0, method='2-point', rel_step=rel_step,
|
||||
f0=f0, args=(c0,), kwargs=dict(c1=c1), bounds=(lb, ub))
|
||||
jac_diff_3 = approx_derivative(
|
||||
self.fun_parametrized, x0, rel_step=rel_step,
|
||||
f0=f0, args=(c0,), kwargs=dict(c1=c1), bounds=(lb, ub))
|
||||
assert_allclose(jac_diff_2, jac_true, rtol=1e-6)
|
||||
assert_allclose(jac_diff_3, jac_true, rtol=1e-9)
|
||||
|
||||
def test_with_bounds_2_point(self):
|
||||
lb = -np.ones(2)
|
||||
ub = np.ones(2)
|
||||
|
||||
x0 = np.array([-2.0, 0.2])
|
||||
assert_raises(ValueError, approx_derivative,
|
||||
self.fun_vector_vector, x0, bounds=(lb, ub))
|
||||
|
||||
x0 = np.array([-1.0, 1.0])
|
||||
jac_diff = approx_derivative(self.fun_vector_vector, x0,
|
||||
method='2-point', bounds=(lb, ub))
|
||||
jac_true = self.jac_vector_vector(x0)
|
||||
assert_allclose(jac_diff, jac_true, rtol=1e-6)
|
||||
|
||||
def test_with_bounds_3_point(self):
|
||||
lb = np.array([1.0, 1.0])
|
||||
ub = np.array([2.0, 2.0])
|
||||
|
||||
x0 = np.array([1.0, 2.0])
|
||||
jac_true = self.jac_vector_vector(x0)
|
||||
|
||||
jac_diff = approx_derivative(self.fun_vector_vector, x0)
|
||||
assert_allclose(jac_diff, jac_true, rtol=1e-9)
|
||||
|
||||
jac_diff = approx_derivative(self.fun_vector_vector, x0,
|
||||
bounds=(lb, np.inf))
|
||||
assert_allclose(jac_diff, jac_true, rtol=1e-9)
|
||||
|
||||
jac_diff = approx_derivative(self.fun_vector_vector, x0,
|
||||
bounds=(-np.inf, ub))
|
||||
assert_allclose(jac_diff, jac_true, rtol=1e-9)
|
||||
|
||||
jac_diff = approx_derivative(self.fun_vector_vector, x0,
|
||||
bounds=(lb, ub))
|
||||
assert_allclose(jac_diff, jac_true, rtol=1e-9)
|
||||
|
||||
def test_tight_bounds(self):
|
||||
x0 = np.array([10.0, 10.0])
|
||||
lb = x0 - 3e-9
|
||||
ub = x0 + 2e-9
|
||||
jac_true = self.jac_vector_vector(x0)
|
||||
jac_diff = approx_derivative(
|
||||
self.fun_vector_vector, x0, method='2-point', bounds=(lb, ub))
|
||||
assert_allclose(jac_diff, jac_true, rtol=1e-6)
|
||||
jac_diff = approx_derivative(
|
||||
self.fun_vector_vector, x0, method='2-point',
|
||||
rel_step=1e-6, bounds=(lb, ub))
|
||||
assert_allclose(jac_diff, jac_true, rtol=1e-6)
|
||||
|
||||
jac_diff = approx_derivative(
|
||||
self.fun_vector_vector, x0, bounds=(lb, ub))
|
||||
assert_allclose(jac_diff, jac_true, rtol=1e-6)
|
||||
jac_diff = approx_derivative(
|
||||
self.fun_vector_vector, x0, rel_step=1e-6, bounds=(lb, ub))
|
||||
assert_allclose(jac_true, jac_diff, rtol=1e-6)
|
||||
|
||||
def test_bound_switches(self):
|
||||
lb = -1e-8
|
||||
ub = 1e-8
|
||||
x0 = 0.0
|
||||
jac_true = self.jac_with_nan(x0)
|
||||
jac_diff_2 = approx_derivative(
|
||||
self.fun_with_nan, x0, method='2-point', rel_step=1e-6,
|
||||
bounds=(lb, ub))
|
||||
jac_diff_3 = approx_derivative(
|
||||
self.fun_with_nan, x0, rel_step=1e-6, bounds=(lb, ub))
|
||||
assert_allclose(jac_diff_2, jac_true, rtol=1e-6)
|
||||
assert_allclose(jac_diff_3, jac_true, rtol=1e-9)
|
||||
|
||||
x0 = 1e-8
|
||||
jac_true = self.jac_with_nan(x0)
|
||||
jac_diff_2 = approx_derivative(
|
||||
self.fun_with_nan, x0, method='2-point', rel_step=1e-6,
|
||||
bounds=(lb, ub))
|
||||
jac_diff_3 = approx_derivative(
|
||||
self.fun_with_nan, x0, rel_step=1e-6, bounds=(lb, ub))
|
||||
assert_allclose(jac_diff_2, jac_true, rtol=1e-6)
|
||||
assert_allclose(jac_diff_3, jac_true, rtol=1e-9)
|
||||
|
||||
def test_non_numpy(self):
|
||||
x0 = 1.0
|
||||
jac_true = self.jac_non_numpy(x0)
|
||||
jac_diff_2 = approx_derivative(self.jac_non_numpy, x0,
|
||||
method='2-point')
|
||||
jac_diff_3 = approx_derivative(self.jac_non_numpy, x0)
|
||||
assert_allclose(jac_diff_2, jac_true, rtol=1e-6)
|
||||
assert_allclose(jac_diff_3, jac_true, rtol=1e-8)
|
||||
|
||||
# math.exp cannot handle complex arguments, hence this raises
|
||||
assert_raises(TypeError, approx_derivative, self.jac_non_numpy, x0,
|
||||
**dict(method='cs'))
|
||||
|
||||
def test_fp(self):
|
||||
# checks that approx_derivative works for FP size other than 64.
|
||||
# Example is derived from the minimal working example in gh12991.
|
||||
np.random.seed(1)
|
||||
|
||||
def func(p, x):
|
||||
return p[0] + p[1] * x
|
||||
|
||||
def err(p, x, y):
|
||||
return func(p, x) - y
|
||||
|
||||
x = np.linspace(0, 1, 100, dtype=np.float64)
|
||||
y = np.random.random(100).astype(np.float64)
|
||||
p0 = np.array([-1.0, -1.0])
|
||||
|
||||
jac_fp64 = approx_derivative(err, p0, method='2-point', args=(x, y))
|
||||
|
||||
# parameter vector is float32, func output is float64
|
||||
jac_fp = approx_derivative(err, p0.astype(np.float32),
|
||||
method='2-point', args=(x, y))
|
||||
assert err(p0, x, y).dtype == np.float64
|
||||
assert_allclose(jac_fp, jac_fp64, atol=1e-3)
|
||||
|
||||
# parameter vector is float64, func output is float32
|
||||
err_fp32 = lambda p: err(p, x, y).astype(np.float32)
|
||||
jac_fp = approx_derivative(err_fp32, p0,
|
||||
method='2-point')
|
||||
assert err_fp32(p0).dtype == np.float32
|
||||
assert_allclose(jac_fp, jac_fp64, atol=1e-3)
|
||||
|
||||
# check upper bound of error on the derivative for 2-point
|
||||
f = lambda x: np.sin(x)
|
||||
g = lambda x: np.cos(x)
|
||||
hess = lambda x: -np.sin(x)
|
||||
|
||||
def calc_atol(h, x0, f, hess, EPS):
|
||||
# truncation error
|
||||
t0 = h / 2 * max(np.abs(hess(x0)), np.abs(hess(x0 + h)))
|
||||
# roundoff error. There may be a divisor (>1) missing from
|
||||
# the following line, so this contribution is possibly
|
||||
# overestimated
|
||||
t1 = EPS / h * max(np.abs(f(x0)), np.abs(f(x0 + h)))
|
||||
return t0 + t1
|
||||
|
||||
for dtype in [np.float16, np.float32, np.float64]:
|
||||
EPS = np.finfo(dtype).eps
|
||||
x0 = np.array(1.0).astype(dtype)
|
||||
h = _compute_absolute_step(None, x0, f(x0), '2-point')
|
||||
atol = calc_atol(h, x0, f, hess, EPS)
|
||||
err = approx_derivative(f, x0, method='2-point',
|
||||
abs_step=h) - g(x0)
|
||||
assert abs(err) < atol
|
||||
|
||||
def test_check_derivative(self):
|
||||
x0 = np.array([-10.0, 10])
|
||||
accuracy = check_derivative(self.fun_vector_vector,
|
||||
self.jac_vector_vector, x0)
|
||||
assert_(accuracy < 1e-9)
|
||||
accuracy = check_derivative(self.fun_vector_vector,
|
||||
self.jac_vector_vector, x0)
|
||||
assert_(accuracy < 1e-6)
|
||||
|
||||
x0 = np.array([0.0, 0.0])
|
||||
accuracy = check_derivative(self.fun_zero_jacobian,
|
||||
self.jac_zero_jacobian, x0)
|
||||
assert_(accuracy == 0)
|
||||
accuracy = check_derivative(self.fun_zero_jacobian,
|
||||
self.jac_zero_jacobian, x0)
|
||||
assert_(accuracy == 0)
|
||||
|
||||
|
||||
class TestApproxDerivativeSparse:
|
||||
# Example from Numerical Optimization 2nd edition, p. 198.
|
||||
def setup_method(self):
|
||||
np.random.seed(0)
|
||||
self.n = 50
|
||||
self.lb = -0.1 * (1 + np.arange(self.n))
|
||||
self.ub = 0.1 * (1 + np.arange(self.n))
|
||||
self.x0 = np.empty(self.n)
|
||||
self.x0[::2] = (1 - 1e-7) * self.lb[::2]
|
||||
self.x0[1::2] = (1 - 1e-7) * self.ub[1::2]
|
||||
|
||||
self.J_true = self.jac(self.x0)
|
||||
|
||||
def fun(self, x):
|
||||
e = x[1:]**3 - x[:-1]**2
|
||||
return np.hstack((0, 3 * e)) + np.hstack((2 * e, 0))
|
||||
|
||||
def jac(self, x):
|
||||
n = x.size
|
||||
J = np.zeros((n, n))
|
||||
J[0, 0] = -4 * x[0]
|
||||
J[0, 1] = 6 * x[1]**2
|
||||
for i in range(1, n - 1):
|
||||
J[i, i - 1] = -6 * x[i-1]
|
||||
J[i, i] = 9 * x[i]**2 - 4 * x[i]
|
||||
J[i, i + 1] = 6 * x[i+1]**2
|
||||
J[-1, -1] = 9 * x[-1]**2
|
||||
J[-1, -2] = -6 * x[-2]
|
||||
|
||||
return J
|
||||
|
||||
def structure(self, n):
|
||||
A = np.zeros((n, n), dtype=int)
|
||||
A[0, 0] = 1
|
||||
A[0, 1] = 1
|
||||
for i in range(1, n - 1):
|
||||
A[i, i - 1: i + 2] = 1
|
||||
A[-1, -1] = 1
|
||||
A[-1, -2] = 1
|
||||
|
||||
return A
|
||||
|
||||
def test_all(self):
|
||||
A = self.structure(self.n)
|
||||
order = np.arange(self.n)
|
||||
groups_1 = group_columns(A, order)
|
||||
np.random.shuffle(order)
|
||||
groups_2 = group_columns(A, order)
|
||||
|
||||
for method, groups, l, u in product(
|
||||
['2-point', '3-point', 'cs'], [groups_1, groups_2],
|
||||
[-np.inf, self.lb], [np.inf, self.ub]):
|
||||
J = approx_derivative(self.fun, self.x0, method=method,
|
||||
bounds=(l, u), sparsity=(A, groups))
|
||||
assert_(isinstance(J, csr_matrix))
|
||||
assert_allclose(J.toarray(), self.J_true, rtol=1e-6)
|
||||
|
||||
rel_step = np.full_like(self.x0, 1e-8)
|
||||
rel_step[::2] *= -1
|
||||
J = approx_derivative(self.fun, self.x0, method=method,
|
||||
rel_step=rel_step, sparsity=(A, groups))
|
||||
assert_allclose(J.toarray(), self.J_true, rtol=1e-5)
|
||||
|
||||
def test_no_precomputed_groups(self):
|
||||
A = self.structure(self.n)
|
||||
J = approx_derivative(self.fun, self.x0, sparsity=A)
|
||||
assert_allclose(J.toarray(), self.J_true, rtol=1e-6)
|
||||
|
||||
def test_equivalence(self):
|
||||
structure = np.ones((self.n, self.n), dtype=int)
|
||||
groups = np.arange(self.n)
|
||||
for method in ['2-point', '3-point', 'cs']:
|
||||
J_dense = approx_derivative(self.fun, self.x0, method=method)
|
||||
J_sparse = approx_derivative(
|
||||
self.fun, self.x0, sparsity=(structure, groups), method=method)
|
||||
assert_allclose(J_dense, J_sparse.toarray(),
|
||||
rtol=5e-16, atol=7e-15)
|
||||
|
||||
def test_check_derivative(self):
|
||||
def jac(x):
|
||||
return csr_matrix(self.jac(x))
|
||||
|
||||
accuracy = check_derivative(self.fun, jac, self.x0,
|
||||
bounds=(self.lb, self.ub))
|
||||
assert_(accuracy < 1e-9)
|
||||
|
||||
accuracy = check_derivative(self.fun, jac, self.x0,
|
||||
bounds=(self.lb, self.ub))
|
||||
assert_(accuracy < 1e-9)
|
||||
|
||||
|
||||
class TestApproxDerivativeLinearOperator:
|
||||
|
||||
def fun_scalar_scalar(self, x):
|
||||
return np.sinh(x)
|
||||
|
||||
def jac_scalar_scalar(self, x):
|
||||
return np.cosh(x)
|
||||
|
||||
def fun_scalar_vector(self, x):
|
||||
return np.array([x[0]**2, np.tan(x[0]), np.exp(x[0])])
|
||||
|
||||
def jac_scalar_vector(self, x):
|
||||
return np.array(
|
||||
[2 * x[0], np.cos(x[0]) ** -2, np.exp(x[0])]).reshape(-1, 1)
|
||||
|
||||
def fun_vector_scalar(self, x):
|
||||
return np.sin(x[0] * x[1]) * np.log(x[0])
|
||||
|
||||
def jac_vector_scalar(self, x):
|
||||
return np.array([
|
||||
x[1] * np.cos(x[0] * x[1]) * np.log(x[0]) +
|
||||
np.sin(x[0] * x[1]) / x[0],
|
||||
x[0] * np.cos(x[0] * x[1]) * np.log(x[0])
|
||||
])
|
||||
|
||||
def fun_vector_vector(self, x):
|
||||
return np.array([
|
||||
x[0] * np.sin(x[1]),
|
||||
x[1] * np.cos(x[0]),
|
||||
x[0] ** 3 * x[1] ** -0.5
|
||||
])
|
||||
|
||||
def jac_vector_vector(self, x):
|
||||
return np.array([
|
||||
[np.sin(x[1]), x[0] * np.cos(x[1])],
|
||||
[-x[1] * np.sin(x[0]), np.cos(x[0])],
|
||||
[3 * x[0] ** 2 * x[1] ** -0.5, -0.5 * x[0] ** 3 * x[1] ** -1.5]
|
||||
])
|
||||
|
||||
def test_scalar_scalar(self):
|
||||
x0 = 1.0
|
||||
jac_diff_2 = approx_derivative(self.fun_scalar_scalar, x0,
|
||||
method='2-point',
|
||||
as_linear_operator=True)
|
||||
jac_diff_3 = approx_derivative(self.fun_scalar_scalar, x0,
|
||||
as_linear_operator=True)
|
||||
jac_diff_4 = approx_derivative(self.fun_scalar_scalar, x0,
|
||||
method='cs',
|
||||
as_linear_operator=True)
|
||||
jac_true = self.jac_scalar_scalar(x0)
|
||||
np.random.seed(1)
|
||||
for i in range(10):
|
||||
p = np.random.uniform(-10, 10, size=(1,))
|
||||
assert_allclose(jac_diff_2.dot(p), jac_true*p,
|
||||
rtol=1e-5)
|
||||
assert_allclose(jac_diff_3.dot(p), jac_true*p,
|
||||
rtol=5e-6)
|
||||
assert_allclose(jac_diff_4.dot(p), jac_true*p,
|
||||
rtol=5e-6)
|
||||
|
||||
def test_scalar_vector(self):
|
||||
x0 = 0.5
|
||||
jac_diff_2 = approx_derivative(self.fun_scalar_vector, x0,
|
||||
method='2-point',
|
||||
as_linear_operator=True)
|
||||
jac_diff_3 = approx_derivative(self.fun_scalar_vector, x0,
|
||||
as_linear_operator=True)
|
||||
jac_diff_4 = approx_derivative(self.fun_scalar_vector, x0,
|
||||
method='cs',
|
||||
as_linear_operator=True)
|
||||
jac_true = self.jac_scalar_vector(np.atleast_1d(x0))
|
||||
np.random.seed(1)
|
||||
for i in range(10):
|
||||
p = np.random.uniform(-10, 10, size=(1,))
|
||||
assert_allclose(jac_diff_2.dot(p), jac_true.dot(p),
|
||||
rtol=1e-5)
|
||||
assert_allclose(jac_diff_3.dot(p), jac_true.dot(p),
|
||||
rtol=5e-6)
|
||||
assert_allclose(jac_diff_4.dot(p), jac_true.dot(p),
|
||||
rtol=5e-6)
|
||||
|
||||
def test_vector_scalar(self):
|
||||
x0 = np.array([100.0, -0.5])
|
||||
jac_diff_2 = approx_derivative(self.fun_vector_scalar, x0,
|
||||
method='2-point',
|
||||
as_linear_operator=True)
|
||||
jac_diff_3 = approx_derivative(self.fun_vector_scalar, x0,
|
||||
as_linear_operator=True)
|
||||
jac_diff_4 = approx_derivative(self.fun_vector_scalar, x0,
|
||||
method='cs',
|
||||
as_linear_operator=True)
|
||||
jac_true = self.jac_vector_scalar(x0)
|
||||
np.random.seed(1)
|
||||
for i in range(10):
|
||||
p = np.random.uniform(-10, 10, size=x0.shape)
|
||||
assert_allclose(jac_diff_2.dot(p), np.atleast_1d(jac_true.dot(p)),
|
||||
rtol=1e-5)
|
||||
assert_allclose(jac_diff_3.dot(p), np.atleast_1d(jac_true.dot(p)),
|
||||
rtol=5e-6)
|
||||
assert_allclose(jac_diff_4.dot(p), np.atleast_1d(jac_true.dot(p)),
|
||||
rtol=1e-7)
|
||||
|
||||
def test_vector_vector(self):
|
||||
x0 = np.array([-100.0, 0.2])
|
||||
jac_diff_2 = approx_derivative(self.fun_vector_vector, x0,
|
||||
method='2-point',
|
||||
as_linear_operator=True)
|
||||
jac_diff_3 = approx_derivative(self.fun_vector_vector, x0,
|
||||
as_linear_operator=True)
|
||||
jac_diff_4 = approx_derivative(self.fun_vector_vector, x0,
|
||||
method='cs',
|
||||
as_linear_operator=True)
|
||||
jac_true = self.jac_vector_vector(x0)
|
||||
np.random.seed(1)
|
||||
for i in range(10):
|
||||
p = np.random.uniform(-10, 10, size=x0.shape)
|
||||
assert_allclose(jac_diff_2.dot(p), jac_true.dot(p), rtol=1e-5)
|
||||
assert_allclose(jac_diff_3.dot(p), jac_true.dot(p), rtol=1e-6)
|
||||
assert_allclose(jac_diff_4.dot(p), jac_true.dot(p), rtol=1e-7)
|
||||
|
||||
def test_exception(self):
|
||||
x0 = np.array([-100.0, 0.2])
|
||||
assert_raises(ValueError, approx_derivative,
|
||||
self.fun_vector_vector, x0,
|
||||
method='2-point', bounds=(1, np.inf))
|
||||
|
||||
|
||||
def test_absolute_step_sign():
|
||||
# test for gh12487
|
||||
# if an absolute step is specified for 2-point differences make sure that
|
||||
# the side corresponds to the step. i.e. if step is positive then forward
|
||||
# differences should be used, if step is negative then backwards
|
||||
# differences should be used.
|
||||
|
||||
# function has double discontinuity at x = [-1, -1]
|
||||
# first component is \/, second component is /\
|
||||
def f(x):
|
||||
return -np.abs(x[0] + 1) + np.abs(x[1] + 1)
|
||||
|
||||
# check that the forward difference is used
|
||||
grad = approx_derivative(f, [-1, -1], method='2-point', abs_step=1e-8)
|
||||
assert_allclose(grad, [-1.0, 1.0])
|
||||
|
||||
# check that the backwards difference is used
|
||||
grad = approx_derivative(f, [-1, -1], method='2-point', abs_step=-1e-8)
|
||||
assert_allclose(grad, [1.0, -1.0])
|
||||
|
||||
# check that the forwards difference is used with a step for both
|
||||
# parameters
|
||||
grad = approx_derivative(
|
||||
f, [-1, -1], method='2-point', abs_step=[1e-8, 1e-8]
|
||||
)
|
||||
assert_allclose(grad, [-1.0, 1.0])
|
||||
|
||||
# check that we can mix forward/backwards steps.
|
||||
grad = approx_derivative(
|
||||
f, [-1, -1], method='2-point', abs_step=[1e-8, -1e-8]
|
||||
)
|
||||
assert_allclose(grad, [-1.0, -1.0])
|
||||
grad = approx_derivative(
|
||||
f, [-1, -1], method='2-point', abs_step=[-1e-8, 1e-8]
|
||||
)
|
||||
assert_allclose(grad, [1.0, 1.0])
|
||||
|
||||
# the forward step should reverse to a backwards step if it runs into a
|
||||
# bound
|
||||
# This is kind of tested in TestAdjustSchemeToBounds, but only for a lower level
|
||||
# function.
|
||||
grad = approx_derivative(
|
||||
f, [-1, -1], method='2-point', abs_step=1e-8,
|
||||
bounds=(-np.inf, -1)
|
||||
)
|
||||
assert_allclose(grad, [1.0, -1.0])
|
||||
|
||||
grad = approx_derivative(
|
||||
f, [-1, -1], method='2-point', abs_step=-1e-8, bounds=(-1, np.inf)
|
||||
)
|
||||
assert_allclose(grad, [-1.0, 1.0])
|
||||
|
||||
|
||||
def test__compute_absolute_step():
|
||||
# tests calculation of absolute step from rel_step
|
||||
methods = ['2-point', '3-point', 'cs']
|
||||
|
||||
x0 = np.array([1e-5, 0, 1, 1e5])
|
||||
|
||||
EPS = np.finfo(np.float64).eps
|
||||
relative_step = {
|
||||
"2-point": EPS**0.5,
|
||||
"3-point": EPS**(1/3),
|
||||
"cs": EPS**0.5
|
||||
}
|
||||
f0 = np.array(1.0)
|
||||
|
||||
for method in methods:
|
||||
rel_step = relative_step[method]
|
||||
correct_step = np.array([rel_step,
|
||||
rel_step * 1.,
|
||||
rel_step * 1.,
|
||||
rel_step * np.abs(x0[3])])
|
||||
|
||||
abs_step = _compute_absolute_step(None, x0, f0, method)
|
||||
assert_allclose(abs_step, correct_step)
|
||||
|
||||
sign_x0 = (-x0 >= 0).astype(float) * 2 - 1
|
||||
abs_step = _compute_absolute_step(None, -x0, f0, method)
|
||||
assert_allclose(abs_step, sign_x0 * correct_step)
|
||||
|
||||
# if a relative step is provided it should be used
|
||||
rel_step = np.array([0.1, 1, 10, 100])
|
||||
correct_step = np.array([rel_step[0] * x0[0],
|
||||
relative_step['2-point'],
|
||||
rel_step[2] * 1.,
|
||||
rel_step[3] * np.abs(x0[3])])
|
||||
|
||||
abs_step = _compute_absolute_step(rel_step, x0, f0, '2-point')
|
||||
assert_allclose(abs_step, correct_step)
|
||||
|
||||
sign_x0 = (-x0 >= 0).astype(float) * 2 - 1
|
||||
abs_step = _compute_absolute_step(rel_step, -x0, f0, '2-point')
|
||||
assert_allclose(abs_step, sign_x0 * correct_step)
|
||||
255
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__remove_redundancy.py
vendored
Normal file
255
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__remove_redundancy.py
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
"""
|
||||
Unit test for Linear Programming via Simplex Algorithm.
|
||||
"""
|
||||
|
||||
# TODO: add tests for:
|
||||
# https://github.com/scipy/scipy/issues/5400
|
||||
# https://github.com/scipy/scipy/issues/6690
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import (
|
||||
assert_,
|
||||
assert_allclose,
|
||||
assert_equal)
|
||||
|
||||
from .test_linprog import magic_square
|
||||
from scipy.optimize._remove_redundancy import _remove_redundancy_svd
|
||||
from scipy.optimize._remove_redundancy import _remove_redundancy_pivot_dense
|
||||
from scipy.optimize._remove_redundancy import _remove_redundancy_pivot_sparse
|
||||
from scipy.optimize._remove_redundancy import _remove_redundancy_id
|
||||
|
||||
from scipy.sparse import csc_matrix
|
||||
|
||||
|
||||
def setup_module():
|
||||
np.random.seed(2017)
|
||||
|
||||
|
||||
def _assert_success(
|
||||
res,
|
||||
desired_fun=None,
|
||||
desired_x=None,
|
||||
rtol=1e-7,
|
||||
atol=1e-7):
|
||||
# res: linprog result object
|
||||
# desired_fun: desired objective function value or None
|
||||
# desired_x: desired solution or None
|
||||
assert_(res.success)
|
||||
assert_equal(res.status, 0)
|
||||
if desired_fun is not None:
|
||||
assert_allclose(
|
||||
res.fun,
|
||||
desired_fun,
|
||||
err_msg="converged to an unexpected objective value",
|
||||
rtol=rtol,
|
||||
atol=atol)
|
||||
if desired_x is not None:
|
||||
assert_allclose(
|
||||
res.x,
|
||||
desired_x,
|
||||
err_msg="converged to an unexpected solution",
|
||||
rtol=rtol,
|
||||
atol=atol)
|
||||
|
||||
|
||||
def redundancy_removed(A, B):
|
||||
"""Checks whether a matrix contains only independent rows of another"""
|
||||
for rowA in A:
|
||||
# `rowA in B` is not a reliable check
|
||||
for rowB in B:
|
||||
if np.all(rowA == rowB):
|
||||
break
|
||||
else:
|
||||
return False
|
||||
return A.shape[0] == np.linalg.matrix_rank(A) == np.linalg.matrix_rank(B)
|
||||
|
||||
|
||||
class RRCommonTests:
|
||||
def test_no_redundancy(self):
|
||||
m, n = 10, 10
|
||||
A0 = np.random.rand(m, n)
|
||||
b0 = np.random.rand(m)
|
||||
A1, b1, status, message = self.rr(A0, b0)
|
||||
assert_allclose(A0, A1)
|
||||
assert_allclose(b0, b1)
|
||||
assert_equal(status, 0)
|
||||
|
||||
def test_infeasible_zero_row(self):
|
||||
A = np.eye(3)
|
||||
A[1, :] = 0
|
||||
b = np.random.rand(3)
|
||||
A1, b1, status, message = self.rr(A, b)
|
||||
assert_equal(status, 2)
|
||||
|
||||
def test_remove_zero_row(self):
|
||||
A = np.eye(3)
|
||||
A[1, :] = 0
|
||||
b = np.random.rand(3)
|
||||
b[1] = 0
|
||||
A1, b1, status, message = self.rr(A, b)
|
||||
assert_equal(status, 0)
|
||||
assert_allclose(A1, A[[0, 2], :])
|
||||
assert_allclose(b1, b[[0, 2]])
|
||||
|
||||
def test_infeasible_m_gt_n(self):
|
||||
m, n = 20, 10
|
||||
A0 = np.random.rand(m, n)
|
||||
b0 = np.random.rand(m)
|
||||
A1, b1, status, message = self.rr(A0, b0)
|
||||
assert_equal(status, 2)
|
||||
|
||||
def test_infeasible_m_eq_n(self):
|
||||
m, n = 10, 10
|
||||
A0 = np.random.rand(m, n)
|
||||
b0 = np.random.rand(m)
|
||||
A0[-1, :] = 2 * A0[-2, :]
|
||||
A1, b1, status, message = self.rr(A0, b0)
|
||||
assert_equal(status, 2)
|
||||
|
||||
def test_infeasible_m_lt_n(self):
|
||||
m, n = 9, 10
|
||||
A0 = np.random.rand(m, n)
|
||||
b0 = np.random.rand(m)
|
||||
A0[-1, :] = np.arange(m - 1).dot(A0[:-1])
|
||||
A1, b1, status, message = self.rr(A0, b0)
|
||||
assert_equal(status, 2)
|
||||
|
||||
def test_m_gt_n(self):
|
||||
np.random.seed(2032)
|
||||
m, n = 20, 10
|
||||
A0 = np.random.rand(m, n)
|
||||
b0 = np.random.rand(m)
|
||||
x = np.linalg.solve(A0[:n, :], b0[:n])
|
||||
b0[n:] = A0[n:, :].dot(x)
|
||||
A1, b1, status, message = self.rr(A0, b0)
|
||||
assert_equal(status, 0)
|
||||
assert_equal(A1.shape[0], n)
|
||||
assert_equal(np.linalg.matrix_rank(A1), n)
|
||||
|
||||
def test_m_gt_n_rank_deficient(self):
|
||||
m, n = 20, 10
|
||||
A0 = np.zeros((m, n))
|
||||
A0[:, 0] = 1
|
||||
b0 = np.ones(m)
|
||||
A1, b1, status, message = self.rr(A0, b0)
|
||||
assert_equal(status, 0)
|
||||
assert_allclose(A1, A0[0:1, :])
|
||||
assert_allclose(b1, b0[0])
|
||||
|
||||
def test_m_lt_n_rank_deficient(self):
|
||||
m, n = 9, 10
|
||||
A0 = np.random.rand(m, n)
|
||||
b0 = np.random.rand(m)
|
||||
A0[-1, :] = np.arange(m - 1).dot(A0[:-1])
|
||||
b0[-1] = np.arange(m - 1).dot(b0[:-1])
|
||||
A1, b1, status, message = self.rr(A0, b0)
|
||||
assert_equal(status, 0)
|
||||
assert_equal(A1.shape[0], 8)
|
||||
assert_equal(np.linalg.matrix_rank(A1), 8)
|
||||
|
||||
def test_dense1(self):
|
||||
A = np.ones((6, 6))
|
||||
A[0, :3] = 0
|
||||
A[1, 3:] = 0
|
||||
A[3:, ::2] = -1
|
||||
A[3, :2] = 0
|
||||
A[4, 2:] = 0
|
||||
b = np.zeros(A.shape[0])
|
||||
|
||||
A1, b1, status, message = self.rr(A, b)
|
||||
assert_(redundancy_removed(A1, A))
|
||||
assert_equal(status, 0)
|
||||
|
||||
def test_dense2(self):
|
||||
A = np.eye(6)
|
||||
A[-2, -1] = 1
|
||||
A[-1, :] = 1
|
||||
b = np.zeros(A.shape[0])
|
||||
A1, b1, status, message = self.rr(A, b)
|
||||
assert_(redundancy_removed(A1, A))
|
||||
assert_equal(status, 0)
|
||||
|
||||
def test_dense3(self):
|
||||
A = np.eye(6)
|
||||
A[-2, -1] = 1
|
||||
A[-1, :] = 1
|
||||
b = np.random.rand(A.shape[0])
|
||||
b[-1] = np.sum(b[:-1])
|
||||
A1, b1, status, message = self.rr(A, b)
|
||||
assert_(redundancy_removed(A1, A))
|
||||
assert_equal(status, 0)
|
||||
|
||||
def test_m_gt_n_sparse(self):
|
||||
np.random.seed(2013)
|
||||
m, n = 20, 5
|
||||
p = 0.1
|
||||
A = np.random.rand(m, n)
|
||||
A[np.random.rand(m, n) > p] = 0
|
||||
rank = np.linalg.matrix_rank(A)
|
||||
b = np.zeros(A.shape[0])
|
||||
A1, b1, status, message = self.rr(A, b)
|
||||
assert_equal(status, 0)
|
||||
assert_equal(A1.shape[0], rank)
|
||||
assert_equal(np.linalg.matrix_rank(A1), rank)
|
||||
|
||||
def test_m_lt_n_sparse(self):
|
||||
np.random.seed(2017)
|
||||
m, n = 20, 50
|
||||
p = 0.05
|
||||
A = np.random.rand(m, n)
|
||||
A[np.random.rand(m, n) > p] = 0
|
||||
rank = np.linalg.matrix_rank(A)
|
||||
b = np.zeros(A.shape[0])
|
||||
A1, b1, status, message = self.rr(A, b)
|
||||
assert_equal(status, 0)
|
||||
assert_equal(A1.shape[0], rank)
|
||||
assert_equal(np.linalg.matrix_rank(A1), rank)
|
||||
|
||||
def test_m_eq_n_sparse(self):
|
||||
np.random.seed(2017)
|
||||
m, n = 100, 100
|
||||
p = 0.01
|
||||
A = np.random.rand(m, n)
|
||||
A[np.random.rand(m, n) > p] = 0
|
||||
rank = np.linalg.matrix_rank(A)
|
||||
b = np.zeros(A.shape[0])
|
||||
A1, b1, status, message = self.rr(A, b)
|
||||
assert_equal(status, 0)
|
||||
assert_equal(A1.shape[0], rank)
|
||||
assert_equal(np.linalg.matrix_rank(A1), rank)
|
||||
|
||||
def test_magic_square(self):
|
||||
A, b, c, numbers, _ = magic_square(3)
|
||||
A1, b1, status, message = self.rr(A, b)
|
||||
assert_equal(status, 0)
|
||||
assert_equal(A1.shape[0], 23)
|
||||
assert_equal(np.linalg.matrix_rank(A1), 23)
|
||||
|
||||
def test_magic_square2(self):
|
||||
A, b, c, numbers, _ = magic_square(4)
|
||||
A1, b1, status, message = self.rr(A, b)
|
||||
assert_equal(status, 0)
|
||||
assert_equal(A1.shape[0], 39)
|
||||
assert_equal(np.linalg.matrix_rank(A1), 39)
|
||||
|
||||
|
||||
class TestRRSVD(RRCommonTests):
|
||||
def rr(self, A, b):
|
||||
return _remove_redundancy_svd(A, b)
|
||||
|
||||
|
||||
class TestRRPivotDense(RRCommonTests):
|
||||
def rr(self, A, b):
|
||||
return _remove_redundancy_pivot_dense(A, b)
|
||||
|
||||
|
||||
class TestRRID(RRCommonTests):
|
||||
def rr(self, A, b):
|
||||
return _remove_redundancy_id(A, b)
|
||||
|
||||
|
||||
class TestRRPivotSparse(RRCommonTests):
|
||||
def rr(self, A, b):
|
||||
rr_res = _remove_redundancy_pivot_sparse(csc_matrix(A), b)
|
||||
A1, b1, status, message = rr_res
|
||||
return A1.toarray(), b1, status, message
|
||||
85
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__root.py
vendored
Normal file
85
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__root.py
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
Unit tests for optimization routines from _root.py.
|
||||
"""
|
||||
from numpy.testing import assert_
|
||||
from pytest import raises as assert_raises
|
||||
import numpy as np
|
||||
|
||||
from scipy.optimize import root
|
||||
|
||||
|
||||
class TestRoot:
|
||||
def test_tol_parameter(self):
|
||||
# Check that the minimize() tol= argument does something
|
||||
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'):
|
||||
# doesn't converge
|
||||
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 = "%s: %s vs. %s" % (method, func(sol1.x), 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):
|
||||
# github issue #3503
|
||||
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):
|
||||
# gh8320
|
||||
# check that decreasing the size of the returned array raises an error
|
||||
# and doesn't segfault
|
||||
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')
|
||||
812
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__shgo.py
vendored
Normal file
812
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__shgo.py
vendored
Normal file
@@ -0,0 +1,812 @@
|
||||
import logging
|
||||
import numpy
|
||||
from numpy.testing import assert_allclose
|
||||
import pytest
|
||||
from pytest import raises as assert_raises, warns
|
||||
from scipy.optimize import shgo, Bounds, minimize
|
||||
from scipy.optimize._shgo import SHGO
|
||||
|
||||
|
||||
class StructTestFunction:
|
||||
def __init__(self, bounds, expected_x, expected_fun=None,
|
||||
expected_xl=None, expected_funl=None):
|
||||
self.bounds = bounds
|
||||
self.expected_x = expected_x
|
||||
self.expected_fun = expected_fun
|
||||
self.expected_xl = expected_xl
|
||||
self.expected_funl = expected_funl
|
||||
|
||||
|
||||
def wrap_constraints(g):
|
||||
cons = []
|
||||
if g is not None:
|
||||
if (type(g) is not tuple) and (type(g) is not list):
|
||||
g = (g,)
|
||||
else:
|
||||
pass
|
||||
for g in g:
|
||||
cons.append({'type': 'ineq',
|
||||
'fun': g})
|
||||
cons = tuple(cons)
|
||||
else:
|
||||
cons = None
|
||||
return cons
|
||||
|
||||
|
||||
class StructTest1(StructTestFunction):
|
||||
def f(self, x):
|
||||
return x[0] ** 2 + x[1] ** 2
|
||||
|
||||
def g(x):
|
||||
return -(numpy.sum(x, axis=0) - 6.0)
|
||||
|
||||
cons = wrap_constraints(g)
|
||||
|
||||
|
||||
test1_1 = StructTest1(bounds=[(-1, 6), (-1, 6)],
|
||||
expected_x=[0, 0])
|
||||
test1_2 = StructTest1(bounds=[(0, 1), (0, 1)],
|
||||
expected_x=[0, 0])
|
||||
test1_3 = StructTest1(bounds=[(None, None), (None, None)],
|
||||
expected_x=[0, 0])
|
||||
|
||||
|
||||
class StructTest2(StructTestFunction):
|
||||
"""
|
||||
Scalar function with several minima to test all minimizer retrievals
|
||||
"""
|
||||
|
||||
def f(self, x):
|
||||
return (x - 30) * numpy.sin(x)
|
||||
|
||||
def g(x):
|
||||
return 58 - numpy.sum(x, axis=0)
|
||||
|
||||
cons = wrap_constraints(g)
|
||||
|
||||
|
||||
test2_1 = StructTest2(bounds=[(0, 60)],
|
||||
expected_x=[1.53567906],
|
||||
expected_fun=-28.44677132,
|
||||
# Important: test that funl return is in the correct order
|
||||
expected_xl=numpy.array([[1.53567906],
|
||||
[55.01782167],
|
||||
[7.80894889],
|
||||
[48.74797493],
|
||||
[14.07445705],
|
||||
[42.4913859],
|
||||
[20.31743841],
|
||||
[36.28607535],
|
||||
[26.43039605],
|
||||
[30.76371366]]),
|
||||
|
||||
expected_funl=numpy.array([-28.44677132, -24.99785984,
|
||||
-22.16855376, -18.72136195,
|
||||
-15.89423937, -12.45154942,
|
||||
-9.63133158, -6.20801301,
|
||||
-3.43727232, -0.46353338])
|
||||
)
|
||||
|
||||
test2_2 = StructTest2(bounds=[(0, 4.5)],
|
||||
expected_x=[1.53567906],
|
||||
expected_fun=[-28.44677132],
|
||||
expected_xl=numpy.array([[1.53567906]]),
|
||||
expected_funl=numpy.array([-28.44677132])
|
||||
)
|
||||
|
||||
|
||||
class StructTest3(StructTestFunction):
|
||||
"""
|
||||
Hock and Schittkowski 18 problem (HS18). Hoch and Schittkowski (1981)
|
||||
http://www.ai7.uni-bayreuth.de/test_problem_coll.pdf
|
||||
Minimize: f = 0.01 * (x_1)**2 + (x_2)**2
|
||||
|
||||
Subject to: x_1 * x_2 - 25.0 >= 0,
|
||||
(x_1)**2 + (x_2)**2 - 25.0 >= 0,
|
||||
2 <= x_1 <= 50,
|
||||
0 <= x_2 <= 50.
|
||||
|
||||
Approx. Answer:
|
||||
f([(250)**0.5 , (2.5)**0.5]) = 5.0
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def f(self, x):
|
||||
return 0.01 * (x[0]) ** 2 + (x[1]) ** 2
|
||||
|
||||
def g1(x):
|
||||
return x[0] * x[1] - 25.0
|
||||
|
||||
def g2(x):
|
||||
return x[0] ** 2 + x[1] ** 2 - 25.0
|
||||
|
||||
g = (g1, g2)
|
||||
|
||||
cons = wrap_constraints(g)
|
||||
|
||||
|
||||
test3_1 = StructTest3(bounds=[(2, 50), (0, 50)],
|
||||
expected_x=[250 ** 0.5, 2.5 ** 0.5],
|
||||
expected_fun=5.0
|
||||
)
|
||||
|
||||
|
||||
class StructTest4(StructTestFunction):
|
||||
"""
|
||||
Hock and Schittkowski 11 problem (HS11). Hoch and Schittkowski (1981)
|
||||
|
||||
NOTE: Did not find in original reference to HS collection, refer to
|
||||
Henderson (2015) problem 7 instead. 02.03.2016
|
||||
"""
|
||||
|
||||
def f(self, x):
|
||||
return ((x[0] - 10) ** 2 + 5 * (x[1] - 12) ** 2 + x[2] ** 4
|
||||
+ 3 * (x[3] - 11) ** 2 + 10 * x[4] ** 6 + 7 * x[5] ** 2 + x[
|
||||
6] ** 4
|
||||
- 4 * x[5] * x[6] - 10 * x[5] - 8 * x[6]
|
||||
)
|
||||
|
||||
def g1(x):
|
||||
return -(2 * x[0] ** 2 + 3 * x[1] ** 4 + x[2] + 4 * x[3] ** 2
|
||||
+ 5 * x[4] - 127)
|
||||
|
||||
def g2(x):
|
||||
return -(7 * x[0] + 3 * x[1] + 10 * x[2] ** 2 + x[3] - x[4] - 282.0)
|
||||
|
||||
def g3(x):
|
||||
return -(23 * x[0] + x[1] ** 2 + 6 * x[5] ** 2 - 8 * x[6] - 196)
|
||||
|
||||
def g4(x):
|
||||
return -(4 * x[0] ** 2 + x[1] ** 2 - 3 * x[0] * x[1] + 2 * x[2] ** 2
|
||||
+ 5 * x[5] - 11 * x[6])
|
||||
|
||||
g = (g1, g2, g3, g4)
|
||||
|
||||
cons = wrap_constraints(g)
|
||||
|
||||
|
||||
test4_1 = StructTest4(bounds=[(-10, 10), ] * 7,
|
||||
expected_x=[2.330499, 1.951372, -0.4775414,
|
||||
4.365726, -0.6244870, 1.038131, 1.594227],
|
||||
expected_fun=680.6300573
|
||||
)
|
||||
|
||||
|
||||
class StructTest5(StructTestFunction):
|
||||
def f(self, x):
|
||||
return (-(x[1] + 47.0)
|
||||
* numpy.sin(numpy.sqrt(abs(x[0] / 2.0 + (x[1] + 47.0))))
|
||||
- x[0] * numpy.sin(numpy.sqrt(abs(x[0] - (x[1] + 47.0))))
|
||||
)
|
||||
|
||||
g = None
|
||||
cons = wrap_constraints(g)
|
||||
|
||||
|
||||
test5_1 = StructTest5(bounds=[(-512, 512), (-512, 512)],
|
||||
expected_fun=[-959.64066272085051],
|
||||
expected_x=[512., 404.23180542])
|
||||
|
||||
|
||||
class StructTestLJ(StructTestFunction):
|
||||
"""
|
||||
LennardJones objective function. Used to test symmetry constraints settings.
|
||||
"""
|
||||
|
||||
def f(self, x, *args):
|
||||
self.N = args[0]
|
||||
k = int(self.N / 3)
|
||||
s = 0.0
|
||||
|
||||
for i in range(k - 1):
|
||||
for j in range(i + 1, k):
|
||||
a = 3 * i
|
||||
b = 3 * j
|
||||
xd = x[a] - x[b]
|
||||
yd = x[a + 1] - x[b + 1]
|
||||
zd = x[a + 2] - x[b + 2]
|
||||
ed = xd * xd + yd * yd + zd * zd
|
||||
ud = ed * ed * ed
|
||||
if ed > 0.0:
|
||||
s += (1.0 / ud - 2.0) / ud
|
||||
|
||||
return s
|
||||
|
||||
g = None
|
||||
cons = wrap_constraints(g)
|
||||
|
||||
|
||||
N = 6
|
||||
boundsLJ = list(zip([-4.0] * 6, [4.0] * 6))
|
||||
|
||||
testLJ = StructTestLJ(bounds=boundsLJ,
|
||||
expected_fun=[-1.0],
|
||||
expected_x=[-2.71247337e-08,
|
||||
-2.71247337e-08,
|
||||
-2.50000222e+00,
|
||||
-2.71247337e-08,
|
||||
-2.71247337e-08,
|
||||
-1.50000222e+00]
|
||||
)
|
||||
|
||||
|
||||
class StructTestTable(StructTestFunction):
|
||||
def f(self, x):
|
||||
if x[0] == 3.0 and x[1] == 3.0:
|
||||
return 50
|
||||
else:
|
||||
return 100
|
||||
|
||||
g = None
|
||||
cons = wrap_constraints(g)
|
||||
|
||||
|
||||
test_table = StructTestTable(bounds=[(-10, 10), (-10, 10)],
|
||||
expected_fun=[50],
|
||||
expected_x=[3.0, 3.0])
|
||||
|
||||
|
||||
class StructTestInfeasible(StructTestFunction):
|
||||
"""
|
||||
Test function with no feasible domain.
|
||||
"""
|
||||
|
||||
def f(self, x, *args):
|
||||
return x[0] ** 2 + x[1] ** 2
|
||||
|
||||
def g1(x):
|
||||
return x[0] + x[1] - 1
|
||||
|
||||
def g2(x):
|
||||
return -(x[0] + x[1] - 1)
|
||||
|
||||
def g3(x):
|
||||
return -x[0] + x[1] - 1
|
||||
|
||||
def g4(x):
|
||||
return -(-x[0] + x[1] - 1)
|
||||
|
||||
g = (g1, g2, g3, g4)
|
||||
cons = wrap_constraints(g)
|
||||
|
||||
|
||||
test_infeasible = StructTestInfeasible(bounds=[(2, 50), (-1, 1)],
|
||||
expected_fun=None,
|
||||
expected_x=None
|
||||
)
|
||||
|
||||
|
||||
def run_test(test, args=(), test_atol=1e-5, n=128, iters=None,
|
||||
callback=None, minimizer_kwargs=None, options=None,
|
||||
sampling_method='sobol'):
|
||||
res = shgo(test.f, test.bounds, args=args, constraints=test.cons,
|
||||
n=n, iters=iters, callback=callback,
|
||||
minimizer_kwargs=minimizer_kwargs, options=options,
|
||||
sampling_method=sampling_method)
|
||||
|
||||
logging.info(res)
|
||||
|
||||
if test.expected_x is not None:
|
||||
numpy.testing.assert_allclose(res.x, test.expected_x,
|
||||
rtol=test_atol,
|
||||
atol=test_atol)
|
||||
|
||||
# (Optional tests)
|
||||
if test.expected_fun is not None:
|
||||
numpy.testing.assert_allclose(res.fun,
|
||||
test.expected_fun,
|
||||
atol=test_atol)
|
||||
|
||||
if test.expected_xl is not None:
|
||||
numpy.testing.assert_allclose(res.xl,
|
||||
test.expected_xl,
|
||||
atol=test_atol)
|
||||
|
||||
if test.expected_funl is not None:
|
||||
numpy.testing.assert_allclose(res.funl,
|
||||
test.expected_funl,
|
||||
atol=test_atol)
|
||||
return
|
||||
|
||||
|
||||
# Base test functions:
|
||||
class TestShgoSobolTestFunctions:
|
||||
"""
|
||||
Global optimization tests with Sobol sampling:
|
||||
"""
|
||||
|
||||
# Sobol algorithm
|
||||
def test_f1_1_sobol(self):
|
||||
"""Multivariate test function 1:
|
||||
x[0]**2 + x[1]**2 with bounds=[(-1, 6), (-1, 6)]"""
|
||||
run_test(test1_1)
|
||||
|
||||
def test_f1_2_sobol(self):
|
||||
"""Multivariate test function 1:
|
||||
x[0]**2 + x[1]**2 with bounds=[(0, 1), (0, 1)]"""
|
||||
run_test(test1_2)
|
||||
|
||||
def test_f1_3_sobol(self):
|
||||
"""Multivariate test function 1:
|
||||
x[0]**2 + x[1]**2 with bounds=[(None, None),(None, None)]"""
|
||||
run_test(test1_3)
|
||||
|
||||
def test_f2_1_sobol(self):
|
||||
"""Univariate test function on
|
||||
f(x) = (x - 30) * sin(x) with bounds=[(0, 60)]"""
|
||||
run_test(test2_1)
|
||||
|
||||
def test_f2_2_sobol(self):
|
||||
"""Univariate test function on
|
||||
f(x) = (x - 30) * sin(x) bounds=[(0, 4.5)]"""
|
||||
run_test(test2_2)
|
||||
|
||||
def test_f3_sobol(self):
|
||||
"""NLP: Hock and Schittkowski problem 18"""
|
||||
run_test(test3_1)
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_f4_sobol(self):
|
||||
"""NLP: (High-dimensional) Hock and Schittkowski 11 problem (HS11)"""
|
||||
# run_test(test4_1, n=500)
|
||||
# run_test(test4_1, n=800)
|
||||
options = {'infty_constraints': False}
|
||||
run_test(test4_1, n=2048, options=options)
|
||||
|
||||
def test_f5_1_sobol(self):
|
||||
"""NLP: Eggholder, multimodal"""
|
||||
run_test(test5_1, n=64)
|
||||
|
||||
def test_f5_2_sobol(self):
|
||||
"""NLP: Eggholder, multimodal"""
|
||||
# run_test(test5_1, n=60, iters=5)
|
||||
run_test(test5_1, n=128, iters=5)
|
||||
|
||||
# def test_t911(self):
|
||||
# """1-D tabletop function"""
|
||||
# run_test(test11_1)
|
||||
|
||||
|
||||
class TestShgoSimplicialTestFunctions:
|
||||
"""
|
||||
Global optimization tests with Simplicial sampling:
|
||||
"""
|
||||
|
||||
def test_f1_1_simplicial(self):
|
||||
"""Multivariate test function 1:
|
||||
x[0]**2 + x[1]**2 with bounds=[(-1, 6), (-1, 6)]"""
|
||||
run_test(test1_1, n=1, sampling_method='simplicial')
|
||||
|
||||
def test_f1_2_simplicial(self):
|
||||
"""Multivariate test function 1:
|
||||
x[0]**2 + x[1]**2 with bounds=[(0, 1), (0, 1)]"""
|
||||
run_test(test1_2, n=1, sampling_method='simplicial')
|
||||
|
||||
def test_f1_3_simplicial(self):
|
||||
"""Multivariate test function 1: x[0]**2 + x[1]**2
|
||||
with bounds=[(None, None),(None, None)]"""
|
||||
run_test(test1_3, n=1, sampling_method='simplicial')
|
||||
|
||||
def test_f2_1_simplicial(self):
|
||||
"""Univariate test function on
|
||||
f(x) = (x - 30) * sin(x) with bounds=[(0, 60)]"""
|
||||
options = {'minimize_every_iter': False}
|
||||
run_test(test2_1, iters=7, options=options,
|
||||
sampling_method='simplicial')
|
||||
|
||||
def test_f2_2_simplicial(self):
|
||||
"""Univariate test function on
|
||||
f(x) = (x - 30) * sin(x) bounds=[(0, 4.5)]"""
|
||||
run_test(test2_2, n=1, sampling_method='simplicial')
|
||||
|
||||
def test_f3_simplicial(self):
|
||||
"""NLP: Hock and Schittkowski problem 18"""
|
||||
run_test(test3_1, n=1, sampling_method='simplicial')
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_f4_simplicial(self):
|
||||
"""NLP: (High-dimensional) Hock and Schittkowski 11 problem (HS11)"""
|
||||
run_test(test4_1, n=1, sampling_method='simplicial')
|
||||
|
||||
def test_lj_symmetry(self):
|
||||
"""LJ: Symmetry-constrained test function"""
|
||||
options = {'symmetry': True,
|
||||
'disp': True}
|
||||
args = (6,) # Number of atoms
|
||||
run_test(testLJ, args=args, n=None,
|
||||
options=options, iters=4,
|
||||
sampling_method='simplicial')
|
||||
|
||||
|
||||
# Argument test functions
|
||||
class TestShgoArguments:
|
||||
def test_1_1_simpl_iter(self):
|
||||
"""Iterative simplicial sampling on TestFunction 1 (multivariate)"""
|
||||
run_test(test1_2, n=None, iters=2, sampling_method='simplicial')
|
||||
|
||||
def test_1_2_simpl_iter(self):
|
||||
"""Iterative simplicial on TestFunction 2 (univariate)"""
|
||||
options = {'minimize_every_iter': False}
|
||||
run_test(test2_1, n=None, iters=7, options=options,
|
||||
sampling_method='simplicial')
|
||||
|
||||
def test_2_1_sobol_iter(self):
|
||||
"""Iterative Sobol sampling on TestFunction 1 (multivariate)"""
|
||||
run_test(test1_2, n=None, iters=1, sampling_method='sobol')
|
||||
|
||||
def test_2_2_sobol_iter(self):
|
||||
"""Iterative Sobol sampling on TestFunction 2 (univariate)"""
|
||||
res = shgo(test2_1.f, test2_1.bounds, constraints=test2_1.cons,
|
||||
n=None, iters=1, sampling_method='sobol')
|
||||
|
||||
numpy.testing.assert_allclose(res.x, test2_1.expected_x, rtol=1e-5,
|
||||
atol=1e-5)
|
||||
numpy.testing.assert_allclose(res.fun, test2_1.expected_fun, atol=1e-5)
|
||||
|
||||
def test_3_1_disp_simplicial(self):
|
||||
"""Iterative sampling on TestFunction 1 and 2 (multi- and univariate)"""
|
||||
|
||||
def callback_func(x):
|
||||
print("Local minimization callback test")
|
||||
|
||||
for test in [test1_1, test2_1]:
|
||||
shgo(test.f, test.bounds, iters=1,
|
||||
sampling_method='simplicial',
|
||||
callback=callback_func, options={'disp': True})
|
||||
shgo(test.f, test.bounds, n=1, sampling_method='simplicial',
|
||||
callback=callback_func, options={'disp': True})
|
||||
|
||||
def test_3_2_disp_sobol(self):
|
||||
"""Iterative sampling on TestFunction 1 and 2 (multi- and univariate)"""
|
||||
|
||||
def callback_func(x):
|
||||
print("Local minimization callback test")
|
||||
|
||||
for test in [test1_1, test2_1]:
|
||||
shgo(test.f, test.bounds, iters=1, sampling_method='sobol',
|
||||
callback=callback_func, options={'disp': True})
|
||||
|
||||
shgo(test.f, test.bounds, n=1, sampling_method='simplicial',
|
||||
callback=callback_func, options={'disp': True})
|
||||
|
||||
def test_args_gh14589(self):
|
||||
# Using `args` used to cause `shgo` to fail; see #14589, #15986, #16506
|
||||
res = shgo(func=lambda x, y, z: x*z + y, bounds=[(0, 3)], args=(1, 2))
|
||||
ref = shgo(func=lambda x: 2*x + 1, bounds=[(0, 3)])
|
||||
assert_allclose(res.fun, ref.fun)
|
||||
assert_allclose(res.x, ref.x)
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_4_1_known_f_min(self):
|
||||
"""Test known function minima stopping criteria"""
|
||||
# Specify known function value
|
||||
options = {'f_min': test4_1.expected_fun,
|
||||
'f_tol': 1e-6,
|
||||
'minimize_every_iter': True}
|
||||
# TODO: Make default n higher for faster tests
|
||||
run_test(test4_1, n=None, test_atol=1e-5, options=options,
|
||||
sampling_method='simplicial')
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_4_2_known_f_min(self):
|
||||
"""Test Global mode limiting local evalutions"""
|
||||
options = { # Specify known function value
|
||||
'f_min': test4_1.expected_fun,
|
||||
'f_tol': 1e-6,
|
||||
# Specify number of local iterations to perform
|
||||
'minimize_every_iter': True,
|
||||
'local_iter': 1}
|
||||
|
||||
run_test(test4_1, n=None, test_atol=1e-5, options=options,
|
||||
sampling_method='simplicial')
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_4_3_known_f_min(self):
|
||||
"""Test Global mode limiting local evalutions"""
|
||||
options = { # Specify known function value
|
||||
'f_min': test4_1.expected_fun,
|
||||
'f_tol': 1e-6,
|
||||
# Specify number of local iterations to perform+
|
||||
'minimize_every_iter': True,
|
||||
'local_iter': 1,
|
||||
'infty_constraints': False}
|
||||
|
||||
run_test(test4_1, n=1024, test_atol=1e-5, options=options,
|
||||
sampling_method='sobol')
|
||||
|
||||
def test_4_4_known_f_min(self):
|
||||
"""Test Global mode limiting local evalutions for 1-D functions"""
|
||||
options = { # Specify known function value
|
||||
'f_min': test2_1.expected_fun,
|
||||
'f_tol': 1e-6,
|
||||
# Specify number of local iterations to perform+
|
||||
'minimize_every_iter': True,
|
||||
'local_iter': 1,
|
||||
'infty_constraints': False}
|
||||
|
||||
res = shgo(test2_1.f, test2_1.bounds, constraints=test2_1.cons,
|
||||
n=None, iters=None, options=options,
|
||||
sampling_method='sobol')
|
||||
numpy.testing.assert_allclose(res.x, test2_1.expected_x, rtol=1e-5,
|
||||
atol=1e-5)
|
||||
|
||||
def test_5_1_simplicial_argless(self):
|
||||
"""Test Default simplicial sampling settings on TestFunction 1"""
|
||||
res = shgo(test1_1.f, test1_1.bounds, constraints=test1_1.cons)
|
||||
numpy.testing.assert_allclose(res.x, test1_1.expected_x, rtol=1e-5,
|
||||
atol=1e-5)
|
||||
|
||||
def test_5_2_sobol_argless(self):
|
||||
"""Test Default sobol sampling settings on TestFunction 1"""
|
||||
res = shgo(test1_1.f, test1_1.bounds, constraints=test1_1.cons,
|
||||
sampling_method='sobol')
|
||||
numpy.testing.assert_allclose(res.x, test1_1.expected_x, rtol=1e-5,
|
||||
atol=1e-5)
|
||||
|
||||
def test_6_1_simplicial_max_iter(self):
|
||||
"""Test that maximum iteration option works on TestFunction 3"""
|
||||
options = {'max_iter': 2}
|
||||
res = shgo(test3_1.f, test3_1.bounds, constraints=test3_1.cons,
|
||||
options=options, sampling_method='simplicial')
|
||||
numpy.testing.assert_allclose(res.x, test3_1.expected_x, rtol=1e-5,
|
||||
atol=1e-5)
|
||||
numpy.testing.assert_allclose(res.fun, test3_1.expected_fun, atol=1e-5)
|
||||
|
||||
def test_6_2_simplicial_min_iter(self):
|
||||
"""Test that maximum iteration option works on TestFunction 3"""
|
||||
options = {'min_iter': 2}
|
||||
res = shgo(test3_1.f, test3_1.bounds, constraints=test3_1.cons,
|
||||
options=options, sampling_method='simplicial')
|
||||
numpy.testing.assert_allclose(res.x, test3_1.expected_x, rtol=1e-5,
|
||||
atol=1e-5)
|
||||
numpy.testing.assert_allclose(res.fun, test3_1.expected_fun, atol=1e-5)
|
||||
|
||||
def test_7_1_minkwargs(self):
|
||||
"""Test the minimizer_kwargs arguments for solvers with constraints"""
|
||||
# Test solvers
|
||||
for solver in ['COBYLA', 'SLSQP']:
|
||||
# Note that passing global constraints to SLSQP is tested in other
|
||||
# unittests which run test4_1 normally
|
||||
minimizer_kwargs = {'method': solver,
|
||||
'constraints': test3_1.cons}
|
||||
print("Solver = {}".format(solver))
|
||||
print("=" * 100)
|
||||
run_test(test3_1, n=128, test_atol=1e-3,
|
||||
minimizer_kwargs=minimizer_kwargs, sampling_method='sobol')
|
||||
|
||||
def test_7_2_minkwargs(self):
|
||||
"""Test the minimizer_kwargs default inits"""
|
||||
minimizer_kwargs = {'ftol': 1e-5}
|
||||
options = {'disp': True} # For coverage purposes
|
||||
SHGO(test3_1.f, test3_1.bounds, constraints=test3_1.cons[0],
|
||||
minimizer_kwargs=minimizer_kwargs, options=options)
|
||||
|
||||
def test_7_3_minkwargs(self):
|
||||
"""Test minimizer_kwargs arguments for solvers without constraints"""
|
||||
for solver in ['Nelder-Mead', 'Powell', 'CG', 'BFGS', 'Newton-CG',
|
||||
'L-BFGS-B', 'TNC', 'dogleg', 'trust-ncg', 'trust-exact',
|
||||
'trust-krylov']:
|
||||
def jac(x):
|
||||
return numpy.array([2 * x[0], 2 * x[1]]).T
|
||||
|
||||
def hess(x):
|
||||
return numpy.array([[2, 0], [0, 2]])
|
||||
|
||||
minimizer_kwargs = {'method': solver,
|
||||
'jac': jac,
|
||||
'hess': hess}
|
||||
logging.info("Solver = {}".format(solver))
|
||||
logging.info("=" * 100)
|
||||
run_test(test1_1, n=128, test_atol=1e-3,
|
||||
minimizer_kwargs=minimizer_kwargs, sampling_method='sobol')
|
||||
|
||||
def test_8_homology_group_diff(self):
|
||||
options = {'minhgrd': 1,
|
||||
'minimize_every_iter': True}
|
||||
|
||||
run_test(test1_1, n=None, iters=None, options=options,
|
||||
sampling_method='simplicial')
|
||||
|
||||
def test_9_cons_g(self):
|
||||
"""Test single function constraint passing"""
|
||||
SHGO(test3_1.f, test3_1.bounds, constraints=test3_1.cons[0])
|
||||
|
||||
def test_10_finite_time(self):
|
||||
"""Test single function constraint passing"""
|
||||
options = {'maxtime': 1e-15}
|
||||
shgo(test1_1.f, test1_1.bounds, n=1, iters=None,
|
||||
options=options, sampling_method='sobol')
|
||||
|
||||
def test_11_f_min_time(self):
|
||||
"""Test to cover the case where f_lowest == 0"""
|
||||
options = {'maxtime': 1e-15,
|
||||
'f_min': 0.0}
|
||||
shgo(test1_2.f, test1_2.bounds, n=1, iters=None,
|
||||
options=options, sampling_method='sobol')
|
||||
|
||||
def test_12_sobol_inf_cons(self):
|
||||
"""Test to cover the case where f_lowest == 0"""
|
||||
options = {'maxtime': 1e-15,
|
||||
'f_min': 0.0}
|
||||
shgo(test1_2.f, test1_2.bounds, n=1, iters=None,
|
||||
options=options, sampling_method='sobol')
|
||||
|
||||
def test_14_local_iter(self):
|
||||
"""Test limited local iterations for a pseudo-global mode"""
|
||||
options = {'local_iter': 4}
|
||||
run_test(test5_1, n=64, options=options)
|
||||
|
||||
def test_15_min_every_iter(self):
|
||||
"""Test minimize every iter options and cover function cache"""
|
||||
options = {'minimize_every_iter': True}
|
||||
run_test(test1_1, n=1, iters=7, options=options,
|
||||
sampling_method='sobol')
|
||||
|
||||
def test_16_disp_bounds_minimizer(self):
|
||||
"""Test disp=True with minimizers that do not support bounds """
|
||||
options = {'disp': True}
|
||||
minimizer_kwargs = {'method': 'nelder-mead'}
|
||||
run_test(test1_2, sampling_method='simplicial',
|
||||
options=options, minimizer_kwargs=minimizer_kwargs)
|
||||
|
||||
def test_17_custom_sampling(self):
|
||||
"""Test the functionality to add custom sampling methods to shgo"""
|
||||
def sample(n, d):
|
||||
return numpy.random.uniform(size=(n,d))
|
||||
|
||||
run_test(test1_1, n=30, sampling_method=sample)
|
||||
|
||||
def test_18_bounds_class(self):
|
||||
# test that new and old bounds yield same result
|
||||
def f(x):
|
||||
return numpy.square(x).sum()
|
||||
|
||||
lb = [-6., 1., -5.]
|
||||
ub = [-1., 3., 5.]
|
||||
bounds_old = list(zip(lb, ub))
|
||||
bounds_new = Bounds(lb, ub)
|
||||
|
||||
res_old_bounds = shgo(f, bounds_old)
|
||||
res_new_bounds = shgo(f, bounds_new)
|
||||
|
||||
assert res_new_bounds.nfev == res_old_bounds.nfev
|
||||
assert res_new_bounds.message == res_old_bounds.message
|
||||
assert res_new_bounds.success == res_old_bounds.success
|
||||
x_opt = numpy.array([-1., 1., 0.])
|
||||
numpy.testing.assert_allclose(res_new_bounds.x, x_opt)
|
||||
numpy.testing.assert_allclose(res_new_bounds.x,
|
||||
res_old_bounds.x)
|
||||
|
||||
|
||||
# Failure test functions
|
||||
class TestShgoFailures:
|
||||
def test_1_maxiter(self):
|
||||
"""Test failure on insufficient iterations"""
|
||||
options = {'maxiter': 2}
|
||||
res = shgo(test4_1.f, test4_1.bounds, n=4, iters=None,
|
||||
options=options, sampling_method='sobol')
|
||||
|
||||
numpy.testing.assert_equal(False, res.success)
|
||||
numpy.testing.assert_equal(4, res.nfev)
|
||||
|
||||
def test_2_sampling(self):
|
||||
"""Rejection of unknown sampling method"""
|
||||
assert_raises(ValueError, shgo, test1_1.f, test1_1.bounds,
|
||||
sampling_method='not_Sobol')
|
||||
|
||||
def test_3_1_no_min_pool_sobol(self):
|
||||
"""Check that the routine stops when no minimiser is found
|
||||
after maximum specified function evaluations"""
|
||||
options = {'maxfev': 10,
|
||||
'disp': True}
|
||||
res = shgo(test_table.f, test_table.bounds, n=4, options=options,
|
||||
sampling_method='sobol')
|
||||
numpy.testing.assert_equal(False, res.success)
|
||||
|
||||
numpy.testing.assert_equal(16, res.nfev)
|
||||
|
||||
def test_3_2_no_min_pool_simplicial(self):
|
||||
"""Check that the routine stops when no minimiser is found
|
||||
after maximum specified sampling evaluations"""
|
||||
options = {'maxev': 10,
|
||||
'disp': True}
|
||||
res = shgo(test_table.f, test_table.bounds, n=3, options=options,
|
||||
sampling_method='simplicial')
|
||||
numpy.testing.assert_equal(False, res.success)
|
||||
|
||||
def test_4_1_bound_err(self):
|
||||
"""Specified bounds ub > lb"""
|
||||
bounds = [(6, 3), (3, 5)]
|
||||
assert_raises(ValueError, shgo, test1_1.f, bounds)
|
||||
|
||||
def test_4_2_bound_err(self):
|
||||
"""Specified bounds are of the form (lb, ub)"""
|
||||
bounds = [(3, 5, 5), (3, 5)]
|
||||
assert_raises(ValueError, shgo, test1_1.f, bounds)
|
||||
|
||||
def test_5_1_1_infeasible_sobol(self):
|
||||
"""Ensures the algorithm terminates on infeasible problems
|
||||
after maxev is exceeded. Use infty constraints option"""
|
||||
options = {'maxev': 64,
|
||||
'disp': True}
|
||||
|
||||
res = shgo(test_infeasible.f, test_infeasible.bounds,
|
||||
constraints=test_infeasible.cons, n=64, options=options,
|
||||
sampling_method='sobol')
|
||||
|
||||
numpy.testing.assert_equal(False, res.success)
|
||||
|
||||
def test_5_1_2_infeasible_sobol(self):
|
||||
"""Ensures the algorithm terminates on infeasible problems
|
||||
after maxev is exceeded. Do not use infty constraints option"""
|
||||
options = {'maxev': 64,
|
||||
'disp': True,
|
||||
'infty_constraints': False}
|
||||
|
||||
res = shgo(test_infeasible.f, test_infeasible.bounds,
|
||||
constraints=test_infeasible.cons, n=64, options=options,
|
||||
sampling_method='sobol')
|
||||
|
||||
numpy.testing.assert_equal(False, res.success)
|
||||
|
||||
def test_5_2_infeasible_simplicial(self):
|
||||
"""Ensures the algorithm terminates on infeasible problems
|
||||
after maxev is exceeded."""
|
||||
options = {'maxev': 1000,
|
||||
'disp': False}
|
||||
|
||||
res = shgo(test_infeasible.f, test_infeasible.bounds,
|
||||
constraints=test_infeasible.cons, n=100, options=options,
|
||||
sampling_method='simplicial')
|
||||
|
||||
numpy.testing.assert_equal(False, res.success)
|
||||
|
||||
def test_6_1_lower_known_f_min(self):
|
||||
"""Test Global mode limiting local evalutions with f* too high"""
|
||||
options = { # Specify known function value
|
||||
'f_min': test2_1.expected_fun + 2.0,
|
||||
'f_tol': 1e-6,
|
||||
# Specify number of local iterations to perform+
|
||||
'minimize_every_iter': True,
|
||||
'local_iter': 1,
|
||||
'infty_constraints': False}
|
||||
args = (test2_1.f, test2_1.bounds)
|
||||
kwargs = {'constraints': test2_1.cons,
|
||||
'n': None,
|
||||
'iters': None,
|
||||
'options': options,
|
||||
'sampling_method': 'sobol'
|
||||
}
|
||||
warns(UserWarning, shgo, *args, **kwargs)
|
||||
|
||||
@pytest.mark.parametrize('derivative', ['jac', 'hess', 'hessp'])
|
||||
def test_21_2_derivative_options(self, derivative):
|
||||
"""shgo used to raise an error when passing `options` with 'jac'
|
||||
# see gh-12829. check that this is resolved
|
||||
"""
|
||||
def objective(x):
|
||||
return 3 * x[0] * x[0] + 2 * x[0] + 5
|
||||
|
||||
def gradient(x):
|
||||
return 6 * x[0] + 2
|
||||
|
||||
def hess(x):
|
||||
return 6
|
||||
|
||||
def hessp(x, p):
|
||||
return 6 * p
|
||||
|
||||
derivative_funcs = {'jac': gradient, 'hess': hess, 'hessp': hessp}
|
||||
options = {derivative: derivative_funcs[derivative]}
|
||||
minimizer_kwargs = {'method': 'trust-constr'}
|
||||
|
||||
bounds = [(-100, 100)]
|
||||
res = shgo(objective, bounds, minimizer_kwargs=minimizer_kwargs,
|
||||
options=options)
|
||||
ref = minimize(objective, x0=[0], bounds=bounds, **minimizer_kwargs,
|
||||
**options)
|
||||
|
||||
assert res.success
|
||||
numpy.testing.assert_allclose(res.fun, ref.fun)
|
||||
numpy.testing.assert_allclose(res.x, ref.x)
|
||||
208
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__spectral.py
vendored
Normal file
208
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test__spectral.py
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
import itertools
|
||||
|
||||
import numpy as np
|
||||
from numpy import exp
|
||||
from numpy.testing import assert_, assert_equal
|
||||
|
||||
from scipy.optimize import root
|
||||
|
||||
|
||||
def test_performance():
|
||||
# Compare performance results to those listed in
|
||||
# [Cheng & Li, IMA J. Num. An. 29, 814 (2008)]
|
||||
# and
|
||||
# [W. La Cruz, J.M. Martinez, M. Raydan, Math. Comp. 75, 1429 (2006)].
|
||||
# and those produced by dfsane.f from M. Raydan's website.
|
||||
#
|
||||
# Where the results disagree, the largest limits are taken.
|
||||
|
||||
e_a = 1e-5
|
||||
e_r = 1e-4
|
||||
|
||||
table_1 = [
|
||||
dict(F=F_1, x0=x0_1, n=1000, nit=5, nfev=5),
|
||||
dict(F=F_1, x0=x0_1, n=10000, nit=2, nfev=2),
|
||||
dict(F=F_2, x0=x0_2, n=500, nit=11, nfev=11),
|
||||
dict(F=F_2, x0=x0_2, n=2000, nit=11, nfev=11),
|
||||
# dict(F=F_4, x0=x0_4, n=999, nit=243, nfev=1188), removed: too sensitive to rounding errors
|
||||
dict(F=F_6, x0=x0_6, n=100, nit=6, nfev=6), # Results from dfsane.f; papers list nit=3, nfev=3
|
||||
dict(F=F_7, x0=x0_7, n=99, nit=23, nfev=29), # Must have n%3==0, typo in papers?
|
||||
dict(F=F_7, x0=x0_7, n=999, nit=23, nfev=29), # Must have n%3==0, typo in papers?
|
||||
dict(F=F_9, x0=x0_9, n=100, nit=12, nfev=18), # Results from dfsane.f; papers list nit=nfev=6?
|
||||
dict(F=F_9, x0=x0_9, n=1000, nit=12, nfev=18),
|
||||
dict(F=F_10, x0=x0_10, n=1000, nit=5, nfev=5), # Results from dfsane.f; papers list nit=2, nfev=12
|
||||
]
|
||||
|
||||
# Check also scaling invariance
|
||||
for xscale, yscale, line_search in itertools.product([1.0, 1e-10, 1e10], [1.0, 1e-10, 1e10],
|
||||
['cruz', 'cheng']):
|
||||
for problem in table_1:
|
||||
n = problem['n']
|
||||
func = lambda x, n: yscale*problem['F'](x/xscale, n)
|
||||
args = (n,)
|
||||
x0 = problem['x0'](n) * xscale
|
||||
|
||||
fatol = np.sqrt(n) * e_a * yscale + e_r * np.linalg.norm(func(x0, n))
|
||||
|
||||
sigma_eps = 1e-10 * min(yscale/xscale, xscale/yscale)
|
||||
sigma_0 = xscale/yscale
|
||||
|
||||
with np.errstate(over='ignore'):
|
||||
sol = root(func, x0, args=args,
|
||||
options=dict(ftol=0, fatol=fatol, maxfev=problem['nfev'] + 1,
|
||||
sigma_0=sigma_0, sigma_eps=sigma_eps,
|
||||
line_search=line_search),
|
||||
method='DF-SANE')
|
||||
|
||||
err_msg = repr([xscale, yscale, line_search, problem, np.linalg.norm(func(sol.x, n)),
|
||||
fatol, sol.success, sol.nit, sol.nfev])
|
||||
assert_(sol.success, err_msg)
|
||||
assert_(sol.nfev <= problem['nfev'] + 1, err_msg) # nfev+1: dfsane.f doesn't count first eval
|
||||
assert_(sol.nit <= problem['nit'], err_msg)
|
||||
assert_(np.linalg.norm(func(sol.x, n)) <= fatol, err_msg)
|
||||
|
||||
|
||||
def test_complex():
|
||||
def func(z):
|
||||
return z**2 - 1 + 2j
|
||||
x0 = 2.0j
|
||||
|
||||
ftol = 1e-4
|
||||
sol = root(func, x0, tol=ftol, method='DF-SANE')
|
||||
|
||||
assert_(sol.success)
|
||||
|
||||
f0 = np.linalg.norm(func(x0))
|
||||
fx = np.linalg.norm(func(sol.x))
|
||||
assert_(fx <= ftol*f0)
|
||||
|
||||
|
||||
def test_linear_definite():
|
||||
# The DF-SANE paper proves convergence for "strongly isolated"
|
||||
# solutions.
|
||||
#
|
||||
# For linear systems F(x) = A x - b = 0, with A positive or
|
||||
# negative definite, the solution is strongly isolated.
|
||||
|
||||
def check_solvability(A, b, line_search='cruz'):
|
||||
func = lambda x: A.dot(x) - b
|
||||
xp = np.linalg.solve(A, b)
|
||||
eps = np.linalg.norm(func(xp)) * 1e3
|
||||
sol = root(func, b, options=dict(fatol=eps, ftol=0, maxfev=17523, line_search=line_search),
|
||||
method='DF-SANE')
|
||||
assert_(sol.success)
|
||||
assert_(np.linalg.norm(func(sol.x)) <= eps)
|
||||
|
||||
n = 90
|
||||
|
||||
# Test linear pos.def. system
|
||||
np.random.seed(1234)
|
||||
A = np.arange(n*n).reshape(n, n)
|
||||
A = A + n*n * np.diag(1 + np.arange(n))
|
||||
assert_(np.linalg.eigvals(A).min() > 0)
|
||||
b = np.arange(n) * 1.0
|
||||
check_solvability(A, b, 'cruz')
|
||||
check_solvability(A, b, 'cheng')
|
||||
|
||||
# Test linear neg.def. system
|
||||
check_solvability(-A, b, 'cruz')
|
||||
check_solvability(-A, b, 'cheng')
|
||||
|
||||
|
||||
def test_shape():
|
||||
def f(x, arg):
|
||||
return x - arg
|
||||
|
||||
for dt in [float, complex]:
|
||||
x = np.zeros([2,2])
|
||||
arg = np.ones([2,2], dtype=dt)
|
||||
|
||||
sol = root(f, x, args=(arg,), method='DF-SANE')
|
||||
assert_(sol.success)
|
||||
assert_equal(sol.x.shape, x.shape)
|
||||
|
||||
|
||||
# Some of the test functions and initial guesses listed in
|
||||
# [W. La Cruz, M. Raydan. Optimization Methods and Software, 18, 583 (2003)]
|
||||
|
||||
def F_1(x, n):
|
||||
g = np.zeros([n])
|
||||
i = np.arange(2, n+1)
|
||||
g[0] = exp(x[0] - 1) - 1
|
||||
g[1:] = i*(exp(x[1:] - 1) - x[1:])
|
||||
return g
|
||||
|
||||
def x0_1(n):
|
||||
x0 = np.empty([n])
|
||||
x0.fill(n/(n-1))
|
||||
return x0
|
||||
|
||||
def F_2(x, n):
|
||||
g = np.zeros([n])
|
||||
i = np.arange(2, n+1)
|
||||
g[0] = exp(x[0]) - 1
|
||||
g[1:] = 0.1*i*(exp(x[1:]) + x[:-1] - 1)
|
||||
return g
|
||||
|
||||
def x0_2(n):
|
||||
x0 = np.empty([n])
|
||||
x0.fill(1/n**2)
|
||||
return x0
|
||||
|
||||
def F_4(x, n):
|
||||
assert_equal(n % 3, 0)
|
||||
g = np.zeros([n])
|
||||
# Note: the first line is typoed in some of the references;
|
||||
# correct in original [Gasparo, Optimization Meth. 13, 79 (2000)]
|
||||
g[::3] = 0.6 * x[::3] + 1.6 * x[1::3]**3 - 7.2 * x[1::3]**2 + 9.6 * x[1::3] - 4.8
|
||||
g[1::3] = 0.48 * x[::3] - 0.72 * x[1::3]**3 + 3.24 * x[1::3]**2 - 4.32 * x[1::3] - x[2::3] + 0.2 * x[2::3]**3 + 2.16
|
||||
g[2::3] = 1.25 * x[2::3] - 0.25*x[2::3]**3
|
||||
return g
|
||||
|
||||
def x0_4(n):
|
||||
assert_equal(n % 3, 0)
|
||||
x0 = np.array([-1, 1/2, -1] * (n//3))
|
||||
return x0
|
||||
|
||||
def F_6(x, n):
|
||||
c = 0.9
|
||||
mu = (np.arange(1, n+1) - 0.5)/n
|
||||
return x - 1/(1 - c/(2*n) * (mu[:,None]*x / (mu[:,None] + mu)).sum(axis=1))
|
||||
|
||||
def x0_6(n):
|
||||
return np.ones([n])
|
||||
|
||||
def F_7(x, n):
|
||||
assert_equal(n % 3, 0)
|
||||
|
||||
def phi(t):
|
||||
v = 0.5*t - 2
|
||||
v[t > -1] = ((-592*t**3 + 888*t**2 + 4551*t - 1924)/1998)[t > -1]
|
||||
v[t >= 2] = (0.5*t + 2)[t >= 2]
|
||||
return v
|
||||
g = np.zeros([n])
|
||||
g[::3] = 1e4 * x[1::3]**2 - 1
|
||||
g[1::3] = exp(-x[::3]) + exp(-x[1::3]) - 1.0001
|
||||
g[2::3] = phi(x[2::3])
|
||||
return g
|
||||
|
||||
def x0_7(n):
|
||||
assert_equal(n % 3, 0)
|
||||
return np.array([1e-3, 18, 1] * (n//3))
|
||||
|
||||
def F_9(x, n):
|
||||
g = np.zeros([n])
|
||||
i = np.arange(2, n)
|
||||
g[0] = x[0]**3/3 + x[1]**2/2
|
||||
g[1:-1] = -x[1:-1]**2/2 + i*x[1:-1]**3/3 + x[2:]**2/2
|
||||
g[-1] = -x[-1]**2/2 + n*x[-1]**3/3
|
||||
return g
|
||||
|
||||
def x0_9(n):
|
||||
return np.ones([n])
|
||||
|
||||
def F_10(x, n):
|
||||
return np.log(1 + x) - x/n
|
||||
|
||||
def x0_10(n):
|
||||
return np.ones([n])
|
||||
131
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_cobyla.py
vendored
Normal file
131
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_cobyla.py
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
import math
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from numpy.testing import assert_allclose, assert_, assert_array_equal
|
||||
|
||||
from scipy.optimize import fmin_cobyla, minimize
|
||||
|
||||
|
||||
class TestCobyla:
|
||||
def setup_method(self):
|
||||
self.x0 = [4.95, 0.66]
|
||||
self.solution = [math.sqrt(25 - (2.0/3)**2), 2.0/3]
|
||||
self.opts = {'disp': False, 'rhobeg': 1, 'tol': 1e-5,
|
||||
'maxiter': 100}
|
||||
|
||||
def fun(self, x):
|
||||
return x[0]**2 + abs(x[1])**3
|
||||
|
||||
def con1(self, x):
|
||||
return x[0]**2 + x[1]**2 - 25
|
||||
|
||||
def con2(self, x):
|
||||
return -self.con1(x)
|
||||
|
||||
def test_simple(self):
|
||||
# use disp=True as smoke test for gh-8118
|
||||
x = fmin_cobyla(self.fun, self.x0, [self.con1, self.con2], rhobeg=1,
|
||||
rhoend=1e-5, maxfun=100, disp=True)
|
||||
assert_allclose(x, self.solution, atol=1e-4)
|
||||
|
||||
def test_minimize_simple(self):
|
||||
class Callback:
|
||||
def __init__(self):
|
||||
self.n_calls = 0
|
||||
self.last_x = None
|
||||
|
||||
def __call__(self, x):
|
||||
self.n_calls += 1
|
||||
self.last_x = x
|
||||
|
||||
callback = Callback()
|
||||
|
||||
# Minimize with method='COBYLA'
|
||||
cons = ({'type': 'ineq', 'fun': self.con1},
|
||||
{'type': 'ineq', 'fun': self.con2})
|
||||
sol = minimize(self.fun, self.x0, method='cobyla', constraints=cons,
|
||||
callback=callback, options=self.opts)
|
||||
assert_allclose(sol.x, self.solution, atol=1e-4)
|
||||
assert_(sol.success, sol.message)
|
||||
assert_(sol.maxcv < 1e-5, sol)
|
||||
assert_(sol.nfev < 70, sol)
|
||||
assert_(sol.fun < self.fun(self.solution) + 1e-3, sol)
|
||||
assert_(sol.nfev == callback.n_calls,
|
||||
"Callback is not called exactly once for every function eval.")
|
||||
assert_array_equal(sol.x, callback.last_x,
|
||||
"Last design vector sent to the callback is not equal to returned value.")
|
||||
|
||||
def test_minimize_constraint_violation(self):
|
||||
np.random.seed(1234)
|
||||
pb = np.random.rand(10, 10)
|
||||
spread = np.random.rand(10)
|
||||
|
||||
def p(w):
|
||||
return pb.dot(w)
|
||||
|
||||
def f(w):
|
||||
return -(w * spread).sum()
|
||||
|
||||
def c1(w):
|
||||
return 500 - abs(p(w)).sum()
|
||||
|
||||
def c2(w):
|
||||
return 5 - abs(p(w).sum())
|
||||
|
||||
def c3(w):
|
||||
return 5 - abs(p(w)).max()
|
||||
|
||||
cons = ({'type': 'ineq', 'fun': c1},
|
||||
{'type': 'ineq', 'fun': c2},
|
||||
{'type': 'ineq', 'fun': c3})
|
||||
w0 = np.zeros((10, 1))
|
||||
message = 'Use of `minimize` with `x0.ndim != 1` is deprecated.'
|
||||
with pytest.warns(DeprecationWarning, match=message):
|
||||
sol = minimize(f, w0, method='cobyla', constraints=cons,
|
||||
options={'catol': 1e-6})
|
||||
assert_(sol.maxcv > 1e-6)
|
||||
assert_(not sol.success)
|
||||
|
||||
|
||||
def test_vector_constraints():
|
||||
# test that fmin_cobyla and minimize can take a combination
|
||||
# of constraints, some returning a number and others an array
|
||||
def fun(x):
|
||||
return (x[0] - 1)**2 + (x[1] - 2.5)**2
|
||||
|
||||
def fmin(x):
|
||||
return fun(x) - 1
|
||||
|
||||
def cons1(x):
|
||||
a = np.array([[1, -2, 2], [-1, -2, 6], [-1, 2, 2]])
|
||||
return np.array([a[i, 0] * x[0] + a[i, 1] * x[1] +
|
||||
a[i, 2] for i in range(len(a))])
|
||||
|
||||
def cons2(x):
|
||||
return x # identity, acts as bounds x > 0
|
||||
|
||||
x0 = np.array([2, 0])
|
||||
cons_list = [fun, cons1, cons2]
|
||||
|
||||
xsol = [1.4, 1.7]
|
||||
fsol = 0.8
|
||||
|
||||
# testing fmin_cobyla
|
||||
sol = fmin_cobyla(fun, x0, cons_list, rhoend=1e-5)
|
||||
assert_allclose(sol, xsol, atol=1e-4)
|
||||
|
||||
sol = fmin_cobyla(fun, x0, fmin, rhoend=1e-5)
|
||||
assert_allclose(fun(sol), 1, atol=1e-4)
|
||||
|
||||
# testing minimize
|
||||
constraints = [{'type': 'ineq', 'fun': cons} for cons in cons_list]
|
||||
sol = minimize(fun, x0, constraints=constraints, tol=1e-5)
|
||||
assert_allclose(sol.x, xsol, atol=1e-4)
|
||||
assert_(sol.success, sol.message)
|
||||
assert_allclose(sol.fun, fsol, atol=1e-4)
|
||||
|
||||
constraints = {'type': 'ineq', 'fun': fmin}
|
||||
sol = minimize(fun, x0, constraints=constraints, tol=1e-5)
|
||||
assert_allclose(sol.fun, 1, atol=1e-4)
|
||||
|
||||
267
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_constraint_conversion.py
vendored
Normal file
267
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_constraint_conversion.py
vendored
Normal file
@@ -0,0 +1,267 @@
|
||||
"""
|
||||
Unit test for constraint conversion
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import (assert_array_almost_equal,
|
||||
assert_allclose, assert_warns, suppress_warnings)
|
||||
import pytest
|
||||
from scipy.optimize import (NonlinearConstraint, LinearConstraint,
|
||||
OptimizeWarning, minimize, BFGS)
|
||||
from .test_minimize_constrained import (Maratos, HyperbolicIneq, Rosenbrock,
|
||||
IneqRosenbrock, EqIneqRosenbrock,
|
||||
BoundedRosenbrock, Elec)
|
||||
|
||||
|
||||
class TestOldToNew:
|
||||
x0 = (2, 0)
|
||||
bnds = ((0, None), (0, None))
|
||||
method = "trust-constr"
|
||||
|
||||
def test_constraint_dictionary_1(self):
|
||||
fun = lambda x: (x[0] - 1)**2 + (x[1] - 2.5)**2
|
||||
cons = ({'type': 'ineq', 'fun': lambda x: x[0] - 2 * x[1] + 2},
|
||||
{'type': 'ineq', 'fun': lambda x: -x[0] - 2 * x[1] + 6},
|
||||
{'type': 'ineq', 'fun': lambda x: -x[0] + 2 * x[1] + 2})
|
||||
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(UserWarning, "delta_grad == 0.0")
|
||||
res = minimize(fun, self.x0, method=self.method,
|
||||
bounds=self.bnds, constraints=cons)
|
||||
assert_allclose(res.x, [1.4, 1.7], rtol=1e-4)
|
||||
assert_allclose(res.fun, 0.8, rtol=1e-4)
|
||||
|
||||
def test_constraint_dictionary_2(self):
|
||||
fun = lambda x: (x[0] - 1)**2 + (x[1] - 2.5)**2
|
||||
cons = {'type': 'eq',
|
||||
'fun': lambda x, p1, p2: p1*x[0] - p2*x[1],
|
||||
'args': (1, 1.1),
|
||||
'jac': lambda x, p1, p2: np.array([[p1, -p2]])}
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(UserWarning, "delta_grad == 0.0")
|
||||
res = minimize(fun, self.x0, method=self.method,
|
||||
bounds=self.bnds, constraints=cons)
|
||||
assert_allclose(res.x, [1.7918552, 1.62895927])
|
||||
assert_allclose(res.fun, 1.3857466063348418)
|
||||
|
||||
def test_constraint_dictionary_3(self):
|
||||
fun = lambda x: (x[0] - 1)**2 + (x[1] - 2.5)**2
|
||||
cons = [{'type': 'ineq', 'fun': lambda x: x[0] - 2 * x[1] + 2},
|
||||
NonlinearConstraint(lambda x: x[0] - x[1], 0, 0)]
|
||||
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(UserWarning, "delta_grad == 0.0")
|
||||
res = minimize(fun, self.x0, method=self.method,
|
||||
bounds=self.bnds, constraints=cons)
|
||||
assert_allclose(res.x, [1.75, 1.75], rtol=1e-4)
|
||||
assert_allclose(res.fun, 1.125, rtol=1e-4)
|
||||
|
||||
|
||||
class TestNewToOld:
|
||||
|
||||
def test_multiple_constraint_objects(self):
|
||||
fun = lambda x: (x[0] - 1)**2 + (x[1] - 2.5)**2 + (x[2] - 0.75)**2
|
||||
x0 = [2, 0, 1]
|
||||
coni = [] # only inequality constraints (can use cobyla)
|
||||
methods = ["slsqp", "cobyla", "trust-constr"]
|
||||
|
||||
# mixed old and new
|
||||
coni.append([{'type': 'ineq', 'fun': lambda x: x[0] - 2 * x[1] + 2},
|
||||
NonlinearConstraint(lambda x: x[0] - x[1], -1, 1)])
|
||||
|
||||
coni.append([LinearConstraint([1, -2, 0], -2, np.inf),
|
||||
NonlinearConstraint(lambda x: x[0] - x[1], -1, 1)])
|
||||
|
||||
coni.append([NonlinearConstraint(lambda x: x[0] - 2 * x[1] + 2, 0, np.inf),
|
||||
NonlinearConstraint(lambda x: x[0] - x[1], -1, 1)])
|
||||
|
||||
for con in coni:
|
||||
funs = {}
|
||||
for method in methods:
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(UserWarning)
|
||||
result = minimize(fun, x0, method=method, constraints=con)
|
||||
funs[method] = result.fun
|
||||
assert_allclose(funs['slsqp'], funs['trust-constr'], rtol=1e-4)
|
||||
assert_allclose(funs['cobyla'], funs['trust-constr'], rtol=1e-4)
|
||||
|
||||
def test_individual_constraint_objects(self):
|
||||
fun = lambda x: (x[0] - 1)**2 + (x[1] - 2.5)**2 + (x[2] - 0.75)**2
|
||||
x0 = [2, 0, 1]
|
||||
|
||||
cone = [] # with equality constraints (can't use cobyla)
|
||||
coni = [] # only inequality constraints (can use cobyla)
|
||||
methods = ["slsqp", "cobyla", "trust-constr"]
|
||||
|
||||
# nonstandard data types for constraint equality bounds
|
||||
cone.append(NonlinearConstraint(lambda x: x[0] - x[1], 1, 1))
|
||||
cone.append(NonlinearConstraint(lambda x: x[0] - x[1], [1.21], [1.21]))
|
||||
cone.append(NonlinearConstraint(lambda x: x[0] - x[1],
|
||||
1.21, np.array([1.21])))
|
||||
|
||||
# multiple equalities
|
||||
cone.append(NonlinearConstraint(
|
||||
lambda x: [x[0] - x[1], x[1] - x[2]],
|
||||
1.21, 1.21)) # two same equalities
|
||||
cone.append(NonlinearConstraint(
|
||||
lambda x: [x[0] - x[1], x[1] - x[2]],
|
||||
[1.21, 1.4], [1.21, 1.4])) # two different equalities
|
||||
cone.append(NonlinearConstraint(
|
||||
lambda x: [x[0] - x[1], x[1] - x[2]],
|
||||
[1.21, 1.21], 1.21)) # equality specified two ways
|
||||
cone.append(NonlinearConstraint(
|
||||
lambda x: [x[0] - x[1], x[1] - x[2]],
|
||||
[1.21, -np.inf], [1.21, np.inf])) # equality + unbounded
|
||||
|
||||
# nonstandard data types for constraint inequality bounds
|
||||
coni.append(NonlinearConstraint(lambda x: x[0] - x[1], 1.21, np.inf))
|
||||
coni.append(NonlinearConstraint(lambda x: x[0] - x[1], [1.21], np.inf))
|
||||
coni.append(NonlinearConstraint(lambda x: x[0] - x[1],
|
||||
1.21, np.array([np.inf])))
|
||||
coni.append(NonlinearConstraint(lambda x: x[0] - x[1], -np.inf, -3))
|
||||
coni.append(NonlinearConstraint(lambda x: x[0] - x[1],
|
||||
np.array(-np.inf), -3))
|
||||
|
||||
# multiple inequalities/equalities
|
||||
coni.append(NonlinearConstraint(
|
||||
lambda x: [x[0] - x[1], x[1] - x[2]],
|
||||
1.21, np.inf)) # two same inequalities
|
||||
cone.append(NonlinearConstraint(
|
||||
lambda x: [x[0] - x[1], x[1] - x[2]],
|
||||
[1.21, -np.inf], [1.21, 1.4])) # mixed equality/inequality
|
||||
coni.append(NonlinearConstraint(
|
||||
lambda x: [x[0] - x[1], x[1] - x[2]],
|
||||
[1.1, .8], [1.2, 1.4])) # bounded above and below
|
||||
coni.append(NonlinearConstraint(
|
||||
lambda x: [x[0] - x[1], x[1] - x[2]],
|
||||
[-1.2, -1.4], [-1.1, -.8])) # - bounded above and below
|
||||
|
||||
# quick check of LinearConstraint class (very little new code to test)
|
||||
cone.append(LinearConstraint([1, -1, 0], 1.21, 1.21))
|
||||
cone.append(LinearConstraint([[1, -1, 0], [0, 1, -1]], 1.21, 1.21))
|
||||
cone.append(LinearConstraint([[1, -1, 0], [0, 1, -1]],
|
||||
[1.21, -np.inf], [1.21, 1.4]))
|
||||
|
||||
for con in coni:
|
||||
funs = {}
|
||||
for method in methods:
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(UserWarning)
|
||||
result = minimize(fun, x0, method=method, constraints=con)
|
||||
funs[method] = result.fun
|
||||
assert_allclose(funs['slsqp'], funs['trust-constr'], rtol=1e-3)
|
||||
assert_allclose(funs['cobyla'], funs['trust-constr'], rtol=1e-3)
|
||||
|
||||
for con in cone:
|
||||
funs = {}
|
||||
for method in methods[::2]: # skip cobyla
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(UserWarning)
|
||||
result = minimize(fun, x0, method=method, constraints=con)
|
||||
funs[method] = result.fun
|
||||
assert_allclose(funs['slsqp'], funs['trust-constr'], rtol=1e-3)
|
||||
|
||||
|
||||
class TestNewToOldSLSQP:
|
||||
method = 'slsqp'
|
||||
elec = Elec(n_electrons=2)
|
||||
elec.x_opt = np.array([-0.58438468, 0.58438466, 0.73597047,
|
||||
-0.73597044, 0.34180668, -0.34180667])
|
||||
brock = BoundedRosenbrock()
|
||||
brock.x_opt = [0, 0]
|
||||
list_of_problems = [Maratos(),
|
||||
HyperbolicIneq(),
|
||||
Rosenbrock(),
|
||||
IneqRosenbrock(),
|
||||
EqIneqRosenbrock(),
|
||||
elec,
|
||||
brock
|
||||
]
|
||||
|
||||
def test_list_of_problems(self):
|
||||
|
||||
for prob in self.list_of_problems:
|
||||
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(UserWarning)
|
||||
result = minimize(prob.fun, prob.x0,
|
||||
method=self.method,
|
||||
bounds=prob.bounds,
|
||||
constraints=prob.constr)
|
||||
|
||||
assert_array_almost_equal(result.x, prob.x_opt, decimal=3)
|
||||
|
||||
def test_warn_mixed_constraints(self):
|
||||
# warns about inefficiency of mixed equality/inequality constraints
|
||||
fun = lambda x: (x[0] - 1)**2 + (x[1] - 2.5)**2 + (x[2] - 0.75)**2
|
||||
cons = NonlinearConstraint(lambda x: [x[0]**2 - x[1], x[1] - x[2]],
|
||||
[1.1, .8], [1.1, 1.4])
|
||||
bnds = ((0, None), (0, None), (0, None))
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(UserWarning, "delta_grad == 0.0")
|
||||
assert_warns(OptimizeWarning, minimize, fun, (2, 0, 1),
|
||||
method=self.method, bounds=bnds, constraints=cons)
|
||||
|
||||
def test_warn_ignored_options(self):
|
||||
# warns about constraint options being ignored
|
||||
fun = lambda x: (x[0] - 1)**2 + (x[1] - 2.5)**2 + (x[2] - 0.75)**2
|
||||
x0 = (2, 0, 1)
|
||||
|
||||
if self.method == "slsqp":
|
||||
bnds = ((0, None), (0, None), (0, None))
|
||||
else:
|
||||
bnds = None
|
||||
|
||||
cons = NonlinearConstraint(lambda x: x[0], 2, np.inf)
|
||||
res = minimize(fun, x0, method=self.method,
|
||||
bounds=bnds, constraints=cons)
|
||||
# no warnings without constraint options
|
||||
assert_allclose(res.fun, 1)
|
||||
|
||||
cons = LinearConstraint([1, 0, 0], 2, np.inf)
|
||||
res = minimize(fun, x0, method=self.method,
|
||||
bounds=bnds, constraints=cons)
|
||||
# no warnings without constraint options
|
||||
assert_allclose(res.fun, 1)
|
||||
|
||||
cons = []
|
||||
cons.append(NonlinearConstraint(lambda x: x[0]**2, 2, np.inf,
|
||||
keep_feasible=True))
|
||||
cons.append(NonlinearConstraint(lambda x: x[0]**2, 2, np.inf,
|
||||
hess=BFGS()))
|
||||
cons.append(NonlinearConstraint(lambda x: x[0]**2, 2, np.inf,
|
||||
finite_diff_jac_sparsity=42))
|
||||
cons.append(NonlinearConstraint(lambda x: x[0]**2, 2, np.inf,
|
||||
finite_diff_rel_step=42))
|
||||
cons.append(LinearConstraint([1, 0, 0], 2, np.inf,
|
||||
keep_feasible=True))
|
||||
for con in cons:
|
||||
assert_warns(OptimizeWarning, minimize, fun, x0,
|
||||
method=self.method, bounds=bnds, constraints=cons)
|
||||
|
||||
|
||||
class TestNewToOldCobyla:
|
||||
method = 'cobyla'
|
||||
|
||||
list_of_problems = [
|
||||
Elec(n_electrons=2),
|
||||
Elec(n_electrons=4),
|
||||
]
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_list_of_problems(self):
|
||||
|
||||
for prob in self.list_of_problems:
|
||||
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(UserWarning)
|
||||
truth = minimize(prob.fun, prob.x0,
|
||||
method='trust-constr',
|
||||
bounds=prob.bounds,
|
||||
constraints=prob.constr)
|
||||
result = minimize(prob.fun, prob.x0,
|
||||
method=self.method,
|
||||
bounds=prob.bounds,
|
||||
constraints=prob.constr)
|
||||
|
||||
assert_allclose(result.fun, truth.fun, rtol=1e-3)
|
||||
234
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_constraints.py
vendored
Normal file
234
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_constraints.py
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import TestCase, assert_array_equal
|
||||
import scipy.sparse as sps
|
||||
from scipy.optimize._constraints import (
|
||||
Bounds, LinearConstraint, NonlinearConstraint, PreparedConstraint,
|
||||
new_bounds_to_old, old_bound_to_new, strict_bounds)
|
||||
|
||||
|
||||
class TestStrictBounds(TestCase):
|
||||
def test_scalarvalue_unique_enforce_feasibility(self):
|
||||
m = 3
|
||||
lb = 2
|
||||
ub = 4
|
||||
enforce_feasibility = False
|
||||
strict_lb, strict_ub = strict_bounds(lb, ub,
|
||||
enforce_feasibility,
|
||||
m)
|
||||
assert_array_equal(strict_lb, [-np.inf, -np.inf, -np.inf])
|
||||
assert_array_equal(strict_ub, [np.inf, np.inf, np.inf])
|
||||
|
||||
enforce_feasibility = True
|
||||
strict_lb, strict_ub = strict_bounds(lb, ub,
|
||||
enforce_feasibility,
|
||||
m)
|
||||
assert_array_equal(strict_lb, [2, 2, 2])
|
||||
assert_array_equal(strict_ub, [4, 4, 4])
|
||||
|
||||
def test_vectorvalue_unique_enforce_feasibility(self):
|
||||
m = 3
|
||||
lb = [1, 2, 3]
|
||||
ub = [4, 5, 6]
|
||||
enforce_feasibility = False
|
||||
strict_lb, strict_ub = strict_bounds(lb, ub,
|
||||
enforce_feasibility,
|
||||
m)
|
||||
assert_array_equal(strict_lb, [-np.inf, -np.inf, -np.inf])
|
||||
assert_array_equal(strict_ub, [np.inf, np.inf, np.inf])
|
||||
|
||||
enforce_feasibility = True
|
||||
strict_lb, strict_ub = strict_bounds(lb, ub,
|
||||
enforce_feasibility,
|
||||
m)
|
||||
assert_array_equal(strict_lb, [1, 2, 3])
|
||||
assert_array_equal(strict_ub, [4, 5, 6])
|
||||
|
||||
def test_scalarvalue_vector_enforce_feasibility(self):
|
||||
m = 3
|
||||
lb = 2
|
||||
ub = 4
|
||||
enforce_feasibility = [False, True, False]
|
||||
strict_lb, strict_ub = strict_bounds(lb, ub,
|
||||
enforce_feasibility,
|
||||
m)
|
||||
assert_array_equal(strict_lb, [-np.inf, 2, -np.inf])
|
||||
assert_array_equal(strict_ub, [np.inf, 4, np.inf])
|
||||
|
||||
def test_vectorvalue_vector_enforce_feasibility(self):
|
||||
m = 3
|
||||
lb = [1, 2, 3]
|
||||
ub = [4, 6, np.inf]
|
||||
enforce_feasibility = [True, False, True]
|
||||
strict_lb, strict_ub = strict_bounds(lb, ub,
|
||||
enforce_feasibility,
|
||||
m)
|
||||
assert_array_equal(strict_lb, [1, -np.inf, 3])
|
||||
assert_array_equal(strict_ub, [4, np.inf, np.inf])
|
||||
|
||||
|
||||
def test_prepare_constraint_infeasible_x0():
|
||||
lb = np.array([0, 20, 30])
|
||||
ub = np.array([0.5, np.inf, 70])
|
||||
x0 = np.array([1, 2, 3])
|
||||
enforce_feasibility = np.array([False, True, True], dtype=bool)
|
||||
bounds = Bounds(lb, ub, enforce_feasibility)
|
||||
pytest.raises(ValueError, PreparedConstraint, bounds, x0)
|
||||
|
||||
pc = PreparedConstraint(Bounds(lb, ub), [1, 2, 3])
|
||||
assert (pc.violation([1, 2, 3]) > 0).any()
|
||||
assert (pc.violation([0.25, 21, 31]) == 0).all()
|
||||
|
||||
x0 = np.array([1, 2, 3, 4])
|
||||
A = np.array([[1, 2, 3, 4], [5, 0, 0, 6], [7, 0, 8, 0]])
|
||||
enforce_feasibility = np.array([True, True, True], dtype=bool)
|
||||
linear = LinearConstraint(A, -np.inf, 0, enforce_feasibility)
|
||||
pytest.raises(ValueError, PreparedConstraint, linear, x0)
|
||||
|
||||
pc = PreparedConstraint(LinearConstraint(A, -np.inf, 0),
|
||||
[1, 2, 3, 4])
|
||||
assert (pc.violation([1, 2, 3, 4]) > 0).any()
|
||||
assert (pc.violation([-10, 2, -10, 4]) == 0).all()
|
||||
|
||||
def fun(x):
|
||||
return A.dot(x)
|
||||
|
||||
def jac(x):
|
||||
return A
|
||||
|
||||
def hess(x, v):
|
||||
return sps.csr_matrix((4, 4))
|
||||
|
||||
nonlinear = NonlinearConstraint(fun, -np.inf, 0, jac, hess,
|
||||
enforce_feasibility)
|
||||
pytest.raises(ValueError, PreparedConstraint, nonlinear, x0)
|
||||
|
||||
pc = PreparedConstraint(nonlinear, [-10, 2, -10, 4])
|
||||
assert (pc.violation([1, 2, 3, 4]) > 0).any()
|
||||
assert (pc.violation([-10, 2, -10, 4]) == 0).all()
|
||||
|
||||
|
||||
def test_violation():
|
||||
def cons_f(x):
|
||||
return np.array([x[0] ** 2 + x[1], x[0] ** 2 - x[1]])
|
||||
|
||||
nlc = NonlinearConstraint(cons_f, [-1, -0.8500], [2, 2])
|
||||
pc = PreparedConstraint(nlc, [0.5, 1])
|
||||
|
||||
assert_array_equal(pc.violation([0.5, 1]), [0., 0.])
|
||||
|
||||
np.testing.assert_almost_equal(pc.violation([0.5, 1.2]), [0., 0.1])
|
||||
|
||||
np.testing.assert_almost_equal(pc.violation([1.2, 1.2]), [0.64, 0])
|
||||
|
||||
np.testing.assert_almost_equal(pc.violation([0.1, -1.2]), [0.19, 0])
|
||||
|
||||
np.testing.assert_almost_equal(pc.violation([0.1, 2]), [0.01, 1.14])
|
||||
|
||||
|
||||
def test_new_bounds_to_old():
|
||||
lb = np.array([-np.inf, 2, 3])
|
||||
ub = np.array([3, np.inf, 10])
|
||||
|
||||
bounds = [(None, 3), (2, None), (3, 10)]
|
||||
assert_array_equal(new_bounds_to_old(lb, ub, 3), bounds)
|
||||
|
||||
bounds_single_lb = [(-1, 3), (-1, None), (-1, 10)]
|
||||
assert_array_equal(new_bounds_to_old(-1, ub, 3), bounds_single_lb)
|
||||
|
||||
bounds_no_lb = [(None, 3), (None, None), (None, 10)]
|
||||
assert_array_equal(new_bounds_to_old(-np.inf, ub, 3), bounds_no_lb)
|
||||
|
||||
bounds_single_ub = [(None, 20), (2, 20), (3, 20)]
|
||||
assert_array_equal(new_bounds_to_old(lb, 20, 3), bounds_single_ub)
|
||||
|
||||
bounds_no_ub = [(None, None), (2, None), (3, None)]
|
||||
assert_array_equal(new_bounds_to_old(lb, np.inf, 3), bounds_no_ub)
|
||||
|
||||
bounds_single_both = [(1, 2), (1, 2), (1, 2)]
|
||||
assert_array_equal(new_bounds_to_old(1, 2, 3), bounds_single_both)
|
||||
|
||||
bounds_no_both = [(None, None), (None, None), (None, None)]
|
||||
assert_array_equal(new_bounds_to_old(-np.inf, np.inf, 3), bounds_no_both)
|
||||
|
||||
|
||||
def test_old_bounds_to_new():
|
||||
bounds = ([1, 2], (None, 3), (-1, None))
|
||||
lb_true = np.array([1, -np.inf, -1])
|
||||
ub_true = np.array([2, 3, np.inf])
|
||||
|
||||
lb, ub = old_bound_to_new(bounds)
|
||||
assert_array_equal(lb, lb_true)
|
||||
assert_array_equal(ub, ub_true)
|
||||
|
||||
bounds = [(-np.inf, np.inf), (np.array([1]), np.array([1]))]
|
||||
lb, ub = old_bound_to_new(bounds)
|
||||
|
||||
assert_array_equal(lb, [-np.inf, 1])
|
||||
assert_array_equal(ub, [np.inf, 1])
|
||||
|
||||
|
||||
class TestBounds:
|
||||
def test_repr(self):
|
||||
# so that eval works
|
||||
from numpy import array, inf # noqa
|
||||
for args in (
|
||||
(-1.0, 5.0),
|
||||
(-1.0, np.inf, True),
|
||||
(np.array([1.0, -np.inf]), np.array([2.0, np.inf])),
|
||||
(np.array([1.0, -np.inf]), np.array([2.0, np.inf]),
|
||||
np.array([True, False])),
|
||||
):
|
||||
bounds = Bounds(*args)
|
||||
bounds2 = eval(repr(Bounds(*args)))
|
||||
assert_array_equal(bounds.lb, bounds2.lb)
|
||||
assert_array_equal(bounds.ub, bounds2.ub)
|
||||
assert_array_equal(bounds.keep_feasible, bounds2.keep_feasible)
|
||||
|
||||
def test_array(self):
|
||||
# gh13501
|
||||
b = Bounds(lb=[0.0, 0.0], ub=[1.0, 1.0])
|
||||
assert isinstance(b.lb, np.ndarray)
|
||||
assert isinstance(b.ub, np.ndarray)
|
||||
|
||||
def test_defaults(self):
|
||||
b1 = Bounds()
|
||||
b2 = Bounds(np.asarray(-np.inf), np.asarray(np.inf))
|
||||
assert b1.lb == b2.lb
|
||||
assert b1.ub == b2.ub
|
||||
|
||||
def test_input_validation(self):
|
||||
message = "`lb`, `ub`, and `keep_feasible` must be broadcastable."
|
||||
with pytest.raises(ValueError, match=message):
|
||||
Bounds([1, 2], [1, 2, 3])
|
||||
|
||||
def test_residual(self):
|
||||
bounds = Bounds(-2, 4)
|
||||
x0 = [-1, 2]
|
||||
np.testing.assert_allclose(bounds.residual(x0), ([1, 4], [5, 2]))
|
||||
|
||||
|
||||
class TestLinearConstraint:
|
||||
def test_defaults(self):
|
||||
A = np.eye(4)
|
||||
lc = LinearConstraint(A)
|
||||
lc2 = LinearConstraint(A, -np.inf, np.inf)
|
||||
assert_array_equal(lc.lb, lc2.lb)
|
||||
assert_array_equal(lc.ub, lc2.ub)
|
||||
|
||||
def test_input_validation(self):
|
||||
A = np.eye(4)
|
||||
message = "`lb`, `ub`, and `keep_feasible` must be broadcastable"
|
||||
with pytest.raises(ValueError, match=message):
|
||||
LinearConstraint(A, [1, 2], [1, 2, 3])
|
||||
|
||||
A = np.empty((4, 3, 5))
|
||||
message = "`A` must have exactly two dimensions."
|
||||
with pytest.raises(ValueError, match=message):
|
||||
LinearConstraint(A)
|
||||
|
||||
def test_residual(self):
|
||||
A = np.eye(2)
|
||||
lc = LinearConstraint(A, -2, 4)
|
||||
x0 = [-1, 2]
|
||||
np.testing.assert_allclose(lc.residual(x0), ([1, 4], [5, 2]))
|
||||
92
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_cython_optimize.py
vendored
Normal file
92
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_cython_optimize.py
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
Test Cython optimize zeros API functions: ``bisect``, ``ridder``, ``brenth``,
|
||||
and ``brentq`` in `scipy.optimize.cython_optimize`, by finding the roots of a
|
||||
3rd order polynomial given a sequence of constant terms, ``a0``, and fixed 1st,
|
||||
2nd, and 3rd order terms in ``args``.
|
||||
|
||||
.. math::
|
||||
|
||||
f(x, a0, args) = ((args[2]*x + args[1])*x + args[0])*x + a0
|
||||
|
||||
The 3rd order polynomial function is written in Cython and called in a Python
|
||||
wrapper named after the zero function. See the private ``_zeros`` Cython module
|
||||
in `scipy.optimize.cython_optimze` for more information.
|
||||
"""
|
||||
|
||||
import numpy.testing as npt
|
||||
from scipy.optimize.cython_optimize import _zeros
|
||||
|
||||
# CONSTANTS
|
||||
# Solve x**3 - A0 = 0 for A0 = [2.0, 2.1, ..., 2.9].
|
||||
# The ARGS have 3 elements just to show how this could be done for any cubic
|
||||
# polynomial.
|
||||
A0 = tuple(-2.0 - x/10.0 for x in range(10)) # constant term
|
||||
ARGS = (0.0, 0.0, 1.0) # 1st, 2nd, and 3rd order terms
|
||||
XLO, XHI = 0.0, 2.0 # first and second bounds of zeros functions
|
||||
# absolute and relative tolerances and max iterations for zeros functions
|
||||
XTOL, RTOL, MITR = 0.001, 0.001, 10
|
||||
EXPECTED = [(-a0) ** (1.0/3.0) for a0 in A0]
|
||||
# = [1.2599210498948732,
|
||||
# 1.2805791649874942,
|
||||
# 1.300591446851387,
|
||||
# 1.3200061217959123,
|
||||
# 1.338865900164339,
|
||||
# 1.3572088082974532,
|
||||
# 1.375068867074141,
|
||||
# 1.3924766500838337,
|
||||
# 1.4094597464129783,
|
||||
# 1.4260431471424087]
|
||||
|
||||
|
||||
# test bisect
|
||||
def test_bisect():
|
||||
npt.assert_allclose(
|
||||
EXPECTED,
|
||||
list(
|
||||
_zeros.loop_example('bisect', A0, ARGS, XLO, XHI, XTOL, RTOL, MITR)
|
||||
),
|
||||
rtol=RTOL, atol=XTOL
|
||||
)
|
||||
|
||||
|
||||
# test ridder
|
||||
def test_ridder():
|
||||
npt.assert_allclose(
|
||||
EXPECTED,
|
||||
list(
|
||||
_zeros.loop_example('ridder', A0, ARGS, XLO, XHI, XTOL, RTOL, MITR)
|
||||
),
|
||||
rtol=RTOL, atol=XTOL
|
||||
)
|
||||
|
||||
|
||||
# test brenth
|
||||
def test_brenth():
|
||||
npt.assert_allclose(
|
||||
EXPECTED,
|
||||
list(
|
||||
_zeros.loop_example('brenth', A0, ARGS, XLO, XHI, XTOL, RTOL, MITR)
|
||||
),
|
||||
rtol=RTOL, atol=XTOL
|
||||
)
|
||||
|
||||
|
||||
# test brentq
|
||||
def test_brentq():
|
||||
npt.assert_allclose(
|
||||
EXPECTED,
|
||||
list(
|
||||
_zeros.loop_example('brentq', A0, ARGS, XLO, XHI, XTOL, RTOL, MITR)
|
||||
),
|
||||
rtol=RTOL, atol=XTOL
|
||||
)
|
||||
|
||||
|
||||
# test brentq with full output
|
||||
def test_brentq_full_output():
|
||||
output = _zeros.full_output_example(
|
||||
(A0[0],) + ARGS, XLO, XHI, XTOL, RTOL, MITR)
|
||||
npt.assert_allclose(EXPECTED[0], output['root'], rtol=RTOL, atol=XTOL)
|
||||
npt.assert_equal(6, output['iterations'])
|
||||
npt.assert_equal(7, output['funcalls'])
|
||||
npt.assert_equal(0, output['error_num'])
|
||||
731
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_differentiable_functions.py
vendored
Normal file
731
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_differentiable_functions.py
vendored
Normal file
@@ -0,0 +1,731 @@
|
||||
import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import (TestCase, assert_array_almost_equal,
|
||||
assert_array_equal, assert_, assert_allclose,
|
||||
assert_equal)
|
||||
from scipy.sparse import csr_matrix
|
||||
from scipy.sparse.linalg import LinearOperator
|
||||
from scipy.optimize._differentiable_functions import (ScalarFunction,
|
||||
VectorFunction,
|
||||
LinearVectorFunction,
|
||||
IdentityVectorFunction)
|
||||
from scipy.optimize import rosen, rosen_der, rosen_hess
|
||||
from scipy.optimize._hessian_update_strategy import BFGS
|
||||
|
||||
|
||||
class ExScalarFunction:
|
||||
|
||||
def __init__(self):
|
||||
self.nfev = 0
|
||||
self.ngev = 0
|
||||
self.nhev = 0
|
||||
|
||||
def fun(self, x):
|
||||
self.nfev += 1
|
||||
return 2*(x[0]**2 + x[1]**2 - 1) - x[0]
|
||||
|
||||
def grad(self, x):
|
||||
self.ngev += 1
|
||||
return np.array([4*x[0]-1, 4*x[1]])
|
||||
|
||||
def hess(self, x):
|
||||
self.nhev += 1
|
||||
return 4*np.eye(2)
|
||||
|
||||
|
||||
class TestScalarFunction(TestCase):
|
||||
|
||||
def test_finite_difference_grad(self):
|
||||
ex = ExScalarFunction()
|
||||
nfev = 0
|
||||
ngev = 0
|
||||
|
||||
x0 = [1.0, 0.0]
|
||||
analit = ScalarFunction(ex.fun, x0, (), ex.grad,
|
||||
ex.hess, None, (-np.inf, np.inf))
|
||||
nfev += 1
|
||||
ngev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev, nfev)
|
||||
assert_array_equal(ex.ngev, ngev)
|
||||
assert_array_equal(analit.ngev, nfev)
|
||||
approx = ScalarFunction(ex.fun, x0, (), '2-point',
|
||||
ex.hess, None, (-np.inf, np.inf))
|
||||
nfev += 3
|
||||
ngev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
assert_array_equal(analit.f, approx.f)
|
||||
assert_array_almost_equal(analit.g, approx.g)
|
||||
|
||||
x = [10, 0.3]
|
||||
f_analit = analit.fun(x)
|
||||
g_analit = analit.grad(x)
|
||||
nfev += 1
|
||||
ngev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
f_approx = approx.fun(x)
|
||||
g_approx = approx.grad(x)
|
||||
nfev += 3
|
||||
ngev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
assert_array_almost_equal(f_analit, f_approx)
|
||||
assert_array_almost_equal(g_analit, g_approx)
|
||||
|
||||
x = [2.0, 1.0]
|
||||
g_analit = analit.grad(x)
|
||||
ngev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
|
||||
g_approx = approx.grad(x)
|
||||
nfev += 3
|
||||
ngev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
assert_array_almost_equal(g_analit, g_approx)
|
||||
|
||||
x = [2.5, 0.3]
|
||||
f_analit = analit.fun(x)
|
||||
g_analit = analit.grad(x)
|
||||
nfev += 1
|
||||
ngev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
f_approx = approx.fun(x)
|
||||
g_approx = approx.grad(x)
|
||||
nfev += 3
|
||||
ngev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
assert_array_almost_equal(f_analit, f_approx)
|
||||
assert_array_almost_equal(g_analit, g_approx)
|
||||
|
||||
x = [2, 0.3]
|
||||
f_analit = analit.fun(x)
|
||||
g_analit = analit.grad(x)
|
||||
nfev += 1
|
||||
ngev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
f_approx = approx.fun(x)
|
||||
g_approx = approx.grad(x)
|
||||
nfev += 3
|
||||
ngev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
assert_array_almost_equal(f_analit, f_approx)
|
||||
assert_array_almost_equal(g_analit, g_approx)
|
||||
|
||||
def test_fun_and_grad(self):
|
||||
ex = ExScalarFunction()
|
||||
|
||||
def fg_allclose(x, y):
|
||||
assert_allclose(x[0], y[0])
|
||||
assert_allclose(x[1], y[1])
|
||||
|
||||
# with analytic gradient
|
||||
x0 = [2.0, 0.3]
|
||||
analit = ScalarFunction(ex.fun, x0, (), ex.grad,
|
||||
ex.hess, None, (-np.inf, np.inf))
|
||||
|
||||
fg = ex.fun(x0), ex.grad(x0)
|
||||
fg_allclose(analit.fun_and_grad(x0), fg)
|
||||
assert analit.ngev == 1
|
||||
|
||||
x0[1] = 1.
|
||||
fg = ex.fun(x0), ex.grad(x0)
|
||||
fg_allclose(analit.fun_and_grad(x0), fg)
|
||||
|
||||
# with finite difference gradient
|
||||
x0 = [2.0, 0.3]
|
||||
sf = ScalarFunction(ex.fun, x0, (), '3-point',
|
||||
ex.hess, None, (-np.inf, np.inf))
|
||||
assert sf.ngev == 1
|
||||
fg = ex.fun(x0), ex.grad(x0)
|
||||
fg_allclose(sf.fun_and_grad(x0), fg)
|
||||
assert sf.ngev == 1
|
||||
|
||||
x0[1] = 1.
|
||||
fg = ex.fun(x0), ex.grad(x0)
|
||||
fg_allclose(sf.fun_and_grad(x0), fg)
|
||||
|
||||
def test_finite_difference_hess_linear_operator(self):
|
||||
ex = ExScalarFunction()
|
||||
nfev = 0
|
||||
ngev = 0
|
||||
nhev = 0
|
||||
|
||||
x0 = [1.0, 0.0]
|
||||
analit = ScalarFunction(ex.fun, x0, (), ex.grad,
|
||||
ex.hess, None, (-np.inf, np.inf))
|
||||
nfev += 1
|
||||
ngev += 1
|
||||
nhev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev, nfev)
|
||||
assert_array_equal(ex.ngev, ngev)
|
||||
assert_array_equal(analit.ngev, ngev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev, nhev)
|
||||
approx = ScalarFunction(ex.fun, x0, (), ex.grad,
|
||||
'2-point', None, (-np.inf, np.inf))
|
||||
assert_(isinstance(approx.H, LinearOperator))
|
||||
for v in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
|
||||
assert_array_equal(analit.f, approx.f)
|
||||
assert_array_almost_equal(analit.g, approx.g)
|
||||
assert_array_almost_equal(analit.H.dot(v), approx.H.dot(v))
|
||||
nfev += 1
|
||||
ngev += 4
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.ngev, ngev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
|
||||
x = [2.0, 1.0]
|
||||
H_analit = analit.hess(x)
|
||||
nhev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.ngev, ngev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
H_approx = approx.hess(x)
|
||||
assert_(isinstance(H_approx, LinearOperator))
|
||||
for v in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
|
||||
assert_array_almost_equal(H_analit.dot(v), H_approx.dot(v))
|
||||
ngev += 4
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.ngev, ngev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
|
||||
x = [2.1, 1.2]
|
||||
H_analit = analit.hess(x)
|
||||
nhev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.ngev, ngev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
H_approx = approx.hess(x)
|
||||
assert_(isinstance(H_approx, LinearOperator))
|
||||
for v in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
|
||||
assert_array_almost_equal(H_analit.dot(v), H_approx.dot(v))
|
||||
ngev += 4
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.ngev, ngev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
|
||||
x = [2.5, 0.3]
|
||||
_ = analit.grad(x)
|
||||
H_analit = analit.hess(x)
|
||||
ngev += 1
|
||||
nhev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.ngev, ngev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
_ = approx.grad(x)
|
||||
H_approx = approx.hess(x)
|
||||
assert_(isinstance(H_approx, LinearOperator))
|
||||
for v in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
|
||||
assert_array_almost_equal(H_analit.dot(v), H_approx.dot(v))
|
||||
ngev += 4
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.ngev, ngev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
|
||||
x = [5.2, 2.3]
|
||||
_ = analit.grad(x)
|
||||
H_analit = analit.hess(x)
|
||||
ngev += 1
|
||||
nhev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.ngev, ngev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
_ = approx.grad(x)
|
||||
H_approx = approx.hess(x)
|
||||
assert_(isinstance(H_approx, LinearOperator))
|
||||
for v in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
|
||||
assert_array_almost_equal(H_analit.dot(v), H_approx.dot(v))
|
||||
ngev += 4
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.ngev, ngev)
|
||||
assert_array_equal(analit.ngev+approx.ngev, ngev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
|
||||
def test_x_storage_overlap(self):
|
||||
# Scalar_Function should not store references to arrays, it should
|
||||
# store copies - this checks that updating an array in-place causes
|
||||
# Scalar_Function.x to be updated.
|
||||
|
||||
def f(x):
|
||||
return np.sum(np.asarray(x) ** 2)
|
||||
|
||||
x = np.array([1., 2., 3.])
|
||||
sf = ScalarFunction(f, x, (), '3-point', lambda x: x, None, (-np.inf, np.inf))
|
||||
|
||||
assert x is not sf.x
|
||||
assert_equal(sf.fun(x), 14.0)
|
||||
assert x is not sf.x
|
||||
|
||||
x[0] = 0.
|
||||
f1 = sf.fun(x)
|
||||
assert_equal(f1, 13.0)
|
||||
|
||||
x[0] = 1
|
||||
f2 = sf.fun(x)
|
||||
assert_equal(f2, 14.0)
|
||||
assert x is not sf.x
|
||||
|
||||
# now test with a HessianUpdate strategy specified
|
||||
hess = BFGS()
|
||||
x = np.array([1., 2., 3.])
|
||||
sf = ScalarFunction(f, x, (), '3-point', hess, None, (-np.inf, np.inf))
|
||||
|
||||
assert x is not sf.x
|
||||
assert_equal(sf.fun(x), 14.0)
|
||||
assert x is not sf.x
|
||||
|
||||
x[0] = 0.
|
||||
f1 = sf.fun(x)
|
||||
assert_equal(f1, 13.0)
|
||||
|
||||
x[0] = 1
|
||||
f2 = sf.fun(x)
|
||||
assert_equal(f2, 14.0)
|
||||
assert x is not sf.x
|
||||
|
||||
# gh13740 x is changed in user function
|
||||
def ff(x):
|
||||
x *= x # overwrite x
|
||||
return np.sum(x)
|
||||
|
||||
x = np.array([1., 2., 3.])
|
||||
sf = ScalarFunction(
|
||||
ff, x, (), '3-point', lambda x: x, None, (-np.inf, np.inf)
|
||||
)
|
||||
assert x is not sf.x
|
||||
assert_equal(sf.fun(x), 14.0)
|
||||
assert_equal(sf.x, np.array([1., 2., 3.]))
|
||||
assert x is not sf.x
|
||||
|
||||
def test_lowest_x(self):
|
||||
# ScalarFunction should remember the lowest func(x) visited.
|
||||
x0 = np.array([2, 3, 4])
|
||||
sf = ScalarFunction(rosen, x0, (), rosen_der, rosen_hess,
|
||||
None, None)
|
||||
sf.fun([1, 1, 1])
|
||||
sf.fun(x0)
|
||||
sf.fun([1.01, 1, 1.0])
|
||||
sf.grad([1.01, 1, 1.0])
|
||||
assert_equal(sf._lowest_f, 0.0)
|
||||
assert_equal(sf._lowest_x, [1.0, 1.0, 1.0])
|
||||
|
||||
sf = ScalarFunction(rosen, x0, (), '2-point', rosen_hess,
|
||||
None, (-np.inf, np.inf))
|
||||
sf.fun([1, 1, 1])
|
||||
sf.fun(x0)
|
||||
sf.fun([1.01, 1, 1.0])
|
||||
sf.grad([1.01, 1, 1.0])
|
||||
assert_equal(sf._lowest_f, 0.0)
|
||||
assert_equal(sf._lowest_x, [1.0, 1.0, 1.0])
|
||||
|
||||
|
||||
class ExVectorialFunction:
|
||||
|
||||
def __init__(self):
|
||||
self.nfev = 0
|
||||
self.njev = 0
|
||||
self.nhev = 0
|
||||
|
||||
def fun(self, x):
|
||||
self.nfev += 1
|
||||
return np.array([2*(x[0]**2 + x[1]**2 - 1) - x[0],
|
||||
4*(x[0]**3 + x[1]**2 - 4) - 3*x[0]])
|
||||
|
||||
def jac(self, x):
|
||||
self.njev += 1
|
||||
return np.array([[4*x[0]-1, 4*x[1]],
|
||||
[12*x[0]**2-3, 8*x[1]]])
|
||||
|
||||
def hess(self, x, v):
|
||||
self.nhev += 1
|
||||
return v[0]*4*np.eye(2) + v[1]*np.array([[24*x[0], 0],
|
||||
[0, 8]])
|
||||
|
||||
|
||||
class TestVectorialFunction(TestCase):
|
||||
|
||||
def test_finite_difference_jac(self):
|
||||
ex = ExVectorialFunction()
|
||||
nfev = 0
|
||||
njev = 0
|
||||
|
||||
x0 = [1.0, 0.0]
|
||||
analit = VectorFunction(ex.fun, x0, ex.jac, ex.hess, None, None,
|
||||
(-np.inf, np.inf), None)
|
||||
nfev += 1
|
||||
njev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev, njev)
|
||||
approx = VectorFunction(ex.fun, x0, '2-point', ex.hess, None, None,
|
||||
(-np.inf, np.inf), None)
|
||||
nfev += 3
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
assert_array_equal(analit.f, approx.f)
|
||||
assert_array_almost_equal(analit.J, approx.J)
|
||||
|
||||
x = [10, 0.3]
|
||||
f_analit = analit.fun(x)
|
||||
J_analit = analit.jac(x)
|
||||
nfev += 1
|
||||
njev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
f_approx = approx.fun(x)
|
||||
J_approx = approx.jac(x)
|
||||
nfev += 3
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
assert_array_almost_equal(f_analit, f_approx)
|
||||
assert_array_almost_equal(J_analit, J_approx, decimal=4)
|
||||
|
||||
x = [2.0, 1.0]
|
||||
J_analit = analit.jac(x)
|
||||
njev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
J_approx = approx.jac(x)
|
||||
nfev += 3
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
assert_array_almost_equal(J_analit, J_approx)
|
||||
|
||||
x = [2.5, 0.3]
|
||||
f_analit = analit.fun(x)
|
||||
J_analit = analit.jac(x)
|
||||
nfev += 1
|
||||
njev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
f_approx = approx.fun(x)
|
||||
J_approx = approx.jac(x)
|
||||
nfev += 3
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
assert_array_almost_equal(f_analit, f_approx)
|
||||
assert_array_almost_equal(J_analit, J_approx)
|
||||
|
||||
x = [2, 0.3]
|
||||
f_analit = analit.fun(x)
|
||||
J_analit = analit.jac(x)
|
||||
nfev += 1
|
||||
njev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
f_approx = approx.fun(x)
|
||||
J_approx = approx.jac(x)
|
||||
nfev += 3
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
assert_array_almost_equal(f_analit, f_approx)
|
||||
assert_array_almost_equal(J_analit, J_approx)
|
||||
|
||||
def test_finite_difference_hess_linear_operator(self):
|
||||
ex = ExVectorialFunction()
|
||||
nfev = 0
|
||||
njev = 0
|
||||
nhev = 0
|
||||
|
||||
x0 = [1.0, 0.0]
|
||||
v0 = [1.0, 2.0]
|
||||
analit = VectorFunction(ex.fun, x0, ex.jac, ex.hess, None, None,
|
||||
(-np.inf, np.inf), None)
|
||||
nfev += 1
|
||||
njev += 1
|
||||
nhev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev, njev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev, nhev)
|
||||
approx = VectorFunction(ex.fun, x0, ex.jac, '2-point', None, None,
|
||||
(-np.inf, np.inf), None)
|
||||
assert_(isinstance(approx.H, LinearOperator))
|
||||
for p in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
|
||||
assert_array_equal(analit.f, approx.f)
|
||||
assert_array_almost_equal(analit.J, approx.J)
|
||||
assert_array_almost_equal(analit.H.dot(p), approx.H.dot(p))
|
||||
nfev += 1
|
||||
njev += 4
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
|
||||
x = [2.0, 1.0]
|
||||
H_analit = analit.hess(x, v0)
|
||||
nhev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
H_approx = approx.hess(x, v0)
|
||||
assert_(isinstance(H_approx, LinearOperator))
|
||||
for p in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
|
||||
assert_array_almost_equal(H_analit.dot(p), H_approx.dot(p),
|
||||
decimal=5)
|
||||
njev += 4
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
|
||||
x = [2.1, 1.2]
|
||||
v = [1.0, 1.0]
|
||||
H_analit = analit.hess(x, v)
|
||||
nhev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
H_approx = approx.hess(x, v)
|
||||
assert_(isinstance(H_approx, LinearOperator))
|
||||
for v in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
|
||||
assert_array_almost_equal(H_analit.dot(v), H_approx.dot(v))
|
||||
njev += 4
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
|
||||
x = [2.5, 0.3]
|
||||
_ = analit.jac(x)
|
||||
H_analit = analit.hess(x, v0)
|
||||
njev += 1
|
||||
nhev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
_ = approx.jac(x)
|
||||
H_approx = approx.hess(x, v0)
|
||||
assert_(isinstance(H_approx, LinearOperator))
|
||||
for v in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
|
||||
assert_array_almost_equal(H_analit.dot(v), H_approx.dot(v), decimal=4)
|
||||
njev += 4
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
|
||||
x = [5.2, 2.3]
|
||||
v = [2.3, 5.2]
|
||||
_ = analit.jac(x)
|
||||
H_analit = analit.hess(x, v)
|
||||
njev += 1
|
||||
nhev += 1
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
_ = approx.jac(x)
|
||||
H_approx = approx.hess(x, v)
|
||||
assert_(isinstance(H_approx, LinearOperator))
|
||||
for v in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
|
||||
assert_array_almost_equal(H_analit.dot(v), H_approx.dot(v), decimal=4)
|
||||
njev += 4
|
||||
assert_array_equal(ex.nfev, nfev)
|
||||
assert_array_equal(analit.nfev+approx.nfev, nfev)
|
||||
assert_array_equal(ex.njev, njev)
|
||||
assert_array_equal(analit.njev+approx.njev, njev)
|
||||
assert_array_equal(ex.nhev, nhev)
|
||||
assert_array_equal(analit.nhev+approx.nhev, nhev)
|
||||
|
||||
def test_x_storage_overlap(self):
|
||||
# VectorFunction should not store references to arrays, it should
|
||||
# store copies - this checks that updating an array in-place causes
|
||||
# Scalar_Function.x to be updated.
|
||||
ex = ExVectorialFunction()
|
||||
x0 = np.array([1.0, 0.0])
|
||||
|
||||
vf = VectorFunction(ex.fun, x0, '3-point', ex.hess, None, None,
|
||||
(-np.inf, np.inf), None)
|
||||
|
||||
assert x0 is not vf.x
|
||||
assert_equal(vf.fun(x0), ex.fun(x0))
|
||||
assert x0 is not vf.x
|
||||
|
||||
x0[0] = 2.
|
||||
assert_equal(vf.fun(x0), ex.fun(x0))
|
||||
assert x0 is not vf.x
|
||||
|
||||
x0[0] = 1.
|
||||
assert_equal(vf.fun(x0), ex.fun(x0))
|
||||
assert x0 is not vf.x
|
||||
|
||||
# now test with a HessianUpdate strategy specified
|
||||
hess = BFGS()
|
||||
x0 = np.array([1.0, 0.0])
|
||||
vf = VectorFunction(ex.fun, x0, '3-point', hess, None, None,
|
||||
(-np.inf, np.inf), None)
|
||||
|
||||
with pytest.warns(UserWarning):
|
||||
# filter UserWarning because ExVectorialFunction is linear and
|
||||
# a quasi-Newton approximation is used for the Hessian.
|
||||
assert x0 is not vf.x
|
||||
assert_equal(vf.fun(x0), ex.fun(x0))
|
||||
assert x0 is not vf.x
|
||||
|
||||
x0[0] = 2.
|
||||
assert_equal(vf.fun(x0), ex.fun(x0))
|
||||
assert x0 is not vf.x
|
||||
|
||||
x0[0] = 1.
|
||||
assert_equal(vf.fun(x0), ex.fun(x0))
|
||||
assert x0 is not vf.x
|
||||
|
||||
|
||||
def test_LinearVectorFunction():
|
||||
A_dense = np.array([
|
||||
[-1, 2, 0],
|
||||
[0, 4, 2]
|
||||
])
|
||||
x0 = np.zeros(3)
|
||||
A_sparse = csr_matrix(A_dense)
|
||||
x = np.array([1, -1, 0])
|
||||
v = np.array([-1, 1])
|
||||
Ax = np.array([-3, -4])
|
||||
|
||||
f1 = LinearVectorFunction(A_dense, x0, None)
|
||||
assert_(not f1.sparse_jacobian)
|
||||
|
||||
f2 = LinearVectorFunction(A_dense, x0, True)
|
||||
assert_(f2.sparse_jacobian)
|
||||
|
||||
f3 = LinearVectorFunction(A_dense, x0, False)
|
||||
assert_(not f3.sparse_jacobian)
|
||||
|
||||
f4 = LinearVectorFunction(A_sparse, x0, None)
|
||||
assert_(f4.sparse_jacobian)
|
||||
|
||||
f5 = LinearVectorFunction(A_sparse, x0, True)
|
||||
assert_(f5.sparse_jacobian)
|
||||
|
||||
f6 = LinearVectorFunction(A_sparse, x0, False)
|
||||
assert_(not f6.sparse_jacobian)
|
||||
|
||||
assert_array_equal(f1.fun(x), Ax)
|
||||
assert_array_equal(f2.fun(x), Ax)
|
||||
assert_array_equal(f1.jac(x), A_dense)
|
||||
assert_array_equal(f2.jac(x).toarray(), A_sparse.toarray())
|
||||
assert_array_equal(f1.hess(x, v).toarray(), np.zeros((3, 3)))
|
||||
|
||||
|
||||
def test_LinearVectorFunction_memoization():
|
||||
A = np.array([[-1, 2, 0], [0, 4, 2]])
|
||||
x0 = np.array([1, 2, -1])
|
||||
fun = LinearVectorFunction(A, x0, False)
|
||||
|
||||
assert_array_equal(x0, fun.x)
|
||||
assert_array_equal(A.dot(x0), fun.f)
|
||||
|
||||
x1 = np.array([-1, 3, 10])
|
||||
assert_array_equal(A, fun.jac(x1))
|
||||
assert_array_equal(x1, fun.x)
|
||||
assert_array_equal(A.dot(x0), fun.f)
|
||||
assert_array_equal(A.dot(x1), fun.fun(x1))
|
||||
assert_array_equal(A.dot(x1), fun.f)
|
||||
|
||||
|
||||
def test_IdentityVectorFunction():
|
||||
x0 = np.zeros(3)
|
||||
|
||||
f1 = IdentityVectorFunction(x0, None)
|
||||
f2 = IdentityVectorFunction(x0, False)
|
||||
f3 = IdentityVectorFunction(x0, True)
|
||||
|
||||
assert_(f1.sparse_jacobian)
|
||||
assert_(not f2.sparse_jacobian)
|
||||
assert_(f3.sparse_jacobian)
|
||||
|
||||
x = np.array([-1, 2, 1])
|
||||
v = np.array([-2, 3, 0])
|
||||
|
||||
assert_array_equal(f1.fun(x), x)
|
||||
assert_array_equal(f2.fun(x), x)
|
||||
|
||||
assert_array_equal(f1.jac(x).toarray(), np.eye(3))
|
||||
assert_array_equal(f2.jac(x), np.eye(3))
|
||||
|
||||
assert_array_equal(f1.hess(x, v).toarray(), np.zeros((3, 3)))
|
||||
318
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_direct.py
vendored
Normal file
318
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_direct.py
vendored
Normal file
@@ -0,0 +1,318 @@
|
||||
"""
|
||||
Unit test for DIRECT optimization algorithm.
|
||||
"""
|
||||
from numpy.testing import (assert_allclose,
|
||||
assert_array_less)
|
||||
import pytest
|
||||
import numpy as np
|
||||
from scipy.optimize import direct, Bounds
|
||||
|
||||
|
||||
class TestDIRECT:
|
||||
|
||||
def setup_method(self):
|
||||
self.fun_calls = 0
|
||||
self.bounds_sphere = 4*[(-2, 3)]
|
||||
self.optimum_sphere_pos = np.zeros((4, ))
|
||||
self.optimum_sphere = 0.0
|
||||
self.bounds_stylinski_tang = Bounds([-4., -4.], [4., 4.])
|
||||
self.maxiter = 1000
|
||||
|
||||
# test functions
|
||||
def sphere(self, x):
|
||||
self.fun_calls += 1
|
||||
return np.square(x).sum()
|
||||
|
||||
def inv(self, x):
|
||||
if np.sum(x) == 0:
|
||||
raise ZeroDivisionError()
|
||||
return 1/np.sum(x)
|
||||
|
||||
def nan_fun(self, x):
|
||||
return np.nan
|
||||
|
||||
def inf_fun(self, x):
|
||||
return np.inf
|
||||
|
||||
def styblinski_tang(self, pos):
|
||||
x, y = pos
|
||||
return 0.5 * (x**4 - 16 * x**2 + 5 * x + y**4 - 16 * y**2 + 5 * y)
|
||||
|
||||
@pytest.mark.parametrize("locally_biased", [True, False])
|
||||
def test_direct(self, locally_biased):
|
||||
res = direct(self.sphere, self.bounds_sphere,
|
||||
locally_biased=locally_biased)
|
||||
|
||||
# test accuracy
|
||||
assert_allclose(res.x, self.optimum_sphere_pos,
|
||||
rtol=1e-3, atol=1e-3)
|
||||
assert_allclose(res.fun, self.optimum_sphere, atol=1e-5, rtol=1e-5)
|
||||
|
||||
# test that result lies within bounds
|
||||
_bounds = np.asarray(self.bounds_sphere)
|
||||
assert_array_less(_bounds[:, 0], res.x)
|
||||
assert_array_less(res.x, _bounds[:, 1])
|
||||
|
||||
# test number of function evaluations. Original DIRECT overshoots by
|
||||
# up to 500 evaluations in last iteration
|
||||
assert res.nfev <= 1000 * (len(self.bounds_sphere) + 1)
|
||||
# test that number of function evaluations is correct
|
||||
assert res.nfev == self.fun_calls
|
||||
|
||||
# test that number of iterations is below supplied maximum
|
||||
assert res.nit <= self.maxiter
|
||||
|
||||
@pytest.mark.parametrize("locally_biased", [True, False])
|
||||
def test_direct_callback(self, locally_biased):
|
||||
# test that callback does not change the result
|
||||
res = direct(self.sphere, self.bounds_sphere,
|
||||
locally_biased=locally_biased)
|
||||
|
||||
def callback(x):
|
||||
x = 2*x
|
||||
dummy = np.square(x)
|
||||
print("DIRECT minimization algorithm callback test")
|
||||
return dummy
|
||||
|
||||
res_callback = direct(self.sphere, self.bounds_sphere,
|
||||
locally_biased=locally_biased,
|
||||
callback=callback)
|
||||
|
||||
assert_allclose(res.x, res_callback.x)
|
||||
|
||||
assert res.nit == res_callback.nit
|
||||
assert res.nfev == res_callback.nfev
|
||||
assert res.status == res_callback.status
|
||||
assert res.success == res_callback.success
|
||||
assert res.fun == res_callback.fun
|
||||
assert_allclose(res.x, res_callback.x)
|
||||
assert res.message == res_callback.message
|
||||
|
||||
# test accuracy
|
||||
assert_allclose(res_callback.x, self.optimum_sphere_pos,
|
||||
rtol=1e-3, atol=1e-3)
|
||||
assert_allclose(res_callback.fun, self.optimum_sphere,
|
||||
atol=1e-5, rtol=1e-5)
|
||||
|
||||
@pytest.mark.parametrize("locally_biased", [True, False])
|
||||
def test_exception(self, locally_biased):
|
||||
bounds = 4*[(-10, 10)]
|
||||
with pytest.raises(ZeroDivisionError):
|
||||
direct(self.inv, bounds=bounds,
|
||||
locally_biased=locally_biased)
|
||||
|
||||
@pytest.mark.parametrize("locally_biased", [True, False])
|
||||
def test_nan(self, locally_biased):
|
||||
bounds = 4*[(-10, 10)]
|
||||
direct(self.nan_fun, bounds=bounds,
|
||||
locally_biased=locally_biased)
|
||||
|
||||
@pytest.mark.parametrize("len_tol", [1e-3, 1e-4])
|
||||
@pytest.mark.parametrize("locally_biased", [True, False])
|
||||
def test_len_tol(self, len_tol, locally_biased):
|
||||
bounds = 4*[(-10., 10.)]
|
||||
res = direct(self.sphere, bounds=bounds, len_tol=len_tol,
|
||||
vol_tol=1e-30, locally_biased=locally_biased)
|
||||
assert res.status == 5
|
||||
assert res.success
|
||||
assert_allclose(res.x, np.zeros((4, )))
|
||||
message = ("The side length measure of the hyperrectangle containing "
|
||||
"the lowest function value found is below "
|
||||
f"len_tol={len_tol}")
|
||||
assert res.message == message
|
||||
|
||||
@pytest.mark.parametrize("vol_tol", [1e-6, 1e-8])
|
||||
@pytest.mark.parametrize("locally_biased", [True, False])
|
||||
def test_vol_tol(self, vol_tol, locally_biased):
|
||||
bounds = 4*[(-10., 10.)]
|
||||
res = direct(self.sphere, bounds=bounds, vol_tol=vol_tol,
|
||||
len_tol=0., locally_biased=locally_biased)
|
||||
assert res.status == 4
|
||||
assert res.success
|
||||
assert_allclose(res.x, np.zeros((4, )))
|
||||
message = ("The volume of the hyperrectangle containing the lowest "
|
||||
f"function value found is below vol_tol={vol_tol}")
|
||||
assert res.message == message
|
||||
|
||||
@pytest.mark.parametrize("f_min_rtol", [1e-3, 1e-5, 1e-7])
|
||||
@pytest.mark.parametrize("locally_biased", [True, False])
|
||||
def test_f_min(self, f_min_rtol, locally_biased):
|
||||
# test that desired function value is reached within
|
||||
# relative tolerance of f_min_rtol
|
||||
f_min = 1.
|
||||
bounds = 4*[(-2., 10.)]
|
||||
res = direct(self.sphere, bounds=bounds, f_min=f_min,
|
||||
f_min_rtol=f_min_rtol,
|
||||
locally_biased=locally_biased)
|
||||
assert res.status == 3
|
||||
assert res.success
|
||||
assert res.fun < f_min * (1. + f_min_rtol)
|
||||
message = ("The best function value found is within a relative "
|
||||
f"error={f_min_rtol} of the (known) global optimum f_min")
|
||||
assert res.message == message
|
||||
|
||||
def circle_with_args(self, x, a, b):
|
||||
return np.square(x[0] - a) + np.square(x[1] - b).sum()
|
||||
|
||||
@pytest.mark.parametrize("locally_biased", [True, False])
|
||||
def test_f_circle_with_args(self, locally_biased):
|
||||
bounds = 2*[(-2.0, 2.0)]
|
||||
|
||||
res = direct(self.circle_with_args, bounds, args=(1, 1), maxfun=1250,
|
||||
locally_biased=locally_biased)
|
||||
assert_allclose(res.x, np.array([1., 1.]), rtol=1e-5)
|
||||
|
||||
@pytest.mark.parametrize("locally_biased", [True, False])
|
||||
def test_failure_maxfun(self, locally_biased):
|
||||
# test that if optimization runs for the maximal number of
|
||||
# evaluations, success = False is returned
|
||||
|
||||
maxfun = 100
|
||||
result = direct(self.styblinski_tang, self.bounds_stylinski_tang,
|
||||
maxfun=maxfun, locally_biased=locally_biased)
|
||||
assert result.success is False
|
||||
assert result.status == 1
|
||||
assert result.nfev >= maxfun
|
||||
message = ("Number of function evaluations done is "
|
||||
f"larger than maxfun={maxfun}")
|
||||
assert result.message == message
|
||||
|
||||
@pytest.mark.parametrize("locally_biased", [True, False])
|
||||
def test_failure_maxiter(self, locally_biased):
|
||||
# test that if optimization runs for the maximal number of
|
||||
# iterations, success = False is returned
|
||||
|
||||
maxiter = 10
|
||||
result = direct(self.styblinski_tang, self.bounds_stylinski_tang,
|
||||
maxiter=maxiter, locally_biased=locally_biased)
|
||||
assert result.success is False
|
||||
assert result.status == 2
|
||||
assert result.nit >= maxiter
|
||||
message = f"Number of iterations is larger than maxiter={maxiter}"
|
||||
assert result.message == message
|
||||
|
||||
@pytest.mark.parametrize("locally_biased", [True, False])
|
||||
def test_bounds_variants(self, locally_biased):
|
||||
# test that new and old bounds yield same result
|
||||
|
||||
lb = [-6., 1., -5.]
|
||||
ub = [-1., 3., 5.]
|
||||
x_opt = np.array([-1., 1., 0.])
|
||||
bounds_old = list(zip(lb, ub))
|
||||
bounds_new = Bounds(lb, ub)
|
||||
|
||||
res_old_bounds = direct(self.sphere, bounds_old,
|
||||
locally_biased=locally_biased)
|
||||
res_new_bounds = direct(self.sphere, bounds_new,
|
||||
locally_biased=locally_biased)
|
||||
|
||||
assert res_new_bounds.nfev == res_old_bounds.nfev
|
||||
assert res_new_bounds.message == res_old_bounds.message
|
||||
assert res_new_bounds.success == res_old_bounds.success
|
||||
assert res_new_bounds.nit == res_old_bounds.nit
|
||||
assert_allclose(res_new_bounds.x, res_old_bounds.x)
|
||||
assert_allclose(res_new_bounds.x, x_opt, rtol=1e-2)
|
||||
|
||||
@pytest.mark.parametrize("locally_biased", [True, False])
|
||||
@pytest.mark.parametrize("eps", [1e-5, 1e-4, 1e-3])
|
||||
def test_epsilon(self, eps, locally_biased):
|
||||
result = direct(self.styblinski_tang, self.bounds_stylinski_tang,
|
||||
eps=eps, vol_tol=1e-6,
|
||||
locally_biased=locally_biased)
|
||||
assert result.status == 4
|
||||
assert result.success
|
||||
|
||||
@pytest.mark.xslow
|
||||
@pytest.mark.parametrize("locally_biased", [True, False])
|
||||
def test_no_segmentation_fault(self, locally_biased):
|
||||
# test that an excessive number of function evaluations
|
||||
# does not result in segmentation fault
|
||||
bounds = [(-5., 20.)] * 100
|
||||
result = direct(self.sphere, bounds, maxfun=10000000,
|
||||
maxiter=1000000, locally_biased=locally_biased)
|
||||
assert result is not None
|
||||
|
||||
@pytest.mark.parametrize("locally_biased", [True, False])
|
||||
def test_inf_fun(self, locally_biased):
|
||||
# test that an objective value of infinity does not crash DIRECT
|
||||
bounds = [(-5., 5.)] * 2
|
||||
result = direct(self.inf_fun, bounds,
|
||||
locally_biased=locally_biased)
|
||||
assert result is not None
|
||||
|
||||
@pytest.mark.parametrize("len_tol", [-1, 2])
|
||||
def test_len_tol_validation(self, len_tol):
|
||||
error_msg = "len_tol must be between 0 and 1."
|
||||
with pytest.raises(ValueError, match=error_msg):
|
||||
direct(self.styblinski_tang, self.bounds_stylinski_tang,
|
||||
len_tol=len_tol)
|
||||
|
||||
@pytest.mark.parametrize("vol_tol", [-1, 2])
|
||||
def test_vol_tol_validation(self, vol_tol):
|
||||
error_msg = "vol_tol must be between 0 and 1."
|
||||
with pytest.raises(ValueError, match=error_msg):
|
||||
direct(self.styblinski_tang, self.bounds_stylinski_tang,
|
||||
vol_tol=vol_tol)
|
||||
|
||||
@pytest.mark.parametrize("f_min_rtol", [-1, 2])
|
||||
def test_fmin_rtol_validation(self, f_min_rtol):
|
||||
error_msg = "f_min_rtol must be between 0 and 1."
|
||||
with pytest.raises(ValueError, match=error_msg):
|
||||
direct(self.styblinski_tang, self.bounds_stylinski_tang,
|
||||
f_min_rtol=f_min_rtol, f_min=0.)
|
||||
|
||||
@pytest.mark.parametrize("maxfun", [1.5, "string", (1, 2)])
|
||||
def test_maxfun_wrong_type(self, maxfun):
|
||||
error_msg = "maxfun must be of type int."
|
||||
with pytest.raises(ValueError, match=error_msg):
|
||||
direct(self.styblinski_tang, self.bounds_stylinski_tang,
|
||||
maxfun=maxfun)
|
||||
|
||||
@pytest.mark.parametrize("maxiter", [1.5, "string", (1, 2)])
|
||||
def test_maxiter_wrong_type(self, maxiter):
|
||||
error_msg = "maxiter must be of type int."
|
||||
with pytest.raises(ValueError, match=error_msg):
|
||||
direct(self.styblinski_tang, self.bounds_stylinski_tang,
|
||||
maxiter=maxiter)
|
||||
|
||||
def test_negative_maxiter(self):
|
||||
error_msg = "maxiter must be > 0."
|
||||
with pytest.raises(ValueError, match=error_msg):
|
||||
direct(self.styblinski_tang, self.bounds_stylinski_tang,
|
||||
maxiter=-1)
|
||||
|
||||
def test_negative_maxfun(self):
|
||||
error_msg = "maxfun must be > 0."
|
||||
with pytest.raises(ValueError, match=error_msg):
|
||||
direct(self.styblinski_tang, self.bounds_stylinski_tang,
|
||||
maxfun=-1)
|
||||
|
||||
@pytest.mark.parametrize("bounds", ["bounds", 2., 0])
|
||||
def test_invalid_bounds_type(self, bounds):
|
||||
error_msg = ("bounds must be a sequence or "
|
||||
"instance of Bounds class")
|
||||
with pytest.raises(ValueError, match=error_msg):
|
||||
direct(self.styblinski_tang, bounds)
|
||||
|
||||
@pytest.mark.parametrize("bounds",
|
||||
[Bounds([-1., -1], [-2, 1]),
|
||||
Bounds([-np.nan, -1], [-2, np.nan]),
|
||||
]
|
||||
)
|
||||
def test_incorrect_bounds(self, bounds):
|
||||
error_msg = 'Bounds are not consistent min < max'
|
||||
with pytest.raises(ValueError, match=error_msg):
|
||||
direct(self.styblinski_tang, bounds)
|
||||
|
||||
def test_inf_bounds(self):
|
||||
error_msg = 'Bounds must not be inf.'
|
||||
bounds = Bounds([-np.inf, -1], [-2, np.inf])
|
||||
with pytest.raises(ValueError, match=error_msg):
|
||||
direct(self.styblinski_tang, bounds)
|
||||
|
||||
@pytest.mark.parametrize("locally_biased", ["bias", [0, 0], 2.])
|
||||
def test_locally_biased_validation(self, locally_biased):
|
||||
error_msg = 'locally_biased must be True or False.'
|
||||
with pytest.raises(ValueError, match=error_msg):
|
||||
direct(self.styblinski_tang, self.bounds_stylinski_tang,
|
||||
locally_biased=locally_biased)
|
||||
208
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_hessian_update_strategy.py
vendored
Normal file
208
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_hessian_update_strategy.py
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
import numpy as np
|
||||
from copy import deepcopy
|
||||
from numpy.linalg import norm
|
||||
from numpy.testing import (TestCase, assert_array_almost_equal,
|
||||
assert_array_equal, assert_array_less)
|
||||
from scipy.optimize import (BFGS, SR1)
|
||||
|
||||
|
||||
class Rosenbrock:
|
||||
"""Rosenbrock function.
|
||||
|
||||
The following optimization problem:
|
||||
minimize sum(100.0*(x[1:] - x[:-1]**2.0)**2.0 + (1 - x[:-1])**2.0)
|
||||
"""
|
||||
|
||||
def __init__(self, n=2, random_state=0):
|
||||
rng = np.random.RandomState(random_state)
|
||||
self.x0 = rng.uniform(-1, 1, n)
|
||||
self.x_opt = np.ones(n)
|
||||
|
||||
def fun(self, x):
|
||||
x = np.asarray(x)
|
||||
r = np.sum(100.0 * (x[1:] - x[:-1]**2.0)**2.0 + (1 - x[:-1])**2.0,
|
||||
axis=0)
|
||||
return r
|
||||
|
||||
def grad(self, x):
|
||||
x = np.asarray(x)
|
||||
xm = x[1:-1]
|
||||
xm_m1 = x[:-2]
|
||||
xm_p1 = x[2:]
|
||||
der = np.zeros_like(x)
|
||||
der[1:-1] = (200 * (xm - xm_m1**2) -
|
||||
400 * (xm_p1 - xm**2) * xm - 2 * (1 - xm))
|
||||
der[0] = -400 * x[0] * (x[1] - x[0]**2) - 2 * (1 - x[0])
|
||||
der[-1] = 200 * (x[-1] - x[-2]**2)
|
||||
return der
|
||||
|
||||
def hess(self, x):
|
||||
x = np.atleast_1d(x)
|
||||
H = np.diag(-400 * x[:-1], 1) - np.diag(400 * x[:-1], -1)
|
||||
diagonal = np.zeros(len(x), dtype=x.dtype)
|
||||
diagonal[0] = 1200 * x[0]**2 - 400 * x[1] + 2
|
||||
diagonal[-1] = 200
|
||||
diagonal[1:-1] = 202 + 1200 * x[1:-1]**2 - 400 * x[2:]
|
||||
H = H + np.diag(diagonal)
|
||||
return H
|
||||
|
||||
|
||||
class TestHessianUpdateStrategy(TestCase):
|
||||
|
||||
def test_hessian_initialization(self):
|
||||
quasi_newton = (BFGS(), SR1())
|
||||
|
||||
for qn in quasi_newton:
|
||||
qn.initialize(5, 'hess')
|
||||
B = qn.get_matrix()
|
||||
|
||||
assert_array_equal(B, np.eye(5))
|
||||
|
||||
# For this list of points, it is known
|
||||
# that no exception occur during the
|
||||
# Hessian update. Hence no update is
|
||||
# skiped or damped.
|
||||
def test_rosenbrock_with_no_exception(self):
|
||||
# Define auxiliar problem
|
||||
prob = Rosenbrock(n=5)
|
||||
# Define iteration points
|
||||
x_list = [[0.0976270, 0.4303787, 0.2055267, 0.0897663, -0.15269040],
|
||||
[0.1847239, 0.0505757, 0.2123832, 0.0255081, 0.00083286],
|
||||
[0.2142498, -0.0188480, 0.0503822, 0.0347033, 0.03323606],
|
||||
[0.2071680, -0.0185071, 0.0341337, -0.0139298, 0.02881750],
|
||||
[0.1533055, -0.0322935, 0.0280418, -0.0083592, 0.01503699],
|
||||
[0.1382378, -0.0276671, 0.0266161, -0.0074060, 0.02801610],
|
||||
[0.1651957, -0.0049124, 0.0269665, -0.0040025, 0.02138184],
|
||||
[0.2354930, 0.0443711, 0.0173959, 0.0041872, 0.00794563],
|
||||
[0.4168118, 0.1433867, 0.0111714, 0.0126265, -0.00658537],
|
||||
[0.4681972, 0.2153273, 0.0225249, 0.0152704, -0.00463809],
|
||||
[0.6023068, 0.3346815, 0.0731108, 0.0186618, -0.00371541],
|
||||
[0.6415743, 0.3985468, 0.1324422, 0.0214160, -0.00062401],
|
||||
[0.7503690, 0.5447616, 0.2804541, 0.0539851, 0.00242230],
|
||||
[0.7452626, 0.5644594, 0.3324679, 0.0865153, 0.00454960],
|
||||
[0.8059782, 0.6586838, 0.4229577, 0.1452990, 0.00976702],
|
||||
[0.8549542, 0.7226562, 0.4991309, 0.2420093, 0.02772661],
|
||||
[0.8571332, 0.7285741, 0.5279076, 0.2824549, 0.06030276],
|
||||
[0.8835633, 0.7727077, 0.5957984, 0.3411303, 0.09652185],
|
||||
[0.9071558, 0.8299587, 0.6771400, 0.4402896, 0.17469338],
|
||||
[0.9190793, 0.8486480, 0.7163332, 0.5083780, 0.26107691],
|
||||
[0.9371223, 0.8762177, 0.7653702, 0.5773109, 0.32181041],
|
||||
[0.9554613, 0.9119893, 0.8282687, 0.6776178, 0.43162744],
|
||||
[0.9545744, 0.9099264, 0.8270244, 0.6822220, 0.45237623],
|
||||
[0.9688112, 0.9351710, 0.8730961, 0.7546601, 0.56622448],
|
||||
[0.9743227, 0.9491953, 0.9005150, 0.8086497, 0.64505437],
|
||||
[0.9807345, 0.9638853, 0.9283012, 0.8631675, 0.73812581],
|
||||
[0.9886746, 0.9777760, 0.9558950, 0.9123417, 0.82726553],
|
||||
[0.9899096, 0.9803828, 0.9615592, 0.9255600, 0.85822149],
|
||||
[0.9969510, 0.9935441, 0.9864657, 0.9726775, 0.94358663],
|
||||
[0.9979533, 0.9960274, 0.9921724, 0.9837415, 0.96626288],
|
||||
[0.9995981, 0.9989171, 0.9974178, 0.9949954, 0.99023356],
|
||||
[1.0002640, 1.0005088, 1.0010594, 1.0021161, 1.00386912],
|
||||
[0.9998903, 0.9998459, 0.9997795, 0.9995484, 0.99916305],
|
||||
[1.0000008, 0.9999905, 0.9999481, 0.9998903, 0.99978047],
|
||||
[1.0000004, 0.9999983, 1.0000001, 1.0000031, 1.00000297],
|
||||
[0.9999995, 1.0000003, 1.0000005, 1.0000001, 1.00000032],
|
||||
[0.9999999, 0.9999997, 0.9999994, 0.9999989, 0.99999786],
|
||||
[0.9999999, 0.9999999, 0.9999999, 0.9999999, 0.99999991]]
|
||||
# Get iteration points
|
||||
grad_list = [prob.grad(x) for x in x_list]
|
||||
delta_x = [np.array(x_list[i+1])-np.array(x_list[i])
|
||||
for i in range(len(x_list)-1)]
|
||||
delta_grad = [grad_list[i+1]-grad_list[i]
|
||||
for i in range(len(grad_list)-1)]
|
||||
# Check curvature condition
|
||||
for s, y in zip(delta_x, delta_grad):
|
||||
if np.dot(s, y) <= 0:
|
||||
raise ArithmeticError()
|
||||
# Define QuasiNewton update
|
||||
for quasi_newton in (BFGS(init_scale=1, min_curvature=1e-4),
|
||||
SR1(init_scale=1)):
|
||||
hess = deepcopy(quasi_newton)
|
||||
inv_hess = deepcopy(quasi_newton)
|
||||
hess.initialize(len(x_list[0]), 'hess')
|
||||
inv_hess.initialize(len(x_list[0]), 'inv_hess')
|
||||
# Compare the hessian and its inverse
|
||||
for s, y in zip(delta_x, delta_grad):
|
||||
hess.update(s, y)
|
||||
inv_hess.update(s, y)
|
||||
B = hess.get_matrix()
|
||||
H = inv_hess.get_matrix()
|
||||
assert_array_almost_equal(np.linalg.inv(B), H, decimal=10)
|
||||
B_true = prob.hess(x_list[len(delta_x)])
|
||||
assert_array_less(norm(B - B_true)/norm(B_true), 0.1)
|
||||
|
||||
def test_SR1_skip_update(self):
|
||||
# Define auxiliary problem
|
||||
prob = Rosenbrock(n=5)
|
||||
# Define iteration points
|
||||
x_list = [[0.0976270, 0.4303787, 0.2055267, 0.0897663, -0.15269040],
|
||||
[0.1847239, 0.0505757, 0.2123832, 0.0255081, 0.00083286],
|
||||
[0.2142498, -0.0188480, 0.0503822, 0.0347033, 0.03323606],
|
||||
[0.2071680, -0.0185071, 0.0341337, -0.0139298, 0.02881750],
|
||||
[0.1533055, -0.0322935, 0.0280418, -0.0083592, 0.01503699],
|
||||
[0.1382378, -0.0276671, 0.0266161, -0.0074060, 0.02801610],
|
||||
[0.1651957, -0.0049124, 0.0269665, -0.0040025, 0.02138184],
|
||||
[0.2354930, 0.0443711, 0.0173959, 0.0041872, 0.00794563],
|
||||
[0.4168118, 0.1433867, 0.0111714, 0.0126265, -0.00658537],
|
||||
[0.4681972, 0.2153273, 0.0225249, 0.0152704, -0.00463809],
|
||||
[0.6023068, 0.3346815, 0.0731108, 0.0186618, -0.00371541],
|
||||
[0.6415743, 0.3985468, 0.1324422, 0.0214160, -0.00062401],
|
||||
[0.7503690, 0.5447616, 0.2804541, 0.0539851, 0.00242230],
|
||||
[0.7452626, 0.5644594, 0.3324679, 0.0865153, 0.00454960],
|
||||
[0.8059782, 0.6586838, 0.4229577, 0.1452990, 0.00976702],
|
||||
[0.8549542, 0.7226562, 0.4991309, 0.2420093, 0.02772661],
|
||||
[0.8571332, 0.7285741, 0.5279076, 0.2824549, 0.06030276],
|
||||
[0.8835633, 0.7727077, 0.5957984, 0.3411303, 0.09652185],
|
||||
[0.9071558, 0.8299587, 0.6771400, 0.4402896, 0.17469338]]
|
||||
# Get iteration points
|
||||
grad_list = [prob.grad(x) for x in x_list]
|
||||
delta_x = [np.array(x_list[i+1])-np.array(x_list[i])
|
||||
for i in range(len(x_list)-1)]
|
||||
delta_grad = [grad_list[i+1]-grad_list[i]
|
||||
for i in range(len(grad_list)-1)]
|
||||
hess = SR1(init_scale=1, min_denominator=1e-2)
|
||||
hess.initialize(len(x_list[0]), 'hess')
|
||||
# Compare the Hessian and its inverse
|
||||
for i in range(len(delta_x)-1):
|
||||
s = delta_x[i]
|
||||
y = delta_grad[i]
|
||||
hess.update(s, y)
|
||||
# Test skip update
|
||||
B = np.copy(hess.get_matrix())
|
||||
s = delta_x[17]
|
||||
y = delta_grad[17]
|
||||
hess.update(s, y)
|
||||
B_updated = np.copy(hess.get_matrix())
|
||||
assert_array_equal(B, B_updated)
|
||||
|
||||
def test_BFGS_skip_update(self):
|
||||
# Define auxiliar problem
|
||||
prob = Rosenbrock(n=5)
|
||||
# Define iteration points
|
||||
x_list = [[0.0976270, 0.4303787, 0.2055267, 0.0897663, -0.15269040],
|
||||
[0.1847239, 0.0505757, 0.2123832, 0.0255081, 0.00083286],
|
||||
[0.2142498, -0.0188480, 0.0503822, 0.0347033, 0.03323606],
|
||||
[0.2071680, -0.0185071, 0.0341337, -0.0139298, 0.02881750],
|
||||
[0.1533055, -0.0322935, 0.0280418, -0.0083592, 0.01503699],
|
||||
[0.1382378, -0.0276671, 0.0266161, -0.0074060, 0.02801610],
|
||||
[0.1651957, -0.0049124, 0.0269665, -0.0040025, 0.02138184]]
|
||||
# Get iteration points
|
||||
grad_list = [prob.grad(x) for x in x_list]
|
||||
delta_x = [np.array(x_list[i+1])-np.array(x_list[i])
|
||||
for i in range(len(x_list)-1)]
|
||||
delta_grad = [grad_list[i+1]-grad_list[i]
|
||||
for i in range(len(grad_list)-1)]
|
||||
hess = BFGS(init_scale=1, min_curvature=10)
|
||||
hess.initialize(len(x_list[0]), 'hess')
|
||||
# Compare the Hessian and its inverse
|
||||
for i in range(len(delta_x)-1):
|
||||
s = delta_x[i]
|
||||
y = delta_grad[i]
|
||||
hess.update(s, y)
|
||||
# Test skip update
|
||||
B = np.copy(hess.get_matrix())
|
||||
s = delta_x[5]
|
||||
y = delta_grad[5]
|
||||
hess.update(s, y)
|
||||
B_updated = np.copy(hess.get_matrix())
|
||||
assert_array_equal(B, B_updated)
|
||||
43
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_lbfgsb_hessinv.py
vendored
Normal file
43
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_lbfgsb_hessinv.py
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
import scipy.linalg
|
||||
from scipy.optimize import minimize
|
||||
|
||||
|
||||
def test_1():
|
||||
def f(x):
|
||||
return x**4, 4*x**3
|
||||
|
||||
for gtol in [1e-8, 1e-12, 1e-20]:
|
||||
for maxcor in range(20, 35):
|
||||
result = minimize(fun=f, jac=True, method='L-BFGS-B', x0=20,
|
||||
options={'gtol': gtol, 'maxcor': maxcor})
|
||||
|
||||
H1 = result.hess_inv(np.array([1])).reshape(1,1)
|
||||
H2 = result.hess_inv.todense()
|
||||
|
||||
assert_allclose(H1, H2)
|
||||
|
||||
|
||||
def test_2():
|
||||
H0 = [[3, 0], [1, 2]]
|
||||
|
||||
def f(x):
|
||||
return np.dot(x, np.dot(scipy.linalg.inv(H0), x))
|
||||
|
||||
result1 = minimize(fun=f, method='L-BFGS-B', x0=[10, 20])
|
||||
result2 = minimize(fun=f, method='BFGS', x0=[10, 20])
|
||||
|
||||
H1 = result1.hess_inv.todense()
|
||||
|
||||
H2 = np.vstack((
|
||||
result1.hess_inv(np.array([1, 0])),
|
||||
result1.hess_inv(np.array([0, 1]))))
|
||||
|
||||
assert_allclose(
|
||||
result1.hess_inv(np.array([1, 0]).reshape(2,1)).reshape(-1),
|
||||
result1.hess_inv(np.array([1, 0])))
|
||||
assert_allclose(H1, H2)
|
||||
assert_allclose(H1, result2.hess_inv, rtol=1e-2, atol=0.03)
|
||||
|
||||
|
||||
116
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_lbfgsb_setulb.py
vendored
Normal file
116
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_lbfgsb_setulb.py
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
import numpy as np
|
||||
from scipy.optimize import _lbfgsb
|
||||
|
||||
|
||||
def objfun(x):
|
||||
"""simplified objective func to test lbfgsb bound violation"""
|
||||
x0 = [0.8750000000000278,
|
||||
0.7500000000000153,
|
||||
0.9499999999999722,
|
||||
0.8214285714285992,
|
||||
0.6363636363636085]
|
||||
x1 = [1.0, 0.0, 1.0, 0.0, 0.0]
|
||||
x2 = [1.0,
|
||||
0.0,
|
||||
0.9889733043149325,
|
||||
0.0,
|
||||
0.026353554421041155]
|
||||
x3 = [1.0,
|
||||
0.0,
|
||||
0.9889917442915558,
|
||||
0.0,
|
||||
0.020341986743231205]
|
||||
|
||||
f0 = 5163.647901211178
|
||||
f1 = 5149.8181642072905
|
||||
f2 = 5149.379332309634
|
||||
f3 = 5149.374490771297
|
||||
|
||||
g0 = np.array([-0.5934820547965749,
|
||||
1.6251549718258351,
|
||||
-71.99168459202559,
|
||||
5.346636965797545,
|
||||
37.10732723092604])
|
||||
g1 = np.array([-0.43295349282641515,
|
||||
1.008607936794592,
|
||||
18.223666726602975,
|
||||
31.927010036981997,
|
||||
-19.667512518739386])
|
||||
g2 = np.array([-0.4699874455100256,
|
||||
0.9466285353668347,
|
||||
-0.016874360242016825,
|
||||
48.44999161133457,
|
||||
5.819631620590712])
|
||||
g3 = np.array([-0.46970678696829116,
|
||||
0.9612719312174818,
|
||||
0.006129809488833699,
|
||||
48.43557729419473,
|
||||
6.005481418498221])
|
||||
|
||||
if np.allclose(x, x0):
|
||||
f = f0
|
||||
g = g0
|
||||
elif np.allclose(x, x1):
|
||||
f = f1
|
||||
g = g1
|
||||
elif np.allclose(x, x2):
|
||||
f = f2
|
||||
g = g2
|
||||
elif np.allclose(x, x3):
|
||||
f = f3
|
||||
g = g3
|
||||
else:
|
||||
raise ValueError(
|
||||
'Simplified objective function not defined '
|
||||
'at requested point')
|
||||
return (np.copy(f), np.copy(g))
|
||||
|
||||
|
||||
def test_setulb_floatround():
|
||||
"""test if setulb() violates bounds
|
||||
|
||||
checks for violation due to floating point rounding error
|
||||
"""
|
||||
|
||||
n = 5
|
||||
m = 10
|
||||
factr = 1e7
|
||||
pgtol = 1e-5
|
||||
maxls = 20
|
||||
iprint = -1
|
||||
nbd = np.full((n,), 2)
|
||||
low_bnd = np.zeros(n, np.float64)
|
||||
upper_bnd = np.ones(n, np.float64)
|
||||
|
||||
x0 = np.array(
|
||||
[0.8750000000000278,
|
||||
0.7500000000000153,
|
||||
0.9499999999999722,
|
||||
0.8214285714285992,
|
||||
0.6363636363636085])
|
||||
x = np.copy(x0)
|
||||
|
||||
f = np.array(0.0, np.float64)
|
||||
g = np.zeros(n, np.float64)
|
||||
|
||||
fortran_int = _lbfgsb.types.intvar.dtype
|
||||
|
||||
wa = np.zeros(2*m*n + 5*n + 11*m*m + 8*m, np.float64)
|
||||
iwa = np.zeros(3*n, fortran_int)
|
||||
task = np.zeros(1, 'S60')
|
||||
csave = np.zeros(1, 'S60')
|
||||
lsave = np.zeros(4, fortran_int)
|
||||
isave = np.zeros(44, fortran_int)
|
||||
dsave = np.zeros(29, np.float64)
|
||||
|
||||
task[:] = b'START'
|
||||
|
||||
for n_iter in range(7): # 7 steps required to reproduce error
|
||||
f, g = objfun(x)
|
||||
|
||||
_lbfgsb.setulb(m, x, low_bnd, upper_bnd, nbd, f, g, factr,
|
||||
pgtol, wa, iwa, task, iprint, csave, lsave,
|
||||
isave, dsave, maxls)
|
||||
|
||||
assert (x <= upper_bnd).all() and (x >= low_bnd).all(), (
|
||||
"_lbfgsb.setulb() stepped to a point outside of the bounds")
|
||||
811
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_least_squares.py
vendored
Normal file
811
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_least_squares.py
vendored
Normal file
@@ -0,0 +1,811 @@
|
||||
from itertools import product
|
||||
|
||||
import numpy as np
|
||||
from numpy.linalg import norm
|
||||
from numpy.testing import (assert_, assert_allclose,
|
||||
assert_equal, suppress_warnings)
|
||||
from pytest import raises as assert_raises
|
||||
from scipy.sparse import issparse, lil_matrix
|
||||
from scipy.sparse.linalg import aslinearoperator
|
||||
|
||||
from scipy.optimize import least_squares, Bounds
|
||||
from scipy.optimize._lsq.least_squares import IMPLEMENTED_LOSSES
|
||||
from scipy.optimize._lsq.common import EPS, make_strictly_feasible
|
||||
|
||||
|
||||
def fun_trivial(x, a=0):
|
||||
return (x - a)**2 + 5.0
|
||||
|
||||
|
||||
def jac_trivial(x, a=0.0):
|
||||
return 2 * (x - a)
|
||||
|
||||
|
||||
def fun_2d_trivial(x):
|
||||
return np.array([x[0], x[1]])
|
||||
|
||||
|
||||
def jac_2d_trivial(x):
|
||||
return np.identity(2)
|
||||
|
||||
|
||||
def fun_rosenbrock(x):
|
||||
return np.array([10 * (x[1] - x[0]**2), (1 - x[0])])
|
||||
|
||||
|
||||
def jac_rosenbrock(x):
|
||||
return np.array([
|
||||
[-20 * x[0], 10],
|
||||
[-1, 0]
|
||||
])
|
||||
|
||||
|
||||
def jac_rosenbrock_bad_dim(x):
|
||||
return np.array([
|
||||
[-20 * x[0], 10],
|
||||
[-1, 0],
|
||||
[0.0, 0.0]
|
||||
])
|
||||
|
||||
|
||||
def fun_rosenbrock_cropped(x):
|
||||
return fun_rosenbrock(x)[0]
|
||||
|
||||
|
||||
def jac_rosenbrock_cropped(x):
|
||||
return jac_rosenbrock(x)[0]
|
||||
|
||||
|
||||
# When x is 1-D array, return is 2-D array.
|
||||
def fun_wrong_dimensions(x):
|
||||
return np.array([x, x**2, x**3])
|
||||
|
||||
|
||||
def jac_wrong_dimensions(x, a=0.0):
|
||||
return np.atleast_3d(jac_trivial(x, a=a))
|
||||
|
||||
|
||||
def fun_bvp(x):
|
||||
n = int(np.sqrt(x.shape[0]))
|
||||
u = np.zeros((n + 2, n + 2))
|
||||
x = x.reshape((n, n))
|
||||
u[1:-1, 1:-1] = x
|
||||
y = u[:-2, 1:-1] + u[2:, 1:-1] + u[1:-1, :-2] + u[1:-1, 2:] - 4 * x + x**3
|
||||
return y.ravel()
|
||||
|
||||
|
||||
class BroydenTridiagonal:
|
||||
def __init__(self, n=100, mode='sparse'):
|
||||
np.random.seed(0)
|
||||
|
||||
self.n = n
|
||||
|
||||
self.x0 = -np.ones(n)
|
||||
self.lb = np.linspace(-2, -1.5, n)
|
||||
self.ub = np.linspace(-0.8, 0.0, n)
|
||||
|
||||
self.lb += 0.1 * np.random.randn(n)
|
||||
self.ub += 0.1 * np.random.randn(n)
|
||||
|
||||
self.x0 += 0.1 * np.random.randn(n)
|
||||
self.x0 = make_strictly_feasible(self.x0, self.lb, self.ub)
|
||||
|
||||
if mode == 'sparse':
|
||||
self.sparsity = lil_matrix((n, n), dtype=int)
|
||||
i = np.arange(n)
|
||||
self.sparsity[i, i] = 1
|
||||
i = np.arange(1, n)
|
||||
self.sparsity[i, i - 1] = 1
|
||||
i = np.arange(n - 1)
|
||||
self.sparsity[i, i + 1] = 1
|
||||
|
||||
self.jac = self._jac
|
||||
elif mode == 'operator':
|
||||
self.jac = lambda x: aslinearoperator(self._jac(x))
|
||||
elif mode == 'dense':
|
||||
self.sparsity = None
|
||||
self.jac = lambda x: self._jac(x).toarray()
|
||||
else:
|
||||
assert_(False)
|
||||
|
||||
def fun(self, x):
|
||||
f = (3 - x) * x + 1
|
||||
f[1:] -= x[:-1]
|
||||
f[:-1] -= 2 * x[1:]
|
||||
return f
|
||||
|
||||
def _jac(self, x):
|
||||
J = lil_matrix((self.n, self.n))
|
||||
i = np.arange(self.n)
|
||||
J[i, i] = 3 - 2 * x
|
||||
i = np.arange(1, self.n)
|
||||
J[i, i - 1] = -1
|
||||
i = np.arange(self.n - 1)
|
||||
J[i, i + 1] = -2
|
||||
return J
|
||||
|
||||
|
||||
class ExponentialFittingProblem:
|
||||
"""Provide data and function for exponential fitting in the form
|
||||
y = a + exp(b * x) + noise."""
|
||||
|
||||
def __init__(self, a, b, noise, n_outliers=1, x_range=(-1, 1),
|
||||
n_points=11, random_seed=None):
|
||||
np.random.seed(random_seed)
|
||||
self.m = n_points
|
||||
self.n = 2
|
||||
|
||||
self.p0 = np.zeros(2)
|
||||
self.x = np.linspace(x_range[0], x_range[1], n_points)
|
||||
|
||||
self.y = a + np.exp(b * self.x)
|
||||
self.y += noise * np.random.randn(self.m)
|
||||
|
||||
outliers = np.random.randint(0, self.m, n_outliers)
|
||||
self.y[outliers] += 50 * noise * np.random.rand(n_outliers)
|
||||
|
||||
self.p_opt = np.array([a, b])
|
||||
|
||||
def fun(self, p):
|
||||
return p[0] + np.exp(p[1] * self.x) - self.y
|
||||
|
||||
def jac(self, p):
|
||||
J = np.empty((self.m, self.n))
|
||||
J[:, 0] = 1
|
||||
J[:, 1] = self.x * np.exp(p[1] * self.x)
|
||||
return J
|
||||
|
||||
|
||||
def cubic_soft_l1(z):
|
||||
rho = np.empty((3, z.size))
|
||||
|
||||
t = 1 + z
|
||||
rho[0] = 3 * (t**(1/3) - 1)
|
||||
rho[1] = t ** (-2/3)
|
||||
rho[2] = -2/3 * t**(-5/3)
|
||||
|
||||
return rho
|
||||
|
||||
|
||||
LOSSES = list(IMPLEMENTED_LOSSES.keys()) + [cubic_soft_l1]
|
||||
|
||||
|
||||
class BaseMixin:
|
||||
def test_basic(self):
|
||||
# Test that the basic calling sequence works.
|
||||
res = least_squares(fun_trivial, 2., method=self.method)
|
||||
assert_allclose(res.x, 0, atol=1e-4)
|
||||
assert_allclose(res.fun, fun_trivial(res.x))
|
||||
|
||||
def test_args_kwargs(self):
|
||||
# Test that args and kwargs are passed correctly to the functions.
|
||||
a = 3.0
|
||||
for jac in ['2-point', '3-point', 'cs', jac_trivial]:
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(UserWarning,
|
||||
"jac='(3-point|cs)' works equivalently to '2-point' for method='lm'")
|
||||
res = least_squares(fun_trivial, 2.0, jac, args=(a,),
|
||||
method=self.method)
|
||||
res1 = least_squares(fun_trivial, 2.0, jac, kwargs={'a': a},
|
||||
method=self.method)
|
||||
|
||||
assert_allclose(res.x, a, rtol=1e-4)
|
||||
assert_allclose(res1.x, a, rtol=1e-4)
|
||||
|
||||
assert_raises(TypeError, least_squares, fun_trivial, 2.0,
|
||||
args=(3, 4,), method=self.method)
|
||||
assert_raises(TypeError, least_squares, fun_trivial, 2.0,
|
||||
kwargs={'kaboom': 3}, method=self.method)
|
||||
|
||||
def test_jac_options(self):
|
||||
for jac in ['2-point', '3-point', 'cs', jac_trivial]:
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(UserWarning,
|
||||
"jac='(3-point|cs)' works equivalently to '2-point' for method='lm'")
|
||||
res = least_squares(fun_trivial, 2.0, jac, method=self.method)
|
||||
assert_allclose(res.x, 0, atol=1e-4)
|
||||
|
||||
assert_raises(ValueError, least_squares, fun_trivial, 2.0, jac='oops',
|
||||
method=self.method)
|
||||
|
||||
def test_nfev_options(self):
|
||||
for max_nfev in [None, 20]:
|
||||
res = least_squares(fun_trivial, 2.0, max_nfev=max_nfev,
|
||||
method=self.method)
|
||||
assert_allclose(res.x, 0, atol=1e-4)
|
||||
|
||||
def test_x_scale_options(self):
|
||||
for x_scale in [1.0, np.array([0.5]), 'jac']:
|
||||
res = least_squares(fun_trivial, 2.0, x_scale=x_scale)
|
||||
assert_allclose(res.x, 0)
|
||||
assert_raises(ValueError, least_squares, fun_trivial,
|
||||
2.0, x_scale='auto', method=self.method)
|
||||
assert_raises(ValueError, least_squares, fun_trivial,
|
||||
2.0, x_scale=-1.0, method=self.method)
|
||||
assert_raises(ValueError, least_squares, fun_trivial,
|
||||
2.0, x_scale=None, method=self.method)
|
||||
assert_raises(ValueError, least_squares, fun_trivial,
|
||||
2.0, x_scale=1.0+2.0j, method=self.method)
|
||||
|
||||
def test_diff_step(self):
|
||||
# res1 and res2 should be equivalent.
|
||||
# res2 and res3 should be different.
|
||||
res1 = least_squares(fun_trivial, 2.0, diff_step=1e-1,
|
||||
method=self.method)
|
||||
res2 = least_squares(fun_trivial, 2.0, diff_step=-1e-1,
|
||||
method=self.method)
|
||||
res3 = least_squares(fun_trivial, 2.0,
|
||||
diff_step=None, method=self.method)
|
||||
assert_allclose(res1.x, 0, atol=1e-4)
|
||||
assert_allclose(res2.x, 0, atol=1e-4)
|
||||
assert_allclose(res3.x, 0, atol=1e-4)
|
||||
assert_equal(res1.x, res2.x)
|
||||
assert_equal(res1.nfev, res2.nfev)
|
||||
|
||||
def test_incorrect_options_usage(self):
|
||||
assert_raises(TypeError, least_squares, fun_trivial, 2.0,
|
||||
method=self.method, options={'no_such_option': 100})
|
||||
assert_raises(TypeError, least_squares, fun_trivial, 2.0,
|
||||
method=self.method, options={'max_nfev': 100})
|
||||
|
||||
def test_full_result(self):
|
||||
# MINPACK doesn't work very well with factor=100 on this problem,
|
||||
# thus using low 'atol'.
|
||||
res = least_squares(fun_trivial, 2.0, method=self.method)
|
||||
assert_allclose(res.x, 0, atol=1e-4)
|
||||
assert_allclose(res.cost, 12.5)
|
||||
assert_allclose(res.fun, 5)
|
||||
assert_allclose(res.jac, 0, atol=1e-4)
|
||||
assert_allclose(res.grad, 0, atol=1e-2)
|
||||
assert_allclose(res.optimality, 0, atol=1e-2)
|
||||
assert_equal(res.active_mask, 0)
|
||||
if self.method == 'lm':
|
||||
assert_(res.nfev < 30)
|
||||
assert_(res.njev is None)
|
||||
else:
|
||||
assert_(res.nfev < 10)
|
||||
assert_(res.njev < 10)
|
||||
assert_(res.status > 0)
|
||||
assert_(res.success)
|
||||
|
||||
def test_full_result_single_fev(self):
|
||||
# MINPACK checks the number of nfev after the iteration,
|
||||
# so it's hard to tell what he is going to compute.
|
||||
if self.method == 'lm':
|
||||
return
|
||||
|
||||
res = least_squares(fun_trivial, 2.0, method=self.method,
|
||||
max_nfev=1)
|
||||
assert_equal(res.x, np.array([2]))
|
||||
assert_equal(res.cost, 40.5)
|
||||
assert_equal(res.fun, np.array([9]))
|
||||
assert_equal(res.jac, np.array([[4]]))
|
||||
assert_equal(res.grad, np.array([36]))
|
||||
assert_equal(res.optimality, 36)
|
||||
assert_equal(res.active_mask, np.array([0]))
|
||||
assert_equal(res.nfev, 1)
|
||||
assert_equal(res.njev, 1)
|
||||
assert_equal(res.status, 0)
|
||||
assert_equal(res.success, 0)
|
||||
|
||||
def test_rosenbrock(self):
|
||||
x0 = [-2, 1]
|
||||
x_opt = [1, 1]
|
||||
for jac, x_scale, tr_solver in product(
|
||||
['2-point', '3-point', 'cs', jac_rosenbrock],
|
||||
[1.0, np.array([1.0, 0.2]), 'jac'],
|
||||
['exact', 'lsmr']):
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(UserWarning,
|
||||
"jac='(3-point|cs)' works equivalently to '2-point' for method='lm'")
|
||||
res = least_squares(fun_rosenbrock, x0, jac, x_scale=x_scale,
|
||||
tr_solver=tr_solver, method=self.method)
|
||||
assert_allclose(res.x, x_opt)
|
||||
|
||||
def test_rosenbrock_cropped(self):
|
||||
x0 = [-2, 1]
|
||||
if self.method == 'lm':
|
||||
assert_raises(ValueError, least_squares, fun_rosenbrock_cropped,
|
||||
x0, method='lm')
|
||||
else:
|
||||
for jac, x_scale, tr_solver in product(
|
||||
['2-point', '3-point', 'cs', jac_rosenbrock_cropped],
|
||||
[1.0, np.array([1.0, 0.2]), 'jac'],
|
||||
['exact', 'lsmr']):
|
||||
res = least_squares(
|
||||
fun_rosenbrock_cropped, x0, jac, x_scale=x_scale,
|
||||
tr_solver=tr_solver, method=self.method)
|
||||
assert_allclose(res.cost, 0, atol=1e-14)
|
||||
|
||||
def test_fun_wrong_dimensions(self):
|
||||
assert_raises(ValueError, least_squares, fun_wrong_dimensions,
|
||||
2.0, method=self.method)
|
||||
|
||||
def test_jac_wrong_dimensions(self):
|
||||
assert_raises(ValueError, least_squares, fun_trivial,
|
||||
2.0, jac_wrong_dimensions, method=self.method)
|
||||
|
||||
def test_fun_and_jac_inconsistent_dimensions(self):
|
||||
x0 = [1, 2]
|
||||
assert_raises(ValueError, least_squares, fun_rosenbrock, x0,
|
||||
jac_rosenbrock_bad_dim, method=self.method)
|
||||
|
||||
def test_x0_multidimensional(self):
|
||||
x0 = np.ones(4).reshape(2, 2)
|
||||
assert_raises(ValueError, least_squares, fun_trivial, x0,
|
||||
method=self.method)
|
||||
|
||||
def test_x0_complex_scalar(self):
|
||||
x0 = 2.0 + 0.0*1j
|
||||
assert_raises(ValueError, least_squares, fun_trivial, x0,
|
||||
method=self.method)
|
||||
|
||||
def test_x0_complex_array(self):
|
||||
x0 = [1.0, 2.0 + 0.0*1j]
|
||||
assert_raises(ValueError, least_squares, fun_trivial, x0,
|
||||
method=self.method)
|
||||
|
||||
def test_bvp(self):
|
||||
# This test was introduced with fix #5556. It turned out that
|
||||
# dogbox solver had a bug with trust-region radius update, which
|
||||
# could block its progress and create an infinite loop. And this
|
||||
# discrete boundary value problem is the one which triggers it.
|
||||
n = 10
|
||||
x0 = np.ones(n**2)
|
||||
if self.method == 'lm':
|
||||
max_nfev = 5000 # To account for Jacobian estimation.
|
||||
else:
|
||||
max_nfev = 100
|
||||
res = least_squares(fun_bvp, x0, ftol=1e-2, method=self.method,
|
||||
max_nfev=max_nfev)
|
||||
|
||||
assert_(res.nfev < max_nfev)
|
||||
assert_(res.cost < 0.5)
|
||||
|
||||
def test_error_raised_when_all_tolerances_below_eps(self):
|
||||
# Test that all 0 tolerances are not allowed.
|
||||
assert_raises(ValueError, least_squares, fun_trivial, 2.0,
|
||||
method=self.method, ftol=None, xtol=None, gtol=None)
|
||||
|
||||
def test_convergence_with_only_one_tolerance_enabled(self):
|
||||
if self.method == 'lm':
|
||||
return # should not do test
|
||||
x0 = [-2, 1]
|
||||
x_opt = [1, 1]
|
||||
for ftol, xtol, gtol in [(1e-8, None, None),
|
||||
(None, 1e-8, None),
|
||||
(None, None, 1e-8)]:
|
||||
res = least_squares(fun_rosenbrock, x0, jac=jac_rosenbrock,
|
||||
ftol=ftol, gtol=gtol, xtol=xtol,
|
||||
method=self.method)
|
||||
assert_allclose(res.x, x_opt)
|
||||
|
||||
|
||||
class BoundsMixin:
|
||||
def test_inconsistent(self):
|
||||
assert_raises(ValueError, least_squares, fun_trivial, 2.0,
|
||||
bounds=(10.0, 0.0), method=self.method)
|
||||
|
||||
def test_infeasible(self):
|
||||
assert_raises(ValueError, least_squares, fun_trivial, 2.0,
|
||||
bounds=(3., 4), method=self.method)
|
||||
|
||||
def test_wrong_number(self):
|
||||
assert_raises(ValueError, least_squares, fun_trivial, 2.,
|
||||
bounds=(1., 2, 3), method=self.method)
|
||||
|
||||
def test_inconsistent_shape(self):
|
||||
assert_raises(ValueError, least_squares, fun_trivial, 2.0,
|
||||
bounds=(1.0, [2.0, 3.0]), method=self.method)
|
||||
# 1-D array wont't be broadcasted
|
||||
assert_raises(ValueError, least_squares, fun_rosenbrock, [1.0, 2.0],
|
||||
bounds=([0.0], [3.0, 4.0]), method=self.method)
|
||||
|
||||
def test_in_bounds(self):
|
||||
for jac in ['2-point', '3-point', 'cs', jac_trivial]:
|
||||
res = least_squares(fun_trivial, 2.0, jac=jac,
|
||||
bounds=(-1.0, 3.0), method=self.method)
|
||||
assert_allclose(res.x, 0.0, atol=1e-4)
|
||||
assert_equal(res.active_mask, [0])
|
||||
assert_(-1 <= res.x <= 3)
|
||||
res = least_squares(fun_trivial, 2.0, jac=jac,
|
||||
bounds=(0.5, 3.0), method=self.method)
|
||||
assert_allclose(res.x, 0.5, atol=1e-4)
|
||||
assert_equal(res.active_mask, [-1])
|
||||
assert_(0.5 <= res.x <= 3)
|
||||
|
||||
def test_bounds_shape(self):
|
||||
def get_bounds_direct(lb, ub):
|
||||
return lb, ub
|
||||
|
||||
def get_bounds_instances(lb, ub):
|
||||
return Bounds(lb, ub)
|
||||
|
||||
for jac in ['2-point', '3-point', 'cs', jac_2d_trivial]:
|
||||
for bounds_func in [get_bounds_direct, get_bounds_instances]:
|
||||
x0 = [1.0, 1.0]
|
||||
res = least_squares(fun_2d_trivial, x0, jac=jac)
|
||||
assert_allclose(res.x, [0.0, 0.0])
|
||||
res = least_squares(fun_2d_trivial, x0, jac=jac,
|
||||
bounds=bounds_func(0.5, [2.0, 2.0]),
|
||||
method=self.method)
|
||||
assert_allclose(res.x, [0.5, 0.5])
|
||||
res = least_squares(fun_2d_trivial, x0, jac=jac,
|
||||
bounds=bounds_func([0.3, 0.2], 3.0),
|
||||
method=self.method)
|
||||
assert_allclose(res.x, [0.3, 0.2])
|
||||
res = least_squares(
|
||||
fun_2d_trivial, x0, jac=jac,
|
||||
bounds=bounds_func([-1, 0.5], [1.0, 3.0]),
|
||||
method=self.method)
|
||||
assert_allclose(res.x, [0.0, 0.5], atol=1e-5)
|
||||
|
||||
def test_bounds_instances(self):
|
||||
res = least_squares(fun_trivial, 0.5, bounds=Bounds())
|
||||
assert_allclose(res.x, 0.0, atol=1e-4)
|
||||
|
||||
res = least_squares(fun_trivial, 3.0, bounds=Bounds(lb=1.0))
|
||||
assert_allclose(res.x, 1.0, atol=1e-4)
|
||||
|
||||
res = least_squares(fun_trivial, 0.5, bounds=Bounds(lb=-1.0, ub=1.0))
|
||||
assert_allclose(res.x, 0.0, atol=1e-4)
|
||||
|
||||
res = least_squares(fun_trivial, -3.0, bounds=Bounds(ub=-1.0))
|
||||
assert_allclose(res.x, -1.0, atol=1e-4)
|
||||
|
||||
res = least_squares(fun_2d_trivial, [0.5, 0.5],
|
||||
bounds=Bounds(lb=[-1.0, -1.0], ub=1.0))
|
||||
assert_allclose(res.x, [0.0, 0.0], atol=1e-5)
|
||||
|
||||
res = least_squares(fun_2d_trivial, [0.5, 0.5],
|
||||
bounds=Bounds(lb=[0.1, 0.1]))
|
||||
assert_allclose(res.x, [0.1, 0.1], atol=1e-5)
|
||||
|
||||
def test_rosenbrock_bounds(self):
|
||||
x0_1 = np.array([-2.0, 1.0])
|
||||
x0_2 = np.array([2.0, 2.0])
|
||||
x0_3 = np.array([-2.0, 2.0])
|
||||
x0_4 = np.array([0.0, 2.0])
|
||||
x0_5 = np.array([-1.2, 1.0])
|
||||
problems = [
|
||||
(x0_1, ([-np.inf, -1.5], np.inf)),
|
||||
(x0_2, ([-np.inf, 1.5], np.inf)),
|
||||
(x0_3, ([-np.inf, 1.5], np.inf)),
|
||||
(x0_4, ([-np.inf, 1.5], [1.0, np.inf])),
|
||||
(x0_2, ([1.0, 1.5], [3.0, 3.0])),
|
||||
(x0_5, ([-50.0, 0.0], [0.5, 100]))
|
||||
]
|
||||
for x0, bounds in problems:
|
||||
for jac, x_scale, tr_solver in product(
|
||||
['2-point', '3-point', 'cs', jac_rosenbrock],
|
||||
[1.0, [1.0, 0.5], 'jac'],
|
||||
['exact', 'lsmr']):
|
||||
res = least_squares(fun_rosenbrock, x0, jac, bounds,
|
||||
x_scale=x_scale, tr_solver=tr_solver,
|
||||
method=self.method)
|
||||
assert_allclose(res.optimality, 0.0, atol=1e-5)
|
||||
|
||||
|
||||
class SparseMixin:
|
||||
def test_exact_tr_solver(self):
|
||||
p = BroydenTridiagonal()
|
||||
assert_raises(ValueError, least_squares, p.fun, p.x0, p.jac,
|
||||
tr_solver='exact', method=self.method)
|
||||
assert_raises(ValueError, least_squares, p.fun, p.x0,
|
||||
tr_solver='exact', jac_sparsity=p.sparsity,
|
||||
method=self.method)
|
||||
|
||||
def test_equivalence(self):
|
||||
sparse = BroydenTridiagonal(mode='sparse')
|
||||
dense = BroydenTridiagonal(mode='dense')
|
||||
res_sparse = least_squares(
|
||||
sparse.fun, sparse.x0, jac=sparse.jac,
|
||||
method=self.method)
|
||||
res_dense = least_squares(
|
||||
dense.fun, dense.x0, jac=sparse.jac,
|
||||
method=self.method)
|
||||
assert_equal(res_sparse.nfev, res_dense.nfev)
|
||||
assert_allclose(res_sparse.x, res_dense.x, atol=1e-20)
|
||||
assert_allclose(res_sparse.cost, 0, atol=1e-20)
|
||||
assert_allclose(res_dense.cost, 0, atol=1e-20)
|
||||
|
||||
def test_tr_options(self):
|
||||
p = BroydenTridiagonal()
|
||||
res = least_squares(p.fun, p.x0, p.jac, method=self.method,
|
||||
tr_options={'btol': 1e-10})
|
||||
assert_allclose(res.cost, 0, atol=1e-20)
|
||||
|
||||
def test_wrong_parameters(self):
|
||||
p = BroydenTridiagonal()
|
||||
assert_raises(ValueError, least_squares, p.fun, p.x0, p.jac,
|
||||
tr_solver='best', method=self.method)
|
||||
assert_raises(TypeError, least_squares, p.fun, p.x0, p.jac,
|
||||
tr_solver='lsmr', tr_options={'tol': 1e-10})
|
||||
|
||||
def test_solver_selection(self):
|
||||
sparse = BroydenTridiagonal(mode='sparse')
|
||||
dense = BroydenTridiagonal(mode='dense')
|
||||
res_sparse = least_squares(sparse.fun, sparse.x0, jac=sparse.jac,
|
||||
method=self.method)
|
||||
res_dense = least_squares(dense.fun, dense.x0, jac=dense.jac,
|
||||
method=self.method)
|
||||
assert_allclose(res_sparse.cost, 0, atol=1e-20)
|
||||
assert_allclose(res_dense.cost, 0, atol=1e-20)
|
||||
assert_(issparse(res_sparse.jac))
|
||||
assert_(isinstance(res_dense.jac, np.ndarray))
|
||||
|
||||
def test_numerical_jac(self):
|
||||
p = BroydenTridiagonal()
|
||||
for jac in ['2-point', '3-point', 'cs']:
|
||||
res_dense = least_squares(p.fun, p.x0, jac, method=self.method)
|
||||
res_sparse = least_squares(
|
||||
p.fun, p.x0, jac,method=self.method,
|
||||
jac_sparsity=p.sparsity)
|
||||
assert_equal(res_dense.nfev, res_sparse.nfev)
|
||||
assert_allclose(res_dense.x, res_sparse.x, atol=1e-20)
|
||||
assert_allclose(res_dense.cost, 0, atol=1e-20)
|
||||
assert_allclose(res_sparse.cost, 0, atol=1e-20)
|
||||
|
||||
def test_with_bounds(self):
|
||||
p = BroydenTridiagonal()
|
||||
for jac, jac_sparsity in product(
|
||||
[p.jac, '2-point', '3-point', 'cs'], [None, p.sparsity]):
|
||||
res_1 = least_squares(
|
||||
p.fun, p.x0, jac, bounds=(p.lb, np.inf),
|
||||
method=self.method,jac_sparsity=jac_sparsity)
|
||||
res_2 = least_squares(
|
||||
p.fun, p.x0, jac, bounds=(-np.inf, p.ub),
|
||||
method=self.method, jac_sparsity=jac_sparsity)
|
||||
res_3 = least_squares(
|
||||
p.fun, p.x0, jac, bounds=(p.lb, p.ub),
|
||||
method=self.method, jac_sparsity=jac_sparsity)
|
||||
assert_allclose(res_1.optimality, 0, atol=1e-10)
|
||||
assert_allclose(res_2.optimality, 0, atol=1e-10)
|
||||
assert_allclose(res_3.optimality, 0, atol=1e-10)
|
||||
|
||||
def test_wrong_jac_sparsity(self):
|
||||
p = BroydenTridiagonal()
|
||||
sparsity = p.sparsity[:-1]
|
||||
assert_raises(ValueError, least_squares, p.fun, p.x0,
|
||||
jac_sparsity=sparsity, method=self.method)
|
||||
|
||||
def test_linear_operator(self):
|
||||
p = BroydenTridiagonal(mode='operator')
|
||||
res = least_squares(p.fun, p.x0, p.jac, method=self.method)
|
||||
assert_allclose(res.cost, 0.0, atol=1e-20)
|
||||
assert_raises(ValueError, least_squares, p.fun, p.x0, p.jac,
|
||||
method=self.method, tr_solver='exact')
|
||||
|
||||
def test_x_scale_jac_scale(self):
|
||||
p = BroydenTridiagonal()
|
||||
res = least_squares(p.fun, p.x0, p.jac, method=self.method,
|
||||
x_scale='jac')
|
||||
assert_allclose(res.cost, 0.0, atol=1e-20)
|
||||
|
||||
p = BroydenTridiagonal(mode='operator')
|
||||
assert_raises(ValueError, least_squares, p.fun, p.x0, p.jac,
|
||||
method=self.method, x_scale='jac')
|
||||
|
||||
|
||||
class LossFunctionMixin:
|
||||
def test_options(self):
|
||||
for loss in LOSSES:
|
||||
res = least_squares(fun_trivial, 2.0, loss=loss,
|
||||
method=self.method)
|
||||
assert_allclose(res.x, 0, atol=1e-15)
|
||||
|
||||
assert_raises(ValueError, least_squares, fun_trivial, 2.0,
|
||||
loss='hinge', method=self.method)
|
||||
|
||||
def test_fun(self):
|
||||
# Test that res.fun is actual residuals, and not modified by loss
|
||||
# function stuff.
|
||||
for loss in LOSSES:
|
||||
res = least_squares(fun_trivial, 2.0, loss=loss,
|
||||
method=self.method)
|
||||
assert_equal(res.fun, fun_trivial(res.x))
|
||||
|
||||
def test_grad(self):
|
||||
# Test that res.grad is true gradient of loss function at the
|
||||
# solution. Use max_nfev = 1, to avoid reaching minimum.
|
||||
x = np.array([2.0]) # res.x will be this.
|
||||
|
||||
res = least_squares(fun_trivial, x, jac_trivial, loss='linear',
|
||||
max_nfev=1, method=self.method)
|
||||
assert_equal(res.grad, 2 * x * (x**2 + 5))
|
||||
|
||||
res = least_squares(fun_trivial, x, jac_trivial, loss='huber',
|
||||
max_nfev=1, method=self.method)
|
||||
assert_equal(res.grad, 2 * x)
|
||||
|
||||
res = least_squares(fun_trivial, x, jac_trivial, loss='soft_l1',
|
||||
max_nfev=1, method=self.method)
|
||||
assert_allclose(res.grad,
|
||||
2 * x * (x**2 + 5) / (1 + (x**2 + 5)**2)**0.5)
|
||||
|
||||
res = least_squares(fun_trivial, x, jac_trivial, loss='cauchy',
|
||||
max_nfev=1, method=self.method)
|
||||
assert_allclose(res.grad, 2 * x * (x**2 + 5) / (1 + (x**2 + 5)**2))
|
||||
|
||||
res = least_squares(fun_trivial, x, jac_trivial, loss='arctan',
|
||||
max_nfev=1, method=self.method)
|
||||
assert_allclose(res.grad, 2 * x * (x**2 + 5) / (1 + (x**2 + 5)**4))
|
||||
|
||||
res = least_squares(fun_trivial, x, jac_trivial, loss=cubic_soft_l1,
|
||||
max_nfev=1, method=self.method)
|
||||
assert_allclose(res.grad,
|
||||
2 * x * (x**2 + 5) / (1 + (x**2 + 5)**2)**(2/3))
|
||||
|
||||
def test_jac(self):
|
||||
# Test that res.jac.T.dot(res.jac) gives Gauss-Newton approximation
|
||||
# of Hessian. This approximation is computed by doubly differentiating
|
||||
# the cost function and dropping the part containing second derivative
|
||||
# of f. For a scalar function it is computed as
|
||||
# H = (rho' + 2 * rho'' * f**2) * f'**2, if the expression inside the
|
||||
# brackets is less than EPS it is replaced by EPS. Here, we check
|
||||
# against the root of H.
|
||||
|
||||
x = 2.0 # res.x will be this.
|
||||
f = x**2 + 5 # res.fun will be this.
|
||||
|
||||
res = least_squares(fun_trivial, x, jac_trivial, loss='linear',
|
||||
max_nfev=1, method=self.method)
|
||||
assert_equal(res.jac, 2 * x)
|
||||
|
||||
# For `huber` loss the Jacobian correction is identically zero
|
||||
# in outlier region, in such cases it is modified to be equal EPS**0.5.
|
||||
res = least_squares(fun_trivial, x, jac_trivial, loss='huber',
|
||||
max_nfev=1, method=self.method)
|
||||
assert_equal(res.jac, 2 * x * EPS**0.5)
|
||||
|
||||
# Now, let's apply `loss_scale` to turn the residual into an inlier.
|
||||
# The loss function becomes linear.
|
||||
res = least_squares(fun_trivial, x, jac_trivial, loss='huber',
|
||||
f_scale=10, max_nfev=1)
|
||||
assert_equal(res.jac, 2 * x)
|
||||
|
||||
# 'soft_l1' always gives a positive scaling.
|
||||
res = least_squares(fun_trivial, x, jac_trivial, loss='soft_l1',
|
||||
max_nfev=1, method=self.method)
|
||||
assert_allclose(res.jac, 2 * x * (1 + f**2)**-0.75)
|
||||
|
||||
# For 'cauchy' the correction term turns out to be negative, and it
|
||||
# replaced by EPS**0.5.
|
||||
res = least_squares(fun_trivial, x, jac_trivial, loss='cauchy',
|
||||
max_nfev=1, method=self.method)
|
||||
assert_allclose(res.jac, 2 * x * EPS**0.5)
|
||||
|
||||
# Now use scaling to turn the residual to inlier.
|
||||
res = least_squares(fun_trivial, x, jac_trivial, loss='cauchy',
|
||||
f_scale=10, max_nfev=1, method=self.method)
|
||||
fs = f / 10
|
||||
assert_allclose(res.jac, 2 * x * (1 - fs**2)**0.5 / (1 + fs**2))
|
||||
|
||||
# 'arctan' gives an outlier.
|
||||
res = least_squares(fun_trivial, x, jac_trivial, loss='arctan',
|
||||
max_nfev=1, method=self.method)
|
||||
assert_allclose(res.jac, 2 * x * EPS**0.5)
|
||||
|
||||
# Turn to inlier.
|
||||
res = least_squares(fun_trivial, x, jac_trivial, loss='arctan',
|
||||
f_scale=20.0, max_nfev=1, method=self.method)
|
||||
fs = f / 20
|
||||
assert_allclose(res.jac, 2 * x * (1 - 3 * fs**4)**0.5 / (1 + fs**4))
|
||||
|
||||
# cubic_soft_l1 will give an outlier.
|
||||
res = least_squares(fun_trivial, x, jac_trivial, loss=cubic_soft_l1,
|
||||
max_nfev=1)
|
||||
assert_allclose(res.jac, 2 * x * EPS**0.5)
|
||||
|
||||
# Turn to inlier.
|
||||
res = least_squares(fun_trivial, x, jac_trivial,
|
||||
loss=cubic_soft_l1, f_scale=6, max_nfev=1)
|
||||
fs = f / 6
|
||||
assert_allclose(res.jac,
|
||||
2 * x * (1 - fs**2 / 3)**0.5 * (1 + fs**2)**(-5/6))
|
||||
|
||||
def test_robustness(self):
|
||||
for noise in [0.1, 1.0]:
|
||||
p = ExponentialFittingProblem(1, 0.1, noise, random_seed=0)
|
||||
|
||||
for jac in ['2-point', '3-point', 'cs', p.jac]:
|
||||
res_lsq = least_squares(p.fun, p.p0, jac=jac,
|
||||
method=self.method)
|
||||
assert_allclose(res_lsq.optimality, 0, atol=1e-2)
|
||||
for loss in LOSSES:
|
||||
if loss == 'linear':
|
||||
continue
|
||||
res_robust = least_squares(
|
||||
p.fun, p.p0, jac=jac, loss=loss, f_scale=noise,
|
||||
method=self.method)
|
||||
assert_allclose(res_robust.optimality, 0, atol=1e-2)
|
||||
assert_(norm(res_robust.x - p.p_opt) <
|
||||
norm(res_lsq.x - p.p_opt))
|
||||
|
||||
|
||||
class TestDogbox(BaseMixin, BoundsMixin, SparseMixin, LossFunctionMixin):
|
||||
method = 'dogbox'
|
||||
|
||||
|
||||
class TestTRF(BaseMixin, BoundsMixin, SparseMixin, LossFunctionMixin):
|
||||
method = 'trf'
|
||||
|
||||
def test_lsmr_regularization(self):
|
||||
p = BroydenTridiagonal()
|
||||
for regularize in [True, False]:
|
||||
res = least_squares(p.fun, p.x0, p.jac, method='trf',
|
||||
tr_options={'regularize': regularize})
|
||||
assert_allclose(res.cost, 0, atol=1e-20)
|
||||
|
||||
|
||||
class TestLM(BaseMixin):
|
||||
method = 'lm'
|
||||
|
||||
def test_bounds_not_supported(self):
|
||||
assert_raises(ValueError, least_squares, fun_trivial,
|
||||
2.0, bounds=(-3.0, 3.0), method='lm')
|
||||
|
||||
def test_m_less_n_not_supported(self):
|
||||
x0 = [-2, 1]
|
||||
assert_raises(ValueError, least_squares, fun_rosenbrock_cropped, x0,
|
||||
method='lm')
|
||||
|
||||
def test_sparse_not_supported(self):
|
||||
p = BroydenTridiagonal()
|
||||
assert_raises(ValueError, least_squares, p.fun, p.x0, p.jac,
|
||||
method='lm')
|
||||
|
||||
def test_jac_sparsity_not_supported(self):
|
||||
assert_raises(ValueError, least_squares, fun_trivial, 2.0,
|
||||
jac_sparsity=[1], method='lm')
|
||||
|
||||
def test_LinearOperator_not_supported(self):
|
||||
p = BroydenTridiagonal(mode="operator")
|
||||
assert_raises(ValueError, least_squares, p.fun, p.x0, p.jac,
|
||||
method='lm')
|
||||
|
||||
def test_loss(self):
|
||||
res = least_squares(fun_trivial, 2.0, loss='linear', method='lm')
|
||||
assert_allclose(res.x, 0.0, atol=1e-4)
|
||||
|
||||
assert_raises(ValueError, least_squares, fun_trivial, 2.0,
|
||||
method='lm', loss='huber')
|
||||
|
||||
|
||||
def test_basic():
|
||||
# test that 'method' arg is really optional
|
||||
res = least_squares(fun_trivial, 2.0)
|
||||
assert_allclose(res.x, 0, atol=1e-10)
|
||||
|
||||
|
||||
def test_small_tolerances_for_lm():
|
||||
for ftol, xtol, gtol in [(None, 1e-13, 1e-13),
|
||||
(1e-13, None, 1e-13),
|
||||
(1e-13, 1e-13, None)]:
|
||||
assert_raises(ValueError, least_squares, fun_trivial, 2.0, xtol=xtol,
|
||||
ftol=ftol, gtol=gtol, method='lm')
|
||||
|
||||
|
||||
def test_fp32_gh12991():
|
||||
# checks that smaller FP sizes can be used in least_squares
|
||||
# this is the minimum working example reported for gh12991
|
||||
np.random.seed(1)
|
||||
|
||||
x = np.linspace(0, 1, 100).astype("float32")
|
||||
y = np.random.random(100).astype("float32")
|
||||
|
||||
def func(p, x):
|
||||
return p[0] + p[1] * x
|
||||
|
||||
def err(p, x, y):
|
||||
return func(p, x) - y
|
||||
|
||||
res = least_squares(err, [-1.0, -1.0], args=(x, y))
|
||||
# previously the initial jacobian calculated for this would be all 0
|
||||
# and the minimize would terminate immediately, with nfev=1, would
|
||||
# report a successful minimization (it shouldn't have done), but be
|
||||
# unchanged from the initial solution.
|
||||
# It was terminating early because the underlying approx_derivative
|
||||
# used a step size for FP64 when the working space was FP32.
|
||||
assert res.nfev > 2
|
||||
assert_allclose(res.x, np.array([0.4082241, 0.15530563]), atol=5e-5)
|
||||
116
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_linear_assignment.py
vendored
Normal file
116
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_linear_assignment.py
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
# Author: Brian M. Clapper, G. Varoquaux, Lars Buitinck
|
||||
# License: BSD
|
||||
|
||||
from numpy.testing import assert_array_equal
|
||||
import pytest
|
||||
|
||||
import numpy as np
|
||||
|
||||
from scipy.optimize import linear_sum_assignment
|
||||
from scipy.sparse import random
|
||||
from scipy.sparse._sputils import matrix
|
||||
from scipy.sparse.csgraph import min_weight_full_bipartite_matching
|
||||
from scipy.sparse.csgraph.tests.test_matching import (
|
||||
linear_sum_assignment_assertions, linear_sum_assignment_test_cases
|
||||
)
|
||||
|
||||
|
||||
def test_linear_sum_assignment_input_shape():
|
||||
with pytest.raises(ValueError, match="expected a matrix"):
|
||||
linear_sum_assignment([1, 2, 3])
|
||||
|
||||
|
||||
def test_linear_sum_assignment_input_object():
|
||||
C = [[1, 2, 3], [4, 5, 6]]
|
||||
assert_array_equal(linear_sum_assignment(C),
|
||||
linear_sum_assignment(np.asarray(C)))
|
||||
assert_array_equal(linear_sum_assignment(C),
|
||||
linear_sum_assignment(matrix(C)))
|
||||
|
||||
|
||||
def test_linear_sum_assignment_input_bool():
|
||||
I = np.identity(3)
|
||||
assert_array_equal(linear_sum_assignment(I.astype(np.bool_)),
|
||||
linear_sum_assignment(I))
|
||||
|
||||
|
||||
def test_linear_sum_assignment_input_string():
|
||||
I = np.identity(3)
|
||||
with pytest.raises(TypeError, match="Cannot cast array data"):
|
||||
linear_sum_assignment(I.astype(str))
|
||||
|
||||
|
||||
def test_linear_sum_assignment_input_nan():
|
||||
I = np.diag([np.nan, 1, 1])
|
||||
with pytest.raises(ValueError, match="contains invalid numeric entries"):
|
||||
linear_sum_assignment(I)
|
||||
|
||||
|
||||
def test_linear_sum_assignment_input_neginf():
|
||||
I = np.diag([1, -np.inf, 1])
|
||||
with pytest.raises(ValueError, match="contains invalid numeric entries"):
|
||||
linear_sum_assignment(I)
|
||||
|
||||
|
||||
def test_linear_sum_assignment_input_inf():
|
||||
I = np.identity(3)
|
||||
I[:, 0] = np.inf
|
||||
with pytest.raises(ValueError, match="cost matrix is infeasible"):
|
||||
linear_sum_assignment(I)
|
||||
|
||||
|
||||
def test_constant_cost_matrix():
|
||||
# Fixes #11602
|
||||
n = 8
|
||||
C = np.ones((n, n))
|
||||
row_ind, col_ind = linear_sum_assignment(C)
|
||||
assert_array_equal(row_ind, np.arange(n))
|
||||
assert_array_equal(col_ind, np.arange(n))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('num_rows,num_cols', [(0, 0), (2, 0), (0, 3)])
|
||||
def test_linear_sum_assignment_trivial_cost(num_rows, num_cols):
|
||||
C = np.empty(shape=(num_cols, num_rows))
|
||||
row_ind, col_ind = linear_sum_assignment(C)
|
||||
assert len(row_ind) == 0
|
||||
assert len(col_ind) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize('sign,test_case', linear_sum_assignment_test_cases)
|
||||
def test_linear_sum_assignment_small_inputs(sign, test_case):
|
||||
linear_sum_assignment_assertions(
|
||||
linear_sum_assignment, np.array, sign, test_case)
|
||||
|
||||
|
||||
# Tests that combine scipy.optimize.linear_sum_assignment and
|
||||
# scipy.sparse.csgraph.min_weight_full_bipartite_matching
|
||||
def test_two_methods_give_same_result_on_many_sparse_inputs():
|
||||
# As opposed to the test above, here we do not spell out the expected
|
||||
# output; only assert that the two methods give the same result.
|
||||
# Concretely, the below tests 100 cases of size 100x100, out of which
|
||||
# 36 are infeasible.
|
||||
np.random.seed(1234)
|
||||
for _ in range(100):
|
||||
lsa_raises = False
|
||||
mwfbm_raises = False
|
||||
sparse = random(100, 100, density=0.06,
|
||||
data_rvs=lambda size: np.random.randint(1, 100, size))
|
||||
# In csgraph, zeros correspond to missing edges, so we explicitly
|
||||
# replace those with infinities
|
||||
dense = np.full(sparse.shape, np.inf)
|
||||
dense[sparse.row, sparse.col] = sparse.data
|
||||
sparse = sparse.tocsr()
|
||||
try:
|
||||
row_ind, col_ind = linear_sum_assignment(dense)
|
||||
lsa_cost = dense[row_ind, col_ind].sum()
|
||||
except ValueError:
|
||||
lsa_raises = True
|
||||
try:
|
||||
row_ind, col_ind = min_weight_full_bipartite_matching(sparse)
|
||||
mwfbm_cost = sparse[row_ind, col_ind].sum()
|
||||
except ValueError:
|
||||
mwfbm_raises = True
|
||||
# Ensure that if one method raises, so does the other one.
|
||||
assert lsa_raises == mwfbm_raises
|
||||
if not lsa_raises:
|
||||
assert lsa_cost == mwfbm_cost
|
||||
312
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_linesearch.py
vendored
Normal file
312
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_linesearch.py
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
"""
|
||||
Tests for line search routines
|
||||
"""
|
||||
from numpy.testing import (assert_equal, assert_array_almost_equal,
|
||||
assert_array_almost_equal_nulp, assert_warns,
|
||||
suppress_warnings)
|
||||
import scipy.optimize._linesearch as ls
|
||||
from scipy.optimize._linesearch import LineSearchWarning
|
||||
import numpy as np
|
||||
|
||||
|
||||
def assert_wolfe(s, phi, derphi, c1=1e-4, c2=0.9, err_msg=""):
|
||||
"""
|
||||
Check that strong Wolfe conditions apply
|
||||
"""
|
||||
phi1 = phi(s)
|
||||
phi0 = phi(0)
|
||||
derphi0 = derphi(0)
|
||||
derphi1 = derphi(s)
|
||||
msg = "s = %s; phi(0) = %s; phi(s) = %s; phi'(0) = %s; phi'(s) = %s; %s" % (
|
||||
s, phi0, phi1, derphi0, derphi1, err_msg)
|
||||
|
||||
assert phi1 <= phi0 + c1*s*derphi0, "Wolfe 1 failed: " + msg
|
||||
assert abs(derphi1) <= abs(c2*derphi0), "Wolfe 2 failed: " + msg
|
||||
|
||||
|
||||
def assert_armijo(s, phi, c1=1e-4, err_msg=""):
|
||||
"""
|
||||
Check that Armijo condition applies
|
||||
"""
|
||||
phi1 = phi(s)
|
||||
phi0 = phi(0)
|
||||
msg = "s = %s; phi(0) = %s; phi(s) = %s; %s" % (s, phi0, phi1, err_msg)
|
||||
assert phi1 <= (1 - c1*s)*phi0, msg
|
||||
|
||||
|
||||
def assert_line_wolfe(x, p, s, f, fprime, **kw):
|
||||
assert_wolfe(s, phi=lambda sp: f(x + p*sp),
|
||||
derphi=lambda sp: np.dot(fprime(x + p*sp), p), **kw)
|
||||
|
||||
|
||||
def assert_line_armijo(x, p, s, f, **kw):
|
||||
assert_armijo(s, phi=lambda sp: f(x + p*sp), **kw)
|
||||
|
||||
|
||||
def assert_fp_equal(x, y, err_msg="", nulp=50):
|
||||
"""Assert two arrays are equal, up to some floating-point rounding error"""
|
||||
try:
|
||||
assert_array_almost_equal_nulp(x, y, nulp)
|
||||
except AssertionError as e:
|
||||
raise AssertionError("%s\n%s" % (e, err_msg)) from e
|
||||
|
||||
|
||||
class TestLineSearch:
|
||||
# -- scalar functions; must have dphi(0.) < 0
|
||||
def _scalar_func_1(self, s):
|
||||
self.fcount += 1
|
||||
p = -s - s**3 + s**4
|
||||
dp = -1 - 3*s**2 + 4*s**3
|
||||
return p, dp
|
||||
|
||||
def _scalar_func_2(self, s):
|
||||
self.fcount += 1
|
||||
p = np.exp(-4*s) + s**2
|
||||
dp = -4*np.exp(-4*s) + 2*s
|
||||
return p, dp
|
||||
|
||||
def _scalar_func_3(self, s):
|
||||
self.fcount += 1
|
||||
p = -np.sin(10*s)
|
||||
dp = -10*np.cos(10*s)
|
||||
return p, dp
|
||||
|
||||
# -- n-d functions
|
||||
|
||||
def _line_func_1(self, x):
|
||||
self.fcount += 1
|
||||
f = np.dot(x, x)
|
||||
df = 2*x
|
||||
return f, df
|
||||
|
||||
def _line_func_2(self, x):
|
||||
self.fcount += 1
|
||||
f = np.dot(x, np.dot(self.A, x)) + 1
|
||||
df = np.dot(self.A + self.A.T, x)
|
||||
return f, df
|
||||
|
||||
# --
|
||||
|
||||
def setup_method(self):
|
||||
self.scalar_funcs = []
|
||||
self.line_funcs = []
|
||||
self.N = 20
|
||||
self.fcount = 0
|
||||
|
||||
def bind_index(func, idx):
|
||||
# Remember Python's closure semantics!
|
||||
return lambda *a, **kw: func(*a, **kw)[idx]
|
||||
|
||||
for name in sorted(dir(self)):
|
||||
if name.startswith('_scalar_func_'):
|
||||
value = getattr(self, name)
|
||||
self.scalar_funcs.append(
|
||||
(name, bind_index(value, 0), bind_index(value, 1)))
|
||||
elif name.startswith('_line_func_'):
|
||||
value = getattr(self, name)
|
||||
self.line_funcs.append(
|
||||
(name, bind_index(value, 0), bind_index(value, 1)))
|
||||
|
||||
np.random.seed(1234)
|
||||
self.A = np.random.randn(self.N, self.N)
|
||||
|
||||
def scalar_iter(self):
|
||||
for name, phi, derphi in self.scalar_funcs:
|
||||
for old_phi0 in np.random.randn(3):
|
||||
yield name, phi, derphi, old_phi0
|
||||
|
||||
def line_iter(self):
|
||||
for name, f, fprime in self.line_funcs:
|
||||
k = 0
|
||||
while k < 9:
|
||||
x = np.random.randn(self.N)
|
||||
p = np.random.randn(self.N)
|
||||
if np.dot(p, fprime(x)) >= 0:
|
||||
# always pick a descent direction
|
||||
continue
|
||||
k += 1
|
||||
old_fv = float(np.random.randn())
|
||||
yield name, f, fprime, x, p, old_fv
|
||||
|
||||
# -- Generic scalar searches
|
||||
|
||||
def test_scalar_search_wolfe1(self):
|
||||
c = 0
|
||||
for name, phi, derphi, old_phi0 in self.scalar_iter():
|
||||
c += 1
|
||||
s, phi1, phi0 = ls.scalar_search_wolfe1(phi, derphi, phi(0),
|
||||
old_phi0, derphi(0))
|
||||
assert_fp_equal(phi0, phi(0), name)
|
||||
assert_fp_equal(phi1, phi(s), name)
|
||||
assert_wolfe(s, phi, derphi, err_msg=name)
|
||||
|
||||
assert c > 3 # check that the iterator really works...
|
||||
|
||||
def test_scalar_search_wolfe2(self):
|
||||
for name, phi, derphi, old_phi0 in self.scalar_iter():
|
||||
s, phi1, phi0, derphi1 = ls.scalar_search_wolfe2(
|
||||
phi, derphi, phi(0), old_phi0, derphi(0))
|
||||
assert_fp_equal(phi0, phi(0), name)
|
||||
assert_fp_equal(phi1, phi(s), name)
|
||||
if derphi1 is not None:
|
||||
assert_fp_equal(derphi1, derphi(s), name)
|
||||
assert_wolfe(s, phi, derphi, err_msg="%s %g" % (name, old_phi0))
|
||||
|
||||
def test_scalar_search_wolfe2_with_low_amax(self):
|
||||
def phi(alpha):
|
||||
return (alpha - 5) ** 2
|
||||
|
||||
def derphi(alpha):
|
||||
return 2 * (alpha - 5)
|
||||
|
||||
s, _, _, _ = assert_warns(LineSearchWarning,
|
||||
ls.scalar_search_wolfe2, phi, derphi, amax=0.001)
|
||||
assert s is None
|
||||
|
||||
def test_scalar_search_wolfe2_regression(self):
|
||||
# Regression test for gh-12157
|
||||
# This phi has its minimum at alpha=4/3 ~ 1.333.
|
||||
def phi(alpha):
|
||||
if alpha < 1:
|
||||
return - 3*np.pi/2 * (alpha - 1)
|
||||
else:
|
||||
return np.cos(3*np.pi/2 * alpha - np.pi)
|
||||
|
||||
def derphi(alpha):
|
||||
if alpha < 1:
|
||||
return - 3*np.pi/2
|
||||
else:
|
||||
return - 3*np.pi/2 * np.sin(3*np.pi/2 * alpha - np.pi)
|
||||
|
||||
s, _, _, _ = ls.scalar_search_wolfe2(phi, derphi)
|
||||
# Without the fix in gh-13073, the scalar_search_wolfe2
|
||||
# returned s=2.0 instead.
|
||||
assert s < 1.5
|
||||
|
||||
def test_scalar_search_armijo(self):
|
||||
for name, phi, derphi, old_phi0 in self.scalar_iter():
|
||||
s, phi1 = ls.scalar_search_armijo(phi, phi(0), derphi(0))
|
||||
assert_fp_equal(phi1, phi(s), name)
|
||||
assert_armijo(s, phi, err_msg="%s %g" % (name, old_phi0))
|
||||
|
||||
# -- Generic line searches
|
||||
|
||||
def test_line_search_wolfe1(self):
|
||||
c = 0
|
||||
smax = 100
|
||||
for name, f, fprime, x, p, old_f in self.line_iter():
|
||||
f0 = f(x)
|
||||
g0 = fprime(x)
|
||||
self.fcount = 0
|
||||
s, fc, gc, fv, ofv, gv = ls.line_search_wolfe1(f, fprime, x, p,
|
||||
g0, f0, old_f,
|
||||
amax=smax)
|
||||
assert_equal(self.fcount, fc+gc)
|
||||
assert_fp_equal(ofv, f(x))
|
||||
if s is None:
|
||||
continue
|
||||
assert_fp_equal(fv, f(x + s*p))
|
||||
assert_array_almost_equal(gv, fprime(x + s*p), decimal=14)
|
||||
if s < smax:
|
||||
c += 1
|
||||
assert_line_wolfe(x, p, s, f, fprime, err_msg=name)
|
||||
|
||||
assert c > 3 # check that the iterator really works...
|
||||
|
||||
def test_line_search_wolfe2(self):
|
||||
c = 0
|
||||
smax = 512
|
||||
for name, f, fprime, x, p, old_f in self.line_iter():
|
||||
f0 = f(x)
|
||||
g0 = fprime(x)
|
||||
self.fcount = 0
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(LineSearchWarning,
|
||||
"The line search algorithm could not find a solution")
|
||||
sup.filter(LineSearchWarning,
|
||||
"The line search algorithm did not converge")
|
||||
s, fc, gc, fv, ofv, gv = ls.line_search_wolfe2(f, fprime, x, p,
|
||||
g0, f0, old_f,
|
||||
amax=smax)
|
||||
assert_equal(self.fcount, fc+gc)
|
||||
assert_fp_equal(ofv, f(x))
|
||||
assert_fp_equal(fv, f(x + s*p))
|
||||
if gv is not None:
|
||||
assert_array_almost_equal(gv, fprime(x + s*p), decimal=14)
|
||||
if s < smax:
|
||||
c += 1
|
||||
assert_line_wolfe(x, p, s, f, fprime, err_msg=name)
|
||||
assert c > 3 # check that the iterator really works...
|
||||
|
||||
def test_line_search_wolfe2_bounds(self):
|
||||
# See gh-7475
|
||||
|
||||
# For this f and p, starting at a point on axis 0, the strong Wolfe
|
||||
# condition 2 is met if and only if the step length s satisfies
|
||||
# |x + s| <= c2 * |x|
|
||||
f = lambda x: np.dot(x, x)
|
||||
fp = lambda x: 2 * x
|
||||
p = np.array([1, 0])
|
||||
|
||||
# Smallest s satisfying strong Wolfe conditions for these arguments is 30
|
||||
x = -60 * p
|
||||
c2 = 0.5
|
||||
|
||||
s, _, _, _, _, _ = ls.line_search_wolfe2(f, fp, x, p, amax=30, c2=c2)
|
||||
assert_line_wolfe(x, p, s, f, fp)
|
||||
|
||||
s, _, _, _, _, _ = assert_warns(LineSearchWarning,
|
||||
ls.line_search_wolfe2, f, fp, x, p,
|
||||
amax=29, c2=c2)
|
||||
assert s is None
|
||||
|
||||
# s=30 will only be tried on the 6th iteration, so this won't converge
|
||||
assert_warns(LineSearchWarning, ls.line_search_wolfe2, f, fp, x, p,
|
||||
c2=c2, maxiter=5)
|
||||
|
||||
def test_line_search_armijo(self):
|
||||
c = 0
|
||||
for name, f, fprime, x, p, old_f in self.line_iter():
|
||||
f0 = f(x)
|
||||
g0 = fprime(x)
|
||||
self.fcount = 0
|
||||
s, fc, fv = ls.line_search_armijo(f, x, p, g0, f0)
|
||||
c += 1
|
||||
assert_equal(self.fcount, fc)
|
||||
assert_fp_equal(fv, f(x + s*p))
|
||||
assert_line_armijo(x, p, s, f, err_msg=name)
|
||||
assert c >= 9
|
||||
|
||||
# -- More specific tests
|
||||
|
||||
def test_armijo_terminate_1(self):
|
||||
# Armijo should evaluate the function only once if the trial step
|
||||
# is already suitable
|
||||
count = [0]
|
||||
|
||||
def phi(s):
|
||||
count[0] += 1
|
||||
return -s + 0.01*s**2
|
||||
s, phi1 = ls.scalar_search_armijo(phi, phi(0), -1, alpha0=1)
|
||||
assert_equal(s, 1)
|
||||
assert_equal(count[0], 2)
|
||||
assert_armijo(s, phi)
|
||||
|
||||
def test_wolfe_terminate(self):
|
||||
# wolfe1 and wolfe2 should also evaluate the function only a few
|
||||
# times if the trial step is already suitable
|
||||
|
||||
def phi(s):
|
||||
count[0] += 1
|
||||
return -s + 0.05*s**2
|
||||
|
||||
def derphi(s):
|
||||
count[0] += 1
|
||||
return -1 + 0.05*2*s
|
||||
|
||||
for func in [ls.scalar_search_wolfe1, ls.scalar_search_wolfe2]:
|
||||
count = [0]
|
||||
r = func(phi, derphi, phi(0), None, derphi(0))
|
||||
assert r[0] is not None, (r, func)
|
||||
assert count[0] <= 2 + 2, (count, func)
|
||||
assert_wolfe(r[0], phi, derphi, err_msg=str(func))
|
||||
2437
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_linprog.py
vendored
Normal file
2437
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_linprog.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
297
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_lsq_common.py
vendored
Normal file
297
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_lsq_common.py
vendored
Normal file
@@ -0,0 +1,297 @@
|
||||
from numpy.testing import assert_, assert_allclose, assert_equal
|
||||
from pytest import raises as assert_raises
|
||||
import numpy as np
|
||||
|
||||
from scipy.optimize._lsq.common import (
|
||||
step_size_to_bound, find_active_constraints, make_strictly_feasible,
|
||||
CL_scaling_vector, intersect_trust_region, build_quadratic_1d,
|
||||
minimize_quadratic_1d, evaluate_quadratic, reflective_transformation,
|
||||
left_multiplied_operator, right_multiplied_operator)
|
||||
|
||||
|
||||
class TestBounds:
|
||||
def test_step_size_to_bounds(self):
|
||||
lb = np.array([-1.0, 2.5, 10.0])
|
||||
ub = np.array([1.0, 5.0, 100.0])
|
||||
x = np.array([0.0, 2.5, 12.0])
|
||||
|
||||
s = np.array([0.1, 0.0, 0.0])
|
||||
step, hits = step_size_to_bound(x, s, lb, ub)
|
||||
assert_equal(step, 10)
|
||||
assert_equal(hits, [1, 0, 0])
|
||||
|
||||
s = np.array([0.01, 0.05, -1.0])
|
||||
step, hits = step_size_to_bound(x, s, lb, ub)
|
||||
assert_equal(step, 2)
|
||||
assert_equal(hits, [0, 0, -1])
|
||||
|
||||
s = np.array([10.0, -0.0001, 100.0])
|
||||
step, hits = step_size_to_bound(x, s, lb, ub)
|
||||
assert_equal(step, np.array(-0))
|
||||
assert_equal(hits, [0, -1, 0])
|
||||
|
||||
s = np.array([1.0, 0.5, -2.0])
|
||||
step, hits = step_size_to_bound(x, s, lb, ub)
|
||||
assert_equal(step, 1.0)
|
||||
assert_equal(hits, [1, 0, -1])
|
||||
|
||||
s = np.zeros(3)
|
||||
step, hits = step_size_to_bound(x, s, lb, ub)
|
||||
assert_equal(step, np.inf)
|
||||
assert_equal(hits, [0, 0, 0])
|
||||
|
||||
def test_find_active_constraints(self):
|
||||
lb = np.array([0.0, -10.0, 1.0])
|
||||
ub = np.array([1.0, 0.0, 100.0])
|
||||
|
||||
x = np.array([0.5, -5.0, 2.0])
|
||||
active = find_active_constraints(x, lb, ub)
|
||||
assert_equal(active, [0, 0, 0])
|
||||
|
||||
x = np.array([0.0, 0.0, 10.0])
|
||||
active = find_active_constraints(x, lb, ub)
|
||||
assert_equal(active, [-1, 1, 0])
|
||||
|
||||
active = find_active_constraints(x, lb, ub, rtol=0)
|
||||
assert_equal(active, [-1, 1, 0])
|
||||
|
||||
x = np.array([1e-9, -1e-8, 100 - 1e-9])
|
||||
active = find_active_constraints(x, lb, ub)
|
||||
assert_equal(active, [0, 0, 1])
|
||||
|
||||
active = find_active_constraints(x, lb, ub, rtol=1.5e-9)
|
||||
assert_equal(active, [-1, 0, 1])
|
||||
|
||||
lb = np.array([1.0, -np.inf, -np.inf])
|
||||
ub = np.array([np.inf, 10.0, np.inf])
|
||||
|
||||
x = np.ones(3)
|
||||
active = find_active_constraints(x, lb, ub)
|
||||
assert_equal(active, [-1, 0, 0])
|
||||
|
||||
# Handles out-of-bound cases.
|
||||
x = np.array([0.0, 11.0, 0.0])
|
||||
active = find_active_constraints(x, lb, ub)
|
||||
assert_equal(active, [-1, 1, 0])
|
||||
|
||||
active = find_active_constraints(x, lb, ub, rtol=0)
|
||||
assert_equal(active, [-1, 1, 0])
|
||||
|
||||
def test_make_strictly_feasible(self):
|
||||
lb = np.array([-0.5, -0.8, 2.0])
|
||||
ub = np.array([0.8, 1.0, 3.0])
|
||||
|
||||
x = np.array([-0.5, 0.0, 2 + 1e-10])
|
||||
|
||||
x_new = make_strictly_feasible(x, lb, ub, rstep=0)
|
||||
assert_(x_new[0] > -0.5)
|
||||
assert_equal(x_new[1:], x[1:])
|
||||
|
||||
x_new = make_strictly_feasible(x, lb, ub, rstep=1e-4)
|
||||
assert_equal(x_new, [-0.5 + 1e-4, 0.0, 2 * (1 + 1e-4)])
|
||||
|
||||
x = np.array([-0.5, -1, 3.1])
|
||||
x_new = make_strictly_feasible(x, lb, ub)
|
||||
assert_(np.all((x_new >= lb) & (x_new <= ub)))
|
||||
|
||||
x_new = make_strictly_feasible(x, lb, ub, rstep=0)
|
||||
assert_(np.all((x_new >= lb) & (x_new <= ub)))
|
||||
|
||||
lb = np.array([-1, 100.0])
|
||||
ub = np.array([1, 100.0 + 1e-10])
|
||||
x = np.array([0, 100.0])
|
||||
x_new = make_strictly_feasible(x, lb, ub, rstep=1e-8)
|
||||
assert_equal(x_new, [0, 100.0 + 0.5e-10])
|
||||
|
||||
def test_scaling_vector(self):
|
||||
lb = np.array([-np.inf, -5.0, 1.0, -np.inf])
|
||||
ub = np.array([1.0, np.inf, 10.0, np.inf])
|
||||
x = np.array([0.5, 2.0, 5.0, 0.0])
|
||||
g = np.array([1.0, 0.1, -10.0, 0.0])
|
||||
v, dv = CL_scaling_vector(x, g, lb, ub)
|
||||
assert_equal(v, [1.0, 7.0, 5.0, 1.0])
|
||||
assert_equal(dv, [0.0, 1.0, -1.0, 0.0])
|
||||
|
||||
|
||||
class TestQuadraticFunction:
|
||||
def setup_method(self):
|
||||
self.J = np.array([
|
||||
[0.1, 0.2],
|
||||
[-1.0, 1.0],
|
||||
[0.5, 0.2]])
|
||||
self.g = np.array([0.8, -2.0])
|
||||
self.diag = np.array([1.0, 2.0])
|
||||
|
||||
def test_build_quadratic_1d(self):
|
||||
s = np.zeros(2)
|
||||
a, b = build_quadratic_1d(self.J, self.g, s)
|
||||
assert_equal(a, 0)
|
||||
assert_equal(b, 0)
|
||||
|
||||
a, b = build_quadratic_1d(self.J, self.g, s, diag=self.diag)
|
||||
assert_equal(a, 0)
|
||||
assert_equal(b, 0)
|
||||
|
||||
s = np.array([1.0, -1.0])
|
||||
a, b = build_quadratic_1d(self.J, self.g, s)
|
||||
assert_equal(a, 2.05)
|
||||
assert_equal(b, 2.8)
|
||||
|
||||
a, b = build_quadratic_1d(self.J, self.g, s, diag=self.diag)
|
||||
assert_equal(a, 3.55)
|
||||
assert_equal(b, 2.8)
|
||||
|
||||
s0 = np.array([0.5, 0.5])
|
||||
a, b, c = build_quadratic_1d(self.J, self.g, s, diag=self.diag, s0=s0)
|
||||
assert_equal(a, 3.55)
|
||||
assert_allclose(b, 2.39)
|
||||
assert_allclose(c, -0.1525)
|
||||
|
||||
def test_minimize_quadratic_1d(self):
|
||||
a = 5
|
||||
b = -1
|
||||
|
||||
t, y = minimize_quadratic_1d(a, b, 1, 2)
|
||||
assert_equal(t, 1)
|
||||
assert_allclose(y, a * t**2 + b * t, rtol=1e-15)
|
||||
|
||||
t, y = minimize_quadratic_1d(a, b, -2, -1)
|
||||
assert_equal(t, -1)
|
||||
assert_allclose(y, a * t**2 + b * t, rtol=1e-15)
|
||||
|
||||
t, y = minimize_quadratic_1d(a, b, -1, 1)
|
||||
assert_equal(t, 0.1)
|
||||
assert_allclose(y, a * t**2 + b * t, rtol=1e-15)
|
||||
|
||||
c = 10
|
||||
t, y = minimize_quadratic_1d(a, b, -1, 1, c=c)
|
||||
assert_equal(t, 0.1)
|
||||
assert_allclose(y, a * t**2 + b * t + c, rtol=1e-15)
|
||||
|
||||
t, y = minimize_quadratic_1d(a, b, -np.inf, np.inf, c=c)
|
||||
assert_equal(t, 0.1)
|
||||
assert_allclose(y, a * t ** 2 + b * t + c, rtol=1e-15)
|
||||
|
||||
t, y = minimize_quadratic_1d(a, b, 0, np.inf, c=c)
|
||||
assert_equal(t, 0.1)
|
||||
assert_allclose(y, a * t ** 2 + b * t + c, rtol=1e-15)
|
||||
|
||||
t, y = minimize_quadratic_1d(a, b, -np.inf, 0, c=c)
|
||||
assert_equal(t, 0)
|
||||
assert_allclose(y, a * t ** 2 + b * t + c, rtol=1e-15)
|
||||
|
||||
a = -1
|
||||
b = 0.2
|
||||
t, y = minimize_quadratic_1d(a, b, -np.inf, np.inf)
|
||||
assert_equal(y, -np.inf)
|
||||
|
||||
t, y = minimize_quadratic_1d(a, b, 0, np.inf)
|
||||
assert_equal(t, np.inf)
|
||||
assert_equal(y, -np.inf)
|
||||
|
||||
t, y = minimize_quadratic_1d(a, b, -np.inf, 0)
|
||||
assert_equal(t, -np.inf)
|
||||
assert_equal(y, -np.inf)
|
||||
|
||||
def test_evaluate_quadratic(self):
|
||||
s = np.array([1.0, -1.0])
|
||||
|
||||
value = evaluate_quadratic(self.J, self.g, s)
|
||||
assert_equal(value, 4.85)
|
||||
|
||||
value = evaluate_quadratic(self.J, self.g, s, diag=self.diag)
|
||||
assert_equal(value, 6.35)
|
||||
|
||||
s = np.array([[1.0, -1.0],
|
||||
[1.0, 1.0],
|
||||
[0.0, 0.0]])
|
||||
|
||||
values = evaluate_quadratic(self.J, self.g, s)
|
||||
assert_allclose(values, [4.85, -0.91, 0.0])
|
||||
|
||||
values = evaluate_quadratic(self.J, self.g, s, diag=self.diag)
|
||||
assert_allclose(values, [6.35, 0.59, 0.0])
|
||||
|
||||
|
||||
class TestTrustRegion:
|
||||
def test_intersect(self):
|
||||
Delta = 1.0
|
||||
|
||||
x = np.zeros(3)
|
||||
s = np.array([1.0, 0.0, 0.0])
|
||||
t_neg, t_pos = intersect_trust_region(x, s, Delta)
|
||||
assert_equal(t_neg, -1)
|
||||
assert_equal(t_pos, 1)
|
||||
|
||||
s = np.array([-1.0, 1.0, -1.0])
|
||||
t_neg, t_pos = intersect_trust_region(x, s, Delta)
|
||||
assert_allclose(t_neg, -3**-0.5)
|
||||
assert_allclose(t_pos, 3**-0.5)
|
||||
|
||||
x = np.array([0.5, -0.5, 0])
|
||||
s = np.array([0, 0, 1.0])
|
||||
t_neg, t_pos = intersect_trust_region(x, s, Delta)
|
||||
assert_allclose(t_neg, -2**-0.5)
|
||||
assert_allclose(t_pos, 2**-0.5)
|
||||
|
||||
x = np.ones(3)
|
||||
assert_raises(ValueError, intersect_trust_region, x, s, Delta)
|
||||
|
||||
x = np.zeros(3)
|
||||
s = np.zeros(3)
|
||||
assert_raises(ValueError, intersect_trust_region, x, s, Delta)
|
||||
|
||||
|
||||
def test_reflective_transformation():
|
||||
lb = np.array([-1, -2], dtype=float)
|
||||
ub = np.array([5, 3], dtype=float)
|
||||
|
||||
y = np.array([0, 0])
|
||||
x, g = reflective_transformation(y, lb, ub)
|
||||
assert_equal(x, y)
|
||||
assert_equal(g, np.ones(2))
|
||||
|
||||
y = np.array([-4, 4], dtype=float)
|
||||
|
||||
x, g = reflective_transformation(y, lb, np.array([np.inf, np.inf]))
|
||||
assert_equal(x, [2, 4])
|
||||
assert_equal(g, [-1, 1])
|
||||
|
||||
x, g = reflective_transformation(y, np.array([-np.inf, -np.inf]), ub)
|
||||
assert_equal(x, [-4, 2])
|
||||
assert_equal(g, [1, -1])
|
||||
|
||||
x, g = reflective_transformation(y, lb, ub)
|
||||
assert_equal(x, [2, 2])
|
||||
assert_equal(g, [-1, -1])
|
||||
|
||||
lb = np.array([-np.inf, -2])
|
||||
ub = np.array([5, np.inf])
|
||||
y = np.array([10, 10], dtype=float)
|
||||
x, g = reflective_transformation(y, lb, ub)
|
||||
assert_equal(x, [0, 10])
|
||||
assert_equal(g, [-1, 1])
|
||||
|
||||
|
||||
def test_linear_operators():
|
||||
A = np.arange(6).reshape((3, 2))
|
||||
|
||||
d_left = np.array([-1, 2, 5])
|
||||
DA = np.diag(d_left).dot(A)
|
||||
J_left = left_multiplied_operator(A, d_left)
|
||||
|
||||
d_right = np.array([5, 10])
|
||||
AD = A.dot(np.diag(d_right))
|
||||
J_right = right_multiplied_operator(A, d_right)
|
||||
|
||||
x = np.array([-2, 3])
|
||||
X = -2 * np.arange(2, 8).reshape((2, 3))
|
||||
xt = np.array([0, -2, 15])
|
||||
|
||||
assert_allclose(DA.dot(x), J_left.matvec(x))
|
||||
assert_allclose(DA.dot(X), J_left.matmat(X))
|
||||
assert_allclose(DA.T.dot(xt), J_left.rmatvec(xt))
|
||||
|
||||
assert_allclose(AD.dot(x), J_right.matvec(x))
|
||||
assert_allclose(AD.dot(X), J_right.matmat(X))
|
||||
assert_allclose(AD.T.dot(xt), J_right.rmatvec(xt))
|
||||
269
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_lsq_linear.py
vendored
Normal file
269
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_lsq_linear.py
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
import pytest
|
||||
|
||||
import numpy as np
|
||||
from numpy.linalg import lstsq
|
||||
from numpy.testing import assert_allclose, assert_equal, assert_
|
||||
|
||||
from scipy.sparse import rand, coo_matrix
|
||||
from scipy.sparse.linalg import aslinearoperator
|
||||
from scipy.optimize import lsq_linear
|
||||
|
||||
|
||||
A = np.array([
|
||||
[0.171, -0.057],
|
||||
[-0.049, -0.248],
|
||||
[-0.166, 0.054],
|
||||
])
|
||||
b = np.array([0.074, 1.014, -0.383])
|
||||
|
||||
|
||||
class BaseMixin:
|
||||
def setup_method(self):
|
||||
self.rnd = np.random.RandomState(0)
|
||||
|
||||
def test_dense_no_bounds(self):
|
||||
for lsq_solver in self.lsq_solvers:
|
||||
res = lsq_linear(A, b, method=self.method, lsq_solver=lsq_solver)
|
||||
assert_allclose(res.x, lstsq(A, b, rcond=-1)[0])
|
||||
assert_allclose(res.x, res.unbounded_sol[0])
|
||||
|
||||
def test_dense_bounds(self):
|
||||
# Solutions for comparison are taken from MATLAB.
|
||||
lb = np.array([-1, -10])
|
||||
ub = np.array([1, 0])
|
||||
unbounded_sol = lstsq(A, b, rcond=-1)[0]
|
||||
for lsq_solver in self.lsq_solvers:
|
||||
res = lsq_linear(A, b, (lb, ub), method=self.method,
|
||||
lsq_solver=lsq_solver)
|
||||
assert_allclose(res.x, lstsq(A, b, rcond=-1)[0])
|
||||
assert_allclose(res.unbounded_sol[0], unbounded_sol)
|
||||
|
||||
lb = np.array([0.0, -np.inf])
|
||||
for lsq_solver in self.lsq_solvers:
|
||||
res = lsq_linear(A, b, (lb, np.inf), method=self.method,
|
||||
lsq_solver=lsq_solver)
|
||||
assert_allclose(res.x, np.array([0.0, -4.084174437334673]),
|
||||
atol=1e-6)
|
||||
assert_allclose(res.unbounded_sol[0], unbounded_sol)
|
||||
|
||||
lb = np.array([-1, 0])
|
||||
for lsq_solver in self.lsq_solvers:
|
||||
res = lsq_linear(A, b, (lb, np.inf), method=self.method,
|
||||
lsq_solver=lsq_solver)
|
||||
assert_allclose(res.x, np.array([0.448427311733504, 0]),
|
||||
atol=1e-15)
|
||||
assert_allclose(res.unbounded_sol[0], unbounded_sol)
|
||||
|
||||
ub = np.array([np.inf, -5])
|
||||
for lsq_solver in self.lsq_solvers:
|
||||
res = lsq_linear(A, b, (-np.inf, ub), method=self.method,
|
||||
lsq_solver=lsq_solver)
|
||||
assert_allclose(res.x, np.array([-0.105560998682388, -5]))
|
||||
assert_allclose(res.unbounded_sol[0], unbounded_sol)
|
||||
|
||||
ub = np.array([-1, np.inf])
|
||||
for lsq_solver in self.lsq_solvers:
|
||||
res = lsq_linear(A, b, (-np.inf, ub), method=self.method,
|
||||
lsq_solver=lsq_solver)
|
||||
assert_allclose(res.x, np.array([-1, -4.181102129483254]))
|
||||
assert_allclose(res.unbounded_sol[0], unbounded_sol)
|
||||
|
||||
lb = np.array([0, -4])
|
||||
ub = np.array([1, 0])
|
||||
for lsq_solver in self.lsq_solvers:
|
||||
res = lsq_linear(A, b, (lb, ub), method=self.method,
|
||||
lsq_solver=lsq_solver)
|
||||
assert_allclose(res.x, np.array([0.005236663400791, -4]))
|
||||
assert_allclose(res.unbounded_sol[0], unbounded_sol)
|
||||
|
||||
def test_np_matrix(self):
|
||||
# gh-10711
|
||||
with np.testing.suppress_warnings() as sup:
|
||||
sup.filter(PendingDeprecationWarning)
|
||||
A = np.matrix([[20, -4, 0, 2, 3], [10, -2, 1, 0, -1]])
|
||||
k = np.array([20, 15])
|
||||
s_t = lsq_linear(A, k)
|
||||
|
||||
def test_dense_rank_deficient(self):
|
||||
A = np.array([[-0.307, -0.184]])
|
||||
b = np.array([0.773])
|
||||
lb = [-0.1, -0.1]
|
||||
ub = [0.1, 0.1]
|
||||
for lsq_solver in self.lsq_solvers:
|
||||
res = lsq_linear(A, b, (lb, ub), method=self.method,
|
||||
lsq_solver=lsq_solver)
|
||||
assert_allclose(res.x, [-0.1, -0.1])
|
||||
assert_allclose(res.unbounded_sol[0], lstsq(A, b, rcond=-1)[0])
|
||||
|
||||
A = np.array([
|
||||
[0.334, 0.668],
|
||||
[-0.516, -1.032],
|
||||
[0.192, 0.384],
|
||||
])
|
||||
b = np.array([-1.436, 0.135, 0.909])
|
||||
lb = [0, -1]
|
||||
ub = [1, -0.5]
|
||||
for lsq_solver in self.lsq_solvers:
|
||||
res = lsq_linear(A, b, (lb, ub), method=self.method,
|
||||
lsq_solver=lsq_solver)
|
||||
assert_allclose(res.optimality, 0, atol=1e-11)
|
||||
assert_allclose(res.unbounded_sol[0], lstsq(A, b, rcond=-1)[0])
|
||||
|
||||
def test_full_result(self):
|
||||
lb = np.array([0, -4])
|
||||
ub = np.array([1, 0])
|
||||
res = lsq_linear(A, b, (lb, ub), method=self.method)
|
||||
|
||||
assert_allclose(res.x, [0.005236663400791, -4])
|
||||
assert_allclose(res.unbounded_sol[0], lstsq(A, b, rcond=-1)[0])
|
||||
|
||||
r = A.dot(res.x) - b
|
||||
assert_allclose(res.cost, 0.5 * np.dot(r, r))
|
||||
assert_allclose(res.fun, r)
|
||||
|
||||
assert_allclose(res.optimality, 0.0, atol=1e-12)
|
||||
assert_equal(res.active_mask, [0, -1])
|
||||
assert_(res.nit < 15)
|
||||
assert_(res.status == 1 or res.status == 3)
|
||||
assert_(isinstance(res.message, str))
|
||||
assert_(res.success)
|
||||
|
||||
# This is a test for issue #9982.
|
||||
def test_almost_singular(self):
|
||||
A = np.array(
|
||||
[[0.8854232310355122, 0.0365312146937765, 0.0365312146836789],
|
||||
[0.3742460132129041, 0.0130523214078376, 0.0130523214077873],
|
||||
[0.9680633871281361, 0.0319366128718639, 0.0319366128718388]])
|
||||
|
||||
b = np.array(
|
||||
[0.0055029366538097, 0.0026677442422208, 0.0066612514782381])
|
||||
|
||||
result = lsq_linear(A, b, method=self.method)
|
||||
assert_(result.cost < 1.1e-8)
|
||||
|
||||
def test_large_rank_deficient(self):
|
||||
np.random.seed(0)
|
||||
n, m = np.sort(np.random.randint(2, 1000, size=2))
|
||||
m *= 2 # make m >> n
|
||||
A = 1.0 * np.random.randint(-99, 99, size=[m, n])
|
||||
b = 1.0 * np.random.randint(-99, 99, size=[m])
|
||||
bounds = 1.0 * np.sort(np.random.randint(-99, 99, size=(2, n)), axis=0)
|
||||
bounds[1, :] += 1.0 # ensure up > lb
|
||||
|
||||
# Make the A matrix strongly rank deficient by replicating some columns
|
||||
w = np.random.choice(n, n) # Select random columns with duplicates
|
||||
A = A[:, w]
|
||||
|
||||
x_bvls = lsq_linear(A, b, bounds=bounds, method='bvls').x
|
||||
x_trf = lsq_linear(A, b, bounds=bounds, method='trf').x
|
||||
|
||||
cost_bvls = np.sum((A @ x_bvls - b)**2)
|
||||
cost_trf = np.sum((A @ x_trf - b)**2)
|
||||
|
||||
assert_(abs(cost_bvls - cost_trf) < cost_trf*1e-10)
|
||||
|
||||
def test_convergence_small_matrix(self):
|
||||
A = np.array([[49.0, 41.0, -32.0],
|
||||
[-19.0, -32.0, -8.0],
|
||||
[-13.0, 10.0, 69.0]])
|
||||
b = np.array([-41.0, -90.0, 47.0])
|
||||
bounds = np.array([[31.0, -44.0, 26.0],
|
||||
[54.0, -32.0, 28.0]])
|
||||
|
||||
x_bvls = lsq_linear(A, b, bounds=bounds, method='bvls').x
|
||||
x_trf = lsq_linear(A, b, bounds=bounds, method='trf').x
|
||||
|
||||
cost_bvls = np.sum((A @ x_bvls - b)**2)
|
||||
cost_trf = np.sum((A @ x_trf - b)**2)
|
||||
|
||||
assert_(abs(cost_bvls - cost_trf) < cost_trf*1e-10)
|
||||
|
||||
|
||||
class SparseMixin:
|
||||
def test_sparse_and_LinearOperator(self):
|
||||
m = 5000
|
||||
n = 1000
|
||||
A = rand(m, n, random_state=0)
|
||||
b = self.rnd.randn(m)
|
||||
res = lsq_linear(A, b)
|
||||
assert_allclose(res.optimality, 0, atol=1e-6)
|
||||
|
||||
A = aslinearoperator(A)
|
||||
res = lsq_linear(A, b)
|
||||
assert_allclose(res.optimality, 0, atol=1e-6)
|
||||
|
||||
def test_sparse_bounds(self):
|
||||
m = 5000
|
||||
n = 1000
|
||||
A = rand(m, n, random_state=0)
|
||||
b = self.rnd.randn(m)
|
||||
lb = self.rnd.randn(n)
|
||||
ub = lb + 1
|
||||
res = lsq_linear(A, b, (lb, ub))
|
||||
assert_allclose(res.optimality, 0.0, atol=1e-6)
|
||||
|
||||
res = lsq_linear(A, b, (lb, ub), lsmr_tol=1e-13,
|
||||
lsmr_maxiter=1500)
|
||||
assert_allclose(res.optimality, 0.0, atol=1e-6)
|
||||
|
||||
res = lsq_linear(A, b, (lb, ub), lsmr_tol='auto')
|
||||
assert_allclose(res.optimality, 0.0, atol=1e-6)
|
||||
|
||||
def test_sparse_ill_conditioned(self):
|
||||
# Sparse matrix with condition number of ~4 million
|
||||
data = np.array([1., 1., 1., 1. + 1e-6, 1.])
|
||||
row = np.array([0, 0, 1, 2, 2])
|
||||
col = np.array([0, 2, 1, 0, 2])
|
||||
A = coo_matrix((data, (row, col)), shape=(3, 3))
|
||||
|
||||
# Get the exact solution
|
||||
exact_sol = lsq_linear(A.toarray(), b, lsq_solver='exact')
|
||||
|
||||
# Default lsmr arguments should not fully converge the solution
|
||||
default_lsmr_sol = lsq_linear(A, b, lsq_solver='lsmr')
|
||||
with pytest.raises(AssertionError, match=""):
|
||||
assert_allclose(exact_sol.x, default_lsmr_sol.x)
|
||||
|
||||
# By increasing the maximum lsmr iters, it will converge
|
||||
conv_lsmr = lsq_linear(A, b, lsq_solver='lsmr', lsmr_maxiter=10)
|
||||
assert_allclose(exact_sol.x, conv_lsmr.x)
|
||||
|
||||
|
||||
class TestTRF(BaseMixin, SparseMixin):
|
||||
method = 'trf'
|
||||
lsq_solvers = ['exact', 'lsmr']
|
||||
|
||||
|
||||
class TestBVLS(BaseMixin):
|
||||
method = 'bvls'
|
||||
lsq_solvers = ['exact']
|
||||
|
||||
|
||||
class TestErrorChecking:
|
||||
def test_option_lsmr_tol(self):
|
||||
# Should work with a positive float, string equal to 'auto', or None
|
||||
_ = lsq_linear(A, b, lsq_solver='lsmr', lsmr_tol=1e-2)
|
||||
_ = lsq_linear(A, b, lsq_solver='lsmr', lsmr_tol='auto')
|
||||
_ = lsq_linear(A, b, lsq_solver='lsmr', lsmr_tol=None)
|
||||
|
||||
# Should raise error with negative float, strings
|
||||
# other than 'auto', and integers
|
||||
err_message = "`lsmr_tol` must be None, 'auto', or positive float."
|
||||
with pytest.raises(ValueError, match=err_message):
|
||||
_ = lsq_linear(A, b, lsq_solver='lsmr', lsmr_tol=-0.1)
|
||||
with pytest.raises(ValueError, match=err_message):
|
||||
_ = lsq_linear(A, b, lsq_solver='lsmr', lsmr_tol='foo')
|
||||
with pytest.raises(ValueError, match=err_message):
|
||||
_ = lsq_linear(A, b, lsq_solver='lsmr', lsmr_tol=1)
|
||||
|
||||
def test_option_lsmr_maxiter(self):
|
||||
# Should work with positive integers or None
|
||||
_ = lsq_linear(A, b, lsq_solver='lsmr', lsmr_maxiter=1)
|
||||
_ = lsq_linear(A, b, lsq_solver='lsmr', lsmr_maxiter=None)
|
||||
|
||||
# Should raise error with 0 or negative max iter
|
||||
err_message = "`lsmr_maxiter` must be None or positive integer."
|
||||
with pytest.raises(ValueError, match=err_message):
|
||||
_ = lsq_linear(A, b, lsq_solver='lsmr', lsmr_maxiter=0)
|
||||
with pytest.raises(ValueError, match=err_message):
|
||||
_ = lsq_linear(A, b, lsq_solver='lsmr', lsmr_maxiter=-1)
|
||||
370
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_milp.py
vendored
Normal file
370
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_milp.py
vendored
Normal file
@@ -0,0 +1,370 @@
|
||||
"""
|
||||
Unit test for Mixed Integer Linear Programming
|
||||
"""
|
||||
import re
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose, assert_array_equal
|
||||
import pytest
|
||||
|
||||
from .test_linprog import magic_square
|
||||
from scipy.optimize import milp, Bounds, LinearConstraint
|
||||
|
||||
|
||||
def test_milp_iv():
|
||||
|
||||
message = "`c` must be a one-dimensional array of finite numbers with"
|
||||
with pytest.raises(ValueError, match=message):
|
||||
milp(np.zeros((3, 4)))
|
||||
with pytest.raises(ValueError, match=message):
|
||||
milp([])
|
||||
with pytest.raises(ValueError, match=message):
|
||||
milp(None)
|
||||
|
||||
message = "`bounds` must be convertible into an instance of..."
|
||||
with pytest.raises(ValueError, match=message):
|
||||
milp(1, bounds=10)
|
||||
|
||||
message = "`constraints` (or each element within `constraints`) must be"
|
||||
with pytest.raises(ValueError, match=re.escape(message)):
|
||||
milp(1, constraints=10)
|
||||
with pytest.raises(ValueError, match=re.escape(message)):
|
||||
milp(np.zeros(3), constraints=([[1, 2, 3]], [2, 3], [2, 3]))
|
||||
|
||||
message = "The shape of `A` must be (len(b_l), len(c))."
|
||||
with pytest.raises(ValueError, match=re.escape(message)):
|
||||
milp(np.zeros(3), constraints=([[1, 2]], [2], [2]))
|
||||
|
||||
message = ("`integrality` must contain integers 0-3 and be broadcastable "
|
||||
"to `c.shape`.")
|
||||
with pytest.raises(ValueError, match=message):
|
||||
milp([1, 2, 3], integrality=[1, 2])
|
||||
with pytest.raises(ValueError, match=message):
|
||||
milp([1, 2, 3], integrality=[1, 5, 3])
|
||||
|
||||
message = "`lb`, `ub`, and `keep_feasible` must be broadcastable."
|
||||
with pytest.raises(ValueError, match=message):
|
||||
milp([1, 2, 3], bounds=([1, 2], [3, 4, 5]))
|
||||
with pytest.raises(ValueError, match=message):
|
||||
milp([1, 2, 3], bounds=([1, 2, 3], [4, 5]))
|
||||
|
||||
message = "`bounds.lb` and `bounds.ub` must contain reals and..."
|
||||
with pytest.raises(ValueError, match=message):
|
||||
milp([1, 2, 3], bounds=([1, 2], [3, 4]))
|
||||
with pytest.raises(ValueError, match=message):
|
||||
milp([1, 2, 3], bounds=([1, 2, 3], ["3+4", 4, 5]))
|
||||
with pytest.raises(ValueError, match=message):
|
||||
milp([1, 2, 3], bounds=([1, 2, 3], [set(), 4, 5]))
|
||||
|
||||
|
||||
@pytest.mark.xfail(run=False,
|
||||
reason="Needs to be fixed in `_highs_wrapper`")
|
||||
def test_milp_options(capsys):
|
||||
# run=False now because of gh-16347
|
||||
message = "Unrecognized options detected: {'ekki'}..."
|
||||
options = {'ekki': True}
|
||||
with pytest.warns(RuntimeWarning, match=message):
|
||||
milp(1, options=options)
|
||||
|
||||
A, b, c, numbers, M = magic_square(3)
|
||||
options = {"disp": True, "presolve": False, "time_limit": 0.05}
|
||||
res = milp(c=c, constraints=(A, b, b), bounds=(0, 1), integrality=1,
|
||||
options=options)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert "Presolve is switched off" in captured.out
|
||||
assert "Time Limit Reached" in captured.out
|
||||
assert not res.success
|
||||
|
||||
|
||||
def test_result():
|
||||
A, b, c, numbers, M = magic_square(3)
|
||||
res = milp(c=c, constraints=(A, b, b), bounds=(0, 1), integrality=1)
|
||||
assert res.status == 0
|
||||
assert res.success
|
||||
msg = "Optimization terminated successfully. (HiGHS Status 7:"
|
||||
assert res.message.startswith(msg)
|
||||
assert isinstance(res.x, np.ndarray)
|
||||
assert isinstance(res.fun, float)
|
||||
assert isinstance(res.mip_node_count, int)
|
||||
assert isinstance(res.mip_dual_bound, float)
|
||||
assert isinstance(res.mip_gap, float)
|
||||
|
||||
A, b, c, numbers, M = magic_square(6)
|
||||
res = milp(c=c*0, constraints=(A, b, b), bounds=(0, 1), integrality=1,
|
||||
options={'time_limit': 0.05})
|
||||
assert res.status == 1
|
||||
assert not res.success
|
||||
msg = "Time limit reached. (HiGHS Status 13:"
|
||||
assert res.message.startswith(msg)
|
||||
assert (res.fun is res.mip_dual_bound is res.mip_gap
|
||||
is res.mip_node_count is res.x is None)
|
||||
|
||||
res = milp(1, bounds=(1, -1))
|
||||
assert res.status == 2
|
||||
assert not res.success
|
||||
msg = "The problem is infeasible. (HiGHS Status 8:"
|
||||
assert res.message.startswith(msg)
|
||||
assert (res.fun is res.mip_dual_bound is res.mip_gap
|
||||
is res.mip_node_count is res.x is None)
|
||||
|
||||
res = milp(-1)
|
||||
assert res.status == 3
|
||||
assert not res.success
|
||||
msg = "The problem is unbounded. (HiGHS Status 10:"
|
||||
assert res.message.startswith(msg)
|
||||
assert (res.fun is res.mip_dual_bound is res.mip_gap
|
||||
is res.mip_node_count is res.x is None)
|
||||
|
||||
|
||||
def test_milp_optional_args():
|
||||
# check that arguments other than `c` are indeed optional
|
||||
res = milp(1)
|
||||
assert res.fun == 0
|
||||
assert_array_equal(res.x, [0])
|
||||
|
||||
|
||||
def test_milp_1():
|
||||
# solve magic square problem
|
||||
n = 3
|
||||
A, b, c, numbers, M = magic_square(n)
|
||||
res = milp(c=c*0, constraints=(A, b, b), bounds=(0, 1), integrality=1)
|
||||
|
||||
# check that solution is a magic square
|
||||
x = np.round(res.x)
|
||||
s = (numbers.flatten() * x).reshape(n**2, n, n)
|
||||
square = np.sum(s, axis=0)
|
||||
np.testing.assert_allclose(square.sum(axis=0), M)
|
||||
np.testing.assert_allclose(square.sum(axis=1), M)
|
||||
np.testing.assert_allclose(np.diag(square).sum(), M)
|
||||
np.testing.assert_allclose(np.diag(square[:, ::-1]).sum(), M)
|
||||
|
||||
|
||||
def test_milp_2():
|
||||
# solve MIP with inequality constraints and all integer constraints
|
||||
# source: slide 5,
|
||||
# https://www.cs.upc.edu/~erodri/webpage/cps/theory/lp/milp/slides.pdf
|
||||
# also check that `milp` accepts all valid ways of specifying constraints
|
||||
c = -np.ones(2)
|
||||
A = [[-2, 2], [-8, 10]]
|
||||
b_l = [1, -np.inf]
|
||||
b_u = [np.inf, 13]
|
||||
linear_constraint = LinearConstraint(A, b_l, b_u)
|
||||
|
||||
# solve original problem
|
||||
res1 = milp(c=c, constraints=(A, b_l, b_u), integrality=True)
|
||||
res2 = milp(c=c, constraints=linear_constraint, integrality=True)
|
||||
res3 = milp(c=c, constraints=[(A, b_l, b_u)], integrality=True)
|
||||
res4 = milp(c=c, constraints=[linear_constraint], integrality=True)
|
||||
res5 = milp(c=c, integrality=True,
|
||||
constraints=[(A[:1], b_l[:1], b_u[:1]),
|
||||
(A[1:], b_l[1:], b_u[1:])])
|
||||
res6 = milp(c=c, integrality=True,
|
||||
constraints=[LinearConstraint(A[:1], b_l[:1], b_u[:1]),
|
||||
LinearConstraint(A[1:], b_l[1:], b_u[1:])])
|
||||
res7 = milp(c=c, integrality=True,
|
||||
constraints=[(A[:1], b_l[:1], b_u[:1]),
|
||||
LinearConstraint(A[1:], b_l[1:], b_u[1:])])
|
||||
xs = np.array([res1.x, res2.x, res3.x, res4.x, res5.x, res6.x, res7.x])
|
||||
funs = np.array([res1.fun, res2.fun, res3.fun,
|
||||
res4.fun, res5.fun, res6.fun, res7.fun])
|
||||
np.testing.assert_allclose(xs, np.broadcast_to([1, 2], xs.shape))
|
||||
np.testing.assert_allclose(funs, -3)
|
||||
|
||||
# solve relaxed problem
|
||||
res = milp(c=c, constraints=(A, b_l, b_u))
|
||||
np.testing.assert_allclose(res.x, [4, 4.5])
|
||||
np.testing.assert_allclose(res.fun, -8.5)
|
||||
|
||||
|
||||
def test_milp_3():
|
||||
# solve MIP with inequality constraints and all integer constraints
|
||||
# source: https://en.wikipedia.org/wiki/Integer_programming#Example
|
||||
c = [0, -1]
|
||||
A = [[-1, 1], [3, 2], [2, 3]]
|
||||
b_u = [1, 12, 12]
|
||||
b_l = np.full_like(b_u, -np.inf, dtype=np.float64)
|
||||
constraints = LinearConstraint(A, b_l, b_u)
|
||||
|
||||
integrality = np.ones_like(c)
|
||||
|
||||
# solve original problem
|
||||
res = milp(c=c, constraints=constraints, integrality=integrality)
|
||||
assert_allclose(res.fun, -2)
|
||||
# two optimal solutions possible, just need one of them
|
||||
assert np.allclose(res.x, [1, 2]) or np.allclose(res.x, [2, 2])
|
||||
|
||||
# solve relaxed problem
|
||||
res = milp(c=c, constraints=constraints)
|
||||
assert_allclose(res.fun, -2.8)
|
||||
assert_allclose(res.x, [1.8, 2.8])
|
||||
|
||||
|
||||
def test_milp_4():
|
||||
# solve MIP with inequality constraints and only one integer constraint
|
||||
# source: https://www.mathworks.com/help/optim/ug/intlinprog.html
|
||||
c = [8, 1]
|
||||
integrality = [0, 1]
|
||||
A = [[1, 2], [-4, -1], [2, 1]]
|
||||
b_l = [-14, -np.inf, -np.inf]
|
||||
b_u = [np.inf, -33, 20]
|
||||
constraints = LinearConstraint(A, b_l, b_u)
|
||||
bounds = Bounds(-np.inf, np.inf)
|
||||
|
||||
res = milp(c, integrality=integrality, bounds=bounds,
|
||||
constraints=constraints)
|
||||
assert_allclose(res.fun, 59)
|
||||
assert_allclose(res.x, [6.5, 7])
|
||||
|
||||
|
||||
def test_milp_5():
|
||||
# solve MIP with inequality and equality constraints
|
||||
# source: https://www.mathworks.com/help/optim/ug/intlinprog.html
|
||||
c = [-3, -2, -1]
|
||||
integrality = [0, 0, 1]
|
||||
lb = [0, 0, 0]
|
||||
ub = [np.inf, np.inf, 1]
|
||||
bounds = Bounds(lb, ub)
|
||||
A = [[1, 1, 1], [4, 2, 1]]
|
||||
b_l = [-np.inf, 12]
|
||||
b_u = [7, 12]
|
||||
constraints = LinearConstraint(A, b_l, b_u)
|
||||
|
||||
res = milp(c, integrality=integrality, bounds=bounds,
|
||||
constraints=constraints)
|
||||
# there are multiple solutions
|
||||
assert_allclose(res.fun, -12)
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.timeout(120) # prerelease_deps_coverage_64bit_blas job
|
||||
def test_milp_6():
|
||||
# solve a larger MIP with only equality constraints
|
||||
# source: https://www.mathworks.com/help/optim/ug/intlinprog.html
|
||||
integrality = 1
|
||||
A_eq = np.array([[22, 13, 26, 33, 21, 3, 14, 26],
|
||||
[39, 16, 22, 28, 26, 30, 23, 24],
|
||||
[18, 14, 29, 27, 30, 38, 26, 26],
|
||||
[41, 26, 28, 36, 18, 38, 16, 26]])
|
||||
b_eq = np.array([7872, 10466, 11322, 12058])
|
||||
c = np.array([2, 10, 13, 17, 7, 5, 7, 3])
|
||||
|
||||
res = milp(c=c, constraints=(A_eq, b_eq, b_eq), integrality=integrality)
|
||||
|
||||
np.testing.assert_allclose(res.fun, 1854)
|
||||
|
||||
|
||||
def test_infeasible_prob_16609():
|
||||
# Ensure presolve does not mark trivially infeasible problems
|
||||
# as Optimal -- see gh-16609
|
||||
c = [1.0, 0.0]
|
||||
integrality = [0, 1]
|
||||
|
||||
lb = [0, -np.inf]
|
||||
ub = [np.inf, np.inf]
|
||||
bounds = Bounds(lb, ub)
|
||||
|
||||
A_eq = [[0.0, 1.0]]
|
||||
b_eq = [0.5]
|
||||
constraints = LinearConstraint(A_eq, b_eq, b_eq)
|
||||
|
||||
res = milp(c, integrality=integrality, bounds=bounds,
|
||||
constraints=constraints)
|
||||
np.testing.assert_equal(res.status, 2)
|
||||
|
||||
|
||||
_msg_time = "Time limit reached. (HiGHS Status 13:"
|
||||
_msg_iter = "Iteration limit reached. (HiGHS Status 14:"
|
||||
|
||||
|
||||
@pytest.mark.skipif(np.intp(0).itemsize < 8,
|
||||
reason="Unhandled 32-bit GCC FP bug")
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.timeout(360)
|
||||
@pytest.mark.parametrize(["options", "msg"], [({"time_limit": 10}, _msg_time),
|
||||
({"node_limit": 1}, _msg_iter)])
|
||||
def test_milp_timeout_16545(options, msg):
|
||||
# Ensure solution is not thrown away if MILP solver times out
|
||||
# -- see gh-16545
|
||||
rng = np.random.default_rng(5123833489170494244)
|
||||
A = rng.integers(0, 5, size=(100, 100))
|
||||
b_lb = np.full(100, fill_value=-np.inf)
|
||||
b_ub = np.full(100, fill_value=25)
|
||||
constraints = LinearConstraint(A, b_lb, b_ub)
|
||||
variable_lb = np.zeros(100)
|
||||
variable_ub = np.ones(100)
|
||||
variable_bounds = Bounds(variable_lb, variable_ub)
|
||||
integrality = np.ones(100)
|
||||
c_vector = -np.ones(100)
|
||||
res = milp(
|
||||
c_vector,
|
||||
integrality=integrality,
|
||||
bounds=variable_bounds,
|
||||
constraints=constraints,
|
||||
options=options,
|
||||
)
|
||||
|
||||
assert res.message.startswith(msg)
|
||||
assert res["x"] is not None
|
||||
|
||||
# ensure solution is feasible
|
||||
x = res["x"]
|
||||
tol = 1e-8 # sometimes needed due to finite numerical precision
|
||||
assert np.all(b_lb - tol <= A @ x) and np.all(A @ x <= b_ub + tol)
|
||||
assert np.all(variable_lb - tol <= x) and np.all(x <= variable_ub + tol)
|
||||
assert np.allclose(x, np.round(x))
|
||||
|
||||
|
||||
def test_three_constraints_16878():
|
||||
# `milp` failed when exactly three constraints were passed
|
||||
# Ensure that this is no longer the case.
|
||||
rng = np.random.default_rng(5123833489170494244)
|
||||
A = rng.integers(0, 5, size=(6, 6))
|
||||
bl = np.full(6, fill_value=-np.inf)
|
||||
bu = np.full(6, fill_value=10)
|
||||
constraints = [LinearConstraint(A[:2], bl[:2], bu[:2]),
|
||||
LinearConstraint(A[2:4], bl[2:4], bu[2:4]),
|
||||
LinearConstraint(A[4:], bl[4:], bu[4:])]
|
||||
constraints2 = [(A[:2], bl[:2], bu[:2]),
|
||||
(A[2:4], bl[2:4], bu[2:4]),
|
||||
(A[4:], bl[4:], bu[4:])]
|
||||
lb = np.zeros(6)
|
||||
ub = np.ones(6)
|
||||
variable_bounds = Bounds(lb, ub)
|
||||
c = -np.ones(6)
|
||||
res1 = milp(c, bounds=variable_bounds, constraints=constraints)
|
||||
res2 = milp(c, bounds=variable_bounds, constraints=constraints2)
|
||||
ref = milp(c, bounds=variable_bounds, constraints=(A, bl, bu))
|
||||
assert res1.success and res2.success
|
||||
assert_allclose(res1.x, ref.x)
|
||||
assert_allclose(res2.x, ref.x)
|
||||
|
||||
|
||||
@pytest.mark.xslow
|
||||
def test_mip_rel_gap_passdown():
|
||||
# Solve problem with decreasing mip_gap to make sure mip_rel_gap decreases
|
||||
# Adapted from test_linprog::TestLinprogHiGHSMIP::test_mip_rel_gap_passdown
|
||||
# MIP taken from test_mip_6 above
|
||||
A_eq = np.array([[22, 13, 26, 33, 21, 3, 14, 26],
|
||||
[39, 16, 22, 28, 26, 30, 23, 24],
|
||||
[18, 14, 29, 27, 30, 38, 26, 26],
|
||||
[41, 26, 28, 36, 18, 38, 16, 26]])
|
||||
b_eq = np.array([7872, 10466, 11322, 12058])
|
||||
c = np.array([2, 10, 13, 17, 7, 5, 7, 3])
|
||||
|
||||
mip_rel_gaps = [0.25, 0.01, 0.001]
|
||||
sol_mip_gaps = []
|
||||
for mip_rel_gap in mip_rel_gaps:
|
||||
res = milp(c=c, bounds=(0, np.inf), constraints=(A_eq, b_eq, b_eq),
|
||||
integrality=True, options={"mip_rel_gap": mip_rel_gap})
|
||||
# assert that the solution actually has mip_gap lower than the
|
||||
# required mip_rel_gap supplied
|
||||
assert res.mip_gap <= mip_rel_gap
|
||||
# check that `res.mip_gap` is as defined in the documentation
|
||||
assert res.mip_gap == (res.fun - res.mip_dual_bound)/res.fun
|
||||
sol_mip_gaps.append(res.mip_gap)
|
||||
|
||||
# make sure that the mip_rel_gap parameter is actually doing something
|
||||
# check that differences between solution gaps are declining
|
||||
# monotonically with the mip_rel_gap parameter.
|
||||
assert np.all(np.diff(sol_mip_gaps) < 0)
|
||||
781
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_minimize_constrained.py
vendored
Normal file
781
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_minimize_constrained.py
vendored
Normal file
@@ -0,0 +1,781 @@
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
from scipy.linalg import block_diag
|
||||
from scipy.sparse import csc_matrix
|
||||
from numpy.testing import (TestCase, assert_array_almost_equal,
|
||||
assert_array_less, assert_, assert_allclose,
|
||||
suppress_warnings)
|
||||
from pytest import raises
|
||||
from scipy.optimize import (NonlinearConstraint,
|
||||
LinearConstraint,
|
||||
Bounds,
|
||||
minimize,
|
||||
BFGS,
|
||||
SR1)
|
||||
|
||||
|
||||
class Maratos:
|
||||
"""Problem 15.4 from Nocedal and Wright
|
||||
|
||||
The following optimization problem:
|
||||
minimize 2*(x[0]**2 + x[1]**2 - 1) - x[0]
|
||||
Subject to: x[0]**2 + x[1]**2 - 1 = 0
|
||||
"""
|
||||
|
||||
def __init__(self, degrees=60, constr_jac=None, constr_hess=None):
|
||||
rads = degrees/180*np.pi
|
||||
self.x0 = [np.cos(rads), np.sin(rads)]
|
||||
self.x_opt = np.array([1.0, 0.0])
|
||||
self.constr_jac = constr_jac
|
||||
self.constr_hess = constr_hess
|
||||
self.bounds = None
|
||||
|
||||
def fun(self, x):
|
||||
return 2*(x[0]**2 + x[1]**2 - 1) - x[0]
|
||||
|
||||
def grad(self, x):
|
||||
return np.array([4*x[0]-1, 4*x[1]])
|
||||
|
||||
def hess(self, x):
|
||||
return 4*np.eye(2)
|
||||
|
||||
@property
|
||||
def constr(self):
|
||||
def fun(x):
|
||||
return x[0]**2 + x[1]**2
|
||||
|
||||
if self.constr_jac is None:
|
||||
def jac(x):
|
||||
return [[2*x[0], 2*x[1]]]
|
||||
else:
|
||||
jac = self.constr_jac
|
||||
|
||||
if self.constr_hess is None:
|
||||
def hess(x, v):
|
||||
return 2*v[0]*np.eye(2)
|
||||
else:
|
||||
hess = self.constr_hess
|
||||
|
||||
return NonlinearConstraint(fun, 1, 1, jac, hess)
|
||||
|
||||
|
||||
class MaratosTestArgs:
|
||||
"""Problem 15.4 from Nocedal and Wright
|
||||
|
||||
The following optimization problem:
|
||||
minimize 2*(x[0]**2 + x[1]**2 - 1) - x[0]
|
||||
Subject to: x[0]**2 + x[1]**2 - 1 = 0
|
||||
"""
|
||||
|
||||
def __init__(self, a, b, degrees=60, constr_jac=None, constr_hess=None):
|
||||
rads = degrees/180*np.pi
|
||||
self.x0 = [np.cos(rads), np.sin(rads)]
|
||||
self.x_opt = np.array([1.0, 0.0])
|
||||
self.constr_jac = constr_jac
|
||||
self.constr_hess = constr_hess
|
||||
self.a = a
|
||||
self.b = b
|
||||
self.bounds = None
|
||||
|
||||
def _test_args(self, a, b):
|
||||
if self.a != a or self.b != b:
|
||||
raise ValueError()
|
||||
|
||||
def fun(self, x, a, b):
|
||||
self._test_args(a, b)
|
||||
return 2*(x[0]**2 + x[1]**2 - 1) - x[0]
|
||||
|
||||
def grad(self, x, a, b):
|
||||
self._test_args(a, b)
|
||||
return np.array([4*x[0]-1, 4*x[1]])
|
||||
|
||||
def hess(self, x, a, b):
|
||||
self._test_args(a, b)
|
||||
return 4*np.eye(2)
|
||||
|
||||
@property
|
||||
def constr(self):
|
||||
def fun(x):
|
||||
return x[0]**2 + x[1]**2
|
||||
|
||||
if self.constr_jac is None:
|
||||
def jac(x):
|
||||
return [[4*x[0], 4*x[1]]]
|
||||
else:
|
||||
jac = self.constr_jac
|
||||
|
||||
if self.constr_hess is None:
|
||||
def hess(x, v):
|
||||
return 2*v[0]*np.eye(2)
|
||||
else:
|
||||
hess = self.constr_hess
|
||||
|
||||
return NonlinearConstraint(fun, 1, 1, jac, hess)
|
||||
|
||||
|
||||
class MaratosGradInFunc:
|
||||
"""Problem 15.4 from Nocedal and Wright
|
||||
|
||||
The following optimization problem:
|
||||
minimize 2*(x[0]**2 + x[1]**2 - 1) - x[0]
|
||||
Subject to: x[0]**2 + x[1]**2 - 1 = 0
|
||||
"""
|
||||
|
||||
def __init__(self, degrees=60, constr_jac=None, constr_hess=None):
|
||||
rads = degrees/180*np.pi
|
||||
self.x0 = [np.cos(rads), np.sin(rads)]
|
||||
self.x_opt = np.array([1.0, 0.0])
|
||||
self.constr_jac = constr_jac
|
||||
self.constr_hess = constr_hess
|
||||
self.bounds = None
|
||||
|
||||
def fun(self, x):
|
||||
return (2*(x[0]**2 + x[1]**2 - 1) - x[0],
|
||||
np.array([4*x[0]-1, 4*x[1]]))
|
||||
|
||||
@property
|
||||
def grad(self):
|
||||
return True
|
||||
|
||||
def hess(self, x):
|
||||
return 4*np.eye(2)
|
||||
|
||||
@property
|
||||
def constr(self):
|
||||
def fun(x):
|
||||
return x[0]**2 + x[1]**2
|
||||
|
||||
if self.constr_jac is None:
|
||||
def jac(x):
|
||||
return [[4*x[0], 4*x[1]]]
|
||||
else:
|
||||
jac = self.constr_jac
|
||||
|
||||
if self.constr_hess is None:
|
||||
def hess(x, v):
|
||||
return 2*v[0]*np.eye(2)
|
||||
else:
|
||||
hess = self.constr_hess
|
||||
|
||||
return NonlinearConstraint(fun, 1, 1, jac, hess)
|
||||
|
||||
|
||||
class HyperbolicIneq:
|
||||
"""Problem 15.1 from Nocedal and Wright
|
||||
|
||||
The following optimization problem:
|
||||
minimize 1/2*(x[0] - 2)**2 + 1/2*(x[1] - 1/2)**2
|
||||
Subject to: 1/(x[0] + 1) - x[1] >= 1/4
|
||||
x[0] >= 0
|
||||
x[1] >= 0
|
||||
"""
|
||||
def __init__(self, constr_jac=None, constr_hess=None):
|
||||
self.x0 = [0, 0]
|
||||
self.x_opt = [1.952823, 0.088659]
|
||||
self.constr_jac = constr_jac
|
||||
self.constr_hess = constr_hess
|
||||
self.bounds = Bounds(0, np.inf)
|
||||
|
||||
def fun(self, x):
|
||||
return 1/2*(x[0] - 2)**2 + 1/2*(x[1] - 1/2)**2
|
||||
|
||||
def grad(self, x):
|
||||
return [x[0] - 2, x[1] - 1/2]
|
||||
|
||||
def hess(self, x):
|
||||
return np.eye(2)
|
||||
|
||||
@property
|
||||
def constr(self):
|
||||
def fun(x):
|
||||
return 1/(x[0] + 1) - x[1]
|
||||
|
||||
if self.constr_jac is None:
|
||||
def jac(x):
|
||||
return [[-1/(x[0] + 1)**2, -1]]
|
||||
else:
|
||||
jac = self.constr_jac
|
||||
|
||||
if self.constr_hess is None:
|
||||
def hess(x, v):
|
||||
return 2*v[0]*np.array([[1/(x[0] + 1)**3, 0],
|
||||
[0, 0]])
|
||||
else:
|
||||
hess = self.constr_hess
|
||||
|
||||
return NonlinearConstraint(fun, 0.25, np.inf, jac, hess)
|
||||
|
||||
|
||||
class Rosenbrock:
|
||||
"""Rosenbrock function.
|
||||
|
||||
The following optimization problem:
|
||||
minimize sum(100.0*(x[1:] - x[:-1]**2.0)**2.0 + (1 - x[:-1])**2.0)
|
||||
"""
|
||||
|
||||
def __init__(self, n=2, random_state=0):
|
||||
rng = np.random.RandomState(random_state)
|
||||
self.x0 = rng.uniform(-1, 1, n)
|
||||
self.x_opt = np.ones(n)
|
||||
self.bounds = None
|
||||
|
||||
def fun(self, x):
|
||||
x = np.asarray(x)
|
||||
r = np.sum(100.0 * (x[1:] - x[:-1]**2.0)**2.0 + (1 - x[:-1])**2.0,
|
||||
axis=0)
|
||||
return r
|
||||
|
||||
def grad(self, x):
|
||||
x = np.asarray(x)
|
||||
xm = x[1:-1]
|
||||
xm_m1 = x[:-2]
|
||||
xm_p1 = x[2:]
|
||||
der = np.zeros_like(x)
|
||||
der[1:-1] = (200 * (xm - xm_m1**2) -
|
||||
400 * (xm_p1 - xm**2) * xm - 2 * (1 - xm))
|
||||
der[0] = -400 * x[0] * (x[1] - x[0]**2) - 2 * (1 - x[0])
|
||||
der[-1] = 200 * (x[-1] - x[-2]**2)
|
||||
return der
|
||||
|
||||
def hess(self, x):
|
||||
x = np.atleast_1d(x)
|
||||
H = np.diag(-400 * x[:-1], 1) - np.diag(400 * x[:-1], -1)
|
||||
diagonal = np.zeros(len(x), dtype=x.dtype)
|
||||
diagonal[0] = 1200 * x[0]**2 - 400 * x[1] + 2
|
||||
diagonal[-1] = 200
|
||||
diagonal[1:-1] = 202 + 1200 * x[1:-1]**2 - 400 * x[2:]
|
||||
H = H + np.diag(diagonal)
|
||||
return H
|
||||
|
||||
@property
|
||||
def constr(self):
|
||||
return ()
|
||||
|
||||
|
||||
class IneqRosenbrock(Rosenbrock):
|
||||
"""Rosenbrock subject to inequality constraints.
|
||||
|
||||
The following optimization problem:
|
||||
minimize sum(100.0*(x[1] - x[0]**2)**2.0 + (1 - x[0])**2)
|
||||
subject to: x[0] + 2 x[1] <= 1
|
||||
|
||||
Taken from matlab ``fmincon`` documentation.
|
||||
"""
|
||||
def __init__(self, random_state=0):
|
||||
Rosenbrock.__init__(self, 2, random_state)
|
||||
self.x0 = [-1, -0.5]
|
||||
self.x_opt = [0.5022, 0.2489]
|
||||
self.bounds = None
|
||||
|
||||
@property
|
||||
def constr(self):
|
||||
A = [[1, 2]]
|
||||
b = 1
|
||||
return LinearConstraint(A, -np.inf, b)
|
||||
|
||||
|
||||
class BoundedRosenbrock(Rosenbrock):
|
||||
"""Rosenbrock subject to inequality constraints.
|
||||
|
||||
The following optimization problem:
|
||||
minimize sum(100.0*(x[1] - x[0]**2)**2.0 + (1 - x[0])**2)
|
||||
subject to: -2 <= x[0] <= 0
|
||||
0 <= x[1] <= 2
|
||||
|
||||
Taken from matlab ``fmincon`` documentation.
|
||||
"""
|
||||
def __init__(self, random_state=0):
|
||||
Rosenbrock.__init__(self, 2, random_state)
|
||||
self.x0 = [-0.2, 0.2]
|
||||
self.x_opt = None
|
||||
self.bounds = Bounds([-2, 0], [0, 2])
|
||||
|
||||
|
||||
class EqIneqRosenbrock(Rosenbrock):
|
||||
"""Rosenbrock subject to equality and inequality constraints.
|
||||
|
||||
The following optimization problem:
|
||||
minimize sum(100.0*(x[1] - x[0]**2)**2.0 + (1 - x[0])**2)
|
||||
subject to: x[0] + 2 x[1] <= 1
|
||||
2 x[0] + x[1] = 1
|
||||
|
||||
Taken from matlab ``fimincon`` documentation.
|
||||
"""
|
||||
def __init__(self, random_state=0):
|
||||
Rosenbrock.__init__(self, 2, random_state)
|
||||
self.x0 = [-1, -0.5]
|
||||
self.x_opt = [0.41494, 0.17011]
|
||||
self.bounds = None
|
||||
|
||||
@property
|
||||
def constr(self):
|
||||
A_ineq = [[1, 2]]
|
||||
b_ineq = 1
|
||||
A_eq = [[2, 1]]
|
||||
b_eq = 1
|
||||
return (LinearConstraint(A_ineq, -np.inf, b_ineq),
|
||||
LinearConstraint(A_eq, b_eq, b_eq))
|
||||
|
||||
|
||||
class Elec:
|
||||
"""Distribution of electrons on a sphere.
|
||||
|
||||
Problem no 2 from COPS collection [2]_. Find
|
||||
the equilibrium state distribution (of minimal
|
||||
potential) of the electrons positioned on a
|
||||
conducting sphere.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] E. D. Dolan, J. J. Mor\'{e}, and T. S. Munson,
|
||||
"Benchmarking optimization software with COPS 3.0.",
|
||||
Argonne National Lab., Argonne, IL (US), 2004.
|
||||
"""
|
||||
def __init__(self, n_electrons=200, random_state=0,
|
||||
constr_jac=None, constr_hess=None):
|
||||
self.n_electrons = n_electrons
|
||||
self.rng = np.random.RandomState(random_state)
|
||||
# Initial Guess
|
||||
phi = self.rng.uniform(0, 2 * np.pi, self.n_electrons)
|
||||
theta = self.rng.uniform(-np.pi, np.pi, self.n_electrons)
|
||||
x = np.cos(theta) * np.cos(phi)
|
||||
y = np.cos(theta) * np.sin(phi)
|
||||
z = np.sin(theta)
|
||||
self.x0 = np.hstack((x, y, z))
|
||||
self.x_opt = None
|
||||
self.constr_jac = constr_jac
|
||||
self.constr_hess = constr_hess
|
||||
self.bounds = None
|
||||
|
||||
def _get_cordinates(self, x):
|
||||
x_coord = x[:self.n_electrons]
|
||||
y_coord = x[self.n_electrons:2 * self.n_electrons]
|
||||
z_coord = x[2 * self.n_electrons:]
|
||||
return x_coord, y_coord, z_coord
|
||||
|
||||
def _compute_coordinate_deltas(self, x):
|
||||
x_coord, y_coord, z_coord = self._get_cordinates(x)
|
||||
dx = x_coord[:, None] - x_coord
|
||||
dy = y_coord[:, None] - y_coord
|
||||
dz = z_coord[:, None] - z_coord
|
||||
return dx, dy, dz
|
||||
|
||||
def fun(self, x):
|
||||
dx, dy, dz = self._compute_coordinate_deltas(x)
|
||||
with np.errstate(divide='ignore'):
|
||||
dm1 = (dx**2 + dy**2 + dz**2) ** -0.5
|
||||
dm1[np.diag_indices_from(dm1)] = 0
|
||||
return 0.5 * np.sum(dm1)
|
||||
|
||||
def grad(self, x):
|
||||
dx, dy, dz = self._compute_coordinate_deltas(x)
|
||||
|
||||
with np.errstate(divide='ignore'):
|
||||
dm3 = (dx**2 + dy**2 + dz**2) ** -1.5
|
||||
dm3[np.diag_indices_from(dm3)] = 0
|
||||
|
||||
grad_x = -np.sum(dx * dm3, axis=1)
|
||||
grad_y = -np.sum(dy * dm3, axis=1)
|
||||
grad_z = -np.sum(dz * dm3, axis=1)
|
||||
|
||||
return np.hstack((grad_x, grad_y, grad_z))
|
||||
|
||||
def hess(self, x):
|
||||
dx, dy, dz = self._compute_coordinate_deltas(x)
|
||||
d = (dx**2 + dy**2 + dz**2) ** 0.5
|
||||
|
||||
with np.errstate(divide='ignore'):
|
||||
dm3 = d ** -3
|
||||
dm5 = d ** -5
|
||||
|
||||
i = np.arange(self.n_electrons)
|
||||
dm3[i, i] = 0
|
||||
dm5[i, i] = 0
|
||||
|
||||
Hxx = dm3 - 3 * dx**2 * dm5
|
||||
Hxx[i, i] = -np.sum(Hxx, axis=1)
|
||||
|
||||
Hxy = -3 * dx * dy * dm5
|
||||
Hxy[i, i] = -np.sum(Hxy, axis=1)
|
||||
|
||||
Hxz = -3 * dx * dz * dm5
|
||||
Hxz[i, i] = -np.sum(Hxz, axis=1)
|
||||
|
||||
Hyy = dm3 - 3 * dy**2 * dm5
|
||||
Hyy[i, i] = -np.sum(Hyy, axis=1)
|
||||
|
||||
Hyz = -3 * dy * dz * dm5
|
||||
Hyz[i, i] = -np.sum(Hyz, axis=1)
|
||||
|
||||
Hzz = dm3 - 3 * dz**2 * dm5
|
||||
Hzz[i, i] = -np.sum(Hzz, axis=1)
|
||||
|
||||
H = np.vstack((
|
||||
np.hstack((Hxx, Hxy, Hxz)),
|
||||
np.hstack((Hxy, Hyy, Hyz)),
|
||||
np.hstack((Hxz, Hyz, Hzz))
|
||||
))
|
||||
|
||||
return H
|
||||
|
||||
@property
|
||||
def constr(self):
|
||||
def fun(x):
|
||||
x_coord, y_coord, z_coord = self._get_cordinates(x)
|
||||
return x_coord**2 + y_coord**2 + z_coord**2 - 1
|
||||
|
||||
if self.constr_jac is None:
|
||||
def jac(x):
|
||||
x_coord, y_coord, z_coord = self._get_cordinates(x)
|
||||
Jx = 2 * np.diag(x_coord)
|
||||
Jy = 2 * np.diag(y_coord)
|
||||
Jz = 2 * np.diag(z_coord)
|
||||
return csc_matrix(np.hstack((Jx, Jy, Jz)))
|
||||
else:
|
||||
jac = self.constr_jac
|
||||
|
||||
if self.constr_hess is None:
|
||||
def hess(x, v):
|
||||
D = 2 * np.diag(v)
|
||||
return block_diag(D, D, D)
|
||||
else:
|
||||
hess = self.constr_hess
|
||||
|
||||
return NonlinearConstraint(fun, -np.inf, 0, jac, hess)
|
||||
|
||||
|
||||
class TestTrustRegionConstr(TestCase):
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_list_of_problems(self):
|
||||
list_of_problems = [Maratos(),
|
||||
Maratos(constr_hess='2-point'),
|
||||
Maratos(constr_hess=SR1()),
|
||||
Maratos(constr_jac='2-point', constr_hess=SR1()),
|
||||
MaratosGradInFunc(),
|
||||
HyperbolicIneq(),
|
||||
HyperbolicIneq(constr_hess='3-point'),
|
||||
HyperbolicIneq(constr_hess=BFGS()),
|
||||
HyperbolicIneq(constr_jac='3-point',
|
||||
constr_hess=BFGS()),
|
||||
Rosenbrock(),
|
||||
IneqRosenbrock(),
|
||||
EqIneqRosenbrock(),
|
||||
BoundedRosenbrock(),
|
||||
Elec(n_electrons=2),
|
||||
Elec(n_electrons=2, constr_hess='2-point'),
|
||||
Elec(n_electrons=2, constr_hess=SR1()),
|
||||
Elec(n_electrons=2, constr_jac='3-point',
|
||||
constr_hess=SR1())]
|
||||
|
||||
for prob in list_of_problems:
|
||||
for grad in (prob.grad, '3-point', False):
|
||||
for hess in (prob.hess,
|
||||
'3-point',
|
||||
SR1(),
|
||||
BFGS(exception_strategy='damp_update'),
|
||||
BFGS(exception_strategy='skip_update')):
|
||||
|
||||
# Remove exceptions
|
||||
if grad in ('2-point', '3-point', 'cs', False) and \
|
||||
hess in ('2-point', '3-point', 'cs'):
|
||||
continue
|
||||
if prob.grad is True and grad in ('3-point', False):
|
||||
continue
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(UserWarning, "delta_grad == 0.0")
|
||||
result = minimize(prob.fun, prob.x0,
|
||||
method='trust-constr',
|
||||
jac=grad, hess=hess,
|
||||
bounds=prob.bounds,
|
||||
constraints=prob.constr)
|
||||
|
||||
if prob.x_opt is not None:
|
||||
assert_array_almost_equal(result.x, prob.x_opt,
|
||||
decimal=5)
|
||||
# gtol
|
||||
if result.status == 1:
|
||||
assert_array_less(result.optimality, 1e-8)
|
||||
# xtol
|
||||
if result.status == 2:
|
||||
assert_array_less(result.tr_radius, 1e-8)
|
||||
|
||||
if result.method == "tr_interior_point":
|
||||
assert_array_less(result.barrier_parameter, 1e-8)
|
||||
# max iter
|
||||
if result.status in (0, 3):
|
||||
raise RuntimeError("Invalid termination condition.")
|
||||
|
||||
def test_default_jac_and_hess(self):
|
||||
def fun(x):
|
||||
return (x - 1) ** 2
|
||||
bounds = [(-2, 2)]
|
||||
res = minimize(fun, x0=[-1.5], bounds=bounds, method='trust-constr')
|
||||
assert_array_almost_equal(res.x, 1, decimal=5)
|
||||
|
||||
def test_default_hess(self):
|
||||
def fun(x):
|
||||
return (x - 1) ** 2
|
||||
bounds = [(-2, 2)]
|
||||
res = minimize(fun, x0=[-1.5], bounds=bounds, method='trust-constr',
|
||||
jac='2-point')
|
||||
assert_array_almost_equal(res.x, 1, decimal=5)
|
||||
|
||||
def test_no_constraints(self):
|
||||
prob = Rosenbrock()
|
||||
result = minimize(prob.fun, prob.x0,
|
||||
method='trust-constr',
|
||||
jac=prob.grad, hess=prob.hess)
|
||||
result1 = minimize(prob.fun, prob.x0,
|
||||
method='L-BFGS-B',
|
||||
jac='2-point')
|
||||
|
||||
result2 = minimize(prob.fun, prob.x0,
|
||||
method='L-BFGS-B',
|
||||
jac='3-point')
|
||||
assert_array_almost_equal(result.x, prob.x_opt, decimal=5)
|
||||
assert_array_almost_equal(result1.x, prob.x_opt, decimal=5)
|
||||
assert_array_almost_equal(result2.x, prob.x_opt, decimal=5)
|
||||
|
||||
def test_hessp(self):
|
||||
prob = Maratos()
|
||||
|
||||
def hessp(x, p):
|
||||
H = prob.hess(x)
|
||||
return H.dot(p)
|
||||
|
||||
result = minimize(prob.fun, prob.x0,
|
||||
method='trust-constr',
|
||||
jac=prob.grad, hessp=hessp,
|
||||
bounds=prob.bounds,
|
||||
constraints=prob.constr)
|
||||
|
||||
if prob.x_opt is not None:
|
||||
assert_array_almost_equal(result.x, prob.x_opt, decimal=2)
|
||||
|
||||
# gtol
|
||||
if result.status == 1:
|
||||
assert_array_less(result.optimality, 1e-8)
|
||||
# xtol
|
||||
if result.status == 2:
|
||||
assert_array_less(result.tr_radius, 1e-8)
|
||||
|
||||
if result.method == "tr_interior_point":
|
||||
assert_array_less(result.barrier_parameter, 1e-8)
|
||||
# max iter
|
||||
if result.status in (0, 3):
|
||||
raise RuntimeError("Invalid termination condition.")
|
||||
|
||||
def test_args(self):
|
||||
prob = MaratosTestArgs("a", 234)
|
||||
|
||||
result = minimize(prob.fun, prob.x0, ("a", 234),
|
||||
method='trust-constr',
|
||||
jac=prob.grad, hess=prob.hess,
|
||||
bounds=prob.bounds,
|
||||
constraints=prob.constr)
|
||||
|
||||
if prob.x_opt is not None:
|
||||
assert_array_almost_equal(result.x, prob.x_opt, decimal=2)
|
||||
|
||||
# gtol
|
||||
if result.status == 1:
|
||||
assert_array_less(result.optimality, 1e-8)
|
||||
# xtol
|
||||
if result.status == 2:
|
||||
assert_array_less(result.tr_radius, 1e-8)
|
||||
if result.method == "tr_interior_point":
|
||||
assert_array_less(result.barrier_parameter, 1e-8)
|
||||
# max iter
|
||||
if result.status in (0, 3):
|
||||
raise RuntimeError("Invalid termination condition.")
|
||||
|
||||
def test_raise_exception(self):
|
||||
prob = Maratos()
|
||||
|
||||
raises(ValueError, minimize, prob.fun, prob.x0, method='trust-constr',
|
||||
jac='2-point', hess='2-point', constraints=prob.constr)
|
||||
|
||||
def test_issue_9044(self):
|
||||
# https://github.com/scipy/scipy/issues/9044
|
||||
# Test the returned `OptimizeResult` contains keys consistent with
|
||||
# other solvers.
|
||||
|
||||
def callback(x, info):
|
||||
assert_('nit' in info)
|
||||
assert_('niter' in info)
|
||||
|
||||
result = minimize(lambda x: x**2, [0], jac=lambda x: 2*x,
|
||||
hess=lambda x: 2, callback=callback,
|
||||
method='trust-constr')
|
||||
assert_(result.get('success'))
|
||||
assert_(result.get('nit', -1) == 1)
|
||||
|
||||
# Also check existence of the 'niter' attribute, for backward
|
||||
# compatibility
|
||||
assert_(result.get('niter', -1) == 1)
|
||||
|
||||
class TestEmptyConstraint(TestCase):
|
||||
"""
|
||||
Here we minimize x^2+y^2 subject to x^2-y^2>1.
|
||||
The actual minimum is at (0, 0) which fails the constraint.
|
||||
Therefore we will find a minimum on the boundary at (+/-1, 0).
|
||||
|
||||
When minimizing on the boundary, optimize uses a set of
|
||||
constraints that removes the constraint that sets that
|
||||
boundary. In our case, there's only one constraint, so
|
||||
the result is an empty constraint.
|
||||
|
||||
This tests that the empty constraint works.
|
||||
"""
|
||||
def test_empty_constraint(self):
|
||||
|
||||
def function(x):
|
||||
return x[0]**2 + x[1]**2
|
||||
|
||||
def functionjacobian(x):
|
||||
return np.array([2.*x[0], 2.*x[1]])
|
||||
|
||||
def functionhvp(x, v):
|
||||
return 2.*v
|
||||
|
||||
def constraint(x):
|
||||
return np.array([x[0]**2 - x[1]**2])
|
||||
|
||||
def constraintjacobian(x):
|
||||
return np.array([[2*x[0], -2*x[1]]])
|
||||
|
||||
def constraintlcoh(x, v):
|
||||
return np.array([[2., 0.], [0., -2.]]) * v[0]
|
||||
|
||||
constraint = NonlinearConstraint(constraint, 1., np.inf, constraintjacobian, constraintlcoh)
|
||||
|
||||
startpoint = [1., 2.]
|
||||
|
||||
bounds = Bounds([-np.inf, -np.inf], [np.inf, np.inf])
|
||||
|
||||
result = minimize(
|
||||
function,
|
||||
startpoint,
|
||||
method='trust-constr',
|
||||
jac=functionjacobian,
|
||||
hessp=functionhvp,
|
||||
constraints=[constraint],
|
||||
bounds=bounds,
|
||||
)
|
||||
|
||||
assert_array_almost_equal(abs(result.x), np.array([1, 0]), decimal=4)
|
||||
|
||||
|
||||
def test_bug_11886():
|
||||
def opt(x):
|
||||
return x[0]**2+x[1]**2
|
||||
|
||||
with np.testing.suppress_warnings() as sup:
|
||||
sup.filter(PendingDeprecationWarning)
|
||||
A = np.matrix(np.diag([1, 1]))
|
||||
lin_cons = LinearConstraint(A, -1, np.inf)
|
||||
minimize(opt, 2*[1], constraints = lin_cons) # just checking that there are no errors
|
||||
|
||||
|
||||
# Remove xfail when gh-11649 is resolved
|
||||
@pytest.mark.xfail(reason="Known bug in trust-constr; see gh-11649.",
|
||||
strict=True)
|
||||
def test_gh11649():
|
||||
bnds = Bounds(lb=[-1, -1], ub=[1, 1], keep_feasible=True)
|
||||
|
||||
def assert_inbounds(x):
|
||||
assert np.all(x >= bnds.lb)
|
||||
assert np.all(x <= bnds.ub)
|
||||
|
||||
def obj(x):
|
||||
assert_inbounds(x)
|
||||
return np.exp(x[0])*(4*x[0]**2 + 2*x[1]**2 + 4*x[0]*x[1] + 2*x[1] + 1)
|
||||
|
||||
def nce(x):
|
||||
assert_inbounds(x)
|
||||
return x[0]**2 + x[1]
|
||||
|
||||
def nci(x):
|
||||
assert_inbounds(x)
|
||||
return x[0]*x[1]
|
||||
|
||||
x0 = np.array((0.99, -0.99))
|
||||
nlcs = [NonlinearConstraint(nci, -10, np.inf),
|
||||
NonlinearConstraint(nce, 1, 1)]
|
||||
|
||||
res = minimize(fun=obj, x0=x0, method='trust-constr',
|
||||
bounds=bnds, constraints=nlcs)
|
||||
assert res.success
|
||||
assert_inbounds(res.x)
|
||||
assert nlcs[0].lb < nlcs[0].fun(res.x) < nlcs[0].ub
|
||||
assert_allclose(nce(res.x), nlcs[1].ub)
|
||||
|
||||
ref = minimize(fun=obj, x0=x0, method='slsqp',
|
||||
bounds=bnds, constraints=nlcs)
|
||||
assert_allclose(res.fun, ref.fun)
|
||||
|
||||
|
||||
class TestBoundedNelderMead:
|
||||
|
||||
@pytest.mark.parametrize('bounds, x_opt',
|
||||
[(Bounds(-np.inf, np.inf), Rosenbrock().x_opt),
|
||||
(Bounds(-np.inf, -0.8), [-0.8, -0.8]),
|
||||
(Bounds(3.0, np.inf), [3.0, 9.0]),
|
||||
(Bounds([3.0, 1.0], [4.0, 5.0]), [3., 5.]),
|
||||
])
|
||||
def test_rosen_brock_with_bounds(self, bounds, x_opt):
|
||||
prob = Rosenbrock()
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(UserWarning, "Initial guess is not within "
|
||||
"the specified bounds")
|
||||
result = minimize(prob.fun, [-10, -10],
|
||||
method='Nelder-Mead',
|
||||
bounds=bounds)
|
||||
assert np.less_equal(bounds.lb, result.x).all()
|
||||
assert np.less_equal(result.x, bounds.ub).all()
|
||||
assert np.allclose(prob.fun(result.x), result.fun)
|
||||
assert np.allclose(result.x, x_opt, atol=1.e-3)
|
||||
|
||||
def test_equal_all_bounds(self):
|
||||
prob = Rosenbrock()
|
||||
bounds = Bounds([4.0, 5.0], [4.0, 5.0])
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(UserWarning, "Initial guess is not within "
|
||||
"the specified bounds")
|
||||
result = minimize(prob.fun, [-10, 8],
|
||||
method='Nelder-Mead',
|
||||
bounds=bounds)
|
||||
assert np.allclose(result.x, [4.0, 5.0])
|
||||
|
||||
def test_equal_one_bounds(self):
|
||||
prob = Rosenbrock()
|
||||
bounds = Bounds([4.0, 5.0], [4.0, 20.0])
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(UserWarning, "Initial guess is not within "
|
||||
"the specified bounds")
|
||||
result = minimize(prob.fun, [-10, 8],
|
||||
method='Nelder-Mead',
|
||||
bounds=bounds)
|
||||
assert np.allclose(result.x, [4.0, 16.0])
|
||||
|
||||
def test_invalid_bounds(self):
|
||||
prob = Rosenbrock()
|
||||
with raises(ValueError, match=r"one of the lower bounds is greater "
|
||||
r"than an upper bound."):
|
||||
bounds = Bounds([-np.inf, 1.0], [4.0, -5.0])
|
||||
minimize(prob.fun, [-10, 3],
|
||||
method='Nelder-Mead',
|
||||
bounds=bounds)
|
||||
|
||||
@pytest.mark.xfail(reason="Failing on Azure Linux and macOS builds, "
|
||||
"see gh-13846")
|
||||
def test_outside_bounds_warning(self):
|
||||
prob = Rosenbrock()
|
||||
with raises(UserWarning, match=r"Initial guess is not within "
|
||||
r"the specified bounds"):
|
||||
bounds = Bounds([-np.inf, 1.0], [4.0, 5.0])
|
||||
minimize(prob.fun, [-10, 8],
|
||||
method='Nelder-Mead',
|
||||
bounds=bounds)
|
||||
973
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_minpack.py
vendored
Normal file
973
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_minpack.py
vendored
Normal file
@@ -0,0 +1,973 @@
|
||||
"""
|
||||
Unit tests for optimization routines from minpack.py.
|
||||
"""
|
||||
import warnings
|
||||
import pytest
|
||||
|
||||
from numpy.testing import (assert_, assert_almost_equal, assert_array_equal,
|
||||
assert_array_almost_equal, assert_allclose,
|
||||
assert_warns, suppress_warnings)
|
||||
from pytest import raises as assert_raises
|
||||
import numpy as np
|
||||
from numpy import array, float64
|
||||
from multiprocessing.pool import ThreadPool
|
||||
|
||||
from scipy import optimize, linalg
|
||||
from scipy.special import lambertw
|
||||
from scipy.optimize._minpack_py import leastsq, curve_fit, fixed_point
|
||||
from scipy.optimize import OptimizeWarning
|
||||
from scipy.optimize._minimize import Bounds
|
||||
|
||||
|
||||
class ReturnShape:
|
||||
"""This class exists to create a callable that does not have a '__name__' attribute.
|
||||
|
||||
__init__ takes the argument 'shape', which should be a tuple of ints. When an instance
|
||||
is called with a single argument 'x', it returns numpy.ones(shape).
|
||||
"""
|
||||
|
||||
def __init__(self, shape):
|
||||
self.shape = shape
|
||||
|
||||
def __call__(self, x):
|
||||
return np.ones(self.shape)
|
||||
|
||||
|
||||
def dummy_func(x, shape):
|
||||
"""A function that returns an array of ones of the given shape.
|
||||
`x` is ignored.
|
||||
"""
|
||||
return np.ones(shape)
|
||||
|
||||
|
||||
def sequence_parallel(fs):
|
||||
with ThreadPool(len(fs)) as pool:
|
||||
return pool.map(lambda f: f(), fs)
|
||||
|
||||
|
||||
# Function and Jacobian for tests of solvers for systems of nonlinear
|
||||
# equations
|
||||
|
||||
|
||||
def pressure_network(flow_rates, Qtot, k):
|
||||
"""Evaluate non-linear equation system representing
|
||||
the pressures and flows in a system of n parallel pipes::
|
||||
|
||||
f_i = P_i - P_0, for i = 1..n
|
||||
f_0 = sum(Q_i) - Qtot
|
||||
|
||||
where Q_i is the flow rate in pipe i and P_i the pressure in that pipe.
|
||||
Pressure is modeled as a P=kQ**2 where k is a valve coefficient and
|
||||
Q is the flow rate.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
flow_rates : float
|
||||
A 1-D array of n flow rates [kg/s].
|
||||
k : float
|
||||
A 1-D array of n valve coefficients [1/kg m].
|
||||
Qtot : float
|
||||
A scalar, the total input flow rate [kg/s].
|
||||
|
||||
Returns
|
||||
-------
|
||||
F : float
|
||||
A 1-D array, F[i] == f_i.
|
||||
|
||||
"""
|
||||
P = k * flow_rates**2
|
||||
F = np.hstack((P[1:] - P[0], flow_rates.sum() - Qtot))
|
||||
return F
|
||||
|
||||
|
||||
def pressure_network_jacobian(flow_rates, Qtot, k):
|
||||
"""Return the jacobian of the equation system F(flow_rates)
|
||||
computed by `pressure_network` with respect to
|
||||
*flow_rates*. See `pressure_network` for the detailed
|
||||
description of parrameters.
|
||||
|
||||
Returns
|
||||
-------
|
||||
jac : float
|
||||
*n* by *n* matrix ``df_i/dQ_i`` where ``n = len(flow_rates)``
|
||||
and *f_i* and *Q_i* are described in the doc for `pressure_network`
|
||||
"""
|
||||
n = len(flow_rates)
|
||||
pdiff = np.diag(flow_rates[1:] * 2 * k[1:] - 2 * flow_rates[0] * k[0])
|
||||
|
||||
jac = np.empty((n, n))
|
||||
jac[:n-1, :n-1] = pdiff * 0
|
||||
jac[:n-1, n-1] = 0
|
||||
jac[n-1, :] = np.ones(n)
|
||||
|
||||
return jac
|
||||
|
||||
|
||||
def pressure_network_fun_and_grad(flow_rates, Qtot, k):
|
||||
return (pressure_network(flow_rates, Qtot, k),
|
||||
pressure_network_jacobian(flow_rates, Qtot, k))
|
||||
|
||||
|
||||
class TestFSolve:
|
||||
def test_pressure_network_no_gradient(self):
|
||||
# fsolve without gradient, equal pipes -> equal flows.
|
||||
k = np.full(4, 0.5)
|
||||
Qtot = 4
|
||||
initial_guess = array([2., 0., 2., 0.])
|
||||
final_flows, info, ier, mesg = optimize.fsolve(
|
||||
pressure_network, initial_guess, args=(Qtot, k),
|
||||
full_output=True)
|
||||
assert_array_almost_equal(final_flows, np.ones(4))
|
||||
assert_(ier == 1, mesg)
|
||||
|
||||
def test_pressure_network_with_gradient(self):
|
||||
# fsolve with gradient, equal pipes -> equal flows
|
||||
k = np.full(4, 0.5)
|
||||
Qtot = 4
|
||||
initial_guess = array([2., 0., 2., 0.])
|
||||
final_flows = optimize.fsolve(
|
||||
pressure_network, initial_guess, args=(Qtot, k),
|
||||
fprime=pressure_network_jacobian)
|
||||
assert_array_almost_equal(final_flows, np.ones(4))
|
||||
|
||||
def test_wrong_shape_func_callable(self):
|
||||
func = ReturnShape(1)
|
||||
# x0 is a list of two elements, but func will return an array with
|
||||
# length 1, so this should result in a TypeError.
|
||||
x0 = [1.5, 2.0]
|
||||
assert_raises(TypeError, optimize.fsolve, func, x0)
|
||||
|
||||
def test_wrong_shape_func_function(self):
|
||||
# x0 is a list of two elements, but func will return an array with
|
||||
# length 1, so this should result in a TypeError.
|
||||
x0 = [1.5, 2.0]
|
||||
assert_raises(TypeError, optimize.fsolve, dummy_func, x0, args=((1,),))
|
||||
|
||||
def test_wrong_shape_fprime_callable(self):
|
||||
func = ReturnShape(1)
|
||||
deriv_func = ReturnShape((2,2))
|
||||
assert_raises(TypeError, optimize.fsolve, func, x0=[0,1], fprime=deriv_func)
|
||||
|
||||
def test_wrong_shape_fprime_function(self):
|
||||
func = lambda x: dummy_func(x, (2,))
|
||||
deriv_func = lambda x: dummy_func(x, (3,3))
|
||||
assert_raises(TypeError, optimize.fsolve, func, x0=[0,1], fprime=deriv_func)
|
||||
|
||||
def test_func_can_raise(self):
|
||||
def func(*args):
|
||||
raise ValueError('I raised')
|
||||
|
||||
with assert_raises(ValueError, match='I raised'):
|
||||
optimize.fsolve(func, x0=[0])
|
||||
|
||||
def test_Dfun_can_raise(self):
|
||||
func = lambda x: x - np.array([10])
|
||||
|
||||
def deriv_func(*args):
|
||||
raise ValueError('I raised')
|
||||
|
||||
with assert_raises(ValueError, match='I raised'):
|
||||
optimize.fsolve(func, x0=[0], fprime=deriv_func)
|
||||
|
||||
def test_float32(self):
|
||||
func = lambda x: np.array([x[0] - 100, x[1] - 1000], dtype=np.float32)**2
|
||||
p = optimize.fsolve(func, np.array([1, 1], np.float32))
|
||||
assert_allclose(func(p), [0, 0], atol=1e-3)
|
||||
|
||||
def test_reentrant_func(self):
|
||||
def func(*args):
|
||||
self.test_pressure_network_no_gradient()
|
||||
return pressure_network(*args)
|
||||
|
||||
# fsolve without gradient, equal pipes -> equal flows.
|
||||
k = np.full(4, 0.5)
|
||||
Qtot = 4
|
||||
initial_guess = array([2., 0., 2., 0.])
|
||||
final_flows, info, ier, mesg = optimize.fsolve(
|
||||
func, initial_guess, args=(Qtot, k),
|
||||
full_output=True)
|
||||
assert_array_almost_equal(final_flows, np.ones(4))
|
||||
assert_(ier == 1, mesg)
|
||||
|
||||
def test_reentrant_Dfunc(self):
|
||||
def deriv_func(*args):
|
||||
self.test_pressure_network_with_gradient()
|
||||
return pressure_network_jacobian(*args)
|
||||
|
||||
# fsolve with gradient, equal pipes -> equal flows
|
||||
k = np.full(4, 0.5)
|
||||
Qtot = 4
|
||||
initial_guess = array([2., 0., 2., 0.])
|
||||
final_flows = optimize.fsolve(
|
||||
pressure_network, initial_guess, args=(Qtot, k),
|
||||
fprime=deriv_func)
|
||||
assert_array_almost_equal(final_flows, np.ones(4))
|
||||
|
||||
def test_concurrent_no_gradient(self):
|
||||
v = sequence_parallel([self.test_pressure_network_no_gradient] * 10)
|
||||
assert all([result is None for result in v])
|
||||
|
||||
def test_concurrent_with_gradient(self):
|
||||
v = sequence_parallel([self.test_pressure_network_with_gradient] * 10)
|
||||
assert all([result is None for result in v])
|
||||
|
||||
|
||||
class TestRootHybr:
|
||||
def test_pressure_network_no_gradient(self):
|
||||
# root/hybr without gradient, equal pipes -> equal flows
|
||||
k = np.full(4, 0.5)
|
||||
Qtot = 4
|
||||
initial_guess = array([2., 0., 2., 0.])
|
||||
final_flows = optimize.root(pressure_network, initial_guess,
|
||||
method='hybr', args=(Qtot, k)).x
|
||||
assert_array_almost_equal(final_flows, np.ones(4))
|
||||
|
||||
def test_pressure_network_with_gradient(self):
|
||||
# root/hybr with gradient, equal pipes -> equal flows
|
||||
k = np.full(4, 0.5)
|
||||
Qtot = 4
|
||||
initial_guess = array([[2., 0., 2., 0.]])
|
||||
final_flows = optimize.root(pressure_network, initial_guess,
|
||||
args=(Qtot, k), method='hybr',
|
||||
jac=pressure_network_jacobian).x
|
||||
assert_array_almost_equal(final_flows, np.ones(4))
|
||||
|
||||
def test_pressure_network_with_gradient_combined(self):
|
||||
# root/hybr with gradient and function combined, equal pipes -> equal
|
||||
# flows
|
||||
k = np.full(4, 0.5)
|
||||
Qtot = 4
|
||||
initial_guess = array([2., 0., 2., 0.])
|
||||
final_flows = optimize.root(pressure_network_fun_and_grad,
|
||||
initial_guess, args=(Qtot, k),
|
||||
method='hybr', jac=True).x
|
||||
assert_array_almost_equal(final_flows, np.ones(4))
|
||||
|
||||
|
||||
class TestRootLM:
|
||||
def test_pressure_network_no_gradient(self):
|
||||
# root/lm without gradient, equal pipes -> equal flows
|
||||
k = np.full(4, 0.5)
|
||||
Qtot = 4
|
||||
initial_guess = array([2., 0., 2., 0.])
|
||||
final_flows = optimize.root(pressure_network, initial_guess,
|
||||
method='lm', args=(Qtot, k)).x
|
||||
assert_array_almost_equal(final_flows, np.ones(4))
|
||||
|
||||
|
||||
class TestLeastSq:
|
||||
def setup_method(self):
|
||||
x = np.linspace(0, 10, 40)
|
||||
a,b,c = 3.1, 42, -304.2
|
||||
self.x = x
|
||||
self.abc = a,b,c
|
||||
y_true = a*x**2 + b*x + c
|
||||
np.random.seed(0)
|
||||
self.y_meas = y_true + 0.01*np.random.standard_normal(y_true.shape)
|
||||
|
||||
def residuals(self, p, y, x):
|
||||
a,b,c = p
|
||||
err = y-(a*x**2 + b*x + c)
|
||||
return err
|
||||
|
||||
def residuals_jacobian(self, _p, _y, x):
|
||||
return -np.vstack([x**2, x, np.ones_like(x)]).T
|
||||
|
||||
def test_basic(self):
|
||||
p0 = array([0,0,0])
|
||||
params_fit, ier = leastsq(self.residuals, p0,
|
||||
args=(self.y_meas, self.x))
|
||||
assert_(ier in (1,2,3,4), 'solution not found (ier=%d)' % ier)
|
||||
# low precision due to random
|
||||
assert_array_almost_equal(params_fit, self.abc, decimal=2)
|
||||
|
||||
def test_basic_with_gradient(self):
|
||||
p0 = array([0,0,0])
|
||||
params_fit, ier = leastsq(self.residuals, p0,
|
||||
args=(self.y_meas, self.x),
|
||||
Dfun=self.residuals_jacobian)
|
||||
assert_(ier in (1,2,3,4), 'solution not found (ier=%d)' % ier)
|
||||
# low precision due to random
|
||||
assert_array_almost_equal(params_fit, self.abc, decimal=2)
|
||||
|
||||
def test_full_output(self):
|
||||
p0 = array([[0,0,0]])
|
||||
full_output = leastsq(self.residuals, p0,
|
||||
args=(self.y_meas, self.x),
|
||||
full_output=True)
|
||||
params_fit, cov_x, infodict, mesg, ier = full_output
|
||||
assert_(ier in (1,2,3,4), 'solution not found: %s' % mesg)
|
||||
|
||||
def test_input_untouched(self):
|
||||
p0 = array([0,0,0],dtype=float64)
|
||||
p0_copy = array(p0, copy=True)
|
||||
full_output = leastsq(self.residuals, p0,
|
||||
args=(self.y_meas, self.x),
|
||||
full_output=True)
|
||||
params_fit, cov_x, infodict, mesg, ier = full_output
|
||||
assert_(ier in (1,2,3,4), 'solution not found: %s' % mesg)
|
||||
assert_array_equal(p0, p0_copy)
|
||||
|
||||
def test_wrong_shape_func_callable(self):
|
||||
func = ReturnShape(1)
|
||||
# x0 is a list of two elements, but func will return an array with
|
||||
# length 1, so this should result in a TypeError.
|
||||
x0 = [1.5, 2.0]
|
||||
assert_raises(TypeError, optimize.leastsq, func, x0)
|
||||
|
||||
def test_wrong_shape_func_function(self):
|
||||
# x0 is a list of two elements, but func will return an array with
|
||||
# length 1, so this should result in a TypeError.
|
||||
x0 = [1.5, 2.0]
|
||||
assert_raises(TypeError, optimize.leastsq, dummy_func, x0, args=((1,),))
|
||||
|
||||
def test_wrong_shape_Dfun_callable(self):
|
||||
func = ReturnShape(1)
|
||||
deriv_func = ReturnShape((2,2))
|
||||
assert_raises(TypeError, optimize.leastsq, func, x0=[0,1], Dfun=deriv_func)
|
||||
|
||||
def test_wrong_shape_Dfun_function(self):
|
||||
func = lambda x: dummy_func(x, (2,))
|
||||
deriv_func = lambda x: dummy_func(x, (3,3))
|
||||
assert_raises(TypeError, optimize.leastsq, func, x0=[0,1], Dfun=deriv_func)
|
||||
|
||||
def test_float32(self):
|
||||
# Regression test for gh-1447
|
||||
def func(p,x,y):
|
||||
q = p[0]*np.exp(-(x-p[1])**2/(2.0*p[2]**2))+p[3]
|
||||
return q - y
|
||||
|
||||
x = np.array([1.475,1.429,1.409,1.419,1.455,1.519,1.472, 1.368,1.286,
|
||||
1.231], dtype=np.float32)
|
||||
y = np.array([0.0168,0.0193,0.0211,0.0202,0.0171,0.0151,0.0185,0.0258,
|
||||
0.034,0.0396], dtype=np.float32)
|
||||
p0 = np.array([1.0,1.0,1.0,1.0])
|
||||
p1, success = optimize.leastsq(func, p0, args=(x,y))
|
||||
|
||||
assert_(success in [1,2,3,4])
|
||||
assert_((func(p1,x,y)**2).sum() < 1e-4 * (func(p0,x,y)**2).sum())
|
||||
|
||||
def test_func_can_raise(self):
|
||||
def func(*args):
|
||||
raise ValueError('I raised')
|
||||
|
||||
with assert_raises(ValueError, match='I raised'):
|
||||
optimize.leastsq(func, x0=[0])
|
||||
|
||||
def test_Dfun_can_raise(self):
|
||||
func = lambda x: x - np.array([10])
|
||||
|
||||
def deriv_func(*args):
|
||||
raise ValueError('I raised')
|
||||
|
||||
with assert_raises(ValueError, match='I raised'):
|
||||
optimize.leastsq(func, x0=[0], Dfun=deriv_func)
|
||||
|
||||
def test_reentrant_func(self):
|
||||
def func(*args):
|
||||
self.test_basic()
|
||||
return self.residuals(*args)
|
||||
|
||||
p0 = array([0,0,0])
|
||||
params_fit, ier = leastsq(func, p0,
|
||||
args=(self.y_meas, self.x))
|
||||
assert_(ier in (1,2,3,4), 'solution not found (ier=%d)' % ier)
|
||||
# low precision due to random
|
||||
assert_array_almost_equal(params_fit, self.abc, decimal=2)
|
||||
|
||||
def test_reentrant_Dfun(self):
|
||||
def deriv_func(*args):
|
||||
self.test_basic()
|
||||
return self.residuals_jacobian(*args)
|
||||
|
||||
p0 = array([0,0,0])
|
||||
params_fit, ier = leastsq(self.residuals, p0,
|
||||
args=(self.y_meas, self.x),
|
||||
Dfun=deriv_func)
|
||||
assert_(ier in (1,2,3,4), 'solution not found (ier=%d)' % ier)
|
||||
# low precision due to random
|
||||
assert_array_almost_equal(params_fit, self.abc, decimal=2)
|
||||
|
||||
def test_concurrent_no_gradient(self):
|
||||
v = sequence_parallel([self.test_basic] * 10)
|
||||
assert all([result is None for result in v])
|
||||
|
||||
def test_concurrent_with_gradient(self):
|
||||
v = sequence_parallel([self.test_basic_with_gradient] * 10)
|
||||
assert all([result is None for result in v])
|
||||
|
||||
def test_func_input_output_length_check(self):
|
||||
|
||||
def func(x):
|
||||
return 2 * (x[0] - 3) ** 2 + 1
|
||||
|
||||
with assert_raises(TypeError,
|
||||
match='Improper input: func input vector length N='):
|
||||
optimize.leastsq(func, x0=[0, 1])
|
||||
|
||||
|
||||
class TestCurveFit:
|
||||
def setup_method(self):
|
||||
self.y = array([1.0, 3.2, 9.5, 13.7])
|
||||
self.x = array([1.0, 2.0, 3.0, 4.0])
|
||||
|
||||
def test_one_argument(self):
|
||||
def func(x,a):
|
||||
return x**a
|
||||
popt, pcov = curve_fit(func, self.x, self.y)
|
||||
assert_(len(popt) == 1)
|
||||
assert_(pcov.shape == (1,1))
|
||||
assert_almost_equal(popt[0], 1.9149, decimal=4)
|
||||
assert_almost_equal(pcov[0,0], 0.0016, decimal=4)
|
||||
|
||||
# Test if we get the same with full_output. Regression test for #1415.
|
||||
# Also test if check_finite can be turned off.
|
||||
res = curve_fit(func, self.x, self.y,
|
||||
full_output=1, check_finite=False)
|
||||
(popt2, pcov2, infodict, errmsg, ier) = res
|
||||
assert_array_almost_equal(popt, popt2)
|
||||
|
||||
def test_two_argument(self):
|
||||
def func(x, a, b):
|
||||
return b*x**a
|
||||
popt, pcov = curve_fit(func, self.x, self.y)
|
||||
assert_(len(popt) == 2)
|
||||
assert_(pcov.shape == (2,2))
|
||||
assert_array_almost_equal(popt, [1.7989, 1.1642], decimal=4)
|
||||
assert_array_almost_equal(pcov, [[0.0852, -0.1260], [-0.1260, 0.1912]],
|
||||
decimal=4)
|
||||
|
||||
def test_func_is_classmethod(self):
|
||||
class test_self:
|
||||
"""This class tests if curve_fit passes the correct number of
|
||||
arguments when the model function is a class instance method.
|
||||
"""
|
||||
|
||||
def func(self, x, a, b):
|
||||
return b * x**a
|
||||
|
||||
test_self_inst = test_self()
|
||||
popt, pcov = curve_fit(test_self_inst.func, self.x, self.y)
|
||||
assert_(pcov.shape == (2,2))
|
||||
assert_array_almost_equal(popt, [1.7989, 1.1642], decimal=4)
|
||||
assert_array_almost_equal(pcov, [[0.0852, -0.1260], [-0.1260, 0.1912]],
|
||||
decimal=4)
|
||||
|
||||
def test_regression_2639(self):
|
||||
# This test fails if epsfcn in leastsq is too large.
|
||||
x = [574.14200000000005, 574.154, 574.16499999999996,
|
||||
574.17700000000002, 574.18799999999999, 574.19899999999996,
|
||||
574.21100000000001, 574.22199999999998, 574.23400000000004,
|
||||
574.245]
|
||||
y = [859.0, 997.0, 1699.0, 2604.0, 2013.0, 1964.0, 2435.0,
|
||||
1550.0, 949.0, 841.0]
|
||||
guess = [574.1861428571428, 574.2155714285715, 1302.0, 1302.0,
|
||||
0.0035019999999983615, 859.0]
|
||||
good = [5.74177150e+02, 5.74209188e+02, 1.74187044e+03, 1.58646166e+03,
|
||||
1.0068462e-02, 8.57450661e+02]
|
||||
|
||||
def f_double_gauss(x, x0, x1, A0, A1, sigma, c):
|
||||
return (A0*np.exp(-(x-x0)**2/(2.*sigma**2))
|
||||
+ A1*np.exp(-(x-x1)**2/(2.*sigma**2)) + c)
|
||||
popt, pcov = curve_fit(f_double_gauss, x, y, guess, maxfev=10000)
|
||||
assert_allclose(popt, good, rtol=1e-5)
|
||||
|
||||
def test_pcov(self):
|
||||
xdata = np.array([0, 1, 2, 3, 4, 5])
|
||||
ydata = np.array([1, 1, 5, 7, 8, 12])
|
||||
sigma = np.array([1, 2, 1, 2, 1, 2])
|
||||
|
||||
def f(x, a, b):
|
||||
return a*x + b
|
||||
|
||||
for method in ['lm', 'trf', 'dogbox']:
|
||||
popt, pcov = curve_fit(f, xdata, ydata, p0=[2, 0], sigma=sigma,
|
||||
method=method)
|
||||
perr_scaled = np.sqrt(np.diag(pcov))
|
||||
assert_allclose(perr_scaled, [0.20659803, 0.57204404], rtol=1e-3)
|
||||
|
||||
popt, pcov = curve_fit(f, xdata, ydata, p0=[2, 0], sigma=3*sigma,
|
||||
method=method)
|
||||
perr_scaled = np.sqrt(np.diag(pcov))
|
||||
assert_allclose(perr_scaled, [0.20659803, 0.57204404], rtol=1e-3)
|
||||
|
||||
popt, pcov = curve_fit(f, xdata, ydata, p0=[2, 0], sigma=sigma,
|
||||
absolute_sigma=True, method=method)
|
||||
perr = np.sqrt(np.diag(pcov))
|
||||
assert_allclose(perr, [0.30714756, 0.85045308], rtol=1e-3)
|
||||
|
||||
popt, pcov = curve_fit(f, xdata, ydata, p0=[2, 0], sigma=3*sigma,
|
||||
absolute_sigma=True, method=method)
|
||||
perr = np.sqrt(np.diag(pcov))
|
||||
assert_allclose(perr, [3*0.30714756, 3*0.85045308], rtol=1e-3)
|
||||
|
||||
# infinite variances
|
||||
|
||||
def f_flat(x, a, b):
|
||||
return a*x
|
||||
|
||||
pcov_expected = np.array([np.inf]*4).reshape(2, 2)
|
||||
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(OptimizeWarning,
|
||||
"Covariance of the parameters could not be estimated")
|
||||
popt, pcov = curve_fit(f_flat, xdata, ydata, p0=[2, 0], sigma=sigma)
|
||||
popt1, pcov1 = curve_fit(f, xdata[:2], ydata[:2], p0=[2, 0])
|
||||
|
||||
assert_(pcov.shape == (2, 2))
|
||||
assert_array_equal(pcov, pcov_expected)
|
||||
|
||||
assert_(pcov1.shape == (2, 2))
|
||||
assert_array_equal(pcov1, pcov_expected)
|
||||
|
||||
def test_array_like(self):
|
||||
# Test sequence input. Regression test for gh-3037.
|
||||
def f_linear(x, a, b):
|
||||
return a*x + b
|
||||
|
||||
x = [1, 2, 3, 4]
|
||||
y = [3, 5, 7, 9]
|
||||
assert_allclose(curve_fit(f_linear, x, y)[0], [2, 1], atol=1e-10)
|
||||
|
||||
def test_indeterminate_covariance(self):
|
||||
# Test that a warning is returned when pcov is indeterminate
|
||||
xdata = np.array([1, 2, 3, 4, 5, 6])
|
||||
ydata = np.array([1, 2, 3, 4, 5.5, 6])
|
||||
assert_warns(OptimizeWarning, curve_fit,
|
||||
lambda x, a, b: a*x, xdata, ydata)
|
||||
|
||||
def test_NaN_handling(self):
|
||||
# Test for correct handling of NaNs in input data: gh-3422
|
||||
|
||||
# create input with NaNs
|
||||
xdata = np.array([1, np.nan, 3])
|
||||
ydata = np.array([1, 2, 3])
|
||||
|
||||
assert_raises(ValueError, curve_fit,
|
||||
lambda x, a, b: a*x + b, xdata, ydata)
|
||||
assert_raises(ValueError, curve_fit,
|
||||
lambda x, a, b: a*x + b, ydata, xdata)
|
||||
|
||||
assert_raises(ValueError, curve_fit, lambda x, a, b: a*x + b,
|
||||
xdata, ydata, **{"check_finite": True})
|
||||
|
||||
def test_empty_inputs(self):
|
||||
# Test both with and without bounds (regression test for gh-9864)
|
||||
assert_raises(ValueError, curve_fit, lambda x, a: a*x, [], [])
|
||||
assert_raises(ValueError, curve_fit, lambda x, a: a*x, [], [],
|
||||
bounds=(1, 2))
|
||||
assert_raises(ValueError, curve_fit, lambda x, a: a*x, [1], [])
|
||||
assert_raises(ValueError, curve_fit, lambda x, a: a*x, [2], [],
|
||||
bounds=(1, 2))
|
||||
|
||||
def test_function_zero_params(self):
|
||||
# Fit args is zero, so "Unable to determine number of fit parameters."
|
||||
assert_raises(ValueError, curve_fit, lambda x: x, [1, 2], [3, 4])
|
||||
|
||||
def test_None_x(self): # Added in GH10196
|
||||
popt, pcov = curve_fit(lambda _, a: a * np.arange(10),
|
||||
None, 2 * np.arange(10))
|
||||
assert_allclose(popt, [2.])
|
||||
|
||||
def test_method_argument(self):
|
||||
def f(x, a, b):
|
||||
return a * np.exp(-b*x)
|
||||
|
||||
xdata = np.linspace(0, 1, 11)
|
||||
ydata = f(xdata, 2., 2.)
|
||||
|
||||
for method in ['trf', 'dogbox', 'lm', None]:
|
||||
popt, pcov = curve_fit(f, xdata, ydata, method=method)
|
||||
assert_allclose(popt, [2., 2.])
|
||||
|
||||
assert_raises(ValueError, curve_fit, f, xdata, ydata, method='unknown')
|
||||
|
||||
def test_full_output(self):
|
||||
def f(x, a, b):
|
||||
return a * np.exp(-b * x)
|
||||
|
||||
xdata = np.linspace(0, 1, 11)
|
||||
ydata = f(xdata, 2., 2.)
|
||||
|
||||
for method in ['trf', 'dogbox', 'lm', None]:
|
||||
popt, pcov, infodict, errmsg, ier = curve_fit(
|
||||
f, xdata, ydata, method=method, full_output=True)
|
||||
assert_allclose(popt, [2., 2.])
|
||||
assert "nfev" in infodict
|
||||
assert "fvec" in infodict
|
||||
if method == 'lm' or method is None:
|
||||
assert "fjac" in infodict
|
||||
assert "ipvt" in infodict
|
||||
assert "qtf" in infodict
|
||||
assert isinstance(errmsg, str)
|
||||
assert ier in (1, 2, 3, 4)
|
||||
|
||||
def test_bounds(self):
|
||||
def f(x, a, b):
|
||||
return a * np.exp(-b*x)
|
||||
|
||||
xdata = np.linspace(0, 1, 11)
|
||||
ydata = f(xdata, 2., 2.)
|
||||
|
||||
# The minimum w/out bounds is at [2., 2.],
|
||||
# and with bounds it's at [1.5, smth].
|
||||
lb = [1., 0]
|
||||
ub = [1.5, 3.]
|
||||
|
||||
# Test that both variants of the bounds yield the same result
|
||||
bounds = (lb, ub)
|
||||
bounds_class = Bounds(lb, ub)
|
||||
for method in [None, 'trf', 'dogbox']:
|
||||
popt, pcov = curve_fit(f, xdata, ydata, bounds=bounds,
|
||||
method=method)
|
||||
assert_allclose(popt[0], 1.5)
|
||||
|
||||
popt_class, pcov_class = curve_fit(f, xdata, ydata,
|
||||
bounds=bounds_class,
|
||||
method=method)
|
||||
assert_allclose(popt_class, popt)
|
||||
|
||||
# With bounds, the starting estimate is feasible.
|
||||
popt, pcov = curve_fit(f, xdata, ydata, method='trf',
|
||||
bounds=([0., 0], [0.6, np.inf]))
|
||||
assert_allclose(popt[0], 0.6)
|
||||
|
||||
# method='lm' doesn't support bounds.
|
||||
assert_raises(ValueError, curve_fit, f, xdata, ydata, bounds=bounds,
|
||||
method='lm')
|
||||
|
||||
def test_bounds_p0(self):
|
||||
# This test is for issue #5719. The problem was that an initial guess
|
||||
# was ignored when 'trf' or 'dogbox' methods were invoked.
|
||||
def f(x, a):
|
||||
return np.sin(x + a)
|
||||
|
||||
xdata = np.linspace(-2*np.pi, 2*np.pi, 40)
|
||||
ydata = np.sin(xdata)
|
||||
bounds = (-3 * np.pi, 3 * np.pi)
|
||||
for method in ['trf', 'dogbox']:
|
||||
popt_1, _ = curve_fit(f, xdata, ydata, p0=2.1*np.pi)
|
||||
popt_2, _ = curve_fit(f, xdata, ydata, p0=2.1*np.pi,
|
||||
bounds=bounds, method=method)
|
||||
|
||||
# If the initial guess is ignored, then popt_2 would be close 0.
|
||||
assert_allclose(popt_1, popt_2)
|
||||
|
||||
def test_jac(self):
|
||||
# Test that Jacobian callable is handled correctly and
|
||||
# weighted if sigma is provided.
|
||||
def f(x, a, b):
|
||||
return a * np.exp(-b*x)
|
||||
|
||||
def jac(x, a, b):
|
||||
e = np.exp(-b*x)
|
||||
return np.vstack((e, -a * x * e)).T
|
||||
|
||||
xdata = np.linspace(0, 1, 11)
|
||||
ydata = f(xdata, 2., 2.)
|
||||
|
||||
# Test numerical options for least_squares backend.
|
||||
for method in ['trf', 'dogbox']:
|
||||
for scheme in ['2-point', '3-point', 'cs']:
|
||||
popt, pcov = curve_fit(f, xdata, ydata, jac=scheme,
|
||||
method=method)
|
||||
assert_allclose(popt, [2, 2])
|
||||
|
||||
# Test the analytic option.
|
||||
for method in ['lm', 'trf', 'dogbox']:
|
||||
popt, pcov = curve_fit(f, xdata, ydata, method=method, jac=jac)
|
||||
assert_allclose(popt, [2, 2])
|
||||
|
||||
# Now add an outlier and provide sigma.
|
||||
ydata[5] = 100
|
||||
sigma = np.ones(xdata.shape[0])
|
||||
sigma[5] = 200
|
||||
for method in ['lm', 'trf', 'dogbox']:
|
||||
popt, pcov = curve_fit(f, xdata, ydata, sigma=sigma, method=method,
|
||||
jac=jac)
|
||||
# Still the optimization process is influenced somehow,
|
||||
# have to set rtol=1e-3.
|
||||
assert_allclose(popt, [2, 2], rtol=1e-3)
|
||||
|
||||
def test_maxfev_and_bounds(self):
|
||||
# gh-6340: with no bounds, curve_fit accepts parameter maxfev (via leastsq)
|
||||
# but with bounds, the parameter is `max_nfev` (via least_squares)
|
||||
x = np.arange(0, 10)
|
||||
y = 2*x
|
||||
popt1, _ = curve_fit(lambda x,p: p*x, x, y, bounds=(0, 3), maxfev=100)
|
||||
popt2, _ = curve_fit(lambda x,p: p*x, x, y, bounds=(0, 3), max_nfev=100)
|
||||
|
||||
assert_allclose(popt1, 2, atol=1e-14)
|
||||
assert_allclose(popt2, 2, atol=1e-14)
|
||||
|
||||
def test_curvefit_simplecovariance(self):
|
||||
|
||||
def func(x, a, b):
|
||||
return a * np.exp(-b*x)
|
||||
|
||||
def jac(x, a, b):
|
||||
e = np.exp(-b*x)
|
||||
return np.vstack((e, -a * x * e)).T
|
||||
|
||||
np.random.seed(0)
|
||||
xdata = np.linspace(0, 4, 50)
|
||||
y = func(xdata, 2.5, 1.3)
|
||||
ydata = y + 0.2 * np.random.normal(size=len(xdata))
|
||||
|
||||
sigma = np.zeros(len(xdata)) + 0.2
|
||||
covar = np.diag(sigma**2)
|
||||
|
||||
for jac1, jac2 in [(jac, jac), (None, None)]:
|
||||
for absolute_sigma in [False, True]:
|
||||
popt1, pcov1 = curve_fit(func, xdata, ydata, sigma=sigma,
|
||||
jac=jac1, absolute_sigma=absolute_sigma)
|
||||
popt2, pcov2 = curve_fit(func, xdata, ydata, sigma=covar,
|
||||
jac=jac2, absolute_sigma=absolute_sigma)
|
||||
|
||||
assert_allclose(popt1, popt2, atol=1e-14)
|
||||
assert_allclose(pcov1, pcov2, atol=1e-14)
|
||||
|
||||
def test_curvefit_covariance(self):
|
||||
|
||||
def funcp(x, a, b):
|
||||
rotn = np.array([[1./np.sqrt(2), -1./np.sqrt(2), 0], [1./np.sqrt(2), 1./np.sqrt(2), 0], [0, 0, 1.0]])
|
||||
return rotn.dot(a * np.exp(-b*x))
|
||||
|
||||
def jacp(x, a, b):
|
||||
rotn = np.array([[1./np.sqrt(2), -1./np.sqrt(2), 0], [1./np.sqrt(2), 1./np.sqrt(2), 0], [0, 0, 1.0]])
|
||||
e = np.exp(-b*x)
|
||||
return rotn.dot(np.vstack((e, -a * x * e)).T)
|
||||
|
||||
def func(x, a, b):
|
||||
return a * np.exp(-b*x)
|
||||
|
||||
def jac(x, a, b):
|
||||
e = np.exp(-b*x)
|
||||
return np.vstack((e, -a * x * e)).T
|
||||
|
||||
np.random.seed(0)
|
||||
xdata = np.arange(1, 4)
|
||||
y = func(xdata, 2.5, 1.0)
|
||||
ydata = y + 0.2 * np.random.normal(size=len(xdata))
|
||||
sigma = np.zeros(len(xdata)) + 0.2
|
||||
covar = np.diag(sigma**2)
|
||||
# Get a rotation matrix, and obtain ydatap = R ydata
|
||||
# Chisq = ydata^T C^{-1} ydata
|
||||
# = ydata^T R^T R C^{-1} R^T R ydata
|
||||
# = ydatap^T Cp^{-1} ydatap
|
||||
# Cp^{-1} = R C^{-1} R^T
|
||||
# Cp = R C R^T, since R^-1 = R^T
|
||||
rotn = np.array([[1./np.sqrt(2), -1./np.sqrt(2), 0], [1./np.sqrt(2), 1./np.sqrt(2), 0], [0, 0, 1.0]])
|
||||
ydatap = rotn.dot(ydata)
|
||||
covarp = rotn.dot(covar).dot(rotn.T)
|
||||
|
||||
for jac1, jac2 in [(jac, jacp), (None, None)]:
|
||||
for absolute_sigma in [False, True]:
|
||||
popt1, pcov1 = curve_fit(func, xdata, ydata, sigma=sigma,
|
||||
jac=jac1, absolute_sigma=absolute_sigma)
|
||||
popt2, pcov2 = curve_fit(funcp, xdata, ydatap, sigma=covarp,
|
||||
jac=jac2, absolute_sigma=absolute_sigma)
|
||||
|
||||
assert_allclose(popt1, popt2, rtol=1.2e-7, atol=1e-14)
|
||||
assert_allclose(pcov1, pcov2, rtol=1.2e-7, atol=1e-14)
|
||||
|
||||
def test_dtypes(self):
|
||||
# regression test for gh-9581: curve_fit fails if x and y dtypes differ
|
||||
x = np.arange(-3, 5)
|
||||
y = 1.5*x + 3.0 + 0.5*np.sin(x)
|
||||
|
||||
def func(x, a, b):
|
||||
return a*x + b
|
||||
|
||||
for method in ['lm', 'trf', 'dogbox']:
|
||||
for dtx in [np.float32, np.float64]:
|
||||
for dty in [np.float32, np.float64]:
|
||||
x = x.astype(dtx)
|
||||
y = y.astype(dty)
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error", OptimizeWarning)
|
||||
p, cov = curve_fit(func, x, y, method=method)
|
||||
|
||||
assert np.isfinite(cov).all()
|
||||
assert not np.allclose(p, 1) # curve_fit's initial value
|
||||
|
||||
def test_dtypes2(self):
|
||||
# regression test for gh-7117: curve_fit fails if
|
||||
# both inputs are float32
|
||||
def hyperbola(x, s_1, s_2, o_x, o_y, c):
|
||||
b_2 = (s_1 + s_2) / 2
|
||||
b_1 = (s_2 - s_1) / 2
|
||||
return o_y + b_1*(x-o_x) + b_2*np.sqrt((x-o_x)**2 + c**2/4)
|
||||
|
||||
min_fit = np.array([-3.0, 0.0, -2.0, -10.0, 0.0])
|
||||
max_fit = np.array([0.0, 3.0, 3.0, 0.0, 10.0])
|
||||
guess = np.array([-2.5/3.0, 4/3.0, 1.0, -4.0, 0.5])
|
||||
|
||||
params = [-2, .4, -1, -5, 9.5]
|
||||
xdata = np.array([-32, -16, -8, 4, 4, 8, 16, 32])
|
||||
ydata = hyperbola(xdata, *params)
|
||||
|
||||
# run optimization twice, with xdata being float32 and float64
|
||||
popt_64, _ = curve_fit(f=hyperbola, xdata=xdata, ydata=ydata, p0=guess,
|
||||
bounds=(min_fit, max_fit))
|
||||
|
||||
xdata = xdata.astype(np.float32)
|
||||
ydata = hyperbola(xdata, *params)
|
||||
|
||||
popt_32, _ = curve_fit(f=hyperbola, xdata=xdata, ydata=ydata, p0=guess,
|
||||
bounds=(min_fit, max_fit))
|
||||
|
||||
assert_allclose(popt_32, popt_64, atol=2e-5)
|
||||
|
||||
def test_broadcast_y(self):
|
||||
xdata = np.arange(10)
|
||||
target = 4.7 * xdata ** 2 + 3.5 * xdata + np.random.rand(len(xdata))
|
||||
fit_func = lambda x, a, b: a*x**2 + b*x - target
|
||||
for method in ['lm', 'trf', 'dogbox']:
|
||||
popt0, pcov0 = curve_fit(fit_func,
|
||||
xdata=xdata,
|
||||
ydata=np.zeros_like(xdata),
|
||||
method=method)
|
||||
popt1, pcov1 = curve_fit(fit_func,
|
||||
xdata=xdata,
|
||||
ydata=0,
|
||||
method=method)
|
||||
assert_allclose(pcov0, pcov1)
|
||||
|
||||
def test_args_in_kwargs(self):
|
||||
# Ensure that `args` cannot be passed as keyword argument to `curve_fit`
|
||||
|
||||
def func(x, a, b):
|
||||
return a * x + b
|
||||
|
||||
with assert_raises(ValueError):
|
||||
curve_fit(func,
|
||||
xdata=[1, 2, 3, 4],
|
||||
ydata=[5, 9, 13, 17],
|
||||
p0=[1],
|
||||
args=(1,))
|
||||
|
||||
def test_data_point_number_validation(self):
|
||||
def func(x, a, b, c, d, e):
|
||||
return a * np.exp(-b * x) + c + d + e
|
||||
|
||||
with assert_raises(TypeError, match="The number of func parameters="):
|
||||
curve_fit(func,
|
||||
xdata=[1, 2, 3, 4],
|
||||
ydata=[5, 9, 13, 17])
|
||||
|
||||
@pytest.mark.filterwarnings('ignore::RuntimeWarning')
|
||||
def test_gh4555(self):
|
||||
# gh-4555 reported that covariance matrices returned by `leastsq`
|
||||
# can have negative diagonal elements and eigenvalues. (In fact,
|
||||
# they can also be asymmetric.) This shows up in the output of
|
||||
# `scipy.optimize.curve_fit`. Check that it has been resolved.giit
|
||||
def f(x, a, b, c, d, e):
|
||||
return a*np.log(x + 1 + b) + c*np.log(x + 1 + d) + e
|
||||
|
||||
rng = np.random.default_rng(408113519974467917)
|
||||
n = 100
|
||||
x = np.arange(n)
|
||||
y = np.linspace(2, 7, n) + rng.random(n)
|
||||
p, cov = optimize.curve_fit(f, x, y, maxfev=100000)
|
||||
assert np.all(np.diag(cov) > 0)
|
||||
eigs = linalg.eigh(cov)[0] # separate line for debugging
|
||||
# some platforms see a small negative eigevenvalue
|
||||
assert np.all(eigs > -1e-2)
|
||||
assert_allclose(cov, cov.T)
|
||||
|
||||
def test_gh4555b(self):
|
||||
# check that PR gh-17247 did not significantly change covariance matrix
|
||||
# for simple cases
|
||||
rng = np.random.default_rng(408113519974467917)
|
||||
|
||||
def func(x, a, b, c):
|
||||
return a * np.exp(-b * x) + c
|
||||
|
||||
xdata = np.linspace(0, 4, 50)
|
||||
y = func(xdata, 2.5, 1.3, 0.5)
|
||||
y_noise = 0.2 * rng.normal(size=xdata.size)
|
||||
ydata = y + y_noise
|
||||
_, res = curve_fit(func, xdata, ydata)
|
||||
# reference from commit 1d80a2f254380d2b45733258ca42eb6b55c8755b
|
||||
ref = [[+0.0158972536486215, 0.0069207183284242, -0.0007474400714749],
|
||||
[+0.0069207183284242, 0.0205057958128679, +0.0053997711275403],
|
||||
[-0.0007474400714749, 0.0053997711275403, +0.0027833930320877]]
|
||||
# Linux_Python_38_32bit_full fails with default tolerance
|
||||
assert_allclose(res, ref, 2e-7)
|
||||
|
||||
|
||||
class TestFixedPoint:
|
||||
|
||||
def test_scalar_trivial(self):
|
||||
# f(x) = 2x; fixed point should be x=0
|
||||
def func(x):
|
||||
return 2.0*x
|
||||
x0 = 1.0
|
||||
x = fixed_point(func, x0)
|
||||
assert_almost_equal(x, 0.0)
|
||||
|
||||
def test_scalar_basic1(self):
|
||||
# f(x) = x**2; x0=1.05; fixed point should be x=1
|
||||
def func(x):
|
||||
return x**2
|
||||
x0 = 1.05
|
||||
x = fixed_point(func, x0)
|
||||
assert_almost_equal(x, 1.0)
|
||||
|
||||
def test_scalar_basic2(self):
|
||||
# f(x) = x**0.5; x0=1.05; fixed point should be x=1
|
||||
def func(x):
|
||||
return x**0.5
|
||||
x0 = 1.05
|
||||
x = fixed_point(func, x0)
|
||||
assert_almost_equal(x, 1.0)
|
||||
|
||||
def test_array_trivial(self):
|
||||
def func(x):
|
||||
return 2.0*x
|
||||
x0 = [0.3, 0.15]
|
||||
with np.errstate(all='ignore'):
|
||||
x = fixed_point(func, x0)
|
||||
assert_almost_equal(x, [0.0, 0.0])
|
||||
|
||||
def test_array_basic1(self):
|
||||
# f(x) = c * x**2; fixed point should be x=1/c
|
||||
def func(x, c):
|
||||
return c * x**2
|
||||
c = array([0.75, 1.0, 1.25])
|
||||
x0 = [1.1, 1.15, 0.9]
|
||||
with np.errstate(all='ignore'):
|
||||
x = fixed_point(func, x0, args=(c,))
|
||||
assert_almost_equal(x, 1.0/c)
|
||||
|
||||
def test_array_basic2(self):
|
||||
# f(x) = c * x**0.5; fixed point should be x=c**2
|
||||
def func(x, c):
|
||||
return c * x**0.5
|
||||
c = array([0.75, 1.0, 1.25])
|
||||
x0 = [0.8, 1.1, 1.1]
|
||||
x = fixed_point(func, x0, args=(c,))
|
||||
assert_almost_equal(x, c**2)
|
||||
|
||||
def test_lambertw(self):
|
||||
# python-list/2010-December/594592.html
|
||||
xxroot = fixed_point(lambda xx: np.exp(-2.0*xx)/2.0, 1.0,
|
||||
args=(), xtol=1e-12, maxiter=500)
|
||||
assert_allclose(xxroot, np.exp(-2.0*xxroot)/2.0)
|
||||
assert_allclose(xxroot, lambertw(1)/2)
|
||||
|
||||
def test_no_acceleration(self):
|
||||
# github issue 5460
|
||||
ks = 2
|
||||
kl = 6
|
||||
m = 1.3
|
||||
n0 = 1.001
|
||||
i0 = ((m-1)/m)*(kl/ks/m)**(1/(m-1))
|
||||
|
||||
def func(n):
|
||||
return np.log(kl/ks/n) / np.log((i0*n/(n - 1))) + 1
|
||||
|
||||
n = fixed_point(func, n0, method='iteration')
|
||||
assert_allclose(n, m)
|
||||
34
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_nnls.py
vendored
Normal file
34
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_nnls.py
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
""" Unit tests for nonnegative least squares
|
||||
Author: Uwe Schmitt
|
||||
Sep 2008
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
from numpy.testing import assert_
|
||||
from pytest import raises as assert_raises
|
||||
|
||||
from scipy.optimize import nnls
|
||||
from numpy import arange, dot
|
||||
from numpy.linalg import norm
|
||||
|
||||
|
||||
class TestNNLS:
|
||||
|
||||
def test_nnls(self):
|
||||
a = arange(25.0).reshape(-1,5)
|
||||
x = arange(5.0)
|
||||
y = dot(a,x)
|
||||
x, res = nnls(a,y)
|
||||
assert_(res < 1e-7)
|
||||
assert_(norm(dot(a,x)-y) < 1e-7)
|
||||
|
||||
def test_maxiter(self):
|
||||
# test that maxiter argument does stop iterations
|
||||
# NB: did not manage to find a test case where the default value
|
||||
# of maxiter is not sufficient, so use a too-small value
|
||||
rndm = np.random.RandomState(1234)
|
||||
a = rndm.uniform(size=(100, 100))
|
||||
b = rndm.uniform(size=100)
|
||||
with assert_raises(RuntimeError):
|
||||
nnls(a, b, maxiter=1)
|
||||
|
||||
490
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_nonlin.py
vendored
Normal file
490
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_nonlin.py
vendored
Normal file
@@ -0,0 +1,490 @@
|
||||
""" Unit tests for nonlinear solvers
|
||||
Author: Ondrej Certik
|
||||
May 2007
|
||||
"""
|
||||
from numpy.testing import assert_
|
||||
import pytest
|
||||
|
||||
from scipy.optimize import _nonlin as nonlin, root
|
||||
from numpy import diag, dot
|
||||
from numpy.linalg import inv
|
||||
import numpy as np
|
||||
|
||||
from .test_minpack import pressure_network
|
||||
|
||||
SOLVERS = {'anderson': nonlin.anderson, 'diagbroyden': nonlin.diagbroyden,
|
||||
'linearmixing': nonlin.linearmixing, 'excitingmixing': nonlin.excitingmixing,
|
||||
'broyden1': nonlin.broyden1, 'broyden2': nonlin.broyden2,
|
||||
'krylov': nonlin.newton_krylov}
|
||||
MUST_WORK = {'anderson': nonlin.anderson, 'broyden1': nonlin.broyden1,
|
||||
'broyden2': nonlin.broyden2, 'krylov': nonlin.newton_krylov}
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Test problems
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def F(x):
|
||||
x = np.asarray(x).T
|
||||
d = diag([3,2,1.5,1,0.5])
|
||||
c = 0.01
|
||||
f = -d @ x - c * float(x.T @ x) * x
|
||||
return f
|
||||
|
||||
|
||||
F.xin = [1,1,1,1,1]
|
||||
F.KNOWN_BAD = {}
|
||||
F.JAC_KSP_BAD = {}
|
||||
F.ROOT_JAC_KSP_BAD = {}
|
||||
|
||||
|
||||
def F2(x):
|
||||
return x
|
||||
|
||||
|
||||
F2.xin = [1,2,3,4,5,6]
|
||||
F2.KNOWN_BAD = {'linearmixing': nonlin.linearmixing,
|
||||
'excitingmixing': nonlin.excitingmixing}
|
||||
F2.JAC_KSP_BAD = {}
|
||||
F2.ROOT_JAC_KSP_BAD = {}
|
||||
|
||||
|
||||
def F2_lucky(x):
|
||||
return x
|
||||
|
||||
|
||||
F2_lucky.xin = [0,0,0,0,0,0]
|
||||
F2_lucky.KNOWN_BAD = {}
|
||||
F2_lucky.JAC_KSP_BAD = {}
|
||||
F2_lucky.ROOT_JAC_KSP_BAD = {}
|
||||
|
||||
|
||||
def F3(x):
|
||||
A = np.array([[-2, 1, 0.], [1, -2, 1], [0, 1, -2]])
|
||||
b = np.array([1, 2, 3.])
|
||||
return A @ x - b
|
||||
|
||||
|
||||
F3.xin = [1,2,3]
|
||||
F3.KNOWN_BAD = {}
|
||||
F3.JAC_KSP_BAD = {}
|
||||
F3.ROOT_JAC_KSP_BAD = {}
|
||||
|
||||
|
||||
def F4_powell(x):
|
||||
A = 1e4
|
||||
return [A*x[0]*x[1] - 1, np.exp(-x[0]) + np.exp(-x[1]) - (1 + 1/A)]
|
||||
|
||||
|
||||
F4_powell.xin = [-1, -2]
|
||||
F4_powell.KNOWN_BAD = {'linearmixing': nonlin.linearmixing,
|
||||
'excitingmixing': nonlin.excitingmixing,
|
||||
'diagbroyden': nonlin.diagbroyden}
|
||||
# In the extreme case, it does not converge for nolinear problem solved by
|
||||
# MINRES and root problem solved by GMRES/BiCGStab/CGS/MINRES/TFQMR when using
|
||||
# Krylov method to approximate Jacobian
|
||||
F4_powell.JAC_KSP_BAD = {'minres'}
|
||||
F4_powell.ROOT_JAC_KSP_BAD = {'gmres', 'bicgstab', 'cgs', 'minres', 'tfqmr'}
|
||||
|
||||
|
||||
def F5(x):
|
||||
return pressure_network(x, 4, np.array([.5, .5, .5, .5]))
|
||||
|
||||
|
||||
F5.xin = [2., 0, 2, 0]
|
||||
F5.KNOWN_BAD = {'excitingmixing': nonlin.excitingmixing,
|
||||
'linearmixing': nonlin.linearmixing,
|
||||
'diagbroyden': nonlin.diagbroyden}
|
||||
# In the extreme case, the Jacobian inversion yielded zero vector for nonlinear
|
||||
# problem solved by CGS/MINRES and it does not converge for root problem solved
|
||||
# by MINRES and when using Krylov method to approximate Jacobian
|
||||
F5.JAC_KSP_BAD = {'cgs', 'minres'}
|
||||
F5.ROOT_JAC_KSP_BAD = {'minres'}
|
||||
|
||||
|
||||
def F6(x):
|
||||
x1, x2 = x
|
||||
J0 = np.array([[-4.256, 14.7],
|
||||
[0.8394989, 0.59964207]])
|
||||
v = np.array([(x1 + 3) * (x2**5 - 7) + 3*6,
|
||||
np.sin(x2 * np.exp(x1) - 1)])
|
||||
return -np.linalg.solve(J0, v)
|
||||
|
||||
|
||||
F6.xin = [-0.5, 1.4]
|
||||
F6.KNOWN_BAD = {'excitingmixing': nonlin.excitingmixing,
|
||||
'linearmixing': nonlin.linearmixing,
|
||||
'diagbroyden': nonlin.diagbroyden}
|
||||
F6.JAC_KSP_BAD = {}
|
||||
F6.ROOT_JAC_KSP_BAD = {}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Tests
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestNonlin:
|
||||
"""
|
||||
Check the Broyden methods for a few test problems.
|
||||
|
||||
broyden1, broyden2, and newton_krylov must succeed for
|
||||
all functions. Some of the others don't -- tests in KNOWN_BAD are skipped.
|
||||
|
||||
"""
|
||||
|
||||
def _check_nonlin_func(self, f, func, f_tol=1e-2):
|
||||
# Test all methods mentioned in the class `KrylovJacobian`
|
||||
if func == SOLVERS['krylov']:
|
||||
for method in ['gmres', 'bicgstab', 'cgs', 'minres', 'tfqmr']:
|
||||
if method in f.JAC_KSP_BAD:
|
||||
continue
|
||||
|
||||
x = func(f, f.xin, method=method, line_search=None,
|
||||
f_tol=f_tol, maxiter=200, verbose=0)
|
||||
assert_(np.absolute(f(x)).max() < f_tol)
|
||||
|
||||
x = func(f, f.xin, f_tol=f_tol, maxiter=200, verbose=0)
|
||||
assert_(np.absolute(f(x)).max() < f_tol)
|
||||
|
||||
def _check_root(self, f, method, f_tol=1e-2):
|
||||
# Test Krylov methods
|
||||
if method == 'krylov':
|
||||
for jac_method in ['gmres', 'bicgstab', 'cgs', 'minres', 'tfqmr']:
|
||||
if jac_method in f.ROOT_JAC_KSP_BAD:
|
||||
continue
|
||||
|
||||
res = root(f, f.xin, method=method,
|
||||
options={'ftol': f_tol, 'maxiter': 200,
|
||||
'disp': 0,
|
||||
'jac_options': {'method': jac_method}})
|
||||
assert_(np.absolute(res.fun).max() < f_tol)
|
||||
|
||||
res = root(f, f.xin, method=method,
|
||||
options={'ftol': f_tol, 'maxiter': 200, 'disp': 0})
|
||||
assert_(np.absolute(res.fun).max() < f_tol)
|
||||
|
||||
@pytest.mark.xfail
|
||||
def _check_func_fail(self, *a, **kw):
|
||||
pass
|
||||
|
||||
def test_problem_nonlin(self):
|
||||
for f in [F, F2, F2_lucky, F3, F4_powell, F5, F6]:
|
||||
for func in SOLVERS.values():
|
||||
if func in f.KNOWN_BAD.values():
|
||||
if func in MUST_WORK.values():
|
||||
self._check_func_fail(f, func)
|
||||
continue
|
||||
self._check_nonlin_func(f, func)
|
||||
|
||||
@pytest.mark.parametrize("method", ['lgmres', 'gmres', 'bicgstab', 'cgs',
|
||||
'minres', 'tfqmr'])
|
||||
def test_tol_norm_called(self, method):
|
||||
# Check that supplying tol_norm keyword to nonlin_solve works
|
||||
self._tol_norm_used = False
|
||||
|
||||
def local_norm_func(x):
|
||||
self._tol_norm_used = True
|
||||
return np.absolute(x).max()
|
||||
|
||||
nonlin.newton_krylov(F, F.xin, method=method, f_tol=1e-2,
|
||||
maxiter=200, verbose=0,
|
||||
tol_norm=local_norm_func)
|
||||
assert_(self._tol_norm_used)
|
||||
|
||||
def test_problem_root(self):
|
||||
for f in [F, F2, F2_lucky, F3, F4_powell, F5, F6]:
|
||||
for meth in SOLVERS:
|
||||
if meth in f.KNOWN_BAD:
|
||||
if meth in MUST_WORK:
|
||||
self._check_func_fail(f, meth)
|
||||
continue
|
||||
self._check_root(f, meth)
|
||||
|
||||
|
||||
class TestSecant:
|
||||
"""Check that some Jacobian approximations satisfy the secant condition"""
|
||||
|
||||
xs = [np.array([1,2,3,4,5], float),
|
||||
np.array([2,3,4,5,1], float),
|
||||
np.array([3,4,5,1,2], float),
|
||||
np.array([4,5,1,2,3], float),
|
||||
np.array([9,1,9,1,3], float),
|
||||
np.array([0,1,9,1,3], float),
|
||||
np.array([5,5,7,1,1], float),
|
||||
np.array([1,2,7,5,1], float),]
|
||||
fs = [x**2 - 1 for x in xs]
|
||||
|
||||
def _check_secant(self, jac_cls, npoints=1, **kw):
|
||||
"""
|
||||
Check that the given Jacobian approximation satisfies secant
|
||||
conditions for last `npoints` points.
|
||||
"""
|
||||
jac = jac_cls(**kw)
|
||||
jac.setup(self.xs[0], self.fs[0], None)
|
||||
for j, (x, f) in enumerate(zip(self.xs[1:], self.fs[1:])):
|
||||
jac.update(x, f)
|
||||
|
||||
for k in range(min(npoints, j+1)):
|
||||
dx = self.xs[j-k+1] - self.xs[j-k]
|
||||
df = self.fs[j-k+1] - self.fs[j-k]
|
||||
assert_(np.allclose(dx, jac.solve(df)))
|
||||
|
||||
# Check that the `npoints` secant bound is strict
|
||||
if j >= npoints:
|
||||
dx = self.xs[j-npoints+1] - self.xs[j-npoints]
|
||||
df = self.fs[j-npoints+1] - self.fs[j-npoints]
|
||||
assert_(not np.allclose(dx, jac.solve(df)))
|
||||
|
||||
def test_broyden1(self):
|
||||
self._check_secant(nonlin.BroydenFirst)
|
||||
|
||||
def test_broyden2(self):
|
||||
self._check_secant(nonlin.BroydenSecond)
|
||||
|
||||
def test_broyden1_update(self):
|
||||
# Check that BroydenFirst update works as for a dense matrix
|
||||
jac = nonlin.BroydenFirst(alpha=0.1)
|
||||
jac.setup(self.xs[0], self.fs[0], None)
|
||||
|
||||
B = np.identity(5) * (-1/0.1)
|
||||
|
||||
for last_j, (x, f) in enumerate(zip(self.xs[1:], self.fs[1:])):
|
||||
df = f - self.fs[last_j]
|
||||
dx = x - self.xs[last_j]
|
||||
B += (df - dot(B, dx))[:,None] * dx[None,:] / dot(dx, dx)
|
||||
jac.update(x, f)
|
||||
assert_(np.allclose(jac.todense(), B, rtol=1e-10, atol=1e-13))
|
||||
|
||||
def test_broyden2_update(self):
|
||||
# Check that BroydenSecond update works as for a dense matrix
|
||||
jac = nonlin.BroydenSecond(alpha=0.1)
|
||||
jac.setup(self.xs[0], self.fs[0], None)
|
||||
|
||||
H = np.identity(5) * (-0.1)
|
||||
|
||||
for last_j, (x, f) in enumerate(zip(self.xs[1:], self.fs[1:])):
|
||||
df = f - self.fs[last_j]
|
||||
dx = x - self.xs[last_j]
|
||||
H += (dx - dot(H, df))[:,None] * df[None,:] / dot(df, df)
|
||||
jac.update(x, f)
|
||||
assert_(np.allclose(jac.todense(), inv(H), rtol=1e-10, atol=1e-13))
|
||||
|
||||
def test_anderson(self):
|
||||
# Anderson mixing (with w0=0) satisfies secant conditions
|
||||
# for the last M iterates, see [Ey]_
|
||||
#
|
||||
# .. [Ey] V. Eyert, J. Comp. Phys., 124, 271 (1996).
|
||||
self._check_secant(nonlin.Anderson, M=3, w0=0, npoints=3)
|
||||
|
||||
|
||||
class TestLinear:
|
||||
"""Solve a linear equation;
|
||||
some methods find the exact solution in a finite number of steps"""
|
||||
|
||||
def _check(self, jac, N, maxiter, complex=False, **kw):
|
||||
np.random.seed(123)
|
||||
|
||||
A = np.random.randn(N, N)
|
||||
if complex:
|
||||
A = A + 1j*np.random.randn(N, N)
|
||||
b = np.random.randn(N)
|
||||
if complex:
|
||||
b = b + 1j*np.random.randn(N)
|
||||
|
||||
def func(x):
|
||||
return dot(A, x) - b
|
||||
|
||||
sol = nonlin.nonlin_solve(func, np.zeros(N), jac, maxiter=maxiter,
|
||||
f_tol=1e-6, line_search=None, verbose=0)
|
||||
assert_(np.allclose(dot(A, sol), b, atol=1e-6))
|
||||
|
||||
def test_broyden1(self):
|
||||
# Broyden methods solve linear systems exactly in 2*N steps
|
||||
self._check(nonlin.BroydenFirst(alpha=1.0), 20, 41, False)
|
||||
self._check(nonlin.BroydenFirst(alpha=1.0), 20, 41, True)
|
||||
|
||||
def test_broyden2(self):
|
||||
# Broyden methods solve linear systems exactly in 2*N steps
|
||||
self._check(nonlin.BroydenSecond(alpha=1.0), 20, 41, False)
|
||||
self._check(nonlin.BroydenSecond(alpha=1.0), 20, 41, True)
|
||||
|
||||
def test_anderson(self):
|
||||
# Anderson is rather similar to Broyden, if given enough storage space
|
||||
self._check(nonlin.Anderson(M=50, alpha=1.0), 20, 29, False)
|
||||
self._check(nonlin.Anderson(M=50, alpha=1.0), 20, 29, True)
|
||||
|
||||
def test_krylov(self):
|
||||
# Krylov methods solve linear systems exactly in N inner steps
|
||||
self._check(nonlin.KrylovJacobian, 20, 2, False, inner_m=10)
|
||||
self._check(nonlin.KrylovJacobian, 20, 2, True, inner_m=10)
|
||||
|
||||
|
||||
class TestJacobianDotSolve:
|
||||
"""Check that solve/dot methods in Jacobian approximations are consistent"""
|
||||
|
||||
def _func(self, x):
|
||||
return x**2 - 1 + np.dot(self.A, x)
|
||||
|
||||
def _check_dot(self, jac_cls, complex=False, tol=1e-6, **kw):
|
||||
np.random.seed(123)
|
||||
|
||||
N = 7
|
||||
|
||||
def rand(*a):
|
||||
q = np.random.rand(*a)
|
||||
if complex:
|
||||
q = q + 1j*np.random.rand(*a)
|
||||
return q
|
||||
|
||||
def assert_close(a, b, msg):
|
||||
d = abs(a - b).max()
|
||||
f = tol + abs(b).max()*tol
|
||||
if d > f:
|
||||
raise AssertionError('%s: err %g' % (msg, d))
|
||||
|
||||
self.A = rand(N, N)
|
||||
|
||||
# initialize
|
||||
x0 = np.random.rand(N)
|
||||
jac = jac_cls(**kw)
|
||||
jac.setup(x0, self._func(x0), self._func)
|
||||
|
||||
# check consistency
|
||||
for k in range(2*N):
|
||||
v = rand(N)
|
||||
|
||||
if hasattr(jac, '__array__'):
|
||||
Jd = np.array(jac)
|
||||
if hasattr(jac, 'solve'):
|
||||
Gv = jac.solve(v)
|
||||
Gv2 = np.linalg.solve(Jd, v)
|
||||
assert_close(Gv, Gv2, 'solve vs array')
|
||||
if hasattr(jac, 'rsolve'):
|
||||
Gv = jac.rsolve(v)
|
||||
Gv2 = np.linalg.solve(Jd.T.conj(), v)
|
||||
assert_close(Gv, Gv2, 'rsolve vs array')
|
||||
if hasattr(jac, 'matvec'):
|
||||
Jv = jac.matvec(v)
|
||||
Jv2 = np.dot(Jd, v)
|
||||
assert_close(Jv, Jv2, 'dot vs array')
|
||||
if hasattr(jac, 'rmatvec'):
|
||||
Jv = jac.rmatvec(v)
|
||||
Jv2 = np.dot(Jd.T.conj(), v)
|
||||
assert_close(Jv, Jv2, 'rmatvec vs array')
|
||||
|
||||
if hasattr(jac, 'matvec') and hasattr(jac, 'solve'):
|
||||
Jv = jac.matvec(v)
|
||||
Jv2 = jac.solve(jac.matvec(Jv))
|
||||
assert_close(Jv, Jv2, 'dot vs solve')
|
||||
|
||||
if hasattr(jac, 'rmatvec') and hasattr(jac, 'rsolve'):
|
||||
Jv = jac.rmatvec(v)
|
||||
Jv2 = jac.rmatvec(jac.rsolve(Jv))
|
||||
assert_close(Jv, Jv2, 'rmatvec vs rsolve')
|
||||
|
||||
x = rand(N)
|
||||
jac.update(x, self._func(x))
|
||||
|
||||
def test_broyden1(self):
|
||||
self._check_dot(nonlin.BroydenFirst, complex=False)
|
||||
self._check_dot(nonlin.BroydenFirst, complex=True)
|
||||
|
||||
def test_broyden2(self):
|
||||
self._check_dot(nonlin.BroydenSecond, complex=False)
|
||||
self._check_dot(nonlin.BroydenSecond, complex=True)
|
||||
|
||||
def test_anderson(self):
|
||||
self._check_dot(nonlin.Anderson, complex=False)
|
||||
self._check_dot(nonlin.Anderson, complex=True)
|
||||
|
||||
def test_diagbroyden(self):
|
||||
self._check_dot(nonlin.DiagBroyden, complex=False)
|
||||
self._check_dot(nonlin.DiagBroyden, complex=True)
|
||||
|
||||
def test_linearmixing(self):
|
||||
self._check_dot(nonlin.LinearMixing, complex=False)
|
||||
self._check_dot(nonlin.LinearMixing, complex=True)
|
||||
|
||||
def test_excitingmixing(self):
|
||||
self._check_dot(nonlin.ExcitingMixing, complex=False)
|
||||
self._check_dot(nonlin.ExcitingMixing, complex=True)
|
||||
|
||||
def test_krylov(self):
|
||||
self._check_dot(nonlin.KrylovJacobian, complex=False, tol=1e-3)
|
||||
self._check_dot(nonlin.KrylovJacobian, complex=True, tol=1e-3)
|
||||
|
||||
|
||||
class TestNonlinOldTests:
|
||||
""" Test case for a simple constrained entropy maximization problem
|
||||
(the machine translation example of Berger et al in
|
||||
Computational Linguistics, vol 22, num 1, pp 39--72, 1996.)
|
||||
"""
|
||||
|
||||
def test_broyden1(self):
|
||||
x = nonlin.broyden1(F,F.xin,iter=12,alpha=1)
|
||||
assert_(nonlin.norm(x) < 1e-9)
|
||||
assert_(nonlin.norm(F(x)) < 1e-9)
|
||||
|
||||
def test_broyden2(self):
|
||||
x = nonlin.broyden2(F,F.xin,iter=12,alpha=1)
|
||||
assert_(nonlin.norm(x) < 1e-9)
|
||||
assert_(nonlin.norm(F(x)) < 1e-9)
|
||||
|
||||
def test_anderson(self):
|
||||
x = nonlin.anderson(F,F.xin,iter=12,alpha=0.03,M=5)
|
||||
assert_(nonlin.norm(x) < 0.33)
|
||||
|
||||
def test_linearmixing(self):
|
||||
x = nonlin.linearmixing(F,F.xin,iter=60,alpha=0.5)
|
||||
assert_(nonlin.norm(x) < 1e-7)
|
||||
assert_(nonlin.norm(F(x)) < 1e-7)
|
||||
|
||||
def test_exciting(self):
|
||||
x = nonlin.excitingmixing(F,F.xin,iter=20,alpha=0.5)
|
||||
assert_(nonlin.norm(x) < 1e-5)
|
||||
assert_(nonlin.norm(F(x)) < 1e-5)
|
||||
|
||||
def test_diagbroyden(self):
|
||||
x = nonlin.diagbroyden(F,F.xin,iter=11,alpha=1)
|
||||
assert_(nonlin.norm(x) < 1e-8)
|
||||
assert_(nonlin.norm(F(x)) < 1e-8)
|
||||
|
||||
def test_root_broyden1(self):
|
||||
res = root(F, F.xin, method='broyden1',
|
||||
options={'nit': 12, 'jac_options': {'alpha': 1}})
|
||||
assert_(nonlin.norm(res.x) < 1e-9)
|
||||
assert_(nonlin.norm(res.fun) < 1e-9)
|
||||
|
||||
def test_root_broyden2(self):
|
||||
res = root(F, F.xin, method='broyden2',
|
||||
options={'nit': 12, 'jac_options': {'alpha': 1}})
|
||||
assert_(nonlin.norm(res.x) < 1e-9)
|
||||
assert_(nonlin.norm(res.fun) < 1e-9)
|
||||
|
||||
def test_root_anderson(self):
|
||||
res = root(F, F.xin, method='anderson',
|
||||
options={'nit': 12,
|
||||
'jac_options': {'alpha': 0.03, 'M': 5}})
|
||||
assert_(nonlin.norm(res.x) < 0.33)
|
||||
|
||||
def test_root_linearmixing(self):
|
||||
res = root(F, F.xin, method='linearmixing',
|
||||
options={'nit': 60,
|
||||
'jac_options': {'alpha': 0.5}})
|
||||
assert_(nonlin.norm(res.x) < 1e-7)
|
||||
assert_(nonlin.norm(res.fun) < 1e-7)
|
||||
|
||||
def test_root_excitingmixing(self):
|
||||
res = root(F, F.xin, method='excitingmixing',
|
||||
options={'nit': 20,
|
||||
'jac_options': {'alpha': 0.5}})
|
||||
assert_(nonlin.norm(res.x) < 1e-5)
|
||||
assert_(nonlin.norm(res.fun) < 1e-5)
|
||||
|
||||
def test_root_diagbroyden(self):
|
||||
res = root(F, F.xin, method='diagbroyden',
|
||||
options={'nit': 11,
|
||||
'jac_options': {'alpha': 1}})
|
||||
assert_(nonlin.norm(res.x) < 1e-8)
|
||||
assert_(nonlin.norm(res.fun) < 1e-8)
|
||||
2855
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_optimize.py
vendored
Normal file
2855
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_optimize.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
431
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_quadratic_assignment.py
vendored
Normal file
431
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_quadratic_assignment.py
vendored
Normal file
@@ -0,0 +1,431 @@
|
||||
import pytest
|
||||
import numpy as np
|
||||
from scipy.optimize import quadratic_assignment, OptimizeWarning
|
||||
from scipy.optimize._qap import _calc_score as _score
|
||||
from numpy.testing import assert_equal, assert_, assert_warns
|
||||
|
||||
|
||||
################
|
||||
# Common Tests #
|
||||
################
|
||||
|
||||
def chr12c():
|
||||
A = [
|
||||
[0, 90, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[90, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[10, 0, 0, 0, 43, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 23, 0, 0, 0, 88, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 43, 0, 0, 0, 26, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 88, 0, 0, 0, 16, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 26, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 16, 0, 0, 0, 96, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 29, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 37],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 0, 0],
|
||||
]
|
||||
B = [
|
||||
[0, 36, 54, 26, 59, 72, 9, 34, 79, 17, 46, 95],
|
||||
[36, 0, 73, 35, 90, 58, 30, 78, 35, 44, 79, 36],
|
||||
[54, 73, 0, 21, 10, 97, 58, 66, 69, 61, 54, 63],
|
||||
[26, 35, 21, 0, 93, 12, 46, 40, 37, 48, 68, 85],
|
||||
[59, 90, 10, 93, 0, 64, 5, 29, 76, 16, 5, 76],
|
||||
[72, 58, 97, 12, 64, 0, 96, 55, 38, 54, 0, 34],
|
||||
[9, 30, 58, 46, 5, 96, 0, 83, 35, 11, 56, 37],
|
||||
[34, 78, 66, 40, 29, 55, 83, 0, 44, 12, 15, 80],
|
||||
[79, 35, 69, 37, 76, 38, 35, 44, 0, 64, 39, 33],
|
||||
[17, 44, 61, 48, 16, 54, 11, 12, 64, 0, 70, 86],
|
||||
[46, 79, 54, 68, 5, 0, 56, 15, 39, 70, 0, 18],
|
||||
[95, 36, 63, 85, 76, 34, 37, 80, 33, 86, 18, 0],
|
||||
]
|
||||
A, B = np.array(A), np.array(B)
|
||||
n = A.shape[0]
|
||||
|
||||
opt_perm = np.array([7, 5, 1, 3, 10, 4, 8, 6, 9, 11, 2, 12]) - [1] * n
|
||||
|
||||
return A, B, opt_perm
|
||||
|
||||
|
||||
class QAPCommonTests:
|
||||
"""
|
||||
Base class for `quadratic_assignment` tests.
|
||||
"""
|
||||
def setup_method(self):
|
||||
np.random.seed(0)
|
||||
|
||||
# Test global optima of problem from Umeyama IVB
|
||||
# https://pcl.sitehost.iu.edu/rgoldsto/papers/weighted%20graph%20match2.pdf
|
||||
# Graph matching maximum is in the paper
|
||||
# QAP minimum determined by brute force
|
||||
def test_accuracy_1(self):
|
||||
# besides testing accuracy, check that A and B can be lists
|
||||
A = [[0, 3, 4, 2],
|
||||
[0, 0, 1, 2],
|
||||
[1, 0, 0, 1],
|
||||
[0, 0, 1, 0]]
|
||||
|
||||
B = [[0, 4, 2, 4],
|
||||
[0, 0, 1, 0],
|
||||
[0, 2, 0, 2],
|
||||
[0, 1, 2, 0]]
|
||||
|
||||
res = quadratic_assignment(A, B, method=self.method,
|
||||
options={"rng": 0, "maximize": False})
|
||||
assert_equal(res.fun, 10)
|
||||
assert_equal(res.col_ind, np.array([1, 2, 3, 0]))
|
||||
|
||||
res = quadratic_assignment(A, B, method=self.method,
|
||||
options={"rng": 0, "maximize": True})
|
||||
|
||||
if self.method == 'faq':
|
||||
# Global optimum is 40, but FAQ gets 37
|
||||
assert_equal(res.fun, 37)
|
||||
assert_equal(res.col_ind, np.array([0, 2, 3, 1]))
|
||||
else:
|
||||
assert_equal(res.fun, 40)
|
||||
assert_equal(res.col_ind, np.array([0, 3, 1, 2]))
|
||||
|
||||
res = quadratic_assignment(A, B, method=self.method,
|
||||
options={"rng": 0, "maximize": True})
|
||||
|
||||
# Test global optima of problem from Umeyama IIIB
|
||||
# https://pcl.sitehost.iu.edu/rgoldsto/papers/weighted%20graph%20match2.pdf
|
||||
# Graph matching maximum is in the paper
|
||||
# QAP minimum determined by brute force
|
||||
def test_accuracy_2(self):
|
||||
|
||||
A = np.array([[0, 5, 8, 6],
|
||||
[5, 0, 5, 1],
|
||||
[8, 5, 0, 2],
|
||||
[6, 1, 2, 0]])
|
||||
|
||||
B = np.array([[0, 1, 8, 4],
|
||||
[1, 0, 5, 2],
|
||||
[8, 5, 0, 5],
|
||||
[4, 2, 5, 0]])
|
||||
|
||||
res = quadratic_assignment(A, B, method=self.method,
|
||||
options={"rng": 0, "maximize": False})
|
||||
if self.method == 'faq':
|
||||
# Global optimum is 176, but FAQ gets 178
|
||||
assert_equal(res.fun, 178)
|
||||
assert_equal(res.col_ind, np.array([1, 0, 3, 2]))
|
||||
else:
|
||||
assert_equal(res.fun, 176)
|
||||
assert_equal(res.col_ind, np.array([1, 2, 3, 0]))
|
||||
|
||||
res = quadratic_assignment(A, B, method=self.method,
|
||||
options={"rng": 0, "maximize": True})
|
||||
assert_equal(res.fun, 286)
|
||||
assert_equal(res.col_ind, np.array([2, 3, 0, 1]))
|
||||
|
||||
def test_accuracy_3(self):
|
||||
|
||||
A, B, opt_perm = chr12c()
|
||||
|
||||
# basic minimization
|
||||
res = quadratic_assignment(A, B, method=self.method,
|
||||
options={"rng": 0})
|
||||
assert_(11156 <= res.fun < 21000)
|
||||
assert_equal(res.fun, _score(A, B, res.col_ind))
|
||||
|
||||
# basic maximization
|
||||
res = quadratic_assignment(A, B, method=self.method,
|
||||
options={"rng": 0, 'maximize': True})
|
||||
assert_(74000 <= res.fun < 85000)
|
||||
assert_equal(res.fun, _score(A, B, res.col_ind))
|
||||
|
||||
# check ofv with strictly partial match
|
||||
seed_cost = np.array([4, 8, 10])
|
||||
seed = np.asarray([seed_cost, opt_perm[seed_cost]]).T
|
||||
res = quadratic_assignment(A, B, method=self.method,
|
||||
options={'partial_match': seed})
|
||||
assert_(11156 <= res.fun < 21000)
|
||||
assert_equal(res.col_ind[seed_cost], opt_perm[seed_cost])
|
||||
|
||||
# check performance when partial match is the global optimum
|
||||
seed = np.asarray([np.arange(len(A)), opt_perm]).T
|
||||
res = quadratic_assignment(A, B, method=self.method,
|
||||
options={'partial_match': seed})
|
||||
assert_equal(res.col_ind, seed[:, 1].T)
|
||||
assert_equal(res.fun, 11156)
|
||||
assert_equal(res.nit, 0)
|
||||
|
||||
# check performance with zero sized matrix inputs
|
||||
empty = np.empty((0, 0))
|
||||
res = quadratic_assignment(empty, empty, method=self.method,
|
||||
options={"rng": 0})
|
||||
assert_equal(res.nit, 0)
|
||||
assert_equal(res.fun, 0)
|
||||
|
||||
def test_unknown_options(self):
|
||||
A, B, opt_perm = chr12c()
|
||||
|
||||
def f():
|
||||
quadratic_assignment(A, B, method=self.method,
|
||||
options={"ekki-ekki": True})
|
||||
assert_warns(OptimizeWarning, f)
|
||||
|
||||
|
||||
class TestFAQ(QAPCommonTests):
|
||||
method = "faq"
|
||||
|
||||
def test_options(self):
|
||||
# cost and distance matrices of QAPLIB instance chr12c
|
||||
A, B, opt_perm = chr12c()
|
||||
n = len(A)
|
||||
|
||||
# check that max_iter is obeying with low input value
|
||||
res = quadratic_assignment(A, B,
|
||||
options={'maxiter': 5})
|
||||
assert_equal(res.nit, 5)
|
||||
|
||||
# test with shuffle
|
||||
res = quadratic_assignment(A, B,
|
||||
options={'shuffle_input': True})
|
||||
assert_(11156 <= res.fun < 21000)
|
||||
|
||||
# test with randomized init
|
||||
res = quadratic_assignment(A, B,
|
||||
options={'rng': 1, 'P0': "randomized"})
|
||||
assert_(11156 <= res.fun < 21000)
|
||||
|
||||
# check with specified P0
|
||||
K = np.ones((n, n)) / float(n)
|
||||
K = _doubly_stochastic(K)
|
||||
res = quadratic_assignment(A, B,
|
||||
options={'P0': K})
|
||||
assert_(11156 <= res.fun < 21000)
|
||||
|
||||
def test_specific_input_validation(self):
|
||||
|
||||
A = np.identity(2)
|
||||
B = A
|
||||
|
||||
# method is implicitly faq
|
||||
|
||||
# ValueError Checks: making sure single value parameters are of
|
||||
# correct value
|
||||
with pytest.raises(ValueError, match="Invalid 'P0' parameter"):
|
||||
quadratic_assignment(A, B, options={'P0': "random"})
|
||||
with pytest.raises(
|
||||
ValueError, match="'maxiter' must be a positive integer"):
|
||||
quadratic_assignment(A, B, options={'maxiter': -1})
|
||||
with pytest.raises(ValueError, match="'tol' must be a positive float"):
|
||||
quadratic_assignment(A, B, options={'tol': -1})
|
||||
|
||||
# TypeError Checks: making sure single value parameters are of
|
||||
# correct type
|
||||
with pytest.raises(TypeError):
|
||||
quadratic_assignment(A, B, options={'maxiter': 1.5})
|
||||
|
||||
# test P0 matrix input
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="`P0` matrix must have shape m' x m', where m'=n-m"):
|
||||
quadratic_assignment(
|
||||
np.identity(4), np.identity(4),
|
||||
options={'P0': np.ones((3, 3))}
|
||||
)
|
||||
|
||||
K = [[0.4, 0.2, 0.3],
|
||||
[0.3, 0.6, 0.2],
|
||||
[0.2, 0.2, 0.7]]
|
||||
# matrix that isn't quite doubly stochastic
|
||||
with pytest.raises(
|
||||
ValueError, match="`P0` matrix must be doubly stochastic"):
|
||||
quadratic_assignment(
|
||||
np.identity(3), np.identity(3), options={'P0': K}
|
||||
)
|
||||
|
||||
|
||||
class Test2opt(QAPCommonTests):
|
||||
method = "2opt"
|
||||
|
||||
def test_deterministic(self):
|
||||
# np.random.seed(0) executes before every method
|
||||
n = 20
|
||||
|
||||
A = np.random.rand(n, n)
|
||||
B = np.random.rand(n, n)
|
||||
res1 = quadratic_assignment(A, B, method=self.method)
|
||||
|
||||
np.random.seed(0)
|
||||
|
||||
A = np.random.rand(n, n)
|
||||
B = np.random.rand(n, n)
|
||||
res2 = quadratic_assignment(A, B, method=self.method)
|
||||
|
||||
assert_equal(res1.nit, res2.nit)
|
||||
|
||||
def test_partial_guess(self):
|
||||
n = 5
|
||||
A = np.random.rand(n, n)
|
||||
B = np.random.rand(n, n)
|
||||
|
||||
res1 = quadratic_assignment(A, B, method=self.method,
|
||||
options={'rng': 0})
|
||||
guess = np.array([np.arange(5), res1.col_ind]).T
|
||||
res2 = quadratic_assignment(A, B, method=self.method,
|
||||
options={'rng': 0, 'partial_guess': guess})
|
||||
fix = [2, 4]
|
||||
match = np.array([np.arange(5)[fix], res1.col_ind[fix]]).T
|
||||
res3 = quadratic_assignment(A, B, method=self.method,
|
||||
options={'rng': 0, 'partial_guess': guess,
|
||||
'partial_match': match})
|
||||
assert_(res1.nit != n*(n+1)/2)
|
||||
assert_equal(res2.nit, n*(n+1)/2) # tests each swap exactly once
|
||||
assert_equal(res3.nit, (n-2)*(n-1)/2) # tests free swaps exactly once
|
||||
|
||||
def test_specific_input_validation(self):
|
||||
# can't have more seed nodes than cost/dist nodes
|
||||
_rm = _range_matrix
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="`partial_guess` can have only as many entries as"):
|
||||
quadratic_assignment(np.identity(3), np.identity(3),
|
||||
method=self.method,
|
||||
options={'partial_guess': _rm(5, 2)})
|
||||
# test for only two seed columns
|
||||
with pytest.raises(
|
||||
ValueError, match="`partial_guess` must have two columns"):
|
||||
quadratic_assignment(
|
||||
np.identity(3), np.identity(3), method=self.method,
|
||||
options={'partial_guess': _range_matrix(2, 3)}
|
||||
)
|
||||
# test that seed has no more than two dimensions
|
||||
with pytest.raises(
|
||||
ValueError, match="`partial_guess` must have exactly two"):
|
||||
quadratic_assignment(
|
||||
np.identity(3), np.identity(3), method=self.method,
|
||||
options={'partial_guess': np.random.rand(3, 2, 2)}
|
||||
)
|
||||
# seeds cannot be negative valued
|
||||
with pytest.raises(
|
||||
ValueError, match="`partial_guess` must contain only pos"):
|
||||
quadratic_assignment(
|
||||
np.identity(3), np.identity(3), method=self.method,
|
||||
options={'partial_guess': -1 * _range_matrix(2, 2)}
|
||||
)
|
||||
# seeds can't have values greater than number of nodes
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="`partial_guess` entries must be less than number"):
|
||||
quadratic_assignment(
|
||||
np.identity(5), np.identity(5), method=self.method,
|
||||
options={'partial_guess': 2 * _range_matrix(4, 2)}
|
||||
)
|
||||
# columns of seed matrix must be unique
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="`partial_guess` column entries must be unique"):
|
||||
quadratic_assignment(
|
||||
np.identity(3), np.identity(3), method=self.method,
|
||||
options={'partial_guess': np.ones((2, 2))}
|
||||
)
|
||||
|
||||
|
||||
class TestQAPOnce():
|
||||
def setup_method(self):
|
||||
np.random.seed(0)
|
||||
|
||||
# these don't need to be repeated for each method
|
||||
def test_common_input_validation(self):
|
||||
# test that non square matrices return error
|
||||
with pytest.raises(ValueError, match="`A` must be square"):
|
||||
quadratic_assignment(
|
||||
np.random.random((3, 4)),
|
||||
np.random.random((3, 3)),
|
||||
)
|
||||
with pytest.raises(ValueError, match="`B` must be square"):
|
||||
quadratic_assignment(
|
||||
np.random.random((3, 3)),
|
||||
np.random.random((3, 4)),
|
||||
)
|
||||
# test that cost and dist matrices have no more than two dimensions
|
||||
with pytest.raises(
|
||||
ValueError, match="`A` and `B` must have exactly two"):
|
||||
quadratic_assignment(
|
||||
np.random.random((3, 3, 3)),
|
||||
np.random.random((3, 3, 3)),
|
||||
)
|
||||
# test that cost and dist matrices of different sizes return error
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="`A` and `B` matrices must be of equal size"):
|
||||
quadratic_assignment(
|
||||
np.random.random((3, 3)),
|
||||
np.random.random((4, 4)),
|
||||
)
|
||||
# can't have more seed nodes than cost/dist nodes
|
||||
_rm = _range_matrix
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="`partial_match` can have only as many seeds as"):
|
||||
quadratic_assignment(np.identity(3), np.identity(3),
|
||||
options={'partial_match': _rm(5, 2)})
|
||||
# test for only two seed columns
|
||||
with pytest.raises(
|
||||
ValueError, match="`partial_match` must have two columns"):
|
||||
quadratic_assignment(
|
||||
np.identity(3), np.identity(3),
|
||||
options={'partial_match': _range_matrix(2, 3)}
|
||||
)
|
||||
# test that seed has no more than two dimensions
|
||||
with pytest.raises(
|
||||
ValueError, match="`partial_match` must have exactly two"):
|
||||
quadratic_assignment(
|
||||
np.identity(3), np.identity(3),
|
||||
options={'partial_match': np.random.rand(3, 2, 2)}
|
||||
)
|
||||
# seeds cannot be negative valued
|
||||
with pytest.raises(
|
||||
ValueError, match="`partial_match` must contain only pos"):
|
||||
quadratic_assignment(
|
||||
np.identity(3), np.identity(3),
|
||||
options={'partial_match': -1 * _range_matrix(2, 2)}
|
||||
)
|
||||
# seeds can't have values greater than number of nodes
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="`partial_match` entries must be less than number"):
|
||||
quadratic_assignment(
|
||||
np.identity(5), np.identity(5),
|
||||
options={'partial_match': 2 * _range_matrix(4, 2)}
|
||||
)
|
||||
# columns of seed matrix must be unique
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="`partial_match` column entries must be unique"):
|
||||
quadratic_assignment(
|
||||
np.identity(3), np.identity(3),
|
||||
options={'partial_match': np.ones((2, 2))}
|
||||
)
|
||||
|
||||
|
||||
def _range_matrix(a, b):
|
||||
mat = np.zeros((a, b))
|
||||
for i in range(b):
|
||||
mat[:, i] = np.arange(a)
|
||||
return mat
|
||||
|
||||
|
||||
def _doubly_stochastic(P, tol=1e-3):
|
||||
# cleaner implementation of btaba/sinkhorn_knopp
|
||||
|
||||
max_iter = 1000
|
||||
c = 1 / P.sum(axis=0)
|
||||
r = 1 / (P @ c)
|
||||
P_eps = P
|
||||
|
||||
for it in range(max_iter):
|
||||
if ((np.abs(P_eps.sum(axis=1) - 1) < tol).all() and
|
||||
(np.abs(P_eps.sum(axis=0) - 1) < tol).all()):
|
||||
# All column/row sums ~= 1 within threshold
|
||||
break
|
||||
|
||||
c = 1 / (r @ P)
|
||||
r = 1 / (P @ c)
|
||||
P_eps = r[:, None] * P * c
|
||||
|
||||
return P_eps
|
||||
40
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_regression.py
vendored
Normal file
40
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_regression.py
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
"""Regression tests for optimize.
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
from numpy.testing import assert_almost_equal
|
||||
from pytest import raises as assert_raises
|
||||
|
||||
import scipy.optimize
|
||||
|
||||
|
||||
class TestRegression:
|
||||
|
||||
def test_newton_x0_is_0(self):
|
||||
# Regression test for gh-1601
|
||||
tgt = 1
|
||||
res = scipy.optimize.newton(lambda x: x - 1, 0)
|
||||
assert_almost_equal(res, tgt)
|
||||
|
||||
def test_newton_integers(self):
|
||||
# Regression test for gh-1741
|
||||
root = scipy.optimize.newton(lambda x: x**2 - 1, x0=2,
|
||||
fprime=lambda x: 2*x)
|
||||
assert_almost_equal(root, 1.0)
|
||||
|
||||
def test_lmdif_errmsg(self):
|
||||
# This shouldn't cause a crash on Python 3
|
||||
class SomeError(Exception):
|
||||
pass
|
||||
counter = [0]
|
||||
|
||||
def func(x):
|
||||
counter[0] += 1
|
||||
if counter[0] < 3:
|
||||
return x**2 - np.array([9, 10, 11])
|
||||
else:
|
||||
raise SomeError()
|
||||
assert_raises(SomeError,
|
||||
scipy.optimize.leastsq,
|
||||
func, [1, 2, 3])
|
||||
|
||||
604
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_slsqp.py
vendored
Normal file
604
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_slsqp.py
vendored
Normal file
@@ -0,0 +1,604 @@
|
||||
"""
|
||||
Unit test for SLSQP optimization.
|
||||
"""
|
||||
from numpy.testing import (assert_, assert_array_almost_equal,
|
||||
assert_allclose, assert_equal)
|
||||
from pytest import raises as assert_raises
|
||||
import pytest
|
||||
import numpy as np
|
||||
|
||||
from scipy.optimize import fmin_slsqp, minimize, Bounds, NonlinearConstraint
|
||||
|
||||
|
||||
class MyCallBack:
|
||||
"""pass a custom callback function
|
||||
|
||||
This makes sure it's being used.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.been_called = False
|
||||
self.ncalls = 0
|
||||
|
||||
def __call__(self, x):
|
||||
self.been_called = True
|
||||
self.ncalls += 1
|
||||
|
||||
|
||||
class TestSLSQP:
|
||||
"""
|
||||
Test SLSQP algorithm using Example 14.4 from Numerical Methods for
|
||||
Engineers by Steven Chapra and Raymond Canale.
|
||||
This example maximizes the function f(x) = 2*x*y + 2*x - x**2 - 2*y**2,
|
||||
which has a maximum at x=2, y=1.
|
||||
"""
|
||||
def setup_method(self):
|
||||
self.opts = {'disp': False}
|
||||
|
||||
def fun(self, d, sign=1.0):
|
||||
"""
|
||||
Arguments:
|
||||
d - A list of two elements, where d[0] represents x and d[1] represents y
|
||||
in the following equation.
|
||||
sign - A multiplier for f. Since we want to optimize it, and the SciPy
|
||||
optimizers can only minimize functions, we need to multiply it by
|
||||
-1 to achieve the desired solution
|
||||
Returns:
|
||||
2*x*y + 2*x - x**2 - 2*y**2
|
||||
|
||||
"""
|
||||
x = d[0]
|
||||
y = d[1]
|
||||
return sign*(2*x*y + 2*x - x**2 - 2*y**2)
|
||||
|
||||
def jac(self, d, sign=1.0):
|
||||
"""
|
||||
This is the derivative of fun, returning a NumPy array
|
||||
representing df/dx and df/dy.
|
||||
|
||||
"""
|
||||
x = d[0]
|
||||
y = d[1]
|
||||
dfdx = sign*(-2*x + 2*y + 2)
|
||||
dfdy = sign*(2*x - 4*y)
|
||||
return np.array([dfdx, dfdy], float)
|
||||
|
||||
def fun_and_jac(self, d, sign=1.0):
|
||||
return self.fun(d, sign), self.jac(d, sign)
|
||||
|
||||
def f_eqcon(self, x, sign=1.0):
|
||||
""" Equality constraint """
|
||||
return np.array([x[0] - x[1]])
|
||||
|
||||
def fprime_eqcon(self, x, sign=1.0):
|
||||
""" Equality constraint, derivative """
|
||||
return np.array([[1, -1]])
|
||||
|
||||
def f_eqcon_scalar(self, x, sign=1.0):
|
||||
""" Scalar equality constraint """
|
||||
return self.f_eqcon(x, sign)[0]
|
||||
|
||||
def fprime_eqcon_scalar(self, x, sign=1.0):
|
||||
""" Scalar equality constraint, derivative """
|
||||
return self.fprime_eqcon(x, sign)[0].tolist()
|
||||
|
||||
def f_ieqcon(self, x, sign=1.0):
|
||||
""" Inequality constraint """
|
||||
return np.array([x[0] - x[1] - 1.0])
|
||||
|
||||
def fprime_ieqcon(self, x, sign=1.0):
|
||||
""" Inequality constraint, derivative """
|
||||
return np.array([[1, -1]])
|
||||
|
||||
def f_ieqcon2(self, x):
|
||||
""" Vector inequality constraint """
|
||||
return np.asarray(x)
|
||||
|
||||
def fprime_ieqcon2(self, x):
|
||||
""" Vector inequality constraint, derivative """
|
||||
return np.identity(x.shape[0])
|
||||
|
||||
# minimize
|
||||
def test_minimize_unbounded_approximated(self):
|
||||
# Minimize, method='SLSQP': unbounded, approximated jacobian.
|
||||
jacs = [None, False, '2-point', '3-point']
|
||||
for jac in jacs:
|
||||
res = minimize(self.fun, [-1.0, 1.0], args=(-1.0, ),
|
||||
jac=jac, method='SLSQP',
|
||||
options=self.opts)
|
||||
assert_(res['success'], res['message'])
|
||||
assert_allclose(res.x, [2, 1])
|
||||
|
||||
def test_minimize_unbounded_given(self):
|
||||
# Minimize, method='SLSQP': unbounded, given Jacobian.
|
||||
res = minimize(self.fun, [-1.0, 1.0], args=(-1.0, ),
|
||||
jac=self.jac, method='SLSQP', options=self.opts)
|
||||
assert_(res['success'], res['message'])
|
||||
assert_allclose(res.x, [2, 1])
|
||||
|
||||
def test_minimize_bounded_approximated(self):
|
||||
# Minimize, method='SLSQP': bounded, approximated jacobian.
|
||||
jacs = [None, False, '2-point', '3-point']
|
||||
for jac in jacs:
|
||||
with np.errstate(invalid='ignore'):
|
||||
res = minimize(self.fun, [-1.0, 1.0], args=(-1.0, ),
|
||||
jac=jac,
|
||||
bounds=((2.5, None), (None, 0.5)),
|
||||
method='SLSQP', options=self.opts)
|
||||
assert_(res['success'], res['message'])
|
||||
assert_allclose(res.x, [2.5, 0.5])
|
||||
assert_(2.5 <= res.x[0])
|
||||
assert_(res.x[1] <= 0.5)
|
||||
|
||||
def test_minimize_unbounded_combined(self):
|
||||
# Minimize, method='SLSQP': unbounded, combined function and Jacobian.
|
||||
res = minimize(self.fun_and_jac, [-1.0, 1.0], args=(-1.0, ),
|
||||
jac=True, method='SLSQP', options=self.opts)
|
||||
assert_(res['success'], res['message'])
|
||||
assert_allclose(res.x, [2, 1])
|
||||
|
||||
def test_minimize_equality_approximated(self):
|
||||
# Minimize with method='SLSQP': equality constraint, approx. jacobian.
|
||||
jacs = [None, False, '2-point', '3-point']
|
||||
for jac in jacs:
|
||||
res = minimize(self.fun, [-1.0, 1.0], args=(-1.0, ),
|
||||
jac=jac,
|
||||
constraints={'type': 'eq',
|
||||
'fun': self.f_eqcon,
|
||||
'args': (-1.0, )},
|
||||
method='SLSQP', options=self.opts)
|
||||
assert_(res['success'], res['message'])
|
||||
assert_allclose(res.x, [1, 1])
|
||||
|
||||
def test_minimize_equality_given(self):
|
||||
# Minimize with method='SLSQP': equality constraint, given Jacobian.
|
||||
res = minimize(self.fun, [-1.0, 1.0], jac=self.jac,
|
||||
method='SLSQP', args=(-1.0,),
|
||||
constraints={'type': 'eq', 'fun':self.f_eqcon,
|
||||
'args': (-1.0, )},
|
||||
options=self.opts)
|
||||
assert_(res['success'], res['message'])
|
||||
assert_allclose(res.x, [1, 1])
|
||||
|
||||
def test_minimize_equality_given2(self):
|
||||
# Minimize with method='SLSQP': equality constraint, given Jacobian
|
||||
# for fun and const.
|
||||
res = minimize(self.fun, [-1.0, 1.0], method='SLSQP',
|
||||
jac=self.jac, args=(-1.0,),
|
||||
constraints={'type': 'eq',
|
||||
'fun': self.f_eqcon,
|
||||
'args': (-1.0, ),
|
||||
'jac': self.fprime_eqcon},
|
||||
options=self.opts)
|
||||
assert_(res['success'], res['message'])
|
||||
assert_allclose(res.x, [1, 1])
|
||||
|
||||
def test_minimize_equality_given_cons_scalar(self):
|
||||
# Minimize with method='SLSQP': scalar equality constraint, given
|
||||
# Jacobian for fun and const.
|
||||
res = minimize(self.fun, [-1.0, 1.0], method='SLSQP',
|
||||
jac=self.jac, args=(-1.0,),
|
||||
constraints={'type': 'eq',
|
||||
'fun': self.f_eqcon_scalar,
|
||||
'args': (-1.0, ),
|
||||
'jac': self.fprime_eqcon_scalar},
|
||||
options=self.opts)
|
||||
assert_(res['success'], res['message'])
|
||||
assert_allclose(res.x, [1, 1])
|
||||
|
||||
def test_minimize_inequality_given(self):
|
||||
# Minimize with method='SLSQP': inequality constraint, given Jacobian.
|
||||
res = minimize(self.fun, [-1.0, 1.0], method='SLSQP',
|
||||
jac=self.jac, args=(-1.0, ),
|
||||
constraints={'type': 'ineq',
|
||||
'fun': self.f_ieqcon,
|
||||
'args': (-1.0, )},
|
||||
options=self.opts)
|
||||
assert_(res['success'], res['message'])
|
||||
assert_allclose(res.x, [2, 1], atol=1e-3)
|
||||
|
||||
def test_minimize_inequality_given_vector_constraints(self):
|
||||
# Minimize with method='SLSQP': vector inequality constraint, given
|
||||
# Jacobian.
|
||||
res = minimize(self.fun, [-1.0, 1.0], jac=self.jac,
|
||||
method='SLSQP', args=(-1.0,),
|
||||
constraints={'type': 'ineq',
|
||||
'fun': self.f_ieqcon2,
|
||||
'jac': self.fprime_ieqcon2},
|
||||
options=self.opts)
|
||||
assert_(res['success'], res['message'])
|
||||
assert_allclose(res.x, [2, 1])
|
||||
|
||||
def test_minimize_bounded_constraint(self):
|
||||
# when the constraint makes the solver go up against a parameter
|
||||
# bound make sure that the numerical differentiation of the
|
||||
# jacobian doesn't try to exceed that bound using a finite difference.
|
||||
# gh11403
|
||||
def c(x):
|
||||
assert 0 <= x[0] <= 1 and 0 <= x[1] <= 1, x
|
||||
return x[0] ** 0.5 + x[1]
|
||||
|
||||
def f(x):
|
||||
assert 0 <= x[0] <= 1 and 0 <= x[1] <= 1, x
|
||||
return -x[0] ** 2 + x[1] ** 2
|
||||
|
||||
cns = [NonlinearConstraint(c, 0, 1.5)]
|
||||
x0 = np.asarray([0.9, 0.5])
|
||||
bnd = Bounds([0., 0.], [1.0, 1.0])
|
||||
minimize(f, x0, method='SLSQP', bounds=bnd, constraints=cns)
|
||||
|
||||
def test_minimize_bound_equality_given2(self):
|
||||
# Minimize with method='SLSQP': bounds, eq. const., given jac. for
|
||||
# fun. and const.
|
||||
res = minimize(self.fun, [-1.0, 1.0], method='SLSQP',
|
||||
jac=self.jac, args=(-1.0, ),
|
||||
bounds=[(-0.8, 1.), (-1, 0.8)],
|
||||
constraints={'type': 'eq',
|
||||
'fun': self.f_eqcon,
|
||||
'args': (-1.0, ),
|
||||
'jac': self.fprime_eqcon},
|
||||
options=self.opts)
|
||||
assert_(res['success'], res['message'])
|
||||
assert_allclose(res.x, [0.8, 0.8], atol=1e-3)
|
||||
assert_(-0.8 <= res.x[0] <= 1)
|
||||
assert_(-1 <= res.x[1] <= 0.8)
|
||||
|
||||
# fmin_slsqp
|
||||
def test_unbounded_approximated(self):
|
||||
# SLSQP: unbounded, approximated Jacobian.
|
||||
res = fmin_slsqp(self.fun, [-1.0, 1.0], args=(-1.0, ),
|
||||
iprint = 0, full_output = 1)
|
||||
x, fx, its, imode, smode = res
|
||||
assert_(imode == 0, imode)
|
||||
assert_array_almost_equal(x, [2, 1])
|
||||
|
||||
def test_unbounded_given(self):
|
||||
# SLSQP: unbounded, given Jacobian.
|
||||
res = fmin_slsqp(self.fun, [-1.0, 1.0], args=(-1.0, ),
|
||||
fprime = self.jac, iprint = 0,
|
||||
full_output = 1)
|
||||
x, fx, its, imode, smode = res
|
||||
assert_(imode == 0, imode)
|
||||
assert_array_almost_equal(x, [2, 1])
|
||||
|
||||
def test_equality_approximated(self):
|
||||
# SLSQP: equality constraint, approximated Jacobian.
|
||||
res = fmin_slsqp(self.fun,[-1.0,1.0], args=(-1.0,),
|
||||
eqcons = [self.f_eqcon],
|
||||
iprint = 0, full_output = 1)
|
||||
x, fx, its, imode, smode = res
|
||||
assert_(imode == 0, imode)
|
||||
assert_array_almost_equal(x, [1, 1])
|
||||
|
||||
def test_equality_given(self):
|
||||
# SLSQP: equality constraint, given Jacobian.
|
||||
res = fmin_slsqp(self.fun, [-1.0, 1.0],
|
||||
fprime=self.jac, args=(-1.0,),
|
||||
eqcons = [self.f_eqcon], iprint = 0,
|
||||
full_output = 1)
|
||||
x, fx, its, imode, smode = res
|
||||
assert_(imode == 0, imode)
|
||||
assert_array_almost_equal(x, [1, 1])
|
||||
|
||||
def test_equality_given2(self):
|
||||
# SLSQP: equality constraint, given Jacobian for fun and const.
|
||||
res = fmin_slsqp(self.fun, [-1.0, 1.0],
|
||||
fprime=self.jac, args=(-1.0,),
|
||||
f_eqcons = self.f_eqcon,
|
||||
fprime_eqcons = self.fprime_eqcon,
|
||||
iprint = 0,
|
||||
full_output = 1)
|
||||
x, fx, its, imode, smode = res
|
||||
assert_(imode == 0, imode)
|
||||
assert_array_almost_equal(x, [1, 1])
|
||||
|
||||
def test_inequality_given(self):
|
||||
# SLSQP: inequality constraint, given Jacobian.
|
||||
res = fmin_slsqp(self.fun, [-1.0, 1.0],
|
||||
fprime=self.jac, args=(-1.0, ),
|
||||
ieqcons = [self.f_ieqcon],
|
||||
iprint = 0, full_output = 1)
|
||||
x, fx, its, imode, smode = res
|
||||
assert_(imode == 0, imode)
|
||||
assert_array_almost_equal(x, [2, 1], decimal=3)
|
||||
|
||||
def test_bound_equality_given2(self):
|
||||
# SLSQP: bounds, eq. const., given jac. for fun. and const.
|
||||
res = fmin_slsqp(self.fun, [-1.0, 1.0],
|
||||
fprime=self.jac, args=(-1.0, ),
|
||||
bounds = [(-0.8, 1.), (-1, 0.8)],
|
||||
f_eqcons = self.f_eqcon,
|
||||
fprime_eqcons = self.fprime_eqcon,
|
||||
iprint = 0, full_output = 1)
|
||||
x, fx, its, imode, smode = res
|
||||
assert_(imode == 0, imode)
|
||||
assert_array_almost_equal(x, [0.8, 0.8], decimal=3)
|
||||
assert_(-0.8 <= x[0] <= 1)
|
||||
assert_(-1 <= x[1] <= 0.8)
|
||||
|
||||
def test_scalar_constraints(self):
|
||||
# Regression test for gh-2182
|
||||
x = fmin_slsqp(lambda z: z**2, [3.],
|
||||
ieqcons=[lambda z: z[0] - 1],
|
||||
iprint=0)
|
||||
assert_array_almost_equal(x, [1.])
|
||||
|
||||
x = fmin_slsqp(lambda z: z**2, [3.],
|
||||
f_ieqcons=lambda z: [z[0] - 1],
|
||||
iprint=0)
|
||||
assert_array_almost_equal(x, [1.])
|
||||
|
||||
def test_integer_bounds(self):
|
||||
# This should not raise an exception
|
||||
fmin_slsqp(lambda z: z**2 - 1, [0], bounds=[[0, 1]], iprint=0)
|
||||
|
||||
def test_array_bounds(self):
|
||||
# NumPy used to treat n-dimensional 1-element arrays as scalars
|
||||
# in some cases. The handling of `bounds` by `fmin_slsqp` still
|
||||
# supports this behavior.
|
||||
bounds = [(-np.inf, np.inf), (np.array([2]), np.array([3]))]
|
||||
x = fmin_slsqp(lambda z: np.sum(z**2 - 1), [2.5, 2.5], bounds=bounds,
|
||||
iprint=0)
|
||||
assert_array_almost_equal(x, [0, 2])
|
||||
|
||||
def test_obj_must_return_scalar(self):
|
||||
# Regression test for Github Issue #5433
|
||||
# If objective function does not return a scalar, raises ValueError
|
||||
with assert_raises(ValueError):
|
||||
fmin_slsqp(lambda x: [0, 1], [1, 2, 3])
|
||||
|
||||
def test_obj_returns_scalar_in_list(self):
|
||||
# Test for Github Issue #5433 and PR #6691
|
||||
# Objective function should be able to return length-1 Python list
|
||||
# containing the scalar
|
||||
fmin_slsqp(lambda x: [0], [1, 2, 3], iprint=0)
|
||||
|
||||
def test_callback(self):
|
||||
# Minimize, method='SLSQP': unbounded, approximated jacobian. Check for callback
|
||||
callback = MyCallBack()
|
||||
res = minimize(self.fun, [-1.0, 1.0], args=(-1.0, ),
|
||||
method='SLSQP', callback=callback, options=self.opts)
|
||||
assert_(res['success'], res['message'])
|
||||
assert_(callback.been_called)
|
||||
assert_equal(callback.ncalls, res['nit'])
|
||||
|
||||
def test_inconsistent_linearization(self):
|
||||
# SLSQP must be able to solve this problem, even if the
|
||||
# linearized problem at the starting point is infeasible.
|
||||
|
||||
# Linearized constraints are
|
||||
#
|
||||
# 2*x0[0]*x[0] >= 1
|
||||
#
|
||||
# At x0 = [0, 1], the second constraint is clearly infeasible.
|
||||
# This triggers a call with n2==1 in the LSQ subroutine.
|
||||
x = [0, 1]
|
||||
f1 = lambda x: x[0] + x[1] - 2
|
||||
f2 = lambda x: x[0]**2 - 1
|
||||
sol = minimize(
|
||||
lambda x: x[0]**2 + x[1]**2,
|
||||
x,
|
||||
constraints=({'type':'eq','fun': f1},
|
||||
{'type':'ineq','fun': f2}),
|
||||
bounds=((0,None), (0,None)),
|
||||
method='SLSQP')
|
||||
x = sol.x
|
||||
|
||||
assert_allclose(f1(x), 0, atol=1e-8)
|
||||
assert_(f2(x) >= -1e-8)
|
||||
assert_(sol.success, sol)
|
||||
|
||||
def test_regression_5743(self):
|
||||
# SLSQP must not indicate success for this problem,
|
||||
# which is infeasible.
|
||||
x = [1, 2]
|
||||
sol = minimize(
|
||||
lambda x: x[0]**2 + x[1]**2,
|
||||
x,
|
||||
constraints=({'type':'eq','fun': lambda x: x[0]+x[1]-1},
|
||||
{'type':'ineq','fun': lambda x: x[0]-2}),
|
||||
bounds=((0,None), (0,None)),
|
||||
method='SLSQP')
|
||||
assert_(not sol.success, sol)
|
||||
|
||||
def test_gh_6676(self):
|
||||
def func(x):
|
||||
return (x[0] - 1)**2 + 2*(x[1] - 1)**2 + 0.5*(x[2] - 1)**2
|
||||
|
||||
sol = minimize(func, [0, 0, 0], method='SLSQP')
|
||||
assert_(sol.jac.shape == (3,))
|
||||
|
||||
def test_invalid_bounds(self):
|
||||
# Raise correct error when lower bound is greater than upper bound.
|
||||
# See Github issue 6875.
|
||||
bounds_list = [
|
||||
((1, 2), (2, 1)),
|
||||
((2, 1), (1, 2)),
|
||||
((2, 1), (2, 1)),
|
||||
((np.inf, 0), (np.inf, 0)),
|
||||
((1, -np.inf), (0, 1)),
|
||||
]
|
||||
for bounds in bounds_list:
|
||||
with assert_raises(ValueError):
|
||||
minimize(self.fun, [-1.0, 1.0], bounds=bounds, method='SLSQP')
|
||||
|
||||
def test_bounds_clipping(self):
|
||||
#
|
||||
# SLSQP returns bogus results for initial guess out of bounds, gh-6859
|
||||
#
|
||||
def f(x):
|
||||
return (x[0] - 1)**2
|
||||
|
||||
sol = minimize(f, [10], method='slsqp', bounds=[(None, 0)])
|
||||
assert_(sol.success)
|
||||
assert_allclose(sol.x, 0, atol=1e-10)
|
||||
|
||||
sol = minimize(f, [-10], method='slsqp', bounds=[(2, None)])
|
||||
assert_(sol.success)
|
||||
assert_allclose(sol.x, 2, atol=1e-10)
|
||||
|
||||
sol = minimize(f, [-10], method='slsqp', bounds=[(None, 0)])
|
||||
assert_(sol.success)
|
||||
assert_allclose(sol.x, 0, atol=1e-10)
|
||||
|
||||
sol = minimize(f, [10], method='slsqp', bounds=[(2, None)])
|
||||
assert_(sol.success)
|
||||
assert_allclose(sol.x, 2, atol=1e-10)
|
||||
|
||||
sol = minimize(f, [-0.5], method='slsqp', bounds=[(-1, 0)])
|
||||
assert_(sol.success)
|
||||
assert_allclose(sol.x, 0, atol=1e-10)
|
||||
|
||||
sol = minimize(f, [10], method='slsqp', bounds=[(-1, 0)])
|
||||
assert_(sol.success)
|
||||
assert_allclose(sol.x, 0, atol=1e-10)
|
||||
|
||||
def test_infeasible_initial(self):
|
||||
# Check SLSQP behavior with infeasible initial point
|
||||
def f(x):
|
||||
x, = x
|
||||
return x*x - 2*x + 1
|
||||
|
||||
cons_u = [{'type': 'ineq', 'fun': lambda x: 0 - x}]
|
||||
cons_l = [{'type': 'ineq', 'fun': lambda x: x - 2}]
|
||||
cons_ul = [{'type': 'ineq', 'fun': lambda x: 0 - x},
|
||||
{'type': 'ineq', 'fun': lambda x: x + 1}]
|
||||
|
||||
sol = minimize(f, [10], method='slsqp', constraints=cons_u)
|
||||
assert_(sol.success)
|
||||
assert_allclose(sol.x, 0, atol=1e-10)
|
||||
|
||||
sol = minimize(f, [-10], method='slsqp', constraints=cons_l)
|
||||
assert_(sol.success)
|
||||
assert_allclose(sol.x, 2, atol=1e-10)
|
||||
|
||||
sol = minimize(f, [-10], method='slsqp', constraints=cons_u)
|
||||
assert_(sol.success)
|
||||
assert_allclose(sol.x, 0, atol=1e-10)
|
||||
|
||||
sol = minimize(f, [10], method='slsqp', constraints=cons_l)
|
||||
assert_(sol.success)
|
||||
assert_allclose(sol.x, 2, atol=1e-10)
|
||||
|
||||
sol = minimize(f, [-0.5], method='slsqp', constraints=cons_ul)
|
||||
assert_(sol.success)
|
||||
assert_allclose(sol.x, 0, atol=1e-10)
|
||||
|
||||
sol = minimize(f, [10], method='slsqp', constraints=cons_ul)
|
||||
assert_(sol.success)
|
||||
assert_allclose(sol.x, 0, atol=1e-10)
|
||||
|
||||
def test_inconsistent_inequalities(self):
|
||||
# gh-7618
|
||||
|
||||
def cost(x):
|
||||
return -1 * x[0] + 4 * x[1]
|
||||
|
||||
def ineqcons1(x):
|
||||
return x[1] - x[0] - 1
|
||||
|
||||
def ineqcons2(x):
|
||||
return x[0] - x[1]
|
||||
|
||||
# The inequalities are inconsistent, so no solution can exist:
|
||||
#
|
||||
# x1 >= x0 + 1
|
||||
# x0 >= x1
|
||||
|
||||
x0 = (1,5)
|
||||
bounds = ((-5, 5), (-5, 5))
|
||||
cons = (dict(type='ineq', fun=ineqcons1), dict(type='ineq', fun=ineqcons2))
|
||||
res = minimize(cost, x0, method='SLSQP', bounds=bounds, constraints=cons)
|
||||
|
||||
assert_(not res.success)
|
||||
|
||||
def test_new_bounds_type(self):
|
||||
f = lambda x: x[0]**2 + x[1]**2
|
||||
bounds = Bounds([1, 0], [np.inf, np.inf])
|
||||
sol = minimize(f, [0, 0], method='slsqp', bounds=bounds)
|
||||
assert_(sol.success)
|
||||
assert_allclose(sol.x, [1, 0])
|
||||
|
||||
def test_nested_minimization(self):
|
||||
|
||||
class NestedProblem():
|
||||
|
||||
def __init__(self):
|
||||
self.F_outer_count = 0
|
||||
|
||||
def F_outer(self, x):
|
||||
self.F_outer_count += 1
|
||||
if self.F_outer_count > 1000:
|
||||
raise Exception("Nested minimization failed to terminate.")
|
||||
inner_res = minimize(self.F_inner, (3, 4), method="SLSQP")
|
||||
assert_(inner_res.success)
|
||||
assert_allclose(inner_res.x, [1, 1])
|
||||
return x[0]**2 + x[1]**2 + x[2]**2
|
||||
|
||||
def F_inner(self, x):
|
||||
return (x[0] - 1)**2 + (x[1] - 1)**2
|
||||
|
||||
def solve(self):
|
||||
outer_res = minimize(self.F_outer, (5, 5, 5), method="SLSQP")
|
||||
assert_(outer_res.success)
|
||||
assert_allclose(outer_res.x, [0, 0, 0])
|
||||
|
||||
problem = NestedProblem()
|
||||
problem.solve()
|
||||
|
||||
def test_gh1758(self):
|
||||
# the test suggested in gh1758
|
||||
# https://nlopt.readthedocs.io/en/latest/NLopt_Tutorial/
|
||||
# implement two equality constraints, in R^2.
|
||||
def fun(x):
|
||||
return np.sqrt(x[1])
|
||||
|
||||
def f_eqcon(x):
|
||||
""" Equality constraint """
|
||||
return x[1] - (2 * x[0]) ** 3
|
||||
|
||||
def f_eqcon2(x):
|
||||
""" Equality constraint """
|
||||
return x[1] - (-x[0] + 1) ** 3
|
||||
|
||||
c1 = {'type': 'eq', 'fun': f_eqcon}
|
||||
c2 = {'type': 'eq', 'fun': f_eqcon2}
|
||||
|
||||
res = minimize(fun, [8, 0.25], method='SLSQP',
|
||||
constraints=[c1, c2], bounds=[(-0.5, 1), (0, 8)])
|
||||
|
||||
np.testing.assert_allclose(res.fun, 0.5443310539518)
|
||||
np.testing.assert_allclose(res.x, [0.33333333, 0.2962963])
|
||||
assert res.success
|
||||
|
||||
def test_gh9640(self):
|
||||
np.random.seed(10)
|
||||
cons = ({'type': 'ineq', 'fun': lambda x: -x[0] - x[1] - 3},
|
||||
{'type': 'ineq', 'fun': lambda x: x[1] + x[2] - 2})
|
||||
bnds = ((-2, 2), (-2, 2), (-2, 2))
|
||||
|
||||
target = lambda x: 1
|
||||
x0 = [-1.8869783504471584, -0.640096352696244, -0.8174212253407696]
|
||||
res = minimize(target, x0, method='SLSQP', bounds=bnds, constraints=cons,
|
||||
options={'disp':False, 'maxiter':10000})
|
||||
|
||||
# The problem is infeasible, so it cannot succeed
|
||||
assert not res.success
|
||||
|
||||
def test_parameters_stay_within_bounds(self):
|
||||
# gh11403. For some problems the SLSQP Fortran code suggests a step
|
||||
# outside one of the lower/upper bounds. When this happens
|
||||
# approx_derivative complains because it's being asked to evaluate
|
||||
# a gradient outside its domain.
|
||||
np.random.seed(1)
|
||||
bounds = Bounds(np.array([0.1]), np.array([1.0]))
|
||||
n_inputs = len(bounds.lb)
|
||||
x0 = np.array(bounds.lb + (bounds.ub - bounds.lb) *
|
||||
np.random.random(n_inputs))
|
||||
|
||||
def f(x):
|
||||
assert (x >= bounds.lb).all()
|
||||
return np.linalg.norm(x)
|
||||
|
||||
with pytest.warns(RuntimeWarning, match='x were outside bounds'):
|
||||
res = minimize(f, x0, method='SLSQP', bounds=bounds)
|
||||
assert res.success
|
||||
355
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_tnc.py
vendored
Normal file
355
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_tnc.py
vendored
Normal file
@@ -0,0 +1,355 @@
|
||||
"""
|
||||
Unit tests for TNC optimization routine from tnc.py
|
||||
"""
|
||||
import pytest
|
||||
from numpy.testing import assert_allclose, assert_equal
|
||||
|
||||
import numpy as np
|
||||
from math import pow
|
||||
|
||||
from scipy import optimize
|
||||
from scipy.sparse._sputils import matrix
|
||||
|
||||
|
||||
class TestTnc:
|
||||
"""TNC non-linear optimization.
|
||||
|
||||
These tests are taken from Prof. K. Schittkowski's test examples
|
||||
for constrained non-linear programming.
|
||||
|
||||
http://www.uni-bayreuth.de/departments/math/~kschittkowski/home.htm
|
||||
|
||||
"""
|
||||
def setup_method(self):
|
||||
# options for minimize
|
||||
self.opts = {'disp': False, 'maxfun': 200}
|
||||
|
||||
# objective functions and Jacobian for each test
|
||||
def f1(self, x, a=100.0):
|
||||
return a * pow((x[1] - pow(x[0], 2)), 2) + pow(1.0 - x[0], 2)
|
||||
|
||||
def g1(self, x, a=100.0):
|
||||
dif = [0, 0]
|
||||
dif[1] = 2 * a * (x[1] - pow(x[0], 2))
|
||||
dif[0] = -2.0 * (x[0] * (dif[1] - 1.0) + 1.0)
|
||||
return dif
|
||||
|
||||
def fg1(self, x, a=100.0):
|
||||
return self.f1(x, a), self.g1(x, a)
|
||||
|
||||
def f3(self, x):
|
||||
return x[1] + pow(x[1] - x[0], 2) * 1.0e-5
|
||||
|
||||
def g3(self, x):
|
||||
dif = [0, 0]
|
||||
dif[0] = -2.0 * (x[1] - x[0]) * 1.0e-5
|
||||
dif[1] = 1.0 - dif[0]
|
||||
return dif
|
||||
|
||||
def fg3(self, x):
|
||||
return self.f3(x), self.g3(x)
|
||||
|
||||
def f4(self, x):
|
||||
return pow(x[0] + 1.0, 3) / 3.0 + x[1]
|
||||
|
||||
def g4(self, x):
|
||||
dif = [0, 0]
|
||||
dif[0] = pow(x[0] + 1.0, 2)
|
||||
dif[1] = 1.0
|
||||
return dif
|
||||
|
||||
def fg4(self, x):
|
||||
return self.f4(x), self.g4(x)
|
||||
|
||||
def f5(self, x):
|
||||
return np.sin(x[0] + x[1]) + pow(x[0] - x[1], 2) - \
|
||||
1.5 * x[0] + 2.5 * x[1] + 1.0
|
||||
|
||||
def g5(self, x):
|
||||
dif = [0, 0]
|
||||
v1 = np.cos(x[0] + x[1])
|
||||
v2 = 2.0*(x[0] - x[1])
|
||||
|
||||
dif[0] = v1 + v2 - 1.5
|
||||
dif[1] = v1 - v2 + 2.5
|
||||
return dif
|
||||
|
||||
def fg5(self, x):
|
||||
return self.f5(x), self.g5(x)
|
||||
|
||||
def f38(self, x):
|
||||
return (100.0 * pow(x[1] - pow(x[0], 2), 2) +
|
||||
pow(1.0 - x[0], 2) + 90.0 * pow(x[3] - pow(x[2], 2), 2) +
|
||||
pow(1.0 - x[2], 2) + 10.1 * (pow(x[1] - 1.0, 2) +
|
||||
pow(x[3] - 1.0, 2)) +
|
||||
19.8 * (x[1] - 1.0) * (x[3] - 1.0)) * 1.0e-5
|
||||
|
||||
def g38(self, x):
|
||||
dif = [0, 0, 0, 0]
|
||||
dif[0] = (-400.0 * x[0] * (x[1] - pow(x[0], 2)) -
|
||||
2.0 * (1.0 - x[0])) * 1.0e-5
|
||||
dif[1] = (200.0 * (x[1] - pow(x[0], 2)) + 20.2 * (x[1] - 1.0) +
|
||||
19.8 * (x[3] - 1.0)) * 1.0e-5
|
||||
dif[2] = (- 360.0 * x[2] * (x[3] - pow(x[2], 2)) -
|
||||
2.0 * (1.0 - x[2])) * 1.0e-5
|
||||
dif[3] = (180.0 * (x[3] - pow(x[2], 2)) + 20.2 * (x[3] - 1.0) +
|
||||
19.8 * (x[1] - 1.0)) * 1.0e-5
|
||||
return dif
|
||||
|
||||
def fg38(self, x):
|
||||
return self.f38(x), self.g38(x)
|
||||
|
||||
def f45(self, x):
|
||||
return 2.0 - x[0] * x[1] * x[2] * x[3] * x[4] / 120.0
|
||||
|
||||
def g45(self, x):
|
||||
dif = [0] * 5
|
||||
dif[0] = - x[1] * x[2] * x[3] * x[4] / 120.0
|
||||
dif[1] = - x[0] * x[2] * x[3] * x[4] / 120.0
|
||||
dif[2] = - x[0] * x[1] * x[3] * x[4] / 120.0
|
||||
dif[3] = - x[0] * x[1] * x[2] * x[4] / 120.0
|
||||
dif[4] = - x[0] * x[1] * x[2] * x[3] / 120.0
|
||||
return dif
|
||||
|
||||
def fg45(self, x):
|
||||
return self.f45(x), self.g45(x)
|
||||
|
||||
# tests
|
||||
# minimize with method=TNC
|
||||
def test_minimize_tnc1(self):
|
||||
x0, bnds = [-2, 1], ([-np.inf, None], [-1.5, None])
|
||||
xopt = [1, 1]
|
||||
iterx = [] # to test callback
|
||||
|
||||
res = optimize.minimize(self.f1, x0, method='TNC', jac=self.g1,
|
||||
bounds=bnds, options=self.opts,
|
||||
callback=iterx.append)
|
||||
assert_allclose(res.fun, self.f1(xopt), atol=1e-8)
|
||||
assert_equal(len(iterx), res.nit)
|
||||
|
||||
def test_minimize_tnc1b(self):
|
||||
x0, bnds = matrix([-2, 1]), ([-np.inf, None],[-1.5, None])
|
||||
xopt = [1, 1]
|
||||
message = 'Use of `minimize` with `x0.ndim != 1` is deprecated.'
|
||||
with pytest.warns(DeprecationWarning, match=message):
|
||||
x = optimize.minimize(self.f1, x0, method='TNC',
|
||||
bounds=bnds, options=self.opts).x
|
||||
assert_allclose(self.f1(x), self.f1(xopt), atol=1e-4)
|
||||
|
||||
def test_minimize_tnc1c(self):
|
||||
x0, bnds = [-2, 1], ([-np.inf, None],[-1.5, None])
|
||||
xopt = [1, 1]
|
||||
x = optimize.minimize(self.fg1, x0, method='TNC',
|
||||
jac=True, bounds=bnds,
|
||||
options=self.opts).x
|
||||
assert_allclose(self.f1(x), self.f1(xopt), atol=1e-8)
|
||||
|
||||
def test_minimize_tnc2(self):
|
||||
x0, bnds = [-2, 1], ([-np.inf, None], [1.5, None])
|
||||
xopt = [-1.2210262419616387, 1.5]
|
||||
x = optimize.minimize(self.f1, x0, method='TNC',
|
||||
jac=self.g1, bounds=bnds,
|
||||
options=self.opts).x
|
||||
assert_allclose(self.f1(x), self.f1(xopt), atol=1e-8)
|
||||
|
||||
def test_minimize_tnc3(self):
|
||||
x0, bnds = [10, 1], ([-np.inf, None], [0.0, None])
|
||||
xopt = [0, 0]
|
||||
x = optimize.minimize(self.f3, x0, method='TNC',
|
||||
jac=self.g3, bounds=bnds,
|
||||
options=self.opts).x
|
||||
assert_allclose(self.f3(x), self.f3(xopt), atol=1e-8)
|
||||
|
||||
def test_minimize_tnc4(self):
|
||||
x0,bnds = [1.125, 0.125], [(1, None), (0, None)]
|
||||
xopt = [1, 0]
|
||||
x = optimize.minimize(self.f4, x0, method='TNC',
|
||||
jac=self.g4, bounds=bnds,
|
||||
options=self.opts).x
|
||||
assert_allclose(self.f4(x), self.f4(xopt), atol=1e-8)
|
||||
|
||||
def test_minimize_tnc5(self):
|
||||
x0, bnds = [0, 0], [(-1.5, 4),(-3, 3)]
|
||||
xopt = [-0.54719755119659763, -1.5471975511965976]
|
||||
x = optimize.minimize(self.f5, x0, method='TNC',
|
||||
jac=self.g5, bounds=bnds,
|
||||
options=self.opts).x
|
||||
assert_allclose(self.f5(x), self.f5(xopt), atol=1e-8)
|
||||
|
||||
def test_minimize_tnc38(self):
|
||||
x0, bnds = np.array([-3, -1, -3, -1]), [(-10, 10)]*4
|
||||
xopt = [1]*4
|
||||
x = optimize.minimize(self.f38, x0, method='TNC',
|
||||
jac=self.g38, bounds=bnds,
|
||||
options=self.opts).x
|
||||
assert_allclose(self.f38(x), self.f38(xopt), atol=1e-8)
|
||||
|
||||
def test_minimize_tnc45(self):
|
||||
x0, bnds = [2] * 5, [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]
|
||||
xopt = [1, 2, 3, 4, 5]
|
||||
x = optimize.minimize(self.f45, x0, method='TNC',
|
||||
jac=self.g45, bounds=bnds,
|
||||
options=self.opts).x
|
||||
assert_allclose(self.f45(x), self.f45(xopt), atol=1e-8)
|
||||
|
||||
# fmin_tnc
|
||||
def test_tnc1(self):
|
||||
fg, x, bounds = self.fg1, [-2, 1], ([-np.inf, None], [-1.5, None])
|
||||
xopt = [1, 1]
|
||||
|
||||
x, nf, rc = optimize.fmin_tnc(fg, x, bounds=bounds, args=(100.0, ),
|
||||
messages=optimize._tnc.MSG_NONE,
|
||||
maxfun=200)
|
||||
|
||||
assert_allclose(self.f1(x), self.f1(xopt), atol=1e-8,
|
||||
err_msg="TNC failed with status: " +
|
||||
optimize._tnc.RCSTRINGS[rc])
|
||||
|
||||
def test_tnc1b(self):
|
||||
x, bounds = [-2, 1], ([-np.inf, None], [-1.5, None])
|
||||
xopt = [1, 1]
|
||||
|
||||
x, nf, rc = optimize.fmin_tnc(self.f1, x, approx_grad=True,
|
||||
bounds=bounds,
|
||||
messages=optimize._tnc.MSG_NONE,
|
||||
maxfun=200)
|
||||
|
||||
assert_allclose(self.f1(x), self.f1(xopt), atol=1e-4,
|
||||
err_msg="TNC failed with status: " +
|
||||
optimize._tnc.RCSTRINGS[rc])
|
||||
|
||||
def test_tnc1c(self):
|
||||
x, bounds = [-2, 1], ([-np.inf, None], [-1.5, None])
|
||||
xopt = [1, 1]
|
||||
|
||||
x, nf, rc = optimize.fmin_tnc(self.f1, x, fprime=self.g1,
|
||||
bounds=bounds,
|
||||
messages=optimize._tnc.MSG_NONE,
|
||||
maxfun=200)
|
||||
|
||||
assert_allclose(self.f1(x), self.f1(xopt), atol=1e-8,
|
||||
err_msg="TNC failed with status: " +
|
||||
optimize._tnc.RCSTRINGS[rc])
|
||||
|
||||
def test_tnc2(self):
|
||||
fg, x, bounds = self.fg1, [-2, 1], ([-np.inf, None], [1.5, None])
|
||||
xopt = [-1.2210262419616387, 1.5]
|
||||
|
||||
x, nf, rc = optimize.fmin_tnc(fg, x, bounds=bounds,
|
||||
messages=optimize._tnc.MSG_NONE,
|
||||
maxfun=200)
|
||||
|
||||
assert_allclose(self.f1(x), self.f1(xopt), atol=1e-8,
|
||||
err_msg="TNC failed with status: " +
|
||||
optimize._tnc.RCSTRINGS[rc])
|
||||
|
||||
def test_tnc3(self):
|
||||
fg, x, bounds = self.fg3, [10, 1], ([-np.inf, None], [0.0, None])
|
||||
xopt = [0, 0]
|
||||
|
||||
x, nf, rc = optimize.fmin_tnc(fg, x, bounds=bounds,
|
||||
messages=optimize._tnc.MSG_NONE,
|
||||
maxfun=200)
|
||||
|
||||
assert_allclose(self.f3(x), self.f3(xopt), atol=1e-8,
|
||||
err_msg="TNC failed with status: " +
|
||||
optimize._tnc.RCSTRINGS[rc])
|
||||
|
||||
def test_tnc4(self):
|
||||
fg, x, bounds = self.fg4, [1.125, 0.125], [(1, None), (0, None)]
|
||||
xopt = [1, 0]
|
||||
|
||||
x, nf, rc = optimize.fmin_tnc(fg, x, bounds=bounds,
|
||||
messages=optimize._tnc.MSG_NONE,
|
||||
maxfun=200)
|
||||
|
||||
assert_allclose(self.f4(x), self.f4(xopt), atol=1e-8,
|
||||
err_msg="TNC failed with status: " +
|
||||
optimize._tnc.RCSTRINGS[rc])
|
||||
|
||||
def test_tnc5(self):
|
||||
fg, x, bounds = self.fg5, [0, 0], [(-1.5, 4),(-3, 3)]
|
||||
xopt = [-0.54719755119659763, -1.5471975511965976]
|
||||
|
||||
x, nf, rc = optimize.fmin_tnc(fg, x, bounds=bounds,
|
||||
messages=optimize._tnc.MSG_NONE,
|
||||
maxfun=200)
|
||||
|
||||
assert_allclose(self.f5(x), self.f5(xopt), atol=1e-8,
|
||||
err_msg="TNC failed with status: " +
|
||||
optimize._tnc.RCSTRINGS[rc])
|
||||
|
||||
def test_tnc38(self):
|
||||
fg, x, bounds = self.fg38, np.array([-3, -1, -3, -1]), [(-10, 10)]*4
|
||||
xopt = [1]*4
|
||||
|
||||
x, nf, rc = optimize.fmin_tnc(fg, x, bounds=bounds,
|
||||
messages=optimize._tnc.MSG_NONE,
|
||||
maxfun=200)
|
||||
|
||||
assert_allclose(self.f38(x), self.f38(xopt), atol=1e-8,
|
||||
err_msg="TNC failed with status: " +
|
||||
optimize._tnc.RCSTRINGS[rc])
|
||||
|
||||
def test_tnc45(self):
|
||||
fg, x, bounds = self.fg45, [2] * 5, [(0, 1), (0, 2), (0, 3),
|
||||
(0, 4), (0, 5)]
|
||||
xopt = [1, 2, 3, 4, 5]
|
||||
|
||||
x, nf, rc = optimize.fmin_tnc(fg, x, bounds=bounds,
|
||||
messages=optimize._tnc.MSG_NONE,
|
||||
maxfun=200)
|
||||
|
||||
assert_allclose(self.f45(x), self.f45(xopt), atol=1e-8,
|
||||
err_msg="TNC failed with status: " +
|
||||
optimize._tnc.RCSTRINGS[rc])
|
||||
|
||||
def test_raising_exceptions(self):
|
||||
# tnc was ported to cython from hand-crafted cpython code
|
||||
# check that Exception handling works.
|
||||
def myfunc(x):
|
||||
raise RuntimeError("myfunc")
|
||||
|
||||
def myfunc1(x):
|
||||
return optimize.rosen(x)
|
||||
|
||||
def callback(x):
|
||||
raise ValueError("callback")
|
||||
|
||||
with pytest.raises(RuntimeError):
|
||||
optimize.minimize(myfunc, [0, 1], method="TNC")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
optimize.minimize(
|
||||
myfunc1, [0, 1], method="TNC", callback=callback
|
||||
)
|
||||
|
||||
def test_callback_shouldnt_affect_minimization(self):
|
||||
# gh14879. The output of a TNC minimization was different depending
|
||||
# on whether a callback was used or not. The two should be equivalent.
|
||||
# The issue was that TNC was unscaling/scaling x, and this process was
|
||||
# altering x in the process. Now the callback uses an unscaled
|
||||
# temporary copy of x.
|
||||
def callback(x):
|
||||
pass
|
||||
|
||||
fun = optimize.rosen
|
||||
bounds = [(0, 10)] * 4
|
||||
x0 = [1, 2, 3, 4.]
|
||||
res = optimize.minimize(
|
||||
fun, x0, bounds=bounds, method="TNC", options={"maxfun": 1000}
|
||||
)
|
||||
res2 = optimize.minimize(
|
||||
fun, x0, bounds=bounds, method="TNC", options={"maxfun": 1000},
|
||||
callback=callback
|
||||
)
|
||||
assert_allclose(res2.x, res.x)
|
||||
assert_allclose(res2.fun, res.fun)
|
||||
assert_equal(res2.nfev, res.nfev)
|
||||
|
||||
def test_maxiter_depreciations(self):
|
||||
msg = "'maxiter' has been deprecated in favor of 'maxfun'"
|
||||
with pytest.warns(DeprecationWarning, match=msg):
|
||||
optimize.minimize(
|
||||
self.f1, [1, 3], method="TNC", options={"maxiter": 1}
|
||||
)
|
||||
112
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_trustregion.py
vendored
Normal file
112
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_trustregion.py
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
"""
|
||||
Unit tests for trust-region optimization routines.
|
||||
|
||||
To run it in its simplest form::
|
||||
nosetests test_optimize.py
|
||||
|
||||
"""
|
||||
import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import assert_, assert_equal, assert_allclose
|
||||
from scipy.optimize import (minimize, rosen, rosen_der, rosen_hess,
|
||||
rosen_hess_prod)
|
||||
|
||||
|
||||
class Accumulator:
|
||||
""" This is for testing callbacks."""
|
||||
def __init__(self):
|
||||
self.count = 0
|
||||
self.accum = None
|
||||
|
||||
def __call__(self, x):
|
||||
self.count += 1
|
||||
if self.accum is None:
|
||||
self.accum = np.array(x)
|
||||
else:
|
||||
self.accum += x
|
||||
|
||||
|
||||
class TestTrustRegionSolvers:
|
||||
|
||||
def setup_method(self):
|
||||
self.x_opt = [1.0, 1.0]
|
||||
self.easy_guess = [2.0, 2.0]
|
||||
self.hard_guess = [-1.2, 1.0]
|
||||
|
||||
def test_dogleg_accuracy(self):
|
||||
# test the accuracy and the return_all option
|
||||
x0 = self.hard_guess
|
||||
r = minimize(rosen, x0, jac=rosen_der, hess=rosen_hess, tol=1e-8,
|
||||
method='dogleg', options={'return_all': True},)
|
||||
assert_allclose(x0, r['allvecs'][0])
|
||||
assert_allclose(r['x'], r['allvecs'][-1])
|
||||
assert_allclose(r['x'], self.x_opt)
|
||||
|
||||
def test_dogleg_callback(self):
|
||||
# test the callback mechanism and the maxiter and return_all options
|
||||
accumulator = Accumulator()
|
||||
maxiter = 5
|
||||
r = minimize(rosen, self.hard_guess, jac=rosen_der, hess=rosen_hess,
|
||||
callback=accumulator, method='dogleg',
|
||||
options={'return_all': True, 'maxiter': maxiter},)
|
||||
assert_equal(accumulator.count, maxiter)
|
||||
assert_equal(len(r['allvecs']), maxiter+1)
|
||||
assert_allclose(r['x'], r['allvecs'][-1])
|
||||
assert_allclose(sum(r['allvecs'][1:]), accumulator.accum)
|
||||
|
||||
def test_dogleg_user_warning(self):
|
||||
with pytest.warns(RuntimeWarning,
|
||||
match=r'Maximum number of iterations'):
|
||||
minimize(rosen, self.hard_guess, jac=rosen_der,
|
||||
hess=rosen_hess, method='dogleg',
|
||||
options={'disp': True, 'maxiter': 1}, )
|
||||
|
||||
def test_solver_concordance(self):
|
||||
# Assert that dogleg uses fewer iterations than ncg on the Rosenbrock
|
||||
# test function, although this does not necessarily mean
|
||||
# that dogleg is faster or better than ncg even for this function
|
||||
# and especially not for other test functions.
|
||||
f = rosen
|
||||
g = rosen_der
|
||||
h = rosen_hess
|
||||
for x0 in (self.easy_guess, self.hard_guess):
|
||||
r_dogleg = minimize(f, x0, jac=g, hess=h, tol=1e-8,
|
||||
method='dogleg', options={'return_all': True})
|
||||
r_trust_ncg = minimize(f, x0, jac=g, hess=h, tol=1e-8,
|
||||
method='trust-ncg',
|
||||
options={'return_all': True})
|
||||
r_trust_krylov = minimize(f, x0, jac=g, hess=h, tol=1e-8,
|
||||
method='trust-krylov',
|
||||
options={'return_all': True})
|
||||
r_ncg = minimize(f, x0, jac=g, hess=h, tol=1e-8,
|
||||
method='newton-cg', options={'return_all': True})
|
||||
r_iterative = minimize(f, x0, jac=g, hess=h, tol=1e-8,
|
||||
method='trust-exact',
|
||||
options={'return_all': True})
|
||||
assert_allclose(self.x_opt, r_dogleg['x'])
|
||||
assert_allclose(self.x_opt, r_trust_ncg['x'])
|
||||
assert_allclose(self.x_opt, r_trust_krylov['x'])
|
||||
assert_allclose(self.x_opt, r_ncg['x'])
|
||||
assert_allclose(self.x_opt, r_iterative['x'])
|
||||
assert_(len(r_dogleg['allvecs']) < len(r_ncg['allvecs']))
|
||||
|
||||
def test_trust_ncg_hessp(self):
|
||||
for x0 in (self.easy_guess, self.hard_guess, self.x_opt):
|
||||
r = minimize(rosen, x0, jac=rosen_der, hessp=rosen_hess_prod,
|
||||
tol=1e-8, method='trust-ncg')
|
||||
assert_allclose(self.x_opt, r['x'])
|
||||
|
||||
def test_trust_ncg_start_in_optimum(self):
|
||||
r = minimize(rosen, x0=self.x_opt, jac=rosen_der, hess=rosen_hess,
|
||||
tol=1e-8, method='trust-ncg')
|
||||
assert_allclose(self.x_opt, r['x'])
|
||||
|
||||
def test_trust_krylov_start_in_optimum(self):
|
||||
r = minimize(rosen, x0=self.x_opt, jac=rosen_der, hess=rosen_hess,
|
||||
tol=1e-8, method='trust-krylov')
|
||||
assert_allclose(self.x_opt, r['x'])
|
||||
|
||||
def test_trust_exact_start_in_optimum(self):
|
||||
r = minimize(rosen, x0=self.x_opt, jac=rosen_der, hess=rosen_hess,
|
||||
tol=1e-8, method='trust-exact')
|
||||
assert_allclose(self.x_opt, r['x'])
|
||||
352
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_trustregion_exact.py
vendored
Normal file
352
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_trustregion_exact.py
vendored
Normal file
@@ -0,0 +1,352 @@
|
||||
"""
|
||||
Unit tests for trust-region iterative subproblem.
|
||||
|
||||
To run it in its simplest form::
|
||||
nosetests test_optimize.py
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
from scipy.optimize._trustregion_exact import (
|
||||
estimate_smallest_singular_value,
|
||||
singular_leading_submatrix,
|
||||
IterativeSubproblem)
|
||||
from scipy.linalg import (svd, get_lapack_funcs, det, qr, norm)
|
||||
from numpy.testing import (assert_array_equal,
|
||||
assert_equal, assert_array_almost_equal)
|
||||
|
||||
|
||||
def random_entry(n, min_eig, max_eig, case):
|
||||
|
||||
# Generate random matrix
|
||||
rand = np.random.uniform(-1, 1, (n, n))
|
||||
|
||||
# QR decomposition
|
||||
Q, _, _ = qr(rand, pivoting='True')
|
||||
|
||||
# Generate random eigenvalues
|
||||
eigvalues = np.random.uniform(min_eig, max_eig, n)
|
||||
eigvalues = np.sort(eigvalues)[::-1]
|
||||
|
||||
# Generate matrix
|
||||
Qaux = np.multiply(eigvalues, Q)
|
||||
A = np.dot(Qaux, Q.T)
|
||||
|
||||
# Generate gradient vector accordingly
|
||||
# to the case is being tested.
|
||||
if case == 'hard':
|
||||
g = np.zeros(n)
|
||||
g[:-1] = np.random.uniform(-1, 1, n-1)
|
||||
g = np.dot(Q, g)
|
||||
elif case == 'jac_equal_zero':
|
||||
g = np.zeros(n)
|
||||
else:
|
||||
g = np.random.uniform(-1, 1, n)
|
||||
|
||||
return A, g
|
||||
|
||||
|
||||
class TestEstimateSmallestSingularValue:
|
||||
|
||||
def test_for_ill_condiotioned_matrix(self):
|
||||
|
||||
# Ill-conditioned triangular matrix
|
||||
C = np.array([[1, 2, 3, 4],
|
||||
[0, 0.05, 60, 7],
|
||||
[0, 0, 0.8, 9],
|
||||
[0, 0, 0, 10]])
|
||||
|
||||
# Get svd decomposition
|
||||
U, s, Vt = svd(C)
|
||||
|
||||
# Get smallest singular value and correspondent right singular vector.
|
||||
smin_svd = s[-1]
|
||||
zmin_svd = Vt[-1, :]
|
||||
|
||||
# Estimate smallest singular value
|
||||
smin, zmin = estimate_smallest_singular_value(C)
|
||||
|
||||
# Check the estimation
|
||||
assert_array_almost_equal(smin, smin_svd, decimal=8)
|
||||
assert_array_almost_equal(abs(zmin), abs(zmin_svd), decimal=8)
|
||||
|
||||
|
||||
class TestSingularLeadingSubmatrix:
|
||||
|
||||
def test_for_already_singular_leading_submatrix(self):
|
||||
|
||||
# Define test matrix A.
|
||||
# Note that the leading 2x2 submatrix is singular.
|
||||
A = np.array([[1, 2, 3],
|
||||
[2, 4, 5],
|
||||
[3, 5, 6]])
|
||||
|
||||
# Get Cholesky from lapack functions
|
||||
cholesky, = get_lapack_funcs(('potrf',), (A,))
|
||||
|
||||
# Compute Cholesky Decomposition
|
||||
c, k = cholesky(A, lower=False, overwrite_a=False, clean=True)
|
||||
|
||||
delta, v = singular_leading_submatrix(A, c, k)
|
||||
|
||||
A[k-1, k-1] += delta
|
||||
|
||||
# Check if the leading submatrix is singular.
|
||||
assert_array_almost_equal(det(A[:k, :k]), 0)
|
||||
|
||||
# Check if `v` fullfil the specified properties
|
||||
quadratic_term = np.dot(v, np.dot(A, v))
|
||||
assert_array_almost_equal(quadratic_term, 0)
|
||||
|
||||
def test_for_simetric_indefinite_matrix(self):
|
||||
|
||||
# Define test matrix A.
|
||||
# Note that the leading 5x5 submatrix is indefinite.
|
||||
A = np.asarray([[1, 2, 3, 7, 8],
|
||||
[2, 5, 5, 9, 0],
|
||||
[3, 5, 11, 1, 2],
|
||||
[7, 9, 1, 7, 5],
|
||||
[8, 0, 2, 5, 8]])
|
||||
|
||||
# Get Cholesky from lapack functions
|
||||
cholesky, = get_lapack_funcs(('potrf',), (A,))
|
||||
|
||||
# Compute Cholesky Decomposition
|
||||
c, k = cholesky(A, lower=False, overwrite_a=False, clean=True)
|
||||
|
||||
delta, v = singular_leading_submatrix(A, c, k)
|
||||
|
||||
A[k-1, k-1] += delta
|
||||
|
||||
# Check if the leading submatrix is singular.
|
||||
assert_array_almost_equal(det(A[:k, :k]), 0)
|
||||
|
||||
# Check if `v` fullfil the specified properties
|
||||
quadratic_term = np.dot(v, np.dot(A, v))
|
||||
assert_array_almost_equal(quadratic_term, 0)
|
||||
|
||||
def test_for_first_element_equal_to_zero(self):
|
||||
|
||||
# Define test matrix A.
|
||||
# Note that the leading 2x2 submatrix is singular.
|
||||
A = np.array([[0, 3, 11],
|
||||
[3, 12, 5],
|
||||
[11, 5, 6]])
|
||||
|
||||
# Get Cholesky from lapack functions
|
||||
cholesky, = get_lapack_funcs(('potrf',), (A,))
|
||||
|
||||
# Compute Cholesky Decomposition
|
||||
c, k = cholesky(A, lower=False, overwrite_a=False, clean=True)
|
||||
|
||||
delta, v = singular_leading_submatrix(A, c, k)
|
||||
|
||||
A[k-1, k-1] += delta
|
||||
|
||||
# Check if the leading submatrix is singular
|
||||
assert_array_almost_equal(det(A[:k, :k]), 0)
|
||||
|
||||
# Check if `v` fullfil the specified properties
|
||||
quadratic_term = np.dot(v, np.dot(A, v))
|
||||
assert_array_almost_equal(quadratic_term, 0)
|
||||
|
||||
|
||||
class TestIterativeSubproblem:
|
||||
|
||||
def test_for_the_easy_case(self):
|
||||
|
||||
# `H` is chosen such that `g` is not orthogonal to the
|
||||
# eigenvector associated with the smallest eigenvalue `s`.
|
||||
H = [[10, 2, 3, 4],
|
||||
[2, 1, 7, 1],
|
||||
[3, 7, 1, 7],
|
||||
[4, 1, 7, 2]]
|
||||
g = [1, 1, 1, 1]
|
||||
|
||||
# Trust Radius
|
||||
trust_radius = 1
|
||||
|
||||
# Solve Subproblem
|
||||
subprob = IterativeSubproblem(x=0,
|
||||
fun=lambda x: 0,
|
||||
jac=lambda x: np.array(g),
|
||||
hess=lambda x: np.array(H),
|
||||
k_easy=1e-10,
|
||||
k_hard=1e-10)
|
||||
p, hits_boundary = subprob.solve(trust_radius)
|
||||
|
||||
assert_array_almost_equal(p, [0.00393332, -0.55260862,
|
||||
0.67065477, -0.49480341])
|
||||
assert_array_almost_equal(hits_boundary, True)
|
||||
|
||||
def test_for_the_hard_case(self):
|
||||
|
||||
# `H` is chosen such that `g` is orthogonal to the
|
||||
# eigenvector associated with the smallest eigenvalue `s`.
|
||||
H = [[10, 2, 3, 4],
|
||||
[2, 1, 7, 1],
|
||||
[3, 7, 1, 7],
|
||||
[4, 1, 7, 2]]
|
||||
g = [6.4852641521327437, 1, 1, 1]
|
||||
s = -8.2151519874416614
|
||||
|
||||
# Trust Radius
|
||||
trust_radius = 1
|
||||
|
||||
# Solve Subproblem
|
||||
subprob = IterativeSubproblem(x=0,
|
||||
fun=lambda x: 0,
|
||||
jac=lambda x: np.array(g),
|
||||
hess=lambda x: np.array(H),
|
||||
k_easy=1e-10,
|
||||
k_hard=1e-10)
|
||||
p, hits_boundary = subprob.solve(trust_radius)
|
||||
|
||||
assert_array_almost_equal(-s, subprob.lambda_current)
|
||||
|
||||
def test_for_interior_convergence(self):
|
||||
|
||||
H = [[1.812159, 0.82687265, 0.21838879, -0.52487006, 0.25436988],
|
||||
[0.82687265, 2.66380283, 0.31508988, -0.40144163, 0.08811588],
|
||||
[0.21838879, 0.31508988, 2.38020726, -0.3166346, 0.27363867],
|
||||
[-0.52487006, -0.40144163, -0.3166346, 1.61927182, -0.42140166],
|
||||
[0.25436988, 0.08811588, 0.27363867, -0.42140166, 1.33243101]]
|
||||
|
||||
g = [0.75798952, 0.01421945, 0.33847612, 0.83725004, -0.47909534]
|
||||
|
||||
# Solve Subproblem
|
||||
subprob = IterativeSubproblem(x=0,
|
||||
fun=lambda x: 0,
|
||||
jac=lambda x: np.array(g),
|
||||
hess=lambda x: np.array(H))
|
||||
p, hits_boundary = subprob.solve(1.1)
|
||||
|
||||
assert_array_almost_equal(p, [-0.68585435, 0.1222621, -0.22090999,
|
||||
-0.67005053, 0.31586769])
|
||||
assert_array_almost_equal(hits_boundary, False)
|
||||
assert_array_almost_equal(subprob.lambda_current, 0)
|
||||
assert_array_almost_equal(subprob.niter, 1)
|
||||
|
||||
def test_for_jac_equal_zero(self):
|
||||
|
||||
H = [[0.88547534, 2.90692271, 0.98440885, -0.78911503, -0.28035809],
|
||||
[2.90692271, -0.04618819, 0.32867263, -0.83737945, 0.17116396],
|
||||
[0.98440885, 0.32867263, -0.87355957, -0.06521957, -1.43030957],
|
||||
[-0.78911503, -0.83737945, -0.06521957, -1.645709, -0.33887298],
|
||||
[-0.28035809, 0.17116396, -1.43030957, -0.33887298, -1.68586978]]
|
||||
|
||||
g = [0, 0, 0, 0, 0]
|
||||
|
||||
# Solve Subproblem
|
||||
subprob = IterativeSubproblem(x=0,
|
||||
fun=lambda x: 0,
|
||||
jac=lambda x: np.array(g),
|
||||
hess=lambda x: np.array(H),
|
||||
k_easy=1e-10,
|
||||
k_hard=1e-10)
|
||||
p, hits_boundary = subprob.solve(1.1)
|
||||
|
||||
assert_array_almost_equal(p, [0.06910534, -0.01432721,
|
||||
-0.65311947, -0.23815972,
|
||||
-0.84954934])
|
||||
assert_array_almost_equal(hits_boundary, True)
|
||||
|
||||
def test_for_jac_very_close_to_zero(self):
|
||||
|
||||
H = [[0.88547534, 2.90692271, 0.98440885, -0.78911503, -0.28035809],
|
||||
[2.90692271, -0.04618819, 0.32867263, -0.83737945, 0.17116396],
|
||||
[0.98440885, 0.32867263, -0.87355957, -0.06521957, -1.43030957],
|
||||
[-0.78911503, -0.83737945, -0.06521957, -1.645709, -0.33887298],
|
||||
[-0.28035809, 0.17116396, -1.43030957, -0.33887298, -1.68586978]]
|
||||
|
||||
g = [0, 0, 0, 0, 1e-15]
|
||||
|
||||
# Solve Subproblem
|
||||
subprob = IterativeSubproblem(x=0,
|
||||
fun=lambda x: 0,
|
||||
jac=lambda x: np.array(g),
|
||||
hess=lambda x: np.array(H),
|
||||
k_easy=1e-10,
|
||||
k_hard=1e-10)
|
||||
p, hits_boundary = subprob.solve(1.1)
|
||||
|
||||
assert_array_almost_equal(p, [0.06910534, -0.01432721,
|
||||
-0.65311947, -0.23815972,
|
||||
-0.84954934])
|
||||
assert_array_almost_equal(hits_boundary, True)
|
||||
|
||||
def test_for_random_entries(self):
|
||||
# Seed
|
||||
np.random.seed(1)
|
||||
|
||||
# Dimension
|
||||
n = 5
|
||||
|
||||
for case in ('easy', 'hard', 'jac_equal_zero'):
|
||||
|
||||
eig_limits = [(-20, -15),
|
||||
(-10, -5),
|
||||
(-10, 0),
|
||||
(-5, 5),
|
||||
(-10, 10),
|
||||
(0, 10),
|
||||
(5, 10),
|
||||
(15, 20)]
|
||||
|
||||
for min_eig, max_eig in eig_limits:
|
||||
# Generate random symmetric matrix H with
|
||||
# eigenvalues between min_eig and max_eig.
|
||||
H, g = random_entry(n, min_eig, max_eig, case)
|
||||
|
||||
# Trust radius
|
||||
trust_radius_list = [0.1, 0.3, 0.6, 0.8, 1, 1.2, 3.3, 5.5, 10]
|
||||
|
||||
for trust_radius in trust_radius_list:
|
||||
# Solve subproblem with very high accuracy
|
||||
subprob_ac = IterativeSubproblem(0,
|
||||
lambda x: 0,
|
||||
lambda x: g,
|
||||
lambda x: H,
|
||||
k_easy=1e-10,
|
||||
k_hard=1e-10)
|
||||
|
||||
p_ac, hits_boundary_ac = subprob_ac.solve(trust_radius)
|
||||
|
||||
# Compute objective function value
|
||||
J_ac = 1/2*np.dot(p_ac, np.dot(H, p_ac))+np.dot(g, p_ac)
|
||||
|
||||
stop_criteria = [(0.1, 2),
|
||||
(0.5, 1.1),
|
||||
(0.9, 1.01)]
|
||||
|
||||
for k_opt, k_trf in stop_criteria:
|
||||
|
||||
# k_easy and k_hard computed in function
|
||||
# of k_opt and k_trf accordingly to
|
||||
# Conn, A. R., Gould, N. I., & Toint, P. L. (2000).
|
||||
# "Trust region methods". Siam. p. 197.
|
||||
k_easy = min(k_trf-1,
|
||||
1-np.sqrt(k_opt))
|
||||
k_hard = 1-k_opt
|
||||
|
||||
# Solve subproblem
|
||||
subprob = IterativeSubproblem(0,
|
||||
lambda x: 0,
|
||||
lambda x: g,
|
||||
lambda x: H,
|
||||
k_easy=k_easy,
|
||||
k_hard=k_hard)
|
||||
p, hits_boundary = subprob.solve(trust_radius)
|
||||
|
||||
# Compute objective function value
|
||||
J = 1/2*np.dot(p, np.dot(H, p))+np.dot(g, p)
|
||||
|
||||
# Check if it respect k_trf
|
||||
if hits_boundary:
|
||||
assert_array_equal(np.abs(norm(p)-trust_radius) <=
|
||||
(k_trf-1)*trust_radius, True)
|
||||
else:
|
||||
assert_equal(norm(p) <= trust_radius, True)
|
||||
|
||||
# Check if it respect k_opt
|
||||
assert_equal(J <= k_opt*J_ac, True)
|
||||
|
||||
170
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_trustregion_krylov.py
vendored
Normal file
170
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_trustregion_krylov.py
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
"""
|
||||
Unit tests for Krylov space trust-region subproblem solver.
|
||||
|
||||
To run it in its simplest form::
|
||||
nosetests test_optimize.py
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
from scipy.optimize._trlib import (get_trlib_quadratic_subproblem)
|
||||
from numpy.testing import (assert_,
|
||||
assert_almost_equal,
|
||||
assert_equal, assert_array_almost_equal)
|
||||
|
||||
KrylovQP = get_trlib_quadratic_subproblem(tol_rel_i=1e-8, tol_rel_b=1e-6)
|
||||
KrylovQP_disp = get_trlib_quadratic_subproblem(tol_rel_i=1e-8, tol_rel_b=1e-6, disp=True)
|
||||
|
||||
class TestKrylovQuadraticSubproblem:
|
||||
|
||||
def test_for_the_easy_case(self):
|
||||
|
||||
# `H` is chosen such that `g` is not orthogonal to the
|
||||
# eigenvector associated with the smallest eigenvalue.
|
||||
H = np.array([[1.0, 0.0, 4.0],
|
||||
[0.0, 2.0, 0.0],
|
||||
[4.0, 0.0, 3.0]])
|
||||
g = np.array([5.0, 0.0, 4.0])
|
||||
|
||||
# Trust Radius
|
||||
trust_radius = 1.0
|
||||
|
||||
# Solve Subproblem
|
||||
subprob = KrylovQP(x=0,
|
||||
fun=lambda x: 0,
|
||||
jac=lambda x: g,
|
||||
hess=lambda x: None,
|
||||
hessp=lambda x, y: H.dot(y))
|
||||
p, hits_boundary = subprob.solve(trust_radius)
|
||||
|
||||
assert_array_almost_equal(p, np.array([-1.0, 0.0, 0.0]))
|
||||
assert_equal(hits_boundary, True)
|
||||
# check kkt satisfaction
|
||||
assert_almost_equal(
|
||||
np.linalg.norm(H.dot(p) + subprob.lam * p + g),
|
||||
0.0)
|
||||
# check trust region constraint
|
||||
assert_almost_equal(np.linalg.norm(p), trust_radius)
|
||||
|
||||
trust_radius = 0.5
|
||||
p, hits_boundary = subprob.solve(trust_radius)
|
||||
|
||||
assert_array_almost_equal(p,
|
||||
np.array([-0.46125446, 0., -0.19298788]))
|
||||
assert_equal(hits_boundary, True)
|
||||
# check kkt satisfaction
|
||||
assert_almost_equal(
|
||||
np.linalg.norm(H.dot(p) + subprob.lam * p + g),
|
||||
0.0)
|
||||
# check trust region constraint
|
||||
assert_almost_equal(np.linalg.norm(p), trust_radius)
|
||||
|
||||
def test_for_the_hard_case(self):
|
||||
|
||||
# `H` is chosen such that `g` is orthogonal to the
|
||||
# eigenvector associated with the smallest eigenvalue.
|
||||
H = np.array([[1.0, 0.0, 4.0],
|
||||
[0.0, 2.0, 0.0],
|
||||
[4.0, 0.0, 3.0]])
|
||||
g = np.array([0.0, 2.0, 0.0])
|
||||
|
||||
# Trust Radius
|
||||
trust_radius = 1.0
|
||||
|
||||
# Solve Subproblem
|
||||
subprob = KrylovQP(x=0,
|
||||
fun=lambda x: 0,
|
||||
jac=lambda x: g,
|
||||
hess=lambda x: None,
|
||||
hessp=lambda x, y: H.dot(y))
|
||||
p, hits_boundary = subprob.solve(trust_radius)
|
||||
|
||||
assert_array_almost_equal(p, np.array([0.0, -1.0, 0.0]))
|
||||
# check kkt satisfaction
|
||||
assert_almost_equal(
|
||||
np.linalg.norm(H.dot(p) + subprob.lam * p + g),
|
||||
0.0)
|
||||
# check trust region constraint
|
||||
assert_almost_equal(np.linalg.norm(p), trust_radius)
|
||||
|
||||
trust_radius = 0.5
|
||||
p, hits_boundary = subprob.solve(trust_radius)
|
||||
|
||||
assert_array_almost_equal(p, np.array([0.0, -0.5, 0.0]))
|
||||
# check kkt satisfaction
|
||||
assert_almost_equal(
|
||||
np.linalg.norm(H.dot(p) + subprob.lam * p + g),
|
||||
0.0)
|
||||
# check trust region constraint
|
||||
assert_almost_equal(np.linalg.norm(p), trust_radius)
|
||||
|
||||
def test_for_interior_convergence(self):
|
||||
|
||||
H = np.array([[1.812159, 0.82687265, 0.21838879, -0.52487006, 0.25436988],
|
||||
[0.82687265, 2.66380283, 0.31508988, -0.40144163, 0.08811588],
|
||||
[0.21838879, 0.31508988, 2.38020726, -0.3166346, 0.27363867],
|
||||
[-0.52487006, -0.40144163, -0.3166346, 1.61927182, -0.42140166],
|
||||
[0.25436988, 0.08811588, 0.27363867, -0.42140166, 1.33243101]])
|
||||
g = np.array([0.75798952, 0.01421945, 0.33847612, 0.83725004, -0.47909534])
|
||||
trust_radius = 1.1
|
||||
|
||||
# Solve Subproblem
|
||||
subprob = KrylovQP(x=0,
|
||||
fun=lambda x: 0,
|
||||
jac=lambda x: g,
|
||||
hess=lambda x: None,
|
||||
hessp=lambda x, y: H.dot(y))
|
||||
p, hits_boundary = subprob.solve(trust_radius)
|
||||
|
||||
# check kkt satisfaction
|
||||
assert_almost_equal(
|
||||
np.linalg.norm(H.dot(p) + subprob.lam * p + g),
|
||||
0.0)
|
||||
|
||||
assert_array_almost_equal(p, [-0.68585435, 0.1222621, -0.22090999,
|
||||
-0.67005053, 0.31586769])
|
||||
assert_array_almost_equal(hits_boundary, False)
|
||||
|
||||
def test_for_very_close_to_zero(self):
|
||||
|
||||
H = np.array([[0.88547534, 2.90692271, 0.98440885, -0.78911503, -0.28035809],
|
||||
[2.90692271, -0.04618819, 0.32867263, -0.83737945, 0.17116396],
|
||||
[0.98440885, 0.32867263, -0.87355957, -0.06521957, -1.43030957],
|
||||
[-0.78911503, -0.83737945, -0.06521957, -1.645709, -0.33887298],
|
||||
[-0.28035809, 0.17116396, -1.43030957, -0.33887298, -1.68586978]])
|
||||
g = np.array([0, 0, 0, 0, 1e-6])
|
||||
trust_radius = 1.1
|
||||
|
||||
# Solve Subproblem
|
||||
subprob = KrylovQP(x=0,
|
||||
fun=lambda x: 0,
|
||||
jac=lambda x: g,
|
||||
hess=lambda x: None,
|
||||
hessp=lambda x, y: H.dot(y))
|
||||
p, hits_boundary = subprob.solve(trust_radius)
|
||||
|
||||
# check kkt satisfaction
|
||||
assert_almost_equal(
|
||||
np.linalg.norm(H.dot(p) + subprob.lam * p + g),
|
||||
0.0)
|
||||
# check trust region constraint
|
||||
assert_almost_equal(np.linalg.norm(p), trust_radius)
|
||||
|
||||
assert_array_almost_equal(p, [0.06910534, -0.01432721,
|
||||
-0.65311947, -0.23815972,
|
||||
-0.84954934])
|
||||
assert_array_almost_equal(hits_boundary, True)
|
||||
|
||||
def test_disp(self, capsys):
|
||||
H = -np.eye(5)
|
||||
g = np.array([0, 0, 0, 0, 1e-6])
|
||||
trust_radius = 1.1
|
||||
|
||||
subprob = KrylovQP_disp(x=0,
|
||||
fun=lambda x: 0,
|
||||
jac=lambda x: g,
|
||||
hess=lambda x: None,
|
||||
hessp=lambda x, y: H.dot(y))
|
||||
p, hits_boundary = subprob.solve(trust_radius)
|
||||
out, err = capsys.readouterr()
|
||||
assert_(out.startswith(' TR Solving trust region problem'), repr(out))
|
||||
|
||||
770
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_zeros.py
vendored
Normal file
770
.CondaPkg/env/Lib/site-packages/scipy/optimize/tests/test_zeros.py
vendored
Normal file
@@ -0,0 +1,770 @@
|
||||
import pytest
|
||||
|
||||
from math import sqrt, exp, sin, cos
|
||||
from functools import lru_cache
|
||||
|
||||
from numpy.testing import (assert_warns, assert_,
|
||||
assert_allclose,
|
||||
assert_equal,
|
||||
assert_array_equal,
|
||||
suppress_warnings)
|
||||
import numpy as np
|
||||
from numpy import finfo, power, nan, isclose
|
||||
|
||||
|
||||
from scipy.optimize import _zeros_py as zeros, newton, root_scalar
|
||||
|
||||
from scipy._lib._util import getfullargspec_no_self as _getfullargspec
|
||||
|
||||
# Import testing parameters
|
||||
from scipy.optimize._tstutils import get_tests, functions as tstutils_functions, fstrings as tstutils_fstrings
|
||||
|
||||
TOL = 4*np.finfo(float).eps # tolerance
|
||||
|
||||
_FLOAT_EPS = finfo(float).eps
|
||||
|
||||
# A few test functions used frequently:
|
||||
# # A simple quadratic, (x-1)^2 - 1
|
||||
def f1(x):
|
||||
return x ** 2 - 2 * x - 1
|
||||
|
||||
|
||||
def f1_1(x):
|
||||
return 2 * x - 2
|
||||
|
||||
|
||||
def f1_2(x):
|
||||
return 2.0 + 0 * x
|
||||
|
||||
|
||||
def f1_and_p_and_pp(x):
|
||||
return f1(x), f1_1(x), f1_2(x)
|
||||
|
||||
|
||||
# Simple transcendental function
|
||||
def f2(x):
|
||||
return exp(x) - cos(x)
|
||||
|
||||
|
||||
def f2_1(x):
|
||||
return exp(x) + sin(x)
|
||||
|
||||
|
||||
def f2_2(x):
|
||||
return exp(x) + cos(x)
|
||||
|
||||
|
||||
# lru cached function
|
||||
@lru_cache()
|
||||
def f_lrucached(x):
|
||||
return x
|
||||
|
||||
|
||||
class TestBasic:
|
||||
|
||||
def run_check_by_name(self, name, smoothness=0, **kwargs):
|
||||
a = .5
|
||||
b = sqrt(3)
|
||||
xtol = 4*np.finfo(float).eps
|
||||
rtol = 4*np.finfo(float).eps
|
||||
for function, fname in zip(tstutils_functions, tstutils_fstrings):
|
||||
if smoothness > 0 and fname in ['f4', 'f5', 'f6']:
|
||||
continue
|
||||
r = root_scalar(function, method=name, bracket=[a, b], x0=a,
|
||||
xtol=xtol, rtol=rtol, **kwargs)
|
||||
zero = r.root
|
||||
assert_(r.converged)
|
||||
assert_allclose(zero, 1.0, atol=xtol, rtol=rtol,
|
||||
err_msg='method %s, function %s' % (name, fname))
|
||||
|
||||
def run_check(self, method, name):
|
||||
a = .5
|
||||
b = sqrt(3)
|
||||
xtol = 4 * _FLOAT_EPS
|
||||
rtol = 4 * _FLOAT_EPS
|
||||
for function, fname in zip(tstutils_functions, tstutils_fstrings):
|
||||
zero, r = method(function, a, b, xtol=xtol, rtol=rtol,
|
||||
full_output=True)
|
||||
assert_(r.converged)
|
||||
assert_allclose(zero, 1.0, atol=xtol, rtol=rtol,
|
||||
err_msg='method %s, function %s' % (name, fname))
|
||||
|
||||
def run_check_lru_cached(self, method, name):
|
||||
# check that https://github.com/scipy/scipy/issues/10846 is fixed
|
||||
a = -1
|
||||
b = 1
|
||||
zero, r = method(f_lrucached, a, b, full_output=True)
|
||||
assert_(r.converged)
|
||||
assert_allclose(zero, 0,
|
||||
err_msg='method %s, function %s' % (name, 'f_lrucached'))
|
||||
|
||||
def _run_one_test(self, tc, method, sig_args_keys=None,
|
||||
sig_kwargs_keys=None, **kwargs):
|
||||
method_args = []
|
||||
for k in sig_args_keys or []:
|
||||
if k not in tc:
|
||||
# If a,b not present use x0, x1. Similarly for f and func
|
||||
k = {'a': 'x0', 'b': 'x1', 'func': 'f'}.get(k, k)
|
||||
method_args.append(tc[k])
|
||||
|
||||
method_kwargs = dict(**kwargs)
|
||||
method_kwargs.update({'full_output': True, 'disp': False})
|
||||
for k in sig_kwargs_keys or []:
|
||||
method_kwargs[k] = tc[k]
|
||||
|
||||
root = tc.get('root')
|
||||
func_args = tc.get('args', ())
|
||||
|
||||
try:
|
||||
r, rr = method(*method_args, args=func_args, **method_kwargs)
|
||||
return root, rr, tc
|
||||
except Exception:
|
||||
return root, zeros.RootResults(nan, -1, -1, zeros._EVALUEERR), tc
|
||||
|
||||
def run_tests(self, tests, method, name,
|
||||
xtol=4 * _FLOAT_EPS, rtol=4 * _FLOAT_EPS,
|
||||
known_fail=None, **kwargs):
|
||||
r"""Run test-cases using the specified method and the supplied signature.
|
||||
|
||||
Extract the arguments for the method call from the test case
|
||||
dictionary using the supplied keys for the method's signature."""
|
||||
# The methods have one of two base signatures:
|
||||
# (f, a, b, **kwargs) # newton
|
||||
# (func, x0, **kwargs) # bisect/brentq/...
|
||||
sig = _getfullargspec(method) # FullArgSpec with args, varargs, varkw, defaults, ...
|
||||
assert_(not sig.kwonlyargs)
|
||||
nDefaults = len(sig.defaults)
|
||||
nRequired = len(sig.args) - nDefaults
|
||||
sig_args_keys = sig.args[:nRequired]
|
||||
sig_kwargs_keys = []
|
||||
if name in ['secant', 'newton', 'halley']:
|
||||
if name in ['newton', 'halley']:
|
||||
sig_kwargs_keys.append('fprime')
|
||||
if name in ['halley']:
|
||||
sig_kwargs_keys.append('fprime2')
|
||||
kwargs['tol'] = xtol
|
||||
else:
|
||||
kwargs['xtol'] = xtol
|
||||
kwargs['rtol'] = rtol
|
||||
|
||||
results = [list(self._run_one_test(
|
||||
tc, method, sig_args_keys=sig_args_keys,
|
||||
sig_kwargs_keys=sig_kwargs_keys, **kwargs)) for tc in tests]
|
||||
# results= [[true root, full output, tc], ...]
|
||||
|
||||
known_fail = known_fail or []
|
||||
notcvgd = [elt for elt in results if not elt[1].converged]
|
||||
notcvgd = [elt for elt in notcvgd if elt[-1]['ID'] not in known_fail]
|
||||
notcvged_IDS = [elt[-1]['ID'] for elt in notcvgd]
|
||||
assert_equal([len(notcvged_IDS), notcvged_IDS], [0, []])
|
||||
|
||||
# The usable xtol and rtol depend on the test
|
||||
tols = {'xtol': 4 * _FLOAT_EPS, 'rtol': 4 * _FLOAT_EPS}
|
||||
tols.update(**kwargs)
|
||||
rtol = tols['rtol']
|
||||
atol = tols.get('tol', tols['xtol'])
|
||||
|
||||
cvgd = [elt for elt in results if elt[1].converged]
|
||||
approx = [elt[1].root for elt in cvgd]
|
||||
correct = [elt[0] for elt in cvgd]
|
||||
notclose = [[a] + elt for a, c, elt in zip(approx, correct, cvgd) if
|
||||
not isclose(a, c, rtol=rtol, atol=atol)
|
||||
and elt[-1]['ID'] not in known_fail]
|
||||
# Evaluate the function and see if is 0 at the purported root
|
||||
fvs = [tc['f'](aroot, *(tc['args'])) for aroot, c, fullout, tc in notclose]
|
||||
notclose = [[fv] + elt for fv, elt in zip(fvs, notclose) if fv != 0]
|
||||
assert_equal([notclose, len(notclose)], [[], 0])
|
||||
|
||||
def run_collection(self, collection, method, name, smoothness=None,
|
||||
known_fail=None,
|
||||
xtol=4 * _FLOAT_EPS, rtol=4 * _FLOAT_EPS,
|
||||
**kwargs):
|
||||
r"""Run a collection of tests using the specified method.
|
||||
|
||||
The name is used to determine some optional arguments."""
|
||||
tests = get_tests(collection, smoothness=smoothness)
|
||||
self.run_tests(tests, method, name, xtol=xtol, rtol=rtol,
|
||||
known_fail=known_fail, **kwargs)
|
||||
|
||||
def test_bisect(self):
|
||||
self.run_check(zeros.bisect, 'bisect')
|
||||
self.run_check_lru_cached(zeros.bisect, 'bisect')
|
||||
self.run_check_by_name('bisect')
|
||||
self.run_collection('aps', zeros.bisect, 'bisect', smoothness=1)
|
||||
|
||||
def test_ridder(self):
|
||||
self.run_check(zeros.ridder, 'ridder')
|
||||
self.run_check_lru_cached(zeros.ridder, 'ridder')
|
||||
self.run_check_by_name('ridder')
|
||||
self.run_collection('aps', zeros.ridder, 'ridder', smoothness=1)
|
||||
|
||||
def test_brentq(self):
|
||||
self.run_check(zeros.brentq, 'brentq')
|
||||
self.run_check_lru_cached(zeros.brentq, 'brentq')
|
||||
self.run_check_by_name('brentq')
|
||||
# Brentq/h needs a lower tolerance to be specified
|
||||
self.run_collection('aps', zeros.brentq, 'brentq', smoothness=1,
|
||||
xtol=1e-14, rtol=1e-14)
|
||||
|
||||
def test_brenth(self):
|
||||
self.run_check(zeros.brenth, 'brenth')
|
||||
self.run_check_lru_cached(zeros.brenth, 'brenth')
|
||||
self.run_check_by_name('brenth')
|
||||
self.run_collection('aps', zeros.brenth, 'brenth', smoothness=1,
|
||||
xtol=1e-14, rtol=1e-14)
|
||||
|
||||
def test_toms748(self):
|
||||
self.run_check(zeros.toms748, 'toms748')
|
||||
self.run_check_lru_cached(zeros.toms748, 'toms748')
|
||||
self.run_check_by_name('toms748')
|
||||
self.run_collection('aps', zeros.toms748, 'toms748', smoothness=1)
|
||||
|
||||
def test_newton_collections(self):
|
||||
known_fail = ['aps.13.00']
|
||||
known_fail += ['aps.12.05', 'aps.12.17'] # fails under Windows Py27
|
||||
for collection in ['aps', 'complex']:
|
||||
self.run_collection(collection, zeros.newton, 'newton',
|
||||
smoothness=2, known_fail=known_fail)
|
||||
|
||||
def test_halley_collections(self):
|
||||
known_fail = ['aps.12.06', 'aps.12.07', 'aps.12.08', 'aps.12.09',
|
||||
'aps.12.10', 'aps.12.11', 'aps.12.12', 'aps.12.13',
|
||||
'aps.12.14', 'aps.12.15', 'aps.12.16', 'aps.12.17',
|
||||
'aps.12.18', 'aps.13.00']
|
||||
for collection in ['aps', 'complex']:
|
||||
self.run_collection(collection, zeros.newton, 'halley',
|
||||
smoothness=2, known_fail=known_fail)
|
||||
|
||||
@staticmethod
|
||||
def f1(x):
|
||||
return x**2 - 2*x - 1 # == (x-1)**2 - 2
|
||||
|
||||
@staticmethod
|
||||
def f1_1(x):
|
||||
return 2*x - 2
|
||||
|
||||
@staticmethod
|
||||
def f1_2(x):
|
||||
return 2.0 + 0*x
|
||||
|
||||
@staticmethod
|
||||
def f2(x):
|
||||
return exp(x) - cos(x)
|
||||
|
||||
@staticmethod
|
||||
def f2_1(x):
|
||||
return exp(x) + sin(x)
|
||||
|
||||
@staticmethod
|
||||
def f2_2(x):
|
||||
return exp(x) + cos(x)
|
||||
|
||||
def test_newton(self):
|
||||
for f, f_1, f_2 in [(self.f1, self.f1_1, self.f1_2),
|
||||
(self.f2, self.f2_1, self.f2_2)]:
|
||||
x = zeros.newton(f, 3, tol=1e-6)
|
||||
assert_allclose(f(x), 0, atol=1e-6)
|
||||
x = zeros.newton(f, 3, x1=5, tol=1e-6) # secant, x0 and x1
|
||||
assert_allclose(f(x), 0, atol=1e-6)
|
||||
x = zeros.newton(f, 3, fprime=f_1, tol=1e-6) # newton
|
||||
assert_allclose(f(x), 0, atol=1e-6)
|
||||
x = zeros.newton(f, 3, fprime=f_1, fprime2=f_2, tol=1e-6) # halley
|
||||
assert_allclose(f(x), 0, atol=1e-6)
|
||||
|
||||
def test_newton_by_name(self):
|
||||
r"""Invoke newton through root_scalar()"""
|
||||
for f, f_1, f_2 in [(f1, f1_1, f1_2), (f2, f2_1, f2_2)]:
|
||||
r = root_scalar(f, method='newton', x0=3, fprime=f_1, xtol=1e-6)
|
||||
assert_allclose(f(r.root), 0, atol=1e-6)
|
||||
|
||||
def test_secant_by_name(self):
|
||||
r"""Invoke secant through root_scalar()"""
|
||||
for f, f_1, f_2 in [(f1, f1_1, f1_2), (f2, f2_1, f2_2)]:
|
||||
r = root_scalar(f, method='secant', x0=3, x1=2, xtol=1e-6)
|
||||
assert_allclose(f(r.root), 0, atol=1e-6)
|
||||
r = root_scalar(f, method='secant', x0=3, x1=5, xtol=1e-6)
|
||||
assert_allclose(f(r.root), 0, atol=1e-6)
|
||||
|
||||
def test_halley_by_name(self):
|
||||
r"""Invoke halley through root_scalar()"""
|
||||
for f, f_1, f_2 in [(f1, f1_1, f1_2), (f2, f2_1, f2_2)]:
|
||||
r = root_scalar(f, method='halley', x0=3,
|
||||
fprime=f_1, fprime2=f_2, xtol=1e-6)
|
||||
assert_allclose(f(r.root), 0, atol=1e-6)
|
||||
|
||||
def test_root_scalar_fail(self):
|
||||
with pytest.raises(ValueError):
|
||||
root_scalar(f1, method='secant', x0=3, xtol=1e-6) # no x1
|
||||
with pytest.raises(ValueError):
|
||||
root_scalar(f1, method='newton', x0=3, xtol=1e-6) # no fprime
|
||||
with pytest.raises(ValueError):
|
||||
root_scalar(f1, method='halley', fprime=f1_1, x0=3, xtol=1e-6) # no fprime2
|
||||
with pytest.raises(ValueError):
|
||||
root_scalar(f1, method='halley', fprime2=f1_2, x0=3, xtol=1e-6) # no fprime
|
||||
|
||||
def test_array_newton(self):
|
||||
"""test newton with array"""
|
||||
|
||||
def f1(x, *a):
|
||||
b = a[0] + x * a[3]
|
||||
return a[1] - a[2] * (np.exp(b / a[5]) - 1.0) - b / a[4] - x
|
||||
|
||||
def f1_1(x, *a):
|
||||
b = a[3] / a[5]
|
||||
return -a[2] * np.exp(a[0] / a[5] + x * b) * b - a[3] / a[4] - 1
|
||||
|
||||
def f1_2(x, *a):
|
||||
b = a[3] / a[5]
|
||||
return -a[2] * np.exp(a[0] / a[5] + x * b) * b**2
|
||||
|
||||
a0 = np.array([
|
||||
5.32725221, 5.48673747, 5.49539973,
|
||||
5.36387202, 4.80237316, 1.43764452,
|
||||
5.23063958, 5.46094772, 5.50512718,
|
||||
5.42046290
|
||||
])
|
||||
a1 = (np.sin(range(10)) + 1.0) * 7.0
|
||||
args = (a0, a1, 1e-09, 0.004, 10, 0.27456)
|
||||
x0 = [7.0] * 10
|
||||
x = zeros.newton(f1, x0, f1_1, args)
|
||||
x_expected = (
|
||||
6.17264965, 11.7702805, 12.2219954,
|
||||
7.11017681, 1.18151293, 0.143707955,
|
||||
4.31928228, 10.5419107, 12.7552490,
|
||||
8.91225749
|
||||
)
|
||||
assert_allclose(x, x_expected)
|
||||
# test halley's
|
||||
x = zeros.newton(f1, x0, f1_1, args, fprime2=f1_2)
|
||||
assert_allclose(x, x_expected)
|
||||
# test secant
|
||||
x = zeros.newton(f1, x0, args=args)
|
||||
assert_allclose(x, x_expected)
|
||||
|
||||
def test_array_newton_complex(self):
|
||||
def f(x):
|
||||
return x + 1+1j
|
||||
|
||||
def fprime(x):
|
||||
return 1.0
|
||||
|
||||
t = np.full(4, 1j)
|
||||
x = zeros.newton(f, t, fprime=fprime)
|
||||
assert_allclose(f(x), 0.)
|
||||
|
||||
# should work even if x0 is not complex
|
||||
t = np.ones(4)
|
||||
x = zeros.newton(f, t, fprime=fprime)
|
||||
assert_allclose(f(x), 0.)
|
||||
|
||||
x = zeros.newton(f, t)
|
||||
assert_allclose(f(x), 0.)
|
||||
|
||||
def test_array_secant_active_zero_der(self):
|
||||
"""test secant doesn't continue to iterate zero derivatives"""
|
||||
x = zeros.newton(lambda x, *a: x*x - a[0], x0=[4.123, 5],
|
||||
args=[np.array([17, 25])])
|
||||
assert_allclose(x, (4.123105625617661, 5.0))
|
||||
|
||||
def test_array_newton_integers(self):
|
||||
# test secant with float
|
||||
x = zeros.newton(lambda y, z: z - y ** 2, [4.0] * 2,
|
||||
args=([15.0, 17.0],))
|
||||
assert_allclose(x, (3.872983346207417, 4.123105625617661))
|
||||
# test integer becomes float
|
||||
x = zeros.newton(lambda y, z: z - y ** 2, [4] * 2, args=([15, 17],))
|
||||
assert_allclose(x, (3.872983346207417, 4.123105625617661))
|
||||
|
||||
def test_array_newton_zero_der_failures(self):
|
||||
# test derivative zero warning
|
||||
assert_warns(RuntimeWarning, zeros.newton,
|
||||
lambda y: y**2 - 2, [0., 0.], lambda y: 2 * y)
|
||||
# test failures and zero_der
|
||||
with pytest.warns(RuntimeWarning):
|
||||
results = zeros.newton(lambda y: y**2 - 2, [0., 0.],
|
||||
lambda y: 2*y, full_output=True)
|
||||
assert_allclose(results.root, 0)
|
||||
assert results.zero_der.all()
|
||||
assert not results.converged.any()
|
||||
|
||||
def test_newton_combined(self):
|
||||
f1 = lambda x: x**2 - 2*x - 1
|
||||
f1_1 = lambda x: 2*x - 2
|
||||
f1_2 = lambda x: 2.0 + 0*x
|
||||
|
||||
def f1_and_p_and_pp(x):
|
||||
return x**2 - 2*x-1, 2*x-2, 2.0
|
||||
|
||||
sol0 = root_scalar(f1, method='newton', x0=3, fprime=f1_1)
|
||||
sol = root_scalar(f1_and_p_and_pp, method='newton', x0=3, fprime=True)
|
||||
assert_allclose(sol0.root, sol.root, atol=1e-8)
|
||||
assert_equal(2*sol.function_calls, sol0.function_calls)
|
||||
|
||||
sol0 = root_scalar(f1, method='halley', x0=3, fprime=f1_1, fprime2=f1_2)
|
||||
sol = root_scalar(f1_and_p_and_pp, method='halley', x0=3, fprime2=True)
|
||||
assert_allclose(sol0.root, sol.root, atol=1e-8)
|
||||
assert_equal(3*sol.function_calls, sol0.function_calls)
|
||||
|
||||
def test_newton_full_output(self):
|
||||
# Test the full_output capability, both when converging and not.
|
||||
# Use simple polynomials, to avoid hitting platform dependencies
|
||||
# (e.g., exp & trig) in number of iterations
|
||||
|
||||
x0 = 3
|
||||
expected_counts = [(6, 7), (5, 10), (3, 9)]
|
||||
|
||||
for derivs in range(3):
|
||||
kwargs = {'tol': 1e-6, 'full_output': True, }
|
||||
for k, v in [['fprime', self.f1_1], ['fprime2', self.f1_2]][:derivs]:
|
||||
kwargs[k] = v
|
||||
|
||||
x, r = zeros.newton(self.f1, x0, disp=False, **kwargs)
|
||||
assert_(r.converged)
|
||||
assert_equal(x, r.root)
|
||||
assert_equal((r.iterations, r.function_calls), expected_counts[derivs])
|
||||
if derivs == 0:
|
||||
assert r.function_calls <= r.iterations + 1
|
||||
else:
|
||||
assert_equal(r.function_calls, (derivs + 1) * r.iterations)
|
||||
|
||||
# Now repeat, allowing one fewer iteration to force convergence failure
|
||||
iters = r.iterations - 1
|
||||
x, r = zeros.newton(self.f1, x0, maxiter=iters, disp=False, **kwargs)
|
||||
assert_(not r.converged)
|
||||
assert_equal(x, r.root)
|
||||
assert_equal(r.iterations, iters)
|
||||
|
||||
if derivs == 1:
|
||||
# Check that the correct Exception is raised and
|
||||
# validate the start of the message.
|
||||
with pytest.raises(
|
||||
RuntimeError,
|
||||
match='Failed to converge after %d iterations, value is .*' % (iters)):
|
||||
x, r = zeros.newton(self.f1, x0, maxiter=iters, disp=True, **kwargs)
|
||||
|
||||
def test_deriv_zero_warning(self):
|
||||
func = lambda x: x**2 - 2.0
|
||||
dfunc = lambda x: 2*x
|
||||
assert_warns(RuntimeWarning, zeros.newton, func, 0.0, dfunc, disp=False)
|
||||
with pytest.raises(RuntimeError, match='Derivative was zero'):
|
||||
zeros.newton(func, 0.0, dfunc)
|
||||
|
||||
def test_newton_does_not_modify_x0(self):
|
||||
# https://github.com/scipy/scipy/issues/9964
|
||||
x0 = np.array([0.1, 3])
|
||||
x0_copy = x0.copy() # Copy to test for equality.
|
||||
newton(np.sin, x0, np.cos)
|
||||
assert_array_equal(x0, x0_copy)
|
||||
|
||||
def test_maxiter_int_check(self):
|
||||
for method in [zeros.bisect, zeros.newton, zeros.ridder, zeros.brentq,
|
||||
zeros.brenth, zeros.toms748]:
|
||||
with pytest.raises(TypeError,
|
||||
match="'float' object cannot be interpreted as an integer"):
|
||||
method(f1, 0.0, 1.0, maxiter=72.45)
|
||||
|
||||
|
||||
def test_gh_5555():
|
||||
root = 0.1
|
||||
|
||||
def f(x):
|
||||
return x - root
|
||||
|
||||
methods = [zeros.bisect, zeros.ridder]
|
||||
xtol = rtol = TOL
|
||||
for method in methods:
|
||||
res = method(f, -1e8, 1e7, xtol=xtol, rtol=rtol)
|
||||
assert_allclose(root, res, atol=xtol, rtol=rtol,
|
||||
err_msg='method %s' % method.__name__)
|
||||
|
||||
|
||||
def test_gh_5557():
|
||||
# Show that without the changes in 5557 brentq and brenth might
|
||||
# only achieve a tolerance of 2*(xtol + rtol*|res|).
|
||||
|
||||
# f linearly interpolates (0, -0.1), (0.5, -0.1), and (1,
|
||||
# 0.4). The important parts are that |f(0)| < |f(1)| (so that
|
||||
# brent takes 0 as the initial guess), |f(0)| < atol (so that
|
||||
# brent accepts 0 as the root), and that the exact root of f lies
|
||||
# more than atol away from 0 (so that brent doesn't achieve the
|
||||
# desired tolerance).
|
||||
def f(x):
|
||||
if x < 0.5:
|
||||
return -0.1
|
||||
else:
|
||||
return x - 0.6
|
||||
|
||||
atol = 0.51
|
||||
rtol = 4 * _FLOAT_EPS
|
||||
methods = [zeros.brentq, zeros.brenth]
|
||||
for method in methods:
|
||||
res = method(f, 0, 1, xtol=atol, rtol=rtol)
|
||||
assert_allclose(0.6, res, atol=atol, rtol=rtol)
|
||||
|
||||
|
||||
def test_brent_underflow_in_root_bracketing():
|
||||
# Tetsing if an interval [a,b] brackets a zero of a function
|
||||
# by checking f(a)*f(b) < 0 is not reliable when the product
|
||||
# underflows/overflows. (reported in issue# 13737)
|
||||
|
||||
underflow_scenario = (-450.0, -350.0, -400.0)
|
||||
overflow_scenario = (350.0, 450.0, 400.0)
|
||||
|
||||
for a, b, root in [underflow_scenario, overflow_scenario]:
|
||||
c = np.exp(root)
|
||||
for method in [zeros.brenth, zeros.brentq]:
|
||||
res = method(lambda x: np.exp(x)-c, a, b)
|
||||
assert_allclose(root, res)
|
||||
|
||||
|
||||
class TestRootResults:
|
||||
def test_repr(self):
|
||||
r = zeros.RootResults(root=1.0,
|
||||
iterations=44,
|
||||
function_calls=46,
|
||||
flag=0)
|
||||
expected_repr = (" converged: True\n flag: 'converged'"
|
||||
"\n function_calls: 46\n iterations: 44\n"
|
||||
" root: 1.0")
|
||||
assert_equal(repr(r), expected_repr)
|
||||
|
||||
|
||||
def test_complex_halley():
|
||||
"""Test Halley's works with complex roots"""
|
||||
def f(x, *a):
|
||||
return a[0] * x**2 + a[1] * x + a[2]
|
||||
|
||||
def f_1(x, *a):
|
||||
return 2 * a[0] * x + a[1]
|
||||
|
||||
def f_2(x, *a):
|
||||
retval = 2 * a[0]
|
||||
try:
|
||||
size = len(x)
|
||||
except TypeError:
|
||||
return retval
|
||||
else:
|
||||
return [retval] * size
|
||||
|
||||
z = complex(1.0, 2.0)
|
||||
coeffs = (2.0, 3.0, 4.0)
|
||||
y = zeros.newton(f, z, args=coeffs, fprime=f_1, fprime2=f_2, tol=1e-6)
|
||||
# (-0.75000000000000078+1.1989578808281789j)
|
||||
assert_allclose(f(y, *coeffs), 0, atol=1e-6)
|
||||
z = [z] * 10
|
||||
coeffs = (2.0, 3.0, 4.0)
|
||||
y = zeros.newton(f, z, args=coeffs, fprime=f_1, fprime2=f_2, tol=1e-6)
|
||||
assert_allclose(f(y, *coeffs), 0, atol=1e-6)
|
||||
|
||||
|
||||
def test_zero_der_nz_dp():
|
||||
"""Test secant method with a non-zero dp, but an infinite newton step"""
|
||||
# pick a symmetrical functions and choose a point on the side that with dx
|
||||
# makes a secant that is a flat line with zero slope, EG: f = (x - 100)**2,
|
||||
# which has a root at x = 100 and is symmetrical around the line x = 100
|
||||
# we have to pick a really big number so that it is consistently true
|
||||
# now find a point on each side so that the secant has a zero slope
|
||||
dx = np.finfo(float).eps ** 0.33
|
||||
# 100 - p0 = p1 - 100 = p0 * (1 + dx) + dx - 100
|
||||
# -> 200 = p0 * (2 + dx) + dx
|
||||
p0 = (200.0 - dx) / (2.0 + dx)
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(RuntimeWarning, "RMS of")
|
||||
x = zeros.newton(lambda y: (y - 100.0)**2, x0=[p0] * 10)
|
||||
assert_allclose(x, [100] * 10)
|
||||
# test scalar cases too
|
||||
p0 = (2.0 - 1e-4) / (2.0 + 1e-4)
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(RuntimeWarning, "Tolerance of")
|
||||
x = zeros.newton(lambda y: (y - 1.0) ** 2, x0=p0, disp=False)
|
||||
assert_allclose(x, 1)
|
||||
with pytest.raises(RuntimeError, match='Tolerance of'):
|
||||
x = zeros.newton(lambda y: (y - 1.0) ** 2, x0=p0, disp=True)
|
||||
p0 = (-2.0 + 1e-4) / (2.0 + 1e-4)
|
||||
with suppress_warnings() as sup:
|
||||
sup.filter(RuntimeWarning, "Tolerance of")
|
||||
x = zeros.newton(lambda y: (y + 1.0) ** 2, x0=p0, disp=False)
|
||||
assert_allclose(x, -1)
|
||||
with pytest.raises(RuntimeError, match='Tolerance of'):
|
||||
x = zeros.newton(lambda y: (y + 1.0) ** 2, x0=p0, disp=True)
|
||||
|
||||
|
||||
def test_array_newton_failures():
|
||||
"""Test that array newton fails as expected"""
|
||||
# p = 0.68 # [MPa]
|
||||
# dp = -0.068 * 1e6 # [Pa]
|
||||
# T = 323 # [K]
|
||||
diameter = 0.10 # [m]
|
||||
# L = 100 # [m]
|
||||
roughness = 0.00015 # [m]
|
||||
rho = 988.1 # [kg/m**3]
|
||||
mu = 5.4790e-04 # [Pa*s]
|
||||
u = 2.488 # [m/s]
|
||||
reynolds_number = rho * u * diameter / mu # Reynolds number
|
||||
|
||||
def colebrook_eqn(darcy_friction, re, dia):
|
||||
return (1 / np.sqrt(darcy_friction) +
|
||||
2 * np.log10(roughness / 3.7 / dia +
|
||||
2.51 / re / np.sqrt(darcy_friction)))
|
||||
|
||||
# only some failures
|
||||
with pytest.warns(RuntimeWarning):
|
||||
result = zeros.newton(
|
||||
colebrook_eqn, x0=[0.01, 0.2, 0.02223, 0.3], maxiter=2,
|
||||
args=[reynolds_number, diameter], full_output=True
|
||||
)
|
||||
assert not result.converged.all()
|
||||
# they all fail
|
||||
with pytest.raises(RuntimeError):
|
||||
result = zeros.newton(
|
||||
colebrook_eqn, x0=[0.01] * 2, maxiter=2,
|
||||
args=[reynolds_number, diameter], full_output=True
|
||||
)
|
||||
|
||||
|
||||
# this test should **not** raise a RuntimeWarning
|
||||
def test_gh8904_zeroder_at_root_fails():
|
||||
"""Test that Newton or Halley don't warn if zero derivative at root"""
|
||||
|
||||
# a function that has a zero derivative at it's root
|
||||
def f_zeroder_root(x):
|
||||
return x**3 - x**2
|
||||
|
||||
# should work with secant
|
||||
r = zeros.newton(f_zeroder_root, x0=0)
|
||||
assert_allclose(r, 0, atol=zeros._xtol, rtol=zeros._rtol)
|
||||
# test again with array
|
||||
r = zeros.newton(f_zeroder_root, x0=[0]*10)
|
||||
assert_allclose(r, 0, atol=zeros._xtol, rtol=zeros._rtol)
|
||||
|
||||
# 1st derivative
|
||||
def fder(x):
|
||||
return 3 * x**2 - 2 * x
|
||||
|
||||
# 2nd derivative
|
||||
def fder2(x):
|
||||
return 6*x - 2
|
||||
|
||||
# should work with newton and halley
|
||||
r = zeros.newton(f_zeroder_root, x0=0, fprime=fder)
|
||||
assert_allclose(r, 0, atol=zeros._xtol, rtol=zeros._rtol)
|
||||
r = zeros.newton(f_zeroder_root, x0=0, fprime=fder,
|
||||
fprime2=fder2)
|
||||
assert_allclose(r, 0, atol=zeros._xtol, rtol=zeros._rtol)
|
||||
# test again with array
|
||||
r = zeros.newton(f_zeroder_root, x0=[0]*10, fprime=fder)
|
||||
assert_allclose(r, 0, atol=zeros._xtol, rtol=zeros._rtol)
|
||||
r = zeros.newton(f_zeroder_root, x0=[0]*10, fprime=fder,
|
||||
fprime2=fder2)
|
||||
assert_allclose(r, 0, atol=zeros._xtol, rtol=zeros._rtol)
|
||||
|
||||
# also test that if a root is found we do not raise RuntimeWarning even if
|
||||
# the derivative is zero, EG: at x = 0.5, then fval = -0.125 and
|
||||
# fder = -0.25 so the next guess is 0.5 - (-0.125/-0.5) = 0 which is the
|
||||
# root, but if the solver continued with that guess, then it will calculate
|
||||
# a zero derivative, so it should return the root w/o RuntimeWarning
|
||||
r = zeros.newton(f_zeroder_root, x0=0.5, fprime=fder)
|
||||
assert_allclose(r, 0, atol=zeros._xtol, rtol=zeros._rtol)
|
||||
# test again with array
|
||||
r = zeros.newton(f_zeroder_root, x0=[0.5]*10, fprime=fder)
|
||||
assert_allclose(r, 0, atol=zeros._xtol, rtol=zeros._rtol)
|
||||
# doesn't apply to halley
|
||||
|
||||
|
||||
def test_gh_8881():
|
||||
r"""Test that Halley's method realizes that the 2nd order adjustment
|
||||
is too big and drops off to the 1st order adjustment."""
|
||||
n = 9
|
||||
|
||||
def f(x):
|
||||
return power(x, 1.0/n) - power(n, 1.0/n)
|
||||
|
||||
def fp(x):
|
||||
return power(x, (1.0-n)/n)/n
|
||||
|
||||
def fpp(x):
|
||||
return power(x, (1.0-2*n)/n) * (1.0/n) * (1.0-n)/n
|
||||
|
||||
x0 = 0.1
|
||||
# The root is at x=9.
|
||||
# The function has positive slope, x0 < root.
|
||||
# Newton succeeds in 8 iterations
|
||||
rt, r = newton(f, x0, fprime=fp, full_output=True)
|
||||
assert r.converged
|
||||
# Before the Issue 8881/PR 8882, halley would send x in the wrong direction.
|
||||
# Check that it now succeeds.
|
||||
rt, r = newton(f, x0, fprime=fp, fprime2=fpp, full_output=True)
|
||||
assert r.converged
|
||||
|
||||
|
||||
def test_gh_9608_preserve_array_shape():
|
||||
"""
|
||||
Test that shape is preserved for array inputs even if fprime or fprime2 is
|
||||
scalar
|
||||
"""
|
||||
def f(x):
|
||||
return x**2
|
||||
|
||||
def fp(x):
|
||||
return 2 * x
|
||||
|
||||
def fpp(x):
|
||||
return 2
|
||||
|
||||
x0 = np.array([-2], dtype=np.float32)
|
||||
rt, r = newton(f, x0, fprime=fp, fprime2=fpp, full_output=True)
|
||||
assert r.converged
|
||||
|
||||
x0_array = np.array([-2, -3], dtype=np.float32)
|
||||
# This next invocation should fail
|
||||
with pytest.raises(IndexError):
|
||||
result = zeros.newton(
|
||||
f, x0_array, fprime=fp, fprime2=fpp, full_output=True
|
||||
)
|
||||
|
||||
def fpp_array(x):
|
||||
return np.full(np.shape(x), 2, dtype=np.float32)
|
||||
|
||||
result = zeros.newton(
|
||||
f, x0_array, fprime=fp, fprime2=fpp_array, full_output=True
|
||||
)
|
||||
assert result.converged.all()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"maximum_iterations,flag_expected",
|
||||
[(10, zeros.CONVERR), (100, zeros.CONVERGED)])
|
||||
def test_gh9254_flag_if_maxiter_exceeded(maximum_iterations, flag_expected):
|
||||
"""
|
||||
Test that if the maximum iterations is exceeded that the flag is not
|
||||
converged.
|
||||
"""
|
||||
result = zeros.brentq(
|
||||
lambda x: ((1.2*x - 2.3)*x + 3.4)*x - 4.5,
|
||||
-30, 30, (), 1e-6, 1e-6, maximum_iterations,
|
||||
full_output=True, disp=False)
|
||||
assert result[1].flag == flag_expected
|
||||
if flag_expected == zeros.CONVERR:
|
||||
# didn't converge because exceeded maximum iterations
|
||||
assert result[1].iterations == maximum_iterations
|
||||
elif flag_expected == zeros.CONVERGED:
|
||||
# converged before maximum iterations
|
||||
assert result[1].iterations < maximum_iterations
|
||||
|
||||
|
||||
def test_gh9551_raise_error_if_disp_true():
|
||||
"""Test that if disp is true then zero derivative raises RuntimeError"""
|
||||
|
||||
def f(x):
|
||||
return x*x + 1
|
||||
|
||||
def f_p(x):
|
||||
return 2*x
|
||||
|
||||
assert_warns(RuntimeWarning, zeros.newton, f, 1.0, f_p, disp=False)
|
||||
with pytest.raises(
|
||||
RuntimeError,
|
||||
match=r'^Derivative was zero\. Failed to converge after \d+ iterations, value is [+-]?\d*\.\d+\.$'):
|
||||
zeros.newton(f, 1.0, f_p)
|
||||
root = zeros.newton(f, complex(10.0, 10.0), f_p)
|
||||
assert_allclose(root, complex(0.0, 1.0))
|
||||
Reference in New Issue
Block a user