删除时间覆盖率的限制,避免覆盖率不足而拟合失败。

develop
lvjunjie 1 week ago
parent 9df3da7b7c
commit d0c0f70d8b

@ -32,7 +32,6 @@ struct AutoFitObjectiveBreakdownLM {
double lateDerivativeSlopeBias;
double lateDerivativeTrendLoss;
bool lateDerivativeTrendReliable;
double coverage;
AutoFitObjectiveBreakdownLM()
: valid(false)
@ -50,7 +49,6 @@ struct AutoFitObjectiveBreakdownLM {
, lateDerivativeSlopeBias(std::numeric_limits<double>::quiet_NaN())
, lateDerivativeTrendLoss(std::numeric_limits<double>::quiet_NaN())
, lateDerivativeTrendReliable(false)
, coverage(std::numeric_limits<double>::quiet_NaN())
{}
};

@ -860,8 +860,7 @@ void nmCalculationAutoFitLM::writeTraceHeader()
<< "late_trend_loss"
<< "late_slope_bias"
<< "late_trend_reliable"
<< "registration_ambiguous"
<< "coverage";
<< "registration_ambiguous";
QTextStream out(&m_traceFile);
out << cols.join(",") << "\n";
@ -978,10 +977,9 @@ void nmCalculationAutoFitLM::writeTraceRow(
<< traceNumber(objectiveBreakdown->lateDerivativeTrendLoss)
<< traceNumber(objectiveBreakdown->lateDerivativeSlopeBias)
<< QString::number(objectiveBreakdown->lateDerivativeTrendReliable ? 1 : 0)
<< QString::number(objectiveBreakdown->registrationAmbiguous ? 1 : 0)
<< traceNumber(objectiveBreakdown->coverage);
<< QString::number(objectiveBreakdown->registrationAmbiguous ? 1 : 0);
} else {
for(int i = 0; i < 14; ++i) {
for(int i = 0; i < 13; ++i) {
cols << QString();
}
}
@ -3247,12 +3245,11 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
const QVector<QVector<double> >& target,
const QVector<QVector<double> >& result) const
{
// 主目标只比较固定网格上的压力和导数残差;上下、左右和形状只负责诊断
// 误差来源和选择参数,避免同一残差在 total 中被重复计算。整个计算过程均
// 位于 log(time)-log(value) 坐标,因此得到的是相对尺度偏差而非原始压力量纲
// 主目标在目标与模拟曲线的公共时间范围内比较压力和导数残差;上下、左右
// 和形状只负责诊断误差来源和选择参数,避免同一残差在 total 中被重复计算。
// 整个计算过程均位于 log(time)-log(value) 坐标。
const double invalidLoss = 1.0e10;
const double valueFloor = 1.0e-12;
const double minimumCoverage = 0.95;
const int numPoints = 80;
m_lastObjectiveBreakdown = AutoFitObjectiveBreakdownLM();
@ -3369,34 +3366,6 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
return isFiniteNumber(*value);
};
// 覆盖率通过后若只缺少首尾少量点,用模拟曲线自身的端点斜率作短距离
// 双对数外推。该外推只用于主损失的固定网格,不参与水平配准搜索。
auto extrapolateEndpointLogValue = [valueFloor](
const QVector<QPointF>& curve,
double x,
double* value) -> bool {
if(!value || curve.size() < 2 || x <= 0.0) {
return false;
}
int left = x < curve.first().x()
? 0
: curve.size() - 2;
int right = left + 1;
double leftLogX = qLn(curve[left].x());
double rightLogX = qLn(curve[right].x());
double denominator = rightLogX - leftLogX;
if(qAbs(denominator) <= 1.0e-12) {
return false;
}
double leftLogY =
qLn(qMax(qAbs(curve[left].y()), valueFloor));
double rightLogY =
qLn(qMax(qAbs(curve[right].y()), valueFloor));
double ratio = (qLn(x) - leftLogX) / denominator;
*value = leftLogY + ratio * (rightLogY - leftLogY);
return isFiniteNumber(*value);
};
const double targetMinX = targetPressure.first().x();
const double targetMaxX = targetPressure.last().x();
const double resultMinX = resultPressure.first().x();
@ -3406,25 +3375,33 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
return invalidLoss;
}
// 与 PSO 保持一致:只在目标与模拟曲线的时间交集内比较,不再设置
// 覆盖率门槛,也不对交集之外的首尾数据做外推。
const double overlapMinX = qMax(targetMinX, resultMinX);
const double overlapMaxX = qMin(targetMaxX, resultMaxX);
if(overlapMinX >= overlapMaxX) {
return invalidLoss;
}
QVector<double> commonX(numPoints);
QVector<double> commonLogX(numPoints);
QVector<double> targetLogPressure(numPoints);
QVector<double> targetLogDerivative(numPoints);
const double targetLogMinX = qLn(targetMinX);
const double targetLogMaxX = qLn(targetMaxX);
const double comparisonLogMinX = qLn(overlapMinX);
const double comparisonLogMaxX = qLn(overlapMaxX);
// 固定使用目标曲线的完整 log-time 网格,候选之间不会因采样点不同而失去可比性。
// 在公共时间范围内生成固定维度的 log-time 网格,保持 LM 残差向量为 160 维
for(int i = 0; i < numPoints; ++i) {
double logX = targetLogMinX +
double logX = comparisonLogMinX +
static_cast<double>(i) *
(targetLogMaxX - targetLogMinX) /
(comparisonLogMaxX - comparisonLogMinX) /
(numPoints - 1);
commonLogX[i] = logX;
// 首尾直接使用原始端点,避免 exp(log(t)) 的舍入误差越过严格插值边界。
if(i == 0) {
commonX[i] = targetMinX;
commonX[i] = overlapMinX;
} else if(i == numPoints - 1) {
commonX[i] = targetMaxX;
commonX[i] = overlapMaxX;
} else {
commonX[i] = qExp(logX);
}
@ -3443,16 +3420,9 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
numPoints, std::numeric_limits<double>::quiet_NaN());
QVector<double> derivativeResidual(
numPoints, std::numeric_limits<double>::quiet_NaN());
int firstSupported = -1;
int lastSupported = -1;
int supportedCount = 0;
// 残差定义为“模拟减目标”:正值表示模拟曲线偏高,负值表示偏低。
for(int i = 0; i < numPoints; ++i) {
if(commonX[i] < resultMinX || commonX[i] > resultMaxX) {
continue;
}
double resultLogPressure = 0.0;
double resultLogDerivative = 0.0;
if(!interpolateLogValue(
@ -3469,59 +3439,9 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
resultLogPressure - targetLogPressure[i];
derivativeResidual[i] =
resultLogDerivative - targetLogDerivative[i];
++supportedCount;
if(firstSupported < 0) {
firstSupported = i;
}
lastSupported = i;
}
// 覆盖率同时约束“有效点数量”和“连续时间跨度”。取两者较小值可避免
// 点数很多但只集中在局部时段的候选被误认为覆盖充分。
AutoFitObjectiveBreakdownLM breakdown;
breakdown.coverage = supportedCount > 0
? static_cast<double>(supportedCount) /
numPoints
: 0.0;
if(firstSupported >= 0 && lastSupported >= firstSupported) {
double targetSpan =
qMax(1.0e-12, targetLogMaxX - targetLogMinX);
double coveredSpan =
commonLogX[lastSupported] - commonLogX[firstSupported];
breakdown.coverage = qMin(
breakdown.coverage,
qMax(0.0, coveredSpan / targetSpan));
}
// 覆盖率只判断候选是否有效,不再加入固定惩罚,避免所有误差被整体抬高。
if(breakdown.coverage < minimumCoverage) {
breakdown.total = invalidLoss;
m_lastObjectiveBreakdown = breakdown;
return invalidLoss;
}
// 通过门槛后最多只缺少首尾少量目标点。按模拟曲线端点趋势补齐后,
// 每个候选仍在固定 80 点上计算均方根误差,不能靠少算难拟合端点获益。
for(int i = 0; i < numPoints; ++i) {
if(isFiniteNumber(pressureResidual[i]) &&
isFiniteNumber(derivativeResidual[i])) {
continue;
}
double resultLogPressure = 0.0;
double resultLogDerivative = 0.0;
if(!extrapolateEndpointLogValue(
resultPressure, commonX[i],
&resultLogPressure) ||
!extrapolateEndpointLogValue(
resultDerivative, commonX[i],
&resultLogDerivative)) {
return invalidLoss;
}
pressureResidual[i] =
resultLogPressure - targetLogPressure[i];
derivativeResidual[i] =
resultLogDerivative - targetLogDerivative[i];
}
// 在指定中心附近计算普通均方根误差。
auto rmseAround = [](
@ -3648,7 +3568,7 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
}
const double logGridStep =
(targetLogMaxX - targetLogMinX) /
(comparisonLogMaxX - comparisonLogMinX) /
(numPoints - 1);
const double resultLogMinX = qLn(resultMinX);
const double resultLogMaxX = qLn(resultMaxX);
@ -4091,7 +4011,7 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
m_lastObjectiveBreakdown = breakdown;
DEBUG_OUT(
QString("LogLog objective: pressure=%1, derivative=%2, vertical=%3, horizontal=%4, shape=%5, ambiguous=%6, shift=%7, coverage=%8, total=%9")
QString("LogLog objective: pressure=%1, derivative=%2, vertical=%3, horizontal=%4, shape=%5, ambiguous=%6, shift=%7, total=%8")
.arg(breakdown.pressureLoss, 0, 'e', 4)
.arg(breakdown.derivativeLoss, 0, 'e', 4)
.arg(breakdown.verticalLoss, 0, 'e', 4)
@ -4099,7 +4019,6 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
.arg(breakdown.shapeLoss, 0, 'e', 4)
.arg(breakdown.registrationAmbiguous)
.arg(breakdown.horizontalPhysicalShift, 0, 'e', 4)
.arg(breakdown.coverage, 0, 'f', 4)
.arg(breakdown.total, 0, 'e', 4));
return breakdown.valid

Loading…
Cancel
Save