The R Project SVN R-packages

Rev

Rev 4972 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 4972 Rev 4981
Line 389... Line 389...
389
  1.3875013875013875e-7,
389
  1.3875013875013875e-7,
390
  1.9270852604185938e-9,
390
  1.9270852604185938e-9,
391
};
391
};
392
 
392
 
393
/**
393
/**
394
 * Matrix exponential - based on the code for Octave's expm function.
394
 * Matrix exponential - based on the _FIXED_ code for Octave's expm function.
395
 *
395
 *
396
 * @param x real square matrix to exponentiate
396
 * @param x real square matrix to exponentiate
397
 *
397
 *
398
 * @return matrix exponential of x
398
 * @return matrix exponential of x
399
 */
399
 */
400
SEXP dgeMatrix_exp(SEXP x)
400
SEXP dgeMatrix_exp(SEXP x)
401
{
401
{
402
    SEXP val = PROTECT(duplicate(x));
402
    const double one = 1.0, zero = 0.0;
-
 
403
    const int i1 = 1;
403
    int *Dims = INTEGER(GET_SLOT(x, Matrix_DimSym));
404
    int *Dims = INTEGER(GET_SLOT(x, Matrix_DimSym));
404
    int i, ilo, ilos, ihi, ihis, j, nc = Dims[1], sqpow;
405
    const int n = Dims[1], nsqr = n * n, np1 = n + 1;
-
 
406
 
405
    int ncp1 = Dims[1] + 1, ncsqr = nc * nc;
407
    SEXP val = PROTECT(duplicate(x));
406
    int *pivot = Alloca(nc, int);
408
    int i, ilo, ilos, ihi, ihis, j, sqpow;
407
    int *iperm = Alloca(nc, int);
409
    int *pivot = Alloca(n, int);
408
    double *dpp = Alloca(ncsqr, double), /* denominator power Pade' */
410
    double *dpp = Alloca(nsqr, double), /* denominator power Pade' */
409
	*npp = Alloca(ncsqr, double), /* numerator power Pade' */
411
	*npp = Alloca(nsqr, double), /* numerator power Pade' */
410
	*perm = Alloca(nc, double),
412
	*perm = Alloca(n, double),
411
	*scale = Alloca(nc, double),
413
	*scale = Alloca(n, double),
412
	*v = REAL(GET_SLOT(val, Matrix_xSym)),
414
	*v = REAL(GET_SLOT(val, Matrix_xSym)),
413
	*work = Alloca(ncsqr, double), inf_norm, m1_j, /* (-1)^j */
415
	*work = Alloca(nsqr, double), inf_norm, m1_j/*= (-1)^j */, trshift;
414
	one = 1., trshift, zero = 0.;
-
 
415
    R_CheckStack();
416
    R_CheckStack();
416
 
417
 
417
    if (nc < 1 || Dims[0] != nc)
418
    if (n < 1 || Dims[0] != n)
418
	error(_("Matrix exponential requires square, non-null matrix"));
419
	error(_("Matrix exponential requires square, non-null matrix"));
419
 
420
 
420
    /* FIXME: Add special treatment for nc == 1 */
421
    /* FIXME: Add special treatment for n == 1 */
421
 
422
 
422
    /* Preconditioning 1.  Shift diagonal by average diagonal if positive. */
423
    /* Preconditioning 1.  Shift diagonal by average diagonal if positive. */
423
    trshift = 0;		/* determine average diagonal element */
424
    trshift = 0;		/* determine average diagonal element */
424
    for (i = 0; i < nc; i++) trshift += v[i * ncp1];
425
    for (i = 0; i < n; i++) trshift += v[i * np1];
425
    trshift /= nc;
426
    trshift /= n;
426
    if (trshift > 0.) {		/* shift diagonal by -trshift */
427
    if (trshift > 0.) {		/* shift diagonal by -trshift */
427
	for (i = 0; i < nc; i++) v[i * ncp1] -= trshift;
428
	for (i = 0; i < n; i++) v[i * np1] -= trshift;
428
    }
429
    }
429
 
430
 
430
    /* Preconditioning 2. Balancing with dgebal. */
431
    /* Preconditioning 2. Balancing with dgebal. */
431
    F77_CALL(dgebal)("P", &nc, v, &nc, &ilo, &ihi, perm, &j);
432
    F77_CALL(dgebal)("P", &n, v, &n, &ilo, &ihi, perm, &j);
432
    if (j) error(_("dgeMatrix_exp: LAPACK routine dgebal returned %d"), j);
433
    if (j) error(_("dgeMatrix_exp: LAPACK routine dgebal returned %d"), j);
433
    F77_CALL(dgebal)("S", &nc, v, &nc, &ilos, &ihis, scale, &j);
434
    F77_CALL(dgebal)("S", &n, v, &n, &ilos, &ihis, scale, &j);
434
    if (j) error(_("dgeMatrix_exp: LAPACK routine dgebal returned %d"), j);
435
    if (j) error(_("dgeMatrix_exp: LAPACK routine dgebal returned %d"), j);
435
 
436
 
436
    /* Preconditioning 3. Scaling according to infinity norm */
437
    /* Preconditioning 3. Scaling according to infinity norm */
437
    inf_norm = F77_CALL(dlange)("I", &nc, &nc, v, &nc, work);
438
    inf_norm = F77_CALL(dlange)("I", &n, &n, v, &n, work);
438
    sqpow = (inf_norm > 0) ? (int) (1 + log(inf_norm)/log(2.)) : 0;
439
    sqpow = (inf_norm > 0) ? (int) (1 + log(inf_norm)/log(2.)) : 0;
439
    if (sqpow < 0) sqpow = 0;
440
    if (sqpow < 0) sqpow = 0;
440
    if (sqpow > 0) {
441
    if (sqpow > 0) {
441
	double scale_factor = 1.0;
442
	double scale_factor = 1.0;
442
	for (i = 0; i < sqpow; i++) scale_factor *= 2.;
443
	for (i = 0; i < sqpow; i++) scale_factor *= 2.;
443
	for (i = 0; i < ncsqr; i++) v[i] /= scale_factor;
444
	for (i = 0; i < nsqr; i++) v[i] /= scale_factor;
444
    }
445
    }
445
 
446
 
446
    /* Pade' approximation. Powers v^8, v^7, ..., v^1 */
447
    /* Pade' approximation. Powers v^8, v^7, ..., v^1 */
447
    AZERO(npp, ncsqr);
448
    AZERO(npp, nsqr);
448
    AZERO(dpp, ncsqr);
449
    AZERO(dpp, nsqr);
449
    m1_j = -1;
450
    m1_j = -1;
450
    for (j = 7; j >=0; j--) {
451
    for (j = 7; j >=0; j--) {
451
	double mult = padec[j];
452
	double mult = padec[j];
452
	/* npp = m * npp + padec[j] *m */
453
	/* npp = m * npp + padec[j] *m */
453
	F77_CALL(dgemm)("N", "N", &nc, &nc, &nc, &one, v, &nc, npp, &nc,
454
	F77_CALL(dgemm)("N", "N", &n, &n, &n, &one, v, &n, npp, &n,
454
			&zero, work, &nc);
455
			&zero, work, &n);
455
	for (i = 0; i < ncsqr; i++) npp[i] = work[i] + mult * v[i];
456
	for (i = 0; i < nsqr; i++) npp[i] = work[i] + mult * v[i];
456
	/* dpp = m * dpp + (m1_j * padec[j]) * m */
457
	/* dpp = m * dpp + (m1_j * padec[j]) * m */
457
	mult *= m1_j;
458
	mult *= m1_j;
458
	F77_CALL(dgemm)("N", "N", &nc, &nc, &nc, &one, v, &nc, dpp, &nc,
459
	F77_CALL(dgemm)("N", "N", &n, &n, &n, &one, v, &n, dpp, &n,
459
			&zero, work, &nc);
460
			&zero, work, &n);
460
	for (i = 0; i < ncsqr; i++) dpp[i] = work[i] + mult * v[i];
461
	for (i = 0; i < nsqr; i++) dpp[i] = work[i] + mult * v[i];
461
	m1_j *= -1;
462
	m1_j *= -1;
462
    }
463
    }
463
    /* Zero power */
464
    /* Zero power */
464
    for (i = 0; i < ncsqr; i++) dpp[i] *= -1.;
465
    for (i = 0; i < nsqr; i++) dpp[i] *= -1.;
465
    for (j = 0; j < nc; j++) {
466
    for (j = 0; j < n; j++) {
466
	npp[j * ncp1] += 1.;
467
	npp[j * np1] += 1.;
467
	dpp[j * ncp1] += 1.;
468
	dpp[j * np1] += 1.;
468
    }
469
    }
469
 
470
 
470
    /* Pade' approximation is solve(dpp, npp) */
471
    /* Pade' approximation is solve(dpp, npp) */
471
    F77_CALL(dgetrf)(&nc, &nc, dpp, &nc, pivot, &j);
472
    F77_CALL(dgetrf)(&n, &n, dpp, &n, pivot, &j);
472
    if (j) error(_("dgeMatrix_exp: dgetrf returned error code %d"), j);
473
    if (j) error(_("dgeMatrix_exp: dgetrf returned error code %d"), j);
473
    F77_CALL(dgetrs)("N", &nc, &nc, dpp, &nc, pivot, npp, &nc, &j);
474
    F77_CALL(dgetrs)("N", &n, &n, dpp, &n, pivot, npp, &n, &j);
474
    if (j) error(_("dgeMatrix_exp: dgetrs returned error code %d"), j);
475
    if (j) error(_("dgeMatrix_exp: dgetrs returned error code %d"), j);
475
    Memcpy(v, npp, ncsqr);
476
    Memcpy(v, npp, nsqr);
476
 
477
 
477
    /* Now undo all of the preconditioning */
478
    /* Now undo all of the preconditioning */
478
    /* Preconditioning 3: square the result for every power of 2 */
479
    /* Preconditioning 3: square the result for every power of 2 */
479
    while (sqpow--) {
480
    while (sqpow--) {
480
	F77_CALL(dgemm)("N", "N", &nc, &nc, &nc, &one, v, &nc, v, &nc,
481
	F77_CALL(dgemm)("N", "N", &n, &n, &n, &one, v, &n, v, &n,
481
			&zero, work, &nc);
482
			&zero, work, &n);
482
	Memcpy(v, work, ncsqr);
483
	Memcpy(v, work, nsqr);
483
    }
484
    }
484
    /* Preconditioning 2: apply inverse scaling */
485
    /* Preconditioning 2: apply inverse scaling */
485
    for (j = 0; j < nc; j++)
486
    for (j = 0; j < n; j++)
486
	for (i = 0; i < nc; i++)
487
	for (i = 0; i < n; i++)
487
	    v[i + j * nc] *= scale[i]/scale[j];
488
	    v[i + j * n] *= scale[i]/scale[j];
-
 
489
 
-
 
490
 
488
    /* Construct balancing permutation vector */
491
    /* 2 b) Inverse permutation  (if not the identity permutation) */
489
    for (i = 0; i < nc; i++) iperm[i] = i; /* identity permutation */
492
    if (ilo != 1 || ihi != n) {
490
    /* Leading permutations applied in forward order */
493
	/* Martin Maechler's code */
-
 
494
 
491
    for (i = 0; i < (ilo - 1); i++) {
495
#define SWAP_ROW(I,J) F77_CALL(dswap)(&n, &v[(I)], &n, &v[(J)], &n)
-
 
496
 
-
 
497
#define SWAP_COL(I,J) F77_CALL(dswap)(&n, &v[(I)*n], &i1, &v[(J)*n], &i1)
-
 
498
 
492
	int swapidx = (int) (perm[i]) - 1;
499
#define RE_PERMUTE(I)				\
493
	int tmp = iperm[i];
500
	int p_I = (int) (perm[I]) - 1;		\
494
	iperm[i] = iperm[swapidx];
501
	SWAP_COL(I, p_I);			\
495
	iperm[swapidx] = tmp;
502
	SWAP_ROW(I, p_I)
496
    }
503
 
497
    /* Trailing permutations applied in reverse order */
504
	/* reversion of "leading permutations" : in reverse order */
498
    for (i = nc - 1; i >= ihi; i--) {
505
	for (i = (ilo - 1) - 1; i >= 0; i--) {
499
	int swapidx = (int) (perm[i]) - 1;
-
 
500
	int tmp = iperm[i];
506
	    RE_PERMUTE(i);
501
	iperm[i] = iperm[swapidx];
-
 
502
	iperm[swapidx] = tmp;
-
 
503
    }
507
	}
-
 
508
 
504
    /* Construct inverse balancing permutation vector */
509
	/* reversion of "trailing permutations" : applied in forward order */
505
    Memcpy(pivot, iperm, nc);
-
 
506
    for (i = 0; i < nc; i++) iperm[pivot[i]] = i;
510
	for (i = (ihi + 1) - 1; i < n; i++) {
507
    /* Apply inverse permutation */
-
 
508
    Memcpy(work, v, ncsqr);
511
	    RE_PERMUTE(i);
509
    for (j = 0; j < nc; j++)
-
 
-
 
512
	}
510
	for (i = 0; i < nc; i++)
513
    }
511
	    v[i + j * nc] = work[iperm[i] + iperm[j] * nc];
-
 
512
 
514
 
513
    /* Preconditioning 1: Trace normalization */
515
    /* Preconditioning 1: Trace normalization */
514
    if (trshift > 0.) {
516
    if (trshift > 0.) {
515
	double mult = exp(trshift);
517
	double mult = exp(trshift);
516
	for (i = 0; i < ncsqr; i++) v[i] *= mult;
518
	for (i = 0; i < nsqr; i++) v[i] *= mult;
517
    }
519
    }
518
 
520
 
519
    /* Clean up */
521
    /* Clean up */
520
    UNPROTECT(1);
522
    UNPROTECT(1);
521
    return val;
523
    return val;