Skip to content
Snippets Groups Projects
Commit 4cce2f93 authored by Mikhail Svechnikov's avatar Mikhail Svechnikov
Browse files

[minimizers] Fit: catalogize algorithms, not minimizers (Closes #42)

Merging branch 'minimizers'  into 'main'.

See merge request !1004
parents 088c8d97 d34b3ade
No related branches found
No related tags found
1 merge request!1004Fit: catalogize algorithms, not minimizers
Pipeline #74501 failed
......@@ -64,7 +64,7 @@ MinimizerInfo MinimizerInfo::buildMinuit2Info(const std::string& defaultAlgo)
"Migrad",
"Variable-metric method with inexact line search, best minimizer according to ROOT.");
result.addAlgorithm("Simplex", "Simplex method of Nelder and Meadh, robust "
result.addAlgorithm("Simplex", "Simplex method of Nelder and Mead, robust "
"against big fluctuations in objective function.");
result.addAlgorithm("Combined", "Combination of Migrad and Simplex (if Migrad fails).");
......@@ -72,7 +72,7 @@ MinimizerInfo MinimizerInfo::buildMinuit2Info(const std::string& defaultAlgo)
result.addAlgorithm("Scan", "Simple objective function scan, one parameter at a time.");
result.addAlgorithm("Fumili", "Gradient descent minimizer similar to "
"Levenberg-Margquardt, sometimes can be better "
"Levenberg-Marquardt, sometimes can be better "
"than all others.");
if (defaultAlgo.empty())
......@@ -108,7 +108,7 @@ MinimizerInfo MinimizerInfo::buildGSLMultiMinInfo(const std::string& defaultAlgo
MinimizerInfo MinimizerInfo::buildGSLLMAInfo()
{
MinimizerInfo result("GSLLMA", "Levenberg-Marquardt from GSL library");
result.addAlgorithm("Default", "Default algorithm");
result.addAlgorithm("Levenberg-Marquardt", "Levenberg-Marquardt");
return result;
}
......@@ -116,8 +116,8 @@ MinimizerInfo MinimizerInfo::buildGSLLMAInfo()
MinimizerInfo MinimizerInfo::buildGSLSimAnInfo()
{
MinimizerInfo result("GSLSimAn", "Simmulated annealing minimizer from GSL library");
result.addAlgorithm("Default", "Default algorithm");
MinimizerInfo result("GSLSimAn", "Simulated annealing minimizer from GSL library");
result.addAlgorithm("Simulated annealing", "Simulated annealing");
return result;
}
......@@ -126,7 +126,7 @@ MinimizerInfo MinimizerInfo::buildGSLSimAnInfo()
MinimizerInfo MinimizerInfo::buildGeneticInfo()
{
MinimizerInfo result("Genetic", "Genetic minimizer from TMVA library");
result.addAlgorithm("Default", "Default algorithm");
result.addAlgorithm("Genetic", "Genetic algorithm");
return result;
}
......
......@@ -110,25 +110,25 @@ TEST_F(Minimize, Bfgs2_WoodFour)
TEST_F(Minimize, GSLSimAn_EasyRosenbrock)
{
EasyRosenbrockPlan plan;
EXPECT_TRUE(runMinimizerTest("GSLSimAn", "Default", plan));
EXPECT_TRUE(runMinimizerTest("GSLSimAn", "Simulated annealing", plan));
}
TEST_F(Minimize, GSLSimAn_EasyWoodFour)
{
EasyWoodFourPlan plan;
EXPECT_TRUE(runMinimizerTest("GSLSimAn", "Default", plan));
EXPECT_TRUE(runMinimizerTest("GSLSimAn", "Simulated annealing", plan));
}
TEST_F(Minimize, Genetic_EasyRosenbrock)
{
EasyRosenbrockPlan plan;
EXPECT_TRUE(runMinimizerTest("Genetic", "Default", plan, "RandomSeed=1"));
EXPECT_TRUE(runMinimizerTest("Genetic", "Genetic", plan, "RandomSeed=1"));
}
TEST_F(Minimize, Genetic_EasyWoodFour)
{
EasyWoodFourPlan plan;
EXPECT_TRUE(runMinimizerTest("Genetic", "Default", plan, "RandomSeed=1"));
EXPECT_TRUE(runMinimizerTest("Genetic", "Genetic", plan, "RandomSeed=1"));
}
TEST_F(Minimize, Fumili_DecayingSin)
......@@ -140,5 +140,5 @@ TEST_F(Minimize, Fumili_DecayingSin)
TEST_F(Minimize, LevenbergMarquardtV3)
{
DecayingSinPlanV2 plan;
EXPECT_TRUE(runMinimizerTest("GSLLMA", "Default", plan));
EXPECT_TRUE(runMinimizerTest("GSLLMA", "Levenberg-Marquardt", plan));
}
......@@ -26,7 +26,6 @@ class MinimizerItemCatalog {
public:
static ComboProperty algorithmCombo(const QString& minimizerType);
private:
static QStringList algorithmNames(const QString& minimizerType);
static QStringList algorithmDescriptions(const QString& minimizerType);
};
......
......@@ -32,6 +32,66 @@ const QMap<QString, QString> minimizer_names_map = {
{"GSL Levenberg-Marquardt", GSLLMAMinimizerItem::M_TYPE}};
const QString default_minimizer = minimizer_names_map.key(MinuitMinimizerItem::M_TYPE);
QMap<QString /*algorithm*/, QString /*minimizer*/> algorithm_minimizer_map;
void add_algorithm_from_minimizer_to_list_and_map(const QString& minimizer,
QStringList& common_algorithms_list,
QStringList& common_algorithms_descriptions)
{
QStringList algorithms = MinimizerItemCatalog::algorithmNames(minimizer);
QStringList descriptions = MinimizerItemCatalog::algorithmDescriptions(minimizer);
ASSERT(algorithms.size() == descriptions.size())
for (int i = 0; i < algorithms.size(); i++) {
common_algorithms_list.append(algorithms[i]);
common_algorithms_descriptions.append(descriptions[i]);
algorithm_minimizer_map.insert(algorithms[i], minimizer_names_map.key(minimizer));
}
}
void create_algorithm_list_and_map(QString& default_common_algorithm, QStringList& algorithms_list,
QStringList& algorithms_descriptions)
{
// Group headers may not directly correspond to minimizers, so for them we don't use
// descriptions from MinimizerInfo
algorithms_list.clear();
algorithms_descriptions.clear();
// group 1
// group header. Can't be selected and have no mapped minimizer
algorithms_list.append("Local optimization");
algorithms_descriptions.append("");
add_algorithm_from_minimizer_to_list_and_map(MinuitMinimizerItem::M_TYPE, algorithms_list,
algorithms_descriptions);
add_algorithm_from_minimizer_to_list_and_map(GSLMultiMinimizerItem::M_TYPE, algorithms_list,
algorithms_descriptions);
add_algorithm_from_minimizer_to_list_and_map(GSLLMAMinimizerItem::M_TYPE, algorithms_list,
algorithms_descriptions);
// group 2
algorithms_list.append("Global optimization");
algorithms_descriptions.append("");
// move "Scan" algorithm from the local group to the global
qsizetype scan_index = algorithms_list.indexOf("Scan");
ASSERT(scan_index >= 0);
QString name = algorithms_list.takeAt(scan_index);
QString descr = algorithms_descriptions.takeAt(scan_index);
algorithms_list.append(name);
algorithms_descriptions.append(descr);
// other algorithms
add_algorithm_from_minimizer_to_list_and_map(GeneticMinimizerItem::M_TYPE, algorithms_list,
algorithms_descriptions);
add_algorithm_from_minimizer_to_list_and_map(SimAnMinimizerItem::M_TYPE, algorithms_list,
algorithms_descriptions);
default_common_algorithm = algorithms_list[1]; // after the first header: Minuit2 Migrad
}
} // namespace
......@@ -51,6 +111,16 @@ MinimizerContainerItem::MinimizerContainerItem()
addProperty<SimAnMinimizerItem>(SimAnMinimizerItem::M_TYPE);
addProperty<GSLLMAMinimizerItem>(GSLLMAMinimizerItem::M_TYPE);
QString default_common_algorithm;
QStringList common_algorithms_list;
QStringList common_algorithms_descriptions;
create_algorithm_list_and_map(default_common_algorithm, common_algorithms_list,
common_algorithms_descriptions);
ComboProperty common_algorithm_combo =
ComboProperty::fromList(common_algorithms_list, default_common_algorithm);
common_algorithm_combo.setToolTips(common_algorithms_descriptions);
addProperty(P_COMMON_ALGORITHMS, common_algorithm_combo.variant());
ComboProperty minimizer_combo =
ComboProperty::fromList(minimizer_names_map.keys(), default_minimizer);
addProperty(P_MINIMIZERS, minimizer_combo.variant());
......@@ -117,6 +187,47 @@ ComboProperty MinimizerContainerItem::minimizerCombo() const
return getItemValue(P_MINIMIZERS).value<ComboProperty>();
}
bool MinimizerContainerItem::algorithmHasMinimizer(const QString& name)
{
return algorithm_minimizer_map.contains(name);
}
void MinimizerContainerItem::setCurrentCommonAlgorithm(const QString& name)
{
ComboProperty combo = commonAlgorithmCombo();
combo.setCurrentValue(name);
setItemValue(P_COMMON_ALGORITHMS, combo.variant());
ASSERT(algorithmHasMinimizer(name));
setCurrentMinimizer(algorithm_minimizer_map.value(name));
applyAlgorithmToMinimizer(name);
}
ComboProperty MinimizerContainerItem::commonAlgorithmCombo() const
{
return getItemValue(P_COMMON_ALGORITHMS).value<ComboProperty>();
}
void MinimizerContainerItem::applyAlgorithmToMinimizer(const QString& name)
{
// Minuit2
if (currentMinimizerM_TYPE() == MinuitMinimizerItem::M_TYPE)
minimizerItemMinuit()->setCurrentAlgorithm(name);
// GSL MultiMin
if (currentMinimizerM_TYPE() == GSLMultiMinimizerItem::M_TYPE)
minimizerItemGSLMulti()->setCurrentAlgorithm(name);
// TMVA Genetic
// do nothing
// GSL Simulated Annealing
// do nothing
// GSL Levenberg-Marquardt
// do nothing
}
QString MinimizerContainerItem::currentObjectiveMetric() const
{
return objectiveMetricCombo().currentValue();
......
......@@ -42,6 +42,7 @@ class GSLLMAMinimizerItem;
class MinimizerContainerItem : public MinimizerItem {
private:
static constexpr auto P_MINIMIZERS{"Minimizer"};
static constexpr auto P_COMMON_ALGORITHMS{"Algorithms"};
static constexpr auto P_METRIC{"Objective metric"};
static constexpr auto P_NORM{"Norm function"};
......@@ -63,6 +64,11 @@ public:
void setCurrentMinimizer(const QString& name);
ComboProperty minimizerCombo() const;
bool algorithmHasMinimizer(const QString& name);
void setCurrentCommonAlgorithm(const QString& name);
ComboProperty commonAlgorithmCombo() const;
void applyAlgorithmToMinimizer(const QString& name);
// Objective metric to use for estimating distance between simulated and experimental data
QString currentObjectiveMetric() const;
void setCurrentObjectiveMetric(const QString& name);
......
......@@ -245,10 +245,12 @@ QString GUI::Session::XML::readProperty(QXmlStreamReader* reader, SessionItem* i
} else if (parameter_type == "ComboProperty") {
QString selections = reader->attributes().value(Tags::ParameterValueAttribute).toString();
QString values = reader->attributes().value(Tags::ParameterExtAttribute).toString();
QString tooltips = reader->attributes().value(Tags::ParameterToolAttribute).toString();
ComboProperty combo_property;
combo_property.setStringOfValues(values);
combo_property.setStringOfSelections(selections);
combo_property.setStringOfTooltips(tooltips);
variant = combo_property.variant();
} else {
......
......@@ -86,6 +86,7 @@ void GUI::Session::XML::writeAttribute(QXmlStreamWriter* writer, const QString&
auto combo = variant.value<ComboProperty>();
writer->writeAttribute(attributeName, combo.stringOfSelections());
writer->writeAttribute(Tags::ParameterExtAttribute, combo.stringOfValues());
writer->writeAttribute(Tags::ParameterToolAttribute, combo.stringOfTooltips());
} else
throw Error("GUI::Session::XML::writeVariant: Parameter type not supported "
+ QString(variant.typeName()));
......
......@@ -40,6 +40,7 @@ constexpr auto ParameterTypeAttribute("ParType");
constexpr auto ParameterValueAttribute("ParValue");
constexpr auto ParameterRoleAttribute("ParRole");
constexpr auto ParameterExtAttribute("ParExt");
constexpr auto ParameterToolAttribute("ParTool");
constexpr auto ExternalPropertyColorAtt("Color");
constexpr auto ExternalPropertyIdentifierAtt("Identifier");
} // namespace Tags
......
......@@ -159,6 +159,16 @@ void ComboProperty::setStringOfValues(const QString& values)
setCurrentIndex(m_values.contains(current) ? m_values.indexOf(current) : 0);
}
QString ComboProperty::stringOfTooltips() const
{
return m_tooltips.join(value_separator);
}
void ComboProperty::setStringOfTooltips(const QString& tooltips)
{
m_tooltips = tooltips.split(value_separator);
}
//! Constructs variant enclosing given ComboProperty.
QVariant ComboProperty::variant() const
......
......@@ -51,6 +51,9 @@ public:
QString stringOfValues() const;
void setStringOfValues(const QString& values);
QString stringOfTooltips() const;
void setStringOfTooltips(const QString& tooltips);
QVariant variant() const;
QVector<int> selectedIndices() const;
......@@ -68,7 +71,7 @@ private:
ComboProperty(QStringList values);
QStringList m_values;
QStringList m_tooltips; // without saving
QStringList m_tooltips;
QVector<int> m_selected_indices;
};
......
......@@ -15,7 +15,6 @@
#include "GUI/View/Fit/MinimizerSettingsWidget.h"
#include "GUI/Model/Job/FitSuiteItem.h"
#include "GUI/Model/Job/JobItem.h"
#include "GUI/Model/Job/MinimizerItem.h"
#include "GUI/View/Common/DoubleSpinBox.h"
#include "GUI/View/Common/SafeSpinBox.h"
#include "GUI/View/Tool/LayoutUtils.h"
......@@ -23,6 +22,7 @@
#include <QComboBox>
#include <QFormLayout>
#include <QPushButton>
#include <QStandardItemModel>
#include <QVBoxLayout>
MinimizerSettingsWidget::MinimizerSettingsWidget(QWidget* parent)
......@@ -55,13 +55,7 @@ void MinimizerSettingsWidget::setMinItem(MinimizerContainerItem* minimizerItem)
if (!m_currentItem)
return;
m_mainLayout->addRow("Minimizer:",
GUI::Util::createComboBox([=] { return m_currentItem->minimizerCombo(); },
[=](const QString& t) {
m_currentItem->setCurrentMinimizer(t);
createMimimizerEdits();
},
&m_updaters));
createGroupedAlgorithmsCombo();
auto* w = new QWidget(this);
m_minimizerLayout = new QFormLayout(w);
......@@ -86,6 +80,78 @@ void MinimizerSettingsWidget::setMinItem(MinimizerContainerItem* minimizerItem)
createMimimizerEdits();
updateUIValues();
}
void MinimizerSettingsWidget::createGroupedAlgorithmsCombo()
{
QComboBox* comboBox = new QComboBox;
QStringList list = m_currentItem->commonAlgorithmCombo().values();
// list with headers and separators
QList<qsizetype> header_indices;
QList<qsizetype> non_separator_indices;
for (QString algorithm : list) {
comboBox->addItem(algorithm);
if (!m_currentItem->algorithmHasMinimizer(algorithm)) {
comboBox->insertSeparator(comboBox->count() - 1);
qsizetype header_index = comboBox->count() - 1;
header_indices.append(header_index);
non_separator_indices.append(header_index);
QStandardItemModel* model = qobject_cast<QStandardItemModel*>(comboBox->model());
QStandardItem* header_item = model->item(header_index);
header_item->setSelectable(false);
QFont font(comboBox->font());
font.setBold(true);
header_item->setFont(font);
comboBox->insertSeparator(comboBox->count());
} else
non_separator_indices.append(comboBox->count() - 1);
}
comboBox->setCurrentText(m_currentItem->commonAlgorithmCombo().currentValue());
// tooltips
QStringList tooltips = m_currentItem->commonAlgorithmCombo().toolTips();
ASSERT(tooltips.size() == list.size())
int list_index = 0;
for (int index : non_separator_indices)
comboBox->setItemData(index, tooltips.at(list_index++), Qt::ToolTipRole);
// action
comboBox->setProperty("previous", comboBox->currentIndex());
QObject::connect(comboBox, &QComboBox::currentTextChanged, [=] {
// skip headers while scrolling
if (header_indices.contains(comboBox->currentIndex())) {
int previous_state = comboBox->property("previous").toInt();
int prev = comboBox->currentIndex() - 2;
int next = comboBox->currentIndex() + 2;
QSignalBlocker b(comboBox);
if (previous_state < comboBox->currentIndex() && next < comboBox->count())
comboBox->setCurrentIndex(next);
else if (previous_state > comboBox->currentIndex() && prev >= 0)
comboBox->setCurrentIndex(prev);
else
comboBox->setCurrentIndex(previous_state);
}
comboBox->setProperty("previous", comboBox->currentIndex());
m_currentItem->setCurrentCommonAlgorithm(comboBox->currentText());
createMimimizerEdits();
});
// update state
m_updaters << [=]() {
QSignalBlocker b(comboBox);
comboBox->setCurrentText(m_currentItem->commonAlgorithmCombo().currentValue());
};
m_mainLayout->addRow("Algorithm:", comboBox);
}
void MinimizerSettingsWidget::createMimimizerEdits()
{
GUI::Util::Layout::clearLayout(m_minimizerLayout);
......@@ -115,11 +181,6 @@ void MinimizerSettingsWidget::createMinuitEdits()
{
MinuitMinimizerItem* minItem = m_currentItem->minimizerItemMinuit();
m_minimizerLayout->addRow(
"Algorithm:", GUI::Util::createComboBox(
[=] { return minItem->algorithmCombo(); },
[=](const QString& t) { minItem->setCurrentAlgorithm(t); }, &m_updaters));
m_minimizerLayout->addRow(
"Strategy:",
GUI::Util::createIntSpinbox([=] { return minItem->strategy(); },
......@@ -159,11 +220,6 @@ void MinimizerSettingsWidget::createGSLMultiMinEdits()
{
GSLMultiMinimizerItem* minItem = m_currentItem->minimizerItemGSLMulti();
m_minimizerLayout->addRow(
"Algorithm:", GUI::Util::createComboBox(
[=] { return minItem->algorithmCombo(); },
[=](const QString& t) { minItem->setCurrentAlgorithm(t); }, &m_updaters));
m_minimizerLayout->addRow(
"Max iterations:",
GUI::Util::createIntSpinbox([=] { return minItem->maxIterations(); },
......
......@@ -15,13 +15,10 @@
#ifndef BORNAGAIN_GUI_VIEW_FIT_MINIMIZERSETTINGSWIDGET_H
#define BORNAGAIN_GUI_VIEW_FIT_MINIMIZERSETTINGSWIDGET_H
#include "GUI/Model/Descriptor/SelectionDescriptor.h"
#include <QList>
#include "GUI/Model/Job/MinimizerItem.h"
#include <QWidget>
#include <functional>
class JobItem;
class MinimizerContainerItem;
class QFormLayout;
//! The MinimizerSettingsWidget contains editor for all minimizer settings and related fit
......@@ -38,6 +35,7 @@ public slots:
void setMinItem(MinimizerContainerItem* minimizerItem);
private:
void createGroupedAlgorithmsCombo();
void createMimimizerEdits();
void createMinuitEdits();
void createGSLMultiMinEdits();
......@@ -47,7 +45,6 @@ private:
void updateUIValues();
private:
MinimizerContainerItem* m_currentItem;
QFormLayout* m_mainLayout;
......
......@@ -270,10 +270,11 @@ TEST_F(TestComboProperty, comboXML)
ComboProperty combo = ComboProperty() << "a1"
<< "a2"
<< "a3";
combo.setToolTips({"t1", "t2", "t3"});
combo.setStringOfSelections("0,2");
QString expected = "<Parameter ParType=\"ComboProperty\" ParRole=\"0\" ParValue=\"0,2\" "
"ParExt=\"a1;a2;a3\"/>";
"ParExt=\"a1;a2;a3\" ParTool=\"t1;t2;t3\"/>";
EXPECT_EQ(UTest::GUI::propertyToXML(combo), expected);
// reading from XML
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment