• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

California-Planet-Search / radvel / 17447393926

03 Sep 2025 10:11PM UTC coverage: 87.165%. First build
17447393926

Pull #395

github

bjfultn
Fix Python 3.11 compatibility: replace old Python 2 raise syntax and bare except clauses
Pull Request #395: testing new ci

170 of 241 new or added lines in 8 files covered. (70.54%)

3735 of 4285 relevant lines covered (87.16%)

0.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.91
/radvel/likelihood.py
1
import numpy as np
1✔
2
import radvel.model
1✔
3
from radvel import gp
1✔
4
from scipy.linalg import cho_factor, cho_solve
1✔
5
import warnings
1✔
6

7

8
_has_celerite = gp._try_celerite()
1✔
9
if _has_celerite:
1✔
10
    import celerite
1✔
11

12

13
def custom_formatwarning(msg, *args, **kwargs):
1✔
14
    # ignore everything except the message
15
    return str(msg) + '\n'
×
16

17

18
warnings.formatwarning = custom_formatwarning
1✔
19

20

21
class Likelihood(object):
1✔
22
    """
23
    Generic Likelihood
24
    """
25
    def __init__(self, model, x, y, yerr, extra_params=[], decorr_params=[],
1✔
26
                 decorr_vectors=[]):
27
        self.model = model
1✔
28
        self.vector = model.vector
1✔
29
        self.params = model.params
1✔
30
        self.x = np.array(x)  # Variables must be arrays.
1✔
31
        self.y = np.array(y)  # Pandas data structures lead to problems.
1✔
32
        self.yerr = np.array(yerr)
1✔
33
        self.dvec = [np.array(d) for d in decorr_vectors]
1✔
34
        n = self.vector.vector.shape[0]
1✔
35
        for key in extra_params:
1✔
36
            if key not in self.params.keys():
1✔
37
                self.params[key] = radvel.model.Parameter(value=0.0)
1✔
38
            if key not in self.vector.indices:
1✔
39
                self.vector.indices.update({key:n})
1✔
40
                n += 1
1✔
41
        for key in decorr_params:
1✔
42
            if key not in self.params.keys():
×
43
                self.params[key] = radvel.model.Parameter(value=0.0)
×
44
            if key not in self.vector.indices:
×
45
                self.vector.indices.update({key:n})
×
46
                n += 1
×
47
        self.uparams = None
1✔
48

49
        self.vector.dict_to_vector()
1✔
50
        self.vector.vector_names()
1✔
51

52
    def __repr__(self):
1✔
53
        s = ""
1✔
54
        if self.uparams is None:
1✔
55
            s += "{:<20s}{:>15s}{:>10s}\n".format(
1✔
56
                'parameter', 'value', 'vary'
57
                )
58
            keys = self.params.keys()
1✔
59
            for key in keys:
1✔
60
                try:
1✔
61
                    vstr = str(self.params[key].vary)
1✔
62
                    if (key.startswith('tc') or key.startswith('tp')) and self.params[key].value > 1e6:
1✔
63
                        par = self.params[key].value - self.model.time_base
1✔
64
                    else:
65
                        par = self.params[key].value
1✔
66

67
                    s += "{:20s}{:15g} {:>10s}\n".format(
1✔
68
                        key, par, vstr
69
                        )
70
                except TypeError:
×
71
                    pass
×
72

73
            try:
1✔
74
                synthbasis = self.params.basis.to_synth(self.params, noVary=True)
1✔
75
                for key in synthbasis.keys():
1✔
76
                    if key not in keys:
1✔
77
                        try:
1✔
78
                            vstr = str(synthbasis[key].vary)
1✔
79
                            if (key.startswith('tc') or key.startswith('tp')) and synthbasis[key].value > 1e6:
1✔
80
                                par = synthbasis[key].value - self.model.time_base
1✔
81
                            else:
82
                                par = synthbasis[key].value
1✔
83

84
                            s += "{:20s}{:15g} {:>10s}\n".format(
1✔
85
                                key, par, vstr
86
                                )
87
                        except TypeError:
×
88
                            pass
×
89
            except TypeError:
×
90
                pass
×
91

92
        else:
93
            s = ""
1✔
94
            s += "{:<20s}{:>15s}{:>10s}{:>10s}\n".format(
1✔
95
                'parameter', 'value', '+/-', 'vary'
96
                )
97
            keys = self.params.keys()
1✔
98
            for key in keys:
1✔
99
                try:
1✔
100
                    vstr = str(self.params[key].vary)
1✔
101
                    if key in self.uparams.keys():
1✔
102
                        err = self.uparams[key]
1✔
103
                    else:
104
                        err = 0
×
105
                    if (key.startswith('tc') or key.startswith('tp')) and \
1✔
106
                            self.params[key].value > 1e6:
107
                        par = self.params[key].value - self.model.time_base
1✔
108
                    else:
109
                        par = self.params[key].value
1✔
110

111
                    s += "{:20s}{:15g}{:10g}{:>10s}\n".format(
1✔
112
                        key, par, err, vstr
113
                        )
114
                except TypeError:
×
115
                    pass
×
116

117
            try:
1✔
118
                synthbasis = self.params.basis.to_synth(self.params, noVary=True)
1✔
119
                for key in synthbasis.keys():
1✔
120
                    if key not in keys:
1✔
121
                        try:
1✔
122
                            vstr = str(synthbasis[key].vary)
1✔
123
                            if key in self.uparams.keys():
1✔
124
                                err = self.uparams[key]
1✔
125
                            else:
126
                                err = 0
×
127
                            if (key.startswith('tc') or key.startswith('tp')) and synthbasis[key].value > 1e6:
1✔
128
                                par = synthbasis[key].value - self.model.time_base
1✔
129
                            else:
130
                                par = synthbasis[key].value
1✔
131

132
                            s += "{:20s}{:15g}{:10g}{:>10s}\n".format(
1✔
133
                                key, par, err, vstr
134
                            )
135
                        except TypeError:
×
136
                            pass
×
137
            except TypeError:
×
138
                pass
×
139

140
        return s
1✔
141

142
    def set_vary_params(self, param_values_array):
1✔
143
        param_values_array = list(param_values_array)
1✔
144
        i = 0
1✔
145
        try:
1✔
146
            if len(self.vary_params) != len(param_values_array):
1✔
147
                self.list_vary_params()
×
148
        except AttributeError:
1✔
149
            self.list_vary_params()
1✔
150
        for index in self.vary_params:
1✔
151
            self.vector.vector[index][0] = param_values_array[i]
1✔
152
            i += 1
1✔
153
        assert i == len(param_values_array), \
1✔
154
            "Length of array must match number of varied parameters"
155

156
    def get_vary_params(self):
1✔
157
        try:
1✔
158
            return self.vector.vector[self.vary_params][:,0]
1✔
159
        except AttributeError:
×
160
            self.list_vary_params()
×
161
            return self.vector.vector[self.vary_params][:, 0]
×
162

163
    def list_vary_params(self):
1✔
164
        self.vary_params = np.where(self.vector.vector[:,1] == True)[0]
1✔
165

166
    def name_vary_params(self):
1✔
167
        list = []
1✔
168
        try:
1✔
169
            for i in self.vary_params:
1✔
170
                list.append(self.vector.names[i])
1✔
171
            return list
1✔
172
        except AttributeError:
×
173
            self.list_vary_params()
×
174
            for i in self.vary_params:
×
175
                list.append(self.vector.names[i])
×
176
            return list
×
177

178
    def residuals(self):
1✔
179
        return self.y - self.model(self.x)
×
180

181
    def neglogprob(self):
1✔
182
        return -1.0 * self.logprob()
×
183

184
    def neglogprob_array(self, params_array):
1✔
185
        return -self.logprob_array(params_array)
1✔
186

187
    def logprob_array(self, params_array):
1✔
188
        self.set_vary_params(params_array)
×
189
        _logprob = self.logprob()
×
190
        return _logprob
×
191

192
    def bic(self):
1✔
193
        """
194
        Calculate the Bayesian information criterion
195
        Returns:
196
            float: BIC
197
        """
198

199
        n = len(self.y)
1✔
200
        k = len(self.get_vary_params())
1✔
201
        _bic = np.log(n) * k - 2.0 * self.logprob()
1✔
202
        return _bic
1✔
203

204
    def aic(self):
1✔
205
        """
206
        Calculate the Aikike information criterion
207
        The Small Sample AIC (AICC) is returned because for most RV data sets n < 40 * k
208
        (see Burnham & Anderson 2002 S2.4).
209
        Returns:
210
            float: AICC
211
        """
212

213
        n = len(self.y)
1✔
214
        k = len(self.get_vary_params())
1✔
215
        aic = - 2.0 * self.logprob() + 2.0 * k
1✔
216
        # Small sample correction
217
        _aicc = aic
1✔
218
        denom = (n - k - 1.0)
1✔
219
        if denom > 0:
1✔
220
            _aicc += (2.0 * k * (k + 1.0)) / denom
1✔
221
        else:
222
            print("Warning: The number of free parameters is greater than or equal to")
×
223
            print("         the number of data points. The AICc comparison calculations")
×
224
            print("         will fail in this case.")
×
225
            _aicc = np.inf
×
226
        return _aicc
1✔
227

228

229
class CompositeLikelihood(Likelihood):
1✔
230
    """Composite Likelihood
231
    A thin wrapper to combine multiple `Likelihood`
232
    objects. One `Likelihood` applies to a dataset from
233
    a particular instrument.
234
    Args:
235
        like_list (list): list of `radvel.likelihood.RVLikelihood` objects
236
    """
237
    def __init__(self, like_list):
1✔
238
        self.nlike = len(like_list)
1✔
239

240
        like0 = like_list[0]
1✔
241
        params = like0.params
1✔
242
        vector = like0.vector
1✔
243
        self.model = like0.model
1✔
244
        self.x = like0.x
1✔
245
        self.y = like0.y
1✔
246
        self.yerr = like0.yerr
1✔
247
        self.telvec = like0.telvec
1✔
248
        self.extra_params = like0.extra_params
1✔
249
        self.suffixes = like0.suffix
1✔
250
        self.uparams = like0.uparams
1✔
251
        self.hnames = []
1✔
252

253
        for i in range(1, self.nlike):
1✔
254
            like = like_list[i]
1✔
255

256
            self.x = np.append(self.x, like.x)
1✔
257
            self.y = np.append(self.y, like.y - like.vector.vector[like.vector.indices[like.gamma_param]][0])
1✔
258
            self.yerr = np.append(self.yerr, like.yerr)
1✔
259
            self.telvec = np.append(self.telvec, like.telvec)
1✔
260
            self.extra_params = np.append(self.extra_params, like.extra_params)
1✔
261
            self.suffixes = np.append(self.suffixes, like.suffix)
1✔
262
            if hasattr(like, 'hnames'):
1✔
263
                self.hnames.extend(like.hnames)
1✔
264
            try:
1✔
265
                self.uparams = self.uparams.update(like.uparams)
1✔
266
            except AttributeError:
1✔
267
                self.uparams = None
1✔
268

269
            for k in like.params:
1✔
270
                if k in params:
1✔
271
                    assert like.params[k]._equals(params[k]), "Name={} {} != {}".format(k, like.params[k], params[k])
1✔
272
                else:
273
                    params[k] = like.params[k]
×
274

275
            assert like.vector is vector, \
1✔
276
                "Likelihoods must hold the same vector"
277

278
        self.extra_params = list(set(self.extra_params))
1✔
279
        self.params = params
1✔
280
        self.vector = vector
1✔
281
        self.like_list = like_list
1✔
282

283
    def logprob(self):
1✔
284
        """
285
        See `radvel.likelihood.RVLikelihood.logprob`
286
        """
287
        _logprob = 0
1✔
288
        for like in self.like_list:
1✔
289
            _logprob += like.logprob()
1✔
290
        return _logprob
1✔
291

292
    def residuals(self):
1✔
293
        """
294
        See `radvel.likelihood.RVLikelihood.residuals`
295
        """
296

297
        res = self.like_list[0].residuals()
1✔
298
        for like in self.like_list[1:]:
1✔
299
            res = np.append(res, like.residuals())
1✔
300

301
        return res
1✔
302

303
    def errorbars(self):
1✔
304
        """
305
        See `radvel.likelihood.RVLikelihood.errorbars`
306
        """
307
        err = self.like_list[0].errorbars()
1✔
308
        for like in self.like_list[1:]:
1✔
309
            err = np.append(err, like.errorbars())
1✔
310

311
        return err
1✔
312

313

314
class RVLikelihood(Likelihood):
1✔
315
    """RV Likelihood
316
    The Likelihood object for a radial velocity dataset
317
    Args:
318
        model (radvel.model.RVModel): RV model object
319
        t (array): time array
320
        vel (array): array of velocities
321
        errvel (array): array of velocity uncertainties
322
        suffix (string): suffix to identify this Likelihood object
323
           useful when constructing a `CompositeLikelihood` object.
324
    """
325
    def __init__(self, model, t, vel, errvel, suffix='', decorr_vars=[],
1✔
326
                 decorr_vectors=[], **kwargs):
327
        self.gamma_param = 'gamma'+suffix
1✔
328
        self.jit_param = 'jit'+suffix
1✔
329
        self.extra_params = [self.gamma_param, self.jit_param]
1✔
330

331
        if suffix.startswith('_'):
1✔
332
            self.suffix = suffix[1:]
1✔
333
        else:
334
            self.suffix = suffix
1✔
335

336
        self.telvec = np.array([self.suffix]*len(t))
1✔
337

338
        self.decorr_params = []
1✔
339
        self.decorr_vectors = decorr_vectors
1✔
340
        if len(decorr_vars) > 0:
1✔
341
            self.decorr_params += ['c1_'+d+suffix for d in decorr_vars]
×
342

343
        super(RVLikelihood, self).__init__(
1✔
344
            model, t, vel, errvel, extra_params=self.extra_params,
345
            decorr_params=self.decorr_params, decorr_vectors=self.decorr_vectors
346
            )
347

348
        self.gamma_index = self.vector.indices[self.gamma_param]
1✔
349
        self.jit_index = self.vector.indices[self.jit_param]
1✔
350

351
    def residuals(self):
1✔
352
        """Residuals
353
        Data minus model
354
        """
355
        mod = self.model(self.x)
1✔
356

357
        if self.vector.vector[self.gamma_index][3] and not self.vector.vector[self.gamma_index][1]:
1✔
358
            ztil = np.sum((self.y - mod)/(self.yerr**2 + self.vector.vector[self.jit_index][0]**2)) / \
1✔
359
                   np.sum(1/(self.yerr**2 + self.vector.vector[self.jit_index][0]**2))
360
            if np.isnan(ztil):
1✔
361
                 ztil = 0.0
×
362
            self.vector.vector[self.gamma_index][0] = ztil
1✔
363

364
        res = self.y - self.vector.vector[self.gamma_index][0] - mod
1✔
365

366
        if len(self.decorr_params) > 0:
1✔
367
            for parname in self.decorr_params:
×
368
                var = parname.split('_')[1]
×
369
                pars = []
×
370
                for par in self.decorr_params:
×
371
                    if var in par:
×
372
                        pars.append(self.vector.vector[self.vector.indices[par]][0])
×
373
                pars.append(0.0)
×
374
                if np.isfinite(self.decorr_vectors[var]).all():
×
375
                    vec = self.decorr_vectors[var] - np.mean(self.decorr_vectors[var])
×
376
                    p = np.poly1d(pars)
×
377
                    res -= p(vec)
×
378
        return res
1✔
379

380
    def errorbars(self):
1✔
381
        """
382
        Return uncertainties with jitter added
383
        in quadrature.
384
        Returns:
385
            array: uncertainties
386
        """
