def initialize(**kwds): obj = Options(**kwds) # # Set obj.available # opt = None try: opt = SolverFactory(obj.name, solver_io=obj.io) except: pass if opt is None or isinstance(opt, UnknownSolver): obj.available = False elif (obj.name == "gurobi") and (not GUROBISHELL.license_is_valid()): obj.available = False elif (obj.name == "baron") and (not BARONSHELL.license_is_valid()): obj.available = False else: obj.available = (opt.available(exception_flag=False)) and ( (not hasattr(opt, "executable")) or (opt.executable() is not None) ) # # Check capabilities # if obj.available: for _c in obj.capabilities: if not _c in opt._capabilities: raise ValueError("Solver %s does not support capability %s!" % (obj.name, _c)) # # Get version # obj.version = opt.version() return obj
def initialize(**kwds): obj = Options(**kwds) # # Set obj.available # opt = None try: opt = SolverFactory(obj.name, solver_io=obj.io) except: pass if opt is None or isinstance(opt, UnknownSolver): obj.available = False elif (obj.name == "gurobi") and \ (not GUROBISHELL.license_is_valid()): obj.available = False elif (obj.name == "baron") and \ (not BARONSHELL.license_is_valid()): obj.available = False else: obj.available = \ (opt.available(exception_flag=False)) and \ ((not hasattr(opt,'executable')) or \ (opt.executable() is not None)) # # Check capabilities # if obj.available: for _c in obj.capabilities: if not _c in opt._capabilities: raise ValueError("Solver %s does not support capability %s!" % (obj.name, _c)) # # Get version # obj.version = opt.version() return obj
def get_solvers(): """Return the solvers avaiable on the system.""" from logging import getLogger logger = getLogger('pyomo.solvers') logger_status = logger.disabled logger.disabled = True # no need for warnings: it's what we're testing! available_solvers = set() try: services = SF.services() # pyutilib version <= 5.6.3 except RuntimeError as e: services = SF # pyutilib version >= 5.6.4 for sname in services: # initial underscore ('_'): Pyomo's method to mark non-public plugins if '_' == sname[0]: continue solver = SF(sname) try: if not solver: continue except ApplicationError as e: continue if 'os' == sname: continue # Workaround current bug in Coopr if not solver.available(exception_flag=False): continue available_solvers.add(sname) logger.disabled = logger_status # put back the way it was. if available_solvers: if 'cplex' in available_solvers: default_solver = 'cplex' elif 'gurobi' in available_solvers: default_solver = 'gurobi' elif 'cbc' in available_solvers: default_solver = 'cbc' elif 'glpk' in available_solvers: default_solver = 'glpk' else: default_solver = iter(available_solvers).next() else: default_solver = 'NONE' SE.write( '\nNOTICE: Pyomo did not find any suitable solvers. Temoa will ' 'not be able to solve any models. If you need help, ask on the ' 'Temoa Project forum: http://temoaproject.org/\n\n') return (available_solvers, default_solver)
def solve_linear_subproblem(mip_model, solve_data, config): GDPopt = mip_model.GDPopt_utils initialize_subproblem(mip_model, solve_data) # Callback immediately before solving NLP subproblem config.call_before_subproblem_solve(mip_model, solve_data) mip_solver = SolverFactory(config.mip_solver) if not mip_solver.available(): raise RuntimeError("MIP solver %s is not available." % config.mip_solver) with SuppressInfeasibleWarning(): mip_args = dict(config.mip_solver_args) elapsed = get_main_elapsed_time(solve_data.timing) remaining = max(config.time_limit - elapsed, 1) if config.mip_solver == 'gams': mip_args['add_options'] = mip_args.get('add_options', []) mip_args['add_options'].append('option reslim=%s;' % remaining) elif config.mip_solver == 'multisolve': mip_args['time_limit'] = min( mip_args.get('time_limit', float('inf')), remaining) results = mip_solver.solve(mip_model, **mip_args) subprob_result = SubproblemResult() subprob_result.feasible = True subprob_result.var_values = list(v.value for v in GDPopt.variable_list) subprob_result.pyomo_results = results subprob_result.dual_values = list( mip_model.dual.get(c, None) for c in GDPopt.constraint_list) subprob_terminate_cond = results.solver.termination_condition if subprob_terminate_cond is tc.optimal: pass elif subprob_terminate_cond is tc.infeasible: config.logger.info('MIP subproblem was infeasible.') subprob_result.feasible = False else: raise ValueError('GDPopt unable to handle MIP subproblem termination ' 'condition of %s. Results: %s' % (subprob_terminate_cond, results)) # Call the NLP post-solve callback config.call_after_subproblem_solve(mip_model, solve_data) # if feasible, call the NLP post-feasible callback if subprob_result.feasible: config.call_after_subproblem_feasible(mip_model, solve_data) return subprob_result
def initialize(**kwds): obj = Options(**kwds) # # Set the limits for the solver's "demo" (unlicensed) mode: # ( nVars, nCons, nNonZeros ) obj.demo_limits = (None, None, None) if (obj.name == "baron") and \ (not BARONSHELL.license_is_valid()): obj.demo_limits = (10, 10, 50) # # # Set obj.available # opt = None try: opt = SolverFactory(obj.name, solver_io=obj.io) except: pass if opt is None or isinstance(opt, UnknownSolver): obj.available = False elif (obj.name == "gurobi") and \ (not GUROBISHELL.license_is_valid()): obj.available = False elif (obj.name == "mosek") and \ (not MosekDirect.license_is_valid()): obj.available = False else: obj.available = \ (opt.available(exception_flag=False)) and \ ((not hasattr(opt,'executable')) or \ (opt.executable() is not None)) # # Check capabilities, even if the solver is not available # if not (opt is None or isinstance(opt, UnknownSolver)): for _c in obj.capabilities: if not _c in opt._capabilities: raise ValueError("Solver %s does not support capability %s!" % (obj.name, _c)) # # Get version # if obj.available: obj.version = opt.version() return obj
def initialize(**kwds): obj = Bunch(**kwds) # # Set obj.available # try: opt = SolverFactory(obj.name, solver_io=obj.io) except: opt = None if opt is None or isinstance(opt, UnknownSolver): obj.available = False elif not opt.available(exception_flag=False): obj.available = False elif hasattr(opt, 'executable') and opt.executable() is None: obj.available = False elif not opt.license_is_valid() \ and obj.name not in licensed_solvers_with_demo_mode: obj.available = False else: obj.available = True # # Set the limits for the solver's "demo" (unlicensed) mode: # ( nVars, nCons, nNonZeros ) obj.demo_limits = (None, None, None) if obj.available: if obj.name == "baron" and not opt.license_is_valid(): obj.demo_limits = (10, 10, 50) # # Check capabilities, even if the solver is not available # if not (opt is None or isinstance(opt, UnknownSolver)): for _c in obj.capabilities: if not _c in opt._capabilities: raise ValueError("Solver %s does not support capability %s!" % (obj.name, _c)) # # Get version # if obj.available: obj.version = opt.version() return obj
def solve_linear_subproblem(mip_model, solve_data, config): GDPopt = mip_model.GDPopt_utils initialize_subproblem(mip_model, solve_data) # Callback immediately before solving NLP subproblem config.call_before_subproblem_solve(mip_model, solve_data) mip_solver = SolverFactory(config.mip_solver) if not mip_solver.available(): raise RuntimeError("MIP solver %s is not available." % config.mip_solver) with SuppressInfeasibleWarning(): results = mip_solver.solve(mip_model, **config.mip_solver_args) subprob_result = SubproblemResult() subprob_result.feasible = True subprob_result.var_values = list(v.value for v in GDPopt.variable_list) subprob_result.pyomo_results = results subprob_result.dual_values = list( mip_model.dual.get(c, None) for c in GDPopt.constraint_list) subprob_terminate_cond = results.solver.termination_condition if subprob_terminate_cond is tc.optimal: pass elif subprob_terminate_cond is tc.infeasible: config.logger.info('MIP subproblem was infeasible.') subprob_result.feasible = False else: raise ValueError('GDPopt unable to handle MIP subproblem termination ' 'condition of %s. Results: %s' % (subprob_terminate_cond, results)) # Call the NLP post-solve callback config.call_after_subproblem_solve(mip_model, solve_data) # if feasible, call the NLP post-feasible callback if subprob_result.feasible: config.call_after_subproblem_feasible(mip_model, solve_data) return subprob_result
def solve_MINLP(model, solve_data, config): """Solve the MINLP subproblem.""" config.logger.info( "Solving MINLP subproblem for fixed logical realizations.") GDPopt = model.GDPopt_utils initialize_subproblem(model, solve_data) # Callback immediately before solving MINLP subproblem config.call_before_subproblem_solve(model, solve_data) minlp_solver = SolverFactory(config.minlp_solver) if not minlp_solver.available(): raise RuntimeError("MINLP solver %s is not available." % config.minlp_solver) with SuppressInfeasibleWarning(): results = minlp_solver.solve(model, **config.minlp_solver_args) subprob_result = SubproblemResult() subprob_result.feasible = True subprob_result.var_values = list(v.value for v in GDPopt.variable_list) subprob_result.pyomo_results = results subprob_result.dual_values = list( model.dual.get(c, None) for c in GDPopt.constraint_list) term_cond = results.solver.termination_condition if any(term_cond == cond for cond in (tc.optimal, tc.locallyOptimal, tc.feasible)): pass elif term_cond == tc.infeasible: config.logger.info('MINLP subproblem was infeasible.') subprob_result.feasible = False elif term_cond == tc.maxIterations: # TODO try something else? Reinitialize with different initial # value? config.logger.info( 'MINLP subproblem failed to converge within iteration limit.') if is_feasible(model, config): config.logger.info( 'MINLP solution is still feasible. ' 'Using potentially suboptimal feasible solution.') else: subprob_result.feasible = False elif term_cond == tc.intermediateNonInteger: config.logger.info( "MINLP solver could not find feasible integer solution: %s" % results.solver.message) subprob_result.feasible = False else: raise ValueError( 'GDPopt unable to handle MINLP subproblem termination ' 'condition of %s. Results: %s' % (term_cond, results)) # Call the subproblem post-solve callback config.call_after_subproblem_solve(model, solve_data) # if feasible, call the subproblem post-feasible callback if subprob_result.feasible: config.call_after_subproblem_feasible(model, solve_data) return subprob_result
def solve_NLP(nlp_model, solve_data, config): """Solve the NLP subproblem.""" config.logger.info('Solving nonlinear subproblem for ' 'fixed binaries and logical realizations.') # Error checking for unfixed discrete variables unfixed_discrete_vars = detect_unfixed_discrete_vars(nlp_model) assert len(unfixed_discrete_vars) == 0, \ "Unfixed discrete variables exist on the NLP subproblem: {0}".format( list(v.name for v in unfixed_discrete_vars)) GDPopt = nlp_model.GDPopt_utils if config.subproblem_presolve: preprocess_subproblem(nlp_model, config) initialize_subproblem(nlp_model, solve_data) # Callback immediately before solving NLP subproblem config.call_before_subproblem_solve(nlp_model, solve_data) nlp_solver = SolverFactory(config.nlp_solver) if not nlp_solver.available(): raise RuntimeError("NLP solver %s is not available." % config.nlp_solver) with SuppressInfeasibleWarning(): results = nlp_solver.solve(nlp_model, **config.nlp_solver_args) nlp_result = SubproblemResult() nlp_result.feasible = True nlp_result.var_values = list(v.value for v in GDPopt.variable_list) nlp_result.pyomo_results = results nlp_result.dual_values = list( nlp_model.dual.get(c, None) for c in GDPopt.constraint_list) subprob_terminate_cond = results.solver.termination_condition if (subprob_terminate_cond is tc.optimal or subprob_terminate_cond is tc.locallyOptimal or subprob_terminate_cond is tc.feasible): pass elif subprob_terminate_cond is tc.infeasible: config.logger.info('NLP subproblem was infeasible.') nlp_result.feasible = False elif subprob_terminate_cond is tc.maxIterations: # TODO try something else? Reinitialize with different initial # value? config.logger.info( 'NLP subproblem failed to converge within iteration limit.') if is_feasible(nlp_model, config): config.logger.info( 'NLP solution is still feasible. ' 'Using potentially suboptimal feasible solution.') else: nlp_result.feasible = False elif subprob_terminate_cond is tc.internalSolverError: # Possible that IPOPT had a restoration failure config.logger.info("NLP solver had an internal failure: %s" % results.solver.message) nlp_result.feasible = False else: raise ValueError('GDPopt unable to handle NLP subproblem termination ' 'condition of %s. Results: %s' % (subprob_terminate_cond, results)) # Call the NLP post-solve callback config.call_after_subproblem_solve(nlp_model, solve_data) # if feasible, call the NLP post-feasible callback if nlp_result.feasible: config.call_after_subproblem_feasible(nlp_model, solve_data) return nlp_result
def sipopt(instance, paramSubList, perturbList, cloneModel=True, streamSoln=False, keepfiles=False): """This function accepts a Pyomo ConcreteModel, a list of parameters, along with their corresponding perterbation list. The model is then converted into the design structure required to call sipopt to get an approximation perturbed solution with updated bounds on the decision variable. Parameters ---------- instance: ConcreteModel pyomo model object paramSubList: list list of mutable parameters perturbList: list list of perturbed parameter values cloneModel: bool, optional indicator to clone the model. If set to False, the original model will be altered streamSoln: bool, optional indicator to stream IPOPT solution keepfiles: bool, optional preserve solver interface files Returns ------- model: ConcreteModel The model modified for use with sipopt. The returned model has three :class:`Suffix` members defined: - ``model.sol_state_1``: the approximated results at the perturbation point - ``model.sol_state_1_z_L``: the updated lower bound - ``model.sol_state_1_z_U``: the updated upper bound Raises ------ ValueError perturbList argument is expecting a List of Params ValueError length(paramSubList) must equal length(perturbList) ValueError paramSubList will not map to perturbList """ #Verify User Inputs if len(paramSubList) != len(perturbList): raise ValueError("Length of paramSubList argument does not equal " "length of perturbList") for pp in paramSubList: if pp.ctype is not Param: raise ValueError( "paramSubList argument is expecting a list of Params") for pp in paramSubList: if not pp._mutable: raise ValueError("parameters within paramSubList must be mutable") for pp in perturbList: if pp.ctype is not Param: raise ValueError( "perturbList argument is expecting a list of Params") #Add model block to compartmentalize all sipopt data b = Block() block_name = unique_component_name(instance, '_sipopt_data') instance.add_component(block_name, b) #Based on user input clone model or use orignal model for anlaysis if cloneModel: b.tmp_lists = (paramSubList, perturbList) m = instance.clone() instance.del_component(block_name) b = getattr(m, block_name) paramSubList, perturbList = b.tmp_lists del b.tmp_lists else: m = instance #Generate component maps for associating Variables to perturbations varSubList = [] for parameter in paramSubList: tempName = unique_component_name(b, parameter.local_name) b.add_component(tempName, Var(parameter.index_set())) myVar = b.component(tempName) varSubList.append(myVar) #Note: substitutions are not currently compatible with # ComponentMap [ECSA 2018/11/23], this relates to Issue #755 paramCompMap = ComponentMap(zip(paramSubList, varSubList)) variableSubMap = {} #variableSubMap = ComponentMap() paramPerturbMap = ComponentMap(zip(paramSubList, perturbList)) perturbSubMap = {} #perturbSubMap = ComponentMap() paramDataList = [] for parameter in paramSubList: # Loop over each ParamData in the Param Component # # Note: Sets are unordered in Pyomo. For this to be # deterministic, we need to sort the index (otherwise, the # ordering of things in the paramDataList may change). We use # sorted_robust to guard against mixed-type Sets in Python 3.x for kk in sorted_robust(parameter): variableSubMap[id(parameter[kk])] = paramCompMap[parameter][kk] perturbSubMap[id(parameter[kk])] = paramPerturbMap[parameter][kk] paramDataList.append(parameter[kk]) #clone Objective, add to Block, and update any Expressions for cc in list( m.component_data_objects(Objective, active=True, descend_into=True)): tempName = unique_component_name(m, cc.local_name) b.add_component( tempName, Objective(expr=ExpressionReplacementVisitor( substitute=variableSubMap, remove_named_expressions=True).dfs_postorder_stack(cc.expr))) cc.deactivate() #clone Constraints, add to Block, and update any Expressions b.constList = ConstraintList() for cc in list( m.component_data_objects(Constraint, active=True, descend_into=True)): if cc.equality: b.constList.add(expr=ExpressionReplacementVisitor( substitute=variableSubMap, remove_named_expressions=True).dfs_postorder_stack(cc.expr)) else: try: b.constList.add(expr=ExpresssionReplacementVisitor( substitute=variableSubMap, remove_named_expressions=True). dfs_postorder_stack(cc.expr)) except: # Params in either the upper or lower bounds of a ranged # inequaltiy will result in an invalid expression (you cannot # have variables in the bounds of a constraint). If we hit that # problem, we will break up the ranged inequality into separate # constraints # Note that the test for lower / upper == None is probably not # necessary, as the only way we should get here (especially if # we are more careful about the exception that we catch) is if # this is a ranged inequality and we are attempting to insert a # variable into either the lower or upper bound. if cc.lower is not None: b.constList.add(expr=ExpressionReplacementVisitor( substitute=variableSubMap, remove_named_expressions=True).dfs_postorder_stack( cc.lower) <= ExpressionReplacementVisitor( substitute=variableSubMap, remove_named_expressions=True). dfs_postorder_stack(cc.body)) #if cc.lower is not None: # b.constList.add(expr=0<=ExpressionReplacementVisitor( # substitute=variableSubMap, # remove_named_expressions=True).dfs_postorder_stack( # cc.lower) - ExpressionReplacementVisitor( # substitute=variableSubMap, # remove_named_expressions= # True).dfs_postorder_stack(cc.body) # ) if cc.upper is not None: b.constList.add(expr=ExpressionReplacementVisitor( substitute=variableSubMap, remove_named_expressions=True).dfs_postorder_stack( cc.upper) >= ExpressionReplacementVisitor( substitute=variableSubMap, remove_named_expressions=True). dfs_postorder_stack(cc.body)) cc.deactivate() #paramData to varData constraint list b.paramConst = ConstraintList() for ii in paramDataList: jj = variableSubMap[id(ii)] b.paramConst.add(ii == jj) #Create the ipopt_sens (aka sIPOPT) solver plugin using the ASL interface opt = SolverFactory('ipopt_sens', solver_io='nl') if not opt.available(False): raise ImportError('ipopt_sens is not available') #Declare Suffixes m.sens_state_0 = Suffix(direction=Suffix.EXPORT) m.sens_state_1 = Suffix(direction=Suffix.EXPORT) m.sens_state_value_1 = Suffix(direction=Suffix.EXPORT) m.sens_init_constr = Suffix(direction=Suffix.EXPORT) m.sens_sol_state_1 = Suffix(direction=Suffix.IMPORT) m.sens_sol_state_1_z_L = Suffix(direction=Suffix.IMPORT) m.sens_sol_state_1_z_U = Suffix(direction=Suffix.IMPORT) #set sIPOPT data opt.options['run_sens'] = 'yes' # for reasons that are not entirely clear, # ipopt_sens requires the indices to start at 1 kk = 1 for ii in paramDataList: m.sens_state_0[variableSubMap[id(ii)]] = kk m.sens_state_1[variableSubMap[id(ii)]] = kk m.sens_state_value_1[variableSubMap[id(ii)]] = \ value(perturbSubMap[id(ii)]) m.sens_init_constr[b.paramConst[kk]] = kk kk += 1 #Send the model to the ipopt_sens and collect the solution results = opt.solve(m, keepfiles=keepfiles, tee=streamSoln) return m
# rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ from pyomo.environ import ConcreteModel, Var, Objective, Constraint, maximize, Expression, log10 from pyomo.opt import SolverFactory, TerminationCondition from pyomo.solvers.plugins.solvers.GAMS import (GAMSShell, GAMSDirect, gdxcc_available) import pyutilib.th as unittest from pyutilib.misc import capture_output import os, shutil from tempfile import mkdtemp opt_py = SolverFactory('gams', solver_io='python') gamspy_available = opt_py.available(exception_flag=False) if gamspy_available: from gams.workspace import GamsExceptionExecution opt_gms = SolverFactory('gams', solver_io='gms') gamsgms_available = opt_gms.available(exception_flag=False) class GAMSTests(unittest.TestCase): @unittest.skipIf(not gamspy_available, "The 'gams' python bindings are not available") def test_check_expr_eval_py(self): with SolverFactory("gams", solver_io="python") as opt: m = ConcreteModel() m.x = Var()
def get_dfds_dcds(model, theta_names, tee=False, solver_options=None): """This function calculates gradient vector of the objective function and constraints with respect to the variables and parameters. e.g) min f: p1*x1+ p2*(x2^2) + p1*p2 s.t c1: x1 + x2 = p1 c2: x2 + x3 = p2 0 <= x1, x2, x3 <= 10 p1 = 10 p2 = 5 - Variables = (x1, x2, x3, p1, p2) - Fix p1 and p2 with estimated values The following terms are used to define the output dimensions: Ncon = number of constraints Nvar = number of variables (Nx + Ntheta) Nx = number of decision (primal) variables Ntheta = number of uncertain parameters. Parameters ---------- model: Pyomo ConcreteModel model should include an objective function theta_names: list of strings List of Var names tee: bool, optional Indicates that ef solver output should be teed solver_options: dict, optional Provides options to the solver (also the name of an attribute) Returns ------- gradient_f: numpy.ndarray Length Nvar array. A gradient vector of the objective function with respect to the (decision variables, parameters) at the optimal solution gradient_c: scipy.sparse.csr.csr_matrix Ncon by Nvar size sparse matrix. A Jacobian matrix of the constraints with respect to the (decision variables, parameters) at the optimal solution. Each row contains [column number, row number, and value], column order follows variable order in col and index starts from 1. Note that it follows k_aug. If no constraint exists, return [] col: list Size Nvar list of variable names row: list Size Ncon+1 list of constraints and objective function names. The final element is the objective function name. line_dic: dict column numbers of the theta_names in the model. Index starts from 1 Raises ------ RuntimeError When ipopt or k_aug or dotsens is not available Exception When ipopt fails """ # Create the solver plugin using the ASL interface ipopt = SolverFactory('ipopt', solver_io='nl') if solver_options is not None: ipopt.options = solver_options k_aug = SolverFactory('k_aug', solver_io='nl') if not ipopt.available(False): raise RuntimeError('ipopt is not available') if not k_aug.available(False): raise RuntimeError('k_aug is not available') # Declare Suffixes _add_sensitivity_suffixes(model) # K_AUG SUFFIXES model.dof_v = Suffix(direction=Suffix.EXPORT) #: SUFFIX FOR K_AUG model.rh_name = Suffix( direction=Suffix.IMPORT) #: SUFFIX FOR K_AUG AS WELL k_aug.options["print_kkt"] = "" results = ipopt.solve(model, tee=tee) # Raise exception if ipopt fails if (results.solver.status == SolverStatus.warning): raise Exception(results.solver.Message) for o in model.component_objects(Objective, active=True): f_mean = value(o) model.ipopt_zL_in.update(model.ipopt_zL_out) model.ipopt_zU_in.update(model.ipopt_zU_out) # run k_aug k_aug_interface = K_augInterface(k_aug=k_aug) k_aug_interface.k_aug(model, tee=tee) #: always call k_aug AFTER ipopt. nl_data = {} with InTempDir(): base_fname = "col_row" nl_file = ".".join((base_fname, "nl")) row_file = ".".join((base_fname, "row")) col_file = ".".join((base_fname, "col")) model.write(nl_file, io_options={"symbolic_solver_labels": True}) for fname in [nl_file, row_file, col_file]: with open(fname, "r") as fp: nl_data[fname] = fp.read() col = nl_data[col_file].strip("\n").split("\n") row = nl_data[row_file].strip("\n").split("\n") # get the column numbers of "parameters" line_dic = {name: col.index(name) for name in theta_names} grad_f_file = os.path.join("GJH", "gradient_f_print.txt") grad_f_string = k_aug_interface.data[grad_f_file] gradient_f = np.fromstring(grad_f_string, sep="\n\t") col = [ i for i in col if SensitivityInterface.get_default_block_name() not in i ] grad_c_file = os.path.join("GJH", "A_print.txt") grad_c_string = k_aug_interface.data[grad_c_file] gradient_c = np.fromstring(grad_c_string, sep="\n\t") # Jacobian file is in "COO format," i.e. an nnz-by-3 array. # Reshape to a numpy array that matches this format. gradient_c = gradient_c.reshape((-1, 3)) num_constraints = len(row) - 1 # Objective is included as a row if num_constraints > 0: row_idx = gradient_c[:, 1] - 1 col_idx = gradient_c[:, 0] - 1 data = gradient_c[:, 2] gradient_c = scipy.sparse.csr_matrix((data, (row_idx, col_idx)), shape=(num_constraints, len(col))) else: gradient_c = np.array([]) return gradient_f, gradient_c, col, row, line_dic
def solve_NLP(nlp_model, solve_data, config): """Solve the NLP subproblem.""" config.logger.info('Solving nonlinear subproblem for ' 'fixed binaries and logical realizations.') unfixed_discrete_vars = detect_unfixed_discrete_vars(nlp_model) if unfixed_discrete_vars: discrete_var_names = list(v.name for v in unfixed_discrete_vars) config.logger.warning( "Unfixed discrete variables exist on the NLP subproblem: %s" % (discrete_var_names, )) GDPopt = nlp_model.GDPopt_utils preprocessing_transformations = [ # Propagate variable bounds 'contrib.propagate_eq_var_bounds', # Detect fixed variables 'contrib.detect_fixed_vars', # Propagate fixed variables 'contrib.propagate_fixed_vars', # Remove zero terms in linear expressions 'contrib.remove_zero_terms', # Remove terms in equal to zero summations 'contrib.propagate_zero_sum', # Transform bound constraints 'contrib.constraints_to_var_bounds', # Detect fixed variables 'contrib.detect_fixed_vars', # Remove terms in equal to zero summations 'contrib.propagate_zero_sum', # Remove trivial constraints 'contrib.deactivate_trivial_constraints' ] for xfrm in preprocessing_transformations: TransformationFactory(xfrm).apply_to(nlp_model) initialize_NLP(nlp_model, solve_data) # Callback immediately before solving NLP subproblem config.call_before_subproblem_solve(nlp_model, solve_data) nlp_solver = SolverFactory(config.nlp_solver) if not nlp_solver.available(): raise RuntimeError("NLP solver %s is not available." % config.nlp_solver) with SuppressInfeasibleWarning(): results = nlp_solver.solve(nlp_model, **config.nlp_solver_args) nlp_result = SubproblemResult() nlp_result.feasible = True nlp_result.var_values = list(v.value for v in GDPopt.working_var_list) nlp_result.pyomo_results = results nlp_result.dual_values = list( nlp_model.dual.get(c, None) for c in GDPopt.working_constraints_list) subprob_terminate_cond = results.solver.termination_condition if subprob_terminate_cond is tc.optimal: pass elif subprob_terminate_cond is tc.infeasible: config.logger.info('NLP subproblem was locally infeasible.') nlp_result.feasible = False elif subprob_terminate_cond is tc.maxIterations: # TODO try something else? Reinitialize with different initial # value? config.logger.info( 'NLP subproblem failed to converge within iteration limit.') if is_feasible(nlp_model, config): config.logger.info( 'NLP solution is still feasible. ' 'Using potentially suboptimal feasible solution.') else: nlp_result.feasible = False elif subprob_terminate_cond is tc.internalSolverError: # Possible that IPOPT had a restoration failture config.logger.info("NLP solver had an internal failure: %s" % results.solver.message) nlp_result.feasible = False else: raise ValueError('GDPopt unable to handle NLP subproblem termination ' 'condition of %s. Results: %s' % (subprob_terminate_cond, results)) # Call the NLP post-solve callback config.call_after_subproblem_solve(nlp_model, solve_data) # if feasible, call the NLP post-feasible callback if nlp_result.feasible: config.call_after_subproblem_feasible(nlp_model, solve_data) return nlp_result
def solve_NLP(nlp_model, solve_data, config): """Solve the NLP subproblem.""" config.logger.info( 'Solving nonlinear subproblem for ' 'fixed binaries and logical realizations.') # Error checking for unfixed discrete variables unfixed_discrete_vars = detect_unfixed_discrete_vars(nlp_model) assert len(unfixed_discrete_vars) == 0, \ "Unfixed discrete variables exist on the NLP subproblem: {0}".format( list(v.name for v in unfixed_discrete_vars)) GDPopt = nlp_model.GDPopt_utils if config.subproblem_presolve: preprocess_subproblem(nlp_model, config) initialize_subproblem(nlp_model, solve_data) # Callback immediately before solving NLP subproblem config.call_before_subproblem_solve(nlp_model, solve_data) nlp_solver = SolverFactory(config.nlp_solver) if not nlp_solver.available(): raise RuntimeError("NLP solver %s is not available." % config.nlp_solver) with SuppressInfeasibleWarning(): results = nlp_solver.solve(nlp_model, **config.nlp_solver_args) nlp_result = SubproblemResult() nlp_result.feasible = True nlp_result.var_values = list(v.value for v in GDPopt.variable_list) nlp_result.pyomo_results = results nlp_result.dual_values = list( nlp_model.dual.get(c, None) for c in GDPopt.constraint_list) subprob_terminate_cond = results.solver.termination_condition if (subprob_terminate_cond is tc.optimal or subprob_terminate_cond is tc.locallyOptimal or subprob_terminate_cond is tc.feasible): pass elif subprob_terminate_cond is tc.infeasible: config.logger.info('NLP subproblem was infeasible.') nlp_result.feasible = False elif subprob_terminate_cond is tc.maxIterations: # TODO try something else? Reinitialize with different initial # value? config.logger.info( 'NLP subproblem failed to converge within iteration limit.') if is_feasible(nlp_model, config): config.logger.info( 'NLP solution is still feasible. ' 'Using potentially suboptimal feasible solution.') else: nlp_result.feasible = False elif subprob_terminate_cond is tc.internalSolverError: # Possible that IPOPT had a restoration failure config.logger.info( "NLP solver had an internal failure: %s" % results.solver.message) nlp_result.feasible = False else: raise ValueError( 'GDPopt unable to handle NLP subproblem termination ' 'condition of %s. Results: %s' % (subprob_terminate_cond, results)) # Call the NLP post-solve callback config.call_after_subproblem_solve(nlp_model, solve_data) # if feasible, call the NLP post-feasible callback if nlp_result.feasible: config.call_after_subproblem_feasible(nlp_model, solve_data) return nlp_result
def solve_NLP(nlp_model, solve_data, config): """Solve the NLP subproblem.""" config.logger.info('Solving nonlinear subproblem for ' 'fixed binaries and logical realizations.') # Error checking for unfixed discrete variables unfixed_discrete_vars = detect_unfixed_discrete_vars(nlp_model) assert len(unfixed_discrete_vars) == 0, \ "Unfixed discrete variables exist on the NLP subproblem: {0}".format( list(v.name for v in unfixed_discrete_vars)) GDPopt = nlp_model.GDPopt_utils initialize_subproblem(nlp_model, solve_data) # Callback immediately before solving NLP subproblem config.call_before_subproblem_solve(nlp_model, solve_data) nlp_solver = SolverFactory(config.nlp_solver) if not nlp_solver.available(): raise RuntimeError("NLP solver %s is not available." % config.nlp_solver) with SuppressInfeasibleWarning(): try: results = nlp_solver.solve(nlp_model, **config.nlp_solver_args) except ValueError as err: if 'Cannot load SolverResults object with bad status: error' in str( err): results = SolverResults() results.solver.termination_condition = tc.error results.solver.message = str(err) else: raise nlp_result = SubproblemResult() nlp_result.feasible = True nlp_result.var_values = list(v.value for v in GDPopt.variable_list) nlp_result.pyomo_results = results nlp_result.dual_values = list( nlp_model.dual.get(c, None) for c in GDPopt.constraint_list) term_cond = results.solver.termination_condition if any(term_cond == cond for cond in (tc.optimal, tc.locallyOptimal, tc.feasible)): pass elif term_cond == tc.infeasible: config.logger.info('NLP subproblem was infeasible.') nlp_result.feasible = False elif term_cond == tc.maxIterations: # TODO try something else? Reinitialize with different initial # value? config.logger.info( 'NLP subproblem failed to converge within iteration limit.') if is_feasible(nlp_model, config): config.logger.info( 'NLP solution is still feasible. ' 'Using potentially suboptimal feasible solution.') else: nlp_result.feasible = False elif term_cond == tc.internalSolverError: # Possible that IPOPT had a restoration failure config.logger.info("NLP solver had an internal failure: %s" % results.solver.message) nlp_result.feasible = False elif (term_cond == tc.other and "Too few degrees of freedom" in str(results.solver.message)): # Possible IPOPT degrees of freedom error config.logger.info("IPOPT has too few degrees of freedom: %s" % results.solver.message) nlp_result.feasible = False elif term_cond == tc.other: config.logger.info( "NLP solver had a termination condition of 'other': %s" % results.solver.message) nlp_result.feasible = False elif term_cond == tc.error: config.logger.info( "NLP solver had a termination condition of 'error': %s" % results.solver.message) nlp_result.feasible = False elif term_cond == tc.maxTimeLimit: config.logger.info( "NLP solver ran out of time. Assuming infeasible for now.") nlp_result.feasible = False else: raise ValueError('GDPopt unable to handle NLP subproblem termination ' 'condition of %s. Results: %s' % (term_cond, results)) # Call the NLP post-solve callback config.call_after_subproblem_solve(nlp_model, solve_data) # if feasible, call the NLP post-feasible callback if nlp_result.feasible: config.call_after_subproblem_feasible(nlp_model, solve_data) return nlp_result
def sipopt(instance,paramSubList,perturbList,cloneModel=True, streamSoln=False, keepfiles=False): """ This function accepts a Pyomo ConcreteModel, a list of parameters, along with their corresponding perterbation list. The model is then converted into the design structure required to call sipopt to get an approximation perturbed solution with updated bounds on the decision variable. Arguments: instance : ConcreteModel: Expectation No Exceptions pyomo model object paramSubList : Param list of mutable parameters Exception : "paramSubList argument is expecting a List of Params" perturbList : Param list of perturbed parameter values Exception : "perturbList argument is expecting a List of Params" length(paramSubList) must equal length(perturbList) Exception : "paramSubList will not map to perturbList" cloneModel : boolean : default=True indicator to clone the model -if set to False, the original model will be altered streamSoln : boolean : default=False indicator to stream IPOPT solution keepfiles : boolean : default=False indicator to print intermediate file names Returns: m : ConcreteModel converted model for sipopt m.sol_state_1 : Suffix approximated results at perturbation m.sol_state_1_z_L : Suffix updated lower bound m.sol_state_1_z_U : Suffix updated upper bound """ #Verify User Inputs if len(paramSubList)!=len(perturbList): raise ValueError("Length of paramSubList argument does not equal " "length of perturbList") for pp in paramSubList: if pp.type() is not Param: raise ValueError("paramSubList argument is expecting a list of Params") for pp in paramSubList: if not pp._mutable: raise ValueError("parameters within paramSubList must be mutable") for pp in perturbList: if pp.type() is not Param: raise ValueError("perturbList argument is expecting a list of Params") #Add model block to compartmentalize all sipopt data b=Block() block_name = unique_component_name(instance, '_sipopt_data') instance.add_component(block_name, b) #Based on user input clone model or use orignal model for anlaysis if cloneModel: b.tmp_lists = (paramSubList, perturbList) m = instance.clone() instance.del_component(block_name) b = getattr(m, block_name) paramSubList, perturbList = b.tmp_lists del b.tmp_lists else: m = instance #Generate component maps for associating Variables to perturbations varSubList = [] for parameter in paramSubList: tempName = unique_component_name(b,parameter.local_name) b.add_component(tempName,Var(parameter.index_set())) myVar = b.component(tempName) varSubList.append(myVar) #Note: substitutions are not currently compatible with # ComponentMap [ECSA 2018/11/23], this relates to Issue #755 paramCompMap = ComponentMap(zip(paramSubList, varSubList)) variableSubMap = {} #variableSubMap = ComponentMap() paramPerturbMap = ComponentMap(zip(paramSubList,perturbList)) perturbSubMap = {} #perturbSubMap = ComponentMap() paramDataList = [] for parameter in paramSubList: # Loop over each ParamData in the Param Component # # Note: Sets are unordered in Pyomo. For this to be # deterministic, we need to sort the index (otherwise, the # ordering of things in the paramDataList may change). We use # sorted_robust to guard against mixed-type Sets in Python 3.x for kk in sorted_robust(parameter): variableSubMap[id(parameter[kk])]=paramCompMap[parameter][kk] perturbSubMap[id(parameter[kk])]=paramPerturbMap[parameter][kk] paramDataList.append(parameter[kk]) #clone Objective, add to Block, and update any Expressions for cc in list(m.component_data_objects(Objective, active=True, descend_into=True)): tempName=unique_component_name(m,cc.local_name) b.add_component(tempName, Objective(expr=ExpressionReplacementVisitor( substitute=variableSubMap, remove_named_expressions=True).dfs_postorder_stack(cc.expr))) cc.deactivate() #clone Constraints, add to Block, and update any Expressions b.constList = ConstraintList() for cc in list(m.component_data_objects(Constraint, active=True, descend_into=True)): if cc.equality: b.constList.add(expr= ExpressionReplacementVisitor( substitute=variableSubMap, remove_named_expressions=True).dfs_postorder_stack(cc.expr)) else: try: b.constList.add(expr=ExpresssionReplacementVisitor( substitute=variableSubMap, remove_named_expressions=True).dfs_postorder_stack(cc.expr)) except: # Params in either the upper or lower bounds of a ranged # inequaltiy will result in an invalid expression (you cannot # have variables in the bounds of a constraint). If we hit that # problem, we will break up the ranged inequality into separate # constraints # Note that the test for lower / upper == None is probably not # necessary, as the only way we should get here (especially if # we are more careful about the exception that we catch) is if # this is a ranged inequality and we are attempting to insert a # variable into either the lower or upper bound. if cc.lower is not None: b.constList.add(expr=ExpressionReplacementVisitor( substitute=variableSubMap, remove_named_expressions=True).dfs_postorder_stack( cc.lower) <= ExpressionReplacementVisitor( substitute=variableSubMap, remove_named_expressions= True).dfs_postorder_stack(cc.body) ) #if cc.lower is not None: # b.constList.add(expr=0<=ExpressionReplacementVisitor( # substitute=variableSubMap, # remove_named_expressions=True).dfs_postorder_stack( # cc.lower) - ExpressionReplacementVisitor( # substitute=variableSubMap, # remove_named_expressions= # True).dfs_postorder_stack(cc.body) # ) if cc.upper is not None: b.constList.add(expr=ExpressionReplacementVisitor( substitute=variableSubMap, remove_named_expressions=True).dfs_postorder_stack( cc.upper) >= ExpressionReplacementVisitor( substitute=variableSubMap, remove_named_expressions= True).dfs_postorder_stack(cc.body) ) cc.deactivate() #paramData to varData constraint list b.paramConst = ConstraintList() for ii in paramDataList: jj=variableSubMap[id(ii)] b.paramConst.add(ii==jj) #Create the ipopt_sens (aka sIPOPT) solver plugin using the ASL interface opt = SolverFactory('ipopt_sens', solver_io='nl') if not opt.available(False): raise ImportError('ipopt_sens is not available') #Declare Suffixes m.sens_state_0 = Suffix(direction=Suffix.EXPORT) m.sens_state_1 = Suffix(direction=Suffix.EXPORT) m.sens_state_value_1 = Suffix(direction=Suffix.EXPORT) m.sens_init_constr = Suffix(direction=Suffix.EXPORT) m.sens_sol_state_1 = Suffix(direction=Suffix.IMPORT) m.sens_sol_state_1_z_L = Suffix(direction=Suffix.IMPORT) m.sens_sol_state_1_z_U = Suffix(direction=Suffix.IMPORT) #set sIPOPT data opt.options['run_sens'] = 'yes' # for reasons that are not entirely clear, # ipopt_sens requires the indices to start at 1 kk=1 for ii in paramDataList: m.sens_state_0[variableSubMap[id(ii)]] = kk m.sens_state_1[variableSubMap[id(ii)]] = kk m.sens_state_value_1[variableSubMap[id(ii)]] = \ value(perturbSubMap[id(ii)]) m.sens_init_constr[b.paramConst[kk]] = kk kk += 1 #Send the model to the ipopt_sens and collect the solution results = opt.solve(m, keepfiles=keepfiles, tee=streamSoln) return m
def solve_linear_GDP(linear_GDP_model, solve_data, config): m = linear_GDP_model GDPopt = m.GDPopt_utils # Transform disjunctions TransformationFactory('gdp.bigm').apply_to(m) preprocessing_transformations = [ # Propagate variable bounds 'contrib.propagate_eq_var_bounds', # Detect fixed variables 'contrib.detect_fixed_vars', # Propagate fixed variables 'contrib.propagate_fixed_vars', # Remove zero terms in linear expressions 'contrib.remove_zero_terms', # Remove terms in equal to zero summations 'contrib.propagate_zero_sum', # Transform bound constraints 'contrib.constraints_to_var_bounds', # Detect fixed variables 'contrib.detect_fixed_vars', # Remove terms in equal to zero summations 'contrib.propagate_zero_sum', # Remove trivial constraints 'contrib.deactivate_trivial_constraints' ] for xfrm in preprocessing_transformations: TransformationFactory(xfrm).apply_to(m) # Deactivate extraneous IMPORT/EXPORT suffixes getattr(m, 'ipopt_zL_out', _DoNothing()).deactivate() getattr(m, 'ipopt_zU_out', _DoNothing()).deactivate() # Load solutions is false because otherwise an annoying error message # appears for infeasible models. mip_solver = SolverFactory(config.mip) if not mip_solver.available(): raise RuntimeError("MIP solver %s is not available." % config.mip) results = mip_solver.solve(m, load_solutions=False, **config.mip_options) terminate_cond = results.solver.termination_condition if terminate_cond is tc.infeasibleOrUnbounded: # Linear solvers will sometimes tell me that it's infeasible or # unbounded during presolve, but fails to distinguish. We need to # resolve with a solver option flag on. tmp_options = deepcopy(config.mip_options) # TODO This solver option is specific to Gurobi. tmp_options['DualReductions'] = 0 results = mip_solver.solve(m, load_solutions=False, **tmp_options) terminate_cond = results.solver.termination_condition if terminate_cond is tc.optimal: m.solutions.load_from(results) return True, list(v.value for v in GDPopt.working_var_list) elif terminate_cond is tc.infeasible: config.logger.info( 'Linear GDP is infeasible. ' 'Problem may have no more feasible discrete configurations.') return False elif terminate_cond is tc.maxTimeLimit: # TODO check that status is actually ok and everything is feasible config.logger.info( 'Unable to optimize linear GDP problem within time limit. ' 'Using current solver feasible solution.') results.solver.status = SolverStatus.ok m.solutions.load_from(results) return True, list(v.value for v in GDPopt.working_var_list) elif (terminate_cond is tc.other and results.solution.status is SolutionStatus.feasible): # load the solution and suppress the warning message by setting # solver status to ok. config.logger.info('Linear GDP solver reported feasible solution, ' 'but not guaranteed to be optimal.') results.solver.status = SolverStatus.ok m.solutions.load_from(results) return True, list(v.value for v in GDPopt.working_var_list) else: raise ValueError('GDPopt unable to handle linear GDP ' 'termination condition ' 'of %s. Solver message: %s' % (terminate_cond, results.solver.message))
def get_dfds_dcds(model, theta_names, tee=False, solver_options=None): """This function calculates gradient vector of the objective function and constraints with respect to the variables in theta_names. e.g) min f: p1*x1+ p2*(x2^2) + p1*p2 s.t c1: x1 + x2 = p1 c2: x2 + x3 = p2 0 <= x1, x2, x3 <= 10 p1 = 10 p2 = 5 - Variables = (x1, x2, x3, p1, p2) - Fix p1 and p2 with estimated values The following terms are used to define the output dimensions: Ncon = number of constraints Nvar = number of variables (Nx + Ntheta) Nx = the numer of decision (primal) variables Ntheta = number of uncertain parameters. Parameters ---------- model: Pyomo ConcreteModel model should includes an objective function theta_names: list of strings List of Var names tee: bool, optional Indicates that ef solver output should be teed solver_options: dict, optional Provides options to the solver (also the name of an attribute) Returns ------- gradient_f: numpy.ndarray Length Nvar array. A gradient vector of the objective function with respect to the (decision variables, parameters) at the optimal solution gradient_c: scipy.sparse.csr.csr_matrix Ncon by Nvar size sparse matrix. A Jacobian matrix of the constraints with respect to the (decision variables, parameters) at the optimal solution. Each row contains [column number, row number, and value], colum order follows variable order in col and index starts from 1. Note that it follows k_aug. If no constraint exists, return [] col: list Size Nvar. list of variable names row: list Size Ncon+1. List of constraints and objective function names The final element is the objective function name. line_dic: dict column numbers of the theta_names in the model. Index starts from 1 Raises ------ RuntimeError When ipopt or kaug or dotsens is not available Exception When ipopt fails """ #Create the solver plugin using the ASL interface ipopt = SolverFactory('ipopt', solver_io='nl') if solver_options is not None: ipopt.options = solver_options kaug = SolverFactory('k_aug', solver_io='nl') dotsens = SolverFactory('dot_sens', solver_io='nl') if not ipopt.available(False): raise RuntimeError('ipopt is not available') if not kaug.available(False): raise RuntimeError('k_aug is not available') if not dotsens.available(False): raise RuntimeError('dotsens is not available') # Declare Suffixes _add_sensitivity_suffixes(model) # K_AUG SUFFIXES model.dof_v = Suffix(direction=Suffix.EXPORT) #: SUFFIX FOR K_AUG model.rh_name = Suffix( direction=Suffix.IMPORT) #: SUFFIX FOR K_AUG AS WELL kaug.options["print_kkt"] = "" results = ipopt.solve(model, tee=tee) # Raise Exception if ipopt fails if (results.solver.status == SolverStatus.warning): raise Exception(results.solver.Message) for o in model.component_objects(Objective, active=True): f_mean = value(o) model.ipopt_zL_in.update(model.ipopt_zL_out) model.ipopt_zU_in.update(model.ipopt_zU_out) #: run k_aug kaug.solve(model, tee=tee) #: always call k_aug AFTER ipopt. model.write('col_row.nl', format='nl', io_options={'symbolic_solver_labels': True}) # get the column numbers of theta line_dic = {} try: for v in theta_names: line_dic[v] = line_num('col_row.col', v) # load gradient of the objective function gradient_f = np.loadtxt("./GJH/gradient_f_print.txt") with open("col_row.col", "r") as myfile: col = myfile.read().splitlines() col = [ i for i in col if SensitivityInterface.get_default_block_name() not in i ] with open("col_row.row", "r") as myfile: row = myfile.read().splitlines() except Exception as e: print('File not found.') raise e # load gradient of all constraints (sparse) # If no constraint exists, return [] num_constraints = len( list( model.component_data_objects(Constraint, active=True, descend_into=True))) if num_constraints > 0: try: # load text file from kaug gradient_c = np.loadtxt("./GJH/A_print.txt") # This is a sparse matrix # gradient_c[:,0] are column index # gradient_c[:,1] are data index # gradient_c[:,1] are the matrix values except Exception as e: print('kaug file ./GJH/A_print.txt not found.') # Subtract 1 from row and column indices to convert from # start at 1 (kaug) to start at 0 (numpy) row_idx = gradient_c[:, 1] - 1 col_idx = gradient_c[:, 0] - 1 data = gradient_c[:, 2] gradient_c = sparse.csr_matrix((data, (row_idx, col_idx)), shape=(len(row) - 1, len(col))) else: gradient_c = np.array([]) # remove all generated files shutil.move("col_row.nl", "./GJH/") shutil.move("col_row.col", "./GJH/") shutil.move("col_row.row", "./GJH/") shutil.rmtree('GJH', ignore_errors=True) return gradient_f, gradient_c, col, row, line_dic
def solve_MINLP(model, solve_data, config): """Solve the MINLP subproblem.""" config.logger.info( "Solving MINLP subproblem for fixed logical realizations." ) GDPopt = model.GDPopt_utils if config.subproblem_presolve: preprocess_subproblem(model, config) initialize_subproblem(model, solve_data) # Callback immediately before solving NLP subproblem config.call_before_subproblem_solve(model, solve_data) minlp_solver = SolverFactory(config.minlp_solver) if not minlp_solver.available(): raise RuntimeError("MINLP solver %s is not available." % config.minlp_solver) with SuppressInfeasibleWarning(): results = minlp_solver.solve(model, **config.minlp_solver_args) subprob_result = SubproblemResult() subprob_result.feasible = True subprob_result.var_values = list(v.value for v in GDPopt.variable_list) subprob_result.pyomo_results = results subprob_result.dual_values = list( model.dual.get(c, None) for c in GDPopt.constraint_list) subprob_terminate_cond = results.solver.termination_condition if (subprob_terminate_cond is tc.optimal or subprob_terminate_cond is tc.locallyOptimal or subprob_terminate_cond is tc.feasible): pass elif subprob_terminate_cond is tc.infeasible: config.logger.info('MINLP subproblem was infeasible.') subprob_result.feasible = False elif subprob_terminate_cond is tc.maxIterations: # TODO try something else? Reinitialize with different initial # value? config.logger.info( 'MINLP subproblem failed to converge within iteration limit.') if is_feasible(model, config): config.logger.info( 'MINLP solution is still feasible. ' 'Using potentially suboptimal feasible solution.') else: subprob_result.feasible = False elif subprob_terminate_cond is tc.intermediateNonInteger: config.logger.info( "MINLP solver could not find feasible integer solution: %s" % results.solver.message) subprob_result.feasible = False else: raise ValueError( 'GDPopt unable to handle MINLP subproblem termination ' 'condition of %s. Results: %s' % (subprob_terminate_cond, results)) # Call the subproblem post-solve callback config.call_after_subproblem_solve(model, solve_data) # if feasible, call the subproblem post-feasible callback if subprob_result.feasible: config.call_after_subproblem_feasible(model, solve_data) return subprob_result