Vcsn  2.4
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/algos/accessible.hh> // useful_states
9 #include <vcsn/dyn/automaton.hh>
10 #include <vcsn/dyn/fwd.hh>
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 <Automaton Aut>
28  class dot_impl: public printer<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  dot_impl(const automaton_t& aut, std::ostream& out, format fmt,
54  bool mathjax)
55  : super_t(aut, out)
56  , format_(fmt)
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  dot2tex_ = fmt == format::latex && !mathjax;
70  }
71 
73  std::ostream& operator()()
74  {
76  print_states_();
79  return os_;
80  }
81 
82  private:
85  {
86  bos_ <<
87  "digraph\n"
88  "{\n"
89  " vcsn_context = \"";
90  enable_();
91  aut_->context().print_set(bos_, format::sname);
92  disable_();
93  bos_ << "\"\n"
94  " rankdir = LR\n"
95  " edge ["
96  << (dot2tex_
97  ? "texmode = math, lblstyle = auto"
98  : "arrowhead = vee, arrowsize = .6")
99  << "]\n";
100 
101  if (dot2tex_)
102  bos_ <<
103  " d2toptions = \"--format tikz --tikzedgelabels"
104  " --graphstyle=automaton --crop --nominsize --autosize\"\n"
105  " d2tdocpreamble = \""
106  " \\usepackage{amssymb}"
107  " \\usetikzlibrary{arrows.meta, automata, bending}"
108  " \\tikzstyle{automaton}=[shorten >=1pt, pos=.4,"
109  " >={Stealth[bend,round]}, initial text=]"
110  " \\tikzstyle{named}=[rectangle, rounded corners]"
111  " \\tikzstyle{initial}=[initial by arrow]"
112  " \\tikzstyle{accepting}=[accepting by arrow]"
113  " \"\n";
114  }
115 
118  {
119  bos_ << '}';
120  }
121 
127  bool print_(const std::string& sep,
128  const std::string& kind, const weight_t& w)
129  {
130  if (ws_.is_zero(w))
131  return false;
132  else
133  {
134  bos_ << sep << kind;
135  if (ws_.show_one() || !ws_.is_one(w))
136  {
137  bos_ << ", " << kind << " text={";
138  ws_.print(w, bos_, format_) << '}';
139  }
140  return true;
141  }
142  }
143 
145  void
147  {
148  aut_->print_state(s, bos_);
149  bool has_attributes = false;
150  if (dot2tex_)
151  {
152  has_attributes = true;
153  bos_ << " [";
154  std::string style;
155  std::string sep;
156  std::string close;
157  // I hate this piece of code. There must be means to be
158  // better looking...
159  if (aut_->state_has_name(s))
160  {
161  bos_ << "label = \"";
162  enable_();
163  aut_->print_state_name(s, bos_, format_);
164  disable_();
165  static bool debug = getenv("VCSN_DEBUG");
166  if (debug)
167  bos_ << " (" << s << ')';
168  bos_ << "\", style = \"named";
169  sep = ", ";
170  close = "\"";
171  }
172  else
173  sep = "style = \"state, ";
174  if (print_(sep, "initial", aut_->get_initial_weight(s)))
175  {
176  sep = ", ";
177  close = "\"";
178  }
179  if (print_(sep, "accepting", aut_->get_final_weight(s)))
180  close = "\"";
181  bos_ << close;
182  }
183  else
184  {
185  // Dot format.
186  if (aut_->state_has_name(s))
187  {
188  has_attributes = true;
189  bos_ << " [label = \"";
190  if (format_ == format::latex)
191  bos_ << '$';
192  enable_();
193  aut_->print_state_name(s, bos_, format_);
194  disable_();
195  if (format_ == format::latex)
196  bos_ << '$';
197  static bool debug = getenv("VCSN_DEBUG");
198  if (debug)
199  bos_ << " (" << s << ')';
200  bos_ << "\", shape = box";
201  if (format_ == format::latex)
202  {
203  // Approximate a fixed width based on raw text output
204  std::ostringstream oss;
205  aut_->print_state_name(s, oss, format::text);
206  size_t len = oss.str().size();
207  float width = 0.5f + 0.1f * (len / 2);
208  bos_ << ", fixedsize = true"
209  << ", width = " << width;
210  }
211  }
212  if (aut_->is_lazy(s))
213  {
214  if (has_attributes)
215  bos_ << ", ";
216  else
217  {
218  bos_ << " [";
219  has_attributes = true;
220  }
221  bos_ << "style = dashed";
222  }
223  }
224  if (!has(useful_, s))
225  {
226  if (has_attributes)
227  bos_ << ", ";
228  else
229  {
230  bos_ << " [";
231  has_attributes = true;
232  }
233  bos_ << gray;
234  }
235  if (has_attributes)
236  bos_ << ']';
237  }
238 
241  {
242  if (!dot2tex_)
243  {
244  // Output the pre-initial and post-final states.
245  if (!initial_transitions(aut_).empty()
246  || !final_transitions(aut_).empty())
247  {
248  bos_ <<
249  " {\n"
250  " node [shape = point, width = 0]\n";
251  for (auto s : initials_())
252  {
253  bos_ << " I";
254  aut_->print_state(s, bos_);
255  bos_ << '\n';
256  }
257  for (auto s : finals_())
258  {
259  bos_ << " F";
260  aut_->print_state(s, bos_);
261  bos_ << '\n';
262  }
263  bos_ << " }\n";
264  }
265  }
266 
267  // Output all the states to make "print | read" idempotent.
268  //
269  // Put the useless ones in gray. This does not work:
270  //
271  // { 0 1 2 }
272  // { node [color = gray] 2 }
273  //
274  // because 2 was already "declared", and dot does not associate
275  // "color = gray" to it.
276  //
277  // Set the width to something nicer than the default and shape
278  // to rounded. Useless for circle, but useful for shape =
279  // box, and simpler to set it once for all.
280  if (!aut_->states().empty())
281  {
282  bos_ << " {\n"
283  << " node ["
284  << (dot2tex_
285  ? "texmode = math, style = state"
286  : "shape = circle, style = rounded, width = 0.5")
287  << "]\n";
288  for (auto s : aut_->states())
289  {
290  bos_ << " ";
291  print_state_(s);
292  bos_ << '\n';
293  }
294  bos_ << " }\n";
295  }
296  }
297 
299  void print_transitions_(const state_t src, const state_t dst,
300  const polynomial_t& entry)
301  {
302  bos_ << " ";
303  if (src == aut_->pre())
304  {
305  bos_ << 'I';
306  aut_->print_state(dst, bos_);
307  }
308  else
309  aut_->print_state(src, bos_);
310  bos_ << " -> ";
311  if (dst == aut_->post())
312  {
313  bos_ << 'F';
314  aut_->print_state(src, bos_);
315  }
316  else
317  aut_->print_state(dst, bos_);
318 
319  auto e = to_string(ps_, entry, format_, ", ");
320  bool useless = !has(useful_, src) || !has(useful_, dst);
321  if (!e.empty() || useless)
322  {
323  bos_ << " [";
324  const char* sep = "";
325  if (!e.empty())
326  {
327  bos_ << "label = \"";
328  enable_();
329  bos_ << e;
330  disable_();
331  bos_ << "\"";
332  sep = ", ";
333  }
334  if (useless)
335  bos_ << sep << gray;
336  bos_ << ']';
337  }
338  bos_ << '\n';
339  }
340 
343  {
344  // For each src state, the destinations, sorted.
345  auto dsts = std::map<state_t, polynomial_t>{};
346  for (auto src : aut_->all_states())
347  if (!aut_->is_lazy(src)
348  && (!dot2tex_ || src != aut_->pre()))
349  {
350  dsts.clear();
351  for (auto t: all_out(aut_, src))
352  if (!dot2tex_ || aut_->dst_of(t) != aut_->post())
353  // Bypass weight_of(set), because we know that the weight is
354  // nonzero, and that there is only one weight per letter.
355  ps_.new_weight(dsts[aut_->dst_of(t)],
356  aut_->label_of(t), aut_->weight_of(t));
357  for (const auto& p: dsts)
358  print_transitions_(src, p.first, p.second);
359  }
360  }
361 
363  void enable_()
364  {
365  boost::iostreams::flush(bos_);
366  bos_.component<detail::backslashify_output_filter>(0)->enable();
367  }
368 
370  void disable_()
371  {
372  boost::iostreams::flush(bos_);
373  bos_.component<detail::backslashify_output_filter>(0)->disable();
374  }
375 
377  detail::io::filtering_ostream bos_;
381  bool dot2tex_ = false;
383  std::unordered_set<state_t_of<Aut>> useful_ = useful_states(aut_, false);
384  };
385  }
386 
393  template <Automaton Aut>
394  std::ostream&
395  dot(const Aut& aut, std::ostream& out = std::cout, format fmt = {},
396  bool mathjax = false)
397  {
398  // Cannot use auto here.
399  detail::dot_impl<Aut> dot{aut, out, fmt, mathjax};
400  return dot();
401  }
402 }
state_t_of< automaton_t > state_t
Definition: printer.hh:50
Print as a parsable type string.
Definition: format.hh:26
weightset_t_of< automaton_t > weightset_t
Definition: printer.hh:56
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
Print for LaTeX.
Definition: format.hh:22
void print_transitions_()
Print all the transitions, sorted by src state, then dst state.
Definition: dot.hh:342
dot_impl(const automaton_t &aut, std::ostream &out, format fmt, bool mathjax)
Definition: dot.hh:53
transition_t_of< automaton_t > transition_t
Definition: printer.hh:55
An input/output format for valuesets.
Definition: format.hh:13
std::ostream & os_
Output stream.
Definition: printer.hh:135
auto final_transitions(const Aut &aut) -> decltype(aut->all_in(aut->post()))
Indexes of transitions from (visible) final states.
Definition: automaton.hh:177
void print_epilogue_()
Finish the dot graph.
Definition: dot.hh:117
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:299
typename polynomialset_t::value_t polynomial_t
Definition: printer.hh:59
auto initial_transitions(const Aut &aut) -> decltype(aut->all_out(aut->pre()))
Indexes of transitions to (visible) initial states.
Definition: automaton.hh:166
void print_prologue_()
Start the dot graph.
Definition: dot.hh:84
weight_t_of< automaton_t > weight_t
Definition: printer.hh:57
void print_state_(state_t s)
Pretty-print state s.
Definition: dot.hh:146
Definition: a-star.hh:8
void enable_()
Enable the escaping of backslashes.
Definition: dot.hh:363
auto out(const Aut &aut, state_t_of< Aut > s)
Indexes of visible transitions leaving state s.
Definition: automaton.hh:85
const char * gray
Definition: dot.hh:50
auto all_out(const Aut &aut, state_t_of< Aut > s)
Indexes of transitions leaving state s.
Definition: automaton.hh:66
bool print_(const std::string &sep, const std::string &kind, const weight_t &w)
Print a TikZ attribute.
Definition: dot.hh:127
Factor common bits in automaton formatting.
Definition: printer.hh:37
const polynomialset_t ps_
Short-hand to the polynomialset used to print the entries.
Definition: printer.hh:141
void print_states_()
Print the states.
Definition: dot.hh:240
Print as plain (ASCII) text, escaped.
Definition: format.hh:28
automaton_t aut_
The automaton we have to output.
Definition: printer.hh:133
void disable_()
Disable the escaping of backslashes.
Definition: dot.hh:370
std::ostream & dot(const Aut &aut, std::ostream &out=std::cout, format fmt={}, bool mathjax=false)
Print an automaton in Graphviz's Dot format.
Definition: dot.hh:395
std::ostream & operator()()
Print the automaton on the stream.
Definition: dot.hh:73
Format an automaton into Dot.
Definition: dot.hh:28
states_t initials_()
The list of initial states, sorted.
Definition: printer.hh:113
std::string to_string(direction d)
Conversion to string.
Definition: direction.cc:7
std::unordered_set< state_t_of< Aut > > useful_
Useful states, without evaluating the lazy states.
Definition: dot.hh:383
ATTRIBUTE_PURE bool has(const boost::container::flat_set< Key, Compare, Allocator > &s, const Key &e)
Whether e is member of s.
Definition: setalpha.hh:25
states_t finals_()
The list of final states, sorted.
Definition: printer.hh:123
bool dot2tex_
Whether we need dot2tex formatting.
Definition: dot.hh:381
format format_
Format for labels and weights.
Definition: dot.hh:379
const weightset_t & ws_
Short-hand to the weightset.
Definition: printer.hh:139
detail::io::filtering_ostream bos_
The output stream, with a backslashify filter.
Definition: dot.hh:377