| # Copyright (c) 2003-2004 The Regents of The University of Michigan | |
| # All rights reserved. | |
| # | |
| # Redistribution and use in source and binary forms, with or without | |
| # modification, are permitted provided that the following conditions are | |
| # met: redistributions of source code must retain the above copyright | |
| # notice, this list of conditions and the following disclaimer; | |
| # redistributions in binary form must reproduce the above copyright | |
| # notice, this list of conditions and the following disclaimer in the | |
| # documentation and/or other materials provided with the distribution; | |
| # neither the name of the copyright holders nor the names of its | |
| # contributors may be used to endorse or promote products derived from | |
| # this software without specific prior written permission. | |
| # | |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| # | |
| # Authors: Nathan Binkert | |
| from __future__ import division | |
| import operator, re, types | |
| class ProxyError(Exception): | |
| pass | |
| def unproxy(proxy): | |
| if hasattr(proxy, '__unproxy__'): | |
| return proxy.__unproxy__() | |
| return proxy | |
| def scalar(stat): | |
| stat = unproxy(stat) | |
| assert(stat.__scalar__() != stat.__vector__()) | |
| return stat.__scalar__() | |
| def vector(stat): | |
| stat = unproxy(stat) | |
| assert(stat.__scalar__() != stat.__vector__()) | |
| return stat.__vector__() | |
| def value(stat, *args): | |
| stat = unproxy(stat) | |
| return stat.__value__(*args) | |
| def values(stat, run): | |
| stat = unproxy(stat) | |
| result = [] | |
| for i in xrange(len(stat)): | |
| val = value(stat, run, i) | |
| if val is None: | |
| return None | |
| result.append(val) | |
| return result | |
| def total(stat, run): | |
| return sum(values(stat, run)) | |
| def len(stat): | |
| stat = unproxy(stat) | |
| return stat.__len__() | |
| class Value(object): | |
| def __scalar__(self): | |
| raise AttributeError, "must define __scalar__ for %s" % (type (self)) | |
| def __vector__(self): | |
| raise AttributeError, "must define __vector__ for %s" % (type (self)) | |
| def __add__(self, other): | |
| return BinaryProxy(operator.__add__, self, other) | |
| def __sub__(self, other): | |
| return BinaryProxy(operator.__sub__, self, other) | |
| def __mul__(self, other): | |
| return BinaryProxy(operator.__mul__, self, other) | |
| def __div__(self, other): | |
| return BinaryProxy(operator.__div__, self, other) | |
| def __truediv__(self, other): | |
| return BinaryProxy(operator.__truediv__, self, other) | |
| def __floordiv__(self, other): | |
| return BinaryProxy(operator.__floordiv__, self, other) | |
| def __radd__(self, other): | |
| return BinaryProxy(operator.__add__, other, self) | |
| def __rsub__(self, other): | |
| return BinaryProxy(operator.__sub__, other, self) | |
| def __rmul__(self, other): | |
| return BinaryProxy(operator.__mul__, other, self) | |
| def __rdiv__(self, other): | |
| return BinaryProxy(operator.__div__, other, self) | |
| def __rtruediv__(self, other): | |
| return BinaryProxy(operator.__truediv__, other, self) | |
| def __rfloordiv__(self, other): | |
| return BinaryProxy(operator.__floordiv__, other, self) | |
| def __neg__(self): | |
| return UnaryProxy(operator.__neg__, self) | |
| def __pos__(self): | |
| return UnaryProxy(operator.__pos__, self) | |
| def __abs__(self): | |
| return UnaryProxy(operator.__abs__, self) | |
| class Scalar(Value): | |
| def __scalar__(self): | |
| return True | |
| def __vector__(self): | |
| return False | |
| def __value__(self, run): | |
| raise AttributeError, '__value__ must be defined' | |
| class VectorItemProxy(Value): | |
| def __init__(self, proxy, index): | |
| self.proxy = proxy | |
| self.index = index | |
| def __scalar__(self): | |
| return True | |
| def __vector__(self): | |
| return False | |
| def __value__(self, run): | |
| return value(self.proxy, run, self.index) | |
| class Vector(Value): | |
| def __scalar__(self): | |
| return False | |
| def __vector__(self): | |
| return True | |
| def __value__(self, run, index): | |
| raise AttributeError, '__value__ must be defined' | |
| def __getitem__(self, index): | |
| return VectorItemProxy(self, index) | |
| class ScalarConstant(Scalar): | |
| def __init__(self, constant): | |
| self.constant = constant | |
| def __value__(self, run): | |
| return self.constant | |
| def __str__(self): | |
| return str(self.constant) | |
| class VectorConstant(Vector): | |
| def __init__(self, constant): | |
| self.constant = constant | |
| def __value__(self, run, index): | |
| return self.constant[index] | |
| def __len__(self): | |
| return len(self.constant) | |
| def __str__(self): | |
| return str(self.constant) | |
| def WrapValue(value): | |
| if isinstance(value, (int, long, float)): | |
| return ScalarConstant(value) | |
| if isinstance(value, (list, tuple)): | |
| return VectorConstant(value) | |
| if isinstance(value, Value): | |
| return value | |
| raise AttributeError, 'Only values can be wrapped' | |
| class Statistic(object): | |
| def __getattr__(self, attr): | |
| if attr in ('data', 'x', 'y'): | |
| result = self.source.data(self, self.ticks) | |
| self.data = result.data | |
| self.x = result.x | |
| self.y = result.y | |
| return super(Statistic, self).__getattribute__(attr) | |
| def __setattr__(self, attr, value): | |
| if attr == 'stat': | |
| raise AttributeError, '%s is read only' % stat | |
| if attr in ('source', 'ticks'): | |
| if getattr(self, attr) != value: | |
| if hasattr(self, 'data'): | |
| delattr(self, 'data') | |
| super(Statistic, self).__setattr__(attr, value) | |
| def __str__(self): | |
| return self.name | |
| class ValueProxy(Value): | |
| def __getattr__(self, attr): | |
| if attr == '__value__': | |
| if scalar(self): | |
| return self.__scalarvalue__ | |
| if vector(self): | |
| return self.__vectorvalue__ | |
| if attr == '__len__': | |
| if vector(self): | |
| return self.__vectorlen__ | |
| return super(ValueProxy, self).__getattribute__(attr) | |
| class UnaryProxy(ValueProxy): | |
| def __init__(self, op, arg): | |
| self.op = op | |
| self.arg = WrapValue(arg) | |
| def __scalar__(self): | |
| return scalar(self.arg) | |
| def __vector__(self): | |
| return vector(self.arg) | |
| def __scalarvalue__(self, run): | |
| val = value(self.arg, run) | |
| if val is None: | |
| return None | |
| return self.op(val) | |
| def __vectorvalue__(self, run, index): | |
| val = value(self.arg, run, index) | |
| if val is None: | |
| return None | |
| return self.op(val) | |
| def __vectorlen__(self): | |
| return len(unproxy(self.arg)) | |
| def __str__(self): | |
| if self.op == operator.__neg__: | |
| return '-%s' % str(self.arg) | |
| if self.op == operator.__pos__: | |
| return '+%s' % str(self.arg) | |
| if self.op == operator.__abs__: | |
| return 'abs(%s)' % self.arg | |
| class BinaryProxy(ValueProxy): | |
| def __init__(self, op, arg0, arg1): | |
| super(BinaryProxy, self).__init__() | |
| self.op = op | |
| self.arg0 = WrapValue(arg0) | |
| self.arg1 = WrapValue(arg1) | |
| def __scalar__(self): | |
| return scalar(self.arg0) and scalar(self.arg1) | |
| def __vector__(self): | |
| return vector(self.arg0) or vector(self.arg1) | |
| def __scalarvalue__(self, run): | |
| val0 = value(self.arg0, run) | |
| val1 = value(self.arg1, run) | |
| if val0 is None or val1 is None: | |
| return None | |
| try: | |
| return self.op(val0, val1) | |
| except ZeroDivisionError: | |
| return None | |
| def __vectorvalue__(self, run, index): | |
| if scalar(self.arg0): | |
| val0 = value(self.arg0, run) | |
| if vector(self.arg0): | |
| val0 = value(self.arg0, run, index) | |
| if scalar(self.arg1): | |
| val1 = value(self.arg1, run) | |
| if vector(self.arg1): | |
| val1 = value(self.arg1, run, index) | |
| if val0 is None or val1 is None: | |
| return None | |
| try: | |
| return self.op(val0, val1) | |
| except ZeroDivisionError: | |
| return None | |
| def __vectorlen__(self): | |
| if vector(self.arg0) and scalar(self.arg1): | |
| return len(self.arg0) | |
| if scalar(self.arg0) and vector(self.arg1): | |
| return len(self.arg1) | |
| len0 = len(self.arg0) | |
| len1 = len(self.arg1) | |
| if len0 != len1: | |
| raise AttributeError, \ | |
| "vectors of different lengths %d != %d" % (len0, len1) | |
| return len0 | |
| def __str__(self): | |
| ops = { operator.__add__ : '+', | |
| operator.__sub__ : '-', | |
| operator.__mul__ : '*', | |
| operator.__div__ : '/', | |
| operator.__truediv__ : '/', | |
| operator.__floordiv__ : '//' } | |
| return '(%s %s %s)' % (str(self.arg0), ops[self.op], str(self.arg1)) | |
| class Proxy(Value): | |
| def __init__(self, name, dict): | |
| self.name = name | |
| self.dict = dict | |
| def __unproxy__(self): | |
| return unproxy(self.dict[self.name]) | |
| def __getitem__(self, index): | |
| return ItemProxy(self, index) | |
| def __getattr__(self, attr): | |
| return AttrProxy(self, attr) | |
| def __str__(self): | |
| return str(self.dict[self.name]) | |
| class ItemProxy(Proxy): | |
| def __init__(self, proxy, index): | |
| self.proxy = proxy | |
| self.index = index | |
| def __unproxy__(self): | |
| return unproxy(unproxy(self.proxy)[self.index]) | |
| def __str__(self): | |
| return '%s[%s]' % (self.proxy, self.index) | |
| class AttrProxy(Proxy): | |
| def __init__(self, proxy, attr): | |
| self.proxy = proxy | |
| self.attr = attr | |
| def __unproxy__(self): | |
| proxy = unproxy(self.proxy) | |
| try: | |
| attr = getattr(proxy, self.attr) | |
| except AttributeError, e: | |
| raise ProxyError, e | |
| return unproxy(attr) | |
| def __str__(self): | |
| return '%s.%s' % (self.proxy, self.attr) | |
| class ProxyGroup(object): | |
| def __init__(self, dict=None, **kwargs): | |
| self.__dict__['dict'] = {} | |
| if dict is not None: | |
| self.dict.update(dict) | |
| if kwargs: | |
| self.dict.update(kwargs) | |
| def __getattr__(self, name): | |
| return Proxy(name, self.dict) | |
| def __setattr__(self, attr, value): | |
| self.dict[attr] = value | |
| class ScalarStat(Statistic,Scalar): | |
| def __value__(self, run): | |
| if run not in self.data: | |
| return None | |
| return self.data[run][0][0] | |
| def display(self, run=None): | |
| import display | |
| p = display.Print() | |
| p.name = self.name | |
| p.desc = self.desc | |
| p.value = value(self, run) | |
| p.flags = self.flags | |
| p.precision = self.precision | |
| if display.all or (self.flags & flags.printable): | |
| p.display() | |
| class VectorStat(Statistic,Vector): | |
| def __value__(self, run, item): | |
| if run not in self.data: | |
| return None | |
| return self.data[run][item][0] | |
| def __len__(self): | |
| return self.x | |
| def display(self, run=None): | |
| import display | |
| d = display.VectorDisplay() | |
| d.name = self.name | |
| d.desc = self.desc | |
| d.value = [ value(self, run, i) for i in xrange(len(self)) ] | |
| d.flags = self.flags | |
| d.precision = self.precision | |
| d.display() | |
| class Formula(Value): | |
| def __getattribute__(self, attr): | |
| if attr not in ( '__scalar__', '__vector__', '__value__', '__len__' ): | |
| return super(Formula, self).__getattribute__(attr) | |
| formula = re.sub(':', '__', self.formula) | |
| value = eval(formula, self.source.stattop) | |
| return getattr(value, attr) | |
| def __str__(self): | |
| return self.name | |
| class SimpleDist(Statistic): | |
| def __init__(self, sums, squares, samples): | |
| self.sums = sums | |
| self.squares = squares | |
| self.samples = samples | |
| def display(self, name, desc, flags, precision): | |
| import display | |
| p = display.Print() | |
| p.flags = flags | |
| p.precision = precision | |
| if self.samples > 0: | |
| p.name = name + ".mean" | |
| p.value = self.sums / self.samples | |
| p.display() | |
| p.name = name + ".stdev" | |
| if self.samples > 1: | |
| var = (self.samples * self.squares - self.sums ** 2) \ | |
| / (self.samples * (self.samples - 1)) | |
| if var >= 0: | |
| p.value = math.sqrt(var) | |
| else: | |
| p.value = 'NaN' | |
| else: | |
| p.value = 0.0 | |
| p.display() | |
| p.name = name + ".samples" | |
| p.value = self.samples | |
| p.display() | |
| def comparable(self, other): | |
| return True | |
| def __eq__(self, other): | |
| return self.sums == other.sums and self.squares == other.squares and \ | |
| self.samples == other.samples | |
| def __isub__(self, other): | |
| self.sums -= other.sums | |
| self.squares -= other.squares | |
| self.samples -= other.samples | |
| return self | |
| def __iadd__(self, other): | |
| self.sums += other.sums | |
| self.squares += other.squares | |
| self.samples += other.samples | |
| return self | |
| def __itruediv__(self, other): | |
| if not other: | |
| return self | |
| self.sums /= other | |
| self.squares /= other | |
| self.samples /= other | |
| return self | |
| class FullDist(SimpleDist): | |
| def __init__(self, sums, squares, samples, minval, maxval, | |
| under, vec, over, min, max, bsize, size): | |
| self.sums = sums | |
| self.squares = squares | |
| self.samples = samples | |
| self.minval = minval | |
| self.maxval = maxval | |
| self.under = under | |
| self.vec = vec | |
| self.over = over | |
| self.min = min | |
| self.max = max | |
| self.bsize = bsize | |
| self.size = size | |
| def display(self, name, desc, flags, precision): | |
| import display | |
| p = display.Print() | |
| p.flags = flags | |
| p.precision = precision | |
| p.name = name + '.min_val' | |
| p.value = self.minval | |
| p.display() | |
| p.name = name + '.max_val' | |
| p.value = self.maxval | |
| p.display() | |
| p.name = name + '.underflow' | |
| p.value = self.under | |
| p.display() | |
| i = self.min | |
| for val in self.vec[:-1]: | |
| p.name = name + '[%d:%d]' % (i, i + self.bsize - 1) | |
| p.value = val | |
| p.display() | |
| i += self.bsize | |
| p.name = name + '[%d:%d]' % (i, self.max) | |
| p.value = self.vec[-1] | |
| p.display() | |
| p.name = name + '.overflow' | |
| p.value = self.over | |
| p.display() | |
| SimpleDist.display(self, name, desc, flags, precision) | |
| def comparable(self, other): | |
| return self.min == other.min and self.max == other.max and \ | |
| self.bsize == other.bsize and self.size == other.size | |
| def __eq__(self, other): | |
| return self.sums == other.sums and self.squares == other.squares and \ | |
| self.samples == other.samples | |
| def __isub__(self, other): | |
| self.sums -= other.sums | |
| self.squares -= other.squares | |
| self.samples -= other.samples | |
| if other.samples: | |
| self.minval = min(self.minval, other.minval) | |
| self.maxval = max(self.maxval, other.maxval) | |
| self.under -= under | |
| self.vec = map(lambda x,y: x - y, self.vec, other.vec) | |
| self.over -= over | |
| return self | |
| def __iadd__(self, other): | |
| if not self.samples and other.samples: | |
| self = other | |
| return self | |
| self.sums += other.sums | |
| self.squares += other.squares | |
| self.samples += other.samples | |
| if other.samples: | |
| self.minval = min(self.minval, other.minval) | |
| self.maxval = max(self.maxval, other.maxval) | |
| self.under += other.under | |
| self.vec = map(lambda x,y: x + y, self.vec, other.vec) | |
| self.over += other.over | |
| return self | |
| def __itruediv__(self, other): | |
| if not other: | |
| return self | |
| self.sums /= other | |
| self.squares /= other | |
| self.samples /= other | |
| if self.samples: | |
| self.under /= other | |
| for i in xrange(len(self.vec)): | |
| self.vec[i] /= other | |
| self.over /= other | |
| return self | |
| class Dist(Statistic): | |
| def display(self): | |
| import display | |
| if not display.all and not (self.flags & flags.printable): | |
| return | |
| self.dist.display(self.name, self.desc, self.flags, self.precision) | |
| def comparable(self, other): | |
| return self.name == other.name and \ | |
| self.dist.compareable(other.dist) | |
| def __eq__(self, other): | |
| return self.dist == other.dist | |
| def __isub__(self, other): | |
| self.dist -= other.dist | |
| return self | |
| def __iadd__(self, other): | |
| self.dist += other.dist | |
| return self | |
| def __itruediv__(self, other): | |
| if not other: | |
| return self | |
| self.dist /= other | |
| return self | |
| class VectorDist(Statistic): | |
| def display(self): | |
| import display | |
| if not display.all and not (self.flags & flags.printable): | |
| return | |
| if isinstance(self.dist, SimpleDist): | |
| return | |
| for dist,sn,sd,i in map(None, self.dist, self.subnames, self.subdescs, | |
| range(len(self.dist))): | |
| if len(sn) > 0: | |
| name = '%s.%s' % (self.name, sn) | |
| else: | |
| name = '%s[%d]' % (self.name, i) | |
| if len(sd) > 0: | |
| desc = sd | |
| else: | |
| desc = self.desc | |
| dist.display(name, desc, self.flags, self.precision) | |
| if (self.flags & flags.total) or 1: | |
| if isinstance(self.dist[0], SimpleDist): | |
| disttotal = SimpleDist( \ | |
| reduce(sums, [d.sums for d in self.dist]), | |
| reduce(sums, [d.squares for d in self.dist]), | |
| reduce(sums, [d.samples for d in self.dist])) | |
| else: | |
| disttotal = FullDist( \ | |
| reduce(sums, [d.sums for d in self.dist]), | |
| reduce(sums, [d.squares for d in self.dist]), | |
| reduce(sums, [d.samples for d in self.dist]), | |
| min([d.minval for d in self.dist]), | |
| max([d.maxval for d in self.dist]), | |
| reduce(sums, [d.under for d in self.dist]), | |
| reduce(sums, [d.vec for d in self.dist]), | |
| reduce(sums, [d.over for d in self.dist]), | |
| dist[0].min, | |
| dist[0].max, | |
| dist[0].bsize, | |
| dist[0].size) | |
| name = '%s.total' % (self.name) | |
| desc = self.desc | |
| disttotal.display(name, desc, self.flags, self.precision) | |
| def comparable(self, other): | |
| return self.name == other.name and \ | |
| alltrue(map(lambda x, y : x.comparable(y), | |
| self.dist, | |
| other.dist)) | |
| def __eq__(self, other): | |
| return alltrue(map(lambda x, y : x == y, self.dist, other.dist)) | |
| def __isub__(self, other): | |
| if isinstance(self.dist, (list, tuple)) and \ | |
| isinstance(other.dist, (list, tuple)): | |
| for sd,od in zip(self.dist, other.dist): | |
| sd -= od | |
| else: | |
| self.dist -= other.dist | |
| return self | |
| def __iadd__(self, other): | |
| if isinstance(self.dist, (list, tuple)) and \ | |
| isinstance(other.dist, (list, tuple)): | |
| for sd,od in zip(self.dist, other.dist): | |
| sd += od | |
| else: | |
| self.dist += other.dist | |
| return self | |
| def __itruediv__(self, other): | |
| if not other: | |
| return self | |
| if isinstance(self.dist, (list, tuple)): | |
| for dist in self.dist: | |
| dist /= other | |
| else: | |
| self.dist /= other | |
| return self | |
| class Vector2d(Statistic): | |
| def display(self): | |
| import display | |
| if not display.all and not (self.flags & flags.printable): | |
| return | |
| d = display.VectorDisplay() | |
| d.__dict__.update(self.__dict__) | |
| if self.__dict__.has_key('ysubnames'): | |
| ysubnames = list(self.ysubnames) | |
| slack = self.x - len(ysubnames) | |
| if slack > 0: | |
| ysubnames.extend(['']*slack) | |
| else: | |
| ysubnames = range(self.x) | |
| for x,sname in enumerate(ysubnames): | |
| o = x * self.y | |
| d.value = self.value[o:o+self.y] | |
| d.name = '%s[%s]' % (self.name, sname) | |
| d.display() | |
| if self.flags & flags.total: | |
| d.value = [] | |
| for y in range(self.y): | |
| xtot = 0.0 | |
| for x in range(self.x): | |
| xtot += self.value[y + x * self.x] | |
| d.value.append(xtot) | |
| d.name = self.name + '.total' | |
| d.display() | |
| def comparable(self, other): | |
| return self.name == other.name and self.x == other.x and \ | |
| self.y == other.y | |
| def __eq__(self, other): | |
| return True | |
| def __isub__(self, other): | |
| return self | |
| def __iadd__(self, other): | |
| return self | |
| def __itruediv__(self, other): | |
| if not other: | |
| return self | |
| return self | |
| def NewStat(source, data): | |
| stat = None | |
| if data.type == 'SCALAR': | |
| stat = ScalarStat() | |
| elif data.type == 'VECTOR': | |
| stat = VectorStat() | |
| elif data.type == 'DIST': | |
| stat = Dist() | |
| elif data.type == 'VECTORDIST': | |
| stat = VectorDist() | |
| elif data.type == 'VECTOR2D': | |
| stat = Vector2d() | |
| elif data.type == 'FORMULA': | |
| stat = Formula() | |
| stat.__dict__['source'] = source | |
| stat.__dict__['ticks'] = None | |
| stat.__dict__.update(data.__dict__) | |
| return stat | |