Vcsn  2.3
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  : super_t(aut, out)
55  , format_(fmt)
56  {
57 #if defined __GNUC__ && ! defined __clang__
58  // GCC 4.9 and 5.0 warnings: see
59  // <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65324>.
60 # pragma GCC diagnostic push
61 # pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
62 #endif
64 #if defined __GNUC__ && ! defined __clang__
65 # pragma GCC diagnostic pop
66 #endif
67  bos_.push(out);
68  }
69 
71  std::ostream& operator()()
72  {
74  print_states_();
77  return os_;
78  }
79 
80  private:
83  {
84  bos_ <<
85  "digraph\n"
86  "{\n"
87  " vcsn_context = \"";
88  enable_();
89  aut_->context().print_set(bos_, format::sname);
90  disable_();
91  bos_ << "\"\n"
92  " rankdir = LR\n"
93  " edge ["
94  << (format_ == format::latex
95  ? "texmode = math, lblstyle = auto"
96  : "arrowhead = vee, arrowsize = .6")
97  << "]\n";
98 
99  if (format_ == format::latex)
100  bos_ <<
101  " d2toptions = \"--format tikz --tikzedgelabels"
102  " --graphstyle=automaton --crop --nominsize --autosize\"\n"
103  " d2tdocpreamble = \""
104  " \\usepackage{amssymb}"
105  " \\usetikzlibrary{arrows.meta, automata, bending}"
106  " \\tikzstyle{automaton}=[shorten >=1pt, pos=.4,"
107  " >={Stealth[bend,round]}, initial text=]"
108  " \\tikzstyle{named}=[rectangle, rounded corners]"
109  " \\tikzstyle{initial}=[initial by arrow]"
110  " \\tikzstyle{accepting}=[accepting by arrow]"
111  " \"\n";
112  }
113 
116  {
117  bos_ << '}';
118  }
119 
125  bool print_(const std::string& sep,
126  const std::string& kind, const weight_t& w)
127  {
128  if (ws_.is_zero(w))
129  return false;
130  else
131  {
132  bos_ << sep << kind;
133  if (ws_.show_one() || !ws_.is_one(w))
134  {
135  bos_ << ", " << kind << " text={";
136  ws_.print(w, bos_, format_) << '}';
137  }
138  return true;
139  }
140  }
141 
143  void
145  {
146  aut_->print_state(s, bos_);
147  bool has_attributes = false;
148  if (format_ == format::latex)
149  {
150  has_attributes = true;
151  bos_ << " [";
152  std::string style;
153  std::string sep;
154  std::string close;
155  // I hate this piece of code. There must be means to be
156  // better looking...
157  if (aut_->state_has_name(s))
158  {
159  bos_ << "label = \"";
160  enable_();
161  aut_->print_state_name(s, bos_, format_);
162  disable_();
163  static bool debug = getenv("VCSN_DEBUG");
164  if (debug)
165  bos_ << " (" << s << ')';
166  bos_ << "\", style = \"named";
167  sep = ", ";
168  close = "\"";
169  }
170  else
171  sep = "style = \"state, ";
172  if (print_(sep, "initial", aut_->get_initial_weight(s)))
173  {
174  sep = ", ";
175  close = "\"";
176  }
177  if (print_(sep, "accepting", aut_->get_final_weight(s)))
178  close = "\"";
179  bos_ << close;
180  }
181  else
182  {
183  // Dot format.
184  if (aut_->state_has_name(s))
185  {
186  has_attributes = true;
187  bos_ << " [label = \"";
188  enable_();
189  aut_->print_state_name(s, bos_, format_);
190  disable_();
191  static bool debug = getenv("VCSN_DEBUG");
192  if (debug)
193  bos_ << " (" << s << ')';
194  bos_ << "\", shape = box";
195  }
196  if (aut_->is_lazy(s))
197  {
198  if (has_attributes)
199  bos_ << ", ";
200  else
201  {
202  bos_ << " [";
203  has_attributes = true;
204  }
205  bos_ << "style = dashed";
206  }
207  }
208  if (!has(useful_, s))
209  {
210  if (has_attributes)
211  bos_ << ", ";
212  else
213  {
214  bos_ << " [";
215  has_attributes = true;
216  }
217  bos_ << gray;
218  }
219  if (has_attributes)
220  bos_ << ']';
221  }
222 
225  {
226  if (format_ != format::latex)
227  {
228  // Output the pre-initial and post-final states.
229  if (!initial_transitions(aut_).empty()
230  || !final_transitions(aut_).empty())
231  {
232  bos_ <<
233  " {\n"
234  " node [shape = point, width = 0]\n";
235  for (auto s : initials_())
236  {
237  bos_ << " I";
238  aut_->print_state(s, bos_);
239  bos_ << '\n';
240  }
241  for (auto s : finals_())
242  {
243  bos_ << " F";
244  aut_->print_state(s, bos_);
245  bos_ << '\n';
246  }
247  bos_ << " }\n";
248  }
249  }
250 
251  // Output all the states to make "print | read" idempotent.
252  //
253  // Put the useless ones in gray. This does not work:
254  //
255  // { 0 1 2 }
256  // { node [color = gray] 2 }
257  //
258  // because 2 was already "declared", and dot does not associate
259  // "color = gray" to it.
260  //
261  // Set the width to something nicer than the default and shape
262  // to rounded. Useless for circle, but useful for shape =
263  // box, and simpler to set it once for all.
264  if (!aut_->states().empty())
265  {
266  bos_ << " {\n"
267  << " node ["
268  << (format_ == format::latex
269  ? "texmode = math, style = state"
270  : "shape = circle, style = rounded, width = 0.5")
271  << "]\n";
272  for (auto s : aut_->states())
273  {
274  bos_ << " ";
275  print_state_(s);
276  bos_ << '\n';
277  }
278  bos_ << " }\n";
279  }
280  }
281 
283  void print_transitions_(const state_t src, const state_t dst,
284  const polynomial_t& entry)
285  {
286  bos_ << " ";
287  if (src == aut_->pre())
288  {
289  bos_ << 'I';
290  aut_->print_state(dst, bos_);
291  }
292  else
293  aut_->print_state(src, bos_);
294  bos_ << " -> ";
295  if (dst == aut_->post())
296  {
297  bos_ << 'F';
298  aut_->print_state(src, bos_);
299  }
300  else
301  aut_->print_state(dst, bos_);
302 
303  auto e = to_string(ps_, entry, format_, ", ");
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_->is_lazy(src)
332  && (format_ != format::latex || src != aut_->pre()))
333  {
334  dsts.clear();
335  for (auto t: all_out(aut_, src))
336  if (format_ != format::latex
337  || aut_->dst_of(t) != aut_->post())
338  // Bypass weight_of(set), because we know that the weight is
339  // nonzero, and that there is only one weight per letter.
340  ps_.new_weight(dsts[aut_->dst_of(t)],
341  aut_->label_of(t), aut_->weight_of(t));
342  for (const auto& p: dsts)
343  print_transitions_(src, p.first, p.second);
344  }
345  }
346 
348  void enable_()
349  {
350  boost::iostreams::flush(bos_);
351  bos_.component<detail::backslashify_output_filter>(0)->enable();
352  }
353 
355  void disable_()
356  {
357  boost::iostreams::flush(bos_);
358  bos_.component<detail::backslashify_output_filter>(0)->disable();
359  }
360 
362  detail::io::filtering_ostream bos_;
366  std::unordered_set<state_t_of<Aut>> useful_ = useful_states(aut_, false);
367  };
368  }
369 
375  template <Automaton Aut>
376  std::ostream&
377  dot(const Aut& aut, std::ostream& out = std::cout, format fmt = {})
378  {
379  // Cannot use auto here.
380  detail::dot_impl<Aut> dot{aut, out, fmt};
381  return dot();
382  }
383 }
Factor common bits in automaton formatting.
Definition: printer.hh:24
bool print_(const std::string &sep, const std::string &kind, const weight_t &w)
Print a TikZ attribute.
Definition: dot.hh:125
std::ostream & operator()()
Print the automaton on the stream.
Definition: dot.hh:71
void print_states_()
Print the states.
Definition: dot.hh:224
const polynomialset_t ps_
Short-hand to the polynomialset used to print the entries.
Definition: printer.hh:128
const weightset_t & ws_
Short-hand to the weightset.
Definition: printer.hh:126
void disable_()
Disable the escaping of backslashes.
Definition: dot.hh:355
states_t initials_()
The list of initial states, sorted.
Definition: printer.hh:100
weight_t_of< automaton_t > weight_t
Definition: printer.hh:44
auto out(const Aut &aut, state_t_of< Aut > s)
Indexes of visible transitions leaving state s.
Definition: automaton.hh:64
states_t finals_()
The list of final states, sorted.
Definition: printer.hh:110
Print as a parsable type string.
Definition: format.hh:26
std::unordered_set< state_t_of< Aut > > useful_
Useful states, without evaluating the lazy states.
Definition: dot.hh:366
typename polynomialset_t::value_t polynomial_t
Definition: printer.hh:46
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
Definition: a-star.hh:8
dot_impl(const automaton_t &aut, std::ostream &out, format fmt)
Definition: dot.hh:53
An input/output format for valuesets.
Definition: format.hh:13
transition_t_of< automaton_t > transition_t
Definition: printer.hh:42
format format_
Format for labels and weights.
Definition: dot.hh:364
void print_state_(state_t s)
Pretty-print state s.
Definition: dot.hh:144
state_t_of< automaton_t > state_t
Definition: printer.hh:37
std::ostream & os_
Output stream.
Definition: printer.hh:122
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
std::string to_string(direction d)
Conversion to string.
Definition: direction.cc:7
weightset_t_of< automaton_t > weightset_t
Definition: printer.hh:43
detail::io::filtering_ostream bos_
The output stream, with a backslashify filter.
Definition: dot.hh:362
void print_epilogue_()
Finish the dot graph.
Definition: dot.hh:115
auto initial_transitions(const Aut &aut) -> decltype(aut->all_out(aut->pre()))
Indexes of transitions to (visible) initial states.
Definition: automaton.hh:145
std::ostream & dot(const Aut &aut, std::ostream &out=std::cout, format fmt={})
Print an automaton in Graphviz's Dot format.
Definition: dot.hh:377
void enable_()
Enable the escaping of backslashes.
Definition: dot.hh:348
Print for LaTeX.
Definition: format.hh:22
automaton_t aut_
The automaton we have to output.
Definition: printer.hh:120
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:45
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:283
auto final_transitions(const Aut &aut) -> decltype(aut->all_in(aut->post()))
Indexes of transitions from (visible) final states.
Definition: automaton.hh:156
Format an automaton into Dot.
Definition: dot.hh:28
void print_prologue_()
Start the dot graph.
Definition: dot.hh:82
void print_transitions_()
Print all the transitions, sorted by src state, then dst state.
Definition: dot.hh:326