Vcsn  2.1
Be Rational
dot.hh
Go to the documentation of this file.
1 #pragma once
2 
3 #include <algorithm>
4 #include <iostream>
5 #include <sstream>
6 
7 #include <vcsn/dyn/fwd.hh>
8 #include <vcsn/dyn/automaton.hh>
9 #include <vcsn/algos/grail.hh>
10 #include <vcsn/algos/accessible.hh> // useful_states
11 #include <vcsn/misc/iostream.hh>
12 #include <vcsn/misc/set.hh>
14 
15 namespace vcsn
16 {
17 
18  namespace detail
19  {
20  /*-------------------------.
21  | dot(automaton, stream). |
22  `-------------------------*/
23 
27  template <typename Aut>
28  class dotter: public outputter<Aut>
29  {
30  private:
32  using typename super_t::automaton_t;
33  using typename super_t::state_t;
34  using typename super_t::polynomial_t;
35  using typename super_t::transition_t;
36  using typename super_t::weightset_t;
37  using typename super_t::weight_t;
38 
39  using super_t::aut_;
40  using super_t::finals_;
41  using super_t::initials_;
42  using super_t::os_;
43  using super_t::ps_;
44  using super_t::ws_;
45 
46  using super_t::super_t;
47 
48  // Dot, by default, uses the X11 color naming scheme, whose "gray"
49  // is really light (it looks almost blue in some cases).
50  const char* gray = "color = DimGray";
51 
52  public:
53  dotter(const automaton_t& aut, std::ostream& out,
54  bool dot2tex = false)
55  : super_t(aut, out)
56  , dot2tex_(dot2tex)
57  {
58 #if defined __GNUC__ && ! defined __clang__
59  // GCC 4.9 and 5.0 warnings: see
60  // <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65324>.
61 # pragma GCC diagnostic push
62 # pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
63 #endif
65 #if defined __GNUC__ && ! defined __clang__
66 # pragma GCC diagnostic pop
67 #endif
68  bos_.push(out);
69  }
70 
72  std::ostream& operator()()
73  {
75  print_states_();
78  return os_;
79  }
80 
81  private:
84  {
85  bos_ <<
86  "digraph\n"
87  "{\n"
88  " vcsn_context = \"";
89  aut_->context().print_set(bos_, format::text);
90  bos_ << "\"\n"
91  " rankdir = LR\n"
92  " edge ["
93  << (dot2tex_
94  ? "texmode = math, lblstyle = auto"
95  : "arrowhead = vee, arrowsize = .6")
96  << "]\n";
97 
98  if (dot2tex_)
99  bos_ <<
100  " d2toptions = \"--format tikz --tikzedgelabels"
101  " --graphstyle=automaton --crop --nominsize --autosize\"\n"
102  " d2tdocpreamble = \""
103  " \\usepackage{amssymb}"
104  " \\usetikzlibrary{arrows.meta, automata, bending}"
105  " \\tikzstyle{automaton}=[shorten >=1pt, pos=.4,"
106  " >={Stealth[bend,round]}, initial text=]"
107  " \\tikzstyle{named}=[rectangle, rounded corners]"
108  " \\tikzstyle{initial}=[initial by arrow]"
109  " \\tikzstyle{accepting}=[accepting by arrow]"
110  " \"\n";
111  }
112 
115  {
116  bos_ << '}';
117  }
118 
123  bool format_(const std::string& sep,
124  const std::string& kind, const weight_t& w)
125  {
126  if (ws_.is_zero(w))
127  return false;
128  else
129  {
130  bos_ << sep << kind;
131  if (ws_.show_one() || !ws_.is_one(w))
132  {
133  bos_ << ", " << kind << " text={";
134  ws_.print(w, bos_) << '}';
135  }
136  return true;
137  }
138  }
139 
141  void
143  {
144  aut_->print_state(s, bos_);
145  bool has_attributes = false;
146  if (dot2tex_)
147  {
148  has_attributes = true;
149  bos_ << " [";
150  std::string style;
151  std::string sep;
152  std::string close;
153  // I hate this piece of code. There must be means to be
154  // better looking...
155  if (aut_->state_has_name(s))
156  {
157  bos_ << "label = \"";
158  enable_();
159  aut_->print_state_name(s, bos_, format::latex);
160  disable_();
161  static bool debug = getenv("VCSN_DEBUG");
162  if (debug)
163  bos_ << " (" << s << ')';
164  bos_ << "\", style = \"named";
165  sep = ", ";
166  close = "\"";
167  }
168  else
169  sep = "style = \"state, ";
170  if (format_(sep, "initial", aut_->get_initial_weight(s)))
171  {
172  sep = ", ";
173  close = "\"";
174  }
175  if (format_(sep, "accepting", aut_->get_final_weight(s)))
176  close = "\"";
177  bos_ << close;
178  }
179  else
180  {
181  // Dot format.
182  if (aut_->state_has_name(s))
183  {
184  has_attributes = true;
185  bos_ << " [label = \"";
186  enable_();
187  aut_->print_state_name(s, bos_, format::text);
188  disable_();
189  static bool debug = getenv("VCSN_DEBUG");
190  if (debug)
191  bos_ << " (" << s << ')';
192  bos_ << "\", shape = box";
193  }
194  if (!aut_->state_is_strict(s))
195  {
196  if (has_attributes)
197  bos_ << ", ";
198  else
199  {
200  bos_ << " [";
201  has_attributes = true;
202  }
203  bos_ << "style = dashed";
204  }
205  }
206  if (!has(useful_, s))
207  {
208  if (has_attributes)
209  bos_ << ", ";
210  else
211  {
212  bos_ << " [";
213  has_attributes = true;
214  }
215  bos_ << gray;
216  }
217  if (has_attributes)
218  bos_ << ']';
219  }
220 
223  {
224  if (!dot2tex_)
225  {
226  // Output the pre-initial and post-final states.
227  if (!aut_->initial_transitions().empty()
228  || !aut_->final_transitions().empty())
229  {
230  bos_ <<
231  " {\n"
232  " node [shape = point, width = 0]\n";
233  for (auto s : initials_())
234  {
235  bos_ << " I";
236  aut_->print_state(s, bos_);
237  bos_ << '\n';
238  }
239  for (auto s : finals_())
240  {
241  bos_ << " F";
242  aut_->print_state(s, bos_);
243  bos_ << '\n';
244  }
245  bos_ << " }\n";
246  }
247  }
248 
249  // Output all the states to make "print | read" idempotent.
250  //
251  // Put the useless ones in gray. This does not work:
252  //
253  // { 0 1 2 }
254  // { node [color = gray] 2 }
255  //
256  // because 2 was already "declared", and dot does not associate
257  // "color = gray" to it.
258  //
259  // Set the width to something nicer than the default and shape
260  // to rounded. Useless for circle, but useful for shape =
261  // box, and simpler to set it once for all.
262  if (!aut_->states().empty())
263  {
264  bos_ << " {\n"
265  << " node ["
266  << (dot2tex_
267  ? "texmode = math, style = state"
268  : "shape = circle, style = rounded, width = 0.5")
269  << "]\n";
270  for (auto s : aut_->states())
271  {
272  bos_ << " ";
273  print_state_(s);
274  bos_ << '\n';
275  }
276  bos_ << " }\n";
277  }
278  }
279 
281  void print_transitions_(const state_t src, const state_t dst,
282  const polynomial_t& entry)
283  {
284  bos_ << " ";
285  if (src == aut_->pre())
286  {
287  bos_ << 'I';
288  aut_->print_state(dst, bos_);
289  }
290  else
291  aut_->print_state(src, bos_);
292  bos_ << " -> ";
293  if (dst == aut_->post())
294  {
295  bos_ << 'F';
296  aut_->print_state(src, bos_);
297  }
298  else
299  aut_->print_state(dst, bos_);
300 
301  auto e = to_string(ps_, entry,
303  ", ");
304  bool useless = !has(useful_, src) || !has(useful_, dst);
305  if (!e.empty() || useless)
306  {
307  bos_ << " [";
308  const char* sep = "";
309  if (!e.empty())
310  {
311  bos_ << "label = \"";
312  enable_();
313  bos_ << e;
314  disable_();
315  bos_ << "\"";
316  sep = ", ";
317  }
318  if (useless)
319  bos_ << sep << gray;
320  bos_ << ']';
321  }
322  bos_ << '\n';
323  }
324 
327  {
328  // For each src state, the destinations, sorted.
329  std::map<state_t, polynomial_t> dsts;
330  for (auto src : aut_->all_states())
331  if (aut_->state_is_strict(src)
332  && (!dot2tex_ || src != aut_->pre()))
333  {
334  dsts.clear();
335  for (auto t: aut_->all_out(src))
336  if (!dot2tex_ || aut_->dst_of(t) != aut_->post())
337  // Bypass weight_of(set), because we know that the weight is
338  // nonzero, and that there is only one weight per letter.
339  ps_.new_weight(dsts[aut_->dst_of(t)],
340  aut_->label_of(t), aut_->weight_of(t));
341  for (const auto& p: dsts)
342  print_transitions_(src, p.first, p.second);
343  }
344  }
345 
347  void enable_()
348  {
349  boost::iostreams::flush(bos_);
350  bos_.component<detail::backslashify_output_filter>(0)->enable();
351  }
352 
354  void disable_()
355  {
356  boost::iostreams::flush(bos_);
357  bos_.component<detail::backslashify_output_filter>(0)->disable();
358  }
359 
361  detail::io::filtering_ostream bos_;
363  bool dot2tex_ = false;
365  std::unordered_set<state_t_of<Aut>> useful_ = useful_states(aut_, false);
366  };
367  }
368 
369  template <typename Aut>
370  std::ostream&
371  dot(const Aut& aut, std::ostream& out, bool dot2tex = false)
372  {
373  detail::dotter<Aut> dot(aut, out, dot2tex);
374  return dot();
375  }
376 }
automaton_t aut_
The automaton we have to output.
Definition: grail.hh:124
void print_epilogue_()
Finish the dot graph.
Definition: dot.hh:114
weightset_t_of< automaton_t > weightset_t
Definition: grail.hh:47
bool format_(const std::string &sep, const std::string &kind, const weight_t &w)
Format a TikZ attribute.
Definition: dot.hh:123
bool dot2tex_
Whether to format for dot2tex.
Definition: dot.hh:363
detail::io::filtering_ostream bos_
The output stream, with a backslashify filter.
Definition: dot.hh:361
void print_prologue_()
Start the dot graph.
Definition: dot.hh:83
states_t finals_()
The list of final states, sorted.
Definition: grail.hh:114
void print_transitions_(const state_t src, const state_t dst, const polynomial_t &entry)
Print the transitions between state src and state dst.
Definition: dot.hh:281
state_t_of< automaton_t > state_t
Definition: grail.hh:41
void disable_()
Disable the escaping of backslashes.
Definition: dot.hh:354
states_t initials_()
The list of initial states, sorted.
Definition: grail.hh:104
void print_transitions_()
Print all the transitions, sorted by src state, then dst state.
Definition: dot.hh:326
std::string to_string(direction d)
Conversion to string.
Definition: direction.cc:7
const char * gray
Definition: dot.hh:50
const polynomialset_t ps_
Short-hand to the polynomialset used to print the entries.
Definition: grail.hh:132
const weightset_t & ws_
Short-hand to the weightset.
Definition: grail.hh:130
dotter(const automaton_t &aut, std::ostream &out, bool dot2tex=false)
Definition: dot.hh:53
Format an automaton into Dot.
Definition: dot.hh:28
std::ostream & os_
Output stream.
Definition: grail.hh:126
std::ostream & operator()()
Print the automaton on the stream.
Definition: dot.hh:72
std::ostream & dot(const Aut &aut, std::ostream &out, bool dot2tex=false)
Definition: dot.hh:371
ATTRIBUTE_PURE bool has(const std::deque< T, Allocator > &s, const T &e)
Whether e is member of s.
Definition: deque.hh:13
transition_t_of< automaton_t > transition_t
Definition: grail.hh:46
states_t< Aut > useful_states(const Aut &a, bool strict=true)
The set of useful states, including possibly pre() and post().
Definition: accessible.hh:75
void enable_()
Enable the escaping of backslashes.
Definition: dot.hh:347
void print_state_(state_t s)
Pretty-print state s for both dot and dot2tex.
Definition: dot.hh:142
typename polynomialset_t::value_t polynomial_t
Definition: grail.hh:50
weight_t_of< automaton_t > weight_t
Definition: grail.hh:48
Factor common bits in automaton formatting.
Definition: grail.hh:28
std::unordered_set< state_t_of< Aut > > useful_
Useful states, without evaluating the lazy states.
Definition: dot.hh:365
void print_states_()
Print the states.
Definition: dot.hh:222