// SPDX-License-Identifier: Apache-2.0 // // Copyright 2008-2016 Conrad Sanderson (http://conradsanderson.id.au) // Copyright 2008-2016 National ICT Australia (NICTA) // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ------------------------------------------------------------------------ //! \addtogroup SpMat //! @{ /** * Initialize a sparse matrix with size 0x0 (empty). */ template inline SpMat::SpMat() : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); init_cold(0,0); } /** * Clean up the memory of a sparse matrix and destruct it. */ template inline SpMat::~SpMat() { arma_extra_debug_sigprint_this(this); if(values ) { memory::release(access::rw(values)); } if(row_indices) { memory::release(access::rw(row_indices)); } if(col_ptrs ) { memory::release(access::rw(col_ptrs)); } } /** * Constructor with size given. */ template inline SpMat::SpMat(const uword in_rows, const uword in_cols) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); init_cold(in_rows, in_cols); } template inline SpMat::SpMat(const SizeMat& s) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); init_cold(s.n_rows, s.n_cols); } template inline SpMat::SpMat(const arma_reserve_indicator&, const uword in_rows, const uword in_cols, const uword new_n_nonzero) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); init_cold(in_rows, in_cols, new_n_nonzero); } template template inline SpMat::SpMat(const arma_layout_indicator&, const SpMat& x) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); init_cold(x.n_rows, x.n_cols, x.n_nonzero); if(x.n_nonzero == 0) { return; } if(x.row_indices) { arrayops::copy(access::rwp(row_indices), x.row_indices, x.n_nonzero + 1); } if(x.col_ptrs ) { arrayops::copy(access::rwp(col_ptrs), x.col_ptrs, x.n_cols + 1); } // NOTE: 'values' array is not initialised } /** * Assemble from text. */ template inline SpMat::SpMat(const char* text) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); init(std::string(text)); } template inline SpMat& SpMat::operator=(const char* text) { arma_extra_debug_sigprint(); init(std::string(text)); return *this; } template inline SpMat::SpMat(const std::string& text) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint(); init(text); } template inline SpMat& SpMat::operator=(const std::string& text) { arma_extra_debug_sigprint(); init(text); return *this; } template inline SpMat::SpMat(const SpMat& x) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); init(x); } template inline SpMat::SpMat(SpMat&& in_mat) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); arma_extra_debug_sigprint(arma_str::format("this = %x in_mat = %x") % this % &in_mat); (*this).steal_mem(in_mat); } template inline SpMat& SpMat::operator=(SpMat&& in_mat) { arma_extra_debug_sigprint(arma_str::format("this = %x in_mat = %x") % this % &in_mat); (*this).steal_mem(in_mat); return *this; } template inline SpMat::SpMat(const MapMat& x) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); init(x); } template inline SpMat& SpMat::operator=(const MapMat& x) { arma_extra_debug_sigprint(); init(x); return *this; } //! Insert a large number of values at once. //! locations.row[0] should be row indices, locations.row[1] should be column indices, //! and values should be the corresponding values. //! If sort_locations is false, then it is assumed that the locations and values //! are already sorted in column-major ordering. template template inline SpMat::SpMat(const Base& locations_expr, const Base& vals_expr, const bool sort_locations) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); const quasi_unwrap locs_tmp( locations_expr.get_ref() ); const quasi_unwrap vals_tmp( vals_expr.get_ref() ); const Mat& locs = locs_tmp.M; const Mat& vals = vals_tmp.M; arma_debug_check( (vals.is_vec() == false), "SpMat::SpMat(): given 'values' object must be a vector" ); arma_debug_check( (locs.n_rows != 2), "SpMat::SpMat(): locations matrix must have two rows" ); arma_debug_check( (locs.n_cols != vals.n_elem), "SpMat::SpMat(): number of locations is different than number of values" ); // If there are no elements in the list, max() will fail. if(locs.n_cols == 0) { init_cold(0, 0); return; } // Automatically determine size before pruning zeros. uvec bounds = arma::max(locs, 1); init_cold(bounds[0] + 1, bounds[1] + 1); // Ensure that there are no zeros const uword N_old = vals.n_elem; uword N_new = 0; for(uword i=0; i < N_old; ++i) { N_new += (vals[i] != eT(0)) ? uword(1) : uword(0); } if(N_new != N_old) { Col filtered_vals( N_new, arma_nozeros_indicator()); Mat filtered_locs(2, N_new, arma_nozeros_indicator()); uword index = 0; for(uword i = 0; i < N_old; ++i) { if(vals[i] != eT(0)) { filtered_vals[index] = vals[i]; filtered_locs.at(0, index) = locs.at(0, i); filtered_locs.at(1, index) = locs.at(1, i); ++index; } } init_batch_std(filtered_locs, filtered_vals, sort_locations); } else { init_batch_std(locs, vals, sort_locations); } } //! Insert a large number of values at once. //! locations.row[0] should be row indices, locations.row[1] should be column indices, //! and values should be the corresponding values. //! If sort_locations is false, then it is assumed that the locations and values //! are already sorted in column-major ordering. //! In this constructor the size is explicitly given. template template inline SpMat::SpMat(const Base& locations_expr, const Base& vals_expr, const uword in_n_rows, const uword in_n_cols, const bool sort_locations, const bool check_for_zeros) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); const quasi_unwrap locs_tmp( locations_expr.get_ref() ); const quasi_unwrap vals_tmp( vals_expr.get_ref() ); const Mat& locs = locs_tmp.M; const Mat& vals = vals_tmp.M; arma_debug_check( (vals.is_vec() == false), "SpMat::SpMat(): given 'values' object must be a vector" ); arma_debug_check( (locs.n_rows != 2), "SpMat::SpMat(): locations matrix must have two rows" ); arma_debug_check( (locs.n_cols != vals.n_elem), "SpMat::SpMat(): number of locations is different than number of values" ); init_cold(in_n_rows, in_n_cols); // Ensure that there are no zeros, unless the user asked not to. if(check_for_zeros) { const uword N_old = vals.n_elem; uword N_new = 0; for(uword i=0; i < N_old; ++i) { N_new += (vals[i] != eT(0)) ? uword(1) : uword(0); } if(N_new != N_old) { Col filtered_vals( N_new, arma_nozeros_indicator()); Mat filtered_locs(2, N_new, arma_nozeros_indicator()); uword index = 0; for(uword i = 0; i < N_old; ++i) { if(vals[i] != eT(0)) { filtered_vals[index] = vals[i]; filtered_locs.at(0, index) = locs.at(0, i); filtered_locs.at(1, index) = locs.at(1, i); ++index; } } init_batch_std(filtered_locs, filtered_vals, sort_locations); } else { init_batch_std(locs, vals, sort_locations); } } else { init_batch_std(locs, vals, sort_locations); } } template template inline SpMat::SpMat(const bool add_values, const Base& locations_expr, const Base& vals_expr, const uword in_n_rows, const uword in_n_cols, const bool sort_locations, const bool check_for_zeros) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); const quasi_unwrap locs_tmp( locations_expr.get_ref() ); const quasi_unwrap vals_tmp( vals_expr.get_ref() ); const Mat& locs = locs_tmp.M; const Mat& vals = vals_tmp.M; arma_debug_check( (vals.is_vec() == false), "SpMat::SpMat(): given 'values' object must be a vector" ); arma_debug_check( (locs.n_rows != 2), "SpMat::SpMat(): locations matrix must have two rows" ); arma_debug_check( (locs.n_cols != vals.n_elem), "SpMat::SpMat(): number of locations is different than number of values" ); init_cold(in_n_rows, in_n_cols); // Ensure that there are no zeros, unless the user asked not to. if(check_for_zeros) { const uword N_old = vals.n_elem; uword N_new = 0; for(uword i=0; i < N_old; ++i) { N_new += (vals[i] != eT(0)) ? uword(1) : uword(0); } if(N_new != N_old) { Col filtered_vals( N_new, arma_nozeros_indicator()); Mat filtered_locs(2, N_new, arma_nozeros_indicator()); uword index = 0; for(uword i = 0; i < N_old; ++i) { if(vals[i] != eT(0)) { filtered_vals[index] = vals[i]; filtered_locs.at(0, index) = locs.at(0, i); filtered_locs.at(1, index) = locs.at(1, i); ++index; } } add_values ? init_batch_add(filtered_locs, filtered_vals, sort_locations) : init_batch_std(filtered_locs, filtered_vals, sort_locations); } else { add_values ? init_batch_add(locs, vals, sort_locations) : init_batch_std(locs, vals, sort_locations); } } else { add_values ? init_batch_add(locs, vals, sort_locations) : init_batch_std(locs, vals, sort_locations); } } //! Insert a large number of values at once. //! Per CSC format, rowind_expr should be row indices, //! colptr_expr should column ptr indices locations, //! and values should be the corresponding values. //! In this constructor the size is explicitly given. //! Values are assumed to be sorted, and the size //! information is trusted template template inline SpMat::SpMat ( const Base& rowind_expr, const Base& colptr_expr, const Base& values_expr, const uword in_n_rows, const uword in_n_cols, const bool check_for_zeros ) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); const quasi_unwrap rowind_tmp( rowind_expr.get_ref() ); const quasi_unwrap colptr_tmp( colptr_expr.get_ref() ); const quasi_unwrap vals_tmp( values_expr.get_ref() ); const Mat& rowind = rowind_tmp.M; const Mat& colptr = colptr_tmp.M; const Mat& vals = vals_tmp.M; arma_debug_check( (rowind.is_vec() == false), "SpMat::SpMat(): given 'rowind' object must be a vector" ); arma_debug_check( (colptr.is_vec() == false), "SpMat::SpMat(): given 'colptr' object must be a vector" ); arma_debug_check( (vals.is_vec() == false), "SpMat::SpMat(): given 'values' object must be a vector" ); // Resize to correct number of elements (this also sets n_nonzero) init_cold(in_n_rows, in_n_cols, vals.n_elem); arma_debug_check( (rowind.n_elem != vals.n_elem), "SpMat::SpMat(): number of row indices is not equal to number of values" ); arma_debug_check( (colptr.n_elem != (n_cols+1) ), "SpMat::SpMat(): number of column pointers is not equal to n_cols+1" ); // copy supplied values into sparse matrix -- not checked for consistency arrayops::copy(access::rwp(row_indices), rowind.memptr(), rowind.n_elem ); arrayops::copy(access::rwp(col_ptrs), colptr.memptr(), colptr.n_elem ); arrayops::copy(access::rwp(values), vals.memptr(), vals.n_elem ); // important: set the sentinel as well access::rw(col_ptrs[n_cols + 1]) = std::numeric_limits::max(); // make sure no zeros are stored if(check_for_zeros) { remove_zeros(); } } template inline SpMat& SpMat::operator=(const eT val) { arma_extra_debug_sigprint(); if(val != eT(0)) { // Resize to 1x1 then set that to the right value. init(1, 1, 1); // Sets col_ptrs to 0. // Manually set element. access::rw(values[0]) = val; access::rw(row_indices[0]) = 0; access::rw(col_ptrs[1]) = 1; } else { init(0, 0); } return *this; } template inline SpMat& SpMat::operator*=(const eT val) { arma_extra_debug_sigprint(); if(val != eT(0)) { sync_csc(); invalidate_cache(); const uword n_nz = n_nonzero; eT* vals = access::rwp(values); bool has_zero = false; for(uword i=0; i inline SpMat& SpMat::operator/=(const eT val) { arma_extra_debug_sigprint(); arma_debug_check( (val == eT(0)), "element-wise division: division by zero" ); sync_csc(); invalidate_cache(); const uword n_nz = n_nonzero; eT* vals = access::rwp(values); bool has_zero = false; for(uword i=0; i inline SpMat& SpMat::operator=(const SpMat& x) { arma_extra_debug_sigprint(); init(x); return *this; } template inline SpMat& SpMat::operator+=(const SpMat& x) { arma_extra_debug_sigprint(); sync_csc(); SpMat out = (*this) + x; steal_mem(out); return *this; } template inline SpMat& SpMat::operator-=(const SpMat& x) { arma_extra_debug_sigprint(); sync_csc(); SpMat out = (*this) - x; steal_mem(out); return *this; } template inline SpMat& SpMat::operator*=(const SpMat& y) { arma_extra_debug_sigprint(); sync_csc(); SpMat z = (*this) * y; steal_mem(z); return *this; } // This is in-place element-wise matrix multiplication. template inline SpMat& SpMat::operator%=(const SpMat& y) { arma_extra_debug_sigprint(); sync_csc(); SpMat z = (*this) % y; steal_mem(z); return *this; } template inline SpMat& SpMat::operator/=(const SpMat& x) { arma_extra_debug_sigprint(); // NOTE: use of this function is not advised; it is implemented only for completeness arma_debug_assert_same_size(n_rows, n_cols, x.n_rows, x.n_cols, "element-wise division"); for(uword c = 0; c < n_cols; ++c) for(uword r = 0; r < n_rows; ++r) { at(r, c) /= x.at(r, c); } return *this; } template template inline SpMat::SpMat(const SpToDOp& expr) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); typedef typename T1::elem_type T; // Make sure the type is compatible. arma_type_check(( is_same_type< eT, T >::no )); op_type::apply(*this, expr); } // Construct a complex matrix out of two non-complex matrices template template inline SpMat::SpMat ( const SpBase::pod_type, T1>& A, const SpBase::pod_type, T2>& B ) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint(); typedef typename T1::elem_type T; // Make sure eT is complex and T is not (compile-time check). arma_type_check(( is_cx::no )); arma_type_check(( is_cx< T>::yes )); // Compile-time abort if types are not compatible. arma_type_check(( is_same_type< std::complex, eT >::no )); const unwrap_spmat tmp1(A.get_ref()); const unwrap_spmat tmp2(B.get_ref()); const SpMat& X = tmp1.M; const SpMat& Y = tmp2.M; arma_debug_assert_same_size(X.n_rows, X.n_cols, Y.n_rows, Y.n_cols, "SpMat()"); const uword l_n_rows = X.n_rows; const uword l_n_cols = X.n_cols; // Set size of matrix correctly. init_cold(l_n_rows, l_n_cols, n_unique(X, Y, op_n_unique_count())); // Now on a second iteration, fill it. typename SpMat::const_iterator x_it = X.begin(); typename SpMat::const_iterator x_end = X.end(); typename SpMat::const_iterator y_it = Y.begin(); typename SpMat::const_iterator y_end = Y.end(); uword cur_pos = 0; while((x_it != x_end) || (y_it != y_end)) { if(x_it == y_it) // if we are at the same place { access::rw(values[cur_pos]) = std::complex((T) *x_it, (T) *y_it); access::rw(row_indices[cur_pos]) = x_it.row(); ++access::rw(col_ptrs[x_it.col() + 1]); ++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(values[cur_pos]) = std::complex((T) *x_it, T(0)); access::rw(row_indices[cur_pos]) = x_it.row(); ++access::rw(col_ptrs[x_it.col() + 1]); ++x_it; } else // x is closer to the end { access::rw(values[cur_pos]) = std::complex(T(0), (T) *y_it); access::rw(row_indices[cur_pos]) = y_it.row(); ++access::rw(col_ptrs[y_it.col() + 1]); ++y_it; } } ++cur_pos; } // Now fix the column pointers; they are supposed to be a sum. for(uword c = 1; c <= n_cols; ++c) { access::rw(col_ptrs[c]) += col_ptrs[c - 1]; } } template template inline SpMat::SpMat(const Base& x) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); (*this).operator=(x); } template template inline SpMat& SpMat::operator=(const Base& expr) { arma_extra_debug_sigprint(); if(is_same_type< T1, Gen, gen_zeros> >::yes) { const Proxy P(expr.get_ref()); (*this).zeros( P.get_n_rows(), P.get_n_cols() ); return *this; } if(is_same_type< T1, Gen, gen_eye> >::yes) { const Proxy P(expr.get_ref()); (*this).eye( P.get_n_rows(), P.get_n_cols() ); return *this; } const quasi_unwrap tmp(expr.get_ref()); const Mat& x = tmp.M; const uword x_n_rows = x.n_rows; const uword x_n_cols = x.n_cols; const uword x_n_elem = x.n_elem; // Count number of nonzero elements in base object. uword n = 0; const eT* x_mem = x.memptr(); for(uword i=0; i < x_n_elem; ++i) { n += (x_mem[i] != eT(0)) ? uword(1) : uword(0); } init(x_n_rows, x_n_cols, n); if(n == 0) { return *this; } // Now the memory is resized correctly; set nonzero elements. n = 0; for(uword j = 0; j < x_n_cols; ++j) for(uword i = 0; i < x_n_rows; ++i) { const eT val = (*x_mem); x_mem++; if(val != eT(0)) { access::rw(values[n]) = val; access::rw(row_indices[n]) = i; access::rw(col_ptrs[j + 1])++; ++n; } } // Sum column counts to be column pointers. for(uword c = 1; c <= n_cols; ++c) { access::rw(col_ptrs[c]) += col_ptrs[c - 1]; } return *this; } template template inline SpMat& SpMat::operator+=(const Base& x) { arma_extra_debug_sigprint(); sync_csc(); return (*this).operator=( (*this) + x.get_ref() ); } template template inline SpMat& SpMat::operator-=(const Base& x) { arma_extra_debug_sigprint(); sync_csc(); return (*this).operator=( (*this) - x.get_ref() ); } template template inline SpMat& SpMat::operator*=(const Base& x) { arma_extra_debug_sigprint(); sync_csc(); return (*this).operator=( (*this) * x.get_ref() ); } // NOTE: use of this function is not advised; it is implemented only for completeness template template inline SpMat& SpMat::operator/=(const Base& x) { arma_extra_debug_sigprint(); sync_csc(); SpMat tmp = (*this) / x.get_ref(); steal_mem(tmp); return *this; } template template inline SpMat& SpMat::operator%=(const Base& x) { arma_extra_debug_sigprint(); const quasi_unwrap U(x.get_ref()); const Mat& B = U.M; arma_debug_assert_same_size(n_rows, n_cols, B.n_rows, B.n_cols, "element-wise multiplication"); sync_csc(); invalidate_cache(); constexpr eT zero = eT(0); bool has_zero = false; for(uword c=0; c < n_cols; ++c) { const uword index_start = col_ptrs[c ]; const uword index_end = col_ptrs[c + 1]; for(uword i=index_start; i < index_end; ++i) { const uword r = row_indices[i]; eT& val = access::rw(values[i]); const eT result = val * B.at(r,c); val = result; if(result == zero) { has_zero = true; } } } if(has_zero) { remove_zeros(); } return *this; } template template inline SpMat::SpMat(const Op& expr) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); (*this).operator=(expr); } template template inline SpMat& SpMat::operator=(const Op& expr) { arma_extra_debug_sigprint(); const diagmat_proxy P(expr.m); const uword max_n_nonzero = (std::min)(P.n_rows, P.n_cols); // resize memory to upper bound init(P.n_rows, P.n_cols, max_n_nonzero); uword count = 0; for(uword i=0; i < max_n_nonzero; ++i) { const eT val = P[i]; if(val != eT(0)) { access::rw(values[count]) = val; access::rw(row_indices[count]) = i; access::rw(col_ptrs[i + 1])++; ++count; } } // fix column pointers to be cumulative for(uword i = 1; i < n_cols + 1; ++i) { access::rw(col_ptrs[i]) += col_ptrs[i - 1]; } // quick resize without reallocating memory and copying data access::rw( n_nonzero) = count; access::rw( values[count]) = eT(0); access::rw(row_indices[count]) = uword(0); return *this; } template template inline SpMat& SpMat::operator+=(const Op& expr) { arma_extra_debug_sigprint(); const SpMat tmp(expr); return (*this).operator+=(tmp); } template template inline SpMat& SpMat::operator-=(const Op& expr) { arma_extra_debug_sigprint(); const SpMat tmp(expr); return (*this).operator-=(tmp); } template template inline SpMat& SpMat::operator*=(const Op& expr) { arma_extra_debug_sigprint(); const SpMat tmp(expr); return (*this).operator*=(tmp); } template template inline SpMat& SpMat::operator/=(const Op& expr) { arma_extra_debug_sigprint(); const SpMat tmp(expr); return (*this).operator/=(tmp); } template template inline SpMat& SpMat::operator%=(const Op& expr) { arma_extra_debug_sigprint(); const SpMat tmp(expr); return (*this).operator%=(tmp); } /** * Functions on subviews. */ template inline SpMat::SpMat(const SpSubview& X) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); (*this).operator=(X); } template inline SpMat& SpMat::operator=(const SpSubview& X) { arma_extra_debug_sigprint(); if(X.n_nonzero == 0) { zeros(X.n_rows, X.n_cols); return *this; } X.m.sync_csc(); const bool alias = (this == &(X.m)); if(alias) { SpMat tmp(X); steal_mem(tmp); } else { init(X.n_rows, X.n_cols, X.n_nonzero); if(X.n_rows == X.m.n_rows) { const uword sv_col_start = X.aux_col1; const uword sv_col_end = X.aux_col1 + X.n_cols - 1; typename SpMat::const_col_iterator m_it = X.m.begin_col_no_sync(sv_col_start); typename SpMat::const_col_iterator m_it_end = X.m.end_col_no_sync(sv_col_end); uword count = 0; while(m_it != m_it_end) { const uword m_it_col_adjusted = m_it.col() - sv_col_start; access::rw(row_indices[count]) = m_it.row(); access::rw(values[count]) = (*m_it); ++access::rw(col_ptrs[m_it_col_adjusted + 1]); count++; ++m_it; } } else { typename SpSubview::const_iterator it = X.begin(); typename SpSubview::const_iterator it_end = X.end(); while(it != it_end) { const uword it_pos = it.pos(); access::rw(row_indices[it_pos]) = it.row(); access::rw(values[it_pos]) = (*it); ++access::rw(col_ptrs[it.col() + 1]); ++it; } } // Now sum column pointers. for(uword c = 1; c <= n_cols; ++c) { access::rw(col_ptrs[c]) += col_ptrs[c - 1]; } } return *this; } template inline SpMat& SpMat::operator+=(const SpSubview& X) { arma_extra_debug_sigprint(); sync_csc(); SpMat tmp = (*this) + X; steal_mem(tmp); return *this; } template inline SpMat& SpMat::operator-=(const SpSubview& X) { arma_extra_debug_sigprint(); sync_csc(); SpMat tmp = (*this) - X; steal_mem(tmp); return *this; } template inline SpMat& SpMat::operator*=(const SpSubview& y) { arma_extra_debug_sigprint(); sync_csc(); SpMat z = (*this) * y; steal_mem(z); return *this; } template inline SpMat& SpMat::operator%=(const SpSubview& x) { arma_extra_debug_sigprint(); sync_csc(); SpMat tmp = (*this) % x; steal_mem(tmp); return *this; } template inline SpMat& SpMat::operator/=(const SpSubview& x) { arma_extra_debug_sigprint(); arma_debug_assert_same_size(n_rows, n_cols, x.n_rows, x.n_cols, "element-wise division"); // There is no pretty way to do this. for(uword elem = 0; elem < n_elem; elem++) { at(elem) /= x(elem); } return *this; } template template inline SpMat::SpMat(const SpSubview_col_list& X) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); SpSubview_col_list::extract(*this, X); } template template inline SpMat& SpMat::operator=(const SpSubview_col_list& X) { arma_extra_debug_sigprint(); const bool alias = (this == &(X.m)); if(alias == false) { SpSubview_col_list::extract(*this, X); } else { SpMat tmp(X); steal_mem(tmp); } return *this; } template template inline SpMat& SpMat::operator+=(const SpSubview_col_list& X) { arma_extra_debug_sigprint(); SpSubview_col_list::plus_inplace(*this, X); return *this; } template template inline SpMat& SpMat::operator-=(const SpSubview_col_list& X) { arma_extra_debug_sigprint(); SpSubview_col_list::minus_inplace(*this, X); return *this; } template template inline SpMat& SpMat::operator*=(const SpSubview_col_list& X) { arma_extra_debug_sigprint(); sync_csc(); SpMat z = (*this) * X; steal_mem(z); return *this; } template template inline SpMat& SpMat::operator%=(const SpSubview_col_list& X) { arma_extra_debug_sigprint(); SpSubview_col_list::schur_inplace(*this, X); return *this; } template template inline SpMat& SpMat::operator/=(const SpSubview_col_list& X) { arma_extra_debug_sigprint(); SpSubview_col_list::div_inplace(*this, X); return *this; } template inline SpMat::SpMat(const spdiagview& X) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); spdiagview::extract(*this, X); } template inline SpMat& SpMat::operator=(const spdiagview& X) { arma_extra_debug_sigprint(); spdiagview::extract(*this, X); return *this; } template inline SpMat& SpMat::operator+=(const spdiagview& X) { arma_extra_debug_sigprint(); const SpMat tmp(X); return (*this).operator+=(tmp); } template inline SpMat& SpMat::operator-=(const spdiagview& X) { arma_extra_debug_sigprint(); const SpMat tmp(X); return (*this).operator-=(tmp); } template inline SpMat& SpMat::operator*=(const spdiagview& X) { arma_extra_debug_sigprint(); const SpMat tmp(X); return (*this).operator*=(tmp); } template inline SpMat& SpMat::operator%=(const spdiagview& X) { arma_extra_debug_sigprint(); const SpMat tmp(X); return (*this).operator%=(tmp); } template inline SpMat& SpMat::operator/=(const spdiagview& X) { arma_extra_debug_sigprint(); const SpMat tmp(X); return (*this).operator/=(tmp); } template template inline SpMat::SpMat(const SpOp& X) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) // set in application of sparse operation , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); spop_type::apply(*this, X); sync_csc(); // in case apply() used element accessors invalidate_cache(); // in case apply() modified the CSC representation } template template inline SpMat& SpMat::operator=(const SpOp& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); spop_type::apply(*this, X); sync_csc(); // in case apply() used element accessors invalidate_cache(); // in case apply() modified the CSC representation return *this; } template template inline SpMat& SpMat::operator+=(const SpOp& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); sync_csc(); const SpMat m(X); return (*this).operator+=(m); } template template inline SpMat& SpMat::operator-=(const SpOp& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); sync_csc(); const SpMat m(X); return (*this).operator-=(m); } template template inline SpMat& SpMat::operator*=(const SpOp& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); sync_csc(); const SpMat m(X); return (*this).operator*=(m); } template template inline SpMat& SpMat::operator%=(const SpOp& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); sync_csc(); const SpMat m(X); return (*this).operator%=(m); } template template inline SpMat& SpMat::operator/=(const SpOp& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); sync_csc(); const SpMat m(X); return (*this).operator/=(m); } template template inline SpMat::SpMat(const SpGlue& X) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); spglue_type::apply(*this, X); sync_csc(); // in case apply() used element accessors invalidate_cache(); // in case apply() modified the CSC representation } template template inline SpMat& SpMat::operator=(const SpGlue& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); spglue_type::apply(*this, X); sync_csc(); // in case apply() used element accessors invalidate_cache(); // in case apply() modified the CSC representation return *this; } template template inline SpMat& SpMat::operator+=(const SpGlue& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); sync_csc(); const SpMat m(X); return (*this).operator+=(m); } template template inline SpMat& SpMat::operator-=(const SpGlue& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); sync_csc(); const SpMat m(X); return (*this).operator-=(m); } template template inline SpMat& SpMat::operator*=(const SpGlue& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); sync_csc(); const SpMat m(X); return (*this).operator*=(m); } template template inline SpMat& SpMat::operator%=(const SpGlue& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); sync_csc(); const SpMat m(X); return (*this).operator%=(m); } template template inline SpMat& SpMat::operator/=(const SpGlue& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); sync_csc(); const SpMat m(X); return (*this).operator/=(m); } template template inline SpMat::SpMat(const mtSpOp& X) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); spop_type::apply(*this, X); sync_csc(); // in case apply() used element accessors invalidate_cache(); // in case apply() modified the CSC representation } template template inline SpMat& SpMat::operator=(const mtSpOp& X) { arma_extra_debug_sigprint(); spop_type::apply(*this, X); sync_csc(); // in case apply() used element accessors invalidate_cache(); // in case apply() modified the CSC representation return *this; } template template inline SpMat& SpMat::operator+=(const mtSpOp& X) { arma_extra_debug_sigprint(); sync_csc(); const SpMat m(X); return (*this).operator+=(m); } template template inline SpMat& SpMat::operator-=(const mtSpOp& X) { arma_extra_debug_sigprint(); sync_csc(); const SpMat m(X); return (*this).operator-=(m); } template template inline SpMat& SpMat::operator*=(const mtSpOp& X) { arma_extra_debug_sigprint(); sync_csc(); const SpMat m(X); return (*this).operator*=(m); } template template inline SpMat& SpMat::operator%=(const mtSpOp& X) { arma_extra_debug_sigprint(); sync_csc(); const SpMat m(X); return (*this).operator%=(m); } template template inline SpMat& SpMat::operator/=(const mtSpOp& X) { arma_extra_debug_sigprint(); sync_csc(); const SpMat m(X); return (*this).operator/=(m); } template template inline SpMat::SpMat(const mtSpGlue& X) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); spglue_type::apply(*this, X); sync_csc(); // in case apply() used element accessors invalidate_cache(); // in case apply() modified the CSC representation } template template inline SpMat& SpMat::operator=(const mtSpGlue& X) { arma_extra_debug_sigprint(); spglue_type::apply(*this, X); sync_csc(); // in case apply() used element accessors invalidate_cache(); // in case apply() modified the CSC representation return *this; } template template inline SpMat& SpMat::operator+=(const mtSpGlue& X) { arma_extra_debug_sigprint(); sync_csc(); const SpMat m(X); return (*this).operator+=(m); } template template inline SpMat& SpMat::operator-=(const mtSpGlue& X) { arma_extra_debug_sigprint(); sync_csc(); const SpMat m(X); return (*this).operator-=(m); } template template inline SpMat& SpMat::operator*=(const mtSpGlue& X) { arma_extra_debug_sigprint(); sync_csc(); const SpMat m(X); return (*this).operator*=(m); } template template inline SpMat& SpMat::operator%=(const mtSpGlue& X) { arma_extra_debug_sigprint(); sync_csc(); const SpMat m(X); return (*this).operator%=(m); } template template inline SpMat& SpMat::operator/=(const mtSpGlue& X) { arma_extra_debug_sigprint(); sync_csc(); const SpMat m(X); return (*this).operator/=(m); } template arma_inline SpSubview_row SpMat::row(const uword row_num) { arma_extra_debug_sigprint(); arma_debug_check_bounds(row_num >= n_rows, "SpMat::row(): out of bounds"); return SpSubview_row(*this, row_num); } template arma_inline const SpSubview_row SpMat::row(const uword row_num) const { arma_extra_debug_sigprint(); arma_debug_check_bounds(row_num >= n_rows, "SpMat::row(): out of bounds"); return SpSubview_row(*this, row_num); } template inline SpSubview_row SpMat::operator()(const uword row_num, const span& col_span) { arma_extra_debug_sigprint(); const bool col_all = col_span.whole; const uword local_n_cols = n_cols; const uword in_col1 = col_all ? 0 : col_span.a; const uword in_col2 = col_span.b; const uword submat_n_cols = col_all ? local_n_cols : in_col2 - in_col1 + 1; arma_debug_check_bounds ( (row_num >= n_rows) || ( col_all ? false : ((in_col1 > in_col2) || (in_col2 >= local_n_cols)) ) , "SpMat::operator(): indices out of bounds or incorrectly used" ); return SpSubview_row(*this, row_num, in_col1, submat_n_cols); } template inline const SpSubview_row SpMat::operator()(const uword row_num, const span& col_span) const { arma_extra_debug_sigprint(); const bool col_all = col_span.whole; const uword local_n_cols = n_cols; const uword in_col1 = col_all ? 0 : col_span.a; const uword in_col2 = col_span.b; const uword submat_n_cols = col_all ? local_n_cols : in_col2 - in_col1 + 1; arma_debug_check_bounds ( (row_num >= n_rows) || ( col_all ? false : ((in_col1 > in_col2) || (in_col2 >= local_n_cols)) ) , "SpMat::operator(): indices out of bounds or incorrectly used" ); return SpSubview_row(*this, row_num, in_col1, submat_n_cols); } template arma_inline SpSubview_col SpMat::col(const uword col_num) { arma_extra_debug_sigprint(); arma_debug_check_bounds(col_num >= n_cols, "SpMat::col(): out of bounds"); return SpSubview_col(*this, col_num); } template arma_inline const SpSubview_col SpMat::col(const uword col_num) const { arma_extra_debug_sigprint(); arma_debug_check_bounds(col_num >= n_cols, "SpMat::col(): out of bounds"); return SpSubview_col(*this, col_num); } template inline SpSubview_col SpMat::operator()(const span& row_span, const uword col_num) { arma_extra_debug_sigprint(); const bool row_all = row_span.whole; const uword local_n_rows = n_rows; const uword in_row1 = row_all ? 0 : row_span.a; const uword in_row2 = row_span.b; const uword submat_n_rows = row_all ? local_n_rows : in_row2 - in_row1 + 1; arma_debug_check_bounds ( (col_num >= n_cols) || ( row_all ? false : ((in_row1 > in_row2) || (in_row2 >= local_n_rows)) ) , "SpMat::operator(): indices out of bounds or incorrectly used" ); return SpSubview_col(*this, col_num, in_row1, submat_n_rows); } template inline const SpSubview_col SpMat::operator()(const span& row_span, const uword col_num) const { arma_extra_debug_sigprint(); const bool row_all = row_span.whole; const uword local_n_rows = n_rows; const uword in_row1 = row_all ? 0 : row_span.a; const uword in_row2 = row_span.b; const uword submat_n_rows = row_all ? local_n_rows : in_row2 - in_row1 + 1; arma_debug_check_bounds ( (col_num >= n_cols) || ( row_all ? false : ((in_row1 > in_row2) || (in_row2 >= local_n_rows)) ) , "SpMat::operator(): indices out of bounds or incorrectly used" ); return SpSubview_col(*this, col_num, in_row1, submat_n_rows); } template arma_inline SpSubview SpMat::rows(const uword in_row1, const uword in_row2) { arma_extra_debug_sigprint(); arma_debug_check_bounds ( (in_row1 > in_row2) || (in_row2 >= n_rows), "SpMat::rows(): indices out of bounds or incorrectly used" ); const uword subview_n_rows = in_row2 - in_row1 + 1; return SpSubview(*this, in_row1, 0, subview_n_rows, n_cols); } template arma_inline const SpSubview SpMat::rows(const uword in_row1, const uword in_row2) const { arma_extra_debug_sigprint(); arma_debug_check_bounds ( (in_row1 > in_row2) || (in_row2 >= n_rows), "SpMat::rows(): indices out of bounds or incorrectly used" ); const uword subview_n_rows = in_row2 - in_row1 + 1; return SpSubview(*this, in_row1, 0, subview_n_rows, n_cols); } template arma_inline SpSubview SpMat::cols(const uword in_col1, const uword in_col2) { arma_extra_debug_sigprint(); arma_debug_check_bounds ( (in_col1 > in_col2) || (in_col2 >= n_cols), "SpMat::cols(): indices out of bounds or incorrectly used" ); const uword subview_n_cols = in_col2 - in_col1 + 1; return SpSubview(*this, 0, in_col1, n_rows, subview_n_cols); } template arma_inline const SpSubview SpMat::cols(const uword in_col1, const uword in_col2) const { arma_extra_debug_sigprint(); arma_debug_check_bounds ( (in_col1 > in_col2) || (in_col2 >= n_cols), "SpMat::cols(): indices out of bounds or incorrectly used" ); const uword subview_n_cols = in_col2 - in_col1 + 1; return SpSubview(*this, 0, in_col1, n_rows, subview_n_cols); } template arma_inline SpSubview SpMat::submat(const uword in_row1, const uword in_col1, const uword in_row2, const uword in_col2) { arma_extra_debug_sigprint(); arma_debug_check_bounds ( (in_row1 > in_row2) || (in_col1 > in_col2) || (in_row2 >= n_rows) || (in_col2 >= n_cols), "SpMat::submat(): indices out of bounds or incorrectly used" ); const uword subview_n_rows = in_row2 - in_row1 + 1; const uword subview_n_cols = in_col2 - in_col1 + 1; return SpSubview(*this, in_row1, in_col1, subview_n_rows, subview_n_cols); } template arma_inline const SpSubview SpMat::submat(const uword in_row1, const uword in_col1, const uword in_row2, const uword in_col2) const { arma_extra_debug_sigprint(); arma_debug_check_bounds ( (in_row1 > in_row2) || (in_col1 > in_col2) || (in_row2 >= n_rows) || (in_col2 >= n_cols), "SpMat::submat(): indices out of bounds or incorrectly used" ); const uword subview_n_rows = in_row2 - in_row1 + 1; const uword subview_n_cols = in_col2 - in_col1 + 1; return SpSubview(*this, in_row1, in_col1, subview_n_rows, subview_n_cols); } template arma_inline SpSubview SpMat::submat(const uword in_row1, const uword in_col1, const SizeMat& s) { arma_extra_debug_sigprint(); const uword l_n_rows = n_rows; const uword l_n_cols = n_cols; const uword s_n_rows = s.n_rows; const uword s_n_cols = s.n_cols; arma_debug_check_bounds ( ((in_row1 >= l_n_rows) || (in_col1 >= l_n_cols) || ((in_row1 + s_n_rows) > l_n_rows) || ((in_col1 + s_n_cols) > l_n_cols)), "SpMat::submat(): indices or size out of bounds" ); return SpSubview(*this, in_row1, in_col1, s_n_rows, s_n_cols); } template arma_inline const SpSubview SpMat::submat(const uword in_row1, const uword in_col1, const SizeMat& s) const { arma_extra_debug_sigprint(); const uword l_n_rows = n_rows; const uword l_n_cols = n_cols; const uword s_n_rows = s.n_rows; const uword s_n_cols = s.n_cols; arma_debug_check_bounds ( ((in_row1 >= l_n_rows) || (in_col1 >= l_n_cols) || ((in_row1 + s_n_rows) > l_n_rows) || ((in_col1 + s_n_cols) > l_n_cols)), "SpMat::submat(): indices or size out of bounds" ); return SpSubview(*this, in_row1, in_col1, s_n_rows, s_n_cols); } template inline SpSubview SpMat::submat(const span& row_span, const span& col_span) { arma_extra_debug_sigprint(); const bool row_all = row_span.whole; const bool col_all = col_span.whole; const uword local_n_rows = n_rows; const uword local_n_cols = n_cols; const uword in_row1 = row_all ? 0 : row_span.a; const uword in_row2 = row_span.b; const uword submat_n_rows = row_all ? local_n_rows : in_row2 - in_row1 + 1; const uword in_col1 = col_all ? 0 : col_span.a; const uword in_col2 = col_span.b; const uword submat_n_cols = col_all ? local_n_cols : in_col2 - in_col1 + 1; arma_debug_check_bounds ( ( row_all ? false : ((in_row1 > in_row2) || (in_row2 >= local_n_rows)) ) || ( col_all ? false : ((in_col1 > in_col2) || (in_col2 >= local_n_cols)) ) , "SpMat::submat(): indices out of bounds or incorrectly used" ); return SpSubview(*this, in_row1, in_col1, submat_n_rows, submat_n_cols); } template inline const SpSubview SpMat::submat(const span& row_span, const span& col_span) const { arma_extra_debug_sigprint(); const bool row_all = row_span.whole; const bool col_all = col_span.whole; const uword local_n_rows = n_rows; const uword local_n_cols = n_cols; const uword in_row1 = row_all ? 0 : row_span.a; const uword in_row2 = row_span.b; const uword submat_n_rows = row_all ? local_n_rows : in_row2 - in_row1 + 1; const uword in_col1 = col_all ? 0 : col_span.a; const uword in_col2 = col_span.b; const uword submat_n_cols = col_all ? local_n_cols : in_col2 - in_col1 + 1; arma_debug_check_bounds ( ( row_all ? false : ((in_row1 > in_row2) || (in_row2 >= local_n_rows)) ) || ( col_all ? false : ((in_col1 > in_col2) || (in_col2 >= local_n_cols)) ) , "SpMat::submat(): indices out of bounds or incorrectly used" ); return SpSubview(*this, in_row1, in_col1, submat_n_rows, submat_n_cols); } template inline SpSubview SpMat::operator()(const span& row_span, const span& col_span) { arma_extra_debug_sigprint(); return submat(row_span, col_span); } template inline const SpSubview SpMat::operator()(const span& row_span, const span& col_span) const { arma_extra_debug_sigprint(); return submat(row_span, col_span); } template arma_inline SpSubview SpMat::operator()(const uword in_row1, const uword in_col1, const SizeMat& s) { arma_extra_debug_sigprint(); return (*this).submat(in_row1, in_col1, s); } template arma_inline const SpSubview SpMat::operator()(const uword in_row1, const uword in_col1, const SizeMat& s) const { arma_extra_debug_sigprint(); return (*this).submat(in_row1, in_col1, s); } template inline SpSubview SpMat::head_rows(const uword N) { arma_extra_debug_sigprint(); arma_debug_check_bounds( (N > n_rows), "SpMat::head_rows(): size out of bounds" ); return SpSubview(*this, 0, 0, N, n_cols); } template inline const SpSubview SpMat::head_rows(const uword N) const { arma_extra_debug_sigprint(); arma_debug_check_bounds( (N > n_rows), "SpMat::head_rows(): size out of bounds" ); return SpSubview(*this, 0, 0, N, n_cols); } template inline SpSubview SpMat::tail_rows(const uword N) { arma_extra_debug_sigprint(); arma_debug_check_bounds( (N > n_rows), "SpMat::tail_rows(): size out of bounds" ); const uword start_row = n_rows - N; return SpSubview(*this, start_row, 0, N, n_cols); } template inline const SpSubview SpMat::tail_rows(const uword N) const { arma_extra_debug_sigprint(); arma_debug_check_bounds( (N > n_rows), "SpMat::tail_rows(): size out of bounds" ); const uword start_row = n_rows - N; return SpSubview(*this, start_row, 0, N, n_cols); } template inline SpSubview SpMat::head_cols(const uword N) { arma_extra_debug_sigprint(); arma_debug_check_bounds( (N > n_cols), "SpMat::head_cols(): size out of bounds" ); return SpSubview(*this, 0, 0, n_rows, N); } template inline const SpSubview SpMat::head_cols(const uword N) const { arma_extra_debug_sigprint(); arma_debug_check_bounds( (N > n_cols), "SpMat::head_cols(): size out of bounds" ); return SpSubview(*this, 0, 0, n_rows, N); } template inline SpSubview SpMat::tail_cols(const uword N) { arma_extra_debug_sigprint(); arma_debug_check_bounds( (N > n_cols), "SpMat::tail_cols(): size out of bounds" ); const uword start_col = n_cols - N; return SpSubview(*this, 0, start_col, n_rows, N); } template inline const SpSubview SpMat::tail_cols(const uword N) const { arma_extra_debug_sigprint(); arma_debug_check_bounds( (N > n_cols), "SpMat::tail_cols(): size out of bounds" ); const uword start_col = n_cols - N; return SpSubview(*this, 0, start_col, n_rows, N); } template template arma_inline SpSubview_col_list SpMat::cols(const Base& indices) { arma_extra_debug_sigprint(); return SpSubview_col_list(*this, indices); } template template arma_inline const SpSubview_col_list SpMat::cols(const Base& indices) const { arma_extra_debug_sigprint(); return SpSubview_col_list(*this, indices); } //! creation of spdiagview (diagonal) template inline spdiagview SpMat::diag(const sword in_id) { arma_extra_debug_sigprint(); const uword row_offset = (in_id < 0) ? uword(-in_id) : 0; const uword col_offset = (in_id > 0) ? uword( in_id) : 0; arma_debug_check_bounds ( ((row_offset > 0) && (row_offset >= n_rows)) || ((col_offset > 0) && (col_offset >= n_cols)), "SpMat::diag(): requested diagonal out of bounds" ); const uword len = (std::min)(n_rows - row_offset, n_cols - col_offset); return spdiagview(*this, row_offset, col_offset, len); } //! creation of spdiagview (diagonal) template inline const spdiagview SpMat::diag(const sword in_id) const { arma_extra_debug_sigprint(); const uword row_offset = uword( (in_id < 0) ? -in_id : 0 ); const uword col_offset = uword( (in_id > 0) ? in_id : 0 ); arma_debug_check_bounds ( ((row_offset > 0) && (row_offset >= n_rows)) || ((col_offset > 0) && (col_offset >= n_cols)), "SpMat::diag(): requested diagonal out of bounds" ); const uword len = (std::min)(n_rows - row_offset, n_cols - col_offset); return spdiagview(*this, row_offset, col_offset, len); } template inline void SpMat::swap_rows(const uword in_row1, const uword in_row2) { arma_extra_debug_sigprint(); arma_debug_check_bounds( ((in_row1 >= n_rows) || (in_row2 >= n_rows)), "SpMat::swap_rows(): out of bounds" ); if(in_row1 == in_row2) { return; } sync_csc(); invalidate_cache(); // The easier way to do this, instead of collecting all the elements in one row and then swapping with the other, will be // to iterate over each column of the matrix (since we store in column-major format) and then swap the two elements in the two rows at that time. // We will try to avoid using the at() call since it is expensive, instead preferring to use an iterator to track our position. uword col1 = (in_row1 < in_row2) ? in_row1 : in_row2; uword col2 = (in_row1 < in_row2) ? in_row2 : in_row1; for(uword lcol = 0; lcol < n_cols; lcol++) { // If there is nothing in this column we can ignore it. if(col_ptrs[lcol] == col_ptrs[lcol + 1]) { continue; } // These will represent the positions of the items themselves. uword loc1 = n_nonzero + 1; uword loc2 = n_nonzero + 1; for(uword search_pos = col_ptrs[lcol]; search_pos < col_ptrs[lcol + 1]; search_pos++) { if(row_indices[search_pos] == col1) { loc1 = search_pos; } if(row_indices[search_pos] == col2) { loc2 = search_pos; break; // No need to look any further. } } // There are four cases: we found both elements; we found one element (loc1); we found one element (loc2); we found zero elements. // If we found zero elements no work needs to be done and we can continue to the next column. if((loc1 != (n_nonzero + 1)) && (loc2 != (n_nonzero + 1))) { // This is an easy case: just swap the values. No index modifying necessary. eT tmp = values[loc1]; access::rw(values[loc1]) = values[loc2]; access::rw(values[loc2]) = tmp; } else if(loc1 != (n_nonzero + 1)) // We only found loc1 and not loc2. { // We need to find the correct place to move our value to. It will be forward (not backwards) because in_row2 > in_row1. // Each iteration of the loop swaps the current value (loc1) with (loc1 + 1); in this manner we move our value down to where it should be. while(((loc1 + 1) < col_ptrs[lcol + 1]) && (row_indices[loc1 + 1] < in_row2)) { // Swap both the values and the indices. The column should not change. eT tmp = values[loc1]; access::rw(values[loc1]) = values[loc1 + 1]; access::rw(values[loc1 + 1]) = tmp; uword tmp_index = row_indices[loc1]; access::rw(row_indices[loc1]) = row_indices[loc1 + 1]; access::rw(row_indices[loc1 + 1]) = tmp_index; loc1++; // And increment the counter. } // Now set the row index correctly. access::rw(row_indices[loc1]) = in_row2; } else if(loc2 != (n_nonzero + 1)) { // We need to find the correct place to move our value to. It will be backwards (not forwards) because in_row1 < in_row2. // Each iteration of the loop swaps the current value (loc2) with (loc2 - 1); in this manner we move our value up to where it should be. while(((loc2 - 1) >= col_ptrs[lcol]) && (row_indices[loc2 - 1] > in_row1)) { // Swap both the values and the indices. The column should not change. eT tmp = values[loc2]; access::rw(values[loc2]) = values[loc2 - 1]; access::rw(values[loc2 - 1]) = tmp; uword tmp_index = row_indices[loc2]; access::rw(row_indices[loc2]) = row_indices[loc2 - 1]; access::rw(row_indices[loc2 - 1]) = tmp_index; loc2--; // And decrement the counter. } // Now set the row index correctly. access::rw(row_indices[loc2]) = in_row1; } /* else: no need to swap anything; both values are zero */ } } template inline void SpMat::swap_cols(const uword in_col1, const uword in_col2) { arma_extra_debug_sigprint(); arma_debug_check_bounds( ((in_col1 >= n_cols) || (in_col2 >= n_cols)), "SpMat::swap_cols(): out of bounds" ); if(in_col1 == in_col2) { return; } // TODO: this is a rudimentary implementation const SpMat tmp1 = (*this).col(in_col1); const SpMat tmp2 = (*this).col(in_col2); (*this).col(in_col2) = tmp1; (*this).col(in_col1) = tmp2; // for(uword lrow = 0; lrow < n_rows; ++lrow) // { // const eT tmp = at(lrow, in_col1); // at(lrow, in_col1) = eT( at(lrow, in_col2) ); // at(lrow, in_col2) = tmp; // } } template inline void SpMat::shed_row(const uword row_num) { arma_extra_debug_sigprint(); arma_debug_check_bounds(row_num >= n_rows, "SpMat::shed_row(): out of bounds"); shed_rows (row_num, row_num); } template inline void SpMat::shed_col(const uword col_num) { arma_extra_debug_sigprint(); arma_debug_check_bounds(col_num >= n_cols, "SpMat::shed_col(): out of bounds"); shed_cols(col_num, col_num); } template inline void SpMat::shed_rows(const uword in_row1, const uword in_row2) { arma_extra_debug_sigprint(); arma_debug_check_bounds ( (in_row1 > in_row2) || (in_row2 >= n_rows), "SpMat::shed_rows(): indices out of bounds or incorectly used" ); sync_csc(); SpMat newmat(n_rows - (in_row2 - in_row1 + 1), n_cols); // First, count the number of elements we will be removing. uword removing = 0; for(uword i = 0; i < n_nonzero; ++i) { const uword lrow = row_indices[i]; if(lrow >= in_row1 && lrow <= in_row2) { ++removing; } } // Obtain counts of the number of points in each column and store them as the // (invalid) column pointers of the new matrix. for(uword i = 1; i < n_cols + 1; ++i) { access::rw(newmat.col_ptrs[i]) = col_ptrs[i] - col_ptrs[i - 1]; } // Now initialize memory for the new matrix. newmat.mem_resize(n_nonzero - removing); // Now, copy over the elements. // i is the index in the old matrix; j is the index in the new matrix. const_iterator it = cbegin(); const_iterator it_end = cend(); uword j = 0; // The index in the new matrix. while(it != it_end) { const uword lrow = it.row(); const uword lcol = it.col(); if(lrow >= in_row1 && lrow <= in_row2) { // This element is being removed. Subtract it from the column counts. --access::rw(newmat.col_ptrs[lcol + 1]); } else { // This element is being kept. We may need to map the row index, // if it is past the section of rows we are removing. if(lrow > in_row2) { access::rw(newmat.row_indices[j]) = lrow - (in_row2 - in_row1 + 1); } else { access::rw(newmat.row_indices[j]) = lrow; } access::rw(newmat.values[j]) = (*it); ++j; // Increment index in new matrix. } ++it; } // Finally, sum the column counts so they are correct column pointers. for(uword i = 1; i < n_cols + 1; ++i) { access::rw(newmat.col_ptrs[i]) += newmat.col_ptrs[i - 1]; } // Now steal the memory of the new matrix. steal_mem(newmat); } template inline void SpMat::shed_cols(const uword in_col1, const uword in_col2) { arma_extra_debug_sigprint(); arma_debug_check_bounds ( (in_col1 > in_col2) || (in_col2 >= n_cols), "SpMat::shed_cols(): indices out of bounds or incorrectly used" ); sync_csc(); invalidate_cache(); // First we find the locations in values and row_indices for the column entries. uword col_beg = col_ptrs[in_col1]; uword col_end = col_ptrs[in_col2 + 1]; // Then we find the number of entries in the column. uword diff = col_end - col_beg; if(diff > 0) { eT* new_values = memory::acquire (n_nonzero + 1 - diff); uword* new_row_indices = memory::acquire(n_nonzero + 1 - diff); // Copy first part. if(col_beg != 0) { arrayops::copy(new_values, values, col_beg); arrayops::copy(new_row_indices, row_indices, col_beg); } // Copy second part. if(col_end != n_nonzero) { arrayops::copy(new_values + col_beg, values + col_end, n_nonzero - col_end); arrayops::copy(new_row_indices + col_beg, row_indices + col_end, n_nonzero - col_end); } // Copy sentry element. new_values[n_nonzero - diff] = values[n_nonzero]; new_row_indices[n_nonzero - diff] = row_indices[n_nonzero]; if(values) { memory::release(access::rw(values)); } if(row_indices) { memory::release(access::rw(row_indices)); } access::rw(values) = new_values; access::rw(row_indices) = new_row_indices; // Update counts and such. access::rw(n_nonzero) -= diff; } // Update column pointers. const uword new_n_cols = n_cols - ((in_col2 - in_col1) + 1); uword* new_col_ptrs = memory::acquire(new_n_cols + 2); new_col_ptrs[new_n_cols + 1] = std::numeric_limits::max(); // Copy first set of columns (no manipulation required). if(in_col1 != 0) { arrayops::copy(new_col_ptrs, col_ptrs, in_col1); } // Copy second set of columns (manipulation required). uword cur_col = in_col1; for(uword i = in_col2 + 1; i <= n_cols; ++i, ++cur_col) { new_col_ptrs[cur_col] = col_ptrs[i] - diff; } if(col_ptrs) { memory::release(access::rw(col_ptrs)); } access::rw(col_ptrs) = new_col_ptrs; // We update the element and column counts, and we're done. access::rw(n_cols) = new_n_cols; access::rw(n_elem) = n_cols * n_rows; } /** * Element access; acces the i'th element (works identically to the Mat accessors). * If there is nothing at element i, 0 is returned. */ template arma_inline SpMat_MapMat_val SpMat::operator[](const uword i) { const uword in_col = i / n_rows; const uword in_row = i % n_rows; return SpMat_MapMat_val((*this), cache, in_row, in_col); } template arma_inline eT SpMat::operator[](const uword i) const { return get_value(i); } template arma_inline SpMat_MapMat_val SpMat::at(const uword i) { const uword in_col = i / n_rows; const uword in_row = i % n_rows; return SpMat_MapMat_val((*this), cache, in_row, in_col); } template arma_inline eT SpMat::at(const uword i) const { return get_value(i); } template arma_inline SpMat_MapMat_val SpMat::operator()(const uword i) { arma_debug_check_bounds( (i >= n_elem), "SpMat::operator(): out of bounds" ); const uword in_col = i / n_rows; const uword in_row = i % n_rows; return SpMat_MapMat_val((*this), cache, in_row, in_col); } template arma_inline eT SpMat::operator()(const uword i) const { arma_debug_check_bounds( (i >= n_elem), "SpMat::operator(): out of bounds" ); return get_value(i); } /** * Element access; access the element at row in_rows and column in_col. * If there is nothing at that position, 0 is returned. */ #if defined(__cpp_multidimensional_subscript) template arma_inline SpMat_MapMat_val SpMat::operator[] (const uword in_row, const uword in_col) { return SpMat_MapMat_val((*this), cache, in_row, in_col); } template arma_inline eT SpMat::operator[] (const uword in_row, const uword in_col) const { return get_value(in_row, in_col); } #endif template arma_inline SpMat_MapMat_val SpMat::at(const uword in_row, const uword in_col) { return SpMat_MapMat_val((*this), cache, in_row, in_col); } template arma_inline eT SpMat::at(const uword in_row, const uword in_col) const { return get_value(in_row, in_col); } template arma_inline SpMat_MapMat_val SpMat::operator()(const uword in_row, const uword in_col) { arma_debug_check_bounds( ((in_row >= n_rows) || (in_col >= n_cols)), "SpMat::operator(): out of bounds" ); return SpMat_MapMat_val((*this), cache, in_row, in_col); } template arma_inline eT SpMat::operator()(const uword in_row, const uword in_col) const { arma_debug_check_bounds( ((in_row >= n_rows) || (in_col >= n_cols)), "SpMat::operator(): out of bounds" ); return get_value(in_row, in_col); } /** * Check if matrix is empty (no size, no values). */ template arma_inline bool SpMat::is_empty() const { return (n_elem == 0); } //! returns true if the object can be interpreted as a column or row vector template arma_inline bool SpMat::is_vec() const { return ( (n_rows == 1) || (n_cols == 1) ); } //! returns true if the object can be interpreted as a row vector template arma_inline bool SpMat::is_rowvec() const { return (n_rows == 1); } //! returns true if the object can be interpreted as a column vector template arma_inline bool SpMat::is_colvec() const { return (n_cols == 1); } //! returns true if the object has the same number of non-zero rows and columnns template arma_inline bool SpMat::is_square() const { return (n_rows == n_cols); } template inline bool SpMat::is_symmetric() const { arma_extra_debug_sigprint(); const SpMat& A = (*this); if(A.n_rows != A.n_cols) { return false; } const SpMat tmp = A - A.st(); return (tmp.n_nonzero == uword(0)); } template inline bool SpMat::is_symmetric(const typename get_pod_type::result tol) const { arma_extra_debug_sigprint(); typedef typename get_pod_type::result T; if(tol == T(0)) { return (*this).is_symmetric(); } arma_debug_check( (tol < T(0)), "is_symmetric(): parameter 'tol' must be >= 0" ); const SpMat& A = (*this); if(A.n_rows != A.n_cols) { return false; } const T norm_A = as_scalar( arma::max(sum(abs(A), 1), 0) ); if(norm_A == T(0)) { return true; } const T norm_A_Ast = as_scalar( arma::max(sum(abs(A - A.st()), 1), 0) ); return ( (norm_A_Ast / norm_A) <= tol ); } template inline bool SpMat::is_hermitian() const { arma_extra_debug_sigprint(); const SpMat& A = (*this); if(A.n_rows != A.n_cols) { return false; } const SpMat tmp = A - A.t(); return (tmp.n_nonzero == uword(0)); } template inline bool SpMat::is_hermitian(const typename get_pod_type::result tol) const { arma_extra_debug_sigprint(); typedef typename get_pod_type::result T; if(tol == T(0)) { return (*this).is_hermitian(); } arma_debug_check( (tol < T(0)), "is_hermitian(): parameter 'tol' must be >= 0" ); const SpMat& A = (*this); if(A.n_rows != A.n_cols) { return false; } const T norm_A = as_scalar( arma::max(sum(abs(A), 1), 0) ); if(norm_A == T(0)) { return true; } const T norm_A_At = as_scalar( arma::max(sum(abs(A - A.t()), 1), 0) ); return ( (norm_A_At / norm_A) <= tol ); } template inline bool SpMat::internal_is_finite() const { arma_extra_debug_sigprint(); sync_csc(); return arrayops::is_finite(values, n_nonzero); } template inline bool SpMat::internal_has_inf() const { arma_extra_debug_sigprint(); sync_csc(); return arrayops::has_inf(values, n_nonzero); } template inline bool SpMat::internal_has_nan() const { arma_extra_debug_sigprint(); sync_csc(); return arrayops::has_nan(values, n_nonzero); } template inline bool SpMat::internal_has_nonfinite() const { arma_extra_debug_sigprint(); sync_csc(); return (arrayops::is_finite(values, n_nonzero) == false); } //! returns true if the given index is currently in range template arma_inline bool SpMat::in_range(const uword i) const { return (i < n_elem); } //! returns true if the given start and end indices are currently in range template arma_inline bool SpMat::in_range(const span& x) const { arma_extra_debug_sigprint(); if(x.whole) { return true; } else { const uword a = x.a; const uword b = x.b; return ( (a <= b) && (b < n_elem) ); } } //! returns true if the given location is currently in range template arma_inline bool SpMat::in_range(const uword in_row, const uword in_col) const { return ( (in_row < n_rows) && (in_col < n_cols) ); } template arma_inline bool SpMat::in_range(const span& row_span, const uword in_col) const { arma_extra_debug_sigprint(); if(row_span.whole) { return (in_col < n_cols); } else { const uword in_row1 = row_span.a; const uword in_row2 = row_span.b; return ( (in_row1 <= in_row2) && (in_row2 < n_rows) && (in_col < n_cols) ); } } template arma_inline bool SpMat::in_range(const uword in_row, const span& col_span) const { arma_extra_debug_sigprint(); if(col_span.whole) { return (in_row < n_rows); } else { const uword in_col1 = col_span.a; const uword in_col2 = col_span.b; return ( (in_row < n_rows) && (in_col1 <= in_col2) && (in_col2 < n_cols) ); } } template arma_inline bool SpMat::in_range(const span& row_span, const span& col_span) const { arma_extra_debug_sigprint(); const uword in_row1 = row_span.a; const uword in_row2 = row_span.b; const uword in_col1 = col_span.a; const uword in_col2 = col_span.b; const bool rows_ok = row_span.whole ? true : ( (in_row1 <= in_row2) && (in_row2 < n_rows) ); const bool cols_ok = col_span.whole ? true : ( (in_col1 <= in_col2) && (in_col2 < n_cols) ); return ( rows_ok && cols_ok ); } template arma_inline bool SpMat::in_range(const uword in_row, const uword in_col, const SizeMat& s) const { const uword l_n_rows = n_rows; const uword l_n_cols = n_cols; if( (in_row >= l_n_rows) || (in_col >= l_n_cols) || ((in_row + s.n_rows) > l_n_rows) || ((in_col + s.n_cols) > l_n_cols) ) { return false; } else { return true; } } //! Set the size to the size of another matrix. template template inline SpMat& SpMat::copy_size(const SpMat& m) { arma_extra_debug_sigprint(); return set_size(m.n_rows, m.n_cols); } template template inline SpMat& SpMat::copy_size(const Mat& m) { arma_extra_debug_sigprint(); return set_size(m.n_rows, m.n_cols); } template inline SpMat& SpMat::set_size(const uword in_elem) { arma_extra_debug_sigprint(); // If this is a row vector, we resize to a row vector. if(vec_state == 2) { set_size(1, in_elem); } else { set_size(in_elem, 1); } return *this; } template inline SpMat& SpMat::set_size(const uword in_rows, const uword in_cols) { arma_extra_debug_sigprint(); invalidate_cache(); // placed here, as set_size() is used during matrix modification if( (n_rows == in_rows) && (n_cols == in_cols) ) { return *this; } init(in_rows, in_cols); return *this; } template inline SpMat& SpMat::set_size(const SizeMat& s) { arma_extra_debug_sigprint(); return (*this).set_size(s.n_rows, s.n_cols); } template inline SpMat& SpMat::resize(const uword in_rows, const uword in_cols) { arma_extra_debug_sigprint(); if( (n_rows == in_rows) && (n_cols == in_cols) ) { return *this; } if( (n_elem == 0) || (n_nonzero == 0) ) { return set_size(in_rows, in_cols); } SpMat tmp(in_rows, in_cols); if(tmp.n_elem > 0) { sync_csc(); const uword last_row = (std::min)(in_rows, n_rows) - 1; const uword last_col = (std::min)(in_cols, n_cols) - 1; tmp.submat(0, 0, last_row, last_col) = (*this).submat(0, 0, last_row, last_col); } steal_mem(tmp); return *this; } template inline SpMat& SpMat::resize(const SizeMat& s) { arma_extra_debug_sigprint(); return (*this).resize(s.n_rows, s.n_cols); } template inline SpMat& SpMat::reshape(const uword in_rows, const uword in_cols) { arma_extra_debug_sigprint(); arma_check( ((in_rows*in_cols) != n_elem), "SpMat::reshape(): changing the number of elements in a sparse matrix is currently not supported" ); if( (n_rows == in_rows) && (n_cols == in_cols) ) { return *this; } if(vec_state == 1) { arma_debug_check( (in_cols != 1), "SpMat::reshape(): object is a column vector; requested size is not compatible" ); } if(vec_state == 2) { arma_debug_check( (in_rows != 1), "SpMat::reshape(): object is a row vector; requested size is not compatible" ); } if(n_nonzero == 0) { return (*this).zeros(in_rows, in_cols); } if(in_cols == 1) { (*this).reshape_helper_intovec(); } else { (*this).reshape_helper_generic(in_rows, in_cols); } return *this; } template inline SpMat& SpMat::reshape(const SizeMat& s) { arma_extra_debug_sigprint(); return (*this).reshape(s.n_rows, s.n_cols); } template inline void SpMat::reshape_helper_generic(const uword in_rows, const uword in_cols) { arma_extra_debug_sigprint(); sync_csc(); invalidate_cache(); // We have to modify all of the relevant row indices and the relevant column pointers. // Iterate over all the points to do this. We won't be deleting any points, but we will be modifying // columns and rows. We'll have to store a new set of column vectors. uword* new_col_ptrs = memory::acquire(in_cols + 2); new_col_ptrs[in_cols + 1] = std::numeric_limits::max(); uword* new_row_indices = memory::acquire(n_nonzero + 1); access::rw(new_row_indices[n_nonzero]) = 0; arrayops::fill_zeros(new_col_ptrs, in_cols + 1); const_iterator it = cbegin(); const_iterator it_end = cend(); for(; it != it_end; ++it) { uword vector_position = (it.col() * n_rows) + it.row(); new_row_indices[it.pos()] = vector_position % in_rows; ++new_col_ptrs[vector_position / in_rows + 1]; } // Now sum the column counts to get the new column pointers. for(uword i = 1; i <= in_cols; i++) { access::rw(new_col_ptrs[i]) += new_col_ptrs[i - 1]; } // Copy the new row indices. if(row_indices) { memory::release(access::rw(row_indices)); } if(col_ptrs) { memory::release(access::rw(col_ptrs)); } access::rw(row_indices) = new_row_indices; access::rw(col_ptrs) = new_col_ptrs; // Now set the size. access::rw(n_rows) = in_rows; access::rw(n_cols) = in_cols; } template inline void SpMat::reshape_helper_intovec() { arma_extra_debug_sigprint(); sync_csc(); invalidate_cache(); const_iterator it = cbegin(); const uword t_n_rows = n_rows; const uword t_n_nonzero = n_nonzero; for(uword i=0; i < t_n_nonzero; ++i) { const uword t_index = (it.col() * t_n_rows) + it.row(); // ensure the iterator is pointing to the next element // before we overwrite the row index of the current element ++it; access::rw(row_indices[i]) = t_index; } access::rw(row_indices[n_nonzero]) = 0; access::rw(col_ptrs[0]) = 0; access::rw(col_ptrs[1]) = n_nonzero; access::rw(col_ptrs[2]) = std::numeric_limits::max(); access::rw(n_rows) = (n_rows * n_cols); access::rw(n_cols) = 1; } //! apply a functor to each non-zero element template template inline SpMat& SpMat::for_each(functor F) { arma_extra_debug_sigprint(); sync_csc(); const uword N = (*this).n_nonzero; eT* rw_values = access::rwp(values); bool modified = false; bool has_zero = false; for(uword i=0; i < N; ++i) { eT& new_value = rw_values[i]; const eT old_value = new_value; F(new_value); if(new_value != old_value) { modified = true; } if(new_value == eT(0) ) { has_zero = true; } } if(modified) { invalidate_cache(); } if(has_zero) { remove_zeros(); } return *this; } template template inline const SpMat& SpMat::for_each(functor F) const { arma_extra_debug_sigprint(); sync_csc(); const uword N = (*this).n_nonzero; for(uword i=0; i < N; ++i) { F(values[i]); } return *this; } //! transform each non-zero element using a functor template template inline SpMat& SpMat::transform(functor F) { arma_extra_debug_sigprint(); sync_csc(); invalidate_cache(); const uword N = (*this).n_nonzero; eT* rw_values = access::rwp(values); bool has_zero = false; for(uword i=0; i < N; ++i) { eT& rw_values_i = rw_values[i]; rw_values_i = eT( F(rw_values_i) ); if(rw_values_i == eT(0)) { has_zero = true; } } if(has_zero) { remove_zeros(); } return *this; } template inline SpMat& SpMat::replace(const eT old_val, const eT new_val) { arma_extra_debug_sigprint(); if(old_val == eT(0)) { arma_debug_warn_level(1, "SpMat::replace(): replacement not done, as old_val = 0"); } else { sync_csc(); invalidate_cache(); arrayops::replace(access::rwp(values), n_nonzero, old_val, new_val); if(new_val == eT(0)) { remove_zeros(); } } return *this; } template inline SpMat& SpMat::clean(const typename get_pod_type::result threshold) { arma_extra_debug_sigprint(); if(n_nonzero == 0) { return *this; } sync_csc(); invalidate_cache(); arrayops::clean(access::rwp(values), n_nonzero, threshold); remove_zeros(); return *this; } template inline SpMat& SpMat::clamp(const eT min_val, const eT max_val) { arma_extra_debug_sigprint(); if(is_cx::no) { arma_debug_check( (access::tmp_real(min_val) > access::tmp_real(max_val)), "SpMat::clamp(): min_val must be less than max_val" ); } else { arma_debug_check( (access::tmp_real(min_val) > access::tmp_real(max_val)), "SpMat::clamp(): real(min_val) must be less than real(max_val)" ); arma_debug_check( (access::tmp_imag(min_val) > access::tmp_imag(max_val)), "SpMat::clamp(): imag(min_val) must be less than imag(max_val)" ); } if(n_nonzero == 0) { return *this; } sync_csc(); invalidate_cache(); arrayops::clamp(access::rwp(values), n_nonzero, min_val, max_val); if( (min_val == eT(0)) || (max_val == eT(0)) ) { remove_zeros(); } return *this; } template inline SpMat& SpMat::zeros() { arma_extra_debug_sigprint(); if((n_nonzero == 0) && (values != nullptr)) { invalidate_cache(); } else { init(n_rows, n_cols); } return *this; } template inline SpMat& SpMat::zeros(const uword in_elem) { arma_extra_debug_sigprint(); if(vec_state == 2) { zeros(1, in_elem); // Row vector } else { zeros(in_elem, 1); } return *this; } template inline SpMat& SpMat::zeros(const uword in_rows, const uword in_cols) { arma_extra_debug_sigprint(); if((n_nonzero == 0) && (n_rows == in_rows) && (n_cols == in_cols) && (values != nullptr)) { invalidate_cache(); } else { init(in_rows, in_cols); } return *this; } template inline SpMat& SpMat::zeros(const SizeMat& s) { arma_extra_debug_sigprint(); return (*this).zeros(s.n_rows, s.n_cols); } template inline SpMat& SpMat::eye() { arma_extra_debug_sigprint(); return (*this).eye(n_rows, n_cols); } template inline SpMat& SpMat::eye(const uword in_rows, const uword in_cols) { arma_extra_debug_sigprint(); const uword N = (std::min)(in_rows, in_cols); init(in_rows, in_cols, N); arrayops::inplace_set(access::rwp(values), eT(1), N); for(uword i = 0; i < N; ++i) { access::rw(row_indices[i]) = i; } for(uword i = 0; i <= N; ++i) { access::rw(col_ptrs[i]) = i; } // take into account non-square matrices for(uword i = (N+1); i <= in_cols; ++i) { access::rw(col_ptrs[i]) = N; } access::rw(n_nonzero) = N; return *this; } template inline SpMat& SpMat::eye(const SizeMat& s) { arma_extra_debug_sigprint(); return (*this).eye(s.n_rows, s.n_cols); } template inline SpMat& SpMat::speye() { arma_extra_debug_sigprint(); return (*this).eye(n_rows, n_cols); } template inline SpMat& SpMat::speye(const uword in_n_rows, const uword in_n_cols) { arma_extra_debug_sigprint(); return (*this).eye(in_n_rows, in_n_cols); } template inline SpMat& SpMat::speye(const SizeMat& s) { arma_extra_debug_sigprint(); return (*this).eye(s.n_rows, s.n_cols); } template inline SpMat& SpMat::sprandu(const uword in_rows, const uword in_cols, const double density) { arma_extra_debug_sigprint(); arma_debug_check( ( (density < double(0)) || (density > double(1)) ), "sprandu(): density must be in the [0,1] interval" ); const uword new_n_nonzero = uword(density * double(in_rows) * double(in_cols) + 0.5); init(in_rows, in_cols, new_n_nonzero); if(new_n_nonzero == 0) { return *this; } arma_rng::randu::fill( access::rwp(values), new_n_nonzero ); uvec indices = linspace( 0u, in_rows*in_cols-1, new_n_nonzero ); // perturb the indices for(uword i=1; i < new_n_nonzero-1; ++i) { const uword index_left = indices[i-1]; const uword index_right = indices[i+1]; const uword center = (index_left + index_right) / 2; const uword delta1 = center - index_left - 1; const uword delta2 = index_right - center - 1; const uword min_delta = (std::min)(delta1, delta2); uword index_new = uword( double(center) + double(min_delta) * (2.0*randu()-1.0) ); // paranoia, but better be safe than sorry if( (index_left < index_new) && (index_new < index_right) ) { indices[i] = index_new; } } uword cur_index = 0; uword count = 0; for(uword lcol = 0; lcol < in_cols; ++lcol) for(uword lrow = 0; lrow < in_rows; ++lrow) { if(count == indices[cur_index]) { access::rw(row_indices[cur_index]) = lrow; access::rw(col_ptrs[lcol + 1])++; ++cur_index; } ++count; } if(cur_index != new_n_nonzero) { // Fix size to correct size. mem_resize(cur_index); } // Sum column pointers. for(uword lcol = 1; lcol <= in_cols; ++lcol) { access::rw(col_ptrs[lcol]) += col_ptrs[lcol - 1]; } return *this; } template inline SpMat& SpMat::sprandu(const SizeMat& s, const double density) { arma_extra_debug_sigprint(); return (*this).sprandu(s.n_rows, s.n_cols, density); } template inline SpMat& SpMat::sprandn(const uword in_rows, const uword in_cols, const double density) { arma_extra_debug_sigprint(); arma_debug_check( ( (density < double(0)) || (density > double(1)) ), "sprandn(): density must be in the [0,1] interval" ); const uword new_n_nonzero = uword(density * double(in_rows) * double(in_cols) + 0.5); init(in_rows, in_cols, new_n_nonzero); if(new_n_nonzero == 0) { return *this; } arma_rng::randn::fill( access::rwp(values), new_n_nonzero ); uvec indices = linspace( 0u, in_rows*in_cols-1, new_n_nonzero ); // perturb the indices for(uword i=1; i < new_n_nonzero-1; ++i) { const uword index_left = indices[i-1]; const uword index_right = indices[i+1]; const uword center = (index_left + index_right) / 2; const uword delta1 = center - index_left - 1; const uword delta2 = index_right - center - 1; const uword min_delta = (std::min)(delta1, delta2); uword index_new = uword( double(center) + double(min_delta) * (2.0*randu()-1.0) ); // paranoia, but better be safe than sorry if( (index_left < index_new) && (index_new < index_right) ) { indices[i] = index_new; } } uword cur_index = 0; uword count = 0; for(uword lcol = 0; lcol < in_cols; ++lcol) for(uword lrow = 0; lrow < in_rows; ++lrow) { if(count == indices[cur_index]) { access::rw(row_indices[cur_index]) = lrow; access::rw(col_ptrs[lcol + 1])++; ++cur_index; } ++count; } if(cur_index != new_n_nonzero) { // Fix size to correct size. mem_resize(cur_index); } // Sum column pointers. for(uword lcol = 1; lcol <= in_cols; ++lcol) { access::rw(col_ptrs[lcol]) += col_ptrs[lcol - 1]; } return *this; } template inline SpMat& SpMat::sprandn(const SizeMat& s, const double density) { arma_extra_debug_sigprint(); return (*this).sprandn(s.n_rows, s.n_cols, density); } template inline void SpMat::reset() { arma_extra_debug_sigprint(); switch(vec_state) { default: init(0, 0); break; case 1: init(0, 1); break; case 2: init(1, 0); break; } } template inline void SpMat::reset_cache() { arma_extra_debug_sigprint(); sync_csc(); #if defined(ARMA_USE_OPENMP) { #pragma omp critical (arma_SpMat_cache) { cache.reset(); sync_state = 0; } } #elif (!defined(ARMA_DONT_USE_STD_MUTEX)) { const std::lock_guard lock(cache_mutex); cache.reset(); sync_state = 0; } #else { cache.reset(); sync_state = 0; } #endif } template inline void SpMat::reserve(const uword in_rows, const uword in_cols, const uword new_n_nonzero) { arma_extra_debug_sigprint(); init(in_rows, in_cols, new_n_nonzero); } template template inline void SpMat::set_real(const SpBase::pod_type,T1>& X) { arma_extra_debug_sigprint(); SpMat_aux::set_real(*this, X); } template template inline void SpMat::set_imag(const SpBase::pod_type,T1>& X) { arma_extra_debug_sigprint(); SpMat_aux::set_imag(*this, X); } //! save the matrix to a file template inline bool SpMat::save(const std::string name, const file_type type) const { arma_extra_debug_sigprint(); sync_csc(); bool save_okay; switch(type) { case csv_ascii: return (*this).save(csv_name(name), type); break; case ssv_ascii: return (*this).save(csv_name(name), type); break; case arma_binary: save_okay = diskio::save_arma_binary(*this, name); break; case coord_ascii: save_okay = diskio::save_coord_ascii(*this, name); break; default: arma_debug_warn_level(1, "SpMat::save(): unsupported file type"); save_okay = false; } if(save_okay == false) { arma_debug_warn_level(3, "SpMat::save(): write failed; file: ", name); } return save_okay; } template inline bool SpMat::save(const csv_name& spec, const file_type type) const { arma_extra_debug_sigprint(); if( (type != csv_ascii) && (type != ssv_ascii) ) { arma_stop_runtime_error("SpMat::save(): unsupported file type for csv_name()"); return false; } const bool do_trans = bool(spec.opts.flags & csv_opts::flag_trans ); const bool no_header = bool(spec.opts.flags & csv_opts::flag_no_header ); const bool with_header = bool(spec.opts.flags & csv_opts::flag_with_header) && (no_header == false); const bool use_semicolon = bool(spec.opts.flags & csv_opts::flag_semicolon ) || (type == ssv_ascii); arma_extra_debug_print("SpMat::save(csv_name): enabled flags:"); if(do_trans ) { arma_extra_debug_print("trans"); } if(no_header ) { arma_extra_debug_print("no_header"); } if(with_header ) { arma_extra_debug_print("with_header"); } if(use_semicolon) { arma_extra_debug_print("semicolon"); } const char separator = (use_semicolon) ? char(';') : char(','); if(with_header) { if( (spec.header_ro.n_cols != 1) && (spec.header_ro.n_rows != 1) ) { arma_debug_warn_level(1, "SpMat::save(): given header must have a vector layout"); return false; } for(uword i=0; i < spec.header_ro.n_elem; ++i) { const std::string& token = spec.header_ro.at(i); if(token.find(separator) != std::string::npos) { arma_debug_warn_level(1, "SpMat::save(): token within the header contains the separator character: '", token, "'"); return false; } } const uword save_n_cols = (do_trans) ? (*this).n_rows : (*this).n_cols; if(spec.header_ro.n_elem != save_n_cols) { arma_debug_warn_level(1, "SpMat::save(): size mismatch between header and matrix"); return false; } } bool save_okay = false; if(do_trans) { const SpMat tmp = (*this).st(); save_okay = diskio::save_csv_ascii(tmp, spec.filename, spec.header_ro, with_header, separator); } else { save_okay = diskio::save_csv_ascii(*this, spec.filename, spec.header_ro, with_header, separator); } if(save_okay == false) { arma_debug_warn_level(3, "SpMat::save(): write failed; file: ", spec.filename); } return save_okay; } //! save the matrix to a stream template inline bool SpMat::save(std::ostream& os, const file_type type) const { arma_extra_debug_sigprint(); sync_csc(); bool save_okay; switch(type) { case csv_ascii: save_okay = diskio::save_csv_ascii(*this, os, char(',')); break; case ssv_ascii: save_okay = diskio::save_csv_ascii(*this, os, char(';')); break; case arma_binary: save_okay = diskio::save_arma_binary(*this, os); break; case coord_ascii: save_okay = diskio::save_coord_ascii(*this, os); break; default: arma_debug_warn_level(1, "SpMat::save(): unsupported file type"); save_okay = false; } if(save_okay == false) { arma_debug_warn_level(3, "SpMat::save(): stream write failed"); } return save_okay; } //! load a matrix from a file template inline bool SpMat::load(const std::string name, const file_type type) { arma_extra_debug_sigprint(); invalidate_cache(); bool load_okay; std::string err_msg; switch(type) { // case auto_detect: // load_okay = diskio::load_auto_detect(*this, name, err_msg); // break; case csv_ascii: return (*this).load(csv_name(name), type); break; case ssv_ascii: return (*this).load(csv_name(name), type); break; case arma_binary: load_okay = diskio::load_arma_binary(*this, name, err_msg); break; case coord_ascii: load_okay = diskio::load_coord_ascii(*this, name, err_msg); break; default: arma_debug_warn_level(1, "SpMat::load(): unsupported file type"); load_okay = false; } if(load_okay == false) { if(err_msg.length() > 0) { arma_debug_warn_level(3, "SpMat::load(): ", err_msg, "; file: ", name); } else { arma_debug_warn_level(3, "SpMat::load(): read failed; file: ", name); } } if(load_okay == false) { (*this).reset(); } return load_okay; } template inline bool SpMat::load(const csv_name& spec, const file_type type) { arma_extra_debug_sigprint(); if( (type != csv_ascii) && (type != ssv_ascii) ) { arma_stop_runtime_error("SpMat::load(): unsupported file type for csv_name()"); return false; } const bool do_trans = bool(spec.opts.flags & csv_opts::flag_trans ); const bool no_header = bool(spec.opts.flags & csv_opts::flag_no_header ); const bool with_header = bool(spec.opts.flags & csv_opts::flag_with_header) && (no_header == false); const bool use_semicolon = bool(spec.opts.flags & csv_opts::flag_semicolon ) || (type == ssv_ascii); const bool strict = bool(spec.opts.flags & csv_opts::flag_strict ); arma_extra_debug_print("SpMat::load(csv_name): enabled flags:"); if(do_trans ) { arma_extra_debug_print("trans"); } if(no_header ) { arma_extra_debug_print("no_header"); } if(with_header ) { arma_extra_debug_print("with_header"); } if(use_semicolon) { arma_extra_debug_print("semicolon"); } if(strict ) { arma_extra_debug_print("strict"); } if(strict) { arma_debug_warn_level(1, "SpMat::load(): option 'strict' not implemented for sparse matrices"); } const char separator = (use_semicolon) ? char(';') : char(','); bool load_okay = false; std::string err_msg; if(do_trans) { SpMat tmp_mat; load_okay = diskio::load_csv_ascii(tmp_mat, spec.filename, err_msg, spec.header_rw, with_header, separator); if(load_okay) { (*this) = tmp_mat.st(); if(with_header) { // field::set_size() preserves data if the number of elements hasn't changed spec.header_rw.set_size(spec.header_rw.n_elem, 1); } } } else { load_okay = diskio::load_csv_ascii(*this, spec.filename, err_msg, spec.header_rw, with_header, separator); } if(load_okay == false) { if(err_msg.length() > 0) { arma_debug_warn_level(3, "SpMat::load(): ", err_msg, "; file: ", spec.filename); } else { arma_debug_warn_level(3, "SpMat::load(): read failed; file: ", spec.filename); } } else { const uword load_n_cols = (do_trans) ? (*this).n_rows : (*this).n_cols; if(with_header && (spec.header_rw.n_elem != load_n_cols)) { arma_debug_warn_level(3, "SpMat::load(): size mismatch between header and matrix"); } } if(load_okay == false) { (*this).reset(); if(with_header) { spec.header_rw.reset(); } } return load_okay; } //! load a matrix from a stream template inline bool SpMat::load(std::istream& is, const file_type type) { arma_extra_debug_sigprint(); invalidate_cache(); bool load_okay; std::string err_msg; switch(type) { // case auto_detect: // load_okay = diskio::load_auto_detect(*this, is, err_msg); // break; case csv_ascii: load_okay = diskio::load_csv_ascii(*this, is, err_msg, char(',')); break; case ssv_ascii: load_okay = diskio::load_csv_ascii(*this, is, err_msg, char(';')); break; case arma_binary: load_okay = diskio::load_arma_binary(*this, is, err_msg); break; case coord_ascii: load_okay = diskio::load_coord_ascii(*this, is, err_msg); break; default: arma_debug_warn_level(1, "SpMat::load(): unsupported file type"); load_okay = false; } if(load_okay == false) { if(err_msg.length() > 0) { arma_debug_warn_level(3, "SpMat::load(): ", err_msg); } else { arma_debug_warn_level(3, "SpMat::load(): stream read failed"); } } if(load_okay == false) { (*this).reset(); } return load_okay; } template inline bool SpMat::quiet_save(const std::string name, const file_type type) const { arma_extra_debug_sigprint(); return (*this).save(name, type); } template inline bool SpMat::quiet_save(std::ostream& os, const file_type type) const { arma_extra_debug_sigprint(); return (*this).save(os, type); } template inline bool SpMat::quiet_load(const std::string name, const file_type type) { arma_extra_debug_sigprint(); return (*this).load(name, type); } template inline bool SpMat::quiet_load(std::istream& is, const file_type type) { arma_extra_debug_sigprint(); return (*this).load(is, type); } /** * Initialize the matrix to the specified size. Data is not preserved, so the matrix is assumed to be entirely sparse (empty). */ template inline void SpMat::init(uword in_rows, uword in_cols, const uword new_n_nonzero) { arma_extra_debug_sigprint(); invalidate_cache(); // placed here, as init() is used during matrix modification // Clean out the existing memory. if(values ) { memory::release(access::rw(values)); } if(row_indices) { memory::release(access::rw(row_indices)); } if(col_ptrs ) { memory::release(access::rw(col_ptrs)); } // in case init_cold() throws an exception access::rw(n_rows) = 0; access::rw(n_cols) = 0; access::rw(n_elem) = 0; access::rw(n_nonzero) = 0; access::rw(values) = nullptr; access::rw(row_indices) = nullptr; access::rw(col_ptrs) = nullptr; init_cold(in_rows, in_cols, new_n_nonzero); } template inline void SpMat::init_cold(uword in_rows, uword in_cols, const uword new_n_nonzero) { arma_extra_debug_sigprint(); // Verify that we are allowed to do this. if(vec_state > 0) { if((in_rows == 0) && (in_cols == 0)) { if(vec_state == 1) { in_cols = 1; } if(vec_state == 2) { in_rows = 1; } } else { if(vec_state == 1) { arma_debug_check( (in_cols != 1), "SpMat::init(): object is a column vector; requested size is not compatible" ); } if(vec_state == 2) { arma_debug_check( (in_rows != 1), "SpMat::init(): object is a row vector; requested size is not compatible" ); } } } #if defined(ARMA_64BIT_WORD) const char* error_message = "SpMat::init(): requested size is too large"; #else const char* error_message = "SpMat::init(): requested size is too large; suggest to enable ARMA_64BIT_WORD"; #endif // Ensure that n_elem can hold the result of (n_rows * n_cols) arma_debug_check ( ( ( (in_rows > ARMA_MAX_UHWORD) || (in_cols > ARMA_MAX_UHWORD) ) ? ( (double(in_rows) * double(in_cols)) > double(ARMA_MAX_UWORD) ) : false ), error_message ); access::rw(col_ptrs) = memory::acquire(in_cols + 2); access::rw(values) = memory::acquire (new_n_nonzero + 1); access::rw(row_indices) = memory::acquire(new_n_nonzero + 1); // fill column pointers with 0, // except for the last element which contains the maximum possible element // (so iterators terminate correctly). arrayops::fill_zeros(access::rwp(col_ptrs), in_cols + 1); access::rw(col_ptrs[in_cols + 1]) = std::numeric_limits::max(); access::rw( values[new_n_nonzero]) = 0; access::rw(row_indices[new_n_nonzero]) = 0; // Set the new size accordingly. access::rw(n_rows) = in_rows; access::rw(n_cols) = in_cols; access::rw(n_elem) = (in_rows * in_cols); access::rw(n_nonzero) = new_n_nonzero; } template inline void SpMat::init(const std::string& text) { arma_extra_debug_sigprint(); Mat tmp(text); if(vec_state == 1) { if((tmp.n_elem > 0) && tmp.is_vec()) { access::rw(tmp.n_rows) = tmp.n_elem; access::rw(tmp.n_cols) = 1; } } if(vec_state == 2) { if((tmp.n_elem > 0) && tmp.is_vec()) { access::rw(tmp.n_rows) = 1; access::rw(tmp.n_cols) = tmp.n_elem; } } (*this).operator=(tmp); } template inline void SpMat::init(const SpMat& x) { arma_extra_debug_sigprint(); if(this == &x) { return; } bool init_done = false; #if defined(ARMA_USE_OPENMP) if(x.sync_state == 1) { #pragma omp critical (arma_SpMat_init) if(x.sync_state == 1) { (*this).init(x.cache); init_done = true; } } #elif (!defined(ARMA_DONT_USE_STD_MUTEX)) if(x.sync_state == 1) { const std::lock_guard lock(x.cache_mutex); if(x.sync_state == 1) { (*this).init(x.cache); init_done = true; } } #else if(x.sync_state == 1) { (*this).init(x.cache); init_done = true; } #endif if(init_done == false) { (*this).init_simple(x); } } template inline void SpMat::init(const MapMat& x) { arma_extra_debug_sigprint(); const uword x_n_rows = x.n_rows; const uword x_n_cols = x.n_cols; const uword x_n_nz = x.get_n_nonzero(); init(x_n_rows, x_n_cols, x_n_nz); if(x_n_nz == 0) { return; } typename MapMat::map_type& x_map_ref = *(x.map_ptr); typename MapMat::map_type::const_iterator x_it = x_map_ref.begin(); uword x_col = 0; uword x_col_index_start = 0; uword x_col_index_endp1 = x_n_rows; for(uword i=0; i < x_n_nz; ++i) { const std::pair& x_entry = (*x_it); const uword x_index = x_entry.first; const eT x_val = x_entry.second; // have we gone past the curent column? if(x_index >= x_col_index_endp1) { x_col = x_index / x_n_rows; x_col_index_start = x_col * x_n_rows; x_col_index_endp1 = x_col_index_start + x_n_rows; } const uword x_row = x_index - x_col_index_start; // // sanity check // // const uword tmp_x_row = x_index % x_n_rows; // const uword tmp_x_col = x_index / x_n_rows; // // if(x_row != tmp_x_row) { cout << "x_row != tmp_x_row" << endl; exit(-1); } // if(x_col != tmp_x_col) { cout << "x_col != tmp_x_col" << endl; exit(-1); } access::rw(values[i]) = x_val; access::rw(row_indices[i]) = x_row; access::rw(col_ptrs[ x_col + 1 ])++; ++x_it; } for(uword i = 0; i < x_n_cols; ++i) { access::rw(col_ptrs[i + 1]) += col_ptrs[i]; } // // OLD METHOD // // for(uword i=0; i < x_n_nz; ++i) // { // const std::pair& x_entry = (*x_it); // // const uword x_index = x_entry.first; // const eT x_val = x_entry.second; // // const uword x_row = x_index % x_n_rows; // const uword x_col = x_index / x_n_rows; // // access::rw(values[i]) = x_val; // access::rw(row_indices[i]) = x_row; // // access::rw(col_ptrs[ x_col + 1 ])++; // // ++x_it; // } // // // for(uword i = 0; i < x_n_cols; ++i) // { // access::rw(col_ptrs[i + 1]) += col_ptrs[i]; // } } template inline void SpMat::init_simple(const SpMat& x) { arma_extra_debug_sigprint(); if(this == &x) { return; } if((x.n_nonzero == 0) && (n_nonzero == 0) && (n_rows == x.n_rows) && (n_cols == x.n_cols) && (values != nullptr)) { invalidate_cache(); } else { init(x.n_rows, x.n_cols, x.n_nonzero); } if(x.n_nonzero != 0) { if(x.values ) { arrayops::copy(access::rwp(values), x.values, x.n_nonzero + 1); } if(x.row_indices) { arrayops::copy(access::rwp(row_indices), x.row_indices, x.n_nonzero + 1); } if(x.col_ptrs ) { arrayops::copy(access::rwp(col_ptrs), x.col_ptrs, x.n_cols + 1); } } } template inline void SpMat::init_batch_std(const Mat& locs, const Mat& vals, const bool sort_locations) { arma_extra_debug_sigprint(); // Resize to correct number of elements. mem_resize(vals.n_elem); // Reset column pointers to zero. arrayops::fill_zeros(access::rwp(col_ptrs), n_cols + 1); bool actually_sorted = true; if(sort_locations) { // check if we really need a time consuming sort const uword locs_n_cols = locs.n_cols; for(uword i = 1; i < locs_n_cols; ++i) { const uword* locs_i = locs.colptr(i ); const uword* locs_im1 = locs.colptr(i-1); const uword row_i = locs_i[0]; const uword col_i = locs_i[1]; const uword row_im1 = locs_im1[0]; const uword col_im1 = locs_im1[1]; if( (col_i < col_im1) || ((col_i == col_im1) && (row_i <= row_im1)) ) { actually_sorted = false; break; } } if(actually_sorted == false) { // see op_sort_index_bones.hpp for the definition of arma_sort_index_packet and arma_sort_index_helper_ascend std::vector< arma_sort_index_packet > packet_vec(locs_n_cols); const uword* locs_mem = locs.memptr(); for(uword i = 0; i < locs_n_cols; ++i) { const uword row = (*locs_mem); locs_mem++; const uword col = (*locs_mem); locs_mem++; packet_vec[i].val = (col * n_rows) + row; packet_vec[i].index = i; } arma_sort_index_helper_ascend comparator; std::sort( packet_vec.begin(), packet_vec.end(), comparator ); // insert the elements in the sorted order for(uword i = 0; i < locs_n_cols; ++i) { const uword index = packet_vec[i].index; const uword* locs_i = locs.colptr(index); const uword row_i = locs_i[0]; const uword col_i = locs_i[1]; arma_debug_check( ( (row_i >= n_rows) || (col_i >= n_cols) ), "SpMat::SpMat(): invalid row or column index" ); if(i > 0) { const uword prev_index = packet_vec[i-1].index; const uword* locs_im1 = locs.colptr(prev_index); const uword row_im1 = locs_im1[0]; const uword col_im1 = locs_im1[1]; arma_debug_check( ( (row_i == row_im1) && (col_i == col_im1) ), "SpMat::SpMat(): detected identical locations" ); } access::rw(values[i]) = vals[index]; access::rw(row_indices[i]) = row_i; access::rw(col_ptrs[ col_i + 1 ])++; } } } if( (sort_locations == false) || (actually_sorted == true) ) { // Now set the values and row indices correctly. // Increment the column pointers in each column (so they are column "counts"). const uword locs_n_cols = locs.n_cols; for(uword i=0; i < locs_n_cols; ++i) { const uword* locs_i = locs.colptr(i); const uword row_i = locs_i[0]; const uword col_i = locs_i[1]; arma_debug_check( ( (row_i >= n_rows) || (col_i >= n_cols) ), "SpMat::SpMat(): invalid row or column index" ); if(i > 0) { const uword* locs_im1 = locs.colptr(i-1); const uword row_im1 = locs_im1[0]; const uword col_im1 = locs_im1[1]; arma_debug_check ( ( (col_i < col_im1) || ((col_i == col_im1) && (row_i < row_im1)) ), "SpMat::SpMat(): out of order points; either pass sort_locations = true, or sort points in column-major ordering" ); arma_debug_check( ( (col_i == col_im1) && (row_i == row_im1) ), "SpMat::SpMat(): detected identical locations" ); } access::rw(values[i]) = vals[i]; access::rw(row_indices[i]) = row_i; access::rw(col_ptrs[ col_i + 1 ])++; } } // Now fix the column pointers. for(uword i = 0; i < n_cols; ++i) { access::rw(col_ptrs[i + 1]) += col_ptrs[i]; } } template inline void SpMat::init_batch_add(const Mat& locs, const Mat& vals, const bool sort_locations) { arma_extra_debug_sigprint(); if(locs.n_cols < 2) { init_batch_std(locs, vals, false); return; } // Reset column pointers to zero. arrayops::fill_zeros(access::rwp(col_ptrs), n_cols + 1); bool actually_sorted = true; if(sort_locations) { // sort_index() uses std::sort() which may use quicksort... so we better // make sure it's not already sorted before taking an O(N^2) sort penalty. for(uword i = 1; i < locs.n_cols; ++i) { const uword* locs_i = locs.colptr(i ); const uword* locs_im1 = locs.colptr(i-1); if( (locs_i[1] < locs_im1[1]) || (locs_i[1] == locs_im1[1] && locs_i[0] <= locs_im1[0]) ) { actually_sorted = false; break; } } if(actually_sorted == false) { // This may not be the fastest possible implementation but it maximizes code reuse. Col abslocs(locs.n_cols, arma_nozeros_indicator()); for(uword i = 0; i < locs.n_cols; ++i) { const uword* locs_i = locs.colptr(i); abslocs[i] = locs_i[1] * n_rows + locs_i[0]; } uvec sorted_indices = sort_index(abslocs); // Ascending sort. // work out the number of unique elments uword n_unique = 1; // first element is unique for(uword i=1; i < sorted_indices.n_elem; ++i) { const uword* locs_i = locs.colptr( sorted_indices[i ] ); const uword* locs_im1 = locs.colptr( sorted_indices[i-1] ); if( (locs_i[1] != locs_im1[1]) || (locs_i[0] != locs_im1[0]) ) { ++n_unique; } } // resize to correct number of elements mem_resize(n_unique); // Now we add the elements in this sorted order. uword count = 0; // first element { const uword i = 0; const uword* locs_i = locs.colptr( sorted_indices[i] ); arma_debug_check( ( (locs_i[0] >= n_rows) || (locs_i[1] >= n_cols) ), "SpMat::SpMat(): invalid row or column index" ); access::rw(values[count]) = vals[ sorted_indices[i] ]; access::rw(row_indices[count]) = locs_i[0]; access::rw(col_ptrs[ locs_i[1] + 1 ])++; } for(uword i=1; i < sorted_indices.n_elem; ++i) { const uword* locs_i = locs.colptr( sorted_indices[i ] ); const uword* locs_im1 = locs.colptr( sorted_indices[i-1] ); arma_debug_check( ( (locs_i[0] >= n_rows) || (locs_i[1] >= n_cols) ), "SpMat::SpMat(): invalid row or column index" ); if( (locs_i[1] == locs_im1[1]) && (locs_i[0] == locs_im1[0]) ) { access::rw(values[count]) += vals[ sorted_indices[i] ]; } else { count++; access::rw(values[count]) = vals[ sorted_indices[i] ]; access::rw(row_indices[count]) = locs_i[0]; access::rw(col_ptrs[ locs_i[1] + 1 ])++; } } } } if( (sort_locations == false) || (actually_sorted == true) ) { // work out the number of unique elments uword n_unique = 1; // first element is unique for(uword i=1; i < locs.n_cols; ++i) { const uword* locs_i = locs.colptr(i ); const uword* locs_im1 = locs.colptr(i-1); if( (locs_i[1] != locs_im1[1]) || (locs_i[0] != locs_im1[0]) ) { ++n_unique; } } // resize to correct number of elements mem_resize(n_unique); // Now set the values and row indices correctly. // Increment the column pointers in each column (so they are column "counts"). uword count = 0; // first element { const uword i = 0; const uword* locs_i = locs.colptr(i); arma_debug_check( ( (locs_i[0] >= n_rows) || (locs_i[1] >= n_cols) ), "SpMat::SpMat(): invalid row or column index" ); access::rw(values[count]) = vals[i]; access::rw(row_indices[count]) = locs_i[0]; access::rw(col_ptrs[ locs_i[1] + 1 ])++; } for(uword i=1; i < locs.n_cols; ++i) { const uword* locs_i = locs.colptr(i ); const uword* locs_im1 = locs.colptr(i-1); arma_debug_check( ( (locs_i[0] >= n_rows) || (locs_i[1] >= n_cols) ), "SpMat::SpMat(): invalid row or column index" ); arma_debug_check ( ( (locs_i[1] < locs_im1[1]) || (locs_i[1] == locs_im1[1] && locs_i[0] < locs_im1[0]) ), "SpMat::SpMat(): out of order points; either pass sort_locations = true, or sort points in column-major ordering" ); if( (locs_i[1] == locs_im1[1]) && (locs_i[0] == locs_im1[0]) ) { access::rw(values[count]) += vals[i]; } else { count++; access::rw(values[count]) = vals[i]; access::rw(row_indices[count]) = locs_i[0]; access::rw(col_ptrs[ locs_i[1] + 1 ])++; } } } // Now fix the column pointers. for(uword i = 0; i < n_cols; ++i) { access::rw(col_ptrs[i + 1]) += col_ptrs[i]; } } //! constructor used by SpRow and SpCol classes template inline SpMat::SpMat(const arma_vec_indicator&, const uword in_vec_state) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(in_vec_state) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); const uword in_n_rows = (in_vec_state == 2) ? 1 : 0; const uword in_n_cols = (in_vec_state == 1) ? 1 : 0; init_cold(in_n_rows, in_n_cols); } //! constructor used by SpRow and SpCol classes template inline SpMat::SpMat(const arma_vec_indicator&, const uword in_n_rows, const uword in_n_cols, const uword in_vec_state) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(in_vec_state) , values(nullptr) , row_indices(nullptr) , col_ptrs(nullptr) { arma_extra_debug_sigprint_this(this); init_cold(in_n_rows, in_n_cols); } template inline void SpMat::mem_resize(const uword new_n_nonzero) { arma_extra_debug_sigprint(); invalidate_cache(); // placed here, as mem_resize() is used during matrix modification if(n_nonzero == new_n_nonzero) { return; } eT* new_values = memory::acquire (new_n_nonzero + 1); uword* new_row_indices = memory::acquire(new_n_nonzero + 1); if( (n_nonzero > 0 ) && (new_n_nonzero > 0) ) { // Copy old elements. uword copy_len = (std::min)(n_nonzero, new_n_nonzero); arrayops::copy(new_values, values, copy_len); arrayops::copy(new_row_indices, row_indices, copy_len); } if(values) { memory::release(access::rw(values)); } if(row_indices) { memory::release(access::rw(row_indices)); } access::rw(values) = new_values; access::rw(row_indices) = new_row_indices; // Set the "fake end" of the matrix by setting the last value and row index to 0. // This helps the iterators work correctly. access::rw( values[new_n_nonzero]) = 0; access::rw(row_indices[new_n_nonzero]) = 0; access::rw(n_nonzero) = new_n_nonzero; } template inline void SpMat::sync() const { arma_extra_debug_sigprint(); sync_csc(); } template inline void SpMat::remove_zeros() { arma_extra_debug_sigprint(); sync_csc(); invalidate_cache(); // placed here, as remove_zeros() is used during matrix modification const uword old_n_nonzero = n_nonzero; uword new_n_nonzero = 0; const eT* old_values = values; constexpr eT zero = eT(0); for(uword i=0; i < old_n_nonzero; ++i) { new_n_nonzero += (old_values[i] != zero) ? uword(1) : uword(0); } if(new_n_nonzero != old_n_nonzero) { if(new_n_nonzero == 0) { init(n_rows, n_cols); return; } SpMat tmp(arma_reserve_indicator(), n_rows, n_cols, new_n_nonzero); uword new_index = 0; const_iterator it = cbegin(); const_iterator it_end = cend(); for(; it != it_end; ++it) { const eT val = eT(*it); if(val != zero) { const uword it_row = it.row(); const uword it_col = it.col(); access::rw(tmp.values[new_index]) = val; access::rw(tmp.row_indices[new_index]) = it_row; access::rw(tmp.col_ptrs[it_col + 1])++; ++new_index; } } for(uword i=0; i < n_cols; ++i) { access::rw(tmp.col_ptrs[i + 1]) += tmp.col_ptrs[i]; } steal_mem(tmp); } } // Steal memory from another matrix. template inline void SpMat::steal_mem(SpMat& x) { arma_extra_debug_sigprint(); if(this == &x) { return; } bool layout_ok = false; if((*this).vec_state == x.vec_state) { layout_ok = true; } else { if( ((*this).vec_state == 1) && (x.n_cols == 1) ) { layout_ok = true; } if( ((*this).vec_state == 2) && (x.n_rows == 1) ) { layout_ok = true; } } if(layout_ok) { arma_extra_debug_print("SpMat::steal_mem(): stealing memory"); x.sync_csc(); steal_mem_simple(x); x.invalidate_cache(); invalidate_cache(); } else { arma_extra_debug_print("SpMat::steal_mem(): copying memory"); (*this).operator=(x); } } template inline void SpMat::steal_mem_simple(SpMat& x) { arma_extra_debug_sigprint(); if(this == &x) { return; } if(values ) { memory::release(access::rw(values)); } if(row_indices) { memory::release(access::rw(row_indices)); } if(col_ptrs ) { memory::release(access::rw(col_ptrs)); } access::rw(n_rows) = x.n_rows; access::rw(n_cols) = x.n_cols; access::rw(n_elem) = x.n_elem; access::rw(n_nonzero) = x.n_nonzero; access::rw(values) = x.values; access::rw(row_indices) = x.row_indices; access::rw(col_ptrs) = x.col_ptrs; // Set other matrix to empty. access::rw(x.n_rows) = 0; access::rw(x.n_cols) = 0; access::rw(x.n_elem) = 0; access::rw(x.n_nonzero) = 0; access::rw(x.values) = nullptr; access::rw(x.row_indices) = nullptr; access::rw(x.col_ptrs) = nullptr; } template template inline void SpMat::init_xform(const SpBase& A, const Functor& func) { arma_extra_debug_sigprint(); // if possible, avoid doing a copy and instead apply func to the generated elements if(SpProxy::Q_is_generated) { (*this) = A.get_ref(); const uword nnz = n_nonzero; eT* t_values = access::rwp(values); bool has_zero = false; for(uword i=0; i < nnz; ++i) { eT& t_values_i = t_values[i]; t_values_i = func(t_values_i); if(t_values_i == eT(0)) { has_zero = true; } } if(has_zero) { remove_zeros(); } } else { init_xform_mt(A.get_ref(), func); } } template template inline void SpMat::init_xform_mt(const SpBase& A, const Functor& func) { arma_extra_debug_sigprint(); const SpProxy P(A.get_ref()); if( P.is_alias(*this) || (is_SpMat::stored_type>::value) ) { // NOTE: unwrap_spmat will convert a submatrix to a matrix, which in effect takes care of aliasing with submatrices; // NOTE: however, when more delayed ops are implemented, more elaborate handling of aliasing will be necessary const unwrap_spmat::stored_type> tmp(P.Q); const SpMat& x = tmp.M; if(void_ptr(this) != void_ptr(&x)) { init(x.n_rows, x.n_cols, x.n_nonzero); arrayops::copy(access::rwp(row_indices), x.row_indices, x.n_nonzero + 1); arrayops::copy(access::rwp(col_ptrs), x.col_ptrs, x.n_cols + 1); } // initialise the elements array with a transformed version of the elements from x const uword nnz = n_nonzero; const eT2* x_values = x.values; eT* t_values = access::rwp(values); bool has_zero = false; for(uword i=0; i < nnz; ++i) { eT& t_values_i = t_values[i]; t_values_i = func(x_values[i]); // NOTE: func() must produce a value of type eT (ie. act as a convertor between eT2 and eT) if(t_values_i == eT(0)) { has_zero = true; } } if(has_zero) { remove_zeros(); } } else { init(P.get_n_rows(), P.get_n_cols(), P.get_n_nonzero()); typename SpProxy::const_iterator_type it = P.begin(); typename SpProxy::const_iterator_type it_end = P.end(); bool has_zero = false; while(it != it_end) { const eT val = func(*it); // NOTE: func() must produce a value of type eT (ie. act as a convertor between eT2 and eT) if(val == eT(0)) { has_zero = true; } const uword it_pos = it.pos(); access::rw(row_indices[it_pos]) = it.row(); access::rw(values[it_pos]) = val; ++access::rw(col_ptrs[it.col() + 1]); ++it; } // Now sum column pointers. for(uword c = 1; c <= n_cols; ++c) { access::rw(col_ptrs[c]) += col_ptrs[c - 1]; } if(has_zero) { remove_zeros(); } } } template arma_inline bool SpMat::is_alias(const SpMat& X) const { return (&X == this); } template inline typename SpMat::iterator SpMat::begin() { arma_extra_debug_sigprint(); sync_csc(); return iterator(*this); } template inline typename SpMat::const_iterator SpMat::begin() const { arma_extra_debug_sigprint(); sync_csc(); return const_iterator(*this); } template inline typename SpMat::const_iterator SpMat::cbegin() const { arma_extra_debug_sigprint(); sync_csc(); return const_iterator(*this); } template inline typename SpMat::iterator SpMat::end() { sync_csc(); return iterator(*this, 0, n_cols, n_nonzero); } template inline typename SpMat::const_iterator SpMat::end() const { sync_csc(); return const_iterator(*this, 0, n_cols, n_nonzero); } template inline typename SpMat::const_iterator SpMat::cend() const { sync_csc(); return const_iterator(*this, 0, n_cols, n_nonzero); } template inline typename SpMat::col_iterator SpMat::begin_col(const uword col_num) { sync_csc(); return col_iterator(*this, 0, col_num); } template inline typename SpMat::const_col_iterator SpMat::begin_col(const uword col_num) const { sync_csc(); return const_col_iterator(*this, 0, col_num); } template inline typename SpMat::col_iterator SpMat::begin_col_no_sync(const uword col_num) { return col_iterator(*this, 0, col_num); } template inline typename SpMat::const_col_iterator SpMat::begin_col_no_sync(const uword col_num) const { return const_col_iterator(*this, 0, col_num); } template inline typename SpMat::col_iterator SpMat::end_col(const uword col_num) { sync_csc(); return col_iterator(*this, 0, col_num + 1); } template inline typename SpMat::const_col_iterator SpMat::end_col(const uword col_num) const { sync_csc(); return const_col_iterator(*this, 0, col_num + 1); } template inline typename SpMat::col_iterator SpMat::end_col_no_sync(const uword col_num) { return col_iterator(*this, 0, col_num + 1); } template inline typename SpMat::const_col_iterator SpMat::end_col_no_sync(const uword col_num) const { return const_col_iterator(*this, 0, col_num + 1); } template inline typename SpMat::row_iterator SpMat::begin_row(const uword row_num) { sync_csc(); return row_iterator(*this, row_num, 0); } template inline typename SpMat::const_row_iterator SpMat::begin_row(const uword row_num) const { sync_csc(); return const_row_iterator(*this, row_num, 0); } template inline typename SpMat::row_iterator SpMat::end_row() { sync_csc(); return row_iterator(*this, n_nonzero); } template inline typename SpMat::const_row_iterator SpMat::end_row() const { sync_csc(); return const_row_iterator(*this, n_nonzero); } template inline typename SpMat::row_iterator SpMat::end_row(const uword row_num) { sync_csc(); return row_iterator(*this, row_num + 1, 0); } template inline typename SpMat::const_row_iterator SpMat::end_row(const uword row_num) const { sync_csc(); return const_row_iterator(*this, row_num + 1, 0); } template inline typename SpMat::row_col_iterator SpMat::begin_row_col() { sync_csc(); return begin(); } template inline typename SpMat::const_row_col_iterator SpMat::begin_row_col() const { sync_csc(); return begin(); } template inline typename SpMat::row_col_iterator SpMat::end_row_col() { sync_csc(); return end(); } template inline typename SpMat::const_row_col_iterator SpMat::end_row_col() const { sync_csc(); return end(); } template inline void SpMat::clear() { (*this).reset(); } template inline bool SpMat::empty() const { return (n_elem == 0); } template inline uword SpMat::size() const { return n_elem; } template arma_inline SpMat_MapMat_val SpMat::front() { arma_debug_check( (n_elem == 0), "SpMat::front(): matrix is empty" ); return SpMat_MapMat_val((*this), cache, 0, 0); } template arma_inline eT SpMat::front() const { arma_debug_check( (n_elem == 0), "SpMat::front(): matrix is empty" ); return get_value(0,0); } template arma_inline SpMat_MapMat_val SpMat::back() { arma_debug_check( (n_elem == 0), "SpMat::back(): matrix is empty" ); return SpMat_MapMat_val((*this), cache, n_rows-1, n_cols-1); } template arma_inline eT SpMat::back() const { arma_debug_check( (n_elem == 0), "SpMat::back(): matrix is empty" ); return get_value(n_rows-1, n_cols-1); } template inline eT SpMat::get_value(const uword i) const { const MapMat& const_cache = cache; // declare as const for clarity of intent // get the element from the cache if it has more recent data than CSC return (sync_state == 1) ? const_cache.operator[](i) : get_value_csc(i); } template inline eT SpMat::get_value(const uword in_row, const uword in_col) const { const MapMat& const_cache = cache; // declare as const for clarity of intent // get the element from the cache if it has more recent data than CSC return (sync_state == 1) ? const_cache.at(in_row, in_col) : get_value_csc(in_row, in_col); } template inline eT SpMat::get_value_csc(const uword i) const { // First convert to the actual location. uword lcol = i / n_rows; // Integer division. uword lrow = i % n_rows; return get_value_csc(lrow, lcol); } template inline const eT* SpMat::find_value_csc(const uword in_row, const uword in_col) const { const uword col_offset = col_ptrs[in_col ]; const uword next_col_offset = col_ptrs[in_col + 1]; const uword* start_ptr = &row_indices[ col_offset]; const uword* end_ptr = &row_indices[next_col_offset]; const uword* pos_ptr = std::lower_bound(start_ptr, end_ptr, in_row); // binary search if( (pos_ptr != end_ptr) && ((*pos_ptr) == in_row) ) { const uword offset = uword(pos_ptr - start_ptr); const uword index = offset + col_offset; return &(values[index]); } return nullptr; } template inline eT SpMat::get_value_csc(const uword in_row, const uword in_col) const { const eT* val_ptr = find_value_csc(in_row, in_col); return (val_ptr != nullptr) ? eT(*val_ptr) : eT(0); } template inline bool SpMat::try_set_value_csc(const uword in_row, const uword in_col, const eT in_val) { const eT* val_ptr = find_value_csc(in_row, in_col); // element not found, ie. it's zero; fail if trying to set it to non-zero value if(val_ptr == nullptr) { return (in_val == eT(0)); } // fail if trying to erase an existing element if(in_val == eT(0)) { return false; } access::rw(*val_ptr) = in_val; invalidate_cache(); return true; } template inline bool SpMat::try_add_value_csc(const uword in_row, const uword in_col, const eT in_val) { const eT* val_ptr = find_value_csc(in_row, in_col); // element not found, ie. it's zero; fail if trying to add a non-zero value if(val_ptr == nullptr) { return (in_val == eT(0)); } const eT new_val = eT(*val_ptr) + in_val; // fail if trying to erase an existing element if(new_val == eT(0)) { return false; } access::rw(*val_ptr) = new_val; invalidate_cache(); return true; } template inline bool SpMat::try_sub_value_csc(const uword in_row, const uword in_col, const eT in_val) { const eT* val_ptr = find_value_csc(in_row, in_col); // element not found, ie. it's zero; fail if trying to subtract a non-zero value if(val_ptr == nullptr) { return (in_val == eT(0)); } const eT new_val = eT(*val_ptr) - in_val; // fail if trying to erase an existing element if(new_val == eT(0)) { return false; } access::rw(*val_ptr) = new_val; invalidate_cache(); return true; } template inline bool SpMat::try_mul_value_csc(const uword in_row, const uword in_col, const eT in_val) { const eT* val_ptr = find_value_csc(in_row, in_col); // element not found, ie. it's zero; succeed if given value is finite; zero multiplied by anything is zero, except for nan and inf if(val_ptr == nullptr) { return arma_isfinite(in_val); } const eT new_val = eT(*val_ptr) * in_val; // fail if trying to erase an existing element if(new_val == eT(0)) { return false; } access::rw(*val_ptr) = new_val; invalidate_cache(); return true; } template inline bool SpMat::try_div_value_csc(const uword in_row, const uword in_col, const eT in_val) { const eT* val_ptr = find_value_csc(in_row, in_col); // element not found, ie. it's zero; succeed if given value is not zero and not nan; zero divided by anything is zero, except for zero and nan if(val_ptr == nullptr) { return ((in_val != eT(0)) && (arma_isnan(in_val) == false)); } const eT new_val = eT(*val_ptr) / in_val; // fail if trying to erase an existing element if(new_val == eT(0)) { return false; } access::rw(*val_ptr) = new_val; invalidate_cache(); return true; } /** * Insert an element at the given position, and return a reference to it. * The element will be set to 0, unless otherwise specified. * If the element already exists, its value will be overwritten. */ template inline eT& SpMat::insert_element(const uword in_row, const uword in_col, const eT val) { arma_extra_debug_sigprint(); sync_csc(); invalidate_cache(); // We will assume the new element does not exist and begin the search for // where to insert it. If we find that it already exists, we will then // overwrite it. uword colptr = col_ptrs[in_col ]; uword next_colptr = col_ptrs[in_col + 1]; uword pos = colptr; // The position in the matrix of this value. if(colptr != next_colptr) { // There are other elements in this column, so we must find where this // element will fit as compared to those. while(pos < next_colptr && in_row > row_indices[pos]) { pos++; } // We aren't inserting into the last position, so it is still possible // that the element may exist. if(pos != next_colptr && row_indices[pos] == in_row) { // It already exists. Then, just overwrite it. access::rw(values[pos]) = val; return access::rw(values[pos]); } } // // Element doesn't exist, so we have to insert it // // We have to update the rest of the column pointers. for(uword i = in_col + 1; i < n_cols + 1; i++) { access::rw(col_ptrs[i])++; // We are only inserting one new element. } const uword old_n_nonzero = n_nonzero; access::rw(n_nonzero)++; // Add to count of nonzero elements. // Allocate larger memory. eT* new_values = memory::acquire (n_nonzero + 1); uword* new_row_indices = memory::acquire(n_nonzero + 1); // Copy things over, before the new element. if(pos > 0) { arrayops::copy(new_values, values, pos); arrayops::copy(new_row_indices, row_indices, pos); } // Insert the new element. new_values[pos] = val; new_row_indices[pos] = in_row; // Copy the rest of things over (including the extra element at the end). arrayops::copy(new_values + pos + 1, values + pos, (old_n_nonzero - pos) + 1); arrayops::copy(new_row_indices + pos + 1, row_indices + pos, (old_n_nonzero - pos) + 1); // Assign new pointers. if(values) { memory::release(access::rw(values)); } if(row_indices) { memory::release(access::rw(row_indices)); } access::rw(values) = new_values; access::rw(row_indices) = new_row_indices; return access::rw(values[pos]); } /** * Delete an element at the given position. */ template inline void SpMat::delete_element(const uword in_row, const uword in_col) { arma_extra_debug_sigprint(); sync_csc(); invalidate_cache(); // We assume the element exists (although... it may not) and look for its // exact position. If it doesn't exist... well, we don't need to do anything. uword colptr = col_ptrs[in_col]; uword next_colptr = col_ptrs[in_col + 1]; if(colptr != next_colptr) { // There's at least one element in this column. // Let's see if we are one of them. for(uword pos = colptr; pos < next_colptr; pos++) { if(in_row == row_indices[pos]) { --access::rw(n_nonzero); // Remove one from the count of nonzero elements. // Found it. Now remove it. // Make new arrays. eT* new_values = memory::acquire (n_nonzero + 1); uword* new_row_indices = memory::acquire(n_nonzero + 1); if(pos > 0) { arrayops::copy(new_values, values, pos); arrayops::copy(new_row_indices, row_indices, pos); } arrayops::copy(new_values + pos, values + pos + 1, (n_nonzero - pos) + 1); arrayops::copy(new_row_indices + pos, row_indices + pos + 1, (n_nonzero - pos) + 1); if(values) { memory::release(access::rw(values)); } if(row_indices) { memory::release(access::rw(row_indices)); } access::rw(values) = new_values; access::rw(row_indices) = new_row_indices; // And lastly, update all the column pointers (decrement by one). for(uword i = in_col + 1; i < n_cols + 1; i++) { --access::rw(col_ptrs[i]); // We only removed one element. } return; // There is nothing left to do. } } } return; // The element does not exist, so there's nothing for us to do. } template arma_inline void SpMat::invalidate_cache() const { arma_extra_debug_sigprint(); if(sync_state == 0) { return; } cache.reset(); sync_state = 0; } template arma_inline void SpMat::invalidate_csc() const { arma_extra_debug_sigprint(); sync_state = 1; } template inline void SpMat::sync_cache() const { arma_extra_debug_sigprint(); // using approach adapted from http://preshing.com/20130930/double-checked-locking-is-fixed-in-cpp11/ // // OpenMP mode: // sync_state uses atomic read/write, which has an implied flush; // flush is also implicitly executed at the entrance and the exit of critical section; // data races are prevented by the 'critical' directive // // C++11 mode: // underlying type for sync_state is std::atomic; // reading and writing to sync_state uses std::memory_order_seq_cst which has an implied fence; // data races are prevented via the mutex #if defined(ARMA_USE_OPENMP) { if(sync_state == 0) { #pragma omp critical (arma_SpMat_cache) { sync_cache_simple(); } } } #elif (!defined(ARMA_DONT_USE_STD_MUTEX)) { if(sync_state == 0) { const std::lock_guard lock(cache_mutex); sync_cache_simple(); } } #else { sync_cache_simple(); } #endif } template inline void SpMat::sync_cache_simple() const { arma_extra_debug_sigprint(); if(sync_state == 0) { cache = (*this); sync_state = 2; } } template inline void SpMat::sync_csc() const { arma_extra_debug_sigprint(); #if defined(ARMA_USE_OPENMP) if(sync_state == 1) { #pragma omp critical (arma_SpMat_cache) { sync_csc_simple(); } } #elif (!defined(ARMA_DONT_USE_STD_MUTEX)) if(sync_state == 1) { const std::lock_guard lock(cache_mutex); sync_csc_simple(); } #else { sync_csc_simple(); } #endif } template inline void SpMat::sync_csc_simple() const { arma_extra_debug_sigprint(); // method: // 1. construct temporary matrix to prevent the cache from getting zapped // 2. steal memory from the temporary matrix // sync_state is only set to 1 by non-const element access operators, // so the shenanigans with const_cast are to satisfy the compiler // see also the note in sync_cache() above if(sync_state == 1) { SpMat& x = const_cast< SpMat& >(*this); SpMat tmp(cache); x.steal_mem_simple(tmp); sync_state = 2; } } // // SpMat_aux template inline void SpMat_aux::set_real(SpMat& out, const SpBase& X) { arma_extra_debug_sigprint(); const unwrap_spmat tmp(X.get_ref()); const SpMat& A = tmp.M; arma_debug_assert_same_size( out, A, "SpMat::set_real()" ); out = A; } template inline void SpMat_aux::set_imag(SpMat&, const SpBase&) { arma_extra_debug_sigprint(); } template inline void SpMat_aux::set_real(SpMat< std::complex >& out, const SpBase& X) { arma_extra_debug_sigprint(); typedef typename std::complex eT; const unwrap_spmat U(X.get_ref()); const SpMat& Y = U.M; arma_debug_assert_same_size(out, Y, "SpMat::set_real()"); SpMat tmp(Y,arma::imag(out)); // arma:: prefix required due to bugs in GCC 4.4 - 4.6 out.steal_mem(tmp); } template inline void SpMat_aux::set_imag(SpMat< std::complex >& out, const SpBase& X) { arma_extra_debug_sigprint(); typedef typename std::complex eT; const unwrap_spmat U(X.get_ref()); const SpMat& Y = U.M; arma_debug_assert_same_size(out, Y, "SpMat::set_imag()"); SpMat tmp(arma::real(out),Y); // arma:: prefix required due to bugs in GCC 4.4 - 4.6 out.steal_mem(tmp); } #if defined(ARMA_EXTRA_SPMAT_MEAT) #include ARMA_INCFILE_WRAP(ARMA_EXTRA_SPMAT_MEAT) #endif //! @}