From e83866af720375d2f5ae5aca19b6bb6fe955c575 Mon Sep 17 00:00:00 2001 From: John McMaster Date: Wed, 29 Aug 2018 14:27:57 -0700 Subject: [PATCH] timfuz: linprog write output csv Signed-off-by: John McMaster --- timfuz/solve_linprog.py | 33 ++++++++++++++++++++++++++++- timfuz/timfuz_solve.py | 47 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/timfuz/solve_linprog.py b/timfuz/solve_linprog.py index 2eef5e75..2389de6c 100644 --- a/timfuz/solve_linprog.py +++ b/timfuz/solve_linprog.py @@ -111,6 +111,36 @@ def run_corner(Anp, b, names, verbose=False, opts={}, meta={}, outfn=None): print('Writing %s' % fn_out) np.save(fn_out, (3, c, A_ub, b_ub, bounds, names, res, meta)) + if outfn: + # ballpark minimum actual observed delay is around 7 (carry chain) + # anything less than one is probably a solver artifact + delta = 0.5 + + print('Writing resutls') + skips = 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): + 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 + #xvali = round(xval) + xvali = math.ceil(xval) + corners = [xvali for _ in range(4)] + items = [str(row_ico), ' '.join([str(x) for x in corners])] + 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))) + + def main(): import argparse @@ -121,6 +151,7 @@ def main(): parser.add_argument('--verbose', action='store_true', help='') parser.add_argument('--massage', action='store_true', help='') + parser.add_argument('--sub-csv', help='') parser.add_argument('--sub-json', help='Group substitutions to make fully ranked') parser.add_argument('--corner', default="slow_max", help='') parser.add_argument('--out', default=None, help='output timing delay .json') @@ -141,7 +172,7 @@ def main(): sub_json = load_sub(args.sub_json) try: - timfuz_solve.run(run_corner=run_corner, sub_json=sub_json, + timfuz_solve.run(run_corner=run_corner, sub_json=sub_json, sub_csv=args.sub_csv, fns_in=fns_in, corner=args.corner, massage=args.massage, outfn=args.out, verbose=args.verbose) finally: print('Exiting after %s' % bench) diff --git a/timfuz/timfuz_solve.py b/timfuz/timfuz_solve.py index a2e6526a..a7f33e77 100644 --- a/timfuz/timfuz_solve.py +++ b/timfuz/timfuz_solve.py @@ -36,7 +36,7 @@ def check_feasible(A_ub, b_ub): Delays should be in order of ns, so a 10 ns delay should be way above what anything should be Series can have several hundred delay elements Max delay in ballpark - ''' + ''' xs = [1e9 for _i in range(cols)] # FIXME: use the correct np function to do this for me @@ -74,7 +74,29 @@ def instances(Ads): ret += sum(row_ds.values()) return ret -def run(fns_in, corner, run_corner, sub_json=None, dedup=True, massage=False, outfn=None, verbose=False, **kwargs): +def Ads2bounds(Ads, b): + ret = {} + for row_ds, row_b in zip(Ads, b): + assert len(row_ds) == 1 + k, v = list(row_ds.items())[0] + assert v == 1 + ret[k] = row_b + return ret + +def filter_bounds(Ads, b, bounds): + '''Given min variable delays, remove rows that won't constrain solution''' + 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()]) + # will this row potentially constrain us more? + if row_b > est: + ret_Ads.append(row_ds) + ret_b.append(row_b) + return ret_Ads, ret_b + +def run(fns_in, corner, run_corner, sub_json=None, sub_csv=None, dedup=True, massage=False, outfn=None, verbose=False, **kwargs): print('Loading data') Ads, b = loadc_Ads_b(fns_in, corner, ico=True) @@ -87,7 +109,7 @@ def run(fns_in, corner, run_corner, sub_json=None, dedup=True, massage=False, ou Ads, b = simplify_rows(Ads, b) print('Simplify %u => %u rows' % (oldn, len(Ads))) print('Simplify %u => %u instances' % (iold, instances(Ads))) - + if sub_json: print('Sub: %u rows' % len(Ads)) iold = instances(Ads) @@ -99,6 +121,23 @@ def run(fns_in, corner, run_corner, sub_json=None, dedup=True, massage=False, ou else: names = index_names(Ads) + # post substitution csv + # actually this might not be strictly necessary since subs would just not apply + # however, I wanted to do some filtering based on expected form + if sub_csv: + Ads2, b2 = loadc_Ads_b([sub_csv], corner, ico=True) + if 0: + Ads = Ads + Ads2 + b = b + b2 + else: + bounds = Ads2bounds(Ads2, b2) + rows_old = len(Ads) + Ads, b = filter_bounds(Ads, b, bounds) + print('Filter bounds: %s => %s rows' % (rows_old, len(Ads))) + if 1: + Ads = Ads + Ads2 + b = b + b2 + if verbose: print print_eqns(Ads, b, verbose=verbose) @@ -115,7 +154,7 @@ def run(fns_in, corner, run_corner, sub_json=None, dedup=True, massage=False, ou However, a better solution is something like a = 10 b = 90 - This creates derived constraints to provide more realistic results + This creates derived constraints to provide more realistic results ''' if massage: Ads, b = massage_equations(Ads, b)