def test_and(m = 5): np.random.seed(0) lb = -np.ones(m) ub = np.ones(m) dom1 = psdr.BoxDomain(lb, ub) Ls = [np.eye(m),] ys = [np.ones(m),] rhos = [0.5,] dom2 = psdr.LinQuadDomain(Ls = Ls, ys = ys, rhos = rhos) # Combine the two domains dom3 = dom1 & dom2 # Check inclusion for it in range(10): p = np.random.randn(m) x = dom3.corner(p) assert dom1.isinside(x) assert dom2.isinside(x) # Now try with a tensor product domain lb = -np.ones(2) ub = np.ones(2) dom1a = psdr.BoxDomain(lb, ub) lb = -np.ones(m-2) ub = np.ones(m-2) dom1b = psdr.BoxDomain(lb, ub) dom1 = dom1a * dom1b # Combine the two domains dom3 = dom1 & dom2 # Check inclusion for it in range(10): p = np.random.randn(m) x = dom3.corner(p) assert dom1.isinside(x) assert dom2.isinside(x) # Combine the two domains dom3 = dom2 & dom1 # Check inclusion for it in range(10): p = np.random.randn(m) x = dom3.corner(p) assert dom1.isinside(x) assert dom2.isinside(x)
def generate_minimax_square(N, seed): np.random.seed(seed) domain = psdr.BoxDomain(0 * np.zeros(2), np.ones(2)) # Generate a design try: X = psdr.minimax_lloyd(domain, N, maxiter=500, xtol=1e-9, verbose=False) # Compute the disc diameter to cover the domain V = psdr.voronoi_vertex(domain, X) D = psdr.cdist(X, V) radius = np.max(np.min(D, axis=0)) # save the file design = { 'author': 'Jeffrey M. Hokanson', 'notes': f'psdr.minimax_lloyd seed={seed}, maxiter=500, xtol = 1e-9', 'objective': 'minimax', 'metric': 'l2', 'domain': 'square', 'radius': radius, 'X': X.tolist() } except: design = {'radius': np.inf} print(f"M: {N:4d} \t seed {seed:4d} finished") return design
def test_sweep(m=5): dom = psdr.BoxDomain(-np.ones(m), np.ones(m)) # Default arguments X, y = dom.sweep() assert np.all(dom.isinside(X)) # Specify sample x = dom.sample() X, y = dom.sweep(x=x) assert np.all(dom.isinside(X)) # Check x is on the line dom2 = psdr.ConvexHullDomain(X) assert dom2.isinside(x) # Specify direction p = np.random.randn(m) X, y = dom.sweep(p=p) assert np.all(dom.isinside(X)) d = (X[-1] - X[0]).reshape(-1, 1) assert np.isclose(subspace_angles(d, p.reshape(-1, 1)), 0) # Check corner X, y = dom.sweep(p=p, corner=True) c = dom.corner(p) assert np.any([np.isclose(x, c) for x in X])
def test_str(): m = 5 lb = -np.ones(m) ub = np.ones(m) dom = psdr.BoxDomain(lb = lb, ub = ub) assert 'BoxDomain' in dom.__str__() A = np.ones((2,m)) b = np.ones(2) dom = psdr.LinIneqDomain(A = A, b = b, lb = lb, ub = ub) assert 'LinIneqDomain' in dom.__str__() assert ' inequality ' in dom.__str__() A_eq = np.ones((1,m)) b_eq = np.ones(1) dom = psdr.LinIneqDomain(A_eq = A_eq, b_eq = b_eq, lb = lb, ub = ub) assert 'LinIneqDomain' in dom.__str__() assert ' equality ' in dom.__str__() Ls = [np.eye(m),] ys = [np.zeros(m),] rhos = [1.,] dom = psdr.LinQuadDomain(Ls = Ls, ys = ys, rhos = rhos) assert 'LinQuadDomain' in dom.__str__() assert ' quadratic ' in dom.__str__()
def generate_minimax_square(N = 10, seed = 0): domain = psdr.BoxDomain(0*np.zeros(2), np.ones(2)) # Generate a design X = psdr.minimax_lloyd(domain, N, maxiter = 500, xtol = 1e-7, verbose = True) #X = domain.sample(N) # Compute the disc diameter to cover the domain V = psdr.voronoi_vertex(domain, X) D = psdr.cdist(X, V) radius = np.max(np.min(D, axis= 0)) # save the file design = { 'author': 'Jeffrey M. Hokanson', 'objective': 'minimax', 'metric': 'l2', 'domain': 'square', 'radius': radius, 'X': X.tolist() } with open('square_%04d.dat' % N, 'w') as f: json.dump(design, f)
def test_multiobj_sample(): m = 2 L1 = np.ones((1,m)) L2 = np.ones((1,m)) L2[0,1] = 0 dom = psdr.BoxDomain(-np.ones(m), np.ones(m)) Ls = [L1, L2] Xhat = [] for i in range(10): x = psdr.seq_maximin_sample(dom, Xhat, Ls, Nsamp = 10) Xhat.append(x) Xhat = np.array(Xhat) # Now check fill distance for L in Ls: assert L.shape[0] == 1, "Only 1-d tests implemented" c1 = dom.corner(L.flatten()) c2 = dom.corner(-L.flatten()) lb, ub = sorted([L.dot(c1), L.dot(c2)]) vol = float(ub - lb) d = psdr.fill_distance_estimate(dom, Xhat, L = L) print("") print("ideal fill distance ", 0.5*vol/(len(Xhat) - 1) ) print("actual fill distance", d) # we add a fudge factor to ensure the suboptimal sampling passes assert 0.25*d < 0.5*vol/(len(Xhat)-1), "Sampling not efficient enough"
def test_sobol(m=3): dom = psdr.BoxDomain(-np.ones(m), np.ones(m)) # Triangular domain dom = dom.add_constraints(A=np.ones((1, m)), b=[0]) X = psdr.sobol_sequence(dom, 100) assert len(X) == 100 assert np.all(dom.isinside(X))
def test_poisson_disk_sample(m = 2, r = 0.3): dom = psdr.BoxDomain(-np.ones(m), np.ones(m)) X = psdr.poisson_disk_sample(dom, r) D = squareform(pdist(X)) D += np.diag(np.nan*np.ones(D.shape[0])) d = np.nanmin(D, axis = 1) print(d) assert np.all(d >= r) and np.all(d <= 2*r)
def test_initial(): np.random.seed(0) m = 5 L = np.random.randn(1, m) domain = psdr.BoxDomain(-np.ones(m), np.ones(m)) X0 = psdr.initial_sample(domain, L, 10) print(X0)
def test_projection_low(m=3, N=5): np.random.seed(0) dom = psdr.BoxDomain(-np.ones(m), np.ones(m)) Ls = [np.random.randn(1, m) for i in range(m - 1)] X = psdr.projection_sample(dom, N, Ls, maxiter=10) assert all(dom.isinside(X)), "Samples not in the domain"
def test_minimax_scale(): M = 15 m = 2 domain = psdr.BoxDomain(-np.ones(m), np.ones(m)) Xhat = psdr.maximin_block(domain, M) psdr.minimax_scale(domain, Xhat)
def test_minimax_1d(): # 1-d domain dom = psdr.BoxDomain(-10, 10) X = psdr.minimax_design_1d(dom, 10) assert np.all(dom.isinside(X)) # Fill distance dist = psdr.fill_distance_estimate(dom, X) print(dist) assert np.isclose(dist, 1.) # 2-d domain with 1-d Lipschitz dom = psdr.BoxDomain(-np.ones(4), np.ones(4)) L = np.ones((1, 4)) X = psdr.minimax_design_1d(dom, 4, L=L) assert np.all(dom.isinside(X)) print(X) dist = psdr.fill_distance_estimate(dom, X, L=L) print(dist) assert np.isclose(dist, 1.)
def test_tensor(): X = [[1,1], [1,-1], [-1,1], [-1,-1]] dom1 = psdr.ConvexHullDomain(X) dom2 = psdr.BoxDomain(-1,1) dom = dom1 * dom2 p = np.ones(3) x = dom.corner(p) print(x) assert np.all(np.isclose(x, [1,1,1]))
def test_pdf_truncate(m=3): cov = np.eye(m) mean = np.zeros(m) dom = psdr.NormalDomain(mean, cov, truncate=5e-1) print(dom.norm_lb) dom_box = psdr.BoxDomain(dom.norm_lb, dom.norm_ub) X, w = dom_box.quadrature_rule(1e6) p = np.sum(w * dom.pdf(X)) print(p) assert np.isclose(p, 1., rtol=1e-3)
def test_pdf_full(m=3): cov = np.eye(m) mean = np.zeros(m) dom = psdr.NormalDomain(mean, cov) dom_box = psdr.BoxDomain(-10 * np.ones(m), 10 * np.ones(m)) X, w = dom_box.quadrature_rule(1e6) p = np.sum(w * dom.pdf(X)) print(p) assert np.isclose(p, 1.)
def test_minimax_covering(m=2): np.random.seed(0) dom = psdr.BoxDomain(-np.ones(m), np.ones(m)) r = 0.5 X = psdr.minimax_covering(dom, r) X2 = dom.sample(1e5) D = cdist(X, X2) min_dist = np.max(np.min(cdist(X, X2), axis=0)) print("minimum distance %5.2e; target %5.2e" % (min_dist, r)) assert min_dist < r, "Sampling did not meet target separation"
def test_minimax_lloyd(m = 2, M = 7, plot = False): domain = psdr.BoxDomain(-np.ones(m), np.ones(m)) Xhat = psdr.minimax_lloyd(domain, M, maxiter = 100) if plot: import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.plot(Xhat[:,0], Xhat[:,1],'k.') ax.set_xlim(-1,1) ax.set_ylim(-1,1) ax.axis('equal') plt.show()
def test_stretch_sample(m, ns): np.random.seed(0) domain = psdr.BoxDomain(-np.ones(m), np.ones(m)) X = domain.sample(2) Ls = [np.random.randn(n, m) for n in ns] # Perform this process a few times for it in range(5): x = psdr.stretch_sample(domain, X, Ls) x_str = ''.join([f'{xi:8.4f}' for xi in x]) print(x_str) X = np.vstack([X, x.reshape(1, -1)]) assert np.all(domain.isinside(X)), "Not all points inside the domain"
def test_minimax(m=3, N=5): np.random.seed(1) dom = psdr.BoxDomain(-np.ones(m), np.ones(m)) Xhat = psdr.minimax_cluster(dom, N, N0=1e3) # Simply see if these points are better than random points X = dom.sample_grid(10) def minimax_score(Xhat): return np.max(np.min(cdist(Xhat, X), axis=0)) Xhat2 = dom.sample(N) print("score minimax", minimax_score(Xhat)) print("score random ", minimax_score(Xhat2)) assert minimax_score(Xhat) <= minimax_score(Xhat2)
def test_stretch_sample_uniform(m, n): r""" In the case of only one metric, this approach boils down to coffeehouse sampling and as such should be approximately uniform throughout the domain """ np.random.seed(0) domain = psdr.BoxDomain(-np.ones(m), np.ones(m)) X = domain.sample(2) Ls = [np.random.randn(n, m) for i in range(1)] L = Ls[0] for it in range(50): x = psdr.stretch_sample(domain, X, Ls) x_str = ''.join([f'{xi:8.4f}' for xi in x]) print(x_str) X = np.vstack([X, x.reshape(1, -1)]) if L.shape[0] == 1: y = (L @ X.T).flatten() # Find the extent in this direction x1 = domain.corner(L.T.flatten()) x2 = domain.corner(-L.T.flatten()) Lx1 = float(L @ x1) Lx2 = float(L @ x2) loc = min(Lx1, Lx2) scale = max(Lx1, Lx2) - loc stat, pvalue = kstest(y, 'uniform', args=(loc, scale)) print(f"probability {pvalue:5.1e}") assert pvalue > 1e-3 else: # as the Kolmogorov-Smirnov test in scipy only allows 1-d comparisions # we test uniformity by taking random directions along the linear combinations S = psdr.sample_sphere(n, 20) for s in S: sL = s @ L x1 = domain.corner(sL.T.flatten()) x2 = domain.corner(-sL.T.flatten()) Lx1 = float(sL @ x1) Lx2 = float(sL @ x2) loc = min(Lx1, Lx2) scale = max(Lx1, Lx2) - loc y = (sL @ X.T).flatten() stat, pvalue = kstest(y, 'uniform', args=(loc, scale)) print(f"probability {pvalue:5.1e}") assert pvalue > 1e-3
def test_projection_design(): m = 5 M = 10 dom = psdr.BoxDomain(-np.ones(m), np.ones(m)) L1 = np.ones((1, m)) if False: L2 = np.ones((2, m)) L2[0, 0] = -1. L2[1, 1] = -1. else: L2 = np.ones((1, m)) L2[0, 0] = -1. X = psdr.projection_design(dom, M, [L1, L2]) print(X)
def test_minimax_lloyd_global_min(plot = False): # Here check if the algorithm converges close to the global minimizer # for a known case. # See JMY90, M = 7 case np.random.seed(2) m = 2 M = 7 domain = psdr.BoxDomain(0*np.ones(m), np.ones(m)) Xhat_true = np.array([ [0.5, 0.5], [0.5, 0], [0.5,1], [1/3 - np.sqrt(7)/12, 3/4], [1/3 - np.sqrt(7)/12, 1/4], [2/3 + np.sqrt(7)/12, 1/4], [2/3 + np.sqrt(7)/12, 3/4], ]) Xhat = psdr.minimax_lloyd(domain, M, maxiter = 100, Xhat = None, xtol = 1e-9) Xhat_best = None best_score = np.inf for perm in permutations(range(len(Xhat))): perm = np.array(perm, dtype = np.int) R, scale = orthogonal_procrustes(Xhat, Xhat_true[perm]) err = np.linalg.norm(Xhat @ R - Xhat_true[perm], 'fro') if err < best_score: best_score = err Xhat_best = Xhat @ R # TODO: need to align points out of Xhat print("Error in fit", best_score) if plot: import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.plot(Xhat_best[:,0], Xhat_best[:,1],'rx') #XhatR = Xhat @ R #ax.plot(XhatR[:,0], XhatR[:,1],'ro') ax.plot(Xhat_true[:,0], Xhat_true[:,1],'k.') ax.set_xlim(-1,1) ax.set_ylim(-1,1) ax.axis('equal') plt.show()
def test_quad(): # test tensor-product quadrature rule func = lambda x: np.sin(5 * x[0] + 0.1) + (x[1] + 0.1)**2 dom = psdr.BoxDomain([-1, -1], [1, 1]) X, w = dom.quadrature_rule(100, method='gauss') int1 = np.sum([wi * func(xi) for xi, wi in zip(X, w)]) int2, err = scipy.integrate.nquad(lambda x1, x2: func([x1, x2]), [(lbi, ubi) for lbi, ubi in zip(dom.lb, dom.ub)]) print("gauss", int1) print("scipy", int2) assert np.isclose(int1, int2), "Quadrature rule failed"
def test_fit_function(m = 4): A = np.random.randn(m,2) A = A.dot(A.T) f = lambda x: 0.5*float(x.dot(A.dot(x))) grad = lambda x: A.dot(x) dom = psdr.BoxDomain(-np.ones(m), np.ones(m)) fun = psdr.Function(f, dom, grads = grad) X = fun.domain.sample(10) fX = fun(X) fun.grad(X) act = psdr.ActiveSubspace() act.fit_function(fun, 1e3) print(act.singvals) assert np.sum(~np.isclose(act.singvals,0)) == 2
def test_maximin_coffeehouse(m=2, N=20): np.random.seed(0) domain = psdr.BoxDomain(-np.ones(m), np.ones(m)) L = np.random.randn(m, m) Xhat = psdr.maximin_coffeehouse(domain, N, L=L, N0=1000) print(Xhat) assert np.all(domain.isinside(Xhat)), "points outside the domain" # We expect that points start getting closer dists = np.zeros(N) for k in range(1, N): dists[k] = np.min(psdr.cdist(Xhat[k], Xhat[:k], L=L)) print(dists[k]) assert np.all( dists[2:] - dists[1:-1] < 0), "distances should be monotonically decreasing"
def test_minimax_l2_design(fname): r""" This checks the design for consistency. It does not check if there is an improvment """ print(f"Loading design '{fname}'") design = get_new_design(fname) if design['domain'] == 'square': domain = psdr.BoxDomain(np.zeros(2), np.ones(2)) else: raise AssertionError('domain type "%s" not recognized' % design['domain']) assert design[ 'metric'] == 'l2', "Expected metric 'l2', got '%s'" % design['metric'] assert design[ 'objective'] == 'minimax', "Expected objective 'minimax', got '%s'" % design[ 'objective'] X = np.array(design['X']) M = int(re.search(r'_(.*?).json', fname).group(1)) assert X.shape[ 0] == M, f"Number of points does not match the file name: name suggests {M}, files has {X.shape[0]}" assert X.shape[1] == len( domain), "Points are in a different dimensional space than the domain" assert np.all(domain.isinside(X)), "All points must be inside the domain" # Check the objective value V = psdr.voronoi_vertex(domain, X) D = psdr.cdist(X, V) radius = np.max(np.min(D, axis=0)) print("Measured radius", '%20.15e' % radius) print("Reported radius", '%20.15e' % design['radius']) assert np.isclose(radius, design['radius'], rtol=1e-10, atol=1e-10)
def no_test_lhs(m = 2): dom = psdr.BoxDomain(-np.ones(m), np.ones(m)) N = 10 np.random.seed(0) for metric, jiggle in product(['maximin', 'corr'], [True, False]): X = dom.latin_hypercube(N, metric = metric, maxiter = 1000, jiggle = jiggle) # Check metric X0 = dom.latin_hypercube(N, metric = metric, maxiter = 1, jiggle = jiggle) if metric == 'maximin': assert np.min(pdist(X)) >= np.min(pdist(X0)) if metric == 'corr': assert np.linalg.norm(np.eye(m) - np.corrcoef(X.T), np.inf) < \ np.linalg.norm(np.eye(m) - np.corrcoef(X0.T), np.inf) # Check spacing is uniform if jiggle is False: for i in range(m): x = np.sort(X[:,i]) assert np.all(np.isclose(x[1:]-x[:-1], x[1] - x[0]))
def test_cq_center(m=3, q=10): np.random.seed(1) dom = psdr.BoxDomain(-np.ones(m), np.ones(m)) X = dom.sample(10) # Isotropic case I = np.eye(len(dom)) L1 = np.random.randn(2, m) L2 = np.random.randn(m, m) for L in [I, L1, L2]: Y = L.dot(X.T).T xhat1 = _cq_center_cvxpy(Y, L, q=q) # Naive solve xhat = cp.Variable(m) obj = cp.sum([cp.norm(xhat.__rmatmul__(L) - y)**q for y in Y]) prob = cp.Problem(cp.Minimize(obj)) prob.solve() xhat2 = xhat.value print(xhat1) print(xhat2) print("mismatch", np.linalg.norm(L.dot(xhat1 - xhat2))) assert np.linalg.norm(L.dot(xhat1 - xhat2)) < 1e-5
def test_and(m=5): np.random.seed(0) dom1 = psdr.BoxDomain(-np.ones(m), np.ones(m)) x = np.zeros(m) dom2 = psdr.PointDomain(x) dom3 = dom1 & dom2 assert np.all(np.isclose(dom3.sample(), x)) dom3 = dom2 & dom1 assert np.all(np.isclose(dom3.sample(), x)) # Check an empty domain x = 2 * np.ones(m) dom2 = psdr.PointDomain(x) dom3 = dom1 & dom2 try: y = dom3.sample() except psdr.EmptyDomainException: pass except: raise Exception("Wrong error returned")
from __future__ import print_function import numpy as np import matplotlib.pyplot as plt from matplotlib.patches import Rectangle import psdr from psdr.pgf import PGF from fig_latin import plot_projection if __name__ == '__main__': # The locking phenomina, where given enough Lipschitz matrices of sufficient rank to # uniquely specify a point if all samples where np.random.seed(0) dom = psdr.BoxDomain(-np.ones(2), np.ones(2)) fig, axes = plt.subplots(1, 2, figsize=(10, 5)) M = 20 # First 9 lock Ls = [np.array([[2, 1]]), np.array([[1, 2]])] for ax, slack in zip(axes, [1, 0.5]): X = [] for i in range(M): x = psdr.seq_maximin_sample(dom, X, Ls=Ls, slack=slack, Nsamp=int(1e3)) X.append(x) X = np.vstack(X) pgf = PGF() pgf.add('x', X[:, 0])