先读点质量透镜放大因子的精确解和匹配滤波那一章。这里不用任何探测器数据,信号和噪声全是合成的。
下载文件
把下面八个文件放进同一个空文件夹:
然后运行:
python3 -m pip install numpy scipy mpmath
python3 reproduce.py
脚本会打印与保存结果的最大差异。所有放大因子相差不超过 2×10−6、所有注入中位数相差不超过 10−6 时,退出状态为 0。我们的环境是 Python 3.9.6、numpy 2.0.2、scipy 1.13.1 和 mpmath 1.3.0。换了版本,随机噪声的末几位可能变化,严格比对就会失败;这时用 python3 reproduce.py --write 保存你自己的结果,中位数应当和我们的接近。
单位与约定
太阳质量参数取 IAU 2015 名义值 1.3271244×1020m3s−2,再除以 c3。
第一步:闭式解
point_lens_reference.py 用 mpmath 以 60 位有效数字计算 Takahashi 和 Nakamura(2003)的式 (17),以及他们的两像公式式 (18)。式 (17) 完全按印出的形式使用。Takahashi 和 Nakamura 2003,第 2 节
第二步:完全不用闭式解的第二条路线
independent_fresnel_integral.py 从透镜平面积分重新出发,把透镜平面上的点按到达时间分组。设 A(τ) 是 T<τ 的区域面积,则
第二个等号用到 ∫0∞eiwτdτ=i/w。I(τ) 是透镜对一个尖脉冲的响应,没有透镜时等于 1,也就是 2003 年论文式 (16) 的时域图像。
脚本分四步算 I(τ):
- 对从透镜出发的每个方向,在 lnr 上用二分法找到 T=τ 的位置,射线上极小值两侧各找一个。
- 用 240 点高斯–勒让德求积对方向积分,先做变量替换,消掉等值线闭合处的平方根奇点。
- 到达时间超过鞍点后,把求积节点集中到透镜另一侧,那里等值线收窄。
- 对列表中的 I(τ) 按分段线性函数做精确傅里叶积分,τ=400 以外的尾部用 c1/τ+c2/τ2 拟合补上。
这条路线完全不碰伽马函数和超几何函数,所以实部和虚部都对上,检验的就不只是式 (17) 的模,还有它的相位约定。
结果。 在 y∈{0.1,0.3,1,3}、w∈{0.1,1,3,10,30} 上,最大相对差是 4.2×10−4,出现在 y=0.3、w=30。每个 y 拟合出的尾部系数都是 c1=0.499。
全部 20 行都在 expected-outputs.json 里。
到达时间里的常数在这里很要紧。如果照 2003 年论文印出的式 (12) 在 T 里写 +ϕm(y),菲涅耳路线得到的 F 会多出相位 e2iwϕm,就和式 (17) 对不上了。论文正文说最早到达时间取为零,这要求写 −ϕm。我们按这个做,除此之外不对论文下任何结论。
第三步:两个极限
这些检验用的是闭式解,它已经在第二步得到验证。
- 低频。 ∣F−1∣ 应当几乎与 w 成正比地减小,因为有 wlnw 项,减得稍慢一点。y=1 时,w=10−2、10−3、10−4 对应 2.2×10−2、3.3×10−3、4.4×10−4。y=0.1、w=10−4 时是 5.13×10−4,一阶公式 πw/4+(iw/2)[ln(w/2)+γ−2ϕm] 给出同样的值。
- 高频。 与两像公式的差除以 ∣F∣,应当随 w 增大而减小。y=1 时从 w=30 的 2.6×10−3 降到 w=1000 的 2.6×10−5。y=0.3 时,w=30、100、300、1000 依次是 3.1×10−2、6.5×10−3、8.7×10−3、1.1×10−3。100 到 300 之间的回升是真实的:剩余的差本身随 w 振荡。
第四步:注入与搜索
signal_injection.py 生成一段 8 s、每秒 1024 个采样的数据:
- 噪声。 按 Ajith(2011)式 (4.7) 着色的高斯噪声,每个频率格的实部和虚部标准差都是 Sn/(4Δf)。Ajith 2011,第 IV 节
- 信号。 10+10M⊙ 的最低阶啁啾,从 20 Hz 到 219.9 Hz,缩放到无透镜信噪比 20;透镜注入再乘上精确的 F。Cutler 和 Flanagan 1994,式 41–42
- 无透镜搜索。 黑洞质量从 9.6 到 10.4M⊙ 取 17 个值,每个都对幅度、到达时间和相位取最大。
- 透镜搜索。 透镜质量在 10 到 1500M⊙ 之间取 36 个值,y 在 0.1 到 3 之间取 9 个值;然后对每个 y,在最佳质量的两个相邻格点之间再细扫 48 个质量;最后对最好的两组做有界的一维优化。透镜模板保持真实的黑洞质量。
- 统计量。 ΔlnL,即最佳透镜模板与最佳无透镜模板的对数似然比之差,报告门槛取 4。
加噪声之前,脚本先检查搜索能否找回注入的透镜。两种透镜情形都找回了:透镜质量误差在 0.001% 以内,ΔlnL 和精确模板的值相差不到 10−8。
结果。
清楚的透镜不会认错。弱透镜像抛硬币,而纯噪声的尾巴能伸到弱透镜也会给出的数值。
有意保留的失败案例
- 格点太粗。 第一版只在 36 个质量格点上搜索,相邻格点相差约 15%。y=2 时延迟是 90 ms,相邻质量之间 200 Hz 处的条纹相位会差好几个弧度,所以即使没有噪声,最好的格点模板也只有 ΔlnL=0.73,而不是 6.03。那一版弱透镜的中位数 2.79 基本是噪声。第四步的细化修正了这个问题,无噪声检查也能再次发现它。
- 放大因子表太粗。 第一版 F 表在 w 上只有 1500 个点。y=3、w 约为 41 时,每个条纹只采到约四个点,插值误差达到 3.8%。改成 6000 个点后降到 0.24%。
- 假透镜。 120 次纯噪声试验中有 14 次越过门槛。最大的一次是种子 3058,数值 7.97,偏好 MLz≈1150、y=1.5。
- 没有包含的东西。 没有自旋、不等质量、并合、铃宕、探测器天线方向图、突发干扰,也没有透镜周围的星系;y 只在格点上搜索。这些数字用来说明方法,不是任何真实搜索的灵敏度。
相信一次重跑之前,逐项核对
- python3 reproduce.py 退出状态为 0,或者你自己用 --write 得到的结果只在与噪声有关的末位上和我们不同。
- provenance.json 里的 SHA-256 校验和与你的文件一致。
- 菲涅耳路线和式 (17) 在实部和虚部上都一致,而不只是模一致。
- 低频时 ∣F−1∣ 大致按 wlnw 减小,高频时两像误差总体在减小。
- 读任何带噪声的结果之前,无噪声搜索先找回了注入的透镜。
- 每个透镜结果旁边都报告了纯噪声的分布,并且用同一个门槛。