feat: OVF formula 67

This commit is contained in:
mayge
2025-09-17 02:45:10 -04:00
parent 1b21b940a4
commit e1bc69c6fb
10 changed files with 724 additions and 446 deletions

174
core/orthonormal_basis.py Normal file
View File

@@ -0,0 +1,174 @@
import numpy as np
# ------------------------------------------------------------
# 按论文公式 (9)(10)(11) 生成 MuntzLaguerre 正交有理基 (解析形式)
#
# 给定稳定极点集合 {p_k} (Re(p_k)<0)。论文记法中使用 -a_k其中 Re(a_k)>0。
# 对应关系p_k = -a_k ⇒ a_k = -p_k, Re(a_k)= -Re(p_k) >0
#
# 连续内积意义下(沿 jω 轴积分)这些 φ_k 解析正交。离散频率采样后数值上
# 可能偏离,可再用加权 QR 做数值再正交(可选)。
#
# 公式在“稳定极点 p 表达”下的改写:
# (原) 实极点: φ_p(s) = sqrt(2 Re(a_p)) / (s + a_p) * Π (s - a_i^*)/(s + a_i)
# 变换 a_p = -p ⇒ Re(a_p)= -Re(p) = σ >0 且 (s + a_p) = (s - p)
# 且乘积 (s - a_i^*)/(s + a_i) = (s + p_i^*)/(s - p_i)
# ⇒ φ_p(s) = sqrt(-2 Re(p)) / (s - p) * Π_{i<p} (s + p_i^*)/(s - p_i)
#
# 复对 (p, p*),取 imag(p)>0 的 p 作为首:
# (原) φ_p = sqrt(2 Re(a_p)) (s - |a_p|)/[(s + a_p)(s + a_p^*)] * Π(...)
# (原) φ_{p+1}= sqrt(2 Re(a_p)) (s + |a_p|)/[(s + a_p)(s + a_p^*)] * Π(...)
# 代入 a_p=-p
# Re(a_p)= -Re(p)=σ>0, (s + a_p) = (s - p), (s + a_p^*)=(s - p^*)
# |a_p| = |p|
# 乘积同上 ⇒ Π_{i<p} (s + p_i^*)/(s - p_i)
#
# ⇒ φ_p(s) = sqrt(-2 Re(p)) (s - |p|)/[(s - p)(s - p^*)] * Π_{i<p} (s + p_i^*)/(s - p_i)
# φ_{p^*}(s)= sqrt(-2 Re(p)) (s + |p|)/[(s - p)(s - p^*)] * Π_{i<p} (s + p_i^*)/(s - p_i)
#
# product 递推维护prod_k = Π_{i≤k} (s + p_i^*)/(s - p_i)
# 对复对要顺序乘两次p 与 p*)。
# ------------------------------------------------------------
def generate_laguerre_basis(poles:list|np.ndarray, s: np.ndarray):
basis = np.zeros((len(poles)+1,len(s)),dtype=complex)
product = np.ones(len(s),dtype=complex)
basis[0] = np.ones(len(s),dtype=complex) # φ_0 = 1
i = 0
while i < len(poles):
if np.real(poles[i]) >= 0:
raise ValueError(f"极点必须在左半平面: {poles[i]}")
# 复对首 (正虚部)
if np.iscomplex(poles[i]) and np.imag(poles[i]) > 0:
if i + 1 >= len(poles):
raise ValueError("复极点缺少共轭")
pc = poles[i + 1]
if not np.isclose(pc, np.conj(poles[i])):
raise ValueError("复极点未按 (p, p*) 顺序排列 (正虚部在前)")
sigma = -np.real(poles[i]) # >0
scale = np.sqrt(2 * sigma)
r = np.abs(poles[i])
denom = (s - poles[i]) * (s - pc)
# 两个基函数
phi_p = scale * (s - r) / denom * product
phi_pc = scale * (s + r) / denom * product
# product 先乘 (s + p^*)/(s - p),再乘 (s + p)/(s - p^*)
product = product * (s + pc) / (s - poles[i])
product = product * (s + poles[i]) / (s - pc)
basis[i + 1] = phi_p
basis[i + 2] = phi_pc
i += 2
continue
# 复对次 (负虚部) —— 应该被首元素处理,出现表示顺序错误
if np.iscomplex(poles[i]) and np.imag(poles[i]) < 0:
raise ValueError("检测到负虚部复极点但其共轭尚未处理,请将正虚部成员放在前面。")
# 实极点
sigma = -np.real(poles[i])
if sigma <= 0:
raise ValueError("实极点实部应为负 (稳定)。")
scale = np.sqrt(2 * sigma)
phi = scale / (s - poles[i]) * product
# 更新乘积
product = product * (s + poles[i]) / (s - poles[i])
i += 1
basis[i + 1] = phi
return basis
# class MuntzLaguerreIterator:
# def __init__(self, s: np.ndarray, stable_poles: list | np.ndarray):
# """
# s: 复频率数组 (Nf,), s = j 2π f
# stable_poles: 稳定极点列表 (Re<0). 复共轭对要求正虚部在前 (p, p*).
# """
# self.s = np.asarray(s, dtype=complex)
# self.poles = list(stable_poles)
# self.N = len(self.poles)
# self.k = 0
# # 初始化乘积 Π_{i<p} (s + p_i^*)/(s - p_i)
# self.product = np.ones_like(self.s, dtype=complex)
# def __iter__(self):
# return self
# def __next__(self):
# if self.k >= self.N:
# raise StopIteration
# p = self.poles[self.k]
# if np.real(p) >= 0:
# raise ValueError(f"极点必须在左半平面: {p}")
# # 复对首 (正虚部)
# if np.iscomplex(p) and np.imag(p) > 0:
# if self.k + 1 >= self.N:
# raise ValueError("复极点缺少共轭")
# pc = self.poles[self.k + 1]
# if not np.isclose(pc, np.conj(p)):
# raise ValueError("复极点未按 (p, p*) 顺序排列 (正虚部在前)")
# sigma = -np.real(p) # >0
# scale = np.sqrt(2 * sigma)
# r = np.abs(p)
# denom = (self.s - p) * (self.s - pc)
# # 两个基函数
# phi_p = scale * (self.s - r) / denom * self.product
# phi_pc = scale * (self.s + r) / denom * self.product
# # product 先乘 (s + p^*)/(s - p),再乘 (s + p)/(s - p^*)
# self.product = self.product * (self.s + pc) / (self.s - p)
# self.product = self.product * (self.s + p) / (self.s - pc)
# self.k += 2
# return [phi_p, phi_pc]
# # 复对次 (负虚部) —— 应该被首元素处理,出现表示顺序错误
# if np.iscomplex(p) and np.imag(p) < 0:
# raise ValueError("检测到负虚部复极点但其共轭尚未处理,请将正虚部成员放在前面。")
# # 实极点
# sigma = -np.real(p)
# if sigma <= 0:
# raise ValueError("实极点实部应为负 (稳定)。")
# scale = np.sqrt(2 * sigma)
# phi = scale / (self.s - p) * self.product
# # 更新乘积
# self.product = self.product * (self.s + p) / (self.s - p)
# self.k += 1
# return [phi]
# def generate_muntz_laguerre_basis(s: np.ndarray, init_poles: list | np.ndarray):
# """
# 生成完整基函数列表: [φ_0=1, φ_1, φ_2, ...]
# """
# basis = [np.ones_like(s, dtype=complex)]
# for block in MuntzLaguerreIterator(s, init_poles):
# basis.extend(block)
# return basis
# if __name__ == "__main__":
# # 示例稳定极点 (复对正虚部在前)
# stable_poles = [
# -0.8e9,
# -1.0e9 + 2.5e9j,
# -1.0e9 - 2.5e9j,
# -2.2e9
# ]
# freqs = np.linspace(1e8, 8e9, 400)
# s = 1j * 2 * np.pi * freqs
# basis = generate_muntz_laguerre_basis(s, stable_poles)
# print(f"生成 {len(basis)} 个基函数,{basis}")

