mCRL2
Loading...
Searching...
No Matches
lts_algorithm.h
Go to the documentation of this file.
1// Author(s): Muck van Weerdenburg, Jan Friso Groote
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
10/** \file
11 *
12 * \brief Algorithms for LTS, such as equivalence reductions, determinisation, etc.
13 * \details This contains the main algorithms useful to manipulate with
14 * labelled transition systems. Typically, it contains algorithms for bisimulation
15 * reduction, removal of tau loops, making an lts deterministic etc.
16 * \author Jan Friso Groote, Bas Ploeger, Muck van Weerdenburg
17 */
18
19#ifndef MCRL2_LTS_LTS_ALGORITHM_H
20#define MCRL2_LTS_LTS_ALGORITHM_H
21
22#include "mcrl2/lts/detail/liblts_bisim.h"
23#include "mcrl2/lts/detail/liblts_bisim_minimal_depth.h"
24#include "mcrl2/lts/detail/liblts_bisim_gjkw.h"
25#include "mcrl2/lts/detail/liblts_bisim_gj.h"
26#include "mcrl2/lts/detail/liblts_bisim_gj_lazy_BLC.h"
27#include "mcrl2/lts/detail/liblts_branching_bisim_minimal_depth.h"
28#include "mcrl2/lts/detail/liblts_weak_bisim.h"
29#include "mcrl2/lts/detail/liblts_add_an_action_loop.h"
30#include "mcrl2/lts/detail/liblts_ready_sim.h"
31#include "mcrl2/lts/detail/liblts_failures_refinement.h"
32#include "mcrl2/lts/detail/liblts_coupledsim.h"
33#include "mcrl2/lts/detail/liblts_impossible_futures.h"
34#include "mcrl2/lts/detail/tree_set.h"
35#include "mcrl2/lts/lts_equivalence.h"
36#include "mcrl2/lts/lts_preorder.h"
37#include "mcrl2/lts/sigref.h"
38#include "mcrl2/utilities/exception.h"
39
40namespace mcrl2::lts
41{
42
43/** \brief Applies a reduction algorithm to this LTS.
44 * \param[in] l A labelled transition system that must be reduced.
45 * \param[in] eq The equivalence with respect to which the LTS will be
46 * reduced.
47 **/
48template <class LTS_TYPE>
49void reduce(LTS_TYPE& l, lts_equivalence eq);
50
51/** \brief Checks whether this LTS is equivalent to another LTS.
52 * \param[in] l1 The first LTS that will be compared.
53 * \param[in] l2 The second LTS that will be compared.
54 * \param[in] eq The equivalence with respect to which the LTSs will be
55 * compared.
56 * \param[in] generate_counter_examples Whether to generate a counter example
57 * \param[in] counter_example_file The file to store the counter example in
58 * \retval true if the LTSs are found to be equivalent.
59 * \retval false otherwise.
60 * \warning This function alters the internal data structure of
61 * both LTSs for efficiency reasons. After comparison, this LTS is
62 * equivalent to the original LTS by equivalence \a eq, and
63 * similarly for the LTS \a l.
64 */
65template <class LTS_TYPE>
66bool destructive_compare(LTS_TYPE& l1,
67 LTS_TYPE& l2,
68 const lts_equivalence eq,
69 const bool generate_counter_examples = false,
70 const std::string& counter_example_file = std::string(),
71 const bool structured_output = false)
72{
73 // Merge this LTS and l and store the result in this LTS.
74 // In the resulting LTS, the initial state i of l will have the
75 // state number i + N where N is the number of states in this
76 // LTS (before the merge).
77
78 switch (eq)
79 {
80 case lts_eq_none:
81 return false;
82 case lts_eq_bisim_jgkw:
83 {
84 return detail::destructive_bisimulation_compare_dnj(l1,l2, false,false,generate_counter_examples,counter_example_file,structured_output);
85 }
86 case lts_eq_bisim_gv:
87 {
88 return detail::destructive_bisimulation_compare(l1,l2, false,false,generate_counter_examples,counter_example_file,structured_output);
89 }
90 case lts_eq_bisim_gjkw:
91 {
92 return detail::destructive_bisimulation_compare_gjkw(l1,l2, false,false,generate_counter_examples,counter_example_file,structured_output);
93 }
94 case lts_eq_bisim:
95 {
96 if (generate_counter_examples)
97 {
98 mCRL2log(mcrl2::log::warning) << "A slower partition refinement algorithm is used to generate minimal-depth counter examples.\n";
99 return detail::destructive_bisimulation_compare_minimal_depth(l1, l2, counter_example_file);
100 }
101 return detail::destructive_bisimulation_compare_gj(l1,l2, false,false,generate_counter_examples,counter_example_file,structured_output);
102 }
103 case lts_eq_bisim_gj:
104 {
105 return detail::destructive_bisimulation_compare_gj(l1,l2, false,false,generate_counter_examples,counter_example_file,structured_output);
106 }
107 case lts_eq_bisim_gj_lazy_BLC:
108 {
109 return detail::destructive_bisimulation_compare_gj_lazy_BLC(l1,l2, false,false,generate_counter_examples,counter_example_file,structured_output);
110 }
111 case lts_eq_branching_bisim_jgkw:
112 {
113 return detail::destructive_bisimulation_compare_dnj(l1,l2, true,false,generate_counter_examples,counter_example_file,structured_output);
114 }
115 case lts_eq_branching_bisim_gv:
116 {
117 return detail::destructive_bisimulation_compare(l1,l2, true,false,generate_counter_examples,counter_example_file,structured_output);
118 }
119 case lts_eq_branching_bisim_gjkw:
120 {
121 return detail::destructive_bisimulation_compare_gjkw(l1,l2, true,false,generate_counter_examples,counter_example_file,structured_output);
122 }
123 case lts_eq_branching_bisim:
124 {
125 if (generate_counter_examples)
126 {
127 mCRL2log(mcrl2::log::warning) << "The default branching bisimulation comparison algorithm cannot generate counter examples. A slower partition refinement algorithm (Martens/Groote 2024) is used instead.\n";
128 return detail::destructive_branching_bisimulation_compare_minimal_depth(l1, l2, counter_example_file);
129 }
130 return detail::destructive_bisimulation_compare_gj(l1,l2, true,false,generate_counter_examples,counter_example_file,structured_output);
131 }
132 case lts_eq_branching_bisim_gj:
133 {
134 return detail::destructive_bisimulation_compare_gj(l1,l2, true,false,generate_counter_examples,counter_example_file,structured_output);
135 }
136 case lts_eq_branching_bisim_gj_lazy_BLC:
137 {
138 return detail::destructive_bisimulation_compare_gj_lazy_BLC(l1,l2, true,false,generate_counter_examples,counter_example_file,structured_output);
139 }
140 case lts_eq_divergence_preserving_branching_bisim_jgkw:
141 {
142 return detail::destructive_bisimulation_compare_dnj(l1,l2, true,true,generate_counter_examples,counter_example_file,structured_output);
143 }
144 case lts_eq_divergence_preserving_branching_bisim_gv:
145 {
146 return detail::destructive_bisimulation_compare(l1,l2, true,true,generate_counter_examples,counter_example_file,structured_output);
147 }
148 case lts_eq_divergence_preserving_branching_bisim_gjkw:
149 {
150 return detail::destructive_bisimulation_compare_gjkw(l1,l2, true,true,generate_counter_examples,counter_example_file,structured_output);
151 }
152 case lts_eq_divergence_preserving_branching_bisim:
153 {
154 if (generate_counter_examples)
155 {
156 mCRL2log(mcrl2::log::warning) << "The default divergence-preserving branching bisimulation comparison algorithm cannot generate counter examples. Therefore the slower gv algorithm is used instead.\n";
157 return detail::destructive_bisimulation_compare(l1,l2, true,true,generate_counter_examples,counter_example_file,structured_output);
158 }
159 return detail::destructive_bisimulation_compare_gj(l1,l2, true,true,generate_counter_examples,counter_example_file,structured_output);
160 }
161 case lts_eq_divergence_preserving_branching_bisim_gj:
162 {
163 return detail::destructive_bisimulation_compare_gj(l1,l2, true,true,generate_counter_examples,counter_example_file,structured_output);
164 }
165 case lts_eq_divergence_preserving_branching_bisim_gj_lazy_BLC:
166 {
167 return detail::destructive_bisimulation_compare_gj_lazy_BLC(l1,l2, true,true,generate_counter_examples,counter_example_file,structured_output);
168 }
169 case lts_eq_weak_bisim:
170 {
171 if (generate_counter_examples)
172 {
173 mCRL2log(log::warning) << "Cannot generate counter examples for weak bisimulation\n";
174 }
175 return detail::destructive_weak_bisimulation_compare(l1,l2,false);
176 }
177 case lts_eq_divergence_preserving_weak_bisim:
178 {
179 if (generate_counter_examples)
180 {
181 mCRL2log(log::warning) << "Cannot generate counter examples for divergence-preserving weak bisimulation\n";
182 }
183 return detail::destructive_weak_bisimulation_compare(l1,l2, true);
184 }
185 case lts_eq_sim:
186 {
187 if (generate_counter_examples)
188 {
189 mCRL2log(log::warning) << "Cannot generate counter examples for simulation equivalence\n";
190 }
191 // Run the partitioning algorithm on this merged LTS
192 std::size_t init_l2 = l2.initial_state() + l1.num_states();
193 detail::merge(l1,l2);
194 l2.clear(); // l2 is not needed anymore.
195 detail::sim_partitioner<LTS_TYPE> sp(l1);
196 sp.partitioning_algorithm();
197
198 return sp.in_same_class(l1.initial_state(),init_l2);
199 }
200 case lts_eq_ready_sim:
201 {
202 if (generate_counter_examples)
203 {
204 mCRL2log(log::warning) << "Cannot generate counter examples for ready-simulation equivalence\n";
205 }
206 // Run the partitioning algorithm on this merged LTS
207 std::size_t init_l2 = l2.initial_state() + l1.num_states();
208 detail::merge(l1,l2);
209 l2.clear(); // l2 is not needed anymore.
210 detail::ready_sim_partitioner<LTS_TYPE> rsp(l1);
211 rsp.partitioning_algorithm();
212
213 return rsp.in_same_class(l1.initial_state(),init_l2);
214 }
215 case lts_eq_trace:
216 {
217 // Determinise first LTS
218 detail::bisimulation_reduce_gj(l1, false);
219 determinise(l1);
220
221 // Determinise second LTS
222 detail::bisimulation_reduce_gj(l2, false);
223 determinise(l2);
224
225 // Trace equivalence now corresponds to bisimilarity
226 if (generate_counter_examples)
227 {
228 return detail::destructive_bisimulation_compare_minimal_depth(l1, l2, counter_example_file);
229 }
230 return detail::destructive_bisimulation_compare(l1, l2, false, false, generate_counter_examples, counter_example_file, structured_output);
231 }
232 case lts_eq_weak_trace:
233 {
234 if (generate_counter_examples)
235 {
236 detail::bisimulation_reduce_gj(l1,true,false); // Branching bisimulation reduction.
237 detail::tau_star_reduce(l1);
238 detail::bisimulation_reduce_gj(l1,false);
239 determinise(l1);
240 detail::bisimulation_reduce_gj(l2,true,false);
241 detail::tau_star_reduce(l2);
242 detail::bisimulation_reduce_gj(l2,false);
243 determinise(l2);
244 return detail::destructive_branching_bisimulation_compare_minimal_depth(l1, l2, counter_example_file);
245 }
246
247 // Eliminate silent steps and determinise first LTS
248 detail::bisimulation_reduce_gj(l1,true,false);
249 detail::tau_star_reduce(l1);
250 detail::bisimulation_reduce_gj(l1,false);
251 determinise(l1);
252
253 // Eliminate silent steps and determinise second LTS
254 detail::bisimulation_reduce_gj(l2,true,false);
255 detail::tau_star_reduce(l2);
256 detail::bisimulation_reduce_gj(l2,false);
257 determinise(l2);
258
259 // Weak trace equivalence now corresponds to bisimilarity
260 return detail::destructive_bisimulation_compare(l1,l2,false,false,false,counter_example_file,structured_output);
261 }
262 case lts_eq_coupled_sim:
263 {
264 return detail::coupled_simulation_compare(l1,l2);
265 }
266 default:
267 throw mcrl2::runtime_error("Comparison for this equivalence is not available");
268 return false;
269 }
270}
271
272/** \brief Checks whether this LTS is equivalent to another LTS.
273 * \details The input labelled transition systems are duplicated in memory to carry
274 * out the comparison. When space efficiency is a concern, one can consider
275 * to use destructive_compare.
276 * \param[in] l1 The first LTS to compare.
277 * \param[in] l2 The second LTS to compare.
278 * \param[in] eq The equivalence with respect to which the LTSs will be
279 * compared.
280 * \param[in] generate_counter_examples Whether to generate a counter example
281 * \param[in] counter_example_file The file to store the counter example in
282 * \retval true if the LTSs are found to be equivalent.
283 * \retval false otherwise.
284 */
285template<class LTS_TYPE>
286bool compare(const LTS_TYPE& l1,
287 const LTS_TYPE& l2,
288 lts_equivalence eq,
289 bool generate_counter_examples = false,
290 const std::string& counter_example_file = "",
291 bool structured_output = false);
292
293/** \brief Checks whether this LTS is smaller than another LTS according
294 * to a preorder.
295 * \param[in] l1 The first LTS to be compared.
296 * \param[in] l2 The second LTS to be compared.
297 * \param[in] pre The preorder with respect to which the LTSs will be
298 * compared.
299 * \param[in] generate_counter_examples Whether to generate a counter example
300 * \param[in] counter_example_file The file to store the counter example in
301 * \param[in] strategy Choose breadth-first or depth-first for exploration strategy
302 * of the antichain algorithms.
303 * \param[in] preprocess Whether to allow preprocessing of the given LTSs.
304 * \retval true if LTS \a l1 is smaller than LTS \a l2 according to
305 * preorder \a pre.
306 * \retval false otherwise.
307 * \warning This function alters the internal data structure of
308 * both LTSs for efficiency reasons. After comparison, this LTS is
309 * equivalent to the original LTS by equivalence \a eq, and
310 * similarly for the LTS \a l, where \a eq is the equivalence
311 * induced by the preorder \a pre (i.e. \f$eq = pre \cap
312 * pre^{-1}\f$).
313 */
314template<class LTS_TYPE>
315bool destructive_compare(LTS_TYPE& l1,
316 LTS_TYPE& l2,
317 lts_preorder pre,
318 bool generate_counter_example,
319 const std::string& counter_example_file = "",
320 bool structured_output = false,
321 lps::exploration_strategy strategy = lps::es_breadth,
322 bool preprocess = true);
323
324/** \brief Checks whether this LTS is smaller than another LTS according
325 * to a preorder.
326 * \param[in] l1 The first LTS to be compared.
327 * \param[in] l2 The second LTS to be compared.
328 * \param[in] pre The preorder with respect to which the LTSs will be compared.
329 * \param[in] generate_counter_examples Whether to generate a counter example
330 * \param[in] counter_example_file The file to store the counter example in
331 * \param[in] strategy Choose breadth-first or depth-first for exploration strategy
332 * of the antichain algorithms.
333 * \param[in] preprocess Whether to allow preprocessing of the given LTSs.
334 * \retval true if this LTS is smaller than LTS \a l according to
335 * preorder \a pre.
336 * \retval false otherwise.
337 */
338template<class LTS_TYPE>
339bool compare(const LTS_TYPE& l1,
340 const LTS_TYPE& l2,
341 lts_preorder pre,
342 bool generate_counter_example,
343 const std::string& counter_example_file = "",
344 bool structured_output = false,
345 lps::exploration_strategy strategy = lps::es_breadth,
346 bool preprocess = true);
347
348/** \brief Determinises this LTS. */
349template <class LTS_TYPE>
350void determinise(LTS_TYPE& l);
351
352
353/** \brief Checks whether all states in this LTS are reachable
354 * from the initial state and remove unreachable states if required.
355 * \details Runs in O(num_states * num_transitions) time.
356 * \param[in] l The LTS on which reachability is checked.
357 * \param[in] remove_unreachable Indicates whether all unreachable states
358 * should be removed from the LTS. This option does not
359 * influence the return value; the return value is with
360 * respect to the original LTS.
361 * \retval true if all states are reachable from the initial state;
362 * \retval false otherwise. */
363template <class SL, class AL, class BASE>
364bool reachability_check(lts < SL, AL, BASE>& l, bool remove_unreachable = false)
365{
366 // First calculate which states can be reached, and store this in the array visited.
367 const outgoing_transitions_per_state_t out_trans(l.get_transitions(),l.num_states(),true);
368
369 std::vector < bool > visited(l.num_states(),false);
370 std::stack<std::size_t> todo;
371
372 visited[l.initial_state()]=true;
373 todo.push(l.initial_state());
374
375 while (!todo.empty())
376 {
377 std::size_t state_to_consider=todo.top();
378 todo.pop();
379 for (detail::state_type i=out_trans.lowerbound(state_to_consider); i<out_trans.upperbound(state_to_consider); ++i)
380 {
381 const outgoing_pair_t& p=out_trans.get_transitions()[i];
382 assert(visited[state_to_consider] && state_to_consider<l.num_states() && to(p)<l.num_states());
383 if (!visited[to(p)])
384 {
385 visited[to(p)]=true;
386 todo.push(to(p));
387 }
388 }
389 }
390
391 // Property: in_visited(s) == true: state s is reachable from the initial state
392
393 // check to see if all states are reachable from the initial state, i.e.
394 // whether all bits are set.
395 bool all_reachable = find(visited.begin(),visited.end(),false)==visited.end();
396
397 if (!all_reachable && remove_unreachable)
398 {
399 // Remove all unreachable states, transitions from such states and labels
400 // that are only used in these transitions.
401
402 std::vector < detail::state_type > state_map(l.num_states());
403 std::vector < detail::state_type > label_map(l.num_action_labels());
404
405 lts < SL, AL, BASE> new_lts=l; // In this way set data specification and action declarations in the new lts.
406 new_lts.clear();
407
408 std::size_t new_nstates = 0;
409 for (std::size_t i=0; i<l.num_states(); i++)
410 {
411 if (visited[i])
412 {
413 state_map[i] = new_nstates;
414 if (l.has_state_info())
415 {
416 new_lts.add_state(l.state_label(i));
417 }
418 else
419 {
420 new_lts.add_state();
421 }
422 new_nstates++;
423 }
424 }
425
426 for (const transition& t: l.get_transitions())
427 {
428 if (visited[t.from()])
429 {
430 label_map[t.label()] = 1;
431 }
432 }
433
434 label_map[0]=1; // Declare the tau action explicitly present.
435 std::size_t new_nlabels = 0;
436 for (std::size_t i=0; i<l.num_action_labels(); i++)
437 {
438 if (label_map[i]>0) // Label i is used.
439 {
440 label_map[i] = new_nlabels;
441 new_lts.add_action(l.action_label(i));
442 new_nlabels++;
443 }
444 }
445
446 for (const transition& t: l.get_transitions())
447 {
448 if (visited[t.from()])
449 {
450 new_lts.add_transition(transition(state_map[t.from()],label_map[t.label()],state_map[t.to()]));
451 }
452 }
453
454 new_lts.set_initial_state(state_map.at(l.initial_state()));
455 l.swap(new_lts);
456 }
457
458 return all_reachable;
459}
460
461/** \brief Checks whether all states in a probabilistic LTS are reachable
462 * from the initial state and remove unreachable states if required.
463 * \details Runs in O(num_states * num_transitions) time.
464 * \param[in] l The LTS on which reachability is checked.
465 * \param[in] remove_unreachable Indicates whether all unreachable states
466 * should be removed from the LTS. This option does not
467 * influence the return value; the return value is with
468 * respect to the original LTS.
469 * \retval true if all states are reachable from the initial state;
470 * \retval false otherwise. */
471template <class SL, class AL, class PROBABILISTIC_STATE, class BASE>
472bool reachability_check(probabilistic_lts < SL, AL, PROBABILISTIC_STATE, BASE>& l, bool remove_unreachable = false)
473{
474 // First calculate which states can be reached, and store this in the array visited.
475 const outgoing_transitions_per_state_t out_trans(l.get_transitions(),l.num_states(),true);
476
477 std::vector < bool > visited(l.num_states(),false);
478 std::stack<std::size_t> todo;
479
480 if (l.initial_probabilistic_state().size()>1) // Target states are in a probabilistic vector.
481 {
482 for(const typename PROBABILISTIC_STATE::state_probability_pair& s: l.initial_probabilistic_state())
483 {
484 visited[s.state()]=true;
485 todo.push(s.state());
486 }
487 }
488 else // it is a singular state;
489 {
490 const typename PROBABILISTIC_STATE::state_t sn=l.initial_probabilistic_state().get();
491 visited[sn]=true;
492 todo.push(sn);
493 }
494
495 while (!todo.empty())
496 {
497 std::size_t state_to_consider=todo.top();
498 todo.pop();
499 for (detail::state_type i=out_trans.lowerbound(state_to_consider); i<out_trans.upperbound(state_to_consider); ++i)
500 {
501 const outgoing_pair_t& p=out_trans.get_transitions()[i];
502 assert(visited[state_to_consider] && state_to_consider<l.num_states() && to(p)<l.num_probabilistic_states());
503 // Walk through the the states in this probabilistic state.
504 if (l.probabilistic_state(to(p)).size()>1) // Target states are in a probabilistic vector.
505 {
506 for(const typename PROBABILISTIC_STATE::state_probability_pair& pr: l.probabilistic_state(to(p)))
507 {
508 if (!visited[pr.state()])
509 {
510 visited[pr.state()]=true;
511 todo.push(pr.state());
512 }
513 }
514 }
515 else // it is a singular state;
516 {
517 const typename PROBABILISTIC_STATE::state_t sn=l.probabilistic_state(to(p)).get();
518 if (!visited[sn])
519 {
520 visited[sn]=true;
521 todo.push(sn);
522 }
523 }
524 }
525 }
526
527 // Property: in_visited(s) == true: state s is reachable from the initial state
528
529 // check to see if all states are reachable from the initial state, i.e.
530 // whether all bits are set.
531 bool all_reachable = find(visited.begin(),visited.end(),false)==visited.end();
532
533 if (!all_reachable && remove_unreachable)
534 {
535 // Remove all unreachable states, transitions from such states and labels
536 // that are only used in these transitions.
537
538 std::vector < detail::state_type > state_map(l.num_states());
539 std::vector < detail::state_type > label_map(l.num_action_labels());
540
541 probabilistic_lts < SL, AL, PROBABILISTIC_STATE, BASE> new_lts=l; // In this way set data specification and action declarations in the new lts.
542 new_lts.clear();
543
544 std::size_t new_nstates = 0;
545 for (std::size_t i=0; i<l.num_states(); i++)
546 {
547 if (visited[i])
548 {
549 state_map[i] = new_nstates;
550 if (l.has_state_info())
551 {
552 new_lts.add_state(l.state_label(i));
553 }
554 else
555 {
556 new_lts.add_state();
557 }
558 new_nstates++;
559 }
560 }
561
562 for (const transition& t: l.get_transitions())
563 {
564 if (visited[t.from()])
565 {
566 label_map[t.label()] = 1;
567 }
568 }
569
570 label_map[0]=1; // Declare the tau action explicitly present.
571 std::size_t new_nlabels = 0;
572 for (std::size_t i=0; i<l.num_action_labels(); i++)
573 {
574 if (label_map[i]>0) // Label i is used.
575 {
576 label_map[i] = new_nlabels;
577 new_lts.add_action(l.action_label(i));
578 new_nlabels++;
579 }
580 }
581
582 for (const transition& t: l.get_transitions())
583 {
584 if (visited[t.from()])
585 {
586 new_lts.add_transition(transition(state_map[t.from()],label_map[t.label()],t.to()));
587 }
588 }
589
590 PROBABILISTIC_STATE new_initial_state;
591 for (std::size_t i=0; i<l.num_probabilistic_states(); ++i)
592 {
593 new_initial_state.clear();
594 if (l.probabilistic_state(i).size()==0)
595 {
596 new_initial_state.set(state_map[l.probabilistic_state(i).get()]);
597 }
598 else
599 {
600 for(const typename PROBABILISTIC_STATE::state_probability_pair& s: l.probabilistic_state(i))
601 {
602 new_initial_state.add(state_map[s.state()], s.probability());
603 }
604 }
605 new_lts.add_probabilistic_state(new_initial_state);
606 }
607
608 new_initial_state.clear();
609 if (l.initial_probabilistic_state().size()==0)
610 {
611 new_initial_state.set(state_map[l.initial_probabilistic_state().get()]);
612 }
613 else
614 {
615 for(const typename PROBABILISTIC_STATE::state_probability_pair& s: l.initial_probabilistic_state())
616 {
617 new_initial_state.add(state_map[s.state()], s.probability());
618 }
619 }
620 new_lts.set_initial_probabilistic_state(new_initial_state);
621 l.swap(new_lts);
622 }
623
624 return all_reachable;
625}
626
627/** \brief Checks whether this LTS is deterministic.
628 * \retval true if this LTS is deterministic;
629 * \retval false otherwise. */
630template <class LTS_TYPE>
631bool is_deterministic(const LTS_TYPE& l);
632
633/** \brief Merge the second lts into the first lts.
634 \param[in,out] l1 The transition system in which l2 is merged.
635 \param[in] l2 The second transition system, which remains unchanged
636 */
637template <class LTS_TYPE>
638void merge(LTS_TYPE& l1, const LTS_TYPE& l2)
639{
640 detail::merge(l1,l2);
641}
642
643
644/* Here the implementations of the declared functions above are given.
645 Originally these were in a .cpp file, before lts's were templated */
646
647
648
649template <class LTS_TYPE>
650void reduce(LTS_TYPE& l,lts_equivalence eq)
651{
652
653 switch (eq)
654 {
655 case lts_eq_none:
656 return;
657 case lts_eq_bisim:
658 {
659 detail::bisimulation_reduce_gj(l,false,false);
660 return;
661 }
662 case lts_eq_bisim_gv:
663 {
664 detail::bisimulation_reduce(l,false,false);
665 return;
666 }
667 case lts_eq_bisim_gjkw:
668 {
669 detail::bisimulation_reduce_gjkw(l,false,false);
670 return;
671 }
672 case lts_eq_bisim_jgkw:
673 {
674 detail::bisimulation_reduce_dnj(l,false,false);
675 return;
676 }
677 case lts_eq_bisim_gj:
678 {
679 detail::bisimulation_reduce_gj(l,false,false);
680 return;
681 }
682 case lts_eq_bisim_gj_lazy_BLC:
683 {
684 detail::bisimulation_reduce_gj_lazy_BLC(l,false,false);
685 return;
686 }
687 case lts_eq_bisim_sigref:
688 {
689 sigref<LTS_TYPE, signature_bisim<LTS_TYPE> > s(l);
690 s.run();
691 return;
692 }
693 case lts_eq_branching_bisim:
694 {
695 detail::bisimulation_reduce_gj(l,true,false);
696 return;
697 }
698 case lts_eq_branching_bisim_gv:
699 {
700 detail::bisimulation_reduce(l,true,false);
701 return;
702 }
703 case lts_eq_branching_bisim_gjkw:
704 {
705 detail::bisimulation_reduce_gjkw(l,true,false);
706 return;
707 }
708 case lts_eq_branching_bisim_jgkw:
709 {
710 detail::bisimulation_reduce_dnj(l,true,false);
711 return;
712 }
713 case lts_eq_branching_bisim_gj:
714 {
715 detail::bisimulation_reduce_gj(l,true,false);
716 return;
717 }
718 case lts_eq_branching_bisim_gj_lazy_BLC:
719 {
720 detail::bisimulation_reduce_gj_lazy_BLC(l,true,false);
721 return;
722 }
723 case lts_eq_branching_bisim_sigref:
724 {
725 sigref<LTS_TYPE, signature_branching_bisim<LTS_TYPE> > s(l);
726 s.run();
727 return;
728 }
729 case lts_eq_divergence_preserving_branching_bisim:
730 {
731 detail::bisimulation_reduce_gj(l,true,true);
732 return;
733 }
734 case lts_eq_divergence_preserving_branching_bisim_gv:
735 {
736 detail::bisimulation_reduce(l,true,true);
737 return;
738 }
739 case lts_eq_divergence_preserving_branching_bisim_gjkw:
740 {
741 detail::bisimulation_reduce_gjkw(l,true,true);
742 return;
743 }
744 case lts_eq_divergence_preserving_branching_bisim_jgkw:
745 {
746 detail::bisimulation_reduce_dnj(l,true,true);
747 return;
748 }
749 case lts_eq_divergence_preserving_branching_bisim_gj:
750 {
751 detail::bisimulation_reduce_gj(l,true,true);
752 return;
753 }
754 case lts_eq_divergence_preserving_branching_bisim_gj_lazy_BLC:
755 {
756 detail::bisimulation_reduce_gj_lazy_BLC(l,true,true);
757 return;
758 }
759 case lts_eq_divergence_preserving_branching_bisim_sigref:
760 {
762 s.run();
763 return;
764 }
765 case lts_eq_weak_bisim:
766 {
767 detail::weak_bisimulation_reduce(l,false);
768 return;
769 }
770 /*
771 case lts_eq_weak_bisim_sigref:
772 {
773 {
774 sigref<LTS_TYPE, signature_branching_bisim<LTS_TYPE> > s1(l);
775 s1.run();
776 }
777 detail::reflexive_transitive_tau_closure(l);
778 {
779 sigref<LTS_TYPE, signature_bisim<LTS_TYPE> > s2(l);
780 s2.run();
781 }
782 scc_reduce(l); // Remove tau loops
783 return;
784 }
785 */
786 case lts_eq_divergence_preserving_weak_bisim:
787 {
788 detail::weak_bisimulation_reduce(l,true);
789 return;
790 }
791 case lts_eq_sim:
792 {
793 // Run the partitioning algorithm on this LTS
794 detail::sim_partitioner<LTS_TYPE> sp(l);
795 sp.partitioning_algorithm();
796
797 // Clear this LTS, but keep the labels
798 // l.clear_type();
799 l.clear_state_labels();
800 l.clear_transitions();
801
802 // Assign the reduced LTS
803 l.set_num_states(sp.num_eq_classes());
804 l.set_initial_state(sp.get_eq_class(l.initial_state()));
805
806 const std::vector <transition> trans=sp.get_transitions();
807 l.clear_transitions();
808 for (const transition& t: trans)
809 {
810 l.add_transition(t);
811 }
812 // Remove unreachable parts
813
814 reachability_check(l,true);
815
816 return;
817 }
818 case lts_eq_ready_sim:
819 {
820 // Run the partitioning algorithm on this LTS
821 detail::ready_sim_partitioner<LTS_TYPE> rsp(l);
822 rsp.partitioning_algorithm();
823
824 // Clear this LTS, but keep the labels
825 // l.clear_type();
826 l.clear_state_labels();
827 l.clear_transitions();
828
829 // Assign the reduced LTS
830 l.set_num_states(rsp.num_eq_classes());
831 l.set_initial_state(rsp.get_eq_class(l.initial_state()));
832
833 const std::vector <transition> trans=rsp.get_transitions();
834 l.clear_transitions();
835 for (const transition& t: trans)
836 {
837 l.add_transition(t);
838 }
839 // Remove unreachable parts
840
841 reachability_check(l,true);
842
843 return;
844 }
845 case lts_eq_trace:
846 detail::bisimulation_reduce_gj(l,false);
847 determinise(l);
848 detail::bisimulation_reduce_gj(l,false);
849 return;
850 case lts_eq_weak_trace:
851 {
852 detail::bisimulation_reduce_gj(l,true,false);
853 detail::tau_star_reduce(l);
854 detail::bisimulation_reduce_gj(l,false);
855 determinise(l);
856 detail::bisimulation_reduce_gj(l,false);
857 return;
858 }
859 case lts_red_tau_star:
860 {
861 detail::bisimulation_reduce_gj(l,true,false);
862 detail::tau_star_reduce(l);
863 detail::bisimulation_reduce_gj(l,false);
864 return;
865 }
866 case lts_red_determinisation:
867 {
868 determinise(l);
869 return;
870 }
871 case lts_red_tau_scc:
872 {
873 scc_reduce(l);
874 return;
875 }
876 // default:
877 // throw mcrl2::runtime_error("Unknown reduction method.");
878 }
879}
880
881template <class LTS_TYPE>
882bool compare(const LTS_TYPE& l1, const LTS_TYPE& l2, const lts_equivalence eq, const bool generate_counter_examples, const std::string& counter_example_file, const bool structured_output)
883{
884 switch (eq)
885 {
886 case lts_eq_none:
887 return false;
888 default:
889 LTS_TYPE l1_copy(l1);
890 LTS_TYPE l2_copy(l2);
891 return destructive_compare(l1_copy, l2_copy, eq ,generate_counter_examples, counter_example_file, structured_output);
892 }
893 return false;
894}
895
896template <class LTS_TYPE>
897bool compare(const LTS_TYPE& l1, const LTS_TYPE& l2, const lts_preorder pre, const bool generate_counter_example, const std::string& counter_example_file, const bool structured_output, const lps::exploration_strategy strategy, const bool preprocess)
898{
899 LTS_TYPE l1_copy(l1);
900 LTS_TYPE l2_copy(l2);
901 return destructive_compare(l1_copy, l2_copy, pre, generate_counter_example, counter_example_file, structured_output, strategy, preprocess);
902}
903
904template <class LTS_TYPE>
905bool destructive_compare(LTS_TYPE& l1, LTS_TYPE& l2, const lts_preorder pre, const bool generate_counter_example, const std::string& counter_example_file, const bool structured_output, const lps::exploration_strategy strategy, const bool preprocess)
906{
907 switch (pre)
908 {
909 case lts_preorder::lts_pre_sim:
910 {
911 // Merge this LTS and l and store the result in this LTS.
912 // In the resulting LTS, the initial state i of l will have the
913 // state number i + N where N is the number of states in this
914 // LTS (before the merge).
915 const std::size_t init_l2 = l2.initial_state() + l1.num_states();
916 detail::merge(l1,l2);
917
918 // We no longer need l, so clear it to save memory
919 l2.clear();
920
921 // Run the partitioning algorithm on this merged LTS
922 detail::sim_partitioner<LTS_TYPE> sp(l1);
923 sp.partitioning_algorithm();
924
925 return sp.in_preorder(l1.initial_state(),init_l2);
926 }
927 case lts_preorder::lts_pre_ready_sim:
928 {
929 // Merge this LTS and l and store the result in this LTS.
930 // In the resulting LTS, the initial state i of l will have the
931 // state number i + N where N is the number of states in this
932 // LTS (before the merge).
933 const std::size_t init_l2 = l2.initial_state() + l1.num_states();
934 detail::merge(l1,l2);
935
936 // We no longer need l, so clear it to save memory
937 l2.clear();
938
939 // Run the partitioning algorithm on this prepropcessed LTS
940 detail::ready_sim_partitioner<LTS_TYPE> rsp(l1);
941 rsp.partitioning_algorithm();
942
943 return rsp.in_preorder(l1.initial_state(),init_l2);
944 }
945 case lts_preorder::lts_pre_trace:
946 {
947 // Preprocessing: reduce modulo strong bisimulation equivalence.
948 // This is not strictly necessary, but may reduce time/memory
949 // needed for determinisation.
950 detail::bisimulation_reduce_gj(l1,false);
951 detail::bisimulation_reduce_gj(l2,false);
952
953 // Determinise both LTSes. As postprocessing, reduce modulo
954 // strong bisimulation equivalence. This is not strictly
955 // necessary, but may reduce time/memory needed for simulation
956 // preorder checking.
957 determinise(l1);
958 detail::bisimulation_reduce_gj(l1,false);
959
960 determinise(l2);
961 detail::bisimulation_reduce_gj(l2,false);
962
963 // Trace preorder now corresponds to simulation preorder
964 return destructive_compare(l1, l2, lts_preorder::lts_pre_sim, generate_counter_example, counter_example_file, structured_output, strategy);
965 }
966 case lts_preorder::lts_pre_weak_trace:
967 {
968 // Eliminate silent steps of first LTS
969 detail::bisimulation_reduce_gj(l1,true,false);
970 detail::tau_star_reduce(l1);
971
972 // Eliminate silent steps of second LTS
973 detail::bisimulation_reduce_gj(l2,true,false);
974 detail::tau_star_reduce(l2);
975
976 // Weak trace preorder now corresponds to strong trace preorder
977 return destructive_compare(l1, l2, lts_preorder::lts_pre_trace, generate_counter_example, counter_example_file, structured_output, strategy);
978 }
979 case lts_preorder::lts_pre_trace_anti_chain:
980 {
981 if (generate_counter_example)
982 {
983 detail::counter_example_constructor cec("counter_example_trace_preorder", counter_example_file, structured_output);
984 return destructive_refinement_checker(l1, l2, refinement_type::trace, false, strategy, preprocess, cec);
985 }
986 return destructive_refinement_checker(l1, l2, refinement_type::trace, false, strategy, preprocess);
987 }
988 case lts_preorder::lts_pre_weak_trace_anti_chain:
989 {
990 if (generate_counter_example)
991 {
992 detail::counter_example_constructor cec("counter_example_weak_trace_preorder", counter_example_file, structured_output);
993 return destructive_refinement_checker(l1, l2, refinement_type::trace, true, strategy, preprocess, cec);
994 }
995 return destructive_refinement_checker(l1, l2, refinement_type::trace, true, strategy, preprocess);
996 }
997 case lts_preorder::lts_pre_failures_refinement:
998 {
999 if (generate_counter_example)
1000 {
1001 detail::counter_example_constructor cec("counter_example_failures_refinement", counter_example_file, structured_output);
1002 return destructive_refinement_checker(l1, l2, refinement_type::failures, false, strategy, preprocess, cec);
1003 }
1004 return destructive_refinement_checker(l1, l2, refinement_type::failures, false, strategy, preprocess);
1005 }
1006 case lts_preorder::lts_pre_weak_failures_refinement:
1007 {
1008 if (generate_counter_example)
1009 {
1010 detail::counter_example_constructor cec("counter_example_weak_failures_refinement", counter_example_file, structured_output);
1011 return destructive_refinement_checker(l1, l2, refinement_type::failures, true, strategy, preprocess, cec);
1012 }
1013 return destructive_refinement_checker(l1, l2, refinement_type::failures, true, strategy, preprocess);
1014 }
1015 case lts_preorder::lts_pre_failures_divergence_refinement:
1016 {
1017 if (generate_counter_example)
1018 {
1019 detail::counter_example_constructor cec("counter_example_failures_divergence_refinement", counter_example_file, structured_output);
1020 return destructive_refinement_checker(l1, l2, refinement_type::failures_divergence, true, strategy, preprocess, cec);
1021 }
1022 return destructive_refinement_checker(l1, l2, refinement_type::failures_divergence, true, strategy, preprocess);
1023 }
1024 case lts_preorder::lts_pre_impossible_futures:
1025 {
1026 if (generate_counter_example)
1027 {
1028 return detail::destructive_impossible_futures(l1, l2, strategy, counter_example_file, generate_counter_example, structured_output);
1029 }
1030
1031 return detail::destructive_impossible_futures(l1, l2, strategy, counter_example_file, false, structured_output);
1032 }
1033 case lts_preorder::lts_pre_none:
1034 {
1035 throw mcrl2::runtime_error("Expected a valid preorder");
1036 }
1037 default:
1038 mCRL2log(log::error) << "Comparison for this preorder is not available\n";
1039 return false;
1040 }
1041}
1042
1043
1044template <class LTS_TYPE>
1045bool is_deterministic(const LTS_TYPE& l)
1046{
1047 if (l.num_transitions() == 0)
1048 {
1049 return true;
1050 }
1051
1052 std::vector<transition> temporary_copy_of_transitions = l.get_transitions();
1053 sort_transitions(temporary_copy_of_transitions, l.hidden_label_set(), src_lbl_tgt);
1054
1055 // Traverse the ordered transitions, and search for two consecutive pairs <s,l,t> and <s,l,t'> with t!=t'.
1056 // Such a pair exists iff l is not deterministic.
1057 transition& previous_t=temporary_copy_of_transitions[0];
1058 bool previous_t_is_valid=false;
1059 for(const transition& t: temporary_copy_of_transitions)
1060 {
1061 if (previous_t_is_valid)
1062 {
1063 if (previous_t.from()==t.from() &&
1064 previous_t.label()==t.label() &&
1065 previous_t.to()!=t.to())
1066 {
1067 return false;
1068 }
1069 }
1070 previous_t=t;
1071 previous_t_is_valid=true;
1072 }
1073 return true;
1074}
1075
1076
1077namespace detail
1078{
1079
1080template <class LTS_TYPE>
1081void get_trans(const outgoing_transitions_per_state_t& begin,
1082 tree_set_store& tss,
1083 std::ptrdiff_t d,
1084 std::vector<transition>& d_trans,
1085 LTS_TYPE& aut)
1086{
1087 if (!tss.is_set_empty(d))
1088 {
1089 if (tss.is_set_empty(tss.get_set_child_right(d)))
1090 {
1091 const state_type from=tss.get_set_child_left(d);
1092 for (detail::state_type i=begin.lowerbound(from); i<begin.upperbound(from); ++i)
1093 {
1094 const outgoing_pair_t& p=begin.get_transitions()[i];
1095 d_trans.push_back(transition(from, aut.apply_hidden_label_map(label(p)), to(p)));
1096 }
1097 }
1098 else
1099 {
1100 get_trans(begin,tss,tss.get_set_child_left(d),d_trans,aut);
1101 get_trans(begin,tss,tss.get_set_child_right(d),d_trans,aut);
1102 }
1103 }
1104}
1105} // namespace detail
1106
1107
1108template <class LTS_TYPE>
1109void determinise(LTS_TYPE& l)
1110{
1111 tree_set_store tss;
1112
1113 std::vector<transition> d_transs;
1114 std::vector<std::ptrdiff_t> d_states;
1115
1116 // create the initial state of the DLTS
1117 d_states.push_back(l.initial_state());
1118 std::ptrdiff_t d_id = tss.set_set_tag(tss.create_set(d_states));
1119 d_states.clear();
1120
1121 const outgoing_transitions_per_state_t begin(l.get_transitions(),l.num_states(),true);
1122
1123 l.clear_transitions();
1124 l.clear_state_labels();
1125 std::size_t d_ntransitions = 0;
1126 std::vector < transition > d_transitions;
1127
1128 std::ptrdiff_t s;
1129 std::size_t i;
1130 std::size_t to;
1131 std::size_t lbl;
1132 std::size_t n_t;
1133
1134 while (d_id < tss.get_next_tag())
1135 {
1136 // collect the outgoing transitions of every state of DLTS state d_id in
1137 // the vector d_transs
1138 detail::get_trans(begin,tss,tss.get_set(d_id),d_transs,l);
1139
1140 // sort d_transs by label and (if labels are equal) by destination
1141 const detail::compare_transitions_lts compare(l.hidden_label_set());
1142 sort(d_transs.begin(),d_transs.end(),compare);
1143
1144 n_t = d_transs.size();
1145 i = 0;
1146 for (lbl = 0; lbl < l.num_action_labels(); ++lbl)
1147 {
1148 // compute the destination of the transition with label lbl
1149 while (i < n_t && l.apply_hidden_label_map(d_transs[i].label()) < lbl)
1150 {
1151 ++i;
1152 }
1153 while (i < n_t && l.apply_hidden_label_map(d_transs[i].label()) == lbl)
1154 {
1155 to = d_transs[i].to();
1156 d_states.push_back(static_cast<std::ptrdiff_t>(to));
1157 while (i < n_t && l.apply_hidden_label_map(d_transs[i].label()) == lbl &&
1158 d_transs[i].to() == to)
1159 {
1160 ++i;
1161 }
1162 }
1163 s = tss.create_set(d_states);
1164
1165 // generate the transitions to each of the next states
1166 if (!tss.is_set_empty(s))
1167 {
1168 d_transitions.emplace_back(d_id, lbl, tss.set_set_tag(s));
1169
1170 if (d_ntransitions%10000 == 0)
1171 {
1172 mCRL2log(log::debug) <<
1173 "generated " << tss.get_next_tag() << " states and " << d_ntransitions
1174 << " transitions; explored " << d_id << " states" << std::endl;
1175 }
1176 }
1177 d_states.clear();
1178 }
1179 d_transs.clear();
1180 ++d_id;
1181 }
1182
1183
1184 l.set_num_states(d_id,false); // remove the state values, and reset the number of states.
1185 l.set_initial_state(0);
1186
1187 for (const transition& t: d_transitions)
1188 {
1189 l.add_transition(t);
1190 }
1191 assert(is_deterministic(l));
1192}
1193
1194} // namespace mcrl2::lts
1195
1196
1197#endif // MCRL2_LTS_LTS_ALGORITHM_H
function object to compare two constln_t pointers based on their contents
A class that can be used to store counterexample trees and.
A class that contains a labelled transition system.
Class for computing the signature for strong bisimulation.
Definition sigref.h:74
Class for computing the signature for branching bisimulation.
Definition sigref.h:104
Class for computing the signature for divergence preserving branching bisimulation.
Definition sigref.h:183
Signature based reductions for labelled transition systems.
Definition sigref.h:349
#define mCRL2log(LEVEL)
mCRL2log(LEVEL) provides the stream used to log.
Definition logger.h:393
The main namespace for the LPS library.
Definition constelm.h:18
lts_type guess_format(std::string const &s, const bool be_verbose)
Determines the LTS format from a filename by its extension.
Definition liblts.cpp:26
static const std::array< std::string, 5 > extension_strings
Definition liblts.cpp:73
std::string supported_lts_formats_text(lts_type default_format, const std::set< lts_type > &supported)
Gives a textual list describing supported LTS formats.
Definition liblts.cpp:152
std::string supported_lts_formats_text(const std::set< lts_type > &supported)
Gives a textual list describing supported LTS formats.
Definition liblts.cpp:185
std::string string_for_type(const lts_type type)
Gives a string representation of an LTS format.
Definition liblts.cpp:112
std::string mime_type_for_type(const lts_type type)
Gives the MIME type associated with an LTS format.
Definition liblts.cpp:122
void get_trans(const outgoing_transitions_per_state_t &begin, tree_set_store &tss, std::ptrdiff_t d, std::vector< transition > &d_trans, LTS_TYPE &aut)
lts_type parse_format(std::string const &s)
Determines the LTS format from a format specification string.
Definition liblts.cpp:91
static const std::array< std::string, 5 > type_strings
Definition liblts.cpp:71
std::string extension_for_type(const lts_type type)
Gives the filename extension associated with an LTS format.
Definition liblts.cpp:117
const std::set< lts_type > & supported_lts_formats()
Gives the set of all supported LTS formats.
Definition liblts.cpp:139
std::string lts_extensions_as_string(const std::set< lts_type > &supported)
Gives a list of extensions for supported LTS formats.
Definition liblts.cpp:221
std::string lts_extensions_as_string(const std::string &sep, const std::set< lts_type > &supported)
Gives a list of extensions for supported LTS formats.
Definition liblts.cpp:190
bool lts_named_cmp(const std::array< std::string, Size > &N, T a, T b)
Definition liblts.cpp:147
static const std::array< std::string, 5 > type_desc_strings
Definition liblts.cpp:75
static const std::array< std::string, 5 > mime_type_strings
Definition liblts.cpp:84
static const std::set< lts_type > & initialise_supported_lts_formats()
Definition liblts.cpp:127
bool is_deterministic(const LTS_TYPE &l)
Checks whether this LTS is deterministic.
void determinise(LTS_TYPE &l)
Determinises this LTS.
void reduce(LTS_TYPE &l, lts_equivalence eq)
Applies a reduction algorithm to this LTS.
bool compare(const LTS_TYPE &l1, const LTS_TYPE &l2, lts_equivalence eq, bool generate_counter_examples=false, const std::string &counter_example_file="", bool structured_output=false)
Checks whether this LTS is equivalent to another LTS.
bool destructive_compare(LTS_TYPE &l1, LTS_TYPE &l2, const lts_equivalence eq, const bool generate_counter_examples=false, const std::string &counter_example_file=std::string(), const bool structured_output=false)
Checks whether this LTS is equivalent to another LTS.
bool destructive_compare(LTS_TYPE &l1, LTS_TYPE &l2, lts_preorder pre, bool generate_counter_example, const std::string &counter_example_file="", bool structured_output=false, lps::exploration_strategy strategy=lps::es_breadth, bool preprocess=true)
Checks whether this LTS is smaller than another LTS according to a preorder.
void merge(LTS_TYPE &l1, const LTS_TYPE &l2)
Merge the second lts into the first lts.
bool reachability_check(lts< SL, AL, BASE > &l, bool remove_unreachable=false)
Checks whether all states in this LTS are reachable from the initial state and remove unreachable sta...
bool reachability_check(probabilistic_lts< SL, AL, PROBABILISTIC_STATE, BASE > &l, bool remove_unreachable=false)
Checks whether all states in a probabilistic LTS are reachable from the initial state and remove unre...
bool compare(const LTS_TYPE &l1, const LTS_TYPE &l2, lts_preorder pre, bool generate_counter_example, const std::string &counter_example_file="", bool structured_output=false, lps::exploration_strategy strategy=lps::es_breadth, bool preprocess=true)
Checks whether this LTS is smaller than another LTS according to a preorder.