def test_pshift_fourier(self): """Test Fourier basis with prescribed phase shifts.""" # build a SignalCollection with timing model and red noise with phase shifts Tspan = self.psr.toas.max() - self.psr.toas.min() pl = utils.powerlaw(log10_A=parameter.Uniform(-18, -12), gamma=parameter.Uniform(0, 7)) ts = gp_signals.TimingModel() rn = gp_signals.FourierBasisGP(pl, components=5, Tspan=Tspan, pseed=parameter.Uniform(0, 32768)) s = ts + rn m = s(self.psr) b1 = m.signals[1].get_basis() b2 = utils.createfourierdesignmatrix_red(nmodes=5, Tspan=Tspan)("")(self.psr.toas)[0] msg = "Fourier bases incorrect (no phase shifts)" assert np.all(b1 == b2), msg b1 = m.signals[1].get_basis() b2 = utils.createfourierdesignmatrix_red(nmodes=5, Tspan=Tspan, pseed=5)("")(self.psr.toas)[0] msg = "Fourier bases incorrect (no-parameter call vs phase shift 5)" assert not np.all(b1 == b2), msg b1 = m.signals[1].get_basis(params={self.psr.name + "_red_noise_pseed": 5}) b2 = utils.createfourierdesignmatrix_red(nmodes=5, Tspan=Tspan, pseed=5)("")(self.psr.toas)[0] msg = "Fourier bases incorrect (phase shift 5)" assert np.all(b1 == b2), msg b1 = m.signals[1].get_basis(params={self.psr.name + "_red_noise_pseed": 5}) b2 = utils.createfourierdesignmatrix_red(nmodes=5, Tspan=Tspan)("")(self.psr.toas)[0] msg = "Fourier bases incorrect (phase-shift-5 call vs no phase shift)" assert not np.all(b1 == b2), msg
def test_red_noise_add(self): """Test that red noise addition only returns independent columns.""" # set up signals pl = utils.powerlaw(log10_A=parameter.Uniform(-18,-12), gamma=parameter.Uniform(1,7)) cpl = utils.powerlaw(log10_A=parameter.Uniform(-18,-12)('log10_Agw'), gamma=parameter.Uniform(1,7)('gamma_gw')) # parameters log10_A, gamma = -14.5, 4.33 log10_Ac, gammac = -15.5, 1.33 params = {'B1855+09_log10_A': log10_A, 'B1855+09_gamma': gamma, 'log10_Agw': log10_Ac, 'gamma_gw': gammac} Tmax = self.psr.toas.max() - self.psr.toas.min() tpars = [(30, 20, Tmax, Tmax), (20, 30, Tmax, Tmax), (30, 30, Tmax, Tmax), (30, 20, Tmax, 1.123*Tmax), (20, 30, Tmax, 1.123*Tmax), (30, 30, 1.123*Tmax, Tmax)] for (nf1, nf2, T1, T2) in tpars: rn = gs.FourierBasisGP(spectrum=pl, components=nf1, Tspan=T1) crn = gs.FourierBasisGP(spectrum=cpl, components=nf2, Tspan=T2) s = rn + crn rnm = s(self.psr) # set up frequencies F1, f1 = utils.createfourierdesignmatrix_red( self.psr.toas, nmodes=nf1, Tspan=T1) F2, f2 = utils.createfourierdesignmatrix_red( self.psr.toas, nmodes=nf2, Tspan=T2) # test power spectrum p1 = utils.powerlaw(f1, log10_A, gamma) p2 = utils.powerlaw(f2, log10_Ac, gammac) if T1 == T2: nf = max(2*nf1, 2*nf2) phi = np.zeros(nf) F = F1 if nf1 > nf2 else F2 phi[:2*nf1] = p1 phi[:2*nf2] += p2 F[:,] else: phi = np.concatenate((p1, p2)) F = np.hstack((F1, F2)) msg = 'Combined red noise PSD incorrect ' msg += 'for {} {} {} {}'.format(nf1, nf2, T1, T2) assert np.all(rnm.get_phi(params) == phi), msg msg = 'Combined red noise PSD inverse incorrect ' msg += 'for {} {} {} {}'.format(nf1, nf2, T1, T2) assert np.all(rnm.get_phiinv(params) == 1/phi), msg msg = 'Combined red noise Fmat incorrect ' msg += 'for {} {} {} {}'.format(nf1, nf2, T1, T2) assert np.allclose(F, rnm.get_basis(params)), msg
def test_gp_parameter(self): """Test GP basis model with parameterized basis.""" pl = utils.powerlaw(log10_A=parameter.Uniform(-18, -12), gamma=parameter.Uniform(0, 7)) basis_env = utils.createfourierdesignmatrix_env( log10_Amp=parameter.Uniform(-10, -5), t0=parameter.Uniform(4.3e9, 5e9), log10_Q=parameter.Uniform(0, 4)) basis_red = utils.createfourierdesignmatrix_red() rn_env = gp_signals.BasisGP(pl, basis_env, name="env") rn = gp_signals.BasisGP(pl, basis_red) s = rn_env + rn m = s(self.psr) # parameters log10_A, gamma = -14.5, 4.33 log10_A_env, gamma_env = -14.0, 2.5 log10_Amp, log10_Q, t0 = -7.3, np.log10(345), 55000 * 86400 params = { "B1855+09_log10_A": log10_A, "B1855+09_gamma": gamma, "B1855+09_env_log10_A": log10_A_env, "B1855+09_env_gamma": gamma_env, "B1855+09_env_log10_Q": log10_Q, "B1855+09_env_log10_Amp": log10_Amp, "B1855+09_env_t0": t0, } # get basis Fred, f2_red = utils.createfourierdesignmatrix_red(self.psr.toas, nmodes=30) Fenv, f2_env = utils.createfourierdesignmatrix_env(self.psr.toas, nmodes=30, log10_Amp=log10_Amp, log10_Q=log10_Q, t0=t0) F = np.hstack((Fenv, Fred)) phi_env = utils.powerlaw(f2_env, log10_A=log10_A_env, gamma=gamma_env) phi_red = utils.powerlaw(f2_red, log10_A=log10_A, gamma=gamma) phi = np.concatenate((phi_env, phi_red)) # basis matrix test msg = "F matrix incorrect for GP Fourier signal." assert np.allclose(F, m.get_basis(params)), msg # spectrum test msg = "Spectrum incorrect for GP Fourier signal." assert np.all(m.get_phi(params) == phi), msg # inverse spectrum test msg = "Spectrum inverse incorrect for GP Fourier signal." assert np.all(m.get_phiinv(params) == 1 / phi), msg # test shape msg = "F matrix shape incorrect" assert m.get_basis(params).shape == F.shape, msg
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_log10_A': lA1, 'B1855+09_gamma': gamma1, 'J1909-3744_log10_A': lA2, 'J1909-3744_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_gp_parameter(self): """Test GP basis model with parameterized basis.""" pl = utils.powerlaw(log10_A=parameter.Uniform(-18,-12), gamma=parameter.Uniform(0, 7)) basis_env = utils.createfourierdesignmatrix_env( log10_Amp=parameter.Uniform(-10, -5), t0=parameter.Uniform(4.3e9, 5e9), log10_Q=parameter.Uniform(0,4)) basis_red = utils.createfourierdesignmatrix_red() rn_env = gs.BasisGP(pl, basis_env, name='env') rn = gs.BasisGP(pl, basis_red) s = rn_env + rn m = s(self.psr) # parameters log10_A, gamma = -14.5, 4.33 log10_A_env, gamma_env = -14.0, 2.5 log10_Amp, log10_Q, t0 = -7.3, np.log10(345), 55000*86400 params = {'B1855+09_log10_A': log10_A, 'B1855+09_gamma': gamma, 'B1855+09_env_log10_A': log10_A_env, 'B1855+09_env_gamma': gamma_env, 'B1855+09_env_log10_Q': log10_Q, 'B1855+09_env_log10_Amp': log10_Amp, 'B1855+09_env_t0': t0} # get basis Fred, f2_red = utils.createfourierdesignmatrix_red( self.psr.toas, nmodes=30) Fenv, f2_env = utils.createfourierdesignmatrix_env( self.psr.toas, nmodes=30, log10_Amp=log10_Amp, log10_Q=log10_Q, t0=t0) F = np.hstack((Fenv, Fred)) phi_env = utils.powerlaw(f2_env, log10_A=log10_A_env, gamma=gamma_env) phi_red = utils.powerlaw(f2_red, log10_A=log10_A, gamma=gamma) phi = np.concatenate((phi_env, phi_red)) # basis matrix test msg = 'F matrix incorrect for GP Fourier signal.' assert np.allclose(F, m.get_basis(params)), msg # spectrum test msg = 'Spectrum incorrect for GP Fourier signal.' assert np.all(m.get_phi(params) == phi), msg # inverse spectrum test msg = 'Spectrum inverse incorrect for GP Fourier signal.' assert np.all(m.get_phiinv(params) == 1/phi), msg # test shape msg = 'F matrix shape incorrect' assert m.get_basis(params).shape == F.shape, msg
def FourierBasisCommonGP( spectrum, orf, coefficients=False, combine=True, components=20, Tspan=None, modes=None, name="common_fourier" ): if coefficients and Tspan is None: raise ValueError( "With coefficients=True, FourierBasisCommonGP " + "requires that you specify Tspan explicitly." ) basis = utils.createfourierdesignmatrix_red(nmodes=components, Tspan=Tspan, modes=modes) BaseClass = BasisCommonGP(spectrum, basis, orf, coefficients=coefficients, combine=combine, name=name) class FourierBasisCommonGP(BaseClass): _Tmin, _Tmax = [], [] def __init__(self, psr): super(FourierBasisCommonGP, self).__init__(psr) if Tspan is None: FourierBasisCommonGP._Tmin.append(psr.toas.min()) FourierBasisCommonGP._Tmax.append(psr.toas.max()) @signal_base.cache_call("basis_params") def _construct_basis(self, params={}): span = Tspan if Tspan is not None else max(FourierBasisCommonGP._Tmax) - min(FourierBasisCommonGP._Tmin) self._basis, self._labels = self._bases(params=params, Tspan=span) return FourierBasisCommonGP
def FourierBasisGP(spectrum, coefficients=False, combine=True, components=20, selection=Selection(selections.no_selection), Tspan=None, modes=None, name='red_noise'): """Convenience function to return a BasisGP class with a fourier basis.""" basis = utils.createfourierdesignmatrix_red(nmodes=components, Tspan=Tspan, modes=modes) BaseClass = BasisGP(spectrum, basis, coefficients=coefficients, combine=combine, selection=selection, name=name) class FourierBasisGP(BaseClass): signal_type = 'basis' signal_name = 'red noise' signal_id = name return FourierBasisGP
def FourierBasisCommonGP(spectrum, orf, components=20, Tspan=None, name=''): basis = utils.createfourierdesignmatrix_red(nmodes=components) BaseClass = BasisCommonGP(spectrum, basis, orf, name=name) class FourierBasisCommonGP(BaseClass): signal_id = 'common_fourier_' + name if name else 'common_fourier' _Tmin, _Tmax = [], [] def __init__(self, psr): super(FourierBasisCommonGP, self).__init__(psr) if Tspan is None: FourierBasisCommonGP._Tmin.append(psr.toas.min()) FourierBasisCommonGP._Tmax.append(psr.toas.max()) @base.cache_call('basis_params') def _construct_basis(self, params={}): span = (Tspan if Tspan is not None else max(FourierBasisCommonGP._Tmax) - min(FourierBasisCommonGP._Tmin)) self._basis, self._labels = self._bases(params=params, Tspan=span) return FourierBasisCommonGP
def solar_wind_perturb(toas, freqs, planetssb, sunssb, pos_t, n_earth_rho=0, n_mean=5, nmodes=20, Tspan=None, logf=False, fmin=None, fmax=None, modes=None): """ Construct DM-Solar Model fourier design matrix. :param toas: vector of time series in seconds :param planetssb: solar system bayrcenter positions :param pos_t: pulsar position as 3-vector :param freqs: radio frequencies of observations [MHz] :param n_earth_rho: electron density from the solar wind at 1 AU. :param n_earth_bins: Number of binned values of n_earth for which to fit or an array or list of bin edges to use for binned n_Earth values. In the latter case the first and last edges must encompass all TOAs and in all cases it must match the size (number of elements) of log10_n_earth. :param t_init: Initial time of earliest TOA in entire dataset, including all pulsar. :param t_final: Final time of latest TOA in entire dataset, including all pulsar. :return dt_DM: DM due to solar wind """ if modes is not None: nmodes = len(modes) #print(n_earth_rho) if n_earth_rho.size != 2 * nmodes: raise ValueError( 'Length of n_earth_rho must match 2 x nmodes.') F, Ffreqs = utils.createfourierdesignmatrix_red(toas, nmodes=nmodes, Tspan=Tspan, logf=logf, fmin=fmin, fmax=fmax, modes=modes) n_Earth = np.einsum('ij,j', F, n_earth_rho) #np.repeat(10**n_earth_rho,2)) # if np.any(np.logical_or(n_Earth<0,n_Earth>50)): # dt_sw = np.zeros_like(n_Earth) # else: theta, R_earth, _, _ = SW.theta_impact(planetssb, sunssb, pos_t) dm_sol_wind = SW.dm_solar(1.0, theta, R_earth) dt_sw = n_Earth * dm_sol_wind * 4.148808e3 / freqs**2 return dt_sw
def test_fourier_red_noise(self): """Test that red noise signal returns correct values.""" # set up signal parameter pl = utils.powerlaw(log10_A=parameter.Uniform(-18,-12), gamma=parameter.Uniform(1,7)) rn = gs.FourierBasisGP(spectrum=pl, components=30) rnm = rn(self.psr) # parameters log10_A, gamma = -14.5, 4.33 params = {'B1855+09_log10_A': log10_A, 'B1855+09_gamma': gamma} # basis matrix test F, f2 = utils.createfourierdesignmatrix_red( self.psr.toas, nmodes=30) msg = 'F matrix incorrect for GP Fourier signal.' assert np.allclose(F, rnm.get_basis(params)), msg # spectrum test phi = utils.powerlaw(f2, log10_A=log10_A, gamma=gamma) msg = 'Spectrum incorrect for GP Fourier signal.' assert np.all(rnm.get_phi(params) == phi), msg # inverse spectrum test msg = 'Spectrum inverse incorrect for GP Fourier signal.' assert np.all(rnm.get_phiinv(params) == 1/phi), msg # test shape msg = 'F matrix shape incorrect' assert rnm.get_basis(params).shape == F.shape, msg
def test_fourier_red_user_freq_array(self): """Test that red noise signal returns correct values with user defined frequency array.""" # set parameters log10_A, gamma = -14.5, 4.33 params = {"B1855+09_red_noise_log10_A": log10_A, "B1855+09_red_noise_gamma": gamma} F, f2 = utils.createfourierdesignmatrix_red(self.psr.toas, nmodes=30) # set up signal model. use list of frequencies to make basis pl = utils.powerlaw(log10_A=parameter.Uniform(-18, -12), gamma=parameter.Uniform(1, 7)) rn = gp_signals.FourierBasisGP(spectrum=pl, modes=f2[::2]) rnm = rn(self.psr) # basis matrix test msg = "F matrix incorrect for GP Fourier signal." assert np.allclose(F, rnm.get_basis(params)), msg # spectrum test phi = utils.powerlaw(f2, log10_A=log10_A, gamma=gamma) msg = "Spectrum incorrect for GP Fourier signal." assert np.all(rnm.get_phi(params) == phi), msg # inverse spectrum test msg = "Spectrum inverse incorrect for GP Fourier signal." assert np.all(rnm.get_phiinv(params) == 1 / phi), msg # test shape msg = "F matrix shape incorrect" assert rnm.get_basis(params).shape == F.shape, msg
def test_fourier_red_noise_pshift(self): """Test that red noise signal returns correct values.""" # set up signal parameter pl = utils.powerlaw(log10_A=parameter.Uniform(-18, -12), gamma=parameter.Uniform(1, 7)) rn = gp_signals.FourierBasisGP(spectrum=pl, components=30, pshift=True, pseed=42) rnm = rn(self.psr) # parameters log10_A, gamma = -14.5, 4.33 params = {"B1855+09_red_noise_log10_A": log10_A, "B1855+09_red_noise_gamma": gamma} # basis matrix test F, f2 = utils.createfourierdesignmatrix_red(self.psr.toas, nmodes=30, pshift=True, pseed=42) msg = "F matrix incorrect for GP Fourier signal." assert np.allclose(F, rnm.get_basis(params)), msg # spectrum test phi = utils.powerlaw(f2, log10_A=log10_A, gamma=gamma) msg = "Spectrum incorrect for GP Fourier signal." assert np.all(rnm.get_phi(params) == phi), msg # inverse spectrum test msg = "Spectrum inverse incorrect for GP Fourier signal." assert np.all(rnm.get_phiinv(params) == 1 / phi), msg # test shape msg = "F matrix shape incorrect" assert rnm.get_basis(params).shape == F.shape, msg
def createfourierdesignmatrix_solar_dm(toas, freqs, planetssb, pos_t, modes=None, nmodes=30, Tspan=None, logf=True, fmin=None, fmax=None): """ Construct DM-Solar Model fourier design matrix. :param toas: vector of time series in seconds :param planetssb: solar system bayrcenter positions :param pos_t: pulsar position as 3-vector :param nmodes: number of fourier coefficients to use :param freqs: radio frequencies of observations [MHz] :param Tspan: option to some other Tspan :param logf: use log frequency spacing :param fmin: lower sampling frequency :param fmax: upper sampling frequency :return: F: SW DM-variation fourier design matrix :return: f: Sampling frequencies """ # get base fourier design matrix and frequencies F, Ffreqs = utils.createfourierdesignmatrix_red(toas, nmodes=nmodes, modes=modes, Tspan=Tspan, logf=logf, fmin=fmin, fmax=fmax) theta, R_earth = theta_impact(planetssb,pos_t) dm_sol_wind = dm_solar(1.0, theta, R_earth) dt_DM = dm_sol_wind * 4.148808e3 /(freqs**2) return F * dt_DM[:, None], Ffreqs
def test_fourier_red_noise_backend(self): """Test that red noise-backend signal returns correct values.""" # set up signal parameter pl = utils.powerlaw(log10_A=parameter.Uniform(-18, -12), gamma=parameter.Uniform(1, 7)) selection = Selection(selections.by_backend) rn = gp_signals.FourierBasisGP(spectrum=pl, components=30, selection=selection) rnm = rn(self.psr) # parameters log10_As = [-14, -14.4, -15, -14.8] gammas = [2.3, 4.4, 1.8, 5.6] params = { "B1855+09_red_noise_430_ASP_gamma": gammas[0], "B1855+09_red_noise_430_PUPPI_gamma": gammas[1], "B1855+09_red_noise_L-wide_ASP_gamma": gammas[2], "B1855+09_red_noise_L-wide_PUPPI_gamma": gammas[3], "B1855+09_red_noise_430_ASP_log10_A": log10_As[0], "B1855+09_red_noise_430_PUPPI_log10_A": log10_As[1], "B1855+09_red_noise_L-wide_ASP_log10_A": log10_As[2], "B1855+09_red_noise_L-wide_PUPPI_log10_A": log10_As[3], } # get the basis bflags = self.psr.backend_flags Fmats, fs, phis = [], [], [] for ct, flag in enumerate(np.unique(bflags)): mask = bflags == flag F, f = utils.createfourierdesignmatrix_red(self.psr.toas[mask], 30) Fmats.append(F) fs.append(f) phis.append(utils.powerlaw(f, log10_As[ct], gammas[ct])) nf = sum(F.shape[1] for F in Fmats) F = np.zeros((len(self.psr.toas), nf)) phi = np.hstack([p for p in phis]) nftot = 0 for ct, flag in enumerate(np.unique(bflags)): mask = bflags == flag nn = Fmats[ct].shape[1] F[mask, nftot:nn + nftot] = Fmats[ct] nftot += nn msg = "F matrix incorrect for GP Fourier backend signal." assert np.allclose(F, rnm.get_basis(params)), msg # spectrum test msg = "Spectrum incorrect for GP Fourier backend signal." assert np.all(rnm.get_phi(params) == phi), msg # inverse spectrum test msg = "Spectrum inverse incorrect for GP Fourier backend signal." assert np.all(rnm.get_phiinv(params) == 1 / phi), msg # test shape msg = "F matrix shape incorrect" assert rnm.get_basis(params).shape == F.shape, msg
def test_fourier_red_noise_backend(self): """Test that red noise-backend signal returns correct values.""" # set up signal parameter pl = utils.powerlaw(log10_A=parameter.Uniform(-18,-12), gamma=parameter.Uniform(1,7)) selection = Selection(selections.by_backend) rn = gs.FourierBasisGP(spectrum=pl, components=30, selection=selection) rnm = rn(self.psr) # parameters log10_As = [-14, -14.4, -15, -14.8] gammas = [2.3, 4.4, 1.8, 5.6] params = {'B1855+09_430_ASP_gamma': gammas[0], 'B1855+09_430_PUPPI_gamma': gammas[1], 'B1855+09_L-wide_ASP_gamma': gammas[2], 'B1855+09_L-wide_PUPPI_gamma': gammas[3], 'B1855+09_430_ASP_log10_A': log10_As[0], 'B1855+09_430_PUPPI_log10_A': log10_As[1], 'B1855+09_L-wide_ASP_log10_A': log10_As[2], 'B1855+09_L-wide_PUPPI_log10_A': log10_As[3]} # get the basis bflags = self.psr.backend_flags Fmats, fs, phis = [], [], [] for ct, flag in enumerate(np.unique(bflags)): mask = bflags == flag F, f = utils.createfourierdesignmatrix_red( self.psr.toas[mask], 30) Fmats.append(F) fs.append(f) phis.append(utils.powerlaw(f, log10_As[ct], gammas[ct])) nf = sum(F.shape[1] for F in Fmats) F = np.zeros((len(self.psr.toas), nf)) phi = np.hstack(p for p in phis) nftot = 0 for ct, flag in enumerate(np.unique(bflags)): mask = bflags == flag nn = Fmats[ct].shape[1] F[mask, nftot:nn+nftot] = Fmats[ct] nftot += nn msg = 'F matrix incorrect for GP Fourier backend signal.' assert np.allclose(F, rnm.get_basis(params)), msg # spectrum test msg = 'Spectrum incorrect for GP Fourier backend signal.' assert np.all(rnm.get_phi(params) == phi), msg # inverse spectrum test msg = 'Spectrum inverse incorrect for GP Fourier backend signal.' assert np.all(rnm.get_phiinv(params) == 1/phi), msg # test shape msg = 'F matrix shape incorrect' assert rnm.get_basis(params).shape == F.shape, msg
def FourierBasisCommonGP( spectrum, orf, coefficients=False, combine=True, components=20, Tspan=None, modes=None, name="common_fourier", pshift=False, pseed=None, ): if coefficients and Tspan is None: raise ValueError("With coefficients=True, FourierBasisCommonGP " + "requires that you specify Tspan explicitly.") basis = utils.createfourierdesignmatrix_red(nmodes=components, Tspan=Tspan, modes=modes, pshift=pshift, pseed=pseed) BaseClass = BasisCommonGP(spectrum, basis, orf, coefficients=coefficients, combine=combine, name=name) class FourierBasisCommonGP(BaseClass): _Tmin, _Tmax = [], [] def __init__(self, psr): super(FourierBasisCommonGP, self).__init__(psr) if Tspan is None: FourierBasisCommonGP._Tmin.append(psr.toas.min()) FourierBasisCommonGP._Tmax.append(psr.toas.max()) # since this function has side-effects, it can only be cached # with limit=1, so it will run again if called with params different # than the last time @signal_base.cache_call("basis_params", 1) def _construct_basis(self, params={}): span = Tspan if Tspan is not None else max( FourierBasisCommonGP._Tmax) - min(FourierBasisCommonGP._Tmin) self._basis, self._labels = self._bases(params=params, Tspan=span) return FourierBasisCommonGP
def FourierBasisGP(spectrum, components=20, selection=Selection(selections.no_selection), Tspan=None, name=''): """Convenience function to return a BasisGP class with a fourier basis.""" basis = utils.createfourierdesignmatrix_red(nmodes=components, Tspan=Tspan) BaseClass = BasisGP(spectrum, basis, selection=selection, name=name) class FourierBasisGP(BaseClass): signal_type = 'basis' signal_name = 'red noise' signal_id = 'red_noise_' + name if name else 'red_noise' return FourierBasisGP
def setUpClass(cls): """Setup the Pulsar object.""" # initialize Pulsar class cls.psr = Pulsar(datadir + "/B1855+09_NANOGrav_9yv1.gls.par", datadir + "/B1855+09_NANOGrav_9yv1.tim") cls.F, _ = utils.createfourierdesignmatrix_red(cls.psr.toas, nmodes=30) cls.Fdm, _ = utils.createfourierdesignmatrix_dm(cls.psr.toas, freqs=cls.psr.freqs, nmodes=30) cls.Feph, cls.feph = utils.createfourierdesignmatrix_ephem( cls.psr.toas, cls.psr.pos, nmodes=30) cls.Mm = utils.create_stabletimingdesignmatrix(cls.psr.Mmat)
def setUpClass(cls): """Setup the Pulsar object.""" # initialize Pulsar class cls.psr = Pulsar(datadir + '/B1855+09_NANOGrav_9yv1.gls.par', datadir + '/B1855+09_NANOGrav_9yv1.tim') cls.F, _ = utils.createfourierdesignmatrix_red( cls.psr.toas, nmodes=30) cls.Fdm, _ = utils.createfourierdesignmatrix_dm( cls.psr.toas,freqs=cls.psr.freqs, nmodes=30) tmp = utils.createfourierdesignmatrix_eph(t=cls.psr.toas, phi=cls.psr.phi, theta=cls.psr.theta, nmodes=30) cls.Fx, cls.Fy, cls.Fz = tmp cls.Mm = utils.create_stabletimingdesignmatrix(cls.psr.Mmat)
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['{}_log10_A'.format(pname)]) gamma.append(params['{}_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
ch_b = ['ASP', 'GASP', 'GUPPI', 'PUPPI', 'NUPPI'] flagvals = filter(lambda x: any(map(lambda y: y in x, ch_b)), flagvals) return {flagval: backend_flags == flagval for flagval in flagvals} selection_ch = selections.Selection(channelized_backends) selection = selections.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_ch) # red noise (powerlaw with 5 frequencies) pl = utils.powerlaw(log10_A=log10_A, gamma=gamma) basis = utils.createfourierdesignmatrix_red(Tspan=Tspan, nmodes=args.nfreqs, logf=args.logf) rn = gp_signals.BasisGP(priorFunction=pl, name='red_noise', basisFunction=basis) # timing model tm = gp_signals.TimingModel(use_svd=False) model = tm + ef + eq + ec + rn if args.dm_gp_psrs[0] == args.psr: dm_basis = utils.createfourierdesignmatrix_dm(Tspan=Tspan, nmodes=args.nfreqs, logf=args.logf) dm_gp = gp_signals.BasisGP(priorFunction=pl,
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 test_pta(self): # 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) # setup basic model efac = parameter.Constant() equad = parameter.Constant() ecorr = parameter.Constant() log10_A = parameter.Constant() gamma = parameter.Constant() selection = Selection(selections.by_backend) ms = white_signals.MeasurementNoise(efac=efac, log10_t2equad=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) s = ms + ec + rn pta = s(self.psrs[0]) + s(self.psrs[1]) # set parameters pta.set_default_params(params) # get parameters efacs, equads, ecorrs, log10_A, gamma = [], [], [], [], [] for pname in [p.name for p in self.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)]) # correct value tflags = [sorted(list(np.unique(p.backend_flags))) for p in self.psrs] cfs, logdets, phis = [], [], [] for ii, (psr, flags) in enumerate(zip(self.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 + 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) phi = utils.powerlaw(f2, log10_A=log10_A[ii], gamma=gamma[ii]) phis.append(phi) # tests Ns = pta.get_ndiag(params) pphis = pta.get_phi(params) pphiinvs = pta.get_phiinv(params) Ts = pta.get_basis(params) zipped = zip(logdets, cfs, phis, self.psrs, Ns, pphis, pphiinvs, Ts) for logdet, cf, phi, psr, N, pphi, pphiinv, T in zipped: msg = "EFAC/ECORR logdet incorrect." assert np.allclose(N.solve(psr.residuals, logdet=True)[1], logdet, rtol=1e-10), msg msg = "EFAC/ECORR D1 solve incorrect." assert np.allclose(N.solve(psr.residuals), sl.cho_solve(cf, psr.residuals), rtol=1e-10), msg msg = "EFAC/ECORR 1D1 solve incorrect." assert np.allclose( N.solve(psr.residuals, left_array=psr.residuals), np.dot(psr.residuals, sl.cho_solve(cf, psr.residuals)), rtol=1e-10, ), msg msg = "EFAC/ECORR 2D1 solve incorrect." assert np.allclose(N.solve(psr.residuals, left_array=T), np.dot(T.T, sl.cho_solve(cf, psr.residuals)), rtol=1e-10), msg msg = "EFAC/ECORR 2D2 solve incorrect." assert np.allclose(N.solve(T, left_array=T), np.dot(T.T, sl.cho_solve(cf, T)), rtol=1e-10), msg # spectrum test msg = "Spectrum incorrect for GP Fourier signal." assert np.all(pphi == phi), msg # inverse spectrum test msg = "Spectrum inverse incorrect for GP Fourier signal." assert np.all(pphiinv == 1 / phi), msg
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_red_noise_add(self): """Test that red noise addition only returns independent columns.""" # set up signals pl = utils.powerlaw(log10_A=parameter.Uniform(-18, -12), gamma=parameter.Uniform(1, 7)) cpl = utils.powerlaw( log10_A=parameter.Uniform(-18, -12)("log10_Agw"), gamma=parameter.Uniform(1, 7)("gamma_gw") ) # parameters log10_A, gamma = -14.5, 4.33 log10_Ac, gammac = -15.5, 1.33 params = { "B1855+09_red_noise_log10_A": log10_A, "B1855+09_red_noise_gamma": gamma, "log10_Agw": log10_Ac, "gamma_gw": gammac, } Tmax = self.psr.toas.max() - self.psr.toas.min() tpars = [ (30, 20, Tmax, Tmax), (20, 30, Tmax, Tmax), (30, 30, Tmax, Tmax), (30, 20, Tmax, 1.123 * Tmax), (20, 30, Tmax, 1.123 * Tmax), (30, 30, 1.123 * Tmax, Tmax), ] for (nf1, nf2, T1, T2) in tpars: rn = gp_signals.FourierBasisGP(spectrum=pl, components=nf1, Tspan=T1) crn = gp_signals.FourierBasisGP(spectrum=cpl, components=nf2, Tspan=T2) s = rn + crn rnm = s(self.psr) # set up frequencies F1, f1 = utils.createfourierdesignmatrix_red(self.psr.toas, nmodes=nf1, Tspan=T1) F2, f2 = utils.createfourierdesignmatrix_red(self.psr.toas, nmodes=nf2, Tspan=T2) # test power spectrum p1 = utils.powerlaw(f1, log10_A, gamma) p2 = utils.powerlaw(f2, log10_Ac, gammac) if T1 == T2: nf = max(2 * nf1, 2 * nf2) phi = np.zeros(nf) F = F1 if nf1 > nf2 else F2 phi[: 2 * nf1] = p1 phi[: 2 * nf2] += p2 F[ :, ] # noqa: E231 else: phi = np.concatenate((p1, p2)) F = np.hstack((F1, F2)) msg = "Combined red noise PSD incorrect " msg += "for {} {} {} {}".format(nf1, nf2, T1, T2) assert np.all(rnm.get_phi(params) == phi), msg msg = "Combined red noise PSD inverse incorrect " msg += "for {} {} {} {}".format(nf1, nf2, T1, T2) assert np.all(rnm.get_phiinv(params) == 1 / phi), msg msg = "Combined red noise Fmat incorrect " msg += "for {} {} {} {}".format(nf1, nf2, T1, T2) assert np.allclose(F, rnm.get_basis(params)), msg
def test_red_noise_add_backend(self): """Test that red noise with backend addition only returns independent columns.""" # set up signals pl = utils.powerlaw(log10_A=parameter.Uniform(-18,-12), gamma=parameter.Uniform(1,7)) selection = Selection(selections.by_backend) cpl = utils.powerlaw(log10_A=parameter.Uniform(-18,-12)('log10_Agw'), gamma=parameter.Uniform(1,7)('gamma_gw')) # parameters log10_As = [-14, -14.4, -15, -14.8] gammas = [2.3, 4.4, 1.8, 5.6] log10_Ac, gammac = -15.5, 1.33 params = {'B1855+09_430_ASP_gamma': gammas[0], 'B1855+09_430_PUPPI_gamma': gammas[1], 'B1855+09_L-wide_ASP_gamma': gammas[2], 'B1855+09_L-wide_PUPPI_gamma': gammas[3], 'B1855+09_430_ASP_log10_A': log10_As[0], 'B1855+09_430_PUPPI_log10_A': log10_As[1], 'B1855+09_L-wide_ASP_log10_A': log10_As[2], 'B1855+09_L-wide_PUPPI_log10_A': log10_As[3], 'log10_Agw': log10_Ac, 'gamma_gw': gammac} Tmax = self.psr.toas.max() - self.psr.toas.min() tpars = [(30, 20, Tmax, Tmax), (20, 30, Tmax, Tmax), (30, 30, Tmax, Tmax), (30, 20, Tmax, 1.123*Tmax), (20, 30, Tmax, 1.123*Tmax), (30, 30, 1.123*Tmax, Tmax), (30, 20, None, Tmax)] for (nf1, nf2, T1, T2) in tpars: rn = gs.FourierBasisGP(spectrum=pl, components=nf1, Tspan=T1, selection=selection) crn = gs.FourierBasisGP(spectrum=cpl, components=nf2, Tspan=T2) s = rn + crn rnm = s(self.psr) # get the basis bflags = self.psr.backend_flags Fmats, fs, phis = [], [], [] F2, f2 = utils.createfourierdesignmatrix_red( self.psr.toas, nf2, Tspan=T2) p2 = utils.powerlaw(f2, log10_Ac, gammac) for ct, flag in enumerate(np.unique(bflags)): mask = bflags == flag F1, f1 = utils.createfourierdesignmatrix_red( self.psr.toas[mask], nf1, Tspan=T1) Fmats.append(F1) fs.append(f1) phis.append(utils.powerlaw(f1, log10_As[ct], gammas[ct])) Fmats.append(F2) phis.append(p2) nf = sum(F.shape[1] for F in Fmats) F = np.zeros((len(self.psr.toas), nf)) phi = np.hstack(p for p in phis) nftot = 0 for ct, flag in enumerate(np.unique(bflags)): mask = bflags == flag nn = Fmats[ct].shape[1] F[mask, nftot:nn+nftot] = Fmats[ct] nftot += nn F[:, -2*nf2:] = F2 msg = 'Combined red noise PSD incorrect ' msg += 'for {} {} {} {}'.format(nf1, nf2, T1, T2) assert np.all(rnm.get_phi(params) == phi), msg msg = 'Combined red noise PSD inverse incorrect ' msg += 'for {} {} {} {}'.format(nf1, nf2, T1, T2) assert np.all(rnm.get_phiinv(params) == 1/phi), msg msg = 'Combined red noise Fmat incorrect ' msg += 'for {} {} {} {}'.format(nf1, nf2, T1, T2) assert np.allclose(F, rnm.get_basis(params)), msg
def test_combine_signals(self): """Test for combining different signals.""" # set up signal parameter ecorr = parameter.Uniform(-10, -5) ec = gs.EcorrBasisModel(log10_ecorr=ecorr) pl = utils.powerlaw(log10_A=parameter.Uniform(-18,-12), gamma=parameter.Uniform(1,7)) rn = gs.FourierBasisGP(spectrum=pl, components=30) 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 = gs.BasisGP(prior, basis, name='se') ts = gs.TimingModel() s = ec + rn + ts + se m = s(self.psr) # parameters ecorr = -6.4 log10_A, gamma = -14.5, 4.33 log10_lam, log10_sigma = 7.4, -6.4 params = {'B1855+09_log10_ecorr': ecorr, 'B1855+09_log10_A': log10_A, 'B1855+09_gamma': gamma, 'B1855+09_se_log10_lam': log10_lam, 'B1855+09_se_log10_sigma': log10_sigma} # combined basis matrix U = utils.create_quantization_matrix(self.psr.toas)[0] M = self.psr.Mmat.copy() norm = np.sqrt(np.sum(M**2, axis=0)) M /= norm F, f2 = utils.createfourierdesignmatrix_red( self.psr.toas, nmodes=30) U2, avetoas = create_quant_matrix(self.psr.toas, dt=7*86400) T = np.hstack((U, F, M, U2)) # combined prior vector jvec = 10**(2*ecorr) * np.ones(U.shape[1]) phim = np.ones(self.psr.Mmat.shape[1]) * 1e40 phi = utils.powerlaw(f2, log10_A=log10_A, gamma=gamma) K = se_kernel(avetoas, log10_lam=log10_lam, log10_sigma=log10_sigma) phivec = np.concatenate((jvec, phi, phim)) phi = sl.block_diag(np.diag(phivec), K) phiinv = np.linalg.inv(phi) # basis matrix test msg = 'Basis matrix incorrect for combined signal.' assert np.allclose(T, m.get_basis(params)), msg # Kernal test msg = 'Prior matrix incorrect for combined signal.' assert np.allclose(m.get_phi(params), phi), msg # inverse Kernel test msg = 'Prior matrix inverse incorrect for combined signal.' assert np.allclose(m.get_phiinv(params), phiinv), msg # test shape msg = 'Basis matrix shape incorrect size for combined signal.' assert m.get_basis(params).shape == T.shape, msg
def test_single_pulsar(self): # get parameters from PAL2 style noise files params = get_noise_from_pal2(datadir + "/B1855+09_noise.txt") # setup basic model efac = parameter.Constant() equad = parameter.Constant() ecorr = parameter.Constant() log10_A = parameter.Constant() gamma = parameter.Constant() selection = Selection(selections.by_backend) ms = white_signals.MeasurementNoise(efac=efac, log10_t2equad=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) s = ms + ec + rn m = s(self.psrs[0]) # set parameters m.set_default_params(params) # get parameters efacs = [params[key] for key in sorted(params.keys()) if "efac" in key] equads = [ params[key] for key in sorted(params.keys()) if "equad" in key ] ecorrs = [ params[key] for key in sorted(params.keys()) if "ecorr" in key ] log10_A = params["B1855+09_red_noise_log10_A"] gamma = params["B1855+09_red_noise_gamma"] # correct value flags = ["430_ASP", "430_PUPPI", "L-wide_ASP", "L-wide_PUPPI"] nvec0 = np.zeros_like(self.psrs[0].toas) for ct, flag in enumerate(np.unique(flags)): ind = flag == self.psrs[0].backend_flags nvec0[ind] = efacs[ct]**2 * ( self.psrs[0].toaerrs[ind]**2 + 10**(2 * equads[ct]) * np.ones(np.sum(ind))) # get the basis bflags = self.psrs[0].backend_flags Umats = [] for flag in np.unique(bflags): mask = bflags == flag Umats.append( utils.create_quantization_matrix(self.psrs[0].toas[mask])[0]) nepoch = sum(U.shape[1] for U in Umats) U = np.zeros((len(self.psrs[0].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[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]))) # test msg = "EFAC/ECORR logdet incorrect." N = m.get_ndiag(params) assert np.allclose(N.solve(self.psrs[0].residuals, logdet=True)[1], logdet, rtol=1e-10), msg msg = "EFAC/ECORR D1 solve incorrect." assert np.allclose(N.solve(self.psrs[0].residuals), sl.cho_solve(cf, self.psrs[0].residuals), rtol=1e-10), msg msg = "EFAC/ECORR 1D1 solve incorrect." assert np.allclose( N.solve(self.psrs[0].residuals, left_array=self.psrs[0].residuals), np.dot(self.psrs[0].residuals, sl.cho_solve(cf, self.psrs[0].residuals)), rtol=1e-10, ), msg msg = "EFAC/ECORR 2D1 solve incorrect." T = m.get_basis(params) assert np.allclose( N.solve(self.psrs[0].residuals, left_array=T), np.dot(T.T, sl.cho_solve(cf, self.psrs[0].residuals)), rtol=1e-10, ), msg msg = "EFAC/ECORR 2D2 solve incorrect." assert np.allclose(N.solve(T, left_array=T), np.dot(T.T, sl.cho_solve(cf, T)), rtol=1e-10), msg F, f2 = utils.createfourierdesignmatrix_red(self.psrs[0].toas, nmodes=20) # spectrum test phi = utils.powerlaw(f2, log10_A=log10_A, gamma=gamma) msg = "Spectrum incorrect for GP Fourier signal." assert np.all(m.get_phi(params) == phi), msg # inverse spectrum test msg = "Spectrum inverse incorrect for GP Fourier signal." assert np.all(m.get_phiinv(params) == 1 / phi), msg
def test_red_noise_add_backend(self): """Test that red noise with backend addition only returns independent columns.""" # set up signals pl = utils.powerlaw(log10_A=parameter.Uniform(-18, -12), gamma=parameter.Uniform(1, 7)) selection = Selection(selections.by_backend) cpl = utils.powerlaw( log10_A=parameter.Uniform(-18, -12)("log10_Agw"), gamma=parameter.Uniform(1, 7)("gamma_gw") ) # parameters log10_As = [-14, -14.4, -15, -14.8] gammas = [2.3, 4.4, 1.8, 5.6] log10_Ac, gammac = -15.5, 1.33 params = { "B1855+09_red_noise_430_ASP_gamma": gammas[0], "B1855+09_red_noise_430_PUPPI_gamma": gammas[1], "B1855+09_red_noise_L-wide_ASP_gamma": gammas[2], "B1855+09_red_noise_L-wide_PUPPI_gamma": gammas[3], "B1855+09_red_noise_430_ASP_log10_A": log10_As[0], "B1855+09_red_noise_430_PUPPI_log10_A": log10_As[1], "B1855+09_red_noise_L-wide_ASP_log10_A": log10_As[2], "B1855+09_red_noise_L-wide_PUPPI_log10_A": log10_As[3], "log10_Agw": log10_Ac, "gamma_gw": gammac, } Tmax = self.psr.toas.max() - self.psr.toas.min() tpars = [ (30, 20, Tmax, Tmax), (20, 30, Tmax, Tmax), (30, 30, Tmax, Tmax), (30, 20, Tmax, 1.123 * Tmax), (20, 30, Tmax, 1.123 * Tmax), (30, 30, 1.123 * Tmax, Tmax), (30, 20, None, Tmax), ] for (nf1, nf2, T1, T2) in tpars: rn = gp_signals.FourierBasisGP(spectrum=pl, components=nf1, Tspan=T1, selection=selection) crn = gp_signals.FourierBasisGP(spectrum=cpl, components=nf2, Tspan=T2) s = rn + crn rnm = s(self.psr) # get the basis bflags = self.psr.backend_flags Fmats, fs, phis = [], [], [] F2, f2 = utils.createfourierdesignmatrix_red(self.psr.toas, nf2, Tspan=T2) p2 = utils.powerlaw(f2, log10_Ac, gammac) for ct, flag in enumerate(np.unique(bflags)): mask = bflags == flag F1, f1 = utils.createfourierdesignmatrix_red(self.psr.toas[mask], nf1, Tspan=T1) Fmats.append(F1) fs.append(f1) phis.append(utils.powerlaw(f1, log10_As[ct], gammas[ct])) Fmats.append(F2) phis.append(p2) nf = sum(F.shape[1] for F in Fmats) F = np.zeros((len(self.psr.toas), nf)) phi = np.hstack([p for p in phis]) nftot = 0 for ct, flag in enumerate(np.unique(bflags)): mask = bflags == flag nn = Fmats[ct].shape[1] F[mask, nftot : nn + nftot] = Fmats[ct] nftot += nn F[:, -2 * nf2 :] = F2 msg = "Combined red noise PSD incorrect " msg += "for {} {} {} {}".format(nf1, nf2, T1, T2) assert np.all(rnm.get_phi(params) == phi), msg msg = "Combined red noise PSD inverse incorrect " msg += "for {} {} {} {}".format(nf1, nf2, T1, T2) assert np.all(rnm.get_phiinv(params) == 1 / phi), msg msg = "Combined red noise Fmat incorrect " msg += "for {} {} {} {}".format(nf1, nf2, T1, T2) assert np.allclose(F, rnm.get_basis(params)), 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_combine_signals(self): """Test for combining different signals.""" # set up signal parameter ecorr = parameter.Uniform(-10, -5) ec = gp_signals.EcorrBasisModel(log10_ecorr=ecorr) pl = utils.powerlaw(log10_A=parameter.Uniform(-18, -12), gamma=parameter.Uniform(1, 7)) rn = gp_signals.FourierBasisGP(spectrum=pl, components=30) 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") ts = gp_signals.TimingModel() s = ec + rn + ts + se m = s(self.psr) # parameters ecorr = -6.4 log10_A, gamma = -14.5, 4.33 log10_lam, log10_sigma = 7.4, -6.4 params = { "B1855+09_basis_ecorr_log10_ecorr": ecorr, "B1855+09_red_noise_log10_A": log10_A, "B1855+09_red_noise_gamma": gamma, "B1855+09_se_log10_lam": log10_lam, "B1855+09_se_log10_sigma": log10_sigma, } # combined basis matrix U = utils.create_quantization_matrix(self.psr.toas)[0] M = self.psr.Mmat.copy() norm = np.sqrt(np.sum(M ** 2, axis=0)) M /= norm F, f2 = utils.createfourierdesignmatrix_red(self.psr.toas, nmodes=30) U2, avetoas = create_quant_matrix(self.psr.toas, dt=7 * 86400) T = np.hstack((U, F, M, U2)) # combined prior vector jvec = 10 ** (2 * ecorr) * np.ones(U.shape[1]) phim = np.ones(self.psr.Mmat.shape[1]) * 1e40 phi = utils.powerlaw(f2, log10_A=log10_A, gamma=gamma) K = se_kernel(avetoas, log10_lam=log10_lam, log10_sigma=log10_sigma) phivec = np.concatenate((jvec, phi, phim)) phi = sl.block_diag(np.diag(phivec), K) phiinv = np.linalg.inv(phi) # basis matrix test msg = "Basis matrix incorrect for combined signal." assert np.allclose(T, m.get_basis(params)), msg # Kernal test msg = "Prior matrix incorrect for combined signal." assert np.allclose(m.get_phi(params), phi), msg # inverse Kernel test msg = "Prior matrix inverse incorrect for combined signal." assert np.allclose(m.get_phiinv(params), phiinv), msg # test shape msg = "Basis matrix shape incorrect size for combined signal." assert m.get_basis(params).shape == T.shape, 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_log10_A': lA1, 'B1855+09_gamma': gamma1, 'J1909-3744_log10_A': lA2, 'J1909-3744_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