Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
DeterministicSchedsLpChecker.cpp
Go to the documentation of this file.
2
13
16
18
19template<typename ModelType, typename GeometryValueType>
21 ModelType const& model, std::vector<DeterministicSchedsObjectiveHelper<ModelType>> const& objectiveHelper)
22 : model(model), objectiveHelper(objectiveHelper), numLpQueries(0) {
23 // intentionally left empty
24}
25
26template<typename ModelType, typename GeometryValueType>
27void DeterministicSchedsLpChecker<ModelType, GeometryValueType>::initialize(Environment const& env) {
28 if (!lpModel) {
29 swInit.start();
30 initializeLpModel(env);
31 swInit.stop();
32 }
33}
34
35template<typename ModelType, typename GeometryValueType>
37 std::stringstream out;
38 out << prefix << swAll << " seconds for LP Checker including... \n";
39 out << prefix << " " << swInit << " seconds for LP initialization\n";
40 out << prefix << " " << swCheckWeightVectors << " seconds for checking weight vectors\n";
41 out << prefix << " " << swCheckAreas << " seconds for checking areas\n";
42 out << prefix << " " << swValidate << " seconds for validating LP solutions\n";
43 out << prefix << " " << numLpQueries << " calls to LP optimization\n";
44 return out.str();
45}
46
47template<typename ModelType, typename GeometryValueType>
49 std::vector<GeometryValueType> const& weightVector) {
50 swAll.start();
51 initialize(env);
52 STORM_LOG_ASSERT(weightVector.size() == objectiveHelper.size(), "Setting a weight vector with invalid number of entries.");
53 if (!currentWeightVector.empty()) {
54 // Pop information of the current weight vector.
55 lpModel->pop();
56 lpModel->update();
57 currentObjectiveVariables.clear();
58 }
59
60 currentWeightVector = weightVector;
61
62 lpModel->push();
63 // set up objective function for the given weight vector
64 for (uint64_t objIndex = 0; objIndex < initialStateResults.size(); ++objIndex) {
65 currentObjectiveVariables.push_back(
66 lpModel->addUnboundedContinuousVariable("w_" + std::to_string(objIndex), storm::utility::convertNumber<ValueType>(weightVector[objIndex])));
67 lpModel->addConstraint("", currentObjectiveVariables.back().getExpression() == initialStateResults[objIndex]);
68 }
69 lpModel->update();
70 swAll.stop();
71}
72
73template<typename ModelType, typename GeometryValueType>
74std::optional<std::pair<std::vector<GeometryValueType>, GeometryValueType>> DeterministicSchedsLpChecker<ModelType, GeometryValueType>::check(
75 storm::Environment const& env, Polytope overapproximation, Point const& eps) {
76 swAll.start();
77 initialize(env);
78 STORM_LOG_ASSERT(!currentWeightVector.empty(), "Checking invoked before specifying a weight vector.");
79 STORM_LOG_TRACE("Checking a vertex...");
80 lpModel->push();
81 auto areaConstraints = overapproximation->getConstraints(lpModel->getManager(), currentObjectiveVariables);
82 for (auto const& c : areaConstraints) {
83 lpModel->addConstraint("", c);
84 }
85
86 if (!eps.empty()) {
87 STORM_LOG_ASSERT(currentWeightVector.size() == eps.size(), "Eps vector has unexpected size.");
88 // Specify the allowed gap between the obtained lower/upper objective bounds.
89 GeometryValueType milpGap = storm::utility::vector::dotProduct(currentWeightVector, eps);
90 lpModel->setMaximalMILPGap(storm::utility::convertNumber<ValueType>(milpGap), false);
91 }
92 lpModel->update();
93 swCheckWeightVectors.start();
94 lpModel->optimize();
95 swCheckWeightVectors.stop();
96 ++numLpQueries;
97 // STORM_PRINT_AND_LOG("Writing model to file '" << std::to_string(numLpQueries) << ".lp'\n";);
98 // lpModel->writeModelToFile(std::to_string(numLpQueries) + ".lp");
99 std::optional<std::pair<Point, GeometryValueType>> result;
100 if (!lpModel->isInfeasible()) {
101 STORM_LOG_ASSERT(!lpModel->isUnbounded(), "LP result is unbounded.");
102 swValidate.start();
103 auto resultPoint = validateCurrentModel(env);
104 swValidate.stop();
105 auto resultValue = storm::utility::vector::dotProduct(resultPoint, currentWeightVector);
106 if (!eps.empty()) {
107 resultValue += storm::utility::convertNumber<GeometryValueType>(lpModel->getMILPGap(false));
108 }
109 result = std::make_pair(resultPoint, resultValue);
110 }
111 lpModel->pop();
112 STORM_LOG_TRACE("\t Done checking a vertex...");
113 swAll.stop();
114 return result;
115}
116
117template<typename ModelType, typename GeometryValueType>
118std::pair<std::vector<std::vector<GeometryValueType>>, std::vector<std::shared_ptr<storm::storage::geometry::Polytope<GeometryValueType>>>>
121 swAll.start();
122 initialize(env);
123 STORM_LOG_INFO("Checking " << polytopeTree.toString());
124 STORM_LOG_ASSERT(!currentWeightVector.empty(), "Checking invoked before specifying a weight vector.");
125 if (polytopeTree.isEmpty()) {
126 return {{}, {}};
127 }
128
129 // Specify a gap between the obtained lower/upper objective bounds.
130 // Let p be the found solution point, q be the optimal (unknown) solution point, and w be the current weight vector.
131 // The gap between the solution p and q is |w*p - w*q| = |w*(p-q)|
132 GeometryValueType milpGap = storm::utility::vector::dotProduct(currentWeightVector, eps);
133 lpModel->setMaximalMILPGap(storm::utility::convertNumber<ValueType>(milpGap), false);
134 lpModel->update();
135
136 std::vector<Point> foundPoints;
137 std::vector<Polytope> infeasableAreas;
138 checkRecursive(env, polytopeTree, eps, foundPoints, infeasableAreas, 0);
139 swAll.stop();
140 return {foundPoints, infeasableAreas};
141}
142
143template<typename ValueType>
145 std::vector<storm::expressions::Expression> choiceVariables;
146 choiceVariables.reserve(matrix.getRowCount());
147 for (uint64_t state = 0; state < matrix.getRowGroupCount(); ++state) {
148 auto choices = matrix.getRowGroupIndices(state);
149 if (choices.size() == 1) {
150 choiceVariables.push_back(lpModel.getConstant(storm::utility::one<ValueType>())); // Unique choice; no variable necessary
151 } else {
152 std::vector<storm::expressions::Expression> localChoices;
153 for (auto const choice : choices) {
154 localChoices.push_back(lpModel.addBinaryVariable("a" + std::to_string(choice)));
155 choiceVariables.push_back(localChoices.back());
156 }
157 lpModel.update();
158 lpModel.addConstraint("", storm::expressions::sum(localChoices) == lpModel.getConstant(1));
159 }
160 }
161 return choiceVariables;
162}
163
164template<typename ValueType, typename HelperType>
165std::vector<storm::expressions::Expression> classicConstraints(storm::solver::LpSolver<ValueType>& lpModel, bool const& indicatorConstraints,
166 storm::storage::SparseMatrix<ValueType> const& matrix, uint64_t initialState, uint64_t objIndex,
167 HelperType const& objectiveHelper,
168 std::vector<storm::expressions::Expression> const& choiceVariables) {
169 // Create variables
170 std::vector<storm::expressions::Expression> objectiveValueVariables(matrix.getRowGroupCount());
171 for (auto const& state : objectiveHelper.getMaybeStates()) {
172 if (indicatorConstraints) {
173 objectiveValueVariables[state] = lpModel.addContinuousVariable("x_" + std::to_string(objIndex) + "_" + std::to_string(state));
174 } else {
175 objectiveValueVariables[state] =
176 lpModel.addBoundedContinuousVariable("x_" + std::to_string(objIndex) + "_" + std::to_string(state),
177 objectiveHelper.getLowerValueBoundAtState(state), objectiveHelper.getUpperValueBoundAtState(state));
178 }
179 }
180 std::vector<storm::expressions::Expression> reachVars;
181 if (objectiveHelper.getInfinityCase() == HelperType::InfinityCase::HasNegativeInfinite) {
182 reachVars.assign(matrix.getRowGroupCount(), {});
183 for (auto const& state : objectiveHelper.getRewMinusInfEStates()) {
184 reachVars[state] = lpModel.addBinaryVariable("c_" + std::to_string(objIndex) + "_" + std::to_string(state));
185 }
186 STORM_LOG_ASSERT(objectiveHelper.getRewMinusInfEStates().get(initialState), "Initial state must be in RewMinusInfEStates.");
187 lpModel.update();
188 lpModel.addConstraint("", reachVars[initialState] == lpModel.getConstant(storm::utility::one<ValueType>()));
189 }
190 lpModel.update();
191 for (auto const& state : objectiveHelper.getMaybeStates()) {
192 bool const requireReachConstraints =
193 objectiveHelper.getInfinityCase() == HelperType::InfinityCase::HasNegativeInfinite && objectiveHelper.getRewMinusInfEStates().get(state);
194 for (auto choice : matrix.getRowGroupIndices(state)) {
195 auto const& choiceVarAsExpression = choiceVariables.at(choice);
196 STORM_LOG_ASSERT(choiceVarAsExpression.isVariable() ||
197 (!choiceVarAsExpression.containsVariables() && storm::utility::isOne(choiceVarAsExpression.evaluateAsRational())),
198 "Unexpected kind of choice variable: " << choiceVarAsExpression);
199 std::vector<storm::expressions::Expression> summands;
200 if (!indicatorConstraints && choiceVarAsExpression.isVariable()) {
201 summands.push_back((lpModel.getConstant(storm::utility::one<ValueType>()) - choiceVarAsExpression) *
202 lpModel.getConstant(objectiveHelper.getUpperValueBoundAtState(state) - objectiveHelper.getLowerValueBoundAtState(state)));
203 }
204 if (auto findRes = objectiveHelper.getChoiceRewards().find(choice); findRes != objectiveHelper.getChoiceRewards().end()) {
205 auto rewExpr = lpModel.getConstant(findRes->second);
206 if (requireReachConstraints) {
207 summands.push_back(reachVars[state] * rewExpr);
208 } else {
209 summands.push_back(rewExpr);
210 }
211 }
212 for (auto const& succ : matrix.getRow(choice)) {
213 if (objectiveHelper.getMaybeStates().get(succ.getColumn())) {
214 summands.push_back(lpModel.getConstant(succ.getValue()) * objectiveValueVariables.at(succ.getColumn()));
215 }
216 if (requireReachConstraints && objectiveHelper.getRewMinusInfEStates().get(succ.getColumn())) {
217 lpModel.addConstraint(
218 "", reachVars[state] <= reachVars[succ.getColumn()] + lpModel.getConstant(storm::utility::one<ValueType>()) - choiceVarAsExpression);
219 }
220 }
221 if (summands.empty()) {
222 summands.push_back(lpModel.getConstant(storm::utility::zero<ValueType>()));
223 }
224 if (indicatorConstraints && choiceVarAsExpression.isVariable()) {
225 auto choiceVar = choiceVarAsExpression.getBaseExpression().asVariableExpression().getVariable();
226 lpModel.addIndicatorConstraint("", choiceVar, true, objectiveValueVariables.at(state) <= storm::expressions::sum(summands));
227 } else {
228 lpModel.addConstraint("", objectiveValueVariables.at(state) <= storm::expressions::sum(summands));
229 }
230 }
231 }
232 return objectiveValueVariables;
233}
234
237template<typename ValueType, typename ObjHelperType>
239 std::vector<ObjHelperType> const& objectiveHelper) {
240 std::vector<std::pair<storm::storage::MaximalEndComponent, std::vector<uint64_t>>> problMecs;
241 for (uint64_t objIndex = 0; objIndex < objectiveHelper.size(); ++objIndex) {
242 auto const& obj = objectiveHelper[objIndex];
243 storm::storage::MaximalEndComponentDecomposition<ValueType> objMecs(matrix, backwardTransitions, obj.getMaybeStates(),
244 obj.getRelevantZeroRewardChoices());
245 for (auto& newMec : objMecs) {
246 bool found = false;
247 for (auto& problMec : problMecs) {
248 if (problMec.first == newMec) {
249 problMec.second.push_back(objIndex);
250 found = true;
251 break;
252 }
253 }
254 if (!found) {
255 problMecs.emplace_back(std::move(newMec), std::vector<uint64_t>({objIndex}));
256 }
257 }
258 }
259 STORM_LOG_DEBUG("Found " << problMecs.size() << " problematic ECs.");
260 return problMecs;
261}
262
263template<typename ValueType, typename UpperBoundsGetterType>
264auto problematicMecConstraintsExpVisits(storm::solver::LpSolver<ValueType>& lpModel, bool const& indicatorConstraints, bool const& redundantConstraints,
266 uint64_t mecIndex, storm::storage::MaximalEndComponent const& problematicMec,
267 std::vector<uint64_t> const& relevantObjectiveIndices,
268 std::vector<std::vector<storm::expressions::Expression>> const& objectiveValueVariables,
269 std::vector<storm::expressions::Expression> const& choiceVariables,
270 UpperBoundsGetterType const& objectiveStateUpperBoundGetter) {
271 storm::expressions::Expression visitsUpperBound;
272 if (!indicatorConstraints) {
273 visitsUpperBound = lpModel.getConstant(VisitingTimesHelper<ValueType>::computeMecVisitsUpperBound(problematicMec, matrix, true));
274 }
275
276 // Create variables and basic lower/upper bounds
277 storm::storage::BitVector mecChoices(matrix.getRowCount(), false);
278 std::map<uint64_t, storm::expressions::Expression> expVisitsVars; // z^C_{s,act}
279 std::map<uint64_t, storm::expressions::Expression> botVars; // z^C_{s,bot}
280 std::map<uint64_t, storm::expressions::Expression> bsccIndicatorVariables; // b^C_{s}
281 for (auto const& stateChoices : problematicMec) {
282 auto const state = stateChoices.first;
283 auto bsccIndicatorVar = lpModel.addBinaryVariable("b_" + std::to_string(mecIndex) + "_" + std::to_string(state));
284 bsccIndicatorVariables.emplace(state, bsccIndicatorVar.getExpression());
285 std::string visitsVarPref = "z_" + std::to_string(mecIndex) + "_";
286 auto stateBotVisitsVar =
287 lpModel.addLowerBoundedContinuousVariable(visitsVarPref + std::to_string(state) + "bot", storm::utility::zero<ValueType>()).getExpression();
288 botVars.emplace(state, stateBotVisitsVar);
289 lpModel.update();
290 if (indicatorConstraints) {
291 lpModel.addIndicatorConstraint("", bsccIndicatorVar, false, stateBotVisitsVar <= lpModel.getConstant(storm::utility::zero<ValueType>()));
292 } else {
293 lpModel.addConstraint("", stateBotVisitsVar <= bsccIndicatorVar.getExpression() * visitsUpperBound);
294 }
295 for (auto choice : matrix.getRowGroupIndices(state)) {
296 auto stateActionVisitsVar =
297 lpModel.addLowerBoundedContinuousVariable(visitsVarPref + std::to_string(choice), storm::utility::zero<ValueType>()).getExpression();
298 lpModel.update();
299 if (indicatorConstraints) {
300 if (auto const& a = choiceVariables[choice]; a.isVariable()) {
301 auto aVar = a.getBaseExpression().asVariableExpression().getVariable();
302 lpModel.addIndicatorConstraint("", aVar, false, stateActionVisitsVar <= lpModel.getConstant(storm::utility::zero<ValueType>()));
303 }
304 } else {
305 lpModel.addConstraint("", stateActionVisitsVar <= choiceVariables[choice] * visitsUpperBound);
306 }
307 expVisitsVars.emplace(choice, stateActionVisitsVar);
308 }
309 for (auto const& ecChoice : stateChoices.second) {
310 mecChoices.set(ecChoice, true);
311 }
312 for (auto const& objIndex : relevantObjectiveIndices) {
313 if (indicatorConstraints) {
314 lpModel.addIndicatorConstraint("", bsccIndicatorVar, true,
315 objectiveValueVariables[objIndex][state] <= lpModel.getConstant(storm::utility::zero<ValueType>()));
316 } else {
317 auto const upperBnd = lpModel.getConstant(objectiveStateUpperBoundGetter(objIndex, state));
318 lpModel.addConstraint("", objectiveValueVariables[objIndex][state] <= upperBnd - upperBnd * bsccIndicatorVar.getExpression());
319 }
320 }
321 }
322
323 // Create visits constraints
325 std::vector<storm::expressions::Expression> outVisitsSummands;
326 for (auto const& stateChoices : problematicMec) {
327 auto const state = stateChoices.first;
328 auto const& choices = stateChoices.second;
329 auto const& stateBotVar = botVars.at(state);
330 outVisitsSummands.push_back(stateBotVar);
331 std::vector<storm::expressions::Expression> stateVisitsSummands;
332 stateVisitsSummands.push_back(stateBotVar);
333 for (auto choice : matrix.getRowGroupIndices(state)) {
334 auto const& choiceVisitsVar = expVisitsVars.at(choice);
335 stateVisitsSummands.push_back(choiceVisitsVar);
336 if (choices.count(choice) != 0) {
337 if (redundantConstraints) {
338 for (auto const& postElem : matrix.getRow(choice)) {
339 if (storm::utility::isZero(postElem.getValue())) {
340 continue;
341 }
342 auto succ = postElem.getColumn();
343 lpModel.addConstraint("", bsccIndicatorVariables.at(state) + choiceVariables.at(choice) <=
344 lpModel.getConstant(storm::utility::one<ValueType>()) + bsccIndicatorVariables.at(succ));
345 }
346 }
347 } else {
348 outVisitsSummands.push_back(choiceVisitsVar);
349 }
350 }
351 for (auto const& preEntry : backwardChoices.getRow(state)) {
352 uint64_t const preChoice = preEntry.getColumn();
353 if (mecChoices.get(preChoice)) {
354 ValueType preProb =
356 stateVisitsSummands.push_back(lpModel.getConstant(-preProb) * expVisitsVars.at(preChoice));
357 }
358 }
359 lpModel.addConstraint("", storm::expressions::sum(stateVisitsSummands) == initProb);
360 }
361 lpModel.addConstraint("", storm::expressions::sum(outVisitsSummands) == lpModel.getConstant(storm::utility::one<ValueType>()));
362}
363
364template<typename ValueType, typename UpperBoundsGetterType>
365auto problematicMecConstraintsOrder(storm::solver::LpSolver<ValueType>& lpModel, bool const& indicatorConstraints, bool const& redundantConstraints,
366 storm::storage::SparseMatrix<ValueType> const& matrix, uint64_t mecIndex,
367 storm::storage::MaximalEndComponent const& problematicMec, std::vector<uint64_t> const& relevantObjectiveIndices,
368 std::vector<storm::expressions::Expression> const& choiceVariables,
369 std::vector<std::vector<storm::expressions::Expression>> const& objectiveValueVariables,
370 UpperBoundsGetterType const& objectiveStateUpperBoundGetter) {
371 // Create bscc indicator and order variables with basic lower/upper bounds
372 storm::storage::BitVector mecChoices(matrix.getRowCount(), false);
373 std::map<uint64_t, storm::expressions::Expression> bsccIndicatorVariables; // b^C_{s}
374 std::map<uint64_t, storm::expressions::Expression> orderVariables; // r^C_{s}
375 for (auto const& stateChoices : problematicMec) {
376 auto const state = stateChoices.first;
377 auto bsccIndicatorVar = lpModel.addBinaryVariable("b_" + std::to_string(mecIndex) + "_" + std::to_string(state));
378 bsccIndicatorVariables.emplace(state, bsccIndicatorVar.getExpression());
379 auto orderVar = lpModel
380 .addBoundedContinuousVariable("r_" + std::to_string(mecIndex) + "_" + std::to_string(state), storm::utility::zero<ValueType>(),
382 .getExpression();
383 lpModel.update();
384 orderVariables.emplace(state, orderVar);
385 for (auto const& ecChoice : stateChoices.second) {
386 mecChoices.set(ecChoice, true);
387 }
388 for (auto const& objIndex : relevantObjectiveIndices) {
389 if (indicatorConstraints) {
390 lpModel.addIndicatorConstraint("", bsccIndicatorVar, true,
391 objectiveValueVariables[objIndex][state] <= lpModel.getConstant(storm::utility::zero<ValueType>()));
392 } else {
393 auto const upperBnd = lpModel.getConstant(objectiveStateUpperBoundGetter(objIndex, state));
394 lpModel.addConstraint("", objectiveValueVariables[objIndex][state] <= upperBnd - upperBnd * bsccIndicatorVar.getExpression());
395 }
396 }
397 }
398
399 // Create order constraints
401 for (auto const& stateChoices : problematicMec) {
402 auto const state = stateChoices.first;
403 auto const& choices = stateChoices.second;
404 auto const& bsccIndicatorVar = bsccIndicatorVariables.at(state);
405 auto const& orderVar = orderVariables.at(state);
406 for (auto choice : choices) {
407 auto const& choiceVariable = choiceVariables.at(choice);
408 std::vector<storm::expressions::Expression> choiceConstraint;
409 choiceConstraint.push_back(bsccIndicatorVar);
410 std::string const transSelectPrefix = "d_" + std::to_string(mecIndex) + "_" + std::to_string(choice) + "_";
411 for (auto const& postElem : matrix.getRow(choice)) {
412 if (storm::utility::isZero(postElem.getValue())) {
413 continue;
414 }
415 auto succ = postElem.getColumn();
416 if (redundantConstraints) {
417 lpModel.addConstraint(
418 "", bsccIndicatorVar + choiceVariable <= lpModel.getConstant(storm::utility::one<ValueType>()) + bsccIndicatorVariables.at(succ));
419 }
420 auto transVar = lpModel.addBinaryVariable(transSelectPrefix + std::to_string(succ)).getExpression();
421 lpModel.update();
422 choiceConstraint.push_back(transVar);
423 lpModel.addConstraint("", transVar <= choiceVariable);
424 lpModel.addConstraint("", orderVar <= minDiff + orderVariables.at(succ) + lpModel.getConstant(storm::utility::one<ValueType>()) - transVar);
425 }
426 lpModel.addConstraint("", choiceVariable <= storm::expressions::sum(choiceConstraint));
427 }
428 }
429}
430
431template<typename ValueType, typename HelperType>
432std::vector<storm::expressions::Expression> expVisitsConstraints(storm::solver::LpSolver<ValueType>& lpModel, bool const& indicatorConstraints,
434 storm::storage::SparseMatrix<ValueType> const& backwardTransitions,
435 storm::storage::SparseMatrix<ValueType> const& backwardChoices, uint64_t initialState,
436 std::vector<HelperType> const& objectiveHelper,
437 std::vector<storm::expressions::Expression> const& choiceVariables) {
438 auto objHelpIt = objectiveHelper.begin();
439 storm::storage::BitVector anyMaybeStates = objHelpIt->getMaybeStates();
440 for (++objHelpIt; objHelpIt != objectiveHelper.end(); ++objHelpIt) {
441 anyMaybeStates |= objHelpIt->getMaybeStates();
442 }
443 storm::storage::BitVector allZeroRewardChoices(matrix.getRowCount(), true);
444 for (auto const& oh : objectiveHelper) {
445 for (auto const& rew : oh.getChoiceRewards()) {
446 STORM_LOG_ASSERT(!storm::utility::isZero(rew.second), "Reward value is zero.");
447 allZeroRewardChoices.set(rew.first, false);
448 }
449 }
450 storm::storage::MaximalEndComponentDecomposition<ValueType> mecs(matrix, backwardTransitions, anyMaybeStates, allZeroRewardChoices);
451 storm::storage::BitVector mecStates(matrix.getRowGroupCount(), false);
452 for (auto const& mec : mecs) {
453 for (auto const& sc : mec) {
454 mecStates.set(sc.first, true);
455 }
456 }
457 std::vector<ValueType> maxVisits;
458 if (!indicatorConstraints) {
459 maxVisits = VisitingTimesHelper<ValueType>::computeUpperBoundsOnExpectedVisitingTimes(anyMaybeStates, matrix, backwardTransitions);
460 }
461
462 // Create variables and basic bounds
463 std::vector<storm::expressions::Expression> choiceVisitsVars(matrix.getRowCount()), botVisitsVars(matrix.getRowGroupCount()),
464 bsccVars(matrix.getRowGroupCount());
465 for (uint64_t state : anyMaybeStates) {
466 STORM_LOG_ASSERT(indicatorConstraints || maxVisits[state] >= storm::utility::zero<ValueType>(), "Unexpected negative max visits.");
467 for (auto choice : matrix.getRowGroupIndices(state)) {
468 choiceVisitsVars[choice] =
469 lpModel.addLowerBoundedContinuousVariable("y_" + std::to_string(choice), storm::utility::zero<ValueType>()).getExpression();
470 lpModel.update();
471 if (indicatorConstraints) {
472 if (auto const& a = choiceVariables[choice]; a.isVariable()) {
473 auto aVar = a.getBaseExpression().asVariableExpression().getVariable();
474 lpModel.addIndicatorConstraint("", aVar, false, choiceVisitsVars[choice] <= lpModel.getConstant(storm::utility::zero<ValueType>()));
475 }
476 } else {
477 lpModel.addConstraint("", choiceVisitsVars[choice] <= choiceVariables.at(choice) * lpModel.getConstant(maxVisits[state]));
478 }
479 }
480 if (mecStates.get(state)) {
481 bsccVars[state] = lpModel.addBinaryVariable("b_" + std::to_string(state)).getExpression();
482 botVisitsVars[state] =
483 lpModel.addLowerBoundedContinuousVariable("y_" + std::to_string(state) + "bot", storm::utility::zero<ValueType>()).getExpression();
484 lpModel.update();
485 if (indicatorConstraints) {
486 lpModel.addIndicatorConstraint("", bsccVars[state].getBaseExpression().asVariableExpression().getVariable(), false,
487 botVisitsVars[state] <= lpModel.getConstant(storm::utility::zero<ValueType>()));
488 } else {
489 lpModel.addConstraint("", botVisitsVars[state] <= bsccVars[state] * lpModel.getConstant(maxVisits[state]));
490 }
491 }
492 }
493
494 // Add expected visiting times constraints
495 auto notMaybe = ~anyMaybeStates;
496 std::vector<storm::expressions::Expression> outSummands;
497 for (uint64_t state : anyMaybeStates) {
498 std::vector<storm::expressions::Expression> visitsSummands;
499 if (mecStates.get(state)) {
500 visitsSummands.push_back(-botVisitsVars[state]);
501 outSummands.push_back(botVisitsVars[state]);
502 }
503 for (auto choice : matrix.getRowGroupIndices(state)) {
504 visitsSummands.push_back(-choiceVisitsVars[choice]);
505 if (auto outProb = matrix.getConstrainedRowSum(choice, notMaybe); !storm::utility::isZero(outProb)) {
506 outSummands.push_back(lpModel.getConstant(outProb) * choiceVisitsVars[choice]);
507 }
508 }
509 if (state == initialState) {
510 visitsSummands.push_back(lpModel.getConstant(storm::utility::one<ValueType>()));
511 }
512 for (auto const& preEntry : backwardChoices.getRow(state)) {
513 STORM_LOG_ASSERT(choiceVisitsVars[preEntry.getColumn()].isInitialized(), "Choice visit variable not initialized.");
514 visitsSummands.push_back(lpModel.getConstant(preEntry.getValue()) * choiceVisitsVars[preEntry.getColumn()]);
515 }
516 lpModel.addConstraint("", storm::expressions::sum(visitsSummands) == lpModel.getConstant(storm::utility::zero<ValueType>()));
517 }
519
520 // Add bscc constraints
521 for (auto const& mec : mecs) {
522 for (auto const& stateChoices : mec) {
523 auto const& state = stateChoices.first;
524 for (auto choice : matrix.getRowGroupIndices(state)) {
525 if (stateChoices.second.count(choice) != 0) {
526 for (auto const& succ : matrix.getRow(choice)) {
527 if (storm::utility::isZero(succ.getValue())) {
528 continue;
529 }
530 STORM_LOG_ASSERT(mecStates.get(succ.getColumn()), "MEC state not set for successor.");
531 lpModel.addConstraint("", bsccVars[state] <= bsccVars[succ.getColumn()] + lpModel.getConstant(storm::utility::one<ValueType>()) -
532 choiceVariables[choice]);
533 }
534 } else {
535 lpModel.addConstraint("", bsccVars[state] <= lpModel.getConstant(storm::utility::one<ValueType>()) - choiceVariables[choice]);
536 }
537 }
538 }
539 }
540
541 // Add objective values
542 std::vector<storm::expressions::Expression> objectiveValueVariables;
543 for (uint64_t objIndex = 0; objIndex < objectiveHelper.size(); ++objIndex) {
544 if (objectiveHelper[objIndex].getMaybeStates().get(initialState)) {
545 objectiveValueVariables.push_back(lpModel.addUnboundedContinuousVariable("x_" + std::to_string(objIndex)).getExpression());
546 lpModel.update();
547 std::vector<storm::expressions::Expression> summands;
548 for (auto const& objRew : objectiveHelper[objIndex].getChoiceRewards()) {
549 STORM_LOG_ASSERT(choiceVisitsVars[objRew.first].isInitialized(), "Choice visit variable not initialized.");
550 summands.push_back(choiceVisitsVars[objRew.first] * lpModel.getConstant(objRew.second));
551 }
552 lpModel.addConstraint("", objectiveValueVariables.back() == storm::expressions::sum(summands));
553 } else {
554 objectiveValueVariables.push_back(lpModel.getConstant(objectiveHelper[objIndex].getConstantInitialStateValue()));
555 }
556 }
557 return objectiveValueVariables;
558}
559
560template<typename HelperType>
561bool useFlowEncoding(storm::Environment const& env, std::vector<HelperType> const& objectiveHelper) {
562 bool supportsFlowEncoding = std::all_of(objectiveHelper.begin(), objectiveHelper.end(), [](auto const& h) { return h.isTotalRewardObjective(); });
563 switch (env.modelchecker().multi().getEncodingType()) {
565 return supportsFlowEncoding;
567 STORM_LOG_THROW(supportsFlowEncoding, storm::exceptions::InvalidOperationException,
568 "Flow encoding only applicable if all objectives are (transformable to) total reward objectives.");
569 return true;
570 default:
571 return false;
572 }
573}
574
575template<typename ModelType, typename GeometryValueType>
576void DeterministicSchedsLpChecker<ModelType, GeometryValueType>::initializeLpModel(Environment const& env) {
577 STORM_LOG_INFO("Initializing LP model with " << model.getNumberOfStates() << " states.");
578 flowEncoding = useFlowEncoding(env, objectiveHelper);
579 STORM_LOG_INFO("Using " << (flowEncoding ? "flow" : "classical") << " encoding.\n");
580 uint64_t initialState = *model.getInitialStates().begin();
581 auto backwardTransitions = model.getBackwardTransitions();
582 auto backwardChoices = model.getTransitionMatrix().transpose();
583 STORM_LOG_WARN_COND(!env.solver().isLpSolverTypeSetFromDefaultValue() || env.solver().getLpSolverType() == storm::solver::LpSolverType::Gurobi,
584 "The selected MILP solver might not perform well. Consider installing / using Gurobi.");
586
587 lpModel->setOptimizationDirection(storm::solver::OptimizationDirection::Maximize);
588 initialStateResults.clear();
589
590 // Create choice variables.
591 choiceVariables = createChoiceVariables(*lpModel, model.getTransitionMatrix());
592 if (flowEncoding) {
593 initialStateResults = expVisitsConstraints(*lpModel, env.modelchecker().multi().getUseIndicatorConstraints(), model.getTransitionMatrix(),
594 backwardTransitions, backwardChoices, initialState, objectiveHelper, choiceVariables);
595 } else {
596 std::vector<std::vector<storm::expressions::Expression>> objectiveValueVariables;
597 initialStateResults.clear();
598 for (uint64_t objIndex = 0; objIndex < objectiveHelper.size(); ++objIndex) {
599 if (objectiveHelper[objIndex].getMaybeStates().get(initialState)) {
600 objectiveValueVariables.push_back(classicConstraints(*lpModel, env.modelchecker().multi().getUseIndicatorConstraints(),
601 model.getTransitionMatrix(), initialState, objIndex, objectiveHelper[objIndex],
602 choiceVariables));
603 initialStateResults.push_back(objectiveValueVariables.back()[initialState]);
604 } else {
605 initialStateResults.push_back(lpModel->getConstant(objectiveHelper[objIndex].getConstantInitialStateValue()));
606 }
607 }
608 auto problematicMecs = computeProblematicMecs(model.getTransitionMatrix(), backwardTransitions, objectiveHelper);
609 uint64_t mecIndex = 0;
610 auto upperBoundsGetter = [&](uint64_t objIndex, uint64_t state) -> ValueType { return objectiveHelper[objIndex].getUpperValueBoundAtState(state); };
611 for (auto const& mecObj : problematicMecs) {
614 env.modelchecker().multi().getUseRedundantBsccConstraints(), model.getTransitionMatrix(), mecIndex, mecObj.first,
615 mecObj.second, choiceVariables, objectiveValueVariables, upperBoundsGetter);
616 } else {
618 env.modelchecker().multi().getUseRedundantBsccConstraints(), model.getTransitionMatrix(), backwardChoices,
619 mecIndex, mecObj.first, mecObj.second, objectiveValueVariables, choiceVariables, upperBoundsGetter);
620 }
621 ++mecIndex;
622 }
623 }
624 lpModel->update();
625 STORM_LOG_INFO("Done initializing LP model.");
626}
627
628template<typename ModelType, typename GeometryValueType>
629void DeterministicSchedsLpChecker<ModelType, GeometryValueType>::checkRecursive(Environment const& env,
630 storm::storage::geometry::PolytopeTree<GeometryValueType>& polytopeTree,
631 Point const& eps, std::vector<Point>& foundPoints,
632 std::vector<Polytope>& infeasableAreas, uint64_t const& depth) {
633 STORM_LOG_ASSERT(!polytopeTree.isEmpty(), "Tree node is empty.");
634 STORM_LOG_ASSERT(!polytopeTree.getPolytope()->isEmpty(), "Tree node is empty.");
635 STORM_LOG_TRACE("Checking at depth " << depth << ": " << polytopeTree.toString());
636
637 lpModel->push();
638 // Assert the constraints of the current polytope
639 auto nodeConstraints = polytopeTree.getPolytope()->getConstraints(lpModel->getManager(), currentObjectiveVariables);
640 for (auto const& constr : nodeConstraints) {
641 lpModel->addConstraint("", constr);
642 }
643 lpModel->update();
644
645 if (polytopeTree.getChildren().empty()) {
646 // At leaf nodes we need to perform the actual check.
647
648 // Numerical instabilities might yield a point that is not actually inside the nodeConstraints.
649 // If the downward closure is disjoint from this tree node, we will not make further progress.
650 // In this case, we sharpen the violating constraints just a little bit.
651 // This way, valid solutions might be excluded, so technically, this can yield false negatives.
652 // However, since we apparently are dealing with numerical algorithms, we can't be sure about correctness anyway.
653 uint64_t num_sharpen = 0;
654 auto halfspaces = polytopeTree.getPolytope()->getHalfspaces();
655 while (true) {
656 STORM_LOG_TRACE("\tSolving MILP...");
657 swCheckAreas.start();
658 lpModel->optimize();
659 swCheckAreas.stop();
660 ++numLpQueries;
661 STORM_LOG_TRACE("\tDone solving MILP...");
662
663 if (lpModel->isInfeasible()) {
664 infeasableAreas.push_back(polytopeTree.getPolytope());
665 polytopeTree.clear();
666 break;
667 } else {
668 STORM_LOG_ASSERT(!lpModel->isUnbounded(), "LP result is unbounded.");
669 swValidate.start();
670 Point newPoint = validateCurrentModel(env);
671 swValidate.stop();
672 // Check whether this new point yields any progress.
673 // There is no progress if (due to numerical inaccuracies) the downwardclosure (including points that are epsilon close to it) contained in this
674 // polytope. We multiply eps by 0.999 so that points that lie on the boundary of polytope and downw. do not count in the intersection.
675 Point newPointPlusEps = newPoint;
677 if (polytopeTree.getPolytope()->contains(newPoint) ||
678 !polytopeTree.getPolytope()
680 ->isEmpty()) {
681 GeometryValueType offset = storm::utility::convertNumber<GeometryValueType>(lpModel->getObjectiveValue());
682 // Get the gap between the found solution and the known bound.
683 offset += storm::utility::convertNumber<GeometryValueType>(lpModel->getMILPGap(false));
684 // we might want to shift the halfspace to guarantee that our point is included.
685 offset = std::max(offset, storm::utility::vector::dotProduct(currentWeightVector, newPoint));
686 auto halfspace = storm::storage::geometry::Halfspace<GeometryValueType>(currentWeightVector, offset).invert();
687 infeasableAreas.push_back(polytopeTree.getPolytope()->intersection(halfspace));
688 if (infeasableAreas.back()->isEmpty()) {
689 infeasableAreas.pop_back();
690 }
692 foundPoints.push_back(newPoint);
693 polytopeTree.substractDownwardClosure(newPoint, eps);
694 if (!polytopeTree.isEmpty()) {
695 checkRecursive(env, polytopeTree, eps, foundPoints, infeasableAreas, depth);
696 }
697 break;
698 } else {
699 // If we end up here, we have to sharpen the violated constraints for this polytope
700 for (auto& h : halfspaces) {
701 GeometryValueType distance = h.distance(newPoint);
702 // Check if the found point is outside of this halfspace
703 if (!storm::utility::isZero(distance)) {
704 // The issue has to be for some normal vector with a negative entry. Otherwise, the intersection with the downward closure wouldn't
705 // be empty
706 bool normalVectorContainsNegative = false;
707 for (auto const& hi : h.normalVector()) {
709 normalVectorContainsNegative = true;
710 break;
711 }
712 }
713 if (normalVectorContainsNegative) {
715 if (num_sharpen == 0) {
716 lpModel->push();
717 }
718 lpModel->addConstraint("", h.toExpression(lpModel->getManager(), currentObjectiveVariables));
719 ++num_sharpen;
720 break;
721 }
722 }
723 }
724 STORM_LOG_TRACE("\tSharpened LP");
725 }
726 }
727 }
728 if (num_sharpen > 0) {
730 "Numerical instabilities detected: LP Solver found an achievable point outside of the search area. The search area had to be sharpened "
731 << num_sharpen << " times.");
732 // pop sharpened constraints
733 lpModel->pop();
734 }
735
736 } else {
737 // Traverse all the non-empty children.
738 for (uint64_t childId = 0; childId < polytopeTree.getChildren().size(); ++childId) {
739 if (polytopeTree.getChildren()[childId].isEmpty()) {
740 continue;
741 }
742 uint64_t newPointIndex = foundPoints.size();
743 checkRecursive(env, polytopeTree.getChildren()[childId], eps, foundPoints, infeasableAreas, depth + 1);
744 STORM_LOG_ASSERT(polytopeTree.getChildren()[childId].isEmpty(), "Expected empty children.");
745 // Make the new points known to the right siblings
746 for (; newPointIndex < foundPoints.size(); ++newPointIndex) {
747 for (uint64_t siblingId = childId + 1; siblingId < polytopeTree.getChildren().size(); ++siblingId) {
748 polytopeTree.getChildren()[siblingId].substractDownwardClosure(foundPoints[newPointIndex], eps);
749 }
750 }
751 }
752 // All children are empty now, so this node becomes empty.
753 polytopeTree.clear();
754 }
755 STORM_LOG_TRACE("Checking DONE at depth " << depth << " with node " << polytopeTree.toString());
756
757 lpModel->pop();
758 lpModel->update();
759}
760
761template<typename ModelType, typename GeometryValueType>
762typename DeterministicSchedsLpChecker<ModelType, GeometryValueType>::Point DeterministicSchedsLpChecker<ModelType, GeometryValueType>::validateCurrentModel(
763 Environment const& env) const {
764 storm::storage::BitVector selectedChoices(model.getNumberOfChoices(), false);
765 for (uint64_t state = 0; state < model.getNumberOfStates(); ++state) {
766 auto choices = model.getTransitionMatrix().getRowGroupIndices(state);
767 if (choices.size() == 1) {
768 selectedChoices.set(*choices.begin());
769 } else {
770 bool choiceFound = false;
771 for (auto choice : choices) {
772 STORM_LOG_ASSERT(choiceVariables[choice].isVariable(), "Choice variable is not a variable.");
773 if (lpModel->getBinaryValue(choiceVariables[choice].getBaseExpression().asVariableExpression().getVariable())) {
774 STORM_LOG_THROW(!choiceFound, storm::exceptions::UnexpectedException, "Multiple choices selected at state " << state << ".");
775 selectedChoices.set(choice, true);
776 choiceFound = true;
777 }
778 }
779 }
780 }
781
782 Point inducedPoint;
783 for (uint64_t objIndex = 0; objIndex < objectiveHelper.size(); ++objIndex) {
784 ValueType inducedValue = objectiveHelper[objIndex].evaluateScheduler(env, selectedChoices);
785 inducedPoint.push_back(storm::utility::convertNumber<GeometryValueType>(inducedValue));
786 // If this objective has weight zero, the lp solution is not necessarily correct
787 if (!storm::utility::isZero(currentWeightVector[objIndex])) {
788 ValueType lpValue = lpModel->getContinuousValue(currentObjectiveVariables[objIndex]);
789 double diff = storm::utility::convertNumber<double>(storm::utility::abs<ValueType>(inducedValue - lpValue));
790 STORM_LOG_WARN_COND(diff <= 1e-4 * std::abs(storm::utility::convertNumber<double>(inducedValue)),
791 "Imprecise value for objective " << objIndex << ": LP says " << lpValue << " but scheduler induces " << inducedValue
792 << " (difference is " << diff << ")");
793 }
794 }
795 return inducedPoint;
796}
797
798template class DeterministicSchedsLpChecker<storm::models::sparse::Mdp<double>, storm::RationalNumber>;
802} // namespace storm::modelchecker::multiobjective
SolverEnvironment & solver()
ModelCheckerEnvironment & modelchecker()
MultiObjectiveModelCheckerEnvironment & multi()
bool isLpSolverTypeSetFromDefaultValue() const
storm::solver::LpSolverType const & getLpSolverType() const
Represents the LP Encoding for achievability under simple strategies.
void setCurrentWeightVector(Environment const &env, std::vector< GeometryValueType > const &weightVector)
Specifies the current direction.
std::string getStatistics(std::string const &prefix="") const
Returns usage statistics in a human readable format.
DeterministicSchedsLpChecker(ModelType const &model, std::vector< DeterministicSchedsObjectiveHelper< ModelType > > const &objectiveHelper)
std::optional< std::pair< Point, GeometryValueType > > check(storm::Environment const &env, Polytope overapproximation, Point const &eps={})
Optimizes in the currently given direction.
std::shared_ptr< storm::storage::geometry::Polytope< GeometryValueType > > Polytope
static ValueType computeMecVisitsUpperBound(storm::storage::MaximalEndComponent const &mec, storm::storage::SparseMatrix< ValueType > const &transitions, bool assumeOptimalTransitionProbabilities=false)
Computes an upper bound for the largest finite expected number of times a state s in the given MEC is...
static std::vector< ValueType > computeUpperBoundsOnExpectedVisitingTimes(storm::storage::BitVector const &subsystem, storm::storage::SparseMatrix< ValueType > const &transitions, storm::storage::SparseMatrix< ValueType > const &backwardTransitions)
Computes for each state in the given subsystem an upper bound for the maximal finite expected number ...
An interface that captures the functionality of an LP solver.
Definition LpSolver.h:50
Variable addContinuousVariable(std::string const &name, std::optional< ValueType > const &lowerBound=std::nullopt, std::optional< ValueType > const &upperBound=std::nullopt, ValueType objectiveFunctionCoefficient=0)
Registers a continuous variable, i.e.
Definition LpSolver.cpp:62
virtual void update() const =0
Updates the model to make the variables that have been declared since the last call to update usable.
virtual void addConstraint(std::string const &name, Constraint const &constraint)=0
Adds a the given constraint to the LP problem.
Variable addLowerBoundedContinuousVariable(std::string const &name, ValueType lowerBound, ValueType objectiveFunctionCoefficient=0)
Registers a lower-bounded continuous variable, i.e.
Definition LpSolver.cpp:44
Constant getConstant(ValueType value) const
Retrieves an expression that characterizes the given constant value.
Definition LpSolver.cpp:108
virtual void addIndicatorConstraint(std::string const &name, Variable indicatorVariable, bool indicatorValue, Constraint const &constraint)=0
Adds the given indicator constraint to the LP problem: "If indicatorVariable == indicatorValue,...
Variable addBoundedContinuousVariable(std::string const &name, ValueType lowerBound, ValueType upperBound, ValueType objectiveFunctionCoefficient=0)
Registers an upper- and lower-bounded continuous variable, i.e.
Definition LpSolver.cpp:37
Variable addUnboundedContinuousVariable(std::string const &name, ValueType objectiveFunctionCoefficient=0)
Registers a unbounded continuous variable, i.e.
Definition LpSolver.cpp:56
Variable addBinaryVariable(std::string const &name, ValueType objectiveFunctionCoefficient=0)
Registers a boolean variable, i.e.
Definition LpSolver.cpp:102
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
void set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
bool get(uint64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
This class represents the decomposition of a nondeterministic model into its maximal end components.
This class represents a maximal end-component of a nondeterministic model.
index_type getNumberOfEntries() const
Retrieves the number of entries in the rows.
A class that holds a possibly non-square matrix in the compressed row storage format.
const_rows getRow(index_type row) const
Returns an object representing the given row.
index_type getRowGroupCount() const
Returns the number of row groups in the matrix.
value_type getConstrainedRowSum(index_type row, storm::storage::BitVector const &columns) const
Sums the entries in the given row and columns.
std::vector< index_type > const & getRowGroupIndices() const
Returns the grouping of rows of this matrix.
index_type getRowCount() const
Returns the number of rows of the matrix.
static std::shared_ptr< Polytope< ValueType > > createDownwardClosure(std::vector< Point > const &points)
Creates the downward closure of the given points (i.e., the set { x | ex.
Definition Polytope.cpp:40
static std::shared_ptr< Polytope< ValueType > > create(std::vector< Halfspace< ValueType > > const &halfspaces)
Creates a polytope from the given halfspaces.
Represents a set of points in Euclidean space.
std::shared_ptr< Polytope< ValueType > > & getPolytope()
Gets the polytope at this node.
void substractDownwardClosure(std::vector< ValueType > const &point)
Substracts the downward closure of the given point from this set.
void setMinus(std::shared_ptr< Polytope< ValueType > > const &rhs)
Substracts the given rhs from this polytope.
bool isEmpty() const
Returns true if this is the empty set.
std::vector< PolytopeTree > & getChildren()
Gets the children at this node.
std::string toString()
Returns a string representation of this node (for debugging purposes).
void clear()
Clears all contents of this set, making it the empty set.
#define STORM_LOG_INFO(message)
Definition logging.h:27
#define STORM_LOG_WARN(message)
Definition logging.h:28
#define STORM_LOG_DEBUG(message)
Definition logging.h:21
#define STORM_LOG_TRACE(message)
Definition logging.h:15
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_WARN_COND(cond, message)
Definition macros.h:36
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
SFTBDDChecker::ValueType ValueType
Expression sum(std::vector< storm::expressions::Expression > const &expressions)
std::vector< storm::expressions::Expression > expVisitsConstraints(storm::solver::LpSolver< ValueType > &lpModel, bool const &indicatorConstraints, storm::storage::SparseMatrix< ValueType > const &matrix, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, storm::storage::SparseMatrix< ValueType > const &backwardChoices, uint64_t initialState, std::vector< HelperType > const &objectiveHelper, std::vector< storm::expressions::Expression > const &choiceVariables)
auto createChoiceVariables(storm::solver::LpSolver< ValueType > &lpModel, storm::storage::SparseMatrix< ValueType > const &matrix)
auto computeProblematicMecs(storm::storage::SparseMatrix< ValueType > const &matrix, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, std::vector< ObjHelperType > const &objectiveHelper)
Computes the set of problematic MECS with the objective indices that induced them An EC is problemati...
auto problematicMecConstraintsExpVisits(storm::solver::LpSolver< ValueType > &lpModel, bool const &indicatorConstraints, bool const &redundantConstraints, storm::storage::SparseMatrix< ValueType > const &matrix, storm::storage::SparseMatrix< ValueType > const &backwardChoices, uint64_t mecIndex, storm::storage::MaximalEndComponent const &problematicMec, std::vector< uint64_t > const &relevantObjectiveIndices, std::vector< std::vector< storm::expressions::Expression > > const &objectiveValueVariables, std::vector< storm::expressions::Expression > const &choiceVariables, UpperBoundsGetterType const &objectiveStateUpperBoundGetter)
std::vector< storm::expressions::Expression > classicConstraints(storm::solver::LpSolver< ValueType > &lpModel, bool const &indicatorConstraints, storm::storage::SparseMatrix< ValueType > const &matrix, uint64_t initialState, uint64_t objIndex, HelperType const &objectiveHelper, std::vector< storm::expressions::Expression > const &choiceVariables)
bool useFlowEncoding(storm::Environment const &env, std::vector< HelperType > const &objectiveHelper)
auto problematicMecConstraintsOrder(storm::solver::LpSolver< ValueType > &lpModel, bool const &indicatorConstraints, bool const &redundantConstraints, storm::storage::SparseMatrix< ValueType > const &matrix, uint64_t mecIndex, storm::storage::MaximalEndComponent const &problematicMec, std::vector< uint64_t > const &relevantObjectiveIndices, std::vector< storm::expressions::Expression > const &choiceVariables, std::vector< std::vector< storm::expressions::Expression > > const &objectiveValueVariables, UpperBoundsGetterType const &objectiveStateUpperBoundGetter)
std::unique_ptr< storm::solver::LpSolver< ValueType > > getLpSolver(storm::Environment const &env, std::string const &name, storm::solver::LpSolverTypeSelection solvType)
Definition solver.cpp:146
T dotProduct(std::vector< T > const &firstOperand, std::vector< T > const &secondOperand)
Computes the dot product (aka scalar product) and returns the result.
Definition vector.h:473
void addScaledVector(std::vector< InValueType1 > &firstOperand, std::vector< InValueType2 > const &secondOperand, InValueType3 const &factor)
Computes x:= x + a*y, i.e., adds each element of the first vector and (the corresponding element of t...
Definition vector.h:460
bool isOne(ValueType const &a)
Definition constants.cpp:37
bool isZero(ValueType const &a)
Definition constants.cpp:42
ValueType abs(ValueType const &number)
ValueType zero()
Definition constants.cpp:24
ValueType one()
Definition constants.cpp:19
TargetType convertNumber(SourceType const &number)