Storm 1.13.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
ViOperatorMultiplier.cpp
Go to the documentation of this file.
2
12
13namespace storm::solver {
14
15namespace detail {
17
22template<typename ValueType, BackendOptimizationDirection Dir = BackendOptimizationDirection::None, bool TrackChoices = false>
24 public:
26 requires(!TrackChoices)
27 : choiceTracking(std::nullopt) {};
28
29 MultiplierBackend(std::vector<uint64_t>& choices, std::vector<uint64_t> const& rowGroupIndices)
30 requires TrackChoices
31 : choiceTracking({choices, rowGroupIndices}) {
32 // intentionally left empty.
33 }
34
36 // intentionally left empty.
37 }
38
39 void firstRow(ValueType&& value, [[maybe_unused]] uint64_t rowGroup, [[maybe_unused]] uint64_t row) {
40 best = std::move(value);
41 if constexpr (TrackChoices) {
42 choiceTracking.currentBestRow = row;
43 }
44 }
45
46 void nextRow(ValueType&& value, [[maybe_unused]] uint64_t rowGroup, [[maybe_unused]] uint64_t row) {
47 if constexpr (TrackChoices) {
48 if (best &= value) {
49 choiceTracking.currentBestRow = row;
50 } else if (*best == value) {
51 // Reaching this point means that there are multiple 'best' values
52 // For rows that are equally good, we prefer to not change the currently selected choice.
53 // This is necessary, e.g., for policy iteration correctness and helps keeping schedulers simple.
54 if (row == choiceTracking.choices[rowGroup] + choiceTracking.rowGroupIndices[rowGroup]) {
55 choiceTracking.currentBestRow = row;
56 }
57 }
58 } else if constexpr (HasDir) {
59 best &= value;
60 } else {
61 STORM_LOG_ASSERT(false, "This backend does not support optimization direction.");
62 }
63 }
64
65 void applyUpdate(ValueType& currValue, [[maybe_unused]] uint64_t rowGroup) {
66 if constexpr (HasDir) {
67 currValue = std::move(*best);
68 if constexpr (TrackChoices) {
69 choiceTracking.choices[rowGroup] = choiceTracking.currentBestRow - choiceTracking.rowGroupIndices[rowGroup];
70 }
71 } else {
72 currValue = std::move(best);
73 }
74 }
75
76 void endOfIteration() const {
77 // intentionally left empty.
78 }
79
80 bool converged() const {
81 return true;
82 }
83
84 bool constexpr abort() const {
85 return false;
86 }
87
88 private:
89 static constexpr bool HasDir = Dir != BackendOptimizationDirection::None;
90 static constexpr storm::OptimizationDirection OptDir =
91 Dir == BackendOptimizationDirection::Maximize ? storm::OptimizationDirection::Maximize : storm::OptimizationDirection::Minimize;
92 static_assert(!TrackChoices || HasDir, "If TrackChoices is true, Dir must be set to either Minimize or Maximize.");
93
94 std::conditional_t<HasDir, storm::utility::Extremum<OptDir, ValueType>, ValueType> best;
95
96 struct SchedulerTrackingData {
97 std::vector<uint64_t>& choices; // Storage for the scheduler choices.
98 std::vector<uint64_t> const& rowGroupIndices; // Indices of the row groups.
99 uint64_t currentBestRow{0};
100 };
101 std::conditional_t<TrackChoices, SchedulerTrackingData, std::nullopt_t> choiceTracking; // Only used if TrackChoices is true.
102};
103
108template<typename ValueType>
110 public:
111 PlainMultiplicationBackend(std::vector<ValueType>& rowResults) : rowResults(rowResults) {};
112
114 // intentionally left empty.
115 }
116
117 void firstRow(ValueType&& value, [[maybe_unused]] uint64_t rowGroup, uint64_t row) {
118 rowResults[row] = std::move(value);
119 }
120
121 void nextRow(ValueType&& value, [[maybe_unused]] uint64_t rowGroup, uint64_t row) {
122 rowResults[row] = std::move(value);
123 }
124
125 void applyUpdate([[maybe_unused]] ValueType& currValue, [[maybe_unused]] uint64_t rowGroup) {
126 // intentionally left empty.
127 }
128
129 void endOfIteration() const {
130 // intentionally left empty.
131 }
132
133 bool converged() const {
134 return true;
135 }
136
137 bool constexpr abort() const {
138 return false;
139 }
140
141 private:
142 std::vector<ValueType>& rowResults;
143};
144
145} // namespace detail
146
147template<typename ValueType, bool TrivialRowGrouping, typename SolutionType>
152
153template<typename ValueType, bool TrivialRowGrouping, typename SolutionType>
154typename ViOperatorMultiplier<ValueType, TrivialRowGrouping, SolutionType>::ViOpT&
155ViOperatorMultiplier<ValueType, TrivialRowGrouping, SolutionType>::initialize() const {
156 if (!viOperatorFwd) {
157 return initialize(false); // default to backward operator
158 } else {
159 return *viOperatorFwd;
160 }
161}
162
163template<typename ValueType, bool TrivialRowGrouping, typename SolutionType>
164typename ViOperatorMultiplier<ValueType, TrivialRowGrouping, SolutionType>::ViOpT&
165ViOperatorMultiplier<ValueType, TrivialRowGrouping, SolutionType>::initialize(bool backwards) const {
166 auto& viOp = backwards ? viOperatorBwd : viOperatorFwd;
167 if (!viOp) {
168 viOp = std::make_unique<ViOpT>();
169 if (backwards) {
170 viOp->setMatrixBackwards(this->matrix);
171 } else {
172 viOp->setMatrixForwards(this->matrix);
173 }
174 }
175 return *viOp;
176}
177
178template<typename ValueType, bool TrivialRowGrouping, typename SolutionType>
180 std::vector<ValueType> const* b, std::vector<SolutionType>& result) const {
181 if (&result == &x) {
182 auto& tmpResult = this->provideCachedVector(x.size());
183 multiply(env, x, b, tmpResult);
184 std::swap(result, tmpResult);
185 return;
186 }
187 auto const& viOp = initialize();
189 // Below, we just add 'result' as a dummy argument to the apply method.
190 // The backend already takes care of filling the result vector while processing the rows.
191 if (b) {
192 viOp.apply(x, result, *b, backend);
193 } else {
194 viOp.apply(x, result, storm::utility::zero<ValueType>(), backend);
195 }
196}
197
198template<typename ValueType, bool TrivialRowGrouping, typename SolutionType>
200 std::vector<ValueType> const* b, bool backwards) const {
201 STORM_LOG_THROW(TrivialRowGrouping, storm::exceptions::NotSupportedException,
202 "This multiplier does not support multiplications without reduction when invoked with non-trivial row groups");
204 auto const& viOp = initialize(backwards);
205 if (b) {
206 viOp.applyInPlace(x, *b, backend);
207 } else {
208 viOp.applyInPlace(x, storm::utility::zero<ValueType>(), backend);
209 }
210}
211
212template<typename ValueType, bool TrivialRowGrouping, typename SolutionType>
214 std::vector<uint64_t> const& rowGroupIndices,
215 std::vector<SolutionType> const& x, std::vector<ValueType> const* b,
216 std::vector<SolutionType>& result,
217 UncertaintyResolutionMode const& uncertaintyResolutionMode,
218 std::vector<uint64_t>* choices) const {
219 if (&result == &x) {
220 auto& tmpResult = this->provideCachedVector(x.size());
221 multiplyAndReduce(env, dir, rowGroupIndices, x, b, tmpResult, uncertaintyResolutionMode, choices);
222 std::swap(result, tmpResult);
223 return;
224 }
225 STORM_LOG_THROW(&rowGroupIndices == &this->matrix.getRowGroupIndices(), storm::exceptions::NotSupportedException,
226 "The row group indices must be the same as the ones stored in the matrix of this multiplier");
227 auto const& viOp = initialize();
228
229 auto applyRobustDirection = [&]<storm::OptimizationDirection Dir, typename BT, typename OffsetType>(BT& backend, OffsetType const& offset) {
230 bool robustUncertainty = false;
231 if constexpr (storm::IsIntervalType<ValueType>) {
232 robustUncertainty = isUncertaintyResolvedRobust(uncertaintyResolutionMode, Dir);
233 }
234
235 if (robustUncertainty) {
236 viOp.template applyRobust<invert(Dir)>(x, result, offset, backend);
237 } else {
238 viOp.template applyRobust<Dir>(x, result, offset, backend);
239 }
240 };
241
242 auto applyBackend = [&]<typename OffsetType>(OffsetType const& offset) {
243 if (storm::solver::minimize(dir)) {
244 if (choices) {
246 this->matrix.getRowGroupIndices());
247 applyRobustDirection.template operator()<OptimizationDirection::Minimize>(backend, offset);
248 } else {
250 applyRobustDirection.template operator()<OptimizationDirection::Minimize>(backend, offset);
251 }
252 } else {
253 if (choices) {
255 this->matrix.getRowGroupIndices());
256 applyRobustDirection.template operator()<OptimizationDirection::Maximize>(backend, offset);
257 } else {
259 applyRobustDirection.template operator()<OptimizationDirection::Maximize>(backend, offset);
260 }
261 }
262 };
263
264 if (b) {
265 applyBackend(*b);
266 } else {
267 applyBackend(storm::utility::zero<ValueType>());
268 }
269}
270
271template<typename ValueType, bool TrivialRowGrouping, typename SolutionType>
273 Environment const& env, OptimizationDirection const& dir, std::vector<uint64_t> const& rowGroupIndices, std::vector<SolutionType>& x,
274 std::vector<ValueType> const* b, std::vector<uint_fast64_t>* choices, bool backwards) const {
275 STORM_LOG_THROW(&rowGroupIndices == &this->matrix.getRowGroupIndices(), storm::exceptions::NotSupportedException,
276 "The row group indices must be the same as the ones stored in the matrix of this multiplier");
277 auto const& viOp = initialize(backwards);
278 auto apply = [&]<typename BT>(BT& backend) {
279 if (b) {
280 viOp.applyInPlace(x, *b, backend);
281 } else {
282 viOp.applyInPlace(x, storm::utility::zero<ValueType>(), backend);
283 }
284 };
285 if (storm::solver::minimize(dir)) {
286 if (choices) {
288 apply(backend);
289 } else {
291 apply(backend);
292 }
293 } else {
294 if (choices) {
296 apply(backend);
297 } else {
299 apply(backend);
300 }
301 }
302}
303
304template<typename ValueType, bool TrivialRowGrouping, typename SolutionType>
310
313
316
319
322
323} // namespace storm::solver
Multiplier(storm::storage::SparseMatrix< ValueType > const &matrix)
std::vector< ValueType > & provideCachedVector(uint64_t size) const
virtual void clearCache() const
storm::storage::SparseMatrix< ValueType > const & matrix
Definition Multiplier.h:168
virtual void multiplyAndReduceGaussSeidel(Environment const &env, OptimizationDirection const &dir, std::vector< uint64_t > const &rowGroupIndices, std::vector< SolutionType > &x, std::vector< ValueType > const *b, std::vector< uint_fast64_t > *choices=nullptr, bool backwards=true) const override
virtual void multiplyAndReduce(Environment const &env, OptimizationDirection const &dir, std::vector< uint64_t > const &rowGroupIndices, std::vector< SolutionType > const &x, std::vector< ValueType > const *b, std::vector< SolutionType > &result, UncertaintyResolutionMode const &uncertaintyResolutionMode=UncertaintyResolutionMode::Unset, std::vector< uint_fast64_t > *choices=nullptr) const override
virtual void multiply(Environment const &env, std::vector< SolutionType > const &x, std::vector< ValueType > const *b, std::vector< SolutionType > &result) const override
ViOperatorMultiplier(storm::storage::SparseMatrix< ValueType > const &matrix)
virtual void multiplyGaussSeidel(Environment const &env, std::vector< SolutionType > &x, std::vector< ValueType > const *b, bool backwards=true) const override
virtual void clearCache() const override
This backend stores the best (maximal or minimal) value of the current row group.
void applyUpdate(ValueType &currValue, uint64_t rowGroup)
void firstRow(ValueType &&value, uint64_t rowGroup, uint64_t row)
void nextRow(ValueType &&value, uint64_t rowGroup, uint64_t row)
MultiplierBackend(std::vector< uint64_t > &choices, std::vector< uint64_t > const &rowGroupIndices)
This backend simply stores the row results in a vector.
void applyUpdate(ValueType &currValue, uint64_t rowGroup)
PlainMultiplicationBackend(std::vector< ValueType > &rowResults)
void firstRow(ValueType &&value, uint64_t rowGroup, uint64_t row)
void nextRow(ValueType &&value, uint64_t rowGroup, uint64_t row)
A class that holds a possibly non-square matrix in the compressed row storage format.
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
bool constexpr minimize(OptimizationDirection d)
bool isUncertaintyResolvedRobust(UncertaintyResolutionMode uncertaintyResolutionMode, OptimizationDirection optimizationDirection)
ValueType zero()
Definition constants.cpp:24
solver::OptimizationDirection OptimizationDirection
constexpr bool IsIntervalType
Helper to check if a type is an interval.