"doc/devel/sync.rst" did not exist on "32a74b8eb0183e6911e87292795f8a8968d214af"
common.hpp 12.8 KB
Newer Older
Po-Yen, Chen's avatar
Po-Yen, Chen committed
1
2
3
4
5
// SPDX-License-Identifier: MIT
// Copyright (c) 2018-2022, Advanced Micro Devices, Inc. All rights reserved.

#pragma once

6
#include <algorithm>
7
#include <cassert>
Po-Yen, Chen's avatar
Po-Yen, Chen committed
8
9
#include <cstddef>
#include <cstdlib>
10
#include <cstring>
Po-Yen, Chen's avatar
Po-Yen, Chen committed
11
#include <iostream>
Po-Yen, Chen's avatar
Po-Yen, Chen committed
12
13
#include <iterator>
#include <numeric>
14
#include <type_traits>
15
#include <utility>
Po-Yen, Chen's avatar
Po-Yen, Chen committed
16
17

#include "ck/ck.hpp"
18
#include "ck/tensor_operation/gpu/device/impl/device_permute_impl.hpp"
Po-Yen, Chen's avatar
Po-Yen, Chen committed
19
#include "ck/tensor_operation/gpu/element/binary_element_wise_operation.hpp"
20
#include "ck/utility/type.hpp"
Po-Yen, Chen's avatar
Po-Yen, Chen committed
21
22
23

#include "ck/library/utility/check_err.hpp"
#include "ck/library/utility/device_memory.hpp"
24
#include "ck/library/utility/fill.hpp"
Po-Yen, Chen's avatar
Po-Yen, Chen committed
25
26
27
#include "ck/library/utility/host_tensor.hpp"
#include "ck/library/utility/host_tensor_generator.hpp"

28
using F16 = ck::half_t;
29
using F32 = float;
30
using F64 = double;
31

Po-Yen, Chen's avatar
Po-Yen, Chen committed
32
33
struct Problem final
{
34
35
36
    static constexpr std::size_t NumDim = 3;

    using Shape = std::array<std::size_t, NumDim>;
37
38
39
40
41
42
43
44
45
46
47
    using Axes  = Shape;

    Problem() = delete;

    explicit Problem(const Shape& default_shape, const Axes& default_axes)
        : shape(default_shape), axes(default_axes)
    {
    }

    Shape shape;
    Axes axes;
Po-Yen, Chen's avatar
Po-Yen, Chen committed
48
49
};

50
51
52
53
54
template <ck::index_t... Is>
using S = ck::Sequence<Is...>;

using PassThrough = ck::tensor_operation::element_wise::PassThrough;

