def get_hessian_of_constraint(constraint, wrt1=None, wrt2=None, nlp=None): constraints = [constraint] if wrt1 is None and wrt2 is None: variables = list( identify_variables(constraint.expr, include_fixed=False)) wrt1 = variables wrt2 = variables elif wrt1 is not None and wrt2 is not None: variables = wrt1 + wrt2 elif wrt1 is not None: # but wrt2 is None wrt2 = wrt1 variables = wrt1 else: # wrt2 is not None and wrt1 is None wrt1 = wrt2 variables = wrt1 if nlp is None: block = create_subsystem_block(constraints, variables=variables) # Could fix input_vars so I don't evaluate the Hessian with respect # to variables I don't care about... # HUGE HACK: Variables not included in a constraint are not written # to the nl file, so we cannot take the derivative with respect to # them, even though we know this derivative is zero. To work around, # we make sure all variables appear on the block in the form of a # dummy constraint. Then we can take derivatives of any constraint # with respect to them. Conveniently, the extract_submatrix_ # call deals with extracting the variables and constraint we care # about, in the proper order. block._dummy_var = Var() block._dummy_con = Constraint(expr=sum(variables) == block._dummy_var) block._obj = Objective(expr=0.0) nlp = PyomoNLP(block) saved_duals = nlp.get_duals() saved_obj_factor = nlp.get_obj_factor() temp_duals = np.zeros(len(saved_duals)) # NOTE: This makes some assumption about how the Lagrangian is constructed. # TODO: Define the convention we assume and convert if necessary. idx = nlp.get_constraint_indices(constraints)[0] temp_duals[idx] = 1.0 nlp.set_duals(temp_duals) nlp.set_obj_factor(0.0) # NOTE: The returned matrix preserves explicit zeros. I.e. it contains # coordinates for every entry that could possibly be nonzero. submatrix = nlp.extract_submatrix_hessian_lag(wrt1, wrt2) nlp.set_obj_factor(saved_obj_factor) nlp.set_duals(saved_duals) return submatrix
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_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 test_compare_evaluations(self): A1 = 5 A2 = 10 c1 = 3 c2 = 4 N = 6 dt = 1 m = create_pyomo_model(A1, A2, c1, c2, N, dt) solver = pyo.SolverFactory('ipopt') solver.options['linear_solver'] = 'mumps' status = solver.solve(m, tee=False) m_nlp = PyomoNLP(m) mex = create_pyomo_external_grey_box_model(A1, A2, c1, c2, N, dt) # mex_nlp = PyomoGreyBoxNLP(mex) mex_nlp = PyomoNLPWithGreyBoxBlocks(mex) # get the variable and constraint order and create the maps # reliable order independent comparisons m_x_order = m_nlp.primals_names() m_c_order = m_nlp.constraint_names() mex_x_order = mex_nlp.primals_names() mex_c_order = mex_nlp.constraint_names() x1list = [ 'h1[0]', 'h1[1]', 'h1[2]', 'h1[3]', 'h1[4]', 'h1[5]', 'h2[0]', 'h2[1]', 'h2[2]', 'h2[3]', 'h2[4]', 'h2[5]', 'F1[1]', 'F1[2]', 'F1[3]', 'F1[4]', 'F1[5]', 'F2[1]', 'F2[2]', 'F2[3]', 'F2[4]', 'F2[5]', 'F12[0]', 'F12[1]', 'F12[2]', 'F12[3]', 'F12[4]', 'F12[5]', 'Fo[0]', 'Fo[1]', 'Fo[2]', 'Fo[3]', 'Fo[4]', 'Fo[5]' ] x2list = [ 'egb.inputs[h1_0]', 'egb.inputs[h1_1]', 'egb.inputs[h1_2]', 'egb.inputs[h1_3]', 'egb.inputs[h1_4]', 'egb.inputs[h1_5]', 'egb.inputs[h2_0]', 'egb.inputs[h2_1]', 'egb.inputs[h2_2]', 'egb.inputs[h2_3]', 'egb.inputs[h2_4]', 'egb.inputs[h2_5]', 'egb.inputs[F1_1]', 'egb.inputs[F1_2]', 'egb.inputs[F1_3]', 'egb.inputs[F1_4]', 'egb.inputs[F1_5]', 'egb.inputs[F2_1]', 'egb.inputs[F2_2]', 'egb.inputs[F2_3]', 'egb.inputs[F2_4]', 'egb.inputs[F2_5]', 'egb.outputs[F12_0]', 'egb.outputs[F12_1]', 'egb.outputs[F12_2]', 'egb.outputs[F12_3]', 'egb.outputs[F12_4]', 'egb.outputs[F12_5]', 'egb.outputs[Fo_0]', 'egb.outputs[Fo_1]', 'egb.outputs[Fo_2]', 'egb.outputs[Fo_3]', 'egb.outputs[Fo_4]', 'egb.outputs[Fo_5]' ] x1_x2_map = dict(zip(x1list, x2list)) x1idx_x2idx_map = { i: mex_x_order.index(x1_x2_map[m_x_order[i]]) for i in range(len(m_x_order)) } c1list = [ 'h1bal[1]', 'h1bal[2]', 'h1bal[3]', 'h1bal[4]', 'h1bal[5]', 'h2bal[1]', 'h2bal[2]', 'h2bal[3]', 'h2bal[4]', 'h2bal[5]', 'F12con[0]', 'F12con[1]', 'F12con[2]', 'F12con[3]', 'F12con[4]', 'F12con[5]', 'Focon[0]', 'Focon[1]', 'Focon[2]', 'Focon[3]', 'Focon[4]', 'Focon[5]', 'min_inflow[1]', 'min_inflow[2]', 'min_inflow[3]', 'min_inflow[4]', 'min_inflow[5]', 'max_outflow[0]', 'max_outflow[1]', 'max_outflow[2]', 'max_outflow[3]', 'max_outflow[4]', 'max_outflow[5]', 'h10', 'h20' ] c2list = [ 'egb.h1bal_1', 'egb.h1bal_2', 'egb.h1bal_3', 'egb.h1bal_4', 'egb.h1bal_5', 'egb.h2bal_1', 'egb.h2bal_2', 'egb.h2bal_3', 'egb.h2bal_4', 'egb.h2bal_5', 'egb.output_constraints[F12_0]', 'egb.output_constraints[F12_1]', 'egb.output_constraints[F12_2]', 'egb.output_constraints[F12_3]', 'egb.output_constraints[F12_4]', 'egb.output_constraints[F12_5]', 'egb.output_constraints[Fo_0]', 'egb.output_constraints[Fo_1]', 'egb.output_constraints[Fo_2]', 'egb.output_constraints[Fo_3]', 'egb.output_constraints[Fo_4]', 'egb.output_constraints[Fo_5]', 'min_inflow[1]', 'min_inflow[2]', 'min_inflow[3]', 'min_inflow[4]', 'min_inflow[5]', 'max_outflow[0]', 'max_outflow[1]', 'max_outflow[2]', 'max_outflow[3]', 'max_outflow[4]', 'max_outflow[5]', 'h10', 'h20' ] c1_c2_map = dict(zip(c1list, c2list)) c1idx_c2idx_map = { i: mex_c_order.index(c1_c2_map[m_c_order[i]]) for i in range(len(m_c_order)) } # get the primals from m and put them in the correct order for mex m_x = m_nlp.get_primals() mex_x = np.zeros(len(m_x)) for i in range(len(m_x)): mex_x[x1idx_x2idx_map[i]] = m_x[i] # get the duals from m and put them in the correct order for mex m_lam = m_nlp.get_duals() mex_lam = np.zeros(len(m_lam)) for i in range(len(m_x)): mex_lam[c1idx_c2idx_map[i]] = m_lam[i] mex_nlp.set_primals(mex_x) mex_nlp.set_duals(mex_lam) m_obj = m_nlp.evaluate_objective() mex_obj = mex_nlp.evaluate_objective() self.assertAlmostEqual(m_obj, mex_obj, places=4) m_gobj = m_nlp.evaluate_grad_objective() mex_gobj = mex_nlp.evaluate_grad_objective() check_vectors_specific_order(self, m_gobj, m_x_order, mex_gobj, mex_x_order, x1_x2_map) m_c = m_nlp.evaluate_constraints() mex_c = mex_nlp.evaluate_constraints() check_vectors_specific_order(self, m_c, m_c_order, mex_c, mex_c_order, c1_c2_map) m_j = m_nlp.evaluate_jacobian() mex_j = mex_nlp.evaluate_jacobian().todense() check_sparse_matrix_specific_order(self, m_j, m_c_order, m_x_order, mex_j, mex_c_order, mex_x_order, c1_c2_map, x1_x2_map) m_h = m_nlp.evaluate_hessian_lag() mex_h = mex_nlp.evaluate_hessian_lag() check_sparse_matrix_specific_order(self, m_h, m_x_order, m_x_order, mex_h, mex_x_order, mex_x_order, x1_x2_map, x1_x2_map) mex_h = 0 * mex_h mex_nlp.evaluate_hessian_lag(out=mex_h) check_sparse_matrix_specific_order(self, m_h, m_x_order, m_x_order, mex_h, mex_x_order, mex_x_order, x1_x2_map, x1_x2_map)
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 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