package de.lmu.ifi.dbs.elki.index.preprocessed.subspaceproj; /* 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 . */ import java.util.ArrayList; import de.lmu.ifi.dbs.elki.data.NumberVector; import de.lmu.ifi.dbs.elki.database.ids.DBIDRef; import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil; import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs; import de.lmu.ifi.dbs.elki.database.query.DistanceDBIDResult; import de.lmu.ifi.dbs.elki.database.query.DistanceResultPair; import de.lmu.ifi.dbs.elki.database.relation.Relation; import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction; import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; import de.lmu.ifi.dbs.elki.logging.Logging; import de.lmu.ifi.dbs.elki.logging.LoggingUtil; import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.LimitEigenPairFilter; import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCAFilteredResult; import de.lmu.ifi.dbs.elki.math.linearalgebra.pca.PCAFilteredRunner; import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil; import de.lmu.ifi.dbs.elki.utilities.documentation.Description; import de.lmu.ifi.dbs.elki.utilities.documentation.Title; import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException; import de.lmu.ifi.dbs.elki.utilities.optionhandling.WrongParameterValueException; import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GlobalParameterConstraint; import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint; import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.LessEqualConstraint; import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterConstraint; import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.ParameterFlagGlobalConstraint; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.ListParameterization; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag; /** * Preprocessor for 4C local dimensionality and locally weighted matrix * assignment to objects of a certain database. * * @author Arthur Zimek * * @apiviz.composedOf PCAFilteredRunner * * @param Vector type * @param Distance type */ @Title("4C Preprocessor") @Description("Computes the local dimensionality and locally weighted matrix of objects of a certain database according to the 4C algorithm.\n" + "The PCA is based on epsilon range queries.") public class FourCSubspaceIndex, D extends Distance> extends AbstractSubspaceProjectionIndex { /** * Our logger */ private final static Logging logger = Logging.getLogger(FourCSubspaceIndex.class); /** * The Filtered PCA Runner */ private PCAFilteredRunner pca; /** * Full constructor. * * @param relation Relation * @param epsilon Epsilon value * @param rangeQueryDistanceFunction * @param minpts MinPts value * @param pca PCA runner */ public FourCSubspaceIndex(Relation relation, D epsilon, DistanceFunction rangeQueryDistanceFunction, int minpts, PCAFilteredRunner pca) { super(relation, epsilon, rangeQueryDistanceFunction, minpts); this.pca = pca; } @Override protected PCAFilteredResult computeProjection(DBIDRef id, DistanceDBIDResult neighbors, Relation database) { ModifiableDBIDs ids = DBIDUtil.newArray(neighbors.size()); for(DistanceResultPair neighbor : neighbors) { ids.add(neighbor.getDBID()); } PCAFilteredResult pcares = pca.processIds(ids, database); if(logger.isDebugging()) { StringBuffer msg = new StringBuffer(); msg.append(id).append(" "); //.append(database.getObjectLabelQuery().get(id)); msg.append("\ncorrDim ").append(pcares.getCorrelationDimension()); logger.debugFine(msg.toString()); } return pcares; } @Override protected Logging getLogger() { return logger; } @Override public String getLongName() { return "4C local Subspaces"; } @Override public String getShortName() { return "4C-subspaces"; } /** * Factory class for 4C preprocessors. * * @author Erich Schubert * * @apiviz.stereotype factory * @apiviz.uses FourCSubspaceIndex oneway - - «creates» * * @param Vector type * @param Distance type */ public static class Factory, D extends Distance> extends AbstractSubspaceProjectionIndex.Factory> { /** * The default value for delta. */ public static final double DEFAULT_DELTA = LimitEigenPairFilter.DEFAULT_DELTA; /** * The Filtered PCA Runner */ private PCAFilteredRunner pca; /** * Constructor. * * @param epsilon * @param rangeQueryDistanceFunction * @param minpts * @param pca */ public Factory(D epsilon, DistanceFunction rangeQueryDistanceFunction, int minpts, PCAFilteredRunner pca) { super(epsilon, rangeQueryDistanceFunction, minpts); this.pca = pca; } @Override public FourCSubspaceIndex instantiate(Relation relation) { return new FourCSubspaceIndex(relation, epsilon, rangeQueryDistanceFunction, minpts, pca); } /** * Parameterization class. * * @author Erich Schubert * * @apiviz.exclude */ public static class Parameterizer, D extends Distance> extends AbstractSubspaceProjectionIndex.Factory.Parameterizer> { /** * The Filtered PCA Runner */ private PCAFilteredRunner pca; @Override protected void makeOptions(Parameterization config) { super.makeOptions(config); // flag absolute boolean absolute = false; Flag absoluteF = new Flag(LimitEigenPairFilter.EIGENPAIR_FILTER_ABSOLUTE); if(config.grab(absoluteF)) { absolute = absoluteF.getValue(); } // Parameter delta double delta = 0.0; DoubleParameter deltaP = new DoubleParameter(LimitEigenPairFilter.EIGENPAIR_FILTER_DELTA, new GreaterEqualConstraint(0), DEFAULT_DELTA); if(config.grab(deltaP)) { delta = deltaP.getValue(); } // Absolute flag doesn't have a sensible default value for delta. if(absolute && deltaP.tookDefaultValue()) { config.reportError(new WrongParameterValueException("Illegal parameter setting: " + "Flag " + absoluteF.getName() + " is set, " + "but no value for " + deltaP.getName() + " is specified.")); } // if (optionHandler.isSet(DELTA_P)) { // delta = (Double) optionHandler.getOptionValue(DELTA_P); // try { // if (!absolute && delta < 0 || delta > 1) // throw new WrongParameterValueException(DELTA_P, "delta", DELTA_D); // } catch (NumberFormatException e) { // throw new WrongParameterValueException(DELTA_P, "delta", DELTA_D, e); // } // } else if (!absolute) { // delta = LimitEigenPairFilter.DEFAULT_DELTA; // } else { // throw new WrongParameterValueException("Illegal parameter setting: " // + // "Flag " + ABSOLUTE_F + " is set, " + "but no value for " + DELTA_P + // " is specified."); // } // Parameterize PCA ListParameterization pcaParameters = new ListParameterization(); // eigen pair filter pcaParameters.addParameter(PCAFilteredRunner.PCA_EIGENPAIR_FILTER, LimitEigenPairFilter.class.getName()); // abs if(absolute) { pcaParameters.addFlag(LimitEigenPairFilter.EIGENPAIR_FILTER_ABSOLUTE); } // delta pcaParameters.addParameter(LimitEigenPairFilter.EIGENPAIR_FILTER_DELTA, delta); // big value pcaParameters.addParameter(PCAFilteredRunner.BIG_ID, 50); // small value pcaParameters.addParameter(PCAFilteredRunner.SMALL_ID, 1); Class> cls = ClassGenericsUtil.uglyCastIntoSubclass(PCAFilteredRunner.class); pca = pcaParameters.tryInstantiate(cls); for(ParameterException e : pcaParameters.getErrors()) { LoggingUtil.warning("Error in internal parameterization: " + e.getMessage()); } final ArrayList> deltaCons = new ArrayList>(); // TODO: this constraint is already set in the parameter itself, since // it // also applies to the relative case, right? -- erich // deltaCons.add(new GreaterEqualConstraint(0)); deltaCons.add(new LessEqualConstraint(1)); GlobalParameterConstraint gpc = new ParameterFlagGlobalConstraint(deltaP, deltaCons, absoluteF, false); config.checkConstraint(gpc); } @Override protected Factory makeInstance() { return new Factory(epsilon, rangeQueryDistanceFunction, minpts, pca); } } } }