diff --git a/Bin/Config/Lang/cn/nmNum_cn.qm b/Bin/Config/Lang/cn/nmNum_cn.qm index c9e8ed0..9df8a26 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 1bb1e95..75fd2ee 100644 --- a/Bin/Config/Lang/cn/nmNum_cn.ts +++ b/Bin/Config/Lang/cn/nmNum_cn.ts @@ -49,6 +49,18 @@ Reason: %1 nmCalculationAutoFitPSO + + Effective improvement threshold: max(%1, %2% of baseline error); %3 consecutive ineffective steps trigger convergence confirmation + 有效改善阈值:取 %1 与基准误差的 %2% 中较大值;连续 %3 次无有效改善后进行收敛确认 + + + No effective improvement for %1 consecutive steps; rebuilding sensitivity model for confirmation + 连续 %1 次无有效改善,正在重建灵敏度模型进行确认 + + + Sensitivity rebuild produced no effective improvement; local convergence detected + 灵敏度重建后仍无有效改善,判定为局部收敛 + === User Stop Request Received === === 用户停止请求已接收 === diff --git a/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp b/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp index 79453f9..94497c7 100644 --- a/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp +++ b/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp @@ -4235,6 +4235,11 @@ StopReasonPSO nmCalculationAutoFitPSO::runTrustRegionFitting() const double maximumTrustRadius = 0.30; const double columnCorrelationLimit = 0.995; const double diagnosisThreshold = 1.0e-5; + // 误差下降至少达到绝对 1e-5 且相对当前有效基准 0.2% 才算有效改善。 + // 更小的下降仍保留为最佳解,但不能反复清除停滞状态、延长拟合时间。 + const double effectiveRelativeImprovement = 2.0e-3; + const double effectiveAbsoluteImprovement = 1.0e-5; + const int maximumIneffectiveSteps = 3; // damping 是 LM 阻尼;拒绝或预测失准时增大,真实下降与预测一致时减小。 // 两组累计量控制 Jacobian 重建,避免长期使用已偏离当前工作点的局部模型。 @@ -4243,9 +4248,11 @@ StopReasonPSO nmCalculationAutoFitPSO::runTrustRegionFitting() int consecutiveRejectedSteps = 0; int consecutiveSolverFailures = 0; int acceptedSinceRebuild = 0; + int consecutiveIneffectiveSteps = 0; double movementSinceRebuild = 0.0; bool rebuildRequested = true; bool modelRebuiltAtMinimumRadius = false; + bool stagnationConfirmationRequested = false; StopReasonPSO stopReason = PSO_MAX_ITERATIONS; // jacobian 的行对应固定 160 维残差,列对应用户勾选的参数。 @@ -4367,6 +4374,54 @@ StopReasonPSO nmCalculationAutoFitPSO::runTrustRegionFitting() .arg(current.fitness, 0, 'e', 4) .arg(maximumEvaluations)); + // 有效改善始终相对“上一次有效改善后的误差”累计判断,避免一连串微小 + // 下降每次都清零计数;累计达到门槛后才开始新的有效改善基准。 + double effectiveImprovementBaseline = current.fitness; + auto registerEffectiveImprovement = [&](double fitness) -> bool { + const double requiredImprovement = qMax( + effectiveAbsoluteImprovement, + qAbs(effectiveImprovementBaseline) * + effectiveRelativeImprovement); + const double improvement = effectiveImprovementBaseline - fitness; + if(improvement < requiredImprovement) { + return false; + } + + effectiveImprovementBaseline = fitness; + consecutiveIneffectiveSteps = 0; + stagnationConfirmationRequested = false; + return true; + }; + + // 连续三次没有有效改善时只请求一次灵敏度重建。重建完成后由主循环 + // 直接检查累计改善,仍达不到门槛就判定局部收敛,不再继续微小试探。 + auto recordIneffectiveStep = [&]() -> bool { + ++consecutiveIneffectiveSteps; + if(consecutiveIneffectiveSteps < maximumIneffectiveSteps) { + return false; + } + + if(stagnationConfirmationRequested) { + return true; + } + + consecutiveIneffectiveSteps = 0; + stagnationConfirmationRequested = true; + rebuildRequested = true; + emit logMessageGenerated( + tr("No effective improvement for %1 consecutive steps; " + "rebuilding sensitivity model for confirmation") + .arg(maximumIneffectiveSteps)); + return false; + }; + + emit logMessageGenerated( + tr("Effective improvement threshold: max(%1, %2% of baseline error); " + "%3 consecutive ineffective steps trigger convergence confirmation") + .arg(effectiveAbsoluteImprovement, 0, 'e', 2) + .arg(effectiveRelativeImprovement * 100.0, 0, 'f', 2) + .arg(maximumIneffectiveSteps)); + if(current.fitness < m_targetError) { return PSO_TARGET_ACHIEVED; } @@ -4603,6 +4658,8 @@ StopReasonPSO nmCalculationAutoFitPSO::runTrustRegionFitting() break; } if(rebuildRequested) { + const bool confirmingStagnation = + stagnationConfirmationRequested; if(!rebuildSensitivity()) { stopReason = m_shouldStop ? PSO_USER_STOPPED @@ -4617,6 +4674,15 @@ StopReasonPSO nmCalculationAutoFitPSO::runTrustRegionFitting() stopReason = PSO_MAX_ITERATIONS; break; } + const bool rebuildEffective = + registerEffectiveImprovement(current.fitness); + if(confirmingStagnation && !rebuildEffective) { + emit logMessageGenerated( + tr("Sensitivity rebuild produced no effective improvement; " + "local convergence detected")); + stopReason = PSO_LOCAL_OPTIMUM; + break; + } } // 先确定当前最突出的可靠诊断误差,用其梯度回答“哪些参数最能改善 @@ -4748,6 +4814,10 @@ StopReasonPSO nmCalculationAutoFitPSO::runTrustRegionFitting() trustRadius = qMax(minimumTrustRadius, trustRadius * 0.5); damping = qMin(1.0e8, damping * 4.0); rebuildRequested = true; + if(recordIneffectiveStep()) { + stopReason = PSO_LOCAL_OPTIMUM; + break; + } continue; } @@ -4865,6 +4935,10 @@ StopReasonPSO nmCalculationAutoFitPSO::runTrustRegionFitting() } rebuildRequested = true; } + if(recordIneffectiveStep()) { + stopReason = PSO_LOCAL_OPTIMUM; + break; + } continue; } @@ -4905,6 +4979,10 @@ StopReasonPSO nmCalculationAutoFitPSO::runTrustRegionFitting() stopReason = PSO_CONSECUTIVE_FAILURES; break; } + if(recordIneffectiveStep()) { + stopReason = PSO_LOCAL_OPTIMUM; + break; + } continue; } @@ -4994,6 +5072,14 @@ StopReasonPSO nmCalculationAutoFitPSO::runTrustRegionFitting() } } + // 候选只要更优就继续作为 current 保存;是否足以解除停滞,则统一 + // 相对上一次有效改善基准判断。拒绝和微小改善都会累计无效次数。 + const bool effectiveImprovement = + registerEffectiveImprovement(current.fitness); + if(!effectiveImprovement && recordIneffectiveStep()) { + stopReason = PSO_LOCAL_OPTIMUM; + } + writeTraceRow(m_currentIteration, -1, "trust_region_candidate", @@ -5018,6 +5104,10 @@ StopReasonPSO nmCalculationAutoFitPSO::runTrustRegionFitting() .arg(accepted ? tr("accepted") : tr("rejected"))); emit progressUpdated(iteration + 1, m_globalBestFitness); + if(stopReason == PSO_LOCAL_OPTIMUM) { + break; + } + if(current.fitness < m_targetError) { stopReason = PSO_TARGET_ACHIEVED; break;