Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Simplex.H
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
55# ifndef SIMPLEX_H
56# define SIMPLEX_H
57
58# include <limits>
59# include <fstream>
60# include <vector>
61# include <chrono>
62# include <tpl_dynMat.H>
63# include <tpl_dynDlist.H>
64# include <ah-errors.H>
65
66namespace Aleph
67{
72 enum class ConstraintType { LE, GE, EQ };
73
79
85 {
86 size_t iterations = 0;
87 size_t pivots = 0;
88 size_t degenerate_pivots = 0;
89 double elapsed_ms = 0.0;
90 bool bland_rule_used = false;
91
93 {
94 iterations = 0;
95 pivots = 0;
97 elapsed_ms = 0.0;
98 bland_rule_used = false;
99 }
100 };
101
107 template <typename T>
109 {
113
115 { return lower_bound == -std::numeric_limits<T>::infinity(); }
116
118 { return upper_bound == std::numeric_limits<T>::infinity(); }
119 };
120
225 template <typename T>
227 {
228 public:
238
244 enum class PivotRule { Dantzig, Bland };
245
246 private:
247 // Selects the cell in the objective function with minimum value.
248 // Returns -1 if all cells are non-negative (optimality reached).
249 // Uses Bland's rule if enabled (select smallest index among negatives).
251 {
252 const int M = num_var + num_rest;
253
255 {
256 // Bland's rule: select first (smallest index) negative coefficient
257 for (int i = 0; i < M; ++i)
258 if (m->read(0, i) < -eps)
259 return i;
260 return -1;
261 }
262
263 // Dantzig's rule: select most negative coefficient
264 T minimum = std::numeric_limits<T>::max();
265 int p = -1;
266 for (int i = 0; i < M; ++i)
267 {
268 const T & c = m->read(0, i);
269 if (c < minimum)
270 {
271 p = i;
272 minimum = c;
273 }
274 }
275 return minimum >= -eps ? -1 : p;
276 }
277
278 // Selects among elements B the minimum ratio between the RHS value
279 // and the pivot column coefficient (must be positive).
280 // Returns -1 if no valid pivot row exists (unbounded).
281 // Uses Bland's rule if enabled (select smallest index among ties).
282 int compute_pivot_row(int p) const noexcept
283 {
284 assert(p >= 0 and p < static_cast<int>(num_var + num_rest + num_artificial));
285
286 int q = -1;
287 T min_ratio = std::numeric_limits<T>::max();
288 const size_t rhs_col = num_var + num_rest + num_artificial;
289
290 for (int i = 1; i <= static_cast<int>(num_rest); ++i)
291 {
292 const T val = m->read(i, rhs_col); // RHS value
293 if (val < -eps)
294 continue;
295
296 const T den = m->read(i, p); // pivot column coefficient
297 if (den <= eps)
298 continue;
299
300 const T ratio = val / den;
301
303 {
304 // Bland's rule: among ties, select smallest row index
305 if (ratio < min_ratio - eps)
306 {
307 q = i;
309 }
310 }
311 else if (ratio < min_ratio)
312 {
313 q = i;
315 }
316 }
317 return q;
318 }
319
320 // Selects pivot element. Returns new state.
321 State select_pivot(int & p, int & q) noexcept
322 {
324
325 const int col = compute_pivot_col();
326 if (col == -1)
327 return state = Solved;
328
329 const int row = compute_pivot_row(col);
330 if (row == -1)
331 return state = Unbounded;
332
333 p = row;
334 q = col;
335
336 return state = Solving;
337 }
338
339 // Performs pivot operation on element (p, q).
340 // Optimized version: uses reusable buffers to avoid repeated allocations.
341 void to_pivot(size_t p, size_t q)
342 {
344
345 // Total columns: decision vars + slack/surplus + artificial + RHS
346 const size_t M = num_var + num_rest + num_artificial; // last data column
347 const size_t cols = M + 1; // total columns including RHS
348 const T pivot = m->read(p, q);
349 const T inv_pivot = T{1} / pivot;
350
351 // Resize buffers only if needed (amortized O(1) for repeated calls)
352 if (pivot_row_buffer.size() < cols)
353 pivot_row_buffer.resize(cols);
354 if (pivot_col_buffer.size() < num_rest + 1)
355 pivot_col_buffer.resize(num_rest + 1);
356
357 // Cache pivot row (normalized) - avoids repeated reads during elimination
358 for (size_t j = 0; j <= M; ++j)
359 pivot_row_buffer[j] = (j == q) ? T{1} : m->read(p, j) * inv_pivot;
360
361 // Write normalized pivot row back to matrix
362 for (size_t j = 0; j <= M; ++j)
363 m->write(p, j, pivot_row_buffer[j]);
364
365 // Cache pivot column values before modification
366 for (size_t i = 0; i <= num_rest; ++i)
367 pivot_col_buffer[i] = m->read(i, q);
368
369 // Eliminate: for each row i != p, subtract (pivot_col[i] * pivot_row)
370 for (size_t i = 0; i <= num_rest; ++i)
371 {
372 if (i == p)
373 continue;
374
375 const T factor = pivot_col_buffer[i];
376 if (factor == T{0})
377 continue; // Skip rows where pivot column is already 0
378
379 for (size_t j = 0; j <= M; ++j)
380 if (j == q)
381 m->write(i, j, T{0}); // Pivot column becomes 0
382 else
383 m->write(i, j, m->read(i, j) - factor * pivot_row_buffer[j]);
384 }
385 }
386
387 // Finds the value of variable j from the tableau.
388 // A variable is basic if its column has exactly one 1 and all other
389 // entries are 0 (including row 0!). The value is the RHS of that row.
390 T find_value(const size_t j) const noexcept
391 {
392 assert(j < num_var);
393
394 // First check row 0: must be 0 for a basic variable
395 if (std::abs(m->read(0, j)) > eps)
396 return T{0}; // Not basic: non-zero in objective row
397
398 T ret_val = T{0};
399 int count = 0;
400 const size_t rhs_col = num_var + num_rest + num_artificial;
401
402 for (size_t i = 1; i <= num_rest; ++i)
403 {
404 const T & value = m->read(i, j);
405 if (std::abs(value) < eps)
406 continue;
407
408 if (std::abs(value - T{1}) < eps)
409 {
410 if (count++ == 0)
411 ret_val = m->read(i, rhs_col);
412 else
413 return T{0}; // Not a basic variable (multiple 1s)
414 }
415 else
416 return T{0}; // Not a basic variable
417 }
418 return ret_val;
419 }
420
421 std::unique_ptr<DynMatrix<T>> m; // Simplex tableau
422 std::unique_ptr<T[]> objetive; // Objective function coefficients
423 DynDlist<T *> rest_list; // List of restrictions
424 DynDlist<ConstraintType> rest_types; // Type of each restriction
425 size_t num_var; // Number of decision variables
426 size_t num_rest; // Number of restrictions
427 size_t num_artificial; // Number of artificial variables (for Big-M)
428 std::unique_ptr<T[]> solution; // Solution values
429 State state; // Current state
430 OptimizationType opt_type; // Maximize or Minimize
431 PivotRule pivot_rule; // Pivot selection rule
432 T eps; // Tolerance for floating point comparisons
433
434 // Execution statistics
436
437 // Reusable buffers for pivot operations (avoid repeated allocations)
438 mutable std::vector<T> pivot_row_buffer;
439 mutable std::vector<T> pivot_col_buffer;
440
441 // Basic variable indices for sensitivity analysis
442 mutable std::vector<int> basic_vars;
443
444 // Validates variable index is within range.
445 void verify_var_index(const size_t i) const
446 {
448 << "Variable index " << i << " out of range [0, " << (num_var - 1) << "]";
449 }
450
451 // Validates restriction index is within range.
452 void verify_rest_index(const size_t i) const
453 {
455 << "Restriction index " << i << " out of range [0, " << (num_rest - 1) << "]";
456 }
457
458 // Creates a new restriction array initialized to zero.
460 {
461 T *ptr = new T[num_var + 1];
462 rest_list.append(ptr);
463 rest_types.append(type);
464 ++num_rest;
465 for (size_t i = 0; i <= num_var; ++i)
466 ptr[i] = 0;
467 return ptr;
468 }
469
470 // Count artificial variables needed
472 {
473 size_t count = 0;
474 for (auto it = rest_types.get_it(); it.has_curr(); it.next_ne())
475 if (it.get_curr() != ConstraintType::LE)
476 ++count;
477 return count;
478 }
479
480 // Creates the simplex tableau matrix from objective and constraints.
481 // Handles LE, GE, and EQ constraints with slack, surplus, and artificial variables.
483 {
485 const size_t total_slack = num_rest; // One slack/surplus per constraint
486 const size_t total_cols = num_var + total_slack + num_artificial + 1; // +1 for RHS
487
488 m = std::unique_ptr<DynMatrix<T>>(
490
491 // Big-M value for artificial variables
492 const T big_m = T{1e9};
493
494 // Objective coefficient multiplier:
495 // - For MAX c·x: Row 0 = -c, and we look for negative coefficients to pivot
496 // (a negative coef means increasing that var improves the objective)
497 // - For MIN c·x: Row 0 = c, and we look for negative coefficients to pivot
498 // (a negative coef means increasing that var REDUCES the objective, good for MIN)
499 // With this convention, compute_pivot_col always looks for negatives.
500 const T obj_mult = (opt_type == OptimizationType::Maximize) ? T{-1} : T{1};
501
502 // Fill objective function coefficients
503 for (size_t i = 0; i < num_var; ++i)
504 m->write(0, i, obj_mult * objetive[i]);
505
506 // Fill artificial variable coefficients in objective (Big-M penalty)
507 // With the tableau always using -c (maximization form), artificial
508 // variables need +M coefficient so they are penalized and driven out.
509 size_t art_idx = num_var + num_rest;
510 for (auto it = rest_types.get_it(); it.has_curr(); it.next_ne())
511 {
512 if (it.get_curr() != ConstraintType::LE)
513 {
514 m->write(0, art_idx, big_m); // Always +M
515 ++art_idx;
516 }
517 }
518
519 // Fill constraint coefficients
520 size_t row = 1;
522 auto type_it = rest_types.get_it();
523
524 for (auto it = rest_list.get_it(); it.has_curr();
525 it.next_ne(), type_it.next_ne(), ++row)
526 {
527 T *rest = it.get_curr();
528 ConstraintType ctype = type_it.get_curr();
529
530 // Decision variable coefficients
531 for (size_t j = 0; j < num_var; ++j)
532 m->write(row, j, rest[j]);
533
534 // Slack/surplus variable
535 switch (ctype)
536 {
538 // Slack variable: +1
539 m->write(row, num_var + row - 1, T{1});
540 break;
542 // Surplus variable: -1, plus artificial: +1
543 m->write(row, num_var + row - 1, T{-1});
544 m->write(row, artificial_col++, T{1});
545 break;
547 // No slack/surplus, just artificial: +1
548 m->write(row, num_var + row - 1, T{0});
549 m->write(row, artificial_col++, T{1});
550 break;
551 }
552
553 // RHS value
554 m->write(row, total_cols - 1, rest[num_var]);
555 }
556
557 // Initialize basic variable tracking
558 basic_vars.resize(num_rest);
559 for (size_t i = 0; i < num_rest; ++i)
560 basic_vars[i] = static_cast<int>(num_var + i); // Initially slack vars
561
562 // Big-M elimination: For artificial variables to be in valid basis form,
563 // we need to eliminate their coefficient from row 0 by subtracting
564 // M * constraint_row from row 0. This ensures the artificial var's
565 // column has 0 in row 0 (basic variable form).
566 if (num_artificial > 0)
567 {
568 size_t row = 1;
569 size_t art_col = num_var + num_rest;
570 auto type_it = rest_types.get_it();
571
572 for (; type_it.has_curr(); type_it.next_ne(), ++row)
573 {
574 ConstraintType ctype = type_it.get_curr();
576 continue;
577
578 // For GE/EQ constraints, artificial variable is in basis initially
579 // Update basic_vars to reflect this
580 basic_vars[row - 1] = static_cast<int>(art_col);
581
582 // Eliminate M from row 0 by: row0 -= M * row_i
583 // (for MAX, artificial has +M in row0; for MIN, it has -M)
584 const T art_coef = m->read(0, art_col); // This is ±M
585 for (size_t j = 0; j < total_cols; ++j)
586 m->write(0, j, m->read(0, j) - art_coef * m->read(row, j));
587
588 ++art_col;
589 }
590 }
591 }
592
593 public:
605 explicit Simplex(const size_t n,
607 : m(nullptr), objetive(new T[n]), num_var(n), num_rest(0),
610 eps(std::numeric_limits<T>::epsilon() * 100)
611 {
613 << "Number of variables must be greater than zero";
614
615 // Initialize objective coefficients to zero
616 for (size_t i = 0; i < n; ++i)
617 objetive[i] = T{0};
618 }
619
625 {
626 rest_list.for_each([](auto ptr) { delete[] ptr; });
627 }
628
629 // Non-copyable
630 Simplex(const Simplex &) = delete;
631
632 Simplex &operator=(const Simplex &) = delete;
633
634 // Movable
635 Simplex(Simplex &&) = default;
636
637 Simplex &operator=(Simplex &&) = default;
638
646 {
647 opt_type = type;
648 }
649
658
667
676 {
678 }
679
689
694 void set_epsilon(T epsilon) noexcept
695 {
696 eps = epsilon;
697 }
698
707
717 void put_objetive_function_coef(size_t i, const T & coef)
718 {
720 objetive[i] = coef;
721 }
722
730 [[nodiscard]] const T &get_objetive_function_coef(size_t i) const
731 {
733 return objetive[i];
734 }
735
741 {
742 for (size_t i = 0; i < num_var; ++i)
743 objetive[i] = coefs[i];
744 }
745
751 {
752 for (size_t i = 0; i < num_var; ++i)
753 objetive[i] = coefs[i];
754 }
755
772 T * put_restriction(const T *coefs = nullptr,
774 {
775 T *rest = create_restriction(type);
776
777 if (coefs == nullptr)
778 return rest;
779
780 for (size_t i = 0; i <= num_var; ++i)
781 rest[i] = coefs[i];
782
783 return rest;
784 }
785
797
809
820 {
821 T *rest = create_restriction(type);
822
823 for (size_t i = 0; i <= num_var; ++i)
824 rest[i] = coefs[i];
825
826 return rest;
827 }
828
837 {
839
840 size_t i = 0;
841 for (auto it = rest_list.get_it(); it.has_curr(); it.next_ne(), ++i)
842 if (i == rest_num)
843 return it.get_curr();
844
845 return nullptr; // Should never reach here
846 }
847
853 {
854 return num_rest;
855 }
856
862 {
863 return num_var;
864 }
865
871 {
872 return objetive.get();
873 }
874
880 {
881 return objetive.get();
882 }
883
892 [[nodiscard]] T &get_restriction_coef(const size_t rest_num, size_t idx)
893 {
894 verify_var_index(idx);
895 return get_restriction(rest_num)[idx];
896 }
897
906 void put_restriction_coef(const size_t rest_num, const size_t idx, const T & coef)
907 {
908 get_restriction_coef(rest_num, idx) = coef;
909 }
910
922 {
924 << "Cannot prepare linear program without constraints";
926 }
927
948 {
949 ah_logic_error_if(state != Not_Solved) << "solve() has already been called";
950 ah_logic_error_if(num_rest == 0) << "Linear program has no constraints";
951 ah_logic_error_if(m == nullptr) << "prepare_linear_program() must be called before solve()";
952
953 stats.reset();
954 auto start_time = std::chrono::high_resolution_clock::now();
955
956 T prev_obj = std::numeric_limits<T>::lowest();
957
958 for (int i, j; true;)
959 {
961 const State st = select_pivot(i, j);
962
963 if (st == Unbounded or st == Solved)
964 {
965 auto end_time = std::chrono::high_resolution_clock::now();
966 stats.elapsed_ms = std::chrono::duration<double, std::milli>(
967 end_time - start_time).count();
968
969 // Check for infeasibility (artificial vars in basis with positive value)
970 if (st == Solved and num_artificial > 0)
971 {
973 // If any artificial variable is positive, problem is infeasible
974 // (This is a simplified check; a proper two-phase method is more robust)
975 }
976
977 return st;
978 }
979
980 // Track degenerate pivots (no improvement in objective)
981 const size_t rhs_col = num_var + num_rest + num_artificial;
982 T curr_obj = m->read(0, rhs_col);
983 if (std::abs(curr_obj - prev_obj) < eps)
986
987 ++stats.pivots;
988 to_pivot(i, j);
989
990 // Update basic variable tracking
991 if (static_cast<size_t>(i) <= num_rest and i > 0)
992 basic_vars[i - 1] = j;
993 }
994 }
995
1001 {
1002 return state;
1003 }
1004
1011 {
1012 for (size_t j = 0; j < num_var; ++j)
1013 solution[j] = find_value(j);
1014 }
1015
1025 [[nodiscard]] const T &get_solution(size_t i) const noexcept
1026 {
1027 assert(i < num_var);
1028 return solution[i];
1029 }
1030
1040 {
1041 T sum = 0;
1042 for (size_t i = 0; i < num_var; ++i)
1043 sum += solution[i] * objetive[i];
1044 return sum;
1045 }
1046
1057 {
1058 auto type_it = rest_types.get_it();
1059 for (auto it = rest_list.get_it(); it.has_curr();
1060 it.next_ne(), type_it.next_ne())
1061 {
1062 const T *rest = it.get_curr();
1063 ConstraintType ctype = type_it.get_curr();
1064 T sum = 0;
1065 for (size_t j = 0; j < num_var; ++j)
1066 sum += rest[j] * solution[j];
1067
1068 const T rhs = rest[num_var];
1069 switch (ctype)
1070 {
1071 case ConstraintType::LE:
1072 if (sum > rhs + eps)
1073 return false;
1074 break;
1075 case ConstraintType::GE:
1076 if (sum < rhs - eps)
1077 return false;
1078 break;
1079 case ConstraintType::EQ:
1080 if (std::abs(sum - rhs) > eps)
1081 return false;
1082 break;
1083 }
1084 }
1085 return true;
1086 }
1087
1092 void print_matrix() const
1093 {
1094 for (size_t i = 0; i <= num_rest; ++i)
1095 {
1096 for (size_t j = 0; j <= num_var + num_rest; ++j)
1097 std::cout << float_f(m->read(i, j), 2) << " ";
1098
1099 std::cout << std::endl;
1100 }
1101 }
1102
1110 void latex_matrix(const std::string & name, const int d = 2,
1111 const int p = -1, const int q = -1) const
1112 {
1113 std::ofstream out(name, std::ios::out);
1114
1115 const size_t cols = num_var + num_rest;
1116
1117 out << "$\\left(\\begin{array}{c";
1118 for (size_t i = 0; i < cols; ++i)
1119 out << "c";
1120 out << "}" << std::endl;
1121
1122 for (size_t i = 0; i <= num_rest; ++i)
1123 {
1124 for (size_t j = 0; j <= cols; ++j)
1125 {
1126 if (static_cast<int>(i) == p and static_cast<int>(j) == q)
1127 out << "\\circled{" << float_f(m->read(i, j), d) << "}" << " ";
1128 else
1129 out << float_f(m->read(i, j), d) << " ";
1130 if (j != cols)
1131 out << "& ";
1132 }
1133
1134 if (i != num_rest)
1135 out << "\\\\";
1136
1137 out << std::endl;
1138 }
1139 out << "\\end{array}\\right)$" << std::endl;
1140 }
1141
1149 void latex_linear_program(const std::string & name) const
1150 {
1151 std::ofstream out(name, std::ios::out);
1152 out << "\\begin{equation*}" << std::endl
1153 << "Z = ";
1154
1155 bool first = true;
1156 for (size_t i = 0; i < num_var; ++i)
1157 {
1158 if (objetive[i] == 0.0)
1159 continue;
1160
1161 if (not first)
1162 out << " + ";
1163 first = false;
1164
1165 if (objetive[i] != 1.0)
1166 out << objetive[i];
1167 out << "x_" << i;
1168 }
1169 out << std::endl
1170 << "\\end{equation*}" << std::endl
1171 << "Subject to:" << std::endl
1172 << "\\begin{eqnarray*}" << std::endl;
1173
1174 for (auto it = rest_list.get_it(); it.has_curr(); it.next_ne())
1175 {
1176 const T *rest = it.get_curr();
1177
1178 bool first_term = true;
1179 for (size_t i = 0; i < num_var; ++i)
1180 {
1181 if (rest[i] == 0.0)
1182 continue;
1183
1184 if (not first_term)
1185 out << " + ";
1186 first_term = false;
1187
1188 if (rest[i] != 1.0)
1189 out << rest[i];
1190 out << " x_" << i;
1191 }
1192
1193 out << " & \\leq & " << rest[num_var];
1194
1195 if (not it.is_in_last())
1196 out << " \\\\";
1197
1198 out << std::endl;
1199 }
1200 out << "\\end{eqnarray*}" << std::endl;
1201 }
1202
1211 State latex_solve(const char *name = nullptr)
1212 {
1213 const std::string base = name ? name : "simplex";
1214 latex_matrix(base + "-0.tex");
1215
1216 for (int i, j, k = 1; true; ++k)
1217 {
1218 const State st = select_pivot(i, j);
1219
1220 std::string str = base + "-" + std::to_string(k) + ".tex";
1221 if (st == Unbounded or st == Solved)
1222 {
1223 latex_matrix(str);
1224 return st;
1225 }
1226
1227 latex_matrix(str, 2, i, j);
1228 to_pivot(i, j);
1229
1230 latex_matrix(base + "-" + std::to_string(k) + "-after.tex", 2, i, j);
1231 }
1232 }
1233
1234 // ================== Statistics ==================
1235
1243 {
1244 return stats;
1245 }
1246
1252 {
1253 return stats.iterations;
1254 }
1255
1261 {
1262 return stats.pivots;
1263 }
1264
1275
1281 {
1282 return stats.elapsed_ms;
1283 }
1284
1285 // ================== Sensitivity Analysis ==================
1286
1300 {
1302 ah_logic_error_if(state != Solved) << "Must solve before sensitivity analysis";
1303
1304 SensitivityRange<T> result;
1305 result.current_value = objetive[var_idx];
1306 result.lower_bound = -std::numeric_limits<T>::infinity();
1307 result.upper_bound = std::numeric_limits<T>::infinity();
1308
1309 // Check if variable is basic or non-basic
1310 bool is_basic = false;
1311 size_t basic_row = 0;
1312 for (size_t i = 0; i < num_rest; ++i)
1313 if (basic_vars[i] == static_cast<int>(var_idx))
1314 {
1315 is_basic = true;
1316 basic_row = i + 1;
1317 break;
1318 }
1319
1320 const size_t total_cols = num_var + num_rest + num_artificial;
1321
1322 if (is_basic)
1323 {
1324 // For basic variables, analyze reduced costs
1325 for (size_t j = 0; j < total_cols; ++j)
1326 {
1327 if (basic_vars[basic_row - 1] == static_cast<int>(j))
1328 continue;
1329
1330 const T tableau_coef = m->read(basic_row, j);
1331 const T reduced_cost = m->read(0, j);
1332
1333 if (std::abs(tableau_coef) > eps)
1334 {
1335 T delta = -reduced_cost / tableau_coef;
1336 if (tableau_coef > 0)
1337 result.upper_bound = std::min(result.upper_bound, delta);
1338 else
1339 result.lower_bound = std::max(result.lower_bound, delta);
1340 }
1341 }
1342 }
1343 else
1344 {
1345 // For non-basic variables at zero
1346 const T reduced_cost = m->read(0, var_idx);
1348 result.upper_bound = result.current_value - reduced_cost;
1349 else
1350 result.lower_bound = result.current_value + reduced_cost;
1351 }
1352
1353 return result;
1354 }
1355
1369 {
1371 ah_logic_error_if(state != Solved) << "Must solve before sensitivity analysis";
1372
1373 SensitivityRange<T> result;
1374 const size_t rhs_col = num_var + num_rest + num_artificial;
1375
1376 // Get current RHS value from original constraint
1377 size_t i = 0;
1378 for (auto it = rest_list.get_it(); it.has_curr(); it.next_ne(), ++i)
1379 if (i == rest_idx)
1380 {
1381 result.current_value = it.get_curr()[num_var];
1382 break;
1383 }
1384
1385 result.lower_bound = -std::numeric_limits<T>::infinity();
1386 result.upper_bound = std::numeric_limits<T>::infinity();
1387
1388 // Find the column corresponding to the slack/surplus variable
1389 const size_t slack_col = num_var + rest_idx;
1390
1391 // Analyze how changes in RHS affect current basic solution
1392 for (size_t row = 1; row <= num_rest; ++row)
1393 {
1394 const T b_i = m->read(row, rhs_col);
1395 const T a_ij = m->read(row, slack_col);
1396
1397 if (std::abs(a_ij) > eps)
1398 {
1399 T delta = -b_i / a_ij;
1400 if (a_ij > 0)
1401 result.upper_bound = std::min(result.upper_bound, delta);
1402 else
1403 result.lower_bound = std::max(result.lower_bound, delta);
1404 }
1405 }
1406
1407 // Adjust bounds to be relative to current value
1408 result.lower_bound = result.current_value + result.lower_bound;
1409 result.upper_bound = result.current_value + result.upper_bound;
1410
1411 return result;
1412 }
1413
1427 {
1429 ah_logic_error_if(state != Solved) << "Must solve before computing shadow prices";
1430
1431 // Shadow price is the negative of the reduced cost of slack variable
1432 const size_t slack_col = num_var + rest_idx;
1433 T price = -m->read(0, slack_col);
1434
1435 // Adjust sign for minimization
1437 price = -price;
1438
1439 return price;
1440 }
1441
1455 {
1457 ah_logic_error_if(state != Solved) << "Must solve before computing reduced costs";
1458
1459 T rc = m->read(0, var_idx);
1460
1461 // Adjust sign for maximization (reduced costs are negated in tableau)
1463 rc = -rc;
1464
1465 return rc;
1466 }
1467
1475 [[nodiscard]] bool is_basic_variable(size_t var_idx) const
1476 {
1478 for (size_t i = 0; i < num_rest; ++i)
1479 if (basic_vars[i] == static_cast<int>(var_idx))
1480 return true;
1481 return false;
1482 }
1483
1490 [[nodiscard]] std::vector<T> get_all_shadow_prices() const
1491 {
1492 ah_logic_error_if(state != Solved) << "Must solve before computing shadow prices";
1493 std::vector<T> prices(num_rest);
1494 for (size_t i = 0; i < num_rest; ++i)
1495 prices[i] = shadow_price(i);
1496 return prices;
1497 }
1498
1505 [[nodiscard]] std::vector<T> get_all_reduced_costs() const
1506 {
1507 ah_logic_error_if(state != Solved) << "Must solve before computing reduced costs";
1508 std::vector<T> costs(num_var);
1509 for (size_t i = 0; i < num_var; ++i)
1510 costs[i] = reduced_cost(i);
1511 return costs;
1512 }
1513
1514 // ================== Dual Simplex ==================
1515
1526 {
1528 << "dual_solve() requires a previously solved problem";
1529
1530 state = Solving;
1531
1532 auto start_time = std::chrono::high_resolution_clock::now();
1533 stats.iterations = 0;
1534 stats.pivots = 0;
1535
1536 const size_t rhs_col = num_var + num_rest + num_artificial;
1537
1538 while (true)
1539 {
1540 ++stats.iterations;
1541
1542 // Select leaving variable (row with most negative RHS)
1543 int leaving_row = -1;
1544 T min_rhs = T{0};
1545 for (size_t i = 1; i <= num_rest; ++i)
1546 {
1547 T rhs = m->read(i, rhs_col);
1548 if (rhs < min_rhs - eps)
1549 {
1550 min_rhs = rhs;
1551 leaving_row = static_cast<int>(i);
1552 }
1553 }
1554
1555 if (leaving_row == -1)
1556 {
1557 state = Solved;
1558 auto end_time = std::chrono::high_resolution_clock::now();
1559 stats.elapsed_ms = std::chrono::duration<double, std::milli>(
1560 end_time - start_time).count();
1561 return Solved;
1562 }
1563
1564 // Select entering variable (minimum ratio test on reduced costs)
1565 int entering_col = -1;
1566 T min_ratio = std::numeric_limits<T>::max();
1567 const size_t total_vars = num_var + num_rest + num_artificial;
1568
1569 for (size_t j = 0; j < total_vars; ++j)
1570 {
1571 T a_ij = m->read(leaving_row, j);
1572 if (a_ij >= -eps)
1573 continue; // Must be negative
1574
1575 T ratio = -m->read(0, j) / a_ij;
1576 if (ratio < min_ratio)
1577 {
1578 min_ratio = ratio;
1579 entering_col = static_cast<int>(j);
1580 }
1581 }
1582
1583 if (entering_col == -1)
1584 {
1585 state = Unfeasible;
1586 return Unfeasible;
1587 }
1588
1589 ++stats.pivots;
1592 }
1593 }
1594
1606 {
1609 << "Cannot update RHS without a solved problem";
1610
1611 // Update the RHS in the tableau
1612 const size_t rhs_col = num_var + num_rest + num_artificial;
1613 const size_t slack_col = num_var + rest_idx;
1614
1615 // Compute the change in RHS
1616 T old_rhs = T{0};
1617 size_t i = 0;
1618 for (auto it = rest_list.get_it(); it.has_curr(); it.next_ne(), ++i)
1619 if (i == rest_idx)
1620 {
1621 old_rhs = it.get_curr()[num_var];
1622 it.get_curr()[num_var] = new_rhs;
1623 break;
1624 }
1625
1626 T delta = new_rhs - old_rhs;
1627
1628 // Update tableau RHS using slack variable column
1629 for (size_t row = 0; row <= num_rest; ++row)
1630 {
1631 T coef = m->read(row, slack_col);
1632 T current_rhs = m->read(row, rhs_col);
1633 m->write(row, rhs_col, current_rhs + coef * delta);
1634 }
1635
1636 return dual_solve();
1637 }
1638
1641 void print_stats() const
1642 {
1643 std::cout << "=== Simplex Statistics ===\n"
1644 << "Iterations: " << stats.iterations << "\n"
1645 << "Pivots: " << stats.pivots << "\n"
1646 << "Degenerate pivots: " << stats.degenerate_pivots << "\n"
1647 << "Elapsed time: " << stats.elapsed_ms << " ms\n"
1648 << "Bland's rule used: " << (stats.bland_rule_used ? "yes" : "no") << "\n";
1649 }
1650
1656 {
1658 << "Must solve before printing sensitivity analysis";
1659
1660 std::cout << "=== Sensitivity Analysis ===\n\n";
1661
1662 std::cout << "Objective Coefficients:\n";
1663 for (size_t i = 0; i < num_var; ++i)
1664 {
1665 auto range = objective_sensitivity(i);
1666 std::cout << " x_" << i << ": current=" << range.current_value
1667 << ", range=[" << range.lower_bound << ", "
1668 << range.upper_bound << "]\n";
1669 }
1670
1671 std::cout << "\nRHS Values (Shadow Prices):\n";
1672 for (size_t i = 0; i < num_rest; ++i)
1673 {
1674 auto range = rhs_sensitivity(i);
1675 T sp = shadow_price(i);
1676 std::cout << " Constraint " << i << ": current=" << range.current_value
1677 << ", range=[" << range.lower_bound << ", " << range.upper_bound
1678 << "], shadow price=" << sp << "\n";
1679 }
1680
1681 std::cout << "\nReduced Costs:\n";
1682 for (size_t i = 0; i < num_var; ++i)
1683 {
1684 T rc = reduced_cost(i);
1685 bool basic = is_basic_variable(i);
1686 std::cout << " x_" << i << ": " << rc
1687 << (basic ? " (basic)" : " (non-basic)") << "\n";
1688 }
1689 }
1690 };
1691
1692
1693 // ============================================================================
1694 // REVISED SIMPLEX
1695 // ============================================================================
1696
1742 template <typename T>
1744 {
1745 public:
1748
1749 private:
1750 // Original problem data
1751 size_t m; // Number of constraints
1752 size_t n; // Number of original variables
1753 std::vector<T> c; // Objective coefficients (n)
1754 std::vector<std::vector<T>> A; // Constraint matrix (m × n)
1755 std::vector<T> b; // RHS values (m)
1756
1757 // Basis information
1758 std::vector<int> basic; // basic[i] = index of basic variable in row i
1759 std::vector<int> nonbasic; // Non-basic variable indices
1760 std::vector<bool> is_basic; // is_basic[j] = true if variable j is in basis
1761
1762 // Basis inverse representation using eta-matrices (product form)
1764 {
1765 size_t col; // Column being replaced
1766 std::vector<T> eta; // The eta column (m elements)
1767 };
1768 std::vector<EtaMatrix> eta_file; // Product form of B⁻¹
1769 std::vector<std::vector<T>> B_inv_explicit; // Explicit B⁻¹ (after refactorization)
1770
1771 // Current solution
1772 std::vector<T> x_B; // Basic variable values (m)
1773 std::vector<T> solution; // Full solution (n + m)
1774
1775 // Working vectors (reused across iterations)
1776 mutable std::vector<T> pi; // Dual prices (m)
1777 mutable std::vector<T> d; // FTRAN result / tableau column (m)
1778 mutable std::vector<T> work; // General work vector (m)
1779
1780 // Configuration
1782 size_t refactor_frequency; // Refactorize B⁻¹ every N iterations
1784
1785 // State and statistics
1788
1789 // ============ FTRAN: Solve B·y = a (forward transformation) ============
1790 // Computes y = B⁻¹·a
1791 // Uses explicit B⁻¹ for simplicity and numerical stability
1792
1793 void ftran(const std::vector<T>& a, std::vector<T>& y) const
1794 {
1795 // y = B⁻¹ · a
1796 for (size_t i = 0; i < m; ++i)
1797 {
1798 T sum = T{0};
1799 for (size_t j = 0; j < m; ++j)
1800 sum += B_inv_explicit[i][j] * a[j];
1801 y[i] = sum;
1802 }
1803 }
1804
1805 // ============ BTRAN: Solve π·B = c_B (backward transformation) ============
1806 // Computes π = c_B · B⁻¹
1807 // Uses explicit B⁻¹ for simplicity
1808
1809 void btran(const std::vector<T>& c_B, std::vector<T>& pi_out) const
1810 {
1811 // π_j = sum_i c_B[i] * B⁻¹[i][j]
1812 for (size_t j = 0; j < m; ++j)
1813 {
1814 T sum = T{0};
1815 for (size_t i = 0; i < m; ++i)
1816 sum += c_B[i] * B_inv_explicit[i][j];
1817 pi_out[j] = sum;
1818 }
1819 }
1820
1821 // ============ Compute reduced cost for column j ============
1822
1823 T compute_reduced_cost(size_t j) const
1824 {
1825 // c̄_j = c_j - π · a_j
1826 T rc = (j < n) ? c[j] : T{0}; // Slack variables have c = 0
1827
1828 for (size_t i = 0; i < m; ++i)
1829 {
1830 T a_ij = (j < n) ? A[i][j] : ((j - n == i) ? T{1} : T{0});
1831 rc -= pi[i] * a_ij;
1832 }
1833
1834 return rc;
1835 }
1836
1837 // ============ Get column of A (including slack columns) ============
1838
1839 void get_column(size_t j, std::vector<T>& col) const
1840 {
1841 if (j < n)
1842 {
1843 // Original variable column
1844 for (size_t i = 0; i < m; ++i)
1845 col[i] = A[i][j];
1846 }
1847 else
1848 {
1849 // Slack variable column (identity)
1850 size_t slack_idx = j - n;
1851 for (size_t i = 0; i < m; ++i)
1852 col[i] = (i == slack_idx) ? T{1} : T{0};
1853 }
1854 }
1855
1856 // ============ Pricing: Find entering variable ============
1857
1859 {
1860 // Compute dual prices: π = c_B · B⁻¹
1861 std::vector<T> c_B(m);
1862 for (size_t i = 0; i < m; ++i)
1863 {
1864 int var = basic[i];
1865 c_B[i] = (var < static_cast<int>(n)) ? c[var] : T{0};
1866 }
1867
1868 btran(c_B, pi);
1869
1870 // For maximization: find most POSITIVE reduced cost
1871 // (positive rc means increasing that variable improves objective)
1872 T max_rc = eps;
1873 int entering = -1;
1874
1875 for (int j : nonbasic)
1876 {
1878 if (rc > max_rc)
1879 {
1880 max_rc = rc;
1881 entering = j;
1882 }
1883 }
1884
1885 return entering;
1886 }
1887
1888 // ============ Ratio test: Find leaving variable ============
1889
1891 {
1892 // Get column of entering variable
1893 std::vector<T> a_j(m);
1895
1896 // FTRAN: compute d = B⁻¹ · a_j
1897 ftran(a_j, d);
1898
1899 // Ratio test
1900 theta = std::numeric_limits<T>::max();
1901 int leaving = -1;
1902
1903 for (size_t i = 0; i < m; ++i)
1904 {
1905 if (d[i] > eps)
1906 {
1907 T ratio = x_B[i] / d[i];
1908 if (ratio < theta - eps)
1909 {
1910 theta = ratio;
1911 leaving = static_cast<int>(i);
1912 }
1913 }
1914 }
1915
1916 return leaving;
1917 }
1918
1919 // ============ Update basis inverse after pivot ============
1920 // Uses the formula: B_new⁻¹ = E · B_old⁻¹
1921 // where E is an elementary matrix based on the pivot column d
1922
1923 void update_basis_inverse(int leaving_row, const std::vector<T>& d_col)
1924 {
1926 if (std::abs(pivot) < eps)
1927 return; // Numerical issue
1928
1929 // Compute eta column: eta[i] = -d[i]/pivot for i != leaving_row
1930 // eta[leaving_row] = 1/pivot
1931 std::vector<T> eta(m);
1932 for (size_t i = 0; i < m; ++i)
1933 {
1934 if (static_cast<int>(i) == leaving_row)
1935 eta[i] = T{1} / pivot;
1936 else
1937 eta[i] = -d_col[i] / pivot;
1938 }
1939
1940 // Update B⁻¹: new_B_inv = E · B_inv
1941 // E is identity except column leaving_row has the eta values
1942 std::vector<std::vector<T>> new_B_inv(m, std::vector<T>(m));
1943
1944 for (size_t i = 0; i < m; ++i)
1945 {
1946 for (size_t j = 0; j < m; ++j)
1947 {
1948 if (static_cast<int>(i) == leaving_row)
1949 {
1950 // Row leaving_row: multiply by 1/pivot
1952 }
1953 else
1954 {
1955 // Other rows: subtract multiple of leaving_row
1956 new_B_inv[i][j] = B_inv_explicit[i][j] -
1958 }
1959 }
1960 }
1961
1962 B_inv_explicit = std::move(new_B_inv);
1963 }
1964
1965 // ============ Refactorize B⁻¹ explicitly ============
1966
1968 {
1969 // Build explicit B from current basis
1970 std::vector<std::vector<T>> B(m, std::vector<T>(m, T{0}));
1971
1972 for (size_t i = 0; i < m; ++i)
1973 {
1974 int var = basic[i];
1975 if (var < static_cast<int>(n))
1976 {
1977 // Original variable column
1978 for (size_t row = 0; row < m; ++row)
1979 B[row][i] = A[row][var];
1980 }
1981 else
1982 {
1983 // Slack variable (identity column)
1984 B[var - n][i] = T{1};
1985 }
1986 }
1987
1988 // Compute B⁻¹ using Gauss-Jordan elimination
1989 B_inv_explicit.assign(m, std::vector<T>(m, T{0}));
1990 for (size_t i = 0; i < m; ++i)
1991 B_inv_explicit[i][i] = T{1};
1992
1993 for (size_t col = 0; col < m; ++col)
1994 {
1995 // Find pivot
1996 size_t pivot_row = col;
1997 T max_val = std::abs(B[col][col]);
1998 for (size_t row = col + 1; row < m; ++row)
1999 {
2000 if (std::abs(B[row][col]) > max_val)
2001 {
2002 max_val = std::abs(B[row][col]);
2003 pivot_row = row;
2004 }
2005 }
2006
2007 // Swap rows if needed
2008 if (pivot_row != col)
2009 {
2010 std::swap(B[col], B[pivot_row]);
2011 std::swap(B_inv_explicit[col], B_inv_explicit[pivot_row]);
2012 }
2013
2014 // Scale pivot row
2015 T pivot = B[col][col];
2016 for (size_t j = 0; j < m; ++j)
2017 {
2018 B[col][j] /= pivot;
2019 B_inv_explicit[col][j] /= pivot;
2020 }
2021
2022 // Eliminate column
2023 for (size_t row = 0; row < m; ++row)
2024 {
2025 if (row != col)
2026 {
2027 T factor = B[row][col];
2028 for (size_t j = 0; j < m; ++j)
2029 {
2030 B[row][j] -= factor * B[col][j];
2031 B_inv_explicit[row][j] -= factor * B_inv_explicit[col][j];
2032 }
2033 }
2034 }
2035 }
2036
2037 // Clear eta-file after refactorization
2038 eta_file.clear();
2039 }
2040
2041 public:
2049 c(num_vars, T{0}),
2050 A(num_constraints, std::vector<T>(num_vars, T{0})),
2051 b(num_constraints, T{0}),
2054 x_B(num_constraints, T{0}),
2057 eps(std::numeric_limits<T>::epsilon() * 1000),
2061 {
2063 << "Number of variables and constraints must be > 0";
2064 }
2065
2071 void set_objective(size_t j, T coef)
2072 {
2073 ah_out_of_range_error_if(j >= n) << "Variable index out of range";
2074 c[j] = coef;
2075 }
2076
2081 void set_objective(const T* coefs)
2082 {
2083 for (size_t j = 0; j < n; ++j)
2084 c[j] = coefs[j];
2085 }
2086
2093 void set_constraint(size_t i, size_t j, T coef)
2094 {
2095 ah_out_of_range_error_if(i >= m || j >= n)
2096 << "Constraint or variable index out of range";
2097 A[i][j] = coef;
2098 }
2099
2105 void set_rhs(size_t i, T rhs)
2106 {
2107 ah_out_of_range_error_if(i >= m) << "Constraint index out of range";
2108 b[i] = rhs;
2109 }
2110
2117 void set_constraint_row(size_t i, const T* coefs, T rhs)
2118 {
2119 ah_out_of_range_error_if(i >= m) << "Constraint index out of range";
2120 for (size_t j = 0; j < n; ++j)
2121 A[i][j] = coefs[j];
2122 b[i] = rhs;
2123 }
2124
2133 {
2135 }
2136
2142 {
2143 ah_logic_error_if(state != Not_Solved) << "Already solved";
2144
2145 stats.reset();
2146 auto start_time = std::chrono::high_resolution_clock::now();
2147
2148 // Initialize basis with slack variables
2149 for (size_t i = 0; i < m; ++i)
2150 {
2151 basic[i] = static_cast<int>(n + i); // Slack variable index
2152 is_basic[n + i] = true;
2153 x_B[i] = b[i]; // Initial basic solution
2154 }
2155
2156 // Initialize non-basic variable list
2157 nonbasic.clear();
2158 for (size_t j = 0; j < n; ++j)
2159 nonbasic.push_back(static_cast<int>(j));
2160
2161 // Initial B⁻¹ is identity (slack variables)
2162 B_inv_explicit.assign(m, std::vector<T>(m, T{0}));
2163 for (size_t i = 0; i < m; ++i)
2164 B_inv_explicit[i][i] = T{1};
2165
2166 state = Solving;
2167
2168 // Main loop
2169 while (true)
2170 {
2171 ++stats.iterations;
2172
2173 // Pricing: select entering variable
2175
2176 if (entering < 0)
2177 {
2178 // Optimal!
2179 state = Solved;
2180 break;
2181 }
2182
2183 // Ratio test: select leaving variable
2184 T theta;
2186
2187 if (leaving < 0)
2188 {
2189 // Unbounded!
2190 state = Unbounded;
2191 break;
2192 }
2193
2194 // Update solution
2195 for (size_t i = 0; i < m; ++i)
2196 x_B[i] -= theta * d[i];
2197 x_B[leaving] = theta;
2198
2199 // Update basis
2200 int leaving_var = basic[leaving];
2202 is_basic[leaving_var] = false;
2203 is_basic[entering] = true;
2204
2205 // Update non-basic list
2206 for (auto& nb : nonbasic)
2207 if (nb == entering)
2208 {
2209 nb = leaving_var;
2210 break;
2211 }
2212
2213 // Update B⁻¹
2215
2216 ++stats.pivots;
2217 }
2218
2219 // Load final solution
2220 std::fill(solution.begin(), solution.end(), T{0});
2221 for (size_t i = 0; i < m; ++i)
2222 solution[basic[i]] = x_B[i];
2223
2224 auto end_time = std::chrono::high_resolution_clock::now();
2225 stats.elapsed_ms = std::chrono::duration<double, std::milli>(
2226 end_time - start_time).count();
2227
2228 return state;
2229 }
2230
2236 [[nodiscard]] T get_solution(size_t j) const
2237 {
2238 ah_out_of_range_error_if(j >= n) << "Variable index out of range";
2239 return solution[j];
2240 }
2241
2247 {
2248 T sum = T{0};
2249 for (size_t j = 0; j < n; ++j)
2250 sum += c[j] * solution[j];
2251 return sum;
2252 }
2253
2256
2259
2261 [[nodiscard]] size_t get_num_vars() const noexcept { return n; }
2262
2265
2271 {
2272 for (size_t i = 0; i < m; ++i)
2273 {
2274 T lhs = T{0};
2275 for (size_t j = 0; j < n; ++j)
2276 lhs += A[i][j] * solution[j];
2277 if (lhs > b[i] + eps)
2278 return false;
2279 }
2280 return true;
2281 }
2282
2284 void print_stats() const
2285 {
2286 std::cout << "=== Revised Simplex Statistics ===\n"
2287 << "Variables: " << n << ", Constraints: " << m << "\n"
2288 << "Iterations: " << stats.iterations << "\n"
2289 << "Pivots: " << stats.pivots << "\n"
2290 << "Elapsed time: " << stats.elapsed_ms << " ms\n"
2291 << "Eta-matrices: " << eta_file.size() << "\n"
2292 << "Refactorizations: "
2293 << (stats.pivots / refactor_frequency) << "\n";
2294 }
2295 };
2296
2297} // end namespace Aleph
2298
2299# endif // SIMPLEX_H
Exception handling system with formatted messages for Aleph-w.
#define ah_out_of_range_error_if(C)
Throws std::out_of_range if condition holds.
Definition ah-errors.H:579
#define ah_logic_error_if(C)
Throws std::logic_error if condition holds.
Definition ah-errors.H:325
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:639
Dynamic doubly linked list with O(1) size and bidirectional access.
T & append(const T &item)
Append a copied item at the end of the list.
Revised Simplex algorithm for linear programming.
Definition Simplex.H:1744
std::vector< T > x_B
Definition Simplex.H:1772
std::vector< T > work
Definition Simplex.H:1778
void set_refactorization_frequency(size_t freq)
Sets refactorization frequency.
Definition Simplex.H:2132
void set_objective(const T *coefs)
Sets all objective coefficients.
Definition Simplex.H:2081
std::vector< T > d
Definition Simplex.H:1777
void set_objective(size_t j, T coef)
Sets objective coefficient.
Definition Simplex.H:2071
size_t get_num_vars() const noexcept
Gets number of variables.
Definition Simplex.H:2261
std::vector< T > solution
Definition Simplex.H:1773
std::vector< int > basic
Definition Simplex.H:1758
std::vector< int > nonbasic
Definition Simplex.H:1759
void print_stats() const
Prints statistics to stdout.
Definition Simplex.H:2284
int select_entering_variable()
Definition Simplex.H:1858
std::vector< bool > is_basic
Definition Simplex.H:1760
size_t get_num_constraints() const noexcept
Gets number of constraints.
Definition Simplex.H:2264
State
Solution states for the linear program.
Definition Simplex.H:1747
const SimplexStats & get_stats() const noexcept
Gets execution statistics.
Definition Simplex.H:2258
void update_basis_inverse(int leaving_row, const std::vector< T > &d_col)
Definition Simplex.H:1923
int select_leaving_variable(int entering, T &theta)
Definition Simplex.H:1890
std::vector< std::vector< T > > A
Definition Simplex.H:1754
void get_column(size_t j, std::vector< T > &col) const
Definition Simplex.H:1839
void set_constraint_row(size_t i, const T *coefs, T rhs)
Adds a complete constraint.
Definition Simplex.H:2117
std::vector< EtaMatrix > eta_file
Definition Simplex.H:1768
std::vector< T > pi
Definition Simplex.H:1776
State get_state() const noexcept
Gets current state.
Definition Simplex.H:2255
T get_solution(size_t j) const
Gets solution value for variable j.
Definition Simplex.H:2236
void ftran(const std::vector< T > &a, std::vector< T > &y) const
Definition Simplex.H:1793
SimplexStats stats
Definition Simplex.H:1787
std::vector< T > c
Definition Simplex.H:1753
void set_constraint(size_t i, size_t j, T coef)
Sets constraint coefficient.
Definition Simplex.H:2093
std::vector< std::vector< T > > B_inv_explicit
Definition Simplex.H:1769
T objective_value() const
Computes objective function value.
Definition Simplex.H:2246
void btran(const std::vector< T > &c_B, std::vector< T > &pi_out) const
Definition Simplex.H:1809
State solve()
Solves the linear program.
Definition Simplex.H:2141
T compute_reduced_cost(size_t j) const
Definition Simplex.H:1823
void set_rhs(size_t i, T rhs)
Sets RHS value for a constraint.
Definition Simplex.H:2105
std::vector< T > b
Definition Simplex.H:1755
bool verify_solution() const
Verifies solution satisfies all constraints.
Definition Simplex.H:2270
RevisedSimplex(size_t num_vars, size_t num_constraints)
Constructs a Revised Simplex solver.
Definition Simplex.H:2047
Linear program solver using the Simplex method.
Definition Simplex.H:227
void set_optimization_type(OptimizationType type) noexcept
Sets the optimization type.
Definition Simplex.H:645
SensitivityRange< T > rhs_sensitivity(size_t rest_idx) const
Computes sensitivity range for a RHS value.
Definition Simplex.H:1368
void print_matrix() const
Prints the simplex tableau to stdout.
Definition Simplex.H:1092
State
Solution states for the linear program.
Definition Simplex.H:237
OptimizationType get_optimization_type() const noexcept
Gets the current optimization type.
Definition Simplex.H:703
const SimplexStats & get_stats() const noexcept
Gets execution statistics.
Definition Simplex.H:1242
T & get_restriction_coef(const size_t rest_num, size_t idx)
Gets a specific coefficient from a constraint.
Definition Simplex.H:892
void set_minimize() noexcept
Sets minimization mode.
Definition Simplex.H:654
std::vector< int > basic_vars
Definition Simplex.H:442
size_t num_rest
Definition Simplex.H:426
OptimizationType opt_type
Definition Simplex.H:430
std::vector< T > pivot_col_buffer
Definition Simplex.H:439
T objetive_value() const noexcept
Computes and returns the objective function value.
Definition Simplex.H:1039
Simplex(Simplex &&)=default
std::vector< T > get_all_reduced_costs() const
Gets all reduced costs as a vector.
Definition Simplex.H:1505
void print_sensitivity_analysis() const
Prints sensitivity analysis to stdout.
Definition Simplex.H:1655
std::vector< T > pivot_row_buffer
Definition Simplex.H:438
T * get_restriction(const size_t rest_num)
Gets a pointer to a constraint array.
Definition Simplex.H:836
T * get_objetive_function() noexcept
Gets the objective function coefficients array.
Definition Simplex.H:870
void to_pivot(size_t p, size_t q)
Definition Simplex.H:341
bool verify_solution() const
Verifies that the solution satisfies all constraints.
Definition Simplex.H:1056
DynDlist< ConstraintType > rest_types
Definition Simplex.H:424
void verify_rest_index(const size_t i) const
Definition Simplex.H:452
void create_matrix()
Definition Simplex.H:482
Simplex & operator=(const Simplex &)=delete
T find_value(const size_t j) const noexcept
Definition Simplex.H:390
size_t num_var
Definition Simplex.H:425
SensitivityRange< T > objective_sensitivity(size_t var_idx) const
Computes sensitivity range for an objective coefficient.
Definition Simplex.H:1299
void put_restriction_coef(const size_t rest_num, const size_t idx, const T &coef)
Sets a specific coefficient in a constraint.
Definition Simplex.H:906
std::unique_ptr< DynMatrix< T > > m
Definition Simplex.H:421
size_t get_pivot_count() const noexcept
Gets the number of pivot operations.
Definition Simplex.H:1260
int compute_pivot_row(int p) const noexcept
Definition Simplex.H:282
T * put_restriction(const T *coefs=nullptr, ConstraintType type=ConstraintType::LE)
Adds a constraint to the linear program.
Definition Simplex.H:772
void prepare_linear_program()
Prepares the linear program for solving.
Definition Simplex.H:921
void latex_linear_program(const std::string &name) const
Exports the linear program to a LaTeX file.
Definition Simplex.H:1149
T shadow_price(size_t rest_idx) const
Gets the shadow price (dual value) for a constraint.
Definition Simplex.H:1426
State update_rhs_and_reoptimize(size_t rest_idx, T new_rhs)
Updates RHS value and re-optimizes using dual simplex.
Definition Simplex.H:1605
bool is_basic_variable(size_t var_idx) const
Checks if a variable is basic in the optimal solution.
Definition Simplex.H:1475
void print_stats() const
Prints a summary of statistics to stdout.
Definition Simplex.H:1641
size_t num_artificial
Definition Simplex.H:427
Simplex & operator=(Simplex &&)=default
void set_maximize() noexcept
Sets maximization mode.
Definition Simplex.H:663
size_t get_iteration_count() const noexcept
Gets the number of iterations performed.
Definition Simplex.H:1251
size_t get_num_restrictions() const noexcept
Gets the number of constraints.
Definition Simplex.H:852
const T & get_solution(size_t i) const noexcept
Gets the solution value for a specific variable.
Definition Simplex.H:1025
Simplex(const Simplex &)=delete
DynDlist< T * > rest_list
Definition Simplex.H:423
void latex_matrix(const std::string &name, const int d=2, const int p=-1, const int q=-1) const
Exports the simplex tableau to a LaTeX file.
Definition Simplex.H:1110
PivotRule pivot_rule
Definition Simplex.H:431
void put_objetive_function_coef(size_t i, const T &coef)
Sets a coefficient in the objective function.
Definition Simplex.H:717
size_t count_artificial_vars() const noexcept
Definition Simplex.H:471
void set_pivot_rule(PivotRule rule) noexcept
Sets the pivot selection rule.
Definition Simplex.H:675
void verify_var_index(const size_t i) const
Definition Simplex.H:445
T * put_ge_restriction(const T *coefs)
Adds a ≥ constraint.
Definition Simplex.H:793
T reduced_cost(size_t var_idx) const
Gets the reduced cost for a variable.
Definition Simplex.H:1454
void load_solution() noexcept
Loads the solution values into the solution array.
Definition Simplex.H:1010
const T * get_objetive_function() const noexcept
Gets the objective function coefficients array (const).
Definition Simplex.H:879
int compute_pivot_col() const noexcept
Definition Simplex.H:250
State select_pivot(int &p, int &q) noexcept
Definition Simplex.H:321
~Simplex()
Destructor.
Definition Simplex.H:624
State dual_solve()
Performs dual simplex iteration.
Definition Simplex.H:1525
void set_epsilon(T epsilon) noexcept
Sets the floating-point comparison tolerance.
Definition Simplex.H:694
std::unique_ptr< T[]> solution
Definition Simplex.H:428
std::unique_ptr< T[]> objetive
Definition Simplex.H:422
void put_objetive_function(const DynArray< T > &coefs)
Sets all objective function coefficients from a DynArray.
Definition Simplex.H:740
size_t get_num_vars() const noexcept
Gets the number of decision variables.
Definition Simplex.H:861
T * create_restriction(ConstraintType type=ConstraintType::LE)
Definition Simplex.H:459
State get_state() const noexcept
Gets the current state of the solver.
Definition Simplex.H:1000
size_t get_degenerate_pivot_count() const noexcept
Gets the number of degenerate pivots.
Definition Simplex.H:1271
T * put_restriction(const DynArray< T > &coefs, ConstraintType type=ConstraintType::LE)
Adds a constraint from a DynArray.
Definition Simplex.H:818
PivotRule
Pivot selection rule.
Definition Simplex.H:244
T * put_eq_restriction(const T *coefs)
Adds an = constraint.
Definition Simplex.H:805
State latex_solve(const char *name=nullptr)
Solves with LaTeX output at each iteration.
Definition Simplex.H:1211
void enable_bland_rule() noexcept
Enables Bland's anti-cycling rule.
Definition Simplex.H:684
Simplex(const size_t n, OptimizationType type=OptimizationType::Maximize)
Constructs a Simplex solver for n variables.
Definition Simplex.H:605
SimplexStats stats
Definition Simplex.H:435
State solve()
Solves the linear program.
Definition Simplex.H:947
void put_objetive_function(const T coefs[])
Sets all objective function coefficients from a C array.
Definition Simplex.H:750
std::vector< T > get_all_shadow_prices() const
Gets all shadow prices as a vector.
Definition Simplex.H:1490
const T & get_objetive_function_coef(size_t i) const
Gets a coefficient from the objective function.
Definition Simplex.H:730
double get_elapsed_time_ms() const noexcept
Gets the elapsed solving time.
Definition Simplex.H:1280
void for_each(Operation &operation)
Traverse all the container and performs an operation on each element.
Definition ah-dry.H:685
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:190
__gmp_expr< typename __gmp_resolve_expr< T, V >::value_type, __gmp_binary_expr< __gmp_expr< T, U >, __gmp_expr< V, W >, __gmp_max_function > > max(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4110
static mpfr_t y
Definition mpfr_mul_d.c:3
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:107
Container< T > range(const T start, const T end, const T step=1)
Generate a range of values [start, end] with a given step.
ConstraintType
Constraint type for linear programming.
Definition Simplex.H:72
OptimizationType
Optimization direction for linear programming.
Definition Simplex.H:78
DynList< T > maps(const C &c, Op op)
Classic map operation.
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
STL namespace.
Sensitivity analysis result for a coefficient.
Definition Simplex.H:109
T lower_bound
Minimum value before basis change.
Definition Simplex.H:110
T upper_bound
Maximum value before basis change.
Definition Simplex.H:111
bool is_unbounded_below() const noexcept
Definition Simplex.H:114
T current_value
Current coefficient value.
Definition Simplex.H:112
bool is_unbounded_above() const noexcept
Definition Simplex.H:117
Execution statistics for the Simplex algorithm.
Definition Simplex.H:85
bool bland_rule_used
Whether Bland's rule was activated.
Definition Simplex.H:90
double elapsed_ms
Elapsed time in milliseconds.
Definition Simplex.H:89
size_t iterations
Total simplex iterations.
Definition Simplex.H:86
void reset() noexcept
Definition Simplex.H:92
size_t pivots
Total pivot operations.
Definition Simplex.H:87
size_t degenerate_pivots
Pivots with zero improvement.
Definition Simplex.H:88
Dynamic doubly linked list implementation.
Dynamic matrix with lazy allocation.