From 92ee6187fa41695068ad7746ca4ad7830c3b645e Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Tue, 6 May 2014 17:20:00 +0200 Subject: [PATCH 01/15] Added more query methods to expressions. SparseMatrix now keeps track of non zero entries and models show correct number of transitions by referring to nonzero entries rather than all entries in the matrix. Former-commit-id: 48180be2fe33c7f12ad0ef20b1b7800910a73935 --- src/models/AbstractModel.h | 2 +- src/storage/SparseMatrix.cpp | 29 ++++++++++++---- src/storage/SparseMatrix.h | 10 ++++++ src/storage/expressions/BaseExpression.cpp | 8 +++++ src/storage/expressions/BaseExpression.h | 16 +++++++++ .../BinaryBooleanFunctionExpression.cpp | 10 ++++++ .../BinaryBooleanFunctionExpression.h | 1 + src/storage/expressions/BinaryExpression.cpp | 6 +++- src/storage/expressions/BinaryExpression.h | 1 + .../BinaryNumericalFunctionExpression.cpp | 11 +++++++ .../BinaryNumericalFunctionExpression.h | 1 + .../expressions/BinaryRelationExpression.cpp | 13 +++++++- .../expressions/BinaryRelationExpression.h | 1 + src/storage/expressions/Expression.cpp | 8 +++++ src/storage/expressions/Expression.h | 15 +++++++++ .../expressions/IfThenElseExpression.cpp | 30 +++++++++++++++++ .../expressions/IfThenElseExpression.h | 5 +++ src/storage/expressions/OperatorType.h | 33 +++++++++++++++++++ .../UnaryBooleanFunctionExpression.cpp | 6 ++++ .../UnaryBooleanFunctionExpression.h | 1 + src/storage/expressions/UnaryExpression.cpp | 4 +++ src/storage/expressions/UnaryExpression.h | 1 + .../UnaryNumericalFunctionExpression.cpp | 8 +++++ .../UnaryNumericalFunctionExpression.h | 1 + .../GmmxxDtmcPrctlModelCheckerTest.cpp | 6 ++-- test/functional/parser/AutoParserTest.cpp | 12 +++---- .../parser/DeterministicModelParserTest.cpp | 4 +-- .../GmmxxDtmcPrctModelCheckerTest.cpp | 4 +-- 28 files changed, 224 insertions(+), 23 deletions(-) create mode 100644 src/storage/expressions/OperatorType.h diff --git a/src/models/AbstractModel.h b/src/models/AbstractModel.h index 301c806d3..05296c584 100644 --- a/src/models/AbstractModel.h +++ b/src/models/AbstractModel.h @@ -220,7 +220,7 @@ class AbstractModel: public std::enable_shared_from_this<AbstractModel<T>> { * @return The number of (non-zero) transitions of the model. */ virtual uint_fast64_t getNumberOfTransitions() const { - return this->getTransitionMatrix().getEntryCount(); + return this->getTransitionMatrix().getNonzeroEntryCount(); } /*! diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index 6955ec053..e4d719006 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -218,17 +218,17 @@ namespace storm { } template<typename T> - SparseMatrix<T>::SparseMatrix() : rowCount(0), columnCount(0), entryCount(0), columnsAndValues(), rowIndications(), rowGroupIndices() { + SparseMatrix<T>::SparseMatrix() : rowCount(0), columnCount(0), entryCount(0), nonzeroEntryCount(0), columnsAndValues(), rowIndications(), rowGroupIndices() { // Intentionally left empty. } template<typename T> - SparseMatrix<T>::SparseMatrix(SparseMatrix<T> const& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), columnsAndValues(other.columnsAndValues), rowIndications(other.rowIndications), rowGroupIndices(other.rowGroupIndices) { + SparseMatrix<T>::SparseMatrix(SparseMatrix<T> const& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(other.columnsAndValues), rowIndications(other.rowIndications), rowGroupIndices(other.rowGroupIndices) { // Intentionally left empty. } template<typename T> - SparseMatrix<T>::SparseMatrix(SparseMatrix<T>&& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), columnsAndValues(std::move(other.columnsAndValues)), rowIndications(std::move(other.rowIndications)), rowGroupIndices(std::move(other.rowGroupIndices)) { + SparseMatrix<T>::SparseMatrix(SparseMatrix<T>&& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(std::move(other.columnsAndValues)), rowIndications(std::move(other.rowIndications)), rowGroupIndices(std::move(other.rowGroupIndices)) { // Now update the source matrix other.rowCount = 0; other.columnCount = 0; @@ -236,13 +236,21 @@ namespace storm { } template<typename T> - SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<std::pair<uint_fast64_t, T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), columnsAndValues(columnsAndValues), rowIndications(rowIndications), rowGroupIndices(rowGroupIndices) { - // Intentionally left empty. + SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<std::pair<uint_fast64_t, T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(columnsAndValues), rowIndications(rowIndications), rowGroupIndices(rowGroupIndices) { + for (auto const& element : *this) { + if (element.second != storm::utility::constantZero<T>()) { + ++this->nonzeroEntryCount; + } + } } template<typename T> - SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<std::pair<uint_fast64_t, T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)), rowGroupIndices(std::move(rowGroupIndices)) { - // Intentionally left empty. + SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<std::pair<uint_fast64_t, T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)), rowGroupIndices(std::move(rowGroupIndices)) { + for (auto const& element : *this) { + if (element.second != storm::utility::constantZero<T>()) { + ++this->nonzeroEntryCount; + } + } } template<typename T> @@ -252,6 +260,7 @@ namespace storm { rowCount = other.rowCount; columnCount = other.columnCount; entryCount = other.entryCount; + nonzeroEntryCount = other.nonzeroEntryCount; columnsAndValues = other.columnsAndValues; rowIndications = other.rowIndications; @@ -268,6 +277,7 @@ namespace storm { rowCount = other.rowCount; columnCount = other.columnCount; entryCount = other.entryCount; + nonzeroEntryCount = other.nonzeroEntryCount; columnsAndValues = std::move(other.columnsAndValues); rowIndications = std::move(other.rowIndications); @@ -330,6 +340,11 @@ namespace storm { return entryCount; } + template<typename T> + uint_fast64_t SparseMatrix<T>::getNonzeroEntryCount() const { + return nonzeroEntryCount; + } + template<typename T> uint_fast64_t SparseMatrix<T>::getRowGroupCount() const { return rowGroupIndices.size() - 1; diff --git a/src/storage/SparseMatrix.h b/src/storage/SparseMatrix.h index f85dfdd52..b8b8b80d3 100644 --- a/src/storage/SparseMatrix.h +++ b/src/storage/SparseMatrix.h @@ -339,6 +339,13 @@ namespace storm { */ uint_fast64_t getEntryCount() const; + /*! + * Returns the number of nonzero entries in the matrix. + * + * @return The number of nonzero entries in the matrix. + */ + uint_fast64_t getNonzeroEntryCount() const; + /*! * Returns the number of row groups in the matrix. * @@ -651,6 +658,9 @@ namespace storm { // The number of entries in the matrix. uint_fast64_t entryCount; + // The number of nonzero entries in the matrix. + uint_fast64_t nonzeroEntryCount; + // The storage for the columns and values of all entries in the matrix. std::vector<std::pair<uint_fast64_t, T>> columnsAndValues; diff --git a/src/storage/expressions/BaseExpression.cpp b/src/storage/expressions/BaseExpression.cpp index 2aef5ab6e..c2f4a5ac3 100644 --- a/src/storage/expressions/BaseExpression.cpp +++ b/src/storage/expressions/BaseExpression.cpp @@ -49,6 +49,10 @@ namespace storm { LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to access identifier of non-constant, non-variable expression."); } + OperatorType BaseExpression::getOperator() const { + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to access operator of non-function application expression."); + } + bool BaseExpression::containsVariables() const { return false; } @@ -69,6 +73,10 @@ namespace storm { return false; } + bool BaseExpression::isFunctionApplication() const { + return false; + } + std::shared_ptr<BaseExpression const> BaseExpression::getSharedPointer() const { return this->shared_from_this(); } diff --git a/src/storage/expressions/BaseExpression.h b/src/storage/expressions/BaseExpression.h index 79dc09c1d..6a02b35ab 100644 --- a/src/storage/expressions/BaseExpression.h +++ b/src/storage/expressions/BaseExpression.h @@ -8,6 +8,7 @@ #include "src/storage/expressions/Valuation.h" #include "src/storage/expressions/ExpressionVisitor.h" +#include "src/storage/expressions/OperatorType.h" #include "src/exceptions/InvalidArgumentException.h" #include "src/utility/OsDetection.h" @@ -96,6 +97,14 @@ namespace storm { */ virtual std::string const& getIdentifier() const; + /*! + * Retrieves the operator of a function application. This is only legal to call if the expression is + * function application. + * + * @return The operator associated with the function application. + */ + virtual OperatorType getOperator() const; + /*! * Retrieves whether the expression contains a variable. * @@ -131,6 +140,13 @@ namespace storm { */ virtual bool isFalse() const; + /*! + * Checks if the expression is a function application (of any sort). + * + * @return True iff the expression is a function application. + */ + virtual bool isFunctionApplication() const; + /*! * Retrieves the set of all variables that appear in the expression. * diff --git a/src/storage/expressions/BinaryBooleanFunctionExpression.cpp b/src/storage/expressions/BinaryBooleanFunctionExpression.cpp index e270293a8..061c231af 100644 --- a/src/storage/expressions/BinaryBooleanFunctionExpression.cpp +++ b/src/storage/expressions/BinaryBooleanFunctionExpression.cpp @@ -13,6 +13,16 @@ namespace storm { return this->operatorType; } + storm::expressions::OperatorType BinaryBooleanFunctionExpression::getOperator() const { + switch (this->getOperatorType()) { + case OperatorType::And: return storm::expressions::OperatorType::And; break; + case OperatorType::Or: return storm::expressions::OperatorType::Or; break; + case OperatorType::Xor: return storm::expressions::OperatorType::Xor; break; + case OperatorType::Implies: return storm::expressions::OperatorType::Implies; break; + case OperatorType::Iff: return storm::expressions::OperatorType::Iff; break; + } + } + bool BinaryBooleanFunctionExpression::evaluateAsBool(Valuation const* valuation) const { LOG_THROW(this->hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as boolean."); diff --git a/src/storage/expressions/BinaryBooleanFunctionExpression.h b/src/storage/expressions/BinaryBooleanFunctionExpression.h index 8b1bb7437..2a1d2417d 100644 --- a/src/storage/expressions/BinaryBooleanFunctionExpression.h +++ b/src/storage/expressions/BinaryBooleanFunctionExpression.h @@ -33,6 +33,7 @@ namespace storm { virtual ~BinaryBooleanFunctionExpression() = default; // Override base class methods. + virtual storm::expressions::OperatorType getOperator() const override; virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; virtual void accept(ExpressionVisitor* visitor) const override; diff --git a/src/storage/expressions/BinaryExpression.cpp b/src/storage/expressions/BinaryExpression.cpp index 0ca77aa0c..4d3c4be81 100644 --- a/src/storage/expressions/BinaryExpression.cpp +++ b/src/storage/expressions/BinaryExpression.cpp @@ -9,6 +9,10 @@ namespace storm { // Intentionally left empty. } + bool BinaryExpression::isFunctionApplication() const { + return true; + } + bool BinaryExpression::containsVariables() const { return this->getFirstOperand()->containsVariables() || this->getSecondOperand()->containsVariables(); } @@ -34,7 +38,7 @@ namespace storm { std::shared_ptr<BaseExpression const> BinaryExpression::getOperand(uint_fast64_t operandIndex) const { LOG_THROW(operandIndex < 2, storm::exceptions::InvalidAccessException, "Unable to access operand " << operandIndex << " in expression of arity 2."); - if (operandIndex == 1) { + if (operandIndex == 0) { return this->getFirstOperand(); } else { return this->getSecondOperand(); diff --git a/src/storage/expressions/BinaryExpression.h b/src/storage/expressions/BinaryExpression.h index dd81343ac..75c9283cb 100644 --- a/src/storage/expressions/BinaryExpression.h +++ b/src/storage/expressions/BinaryExpression.h @@ -30,6 +30,7 @@ namespace storm { virtual ~BinaryExpression() = default; // Override base class methods. + virtual bool isFunctionApplication() const override; virtual bool containsVariables() const override; virtual uint_fast64_t getArity() const override; virtual std::shared_ptr<BaseExpression const> getOperand(uint_fast64_t operandIndex) const override; diff --git a/src/storage/expressions/BinaryNumericalFunctionExpression.cpp b/src/storage/expressions/BinaryNumericalFunctionExpression.cpp index 225bea963..f6172821d 100644 --- a/src/storage/expressions/BinaryNumericalFunctionExpression.cpp +++ b/src/storage/expressions/BinaryNumericalFunctionExpression.cpp @@ -14,6 +14,17 @@ namespace storm { return this->operatorType; } + storm::expressions::OperatorType BinaryNumericalFunctionExpression::getOperator() const { + switch (this->getOperatorType()) { + case OperatorType::Plus: return storm::expressions::OperatorType::Plus; break; + case OperatorType::Minus: return storm::expressions::OperatorType::Minus; break; + case OperatorType::Times: return storm::expressions::OperatorType::Times; break; + case OperatorType::Divide: return storm::expressions::OperatorType::Divide; break; + case OperatorType::Min: return storm::expressions::OperatorType::Min; break; + case OperatorType::Max: return storm::expressions::OperatorType::Max; break; + } + } + int_fast64_t BinaryNumericalFunctionExpression::evaluateAsInt(Valuation const* valuation) const { LOG_THROW(this->hasIntegralReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as integer."); diff --git a/src/storage/expressions/BinaryNumericalFunctionExpression.h b/src/storage/expressions/BinaryNumericalFunctionExpression.h index 4e8573a4c..13ee489df 100644 --- a/src/storage/expressions/BinaryNumericalFunctionExpression.h +++ b/src/storage/expressions/BinaryNumericalFunctionExpression.h @@ -33,6 +33,7 @@ namespace storm { virtual ~BinaryNumericalFunctionExpression() = default; // Override base class methods. + virtual storm::expressions::OperatorType getOperator() const override; virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const override; virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; diff --git a/src/storage/expressions/BinaryRelationExpression.cpp b/src/storage/expressions/BinaryRelationExpression.cpp index 7e48c2286..f31f97fce 100644 --- a/src/storage/expressions/BinaryRelationExpression.cpp +++ b/src/storage/expressions/BinaryRelationExpression.cpp @@ -8,7 +8,18 @@ namespace storm { BinaryRelationExpression::BinaryRelationExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& firstOperand, std::shared_ptr<BaseExpression const> const& secondOperand, RelationType relationType) : BinaryExpression(returnType, firstOperand, secondOperand), relationType(relationType) { // Intentionally left empty. } - + + storm::expressions::OperatorType BinaryRelationExpression::getOperator() const { + switch (this->getRelationType()) { + case RelationType::Equal: return storm::expressions::OperatorType::Equal; break; + case RelationType::NotEqual: return storm::expressions::OperatorType::NotEqual; break; + case RelationType::Less: return storm::expressions::OperatorType::Less; break; + case RelationType::LessOrEqual: return storm::expressions::OperatorType::LessOrEqual; break; + case RelationType::Greater: return storm::expressions::OperatorType::Greater; break; + case RelationType::GreaterOrEqual: return storm::expressions::OperatorType::GreaterOrEqual; break; + } + } + bool BinaryRelationExpression::evaluateAsBool(Valuation const* valuation) const { LOG_THROW(this->hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as boolean."); diff --git a/src/storage/expressions/BinaryRelationExpression.h b/src/storage/expressions/BinaryRelationExpression.h index b88ef71f9..4e03a598e 100644 --- a/src/storage/expressions/BinaryRelationExpression.h +++ b/src/storage/expressions/BinaryRelationExpression.h @@ -33,6 +33,7 @@ namespace storm { virtual ~BinaryRelationExpression() = default; // Override base class methods. + virtual storm::expressions::OperatorType getOperator() const override; virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; virtual void accept(ExpressionVisitor* visitor) const override; diff --git a/src/storage/expressions/Expression.cpp b/src/storage/expressions/Expression.cpp index 863d630f3..e30b53927 100644 --- a/src/storage/expressions/Expression.cpp +++ b/src/storage/expressions/Expression.cpp @@ -55,6 +55,14 @@ namespace storm { return Expression(this->getBaseExpression().simplify()); } + OperatorType Expression::getOperator() const { + return this->getBaseExpression().getOperator(); + } + + bool Expression::isFunctionApplication() const { + return this->getBaseExpression().isFunctionApplication(); + } + uint_fast64_t Expression::getArity() const { return this->getBaseExpression().getArity(); } diff --git a/src/storage/expressions/Expression.h b/src/storage/expressions/Expression.h index 44f3e5728..0c55408dc 100644 --- a/src/storage/expressions/Expression.h +++ b/src/storage/expressions/Expression.h @@ -157,6 +157,21 @@ namespace storm { */ Expression simplify(); + /*! + * Retrieves the operator of a function application. This is only legal to call if the expression is + * function application. + * + * @return The operator associated with the function application. + */ + OperatorType getOperator() const; + + /*! + * Checks if the expression is a function application (of any sort). + * + * @return True iff the expression is a function application. + */ + bool isFunctionApplication() const; + /*! * Retrieves the arity of the expression. * diff --git a/src/storage/expressions/IfThenElseExpression.cpp b/src/storage/expressions/IfThenElseExpression.cpp index 4cc028b3c..bbd0b28d2 100644 --- a/src/storage/expressions/IfThenElseExpression.cpp +++ b/src/storage/expressions/IfThenElseExpression.cpp @@ -1,11 +1,41 @@ #include "src/storage/expressions/IfThenElseExpression.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidAccessException.h" + namespace storm { namespace expressions { IfThenElseExpression::IfThenElseExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& condition, std::shared_ptr<BaseExpression const> const& thenExpression, std::shared_ptr<BaseExpression const> const& elseExpression) : BaseExpression(returnType), condition(condition), thenExpression(thenExpression), elseExpression(elseExpression) { // Intentionally left empty. } + std::shared_ptr<BaseExpression const> IfThenElseExpression::getOperand(uint_fast64_t operandIndex) const { + LOG_THROW(operandIndex < 3, storm::exceptions::InvalidAccessException, "Unable to access operand " << operandIndex << " in expression of arity 3."); + if (operandIndex == 0) { + return this->getCondition(); + } else if (operandIndex == 1) { + return this->getThenExpression(); + } else { + return this->getElseExpression(); + } + } + + OperatorType IfThenElseExpression::getOperator() const { + return OperatorType::Ite; + } + + bool IfThenElseExpression::isFunctionApplication() const { + return true; + } + + bool IfThenElseExpression::containsVariables() const { + return this->getCondition()->containsVariables() || this->getThenExpression()->containsVariables() || this->getElseExpression()->containsVariables(); + } + + uint_fast64_t IfThenElseExpression::getArity() const { + return 3; + } + bool IfThenElseExpression::evaluateAsBool(Valuation const* valuation) const { bool conditionValue = this->condition->evaluateAsBool(valuation); if (conditionValue) { diff --git a/src/storage/expressions/IfThenElseExpression.h b/src/storage/expressions/IfThenElseExpression.h index b44de20be..425633850 100644 --- a/src/storage/expressions/IfThenElseExpression.h +++ b/src/storage/expressions/IfThenElseExpression.h @@ -27,6 +27,11 @@ namespace storm { virtual ~IfThenElseExpression() = default; // Override base class methods. + virtual std::shared_ptr<BaseExpression const> getOperand(uint_fast64_t operandIndex) const override; + virtual OperatorType getOperator() const override; + virtual bool isFunctionApplication() const override; + virtual bool containsVariables() const override; + virtual uint_fast64_t getArity() const override; virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const override; virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; diff --git a/src/storage/expressions/OperatorType.h b/src/storage/expressions/OperatorType.h new file mode 100644 index 000000000..8a4f199aa --- /dev/null +++ b/src/storage/expressions/OperatorType.h @@ -0,0 +1,33 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_OPERATORTYPE_H_ +#define STORM_STORAGE_EXPRESSIONS_OPERATORTYPE_H_ + +namespace storm { + namespace expressions { + // An enum representing all possible operator types. + enum class OperatorType { + And, + Or, + Xor, + Implies, + Iff, + Plus, + Minus, + Times, + Divide, + Min, + Max, + Equal, + NotEqual, + Less, + LessOrEqual, + Greater, + GreaterOrEqual, + Not, + Floor, + Ceil, + Ite + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_OPERATORTYPE_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/UnaryBooleanFunctionExpression.cpp b/src/storage/expressions/UnaryBooleanFunctionExpression.cpp index 7e405bb29..1d9e59119 100644 --- a/src/storage/expressions/UnaryBooleanFunctionExpression.cpp +++ b/src/storage/expressions/UnaryBooleanFunctionExpression.cpp @@ -13,6 +13,12 @@ namespace storm { return this->operatorType; } + storm::expressions::OperatorType UnaryBooleanFunctionExpression::getOperator() const { + switch (this->getOperatorType()) { + case OperatorType::Not: return storm::expressions::OperatorType::Not; + } + } + bool UnaryBooleanFunctionExpression::evaluateAsBool(Valuation const* valuation) const { LOG_THROW(this->hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as boolean."); diff --git a/src/storage/expressions/UnaryBooleanFunctionExpression.h b/src/storage/expressions/UnaryBooleanFunctionExpression.h index 1f2800d8e..c69c6f886 100644 --- a/src/storage/expressions/UnaryBooleanFunctionExpression.h +++ b/src/storage/expressions/UnaryBooleanFunctionExpression.h @@ -32,6 +32,7 @@ namespace storm { virtual ~UnaryBooleanFunctionExpression() = default; // Override base class methods. + virtual storm::expressions::OperatorType getOperator() const override; virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; virtual void accept(ExpressionVisitor* visitor) const override; diff --git a/src/storage/expressions/UnaryExpression.cpp b/src/storage/expressions/UnaryExpression.cpp index 62ff442ed..5d8262766 100644 --- a/src/storage/expressions/UnaryExpression.cpp +++ b/src/storage/expressions/UnaryExpression.cpp @@ -9,6 +9,10 @@ namespace storm { // Intentionally left empty. } + bool UnaryExpression::isFunctionApplication() const { + return true; + } + bool UnaryExpression::containsVariables() const { return this->getOperand()->containsVariables(); } diff --git a/src/storage/expressions/UnaryExpression.h b/src/storage/expressions/UnaryExpression.h index 8ad304ce1..a387ad8d5 100644 --- a/src/storage/expressions/UnaryExpression.h +++ b/src/storage/expressions/UnaryExpression.h @@ -26,6 +26,7 @@ namespace storm { virtual ~UnaryExpression() = default; // Override base class methods. + virtual bool isFunctionApplication() const override; virtual bool containsVariables() const override; virtual uint_fast64_t getArity() const override; virtual std::shared_ptr<BaseExpression const> getOperand(uint_fast64_t operandIndex) const override; diff --git a/src/storage/expressions/UnaryNumericalFunctionExpression.cpp b/src/storage/expressions/UnaryNumericalFunctionExpression.cpp index 02a4bee30..972f2a24a 100644 --- a/src/storage/expressions/UnaryNumericalFunctionExpression.cpp +++ b/src/storage/expressions/UnaryNumericalFunctionExpression.cpp @@ -14,6 +14,14 @@ namespace storm { return this->operatorType; } + storm::expressions::OperatorType UnaryNumericalFunctionExpression::getOperator() const { + switch (this->getOperatorType()) { + case OperatorType::Minus: return storm::expressions::OperatorType::Minus; break; + case OperatorType::Floor: return storm::expressions::OperatorType::Floor; break; + case OperatorType::Ceil: return storm::expressions::OperatorType::Ceil; break; + } + } + int_fast64_t UnaryNumericalFunctionExpression::evaluateAsInt(Valuation const* valuation) const { LOG_THROW(this->hasIntegralReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as integer."); diff --git a/src/storage/expressions/UnaryNumericalFunctionExpression.h b/src/storage/expressions/UnaryNumericalFunctionExpression.h index daa0df40f..10b85e63e 100644 --- a/src/storage/expressions/UnaryNumericalFunctionExpression.h +++ b/src/storage/expressions/UnaryNumericalFunctionExpression.h @@ -32,6 +32,7 @@ namespace storm { virtual ~UnaryNumericalFunctionExpression() = default; // Override base class methods. + virtual storm::expressions::OperatorType getOperator() const override; virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const override; virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override; virtual std::shared_ptr<BaseExpression const> simplify() const override; diff --git a/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp b/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp index fdff14a66..dc2d6d703 100644 --- a/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp +++ b/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp @@ -18,7 +18,7 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, Die) { std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); ASSERT_EQ(dtmc->getNumberOfStates(), 13ull); - ASSERT_EQ(dtmc->getNumberOfTransitions(), 27ull); + ASSERT_EQ(dtmc->getNumberOfTransitions(), 20ull); storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>()); @@ -74,7 +74,7 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, Crowds) { std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); ASSERT_EQ(dtmc->getNumberOfStates(), 8607ull); - ASSERT_EQ(dtmc->getNumberOfTransitions(), 22460ull); + ASSERT_EQ(dtmc->getNumberOfTransitions(), 15113ull); storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>()); @@ -119,7 +119,7 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, SynchronousLeader) { std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); ASSERT_EQ(dtmc->getNumberOfStates(), 12400ull); - ASSERT_EQ(dtmc->getNumberOfTransitions(), 28894ull); + ASSERT_EQ(dtmc->getNumberOfTransitions(), 16495ull); storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>()); diff --git a/test/functional/parser/AutoParserTest.cpp b/test/functional/parser/AutoParserTest.cpp index 3c4927ecd..7c3fcb483 100644 --- a/test/functional/parser/AutoParserTest.cpp +++ b/test/functional/parser/AutoParserTest.cpp @@ -24,7 +24,7 @@ TEST(AutoParserTest, BasicParsing) { // Test if parsed correctly. ASSERT_EQ(storm::models::DTMC, modelPtr->getType()); ASSERT_EQ(12, modelPtr->getNumberOfStates()); - ASSERT_EQ(32, modelPtr->getNumberOfTransitions()); + ASSERT_EQ(26, modelPtr->getNumberOfTransitions()); ASSERT_EQ(1, modelPtr->getInitialStates().getNumberOfSetBits()); ASSERT_TRUE(modelPtr->hasAtomicProposition("three")); ASSERT_FALSE(modelPtr->hasStateRewards()); @@ -56,21 +56,21 @@ TEST(AutoParserTest, Decision) { std::shared_ptr<storm::models::AbstractModel<double>> modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/dtmc.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"); ASSERT_EQ(storm::models::DTMC, modelPtr->getType()); ASSERT_EQ(12, modelPtr->getNumberOfStates()); - ASSERT_EQ(32, modelPtr->getNumberOfTransitions()); + ASSERT_EQ(26, modelPtr->getNumberOfTransitions()); // Ctmc modelPtr.reset(); modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/ctmc.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"); ASSERT_EQ(storm::models::CTMC, modelPtr->getType()); ASSERT_EQ(12, modelPtr->getNumberOfStates()); - ASSERT_EQ(31, modelPtr->getNumberOfTransitions()); + ASSERT_EQ(26, modelPtr->getNumberOfTransitions()); // Mdp modelPtr.reset(); modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/mdp.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"); ASSERT_EQ(storm::models::MDP, modelPtr->getType()); ASSERT_EQ(12, modelPtr->getNumberOfStates()); - ASSERT_EQ(36, modelPtr->getNumberOfTransitions()); + ASSERT_EQ(28, modelPtr->getNumberOfTransitions()); // Ctmdp // Note: For now we use the Mdp from above just given the ctmdp hint, since the implementation of the Ctmdp model seems not Quite right yet. @@ -80,12 +80,12 @@ TEST(AutoParserTest, Decision) { modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/ctmdp.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"); ASSERT_EQ(storm::models::CTMDP, modelPtr->getType()); ASSERT_EQ(12, modelPtr->getNumberOfStates()); - ASSERT_EQ(36, modelPtr->getNumberOfTransitions()); + ASSERT_EQ(28, modelPtr->getNumberOfTransitions()); // MA modelPtr.reset(); modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/ma.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab"); ASSERT_EQ(storm::models::MA, modelPtr->getType()); ASSERT_EQ(12, modelPtr->getNumberOfStates()); - ASSERT_EQ(35, modelPtr->getNumberOfTransitions()); + ASSERT_EQ(27, modelPtr->getNumberOfTransitions()); } diff --git a/test/functional/parser/DeterministicModelParserTest.cpp b/test/functional/parser/DeterministicModelParserTest.cpp index 4a62e5e6e..1b6fe71db 100644 --- a/test/functional/parser/DeterministicModelParserTest.cpp +++ b/test/functional/parser/DeterministicModelParserTest.cpp @@ -26,7 +26,7 @@ TEST(DeterministicModelParserTest, BasicDtmcParsing) { storm::models::Dtmc<double> dtmc(storm::parser::DeterministicModelParser::parseDtmc(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_general.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/dtmc_general.lab", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.state.rew", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.trans.rew")); ASSERT_EQ(8, dtmc.getNumberOfStates()); - ASSERT_EQ(21, dtmc.getNumberOfTransitions()); + ASSERT_EQ(16, dtmc.getNumberOfTransitions()); ASSERT_EQ(2, dtmc.getInitialStates().getNumberOfSetBits()); ASSERT_TRUE(dtmc.getInitialStates().get(0)); @@ -58,7 +58,7 @@ TEST(DeterministicModelParserTest, BasicCtmcParsing) { storm::models::Ctmc<double> ctmc(storm::parser::DeterministicModelParser::parseCtmc(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_general.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/dtmc_general.lab", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.state.rew", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.trans.rew")); ASSERT_EQ(8, ctmc.getNumberOfStates()); - ASSERT_EQ(21, ctmc.getNumberOfTransitions()); + ASSERT_EQ(16, ctmc.getNumberOfTransitions()); ASSERT_EQ(2, ctmc.getInitialStates().getNumberOfSetBits()); ASSERT_TRUE(ctmc.getInitialStates().get(0)); diff --git a/test/performance/modelchecker/GmmxxDtmcPrctModelCheckerTest.cpp b/test/performance/modelchecker/GmmxxDtmcPrctModelCheckerTest.cpp index 809f602fb..2fddf8702 100644 --- a/test/performance/modelchecker/GmmxxDtmcPrctModelCheckerTest.cpp +++ b/test/performance/modelchecker/GmmxxDtmcPrctModelCheckerTest.cpp @@ -17,7 +17,7 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, Crowds) { std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); ASSERT_EQ(dtmc->getNumberOfStates(), 2036647ull); - ASSERT_EQ(dtmc->getNumberOfTransitions(), 8973900ull); + ASSERT_EQ(dtmc->getNumberOfTransitions(), 7362293ull); storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>()); @@ -71,7 +71,7 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, SynchronousLeader) { std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>(); ASSERT_EQ(dtmc->getNumberOfStates(), 1312334ull); - ASSERT_EQ(dtmc->getNumberOfTransitions(), 2886810ull); + ASSERT_EQ(dtmc->getNumberOfTransitions(), 1574477ull); storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>()); From 2d8cc2efcd8d8f1cb269cb3f29207e3fbdaec7d0 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Tue, 6 May 2014 18:14:45 +0200 Subject: [PATCH 02/15] Added reordering functionality to DD interface. Former-commit-id: ffb8ad62f132a26a650a988ba117f27c967fb524 --- src/storage/dd/CuddDdManager.cpp | 21 +++++++++++++++++++++ src/storage/dd/CuddDdManager.h | 26 ++++++++++++++++++++++++++ src/storage/prism/Program.cpp | 2 +- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/storage/dd/CuddDdManager.cpp b/src/storage/dd/CuddDdManager.cpp index 64261d0aa..b3854ee36 100644 --- a/src/storage/dd/CuddDdManager.cpp +++ b/src/storage/dd/CuddDdManager.cpp @@ -224,6 +224,10 @@ namespace storm { Cudd& DdManager<DdType::CUDD>::getCuddManager() { return this->cuddManager; } + + Cudd const& DdManager<DdType::CUDD>::getCuddManager() const { + return this->cuddManager; + } std::vector<std::string> DdManager<DdType::CUDD>::getDdVariableNames() const { // First, we initialize a list DD variables and their names. @@ -246,5 +250,22 @@ namespace storm { return result; } + + void DdManager<DdType::CUDD>::allowDynamicReordering(bool value) { + if (value) { + this->getCuddManager().AutodynEnable(CUDD_REORDER_GROUP_SIFT_CONV); + } else { + this->getCuddManager().AutodynDisable(); + } + } + + bool DdManager<DdType::CUDD>::isDynamicReorderingAllowed() const { + Cudd_ReorderingType type; + return this->getCuddManager().ReorderingStatus(&type); + } + + void DdManager<DdType::CUDD>::triggerReordering() { + this->getCuddManager().ReduceHeap(CUDD_REORDER_GROUP_SIFT_CONV, 0); + } } } \ No newline at end of file diff --git a/src/storage/dd/CuddDdManager.h b/src/storage/dd/CuddDdManager.h index 25844598c..6fba4d254 100644 --- a/src/storage/dd/CuddDdManager.h +++ b/src/storage/dd/CuddDdManager.h @@ -138,6 +138,25 @@ namespace storm { */ bool hasMetaVariable(std::string const& metaVariableName) const; + /*! + * Sets whether or not dynamic reordering is allowed for the DDs managed by this manager. + * + * @param value If set to true, dynamic reordering is allowed and forbidden otherwise. + */ + void allowDynamicReordering(bool value); + + /*! + * Retrieves whether dynamic reordering is currently allowed. + * + * @return True iff dynamic reordering is currently allowed. + */ + bool isDynamicReorderingAllowed() const; + + /*! + * Triggers a reordering of the DDs managed by this manager. + */ + void triggerReordering(); + private: /*! * Retrieves a list of names of the DD variables in the order of their index. @@ -153,6 +172,13 @@ namespace storm { */ Cudd& getCuddManager(); + /*! + * Retrieves the underlying CUDD manager. + * + * @return The underlying CUDD manager. + */ + Cudd const& getCuddManager() const; + // A mapping from variable names to the meta variable information. std::unordered_map<std::string, DdMetaVariable<DdType::CUDD>> metaVariableMap; diff --git a/src/storage/prism/Program.cpp b/src/storage/prism/Program.cpp index 10a1114e6..617a1c52b 100644 --- a/src/storage/prism/Program.cpp +++ b/src/storage/prism/Program.cpp @@ -13,7 +13,7 @@ namespace storm { Program::Program(ModelType modelType, std::vector<Constant> const& constants, std::vector<BooleanVariable> const& globalBooleanVariables, std::vector<IntegerVariable> const& globalIntegerVariables, std::vector<Formula> const& formulas, std::vector<Module> const& modules, std::vector<RewardModel> const& rewardModels, bool fixInitialConstruct, storm::prism::InitialConstruct const& initialConstruct, std::vector<Label> const& labels, std::string const& filename, uint_fast64_t lineNumber, bool checkValidity) : LocatedInformation(filename, lineNumber), modelType(modelType), constants(constants), constantToIndexMap(), globalBooleanVariables(globalBooleanVariables), globalBooleanVariableToIndexMap(), globalIntegerVariables(globalIntegerVariables), globalIntegerVariableToIndexMap(), formulas(formulas), formulaToIndexMap(), modules(modules), moduleToIndexMap(), rewardModels(rewardModels), rewardModelToIndexMap(), initialConstruct(initialConstruct), labels(labels), labelToIndexMap(), actions(), actionsToModuleIndexMap(), variableToModuleIndexMap() { this->createMappings(); - // Create a new initial construct if none was given explicitly. + // Create a new initial construct if the corresponding flag was set. if (fixInitialConstruct) { if (this->getInitialConstruct().getInitialStatesExpression().isFalse()) { storm::expressions::Expression newInitialExpression = storm::expressions::Expression::createTrue(); From db232fe39bf256865e69f19cec2e2b9abbcb98c4 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Tue, 6 May 2014 19:33:54 +0200 Subject: [PATCH 03/15] Moved from pair to MatrixEntry as the basic building block of the matrix. Now matrix elements can be accessed in a more readable way. Former-commit-id: f6514eb0cd627fba813d78d4626763afda0734ee --- src/adapters/GmmxxAdapter.h | 4 +- .../MILPMinimalLabelSetGenerator.h | 50 ++--- .../PathBasedSubsystemGenerator.h | 144 +++++++------- .../SparseMarkovAutomatonCslModelChecker.h | 32 +-- src/models/AbstractDeterministicModel.h | 6 +- src/models/AbstractNondeterministicModel.h | 4 +- src/models/Dtmc.h | 12 +- src/models/MarkovAutomaton.h | 12 +- .../MaximalEndComponentDecomposition.cpp | 8 +- src/storage/SparseMatrix.cpp | 137 ++++++++----- src/storage/SparseMatrix.h | 90 ++++++++- ...tronglyConnectedComponentDecomposition.cpp | 20 +- src/utility/counterexamples.h | 28 +-- src/utility/graph.h | 60 +++--- src/utility/matrix.h | 2 +- ...eterministicSparseTransitionParserTest.cpp | 168 ++++++++-------- ...kovAutomatonSparseTransitionParserTest.cpp | 48 ++--- ...eterministicSparseTransitionParserTest.cpp | 184 +++++++++--------- test/functional/storage/SparseMatrixTest.cpp | 26 +-- test/performance/storage/SparseMatrixTest.cpp | 2 +- 20 files changed, 576 insertions(+), 461 deletions(-) diff --git a/src/adapters/GmmxxAdapter.h b/src/adapters/GmmxxAdapter.h index ff35800b2..b1d2206a5 100644 --- a/src/adapters/GmmxxAdapter.h +++ b/src/adapters/GmmxxAdapter.h @@ -51,8 +51,8 @@ public: columns.reserve(matrix.getEntryCount()); for (auto const& entry : matrix) { - columns.emplace_back(entry.first); - values.emplace_back(entry.second); + columns.emplace_back(entry.getColumn()); + values.emplace_back(entry.getValue()); } std::swap(result->ir, columns); diff --git a/src/counterexamples/MILPMinimalLabelSetGenerator.h b/src/counterexamples/MILPMinimalLabelSetGenerator.h index 197db76ab..4e0f9381c 100644 --- a/src/counterexamples/MILPMinimalLabelSetGenerator.h +++ b/src/counterexamples/MILPMinimalLabelSetGenerator.h @@ -130,7 +130,7 @@ namespace storm { bool allSuccessorsProblematic = true; for (auto const& successorEntry : transitionMatrix.getRow(row)) { // If there is a relevant successor, we need to add the labels of the current choice. - if (stateInformation.relevantStates.get(successorEntry.first) || psiStates.get(successorEntry.first)) { + if (stateInformation.relevantStates.get(successorEntry.getColumn()) || psiStates.get(successorEntry.getColumn())) { for (auto const& label : choiceLabeling[row]) { result.allRelevantLabels.insert(label); } @@ -139,7 +139,7 @@ namespace storm { result.relevantChoicesForRelevantStates[state].push_back(row); } } - if (!stateInformation.problematicStates.get(successorEntry.first)) { + if (!stateInformation.problematicStates.get(successorEntry.getColumn())) { allSuccessorsProblematic = false; } } @@ -297,11 +297,11 @@ namespace storm { std::list<uint_fast64_t> const& relevantChoicesForState = choiceInformation.relevantChoicesForRelevantStates.at(state); for (uint_fast64_t row : relevantChoicesForState) { for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(row)) { - if (stateInformation.relevantStates.get(successorEntry.first)) { - if (resultingMap.find(successorEntry.first) == resultingMap.end()) { + if (stateInformation.relevantStates.get(successorEntry.getColumn())) { + if (resultingMap.find(successorEntry.getColumn()) == resultingMap.end()) { variableNameBuffer.str(""); variableNameBuffer.clear(); - variableNameBuffer << "r" << successorEntry.first; + variableNameBuffer << "r" << successorEntry.getColumn(); resultingMap[state] = solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0); ++numberOfVariablesCreated; } @@ -330,11 +330,11 @@ namespace storm { std::list<uint_fast64_t> const& relevantChoicesForState = choiceInformation.relevantChoicesForRelevantStates.at(state); for (uint_fast64_t row : relevantChoicesForState) { for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(row)) { - if (stateInformation.relevantStates.get(successorEntry.first)) { + if (stateInformation.relevantStates.get(successorEntry.getColumn())) { variableNameBuffer.str(""); variableNameBuffer.clear(); - variableNameBuffer << "t" << state << "to" << successorEntry.first; - resultingMap[std::make_pair(state, successorEntry.first)] = solver.createBinaryVariable(variableNameBuffer.str(), 0); + variableNameBuffer << "t" << state << "to" << successorEntry.getColumn(); + resultingMap[std::make_pair(state, successorEntry.getColumn())] = solver.createBinaryVariable(variableNameBuffer.str(), 0); ++numberOfVariablesCreated; } } @@ -544,11 +544,11 @@ namespace storm { double rightHandSide = 1; for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(choice)) { - if (stateInformation.relevantStates.get(successorEntry.first)) { - variables.push_back(static_cast<int>(variableInformation.stateToProbabilityVariableIndexMap.at(successorEntry.first))); - coefficients.push_back(-successorEntry.second); - } else if (psiStates.get(successorEntry.first)) { - rightHandSide += successorEntry.second; + if (stateInformation.relevantStates.get(successorEntry.getColumn())) { + variables.push_back(static_cast<int>(variableInformation.stateToProbabilityVariableIndexMap.at(successorEntry.getColumn()))); + coefficients.push_back(-successorEntry.getValue()); + } else if (psiStates.get(successorEntry.getColumn())) { + rightHandSide += successorEntry.getValue(); } } @@ -602,7 +602,7 @@ namespace storm { coefficients.push_back(1); for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(problematicChoice)) { - variables.push_back(variableInformation.problematicTransitionToVariableIndexMap.at(std::make_pair(stateListPair.first, successorEntry.first))); + variables.push_back(variableInformation.problematicTransitionToVariableIndexMap.at(std::make_pair(stateListPair.first, successorEntry.getColumn()))); coefficients.push_back(-1); } @@ -619,9 +619,9 @@ namespace storm { variables.push_back(variableInformation.problematicStateToVariableIndexMap.at(state)); coefficients.push_back(1); - variables.push_back(variableInformation.problematicStateToVariableIndexMap.at(successorEntry.first)); + variables.push_back(variableInformation.problematicStateToVariableIndexMap.at(successorEntry.getColumn())); coefficients.push_back(-1); - variables.push_back(variableInformation.problematicTransitionToVariableIndexMap.at(std::make_pair(state, successorEntry.first))); + variables.push_back(variableInformation.problematicTransitionToVariableIndexMap.at(std::make_pair(state, successorEntry.getColumn()))); coefficients.push_back(1); solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS, 1); @@ -677,7 +677,7 @@ namespace storm { for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) { bool psiStateReachableInOneStep = false; for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(choice)) { - if (psiStates.get(successorEntry.first)) { + if (psiStates.get(successorEntry.getColumn())) { psiStateReachableInOneStep = true; } } @@ -690,8 +690,8 @@ namespace storm { coefficients.push_back(1); for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(choice)) { - if (state != successorEntry.first && stateInformation.relevantStates.get(successorEntry.first)) { - std::list<uint_fast64_t> const& successorChoiceVariableIndices = variableInformation.stateToChoiceVariablesIndexMap.at(successorEntry.first); + if (state != successorEntry.getColumn() && stateInformation.relevantStates.get(successorEntry.getColumn())) { + std::list<uint_fast64_t> const& successorChoiceVariableIndices = variableInformation.stateToChoiceVariablesIndexMap.at(successorEntry.getColumn()); for (auto choiceVariableIndex : successorChoiceVariableIndices) { variables.push_back(choiceVariableIndex); @@ -720,8 +720,8 @@ namespace storm { // Compute the set of predecessors. std::unordered_set<uint_fast64_t> predecessors; for (auto const& predecessorEntry : backwardTransitions.getRow(state)) { - if (state != predecessorEntry.first) { - predecessors.insert(predecessorEntry.first); + if (state != predecessorEntry.getColumn()) { + predecessors.insert(predecessorEntry.getColumn()); } } @@ -737,7 +737,7 @@ namespace storm { // Check if the current choice targets the current state. for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(relevantChoice)) { - if (state == successorEntry.first) { + if (state == successorEntry.getColumn()) { choiceTargetsCurrentState = true; break; } @@ -782,8 +782,8 @@ namespace storm { for (auto psiState : psiStates) { // Compute the set of predecessors. for (auto const& predecessorEntry : backwardTransitions.getRow(psiState)) { - if (psiState != predecessorEntry.first) { - predecessors.insert(predecessorEntry.first); + if (psiState != predecessorEntry.getColumn()) { + predecessors.insert(predecessorEntry.getColumn()); } } } @@ -800,7 +800,7 @@ namespace storm { // Check if the current choice targets the current state. for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(relevantChoice)) { - if (psiStates.get(successorEntry.first)) { + if (psiStates.get(successorEntry.getColumn())) { choiceTargetsPsiState = true; break; } diff --git a/src/counterexamples/PathBasedSubsystemGenerator.h b/src/counterexamples/PathBasedSubsystemGenerator.h index 11e24d00d..2325eebe8 100644 --- a/src/counterexamples/PathBasedSubsystemGenerator.h +++ b/src/counterexamples/PathBasedSubsystemGenerator.h @@ -69,31 +69,31 @@ public: for(auto const& trans : transMat.getRow(init)) { //save transition only if it's no 'virtual transition of prob 0 and it doesn't go from init state to init state. - if(trans.second != (T) 0 && !subSysStates.get(trans.first)) { + if(trans.getValue() != (T) 0 && !subSysStates.get(trans.getColumn())) { //new state? - if(distances[trans.first].second == (T) -1) { - distances[trans.first].first = init; - distances[trans.first].second = trans.second; + if(distances[trans.getColumn()].second == (T) -1) { + distances[trans.getColumn()].first = init; + distances[trans.getColumn()].second = trans.getValue(); - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); } - else if(distances[trans.first].second < trans.second){ + else if(distances[trans.getColumn()].second < trans.getValue()){ //This state has already been discovered //And the distance can be improved by using this transition. //find state in set, remove it, reenter it with new and correct values. - auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); for(;range.first != range.second; range.first++) { - if(trans.first == range.first->first) { + if(trans.getColumn() == range.first->first) { activeSet.erase(range.first); break; } } - distances[trans.first].first = init; - distances[trans.first].second = trans.second; + distances[trans.getColumn()].first = init; + distances[trans.getColumn()].second = trans.getValue(); - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, trans.second)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), trans.getValue())); } } } @@ -115,36 +115,36 @@ public: // Look at all neighbors for(auto const& trans : transMat.getRow(activeState.first)) { // Only consider the transition if it's not virtual - if(trans.second != (T) 0) { + if(trans.getValue() != (T) 0) { - T distance = activeState.second * trans.second; + T distance = activeState.second * trans.getValue(); //not discovered or initial terminal state - if(distances[trans.first].second == (T)-1) { + if(distances[trans.getColumn()].second == (T)-1) { //New state discovered -> save it - distances[trans.first].first = activeState.first; - distances[trans.first].second = distance; + distances[trans.getColumn()].first = activeState.first; + distances[trans.getColumn()].second = distance; // push newly discovered state into activeSet - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distance)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distance)); } - else if(distances[trans.first].second < distance ){ + else if(distances[trans.getColumn()].second < distance) { //This state has already been discovered //And the distance can be improved by using this transition. //find state in set, remove it, reenter it with new and correct values. - auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); for(;range.first != range.second; range.first++) { - if(trans.first == range.first->first) { + if(trans.getColumn() == range.first->first) { activeSet.erase(range.first); break; } } - distances[trans.first].first = activeState.first; - distances[trans.first].second = distance; - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distance)); + distances[trans.getColumn()].first = activeState.first; + distances[trans.getColumn()].second = distance; + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distance)); } } } @@ -182,33 +182,33 @@ public: for(auto const& trans : transMat.getRow(init)) { //save transition only if it's no 'virtual transition of prob 0 and it doesn't go from init state to init state. - if(trans.second != (T) 0 && !subSysStates.get(trans.first)) { + if(trans.getValue() != (T) 0 && !subSysStates.get(trans.getColumn())) { //new state? - if(distances[trans.first].second == (T) -1) { + if(distances[trans.getColumn()].second == (T) -1) { //for initialization of subsys -> subsys search use prob (init -> subsys state -> found state) instead of prob(subsys state -> found state) - distances[trans.first].first = init; - distances[trans.first].second = trans.second * (itDistances[init].second == -1 ? 1 : itDistances[init].second); + distances[trans.getColumn()].first = init; + distances[trans.getColumn()].second = trans.getValue() * (itDistances[init].second == -1 ? 1 : itDistances[init].second); - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); } - else if(distances[trans.first].second < trans.second * itDistances[init].second){ + else if(distances[trans.getColumn()].second < trans.getValue() * itDistances[init].second){ //This state has already been discovered //And the distance can be improved by using this transition. //find state in set, remove it, reenter it with new and correct values. - auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); for(;range.first != range.second; range.first++) { - if(trans.first == range.first->first) { + if(trans.getColumn() == range.first->first) { activeSet.erase(range.first); break; } } //for initialization of subsys -> subsys search use prob (init -> subsys state -> found state) instead of prob(subsys state -> found state) - distances[trans.first].first = init; - distances[trans.first].second = trans.second * (itDistances[init].second == -1 ? 1 : itDistances[init].second); + distances[trans.getColumn()].first = init; + distances[trans.getColumn()].second = trans.getValue() * (itDistances[init].second == -1 ? 1 : itDistances[init].second); - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, trans.second)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), trans.getValue())); } } } @@ -225,7 +225,7 @@ public: activeSet.erase(--activeSet.end()); // Always stop at first target/terminal state - //if(terminalStates.get(activeState.first) || subSysStates.get(activeState.first)) break; + //if(terminalStates.get(activeState.getColumn()) || subSysStates.get(activeState.getColumn())) break; // If this is an initial state, do not consider its outgoing transitions, since all relevant ones have already been considered // Same goes for forbidden states since they may not be used on a path, except as last node. @@ -233,36 +233,36 @@ public: // Look at all neighbors for(auto const& trans : transMat.getRow(activeState.first)) { // Only consider the transition if it's not virtual - if(trans.second != (T) 0) { + if(trans.getValue() != (T) 0) { - T distance = activeState.second * trans.second; + T distance = activeState.second * trans.getValue(); //not discovered or initial terminal state - if(distances[trans.first].second == (T)-1) { + if(distances[trans.getColumn()].second == (T)-1) { //New state discovered -> save it - distances[trans.first].first = activeState.first; - distances[trans.first].second = distance; + distances[trans.getColumn()].first = activeState.first; + distances[trans.getColumn()].second = distance; // push newly discovered state into activeSet - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distance)); + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distance)); } - else if(distances[trans.first].second < distance ){ + else if(distances[trans.getColumn()].second < distance) { //This state has already been discovered //And the distance can be improved by using this transition. //find state in set, remove it, reenter it with new and correct values. - auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second)); + auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second)); for(;range.first != range.second; range.first++) { - if(trans.first == range.first->first) { + if(trans.getColumn() == range.first->first) { activeSet.erase(range.first); break; } } - distances[trans.first].first = activeState.first; - distances[trans.first].second = distance; - activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distance)); + distances[trans.getColumn()].first = activeState.first; + distances[trans.getColumn()].second = distance; + activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distance)); } } } @@ -292,8 +292,8 @@ public: // if there is a terminal state that is an initial state then prob == 1 and return if(initStates.get(*target)){ - distances[*target].first = *target; - distances[*target].second = (T) 1; + distances[*target].getColumn() = *target; + distances[*target].getValue() = (T) 1; return; } @@ -302,19 +302,19 @@ public: //only use if allowed and not in subsys and not terminal if(allowedStates.get(*iter) && !subSysStates.get(*iter) && !terminalStates.get(*iter)) { //new state? - if(distances[*iter].second == (T) -1) { + if(distances[*iter].getValue() == (T) -1) { // save as discovered and push into active set - distances[*iter].first = *target; //successor - distances[*iter].second = transMat.getValue(*iter, *target); //prob of shortest path + distances[*iter].getColumn() = *target; //successor + distances[*iter].getValue() = transMat.getValue(*iter, *target); //prob of shortest path activeSet.insert(std::pair<uint_fast64_t, T>(*iter, probabilities[*iter])); //prob of reaching some terminal state from pred. } else { // state was already discovered // is this the better transition? - if(distances[*iter].second > transMat.getValue(*iter, *target)) { - distances[*iter].first = *target; - distances[*iter].second = transMat.getValue(*iter, *target); + if(distances[*iter].getValue() > transMat.getValue(*iter, *target)) { + distances[*iter].getColumn() = *target; + distances[*iter].getValue() = transMat.getValue(*iter, *target); } } } @@ -328,19 +328,19 @@ public: //only use if allowed and not in subsys and not terminal if(allowedStates.get(*iter) && !subSysStates.get(*iter) && !terminalStates.get(*iter)) { //new state? - if(distances[*iter].second == (T) -1) { + if(distances[*iter].getValue() == (T) -1) { // save as discovered and push into active set - distances[*iter].first = *sysState; //successor - distances[*iter].second = transMat.getValue(*iter, *sysState); //prob of shortest path + distances[*iter].getColumn() = *sysState; //successor + distances[*iter].getValue() = transMat.getValue(*iter, *sysState); //prob of shortest path activeSet.insert(std::pair<uint_fast64_t, T>(*iter, probabilities[*iter])); //prob of reaching some terminal state from pred. } else { // state was already discovered // is this the better transition? - if(distances[*iter].second > transMat.getValue(*iter, *sysState)) { - distances[*iter].first = *sysState; - distances[*iter].second = transMat.getValue(*iter, *sysState); + if(distances[*iter].getValue() > transMat.getValue(*iter, *sysState)) { + distances[*iter].getColumn() = *sysState; + distances[*iter].getValue() = transMat.getValue(*iter, *sysState); } } } @@ -355,7 +355,7 @@ public: while(!activeSet.empty()) { // copy here since using a reference leads to segfault state = *(--activeSet.end()); - activeState = state.first; + activeState = state.getColumn(); activeSet.erase(--activeSet.end()); //stop on the first subsys/init state @@ -368,19 +368,19 @@ public: //only if transition is not "virtual" and no selfloop if(*iter != activeState && transMat.getValue(*iter, activeState) != (T) 0) { //new state? - if(distances[*iter].second == (T) -1) { + if(distances[*iter].getValue() == (T) -1) { // save as discovered and push into active set - distances[*iter].first = activeState; - distances[*iter].second = transMat.getValue(*iter, activeState) * distances[activeState].second; + distances[*iter].getColumn() = activeState; + distances[*iter].getValue() = transMat.getValue(*iter, activeState) * distances[activeState].getValue(); activeSet.insert(std::pair<uint_fast64_t, T>(*iter, probabilities[*iter])); } else { // state was already discovered // is this the better transition? - if(distances[*iter].second < transMat.getValue(*iter, activeState) * distances[activeState].second) { - distances[*iter].first = activeState; - distances[*iter].second = transMat.getValue(*iter, activeState) * distances[activeState].second; + if(distances[*iter].getValue() < transMat.getValue(*iter, activeState) * distances[activeState].getValue()) { + distances[*iter].getColumn() = activeState; + distances[*iter].getValue() = transMat.getValue(*iter, activeState) * distances[activeState].getValue(); } } } @@ -389,15 +389,15 @@ public: } //get path probability - probability = distances[activeState].second; + probability = distances[activeState].getValue(); if(probability == (T) -1) probability = 1; // iterate over the successors until reaching the end of the finite path shortestPath.push_back(activeState); - activeState = distances[activeState].first; + activeState = distances[activeState].getColumn(); while(!terminalStates.get(activeState) && !subSysStates.get(activeState)) { shortestPath.push_back(activeState); - activeState = distances[activeState].first; + activeState = distances[activeState].getColumn(); } shortestPath.push_back(activeState); } @@ -482,7 +482,7 @@ public: shortestPath.push_back(bestIndex); //At last compensate for the distance between init and source state - probability = itSearch ? probability : probability / itDistances[bestIndex].second; + probability = itSearch ? probability : probability / itDistances[bestIndex].first; } private: diff --git a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h index a4cd0844a..aab76f807 100644 --- a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h +++ b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h @@ -103,10 +103,10 @@ namespace storm { for (auto state : markovianNonGoalStates) { for (auto& element : aMarkovian.getRow(rowIndex)) { ValueType eTerm = std::exp(-exitRates[state] * delta); - if (element.first == rowIndex) { - element.second = (storm::utility::constantOne<ValueType>() - eTerm) * element.second + eTerm; + if (element.getColumn() == rowIndex) { + element.getValue() = (storm::utility::constantOne<ValueType>() - eTerm) * element.getValue() + eTerm; } else { - element.second = (storm::utility::constantOne<ValueType>() - eTerm) * element.second; + element.getValue() = (storm::utility::constantOne<ValueType>() - eTerm) * element.getValue(); } } ++rowIndex; @@ -116,7 +116,7 @@ namespace storm { rowIndex = 0; for (auto state : markovianNonGoalStates) { for (auto& element : aMarkovianToProbabilistic.getRow(rowIndex)) { - element.second = (1 - std::exp(-exitRates[state] * delta)) * element.second; + element.getValue() = (1 - std::exp(-exitRates[state] * delta)) * element.getValue(); } ++rowIndex; } @@ -133,8 +133,8 @@ namespace storm { bMarkovianFixed.push_back(storm::utility::constantZero<ValueType>()); for (auto& element : transitionMatrix.getRowGroup(state)) { - if (goalStates.get(element.first)) { - bMarkovianFixed.back() += (1 - std::exp(-exitRates[state] * delta)) * element.second; + if (goalStates.get(element.getColumn())) { + bMarkovianFixed.back() += (1 - std::exp(-exitRates[state] * delta)) * element.getValue(); } } } @@ -314,13 +314,13 @@ namespace storm { b.push_back(storm::utility::constantZero<ValueType>()); for (auto element : transitionMatrix.getRow(choice)) { - if (statesNotContainedInAnyMec.get(element.first)) { + if (statesNotContainedInAnyMec.get(element.getColumn())) { // If the target state is not contained in an MEC, we can copy over the entry. - sspMatrixBuilder.addNextValue(currentChoice, statesNotInMecsBeforeIndex[element.first], element.second); + sspMatrixBuilder.addNextValue(currentChoice, statesNotInMecsBeforeIndex[element.getColumn()], element.getValue()); } else { // If the target state is contained in MEC i, we need to add the probability to the corresponding field in the vector // so that we are able to write the cumulative probability to the MEC into the matrix. - auxiliaryStateToProbabilityMap[stateToMecIndexMap[element.first]] += element.second; + auxiliaryStateToProbabilityMap[stateToMecIndexMap[element.getColumn()]] += element.getValue(); } } @@ -350,13 +350,13 @@ namespace storm { b.push_back(storm::utility::constantZero<ValueType>()); for (auto element : transitionMatrix.getRow(choice)) { - if (statesNotContainedInAnyMec.get(element.first)) { + if (statesNotContainedInAnyMec.get(element.getColumn())) { // If the target state is not contained in an MEC, we can copy over the entry. - sspMatrixBuilder.addNextValue(currentChoice, statesNotInMecsBeforeIndex[element.first], element.second); + sspMatrixBuilder.addNextValue(currentChoice, statesNotInMecsBeforeIndex[element.getColumn()], element.getValue()); } else { // If the target state is contained in MEC i, we need to add the probability to the corresponding field in the vector // so that we are able to write the cumulative probability to the MEC into the matrix. - auxiliaryStateToProbabilityMap[stateToMecIndexMap[element.first]] += element.second; + auxiliaryStateToProbabilityMap[stateToMecIndexMap[element.getColumn()]] += element.getValue(); } } @@ -453,8 +453,8 @@ namespace storm { coefficients.push_back(1); for (auto element : transitionMatrix.getRow(nondeterministicChoiceIndices[state])) { - variables.push_back(stateToVariableIndexMap.at(element.first)); - coefficients.push_back(-element.second); + variables.push_back(stateToVariableIndexMap.at(element.getColumn())); + coefficients.push_back(-element.getValue()); } variables.push_back(lraValueVariableIndex); @@ -472,8 +472,8 @@ namespace storm { coefficients.push_back(1); for (auto element : transitionMatrix.getRow(choice)) { - variables.push_back(stateToVariableIndexMap.at(element.first)); - coefficients.push_back(-element.second); + variables.push_back(stateToVariableIndexMap.at(element.getColumn())); + coefficients.push_back(-element.getValue()); } solver->addConstraint("state" + std::to_string(state), variables, coefficients, min ? storm::solver::LpSolver::LESS_EQUAL : storm::solver::LpSolver::GREATER_EQUAL, storm::utility::constantZero<ValueType>()); diff --git a/src/models/AbstractDeterministicModel.h b/src/models/AbstractDeterministicModel.h index c72b914de..88e0d1006 100644 --- a/src/models/AbstractDeterministicModel.h +++ b/src/models/AbstractDeterministicModel.h @@ -87,9 +87,9 @@ class AbstractDeterministicModel: public AbstractModel<T> { for (uint_fast64_t i = 0; i < this->transitionMatrix.getRowCount(); ++i, ++rowIt) { typename storm::storage::SparseMatrix<T>::const_rows row = this->transitionMatrix.getRow(i); for (auto const& transition : row) { - if (transition.second != storm::utility::constantZero<T>()) { - if (subsystem == nullptr || subsystem->get(transition.first)) { - outStream << "\t" << i << " -> " << transition.first << " [ label= \"" << transition.second << "\" ];" << std::endl; + if (transition.getValue() != storm::utility::constantZero<T>()) { + if (subsystem == nullptr || subsystem->get(transition.getColumn())) { + outStream << "\t" << i << " -> " << transition.getColumn() << " [ label= \"" << transition.getValue() << "\" ];" << std::endl; } } } diff --git a/src/models/AbstractNondeterministicModel.h b/src/models/AbstractNondeterministicModel.h index 0058377a6..3a738cbe6 100644 --- a/src/models/AbstractNondeterministicModel.h +++ b/src/models/AbstractNondeterministicModel.h @@ -189,8 +189,8 @@ namespace storm { // Now draw all probabilitic arcs that belong to this nondeterminstic choice. for (auto const& transition : row) { - if (subsystem == nullptr || subsystem->get(transition.first)) { - outStream << "\t\"" << state << "c" << choice << "\" -> " << transition.first << " [ label= \"" << transition.second << "\" ]"; + if (subsystem == nullptr || subsystem->get(transition.getColumn())) { + outStream << "\t\"" << state << "c" << choice << "\" -> " << transition.getColumn() << " [ label= \"" << transition.getValue() << "\" ]"; // If we were given a scheduler to highlight, we do so now. if (scheduler != nullptr) { diff --git a/src/models/Dtmc.h b/src/models/Dtmc.h index b599774f9..eb0e669dd 100644 --- a/src/models/Dtmc.h +++ b/src/models/Dtmc.h @@ -170,7 +170,7 @@ public: for(uint_fast64_t row = 0; row < origMat.getRowCount(); ++row) { if(subSysStates.get(row)){ for(auto const& entry : origMat.getRow(row)) { - if(subSysStates.get(entry.first)) { + if(subSysStates.get(entry.getColumn())) { subSysTransitionCount++; } } @@ -198,10 +198,10 @@ public: if(subSysStates.get(row)){ // Transfer transitions for(auto& entry : origMat.getRow(row)) { - if(subSysStates.get(entry.first)) { - newMatBuilder.addNextValue(newRow, stateMapping[entry.first], entry.second); + if(subSysStates.get(entry.getColumn())) { + newMatBuilder.addNextValue(newRow, stateMapping[entry.getColumn()], entry.getValue()); } else { - rest += entry.second; + rest += entry.getValue(); } } @@ -251,8 +251,8 @@ public: if(subSysStates.get(row)){ // Transfer transition rewards for(auto& entry : this->getTransitionRewardMatrix().getRow(row)) { - if(subSysStates.get(entry.first)) { - newTransRewardsBuilder.addNextValue(newRow, stateMapping[entry.first], entry.second); + if(subSysStates.get(entry.getColumn())) { + newTransRewardsBuilder.addNextValue(newRow, stateMapping[entry.getColumn()], entry.getValue()); } } diff --git a/src/models/MarkovAutomaton.h b/src/models/MarkovAutomaton.h index 22b6ab13f..89ce7ccee 100644 --- a/src/models/MarkovAutomaton.h +++ b/src/models/MarkovAutomaton.h @@ -147,7 +147,7 @@ namespace storm { for (uint_fast64_t row = this->getTransitionMatrix().getRowGroupIndices()[state] + (this->isHybridState(state) ? 1 : 0); row < this->getTransitionMatrix().getRowGroupIndices()[state + 1]; ++row) { for (auto const& entry : this->transitionMatrix.getRow(row)) { - newTransitionMatrixBuilder.addNextValue(currentChoice, entry.first, entry.second); + newTransitionMatrixBuilder.addNextValue(currentChoice, entry.getColumn(), entry.getValue()); } ++currentChoice; } @@ -220,8 +220,8 @@ namespace storm { // Now draw all probabilitic arcs that belong to this nondeterminstic choice. for (auto const& transition : row) { - if (subsystem == nullptr || subsystem->get(transition.first)) { - outStream << "\t\"" << state << "c" << choice << "\" -> " << transition.first << " [ label= \"" << transition.second << "\" ]"; + if (subsystem == nullptr || subsystem->get(transition.getColumn())) { + outStream << "\t\"" << state << "c" << choice << "\" -> " << transition.getColumn() << " [ label= \"" << transition.getValue() << "\" ]"; // If we were given a scheduler to highlight, we do so now. if (scheduler != nullptr) { @@ -237,8 +237,8 @@ namespace storm { } else { // In this case we are emitting a Markovian choice, so draw the arrows directly to the target states. for (auto const& transition : row) { - if (subsystem == nullptr || subsystem->get(transition.first)) { - outStream << "\t\"" << state << "\" -> " << transition.first << " [ label= \"" << transition.second << " (" << this->exitRates[state] << ")\" ]"; + if (subsystem == nullptr || subsystem->get(transition.getColumn())) { + outStream << "\t\"" << state << "\" -> " << transition.getColumn() << " [ label= \"" << transition.getValue() << " (" << this->exitRates[state] << ")\" ]"; } } } @@ -259,7 +259,7 @@ namespace storm { void turnRatesToProbabilities() { for (auto state : this->markovianStates) { for (auto& transition : this->transitionMatrix.getRowGroup(state)) { - transition.second /= this->exitRates[state]; + transition.getValue() /= this->exitRates[state]; } } } diff --git a/src/storage/MaximalEndComponentDecomposition.cpp b/src/storage/MaximalEndComponentDecomposition.cpp index 56ebbd34c..0f6a44f65 100644 --- a/src/storage/MaximalEndComponentDecomposition.cpp +++ b/src/storage/MaximalEndComponentDecomposition.cpp @@ -88,7 +88,7 @@ namespace storm { for (uint_fast64_t choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1]; ++choice) { bool choiceContainedInMEC = true; for (auto const& entry : transitionMatrix.getRow(choice)) { - if (scc.find(entry.first) == scc.end()) { + if (scc.find(entry.getColumn()) == scc.end()) { choiceContainedInMEC = false; break; } @@ -116,8 +116,8 @@ namespace storm { statesToCheck.clear(); for (auto state : statesToRemove) { for (auto const& entry : backwardTransitions.getRow(state)) { - if (scc.find(entry.first) != scc.end()) { - statesToCheck.set(entry.first); + if (scc.find(entry.getColumn()) != scc.end()) { + statesToCheck.set(entry.getColumn()); } } } @@ -154,7 +154,7 @@ namespace storm { for (uint_fast64_t choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1]; ++choice) { bool choiceContained = true; for (auto const& entry : transitionMatrix.getRow(choice)) { - if (mecStateSet.find(entry.first) == mecStateSet.end()) { + if (mecStateSet.find(entry.getColumn()) == mecStateSet.end()) { choiceContained = false; break; } diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index e4d719006..c6bfd0214 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -17,6 +17,41 @@ extern log4cplus::Logger logger; namespace storm { namespace storage { + template<typename T> + MatrixEntry<T>::MatrixEntry(uint_fast64_t column, T value) : entry(column, value) { + // Intentionally left empty. + } + + template<typename T> + MatrixEntry<T>::MatrixEntry(std::pair<uint_fast64_t, T>&& pair) : entry(std::move(pair)) { + // Intentionally left empty. + } + + template<typename T> + uint_fast64_t const& MatrixEntry<T>::getColumn() const { + return this->entry.first; + } + + template<typename T> + uint_fast64_t& MatrixEntry<T>::getColumn() { + return this->entry.first; + } + + template<typename T> + T const& MatrixEntry<T>::getValue() const { + return this->entry.second; + } + + template<typename T> + T& MatrixEntry<T>::getValue() { + return this->entry.second; + } + + template<typename T> + std::pair<uint_fast64_t, T> const& MatrixEntry<T>::getColumnValuePair() const { + return this->entry; + } + template<typename T> SparseMatrixBuilder<T>::SparseMatrixBuilder(uint_fast64_t rows, uint_fast64_t columns, uint_fast64_t entries, bool hasCustomRowGrouping, uint_fast64_t rowGroups) : rowCountSet(rows != 0), rowCount(rows), columnCountSet(columns != 0), columnCount(columns), entryCount(entries), hasCustomRowGrouping(hasCustomRowGrouping), rowGroupCountSet(rowGroups != 0), rowGroupCount(rowGroups), rowGroupIndices(), storagePreallocated(rows != 0 && columns != 0 && entries != 0), columnsAndValues(), rowIndications(), currentEntryCount(0), lastRow(0), lastColumn(0), currentRowGroup(0) { this->prepareInternalStorage(); @@ -168,7 +203,7 @@ namespace storm { void SparseMatrixBuilder<T>::prepareInternalStorage() { // Only allocate the memory for the matrix contents if the dimensions of the matrix are already known. if (storagePreallocated) { - columnsAndValues = std::vector<std::pair<uint_fast64_t, T>>(entryCount, std::make_pair(0, storm::utility::constantZero<T>())); + columnsAndValues = std::vector<MatrixEntry<T>>(entryCount, MatrixEntry<T>(0, storm::utility::constantZero<T>())); rowIndications = std::vector<uint_fast64_t>(rowCount + 1, 0); } else { rowIndications.push_back(0); @@ -236,18 +271,18 @@ namespace storm { } template<typename T> - SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<std::pair<uint_fast64_t, T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(columnsAndValues), rowIndications(rowIndications), rowGroupIndices(rowGroupIndices) { + SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<MatrixEntry<T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(columnsAndValues), rowIndications(rowIndications), rowGroupIndices(rowGroupIndices) { for (auto const& element : *this) { - if (element.second != storm::utility::constantZero<T>()) { + if (element.getValue() != storm::utility::constantZero<T>()) { ++this->nonzeroEntryCount; } } } template<typename T> - SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<std::pair<uint_fast64_t, T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)), rowGroupIndices(std::move(rowGroupIndices)) { + SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<MatrixEntry<T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)), rowGroupIndices(std::move(rowGroupIndices)) { for (auto const& element : *this) { - if (element.second != storm::utility::constantZero<T>()) { + if (element.getValue() != storm::utility::constantZero<T>()) { ++this->nonzeroEntryCount; } } @@ -304,17 +339,17 @@ namespace storm { for (uint_fast64_t row = 0; row < this->getRowCount(); ++row) { for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = other.begin(row), ite2 = other.end(row); it1 != ite1 && it2 != ite2; ++it1, ++it2) { // Skip over all zero entries in both matrices. - while (it1 != ite1 && it1->second == storm::utility::constantZero<T>()) { + while (it1 != ite1 && it1->getValue() == storm::utility::constantZero<T>()) { ++it1; } - while (it2 != ite2 && it2->second == storm::utility::constantZero<T>()) { + while (it2 != ite2 && it2->getValue() == storm::utility::constantZero<T>()) { ++it2; } if ((it1 == ite1) || (it2 == ite2)) { equalityResult = (it1 == ite1) ^ (it2 == ite2); break; } else { - if (it1->first != it2->first || it1->second != it2->second) { + if (it1->getColumn() != it2->getColumn() || it1->getValue() != it2->getValue()) { equalityResult = false; break; } @@ -389,12 +424,12 @@ namespace storm { // If there is at least one entry in this row, we can just set it to one, modify its column value to the // one given by the parameter and set all subsequent elements of this row to zero. - columnValuePtr->first = column; - columnValuePtr->second = storm::utility::constantOne<T>(); + columnValuePtr->getColumn() = column; + columnValuePtr->getValue() = storm::utility::constantOne<T>(); ++columnValuePtr; for (; columnValuePtr != columnValuePtrEnd; ++columnValuePtr) { - columnValuePtr->first = 0; - columnValuePtr->second = storm::utility::constantZero<T>(); + columnValuePtr->getColumn() = 0; + columnValuePtr->getValue() = storm::utility::constantZero<T>(); } } @@ -402,8 +437,8 @@ namespace storm { T SparseMatrix<T>::getConstrainedRowSum(uint_fast64_t row, storm::storage::BitVector const& constraint) const { T result(0); for (const_iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) { - if (constraint.get(it->first)) { - result += it->second; + if (constraint.get(it->getColumn())) { + result += it->getValue(); } } return result; @@ -457,10 +492,10 @@ namespace storm { bool foundDiagonalElement = false; for (const_iterator it = this->begin(i), ite = this->end(i); it != ite; ++it) { - if (columnConstraint.get(it->first)) { + if (columnConstraint.get(it->getColumn())) { ++subEntries; - if (index == it->first) { + if (index == it->getColumn()) { foundDiagonalElement = true; } } @@ -507,14 +542,14 @@ namespace storm { bool insertedDiagonalElement = false; for (const_iterator it = this->begin(i), ite = this->end(i); it != ite; ++it) { - if (columnConstraint.get(it->first)) { - if (index == it->first) { + if (columnConstraint.get(it->getColumn())) { + if (index == it->getColumn()) { insertedDiagonalElement = true; - } else if (insertDiagonalEntries && !insertedDiagonalElement && it->first > index) { + } else if (insertDiagonalEntries && !insertedDiagonalElement && it->getColumn() > index) { matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[index], storm::utility::constantZero<T>()); insertedDiagonalElement = true; } - matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[it->first], it->second); + matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[it->getColumn()], it->getValue()); } } if (insertDiagonalEntries && !insertedDiagonalElement) { @@ -540,7 +575,7 @@ namespace storm { // Iterate through that row and count the number of slots we have to reserve for copying. bool foundDiagonalElement = false; for (const_iterator it = this->begin(rowToCopy), ite = this->end(rowToCopy); it != ite; ++it) { - if (it->first == rowGroupIndex) { + if (it->getColumn() == rowGroupIndex) { foundDiagonalElement = true; } ++subEntries; @@ -562,13 +597,13 @@ namespace storm { // there is no entry yet. bool insertedDiagonalElement = false; for (const_iterator it = this->begin(rowToCopy), ite = this->end(rowToCopy); it != ite; ++it) { - if (it->first == rowGroupIndex) { + if (it->getColumn() == rowGroupIndex) { insertedDiagonalElement = true; - } else if (insertDiagonalEntries && !insertedDiagonalElement && it->first > rowGroupIndex) { + } else if (insertDiagonalEntries && !insertedDiagonalElement && it->getColumn() > rowGroupIndex) { matrixBuilder.addNextValue(rowGroupIndex, rowGroupIndex, storm::utility::constantZero<T>()); insertedDiagonalElement = true; } - matrixBuilder.addNextValue(rowGroupIndex, it->first, it->second); + matrixBuilder.addNextValue(rowGroupIndex, it->getColumn(), it->getValue()); } if (insertDiagonalEntries && !insertedDiagonalElement) { matrixBuilder.addNextValue(rowGroupIndex, rowGroupIndex, storm::utility::constantZero<T>()); @@ -586,13 +621,13 @@ namespace storm { uint_fast64_t entryCount = this->getEntryCount(); std::vector<uint_fast64_t> rowIndications(rowCount + 1); - std::vector<std::pair<uint_fast64_t, T>> columnsAndValues(entryCount); + std::vector<MatrixEntry<T>> columnsAndValues(entryCount); // First, we need to count how many entries each column has. for (uint_fast64_t group = 0; group < columnCount; ++group) { for (auto const& transition : joinGroups ? this->getRowGroup(group) : this->getRow(group)) { - if (transition.second != storm::utility::constantZero<T>()) { - ++rowIndications[transition.first + 1]; + if (transition.getValue() != storm::utility::constantZero<T>()) { + ++rowIndications[transition.getColumn() + 1]; } } } @@ -610,9 +645,9 @@ namespace storm { // Now we are ready to actually fill in the values of the transposed matrix. for (uint_fast64_t group = 0; group < columnCount; ++group) { for (auto const& transition : joinGroups ? this->getRowGroup(group) : this->getRow(group)) { - if (transition.second != storm::utility::constantZero<T>()) { - columnsAndValues[nextIndices[transition.first]] = std::make_pair(group, transition.second); - nextIndices[transition.first]++; + if (transition.getValue() != storm::utility::constantZero<T>()) { + columnsAndValues[nextIndices[transition.getColumn()]] = std::make_pair(group, transition.getValue()); + nextIndices[transition.getColumn()]++; } } } @@ -641,8 +676,8 @@ namespace storm { bool foundDiagonalElement = false; for (uint_fast64_t group = 0; group < this->getRowGroupCount(); ++group) { for (auto& entry : this->getRowGroup(group)) { - if (entry.first == group) { - entry.second = one - entry.second; + if (entry.getColumn() == group) { + entry.getValue() = one - entry.getValue(); foundDiagonalElement = true; } } @@ -659,8 +694,8 @@ namespace storm { // Iterate over all row groups and negate all the elements that are not on the diagonal. for (uint_fast64_t group = 0; group < this->getRowGroupCount(); ++group) { for (auto& entry : this->getRowGroup(group)) { - if (entry.first != group) { - entry.second = -entry.second; + if (entry.getColumn() != group) { + entry.getValue() = -entry.getValue(); } } } @@ -671,8 +706,8 @@ namespace storm { // Iterate over all rows and negate all the elements that are not on the diagonal. for (uint_fast64_t group = 0; group < this->getRowGroupCount(); ++group) { for (auto& entry : this->getRowGroup(group)) { - if (entry.first == group) { - entry.second = storm::utility::constantZero<T>(); + if (entry.getColumn() == group) { + entry.getValue() = storm::utility::constantZero<T>(); } } } @@ -695,9 +730,9 @@ namespace storm { // to invert the entry. T diagonalValue = storm::utility::constantZero<T>(); for (const_iterator it = this->begin(rowNumber), ite = this->end(rowNumber); it != ite; ++it) { - if (it->first == rowNumber) { - diagonalValue += it->second; - } else if (it->first > rowNumber) { + if (it->getColumn() == rowNumber) { + diagonalValue += it->getValue(); + } else if (it->getColumn() > rowNumber) { break; } } @@ -716,13 +751,13 @@ namespace storm { // add the result to the corresponding position in the vector. for (uint_fast64_t row = 0; row < rowCount && row < otherMatrix.rowCount; ++row) { for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = otherMatrix.begin(row), ite2 = otherMatrix.end(row); it1 != ite1 && it2 != ite2; ++it1) { - if (it1->first < it2->first) { + if (it1->getColumn() < it2->getColumn()) { continue; } else { // If the precondition of this method (i.e. that the given matrix is a submatrix // of the current one) was fulfilled, we know now that the two elements are in // the same column, so we can multiply and add them to the row sum vector. - result[row] += it2->second * it1->second; + result[row] += it2->getValue() * it1->getValue(); ++it2; } } @@ -750,7 +785,7 @@ namespace storm { *resultIterator = storm::utility::constantZero<T>(); for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) { - *resultIterator += it->second * vector[it->first]; + *resultIterator += it->getValue() * vector[it->getColumn()]; } } }); @@ -765,7 +800,7 @@ namespace storm { *resultIterator = storm::utility::constantZero<T>(); for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) { - *resultIterator += it->second * vector[it->first]; + *resultIterator += it->getValue() * vector[it->getColumn()]; } } #endif @@ -776,7 +811,7 @@ namespace storm { uint_fast64_t size = sizeof(*this); // Add size of columns and values. - size += sizeof(std::pair<uint_fast64_t, T>) * columnsAndValues.capacity(); + size += sizeof(MatrixEntry<T>) * columnsAndValues.capacity(); // Add row_indications size. size += sizeof(uint_fast64_t) * rowIndications.capacity(); @@ -848,7 +883,7 @@ namespace storm { T SparseMatrix<T>::getRowSum(uint_fast64_t row) const { T sum = storm::utility::constantZero<T>(); for (const_iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) { - sum += it->second; + sum += it->getValue(); } return sum; } @@ -864,10 +899,10 @@ namespace storm { for (uint_fast64_t row = 0; row < this->getRowCount(); ++row) { for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = matrix.begin(row), ite2 = matrix.end(row); it1 != ite1; ++it1) { // Skip over all entries of the other matrix that are before the current entry in the current matrix. - while (it2 != ite2 && it2->first < it1->first) { + while (it2 != ite2 && it2->getColumn() < it1->getColumn()) { ++it2; } - if (it2 == ite2 || it1->first != it2->first) { + if (it2 == ite2 || it1->getColumn() != it2->getColumn()) { return false; } } @@ -894,8 +929,8 @@ namespace storm { out << i << "\t(\t"; uint_fast64_t currentRealIndex = 0; while (currentRealIndex < matrix.columnCount) { - if (nextIndex < matrix.rowIndications[i + 1] && currentRealIndex == matrix.columnsAndValues[nextIndex].first) { - out << matrix.columnsAndValues[nextIndex].second << "\t"; + if (nextIndex < matrix.rowIndications[i + 1] && currentRealIndex == matrix.columnsAndValues[nextIndex].getColumn()) { + out << matrix.columnsAndValues[nextIndex].getValue() << "\t"; ++nextIndex; } else { out << "0\t"; @@ -930,10 +965,12 @@ namespace storm { return result; } - // Explicitly instantiate the builder and the matrix. + // Explicitly instantiate the entry, builder and the matrix. + template class MatrixEntry<double>; template class SparseMatrixBuilder<double>; template class SparseMatrix<double>; template std::ostream& operator<<(std::ostream& out, SparseMatrix<double> const& matrix); + template class MatrixEntry<int>; template class SparseMatrixBuilder<int>; template class SparseMatrix<int>; template std::ostream& operator<<(std::ostream& out, SparseMatrix<int> const& matrix); diff --git a/src/storage/SparseMatrix.h b/src/storage/SparseMatrix.h index b8b8b80d3..b60cbdc16 100644 --- a/src/storage/SparseMatrix.h +++ b/src/storage/SparseMatrix.h @@ -8,6 +8,7 @@ #include "src/storage/BitVector.h" #include "src/utility/constants.h" +#include "src/utility/OsDetection.h" #include "src/exceptions/InvalidArgumentException.h" #include "src/exceptions/OutOfRangeException.h" @@ -27,6 +28,83 @@ namespace storm { // Forward declare matrix class. template<typename T> class SparseMatrix; + template<typename T> + class MatrixEntry { + public: + /*! + * Constructs a matrix entry with the given column and value. + * + * @param column The column of the matrix entry. + * @param value The value of the matrix entry. + */ + MatrixEntry(uint_fast64_t column, T value); + + /*! + * Move-constructs the matrix entry fro the given column-value pair. + * + * @param pair The column-value pair from which to move-construct the matrix entry. + */ + MatrixEntry(std::pair<uint_fast64_t, T>&& pair); + + MatrixEntry() = default; + MatrixEntry(MatrixEntry const& other) = default; + MatrixEntry& operator=(MatrixEntry const& other) = default; +#ifndef WINDOWS + MatrixEntry(MatrixEntry&& other) = default; + MatrixEntry& operator=(MatrixEntry&& other) = default; +#endif + + /*! + * Retrieves the column of the matrix entry. + * + * @return The column of the matrix entry. + */ + uint_fast64_t const& getColumn() const; + + /*! + * Retrieves the column of the matrix entry. + * + * @return The column of the matrix entry. + */ + uint_fast64_t& getColumn(); + + /*! + * Retrieves the value of the matrix entry. + * + * @return The value of the matrix entry. + */ + T const& getValue() const; + + /*! + * Retrieves the value of the matrix entry. + * + * @return The value of the matrix entry. + */ + T& getValue(); + + /*! + * Retrieves a pair of column and value that characterizes this entry. + * + * @return A column-value pair that characterizes this entry. + */ + std::pair<uint_fast64_t, T> const& getColumnValuePair() const; + + private: + // The actual matrix entry. + std::pair<uint_fast64_t, T> entry; + }; + + /*! + * Computes the hash value of a matrix entry. + */ + template<typename T> + std::size_t hash_value(MatrixEntry<T> const& matrixEntry) { + std::size_t seed = 0; + boost::hash_combine(seed, matrixEntry.getColumn()); + boost::hash_combine(seed, matrixEntry.getValue()); + return seed; + } + /*! * A class that can be used to build a sparse matrix by adding value by value. */ @@ -128,7 +206,7 @@ namespace storm { bool storagePreallocated; // The storage for the columns and values of all entries in the matrix. - std::vector<std::pair<uint_fast64_t, T>> columnsAndValues; + std::vector<MatrixEntry<T>> columnsAndValues; // A vector containing the indices at which each given row begins. This index is to be interpreted as an // index in the valueStorage and the columnIndications vectors. Put differently, the values of the entries @@ -175,8 +253,8 @@ namespace storm { friend class storm::adapters::EigenAdapter; friend class storm::adapters::StormAdapter; - typedef typename std::vector<std::pair<uint_fast64_t, T>>::iterator iterator; - typedef typename std::vector<std::pair<uint_fast64_t, T>>::const_iterator const_iterator; + typedef typename std::vector<MatrixEntry<T>>::iterator iterator; + typedef typename std::vector<MatrixEntry<T>>::const_iterator const_iterator; /*! * This class represents a number of consecutive rows of the matrix. @@ -284,7 +362,7 @@ namespace storm { * @param columnsAndValues The vector containing the columns and values of the entries in the matrix. * @param rowGroupIndices The vector representing the row groups in the matrix. */ - SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<std::pair<uint_fast64_t, T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices); + SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<MatrixEntry<T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices); /*! * Constructs a sparse matrix by moving the given contents. @@ -294,7 +372,7 @@ namespace storm { * @param columnsAndValues The vector containing the columns and values of the entries in the matrix. * @param rowGroupIndices The vector representing the row groups in the matrix. */ - SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<std::pair<uint_fast64_t, T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices); + SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<MatrixEntry<T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices); /*! * Assigns the contents of the given matrix to the current one by deep-copying its contents. @@ -662,7 +740,7 @@ namespace storm { uint_fast64_t nonzeroEntryCount; // The storage for the columns and values of all entries in the matrix. - std::vector<std::pair<uint_fast64_t, T>> columnsAndValues; + std::vector<MatrixEntry<T>> columnsAndValues; // A vector containing the indices at which each given row begins. This index is to be interpreted as an // index in the valueStorage and the columnIndications vectors. Put differently, the values of the entries diff --git a/src/storage/StronglyConnectedComponentDecomposition.cpp b/src/storage/StronglyConnectedComponentDecomposition.cpp index 6e9f08386..b14559bcb 100644 --- a/src/storage/StronglyConnectedComponentDecomposition.cpp +++ b/src/storage/StronglyConnectedComponentDecomposition.cpp @@ -114,33 +114,33 @@ namespace storm { // Now, traverse all successors of the current state. for(; successorIt != model.getRows(currentState).end(); ++successorIt) { // Record if the current state has a self-loop if we are to drop naive SCCs later. - if (dropNaiveSccs && currentState == successorIt->first) { + if (dropNaiveSccs && currentState == successorIt->getColumn()) { statesWithSelfloop.set(currentState, true); } // If we have not visited the successor already, we need to perform the procedure recursively on the // newly found state, but only if it belongs to the subsystem in which we are interested. - if (subsystem.get(successorIt->first)) { - if (!visitedStates.get(successorIt->first)) { + if (subsystem.get(successorIt->getColumn())) { + if (!visitedStates.get(successorIt->getColumn())) { // Save current iterator position so we can continue where we left off later. recursionIteratorStack.pop_back(); recursionIteratorStack.push_back(successorIt); // Put unvisited successor on top of our recursion stack and remember that. - recursionStateStack.push_back(successorIt->first); - statesInStack[successorIt->first] = true; + recursionStateStack.push_back(successorIt->getColumn()); + statesInStack[successorIt->getColumn()] = true; // Also, put initial value for iterator on corresponding recursion stack. - recursionIteratorStack.push_back(model.getRows(successorIt->first).begin()); + recursionIteratorStack.push_back(model.getRows(successorIt->getColumn()).begin()); // Perform the actual recursion step in an iterative way. goto recursionStepForward; recursionStepBackward: - lowlinks[currentState] = std::min(lowlinks[currentState], lowlinks[successorIt->first]); - } else if (tarjanStackStates.get(successorIt->first)) { + lowlinks[currentState] = std::min(lowlinks[currentState], lowlinks[successorIt->getColumn()]); + } else if (tarjanStackStates.get(successorIt->getColumn())) { // Update the lowlink of the current state. - lowlinks[currentState] = std::min(lowlinks[currentState], stateIndices[successorIt->first]); + lowlinks[currentState] = std::min(lowlinks[currentState], stateIndices[successorIt->getColumn()]); } } } @@ -165,7 +165,7 @@ namespace storm { if (onlyBottomSccs) { for (auto const& state : scc) { for (auto const& successor : model.getRows(state)) { - if (scc.find(successor.first) == scc.end()) { + if (scc.find(successor.getColumn()) == scc.end()) { isBottomScc = false; break; } diff --git a/src/utility/counterexamples.h b/src/utility/counterexamples.h index 23b5b372e..8b233e402 100644 --- a/src/utility/counterexamples.h +++ b/src/utility/counterexamples.h @@ -36,8 +36,8 @@ namespace storm { // for (auto state : psiStates) { // analysisInformation[state] = boost::container::flat_set<uint_fast64_t>(); // for (auto const& predecessorEntry : backwardTransitions.getRow(state)) { -// if (predecessorEntry.first != state && !psiStates.get(predecessorEntry.first)) { -// worklist.push(std::make_pair(predecessorEntry.first, state)); +// if (predecessorEntry.getColumn() != state && !psiStates.get(predecessorEntry.getColumn())) { +// worklist.push(std::make_pair(predecessorEntry.getColumn(), state)); // } // } // } @@ -57,7 +57,7 @@ namespace storm { // bool choiceTargetsTargetState = false; // // for (auto& entry : transitionMatrix.getRow(currentChoice)) { -// if (entry.first == targetState) { +// if (entry.getColumn() == targetState) { // choiceTargetsTargetState = true; // break; // } @@ -79,8 +79,8 @@ namespace storm { // if (analysisInformation[currentState].size() != analysisInformationSizeBefore) { // for (auto& predecessorEntry : backwardTransitions.getRow(currentState)) { // // Only put the predecessor in the worklist if it's not already a target state. -// if (!psiStates.get(predecessorEntry.first)) { -// worklist.push(std::make_pair(predecessorEntry.first, currentState)); +// if (!psiStates.get(predecessorEntry.getColumn())) { +// worklist.push(std::make_pair(predecessorEntry.getColumn(), currentState)); // } // } // } @@ -96,9 +96,9 @@ namespace storm { for (auto state : psiStates) { analysisInformation[state] = boost::container::flat_set<uint_fast64_t>(); for (auto const& predecessorEntry : backwardTransitions.getRow(state)) { - if (predecessorEntry.first != state && !statesInWorkList.get(predecessorEntry.first) && !psiStates.get(predecessorEntry.first)) { - worklist.push(predecessorEntry.first); - statesInWorkList.set(predecessorEntry.first); + if (predecessorEntry.getColumn() != state && !statesInWorkList.get(predecessorEntry.getColumn()) && !psiStates.get(predecessorEntry.getColumn())) { + worklist.push(predecessorEntry.getColumn()); + statesInWorkList.set(predecessorEntry.getColumn()); markedStates.set(state); } } @@ -116,7 +116,7 @@ namespace storm { bool modifiedChoice = false; for (auto const& entry : transitionMatrix.getRow(currentChoice)) { - if (markedStates.get(entry.first)) { + if (markedStates.get(entry.getColumn())) { modifiedChoice = true; break; } @@ -127,9 +127,9 @@ namespace storm { // and the choice labels. if (modifiedChoice) { for (auto const& entry : transitionMatrix.getRow(currentChoice)) { - if (markedStates.get(entry.first)) { + if (markedStates.get(entry.getColumn())) { boost::container::flat_set<uint_fast64_t> tmpIntersection; - std::set_intersection(analysisInformation[currentState].begin(), analysisInformation[currentState].end(), analysisInformation[entry.first].begin(), analysisInformation[entry.first].end(), std::inserter(tmpIntersection, tmpIntersection.begin())); + std::set_intersection(analysisInformation[currentState].begin(), analysisInformation[currentState].end(), analysisInformation[entry.getColumn()].begin(), analysisInformation[entry.getColumn()].end(), std::inserter(tmpIntersection, tmpIntersection.begin())); std::set_intersection(analysisInformation[currentState].begin(), analysisInformation[currentState].end(), choiceLabeling[currentChoice].begin(), choiceLabeling[currentChoice].end(), std::inserter(tmpIntersection, tmpIntersection.begin())); analysisInformation[currentState] = std::move(tmpIntersection); } @@ -142,9 +142,9 @@ namespace storm { if (analysisInformation[currentState].size() != analysisInformationSizeBefore) { for (auto const& predecessorEntry : backwardTransitions.getRow(currentState)) { // Only put the predecessor in the worklist if it's not already a target state. - if (!psiStates.get(predecessorEntry.first) && !statesInWorkList.get(predecessorEntry.first)) { - worklist.push(predecessorEntry.first); - statesInWorkList.set(predecessorEntry.first); + if (!psiStates.get(predecessorEntry.getColumn()) && !statesInWorkList.get(predecessorEntry.getColumn())) { + worklist.push(predecessorEntry.getColumn()); + statesInWorkList.set(predecessorEntry.getColumn()); } } markedStates.set(currentState, true); diff --git a/src/utility/graph.h b/src/utility/graph.h index 01d0a7f4f..3d0d95fb1 100644 --- a/src/utility/graph.h +++ b/src/utility/graph.h @@ -79,16 +79,16 @@ namespace storm { } for (typename storm::storage::SparseMatrix<T>::const_iterator entryIt = backwardTransitions.begin(currentState), entryIte = backwardTransitions.end(currentState); entryIt != entryIte; ++entryIt) { - if (phiStates[entryIt->first] && (!statesWithProbabilityGreater0.get(entryIt->first) || (useStepBound && remainingSteps[entryIt->first] < currentStepBound - 1))) { + if (phiStates[entryIt->getColumn()] && (!statesWithProbabilityGreater0.get(entryIt->getColumn()) || (useStepBound && remainingSteps[entryIt->getColumn()] < currentStepBound - 1))) { // If we don't have a bound on the number of steps to take, just add the state to the stack. if (!useStepBound) { - statesWithProbabilityGreater0.set(entryIt->first, true); - stack.push_back(entryIt->first); + statesWithProbabilityGreater0.set(entryIt->getColumn(), true); + stack.push_back(entryIt->getColumn()); } else if (currentStepBound > 0) { // If there is at least one more step to go, we need to push the state and the new number of steps. - remainingSteps[entryIt->first] = currentStepBound - 1; - statesWithProbabilityGreater0.set(entryIt->first, true); - stack.push_back(entryIt->first); + remainingSteps[entryIt->getColumn()] = currentStepBound - 1; + statesWithProbabilityGreater0.set(entryIt->getColumn(), true); + stack.push_back(entryIt->getColumn()); stepStack.push_back(currentStepBound - 1); } } @@ -211,16 +211,16 @@ namespace storm { } for (typename storm::storage::SparseMatrix<T>::const_iterator entryIt = backwardTransitions.begin(currentState), entryIte = backwardTransitions.end(currentState); entryIt != entryIte; ++entryIt) { - if (phiStates.get(entryIt->first) && (!statesWithProbabilityGreater0.get(entryIt->first) || (useStepBound && remainingSteps[entryIt->first] < currentStepBound - 1))) { + if (phiStates.get(entryIt->getColumn()) && (!statesWithProbabilityGreater0.get(entryIt->getColumn()) || (useStepBound && remainingSteps[entryIt->getColumn()] < currentStepBound - 1))) { // If we don't have a bound on the number of steps to take, just add the state to the stack. if (!useStepBound) { - statesWithProbabilityGreater0.set(entryIt->first, true); - stack.push_back(entryIt->first); + statesWithProbabilityGreater0.set(entryIt->getColumn(), true); + stack.push_back(entryIt->getColumn()); } else if (currentStepBound > 0) { // If there is at least one more step to go, we need to push the state and the new number of steps. - remainingSteps[entryIt->first] = currentStepBound - 1; - statesWithProbabilityGreater0.set(entryIt->first, true); - stack.push_back(entryIt->first); + remainingSteps[entryIt->getColumn()] = currentStepBound - 1; + statesWithProbabilityGreater0.set(entryIt->getColumn(), true); + stack.push_back(entryIt->getColumn()); stepStack.push_back(currentStepBound - 1); } } @@ -290,13 +290,13 @@ namespace storm { stack.pop_back(); for (typename storm::storage::SparseMatrix<T>::const_iterator predecessorEntryIt = backwardTransitions.begin(currentState), predecessorEntryIte = backwardTransitions.end(currentState); predecessorEntryIt != predecessorEntryIte; ++predecessorEntryIt) { - if (phiStates.get(predecessorEntryIt->first) && !nextStates.get(predecessorEntryIt->first)) { + if (phiStates.get(predecessorEntryIt->getColumn()) && !nextStates.get(predecessorEntryIt->getColumn())) { // Check whether the predecessor has only successors in the current state set for one of the // nondeterminstic choices. - for (uint_fast64_t row = nondeterministicChoiceIndices[predecessorEntryIt->first]; row < nondeterministicChoiceIndices[predecessorEntryIt->first + 1]; ++row) { + for (uint_fast64_t row = nondeterministicChoiceIndices[predecessorEntryIt->getColumn()]; row < nondeterministicChoiceIndices[predecessorEntryIt->getColumn() + 1]; ++row) { bool allSuccessorsInCurrentStates = true; for (typename storm::storage::SparseMatrix<T>::const_iterator successorEntryIt = transitionMatrix.begin(row), successorEntryIte = transitionMatrix.end(row); successorEntryIt != successorEntryIte; ++successorEntryIt) { - if (!currentStates.get(successorEntryIt->first)) { + if (!currentStates.get(successorEntryIt->getColumn())) { allSuccessorsInCurrentStates = false; break; } @@ -306,8 +306,8 @@ namespace storm { // add it to the set of states for the next iteration and perform a backward search from // that state. if (allSuccessorsInCurrentStates) { - nextStates.set(predecessorEntryIt->first, true); - stack.push_back(predecessorEntryIt->first); + nextStates.set(predecessorEntryIt->getColumn(), true); + stack.push_back(predecessorEntryIt->getColumn()); break; } } @@ -401,14 +401,14 @@ namespace storm { } for(typename storm::storage::SparseMatrix<T>::const_iterator predecessorEntryIt = backwardTransitions.begin(currentState), predecessorEntryIte = backwardTransitions.end(currentState); predecessorEntryIt != predecessorEntryIte; ++predecessorEntryIt) { - if (phiStates.get(predecessorEntryIt->first) && (!statesWithProbabilityGreater0.get(predecessorEntryIt->first) || (useStepBound && remainingSteps[predecessorEntryIt->first] < currentStepBound - 1))) { + if (phiStates.get(predecessorEntryIt->getColumn()) && (!statesWithProbabilityGreater0.get(predecessorEntryIt->getColumn()) || (useStepBound && remainingSteps[predecessorEntryIt->getColumn()] < currentStepBound - 1))) { // Check whether the predecessor has at least one successor in the current state set for every // nondeterministic choice. bool addToStatesWithProbabilityGreater0 = true; - for (uint_fast64_t row = nondeterministicChoiceIndices[predecessorEntryIt->first]; row < nondeterministicChoiceIndices[predecessorEntryIt->first + 1]; ++row) { + for (uint_fast64_t row = nondeterministicChoiceIndices[predecessorEntryIt->getColumn()]; row < nondeterministicChoiceIndices[predecessorEntryIt->getColumn() + 1]; ++row) { bool hasAtLeastOneSuccessorWithProbabilityGreater0 = false; for (typename storm::storage::SparseMatrix<T>::const_iterator successorEntryIt = transitionMatrix.begin(row), successorEntryIte = transitionMatrix.end(row); successorEntryIt != successorEntryIte; ++successorEntryIt) { - if (statesWithProbabilityGreater0.get(successorEntryIt->first)) { + if (statesWithProbabilityGreater0.get(successorEntryIt->getColumn())) { hasAtLeastOneSuccessorWithProbabilityGreater0 = true; break; } @@ -424,13 +424,13 @@ namespace storm { if (addToStatesWithProbabilityGreater0) { // If we don't have a bound on the number of steps to take, just add the state to the stack. if (!useStepBound) { - statesWithProbabilityGreater0.set(predecessorEntryIt->first, true); - stack.push_back(predecessorEntryIt->first); + statesWithProbabilityGreater0.set(predecessorEntryIt->getColumn(), true); + stack.push_back(predecessorEntryIt->getColumn()); } else if (currentStepBound > 0) { // If there is at least one more step to go, we need to push the state and the new number of steps. - remainingSteps[predecessorEntryIt->first] = currentStepBound - 1; - statesWithProbabilityGreater0.set(predecessorEntryIt->first, true); - stack.push_back(predecessorEntryIt->first); + remainingSteps[predecessorEntryIt->getColumn()] = currentStepBound - 1; + statesWithProbabilityGreater0.set(predecessorEntryIt->getColumn(), true); + stack.push_back(predecessorEntryIt->getColumn()); stepStack.push_back(currentStepBound - 1); } } @@ -501,12 +501,12 @@ namespace storm { stack.pop_back(); for(typename storm::storage::SparseMatrix<T>::const_iterator predecessorEntryIt = backwardTransitions.begin(currentState), predecessorEntryIte = backwardTransitions.end(currentState); predecessorEntryIt != predecessorEntryIte; ++predecessorEntryIt) { - if (phiStates.get(predecessorEntryIt->first) && !nextStates.get(predecessorEntryIt->first)) { + if (phiStates.get(predecessorEntryIt->getColumn()) && !nextStates.get(predecessorEntryIt->getColumn())) { // Check whether the predecessor has only successors in the current state set for all of the // nondeterminstic choices. bool allSuccessorsInCurrentStatesForAllChoices = true; - for (typename storm::storage::SparseMatrix<T>::const_iterator successorEntryIt = transitionMatrix.begin(nondeterministicChoiceIndices[predecessorEntryIt->first]), successorEntryIte = transitionMatrix.begin(nondeterministicChoiceIndices[predecessorEntryIt->first + 1]); successorEntryIt != successorEntryIte; ++successorEntryIt) { - if (!currentStates.get(successorEntryIt->first)) { + for (typename storm::storage::SparseMatrix<T>::const_iterator successorEntryIt = transitionMatrix.begin(nondeterministicChoiceIndices[predecessorEntryIt->getColumn()]), successorEntryIte = transitionMatrix.begin(nondeterministicChoiceIndices[predecessorEntryIt->getColumn() + 1]); successorEntryIt != successorEntryIte; ++successorEntryIt) { + if (!currentStates.get(successorEntryIt->getColumn())) { allSuccessorsInCurrentStatesForAllChoices = false; goto afterCheckLoop; } @@ -517,8 +517,8 @@ namespace storm { // add it to the set of states for the next iteration and perform a backward search from // that state. if (allSuccessorsInCurrentStatesForAllChoices) { - nextStates.set(predecessorEntryIt->first, true); - stack.push_back(predecessorEntryIt->first); + nextStates.set(predecessorEntryIt->getColumn(), true); + stack.push_back(predecessorEntryIt->getColumn()); } } } diff --git a/src/utility/matrix.h b/src/utility/matrix.h index 446fb413f..1027e7e42 100644 --- a/src/utility/matrix.h +++ b/src/utility/matrix.h @@ -32,7 +32,7 @@ namespace storm { // If a valid choice for this state is defined, we copy over the corresponding entries. typename storm::storage::SparseMatrix<T>::const_rows selectedRow = transitionMatrix.getRow(choice); for (auto const& entry : selectedRow) { - matrixBuilder.addNextValue(state, entry.first, entry.second); + matrixBuilder.addNextValue(state, entry.getColumn(), entry.getValue()); } } else { // If no valid choice for the state is defined, we insert a self-loop. diff --git a/test/functional/parser/DeterministicSparseTransitionParserTest.cpp b/test/functional/parser/DeterministicSparseTransitionParserTest.cpp index 2b301a1e7..89c5c9c6e 100644 --- a/test/functional/parser/DeterministicSparseTransitionParserTest.cpp +++ b/test/functional/parser/DeterministicSparseTransitionParserTest.cpp @@ -35,68 +35,68 @@ TEST(DeterministicSparseTransitionParserTest, BasicTransitionsParsing) { // Test every entry of the matrix. storm::storage::SparseMatrix<double>::const_iterator cIter = transitionMatrix.begin(0); - ASSERT_EQ(0, cIter->first); - ASSERT_EQ(0, cIter->second); + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(0, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(0.4, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.4, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.4, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.4, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(0.1, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.1, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(0.1, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); cIter++; - ASSERT_EQ(6, cIter->first); - ASSERT_EQ(0.7, cIter->second); + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(0.7, cIter->getValue()); cIter++; - ASSERT_EQ(0, cIter->first); - ASSERT_EQ(0.9, cIter->second); + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(0.9, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(0, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); cIter++; - ASSERT_EQ(6, cIter->first); - ASSERT_EQ(0.1, cIter->second); + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); cIter++; - ASSERT_EQ(6, cIter->first); - ASSERT_EQ(0.224653, cIter->second); + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(0.224653, cIter->getValue()); cIter++; - ASSERT_EQ(7, cIter->first); - ASSERT_EQ(0.775347, cIter->second); + ASSERT_EQ(7, cIter->getColumn()); + ASSERT_EQ(0.775347, cIter->getValue()); } TEST(DeterministicSparseTransitionParserTest, BasicTransitionsRewardsParsing) { @@ -112,56 +112,56 @@ TEST(DeterministicSparseTransitionParserTest, BasicTransitionsRewardsParsing) { // Test every entry of the matrix. storm::storage::SparseMatrix<double>::const_iterator cIter = rewardMatrix.begin(0); - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(10, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(10, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(5, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(5, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(5.5, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(5.5, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(21.4, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(21.4, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(4, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(4, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(2, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(2, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(0.1, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(1.1, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(1.1, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(9.5, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(9.5, cIter->getValue()); cIter++; - ASSERT_EQ(6, cIter->first); - ASSERT_EQ(6.7, cIter->second); + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(6.7, cIter->getValue()); cIter++; - ASSERT_EQ(0, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(0, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); cIter++; - ASSERT_EQ(6, cIter->first); - ASSERT_EQ(12, cIter->second); + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(12, cIter->getValue()); cIter++; - ASSERT_EQ(6, cIter->first); - ASSERT_EQ(35.224653, cIter->second); + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(35.224653, cIter->getValue()); cIter++; - ASSERT_EQ(7, cIter->first); - ASSERT_EQ(9.875347, cIter->second); + ASSERT_EQ(7, cIter->getColumn()); + ASSERT_EQ(9.875347, cIter->getValue()); } @@ -201,17 +201,17 @@ TEST(DeterministicSparseTransitionParserTest, FixDeadlocks) { ASSERT_EQ(23, transitionMatrix.getEntryCount()); storm::storage::SparseMatrix<double>::const_iterator cIter = transitionMatrix.begin(7); - ASSERT_EQ(7, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(7, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(6, cIter->first); - ASSERT_EQ(0.224653, cIter->second); + ASSERT_EQ(6, cIter->getColumn()); + ASSERT_EQ(0.224653, cIter->getValue()); cIter++; - ASSERT_EQ(7, cIter->first); - ASSERT_EQ(0.775347, cIter->second); + ASSERT_EQ(7, cIter->getColumn()); + ASSERT_EQ(0.775347, cIter->getValue()); cIter++; - ASSERT_EQ(8, cIter->first); - ASSERT_EQ(0, cIter->second); + ASSERT_EQ(8, cIter->getColumn()); + ASSERT_EQ(0, cIter->getValue()); } TEST(DeterministicSparseTransitionParserTest, DontFixDeadlocks) { diff --git a/test/functional/parser/MarkovAutomatonSparseTransitionParserTest.cpp b/test/functional/parser/MarkovAutomatonSparseTransitionParserTest.cpp index 81c2ccfb8..38b152cd4 100644 --- a/test/functional/parser/MarkovAutomatonSparseTransitionParserTest.cpp +++ b/test/functional/parser/MarkovAutomatonSparseTransitionParserTest.cpp @@ -79,29 +79,29 @@ TEST(MarkovAutomatonSparseTransitionParserTest, BasicParsing) { // Finally, test the transition matrix itself. storm::storage::SparseMatrix<double>::const_iterator cIter = transitionMatrix.begin(0); - ASSERT_EQ(2, cIter->second); + ASSERT_EQ(2, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->second); + ASSERT_EQ(2, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->second); + ASSERT_EQ(4, cIter->getValue()); cIter++; - ASSERT_EQ(8, cIter->second); + ASSERT_EQ(8, cIter->getValue()); cIter++; - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getValue()); cIter++; ASSERT_EQ(transitionMatrix.end(), cIter); } @@ -157,29 +157,29 @@ TEST(MarkovAutomatonSparseTransitionParserTest, Whitespaces) { // Finally, test the transition matrix itself. storm::storage::SparseMatrix<double>::const_iterator cIter = transitionMatrix.begin(0); - ASSERT_EQ(2, cIter->second); + ASSERT_EQ(2, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->second); + ASSERT_EQ(2, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->second); + ASSERT_EQ(4, cIter->getValue()); cIter++; - ASSERT_EQ(8, cIter->second); + ASSERT_EQ(8, cIter->getValue()); cIter++; - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(1, cIter->getValue()); cIter++; ASSERT_EQ(transitionMatrix.end(), cIter); } diff --git a/test/functional/parser/NondeterministicSparseTransitionParserTest.cpp b/test/functional/parser/NondeterministicSparseTransitionParserTest.cpp index 21240a9dd..9494c983f 100644 --- a/test/functional/parser/NondeterministicSparseTransitionParserTest.cpp +++ b/test/functional/parser/NondeterministicSparseTransitionParserTest.cpp @@ -48,71 +48,71 @@ TEST(NondeterministicSparseTransitionParserTest, BasicTransitionsParsing) { // Test every entry of the matrix. storm::storage::SparseMatrix<double>::const_iterator cIter = result.begin(0); - ASSERT_EQ(0, cIter->first); - ASSERT_EQ(0.9, cIter->second); + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(0.9, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0.1, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(0, cIter->first); - ASSERT_EQ(0.1, cIter->second); + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.9, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.9, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(0.5, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.5, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(0.001, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(0.001, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(0.999, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(0.999, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0.7, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.7, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.3, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.3, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(0.6, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0.6, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); } TEST(NondeterministicSparseTransitionParserTest, BasicTransitionsRewardsParsing) { @@ -128,56 +128,56 @@ TEST(NondeterministicSparseTransitionParserTest, BasicTransitionsRewardsParsing) // Test every entry of the matrix. storm::storage::SparseMatrix<double>::const_iterator cIter = result.begin(0); - ASSERT_EQ(0, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(30, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(30, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(15.2, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(15.2, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(75, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(75, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(2.45, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(2.45, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(0, cIter->first); - ASSERT_EQ(0.114, cIter->second); + ASSERT_EQ(0, cIter->getColumn()); + ASSERT_EQ(0.114, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(90, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(90, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(55, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(55, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(87, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(87, cIter->getValue()); cIter++; - ASSERT_EQ(2, cIter->first); - ASSERT_EQ(13, cIter->second); + ASSERT_EQ(2, cIter->getColumn()); + ASSERT_EQ(13, cIter->getValue()); cIter++; - ASSERT_EQ(3, cIter->first); - ASSERT_EQ(999, cIter->second); + ASSERT_EQ(3, cIter->getColumn()); + ASSERT_EQ(999, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0.7, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.7, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.3, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.3, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0.1, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.1, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(6, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(6, cIter->getValue()); } TEST(NondeterministicSparseTransitionParserTest, Whitespaces) { @@ -224,26 +224,26 @@ TEST(NondeterministicSparseTransitionParserTest, FixDeadlocks) { storm::storage::SparseMatrix<double>::const_iterator cIter = result.begin(8); - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0.7, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.7, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.3, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.3, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); cIter++; - ASSERT_EQ(1, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(1, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(4, cIter->first); - ASSERT_EQ(0.2, cIter->second); + ASSERT_EQ(4, cIter->getColumn()); + ASSERT_EQ(0.2, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(0.6, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(0.6, cIter->getValue()); cIter++; - ASSERT_EQ(5, cIter->first); - ASSERT_EQ(1, cIter->second); + ASSERT_EQ(5, cIter->getColumn()); + ASSERT_EQ(1, cIter->getValue()); } diff --git a/test/functional/storage/SparseMatrixTest.cpp b/test/functional/storage/SparseMatrixTest.cpp index 69517a6d4..de89f4950 100644 --- a/test/functional/storage/SparseMatrixTest.cpp +++ b/test/functional/storage/SparseMatrixTest.cpp @@ -147,7 +147,7 @@ TEST(SparseMatrix, Build) { } TEST(SparseMatrix, CreationWithMovingContents) { - std::vector<std::pair<uint_fast64_t, double>> columnsAndValues; + std::vector<storm::storage::MatrixEntry<double>> columnsAndValues; columnsAndValues.emplace_back(1, 1.0); columnsAndValues.emplace_back(2, 1.2); columnsAndValues.emplace_back(0, 0.5); @@ -540,24 +540,24 @@ TEST(SparseMatrix, Iteration) { ASSERT_NO_THROW(matrix = matrixBuilder.build()); for (auto const& entry : matrix.getRow(4)) { - if (entry.first == 0) { - ASSERT_EQ(0.1, entry.second); - } else if (entry.first == 1) { - ASSERT_EQ(0.2, entry.second); - } else if (entry.first == 3) { - ASSERT_EQ(0.3, entry.second); + if (entry.getColumn() == 0) { + ASSERT_EQ(0.1, entry.getValue()); + } else if (entry.getColumn() == 1) { + ASSERT_EQ(0.2, entry.getValue()); + } else if (entry.getColumn() == 3) { + ASSERT_EQ(0.3, entry.getValue()); } else { ASSERT_TRUE(false); } } for (storm::storage::SparseMatrix<double>::iterator it = matrix.begin(4), ite = matrix.end(4); it != ite; ++it) { - if (it->first == 0) { - ASSERT_EQ(0.1, it->second); - } else if (it->first == 1) { - ASSERT_EQ(0.2, it->second); - } else if (it->first == 3) { - ASSERT_EQ(0.3, it->second); + if (it->getColumn() == 0) { + ASSERT_EQ(0.1, it->getValue()); + } else if (it->getColumn() == 1) { + ASSERT_EQ(0.2, it->getValue()); + } else if (it->getColumn() == 3) { + ASSERT_EQ(0.3, it->getValue()); } else { ASSERT_TRUE(false); } diff --git a/test/performance/storage/SparseMatrixTest.cpp b/test/performance/storage/SparseMatrixTest.cpp index 367bd3401..b59d283af 100644 --- a/test/performance/storage/SparseMatrixTest.cpp +++ b/test/performance/storage/SparseMatrixTest.cpp @@ -15,7 +15,7 @@ TEST(SparseMatrix, Iteration) { for (uint_fast64_t row = 0; row < matrix.getRowCount(); ++row) { for (auto const& entry : matrix.getRow(row)) { // The following can never be true, but prevents the compiler from optimizing away the loop. - if (entry.first > matrix.getColumnCount()) { + if (entry.getColumn() > matrix.getColumnCount()) { ASSERT_TRUE(false); } } From 9d3e78ab898f1b862710f901a7391d4c0f40fec8 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Tue, 6 May 2014 23:24:04 +0200 Subject: [PATCH 04/15] Cudd now gets 2GB instead of 2MB by default. Former-commit-id: 06cf8094930ec671fa92d260896bfe894a2d7c7b --- src/storage/dd/CuddDdManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storage/dd/CuddDdManager.cpp b/src/storage/dd/CuddDdManager.cpp index b3854ee36..087382c71 100644 --- a/src/storage/dd/CuddDdManager.cpp +++ b/src/storage/dd/CuddDdManager.cpp @@ -32,7 +32,7 @@ bool CuddOptionsRegistered = storm::settings::Settings::registerNewModule([] (st namespace storm { namespace dd { DdManager<DdType::CUDD>::DdManager() : metaVariableMap(), cuddManager() { - this->cuddManager.SetMaxMemory(storm::settings::Settings::getInstance()->getOptionByLongName("cuddmaxmem").getArgument(0).getValueAsUnsignedInteger() * 1024); + this->cuddManager.SetMaxMemory(storm::settings::Settings::getInstance()->getOptionByLongName("cuddmaxmem").getArgument(0).getValueAsUnsignedInteger() * 1024 * 1024); this->cuddManager.SetEpsilon(storm::settings::Settings::getInstance()->getOptionByLongName("cuddprec").getArgument(0).getValueAsDouble()); } From 3158d19123def9d2ba29374a9a8aadebcea5093f Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Wed, 7 May 2014 18:27:44 +0200 Subject: [PATCH 05/15] Started working on adapting LP solver interface to new expressions. Former-commit-id: 6131736a7fe0e9411a2cef385722d0619e63105c --- src/solver/GlpkLpSolver.cpp | 262 ++++++++++-------- src/solver/GlpkLpSolver.h | 81 ++++-- src/solver/GurobiLpSolver.h | 80 ++++-- src/solver/LpSolver.h | 166 ++++++----- src/storage/expressions/Expression.cpp | 10 + src/storage/expressions/Expression.h | 8 + .../expressions/LinearityCheckVisitor.cpp | 78 ++++++ .../expressions/LinearityCheckVisitor.h | 41 +++ 8 files changed, 493 insertions(+), 233 deletions(-) create mode 100644 src/storage/expressions/LinearityCheckVisitor.cpp create mode 100644 src/storage/expressions/LinearityCheckVisitor.h diff --git a/src/solver/GlpkLpSolver.cpp b/src/solver/GlpkLpSolver.cpp index e122fcb16..fc2c5f261 100644 --- a/src/solver/GlpkLpSolver.cpp +++ b/src/solver/GlpkLpSolver.cpp @@ -4,13 +4,10 @@ #include <iostream> -#include "src/exceptions/InvalidStateException.h" #include "src/settings/Settings.h" - -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" - -extern log4cplus::Logger logger; +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidAccessException.h" +#include "src/exceptions/InvalidStateException.h" bool GlpkLpSolverOptionsRegistered = storm::settings::Settings::registerNewModule([] (storm::settings::Settings* instance) -> bool { instance->addOption(storm::settings::OptionBuilder("GlpkLpSolver", "glpkoutput", "", "If set, the glpk output will be printed to the command line.").build()); @@ -22,7 +19,7 @@ bool GlpkLpSolverOptionsRegistered = storm::settings::Settings::registerNewModul namespace storm { namespace solver { - GlpkLpSolver::GlpkLpSolver(std::string const& name, ModelSense const& modelSense) : LpSolver(modelSense), lp(nullptr), nextVariableIndex(1), nextConstraintIndex(1), modelContainsIntegerVariables(false), isInfeasibleFlag(false), isUnboundedFlag(false), rowIndices(), columnIndices(), coefficientValues() { + GlpkLpSolver::GlpkLpSolver(std::string const& name, ModelSense const& modelSense) : LpSolver(modelSense), lp(nullptr), variableNameToIndexMap(), nextVariableIndex(0), nextConstraintIndex(0), modelContainsIntegerVariables(false), isInfeasibleFlag(false), isUnboundedFlag(false), rowIndices(), columnIndices(), coefficientValues() { // Create the LP problem for glpk. lp = glp_create_prob(); @@ -38,11 +35,11 @@ namespace storm { coefficientValues.push_back(0); } - GlpkLpSolver::GlpkLpSolver(std::string const& name) : GlpkLpSolver(name, MINIMIZE) { + GlpkLpSolver::GlpkLpSolver(std::string const& name) : GlpkLpSolver(name, ModelSense::MINIMIZE) { // Intentionally left empty. } - GlpkLpSolver::GlpkLpSolver() : GlpkLpSolver("", MINIMIZE) { + GlpkLpSolver::GlpkLpSolver() : GlpkLpSolver("", ModelSense::MINIMIZE) { // Intentionally left empty. } @@ -56,69 +53,126 @@ namespace storm { glp_free_env(); } - uint_fast64_t GlpkLpSolver::createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + void GlpkLpSolver::addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { glp_add_cols(this->lp, 1); glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); - switch (variableType) { - case LpSolver::BOUNDED: - glp_set_col_bnds(lp, nextVariableIndex, GLP_DB, lowerBound, upperBound); - break; - case LpSolver::UNBOUNDED: - glp_set_col_bnds(lp, nextVariableIndex, GLP_FR, 0, 0); - break; - case LpSolver::UPPER_BOUND: - glp_set_col_bnds(lp, nextVariableIndex, GLP_UP, 0, upperBound); - break; - case LpSolver::LOWER_BOUND: - glp_set_col_bnds(lp, nextVariableIndex, GLP_LO, lowerBound, 0); - break; - } + glp_set_col_bnds(lp, nextVariableIndex, GLP_DB, lowerBound, upperBound); glp_set_col_kind(this->lp, nextVariableIndex, GLP_CV); glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); - ++nextVariableIndex; - - this->currentModelHasBeenOptimized = false; - return nextVariableIndex - 1; + this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); + ++this->nextVariableIndex; } - uint_fast64_t GlpkLpSolver::createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { - uint_fast64_t index = this->createContinuousVariable(name, variableType, lowerBound, upperBound, objectiveFunctionCoefficient); - glp_set_col_kind(this->lp, index, GLP_IV); + void GlpkLpSolver::addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) { + glp_add_cols(this->lp, 1); + glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); + glp_set_col_bnds(lp, nextVariableIndex, GLP_LO, lowerBound, 0); + glp_set_col_kind(this->lp, nextVariableIndex, GLP_CV); + glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); + this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); + ++this->nextVariableIndex; + } + + void GlpkLpSolver::addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) { + glp_add_cols(this->lp, 1); + glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); + glp_set_col_bnds(lp, nextVariableIndex, GLP_UP, 0, upperBound); + glp_set_col_kind(this->lp, nextVariableIndex, GLP_CV); + glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); + this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); + ++this->nextVariableIndex; + } + + void GlpkLpSolver::addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient) { + glp_add_cols(this->lp, 1); + glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); + glp_set_col_bnds(lp, nextVariableIndex, GLP_FR, 0, 0); + glp_set_col_kind(this->lp, nextVariableIndex, GLP_CV); + glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); + this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); + ++this->nextVariableIndex; + } + + void GlpkLpSolver::addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + glp_add_cols(this->lp, 1); + glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); + glp_set_col_bnds(lp, nextVariableIndex, GLP_DB, lowerBound, upperBound); + glp_set_col_kind(this->lp, nextVariableIndex, GLP_IV); + glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); + this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); + ++this->nextVariableIndex; this->modelContainsIntegerVariables = true; - return index; } - uint_fast64_t GlpkLpSolver::createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) { - uint_fast64_t index = this->createContinuousVariable(name, UNBOUNDED, 0, 1, objectiveFunctionCoefficient); - glp_set_col_kind(this->lp, index, GLP_BV); + void GlpkLpSolver::addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) { + glp_add_cols(this->lp, 1); + glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); + glp_set_col_bnds(lp, nextVariableIndex, GLP_LO, lowerBound, 0); + glp_set_col_kind(this->lp, nextVariableIndex, GLP_IV); + glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); + this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); + ++this->nextVariableIndex; + this->modelContainsIntegerVariables = true; + } + + void GlpkLpSolver::addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) { + glp_add_cols(this->lp, 1); + glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); + glp_set_col_bnds(lp, nextVariableIndex, GLP_UP, 0, upperBound); + glp_set_col_kind(this->lp, nextVariableIndex, GLP_IV); + glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); + this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); + ++this->nextVariableIndex; + this->modelContainsIntegerVariables = true; + } + + void GlpkLpSolver::addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient) { + glp_add_cols(this->lp, 1); + glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); + glp_set_col_bnds(lp, nextVariableIndex, GLP_FR, 0, 0); + glp_set_col_kind(this->lp, nextVariableIndex, GLP_IV); + glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); + this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); + ++this->nextVariableIndex; + this->modelContainsIntegerVariables = true; + } + + void GlpkLpSolver::addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) { + glp_add_cols(this->lp, 1); + glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); + glp_set_col_bnds(lp, nextVariableIndex, GLP_FR, 0, 0); + glp_set_col_kind(this->lp, nextVariableIndex, GLP_BV); + glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); + this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); + ++this->nextVariableIndex; this->modelContainsIntegerVariables = true; - return index; } void GlpkLpSolver::update() const { // Intentionally left empty. } - void GlpkLpSolver::addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) { - if (variables.size() != coefficients.size()) { - LOG4CPLUS_ERROR(logger, "Sizes of variable indices vector and coefficients vector do not match."); - throw storm::exceptions::InvalidStateException() << "Sizes of variable indices vector and coefficients vector do not match."; - } - + void GlpkLpSolver::addConstraint(std::string const& name, storm::expressions::Expression const& constraint) { // Add the row that will represent this constraint. glp_add_rows(this->lp, 1); glp_set_row_name(this->lp, nextConstraintIndex, name.c_str()); + LOG_THROW(constraint.isRelationalExpression(), storm::exceptions::InvalidArgumentException, "Illegal constraint is not a relational expression."); + LOG_THROW(constraint.getOperator() != storm::expressions::OperatorType::NotEqual, storm::exceptions::InvalidArgumentException, "Illegal constraint uses inequality operator."); + + // TODO: get variable/coefficients vector from constraint. + + // Determine the type of the constraint and add it properly. - switch (boundType) { - case LESS: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightHandSideValue - storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); + switch (constraint.getOperator()) { + case storm::expressions::OperatorType::Less: + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightHandSideValue - storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble()); break; case LESS_EQUAL: glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightHandSideValue); break; case GREATER: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightHandSideValue + storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble(), 0); + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightHandSideValue + storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(), 0); break; case GREATER_EQUAL: glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightHandSideValue, 0); @@ -143,13 +197,13 @@ namespace storm { this->isUnboundedFlag = false; // Start by setting the model sense. - glp_set_obj_dir(this->lp, this->getModelSense() == MINIMIZE ? GLP_MIN : GLP_MAX); + glp_set_obj_dir(this->lp, this->getModelSense() == LpSolver::ModelSense::MINIMIZE ? GLP_MIN : GLP_MAX); glp_load_matrix(this->lp, rowIndices.size() - 1, rowIndices.data(), columnIndices.data(), coefficientValues.data()); int error = 0; if (this->modelContainsIntegerVariables) { - glp_iocp* parameters = new glp_iocp; + glp_iocp* parameters = new glp_iocp(); glp_init_iocp(parameters); parameters->presolve = GLP_ON; parameters->tol_int = storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(); @@ -171,11 +225,7 @@ namespace storm { error = glp_simplex(this->lp, nullptr); } - if (error != 0) { - LOG4CPLUS_ERROR(logger, "Unable to optimize glpk model (" << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to optimize glpk model (" << error << ")."; - } - + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to optimize glpk model (" << error << ")."); this->currentModelHasBeenOptimized = true; } @@ -217,103 +267,75 @@ namespace storm { return status == GLP_OPT; } - int_fast64_t GlpkLpSolver::getIntegerValue(uint_fast64_t variableIndex) const { + double GlpkLpSolver::getContinuousValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from infeasible model."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unbounded model."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unoptimized model."); } - + + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); + double value = 0; if (this->modelContainsIntegerVariables) { - value = glp_mip_col_val(this->lp, static_cast<int>(variableIndex)); + value = glp_mip_col_val(this->lp, static_cast<int>(variableIndexPair->second)); } else { - value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex)); - } - - if (std::abs(value - static_cast<int>(value)) <= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) { - // Nothing to do in this case. - } else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) { - LOG4CPLUS_ERROR(logger, "Illegal value for integer variable in glpk solution (" << value << ")."); - throw storm::exceptions::InvalidStateException() << "Illegal value for integer variable in glpk solution (" << value << ")."; + value = glp_get_col_prim(this->lp, static_cast<int>(variableIndexPair->second)); } - - return static_cast<int_fast64_t>(value); + return value; } - bool GlpkLpSolver::getBinaryValue(uint_fast64_t variableIndex) const { + int_fast64_t GlpkLpSolver::getIntegerValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from infeasible model."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unbounded model."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unoptimized model."); } + + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); double value = 0; if (this->modelContainsIntegerVariables) { - value = glp_mip_col_val(this->lp, static_cast<int>(variableIndex)); + value = glp_mip_col_val(this->lp, static_cast<int>(variableIndexPair->second)); } else { - value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex)); + value = glp_get_col_prim(this->lp, static_cast<int>(variableIndexPair->second)); } - if (std::abs(value - 1) <= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) { - // Nothing to do in this case. - } else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) { - LOG4CPLUS_ERROR(logger, "Illegal value for binary variable in Gurobi solution (" << value << ")."); - throw storm::exceptions::InvalidStateException() << "Illegal value for binary variable in Gurobi solution (" << value << ")."; - } + // Now check the desired precision was actually achieved. + LOG_THROW(std::abs(static_cast<int>(value) - value) <= storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for integer variable in glpk solution (" << value << ")."); - return static_cast<bool>(value); + return static_cast<int_fast64_t>(value); } - double GlpkLpSolver::getContinuousValue(uint_fast64_t variableIndex) const { + bool GlpkLpSolver::getBinaryValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from infeasible model."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unbounded model."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unoptimized model."); } + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); + double value = 0; if (this->modelContainsIntegerVariables) { - value = glp_mip_col_val(this->lp, static_cast<int>(variableIndex)); + value = glp_mip_col_val(this->lp, static_cast<int>(variableIndexPair->second)); } else { - value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex)); + value = glp_get_col_prim(this->lp, static_cast<int>(variableIndexPair->second)); } - return value; + + LOG_THROW(std::abs(static_cast<int>(value) - value) <= storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for binary variable in glpk solution (" << value << ")."); + + return static_cast<bool>(value); } double GlpkLpSolver::getObjectiveValue() const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from infeasible model."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unbounded model."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unoptimized model."); } double value = 0; diff --git a/src/solver/GlpkLpSolver.h b/src/solver/GlpkLpSolver.h index 2bc2bf397..0045ced73 100644 --- a/src/solver/GlpkLpSolver.h +++ b/src/solver/GlpkLpSolver.h @@ -14,7 +14,6 @@ namespace storm { namespace solver { #ifdef STORM_HAVE_GLPK - /*! * A class that implements the LpSolver interface using glpk as the background solver. */ @@ -56,33 +55,53 @@ namespace storm { */ virtual ~GlpkLpSolver(); - virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override; - virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override; - virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override; + // Methods to add continuous variables. + virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; + + // Methods to add integer variables. + virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; + + // Methods to add binary variables. + virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override; + + // Methods to incorporate recent changes. virtual void update() const override; - virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override; + // Methods to add constraints + virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) override; + // Methods to optimize and retrieve optimality status. virtual void optimize() const override; virtual bool isInfeasible() const override; virtual bool isUnbounded() const override; virtual bool isOptimal() const override; - virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override; - virtual bool getBinaryValue(uint_fast64_t variableIndex) const override; - virtual double getContinuousValue(uint_fast64_t variableIndex) const override; + // Methods to retrieve values of variables and the objective function in the optimal solutions. + virtual double getContinuousValue(std::string const& name) const override; + virtual int_fast64_t getIntegerValue(std::string const& name) const override; + virtual bool getBinaryValue(std::string const& name) const override; virtual double getObjectiveValue() const override; + // Methods to print the LP problem to a file. virtual void writeModelToFile(std::string const& filename) const override; private: // The glpk LP problem. glp_prob* lp; - // A counter that keeps track of the next free variable index. + // A mapping from variable names to their indices. + std::map<std::string, uint_fast64_t> variableNameToIndexMap; + + // A counter used for getting the next variable index. uint_fast64_t nextVariableIndex; - // A counter that keeps track of the next free constraint index. + // A counter used for getting the next constraint index. uint_fast64_t nextConstraintIndex; // A flag storing whether the model is an LP or an MILP. @@ -121,15 +140,39 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override { + virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override { + virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override { + virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; + } + + virtual uint_fast64_t addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } @@ -137,14 +180,14 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override { + virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - + virtual void optimize() const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - + virtual bool isInfeasible() const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } @@ -157,15 +200,15 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override { + virtual double getContinuousValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual bool getBinaryValue(uint_fast64_t variableIndex) const override { + virtual int_fast64_t getIntegerValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual double getContinuousValue(uint_fast64_t variableIndex) const override { + virtual bool getBinaryValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } diff --git a/src/solver/GurobiLpSolver.h b/src/solver/GurobiLpSolver.h index 18c5bbdbd..27ac66c52 100644 --- a/src/solver/GurobiLpSolver.h +++ b/src/solver/GurobiLpSolver.h @@ -60,23 +60,40 @@ namespace storm { */ virtual ~GurobiLpSolver(); - virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override; - virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override; - virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override; + // Methods to add continuous variables. + virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; + + // Methods to add integer variables. + virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override; + virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; + + // Methods to add binary variables. + virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override; + + // Methods to incorporate recent changes. virtual void update() const override; - - virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override; + // Methods to add constraints + virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) override; + + // Methods to optimize and retrieve optimality status. virtual void optimize() const override; virtual bool isInfeasible() const override; virtual bool isUnbounded() const override; virtual bool isOptimal() const override; - - virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override; - virtual bool getBinaryValue(uint_fast64_t variableIndex) const override; - virtual double getContinuousValue(uint_fast64_t variableIndex) const override; + + // Methods to retrieve values of variables and the objective function in the optimal solutions. + virtual double getContinuousValue(std::string const& name) const override; + virtual int_fast64_t getIntegerValue(std::string const& name) const override; + virtual bool getBinaryValue(std::string const& name) const override; virtual double getObjectiveValue() const override; - + + // Methods to print the LP problem to a file. virtual void writeModelToFile(std::string const& filename) const override; private: @@ -91,8 +108,8 @@ namespace storm { // The Gurobi model. GRBmodel* model; - // A counter that keeps track of the next free variable index. - uint_fast64_t nextVariableIndex; + // A mapping from variable names to their indices. + std::map<std::string, uint_fast64_t> variableNameToIndexMap; }; #else // If Gurobi is not available, we provide a stub implementation that emits an error if any of its methods is called. @@ -114,27 +131,46 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override { + virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override { + virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } + + virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override { + virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual void update() const override { + virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; + } + + virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override { + virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual void setModelSense(ModelSense const& modelSense) { + virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; + } + + virtual uint_fast64_t addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; + } + + virtual void update() const override { + throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; + } + + virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } @@ -154,15 +190,15 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override { + virtual double getContinuousValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual bool getBinaryValue(uint_fast64_t variableIndex) const override { + virtual int_fast64_t getIntegerValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual double getContinuousValue(uint_fast64_t variableIndex) const override { + virtual bool getBinaryValue(std::string const& name) const override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } @@ -175,7 +211,7 @@ namespace storm { } }; #endif - + } } diff --git a/src/solver/LpSolver.h b/src/solver/LpSolver.h index 311ee4b44..f775f2829 100644 --- a/src/solver/LpSolver.h +++ b/src/solver/LpSolver.h @@ -5,34 +5,17 @@ #include <vector> #include <cstdint> +#include "src/storage/expressions/Expression.h" + namespace storm { namespace solver { - /*! * An interface that captures the functionality of an LP solver. */ class LpSolver { public: - // An enumeration to represent the possible types of variables. Variables may be either unbounded, have only - // a lower or an upper bound, respectively, or be bounded from below and from above. - enum VariableType { - UNBOUNDED, - LOWER_BOUND, - UPPER_BOUND, - BOUNDED, - }; - - // An enumeration to represent the possible types of constraints. - enum BoundType { - LESS, - LESS_EQUAL, - GREATER, - GREATER_EQUAL, - EQUAL - }; - // An enumeration to represent whether the objective function is to be minimized or maximized. - enum ModelSense { + enum class ModelSense { MINIMIZE, MAXIMIZE }; @@ -40,7 +23,7 @@ namespace storm { /*! * Creates an empty LP solver. By default the objective function is assumed to be minimized. */ - LpSolver() : currentModelHasBeenOptimized(false), modelSense(MINIMIZE) { + LpSolver() : currentModelHasBeenOptimized(false), modelSense(ModelSense::MINIMIZE) { // Intentionally left empty. } @@ -55,52 +38,99 @@ namespace storm { } /*! - * Creates a continuous variable, i.e. a variable that may take all real values within its bounds. + * Registers an upper- and lower-bounded continuous variable, i.e. a variable that may take all real values + * within its bounds. + * + * @param name The name of the variable. + * @param lowerBound The lower bound of the variable. + * @param upperBound The upper bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers a lower-bounded continuous variable, i.e. a variable that may take all real values up to its + * lower bound. + * + * @param name The name of the variable. + * @param lowerBound The lower bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers an upper-bounded continuous variable, i.e. a variable that may take all real values up to its + * upper bound. + * + * @param name The name of the variable. + * @param upperBound The upper bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers a unbounded continuous variable, i.e. a variable that may take all real values. * * @param name The name of the variable. - * @param variableType The type of the variable. - * @param lowerBound The lower bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from below. - * @param upperBound The upper bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from above. * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective - * function. If set to zero, the variable is irrelevant for the objective value. - * @return The index of the newly created variable. This index can be used to retrieve the variables value - * in a solution after the model has been optimized. + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. */ - virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) = 0; + virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) = 0; /*! - * Creates an integer variable. + * Registers an upper- and lower-bounded integer variable, i.e. a variable that may take all integer values + * within its bounds. + * + * @param name The name of the variable. + * @param lowerBound The lower bound of the variable. + * @param upperBound The upper bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers a lower-bounded integer variable, i.e. a variable that may take all integer values up to its + * lower bound. + * + * @param name The name of the variable. + * @param lowerBound The lower bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers an upper-bounded integer variable, i.e. a variable that may take all integer values up to its + * lower bound. + * + * @param name The name of the variable. + * @param upperBound The upper bound of the variable. + * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. + */ + virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) = 0; + + /*! + * Registers an unbounded integer variable, i.e. a variable that may take all integer values. * * @param name The name of the variable. - * @param variableType The type of the variable. - * @param lowerBound The lower bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from below. - * @param upperBound The upper bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from above. * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective - * function. If set to zero, the variable is irrelevant for the objective value. - * @return The index of the newly created variable. This index can be used to retrieve the variables value - * in a solution after the model has been optimized. + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. */ - virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) = 0; + virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) = 0; /*! - * Creates a binary variable, i.e. a variable that may be either zero or one. + * Registers a boolean variable, i.e. a variable that may be either 0 or 1. * * @param name The name of the variable. - * @param variableType The type of the variable. - * @param lowerBound The lower bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from below. - * @param upperBound The upper bound of the variable. Note that this parameter is ignored if the variable - * is not bounded from above. * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective - * function. If set to zero, the variable is irrelevant for the objective value. - * @return The index of the newly created variable. This index can be used to retrieve the variables value - * in a solution after the model has been optimized. + * function. If omitted (or set to zero), the variable is irrelevant for the objective value. */ - virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) = 0; + virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) = 0; /*! * Updates the model to make the variables that have been declared since the last call to <code>update</code> @@ -109,18 +139,13 @@ namespace storm { virtual void update() const = 0; /*! - * Adds a constraint of the form - * a_1*x_1 + ... + a_n*x_n op c - * to the LP problem. + * Adds a the given constraint to the LP problem. * * @param name The name of the constraint. If empty, the constraint has no name. - * @param variables A vector of variable indices that define the appearing variables x_1, ..., x_n. - * @param coefficients A vector of coefficients that define the a_1, ..., a_n. The i-ith entry specifies the - * coefficient of the variable whose index appears at the i-th position in the vector of variable indices. - * @param boundType The bound type specifies the operator op used in the constraint. - * @param rhs This defines the value of the constant appearing on the right-hand side of the constraint. + * @param constraint An expression that represents the constraint. The given constraint must be a linear + * (in)equality over the registered variables. */ - virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rhs) = 0; + virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) = 0; /*! * Optimizes the LP problem previously constructed. Afterwards, the methods isInfeasible, isUnbounded and @@ -154,34 +179,31 @@ namespace storm { virtual bool isOptimal() const = 0; /*! - * Retrieves the value of the integer variable with the given index. Note that this may only be called, if + * Retrieves the value of the integer variable with the given name. Note that this may only be called, if * the model was found to be optimal, i.e. iff isOptimal() returns true. * - * @param variableIndex The index of the integer variable whose value to query. If this index does not - * belong to a previously declared integer variable, the behaviour is undefined. + * @param name The name of the variable whose integer value (in the optimal solution) to retrieve. * @return The value of the integer variable in the optimal solution. */ - virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const = 0; + virtual int_fast64_t getIntegerValue(std::string const& name) const = 0; /*! - * Retrieves the value of the binary variable with the given index. Note that this may only be called, if + * Retrieves the value of the binary variable with the given name. Note that this may only be called, if * the model was found to be optimal, i.e. iff isOptimal() returns true. * - * @param variableIndex The index of the binary variable whose value to query. If this index does not - * belong to a previously declared binary variable, the behaviour is undefined. + * @param name The name of the variable whose binary (boolean) value (in the optimal solution) to retrieve. * @return The value of the binary variable in the optimal solution. */ - virtual bool getBinaryValue(uint_fast64_t variableIndex) const = 0; + virtual bool getBinaryValue(std::string const& name) const = 0; /*! - * Retrieves the value of the continuous variable with the given index. Note that this may only be called, + * Retrieves the value of the continuous variable with the given name. Note that this may only be called, * if the model was found to be optimal, i.e. iff isOptimal() returns true. * - * @param variableIndex The index of the continuous variable whose value to query. If this index does not - * belong to a previously declared continuous variable, the behaviour is undefined. + * @param name The name of the variable whose continuous value (in the optimal solution) to retrieve. * @return The value of the continuous variable in the optimal solution. */ - virtual double getContinuousValue(uint_fast64_t variableIndex) const = 0; + virtual double getContinuousValue(std::string const& name) const = 0; /*! * Retrieves the value of the objective function. Note that this may only be called, if the model was found diff --git a/src/storage/expressions/Expression.cpp b/src/storage/expressions/Expression.cpp index e30b53927..169578c34 100644 --- a/src/storage/expressions/Expression.cpp +++ b/src/storage/expressions/Expression.cpp @@ -95,6 +95,16 @@ namespace storm { return this->getBaseExpression().isFalse(); } + bool Expression::isRelationalExpression() const { + if (!this->isFunctionApplication()) { + return false; + } + + return this->getOperator() == OperatorType::Equal || this->getOperator() == OperatorType::NotEqual + || this->getOperator() == OperatorType::Less || this->getOperator() == OperatorType::LessOrEqual + || this->getOperator() == OperatorType::Greater || this->getOperator() == OperatorType::GreaterOrEqual; + } + std::set<std::string> Expression::getVariables() const { return this->getBaseExpression().getVariables(); } diff --git a/src/storage/expressions/Expression.h b/src/storage/expressions/Expression.h index 0c55408dc..81ae5cf54 100644 --- a/src/storage/expressions/Expression.h +++ b/src/storage/expressions/Expression.h @@ -230,6 +230,14 @@ namespace storm { */ bool isFalse() const; + /*! + * Retrieves whether this expression is a relation expression, i.e., an expression that has a relation + * (equal, not equal, less, less or equal, etc.) as its top-level operator. + * + * @return True iff the expression is a relation expression. + */ + bool isRelationalExpression() const; + /*! * Retrieves the set of all variables that appear in the expression. * diff --git a/src/storage/expressions/LinearityCheckVisitor.cpp b/src/storage/expressions/LinearityCheckVisitor.cpp new file mode 100644 index 000000000..d4086abf1 --- /dev/null +++ b/src/storage/expressions/LinearityCheckVisitor.cpp @@ -0,0 +1,78 @@ +#include "src/storage/expressions/LinearityCheckVisitor.h" +#include "src/storage/expressions/Expressions.h" + +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidTypeException.h" + +namespace storm { + namespace expressions { + bool LinearityCheckVisitor::check(BaseExpression const* expression) { + expression->accept(this); + return resultStack.top(); + } + + void LinearityCheckVisitor::visit(IfThenElseExpression const* expression) { + // An if-then-else expression is never linear. + resultStack.push(false); + } + + void LinearityCheckVisitor::visit(BinaryBooleanFunctionExpression const* expression) { + // Boolean function applications are not allowed in linear expressions. + resultStack.push(false); + } + + void LinearityCheckVisitor::visit(BinaryNumericalFunctionExpression const* expression) { + bool leftResult = true; + bool rightResult = true; + switch (expression->getOperatorType()) { + case BinaryNumericalFunctionExpression::OperatorType::Plus: + case BinaryNumericalFunctionExpression::OperatorType::Minus: + expression->getFirstOperand()->accept(this); + leftResult = resultStack.top(); + + if (!leftResult) { + + } else { + resultStack.pop(); + + expression->getSecondOperand()->accept(this); + } + + case BinaryNumericalFunctionExpression::OperatorType::Times: + case BinaryNumericalFunctionExpression::OperatorType::Divide: + case BinaryNumericalFunctionExpression::OperatorType::Min: resultStack.push(false); break; + case BinaryNumericalFunctionExpression::OperatorType::Max: resultStack.push(false); break; + } + } + + void LinearityCheckVisitor::visit(BinaryRelationExpression const* expression) { + resultStack.push(false); + } + + void LinearityCheckVisitor::visit(VariableExpression const* expression) { + resultStack.push(true); + } + + void LinearityCheckVisitor::visit(UnaryBooleanFunctionExpression const* expression) { + // Boolean function applications are not allowed in linear expressions. + resultStack.push(false); + } + + void LinearityCheckVisitor::visit(UnaryNumericalFunctionExpression const* expression) { + // Intentionally left empty (just pass subresult one level further). + } + + void LinearityCheckVisitor::visit(BooleanLiteralExpression const* expression) { + // Boolean function applications are not allowed in linear expressions. + resultStack.push(false); + } + + void LinearityCheckVisitor::visit(IntegerLiteralExpression const* expression) { + resultStack.push(true); + } + + void LinearityCheckVisitor::visit(DoubleLiteralExpression const* expression) { + resultStack.push(true); + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/LinearityCheckVisitor.h b/src/storage/expressions/LinearityCheckVisitor.h new file mode 100644 index 000000000..2b1c3937c --- /dev/null +++ b/src/storage/expressions/LinearityCheckVisitor.h @@ -0,0 +1,41 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_LINEARITYCHECKVISITOR_H_ +#define STORM_STORAGE_EXPRESSIONS_LINEARITYCHECKVISITOR_H_ + +#include <stack> + +#include "src/storage/expressions/Expression.h" +#include "src/storage/expressions/ExpressionVisitor.h" + +namespace storm { + namespace expressions { + class LinearityCheckVisitor : public ExpressionVisitor { + public: + /*! + * Creates a linearity check visitor. + */ + LinearityCheckVisitor() = default; + + /*! + * Checks that the given expression is linear. + */ + bool check(BaseExpression const* expression); + + virtual void visit(IfThenElseExpression const* expression) override; + virtual void visit(BinaryBooleanFunctionExpression const* expression) override; + virtual void visit(BinaryNumericalFunctionExpression const* expression) override; + virtual void visit(BinaryRelationExpression const* expression) override; + virtual void visit(VariableExpression const* expression) override; + virtual void visit(UnaryBooleanFunctionExpression const* expression) override; + virtual void visit(UnaryNumericalFunctionExpression const* expression) override; + virtual void visit(BooleanLiteralExpression const* expression) override; + virtual void visit(IntegerLiteralExpression const* expression) override; + virtual void visit(DoubleLiteralExpression const* expression) override; + + private: + // A stack for communicating the results of the subexpressions. + std::stack<bool> resultStack; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_LINEARITYCHECKVISITOR_H_ */ \ No newline at end of file From 024b98978f8e2c704a416b7cd0c66fe9426a1f7c Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Fri, 9 May 2014 19:01:36 +0200 Subject: [PATCH 06/15] Made internal changes to SimpleValuations to (hopefully) make it nice and fast. Former-commit-id: 1e9f18f522a78f158cedf06b5532da5cdbac0c3f --- src/storage/dd/CuddDdForwardIterator.cpp | 5 ++ src/storage/dd/CuddDdForwardIterator.h | 2 +- src/storage/expressions/SimpleValuation.cpp | 98 ++++++--------------- src/storage/expressions/SimpleValuation.h | 28 +----- 4 files changed, 38 insertions(+), 95 deletions(-) diff --git a/src/storage/dd/CuddDdForwardIterator.cpp b/src/storage/dd/CuddDdForwardIterator.cpp index c0e754c8b..017845b47 100644 --- a/src/storage/dd/CuddDdForwardIterator.cpp +++ b/src/storage/dd/CuddDdForwardIterator.cpp @@ -5,6 +5,10 @@ namespace storm { namespace dd { + DdForwardIterator<DdType::CUDD>::DdForwardIterator() : ddManager(), generator(), cube(), value(), isAtEnd(), metaVariables(), cubeCounter(), relevantDontCareDdVariables(), currentValuation() { + // Intentionally left empty. + } + DdForwardIterator<DdType::CUDD>::DdForwardIterator(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, DdGen* generator, int* cube, double value, bool isAtEnd, std::set<std::string> const* metaVariables) : ddManager(ddManager), generator(generator), cube(cube), value(value), isAtEnd(isAtEnd), metaVariables(metaVariables), cubeCounter(), relevantDontCareDdVariables(), currentValuation() { // If the given generator is not yet at its end, we need to create the current valuation from the cube from // scratch. @@ -49,6 +53,7 @@ namespace storm { } DdForwardIterator<DdType::CUDD>::~DdForwardIterator() { + // We free the pointers sind Cudd allocates them using malloc rather than new/delete. if (this->cube != nullptr) { free(this->cube); } diff --git a/src/storage/dd/CuddDdForwardIterator.h b/src/storage/dd/CuddDdForwardIterator.h index 054438d2e..0515475a4 100644 --- a/src/storage/dd/CuddDdForwardIterator.h +++ b/src/storage/dd/CuddDdForwardIterator.h @@ -25,7 +25,7 @@ namespace storm { friend class Dd<DdType::CUDD>; // Default-instantiate the constructor. - DdForwardIterator() = default; + DdForwardIterator(); // Forbid copy-construction and copy assignment, because ownership of the internal pointer is unclear then. DdForwardIterator(DdForwardIterator<DdType::CUDD> const& other) = delete; diff --git a/src/storage/expressions/SimpleValuation.cpp b/src/storage/expressions/SimpleValuation.cpp index d29e6a7c8..f930f7e10 100644 --- a/src/storage/expressions/SimpleValuation.cpp +++ b/src/storage/expressions/SimpleValuation.cpp @@ -2,102 +2,74 @@ #include "src/storage/expressions/SimpleValuation.h" #include "src/exceptions/ExceptionMacros.h" #include "src/exceptions/InvalidArgumentException.h" +#include "src/exceptions/InvalidAccessException.h" namespace storm { namespace expressions { - SimpleValuation::SimpleValuation() : booleanIdentifierToIndexMap(new std::unordered_map<std::string, uint_fast64_t>()), integerIdentifierToIndexMap(new std::unordered_map<std::string, uint_fast64_t>()), doubleIdentifierToIndexMap(new std::unordered_map<std::string, uint_fast64_t>()), booleanValues(), integerValues(), doubleValues() { - // Intentionally left empty. - } - bool SimpleValuation::operator==(SimpleValuation const& other) const { - return this->booleanIdentifierToIndexMap.get() == other.booleanIdentifierToIndexMap.get() && this->integerIdentifierToIndexMap.get() == other.integerIdentifierToIndexMap.get() && this->doubleIdentifierToIndexMap.get() == other.doubleIdentifierToIndexMap.get() && this->booleanValues == other.booleanValues && this->integerValues == other.integerValues && this->doubleValues == other.doubleValues; + return this->identifierToValueMap == other.identifierToValueMap; } void SimpleValuation::addBooleanIdentifier(std::string const& name, bool initialValue) { - LOG_THROW(this->booleanIdentifierToIndexMap->find(name) == this->booleanIdentifierToIndexMap->end(), storm::exceptions::InvalidArgumentException, "Boolean identifier '" << name << "' already registered."); - - this->booleanIdentifierToIndexMap->emplace(name, this->booleanValues.size()); - this->booleanValues.push_back(initialValue); + LOG_THROW(this->identifierToValueMap.find(name) == this->identifierToValueMap.end(), storm::exceptions::InvalidArgumentException, "Identifier '" << name << "' already registered."); + this->identifierToValueMap.emplace(name, initialValue); } void SimpleValuation::addIntegerIdentifier(std::string const& name, int_fast64_t initialValue) { - LOG_THROW(this->booleanIdentifierToIndexMap->find(name) == this->booleanIdentifierToIndexMap->end(), storm::exceptions::InvalidArgumentException, "Integer identifier '" << name << "' already registered."); - - this->integerIdentifierToIndexMap->emplace(name, this->integerValues.size()); - this->integerValues.push_back(initialValue); + LOG_THROW(this->identifierToValueMap.find(name) == this->identifierToValueMap.end(), storm::exceptions::InvalidArgumentException, "Identifier '" << name << "' already registered."); + this->identifierToValueMap.emplace(name, initialValue); } void SimpleValuation::addDoubleIdentifier(std::string const& name, double initialValue) { - LOG_THROW(this->booleanIdentifierToIndexMap->find(name) == this->booleanIdentifierToIndexMap->end(), storm::exceptions::InvalidArgumentException, "Double identifier '" << name << "' already registered."); - - this->doubleIdentifierToIndexMap->emplace(name, this->doubleValues.size()); - this->doubleValues.push_back(initialValue); + LOG_THROW(this->identifierToValueMap.find(name) == this->identifierToValueMap.end(), storm::exceptions::InvalidArgumentException, "Identifier '" << name << "' already registered."); + this->identifierToValueMap.emplace(name, initialValue); } void SimpleValuation::setBooleanValue(std::string const& name, bool value) { - this->booleanValues[this->booleanIdentifierToIndexMap->at(name)] = value; + this->identifierToValueMap[name] = value; } void SimpleValuation::setIntegerValue(std::string const& name, int_fast64_t value) { - this->integerValues[this->integerIdentifierToIndexMap->at(name)] = value; + this->identifierToValueMap[name] = value; } void SimpleValuation::setDoubleValue(std::string const& name, double value) { - this->doubleValues[this->doubleIdentifierToIndexMap->at(name)] = value; + this->identifierToValueMap[name] = value; } bool SimpleValuation::getBooleanValue(std::string const& name) const { - auto const& nameIndexPair = this->booleanIdentifierToIndexMap->find(name); - return this->booleanValues[nameIndexPair->second]; + auto nameValuePair = this->identifierToValueMap.find(name); + LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'."); + return boost::get<bool>(nameValuePair->second); } int_fast64_t SimpleValuation::getIntegerValue(std::string const& name) const { - auto const& nameIndexPair = this->integerIdentifierToIndexMap->find(name); - return this->integerValues[nameIndexPair->second]; + auto nameValuePair = this->identifierToValueMap.find(name); + LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'."); + return boost::get<int_fast64_t>(nameValuePair->second); } double SimpleValuation::getDoubleValue(std::string const& name) const { - auto const& nameIndexPair = this->doubleIdentifierToIndexMap->find(name); - return this->doubleValues[nameIndexPair->second]; + auto nameValuePair = this->identifierToValueMap.find(name); + LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'."); + return boost::get<double>(nameValuePair->second); } std::ostream& operator<<(std::ostream& stream, SimpleValuation const& valuation) { - stream << "valuation { bool ["; - if (!valuation.booleanValues.empty()) { - for (uint_fast64_t i = 0; i < valuation.booleanValues.size() - 1; ++i) { - stream << valuation.booleanValues[i] << ", "; - } - stream << valuation.booleanValues.back(); - } - stream << "] int ["; - if (!valuation.integerValues.empty()) { - for (uint_fast64_t i = 0; i < valuation.integerValues.size() - 1; ++i) { - stream << valuation.integerValues[i] << ", "; - } - stream << valuation.integerValues.back(); + stream << "valuation { "; + for (auto const& nameValuePair : valuation.identifierToValueMap) { + stream << nameValuePair.first << ": " << nameValuePair.second << std::endl; } - stream << "] double ["; - if (!valuation.doubleValues.empty()) { - for (uint_fast64_t i = 0; i < valuation.doubleValues.size() - 1; ++i) { - stream << valuation.doubleValues[i] << ", "; - } - stream << valuation.doubleValues.back(); - } - stream << "] }"; + stream << "}"; return stream; } std::size_t SimpleValuationPointerHash::operator()(SimpleValuation* valuation) const { size_t seed = 0; - for (auto const& value : valuation->booleanValues) { - boost::hash_combine<bool>(seed, value); - } - for (auto const& value : valuation->integerValues) { - boost::hash_combine<int_fast64_t>(seed, value); - } - for (auto const& value : valuation->doubleValues) { - boost::hash_combine<double>(seed, value); + for (auto const& nameValuePair : valuation->identifierToValueMap) { + boost::hash_combine(seed, nameValuePair.first); + boost::hash_combine(seed, nameValuePair.second); } return seed; } @@ -107,21 +79,7 @@ namespace storm { } bool SimpleValuationPointerLess::operator()(SimpleValuation* valuation1, SimpleValuation* valuation2) const { - // Compare boolean variables. - bool less = valuation1->booleanValues < valuation2->booleanValues; - if (less) { - return true; - } - less = valuation1->integerValues < valuation2->integerValues; - if (less) { - return true; - } - less = valuation1->doubleValues < valuation2->doubleValues; - if (less) { - return true; - } else { - return false; - } + return valuation1->identifierToValueMap < valuation2->identifierToValueMap; } } } \ No newline at end of file diff --git a/src/storage/expressions/SimpleValuation.h b/src/storage/expressions/SimpleValuation.h index a196921a6..55caab46b 100644 --- a/src/storage/expressions/SimpleValuation.h +++ b/src/storage/expressions/SimpleValuation.h @@ -1,9 +1,8 @@ #ifndef STORM_STORAGE_EXPRESSIONS_SIMPLEVALUATION_H_ #define STORM_STORAGE_EXPRESSIONS_SIMPLEVALUATION_H_ -#include <memory> -#include <vector> -#include <unordered_map> +#include <boost/container/flat_map.hpp> +#include <boost/variant.hpp> #include <iostream> #include "src/storage/expressions/Valuation.h" @@ -16,12 +15,8 @@ namespace storm { friend class SimpleValuationPointerHash; friend class SimpleValuationPointerLess; - /*! - * Creates a simple valuation without any identifiers. - */ - SimpleValuation(); - // Instantiate some constructors and assignments with their default implementations. + SimpleValuation() = default; SimpleValuation(SimpleValuation const&) = default; SimpleValuation& operator=(SimpleValuation const&) = default; #ifndef WINDOWS @@ -92,22 +87,7 @@ namespace storm { private: // A mapping of boolean identifiers to their local indices in the value container. - std::shared_ptr<std::unordered_map<std::string, uint_fast64_t>> booleanIdentifierToIndexMap; - - // A mapping of integer identifiers to their local indices in the value container. - std::shared_ptr<std::unordered_map<std::string, uint_fast64_t>> integerIdentifierToIndexMap; - - // A mapping of double identifiers to their local indices in the value container. - std::shared_ptr<std::unordered_map<std::string, uint_fast64_t>> doubleIdentifierToIndexMap; - - // The value container for all boolean identifiers. - std::vector<bool> booleanValues; - - // The value container for all integer identifiers. - std::vector<int_fast64_t> integerValues; - - // The value container for all double identifiers. - std::vector<double> doubleValues; + boost::container::flat_map<std::string, boost::variant<bool, int_fast64_t, double>> identifierToValueMap; }; /*! From f60ea09cf460e4fb03341513ff186c6f302a9a95 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Fri, 9 May 2014 19:37:27 +0200 Subject: [PATCH 07/15] Valuations now have methods to check whether they contain a given identifier. Former-commit-id: 541c27d543866ac4c3f419a9514945a018022673 --- src/storage/expressions/SimpleValuation.cpp | 30 +++++++++++++++++++++ src/storage/expressions/SimpleValuation.h | 10 +++++++ src/storage/expressions/Valuation.h | 25 +++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/src/storage/expressions/SimpleValuation.cpp b/src/storage/expressions/SimpleValuation.cpp index f930f7e10..e8fb27611 100644 --- a/src/storage/expressions/SimpleValuation.cpp +++ b/src/storage/expressions/SimpleValuation.cpp @@ -37,6 +37,36 @@ namespace storm { this->identifierToValueMap[name] = value; } + void SimpleValuation::removeIdentifier(std::string const& name) { + auto nameValuePair = this->identifierToValueMap.find(name); + LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidArgumentException, "Deleting unknown identifier '" << name << "'."); + this->identifierToValueMap.erase(nameValuePair); + } + + bool SimpleValuation::containsBooleanIdentifier(std::string const& name) const { + auto nameValuePair = this->identifierToValueMap.find(name); + if (nameValuePair == this->identifierToValueMap.end()) { + return false; + } + return nameValuePair->second.type() == typeid(bool); + } + + bool SimpleValuation::containsIntegerIdentifier(std::string const& name) const { + auto nameValuePair = this->identifierToValueMap.find(name); + if (nameValuePair == this->identifierToValueMap.end()) { + return false; + } + return nameValuePair->second.type() == typeid(int_fast64_t); + } + + bool SimpleValuation::containsDoubleIdentifier(std::string const& name) const { + auto nameValuePair = this->identifierToValueMap.find(name); + if (nameValuePair == this->identifierToValueMap.end()) { + return false; + } + return nameValuePair->second.type() == typeid(double); + } + bool SimpleValuation::getBooleanValue(std::string const& name) const { auto nameValuePair = this->identifierToValueMap.find(name); LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'."); diff --git a/src/storage/expressions/SimpleValuation.h b/src/storage/expressions/SimpleValuation.h index 55caab46b..0ea05a511 100644 --- a/src/storage/expressions/SimpleValuation.h +++ b/src/storage/expressions/SimpleValuation.h @@ -78,7 +78,17 @@ namespace storm { */ void setDoubleValue(std::string const& name, double value); + /*! + * Removes the given identifier from this valuation. + * + * @param name The name of the identifier that is to be removed. + */ + void removeIdentifier(std::string const& name); + // Override base class methods. + virtual bool containsBooleanIdentifier(std::string const& name) const override; + virtual bool containsIntegerIdentifier(std::string const& name) const override; + virtual bool containsDoubleIdentifier(std::string const& name) const override; virtual bool getBooleanValue(std::string const& name) const override; virtual int_fast64_t getIntegerValue(std::string const& name) const override; virtual double getDoubleValue(std::string const& name) const override; diff --git a/src/storage/expressions/Valuation.h b/src/storage/expressions/Valuation.h index 7aa32a859..19792b720 100644 --- a/src/storage/expressions/Valuation.h +++ b/src/storage/expressions/Valuation.h @@ -34,6 +34,31 @@ namespace storm { * @return The value of the double identifier. */ virtual double getDoubleValue(std::string const& name) const = 0; + + /*! + * Retrieves whether there exists a boolean identifier with the given name in the valuation. + * + * @param name The name of the boolean identifier to query. + * @return True iff the identifier exists and is of boolean type. + */ + virtual bool containsBooleanIdentifier(std::string const& name) const = 0; + + /*! + * Retrieves whether there exists a integer identifier with the given name in the valuation. + * + * @param name The name of the integer identifier to query. + * @return True iff the identifier exists and is of boolean type. + */ + virtual bool containsIntegerIdentifier(std::string const& name) const = 0; + + /*! + * Retrieves whether there exists a double identifier with the given name in the valuation. + * + * @param name The name of the double identifier to query. + * @return True iff the identifier exists and is of boolean type. + */ + virtual bool containsDoubleIdentifier(std::string const& name) const = 0; + }; } } From 57a8381f914b2a8103875736c596b6c08da28bc5 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Sat, 10 May 2014 15:18:19 +0200 Subject: [PATCH 08/15] If requested, the DD iterator can now skip meta variables which are 'don't cares' for the function value. Former-commit-id: 061cb5f0fa0c0f3286906e9b5f01779f176674d1 --- src/storage/dd/CuddDd.cpp | 8 ++-- src/storage/dd/CuddDd.h | 8 +++- src/storage/dd/CuddDdForwardIterator.cpp | 45 +++++++++++++++++---- src/storage/dd/CuddDdForwardIterator.h | 8 +++- src/storage/expressions/SimpleValuation.cpp | 9 ++++- test/functional/storage/CuddDdTest.cpp | 22 ++++++++++ 6 files changed, 84 insertions(+), 16 deletions(-) diff --git a/src/storage/dd/CuddDd.cpp b/src/storage/dd/CuddDd.cpp index 72abb87bf..cdff92894 100644 --- a/src/storage/dd/CuddDd.cpp +++ b/src/storage/dd/CuddDd.cpp @@ -473,15 +473,15 @@ namespace storm { return this->ddManager; } - DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::begin() const { + DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::begin(bool enumerateDontCareMetaVariables) const { int* cube; double value; DdGen* generator = this->getCuddAdd().FirstCube(&cube, &value); - return DdForwardIterator<DdType::CUDD>(this->getDdManager(), generator, cube, value, Cudd_IsGenEmpty(generator), &this->getContainedMetaVariableNames()); + return DdForwardIterator<DdType::CUDD>(this->getDdManager(), generator, cube, value, Cudd_IsGenEmpty(generator), &this->getContainedMetaVariableNames(), enumerateDontCareMetaVariables); } - DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::end() const { - return DdForwardIterator<DdType::CUDD>(this->getDdManager(), nullptr, nullptr, 0, true, nullptr); + DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::end(bool enumerateDontCareMetaVariables) const { + return DdForwardIterator<DdType::CUDD>(this->getDdManager(), nullptr, nullptr, 0, true, nullptr, enumerateDontCareMetaVariables); } std::ostream & operator<<(std::ostream& out, const Dd<DdType::CUDD>& dd) { diff --git a/src/storage/dd/CuddDd.h b/src/storage/dd/CuddDd.h index f345c28a2..9f638d329 100644 --- a/src/storage/dd/CuddDd.h +++ b/src/storage/dd/CuddDd.h @@ -440,16 +440,20 @@ namespace storm { /*! * Retrieves an iterator that points to the first meta variable assignment with a non-zero function value. * + * @param enumerateDontCareMetaVariables If set to true, all meta variable assignments are enumerated, even + * if a meta variable does not at all influence the the function value. * @return An iterator that points to the first meta variable assignment with a non-zero function value. */ - DdForwardIterator<DdType::CUDD> begin() const; + DdForwardIterator<DdType::CUDD> begin(bool enumerateDontCareMetaVariables = true) const; /*! * Retrieves an iterator that points past the end of the container. * + * @param enumerateDontCareMetaVariables If set to true, all meta variable assignments are enumerated, even + * if a meta variable does not at all influence the the function value. * @return An iterator that points past the end of the container. */ - DdForwardIterator<DdType::CUDD> end() const; + DdForwardIterator<DdType::CUDD> end(bool enumerateDontCareMetaVariables = true) const; friend std::ostream & operator<<(std::ostream& out, const Dd<DdType::CUDD>& dd); private: diff --git a/src/storage/dd/CuddDdForwardIterator.cpp b/src/storage/dd/CuddDdForwardIterator.cpp index 017845b47..cce5c112c 100644 --- a/src/storage/dd/CuddDdForwardIterator.cpp +++ b/src/storage/dd/CuddDdForwardIterator.cpp @@ -5,11 +5,11 @@ namespace storm { namespace dd { - DdForwardIterator<DdType::CUDD>::DdForwardIterator() : ddManager(), generator(), cube(), value(), isAtEnd(), metaVariables(), cubeCounter(), relevantDontCareDdVariables(), currentValuation() { + DdForwardIterator<DdType::CUDD>::DdForwardIterator() : ddManager(), generator(), cube(), value(), isAtEnd(), metaVariables(), enumerateDontCareMetaVariables(), cubeCounter(), relevantDontCareDdVariables(), currentValuation() { // Intentionally left empty. } - DdForwardIterator<DdType::CUDD>::DdForwardIterator(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, DdGen* generator, int* cube, double value, bool isAtEnd, std::set<std::string> const* metaVariables) : ddManager(ddManager), generator(generator), cube(cube), value(value), isAtEnd(isAtEnd), metaVariables(metaVariables), cubeCounter(), relevantDontCareDdVariables(), currentValuation() { + DdForwardIterator<DdType::CUDD>::DdForwardIterator(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, DdGen* generator, int* cube, double value, bool isAtEnd, std::set<std::string> const* metaVariables, bool enumerateDontCareMetaVariables) : ddManager(ddManager), generator(generator), cube(cube), value(value), isAtEnd(isAtEnd), metaVariables(metaVariables), enumerateDontCareMetaVariables(enumerateDontCareMetaVariables), cubeCounter(), relevantDontCareDdVariables(), currentValuation() { // If the given generator is not yet at its end, we need to create the current valuation from the cube from // scratch. if (!this->isAtEnd) { @@ -113,29 +113,60 @@ namespace storm { // don't cares. In the latter case, we add them to a special list, so we can iterate over their concrete // valuations later. for (auto const& metaVariableName : *this->metaVariables) { + bool metaVariableAppearsInCube = false; + std::vector<std::tuple<ADD, std::string, uint_fast64_t>> localRelenvantDontCareDdVariables; auto const& metaVariable = this->ddManager->getMetaVariable(metaVariableName); if (metaVariable.getType() == DdMetaVariable<DdType::CUDD>::MetaVariableType::Bool) { if (this->cube[metaVariable.getDdVariables()[0].getCuddAdd().NodeReadIndex()] == 0) { - currentValuation.setBooleanValue(metaVariableName, false); + metaVariableAppearsInCube = true; + if (!currentValuation.containsBooleanIdentifier(metaVariableName)) { + currentValuation.addBooleanIdentifier(metaVariableName, false); + } else { + currentValuation.setBooleanValue(metaVariableName, false); + } } else if (this->cube[metaVariable.getDdVariables()[0].getCuddAdd().NodeReadIndex()] == 1) { - currentValuation.setBooleanValue(metaVariableName, true); + metaVariableAppearsInCube = true; + if (!currentValuation.containsBooleanIdentifier(metaVariableName)) { + currentValuation.addBooleanIdentifier(metaVariableName, true); + } else { + currentValuation.setBooleanValue(metaVariableName, true); + } } else { - relevantDontCareDdVariables.push_back(std::make_tuple(metaVariable.getDdVariables()[0].getCuddAdd(), metaVariableName, 0)); + localRelenvantDontCareDdVariables.push_back(std::make_tuple(metaVariable.getDdVariables()[0].getCuddAdd(), metaVariableName, 0)); } } else { int_fast64_t intValue = 0; for (uint_fast64_t bitIndex = 0; bitIndex < metaVariable.getNumberOfDdVariables(); ++bitIndex) { if (cube[metaVariable.getDdVariables()[bitIndex].getCuddAdd().NodeReadIndex()] == 0) { // Leave bit unset. + metaVariableAppearsInCube = true; } else if (cube[metaVariable.getDdVariables()[bitIndex].getCuddAdd().NodeReadIndex()] == 1) { intValue |= 1ull << (metaVariable.getNumberOfDdVariables() - bitIndex - 1); + metaVariableAppearsInCube = true; } else { // Temporarily leave bit unset so we can iterate trough the other option later. // Add the bit to the relevant don't care bits. - this->relevantDontCareDdVariables.push_back(std::make_tuple(metaVariable.getDdVariables()[bitIndex].getCuddAdd(), metaVariableName, metaVariable.getNumberOfDdVariables() - bitIndex - 1)); + localRelenvantDontCareDdVariables.push_back(std::make_tuple(metaVariable.getDdVariables()[bitIndex].getCuddAdd(), metaVariableName, metaVariable.getNumberOfDdVariables() - bitIndex - 1)); + } + } + if (this->enumerateDontCareMetaVariables || metaVariableAppearsInCube) { + if (!currentValuation.containsIntegerIdentifier(metaVariableName)) { + currentValuation.addIntegerIdentifier(metaVariableName); } + currentValuation.setIntegerValue(metaVariableName, intValue + metaVariable.getLow()); } - currentValuation.setIntegerValue(metaVariableName, intValue + metaVariable.getLow()); + } + + // If all meta variables are to be enumerated or the meta variable appeared in the cube, we register the + // missing bits to later enumerate all possible valuations. + if (this->enumerateDontCareMetaVariables || metaVariableAppearsInCube) { + relevantDontCareDdVariables.insert(relevantDontCareDdVariables.end(), localRelenvantDontCareDdVariables.begin(), localRelenvantDontCareDdVariables.end()); + } + + // If the meta variable does not appear in the cube and we're not supposed to enumerate such meta variables + // we remove the meta variable from the valuation. + if (!this->enumerateDontCareMetaVariables && !metaVariableAppearsInCube) { + currentValuation.removeIdentifier(metaVariableName); } } diff --git a/src/storage/dd/CuddDdForwardIterator.h b/src/storage/dd/CuddDdForwardIterator.h index 0515475a4..8b08af929 100644 --- a/src/storage/dd/CuddDdForwardIterator.h +++ b/src/storage/dd/CuddDdForwardIterator.h @@ -82,8 +82,10 @@ namespace storm { * @param isAtEnd A flag that indicates whether the iterator is at its end and may not be moved forward any * more. * @param metaVariables The meta variables that appear in the DD. + * @param enumerateDontCareMetaVariables If set to true, all meta variable assignments are enumerated, even + * if a meta variable does not at all influence the the function value. */ - DdForwardIterator(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, DdGen* generator, int* cube, double value, bool isAtEnd, std::set<std::string> const* metaVariables = nullptr); + DdForwardIterator(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, DdGen* generator, int* cube, double value, bool isAtEnd, std::set<std::string> const* metaVariables = nullptr, bool enumerateDontCareMetaVariables = true); /*! * Recreates the internal information when a new cube needs to be treated. @@ -114,6 +116,10 @@ namespace storm { // The set of meta variables appearing in the DD. std::set<std::string> const* metaVariables; + // A flag that indicates whether the iterator is supposed to enumerate meta variable valuations even if + // they don't influence the function value. + bool enumerateDontCareMetaVariables; + // A number that represents how many assignments of the current cube have already been returned previously. // This is needed, because cubes may represent many assignments (if they have don't care variables). uint_fast64_t cubeCounter; diff --git a/src/storage/expressions/SimpleValuation.cpp b/src/storage/expressions/SimpleValuation.cpp index e8fb27611..4a5441e99 100644 --- a/src/storage/expressions/SimpleValuation.cpp +++ b/src/storage/expressions/SimpleValuation.cpp @@ -86,9 +86,14 @@ namespace storm { } std::ostream& operator<<(std::ostream& stream, SimpleValuation const& valuation) { - stream << "valuation { "; + stream << "{ "; + uint_fast64_t elementIndex = 0; for (auto const& nameValuePair : valuation.identifierToValueMap) { - stream << nameValuePair.first << ": " << nameValuePair.second << std::endl; + stream << nameValuePair.first << " -> " << nameValuePair.second << " "; + ++elementIndex; + if (elementIndex < valuation.identifierToValueMap.size()) { + stream << ", "; + } } stream << "}"; diff --git a/test/functional/storage/CuddDdTest.cpp b/test/functional/storage/CuddDdTest.cpp index 91d14f202..ce40c94d1 100644 --- a/test/functional/storage/CuddDdTest.cpp +++ b/test/functional/storage/CuddDdTest.cpp @@ -307,4 +307,26 @@ TEST(CuddDd, ForwardIteratorTest) { ++numberOfValuations; } EXPECT_EQ(9, numberOfValuations); + + dd = manager->getRange("x"); + dd = dd.ite(manager->getOne(), manager->getOne()); + ASSERT_NO_THROW(it = dd.begin()); + ASSERT_NO_THROW(ite = dd.end()); + numberOfValuations = 0; + while (it != ite) { + ASSERT_NO_THROW(valuationValuePair = *it); + ASSERT_NO_THROW(++it); + ++numberOfValuations; + } + EXPECT_EQ(16, numberOfValuations); + + ASSERT_NO_THROW(it = dd.begin(false)); + ASSERT_NO_THROW(ite = dd.end()); + numberOfValuations = 0; + while (it != ite) { + ASSERT_NO_THROW(valuationValuePair = *it); + ASSERT_NO_THROW(++it); + ++numberOfValuations; + } + EXPECT_EQ(1, numberOfValuations); } From 389fddc99671eee94261dd0f24c0da058f96d467 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Sat, 10 May 2014 21:31:04 +0200 Subject: [PATCH 09/15] Added some more methods to valuations. Changed visitor invocation slightly. Moves ExpressionReturnType in separate file. Finished linearity checking visitor. Started on visitor that extracts coefficients of linear expressions. Former-commit-id: 6e3d0ec9109144f2e517aa0b6034f3d18f554175 --- src/storage/expressions/BaseExpression.cpp | 10 -- src/storage/expressions/BaseExpression.h | 10 +- src/storage/expressions/Expression.cpp | 17 ++-- src/storage/expressions/Expression.h | 15 +++ .../expressions/ExpressionReturnType.cpp | 15 +++ .../expressions/ExpressionReturnType.h | 17 ++++ .../IdentifierSubstitutionVisitor.cpp | 4 +- .../IdentifierSubstitutionVisitor.h | 2 +- .../expressions/LinearCoefficientVisitor.cpp | 70 ++++++++++++++ .../expressions/LinearCoefficientVisitor.h | 46 +++++++++ .../expressions/LinearityCheckVisitor.cpp | 93 ++++++++++++++----- .../expressions/LinearityCheckVisitor.h | 10 +- src/storage/expressions/SimpleValuation.cpp | 59 +++++++++++- src/storage/expressions/SimpleValuation.h | 14 +++ .../expressions/SubstitutionVisitor.cpp | 4 +- src/storage/expressions/SubstitutionVisitor.h | 2 +- src/storage/expressions/TypeCheckVisitor.cpp | 4 +- src/storage/expressions/TypeCheckVisitor.h | 2 +- src/storage/expressions/Valuation.h | 36 +++++++ test/functional/storage/ExpressionTest.cpp | 17 ++++ 20 files changed, 389 insertions(+), 58 deletions(-) create mode 100644 src/storage/expressions/ExpressionReturnType.cpp create mode 100644 src/storage/expressions/ExpressionReturnType.h create mode 100644 src/storage/expressions/LinearCoefficientVisitor.cpp create mode 100644 src/storage/expressions/LinearCoefficientVisitor.h diff --git a/src/storage/expressions/BaseExpression.cpp b/src/storage/expressions/BaseExpression.cpp index c2f4a5ac3..38a0604e2 100644 --- a/src/storage/expressions/BaseExpression.cpp +++ b/src/storage/expressions/BaseExpression.cpp @@ -81,16 +81,6 @@ namespace storm { return this->shared_from_this(); } - std::ostream& operator<<(std::ostream& stream, ExpressionReturnType const& enumValue) { - switch (enumValue) { - case ExpressionReturnType::Undefined: stream << "undefined"; break; - case ExpressionReturnType::Bool: stream << "bool"; break; - case ExpressionReturnType::Int: stream << "int"; break; - case ExpressionReturnType::Double: stream << "double"; break; - } - return stream; - } - std::ostream& operator<<(std::ostream& stream, BaseExpression const& expression) { expression.printToStream(stream); return stream; diff --git a/src/storage/expressions/BaseExpression.h b/src/storage/expressions/BaseExpression.h index 6a02b35ab..9524fcdbb 100644 --- a/src/storage/expressions/BaseExpression.h +++ b/src/storage/expressions/BaseExpression.h @@ -6,6 +6,7 @@ #include <set> #include <iostream> +#include "src/storage/expressions/ExpressionReturnType.h" #include "src/storage/expressions/Valuation.h" #include "src/storage/expressions/ExpressionVisitor.h" #include "src/storage/expressions/OperatorType.h" @@ -13,14 +14,7 @@ #include "src/utility/OsDetection.h" namespace storm { - namespace expressions { - /*! - * Each node in an expression tree has a uniquely defined type from this enum. - */ - enum class ExpressionReturnType {Undefined, Bool, Int, Double}; - - std::ostream& operator<<(std::ostream& stream, ExpressionReturnType const& enumValue); - + namespace expressions { /*! * The base class of all expression classes. */ diff --git a/src/storage/expressions/Expression.cpp b/src/storage/expressions/Expression.cpp index 169578c34..762ed07bf 100644 --- a/src/storage/expressions/Expression.cpp +++ b/src/storage/expressions/Expression.cpp @@ -5,6 +5,7 @@ #include "src/storage/expressions/SubstitutionVisitor.h" #include "src/storage/expressions/IdentifierSubstitutionVisitor.h" #include "src/storage/expressions/TypeCheckVisitor.h" +#include "src/storage/expressions/LinearityCheckVisitor.h" #include "src/storage/expressions/Expressions.h" #include "src/exceptions/InvalidTypeException.h" #include "src/exceptions/ExceptionMacros.h" @@ -16,27 +17,27 @@ namespace storm { } Expression Expression::substitute(std::map<std::string, Expression> const& identifierToExpressionMap) const { - return SubstitutionVisitor<std::map<std::string, Expression>>(identifierToExpressionMap).substitute(this->getBaseExpressionPointer().get()); + return SubstitutionVisitor<std::map<std::string, Expression>>(identifierToExpressionMap).substitute(this); } Expression Expression::substitute(std::unordered_map<std::string, Expression> const& identifierToExpressionMap) const { - return SubstitutionVisitor<std::unordered_map<std::string, Expression>>(identifierToExpressionMap).substitute(this->getBaseExpressionPointer().get()); + return SubstitutionVisitor<std::unordered_map<std::string, Expression>>(identifierToExpressionMap).substitute(this); } Expression Expression::substitute(std::map<std::string, std::string> const& identifierToIdentifierMap) const { - return IdentifierSubstitutionVisitor<std::map<std::string, std::string>>(identifierToIdentifierMap).substitute(this->getBaseExpressionPointer().get()); + return IdentifierSubstitutionVisitor<std::map<std::string, std::string>>(identifierToIdentifierMap).substitute(this); } Expression Expression::substitute(std::unordered_map<std::string, std::string> const& identifierToIdentifierMap) const { - return IdentifierSubstitutionVisitor<std::unordered_map<std::string, std::string>>(identifierToIdentifierMap).substitute(this->getBaseExpressionPointer().get()); + return IdentifierSubstitutionVisitor<std::unordered_map<std::string, std::string>>(identifierToIdentifierMap).substitute(this); } void Expression::check(std::map<std::string, storm::expressions::ExpressionReturnType> const& identifierToTypeMap) const { - return TypeCheckVisitor<std::map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(this->getBaseExpressionPointer().get()); + return TypeCheckVisitor<std::map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(this); } void Expression::check(std::unordered_map<std::string, storm::expressions::ExpressionReturnType> const& identifierToTypeMap) const { - return TypeCheckVisitor<std::unordered_map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(this->getBaseExpressionPointer().get()); + return TypeCheckVisitor<std::unordered_map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(this); } bool Expression::evaluateAsBool(Valuation const* valuation) const { @@ -105,6 +106,10 @@ namespace storm { || this->getOperator() == OperatorType::Greater || this->getOperator() == OperatorType::GreaterOrEqual; } + bool Expression::isLinear() const { + return LinearityCheckVisitor().check(*this); + } + std::set<std::string> Expression::getVariables() const { return this->getBaseExpression().getVariables(); } diff --git a/src/storage/expressions/Expression.h b/src/storage/expressions/Expression.h index 81ae5cf54..08c758f27 100644 --- a/src/storage/expressions/Expression.h +++ b/src/storage/expressions/Expression.h @@ -6,6 +6,7 @@ #include <unordered_map> #include "src/storage/expressions/BaseExpression.h" +#include "src/storage/expressions/ExpressionVisitor.h" #include "src/utility/OsDetection.h" namespace storm { @@ -238,6 +239,13 @@ namespace storm { */ bool isRelationalExpression() const; + /*! + * Retrieves whether this expression is a linear expression. + * + * @return True iff the expression is linear. + */ + bool isLinear() const; + /*! * Retrieves the set of all variables that appear in the expression. * @@ -281,6 +289,13 @@ namespace storm { */ bool hasBooleanReturnType() const; + /*! + * Accepts the given visitor. + * + * @param visitor The visitor to accept. + */ + void accept(ExpressionVisitor* visitor) const; + friend std::ostream& operator<<(std::ostream& stream, Expression const& expression); private: diff --git a/src/storage/expressions/ExpressionReturnType.cpp b/src/storage/expressions/ExpressionReturnType.cpp new file mode 100644 index 000000000..c10810f6c --- /dev/null +++ b/src/storage/expressions/ExpressionReturnType.cpp @@ -0,0 +1,15 @@ +#include "src/storage/expressions/ExpressionReturnType.h" + +namespace storm { + namespace expressions { + std::ostream& operator<<(std::ostream& stream, ExpressionReturnType const& enumValue) { + switch (enumValue) { + case ExpressionReturnType::Undefined: stream << "undefined"; break; + case ExpressionReturnType::Bool: stream << "bool"; break; + case ExpressionReturnType::Int: stream << "int"; break; + case ExpressionReturnType::Double: stream << "double"; break; + } + return stream; + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/ExpressionReturnType.h b/src/storage/expressions/ExpressionReturnType.h new file mode 100644 index 000000000..0cf928ed8 --- /dev/null +++ b/src/storage/expressions/ExpressionReturnType.h @@ -0,0 +1,17 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_EXPRESSIONRETURNTYPE_H_ +#define STORM_STORAGE_EXPRESSIONS_EXPRESSIONRETURNTYPE_H_ + +#include <iostream> + +namespace storm { + namespace expressions { + /*! + * Each node in an expression tree has a uniquely defined type from this enum. + */ + enum class ExpressionReturnType {Undefined, Bool, Int, Double}; + + std::ostream& operator<<(std::ostream& stream, ExpressionReturnType const& enumValue); + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_EXPRESSIONRETURNTYPE_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/IdentifierSubstitutionVisitor.cpp b/src/storage/expressions/IdentifierSubstitutionVisitor.cpp index a2153af6d..19724dea1 100644 --- a/src/storage/expressions/IdentifierSubstitutionVisitor.cpp +++ b/src/storage/expressions/IdentifierSubstitutionVisitor.cpp @@ -13,8 +13,8 @@ namespace storm { } template<typename MapType> - Expression IdentifierSubstitutionVisitor<MapType>::substitute(BaseExpression const* expression) { - expression->accept(this); + Expression IdentifierSubstitutionVisitor<MapType>::substitute(Expression const& expression) { + expression.getBaseExpression().accept(this); return Expression(this->expressionStack.top()); } diff --git a/src/storage/expressions/IdentifierSubstitutionVisitor.h b/src/storage/expressions/IdentifierSubstitutionVisitor.h index 23dae79de..8e8723a70 100644 --- a/src/storage/expressions/IdentifierSubstitutionVisitor.h +++ b/src/storage/expressions/IdentifierSubstitutionVisitor.h @@ -26,7 +26,7 @@ namespace storm { * @return The expression in which all identifiers in the key set of the previously given mapping are * substituted with the mapped-to expressions. */ - Expression substitute(BaseExpression const* expression); + Expression substitute(Expression const& expression); virtual void visit(IfThenElseExpression const* expression) override; virtual void visit(BinaryBooleanFunctionExpression const* expression) override; diff --git a/src/storage/expressions/LinearCoefficientVisitor.cpp b/src/storage/expressions/LinearCoefficientVisitor.cpp new file mode 100644 index 000000000..58fd427d5 --- /dev/null +++ b/src/storage/expressions/LinearCoefficientVisitor.cpp @@ -0,0 +1,70 @@ +#include "src/storage/expressions/LinearCoefficientVisitor.h" + +#include "src/storage/expressions/Expressions.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidArgumentException.h" + +namespace storm { + namespace expressions { + std::pair<SimpleValuation, double> LinearCoefficientVisitor::getLinearCoefficients(Expression const& expression) { + expression.getBaseExpression().accept(this); + return resultStack.top(); + } + + void LinearCoefficientVisitor::visit(IfThenElseExpression const* expression) { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + + void LinearCoefficientVisitor::visit(BinaryBooleanFunctionExpression const* expression) { + + } + + void LinearCoefficientVisitor::visit(BinaryNumericalFunctionExpression const* expression) { + + } + + void LinearCoefficientVisitor::visit(BinaryRelationExpression const* expression) { + + } + + void LinearCoefficientVisitor::visit(VariableExpression const* expression) { + SimpleValuation valuation; + switch (expression->getReturnType()) { + case ExpressionReturnType::Bool: LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); break; + case ExpressionReturnType::Int: + case ExpressionReturnType::Double: valuation.addDoubleIdentifier(expression->getVariableName(), 1); break; + case ExpressionReturnType::Undefined: LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Illegal expression return type."); break; + } + + resultStack.push(std::make_pair(valuation, 0)); + } + + void LinearCoefficientVisitor::visit(UnaryBooleanFunctionExpression const* expression) { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + + void LinearCoefficientVisitor::visit(UnaryNumericalFunctionExpression const* expression) { + if (expression->getOperatorType() == UnaryNumericalFunctionExpression::OperatorType::Minus) { + // Here, we need to negate all double identifiers. + std::pair<SimpleValuation, double>& valuationConstantPair = resultStack.top(); + for (auto const& identifier : valuationConstantPair.first.getDoubleIdentifiers()) { + valuationConstantPair.first.setDoubleValue(identifier, -valuationConstantPair.first.getDoubleValue(identifier)); + } + } else { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + } + + void LinearCoefficientVisitor::visit(BooleanLiteralExpression const* expression) { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } + + void LinearCoefficientVisitor::visit(IntegerLiteralExpression const* expression) { + resultStack.push(std::make_pair(SimpleValuation(), static_cast<double>(expression->getValue()))); + } + + void LinearCoefficientVisitor::visit(DoubleLiteralExpression const* expression) { + resultStack.push(std::make_pair(SimpleValuation(), expression->getValue())); + } + } +} \ No newline at end of file diff --git a/src/storage/expressions/LinearCoefficientVisitor.h b/src/storage/expressions/LinearCoefficientVisitor.h new file mode 100644 index 000000000..263e752c8 --- /dev/null +++ b/src/storage/expressions/LinearCoefficientVisitor.h @@ -0,0 +1,46 @@ +#ifndef STORM_STORAGE_EXPRESSIONS_LINEARCOEFFICIENTVISITOR_H_ +#define STORM_STORAGE_EXPRESSIONS_LINEARCOEFFICIENTVISITOR_H_ + +#include <stack> + +#include "src/storage/expressions/Expression.h" +#include "src/storage/expressions/ExpressionVisitor.h" +#include "src/storage/expressions/SimpleValuation.h" + +namespace storm { + namespace expressions { + class LinearCoefficientVisitor : public ExpressionVisitor { + public: + /*! + * Creates a linear coefficient visitor. + */ + LinearCoefficientVisitor() = default; + + /*! + * Computes the (double) coefficients of all identifiers appearing in the expression if the expression + * was rewritten as a sum of atoms.. If the expression is not linear, an exception is thrown. + * + * @param expression The expression for which to compute the coefficients. + * @return A pair consisting of a mapping from identifiers to their coefficients and the coefficient of + * the constant atom. + */ + std::pair<SimpleValuation, double> getLinearCoefficients(Expression const& expression); + + virtual void visit(IfThenElseExpression const* expression) override; + virtual void visit(BinaryBooleanFunctionExpression const* expression) override; + virtual void visit(BinaryNumericalFunctionExpression const* expression) override; + virtual void visit(BinaryRelationExpression const* expression) override; + virtual void visit(VariableExpression const* expression) override; + virtual void visit(UnaryBooleanFunctionExpression const* expression) override; + virtual void visit(UnaryNumericalFunctionExpression const* expression) override; + virtual void visit(BooleanLiteralExpression const* expression) override; + virtual void visit(IntegerLiteralExpression const* expression) override; + virtual void visit(DoubleLiteralExpression const* expression) override; + + private: + std::stack<std::pair<SimpleValuation, double>> resultStack; + }; + } +} + +#endif /* STORM_STORAGE_EXPRESSIONS_LINEARCOEFFICIENTVISITOR_H_ */ \ No newline at end of file diff --git a/src/storage/expressions/LinearityCheckVisitor.cpp b/src/storage/expressions/LinearityCheckVisitor.cpp index d4086abf1..5dd2f38ac 100644 --- a/src/storage/expressions/LinearityCheckVisitor.cpp +++ b/src/storage/expressions/LinearityCheckVisitor.cpp @@ -6,73 +6,124 @@ namespace storm { namespace expressions { - bool LinearityCheckVisitor::check(BaseExpression const* expression) { - expression->accept(this); - return resultStack.top(); + LinearityCheckVisitor::LinearityCheckVisitor() : resultStack() { + // Intentionally left empty. + } + + bool LinearityCheckVisitor::check(Expression const& expression) { + expression.getBaseExpression().accept(this); + return resultStack.top() == LinearityStatus::LinearWithoutVariables || resultStack.top() == LinearityStatus::LinearContainsVariables; } void LinearityCheckVisitor::visit(IfThenElseExpression const* expression) { // An if-then-else expression is never linear. - resultStack.push(false); + resultStack.push(LinearityStatus::NonLinear); } void LinearityCheckVisitor::visit(BinaryBooleanFunctionExpression const* expression) { // Boolean function applications are not allowed in linear expressions. - resultStack.push(false); + resultStack.push(LinearityStatus::NonLinear); } void LinearityCheckVisitor::visit(BinaryNumericalFunctionExpression const* expression) { - bool leftResult = true; - bool rightResult = true; + LinearityStatus leftResult; + LinearityStatus rightResult; switch (expression->getOperatorType()) { case BinaryNumericalFunctionExpression::OperatorType::Plus: case BinaryNumericalFunctionExpression::OperatorType::Minus: expression->getFirstOperand()->accept(this); leftResult = resultStack.top(); - if (!leftResult) { - + if (leftResult == LinearityStatus::NonLinear) { + return; } else { resultStack.pop(); - expression->getSecondOperand()->accept(this); + rightResult = resultStack.top(); + if (rightResult == LinearityStatus::NonLinear) { + return; + } + resultStack.pop(); } - + + resultStack.push(leftResult == LinearityStatus::LinearContainsVariables || rightResult == LinearityStatus::LinearContainsVariables ? LinearityStatus::LinearContainsVariables : LinearityStatus::LinearWithoutVariables); + break; case BinaryNumericalFunctionExpression::OperatorType::Times: case BinaryNumericalFunctionExpression::OperatorType::Divide: - case BinaryNumericalFunctionExpression::OperatorType::Min: resultStack.push(false); break; - case BinaryNumericalFunctionExpression::OperatorType::Max: resultStack.push(false); break; + expression->getFirstOperand()->accept(this); + leftResult = resultStack.top(); + + if (leftResult == LinearityStatus::NonLinear) { + return; + } else { + resultStack.pop(); + expression->getSecondOperand()->accept(this); + rightResult = resultStack.top(); + if (rightResult == LinearityStatus::NonLinear) { + return; + } + resultStack.pop(); + } + + if (leftResult == LinearityStatus::LinearContainsVariables && rightResult == LinearityStatus::LinearContainsVariables) { + resultStack.push(LinearityStatus::NonLinear); + } + + resultStack.push(leftResult == LinearityStatus::LinearContainsVariables || rightResult == LinearityStatus::LinearContainsVariables ? LinearityStatus::LinearContainsVariables : LinearityStatus::LinearWithoutVariables); + break; + case BinaryNumericalFunctionExpression::OperatorType::Min: resultStack.push(LinearityStatus::NonLinear); break; + case BinaryNumericalFunctionExpression::OperatorType::Max: resultStack.push(LinearityStatus::NonLinear); break; } } void LinearityCheckVisitor::visit(BinaryRelationExpression const* expression) { - resultStack.push(false); + LinearityStatus leftResult; + LinearityStatus rightResult; + expression->getFirstOperand()->accept(this); + leftResult = resultStack.top(); + + if (leftResult == LinearityStatus::NonLinear) { + return; + } else { + resultStack.pop(); + expression->getSecondOperand()->accept(this); + rightResult = resultStack.top(); + if (rightResult == LinearityStatus::NonLinear) { + return; + } + resultStack.pop(); + } + + resultStack.push(leftResult == LinearityStatus::LinearContainsVariables || rightResult == LinearityStatus::LinearContainsVariables ? LinearityStatus::LinearContainsVariables : LinearityStatus::LinearWithoutVariables); } void LinearityCheckVisitor::visit(VariableExpression const* expression) { - resultStack.push(true); + resultStack.push(LinearityStatus::LinearContainsVariables); } void LinearityCheckVisitor::visit(UnaryBooleanFunctionExpression const* expression) { // Boolean function applications are not allowed in linear expressions. - resultStack.push(false); + resultStack.push(LinearityStatus::NonLinear); } void LinearityCheckVisitor::visit(UnaryNumericalFunctionExpression const* expression) { - // Intentionally left empty (just pass subresult one level further). + switch (expression->getOperatorType()) { + case UnaryNumericalFunctionExpression::OperatorType::Minus: break; + case UnaryNumericalFunctionExpression::OperatorType::Floor: + case UnaryNumericalFunctionExpression::OperatorType::Ceil: resultStack.pop(); resultStack.push(LinearityStatus::NonLinear); break; + } } void LinearityCheckVisitor::visit(BooleanLiteralExpression const* expression) { - // Boolean function applications are not allowed in linear expressions. - resultStack.push(false); + resultStack.push(LinearityStatus::NonLinear); } void LinearityCheckVisitor::visit(IntegerLiteralExpression const* expression) { - resultStack.push(true); + resultStack.push(LinearityStatus::LinearWithoutVariables); } void LinearityCheckVisitor::visit(DoubleLiteralExpression const* expression) { - resultStack.push(true); + resultStack.push(LinearityStatus::LinearWithoutVariables); } } } \ No newline at end of file diff --git a/src/storage/expressions/LinearityCheckVisitor.h b/src/storage/expressions/LinearityCheckVisitor.h index 2b1c3937c..d76b658c8 100644 --- a/src/storage/expressions/LinearityCheckVisitor.h +++ b/src/storage/expressions/LinearityCheckVisitor.h @@ -13,12 +13,14 @@ namespace storm { /*! * Creates a linearity check visitor. */ - LinearityCheckVisitor() = default; + LinearityCheckVisitor(); /*! * Checks that the given expression is linear. + * + * @param expression The expression to check for linearity. */ - bool check(BaseExpression const* expression); + bool check(Expression const& expression); virtual void visit(IfThenElseExpression const* expression) override; virtual void visit(BinaryBooleanFunctionExpression const* expression) override; @@ -32,8 +34,10 @@ namespace storm { virtual void visit(DoubleLiteralExpression const* expression) override; private: + enum class LinearityStatus { NonLinear, LinearContainsVariables, LinearWithoutVariables }; + // A stack for communicating the results of the subexpressions. - std::stack<bool> resultStack; + std::stack<LinearityStatus> resultStack; }; } } diff --git a/src/storage/expressions/SimpleValuation.cpp b/src/storage/expressions/SimpleValuation.cpp index 4a5441e99..923bbe84b 100644 --- a/src/storage/expressions/SimpleValuation.cpp +++ b/src/storage/expressions/SimpleValuation.cpp @@ -1,5 +1,8 @@ -#include <boost/functional/hash.hpp> #include "src/storage/expressions/SimpleValuation.h" + +#include <set> + +#include <boost/functional/hash.hpp> #include "src/exceptions/ExceptionMacros.h" #include "src/exceptions/InvalidArgumentException.h" #include "src/exceptions/InvalidAccessException.h" @@ -43,6 +46,18 @@ namespace storm { this->identifierToValueMap.erase(nameValuePair); } + ExpressionReturnType SimpleValuation::getIdentifierType(std::string const& name) const { + auto nameValuePair = this->identifierToValueMap.find(name); + LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'."); + if (nameValuePair->second.type() == typeid(bool)) { + return ExpressionReturnType::Bool; + } else if (nameValuePair->second.type() == typeid(int_fast64_t)) { + return ExpressionReturnType::Int; + } else { + return ExpressionReturnType::Double; + } + } + bool SimpleValuation::containsBooleanIdentifier(std::string const& name) const { auto nameValuePair = this->identifierToValueMap.find(name); if (nameValuePair == this->identifierToValueMap.end()) { @@ -85,6 +100,48 @@ namespace storm { return boost::get<double>(nameValuePair->second); } + std::size_t SimpleValuation::getNumberOfIdentifiers() const { + return this->identifierToValueMap.size(); + } + + std::set<std::string> SimpleValuation::getIdentifiers() const { + std::set<std::string> result; + for (auto const& nameValuePair : this->identifierToValueMap) { + result.insert(nameValuePair.first); + } + return result; + } + + std::set<std::string> SimpleValuation::getBooleanIdentifiers() const { + std::set<std::string> result; + for (auto const& nameValuePair : this->identifierToValueMap) { + if (nameValuePair.second.type() == typeid(bool)) { + result.insert(nameValuePair.first); + } + } + return result; + } + + std::set<std::string> SimpleValuation::getIntegerIdentifiers() const { + std::set<std::string> result; + for (auto const& nameValuePair : this->identifierToValueMap) { + if (nameValuePair.second.type() == typeid(int_fast64_t)) { + result.insert(nameValuePair.first); + } + } + return result; + } + + std::set<std::string> SimpleValuation::getDoubleIdentifiers() const { + std::set<std::string> result; + for (auto const& nameValuePair : this->identifierToValueMap) { + if (nameValuePair.second.type() == typeid(double)) { + result.insert(nameValuePair.first); + } + } + return result; + } + std::ostream& operator<<(std::ostream& stream, SimpleValuation const& valuation) { stream << "{ "; uint_fast64_t elementIndex = 0; diff --git a/src/storage/expressions/SimpleValuation.h b/src/storage/expressions/SimpleValuation.h index 0ea05a511..fccfe2faa 100644 --- a/src/storage/expressions/SimpleValuation.h +++ b/src/storage/expressions/SimpleValuation.h @@ -6,6 +6,7 @@ #include <iostream> #include "src/storage/expressions/Valuation.h" +#include "src/storage/expressions/ExpressionReturnType.h" #include "src/utility/OsDetection.h" namespace storm { @@ -84,11 +85,24 @@ namespace storm { * @param name The name of the identifier that is to be removed. */ void removeIdentifier(std::string const& name); + + /*! + * Retrieves the type of the identifier with the given name. + * + * @param name The name of the identifier whose type to retrieve. + * @return The type of the identifier with the given name. + */ + ExpressionReturnType getIdentifierType(std::string const& name) const; // Override base class methods. virtual bool containsBooleanIdentifier(std::string const& name) const override; virtual bool containsIntegerIdentifier(std::string const& name) const override; virtual bool containsDoubleIdentifier(std::string const& name) const override; + virtual std::size_t getNumberOfIdentifiers() const override; + virtual std::set<std::string> getIdentifiers() const override; + virtual std::set<std::string> getBooleanIdentifiers() const override; + virtual std::set<std::string> getIntegerIdentifiers() const override; + virtual std::set<std::string> getDoubleIdentifiers() const override; virtual bool getBooleanValue(std::string const& name) const override; virtual int_fast64_t getIntegerValue(std::string const& name) const override; virtual double getDoubleValue(std::string const& name) const override; diff --git a/src/storage/expressions/SubstitutionVisitor.cpp b/src/storage/expressions/SubstitutionVisitor.cpp index 2559b474b..43aa01ab3 100644 --- a/src/storage/expressions/SubstitutionVisitor.cpp +++ b/src/storage/expressions/SubstitutionVisitor.cpp @@ -13,8 +13,8 @@ namespace storm { } template<typename MapType> - Expression SubstitutionVisitor<MapType>::substitute(BaseExpression const* expression) { - expression->accept(this); + Expression SubstitutionVisitor<MapType>::substitute(Expression const& expression) { + expression.getBaseExpression().accept(this); return Expression(this->expressionStack.top()); } diff --git a/src/storage/expressions/SubstitutionVisitor.h b/src/storage/expressions/SubstitutionVisitor.h index bc58148e3..0ebc0941e 100644 --- a/src/storage/expressions/SubstitutionVisitor.h +++ b/src/storage/expressions/SubstitutionVisitor.h @@ -26,7 +26,7 @@ namespace storm { * @return The expression in which all identifiers in the key set of the previously given mapping are * substituted with the mapped-to expressions. */ - Expression substitute(BaseExpression const* expression); + Expression substitute(Expression const& expression); virtual void visit(IfThenElseExpression const* expression) override; virtual void visit(BinaryBooleanFunctionExpression const* expression) override; diff --git a/src/storage/expressions/TypeCheckVisitor.cpp b/src/storage/expressions/TypeCheckVisitor.cpp index 044b85237..5ab80e141 100644 --- a/src/storage/expressions/TypeCheckVisitor.cpp +++ b/src/storage/expressions/TypeCheckVisitor.cpp @@ -12,8 +12,8 @@ namespace storm { } template<typename MapType> - void TypeCheckVisitor<MapType>::check(BaseExpression const* expression) { - expression->accept(this); + void TypeCheckVisitor<MapType>::check(Expression const& expression) { + expression.getBaseExpression().accept(this); } template<typename MapType> diff --git a/src/storage/expressions/TypeCheckVisitor.h b/src/storage/expressions/TypeCheckVisitor.h index 7772e0e7e..0cbf40f92 100644 --- a/src/storage/expressions/TypeCheckVisitor.h +++ b/src/storage/expressions/TypeCheckVisitor.h @@ -24,7 +24,7 @@ namespace storm { * * @param expression The expression in which to check the types. */ - void check(BaseExpression const* expression); + void check(Expression const& expression); virtual void visit(IfThenElseExpression const* expression) override; virtual void visit(BinaryBooleanFunctionExpression const* expression) override; diff --git a/src/storage/expressions/Valuation.h b/src/storage/expressions/Valuation.h index 19792b720..baf39462f 100644 --- a/src/storage/expressions/Valuation.h +++ b/src/storage/expressions/Valuation.h @@ -58,6 +58,42 @@ namespace storm { * @return True iff the identifier exists and is of boolean type. */ virtual bool containsDoubleIdentifier(std::string const& name) const = 0; + + /*! + * Retrieves the number of identifiers in this valuation. + * + * @return The number of identifiers in this valuation. + */ + virtual std::size_t getNumberOfIdentifiers() const = 0; + + /*! + * Retrieves the set of all identifiers contained in this valuation. + * + * @return The set of all identifiers contained in this valuation. + */ + virtual std::set<std::string> getIdentifiers() const = 0; + + /*! + * Retrieves the set of boolean identifiers contained in this valuation. + * + * @return The set of boolean identifiers contained in this valuation. + */ + virtual std::set<std::string> getBooleanIdentifiers() const = 0; + + /*! + * Retrieves the set of integer identifiers contained in this valuation. + * + * @return The set of integer identifiers contained in this valuation. + */ + virtual std::set<std::string> getIntegerIdentifiers() const = 0; + + /*! + * Retrieves the set of double identifiers contained in this valuation. + * + * @return The set of double identifiers contained in this valuation. + */ + virtual std::set<std::string> getDoubleIdentifiers() const = 0; + }; } diff --git a/test/functional/storage/ExpressionTest.cpp b/test/functional/storage/ExpressionTest.cpp index 26822190d..d3a920d1d 100644 --- a/test/functional/storage/ExpressionTest.cpp +++ b/test/functional/storage/ExpressionTest.cpp @@ -3,6 +3,7 @@ #include "gtest/gtest.h" #include "src/storage/expressions/Expression.h" +#include "src/storage/expressions/LinearityCheckVisitor.h" #include "src/storage/expressions/SimpleValuation.h" #include "src/exceptions/InvalidTypeException.h" @@ -332,4 +333,20 @@ TEST(Expression, SimpleEvaluationTest) { ASSERT_THROW(tempExpression.evaluateAsDouble(&valuation), storm::exceptions::InvalidTypeException); ASSERT_THROW(tempExpression.evaluateAsInt(&valuation), storm::exceptions::InvalidTypeException); EXPECT_FALSE(tempExpression.evaluateAsBool(&valuation)); +} + +TEST(Expression, VisitorTest) { + storm::expressions::Expression threeExpression; + storm::expressions::Expression piExpression; + storm::expressions::Expression intVarExpression; + storm::expressions::Expression doubleVarExpression; + + ASSERT_NO_THROW(threeExpression = storm::expressions::Expression::createIntegerLiteral(3)); + ASSERT_NO_THROW(piExpression = storm::expressions::Expression::createDoubleLiteral(3.14)); + ASSERT_NO_THROW(intVarExpression = storm::expressions::Expression::createIntegerVariable("y")); + ASSERT_NO_THROW(doubleVarExpression = storm::expressions::Expression::createDoubleVariable("z")); + + storm::expressions::Expression tempExpression = intVarExpression + doubleVarExpression * threeExpression; + storm::expressions::LinearityCheckVisitor visitor; + EXPECT_TRUE(visitor.check(tempExpression)); } \ No newline at end of file From d5c2f9248fb183ee4864b8177a24082d81b3a34b Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Sat, 10 May 2014 23:30:02 +0200 Subject: [PATCH 10/15] Finished linear coefficient visitor and adapted glpk solver to new expression-based LP solver interface. Former-commit-id: ba1d3a912f8bebe6d388f59198870d5b060391c5 --- src/solver/GlpkLpSolver.cpp | 42 ++++++--- .../expressions/LinearCoefficientVisitor.cpp | 87 ++++++++++++++++++- .../expressions/LinearityCheckVisitor.cpp | 19 +--- 3 files changed, 117 insertions(+), 31 deletions(-) diff --git a/src/solver/GlpkLpSolver.cpp b/src/solver/GlpkLpSolver.cpp index fc2c5f261..1cffa1a18 100644 --- a/src/solver/GlpkLpSolver.cpp +++ b/src/solver/GlpkLpSolver.cpp @@ -4,6 +4,8 @@ #include <iostream> +#include "src/storage/expressions/LinearCoefficientVisitor.h" + #include "src/settings/Settings.h" #include "src/exceptions/ExceptionMacros.h" #include "src/exceptions/InvalidAccessException.h" @@ -160,26 +162,46 @@ namespace storm { LOG_THROW(constraint.isRelationalExpression(), storm::exceptions::InvalidArgumentException, "Illegal constraint is not a relational expression."); LOG_THROW(constraint.getOperator() != storm::expressions::OperatorType::NotEqual, storm::exceptions::InvalidArgumentException, "Illegal constraint uses inequality operator."); - // TODO: get variable/coefficients vector from constraint. + std::pair<storm::expressions::SimpleValuation, double> leftCoefficients = storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(0)); + std::pair<storm::expressions::SimpleValuation, double> rightCoefficients = storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(1)); + for (auto const& identifier : rightCoefficients.first.getDoubleIdentifiers()) { + if (leftCoefficients.first.containsDoubleIdentifier(identifier)) { + leftCoefficients.first.setDoubleValue(identifier, leftCoefficients.first.getDoubleValue(identifier) - rightCoefficients.first.getDoubleValue(identifier)); + } else { + leftCoefficients.first.addDoubleIdentifier(identifier, -rightCoefficients.first.getDoubleValue(identifier)); + } + } + rightCoefficients.second -= leftCoefficients.second; + // Now we need to transform the coefficients to the vector representation. + std::vector<int> variables; + std::vector<double> coefficients; + for (auto const& identifier : leftCoefficients.first.getDoubleIdentifiers()) { + auto identifierIndexPair = this->variableNameToIndexMap.find(identifier); + LOG_THROW(identifierIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Constraint contains illegal identifier '" << identifier << "'."); + variables.push_back(identifierIndexPair->second); + coefficients.push_back(leftCoefficients.first.getDoubleValue(identifier)); + } // Determine the type of the constraint and add it properly. switch (constraint.getOperator()) { case storm::expressions::OperatorType::Less: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightHandSideValue - storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble()); + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightCoefficients.second - storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble()); break; - case LESS_EQUAL: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightHandSideValue); + case storm::expressions::OperatorType::LessOrEqual: + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightCoefficients.second); break; - case GREATER: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightHandSideValue + storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(), 0); + case storm::expressions::OperatorType::Greater: + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightCoefficients.second + storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(), 0); break; - case GREATER_EQUAL: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightHandSideValue, 0); + case storm::expressions::OperatorType::GreaterOrEqual: + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightCoefficients.second, 0); break; - case EQUAL: - glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_FX, rightHandSideValue, rightHandSideValue); + case storm::expressions::OperatorType::Equal: + glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_FX, rightCoefficients.second, rightCoefficients.second); break; + default: + LOG_ASSERT(false, "Illegal operator in LP solver constraint."); } // Record the variables and coefficients in the coefficient matrix. diff --git a/src/storage/expressions/LinearCoefficientVisitor.cpp b/src/storage/expressions/LinearCoefficientVisitor.cpp index 58fd427d5..f59804927 100644 --- a/src/storage/expressions/LinearCoefficientVisitor.cpp +++ b/src/storage/expressions/LinearCoefficientVisitor.cpp @@ -16,15 +16,96 @@ namespace storm { } void LinearCoefficientVisitor::visit(BinaryBooleanFunctionExpression const* expression) { - + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); } void LinearCoefficientVisitor::visit(BinaryNumericalFunctionExpression const* expression) { - + if (expression->getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Plus) { + expression->getFirstOperand()->accept(this); + std::pair<SimpleValuation, double> leftResult = resultStack.top(); + resultStack.pop(); + expression->getSecondOperand()->accept(this); + std::pair<SimpleValuation, double>& rightResult = resultStack.top(); + + // Now add the left result to the right result. + for (auto const& identifier : leftResult.first.Valuation::getDoubleIdentifiers()) { + if (rightResult.first.containsDoubleIdentifier(identifier)) { + rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier) + rightResult.first.getDoubleValue(identifier)); + } else { + rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier)); + } + } + rightResult.second += leftResult.second; + return; + } else if (expression->getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Minus) { + expression->getFirstOperand()->accept(this); + std::pair<SimpleValuation, double> leftResult = resultStack.top(); + resultStack.pop(); + expression->getSecondOperand()->accept(this); + std::pair<SimpleValuation, double>& rightResult = resultStack.top(); + + // Now subtract the right result from the left result. + for (auto const& identifier : leftResult.first.getDoubleIdentifiers()) { + if (rightResult.first.containsDoubleIdentifier(identifier)) { + rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier) - rightResult.first.getDoubleValue(identifier)); + } else { + rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier)); + } + } + for (auto const& identifier : rightResult.first.Valuation::getDoubleIdentifiers()) { + if (!leftResult.first.containsDoubleIdentifier(identifier)) { + rightResult.first.setDoubleValue(identifier, -rightResult.first.getDoubleValue(identifier)); + } + } + rightResult.second = leftResult.second - rightResult.second; + return; + } else if (expression->getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Times) { + expression->getFirstOperand()->accept(this); + std::pair<SimpleValuation, double> leftResult = resultStack.top(); + resultStack.pop(); + expression->getSecondOperand()->accept(this); + std::pair<SimpleValuation, double>& rightResult = resultStack.top(); + + // If the expression is linear, either the left or the right side must not contain variables. + LOG_THROW(leftResult.first.getNumberOfIdentifiers() == 0 || rightResult.first.getNumberOfIdentifiers() == 0, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + if (leftResult.first.getNumberOfIdentifiers() == 0) { + for (auto const& identifier : rightResult.first.getDoubleIdentifiers()) { + rightResult.first.setDoubleValue(identifier, leftResult.second * rightResult.first.getDoubleValue(identifier)); + } + } else { + for (auto const& identifier : leftResult.first.getDoubleIdentifiers()) { + rightResult.first.addDoubleIdentifier(identifier, rightResult.second * leftResult.first.getDoubleValue(identifier)); + } + } + rightResult.second *= leftResult.second; + return; + } else if (expression->getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Divide) { + expression->getFirstOperand()->accept(this); + std::pair<SimpleValuation, double> leftResult = resultStack.top(); + resultStack.pop(); + expression->getSecondOperand()->accept(this); + std::pair<SimpleValuation, double>& rightResult = resultStack.top(); + + // If the expression is linear, either the left or the right side must not contain variables. + LOG_THROW(leftResult.first.getNumberOfIdentifiers() == 0 || rightResult.first.getNumberOfIdentifiers() == 0, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + if (leftResult.first.getNumberOfIdentifiers() == 0) { + for (auto const& identifier : rightResult.first.getDoubleIdentifiers()) { + rightResult.first.setDoubleValue(identifier, leftResult.second / rightResult.first.getDoubleValue(identifier)); + } + } else { + for (auto const& identifier : leftResult.first.getDoubleIdentifiers()) { + rightResult.first.addDoubleIdentifier(identifier, leftResult.first.getDoubleValue(identifier) / rightResult.second); + } + } + rightResult.second = leftResult.second / leftResult.second; + return; + } else { + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); + } } void LinearCoefficientVisitor::visit(BinaryRelationExpression const* expression) { - + LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); } void LinearCoefficientVisitor::visit(VariableExpression const* expression) { diff --git a/src/storage/expressions/LinearityCheckVisitor.cpp b/src/storage/expressions/LinearityCheckVisitor.cpp index 5dd2f38ac..9b382ed22 100644 --- a/src/storage/expressions/LinearityCheckVisitor.cpp +++ b/src/storage/expressions/LinearityCheckVisitor.cpp @@ -77,24 +77,7 @@ namespace storm { } void LinearityCheckVisitor::visit(BinaryRelationExpression const* expression) { - LinearityStatus leftResult; - LinearityStatus rightResult; - expression->getFirstOperand()->accept(this); - leftResult = resultStack.top(); - - if (leftResult == LinearityStatus::NonLinear) { - return; - } else { - resultStack.pop(); - expression->getSecondOperand()->accept(this); - rightResult = resultStack.top(); - if (rightResult == LinearityStatus::NonLinear) { - return; - } - resultStack.pop(); - } - - resultStack.push(leftResult == LinearityStatus::LinearContainsVariables || rightResult == LinearityStatus::LinearContainsVariables ? LinearityStatus::LinearContainsVariables : LinearityStatus::LinearWithoutVariables); + resultStack.push(LinearityStatus::NonLinear); } void LinearityCheckVisitor::visit(VariableExpression const* expression) { From 29d8111991a11fdc91092214b05b94b4eb89c9bc Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Sun, 11 May 2014 19:12:20 +0200 Subject: [PATCH 11/15] Adapted Gurobi and glpk LP solvers to expression-based interface. Adapted tests and made them work again. Former-commit-id: 62379ddafd41487e86e55495a797540505184ada --- .../SparseMarkovAutomatonCslModelChecker.cpp | 14 +- .../SparseMarkovAutomatonCslModelChecker.h | 2 +- src/solver/GlpkLpSolver.cpp | 93 ++--- src/solver/GlpkLpSolver.h | 18 +- src/solver/GurobiLpSolver.cpp | 386 +++++++----------- src/solver/GurobiLpSolver.h | 16 +- src/solver/LpSolver.h | 6 +- src/storage/expressions/Expression.cpp | 12 +- .../expressions/LinearCoefficientVisitor.cpp | 4 +- src/storage/expressions/Valuation.h | 1 - test/functional/solver/GlpkLpSolverTest.cpp | 243 ++++++----- test/functional/solver/GurobiLpSolverTest.cpp | 240 +++++------ 12 files changed, 451 insertions(+), 584 deletions(-) diff --git a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp index 82590e31e..580ea9c69 100644 --- a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp +++ b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp @@ -1,8 +1,8 @@ -#include "src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h" +// #include "src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h" -bool SparseMarkovAutomatonCslModelCheckerOptionsRegistered = storm::settings::Settings::registerNewModule([] (storm::settings::Settings* instance) -> bool { - - instance->addOption(storm::settings::OptionBuilder("GmmxxLinearEquationSolver", "digiprecision", "", "Precision used for iterative solving of linear equation systems").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("precision value", "Precision").setDefaultValueDouble(1e-4).addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleRangeValidatorExcluding(0.0, 1.0)).build()).build()); - - return true; -}); \ No newline at end of file +//bool SparseMarkovAutomatonCslModelCheckerOptionsRegistered = storm::settings::Settings::registerNewModule([] (storm::settings::Settings* instance) -> bool { +// +// instance->addOption(storm::settings::OptionBuilder("GmmxxLinearEquationSolver", "digiprecision", "", "Precision used for iterative solving of linear equation systems").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("precision value", "Precision").setDefaultValueDouble(1e-4).addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleRangeValidatorExcluding(0.0, 1.0)).build()).build()); +// +// return true; +//}); diff --git a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h index aab76f807..8a924ba47 100644 --- a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h +++ b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h @@ -428,7 +428,7 @@ namespace storm { */ static ValueType computeLraForMaximalEndComponent(bool min, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, storm::storage::BitVector const& markovianStates, std::vector<ValueType> const& exitRates, storm::storage::BitVector const& goalStates, storm::storage::MaximalEndComponent const& mec, uint_fast64_t mecIndex = 0) { std::shared_ptr<storm::solver::LpSolver> solver = storm::utility::solver::getLpSolver("LRA for MEC"); - solver->setModelSense(min ? storm::solver::LpSolver::MAXIMIZE : storm::solver::LpSolver::MINIMIZE); + solver->setModelSense(min ? storm::solver::LpSolver::ModelSense::Maximize : storm::solver::LpSolver::ModelSense::Minimize); // First, we need to create the variables for the problem. std::map<uint_fast64_t, uint_fast64_t> stateToVariableIndexMap; diff --git a/src/solver/GlpkLpSolver.cpp b/src/solver/GlpkLpSolver.cpp index 1cffa1a18..2a9eec7f6 100644 --- a/src/solver/GlpkLpSolver.cpp +++ b/src/solver/GlpkLpSolver.cpp @@ -21,7 +21,7 @@ bool GlpkLpSolverOptionsRegistered = storm::settings::Settings::registerNewModul namespace storm { namespace solver { - GlpkLpSolver::GlpkLpSolver(std::string const& name, ModelSense const& modelSense) : LpSolver(modelSense), lp(nullptr), variableNameToIndexMap(), nextVariableIndex(0), nextConstraintIndex(0), modelContainsIntegerVariables(false), isInfeasibleFlag(false), isUnboundedFlag(false), rowIndices(), columnIndices(), coefficientValues() { + GlpkLpSolver::GlpkLpSolver(std::string const& name, ModelSense const& modelSense) : LpSolver(modelSense), lp(nullptr), variableNameToIndexMap(), nextVariableIndex(1), nextConstraintIndex(1), modelContainsIntegerVariables(false), isInfeasibleFlag(false), isUnboundedFlag(false), rowIndices(), columnIndices(), coefficientValues() { // Create the LP problem for glpk. lp = glp_create_prob(); @@ -37,11 +37,11 @@ namespace storm { coefficientValues.push_back(0); } - GlpkLpSolver::GlpkLpSolver(std::string const& name) : GlpkLpSolver(name, ModelSense::MINIMIZE) { + GlpkLpSolver::GlpkLpSolver(std::string const& name) : GlpkLpSolver(name, ModelSense::Minimize) { // Intentionally left empty. } - GlpkLpSolver::GlpkLpSolver() : GlpkLpSolver("", ModelSense::MINIMIZE) { + GlpkLpSolver::GlpkLpSolver() : GlpkLpSolver("", ModelSense::Minimize) { // Intentionally left empty. } @@ -56,98 +56,65 @@ namespace storm { } void GlpkLpSolver::addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { - glp_add_cols(this->lp, 1); - glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); - glp_set_col_bnds(lp, nextVariableIndex, GLP_DB, lowerBound, upperBound); - glp_set_col_kind(this->lp, nextVariableIndex, GLP_CV); - glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); - this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); - ++this->nextVariableIndex; + this->addVariable(name, GLP_CV, GLP_DB, lowerBound, upperBound, objectiveFunctionCoefficient); } void GlpkLpSolver::addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) { - glp_add_cols(this->lp, 1); - glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); - glp_set_col_bnds(lp, nextVariableIndex, GLP_LO, lowerBound, 0); - glp_set_col_kind(this->lp, nextVariableIndex, GLP_CV); - glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); - this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); - ++this->nextVariableIndex; + this->addVariable(name, GLP_CV, GLP_LO, lowerBound, 0, objectiveFunctionCoefficient); } void GlpkLpSolver::addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) { - glp_add_cols(this->lp, 1); - glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); - glp_set_col_bnds(lp, nextVariableIndex, GLP_UP, 0, upperBound); - glp_set_col_kind(this->lp, nextVariableIndex, GLP_CV); - glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); - this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); - ++this->nextVariableIndex; + this->addVariable(name, GLP_CV, GLP_UP, 0, upperBound, objectiveFunctionCoefficient); } void GlpkLpSolver::addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient) { - glp_add_cols(this->lp, 1); - glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); - glp_set_col_bnds(lp, nextVariableIndex, GLP_FR, 0, 0); - glp_set_col_kind(this->lp, nextVariableIndex, GLP_CV); - glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); - this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); - ++this->nextVariableIndex; + this->addVariable(name, GLP_CV, GLP_FR, 0, 0, objectiveFunctionCoefficient); } void GlpkLpSolver::addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { - glp_add_cols(this->lp, 1); - glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); - glp_set_col_bnds(lp, nextVariableIndex, GLP_DB, lowerBound, upperBound); - glp_set_col_kind(this->lp, nextVariableIndex, GLP_IV); - glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); - this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); - ++this->nextVariableIndex; + this->addVariable(name, GLP_IV, GLP_DB, lowerBound, upperBound, objectiveFunctionCoefficient); this->modelContainsIntegerVariables = true; } void GlpkLpSolver::addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) { - glp_add_cols(this->lp, 1); - glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); - glp_set_col_bnds(lp, nextVariableIndex, GLP_LO, lowerBound, 0); - glp_set_col_kind(this->lp, nextVariableIndex, GLP_IV); - glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); - this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); - ++this->nextVariableIndex; + this->addVariable(name, GLP_IV, GLP_LO, lowerBound, 0, objectiveFunctionCoefficient); this->modelContainsIntegerVariables = true; } void GlpkLpSolver::addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) { - glp_add_cols(this->lp, 1); - glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); - glp_set_col_bnds(lp, nextVariableIndex, GLP_UP, 0, upperBound); - glp_set_col_kind(this->lp, nextVariableIndex, GLP_IV); - glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); - this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); - ++this->nextVariableIndex; + this->addVariable(name, GLP_IV, GLP_UP, 0, upperBound, objectiveFunctionCoefficient); this->modelContainsIntegerVariables = true; } void GlpkLpSolver::addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient) { - glp_add_cols(this->lp, 1); - glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); - glp_set_col_bnds(lp, nextVariableIndex, GLP_FR, 0, 0); - glp_set_col_kind(this->lp, nextVariableIndex, GLP_IV); - glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); - this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); - ++this->nextVariableIndex; + this->addVariable(name, GLP_IV, GLP_FR, 0, 0, objectiveFunctionCoefficient); this->modelContainsIntegerVariables = true; } void GlpkLpSolver::addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) { + this->addVariable(name, GLP_BV, GLP_FR, 0, 0, objectiveFunctionCoefficient); + this->modelContainsIntegerVariables = true; + } + + void GlpkLpSolver::addVariable(std::string const& name, int variableType, int boundType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + // Check whether variable already exists. + auto nameIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(nameIndexPair == this->variableNameToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Variable '" << nameIndexPair->first << "' already exists."); + + // Check for valid variable type. + LOG_ASSERT(variableType == GLP_CV || variableType == GLP_IV || variableType == GLP_BV, "Illegal type '" << variableType << "' for glpk variable."); + + // Check for valid bound type. + LOG_ASSERT(boundType == GLP_FR || boundType == GLP_UP || boundType == GLP_LO || boundType == GLP_DB, "Illegal bound type for variable '" << name << "'."); + + // Finally, create the actual variable. glp_add_cols(this->lp, 1); glp_set_col_name(this->lp, nextVariableIndex, name.c_str()); - glp_set_col_bnds(lp, nextVariableIndex, GLP_FR, 0, 0); - glp_set_col_kind(this->lp, nextVariableIndex, GLP_BV); + glp_set_col_bnds(lp, nextVariableIndex, boundType, lowerBound, upperBound); + glp_set_col_kind(this->lp, nextVariableIndex, variableType); glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient); this->variableNameToIndexMap.emplace(name, this->nextVariableIndex); ++this->nextVariableIndex; - this->modelContainsIntegerVariables = true; } void GlpkLpSolver::update() const { @@ -219,7 +186,7 @@ namespace storm { this->isUnboundedFlag = false; // Start by setting the model sense. - glp_set_obj_dir(this->lp, this->getModelSense() == LpSolver::ModelSense::MINIMIZE ? GLP_MIN : GLP_MAX); + glp_set_obj_dir(this->lp, this->getModelSense() == LpSolver::ModelSense::Minimize ? GLP_MIN : GLP_MAX); glp_load_matrix(this->lp, rowIndices.size() - 1, rowIndices.data(), columnIndices.data(), coefficientValues.data()); diff --git a/src/solver/GlpkLpSolver.h b/src/solver/GlpkLpSolver.h index 0045ced73..1f6d7bd2a 100644 --- a/src/solver/GlpkLpSolver.h +++ b/src/solver/GlpkLpSolver.h @@ -92,17 +92,29 @@ namespace storm { virtual void writeModelToFile(std::string const& filename) const override; private: + /*! + * Adds a variable with the given name, type, lower and upper bound and objective function coefficient. + * + * @param name The name of the variable. + * @param variableType The type of the variable in terms of glpk's constants. + * @param boundType A glpk flag indicating which bounds apply to the variable. + * @param lowerBound The lower bound of the range of the variable. + * @param upperBound The upper bound of the range of the variable. + * @param objectiveFunctionCoefficient The coefficient of the variable in the objective function. + */ + void addVariable(std::string const& name, int variableType, int boundType, double lowerBound, double upperBound, double objectiveFunctionCoefficient); + // The glpk LP problem. glp_prob* lp; // A mapping from variable names to their indices. - std::map<std::string, uint_fast64_t> variableNameToIndexMap; + std::map<std::string, int> variableNameToIndexMap; // A counter used for getting the next variable index. - uint_fast64_t nextVariableIndex; + int nextVariableIndex; // A counter used for getting the next constraint index. - uint_fast64_t nextConstraintIndex; + int nextConstraintIndex; // A flag storing whether the model is an LP or an MILP. bool modelContainsIntegerVariables; diff --git a/src/solver/GurobiLpSolver.cpp b/src/solver/GurobiLpSolver.cpp index 75a0d5a7e..676867ecb 100644 --- a/src/solver/GurobiLpSolver.cpp +++ b/src/solver/GurobiLpSolver.cpp @@ -3,13 +3,12 @@ #ifdef STORM_HAVE_GUROBI #include <numeric> -#include "src/exceptions/InvalidStateException.h" -#include "src/settings/Settings.h" +#include "src/storage/expressions/LinearCoefficientVisitor.h" -#include "log4cplus/logger.h" -#include "log4cplus/loggingmacros.h" - -extern log4cplus::Logger logger; +#include "src/settings/Settings.h" +#include "src/exceptions/ExceptionMacros.h" +#include "src/exceptions/InvalidStateException.h" +#include "src/exceptions/InvalidAccessException.h" bool GurobiLpSolverOptionsRegistered = storm::settings::Settings::registerNewModule([] (storm::settings::Settings* instance) -> bool { instance->addOption(storm::settings::OptionBuilder("GurobiLpSolver", "gurobithreads", "", "The number of threads that may be used by Gurobi.").addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The number of threads.").setDefaultValueUnsignedInteger(1).build()).build()); @@ -43,7 +42,7 @@ namespace storm { } } - GurobiLpSolver::GurobiLpSolver(std::string const& name) : GurobiLpSolver(name, MINIMIZE) { + GurobiLpSolver::GurobiLpSolver(std::string const& name) : GurobiLpSolver(name, ModelSense::Minimize) { // Intentionally left empty. } @@ -51,7 +50,7 @@ namespace storm { // Intentionally left empty. } - GurobiLpSolver::GurobiLpSolver() : GurobiLpSolver("", MINIMIZE) { + GurobiLpSolver::GurobiLpSolver() : GurobiLpSolver("", ModelSense::Minimize) { // Intentionally left empty. } @@ -66,145 +65,124 @@ namespace storm { // Enable the following line to only print the output of Gurobi if the debug flag is set. error = GRBsetintparam(env, "OutputFlag", storm::settings::Settings::getInstance()->isSet("debug") || storm::settings::Settings::getInstance()->isSet("gurobioutput") ? 1 : 0); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi Parameter OutputFlag (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi Parameter OutputFlag (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi Parameter OutputFlag (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // Enable the following line to restrict Gurobi to one thread only. error = GRBsetintparam(env, "Threads", storm::settings::Settings::getInstance()->getOptionByLongName("gurobithreads").getArgument(0).getValueAsUnsignedInteger()); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi Parameter Threads (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi Parameter Threads (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi Parameter Threads (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // Enable the following line to force Gurobi to be as precise about the binary variables as required by the given precision option. error = GRBsetdblparam(env, "IntFeasTol", storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi Parameter IntFeasTol (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi Parameter IntFeasTol (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi Parameter IntFeasTol (" << GRBgeterrormsg(env) << ", error code " << error << ")."); } void GurobiLpSolver::update() const { int error = GRBupdatemodel(model); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to update Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to update Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to update Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // Since the model changed, we erase the optimality flag. this->currentModelHasBeenOptimized = false; } - uint_fast64_t GurobiLpSolver::createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { - int error = 0; - switch (variableType) { - case LpSolver::BOUNDED: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, upperBound, GRB_CONTINUOUS, name.c_str()); - break; - case LpSolver::UNBOUNDED: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, -GRB_INFINITY, GRB_INFINITY, GRB_CONTINUOUS, name.c_str()); - break; - case LpSolver::UPPER_BOUND: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, -GRB_INFINITY, upperBound, GRB_CONTINUOUS, name.c_str()); - break; - case LpSolver::LOWER_BOUND: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, GRB_INFINITY, GRB_CONTINUOUS, name.c_str()); - break; - } - - if (error) { - LOG4CPLUS_ERROR(logger, "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } - ++nextVariableIndex; - return nextVariableIndex - 1; + void GurobiLpSolver::addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_CONTINUOUS, lowerBound, upperBound, objectiveFunctionCoefficient); } - uint_fast64_t GurobiLpSolver::createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { - int error = 0; - switch (variableType) { - case LpSolver::BOUNDED: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, upperBound, GRB_INTEGER, name.c_str()); - break; - case LpSolver::UNBOUNDED: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, -GRB_INFINITY, GRB_INFINITY, GRB_INTEGER, name.c_str()); - break; - case LpSolver::UPPER_BOUND: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, -GRB_INFINITY, upperBound, GRB_INTEGER, name.c_str()); - break; - case LpSolver::LOWER_BOUND: - error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, GRB_INFINITY, GRB_INTEGER, name.c_str()); - break; - } + void GurobiLpSolver::addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_CONTINUOUS, lowerBound, GRB_INFINITY, objectiveFunctionCoefficient); + } - if (error) { - LOG4CPLUS_ERROR(logger, "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } - ++nextVariableIndex; - return nextVariableIndex - 1; + void GurobiLpSolver::addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_CONTINUOUS, -GRB_INFINITY, upperBound, objectiveFunctionCoefficient); + } + + void GurobiLpSolver::addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_CONTINUOUS, -GRB_INFINITY, GRB_INFINITY, objectiveFunctionCoefficient); } - uint_fast64_t GurobiLpSolver::createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) { - int error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, 0.0, 1.0, GRB_BINARY, name.c_str()); - if (error) { - LOG4CPLUS_ERROR(logger, "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } - ++nextVariableIndex; - return nextVariableIndex - 1; + void GurobiLpSolver::addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_INTEGER, lowerBound, upperBound, objectiveFunctionCoefficient); } - void GurobiLpSolver::addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) { - if (variables.size() != coefficients.size()) { - LOG4CPLUS_ERROR(logger, "Sizes of variable indices vector and coefficients vector do not match."); - throw storm::exceptions::InvalidStateException() << "Sizes of variable indices vector and coefficients vector do not match."; - } + void GurobiLpSolver::addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_INTEGER, lowerBound, GRB_INFINITY, objectiveFunctionCoefficient); + } + + void GurobiLpSolver::addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_INTEGER, -GRB_INFINITY, upperBound, objectiveFunctionCoefficient); + } + + void GurobiLpSolver::addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_INTEGER, -GRB_INFINITY, GRB_INFINITY, objectiveFunctionCoefficient); + } + + void GurobiLpSolver::addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) { + this->addVariable(name, GRB_BINARY, 0, 1, objectiveFunctionCoefficient); + } + + void GurobiLpSolver::addVariable(std::string const& name, char variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) { + // Check whether variable already exists. + auto nameIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(nameIndexPair == this->variableNameToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Variable '" << nameIndexPair->first << "' already exists."); - // As Gurobi requires the indices to be unique, we now explicitly make them unique. For this, we sort the - // variables and coefficients and eliminated duplicates by adding the coefficients. + // Check for valid variable type. + LOG_ASSERT(variableType == GRB_CONTINUOUS || variableType == GRB_INTEGER || variableType == GRB_BINARY, "Illegal type '" << variableType << "' for Gurobi variable."); - // We start by sorting both vectors. - std::vector<uint_fast64_t> sortedVariables(variables); - std::vector<double> sortedCoefficients(coefficients); - std::vector<uint_fast64_t> permutation(variables.size()); - std::iota(permutation.begin(), permutation.end(), 0); - std::sort(permutation.begin(), permutation.end(), [&] (uint_fast64_t i, uint_fast64_t j) { return variables[i] < variables[j]; } ); - std::transform(permutation.begin(), permutation.end(), sortedVariables.begin(), [&] (uint_fast64_t i) { return variables[i]; } ); - std::transform(permutation.begin(), permutation.end(), sortedCoefficients.begin(), [&] (uint_fast64_t i) { return coefficients[i]; } ); + // Finally, create the actual variable. + int error = 0; + error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, upperBound, variableType, name.c_str()); + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ")."); + this->variableNameToIndexMap.emplace(name, nextVariableIndex); + ++nextVariableIndex; + } + + void GurobiLpSolver::addConstraint(std::string const& name, storm::expressions::Expression const& constraint) { + LOG_THROW(constraint.isRelationalExpression(), storm::exceptions::InvalidArgumentException, "Illegal constraint is not a relational expression."); + LOG_THROW(constraint.getOperator() != storm::expressions::OperatorType::NotEqual, storm::exceptions::InvalidArgumentException, "Illegal constraint uses inequality operator."); - // Now we perform the duplicate elimination step. - std::vector<int> uniqueVariables; - std::vector<double> uniqueCoefficients; - for (uint_fast64_t i = 0; i < sortedVariables.size(); ++i) { - if (!uniqueVariables.empty() && uniqueVariables.back() == sortedVariables[i]) { - uniqueCoefficients.back() += sortedCoefficients[i]; + std::pair<storm::expressions::SimpleValuation, double> leftCoefficients = storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(0)); + std::pair<storm::expressions::SimpleValuation, double> rightCoefficients = storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(1)); + for (auto const& identifier : rightCoefficients.first.getDoubleIdentifiers()) { + if (leftCoefficients.first.containsDoubleIdentifier(identifier)) { + leftCoefficients.first.setDoubleValue(identifier, leftCoefficients.first.getDoubleValue(identifier) - rightCoefficients.first.getDoubleValue(identifier)); } else { - uniqueVariables.push_back(static_cast<int>(sortedVariables[i])); - uniqueCoefficients.push_back(sortedCoefficients[i]); + leftCoefficients.first.addDoubleIdentifier(identifier, -rightCoefficients.first.getDoubleValue(identifier)); } } + rightCoefficients.second -= leftCoefficients.second; - bool strictBound = boundType == LESS || boundType == GREATER; - char sense = boundType == LESS || boundType == LESS_EQUAL ? GRB_LESS_EQUAL : boundType == EQUAL ? GRB_EQUAL : GRB_GREATER_EQUAL; - - // If the constraint enforces a strict bound, we need to do some tweaking of the right-hand side value, because Gurobi only supports - // non-strict bounds. - if (strictBound) { - if (boundType == LESS) { - rightHandSideValue -= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble(); - } else if (boundType == GREATER) { - rightHandSideValue += storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble(); - } + // Now we need to transform the coefficients to the vector representation. + std::vector<int> variables; + std::vector<double> coefficients; + for (auto const& identifier : leftCoefficients.first.getDoubleIdentifiers()) { + auto identifierIndexPair = this->variableNameToIndexMap.find(identifier); + LOG_THROW(identifierIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Constraint contains illegal identifier '" << identifier << "'."); + variables.push_back(identifierIndexPair->second); + coefficients.push_back(leftCoefficients.first.getDoubleValue(identifier)); } - int error = GRBaddconstr(model, uniqueVariables.size(), uniqueVariables.data(), uniqueCoefficients.data(), sense, rightHandSideValue, name == "" ? nullptr : name.c_str()); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to assert Gurobi constraint (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to assert Gurobi constraint (" << GRBgeterrormsg(env) << ", error code " << error << ")."; + // Determine the type of the constraint and add it properly. + int error = 0; + switch (constraint.getOperator()) { + case storm::expressions::OperatorType::Less: + error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_LESS_EQUAL, rightCoefficients.second - storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), name == "" ? nullptr : name.c_str()); + break; + case storm::expressions::OperatorType::LessOrEqual: + error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_LESS_EQUAL, rightCoefficients.second, name == "" ? nullptr : name.c_str()); + break; + case storm::expressions::OperatorType::Greater: + error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_GREATER_EQUAL, rightCoefficients.second + storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), name == "" ? nullptr : name.c_str()); + break; + case storm::expressions::OperatorType::GreaterOrEqual: + error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_GREATER_EQUAL, rightCoefficients.second, name == "" ? nullptr : name.c_str()); + break; + case storm::expressions::OperatorType::Equal: + error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_EQUAL, rightCoefficients.second, name == "" ? nullptr : name.c_str()); + break; + default: + LOG_ASSERT(false, "Illegal operator in LP solver constraint."); } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Could not assert constraint (" << GRBgeterrormsg(env) << ", error code " << error << ")."); } void GurobiLpSolver::optimize() const { @@ -212,18 +190,12 @@ namespace storm { this->update(); // Set the most recently set model sense. - int error = GRBsetintattr(model, "ModelSense", this->getModelSense() == MINIMIZE ? 1 : -1); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi model sense (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi model sense (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + int error = GRBsetintattr(model, "ModelSense", this->getModelSense() == ModelSense::Minimize ? 1 : -1); + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi model sense (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // Then we actually optimize the model. error = GRBoptimize(model); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to optimize Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to optimize Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to optimize Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); this->currentModelHasBeenOptimized = true; } @@ -236,34 +208,21 @@ namespace storm { int optimalityStatus = 0; int error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // By default, Gurobi may tell us only that the model is either infeasible or unbounded. To decide which one // it is, we need to perform an extra step. if (optimalityStatus == GRB_INF_OR_UNBD) { - std::cout << "here" << std::endl; error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 0); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); this->optimize(); error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 1); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); } return optimalityStatus == GRB_INFEASIBLE; @@ -277,33 +236,21 @@ namespace storm { int optimalityStatus = 0; int error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); // By default, Gurobi may tell us only that the model is either infeasible or unbounded. To decide which one // it is, we need to perform an extra step. if (optimalityStatus == GRB_INF_OR_UNBD) { error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 0); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); this->optimize(); error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 1); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ")."); } return optimalityStatus == GRB_UNBOUNDED; @@ -316,120 +263,79 @@ namespace storm { int optimalityStatus = 0; int error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ")."); return optimalityStatus == GRB_OPTIMAL; } - int_fast64_t GurobiLpSolver::getIntegerValue(uint_fast64_t variableIndex) const { + double GurobiLpSolver::getContinuousValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); } - double value = 0; - int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndex, &value); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); - if (std::abs(value - static_cast<int>(value)) <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()) { - // Nothing to do in this case. - } else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()) { - LOG4CPLUS_ERROR(logger, "Illegal value for integer variable in Gurobi solution (" << value << ")."); - throw storm::exceptions::InvalidStateException() << "Illegal value for integer variable in Gurobi solution (" << value << ")."; - } + double value = 0; + int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndexPair->second, &value); + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - return static_cast<int_fast64_t>(value); + return value; } - bool GurobiLpSolver::getBinaryValue(uint_fast64_t variableIndex) const { + int_fast64_t GurobiLpSolver::getIntegerValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."; - } - } - - double value = 0; - int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndex, &value); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."; + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); } - if (std::abs(value - 1) <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()) { - // Nothing to do in this case. - } else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()) { - LOG4CPLUS_ERROR(logger, "Illegal value for binary variable in Gurobi solution (" << value << ")."); - throw storm::exceptions::InvalidStateException() << "Illegal value for binary variable in Gurobi solution (" << value << ")."; - } + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); - return static_cast<bool>(value); + double value = 0; + int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndexPair->second, &value); + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); + LOG_THROW(std::abs(static_cast<int>(value) - value) <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for integer variable in Gurobi solution (" << value << ")."); + + return static_cast<int_fast64_t>(value); } - double GurobiLpSolver::getContinuousValue(uint_fast64_t variableIndex) const { + bool GurobiLpSolver::getBinaryValue(std::string const& name) const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); } + auto variableIndexPair = this->variableNameToIndexMap.find(name); + LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'."); + double value = 0; - int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndex, &value); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."; + int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndexPair->second, &value); + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); + + if (value > 0.5) { + LOG_THROW(std::abs(static_cast<int>(value) - 1) <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for integer variable in Gurobi solution (" << value << ")."); + } else { + LOG_THROW(value <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for integer variable in Gurobi solution (" << value << ")."); } - return value; + return static_cast<bool>(value); } double GurobiLpSolver::getObjectiveValue() const { if (!this->isOptimal()) { - if (this->isInfeasible()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."; - } else if (this->isUnbounded()) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."; - } else { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."; - } + LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ")."); + LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ")."); } double value = 0; int error = GRBgetdblattr(model, GRB_DBL_ATTR_OBJVAL, &value); - if (error) { - LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); - throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."; - } + LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ")."); return value; } diff --git a/src/solver/GurobiLpSolver.h b/src/solver/GurobiLpSolver.h index 27ac66c52..e9eac0e85 100644 --- a/src/solver/GurobiLpSolver.h +++ b/src/solver/GurobiLpSolver.h @@ -102,14 +102,28 @@ namespace storm { */ void setGurobiEnvironmentProperties() const; + /*! + * Adds a variable with the given name, type, lower and upper bound and objective function coefficient. + * + * @param name The name of the variable. + * @param variableType The type of the variable in terms of Gurobi's constants. + * @param lowerBound The lower bound of the range of the variable. + * @param upperBound The upper bound of the range of the variable. + * @param objectiveFunctionCoefficient The coefficient of the variable in the objective function. + */ + void addVariable(std::string const& name, char variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient); + // The Gurobi environment. GRBenv* env; // The Gurobi model. GRBmodel* model; + // The index of the next variable. + int nextVariableIndex; + // A mapping from variable names to their indices. - std::map<std::string, uint_fast64_t> variableNameToIndexMap; + std::map<std::string, int> variableNameToIndexMap; }; #else // If Gurobi is not available, we provide a stub implementation that emits an error if any of its methods is called. diff --git a/src/solver/LpSolver.h b/src/solver/LpSolver.h index f775f2829..5e7a5f76e 100644 --- a/src/solver/LpSolver.h +++ b/src/solver/LpSolver.h @@ -16,14 +16,14 @@ namespace storm { public: // An enumeration to represent whether the objective function is to be minimized or maximized. enum class ModelSense { - MINIMIZE, - MAXIMIZE + Minimize, + Maximize }; /*! * Creates an empty LP solver. By default the objective function is assumed to be minimized. */ - LpSolver() : currentModelHasBeenOptimized(false), modelSense(ModelSense::MINIMIZE) { + LpSolver() : currentModelHasBeenOptimized(false), modelSense(ModelSense::Minimize) { // Intentionally left empty. } diff --git a/src/storage/expressions/Expression.cpp b/src/storage/expressions/Expression.cpp index 762ed07bf..e8de2af62 100644 --- a/src/storage/expressions/Expression.cpp +++ b/src/storage/expressions/Expression.cpp @@ -17,27 +17,27 @@ namespace storm { } Expression Expression::substitute(std::map<std::string, Expression> const& identifierToExpressionMap) const { - return SubstitutionVisitor<std::map<std::string, Expression>>(identifierToExpressionMap).substitute(this); + return SubstitutionVisitor<std::map<std::string, Expression>>(identifierToExpressionMap).substitute(*this); } Expression Expression::substitute(std::unordered_map<std::string, Expression> const& identifierToExpressionMap) const { - return SubstitutionVisitor<std::unordered_map<std::string, Expression>>(identifierToExpressionMap).substitute(this); + return SubstitutionVisitor<std::unordered_map<std::string, Expression>>(identifierToExpressionMap).substitute(*this); } Expression Expression::substitute(std::map<std::string, std::string> const& identifierToIdentifierMap) const { - return IdentifierSubstitutionVisitor<std::map<std::string, std::string>>(identifierToIdentifierMap).substitute(this); + return IdentifierSubstitutionVisitor<std::map<std::string, std::string>>(identifierToIdentifierMap).substitute(*this); } Expression Expression::substitute(std::unordered_map<std::string, std::string> const& identifierToIdentifierMap) const { - return IdentifierSubstitutionVisitor<std::unordered_map<std::string, std::string>>(identifierToIdentifierMap).substitute(this); + return IdentifierSubstitutionVisitor<std::unordered_map<std::string, std::string>>(identifierToIdentifierMap).substitute(*this); } void Expression::check(std::map<std::string, storm::expressions::ExpressionReturnType> const& identifierToTypeMap) const { - return TypeCheckVisitor<std::map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(this); + return TypeCheckVisitor<std::map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(*this); } void Expression::check(std::unordered_map<std::string, storm::expressions::ExpressionReturnType> const& identifierToTypeMap) const { - return TypeCheckVisitor<std::unordered_map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(this); + return TypeCheckVisitor<std::unordered_map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(*this); } bool Expression::evaluateAsBool(Valuation const* valuation) const { diff --git a/src/storage/expressions/LinearCoefficientVisitor.cpp b/src/storage/expressions/LinearCoefficientVisitor.cpp index f59804927..7525d6dde 100644 --- a/src/storage/expressions/LinearCoefficientVisitor.cpp +++ b/src/storage/expressions/LinearCoefficientVisitor.cpp @@ -28,7 +28,7 @@ namespace storm { std::pair<SimpleValuation, double>& rightResult = resultStack.top(); // Now add the left result to the right result. - for (auto const& identifier : leftResult.first.Valuation::getDoubleIdentifiers()) { + for (auto const& identifier : leftResult.first.getDoubleIdentifiers()) { if (rightResult.first.containsDoubleIdentifier(identifier)) { rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier) + rightResult.first.getDoubleValue(identifier)); } else { @@ -52,7 +52,7 @@ namespace storm { rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier)); } } - for (auto const& identifier : rightResult.first.Valuation::getDoubleIdentifiers()) { + for (auto const& identifier : rightResult.first.getDoubleIdentifiers()) { if (!leftResult.first.containsDoubleIdentifier(identifier)) { rightResult.first.setDoubleValue(identifier, -rightResult.first.getDoubleValue(identifier)); } diff --git a/src/storage/expressions/Valuation.h b/src/storage/expressions/Valuation.h index baf39462f..f7ab3c985 100644 --- a/src/storage/expressions/Valuation.h +++ b/src/storage/expressions/Valuation.h @@ -94,7 +94,6 @@ namespace storm { */ virtual std::set<std::string> getDoubleIdentifiers() const = 0; - }; } } diff --git a/test/functional/solver/GlpkLpSolverTest.cpp b/test/functional/solver/GlpkLpSolverTest.cpp index 30c3e10f9..a6410611d 100644 --- a/test/functional/solver/GlpkLpSolverTest.cpp +++ b/test/functional/solver/GlpkLpSolverTest.cpp @@ -3,34 +3,34 @@ #include "src/solver/GlpkLpSolver.h" #include "src/exceptions/InvalidStateException.h" +#include "src/exceptions/InvalidAccessException.h" #include "src/settings/Settings.h" TEST(GlpkLpSolver, LPOptimizeMax) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getContinuousValue("x")); ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double yValue = 0; - ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getContinuousValue("y")); ASSERT_LT(std::abs(yValue - 6.5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 2.75), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -42,30 +42,29 @@ TEST(GlpkLpSolver, LPOptimizeMax) { TEST(GlpkLpSolver, LPOptimizeMin) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MINIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 1, 5.7, -1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Minimize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("z", 1, 5.7, -1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getContinuousValue("x")); ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double yValue = 0; - ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getContinuousValue("y")); ASSERT_LT(std::abs(yValue - 0), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 5.7), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -77,30 +76,29 @@ TEST(GlpkLpSolver, LPOptimizeMin) { TEST(GlpkLpSolver, MILPOptimizeMax) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getBinaryValue("x")); ASSERT_EQ(true, xValue); int_fast64_t yValue = 0; - ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getIntegerValue("y")); ASSERT_EQ(6, yValue); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 3), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -112,30 +110,29 @@ TEST(GlpkLpSolver, MILPOptimizeMax) { TEST(GlpkLpSolver, MILPOptimizeMin) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MINIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createIntegerVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 0, 5, -1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Minimize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("z", 0, 5, -1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getBinaryValue("x")); ASSERT_EQ(true, xValue); int_fast64_t yValue = 0; - ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getIntegerValue("y")); ASSERT_EQ(0, yValue); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -147,31 +144,30 @@ TEST(GlpkLpSolver, MILPOptimizeMin) { TEST(GlpkLpSolver, LPInfeasible) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") > storm::expressions::Expression::createDoubleLiteral(7))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_TRUE(solver.isInfeasible()); double xValue = 0; - ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getContinuousValue("x"), storm::exceptions::InvalidAccessException); double yValue = 0; - ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getContinuousValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without glpk support."; #endif @@ -179,31 +175,30 @@ TEST(GlpkLpSolver, LPInfeasible) { TEST(GlpkLpSolver, MILPInfeasible) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") > storm::expressions::Expression::createDoubleLiteral(7))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_TRUE(solver.isInfeasible()); bool xValue = false; - ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getBinaryValue("x"), storm::exceptions::InvalidAccessException); int_fast64_t yValue = 0; - ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getIntegerValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without glpk support."; #endif @@ -211,29 +206,28 @@ TEST(GlpkLpSolver, MILPInfeasible) { TEST(GlpkLpSolver, LPUnbounded) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_TRUE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getContinuousValue("x"), storm::exceptions::InvalidAccessException); double yValue = 0; - ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getContinuousValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without glpk support."; #endif @@ -241,30 +235,29 @@ TEST(GlpkLpSolver, LPUnbounded) { TEST(GlpkLpSolver, MILPUnbounded) { #ifdef STORM_HAVE_GLPK - storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - + storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_TRUE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getBinaryValue("x"), storm::exceptions::InvalidAccessException); int_fast64_t yValue = 0; - ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getIntegerValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without glpk support."; #endif -} \ No newline at end of file +} diff --git a/test/functional/solver/GurobiLpSolverTest.cpp b/test/functional/solver/GurobiLpSolverTest.cpp index 1ca524d8e..ee0ebfa03 100644 --- a/test/functional/solver/GurobiLpSolverTest.cpp +++ b/test/functional/solver/GurobiLpSolverTest.cpp @@ -3,36 +3,34 @@ #include "src/solver/GurobiLpSolver.h" #include "src/exceptions/InvalidStateException.h" +#include "src/exceptions/InvalidAccessException.h" #include "src/settings/Settings.h" TEST(GurobiLpSolver, LPOptimizeMax) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); - - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); + ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getContinuousValue("x")); ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double yValue = 0; - ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getContinuousValue("y")); ASSERT_LT(std::abs(yValue - 6.5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 2.75), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -44,32 +42,29 @@ TEST(GurobiLpSolver, LPOptimizeMax) { TEST(GurobiLpSolver, LPOptimizeMin) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MINIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 1, 5.7, -1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Minimize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("z", 1, 5.7, -1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getContinuousValue("x")); ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double yValue = 0; - ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getContinuousValue("y")); ASSERT_LT(std::abs(yValue - 0), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 5.7), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -81,32 +76,29 @@ TEST(GurobiLpSolver, LPOptimizeMin) { TEST(GurobiLpSolver, MILPOptimizeMax) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getBinaryValue("x")); ASSERT_EQ(true, xValue); int_fast64_t yValue = 0; - ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getIntegerValue("y")); ASSERT_EQ(6, yValue); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 3), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -118,32 +110,29 @@ TEST(GurobiLpSolver, MILPOptimizeMax) { TEST(GurobiLpSolver, MILPOptimizeMin) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MINIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createIntegerVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 0, 5.7, -1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Minimize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("z", 0, 5, -1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_TRUE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex)); + ASSERT_NO_THROW(xValue = solver.getBinaryValue("x")); ASSERT_EQ(true, xValue); int_fast64_t yValue = 0; - ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex)); + ASSERT_NO_THROW(yValue = solver.getIntegerValue("y")); ASSERT_EQ(0, yValue); double zValue = 0; - ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex)); + ASSERT_NO_THROW(zValue = solver.getContinuousValue("z")); ASSERT_LT(std::abs(zValue - 5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()); double objectiveValue = 0; ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue()); @@ -155,33 +144,30 @@ TEST(GurobiLpSolver, MILPOptimizeMin) { TEST(GurobiLpSolver, LPInfeasible) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") > storm::expressions::Expression::createDoubleLiteral(7))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_TRUE(solver.isInfeasible()); double xValue = 0; - ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getContinuousValue("x"), storm::exceptions::InvalidAccessException); double yValue = 0; - ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getContinuousValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without Gurobi support."; #endif @@ -189,33 +175,30 @@ TEST(GurobiLpSolver, LPInfeasible) { TEST(GurobiLpSolver, MILPInfeasible) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") > storm::expressions::Expression::createDoubleLiteral(7))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_FALSE(solver.isUnbounded()); ASSERT_TRUE(solver.isInfeasible()); bool xValue = false; - ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getBinaryValue("x"), storm::exceptions::InvalidAccessException); int_fast64_t yValue = 0; - ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getIntegerValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without Gurobi support."; #endif @@ -223,31 +206,28 @@ TEST(GurobiLpSolver, MILPInfeasible) { TEST(GurobiLpSolver, LPUnbounded) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); + + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); - ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_TRUE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); double xValue = 0; - ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getContinuousValue("x"), storm::exceptions::InvalidAccessException); double yValue = 0; - ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getContinuousValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without Gurobi support."; #endif @@ -255,32 +235,28 @@ TEST(GurobiLpSolver, LPUnbounded) { TEST(GurobiLpSolver, MILPUnbounded) { #ifdef STORM_HAVE_GUROBI - storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE); - uint_fast64_t xIndex; - ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1)); - uint_fast64_t yIndex; - ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2)); - uint_fast64_t zIndex; - ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1)); - - ASSERT_NO_THROW(solver.update()); + storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize); + ASSERT_NO_THROW(solver.addBinaryVariable("x", -1)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2)); + ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1)); + ASSERT_NO_THROW(solver.update()); - ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12)); - ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5)); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12))); + ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5))); ASSERT_NO_THROW(solver.optimize()); ASSERT_FALSE(solver.isOptimal()); ASSERT_TRUE(solver.isUnbounded()); ASSERT_FALSE(solver.isInfeasible()); bool xValue = false; - ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(xValue = solver.getBinaryValue("x"), storm::exceptions::InvalidAccessException); int_fast64_t yValue = 0; - ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(yValue = solver.getIntegerValue("y"), storm::exceptions::InvalidAccessException); double zValue = 0; - ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException); + ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException); double objectiveValue = 0; - ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException); + ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException); #else ASSERT_TRUE(false) << "StoRM built without Gurobi support."; #endif -} \ No newline at end of file +} From d80586b4aa4ae090c17dc42e3827d7d9f7745086 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Sun, 11 May 2014 20:39:41 +0200 Subject: [PATCH 12/15] Adapted MA model checker to new LP solver interface (LRA computation). Former-commit-id: b23b72c8510ca956a9c80a80bf448a0a4289f630 --- .../SparseMarkovAutomatonCslModelChecker.cpp | 14 ++--- .../SparseMarkovAutomatonCslModelChecker.h | 52 +++++++++---------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp index 580ea9c69..b5fa4bad4 100644 --- a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp +++ b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp @@ -1,8 +1,8 @@ -// #include "src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h" +#include "src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h" -//bool SparseMarkovAutomatonCslModelCheckerOptionsRegistered = storm::settings::Settings::registerNewModule([] (storm::settings::Settings* instance) -> bool { -// -// instance->addOption(storm::settings::OptionBuilder("GmmxxLinearEquationSolver", "digiprecision", "", "Precision used for iterative solving of linear equation systems").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("precision value", "Precision").setDefaultValueDouble(1e-4).addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleRangeValidatorExcluding(0.0, 1.0)).build()).build()); -// -// return true; -//}); +bool SparseMarkovAutomatonCslModelCheckerOptionsRegistered = storm::settings::Settings::registerNewModule([] (storm::settings::Settings* instance) -> bool { + + instance->addOption(storm::settings::OptionBuilder("GmmxxLinearEquationSolver", "digiprecision", "", "Precision used for iterative solving of linear equation systems").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("precision value", "Precision").setDefaultValueDouble(1e-4).addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleRangeValidatorExcluding(0.0, 1.0)).build()).build()); + + return true; +}); diff --git a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h index 8a924ba47..dbcf4d8b1 100644 --- a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h +++ b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h @@ -431,58 +431,58 @@ namespace storm { solver->setModelSense(min ? storm::solver::LpSolver::ModelSense::Maximize : storm::solver::LpSolver::ModelSense::Minimize); // First, we need to create the variables for the problem. - std::map<uint_fast64_t, uint_fast64_t> stateToVariableIndexMap; + std::map<uint_fast64_t, std::string> stateToVariableNameMap; for (auto const& stateChoicesPair : mec) { - stateToVariableIndexMap[stateChoicesPair.first] = solver->createContinuousVariable("x" + std::to_string(stateChoicesPair.first), storm::solver::LpSolver::UNBOUNDED, 0, 0, 0); + std::string variableName = "x" + std::to_string(stateChoicesPair.first); + stateToVariableNameMap[stateChoicesPair.first] = variableName; + solver->addUnboundedContinuousVariable(variableName); } - uint_fast64_t lraValueVariableIndex = solver->createContinuousVariable("k", storm::solver::LpSolver::UNBOUNDED, 0, 0, 1); + solver->addUnboundedContinuousVariable("k", 1); solver->update(); // Now we encode the problem as constraints. - std::vector<uint_fast64_t> variables; - std::vector<double> coefficients; for (auto const& stateChoicesPair : mec) { uint_fast64_t state = stateChoicesPair.first; // Now, based on the type of the state, create a suitable constraint. if (markovianStates.get(state)) { - variables.clear(); - coefficients.clear(); - - variables.push_back(stateToVariableIndexMap.at(state)); - coefficients.push_back(1); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(stateToVariableNameMap.at(state)); for (auto element : transitionMatrix.getRow(nondeterministicChoiceIndices[state])) { - variables.push_back(stateToVariableIndexMap.at(element.getColumn())); - coefficients.push_back(-element.getValue()); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(stateToVariableNameMap.at(element.getColumn())); } - variables.push_back(lraValueVariableIndex); - coefficients.push_back(storm::utility::constantOne<ValueType>() / exitRates[state]); - - solver->addConstraint("state" + std::to_string(state), variables, coefficients, min ? storm::solver::LpSolver::LESS_EQUAL : storm::solver::LpSolver::GREATER_EQUAL, goalStates.get(state) ? storm::utility::constantOne<ValueType>() / exitRates[state] : storm::utility::constantZero<ValueType>()); + constraint = constraint + storm::expressions::Expression::createDoubleLiteral(storm::utility::constantOne<ValueType>() / exitRates[state]) * storm::expressions::Expression::createDoubleVariable("k"); + storm::expressions::Expression rightHandSide = goalStates.get(state) ? storm::expressions::Expression::createDoubleLiteral(storm::utility::constantOne<ValueType>() / exitRates[state]) : storm::expressions::Expression::createDoubleLiteral(storm::utility::constantZero<ValueType>()); + if (min) { + constraint = constraint <= rightHandSide; + } else { + constraint = constraint >= rightHandSide; + } + solver->addConstraint("state" + std::to_string(state), constraint); } else { // For probabilistic states, we want to add the constraint x_s <= sum P(s, a, s') * x_s' where a is the current action // and the sum ranges over all states s'. for (auto choice : stateChoicesPair.second) { - variables.clear(); - coefficients.clear(); - - variables.push_back(stateToVariableIndexMap.at(state)); - coefficients.push_back(1); - + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(stateToVariableNameMap.at(state)); + for (auto element : transitionMatrix.getRow(choice)) { - variables.push_back(stateToVariableIndexMap.at(element.getColumn())); - coefficients.push_back(-element.getValue()); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(stateToVariableNameMap.at(element.getColumn())); } - solver->addConstraint("state" + std::to_string(state), variables, coefficients, min ? storm::solver::LpSolver::LESS_EQUAL : storm::solver::LpSolver::GREATER_EQUAL, storm::utility::constantZero<ValueType>()); + storm::expressions::Expression rightHandSide = storm::expressions::Expression::createDoubleLiteral(storm::utility::constantZero<ValueType>()); + if (min) { + constraint = constraint <= rightHandSide; + } else { + constraint = constraint >= rightHandSide; + } + solver->addConstraint("state" + std::to_string(state), constraint); } } } solver->optimize(); - return solver->getContinuousValue(lraValueVariableIndex); + return solver->getContinuousValue("k"); } /*! From db4721ce3ab1aff53bdb3928e7eccdf862ec2db9 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Sun, 11 May 2014 22:30:50 +0200 Subject: [PATCH 13/15] Started adapting MILP-based counterexample generator to new LP solver interface. Former-commit-id: b571d744db9a94e90cf85263ca9d8e5a3b6f132e --- .../MILPMinimalLabelSetGenerator.h | 151 +++++++++--------- 1 file changed, 77 insertions(+), 74 deletions(-) diff --git a/src/counterexamples/MILPMinimalLabelSetGenerator.h b/src/counterexamples/MILPMinimalLabelSetGenerator.h index 4e0f9381c..8da1374c4 100644 --- a/src/counterexamples/MILPMinimalLabelSetGenerator.h +++ b/src/counterexamples/MILPMinimalLabelSetGenerator.h @@ -65,13 +65,13 @@ namespace storm { * A helper struct capturing information about the variables of the MILP formulation. */ struct VariableInformation { - std::unordered_map<uint_fast64_t, uint_fast64_t> labelToVariableIndexMap; - std::unordered_map<uint_fast64_t, std::list<uint_fast64_t>> stateToChoiceVariablesIndexMap; - std::unordered_map<uint_fast64_t, uint_fast64_t> initialStateToChoiceVariableIndexMap; - std::unordered_map<uint_fast64_t, uint_fast64_t> stateToProbabilityVariableIndexMap; - uint_fast64_t virtualInitialStateVariableIndex; - std::unordered_map<uint_fast64_t, uint_fast64_t> problematicStateToVariableIndexMap; - std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, uint_fast64_t, PairHash> problematicTransitionToVariableIndexMap; + std::unordered_map<uint_fast64_t, std::string> labelToVariableMap; + std::unordered_map<uint_fast64_t, std::list<std::string>> stateToChoiceVariablesMap; + std::unordered_map<uint_fast64_t, std::string> initialStateToChoiceVariableMap; + std::unordered_map<uint_fast64_t, std::string> stateToProbabilityVariableMap; + std::string virtualInitialStateVariable; + std::unordered_map<uint_fast64_t, std::string> problematicStateToVariableMap; + std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, std::string, PairHash> problematicTransitionToVariableMap; uint_fast64_t numberOfVariables; VariableInformation() : numberOfVariables(0) {} @@ -167,14 +167,15 @@ namespace storm { * @param relevantLabels The set of relevant labels of the model. * @return A mapping from labels to variable indices. */ - static std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> createLabelVariables(storm::solver::LpSolver& solver, boost::container::flat_set<uint_fast64_t> const& relevantLabels) { + static std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> createLabelVariables(storm::solver::LpSolver& solver, boost::container::flat_set<uint_fast64_t> const& relevantLabels) { std::stringstream variableNameBuffer; - std::unordered_map<uint_fast64_t, uint_fast64_t> resultingMap; + std::unordered_map<uint_fast64_t, std::string> resultingMap; for (auto const& label : relevantLabels) { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "label" << label; - resultingMap[label] = solver.createBinaryVariable(variableNameBuffer.str(), 1); + resultingMap[label] = variableNameBuffer.str(); + solver.addBinaryVariable(resultingMap[label], 1); } return std::make_pair(resultingMap, relevantLabels.size()); } @@ -187,10 +188,10 @@ namespace storm { * @param choiceInformation The information about the choices of the model. * @return A mapping from states to a list of choice variable indices. */ - static std::pair<std::unordered_map<uint_fast64_t, std::list<uint_fast64_t>>, uint_fast64_t> createSchedulerVariables(storm::solver::LpSolver& solver, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { + static std::pair<std::unordered_map<uint_fast64_t, std::list<std::string>>, uint_fast64_t> createSchedulerVariables(storm::solver::LpSolver& solver, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { std::stringstream variableNameBuffer; uint_fast64_t numberOfVariablesCreated = 0; - std::unordered_map<uint_fast64_t, std::list<uint_fast64_t>> resultingMap; + std::unordered_map<uint_fast64_t, std::list<std::string>> resultingMap; for (auto state : stateInformation.relevantStates) { resultingMap.emplace(state, std::list<uint_fast64_t>()); @@ -199,7 +200,8 @@ namespace storm { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "choice" << row << "in" << state; - resultingMap[state].push_back(solver.createBinaryVariable(variableNameBuffer.str(), 0)); + resultingMap[state].push_back(variableNameBuffer.str()); + solver.addBinaryVariable(resultingMap[state].back()); ++numberOfVariablesCreated; } } @@ -215,10 +217,10 @@ namespace storm { * @param stateInformation The information about the states of the model. * @return A mapping from initial states to choice variable indices. */ - static std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> createInitialChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation) { + static std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> createInitialChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation) { std::stringstream variableNameBuffer; uint_fast64_t numberOfVariablesCreated = 0; - std::unordered_map<uint_fast64_t, uint_fast64_t> resultingMap; + std::unordered_map<uint_fast64_t, std::string> resultingMap; for (auto initialState : labeledMdp.getLabeledStates("init")) { // Only consider this initial state if it is relevant. @@ -226,7 +228,8 @@ namespace storm { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "init" << initialState; - resultingMap[initialState] = solver.createBinaryVariable(variableNameBuffer.str(), 0); + resultingMap[initialState] = variableNameBuffer.str(); + solver.addBinaryVariable(resultingMap[initialState]); ++numberOfVariablesCreated; } } @@ -240,16 +243,17 @@ namespace storm { * @param stateInformation The information about the states in the model. * @return A mapping from states to the index of the corresponding probability variables. */ - static std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> createProbabilityVariables(storm::solver::LpSolver& solver, StateInformation const& stateInformation) { + static std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> createProbabilityVariables(storm::solver::LpSolver& solver, StateInformation const& stateInformation) { std::stringstream variableNameBuffer; uint_fast64_t numberOfVariablesCreated = 0; - std::unordered_map<uint_fast64_t, uint_fast64_t> resultingMap; + std::unordered_map<uint_fast64_t, std::string> resultingMap; for (auto state : stateInformation.relevantStates) { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "p" << state; - resultingMap[state] = solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0); + resultingMap[state] = variableNameBuffer.str(); + solver.addBoundedContinuousVariable(resultingMap[state], 0, 1); ++numberOfVariablesCreated; } return std::make_pair(resultingMap, numberOfVariablesCreated); @@ -263,11 +267,12 @@ namespace storm { * label-minimal subsystem of maximal probability is computed. * @return The index of the variable for the probability of the virtual initial state. */ - static std::pair<uint_fast64_t, uint_fast64_t> createVirtualInitialStateVariable(storm::solver::LpSolver& solver, bool maximizeProbability = false) { + static std::pair<std::string, uint_fast64_t> createVirtualInitialStateVariable(storm::solver::LpSolver& solver, bool maximizeProbability = false) { std::stringstream variableNameBuffer; variableNameBuffer << "pinit"; - - return std::make_pair(solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0), 1); + std::string variableName = variableNameBuffer.str(); + solver.addBoundedContinuousVariable(variableName, 0, 1); + return std::make_pair(variableName, 1); } /*! @@ -278,10 +283,10 @@ namespace storm { * @param stateInformation The information about the states in the model. * @return A mapping from problematic states to the index of the corresponding variables. */ - static std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> createProblematicStateVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { + static std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> createProblematicStateVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { std::stringstream variableNameBuffer; uint_fast64_t numberOfVariablesCreated = 0; - std::unordered_map<uint_fast64_t, uint_fast64_t> resultingMap; + std::unordered_map<uint_fast64_t, std::string> resultingMap; for (auto state : stateInformation.problematicStates) { // First check whether there is not already a variable for this state and advance to the next state @@ -290,7 +295,8 @@ namespace storm { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "r" << state; - resultingMap[state] = solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0); + resultingMap[state] = variableNameBuffer.str(); + solver.addBoundedContinuousVariable(resultingMap[state], 0, 1); ++numberOfVariablesCreated; } @@ -302,7 +308,8 @@ namespace storm { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "r" << successorEntry.getColumn(); - resultingMap[state] = solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0); + resultingMap[state] = variableNameBuffer.str(); + solver.addBoundedContinuousVariable(resultingMap[state], 0, 1); ++numberOfVariablesCreated; } } @@ -321,10 +328,10 @@ namespace storm { * @param choiceInformation The information about the choices in the model. * @return A mapping from problematic choices to the index of the corresponding variables. */ - static std::pair<std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, uint_fast64_t, PairHash>, uint_fast64_t> createProblematicChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { + static std::pair<std::unordered_map<std::pair<uint_fast64_t, std::string>, uint_fast64_t, PairHash>, uint_fast64_t> createProblematicChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { std::stringstream variableNameBuffer; uint_fast64_t numberOfVariablesCreated = 0; - std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, uint_fast64_t, PairHash> resultingMap; + std::unordered_map<std::pair<uint_fast64_t, std::string>, uint_fast64_t, PairHash> resultingMap; for (auto state : stateInformation.problematicStates) { std::list<uint_fast64_t> const& relevantChoicesForState = choiceInformation.relevantChoicesForRelevantStates.at(state); @@ -334,7 +341,8 @@ namespace storm { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "t" << state << "to" << successorEntry.getColumn(); - resultingMap[std::make_pair(state, successorEntry.getColumn())] = solver.createBinaryVariable(variableNameBuffer.str(), 0); + resultingMap[std::make_pair(state, successorEntry.getColumn())] = variableNameBuffer.str(); + solver.addBinaryVariable(resultingMap[std::make_pair(state, successorEntry.getColumn())]); ++numberOfVariablesCreated; } } @@ -382,7 +390,7 @@ namespace storm { LOG4CPLUS_DEBUG(logger, "Created variables for the reachability probabilities."); // Create a probability variable for a virtual initial state that nondeterministically chooses one of the system's real initial states as its target state. - std::pair<uint_fast64_t, uint_fast64_t> virtualInitialStateVariableResult = createVirtualInitialStateVariable(solver); + std::pair<std::string, uint_fast64_t> virtualInitialStateVariableResult = createVirtualInitialStateVariable(solver); result.virtualInitialStateVariableIndex = virtualInitialStateVariableResult.first; result.numberOfVariables += virtualInitialStateVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for the virtual initial state."); @@ -419,7 +427,13 @@ namespace storm { * @return The total number of constraints that were created. */ static uint_fast64_t assertProbabilityGreaterThanThreshold(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, VariableInformation const& variableInformation, double probabilityThreshold, bool strictBound) { - solver.addConstraint("ProbGreaterThreshold", {variableInformation.virtualInitialStateVariableIndex}, {1}, strictBound ? storm::solver::LpSolver::GREATER : storm::solver::LpSolver::GREATER_EQUAL, probabilityThreshold); + storm::expressions::Expression constraint; + if (strictBound) { + constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.virtualInitialStateVariable) > storm::expressions::Expression::createDoubleLiteral(probabilityThreshold); + } else { + constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.virtualInitialStateVariable) >= storm::expressions::Expression::createDoubleLiteral(probabilityThreshold); + } + solver.addConstraint("ProbGreaterThreshold", constraint); return 1; } @@ -435,28 +449,28 @@ namespace storm { // Assert that the policy chooses at most one action in each state of the system. uint_fast64_t numberOfConstraintsCreated = 0; for (auto state : stateInformation.relevantStates) { - std::list<uint_fast64_t> const& choiceVariableIndices = variableInformation.stateToChoiceVariablesIndexMap.at(state); - std::vector<uint_fast64_t> variables; - std::vector<double> coefficients(choiceVariableIndices.size(), 1); - variables.reserve(choiceVariableIndices.size()); - for (auto choiceVariableIndex : choiceVariableIndices) { - variables.push_back(choiceVariableIndex); + std::list<std::string> const& choiceVariableIndices = variableInformation.stateToChoiceVariablesMap.at(state); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0); + + for (auto const& choiceVariable : choiceVariableIndices) { + constraint = constraint + storm::expressions::Expression::createDoubleVariable(choiceVariable); } - solver.addConstraint("ValidPolicy" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 1); + constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(1); + + solver.addConstraint("ValidPolicy" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } // Now assert that the virtual initial state picks exactly one initial state from the system as its // successor state. - std::vector<uint_fast64_t> variables; - variables.reserve(variableInformation.initialStateToChoiceVariableIndexMap.size()); - std::vector<double> coefficients(variableInformation.initialStateToChoiceVariableIndexMap.size(), 1); - for (auto initialStateVariableIndexPair : variableInformation.initialStateToChoiceVariableIndexMap) { - variables.push_back(initialStateVariableIndexPair.second); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0); + for (auto const& initialStateVariablePair : variableInformation.initialStateToChoiceVariableMap) { + constraint = constraint + storm::expressions::Expression::createDoubleVariable(initialStateVariablePair.second); } + constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(1); - solver.addConstraint("VirtualInitialStateChoosesOneInitialState", variables, coefficients, storm::solver::LpSolver::EQUAL, 1); + solver.addConstraint("VirtualInitialStateChoosesOneInitialState", constraint); ++numberOfConstraintsCreated; return numberOfConstraintsCreated; @@ -478,13 +492,14 @@ namespace storm { std::vector<boost::container::flat_set<uint_fast64_t>> const& choiceLabeling = labeledMdp.getChoiceLabeling(); for (auto state : stateInformation.relevantStates) { - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) { for (auto label : choiceLabeling[choice]) { - solver.addConstraint("ChoicesImplyLabels" + std::to_string(numberOfConstraintsCreated), {variableInformation.labelToVariableIndexMap.at(label), *choiceVariableIndicesIterator}, {1, -1}, storm::solver::LpSolver::GREATER_EQUAL, 0); + storm::expressions::Expression constraint = storm::expressions::Expression::createIntegerVariable(variableInformation.labelToVariableMap.at(label)) - storm::expressions::Expression::createIntegerVariable(*choiceVariableIterator) >= storm::expressions::Expression::createDoubleLiteral(0); + solver.addConstraint("ChoicesImplyLabels" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } } return numberOfConstraintsCreated; @@ -503,16 +518,12 @@ namespace storm { static uint_fast64_t assertZeroProbabilityWithoutChoice(storm::solver::LpSolver& solver, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation, VariableInformation const& variableInformation) { uint_fast64_t numberOfConstraintsCreated = 0; for (auto state : stateInformation.relevantStates) { - std::list<uint_fast64_t> const& choiceVariableIndices = variableInformation.stateToChoiceVariablesIndexMap.at(state); - - std::vector<double> coefficients(choiceVariableIndices.size() + 1, -1); - coefficients[0] = 1; - std::vector<uint_fast64_t> variables; - variables.reserve(variableInformation.stateToChoiceVariablesIndexMap.at(state).size() + 1); - variables.push_back(variableInformation.stateToProbabilityVariableIndexMap.at(state)); - variables.insert(variables.end(), choiceVariableIndices.begin(), choiceVariableIndices.end()); - - solver.addConstraint("ProbabilityIsZeroIfNoAction" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 0); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(state)); + for (auto const& choiceVariable : variableInformation.stateToChoiceVariablesMap.at(state)) { + constraint = constraint - storm::expressions::Expression::createDoubleVariable(choiceVariable); + } + constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(0); + solver.addConstraint("ProbabilityIsZeroIfNoAction" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } return numberOfConstraintsCreated; @@ -532,40 +543,32 @@ namespace storm { static uint_fast64_t assertReachabilityProbabilities(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& psiStates, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation, VariableInformation const& variableInformation) { uint_fast64_t numberOfConstraintsCreated = 0; for (auto state : stateInformation.relevantStates) { - std::vector<double> coefficients; - std::vector<uint_fast64_t> variables; - - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(state).begin(); for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) { - variables.clear(); - coefficients.clear(); - variables.push_back(variableInformation.stateToProbabilityVariableIndexMap.at(state)); - coefficients.push_back(1.0); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(state)); double rightHandSide = 1; for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(choice)) { if (stateInformation.relevantStates.get(successorEntry.getColumn())) { - variables.push_back(static_cast<int>(variableInformation.stateToProbabilityVariableIndexMap.at(successorEntry.getColumn()))); - coefficients.push_back(-successorEntry.getValue()); + constraint = constraint - storm::expressions::Expression::createDoubleLiteral(successorEntry.getValue()) * storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(successorEntry.getColumn())); } else if (psiStates.get(successorEntry.getColumn())) { rightHandSide += successorEntry.getValue(); } } - coefficients.push_back(1); - variables.push_back(*choiceVariableIndicesIterator); - - solver.addConstraint("ReachabilityProbabilities" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, rightHandSide); + constraint = constraint + storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator) <= storm::expressions::Expression::createDoubleLiteral(rightHandSide); + solver.addConstraint("ReachabilityProbabilities" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } } // Make sure that the virtual initial state is being assigned the probability from the initial state // that it selected as a successor state. - for (auto initialStateVariableIndexPair : variableInformation.initialStateToChoiceVariableIndexMap) { - solver.addConstraint("VirtualInitialStateHasCorrectProbability" + std::to_string(numberOfConstraintsCreated), {variableInformation.virtualInitialStateVariableIndex, variableInformation.stateToProbabilityVariableIndexMap.at(initialStateVariableIndexPair.first), initialStateVariableIndexPair.second}, {1, -1, 1}, storm::solver::LpSolver::LESS_EQUAL, 1); + for (auto const& initialStateVariableIndexPair : variableInformation.initialStateToChoiceVariableIndexMap) { + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.virtualInitialStateVariable) - storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(initialStateVariableIndexPair.first)) + storm::expressions::Expression::createDoubleVariable(initialStateVariableIndexPair.second) <= storm::expressions::Expression::createDoubleLiteral(1); + solver.addConstraint("VirtualInitialStateHasCorrectProbability" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } From 9e746549a8a90923c99e1089ea550021bcf5f92e Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Mon, 12 May 2014 11:41:07 +0200 Subject: [PATCH 14/15] Fully adapted MILP-based counterexample generator to new LP solver interface. Former-commit-id: 83f3b8c5075c5c614671b82ceb6cf6c762e3592c --- .../MILPMinimalLabelSetGenerator.h | 182 ++++++++---------- src/solver/GlpkLpSolver.h | 4 +- src/solver/GurobiLpSolver.h | 4 +- src/solver/LpSolver.h | 2 +- 4 files changed, 87 insertions(+), 105 deletions(-) diff --git a/src/counterexamples/MILPMinimalLabelSetGenerator.h b/src/counterexamples/MILPMinimalLabelSetGenerator.h index 8da1374c4..ad581e616 100644 --- a/src/counterexamples/MILPMinimalLabelSetGenerator.h +++ b/src/counterexamples/MILPMinimalLabelSetGenerator.h @@ -194,7 +194,7 @@ namespace storm { std::unordered_map<uint_fast64_t, std::list<std::string>> resultingMap; for (auto state : stateInformation.relevantStates) { - resultingMap.emplace(state, std::list<uint_fast64_t>()); + resultingMap.emplace(state, std::list<std::string>()); std::list<uint_fast64_t> const& relevantChoicesForState = choiceInformation.relevantChoicesForRelevantStates.at(state); for (uint_fast64_t row : relevantChoicesForState) { variableNameBuffer.str(""); @@ -260,7 +260,7 @@ namespace storm { } /*! - * Creates the variables for the probabilities in the model. + * Creates the variable for the probability of the virtual initial state. * * @param solver The MILP solver. * @param maximizeProbability If set to true, the objective function is constructed in a way that a @@ -308,8 +308,8 @@ namespace storm { variableNameBuffer.str(""); variableNameBuffer.clear(); variableNameBuffer << "r" << successorEntry.getColumn(); - resultingMap[state] = variableNameBuffer.str(); - solver.addBoundedContinuousVariable(resultingMap[state], 0, 1); + resultingMap[successorEntry.getColumn()] = variableNameBuffer.str(); + solver.addBoundedContinuousVariable(resultingMap[successorEntry.getColumn()], 0, 1); ++numberOfVariablesCreated; } } @@ -328,10 +328,10 @@ namespace storm { * @param choiceInformation The information about the choices in the model. * @return A mapping from problematic choices to the index of the corresponding variables. */ - static std::pair<std::unordered_map<std::pair<uint_fast64_t, std::string>, uint_fast64_t, PairHash>, uint_fast64_t> createProblematicChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { + static std::pair<std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, std::string, PairHash>, uint_fast64_t> createProblematicChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) { std::stringstream variableNameBuffer; uint_fast64_t numberOfVariablesCreated = 0; - std::unordered_map<std::pair<uint_fast64_t, std::string>, uint_fast64_t, PairHash> resultingMap; + std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, std::string, PairHash> resultingMap; for (auto state : stateInformation.problematicStates) { std::list<uint_fast64_t> const& relevantChoicesForState = choiceInformation.relevantChoicesForRelevantStates.at(state); @@ -366,44 +366,44 @@ namespace storm { VariableInformation result; // Create variables for involved labels. - std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> labelVariableResult = createLabelVariables(solver, choiceInformation.allRelevantLabels); - result.labelToVariableIndexMap = std::move(labelVariableResult.first); + std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> labelVariableResult = createLabelVariables(solver, choiceInformation.allRelevantLabels); + result.labelToVariableMap = std::move(labelVariableResult.first); result.numberOfVariables += labelVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for labels."); // Create scheduler variables for relevant states and their actions. - std::pair<std::unordered_map<uint_fast64_t, std::list<uint_fast64_t>>, uint_fast64_t> schedulerVariableResult = createSchedulerVariables(solver, stateInformation, choiceInformation); - result.stateToChoiceVariablesIndexMap = std::move(schedulerVariableResult.first); + std::pair<std::unordered_map<uint_fast64_t, std::list<std::string>>, uint_fast64_t> schedulerVariableResult = createSchedulerVariables(solver, stateInformation, choiceInformation); + result.stateToChoiceVariablesMap = std::move(schedulerVariableResult.first); result.numberOfVariables += schedulerVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for nondeterministic choices."); // Create scheduler variables for nondeterministically choosing an initial state. - std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> initialChoiceVariableResult = createInitialChoiceVariables(solver, labeledMdp, stateInformation); - result.initialStateToChoiceVariableIndexMap = std::move(initialChoiceVariableResult.first); + std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> initialChoiceVariableResult = createInitialChoiceVariables(solver, labeledMdp, stateInformation); + result.initialStateToChoiceVariableMap = std::move(initialChoiceVariableResult.first); result.numberOfVariables += initialChoiceVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for the nondeterministic choice of the initial state."); // Create variables for probabilities for all relevant states. - std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> probabilityVariableResult = createProbabilityVariables(solver, stateInformation); - result.stateToProbabilityVariableIndexMap = std::move(probabilityVariableResult.first); + std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> probabilityVariableResult = createProbabilityVariables(solver, stateInformation); + result.stateToProbabilityVariableMap = std::move(probabilityVariableResult.first); result.numberOfVariables += probabilityVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for the reachability probabilities."); // Create a probability variable for a virtual initial state that nondeterministically chooses one of the system's real initial states as its target state. std::pair<std::string, uint_fast64_t> virtualInitialStateVariableResult = createVirtualInitialStateVariable(solver); - result.virtualInitialStateVariableIndex = virtualInitialStateVariableResult.first; + result.virtualInitialStateVariable = virtualInitialStateVariableResult.first; result.numberOfVariables += virtualInitialStateVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for the virtual initial state."); // Create variables for problematic states. - std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> problematicStateVariableResult = createProblematicStateVariables(solver, labeledMdp, stateInformation, choiceInformation); - result.problematicStateToVariableIndexMap = std::move(problematicStateVariableResult.first); + std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> problematicStateVariableResult = createProblematicStateVariables(solver, labeledMdp, stateInformation, choiceInformation); + result.problematicStateToVariableMap = std::move(problematicStateVariableResult.first); result.numberOfVariables += problematicStateVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for the problematic states."); // Create variables for problematic choices. - std::pair<std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, uint_fast64_t, PairHash>, uint_fast64_t> problematicTransitionVariableResult = createProblematicChoiceVariables(solver, labeledMdp, stateInformation, choiceInformation); - result.problematicTransitionToVariableIndexMap = problematicTransitionVariableResult.first; + std::pair<std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, std::string, PairHash>, uint_fast64_t> problematicTransitionVariableResult = createProblematicChoiceVariables(solver, labeledMdp, stateInformation, choiceInformation); + result.problematicTransitionToVariableMap = problematicTransitionVariableResult.first; result.numberOfVariables += problematicTransitionVariableResult.second; LOG4CPLUS_DEBUG(logger, "Created variables for the problematic choices."); @@ -453,7 +453,7 @@ namespace storm { storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0); for (auto const& choiceVariable : choiceVariableIndices) { - constraint = constraint + storm::expressions::Expression::createDoubleVariable(choiceVariable); + constraint = constraint + storm::expressions::Expression::createIntegerVariable(choiceVariable); } constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(1); @@ -466,9 +466,9 @@ namespace storm { // successor state. storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0); for (auto const& initialStateVariablePair : variableInformation.initialStateToChoiceVariableMap) { - constraint = constraint + storm::expressions::Expression::createDoubleVariable(initialStateVariablePair.second); + constraint = constraint + storm::expressions::Expression::createIntegerVariable(initialStateVariablePair.second); } - constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(1); + constraint = constraint == storm::expressions::Expression::createDoubleLiteral(1); solver.addConstraint("VirtualInitialStateChoosesOneInitialState", constraint); ++numberOfConstraintsCreated; @@ -492,7 +492,7 @@ namespace storm { std::vector<boost::container::flat_set<uint_fast64_t>> const& choiceLabeling = labeledMdp.getChoiceLabeling(); for (auto state : stateInformation.relevantStates) { - std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(state).begin(); for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) { for (auto label : choiceLabeling[choice]) { storm::expressions::Expression constraint = storm::expressions::Expression::createIntegerVariable(variableInformation.labelToVariableMap.at(label)) - storm::expressions::Expression::createIntegerVariable(*choiceVariableIterator) >= storm::expressions::Expression::createDoubleLiteral(0); @@ -520,7 +520,7 @@ namespace storm { for (auto state : stateInformation.relevantStates) { storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(state)); for (auto const& choiceVariable : variableInformation.stateToChoiceVariablesMap.at(state)) { - constraint = constraint - storm::expressions::Expression::createDoubleVariable(choiceVariable); + constraint = constraint - storm::expressions::Expression::createIntegerVariable(choiceVariable); } constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(0); solver.addConstraint("ProbabilityIsZeroIfNoAction" + std::to_string(numberOfConstraintsCreated), constraint); @@ -556,7 +556,7 @@ namespace storm { } } - constraint = constraint + storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator) <= storm::expressions::Expression::createDoubleLiteral(rightHandSide); + constraint = constraint + storm::expressions::Expression::createIntegerVariable(*choiceVariableIterator) <= storm::expressions::Expression::createDoubleLiteral(rightHandSide); solver.addConstraint("ReachabilityProbabilities" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; @@ -566,8 +566,8 @@ namespace storm { // Make sure that the virtual initial state is being assigned the probability from the initial state // that it selected as a successor state. - for (auto const& initialStateVariableIndexPair : variableInformation.initialStateToChoiceVariableIndexMap) { - storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.virtualInitialStateVariable) - storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(initialStateVariableIndexPair.first)) + storm::expressions::Expression::createDoubleVariable(initialStateVariableIndexPair.second) <= storm::expressions::Expression::createDoubleLiteral(1); + for (auto const& initialStateVariablePair : variableInformation.initialStateToChoiceVariableMap) { + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.virtualInitialStateVariable) - storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(initialStateVariablePair.first)) + storm::expressions::Expression::createDoubleVariable(initialStateVariablePair.second) <= storm::expressions::Expression::createDoubleLiteral(1); solver.addConstraint("VirtualInitialStateHasCorrectProbability" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } @@ -590,44 +590,34 @@ namespace storm { for (auto stateListPair : choiceInformation.problematicChoicesForProblematicStates) { for (auto problematicChoice : stateListPair.second) { - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(stateListPair.first).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(stateListPair.first).begin(); for (auto relevantChoice : choiceInformation.relevantChoicesForRelevantStates.at(stateListPair.first)) { if (relevantChoice == problematicChoice) { break; } - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } - std::vector<uint_fast64_t> variables; - std::vector<double> coefficients; - - variables.push_back(*choiceVariableIndicesIterator); - coefficients.push_back(1); - + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator); for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(problematicChoice)) { - variables.push_back(variableInformation.problematicTransitionToVariableIndexMap.at(std::make_pair(stateListPair.first, successorEntry.getColumn()))); - coefficients.push_back(-1); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(variableInformation.problematicTransitionToVariableMap.at(std::make_pair(stateListPair.first, successorEntry.getColumn()))); } + constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(0); - solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 0); + solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } } for (auto state : stateInformation.problematicStates) { for (auto problematicChoice : choiceInformation.problematicChoicesForProblematicStates.at(state)) { - for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(state)) { - std::vector<uint_fast64_t> variables; - std::vector<double> coefficients; - - variables.push_back(variableInformation.problematicStateToVariableIndexMap.at(state)); - coefficients.push_back(1); - variables.push_back(variableInformation.problematicStateToVariableIndexMap.at(successorEntry.getColumn())); - coefficients.push_back(-1); - variables.push_back(variableInformation.problematicTransitionToVariableIndexMap.at(std::make_pair(state, successorEntry.getColumn()))); - coefficients.push_back(1); + for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(problematicChoice)) { + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.problematicStateToVariableMap.at(state)); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(variableInformation.problematicStateToVariableMap.at(successorEntry.getColumn())); + constraint = constraint + storm::expressions::Expression::createDoubleVariable(variableInformation.problematicTransitionToVariableMap.at(std::make_pair(state, successorEntry.getColumn()))); + constraint = constraint < storm::expressions::Expression::createDoubleLiteral(1); - solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS, 1); + solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } } @@ -649,7 +639,8 @@ namespace storm { uint_fast64_t numberOfConstraintsCreated = 0; for (auto label : choiceInformation.knownLabels) { - solver.addConstraint("KnownLabels" + std::to_string(numberOfConstraintsCreated), {variableInformation.labelToVariableIndexMap.at(label)}, {1}, storm::solver::LpSolver::EQUAL, 1); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.labelToVariableMap.at(label)) == storm::expressions::Expression::createDoubleLiteral(1); + solver.addConstraint("KnownLabels" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } @@ -670,13 +661,11 @@ namespace storm { static uint_fast64_t assertSchedulerCuts(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& psiStates, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation, VariableInformation const& variableInformation) { storm::storage::SparseMatrix<T> backwardTransitions = labeledMdp.getBackwardTransitions(); uint_fast64_t numberOfConstraintsCreated = 0; - std::vector<uint_fast64_t> variables; - std::vector<double> coefficients; for (auto state : stateInformation.relevantStates) { // Assert that all states, that select an action, this action either has a non-zero probability to // go to a psi state directly, or in the successor states, at least one action is selected as well. - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(state).begin(); for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) { bool psiStateReachableInOneStep = false; for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(choice)) { @@ -686,38 +675,31 @@ namespace storm { } if (!psiStateReachableInOneStep) { - variables.clear(); - coefficients.clear(); - - variables.push_back(static_cast<int>(*choiceVariableIndicesIterator)); - coefficients.push_back(1); - + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator); for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(choice)) { if (state != successorEntry.getColumn() && stateInformation.relevantStates.get(successorEntry.getColumn())) { - std::list<uint_fast64_t> const& successorChoiceVariableIndices = variableInformation.stateToChoiceVariablesIndexMap.at(successorEntry.getColumn()); + std::list<std::string> const& successorChoiceVariableIndices = variableInformation.stateToChoiceVariablesMap.at(successorEntry.getColumn()); - for (auto choiceVariableIndex : successorChoiceVariableIndices) { - variables.push_back(choiceVariableIndex); - coefficients.push_back(-1); + for (auto const& choiceVariable : successorChoiceVariableIndices) { + constraint = constraint - storm::expressions::Expression::createDoubleVariable(choiceVariable); } } } + constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(1); - solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 1); + solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } // For all states assert that there is either a selected incoming transition in the subsystem or the // state is the chosen initial state if there is one selected action in the current state. - variables.clear(); - coefficients.clear(); - - for (auto choiceVariableIndex : variableInformation.stateToChoiceVariablesIndexMap.at(state)) { - variables.push_back(choiceVariableIndex); - coefficients.push_back(1); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0); + + for (auto const& choiceVariable : variableInformation.stateToChoiceVariablesMap.at(state)) { + constraint = constraint + storm::expressions::Expression::createDoubleVariable(choiceVariable); } // Compute the set of predecessors. @@ -734,7 +716,7 @@ namespace storm { continue; } - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(predecessor).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(predecessor).begin(); for (auto relevantChoice : choiceInformation.relevantChoicesForRelevantStates.at(predecessor)) { bool choiceTargetsCurrentState = false; @@ -748,39 +730,36 @@ namespace storm { // If it does, we can add the choice to the sum. if (choiceTargetsCurrentState) { - variables.push_back(static_cast<int>(*choiceVariableIndicesIterator)); - coefficients.push_back(-1); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator); } - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } } // If the current state is an initial state and is selected as a successor state by the virtual // initial state, then this also justifies making a choice in the current state. if (labeledMdp.getLabeledStates("init").get(state)) { - variables.push_back(variableInformation.initialStateToChoiceVariableIndexMap.at(state)); - coefficients.push_back(-1); + constraint = constraint - storm::expressions::Expression::createDoubleVariable(variableInformation.initialStateToChoiceVariableMap.at(state)); } + constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(0); - solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 0); + solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; } // Assert that at least one initial state selects at least one action. - variables.clear(); - coefficients.clear(); + storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0); for (auto initialState : labeledMdp.getLabeledStates("init")) { - for (auto choiceVariableIndex : variableInformation.stateToChoiceVariablesIndexMap.at(initialState)) { - variables.push_back(choiceVariableIndex); - coefficients.push_back(1); + for (auto const& choiceVariable : variableInformation.stateToChoiceVariablesMap.at(initialState)) { + constraint = constraint + storm::expressions::Expression::createDoubleVariable(choiceVariable); } } - solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::GREATER_EQUAL, 1); + constraint = constraint >= storm::expressions::Expression::createDoubleLiteral(1); + solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; // Add constraints that ensure at least one choice is selected that targets a psi state. - variables.clear(); - coefficients.clear(); + constraint = storm::expressions::Expression::createDoubleLiteral(0); std::unordered_set<uint_fast64_t> predecessors; for (auto psiState : psiStates) { // Compute the set of predecessors. @@ -797,7 +776,7 @@ namespace storm { continue; } - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(predecessor).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(predecessor).begin(); for (auto relevantChoice : choiceInformation.relevantChoicesForRelevantStates.at(predecessor)) { bool choiceTargetsPsiState = false; @@ -811,14 +790,14 @@ namespace storm { // If it does, we can add the choice to the sum. if (choiceTargetsPsiState) { - variables.push_back(*choiceVariableIndicesIterator); - coefficients.push_back(1); + constraint = constraint + storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator); } - ++choiceVariableIndicesIterator; + ++choiceVariableIterator; } } + constraint = constraint >= storm::expressions::Expression::createDoubleLiteral(1); - solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::GREATER_EQUAL, 1); + solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), constraint); ++numberOfConstraintsCreated; return numberOfConstraintsCreated; @@ -875,6 +854,9 @@ namespace storm { LOG4CPLUS_DEBUG(logger, "Asserted scheduler cuts."); } + // Finally, we can tell the solver to incorporate the latest changes. + solver.update(); + LOG4CPLUS_INFO(logger, "Successfully created " << numberOfConstraints << " MILP constraints."); } @@ -887,7 +869,7 @@ namespace storm { static boost::container::flat_set<uint_fast64_t> getUsedLabelsInSolution(storm::solver::LpSolver const& solver, VariableInformation const& variableInformation) { boost::container::flat_set<uint_fast64_t> result; - for (auto labelVariablePair : variableInformation.labelToVariableIndexMap) { + for (auto const& labelVariablePair : variableInformation.labelToVariableMap) { bool labelTaken = solver.getBinaryValue(labelVariablePair.second); if (labelTaken) { @@ -911,10 +893,10 @@ namespace storm { std::map<uint_fast64_t, uint_fast64_t> result; for (auto state : stateInformation.relevantStates) { - std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); + std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin(); for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) { - bool choiceTaken = solver.getBinaryValue(*choiceVariableIndicesIterator); - ++choiceVariableIndicesIterator; + bool choiceTaken = solver.getBinaryValue(*choiceVariableIterator); + ++choiceVariableIterator; if (choiceTaken) { result.emplace_hint(result.end(), state, choice); } @@ -933,20 +915,20 @@ namespace storm { */ static std::pair<uint_fast64_t, double> getReachabilityProbability(storm::solver::LpSolver const& solver, storm::models::Mdp<T> const& labeledMdp, VariableInformation const& variableInformation) { uint_fast64_t selectedInitialState = 0; - for (auto initialStateVariableIndexPair : variableInformation.initialStateToChoiceVariableIndexMap) { - bool initialStateChosen = solver.getBinaryValue(initialStateVariableIndexPair.second); + for (auto const& initialStateVariablePair : variableInformation.initialStateToChoiceVariableMap) { + bool initialStateChosen = solver.getBinaryValue(initialStateVariablePair.second); if (initialStateChosen) { - selectedInitialState = initialStateVariableIndexPair.first; + selectedInitialState = initialStateVariablePair.first; break; } } - double reachabilityProbability = solver.getContinuousValue(variableInformation.virtualInitialStateVariableIndex); + double reachabilityProbability = solver.getContinuousValue(variableInformation.virtualInitialStateVariable); return std::make_pair(selectedInitialState, reachabilityProbability); } public: - + static boost::container::flat_set<uint_fast64_t> getMinimalLabelSet(storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, double probabilityThreshold, bool strictBound, bool checkThresholdFeasible = false, bool includeSchedulerCuts = false) { // (0) Check whether the MDP is indeed labeled. if (!labeledMdp.hasChoiceLabeling()) { diff --git a/src/solver/GlpkLpSolver.h b/src/solver/GlpkLpSolver.h index 1f6d7bd2a..0931e1ca5 100644 --- a/src/solver/GlpkLpSolver.h +++ b/src/solver/GlpkLpSolver.h @@ -68,7 +68,7 @@ namespace storm { virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; // Methods to add binary variables. - virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override; + virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; // Methods to incorporate recent changes. virtual void update() const override; @@ -184,7 +184,7 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } - virtual uint_fast64_t addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override { + virtual uint_fast64_t addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support."; } diff --git a/src/solver/GurobiLpSolver.h b/src/solver/GurobiLpSolver.h index e9eac0e85..90bef47eb 100644 --- a/src/solver/GurobiLpSolver.h +++ b/src/solver/GurobiLpSolver.h @@ -73,7 +73,7 @@ namespace storm { virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; // Methods to add binary variables. - virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override; + virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override; // Methods to incorporate recent changes. virtual void update() const override; @@ -176,7 +176,7 @@ namespace storm { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } - virtual uint_fast64_t addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override { + virtual uint_fast64_t addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override { throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support."; } diff --git a/src/solver/LpSolver.h b/src/solver/LpSolver.h index 5e7a5f76e..8c729a323 100644 --- a/src/solver/LpSolver.h +++ b/src/solver/LpSolver.h @@ -130,7 +130,7 @@ namespace storm { * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective * function. If omitted (or set to zero), the variable is irrelevant for the objective value. */ - virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) = 0; + virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) = 0; /*! * Updates the model to make the variables that have been declared since the last call to <code>update</code> From 686618e6e2d5174ae7f8d634dfd0bc648e0c3ad0 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Mon, 12 May 2014 21:57:52 +0200 Subject: [PATCH 15/15] Added missing header to (hopefully) fix MSVC problems. Former-commit-id: 0247ce1e355226573ff04c44f6512aa6c690192f --- src/storage/expressions/Valuation.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/storage/expressions/Valuation.h b/src/storage/expressions/Valuation.h index f7ab3c985..a09c4f07c 100644 --- a/src/storage/expressions/Valuation.h +++ b/src/storage/expressions/Valuation.h @@ -2,6 +2,7 @@ #define STORM_STORAGE_EXPRESSIONS_VALUATION_H_ #include <string> +#include <set> namespace storm { namespace expressions {