package weka.core.elastic_distance_measures;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
Copyright (C) 2011
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Modified by Jason Lines (j.lines@uea.ac.uk)
*/
import weka.core.Instance;
import weka.core.EuclideanDistance;
import weka.core.Instances;
import weka.core.neighboursearch.PerformanceStats;
public class ERPDistance extends EuclideanDistance {
private double g;
private double bandSize;
public ERPDistance(double g, double bandSize) {
this.g = g;
this.bandSize = bandSize;
}
/**
* Distance method
*
* @param first instance 1
* @param second instance 2
* @param cutOffValue used for early abandon
* @param stats
* @return distance between instances
*/
@Override
public double distance(Instance first, Instance second, double cutOffValue, PerformanceStats stats) {
//Get the double arrays
return distance(first, second, cutOffValue);
}
/**
* distance method that converts instances to arrays of doubles
*
* @param first instance 1
* @param second instance 2
* @param cutOffValue used for early abandon
* @return distance between instances
*/
@Override
public double distance(Instance first, Instance second, double cutOffValue) {
//remove class index from first instance if there is one
int firtClassIndex = first.classIndex();
double[] arr1;
if (firtClassIndex > 0) {
arr1 = new double[first.numAttributes() - 1];
for (int i = 0, j = 0; i < first.numAttributes(); i++) {
if (i != firtClassIndex) {
arr1[j] = first.value(i);
j++;
}
}
} else {
arr1 = first.toDoubleArray();
}
//remove class index from second instance if there is one
int secondClassIndex = second.classIndex();
double[] arr2;
if (secondClassIndex > 0) {
arr2 = new double[second.numAttributes() - 1];
for (int i = 0, j = 0; i < second.numAttributes(); i++) {
if (i != secondClassIndex) {
arr2[j] = second.value(i);
j++;
}
}
} else {
arr2 = second.toDoubleArray();
}
return distance(arr1, arr2, cutOffValue);
}
public double distance(double[] first, double[] second, double cutOffValue) {
// return ERPDistance(first, second);
return ERPDistance(new NumberVector(first), new NumberVector(second));
}
private static class NumberVector{
private double[] values;
public NumberVector(double[] values){
this.values = values;
}
public int getDimensionality(){
return values.length;
}
public double doubleValue(int dimension){
try{
return values[dimension - 1];
}catch(IndexOutOfBoundsException e) {
throw new IllegalArgumentException("Dimension " + dimension + " out of range.");
}
}
}
//public double doubleDistance(NumberVector<?, ?> v1, NumberVector<?, ?> v2) {
public double ERPDistance(NumberVector v1, NumberVector v2) {
// Current and previous columns of the matrix
double[] curr = new double[v2.getDimensionality()];
double[] prev = new double[v2.getDimensionality()];
// size of edit distance band
// bandsize is the maximum allowed distance to the diagonal
// int band = (int) Math.ceil(v2.getDimensionality() * bandSize);
int band = (int) Math.ceil(v2.getDimensionality() * bandSize);
// g parameter for local usage
double gValue = g;
for (int i = 0; i < v1.getDimensionality(); i++) {
// Swap current and prev arrays. We'll just overwrite the new curr.
{
double[] temp = prev;
prev = curr;
curr = temp;
}
int l = i - (band + 1);
if (l < 0) {
l = 0;
}
int r = i + (band + 1);
if (r > (v2.getDimensionality() - 1)) {
r = (v2.getDimensionality() - 1);
}
for (int j = l; j <= r; j++) {
if (Math.abs(i - j) <= band) {
// compute squared distance of feature vectors
double val1 = v1.doubleValue(i + 1);
double val2 = gValue;
double diff = (val1 - val2);
final double d1 = Math.sqrt(diff * diff);
val1 = gValue;
val2 = v2.doubleValue(j + 1);
diff = (val1 - val2);
final double d2 = Math.sqrt(diff * diff);
val1 = v1.doubleValue(i + 1);
val2 = v2.doubleValue(j + 1);
diff = (val1 - val2);
final double d12 = Math.sqrt(diff * diff);
final double dist1 = d1 * d1;
final double dist2 = d2 * d2;
final double dist12 = d12 * d12;
final double cost;
if ((i + j) != 0) {
if ((i == 0) || ((j != 0) && (((prev[j - 1] + dist12) > (curr[j - 1] + dist2)) && ((curr[j - 1] + dist2) < (prev[j] + dist1))))) {
// del
cost = curr[j - 1] + dist2;
} else if ((j == 0) || ((i != 0) && (((prev[j - 1] + dist12) > (prev[j] + dist1)) && ((prev[j] + dist1) < (curr[j - 1] + dist2))))) {
// ins
cost = prev[j] + dist1;
} else {
// match
cost = prev[j - 1] + dist12;
}
} else {
cost = 0;
}
curr[j] = cost;
// steps[i][j] = step;
} else {
curr[j] = Double.POSITIVE_INFINITY; // outside band
}
}
}
return Math.sqrt(curr[v2.getDimensionality() - 1]);
}
// utility functions, useful for cv experiments
public static double stdv_p(Instances input){
double sumx = 0;
double sumx2 = 0;
double[] ins2array;
for(int i = 0; i < input.numInstances(); i++){
ins2array = input.instance(i).toDoubleArray();
for(int j = 0; j < ins2array.length-1; j++){//-1 to avoid classVal
sumx+=ins2array[j];
sumx2+=ins2array[j]*ins2array[j];
}
}
int n = input.numInstances()*(input.numAttributes()-1);
double mean = sumx/n;
return Math.sqrt(sumx2/(n)-mean*mean);
}
public static int[] getInclusive10(int min, int max){
int[] output = new int[10];
double diff = (double)(max-min)/9;
double[] doubleOut = new double[10];
doubleOut[0] = min;
output[0] = min;
for(int i = 1; i < 9; i++){
doubleOut[i] = doubleOut[i-1]+diff;
output[i] = (int)Math.round(doubleOut[i]);
}
output[9] = max; // to make sure max isn't omitted due to double imprecision
return output;
}
public static double[] getInclusive10(double min, double max){
double[] output = new double[10];
double diff = (double)(max-min)/9;
output[0] = min;
for(int i = 1; i < 9; i++){
output[i] = output[i-1]+diff;
}
output[9] = max;
return output;
}
public static void main(String[] args){
ERPDistance erp = new ERPDistance(0.5,0.5);
double[] one = {1,2,3,4,5,6,7,8,9,10};
double[] two = {1,2,3,4,5,6,7,8,9,10};
// double[] two = {2,3,4,5,6,7,8,9,10,11};
System.out.println(erp.distance(one, two,0));
}
}