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