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