| # 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 |
| |