From de2f274e4dc21c26008403d2b165d4b90e1f4fd2 Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Tue, 2 Apr 2024 18:55:32 +0200 Subject: [PATCH 1/4] Fixed #249, ensure intermediate_cb returns its value if no exception. --- cyipopt/cython/ipopt_wrapper.pyx | 2 +- cyipopt/tests/unit/test_ipopt_funcs.py | 87 ++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/cyipopt/cython/ipopt_wrapper.pyx b/cyipopt/cython/ipopt_wrapper.pyx index 305b636e..1497ed0a 100644 --- a/cyipopt/cython/ipopt_wrapper.pyx +++ b/cyipopt/cython/ipopt_wrapper.pyx @@ -1278,7 +1278,7 @@ cdef Bool intermediate_cb(Index alg_mod, self.__exception = sys.exc_info() return True - return True + return ret_val class problem(Problem): diff --git a/cyipopt/tests/unit/test_ipopt_funcs.py b/cyipopt/tests/unit/test_ipopt_funcs.py index 789fca9d..25fa7f3a 100644 --- a/cyipopt/tests/unit/test_ipopt_funcs.py +++ b/cyipopt/tests/unit/test_ipopt_funcs.py @@ -391,3 +391,90 @@ def intermediate( np.testing.assert_allclose(pr_violations[-1], np.zeros(m), atol=1e-8) np.testing.assert_allclose(du_violations[-1], np.zeros(n), atol=1e-8) + + +def test_intermediate_cb(): + + class MyProblem(): + def __init__(self): + pass + + def objective(self, x): + return x[0] * x[3] * np.sum(x[0:3]) + x[2] + + def gradient(self, x): + return np.array([ + x[0] * x[3] + x[3] * np.sum(x[0:3]), + x[0] * x[3], + x[0] * x[3] + 1.0, + x[0] * np.sum(x[0:3]) + ]) + + def constraints(self, x): + return np.array((np.prod(x), np.dot(x, x))) + + def jacobian(self, x): + return np.concatenate((np.prod(x) / x, 2*x)) + + def hessianstructure(self): + + return np.nonzero(np.tril(np.ones((4, 4)))) + + def hessian(self, x, lagrange, obj_factor): + H = obj_factor*np.array(( + (2*x[3], 0, 0, 0), + (x[3], 0, 0, 0), + (x[3], 0, 0, 0), + (2*x[0]+x[1]+x[2], x[0], x[0], 0))) + + H += lagrange[0]*np.array(( + (0, 0, 0, 0), + (x[2]*x[3], 0, 0, 0), + (x[1]*x[3], x[0]*x[3], 0, 0), + (x[1]*x[2], x[0]*x[2], x[0]*x[1], 0))) + + H += lagrange[1]*2*np.eye(4) + + row, col = self.hessianstructure() + + return H[row, col] + + def intermediate( + self, + alg_mod, + iter_count, + obj_value, + inf_pr, + inf_du, + mu, + d_norm, + regularization_size, + alpha_du, + alpha_pr, + ls_trials, + ): + return False + + x0 = [1.0, 5.0, 5.0, 1.0] + + lb = [1.0, 1.0, 1.0, 1.0] + ub = [5.0, 5.0, 5.0, 5.0] + + cl = [25.0, 40.0] + cu = [2.0e19, 40.0] + + nlp = cyipopt.Problem( + n=len(x0), + m=len(cl), + problem_obj=MyProblem(), + lb=lb, + ub=ub, + cl=cl, + cu=cu, + ) + + x, info = nlp.solve(x0) + msg = (b'The user call-back function intermediate_callback (see Section ' + b'3.3.4 in the documentation) returned false, i.e., the user code ' + b'requested a premature termination of the optimization.') + assert info['status_msg'] == msg From 1f3a043e1aae9c4fb29e1ccab76f7365543e23fe Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Tue, 2 Apr 2024 19:05:44 +0200 Subject: [PATCH 2/4] Use test fixture. --- cyipopt/tests/unit/test_ipopt_funcs.py | 109 ++++++++----------------- 1 file changed, 35 insertions(+), 74 deletions(-) diff --git a/cyipopt/tests/unit/test_ipopt_funcs.py b/cyipopt/tests/unit/test_ipopt_funcs.py index 25fa7f3a..c0f91b02 100644 --- a/cyipopt/tests/unit/test_ipopt_funcs.py +++ b/cyipopt/tests/unit/test_ipopt_funcs.py @@ -393,88 +393,49 @@ def intermediate( np.testing.assert_allclose(du_violations[-1], np.zeros(n), atol=1e-8) -def test_intermediate_cb(): - - class MyProblem(): - def __init__(self): - pass - - def objective(self, x): - return x[0] * x[3] * np.sum(x[0:3]) + x[2] - - def gradient(self, x): - return np.array([ - x[0] * x[3] + x[3] * np.sum(x[0:3]), - x[0] * x[3], - x[0] * x[3] + 1.0, - x[0] * np.sum(x[0:3]) - ]) - - def constraints(self, x): - return np.array((np.prod(x), np.dot(x, x))) - - def jacobian(self, x): - return np.concatenate((np.prod(x) / x, 2*x)) - - def hessianstructure(self): - - return np.nonzero(np.tril(np.ones((4, 4)))) - - def hessian(self, x, lagrange, obj_factor): - H = obj_factor*np.array(( - (2*x[3], 0, 0, 0), - (x[3], 0, 0, 0), - (x[3], 0, 0, 0), - (2*x[0]+x[1]+x[2], x[0], x[0], 0))) - - H += lagrange[0]*np.array(( - (0, 0, 0, 0), - (x[2]*x[3], 0, 0, 0), - (x[1]*x[3], x[0]*x[3], 0, 0), - (x[1]*x[2], x[0]*x[2], x[0]*x[1], 0))) - - H += lagrange[1]*2*np.eye(4) - - row, col = self.hessianstructure() - - return H[row, col] - - def intermediate( - self, - alg_mod, - iter_count, - obj_value, - inf_pr, - inf_du, - mu, - d_norm, - regularization_size, - alpha_du, - alpha_pr, - ls_trials, - ): - return False +def test_intermediate_cb( + hs071_initial_guess_fixture, + hs071_definition_instance_fixture, + hs071_variable_lower_bounds_fixture, + hs071_variable_upper_bounds_fixture, + hs071_constraint_lower_bounds_fixture, + hs071_constraint_upper_bounds_fixture, +): + x0 = hs071_initial_guess_fixture + lb = hs071_variable_lower_bounds_fixture + ub = hs071_variable_upper_bounds_fixture + cl = hs071_constraint_lower_bounds_fixture + cu = hs071_constraint_upper_bounds_fixture + n = len(x0) + m = len(cl) - x0 = [1.0, 5.0, 5.0, 1.0] + problem_definition = hs071_definition_instance_fixture - lb = [1.0, 1.0, 1.0, 1.0] - ub = [5.0, 5.0, 5.0, 5.0] + def intermediate( + alg_mod, + iter_count, + obj_value, + inf_pr, + inf_du, + mu, + d_norm, + regularization_size, + alpha_du, + alpha_pr, + ls_trials, + ): + return False - cl = [25.0, 40.0] - cu = [2.0e19, 40.0] + problem_definition.intermediate = intermediate nlp = cyipopt.Problem( - n=len(x0), - m=len(cl), - problem_obj=MyProblem(), + n=n, + m=m, + problem_obj=problem_definition, lb=lb, ub=ub, cl=cl, cu=cu, ) - x, info = nlp.solve(x0) - msg = (b'The user call-back function intermediate_callback (see Section ' - b'3.3.4 in the documentation) returned false, i.e., the user code ' - b'requested a premature termination of the optimization.') - assert info['status_msg'] == msg + assert b'premature termination' in info['status_msg'] From 2c64adccfd059869dbc556a138013366abcb6fcb Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Tue, 2 Apr 2024 19:14:50 +0200 Subject: [PATCH 3/4] Bump version to 1.4.1. --- CHANGELOG.rst | 9 +++++++-- cyipopt/version.py | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ba196736..6aec5f10 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -36,8 +36,13 @@ Include sections: Version History --------------- -[1.5.0.dev0] - XXXX-XX-XX -~~~~~~~~~~~~~~~~~~~~~~~~~ +[1.4.1] - 2024-04-02 +~~~~~~~~~~~~~~~~~~~~ + +Fixed ++++++ + +- Addressed regression in return value of ``intermediate_cb``. #250 [1.4.0] - 2024-04-01 ~~~~~~~~~~~~~~~~~~~~ diff --git a/cyipopt/version.py b/cyipopt/version.py index 792e99a2..df29a9cd 100644 --- a/cyipopt/version.py +++ b/cyipopt/version.py @@ -9,4 +9,4 @@ License: EPL 2.0 """ -__version__ = '1.5.0.dev0' +__version__ = '1.4.1' From d18edfa052c9dde446f97db31404a3d06b50a5e2 Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Tue, 2 Apr 2024 19:18:22 +0200 Subject: [PATCH 4/4] Bumped version to 1.5.0.dev0. --- CHANGELOG.rst | 3 +++ cyipopt/version.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6aec5f10..c1fd24f9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -36,6 +36,9 @@ Include sections: Version History --------------- +[1.5.0.dev0] - XXXX-XX-XX +~~~~~~~~~~~~~~~~~~~~~~~~~ + [1.4.1] - 2024-04-02 ~~~~~~~~~~~~~~~~~~~~ diff --git a/cyipopt/version.py b/cyipopt/version.py index df29a9cd..792e99a2 100644 --- a/cyipopt/version.py +++ b/cyipopt/version.py @@ -9,4 +9,4 @@ License: EPL 2.0 """ -__version__ = '1.4.1' +__version__ = '1.5.0.dev0'