#include "src/modelchecker/csl/helper/HybridCtmcCslHelper.h" #include "src/modelchecker/csl/helper/SparseCtmcCslHelper.h" #include "src/modelchecker/prctl/helper/HybridDtmcPrctlHelper.h" #include "src/storage/dd/DdManager.h" #include "src/storage/dd/Add.h" #include "src/storage/dd/Bdd.h" #include "src/utility/macros.h" #include "src/utility/graph.h" #include "src/utility/constants.h" #include "src/models/symbolic/StandardRewardModel.h" #include "src/modelchecker/results/SymbolicQualitativeCheckResult.h" #include "src/modelchecker/results/SymbolicQuantitativeCheckResult.h" #include "src/modelchecker/results/HybridQuantitativeCheckResult.h" #include "src/modelchecker/results/ExplicitQuantitativeCheckResult.h" #include "src/exceptions/InvalidStateException.h" #include "src/exceptions/InvalidPropertyException.h" namespace storm { namespace modelchecker { namespace helper { template std::unique_ptr HybridCtmcCslHelper::computeReachabilityRewards(storm::models::symbolic::Ctmc const& model, storm::dd::Add const& rateMatrix, storm::dd::Add const& exitRateVector, RewardModelType const& rewardModel, storm::dd::Bdd const& targetStates, bool qualitative, storm::solver::LinearEquationSolverFactory const& linearEquationSolverFactory) { return HybridDtmcPrctlHelper::computeReachabilityRewards(model, computeProbabilityMatrix(model, rateMatrix, exitRateVector), rewardModel.divideStateRewardVector(exitRateVector), targetStates, qualitative, linearEquationSolverFactory); } template std::unique_ptr HybridCtmcCslHelper::computeNextProbabilities(storm::models::symbolic::Ctmc const& model, storm::dd::Add const& rateMatrix, storm::dd::Add const& exitRateVector, storm::dd::Bdd const& nextStates) { return HybridDtmcPrctlHelper::computeNextProbabilities(model, computeProbabilityMatrix(model, rateMatrix, exitRateVector), nextStates); } template std::unique_ptr HybridCtmcCslHelper::computeUntilProbabilities(storm::models::symbolic::Ctmc const& model, storm::dd::Add const& rateMatrix, storm::dd::Add const& exitRateVector, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, bool qualitative, storm::solver::LinearEquationSolverFactory const& linearEquationSolverFactory) { return HybridDtmcPrctlHelper::computeUntilProbabilities(model, computeProbabilityMatrix(model, rateMatrix, exitRateVector), phiStates, psiStates, qualitative, linearEquationSolverFactory); } template std::unique_ptr HybridCtmcCslHelper::computeBoundedUntilProbabilities(storm::models::symbolic::Ctmc const& model, storm::dd::Add const& rateMatrix, storm::dd::Add const& exitRateVector, storm::dd::Bdd const& phiStates, storm::dd::Bdd const& psiStates, bool qualitative, double lowerBound, double upperBound, storm::solver::LinearEquationSolverFactory const& linearEquationSolverFactory) { // If the time bounds are [0, inf], we rather call untimed reachability. if (storm::utility::isZero(lowerBound) && upperBound == storm::utility::infinity()) { return computeUntilProbabilities(model, rateMatrix, exitRateVector, phiStates, psiStates, qualitative, linearEquationSolverFactory); } // From this point on, we know that we have to solve a more complicated problem [t, t'] with either t != 0 // or t' != inf. // If we identify the states that have probability 0 of reaching the target states, we can exclude them from the // further computations. storm::dd::Bdd statesWithProbabilityGreater0 = storm::utility::graph::performProbGreater0(model, rateMatrix.notZero(), phiStates, psiStates); STORM_LOG_INFO("Found " << statesWithProbabilityGreater0.getNonZeroCount() << " states with probability greater 0."); storm::dd::Bdd statesWithProbabilityGreater0NonPsi = statesWithProbabilityGreater0 && !psiStates; STORM_LOG_INFO("Found " << statesWithProbabilityGreater0NonPsi.getNonZeroCount() << " 'maybe' states."); if (!statesWithProbabilityGreater0NonPsi.isZero()) { if (storm::utility::isZero(upperBound)) { // In this case, the interval is of the form [0, 0]. return std::unique_ptr(new SymbolicQuantitativeCheckResult(model.getReachableStates(), psiStates.template toAdd())); } else { if (storm::utility::isZero(lowerBound)) { // In this case, the interval is of the form [0, t]. // Note that this excludes [0, inf] since this is untimed reachability and we considered this case earlier. // Find the maximal rate of all 'maybe' states to take it as the uniformization rate. ValueType uniformizationRate = 1.02 * (statesWithProbabilityGreater0NonPsi.template toAdd() * exitRateVector).getMax(); STORM_LOG_THROW(uniformizationRate > 0, storm::exceptions::InvalidStateException, "The uniformization rate must be positive."); // Compute the uniformized matrix. storm::dd::Add uniformizedMatrix = computeUniformizedMatrix(model, rateMatrix, exitRateVector, statesWithProbabilityGreater0NonPsi, uniformizationRate); // Compute the vector that is to be added as a compensation for removing the absorbing states. storm::dd::Add b = (statesWithProbabilityGreater0NonPsi.template toAdd() * rateMatrix * psiStates.swapVariables(model.getRowColumnMetaVariablePairs()).template toAdd()).sumAbstract(model.getColumnVariables()) / model.getManager().getConstant(uniformizationRate); // Create an ODD for the translation to an explicit representation. storm::dd::Odd odd = statesWithProbabilityGreater0NonPsi.createOdd(); // Convert the symbolic parts to their explicit representation. storm::storage::SparseMatrix explicitUniformizedMatrix = uniformizedMatrix.toMatrix(odd, odd); std::vector explicitB = b.toVector(odd); // Finally compute the transient probabilities. std::vector values(statesWithProbabilityGreater0NonPsi.getNonZeroCount(), storm::utility::zero()); std::vector subresult = storm::modelchecker::helper::SparseCtmcCslHelper::computeTransientProbabilities(explicitUniformizedMatrix, &explicitB, upperBound, uniformizationRate, values, linearEquationSolverFactory); return std::unique_ptr(new HybridQuantitativeCheckResult(model.getReachableStates(), (psiStates || !statesWithProbabilityGreater0) && model.getReachableStates(), psiStates.template toAdd(), statesWithProbabilityGreater0NonPsi, odd, subresult)); } else if (upperBound == storm::utility::infinity()) { // In this case, the interval is of the form [t, inf] with t != 0. // Start by computing the (unbounded) reachability probabilities of reaching psi states while // staying in phi states. std::unique_ptr unboundedResult = computeUntilProbabilities(model, rateMatrix, exitRateVector, phiStates, psiStates, qualitative, linearEquationSolverFactory); // Compute the set of relevant states. storm::dd::Bdd relevantStates = statesWithProbabilityGreater0 && phiStates; // Filter the unbounded result such that it only contains values for the relevant states. unboundedResult->filter(SymbolicQualitativeCheckResult(model.getReachableStates(), relevantStates)); // Build an ODD for the relevant states. storm::dd::Odd odd = relevantStates.createOdd(); std::vector result; if (unboundedResult->isHybridQuantitativeCheckResult()) { std::unique_ptr explicitUnboundedResult = unboundedResult->asHybridQuantitativeCheckResult().toExplicitQuantitativeCheckResult(); result = std::move(explicitUnboundedResult->asExplicitQuantitativeCheckResult().getValueVector()); } else { STORM_LOG_THROW(unboundedResult->isSymbolicQuantitativeCheckResult(), storm::exceptions::InvalidStateException, "Expected check result of different type."); result = unboundedResult->asSymbolicQuantitativeCheckResult().getValueVector().toVector(odd); } // Determine the uniformization rate for the transient probability computation. ValueType uniformizationRate = 1.02 * (relevantStates.template toAdd() * exitRateVector).getMax(); // Compute the uniformized matrix. storm::dd::Add uniformizedMatrix = computeUniformizedMatrix(model, rateMatrix, exitRateVector, relevantStates, uniformizationRate); storm::storage::SparseMatrix explicitUniformizedMatrix = uniformizedMatrix.toMatrix(odd, odd); // Compute the transient probabilities. result = storm::modelchecker::helper::SparseCtmcCslHelper::computeTransientProbabilities(explicitUniformizedMatrix, nullptr, lowerBound, uniformizationRate, result, linearEquationSolverFactory); return std::unique_ptr(new HybridQuantitativeCheckResult(model.getReachableStates(), !relevantStates && model.getReachableStates(), model.getManager().template getAddZero(), relevantStates, odd, result)); } else { // In this case, the interval is of the form [t, t'] with t != 0 and t' != inf. if (lowerBound != upperBound) { // In this case, the interval is of the form [t, t'] with t != 0, t' != inf and t != t'. // Find the maximal rate of all 'maybe' states to take it as the uniformization rate. ValueType uniformizationRate = 1.02 * (statesWithProbabilityGreater0NonPsi.template toAdd() * exitRateVector).getMax(); STORM_LOG_THROW(uniformizationRate > 0, storm::exceptions::InvalidStateException, "The uniformization rate must be positive."); // Compute the (first) uniformized matrix. storm::dd::Add uniformizedMatrix = computeUniformizedMatrix(model, rateMatrix, exitRateVector, statesWithProbabilityGreater0NonPsi, uniformizationRate); // Create the one-step vector. storm::dd::Add b = (statesWithProbabilityGreater0NonPsi.template toAdd() * rateMatrix * psiStates.swapVariables(model.getRowColumnMetaVariablePairs()).template toAdd()).sumAbstract(model.getColumnVariables()) / model.getManager().getConstant(uniformizationRate); // Build an ODD for the relevant states and translate the symbolic parts to their explicit representation. storm::dd::Odd odd = statesWithProbabilityGreater0NonPsi.createOdd(); storm::storage::SparseMatrix explicitUniformizedMatrix = uniformizedMatrix.toMatrix(odd, odd); std::vector explicitB = b.toVector(odd); // Compute the transient probabilities. std::vector values(statesWithProbabilityGreater0NonPsi.getNonZeroCount(), storm::utility::zero()); std::vector subResult = storm::modelchecker::helper::SparseCtmcCslHelper::computeTransientProbabilities(explicitUniformizedMatrix, &explicitB, upperBound - lowerBound, uniformizationRate, values, linearEquationSolverFactory); // Transform the explicit result to a hybrid check result, so we can easily convert it to // a symbolic qualitative format. HybridQuantitativeCheckResult hybridResult(model.getReachableStates(), psiStates || (!statesWithProbabilityGreater0 && model.getReachableStates()), psiStates.template toAdd(), statesWithProbabilityGreater0NonPsi, odd, subResult); // Compute the set of relevant states. storm::dd::Bdd relevantStates = statesWithProbabilityGreater0 && phiStates; // Filter the unbounded result such that it only contains values for the relevant states. hybridResult.filter(SymbolicQualitativeCheckResult(model.getReachableStates(), relevantStates)); // Build an ODD for the relevant states. odd = relevantStates.createOdd(); std::unique_ptr explicitResult = hybridResult.toExplicitQuantitativeCheckResult(); std::vector newSubresult = std::move(explicitResult->asExplicitQuantitativeCheckResult().getValueVector()); // Then compute the transient probabilities of being in such a state after t time units. For this, // we must re-uniformize the CTMC, so we need to compute the second uniformized matrix. uniformizationRate = 1.02 * (relevantStates.template toAdd() * exitRateVector).getMax(); STORM_LOG_THROW(uniformizationRate > 0, storm::exceptions::InvalidStateException, "The uniformization rate must be positive."); // If the lower and upper bounds coincide, we have only determined the relevant states at this // point, but we still need to construct the starting vector. if (lowerBound == upperBound) { odd = relevantStates.createOdd(); newSubresult = psiStates.template toAdd().toVector(odd); } // Finally, we compute the second set of transient probabilities. uniformizedMatrix = computeUniformizedMatrix(model, rateMatrix, exitRateVector, relevantStates, uniformizationRate); explicitUniformizedMatrix = uniformizedMatrix.toMatrix(odd, odd); newSubresult = storm::modelchecker::helper::SparseCtmcCslHelper::computeTransientProbabilities(explicitUniformizedMatrix, nullptr, lowerBound, uniformizationRate, newSubresult, linearEquationSolverFactory); return std::unique_ptr(new HybridQuantitativeCheckResult(model.getReachableStates(), !relevantStates && model.getReachableStates(), model.getManager().template getAddZero(), relevantStates, odd, newSubresult)); } else { // In this case, the interval is of the form [t, t] with t != 0, t != inf. // Build an ODD for the relevant states. storm::dd::Odd odd = statesWithProbabilityGreater0.createOdd(); std::vector newSubresult = psiStates.template toAdd().toVector(odd); // Then compute the transient probabilities of being in such a state after t time units. For this, // we must re-uniformize the CTMC, so we need to compute the second uniformized matrix. ValueType uniformizationRate = 1.02 * (statesWithProbabilityGreater0.template toAdd() * exitRateVector).getMax(); STORM_LOG_THROW(uniformizationRate > 0, storm::exceptions::InvalidStateException, "The uniformization rate must be positive."); // Finally, we compute the second set of transient probabilities. storm::dd::Add uniformizedMatrix = computeUniformizedMatrix(model, rateMatrix, exitRateVector, statesWithProbabilityGreater0, uniformizationRate); storm::storage::SparseMatrix explicitUniformizedMatrix = uniformizedMatrix.toMatrix(odd, odd); newSubresult = storm::modelchecker::helper::SparseCtmcCslHelper::computeTransientProbabilities(explicitUniformizedMatrix, nullptr, lowerBound, uniformizationRate, newSubresult, linearEquationSolverFactory); return std::unique_ptr(new HybridQuantitativeCheckResult(model.getReachableStates(), !statesWithProbabilityGreater0 && model.getReachableStates(), model.getManager().template getAddZero(), statesWithProbabilityGreater0, odd, newSubresult)); } } } } else { return std::unique_ptr(new SymbolicQuantitativeCheckResult(model.getReachableStates(), psiStates.template toAdd())); } } template std::unique_ptr HybridCtmcCslHelper::computeInstantaneousRewards(storm::models::symbolic::Ctmc const& model, storm::dd::Add const& rateMatrix, storm::dd::Add const& exitRateVector, RewardModelType const& rewardModel, double timeBound, storm::solver::LinearEquationSolverFactory const& linearEquationSolverFactory) { // Only compute the result if the model has a state-based reward model. STORM_LOG_THROW(rewardModel.hasStateRewards(), storm::exceptions::InvalidPropertyException, "Missing reward model for formula. Skipping formula."); // Create ODD for the translation. storm::dd::Odd odd =model.getReachableStates().createOdd(); // Initialize result to state rewards of the model. std::vector result = rewardModel.getStateRewardVector().toVector(odd); // If the time-bound is not zero, we need to perform a transient analysis. if (timeBound > 0) { ValueType uniformizationRate = 1.02 * exitRateVector.getMax(); STORM_LOG_THROW(uniformizationRate > 0, storm::exceptions::InvalidStateException, "The uniformization rate must be positive."); storm::dd::Add uniformizedMatrix = computeUniformizedMatrix(model, rateMatrix, exitRateVector, model.getReachableStates(), uniformizationRate); storm::storage::SparseMatrix explicitUniformizedMatrix = uniformizedMatrix.toMatrix(odd, odd); result = storm::modelchecker::helper::SparseCtmcCslHelper::computeTransientProbabilities(explicitUniformizedMatrix, nullptr, timeBound, uniformizationRate, result, linearEquationSolverFactory); } return std::unique_ptr(new HybridQuantitativeCheckResult(model.getReachableStates(), model.getManager().getBddZero(), model.getManager().template getAddZero(), model.getReachableStates(), odd, result)); } template std::unique_ptr HybridCtmcCslHelper::computeCumulativeRewards(storm::models::symbolic::Ctmc const& model, storm::dd::Add const& rateMatrix, storm::dd::Add const& exitRateVector, RewardModelType const& rewardModel, double timeBound, storm::solver::LinearEquationSolverFactory const& linearEquationSolverFactory) { // Only compute the result if the model has a state-based reward model. STORM_LOG_THROW(!rewardModel.empty(), storm::exceptions::InvalidPropertyException, "Missing reward model for formula. Skipping formula."); // If the time bound is zero, the result is the constant zero vector. if (timeBound == 0) { return std::unique_ptr(new SymbolicQuantitativeCheckResult(model.getReachableStates(), model.getManager().template getAddZero())); } // Otherwise, we need to perform some computations. // Start with the uniformization. ValueType uniformizationRate = 1.02 * exitRateVector.getMax(); STORM_LOG_THROW(uniformizationRate > 0, storm::exceptions::InvalidStateException, "The uniformization rate must be positive."); // Create ODD for the translation. storm::dd::Odd odd = model.getReachableStates().createOdd(); // Compute the uniformized matrix. storm::dd::Add uniformizedMatrix = computeUniformizedMatrix(model, rateMatrix, exitRateVector, model.getReachableStates(), uniformizationRate); storm::storage::SparseMatrix explicitUniformizedMatrix = uniformizedMatrix.toMatrix(odd, odd); // Then compute the state reward vector to use in the computation. storm::dd::Add totalRewardVector = rewardModel.getTotalRewardVector(rateMatrix, model.getColumnVariables(), exitRateVector); std::vector explicitTotalRewardVector = totalRewardVector.toVector(odd); // Finally, compute the transient probabilities. std::vector result = storm::modelchecker::helper::SparseCtmcCslHelper::template computeTransientProbabilities(explicitUniformizedMatrix, nullptr, timeBound, uniformizationRate, explicitTotalRewardVector, linearEquationSolverFactory); return std::unique_ptr(new HybridQuantitativeCheckResult(model.getReachableStates(), model.getManager().getBddZero(), model.getManager().template getAddZero(), model.getReachableStates(), std::move(odd), std::move(result))); } template std::unique_ptr HybridCtmcCslHelper::computeLongRunAverageProbabilities(storm::models::symbolic::Ctmc const& model, storm::dd::Add const& rateMatrix, storm::dd::Add const& exitRateVector, storm::dd::Bdd const& psiStates, bool qualitative, storm::solver::LinearEquationSolverFactory const& linearEquationSolverFactory) { storm::dd::Add probabilityMatrix = computeProbabilityMatrix(model, rateMatrix, exitRateVector); // Create ODD for the translation. storm::dd::Odd odd = model.getReachableStates().createOdd(); storm::storage::SparseMatrix explicitProbabilityMatrix = probabilityMatrix.toMatrix(odd, odd); std::vector explicitExitRateVector = exitRateVector.toVector(odd); std::vector result = storm::modelchecker::helper::SparseCtmcCslHelper::computeLongRunAverageProbabilities(explicitProbabilityMatrix, psiStates.toVector(odd), &explicitExitRateVector, qualitative, linearEquationSolverFactory); return std::unique_ptr(new HybridQuantitativeCheckResult(model.getReachableStates(), model.getManager().getBddZero(), model.getManager().template getAddZero(), model.getReachableStates(), std::move(odd), std::move(result))); } template storm::dd::Add HybridCtmcCslHelper::computeUniformizedMatrix(storm::models::symbolic::Ctmc const& model, storm::dd::Add const& transitionMatrix, storm::dd::Add const& exitRateVector, storm::dd::Bdd const& maybeStates, ValueType uniformizationRate) { STORM_LOG_DEBUG("Computing uniformized matrix using uniformization rate " << uniformizationRate << "."); STORM_LOG_DEBUG("Keeping " << maybeStates.getNonZeroCount() << " rows."); // Cut all non-maybe rows/columns from the transition matrix. storm::dd::Add uniformizedMatrix = transitionMatrix * maybeStates.template toAdd() * maybeStates.swapVariables(model.getRowColumnMetaVariablePairs()).template toAdd(); // Now perform the uniformization. uniformizedMatrix = uniformizedMatrix / model.getManager().getConstant(uniformizationRate); storm::dd::Add diagonal = model.getRowColumnIdentity() * maybeStates.template toAdd(); storm::dd::Add diagonalOffset = diagonal; diagonalOffset -= diagonal * (exitRateVector / model.getManager().getConstant(uniformizationRate)); uniformizedMatrix += diagonalOffset; return uniformizedMatrix; } template storm::dd::Add HybridCtmcCslHelper::computeProbabilityMatrix(storm::models::symbolic::Ctmc const& model, storm::dd::Add const& rateMatrix, storm::dd::Add const& exitRateVector) { return rateMatrix / exitRateVector; } template class HybridCtmcCslHelper; template class HybridCtmcCslHelper; } } }