Algorithm-Classifier-IsolationForest
view release on metacpan or search on metacpan
lib/Algorithm/Classifier/IsolationForest.pm view on Meta::CPAN
$Algorithm::Classifier::IsolationForest::HAS_SIMD # 0/1 (OpenMP 4.0+)
$Algorithm::Classifier::IsolationForest::OPT_LEVEL # e.g. "-O3 -march=native", '' if HAS_C is 0
$Algorithm::Classifier::IsolationForest::C_SOURCE # 'prebuilt' / 'runtime', '' if HAS_C is 0
Neither dependency is required. Without C<Inline::C> the module falls
back to a pure-Perl implementation that produces identical results, just
slower; without OpenMP the C backend runs single-threaded.
The bundled C<iforest accel> subcommand performs a tiny fit + score and
prints which backend is active (including the build flags below), which
is the recommended way to verify the build picked up the optional
dependencies on a given machine.
=head2 Compile at install time (the prebuilt object)
When C<Inline::C> is usable while the distribution itself is being
built, C<perl Makefile.PL> arranges for the C backend to be compiled
once during C<make> and installed alongside the module like any XS
object. At run time that object is loaded directly through
L<XSLoader>: no C compiler, no C<Inline> modules, and no C<_Inline/>
cache directory are needed on the machine the module ends up running
t/01-accel-flags.t view on Meta::CPAN
#!perl
# 01-accel-flags.t
#
# Sanity-checks the $HAS_C / $HAS_OPENMP / $HAS_SIMD package variables
# that record what the Inline::C build picked up at module load. These
# are read by `iforest accel` and by any user code that wants to make
# decisions based on which backend is active.
#
# What we verify (always):
# * the three vars are defined and 0/1-valued
# * SIMD => OpenMP (`#pragma omp simd` is gated on _OPENMP)
# * OpenMP => Inline::C (OpenMP only matters with the C backend)
#
# We do NOT assert that any backend is *available* -- the test should
# pass cleanly on a pure-Perl install too.
use strict;
use warnings;
use Test::More;
t/02-accel-selection.t view on Meta::CPAN
$out,
qr/Active backend:.*-- (prebuilt at install time|compiled at run time)/,
'Active backend summary includes the C object source'
);
} else {
like( $out, qr/C object\s*:\s*none/, 'C object line says none without a C backend' );
}
# Cross-check the per-feature status lines against the package
# flags the test process observed. This is what makes this test
# actually verify selection rather than just "doesn't crash".
if ($HAS_C) {
like( $out, qr/Inline::C\s*:\s*available/, 'CLI reports Inline::C available, matching $HAS_C' );
} else {
like( $out, qr/Inline::C\s*:\s*not available/, 'CLI reports Inline::C not available, matching $HAS_C' );
}
if ($HAS_OPENMP) {
like( $out, qr/OpenMP\s*:\s*available/, 'CLI reports OpenMP available, matching $HAS_OPENMP' );
} else {
like( $out, qr/OpenMP\s*:\s*not available/, 'CLI reports OpenMP not available, matching $HAS_OPENMP' );
}
t/31-undef-column-no-warnings.t view on Meta::CPAN
subtest "[$be_name] score_predict_samples emits no warnings with undef column(s)" => sub {
my @warns = _capture_warnings( sub { $f->score_predict_samples( \@undef_pts ) } );
is( scalar @warns, 0, 'no warnings from score_predict_samples on undef column(s)' );
};
subtest "[$be_name] score_predict_split emits no warnings with undef column(s)" => sub {
my @warns = _capture_warnings( sub { $f->score_predict_split( \@undef_pts ) } );
is( scalar @warns, 0, 'no warnings from score_predict_split on undef column(s)' );
# Also verify the new method returns scores/labels consistent with
# score_predict_samples on the same data (numeric equality on scores,
# exact equality on labels).
my $pairs = $f->score_predict_samples( \@undef_pts );
my ( $scores, $labels ) = $f->score_predict_split( \@undef_pts );
is( scalar @$scores, scalar @$pairs, 'split returns matching scores length' );
is( scalar @$labels, scalar @$pairs, 'split returns matching labels length' );
my $mismatches = 0;
for my $i ( 0 .. $#$pairs ) {
$mismatches++ if $scores->[$i] != $pairs->[$i][0];
t/33-parallel-fit.t view on Meta::CPAN
subtest 'parallel_fit number must be a positive integer' => sub {
eval { $CLASS->new( parallel_fit => -1 ) };
like( $@, qr/parallel_fit/, 'negative integer rejected' );
eval { $CLASS->new( parallel_fit => 'abc' ) };
like( $@, qr/parallel_fit/, 'non-numeric rejected' );
};
subtest 'parallel_fit=1 is equivalent to serial' => sub {
# n_trees > 1 and parallel_fit == 1 hits the same serial branch as
# parallel_fit undef. Just verify it produces a working model.
my $f = $CLASS->new(
n_trees => 20,
sample_size => 256,
seed => 7,
parallel_fit => 1,
);
$f->fit( \@train );
is( scalar @{ $f->{trees} }, 20, 'tree count matches n_trees' );
my $s = $f->score_samples( \@query );
cmp_ok( $s->[1], '>', $s->[0], 'model separates outlier from inlier' );
( run in 1.578 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )