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

develop
lvjunjie 1 week ago
parent 9df3da7b7c
commit d0c0f70d8b

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

@ -860,8 +860,7 @@ void nmCalculationAutoFitLM::writeTraceHeader()
<< "late_trend_loss" << "late_trend_loss"
<< "late_slope_bias" << "late_slope_bias"
<< "late_trend_reliable" << "late_trend_reliable"
<< "registration_ambiguous" << "registration_ambiguous";
<< "coverage";
QTextStream out(&m_traceFile); QTextStream out(&m_traceFile);
out << cols.join(",") << "\n"; out << cols.join(",") << "\n";
@ -978,10 +977,9 @@ void nmCalculationAutoFitLM::writeTraceRow(
<< traceNumber(objectiveBreakdown->lateDerivativeTrendLoss) << traceNumber(objectiveBreakdown->lateDerivativeTrendLoss)
<< traceNumber(objectiveBreakdown->lateDerivativeSlopeBias) << traceNumber(objectiveBreakdown->lateDerivativeSlopeBias)
<< QString::number(objectiveBreakdown->lateDerivativeTrendReliable ? 1 : 0) << QString::number(objectiveBreakdown->lateDerivativeTrendReliable ? 1 : 0)
<< QString::number(objectiveBreakdown->registrationAmbiguous ? 1 : 0) << QString::number(objectiveBreakdown->registrationAmbiguous ? 1 : 0);
<< traceNumber(objectiveBreakdown->coverage);
} else { } else {
for(int i = 0; i < 14; ++i) { for(int i = 0; i < 13; ++i) {
cols << QString(); cols << QString();
} }
} }
@ -3247,12 +3245,11 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
const QVector<QVector<double> >& target, const QVector<QVector<double> >& target,
const QVector<QVector<double> >& result) const const QVector<QVector<double> >& result) const
{ {
// 主目标只比较固定网格上的压力和导数残差;上下、左右和形状只负责诊断 // 主目标在目标与模拟曲线的公共时间范围内比较压力和导数残差;上下、左右
// 误差来源和选择参数,避免同一残差在 total 中被重复计算。整个计算过程均 // 和形状只负责诊断误差来源和选择参数,避免同一残差在 total 中被重复计算。
// 位于 log(time)-log(value) 坐标,因此得到的是相对尺度偏差而非原始压力量纲 // 整个计算过程均位于 log(time)-log(value) 坐标。
const double invalidLoss = 1.0e10; const double invalidLoss = 1.0e10;
const double valueFloor = 1.0e-12; const double valueFloor = 1.0e-12;
const double minimumCoverage = 0.95;
const int numPoints = 80; const int numPoints = 80;
m_lastObjectiveBreakdown = AutoFitObjectiveBreakdownLM(); m_lastObjectiveBreakdown = AutoFitObjectiveBreakdownLM();
@ -3369,34 +3366,6 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
return isFiniteNumber(*value); 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 targetMinX = targetPressure.first().x();
const double targetMaxX = targetPressure.last().x(); const double targetMaxX = targetPressure.last().x();
const double resultMinX = resultPressure.first().x(); const double resultMinX = resultPressure.first().x();
@ -3406,25 +3375,33 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
return invalidLoss; 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> commonX(numPoints);
QVector<double> commonLogX(numPoints); QVector<double> commonLogX(numPoints);
QVector<double> targetLogPressure(numPoints); QVector<double> targetLogPressure(numPoints);
QVector<double> targetLogDerivative(numPoints); QVector<double> targetLogDerivative(numPoints);
const double targetLogMinX = qLn(targetMinX); const double comparisonLogMinX = qLn(overlapMinX);
const double targetLogMaxX = qLn(targetMaxX); const double comparisonLogMaxX = qLn(overlapMaxX);
// 固定使用目标曲线的完整 log-time 网格,候选之间不会因采样点不同而失去可比性。 // 在公共时间范围内生成固定维度的 log-time 网格,保持 LM 残差向量为 160 维
for(int i = 0; i < numPoints; ++i) { for(int i = 0; i < numPoints; ++i) {
double logX = targetLogMinX + double logX = comparisonLogMinX +
static_cast<double>(i) * static_cast<double>(i) *
(targetLogMaxX - targetLogMinX) / (comparisonLogMaxX - comparisonLogMinX) /
(numPoints - 1); (numPoints - 1);
commonLogX[i] = logX; commonLogX[i] = logX;
// 首尾直接使用原始端点,避免 exp(log(t)) 的舍入误差越过严格插值边界。 // 首尾直接使用原始端点,避免 exp(log(t)) 的舍入误差越过严格插值边界。
if(i == 0) { if(i == 0) {
commonX[i] = targetMinX; commonX[i] = overlapMinX;
} else if(i == numPoints - 1) { } else if(i == numPoints - 1) {
commonX[i] = targetMaxX; commonX[i] = overlapMaxX;
} else { } else {
commonX[i] = qExp(logX); commonX[i] = qExp(logX);
} }
@ -3443,16 +3420,9 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
numPoints, std::numeric_limits<double>::quiet_NaN()); numPoints, std::numeric_limits<double>::quiet_NaN());
QVector<double> derivativeResidual( QVector<double> derivativeResidual(
numPoints, std::numeric_limits<double>::quiet_NaN()); numPoints, std::numeric_limits<double>::quiet_NaN());
int firstSupported = -1;
int lastSupported = -1;
int supportedCount = 0;
// 残差定义为“模拟减目标”:正值表示模拟曲线偏高,负值表示偏低。 // 残差定义为“模拟减目标”:正值表示模拟曲线偏高,负值表示偏低。
for(int i = 0; i < numPoints; ++i) { for(int i = 0; i < numPoints; ++i) {
if(commonX[i] < resultMinX || commonX[i] > resultMaxX) {
continue;
}
double resultLogPressure = 0.0; double resultLogPressure = 0.0;
double resultLogDerivative = 0.0; double resultLogDerivative = 0.0;
if(!interpolateLogValue( if(!interpolateLogValue(
@ -3469,59 +3439,9 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
resultLogPressure - targetLogPressure[i]; resultLogPressure - targetLogPressure[i];
derivativeResidual[i] = derivativeResidual[i] =
resultLogDerivative - targetLogDerivative[i]; resultLogDerivative - targetLogDerivative[i];
++supportedCount;
if(firstSupported < 0) {
firstSupported = i;
}
lastSupported = i;
} }
// 覆盖率同时约束“有效点数量”和“连续时间跨度”。取两者较小值可避免
// 点数很多但只集中在局部时段的候选被误认为覆盖充分。
AutoFitObjectiveBreakdownLM breakdown; 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 = []( auto rmseAround = [](
@ -3648,7 +3568,7 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
} }
const double logGridStep = const double logGridStep =
(targetLogMaxX - targetLogMinX) / (comparisonLogMaxX - comparisonLogMinX) /
(numPoints - 1); (numPoints - 1);
const double resultLogMinX = qLn(resultMinX); const double resultLogMinX = qLn(resultMinX);
const double resultLogMaxX = qLn(resultMaxX); const double resultLogMaxX = qLn(resultMaxX);
@ -4091,7 +4011,7 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
m_lastObjectiveBreakdown = breakdown; m_lastObjectiveBreakdown = breakdown;
DEBUG_OUT( 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.pressureLoss, 0, 'e', 4)
.arg(breakdown.derivativeLoss, 0, 'e', 4) .arg(breakdown.derivativeLoss, 0, 'e', 4)
.arg(breakdown.verticalLoss, 0, 'e', 4) .arg(breakdown.verticalLoss, 0, 'e', 4)
@ -4099,7 +4019,6 @@ double nmCalculationAutoFitLM::calculateLogLogCurveError(
.arg(breakdown.shapeLoss, 0, 'e', 4) .arg(breakdown.shapeLoss, 0, 'e', 4)
.arg(breakdown.registrationAmbiguous) .arg(breakdown.registrationAmbiguous)
.arg(breakdown.horizontalPhysicalShift, 0, 'e', 4) .arg(breakdown.horizontalPhysicalShift, 0, 'e', 4)
.arg(breakdown.coverage, 0, 'f', 4)
.arg(breakdown.total, 0, 'e', 4)); .arg(breakdown.total, 0, 'e', 4));
return breakdown.valid return breakdown.valid

Loading…
Cancel
Save