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