Blame movstat/mvacc.c

Packit 67cb25
/* movstat/mvacc.c
Packit 67cb25
 *
Packit 67cb25
 * Moving window mean/variance accumulator - based on a modification
Packit 67cb25
 * to Welford's algorithm, discussed here:
Packit 67cb25
 *
Packit 67cb25
 * https://stackoverflow.com/a/6664212
Packit 67cb25
 * 
Packit 67cb25
 * Copyright (C) 2018 Patrick Alken
Packit 67cb25
 * 
Packit 67cb25
 * This program is free software; you can redistribute it and/or modify
Packit 67cb25
 * it under the terms of the GNU General Public License as published by
Packit 67cb25
 * the Free Software Foundation; either version 3 of the License, or (at
Packit 67cb25
 * your option) any later version.
Packit 67cb25
 * 
Packit 67cb25
 * This program is distributed in the hope that it will be useful, but
Packit 67cb25
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 67cb25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 67cb25
 * General Public License for more details.
Packit 67cb25
 * 
Packit 67cb25
 * You should have received a copy of the GNU General Public License
Packit 67cb25
 * along with this program; if not, write to the Free Software
Packit 67cb25
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
#include <config.h>
Packit 67cb25
#include <gsl/gsl_math.h>
Packit 67cb25
#include <gsl/gsl_vector.h>
Packit 67cb25
#include <gsl/gsl_errno.h>
Packit 67cb25
#include <gsl/gsl_movstat.h>
Packit 67cb25
Packit 67cb25
typedef double ringbuf_type_t;
Packit 67cb25
Packit 67cb25
#include "ringbuf.c"
Packit 67cb25
Packit 67cb25
typedef struct
Packit 67cb25
{
Packit 67cb25
  size_t n;      /* window size */
Packit 67cb25
  size_t k;      /* number of samples currently in window */
Packit 67cb25
  double mean;   /* current window mean */
Packit 67cb25
  double M2;     /* current window M2 */
Packit 67cb25
  ringbuf *rbuf; /* ring buffer storing current window */
