diff --git a/include/osp/dag_divider/isomorphism_divider/IsomorphicSubgraphScheduler.hpp b/include/osp/dag_divider/isomorphism_divider/IsomorphicSubgraphScheduler.hpp index 3911b3c9..5ba326d9 100644 --- a/include/osp/dag_divider/isomorphism_divider/IsomorphicSubgraphScheduler.hpp +++ b/include/osp/dag_divider/isomorphism_divider/IsomorphicSubgraphScheduler.hpp @@ -63,25 +63,26 @@ class IsomorphicSubgraphScheduler { static constexpr bool verbose = false; const HashComputer>* hash_computer_; size_t symmetry_ = 4; - size_t min_symmetry_ = 2; Scheduler * bsp_scheduler_; bool use_max_group_size_ = false; unsigned max_group_size_ = 0; bool plot_dot_graphs_ = false; v_workw_t work_threshold_ = 10; v_workw_t critical_path_threshold_ = 10; - double orbit_lock_ratio_ = 0.2; + double orbit_lock_ratio_ = 0.4; + double natural_breaks_count_percentage_ = 0.1; bool merge_different_node_types = true; bool allow_use_trimmed_scheduler = true; bool use_max_bsp = false; + bool use_adaptive_symmetry_threshold = true; public: explicit IsomorphicSubgraphScheduler(Scheduler & bsp_scheduler) - : hash_computer_(nullptr), symmetry_(4), bsp_scheduler_(&bsp_scheduler), plot_dot_graphs_(false) {} + : hash_computer_(nullptr), bsp_scheduler_(&bsp_scheduler), plot_dot_graphs_(false) {} IsomorphicSubgraphScheduler(Scheduler & bsp_scheduler, const HashComputer>& hash_computer) - : hash_computer_(&hash_computer), symmetry_(4), bsp_scheduler_(&bsp_scheduler), plot_dot_graphs_(false) {} + : hash_computer_(&hash_computer), bsp_scheduler_(&bsp_scheduler), plot_dot_graphs_(false) {} virtual ~IsomorphicSubgraphScheduler() {} @@ -89,9 +90,8 @@ class IsomorphicSubgraphScheduler { void setWorkThreshold(v_workw_t work_threshold) {work_threshold_ = work_threshold;} void setCriticalPathThreshold(v_workw_t critical_path_threshold) {critical_path_threshold_ = critical_path_threshold;} void setOrbitLockRatio(double orbit_lock_ratio) {orbit_lock_ratio_ = orbit_lock_ratio;} + void setNaturalBreaksCountPercentage(double natural_breaks_count_percentage) {natural_breaks_count_percentage_ = natural_breaks_count_percentage;} void setAllowTrimmedScheduler(bool flag) {allow_use_trimmed_scheduler = flag;} - void set_symmetry(size_t symmetry) { symmetry_ = symmetry; } - void setMinSymmetry(size_t min_symmetry) { min_symmetry_ = min_symmetry; } void set_plot_dot_graphs(bool plot) { plot_dot_graphs_ = plot; } void disable_use_max_group_size() { use_max_group_size_ = false; } void setUseMaxBsp(bool flag) { use_max_bsp = flag; } @@ -99,16 +99,22 @@ class IsomorphicSubgraphScheduler { use_max_group_size_ = true; max_group_size_ = max_group_size; } - + void setEnableAdaptiveSymmetryThreshold() { use_adaptive_symmetry_threshold = true; } + void setUseStaticSymmetryLevel(size_t static_symmetry_level) { + use_adaptive_symmetry_threshold = false; + symmetry_ = static_symmetry_level; + } std::vector> compute_partition(const BspInstance& instance) { - OrbitGraphProcessor orbit_processor(symmetry_); + OrbitGraphProcessor orbit_processor; orbit_processor.set_work_threshold(work_threshold_); orbit_processor.setMergeDifferentNodeTypes(merge_different_node_types); orbit_processor.setCriticalPathThreshold(critical_path_threshold_); orbit_processor.setLockRatio(orbit_lock_ratio_); - orbit_processor.setMinSymmetry(min_symmetry_); - + orbit_processor.setNaturalBreaksCountPercentage(natural_breaks_count_percentage_); + if (not use_adaptive_symmetry_threshold) { + orbit_processor.setUseStaticSymmetryLevel(symmetry_); + } std::unique_ptr>> local_hasher; if (!hash_computer_) { diff --git a/include/osp/dag_divider/isomorphism_divider/OrbitGraphProcessor.hpp b/include/osp/dag_divider/isomorphism_divider/OrbitGraphProcessor.hpp index 81b60d48..ddb99122 100644 --- a/include/osp/dag_divider/isomorphism_divider/OrbitGraphProcessor.hpp +++ b/include/osp/dag_divider/isomorphism_divider/OrbitGraphProcessor.hpp @@ -94,7 +94,6 @@ class OrbitGraphProcessor { std::vector final_groups_; size_t current_symmetry; - size_t symmetry_threshold_ = 8; // max symmetry threshold size_t min_symmetry_ = 2; // min symmetry threshold v_workw_t work_threshold_ = 0; v_workw_t critical_path_threshold_ = 0; @@ -105,6 +104,8 @@ class OrbitGraphProcessor { std::vector work_percentiles_ = {0.50, 0.75}; double natural_breaks_count_percentage_ = 0.2; + bool use_adaptive_symmetry_threshold_ = true; + struct PairHasher { template std::size_t operator()(const std::pair &p) const { @@ -514,20 +515,25 @@ class OrbitGraphProcessor { public: - explicit OrbitGraphProcessor(size_t symmetry_threshold = 2) : symmetry_threshold_(symmetry_threshold) {} + explicit OrbitGraphProcessor() {} - void set_symmetry_threshold(size_t threshold) { symmetry_threshold_ = threshold; } void setMergeDifferentNodeTypes(bool flag) { merge_different_node_types_ = flag; } void set_work_threshold(v_workw_t work_threshold) { work_threshold_ = work_threshold; } void setCriticalPathThreshold(v_workw_t critical_path_threshold) { critical_path_threshold_ = critical_path_threshold; } void setLockRatio(double lock_ratio) { lock_orbit_ratio = lock_ratio; } - void setMinSymmetry(size_t min_symmetry) { min_symmetry_ = min_symmetry; } + void setSymmetryLevelHeuristic(SymmetryLevelHeuristic heuristic) { symmetry_level_heuristic_ = heuristic; } void setWorkPercentiles(const std::vector& percentiles) { work_percentiles_ = percentiles; std::sort(work_percentiles_.begin(), work_percentiles_.end()); } + void setUseStaticSymmetryLevel(size_t static_symmetry_level) { + symmetry_level_heuristic_ = SymmetryLevelHeuristic::NATURAL_BREAKS; + use_adaptive_symmetry_threshold_ = false; + current_symmetry = static_symmetry_level; + } + void setNaturalBreaksCountPercentage(double percentage) { natural_breaks_count_percentage_ = percentage; } @@ -568,13 +574,16 @@ class OrbitGraphProcessor { v_workw_t total_work = 0; for (const auto &[hash, vertices] : orbits) { const size_t orbit_size = vertices.size(); + + if (orbit_size == 1U) continue; // exclude single node orbits from total work + orbit_size_counts[orbit_size]++; v_workw_t orbit_work = 0; for (const auto v : vertices) { orbit_work += dag.vertex_work_weight(v); } - + if (not merge_different_node_types_ && has_typed_vertices_v) { work_per_vertex_type[dag.vertex_type(vertices[0])] += orbit_work; } else { @@ -582,7 +591,7 @@ class OrbitGraphProcessor { } work_per_orbit_size[orbit_size] += orbit_work; - total_work += orbit_work; + total_work += orbit_work; } std::vector> lock_threshold_per_type(work_per_vertex_type.size()); @@ -624,7 +633,28 @@ class OrbitGraphProcessor { } coarser_util::construct_coarse_dag(dag, coarse_graph_, contraction_map_); - perform_coarsening_adaptive_symmetry(dag, coarse_graph_, lock_threshold_per_type, symmetry_levels_to_test); + + if (use_adaptive_symmetry_threshold_) { + perform_coarsening_adaptive_symmetry(dag, coarse_graph_, lock_threshold_per_type, symmetry_levels_to_test); + } else { + size_t total_size_count = 0U; + for (const auto& [size, count] : orbit_size_counts) { + total_size_count += count; + } + + for (const auto& [size, count] : orbit_size_counts) { + if (size == 1U || size > current_symmetry) continue; + + if (count > total_size_count / 2) { + if constexpr (verbose) { + std::cout << "Setting current_symmetry to " << size << " because " << count << " orbits of size " << size << " are more than half of the total number of orbits.\n"; + } + current_symmetry = size; + } + } + + perform_coarsening(dag, coarse_graph_); + } } private: @@ -640,9 +670,7 @@ class OrbitGraphProcessor { if constexpr (verbose) { std::cout << "Using PERCENTILE_BASED heuristic for symmetry levels.\n"; } size_t percentile_idx = 0; v_workw_t cumulative_work = 0; - for (auto it = work_per_orbit_size.rbegin(); - it != work_per_orbit_size.rend(); - ++it) + for (auto it = work_per_orbit_size.rbegin(); it != work_per_orbit_size.rend(); ++it) { cumulative_work += it->second; if (total_work == 0) continue; // Avoid division by zero diff --git a/tests/isomorphic_subgraph_scheduler.cpp b/tests/isomorphic_subgraph_scheduler.cpp index c011ef1b..9165c5a5 100644 --- a/tests/isomorphic_subgraph_scheduler.cpp +++ b/tests/isomorphic_subgraph_scheduler.cpp @@ -98,8 +98,7 @@ BOOST_AUTO_TEST_CASE(TrimSubgraphGroupsTest_WithTrim) { GreedyBspScheduler greedy_scheduler; IsomorphicSubgraphSchedulerTester tester(greedy_scheduler); tester.setAllowTrimmedScheduler(false); - tester.set_symmetry(4); - tester.setMinSymmetry(4); + BspInstance instance; auto& dag = instance.getComputationalDag(); diff --git a/tests/orbit_graph_processor.cpp b/tests/orbit_graph_processor.cpp index 240d76f2..d79a83a9 100644 --- a/tests/orbit_graph_processor.cpp +++ b/tests/orbit_graph_processor.cpp @@ -120,7 +120,7 @@ BOOST_AUTO_TEST_CASE(OrbitGraphProcessor_SimpleMerge) { // Initial orbits: {0, 2} and {1, 3}. Coarse graph: 0 -> 1 // With threshold 2, these should be merged. - OrbitGraphProcessor processor(2); + OrbitGraphProcessor processor; MerkleHashComputer, true> hasher(dag, dag); processor.discover_isomorphic_groups(dag, hasher); @@ -158,7 +158,7 @@ BOOST_AUTO_TEST_CASE(OrbitGraphProcessor_ForkJoinNoMerge) { // Initial orbits: {0}, {1,2}, {3}. Coarse graph: 0 -> 1 -> 2 // Merging 0 and 1 would result in a group of size 1 ({0,1,2}), which is not viable (threshold 2). // Merging 1 and 2 would also result in a group of size 1 ({1,2,3}), not viable. - OrbitGraphProcessor processor(2); + OrbitGraphProcessor processor; MerkleHashComputer, true> hasher(dag, dag); processor.discover_isomorphic_groups(dag, hasher); @@ -193,7 +193,7 @@ BOOST_AUTO_TEST_CASE(OrbitGraphProcessor_PartitionCheck_MediumGraph) { BOOST_REQUIRE_GT(dag.num_vertices(), 0); // Use a higher threshold to encourage more merging on this larger graph - OrbitGraphProcessor processor(4); + OrbitGraphProcessor processor; MerkleHashComputer, true> hasher(dag, dag); processor.discover_isomorphic_groups(dag, hasher); @@ -208,7 +208,7 @@ BOOST_AUTO_TEST_CASE(OrbitGraphProcessor_MultiPipelineMerge) { const auto dag = construct_multi_pipeline_dag(5, 4); BOOST_REQUIRE_EQUAL(dag.num_vertices(), 20); - OrbitGraphProcessor processor(5); // Set threshold to match pipeline count + OrbitGraphProcessor processor; // Set threshold to match pipeline count MerkleHashComputer, true> hasher(dag, dag); processor.discover_isomorphic_groups(dag, hasher); @@ -236,7 +236,7 @@ BOOST_AUTO_TEST_CASE(OrbitGraphProcessor_LadderNoMerge) { const auto dag = construct_ladder_dag(10); BOOST_REQUIRE_EQUAL(dag.num_vertices(), 22); - OrbitGraphProcessor processor(2); + OrbitGraphProcessor processor; MerkleHashComputer, true> hasher(dag, dag); processor.discover_isomorphic_groups(dag, hasher); @@ -256,7 +256,7 @@ BOOST_AUTO_TEST_CASE(OrbitGraphProcessor_AsymmetricNoMerge) { const auto dag = construct_asymmetric_dag(30); BOOST_REQUIRE_EQUAL(dag.num_vertices(), 30); - OrbitGraphProcessor processor(2); + OrbitGraphProcessor processor; MerkleHashComputer, true> hasher(dag, dag); processor.discover_isomorphic_groups(dag, hasher); @@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE(OrbitGraphProcessor_BinaryTreeNoMerge) { const auto dag = construct_binary_out_tree(4); BOOST_REQUIRE_EQUAL(dag.num_vertices(), (1 << 5) - 1); - OrbitGraphProcessor processor(2); + OrbitGraphProcessor processor; MerkleHashComputer, true> hasher(dag, dag); processor.discover_isomorphic_groups(dag, hasher); @@ -293,8 +293,7 @@ BOOST_AUTO_TEST_CASE(OrbitGraphProcessor_ButterflyMerge) { const auto dag = construct_butterfly_dag(3); BOOST_REQUIRE_EQUAL(dag.num_vertices(), (3 + 1) * 8); - OrbitGraphProcessor processor(16); - processor.setMinSymmetry(16); + OrbitGraphProcessor processor; MerkleHashComputer, true> hasher(dag, dag); processor.discover_isomorphic_groups(dag, hasher);