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