You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

285 lines
15 KiB

  1. #include "gtest/gtest.h"
  2. #include "storm-config.h"
  3. #include "src/solver/GurobiLpSolver.h"
  4. #include "src/exceptions/InvalidStateException.h"
  5. #include "src/settings/Settings.h"
  6. TEST(GurobiLpSolver, LPOptimizeMax) {
  7. #ifdef STORM_HAVE_GUROBI
  8. storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
  9. uint_fast64_t xIndex;
  10. ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1));
  11. uint_fast64_t yIndex;
  12. ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
  13. uint_fast64_t zIndex;
  14. ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
  15. ASSERT_NO_THROW(solver.update());
  16. ASSERT_NO_THROW(solver.addConstraint("a", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
  17. ASSERT_NO_THROW(solver.addConstraint("b", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5));
  18. ASSERT_NO_THROW(solver.addConstraint("c", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
  19. ASSERT_NO_THROW(solver.optimize());
  20. ASSERT_TRUE(solver.isOptimal());
  21. ASSERT_FALSE(solver.isUnbounded());
  22. ASSERT_FALSE(solver.isInfeasible());
  23. double xValue = 0;
  24. ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex));
  25. ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
  26. double yValue = 0;
  27. ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex));
  28. ASSERT_LT(std::abs(yValue - 6.5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
  29. double zValue = 0;
  30. ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex));
  31. ASSERT_LT(std::abs(zValue - 2.75), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
  32. double objectiveValue = 0;
  33. ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue());
  34. ASSERT_LT(std::abs(objectiveValue - 14.75), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
  35. #else
  36. ASSERT_TRUE(false) << "StoRM built without Gurobi support.";
  37. #endif
  38. }
  39. TEST(GurobiLpSolver, LPOptimizeMin) {
  40. #ifdef STORM_HAVE_GUROBI
  41. storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MINIMIZE);
  42. uint_fast64_t xIndex;
  43. ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1));
  44. uint_fast64_t yIndex;
  45. ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
  46. uint_fast64_t zIndex;
  47. ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 1, 5.7, -1));
  48. ASSERT_NO_THROW(solver.update());
  49. ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
  50. ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5));
  51. ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
  52. ASSERT_NO_THROW(solver.optimize());
  53. ASSERT_TRUE(solver.isOptimal());
  54. ASSERT_FALSE(solver.isUnbounded());
  55. ASSERT_FALSE(solver.isInfeasible());
  56. double xValue = 0;
  57. ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex));
  58. ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
  59. double yValue = 0;
  60. ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex));
  61. ASSERT_LT(std::abs(yValue - 0), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
  62. double zValue = 0;
  63. ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex));
  64. ASSERT_LT(std::abs(zValue - 5.7), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
  65. double objectiveValue = 0;
  66. ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue());
  67. ASSERT_LT(std::abs(objectiveValue - (-6.7)), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
  68. #else
  69. ASSERT_TRUE(false) << "StoRM built without Gurobi support.";
  70. #endif
  71. }
  72. TEST(GurobiLpSolver, MILPOptimizeMax) {
  73. #ifdef STORM_HAVE_GUROBI
  74. storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
  75. uint_fast64_t xIndex;
  76. ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
  77. uint_fast64_t yIndex;
  78. ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
  79. uint_fast64_t zIndex;
  80. ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
  81. ASSERT_NO_THROW(solver.update());
  82. ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
  83. ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5));
  84. ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
  85. ASSERT_NO_THROW(solver.optimize());
  86. ASSERT_TRUE(solver.isOptimal());
  87. ASSERT_FALSE(solver.isUnbounded());
  88. ASSERT_FALSE(solver.isInfeasible());
  89. bool xValue = false;
  90. ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex));
  91. ASSERT_EQ(true, xValue);
  92. int_fast64_t yValue = 0;
  93. ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex));
  94. ASSERT_EQ(6, yValue);
  95. double zValue = 0;
  96. ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex));
  97. ASSERT_LT(std::abs(zValue - 3), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
  98. double objectiveValue = 0;
  99. ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue());
  100. ASSERT_LT(std::abs(objectiveValue - 14), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
  101. #else
  102. ASSERT_TRUE(false) << "StoRM built without Gurobi support.";
  103. #endif
  104. }
  105. TEST(GurobiLpSolver, MILPOptimizeMin) {
  106. #ifdef STORM_HAVE_GUROBI
  107. storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MINIMIZE);
  108. uint_fast64_t xIndex;
  109. ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
  110. uint_fast64_t yIndex;
  111. ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
  112. uint_fast64_t zIndex;
  113. ASSERT_NO_THROW(zIndex = solver.createIntegerVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 0, 5.7, -1));
  114. ASSERT_NO_THROW(solver.update());
  115. ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
  116. ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5));
  117. ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
  118. ASSERT_NO_THROW(solver.optimize());
  119. ASSERT_TRUE(solver.isOptimal());
  120. ASSERT_FALSE(solver.isUnbounded());
  121. ASSERT_FALSE(solver.isInfeasible());
  122. bool xValue = false;
  123. ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex));
  124. ASSERT_EQ(true, xValue);
  125. int_fast64_t yValue = 0;
  126. ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex));
  127. ASSERT_EQ(0, yValue);
  128. double zValue = 0;
  129. ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex));
  130. ASSERT_LT(std::abs(zValue - 5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
  131. double objectiveValue = 0;
  132. ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue());
  133. ASSERT_LT(std::abs(objectiveValue - (-6)), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
  134. #else
  135. ASSERT_TRUE(false) << "StoRM built without Gurobi support.";
  136. #endif
  137. }
  138. TEST(GurobiLpSolver, LPInfeasible) {
  139. #ifdef STORM_HAVE_GUROBI
  140. storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
  141. uint_fast64_t xIndex;
  142. ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1));
  143. uint_fast64_t yIndex;
  144. ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
  145. uint_fast64_t zIndex;
  146. ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
  147. ASSERT_NO_THROW(solver.update());
  148. ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
  149. ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5));
  150. ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
  151. ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7));
  152. ASSERT_NO_THROW(solver.optimize());
  153. ASSERT_FALSE(solver.isOptimal());
  154. ASSERT_FALSE(solver.isUnbounded());
  155. ASSERT_TRUE(solver.isInfeasible());
  156. double xValue = 0;
  157. ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException);
  158. double yValue = 0;
  159. ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException);
  160. double zValue = 0;
  161. ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException);
  162. double objectiveValue = 0;
  163. ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException);
  164. #else
  165. ASSERT_TRUE(false) << "StoRM built without Gurobi support.";
  166. #endif
  167. }
  168. TEST(GurobiLpSolver, MILPInfeasible) {
  169. #ifdef STORM_HAVE_GUROBI
  170. storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
  171. uint_fast64_t xIndex;
  172. ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
  173. uint_fast64_t yIndex;
  174. ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
  175. uint_fast64_t zIndex;
  176. ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
  177. ASSERT_NO_THROW(solver.update());
  178. ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
  179. ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5));
  180. ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
  181. ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7));
  182. ASSERT_NO_THROW(solver.optimize());
  183. ASSERT_FALSE(solver.isOptimal());
  184. ASSERT_FALSE(solver.isUnbounded());
  185. ASSERT_TRUE(solver.isInfeasible());
  186. bool xValue = false;
  187. ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException);
  188. int_fast64_t yValue = 0;
  189. ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException);
  190. double zValue = 0;
  191. ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException);
  192. double objectiveValue = 0;
  193. ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException);
  194. #else
  195. ASSERT_TRUE(false) << "StoRM built without Gurobi support.";
  196. #endif
  197. }
  198. TEST(GurobiLpSolver, LPUnbounded) {
  199. #ifdef STORM_HAVE_GUROBI
  200. storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
  201. uint_fast64_t xIndex;
  202. ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1));
  203. uint_fast64_t yIndex;
  204. ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
  205. uint_fast64_t zIndex;
  206. ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
  207. ASSERT_NO_THROW(solver.update());
  208. ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
  209. ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
  210. ASSERT_NO_THROW(solver.optimize());
  211. ASSERT_FALSE(solver.isOptimal());
  212. ASSERT_TRUE(solver.isUnbounded());
  213. ASSERT_FALSE(solver.isInfeasible());
  214. double xValue = 0;
  215. ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException);
  216. double yValue = 0;
  217. ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException);
  218. double zValue = 0;
  219. ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException);
  220. double objectiveValue = 0;
  221. ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException);
  222. #else
  223. ASSERT_TRUE(false) << "StoRM built without Gurobi support.";
  224. #endif
  225. }
  226. TEST(GurobiLpSolver, MILPUnbounded) {
  227. #ifdef STORM_HAVE_GUROBI
  228. storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
  229. uint_fast64_t xIndex;
  230. ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
  231. uint_fast64_t yIndex;
  232. ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
  233. uint_fast64_t zIndex;
  234. ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
  235. ASSERT_NO_THROW(solver.update());
  236. ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
  237. ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
  238. ASSERT_NO_THROW(solver.optimize());
  239. ASSERT_FALSE(solver.isOptimal());
  240. ASSERT_TRUE(solver.isUnbounded());
  241. ASSERT_FALSE(solver.isInfeasible());
  242. bool xValue = false;
  243. ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException);
  244. int_fast64_t yValue = 0;
  245. ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException);
  246. double zValue = 0;
  247. ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException);
  248. double objectiveValue = 0;
  249. ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException);
  250. #else
  251. ASSERT_TRUE(false) << "StoRM built without Gurobi support.";
  252. #endif
  253. }