Full Fisher matrix with jax backend

import os
os.environ["XLA_PYTHON_CLIENT_PREALLOCATE"] = "false"
os.environ["JAX_COMPILATION_CACHE_DIR"] = "/home/nrui/.cache/jax"
os.environ["JAX_PERSISTENT_CACHE_MIN_COMPILE_TIME_SECS"] = "10"

import numpy as np
import pandas as pd
import jax
jax.config.update("jax_enable_x64", True)

import lal
import bilby

from pespace.detector.antenna import InterferometerAntenna, FDResponseModelMarset2018
from pespace.detector.tdi import TDIChannelData, FDMichelsonConstantEqualArm
from pespace.detector.orbit import KaplerianHeliocentric, KeplerianGeocentric
from pespace.inference.likelihood_jax import WhittleLikelihoodJax
from pespace.inference.fisher import (
    fisher_matrix,
    sample_from_fisher,
    wrap_angle_params,
)
from pespace.waveform.interface_ripple import IMRPhenomXAS
from pespace.waveform.interface_ripple import IMRPhenomXHM
/tmp/ipykernel_1255073/2682543860.py:11: UserWarning: Wswiglal-redir-stdio:

SWIGLAL standard output/error redirection is enabled in IPython.
This may lead to performance penalties. To disable locally, use:

with lal.no_swig_redirect_standard_output_error():
    ...

To disable globally, use:

lal.swig_redirect_standard_output_error(False)

Note however that this will likely lead to error messages from
LAL functions being either misdirected or lost when called from
Jupyter notebooks.

To suppress this warning, use:

import warnings
warnings.filterwarnings("ignore", "Wswiglal-redir-stdio")
import lal

  import lal
[Taichi] version 1.7.4, llvm 15.0.4, commit b4b956fd, linux, python 3.12.14
[I 10/09/26 11:05:33.915 1255073] [shell.py:_shell_pop_print@23] Graphical python shell detected, using wrapped sys.stdout
tdi_gen = "2.0"
channels = ("A", "E", "T")

dt = 10
f_min = 1e-4
f_max = 0.5 * (1 / dt)
f_ref = f_min
t_start = 0.0

num_tsamples = 2 ** np.ceil(np.log2(7 * lal.DAYJUL_SI / dt))
duration = num_tsamples * dt
before_tc = 0.8 * duration
tc = t_start + before_tc

params = dict(
    total_mass=3e6,
    mass_ratio=0.6,
    chi_1=0.75,
    chi_2=0.62,
    luminosity_distance=56000.0,
    inclination=0.4,
    reference_phase=1.3,
    ecliptic_longitude=1.375,
    ecliptic_latitude=-1.2108,
    polarization=2.659,
    coalescence_time=tc,
)
params = bilby.gw.conversion.generate_mass_parameters(params)
params
{'total_mass': 3000000.0,
 'mass_ratio': 0.6,
 'chi_1': 0.75,
 'chi_2': 0.62,
 'luminosity_distance': 56000.0,
 'inclination': 0.4,
 'reference_phase': 1.3,
 'ecliptic_longitude': 1.375,
 'ecliptic_latitude': -1.2108,
 'polarization': 2.659,
 'coalescence_time': np.float64(524288.0),
 'mass_1': 1875000.0,
 'mass_2': 1125000.0,
 'chirp_mass': 1256226.717491785,
 'symmetric_mass_ratio': np.float64(0.234375)}
corner_parameters = [
    'chirp_mass',
    'mass_ratio',
    'chi_1',
    'chi_2',
    'luminosity_distance',
    'inclination',
    'reference_phase',
    'ecliptic_longitude',
    'ecliptic_latitude',
    'polarization',
    'coalescence_time',
]
latex_labels = {
    'chirp_mass':r"$\mathcal{M}$",
    'mass_ratio':r"$q$",
    'chi_1':r"$\chi_1$",
    'chi_2':r"$\chi_2$",
    'luminosity_distance':r"$d_L$",
    'inclination':r"$\iota$",
    'reference_phase':r"$\phi_{\mathrm{ref}}$",
    'ecliptic_longitude':r"$\lambda$",
    'ecliptic_latitude':r"$\beta$",
    'polarization':r"$\psi$",
    'coalescence_time':r"$t_c$",
}
pe_results= {
    'xas_lisa': ('../pe/outdir_inj_MBHB_LISA_noiseless', 'inj_MBHB_LISA_noiseless'),
    'xas_network': ('../pe/outdir_inj_MBHB_network_noiseless', 'inj_MBHB_network_noiseless'),
    'xhm_lisa': ('../pe/outdir_inj_MBHB_LISA_HM_noiseless_old', 'inj_MBHB_LISA_HM_noiseless'),
    'xhm_network': ('../pe/outdir_inj_MBHB_network_HM_noiseless', 'inj_MBHB_network_HM_noiseless'),
}
tdi_data = TDIChannelData(backend="jax")
tdi_data.set_fd_data_from_zero(
    channels=channels,
    duration=duration,
    delta_time=dt,
    start_time=t_start,
    minimum_frequency=f_min,
    maximum_frequency=f_max,
)
tdi_data.set_fd_noise_power_density_from_model("LISA_SciRDv1", tdi_generation=tdi_gen)

