Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

min-max heap for kNN #17

Merged
merged 20 commits into from
Jul 20, 2023
Merged
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
dependency-reduced-pom.xml

#################
## Eclipse
#################
Expand Down Expand Up @@ -221,4 +223,4 @@ pip-log.txt

#Mr Developer
.mr.developer.cfg
/target/
/target/
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

########## Fix qeury1NN!!!

### Changed
- Moved to GitHub Actions CI. [#13](https://github.com/tzaeschke/tinspin-indexes/pull/13)
- Java JDK 11 default + updated maven dependencies + updated CHANGELOG.md.
[#12](https://github.com/tzaeschke/tinspin-indexes/pull/12)

### Added
- MinMaxHeap & MinHeap for better kNN queries. [#17](https://github.com/tzaeschke/tinspin-indexes/pull/17)
- Proper API, more tests and numerous fixes for multimaps. [#16](https://github.com/tzaeschke/tinspin-indexes/pull/16)
- Proper test (and fixes) for multimaps. [#15](https://github.com/tzaeschke/tinspin-indexes/pull/15)

Expand Down
12 changes: 11 additions & 1 deletion TODO.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
API
===
- Rename Indexes to PointMap/PointMultimap BoxMax/BoxMultimap; deprecate PointIndex
- Remove Comparators in entries!
- Unify (Dist)Entries
- KD-tree remove stored dist function
- QueryIterator should be of type <T> instead of PointEntryDIst<T> (It should still return PED!)
- QueryIterator nextReuse()
- MinMaxHeap: leave entries in array -> reuse/pool
- MinHeap: n-ary heap
- kNN filter should take PointEntryDist i.o PointEntry

TODO TInSpin: common wrapper base class for all TinSpin indexes

Other
=====
- RTree, compare 1NN with kNN query iterator, 1NN seems outdated.
- Fix TODOs in RTree
- quadtrees: implement/benchmark KnnHS
- RTree implement improved KnnHS with minMaxHeap
Expand All @@ -26,4 +36,4 @@ CritBit
- Why do Point queries scale much worse than kd-Tree? Because of merging???? O(k)
- Profile:
- Consider using table for maskDst in CritBit.x.unsetBitAfterSplit() / setBitAfterSplit()
--> Aternatively, consider operating on merged value?
--> Alternatively, consider operating on merged value?
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ public interface RectangleDistanceFunction {
RectangleDistanceFunction CENTER = RectangleDistanceFunction::centerDistance;
RectangleDistanceFunction EDGE = RectangleDistanceFunction::edgeDistance;

double dist(double[] center, double[] min, double[] max);
/**
* @param point A point
* @param min Minimum corner of axis aligned box
* @param max Maximum corner of axis aligned box
* @return Distance between point and box
*/
double dist(double[] point, double[] min, double[] max);

/**
* Some algorithm use this method on the entries containing user supplied values.
Expand Down
7 changes: 1 addition & 6 deletions src/main/java/org/tinspin/index/array/PointArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -215,12 +215,7 @@ private static double dist(double[] a, double[] b) {
return Math.sqrt(dist);
}

private final Comparator<KnnEntry<T>> COMP = new Comparator<KnnEntry<T>>() {
@Override
public int compare(KnnEntry<T> o1, KnnEntry<T> o2) {
return o1.compareTo(o2);
}
};
private final Comparator<KnnEntry<T>> COMP = KnnEntry::compareTo;

private static class KnnEntry<T> implements Comparable<KnnEntry<T>>, PointEntryDist<T> {
private final double[] p;
Expand Down
200 changes: 116 additions & 84 deletions src/main/java/org/tinspin/index/array/RectArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,20 @@
*/
package org.tinspin.index.array;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Predicate;

import org.tinspin.index.QueryIterator;
import org.tinspin.index.QueryIteratorKNN;
import org.tinspin.index.RectangleEntry;
import org.tinspin.index.RectangleEntryDist;
import org.tinspin.index.RectangleIndex;
import org.tinspin.index.Stats;
import org.tinspin.index.*;
import org.tinspin.index.util.QueryIteratorWrapper;

public class RectArray<T> implements RectangleIndex<T> {
public class RectArray<T> implements RectangleIndex<T>, RectangleIndexMM<T> {

private final double[][] phc;
private final int dims;
private int N;
private RectangleEntry<T>[] values;
private int size;
private final RectangleEntry<T>[] values;
private int insPos = 0;

/**
Expand All @@ -37,6 +32,7 @@ public class RectArray<T> implements RectangleIndex<T> {
@SuppressWarnings("unchecked")
public RectArray(int dims, int size) {
this.N = size;
this.size = 0;
this.dims = dims;
phc = new double[2*N][dims];
values = new RectangleEntry[N];
Expand All @@ -49,18 +45,102 @@ public void insert(double[] lower, double[] upper, T value) {
System.arraycopy(upper, 0, phc[insPos*2+1], 0, dims);
values[insPos] = new KnnEntry<>(lower, upper, value, -1);
insPos++;
size++;
}


@Override
public T remove(double[] lower, double[] upper) {
for (int i = 0; i < N; i++) {
if (phc[i*2] != null && eq(phc[i*2], lower)
&& eq(phc[(i*2)+1], upper)) {
phc[i*2] = null;
phc[(i*2)+1] = null;
size--;
return values[i].value();
}
}
return null;
}

@Override
public boolean remove(double[] lower, double[] upper, T value) {
return removeIf(lower, upper, e -> Objects.equals(value, e.value()));
}

@Override
public boolean removeIf(double[] lower, double[] upper, Predicate<RectangleEntry<T>> condition) {
for (int i = 0; i < N; i++) {
if (phc[i*2] != null && eq(phc[i*2], lower)
&& eq(phc[(i*2)+1], upper) && condition.test(values[i])) {
phc[i*2] = null;
phc[(i*2)+1] = null;
size--;
return true;
}
}
return false;
}

@Override
public T update(double[] lo1, double[] up1, double[] lo2, double[] up2) {
for (int i = 0; i < N; i++) {
if (eq(phc[i*2], lo1) && eq(phc[(i*2)+1], up1)) {
System.arraycopy(lo2, 0, phc[i*2], 0, dims);
System.arraycopy(up2, 0, phc[(i*2)+1], 0, dims);
return values[i].value();
}
}
return null;
}

@Override
public boolean update(double[] lo1, double[] up1, double[] lo2, double[] up2, T value) {
for (int i = 0; i < N; i++) {
if (eq(phc[i*2], lo1) && eq(phc[(i*2)+1], up1)) {
if (eq(phc[i*2], lo1) && eq(phc[(i*2)+1], up1) && Objects.equals(value, values[i].value())) {
System.arraycopy(lo2, 0, phc[i * 2], 0, dims);
System.arraycopy(up2, 0, phc[(i * 2) + 1], 0, dims);
return true;
}
}
}
return false;
}

@Override
public T queryExact(double[] lower, double[] upper) {
for (int j = 0; j < N; j++) {
for (int j = 0; j < N; j++) {
if (eq(phc[j*2], lower) && eq(phc[j*2+1], upper)) {
return values[j].value();
}
}
return null;
}

@Override
public boolean contains(double[] lower, double[] upper, T value) {
for (int i = 0; i < N; i++) {
if (eq(phc[i*2], lower) && eq(phc[i*2+1], upper) && Objects.equals(value, values[i].value())) {
return true;
}
}
return false;
}

@Override
public QueryIterator<RectangleEntry<T>> queryRectangle(double[] lower, double[] upper) {
return new QueryIteratorWrapper<>(lower, upper, (low, upp) -> {
ArrayList<RectangleEntry<T>> result = new ArrayList<>();
for (int i = 0; i < N; i++) {
if (phc[i*2] != null && eq(phc[i*2], low) && eq(phc[i*2+1], upp)) {
result.add(values[i]);
}
}
return result.iterator();
});
}

private boolean eq(double[] a, double[] b) {
for (int i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
Expand Down Expand Up @@ -89,38 +169,21 @@ private boolean leq(double[] a, double[] b) {
}

@Override
public AQueryIterator queryIntersect(double[] min, double[] max) {
return new AQueryIterator(min, max);
}

private class AQueryIterator implements QueryIterator<RectangleEntry<T>> {

private Iterator<RectangleEntry<T>> it;

public AQueryIterator(double[] min, double[] max) {
reset(min, max);
}

@Override
public boolean hasNext() {
return it.hasNext();
}

@Override
public RectangleEntry<T> next() {
return it.next();
}

@Override
public void reset(double[] min, double[] max) {
ArrayList<RectangleEntry<T>> results = new ArrayList<>();
for (int i = 0; i < N; i++) {
if (leq(phc[i*2], max) && geq(phc[i*2+1], min)) {
public QueryIterator<RectangleEntry<T>> queryIntersect(double[] min, double[] max) {
return new QueryIteratorWrapper<>(min, max, (lower, upper) -> {
ArrayList<RectangleEntry<T>> results = new ArrayList<>();
for (int i = 0; i < N; i++) {
if (leq(phc[i*2], upper) && geq(phc[i*2+1], lower)) {
results.add(values[i]);
}
}
it = results.iterator();
}
return results.iterator();
});
}

@Override
public RectangleEntryDist<T> query1NN(double[] center) {
return queryKNN(center, 1).next();
}

@Override
Expand All @@ -131,10 +194,15 @@ public QueryIterator<RectangleEntry<T>> iterator() {
}

@Override
public AQueryIteratorKNN queryKNN(double[] center, int k) {
public QueryIteratorKNN<RectangleEntryDist<T>> queryKNN(double[] center, int k) {
return new AQueryIteratorKNN(center, k);
}

@Override
public QueryIteratorKNN<RectangleEntryDist<T>> queryKNN(double[] center, int k, RectangleDistanceFunction distFn) {
return null;
}


private class AQueryIteratorKNN implements QueryIteratorKNN<RectangleEntryDist<T>> {

Expand Down Expand Up @@ -195,12 +263,7 @@ private static double distREdge(double[] center, double[] rLower, double[] rUppe
return Math.sqrt(dist);
}

private final Comparator<KnnEntry<T>> COMP = new Comparator<KnnEntry<T>>() {
@Override
public int compare(KnnEntry<T> o1, KnnEntry<T> o2) {
return o1.compareTo(o2);
}
};
private final Comparator<KnnEntry<T>> COMP = KnnEntry::compareTo;

private static class KnnEntry<T> implements Comparable<KnnEntry<T>>, RectangleEntryDist<T> {
private final double[] min;
Expand Down Expand Up @@ -241,37 +304,6 @@ public double dist() {
}
}

@Override
public T update(double[] lo1, double[] up1, double[] lo2, double[] up2) {
for (int i = 0; i < N; i++) {
if (eq(phc[i*2], lo1) && eq(phc[(i*2)+1], up1)) {
System.arraycopy(lo2, 0, phc[i*2], 0, dims);
System.arraycopy(up2, 0, phc[(i*2)+1], 0, dims);
return values[i].value();
}
}
return null;
}





@Override
public T remove(double[] lower, double[] upper) {
for (int i = 0; i < N; i++) {
if (phc[i*2] != null && eq(phc[i*2], lower)
&& eq(phc[(i*2)+1], upper)) {
phc[i*2] = null;
phc[(i*2)+1] = null;
T val = values[i].value();
return val;
}
}
return null;
}


@Override
public String toString() {
return "NaiveArray";
Expand All @@ -284,7 +316,7 @@ public int getDims() {

@Override
public int size() {
return N;
return size;
}

@Override
Expand All @@ -296,6 +328,7 @@ public void clear() {
phc[i] = null;
}
N = 0;
size = 0;
}

@Override
Expand All @@ -318,8 +351,7 @@ public int getDepth() {
public String toStringTree() {
StringBuilder s = new StringBuilder();
for (int i = 0; i < N; i++) {
s.append(Arrays.toString(phc[i*2]) + "/" + Arrays.toString(phc[i*2+1]) +
" v=" + values[i]);
s.append(Arrays.toString(phc[i * 2])).append("/").append(Arrays.toString(phc[i * 2 + 1])).append(" v=").append(values[i]);
}
return s.toString();
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/tinspin/index/kdtree/KDEntryDist.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.Comparator;

import org.tinspin.index.PointEntryDist;
import org.tinspin.index.rtree.Entry;

public class KDEntryDist<T> implements PointEntryDist<T> {

Expand Down Expand Up @@ -74,5 +75,4 @@ public double[] point() {
public T value() {
return entry.value();
}

}
}
Loading