feat: OVF formula 67
This commit is contained in:
174
core/orthonormal_basis.py
Normal file
174
core/orthonormal_basis.py
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# 按论文公式 (9)(10)(11) 生成 Muntz–Laguerre 正交有理基 (解析形式):
|
||||||
|
#
|
||||||
|
# 给定稳定极点集合 {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}")
|
||||||
526
core/sk_iter.py
526
core/sk_iter.py
@@ -1,250 +1,326 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import List, Sequence, Tuple, Optional
|
from typing import List, Dict, Tuple
|
||||||
|
|
||||||
@dataclass
|
# ---------- 1. 生成初始极点(按论文 Re(-a_p) 小、Im 线性分布) ----------
|
||||||
class OPVFConfig:
|
def generate_starting_poles(n_pairs: int, beta_min: float, beta_max: float, alpha_scale: float = 0.01):
|
||||||
n_poles: int
|
"""
|
||||||
max_iter: int = 5
|
仅生成复共轭对: p = -alpha + j beta, p*。
|
||||||
tol: float = 1e-3
|
n_pairs: 复对数量 (总极点数 = 2*n_pairs)
|
||||||
add_constraint: bool = True
|
beta_min,beta_max: 想要覆盖的虚部范围 (单位: rad/s)
|
||||||
real_split: bool = True
|
alpha_scale: alpha = alpha_scale * beta (文中 {α_p}=0.01{β_p})
|
||||||
lambda_reg: float = 0.0
|
返回: list[complex] (正虚部先, 后跟共轭)
|
||||||
verbose: bool = True
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
# ---------- 参数正交基 ----------
|
# ---------- 2. Muntz–Laguerre 频率基 (与你现有 orthonormal_basis 中一致的核心) ----------
|
||||||
class ParamOrthoBasis:
|
def muntz_laguerre_basis(freqs_hz: np.ndarray, stable_poles: List[complex]) -> np.ndarray:
|
||||||
def __init__(self, params: np.ndarray, total_degree: int):
|
"""
|
||||||
# params: (Ns, d)
|
返回 Φ_ml (Nf, P) 列顺序: φ_0=1, 后续依次 (单极点 / 复对两列)
|
||||||
self.mean = params.mean(0)
|
"""
|
||||||
self.std = params.std(0) + 1e-15
|
s = 1j * 2 * np.pi * freqs_hz
|
||||||
self.exps = self._gen_exps(params.shape[1], total_degree)
|
basis = [np.ones_like(s, dtype=complex)]
|
||||||
M = self._build_monomial_matrix(params) # (Ns,M)
|
product = np.ones_like(s, dtype=complex)
|
||||||
Q,R = np.linalg.qr(M)
|
k = 0
|
||||||
self.Q = Q
|
N = len(stable_poles)
|
||||||
self.R = R
|
while k < N:
|
||||||
def _gen_exps(self, dim, D):
|
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)
|
||||||
|
|
||||||
|
# ---------- 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)
|
||||||
|
|
||||||
|
# ---------- 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
|
||||||
|
|
||||||
|
# ---------- 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=[]
|
exps=[]
|
||||||
def rec(cur, i, rem):
|
def rec(cur,i,rem):
|
||||||
if i==dim:
|
if i==d:
|
||||||
exps.append(tuple(cur)); return
|
exps.append(tuple(cur)); return
|
||||||
for k in range(rem+1):
|
for k in range(rem+1):
|
||||||
cur.append(k); rec(cur, i+1, rem-k); cur.pop()
|
cur.append(k); rec(cur,i+1,rem-k); cur.pop()
|
||||||
rec([],0,D)
|
rec([],0,total_degree)
|
||||||
return exps
|
V = len(exps)
|
||||||
def _build_monomial_matrix(self, params):
|
M = np.zeros((Ns, V))
|
||||||
X = (params - self.mean)/self.std
|
for idx,e in enumerate(exps):
|
||||||
Ns = X.shape[0]
|
|
||||||
M = np.zeros((Ns, len(self.exps)))
|
|
||||||
for idx,e in enumerate(self.exps):
|
|
||||||
v = np.ones(Ns)
|
v = np.ones(Ns)
|
||||||
for j,p in enumerate(e):
|
for j,p in enumerate(e):
|
||||||
if p: v *= X[:,j]**p
|
if p: v *= X[:,j]**p
|
||||||
M[:,idx]=v
|
M[:,idx]=v
|
||||||
return M
|
Q,R = np.linalg.qr(M)
|
||||||
def eval(self, g: np.ndarray) -> np.ndarray:
|
return Q,R,exps
|
||||||
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)
|
|
||||||
|
|
||||||
# ---------- 频率正交(有理)基 ----------
|
# ---------- 6. t=0 构建设计矩阵并解 C ----------
|
||||||
class RationalFreqBasis:
|
def fit_t0(H_list: List[np.ndarray], Phi_f: np.ndarray, Qμ: np.ndarray) -> np.ndarray:
|
||||||
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:
|
|
||||||
"""
|
"""
|
||||||
H(s, μ) = N(s,μ)/D(s,μ)
|
H_list: 长度 Ns, 每项 (Nf,) 复数
|
||||||
N(s,μ)= Σ_k c_k(μ) φ_k(s); D(s,μ)= Σ_k ĉ_k(μ) φ_k(s)
|
Phi_f: (Nf, P)
|
||||||
其中 φ_k(s) 是频率正交有理基; c_k(μ), ĉ_k(μ) 在参数正交基上展开:
|
Qμ: (Ns, V)
|
||||||
c_k(μ)= Σ_q C[k,q] ψ_q(μ); ĉ_k(μ)= Σ_q Ctilde[k,q] ψ_q(μ)
|
返回: C (P,V)
|
||||||
"""
|
"""
|
||||||
def __init__(self, cfg: OPVFConfig, freq_basis: RationalFreqBasis, param_basis: ParamOrthoBasis):
|
Ns = len(H_list); Nf,P = Phi_f.shape; V = Qμ.shape[1]
|
||||||
self.cfg = cfg
|
cols = P*V
|
||||||
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
|
|
||||||
|
|
||||||
def _assemble_phi_param(self, params: np.ndarray) -> np.ndarray:
|
|
||||||
# 返回 (Ns, Qp)
|
|
||||||
return self.pb.Q
|
|
||||||
|
|
||||||
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]
|
|
||||||
|
|
||||||
# t=0: 只解 C (式3, D=1)
|
|
||||||
self._solve_t0(H_samples, Φf, Φμ)
|
|
||||||
|
|
||||||
D_prev = self._eval_D(Φf, Φμ) # (Nf, Ns)
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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)
|
A = np.zeros((Ns*Nf, cols), dtype=complex)
|
||||||
b = np.zeros(Ns*Nf, 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={}
|
|
||||||
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):
|
|
||||||
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)
|
|
||||||
r=0
|
r=0
|
||||||
for n,Hn in enumerate(H_samples):
|
for n,Hn in enumerate(H_list):
|
||||||
phiμ = Φμ[n]
|
blk = np.einsum('fp,v->fpv', Phi_f, Qμ[n]).reshape(Nf, cols)
|
||||||
Dp = D_prev[:,n] # (Nf,)
|
A[r:r+Nf,:]=blk
|
||||||
invDp = 1.0/(Dp + 1e-12)
|
b[r:r+Nf]=Hn
|
||||||
# 分子块: (Φf φμ^T) / D_prev
|
r+=Nf
|
||||||
Num_blk = np.einsum('fk,q->fkq', Φf, phiμ).reshape(Nf, Kp1*Qp)
|
x, *_ = np.linalg.lstsq(A, b, rcond=None)
|
||||||
Num_blk = (Num_blk.T * invDp).T
|
C = x.reshape(P, V)
|
||||||
|
return C
|
||||||
|
|
||||||
|
# ---------- 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 = Qμ.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(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
|
||||||
|
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
|
||||||
|
# 评价当前 D_prev= Σ Ct φ ψ
|
||||||
|
for n,Hn in enumerate(H_list):
|
||||||
|
# 分子块 (Φ_f ⊗ ψ_n)
|
||||||
|
blk = np.einsum('fp,v->fpv', Phi_f, Qμ[n]).reshape(Nf, P*V)
|
||||||
# 填 C 部分
|
# 填 C 部分
|
||||||
for i in range(Kp1):
|
for i in range(P):
|
||||||
for j in range(Qp):
|
for j in range(V):
|
||||||
A[r:r+Nf, idx_map[('C',i,j)]] = Num_blk[:, i*Qp + j]
|
A[r:r+Nf, col_map[('C',i,j)]] = blk[:, i*V + j]
|
||||||
# 分母块: -(H * Φf φμ^T)/D_prev
|
# 分母块: - H * blk
|
||||||
Den_blk = (Num_blk.T * Hn).T * (-1.0)
|
for i in range(P):
|
||||||
for i in range(Kp1):
|
for j in range(V):
|
||||||
for j in range(Qp):
|
|
||||||
if mask_fix[i,j]: continue
|
if mask_fix[i,j]: continue
|
||||||
A[r:r+Nf, idx_map[('Ct',i,j)]] = Den_blk[:, i*Qp + j]
|
A[r:r+Nf, col_map[('Ct',i,j)]] = - Hn * blk[:, i*V + j]
|
||||||
# 右端 0
|
r+=Nf
|
||||||
r += Nf
|
# 求解
|
||||||
|
x, *_ = np.linalg.lstsq(A, b, rcond=None)
|
||||||
# 约束行 (式5)(8) 可选:Re( Σ_k D^{(t)}/D^{(t-1)} ) = K+1
|
# 拆回
|
||||||
if self.cfg.add_constraint:
|
idx=0
|
||||||
row_c = np.zeros(cols, dtype=complex)
|
for i in range(P):
|
||||||
# D^{(t)} ≈ Σ Ct_k φ_k(s) ; 用代表样本 n=0, 对所有频率平均
|
for j in range(V):
|
||||||
n0=0
|
C[i,j]=x[idx]; idx+=1
|
||||||
phiμ0=Φμ[n0]
|
Ct_new = Ct.copy()
|
||||||
mean_phiμ = phiμ0 # 可换平均
|
for i in range(P):
|
||||||
# φ_k(s) 平均
|
for j in range(V):
|
||||||
mean_phif = Φf.mean(0) # (Kp1,)
|
|
||||||
for i in range(Kp1):
|
|
||||||
for j in range(Qp):
|
|
||||||
if mask_fix[i,j]: continue
|
if mask_fix[i,j]: continue
|
||||||
row_c[idx_map[('Ct',i,j)]] = mean_phif[i]*mean_phiμ[j]
|
Ct_new[i,j]=x[idx]; idx+=1
|
||||||
rhs = (Kp1) # K+1
|
# 收敛性简单检查:分母变化
|
||||||
A = np.vstack([A, np.real(row_c) if self.cfg.real_split else row_c])
|
diff = np.max(np.abs(Ct_new - Ct))
|
||||||
b = np.concatenate([b, [rhs]])
|
Ct = Ct_new
|
||||||
|
if diff < tol:
|
||||||
|
break
|
||||||
|
return C, Ct
|
||||||
|
|
||||||
# 拆实虚
|
# ---------- 8. 假极点检测 (在 raw 基上) ----------
|
||||||
if self.cfg.real_split:
|
def detect_spurious_poles(C_ml: np.ndarray, Ct_ml: np.ndarray, T: np.ndarray,
|
||||||
A_real = np.vstack([np.real(A), np.imag(A)])
|
Qμ: np.ndarray,
|
||||||
b_real = np.concatenate([np.real(b), np.imag(b)])
|
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 = Qμ.shape[0]
|
||||||
|
r_vals = C_raw @ Qμ.T
|
||||||
|
q_vals = Ct_raw @ Qμ.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
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------- 9. 统一入口 ----------
|
||||||
|
@dataclass
|
||||||
|
class OPVFResult:
|
||||||
|
C_ml: np.ndarray
|
||||||
|
Ct_ml: np.ndarray
|
||||||
|
C_raw: np.ndarray
|
||||||
|
Ct_raw: np.ndarray
|
||||||
|
T: np.ndarray
|
||||||
|
Qμ: np.ndarray
|
||||||
|
Rμ: 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)
|
||||||
|
|
||||||
|
# 参数基
|
||||||
|
Qμ, Rμ, exps = generate_param_basis(param_matrix, total_degree)
|
||||||
|
|
||||||
|
# t=0
|
||||||
|
C_ml = fit_t0(H_list, Phi_ml, Qμ)
|
||||||
|
|
||||||
|
if do_t1:
|
||||||
|
C_ml, Ct_ml = fit_t1_SK(H_list, Phi_ml, Qμ, C_ml, max_iter=1)
|
||||||
else:
|
else:
|
||||||
A_real, b_real = A, b
|
# D=1
|
||||||
return A_real, b_real
|
P,V = C_ml.shape
|
||||||
|
Ct_ml = np.zeros((P,V), dtype=complex)
|
||||||
|
Ct_ml[0,0]=1.0
|
||||||
|
|
||||||
def _unpack_iter_solution(self, x, Kp1, Qp):
|
# 假极点检测
|
||||||
# 重新填回 C, Ct (保持 Ct[0,0]=1)
|
spurious = detect_spurious_poles(C_ml, Ct_ml, T, Qμ)
|
||||||
# 构建与 _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):
|
|
||||||
if mask_fix[i,j]: continue
|
|
||||||
Ct_new[i,j]=x[pos]; pos+=1
|
|
||||||
self.Ct = Ct_new
|
|
||||||
|
|
||||||
def _eval_C_mu(self, phiμ):
|
return OPVFResult(
|
||||||
# 返回 c_k(μ) (Kp1,)
|
C_ml=C_ml,
|
||||||
return self.C @ phiμ
|
Ct_ml=Ct_ml,
|
||||||
def _eval_Ct_mu(self, phiμ):
|
C_raw=spurious["C_raw"],
|
||||||
return self.Ct @ phiμ
|
Ct_raw=spurious["Ct_raw"],
|
||||||
def _eval_D(self, Φf, Φμ):
|
T=T,
|
||||||
# D(f, sample)= Σ_k ĉ_k(μ_n) φ_k(f)
|
Qμ=Qμ,
|
||||||
Kp1 = Φf.shape[1]
|
Rμ=Rμ,
|
||||||
Ns = Φμ.shape[0]
|
exps=exps,
|
||||||
D = np.zeros((Φf.shape[0], Ns), dtype=complex)
|
poles=poles,
|
||||||
for n in range(Ns):
|
spurious=spurious
|
||||||
ct_mu = self._eval_Ct_mu(Φμ[n])
|
)
|
||||||
D[:,n] = Φf @ ct_mu
|
|
||||||
return D
|
|
||||||
|
|
||||||
def evaluate(self, freqs: np.ndarray, g: np.ndarray):
|
# ---------- 辅助: 梯形权 ----------
|
||||||
assert np.allclose(freqs, self.fb.freqs), "频率需在训练网格上 (示例简化)"
|
def _trap_weights(f: np.ndarray):
|
||||||
Φf = self.fb.Q # (Nf,K+1)
|
if len(f)==1: return np.ones(1)
|
||||||
phiμ = self.pb.eval(g) # (Qp,)
|
df = np.diff(f)
|
||||||
num = Φf @ (self.C @ phiμ)
|
w = np.zeros_like(f)
|
||||||
den = Φf @ (self.Ct @ phiμ)
|
w[0]=0.5*df[0]; w[-1]=0.5*df[-1]
|
||||||
return num / (den + 1e-15)
|
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"])
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
## 留数定理
|
||||||
|
|
||||||
### 1. 正交归一化条件
|
### 1. 正交归一化条件
|
||||||
|
|
||||||
首先给出了一组基函数 \(\phi_m(s)\),要求它们满足正交归一化条件:
|
首先给出了一组基函数 \(\phi_m(s)\),要求它们满足正交归一化条件:
|
||||||
|
|||||||
13
docs/有理函数正交化.md
Normal file
13
docs/有理函数正交化.md
Normal 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
2
env
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export PYTHONPATH=$(pwd)/core:$PYTHONPATH
|
||||||
|
export PYTHONPATH=$(pwd)/models:$PYTHONPATH
|
||||||
128
main.py
128
main.py
@@ -1,15 +1,117 @@
|
|||||||
from models.capa import Capa
|
from core.orthonormal_basis import generate_laguerre_basis
|
||||||
W = []
|
from core.sk_iter import generate_starting_poles
|
||||||
L = []
|
import numpy as np
|
||||||
i = 15.52
|
import skrf as rf
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
while i <= 100:
|
import sympy as sp
|
||||||
W.append(i)
|
from scipy.linalg import null_space
|
||||||
L.append(i)
|
|
||||||
i = int(i*1.05*100 +0.5) / 100.0
|
|
||||||
|
network = rf.Network("/tmp/paramer/simulation/4000/4000.s2p")
|
||||||
capa = Capa()
|
freqs = network.f
|
||||||
capa.sweep(W,L)
|
s = freqs * 2j * np.pi
|
||||||
print(capa.network(result_dir=capa.results[0].result_dir,id=capa.results[0].id))
|
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))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,202 +0,0 @@
|
|||||||
import numpy as np
|
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
# 按论文公式 (9)(10)(11) 生成 Muntz–Laguerre 正交有理基 (解析形式):
|
|
||||||
#
|
|
||||||
# 给定稳定极点集合 {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
9
test/test_numpy.py
Normal 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)
|
||||||
102
test/test_orthonormal_basis.py
Normal file
102
test/test_orthonormal_basis.py
Normal 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)
|
||||||
Reference in New Issue
Block a user