source: coopr.pyomo/stable/coopr/pyomo/base/constraint.py @ 3285

Last change on this file since 3285 was 3285, checked in by wehart, 10 years ago

Merged revisions 3184-3284 via svnmerge from
https://software.sandia.gov/svn/public/coopr/coopr.pyomo/trunk

........

r3188 | jwatson | 2010-10-29 08:09:35 -0600 (Fri, 29 Oct 2010) | 3 lines


Eliminating initial domain check when constructing numeric constants and the default domain (Reals) is specified.

........

r3203 | jwatson | 2010-10-29 14:46:18 -0600 (Fri, 29 Oct 2010) | 3 lines


Fixing bugs in has_discrete_variables() method of PyomoModel?.

........

r3211 | jdsiiro | 2010-11-01 14:49:11 -0600 (Mon, 01 Nov 2010) | 4 lines


bugfixes for Blocks:

  • avoid infinite loop when adding a block to a model
  • support pretty printing of user-defined components

........

r3212 | jdsiiro | 2010-11-02 15:17:13 -0600 (Tue, 02 Nov 2010) | 5 lines


  • cleaning up the management of Block._parent_block and Component.model attributes. Adding & removing blocks now updates the model attribute on all children
  • renaming Block._setattr_exec -> Block._add_component

........

r3213 | jdsiiro | 2010-11-03 10:47:06 -0600 (Wed, 03 Nov 2010) | 2 lines


Bugfix to the PyomoLogHandler? for python 2.4 compatibility

........

r3214 | jdsiiro | 2010-11-03 15:20:47 -0600 (Wed, 03 Nov 2010) | 3 lines


There is no point logging a warning when the problem is encountered
generating a logging.info message: log the warning at the info level.

........

r3215 | wehart | 2010-11-04 23:05:17 -0600 (Thu, 04 Nov 2010) | 3 lines


Adding a simple knapsack example to illustrate the difference between
a concrete and abstract model.

........

r3216 | jwatson | 2010-11-05 09:39:19 -0600 (Fri, 05 Nov 2010) | 3 lines


Fixing error diagnostic when indexing a variable with a bad index.

........

r3219 | jwatson | 2010-11-05 16:01:23 -0600 (Fri, 05 Nov 2010) | 3 lines


Supressing a validation test with NumericConstant?. If the user specifies a value, we are (now) assuming it is actually a numeric value - otherwise, the domain check significantly inflates the run-time associated with expression tree creation. This needs to be revisited in the Coopr 2.5 re-write.

........

r3226 | wehart | 2010-11-06 21:32:59 -0600 (Sat, 06 Nov 2010) | 2 lines


Setting up example, which was never converted.

........

r3233 | wehart | 2010-11-12 15:56:28 -0700 (Fri, 12 Nov 2010) | 4 lines


Migrating OS-specific functionality into coopr.os


Adding coopr.os to the dev.ini config file.

........

r3242 | wehart | 2010-11-13 01:28:57 -0700 (Sat, 13 Nov 2010) | 4 lines


Type fix.


Updating error message.

........

r3244 | wehart | 2010-11-13 10:44:26 -0700 (Sat, 13 Nov 2010) | 2 lines


Skipping OSiL writer when not defined.

........

r3246 | wehart | 2010-11-13 10:54:15 -0700 (Sat, 13 Nov 2010) | 2 lines


bug fix.

........

r3247 | wehart | 2010-11-13 11:14:01 -0700 (Sat, 13 Nov 2010) | 2 lines


Bug fix.

........

r3248 | jwatson | 2010-11-17 13:51:47 -0700 (Wed, 17 Nov 2010) | 3 lines


Interim fixes to output of quadratic terms in LP writer - more to do, but at least the basic examples now work.

........

r3254 | jwatson | 2010-11-19 13:19:19 -0700 (Fri, 19 Nov 2010) | 3 lines


Fixed bug in LP writer involving quadratic terms involving two distinct variables. Added two new quadratic examples.

