/* Copyright (C) 2000 -- 2010, Lou Hafer, International Business Machines, and others. All Rights Reserved. This code is licensed under the terms of the Eclipse Public License (EPL). */ #include "CoinPragma.hpp" #include "OsiUnitTests.hpp" #include "OsiConfig.h" #include "CoinTime.hpp" #include "CoinFloatEqual.hpp" /* #include #include #include #include #include #include #include */ #include "OsiSolverInterface.hpp" /*! \brief Run solvers on NetLib problems. The routine creates a vector of NetLib problems (problem name, objective, various other characteristics), and a vector of solvers to be tested. Each solver is run on each problem. The run is deemed successful if the solver reports the correct problem size after loading and returns the correct objective value after optimization. If multiple solvers are available, the results are compared pairwise against the results reported by adjacent solvers in the solver vector. Due to limitations of the volume solver, it must be the last solver in vecEmptySiP. */ void OsiSolverInterfaceMpsUnitTest(const std::vector< OsiSolverInterface * > &vecEmptySiP, const std::string &mpsDir) { int i; unsigned int m; /* Vectors to hold test problem names and characteristics. The objective value after optimization (objValue) must agree to the specified tolerance (objValueTol). */ std::vector< std::string > mpsName; std::vector< bool > minObj; std::vector< int > nRows; std::vector< int > nCols; std::vector< double > objValue; std::vector< double > objValueTol; /* And a macro to make the vector creation marginally readable. */ #define PUSH_MPS(zz_mpsName_zz, zz_minObj_zz, \ zz_nRows_zz, zz_nCols_zz, zz_objValue_zz, zz_objValueTol_zz) \ mpsName.push_back(zz_mpsName_zz); \ minObj.push_back(zz_minObj_zz); \ nRows.push_back(zz_nRows_zz); \ nCols.push_back(zz_nCols_zz); \ objValueTol.push_back(zz_objValueTol_zz); \ objValue.push_back(zz_objValue_zz); /* Load up the problem vector. Note that the row counts here include the objective function. */ PUSH_MPS("25fv47", true, 822, 1571, 5.5018458883E+03, 1.0e-10) PUSH_MPS("80bau3b", true, 2263, 9799, 9.8722419241E+05, 1.e-10) PUSH_MPS("adlittle", true, 57, 97, 2.2549496316e+05, 1.e-10) PUSH_MPS("afiro", true, 28, 32, -4.6475314286e+02, 1.e-10) PUSH_MPS("agg", true, 489, 163, -3.5991767287e+07, 1.e-10) PUSH_MPS("agg2", true, 517, 302, -2.0239252356e+07, 1.e-10) PUSH_MPS("agg3", true, 517, 302, 1.0312115935e+07, 1.e-10) PUSH_MPS("bandm", true, 306, 472, -1.5862801845e+02, 1.e-10) PUSH_MPS("beaconfd", true, 174, 262, 3.3592485807e+04, 1.e-10) PUSH_MPS("blend", true, 75, 83, -3.0812149846e+01, 1.e-10) PUSH_MPS("bnl1", true, 644, 1175, 1.9776295615E+03, 1.e-10) PUSH_MPS("bnl2", true, 2325, 3489, 1.8112365404e+03, 1.e-10) PUSH_MPS("boeing1", true, /*351*/ 352, 384, -3.3521356751e+02, 1.e-10) PUSH_MPS("boeing2", true, 167, 143, -3.1501872802e+02, 1.e-10) PUSH_MPS("bore3d", true, 234, 315, 1.3730803942e+03, 1.e-10) PUSH_MPS("brandy", true, 221, 249, 1.5185098965e+03, 1.e-10) PUSH_MPS("capri", true, 272, 353, 2.6900129138e+03, 1.e-10) PUSH_MPS("cycle", true, 1904, 2857, -5.2263930249e+00, 1.e-9) PUSH_MPS("czprob", true, 930, 3523, 2.1851966989e+06, 1.e-10) PUSH_MPS("d2q06c", true, 2172, 5167, 122784.21557456, 1.e-7) PUSH_MPS("d6cube", true, 416, 6184, 3.1549166667e+02, 1.e-8) PUSH_MPS("degen2", true, 445, 534, -1.4351780000e+03, 1.e-10) PUSH_MPS("degen3", true, 1504, 1818, -9.8729400000e+02, 1.e-10) PUSH_MPS("dfl001", true, 6072, 12230, 1.1266396047E+07, 1.e-5) PUSH_MPS("e226", true, 224, 282, (-18.751929066 + 7.113), 1.e-10) // NOTE: Objective function has constant of 7.113 PUSH_MPS("etamacro", true, 401, 688, -7.5571521774e+02, 1.e-6) PUSH_MPS("fffff800", true, 525, 854, 5.5567961165e+05, 1.e-6) PUSH_MPS("finnis", true, 498, 614, 1.7279096547e+05, 1.e-6) PUSH_MPS("fit1d", true, 25, 1026, -9.1463780924e+03, 1.e-10) PUSH_MPS("fit1p", true, 628, 1677, 9.1463780924e+03, 1.e-10) PUSH_MPS("fit2d", true, 26, 10500, -6.8464293294e+04, 1.e-10) PUSH_MPS("fit2p", true, 3001, 13525, 6.8464293232e+04, 1.e-9) PUSH_MPS("forplan", true, 162, 421, -6.6421873953e+02, 1.e-6) PUSH_MPS("ganges", true, 1310, 1681, -1.0958636356e+05, 1.e-5) PUSH_MPS("gfrd-pnc", true, 617, 1092, 6.9022359995e+06, 1.e-10) PUSH_MPS("greenbea", true, 2393, 5405, /*-7.2462405908e+07*/ -72555248.129846, 1.e-10) PUSH_MPS("greenbeb", true, 2393, 5405, /*-4.3021476065e+06*/ -4302260.2612066, 1.e-10) PUSH_MPS("grow15", true, 301, 645, -1.0687094129e+08, 1.e-10) PUSH_MPS("grow22", true, 441, 946, -1.6083433648e+08, 1.e-10) PUSH_MPS("grow7", true, 141, 301, -4.7787811815e+07, 1.e-10) PUSH_MPS("israel", true, 175, 142, -8.9664482186e+05, 1.e-10) PUSH_MPS("kb2", true, 44, 41, -1.7499001299e+03, 1.e-10) PUSH_MPS("lotfi", true, 154, 308, -2.5264706062e+01, 1.e-10) PUSH_MPS("maros", true, 847, 1443, -5.8063743701e+04, 1.e-10) PUSH_MPS("maros-r7", true, 3137, 9408, 1.4971851665e+06, 1.e-10) PUSH_MPS("modszk1", true, 688, 1620, 3.2061972906e+02, 1.e-10) PUSH_MPS("nesm", true, 663, 2923, 1.4076073035e+07, 1.e-5) PUSH_MPS("perold", true, 626, 1376, -9.3807580773e+03, 1.e-6) PUSH_MPS("pilot", true, 1442, 3652, /*-5.5740430007e+02*/ -557.48972927292, 5.e-5) PUSH_MPS("pilot4", true, 411, 1000, -2.5811392641e+03, 1.e-6) PUSH_MPS("pilot87", true, 2031, 4883, 3.0171072827e+02, 1.e-4) PUSH_MPS("pilotnov", true, 976, 2172, -4.4972761882e+03, 1.e-10) // ?? PUSH_MPS("qap8",true,913,1632,2.0350000000e+02,1.e-10) // ?? PUSH_MPS("qap12",true,3193,8856,5.2289435056e+02,1.e-10) // ?? PUSH_MPS("qap15",true,6331,22275,1.0409940410e+03,1.e-10) PUSH_MPS("recipe", true, 92, 180, -2.6661600000e+02, 1.e-10) PUSH_MPS("sc105", true, 106, 103, -5.2202061212e+01, 1.e-10) PUSH_MPS("sc205", true, 206, 203, -5.2202061212e+01, 1.e-10) PUSH_MPS("sc50a", true, 51, 48, -6.4575077059e+01, 1.e-10) PUSH_MPS("sc50b", true, 51, 48, -7.0000000000e+01, 1.e-10) PUSH_MPS("scagr25", true, 472, 500, -1.4753433061e+07, 1.e-10) PUSH_MPS("scagr7", true, 130, 140, -2.3313892548e+06, 1.e-6) PUSH_MPS("scfxm1", true, 331, 457, 1.8416759028e+04, 1.e-10) PUSH_MPS("scfxm2", true, 661, 914, 3.6660261565e+04, 1.e-10) PUSH_MPS("scfxm3", true, 991, 1371, 5.4901254550e+04, 1.e-10) PUSH_MPS("scorpion", true, 389, 358, 1.8781248227e+03, 1.e-10) PUSH_MPS("scrs8", true, 491, 1169, 9.0429998619e+02, 1.e-5) PUSH_MPS("scsd1", true, 78, 760, 8.6666666743e+00, 1.e-10) PUSH_MPS("scsd6", true, 148, 1350, 5.0500000078e+01, 1.e-10) PUSH_MPS("scsd8", true, 398, 2750, 9.0499999993e+02, 1.e-8) PUSH_MPS("sctap1", true, 301, 480, 1.4122500000e+03, 1.e-10) PUSH_MPS("sctap2", true, 1091, 1880, 1.7248071429e+03, 1.e-10) PUSH_MPS("sctap3", true, 1481, 2480, 1.4240000000e+03, 1.e-10) PUSH_MPS("seba", true, 516, 1028, 1.5711600000e+04, 1.e-10) PUSH_MPS("share1b", true, 118, 225, -7.6589318579e+04, 1.e-10) PUSH_MPS("share2b", true, 97, 79, -4.1573224074e+02, 1.e-10) PUSH_MPS("shell", true, 537, 1775, 1.2088253460e+09, 1.e-10) PUSH_MPS("ship04l", true, 403, 2118, 1.7933245380e+06, 1.e-10) PUSH_MPS("ship04s", true, 403, 1458, 1.7987147004e+06, 1.e-10) PUSH_MPS("ship08l", true, 779, 4283, 1.9090552114e+06, 1.e-10) PUSH_MPS("ship08s", true, 779, 2387, 1.9200982105e+06, 1.e-10) PUSH_MPS("ship12l", true, 1152, 5427, 1.4701879193e+06, 1.e-10) PUSH_MPS("ship12s", true, 1152, 2763, 1.4892361344e+06, 1.e-10) PUSH_MPS("sierra", true, 1228, 2036, 1.5394362184e+07, 1.e-10) PUSH_MPS("stair", true, 357, 467, -2.5126695119e+02, 1.e-10) PUSH_MPS("standata", true, 360, 1075, 1.2576995000e+03, 1.e-10) // GUB PUSH_MPS("standgub",true,362,1184,1257.6995,1.e-10) PUSH_MPS("standmps", true, 468, 1075, 1.4060175000E+03, 1.e-10) PUSH_MPS("stocfor1", true, 118, 111, -4.1131976219E+04, 1.e-10) PUSH_MPS("stocfor2", true, 2158, 2031, -3.9024408538e+04, 1.e-10) // ?? PUSH_MPS("stocfor3",true,16676,15695,-3.9976661576e+04,1.e-10) // ?? PUSH_MPS("truss",true,1001,8806,4.5881584719e+05,1.e-10) PUSH_MPS("tuff", true, 334, 587, 2.9214776509e-01, 1.e-10) PUSH_MPS("vtpbase", true, 199, 203, 1.2983146246e+05, 1.e-10) PUSH_MPS("wood1p", true, 245, 2594, 1.4429024116e+00, 5.e-5) PUSH_MPS("woodw", true, 1099, 8405, 1.3044763331E+00, 1.e-10) #undef PUSH_MPS const unsigned int numProblems = static_cast< unsigned int >(mpsName.size()); /* Create vectors to hold solver interfaces, the name of each solver interface, the current state of processing, and statistics about the number of problems solved and the time taken. */ const int numSolvers = static_cast< int >(vecEmptySiP.size()); std::vector< OsiSolverInterface * > vecSiP(numSolvers); std::vector< std::string > siName(numSolvers); std::vector< int > siStage(numSolvers); std::vector< int > numProbSolved(numSolvers); std::vector< double > timeTaken(numSolvers); for (i = 0; i < numSolvers; i++) { siName[i] = "unknown"; numProbSolved[i] = 0; timeTaken[i] = 0.0; } /* For each problem, create a fresh clone of the `empty' solvers from vecEmptySiP, then proceed in stages: read the MPS file, solve the problem, check the solution. If there are multiple solvers in vecSiP, the results of each solver are compared with its neighbors in the vector. */ for (m = 0; m < numProblems; m++) { std::cout << " processing mps file: " << mpsName[m] << " (" << m + 1 << " out of " << numProblems << ")" << std::endl; /* Stage 0: Create fresh solver clones. */ int solversReadMpsFile = 0; for (i = numSolvers - 1; i >= 0; --i) { vecSiP[i] = vecEmptySiP[i]->clone(); vecSiP[i]->getStrParam(OsiSolverName, siName[i]); siStage[i] = 0; } /* Stage 1: Read the MPS file into each solver interface. As a basic check, make sure the size of the constraint matrix is correct. */ for (i = 0; i < numSolvers; i++) { std::string fn = mpsDir + mpsName[m]; if (vecSiP[i]->readMps(fn.c_str(), "mps") != 0) OSIUNITTEST_ADD_OUTCOME(*vecSiP[i], "netlib " + mpsName[m], "reading MPS file failed", OsiUnitTest::TestOutcome::ERROR, false); if (minObj[m]) vecSiP[i]->setObjSense(1.0); else vecSiP[i]->setObjSense(-1.0); int nr = vecSiP[i]->getNumRows(); int nc = vecSiP[i]->getNumCols(); if (nr == nRows[m] - 1 && nc == nCols[m]) { siStage[i] = 1; solversReadMpsFile++; } else OSIUNITTEST_ADD_OUTCOME(*vecSiP[i], "netlib " + mpsName[m], "number of rows and columns wrong", OsiUnitTest::TestOutcome::ERROR, false); } /* If more than one solver succeeded, compare representations. */ if (solversReadMpsFile > 0) { // Find an initial pair to compare int s1; for (s1 = 0; s1 < numSolvers - 1 && siStage[s1] < 1; s1++) ; int s2; for (s2 = s1 + 1; s2 < numSolvers && siStage[s2] < 1; s2++) ; while (s2 < numSolvers) { std::cout << " comparing problem representation for " << siName[s1] << " and " << siName[s2] << " ..."; if (OsiUnitTest::compareProblems(vecSiP[s1], vecSiP[s2])) std::cout << " ok." << std::endl; s1 = s2; for (s2++; s2 < numSolvers && siStage[s2] < 1; s2++) ; } } /* Stage 2: Ask each solver that successfully read the problem to solve it, then check the return code and objective. */ for (i = 0; i < numSolvers; ++i) { if (siStage[i] < 1) continue; double startTime = CoinCpuTime(); OSIUNITTEST_CATCH_ERROR(vecSiP[i]->initialSolve(), continue, *vecSiP[i], "netlib " + mpsName[m]); double timeOfSolution = CoinCpuTime() - startTime; OSIUNITTEST_ASSERT_ERROR(vecSiP[i]->isProvenOptimal(), {}, *vecSiP[i], "netlib " + mpsName[m]); if (vecSiP[i]->isProvenOptimal()) { double soln = vecSiP[i]->getObjValue(); CoinRelFltEq eq(objValueTol[m]); OSIUNITTEST_ASSERT_ERROR(eq(soln, objValue[m]), std::cerr << soln << " != " << objValue[m] << "; error = " << fabs(objValue[m] - soln), *vecSiP[i], "netlib " + mpsName[m]); if (eq(soln, objValue[m])) { std::cout << " " << siName[i] << " " << soln << " = " << objValue[m] << ", " << vecSiP[i]->getIterationCount() << " iters; okay"; numProbSolved[i]++; } std::cout << " - took " << timeOfSolution << " seconds." << std::endl; timeTaken[i] += timeOfSolution; } else { std::cout.flush(); std::cerr << " " << siName[i] << "; error "; if (vecSiP[i]->isProvenPrimalInfeasible()) std::cerr << "primal infeasible"; else if (vecSiP[i]->isIterationLimitReached()) std::cerr << "iteration limit"; else if (vecSiP[i]->isAbandoned()) std::cerr << "abandoned"; else std::cerr << "unknown"; } } /* Delete the used solver interfaces so we can reload fresh clones for the next problem. */ for (i = 0; i < numSolvers; i++) delete vecSiP[i]; } /* Print a summary for each solver. */ for (i = 0; i < numSolvers; i++) { std::cout << siName[i] << " solved " << numProbSolved[i] << " out of " << numProblems << " and took " << timeTaken[i] << " seconds." << std::endl; } } /* vi: softtabstop=2 shiftwidth=2 expandtab tabstop=2 */