• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stillwater-sc / universal / 22588398771

02 Mar 2026 05:08PM UTC coverage: 84.059% (-0.2%) from 84.241%
22588398771

Pull #532

github

web-flow
Merge 30e619f50 into 401df414e
Pull Request #532: test(pop): add edge-case tests for ~90% coverage

428 of 567 new or added lines in 5 files covered. (75.49%)

3 existing lines in 1 file now uncovered.

41446 of 49306 relevant lines covered (84.06%)

6383019.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.99
/mixedprecision/pop/test_simplex.cpp
1
// test_simplex.cpp: validate the embedded simplex LP solver
2
//
3
// Copyright (C) 2017 Stillwater Supercomputing, Inc.
4
// SPDX-License-Identifier: MIT
5
//
6
// This file is part of the universal numbers project.
7

8
#include <universal/utility/directives.hpp>
9
#include <universal/mixedprecision/simplex.hpp>
10
#include <iostream>
11
#include <string>
12
#include <cmath>
13

14
namespace sw { namespace universal {
15

16
// Test 1: simple 2-variable LP
17
// minimize  x + y
18
// subject to  x >= 3
19
//             y >= 5
20
// Solution: x=3, y=5, objective=8
21
int TestSimple2Var() {
1✔
22
        int nrOfFailedTestCases = 0;
1✔
23

24
        SimplexSolver lp;
1✔
25
        lp.set_num_vars(2);
1✔
26
        lp.set_objective({1.0, 1.0});
2✔
27

28
        lp.add_ge_constraint({1.0, 0.0}, 3.0);
2✔
29
        lp.add_ge_constraint({0.0, 1.0}, 5.0);
2✔
30

31
        LPStatus status = lp.solve();
1✔
32
        if (status != LPStatus::Optimal) {
1✔
33
                std::cerr << "FAIL: expected Optimal, got " << to_string(status) << std::endl;
×
34
                ++nrOfFailedTestCases;
×
35
                return nrOfFailedTestCases;
×
36
        }
37

38
        double x = lp.get_value(0);
1✔
39
        double y = lp.get_value(1);
1✔
40

41
        if (std::abs(x - 3.0) > 0.01) {
1✔
42
                std::cerr << "FAIL: expected x=3, got " << x << std::endl;
×
43
                ++nrOfFailedTestCases;
×
44
        }
45
        if (std::abs(y - 5.0) > 0.01) {
1✔
46
                std::cerr << "FAIL: expected y=5, got " << y << std::endl;
×
47
                ++nrOfFailedTestCases;
×
48
        }
49
        if (std::abs(lp.objective_value() - 8.0) > 0.01) {
1✔
50
                std::cerr << "FAIL: expected obj=8, got " << lp.objective_value() << std::endl;
×
51
                ++nrOfFailedTestCases;
×
52
        }
53

54
        return nrOfFailedTestCases;
1✔
55
}
1✔
56

57
// Test 2: LP with relational constraints
58
// minimize  2x + 3y
59
// subject to  x + y >= 10
60
//             x >= 2
61
//             y >= 3
62
// Solution: x=7, y=3, objective=23
63
int TestRelational() {
1✔
64
        int nrOfFailedTestCases = 0;
1✔
65

66
        SimplexSolver lp;
1✔
67
        lp.set_num_vars(2);
1✔
68
        lp.set_objective({2.0, 3.0});
2✔
69

70
        lp.add_ge_constraint({1.0, 1.0}, 10.0);
2✔
71
        lp.add_ge_constraint({1.0, 0.0}, 2.0);
2✔
72
        lp.add_ge_constraint({0.0, 1.0}, 3.0);
2✔
73

74
        LPStatus status = lp.solve();
1✔
75
        if (status != LPStatus::Optimal) {
1✔
76
                std::cerr << "FAIL: expected Optimal, got " << to_string(status) << std::endl;
×
77
                ++nrOfFailedTestCases;
×
78
                return nrOfFailedTestCases;
×
79
        }
80

81
        double x = lp.get_value(0);
1✔
82
        double y = lp.get_value(1);
1✔
83

84
        if (std::abs(x - 7.0) > 0.01) {
1✔
85
                std::cerr << "FAIL: expected x=7, got " << x << std::endl;
×
86
                ++nrOfFailedTestCases;
×
87
        }
88
        if (std::abs(y - 3.0) > 0.01) {
1✔
89
                std::cerr << "FAIL: expected y=3, got " << y << std::endl;
×
90
                ++nrOfFailedTestCases;
×
91
        }
92

93
        return nrOfFailedTestCases;
1✔
94
}
1✔
95

96
// Test 3: constraints that mimic POP transfer functions
97
// minimize  a + b + z
98
// subject to  a - z >= 1    (backward mul: nsb(a) >= nsb(z) + 1)
99
//             b - z >= 1    (backward mul: nsb(b) >= nsb(z) + 1)
100
//             z >= 10       (output requirement)
101
// Solution: a=11, b=11, z=10, objective=32
102
int TestPopLikeConstraints() {
1✔
103
        int nrOfFailedTestCases = 0;
1✔
104

105
        SimplexSolver lp;
1✔
106
        lp.set_num_vars(3);
1✔
107
        lp.set_objective({1.0, 1.0, 1.0});
2✔
108

109
        // a >= z + 1  =>  a - z >= 1
110
        lp.add_ge_constraint({1.0, 0.0, -1.0}, 1.0);
2✔
111
        // b >= z + 1  =>  b - z >= 1
112
        lp.add_ge_constraint({0.0, 1.0, -1.0}, 1.0);
2✔
113
        // z >= 10
114
        lp.add_ge_constraint({0.0, 0.0, 1.0}, 10.0);
2✔
115
        // a,b,z >= 1
116
        lp.add_ge_constraint({1.0, 0.0, 0.0}, 1.0);
2✔
117
        lp.add_ge_constraint({0.0, 1.0, 0.0}, 1.0);
2✔
118

119
        LPStatus status = lp.solve();
1✔
120
        if (status != LPStatus::Optimal) {
1✔
121
                std::cerr << "FAIL: expected Optimal, got " << to_string(status) << std::endl;
×
122
                ++nrOfFailedTestCases;
×
123
                return nrOfFailedTestCases;
×
124
        }
125

126
        double a = lp.get_value(0);
1✔
127
        double b = lp.get_value(1);
1✔
128
        double z = lp.get_value(2);
1✔
129

130
        if (std::abs(a - 11.0) > 0.01) {
1✔
131
                std::cerr << "FAIL: expected a=11, got " << a << std::endl;
×
132
                ++nrOfFailedTestCases;
×
133
        }
134
        if (std::abs(b - 11.0) > 0.01) {
1✔
135
                std::cerr << "FAIL: expected b=11, got " << b << std::endl;
×
136
                ++nrOfFailedTestCases;
×
137
        }
138
        if (std::abs(z - 10.0) > 0.01) {
1✔
139
                std::cerr << "FAIL: expected z=10, got " << z << std::endl;
×
140
                ++nrOfFailedTestCases;
×
141
        }
142

143
        return nrOfFailedTestCases;
1✔
144
}
1✔
145

146
// Test 4: 3-variable LP with mixed constraints
147
// minimize  x + y + z
148
// subject to  x + y >= 5
149
//             y + z >= 7
150
//             x >= 1
151
//             z >= 1
152
// Solution: x=1, y=4, z=3, objective=8
153
int TestThreeVar() {
1✔
154
        int nrOfFailedTestCases = 0;
1✔
155

156
        SimplexSolver lp;
1✔
157
        lp.set_num_vars(3);
1✔
158
        lp.set_objective({1.0, 1.0, 1.0});
2✔
159

160
        lp.add_ge_constraint({1.0, 1.0, 0.0}, 5.0);
2✔
161
        lp.add_ge_constraint({0.0, 1.0, 1.0}, 7.0);
2✔
162
        lp.add_ge_constraint({1.0, 0.0, 0.0}, 1.0);
2✔
163
        lp.add_ge_constraint({0.0, 0.0, 1.0}, 1.0);
2✔
164

165
        LPStatus status = lp.solve();
1✔
166
        if (status != LPStatus::Optimal) {
1✔
167
                std::cerr << "FAIL: expected Optimal, got " << to_string(status) << std::endl;
×
168
                ++nrOfFailedTestCases;
×
169
                return nrOfFailedTestCases;
×
170
        }
171

172
        double x = lp.get_value(0);
1✔
173
        double y = lp.get_value(1);
1✔
174
        double z = lp.get_value(2);
1✔
175
        double obj = lp.objective_value();
1✔
176

177
        // x=1, y=4, z=3
178
        if (std::abs(obj - 8.0) > 0.01) {
1✔
179
                std::cerr << "FAIL: expected obj=8, got " << obj
×
180
                          << " (x=" << x << ", y=" << y << ", z=" << z << ")" << std::endl;
×
181
                ++nrOfFailedTestCases;
×
182
        }
183

184
        return nrOfFailedTestCases;
1✔
185
}
1✔
186

187
// Test 5: Infeasible LP
188
// minimize  x + y
189
// subject to  x + y >= 10
190
//             x + y <= 5
191
// No solution exists
192
int TestInfeasible() {
1✔
193
        int nrOfFailedTestCases = 0;
1✔
194

195
        SimplexSolver lp;
1✔
196
        lp.set_num_vars(2);
1✔
197
        lp.set_objective({1.0, 1.0});
2✔
198

199
        lp.add_ge_constraint({1.0, 1.0}, 10.0);
2✔
200
        lp.add_le_constraint({1.0, 1.0}, 5.0);
2✔
201

202
        LPStatus status = lp.solve();
1✔
203
        if (status != LPStatus::Infeasible) {
1✔
NEW
204
                std::cerr << "FAIL: expected Infeasible, got " << to_string(status) << std::endl;
×
NEW
205
                ++nrOfFailedTestCases;
×
206
        }
207

208
        // objective_value should be NaN for non-optimal
209
        if (!std::isnan(lp.objective_value())) {
1✔
NEW
210
                std::cerr << "FAIL: expected NaN objective for non-optimal, got " << lp.objective_value() << std::endl;
×
NEW
211
                ++nrOfFailedTestCases;
×
212
        }
213

214
        return nrOfFailedTestCases;
1✔
215
}
1✔
216

217
// Test 6: Unbounded LP
218
// minimize  -x (maximize x)
219
// subject to  x >= 1
220
// No upper bound -> unbounded
221
int TestUnbounded() {
1✔
222
        int nrOfFailedTestCases = 0;
1✔
223

224
        SimplexSolver lp;
1✔
225
        lp.set_num_vars(1);
1✔
226
        lp.set_objective({-1.0}); // minimize -x = maximize x
2✔
227

228
        lp.add_ge_constraint({1.0}, 1.0); // x >= 1, but no upper bound
2✔
229

230
        LPStatus status = lp.solve();
1✔
231
        if (status != LPStatus::Unbounded) {
1✔
NEW
232
                std::cerr << "FAIL: expected Unbounded, got " << to_string(status) << std::endl;
×
NEW
233
                ++nrOfFailedTestCases;
×
234
        }
235

236
        return nrOfFailedTestCases;
1✔
237
}
1✔
238

239
// Test 7: MaxIterations
240
// minimize  x + y
241
// subject to  x >= 3, y >= 5
242
// Solve with max_iterations=1 to force early termination
243
int TestMaxIterations() {
1✔
244
        int nrOfFailedTestCases = 0;
1✔
245

246
        SimplexSolver lp;
1✔
247
        lp.set_num_vars(2);
1✔
248
        lp.set_objective({1.0, 1.0});
2✔
249

250
        lp.add_ge_constraint({1.0, 0.0}, 3.0);
2✔
251
        lp.add_ge_constraint({0.0, 1.0}, 5.0);
2✔
252

253
        LPStatus status = lp.solve(1); // only 1 iteration
1✔
254
        if (status == LPStatus::Optimal) {
1✔
255
                // Small problem might solve in 1 iteration; that's OK
NEW
256
                std::cout << "  (small LP solved in 1 iteration -- OK)\n";
×
257
        } else if (status == LPStatus::MaxIterations) {
1✔
258
                std::cout << "  (MaxIterations as expected)\n";
1✔
259
        } else {
NEW
260
                std::cerr << "FAIL: expected Optimal or MaxIterations, got " << to_string(status) << std::endl;
×
NEW
261
                ++nrOfFailedTestCases;
×
262
        }
263

264
        return nrOfFailedTestCases;
1✔
265
}
1✔
266

267
// Test 8: Empty LP (no constraints or variables)
268
int TestEmptyLP() {
1✔
269
        int nrOfFailedTestCases = 0;
1✔
270

271
        // Zero variables
272
        {
273
                SimplexSolver lp;
1✔
274
                lp.set_num_vars(0);
1✔
275
                LPStatus status = lp.solve();
1✔
276
                if (status == LPStatus::Optimal) {
1✔
NEW
277
                        std::cerr << "FAIL: empty LP should not be Optimal" << std::endl;
×
NEW
278
                        ++nrOfFailedTestCases;
×
279
                }
280
        }
1✔
281

282
        // Variables but no constraints
283
        {
284
                SimplexSolver lp;
1✔
285
                lp.set_num_vars(2);
1✔
286
                lp.set_objective({1.0, 1.0});
2✔
287
                LPStatus status = lp.solve();
1✔
288
                // No constraints -> m=0, should return early
289
                if (status == LPStatus::Optimal) {
1✔
NEW
290
                        std::cerr << "FAIL: LP with no constraints should not be Optimal" << std::endl;
×
NEW
291
                        ++nrOfFailedTestCases;
×
292
                }
293
        }
1✔
294

295
        return nrOfFailedTestCases;
1✔
296
}
297

298
// Test 9: Single variable LP
299
// minimize  x  subject to  x >= 7
300
// Solution: x=7
301
int TestSingleVar() {
1✔
302
        int nrOfFailedTestCases = 0;
1✔
303

304
        SimplexSolver lp;
1✔
305
        lp.set_num_vars(1);
1✔
306
        lp.set_objective({1.0});
2✔
307
        lp.add_ge_constraint({1.0}, 7.0);
2✔
308

309
        LPStatus status = lp.solve();
1✔
310
        if (status != LPStatus::Optimal) {
1✔
NEW
311
                std::cerr << "FAIL: single-var expected Optimal, got " << to_string(status) << std::endl;
×
NEW
312
                ++nrOfFailedTestCases;
×
NEW
313
                return nrOfFailedTestCases;
×
314
        }
315

316
        if (std::abs(lp.get_value(0) - 7.0) > 0.01) {
1✔
NEW
317
                std::cerr << "FAIL: single-var expected x=7, got " << lp.get_value(0) << std::endl;
×
NEW
318
                ++nrOfFailedTestCases;
×
319
        }
320

321
        return nrOfFailedTestCases;
1✔
322
}
1✔
323

324
// Test 10: <= constraint and eq constraint
325
// minimize  x + y
326
// subject to  x + y == 10
327
//             x >= 3
328
// Solution: x=3, y=7
329
int TestLeAndEqConstraints() {
1✔
330
        int nrOfFailedTestCases = 0;
1✔
331

332
        SimplexSolver lp;
1✔
333
        lp.set_num_vars(2);
1✔
334
        lp.set_objective({1.0, 1.0});
2✔
335

336
        lp.add_eq_constraint({1.0, 1.0}, 10.0); // x + y == 10
2✔
337
        lp.add_ge_constraint({1.0, 0.0}, 3.0);  // x >= 3
2✔
338

339
        LPStatus status = lp.solve();
1✔
340
        if (status != LPStatus::Optimal) {
1✔
NEW
341
                std::cerr << "FAIL: eq constraint LP expected Optimal, got " << to_string(status) << std::endl;
×
NEW
342
                ++nrOfFailedTestCases;
×
NEW
343
                return nrOfFailedTestCases;
×
344
        }
345

346
        double x = lp.get_value(0);
1✔
347
        double y = lp.get_value(1);
1✔
348

349
        if (std::abs(x - 3.0) > 0.01) {
1✔
NEW
350
                std::cerr << "FAIL: eq LP expected x=3, got " << x << std::endl;
×
NEW
351
                ++nrOfFailedTestCases;
×
352
        }
353
        if (std::abs(y - 7.0) > 0.01) {
1✔
NEW
354
                std::cerr << "FAIL: eq LP expected y=7, got " << y << std::endl;
×
NEW
355
                ++nrOfFailedTestCases;
×
356
        }
357
        if (std::abs(lp.objective_value() - 10.0) > 0.01) {
1✔
NEW
358
                std::cerr << "FAIL: eq LP expected obj=10, got " << lp.objective_value() << std::endl;
×
NEW
359
                ++nrOfFailedTestCases;
×
360
        }
361

362
        return nrOfFailedTestCases;
1✔
363
}
1✔
364

365
// Test 11: Negative RHS normalization
366
// minimize  x + y
367
// subject to  -x - y >= -10  (equivalent to x + y <= 10)
368
//             x >= 3
369
//             y >= 5
370
// Solution: x=3, y=5, objective=8
371
int TestNegativeRHS() {
1✔
372
        int nrOfFailedTestCases = 0;
1✔
373

374
        SimplexSolver lp;
1✔
375
        lp.set_num_vars(2);
1✔
376
        lp.set_objective({1.0, 1.0});
2✔
377

378
        lp.add_ge_constraint({-1.0, -1.0}, -10.0); // -x - y >= -10 => x + y <= 10
2✔
379
        lp.add_ge_constraint({1.0, 0.0}, 3.0);
2✔
380
        lp.add_ge_constraint({0.0, 1.0}, 5.0);
2✔
381

382
        LPStatus status = lp.solve();
1✔
383
        if (status != LPStatus::Optimal) {
1✔
NEW
384
                std::cerr << "FAIL: negative RHS expected Optimal, got " << to_string(status) << std::endl;
×
NEW
385
                ++nrOfFailedTestCases;
×
NEW
386
                return nrOfFailedTestCases;
×
387
        }
388

389
        if (std::abs(lp.objective_value() - 8.0) > 0.01) {
1✔
NEW
390
                std::cerr << "FAIL: negative RHS expected obj=8, got " << lp.objective_value() << std::endl;
×
NEW
391
                ++nrOfFailedTestCases;
×
392
        }
393

394
        return nrOfFailedTestCases;
1✔
395
}
1✔
396

397
// Test 12: LPStatus to_string coverage
398
int TestLPStatusStrings() {
1✔
399
        int nrOfFailedTestCases = 0;
1✔
400

401
        if (std::string(to_string(LPStatus::Optimal)) != "Optimal") {
2✔
NEW
402
                ++nrOfFailedTestCases;
×
403
        }
404
        if (std::string(to_string(LPStatus::Infeasible)) != "Infeasible") {
2✔
NEW
405
                ++nrOfFailedTestCases;
×
406
        }
407
        if (std::string(to_string(LPStatus::Unbounded)) != "Unbounded") {
2✔
NEW
408
                ++nrOfFailedTestCases;
×
409
        }
410
        if (std::string(to_string(LPStatus::MaxIterations)) != "MaxIterations") {
2✔
NEW
411
                ++nrOfFailedTestCases;
×
412
        }
413

414
        return nrOfFailedTestCases;
1✔
415
}
416

417
}} // namespace sw::universal
418