........

r3257 | jwatson | 2010-11-19 13:59:35 -0700 (Fri, 19 Nov 2010) | 3 lines


Fixing diagnostic error message when attempting to solve quadratic programs with GLPK - code for generating message was not syntatically legal.

........

r3268 | jwatson | 2010-12-01 15:08:28 -0700 (Wed, 01 Dec 2010) | 3 lines


Fixing issues with the Piecewise construct when breakpoints and slopes are generated via rules. Works now (on a sample of size 1 - the newly added example5.py) for non-indexed rules, likely broken for indexed breakpoint/slope rules.

........

r3272 | jwatson | 2010-12-02 13:53:51 -0700 (Thu, 02 Dec 2010) | 3 lines


Adding omitted pprint() method for SOS constraints - identified while debugging a piecewise issue.

........

r3274 | jwatson | 2010-12-02 16:32:29 -0700 (Thu, 02 Dec 2010) | 3 lines


Adding example of Piecewise construct using breakpoint and slope rules, as opposed to explicit/direct lists.

........

r3276 | jwatson | 2010-12-03 14:06:40 -0700 (Fri, 03 Dec 2010) | 3 lines


Some progress toward functional indexed Piecewise components.

........

File size: 26.4 KB
Line 
1#  _________________________________________________________________________
2#
3#  Coopr: A COmmon Optimization Python Repository
4#  Copyright (c) 2008 Sandia Corporation.
5#  This software is distributed under the BSD License.
6#  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
7#  the U.S. Government retains certain rights in this software.
8#  For more information, see the Coopr README.txt file.
9#  _________________________________________________________________________
10
11__all__ = ['Constraint', 'SOSConstraint', 'ConstraintBase']
12
13import sys
14
15from expr import *
16from indexed_component import IndexedComponent
17from numtypes import *
18from numvalue import *
19from numvalue import create_name
20from pyutilib.component.core import alias
21from sets import _BaseSet, _SetContainer, _ProductSet
22from var import Var, _VarValue, _VarArray, _VarElement
23from objective import Objective
24import pyomo
25import pyutilib.math
26import pyutilib.misc
27
28from component import Component
29from set_types import *
30from sets import _SetContainer
31
32class ConstraintBase(IndexedComponent):
33    """
34    Abstract base class for all constraint types. Keeps track of how many
35    constraint objects are in existence.
36    """
37
38    alias("ConstraintBase", "Abstract base class for all model constraints")
39
40    # The number of explicit constraints declared.
41    _nExplicitConstraints = 0
42
43    def __init__(self, *args, **kwargs):
44        if kwargs.pop('_deep_copying', None):
45            # Hack for Python 2.4 compatibility
46            # Deep copy will copy all items as necessary, so no need to
47            # complete parsing
48            return
49
50        ctype = {'ctype': kwargs.pop("ctype", None)}
51        IndexedComponent.__init__(self, *args, **ctype)
52
53        tmpname  = kwargs.pop('name', 'unknown')
54        self.doc = kwargs.pop('doc', None )
55
56        # Increment the class count
57        ConstraintBase._inc_count()
58
59        self._data = {}
60
61    def dim(self):
62        return self._ndim
63
64    def __len__(self):
65        return len(self._data.keys())
66
67    def keys(self):
68        return self._data.keys()
69        #return self._index
70
71    def __contains__(self,ndx):
72        return ndx in self._data
73        #return ndx in self._index
74
75    def __getitem__(self, ndx):
76        """This method returns a ConstraintData object.  This object can be
77           coerced to a numeric value using the value() function, or using
78           explicity coercion with float().
79        """
80        if ndx in self._data:
81            return self._data[ndx]
82        msg = "Unknown index in constraint '%s': %s"
83        raise KeyError, msg % ( self.name, str(ndx) )
84
85    def __iter__(self):
86        return self._data.keys().__iter__()
87
88    def __del__(self):
89        """ Remove this constraint from the count """
90        self._inc_count(-1)
91
92    @classmethod
93    def _inc_count(cls, n=1):
94        """ Alter the constraint count """
95        cls._nExplicitConstraints += n
96
97    @classmethod
98    def num_explicit_constraints(cls):
99        return cls._nExplicitConstraints
100
101
102class ConstraintData(NumericValue):
103
104    def __init__(self, name=None):
105        NumericValue.__init__(self,name=name,domain=Reals)
106        self._equality = False
107        self.lower = None
108        self.lower_ineq_strict = None
109        self.lin_body = None # a linear compilation of the source expressions tree.
110        self.body = None # an expression tree encoding of the constraint body. if None, an alternative representation (e.g., lin_body) will be != None.
111        self.upper = None
112        self.upper_ineq_strict = None
113        self.label = None
114        self.id = None
115        self.active = True
116
117    def __call__(self, exception=True):
118        if self.body is None:
119            return None
120        return self.body()
121
122    def activate(self):
123        self.active=True
124
125    def deactivate(self):
126        self.active=False
127
128
129class Constraint(NumericValue, ConstraintBase):
130    """An object that defines a objective expression"""
131
132    alias("Constraint", "Constraint expressions a model.")
133
134    def __init__(self, *args, **kwargs):
135        """
136        Construct an objective expression with rule to construct the
137        expression
138
139        keyword arguments:
140        name: name of this object
141        rule: function or rule definition of constraint
142        expr: same as rule
143        doc:  documentation string for constraint
144        """
145        if kwargs.pop('_deep_copying', None):
146            # Hack for Python 2.4 compatibility
147            # Deep copy will copy all items as necessary, so no need to
148            # complete parsing
149            return
150
151        # See if 'ctype' was passed as a keyword argument;
152        # this allows derived classses to alert Pyomo to
153        # their existence through super() calls. If not,
154        # pass Constraint
155        tkwd = {'ctype': kwargs.pop('ctype', Constraint)}
156
157        # Pass some arguments to ConstraintBase
158        tkwd['doc'] = kwargs.pop('doc', None)
159        tkwd['name'] = kwargs.get('name', 'unknown')
160
161        ConstraintBase.__init__(self, *args, **tkwd)
162
163        tmpname = kwargs.pop('name', 'unknown')
164        tmprule = kwargs.pop('rule', None )
165        tmprule = kwargs.pop('expr', tmprule )
166
167        self._no_rule_init = kwargs.pop('noruleinit', None )
168
169        if ( kwargs ): # if dict not empty, there's an error.  Let user know.
170            msg = "Creating constraint '%s': unknown option(s)\n\t%s"
171            msg = msg % ( tmpname, ', '.join(kwargs.keys()) )
172            raise ValueError, msg
173
174        # _no_rule_init is a flag specified by the user to indicate that no
175        # construction rule will be specified, and that constraints will be
176        # explicitly added by the user. set via the "noruleinit" keyword. value
177        # doesn't matter, as long as it isn't "None".
178
179        self._data = {}
180
181        NumericValue.__init__(self,name=tmpname)
182        if None in self._data:
183            # As far as I can tell, this never executes? 2010 Jun 08
184            self._data[None].name=tmpname
185        self.rule = tmprule
186        self.trivial = False # True if all constraint indicies have trivial expressions.
187
188    def clear(self):
189        self._data = {}
190
191    def __call__(self, exception=True):
192        if len(self._data) == 0:
193            return None
194        if None in self._data:
195            if self._data[None].body is None:
196                return None
197            return self._data[None].body()
198        if exception:
199            msg = 'Cannot compute the value of an array of constraints'
200            raise ValueError, msg
201
202
203    def construct(self, data=None, simplify=True):
204
205        if pyomo.debug("verbose") or pyomo.debug("normal"):     #pragma:nocover
206            print "Constructing constraint "+self.name
207        if (self._no_rule_init is not None) and (self.rule is not None):
208            msg = 'WARNING: noruleinit keyword is being used in conjunction ' \
209                  "with rule keyword for constraint '%s'; defaulting to "     \
210                  'rule-based construction.'
211            print msg % self.name
212        if self.rule is None:
213            if self._no_rule_init is None:
214                msg = 'WARNING: No construction rule or expression '          \
215                      "specified for 'constraint '%s'"
216                print msg % self.name
217            return
218        if self._constructed:
219            return
220        self._constructed=True
221        #
222        # Local variables for code optimization
223        #
224
225        _self_rule = self.rule
226        #
227        if isinstance(_self_rule,Expression):
228            if not None in self._index:
229                msg = 'Cannot define multiple indices in a constraint with '  \
230                      'a single expression'
231                raise IndexError, msg
232            self.add(None, _self_rule, simplify)
233        else:
234            for val in self._index:
235                if pyomo.debug("verbose"):                      #pragma:nocover
236                    print "   Constructing constraint index "+str(val)
237                if val is None:
238                    expr = _self_rule(self.model)
239                    if expr is None or (type(expr) in (int, long, float) \
240                                        and expr == 0):
241                        continue
242                else:
243                    if type(val) is tuple:
244                        tmp=list(val)
245                    else:
246                        tmp=[val]
247                    tmp.append(self.model)
248                    tmp=tuple(tmp)
249                    expr = _self_rule(*tmp)
250                    if expr is None or (type(expr) in (int, long, float) \
251                                        and expr == 0):
252                        continue
253                ##constructed_indices.append(val)
254                self.add(val, expr, simplify)
255        #self._index=constructed_indices
256
257    def add(self, index, expr, simplify=True):
258
259        # index: the constraint index (should probably be renamed)
260        # expr: the constraint expression
261
262        #
263        # Ignore an 'empty' constraint
264        #
265        if expr is None:
266            return
267
268        # Verify that the user actually provided an expression or tuple/list --
269        # they can (and have) specified odd expressions that evaluate to
270        # unexpected values, e.g., True or False.
271        #
272        if not isinstance(expr, (Expression, list, tuple)):
273            msg = 'Non-expression object of %s supplied to initialize '       \
274                  'constraint %s[%s]'
275            raise ValueError, msg % ( str(type(expr)), self.name, str(index) )
276
277        # Local variables to optimize runtime performance
278        #
279        _self_data = self._data
280        _self_data[index] = ConstraintData(name=create_name(self.name,index))
281        if not type(expr) in [tuple,list]:
282            _expr_args0 = expr._args[0]
283            _expr_args1 = expr._args[1]
284
285            #_expr_args0.pprint()
286            #print "HERE", is_constant(_expr_args0)
287            #_expr_args1.pprint()
288            #print "HERE", is_constant(_expr_args1)
289
290        greater = (_GreaterThanExpression, _GreaterThanOrEqualExpression )
291        less    = (_LessThanExpression,    _LessThanOrEqualExpression )
292        # If the constructor rule returns an equality expression, then
293        # convert to a 3-tuple
294        #
295        if isinstance(expr,_EqualToExpression):
296            _self_data[index]._equality = True
297            if is_constant(_expr_args1):
298                tpl = [ _expr_args1,_expr_args0,_expr_args1, False, False ]
299            elif is_constant(_expr_args1):
300                tpl = [ _expr_args0,_expr_args1,_expr_args0, False, False ]
301            else:
302                tpl = [ 0.0 , _expr_args0 - _expr_args1, 0.0, False, False ]
303        #
304        # If the constructor rule returns a greater-than expression, then
305        # convert to a 3-tuple
306        #
307        elif isinstance(expr, greater):
308
309            if isinstance(_expr_args0, greater ):
310                tpl = [ _expr_args1, _expr_args0._args[1], _expr_args0._args[0],
311                        isinstance(expr,_GreaterThanExpression),
312                        isinstance(_expr_args0,_GreaterThanExpression) ]
313
314            elif isinstance(_expr_args0, less ):
315                tpl = [ _expr_args1, _expr_args0._args[0], _expr_args0._args[1],
316                        isinstance(expr,_GreaterThanExpression),
317                        isinstance(_expr_args0,_LessThanExpression) ]
318
319            elif isinstance(_expr_args1, greater ):
320                tpl = [ _expr_args1._args[1], _expr_args1._args[0], _expr_args0,
321                        isinstance(_expr_args1, _GreaterThanExpression),
322                        isinstance(expr,_GreaterThanExpression) ]
323
324            elif isinstance(_expr_args1, less ):
325                tpl = [ _expr_args1._args[0], _expr_args1._args[1], _expr_args0,
326                        isinstance(_expr_args1,_LessThanExpression),
327                        isinstance(expr,_GreaterThanExpression) ]
328
329            elif isinstance(_expr_args0,_EqualToExpression) or\
330                    isinstance(_expr_args1,_EqualToExpression):
331                msg = 'Bound error: > expression used with an = expression.'
332                raise ValueError, msg
333
334            elif _expr_args0.is_constant():
335                tpl = [ None, _expr_args1, _expr_args0,
336                        True, isinstance(expr,_GreaterThanExpression) ]
337            elif _expr_args1.is_constant():
338                tpl = [ _expr_args1, _expr_args0, None,
339                        isinstance(expr,_GreaterThanExpression), True]
340            else:
341                tpl = [ 0.0, _expr_args0-_expr_args1, None,
342                        isinstance(expr,_GreaterThanExpression), True ]
343
344        #
345        # If the constructor rule returns a less-than expression, then
346        # convert to a 3-tuple
347        #
348        elif isinstance(expr, less ):
349
350            if isinstance(_expr_args0, less ):
351                tpl = [ _expr_args0._args[0],_expr_args0._args[1],_expr_args1,
352                        isinstance(_expr_args0,_LessThanExpression),
353                        isinstance(expr,_LessThanExpression) ]
354
355            elif isinstance(_expr_args0, greater ):
356                tpl = [ _expr_args0._args[1],_expr_args0._args[0],_expr_args1,
357                        isinstance(_expr_args0,_GreaterThanExpression),
358                        isinstance(expr,_LessThanExpression) ]
359
360            elif isinstance(_expr_args1, less ):
361                tpl = [ _expr_args0,_expr_args1._args[0],_expr_args1._args[1],
362                        isinstance(expr,_LessThanExpression),
363                        isinstance(_expr_args1,_LessThanExpression) ]
364
365            elif isinstance(_expr_args1, greater ):
366                tpl = [ _expr_args0,_expr_args1._args[1],_expr_args1._args[0],
367                        isinstance(expr,_LessThanExpression),
368                        isinstance(_expr_args1,_GreaterThanExpression) ]
369
370            elif isinstance(_expr_args0,_EqualToExpression) or\
371                    isinstance(_expr_args1,_EqualToExpression):
372                msg = 'Bound error: < expression used with = expression.%s%s'
373                raise ValueError, msg % ( str(expr), str(expr._args) )
374
375            elif _expr_args0.is_constant():
376                tpl = [ _expr_args0, _expr_args1, None,
377                        isinstance(expr,_LessThanExpression), True ]
378            elif _expr_args1.is_constant():
379                tpl = [ None, _expr_args0, _expr_args1,
380                        True, isinstance(expr,_LessThanExpression) ]
381            else:
382                tpl = [ 0.0, _expr_args1 - _expr_args0, None,
383                        isinstance(expr,_LessThanExpression), True ]
384        #
385        # If the constructor rule returns a tuple or list, then convert
386        # it into a canonical form
387        #
388        elif type(expr) in [tuple,list]:
389            #
390            # Convert tuples to list
391            #
392            if type(expr) is tuple:
393                expr = list(expr)
394            #
395            # Form equality expression
396            #
397            if len(expr) == 2:
398                _self_data[index]._equality = True
399                if is_constant(expr[1]):
400                    tpl = [ expr[1], expr[0], expr[1], False, False ]
401                else:
402                    tpl = [ expr[0], expr[1], expr[0], False, False ]
403            #
404            # Form inequality expression
405            #
406            elif len(expr) == 3:
407                tpl=list(expr)+[expr[0] is None, expr[2] is None]
408            else:
409                msg = "Constructor rule for constraint '%s' returned a tuple" \
410                      ' of length %d.  Expecting a tuple of length 2 or 3:\n' \
411                      'Equality:   (left, right)\n' \
412                      'Inequality: (lower, variable, upper)'
413                raise ValueError, msg % ( self.name, len(expr) )
414
415        # Unrecognized constraint values
416        #
417        else:
418            msg = "Constraint '%s' does not have a proper value.  Found '%s'\n"\
419                  'Expecting a tuple or equation.  Examples:\n\n' \
420                  ' return summation( model.costs ) == model.income\n' \
421                  ' return (0, model.price[ item ], 50)'
422            raise ValueError, msg % ( self.name, str(expr) )
423        #
424        # Replace numeric bound values with a NumericConstant object,
425        # and reset the values to 'None' if they are 'infinite'
426        #
427        if type(tpl[0]) in (bool, int, long, float):
428            if pyutilib.math.is_finite(tpl[0]):
429                tpl[0] = NumericConstant(value=float(tpl[0]))
430            else:
431                tpl[0] = None
432        elif type(tpl[0]) is NumericConstant and                              \
433                              not pyutilib.math.is_finite(tpl[0]()):
434            tpl[0] = None
435        if type(tpl[1]) in (bool, int, long, float):
436            msg = "Constraint '%s', index='%s', has a numeric body with value=%s; an expression is expected."
437            raise ValueError, msg % (self.name , str(index), str(tpl[1]))
438
439        if _self_data[index]._equality:
440            tpl[2] = tpl[0]
441        elif type(tpl[2]) in (bool, int, long, float):
442            if pyutilib.math.is_finite(tpl[2]):
443                tpl[2] = NumericConstant(value=float(tpl[2]))
444            else:
445                tpl[2] = None
446        elif type(tpl[2]) is NumericConstant and                              \
447                              not pyutilib.math.is_finite(tpl[2]()):
448            tpl[2] = None
449
450        # Error check, to ensure that we don't have an equality constraint with
451        # 'infinite' RHS
452        #
453        if (tpl[0] is None or tpl[2] is None) and _self_data[index]._equality:
454            msg = "Equality constraint '%s' defined with infinite RHS"
455            raise ValueError, msg % self.name
456
457        # Setup identity expressions
458        #
459        if tpl[0] is not None:
460            if not isinstance(tpl[0],Expression):
461                tpl[0] = _IdentityExpression(tpl[0])
462            if simplify is True:
463               tpl[0] = tpl[0].simplify(self.model)
464        if tpl[1] is not None:
465            if not isinstance(tpl[1],Expression):
466                tpl[1] = _IdentityExpression(tpl[1])
467            if simplify is True:
468               tpl[1] = tpl[1].simplify(self.model)
469        if tpl[2] is not None:
470            if not isinstance(tpl[2],Expression):
471                tpl[2] = _IdentityExpression(tpl[2])
472            if simplify is True:
473               tpl[2] = tpl[2].simplify(self.model)
474        #
475        # Finally, setup the data
476        #
477        _self_data[index].lower = tpl[0]
478        _self_data[index].lower_ineq_strict = tpl[3]
479        _self_data[index].body = tpl[1]
480        _self_data[index].upper = tpl[2]
481        _self_data[index].upper_ineq_strict = tpl[4]
482
483
484    def pprint(self, ostream=None):
485        if ostream is None:
486           ostream = sys.stdout
487        print >>ostream, "  ",self.name,":",
488        print >>ostream, "\tSize="+str(len(self._data.keys())),
489        if isinstance(self._index,_BaseSet):
490           print >>ostream, "\tIndex=",self._index.name
491        else:
492           print >>ostream,""
493        for val in self._data:
494          if not val is None:
495             print >>ostream, "\t"+`val`
496          if self._data[val].lower is not None:
497             print >>ostream, "\t\t",
498             self._data[val].lower.pprint(ostream)
499          else:
500             print >>ostream, "\t\t-Inf"
501          if self._data[val].lower_ineq_strict:
502             print >>ostream, "\t\t<"
503          else:
504             print >>ostream, "\t\t<="
505          if self._data[val].body is not None:
506             print >>ostream, "\t\t",
507             self._data[val].body.pprint(ostream)
508          #else:                         #pragma:nocover
509             #raise ValueError, "Unexpected empty constraint body"
510          if self._data[val].upper_ineq_strict:
511             print >>ostream, "\t\t<"
512          else:
513             print >>ostream, "\t\t<="
514          if self._data[val].upper is not None:
515             print >>ostream, "\t\t",
516             self._data[val].upper.pprint(ostream)
517          elif self._data[val]._equality:
518             print >>ostream, "\t\t",
519             self._data[val].lower.pprint(ostream)
520          else:
521             print >>ostream, "\t\tInf"
522
523    def display(self, prefix="", ostream=None):
524        if ostream is None:
525           ostream = sys.stdout
526        print >>ostream, prefix+"Constraint "+self.name,":",
527        print >>ostream, "  Size="+str(len(self))
528        if None in self._data:
529            if self._data[None].body is None:
530                val = 'none'
531            else:
532                val = pyutilib.misc.format_io(self._data[None].body())
533            print >>ostream, '%s  Value=%s' % (prefix, val)
534        else:
535           flag=True
536           for key in self._data:
537             if not self._data[key].active:
538                continue
539             if flag:
540                print >>ostream, prefix+"        \tLower\tBody\t\tUpper"
541                flag=False
542             if self._data[key].lower is not None:
543                lval = str(self._data[key].lower())
544             else:
545                lval = "-Infinity"
546             val = str(self._data[key].body())
547             if self._data[key].upper is not None:
548                uval = str(self._data[key].upper())
549             else:
550                uval = "Infinity"
551             print >>ostream, "%s  %s :\t%s\t%s\t%s" % (
552                              prefix, str(key), lval, val, uval )
553           if flag:
554                print >>ostream, prefix+"  None active"
555
556
557
558class SOSConstraint(ConstraintBase):
559    """
560    Represents an SOS-n constraint.
561
562    Usage:
563    model.C1 = SOSConstraint(
564                             [...],
565                             var=VAR,
566                             [set=SET OR index=SET],
567                             [sos=N OR level=N]
568                             )
569        [...] Any number of sets used to index SET
570        VAR   The set of variables making up the SOS. Indexed by SET.
571        SET   The set used to index VAR. SET is optionally indexed by
572              the [...] sets. If SET is not specified, VAR is indexed
573              over the set(s) it was defined with.
574        N     This constraint is an SOS-N constraint. Defaults to 1.
575
576    Example:
577
578      model = Model()
579      model.A = Set()
580      model.B = Set(A)
581      model.X = Set(B)
582
583      model.C1 = SOSConstraint(model.A, var=model.X, set=model.B, sos=1)
584
585    This constraint actually creates one SOS-1 constraint for each
586    element of model.A (e.g., if |A| == N, there are N constraints).
587    In each constraint, model.X is indexed by the elements of
588    model.D[a], where 'a' is the current index of model.A.
589
590      model = Model()
591      model.A = Set()
592      model.X = Var(model.A)
593
594      model.C2 = SOSConstraint(var=model.X, sos=2)
595
596    This produces exactly one SOS-2 constraint using all the variables
597    in model.X.
598    """
599
600
601    alias("SOSConstraint", "SOS constraint expressions in a model.")
602
603    def __init__(self, *args, **kwargs):
604        if kwargs.pop('_deep_copying', None):
605            # Hack for Python 2.4 compatibility
606            # Deep copy will copy all items as necessary, so no need to
607            # complete parsing
608            return
609
610        name = kwargs.get('name', 'unknown')
611
612        # Get the 'var' parameter
613        sosVars = kwargs.pop('var', None)
614
615        # Get the 'set' or 'index' parameters
616        if 'set' in kwargs and 'index' in kwargs:
617            raise TypeError, "Specify only one of 'set' and 'index' -- " \
618                  "they are equivalent parameters"
619        sosSet = kwargs.pop('set', None)
620        sosSet = kwargs.pop('index', sosSet)
621
622        # Get the 'sos' or 'level' parameters
623        if 'sos' in kwargs and 'index' in kwargs:
624            raise TypeError, "Specify only one of 'sos' and 'level' -- " \
625                  "they are equivalent parameters"
626        sosLevel = kwargs.pop('sos', None)
627        sosLevel = kwargs.pop('level', sosLevel)
628
629        # Make sure sosLevel has been set
630        if sosLevel is None:
631            raise TypeError, "SOSConstraint() requires that either the " \
632                  "'sos' or 'level' keyword arguments be set to indicate " \
633                  "the type of SOS."
634
635        # Make sure we have a variable
636        if sosVars is None:
637            raise TypeError, "SOSConstraint() requires the 'var' keyword " \
638                  "be specified"
639
640        # Find the default sets for sosVars if sosSets is None
641        if sosSet is None:
642            sosSet = sosVars.index()
643
644        # Construct parents
645        kwargs['ctype'] = kwargs.get('ctype', SOSConstraint)
646        ConstraintBase.__init__(self, *args, **kwargs)
647
648        # Set member attributes
649        self._sosVars = sosVars
650        self._sosSet = sosSet
651        self._sosLevel = sosLevel
652
653        # TODO figure out why exactly the code expects this to be defined
654        # likely due to Numericvalue
655        self.domain = None
656
657        # TODO should variables be ordered?
658
659    def construct(self, *args, **kwds):
660        """
661        A quick hack to call add after data has been loaded. construct
662        doesn't actually need to do anything.
663        """
664        self.add()
665
666    def add(self, *args):
667        """
668        Mimics Constraint.add, but is only used to alert preprocessors
669        to the existence of the SOS variables.
670
671        Ignores all arguments passed to it.
672        """
673
674        # self._data is a ConstraintData object, which has a member
675        # .body.  .body needs to have a 3-tuple of expressions
676        # containing the variables that are referenced by this
677        # constraint. We only use one index, None. I have no
678        # particular reason to use None, it just seems consistent with
679        # what Constraint objects do.
680
681        # For simplicity, we sum over the variables.
682
683        vars = self.sos_vars()
684
685        expr = sum(vars[i] for i in vars._index)
686
687        self._data[None] = ConstraintData(name=create_name(self.name, None))
688        self._data[None].body = expr
689
690    def sos_level(self):
691        """ Return n, where this class is an SOS-n constraint """
692        return self._sosLevel
693
694    def sos_vars(self):
695        """ Return the variables in the SOS """
696        return self._sosVars
697
698    def sos_set(self):
699        """ Return the set used to index the variables """
700        return self._sosSet
701
702    def sos_set_set(self):
703        """ Return the sets used to index the sets indexing the variables """
704        return self._index
705
706    def pprint(self, ostream=None):
707        if ostream is None:
708           ostream = sys.stdout
709        print >>ostream, "  ",self.name,":",
710        print >>ostream, "Type="+str(self._sosLevel)
711        print >>ostream, "\tVariable: "+self._sosVars.name
712        print >>ostream, "\tIndices: ",
713        for i in self._sosSet.value:
714           print >>ostream, str(i)+" ",
715        print >>ostream, ""
Note: See TracBrowser for help on using the repository browser.