21#ifdef STORM_HAVE_HIGHS
25constexpr double highInfinity = std::numeric_limits<double>::infinity();
27struct HighsConstraintData {
28 std::vector<HighsInt> variableIndices;
29 std::vector<double> coefficients;
34template<
typename ValueType,
bool RawMode>
36 std::map<storm::expressions::Variable, uint64_t>
const& variableToIndexMap) {
37 HighsConstraintData result;
38 if constexpr (RawMode) {
40 result.relationType = constraint.relationType;
41 result.variableIndices.reserve(constraint.lhsVariableIndices.size());
42 result.coefficients.reserve(constraint.lhsCoefficients.size());
43 for (
auto const& variable : constraint.lhsVariableIndices) {
44 result.variableIndices.push_back(
static_cast<HighsInt
>(variable));
46 for (
auto const& coefficient : constraint.lhsCoefficients) {
50 STORM_LOG_THROW(constraint.isRelationalExpression(), storm::exceptions::InvalidArgumentException,
"Illegal constraint is not a relational expression.");
51 storm::expressions::LinearCoefficientVisitor::VariableCoefficients leftCoefficients =
52 storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(0));
53 storm::expressions::LinearCoefficientVisitor::VariableCoefficients rightCoefficients =
54 storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(1));
57 result.relationType = constraint.getBaseExpression().asBinaryRelationExpression().getRelationType();
58 result.variableIndices.reserve(leftCoefficients.
size());
59 result.coefficients.reserve(leftCoefficients.
size());
60 for (
auto const& variableCoefficientPair : leftCoefficients) {
61 auto variableIndexPair = variableToIndexMap.find(variableCoefficientPair.first);
62 result.variableIndices.push_back(
static_cast<HighsInt
>(variableIndexPair->second));
71template<
typename ValueType,
bool RawMode>
75 highs.setOptionValue(
"output_flag",
false);
78template<
typename ValueType,
bool RawMode>
81template<
typename ValueType,
bool RawMode>
82HighsLpSolver<ValueType, RawMode>::HighsLpSolver(OptimizationDirection
const& optDir) : HighsLpSolver(
"", optDir) {}
84template<
typename ValueType,
bool RawMode>
87template<
typename ValueType,
bool RawMode>
88HighsLpSolver<ValueType, RawMode>::~HighsLpSolver() {}
90template<
typename ValueType,
bool RawMode>
91double HighsLpSolver<ValueType, RawMode>::toHighsBound(
double value)
const {
92 if (!std::isfinite(value)) {
93 return value > 0 ? highs.getInfinity() : -highs.getInfinity();
98template<
typename ValueType,
bool RawMode>
99typename HighsLpSolver<ValueType, RawMode>::Variable HighsLpSolver<ValueType, RawMode>::addVariable(std::string
const& name, VariableType
const& type,
100 std::optional<ValueType>
const& lowerBound,
101 std::optional<ValueType>
const& upperBound,
102 ValueType objectiveFunctionCoefficient) {
104 if constexpr (RawMode) {
105 resultVar = nextVariableIndex;
107 resultVar = this->declareOrGetExpressionVariable(name, type);
108 STORM_LOG_ASSERT(variableToIndexMap.count(resultVar) == 0,
"Variable " << resultVar.getName() <<
" exists already in the model.");
109 this->variableToIndexMap.emplace(resultVar, nextVariableIndex);
114 if (type == VariableType::Binary) {
119 HighsStatus addColStatus =
121 STORM_LOG_THROW(addColStatus != HighsStatus::kError, storm::exceptions::InvalidStateException,
"Unable to add variable to HiGHS model.");
122 HighsInt column =
static_cast<HighsInt
>(nextVariableIndex);
124 if (type != VariableType::Continuous) {
125 HighsStatus integralityStatus = highs.changeColIntegrality(column, HighsVarType::kInteger);
126 STORM_LOG_THROW(integralityStatus != HighsStatus::kError, storm::exceptions::InvalidStateException,
"Unable to set integrality of HiGHS variable.");
129 highs.passColName(column, name);
132 variableBounds.emplace_back(lower, upper);
137template<
typename ValueType,
bool RawMode>
138void HighsLpSolver<ValueType, RawMode>::update()
const {
142template<
typename ValueType,
bool RawMode>
143void HighsLpSolver<ValueType, RawMode>::addConstraint(std::string
const&, Constraint
const& constraint) {
144 if constexpr (!RawMode) {
145 STORM_LOG_ASSERT(constraint.getManager() == this->getManager(),
"Constraint was not built over the proper variables.");
148 auto constraintData = createConstraintData<ValueType, RawMode>(constraint, this->variableToIndexMap);
151 switch (constraintData.relationType) {
154 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"HiGHS only supports nonstrict inequalities.");
157 lower = -highInfinity;
158 upper = constraintData.rhs;
161 lower = constraintData.rhs;
162 upper = highInfinity;
165 lower = constraintData.rhs;
166 upper = constraintData.rhs;
171 highs.addRow(toHighsBound(lower), toHighsBound(upper), constraintData.variableIndices.size(), constraintData.variableIndices.data(),
172 constraintData.coefficients.data());
175template<
typename ValueType,
bool RawMode>
176void HighsLpSolver<ValueType, RawMode>::addIndicatorConstraint(std::string
const&, Variable indicatorVariable,
bool indicatorValue,
177 Constraint
const& constraint) {
178 if constexpr (RawMode) {
179 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Indicator constraints not implemented in RawMode.");
181 STORM_LOG_ASSERT(this->variableToIndexMap.count(indicatorVariable) > 0,
"Indicator Variable " << indicatorVariable.getName() <<
" unknown to solver.");
182 STORM_LOG_ASSERT(indicatorVariable.hasIntegerType(),
"Indicator Variable " << indicatorVariable.getName() <<
" has unexpected type.");
183 STORM_LOG_ASSERT(constraint.getManager() == this->getManager(),
"Constraint was not built over the proper variables.");
185 auto constraintData = createConstraintData<ValueType, RawMode>(constraint, this->variableToIndexMap);
187 STORM_LOG_THROW(
false, storm::exceptions::NotSupportedException,
"HiGHS only supports nonstrict inequalities.");
192 double minActivity = 0.0;
193 double maxActivity = 0.0;
194 for (std::size_t i = 0;
i < constraintData.variableIndices.size(); ++
i) {
195 double coefficient = constraintData.coefficients[
i];
196 auto const& bounds = variableBounds[constraintData.variableIndices[
i]];
197 double lowerBound = bounds.first;
198 double upperBound = bounds.second;
199 if (coefficient > 0) {
200 maxActivity += coefficient * (std::isfinite(upperBound) ? upperBound : highInfinity);
201 minActivity += coefficient * (std::isfinite(lowerBound) ? lowerBound : -highInfinity);
202 }
else if (coefficient < 0) {
203 maxActivity += coefficient * (std::isfinite(lowerBound) ? lowerBound : -highInfinity);
204 minActivity += coefficient * (std::isfinite(upperBound) ? upperBound : highInfinity);
208 HighsInt indicatorIndex =
static_cast<HighsInt
>(this->variableToIndexMap.at(indicatorVariable));
210 auto addIndicatorRow = [
this, &constraintData, indicatorIndex](
double mCoefficient,
double newRhs,
bool isLessEqual) {
211 std::vector<HighsInt> variableIndices = constraintData.variableIndices;
212 std::vector<double> coefficients = constraintData.coefficients;
213 variableIndices.push_back(indicatorIndex);
214 coefficients.push_back(mCoefficient);
215 double lower = -highInfinity;
216 double upper = highInfinity;
222 highs.addRow(toHighsBound(lower), toHighsBound(upper), variableIndices.size(), variableIndices.data(), coefficients.data());
225 auto addSingleIndicatorConstraint = [&](
bool isLessEqual,
bool indicatorValue) {
228 m = maxActivity - constraintData.rhs;
230 m = constraintData.rhs - minActivity;
232 STORM_LOG_THROW(std::isfinite(m), storm::exceptions::NotSupportedException,
233 "Indicator constraints over unbounded variables are not supported by the HiGHS solver.");
234 double mCoefficient = (indicatorValue == isLessEqual) ? m : -m;
235 double newRhs = constraintData.rhs;
236 if (indicatorValue) {
237 newRhs += isLessEqual ? m : -m;
239 addIndicatorRow(mCoefficient, newRhs, isLessEqual);
242 switch (constraintData.relationType) {
244 addSingleIndicatorConstraint(
true, indicatorValue);
247 addSingleIndicatorConstraint(
false, indicatorValue);
250 addSingleIndicatorConstraint(
true, indicatorValue);
251 addSingleIndicatorConstraint(
false, indicatorValue);
259template<
typename ValueType,
bool RawMode>
260void HighsLpSolver<ValueType, RawMode>::optimize()
const {
265 highs.changeObjectiveSense(this->getOptimizationDirection() == OptimizationDirection::Minimize ? ObjSense::kMinimize : ObjSense::kMaximize);
268 HighsStatus status = highs.run();
269 STORM_LOG_THROW(status != HighsStatus::kError, storm::exceptions::InvalidStateException,
"Unable to optimize the model with HiGHS.");
271 this->currentModelHasBeenOptimized =
true;
272 modelStatus = highs.getModelStatus();
275template<
typename ValueType,
bool RawMode>
276bool HighsLpSolver<ValueType, RawMode>::isInfeasible()
const {
277 STORM_LOG_THROW(this->currentModelHasBeenOptimized, storm::exceptions::InvalidStateException,
278 "Illegal call to HighsLpSolver<ValueType, RawMode>::isInfeasible: model has not been optimized.");
279 return modelStatus == HighsModelStatus::kInfeasible;
282template<
typename ValueType,
bool RawMode>
283bool HighsLpSolver<ValueType, RawMode>::isUnbounded()
const {
284 STORM_LOG_THROW(this->currentModelHasBeenOptimized, storm::exceptions::InvalidStateException,
285 "Illegal call to HighsLpSolver<ValueType, RawMode>::isUnbounded: model has not been optimized.");
286 return modelStatus == HighsModelStatus::kUnbounded || modelStatus == HighsModelStatus::kUnboundedOrInfeasible;
289template<
typename ValueType,
bool RawMode>
290bool HighsLpSolver<ValueType, RawMode>::isOptimal()
const {
291 if (!this->currentModelHasBeenOptimized) {
294 return modelStatus == HighsModelStatus::kOptimal;
297template<
typename ValueType,
bool RawMode>
298ValueType HighsLpSolver<ValueType, RawMode>::getContinuousValue(Variable
const& variable)
const {
299 STORM_LOG_THROW(this->isOptimal(), storm::exceptions::InvalidAccessException,
300 "Unable to get HiGHS solution from a model that has not been solved optimally.");
302 uint64_t variableIndex;
303 if constexpr (RawMode) {
304 variableIndex = variable;
306 STORM_LOG_THROW(variableToIndexMap.count(variable) != 0, storm::exceptions::InvalidAccessException,
307 "Accessing value of unknown variable '" << variable.getName() <<
"'.");
308 variableIndex = variableToIndexMap.at(variable);
310 STORM_LOG_ASSERT(variableIndex < nextVariableIndex,
"Variable Index exceeds highest value.");
315template<
typename ValueType,
bool RawMode>
316int_fast64_t HighsLpSolver<ValueType, RawMode>::getIntegerValue(Variable
const& variable)
const {
317 STORM_LOG_THROW(this->isOptimal(), storm::exceptions::InvalidAccessException,
318 "Unable to get HiGHS solution from a model that has not been solved optimally.");
320 uint64_t variableIndex;
321 if constexpr (RawMode) {
322 variableIndex = variable;
324 STORM_LOG_THROW(variableToIndexMap.count(variable) != 0, storm::exceptions::InvalidAccessException,
325 "Accessing value of unknown variable '" << variable.getName() <<
"'.");
326 variableIndex = variableToIndexMap.at(variable);
328 STORM_LOG_ASSERT(variableIndex < nextVariableIndex,
"Variable Index exceeds highest value.");
330 return std::llround(highs.getSolution().col_value[variableIndex]);
333template<
typename ValueType,
bool RawMode>
334bool HighsLpSolver<ValueType, RawMode>::getBinaryValue(Variable
const& variable)
const {
335 STORM_LOG_THROW(this->isOptimal(), storm::exceptions::InvalidAccessException,
336 "Unable to get HiGHS solution from a model that has not been solved optimally.");
338 uint64_t variableIndex;
339 if constexpr (RawMode) {
340 variableIndex = variable;
342 STORM_LOG_THROW(variableToIndexMap.count(variable) != 0, storm::exceptions::InvalidAccessException,
343 "Accessing value of unknown variable '" << variable.getName() <<
"'.");
344 variableIndex = variableToIndexMap.at(variable);
346 STORM_LOG_ASSERT(variableIndex < nextVariableIndex,
"Variable Index exceeds highest value.");
348 return highs.getSolution().col_value[variableIndex] > 0.5;
351template<
typename ValueType,
bool RawMode>
352ValueType HighsLpSolver<ValueType, RawMode>::getObjectiveValue()
const {
353 STORM_LOG_THROW(this->isOptimal(), storm::exceptions::InvalidAccessException,
354 "Unable to get HiGHS solution from a model that has not been solved optimally.");
358template<
typename ValueType,
bool RawMode>
359void HighsLpSolver<ValueType, RawMode>::writeModelToFile(std::string
const& filename)
const {
360 HighsStatus status = highs.writeModel(filename);
361 STORM_LOG_THROW(status != HighsStatus::kError, storm::exceptions::InvalidStateException,
"Unable to write HiGHS model to file '" << filename <<
"'.");
364template<
typename ValueType,
bool RawMode>
365void HighsLpSolver<ValueType, RawMode>::push() {
366 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Push/Pop not supported for HiGHS.");
369template<
typename ValueType,
bool RawMode>
370void HighsLpSolver<ValueType, RawMode>::pop() {
371 STORM_LOG_THROW(
false, storm::exceptions::NotImplementedException,
"Push/Pop not supported for HiGHS.");
374template<
typename ValueType,
bool RawMode>
375void HighsLpSolver<ValueType, RawMode>::setMaximalMILPGap(ValueType
const& gap,
bool relative) {
377 HighsStatus status = relative ? highs.setOptionValue(
"mip_rel_gap", gapAsDouble) : highs.setOptionValue(
"mip_abs_gap", gapAsDouble);
378 STORM_LOG_THROW(status != HighsStatus::kError, storm::exceptions::InvalidStateException,
"Unable to set HiGHS MILP gap.");
381template<
typename ValueType,
bool RawMode>
382ValueType HighsLpSolver<ValueType, RawMode>::getMILPGap(
bool relative)
const {
383 auto const& info = highs.getInfo();
385 double relativeGap = info.mip_gap / 100.0;
396template<
typename ValueType,
bool RawMode>
399 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
400 "version of storm with HiGHS support.");
403template<
typename ValueType,
bool RawMode>
406 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
407 "version of storm with HiGHS support.");
410template<
typename ValueType,
bool RawMode>
413 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
414 "version of storm with HiGHS support.");
417template<
typename ValueType,
bool RawMode>
420 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
421 "version of storm with HiGHS support.");
424template<
typename ValueType,
bool RawMode>
427template<
typename ValueType,
bool RawMode>
429 std::optional<ValueType>
const&,
430 std::optional<ValueType>
const&, ValueType) {
432 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
433 "version of storm with HiGHS support.");
436template<
typename ValueType,
bool RawMode>
439 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
440 "version of storm with HiGHS support.");
443template<
typename ValueType,
bool RawMode>
446 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
447 "version of storm with HiGHS support.");
450template<
typename ValueType,
bool RawMode>
453 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
454 "version of storm with HiGHS support.");
457template<
typename ValueType,
bool RawMode>
460 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
461 "version of storm with HiGHS support.");
464template<
typename ValueType,
bool RawMode>
467 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
468 "version of storm with HiGHS support.");
471template<
typename ValueType,
bool RawMode>
474 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
475 "version of storm with HiGHS support.");
478template<
typename ValueType,
bool RawMode>
481 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
482 "version of storm with HiGHS support.");
485template<
typename ValueType,
bool RawMode>
488 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
489 "version of storm with HiGHS support.");
492template<
typename ValueType,
bool RawMode>
495 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
496 "version of storm with HiGHS support.");
499template<
typename ValueType,
bool RawMode>
502 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
503 "version of storm with HiGHS support.");
506template<
typename ValueType,
bool RawMode>
509 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
510 "version of storm with HiGHS support.");
513template<
typename ValueType,
bool RawMode>
516 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
517 "version of storm with HiGHS support.");
520template<
typename ValueType,
bool RawMode>
523 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
524 "version of storm with HiGHS support.");
527template<
typename ValueType,
bool RawMode>
530 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
531 "version of storm with HiGHS support.");
534template<
typename ValueType,
bool RawMode>
537 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
538 "version of storm with HiGHS support.");
541template<
typename ValueType,
bool RawMode>
544 "This version of storm was compiled without support for HiGHS. Yet, a method was called that requires this support. Please choose a "
545 "version of storm with HiGHS support.");
A class that implements the LpSolver interface using HiGHS.
virtual void optimize() const override
Optimizes the LP problem previously constructed.
virtual void update() const override
Updates the model to make the variables that have been declared since the last call to update usable.
HighsLpSolver(std::string const &name, OptimizationDirection const &optDir)
Constructs a solver with the given name and model sense.
virtual void addConstraint(std::string const &name, Constraint const &constraint) override
Adds a the given constraint to the LP problem.
virtual void setMaximalMILPGap(ValueType const &gap, bool relative) override
Specifies the maximum difference between lower- and upper objective bounds that triggers termination.
typename LpSolver< ValueType, RawMode >::Constraint Constraint
virtual void pop() override
Pops a backtracking point from the solver's stack.
virtual int_fast64_t getIntegerValue(Variable const &variable) const override
Retrieves the value of the integer variable with the given name.
virtual Variable addVariable(std::string const &name, VariableType const &type, std::optional< ValueType > const &lowerBound=std::nullopt, std::optional< ValueType > const &upperBound=std::nullopt, ValueType objectiveFunctionCoefficient=0) override
typename LpSolver< ValueType, RawMode >::Variable Variable
virtual ValueType getMILPGap(bool relative) const override
Returns the obtained gap after a call to optimize().
virtual void writeModelToFile(std::string const &filename) const override
Writes the current LP problem to the given file.
virtual void addIndicatorConstraint(std::string const &name, Variable indicatorVariable, bool indicatorValue, Constraint const &constraint) override
Adds the given indicator constraint to the LP problem: "If indicatorVariable == indicatorValue,...
HighsLpSolver()
Constructs a solver without a name.
virtual void push() override
Pushes a backtracking point on the solver's stack.
typename LpSolver< ValueType, RawMode >::VariableType VariableType
virtual bool isOptimal() const override
Retrieves whether the model was found to be optimal, i.e.
virtual bool getBinaryValue(Variable const &variable) const override
Retrieves the value of the binary variable with the given name.
virtual bool isInfeasible() const override
Retrieves whether the model was found to be infeasible.
virtual ValueType getContinuousValue(Variable const &variable) const override
Retrieves the value of the continuous variable with the given name.
virtual ValueType getObjectiveValue() const override
Retrieves the value of the objective function.
virtual bool isUnbounded() const override
Retrieves whether the model was found to be infeasible.
An interface that captures the functionality of an LP solver.
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
SFTBDDChecker::ValueType ValueType
RelationType
An enum type specifying the different relations applicable.
ValueType abs(ValueType const &number)
TargetType convertNumber(SourceType const &number)
void separateVariablesFromConstantPart(VariableCoefficients &rhs)
Brings all variables of the right-hand side coefficients to the left-hand side by negating them and m...
double getConstantPart() const