|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
"""自动拟合候选曲线目标函数。
|
|
|
|
|
|
|
|
|
|
|
|
本模块提供轻量级的曲线误差计算,用于比较目标曲线和代理模型预测曲线的匹配度。
|
|
|
|
|
|
目标函数同时考虑相对误差和绝对误差,并分别计算 log_pressure 与 log_derivative
|
|
|
|
|
|
两条曲线的贡献,适合在参数筛选、PSO 候选排序和局部邻域验证中复用。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def split_curve_by_layout(curve: np.ndarray, layout: dict) -> dict[str, np.ndarray]:
|
|
|
|
|
|
"""按照 curve_layout 将拼接曲线拆成 log_pressure、log_derivative 和 slope 三段。"""
|
|
|
|
|
|
parts: dict[str, np.ndarray] = {}
|
|
|
|
|
|
for part in layout["parts"]:
|
|
|
|
|
|
start = int(part["start"])
|
|
|
|
|
|
end = int(part["end"])
|
|
|
|
|
|
parts[str(part["name"])] = np.asarray(curve[start:end], dtype=np.float64)
|
|
|
|
|
|
return parts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def calculate_curve_objective_1d(target: np.ndarray, pred: np.ndarray) -> float:
|
|
|
|
|
|
"""计算单段曲线的自动拟合目标,兼顾相对误差和绝对误差。"""
|
|
|
|
|
|
target = np.asarray(target, dtype=np.float64).reshape(-1)
|
|
|
|
|
|
pred = np.asarray(pred, dtype=np.float64).reshape(-1)
|
|
|
|
|
|
|
|
|
|
|
|
if target.size == 0 or pred.size != target.size:
|
|
|
|
|
|
return float("inf")
|
|
|
|
|
|
if not (np.isfinite(target).all() and np.isfinite(pred).all()):
|
|
|
|
|
|
return float("inf")
|
|
|
|
|
|
|
|
|
|
|
|
# 对较大的 log 值适当降权,避免其完全主导排序结果。
|
|
|
|
|
|
weight_factor = np.minimum(100.0, np.abs(target) * 0.01)
|
|
|
|
|
|
weight = 1.0 / (1.0 + weight_factor)
|
|
|
|
|
|
|
|
|
|
|
|
scale = np.maximum(np.maximum(np.abs(target), np.abs(pred)), 1e-12)
|
|
|
|
|
|
relative_error = np.abs(target - pred) / scale
|
|
|
|
|
|
absolute_error = np.abs(target - pred)
|
|
|
|
|
|
|
|
|
|
|
|
# 相对误差用于跨尺度比较曲线形态,绝对误差用于保留整体幅值差异。
|
|
|
|
|
|
point_error = 0.7 * relative_error + 0.3 * absolute_error
|
|
|
|
|
|
weighted_mse = np.sum(weight * (point_error**2)) / max(np.sum(weight), 1e-12)
|
|
|
|
|
|
return float(np.sqrt(weighted_mse))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def dual_log_objective(
|
|
|
|
|
|
curve_target: np.ndarray,
|
|
|
|
|
|
curve_pred: np.ndarray,
|
|
|
|
|
|
curve_layout: dict,
|
|
|
|
|
|
w_pressure: float = 0.5,
|
|
|
|
|
|
w_derivative: float = 0.5,
|
|
|
|
|
|
) -> dict[str, float]:
|
|
|
|
|
|
"""分别计算压力和导数目标,并按权重合成双对数自动拟合目标。"""
|
|
|
|
|
|
parts_target = split_curve_by_layout(curve_target, curve_layout)
|
|
|
|
|
|
parts_pred = split_curve_by_layout(curve_pred, curve_layout)
|
|
|
|
|
|
|
|
|
|
|
|
p_obj = calculate_curve_objective_1d(parts_target["log_pressure"], parts_pred["log_pressure"])
|
|
|
|
|
|
d_obj = calculate_curve_objective_1d(parts_target["log_derivative"], parts_pred["log_derivative"])
|
|
|
|
|
|
|
|
|
|
|
|
total_w = max(float(w_pressure) + float(w_derivative), 1e-12)
|
|
|
|
|
|
combined = (float(w_pressure) * p_obj + float(w_derivative) * d_obj) / total_w
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
"log_pressure_objective": float(p_obj),
|
|
|
|
|
|
"log_derivative_objective": float(d_obj),
|
|
|
|
|
|
"dual_log_objective": float(combined),
|
|
|
|
|
|
}
|