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>
22 #include <unordered_map>
23 #include <unordered_set>
26 #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
27 namespace gor = operations_research;
28 namespace gorsat = operations_research::sat;
82 template<
typename CFN>
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);
90 return all_compatible_nodes_set;
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();
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);
110 return std::make_pair(ct, bool_or);
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);
121 for (
const auto& dom : domains) {
122 lin->add_domain(dom.first);
123 lin->add_domain(dom.second);
125 return std::make_pair(ct, lin);
128 template<
typename Literals>
129 auto addMaxConstraint(gorsat::CpModelProto& cpmp,
const Literals& literals,
GorSatCPVarID equal_to) {
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);
149 bool make_name_solver_safe =
false
151 prefix +=
".from_src";
154 prefix +=
".to_sink";
156 if (sink_compatible_node ) { prefix +=
".at." + mrrg.
getNodeRef(sink_compatible_node).
getFullName(); }
158 return make_name_solver_safe ?
makeNameSolverSafe(std::move(prefix)) : std::move(prefix);
164 bool make_name_solver_safe =
false
167 std::move(prefix), opgraph, mrrg, src_op, src_compatible_node,
172 return make_name_solver_safe ?
makeNameSolverSafe(std::move(result)) : std::move(result);
179 bool make_name_solver_safe =
false
182 std::move(prefix), opgraph, mrrg,
183 src_op, src_compatible_node, sink_op, sink_compatible_node
185 return make_name_solver_safe ?
makeNameSolverSafe(std::move(result)) : std::move(result);
192 bool make_name_solver_safe =
false
195 std::move(prefix), opgraph, mrrg,
196 src_op, src_compatible_node, out_edge, sink_compatible_node
198 return make_name_solver_safe ?
makeNameSolverSafe(std::move(result)) : std::move(result);
204 int trip_count,
int number,
205 bool make_name_solver_safe =
false
208 std::move(prefix), opgraph, mrrg,
209 src_op, src_compatible_node, sink_op, sink_compatible_node, trip_count)
211 return make_name_solver_safe ?
makeNameSolverSafe(std::move(result)) : std::move(result);
217 bool make_name_solver_safe =
false
220 src_op, src_compatible_node, sink_op,
nullptr, make_name_solver_safe);
226 bool make_name_solver_safe =
false
229 src_op,
nullptr, out_edge, sink_compatible_node, make_name_solver_safe);
235 bool make_name_solver_safe =
false
238 nullptr, src_compatible_node,
nullptr, sink_compatible_node, make_name_solver_safe);
244 int trip_count,
int number,
245 bool make_name_solver_safe =
false
249 return make_name_solver_safe ?
makeNameSolverSafe(std::move(result)) : std::move(result);
255 <<
"\tsolution_selector = " << (opts.
solution_selector ?
"set" :
"unset") <<
'\n'
256 <<
"\tedge_coster = " << (opts.
edge_coster ?
"set" :
"unset") <<
'\n'
257 <<
"\tgeneral = " << opts.
general <<
'\n'
301 throw make_from_stream<cgrame_mapper_error>([&](
auto&& s) {
302 s <<
"unhandled solver ID " <<
solver_id;
325 <<
"\tsolver_id = " << opts.
solver_id <<
'\n'
338 template<
typename MappedType>
339 using OpAndFUMap = std::unordered_map<OpAndFU, MappedType, OpAndFU::Hash>;
341 template<
typename MappedType>
342 using EdgeAndFUMap = std::unordered_map<EdgeAndFU, MappedType, EdgeAndFU::Hash>;
344 template<
typename MappedType>
345 using FUMap = std::unordered_map<MRRG::NodeDescriptor, MappedType>;
349 const Mapping& initial_mapping,
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");
362 if (do_print_configuration_and_statistics) {
363 std::cout <<
"finding a mapping via " << solve_approach_name <<
", with...\n "
364 <<
"\t" <<
"Options = " << options <<
'\n'
367 if (options.print_initial_mapping) {
370 timing_seq.
tick(
"initial dumps", 0.01);
372 #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
373 gor::Solver gorCP_solver(
"mapOpsJustByConnectivity");
375 gorsat::CpModelProto gorSatCP_model;
376 gorSatCP_model.set_name(
"mapOpsJustByConnectivity");
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");
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");
390 const auto allowed_cycle_trip_count_min_max = [&]() -> std::pair<int,int> {
391 const auto really_big = std::numeric_limits<int>::max();
392 if (options.general.getBool(
"allow_unbalanced_latency")) {
return {0, really_big}; }
393 else {
return {1, 1}; }
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);
406 timing_seq.
tick(
"compute reverse initial mapping", 0.01);
408 const auto mrrg_node_is_free_or_used_by_op_node = [&](
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()) {
416 auto& ops_mapped = search_result->second;
417 return std::find(ops_mapped.begin(), ops_mapped.end(), op_node) != ops_mapped.end();
421 const auto op_node_is_allowed_to_map_to_mrrg = [&](
424 if(fix_port.empty()) {
428 for (
auto& node : fix_port) {
429 if (op_node->
getName() == node.first) {
430 if (mrrg_node->getHierarchyQualifiedName().find(node.second) == node.second.npos) {
439 const auto op_node_can_use_mrrg_node = [&](
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);
447 const std::vector<MRRG::NodeDescriptor> all_compatible_nodes_list(
begin(all_compatible_nodes_set),
end(all_compatible_nodes_set));
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; }
455 if (paths.empty()) {
continue; }
460 const auto treatAsNeighbours = [&](
const OpAndFU& src_op_and_fu,
const OpAndFU& sink_op_and_fu) {
462 if (src_op_and_fu.
op == sink_op_and_fu.op && src_op_and_fu.
fu != sink_op_and_fu.fu) {
return false; }
464 return fu_neighbours.isReachableFrom(src_op_and_fu.
fu, sink_op_and_fu.fu);
467 if (options.general.getBool(
"print_compatible_nodes")) {
468 std::cout <<
"Compatible Nodes: " << fu_neighbours << std::endl;
471 [&](
auto&& s,
const auto& n) { s << mrrg.
getNodeRef(n).getFullName(); }
475 std::cout << std::endl;
478 if (do_print_neighbours) { std::cout <<
"FU Neighbours: " << fu_neighbours << std::endl; }
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); };
484 auto& mappings_for_op = compatible_fu_nodes[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 "
494 mappings_for_op.push_back(mapped_node);
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(
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);
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;
517 timing_seq.
tick(
"compute compatible_fu_nodes", 0.01);
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;
538 const auto fu_fu_max_edge_counts = [&](){
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);
549 timing_seq.
tick(
"compute opfu_opfu_edge_counts and fu_fu_max_edge_counts", 0.01);
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")) {
554 for (
auto& src_op1 : opgraph.
opNodes()) {
555 for (
auto& sink_op1 : opgraph.
outputOps(src_op1)) {
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; }
565 auto& mrrg_paths_item = emplace_result.first->second;
568 for (
auto& path : paths) {
570 auto& mrrg_paths_for_trip_count = mrrg_paths_item[trip_count];
571 if (do_print_paths) {
573 src_compatible_node1, sink_op1, sink_compatible_node1, trip_count, mrrg_paths_for_trip_count.
size());
577 mrrg_paths_for_trip_count.emplace_back(std::move(path));
579 if (do_print_paths) { std::cout <<
'\n'; }
589 for (
const auto& src_op1 : opgraph.
opNodes()) {
590 for (
const auto& sink_op1 : opgraph.
outputOps(src_op1)) {
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, {}} } });
603 timing_seq.
tick(
"compute mrrg_paths", 0.02);
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());
612 problem_statistics.
addInt(
"DFG - number of OPs", opgraph.
opNodes().size());
614 if (do_print_configuration_and_statistics) {
615 std::cout <<
"Problem Statistics = " << problem_statistics <<
'\n';
618 timing_seq.
tick(
"compute and print statistics", 0.01);
619 timing_seq.
tick(
"setup");
621 if (options.general.getBool(
"just_warm_up_caches")) {
623 timing_seq.
tick(
"warm up caches");
630 GRBEnv env = GRBEnv(
true);
631 if (not print_solve_progress) {
632 env.set(GRB_IntParam_OutputFlag, 0);
635 if (timelimit != 0) {
636 env.set(GRB_DoubleParam_TimeLimit, timelimit);
638 GRBModel model = GRBModel(env);
640 const auto op_is_mapped_to_var_maps = [&]() {
643 #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
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);
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];
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));
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);
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);
681 solve_statistics.
addInt(
"op_is_mapped_to_vars.size()",op_is_mapped_to_vars.size());
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 = [&]() {
687 #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
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);
700 if (not edge_vars_needed) {
704 for (
const auto& src_op : opgraph.
opNodes()) {
705 for (
const auto& out_edge : opgraph.
outEdges(src_op)) {
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()) {
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];
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);
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;
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);
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();
755 const auto path_is_used_vars_maps = [&]() {
757 std::unordered_map<MRRGPathID, GRBVar, MRRGPathID::Hash>
758 #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
760 std::unordered_map<MRRGPathID, gor::IntVar*, MRRGPathID::Hash>,
761 std::unordered_map<MRRGPathID, GorSatCPVarID, MRRGPathID::Hash>
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);
771 if (not options.general.getBool(
"path_exclusivity_modelling")) {
775 std::set<MRRGNodePair> covered_fu_pairs;
777 for (
auto& src_op : opgraph.opNodes()) {
778 for (
auto& sink_op : opgraph.outputOps(src_op)) {
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; }
787 for (
auto& trip_count_and_paths : mrrg_paths.at({src_compatible_node, sink_compatible_node})) {
789 for (
auto& path : trip_count_and_paths.second) {
791 const auto mrrg_path_id =
MRRGPathID {src_compatible_node, sink_compatible_node, trip_count_and_paths.first, path_num};
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];
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;
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);
817 solve_statistics.addInt(
"path_is_used_vars.size()",path_is_used_vars.size());
819 const auto& topological_trip_count_vars =
lazy_pointer([&]() {
821 std::map<MRRG::NodeDescriptor,GRBVar>
822 #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
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);
836 timing_seq.tick(
"create variables");
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;
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));
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);
862 auto c = [&](){
if (options.general.getBool(
"allow_multiple_op_mappings")) {
863 return model.addConstr(sum_of_nodes_mapped_to_op >= 1);
865 return model.addConstr(sum_of_nodes_mapped_to_op == 1);
867 const auto cname =
makeNameSolverSafe(
"this_op_must_be_mapped." + opgraph.getNodeRef(op).getName());
868 c.set(GRB_StringAttr_ConstrName, cname);
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);
875 gorCP_solver.AddConstraint(gorCP_solver.MakeCount(gorCP_op_vars, 1, 1));
876 addConstrainedSum(gorSatCP_model, gorSatCP_op_vars, {{1,1}});
880 timing_seq.tick(
"OpMustBeMapped constraints", 0.01);
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;
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));
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}});
910 timing_seq.tick(
"FunctionalUnitExclusivity constraints", 0.01);
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};
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;
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));
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);
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}});
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);
955 timing_seq.tick(
"FunctionalUnitFanout constraints", 0.01);
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};
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);
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);
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: ";
991 std::cout << std::endl <<
"sink compatible nodes: ";
993 std::cout << std::endl;
997 timing_seq.tick(
"FunctionalUnitFaninRequired constraints", 0.01);
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);
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);
1021 const auto sink_var = op_is_mapped_to_vars.at(sink_op_and_fu);
1022 auto c2 = model.addConstr(eu_var <= sink_var);
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);
1032 timing_seq.tick(
"FunctionalUnitFanoutImpliesUsage constraints", 0.01);
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);
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)) {
1057 std::array<GRBVar, 2> r_values { src_var, sink_var };
1059 src_op, src_compatible_node, out_edge, sink_compatible_node, trip_count_and_paths.first);
1060 model.addGenConstrAnd(
1062 r_values.data(), r_values.size(),
1071 timing_seq.tick(
"EdgeUsage constraints", 0.01);
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;
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);
1098 for (
auto& path : trip_count_and_paths.second) {
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);
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);
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);
1118 GRBLinExpr number_of_paths_used;
1120 for (
auto& path : trip_count_and_paths.second) {
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);
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);
1137 timing_seq.tick(
"EdgeRequiresPath constraints", 0.01);
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)) {
1150 for (
auto& path : trip_count_and_paths.second) {
1154 trip_count_and_paths.first,
1157 const auto input_node = path.at(path.size() - 2);
1158 number_of_paths_used_for_input[input_node] += path_is_used_vars.at(path_id);
1161 for (
const auto& input_and_number_of_paths_used : number_of_paths_used_for_input) {
1163 "use_one_path_per_input", opgraph, mrrg,
1164 src_fu, input_and_number_of_paths_used.first,
true
1166 auto c = model.addConstr(input_and_number_of_paths_used.second <= 1);
1167 c.set(GRB_StringAttr_ConstrName, constr_name);
1170 timing_seq.tick(
"FuInputsUsedByOnePath constraints", 0.01);
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;
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; }
1200 for (
const auto& trip_count_and_paths1 : mrrg_paths.at({src_compatible_node1, sink_compatible_node1})) {
1202 for (
const auto& path1 : trip_count_and_paths1.second) {
1205 for (
const auto& trip_count_and_paths2 : mrrg_paths.at({src_compatible_node2, sink_compatible_node2})) {
1207 for (
const auto& path2 : trip_count_and_paths2.second) {
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();
1218 if (node_in_path1 == node_in_path2) {
1220 if (path2.front() == path1.front()) {
1222 if (path1_cumulative_latency == path2_cumulative_latency) {
continue; }
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 },
1233 not already_constrainted_to_only_use_one_path.insert(incompatible_paths).second
1239 src_compatible_node1, sink_compatible_node1, trip_count_and_paths1.first, path_num1
1241 src_compatible_node2, sink_compatible_node2, trip_count_and_paths2.first, path_num2
1242 ) +
".concerning_node." + mrrg.getNodeRef(node_in_path1).getFullName()
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);
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);
1267 std::set<std::set<MRRGPathID>> already_constrainted_to_only_use_one_path;
1270 for (
const auto& src_op1 : opgraph.opNodes()) {
1271 for (
const auto& sink_op1 : opgraph.outputOps(src_op1)) {
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; }
1280 for (
const auto& trip_count_and_paths1 : mrrg_paths.at({src_compatible_node1, sink_compatible_node1})) {
1282 for (
const auto& path1 : trip_count_and_paths1.second) {
1285 int path1_cumulative_latency = 0;
1286 for (
const auto& node_in_path1 : path1) {
1287 path1_cumulative_latency += mrrg.getNodeRef(node_in_path1).getLatency();
1290 for (
const auto& src_op2 : opgraph.opNodes()) {
1291 for (
const auto& sink_op2 : opgraph.outputOps(src_op2)) {
1293 std::set<MRRGPathID> incompatible_paths;
1294 incompatible_paths.emplace(src_compatible_node1, sink_compatible_node1, trip_count_and_paths1.first, path_num1);
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; }
1303 for (
const auto& trip_count_and_paths2 : mrrg_paths.at({src_compatible_node2, sink_compatible_node2})) {
1305 for (
const auto& path2 : trip_count_and_paths2.second) {
1308 int path2_cumulative_latency = 0;
1309 for (
const auto& node_in_path2 : path2) {
1310 path2_cumulative_latency += mrrg.getNodeRef(node_in_path2).getLatency();
1313 if (node_in_path1 == node_in_path2) {
1315 if (path2.front() == path1.front()) {
1317 if (path1_cumulative_latency == path2_cumulative_latency) {
continue; }
1323 incompatible_paths.emplace(src_compatible_node2, sink_compatible_node2, trip_count_and_paths2.first, path_num2);
1332 incompatible_paths.size() > 1
1333 && already_constrainted_to_only_use_one_path.insert(incompatible_paths).second
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);
1339 auto c = model.addConstr(number_of_times_node_is_used
1340 <= mrrg.getNodeRef(node_in_path1).capacity * allowable_routing_usage_multiplier);
1343 src_compatible_node1, sink_compatible_node1, trip_count_and_paths1.first, path_num1
1345 +
".concerning_node." + mrrg.getNodeRef(node_in_path1).getFullName()
1346 +
".and_src." + src_op2->getName()
1347 +
".to_sink." + sink_op2->getName()
1349 c.set(GRB_StringAttr_ConstrName, cname);
1365 timing_seq.tick(
"PathExclusivity constraints", 0.01);
1371 if (options.add_trip_count_balancing_constraints) {
1372 const auto sum_of_edge_vars_times_trip_count = [&](
const auto& path) {
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; }
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;
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);
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();
1401 auto c = model.addConstr(sum1 == sum2);
1402 c.set(GRB_StringAttr_ConstrName, constraint_name);
1405 for(
const auto & path : trails_to_balance->cyclic_trails) {
1406 auto sum = sum_of_edge_vars_times_trip_count(path);
1408 s <<
"cycle_balance.";
1411 auto c = model.addConstr(sum == 1);
1412 c.set(GRB_StringAttr_ConstrName, constraint_name);
1414 timing_seq.tick(
"LatencyBalancing constraints", 0.01);
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);
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})) {
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),
1448 timing_seq.tick(
"TopologicalTripCount constraints", 0.01);
1452 Mapping result = initial_mapping;
1457 const auto status_print_interval = options.general.getInt(
"status_print_interval");
1458 const auto evaluate_solution = [
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) {
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);
1468 Mapping this_mapping = initial_mapping;
1469 this_mapping.
clear();
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) {
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) {
1489 for (
const auto& path : trip_count_and_paths.second) {
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})
1495 all_paths.push_back({path.data(), path.size() - 1});
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; }
1508 this_mapping.
mapMRRGNode(connecting_val, node_in_path);
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()) {
1527 auto suggested_mapping = options.solution_selector(this_mapping);
1530 result = suggested_mapping;
1532 return suggested_mapping.isMapped();
1535 timing_seq.tick(
"solve setup", 0.0, print_solve_progress ? &std::cout :
nullptr);
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;
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';
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); }
1557 for (
const auto& v1 : path_is_used_vars) { print_name_and_value(v1.second); }
1559 std::cout <<
"[INFO] " << solve_approach_name <<
" done writing intermediate solution to " << solution_dump_filename << std::endl;
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)));
1564 return options.general.getBool(
"path_exclusivity_modelling")
1565 && std::llround(cb_obj.getSolution(path_is_used_vars.at(path_id)));
1570 model.setCallback(&solution_selector_callback);
1572 model.set(GRB_IntParam_Seed, solver_seed);
1573 model.set(GRB_IntParam_Threads, max_threads);
1574 model.set(GRB_IntParam_PoolSearchMode, 2);
1575 model.set(GRB_IntParam_PoolSolutions, options.general.getInt(
"max_solutions"));
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;
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);
1596 gor::DecisionBuilder*
const db = gorCP_solver.MakePhase(
1598 gor::Solver::CHOOSE_FIRST_UNBOUND,
1599 gor::Solver::ASSIGN_MAX_VALUE
1602 gorCP_solver.NewSearch(db);
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();
1614 gorCP_solver.EndSearch();
1619 }
else if (options.use_solver_gor_sat_cp) {
1620 #ifdef CGRAME_USE_GOOGLE_OR_TOOLS
1621 gorsat::Model model;
1623 gorsat::SatParameters parameters;
1624 parameters.set_enumerate_all_solutions(
true);
1625 model.Add(NewSatParameters(parameters));
1630 std::atomic<bool> async_request_stop_solving(
false);
1631 model.GetOrCreate<gor::TimeLimit>()->RegisterExternalBooleanAsLimit(&async_request_stop_solving);
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);
1639 async_request_stop_solving =
true;
1643 const gorsat::CpSolverResponse& response = SolveCpModel(gorSatCP_model, &model);
1653 timing_seq.tick(
"solve");
1655 const auto num_solutions = [&]() ->
long long int {
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) {
1670 solve_statistics.addInt(
"number of solutions evaluated", num_solutions);
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;
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;
1687 }
else if (grb_status == GRB_TIME_LIMIT && num_solutions == 0) {
1692 if (options.print_mapping) {
1693 std::cout <<
"Mapping for " << solve_approach_name <<
":\n";
1694 result.outputMapping(opgraph, std::cout);
1698 if (do_print_configuration_and_statistics) {
1699 std::cout <<
"Solve Statistics = " << solve_statistics <<
'\n';
1704 throwExceptionButWrapIfGurobiException(std::current_exception());
1713 options.
general.
addString(
"solve_approach_name",
"mapOpsJustByConnectivity");
1719 std::unordered_map<std::string, std::string> fix_port,
1725 options.
general.
addString(
"solve_approach_name",
"routeOpMappingByChoosingPaths");
1737 (void)opgraph; (void)mrrg; (void)op; (void)node;
1743 [](std::shared_ptr<CGRA> cgra,
int timelimit,
const ConfigStore& args) {
1744 return std::make_unique<ILPHeuristicMapper>(cgra, timelimit, args);
1747 "Iteratively considers more flexibility.\n"
1748 "Initially presented in 'Generic Connectivity-Based CGRA Mapping via Integer Linear Programming' (FCCM 2019)",
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",
"",
""},
1764 {
"verbosity", 0,
""},
1765 {
"arch_id",
"",
""},
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"},
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},
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},
1789 {
"print_intermediate_mappings", general_options.
getInt(
"verbosity") > 0},
1790 {
"print_intermediate_mappings_placement_only",
"yes"},
1791 {
"max_solutions",
"100"},
1794 {
"solve_approach_name",
"InnerSolve"},
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"},
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)),
1809 const auto& prefix = std::string(
"hm_") + prefix_and_cs.first +
'_';
1810 auto& cs = prefix_and_cs.second;
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"));
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()) {
1829 const auto param_name = std::string{mismatch_results.second, args_kv.first.end()};
1831 const auto& default_value = cs.getStringOr(param_name,
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;
1838 cs.setString(param_name, args_kv.second);
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") {
1849 {
"nneigh_start",
"12"},
1850 {
"nneigh_stop",
"21"},
1852 }
else if (arch_id ==
"hycube" or arch_id ==
"3") {
1855 {
"nneigh_start",
"7"},
1856 {
"nneigh_stop",
"17"},
1858 }
else if (arch_id ==
"clustered" or arch_id ==
"1") {
1860 {
"nneigh_start",
"8"},
1861 {
"nneigh_stop",
"16"},
1864 if (args.getInt(
"verbosity") > 0) {
1865 std::cout <<
"Warning: uncharacterised architecture `" << arch_id <<
"'. Will use basic NN schedule\n";
1868 {
"nneigh_start",
"4"},
1869 {
"nneigh_stop",
"24"},
1875 for (
const auto& kv : arch_id_defaults) {
1876 if (general_options.getString(kv.first).empty()) {
1877 general_options.
setString(kv.first, kv.second);
1883 const auto& opgraph = *opgraph_storage;
1884 auto mrrg_cache_handle = std::make_unique<MRRGProcedureCacheHandle>();
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());
1894 const auto computed_global_options = [&] {
1896 result.
addInt(
"timelimit", std::max(1,time_remaining()));
1897 result.
addBool(
"just_warm_up_caches", cache_warmup);
1901 const auto flow_name = std::string(
"ILP heuristic mapping") + (cache_warmup ?
" cache warmup" :
"");
1903 std::cout <<
"[INFO] Starting flow `" << flow_name <<
"'\n";
1913 auto nneigh = nneigh_start - nneigh_step;
1915 nneigh += nneigh_step;
1919 if (time_remaining() == 0) { mapping.
clear();
break; }
1927 ilphm_cache, mapping
1929 timing_seq.
tick(
"test placement for NN = " +
std::to_string(nneigh), 0.0, ostream_ptr);
1930 if (not test_placement.isMapped()) {
1936 const auto inner_mapping_func = [&](
auto& op_mapping) {
1937 if (time_remaining() == 0) {
auto cpy = op_mapping; cpy.clear();
return cpy; }
1951 outer_option_overrides.
setInt(
"max_num_paths_between_FUs", 20);
1954 if (time_remaining() == 0) { mapping.
clear();
break; }
1964 }, ilphm_cache, mapping
1971 if (time_remaining() == 0) { mapping.
clear();
break; }
1990 return mapping_algo(
false);