Skip to content

Commit

Permalink
Revised LP Encoding for Multi-objective verification under simple str…
Browse files Browse the repository at this point in the history
…ategies (#468)

* Update findGUROBI.cmake to find Gurobi 11

* Revised implementation of multi-objective Achievability under simple strategies

* Added support for achievability queries
  • Loading branch information
tquatmann authored Dec 14, 2023
1 parent 373f9bf commit fdace26
Show file tree
Hide file tree
Showing 18 changed files with 2,007 additions and 1,473 deletions.
5 changes: 4 additions & 1 deletion resources/cmake/find_modules/FindGUROBI.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ else (GUROBI_INCLUDE_DIR)
find_path(GUROBI_INCLUDE_DIR
NAMES gurobi_c++.h
PATHS "$ENV{GUROBI_HOME}/include"
"/Library/gurobi1100/macos_universal2/include"
"/Library/gurobi1000/macos_universal2/include"
"/Library/gurobi951/macos_universal2/include"
"/Library/gurobi950/mac64/include"
Expand All @@ -40,6 +41,7 @@ find_path(GUROBI_INCLUDE_DIR

find_library( GUROBI_LIBRARY
NAMES gurobi
gurobi110
gurobi100
gurobi95
gurobi91
Expand All @@ -58,6 +60,7 @@ find_library( GUROBI_LIBRARY
gurobi46
gurobi45
PATHS "$ENV{GUROBI_HOME}/lib"
"/Library/gurobi1100/macos_universal2/lib"
"/Library/gurobi1000/macos_universal2/lib"
"/Library/gurobi951/macos_universal2/lib"
"/Library/gurobi950/mac64/lib"
Expand All @@ -81,7 +84,7 @@ find_library( GUROBI_LIBRARY
find_library( GUROBI_CXX_LIBRARY
NAMES gurobi_c++
PATHS "$ENV{GUROBI_HOME}/lib"
"/Library/gurobi1000/macos_universal2/lib"
"/Library/gurobi1100/macos_universal2/lib"
"/Library/gurobi951/macos_universal2/lib"
"/Library/gurobi950/mac64/lib"
"/Library/gurobi912/mac64/lib"
Expand Down
19 changes: 19 additions & 0 deletions resources/examples/testfiles/mdp/multiobj_infrew.nm
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
mdp
module main

x : [0..2];
[a] x=0 -> (x'=1);
[b] x=0 -> (x'=2);
[c] x=1 -> (x'=1);
[d] x=1 -> (x'=2);
[e] x=2 -> (x'=1);
endmodule

rewards "one"
[b] true : 10;
[c] true : 1;
endrewards

rewards "two"
[e] true : 1;
endrewards
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ MultiObjectiveModelCheckerEnvironment::MultiObjectiveModelCheckerEnvironment() {
} else if (multiobjectiveSettings.isFlowEncodingSet()) {
encodingType = EncodingType::Flow;
}
STORM_LOG_ASSERT(multiobjectiveSettings.isBsccDetectionViaOrderConstraintsSet() || multiobjectiveSettings.isBsccDetectionViaFlowConstraintsSet(),
"unexpected settings");
bsccOrderEncoding = multiobjectiveSettings.isBsccDetectionViaOrderConstraintsSet();
STORM_LOG_ASSERT(multiobjectiveSettings.isIndicatorConstraintsSet() || multiobjectiveSettings.isBigMConstraintsSet(), "unexpected settings");
indicatorConstraints = multiobjectiveSettings.isIndicatorConstraintsSet();
redundantBsccConstraints = multiobjectiveSettings.isRedundantBsccConstraintsSet();

if (multiobjectiveSettings.isMaxStepsSet()) {
maxSteps = multiobjectiveSettings.getMaxSteps();
Expand Down Expand Up @@ -121,6 +127,28 @@ void MultiObjectiveModelCheckerEnvironment::setEncodingType(EncodingType const&
encodingType = value;
}

bool MultiObjectiveModelCheckerEnvironment::getUseIndicatorConstraints() const {
return indicatorConstraints;
}
void MultiObjectiveModelCheckerEnvironment::setUseIndicatorConstraints(bool value) {
indicatorConstraints = value;
}

bool MultiObjectiveModelCheckerEnvironment::getUseBsccOrderEncoding() const {
return bsccOrderEncoding;
}
void MultiObjectiveModelCheckerEnvironment::setUseBsccOrderEncoding(bool value) {
bsccOrderEncoding = value;
}

bool MultiObjectiveModelCheckerEnvironment::getUseRedundantBsccConstraints() const {
return redundantBsccConstraints;
}

void MultiObjectiveModelCheckerEnvironment::setUseRedundantBsccConstraints(bool value) {
redundantBsccConstraints = value;
}

bool MultiObjectiveModelCheckerEnvironment::isMaxStepsSet() const {
return maxSteps.is_initialized();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ class MultiObjectiveModelCheckerEnvironment {
EncodingType const& getEncodingType() const;
void setEncodingType(EncodingType const& value);

bool getUseIndicatorConstraints() const;
void setUseIndicatorConstraints(bool value);

bool getUseBsccOrderEncoding() const;
void setUseBsccOrderEncoding(bool value);

bool getUseRedundantBsccConstraints() const;
void setUseRedundantBsccConstraints(bool value);

bool isMaxStepsSet() const;
uint64_t const& getMaxSteps() const;
void setMaxSteps(uint64_t const& value);
Expand All @@ -69,6 +78,9 @@ class MultiObjectiveModelCheckerEnvironment {
storm::RationalNumber precision;
PrecisionType precisionType;
EncodingType encodingType;
bool indicatorConstraints;
bool bsccOrderEncoding;
bool redundantBsccConstraints;
boost::optional<uint64_t> maxSteps;
boost::optional<storm::storage::SchedulerClass> schedulerRestriction;
bool printResults;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#include "storm/modelchecker/multiobjective/deterministicScheds/DeterministicSchedsAchievabilityChecker.h"

#include "storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h"
#include "storm/modelchecker/multiobjective/deterministicScheds/DeterministicSchedsLpChecker.h"
#include "storm/modelchecker/multiobjective/deterministicScheds/DeterministicSchedsObjectiveHelper.h"
#include "storm/modelchecker/multiobjective/preprocessing/SparseMultiObjectivePreprocessorResult.h"
#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
#include "storm/models/sparse/MarkovAutomaton.h"
#include "storm/models/sparse/Mdp.h"
#include "storm/models/sparse/StandardRewardModel.h"

namespace storm::modelchecker::multiobjective {

template<class SparseModelType, typename GeometryValueType>
DeterministicSchedsAchievabilityChecker<SparseModelType, GeometryValueType>::DeterministicSchedsAchievabilityChecker(
preprocessing::SparseMultiObjectivePreprocessorResult<SparseModelType>& preprocessorResult)
: model(preprocessorResult.preprocessedModel), originalModelInitialState(*preprocessorResult.originalModel.getInitialStates().begin()) {
for (auto const& obj : preprocessorResult.objectives) {
objectiveHelper.emplace_back(*model, obj);
if (!objectiveHelper.back().hasThreshold()) {
STORM_LOG_ASSERT(!optimizingObjectiveIndex.has_value(),
"Unexpected input: got a (quantitative) achievability query but there are more than one objectives without a threshold.");
optimizingObjectiveIndex = objectiveHelper.size() - 1;
}
}
lpChecker = std::make_shared<DeterministicSchedsLpChecker<SparseModelType, GeometryValueType>>(*model, objectiveHelper);
}

template<class SparseModelType, typename GeometryValueType>
std::unique_ptr<CheckResult> DeterministicSchedsAchievabilityChecker<SparseModelType, GeometryValueType>::check(Environment const& env) {
using Point = typename std::vector<GeometryValueType>;
uint64_t const numObj = objectiveHelper.size();
// Create a polytope that contains all the points that would certify achievability of the point induced by the objective threshold
std::vector<storm::storage::geometry::Halfspace<GeometryValueType>> thresholdHalfspaces;
std::vector<GeometryValueType> objVector;
for (uint64_t objIndex = 0; objIndex < numObj; ++objIndex) {
auto& obj = objectiveHelper[objIndex];
obj.computeLowerUpperBounds(env);
if (!optimizingObjectiveIndex.has_value() || *optimizingObjectiveIndex != objIndex) {
objVector.assign(numObj, storm::utility::zero<GeometryValueType>());
objVector[objIndex] = -storm::utility::one<GeometryValueType>();
thresholdHalfspaces.emplace_back(objVector, storm::utility::convertNumber<GeometryValueType, ModelValueType>(-obj.getThreshold()));
}
}
auto thresholdPolytope = storm::storage::geometry::Polytope<GeometryValueType>::create(std::move(thresholdHalfspaces));

// Set objective weights (none if this is a qualitative achievability query)
objVector.assign(numObj, storm::utility::zero<GeometryValueType>());
auto eps = objVector;
if (optimizingObjectiveIndex.has_value()) {
objVector[*optimizingObjectiveIndex] = storm::utility::one<GeometryValueType>();
eps[*optimizingObjectiveIndex] = env.modelchecker().multi().getPrecision() * storm::utility::convertNumber<GeometryValueType, uint64_t>(2u);
if (env.modelchecker().multi().getPrecisionType() == MultiObjectiveModelCheckerEnvironment::PrecisionType::RelativeToDiff) {
eps[*optimizingObjectiveIndex] *= storm::utility::convertNumber<GeometryValueType, ModelValueType>(
objectiveHelper[*optimizingObjectiveIndex].getUpperValueBoundAtState(originalModelInitialState) -
objectiveHelper[*optimizingObjectiveIndex].getLowerValueBoundAtState(originalModelInitialState));
}
STORM_LOG_INFO("Computing quantitative achievability value with precision " << eps[*optimizingObjectiveIndex] << ".");
}
lpChecker->setCurrentWeightVector(env, objVector);

// Search achieving point
auto achievingPoint = lpChecker->check(env, thresholdPolytope, eps);
bool const isAchievable = achievingPoint.has_value();
if (isAchievable) {
STORM_LOG_INFO(
"Found achievable point: " << storm::utility::vector::toString(achievingPoint->first) << " ( approx. "
<< storm::utility::vector::toString(storm::utility::vector::convertNumericVector<double>(achievingPoint->first))
<< " ).");
if (optimizingObjectiveIndex.has_value()) {
using ValueType = typename SparseModelType::ValueType;
// Average between obtained lower- and upper bounds
auto result =
storm::utility::convertNumber<ValueType, GeometryValueType>(achievingPoint->first[*optimizingObjectiveIndex] + achievingPoint->second);
result /= storm::utility::convertNumber<ValueType, uint64_t>(2u);
if (objectiveHelper[*optimizingObjectiveIndex].minimizing()) {
result *= -storm::utility::one<ValueType>();
}
return std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ValueType>>(originalModelInitialState, result);
}
}
return std::make_unique<storm::modelchecker::ExplicitQualitativeCheckResult>(originalModelInitialState, isAchievable);
}

template class DeterministicSchedsAchievabilityChecker<storm::models::sparse::Mdp<double>, storm::RationalNumber>;
template class DeterministicSchedsAchievabilityChecker<storm::models::sparse::Mdp<storm::RationalNumber>, storm::RationalNumber>;
template class DeterministicSchedsAchievabilityChecker<storm::models::sparse::MarkovAutomaton<double>, storm::RationalNumber>;
template class DeterministicSchedsAchievabilityChecker<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>, storm::RationalNumber>;
} // namespace storm::modelchecker::multiobjective
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma once

#include <memory>
#include <optional>
#include <vector>

namespace storm {
class Environment;

namespace modelchecker {

class CheckResult;

namespace multiobjective {
template<typename SparseModelType, typename GeometryValueType>
class DeterministicSchedsLpChecker;

template<typename SparseModelType>
class DeterministicSchedsObjectiveHelper;

namespace preprocessing {
template<typename SparseModelType>
class SparseMultiObjectivePreprocessorResult;
}

template<class SparseModelType, typename GeometryValueType>
class DeterministicSchedsAchievabilityChecker {
public:
typedef typename SparseModelType::ValueType ModelValueType;

DeterministicSchedsAchievabilityChecker(preprocessing::SparseMultiObjectivePreprocessorResult<SparseModelType>& preprocessorResult);

virtual std::unique_ptr<CheckResult> check(Environment const& env);

private:
std::shared_ptr<DeterministicSchedsLpChecker<SparseModelType, GeometryValueType>> lpChecker;
std::vector<DeterministicSchedsObjectiveHelper<SparseModelType>> objectiveHelper;

std::shared_ptr<SparseModelType> model;
uint64_t const originalModelInitialState;
std::optional<uint64_t> optimizingObjectiveIndex;
};

} // namespace multiobjective
} // namespace modelchecker
} // namespace storm
Loading

0 comments on commit fdace26

Please sign in to comment.