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  while (!sorted_results.empty())
132  {
133  progress_bar.print_progress((original_size - sorted_results.size()) / original_size);
134 
135  // precompute the group indices of each gate
136  std::unordered_map<u32, u32> max_group_size_of_gate;
137  std::unordered_map<u32, std::vector<u32>> groups_of_gate;
138 
139  for (auto g : unassigned_gates)
140  {
141  max_group_size_of_gate.emplace(g, 0);
142  groups_of_gate.emplace(g, std::vector<u32>{});
143  }
144 
145  for (u32 i = 0; i < sorted_results.size(); ++i)
146  {
147  auto size = (u32)sorted_results[i].group->size();
148  for (auto g : *sorted_results[i].group)
149  {
150  auto it = max_group_size_of_gate.find(g);
151  it->second = std::max(it->second, size);
152  groups_of_gate.at(g).push_back(i);
153  }
154  }
155 
156  // counts sequential gates that would end up in bad groups for each scanned candidate
157  // find the current scan range
158  bool first_is_bad = sorted_results.front().group->size() < min_group_size;
159  bool first_is_preferred = std::find(config.prioritized_sizes.begin(), config.prioritized_sizes.end(), sorted_results.front().group->size()) != config.prioritized_sizes.end();
160  float lower_score_bound = sorted_results.front().score * (1.f - percent_scan);
161 
162  u32 num_scanned_groups = 0;
163  for (; num_scanned_groups < sorted_results.size(); ++num_scanned_groups)
164  {
165  if (sorted_results[num_scanned_groups].score < lower_score_bound)
166  {
167  break;
168  }
169 
170  auto scanned_group_size = sorted_results[num_scanned_groups].group->size();
171 
172  if (!first_is_bad && scanned_group_size < min_group_size)
173  {
174  break;
175  }
176  if (first_is_preferred && std::find(config.prioritized_sizes.begin(), config.prioritized_sizes.end(), scanned_group_size) == config.prioritized_sizes.end())
177  {
178  break;
179  }
180  }
181 
182  // scan the first few groups
183  std::vector<float> badness_score_of_group(num_scanned_groups, 0.0f);
184 
185  utils::parallel_for_each(0, num_scanned_groups, [&](u32 scanned_group_idx) {
186  auto& scanned_group = *sorted_results[scanned_group_idx].group;
187  // get all unassigned gates that are not in this group
188  std::vector<u32> unaffected_gates;
189  unaffected_gates.reserve(unassigned_gates.size() - scanned_group.size());
190  std::set_difference(unassigned_gates.begin(), unassigned_gates.end(), scanned_group.begin(), scanned_group.end(), std::back_inserter(unaffected_gates));
191 
192  float score = 0.f;
193 
194  // ##############################################
195 
196  std::unordered_set<u32> affected_groups;
197 
198  for (auto g : scanned_group)
199  {
200  auto& gg = groups_of_gate.at(g);
201  affected_groups.insert(gg.begin(), gg.end());
202  }
203  affected_groups.erase(scanned_group_idx);
204 
205  for (auto gr : affected_groups)
206  {
207  if (std::find(config.prioritized_sizes.begin(), config.prioritized_sizes.end(), sorted_results[gr].group->size()) != config.prioritized_sizes.end())
208  {
209  score += 1.0f;
210  }
211  }
212 
213  // get the maximum size of the groups of each unaffected gate
214  for (auto g : unaffected_gates)
215  {
216  if (max_group_size_of_gate.at(g) < min_group_size)
217  {
218  score += 1.0f;
219  }
220  }
221 
222  // ##############################################
223 
224  badness_score_of_group[scanned_group_idx] = score;
225  });
226 
227  // log_info("dataflow", "scanned {}/{} groups in this iteration", num_scanned_groups, sorted_results.size());
228 
229  // get the scanned group that would result in the least amount of bad groups
230  u32 best_choice = std::distance(badness_score_of_group.begin(), std::min_element(badness_score_of_group.begin(), badness_score_of_group.end()));
231  auto best_group = *sorted_results[best_choice].group;
232 
233  // add this group to the final output
234  {
235  u32 new_group_id = ++id_counter;
236 
237  output->group_control_fingerprint_map[new_group_id] = netlist_abstr.gate_to_fingerprint.at(*best_group.begin());
238  output->operations_on_group_allowed[new_group_id] = true;
239 
240  output->gates_of_group[new_group_id].insert(best_group.begin(), best_group.end());
241  for (const auto& sg : best_group)
242  {
243  output->parent_group_of_gate[sg] = new_group_id;
244  }
245  }
246 
247  unassigned_gates.erase(std::remove_if(unassigned_gates.begin(), unassigned_gates.end(), [&best_group](auto id) { return best_group.find(id) != best_group.end(); }),
248  unassigned_gates.end());
249 
250  // remove all candidate groupings that contain any of the newly assigned gates
251  sorted_results.erase(std::remove_if(sorted_results.begin(),
252  sorted_results.end(),
253  [&best_group](auto check_group) {
254  return std::any_of(check_group.group->begin(), check_group.group->end(), [&best_group](auto check_group_gate) {
255  return best_group.find(check_group_gate) != best_group.end();
256  });
257  }),
258  sorted_results.end());
259  }
260  progress_bar.clear();
261 
262  for (auto g : unassigned_gates)
263  {
264  u32 new_group_id = ++id_counter;
265 
266  output->group_control_fingerprint_map[new_group_id] = netlist_abstr.gate_to_fingerprint.at(g);
267  output->operations_on_group_allowed[new_group_id] = true;
268 
269  output->gates_of_group[new_group_id].insert(g);
270  output->parent_group_of_gate[g] = new_group_id;
271  }
272 
273  return output;
274  }
275 
276  } // namespace
277 
278  evaluation::Result run(const Configuration& config, Context& ctx, const std::shared_ptr<Grouping>& initial_grouping, const processing::Result& result)
279  {
281  output.is_final_result = false;
282 
283  auto scores = compute_scores(result);
284 
285  output.merged_result = generate_output(config, initial_grouping, scores);
286 
287  if (std::any_of(ctx.partial_results.begin(), ctx.partial_results.end(), [&](auto& seen) { return *seen == *output.merged_result; }))
288  {
289  if (*ctx.partial_results.back() == *output.merged_result)
290  {
291  log_info("dataflow", "result does not improve anymore");
292  }
293  else
294  {
295  log_info("dataflow", "found a cycle");
296  }
297 
298  output.is_final_result = true;
299  log_info("dataflow", "voting done");
300  }
301 
302  ctx.partial_results.push_back(output.merged_result);
303 
304  return output;
305  }
306  } // namespace evaluation
307  } // namespace dataflow
308 } // namespace hal
void print_progress(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:278
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