1#  ___________________________________________________________________________
2#
3#  Pyomo: Python Optimization Modeling Objects
4#  Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC
5#  Under the terms of Contract DE-NA0003525 with National Technology and
6#  Engineering Solutions of Sandia, LLC, the U.S. Government retains certain
7#  rights in this software.
8#  This software is distributed under the 3-clause BSD License.
9#  ___________________________________________________________________________
10
11from pyomo.core.base.PyomoModel import ConcreteModel
12from pyomo.solvers.plugins.solvers.xpress_direct import XpressDirect
13from pyomo.solvers.plugins.solvers.persistent_solver import PersistentSolver
14from pyomo.core.expr.numvalue import value, is_fixed
15from pyomo.core.expr import current as EXPR
16from pyomo.opt.base import SolverFactory
17import collections
18
19
20@SolverFactory.register('xpress_persistent', doc='Persistent python interface to Xpress')
21class XpressPersistent(PersistentSolver, XpressDirect):
22    """
23    A class that provides a persistent interface to Xpress. Direct solver interfaces do not use any file io.
24    Rather, they interface directly with the python bindings for the specific solver. Persistent solver interfaces
25    are similar except that they "remember" their model. Thus, persistent solver interfaces allow incremental changes
26    to the solver model (e.g., the gurobi python model or the cplex python model). Note that users are responsible
27    for notifying the persistent solver interfaces when changes are made to the corresponding pyomo model.
28
29    Keyword Arguments
30    -----------------
31    model: ConcreteModel
32        Passing a model to the constructor is equivalent to calling the set_instance mehtod.
33    type: str
34        String indicating the class type of the solver instance.
35    name: str
36        String representing either the class type of the solver instance or an assigned name.
37    doc: str
38        Documentation for the solver
39    options: dict
40        Dictionary of solver options
41    """
42
43    def __init__(self, **kwds):
44        kwds['type'] = 'xpress_persistent'
45        XpressDirect.__init__(self, **kwds)
46
47        self._pyomo_model = kwds.pop('model', None)
48        if self._pyomo_model is not None:
49            self.set_instance(self._pyomo_model, **kwds)
50
51    def _remove_constraint(self, solver_con):
52        self._solver_model.delConstraint(solver_con)
53
54    def _remove_sos_constraint(self, solver_sos_con):
55        self._solver_model.delSOS(solver_sos_con)
56
57    def _remove_var(self, solver_var):
58        self._solver_model.delVariable(solver_var)
59
60    def _warm_start(self):
61        XpressDirect._warm_start(self)
62
63    def _xpress_chgcoltype_from_var(self, var):
64        """
65        This function takes a pyomo variable and returns the appropriate xpress variable type
66        for use in xpress.problem.chgcoltype
67        :param var: pyomo.core.base.var.Var
68        :return: xpress.continuous or xpress.binary or xpress.integer
69        """
70        if var.is_binary():
71            vartype = 'B'
72        elif var.is_integer():
73            vartype = 'I'
74        elif var.is_continuous():
75            vartype = 'C'
76        else:
77            raise ValueError('Variable domain type is not recognized for {0}'.format(var.domain))
78        return vartype
79
80    def update_var(self, var):
81        """Update a single variable in the solver's model.
82
83        This will update bounds, fix/unfix the variable as needed, and
84        update the variable type.
85
86        Parameters
87        ----------
88        var: Var (scalar Var or single _VarData)
89
90        """
91        # see PR #366 for discussion about handling indexed
92        # objects and keeping compatibility with the
93        # pyomo.kernel objects
94        #if var.is_indexed():
95        #    for child_var in var.values():
96        #        self.update_var(child_var)
97        #    return
98        if var not in self._pyomo_var_to_solver_var_map:
99            raise ValueError('The Var provided to update_var needs to be added first: {0}'.format(var))
100        xpress_var = self._pyomo_var_to_solver_var_map[var]
101        qctype = self._xpress_chgcoltype_from_var(var)
102        lb, ub = self._xpress_lb_ub_from_var(var)
103
104        self._solver_model.chgbounds([xpress_var, xpress_var], ['L', 'U'], [lb, ub])
105        self._solver_model.chgcoltype([xpress_var], [qctype])
106
107    def _add_column(self, var, obj_coef, constraints, coefficients):
108        """Add a column to the solver's model
109
110        This will add the Pyomo variable var to the solver's
111        model, and put the coefficients on the associated
112        constraints in the solver model. If the obj_coef is
113        not zero, it will add obj_coef*var to the objective
114        of the solver's model.
115
116        Parameters
117        ----------
118        var: Var (scalar Var or single _VarData)
119        obj_coef: float
120        constraints: list of solver constraints
121        coefficients: list of coefficients to put on var in the associated constraint
122        """
123
124        ## set-up add var
125        varname = self._symbol_map.getSymbol(var, self._labeler)
126        vartype = self._xpress_chgcoltype_from_var(var)
127        lb, ub = self._xpress_lb_ub_from_var(var)
128
129        self._solver_model.addcols(objx=[obj_coef], mstart=[0,len(coefficients)],
130                                    mrwind=constraints, dmatval=coefficients,
131                                    bdl=[lb], bdu=[ub], names=[varname],
132                                    types=[vartype])
133
134        xpress_var = self._solver_model.getVariable(
135                        index=self._solver_model.getIndexFromName(type=2, name=varname))
136
137        self._pyomo_var_to_solver_var_map[var] = xpress_var
138        self._solver_var_to_pyomo_var_map[xpress_var] = var
139        self._referenced_variables[var] = len(coefficients)
140
141    def get_xpress_attribute(self, *args):
142        """
143        Get xpress atrributes.
144
145        Parameters
146        ----------
147        control(s): str, strs, list, None
148            The xpress attribute to get. Options include any xpress attribute.
149            Can also be list of xpress controls or None for every atrribute
150            Please see the Xpress documentation for options.
151
152        See the Xpress documentation for xpress.problem.getAttrib for other
153        uses of this function
154
155        Returns
156        -------
157        control value or dictionary of control values
158        """
159        return self._solver_model.getAttrib(*args)
160
161    def set_xpress_control(self, *args):
162        """
163        Set xpress controls.
164
165        Parameters
166        ----------
167        control: str
168            The xpress control to set. Options include any xpree control.
169            Please see the Xpress documentation for options.
170        val: any
171            The value to set the control to. See Xpress documentation for possible values.
172
173        If one argument, it must be a dictionary with control keys and control values
174        """
175        self._solver_model.setControl(*args)
176
177    def get_xpress_control(self, *args):
178        """
179        Get xpress controls.
180
181        Parameters
182        ----------
183        control(s): str, strs, list, None
184            The xpress control to get. Options include any xpress control.
185            Can also be list of xpress controls or None for every contorl
186            Please see the Xpress documentation for options.
187
188        See the Xpress documentation for xpress.problem.getControl for other
189        uses of this function
190
191        Returns
192        -------
193        control value or dictionary of control values
194        """
195        return self._solver_model.getControl(*args)
196
197    def write(self, filename, flags=''):
198        """
199        Write the model to a file (e.g., a lp file).
200
201        Parameters
202        ----------
203        filename: str
204            Name of the file to which the model should be written.
205        flags: str
206            Flags for xpress.problem.write
207        """
208        self._solver_model.write(filename, flags)
209