def PlotSingleCurveFit(self, curve_idx, kernel = None, center = None, disp = None, \ df = None, scale = None, nugget = None, mask = np.array([True])): # for a single order, plots the true coefficient curve and the curve fit to a GP (with # error bars) for some mask # reads in hyperparameters for the kernel, changing them if they are different from the # original if kernel == None: kernel = self.kernel if center == None: center = self.center if disp == None: disp = self.disp if df == None: df = self.df if scale == None: scale = self.scale if nugget == None: nugget = self.nugget if all(mask): mask = self.x_train_mask # interpolates between training points interpolater = gm.ConjugateGaussianProcess(kernel = kernel, center = center, disp = disp, \ df = df, scale = scale, nugget = nugget) interpolater.fit(self.X[mask], self.coeffs[mask, [curve_idx]]) pred_interp, std_interp = interpolater.predict(self.X, return_std=True) fig, ax = plt.subplots(figsize=(3.5, 3)) # Interpolating curve ax.plot(self.x, self.coeffs[:, [curve_idx]], c=self.colors[curve_idx], label=r'$c_{}$ ($\sigma_n = 0$)'.format(curve_idx), zorder=0) ax.plot(self.x, pred_interp, c=self.colors[curve_idx], ls='--', zorder=0) ax.plot(self.x[mask], self.coeffs[mask, curve_idx], ls = '', marker = 'o', \ c = self.colors[curve_idx], markersize = 7, zorder = 0) ax.fill_between(self.x, pred_interp - 2 * std_interp, pred_interp + 2 * std_interp, \ facecolor = self.light_colors[curve_idx], edgecolor = self.colors[curve_idx], lw = edgewidth, zorder = 0) # Format plot ax.set_xticks([0, 0.5, 1]) ax.set_xticks([0.25, 0.75], minor=True) ax.set_xticklabels([0, 0.5, 1]) ax.set_xlabel(r'$x$') ax.legend() fig.tight_layout()
def log_marginal_likelihood(self, **kwargs): included = self.included orders = self.orders ord_vals = self.ord_vals ref = self.ref train = self.train breakdown = self.breakdown X = self.X mass = self.mass if self.exp_param == 'sum' or self.exp_param == 'halfsum': Q = expansion_parameter_cm(X, breakdown, mass=mass, **kwargs) elif self.exp_param == 'sumsq': Q = expansion_parameter_cm(X, breakdown, mass=mass, **kwargs)**2 elif self.exp_param == 'phillips': Q = expansion_parameter_phillips(breakdown, **kwargs) Q = np.broadcast_to(Q, X.shape[0]) else: raise ValueError() coeffs = coefficients(self.y, Q, self.ref, ord_vals) coeffs = coeffs[self.train][:, included] gp = gm.ConjugateGaussianProcess(**self.kwargs) gp.fit(self.X_train, coeffs) # K = self.compute_conditional_cov(self.X_train, gp) # alpha = np.linalg.solve(K, coeffs) # coeff_log_like = -0.5 * np.einsum('ik,ik->k', coeffs, alpha) - \ # 0.5 * np.linalg.slogdet(2 * np.pi * K)[-1] # coeff_log_like = coeff_log_like.sum() coeff_log_like = gp.log_marginal_likelihood_value_ orders_in = orders[included] n = len(orders_in) try: ref_train = ref[train] except TypeError: ref_train = ref det_factor = np.sum(n * np.log(np.abs(ref_train)) + np.sum(ord_vals[train][:, included], axis=1) * np.log(np.abs(Q[train]))) y_log_like = coeff_log_like - det_factor return y_log_like
def PlotPC(self): # plots the pivoted Cholesky decomposition in one of two ways try: # kernel for GP fit self.kernel_fit = RBF(length_scale = self.ls) + \ WhiteKernel(noise_level = self.nugget, noise_level_bounds = 'fixed') # fits GP and extracts error bars self.gp_diagnostic = gm.ConjugateGaussianProcess(kernel = self.kernel_fit, center = self.center, \ disp = self.disp, df = self.df, scale = self.scale, n_restarts_optimizer = 2, \ random_state = 32) self.gp_diagnostic.fit(self.X[self.x_train_mask], self.coeffs[self.x_train_mask]) self.pred, self.std = self.gp_diagnostic.predict(self.X, return_std=True) self.underlying_std = np.sqrt(self.gp_diagnostic.cov_factor_) # extracts underlying covariance matrix and calculates the diagnostics self.mean_underlying = self.gp_diagnostic.mean( self.X[self.x_valid_mask]) self.cov_underlying = self.gp_diagnostic.cov( self.X[self.x_valid_mask]) self.gdgn = gm.GraphicalDiagnostic(self.coeffs[self.x_valid_mask], \ self.mean_underlying, self.cov_underlying, colors = self.colors, gray = gray, black = softblack) # plots the pivoted Cholesky decomposition with plt.rc_context({ "text.usetex": True, "text.latex.preview": True }): # with plt.rc_context({"text.usetex": True}): fig, ax = plt.subplots(figsize=(3.2, 3.2)) self.gdgn.pivoted_cholesky_errors(ax=ax, title=None) ax.set_xticks([2, 4, 6, 8, 10, 12]) ax.set_xticks([1, 3, 5, 7, 9, 11], minor=True) ax.set_yticks([-2, -1, 0, 1, 2]) ax.text(0.04, 0.967, r'$\mathrm{D}_{\mathrm{PC}}$', bbox = text_bbox, \ transform = ax.transAxes, va = 'top', ha = 'left') fig.tight_layout() plt.show() except: print( "The pivoted Cholesky decomposition could not be calculated at one or more orders." )
def PlotMD(self, plot_type='box'): # plots the Mahalanobis distance in one of two ways (box-and-whisker or histogram) try: # kernel for GP fit self.kernel_fit = RBF(length_scale = self.ls) + \ WhiteKernel(noise_level = self.nugget, noise_level_bounds = 'fixed') # fits GP and extracts error bars self.gp_diagnostic = gm.ConjugateGaussianProcess(kernel = self.kernel_fit, \ center = self.center, disp = self.disp, df = self.df, scale = self.scale, \ n_restarts_optimizer = 2, random_state = 32) self.gp_diagnostic.fit(self.X[self.x_train_mask], self.coeffs[self.x_train_mask]) self.pred, self.std = self.gp_diagnostic.predict(self.X, return_std=True) self.underlying_std = np.sqrt(self.gp_diagnostic.cov_factor_) # extracts underlying covariance matrix and calculates the diagnostics self.mean_underlying = self.gp_diagnostic.mean( self.X[self.x_valid_mask]) self.cov_underlying = self.gp_diagnostic.cov( self.X[self.x_valid_mask]) self.gdgn = gm.GraphicalDiagnostic(self.coeffs[self.x_valid_mask], \ self.mean_underlying, self.cov_underlying, colors = self.colors, gray = gray, black = softblack) # plots the Mahalanobis distance if plot_type == 'box': fig, ax = plt.subplots(figsize=(1.5, 3.0)) ax = self.gdgn.md_squared(type = plot_type, trim = False, title = None, \ xlabel = r'$\mathrm{D}_{\mathrm{MD}}^2$') elif plot_type == 'hist': fig, ax = plt.subplots(figsize=(9, 3.2)) ax = self.gdgn.md_squared(type = plot_type, title = None, \ xlabel = r'$\mathrm{D}_{\mathrm{MD}}^2$') ax.set_ylim(0, 25) else: return 0 offset_xlabel(ax) # fig.tight_layout() except: print( "The Mahalanobis distance could not be calculated at one or more orders." )
def PlotGPCurvesFit(self): # fits the coefficient curves for the orders we're interested in to a GP at training # points, and then plots (with error bars) alongside true curves at each order # kernel for fit self.kernel_fit = RBF(length_scale = self.ls) + \ WhiteKernel(noise_level = self.nugget, noise_level_bounds = 'fixed') # fits to GP and extracts error bars self.gp_diagnostic = gm.ConjugateGaussianProcess(kernel = self.kernel_fit, center = self.center, \ disp = self.disp, df = self.df, scale = self.scale, n_restarts_optimizer = 2, \ random_state = 32) self.gp_diagnostic.fit(self.X[self.x_train_mask], self.coeffs[self.x_train_mask]) self.pred, self.std = self.gp_diagnostic.predict(self.X, return_std=True) self.underlying_std = np.sqrt(self.gp_diagnostic.cov_factor_) fig, ax = plt.subplots(figsize=(3.2, 3.2)) for i, n in enumerate(self.orders_array): # plots true and predicted coefficient curves, mask points, and error bars ax.plot(self.x, self.pred[:, i], c=self.colors[i], zorder=i - 5, ls='--') ax.plot(self.x, self.coeffs[:, i], c=self.colors[i], zorder=i - 5) ax.plot(self.x[self.x_train_mask], self.coeffs[self.x_train_mask, i], \ c = self.colors[i], zorder = i-5, ls = '', marker = 'o', \ label = r'$c_{}$'.format(n)) ax.fill_between(self.x, self.pred[:, i] + 2 * self.std, self.pred[:, i] - 2 * self.std, \ zorder = i-5, facecolor = self.light_colors[i], edgecolor = self.colors[i], \ lw = edgewidth, alpha = 1) ax.axhline(2 * self.underlying_std, 0, 1, c=gray, zorder=-10, lw=1) ax.axhline(-2 * self.underlying_std, 0, 1, c=gray, zorder=-10, lw=1) ax.axhline(0, 0, 1, c=softblack, zorder=-10, lw=1) ax.set_xticks(self.x[self.x_valid_mask], minor=True) ax.set_xlabel(r'$x$') ax.set_xticks([0, 0.25, 0.5, 0.75, 1]) ax.set_xticklabels([0, 0.25, 0.5, 0.75, 1]) ax.tick_params(which='minor', bottom=True, top=False) ax.legend(ncol=2, borderaxespad=0.5, borderpad=0.4) fig.tight_layout()
def change_order(self, change_order_array, change_ratio_array, change_ls_array, \ change_sd_array, seed_array): # can create coefficient curves for some order(s) with a different correlation length, # ratio, variance, etc., from the GP from which all other orders were calculated # reads the information about the changed orders to the class for ease of access self.change_order_array = change_order_array self.change_ratio_array = change_ratio_array self.change_ls_array = change_ls_array self.change_sd_array = change_sd_array self.seed_array = seed_array coeffs_all = self.coeffs_all # calculates the new curve(s) for some seed(s) and swaps them into the array of # coefficients for i, order in enumerate(change_order_array): kernel_bad = RBF(length_scale=change_ls_array[i], length_scale_bounds='fixed') + \ WhiteKernel(noise_level=self.nugget, noise_level_bounds='fixed') gp_bad = gm.ConjugateGaussianProcess(kernel=kernel_bad, center=self.center, \ df=np.inf, scale=change_sd_array[i], nugget=0) coeffs_bad = -gp_bad.sample_y( self.X, n_samples=1, random_state=seed_array[i]) coeffs_all[:, order] = coeffs_bad[:, 0] self.colors[order] = np.array([0, 0, 0]) self.light_colors[order] = np.array( [128 / 255., 128 / 255., 128 / 255.]) # with new coefficients, calculates the data, differences, etc., for all orders self.coeffs_all = coeffs_all self.data_all = gm.partials(self.coeffs_all, self.ratio, ref=self.ref, \ orders=self.orders_all_array) self.diffs_all = np.array( [self.data_all[:, 0], *np.diff(self.data_all, axis=1).T]).T # Get the "all-orders" curve self.data_true = self.data_all[:, -1] self.coeffs = self.coeffs_all[:, :self.n_orders] self.data = self.data_all[:, :self.n_orders] self.diffs = self.diffs_all[:, :self.n_orders]
def __init__(self, gphyperparameters, orderinfo, x, fullyrandomcolors = False, \ color_seed = None, constrained = False, x_power = 1): """ Class for everything involving Jordan Melendez's GSUM library. gphyperparameters (GPHyperparameters) : parameters for fitted Gaussian process orderinfo (order_info) : information on the calculated and plotted orders x (float array) : x-coordinate mesh over which the GP is calculated, plotted, and fitted fullyrandomcolors (boolean) : are all the colors randomly generated? color_seed : value of the seed from which the colors are randomly generated constrained (bool) : is the GP fitting process constrained? x_power : power by which the x-coordinate is scaled (to test stationarity) """ # reads the hyperparameters to the class self.hyp = gphyperparameters self.ls = self.hyp.ls self.sd = self.hyp.sd self.center = self.hyp.center self.ref = self.hyp.ref self.ratio = self.hyp.ratio self.nugget = self.hyp.nugget self.seed = self.hyp.seed self.df = self.hyp.df self.disp = self.hyp.disp self.scale = self.hyp.scale # creates a kernel that defines the Gaussian process (GP) self.kernel = RBF(length_scale = self.ls, length_scale_bounds = 'fixed') + \ WhiteKernel(noise_level = self.nugget, noise_level_bounds= 'fixed') # reads the order information to the class self.orderinfo = orderinfo self.n_orders = self.orderinfo.n_orders self.n_final_order = self.orderinfo.n_final_order self.orders_array = self.orderinfo.orders_array self.orders_all_array = self.orderinfo.orders_all_array # reads whether the colors will be fully randomly chosen or not and what seed will be # used to generate the random ones self.fullyrandomcolors = fullyrandomcolors self.color_seed = color_seed self.color_randomstate = np.random.RandomState(self.color_seed) if self.fullyrandomcolors: # creates an array of random colors self.colors = [] for i in range(0, self.n_final_order + 1): self.colors.append(self.color_randomstate.rand(3, )) self.light_colors = [ scale_lightness(color[:3], 1.5) for color in self.colors ] else: # sets the arrays for the colors and the light colors, keeping Jordan Melendez's # scheme for the first five orders and randomizing the colors for higher orders cmaps = [ plt.get_cmap(name) for name in ['Oranges', 'Greens', 'Blues', 'Reds', 'Purples'] ] self.colors = [ cmap(0.55 - 0.1 * (i == 0)) for i, cmap in enumerate(cmaps) ] for i in range(len(self.colors), self.n_final_order + 1): self.colors.append(self.color_randomstate.rand(3, )) self.light_colors = [ scale_lightness(color[:3], 1.5) for color in self.colors ] # takes in the array of x-values over which the kernel generates the toy curves self.x_underlying = x self.X_underlying = self.x_underlying[:, None] # scales the x-axis by some factor, with the resulting x and X arrays being used for all # plotting and fitting if x_power == 1: self.x = self.x_underlying self.X = self.X_underlying else: self.x = (self.x_underlying)**(x_power) self.X = self.x[:, None] # is the GP constrained? The default answer is No self.constrained = constrained # creates the masks for training and testing the GPs self.x_train_mask, self.x_valid_mask = regular_train_test_split(self.x_underlying, \ dx_train = 24, dx_test = 6, offset_train = 1, offset_test = 1) # creates the masks for training and testing the GPs, taking into account any scaling # power for the x-coordinate self.x_train_mask, self.x_valid_mask = \ mask_mapper(self.x_underlying, self.x, self.x_train_mask), \ mask_mapper(self.x_underlying, self.x, self.x_valid_mask) if not constrained: # for the given hyperparameters, orders, and x-variable, generates the data to all # orders and extracts the coefficient curves at all orders self.gp = gm.ConjugateGaussianProcess(kernel = self.kernel, center = self.center, \ df = np.inf, scale = self.sd, nugget = 0) self.coeffs_all = - self.gp.sample_y(self.X_underlying, \ n_samples = self.n_final_order + 1, \ random_state = self.seed) self.data_all = gm.partials(self.coeffs_all, self.ratio, ref = self.ref, \ orders = self.orders_all_array) self.diffs_all = np.array( [self.data_all[:, 0], *np.diff(self.data_all, axis=1).T]).T self.data_true = self.data_all[:, -1] self.coeffs = self.coeffs_all[:, :self.n_orders] self.data = self.data_all[:, :self.n_orders] self.diffs = self.diffs_all[:, :self.n_orders] else: # given constraints, extracts the coefficient curves at all orders self.gp_constrained = gm.ConjugateGaussianProcess(kernel = self.kernel, \ optimizer = None).fit(np.array([[0], [1]]), np.array([0, 0])) self.cn_constrained = self.gp_constrained.sample_y(self.X_underlying, \ n_samples = self.n_orders, random_state = 5) self.yn_constrained = gm.partials(self.cn_constrained, ratio=self.ratio)
def compute_conditional_cov(self, X, gp=None): if gp is None: gp = gm.ConjugateGaussianProcess(**self.kwargs) gp.fit(self.X_train, self.c_train) if self.degrees_zeros is None and self.omega_zeros is None: return gp.cov(X) [ls_omega, ls_degrees] = gp.kernel_.k1.get_params()['length_scale'] std = np.sqrt(gp.cbar_sq_mean_) w = X[:, [0]] t = X[:, [1]] import gptools kern_omega = gptools.SquaredExponentialKernel( initial_params=[1, ls_omega], fixed_params=[True, True]) kern_theta = gptools.SquaredExponentialKernel( initial_params=[1, ls_degrees], fixed_params=[True, True]) gp_omega = gptools.GaussianProcess(kern_omega) gp_theta = gptools.GaussianProcess(kern_theta) # gp_omega.add_data(np.array([[0], [0]]), np.array([0, 0]), n=np.array([0, 1])) if self.omega_zeros is not None or self.omega_deriv_zeros is not None: w_z = [] n_w = [] if self.omega_zeros is not None: w_z.append(self.omega_zeros) n_w.append(np.zeros(len(self.omega_zeros))) if self.omega_deriv_zeros is not None: w_z.append(self.omega_deriv_zeros) n_w.append(np.ones(len(self.omega_deriv_zeros))) w_z = np.concatenate(w_z)[:, None] n_w = np.concatenate(n_w) print(w_z, n_w) gp_omega.add_data(w_z, np.zeros(w_z.shape[0]), n=n_w) _, K_omega = gp_omega.predict(w, np.zeros(w.shape[0]), return_cov=True) else: K_omega = gp_omega.compute_Kij(w, w, np.zeros(w.shape[0]), np.zeros(w.shape[0])) if self.degrees_zeros is not None or self.degrees_deriv_zeros is not None: t_z = [] n_t = [] if self.degrees_zeros is not None: t_z.append(self.degrees_zeros) n_t.append(np.zeros(len(self.degrees_zeros))) if self.degrees_deriv_zeros is not None: t_z.append(self.degrees_deriv_zeros) n_t.append(np.ones(len(self.degrees_deriv_zeros))) t_z = np.concatenate(t_z)[:, None] n_t = np.concatenate(n_t) gp_theta.add_data(t_z, np.zeros(t_z.shape[0]), n=n_t) _, K_theta = gp_theta.predict(t, np.zeros(t.shape[0]), return_cov=True) else: K_theta = gp_theta.compute_Kij(t, t, np.zeros(t.shape[0]), np.zeros(t.shape[0])) # kernel_omega = RBF(ls_omega) # kernel_theta = RBF(ls_degrees) # if self.omega_zeros is not None: # # w_z = np.atleast_1d(self.omega_zeros)[:, None] # # K_omega = kernel_omega(w) - kernel_omega(w, w_z) @ np.linalg.solve(kernel_omega(w_z), kernel_omega(w_z, w)) # else: # K_omega = kernel_omega(w) # # if self.degrees_zeros is not None: # t_z = np.atleast_1d(self.degrees_zeros)[:, None] # K_theta = kernel_theta(t) - kernel_theta(t, t_z) @ np.linalg.solve(kernel_theta(t_z), kernel_theta(t_z, t)) # else: # K_theta = kernel_theta(t) return std**2 * K_omega * K_theta
def __init__(self, name, nucleon, X, y, orders, train, ref, breakdown, excluded, exp_param='sum', delta_transition=True, degrees_zeros=None, omega_zeros=None, degrees_deriv_zeros=None, omega_deriv_zeros=None, **kwargs): from compton import DesignLabels, mass_proton, mass_neutron self.name = name self.X = X self.y = y self.orders = orders self.train = train self.excluded = excluded self.ref = ref self.breakdown = breakdown self.kwargs = kwargs self.degrees_zeros = degrees_zeros self.omega_zeros = omega_zeros self.degrees_deriv_zeros = degrees_deriv_zeros self.omega_deriv_zeros = omega_deriv_zeros # from gsum import cartesian # self.X_zeros = cartesian(self.omega_zeros, self.degrees_zeros) self.exp_param = exp_param self.exp_param_func = self.exp_param_funcs[exp_param] if nucleon == DesignLabels.proton: mass = mass_proton elif nucleon == DesignLabels.neutron: mass = mass_neutron else: raise ValueError( 'nucleon must be DesignLabels.proton or DesignLabels.neutron') self.nucleon = nucleon self.mass = mass included = ~np.isin(orders, excluded) self.included = included if delta_transition: from compton.constants import order_map # order_map = {0: 0, 2: 1, 3: 2, 4: 3} # ord_vals = np.array([order_transition(order, order_map[order], X[:, 0]) for order in orders]).T ord_vals = np.array([ order_transition_lower_orders(order, X[:, 0]) for order in orders ]).T else: ord_vals = np.array( [np.broadcast_to(order, X.shape[0]) for order in orders]).T self.ord_vals = ord_vals if exp_param == 'sum': Q = expansion_parameter_cm(X, breakdown, mass=mass) elif exp_param == 'halfsum': Q = expansion_parameter_cm(X, breakdown, mass=mass, factor=0.5) elif exp_param == 'sumsq': Q = expansion_parameter_cm(X, breakdown, mass=mass)**2 elif exp_param == 'phillips': Q = np.broadcast_to(expansion_parameter_phillips(breakdown), X.shape[0]) else: raise ValueError('') self.Q = Q self.c = c = coefficients(y, Q, ref, ord_vals) self.c_included = c[:, included] self.X_train = self.X[train] self.y_train = self.y[train][:, included] self.c_train = c[train][:, included] gp = gm.ConjugateGaussianProcess(**kwargs) # print(self.c_train.shape) gp.fit(self.X_train, self.c_train) print('Fit kernel:', gp.kernel_) self.cbar = np.sqrt(gp.cbar_sq_mean_) print('cbar mean:', self.cbar) self.gp = gp