mCRL2
Loading...
Searching...
No Matches
liblts_aut.cpp
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/// \file liblts_aut.cpp
10
11#include <fstream>
12#include "mcrl2/utilities/unordered_map.h"
13#include "mcrl2/lts/lts_aut.h"
14#include "mcrl2/lts/detail/liblts_swap_to_from_probabilistic_lts.h"
15
16
17using namespace mcrl2::lts;
18
19static void read_newline(std::istream& is, const std::size_t line_no)
20{
21 char ch;
22 is.get(ch);
23
24 // Skip over spaces
25 while (ch == ' ' && is.good())
26 {
27 is.get(ch);
28 }
29
30 // Windows systems typically have a carriage return before a newline.
31 if (ch == '\r' && is.good())
32 {
33 is.get(ch);
34 }
35
36 if (ch != '\n' && !is.eof()) // Last line does not need to be terminated with an eoln.
37 {
38 if (line_no==1)
39 {
40 throw mcrl2::runtime_error("Expect a newline after the header des(...,...,...).");
41 }
42 else
43 {
44 throw mcrl2::runtime_error("Expect a newline after the transition at line " + std::to_string(line_no) + ".");
45 }
46 }
47}
48
49// reads a number, puts it in s, and reads one extra character, which must be either a space or a closing bracket.
50static void read_natural_number_to_string(std::istream& is, std::string& s, const std::size_t line_no)
51{
52 assert(s.empty());
53 char ch;
54 is >> std::skipws >> ch;
55 for( ; isdigit(static_cast<unsigned char>(ch)) ; is.get(ch))
56 {
57 s.push_back(ch);
58 }
59 is.putback(ch);
60 if (s.empty())
61 {
62 throw mcrl2::runtime_error("Expect a number at line " + std::to_string(line_no) + ".");
63 }
64}
65
66template <class AUT_LTS_TYPE>
68{
69 std::size_t label;
70
71 assert(labs.at(action_label_string::tau_action())==0);
72 action_label_string as(s);
73 const mcrl2::utilities::unordered_map < action_label_string, std::size_t >::const_iterator i=labs.find(as);
74 if (i==labs.end())
75 {
76 label=l.add_action(as);
77 labs[as]=label;
78 }
79 else
80 {
81 label=i->second;
82 }
83 return label;
84}
85
86static void check_state(std::size_t state, std::size_t number_of_states, std::size_t line_no)
87{
88 if (state>=number_of_states)
89 {
90 throw mcrl2::runtime_error("The state number " + std::to_string(state) + " is not below the number of states (" +
91 std::to_string(number_of_states) + "). Found at line " + std::to_string(line_no) + ".");
92 }
93}
94
95static void check_states(mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t& probability_state,
96 std::size_t number_of_states, std::size_t line_no)
97{
98 if (probability_state.size()<=1) // This is a simple probabilistic state.
99 {
100 check_state(probability_state.get(), number_of_states, line_no);
101 }
102 else // The state consists of a vector of states and probability pairs.
103 {
104 for(mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t::state_probability_pair& p: probability_state)
105 {
106 check_state(p.state(), number_of_states, line_no);
107 }
108 }
109}
110
111// This procedure tries to read states, indicated by numbers
112// with in between fractions of the shape number/number. The
113// last state number is put in state. The remainder as pairs
114// in the vector. Typical expected input is 3 2/3 4 1/6 78 1/6 3.
116 std::istream& is,
117 mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t& result,
118 const std::size_t line_no)
119{
120 assert(result.size()==0);
121
122 std::size_t state;
123
124 is >> std::skipws >> state;
125
126 if (!is.good())
127 {
128 throw mcrl2::runtime_error("Expect a state number at line " + std::to_string(line_no) + ".");
129 }
130
131 // Check whether the next character is a digit. If so a probability follows.
132 char ch;
133 is >> std::skipws >> ch;
134 is.putback(ch);
135
136 if (!isdigit(static_cast<unsigned char>(ch)))
137 {
138 // There is only a single state.
139 result.set(state);
140 return;
141 }
142 bool ready=false;
143
144 mcrl2::utilities::probabilistic_arbitrary_precision_fraction remainder=mcrl2::utilities::probabilistic_arbitrary_precision_fraction::one();
145 while (is.good() && !ready)
146 {
147 // Now read a probabilities followed by the next state.
148 std::string enumerator;
149 read_natural_number_to_string(is,enumerator,line_no);
150 char ch;
151 is >> std::skipws >> ch;
152 if (ch != '/')
153 {
154 throw mcrl2::runtime_error("Expect a / in a probability at line " + std::to_string(line_no) + ".");
155 }
156
157 std::string denominator;
158 read_natural_number_to_string(is,denominator,line_no);
159 mcrl2::utilities::probabilistic_arbitrary_precision_fraction frac(enumerator,denominator);
160 remainder=remainder-frac;
161 result.add(state, frac);
162
163 is >> std::skipws >> state;
164
165 if (!is.good())
166 {
167 throw mcrl2::runtime_error("Expect a state number at line " + std::to_string(line_no) + ".");
168 }
169
170 // Check whether the next character is a digit.
171
172 is >> std::skipws >> ch;
173 is.putback(ch);
174
175 if (!isdigit(static_cast<unsigned char>(ch)))
176 {
177 ready=true;
178 }
179 }
180
181 result.add(state, remainder);
182}
183
184
185static void read_aut_header(
186 std::istream& is,
187 mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t& initial_state,
188 std::size_t& num_transitions,
189 std::size_t& num_states)
190{
191 std::string s;
192 is.width(3);
193 is >> std::skipws >> s;
194
195 if (s!="des")
196 {
197 throw mcrl2::runtime_error("Expect an .aut file to start with 'des'.");
198 }
199
200 char ch;
201 is >> std::skipws >> ch;
202
203 if (ch != '(')
204 {
205 throw mcrl2::runtime_error("Expect an opening bracket '(' after 'des' in the first line of a .aut file.");
206 }
207
208 read_probabilistic_state(is,initial_state,1);
209
210 is >> std::skipws >> ch;
211 if (ch != ',')
212 {
213 throw mcrl2::runtime_error("Expect a comma after the first number in the first line of a .aut file.");
214 }
215
216 is >> std::skipws >> num_transitions;
217
218 is >> std::skipws >> ch;
219 if (ch != ',')
220 {
221 throw mcrl2::runtime_error("Expect a comma after the second number in the first line of a .aut file.");
222 }
223
224 is >> std::skipws >> num_states;
225
226 is >> ch;
227
228 if (ch != ')')
229 {
230 throw mcrl2::runtime_error("Expect a closing bracket ')' after the third number in the first line of a .aut file.");
231 }
232
233 read_newline(is,1);
234}
235
237 std::istream& is,
238 std::size_t& from,
239 std::string& label,
240 const std::size_t line_no)
241{
242 char ch;
243 is >> std::skipws >> ch;
244 if (is.eof())
245 {
246 return false;
247 }
248 if (ch == 0x04) // found EOT character that separates two files
249 {
250 return false;
251 }
252
253 is >> std::skipws >> from;
254
255 is >> std::skipws >> ch;
256 if (ch != ',')
257 {
258 throw mcrl2::runtime_error("Expect that the first number is followed by a comma at line " + std::to_string(line_no) + ".");
259 }
260
261 is >> std::skipws >> ch;
262 if (ch == '"')
263 {
264 label="";
265 // In case the label is using quotes whitespaces
266 // in the label are preserved.
267 is >> std::noskipws >> ch;
268 while ((ch != '"') && !is.eof())
269 {
270 label.push_back(ch);
271 is >> ch;
272 }
273 if (ch != '"')
274 {
275 throw mcrl2::runtime_error("Expect that the second item is a quoted label (using \") at line " + std::to_string(line_no) + ".");
276 }
277 is >> std::skipws >> ch;
278 }
279 else
280 {
281 // In case the label is not within quotes,
282 // whitespaces are removed from the label.
283 label = ch;
284 is >> ch;
285 while ((ch != ',') && !is.eof())
286 {
287 label.push_back(ch);
288 is >> ch;
289 }
290 }
291
292 if (ch != ',')
293 {
294 throw mcrl2::runtime_error("Expect a comma after the quoted label at line " + std::to_string(line_no) + ".");
295 }
296
297 return true;
298}
299
301 std::istream& is,
302 std::size_t& from,
303 std::string& label,
304 mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t& target_probabilistic_state,
305 const std::size_t line_no)
306{
307 if (!read_initial_part_of_an_aut_transition(is,from,label,line_no))
308 {
309 return false;
310 }
311
312 read_probabilistic_state(is,target_probabilistic_state,line_no);
313
314 char ch;
315 is >> ch;
316 if (ch != ')')
317 {
318 throw mcrl2::runtime_error("Expect a closing bracket at the end of the transition at line " + std::to_string(line_no) + ".");
319 }
320
321 read_newline(is,line_no);
322 return true;
323}
324
326 std::istream& is,
327 std::size_t& from,
328 std::string& label,
329 std::size_t& to,
330 const std::size_t line_no)
331{
332 if (!read_initial_part_of_an_aut_transition(is,from,label,line_no))
333 {
334 return false;
335 }
336
337 is >> std::skipws >> to;
338
339 char ch;
340 is >> ch;
341 if (ch != ')')
342 {
343 throw mcrl2::runtime_error("Expect a closing bracket at the end of the transition at line " + std::to_string(line_no) + ".");
344 }
345
346 read_newline(is,line_no);
347 return true;
348}
349
351 mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t& probabilistic_state,
353 mcrl2::utilities::unordered_map < std::size_t, std::size_t>& indices_of_single_probabilistic_states,
354 mcrl2::utilities::unordered_map < mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t, std::size_t>&
355 indices_of_multiple_probabilistic_states)
356{
357 std::size_t fresh_index = indices_of_single_probabilistic_states.size()+indices_of_multiple_probabilistic_states.size();
358 std::size_t index;
359 // Check whether probabilistic states exists.
360 if (probabilistic_state.size()<=1)
361 {
362 index = indices_of_single_probabilistic_states.insert(
363 std::pair< std::size_t, std::size_t>
364 (probabilistic_state.get(),fresh_index)).first->second;
365 }
366 else
367 {
368 assert(probabilistic_state.size()>1);
369 index = indices_of_multiple_probabilistic_states.insert(
370 std::pair< mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t, std::size_t>
371 (probabilistic_state,fresh_index)).first->second;
372 }
373
374 if (index==fresh_index)
375 {
376 std::size_t probabilistic_state_index=l.add_and_reset_probabilistic_state(probabilistic_state);
377 assert(probabilistic_state_index==index);
378 (void)probabilistic_state_index; // Avoid unused variable warning.
379 }
380 return index;
381}
382
383
384static void read_from_aut(probabilistic_lts_aut_t& l, std::istream& is)
385{
386 std::size_t line_no = 1;
387 std::size_t ntrans = 0;
388 std::size_t nstate = 0;
389
390 mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t initial_probabilistic_state;
391 read_aut_header(is,initial_probabilistic_state,ntrans,nstate);
392
393 // The two unordered maps below are used to determine a unique index for each probabilistic state.
394 // Because most states consist of one probabilistic state, the unordered maps are duplicated into
395 // indices_of_single_probabilistic_states and indices_of_multiple_probabilistic_states.
396 // The map indices_of_single_probabilistic_states requires far less memory.
397 mcrl2::utilities::unordered_map < std::size_t, std::size_t> indices_of_single_probabilistic_states;
398 mcrl2::utilities::unordered_map < mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t, std::size_t> indices_of_multiple_probabilistic_states;
399
400 check_states(initial_probabilistic_state, nstate, line_no);
401
402 if (nstate==0)
403 {
404 throw mcrl2::runtime_error("cannot parse AUT input that has no states; at least an initial state is required.");
405 }
406
407 l.set_num_states(nstate,false);
408 l.clear_transitions(ntrans); // Reserve enough space for the transitions.
409
410 mcrl2::utilities::unordered_map < action_label_string, std::size_t > action_labels;
411 action_labels[action_label_string::tau_action()]=0; // A tau action is always stored at position 0.
412 l.set_initial_probabilistic_state(initial_probabilistic_state);
413
414 mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t probabilistic_target_state;
415 std::size_t from;
416 std::string s;
417
418 while (!is.eof())
419 {
420 probabilistic_target_state.clear();
421
422 line_no++;
423
424 if (!read_aut_transition(is,from,s,probabilistic_target_state,line_no))
425 {
426 break; // encountered EOF or something that is not a transition
427 }
428
429 check_state(from, nstate, line_no);
430 check_states(probabilistic_target_state, nstate, line_no);
431 std::size_t index = add_probablistic_state(probabilistic_target_state, l, indices_of_single_probabilistic_states, indices_of_multiple_probabilistic_states);
432
433 l.add_transition(transition(from,find_label_index(s,action_labels,l),index));
434 }
435
436 if (ntrans != l.num_transitions())
437 {
438 throw mcrl2::runtime_error("number of transitions read (" + std::to_string(l.num_transitions()) +
439 ") does not correspond to the number of transition given in the header (" + std::to_string(ntrans) + ").");
440 }
441}
442
443static void read_from_aut(lts_aut_t& l, std::istream& is)
444{
445 std::size_t line_no = 1;
446 std::size_t ntrans = 0;
447 std::size_t nstate = 0;
448
449 mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t initial_probabilistic_state;
450 read_aut_header(is,initial_probabilistic_state,ntrans,nstate);
451
452 if (initial_probabilistic_state.size()>1)
453 {
454 throw mcrl2::runtime_error("Encountered an initial probability distribution while reading an non probabilistic .aut file.");
455 }
456
457 check_states(initial_probabilistic_state, nstate, line_no);
458
459 if (nstate==0)
460 {
461 throw mcrl2::runtime_error("cannot parse AUT input that has no states; at least an initial state is required.");
462 }
463
464 l.set_num_states(nstate,false);
465 l.clear_transitions(ntrans); // Reserve enough space for the transitions.
466
467 mcrl2::utilities::unordered_map < action_label_string, std::size_t > action_labels;
468 action_labels[action_label_string::tau_action()]=0; // A tau action is always stored at position 0.
469 l.set_initial_state(initial_probabilistic_state.get());
470
471 std::size_t from;
472 std::size_t to;
473 std::string s;
474 while (!is.eof())
475 {
476 line_no++;
477
478 if (!read_aut_transition(is,from,s,to,line_no))
479 {
480 break; // eof encountered
481 }
482
483 check_state(from, nstate, line_no);
484 check_state(to, nstate, line_no);
485 l.add_transition(transition(from,find_label_index(s,action_labels,l),to));
486 }
487
488 if (ntrans != l.num_transitions())
489 {
490 throw mcrl2::runtime_error("number of transitions read (" + std::to_string(l.num_transitions()) +
491 ") does not correspond to the number of transition given in the header (" + std::to_string(ntrans) + ").");
492 }
493}
494
495
496static void write_probabilistic_state(const mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t& prob_state, std::ostream& os)
497{
498 mcrl2::utilities::probabilistic_arbitrary_precision_fraction previous_probability;
499 bool first_element=true;
500 if (prob_state.size()<=1) // This is a simple probabilistic state.
501 {
502 os << prob_state.get();
503 }
504 else // The state consists of a vector of states and probability pairs.
505 {
506 for (const mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t::state_probability_pair& p: prob_state)
507 {
508 if (first_element)
509 {
510 os << p.state();
511 previous_probability=p.probability();
512 first_element=false;
513 }
514 else
515 {
516 os << " " << pp(previous_probability) << " " << p.state();
517 previous_probability=p.probability();
518 }
519 }
520 }
521}
522
523static void write_to_aut(const probabilistic_lts_aut_t& l, std::ostream& os)
524{
525 // Do not use "endl" below to avoid flushing. Use "\n" instead.
526 os << "des (";
527 write_probabilistic_state(l.initial_probabilistic_state(),os);
528
529 os << "," << l.num_transitions() << "," << l.num_states() << ")" << "\n";
530
531 for (const transition& t: l.get_transitions())
532 {
533 os << "(" << t.from() << ",\"" << pp(l.action_label(l.apply_hidden_label_map(t.label()))) << "\",";
534 write_probabilistic_state(l.probabilistic_state(t.to()),os);
535 os << ")" << "\n";
536 }
537}
538
539static void write_to_aut(const lts_aut_t& l, std::ostream& os)
540{
541 // Do not use "endl" below to avoid flushing. Use "\n" instead.
542 os << "des (" << l.initial_state() << "," << l.num_transitions() << "," << l.num_states() << ")" << "\n";
543
544 for (const transition& t: l.get_transitions())
545 {
546 os << "(" << t.from() << ",\""
547 << pp(l.action_label(l.apply_hidden_label_map(t.label()))) << "\","
548 << t.to() << ")" << "\n";
549 }
550}
551
552
553namespace mcrl2::lts
554{
555
556void probabilistic_lts_aut_t::load(const std::string& filename)
557{
558 if (filename=="" || filename=="-")
559 {
560 read_from_aut(*this, std::cin);
561 }
562 else
563 {
564 std::ifstream is(filename.c_str());
565
566 if (!is.is_open())
567 {
568 throw mcrl2::runtime_error("cannot open .aut file '" + filename + ".");
569 }
570
571 read_from_aut(*this,is);
572 is.close();
573 }
574}
575
576void probabilistic_lts_aut_t::load(std::istream& is)
577{
578 read_from_aut(*this,is);
579}
580
581void probabilistic_lts_aut_t::save(std::string const& filename) const
582{
583 if (filename=="" || filename=="-")
584 {
585 write_to_aut(*this, std::cout);
586 }
587 else
588 {
589 std::ofstream os(filename.c_str());
590
591 if (!os.is_open())
592 {
593 throw mcrl2::runtime_error("cannot create .aut file '" + filename + ".");
594 }
595 write_to_aut(*this,os);
596 os.close();
597 }
598}
599
600void lts_aut_t::load(const std::string& filename)
601{
602 if (filename.empty() || filename=="-")
603 {
604 read_from_aut(*this, std::cin);
605 }
606 else
607 {
608 std::ifstream is(filename.c_str());
609
610 if (!is.is_open())
611 {
612 throw mcrl2::runtime_error("cannot open .aut file '" + filename + ".");
613 }
614
615 read_from_aut(*this,is);
616 is.close();
617 }
618}
619
620void lts_aut_t::load(std::istream& is)
621{
622 read_from_aut(*this,is);
623}
624
625void lts_aut_t::save(std::string const& filename) const
626{
627 if (filename.empty() || filename=="-")
628 {
629 write_to_aut(*this, std::cout);
630 }
631 else
632 {
633 std::ofstream os(filename.c_str());
634
635 if (!os.is_open())
636 {
637 throw mcrl2::runtime_error("cannot create .aut file '" + filename + ".");
638 }
639 write_to_aut(*this,os);
640 os.close();
641 }
642}
643
644
645}
A simple labelled transition format with only strings as action labels.
Definition lts_aut.h:67
void load(const std::string &filename)
Load the labelled transition system from a file.
void load(std::istream &is)
Load the labelled transition system from an input stream.
void save(const std::string &filename) const
Save the labelled transition system to file.
A simple labelled transition format with only strings as action labels.
Definition lts_aut.h:100
void load(const std::string &filename)
Load the labelled transition system from a file.
void load(std::istream &is)
Load the labelled transition system from an input stream.
void save(const std::string &filename) const
Save the labelled transition system to file.
static void read_probabilistic_state(std::istream &is, mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t &result, const std::size_t line_no)
static void write_probabilistic_state(const mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t &prob_state, std::ostream &os)
static void write_to_aut(const lts_aut_t &l, std::ostream &os)
static void read_from_aut(probabilistic_lts_aut_t &l, std::istream &is)
static size_t add_probablistic_state(mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t &probabilistic_state, probabilistic_lts_aut_t &l, mcrl2::utilities::unordered_map< std::size_t, std::size_t > &indices_of_single_probabilistic_states, mcrl2::utilities::unordered_map< mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t, std::size_t > &indices_of_multiple_probabilistic_states)
static void read_newline(std::istream &is, const std::size_t line_no)
static void read_aut_header(std::istream &is, mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t &initial_state, std::size_t &num_transitions, std::size_t &num_states)
static void write_to_aut(const probabilistic_lts_aut_t &l, std::ostream &os)
static bool read_initial_part_of_an_aut_transition(std::istream &is, std::size_t &from, std::string &label, const std::size_t line_no)
static void read_natural_number_to_string(std::istream &is, std::string &s, const std::size_t line_no)
static void check_states(mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t &probability_state, std::size_t number_of_states, std::size_t line_no)
static void check_state(std::size_t state, std::size_t number_of_states, std::size_t line_no)
static bool read_aut_transition(std::istream &is, std::size_t &from, std::string &label, mcrl2::lts::probabilistic_lts_aut_t::probabilistic_state_t &target_probabilistic_state, const std::size_t line_no)
static std::size_t find_label_index(const std::string &s, mcrl2::utilities::unordered_map< action_label_string, std::size_t > &labs, AUT_LTS_TYPE &l)
static void read_from_aut(lts_aut_t &l, std::istream &is)