From 08fe5fe4184fd0ebf10ee6c6acb025146e2e7628 Mon Sep 17 00:00:00 2001 From: Neil Wu Date: Tue, 5 Dec 2023 17:51:57 -0800 Subject: [PATCH 1/5] fix test for earlier SNOPT versions --- tests/testing_utils.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/testing_utils.py b/tests/testing_utils.py index 84fc2d37..26791de7 100644 --- a/tests/testing_utils.py +++ b/tests/testing_utils.py @@ -6,6 +6,7 @@ from baseclasses.testing.assertions import assert_dict_allclose, assert_equal import numpy as np from numpy.testing import assert_allclose +from pkg_resources import parse_version # First party modules from pyoptsparse import OPT, History @@ -120,9 +121,14 @@ def assert_solution_allclose(self, sol, tol, partial_x=False): self.sol_index = 0 # now we assert against the closest solution # objective - assert_allclose(sol.fStar, self.fStar[self.sol_index], atol=tol, rtol=tol) - # make sure fStar and sol.objectives values match - assert_allclose(sol.fStar, [obj.value for obj in sol.objectives.values()], rtol=1e-12) + sol_objectives = np.array([obj.value for obj in sol.objectives.values()]) + if self.optName == "SNOPT" and parse_version(self.optVersion) < parse_version("7.7.7"): + # don't check sol.fStar because it was broken for earlier versions of SNOPT + assert_allclose(sol_objectives, self.fStar[self.sol_index], atol=tol, rtol=tol) + else: + assert_allclose(sol.fStar, self.fStar[self.sol_index], atol=tol, rtol=tol) + # make sure fStar and sol.objectives values match + assert_allclose(sol.fStar, sol_objectives, rtol=1e-12) # x assert_dict_allclose(sol.xStar, self.xStar[self.sol_index], atol=tol, rtol=tol, partial=partial_x) dv = sol.getDVs() From 73cc20692e467443608ef2a3c0d854f391d4debd Mon Sep 17 00:00:00 2001 From: Neil Wu Date: Thu, 7 Dec 2023 09:57:13 -0800 Subject: [PATCH 2/5] don't store obj value for buggy SNOPT --- pyoptsparse/pySNOPT/pySNOPT.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pyoptsparse/pySNOPT/pySNOPT.py b/pyoptsparse/pySNOPT/pySNOPT.py index bac40403..3a948896 100644 --- a/pyoptsparse/pySNOPT/pySNOPT.py +++ b/pyoptsparse/pySNOPT/pySNOPT.py @@ -15,6 +15,7 @@ from baseclasses.utils import CaseInsensitiveSet import numpy as np from numpy import ndarray +from pkg_resources import parse_version # Local modules from ..pyOpt_error import Error @@ -520,7 +521,14 @@ def __call__( sol_inform["text"] = self.informs[inform] # Create the optimization solution - sol = self._createSolution(optTime, sol_inform, obj, xs[:nvar], multipliers=pi) + if parse_version(self.version) > parse_version("7.7.0") and parse_version(self.version) < parse_version( + "7.7.7" + ): + # buggy SNOPT obj value + sol_objectives = np.array([obj.value for obj in self.optProb.objectives.values()]) + sol = self._createSolution(optTime, sol_inform, sol_objectives, xs[:nvar], multipliers=pi) + else: + sol = self._createSolution(optTime, sol_inform, obj, xs[:nvar], multipliers=pi) restartDict = { "cw": cw, "iw": iw, From ee254e2486fbe51667598e3829dce68f51194911 Mon Sep 17 00:00:00 2001 From: Neil Wu Date: Thu, 7 Dec 2023 09:57:22 -0800 Subject: [PATCH 3/5] update testing asserts --- tests/testing_utils.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/testing_utils.py b/tests/testing_utils.py index 26791de7..98bf2fce 100644 --- a/tests/testing_utils.py +++ b/tests/testing_utils.py @@ -119,16 +119,16 @@ def assert_solution_allclose(self, sol, tol, partial_x=False): else: # assume we have a single solution self.sol_index = 0 + # now we assert against the closest solution # objective sol_objectives = np.array([obj.value for obj in sol.objectives.values()]) - if self.optName == "SNOPT" and parse_version(self.optVersion) < parse_version("7.7.7"): - # don't check sol.fStar because it was broken for earlier versions of SNOPT - assert_allclose(sol_objectives, self.fStar[self.sol_index], atol=tol, rtol=tol) - else: - assert_allclose(sol.fStar, self.fStar[self.sol_index], atol=tol, rtol=tol) - # make sure fStar and sol.objectives values match - assert_allclose(sol.fStar, sol_objectives, rtol=1e-12) + assert_allclose(sol.fStar, self.fStar[self.sol_index], atol=tol, rtol=tol) + # make sure fStar and sol.objectives values match + # NOTE this is not true in general, but true for well-behaving optimizations + # which should be the case for all tests + assert_allclose(sol.fStar, sol_objectives, rtol=1e-12) + # x assert_dict_allclose(sol.xStar, self.xStar[self.sol_index], atol=tol, rtol=tol, partial=partial_x) dv = sol.getDVs() From 5535829f897e5fd2edb4d44d07f32ef8589bf885 Mon Sep 17 00:00:00 2001 From: Eirikur Jonsson Date: Fri, 8 Dec 2023 13:35:45 -0500 Subject: [PATCH 4/5] adjust for scaling --- pyoptsparse/pySNOPT/pySNOPT.py | 13 +++++-------- tests/testing_utils.py | 3 +-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/pyoptsparse/pySNOPT/pySNOPT.py b/pyoptsparse/pySNOPT/pySNOPT.py index 3a948896..8309cf22 100644 --- a/pyoptsparse/pySNOPT/pySNOPT.py +++ b/pyoptsparse/pySNOPT/pySNOPT.py @@ -521,14 +521,11 @@ def __call__( sol_inform["text"] = self.informs[inform] # Create the optimization solution - if parse_version(self.version) > parse_version("7.7.0") and parse_version(self.version) < parse_version( - "7.7.7" - ): - # buggy SNOPT obj value - sol_objectives = np.array([obj.value for obj in self.optProb.objectives.values()]) - sol = self._createSolution(optTime, sol_inform, sol_objectives, xs[:nvar], multipliers=pi) - else: - sol = self._createSolution(optTime, sol_inform, obj, xs[:nvar], multipliers=pi) + if parse_version(self.version) > parse_version("7.7.0") and parse_version(self.version) < parse_version("7.7.7"): + # SNOPT obj value is buggy and returned as 0, its thus overwritten with the solution objective value + obj = np.array([obj.value*obj.scale for obj in self.optProb.objectives.values()]) + + sol = self._createSolution(optTime, sol_inform, obj, xs[:nvar], multipliers=pi) restartDict = { "cw": cw, "iw": iw, diff --git a/tests/testing_utils.py b/tests/testing_utils.py index 98bf2fce..08017b5f 100644 --- a/tests/testing_utils.py +++ b/tests/testing_utils.py @@ -6,7 +6,6 @@ from baseclasses.testing.assertions import assert_dict_allclose, assert_equal import numpy as np from numpy.testing import assert_allclose -from pkg_resources import parse_version # First party modules from pyoptsparse import OPT, History @@ -122,11 +121,11 @@ def assert_solution_allclose(self, sol, tol, partial_x=False): # now we assert against the closest solution # objective - sol_objectives = np.array([obj.value for obj in sol.objectives.values()]) assert_allclose(sol.fStar, self.fStar[self.sol_index], atol=tol, rtol=tol) # make sure fStar and sol.objectives values match # NOTE this is not true in general, but true for well-behaving optimizations # which should be the case for all tests + sol_objectives = np.array([obj.value for obj in sol.objectives.values()]) assert_allclose(sol.fStar, sol_objectives, rtol=1e-12) # x From 5bedf92432fbde7b807404792fe23d7316aaab57 Mon Sep 17 00:00:00 2001 From: Eirikur Jonsson Date: Fri, 8 Dec 2023 15:05:46 -0500 Subject: [PATCH 5/5] black --- pyoptsparse/pySNOPT/pySNOPT.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pyoptsparse/pySNOPT/pySNOPT.py b/pyoptsparse/pySNOPT/pySNOPT.py index 8309cf22..249f4ddd 100644 --- a/pyoptsparse/pySNOPT/pySNOPT.py +++ b/pyoptsparse/pySNOPT/pySNOPT.py @@ -521,9 +521,11 @@ def __call__( sol_inform["text"] = self.informs[inform] # Create the optimization solution - if parse_version(self.version) > parse_version("7.7.0") and parse_version(self.version) < parse_version("7.7.7"): + if parse_version(self.version) > parse_version("7.7.0") and parse_version(self.version) < parse_version( + "7.7.7" + ): # SNOPT obj value is buggy and returned as 0, its thus overwritten with the solution objective value - obj = np.array([obj.value*obj.scale for obj in self.optProb.objectives.values()]) + obj = np.array([obj.value * obj.scale for obj in self.optProb.objectives.values()]) sol = self._createSolution(optTime, sol_inform, obj, xs[:nvar], multipliers=pi) restartDict = {