def test_add_sensitivity_data(self): model = make_indexed_model() sens = SensitivityInterface(model, clone_model=False) sens._add_data_block() param_list = [model.x, model.eta] with self.assertRaises(ValueError) as exc: sens._add_sensitivity_data(param_list) self.assertIn("variables must be fixed", str(exc.exception)) sens.model_instance.x.fix() # NOTE: sending the same component twice probably shouldn't # be supported. It is convenient for testing, however. param_list = [model.x, model.x[1], model.eta, model.eta[1]] sens._add_sensitivity_data(param_list) block_param_list = list(sens.block.component_data_objects(Param)) block_var_list = list(sens.block.component_data_objects(Var)) self.assertEqual(len(block_param_list), 4) self.assertEqual(len(block_var_list), 3) self.assertEqual(len(sens.block._sens_data_list), 7) pred_sens_data_list = [ (model.x[1], Param, 0, 1), (model.x[2], Param, 0, 2), (model.x[3], Param, 0, 3), (model.x[1], Param, 1, _NotAnIndex), (Var, model.eta[1], 2, 1), (Var, model.eta[2], 2, 2), (Var, model.eta[1], 3, _NotAnIndex), ] for data, pred in zip(sens.block._sens_data_list, pred_sens_data_list): if isinstance(pred[0], ComponentData): self.assertIs(data[0], pred[0]) self.assertIs(data[1].ctype, pred[1]) name = data[0].parent_component().local_name self.assertTrue( data[1].parent_component().local_name.startswith(name)) else: self.assertIs(data[0].ctype, pred[0]) self.assertIs(data[1], pred[1]) name = data[1].parent_component().local_name self.assertTrue( data[0].parent_component().local_name.startswith(name)) self.assertEqual(data[2], pred[2]) self.assertEqual(data[3], pred[3])
def test_expression_replacement_no_replacement(self): model = make_indexed_model() sens = SensitivityInterface(model, clone_model=False) sens._add_data_block() instance = sens.model_instance block = sens.block instance.x.fix() param_list = [instance.x[1], instance.x[2], instance.x[3]] sens._add_sensitivity_data(param_list) self.assertEqual(len(block.constList), 0) variable_sub_map = {} sens._replace_parameters_in_constraints(variable_sub_map) self.assertEqual(len(block.constList), 2) # Rely on order of constraints here... Fine as long as # component_data_objects iteration is deterministic pred_const_list = [instance.const[1], instance.const[2]] for orig, replaced in zip(pred_const_list, block.constList.values()): self.assertEqual(orig.expr.to_string(), replaced.expr.to_string()) self.assertFalse(orig.active) self.assertTrue(replaced.active)
def test_expression_replacement_ranged_inequality(self): model = make_model_with_ranged_inequalities() sens = SensitivityInterface(model, clone_model=False) sens._add_data_block() instance = sens.model_instance block = sens.block instance.x.fix() param_list = [instance.eta[1], instance.eta[2]] sens._add_sensitivity_data(param_list) orig_components = ( list(instance.component_data_objects(Constraint, active=True)) + list(instance.component_data_objects(Objective, active=True))) orig_expr = [con.expr for con in orig_components] # These will be modified to account for expected replacements expected_variables = ComponentMap( (con, ComponentSet(identify_variables(con.expr))) for con in orig_components) expected_parameters = ComponentMap( (con, ComponentSet(identify_mutable_parameters(con.expr))) for con in orig_components) # As constructed by the `setup_sensitivity` method: variable_sub_map = dict( (id(param), var) for var, param, list_idx, _ in block._sens_data_list if param_list[list_idx].ctype is Param) # Sanity check self.assertEqual(len(variable_sub_map), 2) # Map each param to the var that should replace it param_var_map = ComponentMap( (param, var) for var, param, _, _ in block._sens_data_list) # Remove parameters we expect to replace and add vars # we expect to replace with. for con in orig_components: for param in param_var_map: if param in expected_parameters[con]: expected_variables[con].add(param_var_map[param]) expected_parameters[con].remove(param) # We check that the new components (Constraints and Objectives) contain # the expected parameters and variables. replaced = sens._replace_parameters_in_constraints(variable_sub_map) # With ranged inequalities, we end up with more constraints than we # started with: self.assertEqual(len(block.constList), 3) for con in block.constList.values(): self.assertTrue(con.active) param_set = ComponentSet(identify_mutable_parameters(con.expr)) var_set = ComponentSet(identify_variables(con.expr)) orig_con = replaced[con] self.assertIsNot(orig_con, con) # Note that for ranged inequalities, it is not valid to check # that the two sets are equal as a mutable parameter could be # contained in only one "sub-inequality" self.assertIsSubset(param_set, expected_parameters[orig_con]) self.assertEqual(var_set, expected_variables[orig_con]) self.assertIs(block.cost.ctype, Objective) obj = block.cost param_set = ComponentSet(identify_mutable_parameters(obj.expr)) var_set = ComponentSet(identify_variables(obj.expr)) orig_obj = replaced[obj] self.assertIsNot(orig_obj, obj) self.assertEqual(param_set, expected_parameters[orig_obj]) self.assertEqual(var_set, expected_variables[orig_obj]) # Original components were deactivated but otherwise not altered for con, expr in zip(orig_components, orig_expr): self.assertFalse(con.active) #self.assertIs(con.expr, expr) # ^Why does this fail? self.assertEqual(con.expr.to_string(), expr.to_string())