c/Mex Code for Spike Time Distances Between Spike
Trains
main cost-based metrics page
algorithm page for cost-based metrics
c/MEX Code for Spike Time Metric
function d=spkdl(stime,sstart,send,cost)
%SPKDL. Calculates the "spike time" distance matrix at fixed cost. (MEX file version.)
% D=SPKDL(STIME,SSTART,SEND,COST). Calculates the full distance matrix for the "spike time" metric
% (Victor & Purpura 1996) for a single cost. Compiled as a MEX file (SPKDL.DLL) on Windows NT, but
% compilation seems to work on Windows 98 and Windows 2000 platforms.
%
% STIME list of spike times (row vector)
% SSTART list of trial start indices (row vector, length: # stimulus XMPsentations)
% SEND list of trial end indices (row vector, length: same as SSTART); if a trial has no
% spikes in it, the SEND value should be one less than the corresponding SSTART value
% COST cost per unit time to move a spike
%
% D the distance , as a row vector (length: #XMPsentations^2)
%
% Copyright (c) 1999, 2024, by Daniel Reich and Jonathan Victor, all rights reserved.
%
% This code is released under a BSD license.
% Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
% * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
% * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
% in the documentation and/or other materials provided with the distribution.
% * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this
% software without specific prior written permission.
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
% BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
% SHALL HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
% LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
%
% Written as a MEX file by Daniel Reich from FORTRAN code by Jonathan Victor.
/*=================================================================
*
* SPKDL.C .MEX file corresponding to SPKD.M
* CALCULATES DISTANCE BETWEEN TWO SPIKE TRAINS
* IN THE SPIKE TIME METRIC BY A CONTINUUM
* MODIFICATION OF THE SELLERS ALGORITHM
*
* INPUT IS ACTUALLY A SERIES OF SPIKE TRAINS WITH INDICES
*
* INPUT VARIABLES:
* STIME: LIST OF SPIKE TIMES (1...n)
* SSTART: LIST OF TRIAL START INDICES
* SEND: LIST OF TRIAL END INDICES
* COST: THE COST PER UNIT TIME TO MOVE A SPIKE
*
* D: THE DISTANCE
*
* MAXCOST is set to 100000
*
* Copyright (c) 1999 by Daniel Reich and Jonathan Victor
* All rights reserved.
*=================================================================*/
/* $Revision: 1.0 $ */
#include "mex.h"
/* Input Arguments */
#define STIME prhs[0]
#define SSTART prhs[1]
#define SEND prhs[2]
#define COST prhs[3]
/* Output Arguments */
#define D plhs[0]
#if !defined(MAX)
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#endif
#if !defined(MIN)
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#endif
#define PI 3.14159265
#define MAXCOST 10000
#define EPS 0.000001
#define NR_END 1
#define FREE_ARG char*
void nrerror(char error_text[])
/* Numerical Recipes standard error handler */
{
fprintf(stderr,"Numerical Recipes run-time error...\n");
fprintf(stderr,"%s\n",error_text);
fprintf(stderr,"...now exiting to system...\n");
exit(1);
}
double *dvector(long nl, long nh)
/* allocate a double vector with subscript range v[nl..nh] */
{
double *v;
v=(double *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(double)));
if (!v) nrerror("allocation failure in dvector()");
return v-nl+NR_END;
}
double **dmatrix(long nrl, long nrh, long ncl, long nch)
/* allocate a double matrix with subscript range m[nrl..nrh][ncl..nch] */
{
long i, nrow=nrh-nrl+1,ncol=nch-ncl+1;
double **m;
/* allocate pointers to rows */
m=(double **) malloc((size_t)((nrow+NR_END)*sizeof(double*)));
if (!m) nrerror("allocation failure 1 in matrix()");
m += NR_END;
m -= nrl;
/* allocate rows and set pointers to them */
m[nrl]=(double *) malloc((size_t)((nrow*ncol+NR_END)*sizeof(double)));
if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
m[nrl] += NR_END;
m[nrl] -= ncl;
for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
/* return pointer to array of pointers to rows */
return m;
}
void free_dvector(double *v, long nl, long nh)
/* free a double vector allocated with dvector() */
{
free((FREE_ARG) (v+nl-NR_END));
}
void free_dmatrix(double **m, long nrl, long nrh, long ncl, long nch)
/* free a double matrix allocated by dmatrix() */
{
free((FREE_ARG) (m[nrl]+ncl-NR_END));
free((FREE_ARG) (m+nrl-NR_END));
}
double dabs(double x)
{
if (x<0) return -x;
else return x;
}
static void getdist(
double *d,
unsigned int nspi,
double tli[],
unsigned int nspj,
double tlj[],
double cost
)
{
double **scr;
unsigned int i,j;
if (cost=MAXCOST)
*d=nspi+nspj;
else {
/* INITIALIZE MARGINS WITH COST OF ADDING A SPIKE */
scr=dmatrix(0,nspi,0,nspj);
for (i=0; i<=nspi; i++)
scr[i][0]=i;
for (j=0; j<=nspj; j++)
scr[0][j]=j;
if (nspi!=0 && nspj!=0) {
/* THE HEART OF THE ALGORITHM */
for (i=1; i<=nspi; i++)
for (j=1; j<=nspj; j++)
scr[i][j]=MIN(MIN(scr[i-1][j]+1,scr[i][j-1]+1),scr[i-1][j-1]+cost*dabs(tli[i-1]-tlj[j-1]));
}
*d=scr[nspi][nspj];
free_dmatrix(scr,0,nspi,0,nspj);
}
return;
}
static void getdistl(
double *d,
unsigned int nstart,
double stime[],
double sstart[],
double send[],
double cost
)
{
double *tli, *tlj;
double dist;
unsigned int i,j,itli,itlj;
unsigned int nspi,nspj;
double junk;
for (i=0; i0) {
tli=dvector(0,nspi-1);
for (itli=0; itli0 && nspj>0) {
tlj=dvector(0,nspj-1);
for (itlj=0; itlj0) dist=nspj;
else if (nspj==0 && nspi>0) dist=nspi;
else if (nspj==0 && nspi==0) dist=0;
/*printf("\n%d %d %d %f",i,j,i*nstart+j,dist);*/
d[i*nstart+j]=d[j*nstart+i]=dist;
}
if (nspi>0) free_dvector(tli,0,nspi-1);
}
return;
}
void mexFunction( int nlhs, mxArray *plhs[],
int nrhs, const mxArray*prhs[] )
{
double *stime, cost;
double *sstart, *send;
double *d;
unsigned int nspikes,nstart,nend,junk;
/* Check for proper number of arguments */
if (nrhs != 4) {
mexErrMsgTxt("Four input arguments required.");
} else if (nlhs > 1) {
mexErrMsgTxt("Too many output arguments.");
}
/* Check the dimensions of STIME. STIME can be n X 1 or 1 X n. */
nspikes = mxGetM(STIME);
junk = mxGetN(STIME);
if (!mxIsDouble(STIME) || (MIN(nspikes,junk) > 1)) {
mexErrMsgTxt("SPKDL requires that STIME be a row or column vector.");
}
nspikes=MAX(nspikes,junk);
nstart = mxGetM(SSTART);
junk = mxGetN(SSTART);
nstart = MAX(nstart,junk);
nend = mxGetM(SEND);
junk = mxGetN(SEND);
nend= MAX(nend,junk);
if (nstart != nend) {
mexErrMsgTxt("SPKDL requires that SSTART and SEND have the same dimensions.");
}
cost = mxGetM(COST);
junk = mxGetN(COST);
if (!mxIsDouble(COST) || (MAX(cost,junk) != 1) || (MIN(cost,junk) != 1)) {
mexErrMsgTxt("SPKD requires that COST be a scalar.");
}
/* Create a matrix for the return argument */
D = mxCreateDoubleMatrix(nstart*nstart, 1, mxREAL);
/* Assign pointers to the various parameters */
d = mxGetPr(D);
stime = mxGetPr(STIME);
sstart = mxGetPr(SSTART);
send = mxGetPr(SEND);
cost = mxGetScalar(COST);
/* Do the actual computations in a subroutine */
getdistl(d,nstart,stime,sstart,send,cost);
return;
}