blob: 0b396ec635338f9634733c4b10d5a23f483386f5 [file] [log] [blame]
use utf8;
package IDS::DeReKoVecs::Read;
use LWP::Simple;
use strict;
use warnings;
use Config;
my $src_file = undef;
our $opt_p = 5676;
our $opt_C;
# Cached values are large (a classic collocator profile is tens to hundreds of
# kilobytes of JSON), so the caches are size limited. Without a limit a long
# running worker accumulates one entry per queried word until the box runs out
# of memory. Defaults can be overridden via configure_cache().
our $CC_CACHE_MAX_KEYS = 200; # classic collocators
our $SP_CACHE_MAX_KEYS = 200; # similar profiles
BEGIN {
$src_file = __FILE__;
$src_file =~ s/Read.pm/derekovecs-server.c/;
}
use Inline C => "$src_file" => CLEAN_AFTER_BUILD => 0, BUILD_NOISY => 1, ccflags => $Config{ccflags} . "-Wall -Wno-unused-result -O4 -mno-avx2 -I/usr/local/include", libs => "-L/usr/local/lib64 -L/usr/local/lib -lcollocatordb";
#use Inline C => Config => BUILD_NOISY => 1, CFLAGS => $Config{cflags}." -O4 -mtune k9";
#use Inline C => Config => CLEAN_AFTER_BUILD => 0, ccflags => $Config{ccflags}." -Ofast -march k8 -mtune k8 ";
use Mojo::JSON qw(decode_json encode_json to_json);
use Mojo::Cache;
use Exporter qw(import);
our @EXPORT = qw(init_net load_sprofiles mergeVectors getVocabSize getDowntimeCalendar getCollocationAssociation getClassicCollocatorsCached getSimilarProfiles getSimilarProfilesCached getBiggestMergedDifferences getMergedEnd filter_garbage get_neighbours getVecs getWordNumber dump_vecs dump_for_numpy cos_similarity_as_json get_version getPosWiseW2VCollocators configure_cache cache_stats);
my $cccache = Mojo::Cache->new(max_keys => $CC_CACHE_MAX_KEYS); # classic collocator cache
my $spcache = Mojo::Cache->new(max_keys => $SP_CACHE_MAX_KEYS); # similar profile cache
# Adjust the caches at startup, e.g.
# configure_cache(collocators_max_keys => 500, no_cache => 1)
# A max_keys value <= 0 disables the respective cache. Setting a limit starts
# from an empty cache, so that a lowered limit takes effect immediately.
sub configure_cache {
my (%opt) = @_;
$opt_C = $opt{no_cache} if defined $opt{no_cache};
$cccache = Mojo::Cache->new(max_keys => $opt{collocators_max_keys})
if defined $opt{collocators_max_keys};
$spcache = Mojo::Cache->new(max_keys => $opt{profiles_max_keys})
if defined $opt{profiles_max_keys};
return;
}
sub cache_stats {
return {
collocators => {
keys => scalar keys %{$cccache->{cache} || {}},
max_keys => $cccache->max_keys
},
profiles => {
keys => scalar keys %{$spcache->{cache} || {}},
max_keys => $spcache->max_keys
}
};
}
sub getDowntimeCalendar {
my ($url) = @_;
if ($url =~ m/^\s*$/) {
return "";
}
my $calendar = LWP::Simple::get($url);
return $calendar;
}
sub getCollocationAssociation {
my ($c, $word, $collocate) = @_;
return getCollocationScores($word, $collocate)
}
sub getClassicCollocatorsCached {
my ($c, $word, $compare_to) = @_;
my $s2 = "";
# The collocator db is the one of the primary model, which sits at
# [merged_end, words) in the merged vocabulary, so its ranks have to be
# shifted back before they can be looked up.
my $merged_end = getMergedEnd();
if($word >= $merged_end) {
$word -= $merged_end;
}
my $pipe;
if($compare_to ne "") {
$c->app->log->debug("comparing syn neighbours to: $compare_to/getClassicCollocators?w=$word");
open $pipe, "lwp-request $compare_to/getClassicCollocators?w=$word |";
}
my $collocators = $opt_C ? undef : $cccache->get($word);
if(!defined $collocators) {
$c->app->log->debug("Getting classic collocates of $word.");
$collocators = getClassicCollocators($word);
$collocators =~ s/:(-?)(nan|inf)/:"${1}${2}"/g;
$collocators =~ s/"""/"\\""/g;
$cccache->set($word => $collocators) unless $opt_C;
} else {
$c->app->log->debug("Getting classic collocates for $word from cache.");
}
if(defined($pipe)) {
while(<$pipe>) {
$s2 .= $_;
}
close($pipe);
}
if(length($s2) > 2000) {
my $d1 = decode_json($collocators);
my $d2 = decode_json($s2);
my %d2ld;
my $minLd = 14;
foreach my $i (@{$d2->{collocates}}) {
$d2ld{$i->{word}}=$i->{ld};
$minLd=$i->{ld} if($i->{ld} < $minLd);
}
foreach my $i (@{$d1->{collocates}}) {
my $w = $i->{word};
$i->{delta} = $i->{ld} - (defined $d2ld{$w} ? $d2ld{$w} : $minLd-0.1);
}
return(encode_json($d1));
} else {
my $d1 = decode_json($collocators);
foreach my $i (@{$d1->{collocates}}) {
$i->{delta} = 0;
}
return(encode_json($d1));
}
}
sub getSimilarProfilesCached {
my ($c, $word) = @_;
# like the collocator db, the precomputed profiles belong to the primary
# model, see getClassicCollocatorsCached()
my $merged_end = getMergedEnd();
$word -= $merged_end if $word >= $merged_end;
my $profiles = $opt_C ? undef : $spcache->get($word);
if(!defined $profiles) {
$profiles = getSimilarProfiles($word);
$spcache->set($word => $profiles) unless $opt_C;
} else {
$c->app->log->debug("Getting similar profiles for $word from cache:");
}
return $profiles;
}
return 1;