package de.lmu.ifi.dbs.elki.index.tree.spatial.rstarvariants.strategies.split;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
Copyright (C) 2015
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
import de.lmu.ifi.dbs.elki.data.ModifiableHyperBoundingBox;
import de.lmu.ifi.dbs.elki.data.spatial.SpatialComparable;
import de.lmu.ifi.dbs.elki.data.spatial.SpatialUtil;
import de.lmu.ifi.dbs.elki.utilities.BitsUtil;
import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.ArrayAdapter;
import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
/**
* Quadratic-time complexity greedy split as used by the original R-Tree.
*
*
* Antonin Guttman:
* R-Trees: A Dynamic Index Structure For Spatial Searching
* in Proceedings of the 1984 ACM SIGMOD international conference on Management
* of data.
*
*
* @author Erich Schubert
* @since 0.5.0
*/
@Reference(authors = "Antonin Guttman", title = "R-Trees: A Dynamic Index Structure For Spatial Searching", booktitle = "Proceedings of the 1984 ACM SIGMOD international conference on Management of data", url = "http://dx.doi.org/10.1145/971697.602266")
public class RTreeQuadraticSplit implements SplitStrategy {
/**
* Static instance.
*/
public static final RTreeQuadraticSplit STATIC = new RTreeQuadraticSplit();
@Override
public long[] split(A entries, ArrayAdapter getter, int minEntries) {
final int num = getter.size(entries);
// Object assignment, and processed objects
long[] assignment = BitsUtil.zero(num);
long[] assigned = BitsUtil.zero(num);
// MBRs and Areas of current assignments
ModifiableHyperBoundingBox mbr1, mbr2;
double area1 = 0, area2 = 0;
// PickSeeds - find worst pair
{
double worst = Double.NEGATIVE_INFINITY;
int w1 = 0, w2 = 0;
// Compute individual areas
double[] areas = new double[num];
for(int e1 = 0; e1 < num - 1; e1++) {
final E e1i = getter.get(entries, e1);
areas[e1] = SpatialUtil.volume(e1i);
}
// Compute area increase
for(int e1 = 0; e1 < num - 1; e1++) {
final E e1i = getter.get(entries, e1);
for(int e2 = e1 + 1; e2 < num; e2++) {
final E e2i = getter.get(entries, e2);
final double areaJ = SpatialUtil.volumeUnion(e1i, e2i);
final double d = areaJ - areas[e1] - areas[e2];
if(d > worst) {
worst = d;
w1 = e1;
w2 = e2;
}
}
}
// Data to keep
// Mark both as used
BitsUtil.setI(assigned,w1);
BitsUtil.setI(assigned,w2);
// Assign second to second set
BitsUtil.setI(assignment,w2);
// Initial mbrs and areas
area1 = areas[w1];
area2 = areas[w2];
mbr1 = new ModifiableHyperBoundingBox(getter.get(entries, w1));
mbr2 = new ModifiableHyperBoundingBox(getter.get(entries, w2));
}
// Second phase, QS2+QS3
{
int in1 = 1, in2 = 1;
int remaining = num - 2;
while(remaining > 0) {
// Shortcut when minEntries must be fulfilled
if(in1 + remaining <= minEntries) {
// No need to updated assigned, no changes to assignment.
break;
}
if(in2 + remaining <= minEntries) {
// Mark unassigned for second.
// Don't bother to update assigned, though
for(int pos = BitsUtil.nextClearBit(assigned, 0); pos < num; pos = BitsUtil.nextClearBit(assigned, pos + 1)) {
BitsUtil.setI(assignment, pos);
}
break;
}
// PickNext
double greatestPreference = Double.NEGATIVE_INFINITY;
int best = -1;
E best_i = null;
boolean preferSecond = false;
for(int pos = BitsUtil.nextClearBit(assigned, 0); pos < num; pos = BitsUtil.nextClearBit(assigned, pos + 1)) {
// Cost of putting object into both mbrs
final E pos_i = getter.get(entries, pos);
final double d1 = SpatialUtil.volumeUnion(mbr1, pos_i) - area1;
final double d2 = SpatialUtil.volumeUnion(mbr2, pos_i) - area2;
// Preference
final double preference = Math.abs(d1 - d2);
if(preference > greatestPreference) {
greatestPreference = preference;
best = pos;
best_i = pos_i;
// Prefer smaller increase
preferSecond = (d2 < d1);
}
}
// QS3: tie handling
if(greatestPreference == 0) {
// Prefer smaller area
if(area1 != area2) {
preferSecond = (area2 < area1);
}
else {
// Prefer smaller group size
preferSecond = (in2 < in1);
}
}
// Mark as used.
BitsUtil.setI(assigned, best);
remaining--;
if(!preferSecond) {
in1++;
mbr1.extend(best_i);
area1 = SpatialUtil.volume(mbr1);
}
else {
in2++;
BitsUtil.setI(assignment, best);
mbr2.extend(best_i);
area2 = SpatialUtil.volume(mbr2);
}
// Loop from QS2
}
// Note: "assigned" and "remaining" likely not updated!
}
return assignment;
}
/**
* Parameterization class.
*
* @author Erich Schubert
*
* @apiviz.exclude
*/
public static class Parameterizer extends AbstractParameterizer {
@Override
protected RTreeQuadraticSplit makeInstance() {
return RTreeQuadraticSplit.STATIC;
}
}
}