Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qsimplex_p.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qsimplex_p.h"
5
6#include <QtCore/qset.h>
7#include <QtCore/qdebug.h>
8
9#include <stdlib.h>
10
12
13using namespace Qt::StringLiterals;
14
45QSimplex::QSimplex() : objective(nullptr), rows(0), columns(0), firstArtificial(0), matrix(nullptr)
46{
47}
48
53{
54 clearDataStructures();
55}
56
60void QSimplex::clearDataStructures()
61{
62 if (matrix == nullptr)
63 return;
64
65 // Matrix
66 rows = 0;
67 columns = 0;
68 firstArtificial = 0;
69 free(matrix);
70 matrix = nullptr;
71
72 // Constraints
73 for (int i = 0; i < constraints.size(); ++i) {
74 delete constraints[i]->helper.first;
75 delete constraints[i]->artificial;
76 delete constraints[i];
77 }
78 constraints.clear();
79
80 // Other
81 variables.clear();
82 objective = nullptr;
83}
84
93bool QSimplex::setConstraints(const QList<QSimplexConstraint *> &newConstraints)
94{
96 // Reset to initial state //
98 clearDataStructures();
99
100 if (newConstraints.isEmpty())
101 return true; // we are ok with no constraints
102
103 // Make deep copy of constraints. We need this copy because we may change
104 // them in the simplification method.
105 for (int i = 0; i < newConstraints.size(); ++i) {
107 c->constant = newConstraints[i]->constant;
108 c->ratio = newConstraints[i]->ratio;
109 c->variables = newConstraints[i]->variables;
110 constraints << c;
111 }
112
113 // Remove constraints of type Var == K and replace them for their value.
114 if (!simplifyConstraints(&constraints)) {
115 qWarning("QSimplex: No feasible solution!");
116 clearDataStructures();
117 return false;
118 }
119
121 // Prepare variables and constraints //
123
124 // Set Variables direct mapping.
125 // "variables" is a list that provides a stable, indexed list of all variables
126 // used in this problem.
127 QSet<QSimplexVariable *> variablesSet;
128 for (int i = 0; i < constraints.size(); ++i) {
129 const auto &v = constraints.at(i)->variables;
130 for (auto it = v.cbegin(), end = v.cend(); it != end; ++it)
131 variablesSet.insert(it.key());
132 }
133 variables = variablesSet.values();
134
135 // Set Variables reverse mapping
136 // We also need to be able to find the index for a given variable, to do that
137 // we store in each variable its index.
138 for (int i = 0; i < variables.size(); ++i) {
139 // The variable "0" goes at the column "1", etc...
140 variables[i]->index = i + 1;
141 }
142
143 // Normalize Constraints
144 // In this step, we prepare the constraints in two ways:
145 // Firstly, we modify all constraints of type "LessOrEqual" or "MoreOrEqual"
146 // by the adding slack or surplus variables and making them "Equal" constraints.
147 // Secondly, we need every single constraint to have a direct, easy feasible
148 // solution. Constraints that have slack variables are already easy to solve,
149 // to all the others we add artificial variables.
150 //
151 // At the end we modify the constraints as follows:
152 // - LessOrEqual: SLACK variable is added.
153 // - Equal: ARTIFICIAL variable is added.
154 // - More or Equal: ARTIFICIAL and SURPLUS variables are added.
155 int variableIndex = variables.size();
156 QList <QSimplexVariable *> artificialList;
157
158 for (int i = 0; i < constraints.size(); ++i) {
159 QSimplexVariable *slack;
160 QSimplexVariable *surplus;
161 QSimplexVariable *artificial;
162
163 Q_ASSERT(constraints[i]->helper.first == 0);
164 Q_ASSERT(constraints[i]->artificial == nullptr);
165
166 switch(constraints[i]->ratio) {
168 slack = new QSimplexVariable;
169 slack->index = ++variableIndex;
170 constraints[i]->helper.first = slack;
171 constraints[i]->helper.second = 1.0;
172 break;
174 surplus = new QSimplexVariable;
175 surplus->index = ++variableIndex;
176 constraints[i]->helper.first = surplus;
177 constraints[i]->helper.second = -1.0;
180 artificial = new QSimplexVariable;
181 constraints[i]->artificial = artificial;
182 artificialList += constraints[i]->artificial;
183 break;
184 }
185 }
186
187 // All original, slack and surplus have already had its index set
188 // at this point. We now set the index of the artificial variables
189 // as to ensure they are at the end of the variable list and therefore
190 // can be easily removed at the end of this method.
191 firstArtificial = variableIndex + 1;
192 for (int i = 0; i < artificialList.size(); ++i)
193 artificialList[i]->index = ++variableIndex;
194 artificialList.clear();
195
197 // Fill the Simplex matrix //
199
200 // One for each variable plus the Basic and BFS columns (first and last)
201 columns = variableIndex + 2;
202 // One for each constraint plus the objective function
203 rows = constraints.size() + 1;
204
205 matrix = (qreal *)malloc(sizeof(qreal) * columns * rows);
206 if (!matrix) {
207 qWarning("QSimplex: Unable to allocate memory!");
208 return false;
209 }
210 for (int i = columns * rows - 1; i >= 0; --i)
211 matrix[i] = 0.0;
212
213 // Fill Matrix
214 for (int i = 1; i <= constraints.size(); ++i) {
215 QSimplexConstraint *c = constraints[i - 1];
216
217 if (c->artificial) {
218 // Will use artificial basic variable
219 setValueAt(i, 0, c->artificial->index);
220 setValueAt(i, c->artificial->index, 1.0);
221
222 if (c->helper.second != 0.0) {
223 // Surplus variable
224 setValueAt(i, c->helper.first->index, c->helper.second);
225 }
226 } else {
227 // Slack is used as the basic variable
228 Q_ASSERT(c->helper.second == 1.0);
229 setValueAt(i, 0, c->helper.first->index);
230 setValueAt(i, c->helper.first->index, 1.0);
231 }
232
234 for (iter = c->variables.constBegin();
235 iter != c->variables.constEnd();
236 ++iter) {
237 setValueAt(i, iter.key()->index, iter.value());
238 }
239
240 setValueAt(i, columns - 1, c->constant);
241 }
242
243 // Set objective for the first-phase Simplex.
244 // Z = -1 * sum_of_artificial_vars
245 for (int j = firstArtificial; j < columns - 1; ++j)
246 setValueAt(0, j, 1.0);
247
248 // Maximize our objective (artificial vars go to zero)
249 solveMaxHelper();
250
251 // If there is a solution where the sum of all artificial
252 // variables is zero, then all of them can be removed and yet
253 // we will have a feasible (but not optimal) solution for the
254 // original problem.
255 // Otherwise, we clean up our structures and report there is
256 // no feasible solution.
257 if ((valueAt(0, columns - 1) != 0.0) && (qAbs(valueAt(0, columns - 1)) > 0.00001)) {
258 qWarning("QSimplex: No feasible solution!");
259 clearDataStructures();
260 return false;
261 }
262
263 // Remove artificial variables. We already have a feasible
264 // solution for the first problem, thus we don't need them
265 // anymore.
266 clearColumns(firstArtificial, columns - 2);
267
268 return true;
269}
270
280void QSimplex::solveMaxHelper()
281{
282 reducedRowEchelon();
283 while (iterate()) ;
284}
285
290{
291 objective = newObjective;
292}
293
297void QSimplex::clearRow(int rowIndex)
298{
299 qreal *item = matrix + rowIndex * columns;
300 for (int i = 0; i < columns; ++i)
301 item[i] = 0.0;
302}
303
307void QSimplex::clearColumns(int first, int last)
308{
309 for (int i = 0; i < rows; ++i) {
310 qreal *row = matrix + i * columns;
311 for (int j = first; j <= last; ++j)
312 row[j] = 0.0;
313 }
314}
315
320{
321 qDebug("---- Simplex Matrix ----\n");
322
323 QString str(" "_L1);
324 for (int j = 0; j < columns; ++j)
325 str += QString::fromLatin1(" <%1 >").arg(j, 2);
326 qDebug("%s", qPrintable(str));
327 for (int i = 0; i < rows; ++i) {
328 str = QString::fromLatin1("Row %1:").arg(i, 2);
329
330 qreal *row = matrix + i * columns;
331 for (int j = 0; j < columns; ++j)
332 str += QString::fromLatin1("%1").arg(row[j], 7, 'f', 2);
333 qDebug("%s", qPrintable(str));
334 }
335 qDebug("------------------------\n");
336}
337
341void QSimplex::combineRows(int toIndex, int fromIndex, qreal factor)
342{
343 if (!factor)
344 return;
345
346 qreal *from = matrix + fromIndex * columns;
347 qreal *to = matrix + toIndex * columns;
348
349 for (int j = 1; j < columns; ++j) {
350 qreal value = from[j];
351
352 // skip to[j] = to[j] + factor*0.0
353 if (value == 0.0)
354 continue;
355
356 to[j] += factor * value;
357
358 // ### Avoid Numerical errors
359 if (qAbs(to[j]) < 0.0000000001)
360 to[j] = 0.0;
361 }
362}
363
367int QSimplex::findPivotColumn()
368{
369 qreal min = 0;
370 int minIndex = -1;
371
372 for (int j = 0; j < columns-1; ++j) {
373 if (valueAt(0, j) < min) {
374 min = valueAt(0, j);
375 minIndex = j;
376 }
377 }
378
379 return minIndex;
380}
381
399int QSimplex::pivotRowForColumn(int column)
400{
401 qreal min = qreal(999999999999.0); // ###
402 int minIndex = -1;
403
404 for (int i = 1; i < rows; ++i) {
405 qreal divisor = valueAt(i, column);
406 if (divisor <= 0)
407 continue;
408
409 qreal quotient = valueAt(i, columns - 1) / divisor;
410 if (quotient < min) {
411 min = quotient;
412 minIndex = i;
413 } else if ((quotient == min) && (valueAt(i, 0) > valueAt(minIndex, 0))) {
414 minIndex = i;
415 }
416 }
417
418 return minIndex;
419}
420
424void QSimplex::reducedRowEchelon()
425{
426 for (int i = 1; i < rows; ++i) {
427 int factorInObjectiveRow = valueAt(i, 0);
428 combineRows(0, i, -1 * valueAt(0, factorInObjectiveRow));
429 }
430}
431
438bool QSimplex::iterate()
439{
440 // Find Pivot column
441 int pivotColumn = findPivotColumn();
442 if (pivotColumn == -1)
443 return false;
444
445 // Find Pivot row for column
446 int pivotRow = pivotRowForColumn(pivotColumn);
447 if (pivotRow == -1) {
448 qWarning("QSimplex: Unbounded problem!");
449 return false;
450 }
451
452 // Normalize Pivot Row
453 qreal pivot = valueAt(pivotRow, pivotColumn);
454 if (pivot != 1.0)
455 combineRows(pivotRow, pivotRow, (1.0 - pivot) / pivot);
456
457 // Update other rows
458 for (int row=0; row < rows; ++row) {
459 if (row == pivotRow)
460 continue;
461
462 combineRows(row, pivotRow, -1 * valueAt(row, pivotColumn));
463 }
464
465 // Update first column
466 setValueAt(pivotRow, 0, pivotColumn);
467
468 // dumpMatrix();
469 // qDebug("------------ end of iteration --------------\n");
470 return true;
471}
472
486qreal QSimplex::solver(SolverFactor factor)
487{
488 // Remove old objective
489 clearRow(0);
490
491 // Set new objective in the first row of the simplex matrix
492 qreal resultOffset = 0;
494 for (iter = objective->variables.constBegin();
495 iter != objective->variables.constEnd();
496 ++iter) {
497
498 // Check if the variable was removed in the simplification process.
499 // If so, we save its offset to the objective function and skip adding
500 // it to the matrix.
501 if (iter.key()->index == -1) {
502 resultOffset += iter.value() * iter.key()->result;
503 continue;
504 }
505
506 setValueAt(0, iter.key()->index, -1 * factor * iter.value());
507 }
508
509 solveMaxHelper();
510 collectResults();
511
512#ifdef QT_DEBUG
513 for (int i = 0; i < constraints.size(); ++i) {
514 Q_ASSERT(constraints[i]->isSatisfied());
515 }
516#endif
517
518 // Return the value calculated by the simplex plus the value of the
519 // fixed variables.
520 return (qToUnderlying(factor) * valueAt(0, columns - 1)) + resultOffset;
521}
522
528{
529 return solver(Minimum);
530}
531
537{
538 return solver(Maximum);
539}
540
547void QSimplex::collectResults()
548{
549 // All variables are zero unless overridden below.
550
551 // ### Is this really needed? Is there any chance that an
552 // important variable remains as non-basic at the end of simplex?
553 for (int i = 0; i < variables.size(); ++i)
554 variables[i]->result = 0;
555
556 // Basic variables
557 // Update the variable indicated in the first column with the value
558 // in the last column.
559 for (int i = 1; i < rows; ++i) {
560 int index = valueAt(i, 0) - 1;
561 if (index < variables.size())
562 variables[index]->result = valueAt(i, columns - 1);
563 }
564}
565
571bool QSimplex::simplifyConstraints(QList<QSimplexConstraint *> *constraints)
572{
573 QHash<QSimplexVariable *, qreal> results; // List of single-valued variables
574 bool modified = true; // Any chance more optimization exists?
575
576 while (modified) {
577 modified = false;
578
579 // For all constraints
580 QList<QSimplexConstraint *>::iterator iter = constraints->begin();
581 while (iter != constraints->end()) {
583 if ((c->ratio == QSimplexConstraint::Equal) && (c->variables.size() == 1)) {
584 // Check whether this is a constraint of type Var == K
585 // If so, save its value to "results".
586 QSimplexVariable *variable = c->variables.constBegin().key();
587 qreal result = c->constant / c->variables.value(variable);
588
590 variable->result = result;
591 variable->index = -1;
592 modified = true;
593
594 }
595
596 // Replace known values among their variables
598 for (r = results.constBegin(); r != results.constEnd(); ++r) {
599 if (c->variables.contains(r.key())) {
600 c->constant -= r.value() * c->variables.take(r.key());
601 modified = true;
602 }
603 }
604
605 // Keep it normalized
606 if (c->constant < 0)
607 c->invert();
608
609 if (c->variables.isEmpty()) {
610 // If constraint became empty due to substitution, delete it.
611 if (c->isSatisfied() == false)
612 // We must ensure that the constraint soon to be deleted would not
613 // make the problem unfeasible if left behind. If that's the case,
614 // we return false so the simplex solver can properly report that.
615 return false;
616
617 delete c;
618 iter = constraints->erase(iter);
619 } else {
620 ++iter;
621 }
622 }
623 }
624
625 return true;
626}
627
629{
631 ratio = Ratio(2 - ratio);
632
634 for (iter = variables.begin(); iter != variables.end(); ++iter) {
635 iter.value() = -iter.value();
636 }
637}
638
\inmodule QtCore
Definition qhash.h:1145
\inmodule QtCore
Definition qhash.h:1103
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1212
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1219
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1215
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1216
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
qsizetype size() const noexcept
Definition qlist.h:397
T & first()
Definition qlist.h:645
iterator insert(qsizetype i, parameter_type t)
Definition qlist.h:488
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
const_iterator constBegin() const noexcept
Definition qlist.h:632
const_iterator constEnd() const noexcept
Definition qlist.h:633
void clear()
Definition qlist.h:434
const_iterator cbegin() const noexcept
Definition qset.h:138
bool setConstraints(const QList< QSimplexConstraint * > &constraints)
void dumpMatrix()
void setObjective(QSimplexConstraint *objective)
qreal solveMax()
qreal solveMin()
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
QString str
[2]
QSet< QString >::iterator it
Combined button and popup list for selecting options.
constexpr const T & min(const T &a, const T &b)
Definition qnumeric.h:366
#define Q_FALLTHROUGH()
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLsizei const GLfloat * v
[13]
GLuint divisor
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLint first
GLenum GLenum GLsizei void GLsizei void * column
const GLubyte * c
GLuint GLenum matrix
GLenum GLenum GLsizei void * row
GLuint64EXT * result
[6]
GLenum GLenum variable
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define qPrintable(string)
Definition qstring.h:1531
QT_BEGIN_NAMESPACE constexpr std::underlying_type_t< Enum > qToUnderlying(Enum e) noexcept
double qreal
Definition qtypes.h:187
static uint toIndex(ExecutionEngine *e, const Value &v)
QObject::connect nullptr
QGraphicsItem * item
QHash< QSimplexVariable *, qreal > variables
Definition qsimplex_p.h:57