info.py revision 1986
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 return self.op(val0, val1) 258 259 def __vectorvalue__(self, run, index): 260 if scalar(self.arg0): 261 val0 = value(self.arg0, run) 262 if vector(self.arg0): 263 val0 = value(self.arg0, run, index) 264 if scalar(self.arg1): 265 val1 = value(self.arg1, run) 266 if vector(self.arg1): 267 val1 = value(self.arg1, run, index) 268 269 if val0 is None or val1 is None: 270 return None 271 272 return self.op(val0, val1) 273 274 def __vectorlen__(self): 275 if vector(self.arg0) and scalar(self.arg1): 276 return len(self.arg0) 277 if scalar(self.arg0) and vector(self.arg1): 278 return len(self.arg1) 279 280 len0 = len(self.arg0) 281 len1 = len(self.arg1) 282 283 if len0 != len1: 284 raise AttributeError, \ 285 "vectors of different lengths %d != %d" % (len0, len1) 286 287 return len0 288 289 def __str__(self): 290 ops = { operator.__add__ : '+', 291 operator.__sub__ : '-', 292 operator.__mul__ : '*', 293 operator.__div__ : '/', 294 operator.__truediv__ : '/', 295 operator.__floordiv__ : '//' } 296 297 return '(%s %s %s)' % (str(self.arg0), ops[self.op], str(self.arg1)) 298 299class Proxy(Value): 300 def __init__(self, name, dict): 301 self.name = name 302 self.dict = dict 303 304 def __unproxy__(self): 305 return unproxy(self.dict[self.name]) 306 307 def __getitem__(self, index): 308 return ItemProxy(self, index) 309 310 def __getattr__(self, attr): 311 return AttrProxy(self, attr) 312 313 def __str__(self): 314 return str(self.dict[self.name]) 315 316class ItemProxy(Proxy): 317 def __init__(self, proxy, index): 318 self.proxy = proxy 319 self.index = index 320 321 def __unproxy__(self): 322 return unproxy(unproxy(self.proxy)[self.index]) 323 324 def __str__(self): 325 return '%s[%s]' % (self.proxy, self.index) 326 327class AttrProxy(Proxy): 328 def __init__(self, proxy, attr): 329 self.proxy = proxy 330 self.attr = attr 331 332 def __unproxy__(self): 333 return unproxy(getattr(unproxy(self.proxy), self.attr)) 334 335 def __str__(self): 336 return '%s.%s' % (self.proxy, self.attr) 337 338class ProxyGroup(object): 339 def __init__(self, dict=None, **kwargs): 340 self.__dict__['dict'] = {} 341 342 if dict is not None: 343 self.dict.update(dict) 344 345 if kwargs: 346 self.dict.update(kwargs) 347 348 def __getattr__(self, name): 349 return Proxy(name, self.dict) 350 351 def __setattr__(self, attr, value): 352 self.dict[attr] = value 353 354class ScalarStat(Statistic,Scalar): 355 def __value__(self, run): 356 if run not in self.data: 357 return None 358 return self.data[run][0][0] 359 360 def display(self, run=None): 361 import display 362 p = display.Print() 363 p.name = self.name 364 p.desc = self.desc 365 p.value = value(self, run) 366 p.flags = self.flags 367 p.precision = self.precision 368 if display.all or (self.flags & flags.printable): 369 p.display() 370 371class VectorStat(Statistic,Vector): 372 def __value__(self, run, item): 373 if run not in self.data: 374 return None 375 return self.data[run][item][0] 376 377 def __len__(self): 378 return self.x 379 380 def display(self, run=None): 381 import display 382 d = display.VectorDisplay() 383 d.name = self.name 384 d.desc = self.desc 385 d.value = [ value(self, run, i) for i in xrange(len(self)) ] 386 d.flags = self.flags 387 d.precision = self.precision 388 d.display() 389 390class Formula(Value): 391 def __getattribute__(self, attr): 392 if attr not in ( '__scalar__', '__vector__', '__value__', '__len__' ): 393 return super(Formula, self).__getattribute__(attr) 394 395 formula = re.sub(':', '__', self.formula) 396 value = eval(formula, self.source.stattop) 397 return getattr(value, attr) 398 399 def __str__(self): 400 return self.name 401 402class SimpleDist(Statistic): 403 def __init__(self, sums, squares, samples): 404 self.sums = sums 405 self.squares = squares 406 self.samples = samples 407 408 def display(self, name, desc, flags, precision): 409 import display 410 p = display.Print() 411 p.flags = flags 412 p.precision = precision 413 414 if self.samples > 0: 415 p.name = name + ".mean" 416 p.value = self.sums / self.samples 417 p.display() 418 419 p.name = name + ".stdev" 420 if self.samples > 1: 421 var = (self.samples * self.squares - self.sums ** 2) \ 422 / (self.samples * (self.samples - 1)) 423 if var >= 0: 424 p.value = math.sqrt(var) 425 else: 426 p.value = 'NaN' 427 else: 428 p.value = 0.0 429 p.display() 430 431 p.name = name + ".samples" 432 p.value = self.samples 433 p.display() 434 435 def comparable(self, other): 436 return True 437 438 def __eq__(self, other): 439 return self.sums == other.sums and self.squares == other.squares and \ 440 self.samples == other.samples 441 442 def __isub__(self, other): 443 self.sums -= other.sums 444 self.squares -= other.squares 445 self.samples -= other.samples 446 return self 447 448 def __iadd__(self, other): 449 self.sums += other.sums 450 self.squares += other.squares 451 self.samples += other.samples 452 return self 453 454 def __itruediv__(self, other): 455 if not other: 456 return self 457 self.sums /= other 458 self.squares /= other 459 self.samples /= other 460 return self 461 462class FullDist(SimpleDist): 463 def __init__(self, sums, squares, samples, minval, maxval, 464 under, vec, over, min, max, bsize, size): 465 self.sums = sums 466 self.squares = squares 467 self.samples = samples 468 self.minval = minval 469 self.maxval = maxval 470 self.under = under 471 self.vec = vec 472 self.over = over 473 self.min = min 474 self.max = max 475 self.bsize = bsize 476 self.size = size 477 478 def display(self, name, desc, flags, precision): 479 import display 480 p = display.Print() 481 p.flags = flags 482 p.precision = precision 483 484 p.name = name + '.min_val' 485 p.value = self.minval 486 p.display() 487 488 p.name = name + '.max_val' 489 p.value = self.maxval 490 p.display() 491 492 p.name = name + '.underflow' 493 p.value = self.under 494 p.display() 495 496 i = self.min 497 for val in self.vec[:-1]: 498 p.name = name + '[%d:%d]' % (i, i + self.bsize - 1) 499 p.value = val 500 p.display() 501 i += self.bsize 502 503 p.name = name + '[%d:%d]' % (i, self.max) 504 p.value = self.vec[-1] 505 p.display() 506 507 508 p.name = name + '.overflow' 509 p.value = self.over 510 p.display() 511 512 SimpleDist.display(self, name, desc, flags, precision) 513 514 def comparable(self, other): 515 return self.min == other.min and self.max == other.max and \ 516 self.bsize == other.bsize and self.size == other.size 517 518 def __eq__(self, other): 519 return self.sums == other.sums and self.squares == other.squares and \ 520 self.samples == other.samples 521 522 def __isub__(self, other): 523 self.sums -= other.sums 524 self.squares -= other.squares 525 self.samples -= other.samples 526 527 if other.samples: 528 self.minval = min(self.minval, other.minval) 529 self.maxval = max(self.maxval, other.maxval) 530 self.under -= under 531 self.vec = map(lambda x,y: x - y, self.vec, other.vec) 532 self.over -= over 533 return self 534 535 def __iadd__(self, other): 536 if not self.samples and other.samples: 537 self = other 538 return self 539 540 self.sums += other.sums 541 self.squares += other.squares 542 self.samples += other.samples 543 544 if other.samples: 545 self.minval = min(self.minval, other.minval) 546 self.maxval = max(self.maxval, other.maxval) 547 self.under += other.under 548 self.vec = map(lambda x,y: x + y, self.vec, other.vec) 549 self.over += other.over 550 return self 551 552 def __itruediv__(self, other): 553 if not other: 554 return self 555 self.sums /= other 556 self.squares /= other 557 self.samples /= other 558 559 if self.samples: 560 self.under /= other 561 for i in xrange(len(self.vec)): 562 self.vec[i] /= other 563 self.over /= other 564 return self 565 566class Dist(Statistic): 567 def display(self): 568 import display 569 if not display.all and not (self.flags & flags.printable): 570 return 571 572 self.dist.display(self.name, self.desc, self.flags, self.precision) 573 574 def comparable(self, other): 575 return self.name == other.name and \ 576 self.dist.compareable(other.dist) 577 578 def __eq__(self, other): 579 return self.dist == other.dist 580 581 def __isub__(self, other): 582 self.dist -= other.dist 583 return self 584 585 def __iadd__(self, other): 586 self.dist += other.dist 587 return self 588 589 def __itruediv__(self, other): 590 if not other: 591 return self 592 self.dist /= other 593 return self 594 595class VectorDist(Statistic): 596 def display(self): 597 import display 598 if not display.all and not (self.flags & flags.printable): 599 return 600 601 if isinstance(self.dist, SimpleDist): 602 return 603 604 for dist,sn,sd,i in map(None, self.dist, self.subnames, self.subdescs, 605 range(len(self.dist))): 606 if len(sn) > 0: 607 name = '%s.%s' % (self.name, sn) 608 else: 609 name = '%s[%d]' % (self.name, i) 610 611 if len(sd) > 0: 612 desc = sd 613 else: 614 desc = self.desc 615 616 dist.display(name, desc, self.flags, self.precision) 617 618 if (self.flags & flags.total) or 1: 619 if isinstance(self.dist[0], SimpleDist): 620 disttotal = SimpleDist( \ 621 reduce(sums, [d.sums for d in self.dist]), 622 reduce(sums, [d.squares for d in self.dist]), 623 reduce(sums, [d.samples for d in self.dist])) 624 else: 625 disttotal = FullDist( \ 626 reduce(sums, [d.sums for d in self.dist]), 627 reduce(sums, [d.squares for d in self.dist]), 628 reduce(sums, [d.samples for d in self.dist]), 629 min([d.minval for d in self.dist]), 630 max([d.maxval for d in self.dist]), 631 reduce(sums, [d.under for d in self.dist]), 632 reduce(sums, [d.vec for d in self.dist]), 633 reduce(sums, [d.over for d in self.dist]), 634 dist[0].min, 635 dist[0].max, 636 dist[0].bsize, 637 dist[0].size) 638 639 name = '%s.total' % (self.name) 640 desc = self.desc 641 disttotal.display(name, desc, self.flags, self.precision) 642 643 def comparable(self, other): 644 return self.name == other.name and \ 645 alltrue(map(lambda x, y : x.comparable(y), 646 self.dist, 647 other.dist)) 648 649 def __eq__(self, other): 650 return alltrue(map(lambda x, y : x == y, self.dist, other.dist)) 651 652 def __isub__(self, other): 653 if isinstance(self.dist, (list, tuple)) and \ 654 isinstance(other.dist, (list, tuple)): 655 for sd,od in zip(self.dist, other.dist): 656 sd -= od 657 else: 658 self.dist -= other.dist 659 return self 660 661 def __iadd__(self, other): 662 if isinstance(self.dist, (list, tuple)) and \ 663 isinstance(other.dist, (list, tuple)): 664 for sd,od in zip(self.dist, other.dist): 665 sd += od 666 else: 667 self.dist += other.dist 668 return self 669 670 def __itruediv__(self, other): 671 if not other: 672 return self 673 if isinstance(self.dist, (list, tuple)): 674 for dist in self.dist: 675 dist /= other 676 else: 677 self.dist /= other 678 return self 679 680class Vector2d(Statistic): 681 def display(self): 682 import display 683 if not display.all and not (self.flags & flags.printable): 684 return 685 686 d = display.VectorDisplay() 687 d.__dict__.update(self.__dict__) 688 689 if self.__dict__.has_key('ysubnames'): 690 ysubnames = list(self.ysubnames) 691 slack = self.x - len(ysubnames) 692 if slack > 0: 693 ysubnames.extend(['']*slack) 694 else: 695 ysubnames = range(self.x) 696 697 for x,sname in enumerate(ysubnames): 698 o = x * self.y 699 d.value = self.value[o:o+self.y] 700 d.name = '%s[%s]' % (self.name, sname) 701 d.display() 702 703 if self.flags & flags.total: 704 d.value = [] 705 for y in range(self.y): 706 xtot = 0.0 707 for x in range(self.x): 708 xtot += self.value[y + x * self.x] 709 d.value.append(xtot) 710 711 d.name = self.name + '.total' 712 d.display() 713 714 def comparable(self, other): 715 return self.name == other.name and self.x == other.x and \ 716 self.y == other.y 717 718 def __eq__(self, other): 719 return True 720 721 def __isub__(self, other): 722 return self 723 724 def __iadd__(self, other): 725 return self 726 727 def __itruediv__(self, other): 728 if not other: 729 return self 730 return self 731 732def NewStat(source, data): 733 stat = None 734 if data.type == 'SCALAR': 735 stat = ScalarStat() 736 elif data.type == 'VECTOR': 737 stat = VectorStat() 738 elif data.type == 'DIST': 739 stat = Dist() 740 elif data.type == 'VECTORDIST': 741 stat = VectorDist() 742 elif data.type == 'VECTOR2D': 743 stat = Vector2d() 744 elif data.type == 'FORMULA': 745 stat = Formula() 746 747 stat.__dict__['source'] = source 748 stat.__dict__['bins'] = None 749 stat.__dict__['ticks'] = None 750 stat.__dict__.update(data.__dict__) 751 752 return stat 753 754