/*!
*
*
* \brief Assignment and evaluation of vector expressions
*
*
*
* \author O. Krause
* \date 2013
*
*
* \par Copyright 1995-2015 Shark Development Team
*
*
* This file is part of Shark.
*
*
* Shark is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Shark is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Shark. If not, see .
*
*/
#ifndef REMORA_ASSIGNMENT_HPP
#define REMORA_ASSIGNMENT_HPP
#include "kernels/matrix_assign.hpp"
#include "kernels/vector_assign.hpp"
#include "detail/traits.hpp"
#include
namespace remora{
//////////////////////////////////////////////////////////////////////
/////Evaluate blockwise expressions
//////////////////////////////////////////////////////////////////////
///\brief conditionally evaluates a vector expression if it is a block expression
///
/// If the expression is a block expression, a temporary vector is created to which
/// the expression is assigned, which is then returned, otherwise the expression itself
/// is returned
template
typename std::conditional<
std::is_base_of<
blockwise_tag,
typename E::evaluation_category
>::value,
typename vector_temporary::type,
E const&
>::type
eval_block(vector_expression const& e){
return e();//either casts to E const& or returns the copied expression
}
///\brief conditionally evaluates a matrix expression if it is a block expression
///
/// If the expression is a block expression, a temporary matrix is created to which
/// the expression is assigned, which is then returned, otherwise the expression itself
/// is returned
template
typename std::conditional<
std::is_base_of<
blockwise_tag,
typename E::evaluation_category
>::value,
typename matrix_temporary::type,
E const&
>::type
eval_block(matrix_expression const& e){
return e();//either casts to E const& or returns the copied expression
}
///\brief Evaluates an expression if it does not have a standard storage layout
///
/// This function evaluates an expression to a temporary if it does not have
/// a known storage type. i.e. proxy expressions and containers are not evaluated but passed
/// through while everything else is evaluated.
template
typename std::conditional<
std::is_same<
unknown_storage,
typename E::storage_type
>::value,
typename vector_temporary::type,
E const&
>::type
eval_expression(vector_expression const& e){
return e();//either casts to E const& or returns the evaluated expression
}
///\brief Evaluates an expression if it does not have a standard storage layout
///
/// This function evaluates an expression to a temporary if it does not have
/// a known storage type. i.e. proxy expressions and containers are not evaluated but passed
/// through while everything else is evaluated.
template
typename std::conditional<
std::is_same<
unknown_storage,
typename E::storage_type
>::value,
typename matrix_temporary::type,
E const&
>::type
eval_expression(matrix_expression const& e){
return e();//either casts to E const& or returns the evaluated expression
}
/////////////////////////////////////////////////////////////////////////////////////
////// Vector Assign
////////////////////////////////////////////////////////////////////////////////////
namespace detail{
template
void assign(
vector_expression& x, vector_expression const& v,
elementwise_tag, typename VecX::value_type alpha
){
typedef typename VecX::value_type value_type;
if(alpha == value_type(1)){
kernels::assign(x, v);
}else{
typename device_traits:: template multiply_assign f(alpha);
kernels::assign(x, v, f);
}
}
template
void assign(
vector_expression& x, vector_expression const& v,
blockwise_tag, typename VecX::value_type alpha
){
v().assign_to(x,alpha);
}
template
void plus_assign(vector_expression& x, vector_expression const& v,
elementwise_tag, typename VecX::value_type alpha
){
typename device_traits:: template multiply_and_add::type> f(alpha);
kernels::assign(x, v, f);
}
template
void plus_assign(
vector_expression& x, vector_expression const& v,
blockwise_tag,typename VecX::value_type alpha
){
v().plus_assign_to(x,alpha);
}
}
/// \brief Dispatches vector assignment on an expression level
///
/// This dispatcher takes care for whether the blockwise evaluation
/// or the elementwise evaluation is called
template
VecX& assign(vector_expression& x, vector_expression const& v, typename VecX::value_type alpha = 1){
REMORA_SIZE_CHECK(x().size() == v().size());
detail::assign(x,v,typename VecV::evaluation_category(),alpha);
return x();
}
/// \brief Dispatches vector plus-assignment on an expression level
///
/// This dispatcher takes care for whether the blockwise evaluation
/// or the elementwise evaluation is called
template
VecX& plus_assign(vector_expression& x, vector_expression const& v, typename VecX::value_type alpha = 1){
REMORA_SIZE_CHECK(x().size() == v().size());
detail::plus_assign(x,v,typename VecV::evaluation_category(),alpha);
return x();
}
/// \brief Dispatches vector multiply-assignment on an expression level
///
/// This dispatcher takes care for whether the blockwise evaluation
/// or the elementwise evaluation is called
template
VecX& multiply_assign(vector_expression& x, vector_expression const& v){
REMORA_SIZE_CHECK(x().size() == v().size());
auto&& veval = eval_block(v);
typedef typename device_traits:: template multiply::type> F;
kernels::assign(x, veval, F());
return x();
}
/// \brief Dispatches vector multiply-assignment on an expression level
///
/// This dispatcher takes care for whether the blockwise evaluation
/// or the elementwise evaluation is called
template
VecX& divide_assign(vector_expression& x, vector_expression const& v){
REMORA_SIZE_CHECK(x().size() == v().size());
auto&& veval = eval_block(v);
typedef typename device_traits:: template divide::type> F;
kernels::assign(x, veval, F());
return x();
}
/////////////////////////////////////////////////////////////////////////////////////
////// Matrix Assign
////////////////////////////////////////////////////////////////////////////////////
namespace detail{
template
void assign(
matrix_expression& A, matrix_expression const& B,
elementwise_tag,typename MatA::value_type alpha
){
typedef typename MatA::value_type value_type;
if(alpha == value_type(1)){
kernels::assign(A, B);
}else{
typename device_traits:: template multiply_assign f(alpha);
kernels::assign(A, B, f);
}
}
template
void assign(
matrix_expression& A, matrix_expression const& B,
blockwise_tag, typename MatA::value_type alpha
){
B().assign_to(A,alpha);
}
template
void plus_assign(
matrix_expression& A, matrix_expression const& B,
elementwise_tag, typename MatA::value_type alpha
){
typename device_traits:: template multiply_and_add::type> f(alpha);
kernels::assign(A,B,f);
}
template
void plus_assign(
matrix_expression& A, matrix_expression const& B,
blockwise_tag, typename MatA::value_type alpha
){
B().plus_assign_to(A,alpha);
}
}
/// \brief Dispatches matrix assignment on an expression level
///
/// This dispatcher takes care for whether the blockwise evaluation
/// or the elementwise evaluation is called
template
MatA& assign(matrix_expression& A, matrix_expression const& B, typename MatA::value_type alpha = 1){
REMORA_SIZE_CHECK(A().size1() == B().size1());
REMORA_SIZE_CHECK(A().size2() == B().size2());
detail::assign(A,B, typename MatB::evaluation_category(),alpha);
return A();
}
/// \brief Dispatches matrix plus-assignment on an expression level
///
/// This dispatcher takes care for whether the blockwise evaluation
/// or the elementwise evaluation is called
template
MatA& plus_assign(matrix_expression& A, matrix_expression const& B, typename MatA::value_type alpha = 1){
REMORA_SIZE_CHECK(A().size1() == B().size1());
REMORA_SIZE_CHECK(A().size2() == B().size2());
detail::plus_assign(A,B, typename MatB::evaluation_category(),alpha);
return A();
}
/// \brief Dispatches matrix multiply-assignment on an expression level
///
/// This dispatcher takes care for whether the blockwise evaluation
/// or the elementwise evaluation is called
template
MatA& multiply_assign(matrix_expression& A, matrix_expression const& B){
REMORA_SIZE_CHECK(A().size1() == B().size1());
REMORA_SIZE_CHECK(A().size2() == B().size2());
auto&& Beval = eval_block(B);
typedef typename device_traits:: template multiply::type> F;
kernels::assign(A, Beval, F());
return A();
}
/// \brief Dispatches matrix divide-assignment on an expression level
///
/// This dispatcher takes care for whether the blockwise evaluation
/// or the elementwise evaluation is called
template
MatA& divide_assign(matrix_expression& A, matrix_expression const& B){
REMORA_SIZE_CHECK(A().size1() == B().size1());
REMORA_SIZE_CHECK(A().size2() == B().size2());
auto&& Beval = eval_block(B);
typedef typename device_traits:: template divide::type> F;
kernels::assign(A, Beval, F());
return A();
}
//////////////////////////////////////////////////////////////////////////////////////
///// Vector Operators
/////////////////////////////////////////////////////////////////////////////////////
/// \brief Add-Assigns two vector expressions
///
/// Performs the operation x_i+=v_i for all elements.
/// Assumes that the right and left hand side aliases and therefore
/// performs a copy of the right hand side before assigning
/// use noalias as in noalias(x)+=v to avoid this if A and B do not alias
template
VecX& operator+=(vector_expression& x, vector_expression const& v){
REMORA_SIZE_CHECK(x().size() == v().size());
typename vector_temporary::type temporary(v);
return plus_assign(x,temporary);
}
template
typename VecX::closure_type operator+=(vector_expression&& x, vector_expression const& v){
REMORA_SIZE_CHECK(x().size() == v().size());
typename vector_temporary::type temporary(v);
return plus_assign(x,temporary);
}
/// \brief Subtract-Assigns two vector expressions
///
/// Performs the operation x_i-=v_i for all elements.
/// Assumes that the right and left hand side aliases and therefore
/// performs a copy of the right hand side before assigning
/// use noalias as in noalias(x)-=v to avoid this if A and B do not alias
template
VecX& operator-=(vector_expression& x, vector_expression const& v){
REMORA_SIZE_CHECK(x().size() == v().size());
typename vector_temporary::type temporary(v);
return plus_assign(x,temporary, typename VecX::value_type(-1.0));
}
template
typename VecX::closure_type operator-=(vector_expression&& x, vector_expression const& v){
REMORA_SIZE_CHECK(x().size() == v().size());
typename vector_temporary::type temporary(v);
return plus_assign(x,temporary, typename VecX::value_type(-1.0));
}
/// \brief Multiply-Assigns two vector expressions
///
/// Performs the operation x_i*=v_i for all elements.
/// Assumes that the right and left hand side aliases and therefore
/// performs a copy of the right hand side before assigning
/// use noalias as in noalias(x)*=v to avoid this if A and B do not alias
template
VecX& operator*=(vector_expression& x, vector_expression const& v){
REMORA_SIZE_CHECK(x().size() == v().size());
typename vector_temporary::type temporary(v);
return multiply_assign(x,temporary);
}
template
typename VecX::closure_type operator*=(vector_expression&& x, vector_expression const& v){
REMORA_SIZE_CHECK(x().size() == v().size());
typename vector_temporary::type temporary(v);
multiply_assign(x,temporary);
}
/// \brief Divide-Assigns two vector expressions
///
/// Performs the operation x_i/=v_i for all elements.
/// Assumes that the right and left hand side aliases and therefore
/// performs a copy of the right hand side before assigning
/// use noalias as in noalias(x)/=v to avoid this if A and B do not alias
template
VecX& operator/=(vector_expression& x, vector_expression const& v){
REMORA_SIZE_CHECK(x().size() == v().size());
typename vector_temporary::type temporary(v);
return divide_assign(x,temporary);
}
template
typename VecX::closure_type operator/=(vector_expression&& x, vector_expression const& v){
REMORA_SIZE_CHECK(x().size() == v().size());
typename vector_temporary::type temporary(v);
divide_assign(x,temporary);
}
/// \brief Adds a scalar to all elements of the vector
///
/// Performs the operation x_i += t for all elements.
template
typename std::enable_if::value,VecX&>::type
operator+=(vector_expression& x, T t){
kernels::assign:: template add > (x, t);
return x();
}
template
typename std::enable_if::value,typename VecX::closure_type>::type
operator+=(vector_expression&& x, T t){
kernels::assign:: template add > (x, t);
return x();
}
/// \brief Subtracts a scalar from all elements of the vector
///
/// Performs the operation x_i += t for all elements.
template
typename std::enable_if::value,VecX&>::type
operator-=(vector_expression& x, T t){
kernels::assign:: template subtract > (x, t);
return x();
}
template
typename std::enable_if::value,typename VecX::closure_type>::type
operator-=(vector_expression&& x, T t){
kernels::assign:: template subtract > (x, t);
return x();
}
/// \brief Multiplies a scalar with all elements of the vector
///
/// Performs the operation x_i *= t for all elements.
template
typename std::enable_if::value,VecX&>::type
operator*=(vector_expression& x, T t){
kernels::assign:: template multiply > (x, t);
return x();
}
template
typename std::enable_if::value,typename VecX::closure_type>::type
operator*=(vector_expression&& x, T t){
kernels::assign:: template multiply > (x, t);
return x();
}
/// \brief Divides all elements of the vector by a scalar
///
/// Performs the operation x_i /= t for all elements.
template
typename std::enable_if::value,VecX&>::type
operator/=(vector_expression& x, T t){
kernels::assign:: template divide > (x, t);
return x();
}
template
typename std::enable_if::value,typename VecX::closure_type>::type
operator/=(vector_expression&& x, T t){
kernels::assign:: template divide > (x, t);
return x();
}
//////////////////////////////////////////////////////////////////////////////////////
///// Matrix Operators
/////////////////////////////////////////////////////////////////////////////////////
/// \brief Add-Assigns two matrix expressions
///
/// Performs the operation A_ij+=B_ij for all elements.
/// Assumes that the right and left hand side aliases and therefore
/// performs a copy of the right hand side before assigning
/// use noalias as in noalias(A)+=B to avoid this if A and B do not alias
template
MatA& operator+=(matrix_expression& A, matrix_expression const& B){
REMORA_SIZE_CHECK(A().size1() == B().size1());
REMORA_SIZE_CHECK(A().size2() == B().size2());
typename matrix_temporary::type temporary(B);
return plus_assign(A,temporary);
}
template
typename MatA::closure_type operator+=(matrix_expression&& A, matrix_expression const& B){
REMORA_SIZE_CHECK(A().size1() == B().size1());
REMORA_SIZE_CHECK(A().size2() == B().size2());
typename matrix_temporary::type temporary(B);
return plus_assign(A,temporary);
}
/// \brief Subtract-Assigns two matrix expressions
///
/// Performs the operation A_ij-=B_ij for all elements.
/// Assumes that the right and left hand side aliases and therefore
/// performs a copy of the right hand side before assigning
/// use noalias as in noalias(A)-=B to avoid this if A and B do not alias
template
MatA& operator-=(matrix_expression& A, matrix_expression const& B){
REMORA_SIZE_CHECK(A().size1() == B().size1());
REMORA_SIZE_CHECK(A().size2() == B().size2());
typename matrix_temporary::type temporary(B);
return plus_assign(A,temporary, typename MatA::value_type(-1.0));
}
template
typename MatA::closure_type operator-=(matrix_expression&& A, matrix_expression const& B){
REMORA_SIZE_CHECK(A().size1() == B().size1());
REMORA_SIZE_CHECK(A().size2() == B().size2());
typename matrix_temporary::type temporary(B);
return plus_assign(A,temporary, typename MatA::value_type(-1.0));
}
/// \brief Multiply-Assigns two matrix expressions
///
/// Performs the operation A_ij*=B_ij for all elements.
/// Assumes that the right and left hand side aliases and therefore
/// performs a copy of the right hand side before assigning
/// use noalias as in noalias(A)*=B to avoid this if A and B do not alias
template
MatA& operator*=(matrix_expression& A, matrix_expression const& B){
REMORA_SIZE_CHECK(A().size1() == B().size1());
REMORA_SIZE_CHECK(A().size2() == B().size2());
typename matrix_temporary::type temporary(B);
return multiply_assign(A,temporary);
}
template
typename MatA::closure_type operator*=(matrix_expression&& A, matrix_expression const& B){
REMORA_SIZE_CHECK(A().size1() == B().size1());
REMORA_SIZE_CHECK(A().size2() == B().size2());
typename matrix_temporary::type temporary(B);
return multiply_assign(A,temporary);
}
/// \brief Divide-Assigns two matrix expressions
///
/// Performs the operation A_ij/=B_ij for all elements.
/// Assumes that the right and left hand side aliases and therefore
/// performs a copy of the right hand side before assigning
/// use noalias as in noalias(A)/=B to avoid this if A and B do not alias
template
MatA& operator/=(matrix_expression& A, matrix_expression const& B){
REMORA_SIZE_CHECK(A().size1() == B().size1());
REMORA_SIZE_CHECK(A().size2() == B().size2());
typename matrix_temporary::type temporary(B);
return divide_assign(A,temporary);
}
template
typename MatA::closure_type operator/=(matrix_expression&& A, matrix_expression const& B){
REMORA_SIZE_CHECK(A().size1() == B().size1());
REMORA_SIZE_CHECK(A().size2() == B().size2());
typename matrix_temporary::type temporary(B);
return divide_assign(A,temporary);
}
/// \brief Adds a scalar to all elements of the matrix
///
/// Performs the operation A_ij += t for all elements.
template
typename std::enable_if::value,MatA&>::type
operator+=(matrix_expression& A, T t){
kernels::assign:: template add > (A, t);
return A();
}
template
typename std::enable_if::value,typename MatA::closure_type>::type
operator+=(matrix_expression&& A, T t){
kernels::assign:: template add > (A, t);
return A();
}
/// \brief Subtracts a scalar from all elements of the matrix
///
/// Performs the operation A_ij -= t for all elements.
template
typename std::enable_if::value,MatA&>::type
operator-=(matrix_expression& A, T t){
kernels::assign:: template subtract > (A, t);
return A();
}
template
typename std::enable_if::value,typename MatA::closure_type>::type
operator-=(matrix_expression&& A, T t){
kernels::assign:: template subtract > (A, t);
return A();
}
/// \brief Multiplies a scalar to all elements of the matrix
///
/// Performs the operation A_ij *= t for all elements.
template
typename std::enable_if::value,MatA&>::type
operator*=(matrix_expression& A, T t){
kernels::assign:: template multiply > (A, t);
return A();
}
template
typename std::enable_if::value,typename MatA::closure_type>::type
operator*=(matrix_expression&& A, T t){
kernels::assign:: template multiply > (A, t);
return A();
}
/// \brief Divides all elements of the matrix by a scalar
///
/// Performs the operation A_ij /= t for all elements.
template
typename std::enable_if::value,MatA&>::type
operator/=(matrix_expression& A, T t){
kernels::assign:: template divide > (A, t);
return A();
}
template
typename std::enable_if::value,typename MatA::closure_type>::type
operator/=(matrix_expression&& A, T t){
kernels::assign:: template divide > (A, t);
return A();
}
// Assignment proxy.
// Provides temporary free assigment when LHS has no alias on RHS
template
class noalias_proxy{
public:
typedef typename C::closure_type closure_type;
typedef typename C::value_type value_type;
noalias_proxy(C &lval): m_lval(lval) {}
noalias_proxy(const noalias_proxy &p):m_lval(p.m_lval) {}
template
closure_type &operator= (const E &e) {
return assign(m_lval, e);
}
template
closure_type &operator+= (const E &e) {
return plus_assign(m_lval, e);
}
template
closure_type &operator-= (const E &e) {
return plus_assign(m_lval, e, typename C::value_type(-1));
}
template
closure_type &operator*= (const E &e) {
return multiply_assign(m_lval, e);
}
template
closure_type &operator/= (const E &e) {
return divide_assign(m_lval, e);
}
//this is not needed, but prevents errors when for example doing noalias(x)+=2;
closure_type &operator+= (value_type t) {
return m_lval += t;
}
//this is not needed, but prevents errors when for example doing noalias(x)-=2;
closure_type &operator-= (value_type t) {
return m_lval -= t;
}
//this is not needed, but prevents errors when for example doing noalias(x)*=2;
closure_type &operator*= (value_type t) {
return m_lval *= t;
}
//this is not needed, but prevents errors when for example doing noalias(x)/=2;
closure_type &operator/= (value_type t) {
return m_lval /= t;
}
private:
closure_type m_lval;
};
// Improve syntax of efficient assignment where no aliases of LHS appear on the RHS
// noalias(lhs) = rhs_expression
template
noalias_proxy noalias(matrix_expression& lvalue) {
return noalias_proxy (lvalue());
}
template
noalias_proxy noalias(vector_expression& lvalue) {
return noalias_proxy (lvalue());
}
template
noalias_proxy noalias(vector_expression&& rvalue) {
return noalias_proxy(rvalue());
}
template
noalias_proxy noalias(matrix_expression&& lvalue) {
return noalias_proxy (lvalue());
}
template
noalias_proxy noalias(vector_set_expression& lvalue) {
return noalias_proxy (lvalue());
}
}
#endif