Correlation Library DLL  1.0
Correlation Library DLL Documentation

Introduction

Correlation Library is a C++ library implemented by Raul C. Muresan. The code is free for non-commercial purposes.
For details on the Scaled Correlation algorithm see paper on "Scaled Correlation Analysis" by Danko Nikolic, Raul C. Muresan, Weijia Feng, and Wolf Singer.

Description

The Correlation Library offers support for computing cross and scaled correlation between a pair of continuous valued, binary valued, or mixed signals.
For each method of computing the correlation and each combination of signals, there is one specialized class that can handle the respective operations.
The library compiles into a Windows DLL that exports a number of functions as wrappers over the original classes.

C++ classes

The C++ classes are not explicitly described here. We only provide a short description for the user to understand the structure of the library.
The class functionalities are exported by the means of wrapper functions that are described in section "Dll functions exported".

Naming conventions

Each class is named as: C_descriptor_sufix, where

  • descriptor - stands for CrossCorrelation or ScaledCorrelation
  • sufix - denotes the combination type of data that the class accepts: B = Binary valued discrete signal (represented as a vector of time stamps corresponding to events with value "1"), C = Continuous valued discrete signal (represented as a vector of samples) => CC = Continuous-Continuos; BC = Binary-Continuous; BB = Binary-Binary.

List of classes

  • CCrossCorrelationCC: A class that computes the cross-correlation for two continuous valued signals (Continuous - Continuous)
  • CCrossCorrelationBC: A class that computes the cross-correlation for a vector of time stamps corresponding to events in a binary signal and a continuous valued signal (Binary - Continuous)
  • CCrossCorrelationBB: A class that computes the cross-correlation for two vectors of time stamps corresponding to events in binary signals (Binary - Binary)
  • CScaledCorrelationCC: A class that computes the scaled correlation for two continuous valued signals (Continuous - Continuous)
  • CScaledCorrelationBC: A class that computes the scaled correlation for a vector of time stamps corresponding to events in a binary signal and a continuous valued signal (Binary - Continuous)
  • CScaledCorrelationBB: A class that computes the scaled correlation for two vectors of time stamps corresponding to events in binary signals (Binary - Binary)

Input data formatting

All classes respect the same standard for accepting the input data. For a binary input, a vector of time stamps is expected as input while for a continuous valued input, a digitized vector of samples is expected as input.

When multiple measurements (trials) are available for the data, the classes can handle multiple trials. The length of the trial, in original sampling units, has to be specified in the constructor. Next, when the input signals are provided in the method call to ComputeCrossCorrelation or ComputeScaledCorrelation, the method determines how many trials are available, computes the correlogram on each trial and finally averages the correlograms into one single correlogram.

When multiple trials are available, the input should be structured as follows:

|.........Trial 1...........||.........Trial 2...........||.........Trial 3...........||.........Trial 4...........||.........Trial 5...........|

  • For continuous valued signals, there must be as many samples in each trial as the previously specified trial length. In any case, the method only considers as many samples as specified in the call to ComputeCrossCorrelation or ComputeScaledCorrelation.

  • For binary signals, the number of available trials is decided by taking into account the time stamp with the largest value. The method then searches, for each trial, the presence of valid time stamps. Time stamps for consecutive trials have to have increasing values. The trial to which a time stamp belongs is decided by taking into account the length of a trial in sampling units.

If the user cannot structure the trials as a contiguous series of datapoints, then the best option is to pass a single trial to the class, get the correlogram for each trial, and then the user should compute the average correlogram.

Note:
For the case of scaled correlation, the user can use the sum of coefficients of correlation for each bin of the correlogram and the count of coefficients to efficiently compute the average, smooth, scaled correlogram for all trials (see section Example). Averaging the sums of correlation coefficients gives a better and smoother estimate of the final scaled correlogram than averaging the individual scaled correlograms for each trial (see Example: Scaled correlogram example with only one trial passed at a time).

Method calls

For the two types of correlations the classes expose an interface to compute the CrossCorrelation and the ScaledCorrelation respectively. Depending on the type of the input data, the methods accept different parameter types. However, they have a general format:

  • ComputeCrossCorrelation(VectorA, SizeOfVectorA, VectorB, SizeOfVectorB, pbNormalizeCorrelogram)
  • ComputeScaledCorrelation(VectorA, SizeOfVectorA, VectorB, SizeOfVectorB, pbUseFisherZTransform)

    The first four parameters specify the input vectors and their sizes (in number of elements). Each vector has a different type depending if the input signal is binary or continuous.
    For binary data, the vectors are arrays of integers holding the time stamps. For continuous valued data, the vectors are arrays of floats, holding the samples.
    The last parameter of each function model is a boolean. pbNormalizeCorrelogram - determines whether the cross-correlogram should be normalized. pbUseFisherZTransform - determines if the scaled correlogram should use the Fisher Z Transform to average the coefficients of correlation.

