Search code examples
pythonpyomo

Modifying the body of a Constraint without calculating its value


I am programming in python 3.6 using pyomo 5.3

I wish to modify the body of a non-indexed Constraint (in case it is not in the standard format I require). The problem is that the value of the constraint at the specific point is calculated when subtracting from the body. However, I require the body in the form of a function because I have to construct an Objective which is the maximum of all non-linear constraints leading to a min-max-problem.

I tried to directly set the body of the constraint passed to the function but I get the output that the attribute cannot be set. Is there a function to set the body of a constraint?

Edit: Here is the solution I found:

constraint._body = ...

I wish to use this to change a change the form of an optimization problem.

(Sorry for the non-english comments)

Here are the functions used in this example:

  1. The first relaxes the domains of all variables to reals.
  2. The second creates a new model by using the epigraph-method. (Here I use ._body to modify the constraints.
def cont_relax_model_same_bounds(model_vars):
    for var in model_vars:
        if str(var.domain) in int_type:
            var.domain = Reals

def epgraph_reformulation_without_bounds(model):

    #Erstelle Epigraph-Modell
    epi_model = model.clone()
    epi_model.alpha_epi = Var(within = Reals)

    #Speichere alle nichtlinearen Restriktionen des usprünglichen Modells in einer Liste
    nonlinear_constrs = []
    for constr in model.component_objects(Constraint):
        if not (constr.body.polynomial_degree() in [0, 1]):
            nonlinear_constrs.append(constr)

    #Speichere alle nichtlinearen Restriktionen des umformulierten Modells in einer Liste
    epi_nonlinear_constrs = []
    for constr in epi_model.component_objects(Constraint):
        if not (constr.body.polynomial_degree() in [0, 1]):
            epi_nonlinear_constrs.append(constr)

    #Kontrollausgabe, ob die Restriktionen richtig in der Liste gespeichert werden        
    for k, constr in enumerate(epi_nonlinear_constrs):
        print(epi_nonlinear_constrs[k].body)

    #Formuliere die nichtlinearen Restriktionen neu
    for k, constr in enumerate(nonlinear_constrs):
        epi_nonlinear_constrs[k]._body = (nonlinear_constrs[k].body - epi_model.alpha_epi)

    epi_model.obj = Objective(expr = epi_model.alpha_epi, sense = minimize)
    return epi_model

Here is the original model:

model_ESH = ConcreteModel(name = "Example 1")

model_ESH.x1 = Var(bounds=(1,20), domain=Reals)
model_ESH.x2 = Var(bounds=(1,20), domain=Integers)

model_ESH.obj = Objective(expr=(-1)*model_ESH.x1-model_ESH.x2)

model_ESH.g1 = Constraint(expr=0.15*((model_ESH.x1 - 8)**2)+0.1*((model_ESH.x2 - 6)**2)+0.025*exp(model_ESH.x1)*((model_ESH.x2)**(-2))-5<=0)
model_ESH.g2 = Constraint(expr=(model_ESH.x1)**(-1) + (model_ESH.x2)**(-1) - ((model_ESH.x1)**(0.5)) * ((model_ESH.x2) ** (0.5))+4<=0)
model_ESH.l1 = Constraint(expr=2 * (model_ESH.x1) - 3 * (model_ESH.x2) -2<=0)
model_ESH.pprint()

Then I clone the model and relax the integer variables

NLP_model = model_ESH.clone()

#Relaxiere das Problem und deaktiviere die nichtlinearen Restriktionen
#Das funktioniert schonmal

cont_relax_model_same_bounds(get_model_vars(NLP_model))
NLP_model.pprint()

2 Var Declarations
    x1 : Size=1, Index=None
        Key  : Lower : Value : Upper : Fixed : Stale : Domain
        None :     1 :  None :    20 : False :  True :  Reals
    x2 : Size=1, Index=None
        Key  : Lower : Value : Upper : Fixed : Stale : Domain
        None :     1 :  None :    20 : False :  True :  Reals

1 Objective Declarations
    obj : Size=1, Index=None, Active=True
        Key  : Active : Sense    : Expression
        None :   True : minimize :  - x1 - x2

3 Constraint Declarations
    g1 : Size=1, Index=None, Active=True
        Key  : Lower : Body                                                                             : Upper : Active
        None :  -Inf : -5 + 0.15*( -8 + x1 )**2.0 + 0.1*( -6 + x2 )**2.0 + 0.025 * exp( x1 ) * x2**-2.0 :   0.0 :   True
    g2 : Size=1, Index=None, Active=True
        Key  : Lower : Body                                        : Upper : Active
        None :  -Inf : 4 + x1**-1.0 + x2**-1.0 - x1**0.5 * x2**0.5 :   0.0 :   True
    l1 : Size=1, Index=None, Active=True
        Key  : Lower : Body             : Upper : Active
        None :  -Inf : -2 + 2*x1 - 3*x2 :   0.0 :   True

6 Declarations: x1 x2 obj g1 g2 l1

Now I use my function for changing/modifying the model:

epi_model_ESH = epgraph_reformulation_without_bounds(NLP_model)
epi_model_ESH.pprint()

WARNING: Implicitly replacing the Component attribute obj (type=<class
    'pyomo.core.base.objective.SimpleObjective'>) on block Example 1 with a
    new Component (type=<class 'pyomo.core.base.objective.SimpleObjective'>).
    This is usually indicative of a modelling error. To avoid this warning,
    use block.del_component() and block.add_component().
3 Var Declarations
    alpha_epi : Size=1, Index=None
        Key  : Lower : Value : Upper : Fixed : Stale : Domain
        None :  None :  None :  None : False :  True :  Reals
    x1 : Size=1, Index=None
        Key  : Lower : Value             : Upper : Fixed : Stale : Domain
        None :     1 : 8.636750397018059 :    20 : False : False :  Reals
    x2 : Size=1, Index=None
        Key  : Lower : Value              : Upper : Fixed : Stale : Domain
        None :     1 : 12.335071455814422 :    20 : False : False :  Reals

1 Objective Declarations
    obj : Size=1, Index=None, Active=True
        Key  : Active : Sense    : Expression
        None :   True : minimize :  alpha_epi

3 Constraint Declarations
    g1 : Size=1, Index=None, Active=True
        Key  : Lower : Body                                                                                         : Upper : Active
        None :  -Inf : -5 + 0.15*( -8 + x1 )**2.0 + 0.1*( -6 + x2 )**2.0 + 0.025 * exp( x1 ) * x2**-2.0 - alpha_epi :   0.0 :   True
    g2 : Size=1, Index=None, Active=True
        Key  : Lower : Body                                                    : Upper : Active
        None :  -Inf : 4 + x1**-1.0 + x2**-1.0 - x1**0.5 * x2**0.5 - alpha_epi :   0.0 :   True
    l1 : Size=1, Index=None, Active=True
        Key  : Lower : Body             : Upper : Active
        None :  -Inf : -2 + 2*x1 - 3*x2 :   0.0 :   True

7 Declarations: x1 x2 g1 g2 l1 alpha_epi obj

However, if I try to use IPOPT to solve the newly created model I get the following error:

opt = SolverFactory('ipopt')
#opt.options['bonmin.algorithm'] = 'Bonmin'
print('using IPOPT')
# Set Options for solver.
#opt.options['bonmin.solution_limit'] = '1'
#opt.options['bonmin.time_limit'] = 1800
results = opt.solve(epi_model_ESH, tee = True)
results.write()

using IPOPT
ERROR: Variable 'x1' is not part of the model being written out, but appears
    in an expression used on this model.
ERROR: Variable 'x2' is not part of the model being written out, but appears
    in an expression used on this model.

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-22-dcc4023897c3> in <module>
      5 #opt.options['bonmin.solution_limit'] = '1'
      6 #opt.options['bonmin.time_limit'] = 1800
----> 7 results = opt.solve(epi_model_ESH, tee = True)
      8 results.write()

~\Anaconda3\envs\seminarorsteinss2019\lib\site-packages\pyomo\opt\base\solvers.py in solve(self, *args, **kwds)
    594             initial_time = time.time()
    595 
--> 596             self._presolve(*args, **kwds)
    597 
    598             presolve_completion_time = time.time()

~\Anaconda3\envs\seminarorsteinss2019\lib\site-packages\pyomo\opt\solver\shellcmd.py in _presolve(self, *args, **kwds)
    194         self._keepfiles = kwds.pop("keepfiles", False)
    195 
--> 196         OptSolver._presolve(self, *args, **kwds)
    197 
    198         #

~\Anaconda3\envs\seminarorsteinss2019\lib\site-packages\pyomo\opt\base\solvers.py in _presolve(self, *args, **kwds)
    691                                       self._problem_format,
    692                                       self._valid_problem_formats,
--> 693                                       **kwds)
    694             total_time = time.time() - write_start_time
    695             if self._report_timing:

