def test_pprint(self): m = ConcreteModel() m.h = ExternalFunction(_g) out = StringIO() m.pprint() m.pprint(ostream=out) self.assertEqual( out.getvalue().strip(), """ 1 ExternalFunction Declarations h : function=_g, units=None, arg_units=None 1 Declarations: h """.strip()) if not pint_available: return m.i = ExternalFunction(function=_h, units=units.kg, arg_units=[units.m, units.s]) out = StringIO() m.pprint(ostream=out) self.assertEqual( out.getvalue().strip(), """ 2 ExternalFunction Declarations h : function=_g, units=None, arg_units=None i : function=_h, units=kg, arg_units=['m', 's'] 2 Declarations: h i """.strip())
def test_pprint(self): m = ConcreteModel() m.f = ExternalFunction(library="junk.so", function="junk") out = StringIO() m.pprint(ostream=out) self.assertEqual( out.getvalue().strip(), """ 1 ExternalFunction Declarations f : function=junk, library=junk.so, units=None, arg_units=None 1 Declarations: f """.strip()) if not pint_available: return m.g = ExternalFunction(library="junk.so", function="junk", units=units.kg, arg_units=[units.m, units.s]) out = StringIO() m.pprint(ostream=out) self.assertEqual( out.getvalue().strip(), """ 2 ExternalFunction Declarations f : function=junk, library=junk.so, units=None, arg_units=None g : function=junk, library=junk.so, units=kg, arg_units=['m', 's'] 2 Declarations: f g """.strip())
def test_constructor_errors(self): m = ConcreteModel() with self.assertRaisesRegex( ValueError, "Duplicate definition of external function " r"through positional and keyword \('function='\)"): m.f = ExternalFunction(_count, function=_count) with self.assertRaisesRegex( ValueError, "PythonCallbackFunction constructor only " "supports 0 - 3 positional arguments"): m.f = ExternalFunction(1, 2, 3, 4) with self.assertRaisesRegex( ValueError, "Cannot specify 'fgh' with any of " "{'function', 'gradient', hessian'}"): m.f = ExternalFunction(_count, fgh=_fgh)
def test_eval_gsl_function(self): DLL = find_GSL() if not DLL: self.skipTest("Could not find the amplgsl.dll library") model = ConcreteModel() model.gamma = ExternalFunction( library=DLL, function="gsl_sf_gamma") model.bessel = ExternalFunction( library=DLL, function="gsl_sf_bessel_Jnu") model.x = Var(initialize=3, bounds=(1e-5,None)) model.o = Objective(expr=model.gamma(model.x)) self.assertAlmostEqual(value(model.o), 2.0, 7) f = model.bessel.evaluate((0.5, 2.0,)) self.assertAlmostEqual(f, 0.5130161365618272, 7)
def test_properties(self): m = ConcreteModel() m.f = ExternalFunction(_sum) e = m.f() self.assertIsInstance(e, NPV_ExternalFunctionExpression) self.assertFalse(e.is_constant()) self.assertTrue(e.is_fixed()) self.assertFalse(e.is_potentially_variable()) # This is just a check for coverage: the expression is_constant # catches that the function ID is a NumericConstant and doesn't # call the method. self.assertFalse(e.arg(0).is_constant()) self.assertTrue(e.arg(0).is_fixed()) self.assertFalse(e.arg(0).is_potentially_variable()) m.p = Param(initialize=1) e = m.f(m.p) self.assertIsInstance(e, NPV_ExternalFunctionExpression) self.assertFalse(e.is_constant()) self.assertTrue(e.is_fixed()) self.assertFalse(e.is_potentially_variable()) m.x = Var(initialize=1) e = m.f(m.p, m.x) self.assertIsInstance(e, ExternalFunctionExpression) self.assertFalse(e.is_constant()) self.assertFalse(e.is_fixed()) self.assertTrue(e.is_potentially_variable())
def test_call_countArgs(self): m = ConcreteModel() m.f = ExternalFunction(_count) self.assertIsInstance(m.f, PythonCallbackFunction) self.assertEqual(value(m.f()), 0) self.assertEqual(value(m.f(2)), 1) self.assertEqual(value(m.f(2, 3)), 2)
def test_load_local_asl_library(self): DLL = find_GSL() if not DLL: self.skipTest("Could not find the amplgsl.dll library") LIB = 'test_pyomo_external_gsl.dll' model = ConcreteModel() model.gamma = ExternalFunction(library=LIB, function="gsl_sf_gamma") model.x = Var(initialize=3, bounds=(1e-5, None)) model.o = Objective(expr=model.gamma(model.x)) with TempfileManager.new_context() as tempfile: dname = tempfile.mkdtemp() shutil.copyfile(DLL, os.path.join(dname, LIB)) # Without changing directories, the load should fail with self.assertRaises(OSError): value(model.o) # Changing directories should pick up the library try: orig_dir = os.getcwd() os.chdir(dname) self.assertAlmostEqual(value(model.o), 2.0, 7) finally: os.chdir(orig_dir)
def test_partial_clone(self): m = ConcreteModel() m.f = ExternalFunction(_sum) m.x = Var(initialize=3) m.y = Var(initialize=5) m.b = Block() m.b.e = Expression(expr=m.f(m.x, m.y)) self.assertIsInstance(m.f, PythonCallbackFunction) self.assertEqual(m.f._fcn_id, m.b.e.arg(0).arg(-1).value) self.assertEqual(value(m.b.e), 10) m.c = m.b.clone() self.assertIsNot(m.b.e, m.c.e) self.assertIsNot(m.b.e.arg(0), m.c.e.arg(0)) self.assertEqual(m.f._fcn_id, m.b.e.arg(0).arg(-1).value) self.assertEqual(m.f._fcn_id, m.c.e.arg(0).arg(-1).value) self.assertEqual(value(m.c.e), 10) # save / restore should not change the fcn_id _fcn_id = m.f._fcn_id m.f.__setstate__(m.f.__getstate__()) self.assertEqual(m.f._fcn_id, _fcn_id) self.assertIsNot(m.b.e, m.c.e) self.assertIsNot(m.b.e.arg(0), m.c.e.arg(0)) self.assertEqual(m.f._fcn_id, m.b.e.arg(0).arg(-1).value) self.assertEqual(m.f._fcn_id, m.c.e.arg(0).arg(-1).value) self.assertEqual(value(m.c.e), 10)
def test_call_sumfcn(self): m = ConcreteModel() m.f = ExternalFunction(_sum) self.assertIsInstance(m.f, PythonCallbackFunction) self.assertEqual(value(m.f()), 2.0) self.assertEqual(value(m.f(1)), 3.0) self.assertEqual(value(m.f(1, 2)), 5.0)
def test_eval_fgh_gsl_function(self): DLL = find_GSL() if not DLL: self.skipTest("Could not find the amplgsl.dll library") model = ConcreteModel() model.gamma = ExternalFunction( library=DLL, function="gsl_sf_gamma") model.bessel = ExternalFunction( library=DLL, function="gsl_sf_bessel_Jnu") f,g,h = model.gamma.evaluate_fgh((2.0,)) self.assertAlmostEqual(f, 1.0, 7) self.assertListsAlmostEqual(g, [0.422784335098467], 7) self.assertListsAlmostEqual(h, [0.8236806608528794], 7) f,g,h = model.bessel.evaluate_fgh((2.5, 2.0,), fixed=[1,0]) self.assertAlmostEqual(f, 0.223924531469, 7) self.assertListsAlmostEqual(g, [0.0, 0.21138811435101745], 7) self.assertListsAlmostEqual(h, [0.0, 0.0, 0.02026349177575621], 7)
def test_eval_gsl_error(self): DLL = find_GSL() if not DLL: self.skipTest("Could not find the amplgsl.dll library") model = ConcreteModel() model.bogus = ExternalFunction(library=DLL, function="bogus_function") with self.assertRaisesRegex( RuntimeError, "Error: external function 'bogus_function' was " "not registered within external library(?s:.*)gsl_sf_gamma"): f = model.bogus.evaluate((1, ))
def test_unknown_library(self): m = ConcreteModel() with LoggingIntercept() as LOG: m.ef = ExternalFunction( library='unknown_pyomo_external_testing_function', function='f') self.assertEqual( LOG.getvalue(), 'Defining AMPL external function, but cannot locate ' 'specified library "unknown_pyomo_external_testing_function"\n')
def test_solve_gsl_function(self): DLL = find_GSL() if not DLL: self.skipTest("Could not find the amplgsl.dll library") model = ConcreteModel() model.z_func = ExternalFunction(library=DLL, function="gsl_sf_gamma") model.x = Var(initialize=3, bounds=(1e-5, None)) model.o = Objective(expr=model.z_func(model.x)) opt = SolverFactory('ipopt') res = opt.solve(model, tee=True) self.assertAlmostEqual(value(model.o), 0.885603194411, 7)
def test_solve_gsl_function_const_arg(self): DLL = find_GSL() if not DLL: self.skipTest("Could not find the amplgsl.dll library") model = ConcreteModel() model.z_func = ExternalFunction(library=DLL, function="gsl_sf_beta") model.x = Var(initialize=1, bounds=(0.1, None)) model.o = Objective(expr=-model.z_func(1, model.x)) opt = SolverFactory('ipopt') res = opt.solve(model, tee=True) self.assertAlmostEqual(value(model.x), 0.1, 5)
def test_getname(self): m = ConcreteModel() m.f = ExternalFunction(library="junk.so", function="junk") self.assertIsInstance(m.f, AMPLExternalFunction) self.assertEqual(m.f.name, "f") self.assertEqual(m.f.local_name, "f") self.assertEqual(m.f.getname(), "f") self.assertEqual(m.f.getname(True), "f") M = ConcreteModel() M.m = m self.assertEqual(M.m.f.name, "m.f") self.assertEqual(M.m.f.local_name, "f") self.assertEqual(M.m.f.getname(), "f") self.assertEqual(M.m.f.getname(True), "m.f")
def test_getname(self): m = ConcreteModel() m.f = ExternalFunction(_sum) self.assertIsInstance(m.f, PythonCallbackFunction) self.assertEqual(m.f.name, "f") self.assertEqual(m.f.local_name, "f") self.assertEqual(m.f.getname(), "f") self.assertEqual(m.f.getname(True), "f") M = ConcreteModel() M.m = m self.assertEqual(M.m.f.name, "m.f") self.assertEqual(M.m.f.local_name, "f") self.assertEqual(M.m.f.getname(), "f") self.assertEqual(M.m.f.getname(True), "m.f")
def test_evaluate_fgh_f_g_h(self): m = ConcreteModel() m.f = ExternalFunction(_f, _g, _h) f, g, h = m.f.evaluate_fgh((5, 7, 11, m.f._fcn_id)) self.assertEqual(f, 5**2 + 3 * 5 * 7 + 5 * 7 * 11**2) self.assertEqual( g, [2 * 5 + 3 * 7 + 7 * 11**2, 3 * 5 + 5 * 11**2, 2 * 5 * 7 * 11, 0]) self.assertEqual(h, [ 2, 3 + 11**2, 0, 2 * 7 * 11, 2 * 5 * 11, 2 * 5 * 7, 0, 0, 0, 0, ]) f, g, h = m.f.evaluate_fgh((5, 7, 11, m.f._fcn_id), fixed=[0, 1, 0, 1]) self.assertEqual(f, 5**2 + 3 * 5 * 7 + 5 * 7 * 11**2) self.assertEqual(g, [2 * 5 + 3 * 7 + 7 * 11**2, 0, 2 * 5 * 7 * 11, 0]) self.assertEqual(h, [ 2, 0, 0, 2 * 7 * 11, 0, 2 * 5 * 7, 0, 0, 0, 0, ]) f, g, h = m.f.evaluate_fgh((5, 7, 11, m.f._fcn_id), fgh=1) self.assertEqual(f, 5**2 + 3 * 5 * 7 + 5 * 7 * 11**2) self.assertEqual( g, [2 * 5 + 3 * 7 + 7 * 11**2, 3 * 5 + 5 * 11**2, 2 * 5 * 7 * 11, 0]) self.assertIsNone(h) f, g, h = m.f.evaluate_fgh((5, 7, 11, m.f._fcn_id), fgh=0) self.assertEqual(f, 5**2 + 3 * 5 * 7 + 5 * 7 * 11**2) self.assertIsNone(g) self.assertIsNone(h)
def test_evaluate_fgh_f(self): m = ConcreteModel() m.f = ExternalFunction(_f) with self.assertRaisesRegex( RuntimeError, "ExternalFunction 'f' was not defined " "with a gradient callback."): f, g, h = m.f.evaluate_fgh((5, 7, 11, m.f._fcn_id)) with self.assertRaisesRegex( RuntimeError, "ExternalFunction 'f' was not defined " "with a gradient callback."): f, g, h = m.f.evaluate_fgh((5, 7, 11, m.f._fcn_id), fgh=1) f, g, h = m.f.evaluate_fgh((5, 7, 11, m.f._fcn_id), fgh=0) self.assertEqual(f, 5**2 + 3 * 5 * 7 + 5 * 7 * 11**2) self.assertIsNone(g) self.assertIsNone(h)
def test_clone(self): m = ConcreteModel() m.f = ExternalFunction(_sum) m.x = Var(initialize=3) m.y = Var(initialize=5) m.e = Expression(expr=m.f(m.x, m.y)) self.assertIsInstance(m.f, PythonCallbackFunction) self.assertEqual(m.f._fcn_id, m.e.arg(0).arg(-1).value) self.assertEqual(value(m.e), 10) i = m.clone() self.assertIsInstance(i.f, PythonCallbackFunction) self.assertIsNot(i.f, m.f) self.assertIsNot(i.e, m.e) self.assertIsNot(i.e.arg(0), m.e.arg(0)) self.assertEqual(i.f._fcn_id, i.e.arg(0).arg(-1).value) self.assertNotEqual(i.f._fcn_id, m.f._fcn_id) self.assertEqual(value(i.e), 10)
def test_evaluate_fgh_f_g(self): m = ConcreteModel() m.f = ExternalFunction(_f, _g) with self.assertRaisesRegex( RuntimeError, "ExternalFunction 'f' was not defined " "with a Hessian callback."): f, g, h = m.f.evaluate_fgh((5, 7, 11, m.f._fcn_id)) f, g, h = m.f.evaluate_fgh((5, 7, 11, m.f._fcn_id), fgh=1) self.assertEqual(f, 5**2 + 3 * 5 * 7 + 5 * 7 * 11**2) self.assertEqual( g, [2 * 5 + 3 * 7 + 7 * 11**2, 3 * 5 + 5 * 11**2, 2 * 5 * 7 * 11, 0]) self.assertIsNone(h) f, g, h = m.f.evaluate_fgh((5, 7, 11, m.f._fcn_id), fgh=0) self.assertEqual(f, 5**2 + 3 * 5 * 7 + 5 * 7 * 11**2) self.assertIsNone(g) self.assertIsNone(h)
def test_evaluate_errors(self): m = ConcreteModel() m.f = ExternalFunction(_f, _g_bad, _h_bad) f = m.f.evaluate((5, 7, 11, m.f._fcn_id)) self.assertEqual(f, 5**2 + 3 * 5 * 7 + 5 * 7 * 11**2) with self.assertRaisesRegex( RuntimeError, "PythonCallbackFunction called with invalid Global ID"): f = m.f.evaluate((5, 7, 11, -1)) with self.assertRaisesRegex( RuntimeError, "External function 'f' returned an invalid " r"derivative vector \(expected 4, received 5\)"): f = m.f.evaluate_fgh((5, 7, 11, m.f._fcn_id), fgh=1) with self.assertRaisesRegex( RuntimeError, "External function 'f' returned an invalid " r"Hessian matrix \(expected 10, received 9\)"): f = m.f.evaluate_fgh((5, 7, 11, m.f._fcn_id), fgh=2)
def add_local_external_functions(block): ef_exprs = [] for comp in block.component_data_objects( (Constraint, Expression), active=True ): ef_exprs.extend(identify_external_functions(comp.expr)) unique_functions = [] fcn_set = set() for expr in ef_exprs: fcn = expr._fcn data = (fcn._library, fcn._function) if data not in fcn_set: fcn_set.add(data) unique_functions.append(data) fcn_comp_map = {} for lib, name in unique_functions: comp_name = unique_component_name(block, "_" + name) comp = ExternalFunction(library=lib, function=name) block.add_component(comp_name, comp) fcn_comp_map[lib, name] = comp return fcn_comp_map
def test_clone_gsl_function(self): DLL = find_GSL() if not DLL: self.skipTest("Could not find the amplgsl.dll library") m = ConcreteModel() m.z_func = ExternalFunction(library=DLL, function="gsl_sf_gamma") self.assertIsInstance(m.z_func, AMPLExternalFunction) m.x = Var(initialize=3, bounds=(1e-5, None)) m.o = Objective(expr=m.z_func(m.x)) opt = SolverFactory('ipopt') # Test a simple clone... model2 = m.clone() res = opt.solve(model2, tee=True) self.assertAlmostEqual(value(model2.o), 0.885603194411, 7) # Trigger the library to be loaded. This tests that the CDLL # objects that are created when the SO/DLL are loaded do not # interfere with cloning the model. self.assertAlmostEqual(value(m.o), 2) model3 = m.clone() res = opt.solve(model3, tee=True) self.assertAlmostEqual(value(model3.o), 0.885603194411, 7)
def test_extra_kwargs(self): m = ConcreteModel() with self.assertRaises(ValueError): m.f = ExternalFunction(_count, this_should_raise_error='foo')
def test_eval_fgh_gsl_function(self): DLL = find_GSL() if not DLL: self.skipTest("Could not find the amplgsl.dll library") model = ConcreteModel() model.gamma = ExternalFunction(library=DLL, function="gsl_sf_gamma") model.beta = ExternalFunction(library=DLL, function="gsl_sf_beta") model.bessel = ExternalFunction(library=DLL, function="gsl_sf_bessel_Jnu") f, g, h = model.gamma.evaluate_fgh((2.0, )) self.assertAlmostEqual(f, 1.0, 7) self.assertListsAlmostEqual(g, [0.422784335098467], 7) self.assertListsAlmostEqual(h, [0.8236806608528794], 7) f, g, h = model.beta.evaluate_fgh(( 2.5, 2.0, ), fixed=[1, 1]) self.assertAlmostEqual(f, 0.11428571428571432, 7) self.assertListsAlmostEqual(g, [0.0, 0.0], 7) self.assertListsAlmostEqual(h, [0.0, 0.0, 0.0], 7) f, g, h = model.beta.evaluate_fgh(( 2.5, 2.0, ), fixed=[0, 1]) self.assertAlmostEqual(f, 0.11428571428571432, 7) self.assertListsAlmostEqual(g, [-0.07836734693877555, 0.0], 7) self.assertListsAlmostEqual(h, [0.08135276967930034, 0.0, 0.0], 7) f, g, h = model.beta.evaluate_fgh(( 2.5, 2.0, )) self.assertAlmostEqual(f, 0.11428571428571432, 7) self.assertListsAlmostEqual( g, [-0.07836734693877555, -0.11040989614412142], 7) self.assertListsAlmostEqual( h, [0.08135276967930034, 0.0472839170086535, 0.15194654464270113], 7) f, g, h = model.beta.evaluate_fgh(( 2.5, 2.0, ), fgh=1) self.assertAlmostEqual(f, 0.11428571428571432, 7) self.assertListsAlmostEqual( g, [-0.07836734693877555, -0.11040989614412142], 7) self.assertIsNone(h) f, g, h = model.beta.evaluate_fgh(( 2.5, 2.0, ), fgh=0) self.assertAlmostEqual(f, 0.11428571428571432, 7) self.assertIsNone(g) self.assertIsNone(h) f, g, h = model.bessel.evaluate_fgh(( 2.5, 2.0, ), fixed=[1, 0]) self.assertAlmostEqual(f, 0.223924531469, 7) self.assertListsAlmostEqual(g, [0.0, 0.21138811435101745], 7) self.assertListsAlmostEqual(h, [0.0, 0.0, 0.02026349177575621], 7) # Note: Not all AMPL-GSL functions honor the fixed flag # (notably, gamma and bessel do not as of 12/2021). We will # test that our interface corrects that f, g, h = model.gamma.evaluate_fgh((2.0, ), fixed=[1]) self.assertAlmostEqual(f, 1.0, 7) self.assertListsAlmostEqual(g, [0.0], 7) self.assertListsAlmostEqual(h, [0.0], 7) f, g, h = model.bessel.evaluate_fgh(( 2.5, 2.0, ), fixed=[1, 1]) self.assertAlmostEqual(f, 0.223924531469, 7) self.assertListsAlmostEqual(g, [0.0, 0.0], 7) self.assertListsAlmostEqual(h, [0.0, 0.0, 0.0], 7)