vector_expression.hpp
Go to the documentation of this file.
1 /*!
2  * \brief expression templates for vector valued math
3  *
4  * \author O. Krause
5  * \date 2013
6  *
7  *
8  * \par Copyright 1995-2015 Shark Development Team
9  *
10  * <BR><HR>
11  * This file is part of Shark.
12  * <http://image.diku.dk/shark/>
13  *
14  * Shark is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU Lesser General Public License as published
16  * by the Free Software Foundation, either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * Shark is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  * GNU Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public License
25  * along with Shark. If not, see <http://www.gnu.org/licenses/>.
26  *
27  */
28 #ifndef REMORA_VECTOR_EXPRESSION_HPP
29 #define REMORA_VECTOR_EXPRESSION_HPP
30 
31 #include "detail/expression_optimizers.hpp"
32 #include "kernels/dot.hpp"
33 #include "kernels/vector_fold.hpp"
34 #include "kernels/vector_max.hpp"
35 
36 namespace remora{
37 
38 template<class T, class VecV, class Device>
39 typename std::enable_if<
40  std::is_convertible<T, typename VecV::value_type >::value,
41  vector_scalar_multiply<VecV>
42 >::type
43 operator* (vector_expression<VecV, Device> const& v, T scalar){
44  typedef typename VecV::value_type value_type;
45  return vector_scalar_multiply<VecV>(v(), value_type(scalar));
46 }
47 template<class T, class VecV, class Device>
48 typename std::enable_if<
49  std::is_convertible<T, typename VecV::value_type >::value,
50  vector_scalar_multiply<VecV>
51 >::type
52 operator* (T scalar, vector_expression<VecV, Device> const& v){
53  typedef typename VecV::value_type value_type;
54  return vector_scalar_multiply<VecV>(v(), value_type(scalar));//explicit cast prevents warning, alternative would be to template functors::scalar_multiply on T as well
55 }
56 
57 template<class VecV, class Device>
58 vector_scalar_multiply<VecV> operator-(vector_expression<VecV, Device> const& v){
59  typedef typename VecV::value_type value_type;
60  return vector_scalar_multiply<VecV>(v(), value_type(-1));//explicit cast prevents warning, alternative would be to template functors::scalar_multiply on T as well
61 }
62 
63 ///\brief Creates a vector having a constant value.
64 ///
65 ///@param scalar the value which is repeated
66 ///@param elements the size of the resulting vector
67 template<class T>
68 typename std::enable_if<std::is_arithmetic<T>::value, scalar_vector<T, cpu_tag> >::type
69 repeat(T scalar, std::size_t elements){
70  return scalar_vector<T, cpu_tag>(elements,scalar);
71 }
72 
73 
74 #define REMORA_UNARY_VECTOR_TRANSFORMATION(name, F)\
75 template<class VecV, class Device>\
76 vector_unary<VecV,typename device_traits<Device>:: template F<typename VecV::value_type> >\
77 name(vector_expression<VecV, Device> const& v){\
78  typedef typename device_traits<Device>:: template F<typename VecV::value_type> functor_type;\
79  return vector_unary<VecV, functor_type >(v(), functor_type());\
80 }
99 #undef REMORA_UNARY_VECTOR_TRANSFORMATION
100 
101 ///\brief Adds two vectors
102 template<class VecV1, class VecV2, class Device>
103 vector_addition<VecV1, VecV2 > operator+ (
104  vector_expression<VecV1, Device> const& v1,
105  vector_expression<VecV2, Device> const& v2
106 ){
107  REMORA_SIZE_CHECK(v1().size() == v2().size());
108  return vector_addition<VecV1, VecV2>(v1(),v2());
109 }
110 ///\brief Subtracts two vectors
111 template<class VecV1, class VecV2, class Device>
112 vector_addition<VecV1, vector_scalar_multiply<VecV2> > operator- (
113  vector_expression<VecV1, Device> const& v1,
114  vector_expression<VecV2, Device> const& v2
115 ){
116  REMORA_SIZE_CHECK(v1().size() == v2().size());
117  return vector_addition<VecV1, vector_scalar_multiply<VecV2> >(v1(),-v2());
118 }
119 
120 ///\brief Adds a vector plus a scalar which is interpreted as a constant vector
121 template<class VecV, class T, class Device>
122 typename std::enable_if<
123  std::is_convertible<T, typename VecV::value_type>::value,
124  vector_addition<VecV, scalar_vector<T, Device> >
125 >::type operator+ (
126  vector_expression<VecV, Device> const& v,
127  T t
128 ){
129  return v + scalar_vector<T, Device>(v().size(),t);
130 }
131 
132 ///\brief Adds a vector plus a scalar which is interpreted as a constant vector
133 template<class T, class VecV, class Device>
134 typename std::enable_if<
135  std::is_convertible<T, typename VecV::value_type>::value,
136  vector_addition<VecV, scalar_vector<T, Device> >
137 >::type operator+ (
138  T t,
139  vector_expression<VecV, Device> const& v
140 ){
141  return v + scalar_vector<T, Device>(v().size(),t);
142 }
143 
144 ///\brief Subtracts a scalar which is interpreted as a constant vector.
145 template<class VecV, class T, class Device>
146 typename std::enable_if<
147  std::is_convertible<T, typename VecV::value_type>::value ,
148  vector_addition<VecV, vector_scalar_multiply<scalar_vector<T, Device> > >
149 >::type operator- (
150  vector_expression<VecV, Device> const& v,
151  T t
152 ){
153  return v - scalar_vector<T, Device>(v().size(),t);
154 }
155 
156 ///\brief Subtracts a vector from a scalar which is interpreted as a constant vector
157 template<class VecV, class T, class Device>
158 typename std::enable_if<
159  std::is_convertible<T, typename VecV::value_type>::value,
160  vector_addition<scalar_vector<T, Device>, vector_scalar_multiply<VecV> >
161 >::type operator- (
162  T t,
163  vector_expression<VecV, Device> const& v
164 ){
165  return scalar_vector<T, Device>(v().size(),t) - v;
166 }
167 
168 #define REMORA_BINARY_VECTOR_EXPRESSION(name, F)\
169 template<class VecV1, class VecV2, class Device>\
170 vector_binary<VecV1, VecV2, typename device_traits<Device>:: template F<typename common_value_type<VecV1,VecV2>::type> >\
171 name(vector_expression<VecV1, Device> const& v1, vector_expression<VecV2, Device> const& v2){\
172  REMORA_SIZE_CHECK(v1().size() == v2().size());\
173  typedef typename common_value_type<VecV1,VecV2>::type type;\
174  typedef typename device_traits<Device>:: template F<type> functor_type;\
175  return vector_binary<VecV1, VecV2, functor_type >(v1(),v2(), functor_type());\
176 }
177 REMORA_BINARY_VECTOR_EXPRESSION(operator*, multiply)
178 REMORA_BINARY_VECTOR_EXPRESSION(element_prod, multiply)
179 REMORA_BINARY_VECTOR_EXPRESSION(operator/, divide)
180 REMORA_BINARY_VECTOR_EXPRESSION(element_div, divide)
184 #undef REMORA_BINARY_VECTOR_EXPRESSION
185 
186 
187 //operations of the form op(v,t)[i] = op(v[i],t)
188 #define REMORA_VECTOR_SCALAR_TRANSFORMATION(name, F)\
189 template<class T, class VecV, class Device> \
190 typename std::enable_if< \
191  std::is_convertible<T, typename VecV::value_type >::value,\
192  vector_binary<VecV, scalar_vector<typename VecV::value_type, Device>, \
193  typename device_traits<Device>:: template F<typename VecV::value_type> > \
194 >::type \
195 name (vector_expression<VecV, Device> const& v, T t){ \
196  typedef typename VecV::value_type type;\
197  typedef typename device_traits<Device>:: template F<type> functor_type;\
198  return vector_binary<VecV, scalar_vector<type, Device>, functor_type >(v(), scalar_vector<type, Device>(v().size(),(type)t), functor_type()); \
199 }
200 REMORA_VECTOR_SCALAR_TRANSFORMATION(operator/, divide)
202 REMORA_VECTOR_SCALAR_TRANSFORMATION(operator<=, less_equal)
203 REMORA_VECTOR_SCALAR_TRANSFORMATION(operator>, greater)
204 REMORA_VECTOR_SCALAR_TRANSFORMATION(operator>=, greater_equal)
205 REMORA_VECTOR_SCALAR_TRANSFORMATION(operator==, equal)
206 REMORA_VECTOR_SCALAR_TRANSFORMATION(operator!=, not_equal)
210 #undef REMORA_VECTOR_SCALAR_TRANSFORMATION
211 
212 // operations of the form op(t,v)[i] = op(t,v[i])
213 #define REMORA_VECTOR_SCALAR_TRANSFORMATION_2(name, F)\
214 template<class T, class VecV, class Device> \
215 typename std::enable_if< \
216  std::is_convertible<T, typename VecV::value_type >::value,\
217  vector_binary<scalar_vector<typename VecV::value_type, Device>, VecV, typename device_traits<Device>:: template F<typename VecV::value_type> > \
218 >::type \
219 name (T t, vector_expression<VecV, Device> const& v){ \
220  typedef typename VecV::value_type type;\
221  typedef typename device_traits<Device>:: template F<type> functor_type;\
222  return vector_binary<scalar_vector<T, Device>, VecV, functor_type >(scalar_vector<T, Device>(v().size(),t), v() ,functor_type()); \
223 }
226 #undef REMORA_VECTOR_SCALAR_TRANSFORMATION_2
227 
228 template<class VecV1, class VecV2, class Device>
229 vector_binary<VecV1, VecV2,
230  typename device_traits<Device>:: template safe_divide<typename common_value_type<VecV1,VecV2>::type >
231 >
232 safe_div(
233  vector_expression<VecV1, Device> const& v1,
234  vector_expression<VecV2, Device> const& v2,
235  typename common_value_type<VecV1,VecV2>::type defaultValue
236 ){
237  REMORA_SIZE_CHECK(v1().size() == v2().size());
238  typedef typename common_value_type<VecV1,VecV2>::type result_type;
239 
240  typedef typename device_traits<Device>:: template safe_divide<result_type> functor_type;
241  return vector_binary<VecV1, VecV2, functor_type>(v1(),v2(), functor_type(defaultValue));
242 }
243 
244 /////VECTOR REDUCTIONS
245 
246 /// \brief sum v = sum_i v_i
247 template<class VecV, class Device>
248 typename VecV::value_type
249 sum(vector_expression<VecV, Device> const& v) {
250  typedef typename VecV::value_type value_type;
251  typedef typename device_traits<Device>:: template add<value_type> functor_type;
252  auto const& elem_result = eval_block(v);
253  value_type result = 0;
254  kernels::vector_fold<functor_type>(elem_result,result);
255  return result;
256 }
257 
258 /// \brief max v = max_i v_i
259 template<class VecV, class Device>
260 typename VecV::value_type
261 max(vector_expression<VecV, Device> const& v) {
262  typedef typename VecV::value_type value_type;
263  typedef typename device_traits<Device>:: template max<value_type> functor_type;
264  auto const& elem_result = eval_block(v);
265  value_type result = std::numeric_limits<value_type>::lowest();
266  kernels::vector_fold<functor_type>(elem_result,result);
267  return result;
268 }
269 
270 /// \brief min v = min_i v_i
271 template<class VecV, class Device>
272 typename VecV::value_type
273 min(vector_expression<VecV, Device> const& v) {
274  typedef typename VecV::value_type value_type;
275  typedef typename device_traits<Device>:: template min<value_type> functor_type;
276  auto const& elem_result = eval_block(v);
277  value_type result = std::numeric_limits<value_type>::max();
278  kernels::vector_fold<functor_type>(elem_result,result);
279  return result;
280 }
281 
282 /// \brief arg_max v = arg max_i v_i
283 template<class VecV, class Device>
284 std::size_t arg_max(vector_expression<VecV, Device> const& v) {
285  REMORA_SIZE_CHECK(v().size() > 0);
286  auto const& elem_result = eval_block(v);
287  return kernels::vector_max(elem_result);
288 }
289 
290 /// \brief arg_min v = arg min_i v_i
291 template<class VecV, class Device>
292 std::size_t arg_min(vector_expression<VecV, Device> const& v) {
293  REMORA_SIZE_CHECK(v().size() > 0);
294  return arg_max(-v);
295 }
296 
297 /// \brief soft_max v = ln(sum(exp(v)))
298 ///
299 /// Be aware that this is NOT the same function as used in machine learning: exp(v)/sum(exp(v))
300 ///
301 /// The function is computed in an numerically stable way to prevent that too high values of v_i produce inf or nan.
302 /// The name of the function comes from the fact that it behaves like a continuous version of max in the respect that soft_max v <= v.size()*max(v)
303 /// max is reached in the limit as the gap between the biggest value and the rest grows to infinity.
304 template<class VecV, class Device>
305 typename VecV::value_type
306 soft_max(vector_expression<VecV, Device> const& v) {
307  typename VecV::value_type maximum = max(v);
308  using std::log;
309  return log(sum(exp(v - maximum))) + maximum;
310 }
311 
312 
313 ////implement all the norms based on sum!
314 
315 /// \brief norm_1 v = sum_i |v_i|
316 template<class VecV, class Device>
317 typename real_traits<typename VecV::value_type >::type
318 norm_1(vector_expression<VecV, Device> const& v) {
319  return sum(abs(eval_block(v)));
320 }
321 
322 /// \brief norm_2 v = sum_i |v_i|^2
323 template<class VecV, class Device>
324 typename real_traits<typename VecV::value_type >::type
325 norm_sqr(vector_expression<VecV, Device> const& v) {
326  return sum(sqr(eval_block(v)));
327 }
328 
329 /// \brief norm_2 v = sqrt (sum_i |v_i|^2 )
330 template<class VecV, class Device>
331 typename real_traits<typename VecV::value_type >::type
332 norm_2(vector_expression<VecV, Device> const& v) {
333  using std::sqrt;
334  return sqrt(norm_sqr(v));
335 }
336 
337 /// \brief norm_inf v = max_i |v_i|
338 template<class VecV, class Device>
339 typename real_traits<typename VecV::value_type >::type
340 norm_inf(vector_expression<VecV, Device> const& v){
341  return max(abs(eval_block(v)));
342 }
343 
344 /// \brief index_norm_inf v = arg max_i |v_i|
345 template<class VecV, class Device>
346 std::size_t index_norm_inf(vector_expression<VecV, Device> const& v){
347  return arg_max(abs(eval_block(v)));
348 }
349 
350 // inner_prod (v1, v2) = sum_i v1_i * v2_i
351 template<class VecV1, class VecV2, class Device>
352 decltype(
353  typename VecV1::value_type() * typename VecV2::value_type()
354 )
355 inner_prod(
356  vector_expression<VecV1, Device> const& v1,
357  vector_expression<VecV2, Device> const& v2
358 ) {
359  REMORA_SIZE_CHECK(v1().size() == v2().size());
360  typedef decltype(
361  typename VecV1::value_type() * typename VecV2::value_type()
362  ) value_type;
363  value_type result = value_type();
364  kernels::dot(eval_block(v1),eval_block(v2),result);
365  return result;
366 }
367 
368 
369 // Vector Concatenation
370 
371 
372 ///\brief Concatenates two vectors
373 ///
374 /// Given two vectors v and w, forms the vector (v,w).
375 template<class VecV1, class VecV2, class Device>
376 vector_concat<VecV1,VecV2> operator|(
377  vector_expression<VecV1, Device> const& v1,
378  vector_expression<VecV2, Device> const& v2
379 ){
380  return vector_concat<VecV1,VecV2>(v1(),v2());
381 }
382 
383 ///\brief Concatenates a vector with a scalar
384 ///
385 /// Given a vector v and a scalar t, forms the vector (v,t)
386 template<class VecV, class T, class Device>
387 typename std::enable_if<
388  std::is_convertible<T, typename VecV::value_type>::value,
389  vector_concat<VecV, scalar_vector<T, Device> >
390 >::type operator| (
391  vector_expression<VecV, Device> const& v,
392  T t
393 ){
394  return v | scalar_vector<T, Device>(1,t);
395 }
396 
397 ///\brief Concatenates a vector with a scalar
398 ///
399 /// Given a vector v and a scalar t, forms the vector (v,t)
400 template<class T, class VecV, class Device>
401 typename std::enable_if<
402  std::is_convertible<T, typename VecV::value_type>::value,
403  vector_concat<scalar_vector<T, Device>,VecV >
404 >::type operator| (
405  T t,
406  vector_expression<VecV, Device> const& v
407 ){
408  return scalar_vector<T, Device>(1,t) | v;
409 }
410 
411 template<class E>
412 E const& copy_to_cpu(vector_expression<E, cpu_tag> const& e){
413  return e();
414 }
415 
416 }
417 
418 #ifdef REMORA_USE_GPU
419 #include "gpu/copy.hpp"
420 #endif
421 
422 #endif