NUMCXX  0.13.20181108
Numerical library for small projects and teaching purposes
tsparsematrix.ixx
Go to the documentation of this file.
1 #include <algorithm>
7 #include <functional>
8 #include "tsolver-lapacklu.hxx"
9 
10 namespace numcxx
11 {
12 
13 
14  template <typename T>
15  class TSparseMatrix<T>::RowEntry
16  {
17  public:
18  index i;
19  T a;
20  };
21 
22  template <typename T>
23  class TSparseMatrix<T>::Extension
24  {
25  public:
27  const index n;
28 
29 
31  std::shared_ptr<std::vector<int>> pIA;
32 
34  std::shared_ptr<std::vector<int>> pJA;
35 
37  std::shared_ptr<std::vector <T> > pA;
38 
40  const T zero=0;
41 
43  Extension(index n);
44 
46  T& entry(int i, int j);
47 
48  void apply(const TArray<T>&U, TArray<T>&V);
49 
50  int next;
51 
52  T read_entry(int i, int j);
53 
54  bool empty();
55  };
56 
57 
58 
59 
60 
61 
62  template <typename T>
64  n(n1),
65  pA(std::make_shared<TArray1<T>>(n1)),
66  pJA(std::make_shared<TArray1<int>>(n1)),
67  pIA(std::make_shared<TArray1<int>>(n1+1))
68  {
69  if (n1!=n2)
70  {
71  char errormsg[80];
72  snprintf(errormsg,80,"numcxx::TSparseMatrix::TSparseMatrix unexpected non-equal matrix dimensions\n");
73  throw std::length_error(errormsg);
74  }
75 
76 
77  auto &IA=*pIA;
78  auto &JA=*pJA;
79  auto &A=*pA;
80  IA=0;
81  JA=0;
82  A=0.0;
83  }
84 
85  template <typename T>
86  inline TSparseMatrix<T>::TSparseMatrix(const std::initializer_list<std::initializer_list<T>> &il):
87  TSparseMatrix(il.size(), il.begin()->size())
88  {
89  index i=0;
90  for (auto jl = il.begin() ; jl != il.end(); jl++,i++)
91  {
92  index j=0;
93  for (auto x = jl->begin() ; x != jl->end(); x++,j++)
94  this->operator()(i,j)= *x;
95 
96  }
97  flush();
98  }
99 
100  template <typename T>
101  inline std::shared_ptr<TSparseMatrix <T> > TSparseMatrix<T>::create(const std::initializer_list<std::initializer_list<T>> &il)
102  {
103  return std::make_shared<TSparseMatrix <T>>(il);
104  }
105 
106  template <typename T>
108  {
109  if (pExt==nullptr) return;
110  if (pExt->empty()) return;
111  int nJA_old=0;
112  if (!empty())
113  nJA_old=pJA->size();
114  int next=pExt->next;
115  int nJA_new=nJA_old+next;
116 
117  auto pNew_IA=TArray1<int>::create(n+1);
118  auto pNew_JA=TArray1<int>::create(nJA_new);
119  auto pNew_A=TArray1<T>::create(nJA_new);
120 
121  auto &New_IA=*pNew_IA;
122  auto &New_JA=*pNew_JA;
123  auto &New_A=*pNew_A;
124 
125  auto &Old_IA=*pIA;
126  auto &Old_JA=*pJA;
127  auto &Old_A=*pA;
128 
129  auto &Ext_IA=*pExt->pIA;
130  auto &Ext_JA=*pExt->pJA;
131  auto &Ext_A=*pExt->pA;
132 
133  int maxrow_ext=0;
134  for (index i=0;i<n;i++)
135  {
136  int k0=i;
137  int lrow=1;
138  for (int k=k0; k>=0;k0=k, k=Ext_IA[k]) lrow++;
139  maxrow_ext=std::max(lrow,maxrow_ext);
140  }
141 
142  std::vector<RowEntry> row(maxrow+maxrow_ext+10);
143  int New_maxrow=0;
144  int j=0;
145  int i;
146  for(i=0;i<n;i++)
147  {
148  // put extension entries into row and sort them
149  int lxrow=0;
150  int k0=i;
151  for (int k=k0; k>=0;k0=k, k=Ext_IA[k])
152  if (Ext_JA[k]>=0)
153  {
154  row[lxrow].i=Ext_JA[k];
155  row[lxrow].a=Ext_A[k];
156  lxrow++;
157  }
158 
159 
160  std::sort(row.begin(),row.begin()+lxrow,
161  [](const RowEntry&e1, const RowEntry &e2)-> bool
162  {
163  return (e1.i<e2.i);
164  });
165 
166  // jointly sort Old and New entries into New_JA
167  int j0;
168  j0=j;
169  New_IA[i]=j;
170  int irow=0;
171  int k=Old_IA[i];
172 
173  for(;;)
174  {
175  if (k<Old_IA[i+1] && ((irow>lxrow) ||Old_JA[k]<row[irow].i))
176  {
177  New_JA[j]=Old_JA[k];
178  New_A[j]=Old_A[k];
179  k++;
180  j++;
181  continue;
182  }
183 
184  if (irow<lxrow)
185  {
186  New_JA[j]=row[irow].i;
187  New_A[j]=row[irow].a;
188  irow++;
189  j++;
190  continue;
191  }
192  break;
193  }
194  New_maxrow=std::max(New_maxrow,j-j0);
195  }
196  New_IA[i]=j;
197 
198  pIA=pNew_IA;
199  pJA=pNew_JA;
200  pA=pNew_A;
201  maxrow=New_maxrow;
202  _pattern_changed=false;
203  _first_flush_done=true;
204  pExt=nullptr;
205  }
206 
207 
208  template <typename T>
209  inline void TSparseMatrix<T>::apply(const TArray<T> &u,TArray<T> &v ) const
210  {
211  v.resize(u.size());
212  auto &IA=*pIA;
213  auto &JA=*pJA;
214  auto &A=*pA;
215  for (int i=0;i<n;i++)
216  {
217  v[i]=0;
218  for (int j=IA[i];j<IA[i+1];j++)
219  v[i]+=A[j]*u[JA[j]];
220  }
221  if (pExt!=nullptr)
222  pExt->apply(u,v);
223  }
224 
225  template <typename T>
226  inline T& TSparseMatrix<T>::operator()(int i, int j)
227  {
228  auto &IA=*pIA;
229  auto &JA=*pJA;
230  auto &A=*pA;
231 #ifdef NUMCXX_CHECK_BOUNDS
232  if (i<0||j<0||i>=n||j>=n)
233  {
234  char errormsg[80];
235  snprintf(errormsg,80,"numcxx::TSparseMatrix::op(): shape=(%d %d) but using index (%d %d)\n",n,n,i,j);
236  throw std::out_of_range(errormsg);
237  }
238 #endif
239  for (int k=IA[i];k<IA[i+1];k++)
240  if (JA[k]==j)
241  return A[k];
242 
243  if (pExt==nullptr) pExt=std::make_shared<Extension>(n);
244  pattern_changed(true);
245  return pExt->entry(i,j);
246  }
247 
248  template <typename T>
249  inline std::shared_ptr<TMatrix<T>> TSparseMatrix<T>::copy_as_dense()
250  {
251  flush();
252  auto pM=TMatrix<T>::create(n,n);
253 
254  auto &M=*pM;
255  auto &IA=*pIA;
256  auto &JA=*pJA;
257  auto &A=*pA;
258  M=0.0;
259  for (int i=0;i<n;i++)
260  for (int j=IA[i];j<IA[i+1];j++)
261  M(i,JA[j])=A[j];
262  return pM;
263  }
264 
265 
268 
269 
270 
271  template <typename T>
273  n(n),
274  next(0),
275  pA(std::make_shared<std::vector<T>>(n)),
276  pJA(std::make_shared<std::vector<int>>(n)),
277  pIA(std::make_shared<std::vector<int>>(n))
278  {
279  auto &IA=*pIA;
280  auto &JA=*pJA;
281  auto &A=*pA;
282 
283  // initIAlization for zero main dIAgonal elements.
284  for (int i=0;i<n;i++)
285  {
286  IA[i]=-1;
287  JA[i]=-1;
288  A[i]=0;
289  }
290  }
291 
292  template <typename T>
293  inline T& TSparseMatrix<T>::Extension::entry(int i, int j)
294  {
295  auto &IA=*pIA;
296  auto &JA=*pJA;
297  auto &A=*pA;
298 
299  // Assume "classical" extension scheme
300  // without main dIAgonal entries,
301  // and with index shift by -1
302 
303  // InitIAl state
304 
305  // a 0 0 0 0
306  // xJA -1 -1 -1 -1
307  // xIA |-1|-1|-1|-1|
308 
309 
310 
311  // Insert 1,1 = 1
312  // xJA[i]=-1/i triggert Existenz des Elements.
313 
314  // erstes element der Zeile
315  // a 0 1 0 0
316  // xJA -1 1 -1 -1
317  // xIA |-1|-1|-1|-1|
318 
319  // naechstes Element der Zeile
320 
321  // a 0 1 0 0 1
322  // xJA -1 2 -1 -1 3
323  // xIA |-1|4|-1|-1|-1
324 
325 
326  // do we access row i for the first time ?
327  // then we initIAlize this row
328 
329  if (JA[i]<0)
330  {
331  JA[i]=j;
332  A[i]=0.0;
333  next++;
334  return A[i];
335  }
336 
337 
338  // xJA /xa entries valid only if xIA entry >=0
339  int k0=i;
340  for (int k=k0; k>=0;k0=k, k=IA[k])
341  {
342 
343  if (JA[k]==j) // no need to add new entry
344  return A[k];
345  }
346  // Access failed (IA[k0]<0)
347  // so we add next entry at end of matrix
348  // and put its index into IA[k0]
349 
350  next++;
351  A.push_back(0);
352  JA.push_back(j);
353  IA.push_back(-1);
354  IA[k0]=A.size()-1;
355  return A[IA[k0]];
356  }
357 
358 
359  template <typename T>
361  {
362 
363  if (empty()) return;
364  auto &IA=*pIA;
365  auto &JA=*pJA;
366  auto &A=*pA;
367 
368  for (int i=0;i<n; i++)
369  for (int k=i; k>=0;k=IA[k])
370  {
371  int j=JA[k];
372  if (j>=0)
373  v[i]+=A[k]*u[j];
374  }
375  }
376 
377 
378  template <typename T>
379  inline T TSparseMatrix<T>::Extension::read_entry(int i, int j)
380  {
381  auto &IA=*pIA;
382  auto &JA=*pJA;
383  auto &A=*pA;
384 
385 
386  int k0=i;
387  for (int k=k0; k>=0;k0=k, k=IA[k])
388  if (JA[k]==j)
389  return A[k];
390 
391  return zero;
392  }
393 
394  template <typename T>
396  {
397  if (next==0) return true;
398  return false;
399  }
400 
401 
402  template <typename T>
403  inline std::shared_ptr<TMatrix<T>> TSparseMatrix<T>::calculate_inverse()
404  {
405  auto pA=copy_as_dense(); // essentially superfluous, but we seldomly use this
406  auto pLU=TSolverLapackLU<T>::create(pA);
407  return pLU->calculate_inverse();
408  }
409 
410 }
std::shared_ptr< TArray1< int > > pJA
Column indices.
std::shared_ptr< TMatrix< T > > calculate_inverse()
Calculate inverse of sparse matrix whuch is dense.
Sparse matrix class using CRS storage scheme.
std::shared_ptr< TMatrix< T > > copy_as_dense()
Create a dense matrix from the sparse matrix.
A::value_type max(const A &a)
Maximum of array or expression.
Definition: util.ixx:70
static std::shared_ptr< TSolverLapackLU< T > > create(const std::shared_ptr< TMatrix< T >> pMatrix)
Static wrapper around constructor.
std::shared_ptr< Extension > pExt
static std::shared_ptr< TMatrix< T > > create(index n1, index n2)
Construct empty square matrix.
Definition: tmatrix.hxx:81
void flush()
Re-create the internal data structure in order to accomodated all newly created elements.
TArray is the common template base class for arrays and dense matrices of the numcxx project...
Definition: tarray.hxx:17
size_t size() const
Obtain size of array.
Definition: tarray.hxx:58
Header for numcxx::TSolverLapackLU.
bool pattern_changed() const
Check if pattern has changed after last solver update.
std::shared_ptr< TArray1< T > > pA
Entries.
unsigned int index
Definition: numcxx.hxx:21
One dimensional array class.
Definition: tarray1.hxx:31
void apply(const TArray< T > &U, TArray< T > &V) const
Apply sparse matrix to vector.
void resize(size_t n)
Resize array.
Definition: tarray.ixx:282
Numcxx template library.
Definition: expression.ixx:41
static std::shared_ptr< TArray1< T > > create(index n1)
Construct smart pointer empty 1D Array.
Definition: tarray1.hxx:81
T & operator()(int i, int j)
Access operator.
static std::shared_ptr< TSparseMatrix< T > > create(index n1, index n2)
Static wrapper around corresponding constructor.
std::shared_ptr< TArray1< int > > pIA
Row pointers.