@samyok/annoy 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +191 -0
- package/README.md +217 -0
- package/binding.gyp +33 -0
- package/deps/annoy/LICENSE +202 -0
- package/deps/annoy/src/annoylib.h +1597 -0
- package/deps/annoy/src/kissrandom.h +120 -0
- package/deps/annoy/src/mman.h +242 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +103 -0
- package/dist/test.d.ts +1 -0
- package/dist/test.js +171 -0
- package/package.json +59 -0
- package/src/addon.cc +353 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#ifndef ANNOY_KISSRANDOM_H
|
|
2
|
+
#define ANNOY_KISSRANDOM_H
|
|
3
|
+
|
|
4
|
+
#if defined(_MSC_VER) && _MSC_VER == 1500
|
|
5
|
+
typedef unsigned __int32 uint32_t;
|
|
6
|
+
typedef unsigned __int64 uint64_t;
|
|
7
|
+
#else
|
|
8
|
+
#include <stdint.h>
|
|
9
|
+
#endif
|
|
10
|
+
|
|
11
|
+
namespace Annoy {
|
|
12
|
+
|
|
13
|
+
// KISS = "keep it simple, stupid", but high quality random number generator
|
|
14
|
+
// http://www0.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf -> "Use a good RNG and build it into your code"
|
|
15
|
+
// http://mathforum.org/kb/message.jspa?messageID=6627731
|
|
16
|
+
// https://de.wikipedia.org/wiki/KISS_(Zufallszahlengenerator)
|
|
17
|
+
|
|
18
|
+
// 32 bit KISS
|
|
19
|
+
struct Kiss32Random {
|
|
20
|
+
uint32_t x;
|
|
21
|
+
uint32_t y;
|
|
22
|
+
uint32_t z;
|
|
23
|
+
uint32_t c;
|
|
24
|
+
|
|
25
|
+
static const uint32_t default_seed = 123456789;
|
|
26
|
+
#if __cplusplus < 201103L
|
|
27
|
+
typedef uint32_t seed_type;
|
|
28
|
+
#endif
|
|
29
|
+
|
|
30
|
+
// seed must be != 0
|
|
31
|
+
Kiss32Random(uint32_t seed = default_seed) {
|
|
32
|
+
x = seed;
|
|
33
|
+
y = 362436000;
|
|
34
|
+
z = 521288629;
|
|
35
|
+
c = 7654321;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
uint32_t kiss() {
|
|
39
|
+
// Linear congruence generator
|
|
40
|
+
x = 69069 * x + 12345;
|
|
41
|
+
|
|
42
|
+
// Xor shift
|
|
43
|
+
y ^= y << 13;
|
|
44
|
+
y ^= y >> 17;
|
|
45
|
+
y ^= y << 5;
|
|
46
|
+
|
|
47
|
+
// Multiply-with-carry
|
|
48
|
+
uint64_t t = 698769069ULL * z + c;
|
|
49
|
+
c = t >> 32;
|
|
50
|
+
z = (uint32_t) t;
|
|
51
|
+
|
|
52
|
+
return x + y + z;
|
|
53
|
+
}
|
|
54
|
+
inline int flip() {
|
|
55
|
+
// Draw random 0 or 1
|
|
56
|
+
return kiss() & 1;
|
|
57
|
+
}
|
|
58
|
+
inline size_t index(size_t n) {
|
|
59
|
+
// Draw random integer between 0 and n-1 where n is at most the number of data points you have
|
|
60
|
+
return kiss() % n;
|
|
61
|
+
}
|
|
62
|
+
inline void set_seed(uint32_t seed) {
|
|
63
|
+
x = seed;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// 64 bit KISS. Use this if you have more than about 2^24 data points ("big data" ;) )
|
|
68
|
+
struct Kiss64Random {
|
|
69
|
+
uint64_t x;
|
|
70
|
+
uint64_t y;
|
|
71
|
+
uint64_t z;
|
|
72
|
+
uint64_t c;
|
|
73
|
+
|
|
74
|
+
static const uint64_t default_seed = 1234567890987654321ULL;
|
|
75
|
+
#if __cplusplus < 201103L
|
|
76
|
+
typedef uint64_t seed_type;
|
|
77
|
+
#endif
|
|
78
|
+
|
|
79
|
+
// seed must be != 0
|
|
80
|
+
Kiss64Random(uint64_t seed = default_seed) {
|
|
81
|
+
x = seed;
|
|
82
|
+
y = 362436362436362436ULL;
|
|
83
|
+
z = 1066149217761810ULL;
|
|
84
|
+
c = 123456123456123456ULL;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
uint64_t kiss() {
|
|
88
|
+
// Linear congruence generator
|
|
89
|
+
z = 6906969069LL*z+1234567;
|
|
90
|
+
|
|
91
|
+
// Xor shift
|
|
92
|
+
y ^= (y<<13);
|
|
93
|
+
y ^= (y>>17);
|
|
94
|
+
y ^= (y<<43);
|
|
95
|
+
|
|
96
|
+
// Multiply-with-carry (uint128_t t = (2^58 + 1) * x + c; c = t >> 64; x = (uint64_t) t)
|
|
97
|
+
uint64_t t = (x<<58)+c;
|
|
98
|
+
c = (x>>6);
|
|
99
|
+
x += t;
|
|
100
|
+
c += (x<t);
|
|
101
|
+
|
|
102
|
+
return x + y + z;
|
|
103
|
+
}
|
|
104
|
+
inline int flip() {
|
|
105
|
+
// Draw random 0 or 1
|
|
106
|
+
return kiss() & 1;
|
|
107
|
+
}
|
|
108
|
+
inline size_t index(size_t n) {
|
|
109
|
+
// Draw random integer between 0 and n-1 where n is at most the number of data points you have
|
|
110
|
+
return kiss() % n;
|
|
111
|
+
}
|
|
112
|
+
inline void set_seed(uint64_t seed) {
|
|
113
|
+
x = seed;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#endif
|
|
120
|
+
// vim: tabstop=2 shiftwidth=2
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
|
|
2
|
+
// This is from https://code.google.com/p/mman-win32/
|
|
3
|
+
//
|
|
4
|
+
// Licensed under MIT
|
|
5
|
+
|
|
6
|
+
#ifndef _MMAN_WIN32_H
|
|
7
|
+
#define _MMAN_WIN32_H
|
|
8
|
+
|
|
9
|
+
#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later.
|
|
10
|
+
#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows.
|
|
11
|
+
#endif
|
|
12
|
+
|
|
13
|
+
#include <sys/types.h>
|
|
14
|
+
#include <windows.h>
|
|
15
|
+
#include <errno.h>
|
|
16
|
+
#include <io.h>
|
|
17
|
+
|
|
18
|
+
#define PROT_NONE 0
|
|
19
|
+
#define PROT_READ 1
|
|
20
|
+
#define PROT_WRITE 2
|
|
21
|
+
#define PROT_EXEC 4
|
|
22
|
+
|
|
23
|
+
#define MAP_FILE 0
|
|
24
|
+
#define MAP_SHARED 1
|
|
25
|
+
#define MAP_PRIVATE 2
|
|
26
|
+
#define MAP_TYPE 0xf
|
|
27
|
+
#define MAP_FIXED 0x10
|
|
28
|
+
#define MAP_ANONYMOUS 0x20
|
|
29
|
+
#define MAP_ANON MAP_ANONYMOUS
|
|
30
|
+
|
|
31
|
+
#define MAP_FAILED ((void *)-1)
|
|
32
|
+
|
|
33
|
+
/* Flags for msync. */
|
|
34
|
+
#define MS_ASYNC 1
|
|
35
|
+
#define MS_SYNC 2
|
|
36
|
+
#define MS_INVALIDATE 4
|
|
37
|
+
|
|
38
|
+
#ifndef FILE_MAP_EXECUTE
|
|
39
|
+
#define FILE_MAP_EXECUTE 0x0020
|
|
40
|
+
#endif
|
|
41
|
+
|
|
42
|
+
static int __map_mman_error(const DWORD err, const int deferr)
|
|
43
|
+
{
|
|
44
|
+
if (err == 0)
|
|
45
|
+
return 0;
|
|
46
|
+
//TODO: implement
|
|
47
|
+
return err;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static DWORD __map_mmap_prot_page(const int prot)
|
|
51
|
+
{
|
|
52
|
+
DWORD protect = 0;
|
|
53
|
+
|
|
54
|
+
if (prot == PROT_NONE)
|
|
55
|
+
return protect;
|
|
56
|
+
|
|
57
|
+
if ((prot & PROT_EXEC) != 0)
|
|
58
|
+
{
|
|
59
|
+
protect = ((prot & PROT_WRITE) != 0) ?
|
|
60
|
+
PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ;
|
|
61
|
+
}
|
|
62
|
+
else
|
|
63
|
+
{
|
|
64
|
+
protect = ((prot & PROT_WRITE) != 0) ?
|
|
65
|
+
PAGE_READWRITE : PAGE_READONLY;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return protect;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static DWORD __map_mmap_prot_file(const int prot)
|
|
72
|
+
{
|
|
73
|
+
DWORD desiredAccess = 0;
|
|
74
|
+
|
|
75
|
+
if (prot == PROT_NONE)
|
|
76
|
+
return desiredAccess;
|
|
77
|
+
|
|
78
|
+
if ((prot & PROT_READ) != 0)
|
|
79
|
+
desiredAccess |= FILE_MAP_READ;
|
|
80
|
+
if ((prot & PROT_WRITE) != 0)
|
|
81
|
+
desiredAccess |= FILE_MAP_WRITE;
|
|
82
|
+
if ((prot & PROT_EXEC) != 0)
|
|
83
|
+
desiredAccess |= FILE_MAP_EXECUTE;
|
|
84
|
+
|
|
85
|
+
return desiredAccess;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
inline void* mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
|
|
89
|
+
{
|
|
90
|
+
HANDLE fm, h;
|
|
91
|
+
|
|
92
|
+
void * map = MAP_FAILED;
|
|
93
|
+
|
|
94
|
+
#ifdef _MSC_VER
|
|
95
|
+
#pragma warning(push)
|
|
96
|
+
#pragma warning(disable: 4293)
|
|
97
|
+
#endif
|
|
98
|
+
|
|
99
|
+
const DWORD dwFileOffsetLow = (sizeof(off_t) <= sizeof(DWORD)) ?
|
|
100
|
+
(DWORD)off : (DWORD)(off & 0xFFFFFFFFL);
|
|
101
|
+
const DWORD dwFileOffsetHigh = (sizeof(off_t) <= sizeof(DWORD)) ?
|
|
102
|
+
(DWORD)0 : (DWORD)((off >> 32) & 0xFFFFFFFFL);
|
|
103
|
+
const DWORD protect = __map_mmap_prot_page(prot);
|
|
104
|
+
const DWORD desiredAccess = __map_mmap_prot_file(prot);
|
|
105
|
+
|
|
106
|
+
const off_t maxSize = off + (off_t)len;
|
|
107
|
+
|
|
108
|
+
const DWORD dwMaxSizeLow = (sizeof(off_t) <= sizeof(DWORD)) ?
|
|
109
|
+
(DWORD)maxSize : (DWORD)(maxSize & 0xFFFFFFFFL);
|
|
110
|
+
const DWORD dwMaxSizeHigh = (sizeof(off_t) <= sizeof(DWORD)) ?
|
|
111
|
+
(DWORD)0 : (DWORD)((maxSize >> 32) & 0xFFFFFFFFL);
|
|
112
|
+
|
|
113
|
+
#ifdef _MSC_VER
|
|
114
|
+
#pragma warning(pop)
|
|
115
|
+
#endif
|
|
116
|
+
|
|
117
|
+
errno = 0;
|
|
118
|
+
|
|
119
|
+
if (len == 0
|
|
120
|
+
/* Unsupported flag combinations */
|
|
121
|
+
|| (flags & MAP_FIXED) != 0
|
|
122
|
+
/* Usupported protection combinations */
|
|
123
|
+
|| prot == PROT_EXEC)
|
|
124
|
+
{
|
|
125
|
+
errno = EINVAL;
|
|
126
|
+
return MAP_FAILED;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
h = ((flags & MAP_ANONYMOUS) == 0) ?
|
|
130
|
+
(HANDLE)_get_osfhandle(fildes) : INVALID_HANDLE_VALUE;
|
|
131
|
+
|
|
132
|
+
if ((flags & MAP_ANONYMOUS) == 0 && h == INVALID_HANDLE_VALUE)
|
|
133
|
+
{
|
|
134
|
+
errno = EBADF;
|
|
135
|
+
return MAP_FAILED;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
fm = CreateFileMapping(h, NULL, protect, dwMaxSizeHigh, dwMaxSizeLow, NULL);
|
|
139
|
+
|
|
140
|
+
if (fm == NULL)
|
|
141
|
+
{
|
|
142
|
+
errno = __map_mman_error(GetLastError(), EPERM);
|
|
143
|
+
return MAP_FAILED;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
map = MapViewOfFile(fm, desiredAccess, dwFileOffsetHigh, dwFileOffsetLow, len);
|
|
147
|
+
|
|
148
|
+
CloseHandle(fm);
|
|
149
|
+
|
|
150
|
+
if (map == NULL)
|
|
151
|
+
{
|
|
152
|
+
errno = __map_mman_error(GetLastError(), EPERM);
|
|
153
|
+
return MAP_FAILED;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return map;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
inline int munmap(void *addr, size_t len)
|
|
160
|
+
{
|
|
161
|
+
if (UnmapViewOfFile(addr))
|
|
162
|
+
return 0;
|
|
163
|
+
|
|
164
|
+
errno = __map_mman_error(GetLastError(), EPERM);
|
|
165
|
+
|
|
166
|
+
return -1;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
inline int mprotect(void *addr, size_t len, int prot)
|
|
170
|
+
{
|
|
171
|
+
DWORD newProtect = __map_mmap_prot_page(prot);
|
|
172
|
+
DWORD oldProtect = 0;
|
|
173
|
+
|
|
174
|
+
if (VirtualProtect(addr, len, newProtect, &oldProtect))
|
|
175
|
+
return 0;
|
|
176
|
+
|
|
177
|
+
errno = __map_mman_error(GetLastError(), EPERM);
|
|
178
|
+
|
|
179
|
+
return -1;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
inline int msync(void *addr, size_t len, int flags)
|
|
183
|
+
{
|
|
184
|
+
if (FlushViewOfFile(addr, len))
|
|
185
|
+
return 0;
|
|
186
|
+
|
|
187
|
+
errno = __map_mman_error(GetLastError(), EPERM);
|
|
188
|
+
|
|
189
|
+
return -1;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
inline int mlock(const void *addr, size_t len)
|
|
193
|
+
{
|
|
194
|
+
if (VirtualLock((LPVOID)addr, len))
|
|
195
|
+
return 0;
|
|
196
|
+
|
|
197
|
+
errno = __map_mman_error(GetLastError(), EPERM);
|
|
198
|
+
|
|
199
|
+
return -1;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
inline int munlock(const void *addr, size_t len)
|
|
203
|
+
{
|
|
204
|
+
if (VirtualUnlock((LPVOID)addr, len))
|
|
205
|
+
return 0;
|
|
206
|
+
|
|
207
|
+
errno = __map_mman_error(GetLastError(), EPERM);
|
|
208
|
+
|
|
209
|
+
return -1;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
#if !defined(__MINGW32__)
|
|
213
|
+
inline int ftruncate(const int fd, const int64_t size) {
|
|
214
|
+
if (fd < 0) {
|
|
215
|
+
errno = EBADF;
|
|
216
|
+
return -1;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
HANDLE h = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
|
|
220
|
+
LARGE_INTEGER li_start, li_size;
|
|
221
|
+
li_start.QuadPart = static_cast<int64_t>(0);
|
|
222
|
+
li_size.QuadPart = size;
|
|
223
|
+
if (SetFilePointerEx(h, li_start, NULL, FILE_CURRENT) == ~0 ||
|
|
224
|
+
SetFilePointerEx(h, li_size, NULL, FILE_BEGIN) == ~0 ||
|
|
225
|
+
!SetEndOfFile(h)) {
|
|
226
|
+
unsigned long error = GetLastError();
|
|
227
|
+
fprintf(stderr, "I/O error while truncating: %lu\n", error);
|
|
228
|
+
switch (error) {
|
|
229
|
+
case ERROR_INVALID_HANDLE:
|
|
230
|
+
errno = EBADF;
|
|
231
|
+
break;
|
|
232
|
+
default:
|
|
233
|
+
errno = EIO;
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
return -1;
|
|
237
|
+
}
|
|
238
|
+
return 0;
|
|
239
|
+
}
|
|
240
|
+
#endif
|
|
241
|
+
|
|
242
|
+
#endif
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type Metric = "angular" | "euclidean" | "manhattan" | "dot";
|
|
2
|
+
export interface NearestNeighborsResult {
|
|
3
|
+
neighbors: number[];
|
|
4
|
+
distances: number[];
|
|
5
|
+
}
|
|
6
|
+
export declare class AnnoyIndex {
|
|
7
|
+
private index;
|
|
8
|
+
constructor(f: number, metric?: Metric);
|
|
9
|
+
addItem(index: number, vector: number[]): void;
|
|
10
|
+
build(numTrees: number, numThreads?: number): void;
|
|
11
|
+
unbuild(): void;
|
|
12
|
+
save(filename: string, prefault?: boolean): void;
|
|
13
|
+
load(filename: string, prefault?: boolean): void;
|
|
14
|
+
unload(): void;
|
|
15
|
+
getDistance(i: number, j: number): number;
|
|
16
|
+
getNnsByItem(item: number, n: number, searchK: number, includeDistances: true): NearestNeighborsResult;
|
|
17
|
+
getNnsByItem(item: number, n: number, searchK?: number, includeDistances?: false): number[];
|
|
18
|
+
getNnsByVector(vector: number[], n: number, searchK: number, includeDistances: true): NearestNeighborsResult;
|
|
19
|
+
getNnsByVector(vector: number[], n: number, searchK?: number, includeDistances?: false): number[];
|
|
20
|
+
getNItems(): number;
|
|
21
|
+
getNTrees(): number;
|
|
22
|
+
getItem(item: number): number[];
|
|
23
|
+
setSeed(seed: number): void;
|
|
24
|
+
onDiskBuild(filename: string): void;
|
|
25
|
+
verbose(v: boolean): void;
|
|
26
|
+
getF(): number;
|
|
27
|
+
}
|
|
28
|
+
export default AnnoyIndex;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.AnnoyIndex = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const addon = require(path.join(__dirname, "..", "build", "Release", "annoy.node"));
|
|
39
|
+
class AnnoyIndex {
|
|
40
|
+
constructor(f, metric = "angular") {
|
|
41
|
+
const constructors = {
|
|
42
|
+
angular: addon.AnnoyIndexAngular,
|
|
43
|
+
euclidean: addon.AnnoyIndexEuclidean,
|
|
44
|
+
manhattan: addon.AnnoyIndexManhattan,
|
|
45
|
+
dot: addon.AnnoyIndexDotProduct,
|
|
46
|
+
};
|
|
47
|
+
const Ctor = constructors[metric];
|
|
48
|
+
if (!Ctor) {
|
|
49
|
+
throw new Error(`Unknown metric "${metric}". Use: angular, euclidean, manhattan, dot`);
|
|
50
|
+
}
|
|
51
|
+
this.index = new Ctor(f);
|
|
52
|
+
}
|
|
53
|
+
addItem(index, vector) {
|
|
54
|
+
this.index.addItem(index, vector);
|
|
55
|
+
}
|
|
56
|
+
build(numTrees, numThreads = -1) {
|
|
57
|
+
this.index.build(numTrees, numThreads);
|
|
58
|
+
}
|
|
59
|
+
unbuild() {
|
|
60
|
+
this.index.unbuild();
|
|
61
|
+
}
|
|
62
|
+
save(filename, prefault = false) {
|
|
63
|
+
this.index.save(filename, prefault);
|
|
64
|
+
}
|
|
65
|
+
load(filename, prefault = false) {
|
|
66
|
+
this.index.load(filename, prefault);
|
|
67
|
+
}
|
|
68
|
+
unload() {
|
|
69
|
+
this.index.unload();
|
|
70
|
+
}
|
|
71
|
+
getDistance(i, j) {
|
|
72
|
+
return this.index.getDistance(i, j);
|
|
73
|
+
}
|
|
74
|
+
getNnsByItem(item, n, searchK = -1, includeDistances = false) {
|
|
75
|
+
return this.index.getNnsByItem(item, n, searchK, includeDistances);
|
|
76
|
+
}
|
|
77
|
+
getNnsByVector(vector, n, searchK = -1, includeDistances = false) {
|
|
78
|
+
return this.index.getNnsByVector(vector, n, searchK, includeDistances);
|
|
79
|
+
}
|
|
80
|
+
getNItems() {
|
|
81
|
+
return this.index.getNItems();
|
|
82
|
+
}
|
|
83
|
+
getNTrees() {
|
|
84
|
+
return this.index.getNTrees();
|
|
85
|
+
}
|
|
86
|
+
getItem(item) {
|
|
87
|
+
return this.index.getItem(item);
|
|
88
|
+
}
|
|
89
|
+
setSeed(seed) {
|
|
90
|
+
this.index.setSeed(seed);
|
|
91
|
+
}
|
|
92
|
+
onDiskBuild(filename) {
|
|
93
|
+
this.index.onDiskBuild(filename);
|
|
94
|
+
}
|
|
95
|
+
verbose(v) {
|
|
96
|
+
this.index.verbose(v);
|
|
97
|
+
}
|
|
98
|
+
getF() {
|
|
99
|
+
return this.index.getF();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.AnnoyIndex = AnnoyIndex;
|
|
103
|
+
exports.default = AnnoyIndex;
|
package/dist/test.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/test.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const index_1 = require("./index");
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const DIMS = 40;
|
|
40
|
+
const NUM_ITEMS = 1000;
|
|
41
|
+
const NUM_TREES = 10;
|
|
42
|
+
const tmpFile = path.join(__dirname, "..", "test_index.ann");
|
|
43
|
+
let passed = 0;
|
|
44
|
+
let failed = 0;
|
|
45
|
+
function assert(condition, msg) {
|
|
46
|
+
if (condition) {
|
|
47
|
+
passed++;
|
|
48
|
+
console.log(` PASS: ${msg}`);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
failed++;
|
|
52
|
+
console.error(` FAIL: ${msg}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function randomVector(dims) {
|
|
56
|
+
return Array.from({ length: dims }, () => Math.random() * 2 - 1);
|
|
57
|
+
}
|
|
58
|
+
function cleanup() {
|
|
59
|
+
try {
|
|
60
|
+
fs.unlinkSync(tmpFile);
|
|
61
|
+
}
|
|
62
|
+
catch { }
|
|
63
|
+
}
|
|
64
|
+
console.log("=== annoy-node test suite ===\n");
|
|
65
|
+
// Test 1: Basic build and query (Angular)
|
|
66
|
+
console.log("Test 1: Build and query (Angular)");
|
|
67
|
+
{
|
|
68
|
+
const index = new index_1.AnnoyIndex(DIMS, "angular");
|
|
69
|
+
for (let i = 0; i < NUM_ITEMS; i++) {
|
|
70
|
+
index.addItem(i, randomVector(DIMS));
|
|
71
|
+
}
|
|
72
|
+
index.build(NUM_TREES);
|
|
73
|
+
assert(index.getNItems() === NUM_ITEMS, `getNItems() === ${NUM_ITEMS}`);
|
|
74
|
+
assert(index.getNTrees() === NUM_TREES, `getNTrees() === ${NUM_TREES}`);
|
|
75
|
+
assert(index.getF() === DIMS, `getF() === ${DIMS}`);
|
|
76
|
+
const neighbors = index.getNnsByItem(0, 10);
|
|
77
|
+
assert(Array.isArray(neighbors), "getNnsByItem returns array");
|
|
78
|
+
assert(neighbors.length === 10, "returns 10 neighbors");
|
|
79
|
+
assert(neighbors[0] === 0, "nearest neighbor of item 0 is itself");
|
|
80
|
+
}
|
|
81
|
+
// Test 2: Include distances
|
|
82
|
+
console.log("\nTest 2: Include distances");
|
|
83
|
+
{
|
|
84
|
+
const index = new index_1.AnnoyIndex(DIMS, "angular");
|
|
85
|
+
for (let i = 0; i < 100; i++) {
|
|
86
|
+
index.addItem(i, randomVector(DIMS));
|
|
87
|
+
}
|
|
88
|
+
index.build(NUM_TREES);
|
|
89
|
+
const result = index.getNnsByItem(0, 5, -1, true);
|
|
90
|
+
assert("neighbors" in result && "distances" in result, "result has neighbors and distances");
|
|
91
|
+
assert(result.neighbors.length === 5, "5 neighbors returned");
|
|
92
|
+
assert(result.distances.length === 5, "5 distances returned");
|
|
93
|
+
assert(result.distances[0] === 0, "distance to self is 0");
|
|
94
|
+
}
|
|
95
|
+
// Test 3: getNnsByVector
|
|
96
|
+
console.log("\nTest 3: getNnsByVector");
|
|
97
|
+
{
|
|
98
|
+
const index = new index_1.AnnoyIndex(DIMS, "euclidean");
|
|
99
|
+
const vectors = [];
|
|
100
|
+
for (let i = 0; i < 100; i++) {
|
|
101
|
+
const v = randomVector(DIMS);
|
|
102
|
+
vectors.push(v);
|
|
103
|
+
index.addItem(i, v);
|
|
104
|
+
}
|
|
105
|
+
index.build(NUM_TREES);
|
|
106
|
+
const result = index.getNnsByVector(vectors[0], 5);
|
|
107
|
+
assert(Array.isArray(result), "getNnsByVector returns array");
|
|
108
|
+
assert(result[0] === 0, "closest to vector[0] is item 0");
|
|
109
|
+
}
|
|
110
|
+
// Test 4: Save and load
|
|
111
|
+
console.log("\nTest 4: Save and load");
|
|
112
|
+
{
|
|
113
|
+
cleanup();
|
|
114
|
+
const index = new index_1.AnnoyIndex(DIMS, "angular");
|
|
115
|
+
for (let i = 0; i < 100; i++) {
|
|
116
|
+
index.addItem(i, randomVector(DIMS));
|
|
117
|
+
}
|
|
118
|
+
index.build(NUM_TREES);
|
|
119
|
+
index.save(tmpFile);
|
|
120
|
+
const loaded = new index_1.AnnoyIndex(DIMS, "angular");
|
|
121
|
+
loaded.load(tmpFile);
|
|
122
|
+
assert(loaded.getNItems() === 100, "loaded index has 100 items");
|
|
123
|
+
assert(loaded.getNTrees() === NUM_TREES, `loaded index has ${NUM_TREES} trees`);
|
|
124
|
+
const item = loaded.getItem(0);
|
|
125
|
+
assert(item.length === DIMS, `getItem returns vector of length ${DIMS}`);
|
|
126
|
+
cleanup();
|
|
127
|
+
}
|
|
128
|
+
// Test 5: getDistance
|
|
129
|
+
console.log("\nTest 5: getDistance");
|
|
130
|
+
{
|
|
131
|
+
const index = new index_1.AnnoyIndex(DIMS, "euclidean");
|
|
132
|
+
const v = randomVector(DIMS);
|
|
133
|
+
index.addItem(0, v);
|
|
134
|
+
index.addItem(1, v);
|
|
135
|
+
index.build(1);
|
|
136
|
+
const dist = index.getDistance(0, 1);
|
|
137
|
+
assert(Math.abs(dist) < 1e-6, `distance between identical vectors is ~0 (got ${dist})`);
|
|
138
|
+
}
|
|
139
|
+
// Test 6: All metrics
|
|
140
|
+
console.log("\nTest 6: All metrics");
|
|
141
|
+
{
|
|
142
|
+
const metrics = ["angular", "euclidean", "manhattan", "dot"];
|
|
143
|
+
for (const metric of metrics) {
|
|
144
|
+
const index = new index_1.AnnoyIndex(DIMS, metric);
|
|
145
|
+
for (let i = 0; i < 50; i++) {
|
|
146
|
+
index.addItem(i, randomVector(DIMS));
|
|
147
|
+
}
|
|
148
|
+
index.build(5);
|
|
149
|
+
const nn = index.getNnsByItem(0, 5);
|
|
150
|
+
assert(nn.length === 5, `${metric}: returns 5 neighbors`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Test 7: setSeed reproducibility
|
|
154
|
+
console.log("\nTest 7: setSeed reproducibility");
|
|
155
|
+
{
|
|
156
|
+
const vectors = Array.from({ length: 100 }, () => randomVector(DIMS));
|
|
157
|
+
function buildWithSeed(seed) {
|
|
158
|
+
const idx = new index_1.AnnoyIndex(DIMS, "angular");
|
|
159
|
+
idx.setSeed(seed);
|
|
160
|
+
for (let i = 0; i < 100; i++) {
|
|
161
|
+
idx.addItem(i, vectors[i]);
|
|
162
|
+
}
|
|
163
|
+
idx.build(5);
|
|
164
|
+
return idx.getNnsByItem(0, 10);
|
|
165
|
+
}
|
|
166
|
+
const r1 = buildWithSeed(42);
|
|
167
|
+
const r2 = buildWithSeed(42);
|
|
168
|
+
assert(JSON.stringify(r1) === JSON.stringify(r2), "same seed produces same results");
|
|
169
|
+
}
|
|
170
|
+
console.log(`\n=== Results: ${passed} passed, ${failed} failed ===`);
|
|
171
|
+
process.exit(failed > 0 ? 1 : 0);
|