Package de.lmu.ifi.dbs.elki.data.synthetic.bymodel

Source Code of de.lmu.ifi.dbs.elki.data.synthetic.bymodel.GeneratorSingleCluster

package de.lmu.ifi.dbs.elki.data.synthetic.bymodel;

/*
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/>.
*/

import java.util.LinkedList;
import java.util.Random;

import de.lmu.ifi.dbs.elki.data.synthetic.bymodel.distribution.Distribution;
import de.lmu.ifi.dbs.elki.math.linearalgebra.AffineTransformation;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException;

/**
* Class to generate a single cluster according to a model as well as getting
* the density of a given model at that point (to evaluate generated points
* according to the same model)
*
* @author Erich Schubert
*
* @apiviz.composedOf Distribution
* @apiviz.composedOf AffineTransformation
*/
public class GeneratorSingleCluster implements GeneratorInterfaceDynamic {
  /**
   * The distribution generators for each axis
   */
  private LinkedList<Distribution> axes = new LinkedList<Distribution>();

  /**
   * The transformation matrix
   */
  private AffineTransformation trans;

  /**
   * The dimensionality
   */
  private int dim;

  /**
   * Clipping vectors. Note: currently, either both or none have to be set!
   */
  private Vector clipmin;

  private Vector clipmax;

  /**
   * Correction factor for probability computation
   */
  private double densitycorrection = 1.0;

  /**
   * Number of points in the cluster (-> density)
   */
  private int size;

  /**
   * Cluster name
   */
  private String name;

  /**
   * Retry count
   */
  // TODO: make configureable.
  private int retries = 1000;

  /**
   * Discarded count
   */
  private int discarded = 0;

  /**
   * The generated cluster points.
   */
  public LinkedList<Vector> points = new LinkedList<Vector>();

  /**
   * Random generator (used for initializing random generators)
   */
  private Random random;

  /**
   * Generator (without axes)
   *
   * @param name Cluster name
   * @param size Cluster size
   * @param densitycorrection Density correction factor
   * @param random Random number generator
   */
  public GeneratorSingleCluster(String name, int size, double densitycorrection, Random random) {
    super();
    this.size = size;
    this.name = name;
    this.densitycorrection = densitycorrection;
    this.random = random;
  }

  /**
   * Add a new generator to the cluster. No transformations must have been added
   * so far!
   *
   * @param gen Distribution generator
   * @throws UnableToComplyException thrown when no new generators may be added anymore
   */
  public void addGenerator(Distribution gen) throws UnableToComplyException {
    if(trans != null) {
      throw new UnableToComplyException("Generators may no longer be added when transformations have been applied.");
    }
    axes.add(gen);
    dim++;
  }

  /**
   * Apply a rotation to the generator
   *
   * @param axis1 First axis (0 <= axis1 < dim)
   * @param axis2 Second axis (0 <= axis2 < dim)
   * @param angle Angle in Radians
   */
  public void addRotation(int axis1, int axis2, double angle) {
    if(trans == null) {
      trans = new AffineTransformation(dim);
    }
    trans.addRotation(axis1, axis2, angle);
  }

  /**
   * Add a translation to the generator
   *
   * @param v translation vector
   */
  public void addTranslation(Vector v) {
    if(trans == null) {
      trans = new AffineTransformation(dim);
    }
    trans.addTranslation(v);
  }

  /**
   * Set a clipping box. min needs to be smaller than max in each component.
   * Note: Clippings are not 'modified' by translation / rotation /
   * transformation operations.
   *
   * @param min Minimum values for clipping
   * @param max Maximum values for clipping
   * @throws UnableToComplyException thrown when invalid vectors were given.
   */
  public void setClipping(Vector min, Vector max) throws UnableToComplyException {
    // if only one dimension was given, expand to all dimensions.
    if(min.getRowDimensionality() == 1 && max.getRowDimensionality() == 1) {
      if(min.get(0) >= max.get(0)) {
        throw new UnableToComplyException("Clipping range empty.");
      }
      clipmin = new Vector(dim);
      clipmax = new Vector(dim);
      for(int i = 0; i < dim; i++) {
        clipmin.set(i, 0, min.get(0));
        clipmax.set(i, 0, max.get(0));
      }
      return;
    }
    if(dim != min.getRowDimensionality()) {
      throw new UnableToComplyException("Clipping vector dimensionalities do not match: " + dim + " vs. " + min.getRowDimensionality());
    }
    if(dim != max.getRowDimensionality()) {
      throw new UnableToComplyException("Clipping vector dimensionalities do not match: " + dim + " vs. " + max.getRowDimensionality());
    }
    for(int i = 0; i < dim; i++) {
      if(min.get(i) >= max.get(i)) {
        throw new UnableToComplyException("Clipping range empty in dimension " + (i + 1));
      }
    }
    clipmin = min;
    clipmax = max;
  }

