Package de.lmu.ifi.dbs.elki.distance.distancefunction

Source Code of de.lmu.ifi.dbs.elki.distance.distancefunction.LocallyWeightedDistanceFunction$Instance

package de.lmu.ifi.dbs.elki.distance.distancefunction;

/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures

Copyright (C) 2012
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/>.
*/

import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.spatial.SpatialComparable;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
import de.lmu.ifi.dbs.elki.index.preprocessed.LocalProjectionIndex;
import de.lmu.ifi.dbs.elki.index.preprocessed.localpca.FilteredLocalPCAIndex;
import de.lmu.ifi.dbs.elki.index.preprocessed.localpca.KNNQueryFilteredPCAIndex;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;

/**
* Provides a locally weighted distance function. Computes the quadratic form
* distance between two vectors P and Q as follows:
*
* result = max{dist<sub>P</sub>(P,Q), dist<sub>Q</sub>(Q,P)} where
* dist<sub>X</sub>(X,Y) = (X-Y)*<b>M<sub>X</sub></b>*(X-Y)<b><sup>T</sup></b>
* and <b>M<sub>X</sub></b> is the weight matrix of vector X.
*
* @author Arthur Zimek
* @param <V> the type of NumberVector to compute the distances in between
*/
// FIXME: implements SpatialPrimitiveDistanceFunction<V, DoubleDistance>
public class LocallyWeightedDistanceFunction<V extends NumberVector<?, ?>> extends AbstractIndexBasedDistanceFunction<V, FilteredLocalPCAIndex<V>, DoubleDistance> implements FilteredLocalPCABasedDistanceFunction<V, FilteredLocalPCAIndex<V>, DoubleDistance> {
  /**
   * Constructor
   *
   * @param indexFactory Index factory
   */
  public LocallyWeightedDistanceFunction(LocalProjectionIndex.Factory<V, FilteredLocalPCAIndex<V>> indexFactory) {
    super(indexFactory);
  }

  @Override
  public DoubleDistance getDistanceFactory() {
    return DoubleDistance.FACTORY;
  }

  @Override
  public boolean isMetric() {
    return false;
  }

  @Override
  public boolean isSymmetric() {
    return true;
  }

  @Override
  public <T extends V> Instance<T> instantiate(Relation<T> database) {
    // We can't really avoid these warnings, due to a limitation in Java
    // Generics (AFAICT)
    @SuppressWarnings("unchecked")
    LocalProjectionIndex<T, ?> indexinst = (LocalProjectionIndex<T, ?>) indexFactory.instantiate((Relation<V>) database);
    return new Instance<T>(database, indexinst, this);
  }

  @Override
  public boolean equals(Object obj) {
    if(obj == null) {
      return false;
    }
    if(!this.getClass().equals(obj.getClass())) {
      return false;
    }
    if(this.indexFactory.equals(((LocallyWeightedDistanceFunction<?>) obj).indexFactory)) {
      return false;
    }
    return true;
  }

  /**
   * Instance of this distance for a particular database.
   *
   * @author Erich Schubert
   */
  public static class Instance<V extends NumberVector<?, ?>> extends AbstractIndexBasedDistanceFunction.Instance<V, LocalProjectionIndex<V, ?>, DoubleDistance, LocallyWeightedDistanceFunction<? super V>> implements FilteredLocalPCABasedDistanceFunction.Instance<V, LocalProjectionIndex<V, ?>, DoubleDistance> {
    /**
     * Constructor.
     *
     * @param database Database
     * @param index Index
     * @param distanceFunction Distance Function
     */
    public Instance(Relation<V> database, LocalProjectionIndex<V, ?> index, LocallyWeightedDistanceFunction<? super V> distanceFunction) {
      super(database, index, distanceFunction);
    }

    /**
     * Computes the distance between two given real vectors according to this
     * distance function.
     *
     * @param id1 first object id
     * @param id2 second object id
     * @return the distance between two given real vectors according to this
     *         distance function
     */
    @Override
    public DoubleDistance distance(DBID id1, DBID id2) {
      Matrix m1 = index.getLocalProjection(id1).similarityMatrix();
      Matrix m2 = index.getLocalProjection(id2).similarityMatrix();

      if(m1 == null || m2 == null) {
        return new DoubleDistance(Double.POSITIVE_INFINITY);
      }

      V v1 = relation.get(id1);
      V v2 = relation.get(id2);
      Vector v1Mv2 = v1.getColumnVector().minusEquals(v2.getColumnVector());
      Vector v2Mv1 = v2.getColumnVector().minusEquals(v1.getColumnVector());

      double dist1 = v1Mv2.transposeTimesTimes(m1, v1Mv2);
      double dist2 = v2Mv1.transposeTimesTimes(m2, v2Mv1);

      if(dist1 < 0) {
        if(-dist1 < 0.000000000001) {
          dist1 = 0;
        }
        else {
          throw new IllegalArgumentException("dist1 " + dist1 + "  < 0!");
        }
      }
      if(dist2 < 0) {
        if(-dist2 < 0.000000000001) {
          dist2 = 0;
        }
        else {
          throw new IllegalArgumentException("dist2 " + dist2 + "  < 0!");
        }
      }

      return new DoubleDistance(Math.max(Math.sqrt(dist1), Math.sqrt(dist2)));
    }

