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) 2014 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ import java.util.logging.Logger; import de.lmu.ifi.dbs.elki.database.ids.DBID; import de.lmu.ifi.dbs.elki.index.tree.AbstractNode; import de.lmu.ifi.dbs.elki.logging.LoggingConfiguration; /** * Abstract super class for nodes in M-Tree variants. * * @author Elke Achtert * * @apiviz.has MTreeEntry oneway - - contains * @apiviz.excludeSubtypes * * @param the type of DatabaseObject to be stored in the M-Tree * @param the type of AbstractMTreeNode used in the M-Tree * @param the type of MetricalEntry used in the M-Tree */ public abstract class AbstractMTreeNode, E extends MTreeEntry> extends AbstractNode { /** * Empty constructor for Externalizable interface. */ public AbstractMTreeNode() { // empty constructor } /** * Creates a new MTreeNode with the specified parameters. * * @param capacity the capacity (maximum number of entries plus 1 for * overflow) of this node * @param isLeaf indicates whether this node is a leaf node * @param eclass Entry class, to initialize array storage */ public AbstractMTreeNode(int capacity, boolean isLeaf, Class eclass) { super(capacity, isLeaf, eclass); } /** * Adjusts the parameters of the entry representing this node (e.g. after * insertion of new objects). Subclasses may need to overwrite this method. * * @param entry the entry representing this node * @param routingObjectID the id of the (new) routing object of this node * @param parentDistance the distance from the routing object of this node to * the routing object of the parent node * @param mTree the M-Tree object holding this node */ public void adjustEntry(E entry, DBID routingObjectID, double parentDistance, AbstractMTree mTree) { entry.setRoutingObjectID(routingObjectID); entry.setParentDistance(parentDistance); entry.setCoveringRadius(coveringRadius(entry.getRoutingObjectID(), mTree)); for (int i = 0; i < getNumEntries(); i++) { E childEntry = getEntry(i); double dist = mTree.distance(routingObjectID, childEntry.getRoutingObjectID()); childEntry.setParentDistance(dist); } } /** * Determines and returns the covering radius of this node. * * @param routingObjectID the object id of the routing object of this node * @param mTree the M-Tree * @return the covering radius of this node */ public double coveringRadius(DBID routingObjectID, AbstractMTree mTree) { double coveringRadius = 0.; for (int i = 0; i < getNumEntries(); i++) { E entry = getEntry(i); double distance = mTree.distance(entry.getRoutingObjectID(), routingObjectID) + entry.getCoveringRadius(); coveringRadius = Math.max(coveringRadius, distance); } return coveringRadius; } /** * Tests this node (for debugging purposes). * * @param mTree the M-Tree holding this node * @param entry the entry representing this node */ @SuppressWarnings("unchecked") public final void integrityCheck(AbstractMTree mTree, E entry) { // leaf node if (isLeaf()) { for (int i = 0; i < getCapacity(); i++) { E e = getEntry(i); if (i < getNumEntries() && e == null) { throw new RuntimeException("i < numEntries && entry == 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++) { E e = getEntry(i); if (i < getNumEntries() && e == null) { throw new RuntimeException("i < numEntries && entry == null"); } if (i >= getNumEntries() && e != null) { throw new RuntimeException("i >= numEntries && entry != null"); } if (e != null) { N node = mTree.getNode(e); 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()) { throw new RuntimeException("Wrong Child: child id no leaf, but node is leaf!"); } // noinspection unchecked node.integrityCheckParameters(entry, (N) this, i, mTree); node.integrityCheck(mTree, e); } } if (LoggingConfiguration.DEBUG) { Logger.getLogger(this.getClass().getName()).fine("DirNode " + getPageID() + " ok!"); } } } /** * Tests, if the parameters of the entry representing this node, are correctly * set. Subclasses may need to overwrite this method. * * @param parentEntry the entry representing the parent * @param parent the parent holding the entry representing this node * @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 mTree) { // test if parent distance is correctly set E entry = parent.getEntry(index); double parentDistance = mTree.distance(entry.getRoutingObjectID(), parentEntry.getRoutingObjectID()); 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 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); } } } }