  /**
   * Get the cluster dimensionality
   *
   * @return dimensionality
   */
  @Override
  public int getDim() {
    return dim;
  }

  /**
   * Test if a point is to be clipped
   *
   * @param p point
   * @return true if the point is to be clipped
   */
  private boolean testClipping(Vector p) {
    if(clipmin == null || clipmax == null) {
      return false;
    }
    for(int i = 0; i < p.getRowDimensionality(); i++) {
      if(p.get(i) < clipmin.get(i)) {
        return true;
      }
      if(p.get(i) > clipmax.get(i)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Generate the given number of additional points.
   *
   * @see de.lmu.ifi.dbs.elki.data.synthetic.bymodel.GeneratorInterface#generate(int)
   */
  @Override
  public LinkedList<Vector> generate(int count) throws UnableToComplyException {
    LinkedList<Vector> result = new LinkedList<Vector>();
    while(result.size() < count) {
      double[] d = new double[dim];
      int i = 0;
      for(Distribution axis : axes) {
        d[i] = axis.generate();
        i++;
      }
      Vector p = new Vector(d);
      if(trans != null) {
        p = trans.apply(p);
      }
      if(testClipping(p)) {
        retries--;
        if(retries < 0) {
          throw new UnableToComplyException("Maximum retry count in generator exceeded.");
        }
        continue;
      }
      result.add(p);
    }
    return result;
  }

  /**
   * Compute density for cluster model at given vector p-
   *
   * @see de.lmu.ifi.dbs.elki.data.synthetic.bymodel.GeneratorInterface#getDensity(de.lmu.ifi.dbs.elki.math.linearalgebra.Vector)
   */
  @Override
  public double getDensity(Vector p) {
    Vector o = p;
    if(trans != null) {
      o = trans.applyInverse(p);
    }

    double density = 1.0;
    int i = 0;
    for(Distribution axis : axes) {
      density = density * axis.explain(o.get(i));
      i++;
    }
    return density * densitycorrection;
  }

  /**
   * Get axis generators. Used for printing model information
   *
   * @return list of distributions
   */
  public LinkedList<Distribution> getAxes() {
    return axes;
  }

  /**
   * Get transformation
   *
   * @return transformation matrix, may be null.
   */
  public AffineTransformation getTrans() {
    return trans;
  }

  /**
   * Return a copy of the 'clipping minimum' vector.
   *
   * @return vector with lower clipping bounds. May be null.
   */
  public Vector getClipmin() {
    if(clipmin == null) {
      return null;
    }
    return clipmin.copy();
  }

  /**
   * Return a copy of the 'clipping maximum' vector
   *
   * @return vector with upper clipping bounds. May be null.
   */
  public Vector getClipmax() {
    if(clipmax == null) {
      return null;
    }
    return clipmax.copy();
  }

  /**
   * Return the list of points (no copy)
   */
  @Override
  public LinkedList<Vector> getPoints() {
    return points;
  }

  /**
   * Set the list of points in the cluster
   *
   * @param points New list of points in this cluster.
   */
  public void setPoints(LinkedList<Vector> points) {
    this.points = points;
  }

  /**
   * Return the size
   *
   * @return size of this cluster.
   */
  @Override
  public int getSize() {
    return size;
  }

  /**
   * Get cluster name.
   *
   * @return name of this cluster.
   */
  @Override
  public String getName() {
    return name;
  }

  /**
   * Get number of discarded points
   *
   * @return number of discarded points
   */
  @Override
  public int getDiscarded() {
    return discarded;
  }

  /**
   * Increase number of discarded points
   *
   * @param discarded number of points discarded.
   */
  @Override
  public void addDiscarded(int discarded) {
    this.discarded += discarded;
  }

  /**
   * Return number of remaining retries.
   *
   * @return Number of retries left in this cluster.
   */
  @Override
  public int getRetries() {
    return retries;
  }

  /**
   * Return density correction factor
   *
   * @return density correction factor
   */
  public double getDensityCorrection() {
    return densitycorrection;
  }

  /**
   * Set density correction factor.
   *
   * @param densitycorrection new density correction factor.
   */
  public void setDensityCorrection(double densitycorrection) {
    this.densitycorrection = densitycorrection;
  }

  /**
   * Create a new random generator (reproducible)
   *
   * @return new random generator derived from cluster master random.
   */
  public Random getNewRandomGenerator() {
    return new Random(random.nextLong());
  }
}
TOP

Related Classes of de.lmu.ifi.dbs.elki.data.synthetic.bymodel.GeneratorSingleCluster

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.