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

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

Rework of Coopr to use the new PyUtilib? package decomposition.

NOTE: to use Coopr with this update, we need to work with a new version of coopr_install.

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