lisa = InterferometerAntenna(
    name="lisa",
    tdi_data=tdi_data,
    orbit_model=KaplerianHeliocentric(2.5e9, 0.0, -np.pi / 9, backend="jax"),
    response_model=FDResponseModelMarset2018(backend="jax"),
    tdi_combination=FDMichelsonConstantEqualArm(
        generation=tdi_gen, orthogonal=True, backend="jax"
    ),
    backend="jax",
)

wf_xas = IMRPhenomXAS(tdi_data.frequency_samples, f_ref)
likelihood_xas_lisa = WhittleLikelihoodJax(waveform=wf_xas, detector=lisa, channels=channels)
# fiducial parameters as a dict, ordered as likelihood_xas_lisa.likelihood_param_type
fiducial_xas_lisa = {
    name: params[name] for name in likelihood_xas_lisa.likelihood_param_type._fields
}
fisher_xas_lisa = fisher_matrix(likelihood_xas_lisa, fiducial_xas_lisa)

theta0_xas_lisa = np.array(list(fiducial_xas_lisa.values()))
num_samples = 100_000
draws_xas_lisa = theta0_xas_lisa + sample_from_fisher(fisher_xas_lisa, num_samples, rkey_seed=2026)
samples_xas_lisa = {name: draws_xas_lisa[:, i] for i, name in enumerate(fiducial_xas_lisa)}
samples_xas_lisa = wrap_angle_params(samples_xas_lisa)

samples_xas_lisa = {name: np.asarray(values) for name, values in samples_xas_lisa.items()}
samples_xas_lisa['mass_ratio'] = bilby.gw.conversion.symmetric_mass_ratio_to_mass_ratio(samples_xas_lisa["symmetric_mass_ratio"])
samples_xas_lisa = pd.DataFrame(samples_xas_lisa)
res_fisher_xas_lisa = bilby.core.result.Result(
    label="fisher_xas_lisa",
    search_parameter_keys=corner_parameters,
    posterior=samples_xas_lisa,
)
fig_xas_lisa = res_fisher_xas_lisa.plot_corner(
    parameters=corner_parameters,
    labels=[latex_labels[name] for name in corner_parameters],
    save=False,
    titles=False,
    quantiles=[0.05, 0.95],
)
../_images/6819c3195d909f6f74aae1df750fb35fcb9b343f6831dcbe12e7b0e5b4c6bd58.png
import os
os.environ['PATH'] = "/home/nrui/.local/texlive/2025/bin/x86_64-linux:" + os.environ['PATH']

import matplotlib 
plot_params = {
    'axes.labelsize': 20,
    'font.family': 'serif',
    'font.serif': 'Computer Modern',
    # 'font.size': 24,
    'legend.fontsize': 40,
    'xtick.labelsize': 16,
    'ytick.labelsize': 16,
    # 'axes.grid' : False,
    'text.usetex': True,
    # 'savefig.dpi' : 100,
    # 'lines.markersize' : 14,
    }
matplotlib.rcParams.update(plot_params)
res_pe_xas_lisa = bilby.core.result.read_in_result(outdir=pe_results['xas_lisa'][0], label=pe_results['xas_lisa'][1])
fig_cmp_xas_lisa = bilby.core.result.plot_multiple(
    [res_fisher_xas_lisa, res_pe_xas_lisa],
    filename='corner_fisher_xas_lisa.pdf',
    labels=["Fisher estimation", "Posterior samples (noise-free)"],
    titles=False,
    parameters=corner_parameters,
    save=False,
    quantiles=[0.05, 0.95],
    corner_labels=[latex_labels[name] for name in corner_parameters],
    label_kwargs={'fontsize': 26},
)

leg = fig_cmp_xas_lisa.axes[10].get_legend()
leg.set_loc("upper right")
leg.set_bbox_to_anchor((0.95, 0.9), transform=fig_cmp_xas_lisa.transFigure)

fig_cmp_xas_lisa.suptitle(r"\texttt{PhenomXAS} + LISA", fontsize=60, y=0.95)
fig_cmp_xas_lisa.savefig('corner_fisher_xas_lisa.pdf')
../_images/283b85e0284524cf75e85cc014b928edc9ff3dfe89169cc4a111901ef944e563.png

