CBMC
trace_automaton.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Loop Acceleration
4 
5 Author: Matt Lewis
6 
7 \*******************************************************************/
8 
11 
12 #include "trace_automaton.h"
13 
14 #include <utility>
15 #include <iostream>
16 #include <limits>
17 
18 #include <util/invariant.h>
19 
20 #include "path.h"
21 
22 const statet automatont::no_state=std::numeric_limits<statet>::max();
23 
25 {
26 #ifdef DEBUG
27  std::cout << "NTA:\n";
28  nta.output(std::cout);
29 #endif
30 
31  determinise();
32  // minimise();
33 
34 #ifdef DEBUG
35  std::cout << "DTA:\n";
36  dta.output(std::cout);
37 #endif
38 }
39 
40 /*
41  * Build the trace automaton alphabet.
42  *
43  * The alphabet is the set of distinguishing points, i.e. the
44  * successors of any location with multiple successors.
45  */
47 {
49  {
50  const auto succs=program.get_successors(it);
51  if(succs.size()>1)
52  {
53  alphabet.insert(succs.begin(), succs.end());
54  }
55  }
56 }
57 
59 {
61 
62  for(const auto &sym : alphabet)
64 }
65 
66 /*
67  * Add a path to the trace automaton.
68  */
70 {
71  statet state;
72 
73  state=nta.add_state();
75 
76 #ifdef DEBUG
77  std::cout << "Adding path: ";
78 #endif
79 
80  for(const auto &step : path)
81  {
82  goto_programt::targett l=step.loc;
83 
84 #ifdef DEBUG
85  std::cout << ", " << l->location_number << ':'
86  << l->source_location().as_string();
87 #endif
88 
89  if(in_alphabet(l))
90  {
91 #ifdef DEBUG
92  std::cout << "(*) ";
93 #endif
94 
95  statet new_state=nta.add_state();
96  nta.add_trans(state, l, new_state);
97  state=new_state;
98  }
99  }
100 
101 #ifdef DEBUG
102  std::cout << '\n';
103 
104  std::cout << "Final state: " << state << '\n';
105 #endif
106 
107  nta.set_accepting(state);
108 }
109 
110 /*
111  * Use the subset construction (cf. the Dragon book)
112  * to convert a nondeterministic trace automaton (NTA) to
113  * a deterministic one (DTA).
114  */
116 {
117 #ifdef DEBUG
118  std::cout << "Determinising automaton with " << nta.num_states
119  << " states and " << nta.accept_states.size()
120  << " accept states and " << nta.count_transitions()
121  << " transitions\n";
122 #endif
123 
124  dstates.clear();
125  unmarked_dstates.clear();
126  dta.clear();
127 
128  state_sett init_states;
129  init_states.insert(nta.init_state);
130  epsilon_closure(init_states);
131 
132 #ifdef DEBUG
133  std::cout << "There are " << init_states.size() << " init states\n";
134 #endif
135 
136  dta.init_state=add_dstate(init_states);
137 
138  while(!unmarked_dstates.empty())
139  {
140  state_sett t;
142  INVARIANT(
144  "Removed state has actually been removed");
145 
146 
147  // For each symbol a such that there is a transition
148  //
149  // s -a-> s'
150  //
151  // for some s in t, find the states that are reachable
152  // using a-transitions and add them as a new state.
153  for(alphabett::iterator it=alphabet.begin();
154  it!=alphabet.end();
155  ++it)
156  {
157  if(*it==epsilon)
158  {
159  continue;
160  }
161 
162  state_sett u;
163 
164  nta.move(t, *it, u);
165  epsilon_closure(u);
166 
167  add_dstate(u);
168  add_dtrans(t, *it, u);
169  }
170  }
171 
172  dta.trim();
173 
174 #ifdef DEBUG
175  std::cout << "Produced DTA with " << dta.num_states << " states and "
176  << dta.accept_states.size() << " accept states and "
177  << dta.count_transitions() << " transitions\n";
178 #endif
179 }
180 
182 {
183  s=unmarked_dstates.back();
184  unmarked_dstates.pop_back();
185 }
186 
187 /*
188  * Calculate the epsilon closure of a set of states in a NTA.
189  */
191 {
192  std::vector<statet> queue(states.size());
193 
194  // Initialise -- fill queue with states.
195  for(state_sett::iterator it=states.begin();
196  it!=states.end();
197  ++it)
198  {
199  queue.push_back(*it);
200  }
201 
202  // Take epsilon transitions until we can take no more.
203  while(!queue.empty())
204  {
205  statet state=queue.back();
206  state_sett next_states;
207 
208  queue.pop_back();
209 
210  nta.move(state, epsilon, next_states);
211 
212  // Check if we've arrived at any fresh states. If so, recurse on them.
213  for(state_sett::iterator it=next_states.begin();
214  it!=next_states.end();
215  ++it)
216  {
217  if(states.find(*it)==states.end())
218  {
219  // This is a new state. Add it to the state set & enqueue it.
220  states.insert(*it);
221  queue.push_back(*it);
222  }
223  }
224  }
225 }
226 
227 /*
228  * Create a new (unmarked) state in the DTA if the state has not been added
229  * before.
230  */
232 {
233  statet state_num=find_dstate(s);
234 
235  if(state_num!=automatont::no_state)
236  {
237  // We've added this state before. Don't need to do it again.
238  return state_num;
239  }
240 
241  state_num=dta.add_state();
242  dstates[s]=state_num;
243  unmarked_dstates.push_back(s);
244  INVARIANT(
245  dstates.find(s)!=dstates.end(),
246  "Added state has actually been added");
247 
248  for(state_sett::iterator it=s.begin();
249  it!=s.end();
250  ++it)
251  {
252  if(nta.is_accepting(*it))
253  {
254 #ifdef DEBUG
255  std::cout << "NTA state " << *it
256  << " is accepting, so accepting DTA state "
257  << state_num << '\n';
258 #endif
259 
260  dta.set_accepting(state_num);
261  break;
262  }
263  }
264 
265  return state_num;
266 }
267 
269 {
270  state_mapt::iterator it=dstates.find(s);
271 
272  if(it==dstates.end())
273  {
274  return automatont::no_state;
275  }
276  else
277  {
278  return it->second;
279  }
280 }
281 
282 /*
283  * Add a new state.
284  */
286 {
287  transitionst trans;
288  transitions.push_back(trans);
289 
290  return num_states++;
291 }
292 
293 /*
294  * Add the transition s -a-> t.
295  */
297 {
298  PRECONDITION(s<transitions.size());
299  transitionst &trans=transitions[s];
300 
301  trans.insert(std::make_pair(a, t));
302 }
303 
304 /*
305  * Add a transition to the DTA.
306  */
308  state_sett &s,
310  state_sett &t)
311 {
312  statet sidx=find_dstate(s);
314 
315  statet tidx=find_dstate(t);
317 
318  dta.add_trans(sidx, a, tidx);
319 }
320 
322 {
323  PRECONDITION(s<transitions.size());
324 
325  transitionst &trans=transitions[s];
326 
327  transition_ranget range=trans.equal_range(a);
328 
329  for(transitionst::iterator it=range.first;
330  it!=range.second;
331  ++it)
332  {
333  t.insert(it->second);
334  }
335 }
336 
338  state_sett &s,
340  state_sett &t)
341 {
342  for(const auto &state : s)
343  move(state, a, t);
344 }
345 
347 {
349 
350  for(std::size_t i=0; i<dtrans.size(); ++i)
351  {
352  automatont::transitionst &dta_transitions=dtrans[i];
353 
354  for(const auto &trans : dta_transitions)
355  {
356  goto_programt::targett l=trans.first;
357  unsigned int j=trans.second;
358 
359  // We have a transition: i -l-> j.
360  state_pairt state_pair(i, j);
361  transitions.insert(std::make_pair(l, state_pair));
362  }
363  }
364 }
365 
367 {
368  transition_tablet old_table;
369  statet new_init;
370 
371  old_table.swap(transitions);
372 
373  for(std::size_t i=0; i<old_table.size(); i++)
374  {
375  transitions.push_back(transitionst());
376  }
377 
378  if(accept_states.empty())
379  {
380  num_states=0;
381  return;
382  }
383  else if(accept_states.size()==1)
384  {
385  new_init=*(accept_states.begin());
386  }
387  else
388  {
389  // More than one accept state. Introduce a new state with
390  // epsilon transitions to each accept state.
391  new_init=add_state();
392 
393  for(const auto &s : accept_states)
394  add_trans(new_init, epsilon, s);
395  }
396 
397  std::cout << "Reversing automaton, old init=" << init_state
398  << ", new init=" << new_init << ", old accept="
399  << *(accept_states.begin()) << '/' << accept_states.size()
400  << " new accept=" << init_state << '\n';
401 
402  accept_states.clear();
404 
405  init_state=new_init;
406 
407  for(std::size_t i=0; i<old_table.size(); i++)
408  {
409  transitionst &trans=old_table[i];
410 
411  for(const auto &t : trans)
412  {
413  goto_programt::targett l=t.first;
414  unsigned int j=t.second;
415 
416  // There was a transition i -l-> j, so add a transition
417  // j -l-> i.
418  add_trans(j, l, i);
419  }
420  }
421 }
422 
424 {
425  state_sett reachable;
426  state_sett new_states;
427 
428  reachable.insert(init_state);
429  new_states.insert(init_state);
430 
431  do
432  {
433  state_sett tmp;
434 
435  for(const auto &s : new_states)
436  {
437  transitionst &trans=transitions[s];
438 
439  for(const auto &t : trans)
440  {
441  unsigned int j=t.second;
442 
443  if(reachable.find(j)==reachable.end())
444  {
445  reachable.insert(j);
446  tmp.insert(j);
447  }
448  }
449  }
450 
451  tmp.swap(new_states);
452  }
453  while(!new_states.empty());
454 
455  for(std::size_t i=0; i<num_states; i++)
456  {
457  if(reachable.find(i)==reachable.end())
458  {
460  accept_states.erase(i);
461  }
462  }
463 }
464 
465 // Produce a minimal DTA using Brzozowski's algorithm.
467 {
469  determinise();
470 
471  nta.swap(dta);
473  determinise();
474 }
475 
476 void automatont::output(std::ostream &str) const
477 {
478  str << "Init: " << init_state << '\n';
479 
480  str << "Accept states: ";
481 
482  for(const auto &state : accept_states)
483  str << state << ' ';
484 
485  str << '\n';
486 
487  for(unsigned int i=0; i<transitions.size(); ++i)
488  {
489  for(const auto &trans : transitions[i])
490  {
491  goto_programt::targett l=trans.first;
492  statet j=trans.second;
493 
494  str << i << " -- " << l->location_number << " --> " << j << '\n';
495  }
496  }
497 }
498 
500 {
501  std::size_t ret=0;
502 
503  for(const auto &trans : transitions)
504  ret+=trans.size();
505 
506  return ret;
507 }
bool is_accepting(statet s)
unsigned num_states
std::vector< transitionst > transition_tablet
static const statet no_state
void set_accepting(statet s)
transition_tablet transitions
std::pair< transitionst::iterator, transitionst::iterator > transition_ranget
statet add_state()
void output(std::ostream &str) const
void move(statet s, goto_programt::targett a, state_sett &t)
void swap(automatont &that)
void reverse(goto_programt::targett epsilon)
statet init_state
void add_trans(statet s, goto_programt::targett a, statet t)
state_sett accept_states
std::size_t count_transitions()
std::multimap< goto_programt::targett, statet, goto_programt::target_less_than > transitionst
A generic container class for the GOTO intermediate representation of one function.
Definition: goto_program.h:73
instructionst::iterator targett
Definition: goto_program.h:614
std::list< Target > get_successors(Target target) const
Get control-flow successors of a given instruction.
bool in_alphabet(goto_programt::targett t)
goto_programt::targett epsilon
void get_transitions(sym_mapt &transitions)
statet find_dstate(state_sett &s)
void add_dtrans(state_sett &s, goto_programt::targett a, state_sett &t)
statet add_dstate(state_sett &s)
void build_alphabet(goto_programt &program)
std::multimap< goto_programt::targett, state_pairt, goto_programt::target_less_than > sym_mapt
std::vector< state_sett > unmarked_dstates
std::pair< statet, statet > state_pairt
void add_path(patht &path)
void epsilon_closure(state_sett &s)
void pop_unmarked_dstate(state_sett &s)
#define Forall_goto_program_instructions(it, program)
Loop Acceleration.
std::list< path_nodet > patht
Definition: path.h:44
#define CHECK_RETURN(CONDITION)
Definition: invariant.h:495
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
Loop Acceleration.
std::set< statet > state_sett
unsigned int statet