1 #ifndef VEXCL_MULTIVECTOR_HPP
2 #define VEXCL_MULTIVECTOR_HPP
3
4 /*
5 The MIT License
6
7 Copyright (c) 2012-2018 Denis Demidov <dennis.demidov@gmail.com>
8
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 */
27
28 /**
29 * \file vexcl/multivector.hpp
30 * \author Denis Demidov <dennis.demidov@gmail.com>
31 * \brief OpenCL device multi-vector.
32 */
33
34 #include <array>
35 #include <vector>
36 #include <map>
37 #include <iostream>
38 #include <sstream>
39 #include <string>
40 #include <type_traits>
41 #include <boost/proto/proto.hpp>
42 #include <boost/iterator/iterator_facade.hpp>
43
44 #include <vexcl/util.hpp>
45 #include <vexcl/operations.hpp>
46 #include <vexcl/vector.hpp>
47
48 /// Vector expression template library for OpenCL.
49 namespace vex {
50
51 struct multivector_terminal {};
52
53 template <typename T, size_t N> class multivector;
54
55 namespace traits {
56
57 // Extract component directly from terminal rather than from value(terminal):
58 template <>
59 struct proto_terminal_is_value< multivector_terminal >
60 : std::true_type
61 { };
62
63 template <>
64 struct is_multivector_expr_terminal< multivector_terminal >
65 : std::true_type
66 { };
67
68 // Hold multivector terminals by reference:
69 template <class T>
70 struct hold_terminal_by_reference< T,
71 typename std::enable_if<
72 boost::proto::matches<
73 typename boost::proto::result_of::as_expr< T >::type,
74 boost::proto::terminal< multivector_terminal >
75 >::value
76 >::type
77 >
78 : std::true_type
79 { };
80
81 template <typename T, size_t N>
82 struct number_of_components< multivector<T, N> > : boost::mpl::size_t<N> {};
83
84 template <size_t I, typename T, size_t N>
85 struct component< I, multivector<T, N> > {
86 typedef const vector<T>& type;
87 };
88
89 } // namespace traits
90
91 template <size_t I, typename T, size_t N>
get(const multivector<T,N> & mv)92 const vector<T>& get(const multivector<T, N> &mv) {
93 static_assert(I < N, "Component number out of bounds");
94
95 return mv(I);
96 }
97
98 template <size_t I, typename T, size_t N>
get(multivector<T,N> & mv)99 vector<T>& get(multivector<T, N> &mv) {
100 static_assert(I < N, "Component number out of bounds");
101
102 return mv(I);
103 }
104
105 namespace detail {
106
107 template <class OP, class LHS, class RHS>
108 void assign_multiexpression(LHS &lhs, const RHS &rhs,
109 const std::vector<backend::command_queue> &queue,
110 const std::vector<size_t> &part
111 );
112 }
113
114
115 typedef multivector_expression<
116 typename boost::proto::terminal< multivector_terminal >::type
117 > multivector_terminal_expression;
118
119 /// Container for several equally sized instances of vex::vector<T>.
120 template <typename T, size_t N>
121 class multivector : public multivector_terminal_expression {
122 public:
123 typedef vex::vector<T> subtype;
124 typedef std::array<T,N> value_type;
125 typedef T sub_value_type;
126
127 const static size_t NDIM = N;
128
129 // Proxy class.
130 class element {
131 public:
operator const value_type() const132 operator const value_type () const {
133 value_type val;
134 for(unsigned i = 0; i < N; i++) val[i] = vec(i)[index];
135 return val;
136 }
137
operator =(value_type val)138 const value_type operator=(value_type val) {
139 for(unsigned i = 0; i < N; i++) vec(i)[index] = val[i];
140 return val;
141 }
142 private:
element(multivector & vec,size_t index)143 element(multivector &vec, size_t index)
144 : vec(vec), index(index) {}
145
146 multivector &vec;
147 const size_t index;
148
149 friend class multivector;
150 };
151
152 // Proxy class.
153 class const_element {
154 public:
operator const value_type() const155 operator const value_type () const {
156 value_type val;
157 for(unsigned i = 0; i < N; i++) val[i] = vec(i)[index];
158 return val;
159 }
160 private:
const_element(const multivector & vec,size_t index)161 const_element(const multivector &vec, size_t index)
162 : vec(vec), index(index) {}
163
164 const multivector &vec;
165 const size_t index;
166
167 friend class multivector;
168 };
169
170 template <class V, class E>
171 class iterator_type
172 : public boost::iterator_facade<
173 iterator_type<V, E>,
174 sub_value_type,
175 std::random_access_iterator_tag,
176 E
177 >
178 {
179 public:
180 typedef boost::iterator_facade<
181 iterator_type<V, E>,
182 sub_value_type,
183 std::random_access_iterator_tag,
184 E
185 > super_type;
186 typedef typename super_type::reference reference;
187 typedef typename super_type::difference_type difference_type;
188
dereference() const189 reference dereference() const {
190 return E(vec, pos);
191 }
192
equal(const iterator_type & it) const193 bool equal(const iterator_type &it) const {
194 return pos == it.pos;
195 }
196
increment()197 void increment() {
198 ++pos;
199 }
200
decrement()201 void decrement() {
202 --pos;
203 }
204
advance(difference_type n)205 void advance(difference_type n) {
206 pos += n;
207 }
208
distance_to(const iterator_type & it) const209 difference_type distance_to(const iterator_type &it) const {
210 return static_cast<difference_type>(it.pos - pos);
211 }
212 private:
iterator_type(V & vec,size_t pos)213 iterator_type(V &vec, size_t pos) : vec(vec), pos(pos) {}
214
215 V &vec;
216 size_t pos;
217
218 friend class multivector;
219 };
220
221 typedef iterator_type<multivector, element> iterator;
222 typedef iterator_type<const multivector, const_element> const_iterator;
223
multivector()224 multivector() {};
225
226 /// Constructor.
227 /**
228 * The host vector data is divided equally between the created
229 * multivector components. Each component gets continuous chunk of the
230 * source vector.
231 */
multivector(const std::vector<backend::command_queue> & queue,const std::vector<T> & host,backend::mem_flags flags=backend::MEM_READ_WRITE)232 multivector(const std::vector<backend::command_queue> &queue,
233 const std::vector<T> &host,
234 backend::mem_flags flags = backend::MEM_READ_WRITE)
235 {
236 static_assert(N > 0, "What's the point?");
237
238 size_t size = host.size() / N;
239 assert(N * size == host.size());
240
241 for(size_t i = 0; i < N; ++i)
242 vec[i].resize(queue, size, host.data() + i * size, flags);
243 }
244
245 /// Constructor.
246 /**
247 * If host pointer is not NULL, it is copied to the underlying vector
248 * components of the multivector. Each component gets continuous chunk
249 * of the source vector.
250 */
multivector(const std::vector<backend::command_queue> & queue,size_t size,const T * host=0,backend::mem_flags flags=backend::MEM_READ_WRITE)251 multivector(const std::vector<backend::command_queue> &queue, size_t size,
252 const T *host = 0, backend::mem_flags flags = backend::MEM_READ_WRITE)
253 {
254 static_assert(N > 0, "What's the point?");
255
256 for(size_t i = 0; i < N; ++i)
257 vec[i].resize(queue, size, host ? host + i * size : 0, flags);
258 }
259
260 /// Constructor.
261 /**
262 * Uses the most recently created VexCL context.
263 */
multivector(size_t size)264 multivector(size_t size) {
265 static_assert(N > 0, "What's the point?");
266
267 for(size_t i = 0; i < N; ++i) vec[i].resize(size);
268 }
269
270 #ifdef VEXCL_NO_COPY_CONSTRUCTORS
271 private:
272 #endif
273 /// Copy constructor.
multivector(const multivector & mv)274 multivector(const multivector &mv) {
275 #ifdef VEXCL_SHOW_COPIES
276 std::cout << "Copying vex::multivector<" << type_name<T>()
277 << ", " << N << "> of size " << size() << std::endl;
278 #endif
279 for(size_t i = 0; i < N; ++i) vec[i].resize(mv(i));
280 }
281 #ifdef VEXCL_NO_COPY_CONSTRUCTORS
282 public:
283 #endif
284
285 /// Move constructor.
multivector(multivector && mv)286 multivector(multivector &&mv) noexcept {
287 for(size_t i = 0; i < N; ++i) vec[i].swap(mv.vec[i]);
288 }
289
290 /// Resizes the multivector.
291 /**
292 * This is equivalent to reconstructing the vector with the given
293 * parameters. Any data contained in the resized vector will be lost
294 * as a result.
295 */
resize(const std::vector<backend::command_queue> & queue,size_t size)296 void resize(const std::vector<backend::command_queue> &queue, size_t size) {
297 for(unsigned i = 0; i < N; i++) vec[i].resize(queue, size);
298 }
299
300 /// Resizes the multivector.
301 /**
302 * Uses the most recently created VexCL context.
303 * This is equivalent to reconstructing the vector with the given
304 * parameters. Any data contained in the resized vector will be lost
305 * as a result.
306 */
resize(size_t size)307 void resize(size_t size) {
308 for(unsigned i = 0; i < N; i++) vec[i].resize(size);
309 }
310
311 /// Fills the multivector with zeros.
clear()312 void clear() {
313 *this = static_cast<T>(0);
314 }
315
316 /// Returns size of the multivector (equals size of individual components).
size() const317 size_t size() const {
318 return vec[0].size();
319 }
320
321 /// Returns i-th multivector component.
operator ()(size_t i) const322 const vex::vector<T>& operator()(size_t i) const {
323 return vec[i];
324 }
325
326 /// Returns i-th multivector component.
operator ()(size_t i)327 vex::vector<T>& operator()(size_t i) {
328 return vec[i];
329 }
330
331 /// Returns const iterator to the first element of the multivector.
begin() const332 const_iterator begin() const {
333 return const_iterator(*this, 0);
334 }
335
336 /// Returns const iterator to the first element of the multivector.
begin()337 iterator begin() {
338 return iterator(*this, 0);
339 }
340
341 /// Returns const iterator referring to the past-the-end element in the multivector.
end() const342 const_iterator end() const {
343 return const_iterator(*this, size());
344 }
345
346 /// Returns iterator referring to the past-the-end element in the multivector.
end()347 iterator end() {
348 return iterator(*this, size());
349 }
350
351 /// Returns i-th elements of all components packed in a std::array<T,N>.
operator [](size_t i) const352 const_element operator[](size_t i) const {
353 return const_element(*this, i);
354 }
355
356 /// Assigns values from std::array<T,N> to i-th elements of all components.
operator [](size_t i)357 element operator[](size_t i) {
358 return element(*this, i);
359 }
360
361 /// Returns reference to the multivector's queue list.
queue_list() const362 const std::vector<backend::command_queue>& queue_list() const {
363 return vec[0].queue_list();
364 }
365
366 /// Assignment operator
operator =(const multivector & mv)367 const multivector& operator=(const multivector &mv) {
368 if (this != &mv)
369 detail::assign_multiexpression<assign::SET>(
370 *this, mv, vec[0].queue_list(), vec[0].partition());
371 return *this;
372 }
373
374 #define VEXCL_ASSIGNMENT(op, op_type) \
375 /** Assignment operator */ \
376 template <class Expr> \
377 auto operator op(const Expr &expr) -> \
378 typename std::enable_if< \
379 boost::proto::matches< \
380 typename boost::proto::result_of::as_expr<Expr>::type, \
381 multivector_expr_grammar>::value || \
382 is_tuple<Expr>::value, \
383 const multivector &>::type \
384 { \
385 detail::assign_multiexpression<op_type>(*this, expr, vec[0].queue_list(), \
386 vec[0].partition()); \
387 return *this; \
388 }
389
VEXCL_ASSIGNMENTS(VEXCL_ASSIGNMENT)390 VEXCL_ASSIGNMENTS(VEXCL_ASSIGNMENT)
391
392 #undef VEXCL_ASSIGNMENT
393
394 #ifndef DOXYGEN
395 template <class Expr>
396 typename std::enable_if<
397 boost::proto::matches<
398 typename boost::proto::result_of::as_expr<Expr>::type,
399 additive_multivector_transform_grammar
400 >::value,
401 const multivector&
402 >::type
403 operator=(const Expr &expr) {
404 detail::apply_additive_transform</*append=*/false>(*this,
405 detail::simplify_additive_transform()( expr ));
406 return *this;
407 }
408
409 template <class Expr>
410 typename std::enable_if<
411 boost::proto::matches<
412 typename boost::proto::result_of::as_expr<Expr>::type,
413 additive_multivector_transform_grammar
414 >::value,
415 const multivector&
416 >::type
operator +=(const Expr & expr)417 operator+=(const Expr &expr) {
418 detail::apply_additive_transform</*append=*/true>(*this,
419 detail::simplify_additive_transform()( expr ));
420 return *this;
421 }
422
423 template <class Expr>
424 typename std::enable_if<
425 boost::proto::matches<
426 typename boost::proto::result_of::as_expr<Expr>::type,
427 additive_multivector_transform_grammar
428 >::value,
429 const multivector&
430 >::type
operator -=(const Expr & expr)431 operator-=(const Expr &expr) {
432 detail::apply_additive_transform</*append=*/true>(*this,
433 detail::simplify_additive_transform()( -expr ));
434 return *this;
435 }
436
437 template <class Expr>
438 typename std::enable_if<
439 boost::proto::matches<
440 typename boost::proto::result_of::as_expr<Expr>::type,
441 multivector_full_grammar
442 >::value &&
443 !boost::proto::matches<
444 typename boost::proto::result_of::as_expr<Expr>::type,
445 multivector_expr_grammar
446 >::value &&
447 !boost::proto::matches<
448 typename boost::proto::result_of::as_expr<Expr>::type,
449 additive_multivector_transform_grammar
450 >::value,
451 const multivector&
452 >::type
operator =(const Expr & expr)453 operator=(const Expr &expr) {
454 *this = detail::extract_multivector_expressions()( expr );
455 *this += detail::extract_additive_multivector_transforms()( expr );
456
457 return *this;
458 }
459
460 template <class Expr>
461 typename std::enable_if<
462 boost::proto::matches<
463 typename boost::proto::result_of::as_expr<Expr>::type,
464 multivector_full_grammar
465 >::value &&
466 !boost::proto::matches<
467 typename boost::proto::result_of::as_expr<Expr>::type,
468 multivector_expr_grammar
469 >::value &&
470 !boost::proto::matches<
471 typename boost::proto::result_of::as_expr<Expr>::type,
472 additive_multivector_transform_grammar
473 >::value,
474 const multivector&
475 >::type
operator +=(const Expr & expr)476 operator+=(const Expr &expr) {
477 *this += detail::extract_multivector_expressions()( expr );
478 *this += detail::extract_additive_multivector_transforms()( expr );
479
480 return *this;
481 }
482
483 template <class Expr>
484 typename std::enable_if<
485 boost::proto::matches<
486 typename boost::proto::result_of::as_expr<Expr>::type,
487 multivector_full_grammar
488 >::value &&
489 !boost::proto::matches<
490 typename boost::proto::result_of::as_expr<Expr>::type,
491 multivector_expr_grammar
492 >::value &&
493 !boost::proto::matches<
494 typename boost::proto::result_of::as_expr<Expr>::type,
495 additive_multivector_transform_grammar
496 >::value,
497 const multivector&
498 >::type
operator -=(const Expr & expr)499 operator-=(const Expr &expr) {
500 *this -= detail::extract_multivector_expressions()( expr );
501 *this -= detail::extract_additive_multivector_transforms()( expr );
502
503 return *this;
504 }
505 #endif
506
507 private:
508 std::array<vector<T>,N> vec;
509 };
510
511 /// Copy multivector to host vector.
512 template <class T, size_t N>
copy(const multivector<T,N> & mv,std::vector<T> & hv)513 void copy(const multivector<T,N> &mv, std::vector<T> &hv) {
514 for(size_t i = 0; i < N; ++i)
515 vex::copy(mv(i).begin(), mv(i).end(), hv.begin() + i * mv.size());
516 }
517
518 /// Copy host vector to multivector.
519 template <class T, size_t N>
copy(const std::vector<T> & hv,multivector<T,N> & mv)520 void copy(const std::vector<T> &hv, multivector<T,N> &mv) {
521 for(size_t i = 0; i < N; ++i)
522 vex::copy(hv.begin() + i * mv.size(), hv.begin() + (i + 1) * mv.size(),
523 mv(i).begin());
524 }
525
526 /// Download and print the vector elements.
527 template<class T, size_t N>
operator <<(std::ostream & o,const vex::multivector<T,N> & t)528 std::ostream &operator<<(std::ostream &o, const vex::multivector<T, N> &t) {
529 boost::io::ios_all_saver stream_state(o);
530 const size_t n = t.size();
531 const size_t chunk = std::max<int>(1, (std::is_integral<T>::value ? 12 : 6) / N);
532
533 std::vector<T> data(n * N);
534 copy(t, data);
535
536 o << "{" << std::setprecision(6);
537 for(size_t i = 0 ; i < n; i++) {
538 if (i % chunk == 0) o << "\n" << std::setw(6) << i << ":";
539
540 std::cout << " (";
541 for(size_t j = 0; j < N; ++j) {
542 if (std::is_integral<T>::value)
543 o << " " << std::setw(6) << data[j * n + i];
544 else if (std::is_arithmetic<T>::value)
545 o << std::scientific << std::setw(14) << data[j * n + i];
546 else
547 o << " " << data[j * n + i];
548 }
549 std::cout << ")";
550 }
551 return o << "\n}\n";
552 }
553
554 } // namespace vex
555
556 namespace boost { namespace fusion { namespace traits {
557
558 template <class T, size_t N>
559 struct is_sequence< vex::multivector<T, N> > : std::false_type
560 {};
561
562 } } }
563
564
565 #endif
566