def _write_reference_scenario_lp(self, ph): # Make sure the pyomo plugins are loaded import pyomo.environ lp_file_writer = pyomo.repn.plugins.cpxlp.ProblemWriter_cpxlp() # Write the LP file print(("Writing LP file to %s" % (self._lpfilename,))) scenariotree_vars = \ self._reference_scenario_instance.\ _ScenarioTreeSymbolMap.bySymbol rootnode_vars = \ ph._scenario_tree.findRootNode()._standard_variable_ids firststage_ids = \ [id(vardata) for scenariotree_id, vardata \ in iteritems(scenariotree_vars) \ if scenariotree_id in rootnode_vars] capabilities = lambda x: True text_labeler = TextLabeler() labeler = lambda x: text_labeler(x) + \ (self._firststage_var_postfix \ if (id(x) in firststage_ids) else \ "") output_filename, symbol_map = \ lp_file_writer(self._reference_scenario_instance, self._lpfilename, capabilities, {'labeler' : labeler}) assert output_filename == self._lpfilename print("\nModel written\n") # Return the max symbol length (for siphelp) max_name_len = max(len(symbol) for symbol in symbol_map.bySymbol) return max_name_len, symbol_map
def _set_instance(self, model, kwds={}): if not isinstance(model, (Model, IBlockStorage, Block, _BlockData)): msg = "The problem instance supplied to the {0} plugin " \ "'_presolve' method must be a Model or a Block".format(type(self)) raise ValueError(msg) self._pyomo_model = model self._symbolic_solver_labels = kwds.pop('symbolic_solver_labels', self._symbolic_solver_labels) self._skip_trivial_constraints = kwds.pop('skip_trivial_constraints', self._skip_trivial_constraints) self._output_fixed_variable_bounds = kwds.pop('output_fixed_variable_bounds', self._output_fixed_variable_bounds) self._pyomo_var_to_solver_var_map = ComponentMap() self._solver_var_to_pyomo_var_map = dict() self._pyomo_con_to_solver_con_map = dict() self._solver_con_to_pyomo_con_map = dict() self._vars_referenced_by_con = ComponentMap() self._vars_referenced_by_obj = ComponentSet() self._referenced_variables = ComponentMap() self._objective_label = None self._objective = None self._symbol_map = SymbolMap() if self._symbolic_solver_labels: self._labeler = TextLabeler() else: self._labeler = NumericLabeler('x')
def set_instance(self, model): saved_config = self.config saved_update_config = self.update_config self.__init__() self.config = saved_config self.update_config = saved_update_config self._model = model if self.config.symbolic_solver_labels: self._var_labeler = TextLabeler() self._con_labeler = TextLabeler() self._param_labeler = TextLabeler() else: self._var_labeler = NumericLabeler('x') self._con_labeler = NumericLabeler('c') self._param_labeler = NumericLabeler('p') self._writer = cmodel.NLWriter() self.add_block(model) if self._objective is None: self.set_objective(None)
def set_instance(self, model, symbolic_solver_labels: Optional[bool] = None): saved_config = self.config saved_update_config = self.update_config self.__init__() self.config = saved_config self.update_config = saved_update_config self._expr_types = cmodel.PyomoExprTypes() if symbolic_solver_labels is not None: self._symbolic_solver_labels = symbolic_solver_labels if self._symbolic_solver_labels: self._var_labeler = TextLabeler() self._con_labeler = TextLabeler() self._param_labeler = TextLabeler() self._obj_labeler = TextLabeler() self._model = model self._cmodel = cmodel.FBBTModel() self.add_block(model) if self._objective is None: self.set_objective(None)
def _convert_external_setup_without_cleanup(worker, scenario, output_directory, firststage_var_suffix, enforce_derived_nonanticipativity, io_options): import pyomo.environ assert os.path.exists(output_directory) io_options = dict(io_options) scenario_tree = worker.scenario_tree reference_model = scenario._instance rootnode = scenario_tree.findRootNode() firststage = scenario_tree.stages[0] secondstage = scenario_tree.stages[1] constraint_name_buffer = {} objective_name_buffer = {} variable_name_buffer = {} all_constraints = list(con for con in reference_model.component_data_objects( Constraint, active=True, descend_into=True)) # # Check for model annotations # stochastic_rhs = locate_annotations(reference_model, StochasticConstraintBoundsAnnotation, max_allowed=1) if len(stochastic_rhs) == 0: stochastic_rhs = None stochastic_rhs_entries = {} empty_rhs_annotation = False else: assert len(stochastic_rhs) == 1 stochastic_rhs = stochastic_rhs[0][1] if stochastic_rhs.has_declarations: empty_rhs_annotation = False stochastic_rhs_entries = stochastic_rhs.expand_entries() stochastic_rhs_entries.sort( key=lambda x: x[0].getname(True, constraint_name_buffer)) if len(stochastic_rhs_entries) == 0: raise RuntimeError( "The %s annotation was declared " "with external entries but no active Constraint " "objects were recovered from those entries." % (StochasticConstraintBoundsAnnotation.__name__)) else: empty_rhs_annotation = True stochastic_rhs_entries = tuple( (con, stochastic_rhs.default) for con in all_constraints) stochastic_matrix = locate_annotations(reference_model, StochasticConstraintBodyAnnotation, max_allowed=1) if len(stochastic_matrix) == 0: stochastic_matrix = None stochastic_matrix_entries = {} empty_matrix_annotation = False else: assert len(stochastic_matrix) == 1 stochastic_matrix = stochastic_matrix[0][1] if stochastic_matrix.has_declarations: empty_matrix_annotation = False stochastic_matrix_entries = stochastic_matrix.expand_entries() stochastic_matrix_entries.sort( key=lambda x: x[0].getname(True, constraint_name_buffer)) if len(stochastic_matrix_entries) == 0: raise RuntimeError( "The %s annotation was declared " "with external entries but no active Constraint " "objects were recovered from those entries." % (StochasticConstraintBoundsAnnotation.__name__)) else: empty_matrix_annotation = True stochastic_matrix_entries = tuple( (con, stochastic_matrix.default) for con in all_constraints) stochastic_constraint_ids = set() stochastic_constraint_ids.update( id(con) for con, _ in stochastic_rhs_entries) stochastic_constraint_ids.update( id(con) for con, _ in stochastic_matrix_entries) stochastic_objective = locate_annotations(reference_model, StochasticObjectiveAnnotation, max_allowed=1) if len(stochastic_objective) == 0: stochastic_objective = None else: assert len(stochastic_objective) == 1 stochastic_objective = stochastic_objective[0][1] stochastic_varbounds = locate_annotations( reference_model, StochasticVariableBoundsAnnotation) if len(stochastic_varbounds) > 0: raise ValueError( "The DDSIP writer does not currently support " "stochastic variable bounds. Invalid annotation type: %s" % (StochasticVariableBoundsAnnotation.__name__)) if (stochastic_rhs is None) and \ (stochastic_matrix is None) and \ (stochastic_objective is None): raise RuntimeError("No stochastic annotations found. DDSIP " "conversion requires at least one of the following " "annotation types:\n - %s\n - %s\n - %s" % (StochasticConstraintBoundsAnnotation.__name__, StochasticConstraintBodyAnnotation.__name__, StochasticObjectiveAnnotation.__name__)) assert not hasattr(reference_model, "_repn") repn_cache = build_repns(reference_model) assert hasattr(reference_model, "_repn") assert not reference_model._gen_obj_repn assert not reference_model._gen_con_repn # compute values for block_repns in repn_cache.values(): for repn in block_repns.values(): repn.constant = value(repn.constant) repn.linear_coefs = [value(c) for c in repn.linear_coefs] repn.quadratic_coefs = [value(c) for c in repn.quadratic_coefs] # # Write the LP file once to obtain the symbol map # output_filename = os.path.join(output_directory, scenario.name + ".lp.setup") with WriterFactory("lp") as writer: assert 'column_order' not in io_options assert 'row_order' not in io_options output_fname, symbol_map = writer(reference_model, output_filename, lambda x: True, io_options) assert output_fname == output_filename _safe_remove_file(output_filename) StageToVariableMap = map_variable_stages( scenario, scenario_tree, symbol_map, enforce_derived_nonanticipativity=enforce_derived_nonanticipativity) firststage_variable_ids = \ set(id(var) for symbol, var, scenario_tree_id in StageToVariableMap[firststage.name]) secondstage_variable_ids = \ set(id(var) for symbol, var, scenario_tree_id in StageToVariableMap[secondstage.name]) StageToConstraintMap = \ map_constraint_stages( scenario, scenario_tree, symbol_map, stochastic_constraint_ids, firststage_variable_ids, secondstage_variable_ids) secondstage_constraint_ids = \ set(id(con) for symbols, con in StageToConstraintMap[secondstage.name]) assert len(scenario_tree.stages) == 2 firststage = scenario_tree.stages[0] secondstage = scenario_tree.stages[1] # # Make sure the objective references all first stage variables. # We do this by directly modifying the _repn of the # objective which the LP/MPS writer will reference next time we call # it. In addition, make sure that the first second-stage variable # in our column ordering also appears in the objective so that # ONE_VAR_CONSTANT does not get identified as the first # second-stage variable. # ** Just do NOT preprocess again until we call the writer ** # objective_object = scenario._instance_objective assert objective_object is not None objective_block = objective_object.parent_block() objective_repn = repn_cache[id(objective_block)][objective_object] # # Create column (variable) ordering maps for LP/MPS files # column_order = ComponentMap() firststage_variable_count = 0 secondstage_variable_count = 0 # first-stage variables for column_index, (symbol, var, scenario_tree_id) \ in enumerate(StageToVariableMap[firststage.name]): column_order[var] = column_index firststage_variable_count += 1 # second-stage variables for column_index, (symbol, var, scenario_tree_id) \ in enumerate(StageToVariableMap[secondstage.name], len(column_order)): column_order[var] = column_index secondstage_variable_count += 1 # account for the ONE_VAR_CONSTANT second-stage variable # added by the LP writer secondstage_variable_count += 1 # # Create row (constraint) ordering maps for LP/MPS files # firststage_constraint_count = 0 secondstage_constraint_count = 0 row_order = ComponentMap() # first-stage constraints for row_index, (symbols, con) \ in enumerate(StageToConstraintMap[firststage.name]): row_order[con] = row_index firststage_constraint_count += len(symbols) # second-stage constraints for row_index, (symbols, con) \ in enumerate(StageToConstraintMap[secondstage.name], len(row_order)): row_order[con] = row_index secondstage_constraint_count += len(symbols) # account for the ONE_VAR_CONSTANT = 1 second-stage constraint # added by the LP writer secondstage_constraint_count += 1 # # Create a custom labeler that allows DDSIP to identify # first-stage variables # if io_options.pop('symbolic_solver_labels', False): _labeler = TextLabeler() else: _labeler = NumericLabeler('x') labeler = lambda x: _labeler(x) + \ ("" if ((not isinstance(x, _VarData)) or \ (id(x) not in firststage_variable_ids)) else \ firststage_var_suffix) # # Write the ordered LP/MPS file # output_filename = os.path.join(output_directory, scenario.name + ".lp") symbols_filename = os.path.join(output_directory, scenario.name + ".lp.symbols") with WriterFactory("lp") as writer: assert 'column_order' not in io_options assert 'row_order' not in io_options assert 'labeler' not in io_options assert 'force_objective_constant' not in io_options io_options['column_order'] = column_order io_options['row_order'] = row_order io_options['force_objective_constant'] = True io_options['labeler'] = labeler output_fname, symbol_map = writer(reference_model, output_filename, lambda x: True, io_options) assert output_fname == output_filename # write the lp file symbol paired with the scenario # tree id for each variable in the root node with open(symbols_filename, "w") as f: st_symbol_map = reference_model._ScenarioTreeSymbolMap lines = [] for id_ in sorted(rootnode._variable_ids): var = st_symbol_map.bySymbol[id_] if not var.is_expression_type(): lp_label = symbol_map.byObject[id(var)] lines.append("%s %s\n" % (lp_label, id_)) f.writelines(lines) # re-generate these maps as the LP/MPS symbol map # is likely different StageToVariableMap = map_variable_stages( scenario, scenario_tree, symbol_map, enforce_derived_nonanticipativity=enforce_derived_nonanticipativity) StageToConstraintMap = map_constraint_stages(scenario, scenario_tree, symbol_map, stochastic_constraint_ids, firststage_variable_ids, secondstage_variable_ids) # generate a few data structures that are used # when writing the .sc files constraint_symbols = ComponentMap( (con, symbols) for stage_name in StageToConstraintMap for symbols, con in StageToConstraintMap[stage_name]) # # Write the body of the .sc files # modified_constraint_lb = ComponentMap() modified_constraint_ub = ComponentMap() # # Stochastic RHS # # **NOTE: In the code that follows we assume the LP # writer always moves constraint body # constants to the rhs and that the lower part # of any range constraints are written before # the upper part. # stochastic_rhs_count = 0 with open(os.path.join(output_directory, scenario.name + ".rhs.sc.struct"), 'w') as f_rhs_struct: with open(os.path.join(output_directory, scenario.name + ".rhs.sc"), 'w') as f_rhs: scenario_probability = scenario.probability rhs_struct_template = " %s\n" rhs_template = " %.17g\n" f_rhs.write("scen\n%.17g\n" % (_no_negative_zero(scenario_probability))) if stochastic_rhs is not None: for con, include_bound in stochastic_rhs_entries: assert isinstance(con, _ConstraintData) if not empty_rhs_annotation: # verify that this constraint was # flagged by PySP or the user as second-stage if id(con) not in secondstage_constraint_ids: raise RuntimeError( "The constraint %s has been declared " "in the %s annotation but it was not identified as " "a second-stage constraint. To correct this issue, " "remove the constraint from this annotation." % (con.name, StochasticConstraintBoundsAnnotation.__name__) ) constraint_repn = \ repn_cache[id(con.parent_block())][con] if not constraint_repn.is_linear(): raise RuntimeError( "Only linear constraints are " "accepted for conversion to DDSIP format. " "Constraint %s is not linear." % (con.name)) body_constant = constraint_repn.constant # We are going to rewrite the core problem file # with all stochastic values set to zero. This will # allow an easy test for missing user annotations. constraint_repn.constant = 0 if body_constant is None: body_constant = 0.0 symbols = constraint_symbols[con] assert len(symbols) > 0 for con_label in symbols: if con_label.startswith('c_e_') or \ con_label.startswith('c_l_'): assert (include_bound is True) or \ (include_bound[0] is True) stochastic_rhs_count += 1 f_rhs_struct.write(rhs_struct_template % (con_label)) f_rhs.write(rhs_template % (_no_negative_zero( value(con.lower) - \ value(body_constant)))) # We are going to rewrite the core problem file # with all stochastic values set to zero. This will # allow an easy test for missing user annotations. modified_constraint_lb[con] = con.lower con._lower = _deterministic_check_constant if con_label.startswith('c_e_'): modified_constraint_ub[con] = con.upper con._upper = _deterministic_check_constant elif con_label.startswith('r_l_'): if (include_bound is True) or \ (include_bound[0] is True): stochastic_rhs_count += 1 f_rhs_struct.write(rhs_struct_template % (con_label)) f_rhs.write(rhs_template % (_no_negative_zero( value(con.lower) - \ value(body_constant)))) # We are going to rewrite the core problem file # with all stochastic values set to zero. This will # allow an easy test for missing user annotations. modified_constraint_lb[con] = con.lower con._lower = _deterministic_check_constant elif con_label.startswith('c_u_'): assert (include_bound is True) or \ (include_bound[1] is True) stochastic_rhs_count += 1 f_rhs_struct.write(rhs_struct_template % (con_label)) f_rhs.write(rhs_template % (_no_negative_zero( value(con.upper) - \ value(body_constant)))) # We are going to rewrite the core problem file # with all stochastic values set to zero. This will # allow an easy test for missing user annotations. modified_constraint_ub[con] = con.upper con._upper = _deterministic_check_constant elif con_label.startswith('r_u_'): if (include_bound is True) or \ (include_bound[1] is True): stochastic_rhs_count += 1 f_rhs_struct.write(rhs_struct_template % (con_label)) f_rhs.write(rhs_template % (_no_negative_zero( value(con.upper) - \ value(body_constant)))) # We are going to rewrite the core problem file # with all stochastic values set to zero. This will # allow an easy test for missing user annotations. modified_constraint_ub[con] = con.upper con._upper = _deterministic_check_constant else: assert False # # Stochastic Matrix # stochastic_matrix_count = 0 with open( os.path.join(output_directory, scenario.name + ".matrix.sc.struct"), 'w') as f_mat_struct: with open(os.path.join(output_directory, scenario.name + ".matrix.sc"), 'w') as f_mat: scenario_probability = scenario.probability matrix_struct_template = " %s %s\n" matrix_template = " %.17g\n" f_mat.write("scen\n") if stochastic_matrix is not None: for con, var_list in stochastic_matrix_entries: assert isinstance(con, _ConstraintData) if not empty_matrix_annotation: # verify that this constraint was # flagged by PySP or the user as second-stage if id(con) not in secondstage_constraint_ids: raise RuntimeError( "The constraint %s has been declared " "in the %s annotation but it was not identified as " "a second-stage constraint. To correct this issue, " "remove the constraint from this annotation." % (con.name, StochasticConstraintBodyAnnotation.__name__)) constraint_repn = \ repn_cache[id(con.parent_block())][con] if not constraint_repn.is_linear(): raise RuntimeError( "Only linear constraints are " "accepted for conversion to DDSIP format. " "Constraint %s is not linear." % (con.name)) assert len(constraint_repn.linear_vars) > 0 if var_list is None: var_list = constraint_repn.linear_vars assert len(var_list) > 0 symbols = constraint_symbols[con] # sort the variable list by the column ordering # so that we have deterministic output var_list = list(var_list) var_list.sort(key=lambda _v: column_order[_v]) new_coefs = list(constraint_repn.linear_coefs) for var in var_list: assert isinstance(var, _VarData) assert not var.fixed var_coef = None for i, (_var, coef) in enumerate( zip(constraint_repn.linear_vars, constraint_repn.linear_coefs)): if _var is var: var_coef = coef # We are going to rewrite with core problem file # with all stochastic values set to zero. This will # allow an easy test for missing user annotations. new_coefs[i] = _deterministic_check_value break if var_coef is None: raise RuntimeError( "The coefficient for variable %s has " "been marked as stochastic in constraint %s using " "the %s annotation, but the variable does not appear" " in the canonical constraint expression." % (var.name, con.name, StochasticConstraintBodyAnnotation.__name__)) var_label = symbol_map.byObject[id(var)] for con_label in symbols: stochastic_matrix_count += 1 f_mat_struct.write(matrix_struct_template % (con_label, var_label)) f_mat.write(matrix_template % (_no_negative_zero(value(var_coef)))) constraint_repn.linear_coefs = tuple(new_coefs) # # Stochastic Objective # stochastic_cost_count = 0 with open( os.path.join(output_directory, scenario.name + ".cost.sc.struct"), 'w') as f_obj_struct: with open(os.path.join(output_directory, scenario.name + ".cost.sc"), 'w') as f_obj: obj_struct_template = " %s\n" obj_template = " %.17g\n" f_obj.write("scen\n") if stochastic_objective is not None: if stochastic_objective.has_declarations: sorted_values = stochastic_objective.expand_entries() assert len(sorted_values) <= 1 if len(sorted_values) == 0: raise RuntimeError( "The %s annotation was declared " "with external entries but no active Objective " "objects were recovered from those entries." % (StochasticObjectiveAnnotation.__name__)) obj, (objective_variables, include_constant) = \ sorted_values[0] assert obj is objective_object else: objective_variables, include_constant = \ stochastic_objective.default if not objective_repn.is_linear(): raise RuntimeError( "Only linear stochastic objectives are " "accepted for conversion to DDSIP format. " "Objective %s is not linear." % (objective_object.name)) if objective_variables is None: objective_variables = objective_repn.linear_vars stochastic_objective_label = symbol_map.byObject[id( objective_object)] # sort the variable list by the column ordering # so that we have deterministic output objective_variables = list(objective_variables) objective_variables.sort(key=lambda _v: column_order[_v]) assert (len(objective_variables) > 0) or include_constant new_coefs = list(objective_repn.linear_coefs) for var in objective_variables: assert isinstance(var, _VarData) var_coef = None for i, (_var, coef) in enumerate( zip(objective_repn.linear_vars, objective_repn.linear_coefs)): if _var is var: var_coef = coef # We are going to rewrite the core problem file # with all stochastic values set to zero. This will # allow an easy test for missing user annotations. new_coefs[i] = _deterministic_check_value break if var_coef is None: raise RuntimeError( "The coefficient for variable %s has " "been marked as stochastic in objective %s using " "the %s annotation, but the variable does not appear" " in the canonical objective expression." % (var.name, objective_object.name, StochasticObjectiveAnnotation.__name__)) var_label = symbol_map.byObject[id(var)] stochastic_cost_count += 1 f_obj_struct.write(obj_struct_template % (var_label)) f_obj.write(obj_template % (_no_negative_zero(value(var_coef)))) objective_repn.linear_coefs = tuple(new_coefs) if include_constant: obj_constant = objective_repn.constant # We are going to rewrite the core problem file # with all stochastic values set to zero. This will # allow an easy test for missing user annotations. objective_repn.constant = _deterministic_check_value if obj_constant is None: obj_constant = 0.0 stochastic_cost_count += 1 f_obj_struct.write(obj_struct_template % ("ONE_VAR_CONSTANT")) f_obj.write(obj_template % (_no_negative_zero(obj_constant))) # # Write the deterministic part of the LP/MPS-file to its own # file for debugging purposes # reference_model_name = reference_model.name reference_model._name = "ZeroStochasticData" det_output_filename = os.path.join(output_directory, scenario.name + ".lp.det") with WriterFactory("lp") as writer: output_fname, symbol_map = writer(reference_model, det_output_filename, lambda x: True, io_options) assert output_fname == det_output_filename reference_model._name = reference_model_name # reset bounds on any constraints that were modified for con, lower in iteritems(modified_constraint_lb): con._lower = as_numeric(lower) for con, upper in iteritems(modified_constraint_ub): con._upper = as_numeric(upper) return (firststage_variable_count, secondstage_variable_count, firststage_constraint_count, secondstage_constraint_count, stochastic_cost_count, stochastic_rhs_count, stochastic_matrix_count)
def __call__(self, model, output_filename, solver_capability, io_options): # Make sure not to modify the user's dictionary, # they may be reusing it outside of this call io_options = dict(io_options) # Skip writing constraints whose body section is # fixed (i.e., no variables) skip_trivial_constraints = \ io_options.pop("skip_trivial_constraints", False) # Use full Pyomo component names in the LP file rather # than shortened symbols (slower, but useful for debugging). symbolic_solver_labels = \ io_options.pop("symbolic_solver_labels", False) output_fixed_variable_bounds = \ io_options.pop("output_fixed_variable_bounds", False) # If False, unused variables will not be included in # the LP file. Otherwise, include all variables in # the bounds sections. include_all_variable_bounds = \ io_options.pop("include_all_variable_bounds", False) labeler = io_options.pop("labeler", None) # How much effort do we want to put into ensuring the # LP file is written deterministically for a Pyomo model: # 0 : None # 1 : sort keys of indexed components (default) # 2 : sort keys AND sort names (over declaration order) file_determinism = io_options.pop("file_determinism", 1) # user defined orderings for variable and constraint # output row_order = io_options.pop("row_order", None) column_order = io_options.pop("column_order", None) # make sure the ONE_VAR_CONSTANT variable appears in # the objective even if the constant part of the # objective is zero force_objective_constant = \ io_options.pop("force_objective_constant", False) if len(io_options): raise ValueError( "ProblemWriter_cpxlp passed unrecognized io_options:\n\t" + "\n\t".join("%s = %s" % (k, v) for k, v in iteritems(io_options))) if symbolic_solver_labels and (labeler is not None): raise ValueError("ProblemWriter_cpxlp: Using both the " "'symbolic_solver_labels' and 'labeler' " "I/O options is forbidden") if symbolic_solver_labels: labeler = TextLabeler() elif labeler is None: labeler = NumericLabeler('x') # clear the collection of referenced variables. self._referenced_variable_ids.clear() if output_filename is None: output_filename = model.name + ".lp" # when sorting, there are a non-trivial number of # temporary objects created. these all yield # non-circular references, so disable GC - the # overhead is non-trivial, and because references # are non-circular, everything will be collected # immediately anyway. with PauseGC() as pgc: with open(output_filename, "w") as output_file: symbol_map = self._print_model_LP( model, output_file, solver_capability, labeler, output_fixed_variable_bounds=output_fixed_variable_bounds, file_determinism=file_determinism, row_order=row_order, column_order=column_order, skip_trivial_constraints=skip_trivial_constraints, force_objective_constant=force_objective_constant, include_all_variable_bounds=include_all_variable_bounds) self._referenced_variable_ids.clear() return output_filename, symbol_map
def compile_instance(self, pyomo_instance, symbolic_solver_labels=False, output_fixed_variable_bounds=False, skip_trivial_constraints=False): from pyomo.core.base import Var, Constraint, SOSConstraint from pyomo.repn import canonical_is_constant, LinearCanonicalRepn, canonical_degree self._symbolic_solver_labels = symbolic_solver_labels self._output_fixed_variable_bounds = output_fixed_variable_bounds self._skip_trivial_constraints = skip_trivial_constraints self._has_quadratic_constraints = False self._has_quadratic_objective = False self._active_cplex_instance = CPLEXDirect._cplex_module.Cplex() if self._symbolic_solver_labels: labeler = self._labeler = TextLabeler() else: labeler = self._labeler = NumericLabeler('x') self._symbol_map = SymbolMap() self._instance = pyomo_instance if isinstance(pyomo_instance, IBlockStorage): # BIG HACK if not hasattr(pyomo_instance, "._symbol_maps"): setattr(pyomo_instance, "._symbol_maps", {}) getattr(pyomo_instance, "._symbol_maps")[id(self._symbol_map)] = \ self._symbol_map else: pyomo_instance.solutions.add_symbol_map(self._symbol_map) self._smap_id = id(self._symbol_map) # we use this when iterating over the constraints because it # will have a much smaller hash table, we also use this for # the warm start code after it is cleaned to only contain # variables referenced in the constraints self._variable_symbol_map = SymbolMap() # cplex wants the caller to set the problem type, which is (for # current purposes) strictly based on variable type counts. self._num_binary_variables = 0 self._num_integer_variables = 0 self._num_continuous_variables = 0 self._used_sos_constraints = False ############################################# # populate the variables in the cplex model # ############################################# var_names = [] var_lbs = [] var_ubs = [] var_types = [] self._referenced_variable_ids.clear() # maps pyomo var data labels to the corresponding CPLEX variable id. self._cplex_variable_ids.clear() # cached in the loop below - used to update the symbol map # immediately following loop termination. var_label_pairs = [] for var_data in pyomo_instance.component_data_objects(Var, active=True): if var_data.fixed and not self._output_fixed_variable_bounds: # if a variable is fixed, and we're preprocessing # fixed variables (as in not outputting them), there # is no need to add them to the compiled model. continue var_name = self._symbol_map.getSymbol(var_data, labeler) var_names.append(var_name) var_label_pairs.append((var_data, var_name)) self._cplex_variable_ids[var_name] = len(self._cplex_variable_ids) if not var_data.has_lb(): var_lbs.append(-CPLEXDirect._cplex_module.infinity) else: var_lbs.append(value(var_data.lb)) if not var_data.has_ub(): var_ubs.append(CPLEXDirect._cplex_module.infinity) else: var_ubs.append(value(var_data.ub)) if var_data.is_integer(): var_types.append(self._active_cplex_instance.variables.type.integer) self._num_integer_variables += 1 elif var_data.is_binary(): var_types.append(self._active_cplex_instance.variables.type.binary) self._num_binary_variables += 1 elif var_data.is_continuous(): var_types.append(self._active_cplex_instance.variables.type.continuous) self._num_continuous_variables += 1 else: raise TypeError("Invalid domain type for variable with name '%s'. " "Variable is not continuous, integer, or binary.") self._active_cplex_instance.variables.add(names=var_names, lb=var_lbs, ub=var_ubs, types=var_types) self._active_cplex_instance.variables.add(lb=[1], ub=[1], names=["ONE_VAR_CONSTANT"]) self._cplex_variable_ids["ONE_VAR_CONSTANT"] = len(self._cplex_variable_ids) self._variable_symbol_map.addSymbols(var_label_pairs) self._cplex_variable_names = self._active_cplex_instance.variables.get_names() ######################################################## # populate the standard constraints in the cplex model # ######################################################## expressions = [] senses = [] rhss = [] range_values = [] names = [] qexpressions = [] qlinears = [] qsenses = [] qrhss = [] qnames = [] for block in pyomo_instance.block_data_objects(active=True): gen_con_canonical_repn = \ getattr(block, "_gen_con_canonical_repn", True) # Get/Create the ComponentMap for the repn if not hasattr(block,'_canonical_repn'): block._canonical_repn = ComponentMap() block_canonical_repn = block._canonical_repn for con in block.component_data_objects(Constraint, active=True, descend_into=False): if (not con.has_lb()) and \ (not con.has_ub()): assert not con.equality continue # not binding at all, don't bother con_repn = None if con._linear_canonical_form: con_repn = con.canonical_form() elif isinstance(con, LinearCanonicalRepn): con_repn = con else: if gen_con_canonical_repn: con_repn = generate_canonical_repn(con.body) block_canonical_repn[con] = con_repn else: con_repn = block_canonical_repn[con] # There are conditions, e.g., when fixing variables, under which # a constraint block might be empty. Ignore these, for both # practical reasons and the fact that the CPLEX LP format # requires a variable in the constraint body. It is also # possible that the body of the constraint consists of only a # constant, in which case the "variable" of if isinstance(con_repn, LinearCanonicalRepn): if self._skip_trivial_constraints and \ ((con_repn.linear is None) or \ (len(con_repn.linear) == 0)): continue else: # we shouldn't come across a constant canonical repn # that is not LinearCanonicalRepn assert not canonical_is_constant(con_repn) name = self._symbol_map.getSymbol(con, labeler) expr = None qexpr = None quadratic = False if isinstance(con_repn, LinearCanonicalRepn): expr, offset = \ self._encode_constraint_body_linear_specialized(con_repn, labeler, use_variable_names=False, cplex_variable_name_index_map=self._cplex_variable_ids) else: degree = canonical_degree(con_repn) if degree == 2: quadratic = True elif (degree != 0) or (degree != 1): raise ValueError( "CPLEXPersistent plugin does not support general nonlinear " "constraint expression (only linear or quadratic).\n" "Constraint: %s" % (con.name)) expr, offset = self._encode_constraint_body_linear(con_repn, labeler) if quadratic: if expr is None: expr = CPLEXDirect._cplex_module.SparsePair(ind=[0],val=[0.0]) self._has_quadratic_constraints = True qexpr = self._encode_constraint_body_quadratic(con_repn,labeler) qnames.append(name) if con.equality: # equality constraint. qsenses.append('E') qrhss.append(self._get_bound(con.lower) - offset) elif con.has_lb() and con.has_ub(): raise RuntimeError( "The CPLEXDirect plugin can not translate range " "constraints containing quadratic expressions.") elif con.has_lb(): assert not con.has_ub() qsenses.append('G') qrhss.append(self._get_bound(con.lower) - offset) else: assert con.has_ub() qsenses.append('L') qrhss.append(self._get_bound(con.upper) - offset) qlinears.append(expr) qexpressions.append(qexpr) else: names.append(name) expressions.append(expr) if con.equality: # equality constraint. senses.append('E') rhss.append(self._get_bound(con.lower) - offset) range_values.append(0.0) elif con.has_lb() and con.has_ub(): # ranged constraint. senses.append('R') lower_bound = self._get_bound(con.lower) - offset upper_bound = self._get_bound(con.upper) - offset rhss.append(lower_bound) range_values.append(upper_bound - lower_bound) elif con.has_lb(): senses.append('G') rhss.append(self._get_bound(con.lower) - offset) range_values.append(0.0) else: assert con.has_ub() senses.append('L') rhss.append(self._get_bound(con.upper) - offset) range_values.append(0.0) ################################################### # populate the SOS constraints in the cplex model # ################################################### # SOS constraints - largely taken from cpxlp.py so updates there, # should be applied here # TODO: Allow users to specify the variables coefficients for custom # branching/set orders - refer to cpxlp.py sosn = self._capabilities.sosn sos1 = self._capabilities.sos1 sos2 = self._capabilities.sos2 modelSOS = ModelSOS() for soscondata in pyomo_instance.component_data_objects(SOSConstraint, active=True): level = soscondata.level if (level == 1 and not sos1) or \ (level == 2 and not sos2) or \ (level > 2 and not sosn): raise Exception("Solver does not support SOS level %s constraints" % (level,)) modelSOS.count_constraint(self._symbol_map, labeler, self._variable_symbol_map, soscondata) if modelSOS.sosType: for key in modelSOS.sosType: self._active_cplex_instance.SOS.add(type = modelSOS.sosType[key], name = modelSOS.sosName[key], SOS = [modelSOS.varnames[key], modelSOS.weights[key]]) self._referenced_variable_ids.update(modelSOS.varids[key]) self._used_sos_constraints = True self._active_cplex_instance.linear_constraints.add( lin_expr=expressions, senses=senses, rhs=rhss, range_values=range_values, names=names) for index in xrange(len(qexpressions)): self._active_cplex_instance.quadratic_constraints.add( lin_expr=qlinears[index], quad_expr=qexpressions[index], sense=qsenses[index], rhs=qrhss[index], name=qnames[index]) ############################################# # populate the objective in the cplex model # ############################################# self.compile_objective(pyomo_instance)
def _populate_gurobi_instance(self, pyomo_instance): from pyomo.core.base import Var, Objective, Constraint, SOSConstraint from pyomo.repn import LinearCanonicalRepn, canonical_degree try: grbmodel = Model(name=pyomo_instance.name) except Exception: e = sys.exc_info()[1] msg = 'Unable to create Gurobi model. Have you installed the Python'\ '\n bindings for Gurobi?\n\n\tError message: %s' raise Exception(msg % e) if self._symbolic_solver_labels: labeler = TextLabeler() else: labeler = NumericLabeler('x') # cache to avoid dictionary getitem calls in the loops below. self_symbol_map = self._symbol_map = SymbolMap() pyomo_instance.solutions.add_symbol_map(self_symbol_map) self._smap_id = id(self_symbol_map) # we use this when iterating over the constraints because it # will have a much smaller hash table, we also use this for # the warm start code after it is cleaned to only contain # variables referenced in the constraints self_variable_symbol_map = self._variable_symbol_map = SymbolMap() var_symbol_pairs = [] # maps _VarData labels to the corresponding Gurobi variable object pyomo_gurobi_variable_map = {} self._referenced_variable_ids.clear() # cache to avoid dictionary getitem calls in the loop below. grb_infinity = GRB.INFINITY for var_value in pyomo_instance.component_data_objects(Var, active=True): lb = -grb_infinity ub = grb_infinity if (var_value.lb is not None) and (var_value.lb != -infinity): lb = value(var_value.lb) if (var_value.ub is not None) and (var_value.ub != infinity): ub = value(var_value.ub) # _VarValue objects will not be in the symbol map yet, so # avoid some checks. var_value_label = self_symbol_map.createSymbol(var_value, labeler) var_symbol_pairs.append((var_value, var_value_label)) # be sure to impart the integer and binary nature of any variables if var_value.is_integer(): var_type = GRB.INTEGER elif var_value.is_binary(): var_type = GRB.BINARY elif var_value.is_continuous(): var_type = GRB.CONTINUOUS else: raise TypeError( "Invalid domain type for variable with name '%s'. " "Variable is not continuous, integer, or binary.") pyomo_gurobi_variable_map[var_value_label] = \ grbmodel.addVar(lb=lb, \ ub=ub, \ vtype=var_type, \ name=var_value_label) self_variable_symbol_map.addSymbols(var_symbol_pairs) grbmodel.update() # The next loop collects the following component types from the model: # - SOSConstraint # - Objective # - Constraint sos1 = self._capabilities.sos1 sos2 = self._capabilities.sos2 modelSOS = ModelSOS() objective_cntr = 0 # Track the range constraints and their associated variables added by gurobi self._last_native_var_idx = grbmodel.NumVars - 1 range_var_idx = grbmodel.NumVars _self_range_con_var_pairs = self._range_con_var_pairs = [] for block in pyomo_instance.block_data_objects(active=True): gen_obj_canonical_repn = \ getattr(block, "_gen_obj_canonical_repn", True) gen_con_canonical_repn = \ getattr(block, "_gen_con_canonical_repn", True) # Get/Create the ComponentMap for the repn if not hasattr(block, '_canonical_repn'): block._canonical_repn = ComponentMap() block_canonical_repn = block._canonical_repn # SOSConstraints for soscondata in block.component_data_objects(SOSConstraint, active=True, descend_into=False): level = soscondata.level if (level == 1 and not sos1) or \ (level == 2 and not sos2) or \ (level > 2): raise RuntimeError( "Solver does not support SOS level %s constraints" % (level, )) modelSOS.count_constraint(self_symbol_map, labeler, self_variable_symbol_map, pyomo_gurobi_variable_map, soscondata) # Objective for obj_data in block.component_data_objects(Objective, active=True, descend_into=False): if objective_cntr > 1: raise ValueError( "Multiple active objectives found on Pyomo instance '%s'. " "Solver '%s' will only handle a single active objective" \ % (pyomo_instance.cname(True), self.type)) sense = GRB_MIN if (obj_data.is_minimizing()) else GRB_MAX grbmodel.ModelSense = sense obj_expr = LinExpr() if gen_obj_canonical_repn: obj_repn = generate_canonical_repn(obj_data.expr) block_canonical_repn[obj_data] = obj_repn else: obj_repn = block_canonical_repn[obj_data] if isinstance(obj_repn, LinearCanonicalRepn): if obj_repn.constant != None: obj_expr.addConstant(obj_repn.constant) if obj_repn.linear != None: for i in xrange(len(obj_repn.linear)): var_coefficient = obj_repn.linear[i] var_value = obj_repn.variables[i] self._referenced_variable_ids.add(id(var_value)) label = self_variable_symbol_map.getSymbol( var_value) obj_expr.addTerms(var_coefficient, pyomo_gurobi_variable_map[label]) else: if 0 in obj_repn: # constant term obj_expr.addConstant(obj_repn[0][None]) if 1 in obj_repn: # first-order terms hash_to_variable_map = obj_repn[-1] for var_hash, var_coefficient in iteritems( obj_repn[1]): vardata = hash_to_variable_map[var_hash] self._referenced_variable_ids.add(id(vardata)) label = self_variable_symbol_map.getSymbol(vardata) obj_expr.addTerms(var_coefficient, pyomo_gurobi_variable_map[label]) if 2 in obj_repn: obj_expr = QuadExpr(obj_expr) hash_to_variable_map = obj_repn[-1] for quad_repn, coef in iteritems(obj_repn[2]): gurobi_expr = QuadExpr(coef) for var_hash, exponent in iteritems(quad_repn): vardata = hash_to_variable_map[var_hash] self._referenced_variable_ids.add(id(vardata)) gurobi_var = pyomo_gurobi_variable_map\ [self_variable_symbol_map.\ getSymbol(vardata)] gurobi_expr *= gurobi_var if exponent == 2: gurobi_expr *= gurobi_var obj_expr += gurobi_expr degree = canonical_degree(obj_repn) if (degree is None) or (degree > 2): raise ValueError( "gurobi_direct plugin does not support general nonlinear " "objective expressions (only linear or quadratic).\n" "Objective: %s" % (obj_data.cname(True))) # need to cache the objective label, because the # GUROBI python interface doesn't track this. # _ObjectiveData objects will not be in the symbol map # yet, so avoid some checks. self._objective_label = \ self_symbol_map.createSymbol(obj_data, labeler) grbmodel.setObjective(obj_expr, sense=sense) # Constraint for constraint_data in block.component_data_objects( Constraint, active=True, descend_into=False): if (constraint_data.lower is None) and \ (constraint_data.upper is None): continue # not binding at all, don't bother con_repn = None if isinstance(constraint_data, LinearCanonicalRepn): con_repn = constraint_data else: if gen_con_canonical_repn: con_repn = generate_canonical_repn( constraint_data.body) block_canonical_repn[constraint_data] = con_repn else: con_repn = block_canonical_repn[constraint_data] offset = 0.0 # _ConstraintData objects will not be in the symbol # map yet, so avoid some checks. constraint_label = \ self_symbol_map.createSymbol(constraint_data, labeler) trivial = False if isinstance(con_repn, LinearCanonicalRepn): # # optimization (these might be generated on the fly) # constant = con_repn.constant coefficients = con_repn.linear variables = con_repn.variables if constant is not None: offset = constant expr = LinExpr() + offset if coefficients is not None: linear_coefs = list() linear_vars = list() for i in xrange(len(coefficients)): var_coefficient = coefficients[i] var_value = variables[i] self._referenced_variable_ids.add(id(var_value)) label = self_variable_symbol_map.getSymbol( var_value) linear_coefs.append(var_coefficient) linear_vars.append( pyomo_gurobi_variable_map[label]) expr += LinExpr(linear_coefs, linear_vars) else: trivial = True else: if 0 in con_repn: offset = con_repn[0][None] expr = LinExpr() + offset if 1 in con_repn: # first-order terms linear_coefs = list() linear_vars = list() hash_to_variable_map = con_repn[-1] for var_hash, var_coefficient in iteritems( con_repn[1]): var = hash_to_variable_map[var_hash] self._referenced_variable_ids.add(id(var)) label = self_variable_symbol_map.getSymbol(var) linear_coefs.append(var_coefficient) linear_vars.append( pyomo_gurobi_variable_map[label]) expr += LinExpr(linear_coefs, linear_vars) if 2 in con_repn: # quadratic constraint if _GUROBI_VERSION_MAJOR < 5: raise ValueError( "The gurobi_direct plugin does not handle quadratic " "constraint expressions for Gurobi major versions " "< 5. Current version: Gurobi %s.%s%s" % (gurobi.version())) expr = QuadExpr(expr) hash_to_variable_map = con_repn[-1] for quad_repn, coef in iteritems(con_repn[2]): gurobi_expr = QuadExpr(coef) for var_hash, exponent in iteritems(quad_repn): vardata = hash_to_variable_map[var_hash] self._referenced_variable_ids.add(id(vardata)) gurobi_var = pyomo_gurobi_variable_map\ [self_variable_symbol_map.\ getSymbol(vardata)] gurobi_expr *= gurobi_var if exponent == 2: gurobi_expr *= gurobi_var expr += gurobi_expr degree = canonical_degree(con_repn) if (degree is None) or (degree > 2): raise ValueError( "gurobi_direct plugin does not support general nonlinear " "constraint expressions (only linear or quadratic).\n" "Constraint: %s" % (constraint_data.cname(True))) if (not trivial) or (not self._skip_trivial_constraints): if constraint_data.equality: sense = GRB.EQUAL bound = self._get_bound(constraint_data.lower) grbmodel.addConstr(lhs=expr, sense=sense, rhs=bound, name=constraint_label) else: # L <= body <= U if (constraint_data.upper is not None) and \ (constraint_data.lower is not None): grb_con = grbmodel.addRange( expr, self._get_bound(constraint_data.lower), self._get_bound(constraint_data.upper), constraint_label) _self_range_con_var_pairs.append( (grb_con, range_var_idx)) range_var_idx += 1 # body <= U elif constraint_data.upper is not None: bound = self._get_bound(constraint_data.upper) if bound < float('inf'): grbmodel.addConstr(lhs=expr, sense=GRB.LESS_EQUAL, rhs=bound, name=constraint_label) # L <= body else: bound = self._get_bound(constraint_data.lower) if bound > -float('inf'): grbmodel.addConstr(lhs=expr, sense=GRB.GREATER_EQUAL, rhs=bound, name=constraint_label) if modelSOS.sosType: for key in modelSOS.sosType: grbmodel.addSOS(modelSOS.sosType[key], \ modelSOS.varnames[key], \ modelSOS.weights[key] ) self._referenced_variable_ids.update(modelSOS.varids[key]) for var_id in self._referenced_variable_ids: varname = self._variable_symbol_map.byObject[var_id] vardata = self._variable_symbol_map.bySymbol[varname]() if vardata.fixed: if not self._output_fixed_variable_bounds: raise ValueError( "Encountered a fixed variable (%s) inside an active objective " "or constraint expression on model %s, which is usually indicative of " "a preprocessing error. Use the IO-option 'output_fixed_variable_bounds=True' " "to suppress this error and fix the variable by overwriting its bounds in " "the Gurobi instance." % ( vardata.cname(True), pyomo_instance.cname(True), )) grbvar = pyomo_gurobi_variable_map[varname] grbvar.setAttr(GRB.Attr.UB, vardata.value) grbvar.setAttr(GRB.Attr.LB, vardata.value) grbmodel.update() self._gurobi_instance = grbmodel self._pyomo_gurobi_variable_map = pyomo_gurobi_variable_map
class IntervalTightener(PersistentBase): def __init__(self): super(IntervalTightener, self).__init__() self._config = IntervalConfig() self._cmodel = None self._var_map = dict() self._con_map = dict() self._param_map = dict() self._rvar_map = dict() self._rcon_map = dict() self._pyomo_expr_types = cmodel.PyomoExprTypes() self._symbolic_solver_labels: bool = False self._symbol_map = SymbolMap() self._var_labeler = None self._con_labeler = None self._param_labeler = None self._obj_labeler = None self._objective = None @property def config(self): return self._config @config.setter def config(self, val: IntervalConfig): self._config = val def set_instance(self, model, symbolic_solver_labels: Optional[bool] = None): saved_config = self.config saved_update_config = self.update_config self.__init__() self.config = saved_config self.update_config = saved_update_config self._expr_types = cmodel.PyomoExprTypes() if symbolic_solver_labels is not None: self._symbolic_solver_labels = symbolic_solver_labels if self._symbolic_solver_labels: self._var_labeler = TextLabeler() self._con_labeler = TextLabeler() self._param_labeler = TextLabeler() self._obj_labeler = TextLabeler() self._model = model self._cmodel = cmodel.FBBTModel() self.add_block(model) if self._objective is None: self.set_objective(None) def _add_variables(self, variables: List[_GeneralVarData]): if self._symbolic_solver_labels: set_name = True symbol_map = self._symbol_map labeler = self._var_labeler else: set_name = False symbol_map = None labeler = None cmodel.process_pyomo_vars(self._pyomo_expr_types, variables, self._var_map, self._param_map, self._vars, self._rvar_map, set_name, symbol_map, labeler, False) def _add_params(self, params: List[_ParamData]): cparams = cmodel.create_params(len(params)) for ndx, p in enumerate(params): cp = cparams[ndx] cp.value = p.value self._param_map[id(p)] = cp if self._symbolic_solver_labels: for ndx, p in enumerate(params): cp = cparams[ndx] cp.name = self._symbol_map.getSymbol(p, self._param_labeler) def _add_constraints(self, cons: List[_GeneralConstraintData]): cmodel.process_fbbt_constraints(self._cmodel, self._pyomo_expr_types, cons, self._var_map, self._param_map, self._active_constraints, self._con_map, self._rcon_map) if self._symbolic_solver_labels: for c, cc in self._con_map.items(): cc.name = self._symbol_map.getSymbol(c, self._con_labeler) def _add_sos_constraints(self, cons: List[_SOSConstraintData]): if len(cons) != 0: raise NotImplementedError( 'IntervalTightener does not support SOS constraints') def _remove_constraints(self, cons: List[_GeneralConstraintData]): if self._symbolic_solver_labels: for c in cons: self._symbol_map.removeSymbol(c) self._con_labeler.remove_obj(c) for c in cons: cc = self._con_map.pop(c) self._cmodel.remove_constraint(cc) del self._rcon_map[cc] def _remove_sos_constraints(self, cons: List[_SOSConstraintData]): if len(cons) != 0: raise NotImplementedError( 'IntervalTightener does not support SOS constraints') def _remove_variables(self, variables: List[_GeneralVarData]): if self._symbolic_solver_labels: for v in variables: self._symbol_map.removeSymbol(v) self._var_labeler.remove_obj(v) for v in variables: cvar = self._var_map.pop(id(v)) del self._rvar_map[cvar] def _remove_params(self, params: List[_ParamData]): if self._symbolic_solver_labels: for p in params: self._symbol_map.removeSymbol(p) self._param_labeler.remove_obj(p) for p in params: del self._param_map[id(p)] def _update_variables(self, variables: List[_GeneralVarData]): cmodel.process_pyomo_vars(self._pyomo_expr_types, variables, self._var_map, self._param_map, self._vars, self._rvar_map, False, None, None, True) def update_params(self): for p_id, p in self._params.items(): cp = self._param_map[p_id] cp.value = p.value def set_objective(self, obj: _GeneralObjectiveData): if self._symbolic_solver_labels: if self._objective is not None: self._symbol_map.removeSymbol(self._objective) self._obj_labeler.remove_obj(self._objective) super().set_objective(obj) def _set_objective(self, obj: _GeneralObjectiveData): if obj is None: ce = cmodel.Constant(0) sense = 0 else: ce = cmodel.appsi_expr_from_pyomo_expr(obj.expr, self._var_map, self._param_map, self._pyomo_expr_types) if obj.sense is minimize: sense = 0 else: sense = 1 cobj = cmodel.FBBTObjective(ce) cobj.sense = sense self._cmodel.objective = cobj self._objective = obj if self._symbolic_solver_labels and obj is not None: cobj.name = self._symbol_map.getSymbol(obj, self._obj_labeler) def _update_pyomo_var_bounds(self): for cv, v in self._rvar_map.items(): cv_lb = cv.get_lb() cv_ub = cv.get_ub() if -cmodel.inf < cv_lb: v.setlb(cv_lb) v_id = id(v) _v, _lb, _ub, _fixed, _domain, _value = self._vars[v_id] self._vars[v_id] = (_v, cv_lb, _ub, _fixed, _domain, _value) if cv_ub < cmodel.inf: v.setub(cv_ub) v_id = id(v) _v, _lb, _ub, _fixed, _domain, _value = self._vars[v_id] self._vars[v_id] = (_v, _lb, cv_ub, _fixed, _domain, _value) def _deactivate_satisfied_cons(self): if self.config.deactivate_satisfied_constraints: for c, cc in self._con_map.items(): if not cc.active: c.deactivate() def perform_fbbt(self, model: _BlockData, symbolic_solver_labels: Optional[bool] = None): if model is not self._model: self.set_instance(model, symbolic_solver_labels=symbolic_solver_labels) else: if symbolic_solver_labels is not None and symbolic_solver_labels != self._symbolic_solver_labels: raise RuntimeError( 'symbolic_solver_labels can only be changed through the set_instance method. ' 'Please either use set_instance or create a new instance of IntervalTightener.' ) self.update() try: n_iter = self._cmodel.perform_fbbt( self.config.feasibility_tol, self.config.integer_tol, self.config.improvement_tol, self.config.max_iter, self.config.deactivate_satisfied_constraints) finally: # we want to make sure the pyomo model and cmodel stay in sync # even if an exception is raised and caught self._update_pyomo_var_bounds() self._deactivate_satisfied_cons() return n_iter def perform_fbbt_with_seed(self, model: _BlockData, seed_var: _GeneralVarData): if model is not self._model: self.set_instance(model) else: self.update() try: n_iter = self._cmodel.perform_fbbt_with_seed( self._var_map[id(seed_var)], self.config.feasibility_tol, self.config.integer_tol, self.config.improvement_tol, self.config.max_iter, self.config.deactivate_satisfied_constraints) finally: # we want to make sure the pyomo model and cmodel stay in sync # even if an exception is raised and caught self._update_pyomo_var_bounds() self._deactivate_satisfied_cons() return n_iter