All Downloads are FREE. Search and download functionalities are using the official Maven repository.

weka.core.neighboursearch.balltrees.MiddleOutConstructor Maven / Gradle / Ivy

Go to download

The Waikato Environment for Knowledge Analysis (WEKA), a machine learning workbench. This version represents the developer version, the "bleeding edge" of development, you could say. New functionality gets added to this version.

There is a newer version: 3.9.6
Show newest version
/*
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see .
 */

/*
 * MiddleOutConstructor.java
 * Copyright (C) 2007-2012 University of Waikato, Hamilton, New Zealand
 */

package weka.core.neighboursearch.balltrees;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Random;
import java.util.Vector;

import weka.core.DenseInstance;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.Option;
import weka.core.Randomizable;
import weka.core.RevisionHandler;
import weka.core.RevisionUtils;
import weka.core.TechnicalInformation;
import weka.core.TechnicalInformation.Field;
import weka.core.TechnicalInformation.Type;
import weka.core.TechnicalInformationHandler;
import weka.core.Utils;

/**
 *  The class that builds a BallTree middle out.
*
* For more information see also:
*
* Andrew W. Moore: The Anchors Hierarchy: Using the Triangle Inequality to * Survive High Dimensional Data. In: UAI '00: Proceedings of the 16th * Conference on Uncertainty in Artificial Intelligence, San Francisco, CA, USA, * 397-405, 2000.
*
* Ashraf Masood Kibriya (2007). Fast Algorithms for Nearest Neighbour Search. * Hamilton, New Zealand. *

* * * BibTeX: * *

 * @inproceedings{Moore2000,
 *    address = {San Francisco, CA, USA},
 *    author = {Andrew W. Moore},
 *    booktitle = {UAI '00: Proceedings of the 16th Conference on Uncertainty in Artificial Intelligence},
 *    pages = {397-405},
 *    publisher = {Morgan Kaufmann Publishers Inc.},
 *    title = {The Anchors Hierarchy: Using the Triangle Inequality to Survive High Dimensional Data},
 *    year = {2000}
 * }
 * 
 * @mastersthesis{Kibriya2007,
 *    address = {Hamilton, New Zealand},
 *    author = {Ashraf Masood Kibriya},
 *    school = {Department of Computer Science, School of Computing and Mathematical Sciences, University of Waikato},
 *    title = {Fast Algorithms for Nearest Neighbour Search},
 *    year = {2007}
 * }
 * 
*

* * * Valid options are: *

* *

 * -S <num>
 *  The seed for the random number generator used
 *  in selecting random anchor.
 * (default: 1)
 * 
* *
 * -R
 *  Use randomly chosen initial anchors.
 * 
* * * * @author Ashraf M. Kibriya (amk14[at-the-rate]cs[dot]waikato[dot]ac[dot]nz) * @version $Revision: 11269 $ */ public class MiddleOutConstructor extends BallTreeConstructor implements Randomizable, TechnicalInformationHandler { /** for serialization. */ private static final long serialVersionUID = -8523314263062524462L; /** Seed form random number generator. */ protected int m_RSeed = 1; /** * The random number generator for selecting the first anchor point randomly * (if selecting randomly). */ protected Random rand = new Random(m_RSeed); /** * The radius of the smallest ball enclosing all the data points. */ private double rootRadius = -1; /** * True if the initial anchor is chosen randomly. False if it is the furthest * point from the mean/centroid. */ protected boolean m_RandomInitialAnchor = true; /** * Creates a new instance of MiddleOutConstructor. */ public MiddleOutConstructor() { } /** * Returns a string describing this nearest neighbour search algorithm. * * @return a description of the algorithm for displaying in the * explorer/experimenter gui */ public String globalInfo() { return "The class that builds a BallTree middle out.\n\n" + "For more information see also:\n\n" + getTechnicalInformation().toString(); } /** * Returns an instance of a TechnicalInformation object, containing detailed * information about the technical background of this class, e.g., paper * reference or book this class is based on. * * @return the technical information about this class */ @Override public TechnicalInformation getTechnicalInformation() { TechnicalInformation result; TechnicalInformation additional; result = new TechnicalInformation(Type.INPROCEEDINGS); result.setValue(Field.AUTHOR, "Andrew W. Moore"); result .setValue( Field.TITLE, "The Anchors Hierarchy: Using the Triangle Inequality to Survive High Dimensional Data"); result.setValue(Field.YEAR, "2000"); result .setValue( Field.BOOKTITLE, "UAI '00: Proceedings of the 16th Conference on Uncertainty in Artificial Intelligence"); result.setValue(Field.PAGES, "397-405"); result.setValue(Field.PUBLISHER, "Morgan Kaufmann Publishers Inc."); result.setValue(Field.ADDRESS, "San Francisco, CA, USA"); additional = result.add(Type.MASTERSTHESIS); additional.setValue(Field.AUTHOR, "Ashraf Masood Kibriya"); additional.setValue(Field.TITLE, "Fast Algorithms for Nearest Neighbour Search"); additional.setValue(Field.YEAR, "2007"); additional .setValue( Field.SCHOOL, "Department of Computer Science, School of Computing and Mathematical Sciences, University of Waikato"); additional.setValue(Field.ADDRESS, "Hamilton, New Zealand"); return result; } /** * Builds a ball tree middle out. * * @return The root node of the tree. * @throws Exception If there is problem building the tree. */ @Override public BallNode buildTree() throws Exception { m_NumNodes = m_MaxDepth = m_NumLeaves = 0; if (rootRadius == -1) { rootRadius = BallNode .calcRadius(m_InstList, m_Instances, BallNode.calcCentroidPivot(m_InstList, m_Instances), m_DistanceFunction); } BallNode root = buildTreeMiddleOut(0, m_Instances.numInstances() - 1); return root; } /** * Builds a ball tree middle out from the portion of the master index array * given by supplied start and end index. * * @param startIdx The start of the portion in master index array. * @param endIdx the end of the portion in master index array. * @return The root node of the built tree. * @throws Exception If there is some problem building the tree. */ protected BallNode buildTreeMiddleOut(int startIdx, int endIdx) throws Exception { Instance pivot; double radius; Vector anchors; int numInsts = endIdx - startIdx + 1; int numAnchors = (int) Math.round(Math.sqrt(numInsts)); // create anchor's hierarchy if (numAnchors > 1) { pivot = BallNode.calcCentroidPivot(startIdx, endIdx, m_InstList, m_Instances); radius = BallNode.calcRadius(startIdx, endIdx, m_InstList, m_Instances, pivot, m_DistanceFunction); if (numInsts <= m_MaxInstancesInLeaf || (rootRadius == 0 ? true : radius / rootRadius < m_MaxRelLeafRadius)) { // just // make // a // leaf // don't // make // anchors // hierarchy BallNode node = new BallNode(startIdx, endIdx, m_NumNodes, pivot, radius); return node; } anchors = new Vector(numAnchors); createAnchorsHierarchy(anchors, numAnchors, startIdx, endIdx); BallNode node = mergeNodes(anchors, startIdx, endIdx); buildLeavesMiddleOut(node); return node; }// end anchors hierarchy else { BallNode node = new BallNode(startIdx, endIdx, m_NumNodes, (pivot = BallNode.calcCentroidPivot(startIdx, endIdx, m_InstList, m_Instances)), BallNode.calcRadius(startIdx, endIdx, m_InstList, m_Instances, pivot, m_DistanceFunction)); return node; } } /** * Creates an anchors hierarchy from a portion of master index array. * * @param anchors The vector for putting the anchors into. * @param numAnchors The number of anchors to create. * @param startIdx The start of the portion of master index array. * @param endIdx The end of the portion of master index array. * @throws Exception If there is some problem in creating the hierarchy. */ protected void createAnchorsHierarchy(Vector anchors, final int numAnchors, final int startIdx, final int endIdx) throws Exception { TempNode anchr1 = m_RandomInitialAnchor ? getRandomAnchor(startIdx, endIdx) : getFurthestFromMeanAnchor(startIdx, endIdx); TempNode amax = anchr1; // double maxradius = anchr1.radius; TempNode newAnchor; Vector anchorDistances = new Vector(numAnchors - 1); anchors.add(anchr1); // creating anchors while (anchors.size() < numAnchors) { // create new anchor newAnchor = new TempNode(); newAnchor.points = new MyIdxList(); Instance newpivot = m_Instances.instance(amax.points.getFirst().idx); newAnchor.anchor = newpivot; newAnchor.idx = amax.points.getFirst().idx; setInterAnchorDistances(anchors, newAnchor, anchorDistances); if (stealPoints(newAnchor, anchors, anchorDistances)) { newAnchor.radius = newAnchor.points.getFirst().distance; } else { newAnchor.radius = 0.0; } anchors.add(newAnchor); // find new amax amax = anchors.elementAt(0); for (int i = 1; i < anchors.size(); i++) { newAnchor = anchors.elementAt(i); if (newAnchor.radius > amax.radius) { amax = newAnchor; } }// end for }// end while } /** * Applies the middle out build procedure to the leaves of the tree. The leaf * nodes should be the ones that were created by createAnchorsHierarchy(). The * process continues recursively for the leaves created for each leaf of the * given tree until for some leaf node <= m_MaxInstancesInLeaf instances * remain in the leaf. * * @param node The root of the tree. * @throws Exception If there is some problem in building the tree leaves. */ protected void buildLeavesMiddleOut(BallNode node) throws Exception { if (node.m_Left != null && node.m_Right != null) { // if an internal node buildLeavesMiddleOut(node.m_Left); buildLeavesMiddleOut(node.m_Right); } else if (node.m_Left != null || node.m_Right != null) { throw new Exception("Invalid leaf assignment. Please check code"); } else { // if node is a leaf BallNode n2 = buildTreeMiddleOut(node.m_Start, node.m_End); if (n2.m_Left != null && n2.m_Right != null) { node.m_Left = n2.m_Left; node.m_Right = n2.m_Right; buildLeavesMiddleOut(node); // the stopping condition in buildTreeMiddleOut will stop the recursion, // where it won't split a node at all, and we won't recurse here. } else if (n2.m_Left != null || n2.m_Right != null) { throw new Exception("Invalid leaf assignment. Please check code"); } } } /** * Merges nodes created by createAnchorsHierarchy() into one top node. * * @param list List of anchor nodes. * @param startIdx The start of the portion of master index array containing * these anchor nodes. * @param endIdx The end of the portion of master index array containing these * anchor nodes. * @return The top/root node after merging the given anchor nodes. * @throws Exception IF there is some problem in merging. */ protected BallNode mergeNodes(Vector list, int startIdx, int endIdx) throws Exception { for (int i = 0; i < list.size(); i++) { TempNode n = list.get(i); n.anchor = calcPivot(n.points, new MyIdxList(), m_Instances); n.radius = calcRadius(n.points, new MyIdxList(), n.anchor, m_Instances); } double minRadius, tmpRadius; // tmpVolume, minVolume; Instance pivot, minPivot = null; TempNode parent; int min1 = -1, min2 = -1; while (list.size() > 1) { // main merging loop minRadius = Double.POSITIVE_INFINITY; for (int i = 0; i < list.size(); i++) { TempNode first = list.get(i); for (int j = i + 1; j < list.size(); j++) { TempNode second = list.get(j); pivot = calcPivot(first, second, m_Instances); tmpRadius = calcRadius(first, second); // calcRadius(first.points, // second.points, pivot, // m_Instances); if (tmpRadius < minRadius) { // (tmpVolume < minVolume) { minRadius = tmpRadius; // minVolume = tmpVolume; minPivot = pivot; min1 = i; min2 = j; // minInstList = tmpInstList; } }// end for(j) }// end for(i) parent = new TempNode(); parent.left = list.get(min1); parent.right = list.get(min2); parent.anchor = minPivot; parent.radius = calcRadius(parent.left.points, parent.right.points, minPivot, m_Instances); // minRadius; parent.points = parent.left.points.append(parent.left.points, parent.right.points); list.remove(min1); list.remove(min2 - 1); list.add(parent); }// end while TempNode tmpRoot = list.get(list.size() - 1); if ((endIdx - startIdx + 1) != tmpRoot.points.length()) { throw new Exception("Root nodes instance list is of irregular length. " + "Please check code. Length should " + "be: " + (endIdx - startIdx + 1) + " whereas it is found to be: " + tmpRoot.points.length()); } for (int i = 0; i < tmpRoot.points.length(); i++) { m_InstList[startIdx + i] = tmpRoot.points.get(i).idx; } BallNode node = makeBallTreeNodes(tmpRoot, startIdx, endIdx, 0); return node; } /** * Makes BallTreeNodes out of TempNodes. * * @param node The root TempNode * @param startidx The start of the portion of master index array the * TempNodes are made from. * @param endidx The end of the portion of master index array the TempNodes * are made from. * @param depth The depth in the tree where this root TempNode is made (needed * when leaves of a tree deeper down are built middle out). * @return The root BallTreeNode. */ protected BallNode makeBallTreeNodes(TempNode node, int startidx, int endidx, int depth) { BallNode ball = null; if (node.left != null && node.right != null) { // make an internal node ball = new BallNode(startidx, endidx, m_NumNodes, node.anchor, node.radius); m_NumNodes += 1; ball.m_Left = makeBallTreeNodes(node.left, startidx, startidx + node.left.points.length() - 1, depth + 1); ball.m_Right = makeBallTreeNodes(node.right, startidx + node.left.points.length(), endidx, depth + 1); m_MaxDepth++; } else { // make a leaf node ball = new BallNode(startidx, endidx, m_NumNodes, node.anchor, node.radius); m_NumNodes += 1; m_NumLeaves += 1; } return ball; } /** * Returns an anchor point which is furthest from the mean point for a given * set of points (instances) (The anchor instance is chosen from the given set * of points). * * @param startIdx The start index of the points for which anchor point is * required. * @param endIdx The end index of the points for which anchor point is * required. * @return The furthest point/instance from the mean of given set of points. */ protected TempNode getFurthestFromMeanAnchor(int startIdx, int endIdx) { TempNode anchor = new TempNode(); Instance centroid = BallNode.calcCentroidPivot(startIdx, endIdx, m_InstList, m_Instances); Instance temp; double tmpr; anchor.radius = Double.NEGATIVE_INFINITY; for (int i = startIdx; i <= endIdx; i++) { temp = m_Instances.instance(m_InstList[i]); tmpr = m_DistanceFunction.distance(centroid, temp); if (tmpr > anchor.radius) { anchor.idx = m_InstList[i]; anchor.anchor = temp; anchor.radius = tmpr; } } setPoints(anchor, startIdx, endIdx, m_InstList); return anchor; } /** * Returns a random anchor point/instance from a given set of * points/instances. * * @param startIdx The start index of the points for which anchor is required. * @param endIdx The end index of the points for which anchor is required. * @return The random anchor point/instance for the given set of */ protected TempNode getRandomAnchor(int startIdx, int endIdx) { TempNode anchr1 = new TempNode(); anchr1.idx = m_InstList[startIdx + rand.nextInt((endIdx - startIdx + 1))]; anchr1.anchor = m_Instances.instance(anchr1.idx); setPoints(anchr1, startIdx, endIdx, m_InstList); anchr1.radius = anchr1.points.getFirst().distance; return anchr1; } /** * Sets the points of an anchor node. It takes the indices of points from the * given portion of an index array and stores those indices, together with * their distances to the given anchor node, in the point index list of the * anchor node. * * @param node The node in which the points are needed to be set. * @param startIdx The start of the portion in the given index array (the * master index array). * @param endIdx The end of the portion in the given index array. * @param indices The index array. */ public void setPoints(TempNode node, int startIdx, int endIdx, int[] indices) { node.points = new MyIdxList(); Instance temp; double dist; for (int i = startIdx; i <= endIdx; i++) { temp = m_Instances.instance(indices[i]); dist = m_DistanceFunction.distance(node.anchor, temp); node.points.insertReverseSorted(indices[i], dist); } } /** * Sets the distances of a supplied new anchor to all the rest of the previous * anchor points. * * @param anchors The old anchor points. * @param newAnchor The new anchor point. * @param anchorDistances The vector to store the distances of newAnchor to * each of the old anchors. * @throws Exception If there is some problem in calculating the distances. */ public void setInterAnchorDistances(Vector anchors, TempNode newAnchor, Vector anchorDistances) throws Exception { double[] distArray = new double[anchors.size()]; for (int i = 0; i < anchors.size(); i++) { Instance anchr = anchors.elementAt(i).anchor; distArray[i] = m_DistanceFunction.distance(anchr, newAnchor.anchor); } anchorDistances.add(distArray); } /** * Removes points from old anchors that are nearer to the given new anchor and * adds them to the list of points of the new anchor. * * @param newAnchor The new anchor. * @param anchors The old anchors. * @param anchorDistances The distances of new anchor to each of the old * anchors. * @return true if any points are removed from the old anchors */ public boolean stealPoints(TempNode newAnchor, Vector anchors, Vector anchorDistances) { double maxDist = Double.NEGATIVE_INFINITY; double[] distArray = anchorDistances.lastElement(); for (double element : distArray) { if (maxDist < element) { maxDist = element; } } boolean anyPointsStolen = false, pointsStolen = false; TempNode anchorI; double newDist, distI, interAnchMidDist; Instance newAnchInst = newAnchor.anchor, anchIInst; for (int i = 0; i < anchors.size(); i++) { anchorI = anchors.elementAt(i); anchIInst = anchorI.anchor; pointsStolen = false; interAnchMidDist = m_DistanceFunction.distance(newAnchInst, anchIInst) / 2D; for (int j = 0; j < anchorI.points.length(); j++) { ListNode tmp = anchorI.points.get(j); // break if we reach a point whose distance is less than the midpoint // of inter anchor distance if (tmp.distance < interAnchMidDist) { break; } // else test if this point can be stolen by the new anchor newDist = m_DistanceFunction.distance(newAnchInst, m_Instances.instance(tmp.idx)); distI = tmp.distance; if (newDist < distI) { newAnchor.points.insertReverseSorted(tmp.idx, newDist); anchorI.points.remove(j); anyPointsStolen = pointsStolen = true; } } if (pointsStolen) { anchorI.radius = anchorI.points.getFirst().distance; } }// end for return anyPointsStolen; }// end stealPoints() /** * /** Calculates the centroid pivot of a node based on its two child nodes * (if merging two nodes). * * @param node1 The first child. * @param node2 The second child. * @param insts The set of instances on which the tree is being built (as * dataset header information is required). * @return The centroid pivot of a node. */ public Instance calcPivot(TempNode node1, TempNode node2, Instances insts) { int classIdx = m_Instances.classIndex(); double[] attrVals = new double[insts.numAttributes()]; Instance temp; double anchr1Ratio = (double) node1.points.length() / (node1.points.length() + node2.points.length()), anchr2Ratio = (double) node2.points .length() / (node1.points.length() + node2.points.length()); ; for (int k = 0; k < node1.anchor.numValues(); k++) { if (node1.anchor.index(k) == classIdx) { continue; } attrVals[k] += node1.anchor.valueSparse(k) * anchr1Ratio; } for (int k = 0; k < node2.anchor.numValues(); k++) { if (node2.anchor.index(k) == classIdx) { continue; } attrVals[k] += node2.anchor.valueSparse(k) * anchr2Ratio; } temp = new DenseInstance(1.0, attrVals); return temp; } /** * Calculates the centroid pivot of a node based on the list of points that it * contains (tbe two lists of its children are provided). * * @param list1 The point index list of first child. * @param list2 The point index list of second child. * @param insts The insts object on which the tree is being built (for header * information). * @return The centroid pivot of the node. */ public Instance calcPivot(MyIdxList list1, MyIdxList list2, Instances insts) { int classIdx = m_Instances.classIndex(); double[] attrVals = new double[insts.numAttributes()]; Instance temp; for (int i = 0; i < list1.length(); i++) { temp = insts.instance(list1.get(i).idx); for (int k = 0; k < temp.numValues(); k++) { if (temp.index(k) == classIdx) { continue; } attrVals[k] += temp.valueSparse(k); } } for (int j = 0; j < list2.length(); j++) { temp = insts.instance(list2.get(j).idx); for (int k = 0; k < temp.numValues(); k++) { if (temp.index(k) == classIdx) { continue; } attrVals[k] += temp.valueSparse(k); } } for (int j = 0, numInsts = list1.length() + list2.length(); j < attrVals.length; j++) { attrVals[j] /= numInsts; } temp = new DenseInstance(1.0, attrVals); return temp; } /** * Calculates the radius of a node based on its two child nodes (if merging * two nodes). * * @param n1 The first child of the node. * @param n2 The second child of the node. * @return The radius of the node. * @throws Exception */ public double calcRadius(TempNode n1, TempNode n2) { Instance p1 = n1.anchor, p2 = n2.anchor; double radius = n1.radius + m_DistanceFunction.distance(p1, p2) + n2.radius; return radius / 2; } /** * Calculates the radius of a node based on the list of points that it * contains (the two lists of its children are provided). * * @param list1 The point index list of first child. * @param list2 The point index list of second child. * @param pivot The centre/pivot of the node. * @param insts The instances on which the tree is being built (for header * info). * @return The radius of the node. */ public double calcRadius(MyIdxList list1, MyIdxList list2, Instance pivot, Instances insts) { double radius = Double.NEGATIVE_INFINITY; for (int i = 0; i < list1.length(); i++) { double dist = m_DistanceFunction.distance(pivot, insts.instance(list1.get(i).idx)); if (dist > radius) { radius = dist; } } for (int j = 0; j < list2.length(); j++) { double dist = m_DistanceFunction.distance(pivot, insts.instance(list2.get(j).idx)); if (dist > radius) { radius = dist; } } return radius; } /** * Adds an instance to the tree. This implementation of MiddleOutConstructor * doesn't support addition of instances to already built tree, hence it * always throws an exception. * * @param node The root of the tree to which the instance is to be added. * @param inst The instance to add to the tree. * @return The updated master index array after adding the instance. * @throws Exception Always as this implementation of MiddleOutConstructor * doesn't support addition of instances after batch construction of * the tree. */ @Override public int[] addInstance(BallNode node, Instance inst) throws Exception { throw new Exception("Addition of instances after the tree is built, not " + "possible with MiddleOutConstructor."); } /** * Sets the maximum number of instances allowed in a leaf. * * @param num The maximum number of instances allowed in a leaf. * @throws Exception If the num is < 2, as the method cannot work for < 2 * instances. */ @Override public void setMaxInstancesInLeaf(int num) throws Exception { if (num < 2) { throw new Exception("The maximum number of instances in a leaf for " + "using MiddleOutConstructor must be >=2."); } super.setMaxInstancesInLeaf(num); } /** * Sets the instances on which the tree is to be built. * * @param insts The instances on which to build the ball tree. */ @Override public void setInstances(Instances insts) { super.setInstances(insts); rootRadius = -1; // this needs to be re-calculated by buildTree() } /** * Sets the master index array that points to instances in m_Instances, so * that only this array is manipulated, and m_Instances is left untouched. * * @param instList The master index array. */ @Override public void setInstanceList(int[] instList) { super.setInstanceList(instList); rootRadius = -1; // this needs to be re-calculated by buildTree() } /** * Returns the tip text for this property. * * @return tip text for this property suitable for displaying in the * explorer/experimenter gui */ public String initialAnchorRandomTipText() { return "Whether the initial anchor is chosen randomly."; } /** * Gets whether if the initial anchor is chosen randomly. * * @return true if the initial anchor is a random one. */ public boolean isInitialAnchorRandom() { return m_RandomInitialAnchor; } /** * Sets whether if the initial anchor is chosen randomly. If not then if it is * the furthest point from the mean/centroid. * * @param randomInitialAnchor Should be true if the first anchor is to be * chosen randomly. */ public void setInitialAnchorRandom(boolean randomInitialAnchor) { m_RandomInitialAnchor = randomInitialAnchor; } /** * Returns the tip text for this property. * * @return tip text for this property suitable for displaying in the * explorer/experimenter gui */ public String seedTipText() { return "The seed value for the random number generator."; } /** * Returns the seed for random number generator. * * @return The random number seed. */ @Override public int getSeed() { return m_RSeed; } /** * Sets the seed for random number generator (that is used for selecting the * first anchor point randomly). * * @param seed The seed. */ @Override public void setSeed(int seed) { m_RSeed = seed; } /** * Returns an enumeration describing the available options. * * @return an enumeration of all the available options. */ @Override public Enumeration




© 2015 - 2024 Weber Informatics LLC | Privacy Policy