blob: 56e037c8745d4bf74cbbbb36bc8d52d4d347399e [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
22//the thread function
23void *connection_handler(void *);
24
25typedef struct {
Marc Kupietz969cab92019-08-05 11:13:42 +020026 long long wordi;
27 long position;
28 float activation;
29 float average;
30 float cprobability; // column wise probability
31 float cprobability_sum;
32 float probability;
33 float activation_sum;
34 float max_activation;
35 float heat[16];
Marc Kupietzf11d20c2019-08-02 15:42:04 +020036} collocator;
37
38typedef struct {
Marc Kupietz969cab92019-08-05 11:13:42 +020039 collocator *best;
40 int length;
Marc Kupietzf11d20c2019-08-02 15:42:04 +020041} knn;
Marc Kupietz969cab92019-08-05 11:13:42 +020042
Marc Kupietzf11d20c2019-08-02 15:42:04 +020043typedef struct {
44 long long wordi[MAX_NEIGHBOURS];
45 char sep[MAX_NEIGHBOURS];
46 int length;
47} wordlist;
48
49typedef struct {
50 long cutoff;
51 wordlist *wl;
Marc Kupietz969cab92019-08-05 11:13:42 +020052 char *token;
53 int N;
54 long from;
55 unsigned long upto;
Marc Kupietzf11d20c2019-08-02 15:42:04 +020056 collocator *best;
57 float *target_sums;
58 float *window_sums;
59 float threshold;
60} knnpars;
61
62typedef struct {
63 uint32_t index;
64 float value;
65} sparse_t;
66
67typedef struct {
68 uint32_t len;
69 sparse_t nbr[100];
70} profile_t;
71
Marc Kupietz969cab92019-08-05 11:13:42 +020072float *M, *M2 = 0L, *syn1neg_window, *expTable;
Marc Kupietzf11d20c2019-08-02 15:42:04 +020073char *vocab;
74char *garbage = NULL;
75COLLOCATORDB *cdb = NULL;
76profile_t *sprofiles = NULL;
77size_t sprofiles_qty = 0;
78
79long long words, size, merged_end;
80long long merge_words = 0;
Marc Kupietz969cab92019-08-05 11:13:42 +020081int num_threads = 20;
82int latin_enc = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +020083int window;
84
85/* load collocation profiles if file exists */
86int load_sprofiles(char *vecsname) {
87 char *basename = strdup(vecsname);
88 char *pos = strstr(basename, ".vecs");
Marc Kupietz969cab92019-08-05 11:13:42 +020089 if (pos)
90 *pos = 0;
91
Marc Kupietzf11d20c2019-08-02 15:42:04 +020092 char binsprofiles_fname[256];
93 strcpy(binsprofiles_fname, basename);
Marc Kupietz969cab92019-08-05 11:13:42 +020094 strcat(binsprofiles_fname, ".sprofiles.bin");
Marc Kupietzf11d20c2019-08-02 15:42:04 +020095 FILE *fp = fopen(binsprofiles_fname, "rb");
96 if (fp == NULL) {
97 printf("Collocation profiles %s not found. No problem.\n", binsprofiles_fname);
98 return 0;
99 }
100 fseek(fp, 0L, SEEK_END);
101 size_t sz = ftell(fp);
102 fclose(fp);
103
104 int fd = open(binsprofiles_fname, O_RDONLY);
Marc Kupietz969cab92019-08-05 11:13:42 +0200105 sprofiles = mmap(0, sz, PROT_READ, MAP_SHARED, fd, 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200106 if (sprofiles == MAP_FAILED) {
107 close(fd);
108 fprintf(stderr, "Cannot mmap %s\n", binsprofiles_fname);
109 sprofiles = NULL;
110 return 0;
Marc Kupietz969cab92019-08-05 11:13:42 +0200111 } else {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200112 sprofiles_qty = sz / sizeof(profile_t);
113 fprintf(stderr, "Successfully mmaped %s containing similar profiles for %ld word forms.\n", binsprofiles_fname, sprofiles_qty);
114 }
115 return 1;
116}
117
Marc Kupietzc0d41872021-02-25 16:33:22 +0100118char *removeExtension(char* myStr) {
119 char *retStr;
120 char *lastExt;
121 if (myStr == NULL) return NULL;
122 if ((retStr = malloc (strlen (myStr) + 1)) == NULL) return NULL;
123 strcpy (retStr, myStr);
124 lastExt = strrchr (retStr, '.');
125 if (lastExt != NULL)
126 *lastExt = '\0';
127 return retStr;
128}
129
Marc Kupietz0efe49b2020-04-06 18:30:22 +0200130int init_net(char *file_name, char *net_name, int latin, int do_open_cdb) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200131 FILE *f, *binvecs, *binwords;
Marc Kupietz969cab92019-08-05 11:13:42 +0200132 int binwords_fd, binvecs_fd, net_fd, i;
Marc Kupietz59865a92021-03-11 17:16:51 +0100133 long long a, b;
Marc Kupietz969cab92019-08-05 11:13:42 +0200134 float len;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200135 double val;
136
Marc Kupietzc0d41872021-02-25 16:33:22 +0100137 char binvecs_fname[1024], binwords_fname[1024];
138
139 if (strstr(file_name, ".txt")) {
140 strcpy(binwords_fname, removeExtension(file_name));
141 } else {
142 strcpy(binwords_fname, file_name);
143 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200144 strcat(binwords_fname, ".words");
145 strcpy(binvecs_fname, file_name);
146 strcat(binvecs_fname, ".vecs");
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200147
148 latin_enc = latin;
149 f = fopen(file_name, "rb");
150 if (f == NULL) {
151 printf("Input file %s not found\n", file_name);
152 return -1;
153 }
154 fscanf(f, "%lld", &words);
Marc Kupietz969cab92019-08-05 11:13:42 +0200155 if (MAX_WORDS > 0 && words > MAX_WORDS) words = MAX_WORDS;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200156 fscanf(f, "%lld", &size);
Marc Kupietz969cab92019-08-05 11:13:42 +0200157 if ((binvecs_fd = open(binvecs_fname, O_RDONLY)) < 0 || (binwords_fd = open(binwords_fname, O_RDONLY)) < 0) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200158 printf("Converting %s to memory mappable structures\n", file_name);
Marc Kupietz969cab92019-08-05 11:13:42 +0200159 vocab = (char *)malloc((long long)words * max_w * sizeof(char));
160 M = (float *)malloc((long long)words * (long long)size * sizeof(float));
161 if (M == NULL) {
162 printf("Cannot allocate memory: %lld MB %lld %lld\n", (long long)words * size * sizeof(float) / 1048576, words, size);
163 return -1;
164 }
165 if (strstr(file_name, ".txt")) {
Marc Kupietzc0d41872021-02-25 16:33:22 +0100166 printf("%lld words in ascii vector file with vector size %lld\n", words, size);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200167 for (b = 0; b < words; b++) {
168 a = 0;
169 while (1) {
170 vocab[b * max_w + a] = fgetc(f);
171 if (feof(f) || (vocab[b * max_w + a] == ' ')) break;
172 if ((a < max_w) && (vocab[b * max_w + a] != '\n')) a++;
173 }
174 vocab[b * max_w + a] = 0;
175 len = 0;
176 for (a = 0; a < size; a++) {
177 fscanf(f, "%lf", &val);
178 M[a + b * size] = val;
179 len += val * val;
Marc Kupietz969cab92019-08-05 11:13:42 +0200180 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200181 len = sqrt(len);
182 for (a = 0; a < size; a++) M[a + b * size] /= len;
183 }
184 } else {
185 for (b = 0; b < words; b++) {
186 a = 0;
187 while (1) {
188 vocab[b * max_w + a] = fgetc(f);
189 if (feof(f) || (vocab[b * max_w + a] == ' ')) break;
190 if ((a < max_w) && (vocab[b * max_w + a] != '\n')) a++;
191 }
192 vocab[b * max_w + a] = 0;
193 fread(&M[b * size], sizeof(float), size, f);
194 len = 0;
195 for (a = 0; a < size; a++) len += M[a + b * size] * M[a + b * size];
196 len = sqrt(len);
197 for (a = 0; a < size; a++) M[a + b * size] /= len;
198 }
199 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200200 if ((binvecs = fopen(binvecs_fname, "wb")) != NULL && (binwords = fopen(binwords_fname, "wb")) != NULL) {
201 fwrite(M, sizeof(float), (long long)words * (long long)size, binvecs);
202 fclose(binvecs);
203 fwrite(vocab, sizeof(char), (long long)words * max_w, binwords);
204 fclose(binwords);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200205 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200206 }
207 if ((binvecs_fd = open(binvecs_fname, O_RDONLY)) >= 0 && (binwords_fd = open(binwords_fname, O_RDONLY)) >= 0) {
208 M = mmap(0, sizeof(float) * (long long)words * (long long)size, PROT_READ, MAP_SHARED, binvecs_fd, 0);
209 vocab = mmap(0, sizeof(char) * (long long)words * max_w, PROT_READ, MAP_SHARED, binwords_fd, 0);
210 if (M == MAP_FAILED || vocab == MAP_FAILED) {
211 close(binvecs_fd);
212 close(binwords_fd);
213 fprintf(stderr, "Cannot mmap %s or %s\n", binwords_fname, binvecs_fname);
214 exit(-1);
215 }
216 } else {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200217 fprintf(stderr, "Cannot open %s or %s\n", binwords_fname, binvecs_fname);
218 exit(-1);
Marc Kupietz969cab92019-08-05 11:13:42 +0200219 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200220 fclose(f);
221
Marc Kupietz969cab92019-08-05 11:13:42 +0200222 if (net_name && strlen(net_name) > 0) {
223 if ((net_fd = open(net_name, O_RDONLY)) >= 0) {
224 window = (lseek(net_fd, 0, SEEK_END) - sizeof(float) * words * size) / words / size / sizeof(float) / 2;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200225 // lseek(net_fd, sizeof(float) * words * size, SEEK_SET);
226 // munmap(M, sizeof(float) * words * size);
227 M2 = mmap(0, sizeof(float) * words * size + sizeof(float) * 2 * window * size * words, PROT_READ, MAP_SHARED, net_fd, 0);
228 if (M2 == MAP_FAILED) {
229 close(net_fd);
230 fprintf(stderr, "Cannot mmap %s\n", net_name);
231 exit(-1);
232 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200233 syn1neg_window = M2 + words * size;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200234 } else {
235 fprintf(stderr, "Cannot open %s\n", net_name);
236 exit(-1);
237 }
238 fprintf(stderr, "Successfully memmaped %s. Determined window size: %d\n", net_name, window);
239
Marc Kupietz0efe49b2020-04-06 18:30:22 +0200240 if (do_open_cdb) {
241 char collocatordb_name[2048];
242 strcpy(collocatordb_name, net_name);
243 char *ext = rindex(collocatordb_name, '.');
244 if (ext) {
245 strcpy(ext, ".rocksdb");
246 if (access(collocatordb_name, R_OK) == 0) {
247 *ext = 0;
248 fprintf(stderr, "Opening collocator DB %s\n", collocatordb_name);
249 cdb = open_collocatordb(collocatordb_name);
Marc Kupietzc0d41872021-02-25 16:33:22 +0100250 } else {
251 fprintf(stderr, "Cannot open collocator DB %s\n", collocatordb_name);
Marc Kupietz0efe49b2020-04-06 18:30:22 +0200252 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200253 }
254 }
255 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200256
Marc Kupietz969cab92019-08-05 11:13:42 +0200257 expTable = (float *)malloc((EXP_TABLE_SIZE + 1) * sizeof(float));
258 for (i = 0; i < EXP_TABLE_SIZE; i++) {
259 expTable[i] = exp((i / (float)EXP_TABLE_SIZE * 2 - 1) * MAX_EXP); // Precompute the exp() table
260 expTable[i] = expTable[i] / (expTable[i] + 1); // Precompute f(x) = x / (x + 1)
261 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200262
263 return 0;
264}
265
Marc Kupietzd6a163c2026-07-30 14:48:10 +0900266/* The collocator threads of one request accumulate their activation sums per
267 window position here. This must not be shared between requests, so every
268 request allocates its own array. Only valid once init_net() has determined
269 the window size, i.e. if M2 is set. */
270float *new_window_sums() {
271 return calloc((window + 1) * 2, sizeof(float));
272}
273
Marc Kupietz969cab92019-08-05 11:13:42 +0200274long mergeVectors(char *file_name) {
Marc Kupietz59865a92021-03-11 17:16:51 +0100275 FILE *f;
276 int binwords_fd, binvecs_fd;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200277 float *merge_vecs;
278 char *merge_vocab;
Marc Kupietz969cab92019-08-05 11:13:42 +0200279 /* long long merge_words, merge_size; */
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200280 long long merge_size;
281
Marc Kupietz969cab92019-08-05 11:13:42 +0200282 char binvecs_fname[256], binwords_fname[256];
Marc Kupietzc0d41872021-02-25 16:33:22 +0100283
284
Marc Kupietz969cab92019-08-05 11:13:42 +0200285 strcpy(binwords_fname, file_name);
286 strcat(binwords_fname, ".words");
287 strcpy(binvecs_fname, file_name);
288 strcat(binvecs_fname, ".vecs");
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200289
290 f = fopen(file_name, "rb");
291 if (f == NULL) {
292 printf("Input file %s not found\n", file_name);
Marc Kupietz59865a92021-03-11 17:16:51 +0100293 exit(-1);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200294 }
295 fscanf(f, "%lld", &merge_words);
296 fscanf(f, "%lld", &merge_size);
Marc Kupietz969cab92019-08-05 11:13:42 +0200297 if (merge_size != size) {
298 fprintf(stderr, "vectors must have the same length\n");
299 exit(-1);
300 }
301 if ((binvecs_fd = open(binvecs_fname, O_RDONLY)) >= 0 && (binwords_fd = open(binwords_fname, O_RDONLY)) >= 0) {
302 merge_vecs = malloc(sizeof(float) * (words + merge_words) * size);
303 merge_vocab = malloc(sizeof(char) * (words + merge_words) * max_w);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200304 if (merge_vecs == NULL || merge_vocab == NULL) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200305 close(binvecs_fd);
306 close(binwords_fd);
307 fprintf(stderr, "Cannot reserve memory for %s or %s\n", binwords_fname, binvecs_fname);
308 exit(-1);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200309 }
310 read(binvecs_fd, merge_vecs, merge_words * size * sizeof(float));
311 read(binwords_fd, merge_vocab, merge_words * max_w);
Marc Kupietz969cab92019-08-05 11:13:42 +0200312 } else {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200313 fprintf(stderr, "Cannot open %s or %s\n", binwords_fname, binvecs_fname);
314 exit(-1);
Marc Kupietz969cab92019-08-05 11:13:42 +0200315 }
316 printf("Successfully reallocated memory\nMerging...\n");
317 fflush(stdout);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200318 memcpy(merge_vecs + merge_words * size, M, words * size * sizeof(float));
319 memcpy(merge_vocab + merge_words * max_w, vocab, words * max_w);
320 munmap(M, words * size * sizeof(float));
321 munmap(vocab, words * max_w);
322 M = merge_vecs;
323 vocab = merge_vocab;
324 merged_end = merge_words;
325 words += merge_words;
326 fclose(f);
Marc Kupietz969cab92019-08-05 11:13:42 +0200327 printf("merged_end: %lld, words: %lld\n", merged_end, words);
328 //printBiggestMergedDifferences();
329 return ((long)merged_end);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200330}
331
332void filter_garbage() {
333 long i;
334 unsigned char *w, previous, c;
335 garbage = malloc(words);
336 memset(garbage, 0, words);
337 for (i = 0; i < words; i++) {
Marc Kupietz59865a92021-03-11 17:16:51 +0100338 w = (unsigned char *) vocab + i * max_w;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200339 previous = 0;
Marc Kupietz59865a92021-03-11 17:16:51 +0100340 if (strncmp("quot", (const char *)w, 4) == 0) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200341 garbage[i] = 1;
342 // printf("Gargabe: %s\n", vocab + i * max_w);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200343 } else {
Marc Kupietz969cab92019-08-05 11:13:42 +0200344 while ((c = *w++) && !garbage[i]) {
345 if (((c <= 90 && c >= 65) && (previous >= 97 && previous <= 122)) ||
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200346 (previous == '-' && (c & 32)) ||
Marc Kupietz969cab92019-08-05 11:13:42 +0200347 (previous == 0xc2 && (c == 0xa4 || c == 0xb6)) ||
348 (previous == 'q' && c == 'u' && *(w) == 'o' && *(w + 1) == 't') || /* quot */
349 c == '<') {
350 garbage[i] = 1;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200351 continue;
352 }
353 previous = c;
354 }
355 }
356 }
357 return;
358}
359
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200360knn *simpleGetCollocators(int word, int number, long cutoff, int *result) {
361 knnpars *pars = calloc(sizeof(knnpars), 1);
Marc Kupietz59865a92021-03-11 17:16:51 +0100362 float *target_sums = NULL;
363 float *my_window_sums = malloc(sizeof(float) * (window + 1) * 2);
Marc Kupietz969cab92019-08-05 11:13:42 +0200364 pars->cutoff = (cutoff ? cutoff : 300000);
Marc Kupietz59865a92021-03-11 17:16:51 +0100365 long a;
Marc Kupietz969cab92019-08-05 11:13:42 +0200366 for (a = 0; a < cutoff; a++)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200367 target_sums[a] = 0;
368 pars->target_sums = target_sums;
Marc Kupietz59865a92021-03-11 17:16:51 +0100369 pars->window_sums = my_window_sums;
Marc Kupietz969cab92019-08-05 11:13:42 +0200370 pars->N = (number ? number : 20);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200371 pars->from = 0;
Marc Kupietz969cab92019-08-05 11:13:42 +0200372 pars->upto = window * 2 - 1;
373 knn *syn_nbs = NULL; // = (knn*) getCollocators(pars);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200374 free(pars);
Marc Kupietz59865a92021-03-11 17:16:51 +0100375 free(my_window_sums);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200376 free(target_sums);
377 return syn_nbs;
378}
379
Marc Kupietz04135302026-07-30 14:40:02 +0900380/* Frees a knn result as returned by getCollocators() via pthread_exit(). */
381void free_knn(knn *nbs) {
382 if (nbs == NULL)
383 return;
384 free(nbs->best);
385 free(nbs);
386}
387
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200388void *getCollocators(void *args) {
389 knnpars *pars = args;
Marc Kupietz969cab92019-08-05 11:13:42 +0200390 int N = pars->N;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200391
392 int cc = pars->wl->wordi[0];
Marc Kupietz969cab92019-08-05 11:13:42 +0200393 knn *nbs = NULL;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200394 long window_layer_size = size * window * 2;
Marc Kupietz59865a92021-03-11 17:16:51 +0100395 long a, b, c, d, window_offset, target, max_target = 0, maxmax_target;
Marc Kupietz969cab92019-08-05 11:13:42 +0200396 float f, max_f, maxmax_f;
397 float *target_sums = NULL, worstbest, wpos_sum;
398 collocator *best;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200399
Marc Kupietz969cab92019-08-05 11:13:42 +0200400 if (M2 == NULL || cc == -1)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200401 return NULL;
402
Marc Kupietz969cab92019-08-05 11:13:42 +0200403 a = posix_memalign((void **)&target_sums, 128, pars->cutoff * sizeof(float));
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200404 memset(target_sums, 0, pars->cutoff * sizeof(float));
Marc Kupietz969cab92019-08-05 11:13:42 +0200405 best = malloc((N > 200 ? N : 200) * sizeof(collocator));
406 memset(best, 0, (N > 200 ? N : 200) * sizeof(collocator));
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200407 worstbest = pars->threshold;
408
409 for (b = 0; b < pars->cutoff; b++)
Marc Kupietz969cab92019-08-05 11:13:42 +0200410 target_sums[b] = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200411 for (b = 0; b < N; b++) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200412 best[b].wordi = -1;
413 best[b].probability = 1;
414 best[b].activation = worstbest;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200415 }
416
417 d = cc;
418 maxmax_f = -1;
419 maxmax_target = 0;
420
421 for (a = pars->from; a < pars->upto; a++) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200422 if (a >= window)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200423 a++;
424 wpos_sum = 0;
425 printf("window pos: %ld\n", a);
426 if (a != window) {
427 max_f = -1;
428 window_offset = a * size;
429 if (a > window)
430 window_offset -= size;
Marc Kupietz969cab92019-08-05 11:13:42 +0200431 for (target = 0; target < pars->cutoff; target++) {
432 if (garbage && garbage[target]) continue;
433 if (target == d)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200434 continue;
435 f = 0;
436 for (c = 0; c < size; c++)
Marc Kupietz969cab92019-08-05 11:13:42 +0200437 f += M2[d * size + c] * syn1neg_window[target * window_layer_size + window_offset + c];
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200438 if (f < -MAX_EXP)
439 continue;
440 else if (f > MAX_EXP)
441 continue;
442 else
Marc Kupietz969cab92019-08-05 11:13:42 +0200443 f = expTable[(int)((f + MAX_EXP) * (EXP_TABLE_SIZE / MAX_EXP / 2))];
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200444 wpos_sum += f;
445
446 target_sums[target] += f;
Marc Kupietz969cab92019-08-05 11:13:42 +0200447 if (f > worstbest) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200448 for (b = 0; b < N; b++) {
449 if (f > best[b].activation) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200450 memmove(best + b + 1, best + b, (N - b - 1) * sizeof(collocator));
451 best[b].activation = f;
452 best[b].wordi = target;
453 best[b].position = window - a;
454 break;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200455 }
456 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200457 if (b == N - 1)
458 worstbest = best[N - 1].activation;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200459 }
460 }
Marc Kupietz59865a92021-03-11 17:16:51 +0100461 printf("%ld %.2f\n", max_target, max_f);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200462 printf("%s (%.2f) ", &vocab[max_target * max_w], max_f);
Marc Kupietz969cab92019-08-05 11:13:42 +0200463 if (max_f > maxmax_f) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200464 maxmax_f = max_f;
465 maxmax_target = max_target;
466 }
467 for (b = 0; b < N; b++)
Marc Kupietz969cab92019-08-05 11:13:42 +0200468 if (best[b].position == window - a)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200469 best[b].cprobability = best[b].activation / wpos_sum;
470 } else {
Marc Kupietz969cab92019-08-05 11:13:42 +0200471 printf("\x1b[1m%s\x1b[0m ", &vocab[d * max_w]);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200472 }
473 pars->window_sums[a] = wpos_sum;
474 }
475 for (b = 0; b < pars->cutoff; b++)
Marc Kupietz969cab92019-08-05 11:13:42 +0200476 pars->target_sums[b] += target_sums[b]; //(target_sums[b] / wpos_sum ) / (window * 2);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200477
478 free(target_sums);
Marc Kupietz969cab92019-08-05 11:13:42 +0200479 for (b = 0; b < N && best[b].wordi >= 0; b++)
480 ;
Marc Kupietz59865a92021-03-11 17:16:51 +0100481 // THIS LOOP IS NEEDED (b...)
Marc Kupietz969cab92019-08-05 11:13:42 +0200482 // printf("%d: best syn: %s %.2f %.5f\n", b, &vocab[best[b].wordi*max_w], best[b].activation, best[b].probability);
483 // printf("\n");
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200484 nbs = malloc(sizeof(knn));
Marc Kupietz969cab92019-08-05 11:13:42 +0200485 nbs->best = best;
486 nbs->length = b - 1;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200487 pthread_exit(nbs);
488}
489
Marc Kupietz0efe49b2020-04-06 18:30:22 +0200490float getOutputWeight(int hidden, long target, int window_position) {
491 const long window_layer_size = size * window * 2;
492 int a;
493
494 if (window_position == 0 || window_position > window || window_position < -window) {
495 fprintf(stderr, "window_position: %d - assert: -%d <= window_position <= %d && window_position != 0 failed.\n", window_position, window, window);
496 exit(-1);
497 }
498
499 if (hidden >= size) {
Marc Kupietz59865a92021-03-11 17:16:51 +0100500 fprintf(stderr, "hidden: %d - assert: hidden < %lld failed.\n", hidden, size);
Marc Kupietz0efe49b2020-04-06 18:30:22 +0200501 exit(-1);
502 }
503
504 if (target >= words) {
Marc Kupietz59865a92021-03-11 17:16:51 +0100505 fprintf(stderr, "target: %ld - assert: target < %lld failed.\n", target, words);
Marc Kupietz0efe49b2020-04-06 18:30:22 +0200506 exit(-1);
507 }
508
509 a = window_position + window;
510 if (a > window) {
511 --a;
512 }
513 long window_offset = a * size;
514 return syn1neg_window[target * window_layer_size + window_offset + hidden];
515}
516
Marc Kupietz04135302026-07-30 14:40:02 +0900517/* Returns an SV* (not an AV*) on purpose: for an AV* return value Inline::C
518 generates newRV(), which leaves the array itself with a reference count of
519 one after the mortal reference is gone, i.e. it leaks the whole array on
520 every call. */
521SV *getVecs(AV *array) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200522 int i, b;
523 AV *result = newAV();
Marc Kupietz969cab92019-08-05 11:13:42 +0200524 for (i = 0; i <= av_len(array); i++) {
525 SV **elem = av_fetch(array, i, 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200526 if (elem != NULL) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200527 long j = (long)SvNV(*elem);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200528 AV *vector = newAV();
Marc Kupietz6f317a92026-07-30 15:09:54 +0900529 /* ranks come from the request, reading outside the model would crash */
530 if (j >= 0 && j < words) {
531 for (b = 0; b < size; b++) {
532 av_push(vector, newSVnv(M[b + j * size]));
533 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200534 }
Marc Kupietzbdd779a2024-08-05 10:02:29 +0200535 av_push(result, newRV_noinc((SV *)vector));
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200536 }
537 }
Marc Kupietz04135302026-07-30 14:40:02 +0900538 return newRV_noinc((SV *)result);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200539}
540
Marc Kupietz6f317a92026-07-30 15:09:54 +0900541/* Word ids of the collocator db refer to the primary model, which occupies
542 [0, words - merged_end) of its own vocabulary. libcollocatordb crashes on
543 ids outside that range, and the ids come straight from the request. */
544int valid_cdb_node(long node) {
545 return cdb != NULL && node >= 0 && node < words - merged_end;
546}
547
Marc Kupietz04135302026-07-30 14:40:02 +0900548/* All functions handing a string back to perl return an SV*, because for a
549 char* return value Inline::C only copies the string into the return SV and
550 never frees the buffer we allocated here. */
551SV *getSimilarProfiles(long node) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200552 int i;
553 char buffer[120000];
554 char pair_buffer[2048];
Marc Kupietz969cab92019-08-05 11:13:42 +0200555 buffer[0] = '[';
556 buffer[1] = 0;
Marc Kupietz6f317a92026-07-30 15:09:54 +0900557 if (node < 0 || node >= sprofiles_qty) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200558 printf("Not available in precomputed profile\n");
Marc Kupietz04135302026-07-30 14:40:02 +0900559 return newSVpv("[{\"w\":\"not available\", \"v\":0}]\n", 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200560 }
561
562 printf("******* %s ******\n", &vocab[max_w * node]);
Marc Kupietz969cab92019-08-05 11:13:42 +0200563
564 for (i = 0; i < 100 && i < sprofiles[node].len; i++) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200565 sprintf(pair_buffer, "{\"w\":\"%s\", \"v\":%f},", &vocab[max_w * (sprofiles[node].nbr[i].index)], sprofiles[node].nbr[i].value);
566 strcat(buffer, pair_buffer);
567 }
Marc Kupietz04135302026-07-30 14:40:02 +0900568 if (i > 0)
569 buffer[strlen(buffer) - 1] = ']';
570 else
571 strcat(buffer, "]");
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200572 strcat(buffer, "\n");
Marc Kupietz59865a92021-03-11 17:16:51 +0100573 printf("%s", buffer);
Marc Kupietz04135302026-07-30 14:40:02 +0900574 return newSVpv(buffer, 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200575}
576
Marc Kupietz04135302026-07-30 14:40:02 +0900577/* get_collocat*_as_json() hand out strdup()ed buffers that we own. */
578SV *getCollocationScores(long node, long collocate) {
Marc Kupietz6f317a92026-07-30 15:09:54 +0900579 char *json = NULL;
580 SV *res;
581 if (valid_cdb_node(node) && valid_cdb_node(collocate))
582 json = (char *)get_collocation_scores_as_json(cdb, node, collocate);
583 res = newSVpv(json ? json : "{\"collocates\":[]}", 0);
Marc Kupietz04135302026-07-30 14:40:02 +0900584 free(json);
585 return res;
Marc Kupietzf6080012021-03-12 09:14:42 +0100586}
587
Marc Kupietz04135302026-07-30 14:40:02 +0900588SV *getClassicCollocators(long node) {
Marc Kupietz6f317a92026-07-30 15:09:54 +0900589 char *json = NULL;
590 SV *res;
591 if (valid_cdb_node(node))
592 json = (char *)get_collocators_as_json(cdb, node);
593 res = newSVpv(json ? json : "{\"collocates\":[]}", 0);
Marc Kupietz04135302026-07-30 14:40:02 +0900594 free(json);
Marc Kupietz969cab92019-08-05 11:13:42 +0200595 return res;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200596}
597
598wordlist *getTargetWords(char *st1, int search_backw) {
599 wordlist *wl = malloc(sizeof(wordlist));
Marc Kupietz59865a92021-03-11 17:16:51 +0100600 char st[100][max_size];
Marc Kupietz969cab92019-08-05 11:13:42 +0200601 long a, b = 0, c = 0, cn = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200602
603 while (1) {
604 st[cn][b] = st1[c];
605 b++;
606 c++;
607 st[cn][b] = 0;
608 if (st1[c] == 0) break;
Marc Kupietzc0d41872021-02-25 16:33:22 +0100609 if (st1[c] == ' ' /*|| st1[c] == '-'*/) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200610 b = 0;
611 c++;
612 }
613 }
614 cn++;
615 for (a = 0; a < cn; a++) {
616 if (search_backw) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200617 for (b = words - 1; b >= (merge_words ? merge_words : 0) && strcmp(&vocab[b * max_w], st[a]) != 0; b--)
618 ;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200619 } else {
Marc Kupietz969cab92019-08-05 11:13:42 +0200620 for (b = 0; b < (merge_words ? merge_words : words) && strcmp(&vocab[b * max_w], st[a]) != 0; b++)
621 ;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200622 }
623 if (b == words) b = -1;
624 wl->wordi[a] = b;
625 if (b == -1) {
626 fprintf(stderr, "Out of dictionary word!\n");
627 cn--;
628 } else {
Marc Kupietz969cab92019-08-05 11:13:42 +0200629 fprintf(stderr, "Word: \"%s\" Position in vocabulary: %lld\n", &vocab[wl->wordi[a] * max_w], wl->wordi[a]);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200630 }
631 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200632 wl->length = cn;
633 return (wl);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200634}
635
Marc Kupietzcb43e492019-12-03 10:07:53 +0100636long getWordNumber(char *word) {
637 wordlist *wl = getTargetWords(word, 0);
Marc Kupietz04135302026-07-30 14:40:02 +0900638 long res = 0;
639 if (wl == NULL)
640 return(0);
Marc Kupietzcb43e492019-12-03 10:07:53 +0100641 if(wl->length > 0)
Marc Kupietz04135302026-07-30 14:40:02 +0900642 res = wl->wordi[0];
643 free(wl);
644 return(res);
Marc Kupietzcb43e492019-12-03 10:07:53 +0100645}
646
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200647float get_distance(long b, long c) {
648 long a;
649 float dist = 0;
650 for (a = 0; a < size; a++) dist += M[a + c * size] * M[a + b * size];
651 return dist;
652}
653
Marc Kupietz04135302026-07-30 14:40:02 +0900654/* The result is computed once and then kept in a static buffer for the
655 lifetime of the process. */
656SV *getBiggestMergedDifferences() {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200657 static char *result = NULL;
Marc Kupietz59865a92021-03-11 17:16:51 +0100658 float dist;
659 long long a, c;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200660 int N = 1000;
661
Marc Kupietz969cab92019-08-05 11:13:42 +0200662 if (merged_end == 0)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200663 result = "[]";
Marc Kupietz969cab92019-08-05 11:13:42 +0200664
665 if (result != NULL)
Marc Kupietz04135302026-07-30 14:40:02 +0900666 return newSVpv(result, 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200667
668 printf("Looking for biggest distances between main and merged vectors ...\n");
669 collocator *best;
670 best = malloc(N * sizeof(collocator));
671 memset(best, 0, N * sizeof(collocator));
672
Marc Kupietz969cab92019-08-05 11:13:42 +0200673 float worstbest = 1000000;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200674
675 for (a = 0; a < N; a++) best[a].activation = worstbest;
676
677 for (c = 0; c < 500000; c++) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200678 if (garbage && garbage[c]) continue;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200679 dist = 0;
Marc Kupietz969cab92019-08-05 11:13:42 +0200680 for (a = 0; a < size; a++) dist += M[a + c * size] * M[a + (c + merged_end) * size];
681 if (dist < worstbest) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200682 for (a = 0; a < N; a++) {
683 if (dist < best[a].activation) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200684 memmove(best + a + 1, best + a, (N - a - 1) * sizeof(collocator));
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200685 best[a].activation = dist;
686 best[a].wordi = c;
687 break;
688 }
689 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200690 worstbest = best[N - 1].activation;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200691 }
692 }
693
Marc Kupietz04135302026-07-30 14:40:02 +0900694 result = malloc(N * (max_w + 64));
Marc Kupietzbdd779a2024-08-05 10:02:29 +0200695 char *p = (char *) result;
Marc Kupietz969cab92019-08-05 11:13:42 +0200696 *p++ = '[';
697 *p = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200698 for (a = 0; a < N; a++) {
Marc Kupietz59865a92021-03-11 17:16:51 +0100699 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 +0200700 }
701 *--p = ']';
Marc Kupietz04135302026-07-30 14:40:02 +0900702 free(best);
703 return newSVpv(result, 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200704}
705
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200706float cos_similarity(long b, long c) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200707 float dist = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200708 long a;
Marc Kupietz969cab92019-08-05 11:13:42 +0200709 for (a = 0; a < size; a++) dist += M[b * size + a] * M[c * size + a];
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200710 return dist;
711}
712
Marc Kupietz04135302026-07-30 14:40:02 +0900713SV *cos_similarity_as_json(char *w1, char *w2) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200714 wordlist *a, *b;
715 float res;
Marc Kupietz04135302026-07-30 14:40:02 +0900716 char json[32];
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200717 a = getTargetWords(w1, 0);
718 b = getTargetWords(w2, 0);
Marc Kupietz969cab92019-08-05 11:13:42 +0200719 if (a == NULL || b == NULL || a->length != 1 || b->length != 1)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200720 res = -1;
Marc Kupietz04135302026-07-30 14:40:02 +0900721 else {
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200722 res = cos_similarity(a->wordi[0], b->wordi[0]);
Marc Kupietz04135302026-07-30 14:40:02 +0900723 fprintf(stderr, "a: %lld b: %lld res:%f\n", a->wordi[0], b->wordi[0], res);
724 }
725 free(a);
726 free(b);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200727 sprintf(json, "%.5f", res);
Marc Kupietz04135302026-07-30 14:40:02 +0900728 return newSVpv(json, 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200729}
730
731void *_get_neighbours(void *arg) {
732 knnpars *pars = arg;
Marc Kupietz969cab92019-08-05 11:13:42 +0200733 int N = pars->N;
734 long from = pars->from;
735 unsigned long upto = pars->upto;
Marc Kupietz59865a92021-03-11 17:16:51 +0100736 char *sep;
Marc Kupietz969cab92019-08-05 11:13:42 +0200737 float dist, len, vec[max_size];
Marc Kupietz59865a92021-03-11 17:16:51 +0100738 long long a, b, c, cn, *bi;
Marc Kupietz969cab92019-08-05 11:13:42 +0200739 knn *nbs = NULL;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200740 wordlist *wl = pars->wl;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200741
Marc Kupietz969cab92019-08-05 11:13:42 +0200742 collocator *best = pars->best;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200743
Marc Kupietz969cab92019-08-05 11:13:42 +0200744 float worstbest = -1;
745
746 for (a = 0; a < N; a++) best[a].activation = 0;
747 a = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200748 bi = wl->wordi;
Marc Kupietz969cab92019-08-05 11:13:42 +0200749 cn = wl->length;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200750 sep = wl->sep;
Marc Kupietz969cab92019-08-05 11:13:42 +0200751 b = bi[0];
Marc Kupietz969cab92019-08-05 11:13:42 +0200752 if (b == -1) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200753 goto end;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200754 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200755 for (a = 0; a < size; a++) vec[a] = 0;
756 for (b = 0; b < cn; b++) {
757 if (bi[b] == -1) continue;
758 if (b > 0 && sep[b - 1] == '-')
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200759 for (a = 0; a < size; a++) vec[a] -= M[a + bi[b] * size];
760 else
761 for (a = 0; a < size; a++) vec[a] += M[a + bi[b] * size];
Marc Kupietz969cab92019-08-05 11:13:42 +0200762 }
763 len = 0;
764 for (a = 0; a < size; a++) len += vec[a] * vec[a];
765 len = sqrt(len);
766 for (a = 0; a < size; a++) vec[a] /= len;
767 for (a = 0; a < N; a++) best[a].activation = -1;
768 for (c = from; c < upto; c++) {
769 if (garbage && garbage[c]) continue;
770 a = 0;
771 // do not skip taget word
772 // for (b = 0; b < cn; b++) if (bi[b] == c) a = 1;
773 // if (a == 1) continue;
774 dist = 0;
775 for (a = 0; a < size; a++) dist += vec[a] * M[a + c * size];
776 if (dist > worstbest) {
777 for (a = 0; a < N; a++) {
778 if (dist > best[a].activation) {
779 memmove(best + a + 1, best + a, (N - a - 1) * sizeof(collocator));
780 best[a].activation = dist;
781 best[a].wordi = c;
782 break;
783 }
784 }
785 worstbest = best[N - 1].activation;
786 }
787 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200788
789end:
Marc Kupietz969cab92019-08-05 11:13:42 +0200790 pthread_exit(nbs);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200791}
792
Marc Kupietz969cab92019-08-05 11:13:42 +0200793int cmp_activation(const void *a, const void *b) {
794 float fb = ((collocator *)a)->activation;
795 float fa = ((collocator *)b)->activation;
796 return (fa > fb) - (fa < fb);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200797}
798
Marc Kupietz969cab92019-08-05 11:13:42 +0200799int cmp_probability(const void *a, const void *b) {
800 float fb = ((collocator *)a)->probability;
801 float fa = ((collocator *)b)->probability;
802 return (fa > fb) - (fa < fb);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200803}
804
Marc Kupietz04135302026-07-30 14:40:02 +0900805SV *getPosWiseW2VCollocators(char *word, long maxPerPos, long cutoff, float threshold, const char *format) {
Marc Kupietz59865a92021-03-11 17:16:51 +0100806 float *target_sums = NULL;
Marc Kupietzd6a163c2026-07-30 14:48:10 +0900807 float *window_sums = NULL;
Marc Kupietz04135302026-07-30 14:40:02 +0900808 long a, b, entries = 0;
Marc Kupietz969cab92019-08-05 11:13:42 +0200809 knn *syn_nbs[MAX_THREADS];
810 knnpars pars[MAX_THREADS];
Marc Kupietz04135302026-07-30 14:40:02 +0900811 pthread_t *pt = NULL;
812 wordlist *wl = NULL;
Marc Kupietz969cab92019-08-05 11:13:42 +0200813 int syn_threads = (M2 ? window * 2 : 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200814 int search_backw = 0;
Marc Kupietz04135302026-07-30 14:40:02 +0900815 char *result = NULL;
816 SV *res_sv;
817
818 for (a = 0; a < MAX_THREADS; a++) syn_nbs[a] = NULL;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200819
Marc Kupietz969cab92019-08-05 11:13:42 +0200820 if (cutoff < 1 || cutoff > words)
821 cutoff = words;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200822
823 wl = getTargetWords(word, search_backw);
Marc Kupietz04135302026-07-30 14:40:02 +0900824 if (wl == NULL || wl->length < 1 || wl->wordi[0] < 0 || syn_threads < 1) {
825 free(wl);
826 return newSVpv("", 0);
827 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200828
Marc Kupietz04135302026-07-30 14:40:02 +0900829 pt = (pthread_t *)malloc((num_threads + 1) * sizeof(pthread_t));
Marc Kupietzd6a163c2026-07-30 14:48:10 +0900830 window_sums = new_window_sums();
Marc Kupietz969cab92019-08-05 11:13:42 +0200831 a = posix_memalign((void **)&target_sums, 128, cutoff * sizeof(float));
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200832 memset(target_sums, 0, cutoff * sizeof(float));
833
834 printf("Starting %d threads\n", syn_threads);
835 fflush(stdout);
Marc Kupietz969cab92019-08-05 11:13:42 +0200836 for (a = 0; a < syn_threads; a++) {
837 pars[a].cutoff = cutoff;
838 pars[a].target_sums = target_sums;
839 pars[a].window_sums = window_sums;
840 pars[a].wl = wl;
841 pars[a].N = maxPerPos;
Marc Kupietz04135302026-07-30 14:40:02 +0900842 pars[a].best = NULL; /* getCollocators() allocates its own result array */
Marc Kupietz969cab92019-08-05 11:13:42 +0200843 pars[a].threshold = threshold;
844 pars[a].from = a;
845 pars[a].upto = a + 1;
846 pthread_create(&pt[a], NULL, getCollocators, (void *)&pars[a]);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200847 }
848 printf("Waiting for syn threads to join\n");
849 fflush(stdout);
Marc Kupietz969cab92019-08-05 11:13:42 +0200850 for (a = 0; a < syn_threads; a++) pthread_join(pt[a], (void *)&syn_nbs[a]);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200851 printf("Syn threads joint\n");
852 fflush(stdout);
Marc Kupietz04135302026-07-30 14:40:02 +0900853 result = malloc((maxPerPos > 0 ? maxPerPos : 1) * (max_w + 96) * syn_threads + 16);
Marc Kupietzbdd779a2024-08-05 10:02:29 +0200854 char *p = (char *) result;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200855 *p = 0;
Marc Kupietz0ab97392024-12-10 16:16:32 +0100856 if (strcmp(format, "tsv") == 0) {
857 for (a = syn_threads - 1; a >= 0; a--) {
Marc Kupietz04135302026-07-30 14:40:02 +0900858 if (syn_nbs[a] == NULL) continue;
859 for (b = 0; b < syn_nbs[a]->length; b++, entries++) {
Marc Kupietz0ab97392024-12-10 16:16:32 +0100860 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);
861 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200862 }
Marc Kupietz0ab97392024-12-10 16:16:32 +0100863 } else {
864 p += sprintf(p, "[");
865 for (a = syn_threads - 1; a >= 0; a--) {
Marc Kupietz04135302026-07-30 14:40:02 +0900866 if (syn_nbs[a] == NULL) continue;
867 for (b = 0; b < syn_nbs[a]->length; b++, entries++) {
Marc Kupietz0ab97392024-12-10 16:16:32 +0100868 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);
869 }
870 }
Marc Kupietz04135302026-07-30 14:40:02 +0900871 if (entries > 0)
872 p -= 2; /* drop the trailing ",\n" */
Marc Kupietz0ab97392024-12-10 16:16:32 +0100873 p += sprintf(p, "\n]");
Marc Kupietz969cab92019-08-05 11:13:42 +0200874 }
Marc Kupietz0ab97392024-12-10 16:16:32 +0100875
Marc Kupietz04135302026-07-30 14:40:02 +0900876 res_sv = newSVpv(result, 0);
877
878 free(result);
879 free(target_sums);
Marc Kupietzd6a163c2026-07-30 14:48:10 +0900880 free(window_sums);
Marc Kupietz04135302026-07-30 14:40:02 +0900881 free(pt);
882 free(wl);
883 for (a = 0; a < syn_threads; a++) free_knn(syn_nbs[a]);
884
885 return res_sv;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200886}
887
Marc Kupietz04135302026-07-30 14:40:02 +0900888SV *getPosWiseW2VCollocatorsAsTsv(char *word, long maxPerPos, long cutoff, float threshold) {
Marc Kupietz0ab97392024-12-10 16:16:32 +0100889 return getPosWiseW2VCollocators(word, maxPerPos, cutoff, threshold, "tsv");
890}
891
Marc Kupietz04135302026-07-30 14:40:02 +0900892SV *getPosWiseW2VCollocatorsAsJson(char *word, long maxPerPos, long cutoff, float threshold) {
Marc Kupietz0ab97392024-12-10 16:16:32 +0100893 return getPosWiseW2VCollocators(word, maxPerPos, cutoff, threshold, "json");
894}
895
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200896SV *get_neighbours(char *st1, int N, int sort_by, int search_backw, long cutoff, int dedupe, int no_similar_profiles) {
897 HV *result = newHV();
Marc Kupietz59865a92021-03-11 17:16:51 +0100898 float *target_sums = NULL;
Marc Kupietzd6a163c2026-07-30 14:48:10 +0900899 float *window_sums = NULL;
Marc Kupietz969cab92019-08-05 11:13:42 +0200900 long a, b, c, d, slice;
901 knn *para_nbs[MAX_THREADS];
902 knn *syn_nbs[MAX_THREADS];
903 knnpars pars[MAX_THREADS];
904 pthread_t *pt = (pthread_t *)malloc((num_threads + 1) * sizeof(pthread_t));
Marc Kupietz04135302026-07-30 14:40:02 +0900905 wordlist *wl = NULL;
Marc Kupietz969cab92019-08-05 11:13:42 +0200906 int syn_threads = (M2 ? window * 2 : 0);
907 int para_threads = (no_similar_profiles ? 0 : num_threads - syn_threads);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200908
Marc Kupietz04135302026-07-30 14:40:02 +0900909 for (a = 0; a < MAX_THREADS; a++) para_nbs[a] = syn_nbs[a] = NULL;
910
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200911 collocator *best = NULL;
Marc Kupietz969cab92019-08-05 11:13:42 +0200912 posix_memalign((void **)&best, 128, 10 * (N >= 200 ? N : 200) * sizeof(collocator));
913 memset(best, 0, (N >= 200 ? N : 200) * sizeof(collocator));
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200914
Marc Kupietz969cab92019-08-05 11:13:42 +0200915 if (N > MAX_NEIGHBOURS) N = MAX_NEIGHBOURS;
916
917 if (cutoff < 1 || cutoff > words)
918 cutoff = words;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200919
920 wl = getTargetWords(st1, search_backw);
Marc Kupietz969cab92019-08-05 11:13:42 +0200921 if (wl == NULL || wl->length < 1)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200922 goto end;
923
Marc Kupietz04135302026-07-30 14:40:02 +0900924 slice = (para_threads > 0 ? cutoff / para_threads : cutoff);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200925
Marc Kupietz969cab92019-08-05 11:13:42 +0200926 a = posix_memalign((void **)&target_sums, 128, cutoff * sizeof(float));
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200927 memset(target_sums, 0, cutoff * sizeof(float));
928
Marc Kupietzc0d41872021-02-25 16:33:22 +0100929 printf("Starting %d threads for paradigmatic search\n", para_threads);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200930 fflush(stdout);
Marc Kupietz969cab92019-08-05 11:13:42 +0200931 for (a = 0; a < para_threads; a++) {
932 pars[a].cutoff = cutoff;
933 pars[a].token = st1;
934 pars[a].wl = wl;
935 pars[a].N = N;
936 pars[a].best = &best[N * a];
937 if (merge_words == 0 || search_backw == 0) {
938 pars[a].from = a * slice;
939 pars[a].upto = ((a + 1) * slice > cutoff ? cutoff : (a + 1) * slice);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200940 } else {
941 pars[a].from = merge_words + a * slice;
Marc Kupietz969cab92019-08-05 11:13:42 +0200942 pars[a].upto = merge_words + ((a + 1) * slice > cutoff ? cutoff : (a + 1) * slice);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200943 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200944 printf("From: %ld, Upto: %ld\n", pars[a].from, pars[a].upto);
945 pthread_create(&pt[a], NULL, _get_neighbours, (void *)&pars[a]);
946 }
947 if (M2) {
Marc Kupietzd6a163c2026-07-30 14:48:10 +0900948 window_sums = new_window_sums();
Marc Kupietz969cab92019-08-05 11:13:42 +0200949 for (a = 0; a < syn_threads; a++) {
950 pars[a + para_threads].cutoff = cutoff;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200951 pars[a + para_threads].target_sums = target_sums;
952 pars[a + para_threads].window_sums = window_sums;
953 pars[a + para_threads].wl = wl;
954 pars[a + para_threads].N = N;
955 pars[a + para_threads].threshold = MIN_RESP;
956 pars[a + para_threads].from = a;
Marc Kupietz969cab92019-08-05 11:13:42 +0200957 pars[a + para_threads].upto = a + 1;
958 pthread_create(&pt[a + para_threads], NULL, getCollocators, (void *)&pars[a + para_threads]);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200959 }
960 }
961 printf("Waiting for para threads to join\n");
962 fflush(stdout);
Marc Kupietz969cab92019-08-05 11:13:42 +0200963 for (a = 0; a < para_threads; a++) pthread_join(pt[a], (void *)&para_nbs[a]);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200964 printf("Para threads joint\n");
965 fflush(stdout);
966
Marc Kupietz969cab92019-08-05 11:13:42 +0200967 /* if(!syn_nbs[0]) */
968 /* goto end; */
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200969
Marc Kupietz969cab92019-08-05 11:13:42 +0200970 qsort(best, N * para_threads, sizeof(collocator), cmp_activation);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200971
972 long long chosen[MAX_NEIGHBOURS];
Marc Kupietz59865a92021-03-11 17:16:51 +0100973 printf("N: %d\n", N);
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200974
Marc Kupietz969cab92019-08-05 11:13:42 +0200975 AV *array = newAV();
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200976 int i, j;
Marc Kupietz969cab92019-08-05 11:13:42 +0200977 int l1_words = 0, l2_words = 0;
978
979 for (a = 0, i = 0; i < N && a < N * para_threads; a++) {
980 int filtered = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200981 long long c = best[a].wordi;
982 if ((merge_words && dedupe && i > 1) || (!merge_words && dedupe && i > 0)) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200983 for (j = 0; j < i && !filtered; j++)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200984 if (strcasestr(&vocab[c * max_w], &vocab[chosen[j] * max_w]) ||
985 strcasestr(&vocab[chosen[j] * max_w], &vocab[c * max_w])) {
Marc Kupietz969cab92019-08-05 11:13:42 +0200986 printf("filtering %s %s\n", &vocab[chosen[j] * max_w], &vocab[c * max_w]);
987 filtered = 1;
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200988 }
Marc Kupietz969cab92019-08-05 11:13:42 +0200989 if (filtered)
Marc Kupietzf11d20c2019-08-02 15:42:04 +0200990 continue;
991 }
992
Marc Kupietz969cab92019-08-05 11:13:42 +0200993 if (0 && merge_words > 0) {
994 if (c >= merge_words) {
995 if (l1_words > N / 2)
996 continue;
997 else
998 l1_words++;
999 } else {
1000 if (l2_words > N / 2)
1001 continue;
1002 else
1003 l2_words++;
1004 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001005 }
1006
Marc Kupietz969cab92019-08-05 11:13:42 +02001007 // printf("%s l1:%d l2:%d i:%d a:%ld\n", &vocab[c * max_w], l1_words, l2_words, i, a);
1008 // fflush(stdout);
1009 HV *hash = newHV();
1010 SV *word = newSVpvf(&vocab[c * max_w], 0);
1011 chosen[i] = c;
1012 if (latin_enc == 0) SvUTF8_on(word);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001013 fflush(stdout);
Marc Kupietz969cab92019-08-05 11:13:42 +02001014 hv_store(hash, "word", strlen("word"), word, 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001015 hv_store(hash, "dist", strlen("dist"), newSVnv(best[a].activation), 0);
1016 hv_store(hash, "rank", strlen("rank"), newSVuv(best[a].wordi), 0);
1017 AV *vector = newAV();
1018 for (b = 0; b < size; b++) {
1019 av_push(vector, newSVnv(M[b + best[a].wordi * size]));
1020 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001021 hv_store(hash, "vector", strlen("vector"), newRV_noinc((SV *)vector), 0);
1022 av_push(array, newRV_noinc((SV *)hash));
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001023 i++;
1024 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001025 hv_store(result, "paradigmatic", strlen("paradigmatic"), newRV_noinc((SV *)array), 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001026
Marc Kupietz969cab92019-08-05 11:13:42 +02001027 for (b = 0; b < MAX_NEIGHBOURS; b++) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001028 best[b].wordi = -1L;
1029 best[b].activation = 0;
1030 best[b].probability = 0;
1031 best[b].position = 0;
1032 best[b].activation_sum = 0;
Marc Kupietz969cab92019-08-05 11:13:42 +02001033 memset(best[b].heat, 0, sizeof(float) * 16);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001034 }
1035
Marc Kupietz969cab92019-08-05 11:13:42 +02001036 float total_activation = 0;
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001037
1038 if (M2) {
1039 printf("Waiting for syn threads to join\n");
1040 fflush(stdout);
Marc Kupietz969cab92019-08-05 11:13:42 +02001041 for (a = 0; a < syn_threads; a++) pthread_join(pt[a + para_threads], (void *)&syn_nbs[a]);
1042 for (a = 0; a <= syn_threads; a++) {
1043 if (a == window) continue;
1044 total_activation += window_sums[a];
Marc Kupietz59865a92021-03-11 17:16:51 +01001045 printf("window pos: %ld, sum: %f\n", a, window_sums[a]);
Marc Kupietz969cab92019-08-05 11:13:42 +02001046 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001047 printf("syn threads joint\n");
1048 fflush(stdout);
1049
Marc Kupietz969cab92019-08-05 11:13:42 +02001050 for (b = 0; b < syn_nbs[0]->length; b++) {
1051 memcpy(best + b, &syn_nbs[0]->best[b], sizeof(collocator));
1052 best[b].position = -1; // syn_nbs[0]->pos[b];
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001053 best[b].activation_sum = target_sums[syn_nbs[0]->best[b].wordi];
Marc Kupietz969cab92019-08-05 11:13:42 +02001054 best[b].max_activation = 0.0;
1055 best[b].average = 0.0;
1056 best[b].probability = 0.0;
1057 best[b].cprobability = syn_nbs[0]->best[b].cprobability;
1058 memset(best[b].heat, 0, sizeof(float) * 16);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001059 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001060
1061 float best_window_sum[MAX_NEIGHBOURS];
Marc Kupietz59865a92021-03-11 17:16:51 +01001062 int found_index = 0, i = 0, w;
Marc Kupietz969cab92019-08-05 11:13:42 +02001063 for (a = 0; a < syn_threads; a++) {
1064 for (b = 0; b < syn_nbs[a]->length; b++) {
1065 for (i = 0; i < found_index; i++)
1066 if (best[i].wordi == syn_nbs[a]->best[b].wordi)
1067 break;
1068 if (i >= found_index) {
1069 best[found_index].max_activation = 0.0;
1070 best[found_index].average = 0.0;
1071 best[found_index].probability = 0.0;
1072 memset(best[found_index].heat, 0, sizeof(float) * 16);
1073 best[found_index].cprobability = syn_nbs[a]->best[b].cprobability;
1074 best[found_index].activation_sum = target_sums[syn_nbs[a]->best[b].wordi]; // syn_nbs[a]->best[b].activation_sum;
1075 best[found_index++].wordi = syn_nbs[a]->best[b].wordi;
1076 // printf("found: %s\n", &vocab[syn_nbs[a]->index[b] * max_w]);
1077 }
1078 }
1079 }
1080 sort_by = 0; // ALWAYS AUTO-FOCUS
1081 if (sort_by != 1 && sort_by != 2) { // sort by auto focus mean
1082 printf("window: %d - syn_threads: %d, %d\n", window, syn_threads, (1 << syn_threads) - 1);
1083 int wpos;
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001084 int bits_set = 0;
Marc Kupietz969cab92019-08-05 11:13:42 +02001085 for (i = 0; i < found_index; i++) {
1086 best[i].activation = best[i].probability = best[i].average = best[i].cprobability_sum = 0;
1087 for (w = 1; w < (1 << syn_threads); w++) { // loop through all possible windows
1088 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 +02001089 bits_set = 0;
Marc Kupietz969cab92019-08-05 11:13:42 +02001090 for (a = 0; a < syn_threads; a++) {
1091 if ((1 << a) & w) {
1092 wpos = (a >= window ? a + 1 : a);
1093 total_window_sum += window_sums[wpos];
1094 }
1095 }
1096 // printf("%d window-sum %f\n", w, total_window_sum);
1097 for (a = 0; a < syn_threads; a++) {
1098 if ((1 << a) & w) {
1099 wpos = (a >= window ? a + 1 : a);
1100 bits_set++;
1101 for (b = 0; b < syn_nbs[a]->length; b++)
1102 if (best[i].wordi == syn_nbs[a]->best[b].wordi) {
1103 // float acti = syn_nbs[a]->best[b].activation / total_window_sum;
1104 // word_window_sum += syn_nbs[a]->dist[b] * syn_nbs[a]->norm[b]; // / window_sums[wpos]; // syn_nbs[a]->norm[b];
1105 // word_window_sum += syn_nbs[a]->norm[b]; // / window_sums[wpos]; // syn_nbs[a]->norm[b];
1106 // 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 +02001107
Marc Kupietz969cab92019-08-05 11:13:42 +02001108 word_window_sum += syn_nbs[a]->best[b].activation; // / window_sums[wpos]; // syn_nbs[a]->norm[b];
1109 // 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 +02001110
Marc Kupietz969cab92019-08-05 11:13:42 +02001111 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 +02001112 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 +02001113 word_activation_sum += syn_nbs[a]->best[b].activation;
1114 if (syn_nbs[a]->best[b].activation > best[i].max_activation)
1115 best[i].max_activation = syn_nbs[a]->best[b].activation;
1116 if (syn_nbs[a]->best[b].activation > best[i].heat[wpos])
1117 best[i].heat[wpos] = syn_nbs[a]->best[b].activation;
1118 }
1119 }
1120 }
1121 if (bits_set) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001122 word_window_average /= bits_set;
Marc Kupietz969cab92019-08-05 11:13:42 +02001123 // word_activation_sum /= bits_set;
1124 // word_window_sum /= bits_set;
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001125 }
1126
Marc Kupietz969cab92019-08-05 11:13:42 +02001127 word_window_sum /= total_window_sum;
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001128
Marc Kupietz969cab92019-08-05 11:13:42 +02001129 if (word_window_sum > best[i].probability) {
1130 // best[i].position = w;
1131 best[i].probability = word_window_sum;
1132 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001133
Marc Kupietz969cab92019-08-05 11:13:42 +02001134 if (word_cprobability_sum > best[i].cprobability_sum) {
1135 best[i].position = w;
1136 best[i].cprobability_sum = word_cprobability_sum;
1137 }
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001138
Marc Kupietz969cab92019-08-05 11:13:42 +02001139 best[i].average = word_window_average;
1140 // best[i].activation = word_activation_sum;
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001141 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001142 }
1143 qsort(best, found_index, sizeof(collocator), cmp_probability);
1144 // for(i=0; i < found_index; i++) {
1145 // printf("found: %s - sum: %f - window: %d\n", &vocab[best[i].wordi * max_w], best[i].activation, best[i].position);
1146 // }
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001147
Marc Kupietz969cab92019-08-05 11:13:42 +02001148 } else if (sort_by == 1) { // responsiveness any window position
1149 int wpos;
1150 for (i = 0; i < found_index; i++) {
1151 float word_window_sum = 0, word_activation_sum = 0, total_window_sum = 0;
1152 for (a = 0; a < syn_threads; a++) {
1153 wpos = (a >= window ? a + 1 : a);
1154 for (b = 0; b < syn_nbs[a]->length; b++)
1155 if (best[i].wordi == syn_nbs[a]->best[b].wordi) {
1156 best[i].probability += syn_nbs[a]->best[b].probability;
1157 if (syn_nbs[a]->best[b].activation > 0.25)
1158 best[i].position |= 1 << wpos;
1159 if (syn_nbs[a]->best[b].activation > best[i].activation) {
1160 best[i].activation = syn_nbs[a]->best[b].activation;
1161 }
1162 }
1163 }
1164 }
1165 qsort(best, found_index, sizeof(collocator), cmp_activation);
1166 } else if (sort_by == 2) { // single window position
1167 for (a = 1; a < syn_threads; a++) {
1168 for (b = 0; b < syn_nbs[a]->length; b++) {
1169 for (c = 0; c < MAX_NEIGHBOURS; c++) {
1170 if (syn_nbs[a]->best[b].activation > best[c].activation) {
1171 for (d = MAX_NEIGHBOURS - 1; d > c; d--) {
1172 memmove(best + d, best + d - 1, sizeof(collocator));
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001173 }
1174 memcpy(best + c, &syn_nbs[a]->best[b], sizeof(collocator));
Marc Kupietz969cab92019-08-05 11:13:42 +02001175 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 +02001176 break;
1177 }
1178 }
1179 }
1180 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001181 } else { // sort by mean p
1182 for (a = 1; a < syn_threads; a++) {
1183 for (b = 0; b < syn_nbs[a]->length; b++) {
1184 for (c = 0; c < MAX_NEIGHBOURS; c++) {
1185 if (target_sums[syn_nbs[a]->best[b].wordi] > best[c].activation_sum) {
1186 for (d = MAX_NEIGHBOURS - 1; d > c; d--) {
1187 memmove(best + d, best + d - 1, sizeof(collocator));
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001188 }
1189 memcpy(best + c, &syn_nbs[a]->best[b], sizeof(collocator));
Marc Kupietz969cab92019-08-05 11:13:42 +02001190 best[c].position = (1 << 2 * window) - 1; // syn_nbs[a]->pos[b];
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001191 best[c].activation_sum = target_sums[syn_nbs[a]->best[b].wordi];
1192 break;
1193 }
1194 }
1195 }
1196 }
1197 }
1198 array = newAV();
Marc Kupietz969cab92019-08-05 11:13:42 +02001199 for (a = 0, i = 0; a < MAX_NEIGHBOURS && best[a].wordi >= 0; a++) {
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001200 long long c = best[a].wordi;
Marc Kupietz969cab92019-08-05 11:13:42 +02001201 /*
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001202 if (dedupe) {
1203 int filtered=0;
1204 for (j=0; j<i; j++)
1205 if (strcasestr(&vocab[c * max_w], chosen[j]) ||
1206 strcasestr(chosen[j], &vocab[c * max_w])) {
1207 printf("filtering %s %s\n", chosen[j], &vocab[c * max_w]);
1208 filtered = 1;
1209 }
1210 if(filtered)
1211 continue;
1212 }
1213*/
Marc Kupietz969cab92019-08-05 11:13:42 +02001214 chosen[i++] = c;
1215 HV *hash = newHV();
1216 SV *word = newSVpvf(&vocab[best[a].wordi * max_w], 0);
1217 AV *heat = newAV();
1218 if (latin_enc == 0) SvUTF8_on(word);
1219 hv_store(hash, "word", strlen("word"), word, 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001220 hv_store(hash, "rank", strlen("rank"), newSVuv(best[a].wordi), 0);
1221 hv_store(hash, "average", strlen("average"), newSVnv(best[a].average), 0);
1222 hv_store(hash, "prob", strlen("prob"), newSVnv(best[a].probability), 0);
1223 hv_store(hash, "cprob", strlen("cprob"), newSVnv(best[a].cprobability_sum), 0);
Marc Kupietz969cab92019-08-05 11:13:42 +02001224 hv_store(hash, "max", strlen("max"), newSVnv(best[a].max_activation), 0); // newSVnv(target_sums[best[a].wordi]), 0);
1225 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 +02001226 hv_store(hash, "pos", strlen("pos"), newSVnv(best[a].position), 0);
Marc Kupietz969cab92019-08-05 11:13:42 +02001227 best[a].heat[5] = 0;
1228 for (i = 10; i >= 0; i--) av_push(heat, newSVnv(best[a].heat[i]));
1229 hv_store(hash, "heat", strlen("heat"), newRV_noinc((SV *)heat), 0);
1230 av_push(array, newRV_noinc((SV *)hash));
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001231 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001232 hv_store(result, "syntagmatic", strlen("syntagmatic"), newRV_noinc((SV *)array), 0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001233 }
1234end:
Marc Kupietz969cab92019-08-05 11:13:42 +02001235 free(best);
Marc Kupietz04135302026-07-30 14:40:02 +09001236 free(target_sums);
Marc Kupietzd6a163c2026-07-30 14:48:10 +09001237 free(window_sums);
Marc Kupietz04135302026-07-30 14:40:02 +09001238 free(pt);
1239 free(wl);
1240 for (a = 0; a < MAX_THREADS; a++) {
1241 free_knn(para_nbs[a]);
1242 free_knn(syn_nbs[a]);
1243 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001244 return newRV_noinc((SV *)result);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001245}
1246
1247int dump_vecs(char *fname) {
Marc Kupietz969cab92019-08-05 11:13:42 +02001248 long i, j;
1249 FILE *f;
1250 /* if(words>100000)
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001251 words=100000;
1252*/
Marc Kupietz969cab92019-08-05 11:13:42 +02001253 if ((f = fopen(fname, "w")) == NULL) {
1254 fprintf(stderr, "cannot open %s for writing\n", fname);
1255 return (-1);
1256 }
1257 fprintf(f, "%lld %lld\n", words, size);
1258 for (i = 0; i < words; i++) {
1259 fprintf(f, "%s ", &vocab[i * max_w]);
1260 for (j = 0; j < size - 1; j++)
1261 fprintf(f, "%f ", M[i * size + j]);
1262 fprintf(f, "%f\n", M[i * size + j]);
1263 }
1264 fclose(f);
1265 return (0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001266}
1267
1268int dump_for_numpy(char *fname) {
Marc Kupietz969cab92019-08-05 11:13:42 +02001269 long i, j;
1270 FILE *f;
Marc Kupietzc0d41872021-02-25 16:33:22 +01001271 int max = words; // 300000;
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001272
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);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001276 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001277 for (i = 0; i < max; i++) {
1278 for (j = 0; j < size - 1; j++)
1279 fprintf(f, "%f\t", M[i * size + j]);
1280 fprintf(f, "%f\n", M[i * size + j]);
1281 printf("%s\r\n", &vocab[i * max_w]);
1282 }
1283 if (merged_end > 0) {
1284 for (i = 0; i < max; i++) {
1285 for (j = 0; j < size - 1; j++)
1286 fprintf(f, "%f\t", M[(merged_end + i) * size + j]);
1287 fprintf(f, "%f\n", M[(merged_end + i) * size + j]);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001288 printf("_%s\r\n", &vocab[i * max_w]);
1289 }
Marc Kupietz969cab92019-08-05 11:13:42 +02001290 }
1291 fclose(f);
1292 return (0);
Marc Kupietzf11d20c2019-08-02 15:42:04 +02001293}
Marc Kupietz043db152023-11-05 17:47:53 +01001294
1295unsigned long getVocabSize() {
1296 return (unsigned long) words;
1297}
Marc Kupietz6f317a92026-07-30 15:09:54 +09001298
1299/* First rank of the primary model in the merged vocabulary, 0 if no second
1300 model was merged in. mergeVectors() puts the merged in model at ranks
1301 [0, merged_end) and the primary model - the one the collocator db belongs
1302 to - at [merged_end, words). */
1303long getMergedEnd() {
1304 return (long) merged_end;
1305}