IMRPhenomXHM + LISA

wf_xhm = IMRPhenomXHM(tdi_data.frequency_samples, f_ref)
likelihood_xhm_lisa = WhittleLikelihoodJax(waveform=wf_xhm, detector=lisa, channels=channels)
fiducial_xhm_lisa = {
    name: params[name] for name in likelihood_xhm_lisa.likelihood_param_type._fields
}
fisher_xhm_lisa = np.asarray(fisher_matrix(likelihood_xhm_lisa, fiducial_xhm_lisa))

theta0_xhm_lisa = np.array(list(fiducial_xhm_lisa.values()))
draws_xhm_lisa = theta0_xhm_lisa + sample_from_fisher(fisher_xhm_lisa, num_samples, rkey_seed=2026)
samples_xhm_lisa = {name: draws_xhm_lisa[:, i] for i, name in enumerate(fiducial_xhm_lisa)}
samples_xhm_lisa = wrap_angle_params(samples_xhm_lisa)
samples_xhm_lisa = {name: np.asarray(values) for name, values in samples_xhm_lisa.items()}

samples_xhm_lisa['chirp_mass'] = bilby.gw.conversion.component_masses_to_chirp_mass(
    samples_xhm_lisa["mass_1"], samples_xhm_lisa["mass_2"]
)
samples_xhm_lisa['mass_ratio'] = bilby.gw.conversion.component_masses_to_mass_ratio(
    samples_xhm_lisa["mass_1"], samples_xhm_lisa["mass_2"]
)

res_fisher_xhm_lisa = bilby.core.result.Result(
    label="fisher_xhm_lisa",
    search_parameter_keys=corner_parameters,
    posterior=pd.DataFrame(samples_xhm_lisa),
)

res_pe_xhm_lisa = bilby.core.result.read_in_result(outdir=pe_results['xhm_lisa'][0], label=pe_results['xhm_lisa'][1])
fig_cmp_xhm_lisa = bilby.core.result.plot_multiple(
    [res_fisher_xhm_lisa, res_pe_xhm_lisa],
    filename='corner_fisher_xhm_lisa.pdf',
    labels=["Fisher estimation", "Posterior samples (noise-free)"],
    titles=False,
    parameters=corner_parameters,
    save=False,
    quantiles=[0.05, 0.95],
    corner_labels=[latex_labels[name] for name in corner_parameters],
    label_kwargs={'fontsize': 26},
)

leg = fig_cmp_xhm_lisa.axes[10].get_legend()
leg.set_loc("upper right")
leg.set_bbox_to_anchor((0.95, 0.9), transform=fig_cmp_xhm_lisa.transFigure)

fig_cmp_xhm_lisa.suptitle(r"\texttt{PhenomXHM} + LISA", fontsize=60, y=0.95)
fig_cmp_xhm_lisa.savefig('corner_fisher_xhm_lisa.pdf')
../_images/be7f991609ad5059f49bb8c9afe3e5e982b65bdcbb283b1d1593360a8eb2d6b1.png

IMRPhenomXHM + Network

det_models = {
    "lisa": ("LISA_SciRDv1", KaplerianHeliocentric(2.5e9, 0.0, -np.pi / 9, backend="jax")),
    "taiji": ("Taiji_TDC", KaplerianHeliocentric(3.0e9, 0.0, np.pi / 9, backend="jax")),
    "tianqin": ("Tianqin_GWSpace", KeplerianGeocentric(1.7e8, 0.0, 0.0, backend="jax")),
}
det_net = []
for name, (psd_model, orbit_model) in det_models.items():
    tdi_data_det = TDIChannelData(backend="jax")
    tdi_data_det.set_fd_data_from_zero(
        channels=channels,
        duration=duration,
        delta_time=dt,
        start_time=t_start,
        minimum_frequency=f_min,
        maximum_frequency=f_max,
    )
    tdi_data_det.set_fd_noise_power_density_from_model(psd_model, tdi_generation=tdi_gen)
    det_net.append(
        InterferometerAntenna(
            name=name,
            tdi_data=tdi_data_det,
            orbit_model=orbit_model,
            response_model=FDResponseModelMarset2018(backend="jax"),
            tdi_combination=FDMichelsonConstantEqualArm(
                generation=tdi_gen, orthogonal=True, backend="jax"
            ),
            backend="jax",
        )
    )

likelihood_xhm_net = WhittleLikelihoodJax(
    waveform=wf_xhm, detector=tuple(det_net), channels=channels
)
fiducial_xhm_net = {
    name: params[name] for name in likelihood_xhm_net.likelihood_param_type._fields
}
fisher_xhm_net = np.asarray(fisher_matrix(likelihood_xhm_net, fiducial_xhm_net))

