use of de.lmu.ifi.dbs.elki.data.spatial.SpatialComparable in project elki by elki-project.
the class XSplitter method add2MBR.
/**
* Adds the minimum and maximum bounds of the MBR of entry
* <code>entrySorting[index]</code> in {@link #entries} to the dimension-wise
* upper and lower bounds, <code>ub</code> and <code>lb</code>. Note that if
* this method is called for <code>ub</code> and <code>lb</code> which are
* already owned by an MBR, this update operation also updates the MBR defined
* by those bounds.
*
* @param entrySorting a sorting providing the mapping of <code>index</code>
* to the entry in {@link #entries} to be added
* @param ub the upper bound of the MBR to be extended
* @param lb the lower bound of the MBR to be extended
* @param index the index in the sorting referencing the entry to be added
*/
private void add2MBR(int[] entrySorting, double[] ub, double[] lb, int index) {
SpatialComparable currMBR = node.getEntry(entrySorting[index]);
for (int d = 0; d < currMBR.getDimensionality(); d++) {
double max = currMBR.getMax(d);
if (max > ub[d]) {
ub[d] = max;
}
double min = currMBR.getMin(d);
if (min < lb[d]) {
lb[d] = min;
}
}
}
use of de.lmu.ifi.dbs.elki.data.spatial.SpatialComparable in project elki by elki-project.
the class AbstractXTree method choosePath.
/**
* Chooses the best path of the specified subtree for insertion of the given
* MBR at the specified level. The selection uses the following criteria:
* <ol>
* <li>Test on containment (<code>mbr</code> <em>is</em> within one of the
* children)</li>
* <li>If there are multiple containing children, the child with the minimum
* volume is chosen.</li>
* <li>Else, if the children point to leaf nodes, chooses the child with the
* minimum multi-overlap increase.</li>
* <li>Else, or the multi-overlap increase leads to ties, the child with the
* minimum volume increase is selected.</li>
* <li>If there are still ties, the child with the minimum volume is
* chosen.</li>
* </ol>
*
* @param subtree the subtree to be tested for insertion
* @param mbr the MBR to be inserted
* @param level the level at which the MBR should be inserted (level 1
* indicates leaf-level)
* @return the path of the appropriate subtree to insert the given
* <code>mbr</code>
*/
@Override
protected IndexTreePath<SpatialEntry> choosePath(IndexTreePath<SpatialEntry> subtree, SpatialComparable mbr, int level, int cur) {
if (getLogger().isDebuggingFiner()) {
getLogger().debugFiner("node " + subtree + ", level " + level);
}
N node = getNode(subtree.getEntry());
if (node == null) {
throw new RuntimeException("Page file did not return node for node id: " + getPageID(subtree.getEntry()));
}
if (node.isLeaf()) {
return subtree;
}
// first test on containment
IndexTreePath<SpatialEntry> newSubtree = containedTest(subtree, node, mbr);
if (newSubtree != null) {
if (height - subtree.getPathCount() == level) {
return newSubtree;
} else {
return choosePath(newSubtree, mbr, level, ++cur);
}
}
int optEntry = -1;
HyperBoundingBox optTestMBR = null;
double optOverlapInc = 0;
// test overlap increase?
boolean isLeafContainer = false;
if ((// also test supernodes
!OMIT_OVERLAP_INCREASE_4_SUPERNODES || // don't
(OMIT_OVERLAP_INCREASE_4_SUPERNODES && !node.isSuperNode())) && getNode(node.getEntry(0)).isLeaf()) {
// children are leafs
// overlap increase is to be tested
optOverlapInc = Double.POSITIVE_INFINITY;
isLeafContainer = true;
}
double optVolume = Double.POSITIVE_INFINITY;
double optVolumeInc = Double.POSITIVE_INFINITY;
double tempVolume, volume;
for (int index = 0; index < node.getNumEntries(); index++) {
SpatialEntry child = node.getEntry(index);
SpatialComparable childMBR = child;
HyperBoundingBox testMBR = SpatialUtil.union(childMBR, mbr);
double pairwiseOverlapInc;
if (isLeafContainer) {
pairwiseOverlapInc = calculateOverlapIncrease(node, child, testMBR);
if (Double.isInfinite(pairwiseOverlapInc) || Double.isNaN(pairwiseOverlapInc)) {
throw new IllegalStateException("an entry's MBR is too large to calculate its overlap increase: " + pairwiseOverlapInc + "; \nplease re-scale your data s.t. it can be dealt with");
}
} else {
// no need to examine overlap increase?
pairwiseOverlapInc = 0;
}
if (pairwiseOverlapInc <= optOverlapInc) {
if (pairwiseOverlapInc == optOverlapInc) {
// If there are multiple entries with the same overlap increase,
// choose the one with the minimum volume increase.
// If there are also multiple entries with the same volume increase
// choose the one with the minimum volume.
volume = SpatialUtil.volume(childMBR);
if (Double.isInfinite(volume) || Double.isNaN(volume)) {
throw new IllegalStateException("an entry's MBR is too large to calculate its volume: " + volume + "; \nplease re-scale your data s.t. it can be dealt with");
}
tempVolume = SpatialUtil.volume(testMBR);
if (Double.isInfinite(tempVolume) || Double.isNaN(tempVolume)) {
throw new IllegalStateException("an entry's MBR is too large to calculate its volume: " + tempVolume + "; \nplease re-scale your data s.t. it can be dealt with");
}
double volumeInc = tempVolume - volume;
if (Double.isNaN(optVolumeInc)) {
// has not yet been calculated
optVolume = SpatialUtil.volume(node.getEntry(optEntry));
optVolumeInc = SpatialUtil.volume(optTestMBR) - optVolume;
}
if (volumeInc < optVolumeInc) {
optVolumeInc = volumeInc;
optVolume = volume;
optEntry = index;
} else if (volumeInc == optVolumeInc && volume < optVolume) {
// TODO: decide whether to remove this option
System.out.println("####\nEQUAL VOLUME INCREASE: HAPPENS!\n####");
optVolumeInc = volumeInc;
optVolume = volume;
optEntry = index;
}
} else {
// already better
optOverlapInc = pairwiseOverlapInc;
optVolume = Double.NaN;
optVolumeInc = Double.NaN;
// for later calculations
optTestMBR = testMBR;
optEntry = index;
}
}
}
assert optEntry >= 0;
newSubtree = new IndexTreePath<>(subtree, node.getEntry(optEntry), optEntry);
if (height - subtree.getPathCount() == level) {
return newSubtree;
} else {
return choosePath(newSubtree, mbr, level, ++cur);
}
}
use of de.lmu.ifi.dbs.elki.data.spatial.SpatialComparable in project elki by elki-project.
the class EvaluateDBCV method evaluateClustering.
/**
* Evaluate a single clustering.
*
* @param db Database
* @param rel Data relation
* @param cl Clustering
*
* @return dbcv DBCV-index
*/
public double evaluateClustering(Database db, Relation<O> rel, Clustering<?> cl) {
final DistanceQuery<O> dq = rel.getDistanceQuery(distanceFunction);
List<? extends Cluster<?>> clusters = cl.getAllClusters();
final int numc = clusters.size();
// DBCV needs a "dimensionality".
@SuppressWarnings("unchecked") final Relation<? extends SpatialComparable> vrel = (Relation<? extends SpatialComparable>) rel;
final int dim = RelationUtil.dimensionality(vrel);
// precompute all core distances
ArrayDBIDs[] cids = new ArrayDBIDs[numc];
double[][] coreDists = new double[numc][];
for (int c = 0; c < numc; c++) {
Cluster<?> cluster = clusters.get(c);
// Singletons are considered as Noise, because they have no sparseness
if (cluster.isNoise() || cluster.size() < 2) {
coreDists[c] = null;
continue;
}
// Store for use below:
ArrayDBIDs ids = cids[c] = DBIDUtil.ensureArray(cluster.getIDs());
double[] clusterCoreDists = coreDists[c] = new double[ids.size()];
for (DBIDArrayIter it = ids.iter(), it2 = ids.iter(); it.valid(); it.advance()) {
double currentCoreDist = 0;
int neighbors = 0;
for (it2.seek(0); it2.valid(); it2.advance()) {
if (DBIDUtil.equal(it, it2)) {
continue;
}
double dist = dq.distance(it, it2);
// We ignore such objects.
if (dist > 0) {
currentCoreDist += MathUtil.powi(1. / dist, dim);
++neighbors;
}
}
// Average, and undo power.
clusterCoreDists[it.getOffset()] = FastMath.pow(currentCoreDist / neighbors, -1. / dim);
}
}
// compute density sparseness of all clusters
int[][] clusterDegrees = new int[numc][];
double[] clusterDscMax = new double[numc];
// describes if a cluster contains any internal edges
boolean[] internalEdges = new boolean[numc];
for (int c = 0; c < numc; c++) {
Cluster<?> cluster = clusters.get(c);
if (cluster.isNoise() || cluster.size() < 2) {
clusterDegrees[c] = null;
clusterDscMax[c] = Double.NaN;
continue;
}
double[] clusterCoreDists = coreDists[c];
ArrayDBIDs ids = cids[c];
// Density Sparseness of the Cluster
double dscMax = 0;
double[][] distances = new double[cluster.size()][cluster.size()];
// create mutability distance matrix for Minimum Spanning Tree
for (DBIDArrayIter it = ids.iter(), it2 = ids.iter(); it.valid(); it.advance()) {
double currentCoreDist = clusterCoreDists[it.getOffset()];
for (it2.seek(it.getOffset() + 1); it2.valid(); it2.advance()) {
double mutualReachDist = MathUtil.max(currentCoreDist, clusterCoreDists[it2.getOffset()], dq.distance(it, it2));
distances[it.getOffset()][it2.getOffset()] = mutualReachDist;
distances[it2.getOffset()][it.getOffset()] = mutualReachDist;
}
}
// generate Minimum Spanning Tree
int[] nodes = PrimsMinimumSpanningTree.processDense(distances);
// get degree of all nodes in the spanning tree
int[] degree = new int[cluster.size()];
for (int i = 0; i < nodes.length; i++) {
degree[nodes[i]]++;
}
// check if cluster contains any internal edges
for (int i = 0; i < nodes.length; i += 2) {
if (degree[nodes[i]] > 1 && degree[nodes[i + 1]] > 1) {
internalEdges[c] = true;
}
}
clusterDegrees[c] = degree;
// find maximum sparseness in the Minimum Spanning Tree
for (int i = 0; i < nodes.length; i = i + 2) {
final int n1 = nodes[i], n2 = nodes[i + 1];
// If a cluster has no internal nodes we consider all edges.
if (distances[n1][n2] > dscMax && (!internalEdges[c] || (degree[n1] > 1 && degree[n2] > 1))) {
dscMax = distances[n1][n2];
}
}
clusterDscMax[c] = dscMax;
}
// compute density separation of all clusters
double dbcv = 0;
for (int c = 0; c < numc; c++) {
Cluster<?> cluster = clusters.get(c);
if (cluster.isNoise() || cluster.size() < 2) {
continue;
}
double currentDscMax = clusterDscMax[c];
double[] clusterCoreDists = coreDists[c];
int[] currentDegree = clusterDegrees[c];
// minimal Density Separation of the Cluster
double dspcMin = Double.POSITIVE_INFINITY;
for (DBIDArrayIter it = cids[c].iter(); it.valid(); it.advance()) {
// nodes.
if (currentDegree[it.getOffset()] < 2 && internalEdges[c]) {
continue;
}
double currentCoreDist = clusterCoreDists[it.getOffset()];
for (int oc = 0; oc < numc; oc++) {
Cluster<?> ocluster = clusters.get(oc);
if (ocluster.isNoise() || ocluster.size() < 2 || cluster == ocluster) {
continue;
}
int[] oDegree = clusterDegrees[oc];
double[] oclusterCoreDists = coreDists[oc];
for (DBIDArrayIter it2 = cids[oc].iter(); it2.valid(); it2.advance()) {
if (oDegree[it2.getOffset()] < 2 && internalEdges[oc]) {
continue;
}
double mutualReachDist = MathUtil.max(currentCoreDist, oclusterCoreDists[it2.getOffset()], dq.distance(it, it2));
dspcMin = mutualReachDist < dspcMin ? mutualReachDist : dspcMin;
}
}
}
// compute DBCV
double vc = (dspcMin - currentDscMax) / MathUtil.max(dspcMin, currentDscMax);
double weight = cluster.size() / (double) rel.size();
dbcv += weight * vc;
}
EvaluationResult ev = EvaluationResult.findOrCreate(db.getHierarchy(), cl, "Internal Clustering Evaluation", "internal evaluation");
MeasurementGroup g = ev.findOrCreateGroup("Distance-based Evaluation");
g.addMeasure("Density Based Clustering Validation", dbcv, 0., Double.POSITIVE_INFINITY, 0., true);
db.getHierarchy().resultChanged(ev);
return dbcv;
}
use of de.lmu.ifi.dbs.elki.data.spatial.SpatialComparable in project elki by elki-project.
the class LeastEnlargementWithAreaInsertionStrategy method choose.
@Override
public <A> int choose(A options, ArrayAdapter<? extends SpatialComparable, A> getter, SpatialComparable obj, int height, int depth) {
final int size = getter.size(options);
assert (size > 0) : "Choose from empty set?";
// As in R-Tree, with a slight modification for ties
double leastEnlargement = Double.POSITIVE_INFINITY;
double minArea = -1;
int best = -1;
for (int i = 0; i < size; i++) {
SpatialComparable entry = getter.get(options, i);
double enlargement = SpatialUtil.enlargement(entry, obj);
if (enlargement < leastEnlargement) {
leastEnlargement = enlargement;
best = i;
minArea = SpatialUtil.volume(entry);
} else if (enlargement == leastEnlargement) {
final double area = SpatialUtil.volume(entry);
if (area < minArea) {
// Tie handling proposed by R*:
best = i;
minArea = area;
}
}
}
assert (best > -1);
return best;
}
use of de.lmu.ifi.dbs.elki.data.spatial.SpatialComparable in project elki by elki-project.
the class MaxExtensionBulkSplit method chooseMaximalExtendedSplitAxis.
/**
* Computes and returns the best split axis. The best split axis is the split
* axes with the maximal extension.
*
* @param objects the spatial objects to be split
* @return the best split axis
*/
private int chooseMaximalExtendedSplitAxis(List<? extends SpatialComparable> objects) {
// maximum and minimum value for the extension
int dimension = objects.get(0).getDimensionality();
double[] maxExtension = new double[dimension];
double[] minExtension = new double[dimension];
Arrays.fill(minExtension, Double.MAX_VALUE);
// compute min and max value in each dimension
for (SpatialComparable object : objects) {
for (int d = 0; d < dimension; d++) {
double min, max;
min = object.getMin(d);
max = object.getMax(d);
if (maxExtension[d] < max) {
maxExtension[d] = max;
}
if (minExtension[d] > min) {
minExtension[d] = min;
}
}
}
// set split axis to dim with maximal extension
int splitAxis = -1;
double max = 0;
for (int d = 0; d < dimension; d++) {
double currentExtension = maxExtension[d] - minExtension[d];
if (max < currentExtension) {
max = currentExtension;
splitAxis = d;
}
}
return splitAxis;
}
Aggregations