diff --git a/Bin/Config/Lang/cn/nmNum_cn.qm b/Bin/Config/Lang/cn/nmNum_cn.qm
index 776d7652..a0b706a0 100644
Binary files a/Bin/Config/Lang/cn/nmNum_cn.qm and b/Bin/Config/Lang/cn/nmNum_cn.qm differ
diff --git a/Bin/Config/Lang/cn/nmNum_cn.ts b/Bin/Config/Lang/cn/nmNum_cn.ts
index 6515933d..623ec74d 100644
--- a/Bin/Config/Lang/cn/nmNum_cn.ts
+++ b/Bin/Config/Lang/cn/nmNum_cn.ts
@@ -1277,16 +1277,16 @@ Reason: %1
接受灵敏度探针:形状误差=%1
- fresh sensitivity at joint shape entry
- 进入联合形状调整时重建灵敏度
+ fresh sensitivity at single-parameter shape entry
+ 进入单参数形状调整时重建灵敏度
Adaptive fitting counts: %1 shape attempts, %2 total-stage iterations, %3 total evaluations.
自适应拟合统计:形状尝试 %1 次,整体阶段迭代 %2 次,累计评估 %3 次。
- LM stage 2: joint shape search; after 2 ineffective steps, refresh sensitivity and confirm with one global step.
- LM 第二阶段:联合调整全部可调形状参数;连续两次改善不足后,刷新灵敏度并用一次全局联合调整确认。
+ LM stage 2: adjust each shape parameter once; double the step after improvement, halve it after rejection, and switch after 3 consecutive rejections.
+ LM 第二阶段:逐个调整形状参数,每个参数只调一轮;改善后步长翻倍,拒绝后减半,连续拒绝三次后换下一个参数。
LM starting point error: %1; independent total-stage evaluation budget: %2
@@ -1313,8 +1313,8 @@ Reason: %1
进入整体阶段时建立完整灵敏度
- no significant shape improvement after fresh sensitivity and one global step
- 刷新灵敏度并进行一次全局联合调整后,形状仍无显著改善
+ all shape parameters visited once
+ 所有形状参数均已完成一轮调整
no valid early sensitivity model
diff --git a/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp b/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp
index 78ab27e9..55d8c7a6 100644
--- a/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp
+++ b/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp
@@ -671,6 +671,30 @@ static bool buildTrustRegionFisherStep(
return projectAndPredict();
}
+// 为每个可调参数独立求解 LM 步,按边界投影后的预测下降量选择一个参数。
+static bool buildBestSingleParameterStep(const TrustRegionFisher& information,
+ const QVector& available, const QVector& coordinates,
+ double damping, double trustRadius, double minimumStep,
+ QVector* selected, QVector* step, double* predictedReduction)
+{
+ selected->clear();
+ step->fill(0.0, coordinates.size());
+ *predictedReduction = 0.0;
+ for(int i = 0; i < available.size(); ++i) {
+ QVector singleColumn(1, available[i]);
+ QVector trialStep;
+ double trialPrediction = 0.0;
+ if(buildTrustRegionFisherStep(information, singleColumn, coordinates,
+ damping, trustRadius, minimumStep, &trialStep, &trialPrediction) &&
+ trialPrediction > *predictedReduction) {
+ *selected = singleColumn;
+ *step = trialStep;
+ *predictedReduction = trialPrediction;
+ }
+ }
+ return !selected->isEmpty();
+}
+
// 前期每次只调一个参数:间距锁定符号,形状 LM 提供幅度和后续验收依据。
static bool buildEarlyGapGuidedStep(const TrustRegionFisher& shapeInformation,
const QVector& selected, const QVector& coordinates, double direction,
@@ -691,7 +715,7 @@ static bool buildEarlyGapGuidedStep(const TrustRegionFisher& shapeInformation,
}
// 放大当前方向,仍受传入的最大半径和参数边界限制。
-// 普通形状步要求预测下降;前期初始探路可跨过预测上坡区,最终仍按真实形状验收。
+// 前期初始探路可跨过预测上坡区,最终仍按真实形状验收。
static bool buildExpandedTrustRegionStep(const TrustRegionFisher& information,
const QVector& coordinates, const QVector& originalStep,
double maximumRadius, QVector* expandedStep, double* prediction,
@@ -1123,7 +1147,7 @@ void nmCalculationAutoFitLM::writeTraceMetaFile()
QTextStream out(&metaFile);
out << "{\n";
- out << " \"schema_version\": 36,\n";
+ out << " \"schema_version\": 39,\n";
out << " \"strategy\": \"permeability_height_then_shape_then_joint_lm\",\n";
out << " \"height_acceptance\": \"reliable_vertical_loss_decrease; no_shape_or_total_loss_constraint\",\n";
out << " \"shape_priority\": \"storage_then_skin_then_shape_without_wellbore_then_optional_wellbore_recheck\",\n";
@@ -1138,11 +1162,12 @@ void nmCalculationAutoFitLM::writeTraceMetaFile()
out << " \"early_wellbore_total_constraint\": false,\n";
out << " \"shape_stage_total_constraint\": false,\n";
out << " \"shape_wellbore_parameters_frozen\": true,\n";
- out << " \"shape_sensitivity_refresh\": \"non_wellbore_columns_only at joint shape entry and stagnation confirmation; full Jacobian rebuilt at total-stage entry\",\n";
- out << " \"shape_expansion_policy\": \"one expansion per accepted shape direction, up to 2x with predicted descent and parameter bounds; no_rho_gate; retain ordinary step on failure\",\n";
- out << " \"shape_global_parameter_selection\": \"all_valid_non_wellbore_columns_with_positive_response_at_every_shape_step; no_window_selection\",\n";
- out << " \"stage2_budget\": \"adaptive, no fixed iteration or evaluation quota; 2 ineffective shape attempts then one fresh-J global confirmation\",\n";
- out << " \"shape_step_improvement_threshold\": \"max(0.0001, 10% of last effective shape loss); accumulate small improvements\",\n";
+ out << " \"shape_sensitivity_refresh\": \"non_wellbore_columns_once_at_sweep_entry; secant updates during sweep; full Jacobian rebuilt at total-stage entry\",\n";
+ out << " \"shape_expansion_policy\": \"lock parameter and direction; after each strict shape decrease double next coordinate step from accepted point; otherwise retain point and halve step\",\n";
+ out << " \"shape_radius_policy\": \"initial LM step capped at 0.24; subsequent coordinate steps capped at 0.60; no prediction-ratio gate\",\n";
+ out << " \"shape_global_parameter_selection\": \"largest predicted reduction among unvisited non-wellbore parameters; finish current parameter before selecting next; each parameter visited once\",\n";
+ out << " \"stage2_budget\": \"one parameter sweep; each parameter stops after 3 consecutive non-improving trials, a bound, or minimum step; existing solver failure limit retained\",\n";
+ out << " \"shape_step_improvement_threshold\": \"any strict shape loss decrease; no cumulative improvement threshold\",\n";
out << " \"iteration_count_scope\": \"max_iterations applies to total-stage LM; trace iteration remains cumulative\",\n";
out << " \"wellbore_recheck_trigger\": \"early loss exceeds initial early-stage exit by max(0.0001, 10%)\",\n";
out << " \"wellbore_recheck_acceptance\": \"early decrease with fixed entry shape limit max(0.0001, 5%); no_total_loss_constraint\",\n";
@@ -1924,6 +1949,9 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
const double minimumCoordinateStep = 1.0e-5;
const double minimumTrustRadius = 2.0e-3;
const double maximumTrustRadius = 0.30;
+ // 第二阶段整体形状允许更大步幅;第三阶段继续使用原来的半径和预测验收规则。
+ const double initialShapeTrustRadius = 0.24;
+ const double maximumShapeTrustRadius = 0.60;
// 误差下降至少达到绝对 1e-5 且相对当前有效基准 0.2% 才算有效改善。
// 更小的下降仍保留为最佳解,但不能反复清除停滞状态、延长拟合时间。
const double effectiveRelativeImprovement = 2.0e-3;
@@ -2120,14 +2148,16 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
if(m_shouldStop) return LM_USER_STOPPED;
- // 第二阶段不按迭代比例截断;连续两次改善不足后,刷新灵敏度并仅做一次全局联合确认。
+ // 第二阶段先测灵敏度,再逐个调完非井筒参数;每个参数只访问一次。
// 普通形状调整冻结井储、表皮,只按形状验收;总误差达标只在第三阶段判断。
bool shapeStage = true;
- bool jointShapeStarted = false;
- double shapeImprovementBaseline = current.breakdown.shapeLoss;
+ bool singleShapeStarted = false;
int shapeStepCount = 0;
- int ineffectiveShapeSteps = 0;
- bool shapeConfirmationRequested = false;
+ QVector shapeParameterFinished(dimensions, false);
+ int activeShapeColumn = -1;
+ double shapeCoordinateStep = 0.0;
+ int consecutiveShapeRejections = 0;
+ const int maximumShapeRejections = 3;
const int storageColumn = m_enabledParamIndices.indexOf(2);
const int skinColumn = m_enabledParamIndices.indexOf(1);
bool hasRemainingShapeParameters = false;
@@ -2193,7 +2223,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
restoreEvaluationState(current);
};
emit logMessageGenerated(shapeStage
- ? tr("LM stage 2: joint shape search; after 2 ineffective steps, refresh sensitivity and confirm with one global step.")
+ ? tr("LM stage 2: adjust each shape parameter once; double the step after improvement, halve it after rejection, and switch after 3 consecutive rejections.")
: tr("LM stage 3: original LM fitting; accept by total error only."));
if(earlyShapeStage) {
emit logMessageGenerated(earlyShapeParameter == 2
@@ -2267,7 +2297,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
consecutiveSolverFailures = 0;
stagnationConfirmationRequested = false;
globalFallbackAttempted = false;
- trustRadius = 0.12;
+ trustRadius = shapeStage && !earlyShapeStage ? initialShapeTrustRadius : 0.12;
damping = 0.01;
};
auto enterTotalStage = [&](const QString& reason) {
@@ -2362,38 +2392,37 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
true, 0, "early_wellbore_to_shape", ¤t.breakdown);
};
- auto completeShapeSearchStep = [&]() {
- if(!shapeStage || earlyShapeStage || m_shouldStop) return;
- // 改善相对上一次有效改善累计,避免单次小步被立即否定;不再等待完整窗口轮次。
- const double required = qMax(1.0e-4, 0.10 * shapeImprovementBaseline);
- const double improvement = shapeImprovementBaseline - current.breakdown.shapeLoss;
- const bool improved = improvement >= required;
- ++shapeStepCount;
- writeTraceRow(m_currentIteration, -1, "shape_step_end", current.parameters, current.fitness,
- true, 0, QString(improved ? "improved_step_%1" : "ineffective_step_%1").arg(shapeStepCount), ¤t.breakdown);
- emit logMessageGenerated(tr("Shape search step %1: shape error=%2, accumulated improvement=%3, required=%4.")
- .arg(shapeStepCount).arg(current.breakdown.shapeLoss, 0, 'g', 6)
- .arg(improvement, 0, 'g', 6).arg(required, 0, 'g', 6));
- // 每次形状调整都是全局联合步,结束后清除本次尝试标记。
- globalFallbackAttempted = false;
- if(improved) {
- shapeImprovementBaseline = current.breakdown.shapeLoss;
- ineffectiveShapeSteps = 0;
- shapeConfirmationRequested = false;
- return;
- }
- ++ineffectiveShapeSteps;
- if(shapeConfirmationRequested) {
- finishShapeStage(tr("no significant shape improvement after fresh sensitivity and one global step"));
- return;
+ auto finishShapeParameter = [&](const QString& reason) {
+ // 已完成的参数不再参与后续选优,保证所有可调参数只扫一遍。
+ if(activeShapeColumn >= 0) {
+ shapeParameterFinished[activeShapeColumn] = true;
+ writeTraceRow(m_currentIteration, activeShapeColumn, "shape_parameter_end",
+ current.parameters, current.fitness, true, 0, reason, ¤t.breakdown);
}
- if(ineffectiveShapeSteps >= 2) {
- jacobian.clear();
- reuseSensitivityForNextStage();
- rebuildRequested = true;
- rebuildReason = QT_TR_NOOP("confirm shape stagnation after 2 ineffective steps");
- shapeConfirmationRequested = true;
+ activeShapeColumn = -1;
+ shapeCoordinateStep = 0.0;
+ consecutiveShapeRejections = 0;
+ for(int column = 0; column < dimensions; ++column) {
+ if(parameterAllowedInStage(column) && !shapeParameterFinished[column]) return;
}
+ finishShapeStage(tr("all shape parameters visited once"));
+ };
+ auto completeShapeSearchStep = [&](bool accepted) {
+ if(!shapeStage || earlyShapeStage || m_shouldStop) return;
+ // 只按本次真实下降控制实际步长;不再使用累计 10% 门槛提前结束整轮。
+ ++shapeStepCount;
+ consecutiveShapeRejections = accepted ? 0 : consecutiveShapeRejections + 1;
+ shapeCoordinateStep = qBound(-maximumShapeTrustRadius,
+ shapeCoordinateStep * (accepted ? 2.0 : 0.5), maximumShapeTrustRadius);
+ writeTraceRow(m_currentIteration, activeShapeColumn, "shape_step_end", current.parameters,
+ current.fitness, true, 0,
+ QString("%1_step_%2_rejections_%3_next_delta_%4")
+ .arg(accepted ? "improved" : "rejected").arg(shapeStepCount)
+ .arg(consecutiveShapeRejections).arg(shapeCoordinateStep, 0, 'g', 12), ¤t.breakdown);
+ if(consecutiveShapeRejections >= maximumShapeRejections)
+ finishShapeParameter("3_consecutive_non_improving_trials");
+ else if(qAbs(shapeCoordinateStep) < minimumCoordinateStep)
+ finishShapeParameter("minimum_coordinate_step");
};
auto registerEffectiveImprovement = [&](double fitness) -> bool {
@@ -2415,11 +2444,11 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
return true;
};
- // 普通形状按连续尝试判断停滞,并用新灵敏度下的全局联合步确认;
+ // 普通形状的失败试算缩步并累计当前参数的拒绝次数;
// 前期井储、表皮与整体 LM 沿用各自的无效次数处理。
auto recordIneffectiveStep = [&]() -> bool {
if(shapeStage && !earlyShapeStage) {
- completeShapeSearchStep();
+ completeShapeSearchStep(false);
return false;
}
++consecutiveIneffectiveSteps;
@@ -2587,9 +2616,11 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
jacobianColumnValid[column] = true;
columnBuilt = true;
+ // 后半段形状探针仅用于灵敏度,正式调整必须经过单参数 LM 预测选优。
// 前期同时测井储和表皮,但缓存探针不能绕过当前子阶段的选参限制。
// 前期首次正式调整必须从小步开始,测形状灵敏度的大探针不能提前成为工作点。
- if((!earlyShapeStage || earlyShapeHasImproved) &&
+ if((!shapeStage || earlyShapeStage) &&
+ (!earlyShapeStage || earlyShapeHasImproved) &&
parameterAllowedInStage(column) && acceptable(probe, base) &&
(!bestProbe.valid || stageError(probe) < stageError(bestProbe))) {
bestProbe = probe;
@@ -2609,14 +2640,14 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
if(parameterAllowedInStage(i)) hasActiveSensitivity = true;
}
}
- // 冻结列成功不能掩盖所有可调形状列的求解失败;有效零梯度仍由停滞逻辑处理。
+ // 冻结列成功不能掩盖所有可调形状列的求解失败;有效零梯度交给逐参数选步处理。
if(validColumnCount == 0 || (shapeStage && !earlyShapeStage && !hasActiveSensitivity) || m_shouldStop) {
restoreEvaluationState(base);
return false;
}
- // 灵敏度试算本身若找到更优真实解也应保留。所有列先基于同一个 base
- // 建完,再用该已知割线把 Jacobian 平移到新工作点,避免边算边移动基点。
+ // 前期井储/表皮和整体阶段仍可接受更优缓存探针;后半段形状只由 LM 选步。
+ // 所有列先基于同一个 base 建完,再用已知割线平移 Jacobian,避免边算边移动基点。
if(bestProbe.valid && bestProbeColumn >= 0) {
QVector acceptedStep(dimensions, 0.0);
acceptedStep[bestProbeColumn] = bestProbeDelta;
@@ -2676,14 +2707,13 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
if(wellboreRecheck && current.breakdown.earlyParallelLoss <=
earlyWellboreBaseline + qMax(1.0e-4, 0.10 * earlyWellboreBaseline))
enterTotalStage(tr("early shape error restored within tolerance"));
- if(shapeStage && !earlyShapeStage && !jointShapeStarted) {
- jointShapeStarted = true;
- shapeImprovementBaseline = current.breakdown.shapeLoss;
+ if(shapeStage && !earlyShapeStage && !singleShapeStarted) {
+ singleShapeStarted = true;
// 初调已改变井储/表皮,普通形状必须在新的工作点重测其余可调参数的响应。
jacobian.clear();
reuseSensitivityForNextStage();
rebuildRequested = true;
- rebuildReason = QT_TR_NOOP("fresh sensitivity at joint shape entry");
+ rebuildReason = QT_TR_NOOP("fresh sensitivity at single-parameter shape entry");
}
if(m_shouldStop) break;
if(!shapeStage && (totalIterations >= m_maxIterations || m_totalEvaluations >= maximumEvaluations)) break;
@@ -2741,13 +2771,13 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
--iteration;
continue;
}
- // 单列探针没有改善不代表联合步无效;重建后继续真实候选评价,再确认停滞。
+ // 差分探针不等于 LM 候选;重建后继续按当前阶段求步并真实评价,再确认停滞。
registerEffectiveImprovement(stageError(current));
}
// 第三阶段独立计数;方向不可行也消耗一次局部尝试,不能无限缩步循环。
if(!shapeStage) ++totalIterations;
- // 每次从最新 J 和当前阶段残差重算 Fisher,联合步使用全局信息。
+ // 每次从最新 J 和当前阶段残差重算全局信息,供单参数或联合 LM 求步。
const QVector objectiveResidual = earlyShapeStage
? current.breakdown.earlyParallelResiduals
: (shapeStage ? current.breakdown.shapeResiduals : current.breakdown.residualVector);
@@ -2775,8 +2805,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
double predictedReduction = 0.0;
const int selectedWindow = earlyShapeStage ? 0 : -1;
- // 前期只开放当前井储或表皮;普通形状和整体 LM 都联合求解全部有效自由列。
- // 保留弱敏感、相关及边界列,让参数相互配合;窗口只保留作误差诊断。
+ // 前期只开放当前井储或表皮;后半段形状从有效自由列中选一个,整体 LM 仍联合求解。
+ // 保留弱敏感、相关及边界列供预测比较;窗口只保留作误差诊断。
for(int column = 0; column < dimensions; ++column) {
if(stageColumnValid[column] && (earlyShapeStage || global.matrix[column][column] > 0.0))
selectedColumns.append(column);
@@ -2800,8 +2830,54 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
damping, earlyStepRadius, minimumCoordinateStep, &coordinateStep, &predictedReduction)) {
selectedColumns.clear();
}
+ } else if(shapeStage) {
+ // 首步由灵敏度选出尚未调过的参数;后续锁定方向,直接使用扩缩后的步长。
+ if(activeShapeColumn < 0) {
+ QVector availableColumns;
+ for(int i = 0; i < selectedColumns.size(); ++i) {
+ if(!shapeParameterFinished[selectedColumns[i]]) availableColumns.append(selectedColumns[i]);
+ }
+ buildBestSingleParameterStep(global, availableColumns, current.coordinates,
+ 0.01, initialShapeTrustRadius, minimumCoordinateStep,
+ &selectedColumns, &coordinateStep, &predictedReduction);
+ if(!selectedColumns.isEmpty()) {
+ activeShapeColumn = selectedColumns[0];
+ shapeCoordinateStep = coordinateStep[activeShapeColumn];
+ consecutiveShapeRejections = 0;
+ writeTraceRow(m_currentIteration, activeShapeColumn, "shape_parameter_start",
+ current.parameters, current.fitness, true, 0,
+ "largest_predicted_reduction_unvisited", ¤t.breakdown);
+ } else {
+ // 剩余参数均无可行预测下降步时逐项记录,不能重复选择已完成参数。
+ for(int column = 0; column < dimensions; ++column) {
+ if(!parameterAllowedInStage(column) || shapeParameterFinished[column]) continue;
+ activeShapeColumn = column;
+ finishShapeParameter(jacobianColumnValid[column]
+ ? "no_feasible_descent_step" : "invalid_sensitivity");
+ if(!shapeStage || earlyShapeStage) break;
+ }
+ --iteration;
+ continue;
+ }
+ } else {
+ selectedColumns.clear();
+ selectedColumns.append(activeShapeColumn);
+ coordinateStep.fill(0.0, dimensions);
+ shapeCoordinateStep = qBound(0.0,
+ current.coordinates[activeShapeColumn] + shapeCoordinateStep, 1.0)
+ - current.coordinates[activeShapeColumn];
+ if(qAbs(shapeCoordinateStep) < minimumCoordinateStep) {
+ finishShapeParameter("parameter_bound_or_minimum_step");
+ --iteration;
+ continue;
+ }
+ coordinateStep[activeShapeColumn] = shapeCoordinateStep;
+ predictedReduction = -global.gradient[activeShapeColumn] * shapeCoordinateStep
+ - 0.5 * global.matrix[activeShapeColumn][activeShapeColumn]
+ * shapeCoordinateStep * shapeCoordinateStep;
+ }
} else {
- // 普通调整和停滞确认共用全局联合方向,不再遍历窗口或枚举三参数子集。
+ // 第三阶段保持全部有效自由参数的联合 LM 调整。
globalFallbackAttempted = true;
if(selectedColumns.isEmpty() || !buildTrustRegionFisherStep(
global, selectedColumns, current.coordinates, damping,
@@ -2818,11 +2894,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
--iteration;
continue;
}
- if(shapeStage) {
- completeShapeSearchStep();
- --iteration;
- continue;
- }
if(trustRadius <= minimumTrustRadius * 1.01 &&
modelRebuiltAtMinimumRadius) {
if(promoteSampling(false)) continue;
@@ -2933,7 +3004,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
"solver_invalid_" + selectionName,
nullptr);
restoreEvaluationState(current);
- if(consecutiveSolverFailures >= 2) {
+ if(consecutiveSolverFailures >= 2 && (!shapeStage || earlyShapeStage)) {
rebuildRequested = true;
rebuildReason = QT_TR_NOOP("2 consecutive solver failures");
}
@@ -2941,8 +3012,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
stopReason = LM_CONSECUTIVE_FAILURES;
break;
}
- // 确认步求解失败不代表形状已收敛,沿用有限重试后按求解失败退出。
- if(shapeStage && !earlyShapeStage && shapeConfirmationRequested) continue;
if(recordIneffectiveStep()) {
if(promoteSampling(false)) continue;
stopReason = LM_LOCAL_OPTIMUM;
@@ -2951,37 +3020,6 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
continue;
}
- // 普通形状步真实改善后,沿同一联合方向最多扩步一次,不再用预测兑现比例拦截。
- // 扩步只按整体形状验收,优于原步才替换,否则保留原候选。
- if(shapeStage && !earlyShapeStage && acceptable(candidate, current) && processPauseAndStop()) {
- QVector expandedStep;
- double expandedPrediction = 0.0;
- if(buildExpandedTrustRegionStep(stepInformation, current.coordinates, coordinateStep,
- maximumTrustRadius, &expandedStep, &expandedPrediction)) {
- writeTraceRow(m_currentIteration, -1, "shape_step_base", candidate.parameters,
- candidate.fitness, true, candidate.elapsedMs, "eligible_for_expansion", &candidate.breakdown);
- TrustRegionEvaluation expanded;
- expanded.coordinates = current.coordinates;
- for(int i = 0; i < dimensions; ++i) expanded.coordinates[i] += expandedStep[i];
- expanded.parameters = parametersFromCoordinates(expanded.coordinates);
- expanded.valid = evaluateTrustRegionPoint(expanded.parameters, &expanded.fitness,
- &expanded.breakdown, &expanded.curve, &expanded.elapsedMs);
- const bool useExpanded = acceptable(expanded, current) && expanded.breakdown.shapeLoss < candidate.breakdown.shapeLoss;
- writeTraceRow(m_currentIteration, -1, "shape_step_expanded", expanded.parameters,
- expanded.fitness, expanded.valid, expanded.elapsedMs, useExpanded ? "selected_expanded" : "retain_ordinary",
- expanded.valid ? &expanded.breakdown : nullptr);
- if(useExpanded) {
- candidate = expanded;
- coordinateStep = expandedStep;
- predictedReduction = expandedPrediction;
- stepNorm = qSqrt(trustRegionSquaredNorm(coordinateStep));
- selectionName += "_expanded";
- }
- // 两次真实调用已分别记录,下方候选行只记录最终决策,避免重复统计耗时。
- candidate.elapsedMs = -1;
- restoreEvaluationState(current);
- }
- }
if(m_shouldStop) {
restoreEvaluationState(current);
stopReason = LM_USER_STOPPED;
@@ -3011,7 +3049,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
const bool poorPrediction = predictedReduction > predictionFloor &&
(!isFiniteNumber(reductionRatio) || reductionRatio < 0.25);
consecutivePoorPredictions = poorPrediction ? consecutivePoorPredictions + 1 : 0;
- if(consecutivePoorPredictions >= 2) {
+ if(consecutivePoorPredictions >= 2 && (!shapeStage || earlyShapeStage)) {
rebuildRequested = true;
rebuildReason = QT_TR_NOOP("2 consecutive steps with actual improvement below 25% of prediction");
}
@@ -3032,21 +3070,24 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
++acceptedSinceRebuild;
consecutiveRejectedSteps = 0;
- if(reductionRatio > 0.75) {
- damping = qMax(1.0e-8, damping * 0.5);
- if(stepNorm >= trustRadius * 0.8) {
- trustRadius = qMin(
- maximumTrustRadius, trustRadius * 1.6);
+ // 井筒调整及第三阶段保留原 LM 控制;逐参数粗调在步末直接扩缩实际步长。
+ if(!shapeStage || earlyShapeStage) {
+ if(reductionRatio > 0.75) {
+ damping = qMax(1.0e-8, damping * 0.5);
+ if(stepNorm >= trustRadius * 0.8) {
+ trustRadius = qMin(
+ maximumTrustRadius, trustRadius * 1.6);
+ }
+ } else if(reductionRatio > 0.25) {
+ damping = qMax(1.0e-8, damping * 0.8);
+ } else {
+ damping = qMin(1.0e8, damping * 2.0);
+ trustRadius = qMax(
+ minimumTrustRadius, trustRadius * 0.75);
}
- } else if(reductionRatio > 0.25) {
- damping = qMax(1.0e-8, damping * 0.8);
- } else {
- damping = qMin(1.0e8, damping * 2.0);
- trustRadius = qMax(
- minimumTrustRadius, trustRadius * 0.75);
}
- if(acceptedSinceRebuild >= 10 && !rebuildRequested) {
+ if(acceptedSinceRebuild >= 10 && !rebuildRequested && (!shapeStage || earlyShapeStage)) {
rebuildRequested = true;
rebuildReason = QT_TR_NOOP("10 accepted steps since last rebuild");
}
@@ -3102,6 +3143,10 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
finishEarlyShapeStage(tr("initial same-direction expansion reached the parameter bound without early shape improvement"));
continue;
}
+ if(shapeStage && !earlyShapeStage) {
+ completeShapeSearchStep(accepted);
+ continue;
+ }
const bool effectiveImprovement = registerEffectiveImprovement(stageError(current));
if(!effectiveImprovement && recordIneffectiveStep()) stopReason = LM_LOCAL_OPTIMUM;