def model(self, x, w): # feature transformation - switch for dealing # with feature transforms that either do or do # not have internal parameters f = 0 if len(self.sig.parameters) == 2: if np.shape(w)[1] == 1: f = self.feature_transforms(x, w) else: f = self.feature_transforms(x, w[0]) else: f = self.feature_transforms(x) # tack a 1 onto the top of each input point all at once o = np.ones((1, np.shape(f)[1])) f = np.vstack((o, f)) # compute linear combination and return # switch for dealing with feature transforms that either # do or do not have internal parameters a = 0 if np.ndim(w) == 2: a = np.dot(f.T, w) elif np.ndim(w) == 3: a = np.dot(f.T, w[1]) return a
def sum_to_match_shape(sum_this, to_match_this): sum_this = np.sum( sum_this, axis=tuple(range(0, np.ndim(sum_this) - np.ndim(to_match_this)))) for axis, size in enumerate(np.shape(to_match_this)): if size == 1: sum_this = np.sum(sum_this, axis=axis, keepdims=True) return sum_this
def repeat_to_match_shape(g, A, axis=None): gout = np.empty_like(A) if np.ndim(gout) == 0: gout = g else: gout = np.ones_like(A) * g return gout
def compare_smoother_grads(lds): init_params, pair_params, node_params = lds symmetrize = make_unop( lambda x: (x + x.T) / 2. if np.ndim(x) == 2 else x, tuple) messages, _ = natural_filter_forward_general(*lds) dotter = randn_like(natural_smoother_general(messages, *lds)) def py_fun(messages): result = natural_smoother_general(messages, *lds) assert shape(result) == shape(dotter) return contract(dotter, result) dense_messages, _ = _natural_filter_forward_general( init_params, pair_params, node_params) def cy_fun(messages): result = _natural_smoother_general(messages, pair_params) result = result[0][:3], result[1], result[2] assert shape(result) == shape(dotter) return contract(dotter, result) result_py = py_fun(messages) result_cy = cy_fun(dense_messages) assert np.isclose(result_py, result_cy) g_py = grad(py_fun)(messages) g_cy = unpack_dense_messages(grad(cy_fun)(dense_messages)) assert allclose(g_py, g_cy)
def visual_comparison(x, weights): ''' Visually compare the results of several runs of PCA applied to two dimensional input and two principal components ''' # do weights weights = np.array(weights) num_runs = np.ndim(weights) # plot data fig = plt.figure(figsize=(10, 4)) gs = gridspec.GridSpec(1, num_runs) for run in range(num_runs): # create subplot ax = plt.subplot(gs[run], aspect='equal') w_best = weights[run] # scatter data ax.scatter(x[0, :], x[1, :], c='k') # plot pc 1 vector_draw(w_best[:, 0], ax, color='red', zorder=1) vector_draw(w_best[:, 1], ax, color='red', zorder=1) # plot vertical / horizontal axes ax.axhline(linewidth=0.5, color='k', zorder=0) ax.axvline(linewidth=0.5, color='k', zorder=0) ax.set_title('run ' + str(run + 1), fontsize=16) ax.set_xlabel(r'$x_1$', fontsize=16) ax.set_ylabel(r'$x_2$', fontsize=16, rotation=0, labelpad=10)
def test_hessian_tensor_product(): fun = lambda a: np.sum(np.sin(a)) a = npr.randn(5, 4, 3) V = npr.randn(5, 4, 3) H = hessian(fun)(a) check_equivalent(np.tensordot(H, V, axes=np.ndim(V)), hessian_tensor_product(fun)(a, V))
def test_tensor_jacobian_product(): fun = lambda a: np.roll(np.sin(a), 1) a = npr.randn(5, 4, 3) V = npr.randn(5, 4) J = jacobian(fun)(a) check_equivalent(np.tensordot(V, J, axes=np.ndim(V)), tensor_jacobian_product(fun)(a, V))
def setUp(self): self.m = m = 100 self.n = n = 50 self.man = Sphere(m, n) # For automatic testing of ehess2rhess self.proj = lambda x, u: u - np.tensordot(x, u, np.ndim(u)) * x
def compare_smoother_grads(lds): init_params, pair_params, node_params = lds symmetrize = make_unop(lambda x: (x + x.T)/2. if np.ndim(x) == 2 else x, tuple) messages, _ = natural_filter_forward_general(*lds) dotter = randn_like(natural_smoother_general(messages, *lds)) def py_fun(messages): result = natural_smoother_general(messages, *lds) assert shape(result) == shape(dotter) return contract(dotter, result) dense_messages, _ = _natural_filter_forward_general( init_params, pair_params, node_params) def cy_fun(messages): result = _natural_smoother_general(messages, pair_params) result = result[0][:3], result[1], result[2] assert shape(result) == shape(dotter) return contract(dotter, result) result_py = py_fun(messages) result_cy = cy_fun(dense_messages) assert np.isclose(result_py, result_cy) g_py = grad(py_fun)(messages) g_cy = unpack_dense_messages(grad(cy_fun)(dense_messages)) assert allclose(g_py, g_cy)
def _compose_einsums(formula, args1, args2, parent_formula, parent_args): parent_formula = debroadcast_formula( parent_formula, *[np.ndim(arg) for arg in parent_args]) parent_in_formulas, parent_out_formula = split_einsum_formula( parent_formula) parent_ndim = len(parent_out_formula) arg_ndims = ([np.ndim(arg) for arg in args1] + [parent_ndim] + [np.ndim(arg) for arg in args2]) formula = debroadcast_formula(formula, *arg_ndims) in_formulas, out_formula = split_einsum_formula(formula) i = len(args1) if len(parent_out_formula) != len(in_formulas[i]): raise ValueError('Input formula {} and parent formula {} have' ' inconsistent numbers of indexes, broadcasting' 'problem?'.format(in_formulas[i], parent_out_formula)) subs_map = collections.defaultdict(iter(_einsum_range).next) # splice out the old input formula old_in_formula = in_formulas[i] in_formulas = in_formulas[:i] + in_formulas[i + 1:] # canonicalize input and output formulas (optional, for cleanliness) in_formulas = [ ''.join(subs_map[idx] for idx in subs) for subs in in_formulas ] out_formula = ''.join(subs_map[idx] for idx in out_formula) # identify parent output indices with corresponding input indices subs_map.update((pidx + '_parent', subs_map[idx]) for pidx, idx in zip(parent_out_formula, old_in_formula)) # update the parent input formulas parent_in_formulas = [ ''.join(subs_map[idx + '_parent'] for idx in subs) for subs in parent_in_formulas ] # splice the formula lists and arguments new_in_formulas = in_formulas[:i] + parent_in_formulas + in_formulas[i:] new_args = args1 + parent_args + args2 new_formula = _reconstitute_einsum_formula(new_in_formulas, out_formula) return np.einsum(new_formula, *new_args)
def _dot_vjp_0(ans, sparse, dense): if max(anp.ndim(sparse), anp.ndim(dense)) > 2: raise NotImplementedError("Current dot vjps only support ndim <= 2.") if anp.ndim(sparse) == 0: return lambda g: anp.sum(dense * g) if anp.ndim(sparse) == 1 and anp.ndim(dense) == 1: return lambda g: g * dense if anp.ndim(sparse) == 2 and anp.ndim(dense) == 1: return lambda g: g[:, None] * dense if anp.ndim(sparse) == 1 and anp.ndim(dense) == 2: print(' 4th case') return lambda g: anp.dot(dense, g) return lambda g: dot(dense.T, g)
def draw_weight_path(self, ax, w_hist, **kwargs): # make colors for plot colorspec = self.make_colorspec(w_hist) arrows = True if 'arrows' in kwargs: arrows = kwargs['arrows'] ### plot function decrease plot in right panel for j in range(len(w_hist)): w_val = w_hist[j] # plot each weight set as a point ax.scatter(w_val[0], w_val[1], s=80, c=colorspec[j], edgecolor='k', linewidth=2 * math.sqrt((1 / (float(j) + 1))), zorder=3) # plot connector between points for visualization purposes if j > 0: pt1 = w_hist[j - 1] pt2 = w_hist[j] # produce scalar for arrow head length pt_length = np.linalg.norm(pt1 - pt2) head_length = 0.1 alpha = (head_length - 0.35) / pt_length + 1 # if points are different draw error if np.linalg.norm(pt1 - pt2) > head_length and arrows == True: if np.ndim(pt1) > 1: pt1 = pt1.flatten() pt2 = pt2.flatten() ax.arrow(pt1[0], pt1[1], (pt2[0] - pt1[0]) * alpha, (pt2[1] - pt1[1]) * alpha, head_width=0.1, head_length=head_length, fc='k', ec='k', linewidth=4, zorder=2, length_includes_head=True) ax.arrow(pt1[0], pt1[1], (pt2[0] - pt1[0]) * alpha, (pt2[1] - pt1[1]) * alpha, head_width=0.1, head_length=head_length, fc='w', ec='w', linewidth=0.25, zorder=2, length_includes_head=True)
def setUp(self): self.m = m = 100 self.n = n = 50 self.manifold = Sphere(m, n) # For automatic testing of euclidean_to_riemannian_hessian self.projection = lambda x, u: u - np.tensordot(x, u, np.ndim(u)) * x super().setUp()
def broadcast(gvs, vs, result, broadcast_idx=0): while anp.ndim(result) < len(vs.shape): result = anp.expand_dims(result, 0) for axis, size in enumerate(anp.shape(result)): if size == 1: result = anp.repeat(result, vs.shape[axis], axis=axis) if vs.iscomplex and not gvs.iscomplex: result = result + 0j return result
def maybe_einsum(formula, *args): formula = debroadcast_formula(formula, *[np.ndim(arg) for arg in args]) if any(_is_constant_zero(arg) for arg in args): return _zeros_like_einsum(formula, args, ()) if len(args) == 1: input_formulas, output_formula = split_einsum_formula(formula) if input_formulas[0] == output_formula: return args[0] return constant_folding_einsum(formula, *args)
def label_meanfield(label_global, gaussian_globals, gaussian_stats): partial_contract = lambda a, b: \ sum(np.tensordot(x, y, axes=np.ndim(y)) for x, y, in zip(a, b)) gaussian_local_natparams = map(niw.expectedstats, gaussian_globals) node_params = np.array([ partial_contract(gaussian_stats, natparam) for natparam in gaussian_local_natparams]).T local_natparam = dirichlet.expectedstats(label_global) + node_params stats = normalize(np.exp(local_natparam - logsumexp(local_natparam, axis=1, keepdims=True))) vlb = np.sum(logsumexp(local_natparam, axis=1)) - contract(stats, node_params) return local_natparam, stats, vlb
def argmin_vjp(ans, x): """ This should return the jacobian-vector product it should calculate d_ans/dx because the vector contains dloss/dans then we get with dloss/dans * dans/dx = dloss/dx which we're actually interested in """ g = elementwise_grad(O2, 1) dg_dy = elementwise_grad(g, 1)(x, initial_y) dg_dx = elementwise_grad(g, 0)(x, initial_y) if np.ndim(dg_dy) == 0: # we have just simple scalar function so we just have to divide instead of inverse return lambda v: v * (1. / dg_dy) * dg_dx return lambda v: v * np.matmul(np.linalg.inv(dg_dy), dg_dx)
def svd_solve(U, s, V, b, s_tol=1e-15): """ Solve the system :math:`A X = b` for :math:`X` where :math:`A` is a positive semi-definite matrix using the singular value decomposition. This truncates the SVD so only dimensions corresponding to non-negative and sufficiently large singular values are used. Parameters ---------- U: numpy.ndarray The :code:`U` factor of :code:`U, s, V = svd(A)` positive semi-definite matrix. s: numpy.ndarray The :code:`s` factor of :code:`U, s, V = svd(A)` positive semi-definite matrix. V: numpy.ndarray The :code:`V` factor of :code:`U, s, V = svd(A)` positive semi-definite matrix. b: numpy.ndarray An array or matrix s_tol: float Cutoff for small singular values. Singular values smaller than :code:`s_tol` are clamped to :code:`s_tol`. Returns ------- X: numpy.ndarray The result of :math:`X = A^-1 b` okind: numpy.ndarray The indices of :code:`s` that are kept in the factorisation """ # Test shapes for efficient computations n = U.shape[0] assert (b.shape[0] == n) m = b.shape[1] if np.ndim(b) > 1 else 1 # Auto clamp SVD based on threshold sclamp = np.maximum(s, s_tol) # Inversion factors ss = 1. / np.sqrt(sclamp) U2 = U * ss[np.newaxis, :] V2 = ss[:, np.newaxis] * V if m < n: # Few queries X = U2.dot(V2.dot(b)) # O(n^2 (2m)) else: X = U2.dot(V2).dot(b) # O(n^2 (m + n)) return X
def draw_weight_path(self, ax, w_hist, **kwargs): # make colors for plot colorspec = self.make_colorspec(w_hist) arrows = True if 'arrows' in kwargs: arrows = kwargs['arrows'] ### plot function decrease plot in right panel for j in range(len(w_hist)): w_val = w_hist[j] # plot each weight set as a point ax.scatter(w_val[0], w_val[1], s=80, color=colorspec[j], edgecolor=self.edgecolor, linewidth=2 * math.sqrt((1 / (float(j) + 1))), zorder=3) # plot connector between points for visualization purposes if j > 0: pt1 = w_hist[j - 1] pt2 = w_hist[j] # produce scalar for arrow head length pt_length = np.linalg.norm(pt1 - pt2) head_length = 0.1 alpha = (head_length - 0.35) / pt_length + 1 # if points are different draw error if np.linalg.norm(pt1 - pt2) > head_length and arrows == True: if np.ndim(pt1) > 1: pt1 = pt1.flatten() pt2 = pt2.flatten() # draw color connectors for visualization w_old = pt1 w_new = pt2 ax.plot([w_old[0], w_new[0]], [w_old[1], w_new[1]], color=colorspec[j], linewidth=2, alpha=1, zorder=2) # plot approx ax.plot([w_old[0], w_new[0]], [w_old[1], w_new[1]], color='k', linewidth=3, alpha=1, zorder=1) # plot approx
def get_arhmm_local_nodeparams(lds_global_natparam, lds_expected_stats): init_stats, pair_stats = lds_expected_stats[:2] all_init_params, all_pair_params = get_all_lds_local_natparams(lds_global_natparam) dense_init_params = map(np.stack, zip(*all_init_params)) dense_pair_params = map(np.stack, zip(*all_pair_params)) partial_contract = lambda a: lambda b: contract(a, b) init_node_potential = np.array(map(partial_contract(init_stats), all_init_params)) partial_contract = lambda a: lambda b: \ sum(np.tensordot(x, y, axes=np.ndim(y)) for x, y in zip(a,b)) remaining_node_potentials = np.vstack(map(partial_contract(pair_stats), all_pair_params)).T node_potentials = np.vstack((init_node_potential, remaining_node_potentials)) return node_potentials
def __init__(self, func, initialX, interval=[-1e15, 1e15], ftol=1e-6, maxIters=1e3, maxItersLS=200): self.costFunc = func self.gradFunc = grad(self.evaluate) self.hessFunc = hessian(self.evaluate) self.maxIters = int(maxIters) self.maxItersLS = maxItersLS self.interval = interval self.fevals = 0 self.ftol = ftol self.x_is_matrix= False if np.ndim(initialX) > 1: self.x_is_matrix = True initialX = np.squeeze(initialX) self.xLen = np.shape(initialX)[0] self.direction = np.zeros((maxIters, self.xLen)) self.x = np.zeros((maxIters, self.xLen)) self.x[0] = initialX
def compute(self, theta): # Not exactly the same as the equation of Gauss-Newton update # d = lstsq(J, r), not the stardard update d = inv (J^T * J) * J * r # however, it works better than implementing the equation malually r = self.flattened_residual(theta) J = self.jacobian(theta) check_non_nan(r) check_non_nan(J) assert(np.ndim(r) == 1) # residuals can be a multi-dimensonal array so flatten them J = J.reshape(r.shape[0], theta.shape[0]) # TODO add weighted Gauss-Newton as an option # weights = self.robustifier.weights(r) delta, error, _, _ = np.linalg.lstsq(J, r, rcond=None) return delta
def __init__(self, func, initialX, interval=[-1e15, 1e15], ftol=1e-6, maxIters=1e3, maxItersLS=200): self.costFunc = func self.gradFunc = grad(self.evaluate) self.hessFunc = hessian(self.evaluate) self.maxIters = int(maxIters) self.maxItersLS = maxItersLS self.interval = interval self.fevals = 0 self.ftol = ftol self.x_is_matrix = False if np.ndim(initialX) > 1: self.x_is_matrix = True initialX = np.squeeze(initialX) self.xLen = np.shape(initialX)[0] self.direction = np.zeros((maxIters, self.xLen)) self.x = np.zeros((maxIters, self.xLen)) self.x[0] = initialX self.gradient = np.zeros((self.maxIters, self.xLen)) self.S = np.zeros((self.maxIters, self.xLen, self.xLen)) self.epsilon1 = self.ftol self.k = 0 self.m = 0 self.rho = 0.1 self.sigma = 0.7 self.tau = 0.1 self.chi = 0.75 self.m_hat = 600 self.epsilon2 = 1e-10 self.S[0] = np.eye(self.xLen)
def test_hessian_tensor_product(): fun = lambda a: np.sum(np.sin(a)) a = npr.randn(5, 4, 3) V = npr.randn(5, 4, 3) H = hessian(fun)(a) check_equivalent(np.tensordot(H, V, axes=np.ndim(V)), hessian_vector_product(fun)(a, V))
def vector_dot_fun(*args, **kwargs): args, vector = args[:-1], args[-1] return np.tensordot(vector, fun(*args, **kwargs), axes=np.ndim(vector))
def vector_dot_grad(*args, **kwargs): args, vector = args[:-1], args[-1] return np.tensordot(fun_grad(*args, **kwargs), vector, np.ndim(vector))
def generalized_outer_product(x): if np.ndim(x) == 1: return np.outer(x, x) return np.matmul(x, np.swapaxes(x, -1, -2))
def T(X): return np.swapaxes(X, -1, -2) if np.ndim(X) > 1 else X def symmetrize(X): return 0.5 * (X + T(X))
def T(X): return anp.swapaxes(X, -1, -2) if anp.ndim(X) > 1 else X
assert shape(a) == shape(b) return binop(a, b) return wrapped make_binop = (lambda make_binop: lambda *args: add_binop_size_check(make_binop(*args)))(make_binop) add = make_binop(operator.add, tuple) sub = make_binop(operator.sub, tuple) mul = make_binop(operator.mul, tuple) div = make_binop(operator.truediv, tuple) allclose = make_binop(np.allclose, all) contract = make_binop(inner, sum) shape = make_unop(np.shape, tuple) unbox = make_unop(getval, tuple) sqrt = make_unop(np.sqrt, tuple) square = make_unop(lambda a: a**2, tuple) randn_like = make_unop(lambda a: npr.normal(size=np.shape(a)), tuple) zeros_like = make_unop(lambda a: np.zeros(np.shape(a)), tuple) flatten = make_unop(lambda a: np.ravel(a), np.concatenate) scale = make_scalar_op(operator.mul, tuple) add_scalar = make_scalar_op(operator.add, tuple) norm = lambda x: np.sqrt(contract(x, x)) rand_dir_like = lambda x: scale(1./norm(x), randn_like(x)) isobjarray = lambda x: isinstance(x, np.ndarray) and x.dtype == np.object tuplify = Y(lambda f: lambda a: a if not istuple(a) and not isobjarray(a) else tuple(map(f, a))) depth = Y(lambda f: lambda a: np.ndim(a) if not istuple(a) else 1+(min(map(f, a)) if len(a) else 1))
def _dot_vjp_1(ans, sparse, dense): if anp.ndim(sparse) != 2 or anp.ndim(dense) > 2: raise NotImplementedError( "Current dot vjps only support sparse matrices with ndim == 2.") return lambda g: dot(sparse.T, g)
make_binop = (lambda make_binop: lambda *args: add_binop_size_check( make_binop(*args)))(make_binop) add = make_binop(operator.add, tuple) sub = make_binop(operator.sub, tuple) mul = make_binop(operator.mul, tuple) div = make_binop(operator.truediv, tuple) allclose = make_binop(np.allclose, all) contract = make_binop(inner, sum) shape = make_unop(np.shape, tuple) unbox = make_unop(getval, tuple) sqrt = make_unop(np.sqrt, tuple) square = make_unop(lambda a: a**2, tuple) randn_like = make_unop(lambda a: npr.normal(size=np.shape(a)), tuple) zeros_like = make_unop(lambda a: np.zeros(np.shape(a)), tuple) flatten = make_unop(lambda a: np.ravel(a), np.concatenate) scale = make_scalar_op(operator.mul, tuple) add_scalar = make_scalar_op(operator.add, tuple) norm = lambda x: np.sqrt(contract(x, x)) rand_dir_like = lambda x: scale(1. / norm(x), randn_like(x)) isobjarray = lambda x: isinstance(x, np.ndarray) and x.dtype == np.object tuplify = Y(lambda f: lambda a: a if not istuple(a) and not isobjarray(a) else tuple(map(f, a))) depth = Y(lambda f: lambda a: np.ndim(a) if not istuple(a) else 1 + (min(map(f, a)) if len(a) else 1))
def test_tensor_jacobian_product(): fun = lambda a: np.roll(np.sin(a), 1) a = npr.randn(5, 4, 3) V = npr.randn(5, 4) J = jacobian(fun)(a) check_equivalent(np.tensordot(V, J, axes=np.ndim(V)), vector_jacobian_product(fun)(a, V))
def show_encode_decode(x, cost_history, weight_history, **kwargs): ''' Examine the results of linear or nonlinear PCA / autoencoder to two-dimensional input. Four panels are shown: - original data (top left panel) - data projected onto lower dimensional curve (top right panel) - lower dimensional curve (lower left panel) - vector field illustrating how points in space are projected onto lower dimensional curve (lower right panel) Inputs: - x: data - encoder: encoding function from autoencoder - decoder: decoding function from autoencoder - cost_history/weight_history: from run of gradient descent minimizing PCA least squares Optinal inputs: - show_pc: show pcs? Only useful really for linear case. - scale: for vector field / quiver plot, adjusts the length of arrows in vector field ''' # user-adjustable args encoder = lambda a, b: np.dot(b.T, a) decoder = lambda a, b: np.dot(b, a) if 'encoder' in kwargs: encoder = kwargs['encoder'] if 'decoder' in kwargs: decoder = kwargs['decoder'] projmap = False if 'projmap' in kwargs: projmap = kwargs['projmap'] show_pc = False if 'show_pc' in kwargs: show_pc = kwargs['show_pc'] scale = 14 if 'scale' in kwargs: scale = kwargs['scale'] encode_label = '' if 'encode_label' in kwargs: encode_label = kwargs['encode_label'] # pluck out best weights ind = np.argmin(cost_history) w_best = weight_history[ind] num_params = 0 if type(w_best) == list: num_params = len(w_best) else: num_params = np.ndim(w_best) - 1 ###### figure 1 - original data, encoded data, decoded data ###### fig = plt.figure(figsize=(10, 4)) gs = gridspec.GridSpec(1, 3) ax1 = plt.subplot(gs[0], aspect='equal') ax2 = plt.subplot(gs[1], aspect='equal') ax3 = plt.subplot(gs[2], aspect='equal') # scatter original data with pc ax1.scatter(x[0, :], x[1, :], c='k', s=60, linewidth=0.75, edgecolor='w') if show_pc == True: for pc in range(np.shape(w_best)[1]): ax1.arrow(0, 0, w_best[0, pc], w_best[1, pc], head_width=0.25, head_length=0.5, fc='k', ec='k', linewidth=4) ax1.arrow(0, 0, w_best[0, pc], w_best[1, pc], head_width=0.25, head_length=0.5, fc='r', ec='r', linewidth=3) ### plot encoded and decoded data ### v = 0 p = 0 if num_params == 2: # create encoded vectors v = encoder(x, w_best[0]) # decode onto basis p = decoder(v, w_best[1]) else: # create encoded vectors v = encoder(x, w_best) # decode onto basis p = decoder(v, w_best) # plot decoded data z = np.zeros((1, np.size(v))) ax2.scatter(v, z, c='k', s=60, linewidth=0.75, edgecolor='w') # plot decoded data ax3.scatter(p[0, :], p[1, :], c='k', s=60, linewidth=0.75, edgecolor='r') # clean up panels xmin1 = np.min(x[0, :]) xmax1 = np.max(x[0, :]) xmin2 = np.min(x[1, :]) xmax2 = np.max(x[1, :]) xgap1 = (xmax1 - xmin1) * 0.2 xgap2 = (xmax2 - xmin2) * 0.2 xmin1 -= xgap1 xmax1 += xgap1 xmin2 -= xgap2 xmax2 += xgap2 for ax in [ax1, ax2, ax3]: if ax == ax1 or ax == ax3: ax.set_xlim([xmin1, xmax1]) ax.set_ylim([xmin2, xmax2]) ax.set_xlabel(r'$x_1$', fontsize=16) ax.set_ylabel(r'$x_2$', fontsize=16, rotation=0, labelpad=10) ax.axvline(linewidth=0.5, color='k', zorder=0) else: ax.set_ylim([-1, 1]) if len(encode_label) > 0: ax.set_xlabel(encode_label, fontsize=16) ax.axhline(linewidth=0.5, color='k', zorder=0) ax1.set_title('original data', fontsize=18) ax2.set_title('encoded data', fontsize=18) ax3.set_title('decoded data', fontsize=18) # plot learned manifold a = np.linspace(xmin1, xmax1, 200) b = np.linspace(xmin2, xmax2, 200) s, t = np.meshgrid(a, b) s.shape = (1, len(a)**2) t.shape = (1, len(b)**2) z = np.vstack((s, t)) v = 0 p = 0 if num_params == 2: # create encoded vectors v = encoder(z, w_best[0]) # decode onto basis p = decoder(v, w_best[1]) else: # create encoded vectors v = encoder(z, w_best) # decode onto basis p = decoder(v, w_best) ax3.scatter(p[0, :], p[1, :], c='k', s=1.5, edgecolor='r', linewidth=1, zorder=0) # set whitespace #fgs.update(wspace=0.01, hspace=0.5) # set the spacing between axes. ##### bottom panels - plot subspace and quiver plot of projections #### if projmap == True: fig = plt.figure(figsize=(10, 4)) gs = gridspec.GridSpec(1, 1) ax1 = plt.subplot(gs[0], aspect='equal') ax1.scatter(p[0, :], p[1, :], c='r', s=9.5) ax1.scatter(p[0, :], p[1, :], c='k', s=1.5) ### create quiver plot of how data is projected ### new_scale = 0.75 a = np.linspace(xmin1 - xgap1 * new_scale, xmax1 + xgap1 * new_scale, 20) b = np.linspace(xmin2 - xgap2 * new_scale, xmax2 + xgap2 * new_scale, 20) s, t = np.meshgrid(a, b) s.shape = (1, len(a)**2) t.shape = (1, len(b)**2) z = np.vstack((s, t)) v = 0 p = 0 if num_params == 2: # create encoded vectors v = encoder(z, w_best[0]) # decode onto basis p = decoder(v, w_best[1]) else: # create encoded vectors v = encoder(z, w_best) # decode onto basis p = decoder(v, w_best) # get directions d = [] for i in range(p.shape[1]): dr = (p[:, i] - z[:, i])[:, np.newaxis] d.append(dr) d = 2 * np.array(d) d = d[:, :, 0].T M = np.hypot(d[0, :], d[1, :]) ax1.quiver(z[0, :], z[1, :], d[0, :], d[1, :], M, alpha=0.5, width=0.01, scale=scale, cmap='autumn') ax1.quiver(z[0, :], z[1, :], d[0, :], d[1, :], edgecolor='k', linewidth=0.25, facecolor='None', width=0.01, scale=scale) #### clean up and label panels #### for ax in [ax1]: ax.set_xlim([xmin1 - xgap1 * new_scale, xmax1 + xgap1 * new_scale]) ax.set_ylim([xmin2 - xgap2 * new_scale, xmax2 + xgap1 * new_scale]) ax.set_xlabel(r'$x_1$', fontsize=16) ax.set_ylabel(r'$x_2$', fontsize=16, rotation=0, labelpad=10) ax1.set_title('projection map', fontsize=18) # set whitespace gs.update(wspace=0.01, hspace=0.5) # set the spacing between axes.