source: trunk/src/gui/graphicsview/qsimplex_p.cpp@ 661

Last change on this file since 661 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

  • Property svn:eol-style set to native
File size: 19.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qsimplex_p.h"
43
44#include <QtCore/qset.h>
45#include <QtCore/qdebug.h>
46
47#include <stdlib.h>
48
49QT_BEGIN_NAMESPACE
50
51/*!
52 \internal
53 \class QSimplex
54
55 The QSimplex class is a Linear Programming problem solver based on the two-phase
56 simplex method.
57
58 It takes a set of QSimplexConstraints as its restrictive constraints and an
59 additional QSimplexConstraint as its objective function. Then methods to maximize
60 and minimize the problem solution are provided.
61
62 The two-phase simplex method is based on the following steps:
63 First phase:
64 1.a) Modify the original, complex, and possibly not feasible problem, into a new,
65 easy to solve problem.
66 1.b) Set as the objective of the new problem, a feasible solution for the original
67 complex problem.
68 1.c) Run simplex to optimize the modified problem and check whether a solution for
69 the original problem exists.
70
71 Second phase:
72 2.a) Go back to the original problem with the feasibl (but not optimal) solution
73 found in the first phase.
74 2.b) Set the original objective.
75 3.c) Run simplex to optimize the original problem towards its optimal solution.
76*/
77
78/*!
79 \internal
80*/
81QSimplex::QSimplex() : objective(0), rows(0), columns(0), firstArtificial(0), matrix(0)
82{
83}
84
85/*!
86 \internal
87*/
88QSimplex::~QSimplex()
89{
90 clearDataStructures();
91}
92
93/*!
94 \internal
95*/
96void QSimplex::clearDataStructures()
97{
98 if (matrix == 0)
99 return;
100
101 // Matrix
102 rows = 0;
103 columns = 0;
104 firstArtificial = 0;
105 free(matrix);
106 matrix = 0;
107
108 // Constraints
109 for (int i = 0; i < constraints.size(); ++i) {
110 delete constraints[i]->helper.first;
111 delete constraints[i]->artificial;
112 delete constraints[i];
113 }
114 constraints.clear();
115
116 // Other
117 variables.clear();
118 objective = 0;
119}
120
121/*!
122 \internal
123 Sets the new constraints in the simplex solver and returns whether the problem
124 is feasible.
125
126 This method sets the new constraints, normalizes them, creates the simplex matrix
127 and runs the first simplex phase.
128*/
129bool QSimplex::setConstraints(const QList<QSimplexConstraint *> newConstraints)
130{
131 ////////////////////////////
132 // Reset to initial state //
133 ////////////////////////////
134 clearDataStructures();
135
136 if (newConstraints.isEmpty())
137 return true; // we are ok with no constraints
138
139 // Make deep copy of constraints. We need this copy because we may change
140 // them in the simplification method.
141 for (int i = 0; i < newConstraints.size(); ++i) {
142 QSimplexConstraint *c = new QSimplexConstraint;
143 c->constant = newConstraints[i]->constant;
144 c->ratio = newConstraints[i]->ratio;
145 c->variables = newConstraints[i]->variables;
146 constraints << c;
147 }
148
149 // Remove constraints of type Var == K and replace them for their value.
150 if (!simplifyConstraints(&constraints)) {
151 qWarning() << "QSimplex: No feasible solution!";
152 clearDataStructures();
153 return false;
154 }
155
156 ///////////////////////////////////////
157 // Prepare variables and constraints //
158 ///////////////////////////////////////
159
160 // Set Variables direct mapping.
161 // "variables" is a list that provides a stable, indexed list of all variables
162 // used in this problem.
163 QSet<QSimplexVariable *> variablesSet;
164 for (int i = 0; i < constraints.size(); ++i)
165 variablesSet += \
166 QSet<QSimplexVariable *>::fromList(constraints[i]->variables.keys());
167 variables = variablesSet.toList();
168
169 // Set Variables reverse mapping
170 // We also need to be able to find the index for a given variable, to do that
171 // we store in each variable its index.
172 for (int i = 0; i < variables.size(); ++i) {
173 // The variable "0" goes at the column "1", etc...
174 variables[i]->index = i + 1;
175 }
176
177 // Normalize Constraints
178 // In this step, we prepare the constraints in two ways:
179 // Firstly, we modify all constraints of type "LessOrEqual" or "MoreOrEqual"
180 // by the adding slack or surplus variables and making them "Equal" constraints.
181 // Secondly, we need every single constraint to have a direct, easy feasible
182 // solution. Constraints that have slack variables are already easy to solve,
183 // to all the others we add artificial variables.
184 //
185 // At the end we modify the constraints as follows:
186 // - LessOrEqual: SLACK variable is added.
187 // - Equal: ARTIFICIAL variable is added.
188 // - More or Equal: ARTIFICIAL and SURPLUS variables are added.
189 int variableIndex = variables.size();
190 QList <QSimplexVariable *> artificialList;
191
192 for (int i = 0; i < constraints.size(); ++i) {
193 QSimplexVariable *slack;
194 QSimplexVariable *surplus;
195 QSimplexVariable *artificial;
196
197 Q_ASSERT(constraints[i]->helper.first == 0);
198 Q_ASSERT(constraints[i]->artificial == 0);
199
200 switch(constraints[i]->ratio) {
201 case QSimplexConstraint::LessOrEqual:
202 slack = new QSimplexVariable;
203 slack->index = ++variableIndex;
204 constraints[i]->helper.first = slack;
205 constraints[i]->helper.second = 1.0;
206 break;
207 case QSimplexConstraint::MoreOrEqual:
208 surplus = new QSimplexVariable;
209 surplus->index = ++variableIndex;
210 constraints[i]->helper.first = surplus;
211 constraints[i]->helper.second = -1.0;
212 // fall through
213 case QSimplexConstraint::Equal:
214 artificial = new QSimplexVariable;
215 constraints[i]->artificial = artificial;
216 artificialList += constraints[i]->artificial;
217 break;
218 }
219 }
220
221 // All original, slack and surplus have already had its index set
222 // at this point. We now set the index of the artificial variables
223 // as to ensure they are at the end of the variable list and therefore
224 // can be easily removed at the end of this method.
225 firstArtificial = variableIndex + 1;
226 for (int i = 0; i < artificialList.size(); ++i)
227 artificialList[i]->index = ++variableIndex;
228 artificialList.clear();
229
230 /////////////////////////////
231 // Fill the Simplex matrix //
232 /////////////////////////////
233
234 // One for each variable plus the Basic and BFS columns (first and last)
235 columns = variableIndex + 2;
236 // One for each constraint plus the objective function
237 rows = constraints.size() + 1;
238
239 matrix = (qreal *)malloc(sizeof(qreal) * columns * rows);
240 if (!matrix) {
241 qWarning() << "QSimplex: Unable to allocate memory!";
242 return false;
243 }
244 for (int i = columns * rows - 1; i >= 0; --i)
245 matrix[i] = 0.0;
246
247 // Fill Matrix
248 for (int i = 1; i <= constraints.size(); ++i) {
249 QSimplexConstraint *c = constraints[i - 1];
250
251 if (c->artificial) {
252 // Will use artificial basic variable
253 setValueAt(i, 0, c->artificial->index);
254 setValueAt(i, c->artificial->index, 1.0);
255
256 if (c->helper.second != 0.0) {
257 // Surplus variable
258 setValueAt(i, c->helper.first->index, c->helper.second);
259 }
260 } else {
261 // Slack is used as the basic variable
262 Q_ASSERT(c->helper.second == 1.0);
263 setValueAt(i, 0, c->helper.first->index);
264 setValueAt(i, c->helper.first->index, 1.0);
265 }
266
267 QHash<QSimplexVariable *, qreal>::const_iterator iter;
268 for (iter = c->variables.constBegin();
269 iter != c->variables.constEnd();
270 ++iter) {
271 setValueAt(i, iter.key()->index, iter.value());
272 }
273
274 setValueAt(i, columns - 1, c->constant);
275 }
276
277 // Set objective for the first-phase Simplex.
278 // Z = -1 * sum_of_artificial_vars
279 for (int j = firstArtificial; j < columns - 1; ++j)
280 setValueAt(0, j, 1.0);
281
282 // Maximize our objective (artificial vars go to zero)
283 solveMaxHelper();
284
285 // If there is a solution where the sum of all artificial
286 // variables is zero, then all of them can be removed and yet
287 // we will have a feasible (but not optimal) solution for the
288 // original problem.
289 // Otherwise, we clean up our structures and report there is
290 // no feasible solution.
291 if ((valueAt(0, columns - 1) != 0.0) && (qAbs(valueAt(0, columns - 1)) > 0.00001)) {
292 qWarning() << "QSimplex: No feasible solution!";
293 clearDataStructures();
294 return false;
295 }
296
297 // Remove artificial variables. We already have a feasible
298 // solution for the first problem, thus we don't need them
299 // anymore.
300 clearColumns(firstArtificial, columns - 2);
301
302 return true;
303}
304
305/*!
306 \internal
307
308 Run simplex on the current matrix with the current objective.
309
310 This is the iterative method. The matrix lines are combined
311 as to modify the variable values towards the best solution possible.
312 The method returns when the matrix is in the optimal state.
313*/
314void QSimplex::solveMaxHelper()
315{
316 reducedRowEchelon();
317 while (iterate()) ;
318}
319
320/*!
321 \internal
322*/
323void QSimplex::setObjective(QSimplexConstraint *newObjective)
324{
325 objective = newObjective;
326}
327
328/*!
329 \internal
330*/
331void QSimplex::clearRow(int rowIndex)
332{
333 qreal *item = matrix + rowIndex * columns;
334 for (int i = 0; i < columns; ++i)
335 item[i] = 0.0;
336}
337
338/*!
339 \internal
340*/
341void QSimplex::clearColumns(int first, int last)
342{
343 for (int i = 0; i < rows; ++i) {
344 qreal *row = matrix + i * columns;
345 for (int j = first; j <= last; ++j)
346 row[j] = 0.0;
347 }
348}
349
350/*!
351 \internal
352*/
353void QSimplex::dumpMatrix()
354{
355 qDebug("---- Simplex Matrix ----\n");
356
357 QString str(QLatin1String(" "));
358 for (int j = 0; j < columns; ++j)
359 str += QString::fromAscii(" <%1 >").arg(j, 2);
360 qDebug("%s", qPrintable(str));
361 for (int i = 0; i < rows; ++i) {
362 str = QString::fromAscii("Row %1:").arg(i, 2);
363
364 qreal *row = matrix + i * columns;
365 for (int j = 0; j < columns; ++j)
366 str += QString::fromAscii("%1").arg(row[j], 7, 'f', 2);
367 qDebug("%s", qPrintable(str));
368 }
369 qDebug("------------------------\n");
370}
371
372/*!
373 \internal
374*/
375void QSimplex::combineRows(int toIndex, int fromIndex, qreal factor)
376{
377 if (!factor)
378 return;
379
380 qreal *from = matrix + fromIndex * columns;
381 qreal *to = matrix + toIndex * columns;
382
383 for (int j = 1; j < columns; ++j) {
384 qreal value = from[j];
385
386 // skip to[j] = to[j] + factor*0.0
387 if (value == 0.0)
388 continue;
389
390 to[j] += factor * value;
391
392 // ### Avoid Numerical errors
393 if (qAbs(to[j]) < 0.0000000001)
394 to[j] = 0.0;
395 }
396}
397
398/*!
399 \internal
400*/
401int QSimplex::findPivotColumn()
402{
403 qreal min = 0;
404 int minIndex = -1;
405
406 for (int j = 0; j < columns-1; ++j) {
407 if (valueAt(0, j) < min) {
408 min = valueAt(0, j);
409 minIndex = j;
410 }
411 }
412
413 return minIndex;
414}
415
416/*!
417 \internal
418
419 For a given pivot column, find the pivot row. That is, the row with the
420 minimum associated "quotient" where:
421
422 - quotient is the division of the value in the last column by the value
423 in the pivot column.
424 - rows with value less or equal to zero are ignored
425 - if two rows have the same quotient, lines are chosen based on the
426 highest variable index (value in the first column)
427
428 The last condition avoids a bug where artificial variables would be
429 left behind for the second-phase simplex, and with 'good'
430 constraints would be removed before it, what would lead to incorrect
431 results.
432*/
433int QSimplex::pivotRowForColumn(int column)
434{
435 qreal min = qreal(999999999999.0); // ###
436 int minIndex = -1;
437
438 for (int i = 1; i < rows; ++i) {
439 qreal divisor = valueAt(i, column);
440 if (divisor <= 0)
441 continue;
442
443 qreal quotient = valueAt(i, columns - 1) / divisor;
444 if (quotient < min) {
445 min = quotient;
446 minIndex = i;
447 } else if ((quotient == min) && (valueAt(i, 0) > valueAt(minIndex, 0))) {
448 minIndex = i;
449 }
450 }
451
452 return minIndex;
453}
454
455/*!
456 \internal
457*/
458void QSimplex::reducedRowEchelon()
459{
460 for (int i = 1; i < rows; ++i) {
461 int factorInObjectiveRow = valueAt(i, 0);
462 combineRows(0, i, -1 * valueAt(0, factorInObjectiveRow));
463 }
464}
465
466/*!
467 \internal
468
469 Does one iteration towards a better solution for the problem.
470 See 'solveMaxHelper'.
471*/
472bool QSimplex::iterate()
473{
474 // Find Pivot column
475 int pivotColumn = findPivotColumn();
476 if (pivotColumn == -1)
477 return false;
478
479 // Find Pivot row for column
480 int pivotRow = pivotRowForColumn(pivotColumn);
481 if (pivotRow == -1) {
482 qWarning() << "QSimplex: Unbounded problem!";
483 return false;
484 }
485
486 // Normalize Pivot Row
487 qreal pivot = valueAt(pivotRow, pivotColumn);
488 if (pivot != 1.0)
489 combineRows(pivotRow, pivotRow, (1.0 - pivot) / pivot);
490
491 // Update other rows
492 for (int row=0; row < rows; ++row) {
493 if (row == pivotRow)
494 continue;
495
496 combineRows(row, pivotRow, -1 * valueAt(row, pivotColumn));
497 }
498
499 // Update first column
500 setValueAt(pivotRow, 0, pivotColumn);
501
502 // dumpMatrix();
503 // qDebug("------------ end of iteration --------------\n");
504 return true;
505}
506
507/*!
508 \internal
509
510 Both solveMin and solveMax are interfaces to this method.
511
512 The enum solverFactor admits 2 values: Minimum (-1) and Maximum (+1).
513
514 This method sets the original objective and runs the second phase
515 Simplex to obtain the optimal solution for the problem. As the internal
516 simplex solver is only able to _maximize_ objectives, we handle the
517 minimization case by inverting the original objective and then
518 maximizing it.
519*/
520qreal QSimplex::solver(solverFactor factor)
521{
522 // Remove old objective
523 clearRow(0);
524
525 // Set new objective in the first row of the simplex matrix
526 qreal resultOffset = 0;
527 QHash<QSimplexVariable *, qreal>::const_iterator iter;
528 for (iter = objective->variables.constBegin();
529 iter != objective->variables.constEnd();
530 ++iter) {
531
532 // Check if the variable was removed in the simplification process.
533 // If so, we save its offset to the objective function and skip adding
534 // it to the matrix.
535 if (iter.key()->index == -1) {
536 resultOffset += iter.value() * iter.key()->result;
537 continue;
538 }
539
540 setValueAt(0, iter.key()->index, -1 * factor * iter.value());
541 }
542
543 solveMaxHelper();
544 collectResults();
545
546#ifdef QT_DEBUG
547 for (int i = 0; i < constraints.size(); ++i) {
548 Q_ASSERT(constraints[i]->isSatisfied());
549 }
550#endif
551
552 // Return the value calculated by the simplex plus the value of the
553 // fixed variables.
554 return (factor * valueAt(0, columns - 1)) + resultOffset;
555}
556
557/*!
558 \internal
559 Minimize the original objective.
560*/
561qreal QSimplex::solveMin()
562{
563 return solver(Minimum);
564}
565
566/*!
567 \internal
568 Maximize the original objective.
569*/
570qreal QSimplex::solveMax()
571{
572 return solver(Maximum);
573}
574
575/*!
576 \internal
577
578 Reads results from the simplified matrix and saves them in the
579 "result" member of each QSimplexVariable.
580*/
581void QSimplex::collectResults()
582{
583 // All variables are zero unless overridden below.
584
585 // ### Is this really needed? Is there any chance that an
586 // important variable remains as non-basic at the end of simplex?
587 for (int i = 0; i < variables.size(); ++i)
588 variables[i]->result = 0;
589
590 // Basic variables
591 // Update the variable indicated in the first column with the value
592 // in the last column.
593 for (int i = 1; i < rows; ++i) {
594 int index = valueAt(i, 0) - 1;
595 if (index < variables.size())
596 variables[index]->result = valueAt(i, columns - 1);
597 }
598}
599
600/*!
601 \internal
602
603 Looks for single-valued variables and remove them from the constraints list.
604*/
605bool QSimplex::simplifyConstraints(QList<QSimplexConstraint *> *constraints)
606{
607 QHash<QSimplexVariable *, qreal> results; // List of single-valued variables
608 bool modified = true; // Any chance more optimization exists?
609
610 while (modified) {
611 modified = false;
612
613 // For all constraints
614 QList<QSimplexConstraint *>::iterator iter = constraints->begin();
615 while (iter != constraints->end()) {
616 QSimplexConstraint *c = *iter;
617 if ((c->ratio == QSimplexConstraint::Equal) && (c->variables.count() == 1)) {
618 // Check whether this is a constraint of type Var == K
619 // If so, save its value to "results".
620 QSimplexVariable *variable = c->variables.constBegin().key();
621 qreal result = c->constant / c->variables.value(variable);
622
623 results.insert(variable, result);
624 variable->result = result;
625 variable->index = -1;
626 modified = true;
627
628 }
629
630 // Replace known values among their variables
631 QHash<QSimplexVariable *, qreal>::const_iterator r;
632 for (r = results.constBegin(); r != results.constEnd(); ++r) {
633 if (c->variables.contains(r.key())) {
634 c->constant -= r.value() * c->variables.take(r.key());
635 modified = true;
636 }
637 }
638
639 // Keep it normalized
640 if (c->constant < 0)
641 c->invert();
642
643 if (c->variables.isEmpty()) {
644 // If constraint became empty due to substitution, delete it.
645 if (c->isSatisfied() == false)
646 // We must ensure that the constraint soon to be deleted would not
647 // make the problem unfeasible if left behind. If that's the case,
648 // we return false so the simplex solver can properly report that.
649 return false;
650
651 delete c;
652 iter = constraints->erase(iter);
653 } else {
654 ++iter;
655 }
656 }
657 }
658
659 return true;
660}
661
662void QSimplexConstraint::invert()
663{
664 constant = -constant;
665 ratio = Ratio(2 - ratio);
666
667 QHash<QSimplexVariable *, qreal>::iterator iter;
668 for (iter = variables.begin(); iter != variables.end(); ++iter) {
669 iter.value() = -iter.value();
670 }
671}
672
673QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.