mirror of https://github.com/openXC7/prjxray.git
timfuz: least square tests
Signed-off-by: John McMaster <johndmcmaster@gmail.com>
This commit is contained in:
parent
d4d0af9d9f
commit
41cf8469b5
|
|
@ -1,10 +0,0 @@
|
|||
.NOTPARALLEL:
|
||||
|
||||
SUBDIRS := $(patsubst %/,%, $(wildcard */))
|
||||
|
||||
.PHONY: $(SUBDIRS)
|
||||
|
||||
$(MAKECMDGOALS): $(SUBDIRS)
|
||||
|
||||
$(SUBDIRS):
|
||||
$(MAKE) -C $@ -f ../Makefile.database $(MAKECMDGOALS)
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
.PHONY: all clean
|
||||
|
||||
all: tilegrid.json
|
||||
|
||||
# Small dance to say that there is a single recipe that is run once to generate
|
||||
# multiple files.
|
||||
tileconn.json tilegrid.json: fuzzers
|
||||
.INTERMEDIATE: fuzzers
|
||||
fuzzers: SHELL:=/bin/bash
|
||||
fuzzers: settings.sh
|
||||
source settings.sh && $(MAKE) -C ../../fuzzers all
|
||||
|
||||
clean:
|
||||
rm -f *.db tileconn.json tilegrid.json *.yaml
|
||||
rm -rf html
|
||||
|
|
@ -0,0 +1,272 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# https://docs.scipy.org/doc/scipy-0.18.1/reference/generated/scipy.optimize.linprog.html
|
||||
from scipy.optimize import linprog
|
||||
from timfuz import Benchmark, Ar_di2np, Ar_ds2t, A_di2ds, A_ds2di, simplify_rows, loadc_Ads_b, index_names, A_ds2np, load_sub, run_sub_json, A_ub_np2d, print_eqns, print_eqns_np
|
||||
from timfuz_massage import massage_equations
|
||||
import numpy as np
|
||||
import glob
|
||||
import json
|
||||
import math
|
||||
from collections import OrderedDict
|
||||
from fractions import Fraction
|
||||
import sys
|
||||
import datetime
|
||||
import os
|
||||
import time
|
||||
import timfuz_solve
|
||||
import numpy
|
||||
import scipy.optimize as optimize
|
||||
from scipy.optimize import least_squares
|
||||
|
||||
def run_corner(Anp, b, names, verbose=False, opts={}, meta={}, outfn=None):
|
||||
# Given timing scores for above delays (-ps)
|
||||
assert type(Anp[0]) is np.ndarray, type(Anp[0])
|
||||
assert type(b) is np.ndarray, type(b)
|
||||
|
||||
#check_feasible(Anp, b)
|
||||
|
||||
'''
|
||||
Be mindful of signs
|
||||
Have something like
|
||||
timing1/timing 2 are constants
|
||||
delay1 + delay2 + delay4 >= timing1
|
||||
delay2 + delay3 >= timing2
|
||||
|
||||
But need it in compliant form:
|
||||
-delay1 + -delay2 + -delay4 <= -timing1
|
||||
-delay2 + -delay3 <= -timing2
|
||||
'''
|
||||
rows = len(Anp)
|
||||
cols = len(Anp[0])
|
||||
|
||||
print('Unique delay elements: %d' % len(names))
|
||||
print('Input paths')
|
||||
print(' # timing scores: %d' % len(b))
|
||||
print(' Rows: %d' % rows)
|
||||
|
||||
'''
|
||||
You must have at least as many things to optimize as variables
|
||||
That is, the system must be plausibly constrained for it to attempt a solve
|
||||
If not, you'll get a message like
|
||||
TypeError: Improper input: N=3 must not exceed M=2
|
||||
'''
|
||||
if rows < cols:
|
||||
raise Exception("rows must be >= cols")
|
||||
|
||||
def func(params, xdata, ydata):
|
||||
return (ydata - np.dot(xdata, params))
|
||||
|
||||
print('')
|
||||
# Now find smallest values for delay constants
|
||||
# Due to input bounds (ex: column limit), some delay elements may get eliminated entirely
|
||||
print('Running leastsq w/ %d r, %d c (%d name)' % (rows, cols, len(names)))
|
||||
x0 = np.array([0.0 for _x in range(cols)])
|
||||
x, cov_x, infodict, mesg, ier = optimize.leastsq(func, x0, args=(Anp, b), full_output=True)
|
||||
print('x', x)
|
||||
print('cov_x', cov_x)
|
||||
print('infodictx', infodict)
|
||||
print('mesg', mesg)
|
||||
print('ier', ier)
|
||||
print(' Solution found: %s' % (ier in (1, 2, 3, 4)))
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Solve timing solution'
|
||||
)
|
||||
|
||||
parser.add_argument('--verbose', action='store_true', help='')
|
||||
parser.add_argument('--massage', action='store_true', 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')
|
||||
parser.add_argument(
|
||||
'fns_in',
|
||||
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)
|
||||
finally:
|
||||
print('Exiting after %s' % bench)
|
||||
|
||||
# optimize.curve_fit wrapper
|
||||
def test1():
|
||||
# Generate artificial data = straight line with a=0 and b=1
|
||||
# plus some noise.
|
||||
xdata = np.array([0.0,1.0,2.0,3.0,4.0,5.0])
|
||||
ydata = np.array([0.1,0.9,2.2,2.8,3.9,5.1])
|
||||
# Initial guess.
|
||||
x0 = np.array([0.0, 0.0, 0.0])
|
||||
sigma = np.array([1.0,1.0,1.0,1.0,1.0,1.0])
|
||||
|
||||
def func(x, a, b, c):
|
||||
return a + b*x + c*x*x
|
||||
|
||||
print(optimize.curve_fit(func, xdata, ydata, x0, sigma))
|
||||
|
||||
# optimize.leastsq
|
||||
def test2():
|
||||
|
||||
# The function whose square is to be minimised.
|
||||
# params ... list of parameters tuned to minimise function.
|
||||
# Further arguments:
|
||||
# xdata ... design matrix for a linear model.
|
||||
# ydata ... observed data.
|
||||
def func(params, xdata, ydata):
|
||||
return (ydata - np.dot(xdata, params))
|
||||
|
||||
x0 = np.array([0.0, 0.0])
|
||||
|
||||
'''
|
||||
a = 10
|
||||
a + b = 100
|
||||
'''
|
||||
xdata = np.array([[1, 0],
|
||||
[1, 1]])
|
||||
ydata = np.array([10, 100])
|
||||
|
||||
'''
|
||||
x [ 10. 90.]
|
||||
cov_x [[ 1. -1.]
|
||||
[-1. 2.]]
|
||||
infodictx {'ipvt': array([1, 2], dtype=int32), 'qtf': array([ 1.69649118e-10, 1.38802454e-10]), 'nfev': 7, 'fjac': array([[ 1.41421356, 0.70710678],
|
||||
[ 0.70710678, 0.70710678]]), 'fvec': array([ 0., 0.])}
|
||||
mesg The relative error between two consecutive iterates is at most 0.000000
|
||||
ier 2
|
||||
Solution found: True
|
||||
'''
|
||||
x, cov_x, infodict, mesg, ier = optimize.leastsq(func, x0, args=(xdata, ydata), full_output=True)
|
||||
print('x', x)
|
||||
print('cov_x', cov_x)
|
||||
print('infodictx', infodict)
|
||||
print('mesg', mesg)
|
||||
print('ier', ier)
|
||||
print(' Solution found: %s' % (ier in (1, 2, 3, 4)))
|
||||
|
||||
# non-square
|
||||
def test3():
|
||||
def func(params, xdata, ydata):
|
||||
return (ydata - np.dot(xdata, params))
|
||||
|
||||
x0 = np.array([0.0, 0.0, 0.0])
|
||||
|
||||
'''
|
||||
a = 10
|
||||
a + b + c = 100
|
||||
'''
|
||||
xdata = np.array([[1, 0, 0],
|
||||
[1, 1, 1],
|
||||
[0, 0, 0]])
|
||||
ydata = np.array([10, 100, 0])
|
||||
|
||||
x, cov_x, infodict, mesg, ier = optimize.leastsq(func, x0, args=(xdata, ydata), full_output=True)
|
||||
print('x', x)
|
||||
print('cov_x', cov_x)
|
||||
print('infodictx', infodict)
|
||||
print('mesg', mesg)
|
||||
print('ier', ier)
|
||||
print(' Solution found: %s' % (ier in (1, 2, 3, 4)))
|
||||
|
||||
def test4():
|
||||
def func(params):
|
||||
print('')
|
||||
print('iter')
|
||||
print(params)
|
||||
print(xdata)
|
||||
print(ydata)
|
||||
return (ydata - np.dot(xdata, params))
|
||||
|
||||
x0 = np.array([0.0, 0.0, 0.0])
|
||||
|
||||
'''
|
||||
You must have at least as many things to optimize as variables
|
||||
That is, the system must be plausibly constrained for it to attempt a solve
|
||||
If not, you'll get a message like
|
||||
TypeError: Improper input: N=3 must not exceed M=2
|
||||
'''
|
||||
xdata = np.array([[1, 0, 0],
|
||||
[1, 1, 1],
|
||||
[1, 0, 1],
|
||||
[0, 1, 1],
|
||||
])
|
||||
ydata = np.array([10, 100, 120, 140])
|
||||
|
||||
x, cov_x, infodict, mesg, ier = optimize.leastsq(func, x0, full_output=True)
|
||||
print('x', x)
|
||||
print('cov_x', cov_x)
|
||||
print('infodictx', infodict)
|
||||
print('mesg', mesg)
|
||||
print('ier', ier)
|
||||
print(' Solution found: %s' % (ier in (1, 2, 3, 4)))
|
||||
|
||||
def test5():
|
||||
from scipy.optimize import least_squares
|
||||
|
||||
def fun_rosenbrock(x):
|
||||
return np.array([10 * (x[1] - x[0]**2), (1 - x[0])])
|
||||
x0_rosenbrock = np.array([2, 2])
|
||||
res = least_squares(fun_rosenbrock, x0_rosenbrock)
|
||||
'''
|
||||
active_mask: array([ 0., 0.])
|
||||
cost: 9.8669242910846867e-30
|
||||
fun: array([ 4.44089210e-15, 1.11022302e-16])
|
||||
grad: array([ -8.89288649e-14, 4.44089210e-14])
|
||||
jac: array([[-20.00000015, 10. ],
|
||||
[ -1. , 0. ]])
|
||||
message: '`gtol` termination condition is satisfied.'
|
||||
nfev: 3
|
||||
njev: 3
|
||||
optimality: 8.8928864934219529e-14
|
||||
status: 1
|
||||
success: True
|
||||
x: array([ 1., 1.])
|
||||
'''
|
||||
print(res)
|
||||
|
||||
def test6():
|
||||
from scipy.optimize import least_squares
|
||||
|
||||
def func(params):
|
||||
return (ydata - np.dot(xdata, params))
|
||||
|
||||
x0 = np.array([0.0, 0.0, 0.0])
|
||||
|
||||
'''
|
||||
a = 10
|
||||
a + b + c = 100
|
||||
'''
|
||||
xdata = np.array([[1, 0, 0],
|
||||
[1, 1, 1],
|
||||
[0, 0, 0]])
|
||||
ydata = np.array([10, 100, 0])
|
||||
|
||||
res = least_squares(func, x0)
|
||||
'''
|
||||
'''
|
||||
print(res)
|
||||
|
||||
if __name__ == '__main__':
|
||||
#main()
|
||||
#test1()
|
||||
#test2()
|
||||
#test3()
|
||||
#test4()
|
||||
#test5()
|
||||
test6()
|
||||
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# https://docs.scipy.org/doc/scipy-0.18.1/reference/generated/scipy.optimize.linprog.html
|
||||
from scipy.optimize import linprog
|
||||
from timfuz import Benchmark, Ar_di2np, Ar_ds2t, A_di2ds, A_ds2di, simplify_rows, loadc_Ads_b, index_names, A_ds2np, load_sub, run_sub_json, A_ub_np2d, print_eqns, print_eqns_np
|
||||
from timfuz_massage import massage_equations
|
||||
import numpy as np
|
||||
import glob
|
||||
import json
|
||||
import math
|
||||
from collections import OrderedDict
|
||||
from fractions import Fraction
|
||||
import sys
|
||||
import datetime
|
||||
import os
|
||||
import time
|
||||
import timfuz_solve
|
||||
|
||||
def run_corner(Anp, b, names, verbose=False, opts={}, meta={}, outfn=None):
|
||||
# Given timing scores for above delays (-ps)
|
||||
assert type(Anp[0]) is np.ndarray, type(Anp[0])
|
||||
assert type(b) is np.ndarray, type(b)
|
||||
|
||||
#check_feasible(Anp, b)
|
||||
|
||||
'''
|
||||
Be mindful of signs
|
||||
Have something like
|
||||
timing1/timing 2 are constants
|
||||
delay1 + delay2 + delay4 >= timing1
|
||||
delay2 + delay3 >= timing2
|
||||
|
||||
But need it in compliant form:
|
||||
-delay1 + -delay2 + -delay4 <= -timing1
|
||||
-delay2 + -delay3 <= -timing2
|
||||
'''
|
||||
rows = len(Anp)
|
||||
cols = len(Anp[0])
|
||||
print('Scaling to solution form...')
|
||||
b_ub = -1.0 * b
|
||||
#A_ub = -1.0 * Anp
|
||||
A_ub = [-1.0 * x for x in Anp]
|
||||
|
||||
if verbose:
|
||||
print('')
|
||||
print('A_ub b_ub')
|
||||
print_eqns_np(A_ub, b_ub, verbose=verbose)
|
||||
print('')
|
||||
|
||||
print('Creating misc constants...')
|
||||
# Minimization function scalars
|
||||
# Treat all logic elements as equally important
|
||||
c = [1 for _i in range(len(names))]
|
||||
# Delays cannot be negative
|
||||
# (this is also the default constraint)
|
||||
#bounds = [(0, None) for _i in range(len(names))]
|
||||
# Also you can provide one to apply to all
|
||||
bounds = (0, None)
|
||||
|
||||
# Seems to take about rows + 3 iterations
|
||||
# Give some margin
|
||||
#maxiter = int(1.1 * rows + 100)
|
||||
#maxiter = max(1000, int(1000 * rows + 1000))
|
||||
# Most of the time I want it to just keep going unless I ^C it
|
||||
maxiter = 1000000
|
||||
|
||||
if verbose >= 2:
|
||||
print('b_ub', b)
|
||||
print('Unique delay elements: %d' % len(names))
|
||||
print(' # delay minimization weights: %d' % len(c))
|
||||
print(' # delay constraints: %d' % len(bounds))
|
||||
print('Input paths')
|
||||
print(' # timing scores: %d' % len(b))
|
||||
print(' Rows: %d' % rows)
|
||||
|
||||
tlast = [time.time()]
|
||||
iters = [0]
|
||||
printn = [0]
|
||||
def callback(xk, **kwargs):
|
||||
iters[0] = kwargs['nit']
|
||||
if time.time() - tlast[0] > 1.0:
|
||||
sys.stdout.write('I:%d ' % kwargs['nit'])
|
||||
tlast[0] = time.time()
|
||||
printn[0] += 1
|
||||
if printn[0] % 10 == 0:
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
print('')
|
||||
# Now find smallest values for delay constants
|
||||
# Due to input bounds (ex: column limit), some delay elements may get eliminated entirely
|
||||
print('Running linprog w/ %d r, %d c (%d name)' % (rows, cols, len(names)))
|
||||
res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, callback=callback,
|
||||
options={"disp": True, 'maxiter': maxiter, 'bland': True, 'tol': 1e-6,})
|
||||
nonzeros = 0
|
||||
print('Ran %d iters' % iters[0])
|
||||
if res.success:
|
||||
print('Result sample (%d elements)' % (len(res.x)))
|
||||
plim = 3
|
||||
for xi, (name, x) in enumerate(zip(names, res.x)):
|
||||
nonzero = x >= 0.001
|
||||
if nonzero:
|
||||
nonzeros += 1
|
||||
#if nonzero and (verbose >= 1 or xi > 30):
|
||||
if nonzero and (verbose or ((nonzeros < 100 or nonzeros % 20 == 0) and nonzeros <= plim)):
|
||||
print(' % 4u % -80s % 10.1f' % (xi, name, x))
|
||||
print('Delay on %d / %d' % (nonzeros, len(res.x)))
|
||||
if not os.path.exists('res'):
|
||||
os.mkdir('res')
|
||||
fn_out = 'res/%s' % datetime.datetime.utcnow().isoformat().split('.')[0]
|
||||
print('Writing %s' % fn_out)
|
||||
np.save(fn_out, (3, c, A_ub, b_ub, bounds, names, res, meta))
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Solve timing solution'
|
||||
)
|
||||
|
||||
parser.add_argument('--verbose', action='store_true', help='')
|
||||
parser.add_argument('--massage', action='store_true', 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')
|
||||
parser.add_argument(
|
||||
'fns_in',
|
||||
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)
|
||||
finally:
|
||||
print('Exiting after %s' % bench)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -718,6 +718,9 @@ def load_sub(fn):
|
|||
return j
|
||||
|
||||
def row_sub_syms(row, sub_json, verbose=False):
|
||||
zero = Fraction(0)
|
||||
zero = 0
|
||||
|
||||
if 0 and verbose:
|
||||
print("")
|
||||
print(row.items())
|
||||
|
|
@ -751,8 +754,9 @@ def row_sub_syms(row, sub_json, verbose=False):
|
|||
if verbose:
|
||||
print('pivot %i %s' % (n, pivot))
|
||||
for subk, subv in sorted(sub_json['subs'][group].items()):
|
||||
oldn = row.get(subk, Fraction(0))
|
||||
rown = oldn - n * subv
|
||||
oldn = row.get(subk, zero)
|
||||
rown = -n * subv
|
||||
rown += type(rown)(oldn)
|
||||
if verbose:
|
||||
print(" %s: %d => %d" % (subk, oldn, rown))
|
||||
if rown == 0:
|
||||
|
|
@ -768,8 +772,9 @@ def row_sub_syms(row, sub_json, verbose=False):
|
|||
# after all constants are applied, the row should end up positive?
|
||||
# numeric precision issues previously limited this
|
||||
# Ex: AssertionError: ('PIP_BSW_2ELSING0', -2.220446049250313e-16)
|
||||
for k, v in sorted(row.items()):
|
||||
assert v > 0, (k, v)
|
||||
if 0:
|
||||
for k, v in sorted(row.items()):
|
||||
assert v > 0, (k, v)
|
||||
|
||||
def run_sub_json(Ads, sub_json, verbose=False):
|
||||
nrows = 0
|
||||
|
|
@ -805,7 +810,7 @@ def run_sub_json(Ads, sub_json, verbose=False):
|
|||
print('')
|
||||
|
||||
print("Sub: %u / %u rows changed" % (nsubs, nrows))
|
||||
print("Sub: %u => %u cols" % (ncols_old, ncols_new))
|
||||
print("Sub: %u => %u non-zero row cols" % (ncols_old, ncols_new))
|
||||
|
||||
def print_eqns(Ads, b, verbose=0, lim=3, label=''):
|
||||
rows = len(b)
|
||||
|
|
|
|||
|
|
@ -322,6 +322,7 @@ def derive_eq_by_col(Ads, b_ub, verbose=0):
|
|||
print('Derive col: %d => %d rows' % (len(b_ub), len(b_ret)))
|
||||
return Ads_ret, b_ret
|
||||
|
||||
# keep derriving until solution is (probably) stable
|
||||
def massage_equations_old(Ads, b, verbose=False, derive_lim=3):
|
||||
'''
|
||||
Equation pipeline
|
||||
|
|
@ -392,7 +393,8 @@ def massage_equations_old(Ads, b, verbose=False, derive_lim=3):
|
|||
debug("final (sorted)")
|
||||
return Ads, b
|
||||
|
||||
def massage_equations_old2(Ads, b, verbose=False):
|
||||
# iteratively increasing column limit until all columns are added
|
||||
def massage_equations_inc_col_lim(Ads, b, verbose=False):
|
||||
'''
|
||||
Equation pipeline
|
||||
Some operations may generate new equations
|
||||
|
|
@ -463,7 +465,9 @@ def massage_equations_old2(Ads, b, verbose=False):
|
|||
print('Massage final: %d => %d rows' % (dstart, dend))
|
||||
return Ads, b
|
||||
|
||||
def massage_equations(Ads, b, verbose=False):
|
||||
# only derive based on nearby equations
|
||||
# theory is they will be the best to diff
|
||||
def massage_equations_near(Ads, b, verbose=False):
|
||||
'''
|
||||
Equation pipeline
|
||||
Some operations may generate new equations
|
||||
|
|
@ -525,3 +529,5 @@ def massage_equations(Ads, b, verbose=False):
|
|||
print('')
|
||||
print('Massage final: %d => %d rows' % (dstart, dend))
|
||||
return Ads, b
|
||||
|
||||
massage_equations = massage_equations_inc_col_lim
|
||||
|
|
|
|||
|
|
@ -32,9 +32,12 @@ def check_feasible(A_ub, b_ub):
|
|||
|
||||
progress = max(1, rows / 100)
|
||||
|
||||
# Chose a high arbitrary value for x
|
||||
# Delays should be in order of ns, so a 10 ns delay should be way above what anything should be
|
||||
xs = [10e3 for _i in range(cols)]
|
||||
'''
|
||||
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
|
||||
# Verify bounds
|
||||
|
|
@ -65,102 +68,14 @@ def check_feasible(A_ub, b_ub):
|
|||
raise Exception("Bad ")
|
||||
print(' done')
|
||||
|
||||
def run_corner(Anp, b, names, verbose=False, opts={}, meta={}):
|
||||
# Given timing scores for above delays (-ps)
|
||||
assert type(Anp[0]) is np.ndarray, type(Anp[0])
|
||||
assert type(b) is np.ndarray, type(b)
|
||||
def instances(Ads):
|
||||
ret = 0
|
||||
for row_ds in Ads:
|
||||
ret += sum(row_ds.values())
|
||||
return ret
|
||||
|
||||
#check_feasible(Anp, b)
|
||||
|
||||
'''
|
||||
Be mindful of signs
|
||||
Have something like
|
||||
timing1/timing 2 are constants
|
||||
delay1 + delay2 + delay4 >= timing1
|
||||
delay2 + delay3 >= timing2
|
||||
|
||||
But need it in compliant form:
|
||||
-delay1 + -delay2 + -delay4 <= -timing1
|
||||
-delay2 + -delay3 <= -timing2
|
||||
'''
|
||||
rows = len(Anp)
|
||||
cols = len(Anp[0])
|
||||
print('Scaling to solution form...')
|
||||
b_ub = -1.0 * b
|
||||
#A_ub = -1.0 * Anp
|
||||
A_ub = [-1.0 * x for x in Anp]
|
||||
|
||||
if verbose:
|
||||
print('')
|
||||
print('A_ub b_ub')
|
||||
print_eqns_np(A_ub, b_ub, verbose=verbose)
|
||||
print('')
|
||||
|
||||
print('Creating misc constants...')
|
||||
# Minimization function scalars
|
||||
# Treat all logic elements as equally important
|
||||
c = [1 for _i in range(len(names))]
|
||||
# Delays cannot be negative
|
||||
# (this is also the default constraint)
|
||||
#bounds = [(0, None) for _i in range(len(names))]
|
||||
# Also you can provide one to apply to all
|
||||
bounds = (0, None)
|
||||
|
||||
# Seems to take about rows + 3 iterations
|
||||
# Give some margin
|
||||
#maxiter = int(1.1 * rows + 100)
|
||||
#maxiter = max(1000, int(1000 * rows + 1000))
|
||||
# Most of the time I want it to just keep going unless I ^C it
|
||||
maxiter = 1000000
|
||||
|
||||
if verbose >= 2:
|
||||
print('b_ub', b)
|
||||
print('Unique delay elements: %d' % len(names))
|
||||
print(' # delay minimization weights: %d' % len(c))
|
||||
print(' # delay constraints: %d' % len(bounds))
|
||||
print('Input paths')
|
||||
print(' # timing scores: %d' % len(b))
|
||||
print(' Rows: %d' % rows)
|
||||
|
||||
tlast = [time.time()]
|
||||
iters = [0]
|
||||
printn = [0]
|
||||
def callback(xk, **kwargs):
|
||||
iters[0] = kwargs['nit']
|
||||
if time.time() - tlast[0] > 1.0:
|
||||
sys.stdout.write('I:%d ' % kwargs['nit'])
|
||||
tlast[0] = time.time()
|
||||
printn[0] += 1
|
||||
if printn[0] % 10 == 0:
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
print('')
|
||||
# Now find smallest values for delay constants
|
||||
# Due to input bounds (ex: column limit), some delay elements may get eliminated entirely
|
||||
print('Running linprog w/ %d r, %d c (%d name)' % (rows, cols, len(names)))
|
||||
res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, callback=callback,
|
||||
options={"disp": True, 'maxiter': maxiter, 'bland': True, 'tol': 1e-6,})
|
||||
nonzeros = 0
|
||||
print('Ran %d iters' % iters[0])
|
||||
if res.success:
|
||||
print('Result sample (%d elements)' % (len(res.x)))
|
||||
plim = 3
|
||||
for xi, (name, x) in enumerate(zip(names, res.x)):
|
||||
nonzero = x >= 0.001
|
||||
if nonzero:
|
||||
nonzeros += 1
|
||||
#if nonzero and (verbose >= 1 or xi > 30):
|
||||
if nonzero and (verbose or ((nonzeros < 100 or nonzeros % 20 == 0) and nonzeros <= plim)):
|
||||
print(' % 4u % -80s % 10.1f' % (xi, name, x))
|
||||
print('Delay on %d / %d' % (nonzeros, len(res.x)))
|
||||
if not os.path.exists('res'):
|
||||
os.mkdir('res')
|
||||
fn_out = 'res/%s' % datetime.datetime.utcnow().isoformat().split('.')[0]
|
||||
print('Writing %s' % fn_out)
|
||||
np.save(fn_out, (3, c, A_ub, b_ub, bounds, names, res, meta))
|
||||
|
||||
def run(fns_in, corner, sub_json=None, dedup=True, massage=False, verbose=False):
|
||||
def run(fns_in, corner, run_corner, sub_json=None, dedup=True, massage=False, outfn=None, verbose=False, **kwargs):
|
||||
print('Loading data')
|
||||
Ads, b = loadc_Ads_b(fns_in, corner, ico=True)
|
||||
|
||||
# Remove duplicate rows
|
||||
|
|
@ -168,15 +83,19 @@ def run(fns_in, corner, sub_json=None, dedup=True, massage=False, verbose=False)
|
|||
# maybe better to just add them into the matrix directly
|
||||
if dedup:
|
||||
oldn = len(Ads)
|
||||
iold = instances(Ads)
|
||||
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)
|
||||
names_old = index_names(Ads)
|
||||
run_sub_json(Ads, sub_json, verbose=verbose)
|
||||
names = index_names(Ads)
|
||||
print("Sub: %u => %u names" % (len(names_old), len(names)))
|
||||
print('Sub: %u => %u instances' % (iold, instances(Ads)))
|
||||
else:
|
||||
names = index_names(Ads)
|
||||
|
||||
|
|
@ -187,46 +106,20 @@ def run(fns_in, corner, sub_json=None, dedup=True, massage=False, verbose=False)
|
|||
#print
|
||||
#col_dist(A_ubd, 'final', names)
|
||||
|
||||
'''
|
||||
Given:
|
||||
a >= 10
|
||||
a + b >= 100
|
||||
A valid solution is:
|
||||
a = 100
|
||||
However, a better solution is something like
|
||||
a = 10
|
||||
b = 90
|
||||
This creates derived constraints to provide more realistic results
|
||||
'''
|
||||
if massage:
|
||||
Ads, b = massage_equations(Ads, b)
|
||||
|
||||
print('Converting to numpy...')
|
||||
names, Anp = A_ds2np(Ads)
|
||||
run_corner(Anp, np.asarray(b), names, verbose=verbose)
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Solve timing solution'
|
||||
)
|
||||
|
||||
parser.add_argument('--verbose', action='store_true', help='')
|
||||
parser.add_argument('--massage', action='store_true', help='')
|
||||
parser.add_argument('--sub-json', help='Group substitutions to make fully ranked')
|
||||
parser.add_argument('--corner', default="slow_max", help='')
|
||||
parser.add_argument(
|
||||
'fns_in',
|
||||
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:
|
||||
run(sub_json=sub_json,
|
||||
fns_in=fns_in, verbose=args.verbose, corner=args.corner, massage=args.massage)
|
||||
finally:
|
||||
print('Exiting after %s' % bench)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
run_corner(Anp, np.asarray(b), names, outfn=outfn, verbose=verbose, **kwargs)
|
||||
|
|
|
|||
Loading…
Reference in New Issue