theta0_xhm_net = np.array(list(fiducial_xhm_net.values()))
draws_xhm_net = theta0_xhm_net + sample_from_fisher(fisher_xhm_net, num_samples, rkey_seed=2026)
samples_xhm_net = {name: draws_xhm_net[:, i] for i, name in enumerate(fiducial_xhm_net)}
samples_xhm_net = wrap_angle_params(samples_xhm_net)
samples_xhm_net = {name: np.asarray(values) for name, values in samples_xhm_net.items()}

samples_xhm_net['chirp_mass'] = bilby.gw.conversion.component_masses_to_chirp_mass(
    samples_xhm_net["mass_1"], samples_xhm_net["mass_2"]
)
samples_xhm_net['mass_ratio'] = bilby.gw.conversion.component_masses_to_mass_ratio(
    samples_xhm_net["mass_1"], samples_xhm_net["mass_2"]
)

res_fisher_xhm_net = bilby.core.result.Result(
    label="fisher_xhm_network",
    search_parameter_keys=corner_parameters,
    posterior=pd.DataFrame(samples_xhm_net),
)

res_pe_xhm_net = bilby.core.result.read_in_result(outdir=pe_results['xhm_network'][0], label=pe_results['xhm_network'][1])
fig_cmp_xhm_net = bilby.core.result.plot_multiple(
    [res_fisher_xhm_net, res_pe_xhm_net],
    filename='corner_fisher_xhm_network.pdf',
    labels=["Fisher estimation", "Posterior samples (noise-free)"],
    titles=False,
    parameters=corner_parameters,
    save=False,
    quantiles=[0.05, 0.95],
    corner_labels=[latex_labels[name] for name in corner_parameters],
    label_kwargs={'fontsize': 26},
)

leg = fig_cmp_xhm_net.axes[10].get_legend()
leg.set_loc("upper right")
leg.set_bbox_to_anchor((0.95, 0.9), transform=fig_cmp_xhm_net.transFigure)

fig_cmp_xhm_net.suptitle(r"\texttt{PhenomXHM} + Network", fontsize=60, y=0.95)
fig_cmp_xhm_net.savefig('corner_fisher_xhm_network.pdf')
../_images/0be59066510b35f3ae4c5bd82d75287ef284ca32e12f9547ff17be403c7dc5cf.png

IMRPhenomXAS + Network

likelihood_xas_net = WhittleLikelihoodJax(
    waveform=wf_xas, detector=tuple(det_net), channels=channels
)
fiducial_xas_net = {
    name: params[name] for name in likelihood_xas_net.likelihood_param_type._fields
}
fisher_xas_net = np.asarray(fisher_matrix(likelihood_xas_net, fiducial_xas_net))

theta0_xas_net = np.array(list(fiducial_xas_net.values()))
draws_xas_net = theta0_xas_net + sample_from_fisher(fisher_xas_net, num_samples, rkey_seed=2026)
samples_xas_net = {name: draws_xas_net[:, i] for i, name in enumerate(fiducial_xas_net)}
samples_xas_net = wrap_angle_params(samples_xas_net)
samples_xas_net = {name: np.asarray(values) for name, values in samples_xas_net.items()}

samples_xas_net['mass_ratio'] = bilby.gw.conversion.symmetric_mass_ratio_to_mass_ratio(
    samples_xas_net["symmetric_mass_ratio"]
)

res_fisher_xas_net = bilby.core.result.Result(
    label="fisher_xas_network",
    search_parameter_keys=corner_parameters,
    posterior=pd.DataFrame(samples_xas_net),
)

res_pe_xas_net = bilby.core.result.read_in_result(outdir=pe_results['xas_network'][0], label=pe_results['xas_network'][1])
fig_cmp_xas_net = bilby.core.result.plot_multiple(
    [res_fisher_xas_net, res_pe_xas_net],
    filename='corner_fisher_xas_network.pdf',
    labels=["Fisher estimation", "Posterior samples (noise-free)"],
    titles=False,
    parameters=corner_parameters,
    save=False,
    quantiles=[0.05, 0.95],
    corner_labels=[latex_labels[name] for name in corner_parameters],
    label_kwargs={'fontsize': 26},
)

leg = fig_cmp_xas_net.axes[10].get_legend()
leg.set_loc("upper right")
leg.set_bbox_to_anchor((0.95, 0.9), transform=fig_cmp_xas_net.transFigure)

fig_cmp_xas_net.suptitle(r"\texttt{PhenomXAS} + Network", fontsize=60, y=0.95)
fig_cmp_xas_net.savefig('corner_fisher_xas_network.pdf')
../_images/3b331b3af19c27d09171611393453196b7b905bd786d912d90862647e07398d4.png