19 double* pivotColumnTmp;
23 void initializeDictionary(
const double* objective,
24 const double* restrictions);
26 void prettyPrint(
const char* what);
28 void pivot(
int j,
int i);
30 bool isSolutionFeasible();
32 int basicVarIndex(
int variable);
34 int nonbasicVarIndex(
int variable);
36 void emitSolution(
double* solution);
38 int findPivotColumn();
40 int findPivotRow(
int pivotColumn);
42 int findPivotColumnBland();
44 int findPivotRowBland(
int pivotColumn);
51 int LinearProblem::basicVarIndex(
int variable)
53 for (
int k = 0; k < basicVars; k++) {
54 if (basicVar[k] == variable) {
61 int LinearProblem::nonbasicVarIndex(
int variable)
63 for (
int l = 0; l < nonbasicVars; l++) {
64 if (nonbasicVar[l] == variable) {
71 void LinearProblem::initializeDictionary(
const double* objective,
72 const double* restrictions)
74 for (
int j = 0; j < basicVars; j++) {
75 dictionary[j*stride + 0] = restrictions[j*(nonbasicVars + 1) + nonbasicVars];
76 for (
int i = 0; i < nonbasicVars; i++) {
77 dictionary[j*stride + i + 1] = -restrictions[j*(nonbasicVars + 1) + i];
80 dictionary[basicVars*stride + 0] = 0.0;
81 for (
int i = 0; i < nonbasicVars; i++) {
82 dictionary[basicVars*stride + i + 1] = objective[i];
85 for (
int i = 0; i < nonbasicVars; i++) {
86 nonbasicVar[i] = i + 1;
88 for (
int i = 0; i < basicVars; i++) {
89 basicVar[i] = nonbasicVars + i + 1;
93 void LinearProblem::prettyPrint(
const char* what)
95 printf(
"%s:\n", what);
97 std::vector<std::pair<int, int>> colPerm(nonbasicVars + 1);
98 colPerm[0] = std::make_pair(0, 0);
99 for (
int i = 0; i < nonbasicVars; i++) {
100 colPerm[i + 1] = std::make_pair(nonbasicVar[i], i + 1);
102 std::sort(colPerm.begin() + 1, colPerm.end(), [](
auto & a,
auto & b) ->
bool {return a.first < b.first; });
104 std::vector<std::pair<int, int>> rowPerm(basicVars);
105 for (
int i = 0; i < basicVars; i++) {
106 rowPerm[i] = std::make_pair(basicVar[i], i);
108 std::sort(rowPerm.begin(), rowPerm.end(), [](
auto & a,
auto & b) ->
bool {return a.first < b.first; });
109 rowPerm.push_back(std::make_pair(0, basicVars));
112 for (
int i = 0; i < nonbasicVars; i++) {
113 int l = colPerm[i + 1].second - 1;
114 printf(
" x%d ", nonbasicVar[l]);
117 for (
int j = 0; j <= basicVars; j++) {
118 int k = rowPerm[j].second;
120 printf(
"x%d = ", basicVar[k]);
125 for (
int i = 0; i < nonbasicVars + 1; i++) {
126 int l = colPerm[i].second;
127 printf(
"%5.2f ", dictionary[k*stride + l]);
133 void LinearProblem::pivot(
int j,
int i)
143 for (
int k = 0; k <= basicVars; k++) {
144 pivotColumnTmp[k] = dictionary[k*stride + i];
146 dictionary[k*stride + i] = 0.0;
152 for (
int k = 0; k <= basicVars; k++) {
153 if (k == j)
continue;
154 for (
int l = 0; l < nonbasicVars + 1; l++) {
155 if (l == i)
continue;
156 dictionary[k*stride + l] -= (pivotColumnTmp[k] * dictionary[j*stride + l]) / pivotColumnTmp[j];
160 dictionary[k*stride + i] += pivotColumnTmp[k] / pivotColumnTmp[j];
162 for (
int l = 0; l < nonbasicVars + 1; l++) {
163 if (l == i)
continue;
165 dictionary[j*stride + l] = -dictionary[j*stride + l] / pivotColumnTmp[j];
168 dictionary[j*stride + i] = 1.0 / pivotColumnTmp[j];
171 std::swap(basicVar[j], nonbasicVar[i - 1]);
174 bool LinearProblem::isSolutionFeasible()
176 for (
int k = 0; k < basicVars; k++) {
177 if (dictionary[k*stride] < 0.0) {
178 if (printSteps) prettyPrint(
"Solution is not feasible");
185 int LinearProblem::findPivotColumn()
190 double best_val = std::numeric_limits<double>::epsilon();
191 for (
int k = 0; k < nonbasicVars; k++) {
194 auto weightDenominator = 1.0;
196 bool hasNegativeCoefficient =
false;
197 for (
int j = 0; j < basicVars; j++) {
198 const auto nonBasicCoefficent = dictionary[j*stride + k + 1];
199 weightDenominator += nonBasicCoefficent*nonBasicCoefficent;
200 hasNegativeCoefficient = hasNegativeCoefficient || (nonBasicCoefficent < -std::numeric_limits<float>::epsilon());
203 if (hasNegativeCoefficient) {
204 auto val = dictionary[basicVars*stride + k + 1] / weightDenominator;
205 if (best_val < val) {
217 int LinearProblem::findPivotRow(
int pivotColumn)
220 double worstRatio = std::numeric_limits<double>::max();
222 for (
int k = 0; k < basicVars; k++) {
224 const auto basicVal = dictionary[k*stride + 0];
225 if (basicVal < -std::numeric_limits<double>::epsilon()) {
230 const auto nonBasicVal = dictionary[k*stride + pivotColumn];
231 if (nonBasicVal < -std::numeric_limits<double>::epsilon()) {
232 const auto ratio = -basicVal / nonBasicVal;
233 if (ratio < worstRatio) {
243 int LinearProblem::findPivotColumnBland()
246 int lowest = std::numeric_limits<int>::max();
247 for (
int l = 0; l < nonbasicVars; l++) {
249 bool hasNegativeCoefficient =
false;
250 for (
int j = 0; (j < basicVars) && !hasNegativeCoefficient; j++) {
251 hasNegativeCoefficient = dictionary[j*stride + l + 1] < -std::numeric_limits<float>::epsilon();
254 if (hasNegativeCoefficient) {
255 auto i = nonbasicVar[l];
256 auto v = dictionary[basicVars*stride + l + 1];
257 if ((0.0 < v) && (i < lowest)) {
266 int LinearProblem::findPivotRowBland(
int pivotColumn)
269 int lowest = std::numeric_limits<int>::max();
270 double worstRatio = std::numeric_limits<double>::max();
272 for (
int k = 0; k < basicVars; k++) {
273 auto j = basicVar[k];
275 auto r = dictionary[k*stride + pivotColumn];
276 if (r < -std::numeric_limits<double>::epsilon()) {
277 auto v = dictionary[k*stride + 0];
280 if ((ratio < worstRatio) || ((ratio == worstRatio) && (j < lowest))) {
291 void LinearProblem::emitSolution(
double* solution)
293 for (
int i = 0; i < basicVars; i++) {
294 auto k = basicVar[i] - 1;
295 if (k < nonbasicVars) {
296 solution[k] = dictionary[i*stride + 0];
299 for (
int i = 0; i < nonbasicVars; i++) {
300 auto k = nonbasicVar[i] - 1;
301 if (k < nonbasicVars) {
309 if(printSteps) prettyPrint(
"Started search");
310 for (
int it = 0; it < maxIterations; it++) {
311 int pivotColumn = findPivotColumn();
312 if (pivotColumn < 0) {
316 int pivotRow = findPivotRow(pivotColumn);
318 if (printSteps) printf(
"We're stalling, trying Bland's rule.\n");
321 pivotColumn = findPivotColumnBland();
322 if (pivotColumn < 0) {
323 if (printSteps) printf(
"Failed to find column.\n");
326 pivotRow = findPivotRowBland(pivotColumn);
328 if (printSteps) printf(
"Failed to find row.\n");
333 pivot(pivotRow, pivotColumn);
334 if (printSteps) prettyPrint(
"Applied pivot");
340 const int maxIterations)
343 if (isSolutionFeasible()) {
344 if (printSteps) prettyPrint(
"Initial solution is feasible");
347 if (printSteps) prettyPrint(
"Initial solution is not feasible");
350 for (
int l = 0; l < nonbasicVars; l++) {
351 objectiveTmp[l] = dictionary[basicVars*stride + l + 1];
355 nonbasicVar[nonbasicVars] = 0;
357 for (
int k = 0; k < basicVars; k++) {
358 dictionary[k*stride + nonbasicVars] = 1.0;
360 dictionary[basicVars*stride + nonbasicVars] = -1.0;
361 for (
int l = 0; l < nonbasicVars; l++) {
362 dictionary[basicVars*stride + l] = 0.0;
365 if(printSteps) prettyPrint(
"Extended problem to find feasible solution");
369 double mostInfeasibleValue = std::numeric_limits<double>::max();
370 for (
int k = 0; k < basicVars; k++) {
371 auto value = dictionary[k*stride + 0];
372 if (value < mostInfeasibleValue) {
374 mostInfeasibleValue = value;
378 pivot(leaving, nonbasicVars);
379 if(printSteps) prettyPrint(
"Applied pivot");
381 auto termination = search(maxIterations);
386 if (!isSolutionFeasible()) {
390 int x0_index = nonbasicVarIndex(0);
394 if (printSteps) prettyPrint(
"x0=%d is in basis while we reached an optimum, infeasible solution?");
400 for (
int l = x0_index; l < nonbasicVars; l++) {
401 nonbasicVar[l] = nonbasicVar[l + 1];
403 for (
int k = 0; k < basicVars; k++) {
404 for (
int l = x0_index; l < nonbasicVars; l++) {
405 dictionary[k*stride + l + 1] = dictionary[k*stride + l + 2];
408 assert(nonbasicVarIndex(0) == -1);
409 assert(basicVarIndex(0) == -1);
412 dictionary[basicVars*stride + 0] = 0.0;
413 for (
int l = 0; l < nonbasicVars; l++) {
414 auto i = nonbasicVar[l] - 1;
415 dictionary[basicVars*stride + l + 1] = i < nonbasicVars ? objectiveTmp[i] : 0.0;
418 for (
int k = 0; k < basicVars; k++) {
419 auto j = basicVar[k] - 1;
420 if (j < nonbasicVars) {
421 auto s = objectiveTmp[j];
422 for (
int l = 0; l <= nonbasicVars; l++) {
423 dictionary[basicVars*stride + l] += s*dictionary[k*stride + l];
430 auto termination = search(maxIterations);
432 emitSolution(solution);
442 const size_t restrictionCount)
445 sizeof(int)*(restrictionCount) +
446 sizeof(int)*(variableCount + 1) +
447 sizeof(double)*(variableCount + 2)*(restrictionCount + 1) +
448 sizeof(
double)*(restrictionCount + 1) +
449 sizeof(
double)*(variableCount) + 8;
455 const int variableCount,
456 const double* objectives,
457 const int objectiveCount,
458 const double* restrictions,
459 const int restrictionCount,
460 const int maxIterationCount,
461 const bool printSteps)
469 LinearProblem problem;
470 problem.nonbasicVars = variableCount;
471 problem.basicVars = restrictionCount;
472 problem.stride = variableCount + 2;
475 scratch = (scratch + 7u) & ~uintptr_t(7);
476 problem.dictionary =
reinterpret_cast<double*
>(scratch); scratch +=
sizeof(double)*(variableCount + 2)*(restrictionCount + 1);
477 problem.pivotColumnTmp =
reinterpret_cast<double*
>(scratch); scratch +=
sizeof(double)*(restrictionCount + 1);
478 problem.objectiveTmp =
reinterpret_cast<double*
>(scratch); scratch +=
sizeof(double)*(variableCount);
479 problem.basicVar =
reinterpret_cast<int*
>(scratch); scratch +=
sizeof(int)*(restrictionCount);
480 problem.nonbasicVar =
reinterpret_cast<int*
>(scratch); scratch +=
sizeof(int)*(variableCount + 1);
481 problem.printSteps = printSteps;
486 for (
int i = 0; i < objectiveCount && (rv == SimplexResult::OptimalSolutionFound); i++) {
487 problem.initializeDictionary(objectives + i*variableCount,
489 rv = problem.maximize(solutions + i*variableCount,
A Context instance contains all the services, systems and runtime components needed to use Cogs.
COGSCORE_DLL_API SimplexResult simplexMaximize(Context *context, uintptr_t scratch, double *solutions, const int variableCount, const double *objectives, const int objectiveCount, const double *restrictions, const int restrictionCount, const int maxIterationCount, const bool printSteps=false)
Find the maximum of a linear programming problem using the simplex method.
@ OptimalSolutionFound
Success, optimal solution was found and written to solution.
@ NoFeasibleSolutions
Success, determined that no feasible solutions exist.
@ TooManyIterations
Failure, ran more iterations than allowed.
@ BlandsFailed
Failure while applying Bland's rule.
COGSCORE_DLL_API size_t simplexScratchSize(const size_t variableCount, const size_t objectiveCount, const size_t restrictionCount)
Number of bytes of scratch array required by simplexMaximize.