-
Notifications
You must be signed in to change notification settings - Fork 277
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #520 from reneeotten/pytest
test-suite: use pytest features, improve coverage, fix mistakes
- Loading branch information
Showing
8 changed files
with
487 additions
and
406 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import numpy as np | ||
import pytest | ||
|
||
import lmfit | ||
|
||
|
||
@pytest.fixture | ||
def minimizer_Alpine02(): | ||
"""Return a lmfit Minimizer object for the Alpine02 function.""" | ||
def residual_Alpine02(params): | ||
x0 = params['x0'].value | ||
x1 = params['x1'].value | ||
return np.prod(np.sqrt(x0) * np.sin(x0)) * np.prod(np.sqrt(x1) * | ||
np.sin(x1)) | ||
|
||
# create Parameters and set initial values and bounds | ||
pars = lmfit.Parameters() | ||
pars.add_many(('x0', 1., True, 0.0, 10.0), | ||
('x1', 1., True, 0.0, 10.0)) | ||
|
||
mini = lmfit.Minimizer(residual_Alpine02, pars) | ||
return mini |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,104 @@ | ||
"""Tests for the AMPGO global minimization algorithm.""" | ||
import sys | ||
|
||
import numpy as np | ||
from numpy.testing import assert_allclose | ||
import pytest | ||
|
||
import lmfit | ||
|
||
# correct result for Alpine02 function | ||
global_optimum = [7.91705268, 4.81584232] | ||
fglob = -6.12950 | ||
|
||
|
||
def test_ampgo_Alpine02(): | ||
@pytest.mark.parametrize("tabustrategy", ['farthest', 'oldest']) | ||
def test_ampgo_Alpine02(minimizer_Alpine02, tabustrategy): | ||
"""Test AMPGO algorithm on Alpine02 function.""" | ||
kws = {'tabustrategy': tabustrategy} | ||
out = minimizer_Alpine02.minimize(method='ampgo', **kws) | ||
out_x = np.array([out.params['x0'].value, out.params['x1'].value]) | ||
|
||
assert_allclose(out.residual, fglob, rtol=1e-5) | ||
assert_allclose(min(out_x), min(global_optimum), rtol=1e-3) | ||
assert_allclose(max(out_x), max(global_optimum), rtol=1e-3) | ||
assert 'global' in out.ampgo_msg | ||
|
||
|
||
def test_ampgo_bounds(minimizer_Alpine02): | ||
"""Test AMPGO algorithm with bounds.""" | ||
# change boundaries of parameters | ||
pars_bounds = lmfit.Parameters() | ||
pars_bounds.add_many(('x0', 1., True, 5.0, 15.0), | ||
('x1', 1., True, 2.5, 7.5)) | ||
|
||
global_optimum = [7.91705268, 4.81584232] | ||
fglob = -6.12950 | ||
out = minimizer_Alpine02.minimize(params=pars_bounds, method='ampgo') | ||
assert 5.0 <= out.params['x0'].value <= 15.0 | ||
assert 2.5 <= out.params['x1'].value <= 7.5 | ||
|
||
def residual_Alpine02(params): | ||
x0 = params['x0'].value | ||
x1 = params['x1'].value | ||
return np.prod(np.sqrt(x0) * np.sin(x0)) * np.prod(np.sqrt(x1) * | ||
np.sin(x1)) | ||
|
||
pars = lmfit.Parameters() | ||
pars.add_many(('x0', 1., True, 0.0, 10.0), | ||
('x1', 1., True, 0.0, 10.0)) | ||
def test_ampgo_disp_true(minimizer_Alpine02, capsys): | ||
"""Test AMPGO algorithm with disp is True.""" | ||
# disp to False for L-BFGS-B to avoid too much output... | ||
kws = {'disp': True, 'local_opts': {'disp': False}} | ||
minimizer_Alpine02.minimize(method='ampgo', **kws) | ||
captured = capsys.readouterr() | ||
assert "Starting MINIMIZATION Phase" in captured.out | ||
|
||
mini = lmfit.Minimizer(residual_Alpine02, pars) | ||
out = mini.minimize(method='ampgo') | ||
|
||
def test_ampgo_maxfunevals(minimizer_Alpine02): | ||
"""Test AMPGO algorithm with maxfunevals.""" | ||
# disp to False for L-BFGS-B to avoid too much output... | ||
kws = {'maxfunevals': 5, 'disp': True, 'local_opts': {'disp': False}} | ||
out = minimizer_Alpine02.minimize(method='ampgo', **kws) | ||
|
||
assert out.ampgo_msg == 'Maximum number of function evaluations exceeded' | ||
|
||
|
||
def test_ampgo_local_solver(minimizer_Alpine02): | ||
"""Test AMPGO algorithm with local solver.""" | ||
kws = {'local': 'Nelder-Mead'} | ||
out = minimizer_Alpine02.minimize(method='ampgo', **kws) | ||
out_x = np.array([out.params['x0'].value, out.params['x1'].value]) | ||
|
||
assert 'ampgo' and 'Nelder-Mead' in out.method | ||
assert_allclose(out.residual, fglob, rtol=1e-5) | ||
assert_allclose(min(out_x), min(global_optimum), rtol=1e-3) | ||
assert_allclose(max(out_x), max(global_optimum), rtol=1e-3) | ||
assert('global' in out.ampgo_msg) | ||
assert 'global' in out.ampgo_msg | ||
|
||
|
||
def test_ampgo_Alpine02_maxfunevals(): | ||
"""Test AMPGO algorithm on Alpine02 function.""" | ||
def test_ampgo_invalid_local_solver(minimizer_Alpine02): | ||
"""Test AMPGO algorithm with invalid local solvers.""" | ||
kws = {'local': 'leastsq'} | ||
with pytest.raises(Exception, match=r'Invalid local solver selected'): | ||
minimizer_Alpine02.minimize(method='ampgo', **kws) | ||
|
||
|
||
def test_ampgo_invalid_tabulistsize(minimizer_Alpine02): | ||
"""Test AMPGO algorithm with invalid tabulistsize.""" | ||
kws = {'tabulistsize': 0} | ||
with pytest.raises(Exception, match=r'Invalid tabulistsize specified'): | ||
minimizer_Alpine02.minimize(method='ampgo', **kws) | ||
|
||
|
||
def test_ampgo_invalid_tabustrategy(minimizer_Alpine02): | ||
"""Test AMPGO algorithm with invalid tabustrategy.""" | ||
kws = {'tabustrategy': 'unknown'} | ||
with pytest.raises(Exception, match=r'Invalid tabustrategy specified'): | ||
minimizer_Alpine02.minimize(method='ampgo', **kws) | ||
|
||
def residual_Alpine02(params): | ||
x0 = params['x0'].value | ||
x1 = params['x1'].value | ||
return np.prod(np.sqrt(x0) * np.sin(x0)) * np.prod(np.sqrt(x1) * | ||
np.sin(x1)) | ||
|
||
pars = lmfit.Parameters() | ||
pars.add_many(('x0', 1., True, 0.0, 10.0), | ||
('x1', 1., True, 0.0, 10.0)) | ||
@pytest.mark.skipif(sys.version_info.major == 2, | ||
reason="does not throw an exception in Python 2") | ||
def test_ampgo_local_opts(minimizer_Alpine02): | ||
"""Test AMPGO algorithm, pass local_opts to solver.""" | ||
# use local_opts to pass maxiter to the local optimizer: providing a string | ||
# whereas an integer is required, this should throw an error. | ||
kws = {'local_opts': {'maxiter': 'string'}} | ||
with pytest.raises(TypeError): | ||
minimizer_Alpine02.minimize(method='ampgo', **kws) | ||
|
||
mini = lmfit.Minimizer(residual_Alpine02, pars) | ||
kws = {'maxfunevals': 50} | ||
out = mini.minimize(method='ampgo', **kws) | ||
assert('function' in out.ampgo_msg) | ||
# for coverage: make sure that both occurences are reached | ||
kws = {'local_opts': {'maxiter': 10}, 'maxfunevals': 50} | ||
minimizer_Alpine02.minimize(method='ampgo', **kws) |
Oops, something went wrong.