diff options
Diffstat (limited to 'src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants')
76 files changed, 2598 insertions, 2128 deletions
diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTree.java index a7a063bd..61ca9116 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTree.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTree.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -28,73 +28,67 @@ import java.util.Collections; import java.util.List; import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.database.ids.DBIDIter; -import de.lmu.ifi.dbs.elki.database.ids.DBIDs; -import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; -import de.lmu.ifi.dbs.elki.distance.DistanceUtil; +import de.lmu.ifi.dbs.elki.database.ids.DBIDRef; import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.BreadthFirstEnumeration; -import de.lmu.ifi.dbs.elki.index.tree.DistanceEntry; import de.lmu.ifi.dbs.elki.index.tree.IndexTreePath; import de.lmu.ifi.dbs.elki.index.tree.TreeIndexPathComponent; import de.lmu.ifi.dbs.elki.index.tree.metrical.MetricalIndexTree; -import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.split.Assignments; -import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.split.MLBDistSplit; -import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.split.MTreeSplit; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.split.Assignments; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.split.DistanceEntry; +import de.lmu.ifi.dbs.elki.logging.Logging; +import de.lmu.ifi.dbs.elki.logging.statistics.Counter; +import de.lmu.ifi.dbs.elki.logging.statistics.LongStatistic; import de.lmu.ifi.dbs.elki.persistent.PageFile; -import de.lmu.ifi.dbs.elki.persistent.PageFileUtil; +import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleIntPair; /** * Abstract super class for all M-Tree variants. * * @author Elke Achtert * - * @apiviz.has SplitResult oneway - - computes + * @apiviz.composedOf MTreeSettings + * @apiviz.composedOf Statistics * @apiviz.has AbstractMTreeNode oneway - - contains + * @apiviz.excludeSubtypes * * @param <O> the type of DatabaseObject to be stored in the metrical index * @param <D> the type of Distance used in the metrical index * @param <N> the type of MetricalNode used in the metrical index * @param <E> the type of MetricalEntry used in the metrical index + * @param <S> the type to store settings in. */ -public abstract class AbstractMTree<O, D extends Distance<D>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry<D>> extends MetricalIndexTree<O, D, N, E> { +public abstract class AbstractMTree<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry, S extends MTreeSettings<O, D, N, E>> extends MetricalIndexTree<O, D, N, E> { /** * Debugging flag: do extra integrity checks. */ protected static final boolean EXTRA_INTEGRITY_CHECKS = false; /** - * Holds the instance of the trees distance function. + * Tree settings. */ - protected DistanceFunction<O, D> distanceFunction; + protected S settings; /** - * The distance query. + * For counting the number of distance computations. */ - protected DistanceQuery<O, D> distanceQuery; + public Statistics statistics = new Statistics(); /** * Constructor. * * @param pagefile Page file - * @param distanceQuery Distance query - * @param distanceFunction Distance function + * @param settings Tree settings */ - public AbstractMTree(PageFile<N> pagefile, DistanceQuery<O, D> distanceQuery, DistanceFunction<O, D> distanceFunction) { + public AbstractMTree(PageFile<N> pagefile, S settings) { super(pagefile); - this.distanceQuery = distanceQuery; - this.distanceFunction = distanceFunction; + this.settings = settings; } @Override - public final DistanceFunction<O, D> getDistanceFunction() { - return distanceFunction; - } - - @Override - public final DistanceQuery<O, D> getDistanceQuery() { - return distanceQuery; + public final DistanceFunction<? super O, D> getDistanceFunction() { + return settings.distanceFunction; } /** @@ -103,7 +97,7 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract * @return the distance factory used */ public final D getDistanceFactory() { - return distanceFunction.getDistanceFactory(); + return settings.distanceFunction.getDistanceFactory(); } /** @@ -131,7 +125,7 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract } } - BreadthFirstEnumeration<N, E> enumeration = new BreadthFirstEnumeration<N, E>(this, getRootPath()); + BreadthFirstEnumeration<N, E> enumeration = new BreadthFirstEnumeration<>(this, getRootPath()); while (enumeration.hasMoreElements()) { IndexTreePath<E> path = enumeration.nextElement(); E entry = path.getLastPathComponent().getEntry(); @@ -158,7 +152,7 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract result.append(leafNodes).append(" Leaf Nodes \n"); result.append(objects).append(" Objects \n"); - PageFileUtil.appendPageFileStatistics(result, getPageFileStatistics()); + // PageFileUtil.appendPageFileStatistics(result, getPageFileStatistics()); return result.toString(); } @@ -180,14 +174,14 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract } // choose subtree for insertion - IndexTreePath<E> subtree = choosePath(entry, getRootPath()); + IndexTreePath<E> subtree = settings.insertStrategy.choosePath(this, entry); if (getLogger().isDebugging()) { getLogger().debugFine("insertion-subtree " + subtree + "\n"); } // determine parent distance E parentEntry = subtree.getLastPathComponent().getEntry(); - D parentDistance = distance(parentEntry.getRoutingObjectID(), entry.getRoutingObjectID()); + double parentDistance = distance(parentEntry.getRoutingObjectID(), entry.getRoutingObjectID()).doubleValue(); entry.setParentDistance(parentDistance); // create leaf entry and do pre insert @@ -232,63 +226,6 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract } /** - * Chooses the best path of the specified subtree for insertion of the given - * object. - * - * @param object the entry to search - * @param subtree the subtree to be tested for insertion - * @return the path of the appropriate subtree to insert the given object - */ - private IndexTreePath<E> choosePath(E object, IndexTreePath<E> subtree) { - N node = getNode(subtree.getLastPathComponent().getEntry()); - - // leaf - if (node.isLeaf()) { - return subtree; - } - - DistanceEntry<D, E> bestCandidate; - D enlarge; // Track best enlargement - null for no enlargement needed. - // Initialize from first: - { - E entry = node.getEntry(0); - D distance = distance(object.getRoutingObjectID(), entry.getRoutingObjectID()); - bestCandidate = new DistanceEntry<D, E>(entry, distance, 0); - if (distance.compareTo(entry.getCoveringRadius()) <= 0) { - enlarge = null; - } else { - enlarge = distance.minus(entry.getCoveringRadius()); - } - } - - // Iterate over remaining - for (int i = 1; i < node.getNumEntries(); i++) { - E entry = node.getEntry(i); - D distance = distance(object.getRoutingObjectID(), entry.getRoutingObjectID()); - - if (distance.compareTo(entry.getCoveringRadius()) <= 0) { - if (enlarge != null || distance.compareTo(bestCandidate.getDistance()) < 0) { - bestCandidate = new DistanceEntry<D, E>(entry, distance, i); - enlarge = null; - } - } else if (enlarge != null) { - D enlrg = distance.minus(entry.getCoveringRadius()); - if (enlrg.compareTo(enlarge) < 0) { - bestCandidate = new DistanceEntry<D, E>(entry, distance, i); - enlarge = enlrg; - } - } - } - - // Apply enlargement - if (enlarge != null) { - bestCandidate.getEntry().setCoveringRadius(enlarge); - } - - return choosePath(object, subtree.pathByAddingChild(new TreeIndexPathComponent<E>(bestCandidate.getEntry(), bestCandidate.getIndex()))); - } - - /** * Sorts the entries of the specified node according to their minimum distance * to the specified object. * @@ -296,44 +233,16 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract * @param q the id of the object * @return a list of the sorted entries */ - protected final List<DistanceEntry<D, E>> getSortedEntries(N node, DBID q) { - List<DistanceEntry<D, E>> result = new ArrayList<DistanceEntry<D, E>>(); - - for (int i = 0; i < node.getNumEntries(); i++) { - E entry = node.getEntry(i); - D distance = distance(entry.getRoutingObjectID(), q); - D radius = entry.getCoveringRadius(); - D minDist = radius.compareTo(distance) > 0 ? getDistanceFactory().nullDistance() : distance.minus(radius); - - result.add(new DistanceEntry<D, E>(entry, minDist, i)); - } - - Collections.sort(result); - return result; - } - - /** - * Sorts the entries of the specified node according to their minimum distance - * to the specified objects. - * - * @param node the node - * @param ids the ids of the objects - * @return a list of the sorted entries - */ - protected final List<DistanceEntry<D, E>> getSortedEntries(N node, DBIDs ids) { - List<DistanceEntry<D, E>> result = new ArrayList<DistanceEntry<D, E>>(); + protected final List<DoubleIntPair> getSortedEntries(N node, DBID q) { + List<DoubleIntPair> result = new ArrayList<>(); for (int i = 0; i < node.getNumEntries(); i++) { E entry = node.getEntry(i); - D radius = entry.getCoveringRadius(); + double distance = distance(entry.getRoutingObjectID(), q).doubleValue(); + double radius = entry.getCoveringRadius(); + double minDist = (radius > distance) ? 0.0 : distance - radius; - D minMinDist = getDistanceFactory().infiniteDistance(); - for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) { - D distance = distanceQuery.distance(entry.getRoutingObjectID(), iter); - D minDist = radius.compareTo(distance) > 0 ? getDistanceFactory().nullDistance() : distance.minus(radius); - minMinDist = DistanceUtil.min(minMinDist, minDist); - } - result.add(new DistanceEntry<D, E>(entry, minMinDist, i)); + result.add(new DoubleIntPair(minDist, i)); } Collections.sort(result); @@ -347,11 +256,17 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract * @param id2 the second id * @return the distance between the two specified ids */ - protected final D distance(DBID id1, DBID id2) { - if (id1 == null || id2 == null) { - return getDistanceFactory().undefinedDistance(); - } - return distanceQuery.distance(id1, id2); + public abstract D distance(DBIDRef id1, DBIDRef id2); + + /** + * Returns the distance between the routing object of two entries. + * + * @param e1 First entry + * @param e2 Second entry + * @return the distance between the two routing objects + */ + public final D distance(E e1, E e2) { + return distance(e1.getRoutingObjectID(), e2.getRoutingObjectID()); } /** @@ -363,38 +278,7 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract * the routing object of the parent node * @return the newly created directory entry */ - protected abstract E createNewDirectoryEntry(N node, DBID routingObjectID, D parentDistance); - - /** - * Splits the specified node and returns the split result. - * - * @param node the node to be split - * @return the split result - */ - private SplitResult split(N node) { - // do the split - // todo split stratgey - MTreeSplit<O, D, N, E> split = new MLBDistSplit<O, D, N, E>(node, distanceQuery); - Assignments<D, E> assignments = split.getAssignments(); - final N newNode; - if (node.isLeaf()) { - newNode = createNewLeafNode(); - } else { - newNode = createNewDirectoryNode(); - } - node.splitTo(newNode, assignments.getFirstAssignments(), assignments.getSecondAssignments()); - - // write changes to file - writeNode(node); - writeNode(newNode); - - if (getLogger().isDebugging()) { - String msg = "Split Node " + node.getPageID() + " (" + this.getClass() + ")\n" + " newNode " + newNode.getPageID() + "\n" + " firstPromoted " + assignments.getFirstRoutingObject() + "\n" + " firstAssignments(" + node.getPageID() + ") " + assignments.getFirstAssignments() + "\n" + " firstCR " + assignments.getFirstCoveringRadius() + "\n" + " secondPromoted " + assignments.getSecondRoutingObject() + "\n" + " secondAssignments(" + newNode.getPageID() + ") " + assignments.getSecondAssignments() + "\n" + " secondCR " + assignments.getSecondCoveringRadius() + "\n"; - getLogger().debugFine(msg); - } - - return new SplitResult(split, newNode); - } + protected abstract E createNewDirectoryEntry(N node, DBID routingObjectID, double parentDistance); /** * Adjusts the tree after insertion of some nodes. @@ -412,15 +296,39 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract // overflow in node; split the node if (hasOverflow(node)) { - SplitResult splitResult = split(node); - N splitNode = splitResult.newNode; - Assignments<D, E> assignments = splitResult.split.getAssignments(); + // do the split + Assignments<E> assignments = settings.splitStrategy.split(this, node); + final N newNode = node.isLeaf() ? createNewLeafNode() : createNewDirectoryNode(); + + List<E> entries1 = new ArrayList<>(assignments.getFirstAssignments().size()); + List<E> entries2 = new ArrayList<>(assignments.getSecondAssignments().size()); + // Store final parent distances: + for (DistanceEntry<E> ent : assignments.getFirstAssignments()) { + final E e = ent.getEntry(); + e.setParentDistance(ent.getDistance()); + entries1.add(e); + } + for (DistanceEntry<E> ent : assignments.getSecondAssignments()) { + final E e = ent.getEntry(); + e.setParentDistance(ent.getDistance()); + entries2.add(e); + } + node.splitTo(newNode, entries1, entries2); + + // write changes to file + writeNode(node); + writeNode(newNode); + + if (getLogger().isDebugging()) { + String msg = "Split Node " + node.getPageID() + " (" + this.getClass() + ")\n" + " newNode " + newNode.getPageID() + "\n" + " firstPromoted " + assignments.getFirstRoutingObject() + "\n" + " firstAssignments(" + node.getPageID() + ") " + assignments.getFirstAssignments() + "\n" + " firstCR " + assignments.getFirstCoveringRadius() + "\n" + " secondPromoted " + assignments.getSecondRoutingObject() + "\n" + " secondAssignments(" + newNode.getPageID() + ") " + assignments.getSecondAssignments() + "\n" + " secondCR " + assignments.getSecondCoveringRadius() + "\n"; + getLogger().debugFine(msg); + } // if root was split: create a new root that points the two split // nodes if (isRoot(node)) { // FIXME: stimmen die parentDistance der Kinder in node & splitNode? - IndexTreePath<E> newRootPath = createNewRoot(node, splitNode, assignments.getFirstRoutingObject(), assignments.getSecondRoutingObject()); + IndexTreePath<E> newRootPath = createNewRoot(node, newNode, assignments.getFirstRoutingObject(), assignments.getSecondRoutingObject()); adjustTree(newRootPath); } // node is not root @@ -431,13 +339,13 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract if (getLogger().isDebugging()) { getLogger().debugFine("parent " + parent); } - D parentDistance2 = distance(parentEntry.getRoutingObjectID(), assignments.getSecondRoutingObject()); + double parentDistance2 = distance(parentEntry.getRoutingObjectID(), assignments.getSecondRoutingObject()).doubleValue(); // logger.warning("parent: "+parent.toString()+" split: " + // splitNode.toString()+ " dist:"+parentDistance2); - parent.addDirectoryEntry(createNewDirectoryEntry(splitNode, assignments.getSecondRoutingObject(), parentDistance2)); + parent.addDirectoryEntry(createNewDirectoryEntry(newNode, assignments.getSecondRoutingObject(), parentDistance2)); // adjust the entry representing the (old) node, that has been split - D parentDistance1 = distance(parentEntry.getRoutingObjectID(), assignments.getFirstRoutingObject()); + double parentDistance1 = distance(parentEntry.getRoutingObjectID(), assignments.getFirstRoutingObject()).doubleValue(); // logger.warning("parent: "+parent.toString()+" node: " + // node.toString()+ " dist:"+parentDistance1); node.adjustEntry(parent.getEntry(nodeIndex), assignments.getFirstRoutingObject(), parentDistance1, this); @@ -517,8 +425,8 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract // firstRoutingObjectID); // D parentDistance2 = distance(getRootEntry().getRoutingObjectID(), // secondRoutingObjectID); - E oldRootEntry = createNewDirectoryEntry(oldRoot, firstRoutingObjectID, null); - E newRootEntry = createNewDirectoryEntry(newNode, secondRoutingObjectID, null); + E oldRootEntry = createNewDirectoryEntry(oldRoot, firstRoutingObjectID, 0.); + E newRootEntry = createNewDirectoryEntry(newNode, secondRoutingObjectID, 0.); root.addDirectoryEntry(oldRootEntry); root.addDirectoryEntry(newRootEntry); @@ -536,41 +444,13 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract getLogger().debugFine(msg); } - return new IndexTreePath<E>(new TreeIndexPathComponent<E>(getRootEntry(), null)); - } - - /** - * Encapsulates a split object and the newly created node. - * - * @apiviz.composedOf MTreeSplit - */ - private class SplitResult { - /** - * Split used - */ - protected MTreeSplit<O, D, N, E> split; - - /** - * New sibling - */ - protected N newNode; - - /** - * Constructor. - * - * @param split Split that was used - * @param newNode New sibling - */ - public SplitResult(MTreeSplit<O, D, N, E> split, N newNode) { - this.split = split; - this.newNode = newNode; - } + return new IndexTreePath<>(new TreeIndexPathComponent<>(getRootEntry(), null)); } @Override public List<E> getLeaves() { - List<E> result = new ArrayList<E>(); - BreadthFirstEnumeration<N, E> enumeration = new BreadthFirstEnumeration<N, E>(this, getRootPath()); + List<E> result = new ArrayList<>(); + BreadthFirstEnumeration<N, E> enumeration = new BreadthFirstEnumeration<>(this, getRootPath()); while (enumeration.hasMoreElements()) { IndexTreePath<E> path = enumeration.nextElement(); E entry = path.getLastPathComponent().getEntry(); @@ -603,4 +483,93 @@ public abstract class AbstractMTree<O, D extends Distance<D>, N extends Abstract } return levels; } + + @Override + public void logStatistics() { + super.logStatistics(); + Logging log = getLogger(); + if (log.isStatistics()) { + log.statistics(new LongStatistic(this.getClass().getName() + ".height", getHeight())); + statistics.logStatistics(); + } + } + + /** + * Class for tracking some statistics. + * + * @author Erich Schubert + * + * @apiviz.composedOf Counter + */ + public class Statistics { + /** + * For counting the number of distance computations. + */ + protected final Counter distanceCalcs; + + /** + * For counting the number of knn queries answered. + */ + protected final Counter knnQueries; + + /** + * For counting the number of range queries answered. + */ + protected final Counter rangeQueries; + + /** + * Constructor. + */ + public Statistics() { + super(); + Logging log = getLogger(); + distanceCalcs = log.isStatistics() ? log.newCounter(this.getClass().getName() + ".distancecalcs") : null; + knnQueries = log.isStatistics() ? log.newCounter(this.getClass().getName() + ".knnqueries") : null; + rangeQueries = log.isStatistics() ? log.newCounter(this.getClass().getName() + ".rangequeries") : null; + } + + /** + * Count a distance computation. + */ + public void countDistanceCalculation() { + if (distanceCalcs != null) { + distanceCalcs.increment(); + } + } + + /** + * Count a knn query invocation. + */ + public void countKNNQuery() { + if (knnQueries != null) { + knnQueries.increment(); + } + } + + /** + * Count a range query invocation. + */ + public void countRangeQuery() { + if (rangeQueries != null) { + rangeQueries.increment(); + } + } + + /** + * Log the statistics. + */ + public void logStatistics() { + Logging log = getLogger(); + if (statistics.distanceCalcs != null) { + log.statistics(statistics.distanceCalcs); + } + if (statistics.knnQueries != null) { + log.statistics(statistics.knnQueries); + } + if (statistics.rangeQueries != null) { + log.statistics(statistics.rangeQueries); + } + } + } + } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTreeFactory.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTreeFactory.java index 3769a562..7a0baa8a 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTreeFactory.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTreeFactory.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -25,10 +25,15 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants; import de.lmu.ifi.dbs.elki.data.type.TypeInformation; import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction; -import de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.distance.distancefunction.minkowski.EuclideanDistanceFunction; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.Index; -import de.lmu.ifi.dbs.elki.index.tree.TreeIndexFactory; +import de.lmu.ifi.dbs.elki.index.PagedIndexFactory; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.insert.MTreeInsert; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.insert.MinimumEnlargementInsert; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.split.MMRadSplit; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.split.MTreeSplit; +import de.lmu.ifi.dbs.elki.persistent.PageFileFactory; import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter; @@ -40,6 +45,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter; * * @apiviz.stereotype factory * @apiviz.uses AbstractMTree oneway - - «create» + * @apiviz.excludeSubtypes * * @param <O> Object type * @param <D> Distance type @@ -47,43 +53,26 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter; * @param <E> Entry type * @param <I> Index type */ -public abstract class AbstractMTreeFactory<O, D extends Distance<D>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry<D>, I extends AbstractMTree<O, D, N, E> & Index> extends TreeIndexFactory<O, I> { +public abstract class AbstractMTreeFactory<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry, I extends AbstractMTree<O, D, N, E, S> & Index, S extends MTreeSettings<O, D, N, E>> extends PagedIndexFactory<O, I> { /** - * Parameter to specify the distance function to determine the distance - * between database objects, must extend - * {@link de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction}. - * <p> - * Key: {@code -mtree.distancefunction} - * </p> - * <p> - * Default value: - * {@link de.lmu.ifi.dbs.elki.distance.distancefunction.EuclideanDistanceFunction} - * </p> + * Tree settings. */ - public static final OptionID DISTANCE_FUNCTION_ID = new OptionID("mtree.distancefunction", "Distance function to determine the distance between database objects."); - - /** - * Holds the instance of the distance function specified by - * {@link #DISTANCE_FUNCTION_ID}. - */ - protected DistanceFunction<O, D> distanceFunction; + protected S settings; /** * Constructor. * - * @param fileName - * @param pageSize - * @param cacheSize - * @param distanceFunction + * @param pageFileFactory Data storage + * @param settings Tree settings */ - public AbstractMTreeFactory(String fileName, int pageSize, long cacheSize, DistanceFunction<O, D> distanceFunction) { - super(fileName, pageSize, cacheSize); - this.distanceFunction = distanceFunction; + public AbstractMTreeFactory(PageFileFactory<?> pageFileFactory, S settings) { + super(pageFileFactory); + this.settings = settings; } @Override public TypeInformation getInputTypeRestriction() { - return distanceFunction.getInputTypeRestriction(); + return settings.distanceFunction.getInputTypeRestriction(); } /** @@ -93,19 +82,63 @@ public abstract class AbstractMTreeFactory<O, D extends Distance<D>, N extends A * * @apiviz.exclude */ - public abstract static class Parameterizer<O, D extends Distance<D>> extends TreeIndexFactory.Parameterizer<O> { - protected DistanceFunction<O, D> distanceFunction = null; + public abstract static class Parameterizer<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry, S extends MTreeSettings<O, D, N, E>> extends PagedIndexFactory.Parameterizer<O> { + /** + * Parameter to specify the distance function to determine the distance + * between database objects, must extend + * {@link de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction}. + * <p> + * Key: {@code -mtree.distancefunction} + * </p> + * <p> + * Default value: + * {@link de.lmu.ifi.dbs.elki.distance.distancefunction.minkowski.EuclideanDistanceFunction} + * </p> + */ + public static final OptionID DISTANCE_FUNCTION_ID = new OptionID("mtree.distancefunction", "Distance function to determine the distance between database objects."); + + /** + * Parameter to specify the splitting strategy to construct the tree. + * <p> + * Key: {@code -mtree.split} + * </p> + */ + public static final OptionID SPLIT_STRATEGY_ID = new OptionID("mtree.split", "Split strategy to use for constructing the M-tree."); + + /** + * Parameter to specify the insertion strategy to construct the tree. + * <p> + * Key: {@code -mtree.insert} + * </p> + */ + public static final OptionID INSERT_STRATEGY_ID = new OptionID("mtree.insert", "Insertion strategy to use for constructing the M-tree."); + + /** + * Tree settings. + */ + protected S settings; @Override protected void makeOptions(Parameterization config) { super.makeOptions(config); - ObjectParameter<DistanceFunction<O, D>> distanceFunctionP = new ObjectParameter<DistanceFunction<O, D>>(DISTANCE_FUNCTION_ID, DistanceFunction.class, EuclideanDistanceFunction.class); - if(config.grab(distanceFunctionP)) { - distanceFunction = distanceFunctionP.instantiateClass(config); + settings = makeSettings(); + ObjectParameter<DistanceFunction<O, D>> distanceFunctionP = new ObjectParameter<>(DISTANCE_FUNCTION_ID, DistanceFunction.class, EuclideanDistanceFunction.class); + if (config.grab(distanceFunctionP)) { + settings.distanceFunction = distanceFunctionP.instantiateClass(config); + } + ObjectParameter<MTreeSplit<O, D, N, E>> splitStrategyP = new ObjectParameter<>(SPLIT_STRATEGY_ID, MTreeSplit.class, MMRadSplit.class); + if (config.grab(splitStrategyP)) { + settings.splitStrategy = splitStrategyP.instantiateClass(config); + } + ObjectParameter<MTreeInsert<O, D, N, E>> insertStrategyP = new ObjectParameter<>(INSERT_STRATEGY_ID, MTreeInsert.class, MinimumEnlargementInsert.class); + if (config.grab(insertStrategyP)) { + settings.insertStrategy = insertStrategyP.instantiateClass(config); } } + abstract protected S makeSettings(); + @Override - protected abstract AbstractMTreeFactory<O, D, ?, ?, ?> makeInstance(); + protected abstract AbstractMTreeFactory<O, D, N, E, ?, ?> makeInstance(); } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTreeNode.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTreeNode.java index 85a9571d..1c1f486c 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTreeNode.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/AbstractMTreeNode.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -26,8 +26,6 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants; import java.util.logging.Logger; import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.distance.DistanceUtil; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.AbstractNode; import de.lmu.ifi.dbs.elki.logging.LoggingConfiguration; @@ -38,13 +36,14 @@ import de.lmu.ifi.dbs.elki.logging.LoggingConfiguration; * @author Elke Achtert * * @apiviz.has MTreeEntry oneway - - contains + * @apiviz.excludeSubtypes * * @param <O> the type of DatabaseObject to be stored in the M-Tree * @param <D> the type of Distance used in the M-Tree * @param <N> the type of AbstractMTreeNode used in the M-Tree * @param <E> the type of MetricalEntry used in the M-Tree */ -public abstract class AbstractMTreeNode<O, D extends Distance<D>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry<D>> extends AbstractNode<E> { +public abstract class AbstractMTreeNode<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry> extends AbstractNode<E> { /** * Empty constructor for Externalizable interface. */ @@ -74,14 +73,14 @@ public abstract class AbstractMTreeNode<O, D extends Distance<D>, N extends Abst * the routing object of the parent node * @param mTree the M-Tree object holding this node */ - public void adjustEntry(E entry, DBID routingObjectID, D parentDistance, AbstractMTree<O, D, N, E> mTree) { + public void adjustEntry(E entry, DBID routingObjectID, double parentDistance, AbstractMTree<O, D, N, E, ?> mTree) { entry.setRoutingObjectID(routingObjectID); entry.setParentDistance(parentDistance); entry.setCoveringRadius(coveringRadius(entry.getRoutingObjectID(), mTree)); - for(int i = 0; i < getNumEntries(); i++) { + for (int i = 0; i < getNumEntries(); i++) { E childEntry = getEntry(i); - D dist = mTree.distance(routingObjectID, childEntry.getRoutingObjectID()); + double dist = mTree.distance(routingObjectID, childEntry.getRoutingObjectID()).doubleValue(); childEntry.setParentDistance(dist); } } @@ -93,17 +92,12 @@ public abstract class AbstractMTreeNode<O, D extends Distance<D>, N extends Abst * @param mTree the M-Tree * @return the covering radius of this node */ - public D coveringRadius(DBID routingObjectID, AbstractMTree<O, D, N, E> mTree) { - D coveringRadius = mTree.getDistanceFactory().nullDistance(); - for(int i = 0; i < getNumEntries(); i++) { + public double coveringRadius(DBID routingObjectID, AbstractMTree<O, D, N, E, ?> mTree) { + double coveringRadius = 0.; + for (int i = 0; i < getNumEntries(); i++) { E entry = getEntry(i); - D distance = mTree.distance(entry.getRoutingObjectID(), routingObjectID); - // extend by the other objects covering radius, if non-null - D d2 = entry.getCoveringRadius(); - if(d2 != null) { - distance = distance.plus(d2); - } - coveringRadius = DistanceUtil.max(coveringRadius, distance); + double distance = mTree.distance(entry.getRoutingObjectID(), routingObjectID).doubleValue() + entry.getCoveringRadius(); + coveringRadius = Math.max(coveringRadius, distance); } return coveringRadius; } @@ -115,48 +109,47 @@ public abstract class AbstractMTreeNode<O, D extends Distance<D>, N extends Abst * @param entry the entry representing this node */ @SuppressWarnings("unchecked") - public final void integrityCheck(AbstractMTree<O, D, N, E> mTree, E entry) { + public final void integrityCheck(AbstractMTree<O, D, N, E, ?> mTree, E entry) { // leaf node - if(isLeaf()) { - for(int i = 0; i < getCapacity(); i++) { + if (isLeaf()) { + for (int i = 0; i < getCapacity(); i++) { E e = getEntry(i); - if(i < getNumEntries() && e == null) { + if (i < getNumEntries() && e == null) { throw new RuntimeException("i < numEntries && entry == null"); } - if(i >= getNumEntries() && e != null) { + if (i >= getNumEntries() && e != null) { throw new RuntimeException("i >= numEntries && entry != null"); } } } - // dir node else { N tmp = mTree.getNode(getEntry(0)); boolean childIsLeaf = tmp.isLeaf(); - for(int i = 0; i < getCapacity(); i++) { + for (int i = 0; i < getCapacity(); i++) { E e = getEntry(i); - if(i < getNumEntries() && e == null) { + if (i < getNumEntries() && e == null) { throw new RuntimeException("i < numEntries && entry == null"); } - if(i >= getNumEntries() && e != null) { + if (i >= getNumEntries() && e != null) { throw new RuntimeException("i >= numEntries && entry != null"); } - if(e != null) { + if (e != null) { N node = mTree.getNode(e); - if(childIsLeaf && !node.isLeaf()) { - for(int k = 0; k < getNumEntries(); k++) { + if (childIsLeaf && !node.isLeaf()) { + for (int k = 0; k < getNumEntries(); k++) { mTree.getNode(getEntry(k)); } throw new RuntimeException("Wrong Child in " + this + " at " + i); } - if(!childIsLeaf && node.isLeaf()) { + if (!childIsLeaf && node.isLeaf()) { throw new RuntimeException("Wrong Child: child id no leaf, but node is leaf!"); } @@ -166,7 +159,7 @@ public abstract class AbstractMTreeNode<O, D extends Distance<D>, N extends Abst } } - if(LoggingConfiguration.DEBUG) { + if (LoggingConfiguration.DEBUG) { Logger.getLogger(this.getClass().getName()).fine("DirNode " + getPageID() + " ok!"); } } @@ -181,30 +174,19 @@ public abstract class AbstractMTreeNode<O, D extends Distance<D>, N extends Abst * @param index the index of the entry in the parents child arry * @param mTree the M-Tree holding this node */ - protected void integrityCheckParameters(E parentEntry, N parent, int index, AbstractMTree<O, D, N, E> mTree) { + protected void integrityCheckParameters(E parentEntry, N parent, int index, AbstractMTree<O, D, N, E, ?> mTree) { // test if parent distance is correctly set E entry = parent.getEntry(index); - D parentDistance = mTree.distance(entry.getRoutingObjectID(), parentEntry.getRoutingObjectID()); - if(!entry.getParentDistance().equals(parentDistance)) { - String soll = parentDistance.toString(); - String ist = entry.getParentDistance().toString(); - throw new RuntimeException("Wrong parent distance in node " + parent.getPageID() + " at index " + index + " (child " + entry + ")" + "\nsoll: " + soll + ",\n ist: " + ist); + double parentDistance = mTree.distance(entry.getRoutingObjectID(), parentEntry.getRoutingObjectID()).doubleValue(); + if (Math.abs(entry.getParentDistance() - parentDistance) > 1E-10) { + throw new RuntimeException("Wrong parent distance in node " + parent.getPageID() + " at index " + index + " (child " + entry + ")" + "\nsoll: " + parentDistance + ",\n ist: " + entry.getParentDistance()); } // test if covering radius is correctly set - D mincover = parentDistance.plus(entry.getCoveringRadius()); - if(parentEntry.getCoveringRadius().compareTo(mincover) < 0) { - String msg = "pcr < pd + cr \n" + parentEntry.getCoveringRadius() + " < " + parentDistance + " + " + entry.getCoveringRadius() + "in node " + parent.getPageID() + " at index " + index + " (child " + entry + "):\n" + "dist(" + entry.getRoutingObjectID() + " - " + parentEntry.getRoutingObjectID() + ")" + " > cr(" + entry + ")"; - - // throw new RuntimeException(msg); - if(parentDistance instanceof NumberDistance<?, ?>) { - double d1 = Double.parseDouble(parentDistance.toString()); - double d2 = Double.parseDouble(entry.getCoveringRadius().toString()); - if(Math.abs(d1 - d2) > 0.000000001) { - throw new RuntimeException(msg); - } - } - else { + double mincover = parentDistance + entry.getCoveringRadius(); + if (parentEntry.getCoveringRadius() < mincover) { + if (Math.abs(parentDistance - entry.getCoveringRadius()) > 1e-10) { + String msg = "pcr < pd + cr \n" + parentEntry.getCoveringRadius() + " < " + parentDistance + " + " + entry.getCoveringRadius() + "in node " + parent.getPageID() + " at index " + index + " (child " + entry + "):\n" + "dist(" + entry.getRoutingObjectID() + " - " + parentEntry.getRoutingObjectID() + ")" + " > cr(" + entry + ")"; throw new RuntimeException(msg); } } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeDirectoryEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeDirectoryEntry.java index 3902973f..92d0a7ea 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeDirectoryEntry.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeDirectoryEntry.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -29,7 +29,6 @@ import java.io.ObjectOutput; import de.lmu.ifi.dbs.elki.database.ids.DBID; import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; import de.lmu.ifi.dbs.elki.index.tree.AbstractDirectoryEntry; /** @@ -39,10 +38,9 @@ import de.lmu.ifi.dbs.elki.index.tree.AbstractDirectoryEntry; * the routing object of the entry to its parent's routing object in the M-Tree. * * @author Elke Achtert - * @param <D> the type of Distance used in the M-Tree */ -public class MTreeDirectoryEntry<D extends Distance<D>> extends AbstractDirectoryEntry implements MTreeEntry<D> { - private static final long serialVersionUID = 1; +public class MTreeDirectoryEntry extends AbstractDirectoryEntry implements MTreeEntry { + private static final long serialVersionUID = 2; /** * The id of routing object of this entry. @@ -53,12 +51,12 @@ public class MTreeDirectoryEntry<D extends Distance<D>> extends AbstractDirector * The distance from the routing object of this entry to its parent's routing * object. */ - private D parentDistance; + private double parentDistance; /** * The covering radius of the entry. */ - private D coveringRadius; + private double coveringRadius; /** * Empty constructor for serialization purposes. @@ -76,7 +74,7 @@ public class MTreeDirectoryEntry<D extends Distance<D>> extends AbstractDirector * @param nodeID the id of the underlying node * @param coveringRadius the covering radius of the entry */ - public MTreeDirectoryEntry(DBID objectID, D parentDistance, Integer nodeID, D coveringRadius) { + public MTreeDirectoryEntry(DBID objectID, double parentDistance, Integer nodeID, double coveringRadius) { super(nodeID); this.routingObjectID = objectID; this.parentDistance = parentDistance; @@ -89,7 +87,7 @@ public class MTreeDirectoryEntry<D extends Distance<D>> extends AbstractDirector * @return the covering radius of this entry */ @Override - public final D getCoveringRadius() { + public final double getCoveringRadius() { return coveringRadius; } @@ -99,7 +97,7 @@ public class MTreeDirectoryEntry<D extends Distance<D>> extends AbstractDirector * @param coveringRadius the covering radius to be set */ @Override - public final void setCoveringRadius(D coveringRadius) { + public final void setCoveringRadius(double coveringRadius) { this.coveringRadius = coveringRadius; } @@ -131,7 +129,7 @@ public class MTreeDirectoryEntry<D extends Distance<D>> extends AbstractDirector * routing object. */ @Override - public final D getParentDistance() { + public final double getParentDistance() { return parentDistance; } @@ -141,7 +139,7 @@ public class MTreeDirectoryEntry<D extends Distance<D>> extends AbstractDirector * @param parentDistance the distance to be set */ @Override - public final void setParentDistance(D parentDistance) { + public final void setParentDistance(double parentDistance) { this.parentDistance = parentDistance; } @@ -153,8 +151,8 @@ public class MTreeDirectoryEntry<D extends Distance<D>> extends AbstractDirector public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal(out); out.writeInt(DBIDUtil.asInteger(routingObjectID)); - out.writeObject(parentDistance); - out.writeObject(coveringRadius); + out.writeDouble(parentDistance); + out.writeDouble(coveringRadius); } /** @@ -162,12 +160,11 @@ public class MTreeDirectoryEntry<D extends Distance<D>> extends AbstractDirector * and the coveringRadius of this entry from the specified input stream. */ @Override - @SuppressWarnings( { "unchecked" }) public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); this.routingObjectID = DBIDUtil.importInteger(in.readInt()); - this.parentDistance = (D) in.readObject(); - this.coveringRadius = (D) in.readObject(); + this.parentDistance = in.readDouble(); + this.coveringRadius = in.readDouble(); } /** @@ -189,24 +186,23 @@ public class MTreeDirectoryEntry<D extends Distance<D>> extends AbstractDirector * and routingObjectID as this entry. */ @Override - @SuppressWarnings("unchecked") public boolean equals(Object o) { - if(this == o) { + if (this == o) { return true; } - if(o == null || getClass() != o.getClass()) { + if (o == null || getClass() != o.getClass()) { return false; } - if(!super.equals(o)) { + if (!super.equals(o)) { return false; } - final MTreeDirectoryEntry<D> that = (MTreeDirectoryEntry<D>) o; + final MTreeDirectoryEntry that = (MTreeDirectoryEntry) o; - if(coveringRadius != null ? !coveringRadius.equals(that.coveringRadius) : that.coveringRadius != null) { + if (Math.abs(coveringRadius - that.coveringRadius) < Double.MIN_NORMAL) { return false; } - if(parentDistance != null ? !parentDistance.equals(that.parentDistance) : that.parentDistance != null) { + if (Math.abs(parentDistance - that.parentDistance) < Double.MIN_NORMAL) { return false; } return !(routingObjectID != null ? !DBIDUtil.equal(routingObjectID, that.routingObjectID) : that.routingObjectID != null); diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeEntry.java index f6969b92..66628087 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeEntry.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeEntry.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -24,16 +24,14 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants; */ import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; import de.lmu.ifi.dbs.elki.index.tree.Entry; /** * Defines the requirements for an entry in an M-Tree node. * * @author Elke Achtert - * @param <D> the type of Distance used in the M-Tree */ -public interface MTreeEntry<D extends Distance<D>> extends Entry { +public interface MTreeEntry extends Entry { /** * Returns the id of the underlying database object of this entry, if this * entry is a leaf entry, the id of the routing object, otherwise. @@ -57,14 +55,14 @@ public interface MTreeEntry<D extends Distance<D>> extends Entry { * * @return the distance from the object to its parent object */ - D getParentDistance(); + double getParentDistance(); /** * Sets the distance from the routing object to routing object of its parent. * * @param parentDistance the distance to be set */ - void setParentDistance(D parentDistance); + void setParentDistance(double parentDistance); /** * Returns the covering radius if this entry is a directory entry, null @@ -72,7 +70,7 @@ public interface MTreeEntry<D extends Distance<D>> extends Entry { * * @return the covering radius of this entry */ - D getCoveringRadius(); + double getCoveringRadius(); /** * Sets the covering radius of this entry if this entry is a directory entry, @@ -80,5 +78,5 @@ public interface MTreeEntry<D extends Distance<D>> extends Entry { * * @param coveringRadius the covering radius to be set */ - void setCoveringRadius(D coveringRadius); + void setCoveringRadius(double coveringRadius); } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeLeafEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeLeafEntry.java index 27fbc1ba..cfb22bac 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeLeafEntry.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeLeafEntry.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -28,7 +28,6 @@ import java.io.ObjectInput; import java.io.ObjectOutput; import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; import de.lmu.ifi.dbs.elki.index.tree.AbstractLeafEntry; /** @@ -38,16 +37,18 @@ import de.lmu.ifi.dbs.elki.index.tree.AbstractLeafEntry; * M-Tree. * * @author Elke Achtert - * @param <D> the type of Distance used in the M-Tree */ -public class MTreeLeafEntry<D extends Distance<D>> extends AbstractLeafEntry implements MTreeEntry<D> { - private static final long serialVersionUID = 1; +public class MTreeLeafEntry extends AbstractLeafEntry implements MTreeEntry { + /** + * Serialization version ID. + */ + private static final long serialVersionUID = 2; /** * The distance from the underlying data object to its parent's routing * object. */ - private D parentDistance; + private double parentDistance; /** * Empty constructor for serialization purposes. @@ -63,7 +64,7 @@ public class MTreeLeafEntry<D extends Distance<D>> extends AbstractLeafEntry imp * @param parentDistance the distance from the underlying data object to its * parent's routing object */ - public MTreeLeafEntry(DBID objectID, D parentDistance) { + public MTreeLeafEntry(DBID objectID, double parentDistance) { super(objectID); this.parentDistance = parentDistance; } @@ -87,7 +88,6 @@ public class MTreeLeafEntry<D extends Distance<D>> extends AbstractLeafEntry imp @Override public final void setRoutingObjectID(DBID objectID) { throw new UnsupportedOperationException("Leaf entries should not be assigned a routing object."); - // super.setEntryID(objectID.getIntegerID()); } /** @@ -98,7 +98,7 @@ public class MTreeLeafEntry<D extends Distance<D>> extends AbstractLeafEntry imp * routing object */ @Override - public final D getParentDistance() { + public final double getParentDistance() { return parentDistance; } @@ -109,18 +109,18 @@ public class MTreeLeafEntry<D extends Distance<D>> extends AbstractLeafEntry imp * @param parentDistance the distance to be set */ @Override - public final void setParentDistance(D parentDistance) { + public final void setParentDistance(double parentDistance) { this.parentDistance = parentDistance; } /** - * Returns null, since a leaf entry has no covering radius. + * Returns zero, since a leaf entry has no covering radius. * - * @return null + * @return Zero */ @Override - public D getCoveringRadius() { - return null; + public double getCoveringRadius() { + return 0.0; } /** @@ -131,7 +131,7 @@ public class MTreeLeafEntry<D extends Distance<D>> extends AbstractLeafEntry imp * radius */ @Override - public void setCoveringRadius(D coveringRadius) { + public void setCoveringRadius(double coveringRadius) { throw new UnsupportedOperationException("This entry is not a directory entry!"); } @@ -142,18 +142,17 @@ public class MTreeLeafEntry<D extends Distance<D>> extends AbstractLeafEntry imp @Override public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal(out); - out.writeObject(parentDistance); + out.writeDouble(parentDistance); } /** * Calls the super method and reads the parentDistance of this entry from the * specified input stream. */ - @SuppressWarnings("unchecked") @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); - this.parentDistance = (D) in.readObject(); + this.parentDistance = in.readDouble(); } /** @@ -164,20 +163,19 @@ public class MTreeLeafEntry<D extends Distance<D>> extends AbstractLeafEntry imp * and has the same parentDistance as this entry. */ @Override - @SuppressWarnings("unchecked") public boolean equals(Object o) { - if(this == o) { + if (this == o) { return true; } - if(o == null || getClass() != o.getClass()) { + if (o == null || getClass() != o.getClass()) { return false; } - if(!super.equals(o)) { + if (!super.equals(o)) { return false; } - final MTreeLeafEntry<D> that = (MTreeLeafEntry<D>) o; + final MTreeLeafEntry that = (MTreeLeafEntry) o; - return !(parentDistance != null ? !parentDistance.equals(that.parentDistance) : that.parentDistance != null); + return Math.abs(parentDistance - that.parentDistance) < Double.MIN_NORMAL; } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeSettings.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeSettings.java new file mode 100644 index 00000000..59f6e598 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/MTreeSettings.java @@ -0,0 +1,56 @@ +package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants; + +/* + This file is part of ELKI: + Environment for Developing KDD-Applications Supported by Index-Structures + + Copyright (C) 2013 + 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.distance.distancefunction.DistanceFunction; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.insert.MTreeInsert; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.split.MTreeSplit; + +/** + * Class to store the MTree settings. + * + * @author Erich Schubert + * + * @param <O> Object type + * @param <D> Distance type + * @param <N> Node type + * @param <E> Entry type + */ +public class MTreeSettings<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry> { + /** + * Holds the instance of the trees distance function. + */ + protected DistanceFunction<? super O, D> distanceFunction; + + /** + * Splitting strategy. + */ + protected MTreeSplit<O, D, N, E> splitStrategy; + + /** + * Insertion strategy. + */ + protected MTreeInsert<O, D, N, E> insertStrategy; +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTree.java index f3c440b5..aec4410e 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTree.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTree.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -31,15 +31,16 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDIter; 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.DBIDs; +import de.lmu.ifi.dbs.elki.database.ids.distance.DistanceDBIDList; +import de.lmu.ifi.dbs.elki.database.ids.distance.KNNList; import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery; -import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.database.relation.Relation; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeSettings; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MTreeQueryUtil; import de.lmu.ifi.dbs.elki.persistent.PageFile; @@ -53,25 +54,42 @@ import de.lmu.ifi.dbs.elki.persistent.PageFile; * @param <D> the type of Distance used in the metrical index * @param <N> the type of MetricalNode used in the metrical index * @param <E> the type of MetricalEntry used in the metrical index + * @param <S> the type of Settings kept. */ -public abstract class AbstractMkTree<O, D extends Distance<D>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry<D>> extends AbstractMTree<O, D, N, E> { +public abstract class AbstractMkTree<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry, S extends MTreeSettings<O, D, N, E>> extends AbstractMTree<O, D, N, E, S> { /** * Internal class for performing knn queries */ protected KNNQuery<O, D> knnq; /** + * Distance query to use. + */ + private DistanceQuery<O, D> distanceQuery; + + /** * Constructor. * + * @param relation Relation to index * @param pagefile Page file - * @param distanceQuery Distance query - * @param distanceFunction Distance function + * @param settings Settings class */ - public AbstractMkTree(PageFile<N> pagefile, DistanceQuery<O, D> distanceQuery, DistanceFunction<O, D> distanceFunction) { - super(pagefile, distanceQuery, distanceFunction); + public AbstractMkTree(Relation<O> relation, PageFile<N> pagefile, S settings) { + super(pagefile, settings); + // TODO: any way to un-tie MkTrees from relations? + this.distanceQuery = getDistanceFunction().instantiate(relation); this.knnq = MTreeQueryUtil.getKNNQuery(this, distanceQuery); } + @Override + public D distance(DBIDRef id1, DBIDRef id2) { + if (id1 == null || id2 == null) { + return getDistanceFactory().undefinedDistance(); + } + statistics.countDistanceCalculation(); + return distanceQuery.distance(id1, id2); + } + /** * Performs a reverse k-nearest neighbor query for the given object ID. The * query result is in ascending order to the distance to the query object. @@ -80,7 +98,7 @@ public abstract class AbstractMkTree<O, D extends Distance<D>, N extends Abstrac * @param k the number of nearest neighbors to be returned * @return a List of the query results */ - public abstract DistanceDBIDResult<D> reverseKNNQuery(final DBIDRef id, int k); + public abstract DistanceDBIDList<D> reverseKNNQuery(final DBIDRef id, int k); /** * Performs a batch k-nearest neighbor query for a list of query objects. @@ -93,8 +111,8 @@ public abstract class AbstractMkTree<O, D extends Distance<D>, N extends Abstrac * @deprecated Change to use by-object NN lookups instead. */ @Deprecated - protected final Map<DBID, KNNResult<D>> batchNN(N node, DBIDs ids, int kmax) { - Map<DBID, KNNResult<D>> res = new HashMap<DBID, KNNResult<D>>(ids.size()); + protected final Map<DBID, KNNList<D>> batchNN(N node, DBIDs ids, int kmax) { + Map<DBID, KNNList<D>> res = new HashMap<>(ids.size()); for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) { DBID id = DBIDUtil.deref(iter); res.put(id, knnq.getKNNForDBID(id, kmax)); diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnified.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnified.java index 34507479..455d372a 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnified.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnified.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -29,10 +29,9 @@ import java.util.Map; import de.lmu.ifi.dbs.elki.database.ids.DBID; 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.distance.DistanceQuery; -import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.database.ids.distance.KNNList; +import de.lmu.ifi.dbs.elki.database.relation.Relation; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.TreeIndexHeader; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; @@ -46,29 +45,24 @@ import de.lmu.ifi.dbs.elki.persistent.PageFile; * @author Elke Achtert * * @apiviz.has MkTreeHeader oneway + * @apiviz.composedOf MkTreeSettings * * @param <O> the type of DatabaseObject to be stored in the metrical index * @param <D> the type of Distance used in the metrical index * @param <N> the type of MetricalNode used in the metrical index * @param <E> the type of MetricalEntry used in the metrical index + * @param <S> the type of Settings used. */ -public abstract class AbstractMkTreeUnified<O, D extends Distance<D>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry<D>> extends AbstractMkTree<O, D, N, E> { - /** - * Holds the maximum value of k to support. - */ - private int k_max; - +public abstract class AbstractMkTreeUnified<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry, S extends MkTreeSettings<O, D, N, E>> extends AbstractMkTree<O, D, N, E, S> { /** * Constructor. * + * @param relation Relation to index * @param pagefile Page file - * @param distanceQuery Distance query - * @param distanceFunction Distance function - * @param k_max Maximum value for k + * @param settings Settings file */ - public AbstractMkTreeUnified(PageFile<N> pagefile, DistanceQuery<O, D> distanceQuery, DistanceFunction<O, D> distanceFunction, int k_max) { - super(pagefile, distanceQuery, distanceFunction); - this.k_max = k_max; + public AbstractMkTreeUnified(Relation<O> relation, PageFile<N> pagefile, S settings) { + super(relation, pagefile, settings); } /** @@ -76,7 +70,7 @@ public abstract class AbstractMkTreeUnified<O, D extends Distance<D>, N extends */ @Override protected TreeIndexHeader createHeader() { - return new MkTreeHeader(getPageSize(), dirCapacity, leafCapacity, k_max); + return new MkTreeHeader(getPageSize(), dirCapacity, leafCapacity, settings.k_max); } @Override @@ -98,7 +92,7 @@ public abstract class AbstractMkTreeUnified<O, D extends Distance<D>, N extends } // do batch nn - Map<DBID, KNNResult<D>> knnLists = batchNN(getRoot(), ids, k_max); + Map<DBID, KNNList<D>> knnLists = batchNN(getRoot(), ids, settings.k_max); // adjust the knn distances kNNdistanceAdjustment(getRootEntry(), knnLists); @@ -114,7 +108,7 @@ public abstract class AbstractMkTreeUnified<O, D extends Distance<D>, N extends * @param entry the root entry of the current subtree * @param knnLists a map of knn lists for each leaf entry */ - protected abstract void kNNdistanceAdjustment(E entry, Map<DBID, KNNResult<D>> knnLists); + protected abstract void kNNdistanceAdjustment(E entry, Map<DBID, KNNList<D>> knnLists); /** * Get the value of k_max. @@ -122,6 +116,6 @@ public abstract class AbstractMkTreeUnified<O, D extends Distance<D>, N extends * @return k_max value. */ public int getKmax() { - return k_max; + return settings.k_max; } } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnifiedFactory.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnifiedFactory.java index 9570995d..fe408035 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnifiedFactory.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/AbstractMkTreeUnifiedFactory.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -23,12 +23,12 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees; along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.Index; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeFactory; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; +import de.lmu.ifi.dbs.elki.persistent.PageFileFactory; import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID; import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization; @@ -36,44 +36,27 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter; /** * Abstract factory for various Mk-Trees + * * @author Erich Schubert * * @apiviz.stereotype factory - * @apiviz.uses AbstractMkTree oneway - - «create» - * + * @apiviz.uses AbstractMkTreeUnified oneway - - «create» + * * @param <O> Object type * @param <D> Distance type * @param <N> Node type * @param <E> Entry type * @param <I> Index type */ -public abstract class AbstractMkTreeUnifiedFactory<O, D extends Distance<D>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry<D>, I extends AbstractMkTree<O, D, N, E> & Index> extends AbstractMTreeFactory<O, D, N, E, I> { - /** - * Parameter specifying the maximal number k of reverse k nearest neighbors to - * be supported, must be an integer greater than 0. - * <p> - * Key: {@code -mktree.kmax} - * </p> - */ - public static final OptionID K_MAX_ID = new OptionID("mktree.kmax", "Specifies the maximal number k of reverse k nearest neighbors to be supported."); - - /** - * Holds the value of parameter {@link #K_MAX_ID}. - */ - protected int k_max; - +public abstract class AbstractMkTreeUnifiedFactory<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry, I extends AbstractMkTree<O, D, N, E, S> & Index, S extends MkTreeSettings<O, D, N, E>> extends AbstractMTreeFactory<O, D, N, E, I, S> { /** * Constructor. - * - * @param fileName - * @param pageSize - * @param cacheSize - * @param distanceFunction - * @param k_max + * + * @param pageFileFactory Data storage + * @param settings Tree settings */ - public AbstractMkTreeUnifiedFactory(String fileName, int pageSize, long cacheSize, DistanceFunction<O, D> distanceFunction, int k_max) { - super(fileName, pageSize, cacheSize, distanceFunction); - this.k_max = k_max; + public AbstractMkTreeUnifiedFactory(PageFileFactory<?> pageFileFactory, S settings) { + super(pageFileFactory, settings); } /** @@ -83,8 +66,15 @@ public abstract class AbstractMkTreeUnifiedFactory<O, D extends Distance<D>, N e * * @apiviz.exclude */ - public abstract static class Parameterizer<O, D extends Distance<D>> extends AbstractMTreeFactory.Parameterizer<O, D> { - protected int k_max; + public abstract static class Parameterizer<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry, S extends MkTreeSettings<O, D, N, E>> extends AbstractMTreeFactory.Parameterizer<O, D, N, E, S> { + /** + * Parameter specifying the maximal number k of reverse k nearest neighbors + * to be supported, must be an integer greater than 0. + * <p> + * Key: {@code -mktree.kmax} + * </p> + */ + public static final OptionID K_MAX_ID = new OptionID("mktree.kmax", "Specifies the maximal number k of reverse k nearest neighbors to be supported."); @Override protected void makeOptions(Parameterization config) { @@ -93,11 +83,11 @@ public abstract class AbstractMkTreeUnifiedFactory<O, D extends Distance<D>, N e k_maxP.addConstraint(new GreaterConstraint(0)); if (config.grab(k_maxP)) { - k_max = k_maxP.getValue(); + settings.k_max = k_maxP.getValue(); } } @Override - protected abstract AbstractMkTreeUnifiedFactory<O, D, ?, ?, ?> makeInstance(); + protected abstract AbstractMkTreeUnifiedFactory<O, D, N, E, ?, S> makeInstance(); } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/MkTreeHeader.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/MkTreeHeader.java index c067a86c..ff49d789 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/MkTreeHeader.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/MkTreeHeader.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -23,11 +23,11 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees; along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import de.lmu.ifi.dbs.elki.index.tree.TreeIndexHeader; - import java.io.IOException; import java.io.RandomAccessFile; +import de.lmu.ifi.dbs.elki.index.tree.TreeIndexHeader; + /** * Encapsulates the header information for subclasses of * {@link de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTreeUnified}. diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/MkTreeSettings.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/MkTreeSettings.java new file mode 100644 index 00000000..6a957bd7 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/MkTreeSettings.java @@ -0,0 +1,46 @@ +package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees; + +/* + This file is part of ELKI: + Environment for Developing KDD-Applications Supported by Index-Structures + + Copyright (C) 2013 + 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.distance.distancevalue.NumberDistance; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeSettings; + +/** + * Class with settings for MkTrees. + * + * @author Erich Schubert + * + * @param <O> Object type + * @param <D> Distance type + * @param <N> Node type + * @param <E> Entry type + */ +public class MkTreeSettings<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry> extends MTreeSettings<O, D, N, E> { + /** + * Holds the maximum value of k to support. + */ + public int k_max; +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppDirectoryEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppDirectoryEntry.java index ce7cf104..fd6f6bab 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppDirectoryEntry.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppDirectoryEntry.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkapp; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -28,7 +28,6 @@ import java.io.ObjectInput; import java.io.ObjectOutput; import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeDirectoryEntry; /** @@ -38,11 +37,11 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeDirectoryEntry * * @author Elke Achtert */ -class MkAppDirectoryEntry<D extends NumberDistance<D, ?>> extends MTreeDirectoryEntry<D> implements MkAppEntry<D> { +class MkAppDirectoryEntry extends MTreeDirectoryEntry implements MkAppEntry { /** * Serial version UID - */ - private static final long serialVersionUID = 1; + */ + private static final long serialVersionUID = 2; /** * The polynomial approximation. @@ -65,7 +64,7 @@ class MkAppDirectoryEntry<D extends NumberDistance<D, ?>> extends MTreeDirectory * @param coveringRadius the covering radius of the entry * @param approximation the polynomial approximation of the knn distances */ - public MkAppDirectoryEntry(DBID objectID, D parentDistance, Integer nodeID, D coveringRadius, PolynomialApproximation approximation) { + public MkAppDirectoryEntry(DBID objectID, double parentDistance, Integer nodeID, double coveringRadius, PolynomialApproximation approximation) { super(objectID, parentDistance, nodeID, coveringRadius); this.approximation = approximation; } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppEntry.java index b6eabf7e..5424f6f1 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppEntry.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppEntry.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkapp; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -23,7 +23,6 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkapp; along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; /** @@ -35,7 +34,7 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; * * @apiviz.composedOf PolynomialApproximation */ -interface MkAppEntry<D extends NumberDistance<D, ?>> extends MTreeEntry<D> { +interface MkAppEntry extends MTreeEntry { /** * Returns the approximated value at the specified k. * @@ -57,4 +56,4 @@ interface MkAppEntry<D extends NumberDistance<D, ?>> extends MTreeEntry<D> { * @param approximation the polynomial approximation to be set */ public void setKnnDistanceApproximation(PolynomialApproximation approximation); -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppLeafEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppLeafEntry.java index 4dbf0244..e1f2305a 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppLeafEntry.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppLeafEntry.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkapp; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -28,7 +28,6 @@ import java.io.ObjectInput; import java.io.ObjectOutput; import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeLeafEntry; /** @@ -38,11 +37,11 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeLeafEntry; * * @author Elke Achtert */ -class MkAppLeafEntry<D extends NumberDistance<D, ?>> extends MTreeLeafEntry<D> implements MkAppEntry<D> { +class MkAppLeafEntry extends MTreeLeafEntry implements MkAppEntry { /** * Serial Version UID */ - private static final long serialVersionUID = 1; + private static final long serialVersionUID = 2; /** * The polynomial approximation. @@ -64,7 +63,7 @@ class MkAppLeafEntry<D extends NumberDistance<D, ?>> extends MTreeLeafEntry<D> i * parent's routing object * @param approximation the polynomial approximation of the knn distances */ - public MkAppLeafEntry(DBID objectID, D parentDistance, PolynomialApproximation approximation) { + public MkAppLeafEntry(DBID objectID, double parentDistance, PolynomialApproximation approximation) { super(objectID, parentDistance); this.approximation = approximation; } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTree.java index 51d59e73..1ff0a030 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTree.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTree.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkapp; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -34,18 +34,18 @@ 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.DBIDs; import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs; -import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; -import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult; +import de.lmu.ifi.dbs.elki.database.ids.distance.DistanceDBIDList; +import de.lmu.ifi.dbs.elki.database.ids.distance.DistanceDBIDListIter; +import de.lmu.ifi.dbs.elki.database.ids.distance.KNNList; +import de.lmu.ifi.dbs.elki.database.ids.generic.GenericDistanceDBIDList; +import de.lmu.ifi.dbs.elki.database.relation.Relation; import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.LeafEntry; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTree; import de.lmu.ifi.dbs.elki.index.tree.query.GenericMTreeDistanceSearchCandidate; import de.lmu.ifi.dbs.elki.logging.Logging; import de.lmu.ifi.dbs.elki.math.statistics.PolynomialRegression; +import de.lmu.ifi.dbs.elki.persistent.ByteArrayUtil; import de.lmu.ifi.dbs.elki.persistent.PageFile; import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap; import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.UpdatableHeap; @@ -57,54 +57,34 @@ import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.UpdatableHeap; * * @author Elke Achtert * + * @apiviz.composedOf MkAppTreeSettings * @apiviz.has MkAppTreeNode oneway - - contains * * @param <O> the type of DatabaseObject to be stored in the metrical index * @param <D> the type of NumberDistance used in the metrical index */ -public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree<O, D, MkAppTreeNode<O, D>, MkAppEntry<D>> { +public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree<O, D, MkAppTreeNode<O, D>, MkAppEntry, MkAppTreeSettings<O, D>> { /** * The logger for this class. */ private static final Logging LOG = Logging.getLogger(MkAppTree.class); /** - * Parameter k. - */ - private int k_max; - - /** - * Parameter p. - */ - private int p; - - /** - * Flag log. - */ - private boolean log; - - /** * Constructor. * + * @param relation Relation to index * @param pageFile Page file - * @param distanceQuery Distance query - * @param distanceFunction Distance function - * @param k_max Maximum value of k supported - * @param p Parameter p - * @param log Logspace flag + * @param settings Tree settings */ - public MkAppTree(PageFile<MkAppTreeNode<O, D>> pageFile, DistanceQuery<O, D> distanceQuery, DistanceFunction<O, D> distanceFunction, int k_max, int p, boolean log) { - super(pageFile, distanceQuery, distanceFunction); - this.k_max = k_max; - this.p = p; - this.log = log; + public MkAppTree(Relation<O> relation, PageFile<MkAppTreeNode<O, D>> pageFile, MkAppTreeSettings<O, D> settings) { + super(relation, pageFile, settings); } /** * @throws UnsupportedOperationException since this operation is not supported */ @Override - public void insert(MkAppEntry<D> id, boolean withPreInsert) { + public void insert(MkAppEntry id, boolean withPreInsert) { throw new UnsupportedOperationException("Insertion of single objects is not supported!"); } @@ -112,7 +92,7 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree * @throws UnsupportedOperationException since this operation is not supported */ @Override - protected void preInsert(MkAppEntry<D> entry) { + protected void preInsert(MkAppEntry entry) { throw new UnsupportedOperationException("Insertion of single objects is not supported!"); } @@ -122,35 +102,35 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree * @param entries the entries to be inserted */ @Override - public void insertAll(List<MkAppEntry<D>> entries) { - if(entries.isEmpty()) { + public void insertAll(List<MkAppEntry> entries) { + if (entries.isEmpty()) { return; } - if(LOG.isDebugging()) { + if (LOG.isDebugging()) { LOG.debugFine("insert " + entries + "\n"); } - if(!initialized) { + if (!initialized) { initialize(entries.get(0)); } ModifiableDBIDs ids = DBIDUtil.newArray(entries.size()); // insert - for(MkAppEntry<D> entry : entries) { + for (MkAppEntry entry : entries) { ids.add(entry.getRoutingObjectID()); // insert the object super.insert(entry, false); } // do batch nn - Map<DBID, KNNResult<D>> knnLists = batchNN(getRoot(), ids, k_max + 1); - + Map<DBID, KNNList<D>> knnLists = batchNN(getRoot(), ids, settings.k_max + 1); + // adjust the knn distances adjustApproximatedKNNDistances(getRootEntry(), knnLists); - if(EXTRA_INTEGRITY_CHECKS) { + if (EXTRA_INTEGRITY_CHECKS) { getRoot().integrityCheck(this, getRootEntry()); } } @@ -164,50 +144,49 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree * @return a List of the query results */ @Override - public DistanceDBIDResult<D> reverseKNNQuery(DBIDRef id, int k) { - GenericDistanceDBIDList<D> result = new GenericDistanceDBIDList<D>(); - final Heap<GenericMTreeDistanceSearchCandidate<D>> pq = new UpdatableHeap<GenericMTreeDistanceSearchCandidate<D>>(); + public DistanceDBIDList<D> reverseKNNQuery(DBIDRef id, int k) { + GenericDistanceDBIDList<D> result = new GenericDistanceDBIDList<>(); + final Heap<GenericMTreeDistanceSearchCandidate> pq = new UpdatableHeap<>(); // push root - pq.add(new GenericMTreeDistanceSearchCandidate<D>(getDistanceQuery().nullDistance(), getRootID(), null, null)); + pq.add(new GenericMTreeDistanceSearchCandidate(0., getRootID(), null)); // search in tree - while(!pq.isEmpty()) { - GenericMTreeDistanceSearchCandidate<D> pqNode = pq.poll(); + while (!pq.isEmpty()) { + GenericMTreeDistanceSearchCandidate pqNode = pq.poll(); // FIXME: cache the distance to the routing object in the queue node! MkAppTreeNode<O, D> node = getNode(pqNode.nodeID); // directory node - if(!node.isLeaf()) { - for(int i = 0; i < node.getNumEntries(); i++) { - MkAppEntry<D> entry = node.getEntry(i); - D distance = getDistanceQuery().distance(entry.getRoutingObjectID(), id); - D minDist = entry.getCoveringRadius().compareTo(distance) > 0 ? getDistanceQuery().nullDistance() : distance.minus(entry.getCoveringRadius()); - - double approxValue = log ? Math.exp(entry.approximatedValueAt(k)) : entry.approximatedValueAt(k); - if(approxValue < 0) { + if (!node.isLeaf()) { + for (int i = 0; i < node.getNumEntries(); i++) { + MkAppEntry entry = node.getEntry(i); + double distance = distance(entry.getRoutingObjectID(), id).doubleValue(); + double minDist = (entry.getCoveringRadius() > distance) ? 0. : distance - entry.getCoveringRadius(); + + double approxValue = settings.log ? Math.exp(entry.approximatedValueAt(k)) : entry.approximatedValueAt(k); + if (approxValue < 0) { approxValue = 0; } - D approximatedKnnDist = getDistanceQuery().getDistanceFactory().fromDouble(approxValue); - if(minDist.compareTo(approximatedKnnDist) <= 0) { - pq.add(new GenericMTreeDistanceSearchCandidate<D>(minDist, getPageID(entry), entry.getRoutingObjectID(), null)); + if (minDist <= approxValue) { + pq.add(new GenericMTreeDistanceSearchCandidate(minDist, getPageID(entry), entry.getRoutingObjectID())); } } } // data node else { - for(int i = 0; i < node.getNumEntries(); i++) { - MkAppLeafEntry<D> entry = (MkAppLeafEntry<D>) node.getEntry(i); - D distance = getDistanceQuery().distance(entry.getRoutingObjectID(), id); - double approxValue = log ? StrictMath.exp(entry.approximatedValueAt(k)) : entry.approximatedValueAt(k); - if(approxValue < 0) { + for (int i = 0; i < node.getNumEntries(); i++) { + MkAppLeafEntry entry = (MkAppLeafEntry) node.getEntry(i); + D distance = distance(entry.getRoutingObjectID(), id); + double approxValue = settings.log ? StrictMath.exp(entry.approximatedValueAt(k)) : entry.approximatedValueAt(k); + if (approxValue < 0) { approxValue = 0; } - D approximatedKnnDist = getDistanceQuery().getDistanceFactory().fromDouble(approxValue); + D approximatedKnnDist = getDistanceFactory().fromDouble(approxValue); - if(distance.compareTo(approximatedKnnDist) <= 0) { + if (distance.compareTo(approximatedKnnDist) <= 0) { result.add(distance, entry.getRoutingObjectID()); } } @@ -222,69 +201,69 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree * @return the value of the k_max parameter */ public int getK_max() { - return k_max; + return settings.k_max; } /** * Determines the maximum and minimum number of entries in a node. */ @Override - protected void initializeCapacities(MkAppEntry<D> exampleLeaf) { - int distanceSize = exampleLeaf.getParentDistance().externalizableSize(); + protected void initializeCapacities(MkAppEntry exampleLeaf) { + int distanceSize = ByteArrayUtil.SIZE_DOUBLE; // exampleLeaf.getParentDistance().externalizableSize(); // overhead = index(4), numEntries(4), id(4), isLeaf(0.125) double overhead = 12.125; - if(getPageSize() - overhead < 0) { + if (getPageSize() - overhead < 0) { throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); } // dirCapacity = (file.getPageSize() - overhead) / (nodeID + objectID + // coveringRadius + parentDistance + approx) + 1 - dirCapacity = (int) (getPageSize() - overhead) / (4 + 4 + distanceSize + distanceSize + (p + 1) * 4 + 2) + 1; + dirCapacity = (int) (getPageSize() - overhead) / (4 + 4 + distanceSize + distanceSize + (settings.p + 1) * 4 + 2) + 1; - if(dirCapacity <= 1) { + if (dirCapacity <= 1) { throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); } - if(dirCapacity < 10) { + if (dirCapacity < 10) { LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a directory node = " + (dirCapacity - 1)); } // leafCapacity = (file.getPageSize() - overhead) / (objectID + // parentDistance + // approx) + 1 - leafCapacity = (int) (getPageSize() - overhead) / (4 + distanceSize + (p + 1) * 4 + 2) + 1; + leafCapacity = (int) (getPageSize() - overhead) / (4 + distanceSize + (settings.p + 1) * 4 + 2) + 1; - if(leafCapacity <= 1) { + if (leafCapacity <= 1) { throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); } - if(leafCapacity < 10) { + if (leafCapacity < 10) { LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a leaf node = " + (leafCapacity - 1)); } initialized = true; - if(LOG.isVerbose()) { + if (LOG.isVerbose()) { LOG.verbose("Directory Capacity: " + (dirCapacity - 1) + "\nLeaf Capacity: " + (leafCapacity - 1)); } } - private List<D> getMeanKNNList(DBIDs ids, Map<DBID, KNNResult<D>> knnLists) { - double[] means = new double[k_max]; - for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) { + private List<D> getMeanKNNList(DBIDs ids, Map<DBID, KNNList<D>> knnLists) { + double[] means = new double[settings.k_max]; + for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) { DBID id = DBIDUtil.deref(iter); - KNNResult<D> knns = knnLists.get(id); + KNNList<D> knns = knnLists.get(id); int k = 0; - for(DistanceDBIDResultIter<D> it = knns.iter(); k < k_max && it.valid(); it.advance(), k++) { + for (DistanceDBIDListIter<D> it = knns.iter(); k < settings.k_max && it.valid(); it.advance(), k++) { means[k] += it.getDistance().doubleValue(); } } - List<D> result = new ArrayList<D>(); - for(int k = 0; k < k_max; k++) { + List<D> result = new ArrayList<>(); + for (int k = 0; k < settings.k_max; k++) { means[k] /= ids.size(); - result.add(getDistanceQuery().getDistanceFactory().fromDouble(means[k])); + result.add(getDistanceFactory().fromDouble(means[k])); } return result; @@ -296,21 +275,20 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree * @param entry the root entry of the current subtree * @param knnLists a map of knn lists for each leaf entry */ - private void adjustApproximatedKNNDistances(MkAppEntry<D> entry, Map<DBID, KNNResult<D>> knnLists) { + private void adjustApproximatedKNNDistances(MkAppEntry entry, Map<DBID, KNNList<D>> knnLists) { MkAppTreeNode<O, D> node = getNode(entry); - if(node.isLeaf()) { - for(int i = 0; i < node.getNumEntries(); i++) { - MkAppLeafEntry<D> leafEntry = (MkAppLeafEntry<D>) node.getEntry(i); + if (node.isLeaf()) { + for (int i = 0; i < node.getNumEntries(); i++) { + MkAppLeafEntry leafEntry = (MkAppLeafEntry) node.getEntry(i); // approximateKnnDistances(leafEntry, // getKNNList(leafEntry.getRoutingObjectID(), knnLists)); PolynomialApproximation approx = approximateKnnDistances(getMeanKNNList(leafEntry.getDBID(), knnLists)); leafEntry.setKnnDistanceApproximation(approx); } - } - else { - for(int i = 0; i < node.getNumEntries(); i++) { - MkAppEntry<D> dirEntry = node.getEntry(i); + } else { + for (int i = 0; i < node.getNumEntries(); i++) { + MkAppEntry dirEntry = node.getEntry(i); adjustApproximatedKNNDistances(dirEntry, knnLists); } } @@ -330,14 +308,13 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree * in the specified subtree */ private void leafEntryIDs(MkAppTreeNode<O, D> node, ModifiableDBIDs result) { - if(node.isLeaf()) { - for(int i = 0; i < node.getNumEntries(); i++) { - MkAppEntry<D> entry = node.getEntry(i); + if (node.isLeaf()) { + for (int i = 0; i < node.getNumEntries(); i++) { + MkAppEntry entry = node.getEntry(i); result.add(((LeafEntry) entry).getDBID()); } - } - else { - for(int i = 0; i < node.getNumEntries(); i++) { + } else { + for (int i = 0; i < node.getNumEntries(); i++) { MkAppTreeNode<O, D> childNode = getNode(node.getEntry(i)); leafEntryIDs(childNode, result); } @@ -355,36 +332,34 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree // count the zero distances (necessary of log-log space is used) int k_0 = 0; - if(log) { - for(int i = 0; i < k_max; i++) { + if (settings.log) { + for (int i = 0; i < settings.k_max; i++) { double dist = knnDistances.get(i).doubleValue(); - if(dist == 0) { + if (dist == 0) { k_0++; - } - else { + } else { break; } } } - de.lmu.ifi.dbs.elki.math.linearalgebra.Vector x = new de.lmu.ifi.dbs.elki.math.linearalgebra.Vector(k_max - k_0); - de.lmu.ifi.dbs.elki.math.linearalgebra.Vector y = new de.lmu.ifi.dbs.elki.math.linearalgebra.Vector(k_max - k_0); + de.lmu.ifi.dbs.elki.math.linearalgebra.Vector x = new de.lmu.ifi.dbs.elki.math.linearalgebra.Vector(settings.k_max - k_0); + de.lmu.ifi.dbs.elki.math.linearalgebra.Vector y = new de.lmu.ifi.dbs.elki.math.linearalgebra.Vector(settings.k_max - k_0); - for(int k = 0; k < k_max - k_0; k++) { - if(log) { + for (int k = 0; k < settings.k_max - k_0; k++) { + if (settings.log) { x.set(k, Math.log(k + k_0)); y.set(k, Math.log(knnDistances.get(k + k_0).doubleValue())); - } - else { + } else { x.set(k, k + k_0); y.set(k, knnDistances.get(k + k_0).doubleValue()); } } - PolynomialRegression regression = new PolynomialRegression(y, x, p); + PolynomialRegression regression = new PolynomialRegression(y, x, settings.p); PolynomialApproximation approximation = new PolynomialApproximation(regression.getEstimatedCoefficients().getArrayCopy()); - if(LOG.isDebugging()) { + if (LOG.isDebugging()) { msg.append("approximation ").append(approximation); LOG.debugFine(msg.toString()); } @@ -399,7 +374,7 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree */ @Override protected MkAppTreeNode<O, D> createNewLeafNode() { - return new MkAppTreeNode<O, D>(leafCapacity, true); + return new MkAppTreeNode<>(leafCapacity, true); } /** @@ -409,7 +384,7 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree */ @Override protected MkAppTreeNode<O, D> createNewDirectoryNode() { - return new MkAppTreeNode<O, D>(dirCapacity, false); + return new MkAppTreeNode<>(dirCapacity, false); } /** @@ -421,8 +396,8 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree * the routing object of the parent node */ @Override - protected MkAppEntry<D> createNewDirectoryEntry(MkAppTreeNode<O, D> node, DBID routingObjectID, D parentDistance) { - return new MkAppDirectoryEntry<D>(routingObjectID, parentDistance, node.getPageID(), node.coveringRadius(routingObjectID, this), null); + protected MkAppEntry createNewDirectoryEntry(MkAppTreeNode<O, D> node, DBID routingObjectID, double parentDistance) { + return new MkAppDirectoryEntry(routingObjectID, parentDistance, node.getPageID(), node.coveringRadius(routingObjectID, this), null); } /** @@ -431,12 +406,12 @@ public class MkAppTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree * @return an entry representing the root node */ @Override - protected MkAppEntry<D> createRootEntry() { - return new MkAppDirectoryEntry<D>(null, null, 0, null, null); + protected MkAppEntry createRootEntry() { + return new MkAppDirectoryEntry(null, 0., 0, 0., null); } @Override protected Logging getLogger() { return LOG; } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeFactory.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeFactory.java index c0f12895..45a2e85f 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeFactory.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeFactory.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkapp; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -24,10 +24,10 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkapp; */ 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.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeFactory; import de.lmu.ifi.dbs.elki.persistent.PageFile; +import de.lmu.ifi.dbs.elki.persistent.PageFileFactory; import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil; import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID; import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint; @@ -46,7 +46,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter; * @param <O> Object type * @param <D> Distance type */ -public class MkAppTreeFactory<O, D extends NumberDistance<D, ?>> extends AbstractMTreeFactory<O, D, MkAppTreeNode<O, D>, MkAppEntry<D>, MkAppTreeIndex<O, D>> { +public class MkAppTreeFactory<O, D extends NumberDistance<D, ?>> extends AbstractMTreeFactory<O, D, MkAppTreeNode<O, D>, MkAppEntry, MkAppTreeIndex<O, D>, MkAppTreeSettings<O, D>> { /** * Parameter for nolog */ @@ -63,42 +63,19 @@ public class MkAppTreeFactory<O, D extends NumberDistance<D, ?>> extends Abstrac public static final OptionID P_ID = new OptionID("mkapp.p", "positive integer specifying the order of the polynomial approximation."); /** - * Parameter k. - */ - private int k_max; - - /** - * Parameter p. - */ - private int p; - - /** - * Flag log. - */ - private boolean log; - - /** * Constructor. * - * @param fileName - * @param pageSize - * @param cacheSize - * @param distanceFunction - * @param k_max - * @param p - * @param log + * @param pageFileFactory Data storage + * @param settings Tree settings */ - public MkAppTreeFactory(String fileName, int pageSize, long cacheSize, DistanceFunction<O, D> distanceFunction, int k_max, int p, boolean log) { - super(fileName, pageSize, cacheSize, distanceFunction); - this.k_max = k_max; - this.p = p; - this.log = log; + public MkAppTreeFactory(PageFileFactory<?> pageFileFactory, MkAppTreeSettings<O, D> settings) { + super(pageFileFactory, settings); } @Override public MkAppTreeIndex<O, D> instantiate(Relation<O> relation) { PageFile<MkAppTreeNode<O, D>> pagefile = makePageFile(getNodeClass()); - return new MkAppTreeIndex<O, D>(relation, pagefile, distanceFunction.instantiate(relation), distanceFunction, k_max, p, log); + return new MkAppTreeIndex<>(relation, pagefile, settings); } protected Class<MkAppTreeNode<O, D>> getNodeClass() { @@ -112,46 +89,36 @@ public class MkAppTreeFactory<O, D extends NumberDistance<D, ?>> extends Abstrac * * @apiviz.exclude */ - public static class Parameterizer<O, D extends NumberDistance<D, ?>> extends AbstractMTreeFactory.Parameterizer<O, D> { - /** - * Parameter k. - */ - protected int k_max; - - /** - * Parameter p. - */ - protected int p; - - /** - * Flag log. - */ - protected boolean log; - + public static class Parameterizer<O, D extends NumberDistance<D, ?>> extends AbstractMTreeFactory.Parameterizer<O, D, MkAppTreeNode<O, D>, MkAppEntry, MkAppTreeSettings<O, D>> { @Override protected void makeOptions(Parameterization config) { super.makeOptions(config); IntParameter kP = new IntParameter(K_ID); kP.addConstraint(new GreaterConstraint(0)); - if(config.grab(kP)) { - k_max = kP.getValue(); + if (config.grab(kP)) { + settings.k_max = kP.getValue(); } IntParameter pP = new IntParameter(P_ID); pP.addConstraint(new GreaterConstraint(0)); - if(config.grab(pP)) { - p = pP.getValue(); + if (config.grab(pP)) { + settings.p = pP.getValue(); } Flag nologF = new Flag(NOLOG_ID); - if(config.grab(nologF)) { - log = !nologF.getValue(); + if (config.grab(nologF)) { + settings.log = !nologF.getValue(); } } @Override protected MkAppTreeFactory<O, D> makeInstance() { - return new MkAppTreeFactory<O, D>(fileName, pageSize, cacheSize, distanceFunction, k_max, p, log); + return new MkAppTreeFactory<>(pageFileFactory, settings); + } + + @Override + protected MkAppTreeSettings<O, D> makeSettings() { + return new MkAppTreeSettings<>(); } } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeIndex.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeIndex.java index 9776de63..2a630bf0 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeIndex.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeIndex.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkapp; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -28,9 +28,7 @@ import java.util.List; import de.lmu.ifi.dbs.elki.database.ids.DBID; import de.lmu.ifi.dbs.elki.database.ids.DBIDIter; -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.DBIDs; import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery; import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery; @@ -43,12 +41,9 @@ import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.KNNIndex; import de.lmu.ifi.dbs.elki.index.RKNNIndex; import de.lmu.ifi.dbs.elki.index.RangeIndex; -import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; -import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTree; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MTreeQueryUtil; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MkTreeRKNNQuery; import de.lmu.ifi.dbs.elki.persistent.PageFile; -import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages; /** * MkAppTree used as database index. @@ -69,16 +64,11 @@ public class MkAppTreeIndex<O, D extends NumberDistance<D, ?>> extends MkAppTree * * @param relation Relation to index * @param pageFile Page file - * @param distanceQuery Distance query - * @param distanceFunction Distance function - * @param k_max Maximum value of k supported - * @param p Parameter p - * @param log Logspace flag + * @param settings Tree settings */ - public MkAppTreeIndex(Relation<O> relation, PageFile<MkAppTreeNode<O, D>> pageFile, DistanceQuery<O, D> distanceQuery, DistanceFunction<O, D> distanceFunction, int k_max, int p, boolean log) { - super(pageFile, distanceQuery, distanceFunction, k_max, p, log); + public MkAppTreeIndex(Relation<O> relation, PageFile<MkAppTreeNode<O, D>> pageFile, MkAppTreeSettings<O, D> settings) { + super(relation, pageFile, settings); this.relation = relation; - this.initialize(); } /** @@ -89,119 +79,88 @@ public class MkAppTreeIndex<O, D extends NumberDistance<D, ?>> extends MkAppTree * @param parentDistance the distance from the object to the routing object of * the parent node */ - protected MkAppEntry<D> createNewLeafEntry(DBID id, O object, D parentDistance) { - return new MkAppLeafEntry<D>(id, parentDistance, null); + protected MkAppEntry createNewLeafEntry(DBID id, O object, double parentDistance) { + return new MkAppLeafEntry(id, parentDistance, null); } @Override - public void insert(DBIDRef id) { - throw new UnsupportedOperationException("Insertion of single objects is not supported!"); - } - - @Override - public void insertAll(DBIDs ids) { - List<MkAppEntry<D>> objs = new ArrayList<MkAppEntry<D>>(ids.size()); - for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) { + public void initialize() { + super.initialize(); + List<MkAppEntry> objs = new ArrayList<>(relation.size()); + for (DBIDIter iter = relation.iterDBIDs(); iter.valid(); iter.advance()) { DBID id = DBIDUtil.deref(iter); final O object = relation.get(id); - objs.add(createNewLeafEntry(id, object, getDistanceFactory().undefinedDistance())); + objs.add(createNewLeafEntry(id, object, Double.NaN)); } insertAll(objs); } - /** - * Throws an UnsupportedOperationException since deletion of objects is not - * yet supported by an M-Tree. - * - * @throws UnsupportedOperationException thrown, since deletions aren't - * implemented yet. - */ - @Override - public final boolean delete(DBIDRef id) { - throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET); - } - - /** - * Throws an UnsupportedOperationException since deletion of objects is not - * yet supported by an M-Tree. - * - * @throws UnsupportedOperationException thrown, since deletions aren't - * implemented yet. - */ - @Override - public void deleteAll(DBIDs ids) { - throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET); - } - @SuppressWarnings("unchecked") @Override public <S extends Distance<S>> KNNQuery<O, S> getKNNQuery(DistanceQuery<O, S> distanceQuery, Object... hints) { // Query on the relation we index - if(distanceQuery.getRelation() != relation) { + if (distanceQuery.getRelation() != relation) { return null; } - DistanceFunction<? super O, S> distanceFunction = distanceQuery.getDistanceFunction(); - if(!this.distanceFunction.equals(distanceFunction)) { - if(getLogger().isDebugging()) { + DistanceFunction<? super O, D> distanceFunction = (DistanceFunction<? super O, D>) distanceQuery.getDistanceFunction(); + if (!this.getDistanceFunction().equals(distanceFunction)) { + if (getLogger().isDebugging()) { getLogger().debug("Distance function not supported by index - or 'equals' not implemented right!"); } return null; } // Bulk is not yet supported - for(Object hint : hints) { - if(hint == DatabaseQuery.HINT_BULK) { + for (Object hint : hints) { + if (hint == DatabaseQuery.HINT_BULK) { return null; } } - AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this; - DistanceQuery<O, S> dq = distanceFunction.instantiate(relation); - return MTreeQueryUtil.getKNNQuery(idx, dq, hints); + DistanceQuery<O, D> dq = distanceFunction.instantiate(relation); + return (KNNQuery<O, S>) MTreeQueryUtil.getKNNQuery(this, dq, hints); } @SuppressWarnings("unchecked") @Override public <S extends Distance<S>> RangeQuery<O, S> getRangeQuery(DistanceQuery<O, S> distanceQuery, Object... hints) { // Query on the relation we index - if(distanceQuery.getRelation() != relation) { + if (distanceQuery.getRelation() != relation) { return null; } - DistanceFunction<? super O, S> distanceFunction = distanceQuery.getDistanceFunction(); - if(!this.distanceFunction.equals(distanceFunction)) { - if(getLogger().isDebugging()) { + DistanceFunction<? super O, D> distanceFunction = (DistanceFunction<? super O, D>) distanceQuery.getDistanceFunction(); + if (!this.getDistanceFunction().equals(distanceFunction)) { + if (getLogger().isDebugging()) { getLogger().debug("Distance function not supported by index - or 'equals' not implemented right!"); } return null; } // Bulk is not yet supported - for(Object hint : hints) { - if(hint == DatabaseQuery.HINT_BULK) { + for (Object hint : hints) { + if (hint == DatabaseQuery.HINT_BULK) { return null; } } - AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this; - DistanceQuery<O, S> dq = distanceFunction.instantiate(relation); - return MTreeQueryUtil.getRangeQuery(idx, dq, hints); + DistanceQuery<O, D> dq = distanceFunction.instantiate(relation); + return (RangeQuery<O, S>) MTreeQueryUtil.getRangeQuery(this, dq, hints); } @SuppressWarnings("unchecked") @Override public <S extends Distance<S>> RKNNQuery<O, S> getRKNNQuery(DistanceQuery<O, S> distanceQuery, Object... hints) { - DistanceFunction<? super O, S> distanceFunction = distanceQuery.getDistanceFunction(); - if(!this.getDistanceFunction().equals(distanceFunction)) { - if(getLogger().isDebugging()) { + DistanceFunction<? super O, D> distanceFunction = (DistanceFunction<? super O, D>) distanceQuery.getDistanceFunction(); + if (!this.getDistanceFunction().equals(distanceFunction)) { + if (getLogger().isDebugging()) { getLogger().debug("Distance function not supported by index - or 'equals' not implemented right!"); } return null; } // Bulk is not yet supported - for(Object hint : hints) { - if(hint == DatabaseQuery.HINT_BULK) { + for (Object hint : hints) { + if (hint == DatabaseQuery.HINT_BULK) { return null; } } - AbstractMkTree<O, S, ?, ?> idx = (AbstractMkTree<O, S, ?, ?>) this; - DistanceQuery<O, S> dq = distanceFunction.instantiate(relation); - return new MkTreeRKNNQuery<O, S>(idx, dq); + DistanceQuery<O, D> dq = distanceFunction.instantiate(relation); + return (RKNNQuery<O, S>) new MkTreeRKNNQuery<>(this, dq); } @Override @@ -213,4 +172,4 @@ public class MkAppTreeIndex<O, D extends NumberDistance<D, ?>> extends MkAppTree public String getShortName() { return "mkapptree"; } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeNode.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeNode.java index c730eb4f..29609274 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeNode.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeNode.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkapp; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -43,8 +43,8 @@ import de.lmu.ifi.dbs.elki.utilities.FormatUtil; * @param <O> object type * @param <D> distance type */ -class MkAppTreeNode<O, D extends NumberDistance<D, ?>> extends AbstractMTreeNode<O, D, MkAppTreeNode<O, D>, MkAppEntry<D>> { - private static final long serialVersionUID = 1; +class MkAppTreeNode<O, D extends NumberDistance<D, ?>> extends AbstractMTreeNode<O, D, MkAppTreeNode<O, D>, MkAppEntry> { + private static final long serialVersionUID = 2; /** * Empty constructor for Externalizable interface. @@ -75,7 +75,7 @@ class MkAppTreeNode<O, D extends NumberDistance<D, ?>> extends AbstractMTreeNode int p_max = 0; double[] b = null; for(int i = 0; i < getNumEntries(); i++) { - MkAppEntry<D> entry = getEntry(i); + MkAppEntry entry = getEntry(i); PolynomialApproximation approximation = entry.getKnnDistanceApproximation(); if(b == null) { p_max = approximation.getPolynomialOrder(); @@ -109,16 +109,16 @@ class MkAppTreeNode<O, D extends NumberDistance<D, ?>> extends AbstractMTreeNode * @param mTree the M-Tree object holding this node */ @Override - public void adjustEntry(MkAppEntry<D> entry, DBID routingObjectID, D parentDistance, AbstractMTree<O, D, MkAppTreeNode<O, D>, MkAppEntry<D>> mTree) { + public void adjustEntry(MkAppEntry entry, DBID routingObjectID, double parentDistance, AbstractMTree<O, D, MkAppTreeNode<O, D>, MkAppEntry, ?> mTree) { super.adjustEntry(entry, routingObjectID, parentDistance, mTree); // entry.setKnnDistanceApproximation(knnDistanceApproximation()); } @Override - protected void integrityCheckParameters(MkAppEntry<D> parentEntry, MkAppTreeNode<O, D> parent, int index, AbstractMTree<O, D, MkAppTreeNode<O, D>, MkAppEntry<D>> mTree) { + protected void integrityCheckParameters(MkAppEntry parentEntry, MkAppTreeNode<O, D> parent, int index, AbstractMTree<O, D, MkAppTreeNode<O, D>, MkAppEntry, ?> mTree) { super.integrityCheckParameters(parentEntry, parent, index, mTree); - MkAppEntry<D> entry = parent.getEntry(index); + MkAppEntry entry = parent.getEntry(index); PolynomialApproximation approximation_soll = knnDistanceApproximation(); PolynomialApproximation approximation_ist = entry.getKnnDistanceApproximation(); diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeSettings.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeSettings.java new file mode 100644 index 00000000..b9e0b8aa --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/MkAppTreeSettings.java @@ -0,0 +1,47 @@ +package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkapp; + +/* + This file is part of ELKI: + Environment for Developing KDD-Applications Supported by Index-Structures + + Copyright (C) 2013 + 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.distance.distancevalue.NumberDistance; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.MkTreeSettings; + +/** + * Settings class for the MkApp Tree. + * + * @author Erich Schubert + * + * @param <O> Object type + * @param <D> Distance type + */ +public class MkAppTreeSettings<O, D extends NumberDistance<D, ?>> extends MkTreeSettings<O, D, MkAppTreeNode<O, D>, MkAppEntry> { + /** + * Parameter p. + */ + protected int p; + + /** + * Flag log. + */ + protected boolean log; +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/PolynomialApproximation.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/PolynomialApproximation.java index 2ad3558e..dc28b7c3 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/PolynomialApproximation.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/PolynomialApproximation.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkapp; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/package-info.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/package-info.java index 0f3cfde9..7b897fe3 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/package-info.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkapp/package-info.java @@ -5,7 +5,7 @@ This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures -Copyright (C) 2012 +Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/ApproximationLine.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/ApproximationLine.java index 7f2aa3fe..318c437b 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/ApproximationLine.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/ApproximationLine.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkcop; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -28,9 +28,6 @@ import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; -import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; -import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; - /** * Provides an approximation for knn-distances line consisting of incline m, * axes intercept t and a start value for k. @@ -111,7 +108,7 @@ public class ApproximationLine implements Externalizable { * @return the function value of the approximation line at the specified k */ public double getValueAt(int k) { - if(k < k_0) { + if (k < k_0) { return Double.POSITIVE_INFINITY; } return m * StrictMath.log(k) + t; @@ -120,17 +117,14 @@ public class ApproximationLine implements Externalizable { /** * Returns the approximated knn-distance at the specified k. * - * @param <O> Object type - * @param <D> Distance type * @param k the value for which the knn-distance should be returned - * @param distanceFunction the distance function * @return the approximated knn-distance at the specified k */ - public <O, D extends NumberDistance<D, ?>> D getApproximatedKnnDistance(int k, DistanceQuery<O, D> distanceFunction) { - if(k < k_0) { - return distanceFunction.nullDistance(); + public double getApproximatedKnnDistance(int k) { + if (k < k_0) { + return 0.; } - return distanceFunction.getDistanceFactory().parseString("" + StrictMath.exp(getValueAt(k))); + return Math.exp(getValueAt(k)); } /** @@ -171,10 +165,10 @@ public class ApproximationLine implements Externalizable { */ @Override public boolean equals(Object o) { - if(this == o) { + if (this == o) { return true; } - if(o == null || getClass() != o.getClass()) { + if (o == null || getClass() != o.getClass()) { return false; } @@ -208,4 +202,4 @@ public class ApproximationLine implements Externalizable { public String toString() { return "m = " + m + ", t = " + t + " k_0 " + k_0; } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/ConvexHull.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/ConvexHull.java index 6787aa9c..fe0e20b0 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/ConvexHull.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/ConvexHull.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkcop; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPDirectoryEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPDirectoryEntry.java index 22b4efa4..8b6282a3 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPDirectoryEntry.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPDirectoryEntry.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkcop; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -28,8 +28,6 @@ import java.io.ObjectInput; import java.io.ObjectOutput; import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; -import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeDirectoryEntry; /** @@ -39,8 +37,11 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeDirectoryEntry * * @author Elke Achtert */ -class MkCoPDirectoryEntry<D extends NumberDistance<D, ?>> extends MTreeDirectoryEntry<D> implements MkCoPEntry<D> { - private static final long serialVersionUID = 1; +class MkCoPDirectoryEntry extends MTreeDirectoryEntry implements MkCoPEntry { + /** + * Serialization version number. + */ + private static final long serialVersionUID = 2; /** * The conservative approximation. @@ -64,7 +65,7 @@ class MkCoPDirectoryEntry<D extends NumberDistance<D, ?>> extends MTreeDirectory * @param conservativeApproximation the conservative approximation of the knn * distances */ - public MkCoPDirectoryEntry(DBID objectID, D parentDistance, Integer nodeID, D coveringRadius, ApproximationLine conservativeApproximation) { + public MkCoPDirectoryEntry(DBID objectID, double parentDistance, Integer nodeID, double coveringRadius, ApproximationLine conservativeApproximation) { super(objectID, parentDistance, nodeID, coveringRadius); this.conservativeApproximation = conservativeApproximation; } @@ -73,12 +74,11 @@ class MkCoPDirectoryEntry<D extends NumberDistance<D, ?>> extends MTreeDirectory * Returns the conservative approximated knn distance of the entry. * * @param k the parameter k of the knn distance - * @param distanceFunction the distance function * @return the conservative approximated knn distance of the entry */ @Override - public <O> D approximateConservativeKnnDistance(int k, DistanceQuery<O, D> distanceFunction) { - return conservativeApproximation.getApproximatedKnnDistance(k, distanceFunction); + public double approximateConservativeKnnDistance(int k) { + return conservativeApproximation.getApproximatedKnnDistance(k); } /** @@ -138,20 +138,19 @@ class MkCoPDirectoryEntry<D extends NumberDistance<D, ?>> extends MTreeDirectory * and has the same conservative approximation as this entry. */ @Override - @SuppressWarnings("unchecked") public boolean equals(Object o) { - if(this == o) { + if (this == o) { return true; } - if(o == null || getClass() != o.getClass()) { + if (o == null || getClass() != o.getClass()) { return false; } - if(!super.equals(o)) { + if (!super.equals(o)) { return false; } - final MkCoPDirectoryEntry<D> that = (MkCoPDirectoryEntry<D>) o; + final MkCoPDirectoryEntry that = (MkCoPDirectoryEntry) o; return !(conservativeApproximation != null ? !conservativeApproximation.equals(that.conservativeApproximation) : that.conservativeApproximation != null); } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPEntry.java index 077fb1ec..ae569d03 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPEntry.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPEntry.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkcop; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -23,8 +23,6 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkcop; along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; -import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; /** @@ -36,17 +34,14 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; * * @apiviz.composedOf ApproximationLine */ -interface MkCoPEntry<D extends NumberDistance<D, ?>> extends MTreeEntry<D> { - +interface MkCoPEntry extends MTreeEntry { /** * Returns the conservative approximated knn distance of the entry. * - * @param <O> Object type * @param k the parameter k of the knn distance - * @param distanceFunction the distance function * @return the conservative approximated knn distance of the entry */ - public <O> D approximateConservativeKnnDistance(int k, DistanceQuery<O, D> distanceFunction); + public double approximateConservativeKnnDistance(int k); /** * Returns the conservative approximation line. @@ -62,4 +57,4 @@ interface MkCoPEntry<D extends NumberDistance<D, ?>> extends MTreeEntry<D> { * set */ public void setConservativeKnnDistanceApproximation(ApproximationLine conservativeApproximation); -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPLeafEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPLeafEntry.java index 0daf7950..7d241eba 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPLeafEntry.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPLeafEntry.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkcop; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -28,8 +28,6 @@ import java.io.ObjectInput; import java.io.ObjectOutput; import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; -import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeLeafEntry; /** @@ -39,8 +37,11 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeLeafEntry; * * @author Elke Achtert */ -class MkCoPLeafEntry<D extends NumberDistance<D, ?>> extends MTreeLeafEntry<D> implements MkCoPEntry<D> { - private static final long serialVersionUID = 1; +class MkCoPLeafEntry extends MTreeLeafEntry implements MkCoPEntry { + /** + * Serialization version ID. + */ + private static final long serialVersionUID = 2; /** * The conservative approximation. @@ -70,7 +71,7 @@ class MkCoPLeafEntry<D extends NumberDistance<D, ?>> extends MTreeLeafEntry<D> i * @param progressiveApproximation the progressive approximation of the knn * distances */ - public MkCoPLeafEntry(DBID objectID, D parentDistance, ApproximationLine conservativeApproximation, ApproximationLine progressiveApproximation) { + public MkCoPLeafEntry(DBID objectID, double parentDistance, ApproximationLine conservativeApproximation, ApproximationLine progressiveApproximation) { super(objectID, parentDistance); this.conservativeApproximation = conservativeApproximation; this.progressiveApproximation = progressiveApproximation; @@ -80,24 +81,21 @@ class MkCoPLeafEntry<D extends NumberDistance<D, ?>> extends MTreeLeafEntry<D> i * Returns the conservative approximated knn distance of the entry. * * @param k the parameter k of the knn distance - * @param distanceFunction the distance function * @return the conservative approximated knn distance of the entry */ @Override - public <O> D approximateConservativeKnnDistance(int k, DistanceQuery<O, D> distanceFunction) { - return conservativeApproximation.getApproximatedKnnDistance(k, distanceFunction); + public double approximateConservativeKnnDistance(int k) { + return conservativeApproximation.getApproximatedKnnDistance(k); } /** * Returns the progressive approximated knn distance of the entry. * - * @param <O> Object type * @param k the parameter k of the knn distance - * @param distanceFunction the distance function * @return the progressive approximated knn distance of the entry */ - public <O> D approximateProgressiveKnnDistance(int k, DistanceQuery<O, D> distanceFunction) { - return progressiveApproximation.getApproximatedKnnDistance(k, distanceFunction); + public double approximateProgressiveKnnDistance(int k) { + return progressiveApproximation.getApproximatedKnnDistance(k); } /** @@ -180,21 +178,20 @@ class MkCoPLeafEntry<D extends NumberDistance<D, ?>> extends MTreeLeafEntry<D> i * entry. */ @Override - @SuppressWarnings("unchecked") public boolean equals(Object o) { - if(this == o) { + if (this == o) { return true; } - if(o == null || getClass() != o.getClass()) { + if (o == null || getClass() != o.getClass()) { return false; } - if(!super.equals(o)) { + if (!super.equals(o)) { return false; } - final MkCoPLeafEntry<D> that = (MkCoPLeafEntry<D>) o; + final MkCoPLeafEntry that = (MkCoPLeafEntry) o; - if(conservativeApproximation != null ? !conservativeApproximation.equals(that.conservativeApproximation) : that.conservativeApproximation != null) { + if (conservativeApproximation != null ? !conservativeApproximation.equals(that.conservativeApproximation) : that.conservativeApproximation != null) { return false; } @@ -211,4 +208,4 @@ class MkCoPLeafEntry<D extends NumberDistance<D, ?>> extends MTreeLeafEntry<D> i return super.toString() + "\ncons " + conservativeApproximation + "\n"; // "prog " + progressiveApproximation; } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTree.java index 562f7f4a..b98d6821 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTree.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTree.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkcop; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -32,20 +32,20 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDIter; 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.distance.DistanceQuery; -import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult; +import de.lmu.ifi.dbs.elki.database.ids.distance.DistanceDBIDList; +import de.lmu.ifi.dbs.elki.database.ids.distance.DistanceDBIDListIter; +import de.lmu.ifi.dbs.elki.database.ids.distance.KNNList; +import de.lmu.ifi.dbs.elki.database.ids.generic.GenericDistanceDBIDList; +import de.lmu.ifi.dbs.elki.database.relation.Relation; import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTree; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.MkTreeSettings; import de.lmu.ifi.dbs.elki.index.tree.query.GenericMTreeDistanceSearchCandidate; import de.lmu.ifi.dbs.elki.logging.Logging; +import de.lmu.ifi.dbs.elki.persistent.ByteArrayUtil; import de.lmu.ifi.dbs.elki.persistent.PageFile; import de.lmu.ifi.dbs.elki.utilities.FormatUtil; -import de.lmu.ifi.dbs.elki.utilities.QueryStatistic; -import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap; +import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.ComparableMinHeap; /** * MkCopTree is a metrical index structure based on the concepts of the M-Tree @@ -55,46 +55,34 @@ import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap; * @author Elke Achtert * * @apiviz.has MkCoPTreeNode oneway - - contains - * @apiviz.uses ConvexHull + * @apiviz.has ConvexHull * * @param <O> Object type * @param <D> Distance type */ -public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree<O, D, MkCoPTreeNode<O, D>, MkCoPEntry<D>> { +public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree<O, D, MkCoPTreeNode<O, D>, MkCoPEntry, MkTreeSettings<O, D, MkCoPTreeNode<O, D>, MkCoPEntry>> { /** * The logger for this class. */ private static final Logging LOG = Logging.getLogger(MkCoPTree.class); /** - * Parameter k. - */ - int k_max; - - /** * The values of log(1),..,log(k_max) */ private double[] log_k; /** - * Provides some statistics about performed reverse knn-queries. - */ - private QueryStatistic rkNNStatistics = new QueryStatistic(); - - /** * Constructor. * + * @param relation Relation to index * @param pagefile Page file - * @param distanceQuery Distance query - * @param distanceFunction Distance function - * @param k_max Maximum value of k supported + * @param settings Tree settings */ - public MkCoPTree(PageFile<MkCoPTreeNode<O, D>> pagefile, DistanceQuery<O, D> distanceQuery, DistanceFunction<O, D> distanceFunction, int k_max) { - super(pagefile, distanceQuery, distanceFunction); - this.k_max = k_max; + public MkCoPTree(Relation<O> relation, PageFile<MkCoPTreeNode<O, D>> pagefile, MkTreeSettings<O, D, MkCoPTreeNode<O, D>, MkCoPEntry> settings) { + super(relation, pagefile, settings); // init log k - log_k = new double[k_max]; - for(int k = 1; k <= k_max; k++) { + log_k = new double[settings.k_max]; + for (int k = 1; k <= settings.k_max; k++) { log_k[k - 1] = Math.log(k); } } @@ -103,7 +91,7 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree * @throws UnsupportedOperationException since this operation is not supported */ @Override - protected void preInsert(MkCoPEntry<D> entry) { + protected void preInsert(MkCoPEntry entry) { throw new UnsupportedOperationException("Insertion of single objects is not supported!"); } @@ -111,40 +99,40 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree * @throws UnsupportedOperationException since this operation is not supported */ @Override - public void insert(MkCoPEntry<D> entry, boolean withPreInsert) { + public void insert(MkCoPEntry entry, boolean withPreInsert) { throw new UnsupportedOperationException("Insertion of single objects is not supported!"); } @Override - public void insertAll(List<MkCoPEntry<D>> entries) { - if(entries.isEmpty()) { + public void insertAll(List<MkCoPEntry> entries) { + if (entries.isEmpty()) { return; } - if(LOG.isDebugging()) { + if (LOG.isDebugging()) { LOG.debugFine("insert " + entries + "\n"); } - if(!initialized) { + if (!initialized) { initialize(entries.get(0)); } ModifiableDBIDs ids = DBIDUtil.newArray(entries.size()); // insert - for(MkCoPEntry<D> entry : entries) { + for (MkCoPEntry entry : entries) { ids.add(entry.getRoutingObjectID()); // insert the object super.insert(entry, false); } // perform nearest neighbor queries - Map<DBID, KNNResult<D>> knnLists = batchNN(getRoot(), ids, k_max); + Map<DBID, KNNList<D>> knnLists = batchNN(getRoot(), ids, settings.k_max); // adjust the knn distances adjustApproximatedKNNDistances(getRootEntry(), knnLists); - if(EXTRA_INTEGRITY_CHECKS) { + if (EXTRA_INTEGRITY_CHECKS) { getRoot().integrityCheck(this, getRootEntry()); } } @@ -158,29 +146,30 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree * @return a List of the query results */ @Override - public DistanceDBIDResult<D> reverseKNNQuery(DBIDRef id, int k) { - if(k > this.k_max) { + public DistanceDBIDList<D> reverseKNNQuery(DBIDRef id, int k) { + if (k > settings.k_max) { throw new IllegalArgumentException("Parameter k has to be less or equal than " + "parameter kmax of the MCop-Tree!"); } - GenericDistanceDBIDList<D> result = new GenericDistanceDBIDList<D>(); + GenericDistanceDBIDList<D> result = new GenericDistanceDBIDList<>(); ModifiableDBIDs candidates = DBIDUtil.newArray(); doReverseKNNQuery(k, id, result, candidates); // refinement of candidates - Map<DBID, KNNResult<D>> knnLists = batchNN(getRoot(), candidates, k); + Map<DBID, KNNList<D>> knnLists = batchNN(getRoot(), candidates, k); result.sort(); // Collections.sort(candidates); - rkNNStatistics.addCandidates(candidates.size()); - rkNNStatistics.addTrueHits(result.size()); + // FIXME: re-add statistics. + // rkNNStatistics.addCandidates(candidates.size()); + // rkNNStatistics.addTrueHits(result.size()); - for(DBIDIter iter = candidates.iter(); iter.valid(); iter.advance()) { + for (DBIDIter iter = candidates.iter(); iter.valid(); iter.advance()) { DBID cid = DBIDUtil.deref(iter); - KNNResult<D> cands = knnLists.get(cid); - for (DistanceDBIDResultIter<D> iter2 = cands.iter(); iter2.valid(); iter2.advance()) { - if(DBIDUtil.equal(id, iter2)) { + KNNList<D> cands = knnLists.get(cid); + for (DistanceDBIDListIter<D> iter2 = cands.iter(); iter2.valid(); iter2.advance()) { + if (DBIDUtil.equal(id, iter2)) { result.add(iter2.getDistance(), cid); break; } @@ -188,45 +177,30 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree } result.sort(); - rkNNStatistics.addResults(result.size()); + // FIXME: re-add statistics. + // rkNNStatistics.addResults(result.size()); return result; } /** - * Returns the statistic for performed rknn queries. - * - * @return the statistic for performed rknn queries - */ - public QueryStatistic getRkNNStatistics() { - return rkNNStatistics; - } - - /** - * Clears the values of the statistic for performed rknn queries - */ - public void clearRkNNStatistics() { - rkNNStatistics.clear(); - } - - /** * Returns the value of the k_max parameter. * * @return the value of the k_max parameter */ public int getK_max() { - return k_max; + return settings.k_max; } /** * Determines the maximum and minimum number of entries in a node. */ @Override - protected void initializeCapacities(MkCoPEntry<D> exampleLeaf) { - int distanceSize = exampleLeaf.getParentDistance().externalizableSize(); + protected void initializeCapacities(MkCoPEntry exampleLeaf) { + int distanceSize = ByteArrayUtil.SIZE_DOUBLE; // exampleLeaf.getParentDistance().externalizableSize(); // overhead = index(4), numEntries(4), id(4), isLeaf(0.125) double overhead = 12.125; - if(getPageSize() - overhead < 0) { + if (getPageSize() - overhead < 0) { throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); } @@ -234,11 +208,11 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree // coveringRadius + parentDistance + consApprox) + 1 dirCapacity = (int) (getPageSize() - overhead) / (4 + 4 + distanceSize + distanceSize + 10) + 1; - if(dirCapacity <= 1) { + if (dirCapacity <= 1) { throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); } - if(dirCapacity < 10) { + if (dirCapacity < 10) { LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a directory node = " + (dirCapacity - 1)); } @@ -247,17 +221,17 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree // consApprox + progrApprox) + 1 leafCapacity = (int) (getPageSize() - overhead) / (4 + distanceSize + 2 * 10) + 1; - if(leafCapacity <= 1) { + if (leafCapacity <= 1) { throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); } - if(leafCapacity < 10) { + if (leafCapacity < 10) { LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a leaf node = " + (leafCapacity - 1)); } initialized = true; - if(LOG.isVerbose()) { + if (LOG.isVerbose()) { LOG.verbose("Directory Capacity: " + (dirCapacity - 1) + "\nLeaf Capacity: " + (leafCapacity - 1)); } } @@ -272,45 +246,44 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree * refinement) */ private void doReverseKNNQuery(int k, DBIDRef q, GenericDistanceDBIDList<D> result, ModifiableDBIDs candidates) { - final Heap<GenericMTreeDistanceSearchCandidate<D>> pq = new Heap<GenericMTreeDistanceSearchCandidate<D>>(); + final ComparableMinHeap<GenericMTreeDistanceSearchCandidate> pq = new ComparableMinHeap<>(); // push root - pq.add(new GenericMTreeDistanceSearchCandidate<D>(getDistanceQuery().nullDistance(), getRootID(), null, null)); + pq.add(new GenericMTreeDistanceSearchCandidate(0., getRootID(), null)); // search in tree - while(!pq.isEmpty()) { - GenericMTreeDistanceSearchCandidate<D> pqNode = pq.poll(); + while (!pq.isEmpty()) { + GenericMTreeDistanceSearchCandidate pqNode = pq.poll(); // FIXME: cache the distance to the routing object in the queue node! MkCoPTreeNode<O, D> node = getNode(pqNode.nodeID); // directory node - if(!node.isLeaf()) { - for(int i = 0; i < node.getNumEntries(); i++) { - MkCoPEntry<D> entry = node.getEntry(i); - D distance = getDistanceQuery().distance(entry.getRoutingObjectID(), q); - D minDist = entry.getCoveringRadius().compareTo(distance) > 0 ? getDistanceQuery().nullDistance() : distance.minus(entry.getCoveringRadius()); - D approximatedKnnDist_cons = entry.approximateConservativeKnnDistance(k, getDistanceQuery()); - - if(minDist.compareTo(approximatedKnnDist_cons) <= 0) { - pq.add(new GenericMTreeDistanceSearchCandidate<D>(minDist, getPageID(entry), entry.getRoutingObjectID(), null)); + if (!node.isLeaf()) { + for (int i = 0; i < node.getNumEntries(); i++) { + MkCoPEntry entry = node.getEntry(i); + double distance = distance(entry.getRoutingObjectID(), q).doubleValue(); + double minDist = entry.getCoveringRadius() > distance ? 0. : distance - entry.getCoveringRadius(); + double approximatedKnnDist_cons = entry.approximateConservativeKnnDistance(k); + + if (minDist <= approximatedKnnDist_cons) { + pq.add(new GenericMTreeDistanceSearchCandidate(minDist, getPageID(entry), entry.getRoutingObjectID())); } } } // data node else { - for(int i = 0; i < node.getNumEntries(); i++) { - MkCoPLeafEntry<D> entry = (MkCoPLeafEntry<D>) node.getEntry(i); - D distance = getDistanceQuery().distance(entry.getRoutingObjectID(), q); - D approximatedKnnDist_prog = entry.approximateProgressiveKnnDistance(k, getDistanceQuery()); + for (int i = 0; i < node.getNumEntries(); i++) { + MkCoPLeafEntry entry = (MkCoPLeafEntry) node.getEntry(i); + D distance = distance(entry.getRoutingObjectID(), q); + double approximatedKnnDist_prog = entry.approximateProgressiveKnnDistance(k); - if(distance.compareTo(approximatedKnnDist_prog) <= 0) { + if (distance.doubleValue() <= approximatedKnnDist_prog) { result.add(distance, entry.getRoutingObjectID()); - } - else { - D approximatedKnnDist_cons = entry.approximateConservativeKnnDistance(k, getDistanceQuery()); - double diff = distance.doubleValue() - approximatedKnnDist_cons.doubleValue(); - if(diff <= 0.0000000001) { + } else { + double approximatedKnnDist_cons = entry.approximateConservativeKnnDistance(k); + double diff = distance.doubleValue() - approximatedKnnDist_cons; + if (diff <= 1E-10) { candidates.add(entry.getRoutingObjectID()); } } @@ -325,23 +298,22 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree * @param entry the root entry of the current subtree * @param knnLists a map of knn lists for each leaf entry */ - private void adjustApproximatedKNNDistances(MkCoPEntry<D> entry, Map<DBID, KNNResult<D>> knnLists) { + private void adjustApproximatedKNNDistances(MkCoPEntry entry, Map<DBID, KNNList<D>> knnLists) { MkCoPTreeNode<O, D> node = getNode(entry); - if(node.isLeaf()) { - for(int i = 0; i < node.getNumEntries(); i++) { - MkCoPLeafEntry<D> leafEntry = (MkCoPLeafEntry<D>) node.getEntry(i); + if (node.isLeaf()) { + for (int i = 0; i < node.getNumEntries(); i++) { + MkCoPLeafEntry leafEntry = (MkCoPLeafEntry) node.getEntry(i); approximateKnnDistances(leafEntry, knnLists.get(leafEntry.getRoutingObjectID())); } - } - else { - for(int i = 0; i < node.getNumEntries(); i++) { - MkCoPEntry<D> dirEntry = node.getEntry(i); + } else { + for (int i = 0; i < node.getNumEntries(); i++) { + MkCoPEntry dirEntry = node.getEntry(i); adjustApproximatedKNNDistances(dirEntry, knnLists); } } - ApproximationLine approx = node.conservativeKnnDistanceApproximation(k_max); + ApproximationLine approx = node.conservativeKnnDistanceApproximation(settings.k_max); entry.setConservativeKnnDistanceApproximation(approx); } @@ -351,7 +323,7 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree private double ssqerr(int k0, int kmax, double[] logk, double[] log_kDist, double m, double t) { int k = kmax - k0; double result = 0; - for(int i = 0; i < k; i++) { + for (int i = 0; i < k; i++) { // double h = log_kDist[i] - (m * (logk[i] - logk[0]) + t); ??? double h = log_kDist[i] - m * logk[i] - t; result += h * h; @@ -377,33 +349,32 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree * @param knnDistances TODO: Spezialbehandlung fuer identische Punkte in DB * (insbes. Distanz 0) */ - private void approximateKnnDistances(MkCoPLeafEntry<D> entry, KNNResult<D> knnDistances) { + private void approximateKnnDistances(MkCoPLeafEntry entry, KNNList<D> knnDistances) { StringBuilder msg = LOG.isDebugging() ? new StringBuilder() : null; - if(msg != null) { + if (msg != null) { msg.append("\nknnDistances ").append(knnDistances); } // count the zero distances int k_0 = 0; - for(int i = 0; i < k_max; i++) { + for (int i = 0; i < settings.k_max; i++) { double dist = knnDistances.get(i).getDistance().doubleValue(); - if(dist == 0) { + if (dist == 0) { k_0++; - } - else { + } else { break; } } // init variables - double[] log_k = new double[k_max - k_0]; - System.arraycopy(this.log_k, k_0, log_k, 0, k_max - k_0); + double[] log_k = new double[settings.k_max - k_0]; + System.arraycopy(this.log_k, k_0, log_k, 0, settings.k_max - k_0); double sum_log_kDist = 0; double sum_log_k_kDist = 0; - double[] log_kDist = new double[k_max - k_0]; + double[] log_kDist = new double[settings.k_max - k_0]; - for(int i = 0; i < k_max - k_0; i++) { + for (int i = 0; i < settings.k_max - k_0; i++) { double dist = knnDistances.get(i + k_0).getDistance().doubleValue(); log_kDist[i] = Math.log(dist); sum_log_kDist += log_kDist[i]; @@ -413,14 +384,14 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree double sum_log_k = 0; double sum_log_k2 = 0; // noinspection ForLoopReplaceableByForEach - for(int i = 0; i < log_k.length; i++) { + for (int i = 0; i < log_k.length; i++) { sum_log_k += log_k[i]; sum_log_k2 += (log_k[i] * log_k[i]); } - if(msg != null) { + if (msg != null) { msg.append("\nk_0 ").append(k_0); - msg.append("\nk_max ").append(k_max); + msg.append("\nk_max ").append(settings.k_max); msg.append("\nlog_k(").append(log_k.length).append(") ").append(FormatUtil.format(log_k)); msg.append("\nsum_log_k ").append(sum_log_k); msg.append("\nsum_log_k^2 ").append(sum_log_k2); @@ -438,15 +409,15 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree ApproximationLine c2 = approximateUpperHull_PAPER(convexHull, log_k, sum_log_k, sum_log_k2, log_kDist, sum_log_kDist, sum_log_k_kDist); - double err1 = ssqerr(k_0, k_max, log_k, log_kDist, conservative.getM(), conservative.getT()); - double err2 = ssqerr(k_0, k_max, log_k, log_kDist, c2.getM(), c2.getT()); + double err1 = ssqerr(k_0, settings.k_max, log_k, log_kDist, conservative.getM(), conservative.getT()); + double err2 = ssqerr(k_0, settings.k_max, log_k, log_kDist, c2.getM(), c2.getT()); - if(msg != null) { + if (msg != null) { msg.append("err1 ").append(err1); msg.append("err2 ").append(err2); } - if(err1 > err2 && err1 - err2 > 0.000000001) { + if (err1 > err2 && err1 - err2 > 0.000000001) { // if (err1 > err2) { StringBuilder warning = new StringBuilder(); @@ -460,7 +431,7 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree warning.append("\nconservative1 ").append(conservative); warning.append("\nconservative2 ").append(c2); - for(int i = 0; i < u; i++) { + for (int i = 0; i < u; i++) { warning.append("\nlog_k[").append(upperHull[i]).append("] = ").append(log_k[upperHull[i]]); warning.append("\nlog_kDist[").append(upperHull[i]).append("] = ").append(log_kDist[upperHull[i]]); } @@ -473,7 +444,7 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree entry.setConservativeKnnDistanceApproximation(conservative); entry.setProgressiveKnnDistanceApproximation(progressive); - if(msg != null) { + if (msg != null) { LOG.debugFine(msg.toString()); } } @@ -491,7 +462,7 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree StringBuilder msg = new StringBuilder(); int[] lowerHull = convexHull.getLowerHull(); int l = convexHull.getNumberOfPointsInLowerHull(); - int k_0 = k_max - lowerHull.length + 1; + int k_0 = settings.k_max - lowerHull.length + 1; // linear search on all line segments on the lower convex hull msg.append("lower hull l = ").append(l).append("\n"); @@ -499,12 +470,12 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree double low_m = 0.0; double low_t = 0.0; - for(int i = 1; i < l; i++) { + for (int i = 1; i < l; i++) { double cur_m = (log_kDist[lowerHull[i]] - log_kDist[lowerHull[i - 1]]) / (log_k[lowerHull[i]] - log_k[lowerHull[i - 1]]); double cur_t = log_kDist[lowerHull[i]] - cur_m * log_k[lowerHull[i]]; - double cur_error = ssqerr(k_0, k_max, log_k, log_kDist, cur_m, cur_t); + double cur_error = ssqerr(k_0, settings.k_max, log_k, log_kDist, cur_m, cur_t); msg.append(" Segment = ").append(i).append(" m = ").append(cur_m).append(" t = ").append(cur_t).append(" lowerror = ").append(cur_error).append("\n"); - if(cur_error < low_error) { + if (cur_error < low_error) { low_error = cur_error; low_m = cur_m; low_t = cur_t; @@ -513,13 +484,13 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree // linear search on all points of the lower convex hull boolean is_right = true; // NEEDED FOR PROOF CHECK - for(int i = 0; i < l; i++) { - double cur_m = optimize(k_0, k_max, sum_log_k, sum_log_k2, log_k[lowerHull[i]], log_kDist[lowerHull[i]], sum_log_k_kDist, sum_log_kDist); + for (int i = 0; i < l; i++) { + double cur_m = optimize(k_0, settings.k_max, sum_log_k, sum_log_k2, log_k[lowerHull[i]], log_kDist[lowerHull[i]], sum_log_k_kDist, sum_log_kDist); double cur_t = log_kDist[lowerHull[i]] - cur_m * log_k[lowerHull[i]]; // only valid if both neighboring points are underneath y=mx+t - if((i == 0 || log_kDist[lowerHull[i - 1]] >= log_kDist[lowerHull[i]] - cur_m * (log_k[lowerHull[i]] - log_k[lowerHull[i - 1]])) && (i == l - 1 || log_kDist[lowerHull[i + 1]] >= log_kDist[lowerHull[i]] + cur_m * (log_k[lowerHull[i + 1]] - log_k[lowerHull[i]]))) { - double cur_error = ssqerr(k_0, k_max, log_k, log_kDist, cur_m, cur_t); - if(cur_error < low_error) { + if ((i == 0 || log_kDist[lowerHull[i - 1]] >= log_kDist[lowerHull[i]] - cur_m * (log_k[lowerHull[i]] - log_k[lowerHull[i - 1]])) && (i == l - 1 || log_kDist[lowerHull[i + 1]] >= log_kDist[lowerHull[i]] + cur_m * (log_k[lowerHull[i + 1]] - log_k[lowerHull[i]]))) { + double cur_error = ssqerr(k_0, settings.k_max, log_k, log_kDist, cur_m, cur_t); + if (cur_error < low_error) { low_error = cur_error; low_m = cur_m; low_t = cur_t; @@ -527,9 +498,9 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree } // check proof of bisection search - if(!(i > 0 && log_kDist[lowerHull[i - 1]] < log_kDist[lowerHull[i]] - cur_m * (log_k[lowerHull[i]] - log_k[lowerHull[i - 1]])) && !is_right) { + if (!(i > 0 && log_kDist[lowerHull[i - 1]] < log_kDist[lowerHull[i]] - cur_m * (log_k[lowerHull[i]] - log_k[lowerHull[i - 1]])) && !is_right) { // warning("ERROR lower: The bisection search will not work properly !"); - if(!(i < l - 1 && log_kDist[lowerHull[i + 1]] < log_kDist[lowerHull[i]] + cur_m * (log_k[lowerHull[i + 1]] - log_k[lowerHull[i]]))) { + if (!(i < l - 1 && log_kDist[lowerHull[i + 1]] < log_kDist[lowerHull[i]] + cur_m * (log_k[lowerHull[i + 1]] - log_k[lowerHull[i]]))) { is_right = false; } } @@ -544,18 +515,18 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree int[] upperHull = convexHull.getUpperHull(); int u = convexHull.getNumberOfPointsInUpperHull(); - int k_0 = k_max - upperHull.length + 1; + int k_0 = settings.k_max - upperHull.length + 1; ApproximationLine approx = null; double error = Double.POSITIVE_INFINITY; - for(int i = 0; i < u - 1; i++) { + for (int i = 0; i < u - 1; i++) { int ii = upperHull[i]; int jj = upperHull[i + 1]; double current_m = (log_kDist[jj] - log_kDist[ii]) / (log_k[jj] - log_k[ii]); double current_t = log_kDist[ii] - current_m * log_k[ii]; ApproximationLine current_approx = new ApproximationLine(k_0, current_m, current_t); - if(LOG.isDebugging()) { + if (LOG.isDebugging()) { msg.append("\nlog_kDist[").append(jj).append("] ").append(log_kDist[jj]); msg.append("\nlog_kDist[").append(ii).append("] ").append(log_kDist[ii]); msg.append("\nlog_k[").append(jj).append("] ").append(log_k[jj]); @@ -566,22 +537,22 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree boolean ok = true; double currentError = 0; - for(int k = k_0; k <= k_max; k++) { + for (int k = k_0; k <= settings.k_max; k++) { double appDist = current_approx.getValueAt(k); - if(appDist < log_kDist[k - k_0] && log_kDist[k - k_0] - appDist > 0.000000001) { + if (appDist < log_kDist[k - k_0] && log_kDist[k - k_0] - appDist > 0.000000001) { ok = false; break; } currentError += (appDist - log_kDist[k - k_0]); } - if(ok && currentError < error) { + if (ok && currentError < error) { approx = current_approx; error = currentError; } } - if(LOG.isDebugging()) { + if (LOG.isDebugging()) { msg.append("\nupper Approx ").append(approx); LOG.debugFine(msg.toString()); } @@ -594,22 +565,22 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree int[] upperHull = convexHull.getUpperHull(); int u = convexHull.getNumberOfPointsInUpperHull(); - List<Integer> marked = new ArrayList<Integer>(); + List<Integer> marked = new ArrayList<>(); - int k_0 = k_max - upperHull.length + 1; + int k_0 = settings.k_max - upperHull.length + 1; int a = u / 2; - while(marked.size() != u) { + while (marked.size() != u) { marked.add(a); double x_a = log_k[upperHull[a]]; double y_a = log_kDist[upperHull[a]]; - double m_a = optimize(k_0, k_max, sum_log_k, sum_log_k2, x_a, y_a, sum_log_k_kDist, sum_log_kDist); + double m_a = optimize(k_0, settings.k_max, sum_log_k, sum_log_k2, x_a, y_a, sum_log_k_kDist, sum_log_kDist); double t_a = y_a - m_a * x_a; - if(msg != null) { + if (msg != null) { msg.append("\na=").append(a).append(" m_a=").append(m_a).append(", t_a=").append(t_a); - msg.append("\n err ").append(ssqerr(k_0, k_max, log_k, log_kDist, m_a, m_a)); + msg.append("\n err ").append(ssqerr(k_0, settings.k_max, log_k, log_kDist, m_a, m_a)); } double x_p = a == 0 ? Double.NaN : log_k[upperHull[a - 1]]; @@ -620,24 +591,23 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree boolean lessThanPre = a == 0 || y_p <= m_a * x_p + t_a; boolean lessThanSuc = a == u || y_s <= m_a * x_s + t_a; - if(lessThanPre && lessThanSuc) { + if (lessThanPre && lessThanSuc) { ApproximationLine appr = new ApproximationLine(k_0, m_a, t_a); - if(msg != null) { + if (msg != null) { msg.append("\n1 anchor = ").append(a); LOG.debugFine(msg.toString()); } return appr; - } - else if(!lessThanPre) { - if(marked.contains(a - 1)) { + } else if (!lessThanPre) { + if (marked.contains(a - 1)) { m_a = (y_a - y_p) / (x_a - x_p); - if(y_a == y_p) { + if (y_a == y_p) { m_a = 0; } t_a = y_a - m_a * x_a; ApproximationLine appr = new ApproximationLine(k_0, m_a, t_a); - if(msg != null) { + if (msg != null) { msg.append("2 anchor = ").append(a); msg.append(" appr1 ").append(appr); msg.append(" x_a ").append(x_a).append(", y_a ").append(y_a); @@ -647,28 +617,25 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree LOG.debugFine(msg.toString()); } return appr; - } - else { + } else { a = a - 1; } - } - else { - if(marked.contains(a + 1)) { + } else { + if (marked.contains(a + 1)) { m_a = (y_a - y_s) / (x_a - x_s); - if(y_a == y_p) { + if (y_a == y_p) { m_a = 0; } t_a = y_a - m_a * x_a; ApproximationLine appr = new ApproximationLine(k_0, m_a, t_a); - if(msg != null) { + if (msg != null) { msg.append("3 anchor = ").append(a).append(" -- ").append((a + 1)); msg.append(" appr2 ").append(appr); LOG.debugFine(msg.toString()); } return appr; - } - else { + } else { a = a + 1; } } @@ -683,18 +650,18 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree StringBuilder msg = new StringBuilder(); int[] upperHull = convexHull.getUpperHull(); int u = convexHull.getNumberOfPointsInUpperHull(); - int k_0 = k_max - upperHull.length + 1; + int k_0 = settings.k_max - upperHull.length + 1; // linear search on all line segments on the upper convex hull msg.append("upper hull:").append(u); double upp_error = Double.MAX_VALUE; double upp_m = 0.0; double upp_t = 0.0; - for(int i = 1; i < u; i++) { + for (int i = 1; i < u; i++) { double cur_m = (log_kDist[upperHull[i]] - log_kDist[upperHull[i - 1]]) / (log_k[upperHull[i]] - log_k[upperHull[i - 1]]); double cur_t = log_kDist[upperHull[i]] - cur_m * log_k[upperHull[i]]; - double cur_error = ssqerr(k_0, k_max, log_k, log_kDist, cur_m, cur_t); - if(cur_error < upp_error) { + double cur_error = ssqerr(k_0, settings.k_max, log_k, log_kDist, cur_m, cur_t); + if (cur_error < upp_error) { upp_error = cur_error; upp_m = cur_m; upp_t = cur_t; @@ -702,13 +669,13 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree } // linear search on all points of the upper convex hull boolean is_left = true; // NEEDED FOR PROOF CHECK - for(int i = 0; i < u; i++) { - double cur_m = optimize(k_0, k_max, sum_log_k, sum_log_k2, log_k[upperHull[i]], log_kDist[upperHull[i]], sum_log_k_kDist, sum_log_kDist); + for (int i = 0; i < u; i++) { + double cur_m = optimize(k_0, settings.k_max, sum_log_k, sum_log_k2, log_k[upperHull[i]], log_kDist[upperHull[i]], sum_log_k_kDist, sum_log_kDist); double cur_t = log_kDist[upperHull[i]] - cur_m * log_k[upperHull[i]]; // only valid if both neighboring points are underneath y=mx+t - if((i == 0 || log_kDist[upperHull[i - 1]] <= log_kDist[upperHull[i]] - cur_m * (log_k[upperHull[i]] - log_k[upperHull[i - 1]])) && (i == u - 1 || log_kDist[upperHull[i + 1]] <= log_kDist[upperHull[i]] + cur_m * (log_k[upperHull[i + 1]] - log_k[upperHull[i]]))) { - double cur_error = ssqerr(k_0, k_max, log_k, log_kDist, cur_m, cur_t); - if(cur_error < upp_error) { + if ((i == 0 || log_kDist[upperHull[i - 1]] <= log_kDist[upperHull[i]] - cur_m * (log_k[upperHull[i]] - log_k[upperHull[i - 1]])) && (i == u - 1 || log_kDist[upperHull[i + 1]] <= log_kDist[upperHull[i]] + cur_m * (log_k[upperHull[i + 1]] - log_k[upperHull[i]]))) { + double cur_error = ssqerr(k_0, settings.k_max, log_k, log_kDist, cur_m, cur_t); + if (cur_error < upp_error) { upp_error = cur_error; upp_m = cur_m; upp_t = cur_t; @@ -716,12 +683,12 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree } // check proof of bisection search - if(!(i > 0 && log_kDist[upperHull[i - 1]] > log_kDist[upperHull[i]] - cur_m * (log_k[upperHull[i]] - log_k[upperHull[i - 1]])) && !is_left) { + if (!(i > 0 && log_kDist[upperHull[i - 1]] > log_kDist[upperHull[i]] - cur_m * (log_k[upperHull[i]] - log_k[upperHull[i - 1]])) && !is_left) { // warning("ERROR upper: The bisection search will not work properly !" // + // "\n" + Util.format(log_kDist)); } - if(!(i < u - 1 && log_kDist[upperHull[i + 1]] > log_kDist[upperHull[i]] + cur_m * (log_k[upperHull[i + 1]] - log_k[upperHull[i]]))) { + if (!(i < u - 1 && log_kDist[upperHull[i + 1]] > log_kDist[upperHull[i]] + cur_m * (log_k[upperHull[i + 1]] - log_k[upperHull[i]]))) { is_left = false; } } @@ -737,7 +704,7 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree */ @Override protected MkCoPTreeNode<O, D> createNewLeafNode() { - return new MkCoPTreeNode<O, D>(leafCapacity, true); + return new MkCoPTreeNode<>(leafCapacity, true); } /** @@ -747,7 +714,7 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree */ @Override protected MkCoPTreeNode<O, D> createNewDirectoryNode() { - return new MkCoPTreeNode<O, D>(dirCapacity, false); + return new MkCoPTreeNode<>(dirCapacity, false); } /** @@ -759,8 +726,8 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree * the routing object of the parent node */ @Override - protected MkCoPEntry<D> createNewDirectoryEntry(MkCoPTreeNode<O, D> node, DBID routingObjectID, D parentDistance) { - return new MkCoPDirectoryEntry<D>(routingObjectID, parentDistance, node.getPageID(), node.coveringRadius(routingObjectID, this), null); + protected MkCoPEntry createNewDirectoryEntry(MkCoPTreeNode<O, D> node, DBID routingObjectID, double parentDistance) { + return new MkCoPDirectoryEntry(routingObjectID, parentDistance, node.getPageID(), node.coveringRadius(routingObjectID, this), null); // node.conservativeKnnDistanceApproximation(k_max)); } @@ -770,12 +737,12 @@ public class MkCoPTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTree * @return an entry representing the root node */ @Override - protected MkCoPEntry<D> createRootEntry() { - return new MkCoPDirectoryEntry<D>(null, null, 0, null, null); + protected MkCoPEntry createRootEntry() { + return new MkCoPDirectoryEntry(null, 0., 0, 0., null); } @Override protected Logging getLogger() { return LOG; } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeIndex.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeIndex.java index d3e45f8c..96def76b 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeIndex.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeIndex.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkcop; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -28,9 +28,7 @@ import java.util.List; import de.lmu.ifi.dbs.elki.database.ids.DBID; import de.lmu.ifi.dbs.elki.database.ids.DBIDIter; -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.DBIDs; import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery; import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery; @@ -43,12 +41,10 @@ import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.KNNIndex; import de.lmu.ifi.dbs.elki.index.RKNNIndex; import de.lmu.ifi.dbs.elki.index.RangeIndex; -import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; -import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTree; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.MkTreeSettings; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MTreeQueryUtil; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MkTreeRKNNQuery; import de.lmu.ifi.dbs.elki.persistent.PageFile; -import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages; /** * MkCoPTree used as database index. @@ -69,14 +65,11 @@ public class MkCoPTreeIndex<O, D extends NumberDistance<D, ?>> extends MkCoPTree * * @param relation Relation to index. * @param pageFile Page file - * @param distanceQuery Distance query - * @param distanceFunction Distance function - * @param k_max Maximum value of k supported + * @param settings Tree settings */ - public MkCoPTreeIndex(Relation<O> relation, PageFile<MkCoPTreeNode<O, D>> pageFile, DistanceQuery<O, D> distanceQuery, DistanceFunction<O, D> distanceFunction, int k_max) { - super(pageFile, distanceQuery, distanceFunction, k_max); + public MkCoPTreeIndex(Relation<O> relation, PageFile<MkCoPTreeNode<O, D>> pageFile, MkTreeSettings<O, D, MkCoPTreeNode<O, D>, MkCoPEntry> settings) { + super(relation, pageFile, settings); this.relation = relation; - this.initialize(); } /** @@ -87,120 +80,89 @@ public class MkCoPTreeIndex<O, D extends NumberDistance<D, ?>> extends MkCoPTree * @param parentDistance the distance from the object to the routing object of * the parent node */ - protected MkCoPEntry<D> createNewLeafEntry(DBID id, O object, D parentDistance) { - MkCoPLeafEntry<D> leafEntry = new MkCoPLeafEntry<D>(id, parentDistance, null, null); + protected MkCoPEntry createNewLeafEntry(DBID id, O object, double parentDistance) { + MkCoPLeafEntry leafEntry = new MkCoPLeafEntry(id, parentDistance, null, null); return leafEntry; } @Override - public void insert(DBIDRef id) { - throw new UnsupportedOperationException("Insertion of single objects is not supported!"); - } - - @Override - public void insertAll(DBIDs ids) { - List<MkCoPEntry<D>> objs = new ArrayList<MkCoPEntry<D>>(ids.size()); - for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) { - DBID id = DBIDUtil.deref(iter); + public void initialize() { + super.initialize(); + List<MkCoPEntry> objs = new ArrayList<>(relation.size()); + for (DBIDIter iter = relation.iterDBIDs(); iter.valid(); iter.advance()) { + DBID id = DBIDUtil.deref(iter); // FIXME: expensive final O object = relation.get(id); - objs.add(createNewLeafEntry(id, object, getDistanceFactory().undefinedDistance())); + objs.add(createNewLeafEntry(id, object, Double.NaN)); } insertAll(objs); } - /** - * Throws an UnsupportedOperationException since deletion of objects is not - * yet supported by an M-Tree. - * - * @throws UnsupportedOperationException thrown, since deletions aren't - * implemented yet. - */ - @Override - public final boolean delete(DBIDRef id) { - throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET); - } - - /** - * Throws an UnsupportedOperationException since deletion of objects is not - * yet supported by an M-Tree. - * - * @throws UnsupportedOperationException thrown, since deletions aren't - * implemented yet. - */ - @Override - public void deleteAll(DBIDs ids) { - throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET); - } - @SuppressWarnings("unchecked") @Override public <S extends Distance<S>> KNNQuery<O, S> getKNNQuery(DistanceQuery<O, S> distanceQuery, Object... hints) { // Query on the relation we index - if(distanceQuery.getRelation() != relation) { + if (distanceQuery.getRelation() != relation) { return null; } - DistanceFunction<? super O, S> distanceFunction = distanceQuery.getDistanceFunction(); - if(!this.distanceFunction.equals(distanceFunction)) { - if(getLogger().isDebugging()) { + DistanceFunction<? super O, D> distanceFunction = (DistanceFunction<? super O, D>) distanceQuery.getDistanceFunction(); + if (!this.getDistanceFunction().equals(distanceFunction)) { + if (getLogger().isDebugging()) { getLogger().debug("Distance function not supported by index - or 'equals' not implemented right!"); } return null; } // Bulk is not yet supported - for(Object hint : hints) { - if(hint == DatabaseQuery.HINT_BULK) { + for (Object hint : hints) { + if (hint == DatabaseQuery.HINT_BULK) { return null; } } - AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this; - DistanceQuery<O, S> dq = distanceFunction.instantiate(relation); - return MTreeQueryUtil.getKNNQuery(idx, dq, hints); + DistanceQuery<O, D> dq = distanceFunction.instantiate(relation); + return (KNNQuery<O, S>) MTreeQueryUtil.getKNNQuery(this, dq, hints); } @SuppressWarnings("unchecked") @Override public <S extends Distance<S>> RangeQuery<O, S> getRangeQuery(DistanceQuery<O, S> distanceQuery, Object... hints) { // Query on the relation we index - if(distanceQuery.getRelation() != relation) { + if (distanceQuery.getRelation() != relation) { return null; } - DistanceFunction<? super O, S> distanceFunction = distanceQuery.getDistanceFunction(); - if(!this.distanceFunction.equals(distanceFunction)) { - if(getLogger().isDebugging()) { + DistanceFunction<? super O, D> distanceFunction = (DistanceFunction<? super O, D>) distanceQuery.getDistanceFunction(); + if (!this.getDistanceFunction().equals(distanceFunction)) { + if (getLogger().isDebugging()) { getLogger().debug("Distance function not supported by index - or 'equals' not implemented right!"); } return null; } // Bulk is not yet supported - for(Object hint : hints) { - if(hint == DatabaseQuery.HINT_BULK) { + for (Object hint : hints) { + if (hint == DatabaseQuery.HINT_BULK) { return null; } } - AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this; - DistanceQuery<O, S> dq = distanceFunction.instantiate(relation); - return MTreeQueryUtil.getRangeQuery(idx, dq, hints); + DistanceQuery<O, D> dq = distanceFunction.instantiate(relation); + return (RangeQuery<O, S>) MTreeQueryUtil.getRangeQuery(this, dq, hints); } @SuppressWarnings("unchecked") @Override public <S extends Distance<S>> RKNNQuery<O, S> getRKNNQuery(DistanceQuery<O, S> distanceQuery, Object... hints) { - DistanceFunction<? super O, S> distanceFunction = distanceQuery.getDistanceFunction(); - if(!this.getDistanceFunction().equals(distanceFunction)) { - if(getLogger().isDebugging()) { + DistanceFunction<? super O, D> distanceFunction = (DistanceFunction<? super O, D>) distanceQuery.getDistanceFunction(); + if (!this.getDistanceFunction().equals(distanceFunction)) { + if (getLogger().isDebugging()) { getLogger().debug("Distance function not supported by index - or 'equals' not implemented right!"); } return null; } // Bulk is not yet supported - for(Object hint : hints) { - if(hint == DatabaseQuery.HINT_BULK) { + for (Object hint : hints) { + if (hint == DatabaseQuery.HINT_BULK) { return null; } } - AbstractMkTree<O, S, ?, ?> idx = (AbstractMkTree<O, S, ?, ?>) this; - DistanceQuery<O, S> dq = distanceFunction.instantiate(relation); - return new MkTreeRKNNQuery<O, S>(idx, dq); + DistanceQuery<O, D> dq = distanceFunction.instantiate(relation); + return (RKNNQuery<O, S>) new MkTreeRKNNQuery<>(this, dq); } @Override @@ -212,4 +174,4 @@ public class MkCoPTreeIndex<O, D extends NumberDistance<D, ?>> extends MkCoPTree public String getShortName() { return "mkcoptree"; } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeNode.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeNode.java index f2e4a114..79a9c6cc 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeNode.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCoPTreeNode.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkcop; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -38,7 +38,7 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; * @param <O> object type * @param <D> distance type */ -class MkCoPTreeNode<O, D extends NumberDistance<D, ?>> extends AbstractMTreeNode<O, D, MkCoPTreeNode<O, D>, MkCoPEntry<D>> { +class MkCoPTreeNode<O, D extends NumberDistance<D, ?>> extends AbstractMTreeNode<O, D, MkCoPTreeNode<O, D>, MkCoPEntry> { /** * Serial version UID */ @@ -77,13 +77,13 @@ class MkCoPTreeNode<O, D extends NumberDistance<D, ?>> extends AbstractMTreeNode double y_kmax = Double.NEGATIVE_INFINITY; for(int i = 0; i < getNumEntries(); i++) { - MkCoPEntry<D> entry = getEntry(i); + MkCoPEntry entry = getEntry(i); ApproximationLine approx = entry.getConservativeKnnDistanceApproximation(); k_0 = Math.min(approx.getK_0(), k_0); } for(int i = 0; i < getNumEntries(); i++) { - MkCoPEntry<D> entry = getEntry(i); + MkCoPEntry entry = getEntry(i); ApproximationLine approx = entry.getConservativeKnnDistanceApproximation(); double entry_y_1 = approx.getValueAt(k_0); double entry_y_kmax = approx.getValueAt(k_max); @@ -122,13 +122,13 @@ class MkCoPTreeNode<O, D extends NumberDistance<D, ?>> extends AbstractMTreeNode double y_kmax = Double.POSITIVE_INFINITY; for(int i = 0; i < getNumEntries(); i++) { - MkCoPLeafEntry<D> entry = (MkCoPLeafEntry<D>) getEntry(i); + MkCoPLeafEntry entry = (MkCoPLeafEntry) getEntry(i); ApproximationLine approx = entry.getProgressiveKnnDistanceApproximation(); k_0 = Math.max(approx.getK_0(), k_0); } for(int i = 0; i < getNumEntries(); i++) { - MkCoPLeafEntry<D> entry = (MkCoPLeafEntry<D>) getEntry(i); + MkCoPLeafEntry entry = (MkCoPLeafEntry) getEntry(i); ApproximationLine approx = entry.getProgressiveKnnDistanceApproximation(); y_1 = Math.min(approx.getValueAt(k_0), y_1); y_kmax = Math.min(approx.getValueAt(k_max), y_kmax); @@ -142,7 +142,7 @@ class MkCoPTreeNode<O, D extends NumberDistance<D, ?>> extends AbstractMTreeNode } @Override - public void adjustEntry(MkCoPEntry<D> entry, DBID routingObjectID, D parentDistance, AbstractMTree<O, D, MkCoPTreeNode<O, D>, MkCoPEntry<D>> mTree) { + public void adjustEntry(MkCoPEntry entry, DBID routingObjectID, double parentDistance, AbstractMTree<O, D, MkCoPTreeNode<O, D>, MkCoPEntry, ?> mTree) { super.adjustEntry(entry, routingObjectID, parentDistance, mTree); // adjust conservative distance approximation // int k_max = ((MkCoPTree<O,D>) mTree).getK_max(); @@ -150,10 +150,10 @@ class MkCoPTreeNode<O, D extends NumberDistance<D, ?>> extends AbstractMTreeNode } @Override - protected void integrityCheckParameters(MkCoPEntry<D> parentEntry, MkCoPTreeNode<O, D> parent, int index, AbstractMTree<O, D, MkCoPTreeNode<O, D>, MkCoPEntry<D>> mTree) { + protected void integrityCheckParameters(MkCoPEntry parentEntry, MkCoPTreeNode<O, D> parent, int index, AbstractMTree<O, D, MkCoPTreeNode<O, D>, MkCoPEntry, ?> mTree) { super.integrityCheckParameters(parentEntry, parent, index, mTree); // test conservative approximation - MkCoPEntry<D> entry = parent.getEntry(index); + MkCoPEntry entry = parent.getEntry(index); int k_max = ((MkCoPTree<O, D>) mTree).getK_max(); ApproximationLine approx = conservativeKnnDistanceApproximation(k_max); if(!entry.getConservativeKnnDistanceApproximation().equals(approx)) { diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCopTreeFactory.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCopTreeFactory.java index 4ac96df3..6a39e92f 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCopTreeFactory.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/MkCopTreeFactory.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkcop; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -24,10 +24,11 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkcop; */ 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.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeFactory; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.MkTreeSettings; import de.lmu.ifi.dbs.elki.persistent.PageFile; +import de.lmu.ifi.dbs.elki.persistent.PageFileFactory; import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil; import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID; import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterConstraint; @@ -45,35 +46,26 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter; * @param <O> Object type * @param <D> Distance type */ -public class MkCopTreeFactory<O, D extends NumberDistance<D, ?>> extends AbstractMTreeFactory<O, D, MkCoPTreeNode<O, D>, MkCoPEntry<D>, MkCoPTreeIndex<O, D>> { +public class MkCopTreeFactory<O, D extends NumberDistance<D, ?>> extends AbstractMTreeFactory<O, D, MkCoPTreeNode<O, D>, MkCoPEntry, MkCoPTreeIndex<O, D>, MkTreeSettings<O, D, MkCoPTreeNode<O, D>, MkCoPEntry>> { /** * Parameter for k */ public static final OptionID K_ID = new OptionID("mkcop.k", "positive integer specifying the maximum number k of reverse k nearest neighbors to be supported."); /** - * Parameter k. - */ - int k_max; - - /** * Constructor. - * - * @param fileName - * @param pageSize - * @param cacheSize - * @param distanceFunction - * @param k_max + * + * @param pageFileFactory Data storage + * @param settings Tree settings */ - public MkCopTreeFactory(String fileName, int pageSize, long cacheSize, DistanceFunction<O, D> distanceFunction, int k_max) { - super(fileName, pageSize, cacheSize, distanceFunction); - this.k_max = k_max; + public MkCopTreeFactory(PageFileFactory<?> pageFileFactory, MkTreeSettings<O, D, MkCoPTreeNode<O, D>, MkCoPEntry> settings) { + super(pageFileFactory, settings); } @Override public MkCoPTreeIndex<O, D> instantiate(Relation<O> relation) { PageFile<MkCoPTreeNode<O, D>> pagefile = makePageFile(getNodeClass()); - return new MkCoPTreeIndex<O, D>(relation, pagefile, distanceFunction.instantiate(relation), distanceFunction, k_max); + return new MkCoPTreeIndex<>(relation, pagefile, settings); } protected Class<MkCoPTreeNode<O, D>> getNodeClass() { @@ -87,22 +79,25 @@ public class MkCopTreeFactory<O, D extends NumberDistance<D, ?>> extends Abstrac * * @apiviz.exclude */ - public static class Parameterizer<O, D extends NumberDistance<D, ?>> extends AbstractMTreeFactory.Parameterizer<O, D> { - protected int k_max = 0; - + public static class Parameterizer<O, D extends NumberDistance<D, ?>> extends AbstractMTreeFactory.Parameterizer<O, D, MkCoPTreeNode<O, D>, MkCoPEntry, MkTreeSettings<O, D, MkCoPTreeNode<O, D>, MkCoPEntry>> { @Override protected void makeOptions(Parameterization config) { super.makeOptions(config); IntParameter k_maxP = new IntParameter(K_ID); k_maxP.addConstraint(new GreaterConstraint(0)); if (config.grab(k_maxP)) { - k_max = k_maxP.intValue(); + settings.k_max = k_maxP.intValue(); } } @Override protected MkCopTreeFactory<O, D> makeInstance() { - return new MkCopTreeFactory<O, D>(fileName, pageSize, cacheSize, distanceFunction, k_max); + return new MkCopTreeFactory<>(pageFileFactory, settings); + } + + @Override + protected MkTreeSettings<O, D, MkCoPTreeNode<O, D>, MkCoPEntry> makeSettings() { + return new MkTreeSettings<>(); } } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/package-info.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/package-info.java index 516af071..c767c565 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/package-info.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkcop/package-info.java @@ -5,7 +5,7 @@ This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures -Copyright (C) 2012 +Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxDirectoryEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxDirectoryEntry.java index 0781dcb1..b3e39ca0 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxDirectoryEntry.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxDirectoryEntry.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkmax; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -28,7 +28,6 @@ import java.io.ObjectInput; import java.io.ObjectOutput; import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeDirectoryEntry; /** @@ -37,19 +36,18 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeDirectoryEntry * the underlying MkMax-Tree node. * * @author Elke Achtert - * @param <D> the type of Distance used in the MkMaxTree */ -class MkMaxDirectoryEntry<D extends Distance<D>> extends MTreeDirectoryEntry<D> implements MkMaxEntry<D> { +class MkMaxDirectoryEntry extends MTreeDirectoryEntry implements MkMaxEntry { /** * Serial version UID */ - private static final long serialVersionUID = 1; + private static final long serialVersionUID = 2; /** * The aggregated k-nearest neighbor distance of the underlying MkMax-Tree * node. */ - private D knnDistance; + private double knnDistance; /** * Empty constructor for serialization purposes. @@ -69,18 +67,18 @@ class MkMaxDirectoryEntry<D extends Distance<D>> extends MTreeDirectoryEntry<D> * @param knnDistance the aggregated knn distance of the underlying MkMax-Tree * node */ - public MkMaxDirectoryEntry(DBID objectID, D parentDistance, Integer nodeID, D coveringRadius, D knnDistance) { + public MkMaxDirectoryEntry(DBID objectID, double parentDistance, Integer nodeID, double coveringRadius, double knnDistance) { super(objectID, parentDistance, nodeID, coveringRadius); this.knnDistance = knnDistance; } @Override - public D getKnnDistance() { + public double getKnnDistance() { return knnDistance; } @Override - public void setKnnDistance(D knnDistance) { + public void setKnnDistance(double knnDistance) { this.knnDistance = knnDistance; } @@ -91,7 +89,7 @@ class MkMaxDirectoryEntry<D extends Distance<D>> extends MTreeDirectoryEntry<D> @Override public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal(out); - out.writeObject(knnDistance); + out.writeDouble(knnDistance); } /** @@ -99,10 +97,9 @@ class MkMaxDirectoryEntry<D extends Distance<D>> extends MTreeDirectoryEntry<D> * specified input stream. */ @Override - @SuppressWarnings("unchecked") public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); - this.knnDistance = (D) in.readObject(); + this.knnDistance = in.readDouble(); } /** @@ -113,20 +110,19 @@ class MkMaxDirectoryEntry<D extends Distance<D>> extends MTreeDirectoryEntry<D> * MkMaxDirectoryEntry and has the same knnDistance as this entry. */ @Override - @SuppressWarnings("unchecked") public boolean equals(Object o) { - if(this == o) { + if (this == o) { return true; } - if(o == null || getClass() != o.getClass()) { + if (o == null || getClass() != o.getClass()) { return false; } - if(!super.equals(o)) { + if (!super.equals(o)) { return false; } - final MkMaxDirectoryEntry<D> that = (MkMaxDirectoryEntry<D>) o; + final MkMaxDirectoryEntry that = (MkMaxDirectoryEntry) o; - return !(knnDistance != null ? !knnDistance.equals(that.knnDistance) : that.knnDistance != null); + return Double.compare(knnDistance, that.knnDistance) == 0; } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxEntry.java index 0a7032f3..9e5133c9 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxEntry.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxEntry.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkmax; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -23,30 +23,28 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkmax; along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; /** * Defines the requirements for an entry in an - * {@link de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkmax.MkMaxTreeNode}. - * Additionally to an entry in an M-Tree an MkMaxEntry holds the k-nearest neighbor distance of the underlying - * data object or MkMax-Tree node. - * + * {@link de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkmax.MkMaxTreeNode} + * . Additionally to an entry in an M-Tree an MkMaxEntry holds the k-nearest + * neighbor distance of the underlying data object or MkMax-Tree node. + * * @author Elke Achtert - * @param <D> the type of Distance used in the MkMaxTree */ -interface MkMaxEntry<D extends Distance<D>> extends MTreeEntry<D> { +interface MkMaxEntry extends MTreeEntry { /** * Returns the knn distance of the entry. - * + * * @return the knn distance of the entry */ - public D getKnnDistance(); + public double getKnnDistance(); /** * Sets the knn distance of the entry. - * + * * @param knnDistance the knn distance to be set */ - public void setKnnDistance(D knnDistance); + public void setKnnDistance(double knnDistance); } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxLeafEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxLeafEntry.java index 29221fdc..d49f18ec 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxLeafEntry.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxLeafEntry.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkmax; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -28,7 +28,6 @@ import java.io.ObjectInput; import java.io.ObjectOutput; import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeLeafEntry; /** @@ -37,9 +36,8 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeLeafEntry; * the underlying data object. * * @author Elke Achtert - * @param <D> the type of Distance used in the MkMaxTree */ -class MkMaxLeafEntry<D extends Distance<D>> extends MTreeLeafEntry<D> implements MkMaxEntry<D> { +class MkMaxLeafEntry extends MTreeLeafEntry implements MkMaxEntry { /** * Serial version number */ @@ -48,7 +46,7 @@ class MkMaxLeafEntry<D extends Distance<D>> extends MTreeLeafEntry<D> implements /** * The k-nearest neighbor distance of the underlying data object. */ - private D knnDistance; + private double knnDistance; /** * Empty constructor for serialization purposes. @@ -65,18 +63,18 @@ class MkMaxLeafEntry<D extends Distance<D>> extends MTreeLeafEntry<D> implements * parent's routing object * @param knnDistance the knn distance of the underlying data object */ - public MkMaxLeafEntry(DBID objectID, D parentDistance, D knnDistance) { + public MkMaxLeafEntry(DBID objectID, double parentDistance, double knnDistance) { super(objectID, parentDistance); this.knnDistance = knnDistance; } @Override - public D getKnnDistance() { + public double getKnnDistance() { return knnDistance; } @Override - public void setKnnDistance(D knnDistance) { + public void setKnnDistance(double knnDistance) { this.knnDistance = knnDistance; } @@ -95,10 +93,9 @@ class MkMaxLeafEntry<D extends Distance<D>> extends MTreeLeafEntry<D> implements * specified input stream. */ @Override - @SuppressWarnings("unchecked") public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); - this.knnDistance = (D) in.readObject(); + this.knnDistance = in.readDouble(); } /** @@ -109,20 +106,19 @@ class MkMaxLeafEntry<D extends Distance<D>> extends MTreeLeafEntry<D> implements * and has the same knnDistance as this entry. */ @Override - @SuppressWarnings("unchecked") public boolean equals(Object o) { - if(this == o) { + if (this == o) { return true; } - if(o == null || getClass() != o.getClass()) { + if (o == null || getClass() != o.getClass()) { return false; } - if(!super.equals(o)) { + if (!super.equals(o)) { return false; } - final MkMaxLeafEntry<D> that = (MkMaxLeafEntry<D>) o; + final MkMaxLeafEntry that = (MkMaxLeafEntry) o; - return !(knnDistance != null ? !knnDistance.equals(that.knnDistance) : that.knnDistance != null); + return Double.compare(knnDistance, that.knnDistance) == 0; } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTree.java index 36f9bbf1..c1f87f20 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTree.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTree.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkmax; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -31,27 +31,25 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDIter; 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.distance.DistanceQuery; -import de.lmu.ifi.dbs.elki.distance.DistanceUtil; -import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.ModifiableDistanceDBIDResult; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; -import de.lmu.ifi.dbs.elki.index.tree.DistanceEntry; +import de.lmu.ifi.dbs.elki.database.ids.distance.DistanceDBIDList; +import de.lmu.ifi.dbs.elki.database.ids.distance.DistanceDBIDListIter; +import de.lmu.ifi.dbs.elki.database.ids.distance.KNNHeap; +import de.lmu.ifi.dbs.elki.database.ids.distance.KNNList; +import de.lmu.ifi.dbs.elki.database.ids.distance.ModifiableDistanceDBIDList; +import de.lmu.ifi.dbs.elki.database.ids.generic.GenericDistanceDBIDList; +import de.lmu.ifi.dbs.elki.database.relation.Relation; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTreeUnified; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.MkTreeSettings; import de.lmu.ifi.dbs.elki.logging.Logging; +import de.lmu.ifi.dbs.elki.persistent.ByteArrayUtil; import de.lmu.ifi.dbs.elki.persistent.PageFile; -import de.lmu.ifi.dbs.elki.utilities.QueryStatistic; +import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleIntPair; /** * MkMaxTree is a metrical index structure based on the concepts of the M-Tree * supporting efficient processing of reverse k nearest neighbor queries for - * parameter k <= k_max. The k-nearest neigbor distance for k = k_max is stored + * parameter k <= k_max. The k-nearest neighbor distance for k = k_max is stored * in each entry of a node. * * @author Elke Achtert @@ -61,49 +59,44 @@ import de.lmu.ifi.dbs.elki.utilities.QueryStatistic; * @param <O> the type of DatabaseObject to be stored in the MkMaxTree * @param <D> the type of Distance used in the MkMaxTree */ -public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O, D, MkMaxTreeNode<O, D>, MkMaxEntry<D>> { +public class MkMaxTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTreeUnified<O, D, MkMaxTreeNode<O, D>, MkMaxEntry, MkTreeSettings<O, D, MkMaxTreeNode<O, D>, MkMaxEntry>> { /** * The logger for this class. */ private static final Logging LOG = Logging.getLogger(MkMaxTree.class); /** - * Provides some statistics about performed reverse knn-queries. - */ - private QueryStatistic rkNNStatistics = new QueryStatistic(); - - /** * Constructor. * + * @param relation Relation to index * @param pagefile Page file - * @param distanceQuery Distance query - * @param distanceFunction Distance function - * @param k_max Maximum value for k + * @param settings Tree settings */ - public MkMaxTree(PageFile<MkMaxTreeNode<O, D>> pagefile, DistanceQuery<O, D> distanceQuery, DistanceFunction<O, D> distanceFunction, int k_max) { - super(pagefile, distanceQuery, distanceFunction, k_max); + public MkMaxTree(Relation<O> relation, PageFile<MkMaxTreeNode<O, D>> pagefile, MkTreeSettings<O, D, MkMaxTreeNode<O, D>, MkMaxEntry> settings) { + super(relation, pagefile, settings); } /** * Performs a reverse k-nearest neighbor query for the given object ID. In the * first step the candidates are chosen by performing a reverse k-nearest - * neighbor query with k = {@link #k_max}. Then these candidates are refined + * neighbor query with k = {@link #getKmax()}. Then these candidates are refined * in a second step. */ @Override - public DistanceDBIDResult<D> reverseKNNQuery(DBIDRef id, int k) { - if(k > this.getKmax()) { + public DistanceDBIDList<D> reverseKNNQuery(DBIDRef id, int k) { + if (k > this.getKmax()) { throw new IllegalArgumentException("Parameter k has to be equal or less than " + "parameter k of the MkMax-Tree!"); } // get the candidates - GenericDistanceDBIDList<D> candidates = new GenericDistanceDBIDList<D>(); + GenericDistanceDBIDList<D> candidates = new GenericDistanceDBIDList<>(); doReverseKNNQuery(id, getRoot(), null, candidates); - if(k == this.getKmax()) { + if (k == this.getKmax()) { candidates.sort(); - rkNNStatistics.addTrueHits(candidates.size()); - rkNNStatistics.addResults(candidates.size()); + // FIXME: re-add statistics. + // rkNNStatistics.addTrueHits(candidates.size()); + // rkNNStatistics.addResults(candidates.size()); return candidates; } @@ -112,49 +105,34 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O for (DBIDIter candidate = candidates.iter(); candidate.valid(); candidate.advance()) { candidateIDs.add(candidate); } - Map<DBID, KNNResult<D>> knnLists = batchNN(getRoot(), candidateIDs, k); + Map<DBID, KNNList<D>> knnLists = batchNN(getRoot(), candidateIDs, k); - GenericDistanceDBIDList<D> result = new GenericDistanceDBIDList<D>(); + GenericDistanceDBIDList<D> result = new GenericDistanceDBIDList<>(); for (DBIDIter iter = candidateIDs.iter(); iter.valid(); iter.advance()) { DBID cid = DBIDUtil.deref(iter); - KNNResult<D> cands = knnLists.get(cid); - for (DistanceDBIDResultIter<D> iter2 = cands.iter(); iter2.valid(); iter2.advance()) { - if(DBIDUtil.equal(id, iter2)) { + KNNList<D> cands = knnLists.get(cid); + for (DistanceDBIDListIter<D> iter2 = cands.iter(); iter2.valid(); iter2.advance()) { + if (DBIDUtil.equal(id, iter2)) { result.add(iter2.getDistance(), cid); break; } } } - rkNNStatistics.addResults(result.size()); - rkNNStatistics.addCandidates(candidates.size()); + // FIXME: re-add statistics. + // rkNNStatistics.addResults(result.size()); + // rkNNStatistics.addCandidates(candidates.size()); result.sort(); return result; } /** - * Returns the statistic for performed rknn queries. - * - * @return the statistic for performed rknn queries - */ - public QueryStatistic getRkNNStatistics() { - return rkNNStatistics; - } - - /** - * Clears the values of the statistic for performed rknn queries - */ - public void clearRkNNStatistics() { - rkNNStatistics.clear(); - } - - /** * Adapts the knn distances before insertion of the specified entry. * */ @Override - protected void preInsert(MkMaxEntry<D> entry) { - KNNHeap<D> knns_o = KNNUtil.newHeap(distanceFunction, getKmax()); + protected void preInsert(MkMaxEntry entry) { + KNNHeap<D> knns_o = DBIDUtil.newHeap(getDistanceFactory(), getKmax()); preInsert(entry, getRootEntry(), knns_o); } @@ -162,21 +140,20 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O * Adjusts the knn distance in the subtree of the specified root entry. */ @Override - protected void kNNdistanceAdjustment(MkMaxEntry<D> entry, Map<DBID, KNNResult<D>> knnLists) { + protected void kNNdistanceAdjustment(MkMaxEntry entry, Map<DBID, KNNList<D>> knnLists) { MkMaxTreeNode<O, D> node = getNode(entry); - D knnDist_node = getDistanceQuery().nullDistance(); - if(node.isLeaf()) { - for(int i = 0; i < node.getNumEntries(); i++) { - MkMaxEntry<D> leafEntry = node.getEntry(i); - leafEntry.setKnnDistance(knnLists.get(leafEntry.getRoutingObjectID()).getKNNDistance()); - knnDist_node = DistanceUtil.max(knnDist_node, leafEntry.getKnnDistance()); + double knnDist_node = 0.; + if (node.isLeaf()) { + for (int i = 0; i < node.getNumEntries(); i++) { + MkMaxEntry leafEntry = node.getEntry(i); + leafEntry.setKnnDistance(knnLists.get(leafEntry.getRoutingObjectID()).getKNNDistance().doubleValue()); + knnDist_node = Math.max(knnDist_node, leafEntry.getKnnDistance()); } - } - else { - for(int i = 0; i < node.getNumEntries(); i++) { - MkMaxEntry<D> dirEntry = node.getEntry(i); + } else { + for (int i = 0; i < node.getNumEntries(); i++) { + MkMaxEntry dirEntry = node.getEntry(i); kNNdistanceAdjustment(dirEntry, knnLists); - knnDist_node = DistanceUtil.max(knnDist_node, dirEntry.getKnnDistance()); + knnDist_node = Math.max(knnDist_node, dirEntry.getKnnDistance()); } } entry.setKnnDistance(knnDist_node); @@ -184,22 +161,22 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O /** * Performs a reverse k-nearest neighbor query in the specified subtree for - * the given query object with k = {@link #k_max}. It recursively traverses + * the given query object with k = {@link #getKmax()}. It recursively traverses * all paths from the specified node, which cannot be excluded from leading to - * qualififying objects. + * qualifying objects. * * @param q the id of the query object * @param node the node of the subtree on which the query is performed * @param node_entry the entry representing the node * @param result the list for the query result */ - private void doReverseKNNQuery(DBIDRef q, MkMaxTreeNode<O, D> node, MkMaxEntry<D> node_entry, ModifiableDistanceDBIDResult<D> result) { + private void doReverseKNNQuery(DBIDRef q, MkMaxTreeNode<O, D> node, MkMaxEntry node_entry, ModifiableDistanceDBIDList<D> result) { // data node - if(node.isLeaf()) { - for(int i = 0; i < node.getNumEntries(); i++) { - MkMaxEntry<D> entry = node.getEntry(i); - D distance = getDistanceQuery().distance(entry.getRoutingObjectID(), q); - if(distance.compareTo(entry.getKnnDistance()) <= 0) { + if (node.isLeaf()) { + for (int i = 0; i < node.getNumEntries(); i++) { + MkMaxEntry entry = node.getEntry(i); + D distance = distance(entry.getRoutingObjectID(), q); + if (distance.doubleValue() <= entry.getKnnDistance()) { result.add(distance, entry.getRoutingObjectID()); } } @@ -207,14 +184,14 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O // directory node else { - for(int i = 0; i < node.getNumEntries(); i++) { - MkMaxEntry<D> entry = node.getEntry(i); - D node_knnDist = node_entry != null ? node_entry.getKnnDistance() : getDistanceQuery().infiniteDistance(); + for (int i = 0; i < node.getNumEntries(); i++) { + MkMaxEntry entry = node.getEntry(i); + double node_knnDist = node_entry != null ? node_entry.getKnnDistance() : Double.POSITIVE_INFINITY; - D distance = getDistanceQuery().distance(entry.getRoutingObjectID(), q); - D minDist = entry.getCoveringRadius().compareTo(distance) > 0 ? getDistanceQuery().nullDistance() : distance.minus(entry.getCoveringRadius()); + double distance = distance(entry.getRoutingObjectID(), q).doubleValue(); + double minDist = (entry.getCoveringRadius() > distance) ? 0.0 : distance - entry.getCoveringRadius(); - if(minDist.compareTo(node_knnDist) <= 0) { + if (minDist <= node_knnDist) { MkMaxTreeNode<O, D> childNode = getNode(entry); doReverseKNNQuery(q, childNode, entry, result); } @@ -226,77 +203,76 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O * Adapts the knn distances before insertion of entry q. * * @param q the entry to be inserted - * @param nodeEntry the entry representing the root of thge current subtree + * @param nodeEntry the entry representing the root of the current subtree * @param knns_q the knns of q */ - private void preInsert(MkMaxEntry<D> q, MkMaxEntry<D> nodeEntry, KNNHeap<D> knns_q) { - if(LOG.isDebugging()) { + private void preInsert(MkMaxEntry q, MkMaxEntry nodeEntry, KNNHeap<D> knns_q) { + if (LOG.isDebugging()) { LOG.debugFine("preInsert " + q + " - " + nodeEntry + "\n"); } - D knnDist_q = knns_q.getKNNDistance(); + double knnDist_q = knns_q.getKNNDistance().doubleValue(); MkMaxTreeNode<O, D> node = getNode(nodeEntry); - D knnDist_node = getDistanceQuery().nullDistance(); + double knnDist_node = 0.; // leaf node - if(node.isLeaf()) { - for(int i = 0; i < node.getNumEntries(); i++) { - MkMaxEntry<D> p = node.getEntry(i); - D dist_pq = getDistanceQuery().distance(p.getRoutingObjectID(), q.getRoutingObjectID()); + if (node.isLeaf()) { + for (int i = 0; i < node.getNumEntries(); i++) { + MkMaxEntry p = node.getEntry(i); + D dist_pq = distance(p.getRoutingObjectID(), q.getRoutingObjectID()); // p is nearer to q than the farthest kNN-candidate of q // ==> p becomes a knn-candidate - if(dist_pq.compareTo(knnDist_q) <= 0) { + if (dist_pq.doubleValue() <= knnDist_q) { knns_q.add(dist_pq, p.getRoutingObjectID()); - if(knns_q.size() >= getKmax()) { - knnDist_q = knns_q.getKNNDistance(); + if (knns_q.size() >= getKmax()) { + knnDist_q = knns_q.getKNNDistance().doubleValue(); q.setKnnDistance(knnDist_q); } } // p is nearer to q than to its farthest knn-candidate // q becomes knn of p - if(dist_pq.compareTo(p.getKnnDistance()) <= 0) { - KNNResult<D> knns_p = knnq.getKNNForDBID(p.getRoutingObjectID(), getKmax() - 1); + if (dist_pq.doubleValue() <= p.getKnnDistance()) { + KNNList<D> knns_p = knnq.getKNNForDBID(p.getRoutingObjectID(), getKmax() - 1); - if(knns_p.size() + 1 < getKmax()) { - p.setKnnDistance(getDistanceQuery().undefinedDistance()); - } - else { - D knnDist_p = DistanceUtil.max(dist_pq, knns_p.getKNNDistance()); + if (knns_p.size() + 1 < getKmax()) { + p.setKnnDistance(Double.NaN); + } else { + double knnDist_p = Math.max(dist_pq.doubleValue(), knns_p.getKNNDistance().doubleValue()); p.setKnnDistance(knnDist_p); } } - knnDist_node = DistanceUtil.max(knnDist_node, p.getKnnDistance()); + knnDist_node = Math.max(knnDist_node, p.getKnnDistance()); } } // directory node else { - List<DistanceEntry<D, MkMaxEntry<D>>> entries = getSortedEntries(node, q.getRoutingObjectID()); - for(DistanceEntry<D, MkMaxEntry<D>> distEntry : entries) { - MkMaxEntry<D> dirEntry = distEntry.getEntry(); - D entry_knnDist = dirEntry.getKnnDistance(); + List<DoubleIntPair> entries = getSortedEntries(node, q.getRoutingObjectID()); + for (DoubleIntPair distEntry : entries) { + MkMaxEntry dirEntry = node.getEntry(distEntry.second); + double entry_knnDist = dirEntry.getKnnDistance(); - if(distEntry.getDistance().compareTo(entry_knnDist) < 0 || distEntry.getDistance().compareTo(knnDist_q) < 0) { + if (distEntry.second < entry_knnDist || distEntry.second < knnDist_q) { preInsert(q, dirEntry, knns_q); - knnDist_q = knns_q.getKNNDistance(); + knnDist_q = knns_q.getKNNDistance().doubleValue(); } - knnDist_node = DistanceUtil.max(knnDist_node, dirEntry.getKnnDistance()); + knnDist_node = Math.max(knnDist_node, dirEntry.getKnnDistance()); } } - if(LOG.isDebugging()) { + if (LOG.isDebugging()) { LOG.debugFine(nodeEntry + "set knn dist " + knnDist_node); } nodeEntry.setKnnDistance(knnDist_node); } @Override - protected void initializeCapacities(MkMaxEntry<D> exampleLeaf) { - int distanceSize = exampleLeaf.getParentDistance().externalizableSize(); + protected void initializeCapacities(MkMaxEntry exampleLeaf) { + int distanceSize = ByteArrayUtil.SIZE_DOUBLE; // exampleLeaf.getParentDistance().externalizableSize(); // overhead = index(4), numEntries(4), id(4), isLeaf(0.125) double overhead = 12.125; - if(getPageSize() - overhead < 0) { + if (getPageSize() - overhead < 0) { throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); } @@ -304,11 +280,11 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O // coveringRadius + parentDistance + knnDistance) + 1 dirCapacity = (int) (getPageSize() - overhead) / (4 + 4 + 3 * distanceSize) + 1; - if(dirCapacity <= 1) { + if (dirCapacity <= 1) { throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); } - if(dirCapacity < 10) { + if (dirCapacity < 10) { LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a directory node = " + (dirCapacity - 1)); } @@ -317,11 +293,11 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O // knnDistance) + 1 leafCapacity = (int) (getPageSize() - overhead) / (4 + 2 * distanceSize) + 1; - if(leafCapacity <= 1) { + if (leafCapacity <= 1) { throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); } - if(leafCapacity < 10) { + if (leafCapacity < 10) { LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a leaf node = " + (leafCapacity - 1)); } } @@ -331,7 +307,7 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O */ @Override protected MkMaxTreeNode<O, D> createNewLeafNode() { - return new MkMaxTreeNode<O, D>(leafCapacity, true); + return new MkMaxTreeNode<>(leafCapacity, true); } /** @@ -339,28 +315,28 @@ public class MkMaxTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O */ @Override protected MkMaxTreeNode<O, D> createNewDirectoryNode() { - return new MkMaxTreeNode<O, D>(dirCapacity, false); + return new MkMaxTreeNode<>(dirCapacity, false); } /** * @return a new MkMaxDirectoryEntry representing the specified node */ @Override - protected MkMaxEntry<D> createNewDirectoryEntry(MkMaxTreeNode<O, D> node, DBID routingObjectID, D parentDistance) { - return new MkMaxDirectoryEntry<D>(routingObjectID, parentDistance, node.getPageID(), node.coveringRadius(routingObjectID, this), node.kNNDistance(getDistanceQuery())); + protected MkMaxEntry createNewDirectoryEntry(MkMaxTreeNode<O, D> node, DBID routingObjectID, double parentDistance) { + return new MkMaxDirectoryEntry(routingObjectID, parentDistance, node.getPageID(), node.coveringRadius(routingObjectID, this), node.kNNDistance()); } /** * @return a new MkMaxDirectoryEntry by calling - * <code>new MkMaxDirectoryEntry<D>(null, null, 0, null)</code> + * <code>new MkMaxDirectoryEntry(null, null, 0, null)</code> */ @Override - protected MkMaxEntry<D> createRootEntry() { - return new MkMaxDirectoryEntry<D>(null, null, 0, null, null); + protected MkMaxEntry createRootEntry() { + return new MkMaxDirectoryEntry(null, 0., 0, 0., 0.); } @Override protected Logging getLogger() { return LOG; } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeFactory.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeFactory.java index 7e9a7753..a03006f4 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeFactory.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeFactory.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkmax; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -24,10 +24,11 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkmax; */ 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.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTreeUnifiedFactory; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.MkTreeSettings; import de.lmu.ifi.dbs.elki.persistent.PageFile; +import de.lmu.ifi.dbs.elki.persistent.PageFileFactory; import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil; /** @@ -37,28 +38,25 @@ import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil; * * @apiviz.stereotype factory * @apiviz.uses MkMaxTreeIndex oneway - - «create» - * + * * @param <O> Object type * @param <D> Distance type */ -public class MkMaxTreeFactory<O, D extends Distance<D>> extends AbstractMkTreeUnifiedFactory<O, D, MkMaxTreeNode<O, D>, MkMaxEntry<D>, MkMaxTreeIndex<O, D>> { +public class MkMaxTreeFactory<O, D extends NumberDistance<D, ?>> extends AbstractMkTreeUnifiedFactory<O, D, MkMaxTreeNode<O, D>, MkMaxEntry, MkMaxTreeIndex<O, D>, MkTreeSettings<O, D, MkMaxTreeNode<O, D>, MkMaxEntry>> { /** * Constructor. - * - * @param fileName - * @param pageSize - * @param cacheSize - * @param distanceFunction - * @param k_max + * + * @param pageFileFactory Data storage + * @param settings Tree settings */ - public MkMaxTreeFactory(String fileName, int pageSize, long cacheSize, DistanceFunction<O, D> distanceFunction, int k_max) { - super(fileName, pageSize, cacheSize, distanceFunction, k_max); + public MkMaxTreeFactory(PageFileFactory<?> pageFileFactory, MkTreeSettings<O, D, MkMaxTreeNode<O, D>, MkMaxEntry> settings) { + super(pageFileFactory, settings); } @Override public MkMaxTreeIndex<O, D> instantiate(Relation<O> relation) { PageFile<MkMaxTreeNode<O, D>> pagefile = makePageFile(getNodeClass()); - return new MkMaxTreeIndex<O, D>(relation, pagefile, distanceFunction.instantiate(relation), distanceFunction, k_max); + return new MkMaxTreeIndex<>(relation, pagefile, settings); } protected Class<MkMaxTreeNode<O, D>> getNodeClass() { @@ -72,10 +70,15 @@ public class MkMaxTreeFactory<O, D extends Distance<D>> extends AbstractMkTreeUn * * @apiviz.exclude */ - public static class Parameterizer<O, D extends Distance<D>> extends AbstractMkTreeUnifiedFactory.Parameterizer<O, D> { + public static class Parameterizer<O, D extends NumberDistance<D, ?>> extends AbstractMkTreeUnifiedFactory.Parameterizer<O, D, MkMaxTreeNode<O, D>, MkMaxEntry, MkTreeSettings<O, D, MkMaxTreeNode<O, D>, MkMaxEntry>> { @Override protected MkMaxTreeFactory<O, D> makeInstance() { - return new MkMaxTreeFactory<O, D>(fileName, pageSize, cacheSize, distanceFunction, k_max); + return new MkMaxTreeFactory<>(pageFileFactory, settings); + } + + @Override + protected MkTreeSettings<O, D, MkMaxTreeNode<O, D>, MkMaxEntry> makeSettings() { + return new MkTreeSettings<>(); } } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeIndex.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeIndex.java index 0bd31dca..482c86eb 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeIndex.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeIndex.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkmax; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -31,6 +31,7 @@ import de.lmu.ifi.dbs.elki.database.ids.DBIDIter; 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.DBIDs; +import de.lmu.ifi.dbs.elki.database.ids.distance.KNNList; import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery; import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery; @@ -38,68 +39,72 @@ import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery; import de.lmu.ifi.dbs.elki.database.query.rknn.RKNNQuery; 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.distanceresultlist.KNNResult; import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; +import de.lmu.ifi.dbs.elki.index.DynamicIndex; import de.lmu.ifi.dbs.elki.index.KNNIndex; import de.lmu.ifi.dbs.elki.index.RKNNIndex; import de.lmu.ifi.dbs.elki.index.RangeIndex; -import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; -import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTreeUnified; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.MkTreeSettings; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MTreeQueryUtil; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MkTreeRKNNQuery; import de.lmu.ifi.dbs.elki.persistent.PageFile; import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages; +import de.lmu.ifi.dbs.elki.utilities.exceptions.NotImplementedException; /** * MkMax tree * * @author Elke Achtert - * + * * @param <O> Object type * @param <D> Distance type */ -public class MkMaxTreeIndex<O, D extends Distance<D>> extends MkMaxTree<O, D> implements RangeIndex<O>, KNNIndex<O>, RKNNIndex<O> { +public class MkMaxTreeIndex<O, D extends NumberDistance<D, ?>> extends MkMaxTree<O, D> implements RangeIndex<O>, KNNIndex<O>, RKNNIndex<O>, DynamicIndex { /** * Relation indexed. */ private Relation<O> relation; - + /** * Constructor. * * @param relation Relation * @param pagefile Page file - * @param distanceQuery Distance query - * @param distanceFunction Distance function - * @param k_max Maximum value for k + * @param settings Tree settings */ - public MkMaxTreeIndex(Relation<O> relation, PageFile<MkMaxTreeNode<O, D>> pagefile, DistanceQuery<O, D> distanceQuery, DistanceFunction<O, D> distanceFunction, int k_max) { - super(pagefile, distanceQuery, distanceFunction, k_max); + public MkMaxTreeIndex(Relation<O> relation, PageFile<MkMaxTreeNode<O, D>> pagefile, MkTreeSettings<O, D, MkMaxTreeNode<O, D>, MkMaxEntry> settings) { + super(relation, pagefile, settings); this.relation = relation; - this.initialize(); } /** * @return a new MkMaxLeafEntry representing the specified data object */ - protected MkMaxLeafEntry<D> createNewLeafEntry(DBID id, O object, D parentDistance) { - KNNResult<D> knns = knnq.getKNNForObject(object, getKmax() - 1); - D knnDistance = knns.getKNNDistance(); - return new MkMaxLeafEntry<D>(id, parentDistance, knnDistance); + protected MkMaxLeafEntry createNewLeafEntry(DBID id, O object, double parentDistance) { + KNNList<D> knns = knnq.getKNNForObject(object, getKmax() - 1); + double knnDistance = knns.getKNNDistance().doubleValue(); + return new MkMaxLeafEntry(id, parentDistance, knnDistance); + } + + @Override + public void initialize() { + super.initialize(); + insertAll(relation.getDBIDs()); } @Override public void insert(DBIDRef id) { - insert(createNewLeafEntry(DBIDUtil.deref(id), relation.get(id), getDistanceFactory().undefinedDistance()), false); + insert(createNewLeafEntry(DBIDUtil.deref(id), relation.get(id), Double.NaN), false); } @Override public void insertAll(DBIDs ids) { - List<MkMaxEntry<D>> objs = new ArrayList<MkMaxEntry<D>>(ids.size()); + List<MkMaxEntry> objs = new ArrayList<>(ids.size()); for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) { DBID id = DBIDUtil.deref(iter); final O object = relation.get(id); - objs.add(createNewLeafEntry(id, object, getDistanceFactory().undefinedDistance())); + objs.add(createNewLeafEntry(id, object, Double.NaN)); } insertAll(objs); } @@ -113,7 +118,7 @@ public class MkMaxTreeIndex<O, D extends Distance<D>> extends MkMaxTree<O, D> im */ @Override public final boolean delete(DBIDRef id) { - throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET); + throw new NotImplementedException(ExceptionMessages.UNSUPPORTED_NOT_YET); } /** @@ -125,78 +130,75 @@ public class MkMaxTreeIndex<O, D extends Distance<D>> extends MkMaxTree<O, D> im */ @Override public void deleteAll(DBIDs ids) { - throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET); + throw new NotImplementedException(ExceptionMessages.UNSUPPORTED_NOT_YET); } @SuppressWarnings("unchecked") @Override public <S extends Distance<S>> KNNQuery<O, S> getKNNQuery(DistanceQuery<O, S> distanceQuery, Object... hints) { // Query on the relation we index - if(distanceQuery.getRelation() != relation) { + if (distanceQuery.getRelation() != relation) { return null; } - DistanceFunction<? super O, S> distanceFunction = distanceQuery.getDistanceFunction(); - if(!this.distanceFunction.equals(distanceFunction)) { - if(getLogger().isDebugging()) { + DistanceFunction<? super O, D> distanceFunction = (DistanceFunction<? super O, D>) distanceQuery.getDistanceFunction(); + if (!this.getDistanceFunction().equals(distanceFunction)) { + if (getLogger().isDebugging()) { getLogger().debug("Distance function not supported by index - or 'equals' not implemented right!"); } return null; } // Bulk is not yet supported - for(Object hint : hints) { - if(hint == DatabaseQuery.HINT_BULK) { + for (Object hint : hints) { + if (hint == DatabaseQuery.HINT_BULK) { return null; } } - AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this; - DistanceQuery<O, S> dq = distanceFunction.instantiate(relation); - return MTreeQueryUtil.getKNNQuery(idx, dq, hints); + DistanceQuery<O, D> dq = distanceFunction.instantiate(relation); + return (KNNQuery<O, S>) MTreeQueryUtil.getKNNQuery(this, dq, hints); } @SuppressWarnings("unchecked") @Override public <S extends Distance<S>> RangeQuery<O, S> getRangeQuery(DistanceQuery<O, S> distanceQuery, Object... hints) { // Query on the relation we index - if(distanceQuery.getRelation() != relation) { + if (distanceQuery.getRelation() != relation) { return null; } - DistanceFunction<? super O, S> distanceFunction = distanceQuery.getDistanceFunction(); - if(!this.distanceFunction.equals(distanceFunction)) { - if(getLogger().isDebugging()) { + DistanceFunction<? super O, D> distanceFunction = (DistanceFunction<? super O, D>) distanceQuery.getDistanceFunction(); + if (!this.getDistanceFunction().equals(distanceFunction)) { + if (getLogger().isDebugging()) { getLogger().debug("Distance function not supported by index - or 'equals' not implemented right!"); } return null; } // Bulk is not yet supported - for(Object hint : hints) { - if(hint == DatabaseQuery.HINT_BULK) { + for (Object hint : hints) { + if (hint == DatabaseQuery.HINT_BULK) { return null; } } - AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this; - DistanceQuery<O, S> dq = distanceFunction.instantiate(relation); - return MTreeQueryUtil.getRangeQuery(idx, dq); + DistanceQuery<O, D> dq = distanceFunction.instantiate(relation); + return (RangeQuery<O, S>) MTreeQueryUtil.getRangeQuery(this, dq); } @SuppressWarnings("unchecked") @Override public <S extends Distance<S>> RKNNQuery<O, S> getRKNNQuery(DistanceQuery<O, S> distanceQuery, Object... hints) { - DistanceFunction<? super O, S> distanceFunction = distanceQuery.getDistanceFunction(); - if(!this.getDistanceFunction().equals(distanceFunction)) { - if(getLogger().isDebugging()) { + DistanceFunction<? super O, D> distanceFunction = (DistanceFunction<? super O, D>) distanceQuery.getDistanceFunction(); + if (!this.getDistanceFunction().equals(distanceFunction)) { + if (getLogger().isDebugging()) { getLogger().debug("Distance function not supported by index - or 'equals' not implemented right!"); } return null; } // Bulk is not yet supported - for(Object hint : hints) { - if(hint == DatabaseQuery.HINT_BULK) { + for (Object hint : hints) { + if (hint == DatabaseQuery.HINT_BULK) { return null; } } - AbstractMkTreeUnified<O, S, ?, ?> idx = (AbstractMkTreeUnified<O, S, ?, ?>) this; - DistanceQuery<O, S> dq = distanceFunction.instantiate(relation); - return new MkTreeRKNNQuery<O, S>(idx, dq); + DistanceQuery<O, D> dq = distanceFunction.instantiate(relation); + return (RKNNQuery<O, S>) new MkTreeRKNNQuery<>(this, dq); } @Override @@ -208,4 +210,4 @@ public class MkMaxTreeIndex<O, D extends Distance<D>> extends MkMaxTree<O, D> im public String getShortName() { return "mkmaxtree"; } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeNode.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeNode.java index 17f85498..84c8bdc4 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeNode.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/MkMaxTreeNode.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkmax; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -24,9 +24,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mkmax; */ import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; -import de.lmu.ifi.dbs.elki.distance.DistanceUtil; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; @@ -40,7 +38,7 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; * @param <O> the type of DatabaseObject to be stored in the MkMaxTree * @param <D> the type of Distance used in the MkMaxTree */ -class MkMaxTreeNode<O, D extends Distance<D>> extends AbstractMTreeNode<O, D, MkMaxTreeNode<O, D>, MkMaxEntry<D>> { +class MkMaxTreeNode<O, D extends NumberDistance<D, ?>> extends AbstractMTreeNode<O, D, MkMaxTreeNode<O, D>, MkMaxEntry> { /** * Serial version */ @@ -68,14 +66,13 @@ class MkMaxTreeNode<O, D extends Distance<D>> extends AbstractMTreeNode<O, D, Mk * Determines and returns the k-nearest neighbor distance of this node as the * maximum of the k-nearest neighbor distances of all entries. * - * @param distanceFunction the distance function * @return the knn distance of this node */ - protected D kNNDistance(DistanceQuery<O, D> distanceFunction) { - D knnDist = distanceFunction.nullDistance(); - for(int i = 0; i < getNumEntries(); i++) { - MkMaxEntry<D> entry = getEntry(i); - knnDist = DistanceUtil.max(knnDist, entry.getKnnDistance()); + protected double kNNDistance() { + double knnDist = 0.; + for (int i = 0; i < getNumEntries(); i++) { + MkMaxEntry entry = getEntry(i); + knnDist = Math.max(knnDist, entry.getKnnDistance()); } return knnDist; } @@ -86,10 +83,10 @@ class MkMaxTreeNode<O, D extends Distance<D>> extends AbstractMTreeNode<O, D, Mk * all its entries. */ @Override - public void adjustEntry(MkMaxEntry<D> entry, DBID routingObjectID, D parentDistance, AbstractMTree<O, D, MkMaxTreeNode<O, D>, MkMaxEntry<D>> mTree) { + public void adjustEntry(MkMaxEntry entry, DBID routingObjectID, double parentDistance, AbstractMTree<O, D, MkMaxTreeNode<O, D>, MkMaxEntry, ?> mTree) { super.adjustEntry(entry, routingObjectID, parentDistance, mTree); // adjust knn distance - entry.setKnnDistance(kNNDistance(mTree.getDistanceQuery())); + entry.setKnnDistance(kNNDistance()); } /** @@ -97,15 +94,13 @@ class MkMaxTreeNode<O, D extends Distance<D>> extends AbstractMTreeNode<O, D, Mk * node is correctly set. */ @Override - protected void integrityCheckParameters(MkMaxEntry<D> parentEntry, MkMaxTreeNode<O, D> parent, int index, AbstractMTree<O, D, MkMaxTreeNode<O, D>, MkMaxEntry<D>> mTree) { + protected void integrityCheckParameters(MkMaxEntry parentEntry, MkMaxTreeNode<O, D> parent, int index, AbstractMTree<O, D, MkMaxTreeNode<O, D>, MkMaxEntry, ?> mTree) { super.integrityCheckParameters(parentEntry, parent, index, mTree); // test if knn distance is correctly set - MkMaxEntry<D> entry = parent.getEntry(index); - D knnDistance = kNNDistance(mTree.getDistanceQuery()); - if(!entry.getKnnDistance().equals(knnDistance)) { - String soll = knnDistance.toString(); - String ist = entry.getKnnDistance().toString(); - throw new RuntimeException("Wrong knnDistance in node " + parent.getPageID() + " at index " + index + " (child " + entry + ")" + "\nsoll: " + soll + ",\n ist: " + ist); + MkMaxEntry entry = parent.getEntry(index); + double knnDistance = kNNDistance(); + if (Math.abs(entry.getKnnDistance() - knnDistance) > 0) { + throw new RuntimeException("Wrong knnDistance in node " + parent.getPageID() + " at index " + index + " (child " + entry + ")" + "\nsoll: " + knnDistance + ",\n ist: " + entry.getKnnDistance()); } } } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/package-info.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/package-info.java index bcde11b4..7bd90d66 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/package-info.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mkmax/package-info.java @@ -5,7 +5,7 @@ This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures -Copyright (C) 2012 +Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabDirectoryEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabDirectoryEntry.java index f8410ad9..9f088448 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabDirectoryEntry.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabDirectoryEntry.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mktab; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -26,11 +26,8 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mktab; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; -import java.util.ArrayList; -import java.util.List; import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeDirectoryEntry; /** @@ -39,20 +36,14 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeDirectoryEntry * parameters k <= k_max. * * @author Elke Achtert - * */ -class MkTabDirectoryEntry<D extends Distance<D>> extends MTreeDirectoryEntry<D> implements MkTabEntry<D> { - private static final long serialVersionUID = 1; - - /** - * The maximal number of knn distances to be stored. - */ - private int k_max; +class MkTabDirectoryEntry extends MTreeDirectoryEntry implements MkTabEntry { + private static final long serialVersionUID = 2; /** * The aggregated knn distances of the underlying node. */ - private List<D> knnDistances; + private double[] knnDistances; /** * Empty constructor for serialization purposes. @@ -71,34 +62,28 @@ class MkTabDirectoryEntry<D extends Distance<D>> extends MTreeDirectoryEntry<D> * @param coveringRadius the covering radius of the entry * @param knnDistances the aggregated knn distances of the underlying node */ - public MkTabDirectoryEntry(DBID objectID, D parentDistance, Integer nodeID, D coveringRadius, List<D> knnDistances) { + public MkTabDirectoryEntry(DBID objectID, double parentDistance, Integer nodeID, double coveringRadius, double[] knnDistances) { super(objectID, parentDistance, nodeID, coveringRadius); this.knnDistances = knnDistances; - this.k_max = knnDistances.size(); } @Override - public List<D> getKnnDistances() { + public double[] getKnnDistances() { return knnDistances; } @Override - public void setKnnDistances(List<D> knnDistances) { + public void setKnnDistances(double[] knnDistances) { this.knnDistances = knnDistances; } @Override - public D getKnnDistance(int k) { - if(k > this.k_max) { + public double getKnnDistance(int k) { + if (k >= this.knnDistances.length) { throw new IllegalArgumentException("Parameter k = " + k + " is not supported!"); } - return knnDistances.get(k - 1); - } - - @Override - public int getK_max() { - return k_max; + return knnDistances[k - 1]; } /** @@ -111,9 +96,10 @@ class MkTabDirectoryEntry<D extends Distance<D>> extends MTreeDirectoryEntry<D> @Override public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal(out); + int k_max = knnDistances.length; out.writeInt(k_max); - for(int i = 0; i < k_max; i++) { - out.writeObject(knnDistances.get(i)); + for (int i = 0; i < k_max; i++) { + out.writeDouble(knnDistances[i]); } } @@ -130,10 +116,10 @@ class MkTabDirectoryEntry<D extends Distance<D>> extends MTreeDirectoryEntry<D> @SuppressWarnings("unchecked") public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); - k_max = in.readInt(); - knnDistances = new ArrayList<D>(); - for(int i = 0; i < k_max; i++) { - knnDistances.add((D) in.readObject()); + int k_max = in.readInt(); + knnDistances = new double[k_max]; + for (int i = 0; i < k_max; i++) { + knnDistances[i] = in.readDouble(); } } @@ -148,21 +134,21 @@ class MkTabDirectoryEntry<D extends Distance<D>> extends MTreeDirectoryEntry<D> @Override @SuppressWarnings("unchecked") public boolean equals(Object o) { - if(this == o) { + if (this == o) { return true; } - if(o == null || getClass() != o.getClass()) { + if (o == null || getClass() != o.getClass()) { return false; } - if(!super.equals(o)) { + if (!super.equals(o)) { return false; } - final MkTabDirectoryEntry<D> that = (MkTabDirectoryEntry<D>) o; + final MkTabDirectoryEntry that = (MkTabDirectoryEntry) o; - if(k_max != that.k_max) { + if (knnDistances.length != that.knnDistances.length) { return false; } return !(knnDistances != null ? !knnDistances.equals(that.knnDistances) : that.knnDistances != null); } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabEntry.java index ba938fd3..a741ed5b 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabEntry.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabEntry.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mktab; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -23,46 +23,35 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mktab; along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import java.util.List; - -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; /** - * Defines the requirements for an entry in an MkCop-Tree node. - * Additionally to an entry in an M-Tree an MkTabEntry holds a list of knn distances - * for for parameters k <= k_max of the underlying data object or MkTab-Tree node. - * + * Defines the requirements for an entry in an MkCop-Tree node. Additionally to + * an entry in an M-Tree an MkTabEntry holds a list of knn distances for for + * parameters k <= k_max of the underlying data object or MkTab-Tree node. + * * @author Elke Achtert */ -interface MkTabEntry<D extends Distance<D>> extends MTreeEntry<D> { +interface MkTabEntry extends MTreeEntry { /** * Returns the list of knn distances of the entry. - * - * @return the list of knn distances of the entry + * + * @return the list of knn distances of the entry */ - public List<D> getKnnDistances(); + public double[] getKnnDistances(); /** * Sets the knn distances of the entry. - * + * * @param knnDistances the knn distances to be set */ - public void setKnnDistances(List<D> knnDistances); + public void setKnnDistances(double[] knnDistances); /** * Returns the knn distance of the entry for the specified parameter k. - * + * * @param k the parameter k of the knn distance * @return the knn distance of the entry */ - public D getKnnDistance(int k); - - /** - * Returns the parameter k_max. - * - * @return the parameter k_max - */ - public int getK_max(); - + public double getKnnDistance(int k); } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabLeafEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabLeafEntry.java index 81446718..969ba781 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabLeafEntry.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabLeafEntry.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mktab; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -26,11 +26,8 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mktab; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; -import java.util.ArrayList; -import java.util.List; import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeLeafEntry; /** @@ -40,18 +37,13 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeLeafEntry; * * @author Elke Achtert */ -class MkTabLeafEntry<D extends Distance<D>> extends MTreeLeafEntry<D> implements MkTabEntry<D> { - private static final long serialVersionUID = 1; - - /** - * The maximal number of knn distances to be stored. - */ - private int k_max; +class MkTabLeafEntry extends MTreeLeafEntry implements MkTabEntry { + private static final long serialVersionUID = 2; /** * The knn distances of the underlying data object. */ - private List<D> knnDistances; + private double[] knnDistances; /** * Empty constructor for serialization purposes. @@ -68,38 +60,32 @@ class MkTabLeafEntry<D extends Distance<D>> extends MTreeLeafEntry<D> implements * parent's routing object * @param knnDistances the knn distances of the underlying data object */ - public MkTabLeafEntry(DBID objectID, D parentDistance, List<D> knnDistances) { + public MkTabLeafEntry(DBID objectID, double parentDistance, double[] knnDistances) { super(objectID, parentDistance); this.knnDistances = knnDistances; - this.k_max = knnDistances.size(); } @Override - public List<D> getKnnDistances() { + public double[] getKnnDistances() { return knnDistances; } @Override - public void setKnnDistances(List<D> knnDistances) { - if(knnDistances.size() != this.k_max) { - throw new IllegalArgumentException("Wrong length of knn distances: " + knnDistances.size()); + public void setKnnDistances(double[] knnDistances) { + if (knnDistances.length != this.knnDistances.length) { + throw new IllegalArgumentException("Wrong length of knn distances: " + knnDistances.length); } this.knnDistances = knnDistances; } @Override - public D getKnnDistance(int k) { - if(k > this.k_max) { + public double getKnnDistance(int k) { + if (k >= this.knnDistances.length) { throw new IllegalArgumentException("Parameter k = " + k + " is not supported!"); } - return knnDistances.get(k - 1); - } - - @Override - public int getK_max() { - return k_max; + return knnDistances[k - 1]; } /** @@ -112,9 +98,10 @@ class MkTabLeafEntry<D extends Distance<D>> extends MTreeLeafEntry<D> implements @Override public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal(out); + int k_max = knnDistances.length; out.writeInt(k_max); - for(int i = 0; i < k_max; i++) { - out.writeObject(knnDistances.get(i)); + for (int i = 0; i < k_max; i++) { + out.writeDouble(knnDistances[i]); } } @@ -131,10 +118,10 @@ class MkTabLeafEntry<D extends Distance<D>> extends MTreeLeafEntry<D> implements @SuppressWarnings("unchecked") public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); - k_max = in.readInt(); - knnDistances = new ArrayList<D>(); - for(int i = 0; i < k_max; i++) { - knnDistances.add((D) in.readObject()); + int k_max = in.readInt(); + knnDistances = new double[k_max]; + for (int i = 0; i < k_max; i++) { + knnDistances[i] = in.readDouble(); } } @@ -148,21 +135,21 @@ class MkTabLeafEntry<D extends Distance<D>> extends MTreeLeafEntry<D> implements @Override @SuppressWarnings("unchecked") public boolean equals(Object o) { - if(this == o) { + if (this == o) { return true; } - if(o == null || getClass() != o.getClass()) { + if (o == null || getClass() != o.getClass()) { return false; } - if(!super.equals(o)) { + if (!super.equals(o)) { return false; } - final MkTabLeafEntry<D> that = (MkTabLeafEntry<D>) o; + final MkTabLeafEntry that = (MkTabLeafEntry) o; - if(k_max != that.k_max) { + if (knnDistances.length != that.knnDistances.length) { return false; } return !(knnDistances != null ? !knnDistances.equals(that.knnDistances) : that.knnDistances != null); } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTree.java index f5410839..481392cb 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTree.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTree.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mktab; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -23,22 +23,20 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mktab; along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import java.util.ArrayList; -import java.util.List; import java.util.Map; import de.lmu.ifi.dbs.elki.database.ids.DBID; import de.lmu.ifi.dbs.elki.database.ids.DBIDRef; -import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; -import de.lmu.ifi.dbs.elki.distance.DistanceUtil; -import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.database.ids.distance.DistanceDBIDList; +import de.lmu.ifi.dbs.elki.database.ids.distance.DistanceDBIDListIter; +import de.lmu.ifi.dbs.elki.database.ids.distance.KNNList; +import de.lmu.ifi.dbs.elki.database.ids.generic.GenericDistanceDBIDList; +import de.lmu.ifi.dbs.elki.database.relation.Relation; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTreeUnified; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.MkTreeSettings; import de.lmu.ifi.dbs.elki.logging.Logging; +import de.lmu.ifi.dbs.elki.persistent.ByteArrayUtil; import de.lmu.ifi.dbs.elki.persistent.PageFile; /** @@ -54,22 +52,21 @@ import de.lmu.ifi.dbs.elki.persistent.PageFile; * @param <O> Object type * @param <D> Distance type */ -public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O, D, MkTabTreeNode<O, D>, MkTabEntry<D>> { +public class MkTabTree<O, D extends NumberDistance<D, ?>> extends AbstractMkTreeUnified<O, D, MkTabTreeNode<O, D>, MkTabEntry, MkTreeSettings<O, D, MkTabTreeNode<O, D>, MkTabEntry>> { /** * The logger for this class. */ private static final Logging LOG = Logging.getLogger(MkTabTree.class); - + /** * Constructor. * + * @param relation Relation * @param pagefile Page file - * @param distanceQuery Distance query - * @param distanceFunction Distance function - * @param k_max Maximum value for k + * @param settings Settings */ - public MkTabTree(PageFile<MkTabTreeNode<O, D>> pagefile, DistanceQuery<O, D> distanceQuery, DistanceFunction<O, D> distanceFunction, int k_max) { - super(pagefile, distanceQuery, distanceFunction, k_max); + public MkTabTree(Relation<O> relation, PageFile<MkTabTreeNode<O, D>> pagefile, MkTreeSettings<O, D, MkTabTreeNode<O, D>, MkTabEntry> settings) { + super(relation, pagefile, settings); } /** @@ -77,7 +74,7 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O * not supported */ @Override - protected void preInsert(MkTabEntry<D> entry) { + protected void preInsert(MkTabEntry entry) { throw new UnsupportedOperationException("Insertion of single objects is not supported!"); } @@ -86,17 +83,17 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O * not supported */ @Override - public void insert(MkTabEntry<D> entry, boolean withPreInsert) { + public void insert(MkTabEntry entry, boolean withPreInsert) { throw new UnsupportedOperationException("Insertion of single objects is not supported!"); } @Override - public DistanceDBIDResult<D> reverseKNNQuery(DBIDRef id, int k) { - if(k > this.getKmax()) { + public DistanceDBIDList<D> reverseKNNQuery(DBIDRef id, int k) { + if (k > this.getKmax()) { throw new IllegalArgumentException("Parameter k has to be less or equal than " + "parameter kmax of the MkTab-Tree!"); } - GenericDistanceDBIDList<D> result = new GenericDistanceDBIDList<D>(); + GenericDistanceDBIDList<D> result = new GenericDistanceDBIDList<>(); doReverseKNNQuery(k, id, null, getRoot(), result); result.sort(); @@ -104,12 +101,12 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O } @Override - protected void initializeCapacities(MkTabEntry<D> exampleLeaf) { - int distanceSize = exampleLeaf.getParentDistance().externalizableSize(); + protected void initializeCapacities(MkTabEntry exampleLeaf) { + int distanceSize = ByteArrayUtil.SIZE_DOUBLE; // exampleLeaf.getParentDistance().externalizableSize(); // overhead = index(4), numEntries(4), id(4), isLeaf(0.125) double overhead = 12.125; - if(getPageSize() - overhead < 0) { + if (getPageSize() - overhead < 0) { throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); } @@ -117,11 +114,11 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O // coveringRadius + parentDistance + kmax + kmax * knnDistance) + 1 dirCapacity = (int) (getPageSize() - overhead) / (4 + 4 + distanceSize + distanceSize + 4 + getKmax() * distanceSize) + 1; - if(dirCapacity <= 1) { + if (dirCapacity <= 1) { throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); } - if(dirCapacity < 10) { + if (dirCapacity < 10) { LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a directory node = " + (dirCapacity - 1)); } @@ -129,30 +126,37 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O // kmax + kmax * knnDistance) + 1 leafCapacity = (int) (getPageSize() - overhead) / (4 + distanceSize + 4 + getKmax() * distanceSize) + 1; - if(leafCapacity <= 1) { + if (leafCapacity <= 1) { throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); } - if(leafCapacity < 10) { + if (leafCapacity < 10) { LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a leaf node = " + (leafCapacity - 1)); } } - + @Override - protected void kNNdistanceAdjustment(MkTabEntry<D> entry, Map<DBID, KNNResult<D>> knnLists) { + protected void kNNdistanceAdjustment(MkTabEntry entry, Map<DBID, KNNList<D>> knnLists) { MkTabTreeNode<O, D> node = getNode(entry); - List<D> knnDistances_node = initKnnDistanceList(); - if(node.isLeaf()) { - for(int i = 0; i < node.getNumEntries(); i++) { - MkTabEntry<D> leafEntry = node.getEntry(i); - leafEntry.setKnnDistances(KNNUtil.asDistanceList(knnLists.get(getPageID(leafEntry)))); + double[] knnDistances_node = initKnnDistanceList(); + if (node.isLeaf()) { + for (int i = 0; i < node.getNumEntries(); i++) { + MkTabEntry leafEntry = node.getEntry(i); + KNNList<D> knns = knnLists.get(getPageID(leafEntry)); + double[] distances = new double[knns.size()]; + int j = 0; + for (DistanceDBIDListIter<D> iter = knns.iter(); iter.valid(); iter.advance(), j++) { + distances[i] = iter.getDistance().doubleValue(); + } + leafEntry.setKnnDistances(distances); + // FIXME: save copy knnDistances_node = max(knnDistances_node, leafEntry.getKnnDistances()); } - } - else { - for(int i = 0; i < node.getNumEntries(); i++) { - MkTabEntry<D> dirEntry = node.getEntry(i); + } else { + for (int i = 0; i < node.getNumEntries(); i++) { + MkTabEntry dirEntry = node.getEntry(i); kNNdistanceAdjustment(dirEntry, knnLists); + // FIXME: save copy knnDistances_node = max(knnDistances_node, dirEntry.getKnnDistances()); } } @@ -161,7 +165,7 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O @Override protected MkTabTreeNode<O, D> createNewLeafNode() { - return new MkTabTreeNode<O, D>(leafCapacity, true); + return new MkTabTreeNode<>(leafCapacity, true); } /** @@ -171,7 +175,7 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O */ @Override protected MkTabTreeNode<O, D> createNewDirectoryNode() { - return new MkTabTreeNode<O, D>(dirCapacity, false); + return new MkTabTreeNode<>(dirCapacity, false); } /** @@ -183,8 +187,8 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O * the routing object of the parent node */ @Override - protected MkTabEntry<D> createNewDirectoryEntry(MkTabTreeNode<O, D> node, DBID routingObjectID, D parentDistance) { - return new MkTabDirectoryEntry<D>(routingObjectID, parentDistance, node.getPageID(), node.coveringRadius(routingObjectID, this), node.kNNDistances(getDistanceQuery())); + protected MkTabEntry createNewDirectoryEntry(MkTabTreeNode<O, D> node, DBID routingObjectID, double parentDistance) { + return new MkTabDirectoryEntry(routingObjectID, parentDistance, node.getPageID(), node.coveringRadius(routingObjectID, this), node.kNNDistances()); } /** @@ -193,8 +197,8 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O * @return an entry representing the root node */ @Override - protected MkTabEntry<D> createRootEntry() { - return new MkTabDirectoryEntry<D>(null, null, 0, null, initKnnDistanceList()); + protected MkTabEntry createRootEntry() { + return new MkTabDirectoryEntry(null, 0., 0, 0., initKnnDistanceList()); } /** @@ -209,13 +213,13 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O * @param node the root of the subtree * @param result the list holding the query result */ - private void doReverseKNNQuery(int k, DBIDRef q, MkTabEntry<D> node_entry, MkTabTreeNode<O, D> node, GenericDistanceDBIDList<D> result) { + private void doReverseKNNQuery(int k, DBIDRef q, MkTabEntry node_entry, MkTabTreeNode<O, D> node, GenericDistanceDBIDList<D> result) { // data node - if(node.isLeaf()) { - for(int i = 0; i < node.getNumEntries(); i++) { - MkTabEntry<D> entry = node.getEntry(i); - D distance = getDistanceQuery().distance(entry.getRoutingObjectID(), q); - if(distance.compareTo(entry.getKnnDistance(k)) <= 0) { + if (node.isLeaf()) { + for (int i = 0; i < node.getNumEntries(); i++) { + MkTabEntry entry = node.getEntry(i); + D distance = distance(entry.getRoutingObjectID(), q); + if (distance.doubleValue() <= entry.getKnnDistance(k)) { result.add(distance, entry.getRoutingObjectID()); } } @@ -223,14 +227,14 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O // directory node else { - for(int i = 0; i < node.getNumEntries(); i++) { - MkTabEntry<D> entry = node.getEntry(i); - D node_knnDist = node_entry != null ? node_entry.getKnnDistance(k) : getDistanceQuery().infiniteDistance(); + for (int i = 0; i < node.getNumEntries(); i++) { + MkTabEntry entry = node.getEntry(i); + double node_knnDist = node_entry != null ? node_entry.getKnnDistance(k) : Double.POSITIVE_INFINITY; - D distance = getDistanceQuery().distance(entry.getRoutingObjectID(), q); - D minDist = entry.getCoveringRadius().compareTo(distance) > 0 ? getDistanceQuery().nullDistance() : distance.minus(entry.getCoveringRadius()); + double distance = distance(entry.getRoutingObjectID(), q).doubleValue(); + double minDist = (entry.getCoveringRadius() > distance) ? 0. : distance - entry.getCoveringRadius(); - if(minDist.compareTo(node_knnDist) <= 0) { + if (minDist <= node_knnDist) { MkTabTreeNode<O, D> childNode = getNode(entry); doReverseKNNQuery(k, q, entry, childNode, result); } @@ -247,17 +251,14 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O * @return an array that holds the maximum values of the both specified arrays * in each index */ - private List<D> max(List<D> distances1, List<D> distances2) { - if(distances1.size() != distances2.size()) { + private double[] max(double[] distances1, double[] distances2) { + if (distances1.length != distances2.length) { throw new RuntimeException("different lengths!"); } - List<D> result = new ArrayList<D>(); - - for(int i = 0; i < distances1.size(); i++) { - D d1 = distances1.get(i); - D d2 = distances2.get(i); - result.add(DistanceUtil.max(d1, d2)); + double[] result = new double[distances1.length]; + for (int i = 0; i < distances1.length; i++) { + result[i] = Math.max(distances1[i], distances2[i]); } return result; } @@ -267,16 +268,13 @@ public class MkTabTree<O, D extends Distance<D>> extends AbstractMkTreeUnified<O * * @return a knn distance list with all distances set to null distance */ - private List<D> initKnnDistanceList() { - List<D> knnDistances = new ArrayList<D>(getKmax()); - for(int i = 0; i < getKmax(); i++) { - knnDistances.add(getDistanceQuery().nullDistance()); - } + private double[] initKnnDistanceList() { + double[] knnDistances = new double[getKmax()]; return knnDistances; } - + @Override protected Logging getLogger() { return LOG; } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeFactory.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeFactory.java index 2bc6c256..9ede76ad 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeFactory.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeFactory.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mktab; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -24,10 +24,11 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mktab; */ 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.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTreeUnifiedFactory; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.MkTreeSettings; import de.lmu.ifi.dbs.elki.persistent.PageFile; +import de.lmu.ifi.dbs.elki.persistent.PageFileFactory; import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil; /** @@ -41,24 +42,21 @@ import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil; * @param <O> Object type * @param <D> Distance type */ -public class MkTabTreeFactory<O, D extends Distance<D>> extends AbstractMkTreeUnifiedFactory<O, D, MkTabTreeNode<O, D>, MkTabEntry<D>, MkTabTreeIndex<O, D>> { +public class MkTabTreeFactory<O, D extends NumberDistance<D, ?>> extends AbstractMkTreeUnifiedFactory<O, D, MkTabTreeNode<O, D>, MkTabEntry, MkTabTreeIndex<O, D>, MkTreeSettings<O, D, MkTabTreeNode<O, D>, MkTabEntry>> { /** * Constructor. * - * @param fileName - * @param pageSize - * @param cacheSize - * @param distanceFunction - * @param k_max + * @param pageFileFactory Data storage + * @param settings Tree settings */ - public MkTabTreeFactory(String fileName, int pageSize, long cacheSize, DistanceFunction<O, D> distanceFunction, int k_max) { - super(fileName, pageSize, cacheSize, distanceFunction, k_max); + public MkTabTreeFactory(PageFileFactory<?> pageFileFactory, MkTreeSettings<O, D, MkTabTreeNode<O, D>, MkTabEntry> settings) { + super(pageFileFactory, settings); } @Override public MkTabTreeIndex<O, D> instantiate(Relation<O> relation) { PageFile<MkTabTreeNode<O, D>> pagefile = makePageFile(getNodeClass()); - return new MkTabTreeIndex<O, D>(relation, pagefile, distanceFunction.instantiate(relation), distanceFunction, k_max); + return new MkTabTreeIndex<>(relation, pagefile, settings); } protected Class<MkTabTreeNode<O, D>> getNodeClass() { @@ -72,10 +70,15 @@ public class MkTabTreeFactory<O, D extends Distance<D>> extends AbstractMkTreeUn * * @apiviz.exclude */ - public static class Parameterizer<O, D extends Distance<D>> extends AbstractMkTreeUnifiedFactory.Parameterizer<O, D> { + public static class Parameterizer<O, D extends NumberDistance<D, ?>> extends AbstractMkTreeUnifiedFactory.Parameterizer<O, D, MkTabTreeNode<O, D>, MkTabEntry, MkTreeSettings<O, D, MkTabTreeNode<O, D>, MkTabEntry>> { @Override protected MkTabTreeFactory<O, D> makeInstance() { - return new MkTabTreeFactory<O, D>(fileName, pageSize, cacheSize, distanceFunction, k_max); + return new MkTabTreeFactory<>(pageFileFactory, settings); + } + + @Override + protected MkTreeSettings<O, D, MkTabTreeNode<O, D>, MkTabEntry> makeSettings() { + return new MkTreeSettings<>(); } } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeIndex.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeIndex.java index b12ac059..150f03e8 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeIndex.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeIndex.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mktab; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -28,9 +28,9 @@ import java.util.List; import de.lmu.ifi.dbs.elki.database.ids.DBID; import de.lmu.ifi.dbs.elki.database.ids.DBIDIter; -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.DBIDs; +import de.lmu.ifi.dbs.elki.database.ids.distance.DistanceDBIDListIter; +import de.lmu.ifi.dbs.elki.database.ids.distance.KNNList; import de.lmu.ifi.dbs.elki.database.query.DatabaseQuery; import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery; @@ -38,18 +38,15 @@ import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery; import de.lmu.ifi.dbs.elki.database.query.rknn.RKNNQuery; 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.distanceresultlist.KNNResult; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil; import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.KNNIndex; import de.lmu.ifi.dbs.elki.index.RKNNIndex; import de.lmu.ifi.dbs.elki.index.RangeIndex; -import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; -import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTreeUnified; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.MkTreeSettings; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MTreeQueryUtil; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MkTreeRKNNQuery; import de.lmu.ifi.dbs.elki.persistent.PageFile; -import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages; /** * MkTabTree used as database index. @@ -59,12 +56,7 @@ import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages; * @param <O> Object type * @param <D> Distance type */ -public class MkTabTreeIndex<O, D extends Distance<D>> extends MkTabTree<O, D> implements RangeIndex<O>, KNNIndex<O>, RKNNIndex<O> { - /** - * The knn query we use internally. - */ - private final KNNQuery<O, D> knnQuery; - +public class MkTabTreeIndex<O, D extends NumberDistance<D, ?>> extends MkTabTree<O, D> implements RangeIndex<O>, KNNIndex<O>, RKNNIndex<O> { /** * The relation indexed. */ @@ -75,15 +67,11 @@ public class MkTabTreeIndex<O, D extends Distance<D>> extends MkTabTree<O, D> im * * @param relation Relation indexed * @param pagefile Page file - * @param distanceQuery Distance query - * @param distanceFunction Distance function - * @param k_max Maximum value for k + * @param settings Tree settings */ - public MkTabTreeIndex(Relation<O> relation, PageFile<MkTabTreeNode<O, D>> pagefile, DistanceQuery<O, D> distanceQuery, DistanceFunction<O, D> distanceFunction, int k_max) { - super(pagefile, distanceQuery, distanceFunction, k_max); + public MkTabTreeIndex(Relation<O> relation, PageFile<MkTabTreeNode<O, D>> pagefile, MkTreeSettings<O, D, MkTabTreeNode<O, D>, MkTabEntry> settings) { + super(relation, pagefile, settings); this.relation = relation; - this.knnQuery = this.getKNNQuery(getDistanceQuery()); - this.initialize(); } /** @@ -94,8 +82,8 @@ public class MkTabTreeIndex<O, D extends Distance<D>> extends MkTabTree<O, D> im * @param parentDistance the distance from the object to the routing object of * the parent node */ - protected MkTabEntry<D> createNewLeafEntry(DBID id, O object, D parentDistance) { - return new MkTabLeafEntry<D>(id, parentDistance, knnDistances(object)); + protected MkTabEntry createNewLeafEntry(DBID id, O object, double parentDistance) { + return new MkTabLeafEntry(id, parentDistance, knnDistances(object)); } /** @@ -104,120 +92,94 @@ public class MkTabTreeIndex<O, D extends Distance<D>> extends MkTabTree<O, D> im * @param object the query object * @return the knn distance of the object with the specified id */ - private List<D> knnDistances(O object) { - KNNResult<D> knns = knnQuery.getKNNForObject(object, getKmax() - 1); - return KNNUtil.asDistanceList(knns); - } - - @Override - public void insert(DBIDRef id) { - throw new UnsupportedOperationException("Insertion of single objects is not supported!"); + private double[] knnDistances(O object) { + KNNList<D> knns = knnq.getKNNForObject(object, getKmax() - 1); + double[] distances = new double[getKmax()]; + int i = 0; + for (DistanceDBIDListIter<D> iter = knns.iter(); iter.valid() && i < getKmax(); iter.advance(), i++) { + distances[i] = iter.getDistance().doubleValue(); + } + return distances; } @Override - public void insertAll(DBIDs ids) { - List<MkTabEntry<D>> objs = new ArrayList<MkTabEntry<D>>(ids.size()); - for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) { - DBID id = DBIDUtil.deref(iter); + public void initialize() { + super.initialize(); + List<MkTabEntry> objs = new ArrayList<>(relation.size()); + for (DBIDIter iter = relation.iterDBIDs(); iter.valid(); iter.advance()) { + DBID id = DBIDUtil.deref(iter); // FIXME: expensive final O object = relation.get(id); - objs.add(createNewLeafEntry(id, object, getDistanceFactory().undefinedDistance())); + objs.add(createNewLeafEntry(id, object, Double.NaN)); } insertAll(objs); } - /** - * Throws an UnsupportedOperationException since deletion of objects is not - * yet supported by an M-Tree. - * - * @throws UnsupportedOperationException thrown, since deletions aren't - * implemented yet. - */ - @Override - public final boolean delete(DBIDRef id) { - throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET); - } - - /** - * Throws an UnsupportedOperationException since deletion of objects is not - * yet supported by an M-Tree. - * - * @throws UnsupportedOperationException thrown, since deletions aren't - * implemented yet. - */ - @Override - public void deleteAll(DBIDs ids) { - throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET); - } - @SuppressWarnings("unchecked") @Override public <S extends Distance<S>> KNNQuery<O, S> getKNNQuery(DistanceQuery<O, S> distanceQuery, Object... hints) { // Query on the relation we index - if(distanceQuery.getRelation() != relation) { + if (distanceQuery.getRelation() != relation) { return null; } - DistanceFunction<? super O, S> distanceFunction = distanceQuery.getDistanceFunction(); - if(!this.distanceFunction.equals(distanceFunction)) { - if(getLogger().isDebugging()) { + DistanceFunction<? super O, D> distanceFunction = (DistanceFunction<? super O, D>) distanceQuery.getDistanceFunction(); + if (!this.getDistanceFunction().equals(distanceFunction)) { + if (getLogger().isDebugging()) { getLogger().debug("Distance function not supported by index - or 'equals' not implemented right!"); } return null; } // Bulk is not yet supported - for(Object hint : hints) { - if(hint == DatabaseQuery.HINT_BULK) { + for (Object hint : hints) { + if (hint == DatabaseQuery.HINT_BULK) { return null; } } - AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this; - DistanceQuery<O, S> dq = distanceFunction.instantiate(relation); - return MTreeQueryUtil.getKNNQuery(idx, dq, hints); + DistanceQuery<O, D> dq = distanceFunction.instantiate(relation); + return (KNNQuery<O, S>) MTreeQueryUtil.getKNNQuery(this, dq, hints); } @SuppressWarnings("unchecked") @Override public <S extends Distance<S>> RangeQuery<O, S> getRangeQuery(DistanceQuery<O, S> distanceQuery, Object... hints) { // Query on the relation we index - if(distanceQuery.getRelation() != relation) { + if (distanceQuery.getRelation() != relation) { return null; } - DistanceFunction<? super O, S> distanceFunction = distanceQuery.getDistanceFunction(); - if(!this.distanceFunction.equals(distanceFunction)) { - if(getLogger().isDebugging()) { + DistanceFunction<? super O, D> distanceFunction = (DistanceFunction<? super O, D>) distanceQuery.getDistanceFunction(); + if (!this.getDistanceFunction().equals(distanceFunction)) { + if (getLogger().isDebugging()) { getLogger().debug("Distance function not supported by index - or 'equals' not implemented right!"); } return null; } // Bulk is not yet supported - for(Object hint : hints) { - if(hint == DatabaseQuery.HINT_BULK) { + for (Object hint : hints) { + if (hint == DatabaseQuery.HINT_BULK) { return null; } } - AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this; - DistanceQuery<O, S> dq = distanceFunction.instantiate(relation); - return MTreeQueryUtil.getRangeQuery(idx, dq); + DistanceQuery<O, D> dq = distanceFunction.instantiate(relation); + return (RangeQuery<O, S>) MTreeQueryUtil.getRangeQuery(this, dq); } @SuppressWarnings("unchecked") @Override public <S extends Distance<S>> RKNNQuery<O, S> getRKNNQuery(DistanceQuery<O, S> distanceQuery, Object... hints) { - DistanceFunction<? super O, S> distanceFunction = distanceQuery.getDistanceFunction(); - if(!this.getDistanceFunction().equals(distanceFunction)) { - if(getLogger().isDebugging()) { + DistanceFunction<? super O, D> distanceFunction = (DistanceFunction<? super O, D>) distanceQuery.getDistanceFunction(); + if (!this.getDistanceFunction().equals(distanceFunction)) { + if (getLogger().isDebugging()) { getLogger().debug("Distance function not supported by index - or 'equals' not implemented right!"); } return null; } // Bulk is not yet supported - for(Object hint : hints) { - if(hint == DatabaseQuery.HINT_BULK) { + for (Object hint : hints) { + if (hint == DatabaseQuery.HINT_BULK) { return null; } } - AbstractMkTreeUnified<O, S, ?, ?> idx = (AbstractMkTreeUnified<O, S, ?, ?>) this; - DistanceQuery<O, S> dq = distanceFunction.instantiate(relation); - return new MkTreeRKNNQuery<O, S>(idx, dq); + DistanceQuery<O, D> dq = distanceFunction.instantiate(relation); + return (RKNNQuery<O, S>) new MkTreeRKNNQuery<>(this, dq); } @Override @@ -229,4 +191,4 @@ public class MkTabTreeIndex<O, D extends Distance<D>> extends MkTabTree<O, D> im public String getShortName() { return "mktabtree"; } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeNode.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeNode.java index b09ac314..1942a78b 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeNode.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/MkTabTreeNode.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mktab; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -23,13 +23,8 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.mktab; along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import java.util.ArrayList; -import java.util.List; - import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; -import de.lmu.ifi.dbs.elki.distance.DistanceUtil; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; @@ -43,8 +38,8 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; * @param <O> object type * @param <D> distance type */ -class MkTabTreeNode<O, D extends Distance<D>> extends AbstractMTreeNode<O, D, MkTabTreeNode<O, D>, MkTabEntry<D>> { - private static final long serialVersionUID = 1; +class MkTabTreeNode<O, D extends NumberDistance<D, ?>> extends AbstractMTreeNode<O, D, MkTabTreeNode<O, D>, MkTabEntry> { + private static final long serialVersionUID = 2; /** * Empty constructor for Externalizable interface. @@ -68,22 +63,17 @@ class MkTabTreeNode<O, D extends Distance<D>> extends AbstractMTreeNode<O, D, Mk * Determines and returns the knn distance of this node as the maximum knn * distance of all entries. * - * @param distanceFunction the distance function * @return the knn distance of this node */ - protected List<D> kNNDistances(DistanceQuery<O, D> distanceFunction) { - int k = getEntry(0).getK_max(); + protected double[] kNNDistances() { + int k = getEntry(0).getKnnDistances().length; - List<D> result = new ArrayList<D>(); - for(int i = 0; i < k; i++) { - result.add(distanceFunction.nullDistance()); - } + double[] result = new double[k]; - for(int i = 0; i < getNumEntries(); i++) { - for(int j = 0; j < k; j++) { - MkTabEntry<D> entry = getEntry(i); - D kDist = result.remove(j); - result.add(j, DistanceUtil.max(kDist, entry.getKnnDistance(j + 1))); + for (int i = 0; i < getNumEntries(); i++) { + for (int j = 0; j < k; j++) { + MkTabEntry entry = getEntry(i); + result[j] = Math.max(result[j], entry.getKnnDistance(j + 1)); } } @@ -91,10 +81,10 @@ class MkTabTreeNode<O, D extends Distance<D>> extends AbstractMTreeNode<O, D, Mk } @Override - public void adjustEntry(MkTabEntry<D> entry, DBID routingObjectID, D parentDistance, AbstractMTree<O, D, MkTabTreeNode<O, D>, MkTabEntry<D>> mTree) { + public void adjustEntry(MkTabEntry entry, DBID routingObjectID, double parentDistance, AbstractMTree<O, D, MkTabTreeNode<O, D>, MkTabEntry, ?> mTree) { super.adjustEntry(entry, routingObjectID, parentDistance, mTree); // adjust knn distances - entry.setKnnDistances(kNNDistances(mTree.getDistanceQuery())); + entry.setKnnDistances(kNNDistances()); } /** @@ -106,12 +96,12 @@ class MkTabTreeNode<O, D extends Distance<D>> extends AbstractMTreeNode<O, D, Mk * @param mTree the underlying M-Tree */ @Override - protected void integrityCheckParameters(MkTabEntry<D> parentEntry, MkTabTreeNode<O, D> parent, int index, AbstractMTree<O, D, MkTabTreeNode<O, D>, MkTabEntry<D>> mTree) { + protected void integrityCheckParameters(MkTabEntry parentEntry, MkTabTreeNode<O, D> parent, int index, AbstractMTree<O, D, MkTabTreeNode<O, D>, MkTabEntry, ?> mTree) { super.integrityCheckParameters(parentEntry, parent, index, mTree); // test knn distances - MkTabEntry<D> entry = parent.getEntry(index); - List<D> knnDistances = kNNDistances(mTree.getDistanceQuery()); - if(!entry.getKnnDistances().equals(knnDistances)) { + MkTabEntry entry = parent.getEntry(index); + double[] knnDistances = kNNDistances(); + if (!entry.getKnnDistances().equals(knnDistances)) { String soll = knnDistances.toString(); String ist = entry.getKnnDistances().toString(); throw new RuntimeException("Wrong knnDistances in node " + parent.getPageID() + " at index " + index + " (child " + entry + ")" + "\nsoll: " + soll + ",\n ist: " + ist); diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/package-info.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/package-info.java index 353acaa5..ed3e24d3 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/package-info.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/mktab/package-info.java @@ -5,7 +5,7 @@ This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures -Copyright (C) 2012 +Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/package-info.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/package-info.java index 201d57c3..13b23e16 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/package-info.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mktrees/package-info.java @@ -7,7 +7,7 @@ This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures -Copyright (C) 2012 +Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTree.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTree.java index 4276329c..1b3d1481 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTree.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTree.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mtree; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -24,12 +24,11 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mtree; */ import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; -import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeDirectoryEntry; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeSettings; import de.lmu.ifi.dbs.elki.logging.Logging; import de.lmu.ifi.dbs.elki.persistent.PageFile; import de.lmu.ifi.dbs.elki.utilities.documentation.Description; @@ -41,6 +40,14 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Title; * Apart from organizing the objects it also provides several methods to search * for certain object in the structure. Persistence is not yet ensured. * + * Reference: + * <p> + * P. Ciaccia, M. Patella, P. Zezula<br /> + * M-tree: An Efficient Access Method for Similarity Search in Metric Spaces<br /> + * In Proceedings of 23rd International Conference on Very Large Data Bases + * (VLDB'97), August 25-29, 1997, Athens, Greece + * </p> + * * @author Elke Achtert * * @apiviz.has MTreeNode oneway - - contains @@ -51,7 +58,7 @@ import de.lmu.ifi.dbs.elki.utilities.documentation.Title; @Title("M-Tree") @Description("Efficient Access Method for Similarity Search in Metric Spaces") @Reference(authors = "P. Ciaccia, M. Patella, P. Zezula", title = "M-tree: An Efficient Access Method for Similarity Search in Metric Spaces", booktitle = "VLDB'97, Proceedings of 23rd International Conference on Very Large Data Bases, August 25-29, 1997, Athens, Greece", url = "http://www.vldb.org/conf/1997/P426.PDF") -public class MTree<O, D extends Distance<D>> extends AbstractMTree<O, D, MTreeNode<O, D>, MTreeEntry<D>> { +abstract public class MTree<O, D extends NumberDistance<D, ?>> extends AbstractMTree<O, D, MTreeNode<O, D>, MTreeEntry, MTreeSettings<O, D, MTreeNode<O, D>, MTreeEntry>> { /** * The logger for this class. */ @@ -61,77 +68,26 @@ public class MTree<O, D extends Distance<D>> extends AbstractMTree<O, D, MTreeNo * Constructor. * * @param pagefile Page file - * @param distanceQuery Distance query - * @param distanceFunction Distance function + * @param settings Tree settings */ - public MTree(PageFile<MTreeNode<O, D>> pagefile, DistanceQuery<O, D> distanceQuery, DistanceFunction<O, D> distanceFunction) { - super(pagefile, distanceQuery, distanceFunction); + public MTree(PageFile<MTreeNode<O, D>> pagefile, MTreeSettings<O, D, MTreeNode<O, D>, MTreeEntry> settings) { + super(pagefile, settings); } /** * Does nothing because no operations are necessary before inserting an entry. */ @Override - protected void preInsert(MTreeEntry<D> entry) { + protected void preInsert(MTreeEntry entry) { // do nothing } - @Override - protected void initializeCapacities(MTreeEntry<D> exampleLeaf) { - int distanceSize = exampleLeaf.getParentDistance().externalizableSize(); - - // FIXME: simulate a proper feature size! - int featuresize = 0; // RelationUtil.dimensionality(relation); - - // overhead = index(4), numEntries(4), id(4), isLeaf(0.125) - double overhead = 12.125; - if(getPageSize() - overhead < 0) { - throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); - } - - // dirCapacity = (pageSize - overhead) / (nodeID + objectID + - // coveringRadius + parentDistance) + 1 - // dirCapacity = (int) (pageSize - overhead) / (4 + 4 + distanceSize + - // distanceSize) + 1; - - // dirCapacity = (pageSize - overhead) / (nodeID + **object feature size** + - // coveringRadius + parentDistance) + 1 - dirCapacity = (int) (getPageSize() - overhead) / (4 + featuresize + distanceSize + distanceSize) + 1; - - if(dirCapacity <= 2) { - throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); - } - - if(dirCapacity < 10) { - LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a directory node = " + (dirCapacity - 1)); - } - // leafCapacity = (pageSize - overhead) / (objectID + parentDistance) + - // 1 - // leafCapacity = (int) (pageSize - overhead) / (4 + distanceSize) + 1; - // leafCapacity = (pageSize - overhead) / (objectID + ** object size ** + - // parentDistance) + - // 1 - leafCapacity = (int) (getPageSize() - overhead) / (4 + featuresize + distanceSize) + 1; - - if(leafCapacity <= 1) { - throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); - } - - if(leafCapacity < 10) { - LOG.warning("Page size is choosen too small! Maximum number of entries " + "in a leaf node = " + (leafCapacity - 1)); - } - - if(LOG.isVerbose()) { - LOG.verbose("Directory Capacity: " + (dirCapacity - 1) + "\nLeaf Capacity: " + (leafCapacity - 1)); - } - } - /** * @return a new MTreeDirectoryEntry representing the specified node */ @Override - protected MTreeEntry<D> createNewDirectoryEntry(MTreeNode<O, D> node, DBID routingObjectID, D parentDistance) { - return new MTreeDirectoryEntry<D>(routingObjectID, parentDistance, node.getPageID(), node.coveringRadius(routingObjectID, this)); + protected MTreeEntry createNewDirectoryEntry(MTreeNode<O, D> node, DBID routingObjectID, double parentDistance) { + return new MTreeDirectoryEntry(routingObjectID, parentDistance, node.getPageID(), node.coveringRadius(routingObjectID, this)); } /** @@ -139,8 +95,8 @@ public class MTree<O, D extends Distance<D>> extends AbstractMTree<O, D, MTreeNo * <code>new MTreeDirectoryEntry<D>(null, null, 0, null)</code> */ @Override - protected MTreeEntry<D> createRootEntry() { - return new MTreeDirectoryEntry<D>(null, null, 0, null); + protected MTreeEntry createRootEntry() { + return new MTreeDirectoryEntry(null, 0., 0, 0.); } /** @@ -148,7 +104,7 @@ public class MTree<O, D extends Distance<D>> extends AbstractMTree<O, D, MTreeNo */ @Override protected MTreeNode<O, D> createNewLeafNode() { - return new MTreeNode<O, D>(leafCapacity, true); + return new MTreeNode<>(leafCapacity, true); } /** @@ -156,11 +112,11 @@ public class MTree<O, D extends Distance<D>> extends AbstractMTree<O, D, MTreeNo */ @Override protected MTreeNode<O, D> createNewDirectoryNode() { - return new MTreeNode<O, D>(dirCapacity, false); + return new MTreeNode<>(dirCapacity, false); } @Override protected Logging getLogger() { return LOG; } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeFactory.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeFactory.java index 5c5aac04..dbc27511 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeFactory.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeFactory.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mtree; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -24,11 +24,13 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mtree; */ 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.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeFactory; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeSettings; import de.lmu.ifi.dbs.elki.persistent.PageFile; +import de.lmu.ifi.dbs.elki.persistent.PageFileFactory; +import de.lmu.ifi.dbs.elki.utilities.Alias; import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil; /** @@ -42,23 +44,22 @@ import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil; * @param <O> Object type * @param <D> Distance type */ -public class MTreeFactory<O, D extends Distance<D>> extends AbstractMTreeFactory<O, D, MTreeNode<O, D>, MTreeEntry<D>, MTreeIndex<O, D>> { +@Alias({ "mtree", "m" }) +public class MTreeFactory<O, D extends NumberDistance<D, ?>> extends AbstractMTreeFactory<O, D, MTreeNode<O, D>, MTreeEntry, MTreeIndex<O, D>, MTreeSettings<O, D, MTreeNode<O, D>, MTreeEntry>> { /** * Constructor. * - * @param fileName file name - * @param pageSize page size - * @param cacheSize cache size - * @param distanceFunction Distance function + * @param pageFileFactory Data storage + * @param settings Tree settings */ - public MTreeFactory(String fileName, int pageSize, long cacheSize, DistanceFunction<O, D> distanceFunction) { - super(fileName, pageSize, cacheSize, distanceFunction); + public MTreeFactory(PageFileFactory<?> pageFileFactory, MTreeSettings<O, D, MTreeNode<O, D>, MTreeEntry> settings) { + super(pageFileFactory, settings); } @Override public MTreeIndex<O, D> instantiate(Relation<O> relation) { PageFile<MTreeNode<O, D>> pagefile = makePageFile(getNodeClass()); - return new MTreeIndex<O, D>(relation, pagefile, distanceFunction.instantiate(relation), distanceFunction); + return new MTreeIndex<>(relation, pagefile, settings); } protected Class<MTreeNode<O, D>> getNodeClass() { @@ -72,10 +73,15 @@ public class MTreeFactory<O, D extends Distance<D>> extends AbstractMTreeFactory * * @apiviz.exclude */ - public static class Parameterizer<O, D extends Distance<D>> extends AbstractMTreeFactory.Parameterizer<O, D> { + public static class Parameterizer<O, D extends NumberDistance<D, ?>> extends AbstractMTreeFactory.Parameterizer<O, D, MTreeNode<O, D>, MTreeEntry, MTreeSettings<O, D, MTreeNode<O, D>, MTreeEntry>> { @Override protected MTreeFactory<O, D> makeInstance() { - return new MTreeFactory<O, D>(fileName, pageSize, cacheSize, distanceFunction); + return new MTreeFactory<>(pageFileFactory, settings); + } + + @Override + protected MTreeSettings<O, D, MTreeNode<O, D>, MTreeEntry> makeSettings() { + return new MTreeSettings<>(); } } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeIndex.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeIndex.java index 8afe1f2a..32908a1e 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeIndex.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeIndex.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mtree; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -26,6 +26,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mtree; import java.util.ArrayList; import java.util.List; +import de.lmu.ifi.dbs.elki.data.FeatureVector; import de.lmu.ifi.dbs.elki.database.ids.DBID; import de.lmu.ifi.dbs.elki.database.ids.DBIDIter; import de.lmu.ifi.dbs.elki.database.ids.DBIDRef; @@ -36,64 +37,137 @@ import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery; import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery; import de.lmu.ifi.dbs.elki.database.relation.Relation; +import de.lmu.ifi.dbs.elki.database.relation.RelationUtil; import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction; import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; +import de.lmu.ifi.dbs.elki.index.DynamicIndex; import de.lmu.ifi.dbs.elki.index.KNNIndex; import de.lmu.ifi.dbs.elki.index.RangeIndex; -import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeLeafEntry; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeSettings; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query.MTreeQueryUtil; +import de.lmu.ifi.dbs.elki.persistent.ByteArrayUtil; import de.lmu.ifi.dbs.elki.persistent.PageFile; import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages; +import de.lmu.ifi.dbs.elki.utilities.exceptions.NotImplementedException; /** * Class for using an m-tree as database index. * * @author Erich Schubert - * + * * @param <O> Object type * @param <D> Distance type */ -public class MTreeIndex<O, D extends Distance<D>> extends MTree<O, D> implements RangeIndex<O>, KNNIndex<O> { +public class MTreeIndex<O, D extends NumberDistance<D, ?>> extends MTree<O, D> implements RangeIndex<O>, KNNIndex<O>, DynamicIndex { /** * The relation indexed. */ private Relation<O> relation; /** + * The distance query. + */ + protected DistanceQuery<O, D> distanceQuery; + + /** * Constructor. - * + * * @param relation Relation indexed * @param pagefile Page file - * @param distanceQuery Distance query - * @param distanceFunction Distance function + * @param settings Tree settings */ - public MTreeIndex(Relation<O> relation, PageFile<MTreeNode<O, D>> pagefile, DistanceQuery<O, D> distanceQuery, DistanceFunction<O, D> distanceFunction) { - super(pagefile, distanceQuery, distanceFunction); + public MTreeIndex(Relation<O> relation, PageFile<MTreeNode<O, D>> pagefile, MTreeSettings<O, D, MTreeNode<O, D>, MTreeEntry> settings) { + super(pagefile, settings); this.relation = relation; - this.initialize(); + this.distanceQuery = getDistanceFunction().instantiate(relation); + } + + @Override + public D distance(DBIDRef id1, DBIDRef id2) { + if (id1 == null || id2 == null) { + return getDistanceFactory().undefinedDistance(); + } + statistics.countDistanceCalculation(); + return distanceQuery.distance(id1, id2); + } + + @Override + protected void initializeCapacities(MTreeEntry exampleLeaf) { + int distanceSize = ByteArrayUtil.SIZE_DOUBLE; // exampleLeaf.getParentDistance().externalizableSize(); + + // FIXME: simulate a proper feature size! + @SuppressWarnings("unchecked") + int featuresize = 8 * RelationUtil.dimensionality((Relation<? extends FeatureVector<?>>) relation); + if (featuresize <= 0) { + getLogger().warning("Relation does not have a dimensionality -- simulating M-tree as external index!"); + featuresize = 0; + } + + // overhead = index(4), numEntries(4), id(4), isLeaf(0.125) + double overhead = 12.125; + if (getPageSize() - overhead < 0) { + throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); + } + + // dirCapacity = (pageSize - overhead) / (nodeID + objectID + coveringRadius + // + parentDistance) + 1 + // dirCapacity = (int) (pageSize - overhead) / (4 + 4 + distanceSize + + // distanceSize) + 1; + + // dirCapacity = (pageSize - overhead) / (nodeID + **object feature size** + + // coveringRadius + parentDistance) + 1 + dirCapacity = (int) (getPageSize() - overhead) / (4 + featuresize + distanceSize + distanceSize) + 1; + + if (dirCapacity <= 2) { + throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); + } + + if (dirCapacity < 10) { + getLogger().warning("Page size is choosen too small! Maximum number of entries " + "in a directory node = " + (dirCapacity - 1)); + } + // leafCapacity = (pageSize - overhead) / (objectID + parentDistance) + 1 + // leafCapacity = (int) (pageSize - overhead) / (4 + distanceSize) + 1; + // leafCapacity = (pageSize - overhead) / (objectID + ** object size ** + + // parentDistance) + 1 + leafCapacity = (int) (getPageSize() - overhead) / (4 + featuresize + distanceSize) + 1; + + if (leafCapacity <= 1) { + throw new RuntimeException("Node size of " + getPageSize() + " Bytes is chosen too small!"); + } + + if (leafCapacity < 10) { + getLogger().warning("Page size is choosen too small! Maximum number of entries " + "in a leaf node = " + (leafCapacity - 1)); + } } /** * @return a new MTreeLeafEntry representing the specified data object */ - protected MTreeEntry<D> createNewLeafEntry(DBID id, O object, D parentDistance) { - return new MTreeLeafEntry<D>(id, parentDistance); + protected MTreeEntry createNewLeafEntry(DBID id, O object, double parentDistance) { + return new MTreeLeafEntry(id, parentDistance); + } + + @Override + public void initialize() { + super.initialize(); + insertAll(relation.getDBIDs()); } @Override public void insert(DBIDRef id) { - insert(createNewLeafEntry(DBIDUtil.deref(id), relation.get(id), getDistanceFactory().undefinedDistance()), false); + insert(createNewLeafEntry(DBIDUtil.deref(id), relation.get(id), Double.NaN), false); } @Override public void insertAll(DBIDs ids) { - List<MTreeEntry<D>> objs = new ArrayList<MTreeEntry<D>>(ids.size()); + List<MTreeEntry> objs = new ArrayList<>(ids.size()); for (DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) { DBID id = DBIDUtil.deref(iter); final O object = relation.get(id); - objs.add(createNewLeafEntry(id, object, getDistanceFactory().undefinedDistance())); + objs.add(createNewLeafEntry(id, object, Double.NaN)); } insertAll(objs); } @@ -107,7 +181,7 @@ public class MTreeIndex<O, D extends Distance<D>> extends MTree<O, D> implements */ @Override public final boolean delete(DBIDRef id) { - throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET); + throw new NotImplementedException(ExceptionMessages.UNSUPPORTED_NOT_YET); } /** @@ -119,57 +193,55 @@ public class MTreeIndex<O, D extends Distance<D>> extends MTree<O, D> implements */ @Override public void deleteAll(DBIDs ids) { - throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET); + throw new NotImplementedException(ExceptionMessages.UNSUPPORTED_NOT_YET); } @SuppressWarnings("unchecked") @Override public <S extends Distance<S>> KNNQuery<O, S> getKNNQuery(DistanceQuery<O, S> distanceQuery, Object... hints) { // Query on the relation we index - if(distanceQuery.getRelation() != relation) { + if (distanceQuery.getRelation() != relation) { return null; } - DistanceFunction<? super O, S> distanceFunction = distanceQuery.getDistanceFunction(); - if(!this.distanceFunction.equals(distanceFunction)) { - if(getLogger().isDebugging()) { + DistanceFunction<? super O, D> distanceFunction = (DistanceFunction<? super O, D>) distanceQuery.getDistanceFunction(); + if (!this.getDistanceFunction().equals(distanceFunction)) { + if (getLogger().isDebugging()) { getLogger().debug("Distance function not supported by index - or 'equals' not implemented right!"); } return null; } // Bulk is not yet supported - for(Object hint : hints) { - if(hint == DatabaseQuery.HINT_BULK) { + for (Object hint : hints) { + if (hint == DatabaseQuery.HINT_BULK) { return null; } } - AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this; - DistanceQuery<O, S> dq = distanceFunction.instantiate(relation); - return MTreeQueryUtil.getKNNQuery(idx, dq, hints); + DistanceQuery<O, D> dq = distanceFunction.instantiate(relation); + return (KNNQuery<O, S>) MTreeQueryUtil.getKNNQuery(this, dq, hints); } @SuppressWarnings("unchecked") @Override public <S extends Distance<S>> RangeQuery<O, S> getRangeQuery(DistanceQuery<O, S> distanceQuery, Object... hints) { // Query on the relation we index - if(distanceQuery.getRelation() != relation) { + if (distanceQuery.getRelation() != relation) { return null; } - DistanceFunction<? super O, S> distanceFunction = distanceQuery.getDistanceFunction(); - if(!this.distanceFunction.equals(distanceFunction)) { - if(getLogger().isDebugging()) { + DistanceFunction<? super O, D> distanceFunction = (DistanceFunction<? super O, D>) distanceQuery.getDistanceFunction(); + if (!this.getDistanceFunction().equals(distanceFunction)) { + if (getLogger().isDebugging()) { getLogger().debug("Distance function not supported by index - or 'equals' not implemented right!"); } return null; } // Bulk is not yet supported - for(Object hint : hints) { - if(hint == DatabaseQuery.HINT_BULK) { + for (Object hint : hints) { + if (hint == DatabaseQuery.HINT_BULK) { return null; } } - AbstractMTree<O, S, ?, ?> idx = (AbstractMTree<O, S, ?, ?>) this; - DistanceQuery<O, S> dq = distanceFunction.instantiate(relation); - return MTreeQueryUtil.getRangeQuery(idx, dq); + DistanceQuery<O, D> dq = distanceFunction.instantiate(relation); + return (RangeQuery<O, S>) MTreeQueryUtil.getRangeQuery(this, dq); } @Override @@ -181,4 +253,4 @@ public class MTreeIndex<O, D extends Distance<D>> extends MTree<O, D> implements public String getShortName() { return "mtree"; } -}
\ No newline at end of file +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeNode.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeNode.java index 017e7d6c..b3aa77fb 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeNode.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/MTreeNode.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mtree; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -23,7 +23,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mtree; along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; @@ -34,7 +34,7 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; * @param <O> Object type * @param <D> Distance type */ -public class MTreeNode<O, D extends Distance<D>> extends AbstractMTreeNode<O, D, MTreeNode<O, D>, MTreeEntry<D>> { +public class MTreeNode<O, D extends NumberDistance<D, ?>> extends AbstractMTreeNode<O, D, MTreeNode<O, D>, MTreeEntry> { /** * Serial version */ diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/package-info.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/package-info.java index a34162a8..c10d683b 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/package-info.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/mtree/package-info.java @@ -5,7 +5,7 @@ This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures -Copyright (C) 2012 +Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/package-info.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/package-info.java index b30d1193..03a4a4d6 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/package-info.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/package-info.java @@ -5,7 +5,7 @@ This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures -Copyright (C) 2012 +Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/DoubleDistanceMetricalIndexKNNQuery.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/DoubleDistanceMetricalIndexKNNQuery.java index 52f777ba..9eb72178 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/DoubleDistanceMetricalIndexKNNQuery.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/DoubleDistanceMetricalIndexKNNQuery.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -23,24 +23,20 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query; along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import java.util.List; - -import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs; import de.lmu.ifi.dbs.elki.database.ids.DBID; -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.distance.DoubleDistanceKNNHeap; +import de.lmu.ifi.dbs.elki.database.ids.distance.KNNList; import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; import de.lmu.ifi.dbs.elki.database.query.knn.AbstractDistanceKNNQuery; import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDoubleDistanceFunction; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceKNNHeap; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult; import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance; import de.lmu.ifi.dbs.elki.index.tree.DirectoryEntry; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; import de.lmu.ifi.dbs.elki.index.tree.query.DoubleMTreeDistanceSearchCandidate; -import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap; -import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages; +import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.ComparableMinHeap; /** * Instance of a KNN query for a particular spatial index. @@ -55,7 +51,7 @@ public class DoubleDistanceMetricalIndexKNNQuery<O> extends AbstractDistanceKNNQ /** * The index to use */ - protected final AbstractMTree<O, DoubleDistance, ?, ?> index; + protected final AbstractMTree<O, DoubleDistance, ?, ?, ?> index; /** * Distance function @@ -69,22 +65,23 @@ public class DoubleDistanceMetricalIndexKNNQuery<O> extends AbstractDistanceKNNQ * @param distanceQuery Distance query used * @param distf Distance function */ - public DoubleDistanceMetricalIndexKNNQuery(AbstractMTree<O, DoubleDistance, ?, ?> index, DistanceQuery<O, DoubleDistance> distanceQuery, PrimitiveDoubleDistanceFunction<? super O> distf) { + public DoubleDistanceMetricalIndexKNNQuery(AbstractMTree<O, DoubleDistance, ?, ?, ?> index, DistanceQuery<O, DoubleDistance> distanceQuery, PrimitiveDoubleDistanceFunction<? super O> distf) { super(distanceQuery); this.index = index; this.distf = distf; } @Override - public KNNResult<DoubleDistance> getKNNForObject(O q, int k) { + public KNNList<DoubleDistance> getKNNForObject(O q, int k) { if (k < 1) { throw new IllegalArgumentException("At least one object has to be requested!"); } + index.statistics.countKNNQuery(); - DoubleDistanceKNNHeap knnList = new DoubleDistanceKNNHeap(k); + DoubleDistanceKNNHeap knnList = (DoubleDistanceKNNHeap) DBIDUtil.newHeap(DoubleDistance.FACTORY, k); double d_k = Double.POSITIVE_INFINITY; - final Heap<DoubleMTreeDistanceSearchCandidate> pq = new Heap<DoubleMTreeDistanceSearchCandidate>(); + final ComparableMinHeap<DoubleMTreeDistanceSearchCandidate> pq = new ComparableMinHeap<>(); // Push the root node pq.add(new DoubleMTreeDistanceSearchCandidate(0, index.getRootID(), null, 0)); @@ -104,15 +101,16 @@ public class DoubleDistanceMetricalIndexKNNQuery<O> extends AbstractDistanceKNNQ // directory node if (!node.isLeaf()) { for (int i = 0; i < node.getNumEntries(); i++) { - final MTreeEntry<DoubleDistance> entry = node.getEntry(i); + final MTreeEntry entry = node.getEntry(i); final DBID id_i = entry.getRoutingObjectID(); - double or_i = entry.getCoveringRadius().doubleValue(); - double d2 = id_p != null ? entry.getParentDistance().doubleValue() : 0; + double or_i = entry.getCoveringRadius(); + double d2 = id_p != null ? entry.getParentDistance() : 0; double diff = Math.abs(d1 - d2); if (diff <= d_k + or_i) { final O ob_i = relation.get(id_i); double d3 = distf.doubleDistance(ob_i, q); + index.statistics.countDistanceCalculation(); double d_min = Math.max(d3 - or_i, 0); if (d_min <= d_k) { pq.add(new DoubleMTreeDistanceSearchCandidate(d_min, ((DirectoryEntry) entry).getPageID(), id_i, d3)); @@ -123,14 +121,15 @@ public class DoubleDistanceMetricalIndexKNNQuery<O> extends AbstractDistanceKNNQ // data node else { for (int i = 0; i < node.getNumEntries(); i++) { - final MTreeEntry<DoubleDistance> entry = node.getEntry(i); + final MTreeEntry entry = node.getEntry(i); final DBID id_i = entry.getRoutingObjectID(); - double d2 = id_p != null ? entry.getParentDistance().doubleValue() : 0; + double d2 = id_p != null ? entry.getParentDistance() : 0; double diff = Math.abs(d1 - d2); if (diff <= d_k) { final O o_i = relation.get(id_i); double d3 = distf.doubleDistance(o_i, q); + index.statistics.countDistanceCalculation(); if (d3 <= d_k) { knnList.add(d3, id_i); d_k = knnList.doubleKNNDistance(); @@ -141,15 +140,4 @@ public class DoubleDistanceMetricalIndexKNNQuery<O> extends AbstractDistanceKNNQ } return knnList.toKNNList(); } - - @Override - public KNNResult<DoubleDistance> getKNNForDBID(DBIDRef id, int k) { - return getKNNForObject(relation.get(id), k); - } - - @Override - public List<KNNResult<DoubleDistance>> getKNNForBulkDBIDs(ArrayDBIDs ids, int k) { - // TODO: implement - throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET); - } } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/DoubleDistanceMetricalIndexRangeQuery.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/DoubleDistanceMetricalIndexRangeQuery.java index c3680111..714498d4 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/DoubleDistanceMetricalIndexRangeQuery.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/DoubleDistanceMetricalIndexRangeQuery.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -24,12 +24,12 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query; */ import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.database.ids.DBIDRef; +import de.lmu.ifi.dbs.elki.database.ids.distance.DistanceDBIDList; +import de.lmu.ifi.dbs.elki.database.ids.distance.DoubleDistanceDBIDPairList; +import de.lmu.ifi.dbs.elki.database.ids.distance.ModifiableDoubleDistanceDBIDList; import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; import de.lmu.ifi.dbs.elki.database.query.range.AbstractDistanceRangeQuery; import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDoubleDistanceFunction; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DoubleDistanceDBIDList; import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance; import de.lmu.ifi.dbs.elki.index.tree.DirectoryEntry; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; @@ -47,7 +47,7 @@ public class DoubleDistanceMetricalIndexRangeQuery<O> extends AbstractDistanceRa /** * The index to use */ - protected final AbstractMTree<O, DoubleDistance, ?, ?> index; + protected final AbstractMTree<O, DoubleDistance, ?, ?, ?> index; /** * Distance function @@ -61,7 +61,7 @@ public class DoubleDistanceMetricalIndexRangeQuery<O> extends AbstractDistanceRa * @param distanceQuery Distance query used * @param distf Distance function */ - public DoubleDistanceMetricalIndexRangeQuery(AbstractMTree<O, DoubleDistance, ?, ?> index, DistanceQuery<O, DoubleDistance> distanceQuery, PrimitiveDoubleDistanceFunction<? super O> distf) { + public DoubleDistanceMetricalIndexRangeQuery(AbstractMTree<O, DoubleDistance, ?, ?, ?> index, DistanceQuery<O, DoubleDistance> distanceQuery, PrimitiveDoubleDistanceFunction<? super O> distf) { super(distanceQuery); this.index = index; this.distf = distf; @@ -78,15 +78,19 @@ public class DoubleDistanceMetricalIndexRangeQuery<O> extends AbstractDistanceRa * @param r_q the query range * @param result the list holding the query results */ - private void doRangeQuery(DBID id_p, AbstractMTreeNode<O, DoubleDistance, ?, ?> node, O q, double r_q, DoubleDistanceDBIDList result) { + private void doRangeQuery(DBID id_p, AbstractMTreeNode<O, DoubleDistance, ?, ?> node, O q, double r_q, ModifiableDoubleDistanceDBIDList result) { final O o_p = id_p != null ? relation.get(id_p) : null; - double d1 = id_p != null ? distf.doubleDistance(o_p, q) : 0; + double d1 = 0.; + if (id_p != null) { + d1 = distf.doubleDistance(o_p, q); + index.statistics.countDistanceCalculation(); + } if (!node.isLeaf()) { for (int i = 0; i < node.getNumEntries(); i++) { - MTreeEntry<DoubleDistance> entry = node.getEntry(i); + MTreeEntry entry = node.getEntry(i); - double r_or = entry.getCoveringRadius().doubleValue(); - double d2 = id_p != null ? entry.getParentDistance().doubleValue() : 0; + double r_or = entry.getCoveringRadius(); + double d2 = id_p != null ? entry.getParentDistance() : 0; double diff = Math.abs(d1 - d2); double sum = r_q + r_or; @@ -94,6 +98,7 @@ public class DoubleDistanceMetricalIndexRangeQuery<O> extends AbstractDistanceRa if (diff <= sum) { DBID id_r = entry.getRoutingObjectID(); double d3 = distf.doubleDistance(relation.get(id_r), q); + index.statistics.countDistanceCalculation(); if (d3 <= sum) { AbstractMTreeNode<O, DoubleDistance, ?, ?> child = index.getNode(((DirectoryEntry) entry).getPageID()); doRangeQuery(id_r, child, q, r_q, result); @@ -102,15 +107,16 @@ public class DoubleDistanceMetricalIndexRangeQuery<O> extends AbstractDistanceRa } } else { for (int i = 0; i < node.getNumEntries(); i++) { - MTreeEntry<DoubleDistance> entry = node.getEntry(i); + MTreeEntry entry = node.getEntry(i); - double d2 = id_p != null ? entry.getParentDistance().doubleValue() : 0; + double d2 = id_p != null ? entry.getParentDistance() : 0; double diff = Math.abs(d1 - d2); if (diff <= r_q) { DBID id_j = entry.getRoutingObjectID(); O o_j = relation.get(id_j); double d3 = distf.doubleDistance(o_j, q); + index.statistics.countDistanceCalculation(); if (d3 <= r_q) { result.add(d3, id_j); } @@ -120,16 +126,12 @@ public class DoubleDistanceMetricalIndexRangeQuery<O> extends AbstractDistanceRa } @Override - public DistanceDBIDResult<DoubleDistance> getRangeForObject(O obj, DoubleDistance range) { - final DoubleDistanceDBIDList result = new DoubleDistanceDBIDList(); + public DistanceDBIDList<DoubleDistance> getRangeForObject(O obj, DoubleDistance range) { + final DoubleDistanceDBIDPairList result = new DoubleDistanceDBIDPairList(); doRangeQuery(null, index.getRoot(), obj, range.doubleValue(), result); + index.statistics.countRangeQuery(); result.sort(); return result; } - - @Override - public DistanceDBIDResult<DoubleDistance> getRangeForDBID(DBIDRef id, DoubleDistance range) { - return getRangeForObject(relation.get(id), range); - } } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MTreeQueryUtil.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MTreeQueryUtil.java index 34980542..cc27b383 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MTreeQueryUtil.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MTreeQueryUtil.java @@ -5,15 +5,15 @@ import de.lmu.ifi.dbs.elki.database.query.knn.KNNQuery; import de.lmu.ifi.dbs.elki.database.query.range.RangeQuery; import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction; import de.lmu.ifi.dbs.elki.distance.distancefunction.PrimitiveDoubleDistanceFunction; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; /* This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -50,17 +50,17 @@ public final class MTreeQueryUtil { * @return Query object */ @SuppressWarnings({ "cast", "unchecked" }) - public static <O, D extends Distance<D>> KNNQuery<O, D> getKNNQuery(AbstractMTree<O, D, ?, ?> tree, DistanceQuery<O, D> distanceQuery, Object... hints) { + public static <O, D extends NumberDistance<D, ?>> KNNQuery<O, D> getKNNQuery(AbstractMTree<O, D, ?, ?, ?> tree, DistanceQuery<O, D> distanceQuery, Object... hints) { DistanceFunction<? super O, D> df = distanceQuery.getDistanceFunction(); // Can we use an optimized query? - if(df instanceof PrimitiveDoubleDistanceFunction) { + if (df instanceof PrimitiveDoubleDistanceFunction) { PrimitiveDoubleDistanceFunction<? super O> dfc = (PrimitiveDoubleDistanceFunction<? super O>) df; - AbstractMTree<O, DoubleDistance, ?, ?> treec = (AbstractMTree<O, DoubleDistance, ?, ?>) tree; + AbstractMTree<O, DoubleDistance, ?, ?, ?> treec = (AbstractMTree<O, DoubleDistance, ?, ?, ?>) tree; DistanceQuery<O, DoubleDistance> dqc = (DistanceQuery<O, DoubleDistance>) distanceQuery; - KNNQuery<O, ?> q = new DoubleDistanceMetricalIndexKNNQuery<O>(treec, dqc, dfc); + KNNQuery<O, ?> q = new DoubleDistanceMetricalIndexKNNQuery<>(treec, dqc, dfc); return (KNNQuery<O, D>) q; } - return new MetricalIndexKNNQuery<O, D>(tree, distanceQuery); + return new MetricalIndexKNNQuery<>(tree, distanceQuery); } /** @@ -75,16 +75,16 @@ public final class MTreeQueryUtil { * @return Query object */ @SuppressWarnings({ "cast", "unchecked" }) - public static <O, D extends Distance<D>> RangeQuery<O, D> getRangeQuery(AbstractMTree<O, D, ?, ?> tree, DistanceQuery<O, D> distanceQuery, Object... hints) { + public static <O, D extends NumberDistance<D, ?>> RangeQuery<O, D> getRangeQuery(AbstractMTree<O, D, ?, ?, ?> tree, DistanceQuery<O, D> distanceQuery, Object... hints) { DistanceFunction<? super O, D> df = distanceQuery.getDistanceFunction(); // Can we use an optimized query? - if(df instanceof PrimitiveDoubleDistanceFunction) { + if (df instanceof PrimitiveDoubleDistanceFunction) { PrimitiveDoubleDistanceFunction<? super O> dfc = (PrimitiveDoubleDistanceFunction<? super O>) df; - AbstractMTree<O, DoubleDistance, ?, ?> treec = (AbstractMTree<O, DoubleDistance, ?, ?>) tree; + AbstractMTree<O, DoubleDistance, ?, ?, ?> treec = (AbstractMTree<O, DoubleDistance, ?, ?, ?>) tree; DistanceQuery<O, DoubleDistance> dqc = (DistanceQuery<O, DoubleDistance>) distanceQuery; - RangeQuery<O, ?> q = new DoubleDistanceMetricalIndexRangeQuery<O>(treec, dqc, dfc); + RangeQuery<O, ?> q = new DoubleDistanceMetricalIndexRangeQuery<>(treec, dqc, dfc); return (RangeQuery<O, D>) q; } - return new MetricalIndexRangeQuery<O, D>(tree, distanceQuery); + return new MetricalIndexRangeQuery<>(tree, distanceQuery); } } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexKNNQuery.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexKNNQuery.java index c2fafdae..f40e001b 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexKNNQuery.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexKNNQuery.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -23,25 +23,19 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query; along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import java.util.List; - -import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs; import de.lmu.ifi.dbs.elki.database.ids.DBID; -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.distance.KNNHeap; +import de.lmu.ifi.dbs.elki.database.ids.distance.KNNList; import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; import de.lmu.ifi.dbs.elki.database.query.knn.AbstractDistanceKNNQuery; -import de.lmu.ifi.dbs.elki.distance.DistanceUtil; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNHeap; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNUtil; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.DirectoryEntry; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; -import de.lmu.ifi.dbs.elki.index.tree.query.GenericMTreeDistanceSearchCandidate; -import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.Heap; -import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages; +import de.lmu.ifi.dbs.elki.index.tree.query.DoubleMTreeDistanceSearchCandidate; +import de.lmu.ifi.dbs.elki.utilities.datastructures.heap.ComparableMinHeap; /** * Instance of a KNN query for a particular spatial index. @@ -53,11 +47,11 @@ import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages; * @param <O> Object type * @param <D> Distance type */ -public class MetricalIndexKNNQuery<O, D extends Distance<D>> extends AbstractDistanceKNNQuery<O, D> { +public class MetricalIndexKNNQuery<O, D extends NumberDistance<D, ?>> extends AbstractDistanceKNNQuery<O, D> { /** * The index to use */ - protected final AbstractMTree<O, D, ?, ?> index; + protected final AbstractMTree<O, D, ?, ?, ?> index; /** * Constructor. @@ -65,55 +59,56 @@ public class MetricalIndexKNNQuery<O, D extends Distance<D>> extends AbstractDis * @param index Index to use * @param distanceQuery Distance query used */ - public MetricalIndexKNNQuery(AbstractMTree<O, D, ?, ?> index, DistanceQuery<O, D> distanceQuery) { + public MetricalIndexKNNQuery(AbstractMTree<O, D, ?, ?, ?> index, DistanceQuery<O, D> distanceQuery) { super(distanceQuery); this.index = index; } @Override - public KNNResult<D> getKNNForObject(O q, int k) { + public KNNList<D> getKNNForObject(O q, int k) { if (k < 1) { throw new IllegalArgumentException("At least one object has to be requested!"); } + index.statistics.countKNNQuery(); - final D nullDistance = index.getDistanceFactory().nullDistance(); - KNNHeap<D> knnList = KNNUtil.newHeap(distanceQuery.getDistanceFactory(), k); + KNNHeap<D> knnList = DBIDUtil.newHeap(distanceQuery.getDistanceFactory(), k); D d_k = knnList.getKNNDistance(); - final Heap<GenericMTreeDistanceSearchCandidate<D>> pq = new Heap<GenericMTreeDistanceSearchCandidate<D>>(); + final ComparableMinHeap<DoubleMTreeDistanceSearchCandidate> pq = new ComparableMinHeap<>(); // push root - pq.add(new GenericMTreeDistanceSearchCandidate<D>(nullDistance, index.getRootID(), null, nullDistance)); + pq.add(new DoubleMTreeDistanceSearchCandidate(0., index.getRootID(), null, 0.)); // search in tree while (!pq.isEmpty()) { - GenericMTreeDistanceSearchCandidate<D> pqNode = pq.poll(); + DoubleMTreeDistanceSearchCandidate pqNode = pq.poll(); - if (knnList.size() >= k && pqNode.mindist.compareTo(d_k) > 0) { + if (knnList.size() >= k && pqNode.mindist > d_k.doubleValue()) { break; } AbstractMTreeNode<?, D, ?, ?> node = index.getNode(pqNode.nodeID); DBID id_p = pqNode.routingObjectID; - D d1 = pqNode.routingDistance; + double d1 = pqNode.routingDistance; // directory node if (!node.isLeaf()) { for (int i = 0; i < node.getNumEntries(); i++) { - MTreeEntry<D> entry = node.getEntry(i); + MTreeEntry entry = node.getEntry(i); DBID o_r = entry.getRoutingObjectID(); - D r_or = entry.getCoveringRadius(); - D d2 = id_p != null ? entry.getParentDistance() : nullDistance; + double r_or = entry.getCoveringRadius(); + double d2 = id_p != null ? entry.getParentDistance() : 0.; - D diff = d1.compareTo(d2) > 0 ? d1.minus(d2) : d2.minus(d1); + double diff = Math.abs(d1 - d2); - D sum = d_k.plus(r_or); + double sum = d_k.doubleValue() + r_or; - if (diff.compareTo(sum) <= 0) { - D d3 = distanceQuery.distance(o_r, q); - D d_min = DistanceUtil.max(d3.minus(r_or), index.getDistanceFactory().nullDistance()); - if (d_min.compareTo(d_k) <= 0) { - pq.add(new GenericMTreeDistanceSearchCandidate<D>(d_min, ((DirectoryEntry) entry).getPageID(), o_r, d3)); + if (diff <= sum) { + double d3 = distanceQuery.distance(o_r, q).doubleValue(); + index.statistics.countDistanceCalculation(); + double d_min = Math.max(d3 - r_or, 0.); + if (d_min <= d_k.doubleValue()) { + pq.add(new DoubleMTreeDistanceSearchCandidate(d_min, ((DirectoryEntry) entry).getPageID(), o_r, d3)); } } } @@ -121,15 +116,16 @@ public class MetricalIndexKNNQuery<O, D extends Distance<D>> extends AbstractDis // data node else { for (int i = 0; i < node.getNumEntries(); i++) { - MTreeEntry<D> entry = node.getEntry(i); + MTreeEntry entry = node.getEntry(i); DBID o_j = entry.getRoutingObjectID(); - D d2 = id_p != null ? entry.getParentDistance() : nullDistance; + double d2 = id_p != null ? entry.getParentDistance() : 0.; - D diff = (d1.compareTo(d2) > 0) ? d1.minus(d2) : d2.minus(d1); + double diff = Math.abs(d1 - d2); - if (diff.compareTo(d_k) <= 0) { + if (diff <= d_k.doubleValue()) { D d3 = distanceQuery.distance(o_j, q); + index.statistics.countDistanceCalculation(); if (d3.compareTo(d_k) <= 0) { knnList.add(d3, o_j); d_k = knnList.getKNNDistance(); @@ -140,15 +136,4 @@ public class MetricalIndexKNNQuery<O, D extends Distance<D>> extends AbstractDis } return knnList.toKNNList(); } - - @Override - public KNNResult<D> getKNNForDBID(DBIDRef id, int k) { - return getKNNForObject(relation.get(id), k); - } - - @Override - public List<KNNResult<D>> getKNNForBulkDBIDs(ArrayDBIDs ids, int k) { - // TODO: implement - throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET); - } } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexRangeQuery.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexRangeQuery.java index 2ca19877..fedf8ddb 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexRangeQuery.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MetricalIndexRangeQuery.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -24,12 +24,11 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query; */ import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.database.ids.DBIDRef; +import de.lmu.ifi.dbs.elki.database.ids.distance.DistanceDBIDList; +import de.lmu.ifi.dbs.elki.database.ids.generic.GenericDistanceDBIDList; import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; import de.lmu.ifi.dbs.elki.database.query.range.AbstractDistanceRangeQuery; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.GenericDistanceDBIDList; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.DirectoryEntry; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; @@ -42,11 +41,11 @@ import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; * * @apiviz.uses AbstractMTree */ -public class MetricalIndexRangeQuery<O, D extends Distance<D>> extends AbstractDistanceRangeQuery<O, D> { +public class MetricalIndexRangeQuery<O, D extends NumberDistance<D, ?>> extends AbstractDistanceRangeQuery<O, D> { /** * The index to use */ - protected final AbstractMTree<O, D, ?, ?> index; + protected final AbstractMTree<O, D, ?, ?, ?> index; /** * Constructor. @@ -54,7 +53,7 @@ public class MetricalIndexRangeQuery<O, D extends Distance<D>> extends AbstractD * @param index Index to use * @param distanceQuery Distance query used */ - public MetricalIndexRangeQuery(AbstractMTree<O, D, ?, ?> index, DistanceQuery<O, D> distanceQuery) { + public MetricalIndexRangeQuery(AbstractMTree<O, D, ?, ?, ?> index, DistanceQuery<O, D> distanceQuery) { super(distanceQuery); this.index = index; } @@ -71,22 +70,26 @@ public class MetricalIndexRangeQuery<O, D extends Distance<D>> extends AbstractD * @param result the list holding the query results */ private void doRangeQuery(DBID o_p, AbstractMTreeNode<O, D, ?, ?> node, O q, D r_q, GenericDistanceDBIDList<D> result) { - final D nullDistance = distanceQuery.nullDistance(); - D d1 = o_p != null ? distanceQuery.distance(o_p, q) : nullDistance; + double d1 = 0.; + if (o_p != null) { + d1 = distanceQuery.distance(o_p, q).doubleValue(); + index.statistics.countDistanceCalculation(); + } if (!node.isLeaf()) { for (int i = 0; i < node.getNumEntries(); i++) { - MTreeEntry<D> entry = node.getEntry(i); + MTreeEntry entry = node.getEntry(i); DBID o_r = entry.getRoutingObjectID(); - D r_or = entry.getCoveringRadius(); - D d2 = o_p != null ? entry.getParentDistance() : nullDistance; - D diff = d1.compareTo(d2) > 0 ? d1.minus(d2) : d2.minus(d1); + double r_or = entry.getCoveringRadius(); + double d2 = o_p != null ? entry.getParentDistance() : 0.; + double diff = Math.abs(d1 - d2); - D sum = r_q.plus(r_or); + double sum = r_q.doubleValue() + r_or; - if (diff.compareTo(sum) <= 0) { + if (diff <= sum) { D d3 = distanceQuery.distance(o_r, q); - if (d3.compareTo(sum) <= 0) { + index.statistics.countDistanceCalculation(); + if (d3.doubleValue() <= sum) { AbstractMTreeNode<O, D, ?, ?> child = index.getNode(((DirectoryEntry) entry).getPageID()); doRangeQuery(o_r, child, q, r_q, result); } @@ -94,15 +97,16 @@ public class MetricalIndexRangeQuery<O, D extends Distance<D>> extends AbstractD } } else { for (int i = 0; i < node.getNumEntries(); i++) { - MTreeEntry<D> entry = node.getEntry(i); + MTreeEntry entry = node.getEntry(i); DBID o_j = entry.getRoutingObjectID(); - D d2 = o_p != null ? entry.getParentDistance() : nullDistance; + double d2 = o_p != null ? entry.getParentDistance() : 0.; - D diff = d1.compareTo(d2) > 0 ? d1.minus(d2) : d2.minus(d1); + double diff = Math.abs(d1 - d2); - if (diff.compareTo(r_q) <= 0) { + if (diff <= r_q.doubleValue()) { D d3 = distanceQuery.distance(o_j, q); + index.statistics.countDistanceCalculation(); if (d3.compareTo(r_q) <= 0) { result.add(d3, o_j); } @@ -112,18 +116,14 @@ public class MetricalIndexRangeQuery<O, D extends Distance<D>> extends AbstractD } @Override - public DistanceDBIDResult<D> getRangeForObject(O obj, D range) { - final GenericDistanceDBIDList<D> result = new GenericDistanceDBIDList<D>(); + public DistanceDBIDList<D> getRangeForObject(O obj, D range) { + final GenericDistanceDBIDList<D> result = new GenericDistanceDBIDList<>(); doRangeQuery(null, index.getRoot(), obj, range, result); + index.statistics.countRangeQuery(); // sort the result according to the distances result.sort(); return result; } - - @Override - public DistanceDBIDResult<D> getRangeForDBID(DBIDRef id, D range) { - return getRangeForObject(relation.get(id), range); - } } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MkTreeRKNNQuery.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MkTreeRKNNQuery.java index e3f35c50..c8cec69f 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MkTreeRKNNQuery.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/MkTreeRKNNQuery.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.query; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -27,13 +27,14 @@ import java.util.List; import de.lmu.ifi.dbs.elki.database.ids.ArrayDBIDs; import de.lmu.ifi.dbs.elki.database.ids.DBIDRef; +import de.lmu.ifi.dbs.elki.database.ids.distance.DistanceDBIDList; import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; import de.lmu.ifi.dbs.elki.database.query.rknn.AbstractRKNNQuery; -import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResult; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.mktrees.AbstractMkTree; import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException; import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages; +import de.lmu.ifi.dbs.elki.utilities.exceptions.NotImplementedException; /** * Instance of a rKNN query for a particular spatial index. @@ -42,11 +43,11 @@ import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages; * * @apiviz.uses AbstractMkTree */ -public class MkTreeRKNNQuery<O, D extends Distance<D>> extends AbstractRKNNQuery<O, D> { +public class MkTreeRKNNQuery<O, D extends NumberDistance<D, ?>> extends AbstractRKNNQuery<O, D> { /** * The index to use */ - protected final AbstractMkTree<O, D, ?, ?> index; + protected final AbstractMkTree<O, D, ?, ?, ?> index; /** * Constructor. @@ -54,24 +55,24 @@ public class MkTreeRKNNQuery<O, D extends Distance<D>> extends AbstractRKNNQuery * @param index Index to use * @param distanceQuery Distance query used */ - public MkTreeRKNNQuery(AbstractMkTree<O, D, ?, ?> index, DistanceQuery<O, D> distanceQuery) { + public MkTreeRKNNQuery(AbstractMkTree<O, D, ?, ?, ?> index, DistanceQuery<O, D> distanceQuery) { super(distanceQuery); this.index = index; } @Override - public DistanceDBIDResult<D> getRKNNForObject(O obj, int k) { + public DistanceDBIDList<D> getRKNNForObject(O obj, int k) { throw new AbortException("Preprocessor KNN query only supports ID queries."); } @Override - public DistanceDBIDResult<D> getRKNNForDBID(DBIDRef id, int k) { + public DistanceDBIDList<D> getRKNNForDBID(DBIDRef id, int k) { return index.reverseKNNQuery(id, k); } @Override - public List<? extends DistanceDBIDResult<D>> getRKNNForBulkDBIDs(ArrayDBIDs ids, int k) { + public List<? extends DistanceDBIDList<D>> getRKNNForBulkDBIDs(ArrayDBIDs ids, int k) { // TODO: implement - throw new UnsupportedOperationException(ExceptionMessages.UNSUPPORTED_NOT_YET); + throw new NotImplementedException(ExceptionMessages.UNSUPPORTED_NOT_YET); } }
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/package-info.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/package-info.java index fa7e6248..a975fdff 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/package-info.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/query/package-info.java @@ -6,7 +6,7 @@ This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures -Copyright (C) 2012 +Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/split/MTreeSplit.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/split/MTreeSplit.java deleted file mode 100644 index f9a3c248..00000000 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/split/MTreeSplit.java +++ /dev/null @@ -1,138 +0,0 @@ -package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.split; - -/* - 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 java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; -import de.lmu.ifi.dbs.elki.distance.DistanceUtil; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; -import de.lmu.ifi.dbs.elki.index.tree.DistanceEntry; -import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; -import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; - -/** - * Abstract super class for splitting a node in an M-Tree. - * - * @author Elke Achtert - * - * @apiviz.composedOf Assignments - * - * @param <O> the type of DatabaseObject to be stored in the M-Tree - * @param <D> the type of Distance used in the M-Tree - * @param <N> the type of AbstractMTreeNode used in the M-Tree - * @param <E> the type of MetricalEntry used in the M-Tree - */ -public abstract class MTreeSplit<O, D extends Distance<D>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry<D>> { - /** - * Encapsulates the two promotion objects and their assignments. - */ - Assignments<D, E> assignments; - - /** - * Creates a balanced partition of the entries of the specified node. - * - * @param node the node to be split - * @param routingObject1 the id of the first routing object - * @param routingObject2 the id of the second routing object - * @param distanceFunction the distance function to compute the distances - * @return an assignment that holds a balanced partition of the entries of the - * specified node - */ - Assignments<D, E> balancedPartition(N node, DBID routingObject1, DBID routingObject2, DistanceQuery<O, D> distanceFunction) { - HashSet<E> assigned1 = new HashSet<E>(); - HashSet<E> assigned2 = new HashSet<E>(); - - D currentCR1 = distanceFunction.nullDistance(); - D currentCR2 = distanceFunction.nullDistance(); - - // determine the nearest neighbors - List<DistanceEntry<D, E>> list1 = new ArrayList<DistanceEntry<D, E>>(); - List<DistanceEntry<D, E>> list2 = new ArrayList<DistanceEntry<D, E>>(); - for(int i = 0; i < node.getNumEntries(); i++) { - DBID id = node.getEntry(i).getRoutingObjectID(); - // determine the distance of o to o1 / o2 - D d1 = distanceFunction.distance(routingObject1, id); - D d2 = distanceFunction.distance(routingObject2, id); - - list1.add(new DistanceEntry<D, E>(node.getEntry(i), d1, i)); - list2.add(new DistanceEntry<D, E>(node.getEntry(i), d2, i)); - } - Collections.sort(list1); - Collections.sort(list2); - - for(int i = 0; i < node.getNumEntries(); i++) { - if(i % 2 == 0) { - currentCR1 = assignNN(assigned1, assigned2, list1, currentCR1, node.isLeaf()); - } - else { - currentCR2 = assignNN(assigned2, assigned1, list2, currentCR2, node.isLeaf()); - } - } - return new Assignments<D, E>(routingObject1, routingObject2, currentCR1, currentCR2, assigned1, assigned2); - } - - /** - * Assigns the first object of the specified list to the first assignment that - * it is not yet assigned to the second assignment. - * - * @param assigned1 the first assignment - * @param assigned2 the second assignment - * @param list the list, the first object should be assigned - * @param currentCR the current covering radius - * @param isLeaf true, if the node of the entries to be assigned is a leaf, - * false otherwise - * @return the new covering radius - */ - private D assignNN(Set<E> assigned1, Set<E> assigned2, List<DistanceEntry<D, E>> list, D currentCR, boolean isLeaf) { - DistanceEntry<D, E> distEntry = list.remove(0); - while(assigned2.contains(distEntry.getEntry())) { - distEntry = list.remove(0); - } - // Update the parent distance. - distEntry.getEntry().setParentDistance(distEntry.getDistance()); - assigned1.add(distEntry.getEntry()); - - if(isLeaf) { - return DistanceUtil.max(currentCR, distEntry.getDistance()); - } - else { - return DistanceUtil.max(currentCR, distEntry.getDistance().plus((distEntry.getEntry()).getCoveringRadius())); - } - } - - /** - * Returns the assignments of this split. - * - * @return the assignments of this split - */ - public Assignments<D, E> getAssignments() { - return assignments; - } -} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/split/package-info.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/split/package-info.java deleted file mode 100644 index ed13ae73..00000000 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/split/package-info.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * <p>Splitting strategies of nodes in an M-Tree (and variants).</p> - */ -/* -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/>. -*/ -package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.split;
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/insert/MTreeInsert.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/insert/MTreeInsert.java new file mode 100644 index 00000000..65fc3768 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/insert/MTreeInsert.java @@ -0,0 +1,50 @@ +package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.insert; + +/* + This file is part of ELKI: + Environment for Developing KDD-Applications Supported by Index-Structures + + Copyright (C) 2013 + 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.distance.distancevalue.NumberDistance; +import de.lmu.ifi.dbs.elki.index.tree.IndexTreePath; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; + +/** + * Default insertion strategy for the M-tree. + * + * <b>Warning:</b> as of now, insertion strategies <b>will already modify the + * tree</b>, i.e. adjust cover radiuses. + * + * FIXME: move this to the actual insert. + * + * @author Erich Schubert + */ +public interface MTreeInsert<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry> { + /** + * Choose the subpath to insert into. + * + * @param tree Tree to process + * @param object Object to insert + * @return Path to insertion node + */ + IndexTreePath<E> choosePath(AbstractMTree<O, D, N, E, ?> tree, E object); +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/insert/MinimumEnlargementInsert.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/insert/MinimumEnlargementInsert.java new file mode 100644 index 00000000..f848f5f4 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/insert/MinimumEnlargementInsert.java @@ -0,0 +1,117 @@ +package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.insert; + +/* + This file is part of ELKI: + Environment for Developing KDD-Applications Supported by Index-Structures + + Copyright (C) 2013 + 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.distance.distancevalue.NumberDistance; +import de.lmu.ifi.dbs.elki.index.tree.IndexTreePath; +import de.lmu.ifi.dbs.elki.index.tree.TreeIndexPathComponent; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; +import de.lmu.ifi.dbs.elki.utilities.documentation.Reference; + +/** + * Default insertion strategy for the M-tree. + * + * Reference: + * <p> + * P. Ciaccia, M. Patella, P. Zezula<br /> + * M-tree: An Efficient Access Method for Similarity Search in Metric Spaces<br /> + * In Proceedings of 23rd International Conference on Very Large Data Bases + * (VLDB'97), August 25-29, 1997, Athens, Greece + * </p> + * + * @author Erich Schubert + */ +@Reference(authors = "P. Ciaccia, M. Patella, P. Zezula", title = "M-tree: An Efficient Access Method for Similarity Search in Metric Spaces", booktitle = "VLDB'97, Proceedings of 23rd International Conference on Very Large Data Bases, August 25-29, 1997, Athens, Greece", url = "http://www.vldb.org/conf/1997/P426.PDF") +public class MinimumEnlargementInsert<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry> implements MTreeInsert<O, D, N, E> { + @Override + public IndexTreePath<E> choosePath(AbstractMTree<O, D, N, E, ?> tree, E object) { + return choosePath(tree, object, tree.getRootPath()); + } + + /** + * Chooses the best path of the specified subtree for insertion of the given + * object. + * + * @param tree the tree to insert into + * @param object the entry to search + * @param subtree the subtree to be tested for insertion + * @return the path of the appropriate subtree to insert the given object + */ + private IndexTreePath<E> choosePath(AbstractMTree<O, D, N, E, ?> tree, E object, IndexTreePath<E> subtree) { + N node = tree.getNode(subtree.getLastPathComponent().getEntry()); + + // leaf + if (node.isLeaf()) { + return subtree; + } + + double bestDistance; + int bestIdx; + E bestEntry; + double enlarge; // Track best enlargement - null for no enlargement needed. + // Initialize from first: + { + bestIdx = 0; + bestEntry = node.getEntry(0); + bestDistance = tree.distance(object.getRoutingObjectID(), bestEntry.getRoutingObjectID()).doubleValue(); + if (bestDistance <= bestEntry.getCoveringRadius()) { + enlarge = 0.; + } else { + enlarge = bestDistance - bestEntry.getCoveringRadius(); + } + } + + // Iterate over remaining + for (int i = 1; i < node.getNumEntries(); i++) { + E entry = node.getEntry(i); + double distance = tree.distance(object.getRoutingObjectID(), entry.getRoutingObjectID()).doubleValue(); + + if (distance <= entry.getCoveringRadius()) { + if (enlarge > 0. || distance < bestDistance) { + bestIdx = i; + bestEntry = entry; + bestDistance = distance; + enlarge = 0.; + } + } else if (enlarge > 0.) { + double enlrg = distance - entry.getCoveringRadius(); + if (enlrg < enlarge) { + bestIdx = i; + bestEntry = entry; + bestDistance = distance; + enlarge = enlrg; + } + } + } + + // FIXME: move this to the actual insertion procedure! + // Apply enlargement + if (enlarge > 0) { + bestEntry.setCoveringRadius(bestEntry.getCoveringRadius() + enlarge); + } + + return choosePath(tree, object, subtree.pathByAddingChild(new TreeIndexPathComponent<>(bestEntry, bestIdx))); + } +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/insert/package-info.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/insert/package-info.java new file mode 100644 index 00000000..64a85c2d --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/insert/package-info.java @@ -0,0 +1,26 @@ +/** + * <p>Insertion (choose path) strategies of nodes in an M-Tree (and variants).</p> + */ +/* + This file is part of ELKI: + Environment for Developing KDD-Applications Supported by Index-Structures + + Copyright (C) 2013 + 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/>. + */ +package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.insert;
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/package-info.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/package-info.java new file mode 100644 index 00000000..0d019cf3 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/package-info.java @@ -0,0 +1,26 @@ +/** + * <p>Strategies for M-Trees (and variants).</p> + */ +/* + This file is part of ELKI: + Environment for Developing KDD-Applications Supported by Index-Structures + + Copyright (C) 2013 + 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/>. + */ +package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies;
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/split/Assignments.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/Assignments.java index 5cb6c794..1079a141 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/split/Assignments.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/Assignments.java @@ -1,10 +1,10 @@ -package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.split; +package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.split; /* This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -23,22 +23,21 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.split; along with this program. If not, see <http://www.gnu.org/licenses/>. */ +import java.util.List; + import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - /** * Encapsulates the attributes of an assignment during a split. * * @author Elke Achtert - * @param <D> the type of Distance used in the M-Tree + * + * @apiviz.composedOf DistanceEntry + * * @param <E> the type of MetricalEntry used in the M-Tree */ -public class Assignments<D extends Distance<D>, E extends MTreeEntry<D>> { +public class Assignments<E extends MTreeEntry> { /** * The id of the first routing object. */ @@ -52,22 +51,22 @@ public class Assignments<D extends Distance<D>, E extends MTreeEntry<D>> { /** * The first covering radius. */ - private D firstCoveringRadius; + private double firstCoveringRadius; /** * The second covering radius. */ - private D secondCoveringRadius; + private double secondCoveringRadius; /** * The assignments to the first routing object. */ - private List<E> firstAssignments; + private List<DistanceEntry<E>> firstAssignments; /** * The assignments to the second routing object. */ - private List<E> secondAssignments; + private List<DistanceEntry<E>> secondAssignments; /** * Provides an assignment during a split of an MTree node. @@ -79,13 +78,13 @@ public class Assignments<D extends Distance<D>, E extends MTreeEntry<D>> { * @param firstAssignments the assignments to the first routing object * @param secondAssignments the assignments to the second routing object */ - public Assignments(DBID id1, DBID id2, D firstCoveringRadius, D secondCoveringRadius, Set<E> firstAssignments, Set<E> secondAssignments) { + public Assignments(DBID id1, DBID id2, double firstCoveringRadius, double secondCoveringRadius, List<DistanceEntry<E>> firstAssignments, List<DistanceEntry<E>> secondAssignments) { this.id1 = id1; this.id2 = id2; this.firstCoveringRadius = firstCoveringRadius; this.secondCoveringRadius = secondCoveringRadius; - this.firstAssignments = new ArrayList<E>(firstAssignments); - this.secondAssignments = new ArrayList<E>(secondAssignments); + this.firstAssignments = firstAssignments; + this.secondAssignments = secondAssignments; } /** @@ -111,7 +110,7 @@ public class Assignments<D extends Distance<D>, E extends MTreeEntry<D>> { * * @return the first covering radius */ - public D getFirstCoveringRadius() { + public double getFirstCoveringRadius() { return firstCoveringRadius; } @@ -120,7 +119,7 @@ public class Assignments<D extends Distance<D>, E extends MTreeEntry<D>> { * * @return the second covering radius */ - public D getSecondCoveringRadius() { + public double getSecondCoveringRadius() { return secondCoveringRadius; } @@ -129,7 +128,7 @@ public class Assignments<D extends Distance<D>, E extends MTreeEntry<D>> { * * @return the assignments to the first routing object */ - public List<E> getFirstAssignments() { + public List<DistanceEntry<E>> getFirstAssignments() { return firstAssignments; } @@ -138,7 +137,7 @@ public class Assignments<D extends Distance<D>, E extends MTreeEntry<D>> { * * @return the assignments to the second routing object */ - public List<E> getSecondAssignments() { + public List<DistanceEntry<E>> getSecondAssignments() { return secondAssignments; } } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/DistanceEntry.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/DistanceEntry.java new file mode 100644 index 00000000..642e5a63 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/DistanceEntry.java @@ -0,0 +1,123 @@ +package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.split; + +import de.lmu.ifi.dbs.elki.index.tree.Entry; + +/* + This file is part of ELKI: + Environment for Developing KDD-Applications Supported by Index-Structures + + Copyright (C) 2013 + 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/>. + */ + +/** + * Helper class: encapsulates an entry in an Index and a distance value + * belonging to this entry. + * + * @author Elke Achtert + * + * @apiviz.uses Entry + * + * @param <E> the type of Entry used in the index + */ +public class DistanceEntry<E extends Entry> implements Comparable<DistanceEntry<E>> { + /** + * The entry of the Index. + */ + private E entry; + + /** + * The distance value belonging to the entry. + */ + private double distance; + + /** + * The index of the entry in its parent's child array. + */ + private int index; + + /** + * Constructs a new DistanceEntry object with the specified parameters. + * + * @param entry the entry of the Index + * @param distance the distance value belonging to the entry + * @param index the index of the entry in its parent' child array + */ + public DistanceEntry(E entry, double distance, int index) { + this.entry = entry; + this.distance = distance; + this.index = index; + } + + /** + * Returns the entry of the Index. + * + * @return the entry of the Index + */ + public E getEntry() { + return entry; + } + + /** + * Returns the distance value belonging to the entry. + * + * @return the distance value belonging to the entry + */ + public double getDistance() { + return distance; + } + + /** + * Returns the index of this entry in its parents child array. + * + * @return the index of this entry in its parents child array + */ + public int getIndex() { + return index; + } + + /** + * Compares this object with the specified object for order. + * + * @param o the Object to be compared. + * @return a negative integer, zero, or a positive integer as this object is + * less than, equal to, or greater than the specified object. + * @throws ClassCastException if the specified object's type prevents it from + * being compared to this Object. + */ + @Override + public int compareTo(DistanceEntry<E> o) { + int comp = Double.compare(distance, o.distance); + if (comp != 0) { + return comp; + } + + // return entry.getEntryID().compareTo(o.entry.getEntryID()); + return 0; + } + + /** + * Returns a string representation of the object. + * + * @return a string representation of the object. + */ + @Override + public String toString() { + return "" + entry.toString() + "(" + distance + ")"; + } +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/split/MLBDistSplit.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/MLBDistSplit.java index f4da4a1a..d294d5b3 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/split/MLBDistSplit.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/MLBDistSplit.java @@ -1,10 +1,10 @@ -package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.split; +package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.split; /* This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -23,37 +23,39 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.split; along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; -import de.lmu.ifi.dbs.elki.index.tree.DistanceEntry; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; +import de.lmu.ifi.dbs.elki.utilities.documentation.Reference; /** * Encapsulates the required methods for a split of a node in an M-Tree. The * routing objects are chosen according to the M_LB_DIST strategy. * + * Reference: + * <p> + * P. Ciaccia, M. Patella, P. Zezula<br /> + * M-tree: An Efficient Access Method for Similarity Search in Metric Spaces<br /> + * In Proceedings of 23rd International Conference on Very Large Data Bases + * (VLDB'97), August 25-29, 1997, Athens, Greece + * </p> + * * @author Elke Achtert + * * @param <O> the type of DatabaseObject to be stored in the M-Tree * @param <D> the type of Distance used in the M-Tree * @param <N> the type of AbstractMTreeNode used in the M-Tree * @param <E> the type of MetricalEntry used in the M-Tree */ -public class MLBDistSplit<O, D extends Distance<D>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry<D>> extends MTreeSplit<O, D, N, E> { +@Reference(authors = "P. Ciaccia, M. Patella, P. Zezula", title = "M-tree: An Efficient Access Method for Similarity Search in Metric Spaces", booktitle = "VLDB'97, Proceedings of 23rd International Conference on Very Large Data Bases, August 25-29, 1997, Athens, Greece", url = "http://www.vldb.org/conf/1997/P426.PDF") +public class MLBDistSplit<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry> extends MTreeSplit<O, D, N, E> { /** * Creates a new split object. - * - * @param node the node to be split - * @param distanceFunction the distance function */ - public MLBDistSplit(N node, DistanceQuery<O, D> distanceFunction) { + public MLBDistSplit() { super(); - promote(node, distanceFunction); } /** @@ -64,22 +66,23 @@ public class MLBDistSplit<O, D extends Distance<D>, N extends AbstractMTreeNode< * This strategy considers all possible pairs of objects and chooses the pair * of objects for which the distance is maximum. * + * @param tree Tree to use * @param node the node to be split - * @param distanceFunction the distance function */ - private void promote(N node, DistanceQuery<O, D> distanceFunction) { + @Override + public Assignments<E> split(AbstractMTree<O, D, N, E, ?> tree, N node) { DBID firstPromoted = null; DBID secondPromoted = null; // choose first and second routing object - D currentMaxDist = distanceFunction.nullDistance(); - for(int i = 0; i < node.getNumEntries(); i++) { + double currentMaxDist = 0.; + for (int i = 0; i < node.getNumEntries(); i++) { DBID id1 = node.getEntry(i).getRoutingObjectID(); - for(int j = i + 1; j < node.getNumEntries(); j++) { + for (int j = i + 1; j < node.getNumEntries(); j++) { DBID id2 = node.getEntry(j).getRoutingObjectID(); - D distance = distanceFunction.distance(id1, id2); - if(distance.compareTo(currentMaxDist) >= 0) { + double distance = tree.distance(id1, id2).doubleValue(); + if (distance >= currentMaxDist) { firstPromoted = id1; secondPromoted = id2; currentMaxDist = distance; @@ -87,20 +90,6 @@ public class MLBDistSplit<O, D extends Distance<D>, N extends AbstractMTreeNode< } } - // partition the entries - List<DistanceEntry<D, E>> list1 = new ArrayList<DistanceEntry<D, E>>(); - List<DistanceEntry<D, E>> list2 = new ArrayList<DistanceEntry<D, E>>(); - for(int i = 0; i < node.getNumEntries(); i++) { - DBID id = node.getEntry(i).getRoutingObjectID(); - D d1 = distanceFunction.distance(firstPromoted, id); - D d2 = distanceFunction.distance(secondPromoted, id); - - list1.add(new DistanceEntry<D, E>(node.getEntry(i), d1, i)); - list2.add(new DistanceEntry<D, E>(node.getEntry(i), d2, i)); - } - Collections.sort(list1); - Collections.sort(list2); - - assignments = balancedPartition(node, firstPromoted, secondPromoted, distanceFunction); + return balancedPartition(tree, node, firstPromoted, secondPromoted); } } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/MMRadSplit.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/MMRadSplit.java new file mode 100644 index 00000000..232df088 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/MMRadSplit.java @@ -0,0 +1,88 @@ +package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.split; + +/* + This file is part of ELKI: + Environment for Developing KDD-Applications Supported by Index-Structures + + Copyright (C) 2013 + 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.distance.distancevalue.NumberDistance; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; +import de.lmu.ifi.dbs.elki.utilities.documentation.Reference; + +/** + * Encapsulates the required methods for a split of a node in an M-Tree. The + * routing objects are chosen according to the mM_rad strategy. + * + * Reference: + * <p> + * P. Ciaccia, M. Patella, P. Zezula<br /> + * M-tree: An Efficient Access Method for Similarity Search in Metric Spaces<br /> + * In Proceedings of 23rd International Conference on Very Large Data Bases + * (VLDB'97), August 25-29, 1997, Athens, Greece + * </p> + * + * @author Elke Achtert + * + * @param <O> the type of DatabaseObject to be stored in the M-Tree + * @param <D> the type of Distance used in the M-Tree + * @param <N> the type of AbstractMTreeNode used in the M-Tree + * @param <E> the type of MetricalEntry used in the M-Tree + */ +@Reference(authors = "P. Ciaccia, M. Patella, P. Zezula", title = "M-tree: An Efficient Access Method for Similarity Search in Metric Spaces", booktitle = "VLDB'97, Proceedings of 23rd International Conference on Very Large Data Bases, August 25-29, 1997, Athens, Greece", url = "http://www.vldb.org/conf/1997/P426.PDF") +public class MMRadSplit<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry> extends MTreeSplit<O, D, N, E> { + /** + * Creates a new split object. + */ + public MMRadSplit() { + super(); + } + + /** + * Selects two objects of the specified node to be promoted and stored into + * the parent node. The mM-RAD strategy considers all possible pairs of + * objects and, after partitioning the set of entries, promotes the pair of + * objects for which the larger of the two covering radiuses is minimum. + * + * @param tree Tree to use + * @param node the node to be split + */ + @Override + public Assignments<E> split(AbstractMTree<O, D, N, E, ?> tree, N node) { + double miSumCR = Double.POSITIVE_INFINITY; + double[] distanceMatrix = computeDistanceMatrix(tree, node); + + Assignments<E> bestAssignment = null; + for (int i = 0; i < node.getNumEntries(); i++) { + for (int j = i + 1; j < node.getNumEntries(); j++) { + Assignments<E> currentAssignments = balancedPartition(tree, node, i, j, distanceMatrix); + + double maxCR = Math.max(currentAssignments.getFirstCoveringRadius(), currentAssignments.getSecondCoveringRadius()); + if (maxCR < miSumCR) { + miSumCR = maxCR; + bestAssignment = currentAssignments; + } + } + } + return bestAssignment; + } +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/split/MRadSplit.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/MRadSplit.java index 5d6c985c..5de15356 100644 --- a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/split/MRadSplit.java +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/MRadSplit.java @@ -1,10 +1,10 @@ -package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.split; +package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.split; /* This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -23,32 +23,38 @@ package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.split; along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import de.lmu.ifi.dbs.elki.database.ids.DBID; -import de.lmu.ifi.dbs.elki.database.query.distance.DistanceQuery; -import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; +import de.lmu.ifi.dbs.elki.utilities.documentation.Reference; /** * Encapsulates the required methods for a split of a node in an M-Tree. The * routing objects are chosen according to the M_rad strategy. * + * Reference: + * <p> + * P. Ciaccia, M. Patella, P. Zezula<br /> + * M-tree: An Efficient Access Method for Similarity Search in Metric Spaces<br /> + * In Proceedings of 23rd International Conference on Very Large Data Bases + * (VLDB'97), August 25-29, 1997, Athens, Greece + * </p> + * * @author Elke Achtert + * * @param <O> the type of DatabaseObject to be stored in the M-Tree * @param <D> the type of Distance used in the M-Tree * @param <N> the type of AbstractMTreeNode used in the M-Tree * @param <E> the type of MetricalEntry used in the M-Tree */ -public class MRadSplit<O, D extends Distance<D>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry<D>> extends MTreeSplit<O, D, N, E> { +@Reference(authors = "P. Ciaccia, M. Patella, P. Zezula", title = "M-tree: An Efficient Access Method for Similarity Search in Metric Spaces", booktitle = "VLDB'97, Proceedings of 23rd International Conference on Very Large Data Bases, August 25-29, 1997, Athens, Greece", url = "http://www.vldb.org/conf/1997/P426.PDF") +public class MRadSplit<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry> extends MTreeSplit<O, D, N, E> { /** * Creates a new split object. - * - * @param node the node to be split - * @param distanceFunction the distance function */ - public MRadSplit(N node, DistanceQuery<O, D> distanceFunction) { + public MRadSplit() { super(); - promote(node, distanceFunction); } /** @@ -57,26 +63,26 @@ public class MRadSplit<O, D extends Distance<D>, N extends AbstractMTreeNode<O, * and, after partitioning the set of entries, promotes the pair of objects * for which the sum of covering radiuses is minimum. * + * @param tree Tree to use * @param node the node to be split - * @param distanceFunction the distance function */ - private void promote(N node, DistanceQuery<O, D> distanceFunction) { - D miSumCR = distanceFunction.infiniteDistance(); - - for(int i = 0; i < node.getNumEntries(); i++) { - DBID id1 = node.getEntry(i).getRoutingObjectID(); + @Override + public Assignments<E> split(AbstractMTree<O, D, N, E, ?> tree, N node) { + double miSumCR = Double.POSITIVE_INFINITY; + double[] distanceMatrix = computeDistanceMatrix(tree, node); - for(int j = i + 1; j < node.getNumEntries(); j++) { - DBID id2 = node.getEntry(i).getRoutingObjectID(); - // ... for each pair do testPartition... - Assignments<D, E> currentAssignments = balancedPartition(node, id1, id2, distanceFunction); + Assignments<E> bestAssignment = null; + for (int i = 0; i < node.getNumEntries(); i++) { + for (int j = i + 1; j < node.getNumEntries(); j++) { + Assignments<E> currentAssignments = balancedPartition(tree, node, i, j, distanceMatrix); - D sumCR = currentAssignments.getFirstCoveringRadius().plus(currentAssignments.getSecondCoveringRadius()); - if(sumCR.compareTo(miSumCR) < 0) { + double sumCR = currentAssignments.getFirstCoveringRadius() + currentAssignments.getSecondCoveringRadius(); + if (sumCR < miSumCR) { miSumCR = sumCR; - assignments = currentAssignments; + bestAssignment = currentAssignments; } } } + return bestAssignment; } } diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/MTreeSplit.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/MTreeSplit.java new file mode 100644 index 00000000..167b5368 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/MTreeSplit.java @@ -0,0 +1,223 @@ +package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.split; + +/* + This file is part of ELKI: + Environment for Developing KDD-Applications Supported by Index-Structures + + Copyright (C) 2013 + 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.ArrayList; +import java.util.BitSet; +import java.util.Collections; +import java.util.List; + +import de.lmu.ifi.dbs.elki.database.ids.DBID; +import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; + +/** + * Abstract super class for splitting a node in an M-Tree. + * + * @author Elke Achtert + * + * @apiviz.composedOf Assignments + * + * @param <O> the type of DatabaseObject to be stored in the M-Tree + * @param <D> the type of Distance used in the M-Tree + * @param <N> the type of AbstractMTreeNode used in the M-Tree + * @param <E> the type of MetricalEntry used in the M-Tree + */ +public abstract class MTreeSplit<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry> { + /** + * Compute the pairwise distances in the given node. + * + * @param tree Tree + * @param node Node + * @return Distance matrix + */ + protected double[] computeDistanceMatrix(AbstractMTree<O, D, N, E, ?> tree, N node) { + final int n = node.getNumEntries(); + double[] distancematrix = new double[n * n]; + // Build distance matrix + for (int i = 0; i < n; i++) { + E ei = node.getEntry(i); + for (int j = 0; j < n; j++) { + if (i == j) { + distancematrix[i * n + j] = 0.0; + } else if (i < j) { + distancematrix[i * n + j] = tree.distance(ei, node.getEntry(j)).doubleValue(); + } else { // i > j + distancematrix[i * n + j] = distancematrix[j * n + i]; + } + } + } + return distancematrix; + } + + /** + * Creates a balanced partition of the entries of the specified node. + * + * @param tree the tree to perform the split in + * @param node the node to be split + * @param routingObject1 the id of the first routing object + * @param routingObject2 the id of the second routing object + * @return an assignment that holds a balanced partition of the entries of the + * specified node + */ + Assignments<E> balancedPartition(AbstractMTree<O, D, N, E, ?> tree, N node, DBID routingObject1, DBID routingObject2) { + BitSet assigned = new BitSet(node.getNumEntries()); + List<DistanceEntry<E>> assigned1 = new ArrayList<>(node.getCapacity()); + List<DistanceEntry<E>> assigned2 = new ArrayList<>(node.getCapacity()); + + double currentCR1 = 0.; + double currentCR2 = 0.; + + List<DistanceEntry<E>> list1 = new ArrayList<>(); + List<DistanceEntry<E>> list2 = new ArrayList<>(); + + // determine the nearest neighbors + for (int i = 0; i < node.getNumEntries(); i++) { + final E ent = node.getEntry(i); + DBID id = ent.getRoutingObjectID(); + if (DBIDUtil.equal(id, routingObject1)) { + assigned1.add(new DistanceEntry<>(ent, 0., i)); + continue; + } + if (DBIDUtil.equal(id, routingObject2)) { + assigned2.add(new DistanceEntry<>(ent, 0., i)); + continue; + } + // determine the distance of o to o1 / o2 + double d1 = tree.distance(routingObject1, id).doubleValue(); + double d2 = tree.distance(routingObject2, id).doubleValue(); + + list1.add(new DistanceEntry<>(ent, d1, i)); + list2.add(new DistanceEntry<>(ent, d2, i)); + } + Collections.sort(list1, Collections.reverseOrder()); + Collections.sort(list2, Collections.reverseOrder()); + + for (int i = 2; i < node.getNumEntries(); i++) { + currentCR1 = assignNN(assigned, assigned1, list1, currentCR1, node.isLeaf()); + i++; + if (i < node.getNumEntries()) { + currentCR2 = assignNN(assigned, assigned2, list2, currentCR2, node.isLeaf()); + } + } + return new Assignments<>(routingObject1, routingObject2, currentCR1, currentCR2, assigned1, assigned2); + } + + /** + * Creates a balanced partition of the entries of the specified node. + * + * @param tree the tree to perform the split in + * @param node the node to be split + * @param routingEntNum1 the entry number of the first routing object + * @param routingEntNum2 the entry number of the second routing object + * @param distanceMatrix precomputed distance matrix to use + * @return an assignment that holds a balanced partition of the entries of the + * specified node + */ + Assignments<E> balancedPartition(AbstractMTree<O, D, N, E, ?> tree, N node, int routingEntNum1, int routingEntNum2, double[] distanceMatrix) { + final int n = node.getNumEntries(); + BitSet assigned = new BitSet(node.getNumEntries()); + List<DistanceEntry<E>> assigned1 = new ArrayList<>(node.getCapacity()); + List<DistanceEntry<E>> assigned2 = new ArrayList<>(node.getCapacity()); + + double currentCR1 = 0.; + double currentCR2 = 0.; + + List<DistanceEntry<E>> list1 = new ArrayList<>(); + List<DistanceEntry<E>> list2 = new ArrayList<>(); + + DBID routingObject1 = null, routingObject2 = null; + // determine the nearest neighbors + for (int i = 0; i < node.getNumEntries(); i++) { + final E ent = node.getEntry(i); + if (i == routingEntNum1) { + routingObject1 = ent.getRoutingObjectID(); + assigned1.add(new DistanceEntry<>(ent, 0., i)); + continue; + } + if (i == routingEntNum2) { + routingObject2 = ent.getRoutingObjectID(); + assigned2.add(new DistanceEntry<>(ent, 0., i)); + continue; + } + // Look up the distances of o to o1 / o2 + double d1 = distanceMatrix[i * n + routingEntNum1]; + double d2 = distanceMatrix[i * n + routingEntNum2]; + + list1.add(new DistanceEntry<>(ent, d1, i)); + list2.add(new DistanceEntry<>(ent, d2, i)); + } + Collections.sort(list1, Collections.reverseOrder()); + Collections.sort(list2, Collections.reverseOrder()); + + for (int i = 2; i < node.getNumEntries(); i++) { + currentCR1 = assignNN(assigned, assigned1, list1, currentCR1, node.isLeaf()); + i++; + if (i < node.getNumEntries()) { + currentCR2 = assignNN(assigned, assigned2, list2, currentCR2, node.isLeaf()); + } + } + return new Assignments<>(routingObject1, routingObject2, currentCR1, currentCR2, assigned1, assigned2); + } + + /** + * Assigns the first object of the specified list to the first assignment that + * it is not yet assigned to the second assignment. + * + * @param assigned List of already assigned objects + * @param assigned1 the first assignment + * @param list the list, the first object should be assigned + * @param currentCR the current covering radius + * @param isLeaf true, if the node of the entries to be assigned is a leaf, + * false otherwise + * @return the new covering radius + */ + private double assignNN(BitSet assigned, List<DistanceEntry<E>> assigned1, List<DistanceEntry<E>> list, double currentCR, boolean isLeaf) { + // Remove last unassigned: + DistanceEntry<E> distEntry = list.remove(list.size() - 1); + while (assigned.get(distEntry.getIndex())) { + distEntry = list.remove(list.size() - 1); + } + assigned1.add(distEntry); + assigned.set(distEntry.getIndex()); + + if (isLeaf) { + return Math.max(currentCR, distEntry.getDistance()); + } else { + return Math.max(currentCR, distEntry.getDistance() + (distEntry.getEntry()).getCoveringRadius()); + } + } + + /** + * Returns the assignments of this split. + * + * @param tree Tree to use + * @param node Node to split + * @return the assignments of this split + */ + abstract public Assignments<E> split(AbstractMTree<O, D, N, E, ?> tree, N node); +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/RandomSplit.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/RandomSplit.java new file mode 100644 index 00000000..faf2acc2 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/RandomSplit.java @@ -0,0 +1,136 @@ +package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.split; + +/* + This file is part of ELKI: + Environment for Developing KDD-Applications Supported by Index-Structures + + Copyright (C) 2013 + 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.Random; + +import de.lmu.ifi.dbs.elki.database.ids.DBID; +import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTree; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.AbstractMTreeNode; +import de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.MTreeEntry; +import de.lmu.ifi.dbs.elki.utilities.RandomFactory; +import de.lmu.ifi.dbs.elki.utilities.documentation.Reference; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.RandomParameter; + +/** + * Encapsulates the required methods for a split of a node in an M-Tree. The + * routing objects are chosen according to the RANDOM strategy. + * + * Note: only the routing objects are chosen at random, this is not a random + * assignment! + * + * Reference: + * <p> + * P. Ciaccia, M. Patella, P. Zezula<br /> + * M-tree: An Efficient Access Method for Similarity Search in Metric Spaces<br /> + * In Proceedings of 23rd International Conference on Very Large Data Bases + * (VLDB'97), August 25-29, 1997, Athens, Greece + * </p> + * + * @author Elke Achtert + * + * @param <O> the type of DatabaseObject to be stored in the M-Tree + * @param <D> the type of Distance used in the M-Tree + * @param <N> the type of AbstractMTreeNode used in the M-Tree + * @param <E> the type of MetricalEntry used in the M-Tree + */ +@Reference(authors = "P. Ciaccia, M. Patella, P. Zezula", title = "M-tree: An Efficient Access Method for Similarity Search in Metric Spaces", booktitle = "VLDB'97, Proceedings of 23rd International Conference on Very Large Data Bases, August 25-29, 1997, Athens, Greece", url = "http://www.vldb.org/conf/1997/P426.PDF") +public class RandomSplit<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry> extends MTreeSplit<O, D, N, E> { + /** + * Random generator. + */ + private Random random; + + /** + * Creates a new split object. + */ + public RandomSplit(RandomFactory rnd) { + super(); + this.random = rnd.getRandom(); + } + + /** + * Selects two objects of the specified node to be promoted and stored into + * the parent node. The m-RAD strategy considers all possible pairs of objects + * and, after partitioning the set of entries, promotes the pair of objects + * for which the sum of covering radiuses is minimum. + * + * @param tree Tree to use + * @param node the node to be split + */ + @Override + public Assignments<E> split(AbstractMTree<O, D, N, E, ?> tree, N node) { + int pos1 = random.nextInt(node.getNumEntries()); + int pos2 = random.nextInt(node.getNumEntries() - 1); + if (pos2 >= pos1) { + ++pos2; + } + DBID id1 = node.getEntry(pos1).getRoutingObjectID(); + DBID id2 = node.getEntry(pos2).getRoutingObjectID(); + + return balancedPartition(tree, node, id1, id2); + } + + /** + * Parameterization class. + * + * @author Erich Schubert + * + * @apiviz.exclude + * + * @param <O> the type of DatabaseObject to be stored in the M-Tree + * @param <D> the type of Distance used in the M-Tree + * @param <N> the type of AbstractMTreeNode used in the M-Tree + * @param <E> the type of MetricalEntry used in the M-Tree + */ + public static class Parameterizer<O, D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<O, D, N, E>, E extends MTreeEntry> extends AbstractParameterizer { + /** + * Option ID for the random generator. + */ + public static final OptionID RANDOM_ID = new OptionID("mtree.randomsplit.random", "Random generator / seed for the randomized split."); + + /** + * Random generator + */ + RandomFactory rnd = RandomFactory.DEFAULT; + + @Override + protected void makeOptions(Parameterization config) { + super.makeOptions(config); + RandomParameter rndP = new RandomParameter(RANDOM_ID); + if (config.grab(rndP)) { + rnd = rndP.getValue(); + } + } + + @Override + protected RandomSplit<O, D, N, E> makeInstance() { + return new RandomSplit<>(rnd); + } + } +} diff --git a/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/package-info.java b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/package-info.java new file mode 100644 index 00000000..ed0fd729 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/index/tree/metrical/mtreevariants/strategies/split/package-info.java @@ -0,0 +1,26 @@ +/** + * <p>Splitting strategies of nodes in an M-Tree (and variants).</p> + */ +/* + This file is part of ELKI: + Environment for Developing KDD-Applications Supported by Index-Structures + + Copyright (C) 2013 + 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/>. + */ +package de.lmu.ifi.dbs.elki.index.tree.metrical.mtreevariants.strategies.split;
\ No newline at end of file |