// Copyright (C) 2008-2015 National ICT Australia (NICTA) // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------- // // Written by Conrad Sanderson - http://conradsanderson.id.au // Written by Ryan Curtin // Written by Matthew Amidon //! \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(memory::acquire_chunked(1)) , row_indices(memory::acquire_chunked(1)) , col_ptrs(memory::acquire(2)) { arma_extra_debug_sigprint_this(this); access::rw(values[0]) = 0; access::rw(row_indices[0]) = 0; access::rw(col_ptrs[0]) = 0; // No elements. access::rw(col_ptrs[1]) = std::numeric_limits::max(); } /** * 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(NULL) , row_indices(NULL) , col_ptrs(NULL) { arma_extra_debug_sigprint_this(this); init(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(NULL) , row_indices(NULL) , col_ptrs(NULL) { arma_extra_debug_sigprint_this(this); init(s.n_rows, s.n_cols); } /** * 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(NULL) , row_indices(NULL) , col_ptrs(NULL) { arma_extra_debug_sigprint_this(this); init(std::string(text)); } template inline const 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(NULL) , row_indices(NULL) , col_ptrs(NULL) { arma_extra_debug_sigprint(); init(text); } template inline const 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(NULL) , row_indices(NULL) , col_ptrs(NULL) { arma_extra_debug_sigprint_this(this); init(x); } #if defined(ARMA_USE_CXX11) template inline SpMat::SpMat(SpMat&& in_mat) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(NULL) , row_indices(NULL) , col_ptrs(NULL) { arma_extra_debug_sigprint_this(this); arma_extra_debug_sigprint(arma_boost::format("this = %x in_mat = %x") % this % &in_mat); (*this).steal_mem(in_mat); } template inline const SpMat& SpMat::operator=(SpMat&& in_mat) { arma_extra_debug_sigprint(arma_boost::format("this = %x in_mat = %x") % this % &in_mat); (*this).steal_mem(in_mat); return *this; } #endif //! 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(NULL) , row_indices(NULL) , col_ptrs(NULL) { arma_extra_debug_sigprint_this(this); const unwrap locs_tmp( locations_expr.get_ref() ); const 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 is not 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(0, 0); return; } // Automatically determine size before pruning zeros. uvec bounds = arma::max(locs, 1); init(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) { if(vals[i] != eT(0)) { ++N_new; } } if(N_new != N_old) { Col filtered_vals(N_new); Mat filtered_locs(2, N_new); 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(NULL) , row_indices(NULL) , col_ptrs(NULL) { arma_extra_debug_sigprint_this(this); const unwrap locs_tmp( locations_expr.get_ref() ); const 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 is not 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(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) { if(vals[i] != eT(0)) { ++N_new; } } if(N_new != N_old) { Col filtered_vals(N_new); Mat filtered_locs(2, N_new); 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(NULL) , row_indices(NULL) , col_ptrs(NULL) { arma_extra_debug_sigprint_this(this); const unwrap locs_tmp( locations_expr.get_ref() ); const 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 is not 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(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) { if(vals[i] != eT(0)) { ++N_new; } } if(N_new != N_old) { Col filtered_vals(N_new); Mat filtered_locs(2, N_new); 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 ) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(NULL) , row_indices(NULL) , col_ptrs(NULL) { arma_extra_debug_sigprint_this(this); init(in_n_rows, in_n_cols); const unwrap rowind_tmp( rowind_expr.get_ref() ); const unwrap colptr_tmp( colptr_expr.get_ref() ); const 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 is not a vector" ); arma_debug_check( (colptr.is_vec() == false), "SpMat::SpMat(): given 'colptr' object is not a vector" ); arma_debug_check( (vals.is_vec() == false), "SpMat::SpMat(): given 'values' object is not a vector" ); 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" ); // Resize to correct number of elements (this also sets n_nonzero) mem_resize(vals.n_elem); // 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(); } template inline const 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); // Sets col_ptrs to 0. mem_resize(1); // One element. // 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 const SpMat& SpMat::operator*=(const eT val) { arma_extra_debug_sigprint(); if(val != eT(0)) { arrayops::inplace_mul( access::rwp(values), val, n_nonzero ); remove_zeros(); } else { // Everything will be zero. init(n_rows, n_cols); } return *this; } template inline const SpMat& SpMat::operator/=(const eT val) { arma_extra_debug_sigprint(); arma_debug_check( (val == eT(0)), "element-wise division: division by zero" ); arrayops::inplace_div( access::rwp(values), val, n_nonzero ); remove_zeros(); return *this; } template inline const SpMat& SpMat::operator=(const SpMat& x) { arma_extra_debug_sigprint(); init(x); return *this; } template inline const SpMat& SpMat::operator+=(const SpMat& x) { arma_extra_debug_sigprint(); SpMat out = (*this) + x; steal_mem(out); return *this; } template inline const SpMat& SpMat::operator-=(const SpMat& x) { arma_extra_debug_sigprint(); SpMat out = (*this) - x; steal_mem(out); return *this; } template inline const SpMat& SpMat::operator*=(const SpMat& y) { arma_extra_debug_sigprint(); SpMat z = (*this) * y; steal_mem(z); return *this; } // This is in-place element-wise matrix multiplication. template inline const SpMat& SpMat::operator%=(const SpMat& y) { arma_extra_debug_sigprint(); SpMat z = (*this) % y; steal_mem(z); return *this; } // 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(NULL) // extra element is set when mem_resize is called , row_indices(NULL) , col_ptrs(NULL) { 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_complex::value == false )); arma_type_check(( is_complex< T>::value == true )); // 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(l_n_rows, l_n_cols); mem_resize(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 inline const SpMat& SpMat::operator/=(const SpMat& x) { arma_extra_debug_sigprint(); arma_debug_assert_same_size(n_rows, n_cols, x.n_rows, x.n_cols, "element-wise division"); // If you use this method, you are probably stupid or misguided, but for compatibility with Mat, we have implemented it anyway. // We have to loop over every element, which is not good. In fact, it makes me physically sad to write this. 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 Base& x) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(NULL) // extra element is set when mem_resize is called in operator=() , row_indices(NULL) , col_ptrs(NULL) { arma_extra_debug_sigprint_this(this); (*this).operator=(x); } template template inline const SpMat& SpMat::operator=(const Base& expr) { arma_extra_debug_sigprint(); 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; init(x_n_rows, x_n_cols); // 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); } mem_resize(n); // Now the memory is resized correctly; add 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 const SpMat& SpMat::operator+=(const Base& x) { arma_extra_debug_sigprint(); return (*this).operator=( (*this) + x.get_ref() ); } template template inline const SpMat& SpMat::operator-=(const Base& x) { arma_extra_debug_sigprint(); return (*this).operator=( (*this) - x.get_ref() ); } template template inline const SpMat& SpMat::operator*=(const Base& y) { arma_extra_debug_sigprint(); const Proxy p(y.get_ref()); arma_debug_assert_mul_size(n_rows, n_cols, p.get_n_rows(), p.get_n_cols(), "matrix multiplication"); // We assume the matrix structure is such that we will end up with a sparse // matrix. Assuming that every entry in the dense matrix is nonzero (which is // a fairly valid assumption), each row with any nonzero elements in it (in this // matrix) implies an entire nonzero column. Therefore, we iterate over all // the row_indices and count the number of rows with any elements in them // (using the quasi-linked-list idea from SYMBMM -- see operator_times.hpp). podarray index(n_rows); index.fill(n_rows); // Fill with invalid links. uword last_index = n_rows + 1; for(uword i = 0; i < n_nonzero; ++i) { if(index[row_indices[i]] == n_rows) { index[row_indices[i]] = last_index; last_index = row_indices[i]; } } // Now count the number of rows which have nonzero elements. uword nonzero_rows = 0; while(last_index != n_rows + 1) { ++nonzero_rows; last_index = index[last_index]; } SpMat z(n_rows, p.get_n_cols()); z.mem_resize(nonzero_rows * p.get_n_cols()); // upper bound on size // Now we have to fill all the elements using a modification of the NUMBMM algorithm. uword cur_pos = 0; podarray partial_sums(n_rows); partial_sums.zeros(); for(uword lcol = 0; lcol < n_cols; ++lcol) { const_iterator it = begin(); while(it != end()) { const eT value = (*it); partial_sums[it.row()] += (value * p.at(it.col(), lcol)); ++it; } // Now add all partial sums to the matrix. for(uword i = 0; i < n_rows; ++i) { if(partial_sums[i] != eT(0)) { access::rw(z.values[cur_pos]) = partial_sums[i]; access::rw(z.row_indices[cur_pos]) = i; ++access::rw(z.col_ptrs[lcol + 1]); //printf("colptr %d now %d\n", lcol + 1, z.col_ptrs[lcol + 1]); ++cur_pos; partial_sums[i] = 0; // Would it be faster to do this in batch later? } } } // Now fix the column pointers. for(uword c = 1; c <= z.n_cols; ++c) { access::rw(z.col_ptrs[c]) += z.col_ptrs[c - 1]; } // Resize to final correct size. z.mem_resize(z.col_ptrs[z.n_cols]); // Now take the memory of the temporary matrix. steal_mem(z); return *this; } /** * Don't use this function. It's not mathematically well-defined and wastes * cycles to trash all your data. This is dumb. */ template template inline const SpMat& SpMat::operator/=(const Base& x) { arma_extra_debug_sigprint(); SpMat tmp = (*this) / x.get_ref(); steal_mem(tmp); return *this; } template template inline const SpMat& SpMat::operator%=(const Base& x) { arma_extra_debug_sigprint(); const Proxy p(x.get_ref()); arma_debug_assert_same_size(n_rows, n_cols, p.get_n_rows(), p.get_n_cols(), "element-wise multiplication"); // Count the number of elements we will need. SpMat tmp(n_rows, n_cols); const_iterator it = begin(); uword new_n_nonzero = 0; while(it != end()) { // prefer_at_accessor == false can't save us any work here if(((*it) * p.at(it.row(), it.col())) != eT(0)) { ++new_n_nonzero; } ++it; } // Resize. tmp.mem_resize(new_n_nonzero); const_iterator c_it = begin(); uword cur_pos = 0; while(c_it != end()) { // prefer_at_accessor == false can't save us any work here const eT val = (*c_it) * p.at(c_it.row(), c_it.col()); if(val != eT(0)) { access::rw(tmp.values[cur_pos]) = val; access::rw(tmp.row_indices[cur_pos]) = c_it.row(); ++access::rw(tmp.col_ptrs[c_it.col() + 1]); ++cur_pos; } ++c_it; } // Fix column pointers. for(uword c = 1; c <= n_cols; ++c) { access::rw(tmp.col_ptrs[c]) += tmp.col_ptrs[c - 1]; } steal_mem(tmp); return *this; } /** * 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(NULL) // extra element added when mem_resize is called , row_indices(NULL) , col_ptrs(NULL) { arma_extra_debug_sigprint_this(this); (*this).operator=(X); } template inline const SpMat& SpMat::operator=(const SpSubview& X) { arma_extra_debug_sigprint(); const uword in_n_cols = X.n_cols; const uword in_n_rows = X.n_rows; const bool alias = (this == &(X.m)); if(alias == false) { init(in_n_rows, in_n_cols); const uword x_n_nonzero = X.n_nonzero; mem_resize(x_n_nonzero); typename SpSubview::const_iterator it = X.begin(); typename SpSubview::const_iterator it_end = X.end(); while(it != it_end) { 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]; } } else { // Create it in a temporary. SpMat tmp(X); steal_mem(tmp); } return *this; } template inline const SpMat& SpMat::operator+=(const SpSubview& X) { arma_extra_debug_sigprint(); SpMat tmp = (*this) + X; steal_mem(tmp); return *this; } template inline const SpMat& SpMat::operator-=(const SpSubview& X) { arma_extra_debug_sigprint(); SpMat tmp = (*this) - X; steal_mem(tmp); return *this; } template inline const SpMat& SpMat::operator*=(const SpSubview& y) { arma_extra_debug_sigprint(); SpMat z = (*this) * y; steal_mem(z); return *this; } template inline const SpMat& SpMat::operator%=(const SpSubview& x) { arma_extra_debug_sigprint(); SpMat tmp = (*this) % x; steal_mem(tmp); return *this; } template inline const 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 SpOp& X) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(NULL) // set in application of sparse operation , row_indices(NULL) , col_ptrs(NULL) { arma_extra_debug_sigprint_this(this); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); spop_type::apply(*this, X); } template template inline const 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); return *this; } template template inline const SpMat& SpMat::operator+=(const SpOp& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); const SpMat m(X); return (*this).operator+=(m); } template template inline const SpMat& SpMat::operator-=(const SpOp& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); const SpMat m(X); return (*this).operator-=(m); } template template inline const SpMat& SpMat::operator*=(const SpOp& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); const SpMat m(X); return (*this).operator*=(m); } template template inline const SpMat& SpMat::operator%=(const SpOp& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); const SpMat m(X); return (*this).operator%=(m); } template template inline const SpMat& SpMat::operator/=(const SpOp& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); 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(NULL) // extra element set in application of sparse glue , row_indices(NULL) , col_ptrs(NULL) { arma_extra_debug_sigprint_this(this); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); spglue_type::apply(*this, X); } template template inline SpMat::SpMat(const mtSpOp& X) : n_rows(0) , n_cols(0) , n_elem(0) , n_nonzero(0) , vec_state(0) , values(NULL) // extra element set in application of sparse glue , row_indices(NULL) , col_ptrs(NULL) { arma_extra_debug_sigprint_this(this); spop_type::apply(*this, X); } template template inline const SpMat& SpMat::operator=(const mtSpOp& X) { arma_extra_debug_sigprint(); spop_type::apply(*this, X); return *this; } template template inline const SpMat& SpMat::operator+=(const mtSpOp& X) { arma_extra_debug_sigprint(); const SpMat m(X); return (*this).operator+=(m); } template template inline const SpMat& SpMat::operator-=(const mtSpOp& X) { arma_extra_debug_sigprint(); const SpMat m(X); return (*this).operator-=(m); } template template inline const SpMat& SpMat::operator*=(const mtSpOp& X) { arma_extra_debug_sigprint(); const SpMat m(X); return (*this).operator*=(m); } template template inline const SpMat& SpMat::operator%=(const mtSpOp& X) { arma_extra_debug_sigprint(); const SpMat m(X); return (*this).operator%=(m); } template template inline const SpMat& SpMat::operator/=(const mtSpOp& X) { arma_extra_debug_sigprint(); const SpMat m(X); return (*this).operator/=(m); } template template inline const 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); return *this; } template template inline const SpMat& SpMat::operator+=(const SpGlue& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); const SpMat m(X); return (*this).operator+=(m); } template template inline const SpMat& SpMat::operator-=(const SpGlue& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); const SpMat m(X); return (*this).operator-=(m); } template template inline const SpMat& SpMat::operator*=(const SpGlue& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); const SpMat m(X); return (*this).operator*=(m); } template template inline const SpMat& SpMat::operator%=(const SpGlue& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); const SpMat m(X); return (*this).operator%=(m); } template template inline const SpMat& SpMat::operator/=(const SpGlue& X) { arma_extra_debug_sigprint(); arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); const SpMat m(X); return (*this).operator/=(m); } template arma_inline SpSubview SpMat::row(const uword row_num) { arma_extra_debug_sigprint(); arma_debug_check(row_num >= n_rows, "SpMat::row(): out of bounds"); return SpSubview(*this, row_num, 0, 1, n_cols); } template arma_inline const SpSubview SpMat::row(const uword row_num) const { arma_extra_debug_sigprint(); arma_debug_check(row_num >= n_rows, "SpMat::row(): out of bounds"); return SpSubview(*this, row_num, 0, 1, n_cols); } template inline SpSubview 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 ( (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(*this, row_num, in_col1, 1, submat_n_cols); } template inline const SpSubview 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 ( (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(*this, row_num, in_col1, 1, submat_n_cols); } template arma_inline SpSubview SpMat::col(const uword col_num) { arma_extra_debug_sigprint(); arma_debug_check(col_num >= n_cols, "SpMat::col(): out of bounds"); return SpSubview(*this, 0, col_num, n_rows, 1); } template arma_inline const SpSubview SpMat::col(const uword col_num) const { arma_extra_debug_sigprint(); arma_debug_check(col_num >= n_cols, "SpMat::col(): out of bounds"); return SpSubview(*this, 0, col_num, n_rows, 1); } template inline SpSubview 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 ( (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(*this, in_row1, col_num, submat_n_rows, 1); } template inline const SpSubview 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 ( (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(*this, in_row1, col_num, submat_n_rows, 1); } template arma_inline SpSubview SpMat::rows(const uword in_row1, const uword in_row2) { arma_extra_debug_sigprint(); arma_debug_check ( (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 ( (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 ( (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 ( (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 ( (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 ( (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 ( ((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 ( ((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 ( ( 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 ( ( 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( (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( (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( (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( (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( (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( (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( (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( (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); } //! 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 ( ((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 = (in_id < 0) ? -in_id : 0; const uword col_offset = (in_id > 0) ? in_id : 0; arma_debug_check ( ((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 ( (in_row1 >= n_rows) || (in_row2 >= n_rows), "SpMat::swap_rows(): out of bounds" ); // Sanity check. if (in_row1 == in_row2) { return; } // 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(); // slow but works for(uword lrow = 0; lrow < n_rows; ++lrow) { eT tmp = at(lrow, in_col1); at(lrow, in_col1) = 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 (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 (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 ( (in_row1 > in_row2) || (in_row2 >= n_rows), "SpMat::shed_rows(): indices out of bounds or incorectly used" ); 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 = begin(); const_iterator it_end = end(); 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 ( (in_col1 > in_col2) || (in_col2 >= n_cols), "SpMat::shed_cols(): indices out of bounds or incorrectly used" ); // 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_chunked (n_nonzero - diff); uword* new_row_indices = memory::acquire_chunked(n_nonzero - 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); } memory::release(values); memory::release(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; } memory::release(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 arma_warn_unused SpValProxy > SpMat::operator[](const uword i) { return get_value(i); } template arma_inline arma_warn_unused eT SpMat::operator[](const uword i) const { return get_value(i); } template arma_inline arma_warn_unused SpValProxy > SpMat::at(const uword i) { return get_value(i); } template arma_inline arma_warn_unused eT SpMat::at(const uword i) const { return get_value(i); } template arma_inline arma_warn_unused SpValProxy > SpMat::operator()(const uword i) { arma_debug_check( (i >= n_elem), "SpMat::operator(): out of bounds"); return get_value(i); } template arma_inline arma_warn_unused eT SpMat::operator()(const uword i) const { arma_debug_check( (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. */ template arma_inline arma_warn_unused SpValProxy > SpMat::at(const uword in_row, const uword in_col) { return get_value(in_row, in_col); } template arma_inline arma_warn_unused eT SpMat::at(const uword in_row, const uword in_col) const { return get_value(in_row, in_col); } template arma_inline arma_warn_unused SpValProxy > SpMat::operator()(const uword in_row, const uword in_col) { arma_debug_check( ((in_row >= n_rows) || (in_col >= n_cols)), "SpMat::operator(): out of bounds"); return get_value(in_row, in_col); } template arma_inline arma_warn_unused eT SpMat::operator()(const uword in_row, const uword in_col) const { arma_debug_check( ((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 arma_warn_unused 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 arma_warn_unused 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 arma_warn_unused bool SpMat::is_rowvec() const { return (n_rows == 1); } //! returns true if the object can be interpreted as a column vector template arma_inline arma_warn_unused 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 arma_warn_unused bool SpMat::is_square() const { return (n_rows == n_cols); } //! returns true if all of the elements are finite template inline arma_warn_unused bool SpMat::is_finite() const { arma_extra_debug_sigprint(); return arrayops::is_finite(values, n_nonzero); } template inline arma_warn_unused bool SpMat::has_inf() const { arma_extra_debug_sigprint(); return arrayops::has_inf(values, n_nonzero); } template inline arma_warn_unused bool SpMat::has_nan() const { arma_extra_debug_sigprint(); return arrayops::has_nan(values, n_nonzero); } //! returns true if the given index is currently in range template arma_inline arma_warn_unused 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 arma_warn_unused bool SpMat::in_range(const span& x) const { arma_extra_debug_sigprint(); if(x.whole == true) { 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 arma_warn_unused 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 arma_warn_unused bool SpMat::in_range(const span& row_span, const uword in_col) const { arma_extra_debug_sigprint(); if(row_span.whole == true) { 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 arma_warn_unused bool SpMat::in_range(const uword in_row, const span& col_span) const { arma_extra_debug_sigprint(); if(col_span.whole == true) { 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 arma_warn_unused 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 == true) && (cols_ok == true) ); } template arma_inline arma_warn_unused 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; } } template inline void SpMat::impl_print(const std::string& extra_text) const { arma_extra_debug_sigprint(); if(extra_text.length() != 0) { const std::streamsize orig_width = ARMA_DEFAULT_OSTREAM.width(); ARMA_DEFAULT_OSTREAM << extra_text << '\n'; ARMA_DEFAULT_OSTREAM.width(orig_width); } arma_ostream::print(ARMA_DEFAULT_OSTREAM, *this, true); } template inline void SpMat::impl_print(std::ostream& user_stream, const std::string& extra_text) const { arma_extra_debug_sigprint(); if(extra_text.length() != 0) { const std::streamsize orig_width = user_stream.width(); user_stream << extra_text << '\n'; user_stream.width(orig_width); } arma_ostream::print(user_stream, *this, true); } template inline void SpMat::impl_raw_print(const std::string& extra_text) const { arma_extra_debug_sigprint(); if(extra_text.length() != 0) { const std::streamsize orig_width = ARMA_DEFAULT_OSTREAM.width(); ARMA_DEFAULT_OSTREAM << extra_text << '\n'; ARMA_DEFAULT_OSTREAM.width(orig_width); } arma_ostream::print(ARMA_DEFAULT_OSTREAM, *this, false); } template inline void SpMat::impl_raw_print(std::ostream& user_stream, const std::string& extra_text) const { arma_extra_debug_sigprint(); if(extra_text.length() != 0) { const std::streamsize orig_width = user_stream.width(); user_stream << extra_text << '\n'; user_stream.width(orig_width); } arma_ostream::print(user_stream, *this, false); } /** * Matrix printing, prepends supplied text. * Prints 0 wherever no element exists. */ template inline void SpMat::impl_print_dense(const std::string& extra_text) const { arma_extra_debug_sigprint(); if(extra_text.length() != 0) { const std::streamsize orig_width = ARMA_DEFAULT_OSTREAM.width(); ARMA_DEFAULT_OSTREAM << extra_text << '\n'; ARMA_DEFAULT_OSTREAM.width(orig_width); } arma_ostream::print_dense(ARMA_DEFAULT_OSTREAM, *this, true); } template inline void SpMat::impl_print_dense(std::ostream& user_stream, const std::string& extra_text) const { arma_extra_debug_sigprint(); if(extra_text.length() != 0) { const std::streamsize orig_width = user_stream.width(); user_stream << extra_text << '\n'; user_stream.width(orig_width); } arma_ostream::print_dense(user_stream, *this, true); } template inline void SpMat::impl_raw_print_dense(const std::string& extra_text) const { arma_extra_debug_sigprint(); if(extra_text.length() != 0) { const std::streamsize orig_width = ARMA_DEFAULT_OSTREAM.width(); ARMA_DEFAULT_OSTREAM << extra_text << '\n'; ARMA_DEFAULT_OSTREAM.width(orig_width); } arma_ostream::print_dense(ARMA_DEFAULT_OSTREAM, *this, false); } template inline void SpMat::impl_raw_print_dense(std::ostream& user_stream, const std::string& extra_text) const { arma_extra_debug_sigprint(); if(extra_text.length() != 0) { const std::streamsize orig_width = user_stream.width(); user_stream << extra_text << '\n'; user_stream.width(orig_width); } arma_ostream::print_dense(user_stream, *this, false); } //! Set the size to the size of another matrix. template template inline void SpMat::copy_size(const SpMat& m) { arma_extra_debug_sigprint(); set_size(m.n_rows, m.n_cols); } template template inline void SpMat::copy_size(const Mat& m) { arma_extra_debug_sigprint(); set_size(m.n_rows, m.n_cols); } template inline void 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); } } template inline void SpMat::set_size(const uword in_rows, const uword in_cols) { arma_extra_debug_sigprint(); if( (n_rows == in_rows) && (n_cols == in_cols) ) { return; } else { init(in_rows, in_cols); } } template inline void SpMat::set_size(const SizeMat& s) { arma_extra_debug_sigprint(); (*this).set_size(s.n_rows, s.n_cols); } template inline void SpMat::resize(const uword in_rows, const uword in_cols) { arma_extra_debug_sigprint(); if( (n_rows == in_rows) && (n_cols == in_cols) ) { return; } if( (n_elem == 0) || (n_nonzero == 0) ) { set_size(in_rows, in_cols); return; } SpMat tmp(in_rows, in_cols); if(tmp.n_elem > 0) { 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); } template inline void SpMat::resize(const SizeMat& s) { arma_extra_debug_sigprint(); (*this).resize(s.n_rows, s.n_cols); } template inline void 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; } // 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_chunked(n_nonzero + 1); access::rw(new_row_indices[n_nonzero]) = 0; arrayops::inplace_set(new_col_ptrs, uword(0), in_cols + 1); for(const_iterator it = begin(); 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. memory::release(row_indices); access::rw(row_indices) = new_row_indices; memory::release(col_ptrs); 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(const SizeMat& s) { arma_extra_debug_sigprint(); (*this).reshape(s.n_rows, s.n_cols); } // this form is deprecated: don't use it template inline void SpMat::reshape(const uword in_rows, const uword in_cols, const uword dim) { arma_extra_debug_sigprint(); arma_debug_check( (dim > 1), "SpMat::reshape(): paramter 'dim' must be 0 or 1" ); if(dim == 0) { (*this).reshape(in_rows, in_cols); } else if(dim == 1) { arma_check( ((in_rows*in_cols) != n_elem), "SpMat::reshape(): changing the number of elements in a sparse matrix is currently not supported" ); // Row-wise reshaping. This is more tedious and we will use a separate sparse matrix to do it. SpMat tmp(in_rows, in_cols); for(const_row_iterator it = begin_row(); it.pos() < n_nonzero; it++) { uword vector_position = (it.row() * n_cols) + it.col(); tmp((vector_position / in_cols), (vector_position % in_cols)) = (*it); } steal_mem(tmp); } } template inline const SpMat& SpMat::zeros() { arma_extra_debug_sigprint(); if(n_nonzero != 0) { init(n_rows, n_cols); } return *this; } template inline const 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 const SpMat& SpMat::zeros(const uword in_rows, const uword in_cols) { arma_extra_debug_sigprint(); const bool already_done = ( (n_nonzero == 0) && (n_rows == in_rows) && (n_cols == in_cols) ); if(already_done == false) { init(in_rows, in_cols); } return *this; } template inline const SpMat& SpMat::zeros(const SizeMat& s) { arma_extra_debug_sigprint(); return (*this).zeros(s.n_rows, s.n_cols); } template inline const SpMat& SpMat::eye() { arma_extra_debug_sigprint(); return (*this).eye(n_rows, n_cols); } template inline const SpMat& SpMat::eye(const uword in_rows, const uword in_cols) { arma_extra_debug_sigprint(); const uword N = (std::min)(in_rows, in_cols); zeros(in_rows, in_cols); mem_resize(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; } access::rw(n_nonzero) = N; return *this; } template inline const SpMat& SpMat::eye(const SizeMat& s) { arma_extra_debug_sigprint(); return (*this).eye(s.n_rows, s.n_cols); } template inline const SpMat& SpMat::speye() { arma_extra_debug_sigprint(); return (*this).eye(n_rows, n_cols); } template inline const 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 const SpMat& SpMat::speye(const SizeMat& s) { arma_extra_debug_sigprint(); return (*this).eye(s.n_rows, s.n_cols); } template inline const 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" ); zeros(in_rows, in_cols); mem_resize( uword(density * double(in_rows) * double(in_cols) + 0.5) ); if(n_nonzero == 0) { return *this; } arma_rng::randu::fill( access::rwp(values), n_nonzero ); uvec indices = linspace( 0u, in_rows*in_cols-1, n_nonzero ); // perturb the indices for(uword i=1; i < 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 != 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 const 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 const 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" ); zeros(in_rows, in_cols); mem_resize( uword(density * double(in_rows) * double(in_cols) + 0.5) ); if(n_nonzero == 0) { return *this; } arma_rng::randn::fill( access::rwp(values), n_nonzero ); uvec indices = linspace( 0u, in_rows*in_cols-1, n_nonzero ); // perturb the indices for(uword i=1; i < 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 != 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 const 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 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 bool print_status) const { arma_extra_debug_sigprint(); bool save_okay; switch(type) { // case raw_ascii: // save_okay = diskio::save_raw_ascii(*this, name); // break; // case csv_ascii: // save_okay = diskio::save_csv_ascii(*this, name); // 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: if(print_status) { arma_debug_warn("SpMat::save(): unsupported file type"); } save_okay = false; } if(print_status && (save_okay == false)) { arma_debug_warn("SpMat::save(): couldn't write to ", name); } return save_okay; } //! save the matrix to a stream template inline bool SpMat::save(std::ostream& os, const file_type type, const bool print_status) const { arma_extra_debug_sigprint(); bool save_okay; switch(type) { // case raw_ascii: // save_okay = diskio::save_raw_ascii(*this, os); // break; // case csv_ascii: // save_okay = diskio::save_csv_ascii(*this, os); // 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: if(print_status) { arma_debug_warn("SpMat::save(): unsupported file type"); } save_okay = false; } if(print_status && (save_okay == false)) { arma_debug_warn("SpMat::save(): couldn't write to the given stream"); } return save_okay; } //! load a matrix from a file template inline bool SpMat::load(const std::string name, const file_type type, const bool print_status) { arma_extra_debug_sigprint(); bool load_okay; std::string err_msg; switch(type) { // case auto_detect: // load_okay = diskio::load_auto_detect(*this, name, err_msg); // break; // case raw_ascii: // load_okay = diskio::load_raw_ascii(*this, name, err_msg); // break; // case csv_ascii: // load_okay = diskio::load_csv_ascii(*this, name, err_msg); // 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: if(print_status) { arma_debug_warn("SpMat::load(): unsupported file type"); } load_okay = false; } if(print_status && (load_okay == false)) { if(err_msg.length() > 0) { arma_debug_warn("SpMat::load(): ", err_msg, name); } else { arma_debug_warn("SpMat::load(): couldn't read ", name); } } if(load_okay == false) { (*this).reset(); } return load_okay; } //! load a matrix from a stream template inline bool SpMat::load(std::istream& is, const file_type type, const bool print_status) { arma_extra_debug_sigprint(); bool load_okay; std::string err_msg; switch(type) { // case auto_detect: // load_okay = diskio::load_auto_detect(*this, is, err_msg); // break; // case raw_ascii: // load_okay = diskio::load_raw_ascii(*this, is, err_msg); // break; // case csv_ascii: // load_okay = diskio::load_csv_ascii(*this, is, err_msg); // 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: if(print_status) { arma_debug_warn("SpMat::load(): unsupported file type"); } load_okay = false; } if(print_status && (load_okay == false)) { if(err_msg.length() > 0) { arma_debug_warn("SpMat::load(): ", err_msg, "the given stream"); } else { arma_debug_warn("SpMat::load(): couldn't load from the given stream"); } } if(load_okay == false) { (*this).reset(); } return load_okay; } //! save the matrix to a file, without printing any error messages template inline bool SpMat::quiet_save(const std::string name, const file_type type) const { arma_extra_debug_sigprint(); return (*this).save(name, type, false); } //! save the matrix to a stream, without printing any error messages template inline bool SpMat::quiet_save(std::ostream& os, const file_type type) const { arma_extra_debug_sigprint(); return (*this).save(os, type, false); } //! load a matrix from a file, without printing any error messages template inline bool SpMat::quiet_load(const std::string name, const file_type type) { arma_extra_debug_sigprint(); return (*this).load(name, type, false); } //! load a matrix from a stream, without printing any error messages template inline bool SpMat::quiet_load(std::istream& is, const file_type type) { arma_extra_debug_sigprint(); return (*this).load(is, type, false); } /** * 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) { 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; } else if(vec_state == 2) { in_rows = 1; } } else { arma_debug_check ( ( ((vec_state == 1) && (in_cols != 1)) || ((vec_state == 2) && (in_rows != 1)) ), "SpMat::init(): object is a row or column vector; requested size is not compatible" ); } } #if (defined(ARMA_USE_CXX11) || 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 compile in C++11 mode or 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 ); // Clean out the existing memory. if (values) { memory::release(values); memory::release(row_indices); } access::rw(values) = memory::acquire_chunked (1); access::rw(row_indices) = memory::acquire_chunked(1); access::rw(values[0]) = 0; access::rw(row_indices[0]) = 0; memory::release(col_ptrs); // 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) = 0; // Try to allocate the column pointers, filling them with 0, // except for the last element which contains the maximum possible element // (so iterators terminate correctly). access::rw(col_ptrs) = memory::acquire(in_cols + 2); arrayops::inplace_set(access::rwp(col_ptrs), uword(0), in_cols + 1); access::rw(col_ptrs[in_cols + 1]) = std::numeric_limits::max(); } /** * Initialize the matrix from a string. */ template inline void SpMat::init(const std::string& text) { arma_extra_debug_sigprint(); // Figure out the size first. uword t_n_rows = 0; uword t_n_cols = 0; bool t_n_cols_found = false; std::string token; std::string::size_type line_start = 0; std::string::size_type line_end = 0; while (line_start < text.length()) { line_end = text.find(';', line_start); if (line_end == std::string::npos) line_end = text.length() - 1; std::string::size_type line_len = line_end - line_start + 1; std::stringstream line_stream(text.substr(line_start, line_len)); // Step through each column. uword line_n_cols = 0; while (line_stream >> token) { ++line_n_cols; } if (line_n_cols > 0) { if (t_n_cols_found == false) { t_n_cols = line_n_cols; t_n_cols_found = true; } else // Check it each time through, just to make sure. arma_check((line_n_cols != t_n_cols), "SpMat::init(): inconsistent number of columns in given string"); ++t_n_rows; } line_start = line_end + 1; } zeros(t_n_rows, t_n_cols); // Second time through will pick up all the values. line_start = 0; line_end = 0; uword lrow = 0; while (line_start < text.length()) { line_end = text.find(';', line_start); if (line_end == std::string::npos) line_end = text.length() - 1; std::string::size_type line_len = line_end - line_start + 1; std::stringstream line_stream(text.substr(line_start, line_len)); uword lcol = 0; eT val; while (line_stream >> val) { // Only add nonzero elements. if (val != eT(0)) { get_value(lrow, lcol) = val; } ++lcol; } ++lrow; line_start = line_end + 1; } } /** * Copy from another matrix. */ template inline void SpMat::init(const SpMat& x) { arma_extra_debug_sigprint(); // Ensure we are not initializing to ourselves. if (this != &x) { init(x.n_rows, x.n_cols); // values and row_indices may not be null. if (values != NULL) { memory::release(values); memory::release(row_indices); } access::rw(values) = memory::acquire_chunked (x.n_nonzero + 1); access::rw(row_indices) = memory::acquire_chunked(x.n_nonzero + 1); // Now copy over the elements. arrayops::copy(access::rwp(values), x.values, x.n_nonzero + 1); 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); access::rw(n_nonzero) = x.n_nonzero; } } 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::inplace_set(access::rwp(col_ptrs), uword(0), n_cols + 1); bool actually_sorted = true; if(sort_locations == true) { // 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); 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. // Now we add the elements in this sorted order. for (uword i = 0; i < sorted_indices.n_elem; ++i) { 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" ); if(i > 0) { const uword* locs_im1 = locs.colptr( sorted_indices[i-1] ); arma_debug_check( ( (locs_i[1] == locs_im1[1]) && (locs_i[0] == locs_im1[0]) ), "SpMat::SpMat(): detected identical locations" ); } access::rw(values[i]) = vals[ sorted_indices[i] ]; access::rw(row_indices[i]) = locs_i[0]; access::rw(col_ptrs[ locs_i[1] + 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"). for(uword i = 0; i < vals.n_elem; ++i) { 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" ); if(i > 0) { const uword* locs_im1 = locs.colptr(i-1); 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" ); arma_debug_check( ( (locs_i[1] == locs_im1[1]) && (locs_i[0] == locs_im1[0]) ), "SpMat::SpMat(): detected identical locations" ); } access::rw(values[i]) = vals[i]; access::rw(row_indices[i]) = 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]; } } 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::inplace_set(access::rwp(col_ptrs), uword(0), n_cols + 1); bool actually_sorted = true; if(sort_locations == true) { // 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); 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]; } } template inline void SpMat::mem_resize(const uword new_n_nonzero) { arma_extra_debug_sigprint(); if(n_nonzero != new_n_nonzero) { if(new_n_nonzero == 0) { memory::release(values); memory::release(row_indices); access::rw(values) = memory::acquire_chunked (1); access::rw(row_indices) = memory::acquire_chunked(1); access::rw( values[0]) = 0; access::rw(row_indices[0]) = 0; } else { // Figure out the actual amount of memory currently allocated // NOTE: this relies on memory::acquire_chunked() being used for the 'values' and 'row_indices' arrays const uword n_alloc = memory::enlarge_to_mult_of_chunksize(n_nonzero); if(n_alloc < new_n_nonzero) { eT* new_values = memory::acquire_chunked (new_n_nonzero + 1); uword* new_row_indices = memory::acquire_chunked(new_n_nonzero + 1); if(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); } memory::release(values); memory::release(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::remove_zeros() { arma_extra_debug_sigprint(); const uword old_n_nonzero = n_nonzero; uword new_n_nonzero = 0; const eT* old_values = values; for(uword i=0; i < old_n_nonzero; ++i) { new_n_nonzero += (old_values[i] != eT(0)) ? uword(1) : uword(0); } if(new_n_nonzero != old_n_nonzero) { if(new_n_nonzero == 0) { init(n_rows, n_cols); return; } SpMat tmp(n_rows, n_cols); tmp.mem_resize(new_n_nonzero); uword new_index = 0; const_iterator it = begin(); const_iterator it_end = end(); for(; it != it_end; ++it) { const eT val = eT(*it); if(val != eT(0)) { 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) { 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) = NULL; access::rw(x.row_indices) = NULL; access::rw(x.col_ptrs) = NULL; } } template template arma_hot 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_created_by_proxy) { (*this) = A.get_ref(); const uword nnz = n_nonzero; eT* t_values = access::rwp(values); for(uword i=0; i < nnz; ++i) { t_values[i] = func(t_values[i]); } remove_zeros(); } else { init_xform_mt(A.get_ref(), func); } } template template arma_hot 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) == true) || (is_SpMat::stored_type>::value == true) ) { // 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); // values and row_indices may not be null. if(values != NULL) { memory::release(values); memory::release(row_indices); } access::rw(values) = memory::acquire_chunked (x.n_nonzero + 1); access::rw(row_indices) = memory::acquire_chunked(x.n_nonzero + 1); 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); access::rw(n_nonzero) = x.n_nonzero; } // 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); for(uword i=0; i < nnz; ++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) } } else { init(P.get_n_rows(), P.get_n_cols()); mem_resize(P.get_n_nonzero()); typename SpProxy::const_iterator_type it = P.begin(); typename SpProxy::const_iterator_type it_end = P.end(); while(it != it_end) { access::rw(row_indices[it.pos()]) = it.row(); access::rw(values[it.pos()]) = func(*it); // NOTE: func() must produce a value of type eT (ie. act as a convertor between eT2 and eT) ++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]; } } remove_zeros(); } template inline typename SpMat::iterator SpMat::begin() { return iterator(*this); } template inline typename SpMat::const_iterator SpMat::begin() const { return const_iterator(*this); } template inline typename SpMat::iterator SpMat::end() { return iterator(*this, 0, n_cols, n_nonzero); } template inline typename SpMat::const_iterator SpMat::end() const { return const_iterator(*this, 0, n_cols, n_nonzero); } template inline typename SpMat::iterator SpMat::begin_col(const uword col_num) { return iterator(*this, 0, col_num); } template inline typename SpMat::const_iterator SpMat::begin_col(const uword col_num) const { return const_iterator(*this, 0, col_num); } template inline typename SpMat::iterator SpMat::end_col(const uword col_num) { return iterator(*this, 0, col_num + 1); } template inline typename SpMat::const_iterator SpMat::end_col(const uword col_num) const { return const_iterator(*this, 0, col_num + 1); } template inline typename SpMat::row_iterator SpMat::begin_row(const uword row_num) { return row_iterator(*this, row_num, 0); } template inline typename SpMat::const_row_iterator SpMat::begin_row(const uword row_num) const { return const_row_iterator(*this, row_num, 0); } template inline typename SpMat::row_iterator SpMat::end_row() { return row_iterator(*this, n_nonzero); } template inline typename SpMat::const_row_iterator SpMat::end_row() const { return const_row_iterator(*this, n_nonzero); } template inline typename SpMat::row_iterator SpMat::end_row(const uword row_num) { return row_iterator(*this, row_num + 1, 0); } template inline typename SpMat::const_row_iterator SpMat::end_row(const uword row_num) const { return const_row_iterator(*this, row_num + 1, 0); } template inline typename SpMat::row_col_iterator SpMat::begin_row_col() { return begin(); } template inline typename SpMat::const_row_col_iterator SpMat::begin_row_col() const { return begin(); } template inline typename SpMat::row_col_iterator SpMat::end_row_col() { return end(); } template inline typename SpMat::const_row_col_iterator SpMat::end_row_col() const { 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 inline arma_hot arma_warn_unused SpValProxy > SpMat::get_value(const uword i) { // First convert to the actual location. uword lcol = i / n_rows; // Integer division. uword lrow = i % n_rows; return get_value(lrow, lcol); } template inline arma_hot arma_warn_unused eT SpMat::get_value(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(lrow, lcol); } template inline arma_hot arma_warn_unused SpValProxy > SpMat::get_value(const uword in_row, const uword in_col) { const uword colptr = col_ptrs[in_col]; const uword next_colptr = col_ptrs[in_col + 1]; // Step through the row indices to see if our element exists. for (uword i = colptr; i < next_colptr; ++i) { const uword row_index = row_indices[i]; // First check that we have not stepped past it. if (in_row < row_index) // If we have, then it doesn't exist: return 0. { return SpValProxy >(in_row, in_col, *this); // Proxy for a zero value. } // Now check if we are at the correct place. if (in_row == row_index) // If we are, return a reference to the value. { return SpValProxy >(in_row, in_col, *this, &access::rw(values[i])); } } // We did not find it, so it does not exist: return 0. return SpValProxy >(in_row, in_col, *this); } template inline arma_hot arma_warn_unused eT SpMat::get_value(const uword in_row, const uword in_col) const { const uword colptr = col_ptrs[in_col]; const uword next_colptr = col_ptrs[in_col + 1]; // Step through the row indices to see if our element exists. for (uword i = colptr; i < next_colptr; ++i) { const uword row_index = row_indices[i]; // First check that we have not stepped past it. if (in_row < row_index) // If we have, then it doesn't exist: return 0. { return eT(0); } // Now check if we are at the correct place. if (in_row == row_index) // If we are, return the value. { return values[i]; } } // We did not find it, so it does not exist: return 0. return eT(0); } /** * Given the index representing which of the nonzero values this is, return its * actual location, either in row/col or just the index. */ template arma_hot arma_inline arma_warn_unused uword SpMat::get_position(const uword i) const { uword lrow, lcol; get_position(i, lrow, lcol); // Assemble the row/col into the element's location in the matrix. return (lrow + n_rows * lcol); } template arma_hot arma_inline void SpMat::get_position(const uword i, uword& row_of_i, uword& col_of_i) const { arma_debug_check((i >= n_nonzero), "SpMat::get_position(): index out of bounds"); col_of_i = 0; while (col_ptrs[col_of_i + 1] <= i) { col_of_i++; } row_of_i = row_indices[i]; return; } /** * Add 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. * * @param in_row Row of new element. * @param in_col Column of new element. * @param in_val Value to set new element to (default 0.0). */ template inline arma_hot arma_warn_unused eT& SpMat::add_element(const uword in_row, const uword in_col, const eT val) { arma_extra_debug_sigprint(); // 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. } // Figure out the actual amount of memory currently allocated // NOTE: this relies on memory::acquire_chunked() being used for the 'values' and 'row_indices' arrays const uword n_alloc = memory::enlarge_to_mult_of_chunksize(n_nonzero + 1); // If possible, avoid time-consuming memory allocation if(n_alloc > (n_nonzero + 1)) { arrayops::copy_backwards(access::rwp(values) + pos + 1, values + pos, (n_nonzero - pos) + 1); arrayops::copy_backwards(access::rwp(row_indices) + pos + 1, row_indices + pos, (n_nonzero - pos) + 1); // Insert the new element. access::rw(values[pos]) = val; access::rw(row_indices[pos]) = in_row; access::rw(n_nonzero)++; } else { 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_chunked (n_nonzero + 1); uword* new_row_indices = memory::acquire_chunked(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. memory::release(values); memory::release(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. * * @param in_row Row of element to be deleted. * @param in_col Column of element to be deleted. */ template inline arma_hot void SpMat::delete_element(const uword in_row, const uword in_col) { arma_extra_debug_sigprint(); // 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]) { const uword old_n_nonzero = n_nonzero; --access::rw(n_nonzero); // Remove one from the count of nonzero elements. // Found it. Now remove it. // Figure out the actual amount of memory currently allocated and the actual amount that will be required // NOTE: this relies on memory::acquire_chunked() being used for the 'values' and 'row_indices' arrays const uword n_alloc = memory::enlarge_to_mult_of_chunksize(old_n_nonzero + 1); const uword n_alloc_mod = memory::enlarge_to_mult_of_chunksize(n_nonzero + 1); // If possible, avoid time-consuming memory allocation if(n_alloc_mod == n_alloc) { if (pos < n_nonzero) // remember, we decremented n_nonzero { arrayops::copy_forwards(access::rwp(values) + pos, values + pos + 1, (n_nonzero - pos) + 1); arrayops::copy_forwards(access::rwp(row_indices) + pos, row_indices + pos + 1, (n_nonzero - pos) + 1); } } else { // Make new arrays. eT* new_values = memory::acquire_chunked (n_nonzero + 1); uword* new_row_indices = memory::acquire_chunked(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); memory::release(values); memory::release(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 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); } #ifdef ARMA_EXTRA_SPMAT_MEAT #include ARMA_INCFILE_WRAP(ARMA_EXTRA_SPMAT_MEAT) #endif //! @}