/* weighted.c ********************************************************************************
* weighted.c -- Routines for re-weighted statistical series objects
*
* User routines
* Pro_WeightedStat() creates a re-weighted statistical series object.
*
* In a re-weighted statistical series, each sample is derived by taking a
* weighted sum of the samples in another statistical series. The user must
* provide a function that calculates the (possibly un-normalized) weights and
* another function that returns the range of nonzero-weighted samples in the old
* series that comprise each new sample.
*
* Version 1.0: Original version.
* Mark Showalter, PDS Ring-Moon Systems Node, March 1998.
* Version 1.1: Support for Pro_StatRange() function added.
* Mark Showalter, January 2000.
*******************************************************************************/
#include <stdio.h>
#include <string.h>
#include "profile.h"
/********************
* Type definitions *
********************/
typedef struct ZPRO_WEIGHTED_STRUCT {
XPRO_CLASS class;
PRO_OBJECT *oldstat;
void (*krange) RL_PROTO((RL_INT4 knew, RL_INT4 *k1, RL_INT4 *k2,
RL_VOID *params));
RL_FLT8 (*weights) RL_PROTO((RL_INT4 knew, RL_INT4 k,
RL_VOID *params));
RL_VOID *params;
RL_SIZE paramsize;
RL_BOOL isduped;
RL_INT4 kmin, kmax;
void (*printmore) RL_PROTO((RL_VOID *params));
} ZPRO_WEIGHTED;
/********************
* Static variables *
********************/
static XPRO_CLASS weighted_class = {XPRO_WEIGHTED_CLASS, "weighted", NULL};
/********************************
* Internal function prototypes *
********************************/
static RL_FLT8 ZPro_WeightedValue RL_PROTO((RL_VOID *pointer, RL_INT4 k,
RL_INT4 *flag));
static RL_FLT8 ZPro_WeightedCovar RL_PROTO((RL_VOID *pointer, RL_INT4 k1,
RL_INT4 k2));
static void ZPro_WeightedRange RL_PROTO((RL_VOID *pointer, RL_INT4 k,
RL_INT4 *k1, RL_INT4 *k2));
static void ZPro_FreeWeighted RL_PROTO((RL_VOID *pointer));
static void ZPro_PrintWeighted RL_PROTO((RL_VOID *pointer));
/*
********************************************************************************
* EXPORTED USER ROUTINES
********************************************************************************
*$ Component_name:
* Pro_WeightedStat (weighted.c)
*$ Abstract:
* This routine creates a new statistical series in which each sample is a
* weighted sum of samples from another statistical series.
*$ Keywords:
* PROFILE
* C, PUBLIC, SUBROUTINE
*$ Declarations:
* PRO_OBJECT *Pro_WeightedStat(stat, k1, k2, x1, dx, krange, weights,
* params, paramsize, dupe, printmore)
* PRO_OBJECT *stat;
* RL_INT4 k1, k2;
* RL_FLT8 x1, dx;
* void (*krange) (RL_INT4 knew, RL_INT4 *k1, RL_INT4 *k2,
* RL_VOID *params);
* RL_FLT8 (*weights) (RL_INT4 knew, RL_INT4 k, RL_VOID *params);
* RL_VOID *params;
* RL_SIZE paramsize;
* RL_BOOL dupe;
* void (*printmore) (RL_VOID *params);
*$ Inputs:
* stat pointer to the statistical series to re-weight.
* k1, k2 new range of indices.
* x1, dx new x coordinate starting value and interval.
* krange function that returns the range of indices on the old
* series corresponding to nonzero weights for a given new
* index.
* weights function that returns the weights on each old sample
* for each new sample.
* params parameter set to pass to krange and weight functions.
* paramsize number of bytes in parameter set.
* dupe TRUE to duplicate parameters; FALSE just to save
* pointer.
* printmore optional subroutine to print more info about weighted
* stat series; NULL to ignore.
*$ Outputs:
* none
*$ Returns:
* pointer to a new statistical series object; NULL on non-fatal error.
*$ Detailed_description:
* This routine creates a new statistical series in which each sample is a
* weighted sum of samples from another statistical series. The user must
* provide subroutines that return the set of samples and (possibly
* unnormalized) weights to apply to each old sample to generate any given
* new sample.
*
* This is the "workhorse" routine used by Pro_Filtered() and
* Pro_Resampled(). Note that there is no FORTRAN-callable equivalent to
* this routine.
*$ External_references:
* Profile toolkit
*$ Side_effects:
* Memory is allocated. The new object links to the original statistical
* series.
*$ Examples:
* Suppose we have a statistical series stat, and we wish to create a new
* series in which each sample is the average of the nearest three samples
* in the original series. (This is actually much easier to do using
* Pro_FilteredStat).
*
* We need these two functions:
*
* void krange(RL_INT4 knew, RL_INT4 *k1, RL_INT4 *k2, RL_VOID *params)
* {
* k1 = knew - 1;
* k2 = knew + 1;
* }
*
* RL_FLT8 weights(RL_INT4 knew, RL_INT4 k, RL_VOID *params)
* {
* return 1.;
* }
*
* Then this snippet of code creates and returns stat3, the new series:
*
* PRO_OBJECT *stat, *stat3;
* RL_INT4 k1, k2;
* RL_FLT8 x1, dx;
* Pro_SeriesIndices(stat, &k1, &k2);
* Pro_SeriesSampling(stat, &x1, NULL, &dx);
* stat3 = Pro_WeightedStat(stat, k1, k2, x1, dx, krange, weights,
* NULL, 0, FALSE, NULL);
*$ Error_handling:
* Profile library error handling is in effect.
*
* Conditions raised:
* RL_MEMORY_ERROR on memory allocation failure.
* PRO_CLASS_ERROR if the object is NULL or is not a stat series.
*$ Limitations:
* none
*$ Author_and_institution:
* Mark R. Showalter
* NASA/Ames Research Center
*$ Version_and_date:
* 1.0: March 1998
*$ Change_history:
* none
*******************************************************************************/
PRO_OBJECT *Pro_WeightedStat(stat, k1, k2, x1, dx, krange, weights,
params, paramsize, dupe, printmore)
PRO_OBJECT *stat;
RL_INT4 k1, k2;
RL_FLT8 x1, dx;
void (*krange) RL_PROTO((RL_INT4 knew, RL_INT4 *k1, RL_INT4 *k2,
RL_VOID *params));
RL_FLT8 (*weights) RL_PROTO((RL_INT4 knew, RL_INT4 k,
RL_VOID *params));
RL_VOID *params;
RL_SIZE paramsize;
RL_BOOL dupe;
void (*printmore) RL_PROTO((RL_VOID *params));
{
ZPRO_WEIGHTED *weighted;
PRO_OBJECT *new;
/* Make sure it's a statistical series */
if (XPro_StatPtr(stat) == NULL) return NULL;
/* Allocate new weighted statistical series structure */
weighted = (ZPRO_WEIGHTED *) XRL_Malloc(sizeof(ZPRO_WEIGHTED));
if (weighted == NULL) return NULL;
/* Initialize structure */
weighted->class = weighted_class;
weighted->oldstat = stat;
weighted->krange = krange;
weighted->weights = weights;
weighted->printmore = printmore;
weighted->kmin = k1;
weighted->kmax = k2;
/* Copy parameters if necessary */
if (dupe && paramsize > 0 && params != NULL) {
weighted->params = XRL_Malloc(paramsize);
if (weighted->params == NULL) {
XRL_Free((RL_VOID *) weighted);
return NULL;
}
memcpy(weighted->params, params, paramsize);
weighted->paramsize = paramsize;
weighted->isduped = TRUE;
}
else {
weighted->params = params;
weighted->paramsize = paramsize;
weighted->isduped = FALSE;
}
/* Create new statistical series object */
new = XPro_MakeStat(k1, k2, x1, dx,
ZPro_WeightedValue,
ZPro_WeightedCovar, ZPro_WeightedRange,
ZPro_FreeWeighted, ZPro_PrintWeighted,
(RL_VOID *) weighted);
/* Enslave old statistical series and check for errors */
if (new != NULL && XPro_EnslaveObject(new, stat)) {
Pro_FreeObject(new);
new = NULL;
}
/* Transfer Y-coordinate name to new object */
Pro_RenameObject(new, 2, Pro_ObjectName(stat,2));
return new;
}
/*
********************************************************************************
* INTERNAL ROUTINES
********************************************************************************
* ZPro_WeightedValue(pointer, k, flag)
*
* This internal routine returns the value of a weighted statistical series.
*
* Inputs:
* pointer pointer to the ZPRO_WEIGHTED structure.
* k index at which to evaluate series.
*
* Outputs:
* *flag 0 if value is valid.
*******************************************************************************/
static RL_FLT8 ZPro_WeightedValue(pointer, k, flag)
RL_VOID *pointer;
RL_INT4 k, *flag;
{
ZPRO_WEIGHTED *stat;
RL_INT4 i, i1, i2, oldflag, newflag;
RL_FLT8 sum0, sum1, value, weight;
stat = (ZPRO_WEIGHTED *) pointer;
(stat->krange) (k, &i1, &i2, stat->params);
sum0 = 0.;
sum1 = 0.;
newflag = PRO_MISSING_FLAG; /* use only if every value is missing */
for (i=i1; i<=i2; i++) {
value = Pro_SeriesValue(stat->oldstat, i, &oldflag);
if (oldflag == 0) {
weight = (stat->weights)(k, i, stat->params);
sum0 = sum0 + weight;
sum1 = sum1 + weight * value;
}
else if (oldflag == PRO_INVALID_FLAG) {
newflag = PRO_INVALID_FLAG; /* change from missing to invalid */
}
}
if (sum0 == 0.) {
*flag = newflag;
return 0.;
}
*flag = 0;
return sum1/sum0;
}
/*
********************************************************************************
* ZPro_WeightedCovar(pointer, k1, k2)
*
* This internal routine evaluates the covariance of a weighted statistical
* series.
*
* Inputs:
* pointer pointer to the ZPRO_WEIGHTED structure.
* k1, k2 pair of new indices at which to evaluate covariance.
*
* Returns: value of covariance between k1 and k2;
*******************************************************************************/
static RL_FLT8 ZPro_WeightedCovar(pointer, k1, k2)
RL_VOID *pointer;
RL_INT4 k1, k2;
{
ZPRO_WEIGHTED *stat;
RL_INT4 i, i1, i2, j, j1, j2, jmin, jmax, flag;
RL_FLT8 sum0, sum1, wsumi, wsumj, weight;
stat = (ZPRO_WEIGHTED *) pointer;
/* Find ranges of associated samples in old series */
(stat->krange) (k1, &i1, &i2, stat->params);
(stat->krange) (k2, &j1, &j2, stat->params);
/*******************************************************************************
** The old code...
**
** sum0 = 0.;
** sum1 = 0.;
** for (i=i1; i<=i2; i++) {
** (void) Pro_SeriesValue(stat->oldstat, i, &flag1);
**
** for (j=j1; j<=j2; j++) {
** (void) Pro_SeriesValue(stat->oldstat, j, &flag2);
**
** if (flag1 == 0 && flag2 == 0) {
** weight = (stat->weights)(k1, i, stat->params) *
** (stat->weights)(k2, j, stat->params);
** sum0 = sum0 + weight;
** sum1 = sum1 + weight * Pro_StatCovar(stat->oldstat, i, j);
** }
** }}
**
** The trouble with the code above is that, as the filter width increases, the
** computational effort goes as the width squared. It fails to take advantage
** of the fact that the covariance of samples i and j in the original series is
** likely to be zero except for i close to j. The revised code shown below
** takes advantage of this fact and so runs much faster.
*******************************************************************************/
/* Sum the products of weights for each pair of old samples */
wsumi = 0.;
for (i=i1; i<=i2; i++) {
(void) Pro_SeriesValue(stat->oldstat, i, &flag);
if (flag == 0) wsumi += (stat->weights)(k1, i, stat->params);
}
if (k1 == k2) {
wsumj = wsumi;
} else {
wsumj = 0.;
for (j=j1; j<=j2; j++) {
(void) Pro_SeriesValue(stat->oldstat, j, &flag);
if (flag == 0) wsumj += (stat->weights)(k2, j, stat->params);
}
}
sum0 = wsumi * wsumj;
/* Sum the products of weights times the covariance of each pair.
* For this sum, we can skip pairs whose covariance is zero.
*/
sum1 = 0.;
for (i=i1; i<=i2; i++) {
(void) Pro_SeriesValue(stat->oldstat, i, &flag);
if (flag != 0) continue;
Pro_StatRange(stat->oldstat, i, &jmin, &jmax);
if (jmax < j1) continue;
if (jmin > j2) continue;
if (jmin < j1) jmin = j1;
if (jmax > j2) jmax = j2;
weight = (stat->weights)(k1, i, stat->params);
for (j=jmin; j<=jmax; j++) {
(void) Pro_SeriesValue(stat->oldstat, j, &flag);
if (flag != 0) continue;
weight *= (stat->weights)(k2, j, stat->params);
sum1 += weight * Pro_StatCovar(stat->oldstat, i, j);
}
}
if (sum0 == 0.) return 0.;
return sum1/sum0;
}
/*
********************************************************************************
* ZPro_WeightedRange(pointer, k, k1, k2)
*
* This internal routine evaluates the range of samples that might have non-zero
* covariances with a given sample in a weighted statistical series.
*
* Inputs:
* pointer pointer to the ZPRO_WEIGHTED structure.
* k index at which to evaluate correlation range.
*
* Outputs:
* k1, k2 range of indices within which samples are correlated.
*
* Returns: none.
*******************************************************************************/
static void ZPro_WeightedRange(pointer, k, k1, k2)
RL_VOID *pointer;
RL_INT4 k, *k1, *k2;
{
ZPRO_WEIGHTED *stat;
RL_INT4 i1, i2, imin, imax, dummy, ktest;
stat = (ZPRO_WEIGHTED *) pointer;
/* Find range of old samples that are used in this sample */
(stat->krange) (k, &i1, &i2, stat->params);
/* Find range of old samples that might correlate with this range */
Pro_StatRange(stat->oldstat, i1, &imin, &dummy);
Pro_StatRange(stat->oldstat, i2, &dummy, &imax);
/* Search down for first uncorrelated new sample */
for (ktest = k; ktest >= stat->kmin; ktest--) {
(stat->krange) (ktest, &i1, &i2, stat->params);
if (i2 < imin || i1 > imax) break;
}
*k1 = ktest + 1;
/* Search up for first uncorrelated sample */
for (ktest = k; ktest <= stat->kmax; ktest++) {
(stat->krange) (ktest, &i1, &i2, stat->params);
if (i2 < imin || i1 > imax) break;
}
*k2 = ktest - 1;
}
/*
********************************************************************************
* ZPro_FreeWeighted(pointer)
*
* This internal routine deallocates the memory used by a weighted statistical
* series.
*
* Input:
* pointer pointer to the ZPRO_WEIGHTED structure.
*******************************************************************************/
static void ZPro_FreeWeighted(pointer)
RL_VOID *pointer;
{
ZPRO_WEIGHTED *stat;
stat = (ZPRO_WEIGHTED *) pointer;
if (stat->isduped) XRL_Free(stat->params);
XRL_Free(pointer);
}
/*
********************************************************************************
* ZPro_PrintWeighted(pointer)
*
* This internal routine prints information about a weighted statistical series.
*
* Input:
* pointer pointer to the ZPRO_FIXEDSTAT structure.
*******************************************************************************/
static void ZPro_PrintWeighted(pointer)
RL_VOID *pointer;
{
ZPRO_WEIGHTED *weighted;
weighted = (ZPRO_WEIGHTED *) pointer;
/* Make sure object is not NULL */
if (weighted == NULL) {
printf("PRINT ERROR: Weighted statistical series pointer is NULL\n");
return;
}
/* Make sure object is a weighted statistical series */
if (weighted->class.id != XPRO_WEIGHTED_CLASS) {
printf("PRINT ERROR: Object is not a weighted statistical series\n");
return;
}
/* Print object info... */
printf("\nWeighted statistical series parameters...\n");
printf("stat series = "); XPro_PrintInfo(weighted->oldstat);
printf(" paramsize = %d\n", weighted->paramsize);
printf(" duplicated = %s\n", (weighted->isduped ? "true" : "false"));
/* Print optional additional info... */
if (weighted->printmore != NULL)
(*weighted->printmore) (weighted->params);
return;
}
/*******************************************************************************
* FORTRAN INTERFACE ROUTINES
********************************************************************************
* Not provided, because generating FORTRAN interfaces to the "krange" and
* "weights" functions is just too complicated.
********************************************************************************
*/