vector_sparse.hpp
Go to the documentation of this file.
1 /*!
2  * \brief Sparse vector class
3  *
4  * \author O. Krause
5  * \date 2013
6  *
7  *
8  * \par Copyright 1995-2015 Shark Development Team
9  *
10  * <BR><HR>
11  * This file is part of Shark.
12  * <http://image.diku.dk/shark/>
13  *
14  * Shark is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU Lesser General Public License as published
16  * by the Free Software Foundation, either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * Shark is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  * GNU Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public License
25  * along with Shark. If not, see <http://www.gnu.org/licenses/>.
26  *
27  */
28 #ifndef REMORA_VECTOR_SPARSE_HPP
29 #define REMORA_VECTOR_SPARSE_HPP
30 
31 #include "assignment.hpp"
32 #include "detail/vector_proxy_classes.hpp"
33 #include <vector>
34 
35 #include <boost/serialization/collection_size_type.hpp>
36 #include <boost/serialization/nvp.hpp>
37 #include <boost/serialization/vector.hpp>
38 namespace remora{
39 
40 /** \brief Compressed array based sparse vector
41  *
42  * a sparse vector of values of type T of variable size. The non zero values are stored as
43  * two seperate arrays: an index array and a value array. The index array is always sorted
44  * and there is at most one entry for each index. Inserting an element can be time consuming.
45  * If the vector contains a few zero entries, then it is better to have a normal vector.
46  * If the vector has a very high dimension with a few non-zero values, then this vector is
47  * very memory efficient (at the cost of a few more computations).
48  *
49  * For a \f$n\f$-dimensional compressed vector and \f$0 \leq i < n\f$ the non-zero elements
50  * \f$v_i\f$ are mapped to consecutive elements of the index and value container, i.e. for
51  * elements \f$k = v_{i_1}\f$ and \f$k + 1 = v_{i_2}\f$ of these containers holds \f$i_1 < i_2\f$.
52  *
53  * Supported parameters for the adapted array (indices and values) are \c unbounded_array<> ,
54  * \c bounded_array<> and \c std::vector<>.
55  *
56  * \tparam T the type of object stored in the vector (like double, float, complex, etc...)
57  * \tparam I the indices stored in the vector
58  */
59 template<class T, class I = std::size_t>
60 class compressed_vector:public vector_container<compressed_vector<T, I>, cpu_tag > {
61 
62  typedef T& true_reference;
63  typedef compressed_vector<T, I> self_type;
64 public:
65  typedef T value_type;
66  typedef const T& const_reference;
67 
68  typedef I size_type;
69 
70  class reference {
71  private:
72 
73  const_reference value()const {
74  return const_cast<self_type const&>(m_vector)(m_i);
75  }
76  value_type& ref() const {
77  //find position of the index in the array
78  size_type const* start = m_vector.m_indices.data();
79  size_type const* end = start + m_vector.nnz();
80  size_type const *pos = std::lower_bound(start,end,m_i);
81 
82  if (pos != end&& *pos == m_i)
83  return m_vector.m_values[pos-start];
84  else {
85  //create iterator to the insertion position and insert new element
86  iterator posIter(m_vector.m_values.data(),m_vector.m_indices.data(),pos-start);
87  return *m_vector.set_element(posIter, m_i, m_vector.m_zero);
88  }
89  }
90 
91  public:
92  // Construction and destruction
93  reference(self_type& m, size_type i):
94  m_vector(m), m_i(i) {}
95 
96  // Assignment
97  value_type& operator = (value_type d)const {
98  return ref()=d;
99  }
100 
101  value_type& operator=(reference const& v ){
102  return ref() = v.value();
103  }
104 
105  value_type& operator += (value_type d)const {
106  return ref()+=d;
107  }
108  value_type& operator -= (value_type d)const {
109  return ref()-=d;
110  }
111  value_type& operator *= (value_type d)const {
112  return ref()*=d;
113  }
114  value_type& operator /= (value_type d)const {
115  return ref()/=d;
116  }
117 
118  // Comparison
119  bool operator == (value_type d) const {
120  return value() == d;
121  }
122  bool operator != (value_type d) const {
123  return value() != d;
124  }
125 
126  operator const_reference() const{
127  return value();
128  }
129  private:
130  self_type& m_vector;
131  size_type m_i;
132  };
133 
134  typedef vector_reference<self_type const> const_closure_type;
135  typedef vector_reference<self_type> closure_type;
136  typedef sparse_vector_storage<T,I> storage_type;
137  typedef sparse_vector_storage<value_type const,size_type const> const_storage_type;
138  typedef elementwise<sparse_tag> evaluation_category;
139 
140  // Construction and destruction
141  compressed_vector():m_size(0), m_nnz(0),m_indices(1,0),m_zero(0){}
142  explicit compressed_vector(size_type size, value_type value = value_type(), size_type non_zeros = 0)
143  :m_size(size), m_nnz(0), m_indices(non_zeros,0), m_values(non_zeros),m_zero(0){}
144  template<class AE>
145  compressed_vector(vector_expression<AE, cpu_tag> const& ae, size_type non_zeros = 0)
146  :m_size(ae().size()), m_nnz(0), m_indices(non_zeros,0), m_values(non_zeros),m_zero(0)
147  {
148  assign(*this, ae);
149  }
150 
151  // Accessors
152  size_type size() const {
153  return m_size;
154  }
155  size_type nnz_capacity() const {
156  return m_indices.size();
157  }
158  size_type nnz() const {
159  return m_nnz;
160  }
161 
162  void set_filled(size_type filled) {
163  REMORA_SIZE_CHECK(filled <= nnz_capacity());
164  m_nnz = filled;
165  }
166 
167  ///\brief Returns the underlying storage structure for low level access
168  storage_type raw_storage(){
169  return {m_values.data(), m_indices.data(), m_nnz};
170  }
171 
172  ///\brief Returns the underlying storage structure for low level access
173  const_storage_type raw_storage() const{
174  return {m_values.data(), m_indices.data(), m_nnz};
175  }
176 
177  typename device_traits<cpu_tag>::queue_type& queue(){
178  return device_traits<cpu_tag>::default_queue();
179  }
180 
181  void resize(size_type size) {
182  m_size = size;
183  m_nnz = 0;
184  }
185  void reserve(size_type non_zeros) {
186  if(non_zeros <= nnz_capacity()) return;
187  non_zeros = std::min(size(),non_zeros);
188  m_indices.resize(non_zeros);
189  m_values.resize(non_zeros);
190  }
191 
192  // Element access
193  const_reference operator()(size_type i) const {
194  REMORA_SIZE_CHECK(i < m_size);
195  std::size_t pos = lower_bound(i);
196  if (pos == nnz() || m_indices[pos] != i)
197  return m_zero;
198  return m_values [pos];
199  }
200  reference operator()(size_type i) {
201  return reference(*this,i);
202  }
203 
204 
205  const_reference operator [](size_type i) const {
206  return (*this)(i);
207  }
208  reference operator [](size_type i) {
209  return (*this)(i);
210  }
211 
212  // Zeroing
213  void clear() {
214  m_nnz = 0;
215  }
216 
217  // Assignment
218  compressed_vector& operator = (compressed_vector const& v) {
219  m_size = v.m_size;
220  m_nnz = v.m_nnz;
221  m_indices = v.m_indices;
222  m_values = v.m_values;
223  return *this;
224  }
225  template<class C> // Container assignment without temporary
226  compressed_vector& operator = (vector_container<C, cpu_tag> const& v) {
227  resize(v().size(), false);
228  assign(*this, v);
229  return *this;
230  }
231  template<class AE>
232  compressed_vector& operator = (vector_expression<AE, cpu_tag> const& ae) {
233  self_type temporary(ae, nnz_capacity());
234  swap(temporary);
235  return *this;
236  }
237 
238  // Swapping
239  void swap(compressed_vector& v) {
240  std::swap(m_size, v.m_size);
241  std::swap(m_nnz, v.m_nnz);
242  m_indices.swap(v.m_indices);
243  m_values.swap(v.m_values);
244  }
245 
246  friend void swap(compressed_vector& v1, compressed_vector& v2){
247  v1.swap(v2);
248  }
249 
250  // Iterator types
251  typedef iterators::compressed_storage_iterator<value_type const, size_type const> const_iterator;
252  typedef iterators::compressed_storage_iterator<value_type, size_type const> iterator;
253 
254  const_iterator begin() const {
255  return const_iterator(m_values.data(),m_indices.data(),0);
256  }
257 
258  const_iterator end() const {
259  return const_iterator(m_values.data(),m_indices.data(),nnz());
260  }
261 
262  iterator begin() {
263  return iterator(m_values.data(),m_indices.data(),0);
264  }
265 
266  iterator end() {
267  return iterator(m_values.data(),m_indices.data(),nnz());
268  }
269 
270  // Element assignment
271  iterator set_element(iterator pos, size_type index, value_type value) {
272  REMORA_RANGE_CHECK(size_type(pos - begin()) <=m_size);
273 
274  if(pos != end() && pos.index() == index){
275  *pos = value;
276  return pos;
277  }
278  //get position of the new element in the array.
279  std::ptrdiff_t arrayPos = pos - begin();
280  if (m_nnz <= nnz_capacity())//reserve more space if needed, this invalidates pos.
281  reserve(std::max<std::size_t>(2 * nnz_capacity(),1));
282 
283  //copy the remaining elements to make space for the new ones
284  std::copy_backward(
285  m_values.begin()+arrayPos,m_values.begin() + m_nnz , m_values.begin() + m_nnz +1
286  );
287  std::copy_backward(
288  m_indices.begin()+arrayPos,m_indices.begin() + m_nnz , m_indices.begin() + m_nnz +1
289  );
290  //insert new element
291  m_values[arrayPos] = value;
292  m_indices[arrayPos] = index;
293  ++m_nnz;
294 
295 
296  //return new iterator to the inserted element.
297  return iterator(m_values.data(),m_indices.data(),arrayPos);
298  }
299 
300  iterator clear_range(iterator start, iterator end) {
301  //get position of the elements in the array.
302  std::ptrdiff_t startPos = start - begin();
303  std::ptrdiff_t endPos = end - begin();
304 
305  //remove the elements in the range
306  std::copy(
307  m_values.begin()+endPos,m_values.begin() + m_nnz, m_values.begin() + startPos
308  );
309  std::copy(
310  m_indices.begin()+endPos,m_indices.begin() + m_nnz , m_indices.begin() + startPos
311  );
312  m_nnz -= endPos - startPos;
313  //return new iterator to the next element
314  return iterator(m_values.data(),m_indices.data(), startPos);
315  }
316 
317  iterator clear_element(iterator pos){
318  //get position of the element in the array.
319  std::ptrdiff_t arrayPos = pos - begin();
320  if(arrayPos == m_nnz-1){//last element
321  --m_nnz;
322  return end();
323  }
324 
325  std::copy(
326  m_values.begin()+arrayPos+1,m_values.begin() + m_nnz , m_values.begin() + arrayPos
327  );
328  std::copy(
329  m_indices.begin()+arrayPos+1,m_indices.begin() + m_nnz , m_indices.begin() + arrayPos
330  );
331  //return new iterator to the next element
332  return iterator(m_values.data(),m_indices.data(),arrayPos);
333  }
334 
335  // Serialization
336  template<class Archive>
337  void serialize(Archive& ar, const unsigned int /* file_version */) {
338  boost::serialization::collection_size_type s(m_size);
339  ar & boost::serialization::make_nvp("size",s);
340  if (Archive::is_loading::value) {
341  m_size = s;
342  }
343  // ISSUE: m_indices and m_values are undefined between m_nnz and capacity (trouble with 'nan'-values)
344  ar & boost::serialization::make_nvp("nnz", m_nnz);
345  ar & boost::serialization::make_nvp("indices", m_indices);
346  ar & boost::serialization::make_nvp("values", m_values);
347  }
348 
349 private:
350  std::size_t lower_bound( size_type t)const{
351  size_type const* begin = m_indices.data();
352  size_type const* end = m_indices.data()+nnz();
353  return std::lower_bound(begin, end, t)-begin;
354  }
355 
356  size_type m_size;
357  size_type m_nnz;
358  std::vector<size_type> m_indices;
359  std::vector<value_type> m_values;
360  value_type m_zero;
361 };
362 
363 template<class T>
364 struct vector_temporary_type<T,sparse_tag, cpu_tag>{
365  typedef compressed_vector<T> type;
366 };
367 
368 }
369 
370 #endif