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