View File

@@ -1,250 +1,326 @@
import numpy as np
from dataclasses import dataclass
from typing import List, Sequence, Tuple, Optional
from typing import List, Dict, Tuple
@dataclass
class OPVFConfig:
n_poles: int
max_iter: int = 5
tol: float = 1e-3
add_constraint: bool = True
real_split: bool = True
lambda_reg: float = 0.0
verbose: bool = True
# ---------- 参数正交基 ----------
class ParamOrthoBasis:
def __init__(self, params: np.ndarray, total_degree: int):
# params: (Ns, d)
self.mean = params.mean(0)
self.std = params.std(0) + 1e-15
self.exps = self._gen_exps(params.shape[1], total_degree)
M = self._build_monomial_matrix(params) # (Ns,M)
Q,R = np.linalg.qr(M)
self.Q = Q
self.R = R
def _gen_exps(self, dim, D):
exps=[]
def rec(cur, i, rem):
if i==dim:
exps.append(tuple(cur)); return
for k in range(rem+1):
cur.append(k); rec(cur, i+1, rem-k); cur.pop()
rec([],0,D)
return exps
def _build_monomial_matrix(self, params):
X = (params - self.mean)/self.std
Ns = X.shape[0]
M = np.zeros((Ns, len(self.exps)))
for idx,e in enumerate(self.exps):
v = np.ones(Ns)
for j,p in enumerate(e):
if p: v *= X[:,j]**p
M[:,idx]=v
return M
def eval(self, g: np.ndarray) -> np.ndarray:
x = (g - self.mean)/self.std
mono=[]
for e in self.exps:
val=1.0
for j,p in enumerate(e):
if p: val*= x[j]**p
mono.append(val)
mono=np.array(mono)
# φ = mono @ R^{-1}
return mono @ np.linalg.inv(self.R)
# ---------- 频率正交(有理)基 ----------
class RationalFreqBasis:
def __init__(self, freqs: np.ndarray, poles: np.ndarray):
# 构造原始矩阵 G: 列0 常数 1, 后面 1/(s-a_k)
s = 1j*2*np.pi*freqs
cols=[np.ones_like(s)]
for a in poles:
cols.append(1.0/(s - a))
G = np.vstack(cols).T # (Nf, K+1)
# 加权可加 w_f这里统一 1
Q, R = np.linalg.qr(G)
self.Q = Q # Φ(f) (Nf, K+1) 频率正交基取样
self.R = R # 变换: G = Q R
self.freqs = freqs
def eval(self) -> np.ndarray:
return self.Q # 直接返回已正交基取样 (Nf, K+1)
# ---------- 主模型 ----------
class OrthonormalParametricVF:
# ---------- 1. 生成初始极点(按论文 Re(-a_p) 小、Im 线性分布) ----------
def generate_starting_poles(n_pairs: int, beta_min: float, beta_max: float, alpha_scale: float = 0.01):
"""
H(s, μ) = N(s,μ)/D(s,μ)
N(s,μ)= Σ_k c_k(μ) φ_k(s); D(s,μ)= Σ_k ĉ_k(μ) φ_k(s)
其中 φ_k(s) 是频率正交有理基; c_k(μ), ĉ_k(μ) 在参数正交基上展开:
c_k(μ)= Σ_q C[k,q] ψ_q(μ); ĉ_k(μ)= Σ_q Ctilde[k,q] ψ_q(μ)
仅生成复共轭对: p = -alpha + j beta, p*。
n_pairs: 复对数量 (总极点数 = 2*n_pairs)
beta_min,beta_max: 想要覆盖的虚部范围 (单位: rad/s)
alpha_scale: alpha = alpha_scale * beta (文中 {α_p}=0.01{β_p})
返回: list[complex] (正虚部先, 后跟共轭)
"""
def __init__(self, cfg: OPVFConfig, freq_basis: RationalFreqBasis, param_basis: ParamOrthoBasis):
self.cfg = cfg
self.fb = freq_basis
self.pb = param_basis
Kp1 = self.fb.Q.shape[1]
Qp = self.pb.Q.shape[1]
self.C = np.zeros((Kp1, Qp), dtype=complex) # 分子
self.Ct = np.zeros((Kp1, Qp), dtype=complex) # 分母
# 初始化分母ĉ_0 ≈ 1其余 0
self.Ct[0,0] = 1.0
betas = 2*np.pi*np.linspace(beta_min, beta_max, n_pairs)
poles = []
for b in betas:
alpha = alpha_scale * b
p = -alpha + 1j * b
poles += [p, np.conj(p)]
print(f"生成 {len(poles)} 个初始极点 (复对) {poles}]")
return poles
def _assemble_phi_param(self, params: np.ndarray) -> np.ndarray:
# 返回 (Ns, Qp)
return self.pb.Q
# ---------- 2. MuntzLaguerre 频率基 (与你现有 orthonormal_basis 中一致的核心) ----------
def muntz_laguerre_basis(freqs_hz: np.ndarray, stable_poles: List[complex]) -> np.ndarray:
"""
返回 Φ_ml (Nf, P) 列顺序: φ_0=1, 后续依次 (单极点 / 复对两列)
"""
s = 1j * 2 * np.pi * freqs_hz
basis = [np.ones_like(s, dtype=complex)]
product = np.ones_like(s, dtype=complex)
k = 0
N = len(stable_poles)
while k < N:
p = stable_poles[k]
if np.real(p) >= 0:
raise ValueError("极点需在左半平面")
if np.imag(p) > 0: # 复对首
if k + 1 >= N or not np.isclose(stable_poles[k+1], np.conj(p)):
raise ValueError("复对未配对")
pc = stable_poles[k+1]
sigma = -np.real(p)
scale = np.sqrt(2 * sigma)
r = np.abs(p)
denom = (s - p) * (s - pc)
phi_p = scale * (s - r) / denom * product
phi_pc = scale * (s + r) / denom * product
basis.extend([phi_p, phi_pc])
# update product
product = product * (s + pc)/(s - p) * (s + p)/(s - pc)
k += 2
elif np.imag(p) < 0:
raise ValueError("负虚部复极点必须跟在正虚部之后")
else: # 实极点
sigma = -np.real(p)
scale = np.sqrt(2 * sigma)
phi = scale / (s - p) * product
basis.append(phi)
product = product * (s + p)/(s - p)
k += 1
return np.column_stack(basis) # (Nf, P)
def fit(self, H_samples: List[np.ndarray], params: np.ndarray):
"""
H_samples: list 长度 Ns, 每个 (Nf,) 复
params: (Ns,d)
"""
Φf = self.fb.eval() # (Nf, K+1)
Ns = len(H_samples)
Nf, Kp1 = Φf.shape
Φμ = self._assemble_phi_param(params) # (Ns, Qp)
Qp = Φμ.shape[1]
# ---------- 3. 原始部分分式基 ----------
def raw_partial_fraction_basis(freqs_hz: np.ndarray, stable_poles: List[complex]) -> np.ndarray:
s = 1j * 2 * np.pi * freqs_hz
cols = [np.ones_like(s, dtype=complex)]
for p in stable_poles:
cols.append(1.0 / (s - p))
return np.column_stack(cols) # (Nf, P)
# t=0: 只解 C (式3, D=1)
self._solve_t0(H_samples, Φf, Φμ)
# ---------- 4. 变换矩阵 Φ_ml T = G_raw ----------
def compute_transform_matrix(Phi_ml: np.ndarray, G_raw: np.ndarray, weights: np.ndarray | None = None) -> np.ndarray:
# 加权最小二乘 T = (Φ^H W Φ)^{-1} Φ^H W G
if weights is None:
WPhi = Phi_ml
WG = G_raw
M = Phi_ml.conj().T @ WPhi
RHS = Phi_ml.conj().T @ WG
else:
w = weights[:, None]
M = Phi_ml.conj().T @ (w * Phi_ml)
RHS = Phi_ml.conj().T @ (w * G_raw)
T = np.linalg.solve(M, RHS)
return T # (P,P), Φ T = G
D_prev = self._eval_D(Φf, Φμ) # (Nf, Ns)
# ---------- 5. 参数多项式正交基 ----------
def generate_param_basis(param_matrix: np.ndarray, total_degree: int):
"""
param_matrix: (Ns, d)
返回:
Qμ: (Ns, V) 正交列
Rμ: (V, V) 上三角
exps: 多指数列表
"""
X = param_matrix
Ns, d = X.shape
# 生成多指数
exps=[]
def rec(cur,i,rem):
if i==d:
exps.append(tuple(cur)); return
for k in range(rem+1):
cur.append(k); rec(cur,i+1,rem-k); cur.pop()
rec([],0,total_degree)
V = len(exps)
M = np.zeros((Ns, V))
for idx,e in enumerate(exps):
v = np.ones(Ns)
for j,p in enumerate(e):
if p: v *= X[:,j]**p
M[:,idx]=v
Q,R = np.linalg.qr(M)
return Q,R,exps
for it in range(1, self.cfg.max_iter+1):
A, b = self._build_iter_system(H_samples, Φf, Φμ, D_prev)
if self.cfg.lambda_reg>0:
lam = self.cfg.lambda_reg
A = np.vstack([A, lam*np.eye(A.shape[1])])
b = np.concatenate([b, np.zeros(A.shape[1])])
x, *_ = np.linalg.lstsq(A, b, rcond=None)
self._unpack_iter_solution(x, Kp1, Qp)
D_new = self._eval_D(Φf, Φμ)
rel = np.max(np.abs(D_new-D_prev)/(np.abs(D_prev)+1e-12))
if self.cfg.verbose:
print(f"[OPVF-Iter {it}] rel_change={rel:.3e}")
D_prev = D_new
if rel < self.cfg.tol:
if self.cfg.verbose:
print(f"[OPVF] converged at {it}")
break
return self
# ---------- 6. t=0 构建设计矩阵并解 C ----------
def fit_t0(H_list: List[np.ndarray], Phi_f: np.ndarray, Qμ: np.ndarray) -> np.ndarray:
"""
H_list: 长度 Ns, 每项 (Nf,) 复数
Phi_f: (Nf, P)
Qμ: (Ns, V)
返回: C (P,V)
"""
Ns = len(H_list); Nf,P = Phi_f.shape; V = Qμ.shape[1]
cols = P*V
A = np.zeros((Ns*Nf, cols), dtype=complex)
b = np.zeros(Ns*Nf, dtype=complex)
r=0
for n,Hn in enumerate(H_list):
blk = np.einsum('fp,v->fpv', Phi_f, [n]).reshape(Nf, cols)
A[r:r+Nf,:]=blk
b[r:r+Nf]=Hn
r+=Nf
x, *_ = np.linalg.lstsq(A, b, rcond=None)
C = x.reshape(P, V)
return C
def _solve_t0(self, H_samples, Φf, Φμ):
Ns = len(H_samples); Nf,Kp1 = Φf.shape; Qp = Φμ.shape[1]
# 设计矩阵行数 Ns*Nf未知数 Kp1*Qp
cols = Kp1*Qp
A = np.zeros((Ns*Nf, cols), dtype=complex)
b = np.zeros(Ns*Nf, dtype=complex)
r = 0
for n,Hn in enumerate(H_samples):
phiμ = Φμ[n] # (Qp,)
# Φf (Nf,Kp1) 外积 phiμ -> (Nf, Kp1*Qp)
blk = np.einsum('fk,q->fkq', Φf, phiμ).reshape(Nf, cols)
A[r:r+Nf,:] = blk
b[r:r+Nf] = Hn
r += Nf
x, *_ = np.linalg.lstsq(A, b, rcond=None)
self.C = x.reshape(Kp1, Qp)
# 分母保持初始 (ĉ_0=1)
def _build_iter_system(self, H_samples, Φf, Φμ, D_prev):
Ns = len(H_samples); Nf,Kp1 = Φf.shape; Qp=Φμ.shape[1]
# 未知C (Kp1*Qp) + Ct (Kp1*Qp) 但固定 Ct[0,0]=1可去掉该变量
mask_fix = np.zeros((Kp1,Qp), dtype=bool)
mask_fix[0,0]=True
idx_map={}
# ---------- 7. t=1 (首轮 SK) 解 C, Ct ----------
def fit_t1_SK(H_list: List[np.ndarray], Phi_f: np.ndarray, Qμ: np.ndarray,
C_prev: np.ndarray, max_iter: int =1, tol: float =1e-3):
"""
只做一轮 (或少量) SK初始 D=1。
返回: C_new, Ct_new
"""
Ns = len(H_list); Nf,P = Phi_f.shape; V = .shape[1]
# 初始化分母系数 Ct: Ct[0,0]=1
Ct = np.zeros((P,V), dtype=complex)
Ct[0,0]=1.0
C = C_prev.copy()
for it in range(max_iter):
# 构建线性系统: (N - D*H)=0
# 未知顺序: C(:), Ct(:) 去掉固定 Ct[0,0]
mask_fix = np.zeros((P,V), dtype=bool); mask_fix[0,0]=True
col_map={}
col=0
for i in range(Kp1):
for j in range(Qp):
idx_map[('C',i,j)] = col; col+=1
for i in range(Kp1):
for j in range(Qp):
for i in range(P):
for j in range(V):
col_map[('C',i,j)]=col; col+=1
for i in range(P):
for j in range(V):
if mask_fix[i,j]: continue
idx_map[('Ct',i,j)] = col; col+=1
cols = col
rows = Ns*Nf
A = np.zeros((rows, cols), dtype=complex)
b = np.zeros(rows, dtype=complex)
col_map[('Ct',i,j)]=col; col+=1
A = np.zeros((Ns*Nf, col), dtype=complex)
b = np.zeros(Ns*Nf, dtype=complex)
r=0
for n,Hn in enumerate(H_samples):
phiμ = Φμ[n]
Dp = D_prev[:,n] # (Nf,)
invDp = 1.0/(Dp + 1e-12)
# 分子块: (Φf φμ^T) / D_prev
Num_blk = np.einsum('fk,q->fkq', Φf, phiμ).reshape(Nf, Kp1*Qp)
Num_blk = (Num_blk.T * invDp).T
# 评价当前 D_prev= Σ Ct φ ψ
for n,Hn in enumerate(H_list):
# 分子块 (Φ_f ⊗ ψ_n)
blk = np.einsum('fp,v->fpv', Phi_f, [n]).reshape(Nf, P*V)
# 填 C 部分
for i in range(Kp1):
for j in range(Qp):
A[r:r+Nf, idx_map[('C',i,j)]] = Num_blk[:, i*Qp + j]
# 分母块: -(H * Φf φμ^T)/D_prev
Den_blk = (Num_blk.T * Hn).T * (-1.0)
for i in range(Kp1):
for j in range(Qp):
for i in range(P):
for j in range(V):
A[r:r+Nf, col_map[('C',i,j)]] = blk[:, i*V + j]
# 分母块: - H * blk
for i in range(P):
for j in range(V):
if mask_fix[i,j]: continue
A[r:r+Nf, idx_map[('Ct',i,j)]] = Den_blk[:, i*Qp + j]
# 右端 0
r += Nf
# 约束行 (式5)(8) 可选Re( Σ_k D^{(t)}/D^{(t-1)} ) = K+1
if self.cfg.add_constraint:
row_c = np.zeros(cols, dtype=complex)
# D^{(t)} ≈ Σ Ct_k φ_k(s) ; 用代表样本 n=0, 对所有频率平均
n0=0
phiμ0=Φμ[n0]
mean_phiμ = phiμ0 # 可换平均
# φ_k(s) 平均
mean_phif = Φf.mean(0) # (Kp1,)
for i in range(Kp1):
for j in range(Qp):
if mask_fix[i,j]: continue
row_c[idx_map[('Ct',i,j)]] = mean_phif[i]*mean_phiμ[j]
rhs = (Kp1) # K+1
A = np.vstack([A, np.real(row_c) if self.cfg.real_split else row_c])
b = np.concatenate([b, [rhs]])
# 拆实虚
if self.cfg.real_split:
A_real = np.vstack([np.real(A), np.imag(A)])
b_real = np.concatenate([np.real(b), np.imag(b)])
else:
A_real, b_real = A, b
return A_real, b_real
def _unpack_iter_solution(self, x, Kp1, Qp):
# 重新填回 C, Ct (保持 Ct[0,0]=1)
# 构建与 _build_iter_system 相同的 idx_map
mask_fix = np.zeros((Kp1,Qp), dtype=bool); mask_fix[0,0]=True
idx_C = Kp1*Qp
# 注意:我们当时 C 索引从 0..(Kp1*Qp-1)
self.C = x[:idx_C].reshape(Kp1, Qp)
Ct_new = self.Ct.copy()
pos = idx_C
for i in range(Kp1):
for j in range(Qp):
A[r:r+Nf, col_map[('Ct',i,j)]] = - Hn * blk[:, i*V + j]
r+=Nf
# 求解
x, *_ = np.linalg.lstsq(A, b, rcond=None)
# 拆回
idx=0
for i in range(P):
for j in range(V):
C[i,j]=x[idx]; idx+=1
Ct_new = Ct.copy()
for i in range(P):
for j in range(V):
if mask_fix[i,j]: continue
Ct_new[i,j]=x[pos]; pos+=1
self.Ct = Ct_new
Ct_new[i,j]=x[idx]; idx+=1
# 收敛性简单检查:分母变化
diff = np.max(np.abs(Ct_new - Ct))
Ct = Ct_new
if diff < tol:
break
return C, Ct
def _eval_C_mu(self, phiμ):
# 返回 c_k(μ) (Kp1,)
return self.C @ phiμ
def _eval_Ct_mu(self, phiμ):
return self.Ct @ phiμ
def _eval_D(self, Φf, Φμ):
# D(f, sample)= Σ_k ĉ_k(μ_n) φ_k(f)
Kp1 = Φf.shape[1]
Ns = Φμ.shape[0]
D = np.zeros((Φf.shape[0], Ns), dtype=complex)
for n in range(Ns):
ct_mu = self._eval_Ct_mu(Φμ[n])
D[:,n] = Φf @ ct_mu
return D
# ---------- 8. 假极点检测 (在 raw 基上) ----------
def detect_spurious_poles(C_ml: np.ndarray, Ct_ml: np.ndarray, T: np.ndarray,
: np.ndarray,
tau_cancel=1e-2, tau_small=1e-3, eps=1e-14):
"""
C_ml, Ct_ml: (P,V) (Muntz-Laguerre 基)
T: Φ_ml T = G_raw
Qμ: (Ns,V)
"""
# raw 系数: c_raw = T^{-1} c_ml
Tinv = np.linalg.inv(T)
C_raw = Tinv @ C_ml
Ct_raw = Tinv @ Ct_ml
P,V = C_ml.shape
Ns = .shape[0]
r_vals = C_raw @ .T
q_vals = Ct_raw @ .T
metrics={}
scales=[]
for k in range(1,P): # 跳过常数列
rv = r_vals[k]; qv = q_vals[k]
diff = np.max(np.abs(rv - qv))
scale = np.max([np.max(np.abs(rv)), np.max(np.abs(qv)), eps])
eta = diff / scale
metrics[k] = {"diff": diff, "scale": scale, "eta": eta}
scales.append(scale)
S_max = max(scales) if scales else 1.0
cancel_spurious = [k for k in range(1,P)
if metrics[k]["eta"] < tau_cancel and metrics[k]["scale"] > tau_small * S_max]
small_spurious = [k for k in range(1,P)
if metrics[k]["scale"] <= tau_small * S_max]
return {
"cancel_spurious": cancel_spurious,
"small_spurious": small_spurious,
"metrics": metrics,
"S_max": S_max,
"C_raw": C_raw,
"Ct_raw": Ct_raw
}
def evaluate(self, freqs: np.ndarray, g: np.ndarray):
assert np.allclose(freqs, self.fb.freqs), "频率需在训练网格上 (示例简化)"
Φf = self.fb.Q # (Nf,K+1)
phiμ = self.pb.eval(g) # (Qp,)
num = Φf @ (self.C @ phiμ)
den = Φf @ (self.Ct @ phiμ)
return num / (den + 1e-15)
# ---------- 9. 统一入口 ----------
@dataclass
class OPVFResult:
C_ml: np.ndarray
Ct_ml: np.ndarray
C_raw: np.ndarray
Ct_raw: np.ndarray
T: np.ndarray
: np.ndarray
: np.ndarray
exps: List[Tuple[int]]
poles: List[complex]
spurious: Dict
def opvf_from_H(H_list: List[np.ndarray],
freqs_hz: np.ndarray,
param_matrix: np.ndarray,
total_degree: int,
poles: List[complex],
do_t1: bool = True) -> OPVFResult:
"""
H_list: 长度 Ns; 每项 shape (Nf,)
freqs_hz: (Nf,)
param_matrix: (Ns,d) 归一化前参数值 (内部不自动标准化以保持可控)
poles: 初始稳定极点列表
"""
# 频率基
Phi_ml = muntz_laguerre_basis(freqs_hz, poles) # (Nf,P)
G_raw = raw_partial_fraction_basis(freqs_hz, poles) # (Nf,P)
# 简单权 (ω 权): w = Δf (因 (1/2π)∫ φφ* dω => ∑ Δf)
w = _trap_weights(freqs_hz)
T = compute_transform_matrix(Phi_ml, G_raw, w)
# 参数基
, , exps = generate_param_basis(param_matrix, total_degree)
# t=0
C_ml = fit_t0(H_list, Phi_ml, )
if do_t1:
C_ml, Ct_ml = fit_t1_SK(H_list, Phi_ml, , C_ml, max_iter=1)
else:
# D=1
P,V = C_ml.shape
Ct_ml = np.zeros((P,V), dtype=complex)
Ct_ml[0,0]=1.0
# 假极点检测
spurious = detect_spurious_poles(C_ml, Ct_ml, T, )
return OPVFResult(
C_ml=C_ml,
Ct_ml=Ct_ml,
C_raw=spurious["C_raw"],
Ct_raw=spurious["Ct_raw"],
T=T,
=,
=,
exps=exps,
poles=poles,
spurious=spurious
)
# ---------- 辅助: 梯形权 ----------
def _trap_weights(f: np.ndarray):
if len(f)==1: return np.ones(1)
df = np.diff(f)
w = np.zeros_like(f)
w[0]=0.5*df[0]; w[-1]=0.5*df[-1]
if len(f)>2:
w[1:-1]=0.5*(df[:-1]+df[1:])
return w
# ---------- 简单测试占位 ----------
if __name__ == "__main__":
# 虚构数据: 2 个参数样本 (Ns=2), 频率 200 点
freqs = np.linspace(1e8, 5e9, 200)
# 真正模型 (示例): H = Σ_k R_k/(s - p_k)
true_poles = [-0.5e3 + 1.2e9j, -0.5e3 - 1.2e9j]
s = 1j*2*np.pi*freqs
def synth(Rs):
return Rs[0]/(s - true_poles[0]) + Rs[1]/(s - true_poles[1])
H_list = [synth([0.8+0.1j, 1.2-0.2j]), synth([0.9+0.05j, 1.1+0.1j])]
params = np.array([[0.0],[1.0]]) # 1 维参数
# 给一个冗余极点集合 (含真实 + 额外)
start_poles = generate_starting_poles(n_pairs=10, beta_min=5e8, beta_max=2.0e9, alpha_scale=0.000001)
res = opvf_from_H(H_list, freqs, params, total_degree=1, poles=start_poles)
print("C_ml shape:", res.C_ml.shape)
print("假极点(cancel):", res.spurious["cancel_spurious"])
print("假极点(small):", res.spurious["small_spurious"])

