mCRL2
Loading...
Searching...
No Matches
liblts_pbisim_bem.h
Go to the documentation of this file.
1// Author(s): Hector Joao Rivera Verduzco
2// Copyright: see the accompanying file COPYING or copy at
3// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
4//
5// Distributed under the Boost Software License, Version 1.0.
6// (See accompanying file LICENSE_1_0.txt or copy at
7// http://www.boost.org/LICENSE_1_0.txt)
8//
9/// \file lts/detail/liblts_pbisim_bem.h
10
11#ifndef MCRL2_LTS_DETAIL_LIBLTS_PBISIM_BEM_H
12#define MCRL2_LTS_DETAIL_LIBLTS_PBISIM_BEM_H
13
14#include "mcrl2/utilities/execution_timer.h"
15#include "mcrl2/lts/detail/liblts_plts_merge.h"
16
17namespace mcrl2::lts::detail
18{
19
20template < class LTS_TYPE>
22{
23
24 public:
25 /** \brief Creates a probabilistic bisimulation partitioner for an PLTS.
26 * \details This bisimulation partitioner applies the algorithm defined in C. Baier, B. Engelen and M. Majster-Cederbaum.
27 * Deciding Bisimilarity and Similarity for Probabilistic Processes. In Journal of Computer and System Sciences 60, 187-237 (2000)
28 */
30 {
31 mCRL2log(log::verbose) << "Probabilistic bisimulation partitioner created for "
32 << l.num_states() << " states and " <<
33 l.num_transitions() << " transitions\n";
34 timer.start("bisimulation_reduce (bem)");
37 timer.finish("bisimulation_reduce (bem)");
39 }
40
41 /** \brief Gives the number of bisimulation equivalence classes of the LTS.
42 * \return The number of bisimulation equivalence classes of the LTS.
43 */
45 {
46 return state_partition.size();
47 }
48
49 /** \brief Gives the bisimulation equivalence class number of a state.
50 * \param[in] s A state number.
51 * \return The number of the bisimulation equivalence class to which \e s belongs. */
53 {
54 assert(s<block_index_of_a_state.size());
55 return block_index_of_a_state[s]; // The block index is the state number of the block.
56 }
57
58 /** \brief Gives the bisimulation equivalence step class number of a probabilistic state.
59 * \param[in] s A probabilistic state number.
60 * \return The number of the step class to which s belongs. */
62 {
63 assert(d < step_class_index_of_a_distribution.size());
64 assert(step_class_index_of_a_distribution[d] < step_classes.size());
65 assert(step_classes[step_class_index_of_a_distribution[d]].equivalent_step_class < step_classes.size());
66 return step_classes[step_class_index_of_a_distribution[d]].equivalent_step_class;
67 }
68
69 /** \brief Replaces the transition relation of the current lts by the transitions
70 * of the bisimulation reduced transition system.
71 * \pre The bisimulation equivalence classes have been computed. */
73 {
74 std::set < transition > resulting_transitions;
75
76 const std::vector<transition>& trans = aut.get_transitions();
77 for (const transition& t : trans)
78 {
79 resulting_transitions.insert(
80 transition(
81 get_eq_class(t.from()),
82 t.label(),
83 get_eq_step_class(t.to())));
84 }
85 // Remove the old transitions
86 aut.clear_transitions();
87
88 // Copy the transitions from the set into the transition system.
89 for (const transition& t : resulting_transitions)
90 {
91 aut.add_transition(t);
92 }
93 }
94
95 /** \brief Replaces the probabilistic states of the current lts by the probabilistic
96 * states of the bisimulation reduced transition system.
97 * \pre The bisimulation step classes have been computed. */
99 {
100 std::vector<typename LTS_TYPE::probabilistic_state_t> new_probabilistic_states;
101
102 // get the equivalent probabilistic state of each probabilistic block and add it to aut
103 for (step_class_type& sc : equivalent_step_classes)
104 {
105 typename LTS_TYPE::probabilistic_state_t equivalent_ps = calculate_equivalent_probabilistic_state(sc);
106 new_probabilistic_states.push_back(equivalent_ps);
107 }
108
109 /* Remove old probabilistic states */
110 aut.clear_probabilistic_states();
111
112 // Add new prob states to aut
113 for (const typename LTS_TYPE::probabilistic_state_t& new_ps : new_probabilistic_states)
114 {
115 aut.add_probabilistic_state(new_ps);
116 }
117
118 typename LTS_TYPE::probabilistic_state_t old_initial_prob_state = aut.initial_probabilistic_state();
119 typename LTS_TYPE::probabilistic_state_t new_initial_prob_state = calculate_new_probabilistic_state(old_initial_prob_state);
120 aut.set_initial_probabilistic_state(new_initial_prob_state);
121 }
122
123 /** \brief Returns whether two states are in the same probabilistic bisimulation equivalence class.
124 * \param[in] s A state number.
125 * \param[in] t A state number.
126 * \retval true if \e s and \e t are in the same bisimulation equivalence class;
127 * \retval false otherwise. */
128 bool in_same_probabilistic_class(const std::size_t s, const std::size_t t) const
129 {
130 return get_eq_step_class(s) == get_eq_step_class(t);
131 }
132
133 private:
134
140 using probability_fraction_type = typename LTS_TYPE::probabilistic_state_t::probability_t;
141
143 {
145 std::vector<std::list<transition*>> incoming_transitions_per_label; // Incoming transitions organized per label
146 };
147
149 {
151 std::list<state_type> states; // The states in the block
152 bool is_in_new_blocks = false;
153 };
154
157 label_type action = 0UL; // action label of the pair <a,M>.
158 std::list<distribution_type*> distributions; // The distributions in the step class <a,M>.
159 std::vector< bool > prev_states; // Previous states that can reach step_class <a,M>
162 };
163
172
173 LTS_TYPE& aut;
174
176 {
179 tree_type* left = nullptr;
180 tree_type* right = nullptr;
181
183 {
184 count = 0;
185 left = nullptr;
186 right = nullptr;
187 }
188 };
189
190 /** \brief Creates the initial partition of step classes and blocks.
191 * \detail The blocks are initially partitioned based on the actions that can perform.
192 * The step classes are partitioned based on the action that leads to the probabilistic state */
194 {
195 std::vector< std::vector< std::list<distribution_type*> > > steps; // Representation of transition in 2-d array
196 std::vector<transition>& transitions = aut.get_transitions();
197 std::vector< std::vector<bool> > distribution_per_step_class;
198
199 distribution_per_step_class.resize(aut.num_action_labels());
200 for (std::vector<bool>& i : distribution_per_step_class)
201 {
202 i.resize(aut.num_probabilistic_states());
203 }
204
205 //---- Preprocessing stage to transform the PLTS to the data structures suggested by Baier ---- //
206
207 // Construct vector of distributions
208 distributions.resize(aut.num_probabilistic_states());
209 std::size_t key = 0;
210 for (distribution_type& distribution : distributions)
211 {
212 distribution.key = key;
213 distribution.incoming_transitions_per_label.resize(aut.num_action_labels());
214 key++;
215 }
216
217 // Initialize the Steps bi-dimientional array
218 steps.resize(aut.num_states());
219 for (std::size_t i = 0; i < steps.size(); i++)
220 {
221 steps[i].resize(aut.num_action_labels());
222 }
223
224 for (transition& t : transitions)
225 {
226 steps[t.from()][t.label()].push_back(&distributions[t.to()]);
227 distributions[t.to()].incoming_transitions_per_label[t.label()].push_back(&t);
228 }
229
230 //---- Start the initialization process (page 207. Fig. 10. Baier) ---- //
231
232 // Initially there are as many step classes as lables
233 step_classes.resize(aut.num_action_labels());
234
235 for (step_class_type& sc : step_classes)
236 {
237 sc.prev_states.resize(aut.num_states());
238 }
239
240 // create tree structure to group action states based on the outgoing transitions
241 std::size_t max_block_size = 0;
242 tree_type* max_block = nullptr;
243 std::deque<tree_type> tree_nodes;
244 std::vector<tree_type*> leaves;
245 tree_type v0;
246 v0.init_node();
247 tree_nodes.push_back(v0);
248
249 // For all s in S do
250 for (state_type s = 0; s < aut.num_states(); s++)
251 {
252 // (1) Create pointer to root of the tree
253 tree_type* v = &tree_nodes[0];
254
255 // (2) Construct tree
256 for (std::size_t i = 0; i < aut.num_action_labels(); i++)
257 {
258 step_classes[i].key = i;
259 step_classes[i].action = i;
260 if (steps[s][i].size() > 0)
261 {
262 for (distribution_type* d : steps[s][i])
263 {
264 if (distribution_per_step_class[i][d->key] == false)
265 {
266 step_classes[i].distributions.push_back(d);
267 distribution_per_step_class[i][d->key] = true;
268 }
269 }
270
271 step_classes[i].prev_states[s] = true;
272
273 if (v->left == nullptr)
274 {
275 // create left node
276 tree_type w;
277 w.init_node();
278 tree_nodes.push_back(w);
279 v->left = &tree_nodes.back();
280
281 // add new node to the leaf nodes if it is a leaf
282 if (i == aut.num_action_labels() - 1)
283 {
284 leaves.push_back(v->left);
285 }
286
287 }
288 v = v->left;
289 }
290 else
291 {
292 if (v->right == nullptr)
293 {
294 // create left node
295 tree_type w;
296 w.init_node();
297 tree_nodes.push_back(w);
298 v->right = &tree_nodes.back();
299
300 // add new node to the leaf nodes if it is a leaf
301 if (i == aut.num_action_labels() - 1)
302 {
303 leaves.push_back(v->right);
304 }
305 }
306 v = v->right;
307 }
308 }
309
310 // (3) Add the state to the leaf and increment its state counter
311 v->states.push_back(s);
312 v->count++;
313
314 // (4) Keep track of the leave containing more states
315 if (v->count > max_block_size)
316 {
317 max_block = v;
318 max_block_size = v->count;
319 }
320 }
321
322 // insert all states of the leaves to blocks
323 blocks.resize(leaves.size());
324
325 key = 0;
326 std::size_t larger_key = 0;
327 for (tree_type* leaf_ptr : leaves)
328 {
329 block_type& block = blocks[key];
330 block.key = key;
331 block.states.splice(block.states.end(), leaf_ptr->states);
332
333 if (leaf_ptr == max_block)
334 {
335 larger_key = key;
336 }
337
338 key++;
339 }
340
341 // Add all blocks to the state partition.
342 // Initially only the larger block has to be at the end of the list
343 for (block_type& b : blocks)
344 {
345 if (b.key != larger_key)
346 {
347 // Push the non-larger blocks to the front of the list. This are the so called new blocks
348 b.is_in_new_blocks = true;
349 state_partition.push_front(&b);
350 }
351 else
352 {
353 // push the larger block to the end of the list
354 b.is_in_new_blocks = false;
355 state_partition.push_back(&b);
356 }
357 }
358
359 // Add all step classes to the step partition.
360 // Initially all are new step classes
361 for (step_class_type& sc : step_classes)
362 {
363 if (sc.distributions.size() > 0)
364 {
365 step_partition.push_front(&sc);
366 sc.is_in_new_step_classes = true;
367 }
368 }
369
370 block_index_of_a_state.resize(aut.num_states());
371 for (const block_type& b : blocks)
372 {
373 for (const state_type s : b.states)
374 {
375 block_index_of_a_state[s] = b.key;
376 }
377 }
378
379 }
380
381 /** \brief Calculates the probability to reach block b from distribution d.
382 * \param[in] d is a probabilistic state (distribution).
383 * b is a block of states.
384 * \return The probability to reach block b. */
385 probability_fraction_type probability_to_block(distribution_type& d, block_type& b)
386 {
387 probability_fraction_type prob_to_block;
388 const typename LTS_TYPE::probabilistic_state_t& prob_state = aut.probabilistic_state(d.key);
389
390 /* Check whether the state is in the distribution d and add up the probability*/
391 if (prob_state.size()>1) // The state is stored as a vector of states/probabilities.
392 {
393 for (const typename LTS_TYPE::probabilistic_state_t::state_probability_pair& prob_pair : prob_state)
394 {
395 const state_type s = prob_pair.state();
396 if (block_index_of_a_state[s] == b.key)
397 {
398 prob_to_block = prob_to_block + prob_pair.probability();
399 }
400 }
401 }
402 else // The state consists of a number with implicit probability 1.
403 {
404 const state_type s = prob_state.get();
405 if (block_index_of_a_state[s] == b.key)
406 {
407 prob_to_block = prob_to_block + LTS_TYPE::probabilistic_state_t::probability_t::one();
408 }
409 }
410
411 return prob_to_block;
412 }
413
414 typename LTS_TYPE::probabilistic_state_t calculate_new_probabilistic_state(typename LTS_TYPE::probabilistic_state_t ps)
415 {
416 typename LTS_TYPE::probabilistic_state_t new_prob_state;
417
418 /* Iterate over all state probability pairs in the selected probabilistic state*/
419 if (ps.size()>1) // The state is stored as a vector of states/probabilities.
420 {
421 std::map<state_type, probability_fraction_type> prob_state_map;
422 for (const typename LTS_TYPE::probabilistic_state_t::state_probability_pair& sp_pair : ps)
423 {
424 /* Check the resulting action state in the final State partition */
425 state_type new_state = get_eq_class(sp_pair.state());
426
427 if (prob_state_map.count(new_state) == 0)
428 {
429 /* The state is not yet in the mapping. Add the state with its probability*/
430 prob_state_map[new_state] = sp_pair.probability();
431 }
432 else
433 {
434 /* The state is already in the mapping. Sum up probabilities */
435 prob_state_map[new_state] = prob_state_map[new_state] + sp_pair.probability();
436 }
437 }
438 /* Add all the state probabilities pairs in the mapping to its actual data type*/
439 /* Add all the state probabilities pairs in the mapping to its actual data type*/
440 typename std::map<state_type, probability_fraction_type>::const_iterator i = prob_state_map.begin();
441 if (++i==prob_state_map.end()) // There is only one state with probability 1.
442 {
443 new_prob_state.set(prob_state_map.begin()->first);
444 }
445 else
446 {
447 // This probabilistic state has more components.
448 for (const std::pair<const state_type, probability_fraction_type>& i: prob_state_map)
449 {
450 new_prob_state.add(i.first, i.second);
451 }
452 }
453
454 }
455 else // The state consists of a number with implicit probability 1.
456 {
457 /* Check the resulting action state in the final State partition */
458 new_prob_state.set(get_eq_class(ps.get()));
459 }
460
461 return new_prob_state;
462 }
463
465 {
466 typename LTS_TYPE::probabilistic_state_t equivalent_prob_state;
467
468 /* Select the first probabilistic state of the step class */
469 distribution_type* d = sc.distributions.front();
470
471 typename LTS_TYPE::probabilistic_state_t old_prob_state = aut.probabilistic_state(d->key);
472
473 equivalent_prob_state = calculate_new_probabilistic_state(old_prob_state);
474
475 return equivalent_prob_state;
476 }
477
478 /** \brief Two-phased partitioning algorithm described in page 204. Fig 9. Baier.
479 * \detail Refinement of state partition and step partition until no new blocks/step classes
480 * are in front of the partition lists
481 */
483 {
484 std::list<step_class_type*> step_partition_old;
485 std::list<block_type*> state_partition_old;
486
487 // Repeat until no new blocks in front of the partition lists
488 while (state_partition.front()->is_in_new_blocks == true ||
489 (step_partition.size()>0 && step_partition.front()->is_in_new_step_classes == true))
490 {
491 // Phase 1: Splitting of step_partition via split(M,C)
492 if (state_partition.front()->is_in_new_blocks == true)
493 {
494 // Choose a new block in front of the state partition and change it to the back of the list
495 block_type* c_block = state_partition.front();
496 state_partition.pop_front();
497 state_partition.push_back(c_block);
498 c_block->is_in_new_blocks = false;
499
500 // swap elements of step_partition (M) to step_partition_old (M_old)
501 step_partition_old.swap(step_partition);
502
503 // vector to add the new step classes
504 static std::vector<typename std::list<step_class_type*>::iterator> pending_new_step_classes;
505 pending_new_step_classes.clear();
506
507 // iterate over all step classes
508 for (typename std::list<step_class_type*>::iterator sc_iter = step_partition_old.begin();
509 sc_iter != step_partition_old.end(); ++sc_iter)
510 {
511 step_class_type* sc_ptr = *sc_iter;
512
513 // Mapping to sort the distributions based on its probability to reach a block, instead of using
514 // an ordered balanced tree as suggsted in Baier
515 static std::map< probability_fraction_type, std::list<distribution_type*> > distributions_ordered_by_prob;
516 distributions_ordered_by_prob.clear();
517
518 // Iterate over all distributions d of the step class and add probability to block to vector
519 for (distribution_type* d : sc_ptr->distributions)
520 {
521 probability_fraction_type probability = probability_to_block(*d, *c_block);
522 distributions_ordered_by_prob[probability].push_back(d);
523 }
524
525 // if there are multiple elements in the distributions_ordered_by_prob then we have to
526 // add them to the step partition as a new step class
527 if (distributions_ordered_by_prob.size() >= 2)
528 {
529 step_class_type* new_step_class_ptr;
530
531 step_classes.resize(step_classes.size() + distributions_ordered_by_prob.size() - 1);
532 // iterate over mapping
533 std::size_t new_class_count = 0;
534 for (std::pair< probability_fraction_type, std::list<distribution_type*> > ordered_dist : distributions_ordered_by_prob)
535 {
536 std::list<distribution_type*>& distribution_list = ordered_dist.second;
537
538 if (new_class_count == 0)
539 {
540 // if it is the first element of the mapping then we do not create a new step class; instead, we
541 // add its elements into the current step class that is being split.
542 sc_ptr->distributions.swap(distribution_list);
543
544 // clear the prev states of the current step class
545 for (std::size_t i = 0; i < sc_ptr->prev_states.size(); i++)
546 {
547 sc_ptr->prev_states[i] = false;
548 }
549
550 // recalculate prev states based on incoming transitions of each distribution
551 for (distribution_type* d : sc_ptr->distributions)
552 {
553 for (transition* t_ptr : d->incoming_transitions_per_label[sc_ptr->action])
554 {
555 sc_ptr->prev_states[t_ptr->from()] = true;
556 }
557 }
558
559 // add to the front of the step partition (as a new step class) if not yet added
560 if (sc_ptr->is_in_new_step_classes == false)
561 {
562 // since we are iterating over this step class, we save its iterator in a pending vector,
563 // so later we can add it to the front of the step partition (outside the loop)
564 pending_new_step_classes.push_back(sc_iter);
565 sc_ptr->is_in_new_step_classes = true;
566 }
567 }
568 else
569 {
570 new_step_class_ptr = &step_classes[step_classes.size() - new_class_count];
571
572 //init new step class
573 new_step_class_ptr->key = step_classes.size() - new_class_count;
574 new_step_class_ptr->action = sc_ptr->action;
575 new_step_class_ptr->is_in_new_step_classes = true;
576 new_step_class_ptr->prev_states.resize(aut.num_states());
577 new_step_class_ptr->distributions.swap(distribution_list);
578
579 // recalculate prev states based ont incoming transitions of each distribution
580 for (distribution_type* d : new_step_class_ptr->distributions)
581 {
582 for (transition* t_ptr : d->incoming_transitions_per_label[new_step_class_ptr->action])
583 {
584 new_step_class_ptr->prev_states[t_ptr->from()] = true;
585 }
586 }
587
588 // add new step class to step partition and new_step_classes
589 step_partition.push_front(new_step_class_ptr);
590 }
591 new_class_count++;
592 }
593 }
594 }
595
596 // Add the pending new blocks to the front of the list
597 for (const typename std::list<step_class_type*>::iterator& sc_iter: pending_new_step_classes)
598 {
599 step_partition.splice(step_partition.begin(), step_partition_old, sc_iter);
600 }
601
602 // move remaining step classes to the end of step partition
603 step_partition.splice(step_partition.end(), step_partition_old);
604 }
605
606 // Phase 2: Refinment of state_partition via Refine(X,a,M)
607 if (step_partition.front()->is_in_new_step_classes == true)
608 {
609 // Choose some step class <a,M> in new_step_classes and remove it from new_step_classes
610 step_class_type* step_class = step_partition.front();
611 step_partition.pop_front();
612 step_class->is_in_new_step_classes = false;
613 step_partition.push_back(step_class);
614
615 // swap elements of state_partition (X) to state_partition_old (X_old)
616 state_partition_old.swap(state_partition);
617
618 static std::vector<typename std::list<block_type*>::iterator > blocks_to_move_to_front;
619 static std::vector<block_type*> new_blocks_to_move_to_front;
620 blocks_to_move_to_front.clear();
621 new_blocks_to_move_to_front.clear();
622 // for all blocks B in X_old
623 for (typename std::list<block_type*>::iterator block_iter = state_partition_old.begin();
624 block_iter != state_partition_old.end(); ++block_iter)
625 {
626 block_type* b_to_split = *block_iter;
627 block_type new_block;
628 new_block.is_in_new_blocks = false;
629 block_type temp_block;
630 block_type* new_block_ptr;
631 new_block.key = blocks.size();
632
633 // iterate over all states in the block to split
634 for (state_type s : b_to_split->states)
635 {
636 // if block b can reach step class <a,M> starting from state s then we move it to antoher block
637 if (step_class->prev_states[s] == true)
638 {
639 new_block.states.push_back(s);
640 }
641 else
642 {
643 temp_block.states.push_back(s);
644 }
645 }
646
647 // if both, the new block and temp block has elements, then we add a new block into the state partition
648 if (new_block.states.size() > 0 && temp_block.states.size() > 0)
649 {
650
651 // First update the block_index_of_a_state by iterating over all states of the new block
652 for (state_type s : new_block.states)
653 {
654 block_index_of_a_state[s] = new_block.key;
655 }
656
657 blocks.push_back(new_block);
658 new_block_ptr = &blocks.back();
659
660 // return states from temp_block to b_to_split
661 b_to_split->states.swap(temp_block.states);
662
663 // if the current block is not currently a new block then add the smaller
664 // block (between new block and current block) to the list of new blocks;
665 // hence, in front of state partition
666 if (b_to_split->is_in_new_blocks == false)
667 {
668 if (new_block_ptr->states.size() < b_to_split->states.size())
669 {
670 new_blocks_to_move_to_front.push_back(new_block_ptr);
671 new_block_ptr->is_in_new_blocks = true;
672 }
673 else
674 {
675 b_to_split->is_in_new_blocks = true;
676 blocks_to_move_to_front.push_back(block_iter);
677
678 // add new block to the back of the state_partition list
679 state_partition.push_back(new_block_ptr);
680 }
681 }
682 else
683 {
684 // the current block is already in new blocks; hence, just add the new block
685 // to the front of the partition as well.
686
687 new_block_ptr->is_in_new_blocks = true;
688
689 // add new block to the back of the state_partition list
690 new_blocks_to_move_to_front.push_back(new_block_ptr);
691 }
692 }
693 }
694
695 // move the remaning blocks to the end of state_partition
696 state_partition.splice(state_partition.begin(), state_partition_old);
697
698 // move the blocks that should be in new blocks to the begining of the list
699 for (const typename std::list<block_type*>::iterator& block_iter: blocks_to_move_to_front)
700 {
701 state_partition.splice(state_partition.begin(), state_partition, block_iter);
702 }
703
704 // move the blocks that are new to the front of state_partition
705 for (block_type* block_ptr : new_blocks_to_move_to_front)
706 {
707 state_partition.push_front(block_ptr);
708 }
709
710 }
711 }
712 }
713
715 {
716 //---- Post processing to keep track of the parent block of each state ----//
717 //block_index_of_a_state.resize(aut.num_states());
718 for (const block_type& b : blocks)
719 {
720 for (const state_type s : b.states)
721 {
722 block_index_of_a_state[s] = b.key;
723 }
724 }
725
726 step_class_index_of_a_distribution.resize(aut.num_probabilistic_states());
727 for (step_class_type& sc : step_classes)
728 {
729 sc.equivalent_step_class = sc.key;
730
731 for (const distribution_type* d : sc.distributions)
732 {
733 step_class_index_of_a_distribution[d->key] = sc.key;
734 }
735 }
736
737 // Merge equivalent step classes in step partition. The algorithm initially separates classes by actions,
738 // but it is possible that a probabilistic sate has multiple incoming actions, hence it will be repeated
739 // among multiple step classes
740 typename LTS_TYPE::probabilistic_state_t new_prob_state;
741 std::unordered_map<typename LTS_TYPE::probabilistic_state_t, std::vector<step_class_type*> > reduced_step_partition;
742
743 /* Iterate over all step classes of Step_partition*/
744 for (step_class_type& sc : step_classes)
745 {
746 if (sc.distributions.size() > 0)
747 {
748 typename LTS_TYPE::probabilistic_state_t new_prob_state = calculate_equivalent_probabilistic_state(sc);
749
750 /* Add the step class index to the new probability state*/
751 reduced_step_partition[new_prob_state].push_back(&sc);
752 }
753 }
754
755 step_class_key_type equivalent_class_key = 0;
756 for (typename std::unordered_map<typename LTS_TYPE::probabilistic_state_t,
757 std::vector<step_class_type*> >::iterator i = reduced_step_partition.begin();
758 i != reduced_step_partition.end(); ++i)
759 {
760 std::vector<step_class_type*>& sc_vector = i->second;
761
762 equivalent_step_classes.push_back(*sc_vector[0]);
763 for (step_class_type* sc :sc_vector)
764 {
765 sc->equivalent_step_class = equivalent_class_key;
766 }
767 equivalent_class_key++;
768 }
769
770 }
771};
772
773
774/** \brief Reduce transition system l with respect to probabilistic bisimulation.
775 * \param[in/out] l The transition system that is reduced.
776 * \param[in/out] timer A timer that can be used to report benchmarking results.
777 */
778template < class LTS_TYPE>
780
781/** \brief Checks whether the two initial states of two plts's are probabilistic bisimilar.
782* \details This lts and the lts l2 are not usable anymore after this call.
783* \param[in/out] l1 A first probabilistic transition system.
784* \param[in/out] l2 A second probabilistic transition system.
785* \retval True iff the initial states of the current transition system and l2 are probabilistic bisimilar */
786template < class LTS_TYPE>
788
789
790/** \brief Checks whether the two initial states of two plts's are probabilistic bisimilar.
791* \details The current transitions system and the lts l2 are first duplicated and subsequently
792* reduced modulo bisimulation. If memory space is a concern, one could consider to
793* use destructive_bisimulation_compare.
794* \param[in/out] l1 A first transition system.
795* \param[in/out] l2 A second transistion system.
796* \retval True iff the initial states of the current transition system and l2 are probabilistic bisimilar */
797template < class LTS_TYPE>
798bool probabilistic_bisimulation_compare_bem(const LTS_TYPE& l1, const LTS_TYPE& l2, utilities::execution_timer& timer);
799
800
801template < class LTS_TYPE>
803{
804
805 // Apply the probabilistic bisimulation reduction algorithm.
806 detail::prob_bisim_partitioner_bem<LTS_TYPE> prob_bisim_part(l,timer);
807
808 // Clear the state labels of the LTS l
809 l.clear_state_labels();
810
811 // Assign the reduced LTS
812 l.set_num_states(prob_bisim_part.num_eq_classes());
813 prob_bisim_part.replace_transitions();
814 prob_bisim_part.replace_probabilistic_states();
815}
816
817template < class LTS_TYPE>
819 const LTS_TYPE& l1,
820 const LTS_TYPE& l2,
822{
823 LTS_TYPE l1_copy(l1);
824 LTS_TYPE l2_copy(l2);
825 return destructive_probabilistic_bisimulation_compare_bem(l1_copy, l2_copy, timer);
826}
827
828template < class LTS_TYPE>
830 LTS_TYPE& l1,
831 LTS_TYPE& l2,
833{
834 std::size_t initial_probabilistic_state_key_l1;
835 std::size_t initial_probabilistic_state_key_l2;
836
837 // Merge states
838 mcrl2::lts::detail::plts_merge(l1, l2);
839 l2.clear(); // No use for l2 anymore.
840
841 // The last two probabilistic states are the initial states of l2 and l1
842 // in the merged plts
843 initial_probabilistic_state_key_l2 = l1.num_probabilistic_states() - 1;
844 initial_probabilistic_state_key_l1 = l1.num_probabilistic_states() - 2;
845
846 detail::prob_bisim_partitioner_bem<LTS_TYPE> prob_bisim_part(l1, timer);
847
848 return prob_bisim_part.in_same_probabilistic_class(initial_probabilistic_state_key_l2,
849 initial_probabilistic_state_key_l1);
850}
851
852} // namespace mcrl2::lts::detail
853
854#endif // MCRL2_LTS_DETAIL_LIBLTS_PBISIM_BEM_H
function object to compare two constln_t pointers based on their contents
std::size_t num_eq_classes() const
Gives the number of bisimulation equivalence classes of the LTS.
std::size_t get_eq_step_class(const std::size_t d) const
Gives the bisimulation equivalence step class number of a probabilistic state.
LTS_TYPE::probabilistic_state_t calculate_new_probabilistic_state(typename LTS_TYPE::probabilistic_state_t ps)
std::vector< block_key_type > block_index_of_a_state
std::vector< step_class_key_type > step_class_index_of_a_distribution
void refine_partition_until_it_becomes_stable()
Two-phased partitioning algorithm described in page 204. Fig 9. Baier. \detail Refinement of state pa...
probability_fraction_type probability_to_block(distribution_type &d, block_type &b)
Calculates the probability to reach block b from distribution d.
bool in_same_probabilistic_class(const std::size_t s, const std::size_t t) const
Returns whether two states are in the same probabilistic bisimulation equivalence class.
void create_initial_partition()
Creates the initial partition of step classes and blocks. \detail The blocks are initially partitione...
std::size_t get_eq_class(const std::size_t s) const
Gives the bisimulation equivalence class number of a state.
prob_bisim_partitioner_bem(LTS_TYPE &l, utilities::execution_timer &timer)
Creates a probabilistic bisimulation partitioner for an PLTS.
std::deque< step_class_type > equivalent_step_classes
void replace_transitions()
Replaces the transition relation of the current lts by the transitions of the bisimulation reduced tr...
LTS_TYPE::probabilistic_state_t calculate_equivalent_probabilistic_state(step_class_type &sc)
std::vector< distribution_type > distributions
void replace_probabilistic_states()
Replaces the probabilistic states of the current lts by the probabilistic states of the bisimulation ...
Simple timer to time the CPU time used by a piece of code.
#define mCRL2log(LEVEL)
mCRL2log(LEVEL) provides the stream used to log.
Definition logger.h:393
bool probabilistic_bisimulation_compare_bem(const LTS_TYPE &l1, const LTS_TYPE &l2, utilities::execution_timer &timer)
Checks whether the two initial states of two plts's are probabilistic bisimilar.
bool destructive_probabilistic_bisimulation_compare_bem(LTS_TYPE &l1, LTS_TYPE &l2, utilities::execution_timer &timer)
Checks whether the two initial states of two plts's are probabilistic bisimilar.
void probabilistic_bisimulation_reduce_bem(LTS_TYPE &l, utilities::execution_timer &timer)
Reduce transition system l with respect to probabilistic bisimulation.