1#include "storm-config.h"
14class DoubleViEnvironment {
16 typedef double ValueType;
17 static const bool isExact =
false;
18 static const bool reportsBounds =
false;
19 static storm::Environment createEnvironment() {
20 storm::Environment env;
27class DoubleViRegMultEnvironment {
29 typedef double ValueType;
30 static const bool isExact =
false;
31 static const bool reportsBounds =
false;
32 static storm::Environment createEnvironment() {
33 storm::Environment env;
41class DoubleSoundViEnvironment {
43 typedef double ValueType;
44 static const bool isExact =
false;
45 static const bool reportsBounds =
true;
46 static storm::Environment createEnvironment() {
47 storm::Environment env;
55class DoubleIntervalIterationEnvironment {
57 typedef double ValueType;
58 static const bool isExact =
false;
59 static const bool reportsBounds =
true;
60 static storm::Environment createEnvironment() {
61 storm::Environment env;
69class DoubleGuessingViEnvironment {
71 typedef double ValueType;
72 static const bool isExact =
false;
73 static const bool reportsBounds =
true;
74 static storm::Environment createEnvironment() {
75 storm::Environment env;
83class DoubleOptimisticViEnvironment {
85 typedef double ValueType;
86 static const bool isExact =
false;
87 static const bool reportsBounds =
true;
88 static storm::Environment createEnvironment() {
89 storm::Environment env;
97class DoubleTopologicalViEnvironment {
99 typedef double ValueType;
100 static const bool isExact =
false;
101 static const bool reportsBounds =
true;
102 static storm::Environment createEnvironment() {
103 storm::Environment env;
111class DoublePIEnvironment {
113 typedef double ValueType;
114 static const bool isExact =
false;
115 static const bool reportsBounds =
false;
116 static storm::Environment createEnvironment() {
117 storm::Environment env;
126class RationalPIEnvironment {
128 typedef storm::RationalNumber ValueType;
129 static const bool isExact =
true;
130 static const bool reportsBounds =
true;
131 static storm::Environment createEnvironment() {
132 storm::Environment env;
137class RationalRationalSearchEnvironment {
139 typedef storm::RationalNumber ValueType;
140 static const bool isExact =
true;
141 static const bool reportsBounds =
true;
142 static storm::Environment createEnvironment() {
143 storm::Environment env;
149template<
typename TestType>
150class MinMaxLinearEquationSolverTest :
public ::testing::Test {
152 typedef typename TestType::ValueType ValueType;
153 MinMaxLinearEquationSolverTest() : _environment(TestType::createEnvironment()) {}
154 storm::Environment
const& env()
const {
157 ValueType precision()
const {
160 ValueType
parseNumber(std::string
const& input)
const {
163 bool reportsBounds()
const {
164 return TestType::reportsBounds;
170 void expectSolutionBoundsEnclose(storm::solver::MinMaxLinearEquationSolver<ValueType>
const& solver, uint64_t index, ValueType
const& value)
const {
173 EXPECT_LE(solver.
getSolutionLowerBounds()[index], value + precision()) <<
"The lower bound exceeds the solution.";
174 EXPECT_LE(value, solver.
getSolutionUpperBounds()[index] + precision()) <<
"The upper bound falls below the solution.";
178 storm::Environment _environment;
181typedef ::testing::Types<DoubleViEnvironment, DoubleViRegMultEnvironment, DoubleSoundViEnvironment, DoubleIntervalIterationEnvironment,
182 DoubleOptimisticViEnvironment, DoubleGuessingViEnvironment, DoubleTopologicalViEnvironment, DoublePIEnvironment, RationalPIEnvironment,
183 RationalRationalSearchEnvironment>
188TYPED_TEST(MinMaxLinearEquationSolverTest, SolveEquations) {
189 typedef typename TestFixture::ValueType
ValueType;
192 ASSERT_NO_THROW(builder.newRowGroup(0));
193 ASSERT_NO_THROW(builder.addNextValue(0, 0, this->parseNumber(
"0.9")));
196 ASSERT_NO_THROW(A = builder.build(2));
198 std::vector<ValueType> x(1);
202 auto solver = factory.create(this->env(), A);
209 ASSERT_NO_THROW(solver->
solveEquations(this->env(), storm::OptimizationDirection::Minimize, x, b));
210 EXPECT_NEAR(x[0], this->
parseNumber(
"0.5"), this->precision());
211 if (this->reportsBounds()) {
212 this->expectSolutionBoundsEnclose(*solver, 0, this->
parseNumber(
"0.5"));
215 ASSERT_NO_THROW(solver->
solveEquations(this->env(), storm::OptimizationDirection::Maximize, x, b));
216 EXPECT_NEAR(x[0], this->
parseNumber(
"0.99"), this->precision());
217 if (this->reportsBounds()) {
218 this->expectSolutionBoundsEnclose(*solver, 0, this->
parseNumber(
"0.99"));
222TEST(MinMaxLinearEquationSolverTest, TopologicalWithoutCyclesIsExact) {
225 builder.newRowGroup(0);
226 builder.addNextValue(0, 0, 0.5);
227 builder.addNextValue(0, 1, 0.25);
228 builder.newRowGroup(1);
229 builder.addNextValue(1, 1, 0.5);
236 std::vector<double> x(2);
237 std::vector<double> b = {0.0, 0.5};
243 ASSERT_NO_THROW(solver->
solveEquations(env, storm::OptimizationDirection::Maximize, x, b));
245 EXPECT_TRUE(solver->
hasExactSolutionBounds()) <<
"An acyclic topological solve should report the values as exact.";
254 builder.newRowGroup(0);
255 builder.addNextValue(0, 1, 0.5);
256 builder.addNextValue(0, 2, 0.5);
257 builder.newRowGroup(1);
258 builder.addNextValue(1, 0, 0.5);
259 builder.addNextValue(1, 2, 0.25);
260 builder.newRowGroup(2);
261 builder.addNextValue(2, 3, 0.5);
262 builder.newRowGroup(3);
263 builder.addNextValue(3, 2, 0.5);
264 return builder.build(4, 4, 4);
275TEST(MinMaxLinearEquationSolverTest, ValueIterationFromBelowReportsLowerBound) {
276 auto A = buildTwoSccMatrix();
277 std::vector<double> b = {0.0, 0.0, 0.25, 0.0};
284 std::vector<double> x(4);
285 auto solver = createSolver(env, A);
286 ASSERT_NO_THROW(solver->
solveEquations(env, storm::OptimizationDirection::Maximize, x, b));
294TEST(MinMaxLinearEquationSolverTest, ValueIterationWithoutSoundnessReportsNoBounds) {
295 auto A = buildTwoSccMatrix();
296 std::vector<double> b = {0.0, 0.0, 0.25, 0.0};
302 std::vector<double> x(4);
303 auto solver = createSolver(env, A);
304 ASSERT_NO_THROW(solver->
solveEquations(env, storm::OptimizationDirection::Maximize, x, b));
310TEST(MinMaxLinearEquationSolverTest, InitialSchedulerDoesNotYieldBounds) {
312 builder.newRowGroup(0);
313 builder.addNextValue(0, 1, 0.5);
314 builder.addNextValue(1, 1, 0.25);
315 builder.newRowGroup(2);
316 builder.addNextValue(2, 1, 0.5);
318 std::vector<double> b = {0.0, 0.0, 0.25};
326 std::vector<double> x(2);
327 auto solver = createSolver(env, A);
329 ASSERT_NO_THROW(solver->
solveEquations(env, storm::OptimizationDirection::Maximize, x, b));
330 EXPECT_NEAR(x[0], 0.25, 1e-6);
337TEST(MinMaxLinearEquationSolverTest, PolicyIterationWithInexactInnerSolveAddsNoBounds) {
338 auto A = buildTwoSccMatrix();
339 std::vector<double> b = {0.0, 0.0, 0.25, 0.0};
344 env.
solver().
native().
setMethod(storm::solver::NativeLinearEquationSolverMethod::SoundValueIteration);
346 std::vector<double> x(4);
347 auto solver = createSolver(env, A);
349 ASSERT_NO_THROW(solver->
solveEquations(env, storm::OptimizationDirection::Maximize, x, b));
360TEST(MinMaxLinearEquationSolverTest, OptimisticValueIterationStoppedEarlyStaysWithinItsBounds) {
361 auto A = buildTwoSccMatrix();
362 std::vector<double> b = {0.0, 0.0, 0.25, 0.0};
368 std::vector<double> x(4);
369 auto solver = createSolver(env, A);
371 EXPECT_FALSE(solver->
solveEquations(env, storm::OptimizationDirection::Maximize, x, b));
381TEST(MinMaxLinearEquationSolverTest, TopologicalWithUnsoundSccMethodReportsNoBounds) {
382 auto A = buildTwoSccMatrix();
383 std::vector<double> b = {0.0, 0.0, 0.25, 0.0};
391 std::vector<double> x(4);
392 auto solver = createSolver(env, A);
393 ASSERT_NO_THROW(solver->
solveEquations(env, storm::OptimizationDirection::Maximize, x, b));
401TEST(MinMaxLinearEquationSolverTest, TopologicalWithSoundSccMethodReportsBounds) {
402 auto A = buildTwoSccMatrix();
403 std::vector<double> b = {0.0, 0.0, 0.25, 0.0};
411 std::vector<double> x(4);
412 auto solver = createSolver(env, A);
414 ASSERT_NO_THROW(solver->
solveEquations(env, storm::OptimizationDirection::Maximize, x, b));
422TEST(MinMaxLinearEquationSolverTest, SoundMethodsTerminateEarly) {
426 builder.newRowGroup(0);
427 builder.addNextValue(0, 0, 0.99);
428 builder.newRowGroup(1);
429 builder.addNextValue(1, 1, 0.5);
431 std::vector<double> b = {0.01, 0.25};
434 for (
auto method : {storm::solver::MinMaxMethod::SoundValueIteration, storm::solver::MinMaxMethod::IntervalIteration,
435 storm::solver::MinMaxMethod::GuessingValueIteration}) {
436 for (
bool useLowerBound : {
true,
false}) {
453 std::vector<double> x(2);
454 ASSERT_NO_THROW(solver->
solveEquations(env, storm::OptimizationDirection::Minimize, x, b));
456 EXPECT_GT(std::abs(x[0] - 1.0), 1e-3) <<
"x[0] = " << x[0];
461TEST(IntervalIterationJustPreciseEnough, MinMax) {
463 builder.newRowGroup(0);
464 builder.addNextValue(0, 0, 0.9);
465 builder.addNextValue(1, 0, 0.9);
466 auto matrix = builder.build();
467 std::vector<double> b = {0.099, 0.05};
468 double const precision = 1e-3;
470 for (
bool relative : {
false,
true}) {
471 for (
auto dir : {storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Maximize}) {
472 SCOPED_TRACE(::testing::Message() <<
"relative=" << relative <<
", maximize=" <<
storm::solver::maximize(dir));
473 auto env = DoubleIntervalIterationEnvironment::createEnvironment();
480 std::vector<double> x(1);
487 double const tolerance = precision * (relative ? lower : 1.0);
488 EXPECT_LE(lower, exact);
489 EXPECT_GE(upper, exact);
490 EXPECT_DOUBLE_EQ(x[0], (lower + upper) / 2.0);
491 EXPECT_NEAR(x[0], exact, precision * (relative ? exact : 1.0));
492 EXPECT_LE(upper - lower, 2.0 * tolerance);
494 EXPECT_GT(upper - lower, tolerance);
499TEST(GuessingValueIterationPrecision, MinMaxSmallValues) {
502 builder.newRowGroup(0);
503 builder.addNextValue(0, 0, 0.9);
504 builder.newRowGroup(2);
505 auto matrix = builder.build(3, 2);
507 for (
bool relative : {
false,
true}) {
508 for (
double scale : {1.0, 1e-8}) {
509 for (
auto direction : {storm::OptimizationDirection::Minimize, storm::OptimizationDirection::Maximize}) {
510 SCOPED_TRACE(::testing::Message() <<
"relative=" << relative <<
", scale=" << scale <<
", direction=" << direction);
511 auto env = DoubleGuessingViEnvironment::createEnvironment();
520 std::vector<double> x(2);
521 std::vector<double> b = {1e-5 *
scale, 5e-5 *
scale, 0.0};
524 double tolerance = relative ? 1e-3 * expected : 1e-3;
525 EXPECT_NEAR(x[0], expected, tolerance);
526 EXPECT_EQ(x[1], 0.0);
TEST(RegionConstraintCheckerTest, AffineConstraintHoldsThroughout)
SolverEnvironment & solver()
void setMultiplicationStyle(storm::solver::MultiplicationStyle value)
storm::RationalNumber const & getPrecision() const
void setPrecision(storm::RationalNumber value)
void setMethod(storm::solver::MinMaxMethod value, bool isSetFromDefault=false)
void setMaximalNumberOfIterations(uint64_t value)
void setRelativeTerminationCriterion(bool value)
void setMethod(storm::solver::NativeLinearEquationSolverMethod value, bool isSetFromDefault=false)
TopologicalSolverEnvironment & topological()
void setLinearEquationSolverType(storm::solver::EquationSolverType const &value, bool isSetFromDefault=false)
MinMaxSolverEnvironment & minMax()
void setForceSoundness(bool value)
void setLinearEquationSolverPrecision(boost::optional< storm::RationalNumber > const &newPrecision, boost::optional< bool > const &relativePrecision=boost::none)
NativeSolverEnvironment & native()
void setUnderlyingMinMaxMethod(storm::solver::MinMaxMethod value, bool isSetFromDefault=false)
void setTerminationCondition(std::unique_ptr< TerminationCondition< SolutionType > > terminationCondition)
Sets a custom termination condition that is used together with the regular termination condition of t...
void setBounds(SolutionType const &lower, SolutionType const &upper)
Sets bounds for the solution that can potentially be used by the solver.
std::vector< SolutionType > const & getSolutionUpperBounds() const
bool hasExactSolutionBounds() const
Retrieves whether the bounds computed by the last call to this solver coincide.
SolutionBounds< SolutionType > const & getSolutionBounds() const
Retrieves both bounds at once, either of which may be unset.
bool hasSolutionLowerBounds() const
Retrieves whether the last call to this solver computed a sound lower resp.
bool hasSolutionUpperBounds() const
std::vector< SolutionType > const & getSolutionLowerBounds() const
Retrieves sound bounds on the solution that the last call to this solver computed.
virtual std::unique_ptr< MinMaxLinearEquationSolver< ValueType, SolutionType > > create(Environment const &env) const override
void setHasUniqueSolution(bool value=true)
Sets whether the solution to the min max equation system is known to be unique.
bool solveEquations(Environment const &env, OptimizationDirection d, std::vector< SolutionType > &x, std::vector< ValueType > const &b) const
Solves the equation system x = min/max(A*x + b) given by the parameters.
void setInitialScheduler(std::vector< uint_fast64_t > &&choices)
Sets a valid initial scheduler that is required by some solvers (see requirements of solvers).
void setRequirementsChecked(bool value=true)
Notifies the solver that the requirements for solving equations have been checked.
virtual MinMaxLinearEquationSolverRequirements getRequirements(Environment const &env, boost::optional< storm::solver::OptimizationDirection > const &direction=boost::none, bool const &hasInitialScheduler=false) const
Retrieves the requirements of this solver for solving equations with the current settings.
void setHasNoEndComponents(bool value=true)
Sets whether the min max equation system is known to not have any end components.
bool hasEnabledRequirement() const
A bit vector that is internally represented as a vector of 64-bit values.
A class that can be used to build a sparse matrix by adding value by value.
A class that holds a possibly non-square matrix in the compressed row storage format.
bool scale(int exp, storm::RationalNumber &r, storm::RationalNumber acc)
SFTBDDChecker::ValueType ValueType
NumberType parseNumber(std::string const &value)
Parse number from string.
bool constexpr maximize(OptimizationDirection d)
std::string toString(GurobiSolverMethod const &method)
Yields a string representation of the GurobiSolverMethod.
bool constexpr minimize(OptimizationDirection d)
storm::storage::BitVector filter(std::vector< T > const &values, std::function< bool(T const &value)> const &function)
Retrieves a bit vector containing all the indices for which the value at this position makes the give...
TargetType convertNumber(SourceType const &number)
TYPED_TEST(GraphTestAR, SymbolicProb01StochasticGameDieSmall)
TYPED_TEST_SUITE(GraphTestAR, TestingTypes,)
::testing::Types< Cudd, Sylvan > TestingTypes