Skip to content

Commit

Permalink
Fix for issue #35 (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
tzaeschke authored Oct 31, 2023
1 parent 15e1973 commit bfb9572
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 7 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

- Nothing yet

## [2.1.2] - 2023-10-31

### Fixed
- Fixed `create()` method for `IndexConfig` not being static [#33](https://github.com/tzaeschke/tinspin-indexes/issue/35)

## [2.1.1] - 2023-10-14

### Fixed
Expand Down Expand Up @@ -146,7 +151,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Added CHANGELOG - [2015-10-11]
- Pushed to v1.3.3-SNAPSHOT - [2015-10-11]

[Unreleased]: https://github.com/tzaeschke/tinspin-indexes/compare/tinspin-indexes-2.1.1...HEAD
[Unreleased]: https://github.com/tzaeschke/tinspin-indexes/compare/tinspin-indexes-2.1.2...HEAD
[2.1.2]: https://github.com/tzaeschke/tinspin-indexes/compare/tinspin-indexes-2.1.1...tinspin-indexes-2.1.2
[2.1.1]: https://github.com/tzaeschke/tinspin-indexes/compare/tinspin-indexes-2.1.0...tinspin-indexes-2.1.1
[2.1.0]: https://github.com/tzaeschke/tinspin-indexes/compare/tinspin-indexes-2.0.1...tinspin-indexes-2.1.0
[2.0.1]: https://github.com/tzaeschke/tinspin-indexes/compare/tinspin-indexes-2.0.0...tinspin-indexes-2.0.1
Expand Down
14 changes: 8 additions & 6 deletions src/main/java/org/tinspin/index/IndexConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ protected IndexConfig(int dimensions) {
this.dimensions = dimensions;
}

public IndexConfig create(int dimensions) {
public static IndexConfig create(int dimensions) {
return new IndexConfig(dimensions);
}

Expand All @@ -34,21 +34,23 @@ public IndexConfig create(int dimensions) {
* Number of dimensions.
* @param dimensions Number of dimensions of keys.
*/
public void setDimensions(int dimensions) {
public IndexConfig setDimensions(int dimensions) {
this.dimensions = dimensions;
return this;
}

/**
* @param defensiveKeyCopy
* Defensive keys copying. If `false`, the kd-tree will store the passed in
* Defensive keys copying. If 'false', the kd-tree will store the passed in
* double[] keys internally (this reduces required memory).
* If `true`, the keys are copied in order to avoid accidental modification.
* The latter obviously requires more memory.
* If 'true', the keys are copied in order to avoid accidental modification.
* The latter obviously requires more memory. Default is 'true'.
* <p>
* This setting works only for kd-trees.
*/
public void setDefensiveKeyCopy(boolean defensiveKeyCopy) {
public IndexConfig setDefensiveKeyCopy(boolean defensiveKeyCopy) {
this.defensiveKeyCopy = defensiveKeyCopy;
return this;
}


Expand Down
181 changes: 181 additions & 0 deletions src/test/java/org/tinspin/index/kdtree/KDTreeConfigTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* Copyright 2009-2017 Tilmann Zaeschke. All rights reserved.
*
* This file is part of TinSpin.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.tinspin.index.kdtree;

import org.junit.Test;
import org.tinspin.index.IndexConfig;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

import static org.junit.Assert.*;
import static org.tinspin.index.Index.PointIterator;
import static org.tinspin.index.Index.PointIteratorKnn;

public class KDTreeConfigTest {

private static final int N_DUP = 4;
private static final int BOUND = 100;

private List<Entry> createInt(long seed, int n, int dim) {
List<Entry> data = new ArrayList<>(n);
Random R = new Random(seed);
for (int i = 0; i < n; i += N_DUP) {
Entry e = new Entry(dim, i);
data.add(e);
Arrays.setAll(e.p, (x) -> R.nextInt(BOUND));
for (int i2 = 1; i2 < N_DUP; ++i2) {
Entry e2 = new Entry(dim, i + i2);
data.add(e2);
System.arraycopy(e.p, 0, e2.p, 0, e.p.length);
}
}
return data;
}

@Test
public void smokeTest2D() {
smokeTest(createInt(0, 100_000, 2));
}

@Test
public void smokeTest3D() {
smokeTest(createInt(0, 10_000, 3));
}

@Test
public void testDefensiveCopyingDefault() {
IndexConfig config = IndexConfig.create(42);
assertEquals(42, config.getDimensions());
assertTrue(config.getDefensiveKeyCopy());
config.setDefensiveKeyCopy(false);
assertFalse(config.getDefensiveKeyCopy());
}

@Test
public void testDefensiveCopyingFalse() {
IndexConfig config = IndexConfig.create(3).setDefensiveKeyCopy(false);
KDTree<Entry> tree = KDTree.create(config);

double[] point = new double[]{1, 2, 3};
double[] pointOriginal = point.clone();
Entry e = new Entry(point, 42);
tree.insert(point, e);

// Never do this! We just verify that the key is not copied.
point[1] = 15;
assertTrue(tree.contains(point));
assertFalse(tree.contains(pointOriginal));
}

@Test
public void testDefensiveCopyingTrue() {
IndexConfig config = IndexConfig.create(3).setDefensiveKeyCopy(true);
KDTree<Entry> tree = KDTree.create(config);

double[] point = new double[]{1, 2, 3};
double[] pointOriginal = point.clone();
Entry e = new Entry(point, 42);
tree.insert(point, e);

// Never do this! We just verify that the key is not copied.
point[1] = 15;
assertFalse(tree.contains(point));
assertTrue(tree.contains(pointOriginal));
}

private void smokeTest(List<Entry> data) {
int dim = data.get(0).p.length;
IndexConfig config = IndexConfig.create(dim).setDefensiveKeyCopy(false);
KDTree<Entry> tree = KDTree.create(config);
for (Entry e : data) {
tree.insert(e.p, e);
}
// System.out.println(tree.toStringTree());
for (Entry e : data) {
if (!tree.contains(e.p)) {
throw new IllegalStateException(Arrays.toString(e.p));
}
}

for (Entry e : data) {
// System.out.println("kNN query: " + e);
PointIteratorKnn<Entry> iter = tree.queryKnn(e.p, N_DUP);
if (!iter.hasNext()) {
throw new IllegalStateException("kNN() failed: " + Arrays.toString(e.p));
}
Entry answer = iter.next().value();
if (answer.p != e.p && !Arrays.equals(answer.p, e.p)) {
throw new IllegalStateException("Expected " + Arrays.toString(e.p) + " but got " + Arrays.toString(answer.p));
}
}

for (Entry e : data) {
// System.out.println("query: " + Arrays.toString(e.p));
PointIterator<Entry> iter = tree.query(e.p, e.p);
if (!iter.hasNext()) {
throw new IllegalStateException("query() failed: " + Arrays.toString(e.p));
}
for (int i = 0; i < N_DUP; ++i) {
// System.out.println(" found: " + i + " " + e);
Entry answer = iter.next().value();
if (!Arrays.equals(answer.p, e.p)) {
throw new IllegalStateException("Expected " + e + " but got " + answer);
}
}
}

for (Entry e : data) {
// System.out.println(tree.toStringTree());
// System.out.println("Removing: " + Arrays.toString(key));
if (!tree.contains(e.p)) {
throw new IllegalStateException("containsExact() failed: " + Arrays.toString(e.p));
}
Entry answer = tree.remove(e.p);
if (answer.p != e.p && !Arrays.equals(answer.p, e.p)) {
throw new IllegalStateException("Expected " + Arrays.toString(e.p) + " but got " + Arrays.toString(answer.p));
}
}
}

private static class Entry {
double[] p;
int id;

public Entry(int dim, int id) {
this.p = new double[dim];
this.id = id;
}

public Entry(double[] key, int id) {
this.p = key;
this.id = id;
}

boolean equals(Entry e) {
return id == e.id && Arrays.equals(p, e.p);
}

@Override
public String toString() {
return "id=" + id + ":" + Arrays.toString(p);
}
}
}

0 comments on commit bfb9572

Please sign in to comment.