387
        return np.sqrt(self.yerr**2 + self.vector.vector[self.jit_index][0]**2)
1✔
388

389
    def logprob(self):
1✔
390
        """
391
        Return log-likelihood given the data and model.
392
        Priors are not applied here.
393
        Returns:
394
            float: Natural log of likelihood
395
        """
396

397
        sigma_jit = self.vector.vector[self.jit_index][0]
1✔
398
        residuals = self.residuals()
1✔
399
        loglike = loglike_jitter(residuals, self.yerr, sigma_jit)
1✔
400

401
        if self.vector.vector[self.gamma_index][3] \
1✔
402
                and not self.vector.vector[self.gamma_index][1]:
403
            sigz = 1/np.sum(1 / (self.yerr**2 + sigma_jit**2))
1✔
404
            loglike += np.log(np.sqrt(2 * np.pi * sigz))
1✔
405

406
        return loglike
1✔
407

408

409
class GPLikelihood(RVLikelihood):
1✔
410
    """GP Likelihood
411
    The Likelihood object for a radial velocity dataset modeled with a GP
412
    Args:
413
        model (radvel.model.GPModel): GP model object
414
        t (array): time array
415
        vel (array): array of velocities
416
        errvel (array): array of velocity uncertainties
417
        hnames (list of string): keys corresponding to radvel.Parameter
418
           objects in model.params that are GP hyperparameters
419
        suffix (string): suffix to identify this Likelihood object;
420
           useful when constructing a `CompositeLikelihood` object
421
    """
422
    def __init__(self, model, t, vel, errvel,
1✔
423
                 hnames=['gp_per', 'gp_perlength', 'gp_explength', 'gp_amp'],
424
                 suffix='', kernel_name="QuasiPer", **kwargs):
425

426
        self.suffix = suffix
1✔
427
        super(GPLikelihood, self).__init__(
1✔
428
              model, t, vel, errvel, suffix=self.suffix,
429
              decorr_vars=[], decorr_vectors={}
430
            )
431
        assert kernel_name in gp.KERNELS.keys(), \
1✔
432
            'GP Kernel not recognized: ' + kernel_name + '\n' + \
433
            'Available kernels: ' + str(gp.KERNELS.keys())
434

435
        self.hnames = hnames  # list of string names of hyperparameters
1✔
436
        self.hyperparams = {k: self.params[k] for k in self.hnames}
1✔
437

438
        self.kernel_call = getattr(gp, kernel_name + "Kernel")
1✔
439
        self.kernel = self.kernel_call(self.hyperparams)
1✔
440

441
        self.kernel.compute_distances(self.x, self.x)
1✔
442
        self.N = len(self.x)
1✔
443

444
    def update_kernel_params(self):
1✔
445
        """ Update the Kernel object with new values of the hyperparameters
446
        """
447
        for key in self.vector.indices:
1✔
448
            if key in self.hnames:
1✔
449
                hparams_key = key.split('_')
1✔
450
                hparams_key = hparams_key[0] + '_' + hparams_key[1]
1✔
451
                self.kernel.hparams[hparams_key].value = self.vector.vector[self.vector.indices[key]][0]
1✔
452

453
    def _resids(self):
1✔
454
        """Residuals for internal GP calculations
455
        Data minus orbit model. For internal use in GP calculations ONLY.
456
        """
457
        res = self.y - self.vector.vector[self.gamma_index][0] - self.model(self.x)
1✔
458
        return res
1✔
459

460
    def residuals(self):
1✔
461
        """Residuals
462
        Data minus (orbit model + predicted mean of GP noise model). For making GP plots.
463
        """
464
        mu_pred, _ = self.predict(self.x)
1✔
465
        res = self.y - self.vector.vector[self.gamma_index][0] - self.model(self.x) - mu_pred
1✔
466
        return res
1✔
467

468
    def logprob(self):
