103 lines
3.9 KiB
Python
103 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Diagnose why ref A + cpp b works but cpp A + cpp b fails despite tiny A diff."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
from scipy import sparse
|
|
from scipy.sparse.linalg import spsolve
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
REF = ROOT / "三维matlab代码/2023-2-端口激励问题(四面体网格)/OutFile_fem4_ab"
|
|
CPP = ROOT / "3D opticsfem-master/port/Release/OutFile_fem4"
|
|
PCOEF = 2.69467866496258e15
|
|
|
|
|
|
def parse_av_line(s: str) -> complex:
|
|
s = s.strip()
|
|
if s.startswith("("):
|
|
body = s[1:-1]
|
|
a, b = body.split(",", 1)
|
|
return complex(float(a), float(b))
|
|
return complex(float(s))
|
|
|
|
|
|
def load_coo(prefix: Path) -> sparse.csr_matrix:
|
|
ai = np.loadtxt(prefix / "Ai.txt", dtype=np.int64)
|
|
aj = np.loadtxt(prefix / "Aj.txt", dtype=np.int64)
|
|
av = [parse_av_line(line) for line in (prefix / "Av.txt").read_text(encoding="utf-8", errors="ignore").splitlines() if line.strip()]
|
|
return sparse.coo_matrix((av, (ai, aj)), shape=(79616, 79616)).tocsr()
|
|
|
|
|
|
def load_b(prefix: Path) -> np.ndarray:
|
|
return np.loadtxt(prefix / "Bv_real.txt") + 1j * np.loadtxt(prefix / "Bv_imag.txt")
|
|
|
|
|
|
def main() -> int:
|
|
print("=== Load matrices ===")
|
|
A_ref = load_coo(REF)
|
|
A_cpp = load_coo(CPP)
|
|
b_ref = load_b(REF)
|
|
b_cpp = load_b(CPP)
|
|
|
|
print(f"A_ref nnz={A_ref.nnz}, A_cpp nnz={A_cpp.nnz}")
|
|
print(f"indices equal: {np.array_equal(A_ref.indices, A_cpp.indices) and np.array_equal(A_ref.indptr, A_cpp.indptr)}")
|
|
ddata = np.abs(A_ref.data - A_cpp.data)
|
|
print(f"A.data max|diff|={ddata.max():.6g}, mean|diff|={ddata.mean():.6g}")
|
|
print(f"A.data rel max|diff|={ddata.max()/max(np.abs(A_ref.data).max(),1e-30):.6g}")
|
|
print(f"b max|diff|={np.abs(b_ref-b_cpp).max():.6g}, count>1e-6={(np.abs(b_ref-b_cpp)>1e-6).sum()}")
|
|
|
|
# duplicate (i,j) in raw COO before CSR merge?
|
|
ai = np.loadtxt(REF / "Ai.txt", dtype=np.int64)
|
|
aj = np.loadtxt(REF / "Aj.txt", dtype=np.int64)
|
|
keys = np.column_stack([ai, aj])
|
|
uniq, counts = np.unique(keys, axis=0, return_counts=True)
|
|
ndup = int((counts > 1).sum())
|
|
print(f"raw COO duplicate (i,j) groups: {ndup}")
|
|
|
|
print("\n=== CRITICAL: same b_cpp, two A ===")
|
|
x_refA = spsolve(A_ref, b_cpp)
|
|
x_cppA = spsolve(A_cpp, b_cpp)
|
|
rel = np.linalg.norm(x_refA - x_cppA) / np.linalg.norm(x_refA)
|
|
print(f"||x_refA - x_cppA|| / ||x_refA|| = {rel:.6g}")
|
|
print(f"x_refA S11={x_refA[-2]/PCOEF} S21={x_refA[-1]/PCOEF}")
|
|
print(f"x_cppA S11={x_cppA[-2]/PCOEF} S21={x_cppA[-1]/PCOEF}")
|
|
|
|
print("\n=== Same A_ref, two b ===")
|
|
x1 = spsolve(A_ref, b_ref)
|
|
x2 = spsolve(A_ref, b_cpp)
|
|
print(f"||x(b_ref)-x(b_cpp)||/||x(b_ref)|| = {np.linalg.norm(x1-x2)/np.linalg.norm(x1):.6g}")
|
|
print(f"x(b_ref) S11={x1[-2]/PCOEF}")
|
|
print(f"x(b_cpp) S11={x2[-2]/PCOEF}")
|
|
|
|
print("\n=== C++ exported X ===")
|
|
x_exp = np.loadtxt(CPP / "X_real.txt") + 1j * np.loadtxt(CPP / "X_imag.txt")
|
|
free_len = 79616
|
|
x_red = x_exp # reduced? export is full 83350
|
|
# map: C++ exports full _mX; reduced solve is 79616
|
|
print(f"X size={x_exp.size}, e1={x_exp[83348]/PCOEF}, e2={x_exp[83349]/PCOEF}")
|
|
print(f"||x_cppA - x_exp[?]|| : reduced x vs full X needs free map")
|
|
|
|
# Where do A values differ most (relative)?
|
|
mask = ddata > 0
|
|
if mask.any():
|
|
rel_d = ddata[mask] / np.maximum(np.abs(A_ref.data[mask]), 1e-30)
|
|
top = np.argsort(-rel_d)[:10]
|
|
print("\nTop relative A diffs (CSR data index):")
|
|
for i in top:
|
|
print(f" idx={i} rel={rel_d[i]:.3g} abs={ddata[i]:.3g} ref={A_ref.data[i]:.6g} cpp={A_cpp.data[i]:.6g}")
|
|
|
|
# Port columns 79614,79615
|
|
for col in [79614, 79615]:
|
|
dcol = (A_ref[:, col] - A_cpp[:, col]).toarray().ravel()
|
|
nz = np.abs(dcol) > 0
|
|
print(f"\nPort col {col}: nnz diff={nz.sum()}, max|diff|={np.abs(dcol).max():.3g}")
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|