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