View File

@@ -1,3 +1,5 @@
## 留数定理
### 1. 正交归一化条件
首先给出了一组基函数 \(\phi_m(s)\),要求它们满足正交归一化条件:

View File

@@ -0,0 +1,13 @@
$$
\begin{align}
&N(s) = \sum^N_{i=0}n_is^i=\xi_1\prod^N_{i=0}(s-p_{ni})\\
&D(s) = \sum^M_{i=0}d_is^i=\xi_2\prod^M_{i=0}(s-p_{mi})\\
&H(s) = \xi\frac{\prod^N_{i=0}(s-p_{ni})}{\prod^M_{i=0}(s-p_{mi})}\\
&let \space \phi_x=\frac{1}{s-p_x}\\
&H(s) = \frac{\sum_{i=0}^Nc_{ni}\phi_i(s)}{\sum_{j=0}^Mc_{mj}\phi_j(s)}\\
&当M=N时通分之后得到\\
&H(s) = \frac{\frac{\sum_{i=0}^Nc_i\prod_{a=0,a\not=i}(s-p_a)}{\prod^N_{i=0}(s-p_i)}}{\frac{\sum_{j=0}^Nc_j\prod_{b=0,b\not=j}(s-p_b)}{\prod^N_{j=0}(s-p_j)}}\\
&此时由于H(s)分母的分子连乘部分存在缺项所以p_b为假极点
\end{align}
$$

2
env Normal file
View File

@@ -0,0 +1,2 @@
export PYTHONPATH=$(pwd)/core:$PYTHONPATH
export PYTHONPATH=$(pwd)/models:$PYTHONPATH

BIN
img.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

128
main.py
View File

@@ -1,15 +1,117 @@
from models.capa import Capa
W = []
L = []
i = 15.52
while i <= 100:
W.append(i)
L.append(i)
i = int(i*1.05*100 +0.5) / 100.0
capa = Capa()
capa.sweep(W,L)
print(capa.network(result_dir=capa.results[0].result_dir,id=capa.results[0].id))
from core.orthonormal_basis import generate_laguerre_basis
from core.sk_iter import generate_starting_poles
import numpy as np
import skrf as rf
import matplotlib.pyplot as plt
import sympy as sp
from scipy.linalg import null_space
network = rf.Network("/tmp/paramer/simulation/4000/4000.s2p")
freqs = network.f
s = freqs * 2j * np.pi
vf = rf.VectorFitting(network)
vf.vector_fit(n_poles_real=2, n_poles_cmplx=1,parameter_type="y")
poles = vf.poles
residues = vf.residues
y = vf.network.y
fig, ax = plt.subplots(2, 2)
fig.set_size_inches(12, 8)
vf.plot("mag",0,0,freqs,ax=ax[0][0],parameter="y")
rms_error11 = vf.get_rms_error(0,0,"y")
print("rms_error11",rms_error11)
vf.plot("mag",1,0,freqs,ax=ax[1][0],parameter="y")
rms_error21 = vf.get_rms_error(1,0,"y")
print("rms_error21",rms_error21)
vf.plot("mag",0,1,freqs,ax=ax[0][1],parameter="y")
rms_error12 = vf.get_rms_error(0,1,"y")
print("rms_error12",rms_error12)
vf.plot("mag",1,1,freqs,ax=ax[1][1],parameter="y")
rms_error22 = vf.get_rms_error(1,1,"y")
print("rms_error22",rms_error22)
fig.tight_layout()
plt.show()
plt.savefig(f"img.png")
diag_values = [y[i][0][0] for i in range(len(y))]
H = np.diag(diag_values)
P = 4
start_poles = generate_starting_poles(P,beta_min=1e8,beta_max=freqs[-1])
basis = generate_laguerre_basis(start_poles,s).T
print("start_poles",start_poles)
print("basis",basis)
# first step iteration
# A*x = b
A11 = np.real(basis)
print("A11 shape:",A11.shape)
A12 = np.real(- H @ basis[:,1:])
print("A12 shape:",A12.shape)
# print("A11",A11)
A21 = np.imag(basis)
print("A21 shape:",A21.shape)
A22 = np.imag(- H @ basis[:,1:])
print("A22 shape:",A22.shape)
A1 = np.hstack([A11,A12])
A2 = np.hstack([A21,A22])
A = np.vstack([A1,A2])
print ("A shape:",A.shape)
b1 = np.real(H @ basis[:,0])
b2 = np.imag(H @ basis[:,0])
b = np.hstack([b1,b2])
Q, R = np.linalg.qr(A, mode='reduced')
print("Q :",Q)
print("R :",R)
print("b shape:",b.shape)
# x = np.linalg.solve(R, Q.T @ b)
x_star, residuals, rank, s = np.linalg.lstsq(A, b, rcond=None)
# print("x_qr",x_star)
print("residuals",residuals)
# print("rank",rank)
# print("s",s)
x_ne = np.linalg.inv(A.T @ A) @ A.T @ b
print("x_ne",x_ne)
# sk iteration
target_vec = np.array([1+0j] + list(x_ne[len(x_ne)//2+1:]))
D_t = basis @ target_vec
print("D_t",D_t)
K = 25
for i in range(K):
print(f"Iteration {i+1}/{K}")
A11 = np.real(basis / D_t[:, np.newaxis])
A12 = np.real(- H @ basis[:,1:] / D_t[:, np.newaxis])
A21 = np.imag(basis / D_t[:, np.newaxis])
A22 = np.imag(- H @ basis[:,1:] / D_t[:, np.newaxis])
A1 = np.hstack([A11,A12])
A2 = np.hstack([A21,A22])
A = np.vstack([A1,A2])
b1 = np.real(H @ basis[:,0])
b2 = np.imag(H @ basis[:,0])
b = np.hstack([b1,b2])
x_star, residuals, rank, s = np.linalg.lstsq(A, b, rcond=None)
# print("x_lstsq",x_star)
# print("residuals",residuals)
# print("rank",rank)
# print("s",s)
x_ne = np.linalg.inv(A.T @ A) @ A.T @ b
# print("x_ne",x_ne)
target_vec = np.array([1+0j] + list(x_ne[len(x_ne)//2+1:]))
D_t_pre = D_t
D_t = basis @ target_vec
print("D_t/D_t_pre",np.linalg.norm(D_t)/np.linalg.norm(D_t_pre))
# print("D_t",D_t)
n_target_vec = np.array(list(x_ne[:len(x_ne)//2+1]))
N_t = basis @ n_target_vec
# print("H = N_t / D_t",np.linalg.norm(H - N_t / D_t)/np.linalg.norm(H))

View File

@@ -1,202 +0,0 @@
import numpy as np
# ------------------------------------------------------------
# 按论文公式 (9)(10)(11) 生成 MuntzLaguerre 正交有理基 (解析形式)
#
# 给定稳定极点集合 {p_k} (Re(p_k)<0)。论文记法中使用 -a_k其中 Re(a_k)>0。
# 对应关系p_k = -a_k ⇒ a_k = -p_k, Re(a_k)= -Re(p_k) >0
#
# 连续内积意义下(沿 jω 轴积分)这些 φ_k 解析正交。离散频率采样后数值上
# 可能偏离,可再用加权 QR 做数值再正交(可选)。
#
# 公式在“稳定极点 p 表达”下的改写:
# (原) 实极点: φ_p(s) = sqrt(2 Re(a_p)) / (s + a_p) * Π (s - a_i^*)/(s + a_i)
# 变换 a_p = -p ⇒ Re(a_p)= -Re(p) = σ >0 且 (s + a_p) = (s - p)
# 且乘积 (s - a_i^*)/(s + a_i) = (s + p_i^*)/(s - p_i)
# ⇒ φ_p(s) = sqrt(-2 Re(p)) / (s - p) * Π_{i<p} (s + p_i^*)/(s - p_i)
#
# 复对 (p, p*),取 imag(p)>0 的 p 作为首:
# (原) φ_p = sqrt(2 Re(a_p)) (s - |a_p|)/[(s + a_p)(s + a_p^*)] * Π(...)
# (原) φ_{p+1}= sqrt(2 Re(a_p)) (s + |a_p|)/[(s + a_p)(s + a_p^*)] * Π(...)
# 代入 a_p=-p
# Re(a_p)= -Re(p)=σ>0, (s + a_p) = (s - p), (s + a_p^*)=(s - p^*)
# |a_p| = |p|
# 乘积同上 ⇒ Π_{i<p} (s + p_i^*)/(s - p_i)
#
# ⇒ φ_p(s) = sqrt(-2 Re(p)) (s - |p|)/[(s - p)(s - p^*)] * Π_{i<p} (s + p_i^*)/(s - p_i)
# φ_{p^*}(s)= sqrt(-2 Re(p)) (s + |p|)/[(s - p)(s - p^*)] * Π_{i<p} (s + p_i^*)/(s - p_i)
#
# product 递推维护prod_k = Π_{i≤k} (s + p_i^*)/(s - p_i)
# 对复对要顺序乘两次p 与 p*)。
# ------------------------------------------------------------
class MuntzLaguerreIterator:
def __init__(self, s: np.ndarray, stable_poles: list | np.ndarray):
"""
s: 复频率数组 (Nf,), s = j 2π f
stable_poles: 稳定极点列表 (Re<0). 复共轭对要求正虚部在前 (p, p*).
"""
self.s = np.asarray(s, dtype=complex)
self.poles = list(stable_poles)
self.N = len(self.poles)
self.k = 0
# 初始化乘积 Π_{i<p} (s + p_i^*)/(s - p_i)
self.product = np.ones_like(self.s, dtype=complex)
def __iter__(self):
return self
def __next__(self):
if self.k >= self.N:
raise StopIteration
p = self.poles[self.k]
if np.real(p) >= 0:
raise ValueError(f"极点必须在左半平面: {p}")
# 复对首 (正虚部)
if np.iscomplex(p) and np.imag(p) > 0:
if self.k + 1 >= self.N:
raise ValueError("复极点缺少共轭")
pc = self.poles[self.k + 1]
if not np.isclose(pc, np.conj(p)):
raise ValueError("复极点未按 (p, p*) 顺序排列 (正虚部在前)")
sigma = -np.real(p) # >0
scale = np.sqrt(2 * sigma)
r = np.abs(p)
denom = (self.s - p) * (self.s - pc)
# 两个基函数
phi_p = scale * (self.s - r) / denom * self.product
phi_pc = scale * (self.s + r) / denom * self.product
# product 先乘 (s + p^*)/(s - p),再乘 (s + p)/(s - p^*)
self.product = self.product * (self.s + pc) / (self.s - p)
self.product = self.product * (self.s + p) / (self.s - pc)
self.k += 2
return [phi_p, phi_pc]
# 复对次 (负虚部) —— 应该被首元素处理,出现表示顺序错误
if np.iscomplex(p) and np.imag(p) < 0:
raise ValueError("检测到负虚部复极点但其共轭尚未处理,请将正虚部成员放在前面。")
# 实极点
sigma = -np.real(p)
if sigma <= 0:
raise ValueError("实极点实部应为负 (稳定)。")
scale = np.sqrt(2 * sigma)
phi = scale / (self.s - p) * self.product
# 更新乘积
self.product = self.product * (self.s + p) / (self.s - p)
self.k += 1
return [phi]
def generate_muntz_laguerre_basis(s: np.ndarray, stable_poles: list | np.ndarray):
"""
生成完整基函数列表: [φ_0=1, φ_1, φ_2, ...]
"""
basis = [np.ones_like(s, dtype=complex)]
for block in MuntzLaguerreIterator(s, stable_poles):
basis.extend(block)
return basis
# ---------- 可选:离散再正交 (加权 QR) ----------
# def trapezoid_weights(freqs: np.ndarray):
# if len(freqs) == 1:
# return np.ones(1)
# df = np.diff(freqs)
# w = np.zeros_like(freqs, dtype=float)
# w[0] = 0.5 * df[0]
# w[-1] = 0.5 * df[-1]
# if len(freqs) > 2:
# w[1:-1] = 0.5 * (df[:-1] + df[1:])
# return w
def weighted_qr_from_basis(basis_cols: list[np.ndarray], weights: np.ndarray | None = None):
A = np.column_stack(basis_cols) # (Nf, M)
if weights is None:
sw = np.ones(A.shape[0])
else:
sw = np.sqrt(weights.real)
Aw = sw[:, None] * A
Qw, R = np.linalg.qr(Aw)
Phi = Qw / (sw[:, None] + 1e-30)
return Phi, R # Raw = Phi R
def check_discrete_orthogonality(Phi: np.ndarray, w: np.ndarray):
G = Phi.conj().T @ (w[:, None] * Phi)
off = np.max(np.abs(G - np.eye(G.shape[0])))
return G, off
def verify_orthonormal(Phi: np.ndarray, w: np.ndarray, atol=1e-10, rtol=1e-8):
"""
返回:
G : Gram 矩阵 (Φ^H W Φ)
max_off : 最大非对角幅值
diag_err : max |diag(G)-1|
passed : 是否满足阈值
"""
G = Phi.conj().T @ (w[:, None] * Phi)
I = np.eye(G.shape[0])
diag_err = np.max(np.abs(np.diag(G) - 1.0))
max_off = np.max(np.abs(G - I + np.diag(np.diag(G) - 1.0)))
passed = (diag_err <= atol) and (max_off <= rtol)
return G, max_off, diag_err, passed
def omega_weights(freqs_hz: np.ndarray):
"""
基于 ω=2πf 的梯形法得到 w_ω = Δω/(2π),使得
(1/2π) ∫_{-∞}^{∞} → Σ w_ω,k
"""
f = freqs_hz
if len(f) == 1:
return np.ones(1)
df = np.diff(f)
w_f = np.zeros_like(f)
w_f[0] = 0.5 * df[0]
w_f[-1] = 0.5 * df[-1]
if len(f) > 2:
w_f[1:-1] = 0.5 * (df[:-1] + df[1:])
# dω = 2π df, (1/2π) * dω = df ⇒ 直接 w_f 就是 w_ω
return w_f # 已等价于 Δω/(2π)
# ------------------ 示例 ------------------
if __name__ == "__main__":
# 示例稳定极点 (复对正虚部在前)
stable_poles = [
-0.8e9,
-1.0e9 + 2.5e9j,
-1.0e9 - 2.5e9j,
-2.2e9
]
freqs = np.linspace(1e8, 8e9, 400)
s = 1j * 2 * np.pi * freqs
basis = generate_muntz_laguerre_basis(s, stable_poles)
print("解析基函数数量 =", len(basis))
print("前5个基函数示例 (每个前10个频点):")
for i in range(5):
print(f"φ_{i}:", basis[i][:10])
w = omega_weights(freqs)
Phi_num, R = weighted_qr_from_basis(basis, w)
Gram, off = check_discrete_orthogonality(Phi_num, w)
print("离散 Gram 最大非对角元素 =", off)
print("R 形状:", R.shape)
# 验证 Raw ≈ Phi R
raw = np.column_stack(basis)
err = np.max(np.abs(raw - Phi_num @ R))
print("重构误差 ||Raw - Phi R||_∞ =", err)
# 验证正交性
print("离散 Gram 矩阵 (前5x5):")
print(Gram[:5, :5])
Gcheck, max_off, diag_err, ok = verify_orthonormal(Phi_num, w)
print(f"Diag 误差={diag_err:.3e}, Max off={max_off:.3e}, Orthonormal={ok}")
# 额外: 验证 R
# raw = Φ R => R ≈ Φ^H W raw (因为 Φ^H W Φ = I)
R_alt = Phi_num.conj().T @ (w[:,None] * raw)
print("R 差异 ||R - R_alt||_max =", np.max(np.abs(R - R_alt)))

9
test/test_numpy.py Normal file
View File

@@ -0,0 +1,9 @@
import numpy as np
# 创建一个复数矩阵
A = np.array([[1+2j, 2-1j], [3+4j, 4+0j]])
Q, R = np.linalg.qr(A)
print("Q =\n", Q)
print("R =\n", R)

View File

@@ -0,0 +1,102 @@
import numpy as np
from core.orthonormal_basis import generate_muntz_laguerre_basis
# ---------- 可选:离散再正交 (加权 QR) ----------
# def trapezoid_weights(freqs: np.ndarray):
# if len(freqs) == 1:
# return np.ones(1)
# df = np.diff(freqs)
# w = np.zeros_like(freqs, dtype=float)
# w[0] = 0.5 * df[0]
# w[-1] = 0.5 * df[-1]
# if len(freqs) > 2:
# w[1:-1] = 0.5 * (df[:-1] + df[1:])
# return w
def weighted_qr_from_basis(basis_cols: list[np.ndarray], weights: np.ndarray | None = None):
A = np.column_stack(basis_cols) # (Nf, M)
if weights is None:
sw = np.ones(A.shape[0])
else:
sw = np.sqrt(weights.real)
Aw = sw[:, None] * A
Qw, R = np.linalg.qr(Aw)
Phi = Qw / (sw[:, None] + 1e-30)
return Phi, R # Raw = Phi R
def check_discrete_orthogonality(Phi: np.ndarray, w: np.ndarray):
G = Phi.conj().T @ (w[:, None] * Phi)
off = np.max(np.abs(G - np.eye(G.shape[0])))
return G, off
def verify_orthonormal(Phi: np.ndarray, w: np.ndarray, atol=1e-10, rtol=1e-8):
"""
返回:
G : Gram 矩阵 (Φ^H W Φ)
max_off : 最大非对角幅值
diag_err : max |diag(G)-1|
passed : 是否满足阈值
"""
G = Phi.conj().T @ (w[:, None] * Phi)
I = np.eye(G.shape[0])
diag_err = np.max(np.abs(np.diag(G) - 1.0))
max_off = np.max(np.abs(G - I + np.diag(np.diag(G) - 1.0)))
passed = (diag_err <= atol) and (max_off <= rtol)
return G, max_off, diag_err, passed
def omega_weights(freqs_hz: np.ndarray):
"""
基于 ω=2πf 的梯形法得到 w_ω = Δω/(2π),使得
(1/2π) ∫_{-∞}^{∞} → Σ w_ω,k
"""
f = freqs_hz
if len(f) == 1:
return np.ones(1)
df = np.diff(f)
w_f = np.zeros_like(f)
w_f[0] = 0.5 * df[0]
w_f[-1] = 0.5 * df[-1]
if len(f) > 2:
w_f[1:-1] = 0.5 * (df[:-1] + df[1:])
# dω = 2π df, (1/2π) * dω = df ⇒ 直接 w_f 就是 w_ω
return w_f # 已等价于 Δω/(2π)
def evaluate(basis,freqs):
w = omega_weights(freqs)
Phi_num, R = weighted_qr_from_basis(basis, w)
Gram, off = check_discrete_orthogonality(Phi_num, w)
print("离散 Gram 最大非对角元素 =", off)
print("R 形状:", R.shape)
# 验证 Raw ≈ Phi R
raw = np.column_stack(basis)
err = np.max(np.abs(raw - Phi_num @ R))
print("重构误差 ||Raw - Phi R||_∞ =", err)
# 验证正交性
print("离散 Gram 矩阵 (前5x5):")
print(Gram[:5, :5])
Gcheck, max_off, diag_err, ok = verify_orthonormal(Phi_num, w)
print(f"Diag 误差={diag_err:.3e}, Max off={max_off:.3e}, Orthonormal={ok}")
# 额外: 验证 R
# raw = Φ R => R ≈ Φ^H W raw (因为 Φ^H W Φ = I)
R_alt = Phi_num.conj().T @ (w[:,None] * raw)
print("R 差异 ||R - R_alt||_max =", np.max(np.abs(R - R_alt)))
# ------------------ 示例 ------------------
if __name__ == "__main__":
# 示例稳定极点 (复对正虚部在前)
init_poles = [
-1.0e3 + 2.5e9j,
-1.0e3 - 2.5e9j,
]
freqs = np.linspace(1e8, 8e9, 40)
s = 1j * 2 * np.pi * freqs
basis = generate_muntz_laguerre_basis(s, init_poles)
print("解析基函数数量 =", len(basis))
print("基函数:")
for i in range(len(basis)):
print(f"φ_{i}:", basis[i][:])
evaluate(basis, freqs)