Sketcher: Use DenseQR for small sketches to avoid SparseQR rank issues (#26559)
This commit is contained in:
@@ -641,6 +641,14 @@ public:
|
||||
{
|
||||
return debugMode;
|
||||
}
|
||||
inline void setAutoQRThreshold(int val)
|
||||
{
|
||||
GCSsys.autoQRThreshold = val;
|
||||
}
|
||||
inline void setSketchAutoAlgo(bool val)
|
||||
{
|
||||
GCSsys.autoChooseAlgorithm = val;
|
||||
}
|
||||
inline void setMaxIter(int maxiter)
|
||||
{
|
||||
GCSsys.maxIter = maxiter;
|
||||
|
||||
@@ -486,6 +486,8 @@ System::System()
|
||||
, convergence(1e-10)
|
||||
, convergenceRedundant(1e-10)
|
||||
, qrAlgorithm(EigenSparseQR)
|
||||
, autoChooseAlgorithm(true)
|
||||
, autoQRThreshold(1000)
|
||||
, dogLegGaussStep(FullPivLU)
|
||||
, qrpivotThreshold(1E-13)
|
||||
, debugMode(Minimal)
|
||||
@@ -4808,6 +4810,15 @@ int System::diagnose(Algorithm alg)
|
||||
hasDiagnosis = true;
|
||||
dofs = pdiagnoselist.size();
|
||||
|
||||
// Use DenseQR for small to medium systems to avoid SparseQR rank issues.
|
||||
// SparseQR is known to fail rank detection on specific geometric structures (e.g. aligned slots).
|
||||
// 200 parameters roughly corresponds to ~100 points/curves, covering most complex sketches
|
||||
// where stability is preferred over pure O(N) performance.
|
||||
// See: https://github.com/FreeCAD/FreeCAD/issues/10903
|
||||
if (autoChooseAlgorithm) {
|
||||
qrAlgorithm = dofs < autoQRThreshold ? EigenDenseQR : EigenSparseQR;
|
||||
}
|
||||
|
||||
// There is a legacy decision to use QR decomposition. I (abdullah) do not know all the
|
||||
// consideration taken in that decisions. I see that:
|
||||
// - QR decomposition is able to provide information about the rank and
|
||||
|
||||
@@ -239,6 +239,8 @@ public:
|
||||
double convergence;
|
||||
double convergenceRedundant;
|
||||
QRAlgorithm qrAlgorithm;
|
||||
bool autoChooseAlgorithm;
|
||||
int autoQRThreshold;
|
||||
DogLegGaussStep dogLegGaussStep;
|
||||
double qrpivotThreshold;
|
||||
DebugMode debugMode;
|
||||
|
||||
@@ -71,6 +71,8 @@ TaskSketcherSolverAdvanced::TaskSketcherSolverAdvanced(ViewProviderSketch* sketc
|
||||
ui->checkBoxSketchSizeMultiplier->onRestore();
|
||||
ui->lineEditConvergence->onRestore();
|
||||
ui->comboBoxQRMethod->onRestore();
|
||||
ui->spinBoxAutoQRThreshold->onRestore();
|
||||
ui->checkBoxAutoChooseAlgo->onRestore();
|
||||
ui->lineEditQRPivotThreshold->onRestore();
|
||||
ui->comboBoxRedundantDefaultSolver->onRestore();
|
||||
ui->spinBoxRedundantSolverMaxIterations->onRestore();
|
||||
@@ -78,6 +80,12 @@ TaskSketcherSolverAdvanced::TaskSketcherSolverAdvanced(ViewProviderSketch* sketc
|
||||
ui->lineEditRedundantConvergence->onRestore();
|
||||
ui->comboBoxDebugMode->onRestore();
|
||||
|
||||
bool autoAlgo = ui->checkBoxAutoChooseAlgo->isChecked();
|
||||
ui->labelAutoQRThreshold->setVisible(autoAlgo);
|
||||
ui->spinBoxAutoQRThreshold->setVisible(autoAlgo);
|
||||
ui->labelQRAlgorithm->setVisible(!autoAlgo);
|
||||
ui->comboBoxQRMethod->setVisible(!autoAlgo);
|
||||
|
||||
updateSketchObject();
|
||||
}
|
||||
|
||||
@@ -104,6 +112,27 @@ void TaskSketcherSolverAdvanced::setupConnections()
|
||||
this,
|
||||
&TaskSketcherSolverAdvanced::onSpinBoxMaxIterValueChanged
|
||||
);
|
||||
connect(
|
||||
ui->spinBoxAutoQRThreshold,
|
||||
qOverload<int>(&QSpinBox::valueChanged),
|
||||
this,
|
||||
&TaskSketcherSolverAdvanced::onSpinBoxAutoQRAlgoChanged
|
||||
);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
|
||||
connect(
|
||||
ui->checkBoxAutoChooseAlgo,
|
||||
&QCheckBox::checkStateChanged,
|
||||
this,
|
||||
&TaskSketcherSolverAdvanced::onCheckBoxAutoQRAlgoStateChanged
|
||||
);
|
||||
#else
|
||||
connect(
|
||||
ui->checkBoxAutoChooseAlgo,
|
||||
&QCheckBox::stateChanged,
|
||||
this,
|
||||
&TaskSketcherSolverAdvanced::onCheckBoxAutoQRAlgoStateChanged
|
||||
);
|
||||
#endif
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
|
||||
connect(
|
||||
ui->checkBoxSketchSizeMultiplier,
|
||||
@@ -637,6 +666,35 @@ void TaskSketcherSolverAdvanced::onSpinBoxMaxIterValueChanged(int i)
|
||||
const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch()).setMaxIter(i);
|
||||
}
|
||||
|
||||
void TaskSketcherSolverAdvanced::onSpinBoxAutoQRAlgoChanged(int i)
|
||||
{
|
||||
ui->spinBoxAutoQRThreshold->onSave();
|
||||
const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch())
|
||||
.setAutoQRThreshold(i);
|
||||
}
|
||||
|
||||
void TaskSketcherSolverAdvanced::onCheckBoxAutoQRAlgoStateChanged(int state)
|
||||
{
|
||||
if (state == Qt::Checked) {
|
||||
ui->spinBoxAutoQRThreshold->show();
|
||||
ui->comboBoxQRMethod->hide();
|
||||
ui->labelAutoQRThreshold->show();
|
||||
ui->labelQRAlgorithm->hide();
|
||||
ui->checkBoxAutoChooseAlgo->onSave();
|
||||
const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch())
|
||||
.setSketchAutoAlgo(true);
|
||||
}
|
||||
else if (state == Qt::Unchecked) {
|
||||
ui->spinBoxAutoQRThreshold->hide();
|
||||
ui->comboBoxQRMethod->show();
|
||||
ui->labelAutoQRThreshold->hide();
|
||||
ui->labelQRAlgorithm->show();
|
||||
ui->checkBoxAutoChooseAlgo->onSave();
|
||||
const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch())
|
||||
.setSketchAutoAlgo(false);
|
||||
}
|
||||
}
|
||||
|
||||
void TaskSketcherSolverAdvanced::onCheckBoxSketchSizeMultiplierStateChanged(int state)
|
||||
{
|
||||
if (state == Qt::Checked) {
|
||||
@@ -784,6 +842,8 @@ void TaskSketcherSolverAdvanced::onPushButtonDefaultsClicked(bool checked /* = f
|
||||
ui->checkBoxSketchSizeMultiplier->onRestore();
|
||||
ui->lineEditConvergence->onRestore();
|
||||
ui->comboBoxQRMethod->onRestore();
|
||||
ui->spinBoxAutoQRThreshold->onRestore();
|
||||
ui->checkBoxAutoChooseAlgo->onRestore();
|
||||
ui->lineEditQRPivotThreshold->onRestore();
|
||||
ui->comboBoxRedundantDefaultSolver->onRestore();
|
||||
ui->spinBoxRedundantSolverMaxIterations->onRestore();
|
||||
@@ -796,30 +856,24 @@ void TaskSketcherSolverAdvanced::onPushButtonDefaultsClicked(bool checked /* = f
|
||||
|
||||
void TaskSketcherSolverAdvanced::updateSketchObject()
|
||||
{
|
||||
const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch())
|
||||
.setDebugMode((GCS::DebugMode)ui->comboBoxDebugMode->currentIndex());
|
||||
const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch())
|
||||
.setSketchSizeMultiplierRedundant(ui->checkBoxRedundantSketchSizeMultiplier->isChecked());
|
||||
const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch())
|
||||
.setMaxIterRedundant(ui->spinBoxRedundantSolverMaxIterations->value());
|
||||
const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch()).defaultSolverRedundant
|
||||
= static_cast<GCS::Algorithm>(ui->comboBoxRedundantDefaultSolver->currentIndex());
|
||||
const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch())
|
||||
.setQRAlgorithm((GCS::QRAlgorithm)ui->comboBoxQRMethod->currentIndex());
|
||||
const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch())
|
||||
.setQRPivotThreshold(ui->lineEditQRPivotThreshold->text().toDouble());
|
||||
const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch())
|
||||
.setConvergenceRedundant(ui->lineEditRedundantConvergence->text().toDouble());
|
||||
const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch())
|
||||
.setConvergence(ui->lineEditConvergence->text().toDouble());
|
||||
const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch())
|
||||
.setSketchSizeMultiplier(ui->checkBoxSketchSizeMultiplier->isChecked());
|
||||
const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch())
|
||||
.setMaxIter(ui->spinBoxMaxIter->value());
|
||||
const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch()).defaultSolver
|
||||
= static_cast<GCS::Algorithm>(ui->comboBoxDefaultSolver->currentIndex());
|
||||
const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch())
|
||||
.setDogLegGaussStep((GCS::DogLegGaussStep)ui->comboBoxDogLegGaussStep->currentIndex());
|
||||
auto& sketch = const_cast<Sketcher::Sketch&>(sketchView->getSketchObject()->getSolvedSketch());
|
||||
|
||||
sketch.setDebugMode((GCS::DebugMode)ui->comboBoxDebugMode->currentIndex());
|
||||
sketch.setSketchSizeMultiplierRedundant(ui->checkBoxRedundantSketchSizeMultiplier->isChecked());
|
||||
sketch.setMaxIterRedundant(ui->spinBoxRedundantSolverMaxIterations->value());
|
||||
sketch.defaultSolverRedundant = static_cast<GCS::Algorithm>(
|
||||
ui->comboBoxRedundantDefaultSolver->currentIndex()
|
||||
);
|
||||
sketch.setQRAlgorithm((GCS::QRAlgorithm)ui->comboBoxQRMethod->currentIndex());
|
||||
sketch.setAutoQRThreshold(ui->spinBoxAutoQRThreshold->value());
|
||||
sketch.setSketchAutoAlgo(ui->checkBoxAutoChooseAlgo->isChecked());
|
||||
sketch.setQRPivotThreshold(ui->lineEditQRPivotThreshold->text().toDouble());
|
||||
sketch.setConvergenceRedundant(ui->lineEditRedundantConvergence->text().toDouble());
|
||||
sketch.setConvergence(ui->lineEditConvergence->text().toDouble());
|
||||
sketch.setSketchSizeMultiplier(ui->checkBoxSketchSizeMultiplier->isChecked());
|
||||
sketch.setMaxIter(ui->spinBoxMaxIter->value());
|
||||
sketch.defaultSolver = static_cast<GCS::Algorithm>(ui->comboBoxDefaultSolver->currentIndex());
|
||||
sketch.setDogLegGaussStep((GCS::DogLegGaussStep)ui->comboBoxDogLegGaussStep->currentIndex());
|
||||
|
||||
updateDefaultMethodParameters();
|
||||
updateRedundantMethodParameters();
|
||||
|
||||
@@ -53,6 +53,8 @@ private:
|
||||
void onComboBoxDefaultSolverCurrentIndexChanged(int index);
|
||||
void onComboBoxDogLegGaussStepCurrentIndexChanged(int index);
|
||||
void onSpinBoxMaxIterValueChanged(int i);
|
||||
void onSpinBoxAutoQRAlgoChanged(int i);
|
||||
void onCheckBoxAutoQRAlgoStateChanged(int state);
|
||||
void onCheckBoxSketchSizeMultiplierStateChanged(int state);
|
||||
void onLineEditConvergenceEditingFinished();
|
||||
void onComboBoxQRMethodCurrentIndexChanged(int index);
|
||||
|
||||
@@ -290,6 +290,87 @@ to determine whether a solution converges or not</string>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_32">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelAutoChooseAlgo">
|
||||
<property name="toolTip">
|
||||
<string>Automatically select the QR algorithm based on number of dofs</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Automatic QR algorithm</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::PrefCheckBox" name="checkBoxAutoChooseAlgo">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Automatically select the QR algorithm based on number of dofs</string>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>AutoChooseAlgo</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/Sketcher/SolverAdvanced</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_62">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelAutoQRThreshold">
|
||||
<property name="toolTip">
|
||||
<string>Maximum number of parameters before switching to sparse QR algorithm</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Auto QR threshold</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::PrefSpinBox" name="spinBoxAutoQRThreshold">
|
||||
<property name="toolTip">
|
||||
<string>Maximum number of parameters before switching to sparse QR algorithm</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10000000</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1000</number>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>AutoQRThreshold</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/Sketcher/SolverAdvanced</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
|
||||
Reference in New Issue
Block a user