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;