~\Anaconda3\envs\seminarorsteinss2019\lib\site-packages\pyomo\opt\base\solvers.py in _convert_problem(self, args, problem_format, valid_problem_formats, **kwds)
    762                                valid_problem_formats,
    763                                self.has_capability,
--> 764                                **kwds)
    765 
    766     def _default_results_format(self, prob_format):

~\Anaconda3\envs\seminarorsteinss2019\lib\site-packages\pyomo\opt\base\convert.py in convert_problem(args, target_problem_type, valid_problem_types, has_capability, **kwds)
    108                     tmpkw = kwds
    109                     tmpkw['capabilities'] = has_capability
--> 110                     problem_files, symbol_map = converter.apply(*tmp, **tmpkw)
    111                     return problem_files, ptype, symbol_map
    112 

~\Anaconda3\envs\seminarorsteinss2019\lib\site-packages\pyomo\solvers\plugins\converter\model.py in apply(self, *args, **kwds)
    190                             format=args[1],
    191                             solver_capability=capabilities,
--> 192                             io_options=io_options)
    193                 return (problem_filename,), symbol_map_id
    194             else:

~\Anaconda3\envs\seminarorsteinss2019\lib\site-packages\pyomo\core\base\block.py in write(self, filename, format, solver_capability, io_options)
   1645                                           filename,
   1646                                           solver_capability,
