RINGMesh  Version 5.0.0
A programming library for geological model meshes
matrix.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012-2017, Association Scientifique pour la Geologie et ses
3  * Applications (ASGA). All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of ASGA nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ASGA BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  * http://www.ring-team.org
28  *
29  * RING Project
30  * Ecole Nationale Superieure de Geologie - GeoRessources
31  * 2 Rue du Doyen Marcel Roubault - TSA 70605
32  * 54518 VANDOEUVRE-LES-NANCY
33  * FRANCE
34  */
35 
36 #pragma once
37 
38 #include <ringmesh/basic/common.h>
39 
40 #include <deque>
41 #include <memory>
42 
48 namespace RINGMesh
49 {
55  {
56  heavy = 0,
57  light = 1,
58  other = 2
59  };
60 
64  template < typename T >
65  struct ElementImpl
66  {
67  const static index_t NOT_USED = index_t( -1 );
68  ElementImpl() : index( NOT_USED )
69  {
70  }
71 
72  T value;
73  index_t index;
74  };
75 
80  template < typename T >
81  class RowImpl
82  {
83  public:
85  RowImpl() : elements_( new Element[4] )
86  {
87  }
88 
89  void set_element( index_t j, const T& value )
90  {
91  index_t index = find( j );
92  if( index == NO_ID )
93  {
94  push_element( j, value );
95  }
96  else
97  {
98  elements_[index].value = value;
99  }
100  }
101 
102  void push_element( index_t j, const T& value )
103  {
104  if( nb_elements_ == capacity_ )
105  {
106  grow();
107  }
108  Element& elt = elements_[nb_elements_++];
109  elt.index = j;
110  elt.value = value;
111  }
112 
113  index_t find( index_t j ) const
114  {
115  for( auto e : range( nb_elements_ ) )
116  {
117  if( elements_[e].index == j )
118  {
119  return e;
120  }
121  }
122  return NO_ID;
123  }
124 
125  bool exist( index_t j )
126  {
127  for( auto e : range( nb_elements_ ) )
128  {
129  if( elements_[e].index == j )
130  {
131  return true;
132  }
133  }
134  return false;
135  }
136 
137  std::tuple< bool, T > get_element( index_t j ) const
138  {
139  index_t index = find( j );
140  if( index == NO_ID )
141  {
142  return std::make_tuple( false, T() );
143  }
144  T value = elements_[index].value;
145  return std::make_tuple( true, value );
146  }
147 
148  T element( index_t e ) const
149  {
150  ringmesh_assert( e < nb_elements_ );
151  return elements_[e].value;
152  }
153 
154  index_t index( index_t e ) const
155  {
156  ringmesh_assert( e < nb_elements_ );
157  return elements_[e].index;
158  }
159 
160  T& operator[]( index_t i ) const
161  {
162  ringmesh_assert( i < nb_elements_ );
163  return elements_[i].value;
164  }
165 
166  index_t nb_elements() const
167  {
168  return nb_elements_;
169  }
170 
171  private:
172  void reallocate( index_t new_capacity )
173  {
174  auto new_elements = new Element[new_capacity];
175  std::copy(
176  elements_.get(), elements_.get() + nb_elements_, new_elements );
177  elements_.reset( new_elements );
178  }
179 
180  void grow()
181  {
182  ringmesh_assert( capacity_ != 0 );
183  capacity_ = capacity_ * 2;
184  reallocate( capacity_ );
185  }
186 
187  private:
188  std::unique_ptr< Element[] > elements_{};
189  index_t nb_elements_{ 0 };
190  index_t capacity_{ 4 };
191  };
192 
199  template < typename T, typename RowType >
201  {
203 
204  public:
206  explicit SparseMatrixImpl( bool is_symmetrical = false )
207  : is_symmetrical_( is_symmetrical )
208  {
209  }
210 
211  ~SparseMatrixImpl() = default;
212 
219  bool exist( index_t i, index_t j ) const
220  { // test existence of the i-j element
221  ringmesh_assert( i < ni_ && j < nj_ && i >= 0 && j >= 0 );
222  return rows_[i].exist( j );
223  }
224 
230  index_t get_nb_elements_in_line( index_t i ) const
231  {
232  ringmesh_assert( i < ni_ );
233  return rows_[i].nb_elements();
234  }
235 
242  index_t get_column_in_line( index_t i, index_t e ) const
243  {
244  return rows_[i].index( e );
245  }
246 
255  std::tuple< bool, index_t > get_index_in_line(
256  index_t i, index_t j ) const
257  {
258  ringmesh_assert( i < ni_ && j < nj_ && i >= 0 && j >= 0 );
259  index_t index = rows_[i].find( j );
260  return std::make_tuple( index != NO_ID, index );
261  }
262 
267  index_t ni() const
268  {
269  return ni_;
270  }
271 
276  index_t nj() const
277  {
278  return nj_;
279  }
280 
285  bool is_symmetrical() const
286  {
287  return is_symmetrical_;
288  }
289 
296  void build_matrix( index_t ni, index_t nj )
297  {
298  ni_ = ni;
299  nj_ = nj;
300  rows_.reset( new Row[ni] );
301  }
302 
311  T get_element_in_line( index_t i, index_t e ) const
312  {
313  ringmesh_unused( i );
314  ringmesh_unused( e );
316  return T();
317  }
318 
319  protected:
320  std::unique_ptr< Row[] > rows_{};
321  index_t ni_{ 0 }, nj_{ 0 }; // matrix dimensions
323  };
324 
329  template < typename T,
330  MatrixType Light = MatrixType(
331  2 * sizeof( T ) <= 2 * sizeof( index_t ) + sizeof( T ) ) >
332  class SparseMatrix : public SparseMatrixImpl< T, T >
333  {
334  };
335 
339  template < typename T >
340  class SparseMatrix< T, light > : public SparseMatrixImpl< T, T >
341  {
342  public:
344  explicit SparseMatrix( bool is_symetrical = false )
345  : SparseMatrixImpl< T, T >( is_symetrical )
346  {
347  }
348 
355  void set_element( index_t i, index_t j, const T& value )
356  {
357  this->rows_[i].set_element( j, value );
358  if( this->is_symmetrical_ )
359  {
360  this->rows_[j].set_element( i, value );
361  }
362  }
363 
371  void push_element( index_t i, index_t j, const T& value )
372  {
373  this->rows_[i].push_element( j, value );
374  if( this->is_symmetrical_ )
375  {
376  this->rows_[j].push_element( i, value );
377  }
378  }
379 
380  /*
381  * get the value of element i-j in the matrix, returns false if no
382  * element is found
383  * @param[in] i row index
384  * @param[in] j column index
385  * @return a tuple containing:
386  * - a boolean: true if success and false if the element is inexistent
387  * - value to retrieve if any.
388  */
389  std::tuple< bool, T > get_element( index_t i, index_t j ) const
390  { // valeur du i_j_ieme element stocke sur la ligne
391  return this->rows_[i].get_element( j );
392  }
393 
401  T get_element_in_line( index_t i, index_t e ) const
402  { // valeur du e_ieme element stocke sur la ligne
403  return this->rows_[i][e];
404  }
405  };
406 
414  template < typename T >
415  class SparseMatrix< T, heavy > : public SparseMatrixImpl< T, index_t >
416  {
417  public:
419  explicit SparseMatrix( bool is_symetrical = false )
420  : SparseMatrixImpl< T, index_t >( is_symetrical )
421  {
422  }
423 
430  void set_element( index_t i, index_t j, const T& value )
431  {
432  // small difference with light type: we fill a deque
433  index_t value_id;
434  if( this->exist( i, j ) )
435  {
436  value_id = get_value_id( i, j );
437  values_[value_id] = value;
438  }
439  else
440  {
441  values_.push_back( value );
442  value_id = values_.size() - 1;
443  }
444  this->rows_[i].set_element( j, value_id );
445  if( this->is_symmetrical_ )
446  {
447  this->rows_[j].set_element( i, value_id );
448  }
449  }
450 
451  /*
452  * get the value of element i-j in the matrix, returns false if no
453  * element is found
454  * @param[in] i row index
455  * @param[in] j column index
456  * @return the value to retrieve
457  */
458  T get_element( index_t i, index_t j ) const
459  {
460  bool value_exists{ false };
461  index_t value_id;
462  std::tie( value_exists, value_id ) =
463  this->rows_[i].get_element( j );
464  if( !value_exists )
465  {
466  return T();
467  }
468  return values_[value_id];
469  }
470 
478  T get_element_in_line( index_t i, index_t e ) const
479  {
480  index_t value_id = this->rows_[i][e];
481  return values_[value_id];
482  }
483 
484  private:
485  index_t get_value_id( index_t i, index_t j )
486  {
487  return this->rows_[i].find( j );
488  }
489 
490  private:
491  std::deque< T > values_{};
492  };
493 
494  // Note: without light or heavy, it does not compile on Windows.
495  // Error C2770. BC
496  template < typename T >
497  std::vector< T > product_matrix_by_vector(
498  const SparseMatrix< T, light >& mat1, const std::vector< T >& mat2 )
499  {
500  ringmesh_assert( mat1.nj() == mat2.size() );
501  std::vector< T > result( mat1.ni(), 0 );
502  parallel_for( mat1.ni(), [&mat1, &mat2, &result]( index_t i ) {
503  for( auto e : range( mat1.get_nb_elements_in_line( i ) ) )
504  {
505  index_t j = mat1.get_column_in_line( i, e );
506  T i_j_result = mat1.get_element_in_line( i, e );
507  i_j_result *= mat2[j];
508  result[i] += i_j_result;
509  }
510  } );
511  return result;
512  }
513 
514 } // namespace RINGMesh
void push_element(index_t i, index_t j, const T &value)
Definition: matrix.h:371
#define ringmesh_disable_copy_and_move(Class)
Definition: common.h:76
void reallocate(index_t new_capacity)
Definition: matrix.h:172
std::tuple< bool, T > get_element(index_t j) const
Definition: matrix.h:137
static const index_t NOT_USED
Definition: matrix.h:67
index_t nj() const
Definition: matrix.h:276
SparseMatrix(bool is_symetrical=false)
Definition: matrix.h:344
void ringmesh_unused(const T &)
Definition: common.h:105
index_t index(index_t e) const
Definition: matrix.h:154
MatrixType
enum of MatrixType, This is useful to further specialize the template in the future ...
Definition: matrix.h:54
T get_element_in_line(index_t i, index_t e) const
Definition: matrix.h:478
void set_element(index_t i, index_t j, const T &value)
Definition: matrix.h:355
T get_element_in_line(index_t i, index_t e) const
Definition: matrix.h:311
void set_element(index_t i, index_t j, const T &value)
Definition: matrix.h:430
void set_element(index_t j, const T &value)
Definition: matrix.h:89
SparseMatrixImpl(bool is_symmetrical=false)
Definition: matrix.h:206
bool exist(index_t i, index_t j) const
Definition: matrix.h:219
T get_element(index_t i, index_t j) const
Definition: matrix.h:458
index_t find(const container &in, const T &value)
Returns the position of the first entity matching.
Definition: algorithm.h:55
index_t get_column_in_line(index_t i, index_t e) const
gets the j that correspond to the given index within the row
Definition: matrix.h:242
SparseMatrix(bool is_symetrical=false)
Definition: matrix.h:419
T get_element_in_line(index_t i, index_t e) const
Definition: matrix.h:401
Basic "Row" of the matrix, this stores the elements of the matrix in a line-oriented way...
Definition: matrix.h:81
index_t ni() const
Definition: matrix.h:267
Basic container for the sparse matrix, i.e. the "elements".
Definition: matrix.h:65
std::tuple< bool, index_t > get_index_in_line(index_t i, index_t j) const
gets the rows_ index corresponding to a given i-j couple
Definition: matrix.h:255
void push_element(index_t j, const T &value)
Definition: matrix.h:102
T & operator[](index_t i) const
Definition: matrix.h:160
#define ringmesh_assert(x)
index_t get_value_id(index_t i, index_t j)
Definition: matrix.h:485
bool exist(index_t j)
Definition: matrix.h:125
index_t get_nb_elements_in_line(index_t i) const
gets number of elements within a row
Definition: matrix.h:230
void build_matrix(index_t ni, index_t nj)
Definition: matrix.h:296
T element(index_t e) const
Definition: matrix.h:148
Classes to build GeoModel from various inputs.
Definition: algorithm.h:48
bool is_symmetrical() const
Definition: matrix.h:285
index_t nb_elements() const
Definition: matrix.h:166
std::tuple< bool, T > get_element(index_t i, index_t j) const
Definition: matrix.h:389
This is the parent class for sparse matrices, the main difference between light and heavy type matric...
Definition: matrix.h:200
index_t find(index_t j) const
Definition: matrix.h:113
std::vector< T > product_matrix_by_vector(const SparseMatrix< T, light > &mat1, const std::vector< T > &mat2)
Definition: matrix.h:497
void parallel_for(index_t size, const ACTION &action)
Definition: common.h:244
#define ringmesh_assert_not_reached