Software for Analysis of Multifractal Time Series 1.0.0

File: <base>/multifractal.c (10,887 bytes)
/* file: multifractal.c		Y. Ashkenazy	  8 February 2004
                               Last revised:	 14 October 2004 (GBM, IH)

-------------------------------------------------------------------------------
multifractal: Calculate the multifractal partition functions of a time series
Copyright (C) 2004 Yossi Ashkenazy

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, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA.

You may contact the author by e-mail (ashkena@bgu.ac.il).  For updates to this
software, please visit PhysioNet (http://www.physionet.org/).
_______________________________________________________________________________

*/

#include <stdio.h>
#include <math.h>
#include <values.h>
#include <stdlib.h>
#include <string.h>

#define SQR(x) ( (x) * (x) )
#define Min(x,y) ( ((x) < (y)) ? (x) : (y) )
#define Max(x,y) ( ((x) > (y)) ? (x) : (y) )
#define SIGN(x) (((x)>0.0)?(1):(-1))
#define SIGN1(x) (((x)<0.0)?(-1):(1))

#define EPS 1e-4
#define EPS1 1e-6
#define MAX_CHAR_IN_LINE 4096
#define STRL     80
#define PI M_PI
#define Pi2 2.0*PI
#define NumOfArgc 5     /* Number of arguments called by the executable file */
#define TimesWS 6       /* The factor of convolution range; i.e., wavelet
			   box =-TimesWS*WS..TimesWS*WS */
#define Ratio (1.0/8.0) /* Defines the ratio between maximum scale (wavelet
			   box) and signal length */
#define DQ 0.2	        /* Resolution of moments q */


/* SUBROUTINE PrintColor

   Prints the color cascade in a ppm format (can be read by xv or ImageMagick).

   Subroutine for constructing color coded 3D wavelet decomposition: 
   x axis is time; y axis is wavelet  scale; z axis is wavelet coefficient
   (bright colors represent large values).
*/

PrintColor(double *vec, long i, long nx, long ny, double max, double min)
{
    long l, j, bin, color1, color2, color3, N, N1, N2, N3, N4, N5, N6;
    double delta;

    N = 255;
    N1 = N;
    N2 = 2*N;
    N3 = 3*N;
    N4 = 4*N;
    N5 = 5*N;
    N6 = 6*N;

    delta = (max - min)/(double)(N6-2);

    printf("P3\n# CREATOR: multifractal\n%d %d\n255\n", nx, ny);

    j = l = 0;
    while (l < i) {
        bin  =  (long)(((vec[l] - min)/delta));

        if (bin < N1) {
	    color1 = N;
            color2 = bin;
	    color3 = 0;
	}
	else if (bin < N2) {
	    color1 = N2-bin;
	    color2 = N;
	    color3 = 0;
	}
	else if (bin < N3) {
	    color1 = 0;
	    color2 = N;
	    color3 = bin-N2;
	}
	else if (bin < N4) {
	    color1 = 0;
	    color2 = N4-bin;
	    color3 = N;
	}
	else if (bin < N5) {
	    color1 = bin-N4;
	    color2 = 0;
	    color3 = N;
	}
	else {
	    color1 = N;
	    color2 = 0;
	    color3 = N6-bin;
	}
	printf("%3d %3d %3d ", color1, color2, color3);
	l++;
	if (++j >= 5) {
	    printf("\n");
	    j = 0;
	}
    }
}


/* SUBROUTINE F

   Calculates continuous Gaussian wavelet functions (zero to 7th derivative).
*/

double F(long n, double t)
{
  switch (n) {
    default:
    case 0:  return exp(-0.5*SQR(t));
    case 1:  return -t*exp(-0.5*SQR(t));
    case 2:  return (-1+t*t)*exp(-0.5*SQR(t));
    case 3:  return t*exp(-0.5*SQR(t))*(3-t*t);
    case 4:  return exp(-0.5*SQR(t))*(pow(t,4.0)-6*t*t+3);
    case 5:  return -t*exp(-0.5*SQR(t))*(pow(t,4.0)-10*t*t+15);
    case 6:  return exp(-0.5*SQR(t))*(pow(t,6.0)-15*pow(t,4.0)+45*t*t+15);
    case 7:  return -t*exp(-0.5*SQR(t))*(pow(t,6.0)-21*pow(t,4.0)+105*t*t-105);
  }
}


