def test_rbf_scaling_invariance(self): dim = 5 ker_par = np.array([[1, 3, 3, 3, 3, 3]], dtype=np.float) tf = GaussianProcessTransform(dim, 1, ker_par) w0 = tf.weights([1] + dim * [1000]) w1 = tf.weights([358.0] + dim * [1000.0]) self.assertTrue( np.alltrue([np.array_equal(a, b) for a, b in zip(w0, w1)]))
def __init__(self, dyn, obs, kern_par_dyn, kern_par_obs, point_hyp=None, dof=4.0, fixed_dof=True): """ Student filter with Gaussian Process quadrature moment transforms using fully-symmetric sigma-point set. Parameters ---------- dyn : TransitionModel obs : MeasurementModel kern_par_dyn : numpy.ndarray Kernel parameters for the GPQ moment transform of the dynamics. kern_par_obs : numpy.ndarray Kernel parameters for the GPQ moment transform of the measurement function. point_hyp : dict Point set parameters with keys: * `'degree'`: Degree (order) of the quadrature rule. * `'kappa'`: Tuning parameter of controlling spread of sigma-points around the center. dof : float Desired degree of freedom for the filtered density. fixed_dof : bool If `True`, DOF will be fixed for all time steps, which preserves the heavy-tailed behaviour of the filter. If `False`, DOF will be increasing after each measurement update, which means the heavy-tailed behaviour is not preserved and therefore converges to a Gaussian filter. """ # degrees of freedom for SSM noises _, _, q_dof = dyn.noise_rv.get_stats() _, _, r_dof = obs.noise_rv.get_stats() # add DOF of the noises to the sigma-point parameters if point_hyp is None: point_hyp = dict() point_hyp_dyn = point_hyp point_hyp_obs = point_hyp point_hyp_dyn.update({'dof': q_dof}) point_hyp_obs.update({'dof': r_dof}) # init moment transforms t_dyn = GaussianProcessTransform(dyn.dim_in, kern_par_dyn, 'rbf-student', 'fs', point_hyp_dyn) t_obs = GaussianProcessTransform(obs.dim_in, kern_par_obs, 'rbf-student', 'fs', point_hyp_obs) super(GPQStudent, self).__init__(dyn, obs, t_dyn, t_obs, dof, fixed_dof)
def test_integral_variance(self): dim = 2 ker_par = np.array([[1, 3, 3]], dtype=np.float) tf = GaussianProcessTransform(dim, 1, ker_par, point_str='sr') ivar0 = tf.model.integral_variance([1, 600, 6]) ivar1 = tf.model.integral_variance([1.1, 600, 6]) # expected model variance must be positive even for numerically unpleasant settings self.assertTrue(np.alltrue(np.array([ivar0, ivar1]) >= 0))
def test_expected_model_variance(self): dim = 2 ker_par = np.array([[1, 3, 3]], dtype=np.float) tf = GaussianProcessTransform(dim, 1, ker_par, point_str='sr') emv0 = tf.model.exp_model_variance(ker_par) emv1 = tf.model.exp_model_variance(ker_par) # expected model variance must be positive even for numerically unpleasant settings self.assertTrue(np.alltrue(np.array([emv0, emv1]) >= 0))
def test_weights_rbf(self): dim = 1 khyp = np.array([[1, 3]], dtype=np.float) phyp = {'kappa': 0.0, 'alpha': 1.0} tf = GaussianProcessTransform(dim, 1, khyp, point_par=phyp) wm, wc, wcc = tf.wm, tf.Wc, tf.Wcc print('wm = \n{}\nwc = \n{}\nwcc = \n{}'.format(wm, wc, wcc)) self.assertTrue(np.allclose(wc, wc.T), "Covariance weight matrix not symmetric.") # print 'GP model variance: {}'.format(tf.model.exp_model_variance()) dim = 2 khyp = np.array([[1, 3, 3]], dtype=np.float) phyp = {'kappa': 0.0, 'alpha': 1.0} tf = GaussianProcessTransform(dim, 1, khyp, point_par=phyp) wm, wc, wcc = tf.wm, tf.Wc, tf.Wcc print('wm = \n{}\nwc = \n{}\nwcc = \n{}'.format(wm, wc, wcc)) self.assertTrue(np.allclose(wc, wc.T), "Covariance weight matrix not symmetric.")
def test_apply(self): for mod in self.models: f = mod.dyn_eval dim = mod.dim_in ker_par = np.hstack((np.ones((1, 1)), 3 * np.ones((1, dim)))) tf = GaussianProcessTransform(dim, dim, ker_par) mean, cov = np.zeros(dim, ), np.eye(dim) tmean, tcov, tccov = tf.apply(f, mean, cov, np.atleast_1d(1.0)) print("Transformed moments\nmean: {}\ncov: {}\nccov: {}".format( tmean, tcov, tccov)) self.assertTrue(tf.I_out.shape == (dim, dim)) # test positive definiteness try: la.cholesky(tcov) except la.LinAlgError: self.fail("Output covariance not positive definite.") # test symmetry self.assertTrue(np.allclose(tcov, tcov.T), "Output covariance not closely symmetric.")
def test_einsum_dot(self): # einsum and dot give different results? dim_in, dim_out = 2, 1 ker_par_mo = np.hstack((np.ones((dim_out, 1)), 1 * np.ones((dim_out, dim_in)))) tf_mo = GaussianProcessTransform(dim_in, dim_out, ker_par_mo, point_str='sr') iK, Q = tf_mo.model.iK, tf_mo.model.Q C1 = iK.dot(Q).dot(iK) C2 = np.einsum('ab, bc, cd', iK, Q, iK) self.assertTrue(np.allclose(C1, C2), "MAX DIFF: {:.4e}".format(np.abs(C1 - C2).max()))
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 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_single_vs_multi_output(self): # results of the GPQ and GPQMO should be same if parameters properly chosen, GPQ is a special case of GPQMO m0 = np.array([6500.4, 349.14, -1.8093, -6.7967, 0.6932]) P0 = np.diag([1e-6, 1e-6, 1e-6, 1e-6, 1]) x0 = GaussRV(5, m0, P0) dyn = ReentryVehicle2DTransition(x0, GaussRV(5)) f = dyn.dyn_eval dim_in, dim_out = dyn.dim_in, dyn.dim_state # input mean and covariance mean_in, cov_in = m0, P0 # single-output GPQ ker_par_so = np.hstack((np.ones((1, 1)), 25 * np.ones((1, dim_in)))) tf_so = GaussianProcessTransform(dim_in, dim_out, ker_par_so) # multi-output GPQ ker_par_mo = np.hstack((np.ones((dim_out, 1)), 25 * np.ones( (dim_out, dim_in)))) tf_mo = MultiOutputGaussianProcessTransform(dim_in, dim_out, ker_par_mo) # transformed moments # FIXME: transformed covariances different mean_so, cov_so, ccov_so = tf_so.apply(f, mean_in, cov_in, np.atleast_1d(0)) mean_mo, cov_mo, ccov_mo = tf_mo.apply(f, mean_in, cov_in, np.atleast_1d(0)) print('mean delta: {}'.format(np.abs(mean_so - mean_mo).max())) print('cov delta: {}'.format(np.abs(cov_so - cov_mo).max())) print('ccov delta: {}'.format(np.abs(ccov_so - ccov_mo).max())) # results of GPQ and GPQMO should be the same self.assertTrue(np.array_equal(mean_so, mean_mo)) self.assertTrue(np.array_equal(cov_so, cov_mo)) self.assertTrue(np.array_equal(ccov_so, ccov_mo))
def gpq_sos_demo(): """Sum of squares analytical moments compared with GPQ, GPQ+D and Spherical Radial transforms.""" # input dimensions dims = [1, 5, 10, 25] sos_data = np.zeros((6, len(dims))) ivar_data = np.zeros((3, len(dims))) ivar_data[0, :] = dims for di, d in enumerate(dims): # input mean and covariance mean_in, cov_in = np.zeros(d), np.eye(d) # 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 = { 'gpq': np.array([[1.0] + d * [10.0]]), 'gpqd': np.array([[1.0] + d * [10.0]]), } transforms = ( SphericalRadialTransform(d), GaussianProcessTransform(d, 1, kern_par=hyp['gpq'], point_str='sr'), GaussianProcessDerTransform(d, 1, kern_par=hyp['gpqd'], point_str='sr', which_der=dmask), ) ivar_data[1, di] = transforms[1].model.integral_var ivar_data[2, di] = transforms[2].model.integral_var mean_true, cov_true = d, 2 * d # print "{:<15}:\t {:.4f} \t{:.4f}".format("True moments", mean_true, cov_true) for ti, t in enumerate(transforms): m, c, cc = t.apply(sos, mean_in, cov_in, None) sos_data[ti, di] = np.asscalar(m) sos_data[ti + len(transforms), di] = np.asscalar(c) # print "{:<15}:\t {:.4f} \t{:.4f}".format(t.__class__.__name__, np.asscalar(m), np.asscalar(c)) row_labels = [t.__class__.__name__ for t in transforms] col_labels = [str(d) for d in dims] sos_table = pd.DataFrame(sos_data, index=row_labels * 2, columns=col_labels) ivar_table = pd.DataFrame(ivar_data[1:, :], index=['GPQ', 'GPQ+D'], columns=col_labels) return sos_table, ivar_table, ivar_data
def taylor_gpqd_demo(f): """Compares performance of GPQ+D-RBF transform w/ finite lengthscale and Linear transform.""" d = 2 # dimension ker_par_gpqd_taylor = np.array([[1.0, 1.0]]) # alpha = 1.0, ell_1 = 1.0 ker_par_gpq = np.array([[1.0] + d * [1.0]]) # function to test on f = toa # sum_of_squares transforms = ( LinearizationTransform(d), TaylorGPQDTransform(d, ker_par_gpqd_taylor), GaussianProcessTransform(d, 1, point_str='ut', kern_par=ker_par_gpq), GaussianProcessDerTransform(d, point_str='ut', kern_par=ker_par_gpq), UnscentedTransform(d, kappa=0.0), # MonteCarlo(d, n=int(1e4)), ) mean = np.array([3, 0]) cov = np.array([[1, 0], [0, 10]]) for ti, t in enumerate(transforms): mean_f, cov_f, cc = t.apply(f, mean, cov, None) print("{}: mean: {}, cov: {}").format(t.__class__.__name__, mean_f, cov_f)
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 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