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