55
56
namespace detail {

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
template <typename Array, std::size_t Difference>
struct enlarge_array_size;

template <typename T, std::size_t Size, std::size_t Difference>
struct enlarge_array_size<std::array<T, Size>, Difference>
{
    using type = std::array<T, Size + Difference>;
};

template <typename Array, std::size_t Difference>
using enlarge_array_size_t = typename enlarge_array_size<Array, Difference>::type;

template <typename Array>
struct get_array_size;

template <typename T, std::size_t Size>
struct get_array_size<std::array<T, Size>> : std::integral_constant<std::size_t, Size>
{
};

template <typename Array>
inline constexpr std::size_t get_array_size_v = get_array_size<Array>::value;

80
81
82
83
84
85
86
87
88
template <typename T, typename = void>
struct is_iterator : std::false_type
{
};

template <typename T>
struct is_iterator<T,
                   std::void_t<decltype(*std::declval<T>()),
                               decltype(++std::declval<std::add_lvalue_reference_t<T>>()),
89
                               decltype(std::declval<std::add_lvalue_reference_t<T>>()++)>>
90
91
92
93
94
95
96
    : std::true_type
{
};

template <typename T>
inline constexpr bool is_iterator_v = is_iterator<T>::value;

97
98
99
100
101
102
struct Placeholder final
{
    template <typename T>
    constexpr inline operator T() const noexcept;
};

103
template <typename Iterator, typename = void>
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
struct is_output_iterator : std::false_type
{
};

template <typename Iterator>
struct is_output_iterator<
    Iterator,
    std::void_t<decltype(*std::declval<Iterator>() = std::declval<Placeholder>())>>
    : std::bool_constant<is_iterator_v<Iterator>>
{
};

template <typename T>
inline constexpr bool is_output_iterator_v = is_output_iterator<T>::value;

119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
template <typename Iterator, typename = void>
struct is_bidirectional_iterator : std::false_type
{
};

template <typename Iterator>
struct is_bidirectional_iterator<
    Iterator,
    std::void_t<decltype(--std::declval<std::add_lvalue_reference_t<Iterator>>()),
                decltype(std::declval<std::add_lvalue_reference_t<Iterator>>()--)>>
    : std::bool_constant<is_iterator_v<Iterator>>
{
};

template <typename Iterator>
inline constexpr bool is_bidirectional_iterator_v = is_bidirectional_iterator<Iterator>::value;

136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
template <typename Iterator, typename = void>
struct is_random_access_iterator : std::false_type
{
};

template <typename Iterator>
struct is_random_access_iterator<Iterator,
                                 std::void_t<decltype(std::declval<Iterator>() + 1),
                                             decltype(std::declval<Iterator>() - 1),
                                             decltype(std::declval<Iterator>()[1])>>
    : std::bool_constant<is_iterator_v<Iterator>>
{
};

template <typename Iterator>
inline constexpr bool is_random_access_iterator_v = is_random_access_iterator<Iterator>::value;

template <typename T, typename = void>
struct is_range : std::false_type
{
};

template <typename T>
struct is_range<T,
Po-Yen, Chen's avatar
Po-Yen, Chen committed
160
161
162
                std::void_t<decltype(begin(std::declval<T>())),
                            decltype(end(std::declval<T>())),
                            decltype(begin(std::declval<T>()) != end(std::declval<T>()))>>
163
164
165
166
167
168
169
    : std::bool_constant<is_iterator_v<ck::remove_cvref_t<decltype(begin(std::declval<T>()))>>>
{
};

template <typename T>
inline constexpr bool is_range_v = is_range<T>::value;

170
171
172
173
174
175
176
177
178
179
180
181
182
183
template <typename Range, typename = void>
struct is_sized_range : std::false_type
{
};

template <typename Range>
struct is_sized_range<Range, std::void_t<decltype(size(std::declval<Range>()))>>
    : std::bool_constant<is_range_v<Range>>
{
};

template <typename Range>
inline constexpr bool is_sized_range_v = is_sized_range<Range>::value;

184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
template <typename Range, typename = void>
struct is_bidirectional_range : std::false_type
{
};

template <typename Range>
struct is_bidirectional_range<Range, std::void_t<>>
    : std::bool_constant<
          is_range_v<Range> &&
          is_bidirectional_iterator_v<ck::remove_cvref_t<decltype(begin(std::declval<Range>()))>>>
{
};

template <typename Range>
inline constexpr bool is_bidirectional_range_v = is_bidirectional_range<Range>::value;

200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
template <typename Range, typename = void>
struct is_random_access_range : std::false_type
{
};

template <typename Range>
struct is_random_access_range<Range, std::void_t<>>
    : std::bool_constant<
          is_range_v<Range> &&
          is_random_access_iterator_v<ck::remove_cvref_t<decltype(begin(std::declval<Range>()))>>>
{
};

template <typename Range>
inline constexpr bool is_random_access_range_v = is_random_access_range<Range>::value;

216
217
218
template <typename Range>
class to_array_proxy
{
Po-Yen, Chen's avatar
Po-Yen, Chen committed
219
220
    static_assert(is_range_v<Range>);

221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
    public:
    explicit to_array_proxy(const Range& source) noexcept : source_(source) {}

    template <typename T, std::size_t Size>
    operator std::array<T, Size>() const
    {
        std::array<T, Size> destination;

        std::copy_n(std::begin(source_),
                    std::min<std::size_t>(Size, std::size(source_)),
                    std::begin(destination));

        return destination;
    }

    private:
    const Range& source_;
};

240
241
} // namespace detail

