diff --git a/pyomo/contrib/ampl_function_demo/build.py b/pyomo/contrib/ampl_function_demo/build.py index 10b9667aded..2bb50ab203a 100644 --- a/pyomo/contrib/ampl_function_demo/build.py +++ b/pyomo/contrib/ampl_function_demo/build.py @@ -15,10 +15,10 @@ def build_ampl_function_demo(user_args=[], parallel=None): return build_cmake_project( - targets=['src'], - package_name='asl_external_demo', - description='AMPL External function demo library', - user_args=['-DBUILD_AMPLASL_IF_NEEDED=ON'] + user_args, + targets=["src"], + package_name="asl_external_demo", + description="AMPL External function demo library", + user_args=["-DBUILD_AMPLASL_IF_NEEDED=ON"] + user_args, parallel=parallel, ) diff --git a/pyomo/contrib/ampl_function_demo/plugins.py b/pyomo/contrib/ampl_function_demo/plugins.py index b06dc08614c..1887ebe0194 100644 --- a/pyomo/contrib/ampl_function_demo/plugins.py +++ b/pyomo/contrib/ampl_function_demo/plugins.py @@ -14,4 +14,4 @@ def load(): - ExtensionBuilderFactory.register('ampl_function_demo')(AMPLFunctionDemoBuilder) + ExtensionBuilderFactory.register("ampl_function_demo")(AMPLFunctionDemoBuilder) diff --git a/pyomo/contrib/ampl_function_demo/src/functions.c b/pyomo/contrib/ampl_function_demo/src/functions.c index fd4372693e7..1c4b3786677 100644 --- a/pyomo/contrib/ampl_function_demo/src/functions.c +++ b/pyomo/contrib/ampl_function_demo/src/functions.c @@ -159,6 +159,27 @@ extern real scbrt(arglist *al){ } +/* A function where f(x) = sgn(x)x^2 + */ +extern real sgnsqr(arglist *al){ + real x = al->ra[al->at[0]]; + + real y = copysign(x * x, x); + + // Compute the first derivative, if requested. + if (al->derivs!=NULL) { + al->derivs[0] = copysign(1, x) * 2 * x; + } + + // Compute the second derivative, if requested. + if (al->hes!=NULL) { + al->hes[0] = copysign(2, x); + } + + return y; +} + + // Register external functions defined in this library with the ASL void funcadd(AmplExports *ae){ /* Arguments for addfunc (this is not fully detailed; see funcadd.h) @@ -173,6 +194,8 @@ void funcadd(AmplExports *ae){ */ addfunc("safe_cbrt", (rfunc)scbrt, FUNCADD_REAL_VALUED, 1, NULL); + addfunc("sgnsqr", (rfunc)sgnsqr, + FUNCADD_REAL_VALUED, 1, NULL); addfunc("demo_function", (rfunc)demo_function, FUNCADD_REAL_VALUED|FUNCADD_STRING_ARGS, -2, NULL); } diff --git a/pyomo/contrib/ampl_function_demo/tests/test_ampl_function_demo.py b/pyomo/contrib/ampl_function_demo/tests/test_ampl_function_demo.py index 196d6b2f4e9..2ba8b360bca 100644 --- a/pyomo/contrib/ampl_function_demo/tests/test_ampl_function_demo.py +++ b/pyomo/contrib/ampl_function_demo/tests/test_ampl_function_demo.py @@ -17,12 +17,12 @@ from pyomo.core.base.external import nan flib = find_library("asl_external_demo") -is_pypy = platform.python_implementation().lower().startswith('pypy') +is_pypy = platform.python_implementation().lower().startswith("pypy") @unittest.skipUnless(flib, 'Could not find the "asl_external_demo.so" library') class TestAMPLExternalFunction(unittest.TestCase): - @unittest.skipIf(is_pypy, 'Cannot evaluate external functions under pypy') + @unittest.skipIf(is_pypy, "Cannot evaluate external functions under pypy") def test_eval_function(self): m = pyo.ConcreteModel() m.tf = pyo.ExternalFunction(library=flib, function="demo_function") @@ -34,7 +34,7 @@ def test_eval_function(self): m.cbrt.evaluate_fgh([0]), (0, [100951], [-1.121679e13]), reltol=1e-5 ) - @unittest.skipIf(is_pypy, 'Cannot evaluate external functions under pypy') + @unittest.skipIf(is_pypy, "Cannot evaluate external functions under pypy") def test_eval_function_fgh(self): m = pyo.ConcreteModel() m.tf = pyo.ExternalFunction(library=flib, function="demo_function") @@ -52,7 +52,7 @@ def test_eval_function_fgh(self): ) @unittest.skipUnless( - check_available_solvers('ipopt'), "The 'ipopt' solver is not available" + check_available_solvers("ipopt"), "The 'ipopt' solver is not available" ) def test_solve_function(self): m = pyo.ConcreteModel() @@ -68,3 +68,18 @@ def test_solve_function(self): solver = pyo.SolverFactory("ipopt") solver.solve(m, tee=True) self.assertAlmostEqual(m.x(), 6, 4) + + @unittest.skipIf(is_pypy, "Cannot evaluate external functions under pypy") + def test_eval_sqnsqr_function_fgh(self): + m = pyo.ConcreteModel() + m.tf = pyo.ExternalFunction(library=flib, function="sgnsqr") + + f, g, h = m.tf.evaluate_fgh((2,)) + self.assertEqual(f, 4) + self.assertEqual(g, [4]) + self.assertEqual(h, [2]) + + f, g, h = m.tf.evaluate_fgh((-2,)) + self.assertAlmostEqual(f, -4) + self.assertStructuredAlmostEqual(g, [4]) + self.assertStructuredAlmostEqual(h, [-2])