/* * Copyright 2004-2010 Information & Software Engineering Group (188/1) * Institute of Software Technology and Interactive Systems * Vienna University of Technology, Austria * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.ifs.tuwien.ac.at/dm/somtoolbox/license.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package at.tuwien.ifs.somtoolbox.layers.metrics; import java.util.logging.Logger; import cern.colt.matrix.DoubleMatrix1D; import at.tuwien.ifs.somtoolbox.SOMToolboxException; import at.tuwien.ifs.somtoolbox.data.InputDatum; import at.tuwien.ifs.somtoolbox.util.VectorTools; /** * Implements a static method for metric instantiation and a method for mean vector calculation. Furthermore, implements * the convenience methods to call the abstract method having two double arrays as arguments (see * {@link #distance(double[], double[])}). This method has to be implemented by classes actually implementing a certain * metric. * * @author Michael Dittenbach * @author Rudolf Mayer * @version $Id: AbstractMetric.java 3883 2010-11-02 17:13:23Z frank $ */ public abstract class AbstractMetric implements DistanceMetric { public static final String PACKAGE_NAME = AbstractMetric.class.getPackage().getName() + "."; /** * Instantiates a certain distance metric class specified by argument <code>mName</code>.<br/> * Note: for backwards compatibility, if the metric name contains the package <code>prefix at.ec3.somtoolbox</code>, * this will be replaced by <code>at.tuwien.ifs.somtoolbox</code>. * * @param mName the name of the metric. * @return a distance metric object of class <code>mName</code>. * @throws ClassNotFoundException if class denoted by argument <code>mName</code> is not found. * @throws InstantiationException if if this Class represents an abstract class, an interface, an array class, a * primitive type, or void; or if the class has no nullary constructor; or if the instantiation fails * for some other reason. * @throws IllegalAccessException if the class or its nullary constructor is not accessible. */ public static DistanceMetric instantiate(String mName) throws ClassNotFoundException, InstantiationException, IllegalAccessException { if (mName.startsWith("at.ec3.somtoolbox")) { mName = mName.replace("at.ec3.somtoolbox", "at.tuwien.ifs.somtoolbox"); } return (DistanceMetric) Class.forName(mName).newInstance(); } /** * Same as {@link #instantiate(String)}, but tries to get the metric with the specified name, and then with the * package prefix, and throwing only a {@link SOMToolboxException} with the root cause nested. */ public static DistanceMetric instantiateNice(String metricName) throws SOMToolboxException { try { return AbstractMetric.instantiate(metricName); } catch (Exception e) { try { Logger.getLogger("at.tuwien.ifs.somtoolbox").info( "Error instantiating metric: " + e.getMessage() + ", trying for metric class " + PACKAGE_NAME + metricName); return AbstractMetric.instantiate(PACKAGE_NAME + metricName); } catch (Exception e2) { throw new SOMToolboxException("Error instantiating metric: " + e.getMessage(), e2); } } } /** * Calculates the mean vector of two double array vectors. * * @param vector1 first vector. * @param vector2 second vector. * @return the mean vector. * @throws MetricException if the dimensionalities of the two vectors differ. */ public static double[] meanVector(double[] vector1, double[] vector2) throws MetricException { if (vector1.length != vector2.length) { throw new MetricException( "Oops ... tried to calculate the mean vector of two vectors with different dimensionalities."); } // initialize mean vector double[] meanVector = new double[vector1.length]; for (int ve = 0; ve < vector1.length; ve++) { // calculating mean vector meanVector[ve] = (vector1[ve] + vector2[ve]) / 2; } return meanVector; } /** Can be used to do some performance testing to compare colt vs. direct distance implementations. */ protected static void performanceTest(DistanceMetric metric, int dim) { try { System.out.println("Metric: " + metric); DoubleMatrix1D mat1 = VectorTools.generateRandomDoubleMatrix1D(dim); DoubleMatrix1D mat2 = VectorTools.generateRandomDoubleMatrix1D(dim); double[] vec1 = mat1.toArray(); double[] vec2 = mat2.toArray(); long startColt = System.currentTimeMillis(); double distanceColt = metric.distance(mat1, mat2); System.out.println("result colt: " + distanceColt); long endColt = System.currentTimeMillis(); long durationColt = endColt - startColt; long startDirect = System.currentTimeMillis(); double distanceDirect = metric.distance(vec1, vec2); System.out.println("result direct: " + distanceDirect); System.out.println("equal? " + (distanceColt == distanceDirect) + ", difference: " + Math.abs(distanceColt - distanceDirect)); long endDirect = System.currentTimeMillis(); long durationDirect = endDirect - startDirect; System.out.println("duration colt: " + durationColt); System.out.println("duration direct: " + durationDirect); } catch (MetricException e) { e.printStackTrace(); } } /** * @see at.tuwien.ifs.somtoolbox.layers.metrics.DistanceMetric#distance(double[], double[]) */ @Override public abstract double distance(double[] vector1, double[] vector2) throws MetricException; /** * @see at.tuwien.ifs.somtoolbox.layers.metrics.DistanceMetric#distance(double[], cern.colt.matrix.DoubleMatrix1D) */ @Override public double distance(double[] vector1, DoubleMatrix1D vector2) throws MetricException { return distance(vector1, vector2.toArray()); } /** * @see at.tuwien.ifs.somtoolbox.layers.metrics.DistanceMetric#distance(double[], * at.tuwien.ifs.somtoolbox.data.InputDatum) */ @Override public double distance(double[] vector, InputDatum data) throws MetricException { return distance(vector, data.getVector()); } /** * @see at.tuwien.ifs.somtoolbox.layers.metrics.DistanceMetric#distance(cern.colt.matrix.DoubleMatrix1D, double[]) */ @Override public double distance(DoubleMatrix1D vector1, double[] vector2) throws MetricException { return distance(vector1.toArray(), vector2); } /** * @see at.tuwien.ifs.somtoolbox.layers.metrics.DistanceMetric#distance(cern.colt.matrix.DoubleMatrix1D, * cern.colt.matrix.DoubleMatrix1D) */ @Override public double distance(DoubleMatrix1D vector1, DoubleMatrix1D vector2) throws MetricException { return distance(vector1.toArray(), vector2.toArray()); } /** * @see at.tuwien.ifs.somtoolbox.layers.metrics.DistanceMetric#distance(cern.colt.matrix.DoubleMatrix1D, * at.tuwien.ifs.somtoolbox.data.InputDatum) */ @Override public double distance(DoubleMatrix1D vector, InputDatum datum) throws MetricException { return distance(vector, datum.getVector()); } /** * @see at.tuwien.ifs.somtoolbox.layers.metrics.DistanceMetric#distance(at.tuwien.ifs.somtoolbox.data.InputDatum, * double[]) */ @Override public double distance(InputDatum data, double[] vector) throws MetricException { return distance(data.getVector(), vector); } /** * @see at.tuwien.ifs.somtoolbox.layers.metrics.DistanceMetric#distance(at.tuwien.ifs.somtoolbox.data.InputDatum, * cern.colt.matrix.DoubleMatrix1D) */ @Override public double distance(InputDatum datum, DoubleMatrix1D vector) throws MetricException { return distance(datum.getVector(), vector); } /** * @see at.tuwien.ifs.somtoolbox.layers.metrics.DistanceMetric#distance(at.tuwien.ifs.somtoolbox.data.InputDatum, * at.tuwien.ifs.somtoolbox.data.InputDatum) */ @Override public double distance(InputDatum datum, InputDatum datum2) throws MetricException { return distance(datum.getVector(), datum2.getVector()); } /** * Performs a check on wether the given vectors have the same dimension. * * @throws MetricException If the given vectors have different dimensions. */ protected void checkDimensions(double[] vector1, double[] vector2) throws MetricException { if (vector1.length != vector2.length) { throw new MetricException( "Tried to calculate distance between two vectors with different dimensionalities."); } } /** * Performs a check on wether the given vectors have the same dimension. * * @throws MetricException If the given vectors have different dimensions. */ protected void checkDimensions(DoubleMatrix1D vector1, DoubleMatrix1D vector2) throws MetricException { if (vector1.cardinality() != vector2.cardinality()) { throw new MetricException( "Tried to calculate distance between two vectors with different dimensionalities."); } } @Override public double transformValue(double value) { return value; } @Override public double[] transformVector(double[] vector) { double[] transformed = new double[vector.length]; for (int i = 0; i < transformed.length; i++) { transformed[i] = transformValue(vector[i]); } return transformed; } /** Empty implementation, subclasses needing to set parameters have to override this class. */ @Override public void setMetricParams(String metricParamString) throws SOMToolboxException { } @Override public int compareTo(DistanceMetric o) { return getClass().getSimpleName().compareTo(o.getClass().getSimpleName()); } }