def test_orf(self): """Test ORF functions.""" p1 = np.array([0.3, -0.5, 0.7]) p2 = np.array([0.9, 0.1, -0.6]) # test auto terms hd = utils.hd_orf(p1, p1) hd_exp = 1.0 dp = utils.dipole_orf(p1, p1) dp_exp = 1.0 + 1e-5 mp = utils.monopole_orf(p1, p1) mp_exp = 1.0 + 1e-5 msg = 'ORF auto term incorrect for {}' keys = ['hd', 'dipole', 'monopole'] vals = [(hd, hd_exp), (dp, dp_exp), (mp, mp_exp)] for key, val in zip(keys, vals): assert val[0] == val[1], msg.format(key) # test off diagonal terms hd = utils.hd_orf(p1, p2) omc2 = (1 - np.dot(p1, p2)) / 2 hd_exp = 1.5 * omc2 * np.log(omc2) - 0.25 * omc2 + 0.5 dp = utils.dipole_orf(p1, p2) dp_exp = np.dot(p1, p2) mp = utils.monopole_orf(p1, p2) mp_exp = 1.0 msg = 'ORF cross term incorrect for {}' keys = ['hd', 'dipole', 'monopole'] vals = [(hd, hd_exp), (dp, dp_exp), (mp, mp_exp)] for key, val in zip(keys, vals): assert val[0] == val[1], msg.format(key)
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
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) print('Number of Fourier frequencies for the GWB/CPL signal: ', nfreqs) 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: print('Adding HD ORF') orf = utils.hd_orf() gwb = gp_signals.FourierBasisCommonGP(gwb_pl, orf, components=nfreqs, name='gwb', 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='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 "varorf" in option: corr_coeff = parameter.Uniform(-1., 1., size=7)('corr_coeff') orf = infer_orf(corr_coeff=corr_coeff) 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 test_orf(self): """Test ORF functions.""" p1 = np.array([0.3, 0.648, 0.7]) p2 = np.array([0.2, 0.775, -0.6]) # test auto terms # hd = utils.hd_orf(p1, p1) hd_exp = 1.0 # dp = utils.dipole_orf(p1, p1) dp_exp = 1.0 + 1e-5 # mp = utils.monopole_orf(p1, p1) mp_exp = 1.0 + 1e-5 # psr_positions = np.array([[1.318116071652818, 2.2142974355881808], [1.1372584174390601, 0.79539883018414359]]) anis_basis = anis.anis_basis(psr_positions, lmax=1) anis_orf = round( utils.anis_orf(p1, p1, [0.0, 1.0, 0.0], anis_basis=anis_basis, psrs_pos=[p1, p2], lmax=1), 3) anis_orf_exp = 1.147 # msg = "ORF auto term incorrect for {}" keys = ["hd", "dipole", "monopole", "anisotropy"] vals = [(hd, hd_exp), (dp, dp_exp), (mp, mp_exp), (anis_orf, anis_orf_exp)] for key, val in zip(keys, vals): assert val[0] == val[1], msg.format(key) # test off diagonal terms # hd = utils.hd_orf(p1, p2) omc2 = (1 - np.dot(p1, p2)) / 2 hd_exp = 1.5 * omc2 * np.log(omc2) - 0.25 * omc2 + 0.5 # dp = utils.dipole_orf(p1, p2) dp_exp = np.dot(p1, p2) # mp = utils.monopole_orf(p1, p2) mp_exp = 1.0 # psr_positions = np.array([[1.318116071652818, 2.2142974355881808], [1.1372584174390601, 0.79539883018414359]]) anis_basis = anis.anis_basis(psr_positions, lmax=1) anis_orf = round( utils.anis_orf(p1, p2, [0.0, 1.0, 0.0], anis_basis=anis_basis, psrs_pos=[p1, p2], lmax=1), 3) anis_orf_exp = -0.150 # msg = "ORF cross term incorrect for {}" keys = ["hd", "dipole", "monopole", "anisotropy"] vals = [(hd, hd_exp), (dp, dp_exp), (mp, mp_exp), (anis_orf, anis_orf_exp)] for key, val in zip(keys, vals): assert val[0] == val[1], msg.format(key)
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_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)
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 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_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 = {p.name: float(p.sample()) for p in 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) 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 = {p.name: float(p.sample()) for p in 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) 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 = {p.name: float(p.sample()) for p in 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) 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 = {p.name: float(p.sample()) for p in 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) 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)