Packit 67cb25
} mvacc_state_t;
Packit 67cb25
Packit 67cb25
static size_t
Packit 67cb25
mvacc_size(const size_t n)
Packit 67cb25
{
Packit 67cb25
  size_t size = 0;
Packit 67cb25
Packit 67cb25
  size += sizeof(mvacc_state_t);
Packit 67cb25
  size += ringbuf_size(n);
Packit 67cb25
Packit 67cb25
  return size;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
mvacc_init(const size_t n, void * vstate)
Packit 67cb25
{
Packit 67cb25
  mvacc_state_t * state = (mvacc_state_t *) vstate;
Packit 67cb25
Packit 67cb25
  state->n = n;
Packit 67cb25
  state->k = 0;
Packit 67cb25
  state->mean = 0.0;
Packit 67cb25
  state->M2 = 0.0;
Packit 67cb25
Packit 67cb25
  state->rbuf = (ringbuf *) ((unsigned char *) vstate + sizeof(mvacc_state_t));
Packit 67cb25
  ringbuf_init(n, state->rbuf);
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
mvacc_insert(const double x, void * vstate)
Packit 67cb25
{
Packit 67cb25
  mvacc_state_t * state = (mvacc_state_t *) vstate;
Packit 67cb25
Packit 67cb25
  if (ringbuf_is_full(state->rbuf))
Packit 67cb25
    {
Packit 67cb25
      /* remove oldest window element and add new one */
Packit 67cb25
      double old = ringbuf_peek_back(state->rbuf);
Packit 67cb25
      double prev_mean = state->mean;
Packit 67cb25
Packit 67cb25
      state->mean += (x - old) / (double) state->n;
Packit 67cb25
      state->M2 += ((old - prev_mean) + (x - state->mean)) * (x - old);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      double delta = x - state->mean;
Packit 67cb25
Packit 67cb25
      /*
Packit 67cb25
       * Welford algorithm:
Packit 67cb25
       *
Packit 67cb25
       * mu_new = mu_old + (x - mu_old) / n
Packit 67cb25
       * M2_new = M2_old + (x - mu_old) * (x - mu_new)
Packit 67cb25
       */
Packit 67cb25
Packit 67cb25
      ++(state->k);
Packit 67cb25
      state->mean += delta / (double) state->k;
Packit 67cb25
      state->M2 += delta * (x - state->mean);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* add new element to ring buffer */
Packit 67cb25
  ringbuf_insert(x, state->rbuf);
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
mvacc_delete(void * vstate)
Packit 67cb25
{
Packit 67cb25
  mvacc_state_t * state = (mvacc_state_t *) vstate;
Packit 67cb25
Packit 67cb25
  if (!ringbuf_is_empty(state->rbuf))
Packit 67cb25
    {
Packit 67cb25
      if (state->k > 1)
Packit 67cb25
        {
Packit 67cb25
          /*
Packit 67cb25
           * mu_new = mu_old + (mu_old - x_old) / (n - 1)
Packit 67cb25
           * M2_new = M2_old - (mu_old - x_old) * (mu_new - x_old)
Packit 67cb25
           */
Packit 67cb25
Packit 67cb25
          double old = ringbuf_peek_back(state->rbuf);
Packit 67cb25
          double prev_mean = state->mean;
Packit 67cb25
          double delta = prev_mean - old;
Packit 67cb25
Packit 67cb25
          state->mean += delta / (state->k - 1.0);
Packit 67cb25
          state->M2 -= delta * (state->mean - old);
Packit 67cb25
        }
Packit 67cb25
      else if (state->k == 1)
Packit 67cb25
        {
Packit 67cb25
          state->mean = 0.0;
Packit 67cb25
          state->M2 = 0.0;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      ringbuf_pop_back(state->rbuf);
Packit 67cb25
      --(state->k);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
mvacc_mean(void * params, double * result, const void * vstate)
Packit 67cb25
{
Packit 67cb25
  const mvacc_state_t * state = (const mvacc_state_t *) vstate;
Packit 67cb25
  (void) params;
Packit 67cb25
  *result = state->mean;
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
mvacc_variance(void * params, double * result, const void * vstate)
Packit 67cb25
{
Packit 67cb25
  const mvacc_state_t * state = (const mvacc_state_t *) vstate;
Packit 67cb25
Packit 67cb25
  (void) params;
Packit 67cb25
Packit 67cb25
  if (state->k < 2)
Packit 67cb25
    *result = 0.0;
Packit 67cb25
  else
Packit 67cb25
    *result = state->M2 / (state->k - 1.0);
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
mvacc_sd(void * params, double * result, const void * vstate)
Packit 67cb25
{
Packit 67cb25
  double variance;
Packit 67cb25
  int status = mvacc_variance(params, &variance, vstate);
Packit 67cb25
  *result = sqrt(variance);
Packit 67cb25
  return status;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static const gsl_movstat_accum mean_accum_type =
Packit 67cb25
{
Packit 67cb25
  mvacc_size,
Packit 67cb25
  mvacc_init,
Packit 67cb25
  mvacc_insert,
Packit 67cb25
  mvacc_delete,
Packit 67cb25
  mvacc_mean
Packit 67cb25
};
Packit 67cb25
Packit 67cb25
const gsl_movstat_accum *gsl_movstat_accum_mean = &mean_accum_type;
Packit 67cb25
Packit 67cb25
static const gsl_movstat_accum variance_accum_type =
Packit 67cb25
{
Packit 67cb25
  mvacc_size,
Packit 67cb25
  mvacc_init,
Packit 67cb25
  mvacc_insert,
Packit 67cb25
  mvacc_delete,
Packit 67cb25
  mvacc_variance
Packit 67cb25
};
Packit 67cb25
Packit 67cb25
const gsl_movstat_accum *gsl_movstat_accum_variance = &variance_accum_type;
Packit 67cb25
Packit 67cb25
static const gsl_movstat_accum sd_accum_type =
Packit 67cb25
{
Packit 67cb25
  mvacc_size,
Packit 67cb25
  mvacc_init,
Packit 67cb25
  mvacc_insert,
Packit 67cb25
  mvacc_delete,
Packit 67cb25
  mvacc_sd
Packit 67cb25
};
Packit 67cb25
Packit 67cb25
const gsl_movstat_accum *gsl_movstat_accum_sd = &sd_accum_type;