TMB Documentation  v1.9.11
expm.hpp
1 // Copyright (C) 2013-2015 Kasper Kristensen
2 // License: GPL-2
3 
4 namespace atomic{
5  /* Matrix class with methods required by Pade approximation */
6  template<class Type>
7  struct Block {
8  typedef Type ScalarType;
9  matrix<Type> A;
10  Block(){}
11  Block(matrix<Type> A_){A=A_;}
12  /* Matrix multiply */
13  Block<Type> operator*(Block<Type> other){
14  return Block( this->A * other.A );
15  }
16  /* Scale matrix */
17  Block<Type> scale(Type c){
18  return Block( c * this->A );
19  }
20  /* Add identity matrix */
21  Block<Type> addIdentity(){
22  int n=A.rows();
23  matrix<double> I(n,n);
24  I.setIdentity();
25  return Block( this->A + I );
26  }
27  /* Infinity norm */
28  Type norm(){
29  matrix<Type> Aabs(this->A.rows(),this->A.cols());
30  Aabs = A.array().abs();
31  vector<Type> rsAabs = Aabs.rowwise().sum();
32  return rsAabs.maxCoeff();
33  }
34  /* Matrix inverse */
35  Block<Type> inverse(){
36  return Block( this->A.inverse() );
37  }
38  /* Increment/decrement */
39  Block<Type>& operator+=(Block<Type> x){ this->A += x.A; return *this; }
40  Block<Type>& operator-=(Block<Type> x){ this->A -= x.A; return *this; }
41  /* Methods for operator square root */
42  typedef Eigen::SelfAdjointEigenSolver<
43  Eigen::Matrix<Type, Eigen::Dynamic, Eigen::Dynamic> > SAES_t;
44  /* Solve *special case* of Sylvester equation: XA+AX=Y */
45  Block<Type> sylvester(Block<Type> Y) {
46  SAES_t saes(A);
47  matrix<Type> V = saes.eigenvectors();
48  vector<Type> D = saes.eigenvalues();
49  // Transform
50  matrix<Type> Y_ = V.transpose() * Y.A * V;
51  // Solve
52  for (int i=0; i<Y_.rows(); i++)
53  for (int j=0; j<Y_.cols(); j++)
54  Y_(i, j) /= (D(i) + D(j));
55  // Transform back
56  matrix<Type> X = V * Y_ * V.transpose();
57  return Block(X);
58  }
59  /* Solve *special case* of Sylvester equation: |A|X + X|A| = AY + YA */
60  Block<Type> sylvester2(Block<Type> Y) {
61  SAES_t saes(A);
62  matrix<Type> V = saes.eigenvectors();
63  vector<Type> D = saes.eigenvalues();
64  // Transform
65  matrix<Type> Y_ = V.transpose() * Y.A * V;
66  // Solve
67  for (int i=0; i<Y_.rows(); i++) {
68  for (int j=0; j<Y_.cols(); j++) {
69  Type denum = std::abs(D(i)) + std::abs(D(j));
70  if (denum == Type(0)) denum = 1;
71  Y_(i, j) *= (D(i) + D(j)) / denum;
72  }
73  }
74  // Transform back
75  matrix<Type> X = V * Y_ * V.transpose();
76  return Block(X);
77  }
78 
79  /* Operator square root (PD case only) */
80  Block<Type> sqrtm() {
81  SAES_t saes(A);
82  matrix<Type> X = saes.operatorSqrt();
83  return Block(X);
84  }
85  /* Operator absolute value (Symmetric case only) */
86  Block<Type> absm() {
87  SAES_t saes(A);
88  matrix<Type> V = saes.eigenvectors();
89  matrix<Type> X = V * saes.eigenvalues().cwiseAbs().asDiagonal() * V.transpose();
90  return Block(X);
91  }
92  };
93 
94  /*
95  Representation of matrix of the form
96 
97  T(A,B) :=
98 
99  [ A 0 ]
100  [ B A ]
101 
102  with methods required by Pade approximation
103  */
104  template<class BlockType>
105  struct Triangle {
106  BlockType A, B;
107  typedef typename BlockType::ScalarType Type;
108  Triangle(){}
109  Triangle(BlockType A_, BlockType B_){A = A_; B = B_;}
110  /* Matrix multiply. Cost = 3 multiplies */
111  Triangle<BlockType> operator*(Triangle<BlockType> other){
112  BlockType A, B;
113  A = (this->A) * (other.A);
114  B = (this->A) * (other.B);
115  B += (this->B) * (other.A);
116  return Triangle(A, B);
117  }
118  /* Scale matrix */
119  Triangle<BlockType> scale(Type c){
120  return Triangle(this->A.scale(c), this->B.scale(c));
121  }
122  /* Add identity matrix */
123  Triangle<BlockType> addIdentity(){
124  return Triangle(this->A.addIdentity(), this->B);
125  }
126  /* Infinity norm (Strictly, the norm is larger. But we
127  wan't the number of pade iterations for the derivative
128  to be the same as for the function value) */
129  Type norm(){
130  return this->A.norm();
131  }
132  /* Matrix inverse. Cost = 1 inverse + 2 multiplies */
133  Triangle<BlockType> inverse(){
134  BlockType A = this->A.inverse();
135  BlockType B = (A * (this->B * A)).scale(-1);
136  return Triangle(A, B);
137  }
138  /* Increment/decrement */
139  Triangle<BlockType>& operator+=(Triangle<BlockType> x){
140  this->A += x.A;
141  this->B += x.B;
142  return *this;
143  }
144  Triangle<BlockType>& operator-=(Triangle<BlockType> x){
145  this->A -= x.A;
146  this->B -= x.B;
147  return *this;
148  }
149  /* Methods for operator square root */
150  Triangle<BlockType> sylvester(Triangle<BlockType> Y) {
151  Triangle<BlockType> X;
152  X.A = (*this).A.sylvester(Y.A);
153  Y.B -= (*this).B * X.A;
154  Y.B -= X.A * (*this).B;
155  X.B = (*this).A.sylvester(Y.B);
156  return X;
157  }
158  /* Solve |A| X + X |A| = A Y + Y A */
159  Triangle<BlockType> sylvester2(Triangle<BlockType> Y) {
160  Triangle<BlockType> Y2 = (*this) * Y;
161  Y2 += Y * (*this);
162  Triangle<BlockType> X = (*this).absm().sylvester(Y2);
163  return X;
164  }
165  Triangle<BlockType> sqrtm() {
166  BlockType A = (*this).A.sqrtm();
167  BlockType B = A.sylvester((*this).B);
168  return Triangle(A, B);
169  }
170  Triangle<BlockType> absm() {
171  BlockType A = (*this).A.absm();
172  BlockType B = (*this).A.sylvester2((*this).B);
173  return Triangle(A, B);
174  }
175  };
176 
196  template <int n>
197  struct nestedTriangle : Triangle<nestedTriangle<n-1> >{
198  typedef double ScalarType;
199  typedef nestedTriangle<n-1> BlockType;
200  typedef Triangle<nestedTriangle<n-1> > Base;
201  nestedTriangle(){}
202  nestedTriangle(Base x) : Base(x){}
203  nestedTriangle(vector<matrix<double> > args) : Base() {
204  int nargs=args.size();
205  vector<matrix<double> > args1 = args.head(nargs-1);
206  matrix<double> zero = args[0]*0.0;
207  vector<matrix<double> > args2(nargs-1);
208  for(int i=0;i<nargs-1;i++)args2[i] = zero;
209  args2[0] = args[nargs-1];
210  Base::A = nestedTriangle<n-1>(args1);
211  Base::B = nestedTriangle<n-1>(args2);
212  }
213  /* For easy recursive extraction */
214  matrix<double> bottomLeftCorner(){
215  return this->B.bottomLeftCorner();
216  }
217  };
218  template <>
219  struct nestedTriangle<0> : Block<double>{
220  typedef Block<double> Base;
221  nestedTriangle<0>(){}
222  nestedTriangle<0>(Base x) : Base(x){}
223  nestedTriangle<0>(vector<matrix<double> > args) : Block<double>(args[0]) {}
224  matrix<double> bottomLeftCorner(){
225  return this->A;
226  }
227  };
228 
229 
230  /*
231  Pade approximation of matrix exponential.
232  Can be applied to any of the previously implemented matrix
233  classes.
234  */
235  template<class matrix_pade>
236  matrix_pade expm(matrix_pade A){
237  double log2NormInf = log( A.norm() );
238  log2NormInf /= log(double(2.0));
239  double e = std::floor(log2NormInf) + 1.0;
240  double s = e + 1.0;
241  s = (s < 0 ? 0 : s);
242  matrix_pade AA = A.scale( 1.0 / pow( 2.0, s ) );
243  matrix_pade X = AA;
244  double c = 0.5;
245  matrix_pade E = AA.scale( c).addIdentity();
246  matrix_pade D = AA.scale(-c).addIdentity();
247  int q = 8; /* R uses 8 - ADMB 6 */
248  int p = 1;
249  for(int k = 2; k<=q; ++k){
250  c *= double(q-k+1) / double(k*(2*q-k+1));
251  X = AA * X;
252  matrix_pade cX = X.scale(c);
253  E += cX;
254  if (p == 1) { D += cX; } else { D -= cX; }
255  p = (p == 1) ? 0 : 1;
256  }
257  matrix_pade invD = D.inverse();
258  E = invD * E;
259  for(int k = 1; k<=s; k++){
260  E = E * E;
261  }
262  return E;
263  }
264 
265  /* Matrix exponential and derivatives up to (in principle) any order.
266  Orders from 0 to 3 are compiled and the appropriate order is dispatched
267  at run-time.
268  */
269  matrix<double> expm(vector<matrix<double> > args)CSKIP({
270  int nargs = args.size();
271  matrix<double> ans;
272  if (nargs==1) ans=expm(nestedTriangle<0>(args)).bottomLeftCorner();
273  else if (nargs==2) ans=expm(nestedTriangle<1>(args)).bottomLeftCorner();
274  else if (nargs==3) ans=expm(nestedTriangle<2>(args)).bottomLeftCorner();
275  else if (nargs==4) ans=expm(nestedTriangle<3>(args)).bottomLeftCorner();
276  else Rf_error("expm: order not implemented.");
277  return ans;
278  })
279 
280  /* Helper to convert list of matrices to CppAD::vector
281  of the format: 'c(length(args), unlist(args))'
282  */
283  template<class Type>
284  CppAD::vector<Type> args2vector(vector<matrix<Type> > args, int skip=-1){
285  int nargs = args.size();
286  int nkeep = nargs - (skip >= 0);
287  int blocksize = args[0].size();
288  int n = 1 + nkeep * blocksize;
289  CppAD::vector<Type> ans(n);
290  ans[0] = nkeep;
291  int k=1;
292  for(int i=0; i<nargs; i++){
293  if(i != skip){
294  for(int j=0; j < blocksize; j++){
295  ans[k] = args[i](j);
296  k++;
297  }
298  }
299  }
300  return ans;
301  }
302 
303 
323  // ATOMIC_NAME
324  expm
325  ,
326  // OUTPUT_DIM
327  (tx.size()-1)/CppAD::Integer(tx[0])
328  ,
329  // ATOMIC_DOUBLE
330  int nargs=CppAD::Integer(tx[0]);
331  int n=sqrt((double)(tx.size()-1)/nargs);
332  vector<matrix<double> > args(nargs);
333  for(int i=0;i<nargs;i++){
334  args[i] = vec2mat(tx, n, n, 1 + i*n*n);
335  }
336  matrix<double> res = expm(args);
337  for(int i=0;i<n*n;i++)ty[i] = res(i);
338  ,
339  // ATOMIC_REVERSE
340  int nargs=CppAD::Integer(tx[0]);
341  int n=sqrt((double)ty.size());
342  vector<matrix<Type> > args(nargs+1);
343  for(int i=0;i<nargs;i++){
344  args[i] = vec2mat(tx, n, n, 1 + i*n*n).transpose();
345  }
346  args[nargs] = vec2mat(py,n,n);
347  vector<CppAD::vector<Type> > res(nargs);
348  res[0] = expm(args2vector(args));
349  for(int i=1;i<nargs;i++){
350  res[i] = expm(args2vector(args, i));
351  }
352  px[0] = Type(0);
353  for(int j=0;j<res.size();j++){
354  for(int i=0;i<n*n;i++){
355  px[1 + i + j*n*n] = res[j][i];
356  }
357  }
358  )
359 
365  template<class Type>
367  vector<matrix<Type> > args(1);
368  args[0]=x;
369  int n=x.rows();
370  return vec2mat(expm(args2vector(args)),n,n);
371  }
372 
373  template<class matrix_pade>
374  matrix_pade sqrtm(matrix_pade A){
375  return A.sqrtm();
376  }
377 
378  matrix<double> sqrtm(vector<matrix<double> > args)CSKIP({
379  int nargs = args.size();
380  matrix<double> ans;
381  if (nargs==1) ans=sqrtm(nestedTriangle<0>(args)).bottomLeftCorner();
382  else if (nargs==2) ans=sqrtm(nestedTriangle<1>(args)).bottomLeftCorner();
383  else if (nargs==3) ans=sqrtm(nestedTriangle<2>(args)).bottomLeftCorner();
384  else if (nargs==4) ans=sqrtm(nestedTriangle<3>(args)).bottomLeftCorner();
385  else Rf_error("sqrtm: order not implemented.");
386  return ans;
387  })
388 
390  // ATOMIC_NAME
391  sqrtm
392  ,
393  // OUTPUT_DIM
394  (tx.size()-1)/CppAD::Integer(tx[0])
395  ,
396  // ATOMIC_DOUBLE
397  int nargs=CppAD::Integer(tx[0]);
398  int n=sqrt((double)(tx.size()-1)/nargs);
399  vector<matrix<double> > args(nargs);
400  for(int i=0;i<nargs;i++){
401  args[i] = vec2mat(tx, n, n, 1 + i*n*n);
402  }
403  matrix<double> res = sqrtm(args);
404  for(int i=0;i<n*n;i++)ty[i] = res(i);
405  ,
406  // ATOMIC_REVERSE
407  int nargs=CppAD::Integer(tx[0]);
408  int n=sqrt((double)ty.size());
409  vector<matrix<Type> > args(nargs+1);
410  for(int i=0;i<nargs;i++){
411  args[i] = vec2mat(tx, n, n, 1 + i*n*n).transpose();
412  }
413  args[nargs] = vec2mat(py,n,n);
414  vector<CppAD::vector<Type> > res(nargs);
415  res[0] = sqrtm(args2vector(args));
416  for(int i=1;i<nargs;i++){
417  res[i] = sqrtm(args2vector(args, i));
418  }
419  px[0] = Type(0);
420  for(int j=0;j<res.size();j++){
421  for(int i=0;i<n*n;i++){
422  px[1 + i + j*n*n] = res[j][i];
423  }
424  }
425  )
426 
432  template<class Type>
434  int n=x.rows();
435  // Copy lower triangle to upper
436  for (int i=0; i<n; i++)
437  for (int j=0; j<i; j++)
438  x(j, i) = x(i, j);
439  vector<matrix<Type> > args(1);
440  args[0]=x;
441  return vec2mat(sqrtm(args2vector(args)),n,n);
442  }
443 
444  template<class matrix_pade>
445  matrix_pade absm(matrix_pade A){
446  return A.absm();
447  }
448 
449  matrix<double> absm(vector<matrix<double> > args)CSKIP({
450  int nargs = args.size();
451  matrix<double> ans;
452  if (nargs==1) ans=absm(nestedTriangle<0>(args)).bottomLeftCorner();
453  else if (nargs==2) ans=absm(nestedTriangle<1>(args)).bottomLeftCorner();
454  else if (nargs==3) ans=absm(nestedTriangle<2>(args)).bottomLeftCorner();
455  else if (nargs==4) ans=absm(nestedTriangle<3>(args)).bottomLeftCorner();
456  else Rf_error("absm: order not implemented.");
457  return ans;
458  })
459 
461  // ATOMIC_NAME
462  absm
463  ,
464  // OUTPUT_DIM
465  (tx.size()-1)/CppAD::Integer(tx[0])
466  ,
467  // ATOMIC_DOUBLE
468  int nargs=CppAD::Integer(tx[0]);
469  int n=sqrt((double)(tx.size()-1)/nargs);
470  vector<matrix<double> > args(nargs);
471  for(int i=0;i<nargs;i++){
472  args[i] = vec2mat(tx, n, n, 1 + i*n*n);
473  }
474  matrix<double> res = absm(args);
475  for(int i=0;i<n*n;i++)ty[i] = res(i);
476  ,
477  // ATOMIC_REVERSE
478  int nargs=CppAD::Integer(tx[0]);
479  int n=sqrt((double)ty.size());
480  vector<matrix<Type> > args(nargs+1);
481  for(int i=0;i<nargs;i++){
482  args[i] = vec2mat(tx, n, n, 1 + i*n*n).transpose();
483  }
484  args[nargs] = vec2mat(py,n,n);
485  vector<CppAD::vector<Type> > res(nargs);
486  res[0] = absm(args2vector(args));
487  for(int i=1;i<nargs;i++){
488  res[i] = absm(args2vector(args, i));
489  }
490  px[0] = Type(0);
491  for(int j=0;j<res.size();j++){
492  for(int i=0;i<n*n;i++){
493  px[1 + i + j*n*n] = res[j][i];
494  }
495  }
496  )
497 
505  template<class Type>
507  int n=x.rows();
508  // Copy lower triangle to upper
509  for (int i=0; i<n; i++)
510  for (int j=0; j<i; j++)
511  x(j, i) = x(i, j);
512  vector<matrix<Type> > args(1);
513  args[0]=x;
514  return vec2mat(absm(args2vector(args)),n,n);
515  }
516 
517 } // end namespace atomic
518 
519 using atomic::expm;
Vector class used by TMB.
Definition: vector.hpp:17
Namespace with special functions and derivatives.
matrix< Type > expm(matrix< Type > x)
Matrix exponential.
Definition: expm.hpp:366
vector< Type > operator*(matrix< Type > A, vector< Type > x)
Definition: convenience.hpp:42
Matrix class used by TMB.
Definition: vector.hpp:101
#define TMB_ATOMIC_VECTOR_FUNCTION( ATOMIC_NAME, OUTPUT_DIM, ATOMIC_DOUBLE, ATOMIC_REVERSE)
Construct atomic vector function based on known derivatives.
License: GPL v2