blob: 6189fdc3b989e7182fc035d8b7976fc8fe0c578e [file] [log] [blame]
/* R : A Computer Language for Statistical Data Analysis
*
* Copyright (C) 1999-2016 The R Core Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, a copy is available at
* https://www.R-project.org/Licenses/.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <R.h>
#include "ts.h"
#ifndef max
#define max(a,b) ((a < b)?(b):(a))
#endif
#ifndef min
#define min(a,b) ((a > b)?(b):(a))
#endif
/* Internal */
static void partrans(int np, double *raw, double *new);
static void dotrans(Starma G, double *raw, double *new, int trans);
/* cor is the autocorrelations starting from 0 lag*/
static void uni_pacf(double *cor, double *p, int nlag)
{
double a, b, c, *v, *w;
v = (double*) R_alloc(nlag, sizeof(double));
w = (double*) R_alloc(nlag, sizeof(double));
w[0] = p[0] = cor[1];
for(int ll = 1; ll < nlag; ll++) {
a = cor[ll+1];
b = 1.0;
for(int i = 0; i < ll; i++) {
a -= w[i] * cor[ll - i];
b -= w[i] * cor[i + 1];
}
p[ll] = c = a/b;
if(ll+1 == nlag) break;
w[ll] = c;
for(int i = 0; i < ll; i++)
v[ll-i-1] = w[i];
for(int i = 0; i < ll; i++)
w[i] -= c*v[i];
}
}
SEXP pacf1(SEXP acf, SEXP lmax)
{
int lagmax = asInteger(lmax);
acf = PROTECT(coerceVector(acf, REALSXP));
SEXP ans = PROTECT(allocVector(REALSXP, lagmax));
uni_pacf(REAL(acf), REAL(ans), lagmax);
SEXP d = PROTECT(allocVector(INTSXP, 3));
INTEGER(d)[0] = lagmax;
INTEGER(d)[1] = INTEGER(d)[2] = 1;
setAttrib(ans, R_DimSymbol, d);
UNPROTECT(3);
return ans;
}
/* Use an external reference to store the structure we keep allocated
memory in */
static SEXP Starma_tag;
#define GET_STARMA \
Starma G; \
if (TYPEOF(pG) != EXTPTRSXP || R_ExternalPtrTag(pG) != Starma_tag) \
error(_("bad Starma struct"));\
G = (Starma) R_ExternalPtrAddr(pG)
SEXP setup_starma(SEXP na, SEXP x, SEXP pn, SEXP xreg, SEXP pm,
SEXP dt, SEXP ptrans, SEXP sncond)
{
Starma G;
int i, n, m, ip, iq, ir, np;
SEXP res;
double *rx = REAL(x), *rxreg = REAL(xreg);
G = Calloc(1, starma_struct);
G->mp = INTEGER(na)[0];
G->mq = INTEGER(na)[1];
G->msp = INTEGER(na)[2];
G->msq = INTEGER(na)[3];
G->ns = INTEGER(na)[4];
G->n = n = asInteger(pn);
G->ncond = asInteger(sncond);
G->m = m = asInteger(pm);
G->params = Calloc(G->mp + G->mq + G->msp + G->msq + G->m, double);
G->p = ip = G->ns*G->msp + G->mp;
G->q = iq = G->ns*G->msq + G->mq;
G->r = ir = max(ip, iq + 1);
G->np = np = (ir*(ir + 1))/2;
G->nrbar = max(1, np*(np - 1)/2);
G->trans = asInteger(ptrans);
G->a = Calloc(ir, double);
G->P = Calloc(np, double);
G->V = Calloc(np, double);
G->thetab = Calloc(np, double);
G->xnext = Calloc(np, double);
G->xrow = Calloc(np, double);
G->rbar = Calloc(G->nrbar, double);
G->w = Calloc(n, double);
G->wkeep = Calloc(n, double);
G->resid = Calloc(n, double);
G->phi = Calloc(ir, double);
G->theta = Calloc(ir, double);
G->reg = Calloc(1 + n*m, double); /* AIX can't calloc 0 items */
G->delta = asReal(dt);
for(i = 0; i < n; i++) G->w[i] = G->wkeep[i] = rx[i];
for(i = 0; i < n*m; i++) G->reg[i] = rxreg[i];
Starma_tag = install("STARMA_TAG");
res = R_MakeExternalPtr(G, Starma_tag, R_NilValue);
return res;
}
SEXP free_starma(SEXP pG)
{
GET_STARMA;
Free(G->params); Free(G->a); Free(G->P); Free(G->V); Free(G->thetab);
Free(G->xnext); Free(G->xrow); Free(G->rbar);
Free(G->w); Free(G->wkeep); Free(G->resid); Free(G->phi); Free(G->theta);
Free(G->reg); Free(G);
return R_NilValue;
}
SEXP Starma_method(SEXP pG, SEXP method)
{
GET_STARMA;
G->method = asInteger(method);
return R_NilValue;
}
SEXP Dotrans(SEXP pG, SEXP x)
{
SEXP y = allocVector(REALSXP, LENGTH(x));
GET_STARMA;
dotrans(G, REAL(x), REAL(y), 1);
return y;
}
SEXP set_trans(SEXP pG, SEXP ptrans)
{
GET_STARMA;
G->trans = asInteger(ptrans);
return R_NilValue;
}
SEXP arma0fa(SEXP pG, SEXP inparams)
{
int i, j, ifault = 0, it, streg;
double sumlog, ssq, tmp, ans;
GET_STARMA;
dotrans(G, REAL(inparams), G->params, G->trans);
if(G->ns > 0) {
/* expand out seasonal ARMA models */
for(i = 0; i < G->mp; i++) G->phi[i] = G->params[i];
for(i = 0; i < G->mq; i++) G->theta[i] = G->params[i + G->mp];
for(i = G->mp; i < G->p; i++) G->phi[i] = 0.0;
for(i = G->mq; i < G->q; i++) G->theta[i] = 0.0;
for(j = 0; j < G->msp; j++) {
G->phi[(j + 1)*G->ns - 1] += G->params[j + G->mp + G->mq];
for(i = 0; i < G->mp; i++)
G->phi[(j + 1)*G->ns + i] -= G->params[i]*
G->params[j + G->mp + G->mq];
}
for(j = 0; j < G->msq; j++) {
G->theta[(j + 1)*G->ns - 1] +=
G->params[j + G->mp + G->mq + G->msp];
for(i = 0; i < G->mq; i++)
G->theta[(j + 1)*G->ns + i] += G->params[i + G->mp]*
G->params[j + G->mp + G->mq + G->msp];
}
} else {
for(i = 0; i < G->mp; i++) G->phi[i] = G->params[i];
for(i = 0; i < G->mq; i++) G->theta[i] = G->params[i + G->mp];
}
streg = G->mp + G->mq + G->msp + G->msq;
if(G->m > 0) {
for(i = 0; i < G->n; i++) {
tmp = G->wkeep[i];
for(j = 0; j < G->m; j++)
tmp -= G->reg[i + G->n*j] * G->params[streg + j];
G->w[i] = tmp;
}
}
if(G->method == 1) {
int p = G->mp + G->ns * G->msp, q = G->mq + G->ns * G->msq, nu = 0;
ssq = 0.0;
for(i = 0; i < G->ncond; i++) G->resid[i] = 0.0;
for(i = G->ncond; i < G->n; i++) {
tmp = G->w[i];
for(j = 0; j < min(i - G->ncond, p); j++)
tmp -= G->phi[j] * G->w[i - j - 1];
for(j = 0; j < min(i - G->ncond, q); j++)
tmp -= G->theta[j] * G->resid[i - j - 1];
G->resid[i] = tmp;
if(!ISNAN(tmp)) {
nu++;
ssq += tmp * tmp;
}
}
G->s2 = ssq/(double)(nu);
ans = 0.5 * log(G->s2);
} else {
starma(G, &ifault);
if(ifault) error(_("starma error code %d"), ifault);
sumlog = 0.0;
ssq = 0.0;
it = 0;
karma(G, &sumlog, &ssq, 1, &it);
G->s2 = ssq/(double)G->nused;
ans = 0.5*(log(ssq/(double)G->nused) + sumlog/(double)G->nused);
}
return ScalarReal(ans);
}
SEXP get_s2(SEXP pG)
{
GET_STARMA;
return ScalarReal(G->s2);
}
SEXP get_resid(SEXP pG)
{
SEXP res;
int i;
double *rres;
GET_STARMA;
res = allocVector(REALSXP, G->n);
rres = REAL(res);
for(i = 0; i < G->n; i++) rres[i] = G->resid[i];
return res;
}
SEXP arma0_kfore(SEXP pG, SEXP pd, SEXP psd, SEXP nahead)
{
int dd = asInteger(pd);
int d, il = asInteger(nahead), ifault = 0, i, j;
double *del, *del2;
SEXP res, x, var;
GET_STARMA;
PROTECT(res = allocVector(VECSXP, 2));
SET_VECTOR_ELT(res, 0, x = allocVector(REALSXP, il));
SET_VECTOR_ELT(res, 1, var = allocVector(REALSXP, il));
d = dd + G->ns * asInteger(psd);
del = (double *) R_alloc(d + 1, sizeof(double));
del2 = (double *) R_alloc(d + 1, sizeof(double));
del[0] = 1;
for(i = 1; i <= d; i++) del[i] = 0;
for (j = 0; j < dd; j++) {
for(i = 0; i <= d; i++) del2[i] = del[i];
for(i = 0; i <= d - 1; i++) del[i+1] -= del2[i];
}
for (j = 0; j < asInteger(psd); j++) {
for(i = 0; i <= d; i++) del2[i] = del[i];
for(i = 0; i <= d - G->ns; i++) del[i + G->ns] -= del2[i];
}
for(i = 1; i <= d; i++) del[i] *= -1;
forkal(G, d, il, del + 1, REAL(x), REAL(var), &ifault);
if(ifault) error(_("forkal error code %d"), ifault);
UNPROTECT(1);
return res;
}
static void artoma(int p, double *phi, double *psi, int npsi)
{
int i, j;
for(i = 0; i < p; i++) psi[i] = phi[i];
for(i = p; i < npsi; i++) psi[i] = 0.0;
for(i = 0; i < npsi - p - 1; i++)
for(j = 0; j < p; j++) psi[i + j + 1] += phi[j]*psi[i];
}
SEXP ar2ma(SEXP ar, SEXP npsi)
{
ar = PROTECT(coerceVector(ar, REALSXP));
int p = LENGTH(ar), ns = asInteger(npsi), ns1 = ns + p + 1;
SEXP psi = PROTECT(allocVector(REALSXP, ns1));
artoma(p, REAL(ar), REAL(psi), ns1);
SEXP ans = lengthgets(psi, ns);
UNPROTECT(2);
return ans;
}
static void partrans(int p, double *raw, double *new)
{
int j, k;
double a, work[100];
if(p > 100) error(_("can only transform 100 pars in arima0"));
/* Step one: map (-Inf, Inf) to (-1, 1) via tanh
The parameters are now the pacf phi_{kk} */
for(j = 0; j < p; j++) work[j] = new[j] = tanh(raw[j]);
/* Step two: run the Durbin-Levinson recursions to find phi_{j.},
j = 2, ..., p and phi_{p.} are the autoregression coefficients */
for(j = 1; j < p; j++) {
a = new[j];
for(k = 0; k < j; k++)
work[k] -= a * new[j - k - 1];
for(k = 0; k < j; k++) new[k] = work[k];
}
}
static void dotrans(Starma G, double *raw, double *new, int trans)
{
int i, v, n = G->mp + G->mq + G->msp + G->msq + G->m;
for(i = 0; i < n; i++) new[i] = raw[i];
if(trans) {
partrans(G->mp, raw, new);
v = G->mp;
partrans(G->mq, raw + v, new + v);
v += G->mq;
partrans(G->msp, raw + v, new + v);
v += G->msp;
partrans(G->msq, raw + v, new + v);
}
}
#if !defined(atanh) && defined(HAVE_DECL_ATANH) && !HAVE_DECL_ATANH
extern double atanh(double x);
#endif
static void invpartrans(int p, double *phi, double *new)
{
int j, k;
double a, work[100];
if(p > 100) error(_("can only transform 100 pars in arima0"));
for(j = 0; j < p; j++) work[j] = new[j] = phi[j];
/* Run the Durbin-Levinson recursions backwards
to find the PACF phi_{j.} from the autoregression coefficients */
for(j = p - 1; j > 0; j--) {
a = new[j];
for(k = 0; k < j; k++)
work[k] = (new[k] + a * new[j - k - 1]) / (1 - a * a);
for(k = 0; k < j; k++) new[k] = work[k];
}
for(j = 0; j < p; j++) new[j] = atanh(new[j]);
}
SEXP Invtrans(SEXP pG, SEXP x)
{
SEXP y = allocVector(REALSXP, LENGTH(x));
int i, v, n;
double *raw = REAL(x), *new = REAL(y);
GET_STARMA;
n = G->mp + G->mq + G->msp + G->msq;
v = 0;
invpartrans(G->mp, raw + v, new + v);
v += G->mp;
invpartrans(G->mq, raw + v, new + v);
v += G->mq;
invpartrans(G->msp, raw + v, new + v);
v += G->msp;
invpartrans(G->msq, raw + v, new + v);
for(i = n; i < n + G->m; i++) new[i] = raw[i];
return y;
}
#define eps 1e-3
SEXP Gradtrans(SEXP pG, SEXP x)
{
SEXP y = allocMatrix(REALSXP, LENGTH(x), LENGTH(x));
int i, j, v, n;
double *raw = REAL(x), *A = REAL(y), w1[100], w2[100], w3[100];
GET_STARMA;
n = G->mp + G->mq + G->msp + G->msq + G->m;
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
A[i + j*n] = (i == j);
if(G->mp > 0) {
for(i = 0; i < G->mp; i++) w1[i] = raw[i];
partrans(G->mp, w1, w2);
for(i = 0; i < G->mp; i++) {
w1[i] += eps;
partrans(G->mp, w1, w3);
for(j = 0; j < G->mp; j++) A[i + j*n] = (w3[j] - w2[j])/eps;
w1[i] -= eps;
}
}
if(G->mq > 0) {
v = G->mp;
for(i = 0; i < G->mq; i++) w1[i] = raw[i + v];
partrans(G->mq, w1, w2);
for(i = 0; i < G->mq; i++) {
w1[i] += eps;
partrans(G->mq, w1, w3);
for(j = 0; j < G->mq; j++) A[i + v + j*n] = (w3[j] - w2[j])/eps;
w1[i] -= eps;
}
}
if(G->msp > 0) {
v = G->mp + G->mq;
for(i = 0; i < G->msp; i++) w1[i] = raw[i + v];
partrans(G->msp, w1, w2);
for(i = 0; i < G->msp; i++) {
w1[i] += eps;
partrans(G->msp, w1, w3);
for(j = 0; j < G->msp; j++)
A[i + v + (j+v)*n] = (w3[j] - w2[j])/eps;
w1[i] -= eps;
}
}
if(G->msq > 0) {
v = G->mp + G->mq + G->msp;
for(i = 0; i < G->msq; i++) w1[i] = raw[i + v];
partrans(G->msq, w1, w2);
for(i = 0; i < G->msq; i++) {
w1[i] += eps;
partrans(G->msq, w1, w3);
for(j = 0; j < G->msq; j++)
A[i + v + (j+v)*n] = (w3[j] - w2[j])/eps;
w1[i] -= eps;
}
}
return y;
}
SEXP
ARMAtoMA(SEXP ar, SEXP ma, SEXP lag_max)
{
int i, j, p = LENGTH(ar), q = LENGTH(ma), m = asInteger(lag_max);
double *phi = REAL(ar), *theta = REAL(ma), *psi, tmp;
SEXP res;
if(m <= 0 || m == NA_INTEGER)
error(_("invalid value of lag.max"));
PROTECT(res = allocVector(REALSXP, m));
psi = REAL(res);
for(i = 0; i < m; i++) {
tmp = (i < q) ? theta[i] : 0.0;
for(j = 0; j < min(i+1, p); j++)
tmp += phi[j] * ((i-j-1 >= 0) ? psi[i-j-1] : 1.0);
psi[i] = tmp;
}
UNPROTECT(1);
return res;
}