def gwb(self,option="hd_vary_gamma"): """ Spatially-correlated quadrupole signal from the nanohertz stochastic gravitational-wave background. """ name = 'gw' if "_nfreqs" in option: split_idx_nfreqs = option.split('_').index('nfreqs') - 1 nfreqs = int(option.split('_')[split_idx_nfreqs]) else: nfreqs = self.determine_nfreqs(sel_func_name=None, common_signal=True) if "_gamma" in option: amp_name = '{}_log10_A'.format(name) if self.params.gwb_lgA_prior == "uniform": gwb_log10_A = parameter.Uniform(self.params.gwb_lgA[0], self.params.gwb_lgA[1])(amp_name) elif self.params.gwb_lgA_prior == "linexp": gwb_log10_A = parameter.LinearExp(self.params.gwb_lgA[0], self.params.gwb_lgA[1])(amp_name) gam_name = '{}_gamma'.format(name) if "vary_gamma" in option: gwb_gamma = parameter.Uniform(self.params.gwb_gamma[0], self.params.gwb_gamma[1])(gam_name) elif "fixed_gamma" in option: gwb_gamma = parameter.Constant(4.33)(gam_name) else: split_idx_gamma = option.split('_').index('gamma') - 1 gamma_val = float(option.split('_')[split_idx_gamma]) gwb_gamma = parameter.Constant(gamma_val)(gam_name) gwb_pl = utils.powerlaw(log10_A=gwb_log10_A, gamma=gwb_gamma) elif "freesp" in option: amp_name = '{}_log10_rho'.format(name) log10_rho = parameter.Uniform(self.params.gwb_lgrho[0], self.params.gwb_lgrho[1], size=nfreqs)(amp_name) gwb_pl = gp_priors.free_spectrum(log10_rho=log10_rho) if "hd" in option: orf = utils.hd_orf() gwb = gp_signals.FourierBasisCommonGP(gwb_pl, orf, components=nfreqs, name='gwb', Tspan=self.params.Tspan) else: gwb = gp_signals.FourierBasisGP(gwb_pl, components=nfreqs, name='gwb', Tspan=self.params.Tspan) return gwb
def gwb(self,option="common_pl"): """ Spatially-correlated quadrupole signal from the nanohertz stochastic gravitational-wave background. """ gwb_log10_A = parameter.Uniform(params.gwb_lgA[0],params.gwb_lgA[1]) if option=="common_pl": gwb_gamma = parameter.Uniform(params.gwb_gamma[0],params.gwb_gamma[1]) elif option=="fixed_gamma": gwb_gamma = parameter.Constant(4.33) gwb_pl = utils.powerlaw(log10_A=gwb_log10_A, gamma=gwb_gamma) nfreqs = self.determine_nfreqs(sel_func_name=None) orf = utils.hd_orf() gwb = gp_signals.FourierBasisCommonGP(gwb_pl, orf, components=nfreqs, \ name='gwb', Tspan=self.params.Tspan) return gwb
def test_summary(self): """ Test summary table.""" T1, T3 = 3.16e8, 3.16e8 nf1 = 30 pl = utils.powerlaw(log10_A=parameter.Uniform(-18, -12), gamma=parameter.Uniform(1, 7)) orf = utils.hd_orf() rn = gp_signals.FourierBasisGP(spectrum=pl, components=nf1, Tspan=T1) crn = gp_signals.FourierBasisCommonGP(spectrum=pl, orf=orf, components=1, name='gw', Tspan=T3) model = rn + crn pta = model(self.psrs[0]) + model(self.psrs[1]) pta.summary(to_stdout=True)
def common_red_noise_block(psd='powerlaw', prior='log-uniform', Tspan=None, components=30, log10_A_val=None, gamma_val=None, delta_val=None, orf=None, orf_ifreq=0, leg_lmax=5, name='gw', coefficients=False, pshift=False, pseed=None): """ Returns common red noise model: 1. Red noise modeled with user defined PSD with 30 sampling frequencies. Available PSDs are ['powerlaw', 'turnover' 'spectrum'] :param psd: PSD to use for common red noise signal. Available options are ['powerlaw', 'turnover' 'spectrum', 'broken_powerlaw'] :param prior: Prior on log10_A. Default if "log-uniform". Use "uniform" for upper limits. :param Tspan: Sets frequency sampling f_i = i / Tspan. Default will use overall time span for individual pulsar. :param log10_A_val: Value of log10_A parameter for fixed amplitude analyses. :param gamma_val: Value of spectral index for power-law and turnover models. By default spectral index is varied of range [0,7] :param delta_val: Value of spectral index for high frequencies in broken power-law and turnover models. By default spectral index is varied in range [0,7]. :param orf: String representing which overlap reduction function to use. By default we do not use any spatial correlations. Permitted values are ['hd', 'dipole', 'monopole']. :param orf_ifreq: Frequency bin at which to start the Hellings & Downs function with numbering beginning at 0. Currently only works with freq_hd orf. :param leg_lmax: Maximum multipole of a Legendre polynomial series representation of the overlap reduction function [default=5] :param pshift: Option to use a random phase shift in design matrix. For testing the null hypothesis. :param pseed: Option to provide a seed for the random phase shift. :param name: Name of common red process """ orfs = { 'crn': None, 'hd': utils.hd_orf(), 'dipole': utils.dipole_orf(), 'monopole': utils.monopole_orf(), 'param_hd': model_orfs.param_hd_orf(a=parameter.Uniform(-1.5, 3.0)('gw_orf_param0'), b=parameter.Uniform(-1.0, 0.5)('gw_orf_param1'), c=parameter.Uniform(-1.0, 1.0)('gw_orf_param2')), 'spline_orf': model_orfs.spline_orf( params=parameter.Uniform(-0.9, 0.9, size=7)('gw_orf_spline')), 'bin_orf': model_orfs.bin_orf( params=parameter.Uniform(-1.0, 1.0, size=7)('gw_orf_bin')), 'zero_diag_hd': model_orfs.zero_diag_hd(), 'zero_diag_bin_orf': model_orfs.zero_diag_bin_orf(params=parameter.Uniform( -1.0, 1.0, size=7)('gw_orf_bin_zero_diag')), 'freq_hd': model_orfs.freq_hd(params=[components, orf_ifreq]), 'legendre_orf': model_orfs.legendre_orf( params=parameter.Uniform(-1.0, 1.0, size=leg_lmax + 1)('gw_orf_legendre')), 'zero_diag_legendre_orf': model_orfs.zero_diag_legendre_orf( params=parameter.Uniform(-1.0, 1.0, size=leg_lmax + 1)('gw_orf_legendre_zero_diag')) } # common red noise parameters if psd in ['powerlaw', 'turnover', 'turnover_knee', 'broken_powerlaw']: amp_name = '{}_log10_A'.format(name) if log10_A_val is not None: log10_Agw = parameter.Constant(log10_A_val)(amp_name) else: if prior == 'uniform': log10_Agw = parameter.LinearExp(-18, -11)(amp_name) elif prior == 'log-uniform' and gamma_val is not None: if np.abs(gamma_val - 4.33) < 0.1: log10_Agw = parameter.Uniform(-18, -14)(amp_name) else: log10_Agw = parameter.Uniform(-18, -11)(amp_name) else: log10_Agw = parameter.Uniform(-18, -11)(amp_name) gam_name = '{}_gamma'.format(name) if gamma_val is not None: gamma_gw = parameter.Constant(gamma_val)(gam_name) else: gamma_gw = parameter.Uniform(0, 7)(gam_name) # common red noise PSD if psd == 'powerlaw': cpl = utils.powerlaw(log10_A=log10_Agw, gamma=gamma_gw) elif psd == 'broken_powerlaw': delta_name = '{}_delta'.format(name) kappa_name = '{}_kappa'.format(name) log10_fb_name = '{}_log10_fb'.format(name) kappa_gw = parameter.Uniform(0.01, 0.5)(kappa_name) log10_fb_gw = parameter.Uniform(-10, -7)(log10_fb_name) if delta_val is not None: delta_gw = parameter.Constant(delta_val)(delta_name) else: delta_gw = parameter.Uniform(0, 7)(delta_name) cpl = gpp.broken_powerlaw(log10_A=log10_Agw, gamma=gamma_gw, delta=delta_gw, log10_fb=log10_fb_gw, kappa=kappa_gw) elif psd == 'turnover': kappa_name = '{}_kappa'.format(name) lf0_name = '{}_log10_fbend'.format(name) kappa_gw = parameter.Uniform(0, 7)(kappa_name) lf0_gw = parameter.Uniform(-9, -7)(lf0_name) cpl = utils.turnover(log10_A=log10_Agw, gamma=gamma_gw, lf0=lf0_gw, kappa=kappa_gw) elif psd == 'turnover_knee': kappa_name = '{}_kappa'.format(name) lfb_name = '{}_log10_fbend'.format(name) delta_name = '{}_delta'.format(name) lfk_name = '{}_log10_fknee'.format(name) kappa_gw = parameter.Uniform(0, 7)(kappa_name) lfb_gw = parameter.Uniform(-9.3, -8)(lfb_name) delta_gw = parameter.Uniform(-2, 0)(delta_name) lfk_gw = parameter.Uniform(-8, -7)(lfk_name) cpl = gpp.turnover_knee(log10_A=log10_Agw, gamma=gamma_gw, lfb=lfb_gw, lfk=lfk_gw, kappa=kappa_gw, delta=delta_gw) if psd == 'spectrum': rho_name = '{}_log10_rho'.format(name) if prior == 'uniform': log10_rho_gw = parameter.LinearExp(-9, -4, size=components)(rho_name) elif prior == 'log-uniform': log10_rho_gw = parameter.Uniform(-9, -4, size=components)(rho_name) cpl = gpp.free_spectrum(log10_rho=log10_rho_gw) if orf is None: crn = gp_signals.FourierBasisGP(cpl, coefficients=coefficients, components=components, Tspan=Tspan, name=name, pshift=pshift, pseed=pseed) elif orf in orfs.keys(): if orf == 'crn': crn = gp_signals.FourierBasisGP(cpl, coefficients=coefficients, components=components, Tspan=Tspan, name=name, pshift=pshift, pseed=pseed) else: crn = gp_signals.FourierBasisCommonGP(cpl, orfs[orf], components=components, Tspan=Tspan, name=name, pshift=pshift, pseed=pseed) elif isinstance(orf, types.FunctionType): crn = gp_signals.FourierBasisCommonGP(cpl, orf, components=components, Tspan=Tspan, name=name, pshift=pshift, pseed=pseed) else: raise ValueError('ORF {} not recognized'.format(orf)) return crn
def gwb(self,option="hd_vary_gamma"): """ Spatially-correlated quadrupole signal from the nanohertz stochastic gravitational-wave background. """ name = 'gw' optsp = option.split('+') for option in optsp: if "_nfreqs" in option: split_idx_nfreqs = option.split('_').index('nfreqs') - 1 nfreqs = int(option.split('_')[split_idx_nfreqs]) else: nfreqs = self.determine_nfreqs(sel_func_name=None, common_signal=True) print('Number of Fourier frequencies for the GWB/CPL signal: ', nfreqs) if "_gamma" in option: amp_name = '{}_log10_A'.format(name) if (len(optsp) > 1 and 'hd' in option) or ('namehd' in option): amp_name += '_hd' elif (len(optsp) > 1 and ('varorf' in option or \ 'interporf' in option)) \ or ('nameorf' in option): amp_name += '_orf' if self.params.gwb_lgA_prior == "uniform": gwb_log10_A = parameter.Uniform(self.params.gwb_lgA[0], self.params.gwb_lgA[1])(amp_name) elif self.params.gwb_lgA_prior == "linexp": gwb_log10_A = parameter.LinearExp(self.params.gwb_lgA[0], self.params.gwb_lgA[1])(amp_name) gam_name = '{}_gamma'.format(name) if "vary_gamma" in option: gwb_gamma = parameter.Uniform(self.params.gwb_gamma[0], self.params.gwb_gamma[1])(gam_name) elif "fixed_gamma" in option: gwb_gamma = parameter.Constant(4.33)(gam_name) else: split_idx_gamma = option.split('_').index('gamma') - 1 gamma_val = float(option.split('_')[split_idx_gamma]) gwb_gamma = parameter.Constant(gamma_val)(gam_name) gwb_pl = utils.powerlaw(log10_A=gwb_log10_A, gamma=gwb_gamma) elif "freesp" in option: amp_name = '{}_log10_rho'.format(name) log10_rho = parameter.Uniform(self.params.gwb_lgrho[0], self.params.gwb_lgrho[1], size=nfreqs)(amp_name) gwb_pl = gp_priors.free_spectrum(log10_rho=log10_rho) if "hd" in option: print('Adding HD ORF') if "noauto" in option: print('Removing auto-correlation') orf = hd_orf_noauto() else: orf = utils.hd_orf() if len(optsp) > 1 or 'namehd' in option: gwname = 'gw_hd' else: gwname = 'gw' gwb = gp_signals.FourierBasisCommonGP(gwb_pl, orf, components=nfreqs, name=gwname, Tspan=self.params.Tspan) elif "mono" in option: print('Adding monopole ORF') orf = utils.monopole_orf() gwb = gp_signals.FourierBasisCommonGP(gwb_pl, orf, components=nfreqs, name='gw', Tspan=self.params.Tspan) elif "dipo" in option: print('Adding dipole ORF') orf = utils.dipole_orf() gwb = gp_signals.FourierBasisCommonGP(gwb_pl, orf, components=nfreqs, name='gw', Tspan=self.params.Tspan) else: gwb = gp_signals.FourierBasisGP(gwb_pl, components=nfreqs, name='gw', Tspan=self.params.Tspan) if 'gw_total' in locals(): gwb_total += gwb else: gwb_total = gwb return gwb_total
def test_parameterized_orf(self): T1 = 3.16e8 pl = utils.powerlaw(log10_A=parameter.Uniform(-18, -12), gamma=parameter.Uniform(1, 7)) orf = hd_orf_generic(a=parameter.Uniform(0, 5), b=parameter.Uniform(0, 5), c=parameter.Uniform(0, 5)) rn = gp_signals.FourierBasisGP(spectrum=pl, Tspan=T1, components=30) crn = gp_signals.FourierBasisCommonGP(spectrum=pl, orf=orf, components=30, name='gw', Tspan=T1) model = rn + crn pta = model(self.psrs[0]) + model(self.psrs[1]) lA1, gamma1 = -13, 1e-15 lA2, gamma2 = -13.3, 1e-15 lAc, gammac = -13.1, 1e-15 a, b, c = 1.9, 0.4, 0.23 params = { 'gw_log10_A': lAc, 'gw_gamma': gammac, 'gw_a': a, 'gw_b': b, 'gw_c': c, 'B1855+09_red_noise_log10_A': lA1, 'B1855+09_red_noise_gamma': gamma1, 'J1909-3744_red_noise_log10_A': lA2, 'J1909-3744_red_noise_gamma': gamma2 } phi = pta.get_phi(params) phiinv = pta.get_phiinv(params) F1, f1 = utils.createfourierdesignmatrix_red(self.psrs[0].toas, nmodes=30, Tspan=T1) F2, f2 = utils.createfourierdesignmatrix_red(self.psrs[1].toas, nmodes=30, Tspan=T1) msg = 'F matrix incorrect' assert np.allclose(pta.get_basis(params)[0], F1, rtol=1e-10), msg assert np.allclose(pta.get_basis(params)[1], F2, rtol=1e-10), msg nftot = 120 phidiag = np.zeros(nftot) phit = np.zeros((nftot, nftot)) phidiag[:60] = utils.powerlaw(f1, lA1, gamma1) phidiag[:60] += utils.powerlaw(f1, lAc, gammac) phidiag[60:] = utils.powerlaw(f2, lA2, gamma2) phidiag[60:] += utils.powerlaw(f2, lAc, gammac) phit[np.diag_indices(nftot)] = phidiag orf = hd_orf_generic(self.psrs[0].pos, self.psrs[1].pos, a=a, b=b, c=c) spec = utils.powerlaw(f1, log10_A=lAc, gamma=gammac) phit[:60, 60:] = np.diag(orf * spec) phit[60:, :60] = phit[:60, 60:] msg = '{} {}'.format(np.diag(phi), np.diag(phit)) assert np.allclose(phi, phit, rtol=1e-15, atol=1e-17), msg msg = 'PTA Phi inverse is incorrect {}.'.format(params) assert np.allclose(phiinv, np.linalg.inv(phit), rtol=1e-15, atol=1e-17), msg
def test_pta_phi(self): T1, T2, T3 = 3.16e8, 3.16e8, 3.16e8 nf1, nf2, nf3 = 2, 2, 1 pl = utils.powerlaw(log10_A=parameter.Uniform(-18, -12), gamma=parameter.Uniform(1, 7)) orf = utils.hd_orf() rn = gp_signals.FourierBasisGP(spectrum=pl, components=nf1, Tspan=T1) crn = gp_signals.FourierBasisCommonGP(spectrum=pl, orf=orf, components=1, name='gw', Tspan=T3) model = rn + crn pta = model(self.psrs[0]) + model(self.psrs[1]) lA1, gamma1 = -13, 1e-15 lA2, gamma2 = -13.3, 1e-15 lAc, gammac = -13.1, 1e-15 params = { 'gw_log10_A': lAc, 'gw_gamma': gammac, 'B1855+09_red_noise_log10_A': lA1, 'B1855+09_red_noise_gamma': gamma1, 'J1909-3744_red_noise_log10_A': lA2, 'J1909-3744_red_noise_gamma': gamma2 } phi = pta.get_phi(params) phiinv = pta.get_phiinv(params) T1, T2, T3 = 3.16e8, 3.16e8, 3.16e8 nf1, nf2, nf3 = 2, 2, 1 F1, f1 = utils.createfourierdesignmatrix_red(self.psrs[0].toas, nf1, Tspan=T1) F2, f2 = utils.createfourierdesignmatrix_red(self.psrs[1].toas, nf2, Tspan=T2) F1c, fc = utils.createfourierdesignmatrix_red(self.psrs[0].toas, nf3, Tspan=T3) F2c, fc = utils.createfourierdesignmatrix_red(self.psrs[1].toas, nf3, Tspan=T3) nftot = 2 * 2 * nf1 phidiag = np.zeros(nftot) phit = np.zeros((nftot, nftot)) phidiag[:4] = utils.powerlaw(f1, lA1, gamma1) phidiag[:2] += utils.powerlaw(fc, lAc, gammac) phidiag[4:] = utils.powerlaw(f2, lA2, gamma2) phidiag[4:6] += utils.powerlaw(fc, lAc, gammac) phit[np.diag_indices(nftot)] = phidiag phit[:2, 4:6] = np.diag( hd_powerlaw(fc, self.psrs[0].pos, self.psrs[1].pos, lAc, gammac)) phit[4:6, :2] = np.diag( hd_powerlaw(fc, self.psrs[0].pos, self.psrs[1].pos, lAc, gammac)) msg = '{} {}'.format(np.diag(phi), np.diag(phit)) assert np.allclose(phi, phit, rtol=1e-15, atol=1e-17), msg msg = 'PTA Phi inverse is incorrect {}.'.format(params) assert np.allclose(phiinv, np.linalg.inv(phit), rtol=1e-15, atol=1e-17), msg
def test_pta_phiinv_methods(self): ef = white_signals.MeasurementNoise(efac=parameter.Uniform(0.1, 5)) span = np.max(self.psrs[0].toas) - np.min(self.psrs[0].toas) pl = utils.powerlaw(log10_A=parameter.Uniform(-16, -13), gamma=parameter.Uniform(1, 7)) orf = utils.hd_orf() vrf = utils.dipole_orf() rn = gp_signals.FourierBasisGP(spectrum=pl, components=30, Tspan=span) hdrn = gp_signals.FourierBasisCommonGP(spectrum=pl, orf=orf, components=20, Tspan=span, name='gw') vrn = gp_signals.FourierBasisCommonGP(spectrum=pl, orf=vrf, components=20, Tspan=span, name='vec') vrn2 = gp_signals.FourierBasisCommonGP(spectrum=pl, orf=vrf, components=20, Tspan=span * 1.234, name='vec2') # two common processes, sharing basis partially model = ef + rn + hdrn # + vrn pta = signal_base.PTA([model(psr) for psr in self.psrs]) ps = parameter.sample(pta.params) phi = pta.get_phi(ps) ldp = np.linalg.slogdet(phi)[1] inv1, ld1 = pta.get_phiinv(ps, method='cliques', logdet=True) inv2, ld2 = pta.get_phiinv(ps, method='partition', logdet=True) inv3, ld3 = pta.get_phiinv(ps, method='sparse', logdet=True) if not isinstance(inv3, np.ndarray): inv3 = inv3.toarray() for ld in [ld1, ld2, ld3]: msg = "Wrong phi log determinant for two common processes" assert np.allclose(ldp, ld, rtol=1e-15, atol=1e-6), msg for inv in [inv1, inv2, inv3]: msg = "Wrong phi inverse for two common processes" assert np.allclose(np.dot(phi, inv), np.eye(phi.shape[0]), rtol=1e-15, atol=1e-6), msg for inva, invb in itertools.combinations([inv1, inv2, inv3], 2): assert np.allclose(inva, invb) # two common processes, no sharing basis model = ef + rn + vrn2 pta = signal_base.PTA([model(psr) for psr in self.psrs]) ps = parameter.sample(pta.params) phi = pta.get_phi(ps) ldp = np.linalg.slogdet(phi)[1] inv1, ld1 = pta.get_phiinv(ps, method='cliques', logdet=True) inv2, ld2 = pta.get_phiinv(ps, method='partition', logdet=True) inv3, ld3 = pta.get_phiinv(ps, method='sparse', logdet=True) if not isinstance(inv3, np.ndarray): inv3 = inv3.toarray() for ld in [ld1, ld2, ld3]: msg = "Wrong phi log determinant for two common processes" assert np.allclose(ldp, ld, rtol=1e-15, atol=1e-6), msg for inv in [inv1, inv2, inv3]: msg = "Wrong phi inverse for two processes" assert np.allclose(np.dot(phi, inv), np.eye(phi.shape[0]), rtol=1e-15, atol=1e-6), msg for inva, invb in itertools.combinations([inv1, inv2, inv3], 2): assert np.allclose(inva, invb) # three common processes, sharing basis partially model = ef + rn + hdrn + vrn pta = signal_base.PTA([model(psr) for psr in self.psrs]) ps = parameter.sample(pta.params) phi = pta.get_phi(ps) ldp = np.linalg.slogdet(phi)[1] inv1, ld1 = pta.get_phiinv(ps, method='cliques', logdet=True) inv2, ld2 = pta.get_phiinv(ps, method='partition', logdet=True) inv3, ld3 = pta.get_phiinv(ps, method='sparse', logdet=True) if not isinstance(inv3, np.ndarray): inv3 = inv3.toarray() for ld in [ld1, ld3]: msg = "Wrong phi log determinant for two common processes" assert np.allclose(ldp, ld, rtol=1e-15, atol=1e-6), msg for inv in [inv1, inv3]: msg = "Wrong phi inverse for three common processes" assert np.allclose(np.dot(phi, inv), np.eye(phi.shape[0]), rtol=1e-15, atol=1e-6), msg for inva, invb in itertools.combinations([inv1, inv3], 2): assert np.allclose(inva, invb) # four common processes, three sharing basis partially model = ef + rn + hdrn + vrn + vrn2 pta = signal_base.PTA([model(psr) for psr in self.psrs]) ps = parameter.sample(pta.params) phi = pta.get_phi(ps) ldp = np.linalg.slogdet(phi)[1] inv1, ld1 = pta.get_phiinv(ps, method='cliques', logdet=True) inv2, ld2 = pta.get_phiinv(ps, method='partition', logdet=True) inv3, ld3 = pta.get_phiinv(ps, method='sparse', logdet=True) if not isinstance(inv3, np.ndarray): inv3 = inv3.toarray() for ld in [ld1, ld3]: msg = "Wrong phi log determinant for two common processes" assert np.allclose(ldp, ld, rtol=1e-15, atol=1e-6), msg for inv in [inv1, inv3]: msg = "Wrong phi inverse for four processes" assert np.allclose(np.dot(phi, inv), np.eye(phi.shape[0]), rtol=1e-15, atol=1e-6), msg for inva, invb in itertools.combinations([inv1, inv3], 2): assert np.allclose(inva, invb)
# red noise (powerlaw with 30 frequencies) pl = utils.powerlaw(log10_A=red_noise_log10_A, gamma=red_noise_gamma) rn = gp_signals.FourierBasisGP(spectrum=pl, components=30, Tspan=Tspan) cpl_1 = utils.powerlaw(log10_A=log10_A_gw_1, gamma=gamma_gw_2) cpl_2 = utils.powerlaw(log10_A=log10_A_gw_2, gamma=gamma_gw_2) #Common red noise process with no correlations #crn = gp_signals.FourierBasisGP(spectrum = cpl, components=30, Tspan=Tspan, name = 'gw') # gwb with Hellings and Downs correlations # Hellings and Downs ORF orf = utils.hd_orf() gwb_1 = gp_signals.FourierBasisCommonGP(cpl_1, orf, components=30, name='gw_1', Tspan=Tspan) gwb_2 = gp_signals.FourierBasisCommonGP(cpl_2, orf, components=30, name='gw_2', Tspan=Tspan) # full model is sum of components model = ef + eq + ec + rn + tm + gwb_1 + gwb_2 #+ crn # initialize PTA pta = signal_base.PTA([model(psr) for psr in psrs]) with open(outdir + '/parameters.json', 'w') as fp:
def common_red_noise_block(psd='powerlaw', prior='log-uniform', Tspan=None, components=30, gamma_val=None, orf=None, name='gw', coefficients=False, pshift=False, pseed=None): """ Returns common red noise model: 1. Red noise modeled with user defined PSD with 30 sampling frequencies. Available PSDs are ['powerlaw', 'turnover' 'spectrum'] :param psd: PSD to use for common red noise signal. Available options are ['powerlaw', 'turnover' 'spectrum'] :param prior: Prior on log10_A. Default if "log-uniform". Use "uniform" for upper limits. :param Tspan: Sets frequency sampling f_i = i / Tspan. Default will use overall time span for indivicual pulsar. :param gamma_val: Value of spectral index for power-law and turnover models. By default spectral index is varied of range [0,7] :param orf: String representing which overlap reduction function to use. By default we do not use any spatial correlations. Permitted values are ['hd', 'dipole', 'monopole']. :param pshift: Option to use a random phase shift in design matrix. For testing the null hypothesis. :param pseed: Option to provide a seed for the random phase shift. :param name: Name of common red process """ orfs = { 'hd': utils.hd_orf(), 'dipole': utils.dipole_orf(), 'monopole': utils.monopole_orf() } # common red noise parameters if psd in ['powerlaw', 'turnover', 'turnover_knee']: amp_name = '{}_log10_A'.format(name) if prior == 'uniform': log10_Agw = parameter.LinearExp(-18, -11)(amp_name) elif prior == 'log-uniform' and gamma_val is not None: if np.abs(gamma_val - 4.33) < 0.1: log10_Agw = parameter.Uniform(-18, -14)(amp_name) else: log10_Agw = parameter.Uniform(-18, -11)(amp_name) else: log10_Agw = parameter.Uniform(-18, -11)(amp_name) gam_name = '{}_gamma'.format(name) if gamma_val is not None: gamma_gw = parameter.Constant(gamma_val)(gam_name) else: gamma_gw = parameter.Uniform(0, 7)(gam_name) # common red noise PSD if psd == 'powerlaw': cpl = utils.powerlaw(log10_A=log10_Agw, gamma=gamma_gw) elif psd == 'turnover': kappa_name = '{}_kappa'.format(name) lf0_name = '{}_log10_fbend'.format(name) kappa_gw = parameter.Uniform(0, 7)(kappa_name) lf0_gw = parameter.Uniform(-9, -7)(lf0_name) cpl = utils.turnover(log10_A=log10_Agw, gamma=gamma_gw, lf0=lf0_gw, kappa=kappa_gw) elif psd == 'turnover_knee': kappa_name = '{}_kappa'.format(name) lfb_name = '{}_log10_fbend'.format(name) delta_name = '{}_delta'.format(name) lfk_name = '{}_log10_fknee'.format(name) kappa_gw = parameter.Uniform(0, 7)(kappa_name) lfb_gw = parameter.Uniform(-9.3, -8)(lfb_name) delta_gw = parameter.Uniform(-2, 0)(delta_name) lfk_gw = parameter.Uniform(-8, -7)(lfk_name) cpl = gpp.turnover_knee(log10_A=log10_Agw, gamma=gamma_gw, lfb=lfb_gw, lfk=lfk_gw, kappa=kappa_gw, delta=delta_gw) if psd == 'spectrum': rho_name = '{}_log10_rho'.format(name) if prior == 'uniform': log10_rho_gw = parameter.LinearExp(-9, -4, size=components)(rho_name) elif prior == 'log-uniform': log10_rho_gw = parameter.Uniform(-9, -4, size=components)(rho_name) cpl = gpp.free_spectrum(log10_rho=log10_rho_gw) if orf is None: crn = gp_signals.FourierBasisGP(cpl, coefficients=coefficients, components=components, Tspan=Tspan, name=name, pshift=pshift, pseed=pseed) elif orf in orfs.keys(): crn = gp_signals.FourierBasisCommonGP(cpl, orfs[orf], components=components, Tspan=Tspan, name=name, pshift=pshift, pseed=pseed) elif isinstance(orf, types.FunctionType): crn = gp_signals.FourierBasisCommonGP(cpl, orf, components=components, Tspan=Tspan, name=name, pshift=pshift, pseed=pseed) else: raise ValueError('ORF {} not recognized'.format(orf)) return crn
def compute_like(self, npsrs=1, inc_corr=False, inc_kernel=False): # get parameters from PAL2 style noise files params = get_noise_from_pal2(datadir + "/B1855+09_noise.txt") params2 = get_noise_from_pal2(datadir + "/J1909-3744_noise.txt") params.update(params2) psrs = self.psrs if npsrs == 2 else [self.psrs[0]] if inc_corr: params.update({"GW_gamma": 4.33, "GW_log10_A": -15.0}) # find the maximum time span to set GW frequency sampling tmin = [p.toas.min() for p in psrs] tmax = [p.toas.max() for p in psrs] Tspan = np.max(tmax) - np.min(tmin) # setup basic model efac = parameter.Constant() equad = parameter.Constant() ecorr = parameter.Constant() log10_A = parameter.Constant() gamma = parameter.Constant() selection = Selection(selections.by_backend) ef = white_signals.MeasurementNoise(efac=efac, selection=selection) eq = white_signals.EquadNoise(log10_equad=equad, selection=selection) ec = white_signals.EcorrKernelNoise(log10_ecorr=ecorr, selection=selection) pl = utils.powerlaw(log10_A=log10_A, gamma=gamma) rn = gp_signals.FourierBasisGP(pl) orf = utils.hd_orf() crn = gp_signals.FourierBasisCommonGP(pl, orf, components=20, name="GW", Tspan=Tspan) tm = gp_signals.TimingModel() log10_sigma = parameter.Uniform(-10, -5) log10_lam = parameter.Uniform(np.log10(86400), np.log10(1500 * 86400)) basis = create_quant_matrix(dt=7 * 86400) prior = se_kernel(log10_sigma=log10_sigma, log10_lam=log10_lam) se = gp_signals.BasisGP(prior, basis, name="se") # set up kernel stuff if isinstance(inc_kernel, bool): inc_kernel = [inc_kernel] * npsrs if inc_corr: s = ef + eq + ec + rn + crn + tm else: s = ef + eq + ec + rn + tm models = [] for ik, psr in zip(inc_kernel, psrs): snew = s + se if ik else s models.append(snew(psr)) pta = signal_base.PTA(models) # set parameters pta.set_default_params(params) # SE kernel parameters log10_sigmas, log10_lams = [-7.0, -6.5], [7.0, 6.5] params.update({ "B1855+09_se_log10_lam": log10_lams[0], "B1855+09_se_log10_sigma": log10_sigmas[0], "J1909-3744_se_log10_lam": log10_lams[1], "J1909-3744_se_log10_sigma": log10_sigmas[1], }) # get parameters efacs, equads, ecorrs, log10_A, gamma = [], [], [], [], [] lsig, llam = [], [] for pname in [p.name for p in psrs]: efacs.append([ params[key] for key in sorted(params.keys()) if "efac" in key and pname in key ]) equads.append([ params[key] for key in sorted(params.keys()) if "equad" in key and pname in key ]) ecorrs.append([ params[key] for key in sorted(params.keys()) if "ecorr" in key and pname in key ]) log10_A.append(params["{}_red_noise_log10_A".format(pname)]) gamma.append(params["{}_red_noise_gamma".format(pname)]) lsig.append(params["{}_se_log10_sigma".format(pname)]) llam.append(params["{}_se_log10_lam".format(pname)]) GW_gamma = 4.33 GW_log10_A = -15.0 # correct value tflags = [sorted(list(np.unique(p.backend_flags))) for p in psrs] cfs, logdets, phis, Ts = [], [], [], [] for ii, (ik, psr, flags) in enumerate(zip(inc_kernel, psrs, tflags)): nvec0 = np.zeros_like(psr.toas) for ct, flag in enumerate(flags): ind = psr.backend_flags == flag nvec0[ind] = efacs[ii][ct]**2 * psr.toaerrs[ind]**2 nvec0[ind] += 10**(2 * equads[ii][ct]) * np.ones(np.sum(ind)) # get the basis bflags = psr.backend_flags Umats = [] for flag in np.unique(bflags): mask = bflags == flag Umats.append( utils.create_quantization_matrix(psr.toas[mask])[0]) nepoch = sum(U.shape[1] for U in Umats) U = np.zeros((len(psr.toas), nepoch)) jvec = np.zeros(nepoch) netot = 0 for ct, flag in enumerate(np.unique(bflags)): mask = bflags == flag nn = Umats[ct].shape[1] U[mask, netot:nn + netot] = Umats[ct] jvec[netot:nn + netot] = 10**(2 * ecorrs[ii][ct]) netot += nn # get covariance matrix cov = np.diag(nvec0) + np.dot(U * jvec[None, :], U.T) cf = sl.cho_factor(cov) logdet = np.sum(2 * np.log(np.diag(cf[0]))) cfs.append(cf) logdets.append(logdet) F, f2 = utils.createfourierdesignmatrix_red(psr.toas, nmodes=20, Tspan=Tspan) Mmat = psr.Mmat.copy() norm = np.sqrt(np.sum(Mmat**2, axis=0)) Mmat /= norm U2, avetoas = create_quant_matrix(psr.toas, dt=7 * 86400) if ik: T = np.hstack((F, Mmat, U2)) else: T = np.hstack((F, Mmat)) Ts.append(T) phi = utils.powerlaw(f2, log10_A=log10_A[ii], gamma=gamma[ii]) if inc_corr: phigw = utils.powerlaw(f2, log10_A=GW_log10_A, gamma=GW_gamma) else: phigw = np.zeros(40) K = se_kernel(avetoas, log10_sigma=log10_sigmas[ii], log10_lam=log10_lams[ii]) k = np.diag( np.concatenate((phi + phigw, np.ones(Mmat.shape[1]) * 1e40))) if ik: k = sl.block_diag(k, K) phis.append(k) # manually compute loglike loglike = 0 TNrs, TNTs = [], [] for ct, psr in enumerate(psrs): TNrs.append(np.dot(Ts[ct].T, sl.cho_solve(cfs[ct], psr.residuals))) TNTs.append(np.dot(Ts[ct].T, sl.cho_solve(cfs[ct], Ts[ct]))) loglike += -0.5 * ( np.dot(psr.residuals, sl.cho_solve(cfs[ct], psr.residuals)) + logdets[ct]) TNr = np.concatenate(TNrs) phi = sl.block_diag(*phis) if inc_corr: hd = utils.hd_orf(psrs[0].pos, psrs[1].pos) phi[len(phis[0]):len(phis[0]) + 40, :40] = np.diag(phigw * hd) phi[:40, len(phis[0]):len(phis[0]) + 40] = np.diag(phigw * hd) cf = sl.cho_factor(phi) phiinv = sl.cho_solve(cf, np.eye(phi.shape[0])) logdetphi = np.sum(2 * np.log(np.diag(cf[0]))) Sigma = sl.block_diag(*TNTs) + phiinv cf = sl.cho_factor(Sigma) expval = sl.cho_solve(cf, TNr) logdetsigma = np.sum(2 * np.log(np.diag(cf[0]))) loglike -= 0.5 * (logdetphi + logdetsigma) loglike += 0.5 * np.dot(TNr, expval) method = ["partition", "sparse", "cliques"] for mth in method: eloglike = pta.get_lnlikelihood(params, phiinv_method=mth) msg = "Incorrect like for npsr={}, phiinv={}".format(npsrs, mth) assert np.allclose(eloglike, loglike), msg
def model_simple(psrs, psd='powerlaw', efac=False, n_gwbfreqs=30, components=30, freqs=None, vary_gamma=False, upper_limit=False, bayesephem=False, select='backend', red_noise=False, Tspan=None, hd_orf=False, rn_dropout=False, dp_threshold=0.5): """ Reads in list of enterprise Pulsar instance and returns a PTA instantiated with the most simple model allowable for enterprise: per pulsar: 1. fixed EFAC per backend/receiver system at 1.0 2. Linear timing model. 3. Red noise modeled as a power-law with 30 sampling frequencies. Default=False global: 1.Common red noise modeled with user defined PSD with 30 sampling frequencies. Available PSDs are ['powerlaw', 'turnover' 'spectrum'] 2. Optional physical ephemeris modeling. :param psd: PSD to use for common red noise signal. Available options are ['powerlaw', 'turnover' 'spectrum']. 'powerlaw' is default value. :param gamma_common: Fixed common red process spectral index value. By default we vary the spectral index over the range [0, 7]. :param upper_limit: Perform upper limit on common red noise amplitude. By default this is set to False. Note that when performing upper limits it is recommended that the spectral index also be fixed to a specific value. :param bayesephem: Include BayesEphem model. Set to False by default """ amp_prior = 'uniform' if upper_limit else 'log-uniform' # find the maximum time span to set GW frequency sampling if Tspan is None: Tspan = model_utils.get_tspan(psrs) # timing model model = gp_signals.TimingModel() #Only White Noise is EFAC set to 1.0 selection = selections.Selection(selections.by_backend) if efac: ef = parameter.Uniform(0.1,10.0) else: ef = parameter.Constant(1.00) model += white_signals.MeasurementNoise(efac=ef, selection=selection) # common red noise block if upper_limit: log10_A_gw = parameter.LinearExp(-18,-12)('gw_log10_A') else: log10_A_gw = parameter.Uniform(-18,-12)('gw_log10_A') if vary_gamma: gamma_gw = parameter.Uniform(0,7)('gw_gamma') else: gamma_gw = parameter.Constant(4.33)('gw_gamma') pl = signal_base.Function(utils.powerlaw, log10_A=log10_A_gw, gamma=gamma_gw) if hd_orf: if freqs is None: gw = gp_signals.FourierBasisCommonGP(spectrum=pl, orf=utils.hd_orf(), components=n_gwbfreqs, Tspan=Tspan, name='gw') else: gw = gp_signals.FourierBasisCommonGP(spectrum=pl, orf=utils.hd_orf(), modes=freqs, name='gw') model += gw else: if freqs is None: crn = gp_signals.FourierBasisGP(spectrum=pl, components=n_gwbfreqs, Tspan=Tspan, name='gw') else: crn = gp_signals.FourierBasisGP(spectrum=pl, modes=freqs, name='gw') model += crn if red_noise and rn_dropout: if amp_prior == 'uniform': log10_A = parameter.LinearExp(-20, -11) elif amp_prior == 'log-uniform': log10_A = parameter.Uniform(-20, -11) else: log10_A = parameter.Uniform(-20, -11) gamma = parameter.Uniform(0, 7) k_drop = parameter.Uniform(0, 1) if dp_threshold == 6.0: dp_threshold = parameter.Uniform(0,1)('k_threshold') pl = dropout.dropout_powerlaw(log10_A=log10_A, gamma=gamma, k_drop=k_drop, k_threshold=dp_threshold) rn = gp_signals.FourierBasisGP(pl, components=components, Tspan=Tspan, name='red_noise') model += rn elif red_noise: # red noise model += models.red_noise_block(prior=amp_prior, Tspan=Tspan, components=components) # ephemeris model if bayesephem: model += deterministic_signals.PhysicalEphemerisSignal(use_epoch_toas=True) # set up PTA pta = signal_base.PTA([model(p) for p in psrs]) return pta
gwb_gamma = parameter.Constant(13 / 3)('gwb_gamma') gwb_pl = utils.powerlaw(log10_A=gwb_log10_A, gamma=gwb_gamma) # signals ef = white_signals.MeasurementNoise(efac=efac, selection=bkend) eq = white_signals.EquadNoise(log10_equad=equad, selection=bkend) ec = white_signals.EcorrKernelNoise(log10_ecorr=ecorr, selection=bkend_NG) dmgp = gp_signals.BasisGP(dm_pl, dm_basis, name='dm_gp') dmexp = models.dm_exponential_dip(tmin=54500, tmax=55000, name='dmexp_1') dm1yr = models.dm_annual_signal() rn = gp_signals.FourierBasisGP(spectrum=rn_pl, components=30, Tspan=Tspan) gwb = gp_signals.FourierBasisCommonGP(gwb_pl, orf, components=30, Tspan=Tspan, name='gw') tm = gp_signals.TimingModel(use_svd=True) be = deterministic_signals.PhysicalEphemerisSignal( # widen prior on jup orbit use_epoch_toas=True, jup_orb_elements=parameter.Uniform(-0.1, 0.1, size=6)('jup_orb_elements')) # full model mod_root = tm + ef + eq + dmgp + dm1yr + rn + gwb if args.be: mod_root += be mod_J1713 = mod_root + dmexp mod_ecorr = mod_root + ec mods = []
def gwb(self,option="hd_vary_gamma"): """ Spatially-correlated quadrupole signal from the nanohertz stochastic gravitational-wave background. """ name = 'gw' optsp = option.split('+') for option in optsp: if "_nfreqs" in option: split_idx_nfreqs = option.split('_').index('nfreqs') - 1 nfreqs = int(option.split('_')[split_idx_nfreqs]) else: nfreqs = self.determine_nfreqs(sel_func_name=None, common_signal=True) print('Number of Fourier frequencies for the GWB/CPL signal: ', nfreqs) if "_gamma" in option: amp_name = '{}_log10_A'.format(name) if (len(optsp) > 1 and 'hd' in option) or ('namehd' in option): amp_name += '_hd' elif (len(optsp) > 1 and ('varorf' in option or \ 'interporf' in option)) \ or ('nameorf' in option): amp_name += '_orf' if self.params.gwb_lgA_prior == "uniform": gwb_log10_A = parameter.Uniform(self.params.gwb_lgA[0], self.params.gwb_lgA[1])(amp_name) elif self.params.gwb_lgA_prior == "linexp": gwb_log10_A = parameter.LinearExp(self.params.gwb_lgA[0], self.params.gwb_lgA[1])(amp_name) elif self.params.gwb_lgA_prior == "normal": gwb_log10_A = parameter.Normal(mu=self.params.gwb_lgA[0], sigma=self.params.gwb_lgA[1])(amp_name) gam_name = '{}_gamma'.format(name) if "vary_gamma" in option: if self.params.gwb_gamma_prior == "uniform": gwb_gamma = parameter.Uniform(self.params.gwb_gamma[0], self.params.gwb_gamma[1])(gam_name) if self.params.gwb_gamma_prior == "normal": gwb_gamma = parameter.Normal(sigma=self.params.gwb_gamma[1], mu=self.params.gwb_gamma[0])(gam_name) elif "fixed_gamma" in option: gwb_gamma = parameter.Constant(4.33)(gam_name) else: split_idx_gamma = option.split('_').index('gamma') - 1 gamma_val = float(option.split('_')[split_idx_gamma]) gwb_gamma = parameter.Constant(gamma_val)(gam_name) gwb_pl = utils.powerlaw(log10_A=gwb_log10_A, gamma=gwb_gamma) elif "freesp" in option: amp_name = '{}_log10_rho'.format(name) log10_rho = parameter.Uniform(self.params.gwb_lgrho[0], self.params.gwb_lgrho[1], size=nfreqs)(amp_name) gwb_pl = gp_priors.free_spectrum(log10_rho=log10_rho) if "hd" in option: print('Adding HD ORF') if "noauto" in option: print('Removing auto-correlation') orf = hd_orf_noauto() else: import time print('AZ about to do hd orf', time.perf_counter()) orf = utils.hd_orf() print('AZ called utils.hd_orf', time.perf_counter()) if len(optsp) > 1 or 'namehd' in option: gwname = 'gwb_hd' else: gwname = 'gwb' #print('evaluating gwb fourierbasiscommongp', time.perf_counter()) gwb = gp_signals.FourierBasisCommonGP(gwb_pl, orf, components=nfreqs, name=gwname, Tspan=self.params.Tspan) #print('evaluating gwb fourierbasiscommongp', time.perf_counter()) elif "mono" in option: print('Adding monopole ORF') orf = utils.monopole_orf() gwb = gp_signals.FourierBasisCommonGP(gwb_pl, orf, components=nfreqs, name='gwb', Tspan=self.params.Tspan) elif "dipo" in option: print('Adding dipole ORF') orf = utils.dipole_orf() gwb = gp_signals.FourierBasisCommonGP(gwb_pl, orf, components=nfreqs, name='gwb', Tspan=self.params.Tspan) elif "halfdip" in option: print('Adding dipole/2 ORF') orf = halfdip_orf() gwb = gp_signals.FourierBasisCommonGP(gwb_pl, orf, components=nfreqs, name='gwb', Tspan=self.params.Tspan) elif "varorf" in option: if len(optsp) > 1 or 'nameorf' in option: gwname = 'gwb_orf' else: gwname = 'gwb' corr_coeff = parameter.Uniform(-1., 1., size=7)('corr_coeff') if "noauto" in option: orf = infer_orf_noauto(corr_coeff=corr_coeff) else: orf = infer_orf(corr_coeff=corr_coeff) gwb = gp_signals.FourierBasisCommonGP(gwb_pl, orf, components=nfreqs, name=gwname, Tspan=self.params.Tspan) elif "interporf" in option: print("Adding numpy-interpolated free ORF") if len(optsp) > 1 or 'nameorf' in option: gwname = 'gwb_orf' else: gwname = 'gwb' corr_coeff = parameter.Uniform(-1., 1., size=7)('corr_coeff') if "noauto" in option: orf = infer_orf_npinterp_noauto(corr_coeff=corr_coeff) else: orf = infer_orf_npinterp(corr_coeff=corr_coeff) if "skyscr" in option: gwb = FourierBasisSkyscrambledGP(gwb_pl, orf, components=nfreqs, name=gwname+'_skyscr', Tspan=self.params.Tspan) else: gwb = gp_signals.FourierBasisCommonGP(gwb_pl, orf, components=nfreqs, name=gwname, Tspan=self.params.Tspan) else: gwb = gp_signals.FourierBasisGP(gwb_pl, components=nfreqs, name='gwb', Tspan=self.params.Tspan) if 'gwb_total' in locals(): gwb_total += gwb else: gwb_total = gwb return gwb_total
def test_conditional_gp(self): ef = white_signals.MeasurementNoise(efac=parameter.Uniform(0.1, 5.0)) tm = gp_signals.TimingModel() ec = gp_signals.EcorrBasisModel(log10_ecorr=parameter.Uniform(-10, -5)) pl = utils.powerlaw(log10_A=parameter.Uniform(-18, -12), gamma=parameter.Uniform(1, 7)) rn = gp_signals.FourierBasisGP(spectrum=pl, components=10, combine=False) model = ef + tm + ec + rn pta = signal_base.PTA([model(self.psr), model(self.psr2)]) p0 = { "B1855+09_basis_ecorr_log10_ecorr": -6.051740765663904, "B1855+09_efac": 2.9027266737466095, "B1855+09_red_noise_gamma": 6.9720332277819725, "B1855+09_red_noise_log10_A": -16.749192700991543, "B1937+21_basis_ecorr_log10_ecorr": -9.726747733721872, "B1937+21_efac": 3.959178240268702, "B1937+21_red_noise_gamma": 2.9030772884814797, "B1937+21_red_noise_log10_A": -17.978562921948992, } c = utils.ConditionalGP(pta) cmean = c.get_mean_coefficients(p0) # build index for the global coefficient vector idx, ntot = {}, 0 for l, v in cmean.items(): idx[l] = slice(ntot, ntot + len(v)) ntot = ntot + len(v) # repeat the computation using the common-signal formalism TNrs = pta.get_TNr(p0) TNTs = pta.get_TNT(p0) phiinvs = pta.get_phiinv(p0, logdet=False, method="cliques") TNr = np.concatenate(TNrs) Sigma = sps.block_diag(TNTs, "csc") + sps.block_diag( [np.diag(phiinvs[0]), np.diag(phiinvs[1])]) ch = cholesky(Sigma) mn = ch(TNr) iSigma = sps.linalg.inv(Sigma) # check mean values msg = "Conditional GP coefficient value does not match" for l, v in cmean.items(): assert np.allclose(mn[idx[l]], v, atol=1e-4, rtol=1e-4), msg # check variances par = "B1937+21_linear_timing_model_coefficients" c1 = np.cov( np.array([cs[par] for cs in c.sample_coefficients(p0, n=10000)]).T) c2 = iSigma[idx[par], idx[par]].toarray().T msg = "Conditional GP coefficient variance does not match" assert np.allclose(c1, c2, atol=1e-4, rtol=1e-4), msg # check mean processes proc = "B1937+21_linear_timing_model" p1 = c.get_mean_processes(p0)[proc] p2 = np.dot(pta["B1937+21"]["linear_timing_model"].get_basis(), mn[idx[par]]) msg = "Conditional GP time series does not match" assert np.allclose(p1, p2, atol=1e-4, rtol=1e-4), msg # check mean of sampled processes p2 = np.mean(np.array( [pc[proc] for pc in c.sample_processes(p0, n=1000)]), axis=0) msg = "Mean of sampled conditional GP processes does not match" assert np.allclose(p1, p2, atol=1e-4, rtol=1e-4) # now try with a common process crn = gp_signals.FourierBasisCommonGP(spectrum=pl, orf=utils.hd_orf(), components=10, combine=False) model = ef + tm + ec + crn pta = signal_base.PTA([model(self.psr), model(self.psr2)]) p0 = { "B1855+09_basis_ecorr_log10_ecorr": -5.861847220080768, "B1855+09_efac": 4.588342210948306, "B1937+21_basis_ecorr_log10_ecorr": -9.151872649912377, "B1937+21_efac": 0.8947815819783302, "common_fourier_gamma": 6.638289750637263, "common_fourier_log10_A": -15.68180643904114, } c = utils.ConditionalGP(pta) cmean = c.get_mean_coefficients(p0) idx, ntot = {}, 0 for l, v in cmean.items(): idx[l] = slice(ntot, ntot + len(v)) ntot = ntot + len(v) TNrs = pta.get_TNr(p0) TNTs = pta.get_TNT(p0) phiinvs = pta.get_phiinv(p0, logdet=False, method="cliques") TNr = np.concatenate(TNrs) Sigma = sps.block_diag(TNTs, "csc") + sps.csc_matrix(phiinvs) ch = cholesky(Sigma) mn = ch(TNr) msg = "Conditional GP coefficient value does not match for common GP" for l, v in cmean.items(): assert np.allclose(mn[idx[l]], v)
def common_red_noise_block(psd='powerlaw', prior='log-uniform', Tspan=None, gamma_val=None, orf=None, name='gwb'): """ Returns common red noise model: 1. Red noise modeled with user defined PSD with 30 sampling frequencies. Available PSDs are ['powerlaw', 'turnover' 'spectrum'] :param psd: PSD to use for common red noise signal. Available options are ['powerlaw', 'turnover' 'spectrum'] :param prior: Prior on log10_A. Default if "log-uniform". Use "uniform" for upper limits. :param Tspan: Sets frequency sampling f_i = i / Tspan. Default will use overall time span for indivicual pulsar. :param gamma_val: Value of spectral index for power-law and turnover models. By default spectral index is varied of range [0,7] :param orf: String representing which overlap reduction function to use. By default we do not use any spatial correlations. Permitted values are ['hd', 'dipole', 'monopole']. :param name: Name of common red process """ orfs = { 'hd': utils.hd_orf(), 'dipole': utils.dipole_orf(), 'monopole': utils.monopole_orf() } # common red noise parameters if psd in ['powerlaw', 'turnover']: amp_name = '{}_log10_A'.format(name) if prior == 'uniform': log10_Agw = parameter.LinearExp(-18, -11)(amp_name) elif prior == 'log-uniform' and gamma_val is not None: if np.abs(gamma_val - 4.33) < 0.1: log10_Agw = parameter.Uniform(-18, -14)(amp_name) else: log10_Agw = parameter.Uniform(-18, -11)(amp_name) else: log10_Agw = parameter.Uniform(-18, -11)(amp_name) gam_name = '{}_gamma'.format(name) if gamma_val is not None: gamma_gw = parameter.Constant(gamma_val)(gam_name) else: gamma_gw = parameter.Uniform(0, 7)(gam_name) # common red noise PSD if psd == 'powerlaw': cpl = utils.powerlaw(log10_A=log10_Agw, gamma=gamma_gw) elif psd == 'turnover': kappa_name = '{}_kappa'.format(name) lf0_name = '{}_log10_fbend'.format(name) kappa_gw = parameter.Uniform(0, 7)(kappa_name) lf0_gw = parameter.Uniform(-9, -7)(lf0_name) cpl = utils.turnover(log10_A=log10_Agw, gamma=gamma_gw, lf0=lf0_gw, kappa=kappa_gw) if orf is None: crn = gp_signals.FourierBasisGP(cpl, components=30, Tspan=Tspan) elif orf in orfs.keys(): crn = gp_signals.FourierBasisCommonGP(cpl, orfs[orf], components=30, Tspan=Tspan) else: raise ValueError('ORF {} not recognized'.format(orf)) return crn
rn_low = blocks.red_noise_block(psd='powerlaw', prior='log-uniform', Tspan=Tspan_PTA, modes=modes, wgts=wgts,) rn_std = blocks.red_noise_block(psd='powerlaw', prior='log-uniform', Tspan=Tspan_PTA, components=30) gamma_gw = parameter.Constant(4.3333)('gw_gamma') log10_Agw = parameter.Uniform(-18, -14)('gw_log10_A') plaw_low = gpp.powerlaw_genmodes(log10_A=log10_Agw, gamma=gamma_gw, wgts=wgts) plaw_std = gpp.powerlaw(log10_A=log10_Agw, gamma=gamma_gw) gw_std = gp_signals.FourierBasisCommonGP(plaw_std, model_orfs.hd_orf(), components=14, Tspan=Tspan_PTA, name='gw') gw_low = gp_signals.FourierBasisCommonGP(plaw_low, model_orfs.hd_orf(), modes=modes, name='gw') std_models = [(s + rn_std + gw_std)(psr) for psr in pkl_psrs] low_models = [(s + rn_low + gw_low)(psr) for psr in pkl_psrs] pta_std = signal_base.PTA(std_models) pta_low = signal_base.PTA(low_models)
def test_common_red_noise(self): """Test of a coefficient-based common GP.""" pl = utils.powerlaw(log10_A=parameter.Uniform(-18, -12), gamma=parameter.Uniform(1, 7)) ef = white_signals.MeasurementNoise( efac=parameter.Uniform(0.1, 5.0)) Tspan = max(self.psr.toas.max(), self.psr2.toas.max()) - min( self.psr.toas.max(), self.psr2.toas.max()) pl = utils.powerlaw(log10_A=parameter.Uniform(-18, -12), gamma=parameter.Uniform(1, 7)) rn = gp_signals.FourierBasisCommonGP(spectrum=pl, orf=utils.hd_orf(), components=20, Tspan=Tspan) model = ef + rn rnc = gp_signals.FourierBasisCommonGP(spectrum=pl, orf=utils.hd_orf(), components=20, Tspan=Tspan, coefficients=True) modelc = ef + rnc pta = signal_base.PTA([model(self.psr), model(self.psr2)]) ptac = signal_base.PTA([modelc(self.psr), modelc(self.psr2)]) params = { "B1855+09_efac": 1.0, "B1937+21_efac": 1.0, "common_fourier_gamma": 5, "common_fourier_log10_A": -15, } # get GP delays in two different ways cf, cf2 = np.random.randn(40), np.random.randn(40) bs = pta.get_basis(params) da = [np.dot(bs[0], cf), np.dot(bs[1], cf2)] params.update({ "B1855+09_common_fourier_coefficients": cf, "B1937+21_common_fourier_coefficients": cf2 }) db = ptac.get_delay(params) msg = "Implicit and explicit GP delays are different." assert np.allclose(da[0], db[0]), msg assert np.allclose(da[1], db[1]), msg cpar = [p for p in ptac.params if "coefficients" in p.name] def shouldfail(): return cpar[0].get_logpdf(params) self.assertRaises(NotImplementedError, shouldfail)