190 lines
3.7 KiB
Go
190 lines
3.7 KiB
Go
|
package ntree
|
||
|
|
||
|
import (
|
||
|
"math/rand"
|
||
|
"runtime"
|
||
|
"sync"
|
||
|
"testing"
|
||
|
)
|
||
|
|
||
|
var wg sync.WaitGroup
|
||
|
|
||
|
func TestInit(t *testing.T) {
|
||
|
// test all available dimensional combinations
|
||
|
for n := 1; n <= MaxN; n++ {
|
||
|
t.Log("Testing", n, "dimension(s).")
|
||
|
center := make([]float64, n)
|
||
|
bounds := make([]float64, n)
|
||
|
for i := range center {
|
||
|
center[i] = 0
|
||
|
bounds[i] = 1
|
||
|
}
|
||
|
nt, err := New(center, bounds)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if nt.N() != n {
|
||
|
t.Fatal(n, "dimensional tree returned n=", nt.N())
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestAdd(t *testing.T) {
|
||
|
count := 500
|
||
|
max := runtime.GOMAXPROCS(-1)
|
||
|
// > 16 dimensions quickly becomes impractical for memory and processing reasons.
|
||
|
for n := 1; n <= 16; n++ {
|
||
|
t.Log("Testing", n, "dimension(s).")
|
||
|
center := make([]float64, n)
|
||
|
bounds := make([]float64, n)
|
||
|
p1 := make([]float64, n)
|
||
|
for i := range center {
|
||
|
center[i] = 0.0
|
||
|
bounds[i] = 1.0
|
||
|
p1[i] = -1.0
|
||
|
}
|
||
|
nt, err := New(center, bounds)
|
||
|
if err != nil {
|
||
|
t.Fatal(err, n)
|
||
|
}
|
||
|
wg.Add(max)
|
||
|
for i := 0; i < max; i++ {
|
||
|
go func() {
|
||
|
for j := 0; j < count/max; j++ {
|
||
|
p := new(Point)
|
||
|
p.Coords = make([]float64, n)
|
||
|
for j := range p.Coords {
|
||
|
p.Coords[j] = (rand.Float64() * 2.0) - 1.0
|
||
|
}
|
||
|
if err = nt.Add(p); err != nil {
|
||
|
t.Error(err, n, i)
|
||
|
}
|
||
|
}
|
||
|
wg.Done()
|
||
|
}()
|
||
|
}
|
||
|
count = (count / max) * max
|
||
|
|
||
|
wg.Wait()
|
||
|
points, err := nt.Search(p1, bounds)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if len(points) != count {
|
||
|
t.Fatal("Search returned", len(points), "points instead of", count)
|
||
|
}
|
||
|
if nt.Count() != uint64(count) {
|
||
|
t.Error("Tree estimated incorrect, sees", nt.Count(), "points instead of", count)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func benchAdd(b *testing.B, n int) {
|
||
|
b.StopTimer()
|
||
|
max := runtime.GOMAXPROCS(-1)
|
||
|
center := make([]float64, n)
|
||
|
bounds := make([]float64, n)
|
||
|
for i := range center {
|
||
|
center[i] = 0
|
||
|
bounds[i] = 1
|
||
|
}
|
||
|
nt, err := New(center, bounds)
|
||
|
if err != nil {
|
||
|
b.Fatal(err)
|
||
|
}
|
||
|
wg.Add(max)
|
||
|
b.StartTimer()
|
||
|
for i := 0; i < max; i++ {
|
||
|
go func() {
|
||
|
for j := 0; j < b.N/max; j++ {
|
||
|
p := new(Point)
|
||
|
p.Coords = make([]float64, n)
|
||
|
for j := range p.Coords {
|
||
|
p.Coords[j] = (rand.Float64() * 2.0) - 1.0
|
||
|
}
|
||
|
if err = nt.Add(p); err != nil {
|
||
|
b.Error(err)
|
||
|
}
|
||
|
}
|
||
|
wg.Done()
|
||
|
}()
|
||
|
}
|
||
|
wg.Wait()
|
||
|
}
|
||
|
|
||
|
func benchSearch(b *testing.B, n int) {
|
||
|
b.StopTimer()
|
||
|
max := runtime.GOMAXPROCS(-1)
|
||
|
center := make([]float64, n)
|
||
|
bounds := make([]float64, n)
|
||
|
for i := range center {
|
||
|
center[i] = 0
|
||
|
bounds[i] = 1
|
||
|
}
|
||
|
nt, err := New(center, bounds)
|
||
|
if err != nil {
|
||
|
b.Fatal(err)
|
||
|
}
|
||
|
// 10k points gives a reasonable search space.
|
||
|
for i := 0; i < 10000; i++ {
|
||
|
p := new(Point)
|
||
|
p.Coords = make([]float64, n)
|
||
|
for j := range p.Coords {
|
||
|
p.Coords[j] = (rand.Float64() * 2.0) - 1.0
|
||
|
}
|
||
|
if err = nt.Add(p); err != nil {
|
||
|
b.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
var swap float64
|
||
|
var count uint64
|
||
|
wg.Add(max)
|
||
|
|
||
|
b.StartTimer()
|
||
|
for i := 0; i < max; i++ {
|
||
|
go func() {
|
||
|
p1 := make([]float64, n)
|
||
|
p2 := make([]float64, n)
|
||
|
for j := 0; j < (b.N / max); j++ {
|
||
|
// generate random search area
|
||
|
for j := range p1 {
|
||
|
p1[j] = (rand.Float64() * 2.0) - 1.0
|
||
|
p2[j] = (rand.Float64() * 2.0) - 1.0
|
||
|
if p1[j] > p2[j] {
|
||
|
swap = p1[j]
|
||
|
p1[j] = p2[j]
|
||
|
p2[j] = swap
|
||
|
}
|
||
|
}
|
||
|
// find points within it!
|
||
|
points, err := nt.Search(p1, p2)
|
||
|
if err != nil {
|
||
|
b.Fatal(err)
|
||
|
}
|
||
|
count += uint64(len(points))
|
||
|
}
|
||
|
wg.Done()
|
||
|
}()
|
||
|
}
|
||
|
wg.Wait()
|
||
|
if count == 0 && b.N > 100 {
|
||
|
b.Log("Failed to find any points after", b.N, "searches.")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func BenchmarkQuadtreeAdd(b *testing.B) {
|
||
|
benchAdd(b, 2)
|
||
|
}
|
||
|
|
||
|
func BenchmarkQuadtreeSearch(b *testing.B) {
|
||
|
benchSearch(b, 2)
|
||
|
}
|
||
|
|
||
|
func BenchmarkOctreeAdd(b *testing.B) {
|
||
|
benchAdd(b, 3)
|
||
|
}
|
||
|
|
||
|
func BenchmarkOcttreeSearch(b *testing.B) {
|
||
|
benchSearch(b, 3)
|
||
|
}
|