|
|
@ -256,7 +256,12 @@ namespace storm { |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (method == MinMaxMethod::OptimisticValueIteration) { |
|
|
|
// OptimisticValueIteration does not have any requirements
|
|
|
|
// OptimisticValueIteration always requires lower bounds and a unique solution.
|
|
|
|
if (!this->hasUniqueSolution()) { |
|
|
|
requirements.requireUniqueSolution(); |
|
|
|
} |
|
|
|
requirements.requireLowerBounds(); |
|
|
|
|
|
|
|
} else if (method == MinMaxMethod::IntervalIteration) { |
|
|
|
// Interval iteration requires a unique solution and lower+upper bounds
|
|
|
|
if (!this->hasUniqueSolution()) { |
|
|
@ -353,6 +358,45 @@ namespace storm { |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
template<typename ValueType> |
|
|
|
ValueType computeMaxAbsDiff(std::vector<ValueType> const& allOldValues, std::vector<ValueType> const& allNewValues) { |
|
|
|
ValueType result = storm::utility::zero<ValueType>(); |
|
|
|
for (uint64_t i = 0; i < allOldValues.size(); ++i) { |
|
|
|
result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allNewValues[i] - allOldValues[i])); |
|
|
|
} |
|
|
|
return result; |
|
|
|
} |
|
|
|
template<typename ValueType> |
|
|
|
ValueType computeMaxRelDiff(std::vector<ValueType> const& allOldValues, std::vector<ValueType> const& allNewValues) { |
|
|
|
ValueType result = storm::utility::zero<ValueType>(); |
|
|
|
for (uint64_t i = 0; i < allOldValues.size(); ++i) { |
|
|
|
STORM_LOG_ASSERT(!storm::utility::isZero(allNewValues[i]) || storm::utility::isZero(allOldValues[i]), "Unexpected entry in iteration vector."); |
|
|
|
if (!storm::utility::isZero(allNewValues[i])) { |
|
|
|
result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allNewValues[i] - allOldValues[i]) / allNewValues[i]); |
|
|
|
} |
|
|
|
} |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
template<typename ValueType> |
|
|
|
ValueType updateIterationPrecision(std::vector<ValueType> const& currentX, std::vector<ValueType> const& newX, bool const& relative) { |
|
|
|
if (relative) { |
|
|
|
return computeMaxRelDiff(newX, currentX) / storm::utility::convertNumber<ValueType>(2); |
|
|
|
} else { |
|
|
|
return computeMaxAbsDiff(newX, currentX) / storm::utility::convertNumber<ValueType>(2); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
template<typename ValueType> |
|
|
|
void guessUpperBoundRelative(std::vector<ValueType> const& x, std::vector<ValueType> &target, ValueType const& relativeBoundGuessingScaler) { |
|
|
|
storm::utility::vector::applyPointwise<ValueType, ValueType>(x, target, [&relativeBoundGuessingScaler] (ValueType const& argument) -> ValueType { return argument * relativeBoundGuessingScaler; }); |
|
|
|
} |
|
|
|
|
|
|
|
template<typename ValueType> |
|
|
|
void guessUpperBoundAbsolute(std::vector<ValueType> const& x, std::vector<ValueType> &target, ValueType const& precision) { |
|
|
|
storm::utility::vector::applyPointwise<ValueType, ValueType>(x, target, [&precision] (ValueType const& argument) -> ValueType { return argument + precision; }); |
|
|
|
} |
|
|
|
|
|
|
|
template<typename ValueType> |
|
|
|
bool IterativeMinMaxLinearEquationSolver<ValueType>::solveEquationsOptimisticValueIteration(Environment const& env, OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const { |
|
|
|
|
|
|
@ -380,29 +424,28 @@ namespace storm { |
|
|
|
// Relative errors
|
|
|
|
bool relative = env.solver().minMax().getRelativeTerminationCriterion(); |
|
|
|
|
|
|
|
// x has to start with a lower bound.
|
|
|
|
this->createLowerBoundsVector(x); |
|
|
|
|
|
|
|
std::vector<ValueType> *currentX = &x; |
|
|
|
std::vector<ValueType> *newX = auxiliaryRowGroupVector.get(); |
|
|
|
std::vector<ValueType> currentUpperBound(currentX->size()); |
|
|
|
std::vector<ValueType> newUpperBound; |
|
|
|
std::vector<ValueType> ubDiffV(newX->size()); |
|
|
|
std::vector<ValueType> boundsDiffV(currentX->size()); |
|
|
|
std::vector<ValueType> newUpperBound(x.size()); |
|
|
|
|
|
|
|
ValueType two = storm::utility::convertNumber<ValueType>(2.0); |
|
|
|
ValueType precision = storm::utility::convertNumber<ValueType>(env.solver().minMax().getPrecision()); |
|
|
|
ValueType relativeBoundGuessingScaler = (storm::utility::one<ValueType>() + precision); |
|
|
|
ValueType doublePrecision = precision * two; |
|
|
|
ValueType terminationPrecision = doublePrecision; |
|
|
|
ValueType iterationPrecision = precision; |
|
|
|
|
|
|
|
SolverStatus status = SolverStatus::InProgress; |
|
|
|
this->startMeasureProgress(); |
|
|
|
|
|
|
|
while (status == SolverStatus::InProgress && overallIterations <= maxOverallIterations) { |
|
|
|
while (status == SolverStatus::InProgress && overallIterations < maxOverallIterations) { |
|
|
|
|
|
|
|
// Perform value iteration until convergence
|
|
|
|
++valueIterationInvocations; |
|
|
|
ValueIterationResult result = performValueIteration(env, dir, currentX, newX, b, iterationPrecision, relative, guarantee, overallIterations, env.solver().minMax().getMaximalNumberOfIterations(), multiplicationStyle); |
|
|
|
|
|
|
|
lastValueIterationIterations = result.iterations; |
|
|
|
overallIterations += result.iterations; |
|
|
|
|
|
|
@ -411,23 +454,21 @@ namespace storm { |
|
|
|
} else { |
|
|
|
currentVerificationIterations = 0; |
|
|
|
|
|
|
|
currentUpperBound = *currentX; |
|
|
|
if (relative) { |
|
|
|
guessUpperBoundRelative(*currentX, currentUpperBound, relativeBoundGuessingScaler); |
|
|
|
} else { |
|
|
|
guessUpperBoundAbsolute(*currentX, currentUpperBound, precision); |
|
|
|
} |
|
|
|
|
|
|
|
newUpperBound = currentUpperBound; |
|
|
|
|
|
|
|
// TODO: More efficient check for verification iterations
|
|
|
|
while (status == SolverStatus::InProgress && overallIterations <= maxOverallIterations && (currentVerificationIterations / 10) < lastValueIterationIterations) { |
|
|
|
bool cancelGuess = false; |
|
|
|
while (status == SolverStatus::InProgress && overallIterations < maxOverallIterations && !cancelGuess) { |
|
|
|
// Perform value iteration stepwise for lower bound and guessed upper bound
|
|
|
|
|
|
|
|
// Lower and upper bound iteration
|
|
|
|
// Compute x' = min/max(A*x + b).
|
|
|
|
if (useGaussSeidelMultiplication) { |
|
|
|
// Copy over the current vectors so we can modify them in-place.
|
|
|
|
// This is necessary as we want to compare the new values with the current ones.
|
|
|
|
*newX = *currentX; |
|
|
|
newUpperBound = currentUpperBound; |
|
|
|
// Do the calculation
|
|
|
@ -438,55 +479,75 @@ namespace storm { |
|
|
|
multiplier.multiplyAndReduce(env, dir, currentUpperBound, &b, newUpperBound); |
|
|
|
} |
|
|
|
|
|
|
|
// Set iteration precision beforehand
|
|
|
|
// Calculate the maximum difference between the old and new iteration
|
|
|
|
iterationPrecision = computeMaxAbsDiff(*newX, *currentX, this->getRelevantValues()); |
|
|
|
|
|
|
|
// Calculate difference vector of upper bound and x
|
|
|
|
storm::utility::vector::subtractVectors<ValueType>(newUpperBound, *newX, boundsDiffV); |
|
|
|
// Calculate difference vector of new and old upper bound
|
|
|
|
storm::utility::vector::subtractVectors<ValueType>(currentUpperBound, newUpperBound, ubDiffV); |
|
|
|
bool newUpperBoundAlwaysHigher = true; |
|
|
|
bool newUpperBoundAlwaysLowerEqual = true; |
|
|
|
bool valuesCrossed = false; |
|
|
|
ValueType maxBoundDiff = storm::utility::zero<ValueType>(); |
|
|
|
for (uint64_t i = 0; i < x.size(); ++i) { |
|
|
|
if (newUpperBound[i] < currentUpperBound[i]) { |
|
|
|
newUpperBoundAlwaysHigher = false; |
|
|
|
} else if (newUpperBound[i] != currentUpperBound[i]) { |
|
|
|
newUpperBoundAlwaysLowerEqual = false; |
|
|
|
} |
|
|
|
if (newUpperBound[i] < (*newX)[i]) { |
|
|
|
valuesCrossed = true; |
|
|
|
break; |
|
|
|
} else { |
|
|
|
maxBoundDiff = std::max<ValueType>(maxBoundDiff, newUpperBound[i] - (*newX)[i]); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Update bounds
|
|
|
|
std::swap(currentX, newX); |
|
|
|
std::swap(currentUpperBound, newUpperBound); |
|
|
|
|
|
|
|
if (!storm::utility::vector::hasPositiveEntry(ubDiffV)) { |
|
|
|
if (newUpperBoundAlwaysHigher) { |
|
|
|
iterationPrecision = updateIterationPrecision(*currentX, *newX, relative); |
|
|
|
// Not all values moved up or stayed the same
|
|
|
|
// If we have a single fixed point, we can safely set the new lower bound, to the wrongly guessed upper bound
|
|
|
|
if (this->hasUniqueSolution()) { |
|
|
|
*currentX = currentUpperBound; |
|
|
|
} |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
if (storm::utility::vector::hasNegativeEntry(boundsDiffV)) { |
|
|
|
// Values crossed
|
|
|
|
} else if (valuesCrossed) { |
|
|
|
iterationPrecision = updateIterationPrecision(*currentX, *newX, relative); |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// All values moved down or stayed the same and we have a maximum difference of twice the requested precision
|
|
|
|
// We can safely use twice the requested precision, as we calculate the center of both vectors
|
|
|
|
// We can use max_if instead of computeMaxAbsDiff, as x is definitely a lower bound and ub is larger in all elements
|
|
|
|
if (!storm::utility::vector::hasNegativeEntry(ubDiffV)) { |
|
|
|
} else if (newUpperBoundAlwaysLowerEqual) { |
|
|
|
// All values moved down or stayed the same and we have a maximum difference of twice the requested precision
|
|
|
|
// We can safely use twice the requested precision, as we calculate the center of both vectors
|
|
|
|
// We can use max_if instead of computeMaxAbsDiff, as x is definitely a lower bound and ub is larger in all elements
|
|
|
|
// Recalculate terminationPrecision if relative error requested
|
|
|
|
// TODO: Is here min or max required?
|
|
|
|
if (relative) { |
|
|
|
terminationPrecision = doublePrecision * storm::utility::vector::min_if(*currentX, this->getRelevantValues()); |
|
|
|
bool reachedPrecision = true; |
|
|
|
for (auto const& valueIndex : this->hasRelevantValues() ? this->getRelevantValues() : storm::storage::BitVector(x.size(), true)) { |
|
|
|
ValueType absDiff = currentUpperBound[valueIndex] - (*currentX)[valueIndex]; |
|
|
|
if (relative) { |
|
|
|
if (absDiff > doublePrecision * (*currentX)[valueIndex]) { |
|
|
|
reachedPrecision = false; |
|
|
|
break; |
|
|
|
} |
|
|
|
} else { |
|
|
|
if (absDiff > doublePrecision) { |
|
|
|
reachedPrecision = false; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (storm::utility::vector::max_if(boundsDiffV, this->getRelevantValues()) < terminationPrecision) { |
|
|
|
if (reachedPrecision) { |
|
|
|
// Calculate the center of both vectors and store it in currentX
|
|
|
|
storm::utility::vector::applyPointwise<ValueType, ValueType, ValueType>(*currentX, currentUpperBound, *currentX, [&two] (ValueType const& a, ValueType const& b) -> ValueType { return (a + b) / two; }); |
|
|
|
status = SolverStatus::Converged; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ((currentVerificationIterations / 10) >= lastValueIterationIterations) { |
|
|
|
cancelGuess = true; |
|
|
|
iterationPrecision = updateIterationPrecision(*currentX, *newX, relative); |
|
|
|
} |
|
|
|
|
|
|
|
++overallIterations; |
|
|
|
++currentVerificationIterations; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
iterationPrecision = iterationPrecision / two; |
|
|
|
} |
|
|
|
|
|
|
|
if (overallIterations > maxOverallIterations) { |
|
|
@ -514,16 +575,6 @@ namespace storm { |
|
|
|
return status == SolverStatus::Converged || status == SolverStatus::TerminatedEarly; |
|
|
|
} |
|
|
|
|
|
|
|
template<typename ValueType> |
|
|
|
void IterativeMinMaxLinearEquationSolver<ValueType>::guessUpperBoundRelative(std::vector<ValueType> &x, std::vector<ValueType> &target, ValueType const& relativeBoundGuessingScaler) const { |
|
|
|
storm::utility::vector::scaleVectorInPlace<ValueType, ValueType>(target, relativeBoundGuessingScaler); |
|
|
|
} |
|
|
|
|
|
|
|
template<typename ValueType> |
|
|
|
void IterativeMinMaxLinearEquationSolver<ValueType>::guessUpperBoundAbsolute(std::vector<ValueType> &x, std::vector<ValueType> &target, ValueType const& precision) const { |
|
|
|
storm::utility::vector::applyPointwise<ValueType, ValueType>(x, target, [&precision] (ValueType const& argument) -> ValueType { return argument + precision; }); |
|
|
|
} |
|
|
|
|
|
|
|
template<typename ValueType> |
|
|
|
bool IterativeMinMaxLinearEquationSolver<ValueType>::solveEquationsValueIteration(Environment const& env, OptimizationDirection dir, std::vector<ValueType>& x, std::vector<ValueType> const& b) const { |
|
|
|
if (!this->multiplierA) { |
|
|
|