HAL
evaluation.cpp
Go to the documentation of this file.
2 
11 #include "hal_core/utilities/log.h"
12 
13 #include <algorithm>
14 #include <limits>
15 
16 namespace hal
17 {
18  namespace dataflow
19  {
20  namespace evaluation
21  {
22  using scoring = std::map<std::set<u32>, float>;
23 
24  namespace
25  {
26  scoring compute_scores(const processing::Result& result)
27  {
28  std::map<std::set<u32>, float> results_by_vote;
29 
30  for (const auto& state : result.unique_groupings)
31  {
32  for (const auto& it : state->gates_of_group)
33  {
34  std::set<u32> sorted_gates(it.second.begin(), it.second.end());
35  results_by_vote[sorted_gates] += result.pass_combinations_leading_to_grouping.at(state).size();
36  }
37  }
38 
39  return results_by_vote;
40  }
41 
42  std::shared_ptr<Grouping> generate_output(const Configuration& config, const std::shared_ptr<Grouping>& initial_grouping, scoring& scores)
43  {
44  measure_block_time("majority voting");
45  const auto& netlist_abstr = initial_grouping->netlist_abstr;
46 
47  const u32 min_group_size = config.min_group_size;
48 
49  struct candidate
50  {
51  const std::set<u32>* group;
52  float score;
53  };
54 
55  // mark all sequential gates as unassigned gates
56  std::vector<u32> unassigned_gates;
57  unassigned_gates.reserve(netlist_abstr.target_gates.size());
58  std::transform(netlist_abstr.target_gates.begin(), netlist_abstr.target_gates.end(), std::back_inserter(unassigned_gates), [](auto& g) { return g->get_id(); });
59 
60  // sort unassignes gates to be able to use std::algorithms
61  std::sort(unassigned_gates.begin(), unassigned_gates.end());
62 
63  std::shared_ptr<Grouping> output = std::make_shared<Grouping>(netlist_abstr);
64 
65  u32 id_counter = -1;
66 
67  // copy known groups to final result and erase from scorings
68  for (const auto& [group_id, gates] : initial_grouping->gates_of_group)
69  {
70  if (!initial_grouping->operations_on_group_allowed.at(group_id))
71  {
72  u32 new_group_id = ++id_counter;
73 
74  output->group_control_fingerprint_map[new_group_id] = initial_grouping->netlist_abstr.gate_to_fingerprint.at(*gates.begin());
75  output->operations_on_group_allowed[new_group_id] = false;
76 
77  output->gates_of_group[new_group_id].insert(gates.begin(), gates.end());
78  for (const auto& sg : gates)
79  {
80  output->parent_group_of_gate[sg] = new_group_id;
81  }
82 
83  std::set<u32> sorted_gates(gates.begin(), gates.end());
84  scores.erase(sorted_gates);
85  unassigned_gates.erase(std::remove_if(unassigned_gates.begin(), unassigned_gates.end(), [&sorted_gates](auto id) { return sorted_gates.find(id) != sorted_gates.end(); }),
86  unassigned_gates.end());
87  }
88  }
89 
90  std::vector<candidate> sorted_results;
91 
92  // sort the scoring results
93  // result has all good groups sorted by score followed by all bad groups sorted by score
94  sorted_results.reserve(scores.size());
95  std::transform(scores.begin(), scores.end(), std::back_inserter(sorted_results), [](auto& it) { return candidate{&it.first, it.second}; });
96  std::sort(sorted_results.begin(), sorted_results.end(), [&](auto a, auto b) {
97  auto a_prio = std::find(config.prioritized_sizes.begin(), config.prioritized_sizes.end(), a.group->size());
98  auto b_prio = std::find(config.prioritized_sizes.begin(), config.prioritized_sizes.end(), b.group->size());
99 
100  if (a_prio < b_prio)
101  {
102  return true;
103  }
104  if (a_prio > b_prio)
105  {
106  return false;
107  }
108 
109  auto a_is_bad = a.group->size() < min_group_size;
110  auto b_is_bad = b.group->size() < min_group_size;
111 
112  if (!a_is_bad && b_is_bad)
113  {
114  return true;
115  }
116  if (!b_is_bad && a_is_bad)
117  {
118  return false;
119  }
120 
121  return a.score > b.score;
122  });
123 
124  const float percent_scan = 0.1f;
125 
126  log_info("dataflow", "got {} voting groups", sorted_results.size());
127 
128  // scan groups until all or done
129  float original_size = sorted_results.size();
130  ProgressPrinter progress_bar;
131  progress_bar.print_message_to_gui("dataflow: evaluate results …");
132  while (!sorted_results.empty())
133  {
134  progress_bar.print_progress_to_stderr((original_size - sorted_results.size()) / original_size);
135  progress_bar.print_progress_to_gui();
136 
137  // precompute the group indices of each gate
138  std::unordered_map<u32, u32> max_group_size_of_gate;
139  std::unordered_map<u32, std::vector<u32>> groups_of_gate;
140 
141  for (auto g : unassigned_gates)
142  {
143  max_group_size_of_gate.emplace(g, 0);
144  groups_of_gate.emplace(g, std::vector<u32>{});
145  }
146 
147  for (u32 i = 0; i < sorted_results.size(); ++i)
148  {
149  auto size = (u32)sorted_results[i].group->size();
150  for (auto g : *sorted_results[i].group)
151  {
152  auto it = max_group_size_of_gate.find(g);
153  it->second = std::max(it->second, size);
154  groups_of_gate.at(g).push_back(i);
155  }
156  }
157 
158  // counts sequential gates that would end up in bad groups for each scanned candidate
159  // find the current scan range
160  bool first_is_bad = sorted_results.front().group->size() < min_group_size;
161  bool first_is_preferred = std::find(config.prioritized_sizes.begin(), config.prioritized_sizes.end(), sorted_results.front().group->size()) != config.prioritized_sizes.end();
162  float lower_score_bound = sorted_results.front().score * (1.f - percent_scan);
163 
164  u32 num_scanned_groups = 0;
165  for (; num_scanned_groups < sorted_results.size(); ++num_scanned_groups)
166  {
167  if (sorted_results[num_scanned_groups].score < lower_score_bound)
168  {
169  break;
170  }
171 
172  auto scanned_group_size = sorted_results[num_scanned_groups].group->size();
173 
174  if (!first_is_bad && scanned_group_size < min_group_size)
175  {
176  break;
177  }
178  if (first_is_preferred && std::find(config.prioritized_sizes.begin(), config.prioritized_sizes.end(), scanned_group_size) == config.prioritized_sizes.end())
179  {
180  break;
181  }
182  }
183 
184  // scan the first few groups
185  std::vector<float> badness_score_of_group(num_scanned_groups, 0.0f);
186 
187  utils::parallel_for_each(0, num_scanned_groups, [&](u32 scanned_group_idx) {
188  auto& scanned_group = *sorted_results[scanned_group_idx].group;
189  // get all unassigned gates that are not in this group
190  std::vector<u32> unaffected_gates;
191  unaffected_gates.reserve(unassigned_gates.size() - scanned_group.size());
192  std::set_difference(unassigned_gates.begin(), unassigned_gates.end(), scanned_group.begin(), scanned_group.end(), std::back_inserter(unaffected_gates));
193 
194  float score = 0.f;
195 
196  // ##############################################
197 
198  std::unordered_set<u32> affected_groups;
199 
200  for (auto g : scanned_group)
201  {
202  auto& gg = groups_of_gate.at(g);
203  affected_groups.insert(gg.begin(), gg.end());
204  }
205  affected_groups.erase(scanned_group_idx);
206 
207  for (auto gr : affected_groups)
208  {
209  if (std::find(config.prioritized_sizes.begin(), config.prioritized_sizes.end(), sorted_results[gr].group->size()) != config.prioritized_sizes.end())
210  {
211  score += 1.0f;
212  }
213  }
214 
215  // get the maximum size of the groups of each unaffected gate
216  for (auto g : unaffected_gates)
217  {
218  if (max_group_size_of_gate.at(g) < min_group_size)
219  {
220  score += 1.0f;
221  }
222  }
223 
224  // ##############################################
225 
226  badness_score_of_group[scanned_group_idx] = score;
227  });
228 
229  // log_info("dataflow", "scanned {}/{} groups in this iteration", num_scanned_groups, sorted_results.size());
230 
231  // get the scanned group that would result in the least amount of bad groups
232  u32 best_choice = std::distance(badness_score_of_group.begin(), std::min_element(badness_score_of_group.begin(), badness_score_of_group.end()));
233  auto best_group = *sorted_results[best_choice].group;
234 
235  // add this group to the final output
236  {
237  u32 new_group_id = ++id_counter;
238 
239  output->group_control_fingerprint_map[new_group_id] = netlist_abstr.gate_to_fingerprint.at(*best_group.begin());
240  output->operations_on_group_allowed[new_group_id] = true;
241 
242  output->gates_of_group[new_group_id].insert(best_group.begin(), best_group.end());
243  for (const auto& sg : best_group)
244  {
245  output->parent_group_of_gate[sg] = new_group_id;
246  }
247  }
248 
249  unassigned_gates.erase(std::remove_if(unassigned_gates.begin(), unassigned_gates.end(), [&best_group](auto id) { return best_group.find(id) != best_group.end(); }),
250  unassigned_gates.end());
251 
252  // remove all candidate groupings that contain any of the newly assigned gates
253  sorted_results.erase(std::remove_if(sorted_results.begin(),
254  sorted_results.end(),
255  [&best_group](auto check_group) {
256  return std::any_of(check_group.group->begin(), check_group.group->end(), [&best_group](auto check_group_gate) {
257  return best_group.find(check_group_gate) != best_group.end();
258  });
259  }),
260  sorted_results.end());
261  }
262  progress_bar.clear();
263 
264  for (auto g : unassigned_gates)
265  {
266  u32 new_group_id = ++id_counter;
267 
268  output->group_control_fingerprint_map[new_group_id] = netlist_abstr.gate_to_fingerprint.at(g);
269  output->operations_on_group_allowed[new_group_id] = true;
270 
271  output->gates_of_group[new_group_id].insert(g);
272  output->parent_group_of_gate[g] = new_group_id;
273  }
274 
275  return output;
276  }
277 
278  } // namespace
279 
280  evaluation::Result run(const Configuration& config, Context& ctx, const std::shared_ptr<Grouping>& initial_grouping, const processing::Result& result)
281  {
283  output.is_final_result = false;
284 
285  auto scores = compute_scores(result);
286 
287  output.merged_result = generate_output(config, initial_grouping, scores);
288 
289  if (std::any_of(ctx.partial_results.begin(), ctx.partial_results.end(), [&](auto& seen) { return *seen == *output.merged_result; }))
290  {
291  if (*ctx.partial_results.back() == *output.merged_result)
292  {
293  log_info("dataflow", "result does not improve anymore");
294  }
295  else
296  {
297  log_info("dataflow", "found a cycle");
298  }
299 
300  output.is_final_result = true;
301  log_info("dataflow", "voting done");
302  }
303 
304  ctx.partial_results.push_back(output.merged_result);
305 
306  return output;
307  }
308  } // namespace evaluation
309  } // namespace dataflow
310 } // namespace hal
void print_message_to_gui(const std::string &message)
void print_progress_to_gui(int percent=-1)
void print_progress_to_stderr(float progress, const std::string &message="")
#define log_info(channel,...)
Definition: log.h:70
std::map< std::set< u32 >, float > scoring
Definition: evaluation.cpp:22
evaluation::Result run(const Configuration &config, Context &ctx, const std::shared_ptr< Grouping > &initial_grouping, const processing::Result &result)
Definition: evaluation.cpp:280
void parallel_for_each(u32 begin, u32 end, R func)
quint32 u32
This file contains the struct that holds all information on the netlist abstraction used for dataflow...
This file contains the class that holds all information of a dataflow analysis grouping.
if(BUILD_TESTS) include_directories($
Definition: CMakeLists.txt:1
std::vector< std::shared_ptr< Grouping > > partial_results
Definition: context.h:41
std::map< std::shared_ptr< Grouping >, std::vector< std::vector< pass_id > > > pass_combinations_leading_to_grouping
Definition: result.h:43
std::vector< std::shared_ptr< Grouping > > unique_groupings
Definition: result.h:42
#define measure_block_time(X)
Definition: timing_utils.h:36