1 // Copyright (C) 2000, International Business Machines
2 // Corporation and others.  All Rights Reserved.
3 // This code is licensed under the terms of the Eclipse Public License (EPL).
4 
5 /*
6 		!!  MAINTAINERS  PLEASE  READ  !!
7 
8 The OSI unit test is gradually undergoing a conversion. Over time, the goal is
9 to factor the original monolith into separate files and routines. Individual
10 routines should collect test outcomes in the global OsiUnitTest::outcomes
11 object using the OSIUNITTEST_* macros defined in OsiUnitTests.hpp.
12 Ideally, the implementor of an OsiXXX could
13 indicated expected failures, to avoid the current practice of modifying the
14 unit test to avoid attempting the test for a particular OsiXXX.
15 
16 The original approach was to use asserts in tests; the net effect is that the
17 unit test chokes and dies as soon as something goes wrong. The current
18 approach is to soldier on until something has gone wrong which makes further
19 testing pointless if OsiUnitTest::haltonerror is set to 0, to hold and ask the
20 user for pressing a key if OsiUnitTest::haltonerror is set to 1, and to stop
21 immediately if OsiUnitTest::haltonerror is set to 2 (but only in case of an error,
22 not for warnings). The general idea is to return the maximum amount of useful
23 information with each run. The OsiUnitTest::verbosity variable should be used
24 to decide on the amount of information to be printed. At level 0, only minimal
25 output should be printed, at level 1, more information about failed tests
26 should be printed, while at level 2, also information on passed tests and other
27 details should be printed.
28 
29 If you work on this code, please keep these conventions in mind:
30 
31   * Tests should be encapsulated in subroutines. If you have a moment, factor
32     something out of the main routine --- it'd be nice to get it down under
33     500 lines.
34 
35   * All local helper routines should be defined in the file-local namespace.
36 
37   * This unit test is meant as a certification that OsiXXX correctly implements
38     the OSI API specification. Don't step around it!
39 
40     If OsiXXX is not capable of meeting a particular requirement and you edit
41     the unit test code to avoid the test, don't just sweep it under the rug!
42     Print a failure message saying the test has been skipped, or something
43     else informative.
44 
45     OsiVol is the worst offender for this (the underlying algorithm is not
46     simplex and imposes serious limitations on the type of lp that vol can
47     handle). Any simplex-oriented solver should *NOT* be exempted from any
48     test. If it's pointless to even try, print a failure message.
49 
50   -- lh, 08.01.07, 10.08.26 --
51 */
52 
53 #include "CoinPragma.hpp"
54 #include "OsiConfig.h"
55 
56 #include <cstdlib>
57 #include <vector>
58 #include <iostream>
59 #include <iomanip>
60 #include <sstream>
61 #include <cstdio>
62 
63 /*
64   A utility definition which allows for easy suppression of unused variable
65   warnings from GCC. Handy in this environment, where we're constantly def'ing
66   things in and out.
67 */
68 #ifndef UNUSED
69 #if defined(__GNUC__)
70 #define UNUSED __attribute__((unused))
71 #else
72 #define UNUSED
73 #endif
74 #endif
75 
76 #include "OsiUnitTests.hpp"
77 #include "OsiSolverInterface.hpp"
78 #include "CoinTime.hpp"
79 #include "CoinFloatEqual.hpp"
80 #include "CoinPackedVector.hpp"
81 #include "CoinPackedMatrix.hpp"
82 #include "CoinWarmStartBasis.hpp"
83 #include "OsiRowCut.hpp"
84 #include "OsiCuts.hpp"
85 #include "OsiPresolve.hpp"
86 
87 /*
88   Define helper routines in the file-local namespace.
89 */
90 
91 namespace OsiUnitTest {
92 extern void testSimplexAPI(const OsiSolverInterface *emptySi, const std::string &mpsDir);
93 }
94 
95 using namespace OsiUnitTest;
96 
97 namespace {
98 
99 //#############################################################################
100 // A routine to build a CoinPackedMatrix matching the exmip1 example.
101 //#############################################################################
102 
BuildExmip1Mtx()103 const CoinPackedMatrix *BuildExmip1Mtx()
104 /*
105   Simple function to build a packed matrix for the exmip1 example used in
106   tests. The function exists solely to hide the intermediate variables.
107   Probably could be written as an initialised declaration.
108   See COIN/Mps/Sample/exmip1.mps for a human-readable presentation.
109 
110   Don't forget to dispose of the matrix when you're done with it.
111 
112   Ordered triples seem easiest. They're listed in row-major order.
113 */
114 
115 {
116   int rowndxs[] = { 0, 0, 0, 0, 0,
117     1, 1,
118     2, 2,
119     3, 3,
120     4, 4, 4 };
121   int colndxs[] = { 0, 1, 3, 4, 7,
122     1, 2,
123     2, 5,
124     3, 6,
125     0, 4, 7 };
126   double coeffs[] = { 3.0, 1.0, -2.0, -1.0, -1.0,
127     2.0, 1.1,
128     1.0, 1.0,
129     2.8, -1.2,
130     5.6, 1.0, 1.9 };
131 
132   CoinPackedMatrix *mtx = new CoinPackedMatrix(true, &rowndxs[0], &colndxs[0], &coeffs[0], 14);
133 
134   return (mtx);
135 }
136 
137 //#############################################################################
138 // Short tests contributed by Vivian DeSmedt. Thanks!
139 //#############################################################################
140 
141 /*
142   DeSmedt Problem #1
143 
144   Initially, max 3*x1 +   x2			x* = [  5 0 ]
145 		 2*x1 +   x2 <= 10	   row_act = [ 10 5 ]
146 		   x1 + 3*x2 <= 15
147 
148   Test for solver status, expected solution, row activity. Then change
149   objective to [1 1], resolve, check again for solver status, expected
150   solution [3 4] and expected row activity [10 15].
151 */
test1VivianDeSmedt(OsiSolverInterface * s)152 bool test1VivianDeSmedt(OsiSolverInterface *s)
153 {
154   bool ret = true;
155 
156   double inf = s->getInfinity();
157 
158   CoinPackedMatrix m;
159 
160   m.transpose();
161 
162   CoinPackedVector r0;
163   r0.insert(0, 2);
164   r0.insert(1, 1);
165   m.appendRow(r0);
166 
167   CoinPackedVector r1;
168   r1.insert(0, 1);
169   r1.insert(1, 3);
170   m.appendRow(r1);
171 
172   int numcol = 2;
173 
174   double *obj = new double[numcol];
175   obj[0] = 3;
176   obj[1] = 1;
177 
178   double *collb = new double[numcol];
179   collb[0] = 0;
180   collb[1] = 0;
181 
182   double *colub = new double[numcol];
183   colub[0] = inf;
184   colub[1] = inf;
185 
186   int numrow = 2;
187 
188   double *rowlb = new double[numrow];
189   rowlb[0] = -inf;
190   rowlb[1] = -inf;
191 
192   double *rowub = new double[numrow];
193   rowub[0] = 10;
194   rowub[1] = 15;
195 
196   s->loadProblem(m, collb, colub, obj, rowlb, rowub);
197 
198   delete[] obj;
199   delete[] collb;
200   delete[] colub;
201 
202   delete[] rowlb;
203   delete[] rowub;
204 
205   s->setObjSense(-1);
206 
207   s->initialSolve();
208 
209   ret = ret && s->isProvenOptimal();
210   ret = ret && !s->isProvenPrimalInfeasible();
211   ret = ret && !s->isProvenDualInfeasible();
212 
213   const double solution1[] = { 5, 0 };
214   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution1, 2);
215 
216   const double activity1[] = { 10, 5 };
217   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity1, 2);
218 
219   s->setObjCoeff(0, 1);
220   s->setObjCoeff(1, 1);
221 
222   s->resolve();
223 
224   ret = ret && s->isProvenOptimal();
225   ret = ret && !s->isProvenPrimalInfeasible();
226   ret = ret && !s->isProvenDualInfeasible();
227 
228   const double solution2[] = { 3, 4 };
229   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution2, 2);
230 
231   const double activity2[] = { 10, 15 };
232   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity2, 2);
233 
234   return ret;
235 }
236 
237 //--------------------------------------------------------------------------
238 
test2VivianDeSmedt(OsiSolverInterface * s)239 bool test2VivianDeSmedt(OsiSolverInterface *s)
240 {
241   bool ret = true;
242 
243   double inf = s->getInfinity();
244 
245   CoinPackedMatrix m;
246 
247   m.transpose();
248 
249   CoinPackedVector r0;
250   r0.insert(0, 2);
251   r0.insert(1, 1);
252   m.appendRow(r0);
253 
254   CoinPackedVector r1;
255   r1.insert(0, 1);
256   r1.insert(1, 3);
257   m.appendRow(r1);
258 
259   CoinPackedVector r2;
260   r2.insert(0, 1);
261   r2.insert(1, 1);
262   m.appendRow(r2);
263 
264   int numcol = 2;
265 
266   double *obj = new double[numcol];
267   obj[0] = 3;
268   obj[1] = 1;
269 
270   double *collb = new double[numcol];
271   collb[0] = 0;
272   collb[1] = 0;
273 
274   double *colub = new double[numcol];
275   colub[0] = inf;
276   colub[1] = inf;
277 
278   int numrow = 3;
279 
280   double *rowlb = new double[numrow];
281   rowlb[0] = -inf;
282   rowlb[1] = -inf;
283   rowlb[2] = 1;
284 
285   double *rowub = new double[numrow];
286   rowub[0] = 10;
287   rowub[1] = 15;
288   rowub[2] = inf;
289 
290   s->loadProblem(m, collb, colub, obj, rowlb, rowub);
291 
292   delete[] obj;
293   delete[] collb;
294   delete[] colub;
295 
296   delete[] rowlb;
297   delete[] rowub;
298 
299   s->setObjSense(-1);
300 
301   s->initialSolve();
302 
303   ret = ret && s->isProvenOptimal();
304   ret = ret && !s->isProvenPrimalInfeasible();
305   ret = ret && !s->isProvenDualInfeasible();
306 
307   const double solution1[] = { 5, 0 };
308   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution1, 2);
309 
310   const double activity1[] = { 10, 5, 5 };
311   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity1, 3);
312 
313   s->setObjCoeff(0, 1);
314   s->setObjCoeff(1, 1);
315 
316   s->resolve();
317 
318   ret = ret && s->isProvenOptimal();
319   ret = ret && !s->isProvenPrimalInfeasible();
320   ret = ret && !s->isProvenDualInfeasible();
321 
322   const double solution2[] = { 3, 4 };
323   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution2, 2);
324 
325   const double activity2[] = { 10, 15, 7 };
326   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity2, 3);
327 
328   return ret;
329 }
330 
331 //--------------------------------------------------------------------------
332 
test3VivianDeSmedt(OsiSolverInterface * s)333 bool test3VivianDeSmedt(OsiSolverInterface *s)
334 {
335   bool ret = true;
336 
337   //double inf = s->getInfinity();
338 
339   CoinPackedVector empty;
340 
341   s->addCol(empty, 0, 10, 3);
342   s->addCol(empty, 0, 10, 1);
343 
344   CoinPackedVector r0;
345   r0.insert(0, 2);
346   r0.insert(1, 1);
347   s->addRow(r0, 0, 10);
348 
349   CoinPackedVector r1;
350   r1.insert(0, 1);
351   r1.insert(1, 3);
352   s->addRow(r1, 0, 15);
353 
354   s->setObjSense(-1);
355 
356   s->writeMps("test");
357 
358   s->initialSolve();
359 
360   ret = ret && s->isProvenOptimal();
361   ret = ret && !s->isProvenPrimalInfeasible();
362   ret = ret && !s->isProvenDualInfeasible();
363 
364   const double solution1[] = { 5, 0 };
365   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution1, 2);
366 
367   const double activity1[] = { 10, 5 };
368   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity1, 2);
369 
370   s->setObjCoeff(0, 1);
371   s->setObjCoeff(1, 1);
372 
373   s->resolve();
374 
375   ret = ret && s->isProvenOptimal();
376   ret = ret && !s->isProvenPrimalInfeasible();
377   ret = ret && !s->isProvenDualInfeasible();
378 
379   const double solution2[] = { 3, 4 };
380   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution2, 2);
381 
382   const double activity2[] = { 10, 15 };
383   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity2, 2);
384 
385   return ret;
386 }
387 
388 //--------------------------------------------------------------------------
389 
test4VivianDeSmedt(OsiSolverInterface * s)390 bool test4VivianDeSmedt(OsiSolverInterface *s)
391 {
392   bool ret = true;
393 
394   double inf = s->getInfinity();
395 
396   CoinPackedVector empty;
397 
398   s->addCol(empty, 0, inf, 3);
399   s->addCol(empty, 0, inf, 1);
400 
401   CoinPackedVector r0;
402   r0.insert(0, 2);
403   r0.insert(1, 1);
404   s->addRow(r0, -inf, 10);
405 
406   CoinPackedVector r1;
407   r1.insert(0, 1);
408   r1.insert(1, 3);
409   s->addRow(r1, -inf, 15);
410 
411   s->setObjSense(-1);
412 
413   s->writeMps("test");
414 
415   s->initialSolve();
416 
417   ret = ret && s->isProvenOptimal();
418   ret = ret && !s->isProvenPrimalInfeasible();
419   ret = ret && !s->isProvenDualInfeasible();
420 
421   const double solution1[] = { 5, 0 };
422   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution1, 2);
423 
424   const double activity1[] = { 10, 5 };
425   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity1, 2);
426 
427   s->setObjCoeff(0, 1);
428   s->setObjCoeff(1, 1);
429 
430   s->resolve();
431 
432   ret = ret && s->isProvenOptimal();
433   ret = ret && !s->isProvenPrimalInfeasible();
434   ret = ret && !s->isProvenDualInfeasible();
435 
436   const double solution2[] = { 3, 4 };
437   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution2, 2);
438 
439   const double activity2[] = { 10, 15 };
440   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity2, 2);
441 
442   return ret;
443 }
444 
445 //--------------------------------------------------------------------------
446 
447 /*
448   Constructs the system
449 
450      max    3x1 +  x2
451 
452     -inf <= 2x1 +  x2 <= 10
453     -inf <=  x1 + 3x2 <= 15
454 
455   The optimal solution is unbounded. Objective is then changed to
456 
457      max     x1 +  x2
458 
459   which has a bounded optimum at x1 = 3, x2 = 4.
460 */
461 
test5VivianDeSmedt(OsiSolverInterface * s)462 bool test5VivianDeSmedt(OsiSolverInterface *s)
463 {
464   bool ret = true;
465 
466   double inf = s->getInfinity();
467 
468   CoinPackedVector empty;
469 
470   s->addCol(empty, -inf, inf, 3);
471   s->addCol(empty, -inf, inf, 1);
472 
473   CoinPackedVector r0;
474   r0.insert(0, 2);
475   r0.insert(1, 1);
476   s->addRow(r0, -inf, 10);
477 
478   CoinPackedVector r1;
479   r1.insert(0, 1);
480   r1.insert(1, 3);
481   s->addRow(r1, -inf, 15);
482 
483   s->setObjSense(-1);
484 
485   s->writeMps("test");
486   s->initialSolve();
487 
488   ret = ret && !s->isProvenOptimal();
489   ret = ret && !s->isProvenPrimalInfeasible();
490   ret = ret && s->isProvenDualInfeasible();
491 
492   s->setObjCoeff(0, 1);
493   s->setObjCoeff(1, 1);
494 
495   s->resolve();
496 
497   ret = ret && s->isProvenOptimal();
498   ret = ret && !s->isProvenPrimalInfeasible();
499   ret = ret && !s->isProvenDualInfeasible();
500 
501   const double solution2[] = { 3, 4 };
502   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution2, 2);
503 
504   const double activity2[] = { 10, 15 };
505   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity2, 2);
506 
507   return ret;
508 }
509 
510 //--------------------------------------------------------------------------
511 
test6VivianDeSmedt(OsiSolverInterface * s)512 bool test6VivianDeSmedt(OsiSolverInterface *s)
513 {
514   bool ret = true;
515 
516   double inf = s->getInfinity();
517 
518   CoinPackedVector empty;
519 
520   s->addCol(empty, 0, inf, 3);
521   s->addCol(empty, 0, inf, 1);
522 
523   CoinPackedVector r0;
524   r0.insert(0, 2);
525   r0.insert(1, 1);
526   s->addRow(r0, 0, 10);
527 
528   CoinPackedVector r1;
529   r1.insert(0, 1);
530   r1.insert(1, 3);
531   s->addRow(r1, 0, 15);
532 
533   s->setObjSense(-1);
534 
535   s->writeMps("test");
536 
537   s->initialSolve();
538 
539   ret = ret && s->isProvenOptimal();
540   ret = ret && !s->isProvenPrimalInfeasible();
541   ret = ret && !s->isProvenDualInfeasible();
542 
543   const double solution1[] = { 5, 0 };
544   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution1, 2);
545 
546   const double activity1[] = { 10, 5 };
547   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity1, 2);
548 
549   s->setObjCoeff(0, 1);
550   s->setObjCoeff(1, 1);
551 
552   s->resolve();
553 
554   ret = ret && s->isProvenOptimal();
555   ret = ret && !s->isProvenPrimalInfeasible();
556   ret = ret && !s->isProvenDualInfeasible();
557 
558   const double solution2[] = { 3, 4 };
559   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution2, 2);
560 
561   const double activity2[] = { 10, 15 };
562   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity2, 2);
563 
564   return ret;
565 }
566 
567 //--------------------------------------------------------------------------
568 
test7VivianDeSmedt(OsiSolverInterface * s)569 bool test7VivianDeSmedt(OsiSolverInterface *s)
570 {
571   bool ret = true;
572 
573   double inf = s->getInfinity();
574 
575   CoinPackedVector empty;
576 
577   s->addCol(empty, 4, inf, 3);
578   s->addCol(empty, 3, inf, 1);
579 
580   CoinPackedVector r0;
581   r0.insert(0, 2);
582   r0.insert(1, 1);
583   s->addRow(r0, 0, 10);
584 
585   CoinPackedVector r1;
586   r1.insert(0, 1);
587   r1.insert(1, 3);
588   s->addRow(r1, 0, 15);
589 
590   s->setObjSense(-1);
591 
592   s->writeMps("test");
593 
594   s->initialSolve();
595 
596   ret = ret && !s->isProvenOptimal();
597   ret = ret && s->isProvenPrimalInfeasible();
598 
599   s->setObjCoeff(0, 1);
600   s->setObjCoeff(1, 1);
601 
602   s->resolve();
603 
604   ret = ret && !s->isProvenOptimal();
605   ret = ret && s->isProvenPrimalInfeasible();
606 
607   return ret;
608 }
609 
610 //--------------------------------------------------------------------------
611 
test8VivianDeSmedt(OsiSolverInterface * s)612 bool test8VivianDeSmedt(OsiSolverInterface *s)
613 {
614   bool ret = true;
615 
616   double inf = s->getInfinity();
617 
618   CoinPackedVector empty;
619 
620   s->addCol(empty, -inf, inf, 3);
621   s->addCol(empty, -inf, inf, 1);
622 
623   CoinPackedVector r0;
624   r0.insert(0, 2);
625   r0.insert(1, 1);
626   s->addRow(r0, 0, 10);
627 
628   CoinPackedVector r1;
629   r1.insert(0, 1);
630   r1.insert(1, 3);
631   s->addRow(r1, 0, 15);
632 
633   s->setObjSense(-1);
634 
635   s->writeMps("test");
636 
637   s->initialSolve();
638 
639   ret = ret && s->isProvenOptimal();
640   ret = ret && !s->isProvenPrimalInfeasible();
641   ret = ret && !s->isProvenDualInfeasible();
642 
643   const double solution1[] = { 6, -2 };
644   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution1, 2);
645 
646   const double activity1[] = { 10, 0 };
647   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity1, 2);
648 
649   s->setObjCoeff(0, 1);
650   s->setObjCoeff(1, 1);
651 
652   s->resolve();
653 
654   ret = ret && s->isProvenOptimal();
655   ret = ret && !s->isProvenPrimalInfeasible();
656   ret = ret && !s->isProvenDualInfeasible();
657 
658   const double solution2[] = { 3, 4 };
659   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution2, 2);
660 
661   const double activity2[] = { 10, 15 };
662   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity2, 2);
663 
664   return ret;
665 }
666 
667 //--------------------------------------------------------------------------
668 
test9VivianDeSmedt(OsiSolverInterface * s)669 bool test9VivianDeSmedt(OsiSolverInterface *s)
670 {
671   bool ret = true;
672 
673   double inf = s->getInfinity();
674 
675   CoinPackedVector empty;
676 
677   s->addCol(empty, -inf, inf, 3);
678   s->addCol(empty, -inf, inf, 1);
679 
680   CoinPackedVector r0;
681   r0.insert(0, 2);
682   r0.insert(1, 1);
683   s->addRow(r0, 0, 10);
684 
685   CoinPackedVector r1;
686   r1.insert(0, 1);
687   r1.insert(1, 3);
688   s->addRow(r1, 0, 15);
689 
690   CoinPackedVector r2;
691   r2.insert(0, 1);
692   r2.insert(1, 4);
693   s->addRow(r2, 12, inf);
694 
695   s->setObjSense(-1);
696 
697   s->writeMps("test");
698 
699   s->initialSolve();
700 
701   ret = ret && s->isProvenOptimal();
702   ret = ret && !s->isProvenPrimalInfeasible();
703   ret = ret && !s->isProvenDualInfeasible();
704 
705   const double solution1[] = { 4, 2 };
706   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution1, 2);
707 
708   const double activity1[] = { 10, 10, 12 };
709   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity1, 3);
710 
711   s->setObjCoeff(0, 1);
712   s->setObjCoeff(1, 1);
713 
714   s->resolve();
715 
716   ret = ret && s->isProvenOptimal();
717   ret = ret && !s->isProvenPrimalInfeasible();
718   ret = ret && !s->isProvenDualInfeasible();
719 
720   const double solution2[] = { 3, 4 };
721   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution2, 2);
722 
723   const double activity2[] = { 10, 15, 19 };
724   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity2, 3);
725 
726   return ret;
727 }
728 
729 //--------------------------------------------------------------------------
730 
test10VivianDeSmedt(OsiSolverInterface * s)731 bool test10VivianDeSmedt(OsiSolverInterface *s)
732 {
733   bool ret = true;
734 
735   double inf = s->getInfinity();
736 
737   int numcols = 2;
738   int numrows = 2;
739   const CoinBigIndex start[] = { 0, 2, 4 };
740   const int index[] = { 0, 1, 0, 1 };
741   const double value[] = { 4, 1, 2, 3 };
742   const double collb[] = { 0, 0 };
743   const double colub[] = { inf, inf };
744   double obj[] = { 3, 1 };
745   char rowsen[] = { 'R', 'R' };
746   double rowrhs[] = { 20, 15 };
747   double rowrng[] = { 20, 15 };
748 
749   s->loadProblem(numcols, numrows, start, index, value, collb, colub, obj, rowsen, rowrhs, rowrng);
750 
751   s->setObjSense(-1);
752 
753   s->writeMps("test");
754 
755   s->initialSolve();
756 
757   ret = ret && s->isProvenOptimal();
758   ret = ret && !s->isProvenPrimalInfeasible();
759   ret = ret && !s->isProvenDualInfeasible();
760 
761   const double solution1[] = { 5, 0 };
762   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution1, 2);
763 
764   const double activity1[] = { 20, 5 };
765   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity1, 2);
766 
767   s->setObjCoeff(0, 1);
768   s->setObjCoeff(1, 1);
769 
770   s->resolve();
771 
772   ret = ret && s->isProvenOptimal();
773   ret = ret && !s->isProvenPrimalInfeasible();
774   ret = ret && !s->isProvenDualInfeasible();
775 
776   const double solution2[] = { 3, 4 };
777   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution2, 2);
778 
779   const double activity2[] = { 20, 15 };
780   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity2, 2);
781 
782   return ret;
783 }
784 
785 //--------------------------------------------------------------------------
786 
test11VivianDeSmedt(OsiSolverInterface * s)787 bool test11VivianDeSmedt(OsiSolverInterface *s)
788 {
789   bool ret = true;
790 
791   double inf = s->getInfinity();
792 
793   int numcols = 2;
794   int numrows = 2;
795   const CoinBigIndex start[] = { 0, 2, 4 };
796   const int index[] = { 0, 1, 0, 1 };
797   const double value[] = { 4, 1, 2, 3 };
798   const double collb[] = { 0, 0 };
799   const double colub[] = { inf, inf };
800   double obj[] = { 3, 1 };
801   double rowlb[] = { 0, 0 };
802   double rowub[] = { 20, 15 };
803 
804   s->loadProblem(numcols, numrows, start, index, value, collb, colub, obj, rowlb, rowub);
805 
806   s->setObjSense(-1);
807 
808   s->writeMps("test");
809 
810   s->initialSolve();
811 
812   ret = ret && s->isProvenOptimal();
813   ret = ret && !s->isProvenPrimalInfeasible();
814   ret = ret && !s->isProvenDualInfeasible();
815 
816   const double solution1[] = { 5, 0 };
817   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution1, 2);
818 
819   const double activity1[] = { 20, 5 };
820   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity1, 2);
821 
822   s->setObjCoeff(0, 1);
823   s->setObjCoeff(1, 1);
824 
825   s->resolve();
826 
827   ret = ret && s->isProvenOptimal();
828   ret = ret && !s->isProvenPrimalInfeasible();
829   ret = ret && !s->isProvenDualInfeasible();
830 
831   const double solution2[] = { 3, 4 };
832   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution2, 2);
833 
834   const double activity2[] = { 20, 15 };
835   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity2, 2);
836 
837   return ret;
838 }
839 
840 //--------------------------------------------------------------------------
841 
test12VivianDeSmedt(OsiSolverInterface * s)842 bool test12VivianDeSmedt(OsiSolverInterface *s)
843 {
844   bool ret = true;
845 
846   double inf = s->getInfinity();
847 
848   CoinPackedMatrix m;
849 
850   m.transpose();
851 
852   CoinPackedVector r0;
853   r0.insert(0, 4);
854   r0.insert(1, 2);
855   m.appendRow(r0);
856 
857   CoinPackedVector r1;
858   r1.insert(0, 1);
859   r1.insert(1, 3);
860   m.appendRow(r1);
861 
862   int numcol = 2;
863 
864   double *obj = new double[numcol];
865   obj[0] = 3;
866   obj[1] = 1;
867 
868   double *collb = new double[numcol];
869   collb[0] = 0;
870   collb[1] = 0;
871 
872   double *colub = new double[numcol];
873   colub[0] = inf;
874   colub[1] = inf;
875 
876   int numrow = 2;
877 
878   double *rowlb = new double[numrow];
879   rowlb[0] = 0;
880   rowlb[1] = 0;
881 
882   double *rowub = new double[numrow];
883   rowub[0] = 20;
884   rowub[1] = 15;
885 
886   s->loadProblem(m, collb, colub, obj, rowlb, rowub);
887 
888   delete[] obj;
889   delete[] collb;
890   delete[] colub;
891 
892   delete[] rowlb;
893   delete[] rowub;
894 
895   s->setObjSense(-1);
896 
897   s->initialSolve();
898 
899   ret = ret && s->isProvenOptimal();
900   ret = ret && !s->isProvenPrimalInfeasible();
901   ret = ret && !s->isProvenDualInfeasible();
902 
903   const double solution1[] = { 5, 0 };
904   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution1, 2);
905 
906   const double activity1[] = { 20, 5 };
907   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity1, 2);
908 
909   s->setObjCoeff(0, 1);
910   s->setObjCoeff(1, 1);
911 
912   s->resolve();
913 
914   ret = ret && s->isProvenOptimal();
915   ret = ret && !s->isProvenPrimalInfeasible();
916   ret = ret && !s->isProvenDualInfeasible();
917 
918   const double solution2[] = { 3, 4 };
919   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution2, 2);
920 
921   const double activity2[] = { 20, 15 };
922   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity2, 2);
923 
924   return ret;
925 }
926 
927 //--------------------------------------------------------------------------
928 
test13VivianDeSmedt(OsiSolverInterface * s)929 bool test13VivianDeSmedt(OsiSolverInterface *s)
930 {
931   bool ret = true;
932 
933   double inf = s->getInfinity();
934 
935   CoinPackedMatrix m;
936 
937   CoinPackedVector c0;
938   c0.insert(0, 4);
939   c0.insert(1, 1);
940   m.appendCol(c0);
941 
942   CoinPackedVector c1;
943   c1.insert(0, 2);
944   c1.insert(1, 3);
945   m.appendCol(c1);
946 
947   int numcol = 2;
948 
949   double *obj = new double[numcol];
950   obj[0] = 3;
951   obj[1] = 1;
952 
953   double *collb = new double[numcol];
954   collb[0] = 0;
955   collb[1] = 0;
956 
957   double *colub = new double[numcol];
958   colub[0] = inf;
959   colub[1] = inf;
960 
961   int numrow = 2;
962 
963   double *rowlb = new double[numrow];
964   rowlb[0] = 0;
965   rowlb[1] = 0;
966 
967   double *rowub = new double[numrow];
968   rowub[0] = 20;
969   rowub[1] = 15;
970 
971   s->loadProblem(m, collb, colub, obj, rowlb, rowub);
972 
973   delete[] obj;
974   delete[] collb;
975   delete[] colub;
976 
977   delete[] rowlb;
978   delete[] rowub;
979 
980   s->setObjSense(-1);
981 
982   s->initialSolve();
983 
984   ret = ret && s->isProvenOptimal();
985   ret = ret && !s->isProvenPrimalInfeasible();
986   ret = ret && !s->isProvenDualInfeasible();
987 
988   const double solution1[] = { 5, 0 };
989   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution1, 2);
990 
991   const double activity1[] = { 20, 5 };
992   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity1, 2);
993 
994   s->setObjCoeff(0, 1);
995   s->setObjCoeff(1, 1);
996 
997   s->resolve();
998 
999   ret = ret && s->isProvenOptimal();
1000   ret = ret && !s->isProvenPrimalInfeasible();
1001   ret = ret && !s->isProvenDualInfeasible();
1002 
1003   const double solution2[] = { 3, 4 };
1004   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution2, 2);
1005 
1006   const double activity2[] = { 20, 15 };
1007   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity2, 2);
1008 
1009   return ret;
1010 }
1011 
1012 //--------------------------------------------------------------------------
1013 
test14VivianDeSmedt(OsiSolverInterface * s)1014 bool test14VivianDeSmedt(OsiSolverInterface *s)
1015 {
1016   bool ret = true;
1017 
1018   double inf = s->getInfinity();
1019 
1020   CoinPackedVector empty;
1021 
1022   s->addCol(empty, 0, inf, 3);
1023   s->addCol(empty, 0, inf, 1);
1024 
1025   CoinPackedVector r0;
1026   r0.insert(0, 4);
1027   r0.insert(1, 2);
1028   s->addRow(r0, 0, 20);
1029 
1030   CoinPackedVector r1;
1031   r1.insert(0, 1);
1032   r1.insert(1, 3);
1033   s->addRow(r1, 0, 15);
1034 
1035   s->setObjSense(-1);
1036 
1037   s->writeMps("test");
1038 
1039   s->initialSolve();
1040 
1041   ret = ret && s->isProvenOptimal();
1042   ret = ret && !s->isProvenPrimalInfeasible();
1043   ret = ret && !s->isProvenDualInfeasible();
1044 
1045   const double solution1[] = { 5, 0 };
1046   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution1, 2);
1047 
1048   const double activity1[] = { 20, 5 };
1049   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity1, 2);
1050 
1051   s->setObjCoeff(0, 1);
1052   s->setObjCoeff(1, 1);
1053 
1054   s->resolve();
1055 
1056   ret = ret && s->isProvenOptimal();
1057   ret = ret && !s->isProvenPrimalInfeasible();
1058   ret = ret && !s->isProvenDualInfeasible();
1059 
1060   const double solution2[] = { 3, 4 };
1061   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution2, 2);
1062 
1063   const double activity2[] = { 20, 15 };
1064   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity2, 2);
1065 
1066   return ret;
1067 }
1068 
1069 //--------------------------------------------------------------------------
1070 
test15VivianDeSmedt(OsiSolverInterface * s)1071 bool test15VivianDeSmedt(OsiSolverInterface *s)
1072 {
1073   bool ret = true;
1074 
1075   double inf = s->getInfinity();
1076 
1077   CoinPackedVector empty;
1078 
1079   s->addRow(empty, 0, 20);
1080   s->addRow(empty, 0, 15);
1081 
1082   CoinPackedVector c0;
1083   c0.insert(0, 4);
1084   c0.insert(1, 1);
1085   s->addCol(c0, 0, inf, 3);
1086 
1087   CoinPackedVector c1;
1088   c1.insert(0, 2);
1089   c1.insert(1, 3);
1090   s->addCol(c1, 0, inf, 1);
1091 
1092   s->setObjSense(-1);
1093 
1094   s->writeMps("test");
1095 
1096   s->initialSolve();
1097 
1098   ret = ret && s->isProvenOptimal();
1099   ret = ret && !s->isProvenPrimalInfeasible();
1100   ret = ret && !s->isProvenDualInfeasible();
1101 
1102   const double solution1[] = { 5, 0 };
1103   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution1, 2);
1104 
1105   const double activity1[] = { 20, 5 };
1106   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity1, 2);
1107 
1108   s->setObjCoeff(0, 1);
1109   s->setObjCoeff(1, 1);
1110 
1111   s->resolve();
1112 
1113   ret = ret && s->isProvenOptimal();
1114   ret = ret && !s->isProvenPrimalInfeasible();
1115   ret = ret && !s->isProvenDualInfeasible();
1116 
1117   const double solution2[] = { 3, 4 };
1118   ret = ret && equivalentVectors(s, s, 0.0001, s->getColSolution(), solution2, 2);
1119 
1120   const double activity2[] = { 20, 15 };
1121   ret = ret && equivalentVectors(s, s, 0.0001, s->getRowActivity(), activity2, 2);
1122 
1123   return ret;
1124 }
1125 
1126 /*
1127   Another test case submitted by Vivian De Smedt.  The test is to modify the
1128   objective function and check that the solver's optimum point tracks
1129   correctly. The initial problem is
1130 
1131   max  3*x1 +   x2
1132 
1133   s.t. 2*x1 +   x2 <= 10
1134 	 x1 + 3*x2 <= 15
1135 
1136   with optimum z* = 15 at (x1,x2) = (5,0). Then the objective is changed to
1137   x1 + x2, with new optimum z* = 7 at (3,4).
1138 
1139   The volume algorithm doesn't return exact solution values, so relax the
1140   test for correctness when we're checking the solution.
1141 */
1142 
changeObjAndResolve(const OsiSolverInterface * emptySi)1143 void changeObjAndResolve(const OsiSolverInterface *emptySi)
1144 
1145 {
1146   OsiSolverInterface *s = emptySi->clone();
1147   double dEmpty = 0;
1148   int iEmpty = 0;
1149   CoinBigIndex iEmpty2 = 0;
1150 
1151   /*
1152   Establish an empty problem. Establish empty columns with bounds and objective
1153   coefficient only. Finally, insert constraint coefficients and set for
1154   maximisation.
1155 */
1156   s->loadProblem(0, 0, &iEmpty2, &iEmpty, &dEmpty,
1157     &dEmpty, &dEmpty, &dEmpty, &dEmpty, &dEmpty);
1158 
1159   CoinPackedVector c;
1160   s->addCol(c, 0, 10, 3);
1161   s->addCol(c, 0, 10, 1);
1162 
1163   double inf = s->getInfinity();
1164   CoinPackedVector r1;
1165   r1.insert(0, 2);
1166   r1.insert(1, 1);
1167   s->addRow(r1, -inf, 10);
1168 
1169   r1.clear();
1170   r1.insert(0, 1);
1171   r1.insert(1, 3);
1172   s->addRow(r1, -inf, 15);
1173 
1174   s->setObjSense(-1);
1175   /*
1176   Optimise for 3*x1 + x2 and check for correctness.
1177 */
1178   s->initialSolve();
1179 
1180   const double *colSol = s->getColSolution();
1181   OSIUNITTEST_ASSERT_ERROR(colSol[0] >= 4.5, {}, *s, "changeObjAndResolve");
1182   OSIUNITTEST_ASSERT_ERROR(colSol[1] <= 0.5, {}, *s, "changeObjAndResolve");
1183   /*
1184   Set objective to x1 + x2 and reoptimise.
1185 */
1186   s->setObjCoeff(0, 1);
1187   s->setObjCoeff(1, 1);
1188 
1189   s->resolve();
1190 
1191   colSol = s->getColSolution();
1192   OSIUNITTEST_ASSERT_ERROR(colSol[0] >= 2.3 && colSol[0] <= 3.7, {}, *s, "changeObjAndResolve");
1193   OSIUNITTEST_ASSERT_ERROR(colSol[1] >= 3.5 && colSol[1] <= 4.5, {}, *s, "changeObjAndResolve");
1194 
1195   delete s;
1196 }
1197 
1198 /*
1199   This code is taken from some bug reports of Sebastian Nowozin. It
1200   demonstrates some issues he had with OsiClp.
1201 
1202   https://projects.coin-or.org/Osi/ticket/54
1203   https://projects.coin-or.org/Osi/ticket/55
1204   https://projects.coin-or.org/Osi/ticket/56
1205   https://projects.coin-or.org/Osi/ticket/58
1206 
1207   The short summary is that enabling/disabling the level 2 simplex interface
1208   (controllable pivoting) with an empty constraint matrix caused problems.
1209   For solvers that don't support simplex level 2, all we're testing is
1210   constraint system mods and resolve.
1211 
1212   Query (lh): The original comments with this code referred to use of Binv to
1213   generate cuts Binv et al. are level 1 simplex interface routines. Perhaps
1214   the test should use level 1.
1215 */
1216 
test16SebastianNowozin(OsiSolverInterface * si)1217 bool test16SebastianNowozin(OsiSolverInterface *si)
1218 
1219 {
1220   CoinAbsFltEq fltEq;
1221 
1222   CoinPackedMatrix *matrix = new CoinPackedMatrix(false, 0, 0);
1223   matrix->setDimensions(0, 4);
1224 
1225   double objective[] = { 0.1, 0.2, -0.1, -0.2 };
1226   double varLB[] = { 0.0, 0.0, 0.0, 0.0 };
1227   double varUB[] = { 1.0, 1.0, 1.0, 1.0 };
1228 
1229   si->loadProblem(*matrix, varLB, varUB, objective, NULL, NULL);
1230   delete matrix;
1231 
1232   /*
1233   Set objective sense prior to objective, just to catch the unwary.
1234 */
1235   si->setObjSense(1);
1236   si->setObjective(objective);
1237   /*
1238   The code provided with ticket 54 is illegal --- this first call cannot be
1239   resolve(). The first solve must be an initialSolve, for the benefit of
1240   solvers which use it for initialisation.  -- lh, 080903 --
1241 */
1242   si->initialSolve();
1243   OSIUNITTEST_ASSERT_ERROR(si->isProvenOptimal(), return false, *si, "test16SebastianNowozin initial solve");
1244   OSIUNITTEST_ASSERT_ERROR(fltEq(si->getObjValue(), -0.3), return false, *si, "test16SebastianNowozin initial solve");
1245   /*
1246   Expected: primal = [ 0 0 1 1 ]
1247 */
1248   OSIUNITTEST_ASSERT_ERROR(si->getColSolution() != NULL, return false, *si, "test16SebastianNowozin initial solve");
1249   /*
1250   Simulate a constraint generation interval that will require the simplex
1251   interface.  Enable, then disable level 2 simplex interface (controllable
1252   pivoting), if the solver has it.
1253 */
1254   if (si->canDoSimplexInterface() >= 2) {
1255     OSIUNITTEST_CATCH_ERROR(si->enableFactorization(), return false, *si, "test16SebastianNowozin initial solve");
1256     OSIUNITTEST_CATCH_ERROR(si->enableSimplexInterface(true), return false, *si, "test16SebastianNowozin initial solve");
1257     //  	try
1258     //    { si->enableFactorization() ;
1259     //      si->enableSimplexInterface(true) ; }
1260     //    catch (CoinError e)
1261     //    { std::string errmsg ;
1262     //      errmsg = "first enableFactorization or enableSimplexInterface" ;
1263     //      errmsg = errmsg + " threw CoinError: " + e.message() ;
1264     //      OSIUNITTEST_ADD_OUTCOME(*si, "test16SebastianNowozin first enableFactorization or enableSimplexInterface", errmsg.c_str(), TestOutcome::ERROR, false);
1265     //      failureMessage(*si,errmsg) ;
1266     //      return (false) ; }
1267     //    OSIUNITTEST_ADD_OUTCOME(*si, "test16SebastianNowozin first enableFactorization or enableSimplexInterface", "no exception", TestOutcome::PASSED, false);
1268     // (...) constraint generation here
1269     si->disableFactorization();
1270   }
1271 
1272   /*
1273   Add two constraints and resolve
1274 */
1275   CoinPackedVector row1; // x_2 + x_3 - x_0 <= 0
1276   row1.insert(0, -1.0);
1277   row1.insert(2, 1.0);
1278   row1.insert(3, 1.0);
1279   si->addRow(row1, -si->getInfinity(), 0.0);
1280 
1281   CoinPackedVector row2; // x_0 + x_1 - x_3 <= 0
1282   row2.insert(0, 1.0);
1283   row2.insert(1, 1.0);
1284   row2.insert(3, -1.0);
1285   si->addRow(row2, -si->getInfinity(), 0.0);
1286 
1287   si->resolve();
1288   OSIUNITTEST_ASSERT_ERROR(si->isProvenOptimal(), return false, *si, "test16SebastianNowozin first resolve");
1289   OSIUNITTEST_ASSERT_ERROR(fltEq(si->getObjValue(), -0.1), return false, *si, "test16SebastianNowozin first resolve");
1290   /*
1291   Expected: primal = [ 1 0 0 1 ]
1292 */
1293   OSIUNITTEST_ASSERT_ERROR(si->getColSolution() != NULL, return false, *si, "test16SebastianNowozin first resolve");
1294   /*
1295   Simulate another constraint generation run.
1296 */
1297   if (si->canDoSimplexInterface() >= 2) {
1298     OSIUNITTEST_CATCH_ERROR(si->enableFactorization(), return false, *si, "test16SebastianNowozin first resolve");
1299     OSIUNITTEST_CATCH_ERROR(si->enableSimplexInterface(true), return false, *si, "test16SebastianNowozin first resolve");
1300     //	try
1301     //    { si->enableFactorization() ;
1302     //      si->enableSimplexInterface(true) ; }
1303     //    catch (CoinError e)
1304     //    { std::string errmsg ;
1305     //      errmsg = "second enableFactorization or enableSimplexInterface" ;
1306     //      errmsg = errmsg + " threw CoinError: " + e.message() ;
1307     //      failureMessage(*si,errmsg) ;
1308     //      OSIUNITTEST_ADD_OUTCOME(*si, "test16SebastianNowozin second enableFactorization or enableSimplexInterface", errmsg.c_str(), TestOutcome::ERROR, false);
1309     //      return (false) ; }
1310     //    OSIUNITTEST_ADD_OUTCOME(*si, "test16SebastianNowozin second enableFactorization or enableSimplexInterface", "no exception", TestOutcome::PASSED, false);
1311     // (...) constraint generation here
1312     si->disableFactorization();
1313   }
1314   /*
1315   Remove a constraint, add .15 to the objective coefficients, and resolve.
1316 */
1317   int rows_to_delete_arr[] = { 0 };
1318   si->deleteRows(1, rows_to_delete_arr);
1319 
1320   std::transform(objective, objective + 4, objective,
1321     std::bind2nd(std::plus< double >(), 0.15));
1322   si->setObjective(objective);
1323   si->resolve();
1324   OSIUNITTEST_ASSERT_ERROR(si->isProvenOptimal(), return false, *si, "test16SebastianNowozin second resolve");
1325   OSIUNITTEST_ASSERT_ERROR(fltEq(si->getObjValue(), -0.05), return false, *si, "test16SebastianNowozin second resolve");
1326   /*
1327   Expected: obj = [ .25 .35 .05 -.05], primal = [ 0 0 0 1 ]
1328 */
1329   OSIUNITTEST_ASSERT_ERROR(si->getColSolution() != NULL, return false, *si, "test16SebastianNowozin second resolve");
1330 
1331   return true;
1332 }
1333 
1334 /*
1335   This code checks an issue reported by Sebastian Nowozin in OsiClp ticket
1336   57.  He said that OsiClpSolverInterface::getReducedGradient() requires a
1337   prior call to both enableSimplexInterface(true) and enableFactorization(),
1338   but only checks/asserts the simplex interface.
1339 
1340   Same comment as test16 --- would simplex level 1 suffice?
1341 */
1342 
test17SebastianNowozin(OsiSolverInterface * si)1343 bool test17SebastianNowozin(OsiSolverInterface *si)
1344 
1345 {
1346   if (si->canDoSimplexInterface() < 2) {
1347     return (true);
1348   }
1349 
1350   CoinPackedMatrix *matrix = new CoinPackedMatrix(false, 0, 0);
1351   matrix->setDimensions(0, 4);
1352 
1353   double objective[] = {
1354     0.1,
1355     0.2,
1356     -0.1,
1357     -0.2,
1358   };
1359   double varLB[] = {
1360     0.0,
1361     0.0,
1362     0.0,
1363     0.0,
1364   };
1365   double varUB[] = {
1366     1.0,
1367     1.0,
1368     1.0,
1369     1.0,
1370   };
1371 
1372   si->loadProblem(*matrix, varLB, varUB, objective, NULL, NULL);
1373   si->setObjSense(1);
1374 
1375   delete matrix;
1376 
1377   CoinPackedVector row1; // x_2 + x_3 - x_0 <= 0
1378   row1.insert(0, -1.0);
1379   row1.insert(2, 1.0);
1380   row1.insert(3, 1.0);
1381   si->addRow(row1, -si->getInfinity(), 0.0);
1382 
1383   si->initialSolve();
1384   OSIUNITTEST_ASSERT_ERROR(si->isProvenOptimal(), return false, *si, "test17SebastianNowozin");
1385   if (!si->isProvenOptimal())
1386     return false;
1387   /*
1388   Unlike test16, here we do not call si->enableFactorization() first.
1389 */
1390   OSIUNITTEST_CATCH_ERROR(si->enableSimplexInterface(true), return false, *si, "test17SebastianNowozin");
1391   /*
1392   Now check that getReducedGradient works.
1393 */
1394   double dummy[4] = { 1., 1., 1., 1. };
1395   OSIUNITTEST_CATCH_ERROR(si->getReducedGradient(dummy, dummy, dummy), return false, *si, "test17SebastianNowozin");
1396 
1397   return true;
1398 }
1399 
1400 //#############################################################################
1401 // Routines to test various feature groups
1402 //#############################################################################
1403 
1404 /*! \brief Test row and column name manipulation
1405 
1406   emptySi should be an empty solver interface, fn the path to the exmpi1
1407   example.
1408 */
1409 
testNames(const OsiSolverInterface * emptySi,std::string fn)1410 void testNames(const OsiSolverInterface *emptySi, std::string fn)
1411 {
1412   bool recognisesOsiNames = true;
1413   bool ok;
1414 
1415   OsiSolverInterface *si = emptySi->clone();
1416 
1417   std::string exmip1ObjName = "OBJ";
1418   OsiSolverInterface::OsiNameVec exmip1RowNames(0);
1419   exmip1RowNames.push_back("ROW01");
1420   exmip1RowNames.push_back("ROW02");
1421   exmip1RowNames.push_back("ROW03");
1422   exmip1RowNames.push_back("ROW04");
1423   exmip1RowNames.push_back("ROW05");
1424   OsiSolverInterface::OsiNameVec exmip1ColNames(0);
1425   exmip1ColNames.push_back("COL01");
1426   exmip1ColNames.push_back("COL02");
1427   exmip1ColNames.push_back("COL03");
1428   exmip1ColNames.push_back("COL04");
1429   exmip1ColNames.push_back("COL05");
1430   exmip1ColNames.push_back("COL06");
1431   exmip1ColNames.push_back("COL07");
1432   exmip1ColNames.push_back("COL08");
1433 
1434   testingMessage("Testing row/column name handling ...");
1435   /*
1436   Try to get the solver name, but don't immediately abort.
1437 */
1438   std::string solverName = "Unknown solver";
1439   OSIUNITTEST_ASSERT_WARNING(si->getStrParam(OsiSolverName, solverName) == true, {}, solverName, "testNames: getStrParam(OsiSolverName)");
1440   /*
1441   Checking default names. dfltRowColName is pretty liberal about indices, but
1442   they should never be negative. Since default row/column names are a letter
1443   plus n digits, asking for a length of 5 on the objective gets you a leading
1444   'O' plus five more letters.
1445 */
1446   std::string dfltName = si->dfltRowColName('o', 0, 5);
1447   std::string expName = "OBJECT";
1448   OSIUNITTEST_ASSERT_WARNING(dfltName == expName, {}, solverName, "testNames: default objective name");
1449 
1450   dfltName = si->dfltRowColName('r', -1, 5);
1451   expName = "!!invalid Row -1!!";
1452   OSIUNITTEST_ASSERT_WARNING(dfltName == expName, {}, solverName, "testNames: default name for invalid row");
1453 
1454   dfltName = si->dfltRowColName('c', -1, 5);
1455   expName = "!!invalid Col -1!!";
1456   OSIUNITTEST_ASSERT_WARNING(dfltName == expName, {}, solverName, "testNames: default name for invalid col");
1457   /*
1458   Start by telling the SI to use lazy names and see if it comes up with
1459   the right names from the MPS file. There's no point in proceeding further
1460   if we can't read an MPS file.
1461 */
1462   // std::cout << "Testing lazy names from MPS input file." << std::endl ;
1463   OSIUNITTEST_ASSERT_SEVERITY_EXPECTED(si->setIntParam(OsiNameDiscipline, 1) == true, recognisesOsiNames = false, solverName, "testNames: switch to lazy names", TestOutcome::NOTE, false);
1464 
1465   OSIUNITTEST_ASSERT_ERROR(si->readMps(fn.c_str(), "mps") == 0, delete si; return, solverName, "testNames: read MPS");
1466 
1467   OsiSolverInterface::OsiNameVec rowNames;
1468   int rowNameCnt;
1469   OsiSolverInterface::OsiNameVec colNames;
1470   int colNameCnt;
1471 
1472   int m = si->getNumRows();
1473 
1474   if (recognisesOsiNames) {
1475     std::string objName = si->getObjName();
1476     OSIUNITTEST_ASSERT_WARNING(objName == exmip1ObjName, {}, solverName, "testNames lazy names: objective name");
1477     OSIUNITTEST_ASSERT_WARNING(si->getRowName(m) == exmip1ObjName, {}, solverName, "testNames lazy names: name of one after last row is objective name");
1478 
1479     rowNames = si->getRowNames();
1480     rowNameCnt = static_cast< int >(rowNames.size());
1481     OSIUNITTEST_ASSERT_WARNING(rowNameCnt == static_cast< int >(exmip1RowNames.size()), {}, solverName, "testNames lazy names: row names count");
1482     ok = true;
1483     for (int i = 0; i < rowNameCnt; i++) {
1484       if (rowNames[i] != exmip1RowNames[i]) {
1485         ok = false;
1486         std::cout << "ERROR! ";
1487         std::cout << "Row " << i << " is \"" << rowNames[i] << "\" expected \"" << exmip1RowNames[i] << "\"." << std::endl;
1488       }
1489     }
1490     OSIUNITTEST_ASSERT_WARNING(ok == true, {}, solverName, "testNames lazy names: row names");
1491 
1492     colNames = si->getColNames();
1493     colNameCnt = static_cast< int >(colNames.size());
1494     OSIUNITTEST_ASSERT_WARNING(colNameCnt == static_cast< int >(exmip1ColNames.size()), {}, solverName, "testNames lazy names: column names count");
1495     ok = true;
1496     for (int j = 0; j < colNameCnt; j++) {
1497       if (colNames[j] != exmip1ColNames[j]) {
1498         ok = false;
1499         std::cout << "ERROR! ";
1500         std::cout << "Column " << j << " is " << colNames[j] << "\" expected \"" << exmip1ColNames[j] << "\"." << std::endl;
1501       }
1502     }
1503     OSIUNITTEST_ASSERT_WARNING(ok == true, {}, solverName, "testNames lazy names: column names");
1504     /*
1505   Switch back to name discipline 0. We should revert to default names. Failure
1506   to switch back to discipline 0 after successfully switching to discipline 1
1507   is some sort of internal confusion in the Osi; abort the test.
1508 */
1509     // std::cout << "Switching to no names (aka default names)." << std::endl ;
1510     OSIUNITTEST_ASSERT_WARNING(si->setIntParam(OsiNameDiscipline, 0) == true, delete si; return, solverName, "testNames: switch to no names");
1511   }
1512   /*
1513   This block of tests for default names should pass even if the underlying
1514   Osi doesn't recognise OsiNameDiscipline. When using default names, name
1515   vectors are not necessary, hence should have size zero.
1516 */
1517   rowNames = si->getRowNames();
1518   OSIUNITTEST_ASSERT_WARNING(rowNames.size() == 0, {}, solverName, "testNames no names: row names count");
1519   ok = true;
1520   for (int i = 0; i < m; i++) {
1521     if (si->getRowName(i) != si->dfltRowColName('r', i)) {
1522       ok = false;
1523       std::cout << "ERROR! ";
1524       std::cout
1525         << "Row " << i << " is \"" << si->getRowName(i)
1526         << "\" expected \"" << si->dfltRowColName('r', i)
1527         << "\"." << std::endl;
1528     }
1529   }
1530   OSIUNITTEST_ASSERT_WARNING(ok == true, {}, solverName, "testNames no names: row names");
1531 
1532   colNames = si->getColNames();
1533   OSIUNITTEST_ASSERT_WARNING(colNames.size() == 0, {}, solverName, "testNames no names: column names count");
1534   int n = si->getNumCols();
1535   ok = true;
1536   for (int j = 0; j < n; j++) {
1537     if (si->getColName(j) != si->dfltRowColName('c', j)) {
1538       ok = false;
1539       std::cout << "ERROR! ";
1540       std::cout
1541         << "Column " << j << " is \"" << si->getColName(j)
1542         << "\" expected \"" << si->dfltRowColName('c', j)
1543         << "\"." << std::endl;
1544     }
1545   }
1546   OSIUNITTEST_ASSERT_WARNING(ok == true, {}, solverName, "testNames no names: column names");
1547   /*
1548   This is as much as we can ask if the underlying solver doesn't recognise
1549   OsiNameDiscipline. Return if that's the case.
1550 */
1551   if (!recognisesOsiNames) {
1552     delete si;
1553     return;
1554   }
1555   /*
1556   Switch back to lazy names. The previous names should again be available.
1557 */
1558   // std::cout << "Switching back to lazy names." << std::endl ;
1559   OSIUNITTEST_ASSERT_WARNING(si->setIntParam(OsiNameDiscipline, 1) == true, delete si; return, solverName, "testNames: switch to lazy names");
1560   rowNames = si->getRowNames();
1561   rowNameCnt = static_cast< int >(rowNames.size());
1562   OSIUNITTEST_ASSERT_WARNING(rowNameCnt == static_cast< int >(exmip1RowNames.size()), {}, solverName, "testNames lazy names: row names count");
1563   ok = true;
1564   for (int i = 0; i < rowNameCnt; i++) {
1565     if (rowNames[i] != exmip1RowNames[i]) {
1566       ok = false;
1567       std::cout << "ERROR! ";
1568       std::cout << "Row " << i << " is \"" << rowNames[i] << "\" expected \"" << exmip1RowNames[i] << "\"." << std::endl;
1569     }
1570   }
1571   OSIUNITTEST_ASSERT_WARNING(ok == true, {}, solverName, "testNames lazy names: row names");
1572 
1573   colNames = si->getColNames();
1574   colNameCnt = static_cast< int >(colNames.size());
1575   OSIUNITTEST_ASSERT_WARNING(colNameCnt == static_cast< int >(exmip1ColNames.size()), {}, solverName, "testNames lazy names: column names count");
1576   ok = true;
1577   for (int j = 0; j < colNameCnt; j++) {
1578     if (colNames[j] != exmip1ColNames[j]) {
1579       ok = false;
1580       std::cout << "ERROR! ";
1581       std::cout << "Column " << j << " is " << colNames[j] << "\" expected \"" << exmip1ColNames[j] << "\"." << std::endl;
1582     }
1583   }
1584   OSIUNITTEST_ASSERT_WARNING(ok == true, {}, solverName, "testNames lazy names: column names");
1585   /*
1586   Add a row. We should see no increase in the size of the row name vector,
1587   and asking for the name of the new row should return a default name.
1588 */
1589   int nels = 5;
1590   int indices[5] = { 0, 2, 3, 5, 7 };
1591   double els[5] = { 1.0, 3.0, 4.0, 5.0, 42.0 };
1592   CoinPackedVector newRow(nels, indices, els);
1593   si->addRow(newRow, -4.2, .42);
1594   OSIUNITTEST_ASSERT_WARNING(si->getNumRows() == m + 1, delete si; return, solverName, "testNames lazy names: added a row");
1595   rowNames = si->getRowNames();
1596   rowNameCnt = static_cast< int >(rowNames.size());
1597   OSIUNITTEST_ASSERT_WARNING(rowNameCnt == m, {}, solverName, "testNames lazy names: row names count after row addition");
1598   OSIUNITTEST_ASSERT_WARNING(si->getRowName(m) == si->dfltRowColName('r', m), {}, solverName, "testNames lazy names: default name for added row");
1599   /*
1600   Now set a name for the row.
1601 */
1602   std::string newRowName = "NewRow";
1603   si->setRowName(m, newRowName);
1604   OSIUNITTEST_ASSERT_WARNING(si->getRowName(m) == newRowName, {}, solverName, "testNames lazy names: setting new row name");
1605   /*
1606   Ok, who are we really talking with? Delete row 0 and see if the names
1607   change appropriately. Since deleteRows is pure virtual, the names will
1608   change only if the underlying OsiXXX supports names (i.e., it must make
1609   a call to deleteRowNames).
1610 */
1611   // std::cout << "Testing row deletion." << std::endl ;
1612   si->deleteRows(1, indices);
1613   rowNames = si->getRowNames();
1614   rowNameCnt = static_cast< int >(rowNames.size());
1615   OSIUNITTEST_ASSERT_WARNING(rowNameCnt == m, {}, solverName, "testNames lazy names: row names count after deleting row");
1616   ok = true;
1617   for (int i = 0; i < rowNameCnt; i++) {
1618     std::string expected;
1619     if (i != m - 1) {
1620       expected = exmip1RowNames[i + 1];
1621     } else {
1622       expected = newRowName;
1623     }
1624     if (rowNames[i] != expected) {
1625       ok = false;
1626       std::cout << "ERROR! ";
1627       std::cout << "Row " << i << " is \"" << rowNames[i] << "\" expected \"" << expected << "\"." << std::endl;
1628     }
1629   }
1630   OSIUNITTEST_ASSERT_WARNING(ok == true, {}, solverName, "testNames lazy names: row names after deleting row");
1631 
1632   /*
1633   Add/delete a column and do the same tests. Expected results as above.
1634 */
1635   nels = 3;
1636   indices[0] = 0;
1637   indices[1] = 2;
1638   indices[2] = 4;
1639   els[0] = 1.0;
1640   els[1] = 4.0;
1641   els[2] = 24.0;
1642   CoinPackedVector newCol(nels, indices, els);
1643   si->addCol(newCol, -4.2, .42, 42.0);
1644   OSIUNITTEST_ASSERT_ERROR(si->getNumCols() == n + 1, delete si; return, solverName, "testNames lazy names: columns count after adding column");
1645   colNames = si->getColNames();
1646   colNameCnt = static_cast< int >(colNames.size());
1647   OSIUNITTEST_ASSERT_WARNING(colNameCnt == n, {}, solverName, "testNames lazy names: columns names count after adding column");
1648   OSIUNITTEST_ASSERT_WARNING(si->getColName(n) == si->dfltRowColName('c', n), {}, solverName, "testNames lazy names default column name after adding column");
1649   std::string newColName = "NewCol";
1650   si->setColName(n, newColName);
1651   OSIUNITTEST_ASSERT_WARNING(si->getColName(n) == newColName, {}, solverName, "testNames lazy names: setting column name");
1652   // std::cout << "Testing column deletion." << std::endl ;
1653   si->deleteCols(1, indices);
1654   colNames = si->getColNames();
1655   colNameCnt = static_cast< int >(colNames.size());
1656   OSIUNITTEST_ASSERT_WARNING(colNameCnt == n, {}, solverName, "testNames lazy names: column names count after deleting column");
1657   ok = true;
1658   for (int j = 0; j < colNameCnt; j++) {
1659     std::string expected;
1660     if (j != n - 1) {
1661       expected = exmip1ColNames[j + 1];
1662     } else {
1663       expected = newColName;
1664     }
1665     if (colNames[j] != expected) {
1666       ok = false;
1667       std::cout << "ERROR! ";
1668       std::cout << "Column " << j << " is \"" << colNames[j] << "\" expected \"" << expected << "\"." << std::endl;
1669     }
1670   }
1671   OSIUNITTEST_ASSERT_WARNING(ok == true, {}, solverName, "testNames lazy names: column names after deleting column");
1672   /*
1673   Interchange row and column names.
1674 */
1675   // std::cout << "Testing bulk replacement of names." << std::endl ;
1676   si->setRowNames(exmip1ColNames, 0, 3, 2);
1677   rowNames = si->getRowNames();
1678   rowNameCnt = static_cast< int >(rowNames.size());
1679   OSIUNITTEST_ASSERT_WARNING(rowNameCnt == m, {}, solverName, "testNames lazy names: row names count after bulk replace");
1680   ok = true;
1681   for (int i = 0; i < rowNameCnt; i++) {
1682     std::string expected;
1683     if (i < 2) {
1684       expected = exmip1RowNames[i + 1];
1685     } else if (i >= 2 && i <= 4) {
1686       expected = exmip1ColNames[i - 2];
1687     } else {
1688       expected = newRowName;
1689     }
1690     if (rowNames[i] != expected) {
1691       ok = false;
1692       std::cout << "ERROR! ";
1693       std::cout << "Row " << i << " is \"" << rowNames[i] << "\" expected \"" << expected << "\"." << std::endl;
1694     }
1695   }
1696   OSIUNITTEST_ASSERT_WARNING(ok == true, {}, solverName, "testNames lazy names: row names after bulk replace");
1697 
1698   si->setColNames(exmip1RowNames, 3, 2, 0);
1699   colNames = si->getColNames();
1700   colNameCnt = static_cast< int >(colNames.size());
1701   OSIUNITTEST_ASSERT_WARNING(colNameCnt == n, {}, solverName, "testNames lazy names: column names count after bulk replace");
1702   ok = true;
1703   for (int j = 0; j < colNameCnt; j++) {
1704     std::string expected;
1705     if (j < 2) {
1706       expected = exmip1RowNames[j + 3];
1707     } else if (j >= 2 && j <= 6) {
1708       expected = exmip1ColNames[j + 1];
1709     } else {
1710       expected = newColName;
1711     }
1712     if (colNames[j] != expected) {
1713       ok = false;
1714       std::cout << "ERROR! ";
1715       std::cout << "Column " << j << " is \"" << colNames[j] << "\" expected \"" << expected << "\"." << std::endl;
1716     }
1717   }
1718   OSIUNITTEST_ASSERT_WARNING(ok == true, {}, solverName, "testNames lazy names: column names after bulk replace");
1719   /*
1720   Delete a few row and column names (directly, as opposed to deleting rows or
1721   columns). Names should shift downward.
1722 */
1723   // std::cout << "Testing name deletion." << std::endl ;
1724   si->deleteRowNames(0, 2);
1725   rowNames = si->getRowNames();
1726   rowNameCnt = static_cast< int >(rowNames.size());
1727   OSIUNITTEST_ASSERT_WARNING(rowNameCnt == m - 2, {}, solverName, "testNames lazy names: row names count after deleting 2 rows");
1728   ok = true;
1729   for (int i = 0; i < rowNameCnt; i++) {
1730     std::string expected;
1731     if (i < rowNameCnt) {
1732       expected = exmip1ColNames[i];
1733     }
1734     if (rowNames[i] != expected) {
1735       ok = false;
1736       std::cout << "ERROR! ";
1737       std::cout << "Row " << i << " is \"" << rowNames[i] << "\" expected \"" << expected << "\"." << std::endl;
1738     }
1739   }
1740   OSIUNITTEST_ASSERT_WARNING(ok == true, {}, solverName, "testNames lazy names: row names after deleting 2 rows");
1741 
1742   si->deleteColNames(5, 3);
1743   colNames = si->getColNames();
1744   colNameCnt = static_cast< int >(colNames.size());
1745   OSIUNITTEST_ASSERT_WARNING(colNameCnt == n - 3, {}, solverName, "testNames lazy names: column names count after deleting 3 columns");
1746   ok = true;
1747   for (int j = 0; j < colNameCnt; j++) {
1748     std::string expected;
1749     if (j < 2) {
1750       expected = exmip1RowNames[j + 3];
1751     } else if (j >= 2 && j < colNameCnt) {
1752       expected = exmip1ColNames[j + 1];
1753     }
1754     if (colNames[j] != expected) {
1755       ok = false;
1756       std::cout << "ERROR! ";
1757       std::cout << "Column " << j << " is \"" << colNames[j] << "\" expected \"" << expected << "\"." << std::endl;
1758     }
1759   }
1760   OSIUNITTEST_ASSERT_WARNING(ok == true, {}, solverName, "testNames lazy names: column names after deleting 3 columns");
1761   /*
1762   Finally, switch to full names, and make sure we retrieve full length
1763   vectors.
1764 */
1765   // std::cout << "Switching to full names." << std::endl ;
1766   OSIUNITTEST_ASSERT_WARNING(si->setIntParam(OsiNameDiscipline, 2), delete si; return, solverName, "testNames lazy names: change name discipline");
1767   m = si->getNumRows();
1768   rowNames = si->getRowNames();
1769   rowNameCnt = static_cast< int >(rowNames.size());
1770   OSIUNITTEST_ASSERT_WARNING(rowNameCnt == m + 1, {}, solverName, "testNames full names: row names count");
1771   OSIUNITTEST_ASSERT_WARNING(rowNames[m] == exmip1ObjName, {}, solverName, "testNames full names: objective name");
1772   ok = true;
1773   for (int i = 0; i < rowNameCnt - 1; i++) {
1774     std::string expected;
1775     if (i < 3) {
1776       expected = exmip1ColNames[i];
1777     } else {
1778       expected = si->dfltRowColName('r', i);
1779     }
1780     if (rowNames[i] != expected) {
1781       ok = false;
1782       std::cout << "ERROR! ";
1783       std::cout << "Row " << i << " is \"" << rowNames[i] << "\" expected \"" << expected << "\"." << std::endl;
1784     }
1785   }
1786   OSIUNITTEST_ASSERT_WARNING(ok == true, {}, solverName, "testNames full names: row names");
1787 
1788   n = si->getNumCols();
1789   colNames = si->getColNames();
1790   colNameCnt = static_cast< int >(colNames.size());
1791   OSIUNITTEST_ASSERT_WARNING(colNameCnt == n, {}, solverName, "testNames full names: column names count");
1792   ok = true;
1793   for (int j = 0; j < colNameCnt; j++) {
1794     std::string expected;
1795     if (j < 2) {
1796       expected = exmip1RowNames[j + 3];
1797     } else if (j >= 2 && j <= 4) {
1798       expected = exmip1ColNames[j + 1];
1799     } else {
1800       expected = si->dfltRowColName('c', j);
1801     }
1802     if (colNames[j] != expected) {
1803       ok = false;
1804       std::cout << "ERROR! ";
1805       std::cout << "Column " << j << " is " << colNames[j] << "\" expected \"" << expected << "\"." << std::endl;
1806     }
1807   }
1808   OSIUNITTEST_ASSERT_WARNING(ok == true, {}, solverName, "testNames full names: column names");
1809 
1810   delete si;
1811 }
1812 
1813 //--------------------------------------------------------------------------
1814 
1815 /*! \brief Tests for a solution imposed by the user.
1816 
1817   Checks the routines setColSolution (primal variables) and setRowSolution
1818   (dual variables). Goes on to check that getReducedCost and getRowActivity
1819   use the imposed solution.
1820 
1821   The prototype OSI supplied as the parameter should be loaded with a smallish
1822   problem.
1823 */
testSettingSolutions(OsiSolverInterface & proto)1824 void testSettingSolutions(OsiSolverInterface &proto)
1825 
1826 {
1827   OsiSolverInterface *si = proto.clone();
1828   bool allOK = true;
1829   int i;
1830   int m = si->getNumRows();
1831   int n = si->getNumCols();
1832   double mval, cval, rval;
1833   const double *rowVec, *colVec, *objVec;
1834   double *colShouldBe = new double[m];
1835   double *rowShouldBe = new double[n];
1836 
1837   CoinAbsFltEq fltEq;
1838 
1839   testingMessage("Checking that solver can set row and column solutions ...");
1840 
1841   /*
1842   Create dummy solution vectors.
1843 */
1844   double *dummyColSol = new double[n];
1845   for (i = 0; i < n; i++) {
1846     dummyColSol[i] = i + .5;
1847   }
1848 
1849   double *dummyRowSol = new double[m];
1850   for (i = 0; i < m; i++) {
1851     dummyRowSol[i] = i - .5;
1852   }
1853 
1854   /*
1855   First the values we can set directly: primal (column) and dual (row)
1856   solutions. The osi should copy the vector, hence the pointer we get back
1857   should not be the pointer we supply. But it's reasonable to expect exact
1858   equality, as no arithmetic should be performed.
1859 */
1860   si->setColSolution(dummyColSol);
1861   OSIUNITTEST_ASSERT_ERROR(dummyColSol != si->getColSolution(), allOK = false, *si, "setting solutions: solver should not return original pointer");
1862   rowVec = si->getColSolution();
1863 
1864   bool ok = true;
1865   for (i = 0; i < n; i++) {
1866     mval = rowVec[i];
1867     rval = dummyColSol[i];
1868     if (mval != rval) {
1869       ok = false;
1870       std::cout
1871         << "x<" << i << "> = " << mval
1872         << ", expecting " << rval
1873         << ", |error| = " << (mval - rval)
1874         << "." << std::endl;
1875     }
1876   }
1877   OSIUNITTEST_ASSERT_ERROR(ok == true, allOK = false, *si, "setting solutions: solver stored column solution correctly");
1878 
1879   si->setRowPrice(dummyRowSol);
1880   OSIUNITTEST_ASSERT_ERROR(dummyRowSol != si->getRowPrice(), allOK = false, *si, "setting solutions: solver should not return original pointer");
1881   colVec = si->getRowPrice();
1882 
1883   if (colVec != NULL) {
1884     ok = true;
1885     for (i = 0; i < m; i++) {
1886       mval = colVec[i];
1887       cval = dummyRowSol[i];
1888       if (mval != cval) {
1889         ok = false;
1890         std::cout
1891           << "y<" << i << "> = " << mval
1892           << ", expecting " << cval
1893           << ", |error| = " << (mval - cval)
1894           << "." << std::endl;
1895       }
1896     }
1897   } else
1898     ok = false;
1899   OSIUNITTEST_ASSERT_ERROR(ok == true, allOK = false, *si, "setting solutions: solver stored row price correctly");
1900   /*
1901   Now let's get serious. Check that reduced costs and row activities match
1902   the values we just specified for row and column solutions. Absolute
1903   equality cannot be assumed here.
1904 
1905   Reduced costs first: c - yA
1906 */
1907   rowVec = si->getReducedCost();
1908   objVec = si->getObjCoefficients();
1909   const CoinPackedMatrix *mtx = si->getMatrixByCol();
1910   mtx->transposeTimes(dummyRowSol, rowShouldBe);
1911   if (rowVec != NULL) {
1912     ok = true;
1913     for (i = 0; i < n; i++) {
1914       mval = rowVec[i];
1915       rval = objVec[i] - rowShouldBe[i];
1916       if (!fltEq(mval, rval)) {
1917         ok = false;
1918         std::cout
1919           << "cbar<" << i << "> = " << mval
1920           << ", expecting " << rval
1921           << ", |error| = " << (mval - rval)
1922           << "." << std::endl;
1923       }
1924     }
1925   } else
1926     ok = false;
1927   OSIUNITTEST_ASSERT_WARNING(ok == true, allOK = false, *si, "setting solutions: reduced costs from solution set with setRowPrice");
1928 
1929   /*
1930   Row activity: Ax
1931 */
1932   colVec = si->getRowActivity();
1933   mtx->times(dummyColSol, colShouldBe);
1934   ok = true;
1935   for (i = 0; i < m; i++) {
1936     mval = colVec[i];
1937     cval = colShouldBe[i];
1938     if (!fltEq(mval, cval)) {
1939       ok = false;
1940       std::cout
1941         << "lhs<" << i << "> = " << mval
1942         << ", expecting " << cval
1943         << ", |error| = " << (mval - cval)
1944         << "." << std::endl;
1945     }
1946   }
1947   OSIUNITTEST_ASSERT_WARNING(ok == true, allOK = false, *si, "setting solutions: row activity from solution set with setColSolution");
1948 
1949   if (allOK) {
1950     testingMessage(" ok.\n");
1951   } else {
1952     failureMessage(*si, "Errors handling imposed column/row solutions.");
1953   }
1954 
1955   delete[] dummyColSol;
1956   delete[] colShouldBe;
1957   delete[] dummyRowSol;
1958   delete[] rowShouldBe;
1959 
1960   delete si;
1961 
1962   return;
1963 }
1964 
1965 //--------------------------------------------------------------------------
1966 
1967 /*! \brief Helper routines to test OSI parameters.
1968 
1969   A set of helper routines to test integer, double, and hint parameter
1970   set/get routines.
1971 */
1972 
testIntParam(OsiSolverInterface * si,int k,int val)1973 bool testIntParam(OsiSolverInterface *si, int k, int val)
1974 {
1975   int i = 123456789, orig = 123456789;
1976   bool ret;
1977   OsiIntParam key = static_cast< OsiIntParam >(k);
1978   si->getIntParam(key, orig);
1979   if (si->setIntParam(key, val)) {
1980     ret = (si->getIntParam(key, i) == true) && (i == val);
1981   } else {
1982     ret = (si->getIntParam(key, i) == true) && (i == orig);
1983   }
1984   return ret;
1985 }
1986 
testDblParam(OsiSolverInterface * si,int k,double val)1987 bool testDblParam(OsiSolverInterface *si, int k, double val)
1988 {
1989   double d = 123456789.0, orig = 123456789.0;
1990   bool ret;
1991   OsiDblParam key = static_cast< OsiDblParam >(k);
1992   si->getDblParam(key, orig);
1993   if (si->setDblParam(key, val)) {
1994     ret = (si->getDblParam(key, d) == true) && (d == val);
1995   } else {
1996     ret = (si->getDblParam(key, d) == true) && (d == orig);
1997   }
1998   return ret;
1999 }
2000 
testHintParam(OsiSolverInterface * si,int k,bool sense,OsiHintStrength strength,int * throws)2001 bool testHintParam(OsiSolverInterface *si, int k, bool sense,
2002   OsiHintStrength strength, int *throws)
2003 /*
2004   Tests for proper behaviour of [set,get]HintParam methods. The initial get
2005   tests the return value to see if the hint is implemented; the values
2006   returned for sense and strength are not checked.
2007 
2008   If the hint is implemented, a pair of set/get calls is performed at the
2009   strength specified by the parameter. The set can return true or, at
2010   strength OsiForceDo, throw an exception if the solver cannot comply. The
2011   rationale would be that only OsiForceDo must be obeyed, so anything else
2012   should return true regardless of whether the solver followed the hint.
2013 
2014   The test checks that the value and strength returned by getHintParam matches
2015   the previous call to setHintParam. This is arguably wrong --- one can argue
2016   that it should reflect the solver's ability to comply with the hint. But
2017   that's how the OSI interface standard has evolved up to now.
2018 
2019   If the hint is not implemented, attempting to set the hint should return
2020   false, or throw an exception at strength OsiForceDo.
2021 
2022   The testing code which calls testHintParam is set up so that a successful
2023   return is defined as true if the hint is implemented, false if it is not.
2024   Information printing is suppressed; uncomment and recompile if you want it.
2025 */
2026 {
2027   bool post_sense;
2028   OsiHintStrength post_strength;
2029   bool ret;
2030   OsiHintParam key = static_cast< OsiHintParam >(k);
2031 
2032   if (si->getHintParam(key, post_sense, post_strength)) {
2033     ret = false;
2034     std::ostringstream tstname;
2035     tstname << "testHintParam: hint " << static_cast< int >(key) << " sense " << sense << " strength " << static_cast< int >(strength);
2036     if (strength == OsiForceDo) {
2037       try {
2038         if (si->setHintParam(key, sense, strength)) {
2039           ret = (si->getHintParam(key, post_sense, post_strength) == true) && (post_strength == strength) && (post_sense == sense);
2040         }
2041       } catch (CoinError &e) {
2042         std::cout << tstname.str() << " catched CoinError exception " << e.message() << std::endl;
2043         ret = true;
2044         (*throws)++;
2045       }
2046     } else {
2047       OSIUNITTEST_CATCH_WARNING(
2048         if (si->setHintParam(key, sense, strength)) {
2049           ret = (si->getHintParam(key, post_sense, post_strength) == true) && (post_strength == strength) && (post_sense == sense);
2050         },
2051         (*throws)++;
2052         ret = (strength == OsiForceDo),
2053         *si, tstname.str());
2054     }
2055   } else {
2056     ret = true;
2057     std::ostringstream tstname;
2058     tstname << "testHintParam: hint " << static_cast< int >(key) << " sense " << sense << " strength " << static_cast< int >(strength);
2059     OSIUNITTEST_CATCH_WARNING(ret = si->setHintParam(key, sense, strength), (*throws)++; ret = !(strength == OsiForceDo), *si, tstname.str());
2060   }
2061 
2062   return ret;
2063 }
2064 
2065 /*
2066   Test functionality related to the objective function:
2067     * Does the solver properly handle a constant offset?
2068     * Does the solver properly handle primal and dual objective limits? This
2069       routine only checks for the correct answers. It does not check whether
2070       the solver stops early due to objective limits.
2071     * Does the solver properly handle minimisation / maximisation via
2072       setObjSense?
2073 
2074   The return value is the number of failures recorded by the routine.
2075 */
2076 
testObjFunctions(const OsiSolverInterface * emptySi,const std::string & mpsDir)2077 void testObjFunctions(const OsiSolverInterface *emptySi,
2078   const std::string &mpsDir)
2079 
2080 {
2081   OsiSolverInterface *si = emptySi->clone();
2082   CoinRelFltEq eq;
2083   int i;
2084 
2085   std::cout
2086     << "Testing functionality related to the objective." << std::endl;
2087 
2088   std::string solverName = "Unknown solver";
2089   si->getStrParam(OsiSolverName, solverName);
2090   /*
2091   Check for default objective sense. This should be minimisation.
2092 */
2093   OSIUNITTEST_ASSERT_ERROR(si->getObjSense() == 1.0 || si->getObjSense() == -1.0, {}, solverName, "testObjFunctions: default objective sense is determinant value");
2094   OSIUNITTEST_ASSERT_WARNING(si->getObjSense() == 1.0, {}, solverName, "testObjFunctions: default objective sense is minimization");
2095 
2096   /*
2097   Read in e226; chosen because it has an offset defined in the mps file.
2098   We can't continue if we can't read the test problem.
2099 */
2100   std::string fn = mpsDir + "e226";
2101   OSIUNITTEST_ASSERT_ERROR(si->readMps(fn.c_str(), "mps") == 0, delete si; return, solverName, "testObjFunctions: read MPS");
2102   /*
2103   Solve and test for the correct objective value.
2104 */
2105   si->initialSolve();
2106   double objValue = si->getObjValue();
2107   double objNoOffset = -18.751929066;
2108   double objOffset = +7.113;
2109   OSIUNITTEST_ASSERT_ERROR(eq(objValue, (objNoOffset + objOffset)), {}, solverName, "testObjFunctions: getObjValue with constant in objective");
2110   /*
2111   Test objective limit methods. If no limit has been specified, they should
2112   return false.
2113 */
2114   OSIUNITTEST_ASSERT_ERROR(!si->isPrimalObjectiveLimitReached(), {}, solverName, "testObjFunctions: isPrimalObjectiveLimitReached without limit (min)");
2115   OSIUNITTEST_ASSERT_ERROR(!si->isDualObjectiveLimitReached(), {}, solverName, "testObjFunctions: isDualObjectiveLimitReached without limit (min)");
2116   /* One could think that also no limit should be reached in case of maximization.
2117  * However, by default primal and dual limits are not unset, but set to plus or minus infinity.
2118  * So, if we change the objective sense, we need to remember to set also the limits to a nonlimiting value.
2119  */
2120   si->setObjSense(-1.0);
2121   si->setDblParam(OsiPrimalObjectiveLimit, COIN_DBL_MAX);
2122   si->setDblParam(OsiDualObjectiveLimit, -COIN_DBL_MAX);
2123   OSIUNITTEST_ASSERT_ERROR(!si->isPrimalObjectiveLimitReached(), {}, solverName, "testObjFunctions: isPrimalObjectiveLimitReached without limit (max)");
2124   OSIUNITTEST_ASSERT_ERROR(!si->isDualObjectiveLimitReached(), {}, solverName, "testObjFunctions: isDualObjectiveLimitReached without limit (max)");
2125   si->setObjSense(1.0);
2126   si->setDblParam(OsiPrimalObjectiveLimit, -COIN_DBL_MAX);
2127   si->setDblParam(OsiDualObjectiveLimit, COIN_DBL_MAX);
2128   /*
2129   Test objective limit methods. There's no attempt to see if the solver stops
2130   early when given a limit that's tighter than the optimal objective.  All
2131   we're doing here is checking that the routines return the correct value
2132   when the limits are exceeded. For minimisation (maximisation) the primal
2133   limit represents an acceptable level of `goodness'; to be true, we should
2134   be below (above) it. The dual limit represents an unacceptable level of
2135   `badness'; to be true, we should be above (below) it.
2136 
2137   The loop performs two iterations, first for maximisation, then for
2138   minimisation. For maximisation, z* = 111.65096. The second iteration is
2139   sort of redundant, but it does test the ability to switch back to
2140   minimisation.
2141 */
2142   double expectedObj[2] = { 111.650960689, objNoOffset + objOffset };
2143   double primalObjLim[2] = { 100.0, -5.0 };
2144   double dualObjLim[2] = { 120.0, -15.0 };
2145   double optSense[2] = { -1.0, 1.0 };
2146   for (i = 0; i <= 1; i++) {
2147     si->setObjSense(optSense[i]);
2148 
2149     // reset objective limits to infinity
2150     si->setDblParam(OsiPrimalObjectiveLimit, -optSense[i] * COIN_DBL_MAX);
2151     si->setDblParam(OsiDualObjectiveLimit, optSense[i] * COIN_DBL_MAX);
2152 
2153     si->initialSolve();
2154     objValue = si->getObjValue();
2155     OSIUNITTEST_ASSERT_ERROR(eq(objValue, expectedObj[i]), {}, solverName, "testObjFunctions: optimal value during max/min switch");
2156 
2157     si->setDblParam(OsiPrimalObjectiveLimit, primalObjLim[i]);
2158     si->setDblParam(OsiDualObjectiveLimit, dualObjLim[i]);
2159     OSIUNITTEST_ASSERT_WARNING(si->isPrimalObjectiveLimitReached(), {}, solverName, "testObjFunctions: primal objective limit");
2160     OSIUNITTEST_ASSERT_WARNING(si->isDualObjectiveLimitReached(), {}, solverName, "testObjFunctions: dual objective limit");
2161   }
2162 
2163   delete si;
2164   si = 0;
2165 
2166   /*
2167   Finally, check that the objective sense is treated as a parameter of the
2168   solver, not a property of the problem. The test clones emptySi, inverts the
2169   default objective sense, clones a second solver, then loads and optimises
2170   e226.
2171 */
2172   si = emptySi->clone();
2173   double dfltSense = si->getObjSense();
2174   dfltSense = -dfltSense;
2175   si->setObjSense(dfltSense);
2176   OsiSolverInterface *si2 = si->clone();
2177   delete si;
2178   si = NULL;
2179   OSIUNITTEST_ASSERT_ERROR(si2->getObjSense() == dfltSense, {}, solverName, "testObjFunctions: objective sense preserved by clone");
2180   OSIUNITTEST_ASSERT_ERROR(si2->readMps(fn.c_str(), "mps") == 0, delete si; return, solverName, "testObjFunctions: 2nd read MPS");
2181   OSIUNITTEST_ASSERT_ERROR(si2->getObjSense() == dfltSense, {}, solverName, "testObjFunctions: objective sense preserved by problem load");
2182   si2->initialSolve();
2183   if (dfltSense < 0) {
2184     i = 0;
2185   } else {
2186     i = 1;
2187   }
2188   objValue = si2->getObjValue();
2189   OSIUNITTEST_ASSERT_ERROR(eq(objValue, expectedObj[i]), {}, solverName, "testObjFunctions: optimal value of load problem after set objective sense");
2190 
2191   delete si2;
2192 }
2193 
2194 /*
2195   Check that solver returns the proper status for artificial variables. The OSI
2196   convention is that this status should be reported as if the artificial uses a
2197   positive coefficient. Specifically:
2198 
2199   ax <= b  ==>  ax + s = b,       0 <= s <= infty
2200   ax >= b  ==>  ax + s = b,  -infty <= s <= 0
2201 
2202   If the constraint is tight at optimum, then the status should be
2203   atLowerBound for a <= constraint, atUpperBound for a >= constraint. The
2204   test problem is
2205 
2206       x1      >= -5	(c0)
2207       x1      <=  2	(c1)
2208            x2 >= 44	(c2)
2209 	   x2 <= 51	(c3)
2210 
2211   This is evaluated for two objectives, so that we have all combinations of
2212   tight constraints under minimisation and maximisation.
2213 
2214 		max x1-x2	min x1-x2
2215 
2216 	  obj	  -42		  -56
2217 
2218 	  c0	  basic		  upper
2219 	  c1	  lower		  basic
2220 	  c2	  upper		  basic
2221 	  c3	  basic		  lower
2222 
2223 */
2224 
testArtifStatus(const OsiSolverInterface * emptySi)2225 void testArtifStatus(const OsiSolverInterface *emptySi)
2226 
2227 {
2228   OsiSolverInterface *si = emptySi->clone();
2229   double infty = si->getInfinity();
2230 
2231   testingMessage("Testing status for artificial variables.\n");
2232   /*
2233   Set up the example problem in packed column-major vector format and load it
2234   into the solver.
2235 */
2236   int colCnt = 2;
2237   int rowCnt = 4;
2238   int indices[] = { 0, 1, 2, 3 };
2239   double coeffs[] = { 1.0, 1.0, 1.0, 1.0 };
2240   CoinBigIndex starts[] = { 0, 2, 4 };
2241   double obj[] = { 1.0, -1.0 };
2242 
2243   double vubs[] = { infty, infty };
2244   double vlbs[] = { -infty, -infty };
2245 
2246   double rubs[] = { infty, 2.0, infty, 51.0 };
2247   double rlbs[] = { -5.0, -infty, 44.0, -infty };
2248   std::string contype[] = { ">=", "<=", ">=", "<=" };
2249   std::string statCode[] = { "isFree", "basic",
2250     "atUpperBound", "atLowerBound" };
2251   std::string sense[] = { "maximise", "minimise" };
2252 
2253   si->loadProblem(colCnt, rowCnt,
2254     starts, indices, coeffs, vlbs, vubs, obj, rlbs, rubs);
2255   /*
2256   Vectors for objective sense and correct answers. Maximise first.
2257 */
2258   double objSense[] = { -1.0, 1.0 };
2259   double zopt[] = { -42.0, -56 };
2260   CoinWarmStartBasis::Status goodStatus[] = { CoinWarmStartBasis::basic,
2261     CoinWarmStartBasis::atLowerBound,
2262     CoinWarmStartBasis::atUpperBound,
2263     CoinWarmStartBasis::basic,
2264     CoinWarmStartBasis::atUpperBound,
2265     CoinWarmStartBasis::basic,
2266     CoinWarmStartBasis::basic,
2267     CoinWarmStartBasis::atLowerBound };
2268   /*
2269   Get to work. Open a loop, set the objective sense, solve the problem, and
2270   then check the results: We should have an optimal solution, with the correct
2271   objective. We should be able to ask for a warm start basis, and it should
2272   show the correct status.
2273 */
2274   CoinRelFltEq eq;
2275 
2276   for (int iter = 0; iter <= 1; iter++) {
2277     si->setObjSense(objSense[iter]);
2278     si->initialSolve();
2279     OSIUNITTEST_ASSERT_ERROR(si->isProvenOptimal(), {}; continue, *si, "testArtifStatus: initial solve");
2280     OSIUNITTEST_ASSERT_ERROR(eq(si->getObjValue(), zopt[iter]), {}; continue, *si, "testArtifStatus: initial solve optimal value");
2281 
2282     CoinWarmStart *ws = si->getWarmStart();
2283     CoinWarmStartBasis *wsb = dynamic_cast< CoinWarmStartBasis * >(ws);
2284     OSIUNITTEST_ASSERT_ERROR(wsb != NULL, {}; continue, *si, "testArtifStatus: initial solve warm start basis");
2285 
2286     CoinWarmStartBasis::Status stati;
2287 
2288     bool ok = true;
2289     for (int i = 0; i < rowCnt; i++) {
2290       stati = wsb->getArtifStatus(i);
2291       if (stati != goodStatus[iter * rowCnt + i]) {
2292         ok = false;
2293         std::cout << "Incorrect status " << statCode[stati] << " for " << contype[i] << " constraint c" << i << " (" << sense[iter] << "), expected " << statCode[goodStatus[iter * rowCnt + i]] << "." << std::endl;
2294       }
2295     }
2296     OSIUNITTEST_ASSERT_ERROR(ok == true, {}, *si, "testArtifStatus: artificial variable status");
2297 
2298     delete ws;
2299   }
2300   /*
2301   Clean up.
2302 */
2303   delete si;
2304 }
2305 
2306 /*
2307   This method checks [cbar<B> cbar<N>] = [c<B>-yB c<N>-yN] = [0 (c<N> - yN)]
2308   for the architectural variables. (But note there's no need to discriminate
2309   between basic and nonbasic columns in the tests below.)  This provides a
2310   moderately strong check on the correctness of y (getRowPrice) and cbar
2311   (getReducedCosts). The method also checks that the sign of cbar<N> is
2312   appropriate for the status of the variables.
2313 */
2314 
testReducedCosts(const OsiSolverInterface * emptySi,const std::string & sampleDir)2315 void testReducedCosts(const OsiSolverInterface *emptySi,
2316   const std::string &sampleDir)
2317 
2318 {
2319   OsiSolverInterface *si = emptySi->clone();
2320   std::string solverName;
2321   si->getStrParam(OsiSolverName, solverName);
2322   si->setHintParam(OsiDoReducePrint, true, OsiHintDo);
2323 
2324   std::cout << "Testing duals and reduced costs ... ";
2325   /*
2326   Read p0033 and solve to optimality (minimisation).
2327 */
2328   std::string fn = sampleDir + "p0033";
2329   si->readMps(fn.c_str(), "mps");
2330   si->setObjSense(1.0);
2331   si->initialSolve();
2332   OSIUNITTEST_ASSERT_ERROR(si->isProvenOptimal(), return, solverName, "testReducedCosts: solving p0033");
2333   if (OsiUnitTest::verbosity >= 1) {
2334     std::cout
2335       << "  " << solverName << " solved p0033 z = " << si->getObjValue()
2336       << "." << std::endl;
2337   }
2338   /*
2339   Get the unchanging components: size, matrix, objective.
2340 */
2341   int n = si->getNumCols();
2342   double *cbarCalc = new double[n];
2343   double dualTol;
2344   si->getDblParam(OsiDualTolerance, dualTol);
2345   CoinRelFltEq eq;
2346   std::string statNames[] = { "NBFR", "B", "NBUB", "NBLB" };
2347   /*
2348   Resolve, first as maximisation, then minimisation, and do the tests.
2349 */
2350   double minmax[] = { -1.0, 1.0 };
2351   for (int ndx = 0; ndx < 2; ndx++) {
2352     si->setObjSense(minmax[ndx]);
2353     si->resolve();
2354     OSIUNITTEST_ASSERT_ERROR(si->isProvenOptimal(), return, solverName, "testReducedCosts: solving p0033 after changing objective sense");
2355     if (OsiUnitTest::verbosity >= 1) {
2356       std::cout
2357         << "  " << solverName
2358         << ((si->getObjSense() < 0) ? " maximised" : " minimised")
2359         << " p0033 z = " << si->getObjValue()
2360         << "." << std::endl;
2361     }
2362     /*
2363   Retrieve status, duals, and reduced costs. Calculate c - yA.
2364 */
2365     const CoinPackedMatrix *mtx = si->getMatrixByCol();
2366     const double *c = si->getObjCoefficients();
2367     const CoinWarmStartBasis *wsb = dynamic_cast< CoinWarmStartBasis * >(si->getWarmStart());
2368     double dir = si->getObjSense();
2369     const double *y = si->getRowPrice();
2370     const double *cbar = si->getReducedCost();
2371     mtx->transposeTimes(y, cbarCalc);
2372     std::transform(c, c + n, cbarCalc, cbarCalc, std::minus< double >());
2373     /*
2374   Walk the architecturals and check that cbar<j> = c<j> - ya<j> and has the
2375   correct sign given the status and objective sense (max/min).
2376 */
2377     bool cbarCalcj_ok = true;
2378     bool testcbarj_ok = true;
2379     for (int j = 0; j < n; j++) {
2380       CoinWarmStartBasis::Status statj = wsb->getStructStatus(j);
2381       double cbarj = cbar[j];
2382       double cbarCalcj = cbarCalc[j];
2383 
2384       if (OsiUnitTest::verbosity >= 1) {
2385         std::cout << "  x<" << j << "> " << statNames[statj] << ", cbar<" << j << "> = " << cbarj << "." << std::endl;
2386       }
2387 
2388       if (!eq(cbarj, cbarCalcj)) {
2389         cbarCalcj_ok = false;
2390         if (OsiUnitTest::verbosity >= 1) {
2391           std::cout
2392             << "  " << cbarj << " = cbar<" << j << "> != c<"
2393             << j << "> - ya<" << j << "> = "
2394             << cbarCalcj << ", diff = "
2395             << cbarj - cbarCalcj << "." << std::endl;
2396         }
2397       }
2398 
2399       double testcbarj = dir * cbarj;
2400       switch (statj) {
2401       case CoinWarmStartBasis::atUpperBound: {
2402         if (testcbarj > dualTol) {
2403           testcbarj_ok = false;
2404           if (OsiUnitTest::verbosity >= 1) {
2405             std::cout
2406               << "  cbar<" << j << "> = " << cbarj
2407               << " has the wrong sign for a NBUB variable."
2408               << std::endl;
2409           }
2410         }
2411         break;
2412       }
2413       case CoinWarmStartBasis::atLowerBound: {
2414         if (testcbarj < -dualTol) {
2415           testcbarj_ok = false;
2416           if (OsiUnitTest::verbosity >= 1) {
2417             std::cout
2418               << "  cbar<" << j << "> = " << cbarj
2419               << " has the wrong sign for a NBLB variable."
2420               << std::endl;
2421           }
2422         }
2423         break;
2424       }
2425       case CoinWarmStartBasis::isFree: {
2426         if (CoinAbs(testcbarj) > dualTol) {
2427           testcbarj_ok = false;
2428           if (OsiUnitTest::verbosity >= 1) {
2429             std::cout
2430               << "  cbar<" << j << "> = " << cbarj
2431               << " should be zero for a NBFR variable."
2432               << std::endl;
2433           }
2434         }
2435         break;
2436       }
2437       case CoinWarmStartBasis::basic: {
2438         if (CoinAbs(testcbarj) > dualTol) {
2439           testcbarj_ok = false;
2440           if (OsiUnitTest::verbosity >= 1) {
2441             std::cout
2442               << "  cbar<" << j << "> = " << cbarj
2443               << " should be zero for a basic variable."
2444               << std::endl;
2445           }
2446         }
2447         break;
2448       }
2449       default: {
2450         break;
2451       }
2452       }
2453     }
2454     OSIUNITTEST_ASSERT_ERROR(cbarCalcj_ok == true, {}, solverName, "testReducedCosts: reduced costs");
2455     OSIUNITTEST_ASSERT_ERROR(testcbarj_ok == true, {}, solverName, "testReducedCosts: basis status of structural variable");
2456 
2457     delete wsb;
2458   }
2459 
2460   delete[] cbarCalc;
2461 }
2462 
2463 /*
2464   Test the writeMps and writeMpsNative functions by loading a problem,
2465   writing it out to a file, reloading it, and solving.
2466 
2467   Implicitly assumes readMps has already been tested.
2468 
2469   fn should be the path to exmip1.
2470 */
2471 
testWriteMps(const OsiSolverInterface * emptySi,std::string fn)2472 void testWriteMps(const OsiSolverInterface *emptySi, std::string fn)
2473 
2474 {
2475   testingMessage("Testing writeMps and writeMpsNative.\n");
2476 
2477   CoinRelFltEq eq(1.0e-8);
2478 
2479   OsiSolverInterface *si1 = emptySi->clone();
2480   OsiSolverInterface *si2 = emptySi->clone();
2481   OsiSolverInterface *si3 = emptySi->clone();
2482   /*
2483   Sanity test. Read in exmip1 and do an initialSolve.
2484 */
2485   OSIUNITTEST_ASSERT_ERROR(si1->readMps(fn.c_str(), "mps") == 0, return, *si1, "testWriteMps: read MPS");
2486 
2487   bool solved = true;
2488   OSIUNITTEST_CATCH_SEVERITY_EXPECTED(si1->initialSolve(), solved = false, *si1, "testWriteMps: solving LP",
2489     TestOutcome::ERROR, e.className() == "OsiVolSolverInterface" || e.className() == "OsiTestSolverInterface");
2490   double soln = si1->getObjValue();
2491   /*
2492   Write a test output file with writeMpsNative, then read and solve. See if
2493   we get the right answer.
2494 
2495   FIXME: Really, this test should verify values --- Vol could participate in
2496   that (lh, 070726).
2497 */
2498   si1->writeMpsNative("test.out", NULL, NULL);
2499   OSIUNITTEST_ASSERT_ERROR(si2->readMps("test.out", "") == 0, return, *si1, "testWriteMps: read LP written by writeMpsNative");
2500   if (solved) {
2501     OSIUNITTEST_CATCH_ERROR(si2->initialSolve(), return, *si1, "testWriteMps: solving LP written by writeMpsNative");
2502     OSIUNITTEST_ASSERT_ERROR(eq(soln, si2->getObjValue()), return, *si1, "testWriteMps: solving LP written by writeMpsNative");
2503   }
2504   /*
2505   Repeat with writeMps.
2506 */
2507   si1->writeMps("test2", "out");
2508   OSIUNITTEST_ASSERT_ERROR(si3->readMps("test2.out", "") == 0, return, *si1, "testWriteMps: read LP written by writeMps");
2509   if (solved) {
2510     OSIUNITTEST_CATCH_ERROR(si3->initialSolve(), return, *si1, "testWriteMps: solving LP written by writeMps");
2511     OSIUNITTEST_ASSERT_ERROR(eq(soln, si3->getObjValue()), return, *si1, "testWriteMps: solving LP written by writeMps");
2512   }
2513   /*
2514   Clean up.
2515 */
2516   delete si1;
2517   delete si2;
2518   delete si3;
2519 }
2520 
2521 /*
2522   Test writeLp and writeLpNative. Same sequence as for testWriteMps, above.
2523   Implicitly assumes readLp has been tested, but in fact that's not the case at
2524   present (lh, 070726).
2525 */
testWriteLp(const OsiSolverInterface * emptySi,std::string fn)2526 void testWriteLp(const OsiSolverInterface *emptySi, std::string fn)
2527 
2528 {
2529   testingMessage("Testing writeLp and writeLpNative.\n");
2530 
2531   CoinRelFltEq eq(1.0e-8);
2532 
2533   OsiSolverInterface *si1 = emptySi->clone();
2534   OsiSolverInterface *si2 = emptySi->clone();
2535   OsiSolverInterface *si3 = emptySi->clone();
2536 
2537   OSIUNITTEST_ASSERT_ERROR(si1->readMps(fn.c_str(), "mps") == 0, return, *si1, "testWriteLp: read MPS");
2538   bool solved = true;
2539   OSIUNITTEST_CATCH_SEVERITY_EXPECTED(si1->initialSolve(), solved = false, *si1, "testWriteLp: solving LP",
2540     TestOutcome::ERROR, e.className() == "OsiVolSolverInterface" || e.className() == "OsiTestSolverInterface");
2541   double soln = si1->getObjValue();
2542 
2543   si1->writeLpNative("test.lp", NULL, NULL, 1.0e-9, 10, 8);
2544 
2545   OSIUNITTEST_ASSERT_ERROR(si2->readLp("test.lp") == 0, return, *si1, "testWriteLp: read LP written by writeLpNative");
2546   if (solved) {
2547     OSIUNITTEST_CATCH_ERROR(si2->initialSolve(), return, *si1, "testWriteLp: solving LP written by writeLpNative");
2548     OSIUNITTEST_ASSERT_ERROR(eq(soln, si2->getObjValue()), return, *si1, "testWriteLp: solving LP written by writeLpNative");
2549   }
2550 
2551   si1->writeLp("test2");
2552   OSIUNITTEST_ASSERT_ERROR(si3->readLp("test2.lp") == 0, return, *si1, "testWriteLp: read LP written by writeLp");
2553   if (solved) {
2554     OSIUNITTEST_CATCH_ERROR(si3->initialSolve(), return, *si1, "testWriteLp: solving LP written by writeLp");
2555     OSIUNITTEST_ASSERT_ERROR(eq(soln, si3->getObjValue()), return, *si1, "testWriteLp: solving LP written by writeLp");
2556   }
2557 
2558   delete si1;
2559   delete si2;
2560   delete si3;
2561 }
2562 
2563 /*
2564   Test load and assign problem. The first batch of tests loads up eight
2565   solvers, using each variant of loadProblem and assignProblem, runs
2566   initialSolve for all, then checks all values for all variants.
2567 */
2568 
testLoadAndAssignProblem(const OsiSolverInterface * emptySi,const OsiSolverInterface * exmip1Si)2569 void testLoadAndAssignProblem(const OsiSolverInterface *emptySi,
2570   const OsiSolverInterface *exmip1Si)
2571 
2572 {
2573   CoinRelFltEq eq(1.0e-8);
2574   std::string solverName;
2575   if (!emptySi->getStrParam(OsiSolverName, solverName))
2576     solverName = "unknown";
2577   /*
2578   Test each variant of loadProblem and assignProblem. Clone a whack of solvers
2579   and use one for each variant. Then run initialSolve() on each solver. Then
2580   check that all values are as they should be.
2581 
2582   Note that we are not testing the variants that supply the matrix as a set
2583   of vectors (row/col starts, col/row indices, coefficients). To be really
2584   thorough, we should do another eight ...
2585 */
2586   {
2587     testingMessage("Testing loadProblem and assignProblem methods.\n");
2588     OsiSolverInterface *base = exmip1Si->clone();
2589     OsiSolverInterface *si1 = emptySi->clone();
2590     OsiSolverInterface *si2 = emptySi->clone();
2591     OsiSolverInterface *si3 = emptySi->clone();
2592     OsiSolverInterface *si4 = emptySi->clone();
2593     OsiSolverInterface *si5 = emptySi->clone();
2594     OsiSolverInterface *si6 = emptySi->clone();
2595     OsiSolverInterface *si7 = emptySi->clone();
2596     OsiSolverInterface *si8 = emptySi->clone();
2597 
2598     si1->loadProblem(*base->getMatrixByCol(),
2599       base->getColLower(), base->getColUpper(),
2600       base->getObjCoefficients(),
2601       base->getRowSense(), base->getRightHandSide(),
2602       base->getRowRange());
2603     si2->loadProblem(*base->getMatrixByRow(),
2604       base->getColLower(), base->getColUpper(),
2605       base->getObjCoefficients(),
2606       base->getRowSense(), base->getRightHandSide(),
2607       base->getRowRange());
2608     si3->loadProblem(*base->getMatrixByCol(),
2609       base->getColLower(), base->getColUpper(),
2610       base->getObjCoefficients(),
2611       base->getRowLower(), base->getRowUpper());
2612     si4->loadProblem(*base->getMatrixByCol(),
2613       base->getColLower(), base->getColUpper(),
2614       base->getObjCoefficients(),
2615       base->getRowLower(), base->getRowUpper());
2616     {
2617       double objOffset;
2618       base->getDblParam(OsiObjOffset, objOffset);
2619       si1->setDblParam(OsiObjOffset, objOffset);
2620       si2->setDblParam(OsiObjOffset, objOffset);
2621       si3->setDblParam(OsiObjOffset, objOffset);
2622       si4->setDblParam(OsiObjOffset, objOffset);
2623       si5->setDblParam(OsiObjOffset, objOffset);
2624       si6->setDblParam(OsiObjOffset, objOffset);
2625       si7->setDblParam(OsiObjOffset, objOffset);
2626       si8->setDblParam(OsiObjOffset, objOffset);
2627     }
2628     /*
2629   Assign methods should set their parameters to NULL, so check for that.
2630 */
2631     CoinPackedMatrix *pm = new CoinPackedMatrix(*base->getMatrixByCol());
2632     double *clb = new double[base->getNumCols()];
2633     std::copy(base->getColLower(),
2634       base->getColLower() + base->getNumCols(), clb);
2635     double *cub = new double[base->getNumCols()];
2636     std::copy(base->getColUpper(),
2637       base->getColUpper() + base->getNumCols(), cub);
2638     double *objc = new double[base->getNumCols()];
2639     std::copy(base->getObjCoefficients(),
2640       base->getObjCoefficients() + base->getNumCols(), objc);
2641     double *rlb = new double[base->getNumRows()];
2642     std::copy(base->getRowLower(),
2643       base->getRowLower() + base->getNumRows(), rlb);
2644     double *rub = new double[base->getNumRows()];
2645     std::copy(base->getRowUpper(),
2646       base->getRowUpper() + base->getNumRows(), rub);
2647     si5->assignProblem(pm, clb, cub, objc, rlb, rub);
2648     OSIUNITTEST_ASSERT_ERROR(pm == NULL, return, solverName, "testLoadAndAssignProblem: si5 assignProblem should set parameters to NULL");
2649     OSIUNITTEST_ASSERT_ERROR(clb == NULL, return, solverName, "testLoadAndAssignProblem: si5 assignProblem should set parameters to NULL");
2650     OSIUNITTEST_ASSERT_ERROR(cub == NULL, return, solverName, "testLoadAndAssignProblem: si5 assignProblem should set parameters to NULL");
2651     OSIUNITTEST_ASSERT_ERROR(objc == NULL, return, solverName, "testLoadAndAssignProblem: si5 assignProblem should set parameters to NULL");
2652     OSIUNITTEST_ASSERT_ERROR(rlb == NULL, return, solverName, "testLoadAndAssignProblem: si5 assignProblem should set parameters to NULL");
2653     OSIUNITTEST_ASSERT_ERROR(rub == NULL, return, solverName, "testLoadAndAssignProblem: si5 assignProblem should set parameters to NULL");
2654 
2655     pm = new CoinPackedMatrix(*base->getMatrixByRow());
2656     clb = new double[base->getNumCols()];
2657     std::copy(base->getColLower(),
2658       base->getColLower() + base->getNumCols(), clb);
2659     cub = new double[base->getNumCols()];
2660     std::copy(base->getColUpper(),
2661       base->getColUpper() + base->getNumCols(), cub);
2662     objc = new double[base->getNumCols()];
2663     std::copy(base->getObjCoefficients(),
2664       base->getObjCoefficients() + base->getNumCols(), objc);
2665     rlb = new double[base->getNumRows()];
2666     std::copy(base->getRowLower(),
2667       base->getRowLower() + base->getNumRows(), rlb);
2668     rub = new double[base->getNumRows()];
2669     std::copy(base->getRowUpper(),
2670       base->getRowUpper() + base->getNumRows(), rub);
2671     si6->assignProblem(pm, clb, cub, objc, rlb, rub);
2672     OSIUNITTEST_ASSERT_ERROR(pm == NULL, return, solverName, "testLoadAndAssignProblem: si6 assignProblem should set parameters to NULL");
2673     OSIUNITTEST_ASSERT_ERROR(clb == NULL, return, solverName, "testLoadAndAssignProblem: si6 assignProblem should set parameters to NULL");
2674     OSIUNITTEST_ASSERT_ERROR(cub == NULL, return, solverName, "testLoadAndAssignProblem: si6 assignProblem should set parameters to NULL");
2675     OSIUNITTEST_ASSERT_ERROR(objc == NULL, return, solverName, "testLoadAndAssignProblem: si6 assignProblem should set parameters to NULL");
2676     OSIUNITTEST_ASSERT_ERROR(rlb == NULL, return, solverName, "testLoadAndAssignProblem: si6 assignProblem should set parameters to NULL");
2677     OSIUNITTEST_ASSERT_ERROR(rub == NULL, return, solverName, "testLoadAndAssignProblem: si6 assignProblem should set parameters to NULL");
2678 
2679     pm = new CoinPackedMatrix(*base->getMatrixByCol());
2680     clb = new double[base->getNumCols()];
2681     std::copy(base->getColLower(),
2682       base->getColLower() + base->getNumCols(), clb);
2683     cub = new double[base->getNumCols()];
2684     std::copy(base->getColUpper(),
2685       base->getColUpper() + base->getNumCols(), cub);
2686     objc = new double[base->getNumCols()];
2687     std::copy(base->getObjCoefficients(),
2688       base->getObjCoefficients() + base->getNumCols(), objc);
2689     char *rsen = new char[base->getNumRows()];
2690     std::copy(base->getRowSense(),
2691       base->getRowSense() + base->getNumRows(), rsen);
2692     double *rhs = new double[base->getNumRows()];
2693     std::copy(base->getRightHandSide(),
2694       base->getRightHandSide() + base->getNumRows(), rhs);
2695     double *rng = new double[base->getNumRows()];
2696     std::copy(base->getRowRange(),
2697       base->getRowRange() + base->getNumRows(), rng);
2698     si7->assignProblem(pm, clb, cub, objc, rsen, rhs, rng);
2699     OSIUNITTEST_ASSERT_ERROR(pm == NULL, return, solverName, "testLoadAndAssignProblem: si7 assignProblem should set parameters to NULL");
2700     OSIUNITTEST_ASSERT_ERROR(clb == NULL, return, solverName, "testLoadAndAssignProblem: si7 assignProblem should set parameters to NULL");
2701     OSIUNITTEST_ASSERT_ERROR(cub == NULL, return, solverName, "testLoadAndAssignProblem: si7 assignProblem should set parameters to NULL");
2702     OSIUNITTEST_ASSERT_ERROR(objc == NULL, return, solverName, "testLoadAndAssignProblem: si7 assignProblem should set parameters to NULL");
2703     OSIUNITTEST_ASSERT_ERROR(rsen == NULL, return, solverName, "testLoadAndAssignProblem: si7 assignProblem should set parameters to NULL");
2704     OSIUNITTEST_ASSERT_ERROR(rhs == NULL, return, solverName, "testLoadAndAssignProblem: si7 assignProblem should set parameters to NULL");
2705     OSIUNITTEST_ASSERT_ERROR(rng == NULL, return, solverName, "testLoadAndAssignProblem: si7 assignProblem should set parameters to NULL");
2706 
2707     pm = new CoinPackedMatrix(*base->getMatrixByCol());
2708     clb = new double[base->getNumCols()];
2709     std::copy(base->getColLower(),
2710       base->getColLower() + base->getNumCols(), clb);
2711     cub = new double[base->getNumCols()];
2712     std::copy(base->getColUpper(),
2713       base->getColUpper() + base->getNumCols(), cub);
2714     objc = new double[base->getNumCols()];
2715     std::copy(base->getObjCoefficients(),
2716       base->getObjCoefficients() + base->getNumCols(), objc);
2717     rsen = new char[base->getNumRows()];
2718     std::copy(base->getRowSense(),
2719       base->getRowSense() + base->getNumRows(), rsen);
2720     rhs = new double[base->getNumRows()];
2721     std::copy(base->getRightHandSide(),
2722       base->getRightHandSide() + base->getNumRows(), rhs);
2723     rng = new double[base->getNumRows()];
2724     std::copy(base->getRowRange(),
2725       base->getRowRange() + base->getNumRows(), rng);
2726     si8->assignProblem(pm, clb, cub, objc, rsen, rhs, rng);
2727     OSIUNITTEST_ASSERT_ERROR(pm == NULL, return, solverName, "testLoadAndAssignProblem: si8 assignProblem should set parameters to NULL");
2728     OSIUNITTEST_ASSERT_ERROR(clb == NULL, return, solverName, "testLoadAndAssignProblem: si8 assignProblem should set parameters to NULL");
2729     OSIUNITTEST_ASSERT_ERROR(cub == NULL, return, solverName, "testLoadAndAssignProblem: si8 assignProblem should set parameters to NULL");
2730     OSIUNITTEST_ASSERT_ERROR(objc == NULL, return, solverName, "testLoadAndAssignProblem: si8 assignProblem should set parameters to NULL");
2731     OSIUNITTEST_ASSERT_ERROR(rsen == NULL, return, solverName, "testLoadAndAssignProblem: si8 assignProblem should set parameters to NULL");
2732     OSIUNITTEST_ASSERT_ERROR(rhs == NULL, return, solverName, "testLoadAndAssignProblem: si8 assignProblem should set parameters to NULL");
2733     OSIUNITTEST_ASSERT_ERROR(rng == NULL, return, solverName, "testLoadAndAssignProblem: si8 assignProblem should set parameters to NULL");
2734 
2735     // Create an indices vector
2736     CoinPackedVector basePv, pv;
2737     OSIUNITTEST_ASSERT_ERROR(base->getNumCols() < 10, return, solverName, "testLoadAndAssignProblem");
2738     OSIUNITTEST_ASSERT_ERROR(base->getNumRows() < 10, return, solverName, "testLoadAndAssignProblem");
2739     int indices[10];
2740     int i;
2741     for (i = 0; i < 10; i++)
2742       indices[i] = i;
2743 
2744     // Test solve methods.
2745     // Vol solver interface is expected to throw
2746     // an error if the data has a ranged row.
2747     // Prepare test that there is non-zero range
2748     basePv.setFull(base->getNumRows(), base->getRowRange());
2749     pv.setConstant(base->getNumRows(), indices, 0.0);
2750 
2751     OSIUNITTEST_CATCH_SEVERITY_EXPECTED(base->initialSolve(), return, *base, "testLoadAndAssignProblem: base initialSolve",
2752       TestOutcome::ERROR, solverName == "vol" && !basePv.isEquivalent(pv));
2753     OSIUNITTEST_CATCH_SEVERITY_EXPECTED(si1->initialSolve(), return, *base, "testLoadAndAssignProblem: si1 initialSolve",
2754       TestOutcome::ERROR, solverName == "vol" && !basePv.isEquivalent(pv));
2755     OSIUNITTEST_CATCH_SEVERITY_EXPECTED(si2->initialSolve(), return, *base, "testLoadAndAssignProblem: si2 initialSolve",
2756       TestOutcome::ERROR, solverName == "vol" && !basePv.isEquivalent(pv));
2757     OSIUNITTEST_CATCH_SEVERITY_EXPECTED(si3->initialSolve(), return, *base, "testLoadAndAssignProblem: si3 initialSolve",
2758       TestOutcome::ERROR, solverName == "vol" && !basePv.isEquivalent(pv));
2759     OSIUNITTEST_CATCH_SEVERITY_EXPECTED(si4->initialSolve(), return, *base, "testLoadAndAssignProblem: si4 initialSolve",
2760       TestOutcome::ERROR, solverName == "vol" && !basePv.isEquivalent(pv));
2761     OSIUNITTEST_CATCH_SEVERITY_EXPECTED(si5->initialSolve(), return, *base, "testLoadAndAssignProblem: si5 initialSolve",
2762       TestOutcome::ERROR, solverName == "vol" && !basePv.isEquivalent(pv));
2763     OSIUNITTEST_CATCH_SEVERITY_EXPECTED(si6->initialSolve(), return, *base, "testLoadAndAssignProblem: si6 initialSolve",
2764       TestOutcome::ERROR, solverName == "vol" && !basePv.isEquivalent(pv));
2765     OSIUNITTEST_CATCH_SEVERITY_EXPECTED(si7->initialSolve(), return, *base, "testLoadAndAssignProblem: si7 initialSolve",
2766       TestOutcome::ERROR, solverName == "vol" && !basePv.isEquivalent(pv));
2767     OSIUNITTEST_CATCH_SEVERITY_EXPECTED(si8->initialSolve(), return, *base, "testLoadAndAssignProblem: si8 initialSolve",
2768       TestOutcome::ERROR, solverName == "vol" && !basePv.isEquivalent(pv));
2769 
2770     // Test collower
2771     basePv.setVector(base->getNumCols(), indices, base->getColLower());
2772     pv.setVector(si1->getNumCols(), indices, si1->getColLower());
2773     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si1 column lower bounds");
2774     pv.setVector(si2->getNumCols(), indices, si2->getColLower());
2775     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si2 column lower bounds");
2776     pv.setVector(si3->getNumCols(), indices, si3->getColLower());
2777     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si3 column lower bounds");
2778     pv.setVector(si4->getNumCols(), indices, si4->getColLower());
2779     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si4 column lower bounds");
2780     pv.setVector(si5->getNumCols(), indices, si5->getColLower());
2781     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si5 column lower bounds");
2782     pv.setVector(si6->getNumCols(), indices, si6->getColLower());
2783     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si6 column lower bounds");
2784     pv.setVector(si7->getNumCols(), indices, si7->getColLower());
2785     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si7 column lower bounds");
2786     pv.setVector(si8->getNumCols(), indices, si8->getColLower());
2787     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si8 column lower bounds");
2788 
2789     // Test colupper
2790     basePv.setVector(base->getNumCols(), indices, base->getColUpper());
2791     pv.setVector(si1->getNumCols(), indices, si1->getColUpper());
2792     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si1 column upper bounds");
2793     pv.setVector(si2->getNumCols(), indices, si2->getColUpper());
2794     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si2 column upper bounds");
2795     pv.setVector(si3->getNumCols(), indices, si3->getColUpper());
2796     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si3 column upper bounds");
2797     pv.setVector(si4->getNumCols(), indices, si4->getColUpper());
2798     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si4 column upper bounds");
2799     pv.setVector(si5->getNumCols(), indices, si5->getColUpper());
2800     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si5 column upper bounds");
2801     pv.setVector(si6->getNumCols(), indices, si6->getColUpper());
2802     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si6 column upper bounds");
2803     pv.setVector(si7->getNumCols(), indices, si7->getColUpper());
2804     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si7 column upper bounds");
2805     pv.setVector(si8->getNumCols(), indices, si8->getColUpper());
2806     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si8 column upper bounds");
2807 
2808     // Test getObjCoefficients
2809     basePv.setVector(base->getNumCols(), indices, base->getObjCoefficients());
2810     pv.setVector(si1->getNumCols(), indices, si1->getObjCoefficients());
2811     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si1 objective coefficients");
2812     pv.setVector(si2->getNumCols(), indices, si2->getObjCoefficients());
2813     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si2 objective coefficients");
2814     pv.setVector(si3->getNumCols(), indices, si3->getObjCoefficients());
2815     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si3 objective coefficients");
2816     pv.setVector(si4->getNumCols(), indices, si4->getObjCoefficients());
2817     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si4 objective coefficients");
2818     pv.setVector(si5->getNumCols(), indices, si5->getObjCoefficients());
2819     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si5 objective coefficients");
2820     pv.setVector(si6->getNumCols(), indices, si6->getObjCoefficients());
2821     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si6 objective coefficients");
2822     pv.setVector(si7->getNumCols(), indices, si7->getObjCoefficients());
2823     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si7 objective coefficients");
2824     pv.setVector(si8->getNumCols(), indices, si8->getObjCoefficients());
2825     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si8 objective coefficients");
2826 
2827     // Test rowrhs
2828     basePv.setFull(base->getNumRows(), base->getRightHandSide());
2829     pv.setFull(si1->getNumRows(), si1->getRightHandSide());
2830     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si1 right hand side");
2831     pv.setFull(si2->getNumRows(), si2->getRightHandSide());
2832     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si2 right hand side");
2833     pv.setFull(si3->getNumRows(), si3->getRightHandSide());
2834     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si3 right hand side");
2835     pv.setFull(si4->getNumRows(), si4->getRightHandSide());
2836     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si4 right hand side");
2837     pv.setFull(si5->getNumRows(), si5->getRightHandSide());
2838     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si5 right hand side");
2839     pv.setFull(si6->getNumRows(), si6->getRightHandSide());
2840     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si6 right hand side");
2841     pv.setFull(si7->getNumRows(), si7->getRightHandSide());
2842     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si7 right hand side");
2843     pv.setFull(si8->getNumRows(), si8->getRightHandSide());
2844     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si8 right hand side");
2845 
2846     // Test rowrange
2847     basePv.setFull(base->getNumRows(), base->getRowRange());
2848     pv.setFull(si1->getNumRows(), si1->getRowRange());
2849     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si1 row range");
2850     pv.setFull(si2->getNumRows(), si2->getRowRange());
2851     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si2 row range");
2852     pv.setFull(si3->getNumRows(), si3->getRowRange());
2853     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si3 row range");
2854     pv.setFull(si4->getNumRows(), si4->getRowRange());
2855     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si4 row range");
2856     pv.setFull(si5->getNumRows(), si5->getRowRange());
2857     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si5 row range");
2858     pv.setFull(si6->getNumRows(), si6->getRowRange());
2859     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si6 row range");
2860     pv.setFull(si7->getNumRows(), si7->getRowRange());
2861     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si7 row range");
2862     pv.setFull(si8->getNumRows(), si8->getRowRange());
2863     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si8 row range");
2864 
2865     // Test row sense
2866     {
2867       const char *cb = base->getRowSense();
2868       const char *c1 = si1->getRowSense();
2869       const char *c2 = si2->getRowSense();
2870       const char *c3 = si3->getRowSense();
2871       const char *c4 = si4->getRowSense();
2872       const char *c5 = si5->getRowSense();
2873       const char *c6 = si6->getRowSense();
2874       const char *c7 = si7->getRowSense();
2875       const char *c8 = si8->getRowSense();
2876       int nr = base->getNumRows();
2877       bool rowsense_ok1 = true;
2878       bool rowsense_ok2 = true;
2879       bool rowsense_ok3 = true;
2880       bool rowsense_ok4 = true;
2881       bool rowsense_ok5 = true;
2882       bool rowsense_ok6 = true;
2883       bool rowsense_ok7 = true;
2884       bool rowsense_ok8 = true;
2885       for (i = 0; i < nr; i++) {
2886         rowsense_ok1 &= cb[i] == c1[i];
2887         rowsense_ok2 &= cb[i] == c2[i];
2888         rowsense_ok3 &= cb[i] == c3[i];
2889         rowsense_ok4 &= cb[i] == c4[i];
2890         rowsense_ok5 &= cb[i] == c5[i];
2891         rowsense_ok6 &= cb[i] == c6[i];
2892         rowsense_ok7 &= cb[i] == c7[i];
2893         rowsense_ok8 &= cb[i] == c8[i];
2894       }
2895       OSIUNITTEST_ASSERT_ERROR(rowsense_ok1, return, solverName, "testLoadAndAssignProblem: si1 row sense");
2896       OSIUNITTEST_ASSERT_ERROR(rowsense_ok2, return, solverName, "testLoadAndAssignProblem: si2 row sense");
2897       OSIUNITTEST_ASSERT_ERROR(rowsense_ok3, return, solverName, "testLoadAndAssignProblem: si3 row sense");
2898       OSIUNITTEST_ASSERT_ERROR(rowsense_ok4, return, solverName, "testLoadAndAssignProblem: si4 row sense");
2899       OSIUNITTEST_ASSERT_ERROR(rowsense_ok5, return, solverName, "testLoadAndAssignProblem: si5 row sense");
2900       OSIUNITTEST_ASSERT_ERROR(rowsense_ok6, return, solverName, "testLoadAndAssignProblem: si6 row sense");
2901       OSIUNITTEST_ASSERT_ERROR(rowsense_ok7, return, solverName, "testLoadAndAssignProblem: si7 row sense");
2902       OSIUNITTEST_ASSERT_ERROR(rowsense_ok8, return, solverName, "testLoadAndAssignProblem: si8 row sense");
2903     }
2904 
2905     // Test rowlower
2906     basePv.setVector(base->getNumRows(), indices, base->getRowLower());
2907     pv.setVector(si1->getNumRows(), indices, si1->getRowLower());
2908     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si1 row lower bounds");
2909     pv.setVector(si2->getNumRows(), indices, si2->getRowLower());
2910     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si2 row lower bounds");
2911     pv.setVector(si3->getNumRows(), indices, si3->getRowLower());
2912     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si3 row lower bounds");
2913     pv.setVector(si4->getNumRows(), indices, si4->getRowLower());
2914     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si4 row lower bounds");
2915     pv.setVector(si5->getNumRows(), indices, si5->getRowLower());
2916     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si5 row lower bounds");
2917     pv.setVector(si6->getNumRows(), indices, si6->getRowLower());
2918     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si6 row lower bounds");
2919     pv.setVector(si7->getNumRows(), indices, si7->getRowLower());
2920     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si7 row lower bounds");
2921     pv.setVector(si8->getNumRows(), indices, si8->getRowLower());
2922     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si8 row lower bounds");
2923 
2924     // Test rowupper
2925     basePv.setVector(base->getNumRows(), indices, base->getRowUpper());
2926     pv.setVector(si1->getNumRows(), indices, si1->getRowUpper());
2927     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si1 row upper bounds");
2928     pv.setVector(si2->getNumRows(), indices, si2->getRowUpper());
2929     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si2 row upper bounds");
2930     pv.setVector(si3->getNumRows(), indices, si3->getRowUpper());
2931     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si3 row upper bounds");
2932     pv.setVector(si4->getNumRows(), indices, si4->getRowUpper());
2933     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si4 row upper bounds");
2934     pv.setVector(si5->getNumRows(), indices, si5->getRowUpper());
2935     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si5 row upper bounds");
2936     pv.setVector(si6->getNumRows(), indices, si6->getRowUpper());
2937     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si6 row upper bounds");
2938     pv.setVector(si7->getNumRows(), indices, si7->getRowUpper());
2939     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si7 row upper bounds");
2940     pv.setVector(si8->getNumRows(), indices, si8->getRowUpper());
2941     OSIUNITTEST_ASSERT_ERROR(basePv.isEquivalent(pv), return, solverName, "testLoadAndAssignProblem: si8 row upper bounds");
2942 
2943     // Test Constraint Matrix
2944     OSIUNITTEST_ASSERT_ERROR(base->getMatrixByCol()->isEquivalent(*si1->getMatrixByCol()), return, solverName, "testLoadAndAssignProblem: si1 constraint matrix");
2945     OSIUNITTEST_ASSERT_ERROR(base->getMatrixByRow()->isEquivalent(*si1->getMatrixByRow()), return, solverName, "testLoadAndAssignProblem: si1 constraint matrix");
2946     OSIUNITTEST_ASSERT_ERROR(base->getMatrixByCol()->isEquivalent(*si2->getMatrixByCol()), return, solverName, "testLoadAndAssignProblem: si2 constraint matrix");
2947     OSIUNITTEST_ASSERT_ERROR(base->getMatrixByRow()->isEquivalent(*si2->getMatrixByRow()), return, solverName, "testLoadAndAssignProblem: si2 constraint matrix");
2948     OSIUNITTEST_ASSERT_ERROR(base->getMatrixByCol()->isEquivalent(*si3->getMatrixByCol()), return, solverName, "testLoadAndAssignProblem: si3 constraint matrix");
2949     OSIUNITTEST_ASSERT_ERROR(base->getMatrixByRow()->isEquivalent(*si3->getMatrixByRow()), return, solverName, "testLoadAndAssignProblem: si3 constraint matrix");
2950     OSIUNITTEST_ASSERT_ERROR(base->getMatrixByCol()->isEquivalent(*si4->getMatrixByCol()), return, solverName, "testLoadAndAssignProblem: si4 constraint matrix");
2951     OSIUNITTEST_ASSERT_ERROR(base->getMatrixByRow()->isEquivalent(*si4->getMatrixByRow()), return, solverName, "testLoadAndAssignProblem: si4 constraint matrix");
2952     OSIUNITTEST_ASSERT_ERROR(base->getMatrixByCol()->isEquivalent(*si5->getMatrixByCol()), return, solverName, "testLoadAndAssignProblem: si5 constraint matrix");
2953     OSIUNITTEST_ASSERT_ERROR(base->getMatrixByRow()->isEquivalent(*si5->getMatrixByRow()), return, solverName, "testLoadAndAssignProblem: si5 constraint matrix");
2954     OSIUNITTEST_ASSERT_ERROR(base->getMatrixByCol()->isEquivalent(*si6->getMatrixByCol()), return, solverName, "testLoadAndAssignProblem: si6 constraint matrix");
2955     OSIUNITTEST_ASSERT_ERROR(base->getMatrixByRow()->isEquivalent(*si6->getMatrixByRow()), return, solverName, "testLoadAndAssignProblem: si6 constraint matrix");
2956     OSIUNITTEST_ASSERT_ERROR(base->getMatrixByCol()->isEquivalent(*si7->getMatrixByCol()), return, solverName, "testLoadAndAssignProblem: si7 constraint matrix");
2957     OSIUNITTEST_ASSERT_ERROR(base->getMatrixByRow()->isEquivalent(*si7->getMatrixByRow()), return, solverName, "testLoadAndAssignProblem: si7 constraint matrix");
2958     OSIUNITTEST_ASSERT_ERROR(base->getMatrixByCol()->isEquivalent(*si8->getMatrixByCol()), return, solverName, "testLoadAndAssignProblem: si8 constraint matrix");
2959     OSIUNITTEST_ASSERT_ERROR(base->getMatrixByRow()->isEquivalent(*si8->getMatrixByRow()), return, solverName, "testLoadAndAssignProblem: si8 constraint matrix");
2960 
2961     // Test Objective Value
2962     OSIUNITTEST_ASSERT_ERROR(eq(base->getObjValue(), si1->getObjValue()), return, solverName, "testLoadAndAssignProblem: si1 objective value");
2963     OSIUNITTEST_ASSERT_ERROR(eq(base->getObjValue(), si2->getObjValue()), return, solverName, "testLoadAndAssignProblem: si2 objective value");
2964     OSIUNITTEST_ASSERT_ERROR(eq(base->getObjValue(), si3->getObjValue()), return, solverName, "testLoadAndAssignProblem: si3 objective value");
2965     OSIUNITTEST_ASSERT_ERROR(eq(base->getObjValue(), si4->getObjValue()), return, solverName, "testLoadAndAssignProblem: si4 objective value");
2966     OSIUNITTEST_ASSERT_ERROR(eq(base->getObjValue(), si5->getObjValue()), return, solverName, "testLoadAndAssignProblem: si5 objective value");
2967     OSIUNITTEST_ASSERT_ERROR(eq(base->getObjValue(), si6->getObjValue()), return, solverName, "testLoadAndAssignProblem: si6 objective value");
2968     OSIUNITTEST_ASSERT_ERROR(eq(base->getObjValue(), si7->getObjValue()), return, solverName, "testLoadAndAssignProblem: si7 objective value");
2969     OSIUNITTEST_ASSERT_ERROR(eq(base->getObjValue(), si8->getObjValue()), return, solverName, "testLoadAndAssignProblem: si8 objective value");
2970 
2971     // Clean-up
2972     delete si8;
2973     delete si7;
2974     delete si6;
2975     delete si5;
2976     delete si4;
2977     delete si3;
2978     delete si2;
2979     delete si1;
2980     delete base;
2981   }
2982   /*
2983   The OSI interface spec says any of the parameters to loadProblem can default
2984   to null. Let's see if that works. Test the rowub, rowlb and sense, rhs,
2985   range variants. Arguably we should check all variants again, but let's
2986   hope that OSI implementors carry things over from one variant to another.
2987 
2988   For Gurobi, this does not work. Since Gurobi does not know about free rows
2989   (rowtype 'N'), OsiGrb translates free rows into 'L' (lower-equal) rows
2990   with a -infty right-hand side.  This makes some of the tests below fail.
2991 
2992   Ok, gurobi has quirks. As do other solvers. But those quirks should be
2993   hidden from users of OsiGrb -- i.e., the translation should be done within
2994   OsiGrb, and should not be visible to the user (or this test). That's the
2995   point of OSI.  It's an implementation failure and should not be swept
2996   under the rug here.  -- lh, 100826 --
2997 
2998   OsiGrb does quite some attempts to hide this translation from the user,
2999   need to check further what could be done. -- sv, 110306 --
3000   ps: it's just coincidence that I actually see your messages here...
3001 */
3002 
3003   if (solverName != "gurobi") {
3004     int i;
3005 
3006     OsiSolverInterface *si1 = emptySi->clone();
3007     OsiSolverInterface *si2 = emptySi->clone();
3008 
3009     si1->loadProblem(*exmip1Si->getMatrixByCol(), NULL, NULL, NULL, NULL, NULL);
3010     si2->loadProblem(*exmip1Si->getMatrixByCol(), NULL, NULL, NULL, NULL, NULL, NULL);
3011 
3012     // Test column settings
3013     OSIUNITTEST_ASSERT_ERROR(si1->getNumCols() == exmip1Si->getNumCols(), return, solverName, "testLoadAndAssignProblem: si1 loadProblem with matrix only");
3014     bool collower_ok = true;
3015     bool colupper_ok = true;
3016     bool colobjcoef_ok = true;
3017     for (i = 0; i < si1->getNumCols(); i++) {
3018       collower_ok &= eq(si1->getColLower()[i], 0.0);
3019       colupper_ok &= eq(si1->getColUpper()[i], si1->getInfinity());
3020       colobjcoef_ok &= eq(si1->getObjCoefficients()[i], 0.0);
3021     }
3022     OSIUNITTEST_ASSERT_ERROR(collower_ok == true, return, solverName, "testLoadAndAssignProblem: si1 loadProblem with matrix only");
3023     OSIUNITTEST_ASSERT_ERROR(colupper_ok == true, return, solverName, "testLoadAndAssignProblem: si1 loadProblem with matrix only");
3024     OSIUNITTEST_ASSERT_ERROR(colobjcoef_ok == true, return, solverName, "testLoadAndAssignProblem: si1 loadProblem with matrix only");
3025 
3026     // Test row settings
3027     OSIUNITTEST_ASSERT_ERROR(si1->getNumRows() == exmip1Si->getNumRows(), return, solverName, "testLoadAndAssignProblem: si1 loadProblem with matrix only");
3028     const double *rh = si1->getRightHandSide();
3029     const double *rr = si1->getRowRange();
3030     const char *rs = si1->getRowSense();
3031     const double *rl = si1->getRowLower();
3032     const double *ru = si1->getRowUpper();
3033     bool rowrhs_ok = true;
3034     bool rowrange_ok = true;
3035     bool rowsense_ok = true;
3036     bool rowlower_ok = true;
3037     bool rowupper_ok = true;
3038     for (i = 0; i < si1->getNumRows(); i++) {
3039       rowrhs_ok &= eq(rh[i], 0.0);
3040       rowrange_ok &= eq(rr[i], 0.0);
3041       rowsense_ok &= 'N' == rs[i];
3042       rowlower_ok &= eq(rl[i], -si1->getInfinity());
3043       rowupper_ok &= eq(ru[i], si1->getInfinity());
3044     }
3045     OSIUNITTEST_ASSERT_ERROR(rowrhs_ok == true, return, solverName, "testLoadAndAssignProblem: si1 loadProblem with matrix only");
3046     OSIUNITTEST_ASSERT_ERROR(rowrange_ok == true, return, solverName, "testLoadAndAssignProblem: si1 loadProblem with matrix only");
3047     OSIUNITTEST_ASSERT_ERROR(rowsense_ok == true, return, solverName, "testLoadAndAssignProblem: si1 loadProblem with matrix only");
3048     OSIUNITTEST_ASSERT_ERROR(rowlower_ok == true, return, solverName, "testLoadAndAssignProblem: si1 loadProblem with matrix only");
3049     OSIUNITTEST_ASSERT_ERROR(rowupper_ok == true, return, solverName, "testLoadAndAssignProblem: si1 loadProblem with matrix only");
3050 
3051     // And repeat for si2
3052     OSIUNITTEST_ASSERT_ERROR(si2->getNumCols() == exmip1Si->getNumCols(), return, solverName, "testLoadAndAssignProblem: si2 loadProblem with matrix only");
3053     collower_ok = true;
3054     colupper_ok = true;
3055     colobjcoef_ok = true;
3056     for (i = 0; i < si2->getNumCols(); i++) {
3057       collower_ok &= eq(si2->getColLower()[i], 0.0);
3058       colupper_ok &= eq(si2->getColUpper()[i], si2->getInfinity());
3059       colobjcoef_ok &= eq(si2->getObjCoefficients()[i], 0.0);
3060     }
3061     OSIUNITTEST_ASSERT_ERROR(collower_ok == true, return, solverName, "testLoadAndAssignProblem: si2 loadProblem with matrix only");
3062     OSIUNITTEST_ASSERT_ERROR(colupper_ok == true, return, solverName, "testLoadAndAssignProblem: si2 loadProblem with matrix only");
3063     OSIUNITTEST_ASSERT_ERROR(colobjcoef_ok == true, return, solverName, "testLoadAndAssignProblem: si2 loadProblem with matrix only");
3064     //
3065     OSIUNITTEST_ASSERT_ERROR(si2->getNumRows() == exmip1Si->getNumRows(), return, solverName, "testLoadAndAssignProblem: si2 loadProblem with matrix only");
3066     rh = si2->getRightHandSide();
3067     rr = si2->getRowRange();
3068     rs = si2->getRowSense();
3069     rl = si2->getRowLower();
3070     ru = si2->getRowUpper();
3071     rowrhs_ok = true;
3072     rowrange_ok = true;
3073     rowsense_ok = true;
3074     rowlower_ok = true;
3075     rowupper_ok = true;
3076     for (i = 0; i < si2->getNumRows(); i++) {
3077       rowrhs_ok &= eq(rh[i], 0.0);
3078       rowrange_ok &= eq(rr[i], 0.0);
3079       rowsense_ok &= 'G' == rs[i];
3080       rowlower_ok &= eq(rl[i], 0.0);
3081       rowupper_ok &= eq(ru[i], si2->getInfinity());
3082     }
3083     OSIUNITTEST_ASSERT_ERROR(rowrhs_ok == true, return, solverName, "testLoadAndAssignProblem: si2 loadProblem with matrix only");
3084     OSIUNITTEST_ASSERT_ERROR(rowrange_ok == true, return, solverName, "testLoadAndAssignProblem: si2 loadProblem with matrix only");
3085     OSIUNITTEST_ASSERT_ERROR(rowsense_ok == true, return, solverName, "testLoadAndAssignProblem: si2 loadProblem with matrix only");
3086     OSIUNITTEST_ASSERT_ERROR(rowlower_ok == true, return, solverName, "testLoadAndAssignProblem: si2 loadProblem with matrix only");
3087     OSIUNITTEST_ASSERT_ERROR(rowupper_ok == true, return, solverName, "testLoadAndAssignProblem: si2 loadProblem with matrix only");
3088 
3089     delete si1;
3090     delete si2;
3091   } else {
3092     failureMessage(solverName, "OsiGrb exposes inability to handle 'N' constraints (expected).");
3093     OSIUNITTEST_ADD_OUTCOME(solverName, "testLoadAndAssignProblem", "ability to handle 'N' constraints", TestOutcome::ERROR, true);
3094   }
3095   /*
3096   Load problem with row rhs, sense and range, but leave column bounds and
3097   objective at defaults. A belt-and-suspenders kind of test. Arguably we should
3098   have the symmetric case, with column bounds valid and row values at default.
3099 */
3100   {
3101     int i;
3102 
3103     OsiSolverInterface *si = emptySi->clone();
3104 
3105     si->loadProblem(*exmip1Si->getMatrixByRow(),
3106       NULL, NULL, NULL,
3107       exmip1Si->getRowSense(),
3108       exmip1Si->getRightHandSide(),
3109       exmip1Si->getRowRange());
3110     // Test column settings
3111     OSIUNITTEST_ASSERT_ERROR(si->getNumCols() == exmip1Si->getNumCols(), return, solverName, "testLoadAndAssignProblem: loadProblem with matrix and row bounds only");
3112     bool collower_ok = true;
3113     bool colupper_ok = true;
3114     bool colobjcoef_ok = true;
3115     for (i = 0; i < si->getNumCols(); i++) {
3116       collower_ok &= eq(si->getColLower()[i], 0.0);
3117       colupper_ok &= eq(si->getColUpper()[i], si->getInfinity());
3118       colobjcoef_ok &= eq(si->getObjCoefficients()[i], 0.0);
3119     }
3120     OSIUNITTEST_ASSERT_ERROR(collower_ok == true, return, solverName, "testLoadAndAssignProblem: loadProblem with matrix and row bounds only");
3121     OSIUNITTEST_ASSERT_ERROR(colupper_ok == true, return, solverName, "testLoadAndAssignProblem: loadProblem with matrix and row bounds only");
3122     OSIUNITTEST_ASSERT_ERROR(colobjcoef_ok == true, return, solverName, "testLoadAndAssignProblem: loadProblem with matrix and row bounds only");
3123     // Test row settings
3124     OSIUNITTEST_ASSERT_ERROR(si->getNumRows() == exmip1Si->getNumRows(), return, solverName, "testLoadAndAssignProblem: loadProblem with matrix and row bounds only");
3125     bool rowrhs_ok = true;
3126     bool rowrange_ok = true;
3127     bool rowsense_ok = true;
3128     bool rowlower_ok = true;
3129     bool rowupper_ok = true;
3130     for (i = 0; i < si->getNumRows(); i++) {
3131       rowrhs_ok &= eq(si->getRightHandSide()[i], exmip1Si->getRightHandSide()[i]);
3132       rowrange_ok &= eq(si->getRowRange()[i], exmip1Si->getRowRange()[i]);
3133       rowsense_ok &= si->getRowSense()[i] == exmip1Si->getRowSense()[i];
3134 
3135       char s = si->getRowSense()[i];
3136       if (s == 'G') {
3137         rowlower_ok &= eq(si->getRowLower()[i], exmip1Si->getRightHandSide()[i]);
3138         rowupper_ok &= eq(si->getRowUpper()[i], si->getInfinity());
3139       } else if (s == 'L') {
3140         rowlower_ok &= eq(si->getRowLower()[i], -si->getInfinity());
3141         rowupper_ok &= eq(si->getRowUpper()[i], exmip1Si->getRightHandSide()[i]);
3142       } else if (s == 'E') {
3143         rowlower_ok &= eq(si->getRowLower()[i], si->getRowUpper()[i]);
3144         rowupper_ok &= eq(si->getRowUpper()[i], exmip1Si->getRightHandSide()[i]);
3145       } else if (s == 'N') {
3146         rowlower_ok &= eq(si->getRowLower()[i], -si->getInfinity());
3147         rowupper_ok &= eq(si->getRowUpper()[i], si->getInfinity());
3148       } else if (s == 'R') {
3149         rowlower_ok &= eq(si->getRowLower()[i], exmip1Si->getRightHandSide()[i] - exmip1Si->getRowRange()[i]);
3150         rowupper_ok &= eq(si->getRowUpper()[i], exmip1Si->getRightHandSide()[i]);
3151       }
3152     }
3153     OSIUNITTEST_ASSERT_ERROR(rowrhs_ok == true, return, solverName, "testLoadAndAssignProblem: loadProblem with matrix and row bounds only");
3154     OSIUNITTEST_ASSERT_ERROR(rowrange_ok == true, return, solverName, "testLoadAndAssignProblem: loadProblem with matrix and row bounds only");
3155     OSIUNITTEST_ASSERT_ERROR(rowsense_ok == true, return, solverName, "testLoadAndAssignProblem: loadProblem with matrix and row bounds only");
3156     OSIUNITTEST_ASSERT_ERROR(rowlower_ok == true, return, solverName, "testLoadAndAssignProblem: loadProblem with matrix and row bounds only");
3157     OSIUNITTEST_ASSERT_ERROR(rowupper_ok == true, return, solverName, "testLoadAndAssignProblem: loadProblem with matrix and row bounds only");
3158 
3159     delete si;
3160   }
3161 
3162   return;
3163 }
3164 
3165 /*
3166   Test adding rows and columns to an empty constraint system.
3167 */
testAddToEmptySystem(const OsiSolverInterface * emptySi,bool volSolverInterface)3168 void testAddToEmptySystem(const OsiSolverInterface *emptySi,
3169   bool volSolverInterface)
3170 
3171 {
3172   CoinRelFltEq eq(1.0e-7);
3173 
3174   std::string solverName = "Unknown solver";
3175   emptySi->getStrParam(OsiSolverName, solverName);
3176   /*
3177   Add rows to an empty system. Begin by creating empty columns, then add some
3178   rows.
3179   */
3180   {
3181     OsiSolverInterface *si = emptySi->clone();
3182     int i;
3183 
3184     //Matrix
3185     int column[] = { 0, 1, 2 };
3186     double row1E[] = { 4.0, 7.0, 5.0 };
3187     double row2E[] = { 7.0, 4.0, 5.0 };
3188     CoinPackedVector row1(3, column, row1E);
3189     CoinPackedVector row2(3, column, row2E);
3190 
3191     double objective[] = { 5.0, 6.0, 5.5 };
3192 
3193     {
3194       // Add empty columns
3195       for (i = 0; i < 3; i++) {
3196         const CoinPackedVector reqdBySunCC;
3197         si->addCol(reqdBySunCC, 0.0, 10.0, objective[i]);
3198       }
3199 
3200       // Add rows
3201       si->addRow(row1, 2.0, 100.0);
3202       si->addRow(row2, 2.0, 100.0);
3203 
3204       // Vol can not solve problem of this form
3205       if (!volSolverInterface) {
3206         // solve
3207         si->initialSolve();
3208         OSIUNITTEST_ASSERT_ERROR(eq(si->getObjValue(), 2.0), {}, solverName, "testAddToEmptySystem: getObjValue after adding empty columns");
3209       }
3210     }
3211 
3212     delete si;
3213   }
3214   // Test adding rows to NULL - alternative row vector format
3215   {
3216     OsiSolverInterface *si = emptySi->clone();
3217     int i;
3218 
3219     //Matrix
3220     int column[] = { 0, 1, 2, 0, 1, 2 };
3221     double row1E[] = { 4.0, 7.0, 5.0 };
3222     double row2E[] = { 7.0, 4.0, 5.0 };
3223     double row12E[] = { 4.0, 7.0, 5.0, 7.0, 4.0, 5.0 };
3224     CoinBigIndex starts[] = { 0, 3, 6 };
3225     double ub[] = { 100.0, 100.0 };
3226 
3227     double objective[] = { 5.0, 6.0, 5.5 };
3228 
3229     {
3230       // Add empty columns
3231       for (i = 0; i < 3; i++) {
3232         const CoinPackedVector reqdBySunCC;
3233         si->addCol(reqdBySunCC, 0.0, 10.0, objective[i]);
3234       }
3235 
3236       // Add rows
3237       si->addRows(2, starts, column, row12E, NULL, ub);
3238       // and again
3239       si->addRow(3, column, row1E, 2.0, 100.0);
3240       si->addRow(3, column, row2E, 2.0, 100.0);
3241 
3242       // Vol can not solve problem of this form
3243       if (!volSolverInterface) {
3244         // solve
3245         si->initialSolve();
3246         OSIUNITTEST_ASSERT_ERROR(eq(si->getObjValue(), 2.0), {}, solverName, "testAddToEmptySystem: getObjValue after adding empty columns and then rows");
3247       }
3248     }
3249 
3250     delete si;
3251   }
3252   /*
3253   Add columns to an empty system. Start by creating empty rows, then add
3254   some columns.
3255   */
3256   {
3257     OsiSolverInterface *si = emptySi->clone();
3258     int i;
3259 
3260     //Matrix
3261     int row[] = { 0, 1 };
3262     double col1E[] = { 4.0, 7.0 };
3263     double col2E[] = { 7.0, 4.0 };
3264     double col3E[] = { 5.0, 5.0 };
3265     CoinPackedVector col1(2, row, col1E);
3266     CoinPackedVector col2(2, row, col2E);
3267     CoinPackedVector col3(2, row, col3E);
3268 
3269     double objective[] = { 5.0, 6.0, 5.5 };
3270     {
3271       // Add empty rows
3272       for (i = 0; i < 2; i++) {
3273         const CoinPackedVector reqdBySunCC;
3274         si->addRow(reqdBySunCC, 2.0, 100.0);
3275       }
3276 
3277       // Add columns
3278       if (volSolverInterface) {
3279         // FIXME: this test could be done w/ the volume, but the rows must not be ranged.
3280         OSIUNITTEST_ADD_OUTCOME(solverName, "testAddToEmptySystem", "addCol adds columns to NULL", TestOutcome::WARNING, true);
3281         failureMessage(solverName, "addCol add columns to null");
3282       } else {
3283         si->addCol(col1, 0.0, 10.0, objective[0]);
3284         si->addCol(col2, 0.0, 10.0, objective[1]);
3285         si->addCol(col3, 0.0, 10.0, objective[2]);
3286 
3287         // solve
3288         si->initialSolve();
3289 
3290         CoinRelFltEq eq(1.0e-7);
3291         OSIUNITTEST_ASSERT_ERROR(eq(si->getObjValue(), 2.0), {}, solverName, "testAddToEmptySystem: getObjValue after adding empty rows and then columns");
3292       }
3293     }
3294     delete si;
3295   }
3296   // Test adding columns to NULL - alternative column vector format
3297   {
3298     OsiSolverInterface *si = emptySi->clone();
3299     int i;
3300 
3301     //Matrix
3302     int row[] = { 0, 1 };
3303     double col1E[] = { 4.0, 7.0 };
3304     double col23E[] = { 7.0, 4.0, 5.0, 5.0 };
3305     int row23E[] = { 0, 1, 0, 1 };
3306     CoinBigIndex start23E[] = { 0, 2, 4 };
3307     double ub23E[] = { 10.0, 10.0 };
3308 
3309     double objective[] = { 5.0, 6.0, 5.5 };
3310     {
3311       // Add empty rows
3312       for (i = 0; i < 2; i++) {
3313         const CoinPackedVector reqdBySunCC;
3314         si->addRow(reqdBySunCC, 2.0, 100.0);
3315       }
3316 
3317       // Add columns
3318       if (volSolverInterface) {
3319         // FIXME: this test could be done w/ the volume, but the rows must not be ranged.
3320         OSIUNITTEST_ADD_OUTCOME(solverName, "testAddToEmptySystem", "addCol adds columns to NULL", TestOutcome::WARNING, true);
3321       } else {
3322         si->addCols(2, start23E, row23E, col23E, NULL, ub23E, objective + 1);
3323         si->addCol(2, row, col1E, 0.0, 10.0, objective[0]);
3324 
3325         // solve
3326         si->initialSolve();
3327         OSIUNITTEST_ASSERT_ERROR(eq(si->getObjValue(), 2.0), {}, solverName, "testAddToEmptySystem: getObjValue after adding empty rows and then columns (alternative format)");
3328       }
3329     }
3330     delete si;
3331   }
3332   /*
3333   Add contradicting columns to an empty system.
3334   Either the OSI rejects these column via an exception or the LP is proven infeasible.
3335   */
3336   {
3337     OsiSolverInterface *si = emptySi->clone();
3338     int i;
3339 
3340     //Matrix
3341     int row[] = { 0, 1 };
3342     double col1E[] = { 4.0, 7.0 };
3343     double col2E[] = { 7.0, 4.0 };
3344     double col3E[] = { 5.0, 5.0 };
3345     CoinPackedVector col1(2, row, col1E);
3346     CoinPackedVector col2(2, row, col2E);
3347     CoinPackedVector col3(2, row, col3E);
3348 
3349     double objective[] = { 5.0, 6.0, 5.5 };
3350     {
3351       // Add empty rows
3352       for (i = 0; i < 2; i++) {
3353         const CoinPackedVector reqdBySunCC;
3354         si->addRow(reqdBySunCC, 100.0, 100.0);
3355       }
3356 
3357       // Add columns
3358       try {
3359     	si->addCol(col1, 10.0, -10.0, objective[0]);
3360     	si->addCol(col2, si->getInfinity(), 0.0, objective[1]);
3361     	si->addCol(col3, si->getInfinity(), si->getInfinity(), objective[2]);
3362 
3363         // solve
3364         si->initialSolve();
3365 
3366         OSIUNITTEST_ASSERT_ERROR(si->isAbandoned() || si->isProvenPrimalInfeasible(), {}, solverName, "testAddToEmptySystem: not infeasible or abandoned after adding contradicting columns");
3367       }
3368       catch( CoinError& e )
3369       {
3370     	OSIUNITTEST_ADD_OUTCOME(solverName, "testAddToEmptySystem", "addCol adds contradicting columns threw CoinError", TestOutcome::PASSED, true);
3371       }
3372     }
3373     delete si;
3374   }
3375   /*
3376   Add rows with contradicting sides to an empty system. Begin by creating empty columns, then add some
3377   rows.
3378   */
3379   {
3380     OsiSolverInterface *si = emptySi->clone();
3381     int i;
3382 
3383     //Matrix
3384     int column[] = { 0, 1, 2 };
3385     double row1E[] = { 4.0, 7.0, 5.0 };
3386     double row2E[] = { 7.0, 4.0, 5.0 };
3387     CoinPackedVector row1(3, column, row1E);
3388     CoinPackedVector row2(3, column, row2E);
3389 
3390     double objective[] = { 5.0, 6.0, 5.5 };
3391 
3392     {
3393       // Add empty columns
3394       for (i = 0; i < 3; i++) {
3395         const CoinPackedVector reqdBySunCC;
3396         si->addCol(reqdBySunCC, 0.0, 10.0, objective[i]);
3397       }
3398 
3399       try {
3400     	// Add rows
3401     	si->addRow(row1, -100.0, 100.0);
3402     	si->addRow(row2, si->getInfinity(), -si->getInfinity());
3403 
3404     	// Vol can not solve problem of this form
3405     	if (!volSolverInterface) {
3406     	  // solve
3407     	  si->initialSolve();
3408           OSIUNITTEST_ASSERT_ERROR(si->isAbandoned() || si->isProvenPrimalInfeasible(), {}, solverName, "testAddToEmptySystem: infeasible or abandoned after adding rows with contradicting sides");
3409     	}
3410       }
3411       catch( CoinError& e )
3412       {
3413     	OSIUNITTEST_ADD_OUTCOME(solverName, "testAddToEmptySystem", "addRow for rows with contradicting sides threw CoinError", TestOutcome::PASSED, true);
3414       }
3415     }
3416 
3417     delete si;
3418   }
3419 }
3420 
3421 /*
3422   OsiPresolve has the property that it will report the correct (untransformed)
3423   objective for the presolved problem.
3424 
3425   Test OsiPresolve by checking the objective that we get by optimising the
3426   presolved problem. Then postsolve to get back to the original problem
3427   statement and check that we have the same objective without further
3428   iterations. This is much more a check on OsiPresolve than on the OsiXXX
3429   under test. OsiPresolve simply calls the underlying OsiXXX when it needs to
3430   solve a model. All the work involved with presolve and postsolve transforms
3431   is handled in OsiPresolve.
3432 
3433   The problems are a selection of problems from Data/Sample. In particular,
3434   e226 is in the list by virtue of having a constant offset (7.113) defined
3435   for the objective, and p0201 is in the list because presolve (as of 071015)
3436   finds no reductions.
3437 
3438   The objective for finnis (1.7279106559e+05) is not the same as the
3439   objective used by Netlib (1.7279096547e+05), but solvers clp, dylp, glpk,
3440   and cplex agree that it's correct.
3441 
3442   This test could be made stronger, but more brittle, by checking for the
3443   expected size of the constraint system after presolve. It would also be good
3444   to add a maximisation problem and check for signs of reduced costs and duals.
3445 
3446   Returns the number of errors encountered.
3447 */
testOsiPresolve(const OsiSolverInterface * emptySi,const std::string & sampleDir)3448 int testOsiPresolve(const OsiSolverInterface *emptySi,
3449   const std::string &sampleDir)
3450 
3451 {
3452   typedef std::pair< std::string, double > probPair;
3453   std::vector< probPair > sampleProbs;
3454 
3455   sampleProbs.push_back(probPair("brandy", 1.5185098965e+03));
3456   sampleProbs.push_back(probPair("e226", (-18.751929066 + 7.113)));
3457   //#ifdef COIN_HAS_GRB
3458   //  // for the demo license of Gurobi, model "finnis" is too large, so we skip it in this case
3459   //  if( dynamic_cast<const OsiGrbSolverInterface*>(emptySi) && dynamic_cast<const OsiGrbSolverInterface*>(emptySi)->isDemoLicense() )
3460   //    std::cout << "Skip model finnis in test of OsiPresolve with Gurobi, since we seem to have only a demo license of Gurobi." << std::endl;
3461   //  else
3462   //#endif
3463   sampleProbs.push_back(probPair("finnis", 1.7279106559e+05));
3464   sampleProbs.push_back(probPair("p0201", 6875));
3465 
3466   CoinRelFltEq eq(1.0e-8);
3467 
3468   int errs = 0;
3469   int warnings = 0;
3470 
3471   std::string solverName = "Unknown solver";
3472   OSIUNITTEST_ASSERT_ERROR(emptySi->getStrParam(OsiSolverName, solverName) == true, ++errs, solverName, "testOsiPresolve: getStrParam(OsiSolverName)");
3473 
3474   std::cout << "Testing OsiPresolve ... " << std::endl;
3475 
3476   for (unsigned i = 0; i < sampleProbs.size(); i++) {
3477     OsiSolverInterface *si = emptySi->clone();
3478     if (!si->setIntParam(OsiNameDiscipline, 1))
3479       std::cout << "  attempt to switch to lazy names failed.";
3480 
3481     std::string mpsName = sampleProbs[i].first;
3482     double correctObj = sampleProbs[i].second;
3483 
3484     std::cout << "  testing presolve on " << mpsName << "." << std::endl;
3485 
3486     std::string fn = sampleDir + mpsName;
3487     OSIUNITTEST_ASSERT_ERROR(si->readMps(fn.c_str(), "mps") == 0, delete si; ++errs; continue, solverName, "testOsiPresolve: read MPS");
3488     /*
3489   Set up for presolve. Allow very slight (1.0e-8) bound relaxation to retain
3490   feasibility. Discard integrality information (false) and limit the number of
3491   presolve passes to 5.
3492 */
3493     OsiSolverInterface *presolvedModel;
3494     OsiPresolve pinfo;
3495     presolvedModel = pinfo.presolvedModel(*si, 1.0e-8, false, 5);
3496     OSIUNITTEST_ASSERT_ERROR(presolvedModel != NULL, delete si; ++errs; continue, solverName, "testOsiPresolve");
3497     /*
3498   Optimise the presolved model and check the objective.  We need to turn off
3499   any native presolve, which may or may not affect the objective.
3500 */
3501     presolvedModel->setHintParam(OsiDoPresolveInInitial, false, OsiHintDo);
3502     presolvedModel->initialSolve();
3503     OSIUNITTEST_ASSERT_ERROR(eq(correctObj, presolvedModel->getObjValue()), delete si; ++errs; continue, solverName, "testOsiPresolve");
3504     /*
3505   Postsolve to return to the original formulation. The presolvedModel should
3506   no longer be needed once we've executed postsolve. Check that we get the
3507   correct objective without iterations. As before, turn off any native
3508   presolve.
3509 */
3510     pinfo.postsolve(true);
3511     delete presolvedModel;
3512     si->setHintParam(OsiDoPresolveInResolve, false, OsiHintDo);
3513     si->resolve();
3514     OSIUNITTEST_ASSERT_ERROR(eq(correctObj, si->getObjValue()), ++errs, solverName, "testOsiPresolve: postsolve objective value");
3515     OSIUNITTEST_ASSERT_WARNING(si->getIterationCount() == 0, ++warnings, solverName, "testOsiPresolve: postsolve number of iterations");
3516 
3517     delete si;
3518   }
3519 
3520   if (errs == 0) {
3521     std::cout << "OsiPresolve test ok with " << warnings << " warnings." << std::endl;
3522   } else {
3523     failureMessage(solverName, "errors during OsiPresolve test.");
3524   }
3525 
3526   return (errs);
3527 }
3528 
3529 /*
3530   Test the values returned by an empty solver interface.
3531 */
testEmptySi(const OsiSolverInterface * emptySi)3532 void testEmptySi(const OsiSolverInterface *emptySi)
3533 
3534 {
3535   std::string solverName;
3536   const OsiSolverInterface *si = emptySi->clone();
3537 
3538   std::cout << "Testing empty solver interface ... " << std::endl;
3539 
3540   si->getStrParam(OsiSolverName, solverName);
3541 
3542   OSIUNITTEST_ASSERT_ERROR(si->getNumRows() == 0, {}, solverName, "testEmptySi");
3543   OSIUNITTEST_ASSERT_ERROR(si->getNumCols() == 0, {}, solverName, "testEmptySi");
3544   OSIUNITTEST_ASSERT_ERROR(si->getNumElements() == 0, {}, solverName, "testEmptySi");
3545   OSIUNITTEST_ASSERT_ERROR(si->getColLower() == NULL, {}, solverName, "testEmptySi");
3546   OSIUNITTEST_ASSERT_ERROR(si->getColUpper() == NULL, {}, solverName, "testEmptySi");
3547   OSIUNITTEST_ASSERT_ERROR(si->getColSolution() == NULL, {}, solverName, "testEmptySi");
3548   OSIUNITTEST_ASSERT_ERROR(si->getObjCoefficients() == NULL, {}, solverName, "testEmptySi");
3549   OSIUNITTEST_ASSERT_ERROR(si->getRowRange() == NULL, {}, solverName, "testEmptySi");
3550   OSIUNITTEST_ASSERT_ERROR(si->getRightHandSide() == NULL, {}, solverName, "testEmptySi");
3551   OSIUNITTEST_ASSERT_ERROR(si->getRowSense() == NULL, {}, solverName, "testEmptySi");
3552   OSIUNITTEST_ASSERT_ERROR(si->getRowLower() == NULL, {}, solverName, "testEmptySi");
3553   OSIUNITTEST_ASSERT_ERROR(si->getRowUpper() == NULL, {}, solverName, "testEmptySi");
3554 
3555   delete si;
3556 }
3557 
3558 /*
3559   This routine uses the problem galenet (included in Data/Sample) to check
3560   getDualRays. Galenet is a primal infeasible flow problem:
3561 
3562   s1: t14 <= 20
3563   s2: t24 + t25 <= 20
3564   s3: t35 <= 20
3565   n4: t14 + t24 - t46 - t47 = 0
3566   n5: t25 + t35 - t57 - t58 = 0
3567   d6: t46 >= 10
3568   d7: t47 + t57 >= 20
3569   d8: t58 >= 30
3570 
3571   t14,t58 <= 30    tt24, t57 <= 20    t25, t35, t46 <= 10    t47 <= 2
3572 
3573   Galenet is the original form, with mixed explicit constraints and implicit
3574   bound constraints. Galenetbnds is the same problem, but with implicit
3575   bounds converted to explicit constraints and all constraints converted to
3576   inequalities so that the algebraic test still works.
3577 
3578   The routine doesn't actually test for specific dual rays; rather, it tests
3579   for rA >= 0 and rb < 0, on the assumption that the dual constraint system
3580   matches the canonical form min yb  yA >= c. (More accurately, on the
3581   assumption that the sign convention of the ray is correct for the canonical
3582   form.)
3583 
3584   The strategy is to check first for the ability to return a ray with row and
3585   column components, then a ray with row components only. If both of these
3586   result in a throw, conclude that the solver does not implement getDualRays.
3587 */
3588 
testDualRays(const OsiSolverInterface * emptySi,const std::string & sampleDir)3589 void testDualRays(const OsiSolverInterface *emptySi,
3590   const std::string &sampleDir)
3591 
3592 {
3593   unsigned int rayNdx, raysReturned;
3594   bool hasGetDualRays = false;
3595 
3596   std::string solverName;
3597   OsiSolverInterface *si = 0;
3598 
3599   std::vector< double * > rays;
3600   const int raysRequested = 5;
3601   const std::string mpsNames[] = { "galenet", "galenetbnds" };
3602   const bool rayTypes[] = { true, false };
3603 
3604   std::cout << "Testing getDualRays ..." << std::endl;
3605   /*
3606   Figure out what we can test. getDualRays only makes sense after solving a
3607   problem, so the strategy is to solve galenet and try for full rays. If that
3608   fails, solve galenetbnds and try for row-component rays. If that fails,
3609   conclude that the solver doesn't implement getDualRays.
3610 */
3611   for (int iter = 0; iter <= 1; iter++) {
3612     const bool fullRay = rayTypes[iter];
3613     const std::string mpsName = mpsNames[iter];
3614     const std::string fn = sampleDir + mpsName;
3615 
3616     si = emptySi->clone();
3617     si->getStrParam(OsiSolverName, solverName);
3618     std::cout
3619       << "  checking if " << solverName << " implements getDualRays(maxRays"
3620       << ((fullRay == true) ? ",true" : "") << ") ... ";
3621 
3622     si->setIntParam(OsiNameDiscipline, 1);
3623 
3624     OSIUNITTEST_ASSERT_ERROR(si->readMps(fn.c_str(), "mps") == 0, delete si; return, solverName, "testDualRays: read MPS");
3625     /*
3626   Solve and report the result. We should be primal infeasible, and not optimal.
3627   Specify maximisation just for kicks.
3628 */
3629     si->setObjSense(-1.0);
3630     si->setHintParam(OsiDoPresolveInInitial, false, OsiHintDo);
3631     si->setHintParam(OsiDoReducePrint, true, OsiHintDo);
3632     si->initialSolve();
3633     OSIUNITTEST_ASSERT_ERROR(!si->isProvenOptimal(), {}, solverName, "testDualRays: infeasible instance not proven optimal");
3634     OSIUNITTEST_ASSERT_ERROR(si->isProvenPrimalInfeasible(), {}, solverName, "testDualRays: recognize infeasiblity of instance");
3635     /*
3636   Try a call to getDualRays. If the call throws, abort this iteration and
3637   try again.
3638 */
3639     try {
3640       rays = si->getDualRays(raysRequested, fullRay);
3641       hasGetDualRays = true;
3642       std::cout << "yes." << std::endl;
3643     } catch (CoinError &err) {
3644       std::cout << "no." << std::endl;
3645       delete si;
3646       si = 0;
3647       continue;
3648     }
3649     /*
3650   We have rays. Check to see how many. There should be at least one, and no
3651   more than the number requested. If there are none, bail out now.
3652 */
3653     raysReturned = static_cast< unsigned int >(rays.size());
3654     OSIUNITTEST_ASSERT_ERROR(raysReturned >= 1, break, solverName, "testDualRays: number of returned rays");
3655     OSIUNITTEST_ASSERT_WARNING(static_cast< int >(raysReturned) <= raysRequested, {}, solverName, "testDualRays: number of returned rays");
3656     /*
3657   Do a bit of setup before checking each ray. If we're dealing with a full
3658   ray, we'll need variable bounds, solution value, and status. Acquire the
3659   bounds arrays, and acquire a warm start object so we can ask for column
3660   status. Failure to retrieve a warm start aborts the test.
3661 */
3662 
3663     unsigned int m, n, i, j;
3664     m = si->getNumRows();
3665     n = si->getNumCols();
3666     unsigned int rayLen = m;
3667     CoinWarmStartBasis *wsb = 0;
3668     const double *vlbs = 0;
3669     const double *vubs = 0;
3670     const double *xvals = 0;
3671     const double *rhs = si->getRightHandSide();
3672     const char *sense = si->getRowSense();
3673     if (fullRay == true) {
3674       rayLen += n;
3675       wsb = dynamic_cast< CoinWarmStartBasis * >(si->getWarmStart());
3676       OSIUNITTEST_ASSERT_ERROR(wsb != NULL, break, solverName, "testDualRays: get warmstart basis");
3677       vlbs = si->getColLower();
3678       vubs = si->getColUpper();
3679       xvals = si->getColSolution();
3680     }
3681 
3682     double tol;
3683     si->getDblParam(OsiDualTolerance, tol);
3684 
3685     double *rA = new double[rayLen];
3686     /*
3687   Open a loop to check each ray for validity.
3688 */
3689     for (rayNdx = 0; rayNdx < raysReturned; rayNdx++) {
3690       double *ray = rays[rayNdx];
3691 
3692       if (OsiUnitTest::verbosity >= 2) {
3693         std::cout << "  Ray[" << rayNdx << "]: " << std::endl;
3694         for (i = 0; i < m; i++) {
3695           if (fabs(ray[i]) > tol) {
3696             std::cout << "    " << si->getRowName(i) << " [" << i << "]: " << ray[i] << "\t rhs: " << rhs[i] << "\t sense: " << sense[i] << std::endl;
3697           }
3698         }
3699         if (fullRay == true) {
3700           for (j = 0; j < n; j++) {
3701             if (fabs(ray[m + j]) > tol) {
3702               std::cout << "    " << si->getColName(j) << " [" << j << "]: " << ray[m + j] << std::endl;
3703             }
3704           }
3705         }
3706       }
3707       /*
3708   Check that the ray is not identically zero.
3709 */
3710       for (i = 0; i < rayLen; i++) {
3711         if (fabs(ray[i]) > tol)
3712           break;
3713       }
3714       OSIUNITTEST_ASSERT_ERROR(i < rayLen, continue, solverName, "testDualRays: ray should not be zero");
3715       /*
3716   Check that dot(r,b) < 0. For the first m components this is a
3717   straightforward dot product. If we're dealing with column components, we
3718   need to synthesize the coefficient on-the-fly. There can be at most one
3719   nonzero associated with an out-of-bound basic primal, which corresponds to
3720   the nonbasic dual that's driving the ray.
3721 */
3722       double rdotb = 0.0;
3723       int nzoobCnt = 0;
3724       for (i = 0; i < m; i++) {
3725         rdotb += rhs[i] * ray[i];
3726       }
3727       if (fullRay == true) {
3728         CoinWarmStartBasis::Status statj;
3729         for (j = 0; j < n; j++) {
3730           statj = wsb->getStructStatus(j);
3731           switch (statj) {
3732           case CoinWarmStartBasis::atUpperBound: {
3733             rdotb += vubs[j] * ray[m + j];
3734             break;
3735           }
3736           case CoinWarmStartBasis::atLowerBound: {
3737             rdotb += (-vlbs[j]) * ray[m + j];
3738             break;
3739           }
3740           case CoinWarmStartBasis::basic: {
3741             if (ray[m + j] != 0) {
3742               nzoobCnt++;
3743               OSIUNITTEST_ASSERT_ERROR(xvals[j] > vubs[j] || xvals[j] < vlbs[j], break, solverName, "testDualRays: xval outside bounds for nonzero ray entry");
3744               if (xvals[j] > vubs[j]) {
3745                 rdotb += vubs[j] * ray[m + j];
3746               } else if (xvals[j] < vlbs[j]) {
3747                 rdotb += (-vlbs[j]) * ray[m + j];
3748               }
3749             }
3750             break;
3751           }
3752           default: {
3753             OSIUNITTEST_ASSERT_ERROR(fabs(ray[i]) <= tol, {}, solverName, "testDualRays: zero ray entry for basic variables");
3754             break;
3755           }
3756           }
3757         }
3758         OSIUNITTEST_ASSERT_ERROR(nzoobCnt <= 1, {}, solverName, "testDualRays: at most one nonzero ray entry for basic variables");
3759       }
3760       if (OsiUnitTest::verbosity >= 2)
3761         std::cout << "dot(r,b) = " << rdotb << std::endl;
3762       OSIUNITTEST_ASSERT_ERROR(rdotb < 0, {}, solverName, "testDualRays: ray points into right direction");
3763       /*
3764   On to rA >= 0. As with dot(r,b), it's trivially easy to do the calculation
3765   for explicit constraints, but we have to synthesize the coefficients
3766   corresponding to bounded variables on-the-fly. Upper bounds look like
3767   x<j> <= u<j>, lower bounds -x<j> <= -l<j>. No need to repeat the ray
3768   coefficient tests.
3769 */
3770       CoinFillN(rA, m, 0.0);
3771       si->getMatrixByCol()->transposeTimes(ray, rA);
3772       if (fullRay == true) {
3773         CoinWarmStartBasis::Status statj;
3774         for (j = 0; j < n; j++) {
3775           statj = wsb->getStructStatus(j);
3776           switch (statj) {
3777           case CoinWarmStartBasis::atUpperBound: {
3778             rA[j] += ray[m + j];
3779             break;
3780           }
3781           case CoinWarmStartBasis::atLowerBound: {
3782             rA[j] += -ray[m + j];
3783             break;
3784           }
3785           case CoinWarmStartBasis::basic: {
3786             if (ray[m + j] != 0) {
3787               if (xvals[j] > vubs[j])
3788                 rA[j] += ray[m + j];
3789               else if (xvals[j] < vlbs[j])
3790                 rA[j] += -ray[m + j];
3791             }
3792             break;
3793           }
3794           default: {
3795             break;
3796           }
3797           }
3798         }
3799       }
3800 
3801       bool badVal = false;
3802       for (j = 0; j < n; j++) {
3803         if (rA[j] < -tol) {
3804           std::cout << "  " << solverName << ": ray[" << rayNdx << "] fails rA >= 0 for column " << j << " with value " << rA[j] << "." << std::endl;
3805           badVal = true;
3806         }
3807       }
3808       OSIUNITTEST_ASSERT_ERROR(badVal == false, {}, solverName, "testDualRays: rA >= 0");
3809       if (badVal == true && OsiUnitTest::verbosity >= 2) {
3810         std::cout << "  Ray[" << rayNdx << "]: " << std::endl;
3811         for (i = 0; i < m; i++) {
3812           if (fabs(ray[i]) > tol) {
3813             std::cout << "    [" << i << "]: " << ray[i] << std::endl;
3814           }
3815         }
3816       }
3817     }
3818     /*
3819   Clean up.
3820 */
3821     delete[] rA;
3822     for (rayNdx = 0; rayNdx < raysReturned; rayNdx++) {
3823       delete[] rays[rayNdx];
3824     }
3825     delete si;
3826   }
3827   /*
3828   Report the result and we're done.
3829 */
3830   OSIUNITTEST_ASSERT_SEVERITY_EXPECTED(hasGetDualRays, {}, solverName, "testDualRays: getDualRays is implemented", TestOutcome::NOTE, false);
3831   if (hasGetDualRays == false) {
3832     testingMessage("  *** WARNING *** getDualRays is unimplemented.\n");
3833   }
3834 }
3835 
3836 } // end file-local namespace
3837 
3838 //#############################################################################
3839 // The main event
3840 //#############################################################################
3841 
3842 /*
3843   The order of tests should be examined. As it stands, we test immediately
3844   for the ability to read an mps file and bail if we can't do it. But quite a
3845   few tests could be performed without reading an mps file.  -- lh, 080107 --
3846 
3847   Gradually, oh so gradually, the Osi unit test is converting to produce some
3848   information about failed tests, and this routine now returns a count.
3849   Whenever you revise a test, please take the time to produce a count of
3850   errors.
3851 */
3852 
OsiSolverInterfaceCommonUnitTest(const OsiSolverInterface * emptySi,const std::string & mpsDir,const std::string &)3853 void OsiSolverInterfaceCommonUnitTest(const OsiSolverInterface *emptySi,
3854   const std::string &mpsDir,
3855   const std::string & /* netlibDir */)
3856 {
3857 
3858   CoinRelFltEq eq;
3859 
3860   /*
3861   Test if the si knows its name. The name will be used for displaying messages
3862   when testing.
3863 */
3864   std::string solverName;
3865   {
3866     OsiSolverInterface *si = emptySi->clone();
3867     assert(si != NULL);
3868     solverName = "Unknown Solver";
3869     OSIUNITTEST_ASSERT_ERROR(si->getStrParam(OsiSolverName, solverName), {}, solverName, "getStrParam(OsiSolverName) supported");
3870     OSIUNITTEST_ASSERT_ERROR(solverName != "Unknown Solver", {}, solverName, "solver knows its name");
3871     delete si;
3872   }
3873   {
3874     std::string temp = ": running common unit tests.\n";
3875     temp = solverName + temp;
3876     testingMessage(temp.c_str());
3877   }
3878 
3879   /*
3880   Set a variable so we can easily determine which solver interface we're
3881   testing. This is so that we can easily decide to omit a test when it's
3882   beyond the capability of a solver.
3883 */
3884   bool volSolverInterface UNUSED = (solverName == "vol");
3885   bool dylpSolverInterface UNUSED = (solverName == "dylp");
3886   bool glpkSolverInterface UNUSED = (solverName == "glpk");
3887   bool xprSolverInterface UNUSED = (solverName == "xpress");
3888   bool symSolverInterface UNUSED = (solverName == "sym");
3889   bool grbSolverInterface UNUSED = (solverName == "gurobi");
3890   bool cpxSolverInterface UNUSED = (solverName == "cplex");
3891   bool spxSolverInterface UNUSED = (solverName == "soplex");
3892 
3893   /*
3894   Test values returned by an empty solver interface.
3895 */
3896   testEmptySi(emptySi);
3897   /*
3898   See if we can read an MPS file. We're dead in the water if we can't do this.
3899 */
3900   std::string fn = mpsDir + "exmip1";
3901   OsiSolverInterface *exmip1Si = emptySi->clone();
3902   assert(exmip1Si != NULL);
3903   OSIUNITTEST_ASSERT_ERROR(exmip1Si->readMps(fn.c_str(), "mps") == 0, return, *exmip1Si, "read MPS file");
3904   /*
3905   Test that the solver correctly handles row and column names.
3906 */
3907   testNames(emptySi, fn);
3908   /*
3909   Test constants in objective function, dual and primal objective limit
3910   functions, objective sense (max/min).
3911   Do not perform test if Vol solver, because it requires problems of a
3912   special form and can not solve netlib e226.
3913 */
3914   if (!volSolverInterface) {
3915     testObjFunctions(emptySi, mpsDir);
3916   } else {
3917     OSIUNITTEST_ADD_OUTCOME(solverName, "testObjFunctions", "skipped test for OsiVol", OsiUnitTest::TestOutcome::NOTE, true);
3918   }
3919 
3920   // Test that problem was loaded correctly
3921 
3922   {
3923     int nc = exmip1Si->getNumCols();
3924     int nr = exmip1Si->getNumRows();
3925     OSIUNITTEST_ASSERT_ERROR(nc == 8, return, *exmip1Si, "problem read correctly: number of columns");
3926     OSIUNITTEST_ASSERT_ERROR(nr == 5, return, *exmip1Si, "problem read correctly: number of rows");
3927 
3928     const char *exmip1Sirs = exmip1Si->getRowSense();
3929     OSIUNITTEST_ASSERT_ERROR(exmip1Sirs[0] == 'G', {}, *exmip1Si, "problem read correctly: row sense");
3930     OSIUNITTEST_ASSERT_ERROR(exmip1Sirs[1] == 'L', {}, *exmip1Si, "problem read correctly: row sense");
3931     OSIUNITTEST_ASSERT_ERROR(exmip1Sirs[2] == 'E', {}, *exmip1Si, "problem read correctly: row sense");
3932     OSIUNITTEST_ASSERT_ERROR(exmip1Sirs[3] == 'R', {}, *exmip1Si, "problem read correctly: row sense");
3933     OSIUNITTEST_ASSERT_ERROR(exmip1Sirs[4] == 'R', {}, *exmip1Si, "problem read correctly: row sense");
3934 
3935     const double *exmip1Sirhs = exmip1Si->getRightHandSide();
3936     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirhs[0], 2.5), {}, *exmip1Si, "problem read correctly: row rhs");
3937     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirhs[1], 2.1), {}, *exmip1Si, "problem read correctly: row rhs");
3938     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirhs[2], 4.0), {}, *exmip1Si, "problem read correctly: row rhs");
3939     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirhs[3], 5.0), {}, *exmip1Si, "problem read correctly: row rhs");
3940     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirhs[4], 15.), {}, *exmip1Si, "problem read correctly: row rhs");
3941 
3942     const double *exmip1Sirr = exmip1Si->getRowRange();
3943     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirr[0], 0.0), {}, *exmip1Si, "problem read correctly: row range");
3944     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirr[1], 0.0), {}, *exmip1Si, "problem read correctly: row range");
3945     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirr[2], 0.0), {}, *exmip1Si, "problem read correctly: row range");
3946     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirr[3], 5.0 - 1.8), {}, *exmip1Si, "problem read correctly: row range");
3947     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirr[4], 15.0 - 3.0), {}, *exmip1Si, "problem read correctly: row range");
3948 
3949     const CoinPackedMatrix *goldByCol = BuildExmip1Mtx();
3950     CoinPackedMatrix goldmtx;
3951     goldmtx.reverseOrderedCopyOf(*goldByCol);
3952     delete goldByCol;
3953 
3954     CoinPackedMatrix pm;
3955     pm.setExtraGap(0.0);
3956     pm.setExtraMajor(0.0);
3957     pm = *exmip1Si->getMatrixByRow();
3958     pm.removeGaps();
3959     OSIUNITTEST_ASSERT_ERROR(goldmtx.isEquivalent(pm), {}, *exmip1Si, "problem read correctly: matrix by row");
3960 
3961     const double *cl = exmip1Si->getColLower();
3962     OSIUNITTEST_ASSERT_ERROR(eq(cl[0], 2.5), {}, *exmip1Si, "problem read correctly: columns lower bounds");
3963     OSIUNITTEST_ASSERT_ERROR(eq(cl[1], 0.0), {}, *exmip1Si, "problem read correctly: columns lower bounds");
3964     OSIUNITTEST_ASSERT_ERROR(eq(cl[2], 0.0), {}, *exmip1Si, "problem read correctly: columns lower bounds");
3965     OSIUNITTEST_ASSERT_ERROR(eq(cl[3], 0.0), {}, *exmip1Si, "problem read correctly: columns lower bounds");
3966     OSIUNITTEST_ASSERT_ERROR(eq(cl[4], 0.5), {}, *exmip1Si, "problem read correctly: columns lower bounds");
3967     OSIUNITTEST_ASSERT_ERROR(eq(cl[5], 0.0), {}, *exmip1Si, "problem read correctly: columns lower bounds");
3968     OSIUNITTEST_ASSERT_ERROR(eq(cl[6], 0.0), {}, *exmip1Si, "problem read correctly: columns lower bounds");
3969     OSIUNITTEST_ASSERT_ERROR(eq(cl[7], 0.0), {}, *exmip1Si, "problem read correctly: columns lower bounds");
3970 
3971     const double *cu = exmip1Si->getColUpper();
3972     OSIUNITTEST_ASSERT_ERROR(eq(cu[0], exmip1Si->getInfinity()), {}, *exmip1Si, "problem read correctly: columns upper bounds");
3973     OSIUNITTEST_ASSERT_ERROR(eq(cu[1], 4.1), {}, *exmip1Si, "problem read correctly: columns upper bounds");
3974     OSIUNITTEST_ASSERT_ERROR(eq(cu[2], 1.0), {}, *exmip1Si, "problem read correctly: columns upper bounds");
3975     OSIUNITTEST_ASSERT_ERROR(eq(cu[3], 1.0), {}, *exmip1Si, "problem read correctly: columns upper bounds");
3976     OSIUNITTEST_ASSERT_ERROR(eq(cu[4], 4.0), {}, *exmip1Si, "problem read correctly: columns upper bounds");
3977     OSIUNITTEST_ASSERT_ERROR(eq(cu[5], exmip1Si->getInfinity()), {}, *exmip1Si, "problem read correctly: columns upper bounds");
3978     OSIUNITTEST_ASSERT_ERROR(eq(cu[6], exmip1Si->getInfinity()), {}, *exmip1Si, "problem read correctly: columns upper bounds");
3979     OSIUNITTEST_ASSERT_ERROR(eq(cu[7], 4.3), {}, *exmip1Si, "problem read correctly: columns upper bounds");
3980 
3981     const double *rl = exmip1Si->getRowLower();
3982     OSIUNITTEST_ASSERT_ERROR(eq(rl[0], 2.5), {}, *exmip1Si, "problem read correctly: rows lower bounds");
3983     OSIUNITTEST_ASSERT_ERROR(eq(rl[1], -exmip1Si->getInfinity()), {}, *exmip1Si, "problem read correctly: rows lower bounds");
3984     OSIUNITTEST_ASSERT_ERROR(eq(rl[2], 4.0), {}, *exmip1Si, "problem read correctly: rows lower bounds");
3985     OSIUNITTEST_ASSERT_ERROR(eq(rl[3], 1.8), {}, *exmip1Si, "problem read correctly: rows lower bounds");
3986     OSIUNITTEST_ASSERT_ERROR(eq(rl[4], 3.0), {}, *exmip1Si, "problem read correctly: rows lower bounds");
3987 
3988     const double *ru = exmip1Si->getRowUpper();
3989     OSIUNITTEST_ASSERT_ERROR(eq(ru[0], exmip1Si->getInfinity()), {}, *exmip1Si, "problem read correctly: rows upper bounds");
3990     OSIUNITTEST_ASSERT_ERROR(eq(ru[1], 2.1), {}, *exmip1Si, "problem read correctly: rows upper bounds");
3991     OSIUNITTEST_ASSERT_ERROR(eq(ru[2], 4.0), {}, *exmip1Si, "problem read correctly: rows upper bounds");
3992     OSIUNITTEST_ASSERT_ERROR(eq(ru[3], 5.0), {}, *exmip1Si, "problem read correctly: rows upper bounds");
3993     OSIUNITTEST_ASSERT_ERROR(eq(ru[4], 15.), {}, *exmip1Si, "problem read correctly: rows upper bounds");
3994 
3995     const double *objCoef = exmip1Si->getObjCoefficients();
3996     OSIUNITTEST_ASSERT_ERROR(eq(objCoef[0], 1.0), {}, *exmip1Si, "problem read correctly: objective coefficients");
3997     OSIUNITTEST_ASSERT_ERROR(eq(objCoef[1], 0.0), {}, *exmip1Si, "problem read correctly: objective coefficients");
3998     OSIUNITTEST_ASSERT_ERROR(eq(objCoef[2], 0.0), {}, *exmip1Si, "problem read correctly: objective coefficients");
3999     OSIUNITTEST_ASSERT_ERROR(eq(objCoef[3], 0.0), {}, *exmip1Si, "problem read correctly: objective coefficients");
4000     OSIUNITTEST_ASSERT_ERROR(eq(objCoef[4], 2.0), {}, *exmip1Si, "problem read correctly: objective coefficients");
4001     OSIUNITTEST_ASSERT_ERROR(eq(objCoef[5], 0.0), {}, *exmip1Si, "problem read correctly: objective coefficients");
4002     OSIUNITTEST_ASSERT_ERROR(eq(objCoef[6], 0.0), {}, *exmip1Si, "problem read correctly: objective coefficients");
4003     OSIUNITTEST_ASSERT_ERROR(eq(objCoef[7], -1.0), {}, *exmip1Si, "problem read correctly: objective coefficients");
4004 
4005   }
4006 
4007   // Test matrixByCol method
4008   {
4009     const CoinPackedMatrix *goldmtx = BuildExmip1Mtx();
4010     OsiSolverInterface &si = *exmip1Si->clone();
4011     CoinPackedMatrix sm = *si.getMatrixByCol();
4012     sm.removeGaps();
4013     OSIUNITTEST_ASSERT_ERROR(goldmtx->isEquivalent(sm), {}, solverName, "getMatrixByCol");
4014     delete goldmtx;
4015 
4016     // Test getting and setting of objective offset
4017     double objOffset;
4018     OSIUNITTEST_ASSERT_ERROR(si.getDblParam(OsiObjOffset, objOffset), {}, solverName, "getDblParam(OsiObjOffset)");
4019     OSIUNITTEST_ASSERT_ERROR(eq(objOffset, 0.0), {}, solverName, "objective offset 0 for exmip1");
4020     OSIUNITTEST_ASSERT_ERROR(si.setDblParam(OsiObjOffset, 3.21), {}, solverName, "setDblParam(OsiObjOffset)");
4021     si.getDblParam(OsiObjOffset, objOffset);
4022     OSIUNITTEST_ASSERT_ERROR(eq(objOffset, 3.21), {}, solverName, "storing objective offset");
4023 
4024     delete &si;
4025   }
4026 
4027   // Test clone
4028   {
4029     OsiSolverInterface *si2;
4030     int ad = 13579;
4031     {
4032       OsiSolverInterface *si1 = exmip1Si->clone();
4033       int ad = 13579;
4034       si1->setApplicationData(&ad);
4035       OSIUNITTEST_ASSERT_ERROR(*(static_cast< int * >(si1->getApplicationData())) == ad, {}, solverName, "storing application data");
4036       si2 = si1->clone();
4037       delete si1;
4038     }
4039     OSIUNITTEST_ASSERT_ERROR(*(static_cast< int * >(si2->getApplicationData())) == ad, {}, solverName, "cloning of application data");
4040 
4041     int nc = si2->getNumCols();
4042     int nr = si2->getNumRows();
4043     OSIUNITTEST_ASSERT_ERROR(nc == 8, return, *exmip1Si, "problem cloned: number of columns");
4044     OSIUNITTEST_ASSERT_ERROR(nr == 5, return, *exmip1Si, "problem cloned: number of rows");
4045 
4046     const char *exmip1Sirs = si2->getRowSense();
4047     OSIUNITTEST_ASSERT_ERROR(exmip1Sirs[0] == 'G', {}, solverName, "problem cloned: row sense");
4048     OSIUNITTEST_ASSERT_ERROR(exmip1Sirs[1] == 'L', {}, solverName, "problem cloned: row sense");
4049     OSIUNITTEST_ASSERT_ERROR(exmip1Sirs[2] == 'E', {}, solverName, "problem cloned: row sense");
4050     OSIUNITTEST_ASSERT_ERROR(exmip1Sirs[3] == 'R', {}, solverName, "problem cloned: row sense");
4051     OSIUNITTEST_ASSERT_ERROR(exmip1Sirs[4] == 'R', {}, solverName, "problem cloned: row sense");
4052 
4053     const double *exmip1Sirhs = si2->getRightHandSide();
4054     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirhs[0], 2.5), {}, solverName, "problem cloned: row rhs");
4055     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirhs[1], 2.1), {}, solverName, "problem cloned: row rhs");
4056     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirhs[2], 4.0), {}, solverName, "problem cloned: row rhs");
4057     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirhs[3], 5.0), {}, solverName, "problem cloned: row rhs");
4058     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirhs[4], 15.), {}, solverName, "problem cloned: row rhs");
4059 
4060     const double *exmip1Sirr = si2->getRowRange();
4061     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirr[0], 0.0), {}, solverName, "problem cloned: row range");
4062     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirr[1], 0.0), {}, solverName, "problem cloned: row range");
4063     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirr[2], 0.0), {}, solverName, "problem cloned: row range");
4064     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirr[3], 5.0 - 1.8), {}, solverName, "problem cloned: row range");
4065     OSIUNITTEST_ASSERT_ERROR(eq(exmip1Sirr[4], 15.0 - 3.0), {}, solverName, "problem cloned: row range");
4066 
4067     const CoinPackedMatrix *goldByCol = BuildExmip1Mtx();
4068     CoinPackedMatrix goldmtx;
4069     goldmtx.reverseOrderedCopyOf(*goldByCol);
4070     CoinPackedMatrix pm;
4071     pm.setExtraGap(0.0);
4072     pm.setExtraMajor(0.0);
4073     pm = *si2->getMatrixByRow();
4074     OSIUNITTEST_ASSERT_ERROR(goldmtx.isEquivalent(pm), {}, solverName, "problem cloned: matrix by row");
4075     delete goldByCol;
4076 
4077     const double *cl = si2->getColLower();
4078     OSIUNITTEST_ASSERT_ERROR(eq(cl[0], 2.5), {}, solverName, "problem cloned: columns lower bounds");
4079     OSIUNITTEST_ASSERT_ERROR(eq(cl[1], 0.0), {}, solverName, "problem cloned: columns lower bounds");
4080     OSIUNITTEST_ASSERT_ERROR(eq(cl[2], 0.0), {}, solverName, "problem cloned: columns lower bounds");
4081     OSIUNITTEST_ASSERT_ERROR(eq(cl[3], 0.0), {}, solverName, "problem cloned: columns lower bounds");
4082     OSIUNITTEST_ASSERT_ERROR(eq(cl[4], 0.5), {}, solverName, "problem cloned: columns lower bounds");
4083     OSIUNITTEST_ASSERT_ERROR(eq(cl[5], 0.0), {}, solverName, "problem cloned: columns lower bounds");
4084     OSIUNITTEST_ASSERT_ERROR(eq(cl[6], 0.0), {}, solverName, "problem cloned: columns lower bounds");
4085     OSIUNITTEST_ASSERT_ERROR(eq(cl[7], 0.0), {}, solverName, "problem cloned: columns lower bounds");
4086 
4087     const double *cu = si2->getColUpper();
4088     OSIUNITTEST_ASSERT_ERROR(eq(cu[0], exmip1Si->getInfinity()), {}, solverName, "problem cloned: columns upper bounds");
4089     OSIUNITTEST_ASSERT_ERROR(eq(cu[1], 4.1), {}, solverName, "problem cloned: columns upper bounds");
4090     OSIUNITTEST_ASSERT_ERROR(eq(cu[2], 1.0), {}, solverName, "problem cloned: columns upper bounds");
4091     OSIUNITTEST_ASSERT_ERROR(eq(cu[3], 1.0), {}, solverName, "problem cloned: columns upper bounds");
4092     OSIUNITTEST_ASSERT_ERROR(eq(cu[4], 4.0), {}, solverName, "problem cloned: columns upper bounds");
4093     OSIUNITTEST_ASSERT_ERROR(eq(cu[5], exmip1Si->getInfinity()), {}, solverName, "problem cloned: columns upper bounds");
4094     OSIUNITTEST_ASSERT_ERROR(eq(cu[6], exmip1Si->getInfinity()), {}, solverName, "problem cloned: columns upper bounds");
4095     OSIUNITTEST_ASSERT_ERROR(eq(cu[7], 4.3), {}, solverName, "problem cloned: columns upper bounds");
4096 
4097     const double *rl = si2->getRowLower();
4098     OSIUNITTEST_ASSERT_ERROR(eq(rl[0], 2.5), {}, solverName, "problem cloned: rows lower bounds");
4099     OSIUNITTEST_ASSERT_ERROR(eq(rl[1], -exmip1Si->getInfinity()), {}, solverName, "problem cloned: rows lower bounds");
4100     OSIUNITTEST_ASSERT_ERROR(eq(rl[2], 4.0), {}, solverName, "problem cloned: rows lower bounds");
4101     OSIUNITTEST_ASSERT_ERROR(eq(rl[3], 1.8), {}, solverName, "problem cloned: rows lower bounds");
4102     OSIUNITTEST_ASSERT_ERROR(eq(rl[4], 3.0), {}, solverName, "problem cloned: rows lower bounds");
4103 
4104     const double *ru = si2->getRowUpper();
4105     OSIUNITTEST_ASSERT_ERROR(eq(ru[0], exmip1Si->getInfinity()), {}, solverName, "problem cloned: rows upper bounds");
4106     OSIUNITTEST_ASSERT_ERROR(eq(ru[1], 2.1), {}, solverName, "problem cloned: rows upper bounds");
4107     OSIUNITTEST_ASSERT_ERROR(eq(ru[2], 4.0), {}, solverName, "problem cloned: rows upper bounds");
4108     OSIUNITTEST_ASSERT_ERROR(eq(ru[3], 5.0), {}, solverName, "problem cloned: rows upper bounds");
4109     OSIUNITTEST_ASSERT_ERROR(eq(ru[4], 15.), {}, solverName, "problem cloned: rows upper bounds");
4110 
4111     const double *objCoef = si2->getObjCoefficients();
4112     OSIUNITTEST_ASSERT_ERROR(eq(objCoef[0], 1.0), {}, solverName, "problem cloned: objective coefficients");
4113     OSIUNITTEST_ASSERT_ERROR(eq(objCoef[1], 0.0), {}, solverName, "problem cloned: objective coefficients");
4114     OSIUNITTEST_ASSERT_ERROR(eq(objCoef[2], 0.0), {}, solverName, "problem cloned: objective coefficients");
4115     OSIUNITTEST_ASSERT_ERROR(eq(objCoef[3], 0.0), {}, solverName, "problem cloned: objective coefficients");
4116     OSIUNITTEST_ASSERT_ERROR(eq(objCoef[4], 2.0), {}, solverName, "problem cloned: objective coefficients");
4117     OSIUNITTEST_ASSERT_ERROR(eq(objCoef[5], 0.0), {}, solverName, "problem cloned: objective coefficients");
4118     OSIUNITTEST_ASSERT_ERROR(eq(objCoef[6], 0.0), {}, solverName, "problem cloned: objective coefficients");
4119     OSIUNITTEST_ASSERT_ERROR(eq(objCoef[7], -1.0), {}, solverName, "problem cloned: objective coefficients");
4120 
4121     // Test getting of objective offset
4122     double objOffset;
4123     OSIUNITTEST_ASSERT_ERROR(si2->getDblParam(OsiObjOffset, objOffset), {}, solverName, "problem cloned: getDblParam(OsiObjOffset)");
4124     OSIUNITTEST_ASSERT_ERROR(eq(objOffset, 0.0), {}, solverName, "problem cloned: objective offset 0.0");
4125     delete si2;
4126   }
4127   // end of clone testing
4128 
4129   // Test apply cuts method
4130   {
4131     OsiSolverInterface &im = *(exmip1Si->clone());
4132     OsiCuts cuts;
4133 
4134     // Generate some cuts
4135     {
4136       // Get number of rows and columns in model
4137       int nr = im.getNumRows();
4138       int nc = im.getNumCols();
4139       OSIUNITTEST_ASSERT_ERROR(nr == 5, return, solverName, "apply cuts: number of rows");
4140       OSIUNITTEST_ASSERT_ERROR(nc == 8, return, solverName, "apply cuts: number of columns");
4141 
4142       // Generate a valid row cut from thin air
4143       int c;
4144       {
4145         int *inx = new int[nc];
4146         for (c = 0; c < nc; c++)
4147           inx[c] = c;
4148         double *el = new double[nc];
4149         for (c = 0; c < nc; c++)
4150           el[c] = (static_cast< double >(c)) * (static_cast< double >(c));
4151 
4152         OsiRowCut rc;
4153         rc.setRow(nc, inx, el);
4154         rc.setLb(-100.);
4155         rc.setUb(100.);
4156         rc.setEffectiveness(22);
4157 
4158         cuts.insert(rc);
4159         delete[] el;
4160         delete[] inx;
4161       }
4162 
4163       // Generate valid col cut from thin air
4164       {
4165         const double *oslColLB = im.getColLower();
4166         const double *oslColUB = im.getColUpper();
4167         int *inx = new int[nc];
4168         for (c = 0; c < nc; c++)
4169           inx[c] = c;
4170         double *lb = new double[nc];
4171         double *ub = new double[nc];
4172         for (c = 0; c < nc; c++)
4173           lb[c] = oslColLB[c] + 0.001;
4174         for (c = 0; c < nc; c++)
4175           ub[c] = oslColUB[c] - 0.001;
4176 
4177         OsiColCut cc;
4178         cc.setLbs(nc, inx, lb);
4179         cc.setUbs(nc, inx, ub);
4180 
4181         cuts.insert(cc);
4182         delete[] ub;
4183         delete[] lb;
4184         delete[] inx;
4185       }
4186 
4187       {
4188         // Generate a row and column cut which are ineffective
4189         OsiRowCut *rcP = new OsiRowCut;
4190         rcP->setEffectiveness(-1.);
4191         cuts.insert(rcP);
4192         OSIUNITTEST_ASSERT_ERROR(rcP == NULL, {}, solverName, "apply cuts: insert row cut keeps pointer");
4193 
4194         OsiColCut *ccP = new OsiColCut;
4195         ccP->setEffectiveness(-12.);
4196         cuts.insert(ccP);
4197         OSIUNITTEST_ASSERT_ERROR(ccP == NULL, {}, solverName, "apply cuts: insert column cut keeps pointer");
4198       }
4199       {
4200         //Generate inconsistent Row cut
4201         OsiRowCut rc;
4202         const int ne = 1;
4203         int inx[ne] = { -10 };
4204         double el[ne] = { 2.5 };
4205         rc.setRow(ne, inx, el);
4206         rc.setLb(3.);
4207         rc.setUb(4.);
4208         OSIUNITTEST_ASSERT_ERROR(!rc.consistent(), {}, solverName, "apply cuts: inconsistent row cut");
4209         cuts.insert(rc);
4210       }
4211       {
4212         //Generate inconsistent col cut
4213         OsiColCut cc;
4214         const int ne = 1;
4215         int inx[ne] = { -10 };
4216         double el[ne] = { 2.5 };
4217         cc.setUbs(ne, inx, el);
4218         OSIUNITTEST_ASSERT_ERROR(!cc.consistent(), {}, solverName, "apply cuts: inconsistent column cut");
4219         cuts.insert(cc);
4220       }
4221       {
4222         // Generate row cut which is inconsistent for model m
4223         OsiRowCut rc;
4224         const int ne = 1;
4225         int inx[ne] = { 10 };
4226         double el[ne] = { 2.5 };
4227         rc.setRow(ne, inx, el);
4228         OSIUNITTEST_ASSERT_ERROR(rc.consistent(), {}, solverName, "apply cuts: row cut inconsistent for model only");
4229         OSIUNITTEST_ASSERT_ERROR(!rc.consistent(im), {}, solverName, "apply cuts: row cut inconsistent for model only");
4230         cuts.insert(rc);
4231       }
4232       {
4233         // Generate col cut which is inconsistent for model m
4234         OsiColCut cc;
4235         const int ne = 1;
4236         int inx[ne] = { 30 };
4237         double el[ne] = { 2.0 };
4238         cc.setLbs(ne, inx, el);
4239         OSIUNITTEST_ASSERT_ERROR(cc.consistent(), {}, solverName, "apply cuts: column cut inconsistent for model only");
4240         OSIUNITTEST_ASSERT_ERROR(!cc.consistent(im), {}, solverName, "apply cuts: column cut inconsistent for model only");
4241         cuts.insert(cc);
4242       }
4243       {
4244         // Generate col cut which is infeasible
4245         OsiColCut cc;
4246         const int ne = 1;
4247         int inx[ne] = { 0 };
4248         double el[ne] = { 2.0 };
4249         cc.setUbs(ne, inx, el);
4250         cc.setEffectiveness(1000.);
4251         OSIUNITTEST_ASSERT_ERROR(cc.consistent(), {}, solverName, "apply cuts: column cut infeasible for model");
4252         OSIUNITTEST_ASSERT_ERROR(cc.consistent(im), {}, solverName, "apply cuts: column cut infeasible for model");
4253         OSIUNITTEST_ASSERT_ERROR(cc.infeasible(im), {}, solverName, "apply cuts: column cut infeasible for model");
4254         cuts.insert(cc);
4255       }
4256     }
4257     OSIUNITTEST_ASSERT_ERROR(cuts.sizeRowCuts() == 4, {}, solverName, "apply cuts: number of stored row cuts");
4258     OSIUNITTEST_ASSERT_ERROR(cuts.sizeColCuts() == 5, {}, solverName, "apply cuts: number of stored column cuts");
4259 
4260     {
4261       OsiSolverInterface::ApplyCutsReturnCode rc = im.applyCuts(cuts);
4262       OSIUNITTEST_ASSERT_ERROR(rc.getNumIneffective() == 2, {}, solverName, "apply cuts: number of row cuts found ineffective");
4263       OSIUNITTEST_ASSERT_ERROR(rc.getNumApplied() == 2, {}, solverName, "apply cuts: number of row cuts applied");
4264       OSIUNITTEST_ASSERT_ERROR(rc.getNumInfeasible() == 1, {}, solverName, "apply cuts: number of row cuts found infeasible");
4265       OSIUNITTEST_ASSERT_ERROR(rc.getNumInconsistentWrtIntegerModel() == 2, {}, solverName, "apply cuts: number of row cuts found inconsistent wr.t. integer model");
4266       OSIUNITTEST_ASSERT_ERROR(rc.getNumInconsistent() == 2, {}, solverName, "apply cuts: number of row cuts found inconsistent");
4267       OSIUNITTEST_ASSERT_ERROR(cuts.sizeCuts() == rc.getNumIneffective() + rc.getNumApplied() + rc.getNumInfeasible() + rc.getNumInconsistentWrtIntegerModel() + rc.getNumInconsistent(), {}, solverName, "apply cuts: consistent count of row cuts");
4268     }
4269 
4270     delete &im;
4271   }
4272   // end of apply cut method testing
4273 
4274   /*
4275   Test setting primal (column) and row (dual) solutions, and test that reduced
4276   cost and row activity match.
4277 
4278   GUROBI does not support setting solutions (only basis can be set), so we
4279   skip this test.
4280 
4281   This is a failure of the implementation of OsiGrb. Most solvers do not
4282   allow you to simply set a solution by giving primal values, it needs to be
4283   handled in the OsiXXX. That's what OsiGrb should do.  See longer rant where
4284   OsiGrb exposes Gurobi's inability to handle 'N' constraints. Here, run the
4285   tests and see the error messages. Shouldn't cause failure because we're not
4286   yet properly counting errors.
4287   -- lh, 100826 --
4288 */
4289   testSettingSolutions(*exmip1Si);
4290 
4291   // Test column type methods
4292   // skip for vol since it does not support this function
4293 
4294   if (volSolverInterface) {
4295     OSIUNITTEST_ADD_OUTCOME(solverName, "testing column type methods", "skipped test for OsiVol", TestOutcome::NOTE, true);
4296   } else {
4297     OsiSolverInterface &fim = *(emptySi->clone());
4298     std::string fn = mpsDir + "exmip1";
4299     fim.readMps(fn.c_str(), "mps");
4300 
4301     // exmip1.mps has 2 integer variables with index 2 & 3
4302     OSIUNITTEST_ASSERT_ERROR(fim.getNumIntegers() == 2, {}, solverName, "column type methods: number of integers");
4303 
4304     OSIUNITTEST_ASSERT_ERROR(fim.isContinuous(0), {}, solverName, "column type methods: isContinuous");
4305     OSIUNITTEST_ASSERT_ERROR(fim.isContinuous(1), {}, solverName, "column type methods: isContinuous");
4306     OSIUNITTEST_ASSERT_ERROR(!fim.isContinuous(2), {}, solverName, "column type methods: isContinuous");
4307     OSIUNITTEST_ASSERT_ERROR(!fim.isContinuous(3), {}, solverName, "column type methods: isContinuous");
4308     OSIUNITTEST_ASSERT_ERROR(fim.isContinuous(4), {}, solverName, "column type methods: isContinuous");
4309 
4310     OSIUNITTEST_ASSERT_ERROR(!fim.isInteger(0), {}, solverName, "column type methods: isInteger");
4311     OSIUNITTEST_ASSERT_ERROR(!fim.isInteger(1), {}, solverName, "column type methods: isInteger");
4312     OSIUNITTEST_ASSERT_ERROR(fim.isInteger(2), {}, solverName, "column type methods: isInteger");
4313     OSIUNITTEST_ASSERT_ERROR(fim.isInteger(3), {}, solverName, "column type methods: isInteger");
4314     OSIUNITTEST_ASSERT_ERROR(!fim.isInteger(4), {}, solverName, "column type methods: isInteger");
4315 
4316     OSIUNITTEST_ASSERT_ERROR(!fim.isBinary(0), {}, solverName, "column type methods: isBinary");
4317     OSIUNITTEST_ASSERT_ERROR(!fim.isBinary(1), {}, solverName, "column type methods: isBinary");
4318     OSIUNITTEST_ASSERT_ERROR(fim.isBinary(2), {}, solverName, "column type methods: isBinary");
4319     OSIUNITTEST_ASSERT_ERROR(fim.isBinary(3), {}, solverName, "column type methods: isBinary");
4320     OSIUNITTEST_ASSERT_ERROR(!fim.isBinary(4), {}, solverName, "column type methods: isBinary");
4321 
4322     OSIUNITTEST_ASSERT_ERROR(!fim.isIntegerNonBinary(0), {}, solverName, "column type methods: isIntegerNonBinary");
4323     OSIUNITTEST_ASSERT_ERROR(!fim.isIntegerNonBinary(1), {}, solverName, "column type methods: isIntegerNonBinary");
4324     OSIUNITTEST_ASSERT_ERROR(!fim.isIntegerNonBinary(2), {}, solverName, "column type methods: isIntegerNonBinary");
4325     OSIUNITTEST_ASSERT_ERROR(!fim.isIntegerNonBinary(3), {}, solverName, "column type methods: isIntegerNonBinary");
4326     OSIUNITTEST_ASSERT_ERROR(!fim.isIntegerNonBinary(4), {}, solverName, "column type methods: isIntegerNonBinary");
4327 
4328     // Test fractionalIndices
4329     do {
4330       double sol[] = { 1.0, 2.0, 2.9, 3.0, 4.0, 0.0, 0.0, 0.0 };
4331       fim.setColSolution(sol);
4332       OsiVectorInt fi = fim.getFractionalIndices(1e-5);
4333       OSIUNITTEST_ASSERT_ERROR(fi.size() == 1, break, solverName, "column type methods: getFractionalIndices");
4334       OSIUNITTEST_ASSERT_ERROR(fi[0] == 2, {}, solverName, "column type methods: getFractionalIndices");
4335 
4336       // Set integer variables very close to integer values
4337       sol[2] = 5 + .00001 / 2.;
4338       sol[3] = 8 - .00001 / 2.;
4339       fim.setColSolution(sol);
4340       fi = fim.getFractionalIndices(1e-5);
4341       OSIUNITTEST_ASSERT_ERROR(fi.size() == 0, {}, solverName, "column type methods: getFractionalIndices");
4342 
4343       // Set integer variables close, but beyond tolerances
4344       sol[2] = 5 + .00001 * 2.;
4345       sol[3] = 8 - .00001 * 2.;
4346       fim.setColSolution(sol);
4347       fi = fim.getFractionalIndices(1e-5);
4348       OSIUNITTEST_ASSERT_ERROR(fi.size() == 2, break, solverName, "column type methods: getFractionalIndices");
4349       OSIUNITTEST_ASSERT_ERROR(fi[0] == 2, {}, solverName, "column type methods: getFractionalIndices");
4350       OSIUNITTEST_ASSERT_ERROR(fi[1] == 3, {}, solverName, "column type methods: getFractionalIndices");
4351     } while (false);
4352 
4353     // Change data so column 2 & 3 are integerNonBinary
4354     fim.setColUpper(2, 5.0);
4355     fim.setColUpper(3, 6.0);
4356     OSIUNITTEST_ASSERT_ERROR(eq(fim.getColUpper()[2], 5.0), {}, solverName, "column type methods: convert binary to integer variable");
4357     OSIUNITTEST_ASSERT_ERROR(eq(fim.getColUpper()[3], 6.0), {}, solverName, "column type methods: convert binary to integer variable");
4358     OSIUNITTEST_ASSERT_ERROR(!fim.isBinary(0), {}, solverName, "column type methods: convert binary to integer variable");
4359     OSIUNITTEST_ASSERT_ERROR(!fim.isBinary(1), {}, solverName, "column type methods: convert binary to integer variable");
4360     OSIUNITTEST_ASSERT_ERROR(!fim.isBinary(2), {}, solverName, "column type methods: convert binary to integer variable");
4361     OSIUNITTEST_ASSERT_ERROR(!fim.isBinary(3), {}, solverName, "column type methods: convert binary to integer variable");
4362     OSIUNITTEST_ASSERT_ERROR(!fim.isBinary(4), {}, solverName, "column type methods: convert binary to integer variable");
4363     OSIUNITTEST_ASSERT_ERROR(fim.getNumIntegers() == 2, {}, solverName, "column type methods: convert binary to integer variable");
4364     OSIUNITTEST_ASSERT_ERROR(!fim.isIntegerNonBinary(0), {}, solverName, "column type methods: convert binary to integer variable");
4365     OSIUNITTEST_ASSERT_ERROR(!fim.isIntegerNonBinary(1), {}, solverName, "column type methods: convert binary to integer variable");
4366     OSIUNITTEST_ASSERT_ERROR(fim.isIntegerNonBinary(2), {}, solverName, "column type methods: convert binary to integer variable");
4367     OSIUNITTEST_ASSERT_ERROR(fim.isIntegerNonBinary(3), {}, solverName, "column type methods: convert binary to integer variable");
4368     OSIUNITTEST_ASSERT_ERROR(!fim.isIntegerNonBinary(4), {}, solverName, "column type methods: convert binary to integer variable");
4369 
4370     delete &fim;
4371   }
4372 
4373   /*
4374   Test load and assign methods, and do an initialSolve while we have the
4375   problem loaded. This routine also puts some stress on cloning --- it creates
4376   nine simultaneous clones of the OSI under test.
4377 */
4378   testLoadAndAssignProblem(emptySi, exmip1Si);
4379   testAddToEmptySystem(emptySi, volSolverInterface);
4380   /*
4381   Test write methods.
4382 */
4383   testWriteMps(emptySi, fn);
4384   testWriteLp(emptySi, fn);
4385   /*
4386   Test the simplex portion of the OSI interface.
4387 */
4388   testSimplexAPI(emptySi, mpsDir);
4389 
4390   // Add a Laci suggested test case
4391   // Load in a problem as column ordered matrix,
4392   // extract the row ordered copy,
4393   // add a row,
4394   // extract the row ordered copy again and test whether it's ok.
4395   // (the same can be done with reversing the role
4396   //  of row and column ordered.)
4397   {
4398     OsiSolverInterface *si = emptySi->clone();
4399 
4400     si->loadProblem(
4401       *(exmip1Si->getMatrixByCol()),
4402       exmip1Si->getColLower(),
4403       exmip1Si->getColUpper(),
4404       exmip1Si->getObjCoefficients(),
4405       exmip1Si->getRowSense(),
4406       exmip1Si->getRightHandSide(),
4407       exmip1Si->getRowRange());
4408 
4409     CoinPackedMatrix pm1 = *(si->getMatrixByRow());
4410 
4411     // Get a row of the matrix to make a cut
4412     const CoinShallowPackedVector neededBySunCC = exmip1Si->getMatrixByRow()->getVector(1);
4413     CoinPackedVector pv = neededBySunCC;
4414 
4415     pv.setElement(0, 3.14 * pv.getElements()[0]);
4416 
4417     OsiRowCut rc;
4418     rc.setRow(pv);
4419     rc.setLb(exmip1Si->getRowLower()[1] - 0.5);
4420     rc.setUb(exmip1Si->getRowUpper()[1] - 0.5);
4421 
4422     OsiCuts cuts;
4423     cuts.insert(rc);
4424 
4425     si->applyCuts(cuts);
4426 
4427     CoinPackedMatrix pm2 = *(si->getMatrixByRow());
4428 
4429     OSIUNITTEST_ASSERT_ERROR(pm1.getNumRows() == pm2.getNumRows() - 1, {}, solverName, "switching from column to row ordering: added row");
4430     int i;
4431     for (i = 0; i < pm1.getNumRows(); ++i) {
4432       const CoinShallowPackedVector neededBySunCC1 = pm1.getVector(i);
4433       const CoinShallowPackedVector neededBySunCC2 = pm2.getVector(i);
4434       if (neededBySunCC1 != neededBySunCC2)
4435         break;
4436     }
4437     OSIUNITTEST_ASSERT_ERROR(i == pm1.getNumRows(), {}, solverName, "switching from column to row ordering: matrix ok");
4438     OSIUNITTEST_ASSERT_ERROR(pm2.getVector(pm2.getNumRows() - 1).isEquivalent(pv), {}, solverName, "switching from column to row ordering: last row equals added cut");
4439 
4440     delete si;
4441   }
4442   {
4443     OsiSolverInterface *si = emptySi->clone();
4444 
4445     si->loadProblem(
4446       *(exmip1Si->getMatrixByRow()),
4447       exmip1Si->getColLower(),
4448       exmip1Si->getColUpper(),
4449       exmip1Si->getObjCoefficients(),
4450       exmip1Si->getRowLower(),
4451       exmip1Si->getRowUpper());
4452 
4453     CoinPackedMatrix pm1 = *(si->getMatrixByCol());
4454 
4455     // Get a row of the matrix to make a cut
4456     const CoinShallowPackedVector neededBySunCC = exmip1Si->getMatrixByRow()->getVector(1);
4457     CoinPackedVector pv = neededBySunCC;
4458     pv.setElement(0, 3.14 * pv.getElements()[0]);
4459 
4460     OsiRowCut rc;
4461     rc.setRow(pv);
4462     rc.setLb(exmip1Si->getRowLower()[1] - 0.5);
4463     rc.setUb(exmip1Si->getRowUpper()[1] - 0.5);
4464 
4465     OsiCuts cuts;
4466     cuts.insert(rc);
4467 
4468     si->applyCuts(cuts);
4469 
4470     CoinPackedMatrix pm2 = *(si->getMatrixByCol());
4471 
4472     OSIUNITTEST_ASSERT_ERROR(pm1.isColOrdered(), {}, solverName, "switching from row to column ordering");
4473     OSIUNITTEST_ASSERT_ERROR(pm2.isColOrdered(), {}, solverName, "switching from row to column ordering");
4474     OSIUNITTEST_ASSERT_ERROR(pm1.getNumRows() == pm2.getNumRows() - 1, {}, solverName, "switching from row to column ordering: added row");
4475 
4476     CoinPackedMatrix pm1ByRow;
4477     pm1ByRow.reverseOrderedCopyOf(pm1);
4478     CoinPackedMatrix pm2ByRow;
4479     pm2ByRow.reverseOrderedCopyOf(pm2);
4480 
4481     OSIUNITTEST_ASSERT_ERROR(!pm1ByRow.isColOrdered(), {}, solverName, "switching from row to column ordering");
4482     OSIUNITTEST_ASSERT_ERROR(!pm2ByRow.isColOrdered(), {}, solverName, "switching from row to column ordering");
4483     OSIUNITTEST_ASSERT_ERROR(pm1ByRow.getNumRows() == pm2ByRow.getNumRows() - 1, {}, solverName, "switching from row to column ordering");
4484     OSIUNITTEST_ASSERT_ERROR(pm1.getNumRows() == pm1ByRow.getNumRows(), {}, solverName, "switching from row to column ordering");
4485     OSIUNITTEST_ASSERT_ERROR(pm2.getNumRows() == pm2ByRow.getNumRows(), {}, solverName, "switching from row to column ordering");
4486 
4487     int i;
4488     for (i = 0; i < pm1ByRow.getNumRows(); ++i) {
4489       const CoinShallowPackedVector neededBySunCC1 = pm1ByRow.getVector(i);
4490       const CoinShallowPackedVector neededBySunCC2 = pm2ByRow.getVector(i);
4491       if (neededBySunCC1 != neededBySunCC2)
4492         break;
4493     }
4494     OSIUNITTEST_ASSERT_ERROR(i == pm1ByRow.getNumRows(), {}, solverName, "switching from row to column ordering: matrix ok");
4495     OSIUNITTEST_ASSERT_ERROR(pm2ByRow.getVector(pm2ByRow.getNumRows() - 1).isEquivalent(pv), {}, solverName, "switching from row to column ordering: last row is added cut");
4496 
4497     delete si;
4498   }
4499 
4500   delete exmip1Si;
4501 
4502   {
4503     // Testing parameter settings
4504     OsiSolverInterface *si = emptySi->clone();
4505     int i;
4506     int ival;
4507     double dval;
4508     bool hint;
4509     OsiHintStrength hintStrength;
4510     OSIUNITTEST_ASSERT_ERROR(si->getIntParam(OsiLastIntParam, ival) == false, {}, solverName, "parameter methods: retrieve last int param");
4511     OSIUNITTEST_ASSERT_ERROR(si->getDblParam(OsiLastDblParam, dval) == false, {}, solverName, "parameter methods: retrieve last double param");
4512     OSIUNITTEST_ASSERT_ERROR(si->getHintParam(OsiLastHintParam, hint) == false, {}, solverName, "parameter methods: retrieve last hint param");
4513     OSIUNITTEST_ASSERT_ERROR(si->setIntParam(OsiLastIntParam, 0) == false, {}, solverName, "parameter methods: set last int param");
4514     OSIUNITTEST_ASSERT_ERROR(si->setDblParam(OsiLastDblParam, 0.0) == false, {}, solverName, "parameter methods: set last double param");
4515     OSIUNITTEST_ASSERT_ERROR(si->setHintParam(OsiLastHintParam, false) == false, {}, solverName, "parameter methods: set last hint param");
4516 
4517     bool param_ok = true;
4518     for (i = 0; i < OsiLastIntParam; ++i) {
4519       const bool exists = si->getIntParam(static_cast< OsiIntParam >(i), ival);
4520       // existence and test should result in the same
4521       param_ok &= (!exists ^ testIntParam(si, i, -1));
4522       param_ok &= (!exists ^ testIntParam(si, i, 0));
4523       param_ok &= (!exists ^ testIntParam(si, i, 1));
4524       param_ok &= (!exists ^ testIntParam(si, i, 9999999));
4525       param_ok &= (!exists ^ testIntParam(si, i, COIN_INT_MAX));
4526       if (exists)
4527         param_ok &= (si->getIntParam(static_cast< OsiIntParam >(i), ival));
4528     }
4529     OSIUNITTEST_ASSERT_ERROR(param_ok == true, {}, solverName, "parameter methods: test intparam");
4530 
4531     param_ok = true;
4532     for (i = 0; i < OsiLastDblParam; ++i) {
4533       const bool exists = si->getDblParam(static_cast< OsiDblParam >(i), dval);
4534       // existence and test should result in the same
4535       param_ok &= (!exists ^ testDblParam(si, i, -1e50));
4536       param_ok &= (!exists ^ testDblParam(si, i, -1e10));
4537       param_ok &= (!exists ^ testDblParam(si, i, -1));
4538       param_ok &= (!exists ^ testDblParam(si, i, -1e-4));
4539       param_ok &= (!exists ^ testDblParam(si, i, -1e-15));
4540       param_ok &= (!exists ^ testDblParam(si, i, 1e50));
4541       param_ok &= (!exists ^ testDblParam(si, i, 1e10));
4542       param_ok &= (!exists ^ testDblParam(si, i, 1));
4543       param_ok &= (!exists ^ testDblParam(si, i, 1e-4));
4544       param_ok &= (!exists ^ testDblParam(si, i, 1e-15));
4545       if (exists)
4546         param_ok &= (si->setDblParam(static_cast< OsiDblParam >(i), dval));
4547     }
4548     OSIUNITTEST_ASSERT_ERROR(param_ok == true, {}, solverName, "parameter methods: test dblparam");
4549 
4550     // test hints --- see testHintParam for detailed explanation.
4551     {
4552       int throws = 0;
4553       param_ok = true;
4554       for (i = 0; i < OsiLastHintParam; ++i) {
4555         const bool exists = si->getHintParam(static_cast< OsiHintParam >(i), hint, hintStrength);
4556         param_ok &= (!exists ^ testHintParam(si, i, true, OsiHintIgnore, &throws));
4557         param_ok &= (!exists ^ testHintParam(si, i, true, OsiHintTry, &throws));
4558         param_ok &= (!exists ^ testHintParam(si, i, false, OsiHintTry, &throws));
4559         param_ok &= (!exists ^ testHintParam(si, i, true, OsiHintDo, &throws));
4560         param_ok &= (!exists ^ testHintParam(si, i, false, OsiHintDo, &throws));
4561         param_ok &= (!exists ^ testHintParam(si, i, true, OsiForceDo, &throws));
4562         param_ok &= (!exists ^ testHintParam(si, i, false, OsiForceDo, &throws));
4563       }
4564       std::cout.flush();
4565       std::cerr << "Checked " << static_cast< int >(OsiLastHintParam) << " hints x (true, false) at strength OsiForceDo; " << throws << " throws." << std::endl;
4566       OSIUNITTEST_ASSERT_ERROR(param_ok == true, {}, solverName, "parameter methods: test hintparam");
4567     }
4568 
4569     delete si;
4570   }
4571   /*
4572   A test to see if resolve gets the correct answer after changing the
4573   objective. Safe for Vol, as the result is checked by testing an interval on
4574   the primal solution.
4575 */
4576   changeObjAndResolve(emptySi);
4577   /*
4578   Test OsiPresolve. This is a `bolt on' presolve, distinct from any presolve
4579   that might be innate to the solver.
4580 */
4581   if (!volSolverInterface && !symSolverInterface) {
4582     testOsiPresolve(emptySi, mpsDir);
4583   } else {
4584     OSIUNITTEST_ADD_OUTCOME(solverName, "testOsiPresolved", "skipped test", OsiUnitTest::TestOutcome::NOTE, true);
4585   }
4586   /*
4587   Do a check to see if the solver returns the correct status for artificial
4588   variables. See the routine for detailed comments. Vol has no basis, hence no
4589   status.
4590 */
4591   if (!volSolverInterface && !symSolverInterface)
4592     testArtifStatus(emptySi);
4593   else
4594     OSIUNITTEST_ADD_OUTCOME(solverName, "testArtifStatus", "skipped test", OsiUnitTest::TestOutcome::NOTE, true);
4595 
4596   // Perform tests that are embodied in functions
4597   if (!volSolverInterface && !symSolverInterface) {
4598     typedef bool (*TestFunction)(OsiSolverInterface *);
4599     std::vector< std::pair< TestFunction, const char * > > test_functions;
4600     test_functions.push_back(std::pair< TestFunction, const char * >(&test1VivianDeSmedt, "test1VivianDeSmedt"));
4601     test_functions.push_back(std::pair< TestFunction, const char * >(&test2VivianDeSmedt, "test2VivianDeSmedt"));
4602     test_functions.push_back(std::pair< TestFunction, const char * >(&test3VivianDeSmedt, "test3VivianDeSmedt"));
4603     test_functions.push_back(std::pair< TestFunction, const char * >(&test4VivianDeSmedt, "test4VivianDeSmedt"));
4604     test_functions.push_back(std::pair< TestFunction, const char * >(&test5VivianDeSmedt, "test5VivianDeSmedt"));
4605     test_functions.push_back(std::pair< TestFunction, const char * >(&test6VivianDeSmedt, "test6VivianDeSmedt"));
4606     test_functions.push_back(std::pair< TestFunction, const char * >(&test7VivianDeSmedt, "test7VivianDeSmedt"));
4607     test_functions.push_back(std::pair< TestFunction, const char * >(&test8VivianDeSmedt, "test8VivianDeSmedt"));
4608     test_functions.push_back(std::pair< TestFunction, const char * >(&test9VivianDeSmedt, "test9VivianDeSmedt"));
4609     test_functions.push_back(std::pair< TestFunction, const char * >(&test10VivianDeSmedt, "test10VivianDeSmedt"));
4610     test_functions.push_back(std::pair< TestFunction, const char * >(&test11VivianDeSmedt, "test11VivianDeSmedt"));
4611     test_functions.push_back(std::pair< TestFunction, const char * >(&test12VivianDeSmedt, "test12VivianDeSmedt"));
4612     test_functions.push_back(std::pair< TestFunction, const char * >(&test13VivianDeSmedt, "test13VivianDeSmedt"));
4613     test_functions.push_back(std::pair< TestFunction, const char * >(&test14VivianDeSmedt, "test14VivianDeSmedt"));
4614     test_functions.push_back(std::pair< TestFunction, const char * >(&test15VivianDeSmedt, "test15VivianDeSmedt"));
4615     test_functions.push_back(std::pair< TestFunction, const char * >(&test16SebastianNowozin, "test16SebastianNowozin"));
4616     test_functions.push_back(std::pair< TestFunction, const char * >(&test17SebastianNowozin, "test17SebastianNowozin"));
4617 
4618     unsigned int i;
4619     for (i = 0; i < test_functions.size(); ++i) {
4620       OsiSolverInterface *s = emptySi->clone();
4621       const char *testName = test_functions[i].second;
4622       {
4623         bool test = test_functions[i].first(s);
4624         OSIUNITTEST_ASSERT_ERROR(test == true, {}, solverName, testName);
4625       }
4626       delete s;
4627     }
4628   } else
4629     OSIUNITTEST_ADD_OUTCOME(solverName, "test*VivianDeSmedt and test*SebastianNowozin", "skipped test", OsiUnitTest::TestOutcome::NOTE, true);
4630   /*
4631   Test duals and reduced costs, then dual rays. Vol doesn't react well to
4632   either test.
4633 */
4634   if (!volSolverInterface && !symSolverInterface) {
4635     testReducedCosts(emptySi, mpsDir);
4636     testDualRays(emptySi, mpsDir);
4637   } else {
4638     OSIUNITTEST_ADD_OUTCOME(solverName, "testReducedCosts", "skipped test", OsiUnitTest::TestOutcome::NOTE, true);
4639     OSIUNITTEST_ADD_OUTCOME(solverName, "testDualRays", "skipped test", OsiUnitTest::TestOutcome::NOTE, true);
4640   }
4641 }
4642 
4643 /*
4644     Orphan comment? If anyone happens to poke at the code that this belongs
4645     to, move it. My (lh) guess is it should go somewhere in the deSmedt tests.
4646     I just haven't made time to check them all.
4647 
4648     And I really do want to find this. It'd be a great test case for the dual
4649     ray routine.
4650 
4651     With this matrix we have a primal/dual infeas problem. Leaving the first
4652     row makes it primal feas, leaving the first col makes it dual feas.
4653     All vars are >= 0
4654 
4655     obj: -1  2 -3  4 -5 (min)
4656 
4657           0 -1  0  0 -2  >=  1
4658           1  0 -3  0  4  >= -2
4659           0  3  0 -5  0  >=  3
4660           0  0  5  0 -6  >= -4
4661           2 -4  0  6  0  >=  5
4662   */
4663 
4664 /* vi: softtabstop=2 shiftwidth=2 expandtab tabstop=2
4665 */
4666