<commit_before>package de.lmu.ifi.dbs.distance.distancefunction;

import de.lmu.ifi.dbs.data.RealVector;
import de.lmu.ifi.dbs.database.AssociationID;
import de.lmu.ifi.dbs.distance.DoubleDistance;
import de.lmu.ifi.dbs.index.spatial.SpatialDistanceFunction;
import de.lmu.ifi.dbs.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.utilities.HyperBoundingBox;

/**
 * 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 <NAME> (<a
 *         href="mailto:<EMAIL>"><EMAIL></a>)
 */
public class LocallyWeightedDistanceFunction<O extends RealVector> extends AbstractLocallyWeightedDistanceFunction<O> implements SpatialDistanceFunction<O, DoubleDistance> {
  static {
    ASSOCIATION_ID = AssociationID.LOCALLY_WEIGHTED_MATRIX;
  }

  /**
   * Provides a locally weighted distance function.
   */
  public LocallyWeightedDistanceFunction() {
    super();
  }

  /**
   * Computes the distance between two given real vectors according to this
   * distance function.
   *
   * @param o1 first RealVector
   * @param o2 second RealVector
   * @return the distance between two given real vectors according to this
   *         distance function
   */
  public DoubleDistance distance(O o1, O o2) {
    Matrix m1 = (Matrix) getDatabase().getAssociation(AssociationID.LOCALLY_WEIGHTED_MATRIX, o1.getID());
    Matrix m2 = (Matrix) getDatabase().getAssociation(AssociationID.LOCALLY_WEIGHTED_MATRIX, o2.getID());

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

    //noinspection unchecked
    Matrix rv1Mrv2 = o1.plus(o2.negativeVector()).getColumnVector();
    //noinspection unchecked
    Matrix rv2Mrv1 = o2.plus(o1.negativeVector()).getColumnVector();

    double dist1 = rv1Mrv2.transpose().times(m1).times(rv1Mrv2).get(0, 0);
    double dist2 = rv2Mrv1.transpose().times(m2).times(rv2Mrv1).get(0, 0);

    // todo: abfragen auf kleine werte nahe null
    if (dist1 < 0 || dist2 < 0) throw new IllegalArgumentException();
    return new DoubleDistance(Math.max(Math.sqrt(dist1), Math.sqrt(dist2)));
  }


  /**
   * Computes the minimum distance between the given MBR and the RealVector
   * object according to this distance function.
   *
   * @param mbr the MBR object
   * @param o   the FeatureVector object
   * @return the minimum distance between the given MBR and the SpatialData
   *         object according to this distance function
   * @see de.lmu.ifi.dbs.index.spatial.SpatialDistanceFunction#minDist(de.lmu.ifi.dbs.utilities.HyperBoundingBox, de.lmu.ifi.dbs.data.NumberVector)
   */<commit_msg>handling of small negative values<commit_after>package de.lmu.ifi.dbs.distance.distancefunction;

import de.lmu.ifi.dbs.data.RealVector;
import de.lmu.ifi.dbs.database.AssociationID;
import de.lmu.ifi.dbs.distance.DoubleDistance;
import de.lmu.ifi.dbs.index.spatial.SpatialDistanceFunction;
import de.lmu.ifi.dbs.math.linearalgebra.Matrix;
import de.lmu.ifi.dbs.utilities.HyperBoundingBox;

/**
 * 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 <NAME> (<a
 *         href="mailto:<EMAIL>"><EMAIL></a>)
 */
public class LocallyWeightedDistanceFunction<O extends RealVector> extends AbstractLocallyWeightedDistanceFunction<O> implements SpatialDistanceFunction<O, DoubleDistance> {
  static {
    ASSOCIATION_ID = AssociationID.LOCALLY_WEIGHTED_MATRIX;
  }

  /**
   * Provides a locally weighted distance function.
   */
  public LocallyWeightedDistanceFunction() {
    super();
  }

  /**
   * Computes the distance between two given real vectors according to this
   * distance function.
   *
   * @param o1 first RealVector
   * @param o2 second RealVector
   * @return the distance between two given real vectors according to this
   *         distance function
   */
  public DoubleDistance distance(O o1, O o2) {
    Matrix m1 = (Matrix) getDatabase().getAssociation(AssociationID.LOCALLY_WEIGHTED_MATRIX, o1.getID());
    Matrix m2 = (Matrix) getDatabase().getAssociation(AssociationID.LOCALLY_WEIGHTED_MATRIX, o2.getID());

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

    //noinspection unchecked
    Matrix rv1Mrv2 = o1.plus(o2.negativeVector()).getColumnVector();
    //noinspection unchecked
    Matrix rv2Mrv1 = o2.plus(o1.negativeVector()).getColumnVector();

    double dist1 = rv1Mrv2.transpose().times(m1).times(rv1Mrv2).get(0, 0);
    double dist2 = rv2Mrv1.transpose().times(m2).times(rv2Mrv1).get(0, 0);

    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)));
  }


  /**
   * Computes the minimum distance between the given MBR and the RealVector
   * object according to this distance function.
   *
   * @param mbr the MBR object
   * @param o   the FeatureVector object
   * @return the minimum distance between the given MBR and the SpatialData
   *         object according to this distance function
   * @see de.lmu.ifi.dbs.index.spatial.SpatialDistanceFunction#minDist(de.lmu.ifi.dbs.utilities.HyperBoundingBox, de.lmu.ifi.dbs.data.FeatureVector)
   */
{'fraction_non_alphanumeric': 0.12637269809089374, 'fraction_numerical': 0.019935799966210508, 'mean_word_length': 3.912863070539419, 'pattern_counts': {'":': 0, '<': 63, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3526695', 'n_tokens_mistral': 2042, 'n_tokens_neox': 1949, 'n_words': 449}