Output data

Each class exposes an interface that allows access to the computed cross or scaled correlogram. The correlogram is stored in the object as a vector of floats. The vector has a size of 2*CorrelationWindow+1.
The correlogram vector is stored such that the correlation at lag -CorrelationWindow is at position 0, the correlation at lag 0 is stored at position CorrelationWindow, and the correlation at lag +CorrelationWindow is stored at position 2*CorrelationWindow.

To retrieve the correlogram, call:

  • GetCrossCorrelogram() - retrieves a pointer to the vector holding the cross-correlogram
  • GetScaledCrossCorrelogram() - retrieves a pointer to the vector holding the scaled correlogram
    The user should not write into the vectors!
Attention:
Very important: the cross and scaled correlation buffers might contain values of Not A Number (NAN = -100000000.0f) if there was no variance in the input signals and correlation was, as a consequence, not defined! Always check the output buffers for NAN values before using them! Otherwise results may be compromised.

Dll functions exported

The correlation library provides flexible means to interact with the classes that compute correlation. It exports a set of wrapper functions that can be used by an external caller to access the full class functionality.

Interfacing convention

To interface a given class method, the library exports a function with a similar name that has an additional parameter, which is an untyped pointer to an already created object of that class.

For example, let ClassA be the class of interest, and Method1 be a method of ClassA. To create an interface to ClassA functionality, one must first create an object of that class and then call a wrapper function to access the class method.

 void* Wrapper_ObjectConstructor_ClassA()
 {
        return new ClassA();
 }

 void Wrapper_ObjectDestructor_ClassA(void* pObj)
 {
        delete ((ClassA*) pObj);
 }

 void Wrapper_Method1_Of_ClassA(void* pObj,int iSomeParam)
 {
        ((ClassA*) pObj)->Method1(iSomeParam);
 }      

 void main()
 {
        void *pObject;
        pObject = Wrapper_ObjectConstructor_ClassA();   //A wrapper over the constructor of class A
        Wrapper_Method1_Of_ClassA(pObject,1);           //A wrapper over Method1 of class A
        Wrapper_ObjectDestructor_ClassA(pObject);       //A wrapper over the destructor of class A
 }
Note:
The wrapper constructors create untyped pointers to objects. The DLL caller can use these pointers as handles for the objects of a given class. All class operations have, as an additional parameter, the pointer to the object being handled. Depending on the wrapper function that is called, a corresponding method will be called from a corresponding class of the object whose handle is passed to the wrapper function.

Naming conventions

Each function is named as: prefix_name_sufix, where

  • prefix - is optional and is usually added to avoid confusion, when a function with the same name is present in both Cross-Correlation and Scaled-Correlation classes. The prefix could be either CrossCorrelation or ScaledCorrelation
  • name - is usually the name of the class method that is being interfaced
  • sufix - denotes the combination type of data that the class accepts: D = Binary, C = Continuous => CC = Continuous-Continuos; BC = Binary-Continuous; BB = Binary-Binary.

List of wrapper functions

Cross-correlation for Continuous-Continuous data:

Cross-correlation for Binary-Continuous data:

Cross-correlation for Binary-Binary data:

Scaled correlation for Continuous-Continuous data:

Scaled correlation for Binary-Continuous data:

Scaled correlation for Binary-Binary data:

Examples of usage

