diff --git a/fuzzers/007-timing/csv_group2flat.py b/fuzzers/007-timing/csv_group2flat.py index a80c11ac..a9bd79ac 100644 --- a/fuzzers/007-timing/csv_group2flat.py +++ b/fuzzers/007-timing/csv_group2flat.py @@ -6,36 +6,48 @@ from timfuz import Benchmark, loadc_Ads_bs, load_sub, Ads2bounds, corners2csv, c def gen_flat(fns_in, sub_json, corner=None): Ads, bs = loadc_Ads_bs(fns_in, ico=True) bounds = Ads2bounds(Ads, bs) - zeros = set() + # Elements with zero delay assigned due to sub group + group_zeros = set() + # Elements with a concrete delay nonzeros = set() + if corner: + zero_row = [None, None, None, None] + zero_row[corner_s2i[corner]] = 0 + else: + zero_row = None + for bound_name, bound_bs in bounds.items(): sub = sub_json['subs'].get(bound_name, None) - if sub: + if bound_name in sub_json['zero_names']: + if zero_row: + yield bound_name, 0 + elif sub: + print('sub', sub) # put entire delay into pivot pivot = sub_json['pivots'][bound_name] - assert pivot not in zeros + assert pivot not in group_zeros nonzeros.add(pivot) non_pivot = set(sub.keys() - set([pivot])) #for name in non_pivot: # assert name not in nonzeros, (pivot, name, nonzeros) - zeros.update(non_pivot) + group_zeros.update(non_pivot) yield pivot, bound_bs else: nonzeros.add(bound_name) yield bound_name, bound_bs # non-pivots can appear multiple times, but they should always be zero # however, due to substitution limitations, just warn - violations = zeros.intersection(nonzeros) + violations = group_zeros.intersection(nonzeros) if len(violations): print('WARNING: %s non-0 non-pivot' % (len(violations))) # XXX: how to best handle these? # should they be fixed 0? - if corner: - zero_row = [None, None, None, None] - zero_row[corner_s2i[corner]] = 0 - for zero in zeros - violations: + if zero_row: + print('zero_row', len(group_zeros), len(violations)) + for zero in group_zeros - violations: + print('zero', zero) yield zero, zero_row diff --git a/fuzzers/007-timing/rref.py b/fuzzers/007-timing/rref.py index 1c665dd0..18a60a67 100644 --- a/fuzzers/007-timing/rref.py +++ b/fuzzers/007-timing/rref.py @@ -9,6 +9,20 @@ import sympy from collections import OrderedDict from fractions import Fraction +def rm_zero_cols(Ads, verbose=True): + removed = OrderedSet() + + print('Removing ZERO elements') + for row_ds in Ads: + for k in set(row_ds.keys()): + if k in removed: + del row_ds[k] + elif k.find('ZERO') >= 0: + del row_ds[k] + removed.add(k) + if verbose: + print(' Removing %s' % k) + return removed def fracr_quick(r): return [Fraction(numerator=int(x), denominator=1) for x in r] @@ -20,14 +34,13 @@ def fracm_quick(m): print('fracm_quick type: %s' % t) return [fracr_quick(r) for r in m] - class State(object): - def __init__(self, Ads, drop_names=[]): + def __init__(self, Ads, zero_names=[]): self.Ads = Ads self.names = index_names(self.Ads) # known zero delay elements - self.drop_names = OrderedSet(drop_names) + self.zero_names = OrderedSet(zero_names) # active names in rows # includes sub variables, excludes variables that have been substituted out self.base_names = OrderedSet(self.names) @@ -46,30 +59,34 @@ class State(object): " Largest: %u" % max([len(x) for x in self.subs.values()])) print(" Rows: %u" % len(self.Ads)) print( - " Cols (in): %u" % (len(self.base_names) + len(self.drop_names))) + " Cols (in): %u" % (len(self.base_names) + len(self.zero_names))) print(" Cols (preprocessed): %u" % len(self.base_names)) - print(" Drop names: %u" % len(self.drop_names)) + print(" ZERO names: %u" % len(self.zero_names)) print(" Cols (out): %u" % len(self.names)) print(" Solvable vars: %u" % len(self.names & self.base_names)) assert len(self.names) >= len(self.subs) @staticmethod - def load(fn_ins, simplify=False, corner=None): + def load(fn_ins, simplify=False, corner=None, rm_zero=False): + zero_names = OrderedSet() + Ads, b = loadc_Ads_b(fn_ins, corner=corner, ico=True) + if rm_zero: + zero_names = rm_zero_cols(Ads) if simplify: print('Simplifying corner %s' % (corner, )) Ads, b = simplify_rows(Ads, b, remove_zd=False, corner=corner) - return State(Ads) + return State(Ads, zero_names=zero_names) def write_state(state, fout): j = { 'names': OrderedDict([(x, None) for x in state.names]), - 'drop_names': - list(state.drop_names), + 'zero_names': + sorted(list(state.zero_names)), 'base_names': - list(state.base_names), + sorted(list(state.base_names)), 'subs': OrderedDict([(name, values) for name, values in state.subs.items()]), 'pivots': @@ -172,11 +189,11 @@ def state_rref(state, verbose=False): return state -def run(fnout, fn_ins, simplify=False, corner=None, verbose=0): +def run(fnout, fn_ins, simplify=False, corner=None, rm_zero=False, verbose=0): print('Loading data') assert len(fn_ins) > 0 - state = State.load(fn_ins, simplify=simplify, corner=corner) + state = State.load(fn_ins, simplify=simplify, corner=corner, rm_zero=rm_zero) state_rref(state, verbose=verbose) state.print_stats() if fnout: @@ -195,6 +212,7 @@ def main(): parser.add_argument('--verbose', action='store_true', help='') parser.add_argument('--simplify', action='store_true', help='') parser.add_argument('--corner', default="slow_max", help='') + parser.add_argument('--rm-zero', action='store_true', help='Remove ZERO elements') parser.add_argument( '--speed-json', default='build_speed/speed.json', @@ -214,6 +232,7 @@ def main(): fn_ins=fns_in, simplify=args.simplify, corner=args.corner, + rm_zero=args.rm_zero, verbose=args.verbose) finally: print('Exiting after %s' % bench) diff --git a/fuzzers/007-timing/solve_leastsq.py b/fuzzers/007-timing/solve_leastsq.py index db22b168..953c4dcf 100644 --- a/fuzzers/007-timing/solve_leastsq.py +++ b/fuzzers/007-timing/solve_leastsq.py @@ -30,47 +30,6 @@ def mkestimate(Anp, b): return x0 -def save(outfn, xvals, names, corner): - # ballpark minimum actual observed delay is around 7 (carry chain) - # anything less than one is probably a solver artifact - delta = 0.5 - corneri = corner_s2i[corner] - - # Round conservatively - roundf = { - 'fast_max': math.ceil, - 'fast_min': math.floor, - 'slow_max': math.ceil, - 'slow_min': math.floor, - }[corner] - - print('Writing results') - skips = 0 - keeps = 0 - with open(outfn, 'w') as fout: - # write as one variable per line - # this natively forms a bound if fed into linprog solver - fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n') - for xval, name in zip(xvals, names): - row_ico = 1 - - # also review ceil vs floor choice for min vs max - # lets be more conservative for now - if xval < delta: - #print('Skipping %s: %0.6f' % (name, xval)) - skips += 1 - continue - keeps += 1 - #xvali = round(xval) - - items = [str(row_ico), acorner2csv(roundf(xval), corneri)] - items.append('%u %s' % (1, name)) - fout.write(','.join(items) + '\n') - print( - 'Wrote: skip %u => %u / %u valid delays' % (skips, keeps, len(names))) - assert keeps, 'Failed to estimate delay' - - def run_corner( Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=None): # Given timing scores for above delays (-ps) @@ -140,7 +99,7 @@ def run_corner( print('Done') if outfn: - save(outfn, res.x, names, corner) + timfuz_solve.solve_save(outfn, res.x, names, corner, verbose=verbose) def main(): diff --git a/fuzzers/007-timing/solve_linprog.py b/fuzzers/007-timing/solve_linprog.py index 0825bac3..e3e99c18 100644 --- a/fuzzers/007-timing/solve_linprog.py +++ b/fuzzers/007-timing/solve_linprog.py @@ -12,50 +12,12 @@ import time import timfuz_solve -def save(outfn, xvals, names, corner): - # ballpark minimum actual observed delay is around 7 (carry chain) - # anything less than one is probably a solver artifact - delta = 0.5 - corneri = corner_s2i[corner] - - roundf = { - 'fast_max': math.ceil, - 'fast_min': math.floor, - 'slow_max': math.ceil, - 'slow_min': math.floor, - }[corner] - - print('Writing results') - zeros = 0 - with open(outfn, 'w') as fout: - # write as one variable per line - # this natively forms a bound if fed into linprog solver - fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n') - for xval, name in zip(xvals, names): - row_ico = 1 - - # FIXME: only report for the given corner? - # also review ceil vs floor choice for min vs max - # lets be more conservative for now - if xval < delta: - print('WARNING: near 0 delay on %s: %0.6f' % (name, xval)) - zeros += 1 - #continue - items = [str(row_ico), acorner2csv(roundf(xval), corneri)] - items.append('%u %s' % (1, name)) - fout.write(','.join(items) + '\n') - nonzeros = len(names) - zeros - print( - 'Wrote: %u / %u constrained delays, %u zeros' % - (nonzeros, len(names), zeros)) - - def run_corner( Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=None): if len(Anp) == 0: print('WARNING: zero equations') if outfn: - save(outfn, [], [], corner) + timfuz_solve.solve_save(outfn, [], [], corner) return maxcorner = { 'slow_max': True, @@ -180,7 +142,7 @@ def run_corner( print('Delay on %d / %d' % (nonzeros, len(res.x))) if outfn: - save(outfn, res.x, names, corner) + timfuz_solve.solve_save(outfn, res.x, names, corner, verbose=verbose) def main(): diff --git a/fuzzers/007-timing/timfuz.py b/fuzzers/007-timing/timfuz.py index a3bc3a1e..540c4abc 100644 --- a/fuzzers/007-timing/timfuz.py +++ b/fuzzers/007-timing/timfuz.py @@ -107,6 +107,7 @@ corner_s2i = OrderedDict( def allow_zero_eqns(): + '''If true, allow a system of equations with no equations''' return os.getenv('ALLOW_ZERO_EQN', 'N') == 'Y' @@ -732,7 +733,7 @@ def row_sub_vars(row, sub_json, strict=False, verbose=False): print(row.items()) delvars = 0 - for k in sub_json['drop_names']: + for k in sub_json['zero_names']: try: del row[k] delvars += 1 @@ -908,3 +909,7 @@ def tilej_stats(tilej): print( ' %s: %u / %u solved, %u / %u covered' % (etype, solved, net, covered, net)) + +def load_bounds(bounds_csv, corner, ico=True): + Ads, b = loadc_Ads_b([bounds_csv], corner, ico=ico) + return Ads2bounds(Ads, b) diff --git a/fuzzers/007-timing/timfuz_solve.py b/fuzzers/007-timing/timfuz_solve.py index de1c5812..b3c43cfb 100644 --- a/fuzzers/007-timing/timfuz_solve.py +++ b/fuzzers/007-timing/timfuz_solve.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 -from timfuz import simplify_rows, loadc_Ads_b, index_names, A_ds2np, run_sub_json, print_eqns, Ads2bounds, instances, SimplifiedToZero, allow_zero_eqns +from timfuz import simplify_rows, loadc_Ads_b, index_names, A_ds2np, run_sub_json, print_eqns, Ads2bounds, instances, SimplifiedToZero, allow_zero_eqns, corner_s2i, acorner2csv from timfuz_massage import massage_equations import numpy as np import sys +import math def check_feasible(A_ub, b_ub): @@ -93,6 +94,44 @@ def filter_bounds(Ads, b, bounds, corner): return ret_Ads, ret_b +def solve_save(outfn, xvals, names, corner, save_zero=True, verbose=False): + # ballpark minimum actual observed delay is around 7 (carry chain) + # anything less than one is probably a solver artifact + delta = 0.5 + corneri = corner_s2i[corner] + + roundf = { + 'fast_max': math.ceil, + 'fast_min': math.floor, + 'slow_max': math.ceil, + 'slow_min': math.floor, + }[corner] + + print('Writing results') + zeros = 0 + with open(outfn, 'w') as fout: + # write as one variable per line + # this natively forms a bound if fed into linprog solver + fout.write('ico,fast_max fast_min slow_max slow_min,rows...\n') + for xval, name in zip(xvals, names): + row_ico = 1 + + if xval < delta: + if verbose: + print('WARNING: near 0 delay on %s: %0.6f' % (name, xval)) + zeros += 1 + if not save_zero: + continue + items = [str(row_ico), acorner2csv(roundf(xval), corneri)] + items.append('%u %s' % (1, name)) + fout.write(','.join(items) + '\n') + nonzeros = len(names) - zeros + print( + 'Wrote: %u / %u constrained delays, %u zeros' % + (nonzeros, len(names), zeros)) + assert nonzeros, 'Failed to estimate delay' + + def run( fns_in, corner,