419
#define TEST_CASE(name, func) \
420
        do { \
421
                int fails = func; \
422
                if (fails) { \
423
                        std::cout << name << ": FAIL (" << fails << " errors)" << std::endl; \
424
                        nrOfFailedTestCases += fails; \
425
                } else { \
426
                        std::cout << name << ": PASS" << std::endl; \
427
                } \
428
        } while(0)
429

430
int main()
1✔
431
try {
432
        using namespace sw::universal;
433

434
        int nrOfFailedTestCases = 0;
1✔
435

436
        std::cout << "POP Simplex Solver Tests\n";
1✔
437
        std::cout << std::string(40, '=') << "\n\n";
1✔
438

439
        TEST_CASE("Simple 2-var LP", TestSimple2Var());
1✔
440
        TEST_CASE("Relational constraints", TestRelational());
1✔
441
        TEST_CASE("POP-like constraints", TestPopLikeConstraints());
1✔
442
        TEST_CASE("Three-variable LP", TestThreeVar());
1✔
443
        TEST_CASE("Infeasible LP", TestInfeasible());
1✔
444
        TEST_CASE("Unbounded LP", TestUnbounded());
1✔
445
        TEST_CASE("MaxIterations LP", TestMaxIterations());
1✔
446
        TEST_CASE("Empty LP", TestEmptyLP());
1✔
447
        TEST_CASE("Single variable LP", TestSingleVar());
1✔
448
        TEST_CASE("LE and EQ constraints", TestLeAndEqConstraints());
1✔
449
        TEST_CASE("Negative RHS", TestNegativeRHS());
1✔
450
        TEST_CASE("LPStatus strings", TestLPStatusStrings());
1✔
451

452
        std::cout << "\n";
1✔
453
        if (nrOfFailedTestCases == 0) {
1✔
454
                std::cout << "All simplex solver tests PASSED\n";
1✔
455
        } else {
456
                std::cout << nrOfFailedTestCases << " test(s) FAILED\n";
×
457
        }
458

459
        return (nrOfFailedTestCases > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
1✔
460
}
461
catch (const char* msg) {
×
462
        std::cerr << "Caught exception: " << msg << std::endl;
×
463
        return EXIT_FAILURE;
×
464
}
×
465
catch (...) {
×
466
        std::cerr << "Caught unknown exception" << std::endl;
×
467
        return EXIT_FAILURE;
×
468
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc