blob: b1aa341e96c131b695727a159b2538413d27d614 [file] [log] [blame]
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001#include <collocatordb.h>
Marc Kupietz969cab92019-08-05 11:13:42 +02002#include <math.h>
3#include <pthread.h>
4#include <stdio.h>
Marc Kupietzc0d41872021-02-25 16:33:22 +01005#include <stdlib.h>
Marc Kupietz969cab92019-08-05 11:13:42 +02006#include <string.h>
7#include <sys/mman.h>
Marc Kupietze288d8e2024-11-15 16:18:50 +01008#include <fcntl.h>
9#include <unistd.h>
10#include <perl.h>
Marc Kupietzf11d20c2019-08-02 15:42:04 +020011
12#define max_size 2000
13#define max_w 50
14#define MAX_NEIGHBOURS 1000
15#define MAX_WORDS -1
16#define MAX_THREADS 100
17#define MAX_CC 50
18#define EXP_TABLE_SIZE 1000
19#define MAX_EXP 6
20#define MIN_RESP 0.50
21
Marc Kupietzc48d3902026-08-21 12:13:45 +020022/* Per request diagnostics. Every neighbourhood request used to write a few
23 hundred lines - one per window position, one per vocabulary lookup, the
24 whole JSON of a similar profile - and a busy instance turned that into a
25 million lines a day, which is what filled the log partition of the
26 production machine. They are off unless DEREKOVECS_DEBUG is set to
27 something other than 0 or the empty string. Startup messages and error
28 messages are not affected. */
29static int derekovecs_debug(void) {
30 static int enabled = -1;
31 if (enabled < 0) {
32 const char *e = getenv("DEREKOVECS_DEBUG");
33 enabled = (e && *e && strcmp(e, "0") != 0) ? 1 : 0;
34 }
35 return enabled;
36}
37
38#define DEBUG_PRINTF(...) do { if (derekovecs_debug()) printf(__VA_ARGS__); } while (0)
39#define DEBUG_EPRINTF(...) do { if (derekovecs_debug()) fprintf(stderr, __VA_ARGS__); } while (0)
40#define DEBUG_FFLUSH() do { if (derekovecs_debug()) fflush(stdout); } while (0)
41
Marc Kupietzf11d20c2019-08-02 15:42:04 +020042//the thread function
43void *connection_handler(void *);
44
45typedef struct {
Marc Kupietz969cab92019-08-05 11:13:42 +020046 long long wordi;
47 long position;
48 float activation;
49 float average;
50 float cprobability; // column wise probability
51 float cprobability_sum;
52 float probability;
53 float activation_sum;
54 float max_activation;
55 float heat[16];
Marc Kupietzf11d20c2019-08-02 15:42:04 +020056} collocator;
57
58typedef struct {
Marc Kupietz969cab92019-08-05 11:13:42 +020059 collocator *best;
60 int length;
Marc Kupietzf11d20c2019-08-02 15:42:04 +020061} knn;
Marc Kupietz969cab92019-08-05 11:13:42 +020062
Marc Kupietzf11d20c2019-08-02 15:42:04 +020063typedef struct {
64 long long wordi[MAX_NEIGHBOURS];
65 char sep[MAX_NEIGHBOURS];
66 int length;
67} wordlist;
68
69typedef struct {
70 long cutoff;
71 wordlist *wl;
Marc Kupietz969cab92019-08-05 11:13:42 +020072 char *token;
73 int N;
74 long from;
75 unsigned long upto;
Marc Kupietzf11d20c2019-08-02 15:42:04 +020076 collocator *best;
77 float *target_sums;
78 float *window_sums;
79 float threshold;
80} knnpars;
81
82typedef struct {
83 uint32_t index;
84 float value;
85} sparse_t;
86
87typedef struct {
88 uint32_t len;
89 sparse_t nbr[100];
90} profile_t;
91
Marc Kupietz969cab92019-08-05 11:13:42 +020092float *M, *M2 = 0L, *syn1neg_window, *expTable;
Marc Kupietzf11d20c2019-08-02 15:42:04 +020093char *vocab;
94char *garbage = NULL;
95COLLOCATORDB *cdb = NULL;
96profile_t *sprofiles = NULL;
97size_t sprofiles_qty = 0;
98
99long long words, size, merged_end;
100long long merge_words = 0;
Marc Kupietz969cab92019-08-05 11:13:42 +0200101int num_threads = 20;
102int latin_enc = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200103int window;
104
105/* load collocation profiles if file exists */
106int load_sprofiles(char *vecsname) {
107 char *basename = strdup(vecsname);
108 char *pos = strstr(basename, ".vecs");
Marc Kupietz969cab92019-08-05 11:13:42 +0200109 if (pos)
110 *pos = 0;
111
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200112 char binsprofiles_fname[256];
113 strcpy(binsprofiles_fname, basename);
Marc Kupietz969cab92019-08-05 11:13:42 +0200114 strcat(binsprofiles_fname, ".sprofiles.bin");
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200115 FILE *fp = fopen(binsprofiles_fname, "rb");
116 if (fp == NULL) {
117 printf("Collocation profiles %s not found. No problem.\n", binsprofiles_fname);
118 return 0;
119 }
120 fseek(fp, 0L, SEEK_END);
121 size_t sz = ftell(fp);
122 fclose(fp);
123
124 int fd = open(binsprofiles_fname, O_RDONLY);
Marc Kupietz969cab92019-08-05 11:13:42 +0200125 sprofiles = mmap(0, sz, PROT_READ, MAP_SHARED, fd, 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200126 if (sprofiles == MAP_FAILED) {
127 close(fd);
128 fprintf(stderr, "Cannot mmap %s\n", binsprofiles_fname);
129 sprofiles = NULL;
130 return 0;
Marc Kupietz969cab92019-08-05 11:13:42 +0200131 } else {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200132 sprofiles_qty = sz / sizeof(profile_t);
133 fprintf(stderr, "Successfully mmaped %s containing similar profiles for %ld word forms.\n", binsprofiles_fname, sprofiles_qty);
134 }
135 return 1;
136}
137
Marc Kupietzc0d41872021-02-25 16:33:22 +0100138char *removeExtension(char* myStr) {
139 char *retStr;
140 char *lastExt;
141 if (myStr == NULL) return NULL;
142 if ((retStr = malloc (strlen (myStr) + 1)) == NULL) return NULL;
143 strcpy (retStr, myStr);
144 lastExt = strrchr (retStr, '.');
145 if (lastExt != NULL)
146 *lastExt = '\0';
147 return retStr;
148}
149
Marc Kupietz0efe49b2020-04-06 18:30:22 +0200150int init_net(char *file_name, char *net_name, int latin, int do_open_cdb) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200151 FILE *f, *binvecs, *binwords;
Marc Kupietz969cab92019-08-05 11:13:42 +0200152 int binwords_fd, binvecs_fd, net_fd, i;
Marc Kupietz59865a92021-03-11 17:16:51 +0100153 long long a, b;
Marc Kupietz969cab92019-08-05 11:13:42 +0200154 float len;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200155 double val;
156
Marc Kupietzc0d41872021-02-25 16:33:22 +0100157 char binvecs_fname[1024], binwords_fname[1024];
158
159 if (strstr(file_name, ".txt")) {
160 strcpy(binwords_fname, removeExtension(file_name));
161 } else {
162 strcpy(binwords_fname, file_name);
163 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200164 strcat(binwords_fname, ".words");
165 strcpy(binvecs_fname, file_name);
166 strcat(binvecs_fname, ".vecs");
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200167
168 latin_enc = latin;
169 f = fopen(file_name, "rb");
170 if (f == NULL) {
171 printf("Input file %s not found\n", file_name);
172 return -1;
173 }
174 fscanf(f, "%lld", &words);
Marc Kupietz969cab92019-08-05 11:13:42 +0200175 if (MAX_WORDS > 0 && words > MAX_WORDS) words = MAX_WORDS;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200176 fscanf(f, "%lld", &size);
Marc Kupietz969cab92019-08-05 11:13:42 +0200177 if ((binvecs_fd = open(binvecs_fname, O_RDONLY)) < 0 || (binwords_fd = open(binwords_fname, O_RDONLY)) < 0) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200178 printf("Converting %s to memory mappable structures\n", file_name);
Marc Kupietz969cab92019-08-05 11:13:42 +0200179 vocab = (char *)malloc((long long)words * max_w * sizeof(char));
180 M = (float *)malloc((long long)words * (long long)size * sizeof(float));
181 if (M == NULL) {
182 printf("Cannot allocate memory: %lld MB %lld %lld\n", (long long)words * size * sizeof(float) / 1048576, words, size);
183 return -1;
184 }
185 if (strstr(file_name, ".txt")) {
Marc Kupietzc0d41872021-02-25 16:33:22 +0100186 printf("%lld words in ascii vector file with vector size %lld\n", words, size);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200187 for (b = 0; b < words; b++) {
188 a = 0;
189 while (1) {
190 vocab[b * max_w + a] = fgetc(f);
191 if (feof(f) || (vocab[b * max_w + a] == ' ')) break;
192 if ((a < max_w) && (vocab[b * max_w + a] != '\n')) a++;
193 }
194 vocab[b * max_w + a] = 0;
195 len = 0;
196 for (a = 0; a < size; a++) {
197 fscanf(f, "%lf", &val);
198 M[a + b * size] = val;
199 len += val * val;
Marc Kupietz969cab92019-08-05 11:13:42 +0200200 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200201 len = sqrt(len);
202 for (a = 0; a < size; a++) M[a + b * size] /= len;
203 }
204 } else {
205 for (b = 0; b < words; b++) {
206 a = 0;
207 while (1) {
208 vocab[b * max_w + a] = fgetc(f);
209 if (feof(f) || (vocab[b * max_w + a] == ' ')) break;
210 if ((a < max_w) && (vocab[b * max_w + a] != '\n')) a++;
211 }
212 vocab[b * max_w + a] = 0;
213 fread(&M[b * size], sizeof(float), size, f);
214 len = 0;
215 for (a = 0; a < size; a++) len += M[a + b * size] * M[a + b * size];
216 len = sqrt(len);
217 for (a = 0; a < size; a++) M[a + b * size] /= len;
218 }
219 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200220 if ((binvecs = fopen(binvecs_fname, "wb")) != NULL && (binwords = fopen(binwords_fname, "wb")) != NULL) {
221 fwrite(M, sizeof(float), (long long)words * (long long)size, binvecs);
222 fclose(binvecs);
223 fwrite(vocab, sizeof(char), (long long)words * max_w, binwords);
224 fclose(binwords);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200225 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200226 }
227 if ((binvecs_fd = open(binvecs_fname, O_RDONLY)) >= 0 && (binwords_fd = open(binwords_fname, O_RDONLY)) >= 0) {
228 M = mmap(0, sizeof(float) * (long long)words * (long long)size, PROT_READ, MAP_SHARED, binvecs_fd, 0);
229 vocab = mmap(0, sizeof(char) * (long long)words * max_w, PROT_READ, MAP_SHARED, binwords_fd, 0);
230 if (M == MAP_FAILED || vocab == MAP_FAILED) {
231 close(binvecs_fd);
232 close(binwords_fd);
233 fprintf(stderr, "Cannot mmap %s or %s\n", binwords_fname, binvecs_fname);
234 exit(-1);
235 }
236 } else {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200237 fprintf(stderr, "Cannot open %s or %s\n", binwords_fname, binvecs_fname);
238 exit(-1);
Marc Kupietz969cab92019-08-05 11:13:42 +0200239 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200240 fclose(f);
241
Marc Kupietz969cab92019-08-05 11:13:42 +0200242 if (net_name && strlen(net_name) > 0) {
243 if ((net_fd = open(net_name, O_RDONLY)) >= 0) {
244 window = (lseek(net_fd, 0, SEEK_END) - sizeof(float) * words * size) / words / size / sizeof(float) / 2;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200245 // lseek(net_fd, sizeof(float) * words * size, SEEK_SET);
246 // munmap(M, sizeof(float) * words * size);
247 M2 = mmap(0, sizeof(float) * words * size + sizeof(float) * 2 * window * size * words, PROT_READ, MAP_SHARED, net_fd, 0);
248 if (M2 == MAP_FAILED) {
249 close(net_fd);
250 fprintf(stderr, "Cannot mmap %s\n", net_name);
251 exit(-1);
252 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200253 syn1neg_window = M2 + words * size;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200254 } else {
255 fprintf(stderr, "Cannot open %s\n", net_name);
256 exit(-1);
257 }
258 fprintf(stderr, "Successfully memmaped %s. Determined window size: %d\n", net_name, window);
259
Marc Kupietz0efe49b2020-04-06 18:30:22 +0200260 if (do_open_cdb) {
261 char collocatordb_name[2048];
262 strcpy(collocatordb_name, net_name);
263 char *ext = rindex(collocatordb_name, '.');
264 if (ext) {
265 strcpy(ext, ".rocksdb");
266 if (access(collocatordb_name, R_OK) == 0) {
267 *ext = 0;
268 fprintf(stderr, "Opening collocator DB %s\n", collocatordb_name);
269 cdb = open_collocatordb(collocatordb_name);
Marc Kupietzc0d41872021-02-25 16:33:22 +0100270 } else {
271 fprintf(stderr, "Cannot open collocator DB %s\n", collocatordb_name);
Marc Kupietz0efe49b2020-04-06 18:30:22 +0200272 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200273 }
274 }
275 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200276
Marc Kupietz969cab92019-08-05 11:13:42 +0200277 expTable = (float *)malloc((EXP_TABLE_SIZE + 1) * sizeof(float));
278 for (i = 0; i < EXP_TABLE_SIZE; i++) {
279 expTable[i] = exp((i / (float)EXP_TABLE_SIZE * 2 - 1) * MAX_EXP); // Precompute the exp() table
280 expTable[i] = expTable[i] / (expTable[i] + 1); // Precompute f(x) = x / (x + 1)
281 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200282
283 return 0;
284}
285
Marc Kupietzd6a163c2026-07-30 14:48:10 +0900286/* The collocator threads of one request accumulate their activation sums per
287 window position here. This must not be shared between requests, so every
288 request allocates its own array. Only valid once init_net() has determined
289 the window size, i.e. if M2 is set. */
290float *new_window_sums() {
291 return calloc((window + 1) * 2, sizeof(float));
292}
293
Marc Kupietz969cab92019-08-05 11:13:42 +0200294long mergeVectors(char *file_name) {
Marc Kupietz59865a92021-03-11 17:16:51 +0100295 FILE *f;
296 int binwords_fd, binvecs_fd;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200297 float *merge_vecs;
298 char *merge_vocab;
Marc Kupietz969cab92019-08-05 11:13:42 +0200299 /* long long merge_words, merge_size; */
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200300 long long merge_size;
301
Marc Kupietz969cab92019-08-05 11:13:42 +0200302 char binvecs_fname[256], binwords_fname[256];
Marc Kupietzc0d41872021-02-25 16:33:22 +0100303
304
Marc Kupietz969cab92019-08-05 11:13:42 +0200305 strcpy(binwords_fname, file_name);
306 strcat(binwords_fname, ".words");
307 strcpy(binvecs_fname, file_name);
308 strcat(binvecs_fname, ".vecs");
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200309
310 f = fopen(file_name, "rb");
311 if (f == NULL) {
312 printf("Input file %s not found\n", file_name);
Marc Kupietz59865a92021-03-11 17:16:51 +0100313 exit(-1);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200314 }
315 fscanf(f, "%lld", &merge_words);
316 fscanf(f, "%lld", &merge_size);
Marc Kupietz969cab92019-08-05 11:13:42 +0200317 if (merge_size != size) {
318 fprintf(stderr, "vectors must have the same length\n");
319 exit(-1);
320 }
321 if ((binvecs_fd = open(binvecs_fname, O_RDONLY)) >= 0 && (binwords_fd = open(binwords_fname, O_RDONLY)) >= 0) {
322 merge_vecs = malloc(sizeof(float) * (words + merge_words) * size);
323 merge_vocab = malloc(sizeof(char) * (words + merge_words) * max_w);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200324 if (merge_vecs == NULL || merge_vocab == NULL) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200325 close(binvecs_fd);
326 close(binwords_fd);
327 fprintf(stderr, "Cannot reserve memory for %s or %s\n", binwords_fname, binvecs_fname);
328 exit(-1);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200329 }
330 read(binvecs_fd, merge_vecs, merge_words * size * sizeof(float));
331 read(binwords_fd, merge_vocab, merge_words * max_w);
Marc Kupietz969cab92019-08-05 11:13:42 +0200332 } else {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200333 fprintf(stderr, "Cannot open %s or %s\n", binwords_fname, binvecs_fname);
334 exit(-1);
Marc Kupietz969cab92019-08-05 11:13:42 +0200335 }
336 printf("Successfully reallocated memory\nMerging...\n");
337 fflush(stdout);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200338 memcpy(merge_vecs + merge_words * size, M, words * size * sizeof(float));
339 memcpy(merge_vocab + merge_words * max_w, vocab, words * max_w);
340 munmap(M, words * size * sizeof(float));
341 munmap(vocab, words * max_w);
342 M = merge_vecs;
343 vocab = merge_vocab;
344 merged_end = merge_words;
345 words += merge_words;
346 fclose(f);
Marc Kupietz969cab92019-08-05 11:13:42 +0200347 printf("merged_end: %lld, words: %lld\n", merged_end, words);
348 //printBiggestMergedDifferences();
349 return ((long)merged_end);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200350}
351
352void filter_garbage() {
353 long i;
354 unsigned char *w, previous, c;
355 garbage = malloc(words);
356 memset(garbage, 0, words);
357 for (i = 0; i < words; i++) {
Marc Kupietz59865a92021-03-11 17:16:51 +0100358 w = (unsigned char *) vocab + i * max_w;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200359 previous = 0;
Marc Kupietz59865a92021-03-11 17:16:51 +0100360 if (strncmp("quot", (const char *)w, 4) == 0) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200361 garbage[i] = 1;
362 // printf("Gargabe: %s\n", vocab + i * max_w);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200363 } else {
Marc Kupietz969cab92019-08-05 11:13:42 +0200364 while ((c = *w++) && !garbage[i]) {
365 if (((c <= 90 && c >= 65) && (previous >= 97 && previous <= 122)) ||
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200366 (previous == '-' && (c & 32)) ||
Marc Kupietz969cab92019-08-05 11:13:42 +0200367 (previous == 0xc2 && (c == 0xa4 || c == 0xb6)) ||
368 (previous == 'q' && c == 'u' && *(w) == 'o' && *(w + 1) == 't') || /* quot */
369 c == '<') {
370 garbage[i] = 1;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200371 continue;
372 }
373 previous = c;
374 }
375 }
376 }
377 return;
378}
379
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200380knn *simpleGetCollocators(int word, int number, long cutoff, int *result) {
381 knnpars *pars = calloc(sizeof(knnpars), 1);
Marc Kupietz59865a92021-03-11 17:16:51 +0100382 float *target_sums = NULL;
383 float *my_window_sums = malloc(sizeof(float) * (window + 1) * 2);
Marc Kupietz969cab92019-08-05 11:13:42 +0200384 pars->cutoff = (cutoff ? cutoff : 300000);
Marc Kupietz59865a92021-03-11 17:16:51 +0100385 long a;
Marc Kupietz969cab92019-08-05 11:13:42 +0200386 for (a = 0; a < cutoff; a++)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200387 target_sums[a] = 0;
388 pars->target_sums = target_sums;
Marc Kupietz59865a92021-03-11 17:16:51 +0100389 pars->window_sums = my_window_sums;
Marc Kupietz969cab92019-08-05 11:13:42 +0200390 pars->N = (number ? number : 20);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200391 pars->from = 0;
Marc Kupietz969cab92019-08-05 11:13:42 +0200392 pars->upto = window * 2 - 1;
393 knn *syn_nbs = NULL; // = (knn*) getCollocators(pars);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200394 free(pars);
Marc Kupietz59865a92021-03-11 17:16:51 +0100395 free(my_window_sums);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200396 free(target_sums);
397 return syn_nbs;
398}
399
Marc Kupietz04135302026-07-30 14:40:02 +0900400/* Frees a knn result as returned by getCollocators() via pthread_exit(). */
401void free_knn(knn *nbs) {
402 if (nbs == NULL)
403 return;
404 free(nbs->best);
405 free(nbs);
406}
407
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200408void *getCollocators(void *args) {
409 knnpars *pars = args;
Marc Kupietz969cab92019-08-05 11:13:42 +0200410 int N = pars->N;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200411
412 int cc = pars->wl->wordi[0];
Marc Kupietz969cab92019-08-05 11:13:42 +0200413 knn *nbs = NULL;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200414 long window_layer_size = size * window * 2;
Marc Kupietz59865a92021-03-11 17:16:51 +0100415 long a, b, c, d, window_offset, target, max_target = 0, maxmax_target;
Marc Kupietz969cab92019-08-05 11:13:42 +0200416 float f, max_f, maxmax_f;
417 float *target_sums = NULL, worstbest, wpos_sum;
418 collocator *best;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200419
Marc Kupietz969cab92019-08-05 11:13:42 +0200420 if (M2 == NULL || cc == -1)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200421 return NULL;
422
Marc Kupietz969cab92019-08-05 11:13:42 +0200423 a = posix_memalign((void **)&target_sums, 128, pars->cutoff * sizeof(float));
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200424 memset(target_sums, 0, pars->cutoff * sizeof(float));
Marc Kupietz969cab92019-08-05 11:13:42 +0200425 best = malloc((N > 200 ? N : 200) * sizeof(collocator));
426 memset(best, 0, (N > 200 ? N : 200) * sizeof(collocator));
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200427 worstbest = pars->threshold;
428
429 for (b = 0; b < pars->cutoff; b++)
Marc Kupietz969cab92019-08-05 11:13:42 +0200430 target_sums[b] = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200431 for (b = 0; b < N; b++) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200432 best[b].wordi = -1;
433 best[b].probability = 1;
434 best[b].activation = worstbest;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200435 }
436
437 d = cc;
438 maxmax_f = -1;
439 maxmax_target = 0;
440
441 for (a = pars->from; a < pars->upto; a++) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200442 if (a >= window)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200443 a++;
444 wpos_sum = 0;
Marc Kupietzc48d3902026-08-21 12:13:45 +0200445 DEBUG_PRINTF("window pos: %ld\n", a);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200446 if (a != window) {
447 max_f = -1;
448 window_offset = a * size;
449 if (a > window)
450 window_offset -= size;
Marc Kupietz969cab92019-08-05 11:13:42 +0200451 for (target = 0; target < pars->cutoff; target++) {
452 if (garbage && garbage[target]) continue;
453 if (target == d)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200454 continue;
455 f = 0;
456 for (c = 0; c < size; c++)
Marc Kupietz969cab92019-08-05 11:13:42 +0200457 f += M2[d * size + c] * syn1neg_window[target * window_layer_size + window_offset + c];
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200458 if (f < -MAX_EXP)
459 continue;
460 else if (f > MAX_EXP)
461 continue;
462 else
Marc Kupietz969cab92019-08-05 11:13:42 +0200463 f = expTable[(int)((f + MAX_EXP) * (EXP_TABLE_SIZE / MAX_EXP / 2))];
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200464 wpos_sum += f;
465
466 target_sums[target] += f;
Marc Kupietz969cab92019-08-05 11:13:42 +0200467 if (f > worstbest) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200468 for (b = 0; b < N; b++) {
469 if (f > best[b].activation) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200470 memmove(best + b + 1, best + b, (N - b - 1) * sizeof(collocator));
471 best[b].activation = f;
472 best[b].wordi = target;
473 best[b].position = window - a;
474 break;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200475 }
476 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200477 if (b == N - 1)
478 worstbest = best[N - 1].activation;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200479 }
480 }
Marc Kupietzc48d3902026-08-21 12:13:45 +0200481 DEBUG_PRINTF("%ld %.2f\n", max_target, max_f);
482 DEBUG_PRINTF("%s (%.2f) ", &vocab[max_target * max_w], max_f);
Marc Kupietz969cab92019-08-05 11:13:42 +0200483 if (max_f > maxmax_f) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200484 maxmax_f = max_f;
485 maxmax_target = max_target;
486 }
487 for (b = 0; b < N; b++)
Marc Kupietz969cab92019-08-05 11:13:42 +0200488 if (best[b].position == window - a)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200489 best[b].cprobability = best[b].activation / wpos_sum;
490 } else {
Marc Kupietzc48d3902026-08-21 12:13:45 +0200491 DEBUG_PRINTF("\x1b[1m%s\x1b[0m ", &vocab[d * max_w]);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200492 }
493 pars->window_sums[a] = wpos_sum;
494 }
495 for (b = 0; b < pars->cutoff; b++)
Marc Kupietz969cab92019-08-05 11:13:42 +0200496 pars->target_sums[b] += target_sums[b]; //(target_sums[b] / wpos_sum ) / (window * 2);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200497
498 free(target_sums);
Marc Kupietz969cab92019-08-05 11:13:42 +0200499 for (b = 0; b < N && best[b].wordi >= 0; b++)
500 ;
Marc Kupietz59865a92021-03-11 17:16:51 +0100501 // THIS LOOP IS NEEDED (b...)
Marc Kupietz969cab92019-08-05 11:13:42 +0200502 // printf("%d: best syn: %s %.2f %.5f\n", b, &vocab[best[b].wordi*max_w], best[b].activation, best[b].probability);
503 // printf("\n");
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200504 nbs = malloc(sizeof(knn));
Marc Kupietz969cab92019-08-05 11:13:42 +0200505 nbs->best = best;
506 nbs->length = b - 1;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200507 pthread_exit(nbs);
508}
509
Marc Kupietz0efe49b2020-04-06 18:30:22 +0200510float getOutputWeight(int hidden, long target, int window_position) {
511 const long window_layer_size = size * window * 2;
512 int a;
513
514 if (window_position == 0 || window_position > window || window_position < -window) {
515 fprintf(stderr, "window_position: %d - assert: -%d <= window_position <= %d && window_position != 0 failed.\n", window_position, window, window);
516 exit(-1);
517 }
518
519 if (hidden >= size) {
Marc Kupietz59865a92021-03-11 17:16:51 +0100520 fprintf(stderr, "hidden: %d - assert: hidden < %lld failed.\n", hidden, size);
Marc Kupietz0efe49b2020-04-06 18:30:22 +0200521 exit(-1);
522 }
523
524 if (target >= words) {
Marc Kupietz59865a92021-03-11 17:16:51 +0100525 fprintf(stderr, "target: %ld - assert: target < %lld failed.\n", target, words);
Marc Kupietz0efe49b2020-04-06 18:30:22 +0200526 exit(-1);
527 }
528
529 a = window_position + window;
530 if (a > window) {
531 --a;
532 }
533 long window_offset = a * size;
534 return syn1neg_window[target * window_layer_size + window_offset + hidden];
535}
536
Marc Kupietz04135302026-07-30 14:40:02 +0900537/* Returns an SV* (not an AV*) on purpose: for an AV* return value Inline::C
538 generates newRV(), which leaves the array itself with a reference count of
539 one after the mortal reference is gone, i.e. it leaks the whole array on
540 every call. */
541SV *getVecs(AV *array) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200542 int i, b;
543 AV *result = newAV();
Marc Kupietz969cab92019-08-05 11:13:42 +0200544 for (i = 0; i <= av_len(array); i++) {
545 SV **elem = av_fetch(array, i, 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200546 if (elem != NULL) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200547 long j = (long)SvNV(*elem);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200548 AV *vector = newAV();
Marc Kupietz6f317a92026-07-30 15:09:54 +0900549 /* ranks come from the request, reading outside the model would crash */
550 if (j >= 0 && j < words) {
551 for (b = 0; b < size; b++) {
552 av_push(vector, newSVnv(M[b + j * size]));
553 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200554 }
Marc Kupietzbdd779a2024-08-05 10:02:29 +0200555 av_push(result, newRV_noinc((SV *)vector));
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200556 }
557 }
Marc Kupietz04135302026-07-30 14:40:02 +0900558 return newRV_noinc((SV *)result);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200559}
560
Marc Kupietz6f317a92026-07-30 15:09:54 +0900561/* Word ids of the collocator db refer to the primary model, which occupies
562 [0, words - merged_end) of its own vocabulary. libcollocatordb crashes on
563 ids outside that range, and the ids come straight from the request. */
564int valid_cdb_node(long node) {
565 return cdb != NULL && node >= 0 && node < words - merged_end;
566}
567
Marc Kupietz04135302026-07-30 14:40:02 +0900568/* All functions handing a string back to perl return an SV*, because for a
569 char* return value Inline::C only copies the string into the return SV and
570 never frees the buffer we allocated here. */
571SV *getSimilarProfiles(long node) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200572 int i;
573 char buffer[120000];
574 char pair_buffer[2048];
Marc Kupietz969cab92019-08-05 11:13:42 +0200575 buffer[0] = '[';
576 buffer[1] = 0;
Marc Kupietz6f317a92026-07-30 15:09:54 +0900577 if (node < 0 || node >= sprofiles_qty) {
Marc Kupietzc48d3902026-08-21 12:13:45 +0200578 DEBUG_PRINTF("Not available in precomputed profile\n");
Marc Kupietz04135302026-07-30 14:40:02 +0900579 return newSVpv("[{\"w\":\"not available\", \"v\":0}]\n", 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200580 }
581
Marc Kupietzc48d3902026-08-21 12:13:45 +0200582 DEBUG_PRINTF("******* %s ******\n", &vocab[max_w * node]);
Marc Kupietz969cab92019-08-05 11:13:42 +0200583
584 for (i = 0; i < 100 && i < sprofiles[node].len; i++) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200585 sprintf(pair_buffer, "{\"w\":\"%s\", \"v\":%f},", &vocab[max_w * (sprofiles[node].nbr[i].index)], sprofiles[node].nbr[i].value);
586 strcat(buffer, pair_buffer);
587 }
Marc Kupietz04135302026-07-30 14:40:02 +0900588 if (i > 0)
589 buffer[strlen(buffer) - 1] = ']';
590 else
591 strcat(buffer, "]");
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200592 strcat(buffer, "\n");
Marc Kupietzc48d3902026-08-21 12:13:45 +0200593 DEBUG_PRINTF("%s", buffer);
Marc Kupietz04135302026-07-30 14:40:02 +0900594 return newSVpv(buffer, 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200595}
596
Marc Kupietz04135302026-07-30 14:40:02 +0900597/* get_collocat*_as_json() hand out strdup()ed buffers that we own. */
598SV *getCollocationScores(long node, long collocate) {
Marc Kupietz6f317a92026-07-30 15:09:54 +0900599 char *json = NULL;
600 SV *res;
601 if (valid_cdb_node(node) && valid_cdb_node(collocate))
602 json = (char *)get_collocation_scores_as_json(cdb, node, collocate);
603 res = newSVpv(json ? json : "{\"collocates\":[]}", 0);
Marc Kupietz04135302026-07-30 14:40:02 +0900604 free(json);
605 return res;
Marc Kupietzf6080012021-03-12 09:14:42 +0100606}
607
Marc Kupietz04135302026-07-30 14:40:02 +0900608SV *getClassicCollocators(long node) {
Marc Kupietz6f317a92026-07-30 15:09:54 +0900609 char *json = NULL;
610 SV *res;
611 if (valid_cdb_node(node))
612 json = (char *)get_collocators_as_json(cdb, node);
613 res = newSVpv(json ? json : "{\"collocates\":[]}", 0);
Marc Kupietz04135302026-07-30 14:40:02 +0900614 free(json);
Marc Kupietz969cab92019-08-05 11:13:42 +0200615 return res;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200616}
617
618wordlist *getTargetWords(char *st1, int search_backw) {
619 wordlist *wl = malloc(sizeof(wordlist));
Marc Kupietz59865a92021-03-11 17:16:51 +0100620 char st[100][max_size];
Marc Kupietz969cab92019-08-05 11:13:42 +0200621 long a, b = 0, c = 0, cn = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200622
623 while (1) {
624 st[cn][b] = st1[c];
625 b++;
626 c++;
627 st[cn][b] = 0;
628 if (st1[c] == 0) break;
Marc Kupietzc0d41872021-02-25 16:33:22 +0100629 if (st1[c] == ' ' /*|| st1[c] == '-'*/) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200630 b = 0;
631 c++;
632 }
633 }
634 cn++;
635 for (a = 0; a < cn; a++) {
636 if (search_backw) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200637 for (b = words - 1; b >= (merge_words ? merge_words : 0) && strcmp(&vocab[b * max_w], st[a]) != 0; b--)
638 ;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200639 } else {
Marc Kupietz969cab92019-08-05 11:13:42 +0200640 for (b = 0; b < (merge_words ? merge_words : words) && strcmp(&vocab[b * max_w], st[a]) != 0; b++)
641 ;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200642 }
643 if (b == words) b = -1;
644 wl->wordi[a] = b;
645 if (b == -1) {
Marc Kupietzc48d3902026-08-21 12:13:45 +0200646 DEBUG_EPRINTF("Out of dictionary word!\n");
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200647 cn--;
648 } else {
Marc Kupietzc48d3902026-08-21 12:13:45 +0200649 DEBUG_EPRINTF("Word: \"%s\" Position in vocabulary: %lld\n", &vocab[wl->wordi[a] * max_w], wl->wordi[a]);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200650 }
651 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200652 wl->length = cn;
653 return (wl);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200654}
655
Marc Kupietzcb43e492019-12-03 10:07:53 +0100656long getWordNumber(char *word) {
657 wordlist *wl = getTargetWords(word, 0);
Marc Kupietz04135302026-07-30 14:40:02 +0900658 long res = 0;
659 if (wl == NULL)
660 return(0);
Marc Kupietzcb43e492019-12-03 10:07:53 +0100661 if(wl->length > 0)
Marc Kupietz04135302026-07-30 14:40:02 +0900662 res = wl->wordi[0];
663 free(wl);
664 return(res);
Marc Kupietzcb43e492019-12-03 10:07:53 +0100665}
666
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200667float get_distance(long b, long c) {
668 long a;
669 float dist = 0;
670 for (a = 0; a < size; a++) dist += M[a + c * size] * M[a + b * size];
671 return dist;
672}
673
Marc Kupietz04135302026-07-30 14:40:02 +0900674/* The result is computed once and then kept in a static buffer for the
675 lifetime of the process. */
676SV *getBiggestMergedDifferences() {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200677 static char *result = NULL;
Marc Kupietz59865a92021-03-11 17:16:51 +0100678 float dist;
679 long long a, c;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200680 int N = 1000;
681
Marc Kupietz969cab92019-08-05 11:13:42 +0200682 if (merged_end == 0)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200683 result = "[]";
Marc Kupietz969cab92019-08-05 11:13:42 +0200684
685 if (result != NULL)
Marc Kupietz04135302026-07-30 14:40:02 +0900686 return newSVpv(result, 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200687
Marc Kupietzc48d3902026-08-21 12:13:45 +0200688 DEBUG_PRINTF("Looking for biggest distances between main and merged vectors ...\n");
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200689 collocator *best;
690 best = malloc(N * sizeof(collocator));
691 memset(best, 0, N * sizeof(collocator));
692
Marc Kupietz969cab92019-08-05 11:13:42 +0200693 float worstbest = 1000000;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200694
695 for (a = 0; a < N; a++) best[a].activation = worstbest;
696
697 for (c = 0; c < 500000; c++) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200698 if (garbage && garbage[c]) continue;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200699 dist = 0;
Marc Kupietz969cab92019-08-05 11:13:42 +0200700 for (a = 0; a < size; a++) dist += M[a + c * size] * M[a + (c + merged_end) * size];
701 if (dist < worstbest) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200702 for (a = 0; a < N; a++) {
703 if (dist < best[a].activation) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200704 memmove(best + a + 1, best + a, (N - a - 1) * sizeof(collocator));
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200705 best[a].activation = dist;
706 best[a].wordi = c;
707 break;
708 }
709 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200710 worstbest = best[N - 1].activation;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200711 }
712 }
713
Marc Kupietz04135302026-07-30 14:40:02 +0900714 result = malloc(N * (max_w + 64));
Marc Kupietzbdd779a2024-08-05 10:02:29 +0200715 char *p = (char *) result;
Marc Kupietz969cab92019-08-05 11:13:42 +0200716 *p++ = '[';
717 *p = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200718 for (a = 0; a < N; a++) {
Marc Kupietz59865a92021-03-11 17:16:51 +0100719 p += sprintf(p, "{\"rank\":%lld,\"word\":\"%s\",\"dist\":%.3f},", a, &vocab[best[a].wordi * max_w], 1 - best[a].activation);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200720 }
721 *--p = ']';
Marc Kupietz04135302026-07-30 14:40:02 +0900722 free(best);
723 return newSVpv(result, 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200724}
725
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200726float cos_similarity(long b, long c) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200727 float dist = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200728 long a;
Marc Kupietz969cab92019-08-05 11:13:42 +0200729 for (a = 0; a < size; a++) dist += M[b * size + a] * M[c * size + a];
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200730 return dist;
731}
732
Marc Kupietz04135302026-07-30 14:40:02 +0900733SV *cos_similarity_as_json(char *w1, char *w2) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200734 wordlist *a, *b;
735 float res;
Marc Kupietz04135302026-07-30 14:40:02 +0900736 char json[32];
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200737 a = getTargetWords(w1, 0);
738 b = getTargetWords(w2, 0);
Marc Kupietz969cab92019-08-05 11:13:42 +0200739 if (a == NULL || b == NULL || a->length != 1 || b->length != 1)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200740 res = -1;
Marc Kupietz04135302026-07-30 14:40:02 +0900741 else {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200742 res = cos_similarity(a->wordi[0], b->wordi[0]);
Marc Kupietzc48d3902026-08-21 12:13:45 +0200743 DEBUG_EPRINTF("a: %lld b: %lld res:%f\n", a->wordi[0], b->wordi[0], res);
Marc Kupietz04135302026-07-30 14:40:02 +0900744 }
745 free(a);
746 free(b);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200747 sprintf(json, "%.5f", res);
Marc Kupietz04135302026-07-30 14:40:02 +0900748 return newSVpv(json, 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200749}
750
751void *_get_neighbours(void *arg) {
752 knnpars *pars = arg;
Marc Kupietz969cab92019-08-05 11:13:42 +0200753 int N = pars->N;
754 long from = pars->from;
755 unsigned long upto = pars->upto;
Marc Kupietz59865a92021-03-11 17:16:51 +0100756 char *sep;
Marc Kupietz969cab92019-08-05 11:13:42 +0200757 float dist, len, vec[max_size];
Marc Kupietz59865a92021-03-11 17:16:51 +0100758 long long a, b, c, cn, *bi;
Marc Kupietz969cab92019-08-05 11:13:42 +0200759 knn *nbs = NULL;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200760 wordlist *wl = pars->wl;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200761
Marc Kupietz969cab92019-08-05 11:13:42 +0200762 collocator *best = pars->best;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200763
Marc Kupietz969cab92019-08-05 11:13:42 +0200764 float worstbest = -1;
765
766 for (a = 0; a < N; a++) best[a].activation = 0;
767 a = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200768 bi = wl->wordi;
Marc Kupietz969cab92019-08-05 11:13:42 +0200769 cn = wl->length;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200770 sep = wl->sep;
Marc Kupietz969cab92019-08-05 11:13:42 +0200771 b = bi[0];
Marc Kupietz969cab92019-08-05 11:13:42 +0200772 if (b == -1) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200773 goto end;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200774 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200775 for (a = 0; a < size; a++) vec[a] = 0;
776 for (b = 0; b < cn; b++) {
777 if (bi[b] == -1) continue;
778 if (b > 0 && sep[b - 1] == '-')
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200779 for (a = 0; a < size; a++) vec[a] -= M[a + bi[b] * size];
780 else
781 for (a = 0; a < size; a++) vec[a] += M[a + bi[b] * size];
Marc Kupietz969cab92019-08-05 11:13:42 +0200782 }
783 len = 0;
784 for (a = 0; a < size; a++) len += vec[a] * vec[a];
785 len = sqrt(len);
786 for (a = 0; a < size; a++) vec[a] /= len;
787 for (a = 0; a < N; a++) best[a].activation = -1;
788 for (c = from; c < upto; c++) {
789 if (garbage && garbage[c]) continue;
790 a = 0;
791 // do not skip taget word
792 // for (b = 0; b < cn; b++) if (bi[b] == c) a = 1;
793 // if (a == 1) continue;
794 dist = 0;
795 for (a = 0; a < size; a++) dist += vec[a] * M[a + c * size];
796 if (dist > worstbest) {
797 for (a = 0; a < N; a++) {
798 if (dist > best[a].activation) {
799 memmove(best + a + 1, best + a, (N - a - 1) * sizeof(collocator));
800 best[a].activation = dist;
801 best[a].wordi = c;
802 break;
803 }
804 }
805 worstbest = best[N - 1].activation;
806 }
807 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200808
809end:
Marc Kupietz969cab92019-08-05 11:13:42 +0200810 pthread_exit(nbs);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200811}
812
Marc Kupietz969cab92019-08-05 11:13:42 +0200813int cmp_activation(const void *a, const void *b) {
814 float fb = ((collocator *)a)->activation;
815 float fa = ((collocator *)b)->activation;
816 return (fa > fb) - (fa < fb);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200817}
818
Marc Kupietz969cab92019-08-05 11:13:42 +0200819int cmp_probability(const void *a, const void *b) {
820 float fb = ((collocator *)a)->probability;
821 float fa = ((collocator *)b)->probability;
822 return (fa > fb) - (fa < fb);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200823}
824
Marc Kupietz04135302026-07-30 14:40:02 +0900825SV *getPosWiseW2VCollocators(char *word, long maxPerPos, long cutoff, float threshold, const char *format) {
Marc Kupietz59865a92021-03-11 17:16:51 +0100826 float *target_sums = NULL;
Marc Kupietzd6a163c2026-07-30 14:48:10 +0900827 float *window_sums = NULL;
Marc Kupietz04135302026-07-30 14:40:02 +0900828 long a, b, entries = 0;
Marc Kupietz969cab92019-08-05 11:13:42 +0200829 knn *syn_nbs[MAX_THREADS];
830 knnpars pars[MAX_THREADS];
Marc Kupietz04135302026-07-30 14:40:02 +0900831 pthread_t *pt = NULL;
832 wordlist *wl = NULL;
Marc Kupietz969cab92019-08-05 11:13:42 +0200833 int syn_threads = (M2 ? window * 2 : 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200834 int search_backw = 0;
Marc Kupietz04135302026-07-30 14:40:02 +0900835 char *result = NULL;
836 SV *res_sv;
837
838 for (a = 0; a < MAX_THREADS; a++) syn_nbs[a] = NULL;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200839
Marc Kupietz969cab92019-08-05 11:13:42 +0200840 if (cutoff < 1 || cutoff > words)
841 cutoff = words;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200842
843 wl = getTargetWords(word, search_backw);
Marc Kupietz04135302026-07-30 14:40:02 +0900844 if (wl == NULL || wl->length < 1 || wl->wordi[0] < 0 || syn_threads < 1) {
845 free(wl);
846 return newSVpv("", 0);
847 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200848
Marc Kupietz04135302026-07-30 14:40:02 +0900849 pt = (pthread_t *)malloc((num_threads + 1) * sizeof(pthread_t));
Marc Kupietzd6a163c2026-07-30 14:48:10 +0900850 window_sums = new_window_sums();
Marc Kupietz969cab92019-08-05 11:13:42 +0200851 a = posix_memalign((void **)&target_sums, 128, cutoff * sizeof(float));
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200852 memset(target_sums, 0, cutoff * sizeof(float));
853
Marc Kupietzc48d3902026-08-21 12:13:45 +0200854 DEBUG_PRINTF("Starting %d threads\n", syn_threads);
855 DEBUG_FFLUSH();
Marc Kupietz969cab92019-08-05 11:13:42 +0200856 for (a = 0; a < syn_threads; a++) {
857 pars[a].cutoff = cutoff;
858 pars[a].target_sums = target_sums;
859 pars[a].window_sums = window_sums;
860 pars[a].wl = wl;
861 pars[a].N = maxPerPos;
Marc Kupietz04135302026-07-30 14:40:02 +0900862 pars[a].best = NULL; /* getCollocators() allocates its own result array */
Marc Kupietz969cab92019-08-05 11:13:42 +0200863 pars[a].threshold = threshold;
864 pars[a].from = a;
865 pars[a].upto = a + 1;
866 pthread_create(&pt[a], NULL, getCollocators, (void *)&pars[a]);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200867 }
Marc Kupietzc48d3902026-08-21 12:13:45 +0200868 DEBUG_PRINTF("Waiting for syn threads to join\n");
869 DEBUG_FFLUSH();
Marc Kupietz969cab92019-08-05 11:13:42 +0200870 for (a = 0; a < syn_threads; a++) pthread_join(pt[a], (void *)&syn_nbs[a]);
Marc Kupietzc48d3902026-08-21 12:13:45 +0200871 DEBUG_PRINTF("Syn threads joint\n");
872 DEBUG_FFLUSH();
Marc Kupietz04135302026-07-30 14:40:02 +0900873 result = malloc((maxPerPos > 0 ? maxPerPos : 1) * (max_w + 96) * syn_threads + 16);
Marc Kupietzbdd779a2024-08-05 10:02:29 +0200874 char *p = (char *) result;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200875 *p = 0;
Marc Kupietz0ab97392024-12-10 16:16:32 +0100876 if (strcmp(format, "tsv") == 0) {
877 for (a = syn_threads - 1; a >= 0; a--) {
Marc Kupietz04135302026-07-30 14:40:02 +0900878 if (syn_nbs[a] == NULL) continue;
879 for (b = 0; b < syn_nbs[a]->length; b++, entries++) {
Marc Kupietz0ab97392024-12-10 16:16:32 +0100880 p += sprintf(p, "%ld\t%s\t%f\n", syn_nbs[a]->best[b].position, &vocab[syn_nbs[a]->best[b].wordi * max_w], syn_nbs[a]->best[b].activation);
881 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200882 }
Marc Kupietz0ab97392024-12-10 16:16:32 +0100883 } else {
884 p += sprintf(p, "[");
885 for (a = syn_threads - 1; a >= 0; a--) {
Marc Kupietz04135302026-07-30 14:40:02 +0900886 if (syn_nbs[a] == NULL) continue;
887 for (b = 0; b < syn_nbs[a]->length; b++, entries++) {
Marc Kupietz0ab97392024-12-10 16:16:32 +0100888 p += sprintf(p, "{\"pos\": %ld, \"word\":\"%s\",\"activation\": %f},\n", syn_nbs[a]->best[b].position, &vocab[syn_nbs[a]->best[b].wordi * max_w], syn_nbs[a]->best[b].activation);
889 }
890 }
Marc Kupietz04135302026-07-30 14:40:02 +0900891 if (entries > 0)
892 p -= 2; /* drop the trailing ",\n" */
Marc Kupietz0ab97392024-12-10 16:16:32 +0100893 p += sprintf(p, "\n]");
Marc Kupietz969cab92019-08-05 11:13:42 +0200894 }
Marc Kupietz0ab97392024-12-10 16:16:32 +0100895
Marc Kupietz04135302026-07-30 14:40:02 +0900896 res_sv = newSVpv(result, 0);
897
898 free(result);
899 free(target_sums);
Marc Kupietzd6a163c2026-07-30 14:48:10 +0900900 free(window_sums);
Marc Kupietz04135302026-07-30 14:40:02 +0900901 free(pt);
902 free(wl);
903 for (a = 0; a < syn_threads; a++) free_knn(syn_nbs[a]);
904
905 return res_sv;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200906}
907
Marc Kupietz04135302026-07-30 14:40:02 +0900908SV *getPosWiseW2VCollocatorsAsTsv(char *word, long maxPerPos, long cutoff, float threshold) {
Marc Kupietz0ab97392024-12-10 16:16:32 +0100909 return getPosWiseW2VCollocators(word, maxPerPos, cutoff, threshold, "tsv");
910}
911
Marc Kupietz04135302026-07-30 14:40:02 +0900912SV *getPosWiseW2VCollocatorsAsJson(char *word, long maxPerPos, long cutoff, float threshold) {
Marc Kupietz0ab97392024-12-10 16:16:32 +0100913 return getPosWiseW2VCollocators(word, maxPerPos, cutoff, threshold, "json");
914}
915
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200916SV *get_neighbours(char *st1, int N, int sort_by, int search_backw, long cutoff, int dedupe, int no_similar_profiles) {
917 HV *result = newHV();
Marc Kupietz59865a92021-03-11 17:16:51 +0100918 float *target_sums = NULL;
Marc Kupietzd6a163c2026-07-30 14:48:10 +0900919 float *window_sums = NULL;
Marc Kupietz969cab92019-08-05 11:13:42 +0200920 long a, b, c, d, slice;
921 knn *para_nbs[MAX_THREADS];
922 knn *syn_nbs[MAX_THREADS];
923 knnpars pars[MAX_THREADS];
924 pthread_t *pt = (pthread_t *)malloc((num_threads + 1) * sizeof(pthread_t));
Marc Kupietz04135302026-07-30 14:40:02 +0900925 wordlist *wl = NULL;
Marc Kupietz969cab92019-08-05 11:13:42 +0200926 int syn_threads = (M2 ? window * 2 : 0);
927 int para_threads = (no_similar_profiles ? 0 : num_threads - syn_threads);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200928
Marc Kupietz04135302026-07-30 14:40:02 +0900929 for (a = 0; a < MAX_THREADS; a++) para_nbs[a] = syn_nbs[a] = NULL;
930
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200931 collocator *best = NULL;
Marc Kupietz969cab92019-08-05 11:13:42 +0200932 posix_memalign((void **)&best, 128, 10 * (N >= 200 ? N : 200) * sizeof(collocator));
933 memset(best, 0, (N >= 200 ? N : 200) * sizeof(collocator));
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200934
Marc Kupietz969cab92019-08-05 11:13:42 +0200935 if (N > MAX_NEIGHBOURS) N = MAX_NEIGHBOURS;
936
937 if (cutoff < 1 || cutoff > words)
938 cutoff = words;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200939
940 wl = getTargetWords(st1, search_backw);
Marc Kupietz969cab92019-08-05 11:13:42 +0200941 if (wl == NULL || wl->length < 1)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200942 goto end;
943
Marc Kupietz04135302026-07-30 14:40:02 +0900944 slice = (para_threads > 0 ? cutoff / para_threads : cutoff);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200945
Marc Kupietz969cab92019-08-05 11:13:42 +0200946 a = posix_memalign((void **)&target_sums, 128, cutoff * sizeof(float));
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200947 memset(target_sums, 0, cutoff * sizeof(float));
948
Marc Kupietzc48d3902026-08-21 12:13:45 +0200949 DEBUG_PRINTF("Starting %d threads for paradigmatic search\n", para_threads);
950 DEBUG_FFLUSH();
Marc Kupietz969cab92019-08-05 11:13:42 +0200951 for (a = 0; a < para_threads; a++) {
952 pars[a].cutoff = cutoff;
953 pars[a].token = st1;
954 pars[a].wl = wl;
955 pars[a].N = N;
956 pars[a].best = &best[N * a];
957 if (merge_words == 0 || search_backw == 0) {
958 pars[a].from = a * slice;
959 pars[a].upto = ((a + 1) * slice > cutoff ? cutoff : (a + 1) * slice);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200960 } else {
961 pars[a].from = merge_words + a * slice;
Marc Kupietz969cab92019-08-05 11:13:42 +0200962 pars[a].upto = merge_words + ((a + 1) * slice > cutoff ? cutoff : (a + 1) * slice);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200963 }
Marc Kupietzc48d3902026-08-21 12:13:45 +0200964 DEBUG_PRINTF("From: %ld, Upto: %ld\n", pars[a].from, pars[a].upto);
Marc Kupietz969cab92019-08-05 11:13:42 +0200965 pthread_create(&pt[a], NULL, _get_neighbours, (void *)&pars[a]);
966 }
967 if (M2) {
Marc Kupietzd6a163c2026-07-30 14:48:10 +0900968 window_sums = new_window_sums();
Marc Kupietz969cab92019-08-05 11:13:42 +0200969 for (a = 0; a < syn_threads; a++) {
970 pars[a + para_threads].cutoff = cutoff;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200971 pars[a + para_threads].target_sums = target_sums;
972 pars[a + para_threads].window_sums = window_sums;
973 pars[a + para_threads].wl = wl;
974 pars[a + para_threads].N = N;
975 pars[a + para_threads].threshold = MIN_RESP;
976 pars[a + para_threads].from = a;
Marc Kupietz969cab92019-08-05 11:13:42 +0200977 pars[a + para_threads].upto = a + 1;
978 pthread_create(&pt[a + para_threads], NULL, getCollocators, (void *)&pars[a + para_threads]);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200979 }
980 }
Marc Kupietzc48d3902026-08-21 12:13:45 +0200981 DEBUG_PRINTF("Waiting for para threads to join\n");
982 DEBUG_FFLUSH();
Marc Kupietz969cab92019-08-05 11:13:42 +0200983 for (a = 0; a < para_threads; a++) pthread_join(pt[a], (void *)&para_nbs[a]);
Marc Kupietzc48d3902026-08-21 12:13:45 +0200984 DEBUG_PRINTF("Para threads joint\n");
985 DEBUG_FFLUSH();
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200986
Marc Kupietz969cab92019-08-05 11:13:42 +0200987 /* if(!syn_nbs[0]) */
988 /* goto end; */
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200989
Marc Kupietz969cab92019-08-05 11:13:42 +0200990 qsort(best, N * para_threads, sizeof(collocator), cmp_activation);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200991
992 long long chosen[MAX_NEIGHBOURS];
Marc Kupietzc48d3902026-08-21 12:13:45 +0200993 DEBUG_PRINTF("N: %d\n", N);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200994
Marc Kupietz969cab92019-08-05 11:13:42 +0200995 AV *array = newAV();
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200996 int i, j;
Marc Kupietz969cab92019-08-05 11:13:42 +0200997 int l1_words = 0, l2_words = 0;
998
999 for (a = 0, i = 0; i < N && a < N * para_threads; a++) {
1000 int filtered = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001001 long long c = best[a].wordi;
1002 if ((merge_words && dedupe && i > 1) || (!merge_words && dedupe && i > 0)) {
Marc Kupietz969cab92019-08-05 11:13:42 +02001003 for (j = 0; j < i && !filtered; j++)
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001004 if (strcasestr(&vocab[c * max_w], &vocab[chosen[j] * max_w]) ||
1005 strcasestr(&vocab[chosen[j] * max_w], &vocab[c * max_w])) {
Marc Kupietzc48d3902026-08-21 12:13:45 +02001006 DEBUG_PRINTF("filtering %s %s\n", &vocab[chosen[j] * max_w], &vocab[c * max_w]);
Marc Kupietz969cab92019-08-05 11:13:42 +02001007 filtered = 1;
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001008 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001009 if (filtered)
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001010 continue;
1011 }
1012
Marc Kupietz969cab92019-08-05 11:13:42 +02001013 if (0 && merge_words > 0) {
1014 if (c >= merge_words) {
1015 if (l1_words > N / 2)
1016 continue;
1017 else
1018 l1_words++;
1019 } else {
1020 if (l2_words > N / 2)
1021 continue;
1022 else
1023 l2_words++;
1024 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001025 }
1026
Marc Kupietz969cab92019-08-05 11:13:42 +02001027 // printf("%s l1:%d l2:%d i:%d a:%ld\n", &vocab[c * max_w], l1_words, l2_words, i, a);
1028 // fflush(stdout);
1029 HV *hash = newHV();
1030 SV *word = newSVpvf(&vocab[c * max_w], 0);
1031 chosen[i] = c;
1032 if (latin_enc == 0) SvUTF8_on(word);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001033 fflush(stdout);
Marc Kupietz969cab92019-08-05 11:13:42 +02001034 hv_store(hash, "word", strlen("word"), word, 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001035 hv_store(hash, "dist", strlen("dist"), newSVnv(best[a].activation), 0);
1036 hv_store(hash, "rank", strlen("rank"), newSVuv(best[a].wordi), 0);
1037 AV *vector = newAV();
1038 for (b = 0; b < size; b++) {
1039 av_push(vector, newSVnv(M[b + best[a].wordi * size]));
1040 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001041 hv_store(hash, "vector", strlen("vector"), newRV_noinc((SV *)vector), 0);
1042 av_push(array, newRV_noinc((SV *)hash));
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001043 i++;
1044 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001045 hv_store(result, "paradigmatic", strlen("paradigmatic"), newRV_noinc((SV *)array), 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001046
Marc Kupietz969cab92019-08-05 11:13:42 +02001047 for (b = 0; b < MAX_NEIGHBOURS; b++) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001048 best[b].wordi = -1L;
1049 best[b].activation = 0;
1050 best[b].probability = 0;
1051 best[b].position = 0;
1052 best[b].activation_sum = 0;
Marc Kupietz969cab92019-08-05 11:13:42 +02001053 memset(best[b].heat, 0, sizeof(float) * 16);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001054 }
1055
Marc Kupietz969cab92019-08-05 11:13:42 +02001056 float total_activation = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001057
1058 if (M2) {
Marc Kupietzc48d3902026-08-21 12:13:45 +02001059 DEBUG_PRINTF("Waiting for syn threads to join\n");
1060 DEBUG_FFLUSH();
Marc Kupietz969cab92019-08-05 11:13:42 +02001061 for (a = 0; a < syn_threads; a++) pthread_join(pt[a + para_threads], (void *)&syn_nbs[a]);
1062 for (a = 0; a <= syn_threads; a++) {
1063 if (a == window) continue;
1064 total_activation += window_sums[a];
Marc Kupietzc48d3902026-08-21 12:13:45 +02001065 DEBUG_PRINTF("window pos: %ld, sum: %f\n", a, window_sums[a]);
Marc Kupietz969cab92019-08-05 11:13:42 +02001066 }
Marc Kupietzc48d3902026-08-21 12:13:45 +02001067 DEBUG_PRINTF("syn threads joint\n");
1068 DEBUG_FFLUSH();
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001069
Marc Kupietz969cab92019-08-05 11:13:42 +02001070 for (b = 0; b < syn_nbs[0]->length; b++) {
1071 memcpy(best + b, &syn_nbs[0]->best[b], sizeof(collocator));
1072 best[b].position = -1; // syn_nbs[0]->pos[b];
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001073 best[b].activation_sum = target_sums[syn_nbs[0]->best[b].wordi];
Marc Kupietz969cab92019-08-05 11:13:42 +02001074 best[b].max_activation = 0.0;
1075 best[b].average = 0.0;
1076 best[b].probability = 0.0;
1077 best[b].cprobability = syn_nbs[0]->best[b].cprobability;
1078 memset(best[b].heat, 0, sizeof(float) * 16);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001079 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001080
1081 float best_window_sum[MAX_NEIGHBOURS];
Marc Kupietz59865a92021-03-11 17:16:51 +01001082 int found_index = 0, i = 0, w;
Marc Kupietz969cab92019-08-05 11:13:42 +02001083 for (a = 0; a < syn_threads; a++) {
1084 for (b = 0; b < syn_nbs[a]->length; b++) {
1085 for (i = 0; i < found_index; i++)
1086 if (best[i].wordi == syn_nbs[a]->best[b].wordi)
1087 break;
1088 if (i >= found_index) {
1089 best[found_index].max_activation = 0.0;
1090 best[found_index].average = 0.0;
1091 best[found_index].probability = 0.0;
1092 memset(best[found_index].heat, 0, sizeof(float) * 16);
1093 best[found_index].cprobability = syn_nbs[a]->best[b].cprobability;
1094 best[found_index].activation_sum = target_sums[syn_nbs[a]->best[b].wordi]; // syn_nbs[a]->best[b].activation_sum;
1095 best[found_index++].wordi = syn_nbs[a]->best[b].wordi;
1096 // printf("found: %s\n", &vocab[syn_nbs[a]->index[b] * max_w]);
1097 }
1098 }
1099 }
1100 sort_by = 0; // ALWAYS AUTO-FOCUS
1101 if (sort_by != 1 && sort_by != 2) { // sort by auto focus mean
Marc Kupietzc48d3902026-08-21 12:13:45 +02001102 DEBUG_PRINTF("window: %d - syn_threads: %d, %d\n", window, syn_threads, (1 << syn_threads) - 1);
Marc Kupietz969cab92019-08-05 11:13:42 +02001103 int wpos;
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001104 int bits_set = 0;
Marc Kupietz969cab92019-08-05 11:13:42 +02001105 for (i = 0; i < found_index; i++) {
1106 best[i].activation = best[i].probability = best[i].average = best[i].cprobability_sum = 0;
1107 for (w = 1; w < (1 << syn_threads); w++) { // loop through all possible windows
1108 float word_window_sum = 0, word_window_average = 0, word_cprobability_sum = 0, word_activation_sum = 0, total_window_sum = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001109 bits_set = 0;
Marc Kupietz969cab92019-08-05 11:13:42 +02001110 for (a = 0; a < syn_threads; a++) {
1111 if ((1 << a) & w) {
1112 wpos = (a >= window ? a + 1 : a);
1113 total_window_sum += window_sums[wpos];
1114 }
1115 }
1116 // printf("%d window-sum %f\n", w, total_window_sum);
1117 for (a = 0; a < syn_threads; a++) {
1118 if ((1 << a) & w) {
1119 wpos = (a >= window ? a + 1 : a);
1120 bits_set++;
1121 for (b = 0; b < syn_nbs[a]->length; b++)
1122 if (best[i].wordi == syn_nbs[a]->best[b].wordi) {
1123 // float acti = syn_nbs[a]->best[b].activation / total_window_sum;
1124 // word_window_sum += syn_nbs[a]->dist[b] * syn_nbs[a]->norm[b]; // / window_sums[wpos]; // syn_nbs[a]->norm[b];
1125 // word_window_sum += syn_nbs[a]->norm[b]; // / window_sums[wpos]; // syn_nbs[a]->norm[b];
1126 // word_window_sum = (word_window_sum + syn_nbs[a]->norm[b]) - (word_window_sum * syn_nbs[a]->norm[b]); // syn_nbs[a]->norm[b];
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001127
Marc Kupietz969cab92019-08-05 11:13:42 +02001128 word_window_sum += syn_nbs[a]->best[b].activation; // / window_sums[wpos]; // syn_nbs[a]->norm[b];
1129 // word_window_sum += acti - (word_window_sum * acti); syn_nbs[a]->best[b].activation; // / window_sums[wpos]; // syn_nbs[a]->norm[b];
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001130
Marc Kupietz969cab92019-08-05 11:13:42 +02001131 word_window_average += syn_nbs[a]->best[b].activation; // - word_window_average * syn_nbs[a]->best[b].activation; // conormalied activation sum
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001132 word_cprobability_sum += syn_nbs[a]->best[b].cprobability - word_cprobability_sum * syn_nbs[a]->best[b].cprobability; // conormalied column probability sum
Marc Kupietz969cab92019-08-05 11:13:42 +02001133 word_activation_sum += syn_nbs[a]->best[b].activation;
1134 if (syn_nbs[a]->best[b].activation > best[i].max_activation)
1135 best[i].max_activation = syn_nbs[a]->best[b].activation;
1136 if (syn_nbs[a]->best[b].activation > best[i].heat[wpos])
1137 best[i].heat[wpos] = syn_nbs[a]->best[b].activation;
1138 }
1139 }
1140 }
1141 if (bits_set) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001142 word_window_average /= bits_set;
Marc Kupietz969cab92019-08-05 11:13:42 +02001143 // word_activation_sum /= bits_set;
1144 // word_window_sum /= bits_set;
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001145 }
1146
Marc Kupietz969cab92019-08-05 11:13:42 +02001147 word_window_sum /= total_window_sum;
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001148
Marc Kupietz969cab92019-08-05 11:13:42 +02001149 if (word_window_sum > best[i].probability) {
1150 // best[i].position = w;
1151 best[i].probability = word_window_sum;
1152 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001153
Marc Kupietz969cab92019-08-05 11:13:42 +02001154 if (word_cprobability_sum > best[i].cprobability_sum) {
1155 best[i].position = w;
1156 best[i].cprobability_sum = word_cprobability_sum;
1157 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001158
Marc Kupietz969cab92019-08-05 11:13:42 +02001159 best[i].average = word_window_average;
1160 // best[i].activation = word_activation_sum;
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001161 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001162 }
1163 qsort(best, found_index, sizeof(collocator), cmp_probability);
1164 // for(i=0; i < found_index; i++) {
1165 // printf("found: %s - sum: %f - window: %d\n", &vocab[best[i].wordi * max_w], best[i].activation, best[i].position);
1166 // }
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001167
Marc Kupietz969cab92019-08-05 11:13:42 +02001168 } else if (sort_by == 1) { // responsiveness any window position
1169 int wpos;
1170 for (i = 0; i < found_index; i++) {
1171 float word_window_sum = 0, word_activation_sum = 0, total_window_sum = 0;
1172 for (a = 0; a < syn_threads; a++) {
1173 wpos = (a >= window ? a + 1 : a);
1174 for (b = 0; b < syn_nbs[a]->length; b++)
1175 if (best[i].wordi == syn_nbs[a]->best[b].wordi) {
1176 best[i].probability += syn_nbs[a]->best[b].probability;
1177 if (syn_nbs[a]->best[b].activation > 0.25)
1178 best[i].position |= 1 << wpos;
1179 if (syn_nbs[a]->best[b].activation > best[i].activation) {
1180 best[i].activation = syn_nbs[a]->best[b].activation;
1181 }
1182 }
1183 }
1184 }
1185 qsort(best, found_index, sizeof(collocator), cmp_activation);
1186 } else if (sort_by == 2) { // single window position
1187 for (a = 1; a < syn_threads; a++) {
1188 for (b = 0; b < syn_nbs[a]->length; b++) {
1189 for (c = 0; c < MAX_NEIGHBOURS; c++) {
1190 if (syn_nbs[a]->best[b].activation > best[c].activation) {
1191 for (d = MAX_NEIGHBOURS - 1; d > c; d--) {
1192 memmove(best + d, best + d - 1, sizeof(collocator));
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001193 }
1194 memcpy(best + c, &syn_nbs[a]->best[b], sizeof(collocator));
Marc Kupietz969cab92019-08-05 11:13:42 +02001195 best[c].position = 1 << (-syn_nbs[a]->best[b].position + window - (syn_nbs[a]->best[b].position < 0 ? 1 : 0));
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001196 break;
1197 }
1198 }
1199 }
1200 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001201 } else { // sort by mean p
1202 for (a = 1; a < syn_threads; a++) {
1203 for (b = 0; b < syn_nbs[a]->length; b++) {
1204 for (c = 0; c < MAX_NEIGHBOURS; c++) {
1205 if (target_sums[syn_nbs[a]->best[b].wordi] > best[c].activation_sum) {
1206 for (d = MAX_NEIGHBOURS - 1; d > c; d--) {
1207 memmove(best + d, best + d - 1, sizeof(collocator));
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001208 }
1209 memcpy(best + c, &syn_nbs[a]->best[b], sizeof(collocator));
Marc Kupietz969cab92019-08-05 11:13:42 +02001210 best[c].position = (1 << 2 * window) - 1; // syn_nbs[a]->pos[b];
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001211 best[c].activation_sum = target_sums[syn_nbs[a]->best[b].wordi];
1212 break;
1213 }
1214 }
1215 }
1216 }
1217 }
1218 array = newAV();
Marc Kupietz969cab92019-08-05 11:13:42 +02001219 for (a = 0, i = 0; a < MAX_NEIGHBOURS && best[a].wordi >= 0; a++) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001220 long long c = best[a].wordi;
Marc Kupietz969cab92019-08-05 11:13:42 +02001221 /*
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001222 if (dedupe) {
1223 int filtered=0;
1224 for (j=0; j<i; j++)
1225 if (strcasestr(&vocab[c * max_w], chosen[j]) ||
1226 strcasestr(chosen[j], &vocab[c * max_w])) {
1227 printf("filtering %s %s\n", chosen[j], &vocab[c * max_w]);
1228 filtered = 1;
1229 }
1230 if(filtered)
1231 continue;
1232 }
1233*/
Marc Kupietz969cab92019-08-05 11:13:42 +02001234 chosen[i++] = c;
1235 HV *hash = newHV();
1236 SV *word = newSVpvf(&vocab[best[a].wordi * max_w], 0);
1237 AV *heat = newAV();
1238 if (latin_enc == 0) SvUTF8_on(word);
1239 hv_store(hash, "word", strlen("word"), word, 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001240 hv_store(hash, "rank", strlen("rank"), newSVuv(best[a].wordi), 0);
1241 hv_store(hash, "average", strlen("average"), newSVnv(best[a].average), 0);
1242 hv_store(hash, "prob", strlen("prob"), newSVnv(best[a].probability), 0);
1243 hv_store(hash, "cprob", strlen("cprob"), newSVnv(best[a].cprobability_sum), 0);
Marc Kupietz969cab92019-08-05 11:13:42 +02001244 hv_store(hash, "max", strlen("max"), newSVnv(best[a].max_activation), 0); // newSVnv(target_sums[best[a].wordi]), 0);
1245 hv_store(hash, "overall", strlen("overall"), newSVnv(best[a].activation_sum / total_activation), 0); // newSVnv(target_sums[best[a].wordi]), 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001246 hv_store(hash, "pos", strlen("pos"), newSVnv(best[a].position), 0);
Marc Kupietz969cab92019-08-05 11:13:42 +02001247 best[a].heat[5] = 0;
1248 for (i = 10; i >= 0; i--) av_push(heat, newSVnv(best[a].heat[i]));
1249 hv_store(hash, "heat", strlen("heat"), newRV_noinc((SV *)heat), 0);
1250 av_push(array, newRV_noinc((SV *)hash));
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001251 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001252 hv_store(result, "syntagmatic", strlen("syntagmatic"), newRV_noinc((SV *)array), 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001253 }
1254end:
Marc Kupietz969cab92019-08-05 11:13:42 +02001255 free(best);
Marc Kupietz04135302026-07-30 14:40:02 +09001256 free(target_sums);
Marc Kupietzd6a163c2026-07-30 14:48:10 +09001257 free(window_sums);
Marc Kupietz04135302026-07-30 14:40:02 +09001258 free(pt);
1259 free(wl);
1260 for (a = 0; a < MAX_THREADS; a++) {
1261 free_knn(para_nbs[a]);
1262 free_knn(syn_nbs[a]);
1263 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001264 return newRV_noinc((SV *)result);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001265}
1266
1267int dump_vecs(char *fname) {
Marc Kupietz969cab92019-08-05 11:13:42 +02001268 long i, j;
1269 FILE *f;
1270 /* if(words>100000)
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001271 words=100000;
1272*/
Marc Kupietz969cab92019-08-05 11:13:42 +02001273 if ((f = fopen(fname, "w")) == NULL) {
1274 fprintf(stderr, "cannot open %s for writing\n", fname);
1275 return (-1);
1276 }
1277 fprintf(f, "%lld %lld\n", words, size);
1278 for (i = 0; i < words; i++) {
1279 fprintf(f, "%s ", &vocab[i * max_w]);
1280 for (j = 0; j < size - 1; j++)
1281 fprintf(f, "%f ", M[i * size + j]);
1282 fprintf(f, "%f\n", M[i * size + j]);
1283 }
1284 fclose(f);
1285 return (0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001286}
1287
1288int dump_for_numpy(char *fname) {
Marc Kupietz969cab92019-08-05 11:13:42 +02001289 long i, j;
1290 FILE *f;
Marc Kupietzc0d41872021-02-25 16:33:22 +01001291 int max = words; // 300000;
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001292
Marc Kupietz969cab92019-08-05 11:13:42 +02001293 if ((f = fopen(fname, "w")) == NULL) {
1294 fprintf(stderr, "cannot open %s for writing\n", fname);
1295 return (-1);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001296 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001297 for (i = 0; i < max; i++) {
1298 for (j = 0; j < size - 1; j++)
1299 fprintf(f, "%f\t", M[i * size + j]);
1300 fprintf(f, "%f\n", M[i * size + j]);
1301 printf("%s\r\n", &vocab[i * max_w]);
1302 }
1303 if (merged_end > 0) {
1304 for (i = 0; i < max; i++) {
1305 for (j = 0; j < size - 1; j++)
1306 fprintf(f, "%f\t", M[(merged_end + i) * size + j]);
1307 fprintf(f, "%f\n", M[(merged_end + i) * size + j]);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001308 printf("_%s\r\n", &vocab[i * max_w]);
1309 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001310 }
1311 fclose(f);
1312 return (0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001313}
Marc Kupietz043db152023-11-05 17:47:53 +01001314
1315unsigned long getVocabSize() {
1316 return (unsigned long) words;
1317}
Marc Kupietz6f317a92026-07-30 15:09:54 +09001318
1319/* First rank of the primary model in the merged vocabulary, 0 if no second
1320 model was merged in. mergeVectors() puts the merged in model at ranks
1321 [0, merged_end) and the primary model - the one the collocator db belongs
1322 to - at [merged_end, words). */
1323long getMergedEnd() {
1324 return (long) merged_end;
1325}