matrix_expression.hpp
Go to the documentation of this file.
1 /*!
2  * \brief Expression templates for expressions involving matrices
3  *
4  * \author O. Krause
5  * \date 2016
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_MATRIX_EXPRESSION_HPP
29 #define REMORA_MATRIX_EXPRESSION_HPP
30 
31 #include "detail/expression_optimizers.hpp"
32 #include "kernels/matrix_fold.hpp"
33 #include "matrix_proxy.hpp"
34 #include "vector_proxy.hpp"
35 #include "vector_expression.hpp"
36 
37 namespace remora{
38 
39 
40 ///\brief Computes the outer product of two vectors.
41 ///
42 /// The outer product of two vectors v1 and v2 is defined as the matrix
43 /// (outer_prod (v1, v2))_ij [i] [j] = v1[i] * v2 [j]
44 template<class VecA, class VecB, class Device>
45 outer_product<VecA, VecB >
46 outer_prod(
47  vector_expression<VecA, Device> const& v1,
48  vector_expression<VecB, Device> const& v2
49 ) {
50  return outer_product<VecA, VecB>(v1(), v2());
51 }
52 
53 
54 
55 ///\brief Creates a matrix from a vector by repeating the vector in every row of the matrix.
56 ///
57 ///example: vector = (1,2,3)
58 ///repeat(vector,3) results in
59 ///(1,2,3)
60 ///(1,2,3)
61 ///(1,2,3)
62 ///@param vector the vector which is to be repeated as the rows of the resulting matrix
63 ///@param rows the number of rows of the matrix
64 template<class VecV, class Device>
65 vector_repeater<VecV> repeat(vector_expression<VecV, Device> const& vector, std::size_t rows){
66  return vector_repeater<VecV>(vector(),rows);
67 }
68 
69 /// \brief Repeats a single element to form a matrix of size rows x columns.
70 ///
71 ///TODO: cpu only!
72 ///@param scalar the value which is repeated
73 ///@param rows the number of rows of the resulting vector
74 ///@param columns the number of columns of the resulting vector
75 template<class T>
76 typename std::enable_if<std::is_arithmetic<T>::value, scalar_matrix<T,cpu_tag> >::type
77 repeat(T scalar, std::size_t rows, std::size_t columns){
78  return scalar_matrix<T,cpu_tag>(rows, columns, scalar);
79 }
80 
81 
82 /// \brief Computes the multiplication of a matrix-expression A with a scalar t.
83 ///
84 /// \f$ (A*t)_{ij} = e_{ij}*t \f$
85 template<class MatA, class T, class Device>
86 typename std::enable_if<
87  std::is_convertible<T, typename MatA::value_type >::value,
88  matrix_scalar_multiply<MatA>
89 >::type
90 operator* (matrix_expression<MatA, Device> const& A, T scalar){
91  return matrix_scalar_multiply<MatA>(A(), typename MatA::value_type(scalar));
92 }
93 
94 /// \brief Computes the multiplication of a matrix-expression A with a scalar t.
95 ///
96 /// \f$ (t*A)_{ij} = t*e_{ij} \f$
97 template<class T, class MatA, class Device>
98 typename std::enable_if<
99  std::is_convertible<T, typename MatA::value_type >::value,
100  matrix_scalar_multiply<MatA>
101 >::type
102 operator* (T scalar, matrix_expression<MatA, Device> const& A){
103  return matrix_scalar_multiply<MatA>(A(), typename MatA::value_type(scalar));
104 }
105 
106 /// \brief Negates the matrix-expression A.
107 ///
108 /// \f$ (-A)_{ij} = - e_{ij} \f$
109 template<class MatA, class Device>
110 matrix_scalar_multiply<MatA> operator-(matrix_expression<MatA, Device> const& A){
111  return matrix_scalar_multiply<MatA>(A(), typename MatA::value_type(-1));
112 }
113 
114 #define REMORA_UNARY_MATRIX_TRANSFORMATION(name, F)\
115 template<class MatA, class Device>\
116 matrix_unary<MatA,typename device_traits<Device>:: template F<typename MatA::value_type> >\
117 name(matrix_expression<MatA, Device> const& v){\
118  typedef typename device_traits<Device>:: template F<typename MatA::value_type> functor_type;\
119  return matrix_unary<MatA, functor_type >(v(), functor_type());\
120 }
139 #undef REMORA_UNARY_MATRIX_TRANSFORMATION
140 
141 ///\brief Adds two Matrices
142 template<class MatA, class MatB, class Device>
143 matrix_addition<MatA, MatB > operator+ (
144  matrix_expression<MatA, Device> const& A,
145  matrix_expression<MatB, Device> const& B
146 ){
147  REMORA_SIZE_CHECK(A().size1() == B().size1());
148  REMORA_SIZE_CHECK(A().size2() == B().size2());
149  return matrix_addition<MatA, MatB>(A(),B());
150 }
151 
152 ///\brief Subtracts two Matrices
153 template<class MatA, class MatB, class Device>
154 matrix_addition<MatA, matrix_scalar_multiply<MatB> > operator- (
155  matrix_expression<MatA, Device> const& A,
156  matrix_expression<MatB, Device> const& B
157 ){
158  REMORA_SIZE_CHECK(A().size1() == B().size1());
159  REMORA_SIZE_CHECK(A().size2() == B().size2());
160  return matrix_addition<MatA, matrix_scalar_multiply<MatB> >(A(),-B());
161 }
162 
163 ///\brief Adds a matrix plus a scalar which is interpreted as a constant matrix
164 template<class MatA, class T, class Device>
165 typename std::enable_if<
166  std::is_convertible<T, typename MatA::value_type>::value,
167  matrix_addition<MatA, scalar_matrix<T,Device> >
168 >::type operator+ (
169  matrix_expression<MatA, Device> const& A,
170  T t
171 ){
172  return A + scalar_matrix<T,Device>(A().size1(),A().size2(),t);
173 }
174 
175 ///\brief Adds a matrix plus a scalar which is interpreted as a constant matrix
176 template<class T, class MatA, class Device>
177 typename std::enable_if<
178  std::is_convertible<T, typename MatA::value_type>::value,
179  matrix_addition<MatA, scalar_matrix<T,Device> >
180 >::type operator+ (
181  T t,
182  matrix_expression<MatA, Device> const& A
183 ){
184  return A + scalar_matrix<T,Device>(A().size1(),A().size2(),t);
185 }
186 
187 ///\brief Subtracts a scalar which is interpreted as a constant matrix from a matrix.
188 template<class MatA, class T, class Device>
189 typename std::enable_if<
190  std::is_convertible<T, typename MatA::value_type>::value ,
191  matrix_addition<MatA, matrix_scalar_multiply<scalar_matrix<T,Device> > >
192 >::type operator- (
193  matrix_expression<MatA, Device> const& A,
194  T t
195 ){
196  return A - scalar_matrix<T,Device>(A().size1(),A().size2(),t);
197 }
198 
199 ///\brief Subtracts a matrix from a scalar which is interpreted as a constant matrix
200 template<class MatA, class T, class Device>
201 typename std::enable_if<
202  std::is_convertible<T, typename MatA::value_type>::value,
203  matrix_addition<scalar_matrix<T,Device>, matrix_scalar_multiply<MatA> >
204 >::type operator- (
205  T t,
206  matrix_expression<MatA, Device> const& A
207 ){
208  return scalar_matrix<T,Device>(A().size1(),A().size2(),t) - A;
209 }
210 
211 #define REMORA_BINARY_MATRIX_EXPRESSION(name, F)\
212 template<class MatA, class MatB, class Device>\
213 matrix_binary<MatA, MatB, typename device_traits<Device>:: template F<typename common_value_type<MatA,MatB>::type> >\
214 name(matrix_expression<MatA, Device> const& m1, matrix_expression<MatB, Device> const& m2){\
215  REMORA_SIZE_CHECK(m1().size1() == m2().size1());\
216  REMORA_SIZE_CHECK(m1().size2() == m2().size2());\
217  typedef typename common_value_type<MatA,MatB>::type type;\
218  typedef typename device_traits<Device>:: template F<type> functor_type;\
219  return matrix_binary<MatA, MatB, functor_type >(m1(),m2(), functor_type());\
220 }
221 REMORA_BINARY_MATRIX_EXPRESSION(operator*, multiply)
222 REMORA_BINARY_MATRIX_EXPRESSION(element_prod, multiply)
223 REMORA_BINARY_MATRIX_EXPRESSION(operator/, divide)
224 REMORA_BINARY_MATRIX_EXPRESSION(element_div, divide)
228 #undef REMORA_BINARY_MATRIX_EXPRESSION
229 
230 #define REMORA_MATRIX_SCALAR_TRANSFORMATION(name, F)\
231 template<class T, class MatA, class Device> \
232 typename std::enable_if< \
233  std::is_convertible<T, typename MatA::value_type >::value,\
234  matrix_binary<MatA, scalar_matrix<typename MatA::value_type,Device>,typename device_traits<Device>:: template F<typename MatA::value_type> > \
235 >::type \
236 name (matrix_expression<MatA, Device> const& m, T t){ \
237  typedef typename MatA::value_type type;\
238  typedef typename device_traits<Device>:: template F<type> functor_type;\
239  return matrix_binary<MatA, scalar_matrix<type,Device>, functor_type >(m(), scalar_matrix<type,Device>(m().size1(), m().size2(), t) ,functor_type()); \
240 }
241 REMORA_MATRIX_SCALAR_TRANSFORMATION(operator/, divide)
243 REMORA_MATRIX_SCALAR_TRANSFORMATION(operator<=, less_equal)
244 REMORA_MATRIX_SCALAR_TRANSFORMATION(operator>, greater)
245 REMORA_MATRIX_SCALAR_TRANSFORMATION(operator>=, greater_equal)
246 REMORA_MATRIX_SCALAR_TRANSFORMATION(operator==, equal)
247 REMORA_MATRIX_SCALAR_TRANSFORMATION(operator!=, not_equal)
251 #undef REMORA_MATRIX_SCALAR_TRANSFORMATION
252 
253 // operations of the form op(t,v)[i,j] = op(t,v[i,j])
254 #define REMORA_MATRIX_SCALAR_TRANSFORMATION_2(name, F)\
255 template<class T, class MatA, class Device> \
256 typename std::enable_if< \
257  std::is_convertible<T, typename MatA::value_type >::value,\
258  matrix_binary<scalar_matrix< typename MatA::value_type,Device>, MatA, typename device_traits<Device>:: template F< typename MatA::value_type> > \
259 >::type \
260 name (T t, matrix_expression<MatA, Device> const& m){ \
261  typedef typename MatA::value_type type;\
262  typedef typename device_traits<Device>:: template F<type> functor_type;\
263  return matrix_binary<scalar_matrix<type,Device>, MatA, functor_type >(scalar_matrix<type,Device>(m().size1(), m().size2(), t), m(), functor_type()); \
264 }
267 #undef REMORA_MATRIX_SCALAR_TRANSFORMATION_2
268 
269 template<class MatA, class MatB, class Device>
270 matrix_binary<MatA, MatB,
271  typename device_traits<Device>:: template safe_divide<typename common_value_type<MatA,MatB>::type>
272 >
273 safe_div(
274  matrix_expression<MatA, Device> const& A,
275  matrix_expression<MatB, Device> const& B,
276  typename common_value_type<MatA,MatB>::type defaultValue
277 ){
278  REMORA_SIZE_CHECK(A().size1() == B().size1());
279  REMORA_SIZE_CHECK(A().size2() == B().size2());
280  typedef typename common_value_type<MatA,MatB>::type result_type;
281  typedef typename device_traits<Device>:: template safe_divide<result_type> functor_type;
282  return matrix_binary<MatA, MatB, functor_type>(A(),B(), functor_type(defaultValue));
283 }
284 
285 
286 /// \brief computes the matrix-vector product x+=Av
287 ///
288 /// The call to prod does not compute the product itself but instead, as all other expressions,
289 /// it returns an expression-object which can compute it. In contrast to other expression,
290 /// this expression is optimized to make use of well known mathematical identities to reduce run time of the algorithm.
291 template<class MatA, class VecV, class Device>
292 typename detail::matrix_vector_prod_optimizer<MatA,VecV>::type prod(
293  matrix_expression<MatA, Device> const& A,vector_expression<VecV, Device> const& v
294 ) {
295  REMORA_SIZE_CHECK(A().size2() == v().size());
296  return detail::matrix_vector_prod_optimizer<MatA,VecV>::create(A(),v());
297 }
298 
299 /// \brief computes the matrix-vector product x+=v^TA
300 ///
301 /// it is computed via the identity (v^TA)^T= A^Tv
302 ///
303 /// The call to prod does not compute the product itself but instead, as all other expressions,
304 /// it returns an expression-object which can compute it. In contrast to other expression,
305 /// this expression is optimized to make use of well known mathematical identities to reduce run time of the algorithm.
306 template<class MatA, class VecV, class Device>
307 auto prod(vector_expression<VecV, Device> const& v,matrix_expression<MatA, Device> const& A) -> decltype(prod(trans(A),v)){
308  REMORA_SIZE_CHECK(A().size1() == v().size());
309  return prod(trans(A),v);
310 }
311 
312 /// \brief Operator syntax for computes the matrix-vector product
313 ///
314 /// v%A= prod(v,A).
315 template<class MatA, class VecV, class Device>
316 auto operator%(vector_expression<VecV, Device> const& v,matrix_expression<MatA, Device> const& A) -> decltype(prod(trans(A),v)){
317  REMORA_SIZE_CHECK(A().size1() == v().size());
318  return prod(trans(A),v);
319 }
320 
321 /// \brief Operator syntax for computes the matrix-vector product
322 ///
323 /// A%v = prod(A,v).
324 template<class MatA, class VecV, class Device>
325 auto operator%(matrix_expression<MatA, Device> const& A,vector_expression<VecV, Device> const& v) -> decltype(prod(A,v)){
326  REMORA_SIZE_CHECK(A().size2() == v().size());
327  return prod(A,v);
328 }
329 
330 /// \brief Computes the matrix-vector product x+= alpha * Av or x= alpha * Av
331 ///
332 /// A is interpreted as triangular matrix.
333 /// The first template argument governs the type
334 /// of triangular matrix: lower, upper, unit_lower and unit_upper.
335 ///
336 ///Example: x += triangular_prod<lower>(A,v);
337 template<class TriangularType, class MatA, class VecV, class Device>
338 matrix_vector_prod<detail::dense_triangular_proxy<typename const_expression<MatA>::type,TriangularType> ,VecV>
339 triangular_prod(
340  matrix_expression<MatA, Device> const& A,
341  vector_expression<VecV, Device>& v
342 ) {
343  REMORA_SIZE_CHECK(A().size2() == v().size());
344  typedef detail::dense_triangular_proxy<typename const_expression<MatA>::type,TriangularType> Wrapper;
345  return matrix_vector_prod<Wrapper ,VecV>(Wrapper(A()), v());
346 }
347 
348 /// \brief computes the matrix-matrix product X+=AB
349 template<class MatA, class MatB, class Device>
350 typename detail::matrix_matrix_prod_optimizer<MatA,MatB>::type prod(
351  matrix_expression<MatA, Device> const& A,
352  matrix_expression<MatB, Device> const& B
353 ) {
354  REMORA_SIZE_CHECK(A().size2() == B().size1());
355  static_assert(std::is_base_of<linear_structure, typename MatA::orientation>::value, "A must be linearly stored");
356  static_assert(std::is_base_of<linear_structure, typename MatB::orientation>::value, "B must be linearly stored");
357  return detail::matrix_matrix_prod_optimizer<MatA,MatB>::create(A(),B());
358 }
359 
360 /// \brief Operator syntax for computes the matrix-matrix product
361 ///
362 /// A%B= prod(A,B).
363 template<class MatA, class MatB, class Device>
364 auto operator%(
365  matrix_expression<MatA, Device> const& A,
366  matrix_expression<MatB, Device> const& B
367 ) -> decltype(prod(A,B)){
368  REMORA_SIZE_CHECK(A().size2() == B().size1());
369  return prod(A,B);
370 }
371 
372 /// \brief Computes the matrix-vector product x+= alpha * AB or x= alpha * AB
373 ///
374 /// A is interpreted as triangular matrix.
375 /// The first template argument governs the type
376 /// of triangular matrix: lower, upper, unit_lower and unit_upper.
377 /// B is interpreted as dense matrix.
378 ///
379 ///Example: x += triangular_prod<lower>(A,v);
380 template<class TriangularType, class MatA, class MatB, class Device>
381 matrix_matrix_prod<detail::dense_triangular_proxy<typename const_expression<MatA>::type,TriangularType> ,MatB>
382 triangular_prod(
383  matrix_expression<MatA, Device> const& A,
384  matrix_expression<MatB, Device> const& B
385 ) {
386  REMORA_SIZE_CHECK(A().size2() == B().size1());
387  static_assert(std::is_base_of<linear_structure, typename MatA::orientation>::value, "A must be linearly stored");
388  static_assert(std::is_base_of<linear_structure, typename MatB::orientation>::value, "B must be linearly stored");
389  typedef detail::dense_triangular_proxy<typename const_expression<MatA>::type,TriangularType> Wrapper;
390  return matrix_matrix_prod<Wrapper ,MatB>(Wrapper(A()), B());
391 }
392 
393 template<class MatA, class Device>
394 sum_matrix_rows<MatA>
395 sum_rows(matrix_expression<MatA, Device> const& A){
396  return sum_matrix_rows<MatA>(A());
397 }
398 
399 template<class MatA, class Device>
400 auto sum_columns(matrix_expression<MatA, Device> const& A)->decltype(sum_rows(trans(A))){
401  return sum_rows(trans(A));
402 }
403 
404 
405 template<class MatA, class Device>
406 typename MatA::value_type sum(matrix_expression<MatA, Device> const& A){
407  typedef typename MatA::value_type value_type;
408  typedef typename device_traits<Device>:: template add<value_type> functor_type;
409  auto const& elem_result = eval_block(A);
410  value_type result = 0;
411  kernels::matrix_fold<functor_type>(elem_result,result);
412  return result;
413 }
414 
415 template<class MatA, class Device>
416 typename MatA::value_type max(matrix_expression<MatA, Device> const& A){
417  typedef typename MatA::value_type value_type;
418  typedef typename device_traits<Device>:: template max<value_type> functor_type;
419  auto const& elem_result = eval_block(A);
420  value_type result = 0;
421  kernels::matrix_fold<functor_type>(elem_result,result);
422  return result;
423 }
424 
425 template<class MatA, class Device>
426 typename MatA::value_type min(matrix_expression<MatA, Device> const& A){
427  typedef typename MatA::value_type value_type;
428  typedef typename device_traits<Device>:: template min<value_type> functor_type;
429  auto const& elem_result = eval_block(A);
430  value_type result = 0;
431  kernels::matrix_fold<functor_type>(elem_result,result);
432  return result;
433 }
434 
435 /// \brief Returns the frobenius inner-product between matrices exprssions 1 and B.
436 ///
437 ///The frobenius inner product is defined as \f$ <A,B>_F=\sum_{ij} A_ij*B_{ij} \f$. It induces the
438 /// Frobenius norm \f$ ||A||_F = \sqrt{<A,A>_F} \f$
439 template<class MatA, class MatB, class Device>
440 decltype(typename MatA::value_type() * typename MatB::value_type())
441 frobenius_prod(
442  matrix_expression<MatA, Device> const& A,
443  matrix_expression<MatB, Device> const& B
444 ) {
445  REMORA_SIZE_CHECK(A().size1() == B().size1());
446  REMORA_SIZE_CHECK(A().size2() == B().size2());
447  return sum(eval_block(A*B));
448 }
449 
450 /// \brief Computes the matrix 1-norm |A|_1
451 ///
452 /// It is defined as \f$ \max_i \sum_j |A_{ij}| \f$
453 template<class MatA, class Device>
454 typename real_traits<typename MatA::value_type>::type
455 norm_1(matrix_expression<MatA, Device> const& A) {
456  return max(sum_rows(abs(A)));
457 }
458 
459 /// \brief computes the frobenius norm |A|_F
460 ///
461 /// It is defined as \f$ \sqrt{Tr(A^TA)}=\sqrt{\sum_{ij} A_{ij}^2} \f$
462 template<class MatA, class Device>
463 typename real_traits<typename MatA::value_type>::type
464 norm_frobenius(matrix_expression<MatA, Device> const& A) {
465  using std::sqrt;
466  return sqrt(sum(sqr(eval_block(A))));
467 }
468 
469 /// \brief Computes the matrix inf-norm |A|_inf
470 ///
471 /// It is defined as \f$ \max_i \sum_j |A_{ij}| \f$
472 template<class MatA, class Device>
473 typename real_traits<typename MatA::value_type>::type
474 norm_inf(matrix_expression<MatA, Device> const& A) {
475  return max(sum_columns(abs(A)));
476 }
477 
478 /// \brief Evaluates the trace of matrix A
479 ///
480 /// The rtace is defined as the sum of the diagonal elements of A,
481 /// \f$ \text{trace}(A) = \sum_i A_{ii}\f$
482 ///
483 /// \param A square matrix
484 /// \return the sum of the values at the diagonal of \em A
485 template < class MatA, class Device>
486 typename MatA::value_type trace(matrix_expression<MatA, Device> const& A)
487 {
488  REMORA_SIZE_CHECK(A().size1() == A().size2());
489  return sum(diag(A));
490 }
491 
492 /** \brief An identity matrix with values of type \c T
493  *
494  * Elements or cordinates \f$(i,i)\f$ are equal to 1 (one) and all others to 0 (zero).
495  */
496 template<class T>
497 class identity_matrix: public diagonal_matrix<scalar_vector<T, cpu_tag> > {
498  typedef diagonal_matrix<scalar_vector<T, cpu_tag> > base_type;
499 public:
500  identity_matrix(){}
501  identity_matrix(std::size_t size):base_type(scalar_vector<T, cpu_tag>(size,T(1))){}
502 };
503 
504 
505 template<class MatA, class Device>
506 diagonal_matrix<MatA> to_diagonal(vector_expression<MatA, Device> const& A){
507  return diagonal_matrix<MatA>(A());
508 }
509 
510 
511 // Block-Matrix Creation
512 
513 /// \brief Forms the block matrix (A|B) where B is to the right of A
514 template<class MatA, class MatB, class Device>
515 matrix_concat<MatA, MatB, true> operator|(
516  matrix_expression<MatA, Device> const& A,
517  matrix_expression<MatB, Device> const& B
518 ){
519  return matrix_concat<MatA, MatB, true>(A(),B());
520 }
521 
522 /// \brief Forms the block matrix (A|v) where v is a column vector to the right of A
523 template<class MatA, class VecV, class Device>
524 auto operator|(
525  matrix_expression<MatA, Device> const& A,
526  vector_expression<VecV, Device> const& v
527 ) -> decltype(A | trans(repeat(v,1))){
528  return A | trans(repeat(v,1));
529 }
530 
531 /// \brief Forms the block matrix (v|A) where v is a column vector to the left of A
532 template<class MatA, class VecV, class Device>
533 auto operator|(
534  vector_expression<VecV, Device> const& v,
535  matrix_expression<MatA, Device> const& A
536 ) -> decltype(trans(repeat(v,1)) | A){
537  return trans(repeat(v,1)) | A;
538 }
539 
540 /// \brief Forms the block matrix (A|t)
541 ///
542 /// The scalar t is interpreted as column vector
543 template<class MatA, class T, class Device>
544 typename std::enable_if<
545  std::is_convertible<T, typename MatA::value_type>::value,
546  matrix_concat<MatA, scalar_matrix<T, Device>, true >
547 >::type operator|(
548  matrix_expression<MatA, Device> const& A,
549  T const& t
550 ){
551  return A | repeat(t,A().size1(), 1);
552 }
553 
554 /// \brief Forms the block matrix (t|A)
555 ///
556 /// The scalar t is interpreted as column vector
557 template<class MatA, class T, class Device>
558 typename std::enable_if<
559  std::is_convertible<T, typename MatA::value_type>::value,
560  matrix_concat<scalar_matrix<T, Device>, MatA, true >
561 >::type operator|(
562  T const& t,
563  matrix_expression<MatA, Device> const& A
564 ){
565  return repeat(t,A().size1(), 1) | A;
566 }
567 
568 ///\brief Forms the block matrix A&B where A is on top of B
569 template<class MatA, class MatB, class Device>
570 matrix_concat<MatA, MatB, false> operator&(
571  matrix_expression<MatA, Device> const& A,
572  matrix_expression<MatB, Device> const& B
573 ){
574  return matrix_concat<MatA, MatB, false>(A(),B());
575 }
576 
577 /// \brief Forms the block matrix (A & v) where v is a row vector on the bottom of A
578 template<class MatA, class VecV, class Device>
579 auto operator&(
580  matrix_expression<MatA, Device> const& A,
581  vector_expression<VecV, Device> const& v
582 ) -> decltype(A & repeat(v,1)){
583  return A & repeat(v,1);
584 }
585 
586 /// \brief Forms the block matrix (A & v) where v is a row vector on the top of A
587 template<class MatA, class VecV, class Device>
588 auto operator&(
589  vector_expression<VecV, Device> const& v,
590  matrix_expression<MatA, Device> const& A
591 ) -> decltype(repeat(v,1) & A){
592  return repeat(v,1) & A;
593 }
594 
595 /// \brief Forms the block matrix (A & t)
596 ///
597 /// The scalar t is interpreted as row vector
598 template<class MatA, class T, class Device>
599 typename std::enable_if<
600  std::is_convertible<T, typename MatA::value_type>::value,
601  matrix_concat<MatA, scalar_matrix<T, Device>, false >
602 >::type operator&(
603  matrix_expression<MatA, Device> const& A,
604  T const& t
605 ){
606  return A & repeat(t, 1, A().size2());
607 }
608 
609 /// \brief Forms the block matrix (t & A)
610 ///
611 /// The scalar t is interpreted as row vector
612 template<class MatA, class T, class Device>
613 typename std::enable_if<
614  std::is_convertible<T, typename MatA::value_type>::value,
615  matrix_concat<scalar_matrix<T, Device>, MatA, false >
616 >::type operator&(
617  T const& t,
618  matrix_expression<MatA, Device> const& A
619 ){
620  return repeat(t,1, A().size2()) & A;
621 }
622 
623 
624 //copy between cpu and device
625 template<class E>
626 E const& copy_to_cpu(matrix_expression<E, cpu_tag> const& e){
627  return e();
628 }
629 
630 }
631 
632 #ifdef REMORA_USE_GPU
633 #include "gpu/copy.hpp"
634 #endif
635 
636 
637 #endif