From ee3ef206adcd9166d12fa5a4f947cc25440d533a Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 19 Sep 2018 11:53:57 -0700 Subject: [PATCH] timfuz: make rref deterministic Signed-off-by: John McMaster --- fuzzers/007-timing/rref.py | 32 ++++++---- fuzzers/007-timing/solve_linprog.py | 3 +- fuzzers/007-timing/test_zero/process.py | 15 +++-- fuzzers/007-timing/timfuz.py | 81 ++++++++++++++++++++++--- 4 files changed, 103 insertions(+), 28 deletions(-) diff --git a/fuzzers/007-timing/rref.py b/fuzzers/007-timing/rref.py index fedcfef5..f3169b64 100644 --- a/fuzzers/007-timing/rref.py +++ b/fuzzers/007-timing/rref.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -from timfuz import Benchmark, Ar_di2np, loadc_Ads_b, index_names, A_ds2np, simplify_rows +from timfuz import Benchmark, Ar_di2np, loadc_Ads_b, index_names, A_ds2np, simplify_rows, OrderedSet import numpy as np import glob import math @@ -27,14 +27,15 @@ class State(object): self.names = index_names(self.Ads) # known zero delay elements - self.drop_names = set(drop_names) + self.drop_names = OrderedSet(drop_names) # active names in rows # includes sub variables, excludes variables that have been substituted out - self.base_names = set(self.names) + self.base_names = OrderedSet(self.names) + #self.names = OrderedSet(self.base_names) self.names = set(self.base_names) # List of variable substitutions # k => dict of v:n entries that it came from - self.subs = {} + self.subs = OrderedDict() self.verbose = True def print_stats(self): @@ -63,11 +64,16 @@ class State(object): def write_state(state, fout): j = { - 'names': dict([(x, None) for x in state.names]), - 'drop_names': list(state.drop_names), - 'base_names': list(state.base_names), - 'subs': dict([(name, values) for name, values in state.subs.items()]), - 'pivots': state.pivots, + 'names': + OrderedDict([(x, None) for x in state.names]), + 'drop_names': + list(state.drop_names), + 'base_names': + list(state.base_names), + 'subs': + OrderedDict([(name, values) for name, values in state.subs.items()]), + 'pivots': + state.pivots, } json.dump(j, fout, sort_keys=True, indent=4, separators=(',', ': ')) @@ -89,7 +95,7 @@ def Anp2matrix(Anp): def row_np2ds(rownp, names): - ret = {} + ret = OrderedDict() assert len(rownp) == len(names), (len(rownp), len(names)) for namei, name in enumerate(names): v = rownp[namei] @@ -102,7 +108,7 @@ def row_sym2dsf(rowsym, names): '''Convert a sympy row into a dictionary of keys to (numerator, denominator) tuples''' from sympy import fraction - ret = {} + ret = OrderedDict() assert len(rowsym) == len(names), (len(rowsym), len(names)) for namei, name in enumerate(names): v = rowsym[namei] @@ -145,7 +151,7 @@ def state_rref(state, verbose=False): print('rref') sympy.pprint(rref) - state.pivots = {} + state.pivots = OrderedDict() def row_solved(rowsym, row_pivot): for ci, c in enumerate(rowsym): @@ -177,7 +183,7 @@ def state_rref(state, verbose=False): state.names.add(group_name) # Remove substituted variables # Note: variables may appear multiple times - state.names.difference_update(set(rowdsf.keys())) + state.names.difference_update(OrderedSet(rowdsf.keys())) pivot_name = names[row_pivot] state.pivots[group_name] = pivot_name if verbose: diff --git a/fuzzers/007-timing/solve_linprog.py b/fuzzers/007-timing/solve_linprog.py index 02ed7396..0825bac3 100644 --- a/fuzzers/007-timing/solve_linprog.py +++ b/fuzzers/007-timing/solve_linprog.py @@ -192,7 +192,8 @@ def main(): parser.add_argument('--verbose', action='store_true', help='') parser.add_argument('--massage', action='store_true', help='') - parser.add_argument('--bounds-csv', help='Previous solve result starting point') + parser.add_argument( + '--bounds-csv', help='Previous solve result starting point') parser.add_argument( '--sub-json', help='Group substitutions to make fully ranked') parser.add_argument('--corner', required=True, default="slow_max", help='') diff --git a/fuzzers/007-timing/test_zero/process.py b/fuzzers/007-timing/test_zero/process.py index 21d322eb..3b2ce10c 100644 --- a/fuzzers/007-timing/test_zero/process.py +++ b/fuzzers/007-timing/test_zero/process.py @@ -7,7 +7,6 @@ import json def run_types(tilej, verbose=False): - def process(etype): # dict[model] = set((tile, wire/pip)) zeros = {} @@ -19,7 +18,9 @@ def run_types(tilej, verbose=False): zeros.setdefault(emodel, set()).add((tilek, ename)) # Print out delay model instances print('%s ZERO types: %u, %s' % (etype, len(zeros), zeros.keys())) - print('%s ZERO instances: %u' % (etype, sum([len(x) for x in zeros.values()]))) + print( + '%s ZERO instances: %u' % + (etype, sum([len(x) for x in zeros.values()]))) for model in sorted(zeros.keys()): modelv = zeros[model] print('Model: %s' % model) @@ -30,8 +31,8 @@ def run_types(tilej, verbose=False): print('') process('pips') -def run_prefix(tilej, verbose=False): +def run_prefix(tilej, verbose=False): def process(etype): prefixes = set() print('Processing %s' % etype) @@ -48,6 +49,7 @@ def run_prefix(tilej, verbose=False): print('') process('pips') + def run(fnin, verbose=False): tilej = json.load((open(fnin, 'r'))) run_types(tilej) @@ -55,11 +57,16 @@ def run(fnin, verbose=False): print('') run_prefix(tilej) + def main(): import argparse parser = argparse.ArgumentParser(description='Solve timing solution') - parser.add_argument('fnin', default="../timgrid/build/timgrid-s.json", nargs='?', help='input timgrid JSON') + parser.add_argument( + 'fnin', + default="../timgrid/build/timgrid-s.json", + nargs='?', + help='input timgrid JSON') args = parser.parse_args() run(args.fnin, verbose=False) diff --git a/fuzzers/007-timing/timfuz.py b/fuzzers/007-timing/timfuz.py index 581de3a0..a3bc3a1e 100644 --- a/fuzzers/007-timing/timfuz.py +++ b/fuzzers/007-timing/timfuz.py @@ -13,10 +13,76 @@ import sys import random import glob from fractions import Fraction +import collections from benchmark import Benchmark -NAME_ZERO = set( + +# Equations are filtered out until nothing is left +class SimplifiedToZero(Exception): + pass + + +# http://code.activestate.com/recipes/576694/ +class OrderedSet(collections.MutableSet): + def __init__(self, iterable=None): + self.end = end = [] + end += [None, end, end] # sentinel node for doubly linked list + self.map = {} # key --> [key, prev, next] + if iterable is not None: + self |= iterable + + def __len__(self): + return len(self.map) + + def __contains__(self, key): + return key in self.map + + def add(self, key): + if key not in self.map: + end = self.end + curr = end[1] + curr[2] = end[1] = self.map[key] = [key, curr, end] + + def discard(self, key): + if key in self.map: + key, prev, next = self.map.pop(key) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def pop(self, last=True): + if not self: + raise KeyError('set is empty') + key = self.end[1][0] if last else self.end[2][0] + self.discard(key) + return key + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__, ) + return '%s(%r)' % (self.__class__.__name__, list(self)) + + def __eq__(self, other): + if isinstance(other, OrderedSet): + return len(self) == len(other) and list(self) == list(other) + return set(self) == set(other) + + +NAME_ZERO = OrderedSet( [ "BSW_CLK_ZERO", "BSW_ZERO", @@ -40,11 +106,6 @@ corner_s2i = OrderedDict( ]) -# Equations are filtered out until nothing is left -class SimplifiedToZero(Exception): - pass - - def allow_zero_eqns(): return os.getenv('ALLOW_ZERO_EQN', 'N') == 'Y' @@ -148,7 +209,7 @@ def check_feasible(A_ub, b_ub): def Ab_ub_dt2d(eqns): '''Convert dict using the rows as keys into a list of dicts + b_ub list (ie return A_ub, b_ub)''' #return [dict(rowt) for rowt in eqns] - rows = [(dict(rowt), b) for rowt, b in eqns.items()] + rows = [(OrderedDict(rowt), b) for rowt, b in eqns.items()] A_ubd, b_ub = zip(*rows) return list(A_ubd), list(b_ub) @@ -411,7 +472,7 @@ def derive_eq_by_col(A_ubd, b_ub, verbose=0): b_ub[row_refi] /= v knowns[k] = b_ub[row_refi] print(' done') - #knowns_set = set(knowns.keys()) + #knowns_set = OrderedSet(knowns.keys()) print('%d constrained' % len(knowns)) ''' Now see what we can do @@ -648,7 +709,7 @@ def loadc_Ads_raw(fns): def index_names(Ads): - names = set() + names = OrderedSet() for row_ds in Ads: for k1 in row_ds.keys(): names.add(k1) @@ -740,7 +801,7 @@ def run_sub_json(Ads, sub_json, strict=False, verbose=False): ncols_new = 0 print('Subbing %u rows' % len(Ads)) - prints = set() + prints = OrderedSet() for rowi, row in enumerate(Ads): if 0 and verbose: