info.py revision 1934
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 149class VectorConstant(Vector): 150 def __init__(self, constant): 151 self.constant = constant 152 def __value__(self, run, index): 153 return self.constant[index] 154 def __len__(self): 155 return len(self.constant) 156 157def WrapValue(value): 158 if isinstance(value, (int, long, float)): 159 return ScalarConstant(value) 160 if isinstance(value, (list, tuple)): 161 return VectorConstant(value) 162 if isinstance(value, Value): 163 return value 164 165 raise AttributeError, 'Only values can be wrapped' 166 167class Statistic(object): 168 def __getattr__(self, attr): 169 if attr in ('data', 'x', 'y'): 170 result = self.source.data(self, self.bins, self.ticks) 171 self.data = result.data 172 self.x = result.x 173 self.y = result.y 174 return super(Statistic, self).__getattribute__(attr) 175 176 def __setattr__(self, attr, value): 177 if attr == 'stat': 178 raise AttributeError, '%s is read only' % stat 179 if attr in ('source', 'bins', 'ticks'): 180 if getattr(self, attr) != value: 181 if hasattr(self, 'data'): 182 delattr(self, 'data') 183 184 super(Statistic, self).__setattr__(attr, value) 185 186class ValueProxy(Value): 187 def __getattr__(self, attr): 188 if attr == '__value__': 189 if scalar(self): 190 return self.__scalarvalue__ 191 if vector(self): 192 return self.__vectorvalue__ 193 if attr == '__len__': 194 if vector(self): 195 return self.__vectorlen__ 196 return super(ValueProxy, self).__getattribute__(attr) 197 198class UnaryProxy(ValueProxy): 199 def __init__(self, op, arg): 200 self.op = op 201 self.arg = WrapValue(arg) 202 203 def __scalar__(self): 204 return scalar(self.arg) 205 206 def __vector__(self): 207 return vector(self.arg) 208 209 def __scalarvalue__(self, run): 210 val = value(self.arg, run) 211 if val is None: 212 return None 213 return self.op(val) 214 215 def __vectorvalue__(self, run, index): 216 val = value(self.arg, run, index) 217 if val is None: 218 return None 219 return self.op(val) 220 221 def __vectorlen__(self): 222 return len(unproxy(self.arg)) 223 224class BinaryProxy(ValueProxy): 225 def __init__(self, op, arg0, arg1): 226 super(BinaryProxy, self).__init__() 227 self.op = op 228 self.arg0 = WrapValue(arg0) 229 self.arg1 = WrapValue(arg1) 230 231 def __scalar__(self): 232 return scalar(self.arg0) and scalar(self.arg1) 233 234 def __vector__(self): 235 return vector(self.arg0) or vector(self.arg1) 236 237 def __scalarvalue__(self, run): 238 val0 = value(self.arg0, run) 239 val1 = value(self.arg1, run) 240 if val0 is None or val1 is None: 241 return None 242 return self.op(val0, val1) 243 244 def __vectorvalue__(self, run, index): 245 if scalar(self.arg0): 246 val0 = value(self.arg0, run) 247 if vector(self.arg0): 248 val0 = value(self.arg0, run, index) 249 if scalar(self.arg1): 250 val1 = value(self.arg1, run) 251 if vector(self.arg1): 252 val1 = value(self.arg1, run, index) 253 254 if val0 is None or val1 is None: 255 return None 256 257 return self.op(val0, val1) 258 259 def __vectorlen__(self): 260 if vector(self.arg0) and scalar(self.arg1): 261 return len(self.arg0) 262 if scalar(self.arg0) and vector(self.arg1): 263 return len(self.arg1) 264 265 len0 = len(self.arg0) 266 len1 = len(self.arg1) 267 268 if len0 != len1: 269 raise AttributeError, \ 270 "vectors of different lengths %d != %d" % (len0, len1) 271 272 return len0 273 274class Proxy(Value): 275 def __init__(self, name, dict): 276 self.name = name 277 self.dict = dict 278 279 def __unproxy__(self): 280 return unproxy(self.dict[self.name]) 281 282 def __getitem__(self, index): 283 return ItemProxy(self, index) 284 285 def __getattr__(self, attr): 286 return AttrProxy(self, attr) 287 288class ItemProxy(Proxy): 289 def __init__(self, proxy, index): 290 self.proxy = proxy 291 self.index = index 292 293 def __unproxy__(self): 294 return unproxy(unproxy(self.proxy)[self.index]) 295 296class AttrProxy(Proxy): 297 def __init__(self, proxy, attr): 298 self.proxy = proxy 299 self.attr = attr 300 301 def __unproxy__(self): 302 return unproxy(getattr(unproxy(self.proxy), self.attr)) 303 304class ProxyGroup(object): 305 def __init__(self, dict=None, **kwargs): 306 self.__dict__['dict'] = {} 307 308 if dict is not None: 309 self.dict.update(dict) 310 311 if kwargs: 312 self.dict.update(kwargs) 313 314 def __getattr__(self, name): 315 return Proxy(name, self.dict) 316 317 def __setattr__(self, attr, value): 318 self.dict[attr] = value 319 320class ScalarStat(Statistic,Scalar): 321 def __value__(self, run): 322 if run not in self.data: 323 return None 324 return self.data[run][0][0] 325 326 def display(self, run=None): 327 import display 328 p = display.Print() 329 p.name = self.name 330 p.desc = self.desc 331 p.value = value(self, run) 332 p.flags = self.flags 333 p.precision = self.precision 334 if display.all or (self.flags & flags.printable): 335 p.display() 336 337class VectorStat(Statistic,Vector): 338 def __value__(self, run, item): 339 if run not in self.data: 340 return None 341 return self.data[run][item][0] 342 343 def __len__(self): 344 return self.x 345 346 def display(self, run=None): 347 import display 348 d = display.VectorDisplay() 349 d.name = self.name 350 d.desc = self.desc 351 d.value = [ value(self, run, i) for i in xrange(len(self)) ] 352 d.flags = self.flags 353 d.precision = self.precision 354 d.display() 355 356class Formula(Value): 357 def __getattribute__(self, attr): 358 if attr not in ( '__scalar__', '__vector__', '__value__', '__len__' ): 359 return super(Formula, self).__getattribute__(attr) 360 361 formula = re.sub(':', '__', self.formula) 362 value = eval(formula, self.source.stattop) 363 return getattr(value, attr) 364 365class SimpleDist(Statistic): 366 def __init__(self, sums, squares, samples): 367 self.sums = sums 368 self.squares = squares 369 self.samples = samples 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 display(self, name, desc, flags, precision): 442 import display 443 p = display.Print() 444 p.flags = flags 445 p.precision = precision 446 447 p.name = name + '.min_val' 448 p.value = self.minval 449 p.display() 450 451 p.name = name + '.max_val' 452 p.value = self.maxval 453 p.display() 454 455 p.name = name + '.underflow' 456 p.value = self.under 457 p.display() 458 459 i = self.min 460 for val in self.vec[:-1]: 461 p.name = name + '[%d:%d]' % (i, i + self.bsize - 1) 462 p.value = val 463 p.display() 464 i += self.bsize 465 466 p.name = name + '[%d:%d]' % (i, self.max) 467 p.value = self.vec[-1] 468 p.display() 469 470 471 p.name = name + '.overflow' 472 p.value = self.over 473 p.display() 474 475 SimpleDist.display(self, name, desc, flags, precision) 476 477 def comparable(self, other): 478 return self.min == other.min and self.max == other.max and \ 479 self.bsize == other.bsize and self.size == other.size 480 481 def __eq__(self, other): 482 return self.sums == other.sums and self.squares == other.squares and \ 483 self.samples == other.samples 484 485 def __isub__(self, other): 486 self.sums -= other.sums 487 self.squares -= other.squares 488 self.samples -= other.samples 489 490 if other.samples: 491 self.minval = min(self.minval, other.minval) 492 self.maxval = max(self.maxval, other.maxval) 493 self.under -= under 494 self.vec = map(lambda x,y: x - y, self.vec, other.vec) 495 self.over -= over 496 return self 497 498 def __iadd__(self, other): 499 if not self.samples and other.samples: 500 self = other 501 return self 502 503 self.sums += other.sums 504 self.squares += other.squares 505 self.samples += other.samples 506 507 if other.samples: 508 self.minval = min(self.minval, other.minval) 509 self.maxval = max(self.maxval, other.maxval) 510 self.under += other.under 511 self.vec = map(lambda x,y: x + y, self.vec, other.vec) 512 self.over += other.over 513 return self 514 515 def __itruediv__(self, other): 516 if not other: 517 return self 518 self.sums /= other 519 self.squares /= other 520 self.samples /= other 521 522 if self.samples: 523 self.under /= other 524 for i in xrange(len(self.vec)): 525 self.vec[i] /= other 526 self.over /= other 527 return self 528 529class Dist(Statistic): 530 def display(self): 531 import display 532 if not display.all and not (self.flags & flags.printable): 533 return 534 535 self.dist.display(self.name, self.desc, self.flags, self.precision) 536 537 def comparable(self, other): 538 return self.name == other.name and \ 539 self.dist.compareable(other.dist) 540 541 def __eq__(self, other): 542 return self.dist == other.dist 543 544 def __isub__(self, other): 545 self.dist -= other.dist 546 return self 547 548 def __iadd__(self, other): 549 self.dist += other.dist 550 return self 551 552 def __itruediv__(self, other): 553 if not other: 554 return self 555 self.dist /= other 556 return self 557 558class VectorDist(Statistic): 559 def display(self): 560 import display 561 if not display.all and not (self.flags & flags.printable): 562 return 563 564 if isinstance(self.dist, SimpleDist): 565 return 566 567 for dist,sn,sd,i in map(None, self.dist, self.subnames, self.subdescs, 568 range(len(self.dist))): 569 if len(sn) > 0: 570 name = '%s.%s' % (self.name, sn) 571 else: 572 name = '%s[%d]' % (self.name, i) 573 574 if len(sd) > 0: 575 desc = sd 576 else: 577 desc = self.desc 578 579 dist.display(name, desc, self.flags, self.precision) 580 581 if (self.flags & flags.total) or 1: 582 if isinstance(self.dist[0], SimpleDist): 583 disttotal = SimpleDist( \ 584 reduce(sums, [d.sums for d in self.dist]), 585 reduce(sums, [d.squares for d in self.dist]), 586 reduce(sums, [d.samples for d in self.dist])) 587 else: 588 disttotal = FullDist( \ 589 reduce(sums, [d.sums for d in self.dist]), 590 reduce(sums, [d.squares for d in self.dist]), 591 reduce(sums, [d.samples for d in self.dist]), 592 min([d.minval for d in self.dist]), 593 max([d.maxval for d in self.dist]), 594 reduce(sums, [d.under for d in self.dist]), 595 reduce(sums, [d.vec for d in self.dist]), 596 reduce(sums, [d.over for d in self.dist]), 597 dist[0].min, 598 dist[0].max, 599 dist[0].bsize, 600 dist[0].size) 601 602 name = '%s.total' % (self.name) 603 desc = self.desc 604 disttotal.display(name, desc, self.flags, self.precision) 605 606 def comparable(self, other): 607 return self.name == other.name and \ 608 alltrue(map(lambda x, y : x.comparable(y), 609 self.dist, 610 other.dist)) 611 612 def __eq__(self, other): 613 return alltrue(map(lambda x, y : x == y, self.dist, other.dist)) 614 615 def __isub__(self, other): 616 if isinstance(self.dist, (list, tuple)) and \ 617 isinstance(other.dist, (list, tuple)): 618 for sd,od in zip(self.dist, other.dist): 619 sd -= od 620 else: 621 self.dist -= other.dist 622 return self 623 624 def __iadd__(self, other): 625 if isinstance(self.dist, (list, tuple)) and \ 626 isinstance(other.dist, (list, tuple)): 627 for sd,od in zip(self.dist, other.dist): 628 sd += od 629 else: 630 self.dist += other.dist 631 return self 632 633 def __itruediv__(self, other): 634 if not other: 635 return self 636 if isinstance(self.dist, (list, tuple)): 637 for dist in self.dist: 638 dist /= other 639 else: 640 self.dist /= other 641 return self 642 643class Vector2d(Statistic): 644 def display(self): 645 import display 646 if not display.all and not (self.flags & flags.printable): 647 return 648 649 d = display.VectorDisplay() 650 d.__dict__.update(self.__dict__) 651 652 if self.__dict__.has_key('ysubnames'): 653 ysubnames = list(self.ysubnames) 654 slack = self.x - len(ysubnames) 655 if slack > 0: 656 ysubnames.extend(['']*slack) 657 else: 658 ysubnames = range(self.x) 659 660 for x,sname in enumerate(ysubnames): 661 o = x * self.y 662 d.value = self.value[o:o+self.y] 663 d.name = '%s[%s]' % (self.name, sname) 664 d.display() 665 666 if self.flags & flags.total: 667 d.value = [] 668 for y in range(self.y): 669 xtot = 0.0 670 for x in range(self.x): 671 xtot += self.value[y + x * self.x] 672 d.value.append(xtot) 673 674 d.name = self.name + '.total' 675 d.display() 676 677 def comparable(self, other): 678 return self.name == other.name and self.x == other.x and \ 679 self.y == other.y 680 681 def __eq__(self, other): 682 return True 683 684 def __isub__(self, other): 685 return self 686 687 def __iadd__(self, other): 688 return self 689 690 def __itruediv__(self, other): 691 if not other: 692 return self 693 return self 694 695def NewStat(source, data): 696 stat = None 697 if data.type == 'SCALAR': 698 stat = ScalarStat() 699 elif data.type == 'VECTOR': 700 stat = VectorStat() 701 elif data.type == 'DIST': 702 stat = Dist() 703 elif data.type == 'VECTORDIST': 704 stat = VectorDist() 705 elif data.type == 'VECTOR2D': 706 stat = Vector2d() 707 elif data.type == 'FORMULA': 708 stat = Formula() 709 710 stat.__dict__['source'] = source 711 stat.__dict__['bins'] = None 712 stat.__dict__['ticks'] = None 713 stat.__dict__.update(data.__dict__) 714 715 return stat 716 717