source: coopr.pyomo/stable/2.3/coopr/pyomo/base/sets.py @ 2388

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

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

........

r2369 | wehart | 2010-02-17 18:11:43 -0700 (Wed, 17 Feb 2010) | 2 lines


Adding functionality for ordered sets.

........

r2378 | wehart | 2010-02-19 15:17:49 -0700 (Fri, 19 Feb 2010) | 2 lines


Specifying the symbols that are exported from convert.py

........

r2379 | wehart | 2010-02-19 15:31:35 -0700 (Fri, 19 Feb 2010) | 5 lines


Resolving a weakness in the pmedian.py data declaration. The facility
'd' data was monotonically increasing for every facility, so only one sensor placement
was important. The current model eliminates this, which should make the results portable
to other solvers.

........

r2381 | wehart | 2010-02-19 15:50:11 -0700 (Fri, 19 Feb 2010) | 2 lines


Misc baseline updates.

........

r2385 | wehart | 2010-02-21 23:14:18 -0700 (Sun, 21 Feb 2010) | 3 lines


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

........

r2387 | wehart | 2010-02-21 23:27:44 -0700 (Sun, 21 Feb 2010) | 3 lines


Bug fix: needed to protect the use of ordered_dict_inv with
the using_bidict flag.

........

File size: 37.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
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            if using_bidict:
384                return self.order_dict.inv[key]
385            else:
386                return self.order_dict_inv[key]
387        #
388        # If the set is not ordered, then we use the intrinsic ordering imposed by the set.
389        # We convert the set to a list and return the specified value.
390        #
391        if key >= 1:
392            return list(self.value)[key-1]
393        elif key < 0:
394            return list(self.value)[key]
395        else:
396            raise IndexError, "Valid index values for sets are 1 ... len(set) or -1 ... -len(set)"
397
398    def ord(self, match_element):
399        """ For ordered sets, return the position index (1-based)
400            of the input element.
401        """
402        if self.ordered is False:
403           raise AttributeError, "Cannot invoke ord() method for unordered set="+str(self.name)
404        try:
405            return self.order[match_element]
406        except IndexError:
407            raise IndexError, "Unknown input element="+str(match_element)+" provided as input to ord() method for set="+str(self.name)
408
409    def next(self, match_element, k=1):
410        return self.order[self.order_dict[element]+k]
411
412    def nextw(self, match_element, k=1):
413        ndx = self.order_dict[match_element]+k
414        total = len(self.order)
415        if ndx > total:
416            ndx -= total
417        if ndx <= 0:
418            ndx += total
419        return self.order.inv[ndx]
420
421    def prev(self, match_element, k=1):
422        return self.next(match_element, k=-k)
423
424    def prevw(self, match_element, k=1):
425        return self.nextw(match_element, k=-k)
426
427    def pprint(self, ostream=None):
428        if ostream is None:
429           ostream = sys.stdout
430        print >>ostream, "  ",self.name,":",
431        print >>ostream, "\tDim="+str(self.dim()),
432        print >>ostream, "\tDimen="+str(self.dimen),
433        print >>ostream, "\tSize="+str(len(self)),
434        if self.domain is not None:
435           print >>ostream, "\tDomain="+str(self.domain.name),
436        else:
437           print >>ostream, "\tDomain="+str(None),
438        print >>ostream, "\tOrdered="+str(self.ordered),
439        print >>ostream, "\tBounds="+str(self._bounds)
440        if self.model is None:
441           print >>ostream, "\t Model=None"
442        else:
443           print >>ostream, "\t Model="+str(self.model.name)
444        if self.virtual:
445           print >>ostream, "\t  Virtual"
446        else:
447           if self.ordered:
448              tmp = copy.copy(self.order)
449           else:
450              tmp = copy.copy(list(self.value))
451              tmp.sort()
452           print >>ostream, "\t  ",tmp
453
454    def __len__(self):
455        """ The number of items in the set.
456            The set is empty until a concrete instance has been setup.
457        """
458        if self.virtual:
459           raise ValueError, "The size of a virtual set is unknown"
460        return len(self.value)
461
462    #def __repr__(self):
463        #"""Return string representation of the underlying set"""
464        #return str(self)+" : "+str(self.value)
465
466    def __iter__(self):
467        """Return an iterator for the underlying set"""
468        if self.virtual:
469           raise TypeError, "Cannot iterate over Set object `"+self.name+"', which is a virtual set"
470        if self.ordered:
471           return self.order.__iter__()
472        return self.value.__iter__()
473
474    def __hash__(self):
475        """Hash this object"""
476        return _BaseSet.__hash__(self)
477
478    def __eq__(self,other):
479        """ Equality comparison """
480        tmp = self._set_repn(other)
481        if self.virtual:
482            if other.virtual:
483                return hash(self) == hash(tmp)
484            return False
485        if self.dimen != other.dimen:
486           return False
487        return self.value.__eq__( tmp )
488
489    def __ne__(self,other):
490        return not self.__eq__(other)
491
492    def __contains__(self, element):
493        """Report whether an element is a member of this set.
494        (Called in response to the expression 'element in self'.)
495        """
496        #
497        # If the element is a set, then see if this is a subset
498        #
499        if isinstance(element,_SetContainer):
500           return element.issubset(self)
501        #
502        # If this is not a valid element, then return False
503        #
504        if not self._verify(element,False):
505           return False
506        #
507        # If the restriction rule is used then we do not actually
508        # check whether the data is in the set self.value.
509        #
510        if self.validate is not None and self.virtual:
511           return True
512        #
513        # The final check: return true if self.virtual is True, since we should
514        # have already validated this value
515        #
516        return self.virtual or element in self.value
517
518    def issubset(self,other):
519        """Report whether another set contains this set"""
520        if self.virtual:
521           raise TypeError, "ERROR: cannot perform \"issubset\" test because the current set is a virtual set."
522        if self.dimen != other.dimen:
523           raise ValueError, "Cannot perform set operation with sets "+self.name+" and "+other.name+" that have different element dimensions: "+str(self.dimen)+" "+str(other.dimen)
524        for val in self.value:
525          if val not in other:
526             return False
527        return True
528
529    def issuperset(self,other):
530        """Report this set contains another set"""
531        if isinstance(other,_BaseSet):
532           if other.virtual:
533              raise TypeError, "ERROR: cannot perform \"issuperset\" test because the target set is a virtual set."
534           return other.issubset(self) and (self.virtual or len(self) >= len(other))
535        for val in other:
536          if val not in self:
537             return False
538        return True
539
540    __le__ = issubset
541    __ge__ = issuperset
542
543    def __lt__(self,other):
544        if self.virtual or \
545           (isinstance(other,_BaseSet) and other.virtual):
546           raise TypeError, "ERROR: cannot test for __lt__ because either or both of the sets are virtual."
547        if self.dimen != other.dimen:
548           raise ValueError, "Cannot perform set operation with sets "+self.name+" and "+other.name+" that have different element dimensions: "+str(self.dimen)+" "+str(other.dimen)
549        for val in self.value:
550          if val not in other:
551             return False
552        return len(self) < len(other)
553
554    def __gt__(self,other):
555        if self.virtual or \
556           (isinstance(other,_BaseSet) and other.virtual):
557           raise TypeError, "ERROR: cannot test for __lt__ because either or both of the sets are virtual."
558        if self.dimen != other.dimen:
559           raise ValueError, "Cannot perform set operation with sets "+self.name+" and "+other.name+" that have different element dimensions: "+str(self.dimen)+" "+str(other.dimen)
560        for val in other:
561          if val not in self.value:
562             return False
563        return len(self) > len(other)
564
565    def _set_repn(self,other):
566        if isinstance(other,_SetContainer) and other.virtual:
567            return other
568        if isinstance(other,_SetContainer) and type(other.value) is not dict:
569           return other.value
570        raise TypeError, "Cannot apply Set operation with a "+str(type(other))+" object"
571       
572    def __or__(self,other):
573        """ Return a _SetContainer object that is the union of this set
574        with another set. """
575        if self.virtual:
576           raise TypeError, "Cannot perform set operations with virtual set '"+self.name+"'"
577        if other.virtual:
578           raise TypeError, "Cannot perform set operations with virtual set '"+other.name+"'"
579        if self.dimen != other.dimen:
580           raise ValueError, "Cannot perform set operation with sets "+self.name+" and "+other.name+" that have different element dimensions: "+str(self.dimen)+" "+str(other.dimen)
581        def SetUnionRule(model):
582            data = set()
583            for val in getattr(model,SetUnionRule.Set1):
584              data.add(val)
585            for val in getattr(model,SetUnionRule.Set2):
586              data.add(val)
587            return data
588        SetUnionRule.Set1 = self.name
589        SetUnionRule.Set2 = other.name
590        return _SetContainer(dimen=self.dimen, initialize=SetUnionRule)
591
592    def __and__(self,other):
593        """Return a _SetContainer object that is the intersection of this set
594        with another set"""
595        if self.virtual:
596           raise TypeError, "Cannot perform set operations with virtual set '"+self.name+"'"
597        if other.virtual:
598           raise TypeError, "Cannot perform set operations with virtual set '"+other.name+"'"
599        if self.dimen != other.dimen:
600           raise ValueError, "Cannot perform set operation with sets "+self.name+" and "+other.name+" that have different element dimensions: "+str(self.dimen)+" "+str(other.dimen)
601        def SetIntersectionRule(model):
602            data = set()
603            for val in getattr(model,SetIntersectionRule.Set1):
604              if val in getattr(model,SetIntersectionRule.Set2):
605                 data.add(val)
606            return data
607        SetIntersectionRule.Set1 = self.name
608        SetIntersectionRule.Set2 = other.name
609        return _SetContainer(dimen=self.dimen, initialize=SetIntersectionRule)
610
611    def __xor__(self,other):
612        """Return a _SetContainer object that is the symmetric difference of
613        this set with another set"""
614        if self.virtual:
615           raise TypeError, "Cannot perform set operations with virtual set '"+self.name+"'"
616        if other.virtual:
617           raise TypeError, "Cannot perform set operations with virtual set '"+other.name+"'"
618        if self.dimen != other.dimen:
619           raise ValueError, "Cannot perform set operation with sets "+self.name+" and "+other.name+" that have different element dimensions: "+str(self.dimen)+" "+str(other.dimen)
620        def SetSymmetricDifferenceRule(M):
621            Set1 = getattr(M,SetSymmetricDifferenceRule.Set1)
622            Set2 = getattr(M,SetSymmetricDifferenceRule.Set2)
623            data = set()
624            for val in Set1:
625              if val not in Set2:
626                 data.add(val)
627            for val in Set2:
628              if val not in Set1:
629                 data.add(val)
630            return data
631        SetSymmetricDifferenceRule.Set1 = self.name
632        SetSymmetricDifferenceRule.Set2 = other.name
633        return _SetContainer(dimen=self.dimen, initialize=SetSymmetricDifferenceRule)
634
635    def __sub__(self,other):
636        """Return a _SetContainer object that is the difference between this set
637        and another set"""
638        if self.virtual:
639           raise TypeError, "Cannot perform set operations with virtual set '"+self.name+"'"
640        if other.virtual:
641           raise TypeError, "Cannot perform set operations with virtual set '"+other.name+"'"
642        if self.dimen != other.dimen:
643           raise ValueError, "Cannot perform set operation with sets "+self.name+" and "+other.name+" that have different element dimensions: "+str(self.dimen)+" "+str(other.dimen)
644        def SetDifferenceRule(model):
645            data = set()
646            for val in getattr(model,SetDifferenceRule.Set1):
647              if val not in getattr(model,SetDifferenceRule.Set2):
648                 data.add(val)
649            return data
650        SetDifferenceRule.Set1 = self.name
651        SetDifferenceRule.Set2 = other.name
652        return _SetContainer(dimen=self.dimen, initialize=SetDifferenceRule)
653
654    def __mul__(self,other):
655        """Return a _ProductSet that is the cross-product between this set and
656        and another set"""
657        if not isinstance(other,_SetContainer):
658           raise TypeError, "Set operator __mul__ cannot be applied with an object of type "+str(type(other))
659        if self.virtual:
660           raise TypeError, "Cannot perform set operations with virtual set '"+self.name+"'"
661        if other.virtual:
662           raise TypeError, "Cannot perform set operations with virtual set '"+other.name+"'"
663        if isinstance(self,_ProductSet) and self.name=="_unknown_":
664           self.set_tuple = tuple( list(self.set_tuple) + [other] )
665           self._compute_dimen()
666           return self
667        return _ProductSet(self,other)
668
669
670class _SetArray(_BaseSet):
671    """An array of sets, which are indexed by other sets"""
672
673    def __init__(self, *args, **kwds):      #pragma:nocover
674        """ Constructor """
675        self._index_set=None
676        self.value={}
677        self._ndim=len(args)
678        if len(args) == 1:
679            if isinstance(args[0],_BaseSet):
680                self._index=args[0]
681            else:
682                try:
683                    options = getattr(args[0],'set_options')
684                    options['initialize'] = args[0]
685                    self._index=Set(**options)
686                except:
687                    self._index=Set(initialize=args[0])
688        else:
689            self._index=None
690            tmp = []
691            for arg in args:
692                if isinstance(arg,_BaseSet):
693                    tmp.append(arg)
694                else:
695                    try:
696                        options = getattr(arg,'set_options')
697                        options['initialize'] = arg
698                        tmp.append( Set(**options) )
699                    except:
700                        tmp.append( Set(initialize=arg) )
701            self._index_set=tuple(tmp)
702        _BaseSet.__init__(self,**kwds)
703        self.value={}
704
705    def keys(self):
706        return self.value.keys()
707
708    def __contains__(self, element):
709        return element in self.value.keys()
710
711    def clear(self):
712        """Remove all elements from the set."""
713        for key in self.value:
714          self.value[key].clear()
715
716    def data(self):
717        """The underlying set data."""
718        raise TypeError, "Cannot access underlying set data for array set "+self.name
719
720    def __getitem__(self, key):
721        if key not in self.value:
722           raise KeyError, "Cannot access index "+str(key)+" in array set "+self.name
723        return self.value[key]
724
725    def __setitem__(self, key, val):
726        if key not in self._index:
727           raise KeyError, "Cannot set index "+str(key)+" in array set "+self.name
728        if key in self.value:
729           self.value[key].clear()
730           for elt in val:
731             self.value[key].add(elt)
732        else:
733           self.value[key] = Set(initialize=val,within=self.domain,validate=self.validate,ordered=self.ordered,name=self.name+"["+str(key)+"]",dimen=self.dimen)
734           self.value[key].construct()
735
736    def __len__(self):
737        result = 0
738        for val in self.value.values():
739            result += len(val)
740        return result
741
742    def __iter__(self):
743        """Return an iterator for the set array"""
744        return self.value.__iter__()
745
746    def check_values(self):
747        for key in self.value:
748          for val in self.value[key]:
749            self._verify(val,True)
750
751    def pprint(self, ostream=None):
752        if ostream is None:
753           ostream = sys.stdout
754        print >>ostream, "  ",self.name,":",
755        print >>ostream, "\tDim="+str(self.dim()),
756        print >>ostream, "\tDimen="+str(self.dimen),
757        if self.domain is None:
758           print >>ostream, "\tDomain="+str(self.domain),
759        else:
760           print >>ostream, "\tDomain="+str(self.domain.name),
761        print >>ostream, "\tArraySize="+str(len(self.value.keys())),
762        print >>ostream, "\tOrdered="+str(self.ordered)
763        if self.model is None:
764           print >>ostream, "\t Model=None"
765        else:
766           print >>ostream, "\t Model="+str(self.model.name)       
767        tmp=self.value.keys()
768        if tmp is not None:
769            tmp.sort()
770        else:
771            tmp=[]
772        for val in tmp:
773          if self.ordered:
774             ttmp = copy.copy(self.value[val].order)
775          else:
776             ttmp = copy.copy(list(self.value[val].value))
777             ttmp.sort()
778          print >>ostream, "\t",self.name+"["+str(val)+"]\t",ttmp
779
780    def construct(self, values=None):
781        """ Apply the rule to construct values in each set"""
782        log.debug("Constructing _SetArray, name="+self.name+", from data="+repr(values))
783        if self._constructed:
784            return
785        self._constructed=True
786        if self.virtual:                                #pragma:nocover
787           raise TypeError, "It doesn't make sense to create a virtual set array"
788        #
789        # Construct using the values list
790        #
791        if values is not None:
792           for key in values:
793             if type(key) is tuple and len(key)==1:
794                tmpkey=key[0]
795             else:
796                tmpkey=key
797             if tmpkey not in self._index:
798                raise KeyError, "Cannot set index "+str(tmpkey)+" in array set "+self.name
799             self.value[tmpkey] = Set(initialize=values[key],within=self.domain,validate=self.validate,ordered=self.ordered,name=self.name+"["+str(tmpkey)+"]",dimen=self.dimen)
800             self.value[tmpkey].model = self.model
801             self.value[tmpkey].construct()
802        #
803        # Construct using the rule
804        #
805        elif type(self.initialize) is types.FunctionType:
806           if self.model is None:
807              raise ValueError, "Need model to construct a set array with a function"
808           if self._index is None:
809              raise ValueError, "No index for set "+self.name
810           for key in self._index:
811             if isinstance(key,tuple):
812                tmp = list(key)
813             else:
814                tmp = [key]
815             if self.initialize.func_code.co_argcount == len(tmp)+1:
816                tmp.append(self.model)
817                tmp = tuple(tmp)
818                rule_list = list(self.initialize(*tmp))
819             else:
820                rule_list=[]
821                ctr=1
822                args = tuple(tmp+[ctr,self.model])
823                val = self.initialize(*args)
824                while val is not None:
825                  rule_list.append(val)
826                  ctr += 1
827                  args = tuple(tmp+[ctr,self.model])
828                  val = self.initialize(*args)
829             if self.dimen is None and len(rule_list) > 0:
830                   if type(rule_list[0]) in [tuple,list]:
831                        self.dimen=len(rule_list[0])
832                   else:
833                        self.dimen=1
834             self.value[key] = Set(initialize=rule_list,within=self.domain,validate=self.validate,ordered=self.ordered,name=self.name+"["+str(key)+"]",dimen=self.dimen)
835             self.value[key].model = self.model
836             self.value[key].construct()
837        #
838        # Construct using the default values
839        #
840        else:
841           if self.initialize is not None:
842              if type(self.initialize) is not dict:
843                 for key in self._index:
844                   self.value[key] = Set(initialize=self.initialize,within=self.domain,validate=self.validate,ordered=self.ordered,name=self.name+"["+str(key)+"]",dimen=self.dimen)
845                   self.value[key].model = self.model
846                   self.value[key].construct()
847              else:
848                 for key in self.initialize:
849                   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)
850                   self.value[key].model = self.model
851                   self.value[key].construct()
852
853
854##------------------------------------------------------------------------
855##
856## A set that defines an abstract cross-product set.
857##
858##------------------------------------------------------------------------
859
860class _ProductSet(_SetContainer):
861    """Set that represents a cross-product of other sets"""
862
863    """ Constructor
864        Arguments:
865           name         The name of this set
866           initialize   Default set members, which may be overriden
867                            when setting up this set
868           rule         A rule for setting up this set with
869                            existing model data.  This has the functional form:
870                            f: pyomo.Model -> set
871    """
872    def __init__(self, *args, **kwd):
873        self._class_override=False
874        self.set_tuple = args
875        tmp=()
876        _SetContainer.__init__(self,*tmp,**kwd)
877        self._compute_dimen()
878        self._len=0
879
880    def __iter__(self):
881        """Returns an iterator/generator for the cross-product"""
882        return pyutilib.misc.flattened_cross_iter(*self.set_tuple)
883
884    def __len__(self):
885        return self._len
886
887    def _compute_dimen(self):
888        ans=0
889        for set in self.set_tuple:
890            if set.dimen is None:
891                self.dimen=None
892                return
893            else:
894                ans += set.dimen
895        self.dimen = ans
896
897    def _verify(self,element,use_exception=True):
898        """
899        If this set is virtual, then an additional check is made
900        to ensure that the element is in each of the underlying sets.
901        """
902        tmp = _SetContainer._verify(self,element,use_exception)
903        if not tmp or not self.virtual:
904            return tmp
905
906        next_tuple_index = 0
907        member_set_index = 0
908        for member_set in self.set_tuple:
909           tuple_slice = element[next_tuple_index:next_tuple_index + member_set.dimen]
910           if member_set.dimen == 1:
911              tuple_slice = tuple_slice[0]
912           if tuple_slice not in member_set:
913             return False
914           member_set_index += 1
915           next_tuple_index += member_set.dimen
916        return True
917       
918    def construct(self, values=None):
919        """ Apply the rule to construct values in this set """
920        log.debug("Constructing _ProductSet, name="+self.name+", from data="+repr(values))
921        if self._constructed:
922            return
923        self._constructed=True
924        if self.virtual:
925           if len(self.set_tuple) == 0:
926              self._len=0
927           else:
928              self._len=1
929              for val in self.set_tuple:
930                self._len *= len(val)
931        #
932        # Construct using the values list
933        #
934        elif values is not None:
935           for val in values[None]:
936             self.add(val)
937        #
938        # Construct using the rule
939        #
940        elif type(self.initialize) is types.FunctionType:
941           for val in self.initialize(self.model):
942             self.add(val)
943        #
944        # Construct using the default values
945        #
946        else:
947           if self.initialize is not None:
948              if type(self.initialize) is dict:
949                 for val in self.initialize:
950                   self.add(val)
951              else:
952                 self.add(self.initialize)
953           else:
954              for val in pyutilib.misc.cross(self.set_tuple):
955                self.add(val)
956        #
957        if not self.virtual:
958            self._len=len(self.value)
959        self._compute_dimen()
960        if self.dimen is None:
961            raise ValueError, "The product set dimension must be computeable after constructing this set!"
962
963
964class Set(object):
965    """Set objects that are used to index other Pyomo objects
966
967       This class has a similar look-and-feel as a built-in set class.  However,
968       the set operations defined in this class return another abstract
969       Set object.  This class contains a concrete set, which can be
970       initialized by the load() method.
971    """
972    def __new__(cls, *args, **kwds):
973        if args == ():
974            self = _SetContainer(*args, **kwds)
975        else:
976            self = _SetArray(*args, **kwds)
977        return self
978
979
980ComponentRegistration("Set", Set, "Set data that is used to define a model instance.")
981
Note: See TracBrowser for help on using the repository browser.