def try_factorization_and_reallocation(kkt, linear_solver, reallocation_factor, max_iter, timer=None): if timer is None: timer = HierarchicalTimer() assert max_iter >= 1 for count in range(max_iter): timer.start('symbolic') """ Performance could be improved significantly by only performing symbolic factorization once. However, we first have to make sure the nonzero structure (and ordering of row and column arrays) of the KKT matrix never changes. We have not had time to test this thoroughly, yet. """ res = linear_solver.do_symbolic_factorization(matrix=kkt, raise_on_error=False) timer.stop('symbolic') if res.status == LinearSolverStatus.successful: timer.start('numeric') res = linear_solver.do_numeric_factorization(matrix=kkt, raise_on_error=False) timer.stop('numeric') status = res.status if status == LinearSolverStatus.not_enough_memory: linear_solver.increase_memory_allocation(reallocation_factor) else: break return status, count
def solve(self, model, timer: HierarchicalTimer = None): avail = self.available() if not avail: raise PyomoException( f'Solver {self.__class__} is not available ({avail}).') if self._last_results_object is not None: self._last_results_object.solution_loader.invalidate() if timer is None: timer = HierarchicalTimer() try: TempfileManager.push() if self.config.filename is None: self._filename = TempfileManager.create_tempfile() else: self._filename = self.config.filename TempfileManager.add_tempfile(self._filename + '.lp', exists=False) TempfileManager.add_tempfile(self._filename + '.log', exists=False) timer.start('write lp file') self._writer.write(model, self._filename + '.lp', timer=timer) timer.stop('write lp file') res = self._apply_solver(timer) self._last_results_object = res if self.config.report_timing: logger.info('\n' + str(timer)) return res finally: # finally, clean any temporary files registered with the # temp file manager, created/populated *directly* by this # plugin. TempfileManager.pop(remove=not self.config.keepfiles) if not self.config.keepfiles: self._filename = None
def solve(self, model, timer: HierarchicalTimer = None): self.available(exception_flag=True) if timer is None: timer = HierarchicalTimer() try: TempfileManager.push() if self.config.filename is None: self._filename = TempfileManager.create_tempfile() else: self._filename = self.config.filename TempfileManager.add_tempfile(self._filename + '.lp', exists=False) TempfileManager.add_tempfile(self._filename + '.soln', exists=False) TempfileManager.add_tempfile(self._filename + '.log', exists=False) timer.start('write lp file') self._writer.write(model, self._filename+'.lp', timer=timer) timer.stop('write lp file') res = self._apply_solver(timer) if self.config.report_timing: logger.info('\n' + str(timer)) return res finally: # finally, clean any temporary files registered with the # temp file manager, created/populated *directly* by this # plugin. TempfileManager.pop(remove=not self.config.keepfiles) if not self.config.keepfiles: self._filename = None if self.config.report_timing: print(timer)
def _apply_solver(self, timer: HierarchicalTimer): config = self.config if config.time_limit is not None: timeout = config.time_limit + min(max(1, 0.01 * config.time_limit), 100) else: timeout = None ostreams = [LogStream(level=self.config.log_level, logger=self.config.solver_output_logger)] if self.config.stream_solver: ostreams.append(sys.stdout) cmd = [str(config.executable), self._filename + '.nl', '-AMPL', 'option_file_name=' + self._filename + '.opt'] if 'option_file_name' in self.solver_options: raise ValueError('Use Ipopt.config.filename to specify the name of the options file. ' 'Do not use Ipopt.solver_options["option_file_name"].') for k, v in self.solver_options.items(): cmd.append(str(k) + '=' + str(v)) with TeeStream(*ostreams) as t: timer.start('subprocess') cp = subprocess.run(cmd, timeout=timeout, stdout=t.STDOUT, stderr=t.STDERR, universal_newlines=True) timer.stop('subprocess') if cp.returncode != 0: if self.config.load_solution: raise RuntimeError('A feasible solution was not found, so no solution can be loaded.' 'Please set opt.config.load_solution=False and check ' 'results.termination_condition and ' 'results.best_feasible_objective before loading a solution.') results = Results() results.termination_condition = TerminationCondition.error results.best_feasible_objective = None self._primal_sol = None self._dual_sol = None else: timer.start('parse solution') results = self._parse_sol() timer.stop('parse solution') if self._writer.get_active_objective() is None: results.best_objective_bound = None else: if self._writer.get_active_objective().sense == minimize: results.best_objective_bound = -math.inf else: results.best_objective_bound = math.inf return results
def evaluate_primal_dual_kkt_matrix(self, timer=None): if timer is None: timer = HierarchicalTimer() timer.start('eval hess') hessian = self._nlp.evaluate_hessian_lag() timer.stop('eval hess') timer.start('eval jac') jac_eq = self._nlp.evaluate_jacobian_eq() jac_ineq = self._nlp.evaluate_jacobian_ineq() timer.stop('eval jac') duals_primals_lb = self._duals_primals_lb duals_primals_ub = self._duals_primals_ub duals_slacks_lb = self._duals_slacks_lb duals_slacks_ub = self._duals_slacks_ub primals = self._nlp.get_primals() timer.start('hess block') data = (duals_primals_lb / (primals - self._nlp.primals_lb()) + duals_primals_ub / (self._nlp.primals_ub() - primals)) n = self._nlp.n_primals() indices = np.arange(n) hess_block = scipy.sparse.coo_matrix((data, (indices, indices)), shape=(n, n)) hess_block += hessian timer.stop('hess block') timer.start('slack block') data = (duals_slacks_lb / (self._slacks - self._nlp.ineq_lb()) + duals_slacks_ub / (self._nlp.ineq_ub() - self._slacks)) n = self._nlp.n_ineq_constraints() indices = np.arange(n) slack_block = scipy.sparse.coo_matrix((data, (indices, indices)), shape=(n, n)) timer.stop('slack block') timer.start('set block') kkt = BlockMatrix(4, 4) kkt.set_block(0, 0, hess_block) kkt.set_block(1, 1, slack_block) kkt.set_block(2, 0, jac_eq) kkt.set_block(0, 2, jac_eq.transpose()) kkt.set_block(3, 0, jac_ineq) kkt.set_block(0, 3, jac_ineq.transpose()) kkt.set_block( 3, 1, -scipy.sparse.identity(self._nlp.n_ineq_constraints(), format='coo')) kkt.set_block( 1, 3, -scipy.sparse.identity(self._nlp.n_ineq_constraints(), format='coo')) timer.stop('set block') return kkt
def main(plot=True, n_points=200): import numpy as np # create a Pyomo model m = pe.ConcreteModel() m.x = pe.Var() m.y = pe.Var() m.p = pe.Param(initialize=1, mutable=True) m.obj = pe.Objective(expr=m.x**2 + m.y**2) m.c1 = pe.Constraint(expr=m.y >= (m.x + 1)**2) m.c2 = pe.Constraint(expr=m.y >= (m.x - m.p)**2) opt = appsi.solvers.Cplex() # create an APPSI solver interface opt.config.load_solution = False # modify the config options opt.update_config.check_for_new_or_removed_vars = False # change how automatic updates are handled opt.update_config.update_vars = False # write a for loop to vary the value of parameter p from 1 to 10 p_values = [float(i) for i in np.linspace(1, 10, n_points)] obj_values = list() x_values = list() timer = HierarchicalTimer() # create a timer for some basic profiling timer.start('p loop') for p_val in p_values: m.p.value = p_val res = opt.solve(m, timer=timer) assert res.termination_condition == appsi.base.TerminationCondition.optimal obj_values.append(res.best_feasible_objective) opt.load_vars([m.x]) x_values.append(m.x.value) timer.stop('p loop') print(timer) if plot: import matplotlib.pyplot as plt # plot the results fig, ax1 = plt.subplots() ax1.set_xlabel('p') ax1.set_ylabel('objective') ax1.plot(p_values, obj_values, ':k', label='objective') ax2 = ax1.twinx() ax2.set_ylabel('x') ax2.plot(p_values, x_values, '-b', label='x') fig.legend() plt.show()
def do_back_solve(self, rhs, timer=None): """ Performs a back solve with the factorized matrix. Should only be called after do_numeric_factorixation. Parameters ---------- rhs: MPIBlockVector timer: HierarchicalTimer Returns ------- result: MPIBlockVector """ if timer is None: timer = HierarchicalTimer() timer.start('back_solve') schur_complement_rhs = np.zeros(rhs.get_block(self.block_dim - 1).size, dtype='d') for ndx in self.local_block_indices: A = self.block_matrix.get_block(self.block_dim - 1, ndx) contribution = self.subproblem_solvers[ndx].do_back_solve( rhs.get_block(ndx)) schur_complement_rhs -= A.tocsr().dot(contribution.flatten()) res = np.zeros(rhs.get_block(self.block_dim - 1).shape[0], dtype='d') comm.Allreduce(schur_complement_rhs, res) schur_complement_rhs = rhs.get_block(self.block_dim - 1) + res result = rhs.copy_structure() coupling = self.schur_complement_solver.do_back_solve( schur_complement_rhs) for ndx in self.local_block_indices: A = self.block_matrix.get_block(self.block_dim - 1, ndx) result.set_block( ndx, self.subproblem_solvers[ndx].do_back_solve( rhs.get_block(ndx) - A.tocsr().transpose().dot(coupling.flatten()))) result.set_block(self.block_dim - 1, coupling) timer.stop('back_solve') return result
def _apply_solver(self, timer: HierarchicalTimer): config = self.config timer.start('cplex read lp') self._cplex_model = cplex_model = self._cplex.Cplex() cplex_model.read(self._filename + '.lp') timer.stop('cplex read lp') log_stream = LogStream(level=self.config.log_level, logger=self.config.solver_output_logger) if config.stream_solver: def _process_stream(arg): sys.stdout.write(arg) return arg cplex_model.set_results_stream(log_stream, _process_stream) else: cplex_model.set_results_stream(log_stream) for key, option in self.cplex_options.items(): opt_cmd = cplex_model.parameters key_pieces = key.split('_') for key_piece in key_pieces: opt_cmd = getattr(opt_cmd, key_piece) opt_cmd.set(option) if config.time_limit is not None: cplex_model.parameters.timelimit.set(config.time_limit) if config.mip_gap is not None: cplex_model.parameters.mip.tolerances.mipgap.set(config.mip_gap) timer.start('cplex solve') t0 = time.time() cplex_model.solve() t1 = time.time() timer.stop('cplex solve') return self._postsolve(timer, t1 - t0)
def evaluate_primal_dual_kkt_rhs(self, timer=None): if timer is None: timer = HierarchicalTimer() timer.start('eval grad obj') grad_obj = self.get_obj_factor() * self.evaluate_grad_objective() timer.stop('eval grad obj') timer.start('eval jac') jac_eq = self._nlp.evaluate_jacobian_eq() jac_ineq = self._nlp.evaluate_jacobian_ineq() timer.stop('eval jac') timer.start('eval cons') eq_resid = self._nlp.evaluate_eq_constraints() ineq_resid = self._nlp.evaluate_ineq_constraints() - self._slacks timer.stop('eval cons') timer.start('grad_lag_primals') grad_lag_primals = ( grad_obj + jac_eq.transpose() * self._nlp.get_duals_eq() + jac_ineq.transpose() * self._nlp.get_duals_ineq() - self._barrier / (self._nlp.get_primals() - self._nlp.primals_lb()) + self._barrier / (self._nlp.primals_ub() - self._nlp.get_primals())) timer.stop('grad_lag_primals') timer.start('grad_lag_slacks') grad_lag_slacks = (-self._nlp.get_duals_ineq() - self._barrier / (self._slacks - self._nlp.ineq_lb()) + self._barrier / (self._nlp.ineq_ub() - self._slacks)) timer.stop('grad_lag_slacks') rhs = BlockVector(4) rhs.set_block(0, grad_lag_primals) rhs.set_block(1, grad_lag_slacks) rhs.set_block(2, eq_resid) rhs.set_block(3, ineq_resid) rhs = -rhs return rhs
def test_HierarchicalTimer(self): RES = 1e-2 # resolution (seconds) timer = HierarchicalTimer() start_time = time.time() timer.start('all') time.sleep(0.02) for i in range(10): timer.start('a') time.sleep(0.01) for j in range(5): timer.start('aa') time.sleep(0.001) timer.stop('aa') timer.start('ab') timer.stop('ab') timer.stop('a') end_time = time.time() timer.stop('all') ref = \ """Identifier ncalls cumtime percall % --------------------------------------------------- all 1 [0-9.]+ +[0-9.]+ +100.0 ---------------------------------------------- a 10 [0-9.]+ +[0-9.]+ +[0-9.]+ ----------------------------------------- aa 50 [0-9.]+ +[0-9.]+ +[0-9.]+ ab 10 [0-9.]+ +[0-9.]+ +[0-9.]+ other n/a [0-9.]+ +n/a +[0-9.]+ ========================================= other n/a [0-9.]+ +n/a +[0-9.]+ ============================================== =================================================== """.splitlines() for l, r in zip(str(timer).splitlines(), ref): self.assertRegex(l, r) self.assertEqual(1, timer.get_num_calls('all')) self.assertAlmostEqual(end_time - start_time, timer.get_total_time('all'), delta=RES) self.assertEqual(100., timer.get_relative_percent_time('all')) self.assertTrue(100. > timer.get_relative_percent_time('all.a')) self.assertTrue(50. < timer.get_relative_percent_time('all.a'))
def write(self, model: _BlockData, filename: str, timer: HierarchicalTimer = None): if timer is None: timer = HierarchicalTimer() if model is not self._model: timer.start('set_instance') self.set_instance(model) timer.stop('set_instance') else: timer.start('update') self.update(timer=timer) timer.stop('update') timer.start('write file') self._writer.write(filename) timer.stop('write file')
def test_HierarchicalTimer_longNames(self): RES = 0.01 # resolution (seconds) timer = HierarchicalTimer() start_time = time.perf_counter() timer.start('all' * 25) time.sleep(0.02) for i in range(10): timer.start('a' * 75) time.sleep(0.01) for j in range(5): timer.start('aa' * 20) time.sleep(0.001) timer.stop('aa' * 20) timer.start('ab' * 20) timer.stop('ab' * 20) timer.stop('a' * 75) end_time = time.perf_counter() timer.stop('all' * 25) ref = ("""Identifier%s ncalls cumtime percall %% %s------------------------------------ %s%s 1 [0-9.]+ +[0-9.]+ +100.0 %s------------------------------------ %s%s 10 [0-9.]+ +[0-9.]+ +[0-9.]+ %s------------------------------------ %s%s 50 [0-9.]+ +[0-9.]+ +[0-9.]+ %s%s 10 [0-9.]+ +[0-9.]+ +[0-9.]+ other%s n/a [0-9.]+ +n/a +[0-9.]+ %s==================================== other%s n/a [0-9.]+ +n/a +[0-9.]+ %s==================================== %s==================================== """ % (' ' * 69, '-' * 79, 'all' * 25, ' ' * 4, '-' * 75, 'a' * 75, '', '-' * 71, 'aa' * 20, ' ' * 31, 'ab' * 20, ' ' * 31, ' ' * 66, '=' * 71, ' ' * 70, '=' * 75, '=' * 79)).splitlines() for l, r in zip(str(timer).splitlines(), ref): self.assertRegex(l, r)
def write(self, model: _BlockData, filename: str, timer: HierarchicalTimer = None): if timer is None: timer = HierarchicalTimer() if model is not self._model: timer.start('set_instance') self.set_instance(model) timer.stop('set_instance') else: timer.start('update') self.update(timer=timer) for cv, v in self._solver_var_to_pyomo_var_map.items(): if v.value is not None: cv.value = v.value timer.stop('update') timer.start('write file') self._writer.write(filename) timer.stop('write file')
def _apply_solver(self, timer: HierarchicalTimer): config = self.config if config.time_limit is not None: timeout = config.time_limit + min(max(1, 0.01 * config.time_limit), 100) else: timeout = None def _check_and_escape_options(): for key, val in self.solver_options.items(): tmp_k = str(key) _bad = ' ' in tmp_k tmp_v = str(val) if ' ' in tmp_v: if '"' in tmp_v: if "'" in tmp_v: _bad = True else: tmp_v = "'" + tmp_v + "'" else: tmp_v = '"' + tmp_v + '"' if _bad: raise ValueError("Unable to properly escape solver option:" "\n\t%s=%s" % (key, val) ) yield tmp_k, tmp_v cmd = [str(config.executable)] action_options = list() if config.time_limit is not None: cmd.extend(['-sec', str(config.time_limit)]) cmd.extend(['-timeMode', 'elapsed']) for key, val in _check_and_escape_options(): if val.strip() != '': cmd.append('-'+key, val) else: action_options.append('-'+key) cmd.extend(['-printingOptions', 'all']) cmd.extend(['-import', self._filename + '.lp']) cmd.extend(action_options) cmd.extend(['-stat=1']) cmd.extend(['-solve']) cmd.extend(['-solu', self._filename + '.soln']) ostreams = [LogStream(level=self.config.log_level, logger=self.config.solver_output_logger)] if self.config.stream_solver: ostreams.append(sys.stdout) with TeeStream(*ostreams) as t: timer.start('subprocess') cp = subprocess.run(cmd, timeout=timeout, stdout=t.STDOUT, stderr=t.STDERR, universal_newlines=True) timer.stop('subprocess') if cp.returncode != 0: if self.config.load_solution: raise RuntimeError('A feasible solution was not found, so no solution can be loaded.' 'Please set opt.config.load_solution=False and check ' 'results.termination_condition and ' 'results.best_feasible_objective before loading a solution.') results = Results() results.termination_condition = TerminationCondition.error results.best_feasible_objective = None self._primal_sol = None self._dual_sol = None self._reduced_costs = None else: timer.start('parse solution') results = self._parse_soln() timer.stop('parse solution') if self._writer.get_active_objective() is None: results.best_feasible_objective = None results.best_objective_bound = None else: if self._writer.get_active_objective().sense == minimize: results.best_objective_bound = -math.inf else: results.best_objective_bound = math.inf return results
def _postsolve(self, timer: HierarchicalTimer, solve_time): config = self.config cpxprob = self._cplex_model results = CplexResults(solver=self) results.wallclock_time = solve_time status = cpxprob.solution.get_status() if status in [1, 101, 102]: results.termination_condition = TerminationCondition.optimal elif status in [2, 40, 118, 133, 134]: results.termination_condition = TerminationCondition.unbounded elif status in [4, 119, 134]: results.termination_condition = TerminationCondition.infeasibleOrUnbounded elif status in [3, 103]: results.termination_condition = TerminationCondition.infeasible elif status in [10]: results.termination_condition = TerminationCondition.maxIterations elif status in [11, 25, 107, 131]: results.termination_condition = TerminationCondition.maxTimeLimit else: results.termination_condition = TerminationCondition.unknown if self._writer.get_active_objective() is None: results.best_feasible_objective = None results.best_objective_bound = None else: if cpxprob.solution.get_solution_type( ) != cpxprob.solution.type.none: if (cpxprob.variables.get_num_binary() + cpxprob.variables.get_num_integer()) == 0: results.best_feasible_objective = cpxprob.solution.get_objective_value( ) results.best_objective_bound = cpxprob.solution.get_objective_value( ) else: results.best_feasible_objective = cpxprob.solution.get_objective_value( ) results.best_objective_bound = cpxprob.solution.MIP.get_best_objective( ) else: results.best_feasible_objective = None if cpxprob.objective.get_sense( ) == cpxprob.objective.sense.minimize: results.best_objective_bound = -math.inf else: results.best_objective_bound = math.inf if config.load_solution: if cpxprob.solution.get_solution_type( ) == cpxprob.solution.type.none: raise RuntimeError( 'A feasible solution was not found, so no solution can be loades. ' 'Please set opt.config.load_solution=False and check ' 'results.termination_condition and ' 'results.best_feasible_objective before loading a solution.' ) else: if results.termination_condition != TerminationCondition.optimal: logger.warning( 'Loading a feasible but suboptimal solution. ' 'Please set load_solution=False and check ' 'results.termination_condition before loading a solution.' ) timer.start('load solution') self.load_vars() timer.stop('load solution') return results
def update(self, timer: HierarchicalTimer = None): if timer is None: timer = HierarchicalTimer() config = self.update_config new_vars = list() old_vars = list() new_params = list() old_params = list() new_cons = list() old_cons = list() old_sos = list() new_sos = list() current_vars_dict = dict() current_cons_dict = dict() current_sos_dict = dict() timer.start('vars') if config.check_for_new_or_removed_vars or config.update_vars: current_vars_dict = {id(v): v for v in self._model.component_data_objects(Var, descend_into=True, sort=False)} for v_id, v in current_vars_dict.items(): if v_id not in self._vars: new_vars.append(v) for v_id, v_tuple in self._vars.items(): if v_id not in current_vars_dict: old_vars.append(v_tuple[0]) timer.stop('vars') timer.start('params') if config.check_for_new_or_removed_params: current_params_dict = {id(p): p for p in self._model.component_data_objects(Param, descend_into=True, sort=False)} for p_id, p in current_params_dict.items(): if p_id not in self._params: new_params.append(p) for p_id, p in self._params.items(): if p_id not in current_params_dict: old_params.append(p) timer.stop('params') timer.start('cons') if config.check_for_new_or_removed_constraints or config.update_constraints: current_cons_dict = {c: None for c in self._model.component_data_objects(Constraint, descend_into=True, active=True, sort=False)} current_sos_dict = {c: None for c in self._model.component_data_objects(SOSConstraint, descend_into=True, active=True, sort=False)} for c in current_cons_dict.keys(): if c not in self._vars_referenced_by_con: new_cons.append(c) for c in current_sos_dict.keys(): if c not in self._vars_referenced_by_con: new_sos.append(c) for c in self._vars_referenced_by_con.keys(): if c not in current_cons_dict and c not in current_sos_dict: if (c.ctype is Constraint) or (c.ctype is None and isinstance(c, _GeneralConstraintData)): old_cons.append(c) else: assert (c.ctype is SOSConstraint) or (c.ctype is None and isinstance(c, _SOSConstraintData)) old_sos.append(c) self.remove_constraints(old_cons) self.remove_sos_constraints(old_sos) timer.stop('cons') timer.start('vars') self.remove_variables(old_vars) timer.stop('vars') timer.start('params') self.remove_params(old_params) # sticking this between removal and addition # is important so that we don't do unnecessary work if config.update_params: self.update_params() self.add_params(new_params) timer.stop('params') timer.start('vars') self.add_variables(new_vars) timer.stop('vars') timer.start('cons') self.add_constraints(new_cons) self.add_sos_constraints(new_sos) new_cons_set = set(new_cons) new_sos_set = set(new_sos) new_vars_set = set(id(v) for v in new_vars) if config.update_constraints: cons_to_update = list() sos_to_update = list() for c in current_cons_dict.keys(): if c not in new_cons_set: cons_to_update.append(c) for c in current_sos_dict.keys(): if c not in new_sos_set: sos_to_update.append(c) cons_to_remove_and_add = list() for c in cons_to_update: lower, body, upper = self._active_constraints[c] if c.lower is not lower or c.body is not body or c.upper is not upper: cons_to_remove_and_add.append(c) self.remove_constraints(cons_to_remove_and_add) self.add_constraints(cons_to_remove_and_add) self.remove_sos_constraints(sos_to_update) self.add_sos_constraints(sos_to_update) timer.stop('cons') timer.start('vars') if config.update_vars: vars_to_check = list() for v_id, v in current_vars_dict.items(): if v_id not in new_vars_set: vars_to_check.append(v) vars_to_update = list() for v in vars_to_check: _v, lb, ub, fixed, domain, value = self._vars[id(v)] if lb is not v.lb: vars_to_update.append(v) elif ub is not v.ub: vars_to_update.append(v) elif fixed is not v.is_fixed(): vars_to_update.append(v) elif domain is not v.domain: vars_to_update.append(v) elif fixed and (value is not v.value): vars_to_update.append(v) self.update_variables(vars_to_update) timer.stop('vars') timer.start('named expressions') if config.update_named_expressions: cons_to_update = list() for c, expr_list in self._named_expressions.items(): if c in new_cons_set: continue for named_expr, old_expr in expr_list: if named_expr.expr is not old_expr: cons_to_update.append(c) break self.remove_constraints(cons_to_update) self.add_constraints(cons_to_update) timer.stop('named expressions') timer.start('objective') pyomo_obj = get_objective(self._model) need_to_set_objective = False if pyomo_obj is not self._objective: need_to_set_objective = True elif pyomo_obj is not None and pyomo_obj.expr is not self._objective_expr: need_to_set_objective = True elif pyomo_obj is not None and pyomo_obj.sense is not self._objective_sense: need_to_set_objective = True elif config.update_named_expressions: for named_expr, old_expr in self._obj_named_expressions: if named_expr.expr is not old_expr: need_to_set_objective = True break if need_to_set_objective: self.set_objective(pyomo_obj) timer.stop('objective')
def check_convergence(self, barrier, timer=None): """ Parameters ---------- barrier: float timer: HierarchicalTimer Returns ------- primal_inf: float dual_inf: float complimentarity_inf: float """ if timer is None: timer = HierarchicalTimer() interface = self.interface slacks = interface.get_slacks() timer.start('grad obj') grad_obj = interface.get_obj_factor() * \ interface.evaluate_grad_objective() timer.stop('grad obj') timer.start('jac eq') jac_eq = interface.evaluate_jacobian_eq() timer.stop('jac eq') timer.start('jac ineq') jac_ineq = interface.evaluate_jacobian_ineq() timer.stop('jac ineq') timer.start('eq cons') eq_resid = interface.evaluate_eq_constraints() timer.stop('eq cons') timer.start('ineq cons') ineq_resid = interface.evaluate_ineq_constraints() - slacks timer.stop('ineq cons') primals = interface.get_primals() duals_eq = interface.get_duals_eq() duals_ineq = interface.get_duals_ineq() duals_primals_lb = interface.get_duals_primals_lb() duals_primals_ub = interface.get_duals_primals_ub() duals_slacks_lb = interface.get_duals_slacks_lb() duals_slacks_ub = interface.get_duals_slacks_ub() primals_lb = interface.primals_lb() primals_ub = interface.primals_ub() primals_lb_mod = primals_lb.copy() primals_ub_mod = primals_ub.copy() primals_lb_mod[np.isneginf( primals_lb)] = 0 # these entries get multiplied by 0 primals_ub_mod[np.isinf( primals_ub)] = 0 # these entries get multiplied by 0 ineq_lb = interface.ineq_lb() ineq_ub = interface.ineq_ub() ineq_lb_mod = ineq_lb.copy() ineq_ub_mod = ineq_ub.copy() ineq_lb_mod[np.isneginf( ineq_lb)] = 0 # these entries get multiplied by 0 ineq_ub_mod[np.isinf(ineq_ub)] = 0 # these entries get multiplied by 0 timer.start('grad_lag_primals') grad_lag_primals = grad_obj + jac_eq.transpose() * duals_eq grad_lag_primals += jac_ineq.transpose() * duals_ineq grad_lag_primals -= duals_primals_lb grad_lag_primals += duals_primals_ub timer.stop('grad_lag_primals') timer.start('grad_lag_slacks') grad_lag_slacks = (-duals_ineq - duals_slacks_lb + duals_slacks_ub) timer.stop('grad_lag_slacks') timer.start('bound resids') primals_lb_resid = (primals - primals_lb_mod) * duals_primals_lb - barrier primals_ub_resid = (primals_ub_mod - primals) * duals_primals_ub - barrier primals_lb_resid[np.isneginf(primals_lb)] = 0 primals_ub_resid[np.isinf(primals_ub)] = 0 slacks_lb_resid = (slacks - ineq_lb_mod) * duals_slacks_lb - barrier slacks_ub_resid = (ineq_ub_mod - slacks) * duals_slacks_ub - barrier slacks_lb_resid[np.isneginf(ineq_lb)] = 0 slacks_ub_resid[np.isinf(ineq_ub)] = 0 timer.stop('bound resids') if eq_resid.size == 0: max_eq_resid = 0 else: max_eq_resid = np.max(np.abs(eq_resid)) if ineq_resid.size == 0: max_ineq_resid = 0 else: max_ineq_resid = np.max(np.abs(ineq_resid)) primal_inf = max(max_eq_resid, max_ineq_resid) max_grad_lag_primals = np.max(np.abs(grad_lag_primals)) if grad_lag_slacks.size == 0: max_grad_lag_slacks = 0 else: max_grad_lag_slacks = np.max(np.abs(grad_lag_slacks)) dual_inf = max(max_grad_lag_primals, max_grad_lag_slacks) if primals_lb_resid.size == 0: max_primals_lb_resid = 0 else: max_primals_lb_resid = np.max(np.abs(primals_lb_resid)) if primals_ub_resid.size == 0: max_primals_ub_resid = 0 else: max_primals_ub_resid = np.max(np.abs(primals_ub_resid)) if slacks_lb_resid.size == 0: max_slacks_lb_resid = 0 else: max_slacks_lb_resid = np.max(np.abs(slacks_lb_resid)) if slacks_ub_resid.size == 0: max_slacks_ub_resid = 0 else: max_slacks_ub_resid = np.max(np.abs(slacks_ub_resid)) complimentarity_inf = max(max_primals_lb_resid, max_primals_ub_resid, max_slacks_lb_resid, max_slacks_ub_resid) return primal_inf, dual_inf, complimentarity_inf
def solve(self, interface, timer=None, report_timing=False): """ Parameters ---------- interface: pyomo.contrib.interior_point.interface.BaseInteriorPointInterface The interior point interface. This object handles the function evaluation, building the KKT matrix, and building the KKT right hand side. timer: HierarchicalTimer report_timing: bool """ linear_solver = self.linear_solver max_iter = self.max_iter tol = self.tol if timer is None: timer = HierarchicalTimer() timer.start('IP solve') timer.start('init') self._barrier_parameter = 0.1 self.set_interface(interface) t0 = time.time() primals = interface.init_primals().copy() slacks = interface.init_slacks().copy() duals_eq = interface.init_duals_eq().copy() duals_ineq = interface.init_duals_ineq().copy() duals_primals_lb = interface.init_duals_primals_lb().copy() duals_primals_ub = interface.init_duals_primals_ub().copy() duals_slacks_lb = interface.init_duals_slacks_lb().copy() duals_slacks_ub = interface.init_duals_slacks_ub().copy() self.process_init(primals, interface.primals_lb(), interface.primals_ub()) self.process_init(slacks, interface.ineq_lb(), interface.ineq_ub()) self.process_init_duals_lb(duals_primals_lb, self.interface.primals_lb()) self.process_init_duals_ub(duals_primals_ub, self.interface.primals_ub()) self.process_init_duals_lb(duals_slacks_lb, self.interface.ineq_lb()) self.process_init_duals_ub(duals_slacks_ub, self.interface.ineq_ub()) interface.set_barrier_parameter(self._barrier_parameter) alpha_primal_max = 1 alpha_dual_max = 1 self.logger.info('{_iter:<6}' '{objective:<11}' '{primal_inf:<11}' '{dual_inf:<11}' '{compl_inf:<11}' '{barrier:<11}' '{alpha_p:<11}' '{alpha_d:<11}' '{reg:<11}' '{time:<7}'.format(_iter='Iter', objective='Objective', primal_inf='Prim Inf', dual_inf='Dual Inf', compl_inf='Comp Inf', barrier='Barrier', alpha_p='Prim Step', alpha_d='Dual Step', reg='Reg', time='Time')) reg_coef = 0 timer.stop('init') status = InteriorPointStatus.error for _iter in range(max_iter): self._iter = _iter interface.set_primals(primals) interface.set_slacks(slacks) interface.set_duals_eq(duals_eq) interface.set_duals_ineq(duals_ineq) interface.set_duals_primals_lb(duals_primals_lb) interface.set_duals_primals_ub(duals_primals_ub) interface.set_duals_slacks_lb(duals_slacks_lb) interface.set_duals_slacks_ub(duals_slacks_ub) timer.start('convergence check') primal_inf, dual_inf, complimentarity_inf = \ self.check_convergence(barrier=0, timer=timer) timer.stop('convergence check') objective = interface.evaluate_objective() self.logger.info('{_iter:<6}' '{objective:<11.2e}' '{primal_inf:<11.2e}' '{dual_inf:<11.2e}' '{compl_inf:<11.2e}' '{barrier:<11.2e}' '{alpha_p:<11.2e}' '{alpha_d:<11.2e}' '{reg:<11.2e}' '{time:<7.3f}'.format( _iter=_iter, objective=objective, primal_inf=primal_inf, dual_inf=dual_inf, compl_inf=complimentarity_inf, barrier=self._barrier_parameter, alpha_p=alpha_primal_max, alpha_d=alpha_dual_max, reg=reg_coef, time=time.time() - t0)) if max(primal_inf, dual_inf, complimentarity_inf) <= tol: status = InteriorPointStatus.optimal break timer.start('convergence check') primal_inf, dual_inf, complimentarity_inf = \ self.check_convergence( barrier=self._barrier_parameter, timer=timer) timer.stop('convergence check') if max(primal_inf, dual_inf, complimentarity_inf) \ <= 0.1 * self._barrier_parameter: # This comparison is made with barrier problem infeasibility. # Sometimes have trouble getting dual infeasibility low enough self.update_barrier_parameter() interface.set_barrier_parameter(self._barrier_parameter) timer.start('eval') timer.start('eval kkt') kkt = interface.evaluate_primal_dual_kkt_matrix(timer=timer) timer.stop('eval kkt') timer.start('eval rhs') rhs = interface.evaluate_primal_dual_kkt_rhs(timer=timer) timer.stop('eval rhs') timer.stop('eval') # Factorize linear system timer.start('factorize') reg_coef = self.factorize(kkt=kkt, timer=timer) timer.stop('factorize') timer.start('back solve') with self.linear_solve_context: self.logger.info('Iter: %s' % self._iter) delta = linear_solver.do_back_solve(rhs) timer.stop('back solve') interface.set_primal_dual_kkt_solution(delta) timer.start('frac boundary') alpha_primal_max, alpha_dual_max = \ self.fraction_to_the_boundary() timer.stop('frac boundary') delta_primals = interface.get_delta_primals() delta_slacks = interface.get_delta_slacks() delta_duals_eq = interface.get_delta_duals_eq() delta_duals_ineq = interface.get_delta_duals_ineq() delta_duals_primals_lb = interface.get_delta_duals_primals_lb() delta_duals_primals_ub = interface.get_delta_duals_primals_ub() delta_duals_slacks_lb = interface.get_delta_duals_slacks_lb() delta_duals_slacks_ub = interface.get_delta_duals_slacks_ub() primals += alpha_primal_max * delta_primals slacks += alpha_primal_max * delta_slacks duals_eq += alpha_dual_max * delta_duals_eq duals_ineq += alpha_dual_max * delta_duals_ineq duals_primals_lb += alpha_dual_max * delta_duals_primals_lb duals_primals_ub += alpha_dual_max * delta_duals_primals_ub duals_slacks_lb += alpha_dual_max * delta_duals_slacks_lb duals_slacks_ub += alpha_dual_max * delta_duals_slacks_ub timer.stop('IP solve') if report_timing: print(timer) return status