-
Notifications
You must be signed in to change notification settings - Fork 518
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New add_column() method for appsi solvers #2804
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -803,6 +803,34 @@ | |
def add_block(self, block: _BlockData): | ||
pass | ||
|
||
@abc.abstractmethod | ||
def add_column( | ||
self, | ||
var: _GeneralVarData, | ||
obj_coef: float, | ||
constraints: List[_GeneralConstraintData], | ||
coefficients: List[float], | ||
): | ||
"""Add a column to the solver's model | ||
|
||
Add the Pyomo variable var to the solver's model, and put the | ||
coefficients on the associated constraints in the solver model. | ||
If the obj_coef is not zero, add obj_coef*var to the objective | ||
of the solver's model. | ||
|
||
The column will have already been added to the Pyomo model before | ||
this method is called, such that the variable is already incorporated | ||
into the passed in constraints. | ||
|
||
Parameters | ||
---------- | ||
var: Var (scalar Var or single _VarData) | ||
obj_coef: float | ||
constraints: list of solver constraints | ||
coefficients: list of coefficients to put on var in the associated constraint | ||
""" | ||
pass | ||
|
||
@abc.abstractmethod | ||
def remove_variables(self, variables: List[_GeneralVarData]): | ||
pass | ||
|
@@ -948,21 +976,18 @@ | |
|
||
def add_variables(self, variables: List[_GeneralVarData]): | ||
for v in variables: | ||
if id(v) in self._referenced_variables: | ||
raise ValueError( | ||
'variable {name} has already been added'.format(name=v.name) | ||
) | ||
self._referenced_variables[id(v)] = [dict(), dict(), None] | ||
self._vars[id(v)] = ( | ||
v, | ||
v._lb, | ||
v._ub, | ||
v.fixed, | ||
v.domain.get_interval(), | ||
v.value, | ||
) | ||
self._cache_variable_data(v) | ||
self._add_variables(variables) | ||
|
||
def _cache_variable_data(self, variable: _GeneralVarData): | ||
v = variable | ||
if id(v) in self._referenced_variables: | ||
raise ValueError( | ||
'variable {name} has already been added'.format(name=v.name) | ||
) | ||
self._referenced_variables[id(v)] = [dict(), dict(), None] | ||
self._vars[id(v)] = (v, v._lb, v._ub, v.fixed, v.domain.get_interval(), v.value) | ||
|
||
@abc.abstractmethod | ||
def _add_params(self, params: List[_ParamData]): | ||
pass | ||
|
@@ -1042,6 +1067,43 @@ | |
self._referenced_variables[id(v)][1][con] = None | ||
self._add_sos_constraints(cons) | ||
|
||
def _add_column( | ||
self, | ||
var: _GeneralVarData, | ||
obj_coef: float, | ||
constraints: List[_GeneralConstraintData], | ||
coefficients: List[float], | ||
): | ||
"""Add a column to the solver's model | ||
|
||
Add the Pyomo variable var to the solver's model, and put the | ||
coefficients on the associated constraints in the solver model. | ||
If the obj_coef is not zero, add obj_coef*var to the objective | ||
of the solver's model. | ||
|
||
The column will have already been added to the Pyomo model before | ||
this method is called, such that the variable is already incorporated | ||
into the passed in constraints. | ||
|
||
Parameters | ||
---------- | ||
var: Var (scalar Var or single _VarData) | ||
obj_coef: float | ||
constraints: list of solver constraints | ||
coefficients: list of coefficients to put on var in the associated constraint | ||
""" | ||
|
||
# This method is intended to be overridden by derived classes when | ||
# it can be done more efficiently than the default implementation found | ||
# here. The default implementation replaces the existing objective and | ||
# constraints. | ||
self._add_variables([var]) | ||
if obj_coef != 0.0: | ||
self._set_objective(self._objective) | ||
# Constraint objects have already been updated, just replace them | ||
self._remove_constraints(constraints) | ||
self._add_constraints(constraints) | ||
|
||
@abc.abstractmethod | ||
def _set_objective(self, obj: _GeneralObjectiveData): | ||
pass | ||
|
@@ -1459,13 +1521,81 @@ | |
if need_to_set_objective: | ||
self.set_objective(pyomo_obj) | ||
timer.stop('objective') | ||
|
||
# this has to be done after the objective and constraints in case the | ||
# old objective/constraints use old variables | ||
timer.start('vars') | ||
self.remove_variables(old_vars) | ||
timer.stop('vars') | ||
|
||
def add_column( | ||
self, | ||
var: _GeneralVarData, | ||
obj_coef: float, | ||
constraints: List[_GeneralConstraintData], | ||
coefficients: List[float], | ||
timer: HierarchicalTimer = None, | ||
): | ||
"""Add a column to Pyomo model and solver model. | ||
|
||
This method takes a Pyomo variable var that has already been | ||
added to the Pyomo model and adds it to the Pyomo model's objective | ||
and constraints. It will then add the variable to the solver's model, | ||
including changes to the objective and constraints. | ||
|
||
Parameters | ||
---------- | ||
model: pyomo ConcreteModel to which the column will be added | ||
var: Var (scalar Var or single _VarData) | ||
obj_coef: float, pyo.Param | ||
constraints: list of scalar Constraints of single _ConstraintDatas | ||
coefficients: list of the coefficient to put on var in the associated constraint | ||
|
||
""" | ||
if timer is None: | ||
timer = HierarchicalTimer() | ||
|
||
timer.start('add column') | ||
|
||
# First we update the Pyomo model and the PersistentBase data | ||
timer.start('update Pyomo model') | ||
|
||
# Add the column's data to cached model info. | ||
# We do this before calling update() so that the variable | ||
# won't be added to the solver model at this time. | ||
self._cache_variable_data(var) | ||
vid = id(var) | ||
|
||
# Make sure the model is up to date before adding the new column | ||
self.update(timer) | ||
|
||
# Add the variable to the objective | ||
if obj_coef != 0.0 and self._objective is not None: | ||
# Add variable to the pyomo objective | ||
self._objective.expr = self._objective.expr + obj_coef * var | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is certainly convenient, but I'm not sure the solver interface should ever modify the pyomo model... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having said that, I don't think there is a better way to do this. At least the doc string is clear. |
||
# Update cache | ||
self._objective_expr = self._objective.expr | ||
self._referenced_variables[vid][2] = self._objective | ||
self._vars_referenced_by_obj.append(var) | ||
|
||
# Add the variable to each constraint | ||
for con, coef in zip(constraints, coefficients): | ||
if coef != 0.0: | ||
# Add variable to the pyomo constraint | ||
con.set_value((con.lower, con.body + coef * var, con.upper)) | ||
# Update cache | ||
self._active_constraints[con] = (con.lower, con.body, con.upper) | ||
self._referenced_variables[vid][0][con] = None | ||
self._vars_referenced_by_con[con].append(var) | ||
|
||
timer.stop('update Pyomo model') | ||
|
||
# Add the new column to the solver model | ||
timer.start('update solver model') | ||
self._add_column(var, obj_coef, constraints, coefficients) | ||
timer.stop('update solver model') | ||
|
||
timer.stop('add column') | ||
|
||
|
||
legacy_termination_condition_map = { | ||
TerminationCondition.unknown: LegacyTerminationCondition.unknown, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this correct?