package weka.core;
/**
Basic DTW implementation for Weka. /Each instance is assumed to be a time series. Basically we
pull all the data out and proceed as usual!
NOTE: Need to implement the early abandon, no point doing all the sums. Also nee to implement a pre calculated version
Needs black box debug.
**/
import java.util.ArrayList;
import java.util.Enumeration;
import weka.core.neighboursearch.PerformanceStats;
public class DTW_DistanceBasic extends EuclideanDistance{
/**
*
*/
private static final long serialVersionUID = 1L;
double r=1; //Warping window size percentage, between 0 and 1
double[][] matrixD;
int endX=0;
int endY=0;
public DTW_DistanceBasic(){
super();
m_DontNormalize=true;
}
public DTW_DistanceBasic(Instances data) {
super(data);
m_DontNormalize=true;
}
//Needs overriding to avoid cutoff check
public double distance(Instance first, Instance second){
return distance(first, second, Double.POSITIVE_INFINITY, null, false);
}
public double distance(Instance first, Instance second, PerformanceStats stats) { //debug method pls remove after use
return distance(first, second, Double.POSITIVE_INFINITY, stats, false);
}
/* ASSUMES THE CLASS INDEX IS THE LAST DATA FOR THE NORMALISATION. But dont do the normalisation here anyway.
*
Euclidean distance normalises to [0..1] based on the flag m_DontNormalize, but it does this in the calculation.
More efficient to do this as a filter, otherwise you are repeatedly recalculating.
Basic normalisation is implemented, but advised not to use it, so flag m_DontNormalize set by default to true.
*/
public double distance(Instance first, Instance second, double cutOffValue, PerformanceStats stats){
return distance(first,second,cutOffValue,stats,false);
}
public double distance(Instance first, Instance second, double cutOffValue, PerformanceStats stats, boolean print) {
//Get the double arrays
return distance(first,second,cutOffValue);
}
public double distance(Instance first, Instance second, double cutOffValue) {
double[] f;
double[] s;
// System.out.println(" In DTW Basic distance, cutoff ="+cutOffValue);
//Need to remove class value if present.
int fClass=first.classIndex();
if(fClass>0) {
f=new double[first.numAttributes()-1];
int count=0;
for(int i=0;i<f.length+1;i++){
if(i!=fClass){
f[count]=first.value(i);
count++;
}
}
}
else
f=first.toDoubleArray();
int sClass=second.classIndex();
if(sClass>0) {
s=new double[second.numAttributes()-1];
int count=0;
for(int i=0;i<s.length;i++){
if(i!=sClass){
s[count]=second.value(i);
count++;
}
}
}
else
s=second.toDoubleArray();
/* Dont use this
if(!m_DontNormalize){
for(int i=0;i<f.length;i++){
int index=first.index(i);
//ASSUME THE CLASS INDEX IS THE LAST DATA
f[i]=(f[i]-m_Ranges[index][R_MIN])/(m_Ranges[index][R_MAX] - m_Ranges[index][R_MIN]);
}
for(int i=0;i<s.length;i++){
int index=second.index(i);
//ASSUME THE CLASS INDEX IS THE LAST DATA
s[i]=(s[i]-m_Ranges[index][R_MIN])/(m_Ranges[index][R_MAX] - m_Ranges[index][R_MIN]);
}
}
*/
return distance(f,s,cutOffValue);
}
/* DTW Distance: Need to implement the early abandon
*
*/
public double distance(double[] a,double[] b, double cutoff){
double dist=0, minDist;
// Set the longest series to a
double[] temp;
if(a.length<b.length){
temp=a;
a=b;
b=temp;
}
int n=a.length;
int m=b.length;
/* Parameter 0<=r<=1. 0 == no warp, 1 == full warp
generalised for variable window size
* */
int windowSize = getWindowSize(0,0,n);
//Set all to max
matrixD = new double[n][m];
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
matrixD[i][j]=Double.MAX_VALUE;
matrixD[0][0]=(a[0]-b[0])*(a[0]-b[0]);
//Base cases for warping 0 to all with max interval r
//Warp a[0] onto all b[1]...b[r+1]
for(int j=1;j<windowSize && j<n;j++)
matrixD[0][j]=matrixD[0][j-1]+(a[0]-b[j])*(a[0]-b[j]);
// Warp b[0] onto all a[1]...a[r+1]
for(int i=1;i<windowSize && i<n;i++)
matrixD[i][0]=matrixD[i-1][0]+(a[i]-b[0])*(a[i]-b[0]);
//Warp the rest,
int start,end;
for (int i=1;i<n;i++){
windowSize = getWindowSize(i,1,n);
if(i-windowSize<1)
start=1;
else
start=i-windowSize+1;
if(start+windowSize>=m)
end=m;
else
end=start+windowSize;
for (int j = start;j<end;j++)
{
//Find the min of matrixD[i][j-1],matrixD[i-1][j] and matrixD[i-1][j-1]
minDist=matrixD[i][j-1];
if(matrixD[i-1][j]<minDist)
minDist=matrixD[i-1][j];
if(matrixD[i-1][j-1]<minDist)
minDist=matrixD[i-1][j-1];
matrixD[i][j]=minDist+(a[i]-b[j])*(a[i]-b[j]);
//Is this a correct early
}
}
/* for(int i=0;i<n;i++){
System.out.print("Row ="+i+" = ");
for(int j=0;j<n;j++){
System.out.print(" "+matrixD[i][j]);
}
System.out.print("\n");
}
*/
//Find the minimum distance at the end points, within the warping window. Maybe should not have to match the ends?
return matrixD[n-1][m-1];
/* int x=n-1,y=0;
dist=matrixD[n-1][0];
for(int j=1;j<m;j++){
if(matrixD[n-1][j]<dist){
dist=matrixD[n-1][j];
y=j;
}
}
for(int i=0;i<n;i++){
if(matrixD[i][m-1]<dist){
x=i;
y=n-1;
dist=matrixD[i][m-1];
}
}
endX=x;
endY=y;
return dist;
*/ }
// This could be generalised to allow for different window algorithms
int getWindowSize(int i, int j, int n){
int w=(int)(Math.floor(this.r*n));
//No Warp, windowSize=1
if(w<1) w=1;
//Full Warp : windowSize=n, otherwise scale between
else if(w<n)
w++;
return w;
}
void printPath(){
//Find Path backwards in pairs?
int n=matrixD.length;
int m=matrixD[0].length;
int x=n-1,y=m-1;
int count=0;
System.out.println(count+"END Point = "+x+","+y+" value ="+matrixD[x][y]);
while(x>0 && y>0)
{
//Look along
double min=matrixD[x-1][y-1];
if(min<=matrixD[x-1][y] && min<=matrixD[x][y-1]){
x--;
y--;
}
else if(matrixD[x-1][y] < matrixD[x][y-1])
x--;
else
y--;
count++;
System.out.println(count+" Point = "+x+","+y+" value ="+matrixD[x][y]);
}
while(x>0){
x--;
System.out.println(count+" Point = "+x+","+y+" value ="+matrixD[x][y]);
}
while(y>0){
y--;
System.out.println(count+" Point = "+x+","+y+" value ="+matrixD[x][y]);
}
}
public String toString() {
return "DTW BASIC. r="+r;
}
public String globalInfo() {
return " DTW Basic Distance";
}
public String getRevision() {
return null;
}
public void setR(double x){ r=x;}
/* JML Implementation version
*
*/
public double measure(double[] ts1, double[] ts2) {
/** Build a point-to-point distance matrix */
double[][] dP2P = new double[ts1.length][ts2.length];
for (int i = 0; i < ts1.length; i++) {
for (int j = 0; j < ts2.length; j++) {
dP2P[i][j] = (ts1[i]-ts2[j])*(ts1[i]-ts2[j]);
}
}
/** Check for some special cases due to ultra short time series */
if (ts1.length == 0 || ts2.length == 0) {
return Double.NaN;
}
if (ts1.length == 1 && ts2.length == 1) {
return dP2P[0][0];
}
/**
* Build the optimal distance matrix using a dynamic programming
* approach
*/
double[][] D = new double[ts1.length][ts2.length];
D[0][0] = dP2P[0][0]; // Starting point
for (int i = 1; i < ts1.length; i++) { // Fill the first column of our
// distance matrix with optimal
// values
D[i][0] = dP2P[i][0] + D[i - 1][0];
}
if (ts2.length == 1) { // TS2 is a point
double sum = 0;
for (int i = 0; i < ts1.length; i++) {
sum += D[i][0];
}
return sum;
}
for (int j = 1; j < ts2.length; j++) { // Fill the first row of our
// distance matrix with optimal
// values
D[0][j] = dP2P[0][j] + D[0][j - 1];
}
if (ts1.length == 1) { // TS1 is a point
double sum = 0;
for (int j = 0; j < ts2.length; j++) {
sum += D[0][j];
}
return sum;
}
System.out.println(" DTW Distances =");
for (int i = 1; i < ts1.length; i++) { // Fill the rest
System.out.print(" ROW "+i+" =");
for (int j = 1; j < ts2.length; j++) {
double[] steps = { D[i - 1][j - 1], D[i - 1][j], D[i][j - 1] };
double min = Math.min(steps[0], Math.min(steps[1], steps[2]));
D[i][j] = dP2P[i][j] + min;
System.out.print(D[i][j]+" ");
}
System.out.print("\n");
}
/**
* Calculate the distance between the two time series through optimal
* alignment.
*/
//This step is surely just wrong!!
int i = ts1.length - 1;
int j = ts2.length - 1;
int k = 1;
double dist = D[i][j];
while (i + j > 2) {
if (i == 0) {
j--;
} else if (j == 0) {
i--;
} else {
double[] steps = { D[i - 1][j - 1], D[i - 1][j], D[i][j - 1] };
double min = Math.min(steps[0], Math.min(steps[1], steps[2]));
if (min == steps[0]) {
i--;
j--;
} else if (min == steps[1]) {
i--;
} else if (min == steps[2]) {
j--;
}
}
k++;
dist += D[i][j];
}
return dist;
}
/* OLD IMPLEMENTATION
double r=0.05; //Warping window size percentage, between 0 and 1
protected DynamicTimeWarping(){}
public DynamicTimeWarping(double r){this.r=r;}
public double distance(Complex[] a,Complex[] b){
double dist=0, minDist;
// Set the longest series to a
Complex[] temp;
if(a.length<b.length)
{
temp=a;
a=b;
b=temp;
}
int n=a.length;
int m=b.length;
//Use this to make it the same as Euclidean when this.r=0
int r = (int)(Math.floor(this.r*n)+1);
//Set all to max
double[][] matrixD = new double[n][m];
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
matrixD[i][j]=Double.MAX_VALUE;
matrixD[0][0]=Complex.distance(a[0],b[0]);
//Base cases for warping 0 to all
//Warp a[0] onto all b[1]...b[r+1]
for(int j=1;j<=r && j<n;j++)
matrixD[0][j]=matrixD[0][j-1]+Complex.distance(a[0],b[j]);
// Warp b[0] onto all a[1]...a[r+1]
for(int i=1;i<=r && i<n;i++)
matrixD[i][0]=matrixD[i-1][0]+Complex.distance(a[i],b[0]);
//Warp the rest,
int start,end;
for (int i=1;i<n;i++)
{
if(i-r<1)
start=1;
else
start=i-r;
if(i+r>m)
end=m;
else
end=i+r;
for (int j = start;j<end;j++)
{
//Find the min of matrixD[i][j-1],matrixD[i-1][j] and matrixD[i-1][j-1]
minDist=matrixD[i][j-1];
if(matrixD[i-1][j]<minDist)
minDist=matrixD[i-1][j];
if(matrixD[i-1][j-1]<minDist)
minDist=matrixD[i-1][j-1];
matrixD[i][j]=minDist+Complex.distance(a[i],b[j]);
}
}
//Find the minimum distance
dist=matrixD[n-1][0];
for(int j=1;j<m;j++)
if(matrixD[n-1][j]<dist)
dist=matrixD[n-1][j];
for(int i=0;i<n;i++)
if(matrixD[i][m-1]<dist)
dist=matrixD[i][m-1];
return dist;
}
public double distance(DataPoint dp1,DataPoint dp2){
double[] a = dp1.getData();
double[] b= dp2.getData();
return distance(a,b);
}
public double distance(ComplexDataPoint dp1,ComplexDataPoint dp2){
double[] a1 = dp1.getReal();
double[] a2 = dp1.getImaginary();
double[] b1= dp2.getReal();
double[] b2= dp2.getImaginary();
Complex[] a = new Complex[a1.length];
for(int i=0;i<a.length;i++)
a[i]=new Complex((float)a1[i],(float)a2[i]);
Complex[] b = new Complex[b1.length];
for(int i=0;i<b.length;i++)
b[i]=new Complex((float)b1[i],(float)b2[i]);
return distance(a,b);
}
public double distance(double[] a,double[] b){
double dist=0, minDist;
// Set the longest series to a
double[] temp;
if(a.length<b.length)
{
temp=a;
a=b;
b=temp;
}
int n=a.length;
int m=b.length;
int r = (int)(Math.floor(this.r*n)+1);
// int r = (int)Math.ceil(this.r*n);
// System.out.println(" r = "+r);
//Set all to max
double[][] matrixD = new double[n][m];
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
matrixD[i][j]=Double.MAX_VALUE;
matrixD[0][0]=(a[0]-b[0])*(a[0]-b[0]);
//Base cases for warping 0 to all
//Warp a[0] onto all b[1]...b[r+1]
for(int j=1;j<=r && j<n;j++)
matrixD[0][j]=matrixD[0][j-1]+(a[0]-b[j])*(a[0]-b[j]);
// Warp b[0] onto all a[1]...a[r+1]
for(int i=1;i<=r && i<n;i++)
matrixD[i][0]=matrixD[i-1][0]+(a[i]-b[0])*(a[i]-b[0]);
//Warp the rest,
int start,end;
for (int i=1;i<n;i++)
{
if(i-r<1)
start=1;
else
start=i-r;
if(i+r>m)
end=m;
else
end=i+r;
for (int j = start;j<end;j++)
{
//Find the min of matrixD[i][j-1],matrixD[i-1][j] and matrixD[i-1][j-1]
minDist=matrixD[i][j-1];
if(matrixD[i-1][j]<minDist)
minDist=matrixD[i-1][j];
if(matrixD[i-1][j-1]<minDist)
minDist=matrixD[i-1][j-1];
matrixD[i][j]=minDist+(a[i]-b[j])*(a[i]-b[j]);
}
}
//Find the minimum distance
dist=matrixD[n-1][0];
for(int j=1;j<m;j++)
if(matrixD[n-1][j]<dist)
dist=matrixD[n-1][j];
for(int i=0;i<n;i++)
if(matrixD[i][m-1]<dist)
dist=matrixD[i][m-1];
return dist;
}
public double distance(short[] a,short[] b){
short dist=0, minDist;
// Set the longest series to a
short[] temp;
if(a.length<b.length)
{
temp=a;
a=b;
b=temp;
}
int n=a.length;
int m=b.length;
int r = (int)(Math.floor(this.r*n)+1);
// int r = (int)Math.ceil(this.r*n);
// System.out.println(" r = "+r);
//Set all to max
short[][] matrixD = new short[n][m];
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
matrixD[i][j]=Short.MAX_VALUE;
matrixD[0][0]=(short)((a[0]-b[0])*(a[0]-b[0]));
//Base cases for warping 0 to all
//Warp a[0] onto all b[1]...b[r+1]
for(int j=1;j<=r && j<n;j++)
matrixD[0][j]=(short)(matrixD[0][j-1]+(short)((a[0]-b[j])*(a[0]-b[j])));
// Warp b[0] onto all a[1]...a[r+1]
for(int i=1;i<=r && i<n;i++)
matrixD[i][0]=(short)(matrixD[i-1][0]+(short)((a[i]-b[0])*(a[i]-b[0])));
//Warp the rest,
int start,end;
for (int i=1;i<n;i++)
{
if(i-r<1)
start=1;
else
start=i-r;
if(i+r>m)
end=m;
else
end=i+r;
for (int j = start;j<end;j++)
{
//Find the min of matrixD[i][j-1],matrixD[i-1][j] and matrixD[i-1][j-1]
minDist=matrixD[i][j-1];
if(matrixD[i-1][j]<minDist)
minDist=matrixD[i-1][j];
if(matrixD[i-1][j-1]<minDist)
minDist=matrixD[i-1][j-1];
matrixD[i][j]=(short)(minDist+(a[i]-b[j])*(a[i]-b[j]));
}
}
//Find the minimum distance
dist=matrixD[n-1][0];
for(int j=1;j<m;j++)
if(matrixD[n-1][j]<dist)
dist=matrixD[n-1][j];
for(int i=0;i<n;i++)
if(matrixD[i][m-1]<dist)
dist=matrixD[i][m-1];
return dist;
}
public static void main(String[] args)
{
System.out.println(" Very basic test for DTW distance");
double[] a ={1,2,3,4,5,6,7,8};
double[] b ={2,3,4,5,6,7,8,9};
for(int i=0;i<a.length;i++)
System.out.print(a[i]+",");
System.out.println("\n************");
for(int i=0;i<b.length;i++)
System.out.print(b[i]+",");
System.out.println("\n Euclidean distance is 8, DTW should be 0??");
DynamicTimeWarping dtw= new DynamicTimeWarping(0.05);
System.out.println(" DTW Distance ="+dtw.distance(a,b));
}
*/
}