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