source: tags/MetisMQIDemo/src/main/java/weka/core/neighboursearch/balltrees/TopDownConstructor.java

Last change on this file was 29, checked in by gnappo, 15 years ago

Taggata versione per la demo e aggiunto branch.

File size: 12.3 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 * TopDownConstructor.java
19 * Copyright (C) 2007 University of Waikato, Hamilton, New Zealand
20 */
21
22package weka.core.neighboursearch.balltrees;
23
24import weka.core.EuclideanDistance;
25import weka.core.Instance;
26import weka.core.Option;
27import weka.core.RevisionUtils;
28import weka.core.TechnicalInformation;
29import weka.core.TechnicalInformationHandler;
30import weka.core.Utils;
31import weka.core.TechnicalInformation.Field;
32import weka.core.TechnicalInformation.Type;
33import weka.core.neighboursearch.balltrees.BallNode;
34
35import java.util.Enumeration;
36import java.util.Vector;
37
38/**
39 <!-- globalinfo-start -->
40 * The class implementing the TopDown construction method of ball trees. It further uses one of a number of different splitting methods to split a ball while constructing the tree top down.<br/>
41 * <br/>
42 * For more information see also:<br/>
43 * <br/>
44 * Stephen M. Omohundro (1989). Five Balltree Construction Algorithms.
45 * <p/>
46 <!-- globalinfo-end -->
47 *
48 <!-- technical-bibtex-start -->
49 * BibTeX:
50 * <pre>
51 * &#64;techreport{Omohundro1989,
52 *    author = {Stephen M. Omohundro},
53 *    institution = {International Computer Science Institute},
54 *    month = {December},
55 *    number = {TR-89-063},
56 *    title = {Five Balltree Construction Algorithms},
57 *    year = {1989}
58 * }
59 * </pre>
60 * <p/>
61 <!-- technical-bibtex-end -->
62 *
63 <!-- options-start -->
64 * Valid options are: <p/>
65 *
66 * <pre> -S &lt;classname and options&gt;
67 *  Ball splitting algorithm to use.</pre>
68 *
69 <!-- options-end -->
70 *
71 * @author Ashraf M. Kibriya (amk14[at-the-rate]cs[dot]waikato[dot]ac[dot]nz)
72 * @version $Revision: 5953 $
73 */
74public class TopDownConstructor
75  extends BallTreeConstructor
76  implements TechnicalInformationHandler {
77 
78  /** for serialization. */
79  private static final long serialVersionUID = -5150140645091889979L;
80 
81  /**
82   * The BallSplitter algorithm used by the TopDown BallTree constructor, if it
83   * is selected.
84   */
85  protected BallSplitter m_Splitter = new PointsClosestToFurthestChildren();
86
87  /**
88   * Creates a new instance of TopDownConstructor.
89   */
90  public TopDownConstructor() {
91  }
92
93  /**
94   * Returns a string describing this nearest neighbour search algorithm.
95   *
96   * @return            a description of the algorithm for displaying in the
97   *                    explorer/experimenter gui
98   */
99  public String globalInfo() {
100    return 
101        "The class implementing the TopDown construction method of "
102      + "ball trees. It further uses one of a number of different splitting "
103      + "methods to split a ball while constructing the tree top down.\n\n"
104      + "For more information see also:\n\n"
105      + getTechnicalInformation().toString();
106  }
107 
108  /**
109   * Returns an instance of a TechnicalInformation object, containing detailed
110   * information about the technical background of this class, e.g., paper
111   * reference or book this class is based on.
112   *
113   * @return            the technical information about this class
114   */
115  public TechnicalInformation getTechnicalInformation() {
116    TechnicalInformation result;
117   
118    result = new TechnicalInformation(Type.TECHREPORT);
119    result.setValue(Field.AUTHOR, "Stephen M. Omohundro");
120    result.setValue(Field.YEAR, "1989");
121    result.setValue(Field.TITLE, "Five Balltree Construction Algorithms");
122    result.setValue(Field.MONTH, "December");
123    result.setValue(Field.NUMBER, "TR-89-063");
124    result.setValue(Field.INSTITUTION, "International Computer Science Institute");
125
126    return result;
127  }
128
129  /**
130   * Builds the ball tree top down.
131   * @return The root node of the tree.
132   * @throws Exception If there is problem building
133   * the tree.
134   */
135  public BallNode buildTree() throws Exception {
136    BallNode root;
137   
138    m_NumNodes = m_MaxDepth = 0;
139    m_NumLeaves = 1;
140   
141    m_Splitter.setInstances(m_Instances);
142    m_Splitter.setInstanceList(m_InstList);
143    m_Splitter.
144    setEuclideanDistanceFunction((EuclideanDistance)m_DistanceFunction);
145   
146    root = new BallNode(0, m_InstList.length-1, 0);
147    root.setPivot(BallNode.calcCentroidPivot(m_InstList, m_Instances));
148    root.setRadius(BallNode.calcRadius(m_InstList, m_Instances, root.getPivot(), m_DistanceFunction));
149   
150    splitNodes(root, m_MaxDepth+1, root.m_Radius);
151   
152    return root; 
153  }
154   
155  /**
156   * Recursively splits nodes of a ball tree until
157   * <=m_MaxInstancesInLeaf instances remain in a node.
158   * @param node The node to split.
159   * @param depth The depth of this node in the tree,
160   * so that m_MaxDepth is correctly updated.
161   * @param rootRadius The smallest ball enclosing all
162   * the data points.
163   * @throws Exception If there is some problem in
164   * splitting.
165   */
166  protected void splitNodes(BallNode node, int depth, final double rootRadius) throws Exception {
167   
168    if(node.m_NumInstances <= m_MaxInstancesInLeaf || 
169       (rootRadius==0 ? true : node.m_Radius/rootRadius < m_MaxRelLeafRadius))
170      return;
171   
172    m_NumLeaves--;
173    m_Splitter.splitNode(node, m_NumNodes);
174    m_NumNodes += 2;
175    m_NumLeaves += 2;
176   
177    if(m_MaxDepth < depth)
178      m_MaxDepth = depth;
179 
180    splitNodes(node.m_Left, depth+1, rootRadius);
181    splitNodes(node.m_Right, depth+1, rootRadius);
182   
183    if(m_FullyContainChildBalls) {
184      double radius = BallNode.calcRadius(node.m_Left, node.m_Right, 
185                                         node.getPivot(), m_DistanceFunction);
186      Instance pivot = BallNode.calcPivot(node.m_Left, node.m_Right, m_Instances);
187//      System.err.println("Left Radius: "+node.m_Left.getRadius()+
188//                         " Right Radius: "+node.m_Right.getRadius()+
189//                         " d(p1,p2): "+
190//                         m_DistanceFunction.distance(node.m_Left.getPivot(), node.m_Right.getPivot())+
191//                         " node's old radius: "+node.getRadius()+
192//                         " node's new Radius: "+radius+
193//                         " node;s old pivot: "+node.getPivot()+
194//                         " node's new pivot: "+pivot);
195      node.setRadius(radius);
196    }   
197  }
198   
199  /**
200   * Adds an instance to the ball tree.
201   * @param node The root node of the tree.
202   * @param inst The instance to add to the tree.
203   * @return The new master index array after adding the
204   * instance.
205   * @throws Exception If there is some problem adding the
206   * given instance to the tree.
207   */
208  public int[] addInstance(BallNode node, Instance inst) throws Exception {
209   
210    double leftDist, rightDist;
211   
212    if (node.m_Left!=null && node.m_Right!=null) { //if node is not a leaf     
213      // go further down the tree to look for the leaf the instance should be in
214     
215      leftDist = m_DistanceFunction.distance(inst, node.m_Left.getPivot(), 
216                                    Double.POSITIVE_INFINITY); //instance.value(m_SplitDim);
217      rightDist = m_DistanceFunction.distance(inst, node.m_Right.getPivot(), 
218                                    Double.POSITIVE_INFINITY); 
219      if (leftDist < rightDist) {
220        addInstance(node.m_Left, inst);
221        // go into right branch to correct instance list boundaries
222        processNodesAfterAddInstance(node.m_Right);
223      }
224      else {
225        addInstance(node.m_Right, inst);
226      }
227      // correct end index of instance list of this node
228      node.m_End++;
229    }
230    else if(node.m_Left!=null || node.m_Right!=null) {
231      throw new Exception("Error: Only one leaf of the built ball tree is " +
232                          "assigned. Please check code.");
233    }
234    else { // found the leaf to insert instance
235           
236      int index = m_Instances.numInstances() - 1;
237     
238      int instList[] = new int[m_Instances.numInstances()];
239      System.arraycopy(m_InstList, 0, instList, 0, node.m_End+1);
240      if(node.m_End < m_InstList.length-1)
241        System.arraycopy(m_InstList, node.m_End+2, instList, node.m_End+2, m_InstList.length-node.m_End-1);     
242      instList[node.m_End+1] = index;
243      node.m_End++;
244      node.m_NumInstances++;
245      m_InstList = instList;
246     
247      m_Splitter.setInstanceList(m_InstList);
248     
249      if(node.m_NumInstances > m_MaxInstancesInLeaf) {
250        m_Splitter.splitNode(node, m_NumNodes);
251        m_NumNodes += 2;
252      }
253    }
254    return m_InstList;
255  }
256 
257  /**
258   * Post process method to correct the start and end
259   * indices of BallNodes on the right of the
260   * node where the instance was added. 
261   * @param node The node whose m_Start and m_End
262   * need to be updated.
263   */
264  protected void processNodesAfterAddInstance(BallNode node) {
265    //updating start and end indices for the node
266    node.m_Start++;
267    node.m_End++;   
268    //processing child nodes
269    if(node.m_Left!=null && node.m_Right!=null) {
270      processNodesAfterAddInstance(node.m_Left);
271      processNodesAfterAddInstance(node.m_Right);
272    }
273  }
274 
275  /**
276   * Returns the tip text for this property.
277   *
278   * @return            tip text for this property suitable for
279   *                    displaying in the explorer/experimenter gui
280   */
281  public String ballSplitterTipText() {
282    return 
283        "The BallSplitter algorithm set that would be used by the TopDown "
284      + "BallTree constructor.";
285  }
286 
287  /**
288   * Returns the BallSplitter algorithm set that would be
289   * used by the TopDown BallTree constructor.
290   * @return The BallSplitter currently in use.
291   */
292  public BallSplitter getBallSplitter() {
293    return m_Splitter;
294  }
295 
296  /**
297   * Sets the ball splitting algorithm to be used by the
298   * TopDown constructor.
299   * @param splitter The BallSplitter to use.
300   */
301  public void setBallSplitter(BallSplitter splitter) {
302    m_Splitter = splitter;
303  }
304 
305  /**
306   * Returns an enumeration describing the available options.
307   *
308   * @return            an enumeration of all the available options.
309   */
310  public Enumeration listOptions() {
311    Vector<Option> newVector = new Vector<Option>();
312
313    newVector.addElement(new Option(
314        "\tBall splitting algorithm to use.",
315        "S", 1, "-S <classname and options>"));
316   
317    return newVector.elements();
318  }
319
320  /**
321   * Parses a given list of options.
322   *
323   <!-- options-start -->
324   * Valid options are: <p/>
325   *
326   * <pre> -S &lt;classname and options&gt;
327   *  Ball splitting algorithm to use.</pre>
328   *
329   <!-- options-end -->
330   *
331   * @param options     the list of options as an array of strings
332   * @throws Exception  if an option is not supported
333   */
334  public void setOptions(String[] options)
335    throws Exception {
336
337    super.setOptions(options);
338   
339    String optionString = Utils.getOption('S', options);
340    if(optionString.length() != 0) {
341      String nnSearchClassSpec[] = Utils.splitOptions(optionString);
342      if(nnSearchClassSpec.length == 0) { 
343        throw new Exception("Invalid BallSplitter specification string."); 
344      }
345      String className = nnSearchClassSpec[0];
346      nnSearchClassSpec[0] = "";
347
348      setBallSplitter( (BallSplitter)
349                            Utils.forName( BallSplitter.class, 
350                                           className, nnSearchClassSpec) );
351    }
352    else {
353      setBallSplitter(new PointsClosestToFurthestChildren()); 
354    }
355  }
356
357  /**
358   * Gets the current settings of KDtree.
359   *
360   * @return            an array of strings suitable for passing to setOptions
361   */
362  public String[] getOptions() {
363    Vector<String>      result;
364    String[]            options;
365    int                 i;
366   
367    result = new Vector<String>();
368   
369    options = super.getOptions();
370    for (i = 0; i < options.length; i++)
371      result.add(options[i]);
372   
373    result.add("-S");
374    result.add(m_Splitter.getClass().getName());
375
376    return result.toArray(new String[result.size()]);
377  }
378 
379  /**
380   * Returns the revision string.
381   *
382   * @return            the revision
383   */
384  public String getRevision() {
385    return RevisionUtils.extract("$Revision: 5953 $");
386  }
387}
Note: See TracBrowser for help on using the repository browser.