CGRA-ME
HeuristicMappers.cpp
Go to the documentation of this file.
2 
3 #include <CGRA/ConstraintSet.h>
4 #include <CGRA/GraphAlgorithms.h>
5 #include <CGRA/Module.h>
6 #include <CGRA/MRRGProcedures.h>
8 #include <CGRA/TupleHash.h>
11 
12 #include <gurobi_c++.h>
13 #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
14 #include <ortools/constraint_solver/constraint_solver.h>
15 #include <ortools/sat/cp_model_solver.h>
16 #include <ortools/util/time_limit.h>
17 #endif
18 
19 #include <chrono>
20 #include <fstream>
21 #include <numeric>
22 #include <unordered_map>
23 #include <unordered_set>
24 #include <vector>
25 
26 #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
27 namespace gor = operations_research;
28 namespace gorsat = operations_research::sat;
29 #endif
30 
31 const NodeFilter allowAllNodes = [](const MRRG::NodeDescriptor&) { return true; };
32 const OpNodeFilter allowAllNodesForOps = [](const OpGraph::OpDescriptor&, const MRRG::NodeDescriptor&) { return true; };
33 
34 struct OpAndFU : tuple_hash::TupleHashOps<OpAndFU> {
37 
39  : op(op), fu(fu)
40  { }
41 
42  auto asTuple() const { return std::make_tuple(op, fu); }
43 };
44 
45 struct EdgeAndFU : tuple_hash::TupleHashOps<EdgeAndFU> {
48 
50  : edge(edge), fu(fu)
51  { }
52 
53  auto asTuple() const { return std::make_tuple(edge.val, edge.output_index, fu); }
54 };
55 
56 struct MRRGNodePair : tuple_hash::TupleHashOps<MRRGNodePair> {
59 
62  { }
63 
64  auto asTuple() const { return std::make_tuple(src_fu, sink_fu); }
65 };
66 
67 struct MRRGPathID : tuple_hash::TupleHashOps<MRRGPathID> {
71  int index;
72 
75  { }
76 
77  auto asTuple() const { return std::make_tuple(src_fu, sink_fu, trip_count, index); }
78 };
79 
80 struct GorSatCPVarID { int index; };
81 
82 template<typename CFN>
83 auto unionOfCompatibleNodes(const OpGraph& opgraph, const CFN& compatible_fu_nodes) {
84  std::unordered_set<MRRG::NodeDescriptor> all_compatible_nodes_set;
85  for (auto& op : opgraph.opNodes()) {
86  for (auto& compatible_node : compatible_fu_nodes.at(op)) {
87  all_compatible_nodes_set.emplace(compatible_node);
88  }
89  }
90  return all_compatible_nodes_set;
91 }
92 
93 #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
94 GorSatCPVarID makeBoolVar(gorsat::CpModelProto& cpmp, const std::string& name) {
95  const int index = cpmp.variables_size();
96  gorsat::IntegerVariableProto* const var = cpmp.add_variables();
97  var->set_name(name);
98  var->add_domain(0);
99  var->add_domain(1);
100  return GorSatCPVarID{index};
101 }
102 
103 template<typename Literals>
104 auto addLogicalOr(gorsat::CpModelProto& cpmp, const Literals& literals) {
105  gorsat::ConstraintProto* const ct = cpmp.add_constraints();
106  gorsat::BoolArgumentProto* const bool_or = ct->mutable_bool_or();
107  for (const auto& lit : literals) {
108  bool_or->add_literals(lit.index);
109  }
110  return std::make_pair(ct, bool_or);
111 }
112 
113 template<typename Literals>
114 auto addConstrainedSum(gorsat::CpModelProto& cpmp, const Literals& literals, const std::vector<std::pair<long long, long long>>& domains) {
115  gorsat::ConstraintProto* const ct = cpmp.add_constraints();
116  gorsat::LinearConstraintProto* const lin = ct->mutable_linear();
117  for (const auto& lit : literals) {
118  lin->add_vars(lit.index);
119  lin->add_coeffs(1);
120  }
121  for (const auto& dom : domains) {
122  lin->add_domain(dom.first);
123  lin->add_domain(dom.second);
124  }
125  return std::make_pair(ct, lin);
126 }
127 
128 template<typename Literals>
129 auto addMaxConstraint(gorsat::CpModelProto& cpmp, const Literals& literals, GorSatCPVarID equal_to) {
130  // no idea why this doesn't work
131  // gorsat::ConstraintProto* const ct = cpmp.add_constraints();
132  // gorsat::IntegerArgumentProto* const maxCt = ct->mutable_int_max();
133  // for (const auto& lit : literals) {
134  // maxCt->add_vars(lit.index);
135  // }
136  // maxCt->set_target(equal_to.index);
137  // return std::make_pair(ct, maxCt);
138 
139  auto ct_and_lin = addConstrainedSum(cpmp, literals, {{0,kint64max}});
140  ct_and_lin.second->add_vars(equal_to.index);
141  ct_and_lin.second->add_coeffs(-1);
142  return ct_and_lin;
143 }
144 #endif
145 
146 std::string from_src_at_to_sink_at(std::string prefix, const OpGraph& opgraph, const MRRG& mrrg,
147  OpGraph::OpDescriptor src_op, MRRG::NodeDescriptor src_compatible_node,
148  OpGraph::OpDescriptor sink_op, MRRG::NodeDescriptor sink_compatible_node,
149  bool make_name_solver_safe = false
150 ) {
151  prefix += ".from_src";
152  if (src_op ) { prefix += '.' + opgraph.getNodeRef(src_op).getName(); }
153  if (src_compatible_node ) { prefix += ".at." + mrrg.getNodeRef(src_compatible_node).getFullName(); }
154  prefix += ".to_sink";
155  if (sink_op ) { prefix += '.' + opgraph.getNodeRef(sink_op).getName(); }
156  if (sink_compatible_node ) { prefix += ".at." + mrrg.getNodeRef(sink_compatible_node).getFullName(); }
157 
158  return make_name_solver_safe ? makeNameSolverSafe(std::move(prefix)) : std::move(prefix);
159 }
160 
161 std::string from_src_at_to_sink_at(std::string prefix, const OpGraph& opgraph, const MRRG& mrrg,
162  OpGraph::OpDescriptor src_op, MRRG::NodeDescriptor src_compatible_node,
163  OpGraph::EdgeDescriptor out_edge, MRRG::NodeDescriptor sink_compatible_node,
164  bool make_name_solver_safe = false
165 ) {
166  auto result = from_src_at_to_sink_at(
167  std::move(prefix), opgraph, mrrg, src_op, src_compatible_node,
168  opgraph.targetOfEdge(out_edge), sink_compatible_node,
169  false
170  ) + ".out_edge" + std::to_string(out_edge.output_index);
171 
172  return make_name_solver_safe ? makeNameSolverSafe(std::move(result)) : std::move(result);
173 }
174 
175 std::string from_src_at_to_sink_at_trip_count(std::string prefix, const OpGraph& opgraph, const MRRG& mrrg,
176  OpGraph::OpDescriptor src_op, MRRG::NodeDescriptor src_compatible_node,
177  OpGraph::OpDescriptor sink_op, MRRG::NodeDescriptor sink_compatible_node,
178  int trip_count,
179  bool make_name_solver_safe = false
180 ) {
181  auto result = from_src_at_to_sink_at(
182  std::move(prefix), opgraph, mrrg,
183  src_op, src_compatible_node, sink_op, sink_compatible_node
184  ) + ".trip_count." + std::to_string(trip_count);
185  return make_name_solver_safe ? makeNameSolverSafe(std::move(result)) : std::move(result);
186 }
187 
188 std::string from_src_at_to_sink_at_trip_count(std::string prefix, const OpGraph& opgraph, const MRRG& mrrg,
189  OpGraph::OpDescriptor src_op, MRRG::NodeDescriptor src_compatible_node,
190  OpGraph::EdgeDescriptor out_edge, MRRG::NodeDescriptor sink_compatible_node,
191  int trip_count,
192  bool make_name_solver_safe = false
193 ) {
194  auto result = from_src_at_to_sink_at(
195  std::move(prefix), opgraph, mrrg,
196  src_op, src_compatible_node, out_edge, sink_compatible_node
197  ) + ".trip_count." + std::to_string(trip_count);
198  return make_name_solver_safe ? makeNameSolverSafe(std::move(result)) : std::move(result);
199 }
200 
201 std::string from_src_at_to_sink_at_trip_count_number(std::string prefix, const OpGraph& opgraph, const MRRG& mrrg,
202  OpGraph::OpDescriptor src_op, MRRG::NodeDescriptor src_compatible_node,
203  OpGraph::OpDescriptor sink_op, MRRG::NodeDescriptor sink_compatible_node,
204  int trip_count, int number,
205  bool make_name_solver_safe = false
206 ) {
207  auto result = from_src_at_to_sink_at_trip_count(
208  std::move(prefix), opgraph, mrrg,
209  src_op, src_compatible_node, sink_op, sink_compatible_node, trip_count)
210  + ".number." + std::to_string(number);
211  return make_name_solver_safe ? makeNameSolverSafe(std::move(result)) : std::move(result);
212 }
213 
214 std::string from_src_at_to_sink(std::string prefix, const OpGraph& opgraph, const MRRG& mrrg,
215  OpGraph::OpDescriptor src_op, MRRG::NodeDescriptor src_compatible_node,
216  OpGraph::OpDescriptor sink_op,
217  bool make_name_solver_safe = false
218 ) {
219  return from_src_at_to_sink_at(std::move(prefix), opgraph, mrrg,
220  src_op, src_compatible_node, sink_op, nullptr, make_name_solver_safe);
221 }
222 
223 std::string from_src_to_sink_at(std::string prefix, const OpGraph& opgraph, const MRRG& mrrg,
224  OpGraph::OpDescriptor src_op,
225  OpGraph::EdgeDescriptor out_edge, MRRG::NodeDescriptor sink_compatible_node,
226  bool make_name_solver_safe = false
227 ) {
228  return from_src_at_to_sink_at(std::move(prefix), opgraph, mrrg,
229  src_op, nullptr, out_edge, sink_compatible_node, make_name_solver_safe);
230 }
231 
232 std::string from_mrrg_node_to_mrrg_node(std::string prefix, const OpGraph& opgraph, const MRRG& mrrg,
233  MRRG::NodeDescriptor src_compatible_node,
234  MRRG::NodeDescriptor sink_compatible_node,
235  bool make_name_solver_safe = false
236 ) {
237  return from_src_at_to_sink_at(std::move(prefix), opgraph, mrrg,
238  nullptr, src_compatible_node, nullptr, sink_compatible_node, make_name_solver_safe);
239 }
240 
241 std::string from_mrrg_node_to_mrrg_node_trip_count_number(std::string prefix, const OpGraph& opgraph, const MRRG& mrrg,
242  MRRG::NodeDescriptor src_compatible_node,
243  MRRG::NodeDescriptor sink_compatible_node,
244  int trip_count, int number,
245  bool make_name_solver_safe = false
246 ) {
247  auto result = from_mrrg_node_to_mrrg_node(std::move(prefix), opgraph, mrrg, src_compatible_node, sink_compatible_node)
248  + ".trip_count." + std::to_string(trip_count) + ".number." + std::to_string(number);
249  return make_name_solver_safe ? makeNameSolverSafe(std::move(result)) : std::move(result);
250 }
251 
252 std::ostream& operator<<(std::ostream& os, const ILPHeuristicMapperOptions& opts) {
253  os << "{\n"
254  << "\tfind_all_neighbour_FUs = " << (opts.find_all_neighbour_FUs ? "set" : "unset") << '\n'
255  << "\tsolution_selector = " << (opts.solution_selector ? "set" : "unset") << '\n'
256  << "\tedge_coster = " << (opts.edge_coster ? "set" : "unset") << '\n'
257  << "\tgeneral = " << opts.general << '\n'
258  << '}';
259  return os;
260 }
261 
263  std::string solver_id;
267 
271 
273 
278 
281 
282  , solver_id( general.getString("solver") )
283  , use_solver_gurobi( solver_id == Solvers::Gurobi.id )
284  , use_solver_gor_classic_cp( solver_id == Solvers::GoogleORTools_ClassicCP.id )
285  , use_solver_gor_sat_cp( solver_id == Solvers::GoogleORTools_SatCP.id )
286 
287  , print_initial_mapping( general.getBool("print_initial_mapping") )
288  , print_intermediate_mappings( general.getBool("print_intermediate_mappings") )
289  , print_mapping( general.getBool("print_mapping") )
290 
291  , operand_nodes_are_normal( general.getBool("operand_nodes_are_normal") )
292 
293  , use_edge_vars_constrained_via_implication_network(general.getBool("allow_multiple_op_mappings") || general.getBool("path_exclusivity_modelling"))
297  {
298  override_all(ConfigStore(default_general_opts()), general); // check for typos
299 
301  throw make_from_stream<cgrame_mapper_error>([&](auto&& s) {
302  s << "unhandled solver ID " << solver_id;
303  });
304  }
305 
306  if (general.getBool("path_exclusivity_modelling")) {
307  if (not general.getBool("allow_unbalanced_latency")) {
309  if (general.getBool("allow_multiple_op_mappings")) {
311  } else {
313  }
314  }
315  }
316 
317  is_node_allowed_for_op = [this, old_inafo=is_node_allowed_for_op](const auto& op, const auto& n) {
318  return this->is_node_allowed ? this->is_node_allowed(n) : (old_inafo ? old_inafo(op,n) : true);
319  };
320  }
321 
322  friend std::ostream& operator<<(std::ostream& os, const InternalILPHeuristicMapperOptions& opts) {
323  os << "{\n"
324  << "\tBase Options = " << static_cast<const ILPHeuristicMapperOptions&>(opts) << '\n'
325  << "\tsolver_id = " << opts.solver_id << '\n'
326  << "\tuse_solver_gurobi = " << opts.use_solver_gurobi << '\n'
327  << "\tuse_solver_gor_classic_cp = " << opts.use_solver_gor_classic_cp << '\n'
328  << "\tuse_solver_gor_sat_cp = " << opts.use_solver_gor_sat_cp << '\n'
329  << '}';
330  return os;
331  }
332 };
333 
337 
338 template<typename MappedType>
339 using OpAndFUMap = std::unordered_map<OpAndFU, MappedType, OpAndFU::Hash>;
340 
341 template<typename MappedType>
342 using EdgeAndFUMap = std::unordered_map<EdgeAndFU, MappedType, EdgeAndFU::Hash>;
343 
344 template<typename MappedType>
345 using FUMap = std::unordered_map<MRRG::NodeDescriptor, MappedType>;
346 
347 Mapping mapViaConnectivityAndPathChoosing(std::unordered_map<std::string, std::string> fix_port,
348  const OpGraph& opgraph, const MRRG& mrrg,
349  const Mapping& initial_mapping,
350  ILPHeuristicMapperOptions options_,
352 ) try {
353  const auto options = InternalILPHeuristicMapperOptions(std::move(options_));
354 
355  const auto solve_approach_name = options.general.getString("solve_approach_name");
356  const bool print_solve_progress = options.general.getBool("print_solve_progress");
357  const bool do_print_configuration_and_statistics = options.general.getBool("print_configuration_and_statistics");
358  PrintOnDestructionChronoSequence timing_seq(solve_approach_name, print_solve_progress ? &std::cout : nullptr);
359  ConfigStore problem_statistics;
360  ConfigStore solve_statistics;
361 
362  if (do_print_configuration_and_statistics) {
363  std::cout << "finding a mapping via " << solve_approach_name << ", with...\n "
364  << "\t" << "Options = " << options << '\n'
365  ;
366  }
367  if (options.print_initial_mapping) {
368  initial_mapping.outputMapping(opgraph, std::cout);
369  }
370  timing_seq.tick("initial dumps", 0.01);
371 
372  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
373  gor::Solver gorCP_solver("mapOpsJustByConnectivity");
374 
375  gorsat::CpModelProto gorSatCP_model;
376  gorSatCP_model.set_name("mapOpsJustByConnectivity");
377  #endif
378 
379  const auto solver_seed = options.general.getInt("seed");
380  const auto max_threads = options.general.getInt("max_threads");
381  const auto timelimit = options.general.getInt("timelimit");
382  const bool do_print_neighbours = options.general.getBool("print_neighbours");
383  const bool do_print_paths = options.general.getBool("print_paths");
384  const auto model_dump_filename = options.general.getString("model_dump_filename");
385  const auto model_IIS_dump_filename = options.general.getString("model_IIS_dump_filename");
386 
387  const auto max_num_paths = options.general.getInt("max_num_paths_between_FUs");
388  const auto allowable_routing_usage_multiplier = options.general.getInt("allowable_routing_usage_multiplier");
389 
390  const auto allowed_cycle_trip_count_min_max = [&]() -> std::pair<int,int> { // (immediately invoked)
391  const auto really_big = std::numeric_limits<int>::max();
392  if (options.general.getBool("allow_unbalanced_latency")) { return {0, really_big}; } // anything goes
393  else { return {1, 1}; } // self-loops must be trip_count == 1;
394  }();
395 
396  // TODO: Move this to functionality on class Mapping
397  const auto mrrg_node_user_in_initial_mapping = [&]() {
398  std::unordered_map<MRRG::NodeDescriptor, std::vector<OpGraph::OpDescriptor>> result;
399  initial_mapping.forEachMapping([&](const auto& og_ndesc, const auto& mrrg_ndesc) {
400  const auto op_desc = opgraph.asOp(og_ndesc);
401  if (not op_desc) { return; }
402  result[mrrg_ndesc].push_back(op_desc);
403  });
404  return result;
405  }();
406  timing_seq.tick("compute reverse initial mapping", 0.01);
407 
408  const auto mrrg_node_is_free_or_used_by_op_node = [&](
409  const OpGraph::NodeDescriptor& op_node, const MRRG::NodeDescriptor& mrrg_node
410  ) {
411  const auto search_result = mrrg_node_user_in_initial_mapping.find(mrrg_node);
412  if (search_result == mrrg_node_user_in_initial_mapping.end()) {
413  return true; // not in use, go ahead
414  } else {
415  // if used, only okay if op_node is the initial mapping
416  auto& ops_mapped = search_result->second;
417  return std::find(ops_mapped.begin(), ops_mapped.end(), op_node) != ops_mapped.end();
418  }
419  };
420 
421  const auto op_node_is_allowed_to_map_to_mrrg = [&](
422  const OpGraph::NodeDescriptor& op_node, const MRRG::NodeDescriptor& mrrg_node, std::unordered_map<std::string, std::string> fix_port
423  ) {
424  if(fix_port.empty()) {
425  return true;
426  }
427  bool result = true;
428  for (auto& node : fix_port) {
429  if (op_node->getName() == node.first) {
430  if (mrrg_node->getHierarchyQualifiedName().find(node.second) == node.second.npos) {
431  result = false;
432  }
433  }
434  }
435  return result;
436  };
437 
438  // the true mrrg<->op filter
439  const auto op_node_can_use_mrrg_node = [&](
440  const OpGraph::OpDescriptor& op, const MRRG::NodeDescriptor& n
441  ) {
442  return options.is_node_allowed_for_op(op, n) && mrrg_node_is_free_or_used_by_op_node(op, n) && op_node_is_allowed_to_map_to_mrrg(op, n, fix_port);
443  };
444 
445  const auto all_compatible_nodes = findAllCompatibleFUs(opgraph, mrrg);
446  const auto all_compatible_nodes_set = unionOfCompatibleNodes(opgraph, all_compatible_nodes);
447  const std::vector<MRRG::NodeDescriptor> all_compatible_nodes_list(begin(all_compatible_nodes_set), end(all_compatible_nodes_set));
448  // TODO: filter the isReachableFrom by (src_op == sink_op || src_compatible_node != sink_compatible_node)
449  auto fu_neighbours = options.find_all_neighbour_FUs ? options.find_all_neighbour_FUs(mrrg, all_compatible_nodes_list) : FunctionalUnitNeighbours{};
450  if (options.general.getBool("add_self_as_neighbour_if_path")) {
451  for (auto& fu_and_info : fu_neighbours) {
452  const auto fu = fu_and_info.first;
453  if (fu_neighbours.isReachableFrom(fu, fu)) { continue; }
454  const auto& paths = findNRoutingPathsBetween(1, mrrg, fu, fu, allowed_cycle_trip_count_min_max, {}, caches.mrrg_proc_cache);
455  if (paths.empty()) { continue; }
456  fu_and_info.second.emplace(fu, FunctionalUnitNeighbours::NodeInfo{fu, (int)paths.at(0).size() - 1});
457  }
458  }
459 
460  const auto treatAsNeighbours = [&](const OpAndFU& src_op_and_fu, const OpAndFU& sink_op_and_fu) {
461  // no point in having edges for DFG self-loops mapped to different FU nodes!
462  if (src_op_and_fu.op == sink_op_and_fu.op && src_op_and_fu.fu != sink_op_and_fu.fu) { return false; }
463  // if nothing else applies, just check if they are neighbours.
464  return fu_neighbours.isReachableFrom(src_op_and_fu.fu, sink_op_and_fu.fu);
465  };
466 
467  if (options.general.getBool("print_compatible_nodes")) {
468  std::cout << "Compatible Nodes: " << fu_neighbours << std::endl;
469  print_assoc_container(std::cout, all_compatible_nodes,
470  [&](auto&& s, const auto& nodes) { print_container(s, nodes,
471  [&](auto&& s, const auto& n) { s << mrrg.getNodeRef(n).getFullName(); }
472  );},
473  [&](auto&& s, const auto& op) { s << opgraph.getNodeRef(op).getName(); }
474  );
475  std::cout << std::endl;
476  }
477 
478  if (do_print_neighbours) { std::cout << "FU Neighbours: " << fu_neighbours << std::endl; }
479 
480  std::unordered_map<OpGraph::OpDescriptor, std::vector<MRRG::NodeDescriptor>> compatible_fu_nodes;
481  for (auto& op : opgraph.opNodes()) {
482  const auto is_node_allowed_for_this_op = [&](auto&& n) { return op_node_can_use_mrrg_node(op, n); };
483 
484  auto& mappings_for_op = compatible_fu_nodes[op];
485  if (initial_mapping.hasMapping(op)) {
486  for (const auto& mapped_node : initial_mapping.getMappingList(op)) {
487  if (not is_node_allowed_for_this_op(mapped_node)) {
488  if (not options.general.getBool("silence_warnings")) {
489  std::cout << "Warning: node filter(s) disallow the existing mapping of "
490  << opgraph.getNodeRef(op) << " to " << mrrg.getNodeRef(mapped_node) << std::endl;
491  }
492  continue;
493  }
494  mappings_for_op.push_back(mapped_node);
495  // if a fanout is mapped too, make sure the fanout's FU is a neighbour
496  for (const auto& sink_op : opgraph.outputOps(op)) {
497  if (not initial_mapping.hasMapping(sink_op)) { continue; }
498  for (const auto& sink_mapped_node : initial_mapping.getMappingList(sink_op)) {
499  if (not treatAsNeighbours({op, mapped_node}, {sink_op, sink_mapped_node})) {
500  fu_neighbours.neighbours[mapped_node].emplace(
501  sink_mapped_node,
502  FunctionalUnitNeighbours::NodeInfo{sink_mapped_node, -1}
503  );
504  }
505  }
506  }
507  }
508  } else if (options.general.getBool("map_unmapped_ops")) {
509  const auto& compat_nodes = all_compatible_nodes.at(op);
510  std::copy_if(compat_nodes.begin(), compat_nodes.end(), std::back_inserter(mappings_for_op), is_node_allowed_for_this_op);
511  }
512 
513  if (mappings_for_op.empty() && not options.general.getBool("silence_warnings")) {
514  std::cout << "Warning: no compatible nodes for " << opgraph.getNodeRef(op) << std::endl;
515  }
516  }
517  timing_seq.tick("compute compatible_fu_nodes", 0.01);
518 
519  const auto opfu_opfu_edge_counts = [&](){
521  for (const auto& src_op : opgraph.opNodes()) {
522  for (const auto& src_compatible_node : compatible_fu_nodes.at(src_op)) {
523  const auto src_op_and_fu = OpAndFU{src_op, src_compatible_node};
524  for (const auto& sink_op : opgraph.outputOps(src_op)) {
525  for (const auto& sink_compatible_node : compatible_fu_nodes.at(sink_op)) {
526  const auto sink_op_and_fu = OpAndFU{sink_op, sink_compatible_node};
527  if (treatAsNeighbours(src_op_and_fu, sink_op_and_fu)) {
528  result[src_op_and_fu][sink_op_and_fu] += 1;
529  }
530  }
531  }
532  }
533  }
534 
535  return result;
536  }();
537 
538  const auto fu_fu_max_edge_counts = [&](){
539  FUMap<FUMap<int>> result;
540  for (const auto& src_opfu_and_sink_opfu_edge_counts : opfu_opfu_edge_counts) {
541  for (const auto& sink_opfu_and_edge_count : src_opfu_and_sink_opfu_edge_counts.second) {
542  auto& max_paths_needed = result[src_opfu_and_sink_opfu_edge_counts.first.fu][sink_opfu_and_edge_count.first.fu];
543  max_paths_needed = std::max(max_paths_needed, sink_opfu_and_edge_count.second);
544  }
545  }
546 
547  return result;
548  }();
549  timing_seq.tick("compute opfu_opfu_edge_counts and fu_fu_max_edge_counts", 0.01);
550 
551  std::unordered_map<MRRGNodePair, std::map<int, std::vector<std::vector<MRRG::NodeDescriptor>>>, MRRGNodePair::Hash> mrrg_paths;
552  if (options.general.getBool("path_exclusivity_modelling")) {
553  // for each edge
554  for (auto& src_op1 : opgraph.opNodes()) {
555  for (auto& sink_op1 : opgraph.outputOps(src_op1)) {
556 
557  // for compatible node
558  for (auto& src_compatible_node1 : compatible_fu_nodes.at(src_op1)) {
559  for (auto& sink_compatible_node1 : compatible_fu_nodes.at(sink_op1)) {
560  const auto src_op_and_fu1 = OpAndFU{src_op1, src_compatible_node1};
561  const auto sink_op_and_fu1= OpAndFU {sink_op1, sink_compatible_node1};
562  if (not treatAsNeighbours(src_op_and_fu1, sink_op_and_fu1)) { continue; }
563  const auto emplace_result = mrrg_paths.insert({{src_compatible_node1, sink_compatible_node1}, {}});
564  if (not emplace_result.second) { continue; } // avoid recomputation
565  auto& mrrg_paths_item = emplace_result.first->second;
566  const auto& paths = findNRoutingPathsBetween(max_num_paths, mrrg, src_compatible_node1, sink_compatible_node1,
567  allowed_cycle_trip_count_min_max, {}, caches.mrrg_proc_cache);
568  for (auto& path : paths) {
569  auto trip_count = tripCountOfWalk(mrrg, path);
570  auto& mrrg_paths_for_trip_count = mrrg_paths_item[trip_count];
571  if (do_print_paths) {
572  std::cout << from_src_at_to_sink_at_trip_count_number("path_for", opgraph, mrrg, src_op1,
573  src_compatible_node1, sink_op1, sink_compatible_node1, trip_count, mrrg_paths_for_trip_count.size());
574  print_container(std::cout, path);
575  std::cout << '\n';
576  }
577  mrrg_paths_for_trip_count.emplace_back(std::move(path));
578  }
579  if (do_print_paths) { std::cout << '\n'; }
580  }
581  }
582  }
583  }
584  } else {
585  // There is one edge variable per value of trip_count;
586  // if the map is empty, then there won't be any edge variables.
587  // Therefore, make sure there is one trip count value, even if it is bogus.
588  // for each edge
589  for (const auto& src_op1 : opgraph.opNodes()) {
590  for (const auto& sink_op1 : opgraph.outputOps(src_op1)) {
591  // for each compatible node
592  for (const auto& src_compatible_node1 : compatible_fu_nodes.at(src_op1)) {
593  for (const auto& sink_compatible_node1 : compatible_fu_nodes.at(sink_op1)) {
594  const auto src_op_and_fu1 = OpAndFU{ src_op1, src_compatible_node1};
595  const auto sink_op_and_fu1 = OpAndFU{sink_op1, sink_compatible_node1};
596  if (not treatAsNeighbours(src_op_and_fu1, sink_op_and_fu1)) { continue; }
597  mrrg_paths.insert({ {src_compatible_node1, sink_compatible_node1}, { {-1, {}} } });
598  }
599  }
600  }
601  }
602  }
603  timing_seq.tick("compute mrrg_paths", 0.02);
604 
605  auto trails_to_balance = lazy_pointer([&]{ return computeTrailsToBalance(opgraph); });
606  auto dfg_edge_latency = lazy_pointer([&]{ return opgraph.edgeLatencies(); });
607 
608  problem_statistics.addReal("average neighbour count", std::accumulate(fu_neighbours.begin(), fu_neighbours.end(), 0L,
609  [&](auto& v, auto& item) { return v + item.second.size(); }
610  )/(double)fu_neighbours.neighbours.size());
611 
612  problem_statistics.addInt("DFG - number of OPs", opgraph.opNodes().size());
613 
614  if (do_print_configuration_and_statistics) {
615  std::cout << "Problem Statistics = " << problem_statistics << '\n';
616  }
617 
618  timing_seq.tick("compute and print statistics", 0.01);
619  timing_seq.tick("setup");
620 
621  if (options.general.getBool("just_warm_up_caches")) {
622  // resolve any lazy (but cacheable) data here
623  timing_seq.tick("warm up caches");
624  Mapping cpy = initial_mapping;
625  cpy.clear();
626  cpy.setStatus(MappingStatus::success); // successfully did nothing!
627  return cpy;
628  }
629 
630  GRBEnv env = GRBEnv(true);
631  if (not print_solve_progress) {
632  env.set(GRB_IntParam_OutputFlag, 0); // (nearly) silence Gurobi
633  }
634  env.start();
635  if (timelimit != 0) {
636  env.set(GRB_DoubleParam_TimeLimit, timelimit);
637  }
638  GRBModel model = GRBModel(env);
639 
640  const auto op_is_mapped_to_var_maps = [&]() {
641  std::tuple<
643  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
644  ,
647  #endif
648  > retval;
649 
650  auto& op_is_mapped_to_vars_result = std::get<0>(retval);
651  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
652  auto& gorCP_op_is_mapped_to_vars_result = std::get<1>(retval);
653  auto& gorSatCP_op_is_mapped_to_vars_result = std::get<2>(retval);
654  #endif
655 
656  for (auto& op : opgraph.opNodes()) {
657  for (auto& compatible_node : compatible_fu_nodes.at(op)) {
658  const OpAndFU op_and_fu {op, compatible_node};
659  auto var = std::unique_ptr<GRBVar[]>(model.addVars(1, GRB_BINARY))[0]; // free the returned pointer immediately
660  const std::string name = "op." + opgraph.getNodeRef(op).name + ".is_mapped_to." + mrrg.getNodeRef(compatible_node).getFullName();
661  var.set(GRB_StringAttr_VarName, name);
662  op_is_mapped_to_vars_result.emplace(op_and_fu, var);
663  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
664  gorCP_op_is_mapped_to_vars_result.emplace(op_and_fu, gorCP_solver.MakeBoolVar(name));
665  gorSatCP_op_is_mapped_to_vars_result.emplace(op_and_fu, makeBoolVar(gorSatCP_model, name));
666  #endif
667  if (options.fu_coster) {
668  const auto fu_cost = options.fu_coster(opgraph, mrrg, op, compatible_node);
669  var.set(GRB_DoubleAttr_Obj, fu_cost);
670  }
671  }
672  }
673  return retval;
674  }();
675 
676  const auto& op_is_mapped_to_vars = std::get<0>(op_is_mapped_to_var_maps);
677  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
678  const auto& gorCP_op_is_mapped_to_vars = std::get<1>(op_is_mapped_to_var_maps);
679  const auto& gorSatCP_op_is_mapped_to_vars = std::get<2>(op_is_mapped_to_var_maps);
680  #endif
681  solve_statistics.addInt("op_is_mapped_to_vars.size()",op_is_mapped_to_vars.size());
682 
683  const bool edge_vars_needed = (bool)options.edge_coster || options.use_edge_vars_constrained_via_implication_network;
684  const auto edge_is_used_vars_maps = [&]() {
685  std::tuple<
687  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
688  ,
691  #endif
692  > retval;
693 
694  auto& edge_is_used_vars_result = std::get<0>(retval);
695  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
696  auto& gorCP_edge_is_used_vars_result = std::get<1>(retval);
697  auto& gorSatCP_edge_is_used_vars_result = std::get<2>(retval);
698  #endif
699 
700  if (not edge_vars_needed) {
701  return retval;
702  }
703 
704  for (const auto& src_op : opgraph.opNodes()) {
705  for (const auto& out_edge : opgraph.outEdges(src_op)) {
706  const auto sink_op = opgraph.targetOfEdge(out_edge);
707  for (const auto& src_compatible_node : compatible_fu_nodes.at(src_op)) {
708  const auto src_op_and_fu = OpAndFU{src_op, src_compatible_node};
709  for (const auto& sink_compatible_node : compatible_fu_nodes.at(sink_op)) {
710  const auto sink_op_and_fu = OpAndFU{sink_op, sink_compatible_node};
711  const auto out_edge_and_fu = EdgeAndFU{out_edge, sink_compatible_node};
712  if (not treatAsNeighbours(src_op_and_fu, sink_op_and_fu)) { continue; }
713  if (edge_is_used_vars_result[src_op_and_fu].find(out_edge_and_fu) != edge_is_used_vars_result[src_op_and_fu].end()) {
714  continue; // multiple edges between ops is handled in the path variables
715  }
716  for (const auto& trip_count_and_paths : mrrg_paths.at({src_compatible_node,sink_compatible_node})) {
717  auto eu_var = std::unique_ptr<GRBVar[]>(model.addVars(1, GRB_BINARY))[0]; // free the returned pointer immediately
718  const auto var_name = from_src_at_to_sink_at_trip_count("edge_is_used", opgraph, mrrg,
719  src_op, src_compatible_node, out_edge, sink_compatible_node, trip_count_and_paths.first);
720  eu_var.set(GRB_StringAttr_VarName, var_name);
721  if (options.edge_coster) {
722  const auto edge_cost = options.edge_coster(fu_neighbours, *src_op, src_compatible_node, *sink_op, sink_compatible_node);
723  eu_var.set(GRB_DoubleAttr_Obj, edge_cost);
724  }
725  edge_is_used_vars_result[src_op_and_fu][out_edge_and_fu].emplace(trip_count_and_paths.first, eu_var);
726  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
727  gorCP_edge_is_used_vars_result[src_op_and_fu][out_edge_and_fu].emplace(
728  trip_count_and_paths.first, gorCP_solver.MakeBoolVar(var_name));
729  (void)gorSatCP_edge_is_used_vars_result;
730  // gorSatCP_edge_is_used_vars_result[src_op_and_fu][out_edge_and_fu].emplace(
731  // trip_count_and_paths.first, makeBoolVar(gorSatCP_model, var_name));
732  #endif
733  }
734  }
735  }
736  }
737  }
738 
739  return retval;
740  }();
741 
742  const auto& edge_is_used_vars = std::get<0>(edge_is_used_vars_maps);
743  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
744  const auto& gorCP_edge_is_used_vars = std::get<1>(edge_is_used_vars_maps);
745  const auto& gorSatCP_edge_is_used_vars = std::get<2>(edge_is_used_vars_maps);
746  #endif
747  solve_statistics.addInt("num edge_is_used_vars",
748  std::accumulate(edge_is_used_vars.begin(), edge_is_used_vars.end(), (std::ptrdiff_t)0, [](auto sum, auto&& elem) {
749  return std::accumulate(elem.second.begin(), elem.second.end(), sum, [](auto sum2, auto&& elem2) {
750  return sum2 + (std::ptrdiff_t)elem2.second.size();
751  });
752  })
753  );
754 
755  const auto path_is_used_vars_maps = [&]() {
756  std::tuple<
757  std::unordered_map<MRRGPathID, GRBVar, MRRGPathID::Hash>
758  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
759  ,
760  std::unordered_map<MRRGPathID, gor::IntVar*, MRRGPathID::Hash>,
761  std::unordered_map<MRRGPathID, GorSatCPVarID, MRRGPathID::Hash>
762  #endif
763  > retval;
764 
765  auto& path_is_used_vars_result = std::get<0>(retval);
766  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
767  auto& gorCP_path_is_used_vars_result = std::get<1>(retval);
768  auto& gorSatCP_path_is_used_vars_result = std::get<2>(retval);
769  #endif
770 
771  if (not options.general.getBool("path_exclusivity_modelling")) {
772  return retval;
773  }
774 
775  std::set<MRRGNodePair> covered_fu_pairs; // don't add the same paths more than once
776 
777  for (auto& src_op : opgraph.opNodes()) {
778  for (auto& sink_op : opgraph.outputOps(src_op)) {
779 
780  for (auto& src_compatible_node : compatible_fu_nodes.at(src_op)) {
781  const auto src_op_and_fu = OpAndFU{src_op, src_compatible_node};
782  for (auto& sink_compatible_node : compatible_fu_nodes.at(sink_op)) {
783  const auto sink_op_and_fu= OpAndFU {sink_op, sink_compatible_node};
784  if (not treatAsNeighbours(src_op_and_fu, sink_op_and_fu)) { continue; }
785  if (not covered_fu_pairs.emplace(src_compatible_node, sink_compatible_node).second) { continue; }
786 
787  for (auto& trip_count_and_paths : mrrg_paths.at({src_compatible_node, sink_compatible_node})) {
788  int path_num = 0;
789  for (auto& path : trip_count_and_paths.second) {
790  (void)path;
791  const auto mrrg_path_id = MRRGPathID {src_compatible_node, sink_compatible_node, trip_count_and_paths.first, path_num};
792  const auto var_name = from_mrrg_node_to_mrrg_node_trip_count_number("path_is_used", opgraph, mrrg,
793  src_compatible_node, sink_compatible_node, trip_count_and_paths.first, path_num);
794  auto var = std::unique_ptr<GRBVar[]>(model.addVars(1, GRB_BINARY))[0]; // free the returned pointer immediately
795  var.set(GRB_StringAttr_VarName, var_name);
796  path_is_used_vars_result.emplace(mrrg_path_id, var);
797  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
798  gorCP_path_is_used_vars_result.emplace(mrrg_path_id, gorCP_solver.MakeBoolVar(var_name));
799  (void)gorSatCP_path_is_used_vars_result;
800  // gorSatCP_path_is_used_vars_result.emplace(mrrg_path_id, makeBoolVar(gorSatCP_model, var_name));
801  #endif
802  path_num += 1;
803  }
804  }
805  }
806  }
807  }
808  }
809  return retval;
810  }();
811 
812  const auto& path_is_used_vars = std::get<0>(path_is_used_vars_maps);
813  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
814  const auto& gorCP_path_is_used_vars = std::get<1>(path_is_used_vars_maps);
815  const auto& gorSatCP_path_is_used_vars = std::get<2>(path_is_used_vars_maps);
816  #endif
817  solve_statistics.addInt("path_is_used_vars.size()",path_is_used_vars.size());
818 
819  const auto& topological_trip_count_vars = lazy_pointer([&]() {
820  std::tuple<
821  std::map<MRRG::NodeDescriptor,GRBVar>
822  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
823  #endif
824  > retval;
825 
826  for (const auto& node : all_compatible_nodes_set) {
827  auto var = std::unique_ptr<GRBVar[]>(model.addVars(1, GRB_INTEGER))[0];
828  const auto var_name = makeNameSolverSafe("topological_trip_count_for." + mrrg.getNodeRef(node).getFullName());
829  var.set(GRB_StringAttr_VarName, var_name);
830  std::get<0>(retval).emplace(node, var);
831  }
832 
833  return retval;
834  });
835 
836  timing_seq.tick("create variables");
837 
838  // constraints.registerGenerator(OpMustBeMapped, [&](ConstraintSet::ConstraintGeneratorParams params) {
839  for (auto& op : opgraph.opNodes()) {
840  GRBLinExpr sum_of_nodes_mapped_to_op;
841  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
842  std::vector<gor::IntVar*> gorCP_op_vars;
843  std::vector<GorSatCPVarID> gorSatCP_op_vars;
844  #endif
845  OpGraphOp *opJ = op;
846  bool isIOorMem = (opJ->opcode == OpCode::LOAD) || (opJ->opcode == OpCode::STORE) || (opJ->opcode == OpCode::INPUT) || (opJ->opcode == OpCode::OUTPUT);
847  for (auto& compatible_node : compatible_fu_nodes.at(op)) {
848  const OpAndFU op_and_fu {op, compatible_node};
849  sum_of_nodes_mapped_to_op += op_is_mapped_to_vars.at(op_and_fu);
850  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
851  gorCP_op_vars.push_back(gorCP_op_is_mapped_to_vars.at(op_and_fu));
852  gorSatCP_op_vars.push_back(gorSatCP_op_is_mapped_to_vars.at(op_and_fu));
853  #endif
854  }
855  if (isIOorMem) {
856  auto c = model.addConstr(sum_of_nodes_mapped_to_op == 1);
857  const auto cname = makeNameSolverSafe("this_op_must_be_mapped." + opgraph.getNodeRef(op).getName());
858  c.set(GRB_StringAttr_ConstrName, cname);
859 
860  }
861  else {
862  auto c = [&](){ if (options.general.getBool("allow_multiple_op_mappings")) {
863  return model.addConstr(sum_of_nodes_mapped_to_op >= 1);
864  } else {
865  return model.addConstr(sum_of_nodes_mapped_to_op == 1);
866  } }();
867  const auto cname = makeNameSolverSafe("this_op_must_be_mapped." + opgraph.getNodeRef(op).getName());
868  c.set(GRB_StringAttr_ConstrName, cname);
869  }
870  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
871  if (options.general.getBool("allow_multiple_op_mappings")) {
872  gorCP_solver.AddConstraint(gorCP_solver.MakeEquality(gorCP_solver.MakeMax(gorCP_op_vars), 1));
873  addLogicalOr(gorSatCP_model, gorSatCP_op_vars);
874  } else {
875  gorCP_solver.AddConstraint(gorCP_solver.MakeCount(gorCP_op_vars, 1, 1));
876  addConstrainedSum(gorSatCP_model, gorSatCP_op_vars, {{1,1}});
877  }
878  #endif
879  }
880  timing_seq.tick("OpMustBeMapped constraints", 0.01);
881  // });
882 
883  // constraints.registerGenerator(FunctionalUnitExclusivity, [&](ConstraintSet::ConstraintGeneratorParams params) {
884  for (const auto& outer_compatible_node : all_compatible_nodes_set) {
885  GRBLinExpr sum_of_ops_mapped_to_node;
886  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
887  std::vector<gor::IntVar*> gorCP_fu_vars;
888  std::vector<GorSatCPVarID> gorSatCP_fu_vars;
889  #endif
890  for (const auto& op : opgraph.opNodes()) {
891  for (const auto& compatible_node : compatible_fu_nodes.at(op)) {
892  if (outer_compatible_node == compatible_node) {
893  const OpAndFU op_and_fu {op, compatible_node};
894  sum_of_ops_mapped_to_node += op_is_mapped_to_vars.at(op_and_fu);
895  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
896  gorCP_fu_vars.push_back(gorCP_op_is_mapped_to_vars.at(op_and_fu));
897  gorSatCP_fu_vars.push_back(gorSatCP_op_is_mapped_to_vars.at(op_and_fu));
898  #endif
899  }
900  }
901  }
902  auto c = model.addConstr(sum_of_ops_mapped_to_node <= 1);
903  const auto cname = makeNameSolverSafe("functional_unit_exclusivity_for." + mrrg.getNodeRef(outer_compatible_node).getFullName());
904  c.set(GRB_StringAttr_ConstrName, cname);
905  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
906  gorCP_solver.AddConstraint(gorCP_solver.MakeAllDifferentExcept(gorCP_fu_vars, 0));
907  addConstrainedSum(gorSatCP_model, gorSatCP_fu_vars, {{0,1}});
908  #endif
909  }
910  timing_seq.tick("FunctionalUnitExclusivity constraints", 0.01);
911  // });
912 
913  // constraints.registerGenerator(FunctionalUnitFanout, [&](ConstraintSet::ConstraintGeneratorParams params) {
914  if (not options.use_edge_vars_constrained_via_implication_network) {
915  for (auto& src_op : opgraph.opNodes()) {
916  for (auto& sink_op : opgraph.outputOps(src_op)) {
917  for (auto& src_compatible_node : compatible_fu_nodes.at(src_op)) {
918  const OpAndFU src_op_and_fu {src_op, src_compatible_node};
919 
920  GRBLinExpr sum_of_sinks_used;
921  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
922  std::vector<gor::IntVar*> gorCP_sinks_used;
923  std::vector<GorSatCPVarID> gorSatCP_sinks_used;
924  #endif
925  for (auto& sink_compatible_node : compatible_fu_nodes.at(sink_op)) {
926  const OpAndFU sink_op_and_fu {sink_op, sink_compatible_node};
927  if (treatAsNeighbours(src_op_and_fu, sink_op_and_fu)) {
928  sum_of_sinks_used += op_is_mapped_to_vars.at(sink_op_and_fu);
929  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
930  gorCP_sinks_used.push_back(gorCP_op_is_mapped_to_vars.at(sink_op_and_fu));
931  gorSatCP_sinks_used.push_back(gorSatCP_op_is_mapped_to_vars.at(sink_op_and_fu));
932  #endif
933  }
934  }
935 
936  auto& src_var = op_is_mapped_to_vars.at(src_op_and_fu);
937  auto c = model.addConstr(sum_of_sinks_used >= src_var);
938  const auto constr_name = from_src_at_to_sink("functional_unit_fanout", opgraph, mrrg,
939  src_op, src_compatible_node, sink_op, true);
940  c.set(GRB_StringAttr_ConstrName, constr_name);
941  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
942  auto& gorCP_src_var = gorCP_op_is_mapped_to_vars.at(src_op_and_fu);
943  const auto gorSatCP_src_var = gorSatCP_op_is_mapped_to_vars.at(src_op_and_fu);
944  if (gorCP_sinks_used.empty()) {
945  gorCP_solver.AddConstraint(gorCP_solver.MakeEquality(gorCP_src_var, 0));
946  addConstrainedSum(gorSatCP_model, std::vector<GorSatCPVarID>{gorSatCP_src_var}, {{0,0}}); // not sure if better way to set to zero
947  } else {
948  gorCP_solver.AddConstraint(gorCP_solver.MakeGreaterOrEqual(gorCP_solver.MakeMax(gorCP_sinks_used), gorCP_src_var));
949  addMaxConstraint(gorSatCP_model, gorSatCP_sinks_used, gorSatCP_src_var);
950  }
951  #endif
952  }
953  }
954  }
955  timing_seq.tick("FunctionalUnitFanout constraints", 0.01);
956  }
957  // });
958 
959  // Using FU f implies that for {each fanin Op} there is {an edge that ends at f}
960  // constraints.registerGenerator(FunctionalUnitFaninRequired, [&](ConstraintSet::ConstraintGeneratorParams params) {
961  if (options.use_edge_vars_constrained_via_implication_network) {
962  for (const auto& src_op : opgraph.opNodes()) {
963  for (const auto& out_edge : opgraph.outEdges(src_op)) {
964  const auto sink_op = opgraph.targetOfEdge(out_edge);
965  bool has_fanin_edges = false;
966  for (const auto& sink_compatible_node : compatible_fu_nodes.at(sink_op)) {
967  const auto sink_op_and_fu = OpAndFU{sink_op, sink_compatible_node};
968 
969  GRBLinExpr sum_of_in_edges_used;
970  for (const auto& src_compatible_node : compatible_fu_nodes.at(src_op)) {
971  const auto src_op_and_fu = OpAndFU{src_op, src_compatible_node};
972  if (not treatAsNeighbours(src_op_and_fu, sink_op_and_fu)) { continue; }
973  const auto out_edge_and_fu = EdgeAndFU{out_edge, sink_compatible_node};
974  for (const auto& trip_count_and_paths : mrrg_paths.at({src_compatible_node, sink_compatible_node})) {
975  has_fanin_edges = true;
976  sum_of_in_edges_used += edge_is_used_vars.at(src_op_and_fu).at(out_edge_and_fu).at(trip_count_and_paths.first);
977  }
978  }
979 
980  const auto& sink_var = op_is_mapped_to_vars.at(sink_op_and_fu);
981  auto c = model.addConstr(sink_var <= sum_of_in_edges_used);
982  const auto constr_name = from_src_to_sink_at("functional_unit_fanin_required", opgraph, mrrg,
983  src_op, out_edge, sink_compatible_node, true);
984  c.set(GRB_StringAttr_ConstrName, constr_name);
985  }
986 
987  if (not has_fanin_edges && not options.general.getBool("silence_warnings")) {
988  std::cout << "Warning: no edges for " << opgraph.getNodeRef(src_op) << " -> " << opgraph.getNodeRef(sink_op) << std::endl;
989  std::cout << "src compatible nodes: ";
990  print_container(std::cout, compatible_fu_nodes.at(src_op));
991  std::cout << std::endl << "sink compatible nodes: ";
992  print_container(std::cout, compatible_fu_nodes.at(sink_op));
993  std::cout << std::endl;
994  }
995  }
996  }
997  timing_seq.tick("FunctionalUnitFaninRequired constraints", 0.01);
998  }
999  // });
1000 
1001  // Using an edge from a FU implies that that FU is in use
1002  // constraints.registerGenerator(FunctionalUnitFanoutImpliesUsage, [&](ConstraintSet::ConstraintGeneratorParams params) {
1003  if (options.use_edge_vars_constrained_via_implication_network) {
1004  for (const auto& src_op : opgraph.opNodes()) {
1005  for (const auto& out_edge : opgraph.outEdges(src_op)) {
1006  const auto sink_op = opgraph.targetOfEdge(out_edge);
1007  for (const auto& src_compatible_node : compatible_fu_nodes.at(src_op)) {
1008  const auto src_op_and_fu = OpAndFU{src_op, src_compatible_node};
1009  const auto& src_var = op_is_mapped_to_vars.at(src_op_and_fu);
1010  for (const auto& sink_compatible_node : compatible_fu_nodes.at(sink_op)) {
1011  const auto out_edge_and_fu = EdgeAndFU{out_edge, sink_compatible_node};
1012  const auto sink_op_and_fu = OpAndFU{sink_op, sink_compatible_node};
1013  if (not treatAsNeighbours(src_op_and_fu, sink_op_and_fu)) { continue; }
1014  for (const auto& trip_count_and_paths : mrrg_paths.at({src_compatible_node, sink_compatible_node})) {
1015  const auto& eu_var = edge_is_used_vars.at(src_op_and_fu).at(out_edge_and_fu).at(trip_count_and_paths.first);
1016  auto c = model.addConstr(eu_var <= src_var);
1017  const auto constr_name = from_src_at_to_sink_at_trip_count("functional_unit_fanout_implies_usage", opgraph, mrrg,
1018  src_op, src_compatible_node, out_edge, sink_compatible_node, trip_count_and_paths.first, true);
1019  c.set(GRB_StringAttr_ConstrName, constr_name);
1020 
1021  const auto sink_var = op_is_mapped_to_vars.at(sink_op_and_fu);
1022  auto c2 = model.addConstr(eu_var <= sink_var);
1023  const auto constr2_name = from_src_at_to_sink_at_trip_count("functional_unit_fanin_implies_usage", opgraph, mrrg,
1024  src_op, src_compatible_node, out_edge, sink_compatible_node, trip_count_and_paths.first, true);
1025  c2.set(GRB_StringAttr_ConstrName, constr2_name);
1026  }
1027  }
1028 
1029  }
1030  }
1031  }
1032  timing_seq.tick("FunctionalUnitFanoutImpliesUsage constraints", 0.01);
1033  }
1034  // });
1035 
1036  // f_uv && fu_pv == e_opuv; needed if a cost function is given for a placement, and edges vars are not needed otherwise
1037  // constraints.registerGenerator(EdgeUsage, [&](ConstraintSet::ConstraintGeneratorParams params) {
1038  if (options.edge_coster && not options.use_edge_vars_constrained_via_implication_network) {
1039  for (auto& src_op : opgraph.opNodes()) {
1040  for (const auto& out_edge : opgraph.outEdges(src_op)) {
1041  const auto sink_op = opgraph.targetOfEdge(out_edge);
1042  for (auto& src_compatible_node : compatible_fu_nodes.at(src_op)) {
1043  const auto src_op_and_fu = OpAndFU{src_op, src_compatible_node};
1044  auto& src_var = op_is_mapped_to_vars.at(src_op_and_fu);
1045 
1046  for (auto& sink_compatible_node : compatible_fu_nodes.at(sink_op)) {
1047  const auto out_edge_and_fu = EdgeAndFU{out_edge, sink_compatible_node};
1048  const auto sink_op_and_fu = OpAndFU{sink_op, sink_compatible_node};
1049  if (treatAsNeighbours(src_op_and_fu, sink_op_and_fu)) { continue; }
1050  auto& sink_var = op_is_mapped_to_vars.at(sink_op_and_fu);
1051  for (const auto& trip_count_and_paths : mrrg_paths.at({src_compatible_node, sink_compatible_node})) {
1052  auto eu_var = edge_is_used_vars.at(src_op_and_fu).at(out_edge_and_fu).at(trip_count_and_paths.first);
1053  if (0 == options.edge_coster(fu_neighbours, *src_op, src_compatible_node, *sink_op, sink_compatible_node)) {
1054  continue;
1055  }
1056 
1057  std::array<GRBVar, 2> r_values { src_var, sink_var };
1058  const auto constr_name = from_src_at_to_sink_at_trip_count("edge_usage", opgraph, mrrg,
1059  src_op, src_compatible_node, out_edge, sink_compatible_node, trip_count_and_paths.first);
1060  model.addGenConstrAnd(
1061  eu_var,
1062  r_values.data(), r_values.size(),
1063  constr_name
1064  );
1065  }
1066  }
1067 
1068  }
1069  }
1070  }
1071  timing_seq.tick("EdgeUsage constraints", 0.01);
1072  }
1073  // });
1074 
1075  // constraints.registerGenerator(EdgeRequiresPath, [&](ConstraintSet::ConstraintGeneratorParams params) {
1076  if (options.general.getBool("path_exclusivity_modelling")) {
1077  for (const auto& src_op : opgraph.opNodes()) {
1078  const auto& all_output_ops = opgraph.outputOps(src_op);
1079  const auto unique_output_ops = std::set<OpGraph::OpDescriptor>(all_output_ops.begin(), all_output_ops.end());
1080  const auto& all_out_edges = opgraph.outEdges(src_op);
1081  for (const auto& sink_op : unique_output_ops) {
1082  for (const auto& src_compatible_node : compatible_fu_nodes.at(src_op)) {
1083  const auto src_op_and_fu = OpAndFU{src_op, src_compatible_node};
1084  for (const auto& sink_compatible_node : compatible_fu_nodes.at(sink_op)) {
1085  const auto sink_op_and_fu = OpAndFU {sink_op, sink_compatible_node};
1086  if (not treatAsNeighbours(src_op_and_fu, sink_op_and_fu)) { continue; }
1087  for (const auto& trip_count_and_paths : mrrg_paths.at({src_compatible_node, sink_compatible_node})) {
1088  GRBLinExpr number_of_paths_used;
1089  GRBLinExpr number_of_edges_used;
1090  std::set<int> added_paths;
1091 
1092  for (const auto& out_edge : all_out_edges) {
1093  if (opgraph.targetOfEdge(out_edge) != sink_op) { continue; }
1094  const auto out_edge_and_fu = EdgeAndFU{out_edge, sink_compatible_node};
1095  number_of_edges_used += edge_is_used_vars.at(src_op_and_fu).at(out_edge_and_fu).at(trip_count_and_paths.first);
1096 
1097  int path_num = -1;
1098  for (auto& path : trip_count_and_paths.second) {
1099  path_num += 1;
1100  if (not walkIsCompatibleWithOperand(mrrg, path, opgraph.getOperandTag(out_edge))) continue;
1101  const auto path_id = MRRGPathID{src_compatible_node, sink_compatible_node, trip_count_and_paths.first, path_num};
1102  if (added_paths.insert(path_num).second) {
1103  number_of_paths_used += path_is_used_vars.at(path_id);
1104  }
1105  }
1106  }
1107 
1108  const auto constr_name = from_src_at_to_sink_at_trip_count("edges_require_paths", opgraph, mrrg,
1109  src_op, src_compatible_node, sink_op, sink_compatible_node, trip_count_and_paths.first, true);
1110  auto c = model.addConstr(number_of_edges_used <= number_of_paths_used);
1111  c.set(GRB_StringAttr_ConstrName, constr_name);
1112 
1113  for (const auto& out_edge : all_out_edges) {
1114  if (opgraph.targetOfEdge(out_edge) != sink_op) { continue; }
1115  const auto out_edge_and_fu = EdgeAndFU{out_edge, sink_compatible_node};
1116  const auto eu_var = edge_is_used_vars.at(src_op_and_fu).at(out_edge_and_fu).at(trip_count_and_paths.first);
1117 
1118  GRBLinExpr number_of_paths_used;
1119  int path_num = -1;
1120  for (auto& path : trip_count_and_paths.second) {
1121  path_num += 1;
1122  if (not walkIsCompatibleWithOperand(mrrg, path, opgraph.getOperandTag(out_edge))) continue;
1123  const auto path_id = MRRGPathID{src_compatible_node, sink_compatible_node, trip_count_and_paths.first, path_num};
1124  number_of_paths_used += path_is_used_vars.at(path_id);
1125  }
1126 
1127  const auto constr_name = from_src_at_to_sink_at_trip_count("edge_requires_path", opgraph, mrrg,
1128  src_op, src_compatible_node, out_edge, sink_compatible_node, trip_count_and_paths.first, true);
1129  auto c = model.addConstr(eu_var <= number_of_paths_used);
1130  c.set(GRB_StringAttr_ConstrName, constr_name);
1131  }
1132  }
1133  }
1134  }
1135  }
1136  }
1137  timing_seq.tick("EdgeRequiresPath constraints", 0.01);
1138  }
1139  // });
1140 
1141  // of the paths that connect a given FU pair and go through the same FU input, at most one may be used
1142  // constraints.registerGenerator(FuInputsUsedByOnePath, [&](ConstraintSet::ConstraintGeneratorParams params) {
1143  if (options.general.getBool("path_exclusivity_modelling")) {
1144  for (const auto& fu_pair_and_paths : mrrg_paths) {
1145  const auto sink_fu = fu_pair_and_paths.first.sink_fu;
1146  const auto src_fu = fu_pair_and_paths.first.src_fu;
1147  std::map<MRRG::NodeDescriptor, GRBLinExpr> number_of_paths_used_for_input;
1148  for (const auto& trip_count_and_paths : mrrg_paths.at(fu_pair_and_paths.first)) {
1149  int path_num = 0;
1150  for (auto& path : trip_count_and_paths.second) {
1151  const auto path_id = MRRGPathID{
1152  src_fu,
1153  sink_fu,
1154  trip_count_and_paths.first,
1155  path_num++,
1156  };
1157  const auto input_node = path.at(path.size() - 2); // second last
1158  number_of_paths_used_for_input[input_node] += path_is_used_vars.at(path_id);
1159  }
1160  }
1161  for (const auto& input_and_number_of_paths_used : number_of_paths_used_for_input) {
1162  const auto constr_name = from_mrrg_node_to_mrrg_node(
1163  "use_one_path_per_input", opgraph, mrrg,
1164  src_fu, input_and_number_of_paths_used.first, true
1165  );
1166  auto c = model.addConstr(input_and_number_of_paths_used.second <= 1);
1167  c.set(GRB_StringAttr_ConstrName, constr_name);
1168  }
1169  }
1170  timing_seq.tick("FuInputsUsedByOnePath constraints", 0.01);
1171  }
1172  // });
1173 
1174  // constraints.registerGenerator(PathExclusivity, [&](ConstraintSet::ConstraintGeneratorParams params) {
1175  if (options.general.getBool("path_exclusivity_modelling")) {
1176  if (options.general.getBool("path_exclusivity_constraints_are_pairwise")) {
1177  std::set<std::array<MRRGPathID,2>> already_constrainted_to_only_use_one_path;
1178  std::set<MRRGNodePair> visited_fu_pairs1;
1179  std::set<std::pair<MRRGNodePair, MRRGNodePair>> visited_fu_pair_pairs;
1180 
1181  for (const auto& src_op1 : opgraph.opNodes()) {
1182  for (const auto& sink_op1 : opgraph.outputOps(src_op1)) {
1183  for (const auto& src_compatible_node1 : compatible_fu_nodes.at(src_op1)) {
1184  for (const auto& sink_compatible_node1 : compatible_fu_nodes.at(sink_op1)) {
1185  const auto src_op_and_fu1 = OpAndFU{src_op1, src_compatible_node1};
1186  const auto sink_op_and_fu1= OpAndFU{sink_op1, sink_compatible_node1};
1187  const auto fu_pair1 = MRRGNodePair{src_compatible_node1, sink_compatible_node1};
1188  if (not treatAsNeighbours(src_op_and_fu1, sink_op_and_fu1)) { continue; }
1189  if (not visited_fu_pairs1.emplace(fu_pair1).second) { continue; }
1190  for (const auto& src_op2 : opgraph.opNodes()) {
1191  for (const auto& sink_op2 : opgraph.outputOps(src_op2)) {
1192  for (const auto& src_compatible_node2 : compatible_fu_nodes.at(src_op2)) {
1193  for (const auto& sink_compatible_node2 : compatible_fu_nodes.at(sink_op2)) {
1194  const auto src_op_and_fu2 = OpAndFU{src_op2, src_compatible_node2};
1195  const auto sink_op_and_fu2= OpAndFU {sink_op2, sink_compatible_node2};
1196  const auto fu_pair2 = MRRGNodePair{src_compatible_node2, sink_compatible_node2};
1197  if (not treatAsNeighbours(src_op_and_fu2, sink_op_and_fu2)) { continue; }
1198  if (not visited_fu_pair_pairs.emplace(fu_pair1, fu_pair2).second) { continue; }
1199 
1200  for (const auto& trip_count_and_paths1 : mrrg_paths.at({src_compatible_node1, sink_compatible_node1})) {
1201  int path_num1 = -1;
1202  for (const auto& path1 : trip_count_and_paths1.second) {
1203  path_num1 += 1;
1204 
1205  for (const auto& trip_count_and_paths2 : mrrg_paths.at({src_compatible_node2, sink_compatible_node2})) {
1206  int path_num2 = -1;
1207  for (const auto& path2 : trip_count_and_paths2.second) {
1208  path_num2 += 1;
1209 
1210  int path1_cumulative_latency = 0;
1211  for (const auto& node_in_path1 : path1) {
1212  path1_cumulative_latency += mrrg.getNodeRef(node_in_path1).getLatency();
1213  int path2_cumulative_latency = 0;
1214  for (const auto& node_in_path2 : path2) {
1215  path2_cumulative_latency += mrrg.getNodeRef(node_in_path2).getLatency();
1216  if (node_in_path2->type != MRRG_NODE_ROUTING) { continue; }
1217  // no overlap allowed
1218  if (node_in_path1 == node_in_path2) {
1219  // unless the paths start at the same FU
1220  if (path2.front() == path1.front()) {
1221  // and their latency matches
1222  if (path1_cumulative_latency == path2_cumulative_latency) { continue; }
1223  }
1224  } else {
1225  continue;
1226  }
1227 
1228  decltype(already_constrainted_to_only_use_one_path)::value_type incompatible_paths {{
1229  { src_compatible_node1, sink_compatible_node1, trip_count_and_paths1.first, path_num1 },
1230  { src_compatible_node2, sink_compatible_node2, trip_count_and_paths2.first, path_num2 },
1231  }};
1232  if (
1233  not already_constrainted_to_only_use_one_path.insert(incompatible_paths).second
1234  ) {
1235  continue;
1236  }
1237  const auto cname = makeNameSolverSafe(
1238  from_mrrg_node_to_mrrg_node_trip_count_number("path_incompatibility_between", opgraph, mrrg,
1239  src_compatible_node1, sink_compatible_node1, trip_count_and_paths1.first, path_num1
1240  ) + from_mrrg_node_to_mrrg_node_trip_count_number(".and", opgraph, mrrg,
1241  src_compatible_node2, sink_compatible_node2, trip_count_and_paths2.first, path_num2
1242  ) + ".concerning_node." + mrrg.getNodeRef(node_in_path1).getFullName()
1243  );
1244  GRBLinExpr number_of_times_node_is_used;
1245  for (const auto& path_id : incompatible_paths) {
1246  number_of_times_node_is_used += path_is_used_vars.at(path_id);
1247  }
1248  auto c = model.addConstr(number_of_times_node_is_used
1249  <= mrrg.getNodeRef(node_in_path1).capacity * allowable_routing_usage_multiplier);
1250  c.set(GRB_StringAttr_ConstrName, cname);
1251  break; // done with this path
1252  }
1253  }
1254  }
1255  }
1256  }
1257  }
1258  }
1259  }
1260  }
1261  }
1262  }
1263  }
1264  }
1265  }
1266  } else {
1267  std::set<std::set<MRRGPathID>> already_constrainted_to_only_use_one_path;
1268 
1269  // for each edge
1270  for (const auto& src_op1 : opgraph.opNodes()) {
1271  for (const auto& sink_op1 : opgraph.outputOps(src_op1)) {
1272 
1273  // for each path
1274  for (const auto& src_compatible_node1 : compatible_fu_nodes.at(src_op1)) {
1275  for (const auto& sink_compatible_node1 : compatible_fu_nodes.at(sink_op1)) {
1276  const auto src_op_and_fu1 = OpAndFU{src_op1, src_compatible_node1};
1277  const auto sink_op_and_fu1= OpAndFU {sink_op1, sink_compatible_node1};
1278  if (not treatAsNeighbours(src_op_and_fu1, sink_op_and_fu1)) { continue; }
1279 
1280  for (const auto& trip_count_and_paths1 : mrrg_paths.at({src_compatible_node1, sink_compatible_node1})) {
1281  int path_num1 = 0;
1282  for (const auto& path1 : trip_count_and_paths1.second) {
1283 
1284  // for each node in the path
1285  int path1_cumulative_latency = 0;
1286  for (const auto& node_in_path1 : path1) {
1287  path1_cumulative_latency += mrrg.getNodeRef(node_in_path1).getLatency();
1288 
1289  // for each edge
1290  for (const auto& src_op2 : opgraph.opNodes()) {
1291  for (const auto& sink_op2 : opgraph.outputOps(src_op2)) {
1292 
1293  std::set<MRRGPathID> incompatible_paths;
1294  incompatible_paths.emplace(src_compatible_node1, sink_compatible_node1, trip_count_and_paths1.first, path_num1);
1295 
1296  // for each path
1297  for (const auto& src_compatible_node2 : compatible_fu_nodes.at(src_op2)) {
1298  for (const auto& sink_compatible_node2 : compatible_fu_nodes.at(sink_op2)) {
1299  const auto src_op_and_fu2 = OpAndFU{src_op2, src_compatible_node2};
1300  const auto sink_op_and_fu2= OpAndFU {sink_op2, sink_compatible_node2};
1301  if (not treatAsNeighbours(src_op_and_fu2, sink_op_and_fu2)) { continue; }
1302 
1303  for (const auto& trip_count_and_paths2 : mrrg_paths.at({src_compatible_node2, sink_compatible_node2})) {
1304  int path_num2 = 0;
1305  for (const auto& path2 : trip_count_and_paths2.second) {
1306 
1307  // for each node in the path
1308  int path2_cumulative_latency = 0;
1309  for (const auto& node_in_path2 : path2) {
1310  path2_cumulative_latency += mrrg.getNodeRef(node_in_path2).getLatency();
1311  if (node_in_path2->type != MRRG_NODE_ROUTING) { continue; }
1312  // no overlap allowed
1313  if (node_in_path1 == node_in_path2) {
1314  // unless the paths start at the same FU
1315  if (path2.front() == path1.front()) {
1316  // and their latency matches
1317  if (path1_cumulative_latency == path2_cumulative_latency) { continue; }
1318  }
1319  } else {
1320  continue;
1321  }
1322 
1323  incompatible_paths.emplace(src_compatible_node2, sink_compatible_node2, trip_count_and_paths2.first, path_num2);
1324  break; // done with this path
1325  }
1326 
1327  path_num2 += 1;
1328  }
1329  }
1330 
1331  if (
1332  incompatible_paths.size() > 1
1333  && already_constrainted_to_only_use_one_path.insert(incompatible_paths).second
1334  ) {
1335  GRBLinExpr number_of_times_node_is_used;
1336  for (const auto& path_id : incompatible_paths) {
1337  number_of_times_node_is_used += path_is_used_vars.at(path_id);
1338  }
1339  auto c = model.addConstr(number_of_times_node_is_used
1340  <= mrrg.getNodeRef(node_in_path1).capacity * allowable_routing_usage_multiplier);
1341  const auto cname = makeNameSolverSafe(
1342  from_mrrg_node_to_mrrg_node_trip_count_number("path_incompatibility_with", opgraph, mrrg,
1343  src_compatible_node1, sink_compatible_node1, trip_count_and_paths1.first, path_num1
1344  )
1345  + ".concerning_node." + mrrg.getNodeRef(node_in_path1).getFullName()
1346  + ".and_src." + src_op2->getName()
1347  + ".to_sink." + sink_op2->getName()
1348  );
1349  c.set(GRB_StringAttr_ConstrName, cname);
1350  }
1351  }
1352  }
1353  }
1354  }
1355  }
1356  path_num1 += 1;
1357  }
1358  }
1359  }
1360  }
1361  }
1362  }
1363 
1364  }
1365  timing_seq.tick("PathExclusivity constraints", 0.01);
1366  }
1367  // });
1368 
1369  // Sum trip counts for particular DFG edges: re-convergence must be balanced, and cycles must have latency of II
1370  // constraint_set.registerGenerator(ConstraintGroup::LatencyBalancing, [&](ConstraintSet::ConstraintGeneratorParams params) {
1371  if (options.add_trip_count_balancing_constraints) {
1372  const auto sum_of_edge_vars_times_trip_count = [&](const auto& path) {
1373  GRBLinExpr result;
1374  for (const auto& edge : path) {
1375  const auto src_op = opgraph.inputOp(edge.val);
1376  const auto sink_op = opgraph.targetOfEdge(edge);
1377  for (const auto& src_compatible_node : compatible_fu_nodes.at( src_op)) {
1378  for (const auto& sink_compatible_node : compatible_fu_nodes.at(sink_op)) {
1379  const auto edge_and_sink_fu = EdgeAndFU{edge, sink_compatible_node};
1380  const auto src_op_and_fu = OpAndFU{ src_op, src_compatible_node};
1381  const auto sink_op_and_fu = OpAndFU{sink_op, sink_compatible_node};
1382  if (not treatAsNeighbours(src_op_and_fu, sink_op_and_fu)) { continue; }
1383  for (const auto& trip_count_and_paths : mrrg_paths.at({src_compatible_node, sink_compatible_node})) {
1384  if (trip_count_and_paths.first <= 0) { continue; } // cleaner model
1385  const auto& eu_var = edge_is_used_vars.at(src_op_and_fu).at(edge_and_sink_fu).at(trip_count_and_paths.first);
1386  result += trip_count_and_paths.first * eu_var;
1387  }
1388  }
1389  }
1390  }
1391  return result;
1392  };
1393 
1394  for(const auto & path_pair : trails_to_balance->noncyclic_trail_pairs) {
1395  auto sum1 = sum_of_edge_vars_times_trip_count(path_pair.first);
1396  auto sum2 = sum_of_edge_vars_times_trip_count(path_pair.second);
1397  const auto constraint_name = makeNameSolverSafe(string_from_stream([&](auto&& s) {
1398  s << "line_balance." << opgraph.getNodeRef(opgraph.inputOp(path_pair.first.front().val)).getName()
1399  << ".to." << opgraph.getNodeRef(opgraph.targetOfEdge(path_pair.first.back())).getName();
1400  }));
1401  auto c = model.addConstr(sum1 == sum2);
1402  c.set(GRB_StringAttr_ConstrName, constraint_name);
1403  }
1404 
1405  for(const auto & path : trails_to_balance->cyclic_trails) {
1406  auto sum = sum_of_edge_vars_times_trip_count(path);
1407  const auto constraint_name = makeNameSolverSafe(string_from_stream([&](auto&& s) {
1408  s << "cycle_balance.";
1409  print_container(s, path, "", "", ".");
1410  }));
1411  auto c = model.addConstr(sum == 1);
1412  c.set(GRB_StringAttr_ConstrName, constraint_name);
1413  }
1414  timing_seq.tick("LatencyBalancing constraints", 0.01);
1415  }
1416  // });
1417 
1418  // Assign an integer to each FU.
1419  // For each time a path goes from context N to 0 ("trip_count") the target must be trip_count more than the source
1420  // Edges in DFG with specified cycle latencies subtract from the required trip_count.
1421  // constraint_set.registerGenerator(ConstraintGroup::TopologicalTripCount, [&](ConstraintSet::ConstraintGeneratorParams params) {
1422  if (options.add_trip_count_topological_constraints) {
1423  for (const auto& src_op : opgraph.opNodes()) {
1424  for (const auto& out_edge : opgraph.outEdges(src_op)) {
1425  const auto sink_op = opgraph.targetOfEdge(out_edge);
1426 
1427  // for mappable FU pair
1428  for (const auto& src_compatible_node : compatible_fu_nodes.at( src_op)) {
1429  for (const auto& sink_compatible_node : compatible_fu_nodes.at(sink_op)) {
1430  const auto edge_and_sink_fu = EdgeAndFU{out_edge, sink_compatible_node};
1431  const auto src_op_and_fu = OpAndFU{ src_op, src_compatible_node};
1432  const auto sink_op_and_fu = OpAndFU{sink_op, sink_compatible_node};
1433  if (not treatAsNeighbours(src_op_and_fu, sink_op_and_fu)) { continue; }
1434  for (const auto& trip_count_and_paths : mrrg_paths.at({src_compatible_node, sink_compatible_node})) {
1435  const auto constraint_name = makeNameSolverSafe(from_src_at_to_sink_at_trip_count("topo_trip_count", opgraph, mrrg,
1436  src_op, src_compatible_node, out_edge, sink_compatible_node, trip_count_and_paths.first));
1437  model.addGenConstrIndicator(
1438  edge_is_used_vars.at(src_op_and_fu).at(edge_and_sink_fu).at(trip_count_and_paths.first), true,
1439  std::get<0>(*topological_trip_count_vars).at(src_compatible_node) + trip_count_and_paths.first
1440  == std::get<0>(*topological_trip_count_vars).at(sink_compatible_node) + dfg_edge_latency->at(out_edge),
1441  constraint_name
1442  );
1443  }
1444  }
1445  }
1446  }
1447  }
1448  timing_seq.tick("TopologicalTripCount constraints", 0.01);
1449  }
1450  // });
1451 
1452  Mapping result = initial_mapping;
1453  result.clear();
1455  int cb_count = 0;
1456 
1457  const auto status_print_interval = options.general.getInt("status_print_interval");
1458  const auto evaluate_solution = [
1459  // maple/Ubuntu 16.04 g++ is old & buggy. must use explicit captures to prevent 'internal compiler error'
1460  &cb_count, &status_print_interval, &timing_seq, &initial_mapping, &opgraph,
1461  &compatible_fu_nodes, &mrrg_paths, &treatAsNeighbours, &mrrg, &options, &result, &print_solve_progress
1462  ](auto&& is_op_using_fu, auto&& is_path_used) {
1463  cb_count += 1;
1464  if (cb_count != 1 && cb_count % status_print_interval == 0) {
1465  timing_seq.tick("at " + std::to_string(cb_count) +" solutions", 0.0, print_solve_progress ? &std::cout : nullptr);
1466  }
1467 
1468  Mapping this_mapping = initial_mapping;
1469  this_mapping.clear();
1470 
1471  for (auto& op : opgraph.opNodes()) {
1472  for (auto& compatible_node : compatible_fu_nodes.at(op)) {
1473  const OpAndFU op_and_fu {op, compatible_node};
1474  const auto& is_used_var_value = is_op_using_fu(op_and_fu);
1475  if (is_used_var_value) {
1476  this_mapping.mapMRRGNode(op, compatible_node);
1477  }
1478  }
1479 
1480  const auto& connecting_val = opgraph.outputVal(op);
1481  for (auto& src_compatible_node : compatible_fu_nodes.at(op)) {
1482  std::vector<MrrgNodeSpan> all_paths;
1483  for (auto& sink_op : opgraph.outputOps(op)) {
1484  for (auto& sink_compatible_node : compatible_fu_nodes.at(sink_op)) {
1485  const auto mrrg_paths_search = mrrg_paths.find({src_compatible_node, sink_compatible_node});
1486  if (mrrg_paths_search == mrrg_paths.end()) { continue; }
1487  for (const auto& trip_count_and_paths : mrrg_paths_search->second) {
1488  int path_num = 0;
1489  for (const auto& path : trip_count_and_paths.second) {
1490  if (
1491  is_path_used( MRRGPathID{src_compatible_node, sink_compatible_node, trip_count_and_paths.first, path_num} )
1492  && is_op_using_fu({op, src_compatible_node})
1493  && is_op_using_fu({sink_op, sink_compatible_node})
1494  ) {
1495  all_paths.push_back({path.data(), path.size() - 1}); // leave-off the sink, so merging multi-edges works
1496  }
1497  path_num += 1;
1498  }
1499  }
1500  }
1501  }
1502  const auto merged_paths = mergeMRRGWalks(mrrg, all_paths);
1503  std::set<MRRG::NodeDescriptor> already_added;
1504  for (auto& final_path : merged_paths) {
1505  for (auto& node_in_path : final_path) {
1506  if (not already_added.insert(node_in_path).second) { continue; }
1507  if (node_in_path->type != MRRG_NODE_ROUTING) { continue; }
1508  this_mapping.mapMRRGNode(connecting_val, node_in_path);
1509  }
1510  }
1511  }
1512  }
1513 
1514  if (options.print_intermediate_mappings) {
1515  Mapping mapping_to_print = this_mapping;
1516  if (options.general.getBool("print_intermediate_mappings_placement_only")) {
1517  mapping_to_print.clear();
1518  for (auto& op : opgraph.opNodes()) {
1519  for (auto& mapping : this_mapping.getMappingList(op)) {
1520  mapping_to_print.mapMRRGNode(op, mapping);
1521  }
1522  }
1523  }
1524  mapping_to_print.outputMapping(opgraph, std::cout);
1525  }
1526 
1527  auto suggested_mapping = options.solution_selector(this_mapping);
1528 
1529  this_mapping.setStatus(MappingStatus::success);
1530  result = suggested_mapping;
1531 
1532  return suggested_mapping.isMapped();
1533  };
1534 
1535  timing_seq.tick("solve setup", 0.0, print_solve_progress ? &std::cout : nullptr);
1536 
1537  if (false) {
1538  // do nothing
1539  } else if (options.use_solver_gurobi) {
1540  auto solution_selector_callback = makeOnMIPSolutionFunctorGRBCallback([&](auto& cb_obj) {
1541  {const auto& solution_dump_filename_base = options.general.getString("intermediate_solution_dump_filename_base");
1542  if (not solution_dump_filename_base.empty()) {
1543  const auto solution_dump_filename = solution_dump_filename_base + std::to_string(cb_count) + ".sol.txt";
1544  std::cout << "[INFO] " << solve_approach_name << " writing intermediate solution to " << solution_dump_filename << std::endl;
1545 
1546  std::ofstream sol(solution_dump_filename);
1547  const auto print_name_and_value = [&](const auto& var) {
1548  sol << var.get(GRB_StringAttr_VarName) << " = " << std::llround(cb_obj.getSolution(var)) << '\n';
1549  };
1550 
1551  for (const auto& v1 : op_is_mapped_to_vars) {print_name_and_value(v1.second); }
1552  for (const auto& v1 : edge_is_used_vars) {
1553  for (const auto& v2 : v1.second) {
1554  for (const auto& v3 : v2.second) { print_name_and_value(v3.second); }
1555  }
1556  }
1557  for (const auto& v1 : path_is_used_vars) { print_name_and_value(v1.second); }
1558 
1559  std::cout << "[INFO] " << solve_approach_name << " done writing intermediate solution to " << solution_dump_filename << std::endl;
1560  }}
1561  if (evaluate_solution([&](OpAndFU op_and_fu) -> bool {
1562  return std::llround(cb_obj.getSolution(op_is_mapped_to_vars.at(op_and_fu)));
1563  }, [&](MRRGPathID path_id) -> bool {
1564  return options.general.getBool("path_exclusivity_modelling")
1565  && std::llround(cb_obj.getSolution(path_is_used_vars.at(path_id)));
1566  })) {
1567  cb_obj.abort(); // safely stop further searching
1568  }
1569  });
1570  model.setCallback(&solution_selector_callback);
1571 
1572  model.set(GRB_IntParam_Seed, solver_seed);
1573  model.set(GRB_IntParam_Threads, max_threads);
1574  model.set(GRB_IntParam_PoolSearchMode, 2); // finds up to ''PoolSolutions'' best solutions
1575  model.set(GRB_IntParam_PoolSolutions, options.general.getInt("max_solutions"));
1576 
1577  if (not model_dump_filename.empty()) {
1578  const auto dot_pos = model_dump_filename.find_last_of('.');
1579  const auto extension = dot_pos == model_dump_filename.npos ? ".lp" : model_dump_filename.substr(dot_pos);
1580  const auto model_dump_filename_with_extension = model_dump_filename.substr(0, dot_pos) + '.' + solve_approach_name + extension;
1581  std::cout << "[INFO] " << solve_approach_name << " writing model to " << model_dump_filename_with_extension << std::endl;
1582  model.write(model_dump_filename_with_extension);
1583  std::cout << "[INFO] " << solve_approach_name << " done writing model to " << model_dump_filename_with_extension << std::endl;
1584  }
1585 
1586  model.optimize();
1587 
1588  } else if (options.use_solver_gor_classic_cp) {
1589  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
1590  std::vector<gor::IntVar*> allvars;
1591  for (const auto& name_and_var : gorCP_op_is_mapped_to_vars) {
1592  allvars.push_back(name_and_var.second);
1593  }
1594  // sort allvars - start at output node FUs, have all edge vars last.
1595 
1596  gor::DecisionBuilder* const db = gorCP_solver.MakePhase(
1597  allvars,
1598  gor::Solver::CHOOSE_FIRST_UNBOUND,
1599  gor::Solver::ASSIGN_MAX_VALUE
1600  );
1601 
1602  gorCP_solver.NewSearch(db);
1603 
1604  // gorCP_solver.Accept(gorCP_solver.MakePrintModelVisitor());
1605  while (gorCP_solver.NextSolution()) {
1606  if (evaluate_solution([&](OpAndFU op_and_fu) -> bool {
1607  return gorCP_op_is_mapped_to_vars.at(op_and_fu)->Value();
1608  },
1609  std::function<bool(MRRGPathID)>{ // placeholder
1610  })) {
1611  break;
1612  }
1613  }
1614  gorCP_solver.EndSearch();
1615  #else
1616  throw cgrame_mapper_error("Must be built to support Google Or-Tools based solvers");
1617  #endif
1618 
1619  } else if (options.use_solver_gor_sat_cp) {
1620  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
1621  gorsat::Model model;
1622 
1623  gorsat::SatParameters parameters;
1624  parameters.set_enumerate_all_solutions(true);
1625  model.Add(NewSatParameters(parameters));
1626 
1627  // DecisionStrategyProto* const strategy = gorSatCP_model.add_search_strategy();
1628  // std::ofstream("gorSatCP-ops-model.txt") << gorSatCP_model.DebugString();
1629 
1630  std::atomic<bool> async_request_stop_solving(false);
1631  model.GetOrCreate<gor::TimeLimit>()->RegisterExternalBooleanAsLimit(&async_request_stop_solving);
1632 
1633  model.Add(gorsat::NewFeasibleSolutionObserver([&](const gorsat::CpSolverResponse& r) {
1634  if (evaluate_solution([&](OpAndFU op_and_fu) -> bool {
1635  return r.solution(gorSatCP_op_is_mapped_to_vars.at(op_and_fu).index);
1636  },
1637  std::function<bool(MRRGPathID)>{ // placeholder
1638  })) {
1639  async_request_stop_solving = true;
1640  }
1641  }));
1642 
1643  const gorsat::CpSolverResponse& response = SolveCpModel(gorSatCP_model, &model);
1644  (void)response;
1645  #else
1646  throw cgrame_mapper_error("Must be built to support Google Or-Tools based solvers");
1647  #endif
1648 
1649  } else {
1650  throw cgrame_mapper_error("Solver not available; Solver ID = " + options.solver_id);
1651  }
1652 
1653  timing_seq.tick("solve");
1654 
1655  const auto num_solutions = [&]() -> long long int {
1656  if (false) {
1657  // do nothing
1658  } else if (options.use_solver_gurobi) {
1659  return model.get(GRB_IntAttr_SolCount);
1660  #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
1661  } else if (options.use_solver_gor_classic_cp) {
1662  return gorCP_solver.solutions();
1663  } else if (options.use_solver_gor_sat_cp) {
1664  return cb_count; // no way to check this?
1665  #endif
1666  } else {
1667  throw cgrame_mapper_error("Solver not available; Solver ID = " + options.solver_id);
1668  }
1669  }();
1670  solve_statistics.addInt("number of solutions evaluated", num_solutions);
1671 
1672  if (false) {
1673  // do nothing
1674  } else if (options.use_solver_gurobi) {
1675  const auto grb_status = model.get(GRB_IntAttr_Status);
1676  if (grb_status == GRB_INFEASIBLE) {
1677  if (not model_IIS_dump_filename.empty()) {
1678  const auto dot_pos = model_IIS_dump_filename.find_last_of('.');
1679  const auto extension = dot_pos == model_IIS_dump_filename.npos ? ".ilp" : model_IIS_dump_filename.substr(dot_pos);
1680  const auto iis_filename_with_extension = model_IIS_dump_filename.substr(0, dot_pos) + '.' + solve_approach_name + extension;
1681  std::cout << "[INFO] " << solve_approach_name << " computing IIS, will dump to " << iis_filename_with_extension << std::endl;
1682  model.computeIIS();
1683  std::cout << "[INFO] " << solve_approach_name << " writing IIS to " << iis_filename_with_extension << std::endl;
1684  model.write(iis_filename_with_extension);
1685  std::cout << "[INFO] " << solve_approach_name << " done writing IIS to " << iis_filename_with_extension << std::endl;
1686  }
1687  } else if (grb_status == GRB_TIME_LIMIT && num_solutions == 0) {
1688  result.setStatus(MappingStatus::timeout);
1689  }
1690  }
1691 
1692  if (options.print_mapping) {
1693  std::cout << "Mapping for " << solve_approach_name << ":\n";
1694  result.outputMapping(opgraph, std::cout);
1695  std::cout << '\n';
1696  }
1697 
1698  if (do_print_configuration_and_statistics) {
1699  std::cout << "Solve Statistics = " << solve_statistics << '\n';
1700  }
1701 
1702  return result;
1703 } catch (...) {
1704  throwExceptionButWrapIfGurobiException(std::current_exception());
1705 }
1706 
1707 Mapping mapOpsJustByConnectivity(std::unordered_map<std::string, std::string> fix_port,
1708  const OpGraph& opgraph, const MRRG& mrrg,
1709  ILPHeuristicMapperOptions options,
1710  ILPHeuristicMapperCaches caches,
1711  Mapping initial_mapping
1712 ) {
1713  options.general.addString("solve_approach_name", "mapOpsJustByConnectivity");
1714  options.general.addBool("path_exclusivity_modelling", false); // Unless it's requested, disable
1715  return mapViaConnectivityAndPathChoosing(fix_port, opgraph, mrrg, initial_mapping, std::move(options), std::move(caches));
1716 }
1717 
1719  std::unordered_map<std::string, std::string> fix_port,
1720  const Mapping& op_mapping,
1721  const OpGraph& opgraph, const MRRG& mrrg,
1724 ) {
1725  options.general.addString("solve_approach_name", "routeOpMappingByChoosingPaths");
1726  options.general.addBool("path_exclusivity_modelling", true); // Unless it's requested, enable
1727  options.general.addBool("map_unmapped_ops", false);
1728  return mapViaConnectivityAndPathChoosing(fix_port, opgraph, mrrg, op_mapping, std::move(options), std::move(caches));
1729 }
1730 
1731 static const std::map<std::string,FUCoster> fu_costers {
1732  {"", {}},
1733  {"one", [](
1734  const OpGraph& opgraph, const MRRG& mrrg,
1735  const OpGraph::OpDescriptor& op, const MRRG::NodeDescriptor& node
1736  ) {
1737  (void)opgraph; (void)mrrg; (void)op; (void)node;
1738  return 1.0;
1739  }},
1740 };
1741 
1742 AutoRegisterMapper ILPHeuristicMapper_arm("ILPHeuristicMapper",
1743  [](std::shared_ptr<CGRA> cgra, int timelimit, const ConfigStore& args) {
1744  return std::make_unique<ILPHeuristicMapper>(cgra, timelimit, args);
1745  },
1746  false, // not a composite
1747  "Iteratively considers more flexibility.\n"
1748  "Initially presented in 'Generic Connectivity-Based CGRA Mapping via Integer Linear Programming' (FCCM 2019)",
1749  { // required args
1750  {"allow_recomputation", false, "Allow ops to map to more than one FU"},
1751  {"allow_unbalanced_latency", false, "Assume a dataflow-like architecture"},
1752  {"fu_coster", "", "Specify one of the available cost functions on functional units.\n"
1753  "Empty string for none. Other options include: `one': each FU costs 1"},
1754  {"do_cache_warmup", true, "Low-overhead option that gives a better estimate of what mapping time would be like if arch-specific data was saved to disk"},
1755  {"nneigh_start", "", "The number-of-neighbours to start solving at. Default depends on arch_id"},
1756  {"nneigh_stop", "", "The last number-of-neighbours to try. Default depends on arch_id"},
1757  {"nneigh_step", 2, "The amount number-of-neighbours is increased between attempts"},
1758  {"do_test_placement", true, "At each number-of-neighbours, try solving without modelling routing first"},
1759  {"do_final_combined_attempt", false, "At each number-of-neighbours, if unable to route any placements, try placing & routing at the same time"},
1760  {"max_threads", 1, ""},
1761  {"model_dump_filename", "", ""},
1762  {"model_IIS_dump_filename", "", ""},
1763  {"seed", 0, ""},
1764  {"verbosity", 0, ""},
1765  {"arch_id", "", ""},
1766  },
1767  { // optional arg regexes
1768  {"hm_test_placement_.*", "Pass options directly to test_placement"},
1769  {"hm_outer_.*", "Pass options directly to outer"},
1770  {"hm_inner_.*", "Pass options directly to inner"},
1771  {"hm_combined_.*", "Pass options directly to final combined P+R attempt"},
1772  }
1773 );
1774 
1775 ILPHeuristicMapper::ILPHeuristicMapper(std::shared_ptr<CGRA> cgra, int timelimit, const ConfigStore& args)
1776  : Mapper(cgra, timelimit)
1777  , general_options{args}
1778  , test_placement_options {
1779  {"solve_approach_name", "TestPlacement"},
1780  {"path_exclusivity_modelling", false},
1781  {"allow_unbalanced_latency", true}, // (not overridden by 'common option' code)
1782  }
1783  , outer_options {
1784  {"solve_approach_name", "OuterSolve"},
1785  {"path_exclusivity_modelling", "on"},
1786  {"max_num_paths_between_FUs", "5"},
1787  {"allowable_routing_usage_multiplier", "2"},
1788  {"allow_unbalanced_latency", true}, // (not overridden by 'common option' code)
1789  {"print_intermediate_mappings", general_options.getInt("verbosity") > 0},
1790  {"print_intermediate_mappings_placement_only", "yes"},
1791  {"max_solutions", "100"},
1792  }
1793  , inner_options {
1794  {"solve_approach_name", "InnerSolve"},
1795  }
1796  , final_combined_attempt_options {
1797  {"solve_approach_name", "FinalCombinedAttempt"},
1798  {"path_exclusivity_modelling", "on"},
1799  {"max_num_paths_between_FUs", "20"},
1800  {"timelimit", "60"},
1801  }
1802 {
1803  for (auto& prefix_and_cs : {
1804  std::make_pair("test_placement", std::ref(test_placement_options)),
1805  std::make_pair("outer", std::ref(outer_options)),
1806  std::make_pair("inner", std::ref(inner_options)),
1807  std::make_pair("combined", std::ref(final_combined_attempt_options)),
1808  }) {
1809  const auto& prefix = std::string("hm_") + prefix_and_cs.first + '_';
1810  auto& cs = prefix_and_cs.second;
1811 
1812  // common options. Use 'add' to not override any existing settings
1813  cs.addBool("allow_unbalanced_latency", args.getBool("allow_unbalanced_latency"));
1814  cs.addBool("print_configuration_and_statistics", args.getInt("verbosity") > 0);
1815  cs.addBool("allow_multiple_op_mappings", args.getBool("allow_recomputation"));
1816  cs.addBool("print_solve_progress", args.getInt("verbosity") > 0);
1817  cs.addBool("silence_warnings", args.getInt("verbosity") <= 0);
1818  cs.addInt("seed", args.getInt("seed"));
1819  cs.addInt("max_threads", args.getInt("max_threads"));
1820  cs.addString("model_dump_filename", args.getString("model_dump_filename"));
1821  cs.addString("model_IIS_dump_filename", args.getString("model_IIS_dump_filename"));
1822 
1823  // options passed in by the user
1824  for (auto& args_kv : args) {
1825  const auto mismatch_results = std::mismatch(prefix.begin(), prefix.end(), args_kv.first.begin(), args_kv.first.end());
1826  if (mismatch_results.first != prefix.end()) { // skip if not same prefix
1827  continue;
1828  }
1829  const auto param_name = std::string{mismatch_results.second, args_kv.first.end()}; // with the prefix removed
1830  // get our overridden value, or the global default. Also, checks if key is spelled correctly
1831  const auto& default_value = cs.getStringOr(param_name,
1832  ILPHeuristicMapperOptions::default_general_opts().getString(param_name)
1833  );
1834  if (args_kv.second != default_value && general_options.getInt("verbosity") > 0) {
1835  std::cout << "[INFO] " << cs.getStringOr("solve_approach_name", prefix) << "'s "
1836  << param_name << " overridden from " << default_value << " to " << args_kv.second << std::endl;
1837  }
1838  cs.setString(param_name, args_kv.second);
1839  }
1840  }
1841 
1842  const auto& arch_id = args.getString("arch_id");
1843  const auto& arch_id_defaults = [&]() -> ConfigStore {
1844  if (false) { return {};
1845  } else if (arch_id == "adres" or arch_id == "2") {
1846  // ideal sched: 12, 14, 18, 21;
1847  // cusp at: 17
1848  return {
1849  {"nneigh_start", "12"},
1850  {"nneigh_stop", "21"},
1851  };
1852  } else if (arch_id == "hycube" or arch_id == "3") {
1853  // ideal sched: 7, 9, 11, 16
1854  return {
1855  {"nneigh_start", "7"},
1856  {"nneigh_stop", "17"},
1857  };
1858  } else if (arch_id == "clustered" or arch_id == "1") {
1859  return {
1860  {"nneigh_start", "8"},
1861  {"nneigh_stop", "16"},
1862  };
1863  } else {
1864  if (args.getInt("verbosity") > 0) {
1865  std::cout << "Warning: uncharacterised architecture `" << arch_id << "'. Will use basic NN schedule\n";
1866  }
1867  return {
1868  {"nneigh_start", "4"},
1869  {"nneigh_stop", "24"},
1870  };
1871  }
1872  }();
1873 
1874  // set the per-arch options, but only if they're not user-specified (ie. left as empty strings)
1875  for (const auto& kv : arch_id_defaults) {
1876  if (general_options.getString(kv.first).empty()) {
1877  general_options.setString(kv.first, kv.second);
1878  }
1879  }
1880 }
1881 
1882 Mapping ILPHeuristicMapper::real_mapOpGraph(std::shared_ptr<OpGraph> opgraph_storage, int II, const MRRG& mrrg, std::unordered_map<std::string, std::string> fix_port) const {
1883  const auto& opgraph = *opgraph_storage;
1884  auto mrrg_cache_handle = std::make_unique<MRRGProcedureCacheHandle>();
1885  auto ilphm_cache = ILPHeuristicMapperCaches{ mrrg_cache_handle.get() };
1886 
1887  const auto mapping_algo = [&](bool cache_warmup) {
1888  const auto now = []{ return std::chrono::steady_clock::now(); };
1889  const auto start_time = now();
1890  const auto time_remaining = [&] {
1891  return std::max<int>(0, timelimit - std::chrono::duration_cast<std::chrono::seconds>(now() - start_time).count());
1892  };
1893 
1894  const auto computed_global_options = [&] {
1895  ConfigStore result;
1896  result.addInt("timelimit", std::max(1,time_remaining()));
1897  result.addBool("just_warm_up_caches", cache_warmup);
1898  return result;
1899  };
1900 
1901  const auto flow_name = std::string("ILP heuristic mapping") + (cache_warmup ? " cache warmup" : "");
1902  if (general_options.getInt("verbosity") > 0) {
1903  std::cout << "[INFO] Starting flow `" << flow_name << "'\n";
1904  }
1905 
1906  auto ostream_ptr = cache_warmup || general_options.getInt("verbosity") <= 0 ? nullptr : &std::cout;
1907  PrintOnDestructionChronoSequence timing_seq(flow_name, ostream_ptr);
1908 
1909  Mapping mapping(cgra, mrrg.initiationInterval(), opgraph_storage);
1910 
1911  auto nneigh_start = cache_warmup ? general_options.getInt("nneigh_stop") : general_options.getInt("nneigh_start"); // don't waste time
1912  const auto nneigh_step = general_options.getInt("nneigh_step");
1913  auto nneigh = nneigh_start - nneigh_step;
1914  while (nneigh < general_options.getInt("nneigh_stop") && not mapping.isMapped()) {
1915  nneigh += nneigh_step;
1916 
1917  // maybe try once with no path exclusivity
1918  if (general_options.getBool("do_test_placement")) {
1919  if (time_remaining() == 0) { mapping.clear(); break; }
1920  const auto test_placement = mapOpsJustByConnectivity(fix_port, opgraph, mrrg,
1924  noEdgeCosting,
1925  with_set(test_placement_options, computed_global_options()),
1926  },
1927  ilphm_cache, mapping
1928  );
1929  timing_seq.tick("test placement for NN = " + std::to_string(nneigh), 0.0, ostream_ptr);
1930  if (not test_placement.isMapped()) {
1931  continue;
1932  }
1933  }
1934 
1935  // then try with the full options
1936  const auto inner_mapping_func = [&](auto& op_mapping) {
1937  if (time_remaining() == 0) { auto cpy = op_mapping; cpy.clear(); return cpy; }
1938  return routeOpMappingByChoosingPaths(fix_port, op_mapping, opgraph, mrrg,
1940  {},
1942  {},
1943  with_set(inner_options, computed_global_options()),
1944  },
1945  ilphm_cache
1946  );
1947  };
1948 
1949  ConfigStore outer_option_overrides;
1950  if (cache_warmup) {
1951  outer_option_overrides.setInt("max_num_paths_between_FUs", 20); // enough for both phases
1952  }
1953 
1954  if (time_remaining() == 0) { mapping.clear(); break; }
1955  mapping = mapOpsJustByConnectivity(fix_port, opgraph, mrrg,
1958  inner_mapping_func,
1959  noEdgeCosting,
1960  with_set(outer_options, outer_option_overrides, computed_global_options()),
1961  allowAllNodes,
1963  fu_costers.at(general_options.getString("fu_coster")),
1964  }, ilphm_cache, mapping
1965  );
1966  timing_seq.tick("tried NN = " + std::to_string(nneigh), 0.0, ostream_ptr);
1967 
1968  // and maybe try one final combined attempt
1969  if (not mapping.isMapped() && general_options.getBool("do_final_combined_attempt")) {
1970  mapping.clear();
1971  if (time_remaining() == 0) { mapping.clear(); break; }
1972  mapping = mapViaConnectivityAndPathChoosing(fix_port, opgraph, mrrg, mapping,
1976  }, ilphm_cache
1977  );
1978  timing_seq.tick("FCA for NN = " + std::to_string(nneigh), 0.0, ostream_ptr);
1979  };
1980  }
1981 
1982  mapping.solve_time_in_seconds = timing_seq.secondsSinceStart();
1983 
1984  return mapping;
1985  };
1986 
1987  if (general_options.getBool("do_cache_warmup")) {
1988  mapping_algo(true);
1989  }
1990  return mapping_algo(false);
1991 }
findAllCompatibleFUs
std::unordered_map< OpGraph::OpDescriptor, std::vector< MRRG::NodeDescriptor > > findAllCompatibleFUs(const OpGraph &opgraph, const MRRG &base_mrrg)
Definition: MRRGProcedures.cpp:230
makeNClosestNeighbourFinder
auto makeNClosestNeighbourFinder(int min_neighbours)
Definition: MRRGProcedures.h:109
EdgeAndFU::fu
MRRG::NodeDescriptor fu
Definition: HeuristicMappers.cpp:47
MRRGNode::getFullName
std::string getFullName() const
Definition: MRRG.cpp:569
EdgeAndFU::edge
OpGraph::EdgeDescriptor edge
Definition: HeuristicMappers.cpp:46
ConfigStore::addReal
bool addReal(std::string key, double value)
Definition: ConfigStore.h:122
computeTrailsToBalance
TrailsToBalance computeTrailsToBalance(const OpGraph &opgraph)
Definition: OpGraphProcedures.cpp:40
ILPHeuristicMapperOptions::is_node_allowed_for_op
OpNodeFilter is_node_allowed_for_op
Definition: HeuristicMappers.h:65
OpAndFU::OpAndFU
OpAndFU(OpGraph::OpDescriptor op, MRRG::NodeDescriptor fu)
Definition: HeuristicMappers.cpp:38
CodeProfiling.h
from_mrrg_node_to_mrrg_node_trip_count_number
std::string from_mrrg_node_to_mrrg_node_trip_count_number(std::string prefix, const OpGraph &opgraph, const MRRG &mrrg, MRRG::NodeDescriptor src_compatible_node, MRRG::NodeDescriptor sink_compatible_node, int trip_count, int number, bool make_name_solver_safe=false)
Definition: HeuristicMappers.cpp:241
OpGraph::EdgeDescriptor::val
OpGraph::ValDescriptor val
Definition: OpGraph.h:225
OpNodeFilter
std::function< bool(const OpGraph::OpDescriptor &op, const MRRG::NodeDescriptor &node)> OpNodeFilter
Definition: HeuristicMappers.h:53
ILPHeuristicMapper::outer_options
ConfigStore outer_options
Definition: HeuristicMappers.h:176
GorSatCPVarID
Definition: HeuristicMappers.cpp:80
MRRGPathID::asTuple
auto asTuple() const
Definition: HeuristicMappers.cpp:77
ILPHeuristicMapperOptions::add_defaults
static ILPHeuristicMapperOptions & add_defaults(ILPHeuristicMapperOptions &options)
Definition: HeuristicMappers.h:79
findNRoutingPathsBetween
std::vector< std::vector< MRRG::NodeDescriptor > > findNRoutingPathsBetween(int num_paths, const MRRG &mrrg, MRRG::NodeDescriptor source, MRRG::NodeDescriptor sink, std::pair< int, int > cycle_trip_count_min_max, const OperandTag &operand_tag, MRRGProcedureCacheHandle *cache_handle)
Definition: MRRGProcedures.cpp:248
MRRGPathID::trip_count
int trip_count
Definition: HeuristicMappers.cpp:70
ConfigStore::setString
void setString(std::string key, std::string value)
Definition: ConfigStore.h:128
MRRG
Definition: MRRG.h:216
InternalILPHeuristicMapperOptions::solver_id
std::string solver_id
Definition: HeuristicMappers.cpp:263
GraphAlgorithms.h
MRRGPathID::MRRGPathID
MRRGPathID(MRRG::NodeDescriptor src_fu, MRRG::NodeDescriptor sink_fu, int trip_count, int index)
Definition: HeuristicMappers.cpp:73
ILPHeuristicMapper::ILPHeuristicMapper
ILPHeuristicMapper(std::shared_ptr< CGRA > cgra, int timelimit, const ConfigStore &args)
Definition: HeuristicMappers.cpp:1775
OpGraphProcedures.h
OpGraph::getNodeRef
OpGraphOp & getNodeRef(OpDescriptor ndesc)
Definition: OpGraph.h:376
ConstraintSet.h
OpGraph::targetOfEdge
OpDescriptor targetOfEdge(EdgeDescriptor ed) const
Definition: OpGraph.cpp:525
from_src_at_to_sink_at_trip_count_number
std::string from_src_at_to_sink_at_trip_count_number(std::string prefix, const OpGraph &opgraph, const MRRG &mrrg, OpGraph::OpDescriptor src_op, MRRG::NodeDescriptor src_compatible_node, OpGraph::OpDescriptor sink_op, MRRG::NodeDescriptor sink_compatible_node, int trip_count, int number, bool make_name_solver_safe=false)
Definition: HeuristicMappers.cpp:201
begin
auto begin(const SingleItemImmutableSet< VertexID > &siis)
Definition: Collections.h:137
ILPHeuristicMapperOptions::find_all_neighbour_FUs
NeighbourFinder find_all_neighbour_FUs
Definition: HeuristicMappers.h:60
MRRGPathID::sink_fu
MRRG::NodeDescriptor sink_fu
Definition: HeuristicMappers.cpp:69
Module.h
OpGraph::asOp
OpDescriptor asOp(NodeDescriptor ndesc) const
Definition: OpGraph.cpp:1275
InternalILPHeuristicMapperOptions::add_trip_count_balancing_constraints
bool add_trip_count_balancing_constraints
Definition: HeuristicMappers.cpp:276
Mapping::solve_time_in_seconds
double solve_time_in_seconds
Definition: Mapping.h:110
ILPHeuristicMapperOptions::Solvers::GoogleORTools_ClassicCP
static SolverID GoogleORTools_ClassicCP
Definition: HeuristicMappers.h:72
ConfigStore::setInt
void setInt(std::string key, long long value)
Definition: ConfigStore.h:129
InternalILPHeuristicMapperOptions
Definition: HeuristicMappers.cpp:262
override_all
ConfigStore & override_all(ConfigStore &into, const ConfigStore &from)
Definition: ConfigStore.h:257
ILPHeuristicMapperOptions::Solvers::Gurobi
static SolverID Gurobi
Definition: HeuristicMappers.h:71
ConfigStore::addInt
bool addInt(std::string key, long long value)
Definition: ConfigStore.h:121
OpCode::STORE
@ STORE
fu_costers
static const std::map< std::string, FUCoster > fu_costers
Definition: HeuristicMappers.cpp:1731
acceptTheFirstSolution
const SolutionSelector acceptTheFirstSolution
Definition: HeuristicMappers.h:41
unionOfCompatibleNodes
auto unionOfCompatibleNodes(const OpGraph &opgraph, const CFN &compatible_fu_nodes)
Definition: HeuristicMappers.cpp:83
ILPHeuristicMapperOptions::edge_coster
EdgeCoster edge_coster
Definition: HeuristicMappers.h:62
Mapping::getMappingList
const std::vector< MRRG::NodeDescriptor > & getMappingList(OpGraph::NodeDescriptor key) const
Definition: Mapping.h:57
Mapping::outputMapping
void outputMapping(std::ostream &o=std::cout) const
Definition: Mapping.h:72
InternalILPHeuristicMapperOptions::use_solver_gurobi
bool use_solver_gurobi
Definition: HeuristicMappers.cpp:264
EdgeAndFU::asTuple
auto asTuple() const
Definition: HeuristicMappers.cpp:53
AutoRegisterMapper
Special helper for registering mappers to the default mapper registry.
Definition: Mapper.h:200
ConfigStore
Definition: ConfigStore.h:76
allowAllNodesForOps
const OpNodeFilter allowAllNodesForOps
Definition: HeuristicMappers.cpp:32
from_src_to_sink_at
std::string from_src_to_sink_at(std::string prefix, const OpGraph &opgraph, const MRRG &mrrg, OpGraph::OpDescriptor src_op, OpGraph::EdgeDescriptor out_edge, MRRG::NodeDescriptor sink_compatible_node, bool make_name_solver_safe=false)
Definition: HeuristicMappers.cpp:223
InternalILPHeuristicMapperOptions::operator<<
friend std::ostream & operator<<(std::ostream &os, const InternalILPHeuristicMapperOptions &opts)
Definition: HeuristicMappers.cpp:322
ILPHeuristicMapperOptions
Definition: HeuristicMappers.h:57
MRRGNodePair::src_fu
MRRG::NodeDescriptor src_fu
Definition: HeuristicMappers.cpp:57
Mapper
Common interface for mappers.
Definition: Mapper.h:28
OpGraph::edgeLatencies
std::map< EdgeDescriptor, int > edgeLatencies() const
Definition: OpGraph.cpp:906
ChronoSequence::secondsSinceStart
double secondsSinceStart()
Definition: CodeProfiling.h:94
ILPHeuristicMapper::final_combined_attempt_options
ConfigStore final_combined_attempt_options
Definition: HeuristicMappers.h:178
EdgeAndFUMap
std::unordered_map< EdgeAndFU, MappedType, EdgeAndFU::Hash > EdgeAndFUMap
Definition: HeuristicMappers.cpp:342
mapOpsJustByConnectivity
Mapping mapOpsJustByConnectivity(std::unordered_map< std::string, std::string > fix_port, const OpGraph &opgraph, const MRRG &mrrg, ILPHeuristicMapperOptions options, ILPHeuristicMapperCaches caches, Mapping initial_mapping)
Definition: HeuristicMappers.cpp:1707
InternalILPHeuristicMapperOptions::InternalILPHeuristicMapperOptions
InternalILPHeuristicMapperOptions(ILPHeuristicMapperOptions options_)
Definition: HeuristicMappers.cpp:279
to_string
const std::string & to_string(const OpGraphOpCode &opcode)
Definition: OpGraph.cpp:111
Mapping::mapMRRGNode
void mapMRRGNode(OpGraph::NodeDescriptor, MRRG::NodeDescriptor node)
Definition: Mapping.cpp:58
makeNameSolverSafe
std::string makeNameSolverSafe(std::string s)
Definition: ConstraintSet.cpp:126
OpCode::LOAD
@ LOAD
print_assoc_container
void print_assoc_container(OSTREAM &&os, const ASSOC_CONTAINER &c, const T1 &element_separator, const T2 &key_value_separator, const T3 &element_prefix, const T4 &element_suffix, const T5 &container_prefix, const T6 &container_suffix, KEY_PRINTER &&value_printer=KEY_PRINTER{}, VALUE_PRINTER &&key_printer=VALUE_PRINTER{})
Definition: Collections.h:488
from_src_at_to_sink
std::string from_src_at_to_sink(std::string prefix, const OpGraph &opgraph, const MRRG &mrrg, OpGraph::OpDescriptor src_op, MRRG::NodeDescriptor src_compatible_node, OpGraph::OpDescriptor sink_op, bool make_name_solver_safe=false)
Definition: HeuristicMappers.cpp:214
ILPHeuristicMapperOptions::general
ConfigStore general
Definition: HeuristicMappers.h:63
ILPHeuristicMapper::inner_options
ConfigStore inner_options
Definition: HeuristicMappers.h:177
Mapping::hasMapping
bool hasMapping(OpGraph::NodeDescriptor key) const
Definition: Mapping.h:50
Mapping
Definition: Mapping.h:31
allowAllNodes
const NodeFilter allowAllNodes
Definition: HeuristicMappers.cpp:31
operator<<
std::ostream & operator<<(std::ostream &os, const ILPHeuristicMapperOptions &opts)
Definition: HeuristicMappers.cpp:252
InternalILPHeuristicMapperOptions::use_solver_gor_classic_cp
bool use_solver_gor_classic_cp
Definition: HeuristicMappers.cpp:265
ILPHeuristicMapperCaches
Definition: HeuristicMappers.h:130
OpGraph::EdgeDescriptor::output_index
int output_index
Definition: OpGraph.h:225
tuple_hash::TupleHashOps
Definition: TupleHash.h:94
OpAndFU::fu
MRRG::NodeDescriptor fu
Definition: HeuristicMappers.cpp:36
ConfigStore::addString
bool addString(std::string key, std::string value)
Definition: ConfigStore.h:120
MappingStatus::timeout
@ timeout
OpGraph::outputOps
const std::vector< OpGraphOp * > & outputOps(ValDescriptor op) const
Definition: OpGraph.cpp:488
MRRG::getNodeRef
MRRGNode & getNodeRef(NodeDescriptor ndesc)
Definition: MRRG.h:273
Functional.h
OpGraphNode::name
std::string name
Definition: OpGraph.h:123
OpCode::INPUT
@ INPUT
ILPHeuristicMapperOptions::default_general_opts
static const ConfigStore & default_general_opts()
Definition: HeuristicMappers.h:86
MappingStatus::failure
@ failure
OpGraph::opNodes
auto & opNodes() const
Definition: OpGraph.h:313
ILPHeuristicMapperOptions::is_node_allowed
NodeFilter is_node_allowed
Definition: HeuristicMappers.h:64
GorSatCPVarID::index
int index
Definition: HeuristicMappers.cpp:80
OpGraphNode
Definition: OpGraph.h:113
OpAndFU::op
OpGraph::OpDescriptor op
Definition: HeuristicMappers.cpp:35
mapViaConnectivityAndPathChoosing
Mapping mapViaConnectivityAndPathChoosing(std::unordered_map< std::string, std::string > fix_port, const OpGraph &opgraph, const MRRG &mrrg, const Mapping &initial_mapping, ILPHeuristicMapperOptions options_, ILPHeuristicMapperCaches caches)
Definition: HeuristicMappers.cpp:347
MRRGPathID::index
int index
Definition: HeuristicMappers.cpp:71
ILPHeuristicMapperOptions::Solvers
Definition: HeuristicMappers.h:68
string_from_stream
std::string string_from_stream(F &&f)
Definition: Exception.h:95
FunctionalUnitNeighbours
Definition: MRRGProcedures.h:48
ILPHeuristicMapper::general_options
ConfigStore general_options
Definition: HeuristicMappers.h:174
PrintOnDestructionChronoSequence
Definition: CodeProfiling.h:121
ChronoSequence::tick
TickResult tick(std::string timepoint_name, double threshold=0.0, std::ostream *os=nullptr)
Definition: CodeProfiling.h:54
FUMap
std::unordered_map< MRRG::NodeDescriptor, MappedType > FUMap
Definition: HeuristicMappers.cpp:345
MRRG_NODE_ROUTING
@ MRRG_NODE_ROUTING
Definition: MRRG.h:45
OpAndFU
Definition: HeuristicMappers.cpp:34
from_mrrg_node_to_mrrg_node
std::string from_mrrg_node_to_mrrg_node(std::string prefix, const OpGraph &opgraph, const MRRG &mrrg, MRRG::NodeDescriptor src_compatible_node, MRRG::NodeDescriptor sink_compatible_node, bool make_name_solver_safe=false)
Definition: HeuristicMappers.cpp:232
routeOpMappingByChoosingPaths
Mapping routeOpMappingByChoosingPaths(std::unordered_map< std::string, std::string > fix_port, const Mapping &op_mapping, const OpGraph &opgraph, const MRRG &mrrg, RouteOpMappingByChoosingPathsOptions options, ILPHeuristicMapperCaches caches)
Definition: HeuristicMappers.cpp:1718
Mapper::cgra
std::shared_ptr< CGRA > cgra
Definition: Mapper.h:45
MRRGNodePair::sink_fu
MRRG::NodeDescriptor sink_fu
Definition: HeuristicMappers.cpp:58
end
auto end(const SingleItemImmutableSet< VertexID > &siis)
Definition: Collections.h:138
MRRGNodePair::MRRGNodePair
MRRGNodePair(MRRG::NodeDescriptor src_fu, MRRG::NodeDescriptor sink_fu)
Definition: HeuristicMappers.cpp:60
OpAndFU::asTuple
auto asTuple() const
Definition: HeuristicMappers.cpp:42
TupleHash.h
InternalILPHeuristicMapperOptions::use_edge_vars_constrained_via_implication_network
bool use_edge_vars_constrained_via_implication_network
Definition: HeuristicMappers.cpp:274
OpGraphOp
Definition: OpGraph.h:131
HeuristicMappers.h
MRRGNodePair
Definition: HeuristicMappers.cpp:56
print_container
void print_container(OSTREAM &&os, const CONTAINER &c, const T1 &element_separator, const T2 &container_prefix, const T3 &container_suffix, PRINTER &&element_printer=PRINTER{})
Definition: Collections.h:435
MRRGNode
Definition: MRRG.h:60
InternalILPHeuristicMapperOptions::split_edge_variables_by_trip_count
bool split_edge_variables_by_trip_count
Definition: HeuristicMappers.cpp:275
lazy_pointer
auto lazy_pointer(Gen &&g)
Definition: Functional.h:42
EdgeAndFU
Definition: HeuristicMappers.cpp:45
from_src_at_to_sink_at
std::string from_src_at_to_sink_at(std::string prefix, const OpGraph &opgraph, const MRRG &mrrg, OpGraph::OpDescriptor src_op, MRRG::NodeDescriptor src_compatible_node, OpGraph::OpDescriptor sink_op, MRRG::NodeDescriptor sink_compatible_node, bool make_name_solver_safe=false)
Definition: HeuristicMappers.cpp:146
ILPHeuristicMapperOptions::Solvers::SolverID
Definition: HeuristicMappers.h:69
noEdgeCosting
const EdgeCoster noEdgeCosting
Definition: HeuristicMappers.h:42
Mapping::clear
void clear()
Definition: Mapping.cpp:95
InternalILPHeuristicMapperOptions::add_trip_count_topological_constraints
bool add_trip_count_topological_constraints
Definition: HeuristicMappers.cpp:277
InternalILPHeuristicMapperOptions::print_intermediate_mappings
bool print_intermediate_mappings
Definition: HeuristicMappers.cpp:269
ConfigStore::getString
const std::string & getString(const std::string &key) const
Definition: ConfigStore.h:136
InternalILPHeuristicMapperOptions::print_mapping
bool print_mapping
Definition: HeuristicMappers.cpp:270
with_set
ConfigStore with_set(ConfigStore into, const ConfigStore &from1, const CSes &... froms)
Definition: ConfigStore.h:279
OpCode::OUTPUT
@ OUTPUT
InternalILPHeuristicMapperOptions::operand_nodes_are_normal
bool operand_nodes_are_normal
Definition: HeuristicMappers.cpp:272
OpGraph::EdgeDescriptor
Definition: OpGraph.h:224
ILPHeuristicMapperCaches::mrrg_proc_cache
MRRGProcedureCacheHandle * mrrg_proc_cache
Definition: HeuristicMappers.h:131
MRRG::size
std::ptrdiff_t size() const
Definition: MRRG.cpp:558
NodeFilter
std::function< bool(const MRRG::NodeDescriptor &node)> NodeFilter
Definition: HeuristicMappers.h:46
MRRGPathID
Definition: HeuristicMappers.cpp:67
ConfigStore::addBool
bool addBool(std::string key, bool value)
Definition: ConfigStore.h:123
ILPHeuristicMapperOptions::Solvers::GoogleORTools_SatCP
static SolverID GoogleORTools_SatCP
Definition: HeuristicMappers.h:73
MappingStatus::success
@ success
MRRGNodePair::asTuple
auto asTuple() const
Definition: HeuristicMappers.cpp:64
OpAndFUMap
std::unordered_map< OpAndFU, MappedType, OpAndFU::Hash > OpAndFUMap
Definition: HeuristicMappers.cpp:339
Mapping::forEachMapping
void forEachMapping(F &&f) const
Definition: Mapping.h:93
MRRG::initiationInterval
int initiationInterval() const
Definition: MRRG.h:346
MRRGProcedures.h
MRRGPathID::src_fu
MRRG::NodeDescriptor src_fu
Definition: HeuristicMappers.cpp:68
InternalILPHeuristicMapperOptions::use_solver_gor_sat_cp
bool use_solver_gor_sat_cp
Definition: HeuristicMappers.cpp:266
ILPHeuristicMapperOptions::solution_selector
SolutionSelector solution_selector
Definition: HeuristicMappers.h:61
mergeMRRGWalks
std::vector< std::vector< MRRG::NodeDescriptor > > mergeMRRGWalks(const MRRG &mrrg, const std::vector< MrrgNodeSpan > &walks)
Definition: MRRGProcedures.cpp:296
ILPHeuristicMapper::test_placement_options
ConfigStore test_placement_options
Definition: HeuristicMappers.h:175
from_src_at_to_sink_at_trip_count
std::string from_src_at_to_sink_at_trip_count(std::string prefix, const OpGraph &opgraph, const MRRG &mrrg, OpGraph::OpDescriptor src_op, MRRG::NodeDescriptor src_compatible_node, OpGraph::OpDescriptor sink_op, MRRG::NodeDescriptor sink_compatible_node, int trip_count, bool make_name_solver_safe=false)
Definition: HeuristicMappers.cpp:175
OpGraph::outEdges
std::vector< EdgeDescriptor > outEdges(const OpDescriptor &op) const
Definition: OpGraph.cpp:530
Mapping::isMapped
bool isMapped() const
Definition: Mapping.h:37
ILPHeuristicMapper::real_mapOpGraph
Mapping real_mapOpGraph(std::shared_ptr< OpGraph > opgraph, int II, const MRRG &mrrg, std::unordered_map< std::string, std::string >fix_port) const
Definition: HeuristicMappers.cpp:1882
ConfigStore::getInt
long long getInt(const std::string &key) const
Definition: ConfigStore.h:146
tripCountOfWalk
int tripCountOfWalk(const MRRG &mrrg, const std::vector< MRRG::NodeDescriptor > &walk)
Definition: MRRGProcedures.cpp:379
walkIsCompatibleWithOperand
bool walkIsCompatibleWithOperand(const MRRG &mrrg, const std::vector< MRRG::NodeDescriptor > &walk, const OperandTag &operand_tag)
Definition: MRRGProcedures.cpp:396
cgrame_mapper_error
Definition: Exception.h:27
OpGraph
Definition: OpGraph.h:215
OpGraphOp::opcode
OpGraphOpCode opcode
Definition: OpGraph.h:158
InternalILPHeuristicMapperOptions::print_initial_mapping
bool print_initial_mapping
Definition: HeuristicMappers.cpp:268
ILPHeuristicMapper_arm
AutoRegisterMapper ILPHeuristicMapper_arm("ILPHeuristicMapper", [](std::shared_ptr< CGRA > cgra, int timelimit, const ConfigStore &args) { return std::make_unique< ILPHeuristicMapper >(cgra, timelimit, args);}, false, "Iteratively considers more flexibility.\n" "Initially presented in 'Generic Connectivity-Based CGRA Mapping via Integer Linear Programming' (FCCM 2019)", { {"allow_recomputation", false, "Allow ops to map to more than one FU"}, {"allow_unbalanced_latency", false, "Assume a dataflow-like architecture"}, {"fu_coster", "", "Specify one of the available cost functions on functional units.\n" "Empty string for none. Other options include: `one': each FU costs 1"}, {"do_cache_warmup", true, "Low-overhead option that gives a better estimate of what mapping time would be like if arch-specific data was saved to disk"}, {"nneigh_start", "", "The number-of-neighbours to start solving at. Default depends on arch_id"}, {"nneigh_stop", "", "The last number-of-neighbours to try. Default depends on arch_id"}, {"nneigh_step", 2, "The amount number-of-neighbours is increased between attempts"}, {"do_test_placement", true, "At each number-of-neighbours, try solving without modelling routing first"}, {"do_final_combined_attempt", false, "At each number-of-neighbours, if unable to route any placements, try placing & routing at the same time"}, {"max_threads", 1, ""}, {"model_dump_filename", "", ""}, {"model_IIS_dump_filename", "", ""}, {"seed", 0, ""}, {"verbosity", 0, ""}, {"arch_id", "", ""}, }, { {"hm_test_placement_.*", "Pass options directly to test_placement"}, {"hm_outer_.*", "Pass options directly to outer"}, {"hm_inner_.*", "Pass options directly to inner"}, {"hm_combined_.*", "Pass options directly to final combined P+R attempt"}, })
FunctionalUnitNeighbours::NodeInfo
Definition: MRRGProcedures.h:49
Mapping::setStatus
void setStatus(MappingStatus new_status)
Definition: Mapping.h:40
ConfigStore::getBool
bool getBool(const std::string &key) const
Definition: ConfigStore.h:166
OpGraphNode::getName
const std::string & getName() const
Definition: OpGraph.h:121
Mapper::timelimit
int timelimit
Definition: Mapper.h:46
EdgeAndFU::EdgeAndFU
EdgeAndFU(OpGraph::EdgeDescriptor edge, MRRG::NodeDescriptor fu)
Definition: HeuristicMappers.cpp:49