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.404 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )