Changeset 2658


Ignore:
Timestamp:
Jun 14, 2010 6:02:07 PM (11 years ago)
Author:
prsteel
Message:

Changes to coopr.pyomo to support SOS constraints.

util.py was altered to attach the 'has_capability' function from the chosen
solver to the model, which allows the writer to know whether the solver
supports SOS constraints or not.

cpxlp.py was altered to output SOS constraints in CPLEX format when asked.

constraint.py was altered to add a new base class for constraint objects, ConstraintBase?, as well as a new derived constraint, SOSConstraint.

Location:
coopr.pyomo/trunk/coopr/pyomo
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • coopr.pyomo/trunk/coopr/pyomo/base/constraint.py

    r2656 r2658  
    99#  _________________________________________________________________________
    1010
    11 __all__ = ['Objective', 'Constraint']
     11__all__ = ['Objective', 'Constraint', 'SOSConstraint']
    1212
    1313from numvalue import *
     
    1616from pyutilib.component.core import alias
    1717import pyomo
    18 from var import Var, _VarValue
     18from var import Var, _VarValue, _VarArray, _VarElement
    1919from indexed_component import IndexedComponent
    20 from sets import _BaseSet
     20from sets import _BaseSet, _SetContainer, _ProductSet
    2121import pyutilib.misc
    2222import pyutilib.math
     
    226226
    227227
    228 class ConstraintBase(Component):
     228class ConstraintBase(IndexedComponent):
    229229    """
    230     Abstract base class for all constaint types. Keeps track of how many
    231     constaint objects are in existence.
     230    Abstract base class for all constraint types. Keeps track of how many
     231    constraint objects are in existence.
    232232    """
    233233
     
    238238
    239239    def __init__(self, *args, **kwargs):
    240         ctype = kwargs.pop("ctype", None)
    241         if ctype is None:
    242             raise ValueError, "Keyword argument 'ctype' must not be " + \
    243                   "'None'; must specify a class for the component type"
    244         Component.__init__(self, ctype)
     240       
     241        ctype = {'ctype': kwargs.pop("ctype", None)}
     242        IndexedComponent.__init__(self, *args, **ctype)
    245243
    246244        tmpname  = kwargs.pop('name', 'unknown')
     
    290288
    291289
    292 class Constraint(NumericValue, IndexedComponent, ConstraintBase):
     290class Constraint(NumericValue, ConstraintBase):
    293291    """An object that defines a objective expression"""
    294292
     
    311309        # pass Constraint
    312310        tkwd = { 'ctype' : kwargs.pop('ctype', Constraint) }
    313         IndexedComponent.__init__(self, *args, **tkwd)
     311        ConstraintBase.__init__(self, *args, **tkwd)
    314312
    315313        # Pass some arguments to ConstraintBase
     
    317315        tkwd['name'] = kwargs.get('name', 'unknown')
    318316
    319         # Call constraint base class constructor
    320         ConstraintBase.__init__(self, *args, **tkwd)
    321        
    322317        tmpname  = kwargs.pop('name', 'unknown')
    323318        tmprule  = kwargs.pop('rule', None )
     
    711706
    712707
     708class SOSConstraint(ConstraintBase):
     709    """
     710    Represents an SOS-n constraint.
     711
     712    Usage:
     713    model.C1 = SOSConstraint(
     714                             [...],
     715                             var=VAR,
     716                             [set=SET OR index=SET],
     717                             [sos=N OR level=N]
     718                             )
     719        [...] Any number of sets used to index SET
     720        VAR   The set of variables making up the SOS. Indexed by SET.
     721        SET   The set used to index VAR. SET is optionally indexed by
     722              the [...] sets. If SET is not specified, VAR is indexed
     723              over the set(s) it was defined with.
     724        N     This constraint is an SOS-N constraint. Defaults to 1.
     725
     726    Example:
     727   
     728      model = Model()
     729      model.A = Set()
     730      model.B = Set(A)
     731      model.X = Set(B)
     732
     733      model.C1 = SOSConstraint(model.A, var=model.X, set=model.B, sos=1)
     734
     735    This constraint actually creates one SOS-1 constraint for each
     736    element of model.A (e.g., if |A| == N, there are N constraints).
     737    In each constraint, model.X is indexed by the elements of
     738    model.D[a], where 'a' is the current index of model.A.
     739
     740      model = Model()
     741      model.A = Set()
     742      model.X = Var(model.A)
     743
     744      model.C2 = SOSConstraint(var=model.X, sos=2)
     745
     746    This produces exactly one SOS-2 constraint using all the variables
     747    in model.X.
     748    """
     749
     750
     751    alias("SOSConstraint", "SOS constraint expressions in a model.")
     752
     753    def __init__(self, *args, **kwargs):
     754
     755        name = kwargs.get('name', 'unknown')
     756
     757        # Get the 'var' parameter
     758        sosVars = kwargs.pop('var', None)
     759
     760        # Get the 'set' or 'index' parameters
     761        if 'set' in kwargs and 'index' in kwargs:
     762            raise TypeError, "Specify only one of 'set' and 'index' -- " + \
     763                  "they are equivalent parameters"
     764        sosSet = kwargs.pop('set', None)
     765        sosSet = kwargs.pop('index', sosSet)
     766
     767        # Get the 'sos' or 'level' parameters
     768        if 'sos' in kwargs and 'index' in kwargs:
     769            raise TypeError, "Specify only one of 'sos' and 'level' -- " + \
     770                  "they are equivalent parameters"
     771        sosLevel = kwargs.pop('sos', 1)
     772        sosLevel = kwargs.pop('level', sosLevel)
     773
     774        # Make sure we have a variable
     775        if sosVars is None:
     776            raise TypeError, "SOSConstraint() requires the 'var' keyword " + \
     777                  "be specified"
     778
     779        # Find the default sets for sosVars if sosSets is None
     780        if sosSet is None:
     781            sosSet = sosVars.index()
     782
     783        # Construct parents
     784        kwargs['ctype'] = kwargs.get('ctype', SOSConstraint)
     785        ConstraintBase.__init__(self, *args, **kwargs)
     786
     787        # Set member attributes
     788        self._sosVars = sosVars
     789        self._sosSet = sosSet
     790        self._sosLevel = sosLevel
     791
     792        # TODO figure out why exactly the code expects this to be defined
     793        # likely due to Numericvalue
     794        self.domain = None
     795
     796        self._data = {}
     797        self._data[name] = self
     798
     799        # TODO should variables be ordered?
     800
     801    def sos_level(self):
     802        """ Return n, where this class is an SOS-n constraint """
     803        return self._sosLevel
     804
     805    def sos_vars(self):
     806        """ Return the variables in the SOS """
     807        return self._sosVars
     808
     809    def sos_set(self):
     810        """ Return the set used to index the variables """
     811        return self._sosSet
     812
     813    def sos_set_set(self):
     814        """ Return the sets used to index the sets indexing the variables """
     815        return self._index
  • coopr.pyomo/trunk/coopr/pyomo/io/cpxlp.py

    r2630 r2658  
    1414
    1515from coopr.opt import ProblemFormat
    16 from coopr.pyomo.base import expr, Var, Constraint, Objective, NumericConstant
     16from coopr.pyomo.base import expr, Var, Constraint, Objective, \
     17     NumericConstant, SOSConstraint
    1718from coopr.pyomo.base.var import _VarValue, _VarBase
    1819from coopr.pyomo.base.param import _ParamValue
     
    5253        # integer and binary (and continuous) variables to appear in a
    5354        # single continuous block.
     55        #
     56        # SOS constraints must come after Bounds, General, Binary, and
     57        # Semi-Continuous sections
     58        #
    5459        self._output_continuous_variables = True
    5560        self._output_integer_variables = True
     
    217222                print `arg`
    218223                raise ValueError, "Unknown expression sub-type found in quadratic objective expression"   
    219    
     224
     225    @staticmethod
     226    def printSOS(con, OUTPUT, name, index=None):
     227        """
     228        Prints the SOS constraint associated with con. If specified,
     229        index is passed to con.sos_set().
     230
     231        Arguments:
     232        con    The SOS constraint object
     233        OUTPUT The output stream to print to
     234        name   The name of the variable
     235        index  [Optional] the index to pass to the sets indexing the variables.
     236        """
     237
     238        # The name of the variable being indexed
     239        varName = str(con.sos_vars())
     240
     241        # The list of variable names to be printed, including indices
     242        varNames = []
     243
     244        # Get all the variables
     245        if index is None:
     246            tmpSet = con.sos_set()
     247        else:
     248            tmpSet = con.sos_set()[index]
     249        for x in tmpSet:
     250            strX = str(x)
     251            if strX[0] == "(":
     252                # its a tuple, remove whitespace
     253                varNames.append(varName + strX.replace(" ",""))
     254            else:
     255                # its a single number, add parenthesis
     256                varNames.append(varName + "(" + strX + ")")
     257
     258        # We need to 'weight' each variable
     259        # For now we just increment a counter
     260        resultStr = ""
     261        for i in range(0, len(varNames)):
     262            resultStr += varNames[i] + ":" + str(i+1) + " "
     263
     264        conNameIndex = ""
     265        if index is not None:
     266            conNameIndex = str(index)
     267
     268        # Print to CPLEX file
     269        print >>OUTPUT, "    " + str(name) + conNameIndex + ": S" + \
     270                          str(con.sos_level()) + ":: " + resultStr
    220271
    221272    def _print_model_LP(self, model, OUTPUT):
    222 
    223273        _obj = model.active_components(Objective)
    224274       
     
    424474                          print >>OUTPUT, "   ", prefix+convert_name(var[ndx].label)
    425475
     476
     477        #
     478        # SOS constraints
     479        #
     480        # For now, we write out SOS1 and SOS2 constraints in the cplex format
     481        #
     482        # All Component objects are stored in model._component, which is a
     483        # dictionary of {class: {objName: object}}.
     484        #
     485        # Consider the variable X,
     486        #
     487        #   model.X = Var(...)
     488        #
     489        # We print X to CPLEX format as X(i,j,k,...) where i, j, k, ... are the
     490        # indices of X.
     491        #
     492        # TODO Allow users to specify the variables coefficients for custom
     493        # branching/set orders
     494        #
     495        #
     496        sosn = model.has_capability("sosn")
     497        sos1 = model.has_capability("sos1")
     498        sos2 = model.has_capability("sos2")
     499        print sos1
     500        if sosn or sos1 or sos2:
     501            writtenSOS = False
     502            constrs = model._component.get(SOSConstraint, {})
     503            for name in constrs:
     504                con = constrs[name]
     505                level = con.sos_level()
     506                if (level == 1 and sos1) or (level == 2 and sos2) or (sosn):
     507                    if writtenSOS == False:
     508                        print >>OUTPUT, "SOS"
     509                        writtenSOS = True
     510                    masterIndex = con.sos_set_set()
     511                    if None in masterIndex:
     512                        # A single constraint
     513                        self.printSOS(con, OUTPUT, name)
     514                    else:
     515                        # A series of indexed constraints
     516                        for index in masterIndex:
     517                            self.printSOS(con, OUTPUT, name, index)
     518
    426519        #
    427520        # wrap-up
     
    433526           #
    434527           print >>OUTPUT, "end "
    435        
  • coopr.pyomo/trunk/coopr/pyomo/scripting/util.py

    r2642 r2658  
    327327        solver, subsolver = solver.split(':')
    328328    opt = SolverFactory( solver )
     329
     330    # let the model know of our chosen solver's capabilities
     331    instance.has_capability = opt.has_capability
     332
    329333    if opt is None:
    330334       raise ValueError, "Problem constructing solver `"+str(solver)+"'"
Note: See TracChangeset for help on using the changeset viewer.