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