Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SparseParametricModelSimplifier.cpp
Go to the documentation of this file.
2#include <memory>
3#include <type_traits>
4
15
16namespace storm {
17namespace transformer {
18
19namespace {
20
31template<typename SparseModelType>
32std::shared_ptr<SparseModelType> eliminateConstantDeterministicStates(SparseModelType const& model, bool preserveParametricTransitions,
33 storm::storage::BitVector const& consideredStates,
34 std::optional<std::string> const& rewardModelName) {
35 storm::storage::SparseMatrix<typename SparseModelType::ValueType> const& sparseMatrix = model.getTransitionMatrix();
36 auto backwardsSparseMatrix = sparseMatrix.transpose();
37
38 // get the action-based reward values
39 std::vector<typename SparseModelType::ValueType> actionRewards;
40 if (rewardModelName) {
41 actionRewards = model.getRewardModel(*rewardModelName).getTotalRewardVector(sparseMatrix);
42 } else {
43 actionRewards = std::vector<typename SparseModelType::ValueType>(model.getTransitionMatrix().getRowCount(),
45 }
46
47 // Find the states that are to be eliminated
48 storm::storage::BitVector selectedStates = consideredStates;
49 for (uint64_t state : consideredStates) {
50 if (sparseMatrix.getRowGroupSize(state) == 1 &&
51 (!rewardModelName.has_value() || storm::utility::isConstant(actionRewards[sparseMatrix.getRowGroupIndices()[state]]))) {
52 for (auto const& entry : sparseMatrix.getRowGroup(state)) {
53 if (!storm::utility::isConstant(entry.getValue())) {
54 selectedStates.set(state, false);
55 break;
56 }
57 }
58 if (preserveParametricTransitions) {
59 for (auto const& entry : backwardsSparseMatrix.getRowGroup(state)) {
60 if (!storm::utility::isConstant(entry.getValue())) {
61 selectedStates.set(state, false);
62 break;
63 }
64 }
65 }
66 } else {
67 selectedStates.set(state, false);
68 }
69 }
70
71 // invoke elimination and obtain resulting transition matrix
72 storm::storage::FlexibleSparseMatrix<typename SparseModelType::ValueType> flexibleMatrix(sparseMatrix);
73 storm::storage::FlexibleSparseMatrix<typename SparseModelType::ValueType> flexibleBackwardTransitions(backwardsSparseMatrix, true);
74 storm::solver::stateelimination::NondeterministicModelStateEliminator<typename SparseModelType::ValueType> stateEliminator(
75 flexibleMatrix, flexibleBackwardTransitions, actionRewards);
76 for (uint64_t state : selectedStates) {
77 stateEliminator.eliminateState(state, true);
78 }
79 selectedStates.complement();
80 auto keptRows = sparseMatrix.getRowFilter(selectedStates);
81 storm::storage::SparseMatrix<typename SparseModelType::ValueType> newTransitionMatrix = flexibleMatrix.createSparseMatrix(keptRows, selectedStates);
82
83 // obtain the reward model for the resulting system
84 std::unordered_map<std::string, typename SparseModelType::RewardModelType> rewardModels;
85 if (rewardModelName) {
86 storm::utility::vector::filterVectorInPlace(actionRewards, keptRows);
87 rewardModels.insert(std::make_pair(*rewardModelName, typename SparseModelType::RewardModelType(std::nullopt, std::move(actionRewards))));
88 }
89
90 return std::make_shared<SparseModelType>(std::move(newTransitionMatrix), model.getStateLabeling().getSubLabeling(selectedStates), std::move(rewardModels));
91}
92
103template<typename SparseModelType>
104std::shared_ptr<SparseModelType> eliminateNeutralEndComponents(SparseModelType const& model, storm::storage::BitVector const& ignoredStates,
105 std::optional<std::string> const& rewardModelName) {
106 // Get the actions that can be part of an EC
107 storm::storage::BitVector possibleECActions(model.getNumberOfChoices(), true);
108 for (auto state : ignoredStates) {
109 for (uint64_t const actionIndex : model.getTransitionMatrix().getRowGroupIndices(state)) {
110 possibleECActions.set(actionIndex, false);
111 }
112 }
113
114 // Get the action-based reward values and unselect non-zero reward actions
115 std::vector<typename SparseModelType::ValueType> actionRewards;
116 if (rewardModelName) {
117 actionRewards = model.getRewardModel(*rewardModelName).getTotalRewardVector(model.getTransitionMatrix());
118 uint64_t actionIndex = 0;
119 for (auto const& actionReward : actionRewards) {
120 if (!storm::utility::isZero(actionReward)) {
121 possibleECActions.set(actionIndex, false);
122 }
123 ++actionIndex;
124 }
125 }
126
127 // Invoke EC Elimination
129 model.getTransitionMatrix(), storm::storage::BitVector(model.getNumberOfStates(), true), possibleECActions,
130 storm::storage::BitVector(model.getNumberOfStates(), false));
131
132 // obtain the reward model for the resulting system
133 std::unordered_map<std::string, typename SparseModelType::RewardModelType> rewardModels;
134 if (rewardModelName) {
135 std::vector<typename SparseModelType::ValueType> newActionRewards(ecEliminatorResult.matrix.getRowCount());
136 storm::utility::vector::selectVectorValues(newActionRewards, ecEliminatorResult.newToOldRowMapping, actionRewards);
137 rewardModels.insert(std::make_pair(*rewardModelName, typename SparseModelType::RewardModelType(std::nullopt, std::move(newActionRewards))));
138 }
139
140 // the new labeling
141 storm::models::sparse::StateLabeling labeling(ecEliminatorResult.matrix.getRowGroupCount());
142 for (auto const& label : model.getStateLabeling().getLabels()) {
143 auto const& origStatesWithLabel = model.getStates(label);
144 storm::storage::BitVector newStatesWithLabel(ecEliminatorResult.matrix.getRowGroupCount(), false);
145 for (auto const& origState : origStatesWithLabel) {
146 newStatesWithLabel.set(ecEliminatorResult.oldToNewStateMapping[origState], true);
147 }
148 labeling.addLabel(label, std::move(newStatesWithLabel));
149 }
150
151 return std::make_shared<SparseModelType>(std::move(ecEliminatorResult.matrix), std::move(labeling), std::move(rewardModels));
152}
153
154} // namespace
155
156template<typename SparseModelType>
158 // intentionally left empty
159}
160
161template<typename SparseModelType>
163 static_assert(std::is_same_v<SparseModelType, storm::models::sparse::Dtmc<storm::RationalFunction>> ||
164 std::is_same_v<SparseModelType, storm::models::sparse::Mdp<storm::RationalFunction>>,
165 "SparseParametricModelSimplifier is only implemented for parametric DTMCs and MDPs.");
166 // Make sure that there is no old result from a previous call
167 simplifiedModel = nullptr;
168 simplifiedFormula = nullptr;
169
170 // Go through various cases for the formula, simplify if possible, and decide if state elimination and end component elimination preserves the formula.
171 if (!formula.isProbabilityOperatorFormula() && !formula.isRewardOperatorFormula()) {
172 STORM_LOG_DEBUG("Simplification not possible because the formula is not supported. Formula: " << formula);
173 return false;
174 }
175 auto const& operatorFormula = formula.asOperatorFormula();
176 STORM_LOG_THROW(!originalModel.isNondeterministicModel() || operatorFormula.hasOptimalityType() || operatorFormula.hasBound(),
177 storm::exceptions::InvalidPropertyException,
178 "The formula " << formula << " is not supported for nondeterministic models. It must either have an optimality type or a bound.");
179 bool const minimizing = originalModel.isNondeterministicModel() &&
180 (operatorFormula.hasOptimalityType() ? storm::solver::minimize(operatorFormula.getOptimalityType())
181 : storm::logic::isLowerBound(operatorFormula.getBound().comparisonType));
182 bool enableStateElimination = false;
183 bool enableEndComponentElimination = false;
184 std::optional<std::string> rewardModelName;
185 if (operatorFormula.isProbabilityOperatorFormula()) {
186 if (operatorFormula.getSubformula().isUntilFormula()) {
187 // GoalStateMerger merges all states violating the left subformula (if any) into the sink state, so it can safely be dropped here. Note that the
188 // subformula may either be an until formula (phi U psi) or a plain reachability-probability formula (F psi, i.e. implicitly true U psi).
189 auto eventuallyFormula = std::make_shared<storm::logic::EventuallyFormula const>(
190 operatorFormula.getSubformula().asUntilFormula().getRightSubformula().asSharedPointer(), storm::logic::FormulaContext::Probability);
191 simplifiedFormula = std::make_shared<storm::logic::ProbabilityOperatorFormula const>(eventuallyFormula, operatorFormula.getOperatorInformation());
192 enableStateElimination = true;
193 enableEndComponentElimination = originalModel.isNondeterministicModel() && !minimizing; // end components can only exist for Pmax queries.
194 } else if (operatorFormula.getSubformula().isReachabilityProbabilityFormula()) {
195 enableStateElimination = true;
196 enableEndComponentElimination = originalModel.isNondeterministicModel() && !minimizing; // end components can only exist for Pmax queries.
197 } else if (operatorFormula.getSubformula().isBoundedUntilFormula()) {
198 auto const& boundedUntilFormula = operatorFormula.getSubformula().asBoundedUntilFormula();
199 // multidimensional or reward bounded until formulas are not considered in this simplifier.
200 if (!boundedUntilFormula.isMultiDimensional() && !boundedUntilFormula.getTimeBoundReference().isRewardBound()) {
201 // Since we have a discrete-time model, we may assume a step bound.
202 // Similar to (unbounded) until above, we can also drop the left subformula.
203 auto newBoundedUntilFormula = std::make_shared<storm::logic::BoundedUntilFormula const>(
204 storm::logic::Formula::getTrueFormula(), boundedUntilFormula.getRightSubformula().asSharedPointer(),
205 boundedUntilFormula.getLowerBoundAsOptionalTimeBound(), boundedUntilFormula.getUpperBoundAsOptionalTimeBound(),
207 simplifiedFormula =
208 std::make_shared<storm::logic::ProbabilityOperatorFormula const>(newBoundedUntilFormula, operatorFormula.getOperatorInformation());
209 // neither state elimination nor end component elimination preserve step-bounded formulas.
210 }
211 }
212 } else if (operatorFormula.isRewardOperatorFormula()) {
213 if (operatorFormula.asRewardOperatorFormula().hasRewardModelName()) {
214 rewardModelName = operatorFormula.asRewardOperatorFormula().getRewardModelName();
215 STORM_LOG_THROW(originalModel.hasRewardModel(*rewardModelName), storm::exceptions::InvalidPropertyException,
216 "The reward model specified by formula " << formula << " is not available in the given model.");
217 } else {
218 STORM_LOG_THROW(originalModel.hasUniqueRewardModel(), storm::exceptions::InvalidPropertyException,
219 "The formula " << formula << " does not specify a reward model and the reward model is not unique.");
220 rewardModelName = originalModel.getUniqueRewardModelName();
221 }
222 if (operatorFormula.getSubformula().isReachabilityRewardFormula()) {
223 enableStateElimination = true;
224 enableEndComponentElimination = originalModel.isNondeterministicModel() && minimizing; // end components can only exist for Rmin queries.
225 } else if (operatorFormula.getSubformula().isCumulativeRewardFormula()) {
226 // multidimensional or reward bounded cumulative reward formulas are not considered in this simplifier
227 if (auto const& cumulativeRewardFormula = operatorFormula.getSubformula().asCumulativeRewardFormula();
228 !cumulativeRewardFormula.isMultiDimensional() && !cumulativeRewardFormula.getTimeBoundReference().isRewardBound()) {
229 // Since we have a discrete-time model, we may assume a step bound.
230 boost::optional<storm::logic::RewardAccumulation> rewAcc;
231 if (cumulativeRewardFormula.hasRewardAccumulation()) {
232 rewAcc = cumulativeRewardFormula.getRewardAccumulation();
233 }
234 storm::logic::TimeBound timeBound(cumulativeRewardFormula.isBoundStrict(), cumulativeRewardFormula.getBound());
235 auto newCumulativeRewardFormula =
236 std::make_shared<storm::logic::CumulativeRewardFormula const>(timeBound, cumulativeRewardFormula.getTimeBoundReference(), rewAcc);
237 simplifiedFormula = std::make_shared<storm::logic::RewardOperatorFormula const>(newCumulativeRewardFormula, rewardModelName.value(),
238 operatorFormula.getOperatorInformation());
239 // neither state elimination nor end component elimination preserve step-bounded formulas.
240 }
241 }
242 }
243
244 // If we have not set a simplified formula yet, we do so now by taking the original formula.
245 if (!simplifiedFormula) {
246 simplifiedFormula = formula.clone();
247 }
248
249 // Perform the actual simplifications.
250 // First merge equivalent states based on the formula (e.g. prob0/1 states for until probabilities)
252 auto mergerResult = goalStateMerger.mergeForFormula(formula, true);
253 if (!mergerResult) {
254 STORM_LOG_DEBUG("Simplification not possible because the goal state merger does not support it.");
255 return false;
256 }
257 simplifiedModel = mergerResult->model->template as<SparseModelType>();
258
259 // Next do state elimination and end component elimination.
260 std::string bottomLabel = "";
261 if (enableEndComponentElimination) {
262 // Insert a bottom label *before* doing state elimination. This is so that we still know what the sink/target states are after elimination.
263 storm::storage::BitVector bottomStates(simplifiedModel->getNumberOfStates(), false);
264 if (mergerResult->targetState) {
265 bottomStates.set(*mergerResult->targetState, true);
266 }
267 if (mergerResult->sinkState) {
268 bottomStates.set(*mergerResult->sinkState, true);
269 }
270 bottomLabel = simplifiedModel->getStateLabeling().addUniqueLabel("bottom", std::move(bottomStates));
271 }
272 if (enableStateElimination) {
273 // Eliminate all states for which all outgoing transitions are constant
274 storm::storage::BitVector considerForElimination = ~simplifiedModel->getInitialStates();
275 if (mergerResult->targetState) {
276 considerForElimination.set(*mergerResult->targetState, false);
277 }
278 if (mergerResult->sinkState) {
279 considerForElimination.set(*mergerResult->sinkState, false);
280 }
281 simplifiedModel = eliminateConstantDeterministicStates(*simplifiedModel, preserveParametricTransitions, considerForElimination, rewardModelName);
282 }
283 if (enableEndComponentElimination) {
284 // Eliminate the end components that do not contain a target or a sink state
285 simplifiedModel = eliminateNeutralEndComponents(*simplifiedModel, simplifiedModel->getStates(bottomLabel), rewardModelName);
286 }
287
288 return true; // simplification successful
289}
290
291template<typename SparseModelType>
293 STORM_LOG_THROW(simplifiedModel, storm::exceptions::InvalidStateException, "Tried to get the simplified model but simplification was not invoked before.");
294 return simplifiedModel;
295}
296
297template<typename SparseModelType>
298std::shared_ptr<storm::logic::Formula const> SparseParametricModelSimplifier<SparseModelType>::getSimplifiedFormula() const {
299 STORM_LOG_THROW(simplifiedFormula, storm::exceptions::InvalidStateException,
300 "Tried to get the simplified formula but simplification was not invoked before.");
301 return simplifiedFormula;
302}
303
304template<typename SparseModelType>
306 this->preserveParametricTransitions = preserveParametricTransitions;
307}
308
309template<typename SparseModelType>
311 return this->preserveParametricTransitions;
312}
313
316} // namespace transformer
317} // namespace storm
static std::shared_ptr< Formula const > getTrueFormula()
Definition Formula.cpp:227
virtual bool isProbabilityOperatorFormula() const
Definition Formula.cpp:194
OperatorFormula & asOperatorFormula()
Definition Formula.cpp:522
virtual bool isRewardOperatorFormula() const
Definition Formula.cpp:198
std::shared_ptr< Formula > clone() const
Definition Formula.cpp:554
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
void set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
const_rows getRowGroup(index_type rowGroup) const
Returns an object representing the given row group.
std::vector< index_type > const & getRowGroupIndices() const
Returns the grouping of rows of this matrix.
index_type getRowGroupSize(index_type group) const
Returns the size of the given row group.
storm::storage::SparseMatrix< value_type > transpose(bool joinGroups=false, bool keepZeros=false) const
Transposes the matrix.
storm::storage::BitVector getRowFilter(storm::storage::BitVector const &groupConstraint) const
Returns a bitvector representing the set of rows, with all indices set that correspond to one of the ...
static EndComponentEliminatorReturnType transform(storm::storage::SparseMatrix< ValueType > const &originalMatrix, storm::storage::MaximalEndComponentDecomposition< ValueType > ecs, storm::storage::BitVector const &subsystemStates, storm::storage::BitVector const &addSinkRowStates, bool addSelfLoopAtSinkStates=false)
std::optional< ReturnType > mergeForFormula(storm::logic::Formula const &formula, bool const dropUnreachableFromInit) const
Identifies the maybeStates, targetStates, sinkStates (and, if applicable, the reward model and choice...
This class performs different steps to simplify the given (parametric) model.
void setPreserveParametricTransitions(bool preserveParametricTransitions)
Set whether to preserve parametric transions (i.e.
std::shared_ptr< storm::logic::Formula const > getSimplifiedFormula() const
bool isPreserveParametricTransitionsSet() const
Whether this SparseParametricModelSimplifier preserves parametric transitions.
#define STORM_LOG_DEBUG(message)
Definition logging.h:28
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
bool isLowerBound(ComparisonType t)
bool constexpr minimize(OptimizationDirection d)
void selectVectorValues(std::vector< T > &vector, storm::storage::BitVector const &positions, std::vector< T > const &values)
Selects the elements from a vector at the specified positions and writes them consecutively into anot...
Definition vector.h:185
void filterVectorInPlace(std::vector< Type > &v, storm::storage::BitVector const &filter)
Definition vector.h:1080
bool isConstant(ValueType const &)
bool isZero(ValueType const &a)
Definition constants.cpp:44
ValueType zero()
Definition constants.cpp:26