Let us consider we have two continuous valued signals that we want to correlate. The signals, we suppose, have been sampled at 1 kHz. Each signal has 2 trials recorded, with a length of 1000 ms each. We want to compute the cross-correlation and the scaled correlation.

 //Cross correlogram example:
 #include "CorrelationLib.h"
 #include "Constants.h"
 float SignalA[2000];
 float SignalB[2000];
 float* CrossCorrelogram;
 int iTrialLength = 1000;               //the legth of the trial is 1000 so we have signal vectors holding 2 trials (2000 entries)
 int iCorrelationWindow = 100;          //-100..+100 correlation lags
 
 //Here you should fill the SignalA and SignalB arrays with values

 //And then compute the correlogram
 int isError = 0;

 void* ClassicCC_Computer = NULL;
 ClassicCC_Computer = CreateCrossCorrelationComputerCC(iCorrelationWindow,iTrialLength,isError);
 if(isError == 0)
 {
        ComputeCrossCorrelationCC(ClassicCC_Computer,SignalA,2000,SignalB,2000,0);
        CrossCorrelogram = GetCrossCorrelogramCC(ClassicCC_Computer);
        for(int j=0;j<2*iCorrelationWindow+1;j++)
        {
                if(CrossCorrelogram[j] != NAN)
                {
                        //use the CrossCorrelogram only if the value is not NAN (only if the correlation was defined)
                }
        }
 }
 if(ClassicCC_Computer != NULL) FreeCrossCorrelationComputerCC(ClassicCC_Computer);



 //Scaled correlogram example:
 #include "CorrelationLib.h"
 #include "Constants.h"
 float SignalA[2000];
 float SignalB[2000];
 float* ScaledCorrelogram;
 int iTrialLength = 1000;               //the legth of the trial is 1000 so we have signal vectors holding 2 trials (2000 entries)
 int iCorrelationWindow = 100;          //-100..+100 correlation lags
 int iScaleWindow = 25;                 //25 ms - if the sampling frequency of the signals is 1 kHz => we are looking to components faster than 40 Hz
 
 //Here you should fill the SignalA and SignalB arrays with values

 //And then compute the correlogram
 int isError = 0;
 void* ScaledCC_Computer = NULL;
 ScaledCC_Computer = CreateScaledCorrelationComputerCC(iScaleWindow,iCorrelationWindow,iTrialLength,isError);
 if(isError == 0)
 {
        ComputeScaledCorrelationCC(ScaledCC_Computer,SignalA,2000,SignalB,2000,0);
        ScaledCorrelogram = GetScaledCrossCorrelogramCC(ScaledCC_Computer);
        for(int j=0;j<2*iCorrelationWindow+1;j++)
        {
                if(ScaledCorrelogram[j] != NAN)
                {
                        //use the ScaledCorrelogram only if the value is not NAN (only if the correlation was defined)
                }
        }
 }
 if(ScaledCC_Computer != NULL) FreeScaledCorrelationComputerCC(ScaledCC_Computer);



Let us now assume we have again two signals each with two trials. This time however, we want to compute the correlogram manually by computing the sum of correlation coefficients for each trial and then computing the average in the end.

 //Scaled correlogram example with only one trial passed at a time:
 //This is useful when the user wants to compute the correlogram per trial and in the end average the correlation coefficients himself.
 //Averaging the sums of correlation coefficients gives a better and smoother estimate of the final scaled correlogram than averaging the individual scaled correlograms for each trial!!!
 #include "CorrelationLib.h"
 #include "Constants.h"
 float SignalA[2000];
 float SignalB[2000];
 float ScaledCorrelogram[201];
 int   SummedCoefficientsCount[201];
 float *coeff_sum_buffer;
 int *coeff_count_buffer;
 int iTrialLength = 1000;               //the legth of the trial is 1000 so we have signal vectors holding 2 trials (2000 entries)
 int iCorrelationWindow = 100;          //-100..+100 correlation lags
 int iScaleWindow = 25;                 //25 ms - if the sampling frequency of the signals is 1 kHz => we are looking to components faster than 40 Hz
 int iTrialNr = 2;                      //Two trials
 
 //Here you should fill the SignalA and SignalB arrays with values

 //And then compute the correlogram
 int isError = 0;
 void* ScaledCC_Computer;
 ScaledCC_Computer = CreateScaledCorrelationComputerCC(iScaleWindow,iCorrelationWindow,iTrialLength,isError);
 
 if(isError == 0)
 {
        //Cleanup the final buffer and the counting buffer
        for(int j=0;j<2*iCorrelationWindow+1;j++)
        {
                ScaledCorrelogram[j] = 0;
                SummedCoefficientsCount[j] = 0;
        }

        //For each trial, compute the sum of correlation coefficients
        for(int i=0;i<iTrialNr;i++)
        {
                ComputeScaledCorrelationCC(ScaledCC_Computer,&(SignalA[i*iTrialLength]),iTrialLength,&(SignalB[i*iTrialLength]),iTrialLength,0);
                coeff_sum_buffer = GetPearsonCoefficientSumsCC(ScaledCC_Computer);
                coeff_count_buffer = GetPearsonCoefficientCountsCC(ScaledCC_Computer);
                for(j=0;j<2*iCorrelationWindow+1;j++)
                {
                        if(coeff_sum_buffer[j] != NAN) ScaledCorrelogram[j] += coeff_sum_buffer[j];
                        SummedCoefficientsCount[j] += coeff_count_buffer[j];
                }               
        }

        //Now compute the final scaled correlogram by computing the average
        for(j=0;j<2*iCorrelationWindow+1;j++) if(SummedCoefficientsCount[j]) ScaledCorrelogram[j] /= SummedCoefficientsCount[j];
 }
 if(ScaledCC_Computer != NULL) FreeScaledCorrelationComputerCC(ScaledCC_Computer);