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

Last change on this file since 2385 was 2385, checked in by wehart, 11 years ago

Adding a work-around when importing 'bidict' fails. This occurs with
Python 2.5 and earlier.

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