/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package tr.gov.ulakbim.jDenetX.clusterers.clustree; /** * A representation of an entry in the tree. * * @author sanchez */ public class Entry { /** * The actual entry data. */ private ClusKernel data; /** * The buffer of this entry. It can also be seen as the buffer of the child * node, it is here just to simplify the insertion recuersion. */ private ClusKernel buffer; /** * A reference to the next node in the tree. <code>null</code> if we are * at a leaf, or this is an entry is part of a lying <code>Node</code>. */ private Node child; /** * Last time this entry was changed. */ private long timestamp; /** * The timestamp to be used when no operation has yet been done on this * entry. * * @see #timestamp */ private static final long defaultTimestamp = 0; /** * Constructor for the entry. To be used when we want to create an empty * entry. Notice that the timestamp will be set to zero, since there is no * reason to know when an empty entry was generated. * * @param numberDimensions The dimensionality of the data point in tree * where this entry is used. */ public Entry(int numberDimensions) { this.data = new ClusKernel(numberDimensions); this.buffer = new ClusKernel(numberDimensions); this.child = null; this.timestamp = Entry.defaultTimestamp; } /** * Constructor that creates an <code>Entry</code> that points to the given * node. The values of <code>data</code> will be calculated for this. * * @param numberDimensions The dimensionality of the node. * @param node The node to which the new <code>Entry</code> should point. * @param currentTime The timestamp for the moment where this Entry was * was generated. * @see Node * @see #data */ protected Entry(int numberDimensions, Node node, long currentTime) { this(numberDimensions); this.child = node; Entry[] entries = node.getEntries(); for (int i = 0; i < entries.length; i++) { Entry entry = entries[i]; if (entry.isEmpty()) { break; } this.add(entry); } this.timestamp = currentTime; } /** * Constructuctor that creates an <code>Entry</code> with an empty buffer * and the <code>data</code> given by the <code>Kernel</code>. * * @param numberDimensions The dimensionality of the information in the * cluster. * @param cluster The cluster from which the information is to be extracted. * @param currentTime The timestamp for the moment where this Entry was * was generated. * @see Kernel * @see #data */ protected Entry(int numberDimensions, ClusKernel cluster, long currentTime) { this(numberDimensions); this.data.add(cluster); this.timestamp = currentTime; } /** * Copy constructor. Everythin is copied, including the child. * * @param other */ protected Entry(Entry other) { this.buffer = new ClusKernel(other.buffer); this.data = new ClusKernel(other.data); this.timestamp = other.timestamp; this.child = other.child; } /** * Clear the Entry. All points in the buffer and in the data cluster are * lost, the connection to the child is lost and the timestamp is set to * the default value. */ protected void clear() { this.data.clear(); this.buffer.clear(); this.child = null; this.timestamp = Entry.defaultTimestamp; } /** * Clear the <code>data</code> and the <code>buffer Custer</code> in this * entry. This function does not clear the child of this <code>Entry</code>. * * @see #data * @see #buffer * @see Kernel */ protected void shallowClear() { this.buffer.clear(); this.data.clear(); } /** * Calculates the distance to the data in this entry. * * @param cluster The Kernel cluster to which the distance is to be * calculated. * @return The distance to the data <code>Kernel</code> in this * <code>Entry</code> * @see Kernel * @see #data */ protected double calcDistance(ClusKernel cluster) { return data.calcDistance(cluster); } /** * Calculates the distance to the data in this entry of the data in the * given entry. * * @param other The <code>Entry</code> to which the distance is to be * calculated. * @return The distance to the data <code>Kernel</code> in this * <code>Entry</code> of the data <code>Kernel</code> in the other * <code>Entry</code>. * @see Kernel * @see #data */ protected double calcDistance(Entry other) { return this.getData().calcDistance(other.getData()); } /** * When this entry is empty, give it it's first values. It makes sense to * have this operation separated from the aggregation, because the * aggregation first weights the values in <code>data</code> and * <code>Kernel</code>, which makes no sense in an empty entry. * * @param other The entry with the information to be used to initialize * this entry. * @param currentTime The time at which this is happening. */ protected void initializeEntry(Entry other, long currentTime) { assert (this.isEmpty()); assert (other.getBuffer().isEmpty()); this.data.add(other.data); this.timestamp = currentTime; this.child = other.child; } /** * Add the data cluster of another entry to the data cluster of this entry. * By using this function the timestamp does not get updated, nor does this * entry get older. * * @param other The entry of which the data cluster should be added to * the local data cluster. * @see #data * @see Kernel#add(tree.Kernel) */ protected void add(Entry other) { this.data.add(other.data); } /** * Aggregate the <code>data</code> in the <code>Kernel</code> of the other * <code>Entry</code>. * * @param other The <code>Entry</code> to be aggregated. * @see #data * @see Kernel */ protected void aggregateEntry(Entry other, long currentTime, double negLambda) { this.data.aggregate(other.data, currentTime - this.timestamp, negLambda); this.timestamp = currentTime; } /** * Aggregate the given <code>Kernel</code> to the <code>data</code> cluster * of this entry. * * @param otherData The <code>Entry</code> to be aggregated. * @see #data * @see Kernel */ protected void aggregateCluster(ClusKernel otherData, long currentTime, double negLambda) { this.getData().aggregate(otherData, currentTime - this.timestamp, negLambda); this.timestamp = currentTime; } /** * Aggregate the given <code>Kernel</code> to the <code>buffer</code> * cluster of this entry. * * @param pointToInsert The cluster to aggregate to the buffer. * @param currentTime The time at which the aggregation occurs. * @param negLambda A parameter needed to weight the current state of the * buffer. */ protected void aggregateToBuffer(ClusKernel pointToInsert, long currentTime, double negLambda) { ClusKernel currentBuffer = this.getBuffer(); currentBuffer.aggregate(pointToInsert, currentTime - this.timestamp, negLambda); this.timestamp = currentTime; } /** * Merge this entry witht the given <code>Entry</code>. This adds the data * cluster of the given Entry to the data cluster of this entry and sets the * timestamp to the newest one of the the two entries. * * @param other The entry from which the data cluster is added. * @see Kernel#add(tree.Kernel) */ protected void mergeWith(Entry other) { // We should only merge entries in leafs, and leafes should have empty // buffers. assert (this.child == null); assert (other.child == null); assert (other.buffer.isEmpty()); this.data.add(other.data); if (this.timestamp < other.timestamp) { this.timestamp = other.timestamp; } } /** * Getter for the buffer. It is the real object, that means side effects are * possible! * * @return A reference to the buffer in this entry. */ protected ClusKernel getBuffer() { return buffer; } /** * Return the reference to the child of this <code>Entry</code> to navigate * in the tree. * * @return A reference to the child of this <code>Entry</code> */ protected Node getChild() { return child; } /** * Getter for the data. It is the real object, that means side effects are * possible! * * @return A reference to the data <code>Kernel</code> in this entry. * @see Kernel */ protected ClusKernel getData() { return data; } /** * Setter for the child in this entry. Use to build the tree. * * @param child The <code>Node</code> that should be a child of this * <code>Entry</code> * @see Node */ protected void setChild(Node child) { this.child = child; } /** * Return the current timestamp. * * @return The current timestamp. */ public long getTimestamp() { return timestamp; } /** * Clear the buffer in this entry and return a copy. No side effects are * possible (given that the copy constructor of <code>Kernel</code> makes * a deep copy). * * @return A copy of the buffer. */ protected ClusKernel emptyBuffer(long currentTime, double negLambda) { this.buffer.makeOlder(currentTime - this.timestamp, negLambda); ClusKernel bufferCopy = new ClusKernel(this.buffer); this.buffer.clear(); return bufferCopy; } /** * Check if this <code>Entry</code> is empty or not. An <code>Entry</code> * is empty if the <code>data Kernel</code> is empty, since then the buffer * has to be empty. * * @return <code>true</code> if the data cluster has no data points, * <code>false</code> otherwise. */ protected boolean isEmpty() { // Assert that if the data cluster is empty, the buffer cluster is // empty too. assert ((this.data.isEmpty() && this.buffer.isEmpty()) || !this.data.isEmpty()); return this.data.isEmpty(); } /** * Overwrites the LS, SS and weightedN in the data cluster of this * <code>Entry</code> to the values of the data cluster in the given * <code>Entry</code>, but adds N and classCount of the cluster in the given * Entry to the data cluster in this one. This function is useful when the * weight of an entry becomes to small, and we want to forget the * information of the old points. * * @param newEntry The cluster that should overwrite the information. */ protected void overwriteOldEntry(Entry newEntry) { assert (this.getBuffer().isEmpty()); assert (newEntry.getBuffer().isEmpty()); this.data.overwriteOldCluster(newEntry.data); } /** * This functions reads every entry in the child node and calculates the * corresponding <code>data Kernel</code>. Timestamps are not changed. * * @see #data * @see Kernel */ protected void recalculateData() { Node currentChild = this.getChild(); if (currentChild != null) { ClusKernel currentData = this.getData(); currentData.clear(); Entry[] entries = currentChild.getEntries(); for (int i = 0; i < entries.length; i++) { currentData.add(entries[i].getData()); } } else { this.clear(); } } /** * Returns true if this entry is irrelevant with respecto the given * threshold. This is done by comparing the weighted N of this Entry to * the threshold, if it is smaller, than the entry is deemed to be * irrelevant. * * @param threshold The threshold under which entries at leafs can be * erased. * @return True if this entry is deemed irrelevant, false otherwise. */ protected boolean isIrrelevant(double threshold) { return this.getData().getWeightedN() < threshold; } /** * Ages this entrie's data AND buffer according to the given * time and aging constant. * * @param currentTime the current time * @param negLambda the aging constant */ protected void makeOlder(long currentTime, double negLambda) { assert (currentTime > this.timestamp) : "currentTime : " + currentTime + ", this.timestamp: " + this.timestamp; long diff = currentTime - this.timestamp; this.buffer.makeOlder(diff, negLambda); this.data.makeOlder(diff, negLambda); this.timestamp = currentTime; } }