1✔
469
        """
470
        Return GP log-likelihood given the data and model.
471
        log-likelihood is computed using Cholesky decomposition as:
472
        .. math::
473
           lnL = -0.5r^TK^{-1}r - 0.5ln[det(K)] - 0.5N*ln(2pi)
474
        where r = vector of residuals (GPLikelihood._resids),
475
        K = covariance matrix, and N = number of datapoints.
476
        Priors are not applied here.
477
        Constant has been omitted.
478
        Returns:
479
            float: Natural log of likelihood
480
        """
481
        # update the Kernel object hyperparameter values
482
        self.update_kernel_params()
1✔
483

484
        r = self._resids()
1✔
485

486
        self.kernel.compute_covmatrix(self.errorbars())
1✔
487

488
        K = self.kernel.covmatrix
1✔
489

490
        # solve alpha = inverse(K)*r
491
        try:
1✔
492
            alpha = cho_solve(cho_factor(K),r)
1✔
493

494
            # compute determinant of K
495
            (s,d) = np.linalg.slogdet(K)
1✔
496

497
            # calculate likelihood
498
            like = -.5 * (np.dot(r, alpha) + d + self.N*np.log(2.*np.pi))
1✔
499

500
            return like
1✔
501

502
        except (np.linalg.linalg.LinAlgError, ValueError):
×
503
            warnings.warn("Non-positive definite kernel detected.", RuntimeWarning)
×
504
            return -np.inf
×
505

506
    def predict(self, xpred):
1✔
507
        """ Realize the GP using the current values of the hyperparameters at values x=xpred.
508
            Used for making GP plots.
509
            Args:
510
                xpred (np.array): numpy array of x values for realizing the GP
511
            Returns:
512
                tuple: tuple containing:
513
                    np.array: the numpy array of predictive means \n
514
                    np.array: the numpy array of predictive standard deviations
515
        """
516

517
        self.update_kernel_params()
1✔
518

519
        r = np.array([self._resids()]).T
1✔
520

521
        self.kernel.compute_distances(self.x, self.x)
1✔
522
        K = self.kernel.compute_covmatrix(self.errorbars())
1✔
523

524
        # Validate covariance matrix before Cholesky decomposition
525
        if np.any(np.isnan(K)):
1✔
NEW
526
            raise ValueError("Covariance matrix K contains NaNs - cannot proceed with Cholesky decomposition")
×
527
        if np.any(np.isinf(K)):
1✔
NEW
528
            raise ValueError("Covariance matrix K contains infs - cannot proceed with Cholesky decomposition")
×
529

530
        self.kernel.compute_distances(xpred, self.x)
1✔
531
        Ks = self.kernel.compute_covmatrix(0.)
1✔
532

533
        L = cho_factor(K)
1✔
534
        alpha = cho_solve(L, r)
1✔
535
        mu = np.dot(Ks, alpha).flatten()
1✔
536

537
        self.kernel.compute_distances(xpred, xpred)
1✔
538
        Kss = self.kernel.compute_covmatrix(0.)
1✔
539
        B = cho_solve(L, Ks.T)
1✔
540
        var = np.array(np.diag(Kss - np.dot(Ks, B))).flatten()
1✔
541
        stdev = np.sqrt(var)
1✔
542

543
        # set the default distances back to their regular values
544
        self.kernel.compute_distances(self.x, self.x)
1✔
545

546
        return mu, stdev
1✔
547

548

549
class CeleriteLikelihood(GPLikelihood):
1✔
550
    """Celerite GP Likelihood
551
    The Likelihood object for a radial velocity dataset modeled with a GP
552
    whose kernel is an approximation to the quasi-periodic kernel.
553
    See celerite.readthedocs.io and Foreman-Mackey et al. 2017. AJ, 154, 220
554
    (equation 56) for more details.
555
    See `radvel/example_planets/k2-131_celerite.py` for an example of a setup
556
    file that uses this Likelihood object.
557
    Args:
558
        model (radvel.model.RVModel): RVModel object
559
        t (array): time array
560
        vel (array): array of velocities
561
        errvel (array): array of velocity uncertainties
562
        hnames (list of string): keys corresponding to radvel.Parameter
563
           objects in model.params that are GP hyperparameters
564
        suffix (string): suffix to identify this Likelihood object;
565
           useful when constructing a `CompositeLikelihood` object
566
    """
