Algorithm-Classifier-IsolationForest
view release on metacpan or search on metacpan
Makefile.PL view on Meta::CPAN
}
1;
BUILDFLAGS
close $bf_fh or die "Makefile.PL: closing $bf_path failed: $!";
# Appended to the Makefile when the install-time build is on. Modelled on
# what Inline::MakeMaker generates, with two differences: only the one
# module that actually embeds C gets a rule (Inline::MakeMaker emits one
# per .pm in lib/), and install mode is signalled to the module via
# IF_INSTALL_BUILD=1 in the rule's environment instead of a global
# -MInline=_INSTALL_ import, so the module can pass Inline's _INSTALL_
# config itself alongside NAME/VERSION. Inline's install mode reads the
# version and blib/arch destination from @ARGV. The trailing -e writes a
# stub .inl file satisfying the make dependency (also what
# Inline::MakeMaker does); the compiled object itself lands under
# blib/arch/auto/ and is picked up by `make install`.
sub MY::postamble {
return '' unless $prebuilt;
return <<'POSTAMBLE';
# --- Inline::C install-time build (generated by Makefile.PL):
examples/basic-anomaly-detection.pl view on Meta::CPAN
# perl -Ilib examples/basic-anomaly-detection.pl
# or, if the module is installed:
# perl examples/basic-anomaly-detection.pl
use strict;
use warnings;
use Algorithm::Classifier::IsolationForest;
use constant PI => 3.14159265358979;
# Seed the global RNG so the *data* is reproducible from run to run. The forest
# gets its own seed below; fit() reseeds internally, which is fine because the
# data has already been generated by then.
srand(7);
sub gaussian {
my ( $mu, $sigma ) = @_;
my $u1 = rand() || 1e-12;
my $u2 = rand();
return $mu + $sigma * sqrt( -2 * log($u1) ) * cos( 2 * PI * $u2 );
}
lib/Algorithm/Classifier/IsolationForest.pm view on Meta::CPAN
double u1 = sm64_drand(s);
double u2;
if (u1 == 0.0) u1 = 1e-12;
u2 = sm64_drand(s);
return sqrt(-2.0 * log(u1)) * cos(6.283185307179586 * u2);
}
/* Thread-safe twin of _build_node_c: same split algorithm, but reads
* randomness from a thread-private splitmix64 stream instead of
* Drand01(), and writes into a TreeBuf instead of allocating Perl AVs
* -- so it touches no interpreter-global state and is safe to call
* concurrently from an OpenMP parallel region, one tree per thread. */
static int _build_node_packed(const double* x, int nf, int* idxs, int size,
int depth, int limit, int mode_flag,
int ext_active, TreeBuf *buf, uint64_t *rng) {
double *lo, *hi;
int *varying, nv, f, my_idx;
if (depth >= limit || size <= 1) {
my_idx = tb_push_node(buf, 0.0, (double)size, 0.0, 0.0, 0.0, 0.0);
free(idxs);
lib/Algorithm/Classifier/IsolationForest.pm view on Meta::CPAN
#
# Example:
# $self->_learn_contamination_threshold_streaming( 'train.csv', 1, 1_000_000, 1 );
sub _learn_contamination_threshold_streaming {
my ( $self, $path, $skip_first, $n, $c_scan ) = @_;
my $k = int( $self->{contamination} * $n + 0.5 );
$k = 1 if $k < 1;
$k = $n if $k > $n;
# Whole set flagged: sit the cut just below the global minimum score.
if ( $k >= $n ) {
my $min;
$self->_stream_scores( $path, $skip_first, sub { $min = $_[0] if !defined $min || $_[0] < $min }, $c_scan );
$self->{threshold} = $min - 1e-9;
return;
}
# One scoring pass keeps the k+1 largest scores (a min-heap rooted at the
# smallest kept). contamination <= 0.5 bounds k at n/2, so this tail is
# always the smaller side of the split.
lib/Algorithm/Classifier/IsolationForest/App.pm view on Meta::CPAN
package Algorithm::Classifier::IsolationForest::App;
use 5.006;
use strict;
use warnings;
use App::Cmd::Setup -app;
sub global_opt_spec {
return ( [ 'help|h' => "This usage screen." ], [ 'version|v' => "This usage screen." ], );
}
=head1 NAME
Algorithm::Classifier::IsolationForest::App - the App::Cmd application behind the iforest command
=head1 DESCRIPTION
The L<App::Cmd> application class C<bin/iforest> runs. Subcommands live
under C<Algorithm::Classifier::IsolationForest::App::Command::> and are
discovered by App::Cmd, so adding a module there adds a command -- there
is no registry to update.
They all inherit from
L<Algorithm::Classifier::IsolationForest::App::Command>, which App::Cmd
also finds by name alone.
=head1 METHODS
=head2 global_opt_spec
The options every subcommand accepts on top of its own, as the list of
arrayrefs L<Getopt::Long::Descriptive> expects. App::Cmd calls this
while assembling the option spec for whichever command is about to run.
iforest fit -h # handled through this spec
=cut
1;
lib/Algorithm/Classifier/IsolationForest/App/Command.pm view on Meta::CPAN
package Algorithm::Classifier::IsolationForest::App::Command;
use strict;
use warnings;
use App::Cmd::Setup -command;
sub global_opt_spec {
my ( $class, $app ) = @_;
return ( $class->options($app), );
}
sub validate_args {
my ( $self, $opt, $args ) = @_;
if ( $opt->{help} ) {
my ($command) = $self->command_names;
$self->app->execute_command( $self->app->prepare_command( "help", $command ) );
exit;
lib/Algorithm/Classifier/IsolationForest/App/Command.pm view on Meta::CPAN
Every C<iforest> subcommand inherits from this. L<App::Cmd> finds it by
name alone -- L<Algorithm::Classifier::IsolationForest::App>'s C<-app>
setup looks for C<< <app class>::Command >> -- so the command modules
never mention it.
It earns its keep through L</validate_args>, which makes C<-h> print a
command's help instead of being validated like any other flag.
=head1 METHODS
=head2 global_opt_spec
Option-spec hook delegating to an C<options> method on the command,
taking the L<App::Cmd> application object and returning whatever that
method returns.
Nothing reaches this in practice: App::Cmd calls C<global_opt_spec> on
the application class rather than on the command base, and no command
here defines C<options> -- they all use App::Cmd's own C<opt_spec>.
Calling it would die on the missing method.
=head2 validate_args
App::Cmd's per-command validation hook, wrapped so C<-h> short-circuits
it. Without this, C<-h> would fall through to the command's own
C<validate> and trip over whatever required options the user has not
typed yet -- which is exactly the moment they are reaching for the help.
t/91-streamd.t view on Meta::CPAN
subtest 'saves: command, interval, symlink' => sub {
my $r = rt( $c, { cmd => 'save', tag => 's1' } );
like( $r->{ok}{saved}, qr/\Aoiforest-\d{8}-\d{6}(?:-\d+)?\.json\z/, 'save returns the file name' );
is( $r->{tag}, 's1', 'save reply carries the tag' );
ok( -f "$mdir/$r->{ok}{saved}", 'the timestamped file exists' );
ok( -l $latest, 'latest.json is a symlink' );
is( readlink($latest), $r->{ok}{saved}, 'and points at the newest save (relative target)' );
# Interval saves happen only after learning; learn then wait past the
# 1s interval.
my $count_before = () = glob("$mdir/oiforest-*.json");
rt( $c, { row => [ 0.2, 0.8 ] } );
select( undef, undef, undef, 2.5 ); ## no critic (ProhibitSleepViaSelect)
rt( $c, { cmd => 'ping' } ); # tick the loop
my $count_after = () = glob("$mdir/oiforest-*.json");
cmp_ok( $count_after, '>', $count_before, 'a periodic save fired after learning' );
}; ## end 'saves: command, interval, symlink' => sub
my $seen_at_shutdown = rt( $c, { cmd => 'stats' } )->{ok}{seen};
subtest 'clean shutdown and resume' => sub {
is( stop_daemon($daemon), 0, 'SIGTERM exits 0' );
undef $daemon;
ok( !-e $sock, 'socket removed' );
ok( !-e $pidf, 'pid file removed' );
( run in 1.209 second using v1.01-cache-2.11-cpan-0fb53d1c279 )