inline arma_warn_unused typename enable_if2 <(is_arma_sparse_type<T1>::value) && (is_arma_sparse_type<T2>::value) && (is_same_type<typename T1::elem_type, typename T2::elem_type>::value), typename T1::elem_type >::result dot ( const SpBase<typename T1::elem_type, T1>& x, const SpBase<typename T2::elem_type, T2>& y ) { arma_extra_debug_sigprint(); const SpProxy<T1> pa(x.get_ref()); const SpProxy<T2> pb(y.get_ref()); arma_debug_assert_same_size(pa.get_n_rows(), pa.get_n_cols(), pb.get_n_rows(), pb.get_n_cols(), "dot()"); typedef typename T1::elem_type eT; if((&(x.get_ref()) == &(y.get_ref())) && (SpProxy<T1>::must_use_iterator == false)) { // We can do it directly! return op_dot::direct_dot_arma(pa.get_n_nonzero(), pa.get_values(), pa.get_values()); } else { // Iterate over both objects and see when they are the same eT result = eT(0); typename SpProxy<T1>::const_iterator_type a_it = pa.begin(); typename SpProxy<T2>::const_iterator_type b_it = pb.begin(); while((a_it.pos() < pa.get_n_nonzero()) && (b_it.pos() < pb.get_n_nonzero())) { if(a_it == b_it) { result += (*a_it) * (*b_it); ++a_it; ++b_it; } else if((a_it.col() < b_it.col()) || ((a_it.col() == b_it.col()) && (a_it.row() < b_it.row()))) { // a_it is "behind" ++a_it; } else { // b_it is "behind" ++b_it; } } return result; } }
inline arma_warn_unused typename enable_if2 <(is_arma_type<T1>::value) && (is_arma_sparse_type<T2>::value) && (is_same_type<typename T1::elem_type, typename T2::elem_type>::value), typename T1::elem_type >::result dot ( const Base<typename T1::elem_type, T1>& x, const SpBase<typename T2::elem_type, T2>& y ) { arma_extra_debug_sigprint(); const Proxy<T1> pa(x.get_ref()); const SpProxy<T2> pb(y.get_ref()); arma_debug_assert_same_size(pa.get_n_rows(), pa.get_n_cols(), pb.get_n_rows(), pb.get_n_cols(), "dot()"); typedef typename T1::elem_type eT; eT result = eT(0); typename SpProxy<T2>::const_iterator_type it = pb.begin(); // prefer_at_accessor won't save us operations while(it.pos() < pb.get_n_nonzero()) { result += (*it) * pa.at(it.row(), it.col()); ++it; } return result; }
inline typename enable_if2 < (is_arma_type<T1>::value && is_arma_sparse_type<T2>::value && is_same_type<typename T1::elem_type, typename T2::elem_type>::value), Mat<typename T1::elem_type> >::result operator/ ( const Base<typename T1::elem_type, T1>& x, const SpBase<typename T2::elem_type, T2>& y ) { arma_extra_debug_sigprint(); const Proxy<T1> pa(x.get_ref()); const SpProxy<T2> pb(y.get_ref()); arma_debug_assert_same_size(pa.get_n_rows(), pa.get_n_cols(), pb.get_n_rows(), pb.get_n_cols(), "element-wise division"); Mat<typename T1::elem_type> result(pa.get_n_rows(), pa.get_n_cols()); result.fill(Datum<typename T1::elem_type>::inf); // Now divide each element typename SpProxy<T2>::const_iterator_type it = pb.begin(); while(it.pos() < pb.get_n_nonzero()) { if(Proxy<T1>::prefer_at_accessor == false) { const uword index = (it.col() * result.n_rows) + it.row(); result[index] = pa[index] / (*it); } else { result.at(it.row(), it.col()) = pa.at(it.row(), it.col()) / (*it); } ++it; } return result; }
inline void spop_mean::apply_noalias_slow ( SpMat<typename T1::elem_type>& out, const SpProxy<T1>& p, const uword dim ) { arma_extra_debug_sigprint(); typedef typename T1::elem_type eT; const uword p_n_rows = p.get_n_rows(); const uword p_n_cols = p.get_n_cols(); if(dim == 0) // find the mean in each column { arma_extra_debug_print("spop_mean::apply_noalias(): dim = 0"); out.set_size((p_n_rows > 0) ? 1 : 0, p_n_cols); if( (p_n_rows == 0) || (p.get_n_nonzero() == 0) ) { return; } for(uword col = 0; col < p_n_cols; ++col) { // Do we have to use an iterator or can we use memory directly? if(SpProxy<T1>::must_use_iterator) { typename SpProxy<T1>::const_iterator_type it = p.begin_col(col); typename SpProxy<T1>::const_iterator_type end = p.begin_col(col + 1); const uword n_zero = p_n_rows - (end.pos() - it.pos()); out.at(0,col) = spop_mean::iterator_mean(it, end, n_zero, eT(0)); } else { out.at(0,col) = spop_mean::direct_mean ( &p.get_values()[p.get_col_ptrs()[col]], p.get_col_ptrs()[col + 1] - p.get_col_ptrs()[col], p_n_rows ); } } } else if(dim == 1) // find the mean in each row { arma_extra_debug_print("spop_mean::apply_noalias(): dim = 1"); out.set_size(p_n_rows, (p_n_cols > 0) ? 1 : 0); if( (p_n_cols == 0) || (p.get_n_nonzero() == 0) ) { return; } for(uword row = 0; row < p_n_rows; ++row) { // We must use an iterator regardless of how it is stored. typename SpProxy<T1>::const_row_iterator_type it = p.begin_row(row); typename SpProxy<T1>::const_row_iterator_type end = p.end_row(row); const uword n_zero = p_n_cols - (end.pos() - it.pos()); out.at(row,0) = spop_mean::iterator_mean(it, end, n_zero, eT(0)); } } }
inline void spop_var::apply_noalias ( SpMat<typename T1::pod_type>& out_ref, const SpProxy<T1>& p, const uword norm_type, const uword dim ) { arma_extra_debug_sigprint(); typedef typename T1::elem_type in_eT; //typedef typename T1::pod_type out_eT; const uword p_n_rows = p.get_n_rows(); const uword p_n_cols = p.get_n_cols(); if(dim == 0) { arma_extra_debug_print("spop_var::apply(), dim = 0"); arma_debug_check((p_n_rows == 0), "var(): given object has zero rows"); out_ref.set_size(1, p_n_cols); for(uword col = 0; col < p_n_cols; ++col) { if(SpProxy<T1>::must_use_iterator == true) { // We must use an iterator; we can't access memory directly. typename SpProxy<T1>::const_iterator_type it = p.begin_col(col); typename SpProxy<T1>::const_iterator_type end = p.begin_col(col + 1); const uword n_zero = p.get_n_rows() - (end.pos() - it.pos()); // in_eT is used just to get the specialization right (complex / noncomplex) out_ref.at(col) = spop_var::iterator_var(it, end, n_zero, norm_type, in_eT(0)); } else { // We can use direct memory access to calculate the variance. out_ref.at(col) = spop_var::direct_var ( &p.get_values()[p.get_col_ptrs()[col]], p.get_col_ptrs()[col + 1] - p.get_col_ptrs()[col], p.get_n_rows(), norm_type ); } } } else if(dim == 1) { arma_extra_debug_print("spop_var::apply_noalias(), dim = 1"); arma_debug_check((p_n_cols == 0), "var(): given object has zero columns"); out_ref.set_size(p_n_rows, 1); for(uword row = 0; row < p_n_rows; ++row) { // We have to use an iterator here regardless of whether or not we can // directly access memory. typename SpProxy<T1>::const_row_iterator_type it = p.begin_row(row); typename SpProxy<T1>::const_row_iterator_type end = p.end_row(row); const uword n_zero = p.get_n_cols() - (end.pos() - it.pos()); out_ref.at(row) = spop_var::iterator_var(it, end, n_zero, norm_type, in_eT(0)); } } }
inline typename enable_if2 < (is_arma_sparse_type<T1>::value && is_arma_sparse_type<T2>::value && is_same_type<typename T1::elem_type, typename T2::elem_type>::value), SpMat<typename T1::elem_type> >::result operator% ( const SpBase<typename T1::elem_type, T1>& x, const SpBase<typename T2::elem_type, T2>& y ) { arma_extra_debug_sigprint(); const SpProxy<T1> pa(x.get_ref()); const SpProxy<T2> pb(y.get_ref()); arma_debug_assert_same_size(pa.get_n_rows(), pa.get_n_cols(), pb.get_n_rows(), pb.get_n_cols(), "element-wise multiplication"); SpMat<typename T1::elem_type> result(pa.get_n_rows(), pa.get_n_cols()); // Resize memory to correct size. result.mem_resize(n_unique(x, y, op_n_unique_mul())); // Now iterate across both matrices. typename SpProxy<T1>::const_iterator_type x_it = pa.begin(); typename SpProxy<T2>::const_iterator_type y_it = pb.begin(); uword cur_val = 0; while((x_it.pos() < pa.get_n_nonzero()) || (y_it.pos() < pb.get_n_nonzero())) { if(x_it == y_it) { const typename T1::elem_type val = (*x_it) * (*y_it); if (val != 0) { access::rw(result.values[cur_val]) = val; access::rw(result.row_indices[cur_val]) = x_it.row(); ++access::rw(result.col_ptrs[x_it.col() + 1]); ++cur_val; } ++x_it; ++y_it; } else { if((x_it.col() < y_it.col()) || ((x_it.col() == y_it.col()) && (x_it.row() < y_it.row()))) // if y is closer to the end { ++x_it; } else { ++y_it; } } } // Fix column pointers to be cumulative. for(uword c = 1; c <= result.n_cols; ++c) { access::rw(result.col_ptrs[c]) += result.col_ptrs[c - 1]; } return result; }
inline typename enable_if2 < (is_arma_type<T1>::value && is_arma_sparse_type<T2>::value && is_same_type<typename T1::elem_type, typename T2::elem_type>::value), SpMat<typename T1::elem_type> >::result operator% ( const Base<typename T1::elem_type, T1>& x, const SpBase<typename T2::elem_type, T2>& y ) { arma_extra_debug_sigprint(); const Proxy<T1> pa(x.get_ref()); const SpProxy<T2> pb(y.get_ref()); arma_debug_assert_same_size(pa.get_n_rows(), pa.get_n_cols(), pb.get_n_rows(), pb.get_n_cols(), "element-wise multiplication"); SpMat<typename T1::elem_type> result(pa.get_n_rows(), pa.get_n_cols()); if(Proxy<T1>::prefer_at_accessor == false) { // use direct operator[] access // count new size uword new_n_nonzero = 0; typename SpProxy<T2>::const_iterator_type it = pb.begin(); while(it.pos() < pb.get_n_nonzero()) { if(((*it) * pa[(it.col() * pa.get_n_rows()) + it.row()]) != 0) { ++new_n_nonzero; } ++it; } // Resize memory accordingly. result.mem_resize(new_n_nonzero); uword cur_val = 0; typename SpProxy<T2>::const_iterator_type it2 = pb.begin(); while(it2.pos() < pb.get_n_nonzero()) { const typename T1::elem_type val = (*it2) * pa[(it2.col() * pa.get_n_rows()) + it2.row()]; if(val != 0) { access::rw(result.values[cur_val]) = val; access::rw(result.row_indices[cur_val]) = it2.row(); ++access::rw(result.col_ptrs[it2.col() + 1]); ++cur_val; } ++it2; } } else { // use at() access // count new size uword new_n_nonzero = 0; typename SpProxy<T2>::const_iterator_type it = pb.begin(); while(it.pos() < pb.get_n_nonzero()) { if(((*it) * pa.at(it.row(), it.col())) != 0) { ++new_n_nonzero; } ++it; } // Resize memory accordingly. result.mem_resize(new_n_nonzero); uword cur_val = 0; typename SpProxy<T2>::const_iterator_type it2 = pb.begin(); while(it2.pos() < pb.get_n_nonzero()) { const typename T1::elem_type val = (*it2) * pa.at(it2.row(), it2.col()); if(val != 0) { access::rw(result.values[cur_val]) = val; access::rw(result.row_indices[cur_val]) = it2.row(); ++access::rw(result.col_ptrs[it2.col() + 1]); ++cur_val; } ++it2; } } // Fix column pointers. for(uword c = 1; c <= result.n_cols; ++c) { access::rw(result.col_ptrs[c]) += result.col_ptrs[c - 1]; } return result; }
inline void spop_var::apply_noalias ( SpMat<typename T1::pod_type>& out, const SpProxy<T1>& p, const uword norm_type, const uword dim ) { arma_extra_debug_sigprint(); typedef typename T1::elem_type in_eT; //typedef typename T1::pod_type out_eT; const uword p_n_rows = p.get_n_rows(); const uword p_n_cols = p.get_n_cols(); // TODO: this is slow; rewrite based on the approach used by sparse mean() if(dim == 0) // find variance in each column { arma_extra_debug_print("spop_var::apply_noalias(): dim = 0"); out.set_size((p_n_rows > 0) ? 1 : 0, p_n_cols); if( (p_n_rows == 0) || (p.get_n_nonzero() == 0) ) { return; } for(uword col = 0; col < p_n_cols; ++col) { if(SpProxy<T1>::must_use_iterator) { // We must use an iterator; we can't access memory directly. typename SpProxy<T1>::const_iterator_type it = p.begin_col(col); typename SpProxy<T1>::const_iterator_type end = p.begin_col(col + 1); const uword n_zero = p_n_rows - (end.pos() - it.pos()); // in_eT is used just to get the specialization right (complex / noncomplex) out.at(0, col) = spop_var::iterator_var(it, end, n_zero, norm_type, in_eT(0)); } else { // We can use direct memory access to calculate the variance. out.at(0, col) = spop_var::direct_var ( &p.get_values()[p.get_col_ptrs()[col]], p.get_col_ptrs()[col + 1] - p.get_col_ptrs()[col], p_n_rows, norm_type ); } } } else if(dim == 1) // find variance in each row { arma_extra_debug_print("spop_var::apply_noalias(): dim = 1"); out.set_size(p_n_rows, (p_n_cols > 0) ? 1 : 0); if( (p_n_cols == 0) || (p.get_n_nonzero() == 0) ) { return; } for(uword row = 0; row < p_n_rows; ++row) { // We have to use an iterator here regardless of whether or not we can // directly access memory. typename SpProxy<T1>::const_row_iterator_type it = p.begin_row(row); typename SpProxy<T1>::const_row_iterator_type end = p.end_row(row); const uword n_zero = p_n_cols - (end.pos() - it.pos()); out.at(row, 0) = spop_var::iterator_var(it, end, n_zero, norm_type, in_eT(0)); } } }
arma_hot inline void spglue_minus::apply_noalias(SpMat<eT>& result, const SpProxy<T1>& pa, const SpProxy<T2>& pb) { arma_extra_debug_sigprint(); arma_debug_assert_same_size(pa.get_n_rows(), pa.get_n_cols(), pb.get_n_rows(), pb.get_n_cols(), "subtraction"); result.set_size(pa.get_n_rows(), pa.get_n_cols()); // Resize memory to correct size. result.mem_resize(n_unique(pa, pb, op_n_unique_sub())); // Now iterate across both matrices. typename SpProxy<T1>::const_iterator_type x_it = pa.begin(); typename SpProxy<T2>::const_iterator_type y_it = pb.begin(); uword cur_val = 0; while((x_it.pos() < pa.get_n_nonzero()) || (y_it.pos() < pb.get_n_nonzero())) { if(x_it == y_it) { const typename T1::elem_type val = (*x_it) - (*y_it); if (val != 0) { access::rw(result.values[cur_val]) = val; access::rw(result.row_indices[cur_val]) = x_it.row(); ++access::rw(result.col_ptrs[x_it.col() + 1]); ++cur_val; } ++x_it; ++y_it; } else { if((x_it.col() < y_it.col()) || ((x_it.col() == y_it.col()) && (x_it.row() < y_it.row()))) // if y is closer to the end { access::rw(result.values[cur_val]) = (*x_it); access::rw(result.row_indices[cur_val]) = x_it.row(); ++access::rw(result.col_ptrs[x_it.col() + 1]); ++cur_val; ++x_it; } else { access::rw(result.values[cur_val]) = -(*y_it); access::rw(result.row_indices[cur_val]) = y_it.row(); ++access::rw(result.col_ptrs[y_it.col() + 1]); ++cur_val; ++y_it; } } } // Fix column pointers to be cumulative. for(uword c = 1; c <= result.n_cols; ++c) { access::rw(result.col_ptrs[c]) += result.col_ptrs[c - 1]; } }
inline typename enable_if2 < (is_arma_sparse_type<T1>::value && is_arma_type<T2>::value && is_same_type<typename T1::elem_type, typename T2::elem_type>::value), SpMat<typename T1::elem_type> >::result operator/ ( const SpBase<typename T1::elem_type, T1>& x, const Base<typename T2::elem_type, T2>& y ) { arma_extra_debug_sigprint(); const SpProxy<T1> pa(x.get_ref()); const Proxy<T2> pb(y.get_ref()); arma_debug_assert_same_size(pa.get_n_rows(), pa.get_n_cols(), pb.get_n_rows(), pb.get_n_cols(), "element-wise division"); SpMat<typename T1::elem_type> result(pa.get_n_rows(), pa.get_n_cols()); // The compiler should be smart enough to optimize out the inner if/else statement entirely typename SpProxy<T1>::const_iterator_type it = pa.begin(); uword new_n_nonzero; while(it.pos() < pa.get_n_nonzero()) { if(Proxy<T2>::prefer_at_accessor == false) { const typename T1::elem_type val = (*it) / pb[(it.col() * pb.get_n_rows()) + it.row()]; if(val != 0) { ++new_n_nonzero; } } else { const typename T1::elem_type val = (*it) / pb.at(it.row(), it.col()); if(val != 0) { ++new_n_nonzero; } } ++it; } result.mem_resize(new_n_nonzero); typename SpProxy<T1>::const_iterator_type it2 = pa.begin(); uword cur_pos = 0; while(it2.pos() < pa.get_n_nonzero()) { if(Proxy<T2>::prefer_at_accessor == false) { const typename T1::elem_type val = (*it2) / pb[(it2.col() * pb.get_n_rows()) + it2.row()]; if(val != 0) { access::rw(result.values[cur_pos]) = val; access::rw(result.row_indices[cur_pos]) = it2.row(); ++access::rw(result.col_ptrs[it2.col() + 1]); ++cur_pos; } } else { const typename T1::elem_type val = (*it2) / pb.at(it2.row(), it2.col()); if(val != 0) { access::rw(result.values[cur_pos]) = val; access::rw(result.row_indices[cur_pos]) = it2.row(); ++access::rw(result.col_ptrs[it2.col() + 1]); ++cur_pos; } } ++it2; } // Fix column pointers for(uword col = 1; col <= result.n_cols; ++col) { access::rw(result.col_ptrs[col]) += result.col_ptrs[col - 1]; } return result; }