/* SUBROUTINE  PrepareVecF

   Vector storing the values of the wavelet function; done for numerical
   efficiency
*/

void PrepareVecF( double ws, double vec[], long Derivative )
{
  long i, tempi = (long)((double)TimesWS*ws);

  for (i = 0; i <= 2*tempi; i++)
      vec[i] = F((long)Derivative,((double)i-(double)(tempi))/ws);
}

/* SUBROUTINE MF1


   Find the maximum for the scale window ws, by performing the following steps:
    1) Wavelet convolution of the signal for increasing wavelet scale.
    2) Locate the local maxima of the absolute value of wavelet
       coefficient as a function of time for each wavelet scale.
    3) Check whether a local maximum at a given wavelet scale is located
       close to a maximum at a smaller scale - if yes connect both maxima,
       otherwise cancel it. Generate maxima lines.
    4) Check that the number of maxima at larger scales is less or equal
       to that at a smaller scale. 
    5) Track maxima lines for increasing wavelet scale by choosing at each
       scale value the supremum between all previous values at smaller
       scales.
*/

double MF1(double *vec, long n, double min_q, double max_q, double dq, 
	 double min_ws, double max_ws, double dws, long Derivative, long code)
{
    long nq,i,i1,i2,j,jj,sign,sign1,tempi_max,tempi,tempi1,tempi2,n_max,icolor;
    double *s, s1, min, max, temp, temp1, temp2, q;
    double *MaxVec, *VecWL, *VecF, *VecColor, *pt, ws;
    long *VecWLX, *MaxVecX, *ptl;
  
    nq = (long)((max_q-min_q)/dq)+1;

    MaxVec = (double *)calloc((size_t)(n+1),(size_t)(sizeof(double)));
    MaxVecX = (long *)calloc((size_t)(n+1),(size_t)(sizeof(long)));
    tempi_max = (long)((double)TimesWS*max_ws);
    s = (double *) calloc( (size_t)(nq+1),sizeof(double) );
    VecF = (double *) calloc( (size_t)(2*tempi_max+1),sizeof(double) );
    VecWL = (double *) calloc( (size_t)(n+1),sizeof(double) );
    VecWLX = (long *) calloc( (size_t)(n+1),sizeof(long) );

    if (code == 1) {
	printf("# %f\n", min_q);
	fflush(NULL);
    }
    else if (code == 3) {
	for (ws = min_ws, tempi1 = 0; ws <= max_ws; ws *= dws)
	    tempi1++;
	icolor = 1;
	max = -1e20;
	min = 1e20;
	VecColor = (double *)calloc((size_t)((n-2*tempi_max-1)*tempi1+1),
				    sizeof(double));
    }
    jj=0;
    for (ws = min_ws; ws <= max_ws; ws *= dws) {
	tempi = (long)((double)TimesWS*ws);
	for (i = 0; i < (2*tempi+1); i++)
	    VecF[i] = 0.0;
	for (i = 0; i < (n+1); i++)
	    VecWL[i] = 0.0;
	for (i = 0; i < (n+1); i++)
	    VecWLX[i] = 0;
	PrepareVecF(ws, VecF, Derivative);
      
	/* do the convolution */
	for (i = tempi_max; i < (n-tempi_max-1); i++) {
	    s1 = 0.0;
	    for (j = i-tempi; j <= (i+tempi); j++)
		s1 += vec[j] * VecF[j-i+tempi];
	    VecWL[i] = fabs(s1/ws); 
	    if (code == 3) {
		VecColor[icolor] = VecWL[i];
		icolor++;
		if (max < VecWL[i])
		    max = VecWL[i];
		if (min >VecWL[i])
		    min = VecWL[i];
	    }
	}
      
	/* Find the local maxima of the wavelet coefficient for each scale  */
	n_max = 0;
	sign = SIGN(VecWL[tempi_max+1] - VecWL[tempi_max]);
	temp1=0.0;
	for (j = 0, i = tempi_max+2; i < (n-tempi_max-1); i++) {
	    if ((fabs(VecWL[i] - VecWL[i-1]) > 0.0) && 
		((sign == 1) && ((SIGN(VecWL[i] - VecWL[i-1])) == (-1)))) {
		temp = VecWL[i-1];
		if (code == 2) {	/* Print the maxima lines (code 2) */
		    printf("%d %g\n", i, log(ws)/log(10.0));
		    fflush(NULL);
		}
		n_max++;
		VecWL[j]=temp;
		VecWLX[j]=i-1;
		temp1=temp;
		j++;
	    }
	    sign=SIGN(VecWL[i]-VecWL[i-1]);
	}
      
	/* Tracking the maxima lines: test for supremum */
	if (ws > min_ws) {
	    for (i1 = i2 = i = 0; i < nq;i++)
		s[i]=0.0;
	    while (((i1-1) < n_max) && ((i2-1) < jj)) {
		if ((VecWLX[i1] - MaxVecX[i2]) <= (MaxVecX[i2+1] - VecWLX[i1]))
		    VecWL[i1] = Max(VecWL[i1],MaxVec[i2]);
		else
		    VecWL[i1] = Max(VecWL[i1],MaxVec[i2+1]);
		for (i = 0; i < nq; i++)
		    s[i] += pow(VecWL[i1], min_q+(double)i*dq);
		i1++;
		i2++;
		while ((i2 < jj) && (VecWLX[i1] >= MaxVecX[i2]))
		    i2++;
		i2--;
	    }
	    if (code == 1) {	    /* Print the partition function */
		printf("%g ", log(ws)/log(10.0));
		for (i = 0; i < nq; i++)
		    printf("%g ", log(s[i])/log(10.0));
		printf("\n");
		fflush(NULL);
	    }
	}
	jj = n_max;
	pt = MaxVec;
	MaxVec = VecWL;
	VecWL = pt;
	ptl = MaxVecX;
	MaxVecX = VecWLX;
	VecWLX = ptl;
    }
    free(VecF);
    free(VecWL);
    free(VecWLX);
    free(MaxVec);
    free(MaxVecX);

    /* Print the wavelet cascade (code 3) */
    if (code == 3) {
	PrintColor(VecColor,icolor,n-2*tempi_max-1,tempi1,max,min);
	free(VecColor);
    }
}