242
243
template <typename Range>
inline auto to_array(Range& range) noexcept
Po-Yen, Chen's avatar
Po-Yen, Chen committed
244
245
    -> std::enable_if_t<detail::is_range_v<Range>,
                        detail::to_array_proxy<ck::remove_cvref_t<Range>>>
246
247
248
249
{
    return detail::to_array_proxy<ck::remove_cvref_t<Range>>{range};
}

250
251
252
253
254
255
namespace ranges {
template <typename InputRange, typename OutputIterator>
inline auto copy(InputRange&& range, OutputIterator iter)
    -> decltype(std::copy(std::begin(std::forward<InputRange>(range)),
                          std::end(std::forward<InputRange>(range)),
                          iter))
256
{
257
258
259
    return std::copy(std::begin(std::forward<InputRange>(range)),
                     std::end(std::forward<InputRange>(range)),
                     iter);
260
}
261
} // namespace ranges
262

263
template <typename Axes>
264
265
inline auto is_valid_axes(const Axes& axes)
    -> std::enable_if_t<detail::is_random_access_range_v<Axes>, bool>
266
267
268
269
270
271
272
273
{
    using std::empty;
    if(empty(axes))
    {
        return false;
    }

    using std::begin, std::end;
274
    std::vector<std::size_t> sorted_axes(begin(axes), end(axes));
275

276
277
    std::sort(begin(sorted_axes), end(sorted_axes));
    const auto last = std::unique(begin(sorted_axes), end(sorted_axes));
278

279
280
    return (last == end(sorted_axes)) && (*begin(sorted_axes) == 0) &&
           (*std::prev(last) == size(axes) - 1);
281
282
}

283
template <typename Shape>
284
inline auto is_valid_shape(const Shape& shape) -> std::enable_if_t<detail::is_range_v<Shape>, bool>
285
{
286
287
    static_assert(std::is_unsigned_v<ck::remove_cvref_t<decltype(*std::begin(shape))>>);

288
289
290
291
292
293
    using std::begin, std::end;
    using std::empty;
    return !empty(shape) && std::all_of(begin(shape), end(shape), [](auto dim) { return 0 < dim; });
}

template <typename Shape, typename Indices>
294
295
inline auto is_valid_indices(const Shape& shape, const Indices& indices)
    -> std::enable_if_t<detail::is_sized_range_v<Shape> && detail::is_sized_range_v<Indices>, bool>
296
{
297
298
    static_assert(std::is_unsigned_v<ck::remove_cvref_t<decltype(*std::begin(indices))>>);

Po-Yen, Chen's avatar
Po-Yen, Chen committed
299
300
301
302
    if(!is_valid_shape(shape))
    {
        return false;
    }
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330

    using std::empty;
    if(empty(indices))
    {
        return false;
    }

    using std::size;
    if(size(shape) != size(indices))
    {
        return false;
    }

    using std::begin, std::end;

    auto dim = begin(shape);
    auto idx = begin(indices);
    for(; dim != end(shape) && idx != end(indices); ++dim, ++idx)
    {
        if(*dim <= *idx)
        {
            return false;
        }
    }

    return true;
}

331
332
333
template <std::size_t Size>
std::array<std::size_t, Size> transpose(const std::array<std::size_t, Size>& shape,
                                        const std::array<std::size_t, Size>& axes)
334
335
336
{
    assert(is_valid_shape(shape) && is_valid_axes(axes));

337
338
    std::array<std::size_t, Size> transposed;
    auto iter = std::begin(transposed);
339
340
341
342
343
    for(const auto axis : axes)
    {
        *iter++ = shape[axis];
    }

344
    return transposed;
345
346
}

347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
auto extend_shape(const Problem::Shape& shape, std::size_t new_dim)
{
    detail::enlarge_array_size_t<Problem::Shape, 1> extended_shape;

    using std::begin, std::end;

    std::copy(begin(shape), end(shape), begin(extended_shape));
    extended_shape.back() = new_dim;

    return extended_shape;
}

auto extend_axes(const Problem::Axes& axes)
{
    detail::enlarge_array_size_t<Problem::Axes, 1> extended_axes;

    using std::begin, std::end;

    std::copy(begin(axes), end(axes), begin(extended_axes));
    extended_axes.back() = detail::get_array_size_v<Problem::Axes>;

    return extended_axes;
}

