File size: 5,272 Bytes
7885a28 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
import math
import numpy as np
from numpy.testing import assert_allclose, assert_, assert_array_equal
import pytest
from scipy.optimize import fmin_cobyla, minimize, Bounds
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)
@pytest.mark.xslow(True, reason='not slow, but noisy so only run rarely')
def test_simple(self, capfd):
# 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):
rng = np.random.RandomState(1234)
pb = rng.rand(10, 10)
spread = rng.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,))
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)
class TestBounds:
# Test cobyla support for bounds (only when used via `minimize`)
# Invalid bounds is tested in
# test_optimize.TestOptimizeSimple.test_minimize_invalid_bounds
def test_basic(self):
def f(x):
return np.sum(x**2)
lb = [-1, None, 1, None, -0.5]
ub = [-0.5, -0.5, None, None, -0.5]
bounds = [(a, b) for a, b in zip(lb, ub)]
# these are converted to Bounds internally
res = minimize(f, x0=[1, 2, 3, 4, 5], method='cobyla', bounds=bounds)
ref = [-0.5, -0.5, 1, 0, -0.5]
assert res.success
assert_allclose(res.x, ref, atol=1e-3)
def test_unbounded(self):
def f(x):
return np.sum(x**2)
bounds = Bounds([-np.inf, -np.inf], [np.inf, np.inf])
res = minimize(f, x0=[1, 2], method='cobyla', bounds=bounds)
assert res.success
assert_allclose(res.x, 0, atol=1e-3)
bounds = Bounds([1, -np.inf], [np.inf, np.inf])
res = minimize(f, x0=[1, 2], method='cobyla', bounds=bounds)
assert res.success
assert_allclose(res.x, [1, 0], atol=1e-3)
|