def test_crash(self): d = 1 tmc = MonteCarloTransform(d, n=1e4) f = UNGMTransition(GaussRV(1, cov=1.0), GaussRV(1, cov=10.0)).dyn_eval mean = np.zeros(d) cov = np.eye(d) # does it crash ? tmc.apply(f, mean, cov, np.atleast_1d(1.0))
def test_cho_dot_ein(self): # attempt to compute the transformed covariance using cholesky decomposition # integrand # input moments mean_in = np.array([6500.4, 349.14, 1.8093, 6.7967, 0.6932]) cov_in = np.diag([1e-6, 1e-6, 1e-6, 1e-6, 1]) f = ReentryVehicle2DTransition(GaussRV(5, mean_in, cov_in), GaussRV(3)).dyn_eval dim_in, dim_out = ReentryVehicle2DTransition.dim_state, 1 # transform ker_par_mo = np.hstack((np.ones((dim_out, 1)), 25 * np.ones((dim_out, dim_in)))) tf_so = GaussianProcessTransform(dim_in, dim_out, ker_par_mo, point_str='sr') # Monte-Carlo for ground truth # tf_ut = UnscentedTransform(dim_in) # tf_ut.apply(f, mean_in, cov_in, np.atleast_1d(1), None) tf_mc = MonteCarloTransform(dim_in, 1000) mean_mc, cov_mc, ccov_mc = tf_mc.apply(f, mean_in, cov_in, np.atleast_1d(1)) C_MC = cov_mc + np.outer(mean_mc, mean_mc.T) # evaluate integrand x = mean_in[:, None] + la.cholesky(cov_in).dot(tf_so.model.points) Y = np.apply_along_axis(f, 0, x, 1.0, None) # covariance via np.dot iK, Q = tf_so.model.iK, tf_so.model.Q C1 = iK.dot(Q).dot(iK) C1 = Y.dot(C1).dot(Y.T) # covariance via np.einsum C2 = np.einsum('ab, bc, cd', iK, Q, iK) C2 = np.einsum('ab,bc,cd', Y, C2, Y.T) # covariance via np.dot and cholesky K = tf_so.model.kernel.eval(tf_so.model.kernel.par, tf_so.model.points) L_lower = la.cholesky(K) Lq = la.cholesky(Q) phi = la.solve(L_lower, Lq) psi = la.solve(L_lower, Y.T) bet = psi.T.dot(phi) C3_dot = bet.dot(bet.T) C3_ein = np.einsum('ij, jk', bet, bet.T) logging.debug("MAX DIFF: {:.4e}".format(np.abs(C1 - C2).max())) logging.debug("MAX DIFF: {:.4e}".format(np.abs(C3_dot - C3_ein).max())) self.assertTrue(np.allclose(C1, C2), "MAX DIFF: {:.4e}".format(np.abs(C1 - C2).max())) self.assertTrue(np.allclose(C3_dot, C3_ein), "MAX DIFF: {:.4e}".format(np.abs(C3_dot - C3_ein).max())) self.assertTrue(np.allclose(C1, C3_dot), "MAX DIFF: {:.4e}".format(np.abs(C1 - C3_dot).max()))
def test_increasing_samples(self): d = 1 tmc = ( MonteCarloTransform(d, n=1e1), MonteCarloTransform(d, n=1e2), MonteCarloTransform(d, n=1e3), MonteCarloTransform(d, n=1e4), MonteCarloTransform(d, n=1e5), ) f = sum_of_squares # UNGM().dyn_eval mean = np.zeros(d) cov = np.eye(d) # does it crash ? for t in tmc: print(t.apply(f, mean, cov, np.atleast_1d(1.0)))
def gpq_int_var_demo(): """Compares integral variances of GPQ and GPQ+D by plotting.""" d = 1 f = UNGMTransition(GaussRV(d), GaussRV(d)).dyn_eval mean = np.zeros(d) cov = np.eye(d) kpar = np.array([[10.0] + d * [0.7]]) gpq = GaussianProcessTransform(d, 1, kern_par=kpar, kern_str='rbf', point_str='ut', point_par={'kappa': 0.0}) gpqd = GaussianProcessDerTransform(d, 1, kern_par=kpar, point_str='ut', point_par={'kappa': 0.0}) mct = MonteCarloTransform(d, n=1e4) mean_gpq, cov_gpq, cc_gpq = gpq.apply(f, mean, cov, np.atleast_1d(1.0)) mean_gpqd, cov_gpqd, cc_gpqd = gpqd.apply(f, mean, cov, np.atleast_1d(1.0)) mean_mc, cov_mc, cc_mc = mct.apply(f, mean, cov, np.atleast_1d(1.0)) xmin_gpq = norm.ppf(0.0001, loc=mean_gpq, scale=gpq.model.integral_var) xmax_gpq = norm.ppf(0.9999, loc=mean_gpq, scale=gpq.model.integral_var) xmin_gpqd = norm.ppf(0.0001, loc=mean_gpqd, scale=gpqd.model.integral_var) xmax_gpqd = norm.ppf(0.9999, loc=mean_gpqd, scale=gpqd.model.integral_var) xgpq = np.linspace(xmin_gpq, xmax_gpq, 500) ygpq = norm.pdf(xgpq, loc=mean_gpq, scale=gpq.model.integral_var) xgpqd = np.linspace(xmin_gpqd, xmax_gpqd, 500) ygpqd = norm.pdf(xgpqd, loc=mean_gpqd, scale=gpqd.model.integral_var) plt.figure() plt.plot(xgpq, ygpq, lw=2, label='gpq') plt.plot(xgpqd, ygpqd, lw=2, label='gpq+d') plt.gca().add_line( Line2D([mean_mc, mean_mc], [0, 150], linewidth=2, color='k')) plt.legend() plt.show()
def gpq_polar2cartesian_demo(): dim = 2 # Initialize transforms # high el[0], because the function is linear given x[1] kpar = np.array([[1.0, 600, 6]]) tf_gpq = GaussianProcessTransform(dim, 1, kpar, kern_str='rbf', point_str='sr') tf_sr = SphericalRadialTransform(dim) tf_mc = MonteCarloTransform(dim, n=1e4) # 10k samples # Input mean and covariance mean_in = np.array([1, np.pi / 2]) cov_in = np.diag([0.05**2, (np.pi / 10)**2]) # mean_in = np.array([10, 0]) # cov_in = np.diag([0.5**2, (5*np.pi/180)**2]) # Mapped samples x = np.random.multivariate_normal(mean_in, cov_in, size=int(1e3)).T fx = np.apply_along_axis(polar2cartesian, 0, x, None) # MC transformed moments mean_mc, cov_mc, cc_mc = tf_mc.apply(polar2cartesian, mean_in, cov_in, None) ellipse_mc = ellipse_points(mean_mc, cov_mc) # GPQ transformed moments with ellipse points mean_gpq, cov_gpq, cc = tf_gpq.apply(polar2cartesian, mean_in, cov_in, None) ellipse_gpq = ellipse_points(mean_gpq, cov_gpq) # SR transformed moments with ellipse points mean_sr, cov_sr, cc = tf_sr.apply(polar2cartesian, mean_in, cov_in, None) ellipse_sr = ellipse_points(mean_sr, cov_sr) # Plots plt.figure() # MC ground truth mean w/ covariance ellipse plt.plot(mean_mc[0], mean_mc[1], 'ro', markersize=6, lw=2) plt.plot(ellipse_mc[0, :], ellipse_mc[1, :], 'r--', lw=2, label='MC') # GPQ transformed mean w/ covariance ellipse plt.plot(mean_gpq[0], mean_gpq[1], 'go', markersize=6) plt.plot(ellipse_gpq[0, :], ellipse_gpq[1, :], color='g', label='GPQ') # SR transformed mean w/ covariance ellipse plt.plot(mean_sr[0], mean_sr[1], 'bo', markersize=6) plt.plot(ellipse_sr[0, :], ellipse_sr[1, :], color='b', label='SR') # Transformed samples of the input random variable plt.plot(fx[0, :], fx[1, :], 'k.', alpha=0.15) plt.axes().set_aspect('equal') plt.legend() plt.show() np.set_printoptions(precision=2) print("GPQ") print("Mean weights: {}".format(tf_gpq.wm)) print("Cov weight matrix eigvals: {}".format(la.eigvals(tf_gpq.Wc))) print("Integral variance: {:.2e}".format( tf_gpq.model.integral_variance(None))) print("Expected model variance: {:.2e}".format( tf_gpq.model.exp_model_variance(None))) print("SKL Score:") print("SR: {:.2e}".format( symmetrized_kl_divergence(mean_mc, cov_mc, mean_sr, cov_sr))) print("GPQ: {:.2e}".format( symmetrized_kl_divergence(mean_mc, cov_mc, mean_gpq, cov_gpq)))
def polar2cartesian_skl_demo(): num_dim = 2 # create spiral in polar domain r_spiral = lambda x: 10 * x theta_min, theta_max = 0.25 * np.pi, 2.25 * np.pi # equidistant points on a spiral num_mean = 10 theta_pt = np.linspace(theta_min, theta_max, num_mean) r_pt = r_spiral(theta_pt) # samples from normal RVs centered on the points of the spiral mean = np.array([r_pt, theta_pt]) r_std = 0.5 # multiple azimuth covariances in increasing order num_cov = 10 theta_std = np.deg2rad(np.linspace(6, 36, num_cov)) cov = np.zeros((num_dim, num_dim, num_cov)) for i in range(num_cov): cov[..., i] = np.diag([r_std**2, theta_std[i]**2]) # COMPARE moment transforms ker_par = np.array([[1.0, 60, 6]]) moment_tforms = OrderedDict([ ('gpq-sr', GaussianProcessTransform(num_dim, 1, ker_par, kern_str='rbf', point_str='sr')), ('sr', SphericalRadialTransform(num_dim)), ]) baseline_mtf = MonteCarloTransform(num_dim, n=10000) num_tforms = len(moment_tforms) # initialize storage of SKL scores skl_dict = dict([(mt_str, np.zeros((num_mean, num_cov))) for mt_str in moment_tforms.keys()]) # for each mean for i in range(num_mean): # for each covariance for j in range(num_cov): mean_in, cov_in = mean[..., i], cov[..., j] # calculate baseline using Monte Carlo mean_out_mc, cov_out_mc, cc = baseline_mtf.apply( polar2cartesian, mean_in, cov_in, None) # for each MT for mt_str in moment_tforms.keys(): # calculate the transformed moments mean_out, cov_out, cc = moment_tforms[mt_str].apply( polar2cartesian, mean_in, cov_in, None) # compute SKL skl_dict[mt_str][i, j] = symmetrized_kl_divergence( mean_out_mc, cov_out_mc, mean_out, cov_out) # PLOT the SKL score for each MT and position on the spiral plt.style.use('seaborn-deep') printfig = FigurePrint() fig = plt.figure() # Average over mean indexes ax1 = fig.add_subplot(121) index = np.arange(num_mean) + 1 for mt_str in moment_tforms.keys(): ax1.plot(index, skl_dict[mt_str].mean(axis=1), marker='o', label=mt_str.upper()) ax1.set_xlabel('Position index') ax1.set_ylabel('SKL') # Average over azimuth variances ax2 = fig.add_subplot(122, sharey=ax1) for mt_str in moment_tforms.keys(): ax2.plot(np.rad2deg(theta_std), skl_dict[mt_str].mean(axis=0), marker='o', label=mt_str.upper()) ax2.set_xlabel(r'Azimuth STD [$ \circ $]') ax2.legend() fig.tight_layout(pad=0) # save figure printfig.savefig('polar2cartesian_skl')
def polar2cartesian_skl_demo(): dim = 2 # create spiral in polar domain r_spiral = lambda x: 10 * x theta_min, theta_max = 0.25 * np.pi, 2.25 * np.pi # equidistant points on a spiral num_mean = 10 theta_pt = np.linspace(theta_min, theta_max, num_mean) r_pt = r_spiral(theta_pt) # setup input moments: means are placed on the points of the spiral num_cov = 10 # num_cov covariances are considered for each mean r_std = 0.5 theta_std = np.deg2rad(np.linspace(6, 36, num_cov)) mean = np.array([r_pt, theta_pt]) cov = np.zeros((dim, dim, num_cov)) for i in range(num_cov): cov[..., i] = np.diag([r_std**2, theta_std[i]**2]) # COMPARE moment transforms ker_par = np.array([[1.0, 60, 6]]) mul_ind = np.hstack((np.zeros( (dim, 1)), np.eye(dim), 2 * np.eye(dim))).astype(np.int) tforms = OrderedDict([ ('bsq-ut', BayesSardTransform(dim, dim, ker_par, mul_ind, point_str='ut', point_par={ 'kappa': 2, 'alpha': 1 })), ('gpq-ut', GaussianProcessTransform(dim, dim, ker_par, point_str='ut', point_par={ 'kappa': 2, 'alpha': 1 })), ('ut', UnscentedTransform(dim, kappa=2, alpha=1, beta=0)), ]) baseline_mtf = MonteCarloTransform(dim, n=10000) num_tforms = len(tforms) # initialize storage of SKL scores skl_dict = dict([(mt_str, np.zeros((num_mean, num_cov))) for mt_str in tforms.keys()]) # for each mean for i in range(num_mean): # for each covariance for j in range(num_cov): mean_in, cov_in = mean[..., i], cov[..., j] # calculate baseline using Monte Carlo mean_out_mc, cov_out_mc, cc = baseline_mtf.apply( polar2cartesian, mean_in, cov_in, None) # for each moment transform for mt_str in tforms.keys(): # calculate the transformed moments mean_out, cov_out, cc = tforms[mt_str].apply( polar2cartesian, mean_in, cov_in, None) # compute SKL skl_dict[mt_str][i, j] = symmetrized_kl_divergence( mean_out_mc, cov_out_mc, mean_out, cov_out) # PLOT the SKL score for each MT and position on the spiral plt.style.use('seaborn-deep') printfig = FigurePrint() fig = plt.figure() # Average over mean indexes ax1 = fig.add_subplot(121) index = np.arange(num_mean) + 1 for mt_str in tforms.keys(): ax1.plot(index, skl_dict[mt_str].mean(axis=1), marker='o', label=mt_str.upper()) ax1.set_xlabel('Position index') ax1.set_ylabel('SKL') # Average over azimuth variances ax2 = fig.add_subplot(122, sharey=ax1) for mt_str in tforms.keys(): ax2.plot(np.rad2deg(theta_std), skl_dict[mt_str].mean(axis=0), marker='o', label=mt_str.upper()) ax2.set_xlabel('Azimuth STD [$ \circ $]') ax2.legend() fig.tight_layout(pad=0) plt.show() # save figure printfig.savefig('polar2cartesian_skl')
def mt_trunc_demo(mt_trunc, mt, dim=None, full_input_cov=True, **kwargs): """ Comparison of truncated MT and vanilla MT on polar2cartesian transform for increasing state dimensions. The truncated MT is aware of the effective dimension, so we expect it to be closer to the true covariance Observation: Output covariance of the Truncated UT stays closer to the MC baseline than the covariance produced by the vanilla UT. There's some merit to the idea, but the problem is with computing the input-output cross-covariance. Parameters ---------- mt_trunc mt dim full_input_cov : boolean If `False`, a diagonal input covariance is used, otherwise a full covariance is used. kwargs Returns ------- """ assert issubclass(mt_trunc, MomentTransform) and issubclass(mt, MomentTransform) # state dimensions and effective dimension dim = [2, 3, 4, 5] if dim is None else dim d_eff = 2 # nonlinear integrand f = polar2cartesian # input mean and covariance mean_eff, cov_eff = np.array([1, np.pi / 2]), np.diag([0.05 ** 2, (np.pi / 10) ** 2]) if full_input_cov: A = np.random.rand(d_eff, d_eff) cov_eff = A.dot(cov_eff).dot(A.T) # use MC transform with lots of samples to compute the true transformed moments tmc = MonteCarloTransform(d_eff, n=1e4) M_mc, C_mc, cc_mc = tmc.apply(f, mean_eff, cov_eff, None) # transformed samples x = np.random.multivariate_normal(mean_eff, cov_eff, size=int(1e3)).T fx = np.apply_along_axis(f, 0, x, None) X_mc = ellipse_points(M_mc, C_mc) M = np.zeros((2, len(dim), 2)) C = np.zeros((2, 2, len(dim), 2)) X = np.zeros((2, 50, len(dim), 2)) for i, d in enumerate(dim): t = mt_trunc(d, d_eff, **kwargs) s = mt(d, **kwargs) # input mean and covariance mean, cov = np.zeros(d), np.eye(d) mean[:d_eff], cov[:d_eff, :d_eff] = mean_eff, cov_eff # transformed moments (w/o cross-covariance) M[:, i, 0], C[..., i, 0], cc = t.apply(f, mean, cov, None) M[:, i, 1], C[..., i, 1], cc = s.apply(f, mean, cov, None) # points on the ellipse defined by the transformed mean and covariance for plotting X[..., i, 0] = ellipse_points(M[:, i, 0], C[..., i, 0]) X[..., i, 1] = ellipse_points(M[:, i, 1], C[..., i, 1]) # PLOTS: transformed samples, MC mean and covariance ground truth fig, ax = plt.subplots(1, 2) ax[0].plot(fx[0, :], fx[1, :], 'k.', alpha=0.15) ax[0].plot(M_mc[0], M_mc[1], 'ro', markersize=6, lw=2) ax[0].plot(X_mc[0, :], X_mc[1, :], 'r--', lw=2, label='MC') # SR and SR-T mean and covariance for various state dimensions # TODO: it's more effective to plot SKL between the transformed and baseline covariances. for i, d in enumerate(dim): ax[0].plot(M[0, i, 0], M[1, i, 0], 'b+', markersize=10, lw=2) ax[0].plot(X[0, :, i, 0], X[1, :, i, 0], color='b', label='mt-trunc (d={})'.format(d)) for i, d in enumerate(dim): ax[0].plot(M[0, i, 1], M[1, i, 1], 'go', markersize=6) ax[0].plot(X[0, :, i, 1], X[1, :, i, 1], color='g', label='mt (d={})'.format(d)) ax[0].set_aspect('equal') plt.legend() # symmetrized KL-divergence skl = np.zeros((len(dim), 2)) for i, d in enumerate(dim): skl[i, 0] = symmetrized_kl_divergence(M_mc, C_mc, M[:, i, 0], C[..., i, 0]) skl[i, 1] = symmetrized_kl_divergence(M_mc, C_mc, M[:, i, 1], C[..., i, 1]) plt_opt = {'lw': 2, 'marker': 'o'} ax[1].plot(dim, skl[:, 0], label='truncated', **plt_opt) ax[1].plot(dim, skl[:, 1], label='original', **plt_opt) ax[1].set_xticks(dim) ax[1].set_xlabel('Dimension') ax[1].set_ylabel('SKL') plt.legend() plt.show()
def gpq_kl_demo(): """Compares moment transforms in terms of symmetrized KL divergence.""" # input dimension d = 2 # unit sigma-points pts = SphericalRadialTransform.unit_sigma_points(d) # derivative mask, which derivatives to use dmask = np.arange(pts.shape[1]) # RBF kernel hyper-parameters hyp = { 'sos': np.array([[10.0] + d * [6.0]]), 'rss': np.array([[10.0] + d * [0.2]]), 'toa': np.array([[10.0] + d * [3.0]]), 'doa': np.array([[1.0] + d * [2.0]]), 'rdr': np.array([[10.0] + d * [5.0]]), } # baseline: Monte Carlo transform w/ 20,000 samples mc_baseline = MonteCarloTransform(d, n=2e4) # tested functions # rss has singularity at 0, therefore no derivative at 0 # toa does not have derivative at 0, for d = 1 # rss, toa and sos can be tested for all d > 0; physically d=2,3 make sense # radar and doa only for d = 2 test_functions = ( # sos, toa, rss, doa, rdr, ) # fix seed np.random.seed(3) # moments of the input Gaussian density mean = np.zeros(d) cov_samples = 100 # space allocation for KL divergence kl_data = np.zeros((3, len(test_functions), cov_samples)) re_data_mean = np.zeros((3, len(test_functions), cov_samples)) re_data_cov = np.zeros((3, len(test_functions), cov_samples)) print( 'Calculating symmetrized KL-divergences using {:d} covariance samples...' .format(cov_samples)) for i in trange(cov_samples): # random PD matrix a = np.random.randn(d, d) cov = a.dot(a.T) a = np.diag(1.0 / np.sqrt(np.diag(cov))) # 1 on diagonal cov = a.dot(cov).dot(a.T) for idf, f in enumerate(test_functions): # print "Testing {}".format(f.__name__) mean[:d - 1] = 0.2 if f.__name__ in 'rss' else mean[:d - 1] mean[:d - 1] = 3.0 if f.__name__ in 'doa rdr' else mean[:d - 1] jitter = 1e-8 * np.eye( 2) if f.__name__ == 'rdr' else 1e-8 * np.eye(1) # baseline moments using Monte Carlo mean_mc, cov_mc, cc = mc_baseline.apply(f, mean, cov, None) # tested moment transforms transforms = ( SphericalRadialTransform(d), GaussianProcessTransform(d, 1, kern_par=hyp[f.__name__], point_str='sr'), GaussianProcessDerTransform(d, 1, kern_par=hyp[f.__name__], point_str='sr', which_der=dmask), ) for idt, t in enumerate(transforms): # apply transform mean_t, cov_t, cc = t.apply(f, mean, cov, None) # calculate KL distance to the baseline moments kl_data[idt, idf, i] = kl_div_sym(mean_mc, cov_mc + jitter, mean_t, cov_t + jitter) re_data_mean[idt, idf, i] = rel_error(mean_mc, mean_t) re_data_cov[idt, idf, i] = rel_error(cov_mc, cov_t) # average over MC samples kl_data = kl_data.mean(axis=2) re_data_mean = re_data_mean.mean(axis=2) re_data_cov = re_data_cov.mean(axis=2) # put into pandas dataframe for nice printing and latex output row_labels = [t.__class__.__name__ for t in transforms] col_labels = [f.__name__ for f in test_functions] kl_df = pd.DataFrame(kl_data, index=row_labels, columns=col_labels) re_mean_df = pd.DataFrame(re_data_mean, index=row_labels, columns=col_labels) re_cov_df = pd.DataFrame(re_data_cov, index=row_labels, columns=col_labels) return kl_df, re_mean_df, re_cov_df