567

568
    def __init__(self, model, t, vel, errvel, hnames, suffix='', **kwargs):
1✔
569

570
        super(CeleriteLikelihood, self).__init__(
1✔
571
            model, t, vel, errvel, hnames,
572
            suffix=suffix, kernel_name='Celerite'
573
        )
574

575
        # Sort inputs in time order. Required for celerite calculations.
576
        order = np.argsort(self.x)
1✔
577
        self.x = self.x[order]
1✔
578
        self.y = self.y[order]
1✔
579
        self.yerr = self.yerr[order]
1✔
580
        self.N = len(self.x)
1✔
581

582
    def logprob(self):
1✔
583

584
        self.update_kernel_params()
1✔
585

586
        try:
1✔
587
            solver = self.kernel.compute_covmatrix(self.errorbars())
1✔
588

589
            # calculate log likelihood
590
            lnlike = -0.5 * (solver.dot_solve(self._resids()) + solver.log_determinant() + self.N*np.log(2.*np.pi))
1✔
591

592
            return lnlike
1✔
593

594
        except celerite.solver.LinAlgError:
×
595
            warnings.warn("Non-positive definite kernel detected.", RuntimeWarning)
×
596
            return -np.inf
×
597

598
    def predict(self,xpred):
1✔
599
        """ Realize the GP using the current values of the hyperparameters at values x=xpred.
600
            Used for making GP plots. Wrapper for `celerite.GP.predict()`.
601
            Args:
602
                xpred (np.array): numpy array of x values for realizing the GP
603
            Returns:
604
                tuple: tuple containing:
605
                    np.array: numpy array of predictive means \n
606
                    np.array: numpy array of predictive standard deviations
607
        """
608

609
        self.update_kernel_params()
1✔
610

611
        B = self.kernel.hparams['gp_B'].value
1✔
612
        C = self.kernel.hparams['gp_C'].value
1✔
613
        L = self.kernel.hparams['gp_L'].value
1✔
614
        Prot = self.kernel.hparams['gp_Prot'].value
1✔
615

616
        # build celerite kernel with current values of hparams
617
        kernel = celerite.terms.JitterTerm(
1✔
618
                log_sigma = np.log(self.vector.vector[self.jit_index][0])
619
                )
620

621
        kernel += celerite.terms.RealTerm(
1✔
622
            log_a=np.log(B*(1+C)/(2+C)),
623
            log_c=np.log(1/L)
624
        )
625

626
        kernel += celerite.terms.ComplexTerm(
1✔
627
            log_a=np.log(B/(2+C)),
628
            log_b=-np.inf,
629
            log_c=np.log(1/L),
630
            log_d=np.log(2*np.pi/Prot)
631
        )
632

633
        gp = celerite.GP(kernel)
1✔
634
        gp.compute(self.x, self.yerr)
1✔
635
        mu, var = gp.predict(self._resids(), xpred, return_var=True)
1✔
636

637
        stdev = np.sqrt(var)
1✔
638

639
        return mu, stdev
1✔
640

641

642
def loglike_jitter(residuals, sigma, sigma_jit):
1✔
643
    """
644
    Log-likelihood incorporating jitter
645
    See equation (1) in Howard et al. 2014. Returns loglikelihood, where
646
    sigma**2 is replaced by sigma**2 + sigma_jit**2. It penalizes
647
    excessively large values of jitter
648
    Args:
649
        residuals (array): array of residuals
650
        sigma (array): array of measurement errors
651
        sigma_jit (float): jitter
652
    Returns:
653
        float: log-likelihood
654
    """
655
    sum_sig_quad = sigma**2 + sigma_jit**2
1✔
656
    penalty = np.sum( np.log( np.sqrt( 2 * np.pi * sum_sig_quad ) ) )
1✔
657
    chi2 = np.sum(residuals**2 / sum_sig_quad)
1✔
658
    loglike = -0.5 * chi2 - penalty
1✔
659

660
    return loglike
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc