Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Simplex_test.cc
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
51#include <gtest/gtest.h>
52#include <Simplex.H>
53#include <cmath>
54
55using namespace Aleph;
56
57//==============================================================================
58// Test Fixture
59//==============================================================================
60
61class SimplexTest : public ::testing::Test
62{
63protected:
64 static constexpr double EPSILON = 1e-6;
65
66 bool nearly_equal(double a, double b, double eps = EPSILON)
67 {
68 return std::fabs(a - b) < eps;
69 }
70};
71
72//==============================================================================
73// Constructor Tests
74//==============================================================================
75
77{
79
80 EXPECT_EQ(simplex.get_num_vars(), 3);
81 EXPECT_EQ(simplex.get_num_restrictions(), 0);
83}
84
89
95
101
102//==============================================================================
103// Objective Function Tests
104//==============================================================================
105
107{
109
110 simplex.put_objetive_function_coef(0, 3.0);
111 simplex.put_objetive_function_coef(1, 2.0);
112 simplex.put_objetive_function_coef(2, 1.0);
113
114 EXPECT_DOUBLE_EQ(simplex.get_objetive_function_coef(0), 3.0);
115 EXPECT_DOUBLE_EQ(simplex.get_objetive_function_coef(1), 2.0);
116 EXPECT_DOUBLE_EQ(simplex.get_objetive_function_coef(2), 1.0);
117}
118
120{
122
123 EXPECT_THROW(simplex.put_objetive_function_coef(3, 1.0), std::out_of_range);
124 EXPECT_THROW(simplex.put_objetive_function_coef(100, 1.0), std::out_of_range);
125}
126
128{
130
131 EXPECT_THROW(simplex.get_objetive_function_coef(3), std::out_of_range);
132}
133
135{
137 double coefs[] = {5.0, 4.0, 3.0};
138
139 simplex.put_objetive_function(coefs);
140
141 EXPECT_DOUBLE_EQ(simplex.get_objetive_function_coef(0), 5.0);
142 EXPECT_DOUBLE_EQ(simplex.get_objetive_function_coef(1), 4.0);
143 EXPECT_DOUBLE_EQ(simplex.get_objetive_function_coef(2), 3.0);
144}
145
147{
150 coefs[0] = 5.0;
151 coefs[1] = 4.0;
152 coefs[2] = 3.0;
153
154 simplex.put_objetive_function(coefs);
155
156 EXPECT_DOUBLE_EQ(simplex.get_objetive_function_coef(0), 5.0);
157 EXPECT_DOUBLE_EQ(simplex.get_objetive_function_coef(1), 4.0);
158 EXPECT_DOUBLE_EQ(simplex.get_objetive_function_coef(2), 3.0);
159}
160
162{
164 simplex.put_objetive_function_coef(0, 7.0);
165
166 double* ptr = simplex.get_objetive_function();
167 ASSERT_NE(ptr, nullptr);
168 EXPECT_DOUBLE_EQ(ptr[0], 7.0);
169}
170
171//==============================================================================
172// Restriction Tests
173//==============================================================================
174
176{
178 double rest[] = {1.0, 2.0, 10.0};
179
180 simplex.put_restriction(rest);
181
182 EXPECT_EQ(simplex.get_num_restrictions(), 1);
183}
184
186{
188 double r1[] = {1.0, 1.0, 4.0};
189 double r2[] = {2.0, 1.0, 5.0};
190 double r3[] = {1.0, 2.0, 6.0};
191
192 simplex.put_restriction(r1);
193 simplex.put_restriction(r2);
194 simplex.put_restriction(r3);
195
196 EXPECT_EQ(simplex.get_num_restrictions(), 3);
197}
198
200{
202
203 double* rest = simplex.put_restriction(nullptr);
204
205 ASSERT_NE(rest, nullptr);
206 EXPECT_EQ(simplex.get_num_restrictions(), 1);
207
208 // Should be initialized to zero
209 EXPECT_DOUBLE_EQ(rest[0], 0.0);
210 EXPECT_DOUBLE_EQ(rest[1], 0.0);
211 EXPECT_DOUBLE_EQ(rest[2], 0.0);
212}
213
215{
217
218 double* rest = simplex.put_restriction(nullptr);
219 rest[0] = 3.0;
220 rest[1] = 4.0;
221 rest[2] = 12.0;
222
223 double* retrieved = simplex.get_restriction(0);
226 EXPECT_DOUBLE_EQ(retrieved[2], 12.0);
227}
228
230{
232 double rest[] = {1.0, 1.0, 4.0};
233 simplex.put_restriction(rest);
234
235 EXPECT_THROW(simplex.get_restriction(1), std::out_of_range);
236 EXPECT_THROW(simplex.get_restriction(100), std::out_of_range);
237}
238
240{
242 double rest[] = {1.0, 2.0, 3.0, 10.0};
243 simplex.put_restriction(rest);
244
245 EXPECT_DOUBLE_EQ(simplex.get_restriction_coef(0, 0), 1.0);
246 EXPECT_DOUBLE_EQ(simplex.get_restriction_coef(0, 1), 2.0);
247 EXPECT_DOUBLE_EQ(simplex.get_restriction_coef(0, 2), 3.0);
248}
249
251{
253 simplex.put_restriction(nullptr);
254
255 simplex.put_restriction_coef(0, 0, 5.0);
256 simplex.put_restriction_coef(0, 1, 6.0);
257
258 EXPECT_DOUBLE_EQ(simplex.get_restriction_coef(0, 0), 5.0);
259 EXPECT_DOUBLE_EQ(simplex.get_restriction_coef(0, 1), 6.0);
260}
261
263{
266 coefs[0] = 1.0;
267 coefs[1] = 2.0;
268 coefs[2] = 8.0;
269
270 simplex.put_restriction(coefs);
271
272 EXPECT_EQ(simplex.get_num_restrictions(), 1);
273 EXPECT_DOUBLE_EQ(simplex.get_restriction_coef(0, 0), 1.0);
274 EXPECT_DOUBLE_EQ(simplex.get_restriction_coef(0, 1), 2.0);
275}
276
277//==============================================================================
278// Solve Error Handling Tests
279//==============================================================================
280
282{
284 simplex.put_objetive_function_coef(0, 3.0);
285 simplex.put_objetive_function_coef(1, 2.0);
286
287 EXPECT_THROW(simplex.prepare_linear_program(), std::logic_error);
288}
289
291{
293 simplex.put_objetive_function_coef(0, 3.0);
294 simplex.put_objetive_function_coef(1, 2.0);
295 double rest[] = {1.0, 1.0, 4.0};
296 simplex.put_restriction(rest);
297
298 EXPECT_THROW(simplex.solve(), std::logic_error);
299}
300
302{
304 simplex.put_objetive_function_coef(0, 3.0);
305 simplex.put_objetive_function_coef(1, 2.0);
306 double rest[] = {1.0, 1.0, 4.0};
307 simplex.put_restriction(rest);
308
309 simplex.prepare_linear_program();
310 simplex.solve();
311
312 EXPECT_THROW(simplex.solve(), std::logic_error);
313}
314
315//==============================================================================
316// Classic Linear Programming Problems
317//==============================================================================
318
320{
321 // Maximize Z = 3x + 2y
322 // Subject to:
323 // x + y <= 4
324 // x <= 2
325 // y <= 3
326 // x, y >= 0
327 // Expected solution: x=2, y=2, Z=10
328
330 simplex.put_objetive_function_coef(0, 3.0); // x coefficient
331 simplex.put_objetive_function_coef(1, 2.0); // y coefficient
332
333 double c1[] = {1.0, 1.0, 4.0};
334 double c2[] = {1.0, 0.0, 2.0};
335 double c3[] = {0.0, 1.0, 3.0};
336
337 simplex.put_restriction(c1);
338 simplex.put_restriction(c2);
339 simplex.put_restriction(c3);
340
341 simplex.prepare_linear_program();
342 auto state = simplex.solve();
343
345
346 simplex.load_solution();
347
348 EXPECT_TRUE(nearly_equal(simplex.get_solution(0), 2.0));
349 EXPECT_TRUE(nearly_equal(simplex.get_solution(1), 2.0));
350 EXPECT_TRUE(nearly_equal(simplex.objetive_value(), 10.0));
351 EXPECT_TRUE(simplex.verify_solution());
352}
353
355{
356 // Maximize Z = 5x1 + 4x2 + 3x3
357 // Subject to:
358 // 2x1 + 3x2 + x3 <= 5
359 // 4x1 + 2x2 + 3x3 <= 11
360 // 3x1 + 4x2 + 2x3 <= 8
361 // x1, x2, x3 >= 0
362
364 simplex.put_objetive_function_coef(0, 5.0);
365 simplex.put_objetive_function_coef(1, 4.0);
366 simplex.put_objetive_function_coef(2, 3.0);
367
368 double c1[] = {2.0, 3.0, 1.0, 5.0};
369 double c2[] = {4.0, 2.0, 3.0, 11.0};
370 double c3[] = {3.0, 4.0, 2.0, 8.0};
371
372 simplex.put_restriction(c1);
373 simplex.put_restriction(c2);
374 simplex.put_restriction(c3);
375
376 simplex.prepare_linear_program();
377 auto state = simplex.solve();
378
380
381 simplex.load_solution();
382 EXPECT_TRUE(simplex.verify_solution());
383
384 // Objective value should be positive and bounded
385 EXPECT_GT(simplex.objetive_value(), 0.0);
386}
387
389{
390 // Classic product mix problem
391 // Maximize Z = 40x1 + 30x2 (profit)
392 // Subject to:
393 // x1 + x2 <= 40 (labor hours)
394 // 2x1 + x2 <= 60 (machine hours)
395 // x1, x2 >= 0
396 // Expected: x1=20, x2=20, Z=1400
397
399 simplex.put_objetive_function_coef(0, 40.0);
400 simplex.put_objetive_function_coef(1, 30.0);
401
402 double c1[] = {1.0, 1.0, 40.0};
403 double c2[] = {2.0, 1.0, 60.0};
404
405 simplex.put_restriction(c1);
406 simplex.put_restriction(c2);
407
408 simplex.prepare_linear_program();
409 auto state = simplex.solve();
410
412
413 simplex.load_solution();
414
415 EXPECT_TRUE(nearly_equal(simplex.get_solution(0), 20.0));
416 EXPECT_TRUE(nearly_equal(simplex.get_solution(1), 20.0));
417 EXPECT_TRUE(nearly_equal(simplex.objetive_value(), 1400.0));
418 EXPECT_TRUE(simplex.verify_solution());
419}
420
422{
423 // Simplified diet problem (but maximizing nutrition here)
424 // Maximize Z = 10x1 + 15x2 + 12x3 (nutrition value)
425 // Subject to:
426 // x1 + x2 + x3 <= 10 (total servings)
427 // 2x1 + x2 + 3x3 <= 18 (cost constraint)
428 // x1, x2, x3 >= 0
429
431 simplex.put_objetive_function_coef(0, 10.0);
432 simplex.put_objetive_function_coef(1, 15.0);
433 simplex.put_objetive_function_coef(2, 12.0);
434
435 double c1[] = {1.0, 1.0, 1.0, 10.0};
436 double c2[] = {2.0, 1.0, 3.0, 18.0};
437
438 simplex.put_restriction(c1);
439 simplex.put_restriction(c2);
440
441 simplex.prepare_linear_program();
442 auto state = simplex.solve();
443
445
446 simplex.load_solution();
447 EXPECT_TRUE(simplex.verify_solution());
448 EXPECT_GT(simplex.objetive_value(), 0.0);
449}
450
452{
453 // Maximize Z = 2x1 + 3x2 + x3 + 4x4
454 // Subject to:
455 // x1 + x2 <= 4
456 // x3 + x4 <= 6
457 // x1 + x3 <= 5
458 // x2 + x4 <= 5
459
461 simplex.put_objetive_function_coef(0, 2.0);
462 simplex.put_objetive_function_coef(1, 3.0);
463 simplex.put_objetive_function_coef(2, 1.0);
464 simplex.put_objetive_function_coef(3, 4.0);
465
466 double c1[] = {1.0, 1.0, 0.0, 0.0, 4.0};
467 double c2[] = {0.0, 0.0, 1.0, 1.0, 6.0};
468 double c3[] = {1.0, 0.0, 1.0, 0.0, 5.0};
469 double c4[] = {0.0, 1.0, 0.0, 1.0, 5.0};
470
471 simplex.put_restriction(c1);
472 simplex.put_restriction(c2);
473 simplex.put_restriction(c3);
474 simplex.put_restriction(c4);
475
476 simplex.prepare_linear_program();
477 auto state = simplex.solve();
478
480
481 simplex.load_solution();
482 EXPECT_TRUE(simplex.verify_solution());
483}
484
485//==============================================================================
486// Edge Cases
487//==============================================================================
488
490{
491 // Maximize Z = 5x
492 // Subject to: x <= 10
493 // Expected: x=10, Z=50
494
496 simplex.put_objetive_function_coef(0, 5.0);
497
498 double c1[] = {1.0, 10.0};
499 simplex.put_restriction(c1);
500
501 simplex.prepare_linear_program();
502 auto state = simplex.solve();
503
505
506 simplex.load_solution();
507 EXPECT_TRUE(nearly_equal(simplex.get_solution(0), 10.0));
508 EXPECT_TRUE(nearly_equal(simplex.objetive_value(), 50.0));
509 EXPECT_TRUE(simplex.verify_solution());
510}
511
513{
514 // Maximize Z = 0*x + 5*y
515 // Subject to:
516 // x + y <= 10
517 // y <= 10 (explicit bound on y)
518 // x, y >= 0
519 // Expected: y=10, x=0, Z=50
520
522 simplex.put_objetive_function_coef(0, 0.0);
523 simplex.put_objetive_function_coef(1, 5.0);
524
525 double c1[] = {1.0, 1.0, 10.0};
526 double c2[] = {0.0, 1.0, 10.0};
527 simplex.put_restriction(c1);
528 simplex.put_restriction(c2);
529
530 simplex.prepare_linear_program();
531 auto state = simplex.solve();
532
534
535 simplex.load_solution();
536 // Solution should maximize y within constraints
537 EXPECT_GE(simplex.objetive_value(), 0.0);
538}
539
541{
542 // Maximize Z = x + y
543 // Subject to:
544 // x <= 5
545 // y <= 5
546 // x + y <= 10
547 // Expected: x=5, y=5, Z=10
548
550 simplex.put_objetive_function_coef(0, 1.0);
551 simplex.put_objetive_function_coef(1, 1.0);
552
553 double c1[] = {1.0, 0.0, 5.0};
554 double c2[] = {0.0, 1.0, 5.0};
555 double c3[] = {1.0, 1.0, 10.0};
556
557 simplex.put_restriction(c1);
558 simplex.put_restriction(c2);
559 simplex.put_restriction(c3);
560
561 simplex.prepare_linear_program();
562 auto state = simplex.solve();
563
565
566 simplex.load_solution();
567 EXPECT_TRUE(nearly_equal(simplex.get_solution(0), 5.0));
568 EXPECT_TRUE(nearly_equal(simplex.get_solution(1), 5.0));
569 EXPECT_TRUE(nearly_equal(simplex.objetive_value(), 10.0));
570 EXPECT_TRUE(simplex.verify_solution());
571}
572
574{
575 // Maximize Z = x + y
576 // Subject to:
577 // x + y <= 10
578 // x <= 5
579 // y <= 5
580 // x, y >= 0
581
583 simplex.put_objetive_function_coef(0, 1.0);
584 simplex.put_objetive_function_coef(1, 1.0);
585
586 double c1[] = {1.0, 1.0, 10.0};
587 double c2[] = {1.0, 0.0, 5.0};
588 double c3[] = {0.0, 1.0, 5.0};
589
590 simplex.put_restriction(c1);
591 simplex.put_restriction(c2);
592 simplex.put_restriction(c3);
593
594 simplex.prepare_linear_program();
595 auto state = simplex.solve();
596
598
599 simplex.load_solution();
600 EXPECT_TRUE(nearly_equal(simplex.objetive_value(), 10.0));
601 EXPECT_TRUE(simplex.verify_solution());
602}
603
605{
606 // Maximize Z = 3x + 5y
607 // Subject to:
608 // x + y <= 4
609 // 2x + 3y <= 9
610 // x <= 4
611 // y <= 4
612 // Optimal: x=3, y=1, Z=14
613
615 simplex.put_objetive_function_coef(0, 3.0);
616 simplex.put_objetive_function_coef(1, 5.0);
617
618 double c1[] = {1.0, 1.0, 4.0};
619 double c2[] = {2.0, 3.0, 9.0};
620 double c3[] = {1.0, 0.0, 4.0};
621 double c4[] = {0.0, 1.0, 4.0};
622
623 simplex.put_restriction(c1);
624 simplex.put_restriction(c2);
625 simplex.put_restriction(c3);
626 simplex.put_restriction(c4);
627
628 simplex.prepare_linear_program();
629 auto state = simplex.solve();
630
632
633 simplex.load_solution();
634 EXPECT_TRUE(simplex.verify_solution());
635
636 // Optimal value should be around 14
637 EXPECT_GT(simplex.objetive_value(), 10.0);
638}
639
640//==============================================================================
641// Float Type Tests
642//==============================================================================
643
645{
646 // Same problem but with float instead of double
648 simplex.put_objetive_function_coef(0, 3.0f);
649 simplex.put_objetive_function_coef(1, 2.0f);
650
651 float c1[] = {1.0f, 1.0f, 4.0f};
652 float c2[] = {1.0f, 0.0f, 2.0f};
653
654 simplex.put_restriction(c1);
655 simplex.put_restriction(c2);
656
657 simplex.prepare_linear_program();
658 auto state = simplex.solve();
659
661
662 simplex.load_solution();
663 EXPECT_TRUE(simplex.verify_solution());
664}
665
666//==============================================================================
667// State Transition Tests
668//==============================================================================
669
671{
674
675 simplex.put_objetive_function_coef(0, 1.0);
676 simplex.put_objetive_function_coef(1, 1.0);
677
678 double c1[] = {1.0, 1.0, 10.0};
679 simplex.put_restriction(c1);
680
681 simplex.prepare_linear_program();
683
684 auto state = simplex.solve();
685
688}
689
690//==============================================================================
691// Verify Solution Tests
692//==============================================================================
693
695{
697 simplex.put_objetive_function_coef(0, 1.0);
698 simplex.put_objetive_function_coef(1, 1.0);
699
700 double c1[] = {1.0, 0.0, 5.0};
701 double c2[] = {0.0, 1.0, 5.0};
702
703 simplex.put_restriction(c1);
704 simplex.put_restriction(c2);
705
706 simplex.prepare_linear_program();
707 simplex.solve();
708 simplex.load_solution();
709
710 EXPECT_TRUE(simplex.verify_solution());
711}
712
713//==============================================================================
714// Accessor Tests
715//==============================================================================
716
718{
720 EXPECT_EQ(simplex.get_num_vars(), 5);
721}
722
724{
726 EXPECT_EQ(simplex.get_num_restrictions(), 0);
727
728 double c1[] = {1.0, 1.0, 4.0};
729 simplex.put_restriction(c1);
730 EXPECT_EQ(simplex.get_num_restrictions(), 1);
731
732 double c2[] = {2.0, 1.0, 6.0};
733 simplex.put_restriction(c2);
734 EXPECT_EQ(simplex.get_num_restrictions(), 2);
735}
736
737//==============================================================================
738// Large Problem Tests
739//==============================================================================
740
742{
743 // 5 variables, 5 constraints - each variable bounded
744 const int n = 5;
745
747
748 // Set objective: maximize sum of all variables
749 for (int i = 0; i < n; ++i)
750 simplex.put_objetive_function_coef(i, 1.0);
751
752 // Add constraints: each variable <= 1
753 for (int j = 0; j < n; ++j)
754 {
755 double* rest = simplex.put_restriction(nullptr);
756 for (int i = 0; i < n; ++i)
757 rest[i] = (i == j) ? 1.0 : 0.0;
758 rest[n] = 1.0; // RHS
759 }
760
761 simplex.prepare_linear_program();
762 auto state = simplex.solve();
763
765
766 simplex.load_solution();
767 EXPECT_TRUE(simplex.verify_solution());
768 EXPECT_TRUE(nearly_equal(simplex.objetive_value(), 5.0));
769}
770
771//==============================================================================
772// Numerical Stability Tests
773//==============================================================================
774
776{
778 simplex.put_objetive_function_coef(0, 0.001);
779 simplex.put_objetive_function_coef(1, 0.002);
780
781 double c1[] = {0.001, 0.001, 0.01};
782 double c2[] = {0.001, 0.0, 0.005};
783 double c3[] = {0.0, 0.001, 0.005};
784 simplex.put_restriction(c1);
785 simplex.put_restriction(c2);
786 simplex.put_restriction(c3);
787
788 simplex.prepare_linear_program();
789 auto state = simplex.solve();
790
792
793 simplex.load_solution();
794 // Just verify we get a valid solution
795 EXPECT_GE(simplex.objetive_value(), 0.0);
796}
797
799{
801 simplex.put_objetive_function_coef(0, 1000.0);
802 simplex.put_objetive_function_coef(1, 2000.0);
803
804 double c1[] = {1000.0, 1000.0, 10000.0};
805 double c2[] = {1000.0, 0.0, 5000.0};
806 double c3[] = {0.0, 1000.0, 5000.0};
807 simplex.put_restriction(c1);
808 simplex.put_restriction(c2);
809 simplex.put_restriction(c3);
810
811 simplex.prepare_linear_program();
812 auto state = simplex.solve();
813
815
816 simplex.load_solution();
817 EXPECT_TRUE(simplex.verify_solution());
818 // x=5, y=5, Z=15000
819 EXPECT_TRUE(nearly_equal(simplex.objetive_value(), 15000.0));
820}
821
822//==============================================================================
823// New Features Tests: Optimization Types, Constraints, Statistics
824//==============================================================================
825
827{
828 // Test that minimization mode is properly set and affects optimization
829 // Standard maximization problem for comparison
830 Simplex<double> simplex_max(2, OptimizationType::Maximize);
831 simplex_max.put_objetive_function_coef(0, 40.0);
832 simplex_max.put_objetive_function_coef(1, 30.0);
833
834 double c1[] = {1.0, 1.0, 40.0};
835 double c2[] = {2.0, 1.0, 60.0};
836 simplex_max.put_restriction(c1);
837 simplex_max.put_restriction(c2);
838
839 simplex_max.prepare_linear_program();
840 auto state_max = simplex_max.solve();
842
843 simplex_max.load_solution();
844 double max_value = simplex_max.objetive_value();
845
846 // Same problem but with minimization constructor
847 Simplex<double> simplex_min(2, OptimizationType::Minimize);
848 simplex_min.put_objetive_function_coef(0, 40.0);
849 simplex_min.put_objetive_function_coef(1, 30.0);
850
851 simplex_min.put_restriction(c1);
852 simplex_min.put_restriction(c2);
853
854 simplex_min.prepare_linear_program();
855 auto state_min = simplex_min.solve();
857
858 simplex_min.load_solution();
859 double min_value = simplex_min.objetive_value();
860
861 // The minimization should find a value <= maximization value
862 // (In this bounded problem, min is at origin: 0)
864
865 // Verify optimization types are correctly reported
866 EXPECT_EQ(simplex_max.get_optimization_type(), OptimizationType::Maximize);
867 EXPECT_EQ(simplex_min.get_optimization_type(), OptimizationType::Minimize);
868}
869
871{
872 // Maximize Z = 5x + 4y
873 // s.t. x + y = 10 (simulated with x + y <= 10 and -x - y <= -10)
874 // x <= 6
875 // y <= 8
876 // x, y >= 0
877 //
878 // Note: True equality constraints require Big-M or two-phase simplex.
879 // Here we test by using tight upper/lower bounds on the sum.
880
882 simplex.put_objetive_function_coef(0, 5.0);
883 simplex.put_objetive_function_coef(1, 4.0);
884
885 double c1[] = {1.0, 1.0, 10.0}; // x + y <= 10
886 double c2[] = {1.0, 0.0, 6.0}; // x <= 6
887 double c3[] = {0.0, 1.0, 8.0}; // y <= 8
888
889 simplex.put_restriction(c1);
890 simplex.put_restriction(c2);
891 simplex.put_restriction(c3);
892
893 simplex.prepare_linear_program();
894 auto state = simplex.solve();
895
897
898 simplex.load_solution();
899
900 // With x + y <= 10, maximize 5x + 4y
901 // Optimal: x=6, y=4, Z=46 (using full capacity of sum constraint)
902 EXPECT_NEAR(simplex.objetive_value(), 46.0, 0.1);
903}
904
906{
907 // Test that constraint types are properly stored
908 // Maximize Z = 2x + 3y with standard LE constraints
909
911 simplex.put_objetive_function_coef(0, 2.0);
912 simplex.put_objetive_function_coef(1, 3.0);
913
914 double c1[] = {1.0, 1.0, 10.0}; // x + y <= 10
915 double c2[] = {1.0, 0.0, 8.0}; // x <= 8
916 double c3[] = {0.0, 1.0, 6.0}; // y <= 6
917
918 simplex.put_restriction(c1, ConstraintType::LE);
919 simplex.put_restriction(c2, ConstraintType::LE);
920 simplex.put_restriction(c3, ConstraintType::LE);
921
922 simplex.prepare_linear_program();
923 auto state = simplex.solve();
924
926
927 simplex.load_solution();
928
929 // Optimal: x=4, y=6, Z=8+18=26 (limited by x+y<=10 and y<=6)
930 EXPECT_NEAR(simplex.objetive_value(), 26.0, 0.1);
931 EXPECT_LE(simplex.get_solution(0) + simplex.get_solution(1), 10.1);
932}
933
935{
937 simplex.put_objetive_function_coef(0, 40.0);
938 simplex.put_objetive_function_coef(1, 30.0);
939
940 double c1[] = {1.0, 1.0, 40.0};
941 double c2[] = {2.0, 1.0, 60.0};
942 simplex.put_restriction(c1);
943 simplex.put_restriction(c2);
944
945 simplex.prepare_linear_program();
946 simplex.solve();
947
948 auto stats = simplex.get_stats();
949
950 EXPECT_GT(stats.iterations, 0u);
951 EXPECT_GT(stats.pivots, 0u);
952 EXPECT_GE(stats.elapsed_ms, 0.0);
953}
954
956{
957 // Test that Bland's rule can be enabled and affects pivot selection
959 simplex.enable_bland_rule();
960
961 simplex.put_objetive_function_coef(0, 100.0);
962 simplex.put_objetive_function_coef(1, 10.0);
963 simplex.put_objetive_function_coef(2, 1.0);
964
965 double c1[] = {1.0, 0.0, 0.0, 1.0};
966 double c2[] = {20.0, 1.0, 0.0, 100.0};
967 double c3[] = {200.0, 20.0, 1.0, 10000.0};
968
969 simplex.put_restriction(c1);
970 simplex.put_restriction(c2);
971 simplex.put_restriction(c3);
972
973 simplex.prepare_linear_program();
974 auto state = simplex.solve();
975
977
978 // Verify solution is valid
979 simplex.load_solution();
980 EXPECT_TRUE(simplex.verify_solution());
981
982 // Optimal is x1=1, x2=80, x3=9800, Z=100+800+9800=10700
983 EXPECT_GT(simplex.objetive_value(), 0.0);
984}
985
987{
989 simplex.put_objetive_function_coef(0, 40.0);
990 simplex.put_objetive_function_coef(1, 30.0);
991
992 double c1[] = {1.0, 1.0, 40.0};
993 double c2[] = {2.0, 1.0, 60.0};
994 simplex.put_restriction(c1);
995 simplex.put_restriction(c2);
996
997 simplex.prepare_linear_program();
998 simplex.solve();
999 simplex.load_solution();
1000
1001 auto range = simplex.objective_sensitivity(0);
1002
1003 EXPECT_EQ(range.current_value, 40.0);
1004 // Range should be finite
1005 EXPECT_FALSE(std::isinf(range.lower_bound) && std::isinf(range.upper_bound));
1006}
1007
1009{
1011 simplex.put_objetive_function_coef(0, 40.0);
1012 simplex.put_objetive_function_coef(1, 30.0);
1013
1014 double c1[] = {1.0, 1.0, 40.0};
1015 double c2[] = {2.0, 1.0, 60.0};
1016 simplex.put_restriction(c1);
1017 simplex.put_restriction(c2);
1018
1019 simplex.prepare_linear_program();
1020 simplex.solve();
1021 simplex.load_solution();
1022
1023 auto prices = simplex.get_all_shadow_prices();
1024
1025 EXPECT_EQ(prices.size(), 2u);
1026 // Shadow prices should be non-negative for <= constraints in maximization
1027}
1028
1030{
1032 simplex.put_objetive_function_coef(0, 40.0);
1033 simplex.put_objetive_function_coef(1, 30.0);
1034
1035 double c1[] = {1.0, 1.0, 40.0};
1036 double c2[] = {2.0, 1.0, 60.0};
1037 simplex.put_restriction(c1);
1038 simplex.put_restriction(c2);
1039
1040 simplex.prepare_linear_program();
1041 simplex.solve();
1042 simplex.load_solution();
1043
1044 auto costs = simplex.get_all_reduced_costs();
1045
1046 EXPECT_EQ(costs.size(), 2u);
1047}
1048
1050{
1052 simplex.put_objetive_function_coef(0, 40.0);
1053 simplex.put_objetive_function_coef(1, 30.0);
1054
1055 double c1[] = {1.0, 1.0, 40.0};
1056 double c2[] = {2.0, 1.0, 60.0};
1057 simplex.put_restriction(c1);
1058 simplex.put_restriction(c2);
1059
1060 simplex.prepare_linear_program();
1061 auto state1 = simplex.solve();
1063
1064 simplex.load_solution();
1065 double original_obj = simplex.objetive_value();
1066
1067 // Update RHS of first constraint and reoptimize
1068 auto state2 = simplex.update_rhs_and_reoptimize(0, 50.0);
1069
1071 {
1072 simplex.load_solution();
1073 // New objective should be different (likely higher with more room)
1074 EXPECT_GE(simplex.objetive_value(), original_obj);
1075 EXPECT_TRUE(simplex.verify_solution());
1076 }
1077}
1078
1080{
1081 // Create a problem with many constraints that may have degenerate solutions
1082 const size_t n = 10;
1084 simplex.enable_bland_rule(); // Prevent cycling
1085
1086 // Objective: maximize sum of xi
1087 for (size_t i = 0; i < n; ++i)
1088 simplex.put_objetive_function_coef(i, 1.0);
1089
1090 // Constraints: each xi <= 1
1091 for (size_t i = 0; i < n; ++i)
1092 {
1093 DynArray<double> c(n + 1);
1094 for (size_t j = 0; j <= n; ++j)
1095 c[j] = 0.0;
1096 c[i] = 1.0;
1097 c[n] = 1.0;
1098 simplex.put_restriction(c);
1099 }
1100
1101 // Sum constraint: sum <= n/2
1102 DynArray<double> sum_c(n + 1);
1103 for (size_t i = 0; i < n; ++i)
1104 sum_c[i] = 1.0;
1105 sum_c[n] = static_cast<double>(n) / 2.0;
1106 simplex.put_restriction(sum_c);
1107
1108 simplex.prepare_linear_program();
1109 auto state = simplex.solve();
1110
1112
1113 simplex.load_solution();
1114 EXPECT_NEAR(simplex.objetive_value(), static_cast<double>(n) / 2.0, 0.1);
1115
1116 // Check for degenerate pivots
1117 auto stats = simplex.get_stats();
1118 // Some degenerate pivots are expected in this problem
1119 (void)stats; // Just check it doesn't crash
1120}
1121
1123{
1125
1126 EXPECT_EQ(simplex.get_optimization_type(), OptimizationType::Maximize);
1127
1128 simplex.set_minimize();
1129 EXPECT_EQ(simplex.get_optimization_type(), OptimizationType::Minimize);
1130
1131 simplex.set_maximize();
1132 EXPECT_EQ(simplex.get_optimization_type(), OptimizationType::Maximize);
1133}
1134
1136{
1138 simplex.put_objetive_function_coef(0, 40.0);
1139 simplex.put_objetive_function_coef(1, 30.0);
1140
1141 double c1[] = {1.0, 1.0, 40.0};
1142 double c2[] = {2.0, 1.0, 60.0};
1143 simplex.put_restriction(c1);
1144 simplex.put_restriction(c2);
1145
1146 simplex.prepare_linear_program();
1147 simplex.solve();
1148 simplex.load_solution();
1149
1150 // At least one variable should be basic
1151 bool has_basic = simplex.is_basic_variable(0) || simplex.is_basic_variable(1);
1153}
1154
1155//==============================================================================
1156// Revised Simplex Tests
1157//==============================================================================
1158
1159class RevisedSimplexTest : public ::testing::Test
1160{
1161protected:
1162 static constexpr double EPSILON = 1e-6;
1163
1164 bool nearly_equal(double a, double b, double eps = EPSILON)
1165 {
1166 return std::fabs(a - b) < eps;
1167 }
1168};
1169
1171{
1173
1174 EXPECT_EQ(simplex.get_num_vars(), 3);
1175 EXPECT_EQ(simplex.get_num_constraints(), 2);
1177}
1178
1180{
1181 // Maximize Z = 40x + 30y
1182 // s.t. x + y <= 40
1183 // 2x + y <= 60
1184
1186
1187 // Objective
1188 simplex.set_objective(0, 40.0);
1189 simplex.set_objective(1, 30.0);
1190
1191 // Constraints
1192 double c1[] = {1.0, 1.0};
1193 double c2[] = {2.0, 1.0};
1194 simplex.set_constraint_row(0, c1, 40.0);
1195 simplex.set_constraint_row(1, c2, 60.0);
1196
1197 auto state = simplex.solve();
1198
1200
1201 // Optimal: x=20, y=20, Z=1400
1202 EXPECT_TRUE(nearly_equal(simplex.get_solution(0), 20.0, 0.1));
1203 EXPECT_TRUE(nearly_equal(simplex.get_solution(1), 20.0, 0.1));
1204 EXPECT_TRUE(nearly_equal(simplex.objective_value(), 1400.0, 0.1));
1205
1206 EXPECT_TRUE(simplex.verify_solution());
1207}
1208
1210{
1211 // Maximize Z = 5x + 4y + 3z
1212 // s.t. 2x + 3y + z <= 5
1213 // 4x + 2y + 3z <= 11
1214 // 3x + 4y + 2z <= 8
1215
1217
1218 simplex.set_objective(0, 5.0);
1219 simplex.set_objective(1, 4.0);
1220 simplex.set_objective(2, 3.0);
1221
1222 double c1[] = {2.0, 3.0, 1.0};
1223 double c2[] = {4.0, 2.0, 3.0};
1224 double c3[] = {3.0, 4.0, 2.0};
1225
1226 simplex.set_constraint_row(0, c1, 5.0);
1227 simplex.set_constraint_row(1, c2, 11.0);
1228 simplex.set_constraint_row(2, c3, 8.0);
1229
1230 auto state = simplex.solve();
1231
1233 EXPECT_TRUE(simplex.verify_solution());
1234 EXPECT_GT(simplex.objective_value(), 0.0);
1235}
1236
1238{
1239 // Same problem solved by both algorithms
1240 // Maximize Z = 3x + 2y + 4z
1241 // s.t. x + y + 2z <= 4
1242 // 2x + y + z <= 5
1243
1244 // Standard Simplex
1246 std_simplex.put_objetive_function_coef(0, 3.0);
1247 std_simplex.put_objetive_function_coef(1, 2.0);
1248 std_simplex.put_objetive_function_coef(2, 4.0);
1249
1250 double c1[] = {1.0, 1.0, 2.0, 4.0};
1251 double c2[] = {2.0, 1.0, 1.0, 5.0};
1252 std_simplex.put_restriction(c1);
1253 std_simplex.put_restriction(c2);
1254
1255 std_simplex.prepare_linear_program();
1256 std_simplex.solve();
1257 std_simplex.load_solution();
1258 double std_obj = std_simplex.objetive_value();
1259
1260 // Revised Simplex
1262 rev_simplex.set_objective(0, 3.0);
1263 rev_simplex.set_objective(1, 2.0);
1264 rev_simplex.set_objective(2, 4.0);
1265
1266 double r1[] = {1.0, 1.0, 2.0};
1267 double r2[] = {2.0, 1.0, 1.0};
1268 rev_simplex.set_constraint_row(0, r1, 4.0);
1269 rev_simplex.set_constraint_row(1, r2, 5.0);
1270
1271 rev_simplex.solve();
1272 double rev_obj = rev_simplex.objective_value();
1273
1274 // Both should find same optimal value
1276}
1277
1279{
1280 // 10 variables, 5 constraints
1281 const size_t n = 10;
1282 const size_t m = 5;
1283
1285
1286 // Random but consistent coefficients
1287 for (size_t j = 0; j < n; ++j)
1288 simplex.set_objective(j, static_cast<double>((j + 1) * 3 % 7 + 1));
1289
1290 for (size_t i = 0; i < m; ++i)
1291 {
1292 for (size_t j = 0; j < n; ++j)
1293 simplex.set_constraint(i, j, static_cast<double>((i + j + 1) % 5 + 1));
1294 simplex.set_rhs(i, static_cast<double>((i + 1) * 20));
1295 }
1296
1297 auto state = simplex.solve();
1298
1300 EXPECT_TRUE(simplex.verify_solution());
1301 EXPECT_GT(simplex.objective_value(), 0.0);
1302}
1303
1305{
1307
1308 simplex.set_objective(0, 40.0);
1309 simplex.set_objective(1, 30.0);
1310
1311 double c1[] = {1.0, 1.0};
1312 double c2[] = {2.0, 1.0};
1313 simplex.set_constraint_row(0, c1, 40.0);
1314 simplex.set_constraint_row(1, c2, 60.0);
1315
1316 simplex.solve();
1317
1318 auto stats = simplex.get_stats();
1319
1320 EXPECT_GT(stats.iterations, 0u);
1321 EXPECT_GT(stats.pivots, 0u);
1322 EXPECT_GE(stats.elapsed_ms, 0.0);
1323}
1324
1326{
1327 // Compare performance on a moderate problem with well-defined coefficients
1328 const size_t n = 20; // 20 variables
1329 const size_t m = 10; // 10 constraints
1330
1331 // Setup problem with bounded coefficients
1332 std::vector<double> obj(n);
1333 std::vector<std::vector<double>> A(m, std::vector<double>(n));
1334 std::vector<double> b(m);
1335
1336 for (size_t j = 0; j < n; ++j)
1337 obj[j] = static_cast<double>(j % 5 + 1); // 1-5
1338
1339 for (size_t i = 0; i < m; ++i)
1340 {
1341 double row_sum = 0;
1342 for (size_t j = 0; j < n; ++j)
1343 {
1344 A[i][j] = static_cast<double>((i + j) % 3 + 1); // 1-3
1345 row_sum += A[i][j];
1346 }
1347 b[i] = row_sum * 2; // Ensure feasible region
1348 }
1349
1350 // Standard Simplex
1352 for (size_t j = 0; j < n; ++j)
1353 std_simplex.put_objetive_function_coef(j, obj[j]);
1354
1355 for (size_t i = 0; i < m; ++i)
1356 {
1357 DynArray<double> row(n + 1);
1358 for (size_t j = 0; j < n; ++j)
1359 row[j] = A[i][j];
1360 row[n] = b[i];
1361 std_simplex.put_restriction(row);
1362 }
1363
1364 std_simplex.prepare_linear_program();
1365 auto std_state = std_simplex.solve();
1366 std_simplex.load_solution();
1367 auto std_stats = std_simplex.get_stats();
1368 double std_obj = std_simplex.objetive_value();
1369
1370 // Revised Simplex
1372 rev_simplex.set_objective(obj.data());
1373 for (size_t i = 0; i < m; ++i)
1374 rev_simplex.set_constraint_row(i, A[i].data(), b[i]);
1375
1376 auto rev_state = rev_simplex.solve();
1377 auto rev_stats = rev_simplex.get_stats();
1378 double rev_obj = rev_simplex.objective_value();
1379
1380 // Both should solve successfully
1383
1384 // Both should find good solutions
1385 EXPECT_GT(std_obj, 0.0);
1386 EXPECT_GT(rev_obj, 0.0);
1387
1388 std::cout << "\n=== Performance Comparison (n=" << n << ", m=" << m << ") ===\n"
1389 << "Standard Simplex: " << std_stats.elapsed_ms << " ms, "
1390 << std_stats.pivots << " pivots, obj=" << std_obj << "\n"
1391 << "Revised Simplex: " << rev_stats.elapsed_ms << " ms, "
1392 << rev_stats.pivots << " pivots, obj=" << rev_obj << "\n";
1393}
1394
1395//==============================================================================
1396// Non-Standard Constraints Tests (>=, ==)
1397//==============================================================================
1398
1399class NonStandardConstraintsTest : public ::testing::Test
1400{
1401protected:
1402 static constexpr double EPSILON = 1e-6;
1403
1404 bool nearly_equal(double a, double b, double eps = EPSILON)
1405 {
1406 return std::fabs(a - b) < eps;
1407 }
1408};
1409
1411{
1412 // Maximize Z = 3x + 2y
1413 // Subject to:
1414 // x + y >= 4 (GE constraint)
1415 // x <= 6 (LE constraint)
1416 // y <= 5 (LE constraint)
1417 // x, y >= 0
1418 // Expected: x=6, y=5 (maximize on corner), but wait, that gives 28
1419 // Actually with x + y >= 4, we want to maximize, so push to upper bounds
1420 // x=6, y=5 satisfies x+y >= 4 (11 >= 4), x <= 6, y <= 5
1421 // Z = 3*6 + 2*5 = 18 + 10 = 28
1422
1424 simplex.put_objetive_function_coef(0, 3.0);
1425 simplex.put_objetive_function_coef(1, 2.0);
1426
1427 // GE constraint: x + y >= 4
1428 double c1[] = {1.0, 1.0, 4.0};
1429 simplex.put_restriction(c1, ConstraintType::GE);
1430
1431 // LE constraints
1432 double c2[] = {1.0, 0.0, 6.0};
1433 double c3[] = {0.0, 1.0, 5.0};
1434 simplex.put_restriction(c2, ConstraintType::LE);
1435 simplex.put_restriction(c3, ConstraintType::LE);
1436
1437 simplex.prepare_linear_program();
1438 auto state = simplex.solve();
1439
1441
1442 simplex.load_solution();
1443
1444 // Should maximize to x=6, y=5, Z=28
1445 EXPECT_TRUE(nearly_equal(simplex.get_solution(0), 6.0, 0.1));
1446 EXPECT_TRUE(nearly_equal(simplex.get_solution(1), 5.0, 0.1));
1447 EXPECT_TRUE(nearly_equal(simplex.objetive_value(), 28.0, 0.1));
1448 EXPECT_TRUE(simplex.verify_solution());
1449}
1450
1452{
1453 // Maximize Z = 5x + 4y
1454 // Subject to:
1455 // x + y = 10 (EQ constraint)
1456 // x <= 6 (LE constraint)
1457 // y <= 8 (LE constraint)
1458 // x, y >= 0
1459 // With x + y = 10, to maximize 5x + 4y, we want x as large as possible
1460 // x = 6 (bounded), then y = 4
1461 // Z = 5*6 + 4*4 = 30 + 16 = 46
1462
1464 simplex.put_objetive_function_coef(0, 5.0);
1465 simplex.put_objetive_function_coef(1, 4.0);
1466
1467 // EQ constraint: x + y = 10
1468 double c1[] = {1.0, 1.0, 10.0};
1469 simplex.put_restriction(c1, ConstraintType::EQ);
1470
1471 // LE constraints
1472 double c2[] = {1.0, 0.0, 6.0};
1473 double c3[] = {0.0, 1.0, 8.0};
1474 simplex.put_restriction(c2, ConstraintType::LE);
1475 simplex.put_restriction(c3, ConstraintType::LE);
1476
1477 simplex.prepare_linear_program();
1478 auto state = simplex.solve();
1479
1481
1482 simplex.load_solution();
1483
1484 // Optimal: x=6, y=4, Z=46
1485 EXPECT_TRUE(nearly_equal(simplex.get_solution(0), 6.0, 0.1));
1486 EXPECT_TRUE(nearly_equal(simplex.get_solution(1), 4.0, 0.1));
1487 EXPECT_TRUE(nearly_equal(simplex.objetive_value(), 46.0, 0.1));
1488 EXPECT_TRUE(simplex.verify_solution());
1489
1490 // Verify equality constraint is satisfied
1491 double sum = simplex.get_solution(0) + simplex.get_solution(1);
1492 EXPECT_TRUE(nearly_equal(sum, 10.0, 0.1));
1493}
1494
1496{
1497 // Maximize Z = 2x + 3y + z
1498 // Subject to:
1499 // x + y + z >= 5 (GE)
1500 // x + 2y <= 10 (LE)
1501 // 2x + y = 8 (EQ)
1502 // z <= 20 (LE) - Added to bound z
1503 // x, y, z >= 0
1504 //
1505 // Analysis: From 2x + y = 8, we get y = 8 - 2x
1506 // For y >= 0: x <= 4
1507 // For x >= 0: x >= 0
1508 // Substituting in x + 2y <= 10: x + 2(8-2x) <= 10 => x >= 2
1509 // So x ∈ [2, 4]
1510 // At x=2: y=4, then z >= 5 - 2 - 4 = -1, so z can be 0 to 20
1511 // Maximize: 2(2) + 3(4) + z = 16 + z, max at z=20 => Z = 36
1512
1514 simplex.put_objetive_function_coef(0, 2.0);
1515 simplex.put_objetive_function_coef(1, 3.0);
1516 simplex.put_objetive_function_coef(2, 1.0);
1517
1518 double c1[] = {1.0, 1.0, 1.0, 5.0}; // GE
1519 double c2[] = {1.0, 2.0, 0.0, 10.0}; // LE
1520 double c3[] = {2.0, 1.0, 0.0, 8.0}; // EQ
1521 double c4[] = {0.0, 0.0, 1.0, 20.0}; // z <= 20 (bound z)
1522
1523 simplex.put_restriction(c1, ConstraintType::GE);
1524 simplex.put_restriction(c2, ConstraintType::LE);
1525 simplex.put_restriction(c3, ConstraintType::EQ);
1526 simplex.put_restriction(c4, ConstraintType::LE);
1527
1528 simplex.prepare_linear_program();
1529 auto state = simplex.solve();
1530
1532
1533 simplex.load_solution();
1534 EXPECT_TRUE(simplex.verify_solution());
1535 EXPECT_GT(simplex.objetive_value(), 0.0);
1536}
1537
1539{
1540 // Maximize Z = x + y + z
1541 // Subject to:
1542 // x + y = 5
1543 // y + z = 6
1544 // x, y, z >= 0
1545 // From eq1: x = 5 - y
1546 // From eq2: z = 6 - y
1547 // Z = (5-y) + y + (6-y) = 11 - y
1548 // To maximize, we want y as small as possible (y=0)
1549 // But we need x, z >= 0: x = 5-y >= 0 => y <= 5
1550 // z = 6-y >= 0 => y <= 6
1551 // So y can be 0, giving x=5, z=6, Z=11
1552
1554 simplex.put_objetive_function_coef(0, 1.0);
1555 simplex.put_objetive_function_coef(1, 1.0);
1556 simplex.put_objetive_function_coef(2, 1.0);
1557
1558 double c1[] = {1.0, 1.0, 0.0, 5.0};
1559 double c2[] = {0.0, 1.0, 1.0, 6.0};
1560
1561 simplex.put_restriction(c1, ConstraintType::EQ);
1562 simplex.put_restriction(c2, ConstraintType::EQ);
1563
1564 simplex.prepare_linear_program();
1565 auto state = simplex.solve();
1566
1568
1569 simplex.load_solution();
1570
1571 double x = simplex.get_solution(0);
1572 double y = simplex.get_solution(1);
1573 double z = simplex.get_solution(2);
1574
1575 // Verify equality constraints
1576 EXPECT_TRUE(nearly_equal(x + y, 5.0, 0.1));
1577 EXPECT_TRUE(nearly_equal(y + z, 6.0, 0.1));
1578
1579 EXPECT_TRUE(simplex.verify_solution());
1580}
1581
1583{
1584 // Maximize Z = x + y
1585 // Subject to:
1586 // x + y >= 10 (GE)
1587 // x <= 3 (LE)
1588 // y <= 4 (LE)
1589 // x, y >= 0
1590 // This is infeasible: x + y >= 10 but x <= 3 and y <= 4 => x + y <= 7
1591 //
1592 // NOTE: This tests whether the Simplex correctly detects infeasibility
1593 // with mixed GE/LE constraints. If the implementation doesn't detect
1594 // infeasibility correctly with GE constraints via Big-M method,
1595 // verify_solution() should still fail.
1596
1598 simplex.put_objetive_function_coef(0, 1.0);
1599 simplex.put_objetive_function_coef(1, 1.0);
1600
1601 double c1[] = {1.0, 1.0, 10.0};
1602 double c2[] = {1.0, 0.0, 3.0};
1603 double c3[] = {0.0, 1.0, 4.0};
1604
1605 simplex.put_restriction(c1, ConstraintType::GE);
1606 simplex.put_restriction(c2, ConstraintType::LE);
1607 simplex.put_restriction(c3, ConstraintType::LE);
1608
1609 simplex.prepare_linear_program();
1610 auto state = simplex.solve();
1611
1612 // The problem is mathematically infeasible
1613 // Either the solver correctly reports Unfeasible, or if it reports Solved,
1614 // the verify_solution() must fail (proving the "solution" violates constraints)
1615 if (state == Simplex<double>::Solved)
1616 {
1617 simplex.load_solution();
1618 double x = simplex.get_solution(0);
1619 double y = simplex.get_solution(1);
1620
1621 // Either verification fails, or the solution actually violates constraints
1622 bool ge_satisfied = (x + y >= 10.0 - EPSILON);
1623 bool le1_satisfied = (x <= 3.0 + EPSILON);
1624 bool le2_satisfied = (y <= 4.0 + EPSILON);
1625
1626 // At least one constraint must be violated since problem is infeasible
1627 // If all appear satisfied, then there's a numerical tolerance issue
1628 // or the implementation has a bug
1630 {
1631 // This would indicate a bug - log it for debugging
1632 std::cout << "WARNING: Simplex returned 'Solved' for infeasible problem\n";
1633 std::cout << " x=" << x << ", y=" << y << ", x+y=" << (x+y) << "\n";
1634 }
1635
1636 // The GE constraint cannot be satisfied given the LE bounds
1638 << "All constraints appear satisfied but problem is mathematically infeasible";
1639 }
1640 else
1641 {
1643 }
1644}
1645
1646//==============================================================================
1647// Minimization Tests
1648//==============================================================================
1649
1650class MinimizationTest : public ::testing::Test
1651{
1652protected:
1653 static constexpr double EPSILON = 1e-6;
1654
1655 bool nearly_equal(double a, double b, double eps = EPSILON)
1656 {
1657 return std::fabs(a - b) < eps;
1658 }
1659};
1660
1662{
1663 // Minimize Z = 2x + 3y
1664 // Subject to:
1665 // x + y >= 4
1666 // x + 2y >= 6
1667 // x, y >= 0
1668 // To minimize cost, find the corner of feasible region closest to origin
1669 // Corners: intersection of x+y=4 and x+2y=6
1670 // From eq1: x = 4 - y
1671 // Substitute: (4-y) + 2y = 6 => 4 + y = 6 => y = 2, x = 2
1672 // Z = 2*2 + 3*2 = 4 + 6 = 10
1673
1674 Simplex<double> simplex(2, OptimizationType::Minimize);
1675 simplex.put_objetive_function_coef(0, 2.0);
1676 simplex.put_objetive_function_coef(1, 3.0);
1677
1678 double c1[] = {1.0, 1.0, 4.0};
1679 double c2[] = {1.0, 2.0, 6.0};
1680
1681 simplex.put_restriction(c1, ConstraintType::GE);
1682 simplex.put_restriction(c2, ConstraintType::GE);
1683
1684 simplex.prepare_linear_program();
1685 auto state = simplex.solve();
1686
1688
1689 simplex.load_solution();
1690
1691 EXPECT_TRUE(nearly_equal(simplex.get_solution(0), 2.0, 0.1));
1692 EXPECT_TRUE(nearly_equal(simplex.get_solution(1), 2.0, 0.1));
1693 EXPECT_TRUE(nearly_equal(simplex.objetive_value(), 10.0, 0.1));
1694 EXPECT_TRUE(simplex.verify_solution());
1695}
1696
1698{
1699 // Classic diet problem (minimize cost)
1700 // Minimize Z = 3x + 5y (cost per serving)
1701 // Subject to:
1702 // 2x + y >= 10 (protein requirement)
1703 // x + 2y >= 8 (vitamin requirement)
1704 // x, y >= 0
1705
1706 Simplex<double> simplex(2, OptimizationType::Minimize);
1707 simplex.put_objetive_function_coef(0, 3.0);
1708 simplex.put_objetive_function_coef(1, 5.0);
1709
1710 double c1[] = {2.0, 1.0, 10.0};
1711 double c2[] = {1.0, 2.0, 8.0};
1712
1713 simplex.put_restriction(c1, ConstraintType::GE);
1714 simplex.put_restriction(c2, ConstraintType::GE);
1715
1716 simplex.prepare_linear_program();
1717 auto state = simplex.solve();
1718
1720
1721 simplex.load_solution();
1722
1723 double x = simplex.get_solution(0);
1724 double y = simplex.get_solution(1);
1725
1726 // Verify constraints are satisfied
1727 EXPECT_GE(2*x + y, 10.0 - EPSILON);
1728 EXPECT_GE(x + 2*y, 8.0 - EPSILON);
1729
1730 EXPECT_TRUE(simplex.verify_solution());
1731 EXPECT_GT(simplex.objetive_value(), 0.0);
1732}
1733
1735{
1736 // Minimize Z = 4x1 + 2x2 + 3x3 + x4
1737 // Subject to:
1738 // x1 + x2 >= 10 (demand at location 1)
1739 // x3 + x4 >= 15 (demand at location 2)
1740 // x1 + x3 <= 20 (supply from warehouse 1)
1741 // x2 + x4 <= 20 (supply from warehouse 2)
1742 // all xi >= 0
1743
1744 Simplex<double> simplex(4, OptimizationType::Minimize);
1745 simplex.put_objetive_function_coef(0, 4.0);
1746 simplex.put_objetive_function_coef(1, 2.0);
1747 simplex.put_objetive_function_coef(2, 3.0);
1748 simplex.put_objetive_function_coef(3, 1.0);
1749
1750 double c1[] = {1.0, 1.0, 0.0, 0.0, 10.0}; // GE
1751 double c2[] = {0.0, 0.0, 1.0, 1.0, 15.0}; // GE
1752 double c3[] = {1.0, 0.0, 1.0, 0.0, 20.0}; // LE
1753 double c4[] = {0.0, 1.0, 0.0, 1.0, 20.0}; // LE
1754
1755 simplex.put_restriction(c1, ConstraintType::GE);
1756 simplex.put_restriction(c2, ConstraintType::GE);
1757 simplex.put_restriction(c3, ConstraintType::LE);
1758 simplex.put_restriction(c4, ConstraintType::LE);
1759
1760 simplex.prepare_linear_program();
1761 auto state = simplex.solve();
1762
1764
1765 simplex.load_solution();
1766 EXPECT_TRUE(simplex.verify_solution());
1767
1768 // Cost should be minimized (use cheaper routes)
1769 double cost = simplex.objetive_value();
1770 EXPECT_GT(cost, 0.0);
1771 EXPECT_LT(cost, 100.0); // Should be reasonable
1772}
1773
1775{
1776 // Test using set_minimize() method
1777 Simplex<double> simplex(2); // Default is Maximize
1778
1779 simplex.set_minimize();
1780 EXPECT_EQ(simplex.get_optimization_type(), OptimizationType::Minimize);
1781
1782 simplex.put_objetive_function_coef(0, 5.0);
1783 simplex.put_objetive_function_coef(1, 4.0);
1784
1785 double c1[] = {1.0, 1.0, 8.0};
1786 simplex.put_restriction(c1, ConstraintType::GE);
1787
1788 simplex.prepare_linear_program();
1789 auto state = simplex.solve();
1790
1792 simplex.load_solution();
1793
1794 // Should minimize to a corner satisfying x + y >= 8
1795 // Optimal would be on the line x + y = 8, minimizing 5x + 4y
1796 // To minimize, we want more of y (cheaper): x=0, y=8 gives Z=32
1797 double x = simplex.get_solution(0);
1798 double y = simplex.get_solution(1);
1799
1800 EXPECT_GE(x + y, 8.0 - EPSILON);
1801 EXPECT_TRUE(simplex.verify_solution());
1802}
1803
1805{
1806 // Minimize Z = x + 2y + 3z
1807 // Subject to:
1808 // x + y + z = 10
1809 // x + z >= 4
1810 // y >= 2
1811
1812 Simplex<double> simplex(3, OptimizationType::Minimize);
1813 simplex.put_objetive_function_coef(0, 1.0);
1814 simplex.put_objetive_function_coef(1, 2.0);
1815 simplex.put_objetive_function_coef(2, 3.0);
1816
1817 double c1[] = {1.0, 1.0, 1.0, 10.0};
1818 double c2[] = {1.0, 0.0, 1.0, 4.0};
1819 double c3[] = {0.0, 1.0, 0.0, 2.0};
1820
1821 simplex.put_restriction(c1, ConstraintType::EQ);
1822 simplex.put_restriction(c2, ConstraintType::GE);
1823 simplex.put_restriction(c3, ConstraintType::GE);
1824
1825 simplex.prepare_linear_program();
1826 auto state = simplex.solve();
1827
1829
1830 simplex.load_solution();
1831
1832 double x = simplex.get_solution(0);
1833 double y = simplex.get_solution(1);
1834 double z = simplex.get_solution(2);
1835
1836 // Verify constraints
1837 EXPECT_TRUE(nearly_equal(x + y + z, 10.0, 0.1));
1838 EXPECT_GE(x + z, 4.0 - EPSILON);
1839 EXPECT_GE(y, 2.0 - EPSILON);
1840
1841 EXPECT_TRUE(simplex.verify_solution());
1842}
1843
1845{
1846 // Verify that Minimize(c·x) = -Maximize(-c·x)
1847
1848 // Problem: Minimize 3x + 2y subject to x + y >= 5, x,y >= 0
1849 Simplex<double> simplex_min(2, OptimizationType::Minimize);
1850 simplex_min.put_objetive_function_coef(0, 3.0);
1851 simplex_min.put_objetive_function_coef(1, 2.0);
1852
1853 double c1[] = {1.0, 1.0, 5.0};
1854 simplex_min.put_restriction(c1, ConstraintType::GE);
1855
1856 simplex_min.prepare_linear_program();
1857 simplex_min.solve();
1858 simplex_min.load_solution();
1859
1860 double min_value = simplex_min.objetive_value();
1861
1862 // Same problem as maximization with negated coefficients
1863 Simplex<double> simplex_max(2, OptimizationType::Maximize);
1864 simplex_max.put_objetive_function_coef(0, -3.0);
1865 simplex_max.put_objetive_function_coef(1, -2.0);
1866
1867 simplex_max.put_restriction(c1, ConstraintType::GE);
1868
1869 simplex_max.prepare_linear_program();
1870 simplex_max.solve();
1871 simplex_max.load_solution();
1872
1873 double max_value = simplex_max.objetive_value();
1874
1875 // They should be negatives of each other
1876 EXPECT_TRUE(nearly_equal(min_value, -max_value, 0.1));
1877}
1878
1879//==============================================================================
1880// Main
1881//==============================================================================
1882
1883int main(int argc, char **argv)
1884{
1885 ::testing::InitGoogleTest(&argc, argv);
1886 return RUN_ALL_TESTS();
1887}
Simplex algorithm for linear programming.
TEST_F(SimplexTest, ConstructorBasic)
int main()
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
Revised Simplex algorithm for linear programming.
Definition Simplex.H:1744
Linear program solver using the Simplex method.
Definition Simplex.H:227
bool nearly_equal(double a, double b, double eps=EPSILON)
static constexpr double EPSILON
bool nearly_equal(double a, double b, double eps=EPSILON)
static constexpr double EPSILON
static constexpr double EPSILON
bool nearly_equal(double a, double b, double eps=EPSILON)
bool nearly_equal(double a, double b, double eps=EPSILON)
static constexpr double EPSILON
constexpr double EPSILON
static mpfr_t y
Definition mpfr_mul_d.c:3
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
auto min_value(const Container &data) -> std::decay_t< decltype(*std::begin(data))>
Compute minimum value.
Definition stat_utils.H:272
Container< T > range(const T start, const T end, const T step=1)
Generate a range of values [start, end] with a given step.
auto max_value(const Container &data) -> std::decay_t< decltype(*std::begin(data))>
Compute maximum value.
Definition stat_utils.H:296
DynList< T > maps(const C &c, Op op)
Classic map operation.
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.