1+ from prima import minimize , NonlinearConstraint
2+ from prima .backends .pyprima .common .infos import CALLBACK_TERMINATE , SMALL_TR_RADIUS
3+ from prima .backends .pybindings import __cmake_build_type__
4+ import numpy as np
5+ import pytest
6+ import platform
7+
8+ def obj (x ):
9+ return (x [0 ] - 1 )** 2 + (x [1 ] - 2.5 )** 2
10+ obj .x0 = np .array ([5 , 5 ])
11+ obj .optimal = np .array ([1 , 2.5 ])
12+
13+
14+ def test_callback_terminate (pyprima_turn_on_debugging , backend_fixture ):
15+ def callback (x , * args ):
16+ return True
17+ result = minimize (obj , obj .x0 , method = 'cobyla' , callback = callback , options = {'backend' : backend_fixture })
18+ assert result .nfev == 3
19+ assert result .status == CALLBACK_TERMINATE
20+
21+
22+ def test_callback_no_terminate (backend_fixture ):
23+ def callback (x , * args ):
24+ pass
25+ result = minimize (obj , obj .x0 , method = 'cobyla' , callback = callback , options = {'backend' : backend_fixture })
26+ # Different platforms finish with different nfev due to different optimizations
27+ assert result .nfev == (56 if (
28+ (platform .machine ().lower () in ["x86_64" , "amd64" , "i386" ]) or
29+ (__cmake_build_type__ == "Debug" ) or (backend_fixture == 'Python' )
30+ ) else 54 )
31+ assert np .allclose (result .x , obj .optimal , atol = 1e-3 )
32+ assert result .status == SMALL_TR_RADIUS
33+
34+
35+ def test_rhoend_without_rhobeg (backend_fixture ):
36+ result = minimize (obj , obj .x0 , method = 'cobyla' , options = {'rhoend' : 4e-4 , 'backend' : backend_fixture })
37+ assert np .allclose (result .x , obj .optimal , atol = 1e-3 )
38+
39+
40+ def test_rhobeg_without_rhoend (backend_fixture ):
41+ # Needs to be negative to trigger the right section of code.
42+ if backend_fixture == "Python" :
43+ with pytest .warns (UserWarning , match = "COBYLA: Invalid RHOBEG; it should be a positive number; it is set to 1" ):
44+ result = minimize (obj , obj .x0 , method = 'cobyla' , options = {'rhobeg' : - 1 , 'backend' : backend_fixture })
45+ else :
46+ # The Fortran emits warnings to stdout as opposed to a Python warning
47+ result = minimize (obj , obj .x0 , method = 'cobyla' , options = {'rhobeg' : - 1 , 'backend' : backend_fixture })
48+ assert np .allclose (result .x , obj .optimal , atol = 1e-3 )
49+
50+
51+ def test_eta2_without_eta1 (backend_fixture ):
52+ result = minimize (obj , obj .x0 , method = 'cobyla' , options = {'eta2' : 0.7 , 'backemd' : backend_fixture })
53+ assert np .allclose (result .x , obj .optimal , atol = 1e-3 )
54+
55+
56+ def test_eta2_without_eta1_and_eta2_out_of_range (backend_fixture ):
57+ if backend_fixture == 'Python' :
58+ with pytest .warns (UserWarning , match = r"COBYLA: Invalid ETA2; it should be in the interval \[0, 1\) and not less than ETA1; it is set to 0.7000000000000001" ):
59+ result = minimize (obj , obj .x0 , method = 'cobyla' , options = {'eta2' : 1.7 , 'backend' : backend_fixture })
60+ else :
61+ result = minimize (obj , obj .x0 , method = 'cobyla' , options = {'eta2' : 1.7 , 'backend' : backend_fixture })
62+ assert np .allclose (result .x , obj .optimal , atol = 1e-3 )
63+
64+
65+ def test_eta1_without_eta2_and_eta1_out_of_range (backend_fixture ):
66+ if backend_fixture == 'Python' :
67+ with pytest .warns (UserWarning , match = r"COBYLA: Invalid ETA1; it should be in the interval \[0, 1\) and not more than ETA2; it is set to 0.09999999999999999" ):
68+ with pytest .warns (UserWarning , match = r"COBYLA: Invalid ETA2; it should be in the interval \[0, 1\) and not less than ETA1; it is set to 0.7000000000000001" ):
69+ result = minimize (obj , obj .x0 , method = 'cobyla' , options = {'eta1' : 1.7 , 'backend' : backend_fixture })
70+ else :
71+ result = minimize (obj , obj .x0 , method = 'cobyla' , options = {'eta1' : 1.7 , 'backend' : backend_fixture })
72+ assert np .allclose (result .x , obj .optimal , atol = 1e-3 )
73+
74+ @pytest .mark .parametrize ('iprint' , [- 1 , 0 , 1 , 2 , 3 , 4 ])
75+ def test_iprint (iprint , backend_fixture ):
76+ if backend_fixture == 'Python' and iprint == 4 :
77+ with pytest .warns (UserWarning , match = r"COBYLA: Invalid IPRINT; it should be 0, 1, -1, 2, -2, 3, or -3; it is set to 0" ):
78+ result = minimize (obj , obj .x0 , method = 'cobyla' , options = {'iprint' : iprint , 'backend' : backend_fixture })
79+ else :
80+ result = minimize (obj , obj .x0 , method = 'cobyla' , options = {'iprint' : iprint , 'backend' : backend_fixture })
81+ assert np .allclose (result .x , obj .optimal , atol = 1e-3 )
82+
83+
84+ def test_minimize_constraint_violation (backend_fixture ):
85+ # We set up conflicting constraints so that the algorithm will be
86+ # guaranteed to end up with maxcv > 0.
87+ cons = [NonlinearConstraint (lambda x : x - 4 , - np .inf , 0 ),
88+ NonlinearConstraint (lambda x : 5 - x , - np .inf , 0 )]
89+ result = minimize (lambda x : x [0 ], np .array ([0 ]), method = 'cobyla' , constraints = cons ,
90+ options = {'backend' : backend_fixture })
91+ assert result .maxcv > 0.1
92+ assert result .status == SMALL_TR_RADIUS
93+
94+
95+ def test_scalar ():
96+ result = minimize (lambda x : x ** 2 , 5 , method = 'cobyla' )
97+ assert np .allclose (result .x , 0 , atol = 1e-3 )
0 commit comments