Algorithm-Classifier-IsolationForest

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

          the classic runtime build even when the flags match
        - iforest accel updated to reflect various changes to the C backend
        - scoring: the per-leaf path-length adjustment c(size) is now
          precomputed at tree-pack time and stored in the (previously
          unused) third slot of packed leaf records, removing a log()
          call per point per tree from the C scoring hot loop -- about
          25% faster axis-mode scoring; results are bit-identical
        - scoring: score_all_xs now picks between two loop shapes based
          on total forest size: small forests keep the point-major loop
          (whole forest stays cache-resident anyway), while forests
          over 4 MB switch to a tree-blocked loop that walks a block of
          points through one tree at a time so each tree stays hot in
          L1/L2 instead of being re-streamed from memory per point --
          measured ~3.2x faster extended-mode scoring at 400 trees
          (20k points, 16 features); both shapes add the same terms in
          the same order, so scores are bit-identical either way
        - any -march (IF_ARCH / IF_NATIVE, configure- or run-time) now
          automatically adds -ffp-contract=off: with FMA available the
          compiler otherwise contracts a*b+c into fused multiply-adds
          whose different rounding silently broke the use_c bit-parity
          guarantee (one ulp in a split value builds a different tree);

lib/Algorithm/Classifier/IsolationForest.pm  view on Meta::CPAN

    sm = (double*)SvPVbyte_force(sm_sv, tl);

    /* Two loop shapes over the same per-point ascending-t additions --
     * bit-identical results either way, so the size heuristic choosing
     * between them can never change scores.
     *
     * Point-major (small forests): each point walks all trees with its
     * path-length sum held in a register.  Cheapest per walk, and the
     * whole forest stays cache-resident across points anyway.
     *
     * Tree-blocked (large forests): once the forest outgrows L3, the
     * point-major loop re-streams every tree's nodes and coefficients
     * from memory for every point -- an extended-mode tree is ~56 KB
     * at 16 features (24 KB nodes + 32 KB dense coefficients), and its
     * per-tree scoring cost measured 2.2x worse at 400 trees than at
     * 100.  Walking a block of points through ONE tree at a time keeps
     * that tree hot in L1/L2 while the block's rows stream through it
     * (measured 3.1x faster at 400 extended trees, 20k points).  The
     * blocked shape pays an sm[i] load+store per walk instead of a
     * register add, which measurably hurts cheap axis walks while the
     * forest still fits in cache -- hence the byte threshold rather
     * than always tiling. */
    if (forest_bytes <= (size_t)4 * 1024 * 1024) {
#ifdef _OPENMP
        #pragma omp parallel for schedule(static) if(use_openmp)
#endif
        for (int i = 0; i < n_pts; i++) {
            const double *xi = xd + (size_t)i * (size_t)n_feats;
            double sum = 0.0;

lib/Algorithm/Classifier/IsolationForest.pm  view on Meta::CPAN

 *
 * min_votes == 0: sm[i] = the point's full vote count over all n_trees
 *   trees (a small integer stored as a double, so the existing
 *   finalize_* helpers work on the buffer unchanged).
 * min_votes > 0:  sm[i] = 1.0/0.0 anomaly label, with per-point early
 *   exit: the walk stops as soon as the point has min_votes votes (the
 *   remaining trees can't change the outcome) or can no longer reach
 *   min_votes.  This is MVIForest's "stop at majority" scoring loop.
 *
 * Always point-major, unlike score_all_xs's two loop shapes: the vote
 * count / early exit is per-point state, so a tree-blocked loop would
 * have to re-load it per walk and could never exit a point early.
 * Votes are integer counts, so there is no summation-order concern
 * either way.  Thread-safety matches score_all_xs: the parallel region
 * reads extracted pointers and writes a unique sm[i] per iteration. */
void vote_all_xs(SV* nodes_av_sv, SV* idx_av_sv, SV* val_av_sv,
                 SV* x_sv, SV* sm_sv,
                 int n_pts, int n_feats, int n_trees,
                 double depth_cut, int min_votes, int use_openmp){
    STRLEN tl;
    AV *nodes_av, *idx_av, *val_av;



( run in 0.422 second using v1.01-cache-2.11-cpan-cd2fffc590a )