Matrix r4655
Loading...
Searching...
No Matches
expm.c
Go to the documentation of this file.
1#include "Lapack-etc.h"
2#include "Mdefines.h"
3#include "expm.h"
4
5/* For matrix exponential calculation : */
6const static double padec [] =
7{
8 5.0000000000000000e-1,
9 1.1666666666666667e-1,
10 1.6666666666666667e-2,
11 1.6025641025641026e-3,
12 1.0683760683760684e-4,
13 4.8562548562548563e-6,
14 1.3875013875013875e-7,
15 1.9270852604185938e-9,
16};
17
18/* Based on _corrected_ code for Octave function 'expm' : */
19SEXP dgeMatrix_expm(SEXP x)
20{
21 const double one = 1.0, zero = 0.0;
22 const int i1 = 1;
23 int *Dims = INTEGER(GET_SLOT(x, Matrix_DimSym));
24 const int n = Dims[1];
25 const R_xlen_t n_ = n, np1 = n + 1, nsqr = n_ * n_; // nsqr = n^2
26
27 SEXP val = PROTECT(duplicate(x));
28 int i, ilo, ilos, ihi, ihis, j, sqpow;
29 int *pivot = R_Calloc(n, int);
30 double *dpp = R_Calloc(nsqr, double), /* denominator power Pade' */
31 *npp = R_Calloc(nsqr, double), /* numerator power Pade' */
32 *perm = R_Calloc(n, double),
33 *scale = R_Calloc(n, double),
34 *v = REAL(GET_SLOT(val, Matrix_xSym)),
35 *work = R_Calloc(nsqr, double), inf_norm, m1_j/*= (-1)^j */, trshift;
36 R_CheckStack();
37
38 if (n < 1 || Dims[0] != n)
39 error(_("Matrix exponential requires square, non-null matrix"));
40 if(n == 1) {
41 v[0] = exp(v[0]);
42 UNPROTECT(1);
43 return val;
44 }
45
46 /* Preconditioning 1. Shift diagonal by average diagonal if positive. */
47 trshift = 0; /* determine average diagonal element */
48 for (i = 0; i < n; i++) trshift += v[i * np1];
49 trshift /= n;
50 if (trshift > 0.) { /* shift diagonal by -trshift */
51 for (i = 0; i < n; i++) v[i * np1] -= trshift;
52 }
53
54 /* Preconditioning 2. Balancing with dgebal. */
55 F77_CALL(dgebal)("P", &n, v, &n, &ilo, &ihi, perm, &j FCONE);
56 if (j) error(_("dgeMatrix_exp: LAPACK routine dgebal returned %d"), j);
57 F77_CALL(dgebal)("S", &n, v, &n, &ilos, &ihis, scale, &j FCONE);
58 if (j) error(_("dgeMatrix_exp: LAPACK routine dgebal returned %d"), j);
59
60 /* Preconditioning 3. Scaling according to infinity norm */
61 inf_norm = F77_CALL(dlange)("I", &n, &n, v, &n, work FCONE);
62 sqpow = (inf_norm > 0) ? (int) (1 + log(inf_norm)/log(2.)) : 0;
63 if (sqpow < 0) sqpow = 0;
64 if (sqpow > 0) {
65 double scale_factor = 1.0;
66 for (i = 0; i < sqpow; i++) scale_factor *= 2.;
67 for (R_xlen_t i = 0; i < nsqr; i++) v[i] /= scale_factor;
68 }
69
70 /* Pade' approximation. Powers v^8, v^7, ..., v^1 */
71 Matrix_memset(npp, 0, nsqr, sizeof(double));
72 Matrix_memset(dpp, 0, nsqr, sizeof(double));
73 m1_j = -1;
74 for (j = 7; j >=0; j--) {
75 double mult = padec[j];
76 /* npp = m * npp + padec[j] *m */
77 F77_CALL(dgemm)("N", "N", &n, &n, &n, &one, v, &n, npp, &n,
78 &zero, work, &n FCONE FCONE);
79 for (R_xlen_t i = 0; i < nsqr; i++) npp[i] = work[i] + mult * v[i];
80 /* dpp = m * dpp + (m1_j * padec[j]) * m */
81 mult *= m1_j;
82 F77_CALL(dgemm)("N", "N", &n, &n, &n, &one, v, &n, dpp, &n,
83 &zero, work, &n FCONE FCONE);
84 for (R_xlen_t i = 0; i < nsqr; i++) dpp[i] = work[i] + mult * v[i];
85 m1_j *= -1;
86 }
87 /* Zero power */
88 for (R_xlen_t i = 0; i < nsqr; i++) dpp[i] *= -1.;
89 for (j = 0; j < n; j++) {
90 npp[j * np1] += 1.;
91 dpp[j * np1] += 1.;
92 }
93
94 /* Pade' approximation is solve(dpp, npp) */
95 F77_CALL(dgetrf)(&n, &n, dpp, &n, pivot, &j);
96 if (j) error(_("dgeMatrix_exp: dgetrf returned error code %d"), j);
97 F77_CALL(dgetrs)("N", &n, &n, dpp, &n, pivot, npp, &n, &j FCONE);
98 if (j) error(_("dgeMatrix_exp: dgetrs returned error code %d"), j);
99 Memcpy(v, npp, nsqr);
100
101 /* Now undo all of the preconditioning */
102 /* Preconditioning 3: square the result for every power of 2 */
103 while (sqpow--) {
104 F77_CALL(dgemm)("N", "N", &n, &n, &n, &one, v, &n, v, &n,
105 &zero, work, &n FCONE FCONE);
106 Memcpy(v, work, nsqr);
107 }
108 /* Preconditioning 2: apply inverse scaling */
109 for (j = 0; j < n; j++) {
110 R_xlen_t jn = j * n_;
111 for (i = 0; i < n; i++)
112 v[i + jn] *= scale[i]/scale[j];
113 }
114
115
116 /* 2 b) Inverse permutation (if not the identity permutation) */
117 if (ilo != 1 || ihi != n) {
118 /* Martin Maechler's code */
119
120#define SWAP_ROW(I,J) F77_CALL(dswap)(&n, &v[(I)], &n, &v[(J)], &n)
121
122#define SWAP_COL(I,J) F77_CALL(dswap)(&n, &v[(I)*n_], &i1, &v[(J)*n_], &i1)
123
124#define RE_PERMUTE(I) \
125 int p_I = (int) (perm[I]) - 1; \
126 SWAP_COL(I, p_I); \
127 SWAP_ROW(I, p_I)
128
129 /* reversion of "leading permutations" : in reverse order */
130 for (i = (ilo - 1) - 1; i >= 0; i--) {
131 RE_PERMUTE(i);
132 }
133
134 /* reversion of "trailing permutations" : applied in forward order */
135 for (i = (ihi + 1) - 1; i < n; i++) {
136 RE_PERMUTE(i);
137 }
138 }
139
140 /* Preconditioning 1: Trace normalization */
141 if (trshift > 0.) {
142 double mult = exp(trshift);
143 for (R_xlen_t i = 0; i < nsqr; i++) v[i] *= mult;
144 }
145
146 /* Clean up */
147 R_Free(work); R_Free(scale); R_Free(perm); R_Free(npp); R_Free(dpp); R_Free(pivot);
148 UNPROTECT(1);
149 return val;
150}
#define FCONE
Definition Lapack-etc.h:18
#define _(String)
Definition Mdefines.h:44
#define GET_SLOT(x, what)
Definition Mdefines.h:85
SEXP Matrix_DimSym
Definition Msymbols.h:3
SEXP Matrix_xSym
Definition Msymbols.h:22
static const double padec[]
Definition expm.c:6
#define RE_PERMUTE(I)
SEXP dgeMatrix_expm(SEXP x)
Definition expm.c:19
void * Matrix_memset(void *dest, int ch, R_xlen_t length, size_t size)
Definition utils.c:8