371
template <typename Shape, typename Indices>
372
373
374
375
auto advance_indices(const Shape& shape, Indices& indices) -> std::enable_if_t<
    detail::is_bidirectional_range_v<Shape> && detail::is_sized_range_v<Shape> &&
        detail::is_bidirectional_range_v<Indices> && detail::is_sized_range_v<Indices>,
    bool>
376
{
377
    using std::size;
Po-Yen, Chen's avatar
Po-Yen, Chen committed
378
379
380
381
    if(!(is_valid_shape(shape) && is_valid_indices(shape, indices) && size(shape) == size(indices)))
    {
        return false;
    }
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396

    bool carry = true;

    using std::rbegin, std::rend;
    auto dim = rbegin(shape);
    auto idx = rbegin(indices);
    for(; carry && dim != rend(shape) && idx != rend(indices); ++dim, ++idx)
    {
        *idx  = (*idx + carry);
        carry = ((*idx == *dim) ? (*idx = 0, true) : false);
    }

    return !carry;
}

Po-Yen, Chen's avatar
Po-Yen, Chen committed
397
template <typename Src, typename Axes, typename Functor, typename Dest>
398
399
400
401
402
403
auto host_permute(const Tensor<Src>& src, const Axes& axes, Functor functor, Tensor<Dest>& dest)
    -> std::enable_if_t<detail::is_random_access_range_v<Axes> && detail::is_sized_range_v<Axes> &&
                            std::is_invocable_v<Functor,
                                                std::add_lvalue_reference_t<Dest>,
                                                std::add_lvalue_reference_t<Src>>,
                        bool>
404
405
406
{
    const auto& shape            = src.mDesc.GetLengths();
    const auto& transposed_shape = dest.mDesc.GetLengths();
Po-Yen, Chen's avatar
Po-Yen, Chen committed
407
408
409
410
411
412
    if(!(is_valid_shape(shape) && is_valid_shape(transposed_shape)))
    {
        return false;
    }

    using std::size;
413
    if(!is_valid_axes(axes))
Po-Yen, Chen's avatar
Po-Yen, Chen committed
414
415
416
    {
        return false;
    }
417
418
419
420

    static_assert(detail::is_sized_range_v<ck::remove_cvref_t<decltype(shape)>> &&
                  detail::is_sized_range_v<ck::remove_cvref_t<decltype(transposed_shape)>>);

421
    if(size(shape) != size(transposed_shape))
Po-Yen, Chen's avatar
Po-Yen, Chen committed
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
    {
        return false;
    }

    static_assert(detail::is_random_access_range_v<ck::remove_cvref_t<decltype(shape)>> &&
                  detail::is_random_access_range_v<ck::remove_cvref_t<decltype(transposed_shape)>>);
    {
        for(std::size_t idx = 0; idx < size(shape); ++idx)
        {
            if(transposed_shape[idx] != shape[axes[idx]])
            {
                return false;
            }
        }
    }
437

438
    std::vector<std::size_t> indices(size(shape), 0);
Po-Yen, Chen's avatar
Po-Yen, Chen committed
439
440
441
442
    if(!is_valid_indices(shape, indices))
    {
        return false;
    }
443

444
    switch(size(shape))
445
    {
446
    case 3: {
447
448
449
450
451
452
453
        do
        {
            Dest output = 0;
            functor(output, src(indices[0], indices[1], indices[2]));
            dest(indices[axes[0]], indices[axes[1]], indices[axes[2]]) = output;
        } while(advance_indices(shape, indices));
    }
454
455
    break;
    case 4: {
456
457
458
459
460
461
462
        do
        {
            Dest output = 0;
            functor(output, src(indices[0], indices[1], indices[2], indices[3]));
            dest(indices[axes[0]], indices[axes[1]], indices[axes[2]], indices[axes[3]]) = output;
        } while(advance_indices(shape, indices));
    }
463
464
    break;
    default: return false;
465
    }
Po-Yen, Chen's avatar
Po-Yen, Chen committed
466
467

    return true;
468
}