info.py revision 1758:74acd5b23964
1
2# Copyright (c) 2003-2004 The Regents of The University of Michigan
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met: redistributions of source code must retain the above copyright
8# notice, this list of conditions and the following disclaimer;
9# redistributions in binary form must reproduce the above copyright
10# notice, this list of conditions and the following disclaimer in the
11# documentation and/or other materials provided with the distribution;
12# neither the name of the copyright holders nor the names of its
13# contributors may be used to endorse or promote products derived from
14# this software without specific prior written permission.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#Permission is granted to use, copy, create derivative works and
29#redistribute this software and such derivative works for any purpose,
30#so long as the copyright notice above, this grant of permission, and
31#the disclaimer below appear in all copies made; and so long as the
32#name of The University of Michigan is not used in any advertising or
33#publicity pertaining to the use or distribution of this software
34#without specific, written prior authorization.
35#
36#THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE
37#UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND WITHOUT
38#WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER EXPRESS OR
39#IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
40#MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE REGENTS OF
41#THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE FOR ANY DAMAGES,
42#INCLUDING DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
43#DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR IN CONNECTION
44#WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS HEREAFTER
45#ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
46
47from __future__ import division
48import operator, re, types
49
50source = None
51display_run = 0
52global globalTicks
53globalTicks = None
54
55def total(f):
56    if isinstance(f, FormulaStat):
57        v = f.value
58    else:
59        v = f
60
61    f = FormulaStat()
62    if isinstance(v, (list, tuple)):
63        f.value = reduce(operator.add, v)
64    else:
65        f.value = v
66
67    return f
68
69def unaryop(op, f):
70    if isinstance(f, FormulaStat):
71        v = f.value
72    else:
73        v = f
74
75    if isinstance(v, (list, tuple)):
76        return map(op, v)
77    else:
78        return op(v)
79
80def zerodiv(lv, rv):
81    if rv == 0.0:
82        return 0.0
83    else:
84        return operator.truediv(lv, rv)
85
86def wrapop(op, lv, rv):
87    if isinstance(lv, str):
88        return lv
89
90    if isinstance(rv, str):
91        return rv
92
93    return op(lv, rv)
94
95def same(lrun, rrun):
96    for lx,rx in zip(lrun.keys(),rrun.keys()):
97        if lx != rx:
98            print 'lx != rx'
99            print lx, rx
100            print lrun.keys()
101            print rrun.keys()
102            return False
103        for ly,ry in zip(lrun[lx].keys(),rrun[rx].keys()):
104            if ly != ry:
105                print 'ly != ry'
106                print ly, ry
107                print lrun[lx].keys()
108                print rrun[rx].keys()
109                return False
110    return True
111
112
113def binaryop(op, lf, rf):
114    result = {}
115
116    if isinstance(lf, FormulaStat) and isinstance(rf, FormulaStat):
117        lv = lf.value
118        rv = rf.value
119
120        theruns = []
121        for r in lv.keys():
122            if rv.has_key(r):
123                if same(lv[r], rv[r]):
124                    theruns.append(r)
125                else:
126                    raise AttributeError
127
128        for run in theruns:
129            result[run] = {}
130            for x in lv[run].keys():
131                result[run][x] = {}
132                for y in lv[run][x].keys():
133                    result[run][x][y] = wrapop(op, lv[run][x][y],
134                                               rv[run][x][y])
135    elif isinstance(lf, FormulaStat):
136        lv = lf.value
137        for run in lv.keys():
138            result[run] = {}
139            for x in lv[run].keys():
140                result[run][x] = {}
141                for y in lv[run][x].keys():
142                    result[run][x][y] = wrapop(op, lv[run][x][y], rf)
143    elif isinstance(rf, FormulaStat):
144        rv = rf.value
145        for run in rv.keys():
146            result[run] = {}
147            for x in rv[run].keys():
148                result[run][x] = {}
149                for y in rv[run][x].keys():
150                    result[run][x][y] = wrapop(op, lf, rv[run][x][y])
151
152    return result
153
154def sums(x, y):
155    if isinstance(x, (list, tuple)):
156        return map(lambda x, y: x + y, x, y)
157    else:
158        return x + y
159
160def alltrue(seq):
161    return reduce(lambda x, y: x and y, seq)
162
163def allfalse(seq):
164    return not reduce(lambda x, y: x or y, seq)
165
166def enumerate(seq):
167    return map(None, range(len(seq)), seq)
168
169def cmp(a, b):
170    if a < b:
171        return -1
172    elif a == b:
173        return 0
174    else:
175        return 1
176
177class Statistic(object):
178
179    def __init__(self, data):
180        self.__dict__.update(data.__dict__)
181        if not self.__dict__.has_key('value'):
182            self.__dict__['value'] = None
183        if not self.__dict__.has_key('bins'):
184            self.__dict__['bins'] = None
185        if not self.__dict__.has_key('ticks'):
186            self.__dict__['ticks'] = None
187        if 'vc' not in self.__dict__:
188            self.vc = {}
189
190    def __getattribute__(self, attr):
191        if attr == 'ticks':
192            if self.__dict__['ticks'] != globalTicks:
193                self.__dict__['value'] = None
194                self.__dict__['ticks'] = globalTicks
195            return self.__dict__['ticks']
196        if attr == 'value':
197            if self.__dict__['ticks'] != globalTicks:
198                if self.__dict__['ticks'] != None and \
199                                    len(self.__dict__['ticks']) == 1:
200                    self.vc[self.__dict__['ticks'][0]] = self.__dict__['value']
201                self.__dict__['ticks'] = globalTicks
202                if len(globalTicks) == 1 and self.vc.has_key(globalTicks[0]):
203                    self.__dict__['value'] = self.vc[globalTicks[0]]
204                else:
205                    self.__dict__['value'] = None
206            if self.__dict__['value'] == None:
207                self.__dict__['value'] = self.getValue()
208            return self.__dict__['value']
209        else:
210            return super(Statistic, self).__getattribute__(attr)
211
212    def __setattr__(self, attr, value):
213        if attr == 'bins' or attr == 'ticks':
214            if attr == 'bins':
215                if value is not None:
216                    value = source.getBin(value)
217            #elif attr == 'ticks' and type(value) is str:
218            #    value = [ int(x) for x in value.split() ]
219
220            self.__dict__[attr] = value
221            self.__dict__['value'] = None
222            self.vc = {}
223        else:
224            super(Statistic, self).__setattr__(attr, value)
225
226    def getValue(self):
227        raise AttributeError, 'getValue() must be defined'
228
229    def zero(self):
230        return False
231
232    def __ne__(self, other):
233        return not (self == other)
234
235    def __str__(self):
236        return '%f' % (float(self))
237
238class FormulaStat(object):
239    def __add__(self, other):
240        f = FormulaStat()
241        f.value = binaryop(operator.add, self, other)
242        return f
243    def __sub__(self, other):
244        f = FormulaStat()
245        f.value = binaryop(operator.sub, self, other)
246        return f
247    def __mul__(self, other):
248        f = FormulaStat()
249        f.value = binaryop(operator.mul, self, other)
250        return f
251    def __truediv__(self, other):
252        f = FormulaStat()
253        f.value = binaryop(zerodiv, self, other)
254        return f
255    def __mod__(self, other):
256        f = FormulaStat()
257        f.value = binaryop(operator.mod, self, other)
258        return f
259    def __radd__(self, other):
260        f = FormulaStat()
261        f.value = binaryop(operator.add, other, self)
262        return f
263    def __rsub__(self, other):
264        f = FormulaStat()
265        f.value = binaryop(operator.sub, other, self)
266        return f
267    def __rmul__(self, other):
268        f = FormulaStat()
269        f.value = binaryop(operator.mul, other, self)
270        return f
271    def __rtruediv__(self, other):
272        f = FormulaStat()
273        f.value = binaryop(zerodiv, other, self)
274        return f
275    def __rmod__(self, other):
276        f = FormulaStat()
277        f.value = binaryop(operator.mod, other, self)
278        return f
279    def __neg__(self):
280        f = FormulaStat()
281        f.value = unaryop(operator.neg, self)
282        return f
283    def __getitem__(self, idx):
284        f = FormulaStat()
285        f.value = {}
286        for key in self.value.keys():
287            f.value[key] = {}
288            f.value[key][0] = {}
289            f.value[key][0][0] = self.value[key][idx][0]
290        return f
291
292    def __float__(self):
293        if isinstance(self.value, FormulaStat):
294            return float(self.value)
295        if not self.value.has_key(display_run):
296            return (1e300*1e300)
297        if len(self.value[display_run]) == 1:
298            return self.value[display_run][0][0]
299        else:
300            #print self.value[display_run]
301            return self.value[display_run][4][0]
302            #raise ValueError
303
304    def display(self):
305        import display
306        d = display.VectorDisplay()
307        d.flags = 0
308        d.precision = 1
309        d.name = 'formula'
310        d.desc = 'formula'
311        val = self.value[display_run]
312        d.value = [ val[x][0] for x in val.keys() ]
313        d.display()
314
315
316class Scalar(Statistic,FormulaStat):
317    def getValue(self):
318        return source.data(self, self.bins, self.ticks)
319
320    def display(self):
321        import display
322        p = display.Print()
323        p.name = self.name
324        p.desc = self.desc
325        p.value = float(self)
326        p.flags = self.flags
327        p.precision = self.precision
328        if display.all or (self.flags & flags.printable):
329            p.display()
330
331    def comparable(self, other):
332        return self.name == other.name
333
334    def __eq__(self, other):
335        return self.value == other.value
336
337    def __isub__(self, other):
338        self.value -= other.value
339        return self
340
341    def __iadd__(self, other):
342        self.value += other.value
343        return self
344
345    def __itruediv__(self, other):
346        if not other:
347            return self
348        self.value /= other
349        return self
350
351class Vector(Statistic,FormulaStat):
352    def getValue(self):
353        return source.data(self, self.bins, self.ticks);
354
355    def display(self):
356        import display
357        if not display.all and not (self.flags & flags.printable):
358            return
359
360        d = display.VectorDisplay()
361        d.__dict__.update(self.__dict__)
362        d.display()
363
364    def comparable(self, other):
365        return self.name == other.name and \
366               len(self.value) == len(other.value)
367
368    def __eq__(self, other):
369        if isinstance(self.value, (list, tuple)) != \
370               isinstance(other.value, (list, tuple)):
371            return False
372
373        if isinstance(self.value, (list, tuple)):
374            if len(self.value) != len(other.value):
375                return False
376            else:
377                for v1,v2 in zip(self.value, other.value):
378                    if v1 != v2:
379                        return False
380                return True
381        else:
382            return self.value == other.value
383
384    def __isub__(self, other):
385        self.value = binaryop(operator.sub, self.value, other.value)
386        return self
387
388    def __iadd__(self, other):
389        self.value = binaryop(operator.add, self.value, other.value)
390        return self
391
392    def __itruediv__(self, other):
393        if not other:
394            return self
395        if isinstance(self.value, (list, tuple)):
396            for i in xrange(len(self.value)):
397                self.value[i] /= other
398        else:
399            self.value /= other
400        return self
401
402class Formula(Vector):
403    def getValue(self):
404        formula = re.sub(':', '__', self.formula)
405        x = eval(formula, source.stattop)
406        return x.value
407
408    def comparable(self, other):
409        return self.name == other.name and \
410               compare(self.dist, other.dist)
411
412    def __eq__(self, other):
413        return self.value == other.value
414
415    def __isub__(self, other):
416        return self
417
418    def __iadd__(self, other):
419        return self
420
421    def __itruediv__(self, other):
422        if not other:
423            return self
424        return self
425
426class SimpleDist(object):
427    def __init__(self, sums, squares, samples):
428        self.sums = sums
429        self.squares = squares
430        self.samples = samples
431
432    def getValue(self):
433        return 0.0
434
435    def display(self, name, desc, flags, precision):
436        import display
437        p = display.Print()
438        p.flags = flags
439        p.precision = precision
440
441        if self.samples > 0:
442            p.name = name + ".mean"
443            p.value = self.sums / self.samples
444            p.display()
445
446            p.name = name + ".stdev"
447            if self.samples > 1:
448                var = (self.samples * self.squares - self.sums ** 2) \
449                      / (self.samples * (self.samples - 1))
450                if var >= 0:
451                    p.value = math.sqrt(var)
452                else:
453                    p.value = 'NaN'
454            else:
455                p.value = 0.0
456            p.display()
457
458        p.name = name + ".samples"
459        p.value = self.samples
460        p.display()
461
462    def comparable(self, other):
463        return True
464
465    def __eq__(self, other):
466        return self.sums == other.sums and self.squares == other.squares and \
467               self.samples == other.samples
468
469    def __isub__(self, other):
470        self.sums -= other.sums
471        self.squares -= other.squares
472        self.samples -= other.samples
473        return self
474
475    def __iadd__(self, other):
476        self.sums += other.sums
477        self.squares += other.squares
478        self.samples += other.samples
479        return self
480
481    def __itruediv__(self, other):
482        if not other:
483            return self
484        self.sums /= other
485        self.squares /= other
486        self.samples /= other
487        return self
488
489class FullDist(SimpleDist):
490    def __init__(self, sums, squares, samples, minval, maxval,
491                 under, vec, over, min, max, bsize, size):
492        self.sums = sums
493        self.squares = squares
494        self.samples = samples
495        self.minval = minval
496        self.maxval = maxval
497        self.under = under
498        self.vec = vec
499        self.over = over
500        self.min = min
501        self.max = max
502        self.bsize = bsize
503        self.size = size
504
505    def getValue(self):
506        return 0.0
507
508    def display(self, name, desc, flags, precision):
509        import display
510        p = display.Print()
511        p.flags = flags
512        p.precision = precision
513
514        p.name = name + '.min_val'
515        p.value = self.minval
516        p.display()
517
518        p.name = name + '.max_val'
519        p.value = self.maxval
520        p.display()
521
522        p.name = name + '.underflow'
523        p.value = self.under
524        p.display()
525
526        i = self.min
527        for val in self.vec[:-1]:
528            p.name = name + '[%d:%d]' % (i, i + self.bsize - 1)
529            p.value = val
530            p.display()
531            i += self.bsize
532
533        p.name = name + '[%d:%d]' % (i, self.max)
534        p.value = self.vec[-1]
535        p.display()
536
537
538        p.name = name + '.overflow'
539        p.value = self.over
540        p.display()
541
542        SimpleDist.display(self, name, desc, flags, precision)
543
544    def comparable(self, other):
545        return self.min == other.min and self.max == other.max and \
546               self.bsize == other.bsize and self.size == other.size
547
548    def __eq__(self, other):
549        return self.sums == other.sums and self.squares == other.squares and \
550               self.samples == other.samples
551
552    def __isub__(self, other):
553        self.sums -= other.sums
554        self.squares -= other.squares
555        self.samples -= other.samples
556
557        if other.samples:
558            self.minval = min(self.minval, other.minval)
559            self.maxval = max(self.maxval, other.maxval)
560            self.under -= under
561            self.vec = map(lambda x,y: x - y, self.vec, other.vec)
562            self.over -= over
563        return self
564
565    def __iadd__(self, other):
566        if not self.samples and other.samples:
567            self = other
568            return self
569
570        self.sums += other.sums
571        self.squares += other.squares
572        self.samples += other.samples
573
574        if other.samples:
575            self.minval = min(self.minval, other.minval)
576            self.maxval = max(self.maxval, other.maxval)
577            self.under += other.under
578            self.vec = map(lambda x,y: x + y, self.vec, other.vec)
579            self.over += other.over
580        return self
581
582    def __itruediv__(self, other):
583        if not other:
584            return self
585        self.sums /= other
586        self.squares /= other
587        self.samples /= other
588
589        if self.samples:
590            self.under /= other
591            for i in xrange(len(self.vec)):
592                self.vec[i] /= other
593            self.over /= other
594        return self
595
596class Dist(Statistic):
597    def getValue(self):
598        return 0.0
599
600    def display(self):
601        import display
602        if not display.all and not (self.flags & flags.printable):
603            return
604
605        self.dist.display(self.name, self.desc, self.flags, self.precision)
606
607    def comparable(self, other):
608        return self.name == other.name and \
609               self.dist.compareable(other.dist)
610
611    def __eq__(self, other):
612        return self.dist == other.dist
613
614    def __isub__(self, other):
615        self.dist -= other.dist
616        return self
617
618    def __iadd__(self, other):
619        self.dist += other.dist
620        return self
621
622    def __itruediv__(self, other):
623        if not other:
624            return self
625        self.dist /= other
626        return self
627
628class VectorDist(Statistic):
629    def getValue(self):
630        return 0.0
631
632    def display(self):
633        import display
634        if not display.all and not (self.flags & flags.printable):
635            return
636
637        if isinstance(self.dist, SimpleDist):
638            return
639
640        for dist,sn,sd,i in map(None, self.dist, self.subnames, self.subdescs,
641                                range(len(self.dist))):
642            if len(sn) > 0:
643                name = '%s.%s' % (self.name, sn)
644            else:
645                name = '%s[%d]' % (self.name, i)
646
647            if len(sd) > 0:
648                desc = sd
649            else:
650                desc = self.desc
651
652            dist.display(name, desc, self.flags, self.precision)
653
654        if (self.flags & flags.total) or 1:
655            if isinstance(self.dist[0], SimpleDist):
656                disttotal = SimpleDist( \
657                    reduce(sums, [d.sums for d in self.dist]),
658                    reduce(sums, [d.squares for d in self.dist]),
659                    reduce(sums, [d.samples for d in self.dist]))
660            else:
661                disttotal = FullDist( \
662                    reduce(sums, [d.sums for d in self.dist]),
663                    reduce(sums, [d.squares for d in self.dist]),
664                    reduce(sums, [d.samples for d in self.dist]),
665                    min([d.minval for d in self.dist]),
666                    max([d.maxval for d in self.dist]),
667                    reduce(sums, [d.under for d in self.dist]),
668                    reduce(sums, [d.vec for d in self.dist]),
669                    reduce(sums, [d.over for d in self.dist]),
670                    dist[0].min,
671                    dist[0].max,
672                    dist[0].bsize,
673                    dist[0].size)
674
675            name = '%s.total' % (self.name)
676            desc = self.desc
677            disttotal.display(name, desc, self.flags, self.precision)
678
679    def comparable(self, other):
680        return self.name == other.name and \
681               alltrue(map(lambda x, y : x.comparable(y),
682                           self.dist,
683                           other.dist))
684
685    def __eq__(self, other):
686        return alltrue(map(lambda x, y : x == y, self.dist, other.dist))
687
688    def __isub__(self, other):
689        if isinstance(self.dist, (list, tuple)) and \
690               isinstance(other.dist, (list, tuple)):
691            for sd,od in zip(self.dist, other.dist):
692                sd -= od
693        else:
694            self.dist -= other.dist
695        return self
696
697    def __iadd__(self, other):
698        if isinstance(self.dist, (list, tuple)) and \
699               isinstance(other.dist, (list, tuple)):
700            for sd,od in zip(self.dist, other.dist):
701                sd += od
702        else:
703            self.dist += other.dist
704        return self
705
706    def __itruediv__(self, other):
707        if not other:
708            return self
709        if isinstance(self.dist, (list, tuple)):
710            for dist in self.dist:
711                dist /= other
712        else:
713            self.dist /= other
714        return self
715
716class Vector2d(Statistic):
717    def getValue(self):
718        return 0.0
719
720    def display(self):
721        import display
722        if not display.all and not (self.flags & flags.printable):
723            return
724
725        d = display.VectorDisplay()
726        d.__dict__.update(self.__dict__)
727
728        if self.__dict__.has_key('ysubnames'):
729            ysubnames = list(self.ysubnames)
730            slack = self.x - len(ysubnames)
731            if slack > 0:
732                ysubnames.extend(['']*slack)
733        else:
734            ysubnames = range(self.x)
735
736        for x,sname in enumerate(ysubnames):
737            o = x * self.y
738            d.value = self.value[o:o+self.y]
739            d.name = '%s[%s]' % (self.name, sname)
740            d.display()
741
742        if self.flags & flags.total:
743            d.value = []
744            for y in range(self.y):
745                xtot = 0.0
746                for x in range(self.x):
747                    xtot += self.value[y + x * self.x]
748                d.value.append(xtot)
749
750            d.name = self.name + '.total'
751            d.display()
752
753    def comparable(self, other):
754        return self.name == other.name and self.x == other.x and \
755               self.y == other.y
756
757    def __eq__(self, other):
758        return True
759
760    def __isub__(self, other):
761        return self
762
763    def __iadd__(self, other):
764        return self
765
766    def __itruediv__(self, other):
767        if not other:
768            return self
769        return self
770
771def NewStat(data):
772    stat = None
773    if data.type == 'SCALAR':
774        stat = Scalar(data)
775    elif data.type == 'VECTOR':
776        stat = Vector(data)
777    elif data.type == 'DIST':
778        stat = Dist(data)
779    elif data.type == 'VECTORDIST':
780        stat = VectorDist(data)
781    elif data.type == 'VECTOR2D':
782        stat = Vector2d(data)
783    elif data.type == 'FORMULA':
784        stat = Formula(data)
785
786    return stat
787
788