diff --git a/fuzzers/007-timing/solve_leastsq.py b/fuzzers/007-timing/solve_leastsq.py index 10cacbe3..5bdd4fad 100644 --- a/fuzzers/007-timing/solve_leastsq.py +++ b/fuzzers/007-timing/solve_leastsq.py @@ -33,34 +33,44 @@ def mkestimate(Anp, b): x0[coli] = min(x0[coli], ub) return x0 -def save(outfn, res, names, corner): +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] - print('Writing resutls') + # 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(res.x, names): + 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('Skipping %s: %0.6f' % (name, xval)) skips += 1 continue + keeps += 1 #xvali = round(xval) - items = [str(row_ico), acorner2csv(math.ceil(xval), corneri)] + 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, len(names) - skips, len(names))) + 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) @@ -131,7 +141,7 @@ def run_corner(Anp, b, names, corner, verbose=False, opts={}, meta={}, outfn=Non print('Done') if outfn: - save(outfn, res, names, corner) + save(outfn, res.x, names, corner) def main(): import argparse @@ -148,23 +158,19 @@ def main(): parser.add_argument('--out', default=None, help='output timing delay .json') parser.add_argument( 'fns_in', - nargs='*', + nargs='+', help='timing3.csv input files') args = parser.parse_args() # Store options in dict to ease passing through functions bench = Benchmark() - fns_in = args.fns_in - if not fns_in: - fns_in = glob.glob('specimen_*/timing3.csv') - sub_json = None if args.sub_json: sub_json = load_sub(args.sub_json) try: timfuz_solve.run(run_corner=run_corner, sub_json=sub_json, - fns_in=fns_in, corner=args.corner, massage=args.massage, outfn=args.out, verbose=args.verbose) + fns_in=args.fns_in, corner=args.corner, massage=args.massage, outfn=args.out, verbose=args.verbose) finally: print('Exiting after %s' % bench) diff --git a/fuzzers/007-timing/solve_linprog.py b/fuzzers/007-timing/solve_linprog.py index 9c943ecb..e10ce7fb 100644 --- a/fuzzers/007-timing/solve_linprog.py +++ b/fuzzers/007-timing/solve_linprog.py @@ -18,7 +18,7 @@ def save(outfn, xvals, names, corner): delta = 0.5 corneri = corner_s2i[corner] - print('Writing resutls') + print('Writing results') zeros = 0 with open(outfn, 'w') as fout: # write as one variable per line diff --git a/fuzzers/007-timing/timfuz.py b/fuzzers/007-timing/timfuz.py index c0d0aad2..c1ed1bb7 100644 --- a/fuzzers/007-timing/timfuz.py +++ b/fuzzers/007-timing/timfuz.py @@ -41,6 +41,10 @@ corner_s2i = OrderedDict([ class SimplifiedToZero(Exception): pass + +def allow_zero_eqns(): + return os.getenv('ALLOW_ZERO_EQN', 'N') == 'Y' + def print_eqns(A_ubd, b_ub, verbose=0, lim=3, label=''): rows = len(b_ub) @@ -147,6 +151,13 @@ def simplify_rows(Ads, b_ub, remove_zd=False, corner=None): 'slow_max': max, 'slow_min': min, }[corner] + # An outlier to make unknown values be ignored + T_UNK = { + 'fast_max': 0, + 'fast_min': 10e9, + 'slow_max': 0, + 'slow_min': 10e9, + }[corner] sys.stdout.write('SimpR ') sys.stdout.flush() @@ -173,7 +184,7 @@ def simplify_rows(Ads, b_ub, remove_zd=False, corner=None): continue rowt = Ar_ds2t(rowd) - eqns[rowt] = minmax(eqns.get(rowt, 0), b) + eqns[rowt] = minmax(eqns.get(rowt, T_UNK), b) print(' done') if len(eqns) == 0: diff --git a/fuzzers/007-timing/timfuz_massage.py b/fuzzers/007-timing/timfuz_massage.py index e35f4e28..1f52af5b 100644 --- a/fuzzers/007-timing/timfuz_massage.py +++ b/fuzzers/007-timing/timfuz_massage.py @@ -80,8 +80,8 @@ def derive_eq_by_row(Ads, b, verbose=0, col_lim=0, tweak=False): How to do this in a reasonable time span? Also equations are sparse, which makes this harder to compute ''' + assert len(Ads) == len(b), 'Ads, b length mismatch' rows = len(Ads) - assert rows == len(b) # Index equations into hash maps so can lookup sparse elements quicker assert len(Ads) == len(b) @@ -403,6 +403,8 @@ def massage_equations_inc_col_lim(Ads, b, verbose=False, corner=None): Remove these columns as necessary to speed up solving ''' + assert len(Ads) == len(b), 'Ads, b length mismatch' + def debug(what): if verbose: print('') diff --git a/fuzzers/007-timing/timfuz_solve.py b/fuzzers/007-timing/timfuz_solve.py index ccd2b738..52a4c43e 100644 --- a/fuzzers/007-timing/timfuz_solve.py +++ b/fuzzers/007-timing/timfuz_solve.py @@ -2,7 +2,7 @@ # https://docs.scipy.org/doc/scipy-0.18.1/reference/generated/scipy.optimize.linprog.html from scipy.optimize import linprog -from timfuz import Benchmark, simplify_rows, loadc_Ads_b, index_names, A_ds2np, run_sub_json, print_eqns, Ads2bounds, instances, SimplifiedToZero +from timfuz import Benchmark, simplify_rows, loadc_Ads_b, index_names, A_ds2np, run_sub_json, print_eqns, Ads2bounds, instances, SimplifiedToZero, allow_zero_eqns from timfuz_massage import massage_equations import numpy as np import glob @@ -68,15 +68,30 @@ def check_feasible(A_ub, b_ub): raise Exception("Bad ") print(' done') -def filter_bounds(Ads, b, bounds): +def filter_bounds(Ads, b, bounds, corner): '''Given min variable delays, remove rows that won't constrain solution''' + #assert len(bounds) > 0 + + if 'max' in corner: + # Keep delays possibly larger than current bound + def keep(row_b, est): + return row_b > est + T_UNK = 0 + elif 'min' in corner: + # Keep delays possibly smaller than current bound + def keep(row_b, est): + return row_b < est + T_UNK = 1e9 + else: + assert 0 + ret_Ads = [] ret_b = [] for row_ds, row_b in zip(Ads, b): # some variables get estimated at 0 - est = sum([bounds.get(k, 0) * v for k, v in row_ds.items()]) + est = sum([bounds.get(k, T_UNK) * v for k, v in row_ds.items()]) # will this row potentially constrain us more? - if row_b > est: + if keep(row_b, est): ret_Ads.append(row_ds) ret_b.append(row_b) return ret_Ads, ret_b @@ -114,11 +129,14 @@ def run(fns_in, corner, run_corner, sub_json=None, sub_csv=None, dedup=True, mas if sub_csv: Ads2, b2 = loadc_Ads_b([sub_csv], corner, ico=True) bounds = Ads2bounds(Ads2, b2) + assert len(bounds), 'Failed to load bounds' rows_old = len(Ads) - Ads, b = filter_bounds(Ads, b, bounds) - print('Filter bounds: %s => %s rows' % (rows_old, len(Ads))) + Ads, b = filter_bounds(Ads, b, bounds, corner) + print('Filter bounds: %s => %s + %s rows' % (rows_old, len(Ads), len(Ads2))) Ads = Ads + Ads2 b = b + b2 + assert len(Ads) or allow_zero_eqns() + assert len(Ads) == len(b), 'Ads, b length mismatch' if verbose: print @@ -126,6 +144,7 @@ def run(fns_in, corner, run_corner, sub_json=None, sub_csv=None, dedup=True, mas #print #col_dist(A_ubd, 'final', names) + print('b10', b[0:100]) ''' Given: @@ -142,7 +161,7 @@ def run(fns_in, corner, run_corner, sub_json=None, sub_csv=None, dedup=True, mas try: Ads, b = massage_equations(Ads, b, corner=corner) except SimplifiedToZero: - if os.getenv('ALLOW_ZERO_EQN', 'N') != 'Y': + if not allow_zero_eqns(): raise print('WARNING: simplified to zero equations') Ads = []