package tr.gov.ulakbim.jDenetX.clusterers.clustree; //import static OldStructureConstants; /** * A representation of a Node in the <code>Tree</code> * * @author Fernando Sanchez Villaamil * @see Tree */ public class Node { final int NUMBER_ENTRIES = 3; static int INSERTIONS_BETWEEN_CLEANUPS = 10000; /** * The children of this node. */ private Entry[] entries; // Information about the state in the tree. /** * The depth at which this <code>Node</code> is in the tree. */ private int level; /** * Initialze a normal node, which is not fake. * * @param numberDimensions The dimensionality of the data points it * manipulates. * @param level The INVERSE level at which this node hangs. */ protected Node(int numberDimensions, int level) { this.level = level; this.entries = new Entry[NUMBER_ENTRIES]; // Generate all entries when we generate a Node. // That no entry can be null makes it much easier to handle. for (int i = 0; i < NUMBER_ENTRIES; i++) { entries[i] = new Entry(numberDimensions); } } /** * Initialiazes a node which is a fake root depending on the given * <code>boolean</code>. * * @param numberDimensions The dimensionality of the data points it * manipulates. * @param level The level at which this node hangs. * @param fakeRoot A parameter the says if the node is to be fake or not. */ protected Node(int numberDimensions, int numberClasses, int level, boolean fakeRoot) { this.level = level; this.entries = new Entry[NUMBER_ENTRIES]; // Generate all entries when we generate a Node. // That no entry can be null makes it much easier to handle. for (int i = 0; i < NUMBER_ENTRIES; i++) { entries[i] = new Entry(numberDimensions); } } /** * Checks if this node is a leaf. A node is a leaf when none of the entries * in the node have children. * * @return <code>true</code> if the node is leaf, <code>false</code> * otherwise. */ protected boolean isLeaf() { for (int i = 0; i < entries.length; i++) { Entry entry = entries[i]; if (entry.getChild() != null) { return false; } } return true; } /** * Returns the neareast <code>Entry</code> to the given <code>Cluster</code>. * The distance is minimized over <code>Entry.calcDistance(Cluster)</code>. * * @param buffer The cluster to which the distance has to be compared. * @return The <code>Entry</code> with minimal distance to the given * cluster. * @throws EmptyNodeException This Exception is thrown when this function is * called on an empty node. * @see Kernel * @see Entry#calcDistance(tree.Kernel) */ protected Entry nearestEntry(ClusKernel buffer) { // TODO: (Fernando) Adapt the nearestEntry(...) function to the new algorithmn. Entry res = entries[0]; double min = res.calcDistance(buffer); for (int i = 1; i < entries.length; i++) { Entry entry = entries[i]; if (entry.isEmpty()) { break; } double distance = entry.calcDistance(buffer); if (distance < min) { min = distance; res = entry; } } return res; } /** * Return the nearest entry to the given one. The * <code>calcDistance(Entry)</code> function is find the one with the * shortest distance in this node to the given one. * * @param newEntry The entry to which the entry with the minimal distance * to it is calculated. * @return The entry with the minimal distance to the given one. */ protected Entry nearestEntry(Entry newEntry) { assert (!this.entries[0].isEmpty()); Entry res = entries[0]; double min = res.calcDistance(newEntry); for (int i = 1; i < entries.length; i++) { if (this.entries[i].isEmpty()) { break; } Entry entry = entries[i]; double distance = entry.calcDistance(newEntry); if (distance < min) { min = distance; res = entry; } } return res; } /** * Return the number of free <code>Entry</code>s in this node. * * @return The number of free <code>Entry</code>s in this node. * @see Entry */ protected int numFreeEntries() { int res = 0; for (int i = 0; i < entries.length; i++) { Entry entry = entries[i]; if (entry.isEmpty()) { res++; } } assert (NUMBER_ENTRIES == entries.length); return res; } /** * Add a new <code>Entry</code> to this node. If there is no space left a * <code>NoFreeEntryException</code> is thrown. * * @param newEntry The <code>Entry</code> to be added. * @throws NoFreeEntryException Is thrown when there is no space left in * the node for the new entry. */ protected void addEntry(Entry newEntry, long currentTime) { int freePosition = getNextEmptyPosition(); entries[freePosition].initializeEntry(newEntry, currentTime); } /** * Returns the position of the next free Entry. * * @return The position of the next free Entry. * @throws NoFreeEntryException Is thrown when there is no free entry left in * the node. */ private int getNextEmptyPosition() { int counter; for (counter = 0; counter < entries.length; counter++) { Entry e = entries[counter]; if (e.isEmpty()) { break; } } if (counter == entries.length) { throw new RuntimeException("Entry added to a node which is already full."); } return counter; } /** * If there exists an entry, whose relevance is under the threshold given * as a parameter to the tree, this entry is returned. Otherwise * <code>null</code> is returned. * * @return An irrelevant <code>Entry</code> if there exists one, * <code>null</code> otherwise. * @see Entry * @see Entry#isIrrelevant(double) */ protected Entry getIrrelevantEntry(double threshold) { for (int i = 0; i < this.entries.length; i++) { Entry entry = this.entries[i]; if (entry.isIrrelevant(threshold)) { return entry; } } return null; } /** * Return an array with references to the children of this node. These are * not copies, that means side effects are possible! * * @return An array with references to the children of this node. * @see Entry */ protected Entry[] getEntries() { return entries; } /** * Return the level number in the node. This is not the real level. For the * real level one has to call <code>getLevel(Tree tree)</code>. * * @return The raw level of the node. * @see #getLevel(tree.Tree) */ protected int getRawLevel() { return level; } /** * Returns the level at which this node is in the tree. If a tree is passed * to which the node does not belonged a value is returned, but it is * gibberish. * * @param tree The tree to which this node belongs. * @return The level at which this node hangs. */ protected int getLevel(ClusTree tree) { int numRootSplits = tree.getNumRootSplits(); return numRootSplits - this.getRawLevel(); } /** * Clear this Node, which means that the noiseBuffer is cleared, that * <code>shallowClear</code> is called upon all the entries of the node, * that the split counter is set to zero and the node is set to not be a * fake root. Notice that the level stays the same after calling this * function. * * @see Kernel#clear() * @see Entry#shallowClear() */ // Level stays the same. protected void clear() { for (int i = 0; i < NUMBER_ENTRIES; i++) { entries[i].shallowClear(); } } /** * Merge the two entries at the given position. The entries are reordered in * the <code>entries</code> array so that the non-empty entries are still * at the beginning. * * @param pos1 The position of the first entry to be merged. This position * has to be smaller than the the second position. * @param pos2 The position of the second entry to be merged. This position * has to be greater than the the first position. */ protected void mergeEntries(int pos1, int pos2) { assert (this.numFreeEntries() == 0); assert (pos1 < pos2); this.entries[pos1].mergeWith(this.entries[pos2]); System.arraycopy(entries, 0, entries, 1, entries.length - 1); entries[entries.length - 1].clear(); } protected void makeOlder(long currentTime, double negLambda) { for (int i = 0; i < this.entries.length; i++) { Entry entry = this.entries[i]; if (entry.isEmpty()) { break; } entry.makeOlder(currentTime, negLambda); } } }