Пример #1
0
    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()
Пример #2
0
    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
Пример #3
0
    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."
            )
Пример #4
0
    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."
            )
Пример #5
0
    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()
Пример #6
0
    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]
Пример #7
0
    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)
Пример #8
0
    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
Пример #9
0
    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