From 924fbd724fcca29a7c60690f001f65729d1d0e43 Mon Sep 17 00:00:00 2001 From: lvjunjie Date: Wed, 14 Oct 2026 17:25:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(nmNum):=20=E6=8E=A5=E9=80=9A=20Monte-Carlo?= =?UTF-8?q?=20=E6=89=B9=E9=87=8F=E6=B1=82=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 生成复用预览样本,无有效样本时自动采样。 - 设置或模型变化后清除旧样本。 - 复用已有批量求解、停止处理与结果展示。 --- .../nmCalculationDllPebiSolverTask.h | 2 +- Include/nmNum/nmData/nmDataSensitive.h | 4 +++- .../nmCalculationDllPebiSolverTask.cpp | 5 +++-- Src/nmNum/nmData/nmDataSensitive.cpp | 13 +++++++++++-- Src/nmNum/nmSubWxs/nmWxSensitive.cpp | 18 ++++++++++++++---- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/Include/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.h b/Include/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.h index c5eb69bc..c583686b 100644 --- a/Include/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.h +++ b/Include/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.h @@ -111,7 +111,7 @@ class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread { typedef nmDataSensitive::RunResult SensitivityRunResult; /** - * @brief 从源快照创建一份副本,按两种确定性方法的预览顺序串行求解全部组合。 + * @brief 从源快照创建一份副本,按确定性组合或 Monte-Carlo 预览样本顺序串行求解。 * @param results 本次批量返回值;进入时清空,每组结果在更新下一组前独立保留。 * @return 全部组合遍历完成返回 true,单组成功与否读取对应 m_bSucceeded。 * 配置、基础网格或内存不足时返回 false;已完成的结果仍保留。 diff --git a/Include/nmNum/nmData/nmDataSensitive.h b/Include/nmNum/nmData/nmDataSensitive.h index 4acf003d..327133af 100644 --- a/Include/nmNum/nmData/nmDataSensitive.h +++ b/Include/nmNum/nmData/nmDataSensitive.h @@ -156,8 +156,10 @@ public: quint64 calculateModelCount(bool* overflow = nullptr) const; // 返回与变量列表对应的基准单位值,未改变的参数保持模型值。 bool getRunValues(quint64 runIndex, QVector& values) const; - // 每次打开 Monte-Carlo 预览重新分层抽样,预览滚动只读取本批结果。 + // 每次预览重新分层抽样,生成和预览滚动复用本批结果。 bool resampleMonteCarlo(QString* error = nullptr); + // 采样设置改变后丢弃旧样本,下次预览或生成时重建。 + void clearMonteCarloSamples(); int getMonteCarloCount() const; void setMonteCarloCount(int count); bool usesBaseUnits() const; diff --git a/Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp b/Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp index c83a4503..ac9be4d4 100644 --- a/Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp +++ b/Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp @@ -921,7 +921,8 @@ nmCalculationDllPebiSolverTask* nmCalculationDllPebiSolverTask::createSensitivit const nmDataSensitive::CalculationType type = frozenSampling.getCalculationType(); if(overflow || count == 0 || !frozenSampling.usesBaseUnits() || (type != nmDataSensitive::CALC_DETERMINISTIC && - type != nmDataSensitive::CALC_DETERMINISTIC_MULTIVARIATE)) { + type != nmDataSensitive::CALC_DETERMINISTIC_MULTIVARIATE && + type != nmDataSensitive::CALC_MONTE_CARLO)) { if(errorMessage) *errorMessage = tr("Invalid sensitivity sampling configuration."); return nullptr; } @@ -995,7 +996,7 @@ bool nmCalculationDllPebiSolverTask::executeSensitivityBatch() if(isCancelRequested()) return false; SensitivityRunResult result; result.m_nRunIndex = runIndex; - // 单参数法保持其余参数的模型值,多参数法按预览组合顺序逐组取值。 + // 确定性组合和 Monte-Carlo 样本均按预览顺序读取,后台不重新采样。 bool inputReady = m_pSensitivitySampling->getRunValues(runIndex, result.m_vecParameters); if(!inputReady) { result.m_sFailureReason = tr("Invalid sensitivity sampling configuration."); diff --git a/Src/nmNum/nmData/nmDataSensitive.cpp b/Src/nmNum/nmData/nmDataSensitive.cpp index f7af77a8..3caebb55 100644 --- a/Src/nmNum/nmData/nmDataSensitive.cpp +++ b/Src/nmNum/nmData/nmDataSensitive.cpp @@ -573,6 +573,8 @@ nmDataSensitive::CalculationType nmDataSensitive::getCalculationType() const void nmDataSensitive::setCalculationType(CalculationType type) { + // 切换计算方法后不再沿用上一种方法的预览样本。 + if(type != m_calcType) clearMonteCarloSamples(); m_calcType = type; } @@ -666,11 +668,17 @@ int nmDataSensitive::getMonteCarloCount() const void nmDataSensitive::setMonteCarloCount(int count) { - // 次数变化后旧预览失效,下一次预览统一重新抽样。 - if(count != m_monteCarloCount) m_monteCarloValues.clear(); + // 次数变化后旧样本失效,下次预览或生成时重新抽样。 + if(count != m_monteCarloCount) clearMonteCarloSamples(); m_monteCarloCount = count; } +void nmDataSensitive::clearMonteCarloSamples() +{ + // 只丢弃待计算样本,已完成批次的结果仍由结果窗口管理。 + m_monteCarloValues.clear(); +} + bool nmDataSensitive::resampleMonteCarlo(QString* error) { // 各参数的累积概率均分为 N 层,每层抽一点,再独立打乱,组成 N 组参数。 @@ -717,6 +725,7 @@ QString nmDataSensitive::getModelSnapshotId() const void nmDataSensitive::setModelSnapshotId(const QString& snapshotId) { // 仅在同步模型与范围后登记,编辑采样值不改变此标识。 + if(snapshotId != m_modelSnapshotId) clearMonteCarloSamples(); m_modelSnapshotId = snapshotId; } diff --git a/Src/nmNum/nmSubWxs/nmWxSensitive.cpp b/Src/nmNum/nmSubWxs/nmWxSensitive.cpp index 8ffbf8bc..ff2e7426 100644 --- a/Src/nmNum/nmSubWxs/nmWxSensitive.cpp +++ b/Src/nmNum/nmSubWxs/nmWxSensitive.cpp @@ -1062,8 +1062,7 @@ void nmWxSensitive::updateModelCount() const bool monteCarlo = m_sensitiveData.getCalculationType() == nmDataSensitive::CALC_MONTE_CARLO; modelCountLabel->setVisible(!monteCarlo); m_monteCarloCount->setVisible(monteCarlo); - generateButton->setEnabled(!monteCarlo && m_pDataManager && !overflow && count > 0 && !m_calculationRunning); - generateButton->setToolTip(monteCarlo ? tr("Monte-Carlo sampling preview is available; calculation is not yet available.") : QString()); + generateButton->setEnabled(m_pDataManager && !overflow && count > 0 && !m_calculationRunning); } bool nmWxSensitive::saveDataFromUI() @@ -1828,6 +1827,8 @@ void nmWxSensitive::onVariableParamChanged() { // 数据加载时不响应控件逐项赋值,编辑时只刷新所属参数页。 if (m_loading) return; + // 分布设置或次数改变后,生成必须重新抽样,不能使用旧预览。 + m_sensitiveData.clearMonteCarloSamples(); for (int i = 0; i < m_numSpins.size(); ++i) { if (sender() == m_numSpins[i] || sender() == m_minSpins[i] || sender() == m_maxSpins[i] || sender() == m_modeCombos[i] || @@ -1857,6 +1858,8 @@ void nmWxSensitive::onTreeItemChanged(QTreeWidgetItem* item, int column) { Q_UNUSED(column); if (m_loading) return; + // 勾选变量改变会改变整批组合;初始化树状态时保留已有样本。 + if(item) m_sensitiveData.clearMonteCarloSamples(); // 分组选择与子项选择双向同步,禁用的参数不参与全选和总次数。 m_loading = true; variablesTree->blockSignals(true); @@ -1884,12 +1887,18 @@ void nmWxSensitive::onTreeItemChanged(QTreeWidgetItem* item, int column) void nmWxSensitive::onGenerateClicked() { - // 本阶段只开放 Monte-Carlo 参数采样与预览,尚不启动数值计算。 - if(calculationTypeCombo->currentIndex() == nmDataSensitive::CALC_MONTE_CARLO) return; if(m_calculationRunning || !m_pDataManager || !saveDataFromUI()) return; QString error; QScopedPointer task; try { + // 优先使用预览的整批样本;未预览或设置改变后才重新采样。 + if(m_sensitiveData.getCalculationType() == nmDataSensitive::CALC_MONTE_CARLO) { + QVector values; + if(!m_sensitiveData.getRunValues(0, values) && !m_sensitiveData.resampleMonteCarlo(&error)) { + QMessageBox::warning(this, tr("Sensitivity"), error); + return; + } + } // 从最后成功结果恢复完整输入,当前编辑参数和全局拟压力配置不参与本次分析。 nmCalculationDllPebiSolverTask source(QString(), m_pDataManager, QString(), nullptr, false, true); task.reset(source.createSensitivityBatch(m_sensitiveData, &error)); @@ -1942,6 +1951,7 @@ void nmWxSensitive::setCalculationRunning(bool running) calculationTypeCombo->setEnabled(!running); variablesTree->setEnabled(!running); detailStack->setEnabled(!running); + m_monteCarloCount->setEnabled(!running); previewButton->setEnabled(!running); cancelButton->setEnabled(!running); if(running) {