def sample_carma(p, q): """ Randomly generate a stationary CARMA model given the orders (p and q). Args: p (int): The p order of a CARMA(p, q) model. q (int): The q order of a CARMA(p, q) model. Returns: AR and MA coefficients in two separate arrays. """ init_log_fcoeffs = carma_log_fcoeff_init(p, q) logAR, logMA = CARMA_term.fcoeffs2carma_log(init_log_fcoeffs, p) return np.exp(logAR), np.exp(logMA)
def pred_lc(t, y, yerr, params, p, t_pred, return_var=True): """ Generate predicted values at particular time stamps given the initial time series and a best-fit model. Args: t (array(float)): Time stamps of the initial time series. y (array(float)): y values (i.e., flux) of the initial time series. yerr (array(float)): Measurement errors of the initial time series. params (array(float)): Best-fit CARMA parameters p (int): The AR order (p) of the given best-fit model. t_pred (array(float)): Time stamps to generate predicted time series. return_var (bool, optional): Whether to return uncertainties in the mean prediction. Defaults to True. Returns: (array(float), array(float), array(float)): t_pred, mean prediction at t_pred and uncertainties (variance) of the mean prediction. """ assert p >= len( params) - p, "The dimension of AR must be greater than that of MA" # get ar, ma ar = params[:p] ma = params[p:] # reposition lc y_aln = y - np.median(y) # init kernel, gp and compute matrix kernel = CARMA_term(np.log(ar), np.log(ma)) gp = celerite.GP(kernel, mean=0) gp.compute(t, yerr) try: mu, var = gp.predict(y_aln, t_pred, return_var=return_var) except FloatingPointError as e: print(e) print("No (super small) variance will be returned") return_var = False mu, var = gp.predict(y_aln, t_pred, return_var=return_var) return t_pred, mu + np.median(y), var
def carma_fit( t, y, yerr, p, q, init_func=None, neg_lp_func=None, optimizer_func=None, n_opt=20, user_bounds=None, scipy_opt_kwargs={}, scipy_opt_options={}, debug=False, ): """ Fit an arbitrary CARMA model The default settings are optimized for normalized LCs. Args: t (array(float)): Time stamps of the input time series (the default unit is day). y (array(float)): y values of the input time series. yerr (array(float)): Measurement errors for y values. p (int): The p order of a CARMA(p, q) model. q (int): The q order of a CARMA(p, q) model. init_func (object, optional): A user-provided function to generate initial guesses for the optimizer. Defaults to None. neg_lp_func (object, optional): A user-provided function to compute negative probability given an array of parameters, an array of time series values and a celerite GP instance. Defaults to None. optimizer_func (object, optional): A user-provided optimizer function. Defaults to None. n_opt (int, optional): Number of optimizers to run. Defaults to 20. user_bounds (array(float), optional): Parameter boundaries for the default optimizer. If p > 2, these are boundaries for the coefficients of the factored polynomial. Defaults to None. scipy_opt_kwargs (dict, optional): Keyword arguments for scipy.optimize.minimize. Defaults to {}. scipy_opt_options (dict, optional): "options" argument for scipy.optimize.minimize. Defaults to {}. debug (bool, optional): Turn on/off debug mode. Defaults to False. Raises: celerite.solver.LinAlgError: For non-positive definite autocovariance matrices. Returns: array(float): Best-fit parameters """ # set core config dim = int(p + q + 1) mode = "fcoeff" if p > 2 else "param" # init bounds for fitting if user_bounds is not None and (len(user_bounds) == dim): bounds = user_bounds else: bounds = [(-15, 15)] * dim bounds[p:-1] = [(a[0] - 5, a[1] - 5) for a in bounds[p:-1]] bounds[-1] = (-15, 5) # re-position lc t = t - t[0] y = y - np.median(y) y_std = mad(y) * 1.4826 y = y / y_std yerr = yerr / y_std # initialize parameter and kernel ARpars, MApars = sample_carma(p, q) kernel = CARMA_term(np.log(ARpars), np.log(MApars)) gp = GP(kernel, mean=0) gp.compute(t, yerr) # determine/set init func if init_func is not None: init = init_func else: init = partial(carma_log_fcoeff_init, p, q) # determine/set negative log probability function if neg_lp_func is None: neg_lp = partial(neg_lp_flat, bounds=np.array(bounds), mode=mode) else: neg_lp = neg_lp_func # determine/set optimizer function if optimizer_func is None: scipy_opt_kwargs.update({"method": "L-BFGS-B", "bounds": bounds}) opt = partial( scipy_opt, mode=mode, opt_kwargs=scipy_opt_kwargs, opt_options=scipy_opt_options, debug=debug, ) else: opt = optimizer_func # get best-fit solution & adjust MA params (multiply by y_std) best_fit_return = opt(y, gp, init, neg_lp, n_opt) best_fit_return[p:] = best_fit_return[p:] * y_std return best_fit_return
def scipy_opt( y, gp, init_func, neg_lp_func, n_opt, mode="fcoeff", debug=False, opt_kwargs={}, opt_options={}, ): """ A wrapper for scipy.optimize.minimize method. Args: y (array(float)): y values of the input time series. gp (object): celerite GP object with a proper CARMA kernel. init_func (object): A user-provided function to generate initial guesses for the optimizer. Defaults to None. neg_lp_func (object): A user-provided function to compute negative probability given an array of parameters, an array of time series values and a celerite GP instance. Defaults to None. n_opt (int): Number of iterations to run the optimizer. mode (str, optional): The parameter space in which to make proposals, this should be determined in the "_fit" functions based on the value of the p order. Defaults to "fcoeff". debug (bool, optional): Turn on/off debug mode. Defaults to False. opt_kwargs (dict, optional): Keyword arguments for scipy.optimize.minimize. Defaults to {}. opt_options (dict, optional): "options" argument for scipy.optimize.minimize. Defaults to {}. Returns: Best-fit parameters if "debug" is False, an array of scipy.optimize.OptimizeResult objects otherwise. """ initial_params = init_func(size=n_opt) dim = gp.kernel.p + gp.kernel.q + 1 rs = [] for i in range(n_opt): r = minimize( neg_lp_func, initial_params[i], args=(y, gp), **opt_kwargs, options=opt_options, ) rs.append(r) if debug: return rs else: good_rs = [r for r in rs if r.success and r.fun != -np.inf] if len(good_rs) == 0: return [np.nan] * dim else: lls = [-r.fun for r in good_rs] log_sols = [r.x for r in good_rs] best_sol = log_sols[np.argmax(lls)] if mode == "fcoeff": return np.concatenate(CARMA_term.fcoeffs2carma(best_sol, gp.kernel.p)) else: return np.exp(best_sol)
def carma_fit(t, y, yerr, p, q, de=True, debug=False, mode="coeff", user_bounds=None, n_iter=10): """Fit time series to any CARMA model. Args: t (object): An array of time stamps in days. y (object): An array of y values. yerr (object): An array of the errors in y values. p (int): P order of a CARMA(p, q) model. q (int): Q order of a CARMA(p, q) model. de (bool, optional): Whether to use differential_evolution as the optimizer. Defaults to True. debug (bool, optional): Turn on/off debug mode. Defaults to False. mode (str, optional): Specify which space to sample, 'param' or 'coeff'. Defaults to 'coeff'. user_bounds (list, optional): Factorized polynomial coefficient boundaries for the optimizer. Defaults to None. n_iter (int, optional): Number of iterations to run the optimizer if de==False. Defaults to 10. Raises: celerite.solver.LinAlgError: For non-positive definite matrices. Returns: object: An array of best-fit CARMA parameters """ dim = int(p + q + 1) best_fit = np.empty(dim) # init bounds for fitting if user_bounds is not None and (len(user_bounds) == dim): bounds = user_bounds elif p == 2 and q == 1: bounds = [(-10, 13), (-14, 7), (-10, 6), (-12, 3)] elif p == 2 and q == 0: bounds = [(-10, 16), (-14, 16), (-13, 15)] else: ARbounds = [(-6, 3)] * p MAbounds = [(-6, 1)] * (q + 1) bounds = ARbounds + MAbounds # re-position lc t = t - t[0] y = y - np.median(y) # initialize parameter and kernel ARpars, MApars = sample_carma(p, q) kernel = CARMA_term(np.log(ARpars), np.log(MApars)) gp = GP(kernel, mean=np.median(y)) gp.compute(t, yerr) if p > 2: mode = "coeff" if mode == "coeff": init_func = lambda: carma_log_fcoeff_init(dim) else: init_func = lambda: carma_log_param_init(dim) if de: best_fit_return = _de_opt( y, best_fit, gp, init_func, mode, debug, bounds, ) else: best_fit_return = _min_opt( y, best_fit, gp, init_func, mode, debug, bounds, n_iter, ) return best_fit_return
def carma_fit( t, y, yerr, p, q, debug=False, user_bounds=None, init_ranges=None, n_iter=15, ): """ Fit time series to an arbitrary CARMA model. Args: t (array(float)): Time stamps of the input time series (the default unit is day). y (array(float)): y values of the input time series. yerr (array(float)): Measurement errors for y values. p (int): The p order of a CARMA(p, q) model. q (int): The q order of a CARMA(p, q) model. debug (bool, optional): Turn on/off debug mode. Defaults to False. user_bounds (list, optional): Parameter boundaries for the optimizer. If p > 2, these are boundaries for the coefficients of the factored polynomial. Defaults to None. init_ranges (list, optional): Tuples of custom ranges to draw initial parameter proposals from. If p > 2, same as for the user_bounds. Defaults to None. n_iter (int, optional): Number of iterations to run the optimizer if de==False. Defaults to 15. Raises: celerite.solver.LinAlgError: For non-positive definite autocovariance matrices. Returns: array(float): Best-fit parameters """ dim = int(p + q + 1) best_fit = np.empty(dim) # init bounds for fitting if user_bounds is not None and (len(user_bounds) == dim): bounds = user_bounds else: bounds = [(-15, 15)] * dim # re-position lc t = t - t[0] y = y - np.median(y) # determine shift due amp too large/small shift = np.array(0) if np.std(y) < 1e-4 or np.std(y) > 1e4: shift = np.log(np.std(y)) # initialize parameter and kernel ARpars, MApars = sample_carma(p, q, shift=float(shift)) kernel = CARMA_term(np.log(ARpars), np.log(MApars)) gp = GP(kernel, mean=0) gp.compute(t, yerr) if p > 2: mode = "coeff" init_func = lambda: carma_log_fcoeff_init( p, q, ranges=init_ranges, size=n_iter, shift=float(shift)) bounds[-1] += shift else: mode = "param" init_func = lambda: carma_log_param_init( p, q, ranges=init_ranges, size=n_iter, shift=float(shift)) bounds[p:] += shift best_fit_return = _min_opt( y, best_fit, gp, init_func, mode, debug, bounds, n_iter, ) return best_fit_return