|
|
|
|
@ -1041,7 +1041,7 @@ void nmCalculationAutoFitLM::writeTraceMetaFile()
|
|
|
|
|
|
|
|
|
|
QTextStream out(&metaFile);
|
|
|
|
|
out << "{\n";
|
|
|
|
|
out << " \"schema_version\": 9,\n";
|
|
|
|
|
out << " \"schema_version\": 10,\n";
|
|
|
|
|
out << " \"strategy\": \"permeability_height_then_shape_then_original_lm\",\n";
|
|
|
|
|
out << " \"shape_metric\": \"pressure_and_derivative_log_slopes_81_points_lag_8\",\n";
|
|
|
|
|
out << " \"shape_stage_total_tolerance\": \"max(0.02, 25% of stage entry total)\",\n";
|
|
|
|
|
@ -1819,15 +1819,17 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
|
const int maximumIneffectiveSteps = 3;
|
|
|
|
|
|
|
|
|
|
// damping 是 LM 阻尼;拒绝或预测失准时增大,真实下降与预测一致时减小。
|
|
|
|
|
// 两组累计量控制 Jacobian 重建,避免长期使用已偏离当前工作点的局部模型。
|
|
|
|
|
// 按连续预测失准刷新 Jacobian;接受步数只作兜底,不因累计移动距离强制重建。
|
|
|
|
|
double trustRadius = 0.12;
|
|
|
|
|
double damping = 1.0e-2;
|
|
|
|
|
int consecutiveRejectedSteps = 0;
|
|
|
|
|
int consecutiveSolverFailures = 0;
|
|
|
|
|
int acceptedSinceRebuild = 0;
|
|
|
|
|
int consecutiveIneffectiveSteps = 0;
|
|
|
|
|
double movementSinceRebuild = 0.0;
|
|
|
|
|
int consecutivePoorPredictions = 0;
|
|
|
|
|
bool rebuildRequested = true;
|
|
|
|
|
// 跟踪文件保留稳定英文原因,界面输出时再翻译,避免受本地编码影响。
|
|
|
|
|
const char* rebuildReason = QT_TR_NOOP("initial sensitivity model");
|
|
|
|
|
bool modelRebuiltAtMinimumRadius = false;
|
|
|
|
|
bool stagnationConfirmationRequested = false;
|
|
|
|
|
QVector<bool> attemptedWindows(kAutoFitTimeWindowCount, false);
|
|
|
|
|
@ -2061,12 +2063,13 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
|
jacobian.clear();
|
|
|
|
|
jacobianColumnValid.fill(false);
|
|
|
|
|
rebuildRequested = true;
|
|
|
|
|
rebuildReason = QT_TR_NOOP("sampling layer changed");
|
|
|
|
|
trustRadius = 0.12;
|
|
|
|
|
damping = 1.0e-2;
|
|
|
|
|
consecutiveRejectedSteps = 0;
|
|
|
|
|
consecutiveIneffectiveSteps = 0;
|
|
|
|
|
acceptedSinceRebuild = 0;
|
|
|
|
|
movementSinceRebuild = 0.0;
|
|
|
|
|
consecutivePoorPredictions = 0;
|
|
|
|
|
modelRebuiltAtMinimumRadius = false;
|
|
|
|
|
stagnationConfirmationRequested = false;
|
|
|
|
|
attemptedWindows.fill(false);
|
|
|
|
|
@ -2088,7 +2091,12 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
|
|
|
|
|
|
auto enterTotalStage = [&](const QString& reason) {
|
|
|
|
|
// 保留当前曲线和完整 J,只重置阶段停滞状态、阻尼及信赖半径。
|
|
|
|
|
rebuildRequested = jacobian.isEmpty() || consecutiveSolverFailures > 0;
|
|
|
|
|
rebuildRequested = jacobian.isEmpty() || consecutiveSolverFailures > 0 || acceptedSinceRebuild >= 10;
|
|
|
|
|
if(jacobian.isEmpty()) rebuildReason = QT_TR_NOOP("no valid sensitivity model");
|
|
|
|
|
else if(consecutiveSolverFailures > 0) rebuildReason = QT_TR_NOOP("solver failure before stage switch");
|
|
|
|
|
else if(acceptedSinceRebuild >= 10) rebuildReason = QT_TR_NOOP("10 accepted steps since last rebuild");
|
|
|
|
|
// 阶段目标已改变,不把形状目标下的预测失准累计到整体阶段。
|
|
|
|
|
consecutivePoorPredictions = 0;
|
|
|
|
|
modelRebuiltAtMinimumRadius = false;
|
|
|
|
|
shapeStage = false;
|
|
|
|
|
effectiveImprovementBaseline = current.fitness;
|
|
|
|
|
@ -2148,6 +2156,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
|
|
|
|
|
|
stagnationConfirmationRequested = true;
|
|
|
|
|
rebuildRequested = true;
|
|
|
|
|
rebuildReason = QT_TR_NOOP("confirm stagnation after ineffective steps");
|
|
|
|
|
emit logMessageGenerated(
|
|
|
|
|
tr("No effective improvement for %1 consecutive steps; "
|
|
|
|
|
"rebuilding sensitivity model for confirmation")
|
|
|
|
|
@ -2324,7 +2333,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
acceptedSinceRebuild = 0;
|
|
|
|
|
movementSinceRebuild = 0.0;
|
|
|
|
|
consecutivePoorPredictions = 0;
|
|
|
|
|
consecutiveRejectedSteps = 0;
|
|
|
|
|
rebuildRequested = false;
|
|
|
|
|
// 若重建过程中接受了试算点,当前模型已通过割线平移而不是在新点完整
|
|
|
|
|
@ -2356,18 +2365,34 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
|
enterTotalStage(tr("reserve remaining iterations and evaluations for total fitting"));
|
|
|
|
|
}
|
|
|
|
|
if(!shapeStage && m_layeredSampling && m_samplingStride > 1) {
|
|
|
|
|
const bool reserveFinalBudget = maximumEvaluations - m_totalEvaluations <= 2 * (dimensions + 1);
|
|
|
|
|
const int remainingEvaluations = maximumEvaluations - m_totalEvaluations;
|
|
|
|
|
// 已准备重建时,提前计入本轮差分及候选的开销,避免紧接着因预算再加密。
|
|
|
|
|
const bool reserveFinalBudget = remainingEvaluations <= 2 * (dimensions + 1) ||
|
|
|
|
|
(rebuildRequested && remainingEvaluations <= 3 * (dimensions + 1));
|
|
|
|
|
const int layerDeadline = qMax(1, m_maxIterations * (m_samplingStride == 4 ? 1 : 2) / 3);
|
|
|
|
|
if(reserveFinalBudget || iteration >= layerDeadline) {
|
|
|
|
|
promoteSampling(reserveFinalBudget);
|
|
|
|
|
const bool mergeRefinement = rebuildRequested && iteration < layerDeadline &&
|
|
|
|
|
(iteration + 1 >= layerDeadline || reserveFinalBudget);
|
|
|
|
|
if(reserveFinalBudget || iteration >= layerDeadline || mergeRefinement) {
|
|
|
|
|
const bool promoted = promoteSampling(reserveFinalBudget);
|
|
|
|
|
if(samplingRefreshFailed) break;
|
|
|
|
|
if(promoted && mergeRefinement) {
|
|
|
|
|
rebuildReason = QT_TR_NOOP("sampling refinement merged with pending rebuild");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(rebuildRequested) {
|
|
|
|
|
emit logMessageGenerated(tr("Rebuilding sensitivity model: %1").arg(tr(rebuildReason)));
|
|
|
|
|
writeTraceRow(m_currentIteration, -1, "sensitivity_rebuild", current.parameters,
|
|
|
|
|
current.fitness, true, 0, rebuildReason, ¤t.breakdown);
|
|
|
|
|
const bool confirmingStagnation =
|
|
|
|
|
stagnationConfirmationRequested;
|
|
|
|
|
if(!rebuildSensitivity()) {
|
|
|
|
|
if(shapeStage && !m_shouldStop) { enterTotalStage(tr("no valid shape sensitivity model")); rebuildRequested = true; continue; }
|
|
|
|
|
if(shapeStage && !m_shouldStop) {
|
|
|
|
|
enterTotalStage(tr("no valid shape sensitivity model"));
|
|
|
|
|
rebuildRequested = true;
|
|
|
|
|
rebuildReason = QT_TR_NOOP("no valid sensitivity model");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if(promoteSampling(false)) continue;
|
|
|
|
|
stopReason = m_shouldStop
|
|
|
|
|
? LM_USER_STOPPED
|
|
|
|
|
@ -2507,6 +2532,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
|
trustRadius = qMax(minimumTrustRadius, trustRadius * 0.5);
|
|
|
|
|
damping = qMin(1.0e8, damping * 4.0);
|
|
|
|
|
rebuildRequested = true;
|
|
|
|
|
rebuildReason = QT_TR_NOOP("no feasible descent step");
|
|
|
|
|
if(recordIneffectiveStep()) {
|
|
|
|
|
if(promoteSampling(false)) continue;
|
|
|
|
|
stopReason = LM_LOCAL_OPTIMUM;
|
|
|
|
|
@ -2544,6 +2570,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
|
// 对应误差快照,再缩小信赖域;连续失败达到上限才终止整个拟合。
|
|
|
|
|
++consecutiveSolverFailures;
|
|
|
|
|
++consecutiveRejectedSteps;
|
|
|
|
|
consecutivePoorPredictions = 0;
|
|
|
|
|
trustRadius = qMax(minimumTrustRadius, trustRadius * 0.5);
|
|
|
|
|
damping = qMin(1.0e8, damping * 4.0);
|
|
|
|
|
writeTraceRow(m_currentIteration,
|
|
|
|
|
@ -2556,8 +2583,9 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
|
"solver_invalid_" + selectionName,
|
|
|
|
|
nullptr);
|
|
|
|
|
restoreEvaluationState(current);
|
|
|
|
|
if(consecutiveRejectedSteps >= 2) {
|
|
|
|
|
if(consecutiveSolverFailures >= 2) {
|
|
|
|
|
rebuildRequested = true;
|
|
|
|
|
rebuildReason = QT_TR_NOOP("2 consecutive solver failures");
|
|
|
|
|
}
|
|
|
|
|
if(consecutiveSolverFailures >= m_maxConsecutiveFailures) {
|
|
|
|
|
stopReason = LM_CONSECUTIVE_FAILURES;
|
|
|
|
|
@ -2582,9 +2610,20 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
|
coordinateStep);
|
|
|
|
|
// reductionRatio 衡量局部线性模型的可信度:接近 1 表示预测准确;
|
|
|
|
|
// 值较小表示虽然可能下降,但模型低估了非线性,需要收紧下一步。
|
|
|
|
|
double actualReduction = 0.5 * (trustRegionSquaredNorm(objectiveResidual) -
|
|
|
|
|
const double objectiveEnergy = 0.5 * trustRegionSquaredNorm(objectiveResidual);
|
|
|
|
|
double actualReduction = objectiveEnergy - 0.5 * (
|
|
|
|
|
trustRegionSquaredNorm(shapeStage ? candidate.breakdown.shapeResiduals : candidate.breakdown.residualVector));
|
|
|
|
|
double reductionRatio = actualReduction / predictedReduction;
|
|
|
|
|
// 使用同一采样层、同一阶段目标比较预测和实际改善。接近收敛时的微小
|
|
|
|
|
// 预测量交给停滞逻辑处理,避免比例数值波动反复触发昂贵的全参数重建。
|
|
|
|
|
const double predictionFloor = qMax(1.0e-14, objectiveEnergy * 1.0e-8);
|
|
|
|
|
const bool poorPrediction = predictedReduction > predictionFloor &&
|
|
|
|
|
(!isFiniteNumber(reductionRatio) || reductionRatio < 0.25);
|
|
|
|
|
consecutivePoorPredictions = poorPrediction ? consecutivePoorPredictions + 1 : 0;
|
|
|
|
|
if(consecutivePoorPredictions >= 2) {
|
|
|
|
|
rebuildRequested = true;
|
|
|
|
|
rebuildReason = QT_TR_NOOP("2 consecutive steps with actual improvement below 25% of prediction");
|
|
|
|
|
}
|
|
|
|
|
bool accepted = acceptable(candidate, current);
|
|
|
|
|
// 粗层认为下降而完整数据不认可时累计,连续两次就提前加密。
|
|
|
|
|
if(!shapeStage && m_layeredSampling && m_samplingStride > 1 && !accepted && actualReduction > 0.0) {
|
|
|
|
|
@ -2599,7 +2638,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
|
// 模型预测可靠时减小阻尼并可扩大半径,预测较差时保守收缩。
|
|
|
|
|
acceptPoint(candidate);
|
|
|
|
|
++acceptedSinceRebuild;
|
|
|
|
|
movementSinceRebuild += stepNorm;
|
|
|
|
|
consecutiveRejectedSteps = 0;
|
|
|
|
|
|
|
|
|
|
if(reductionRatio > 0.75) {
|
|
|
|
|
@ -2616,21 +2654,19 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
|
minimumTrustRadius, trustRadius * 0.75);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(acceptedSinceRebuild >= 6 ||
|
|
|
|
|
movementSinceRebuild >= 0.30) {
|
|
|
|
|
if(acceptedSinceRebuild >= 10 && !rebuildRequested) {
|
|
|
|
|
rebuildRequested = true;
|
|
|
|
|
rebuildReason = QT_TR_NOOP("10 accepted steps since last rebuild");
|
|
|
|
|
}
|
|
|
|
|
modelRebuiltAtMinimumRadius = false;
|
|
|
|
|
} else {
|
|
|
|
|
// 拒绝时 candidate 只保留在 trace 中,DataManager 和内存状态都恢复
|
|
|
|
|
// 到 current。连续拒绝说明割线模型可能失真,因此请求重新试算灵敏度。
|
|
|
|
|
// 到 current。形状上限或全目标点验收也可能拒绝候选,不能仅凭拒绝
|
|
|
|
|
// 次数认定模型失准;重建由上面的预测质量判断,约束冲突先缩步。
|
|
|
|
|
++consecutiveRejectedSteps;
|
|
|
|
|
damping = qMin(1.0e8, damping * 4.0);
|
|
|
|
|
trustRadius = qMax(minimumTrustRadius, trustRadius * 0.5);
|
|
|
|
|
restoreEvaluationState(current);
|
|
|
|
|
if(consecutiveRejectedSteps >= 2) {
|
|
|
|
|
rebuildRequested = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 候选只要更优就继续作为 current 保存;是否足以解除停滞,则统一
|
|
|
|
|
@ -2687,13 +2723,14 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if(selectedWindow < 0 && trustRadius <= minimumTrustRadius * 1.01 &&
|
|
|
|
|
consecutiveRejectedSteps >= 2) {
|
|
|
|
|
consecutiveRejectedSteps >= 2 && consecutivePoorPredictions >= 2) {
|
|
|
|
|
if(modelRebuiltAtMinimumRadius) {
|
|
|
|
|
if(promoteSampling(false)) continue;
|
|
|
|
|
stopReason = LM_LOCAL_OPTIMUM;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
rebuildRequested = true;
|
|
|
|
|
rebuildReason = QT_TR_NOOP("inaccurate model at minimum trust radius");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|