1+ /*
2+ Copyright 2024 Huawei Technologies Co., Ltd.
3+
4+ Licensed under the Apache License, Version 2.0 (the "License");
5+ you may not use this file except in compliance with the License.
6+ You may obtain a copy of the License at
7+
8+ http://www.apache.org/licenses/LICENSE-2.0
9+
10+ Unless required by applicable law or agreed to in writing, software
11+ distributed under the License is distributed on an "AS IS" BASIS,
12+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ See the License for the specific language governing permissions and
14+ limitations under the License.
15+
16+ @author Toni Boehnlein, Benjamin Lozes, Pal Andras Papp, Raphael S. Steiner
17+ */
18+
19+ #define BOOST_TEST_MODULE IsomorphicSubgraphScheduler
20+ #include < boost/test/unit_test.hpp>
21+
22+ #include " test_graphs.hpp"
23+ #include " osp/bsp/scheduler/GreedySchedulers/GreedyBspScheduler.hpp"
24+ #include " osp/dag_divider/isomorphism_divider/IsomorphicSubgraphScheduler.hpp"
25+ #include " osp/graph_implementations/adj_list_impl/computational_dag_vector_impl.hpp"
26+
27+ #include < numeric>
28+ #include < set>
29+
30+ using namespace osp ;
31+
32+ using graph_t = computational_dag_vector_impl_def_t ;
33+ using constr_graph_t = computational_dag_vector_impl_def_t ;
34+
35+ using group_t = typename OrbitGraphProcessor<graph_t , constr_graph_t >::Group;
36+
37+ // A test class to expose private methods of IsomorphicSubgraphScheduler
38+ template <typename Graph_t, typename Constr_Graph_t>
39+ class IsomorphicSubgraphSchedulerTester : public IsomorphicSubgraphScheduler <Graph_t, Constr_Graph_t> {
40+ public:
41+ using IsomorphicSubgraphScheduler<Graph_t, Constr_Graph_t>::IsomorphicSubgraphScheduler;
42+
43+ void test_trim_subgraph_groups (std::vector<group_t >& isomorphic_groups,
44+ const unsigned min_proc_type_count) {
45+ this ->trim_subgraph_groups (isomorphic_groups, min_proc_type_count);
46+ }
47+
48+ void test_schedule_isomorphic_group (const BspInstance<Graph_t> &instance,
49+ const std::vector<group_t >& isomorphic_groups,
50+ const SubgraphSchedule &sub_sched,
51+ std::vector<vertex_idx_t <Graph_t>> &partition) {
52+ this ->schedule_isomorphic_group (instance, isomorphic_groups, sub_sched, partition);
53+ }
54+ };
55+
56+ BOOST_AUTO_TEST_SUITE (IsomorphicSubgraphSchedulerTestSuite)
57+
58+ BOOST_AUTO_TEST_CASE(EmptyGraphTest) {
59+
60+ BspInstance<graph_t > instance;
61+ instance.getArchitecture ().setNumberOfProcessors (4 );
62+
63+ GreedyBspScheduler<constr_graph_t > greedy_scheduler;
64+ IsomorphicSubgraphScheduler<graph_t , constr_graph_t > iso_scheduler (greedy_scheduler);
65+
66+ auto partition = iso_scheduler.compute_partition (instance);
67+ BOOST_CHECK (partition.empty ());
68+ }
69+
70+ BOOST_AUTO_TEST_CASE (TrimSubgraphGroupsTest_NoTrim) {
71+ GreedyBspScheduler<constr_graph_t > greedy_scheduler;
72+ IsomorphicSubgraphSchedulerTester<graph_t , constr_graph_t > tester (greedy_scheduler);
73+
74+ // A single group with 4 subgraphs, each with 1 node.
75+ std::vector<group_t > iso_groups = { group_t { { {0 }, {1 }, {2 }, {3 } } } };
76+
77+ // Group size (4) is a divisor of min_proc_type_count (8), so no trim.
78+ tester.test_trim_subgraph_groups (iso_groups, 8 );
79+
80+ BOOST_CHECK_EQUAL (iso_groups.size (), 1 );
81+ BOOST_CHECK_EQUAL (iso_groups[0 ].subgraphs .size (), 4 ); // Still 4 subgraphs in the group
82+ }
83+
84+ BOOST_AUTO_TEST_CASE (TrimSubgraphGroupsTest_WithTrim) {
85+ GreedyBspScheduler<constr_graph_t > greedy_scheduler;
86+ IsomorphicSubgraphSchedulerTester<graph_t , constr_graph_t > tester (greedy_scheduler);
87+
88+ // 6 subgraphs, each with 1 node and work weight 10.
89+ std::vector<group_t > iso_groups = { group_t { { {0 }, {1 }, {2 }, {3 }, {4 }, {5 } } } };
90+
91+ // Group size (6) is not a divisor of min_proc_type_count (8).
92+ // gcd(6, 8) = 2.
93+ // merge_size = 6 / 2 = 3.
94+ // The 6 subgraphs should be merged into 2 new subgraphs, each containing 3 old ones.
95+ tester.test_trim_subgraph_groups (iso_groups, 8 );
96+
97+ BOOST_CHECK_EQUAL (iso_groups.size (), 1 );
98+ BOOST_REQUIRE_EQUAL (iso_groups[0 ].subgraphs .size (), 2 ); // Group now contains 2 merged subgraphs
99+
100+ // Check that the new subgraphs are correctly merged.
101+ BOOST_CHECK_EQUAL (iso_groups[0 ].subgraphs [0 ].size (), 3 );
102+ BOOST_CHECK_EQUAL (iso_groups[0 ].subgraphs [1 ].size (), 3 );
103+
104+ const auto & final_sgs = iso_groups[0 ].subgraphs ;
105+ std::set<unsigned > vertices_sg0 (final_sgs[0 ].begin (), final_sgs[0 ].end ());
106+ std::set<unsigned > vertices_sg1 (final_sgs[1 ].begin (), final_sgs[1 ].end ());
107+ std::set<unsigned > expected_sg0 = {0 , 1 , 2 };
108+ std::set<unsigned > expected_sg1 = {3 , 4 , 5 };
109+ BOOST_CHECK (vertices_sg0 == expected_sg0);
110+ BOOST_CHECK (vertices_sg1 == expected_sg1);
111+ }
112+
113+ BOOST_AUTO_TEST_CASE (TrimSubgraphGroupsTest_MultipleGroups) {
114+ GreedyBspScheduler<constr_graph_t > greedy_scheduler;
115+ IsomorphicSubgraphSchedulerTester<graph_t , constr_graph_t > tester (greedy_scheduler);
116+
117+ // Group 1: size 6. gcd(6, 9) = 3. merge_size = 6/3 = 2. -> 3 subgraphs of size 2.
118+ // Group 2: size 3. gcd(3, 9) = 3. merge_size = 3/3 = 1. -> no trim.
119+ // Group 3: size 5. gcd(5, 9) = 1. merge_size = 5/1 = 5. -> 1 subgraph of size 5.
120+ std::vector<group_t > iso_groups = {
121+ group_t { { {0 }, {1 }, {2 }, {3 }, {4 }, {5 } } }, // Group 1
122+ group_t { { {10 }, {11 }, {12 } } }, // Group 2
123+ group_t { { {20 }, {21 }, {22 }, {23 }, {24 } } } // Group 3
124+ };
125+
126+ tester.test_trim_subgraph_groups (iso_groups, 9 );
127+
128+ BOOST_REQUIRE_EQUAL (iso_groups.size (), 3 );
129+
130+ // Check Group 1
131+ BOOST_REQUIRE_EQUAL (iso_groups[0 ].subgraphs .size (), 3 );
132+ BOOST_CHECK_EQUAL (iso_groups[0 ].subgraphs [0 ].size (), 2 );
133+ BOOST_CHECK_EQUAL (iso_groups[0 ].subgraphs [1 ].size (), 2 );
134+ BOOST_CHECK_EQUAL (iso_groups[0 ].subgraphs [2 ].size (), 2 );
135+
136+ // Check Group 2
137+ BOOST_REQUIRE_EQUAL (iso_groups[1 ].subgraphs .size (), 3 );
138+ BOOST_CHECK_EQUAL (iso_groups[1 ].subgraphs [0 ].size (), 1 );
139+
140+ // Check Group 3
141+ BOOST_REQUIRE_EQUAL (iso_groups[2 ].subgraphs .size (), 1 );
142+ BOOST_CHECK_EQUAL (iso_groups[2 ].subgraphs [0 ].size (), 5 );
143+ }
144+
145+ BOOST_AUTO_TEST_CASE (ScheduleIsomorphicGroup_HeterogeneousArch) {
146+ // --- Setup ---
147+ BspInstance<graph_t > instance;
148+ auto & dag = instance.getComputationalDag ();
149+ // Two isomorphic groups:
150+ // Group 0: {0,1}, {2,3} (type 0)
151+ // Group 1: {4}, {5} (type 1)
152+ dag.add_vertex (10 , 1 , 1 , 0 ); dag.add_vertex (10 , 1 , 1 , 0 ); // 0, 1
153+ dag.add_vertex (10 , 1 , 1 , 0 ); dag.add_vertex (10 , 1 , 1 , 0 ); // 2, 3
154+ dag.add_vertex (20 , 1 , 1 , 1 ); // 4
155+ dag.add_vertex (20 , 1 , 1 , 1 ); // 5
156+ dag.add_edge (0 , 1 ); dag.add_edge (2 , 3 );
157+ dag.add_edge (1 , 4 ); dag.add_edge (3 , 5 );
158+
159+ // 2 procs of type 0, 2 procs of type 1
160+ instance.getArchitecture ().setProcessorsWithTypes ({0 , 0 , 1 , 1 });
161+ instance.setDiagonalCompatibilityMatrix (2 );
162+
163+ std::vector<group_t > iso_groups = {
164+ group_t { { {0 , 1 }, {2 , 3 } } },
165+ group_t { { {4 }, {5 } } }
166+ };
167+
168+ // Mock SubgraphSchedule from EFT scheduler
169+ // Group 0 (2 subgraphs) gets 2 workers of type 0
170+ // Group 1 (2 subgraphs) gets 2 workers of type 1
171+ SubgraphSchedule sub_sched;
172+ sub_sched.node_assigned_worker_per_type .resize (2 );
173+ sub_sched.node_assigned_worker_per_type [0 ] = {2 , 0 }; // 2xT0 for group 0
174+ sub_sched.node_assigned_worker_per_type [1 ] = {0 , 2 }; // 2xT1 for group 1
175+
176+ std::vector<vertex_idx_t <graph_t >> partition (dag.num_vertices ());
177+
178+ GreedyBspScheduler<constr_graph_t > greedy_scheduler;
179+ IsomorphicSubgraphSchedulerTester<graph_t , constr_graph_t > tester (greedy_scheduler);
180+
181+ // --- Execute ---
182+ tester.test_schedule_isomorphic_group (instance, iso_groups, sub_sched, partition);
183+
184+ // --- Assert ---
185+ // Group 0 has 2 subgraphs, scheduled on 2 processors.
186+ // The internal scheduler for the representative {0,1} will likely put both on one processor.
187+ // So, {0,1} will be in one partition, and {2,3} will be in another.
188+ BOOST_CHECK_EQUAL (partition[0 ], partition[1 ]);
189+ BOOST_CHECK_EQUAL (partition[2 ], partition[3 ]);
190+ BOOST_CHECK_NE (partition[0 ], partition[2 ]);
191+
192+ // Group 1 has 2 subgraphs, scheduled on 2 processors.
193+ // Each subgraph {4} and {5} gets its own partition.
194+ BOOST_CHECK_NE (partition[4 ], partition[5 ]);
195+
196+ // Check that partitions for different groups are distinct
197+ BOOST_CHECK_NE (partition[0 ], partition[4 ]);
198+ BOOST_CHECK_NE (partition[0 ], partition[5 ]);
199+ BOOST_CHECK_NE (partition[2 ], partition[4 ]);
200+ BOOST_CHECK_NE (partition[2 ], partition[5 ]);
201+
202+ // Verify all partitions are unique as expected
203+ std::set<vertex_idx_t <graph_t >> partition_ids;
204+ for (const auto & p_id : partition) partition_ids.insert (p_id);
205+ BOOST_CHECK_EQUAL (partition_ids.size (), 4 );
206+ }
207+
208+ BOOST_AUTO_TEST_SUITE_END ()
0 commit comments