main(int argc, char *argv[])
{
    long i, j;
    long N, Derivative = 3, code = 1;
    double *VecX, temp, tempx, tempy, ws, dw, MinScale, MaxScale, 
	q, q_min, q_max, dq;
    FILE *fp;

    if (argc < NumOfArgc) {
      printf("Usage: %s INPUT N QMIN QMAX DW MODE >OUTPUT\n", argv[0]);
      printf("  INPUT  name of file containing the input time series\n");
      printf("  N      number of points (lines) in INPUT\n");
      printf("  QMIN   minimum MF order\n");
      printf("  QMAX   maximum MF order\n");
      printf("  DW     order of the Gaussian derivative wavelet (0-7)\n");
      printf("  MODE   the type of output to be produced, one of:\n");
      printf("            1: partition functions (text)\n");
      printf("            2: maxima lines (text)\n");
      printf("            3: wavelet cascade (PPM image)\n\n");
      printf(" INPUT is a text file containing two columns of numbers; the\n");
      printf(" first is ignored, and the second contains the data values.\n");
      exit(0);
    }

    /* N is the series length */
    N = atol(argv[2]);

    /* VecX is the vector  storing the data */
    VecX = (double *) malloc((size_t)(N+1) * sizeof(double));

    for (i = 0; i < N+1; i++)
	VecX[i] = 0.0;

    /* Open data file for reading two column data file. The second column
       should be the data that are analyzed. */
    fp = fopen( argv[1], "r");
    for (i = 0; (i < N) && (fscanf(fp, "%lf%lf", &temp, &(VecX[i])) == 2); i++)
	;
    fclose(fp);
    N = i;

    /* MaxScale is the maximum scale analyzed */
    MaxScale = Ratio*0.5*(((double)N)-1.0)/(double)TimesWS;
    /* MinScale is the minimum scale analyzed */
    MinScale = 2.0;	/* 0.5*M_E */
    /* q_min is the minimum moment q for which the partition function is
       calculated */
    q_min = atof(argv[3]);
    /* q_max is the maximum moment q for which the partition function is
       calculated */
    q_max = atof(argv[4]);
    /* resolution of moments in steps dq for which the partition function is
       calculated */
    dq = DQ;
    if (argc > NumOfArgc) {
	Derivative = atoi(argv[5]);
	if (argc > (NumOfArgc+1))
	    code = atoi(argv[6]);
    }
    /* scale resolution for which the partition function is calculated */
    dw = pow(2.0,0.05);
    MF1(VecX, N, q_min, q_max, dq, MinScale, MaxScale, dw, Derivative, code);
    free(VecX);
}