source: coopr.pyomo/trunk/coopr/pyomo/base/sets.py @ 2359

Last change on this file since 2359 was 2359, checked in by wehart, 10 years ago
  1. Renaming the 'presolve' phase in Pyomo to 'preprocess'. We'll

eventually want an explicit presolve phase, but that will be applied
as a transformation, right before the problem is written and sent
to the solver. Currently, this preprocess phase collects data about
the model and computes the canonical representations for expressions.

  1. Created the ability to support the construction of concrete models.

The concrete_mode() method for Model objects enables this mode.
When in the concrete mode, each component added to the model has
its construct() method called immediately after the component is
added to the model. The symbolic_mode() method for Model objects
can be called to switch back to a symbolic mode (which defers the
execution of construct().

Bad things might happen if you switch back and forth between these
modes. Notably, if you ever generate components in symbolic mode,
then you need to switch back to symbolic mode before calling create().

Note that the Model.create() method is still called when the model
construction is 'done', even when done completely in concrete mode.
This basically calls the preprocessing steps, and this method returns
the current model (i.e. no cloning is done when building a concrete
model).

  1. Created an example directory for concrete models. Note that these

can still be solved with the Pyomo command line. In this
instance, the command line simply executes the script and applies
a solver.

File size: 35.0 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__ = ['Set', '_BaseSet', '_SetContainer', '_SetArray', '_ProductSet', 'set_options']
12
13import pyutilib.component.core
14from coopr.pyomo.base.plugin import ComponentRegistration
15from component import Component
16import pyutilib.misc
17import sys
18import types
19import copy
20
21log = pyutilib.component.core.PluginGlobals.env().log
22
23#
24# Decorator for set initializer functions
25#
26def set_options(**kwds):
27    def decorator(func):
28        func.set_options = kwds
29        return func
30    return decorator
31
32
33##------------------------------------------------------------------------
34##
35## An abstract Set class
36##
37##------------------------------------------------------------------------
38
39class _BaseSet(Component):
40    """The base class for set objects that are used to index other Pyomo objects
41
42       This class has a similar look-and-feel as a built-in set class.  However,
43       the set operations defined in this class return another abstract
44       Set object.  This class contains a concrete set, which can be
45       initialized by the load() method.
46    """
47
48    """ Constructor
49        Arguments:
50           name          The name of the set
51           within        A set that defines the type of values that can
52                          be contained in this set
53           rule
54           initialize    Default set members, which may be overriden
55                          when setting up this set
56           validate      Define a function for validating membership in a
57                          set.  This has the functional form:
58                          f: data -> bool
59                          and returns true if the data belongs in the set
60           dimen         Specify the set's arity.
61           doc           Documentation for this object
62           virtual       Indicate that this is a virtual set that does not
63                            have concrete members.
64    """
65    def __init__(self, **kwds):
66        Component.__init__(self, ctype=Set)
67        self.initialize=None
68        self.name="_unknown_"
69        self.validate=None
70        self.ordered=False
71        self.order=[]
72        self.domain=None
73        self.dimen = None
74        self.virtual = False
75        self._bounds=None
76        self.doc = ""
77        for key in kwds.keys():
78          if key == "dimen":
79             pass
80          elif key == "doc":
81             self.doc=kwds[key]
82          elif key == "virtual":
83             self.virtual=kwds[key]
84          elif key == "bounds":
85             self._bounds=kwds[key]
86          elif key == "within":
87             self.domain = kwds[key]
88             if self.domain is not None:
89                self.dimen = self.domain.dimen
90          elif key == "rule":
91             setattr(self,"initialize",kwds[key])
92          elif key in ["name", "initialize", "validate", "ordered"]:
93             setattr(self,key,kwds[key])
94          else:
95             raise ValueError, "Unknown option '"+key+"' when constructing Set "
96        if "dimen" in kwds.keys():
97           if self.within is not None and self.dimen != kwds["dimen"]:
98              raise ValueError, "Option dimen "+str(kwds["dimen"])+" is different from the dimen of superset "+self.within.name+": "+str(self.dimen)
99           self.dimen = kwds["dimen"]
100        if self.dimen is None:
101            # We set the default to 1
102            self.dimen=1
103        if self.initialize is not None:
104           #
105           # Convert generators to lists, since a generator is not copyable
106           #
107           if type(self.initialize) is types.GeneratorType:
108              self.initialize = list(self.initialize)
109           #
110           # Try to guess dimen from the initialize list
111           #
112           tmp=None
113           if type(self.initialize) is tuple:
114              tmp = len(self.initialize)
115           elif type(self.initialize) is list and len(self.initialize) > 0 \
116                and type(self.initialize[0]) is tuple:
117              tmp = len(self.initialize[0])
118           if tmp is not None:
119              if "dimen" in kwds and tmp != self.dimen:
120                 raise ValueError, "Dimension argument differs from the data in the initialize list"
121              else:
122                 self.dimen=tmp
123
124    def reset(self):
125        pass
126
127    def dim(self):
128        return self._ndim
129
130    def display(self, ostream=None):
131        self.pprint(ostream=ostream)
132
133    def __getattr__(self,name):
134        if name is "within":
135           return self.domain
136        raise AttributeError, "Unknown attribute "+name
137
138    def __setattr__(self,name,val):
139        if name is "within":
140           self.domain = val
141        return object.__setattr__(self,name,val)
142
143    def _verify(self,element,use_exception=True):
144        # A utility routine that is used to verify if the element
145        # is valid for this set.
146        if self.domain is not None and element not in self.domain:
147           if use_exception:
148              raise ValueError, "Value "+str(element)+" is not valid for set "+self.name+", because it is not within set "+self.domain.name
149           return False
150        if self.validate is not None and not self.validate(element,self.model):
151           if use_exception:
152              raise ValueError, "Value "+str(element)+" violates the validation rule of set "+self.name
153           return False
154        if self.dimen > 1 and type(element) is not tuple:
155           if use_exception:
156              raise ValueError, "Value "+str(element)+" is not a tuple for set "+self.name+", which has dimen "+str(self.dimen)
157           return False
158        elif self.dimen == 1 and type(element) is tuple:
159           if use_exception:
160              raise ValueError, "Value "+str(element)+" is a tuple for set "+self.name+", which has dimen "+str(self.dimen)
161           return False
162        elif type(element) is tuple and len(element) != self.dimen:
163           if use_exception:
164              raise ValueError, "Value "+str(element)+" does not have dimension "+str(self.dimen)+", which is needed for set "+self.name
165           return False
166        return True
167
168    def bounds(self):
169        """Return bounds information.  The default value is 'None', which
170        indicates that this set does not contain bounds."""
171        return self._bounds
172
173
174class _SetContainer(_BaseSet):
175    """A derived _BaseSet object that contains a single set."""
176
177    def __init__(self, *args, **kwds):
178        """ Constructor """
179        if args != ():
180            raise TypeError, "A _SetContainer expects no arguments"
181        self._index_set=None
182        self._index=[None]
183        self._ndim=0
184        self.value=set()
185        _BaseSet.__init__(self,**kwds)
186
187    def construct(self, values=None):
188        """ Apply the rule to construct values in this set """
189        log.debug("Constructing _SetContainer, name="+self.name+", from data="+repr(values))
190        if self._constructed:
191            return
192        self._constructed=True
193        #
194        # Construct using the values list
195        #
196        if values is not None:
197           if type(self._bounds) is tuple:
198                first=self._bounds[0]
199                last=self._bounds[1]
200           else:
201                first=None
202                last=None
203           all_numeric=True
204           for val in values[None]:
205                if type(val) in [int,float,long]:
206                    if first is None or val<first:
207                        first=val
208                    if last is None or val>last:
209                        last=val
210                else:
211                    all_numeric=False
212                self.add(val)
213           if all_numeric:
214                self._bounds = (first,last)
215        #
216        # Construct using the rule
217        #
218        elif type(self.initialize) is types.FunctionType:
219           if self.model is None:
220              raise ValueError, "Must pass a model in to initialize with a function"
221           if self.initialize.func_code.co_argcount == 1:
222              #
223              # Using a rule of the form f(model) -> iterator
224              #
225              tmp = self.initialize(self.model)
226              for val in tmp:
227                if self.dimen is None:
228                   if type(val) in [tuple,list]:
229                        self.dimen=len(val)
230                   else:
231                        self.dimen=1
232                self.add(val)
233           else:
234              #
235              # Using a rule of the form f(z,model) -> element
236              #
237              ctr=0
238              val = self.initialize(ctr,self.model)
239              if self.dimen is None:
240                   if type(val) in [tuple,list]:
241                        self.dimen=len(val)
242                   else:
243                        self.dimen=1
244              while val is not None:
245                self.add(val)
246                ctr += 1
247                val = self.initialize(ctr,self.model)
248           #
249           # We should be verifying as part of the add() method
250           #
251           #for val in self.value:
252             #self._verify(val)
253        #
254        # Construct using the default values
255        #
256        elif self.initialize is not None:
257              if type(self.initialize) is dict:
258                 raise ValueError, "Cannot initialize set "+self.name+" with dictionary data"
259              if type(self._bounds) is tuple:
260                first=self._bounds[0]
261                last=self._bounds[1]
262              else:
263                first=None
264                last=None
265              all_numeric=True
266              for val in self.initialize:
267                if type(val) in [int,float,long]:
268                    if first is None or val<first:
269                        first=val
270                    if last is None or val>last:
271                        last=val
272                else:
273                    all_numeric=False
274                self.add(val)
275              #print "HERE",self.name,first,last,all_numeric
276              if all_numeric:
277                self._bounds = (first,last)
278
279    def data(self):
280        """The underlying set data."""
281        if self.virtual:
282           raise TypeError, "Cannot access underlying set data for virtual set "+self.name
283        return self.value
284
285    def clear(self):
286        """Remove all elements from the set."""
287        if self.virtual:
288           raise TypeError, "Cannot clear virtual Set object `"+self.name+"'"
289        self.value.clear()
290        self.order=[]
291
292    def check_values(self):
293        """ Verify that the values in this set are valid.
294        """
295        if self.virtual:
296           return
297        for val in self.value:
298          self._verify(val)
299
300    def first(self):
301        if self.virtual:
302           raise TypeError, "Cannot access the first element of virtual set `"+self.name+"'"
303        if len(self.order) > 0:
304           return self.order[0]
305        else:
306           for val in self.value:
307             return val
308
309    def add(self, *args):
310        """Add one or more elements to a set."""
311        if self.virtual:
312           raise TypeError, "Cannot add elements to virtual set `"+self.name+"'"
313        for val in args:
314          tmp = pyutilib.misc.flatten_tuple(val)
315          self._verify(tmp)
316          try:
317            if tmp in self.value:
318               raise ValueError, "Element "+str(tmp)+" already exists in set "+self.name
319            self.value.add(tmp)
320            if self.ordered:
321               self.order.append(tmp)
322          except TypeError:
323            raise TypeError, "Problem inserting "+str(tmp)+" into set "+self.name
324
325    def remove(self, element):
326        """Remove an element from the set.
327           If the element is not a member, raise an error.
328        """
329        if self.virtual:
330           raise KeyError, "Cannot remove element `"+str(element)+"' from virtual set "+str(self.name)
331        if element not in self.value:
332           raise KeyError, "Cannot remove element `"+str(element)+"' from set "+str(self.name)
333        self.value.remove(element)
334        if self.ordered:
335           self.order.remove(element)
336
337    def discard(self, element):
338        """Remove an element from the set.
339           If the element is not a member, do nothing.
340        """
341        if self.virtual:
342           raise KeyError, "Cannot discard element `"+str(element)+"' from virtual set "+str(self.name)
343        if self.ordered and element in self.value:
344           self.order.remove(element)
345        self.value.discard(element)
346
347    def ord(self, match_element):
348        """ For ordered sets, return the position index (1-based)
349            of the input element.
350        """
351        if self.ordered is False:
352           raise AttributeError, "Cannot invoke ord method for an unordered set!"
353        # linear, but the only way given the current representation/attributes of a set.
354        i=1
355        for element in self.order:
356           if match_element == element:
357              return i
358           i=i+1
359        raise ValueError, "Unknown input element="+str(match_element)+" provided as input to ord() method for set="+str(self.name)
360
361    def pprint(self, ostream=None):
362        if ostream is None:
363           ostream = sys.stdout
364        print >>ostream, "  ",self.name,":",
365        print >>ostream, "\tDim="+str(self.dim()),
366        print >>ostream, "\tDimen="+str(self.dimen),
367        print >>ostream, "\tSize="+str(len(self)),
368        if self.domain is not None:
369           print >>ostream, "\tDomain="+str(self.domain.name),
370        else:
371           print >>ostream, "\tDomain="+str(None),
372        print >>ostream, "\tOrdered="+str(self.ordered),
373        print >>ostream, "\tBounds="+str(self._bounds)
374        if self.model is None:
375           print >>ostream, "\t Model=None"
376        else:
377           print >>ostream, "\t Model="+str(self.model.name)
378        if self.virtual:
379           print >>ostream, "\t  Virtual"
380        else:
381           if self.ordered:
382              tmp = copy.copy(self.order)
383           else:
384              tmp = copy.copy(list(self.value))
385           tmp.sort()
386           print >>ostream, "\t  ",tmp
387
388    def __getitem__(self, key):
389        if not self.ordered:
390           raise IndexError, "Cannot get an item in unordered set "+self.name
391        return self.order[key]
392
393    def __len__(self):
394        """ The number of items in the set.
395            The set is empty until a concrete instance has been setup.
396        """
397        if self.virtual:
398           raise ValueError, "The size of a virtual set is unknown"
399        return len(self.value)
400
401    #def __repr__(self):
402        #"""Return string representation of the underlying set"""
403        #return str(self)+" : "+str(self.value)
404
405    def __iter__(self):
406        """Return an iterator for the underlying set"""
407        if self.virtual:
408           raise TypeError, "Cannot iterate over Set object `"+self.name+"', which is a virtual set"
409        if self.ordered:
410           return self.order.__iter__()
411        return self.value.__iter__()
412
413    def __hash__(self):
414        """Hash this object"""
415        return _BaseSet.__hash__(self)
416
417    def __eq__(self,other):
418        """ Equality comparison """
419        tmp = self._set_repn(other)
420        if self.virtual:
421            if other.virtual:
422                return hash(self) == hash(tmp)
423            return False
424        if self.dimen != other.dimen:
425           return False
426        return self.value.__eq__( tmp )
427
428    def __ne__(self,other):
429        return not self.__eq__(other)
430
431    def __contains__(self, element):
432        """Report whether an element is a member of this set.
433        (Called in response to the expression 'element in self'.)
434        """
435        #
436        # If the element is a set, then see if this is a subset
437        #
438        if isinstance(element,_SetContainer):
439           return element.issubset(self)
440        #
441        # If this is not a valid element, then return False
442        #
443        if not self._verify(element,False):
444           return False
445        #
446        # If the restriction rule is used then we do not actually
447        # check whether the data is in the set self.value.
448        #
449        if self.validate is not None and self.virtual:
450           return True
451        #
452        # The final check: return true if self.virtual is True, since we should
453        # have already validated this value
454        #
455        return self.virtual or element in self.value
456
457    def issubset(self,other):
458        """Report whether another set contains this set"""
459        if self.virtual:
460           raise TypeError, "ERROR: cannot perform \"issubset\" test because the current set is a virtual set."
461        if self.dimen != other.dimen:
462           raise ValueError, "Cannot perform set operation with sets "+self.name+" and "+other.name+" that have different element dimensions: "+str(self.dimen)+" "+str(other.dimen)
463        for val in self.value:
464          if val not in other:
465             return False
466        return True
467
468    def issuperset(self,other):
469        """Report this set contains another set"""
470        if isinstance(other,_BaseSet):
471           if other.virtual:
472              raise TypeError, "ERROR: cannot perform \"issuperset\" test because the target set is a virtual set."
473           return other.issubset(self) and (self.virtual or len(self) >= len(other))
474        for val in other:
475          if val not in self:
476             return False
477        return True
478
479    __le__ = issubset
480    __ge__ = issuperset
481
482    def __lt__(self,other):
483        if self.virtual or \
484           (isinstance(other,_BaseSet) and other.virtual):
485           raise TypeError, "ERROR: cannot test for __lt__ because either or both of the sets are virtual."
486        if self.dimen != other.dimen:
487           raise ValueError, "Cannot perform set operation with sets "+self.name+" and "+other.name+" that have different element dimensions: "+str(self.dimen)+" "+str(other.dimen)
488        for val in self.value:
489          if val not in other:
490             return False
491        return len(self) < len(other)
492
493    def __gt__(self,other):
494        if self.virtual or \
495           (isinstance(other,_BaseSet) and other.virtual):
496           raise TypeError, "ERROR: cannot test for __lt__ because either or both of the sets are virtual."
497        if self.dimen != other.dimen:
498           raise ValueError, "Cannot perform set operation with sets "+self.name+" and "+other.name+" that have different element dimensions: "+str(self.dimen)+" "+str(other.dimen)
499        for val in other:
500          if val not in self.value:
501             return False
502        return len(self) > len(other)
503
504    def _set_repn(self,other):
505        if isinstance(other,_SetContainer) and other.virtual:
506            return other
507        if isinstance(other,_SetContainer) and type(other.value) is not dict:
508           return other.value
509        raise TypeError, "Cannot apply Set operation with a "+str(type(other))+" object"
510       
511    def __or__(self,other):
512        """ Return a _SetContainer object that is the union of this set
513        with another set. """
514        if self.virtual:
515           raise TypeError, "Cannot perform set operations with virtual set '"+self.name+"'"
516        if other.virtual:
517           raise TypeError, "Cannot perform set operations with virtual set '"+other.name+"'"
518        if self.dimen != other.dimen:
519           raise ValueError, "Cannot perform set operation with sets "+self.name+" and "+other.name+" that have different element dimensions: "+str(self.dimen)+" "+str(other.dimen)
520        def SetUnionRule(model):
521            data = set()
522            for val in getattr(model,SetUnionRule.Set1):
523              data.add(val)
524            for val in getattr(model,SetUnionRule.Set2):
525              data.add(val)
526            return data
527        SetUnionRule.Set1 = self.name
528        SetUnionRule.Set2 = other.name
529        return _SetContainer(dimen=self.dimen, initialize=SetUnionRule)
530
531    def __and__(self,other):
532        """Return a _SetContainer object that is the intersection of this set
533        with another set"""
534        if self.virtual:
535           raise TypeError, "Cannot perform set operations with virtual set '"+self.name+"'"
536        if other.virtual:
537           raise TypeError, "Cannot perform set operations with virtual set '"+other.name+"'"
538        if self.dimen != other.dimen:
539           raise ValueError, "Cannot perform set operation with sets "+self.name+" and "+other.name+" that have different element dimensions: "+str(self.dimen)+" "+str(other.dimen)
540        def SetIntersectionRule(model):
541            data = set()
542            for val in getattr(model,SetIntersectionRule.Set1):
543              if val in getattr(model,SetIntersectionRule.Set2):
544                 data.add(val)
545            return data
546        SetIntersectionRule.Set1 = self.name
547        SetIntersectionRule.Set2 = other.name
548        return _SetContainer(dimen=self.dimen, initialize=SetIntersectionRule)
549
550    def __xor__(self,other):
551        """Return a _SetContainer object that is the symmetric difference of
552        this set with another set"""
553        if self.virtual:
554           raise TypeError, "Cannot perform set operations with virtual set '"+self.name+"'"
555        if other.virtual:
556           raise TypeError, "Cannot perform set operations with virtual set '"+other.name+"'"
557        if self.dimen != other.dimen:
558           raise ValueError, "Cannot perform set operation with sets "+self.name+" and "+other.name+" that have different element dimensions: "+str(self.dimen)+" "+str(other.dimen)
559        def SetSymmetricDifferenceRule(M):
560            Set1 = getattr(M,SetSymmetricDifferenceRule.Set1)
561            Set2 = getattr(M,SetSymmetricDifferenceRule.Set2)
562            data = set()
563            for val in Set1:
564              if val not in Set2:
565                 data.add(val)
566            for val in Set2:
567              if val not in Set1:
568                 data.add(val)
569            return data
570        SetSymmetricDifferenceRule.Set1 = self.name
571        SetSymmetricDifferenceRule.Set2 = other.name
572        return _SetContainer(dimen=self.dimen, initialize=SetSymmetricDifferenceRule)
573
574    def __sub__(self,other):
575        """Return a _SetContainer object that is the difference between this set
576        and another set"""
577        if self.virtual:
578           raise TypeError, "Cannot perform set operations with virtual set '"+self.name+"'"
579        if other.virtual:
580           raise TypeError, "Cannot perform set operations with virtual set '"+other.name+"'"
581        if self.dimen != other.dimen:
582           raise ValueError, "Cannot perform set operation with sets "+self.name+" and "+other.name+" that have different element dimensions: "+str(self.dimen)+" "+str(other.dimen)
583        def SetDifferenceRule(model):
584            data = set()
585            for val in getattr(model,SetDifferenceRule.Set1):
586              if val not in getattr(model,SetDifferenceRule.Set2):
587                 data.add(val)
588            return data
589        SetDifferenceRule.Set1 = self.name
590        SetDifferenceRule.Set2 = other.name
591        return _SetContainer(dimen=self.dimen, initialize=SetDifferenceRule)
592
593    def __mul__(self,other):
594        """Return a _ProductSet that is the cross-product between this set and
595        and another set"""
596        if not isinstance(other,_SetContainer):
597           raise TypeError, "Set operator __mul__ cannot be applied with an object of type "+str(type(other))
598        if self.virtual:
599           raise TypeError, "Cannot perform set operations with virtual set '"+self.name+"'"
600        if other.virtual:
601           raise TypeError, "Cannot perform set operations with virtual set '"+other.name+"'"
602        if isinstance(self,_ProductSet) and self.name=="_unknown_":
603           self.set_tuple = tuple( list(self.set_tuple) + [other] )
604           self._compute_dimen()
605           return self
606        return _ProductSet(self,other)
607
608
609class _SetArray(_BaseSet):
610    """An array of sets, which are indexed by other sets"""
611
612    def __init__(self, *args, **kwds):      #pragma:nocover
613        """ Constructor """
614        self._index_set=None
615        self.value={}
616        self._ndim=len(args)
617        if len(args) == 1:
618            if isinstance(args[0],_BaseSet):
619                self._index=args[0]
620            else:
621                try:
622                    options = getattr(args[0],'set_options')
623                    options['initialize'] = args[0]
624                    self._index=Set(**options)
625                except:
626                    self._index=Set(initialize=args[0])
627        else:
628            self._index=None
629            tmp = []
630            for arg in args:
631                if isinstance(arg,_BaseSet):
632                    tmp.append(arg)
633                else:
634                    try:
635                        options = getattr(arg,'set_options')
636                        options['initialize'] = arg
637                        tmp.append( Set(**options) )
638                    except:
639                        tmp.append( Set(initialize=arg) )
640            self._index_set=tuple(tmp)
641        _BaseSet.__init__(self,**kwds)
642        self.value={}
643
644    def keys(self):
645        return self.value.keys()
646
647    def __contains__(self, element):
648        return element in self.value.keys()
649
650    def clear(self):
651        """Remove all elements from the set."""
652        for key in self.value:
653          self.value[key].clear()
654
655    def data(self):
656        """The underlying set data."""
657        raise TypeError, "Cannot access underlying set data for array set "+self.name
658
659    def __getitem__(self, key):
660        if key not in self.value:
661           raise KeyError, "Cannot access index "+str(key)+" in array set "+self.name
662        return self.value[key]
663
664    def __setitem__(self, key, val):
665        if key not in self._index:
666           raise KeyError, "Cannot set index "+str(key)+" in array set "+self.name
667        if key in self.value:
668           self.value[key].clear()
669           for elt in val:
670             self.value[key].add(elt)
671        else:
672           self.value[key] = Set(initialize=val,within=self.domain,validate=self.validate,ordered=self.ordered,name=self.name+"["+str(key)+"]",dimen=self.dimen)
673           self.value[key].construct()
674
675    def __len__(self):
676        result = 0
677        for val in self.value.values():
678            result += len(val)
679        return result
680
681    def __iter__(self):
682        """Return an iterator for the set array"""
683        return self.value.__iter__()
684
685    def check_values(self):
686        for key in self.value:
687          for val in self.value[key]:
688            self._verify(val,True)
689
690    def pprint(self, ostream=None):
691        if ostream is None:
692           ostream = sys.stdout
693        print >>ostream, "  ",self.name,":",
694        print >>ostream, "\tDim="+str(self.dim()),
695        print >>ostream, "\tDimen="+str(self.dimen),
696        if self.domain is None:
697           print >>ostream, "\tDomain="+str(self.domain),
698        else:
699           print >>ostream, "\tDomain="+str(self.domain.name),
700        print >>ostream, "\tArraySize="+str(len(self.value.keys())),
701        print >>ostream, "\tOrdered="+str(self.ordered)
702        if self.model is None:
703           print >>ostream, "\t Model=None"
704        else:
705           print >>ostream, "\t Model="+str(self.model.name)       
706        tmp=self.value.keys()
707        if tmp is not None:
708            tmp.sort()
709        else:
710            tmp=[]
711        for val in tmp:
712          if self.ordered:
713             ttmp = copy.copy(self.value[val].order)
714          else:
715             ttmp = copy.copy(list(self.value[val].value))
716             ttmp.sort()
717          print >>ostream, "\t",self.name+"["+str(val)+"]\t",ttmp
718
719    def construct(self, values=None):
720        """ Apply the rule to construct values in each set"""
721        log.debug("Constructing _SetArray, name="+self.name+", from data="+repr(values))
722        if self._constructed:
723            return
724        self._constructed=True
725        if self.virtual:                                #pragma:nocover
726           raise TypeError, "It doesn't make sense to create a virtual set array"
727        #
728        # Construct using the values list
729        #
730        if values is not None:
731           for key in values:
732             if type(key) is tuple and len(key)==1:
733                tmpkey=key[0]
734             else:
735                tmpkey=key
736             if tmpkey not in self._index:
737                raise KeyError, "Cannot set index "+str(tmpkey)+" in array set "+self.name
738             self.value[tmpkey] = Set(initialize=values[key],within=self.domain,validate=self.validate,ordered=self.ordered,name=self.name+"["+str(tmpkey)+"]",dimen=self.dimen)
739             self.value[tmpkey].model = self.model
740             self.value[tmpkey].construct()
741        #
742        # Construct using the rule
743        #
744        elif type(self.initialize) is types.FunctionType:
745           if self.model is None:
746              raise ValueError, "Need model to construct a set array with a function"
747           if self._index is None:
748              raise ValueError, "No index for set "+self.name
749           for key in self._index:
750             if isinstance(key,tuple):
751                tmp = list(key)
752             else:
753                tmp = [key]
754             if self.initialize.func_code.co_argcount == len(tmp)+1:
755                tmp.append(self.model)
756                tmp = tuple(tmp)
757                rule_list = list(self.initialize(*tmp))
758             else:
759                rule_list=[]
760                ctr=0
761                args = tuple(tmp+[ctr,self.model])
762                val = self.initialize(*args)
763                while val is not None:
764                  rule_list.append(val)
765                  ctr += 1
766                  args = tuple(tmp+[ctr,self.model])
767                  val = self.initialize(*args)
768             if self.dimen is None and len(rule_list) > 0:
769                   if type(rule_list[0]) in [tuple,list]:
770                        self.dimen=len(rule_list[0])
771                   else:
772                        self.dimen=1
773             self.value[key] = Set(initialize=rule_list,within=self.domain,validate=self.validate,ordered=self.ordered,name=self.name+"["+str(key)+"]",dimen=self.dimen)
774             self.value[key].model = self.model
775             self.value[key].construct()
776        #
777        # Construct using the default values
778        #
779        else:
780           if self.initialize is not None:
781              if type(self.initialize) is not dict:
782                 for key in self._index:
783                   self.value[key] = Set(initialize=self.initialize,within=self.domain,validate=self.validate,ordered=self.ordered,name=self.name+"["+str(key)+"]",dimen=self.dimen)
784                   self.value[key].model = self.model
785                   self.value[key].construct()
786              else:
787                 for key in self.initialize:
788                   self.value[key] = Set(initialize=self.initialize[key],within=self.domain,validate=self.validate,ordered=self.ordered,name=self.name+"["+str(key)+"]",dimen=self.dimen)
789                   self.value[key].model = self.model
790                   self.value[key].construct()
791
792
793##------------------------------------------------------------------------
794##
795## A set that defines an abstract cross-product set.
796##
797##------------------------------------------------------------------------
798
799class _ProductSet(_SetContainer):
800    """Set that represents a cross-product of other sets"""
801
802    """ Constructor
803        Arguments:
804           name         The name of this set
805           initialize   Default set members, which may be overriden
806                            when setting up this set
807           rule         A rule for setting up this set with
808                            existing model data.  This has the functional form:
809                            f: pyomo.Model -> set
810    """
811    def __init__(self, *args, **kwd):
812        self._class_override=False
813        self.set_tuple = args
814        tmp=()
815        _SetContainer.__init__(self,*tmp,**kwd)
816        self._compute_dimen()
817        self._len=0
818
819    def __iter__(self):
820        """Returns an iterator/generator for the cross-product"""
821        return pyutilib.misc.flattened_cross_iter(*self.set_tuple)
822
823    def __len__(self):
824        return self._len
825
826    def _compute_dimen(self):
827        ans=0
828        for set in self.set_tuple:
829            if set.dimen is None:
830                self.dimen=None
831                return
832            else:
833                ans += set.dimen
834        self.dimen = ans
835
836    def _verify(self,element,use_exception=True):
837        """
838        If this set is virtual, then an additional check is made
839        to ensure that the element is in each of the underlying sets.
840        """
841        tmp = _SetContainer._verify(self,element,use_exception)
842        if not tmp or not self.virtual:
843            return tmp
844
845        next_tuple_index = 0
846        member_set_index = 0
847        for member_set in self.set_tuple:
848           tuple_slice = element[next_tuple_index:next_tuple_index + member_set.dimen]
849           if member_set.dimen == 1:
850              tuple_slice = tuple_slice[0]
851           if tuple_slice not in member_set:
852             return False
853           member_set_index += 1
854           next_tuple_index += member_set.dimen
855        return True
856       
857    def construct(self, values=None):
858        """ Apply the rule to construct values in this set """
859        log.debug("Constructing _ProductSet, name="+self.name+", from data="+repr(values))
860        if self._constructed:
861            return
862        self._constructed=True
863        if self.virtual:
864           if len(self.set_tuple) == 0:
865              self._len=0
866           else:
867              self._len=1
868              for val in self.set_tuple:
869                self._len *= len(val)
870        #
871        # Construct using the values list
872        #
873        elif values is not None:
874           for val in values[None]:
875             self.add(val)
876        #
877        # Construct using the rule
878        #
879        elif type(self.initialize) is types.FunctionType:
880           for val in self.initialize(self.model):
881             self.add(val)
882        #
883        # Construct using the default values
884        #
885        else:
886           if self.initialize is not None:
887              if type(self.initialize) is dict:
888                 for val in self.initialize:
889                   self.add(val)
890              else:
891                 self.add(self.initialize)
892           else:
893              for val in pyutilib.misc.cross(self.set_tuple):
894                self.add(val)
895        #
896        if not self.virtual:
897            self._len=len(self.value)
898        self._compute_dimen()
899        if self.dimen is None:
900            raise ValueError, "The product set dimension must be computeable after constructing this set!"
901
902
903class Set(object):
904    """Set objects that are used to index other Pyomo objects
905
906       This class has a similar look-and-feel as a built-in set class.  However,
907       the set operations defined in this class return another abstract
908       Set object.  This class contains a concrete set, which can be
909       initialized by the load() method.
910    """
911    def __new__(cls, *args, **kwds):
912        if args == ():
913            self = _SetContainer(*args, **kwds)
914        else:
915            self = _SetArray(*args, **kwds)
916        return self
917
918
919ComponentRegistration("Set", Set, "Set data that is used to define a model instance.")
920
Note: See TracBrowser for help on using the repository browser.