    // @Override
    // TODO: re-enable spatial interfaces
    public DoubleDistance minDistBROKEN(SpatialComparable mbr, V v) {
      if(mbr.getDimensionality() != v.getDimensionality()) {
        throw new IllegalArgumentException("Different dimensionality of objects\n  first argument: " + mbr.toString() + "\n  second argument: " + v.toString());
      }

      double[] r = new double[v.getDimensionality()];
      for(int d = 1; d <= v.getDimensionality(); d++) {
        double value = v.doubleValue(d);
        if(value < mbr.getMin(d)) {
          r[d - 1] = mbr.getMin(d);
        }
        else if(value > mbr.getMax(d)) {
          r[d - 1] = mbr.getMax(d);
        }
        else {
          r[d - 1] = value;
        }
      }

      Matrix m = null; // index.getLocalProjection(v.getID()).similarityMatrix();
      Vector rv1Mrv2 = v.getColumnVector().minusEquals(new Vector(r));
      double dist = rv1Mrv2.transposeTimesTimes(m, rv1Mrv2);

      return new DoubleDistance(Math.sqrt(dist));
    }

    // TODO: Remove?
    // @Override
    // public DoubleDistance minDist(SpatialComparable mbr, DBID id) {
    // return minDist(mbr, database.get(id));
    // }

    // @Override
    // TODO: re-enable spatial interface
    public DoubleDistance distance(SpatialComparable mbr1, SpatialComparable mbr2) {
      if(mbr1.getDimensionality() != mbr2.getDimensionality()) {
        throw new IllegalArgumentException("Different dimensionality of objects\n  first argument: " + mbr1.toString() + "\n  second argument: " + mbr2.toString());
      }

      double sqrDist = 0;
      for(int d = 1; d <= mbr1.getDimensionality(); d++) {
        double m1, m2;
        if(mbr1.getMax(d) < mbr2.getMin(d)) {
          m1 = mbr1.getMax(d);
          m2 = mbr2.getMin(d);
        }
        else if(mbr1.getMin(d) > mbr2.getMax(d)) {
          m1 = mbr1.getMin(d);
          m2 = mbr2.getMax(d);
        }
        else { // The mbrs intersect!
          m1 = 0;
          m2 = 0;
        }
        double manhattanI = m1 - m2;
        sqrDist += manhattanI * manhattanI;
      }
      return new DoubleDistance(Math.sqrt(sqrDist));
    }

    // @Override
    // TODO: re-enable spatial interface
    public DoubleDistance centerDistance(SpatialComparable mbr1, SpatialComparable mbr2) {
      if(mbr1.getDimensionality() != mbr2.getDimensionality()) {
        throw new IllegalArgumentException("Different dimensionality of objects\n first argument:  " + mbr1.toString() + "\n  second argument: " + mbr2.toString());
      }

      double sqrDist = 0;
      for(int d = 1; d <= mbr1.getDimensionality(); d++) {
        double c1 = (mbr1.getMin(d) + mbr1.getMax(d)) / 2;
        double c2 = (mbr2.getMin(d) + mbr2.getMax(d)) / 2;

        double manhattanI = c1 - c2;
        sqrDist += manhattanI * manhattanI;
      }
      return new DoubleDistance(Math.sqrt(sqrDist));
    }
  }

  /**
   * Parameterization class.
   *
   * @author Erich Schubert
   *
   * @apiviz.exclude
   */
  public static class Parameterizer<V extends NumberVector<?, ?>> extends AbstractIndexBasedDistanceFunction.Parameterizer<LocalProjectionIndex.Factory<V, FilteredLocalPCAIndex<V>>> {
    @Override
    protected void makeOptions(Parameterization config) {
      super.makeOptions(config);
      configIndexFactory(config, LocalProjectionIndex.Factory.class, KNNQueryFilteredPCAIndex.Factory.class);
    }

    @Override
    protected LocallyWeightedDistanceFunction<V> makeInstance() {
      return new LocallyWeightedDistanceFunction<V>(factory);
    }
  }
}
TOP

Related Classes of de.lmu.ifi.dbs.elki.distance.distancefunction.LocallyWeightedDistanceFunction$Instance

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.