Awali
Another Weighted Automata library
dot.hh
Go to the documentation of this file.
1 // This file is part of Awali.
2 // Copyright 2016-2022 Sylvain Lombardy, Victor Marsault, Jacques Sakarovitch
3 //
4 // Awali is a free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 
17 #ifndef AWALI_ALGOS_DOT_HH
18 # define AWALI_ALGOS_DOT_HH
19 
20 # include <algorithm>
21 # include <iostream>
22 
24 #include <awali/sttc/algos/accessible.hh> // useful_states
26 #include <awali/sttc/misc/set.hh>
27 
28 namespace awali { namespace sttc {
29 
30 
31  namespace internal
32  {
33  /*-------------------------.
34  | dot(automaton, stream). |
35  `-------------------------*/
36 
40  template <typename Aut>
41  class dotter: public outputter<Aut>
42  {
43  private:
44  using super_type = outputter<Aut>;
45  using typename super_type::automaton_t;
46  using typename super_type::weightset_t;
47  using typename super_type::weight_t;
48 
49  using super_type::aut_;
50  using super_type::finals_;
53  using super_type::os_;
54  using super_type::ws_;
55 
56  using super_type::super_type;
57 
58  // Dot, by default, uses the X11 color naming scheme, whose "gray"
59  // is really light (it looks almost blue in some cases).
60  const char* gray = "color = DimGray";
61 
62  public:
63  dotter(const automaton_t& aut, std::ostream& out,
64  bool dot2tex, bool print_history, bool horizontal=true)
65  : super_type(aut, out)
66  , dot2tex_(dot2tex)
67  , print_history_(print_history)
68  , horizontal_(horizontal)
69  {}
70 
75  bool format(const std::string& sep,
76  const std::string& kind, const weight_t& w)
77  {
78  if (ws_.is_zero(w))
79  return false;
80  else
81  {
82  os_ << sep << kind;
83  if (ws_.show_one() || !ws_.is_one(w))
84  {
85  os_ << ", " << kind << " text={";
86  ws_.print(w, os_) << '}';
87  }
88  return true;
89  }
90  }
91 
93  void
95  {
96  os_ << s;
97  if (dot2tex_)
98  {
99  os_ << " [";
100  std::string style;
101  std::string sep;
102  std::string close;
103  // I hate this piece of code. There must be means to be
104  // better looking...
105  os_ << "label = \"";
106  aut_->print_state_name(s, os_, "latex");
107  static bool debug = getenv("AWALI_DEBUG");
108  if (debug)
109  os_ << " (" << s << ')';
110  os_ << "\", style = \"named";
111  sep = ", ";
112  close = "\"";
113  if (format(sep, "initial", aut_->get_initial_weight(s)))
114  {
115  sep = ", ";
116  close = "\"";
117  }
118  if (format(sep, "accepting", aut_->get_final_weight(s)))
119  close = "\"";
120  os_ << close << ']';
121  }
122  else
123  {
124  // Dot format.
125  os_ << " [label = \"";
126  if(print_history_ && aut_->has_history(s))
127  aut_->print_state_history(s, os_);
128  else
129  aut_->print_state_name(s, os_, "text");
130  static bool debug = getenv("AWALI_DEBUG");
131  if (debug)
132  os_ << " (" << s << ')';
133  os_ << "\", shape = box, style = rounded]";
134  }
135  }
136 
137  std::ostream& operator()()
138  {
139  auto useful = useful_states(aut_, true);
140 
141  os_ <<
142  "digraph\n"
143  "{\n"
144  " vcsn_context = \"" << aut_->context().vname() << "\"\n";
145  if(horizontal_)
146  os_ << " rankdir = LR\n";
147 
148  if (dot2tex_)
149  os_ <<
150  " d2toptions = \"--format tikz --tikzedgelabels --graphstyle=automaton --crop --nominsize --autosize\"\n"
151  " d2tdocpreamble = \""
152  " \\usepackage{amssymb}"
153  " \\usetikzlibrary{arrows, automata}"
154  " \\tikzstyle{automaton}=[shorten >=1pt, pos=.4, >=stealth', initial text=]"
155  " \\tikzstyle{named}=[rectangle, rounded corners]"
156  " \\tikzstyle{initial}=[initial by arrow]"
157  " \\tikzstyle{accepting}=[accepting by arrow]"
158  " \"\n";
159  else
160  {
161  // Output the pre-initial and post-final states.
162  if (!aut_->initial_transitions().empty()
163  || !aut_->final_transitions().empty())
164  {
165  os_ <<
166  " {\n"
167  " node [shape = point, width = 0]\n";
168  for (auto s : initials_())
169  {
170  os_ << " I" << s;
171  os_ << '\n';
172  }
173  for (auto s : finals_())
174  {
175  os_ << " F" << s;
176  os_ << '\n';
177  }
178  os_ << " }\n";
179  }
180  }
181 
182  // Output all the states to make "print | read" idempotent.
183  //
184  // Put the useless ones in gray. This does not work:
185  //
186  // { 0 1 2 }
187  // { node [color = gray] 2 }
188  //
189  // because 2 was already "declared", and dot does not associate
190  // "color = gray" to it.
191  if (!aut_->states().empty())
192  {
193  os_ << " {\n"
194  << " node ["
195  << (dot2tex_
196  ? "texmode = math, style = state"
197  : "shape = circle")
198  << "]\n";
199  for (auto s : aut_->states())
200  {
201  os_ << " ";
202  print_state_(s);
203  if (useful.find(s)==useful.end())
204  os_ << " [" << gray << ']';
205  os_ << '\n';
206  }
207  os_ << " }\n";
208  }
209  if (dot2tex_)
210  os_ << " edge [texmode = math, lblstyle = auto]\n";
211  for (auto src : dot2tex_ ? aut_->states() : aut_->all_states())
212  {
213  // Sort by destination state.
214  std::set<state_t> ds;
215  if (dot2tex_)
216  for (auto t: aut_->out(src))
217  ds.insert(aut_->dst_of(t));
218  else
219  for (auto t: aut_->all_out(src))
220  ds.insert(aut_->dst_of(t));
221  for (auto dst: ds)
222  {
223  os_ << " ";
224  if (src == aut_->pre())
225  {
226  os_ << 'I' << dst;
227  os_ << " -> " << dst;
228  }
229  else if (dst == aut_->post())
230  {
231  os_ << src << " -> ";
232  os_ << 'F' << src;
233  }
234  else
235  {
236  os_ << src << " -> " << dst;
237  }
238  std::string s = format_entry_(src, dst,
239  dot2tex_ ? "latex" : "dot");
240  bool useless = useful.find(src)==useful.end()
241  || useful.find(dst)==useful.end();
242  if (!s.empty() || useless)
243  {
244  os_ << " [";
245  const char* sep = "";
246  if (!s.empty())
247  {
248  os_ << "label = \"" << str_escape(s) << "\"";
249  sep = ", ";
250  }
251  if (useless)
252  os_ << sep << gray;
253  os_ << ']';
254  }
255  os_ << '\n';
256  }
257  }
258  return os_ << '}';
259  }
260 
262  bool dot2tex_ = false;
263  bool print_history_ = false;
264  bool horizontal_ = true;
265  };
266  }
267 
268  template <typename Aut>
269  std::ostream&
270  dot(const Aut& aut, std::ostream& out, bool dot2tex = false, bool keep_history = true, bool horizontal = true)
271  {
272  internal::dotter<Aut> dot(aut, out, dot2tex, keep_history, horizontal);
273  return dot();
274  }
275 
276 }}//end of ns awali::stc
277 
278 #endif // !AWALI_ALGOS_DOT_HH
Format an automaton into Dot.
Definition: dot.hh:42
void print_state_(state_t s)
Pretty-print state s for both dot and dot2tex.
Definition: dot.hh:94
bool print_history_
Definition: dot.hh:263
bool format(const std::string &sep, const std::string &kind, const weight_t &w)
Format a TikZ attribute.
Definition: dot.hh:75
bool horizontal_
Definition: dot.hh:264
dotter(const automaton_t &aut, std::ostream &out, bool dot2tex, bool print_history, bool horizontal=true)
Definition: dot.hh:63
std::ostream & operator()()
Definition: dot.hh:137
bool dot2tex_
Whether to format for dot2tex.
Definition: dot.hh:262
Factor common bits in automaton formatting.
Definition: grail.hh:43
std::string format_entry_(state_t src, state_t dst, const std::string &fmt="text")
The labels and weights of transitions from src to dst.
Definition: grail.hh:88
const automaton_t & aut_
The automaton we have to output.
Definition: grail.hh:154
states_t finals_()
The list of final states, sorted.
Definition: grail.hh:144
weight_t_of< automaton_t > weight_t
Definition: grail.hh:62
Aut automaton_t
Definition: grail.hh:45
const weightset_t & ws_
Short-hand to the weightset.
Definition: grail.hh:160
std::ostream & os_
Output stream.
Definition: grail.hh:156
states_t initials_()
The list of initial states, sorted.
Definition: grail.hh:134
weightset_t_of< automaton_t > weightset_t
Definition: grail.hh:61
std::ostream & str_escape(std::ostream &os, const int c)
Definition: escape.hh:30
std::set< state_t > useful_states(const Aut &aut, bool include_pre_post=false)
List of useful states.
Definition: accessible.hh:131
std::ostream & dot(const Aut &aut, std::ostream &out, bool dot2tex=false, bool keep_history=true, bool horizontal=true)
Definition: dot.hh:270
Main namespace of Awali.
Definition: ato.hh:22
unsigned state_t
Definition: types.hh:21