Version: 1.0
graph_code_generator.cpp
Go to the documentation of this file.
1 // Copyright (c) 2020, the GRAPHGEN contributors, as
2 // shown by the AUTHORS file. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file.
6 
7 #include "graph_code_generator.h"
8 
9 #include <map>
10 
11 #include "utilities.h"
12 
13 using namespace std;
14 
16 {
17  return std::string();
18 }
19 
20 // This function defines and returns the string that should be put before each (main)
21 // tree when generating the code for a forest of decision trees. The string contains
22 // an if to check whether the end of a line is reached and a goto to the corresponding
23 // tree in the end line forest. The string is specific for algorithms which exploit a
24 // mask with a horizontal shift of one pixel like most of the thinning algorithms, PRED,
25 // SAUF, CTB and so on. When the end line forest is not generated a different string
26 // should be used, for example replacing the goto with a continue and changing the if
27 // condition accordingly.
29 {
30  return prefix + "tree_" + to_string(index) + ": if ((c+=1) >= w - 1) goto " +
31  prefix + "break_0_" + to_string(mapping[0][index]) + ";\n";
32 }
33 
34 // This function defines and returns the string that should be put before each (main)
35 // tree when generating the code for a forest of decision trees. The string contains
36 // an if to check whether the end of a line is reached and a goto to the corresponding
37 // tree in the end line forest. The string is specific for algorithms which exploit a
38 // mask with a horizontal shift of two pixels like BBDT, DRAG, Spaghetti and so on.
39 // When the end line forest is not generated a different string should be used, for
40 // example replacing the goto with a continue and changing the if condition accordingly.
42 {
43  return prefix + "tree_" + to_string(index) + ": if ((c+=2) >= w - 2) { if (c > w - 2) { goto " +
44  prefix + "break_0_" + to_string(mapping[0][index]) + "; } else { goto " +
45  prefix + "break_1_" + to_string(mapping[1][index]) + "; } } \n";
46 }
47 
49 {
50  return prefix + "break_" + to_string(end_group_id) + "_" + to_string(index) + ":\n";
51 }
52 
54 {
55  return std::string(2, '\t') + "continue;\n";
56 }
57 
59 {
60  return std::string(2, '\t') + "goto " + prefix + ";\n";
61 }
62 
63 
64 // This class allows to sum-up all the data required by the recursive functions that generate
65 // the DRAG source code, thus simplifying its signature/call.
67  // with_gotos_ is used to add gotos to next DRAGs on leaves.
68  bool with_gotos_;
69 
70  std::string prefix_;
71 
72  // printed_node keeps track of the nodes that have already been written in the C++ source code and allows to avoid
73  // duplicated nodes in the final result. Indeed, the same node can be pointed by multiple nodes in the DAG but it
74  // will have to appear only once in the final code.
75  std::map<BinaryDrag<conact>::node*, size_t> printed_nodes_;
76 
77  // nodes_requring_labels keeps track of the DAG nodes that are pointed by other nodes and thus need to have a label.
78  // We need this in order to know if we have to create a label for this node or not. This map is populated by the
79  // CheckNodesTraversalRec procedure.
80  std::map<BinaryDrag<conact>::node*, bool> nodes_requiring_labels_;
81 
82  nodeid id_;
83 
84 public:
85 
86  GenerateCodeClass(bool with_gotos, std::string prefix, map<BinaryDrag<conact>::node*, size_t> printed_nodes) :
87  with_gotos_(with_gotos),
88  prefix_(prefix),
89  printed_nodes_(printed_nodes)
90  {}
91 
92  void Clear() {
93  ClearPrintedNodes();
94  ClearNodesRequiringLabels();
95  ClearId();
96  }
97 
99  printed_nodes_.clear();
100  }
101 
103  nodes_requiring_labels_.clear();
104  }
105 
106  void ClearId() {
107  id_.Clear();
108  }
109 
110  void SetId(size_t id) {
111  id_.SetId(id);
112  }
113 
114  size_t NextId() {
115  return id_.next();
116  }
117 
118  size_t GetId() {
119  return id_.get();
120  }
121 
122  void SetPrefix(std::string prefix) {
123  if (!prefix.empty() && prefix.back() != '_') {
124  prefix += '_';
125  }
126  prefix_ = std::move(prefix);
127  }
128 
129  void SetWithGotos(bool with_gotos) {
130  with_gotos_ = with_gotos;
131  }
132 
133  bool WithGotos() {
134  return with_gotos_;
135  }
136 
138  return nodes_requiring_labels_;
139  }
140 
142  return printed_nodes_;
143  }
144 
145  std::string& GetPrefix() {
146  return prefix_;
147  }
148 
149  // This procedure writes to the output stream the C++ source exploring recursively the specified DRAG.
150  // When a leaf with multiple actions is found only the first action is considered and written in the
151  // output file.
152  void GenerateCodeRec(std::ostream& os, BinaryDrag<conact>::node *n, int tab)
153  {
154  // Extract needed data from the GenerateCodeClass
155  auto& m = printed_nodes_;
156  auto& ml = nodes_requiring_labels_;
157 
158  if (n->isleaf()) {
159  vector<uint> actions = n->data.actions();
160  os << string(tab, '\t') << "ACTION_" << actions[0] << "\n";
161  if (with_gotos_) {
162  os << string(tab, '\t') << "goto " << prefix_ << "tree_" << n->data.next << ";\n";
163  }
164  return;
165  }
166 
167  if (m.find(n) == end(m)) {
168  // code not generated yet
169  if (ml[n]) {
170  // The node will be accessed more than once, so we store its Id in a map to remember that
171  // we already generated the corresponding code
172  m[n] = NextId();
173  os << string(tab, '\t') << "NODE_" << m[n] << ":\n";
174  }
175  string condition = n->data.condition;
176  transform(condition.begin(), condition.end(), condition.begin(), ::toupper);
177  os << string(tab, '\t') << "if (CONDITION_" << condition << ") {\n";
178  GenerateCodeRec(os, n->right, tab + 1);
179  os << string(tab, '\t') << "}\n";
180  os << string(tab, '\t') << "else {\n";
181  GenerateCodeRec(os, n->left, tab + 1);
182  os << string(tab, '\t') << "}\n";
183  }
184  else {
185  // code already exists
186  os << string(tab, '\t') << "goto NODE_" << m[n] << ";\n";
187  }
188  }
189 
190  // This procedure checks and stores (inside the nodes_requiring_labels_ map) the nodes which will require labels,
191  // that are nodes pointed by other nodes inside the DAG. This allows to handle labels/gotos during the generation
192  // of the C++ source code.
194  {
195  auto& ml = nodes_requiring_labels_;
196 
197  if (n->isleaf())
198  return;
199 
200  if (ml.find(n) != end(ml)) {
201  ml[n] = true;
202  }
203  else {
204  ml[n] = false;
205  CheckNodesTraversalRec(n->left);
206  CheckNodesTraversalRec(n->right);
207  }
208  }
209 
210 };
211 
212 // Actual implementation of the GenerateDragCode func. This allows to hide useless
213 // parameters (like prefix string) from the public interface. The prefix string is
214 // used to generate specific labels for the start/end trees during the forest code
215 // generation, so it is useless during the code generation of a tree. TODO, remove it?
216 //bool GenerateDragCode(const string& algorithm_name, BinaryDrag<conact>& bd, std::string prefix)
217 //{
218 // filesystem::path code_path = conf.treecode_path_;
219 //
220 // ofstream os(code_path);
221 // if (!os) {
222 // return false;
223 // }
224 //
225 // // This object wraps all the variables needed by the recursive function GenerateCodeRec and allows to simplify its
226 // // following call.
227 // GenerateCodeClass gcc(bd.roots_.size() > 1, prefix, { {} }); // t.roots_.size() > 1 serves to distinguish between
228 // // "simple" and multi-rooted DRAGs. In the latter case
229 // // gotos to the next tree will be added.
230 //
231 // // Populates the nodes_requring_labels to keep tracks of the DAG nodes that are pointed by other nodes and thus need
232 // // to have a label
233 // for (auto& t : bd.roots_) {
234 // gcc.CheckNodesTraversalRec(t);
235 // }
236 //
237 // // This function actually generates and writes into the output stream the C++ source code using pre-calculated data.
238 // for (auto& t : bd.roots_) {
239 // // TODO We actually need to call prefix and suffix functions here.
240 // gcc.GenerateCodeRec(os, t, 2);
241 // }
242 //
243 // return true;
244 //}
245 
247  bool with_gotos,
248  BEFORE_AFTER_FUNC(before),
249  BEFORE_AFTER_FUNC(after),
250  const std::string prefix,
251  size_t start_id,
252  const std::vector<std::vector<size_t>> mapping,
253  size_t end_group_id)
254 {
255  filesystem::path code_path = conf.treecode_path_;
256 
257  ofstream os(code_path);
258  if (!os) {
259  return false;
260  }
261 
262  return GenerateDragCode(os, bd, with_gotos, before, after, prefix, start_id, mapping, end_group_id);
263 }
264 
265 size_t GenerateDragCode(std::ostream& os,
266  const BinaryDrag<conact>& bd,
267  bool with_gotos,
268  BEFORE_AFTER_FUNC(before),
269  BEFORE_AFTER_FUNC(after),
270  const std::string prefix,
271  size_t start_id,
272  const std::vector<std::vector<size_t>> mapping,
273  size_t end_group_id)
274 {
275  // This object wraps all the variables needed by the recursive function GenerateCodeRec and allows to simplify its
276  // following call.
277  GenerateCodeClass gcc(with_gotos, prefix, { {} });
278 
279  // Populates the nodes_requring_labels to keep tracks of the DAG nodes that are pointed by other nodes and thus need
280  // to have a label
281  for (auto& t : bd.roots_) {
282  gcc.CheckNodesTraversalRec(t);
283  }
284 
285  // And then we generate and write into the output stream the C++ source code using pre-calculated data.
286  gcc.SetId(start_id);
287  for (size_t i = 0; i < bd.roots_.size(); ++i) {
288  os << before(i, prefix, mapping, end_group_id);
289  gcc.GenerateCodeRec(os, bd.roots_[i], 2);
290  os << after(i, prefix, mapping, end_group_id);
291  }
292 
293  return gcc.GetId();
294 }
295 
296 // This function generates forest code using numerical labels starting from start_id and returns
297 // the last used_id.
298 size_t GenerateLineForestCode(std::ostream& os,
299  const LineForestHandler& lfh,
300  std::string prefix,
301  size_t start_id,
302  BEFORE_AFTER_FUNC(before_main),
303  BEFORE_AFTER_FUNC(after_main),
304  BEFORE_AFTER_FUNC(before_end),
305  BEFORE_AFTER_FUNC(after_end))
306 {
307  // Generate the code for the main forest
308  size_t last_id = GenerateDragCode(os, lfh.f_, true, before_main, after_main, prefix, start_id, lfh.main_end_tree_mapping_);
309 
310  // Generate the code for the end of the line forests
311  for (size_t i = 0; i < lfh.end_forests_.size(); ++i) {
312  last_id = GenerateDragCode(os, lfh.end_forests_[i], false, before_end, after_end, prefix, last_id, lfh.main_end_tree_mapping_, i);
313  }
314 
315  return last_id;
316 }
A BinaryDrag is the GRAPHGEN implementation of a Binary Directed Rooted Acyclic Graph (DRAG in short)
Definition: drag.h:28
std::vector< node * > roots_
Definition: drag.h:58
void GenerateCodeRec(std::ostream &os, BinaryDrag< conact >::node *n, int tab)
void SetWithGotos(bool with_gotos)
GenerateCodeClass(bool with_gotos, std::string prefix, map< BinaryDrag< conact >::node *, size_t > printed_nodes)
void CheckNodesTraversalRec(BinaryDrag< conact >::node *n)
void SetPrefix(std::string prefix)
std::string BeforeMainShiftOne(size_t index, const std::string &prefix, const std::vector< std::vector< size_t >> &mapping, size_t end_group_id)
std::string AfterEndNoLoop(size_t index, const std::string &prefix, const std::vector< std::vector< size_t >> &mapping, size_t end_group_id)
std::string AfterEnd(size_t index, const std::string &prefix, const std::vector< std::vector< size_t >> &mapping, size_t end_group_id)
std::string BeforeEnd(size_t index, const std::string &prefix, const std::vector< std::vector< size_t >> &mapping, size_t end_group_id)
size_t GenerateLineForestCode(std::ostream &os, const LineForestHandler &lfh, std::string prefix, size_t start_id, std::string before_main(size_t index, const std::string &prefix, const std::vector< std::vector< size_t >> &mapping, size_t end_group_id), std::string after_main(size_t index, const std::string &prefix, const std::vector< std::vector< size_t >> &mapping, size_t end_group_id), std::string before_end(size_t index, const std::string &prefix, const std::vector< std::vector< size_t >> &mapping, size_t end_group_id), std::string after_end(size_t index, const std::string &prefix, const std::vector< std::vector< size_t >> &mapping, size_t end_group_id))
Generate the C++ code for the given Forest.
std::string DefaultEmptyFunc(size_t index, const std::string &prefix, const std::vector< std::vector< size_t >> &mapping, size_t end_group_id)
std::string BeforeMainShiftTwo(size_t index, const std::string &prefix, const std::vector< std::vector< size_t >> &mapping, size_t end_group_id)
bool GenerateDragCode(const BinaryDrag< conact > &bd, bool with_gotos, std::string before(size_t index, const std::string &prefix, const std::vector< std::vector< size_t >> &mapping, size_t end_group_id), std::string after(size_t index, const std::string &prefix, const std::vector< std::vector< size_t >> &mapping, size_t end_group_id), const std::string prefix, size_t start_id, const std::vector< std::vector< size_t >> mapping, size_t end_group_id)
Overload. No output stream required in this case.
#define BEFORE_AFTER_FUNC(func_name)
Macro used to define the base signature of before and after functions.
std::filesystem::path treecode_path_
Definition: config_data.h:53
Generates all the forests needed to handle one line of the image.
Definition: forest.h:51
BinaryDrag< conact > f_
Definition: forest.h:92
std::vector< std::vector< size_t > > main_end_tree_mapping_
Definition: forest.h:98
std::vector< BinaryDrag< conact > > end_forests_
Definition: forest.h:93
size_t next()
Definition: utilities.h:51
void SetId(size_t new_id)
Definition: utilities.h:58
size_t get()
Definition: utilities.h:52
void Clear()
Definition: utilities.h:54
ConfigData conf
Definition: utilities.cpp:9