-> 1647                                           io_options)
   1648         smap_id = id(smap)
   1649         if not hasattr(self, 'solutions'):

~\Anaconda3\envs\seminarorsteinss2019\lib\site-packages\pyomo\repn\plugins\ampl\ampl_.py in __call__(self, model, filename, solver_capability, io_options)
    390                     skip_trivial_constraints=skip_trivial_constraints,
    391                     file_determinism=file_determinism,
--> 392                     include_all_variable_bounds=include_all_variable_bounds)
    393 
    394         self._symbolic_solver_labels = False

~\Anaconda3\envs\seminarorsteinss2019\lib\site-packages\pyomo\repn\plugins\ampl\ampl_.py in _print_model_NL(self, model, solver_capability, show_section_timing, skip_trivial_constraints, file_determinism, include_all_variable_bounds)
    959                         ampl_repn,
    960                         list(self_varID_map[id(var)] for var in ampl_repn._linear_vars),
--> 961                         list(self_varID_map[id(var)] for var in ampl_repn._nonlinear_vars))
    962                 except KeyError as err:
    963                     self._symbolMapKeyError(err, model, self_varID_map,

~\Anaconda3\envs\seminarorsteinss2019\lib\site-packages\pyomo\repn\plugins\ampl\ampl_.py in <genexpr>(.0)
    959                         ampl_repn,
    960                         list(self_varID_map[id(var)] for var in ampl_repn._linear_vars),
--> 961                         list(self_varID_map[id(var)] for var in ampl_repn._nonlinear_vars))
    962                 except KeyError as err:
    963                     self._symbolMapKeyError(err, model, self_varID_map,

KeyError: (200822968, "Variable 'x1' is not part of the model being written out, but appears in an expression used on this model.", "Variable 'x2' is not part of the model being written out, but appears in an expression used on this model.")

The pprint() of the new model still lists x1 and x2 as variables.

Did me using constraint._body = ... cause this?


Solution

  • I found the mistake.

    In

    epi_nonlinear_constrs[k]._body = (epi_nonlinear_constrs[k].body - epi_model.alpha_epi)   
    

    I used the bodies of the constraints of the other model nonlinear_constrs[k].body instead of those of the same model. Therefore, the constraints had variables which were not referenced in the model. Hence, the error message from the solver.