1 /**
2 * Copyright 2019 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 #include "parallel/tensor_layout/util_layout_gen_test.h"
17 #include <cmath>
18 #include <map>
19 #include <tuple>
20 #include <vector>
21 #include <utility>
22 #include <algorithm>
23 #include <iterator>
24 #include "frontend/parallel/tensor_layout/shape_util.h"
25 #include "common/common_test.h"
26
27 using std::pow;
28
29 namespace mindspore {
30 namespace parallel {
combine(const Shape & in,int64_t target)31 std::vector<Shape> combine(const Shape &in, int64_t target) {
32 std::vector<Shape> output;
33 for (int64_t i = 0; i < pow(2, in.size()); i++) {
34 size_t temp = 0;
35 size_t count = 0;
36 Shape left;
37 for (size_t j = 0; j < in.size(); j++) {
38 if ((i & (1 << j)) != 0) {
39 left.push_back(j);
40 count++;
41 }
42 }
43 if (count == target) {
44 Shape one_case;
45 for (size_t j = 0; j < count; j++) {
46 temp = in.size() - 1 - left[j];
47 one_case.push_back(in[temp]);
48 }
49 if (one_case.size() > 0) {
50 output.push_back(one_case);
51 }
52 }
53 }
54 return output;
55 }
56
GenerateValidShapeBySizeAndDim(int64_t pow_size,int64_t dim,std::vector<Shape> * out)57 void GenerateValidShapeBySizeAndDim(int64_t pow_size, int64_t dim, std::vector<Shape> *out) {
58 out->clear();
59 Shape in;
60 for (int64_t i = 1; i < pow_size; i++) {
61 in.push_back(i);
62 }
63 std::vector<Shape> combine_result;
64 combine_result = combine(in, dim - 1);
65 if (combine_result.size() == 0) {
66 int64_t size = exp2(pow_size);
67 Shape item = {size};
68 out->push_back(item);
69 }
70 for (size_t i = 0; i < combine_result.size(); i++) {
71 Shape item;
72 int64_t prev = 0;
73 for (int64_t j = combine_result[i].size() - 1; j >= 0; j--) {
74 item.push_back(exp2(combine_result[i][j] - prev));
75 prev = combine_result[i][j];
76 }
77 item.push_back(exp2(pow_size - prev));
78 out->push_back(item);
79 }
80 return;
81 }
82
GenerateValidShapeBySize(int64_t pow_size,std::vector<Shape> * out)83 void GenerateValidShapeBySize(int64_t pow_size, std::vector<Shape> *out) {
84 out->clear();
85 for (int64_t dim = 1; dim <= pow_size; dim++) {
86 std::vector<Shape> combine_result;
87 GenerateValidShapeBySizeAndDim(pow_size, dim, &combine_result);
88 for (size_t i = 0; i < combine_result.size(); i++) {
89 out->push_back(combine_result[i]);
90 }
91 }
92 return;
93 }
94
GenerateTensorMap(const int64_t & map_size,const Shape & pos_index,const Shape & pos_value)95 TensorMap GenerateTensorMap(const int64_t &map_size, const Shape &pos_index, const Shape &pos_value) {
96 TensorMap tensor_map(map_size, -1);
97 for (size_t i = 0; i < pos_index.size() && i < pos_value.size(); i++) {
98 if (pos_index[i] >= map_size) {
99 continue;
100 }
101 tensor_map[pos_index[i]] = pos_value[i];
102 }
103 return tensor_map;
104 }
105
GenerateValidTensorMap(const DeviceArrangement & device_arrangement,const TensorShape & tensor_shape,std::vector<TensorMap> * tensor_map_list)106 void GenerateValidTensorMap(const DeviceArrangement &device_arrangement, const TensorShape &tensor_shape,
107 std::vector<TensorMap> *tensor_map_list) {
108 tensor_map_list->clear();
109 int64_t device_size = device_arrangement.size();
110 int64_t shape_size = tensor_shape.size();
111 Shape pos_ind_combine_in;
112 for (int64_t i = 0; i < shape_size; i++) {
113 pos_ind_combine_in.push_back(i);
114 }
115 Shape dev_ind_combine_in;
116 for (int64_t i = 0; i < device_size; i++) {
117 dev_ind_combine_in.push_back(i);
118 }
119 TensorMap none_map(tensor_shape.size(), -1);
120 tensor_map_list->push_back(none_map);
121 for (int64_t pos_num = 1; (pos_num <= shape_size) && (pos_num <= device_size); pos_num++) {
122 std::vector<Shape> pos_index;
123 pos_index = combine(pos_ind_combine_in, pos_num);
124 std::vector<Shape> dev_index;
125 dev_index = combine(dev_ind_combine_in, pos_num);
126 for (size_t l = 0; l < dev_index.size(); l++) {
127 Shape pos_value_combine_in;
128 for (int32_t i = dev_index[l].size() - 1; i >= 0; i--) {
129 pos_value_combine_in.push_back(dev_index[l][i]);
130 }
131 std::vector<Shape> pos_value;
132 Shape::iterator it = pos_value_combine_in.begin();
133 do {
134 Shape pos_value_item;
135 for (size_t m = 0; m < pos_num; m++) {
136 pos_value_item.push_back(pos_value_combine_in[m]);
137 }
138 pos_value.push_back(pos_value_item);
139 } while (next_permutation(it, it + pos_num));
140 for (size_t j = 0; j < pos_index.size(); j++) {
141 for (size_t k = 0; k < pos_value.size(); k++) {
142 TensorMap tensor_map = GenerateTensorMap(shape_size, pos_index[j], pos_value[k]);
143 tensor_map_list->push_back(tensor_map);
144 }
145 }
146 }
147 }
148 return;
149 }
150
GenerateValidLayoutByDeviceSizeAndTensorSize(int64_t device_pow_size,int64_t tensor_pow_size,int64_t max_device_dim,int64_t max_shape_dim,std::vector<std::tuple<DeviceArrangement,TensorMap,TensorShape>> * layout_list)151 void GenerateValidLayoutByDeviceSizeAndTensorSize(
152 int64_t device_pow_size, int64_t tensor_pow_size, int64_t max_device_dim, int64_t max_shape_dim,
153 std::vector<std::tuple<DeviceArrangement, TensorMap, TensorShape>> *layout_list) {
154 layout_list->clear();
155 std::vector<DeviceArrangement> device_arrangement_list;
156 GenerateValidShapeBySize(device_pow_size, &device_arrangement_list);
157 std::vector<TensorShape> tensor_shape_list;
158 GenerateValidShapeBySize(tensor_pow_size, &tensor_shape_list);
159 for (size_t device_idx = 0; device_idx < device_arrangement_list.size(); device_idx++) {
160 for (size_t shape_idx = 0; shape_idx < tensor_shape_list.size(); shape_idx++) {
161 std::vector<TensorMap> tensor_map_list;
162 GenerateValidTensorMap(device_arrangement_list[device_idx], tensor_shape_list[shape_idx], &tensor_map_list);
163 for (size_t map_idx = 0; map_idx < tensor_map_list.size(); map_idx++) {
164 if (!CheckLayoutValid(device_arrangement_list[device_idx], tensor_map_list[map_idx],
165 tensor_shape_list[shape_idx])) {
166 continue;
167 }
168 layout_list->push_back(
169 std::make_tuple(device_arrangement_list[device_idx], tensor_map_list[map_idx], tensor_shape_list[shape_idx]));
170 }
171 }
172 }
173 return;
174 }
175
CheckLayoutValid(const DeviceArrangement & device_arrangement,const TensorMap & tensor_map,const TensorShape & tensor_shape)176 bool CheckLayoutValid(const DeviceArrangement &device_arrangement, const TensorMap &tensor_map,
177 const TensorShape &tensor_shape) {
178 bool flag = false;
179 if ((tensor_map.size() - ComputeNoneNumber(tensor_map)) > device_arrangement.size()) {
180 return flag;
181 }
182 if (!ShapeIsDividedByDevice(device_arrangement, tensor_map, tensor_shape)) {
183 return flag;
184 }
185 return true;
186 }
187
ComputeNoneNumber(const TensorMap & tensor_map)188 size_t ComputeNoneNumber(const TensorMap &tensor_map) {
189 size_t num = 0;
190 for (size_t i = 0; i < tensor_map.size(); i++) {
191 if (tensor_map[i] == -1) {
192 num++;
193 }
194 }
195 return num;
196 }
197
ShapeIsDividedByDevice(const DeviceArrangement & device_arrangement,const TensorMap & tensor_map,const TensorShape & tensor_shape)198 bool ShapeIsDividedByDevice(const DeviceArrangement &device_arrangement, const TensorMap &tensor_map,
199 const TensorShape &tensor_shape) {
200 bool flag = false;
201 for (uint32_t i = 0; i < tensor_map.size() && i < tensor_shape.size(); i++) {
202 if (tensor_map[i] == -1) {
203 continue;
204 }
205 int64_t dim = device_arrangement[device_arrangement.size() - 1 - tensor_map[i]];
206 if (tensor_shape[i] % dim != 0) {
207 return flag;
208 }
209 }
210 return true;
211 }
212
IsExpended(const Shape & in1,const Shape & in2)213 bool IsExpended(const Shape &in1, const Shape &in2) {
214 int64_t size = 1;
215 uint32_t ind = 0;
216 for (uint32_t i = 0; i < in1.size(); i++) {
217 size *= in1[i];
218 if (ind >= in2.size()) {
219 return false;
220 }
221 if (size > in2[ind]) {
222 return false;
223 } else if (size < in2[ind]) {
224 continue;
225 } else {
226 ind++;
227 size = 1;
228 }
229 }
230 if (ind != in2.size()) {
231 return false;
232 }
233 return true;
234 }
235
ComputeAccumDeviceTOAccumShapeMap(const DeviceArrangement & device_arrangement,const TensorMap & tensor_map,const TensorShape & tensor_shape,std::map<int64_t,int64_t> * accum_device_to_accum_shape_map)236 void ComputeAccumDeviceTOAccumShapeMap(const DeviceArrangement &device_arrangement, const TensorMap &tensor_map,
237 const TensorShape &tensor_shape,
238 std::map<int64_t, int64_t> *accum_device_to_accum_shape_map) {
239 accum_device_to_accum_shape_map->clear();
240 std::vector<int64_t> shape_accum_reverse;
241 Status status = ShapeToAccumulateProductReverse(tensor_shape, &shape_accum_reverse);
242 ASSERT_EQ(Status::SUCCESS, status);
243 std::vector<int64_t> device_accum_reverse;
244 status = ShapeToAccumulateProductReverse(device_arrangement, &device_accum_reverse);
245 ASSERT_EQ(Status::SUCCESS, status);
246 for (int32_t i = 0; i < device_accum_reverse.size(); i++) {
247 auto iter = std::find(tensor_map.begin(), tensor_map.end(), device_accum_reverse.size() - 1 - i);
248 if (iter == tensor_map.end()) {
249 accum_device_to_accum_shape_map->insert(std::make_pair(device_accum_reverse[i], -1));
250 } else {
251 accum_device_to_accum_shape_map->insert(
252 std::make_pair(device_accum_reverse[i], shape_accum_reverse[std::distance(tensor_map.begin(), iter)]));
253 }
254 }
255 return;
256 }
257
IsLinearValue(int64_t small,int64_t big,int64_t small_value,int64_t big_value,int64_t middle,int64_t middle_value)258 void IsLinearValue(int64_t small, int64_t big, int64_t small_value, int64_t big_value, int64_t middle,
259 int64_t middle_value) {
260 ASSERT_NE(big, small);
261 int64_t value = (middle - small) * (big_value - small_value) / (big - small) + small_value;
262 ASSERT_EQ(middle_value, value);
263 }
264
LayoutTransferValidLayoutChangeCheck(const DeviceArrangement & in_device_arrangement,const TensorMap & in_tensor_map,const TensorShape & in_tensor_shape,const DeviceArrangement & out_device_arrangement,const TensorMap & out_tensor_map,const TensorShape & out_tensor_shape)265 void LayoutTransferValidLayoutChangeCheck(const DeviceArrangement &in_device_arrangement,
266 const TensorMap &in_tensor_map, const TensorShape &in_tensor_shape,
267 const DeviceArrangement &out_device_arrangement,
268 const TensorMap &out_tensor_map, const TensorShape &out_tensor_shape) {
269 bool is_expended = IsExpended(out_device_arrangement, in_device_arrangement);
270 ASSERT_EQ(true, is_expended);
271 is_expended = IsExpended(out_tensor_shape, in_tensor_shape);
272 ASSERT_EQ(true, is_expended);
273 std::map<int64_t, int64_t> out_accum_device_to_accum_shape_map;
274 ComputeAccumDeviceTOAccumShapeMap(out_device_arrangement, out_tensor_map, out_tensor_shape,
275 &out_accum_device_to_accum_shape_map);
276 std::map<int64_t, int64_t> in_accum_device_to_accum_shape_map;
277 ComputeAccumDeviceTOAccumShapeMap(in_device_arrangement, in_tensor_map, in_tensor_shape,
278 &in_accum_device_to_accum_shape_map);
279 std::map<int64_t, int64_t>::iterator in_iter = in_accum_device_to_accum_shape_map.begin();
280 while (in_iter != in_accum_device_to_accum_shape_map.end()) {
281 if (in_iter->second != out_accum_device_to_accum_shape_map[in_iter->first]) {
282 continue;
283 }
284 in_iter++;
285 }
286 std::map<int64_t, int64_t>::iterator out_iter = out_accum_device_to_accum_shape_map.begin();
287 while (out_iter != out_accum_device_to_accum_shape_map.end()) {
288 if (out_accum_device_to_accum_shape_map.find(out_iter->first) == out_accum_device_to_accum_shape_map.end()) {
289 in_iter = in_accum_device_to_accum_shape_map.begin();
290 int64_t small = 1;
291 int64_t big = 1;
292 while (in_iter != in_accum_device_to_accum_shape_map.end()) {
293 if (in_iter->first < out_iter->first) {
294 small = in_iter->second;
295 } else if (in_iter->first > out_iter->first) {
296 big = in_iter->second;
297 break;
298 } else {
299 ASSERT_EQ(true, false);
300 }
301 in_iter++;
302 }
303 if (small == 1) {
304 ASSERT_EQ(true, false);
305 }
306 if (big == 1) {
307 ASSERT_EQ(true, false);
308 }
309 int64_t small_value = in_accum_device_to_accum_shape_map[small];
310 int64_t big_value = in_accum_device_to_accum_shape_map[big];
311 IsLinearValue(small, big, small_value, big_value, out_iter->first, out_iter->second);
312 }
313 out_iter++;
314 }
315 }
316
ValidLayoutChangeCheck(const DeviceArrangement & in_device_arrangement,const TensorMap & in_tensor_map,const TensorShape & in_tensor_shape,const DeviceArrangement & out_device_arrangement,const TensorMap & out_tensor_map,const TensorShape & out_tensor_shape)317 void ValidLayoutChangeCheck(const DeviceArrangement &in_device_arrangement, const TensorMap &in_tensor_map,
318 const TensorShape &in_tensor_shape, const DeviceArrangement &out_device_arrangement,
319 const TensorMap &out_tensor_map, const TensorShape &out_tensor_shape) {
320 LayoutTransferValidLayoutChangeCheck(in_device_arrangement, in_tensor_map, in_tensor_shape, out_device_arrangement,
321 out_tensor_map, out_tensor_shape);
322 }
323
324 } // namespace parallel
325 } // namespace mindspore
326