def calculate_duals(self): """Get duals of the current model :return: self.duals :rtype: dict """ # For testing - very slow and should not be used! if self.kkt_method == 'pynumero': nlp = PyomoNLP(self.model_object) varList = nlp.get_pyomo_variables() conList = nlp.get_pyomo_constraints() duals = nlp.get_duals() J = nlp.extract_submatrix_jacobian(pyomo_variables=varList, pyomo_constraints=conList) H = nlp.extract_submatrix_hessian_lag(pyomo_variables_rows=varList, pyomo_variables_cols=varList) J = csc_matrix(J) var_index_names = [v.name for v in varList] con_index_names = [v.name for v in conList] dummy_constraints = [ f'{self.global_constraint_name}[{k}]' for k in self.parameter_set ] jac_row_ind = [con_index_names.index(d) for d in dummy_constraints] duals_imp = [duals[i] for i in jac_row_ind] self.duals = dict(zip(self.parameter_set, duals_imp)) if self.verbose: print(f'The pynumero results are:') print(self.duals) else: self.duals = { key: self.model_object.dual[getattr( self.model_object, self.global_constraint_name)[key]] for key, val in getattr(self.model_object, self.global_param_name).items() } if self.verbose: print('The duals are:') print(self.duals) self.delete_sol_files() return self.duals
def _get_kkt_info(self, model): """Takes the model and uses PyNumero to get the jacobian and Hessian information as dataframes Args: model (pyomo ConcreteModel): A pyomo model instance of the current problem (used in calculating the reduced Hessian) Returns: KKT (pd.DataFrame): the KKT matrix as a dataframe H_df (pd.DataFrame): the Hessian as a dataframe J_df (pd.DataFrame): the jacobian as a dataframe var_index_names (list): the index of variables con_index_names (list): the index of constraints """ nlp = PyomoNLP(model) varList = nlp.get_pyomo_variables() conList = nlp.get_pyomo_constraints() J = nlp.extract_submatrix_jacobian(pyomo_variables=varList, pyomo_constraints=conList) H = nlp.extract_submatrix_hessian_lag(pyomo_variables_rows=varList, pyomo_variables_cols=varList) var_index_names = [v.name for v in varList] con_index_names = [v.name for v in conList] J_df = pd.DataFrame(J.todense(), columns=var_index_names, index=con_index_names) H_df = pd.DataFrame(H.todense(), columns=var_index_names, index=var_index_names) var_index_names = pd.DataFrame(var_index_names) KKT_up = pd.merge(H_df, J_df.transpose(), left_index=True, right_index=True) KKT = pd.concat((KKT_up, J_df)) KKT = KKT.fillna(0) return KKT, H_df, J_df, var_index_names, con_index_names
def _get_kkt_matrix(model): """This uses pynumero to get the Hessian and Jacobian in order to build the KKT matrix Args: model (pyomo ConcreteModel): the current model used in the inner problem optimization Returns: KKT (pandas.DataFrame): KKT matrix for the current iteration var_index_names (list): list of variable names con_index_names (list): list of constraint names """ nlp = PyomoNLP(model) varList = nlp.get_pyomo_variables() conList = nlp.get_pyomo_constraints() duals = nlp.get_duals() J = nlp.extract_submatrix_jacobian(pyomo_variables=varList, pyomo_constraints=conList) H = nlp.extract_submatrix_hessian_lag(pyomo_variables_rows=varList, pyomo_variables_cols=varList) var_index_names = [v.name for v in varList] con_index_names = [v.name for v in conList] J_df = pd.DataFrame(J.todense(), columns=var_index_names, index=con_index_names) H_df = pd.DataFrame(H.todense(), columns=var_index_names, index=var_index_names) var_index_names = pd.DataFrame(var_index_names) KKT_up = pd.merge(H_df, J_df.transpose(), left_index=True, right_index=True) KKT = pd.concat((KKT_up, J_df)) KKT = KKT.fillna(0) return KKT, var_index_names, con_index_names
def constraint_autoscale_large_jac(m, ignore_constraint_scaling=False, ignore_variable_scaling=False, max_grad=100, min_scale=1e-6, no_scale=False): """Automatically scale constraints based on the Jacobian. This function immitates Ipopt's default constraint scaling. This scales constraints down to avoid extremely large values in the Jacobian Args: m: model to scale ignore_constraint_scaling: ignore existing constraint scaling ignore_variable_scaling: ignore existing variable scaling max_grad: maximum value in Jacobian after scaling, subject to minimum scaling factor restriction. min_scale: minimum scaling factor allowed, keeps constraints from being scaled too much. no_scale: just calculate the Jacobian and scaled Jacobian, don't scale anything """ # Pynumero requires an objective, but I don't, so let's see if we have one n_obj = 0 for c in m.component_data_objects(pyo.Objective, active=True): n_obj += 1 # Add an objective if there isn't one if n_obj == 0: dummy_objective_name = unique_component_name(m, "objective") setattr(m, dummy_objective_name, pyo.Objective(expr=0)) # Create NLP and calculate the objective nlp = PyomoNLP(m) jac = nlp.evaluate_jacobian().tocsr() # Get lists of varibles and constraints to translate Jacobian indexes clist = nlp.get_pyomo_constraints() vlist = nlp.get_pyomo_variables() # Create a scaled Jacobian to account for variable scaling, for now ignore # constraint scaling jac_scaled = jac.copy() for i in range(len(clist)): for j in jac_scaled[i].indices: v = vlist[j] if ignore_variable_scaling: sv = 1 else: sv = get_scaling_factor(v, default=1) jac_scaled[i, j] = jac_scaled[i, j] / sv # calculate constraint scale factors for i in range(len(clist)): c = clist[i] sc = get_scaling_factor(c, default=1) if not no_scale: if (ignore_constraint_scaling or get_scaling_factor(c) is None): row = jac_scaled[i] for d in row.indices: row[0, d] = abs(row[0, d]) mg = row.max() if mg > max_grad: sc = max(min_scale, max_grad / mg) set_scaling_factor(c, sc) for j in jac_scaled[i].indices: # update the scaled jacobian jac_scaled[i, j] = jac_scaled[i, j] * sc # delete dummy objective if n_obj == 0: delattr(m, dummy_objective_name) return jac, jac_scaled, nlp
nlp = PyomoNLP(m) x = nlp.init_primals() y = compute_init_lam(nlp, x=x) nlp.set_primals(x) nlp.set_duals(y) J = nlp.evaluate_jacobian() H = nlp.evaluate_hessian_lag() M = BlockSymMatrix(2) M[0, 0] = H M[1, 0] = J Np = BlockMatrix(2, 1) Np[0, 0] = nlp.extract_submatrix_hessian_lag( pyomo_variables_rows=nlp.get_pyomo_variables(), pyomo_variables_cols=[m.eta1, m.eta2]) Np[1, 0] = nlp.extract_submatrix_jacobian( pyomo_variables=[m.eta1, m.eta2], pyomo_constraints=nlp.get_pyomo_constraints()) ds = spsolve(M.tocsc(), Np.tocsc()) print(nlp.variable_names()) ################################################################# p0 = np.array([pyo.value(m.nominal_eta1), pyo.value(m.nominal_eta2)]) p = np.array([4.45, 1.05]) dp = p - p0 dx = ds.dot(dp)[0:nlp.n_primals()] new_x = x + dx
def get_kkt_info(self): """Takes the model and uses PyNumero to get the jacobian and Hessian information as dataframes Args: model_object (pyomo ConcreteModel): A pyomo model instance of the current problem (used in calculating the reduced Hessian) method (str): defaults to k_aug, method by which to obtain optimization results Returns: kkt_data (dict): dictionary with the following structure: { 'J': J, # Jacobian 'H': H, # Hessian 'var_ind': var_index_names, # Variable index 'con_ind': con_index_names, # Constraint index 'duals': duals, # Duals } """ self.get_file_info() if self.kkt_method == 'pynumero': nlp = PyomoNLP(self.model_object) varList = nlp.get_pyomo_variables() conList = nlp.get_pyomo_constraints() duals = nlp.get_duals() J = nlp.extract_submatrix_jacobian(pyomo_variables=varList, pyomo_constraints=conList) H = nlp.extract_submatrix_hessian_lag(pyomo_variables_rows=varList, pyomo_variables_cols=varList) J = csc_matrix(J) var_index_names = [v.name for v in varList] con_index_names = [v.name for v in conList] elif self.kkt_method == 'k_aug': kaug = SolverFactory('k_aug') kaug.options["deb_kkt"] = "" kaug.solve(self.model_object, tee=False) hess = pd.read_csv('hess_debug.in', delim_whitespace=True, header=None, skipinitialspace=True) hess.columns = ['irow', 'jcol', 'vals'] hess.irow -= 1 hess.jcol -= 1 os.unlink('hess_debug.in') jac = pd.read_csv('jacobi_debug.in', delim_whitespace=True, header=None, skipinitialspace=True) m = jac.iloc[0, 0] n = jac.iloc[0, 1] jac.drop(index=[0], inplace=True) jac.columns = ['irow', 'jcol', 'vals'] jac.irow -= 1 jac.jcol -= 1 os.unlink('jacobi_debug.in') #try: # duals = read_duals(stub + '.sol') #except: duals = None J = coo_matrix((jac.vals, (jac.irow, jac.jcol)), shape=(m, n)) Hess_coo = coo_matrix((hess.vals, (hess.irow, hess.jcol)), shape=(n, n)) H = Hess_coo + triu(Hess_coo, 1).T var_index_names = pd.read_csv(self.sol_files['col'], sep=';', header=None) # dummy sep con_index_names = pd.read_csv(self.sol_files['row'], sep=';', header=None) # dummy sep var_index_names = [var_name for var_name in var_index_names[0]] con_index_names = [ con_name for con_name in con_index_names[0].iloc[:-1] ] con_index_number = {v: k for k, v in enumerate(con_index_names)} self.delete_sol_files() self.kkt_data = { 'J': J, 'H': H, 'var_ind': var_index_names, 'con_ind': con_index_names, 'duals': duals, } return None
def test_indices_methods(self): nlp = PyomoNLP(self.pm) # get_pyomo_variables variables = nlp.get_pyomo_variables() expected_ids = [id(self.pm.x[i]) for i in range(1, 10)] ids = [id(variables[i]) for i in range(9)] self.assertTrue(expected_ids == ids) variable_names = nlp.variable_names() expected_names = [self.pm.x[i].getname() for i in range(1, 10)] self.assertTrue(variable_names == expected_names) # get_pyomo_constraints constraints = nlp.get_pyomo_constraints() expected_ids = [id(self.pm.c[i]) for i in range(1, 10)] ids = [id(constraints[i]) for i in range(9)] self.assertTrue(expected_ids == ids) constraint_names = nlp.constraint_names() expected_names = [c.getname() for c in nlp.get_pyomo_constraints()] self.assertTrue(constraint_names == expected_names) # get_pyomo_equality_constraints eq_constraints = nlp.get_pyomo_equality_constraints() # 2 and 6 are the equality constraints eq_indices = [2, 6] # "indices" here is a bit overloaded expected_eq_ids = [id(self.pm.c[i]) for i in eq_indices] eq_ids = [id(con) for con in eq_constraints] self.assertEqual(eq_ids, expected_eq_ids) eq_constraint_names = nlp.equality_constraint_names() expected_eq_names = [ c.getname(fully_qualified=True) for c in nlp.get_pyomo_equality_constraints() ] self.assertEqual(eq_constraint_names, expected_eq_names) # get_pyomo_inequality_constraints ineq_constraints = nlp.get_pyomo_inequality_constraints() # 1, 3, 4, 5, 7, 8, and 9 are the inequality constraints ineq_indices = [1, 3, 4, 5, 7, 8, 9] expected_ineq_ids = [id(self.pm.c[i]) for i in ineq_indices] ineq_ids = [id(con) for con in ineq_constraints] self.assertEqual(eq_ids, expected_eq_ids) # get_primal_indices expected_primal_indices = [i for i in range(9)] self.assertTrue( expected_primal_indices == nlp.get_primal_indices([self.pm.x])) expected_primal_indices = [0, 3, 8, 4] variables = [self.pm.x[1], self.pm.x[4], self.pm.x[9], self.pm.x[5]] self.assertTrue( expected_primal_indices == nlp.get_primal_indices(variables)) # get_constraint_indices expected_constraint_indices = [i for i in range(9)] self.assertTrue(expected_constraint_indices == nlp.get_constraint_indices([self.pm.c])) expected_constraint_indices = [0, 3, 8, 4] constraints = [self.pm.c[1], self.pm.c[4], self.pm.c[9], self.pm.c[5]] self.assertTrue(expected_constraint_indices == nlp.get_constraint_indices(constraints)) # get_equality_constraint_indices pyomo_eq_indices = [2, 6] with self.assertRaises(KeyError): # At least one data object in container is not an equality nlp.get_equality_constraint_indices([self.pm.c]) eq_constraints = [self.pm.c[i] for i in pyomo_eq_indices] expected_eq_indices = [0, 1] # ^indices in the list of equality constraints eq_constraint_indices = nlp.get_equality_constraint_indices( eq_constraints) self.assertEqual(expected_eq_indices, eq_constraint_indices) # get_inequality_constraint_indices pyomo_ineq_indices = [1, 3, 4, 5, 7, 9] with self.assertRaises(KeyError): # At least one data object in container is not an equality nlp.get_inequality_constraint_indices([self.pm.c]) ineq_constraints = [self.pm.c[i] for i in pyomo_ineq_indices] expected_ineq_indices = [0, 1, 2, 3, 4, 6] # ^indices in the list of equality constraints; didn't include 8 ineq_constraint_indices = nlp.get_inequality_constraint_indices( ineq_constraints) self.assertEqual(expected_ineq_indices, ineq_constraint_indices) # extract_subvector_grad_objective expected_gradient = np.asarray( [2 * sum((i + 1) * (j + 1) for j in range(9)) for i in range(9)], dtype=np.float64) grad_obj = nlp.extract_subvector_grad_objective([self.pm.x]) self.assertTrue(np.array_equal(expected_gradient, grad_obj)) expected_gradient = np.asarray([ 2 * sum((i + 1) * (j + 1) for j in range(9)) for i in [0, 3, 8, 4] ], dtype=np.float64) variables = [self.pm.x[1], self.pm.x[4], self.pm.x[9], self.pm.x[5]] grad_obj = nlp.extract_subvector_grad_objective(variables) self.assertTrue(np.array_equal(expected_gradient, grad_obj)) # extract_subvector_constraints expected_con = np.asarray( [45, 88, 3 * 45, 4 * 45, 5 * 45, 276, 7 * 45, 8 * 45, 9 * 45], dtype=np.float64) con = nlp.extract_subvector_constraints([self.pm.c]) self.assertTrue(np.array_equal(expected_con, con)) expected_con = np.asarray([45, 4 * 45, 9 * 45, 5 * 45], dtype=np.float64) constraints = [self.pm.c[1], self.pm.c[4], self.pm.c[9], self.pm.c[5]] con = nlp.extract_subvector_constraints(constraints) self.assertTrue(np.array_equal(expected_con, con)) # extract_submatrix_jacobian expected_jac = [[(i) * (j) for j in range(1, 10)] for i in range(1, 10)] expected_jac = np.asarray(expected_jac, dtype=np.float64) jac = nlp.extract_submatrix_jacobian(pyomo_variables=[self.pm.x], pyomo_constraints=[self.pm.c]) dense_jac = jac.todense() self.assertTrue(np.array_equal(dense_jac, expected_jac)) expected_jac = [[(i) * (j) for j in [1, 4, 9, 5]] for i in [2, 6, 4]] expected_jac = np.asarray(expected_jac, dtype=np.float64) variables = [self.pm.x[1], self.pm.x[4], self.pm.x[9], self.pm.x[5]] constraints = [self.pm.c[2], self.pm.c[6], self.pm.c[4]] jac = nlp.extract_submatrix_jacobian(pyomo_variables=variables, pyomo_constraints=constraints) dense_jac = jac.todense() self.assertTrue(np.array_equal(dense_jac, expected_jac)) # extract_submatrix_hessian_lag expected_hess = [[2.0 * i * j for j in range(1, 10)] for i in range(1, 10)] expected_hess = np.asarray(expected_hess, dtype=np.float64) hess = nlp.extract_submatrix_hessian_lag( pyomo_variables_rows=[self.pm.x], pyomo_variables_cols=[self.pm.x]) dense_hess = hess.todense() self.assertTrue(np.array_equal(dense_hess, expected_hess)) expected_hess = [[2.0 * i * j for j in [1, 4, 9, 5]] for i in [1, 4, 9, 5]] expected_hess = np.asarray(expected_hess, dtype=np.float64) variables = [self.pm.x[1], self.pm.x[4], self.pm.x[9], self.pm.x[5]] hess = nlp.extract_submatrix_hessian_lag( pyomo_variables_rows=variables, pyomo_variables_cols=variables) dense_hess = hess.todense() self.assertTrue(np.array_equal(dense_hess, expected_hess))
def test_indices_methods(self): nlp = PyomoNLP(self.pm) # get_pyomo_variables variables = nlp.get_pyomo_variables() expected_ids = [id(self.pm.x[i]) for i in range(1, 10)] ids = [id(variables[i]) for i in range(9)] self.assertTrue(expected_ids == ids) variable_names = nlp.variable_names() expected_names = [self.pm.x[i].getname() for i in range(1, 10)] self.assertTrue(variable_names == expected_names) # get_pyomo_constraints constraints = nlp.get_pyomo_constraints() expected_ids = [id(self.pm.c[i]) for i in range(1, 10)] ids = [id(constraints[i]) for i in range(9)] self.assertTrue(expected_ids == ids) constraint_names = nlp.constraint_names() expected_names = [c.getname() for c in nlp.get_pyomo_constraints()] self.assertTrue(constraint_names == expected_names) # get_primal_indices expected_primal_indices = [i for i in range(9)] self.assertTrue( expected_primal_indices == nlp.get_primal_indices([self.pm.x])) expected_primal_indices = [0, 3, 8, 4] variables = [self.pm.x[1], self.pm.x[4], self.pm.x[9], self.pm.x[5]] self.assertTrue( expected_primal_indices == nlp.get_primal_indices(variables)) # get_constraint_indices expected_constraint_indices = [i for i in range(9)] self.assertTrue(expected_constraint_indices == nlp.get_constraint_indices([self.pm.c])) expected_constraint_indices = [0, 3, 8, 4] constraints = [self.pm.c[1], self.pm.c[4], self.pm.c[9], self.pm.c[5]] self.assertTrue(expected_constraint_indices == nlp.get_constraint_indices(constraints)) # extract_subvector_grad_objective expected_gradient = np.asarray( [2 * sum((i + 1) * (j + 1) for j in range(9)) for i in range(9)], dtype=np.float64) grad_obj = nlp.extract_subvector_grad_objective([self.pm.x]) self.assertTrue(np.array_equal(expected_gradient, grad_obj)) expected_gradient = np.asarray([ 2 * sum((i + 1) * (j + 1) for j in range(9)) for i in [0, 3, 8, 4] ], dtype=np.float64) variables = [self.pm.x[1], self.pm.x[4], self.pm.x[9], self.pm.x[5]] grad_obj = nlp.extract_subvector_grad_objective(variables) self.assertTrue(np.array_equal(expected_gradient, grad_obj)) # extract_subvector_constraints expected_con = np.asarray( [45, 88, 3 * 45, 4 * 45, 5 * 45, 276, 7 * 45, 8 * 45, 9 * 45], dtype=np.float64) con = nlp.extract_subvector_constraints([self.pm.c]) self.assertTrue(np.array_equal(expected_con, con)) expected_con = np.asarray([45, 4 * 45, 9 * 45, 5 * 45], dtype=np.float64) constraints = [self.pm.c[1], self.pm.c[4], self.pm.c[9], self.pm.c[5]] con = nlp.extract_subvector_constraints(constraints) self.assertTrue(np.array_equal(expected_con, con)) # extract_submatrix_jacobian expected_jac = [[(i) * (j) for j in range(1, 10)] for i in range(1, 10)] expected_jac = np.asarray(expected_jac, dtype=np.float64) jac = nlp.extract_submatrix_jacobian(pyomo_variables=[self.pm.x], pyomo_constraints=[self.pm.c]) dense_jac = jac.todense() self.assertTrue(np.array_equal(dense_jac, expected_jac)) expected_jac = [[(i) * (j) for j in [1, 4, 9, 5]] for i in [2, 6, 4]] expected_jac = np.asarray(expected_jac, dtype=np.float64) variables = [self.pm.x[1], self.pm.x[4], self.pm.x[9], self.pm.x[5]] constraints = [self.pm.c[2], self.pm.c[6], self.pm.c[4]] jac = nlp.extract_submatrix_jacobian(pyomo_variables=variables, pyomo_constraints=constraints) dense_jac = jac.todense() self.assertTrue(np.array_equal(dense_jac, expected_jac)) # extract_submatrix_hessian_lag expected_hess = [[2.0 * i * j for j in range(1, 10)] for i in range(1, 10)] expected_hess = np.asarray(expected_hess, dtype=np.float64) hess = nlp.extract_submatrix_hessian_lag( pyomo_variables_rows=[self.pm.x], pyomo_variables_cols=[self.pm.x]) dense_hess = hess.todense() self.assertTrue(np.array_equal(dense_hess, expected_hess)) expected_hess = [[2.0 * i * j for j in [1, 4, 9, 5]] for i in [1, 4, 9, 5]] expected_hess = np.asarray(expected_hess, dtype=np.float64) variables = [self.pm.x[1], self.pm.x[4], self.pm.x[9], self.pm.x[5]] hess = nlp.extract_submatrix_hessian_lag( pyomo_variables_rows=variables, pyomo_variables_cols=variables) dense_hess = hess.todense() self.assertTrue(np.array_equal(dense_hess, expected_hess))
def test_model1(self): model = create_model1() nlp = PyomoNLP(model) cynlp = CyIpoptNLP(nlp) # test x_init expected_xinit = np.asarray([4.0, 4.0, 4.0], dtype=np.float64) xinit = cynlp.x_init() self.assertTrue(np.array_equal(xinit, expected_xinit)) # test x_lb expected_xlb = list() for v in nlp.get_pyomo_variables(): if v.lb == None: expected_xlb.append(-np.inf) else: expected_xlb.append(v.lb) expected_xlb = np.asarray(expected_xlb) xlb = cynlp.x_lb() self.assertTrue(np.array_equal(xlb, expected_xlb)) # test x_ub expected_xub = list() for v in nlp.get_pyomo_variables(): if v.ub == None: expected_xub.append(np.inf) else: expected_xub.append(v.ub) expected_xub = np.asarray(expected_xub) xub = cynlp.x_ub() self.assertTrue(np.array_equal(xub, expected_xub)) # test g_lb expected_glb = np.asarray([-np.inf, 0.0], dtype=np.float64) glb = cynlp.g_lb() self.assertTrue(np.array_equal(glb, expected_glb)) # test g_ub expected_gub = np.asarray([18, 0.0], dtype=np.float64) gub = cynlp.g_ub() print(expected_gub) print(gub) self.assertTrue(np.array_equal(gub, expected_gub)) x = cynlp.x_init() # test objective self.assertEqual(cynlp.objective(x), -504) # test gradient expected = np.asarray([-576, 8, 64], dtype=np.float64) self.assertTrue(np.allclose(expected, cynlp.gradient(x))) # test constraints expected = np.asarray([20, -5], dtype=np.float64) constraints = cynlp.constraints(x) self.assertTrue(np.allclose(expected, constraints)) # test jacobian expected = np.asarray([[8.0, 0, 1.0],[0.0, 8.0, 1.0]]) spexpected = spa.coo_matrix(expected).todense() rows, cols = cynlp.jacobianstructure() values = cynlp.jacobian(x) jac = spa.coo_matrix((values, (rows,cols)), shape=(len(constraints), len(x))).todense() self.assertTrue(np.allclose(spexpected, jac)) # test hessian y = constraints.copy() y.fill(1.0) rows, cols = cynlp.hessianstructure() values = cynlp.hessian(x, y, obj_factor=1.0) hess_lower = spa.coo_matrix((values, (rows,cols)), shape=(len(x), len(x))).todense() expected_hess_lower = np.asarray([[-286.0, 0.0, 0.0], [0.0, 4.0, 0.0], [-144.0, 0.0, 192.0]], dtype=np.float64) self.assertTrue(np.allclose(expected_hess_lower, hess_lower))
def get_kkt_info(self): """Takes the model and uses PyNumero or k_aug to get the jacobian and Hessian information as dataframes. This is done in place and does not return anything. kkt_data (dict): dictionary with the following structure: { 'J': J, # Jacobian 'H': H, # Hessian 'var_ind': var_index_names, # Variable index 'con_ind': con_index_names, # Constraint index 'duals': duals, # Duals } :return: None """ self.get_file_info() if self.kkt_method == 'pynumero': nlp = PyomoNLP(self.model_object) varList = nlp.get_pyomo_variables() conList = nlp.get_pyomo_constraints() duals = nlp.get_duals() J = nlp.extract_submatrix_jacobian(pyomo_variables=varList, pyomo_constraints=conList) H = nlp.extract_submatrix_hessian_lag(pyomo_variables_rows=varList, pyomo_variables_cols=varList) J = csc_matrix(J) var_index_names = [v.name for v in varList] con_index_names = [v.name for v in conList] elif self.kkt_method == 'k_aug': kaug = SolverFactory('k_aug') kaug.options["print_kkt"] = "" kaug.solve(self.model_object, tee=True) kaug_files = Path('GJH') var_index_names = pd.read_csv(self.sol_files['col'], sep=';', header=None) # dummy sep con_index_names = pd.read_csv(self.sol_files['row'], sep=';', header=None) # dummy sep var_index_names = [var_name for var_name in var_index_names[0]] con_index_names = [ con_name for con_name in con_index_names[0].iloc[:-1] ] # con_index_number = {v: k for k, v in enumerate(con_index_names)} n = len(var_index_names) m = len(con_index_names) print(f'size: vars: {n}, cons {m}') hess_file = kaug_files.joinpath('H_print.txt') hess = pd.read_csv(hess_file, delim_whitespace=True, header=None, skipinitialspace=True) hess.columns = ['irow', 'jcol', 'vals'] hess.irow -= 1 hess.jcol -= 1 # os.unlink(f'{kaug_files}hess_debug.in') jac_file = kaug_files.joinpath('A_print.txt') jac = pd.read_csv(jac_file, delim_whitespace=True, header=None, skipinitialspace=True) jac.columns = ['irow', 'jcol', 'vals'] jac.irow -= 1 jac.jcol -= 1 # os.unlink(f'{kaug_files}jacobi_debug.in') # try: # duals = read_duals(stub + '.sol') # except: duals = None J = coo_matrix((jac.vals, (jac.jcol, jac.irow)), shape=(m, n)) Hess_coo = coo_matrix((hess.vals, (hess.irow, hess.jcol)), shape=(n, n)) H = Hess_coo + triu(Hess_coo, 1).T print('This sizes of H and J') print(H.shape) print(J.shape) self.delete_sol_files() self.kkt_data = { 'J': J, 'H': H, 'var_ind': var_index_names, 'con_ind': con_index_names, 'duals': duals, } return None
class PyomoExternalCyIpoptProblem(CyIpoptProblemInterface): def __init__(self, pyomo_model, ex_input_output_model, inputs, outputs, outputs_eqn_scaling=None): """ Create an instance of this class to pass as a problem to CyIpopt. Parameters ---------- pyomo_model : ConcreteModel The ConcreteModel representing the Pyomo part of the problem. This model must contain Pyomo variables for the inputs and the outputs. ex_input_output_model : ExternalInputOutputModel An instance of a derived class (from ExternalInputOutputModel) that provides the methods to compute the outputs and the derivatives. inputs : list of Pyomo variables (_VarData) The Pyomo model needs to have variables to represent the inputs to the external model. This is the list of those input variables in the order that corresponds to the input_values vector provided in the set_inputs call. outputs : list of Pyomo variables (_VarData) The Pyomo model needs to have variables to represent the outputs from the external model. This is the list of those output variables in the order that corresponds to the numpy array returned from the evaluate_outputs call. outputs_eqn_scaling : list or array-like or None This sets the value of scaling parameters for the additional output equations that are generated. No scaling is done if this is set to None. """ self._pyomo_model = pyomo_model self._ex_io_model = ex_input_output_model # verify that the inputs and outputs were passed correctly self._inputs = [v for v in inputs] for v in self._inputs: if not isinstance(v, _VarData): raise RuntimeError('Argument inputs passed to PyomoExternalCyIpoptProblem must be' ' a list of VarData objects. Note: if you have an indexed variable, pass' ' each index as a separate entry in the list (e.g., inputs=[m.x[1], m.x[2]]).') self._outputs = [v for v in outputs] for v in self._outputs: if not isinstance(v, _VarData): raise RuntimeError('Argument outputs passed to PyomoExternalCyIpoptProblem must be' ' a list of VarData objects. Note: if you have an indexed variable, pass' ' each index as a separate entry in the list (e.g., inputs=[m.x[1], m.x[2]]).') # we need to add a dummy variable and constraint to the pyomo_nlp # to make sure it does not remove variables that do not # appear in the pyomo part of the model - also ensure unique name in case model # is used in more than one instance of this class # ToDo: Improve this by convincing Pyomo not to remove the inputs and outputs dummy_var_name = unique_component_name(self._pyomo_model, '_dummy_variable_CyIpoptPyomoExNLP') dummy_var = Var() setattr(self._pyomo_model, dummy_var_name, dummy_var) dummy_con_name = unique_component_name(self._pyomo_model, '_dummy_constraint_CyIpoptPyomoExNLP') dummy_con = Constraint( expr = getattr(self._pyomo_model, dummy_var_name) == \ sum(v for v in self._inputs) + sum(v for v in self._outputs) ) setattr(self._pyomo_model, dummy_con_name, dummy_con) # initialize the dummy var to the right hand side dummy_var_value = 0 for v in self._inputs: if v.value is not None: dummy_var_value += value(v) for v in self._outputs: if v.value is not None: dummy_var_value += value(v) dummy_var.value = dummy_var_value # make an nlp interface from the pyomo model self._pyomo_nlp = PyomoNLP(self._pyomo_model) # create initial value vectors for primals and duals init_primals = self._pyomo_nlp.init_primals() init_duals_pyomo = self._pyomo_nlp.init_duals() if np.any(np.isnan(init_duals_pyomo)): # set initial values to 1 for any entries that we don't get # (typically, all are set, or none are set) init_duals_pyomo[np.isnan(init_duals_pyomo)] = 1.0 init_duals_ex = np.ones(len(self._outputs), dtype=np.float64) init_duals = BlockVector(2) init_duals.set_block(0, init_duals_pyomo) init_duals.set_block(1, init_duals_ex) # build the map from inputs and outputs to the full x vector self._input_columns = self._pyomo_nlp.get_primal_indices(self._inputs) #self._input_x_mask = np.zeros(self._pyomo_nlp.n_primals(), dtype=np.float64) #self._input_x_mask[self._input_columns] = 1.0 self._output_columns = self._pyomo_nlp.get_primal_indices(self._outputs) #self._output_x_mask = np.zeros(self._pyomo_nlp.n_primals(), dtype=np.float64) #self._output_x_mask[self._output_columns] = 1.0 # create caches for primals and duals self._cached_primals = init_primals.copy() self._cached_duals = init_duals.clone(copy=True) self._cached_obj_factor = 1.0 # set the initial values for the pyomo primals and duals self._pyomo_nlp.set_primals(self._cached_primals) self._pyomo_nlp.set_duals(self._cached_duals.get_block(0)) # set the initial values for the external inputs ex_inputs = self._ex_io_inputs_from_full_primals(self._cached_primals) self._ex_io_model.set_inputs(ex_inputs) # create the lower and upper bounds for the complete problem pyomo_nlp_con_lb = self._pyomo_nlp.constraints_lb() ex_con_lb = np.zeros(len(self._outputs), dtype=np.float64) self._gL = np.concatenate((pyomo_nlp_con_lb, ex_con_lb)) pyomo_nlp_con_ub = self._pyomo_nlp.constraints_ub() ex_con_ub = np.zeros(len(self._outputs), dtype=np.float64) self._gU = np.concatenate((pyomo_nlp_con_ub, ex_con_ub)) # create the scaling parameters if they are provided self._obj_scaling = self._pyomo_nlp.get_obj_scaling() self._primals_scaling = self._pyomo_nlp.get_primals_scaling() pyomo_constraints_scaling = self._pyomo_nlp.get_constraints_scaling() self._constraints_scaling = None # check if we need constraint scaling, and if so, add in the # outputs_eqn_scaling if pyomo_constraints_scaling is not None or outputs_eqn_scaling is not None: if pyomo_constraints_scaling is None: pyomo_constraints_scaling = np.ones(self._pyomo_nlp.n_primals(), dtype=np.float64) if outputs_eqn_scaling is None: outputs_eqn_scaling = np.ones(len(self._outputs), dtype=np.float64) if type(outputs_eqn_scaling) is list: outputs_eqn_scaling = np.asarray(outputs_eqn_scaling, dtype=np.float64) self._constraints_scaling = np.concatenate((pyomo_constraints_scaling, outputs_eqn_scaling)) ### setup the jacobian structures self._jac_pyomo = self._pyomo_nlp.evaluate_jacobian() # We will be mapping the dense external jacobian (doutputs/dinputs) # to the correct columns from the full x vector ex_start_row = self._pyomo_nlp.n_constraints() jac_ex = self._ex_io_model.evaluate_derivatives() # the jacobian returned from the external model is in the # space of the external model only. We need to shift # the rows down and shift the columns appropriately jac_ex_irows = np.copy(jac_ex.row) jac_ex_irows += ex_start_row jac_ex_jcols = np.copy(jac_ex.col) for z,col in enumerate(jac_ex_jcols): jac_ex_jcols[z] = self._input_columns[col] jac_ex_data = np.copy(jac_ex.data) # CDL: this code was for the dense version of evaluate_derivatives # for i in range(len(self._outputs)): # for j in range(len(self._inputs)): # jac_ex_irows.append(ex_start_row + i) # jac_ex_jcols.append(self._input_columns[j]) # jac_ex_data.append(jac_ex[i,j]) jac_ex_output_irows = list() jac_ex_output_jcols = list() jac_ex_output_data = list() # add the jac for output variables from the extra equations for i in range(len(self._outputs)): jac_ex_output_irows.append(ex_start_row + i) jac_ex_output_jcols.append(self._output_columns[i]) jac_ex_output_data.append(-1.0) self._full_jac_irows = np.concatenate((self._jac_pyomo.row, jac_ex_irows, jac_ex_output_irows)) self._full_jac_jcols = np.concatenate((self._jac_pyomo.col, jac_ex_jcols, jac_ex_output_jcols)) self._full_jac_data = np.concatenate((self._jac_pyomo.data, jac_ex_data, jac_ex_output_data)) # currently, this interface does not do anything with Hessians def load_x_into_pyomo(self, primals): """ Use this method to load a numpy array of values into the corresponding Pyomo variables (e.g., the solution from CyIpopt) Parameters ---------- primals : numpy array The array of values that will be given to the Pyomo variables. The order of this array is the same as the order in the PyomoNLP created internally. """ pyomo_variables = self._pyomo_nlp.get_pyomo_variables() for i,v in enumerate(primals): pyomo_variables[i].set_value(v) def _set_primals_if_necessary(self, primals): if not np.array_equal(primals, self._cached_primals): self._pyomo_nlp.set_primals(primals) ex_inputs = self._ex_io_inputs_from_full_primals(primals) self._ex_io_model.set_inputs(ex_inputs) self._cached_primals = primals.copy() def _set_duals_if_necessary(self, duals): if not np.array_equal(duals, self._cached_duals): self._cached_duals.copy_from(duals) self._pyomo_nlp.set_duals(self._cached_duals.get_block(0)) def _set_obj_factor_if_necessary(self, obj_factor): if obj_factor != self._cached_obj_factor: self._pyomo_nlp.set_obj_factor(obj_factor) self._cached_obj_factor = obj_factor def x_init(self): return self._pyomo_nlp.init_primals() def x_lb(self): return self._pyomo_nlp.primals_lb() def x_ub(self): return self._pyomo_nlp.primals_ub() def g_lb(self): return self._gL.copy() def g_ub(self): return self._gU.copy() def scaling_factors(self): return self._obj_scaling, self._primals_scaling, self._constraints_scaling def objective(self, primals): self._set_primals_if_necessary(primals) return self._pyomo_nlp.evaluate_objective() def gradient(self, primals): self._set_primals_if_necessary(primals) return self._pyomo_nlp.evaluate_grad_objective() def constraints(self, primals): self._set_primals_if_necessary(primals) pyomo_constraints = self._pyomo_nlp.evaluate_constraints() ex_io_outputs = self._ex_io_model.evaluate_outputs() ex_io_constraints = ex_io_outputs - self._ex_io_outputs_from_full_primals(primals) constraints = BlockVector(2) constraints.set_block(0, pyomo_constraints) constraints.set_block(1, ex_io_constraints) return constraints.flatten() def jacobianstructure(self): return self._full_jac_irows, self._full_jac_jcols def jacobian(self, primals): self._set_primals_if_necessary(primals) self._pyomo_nlp.evaluate_jacobian(out=self._jac_pyomo) pyomo_data = self._jac_pyomo.data ex_io_deriv = self._ex_io_model.evaluate_derivatives() # CDL: dense version: ex_io_deriv = self._ex_io_model.evaluate_derivatives().flatten('C') self._full_jac_data[0:len(pyomo_data)] = pyomo_data self._full_jac_data[len(pyomo_data):len(pyomo_data)+len(ex_io_deriv.data)] = ex_io_deriv.data # CDL: dense version: self._full_jac_data[len(pyomo_data):len(pyomo_data)+len(ex_io_deriv)] = ex_io_deriv # the -1s for the output variables should still be here return self._full_jac_data def hessianstructure(self): return np.zeros(0), np.zeros(0) #raise NotImplementedError('No Hessians for now') def hessian(self, x, y, obj_factor): raise NotImplementedError('No Hessians for now') def _ex_io_inputs_from_full_primals(self, primals): return primals[self._input_columns] #return np.compress(self._input_x_mask, primals) def _ex_io_outputs_from_full_primals(self, primals): return primals[self._output_columns] #return np.compress(self._output_x_mask, primals)
def load_solution(m: pe.ConcreteModel(), nlp: PyomoNLP): primals = nlp.get_primals() pyomo_vars = nlp.get_pyomo_variables() for v, val in zip(pyomo_vars, primals): v.value = val