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