Releases: evandeilton/OptimalBinningWoE
Release list
v1.11.0 — Runtime audit: crash, hang, data-loss and reproducibility fixes
Runtime audit of the C++ engine, following the static audit that shipped in 1.10.0. This round was driven by instrumented builds (-fsanitize=address,undefined), a degenerate-input stress harness, and a golden-output regression suite covering all 37 exported algorithms (~3,200 result comparisons).
Every fix below is pinned by a test in tests/testthat/test-regression-audit.R, and each of those tests fails on 1.10.0.
Crashes and hangs
-
ob_categorical_ivbcrashed the R session — a segmentation fault, not a catchable error — for any feature with no more categories thanmax_bins. With the defaultmax_bins = 5that meant every 2- to 5-level predictor: sex, marital status, region, education. Thencat <= max_binsfast path skipped the only placestats_cachewas built, while result assembly dereferenced it unconditionally. -
ob_numerical_ir,ob_numerical_jediandob_numerical_jedi_mwoelooped forever whenevermin_binsexceeded the number of distinct feature values — routine when onemin_binsis applied across a whole feature set. The loops contained noR_CheckUserInterrupt(), so Ctrl-C could not interrupt them either; the session had to be killed.
Silent data loss
ob_numerical_cmdiscarded observations. Its equal-frequency pre-binner, on hitting a run of tied values straddling a bin boundary, advanced past those records without assigning them to any bin. In testing this lost 26% of an integer feature and 40% of a coarse one, with the reported WoE/IV computed from the survivors alone.
Interval convention — standardised on (a, b]
The package previously disagreed with itself about what a bin is. Labels advertised (a;b] while many algorithms assigned values as [a;b); ob_apply_woe_num() had its two binary searches swapped, so both settings of include_upper_bound did the opposite of what the argument documents; and several pre-binners split runs of tied values, so their counts could not be derived from their own cutpoints at all. A value landing exactly on a cutpoint — routine for integer, rounded or currency features — could be scored into a different bin than the one it was trained in.
Measured across 63 algorithm/dataset combinations: 31 violated the documented convention before this release, 0 do now.
Reproducibility
-
ob_categorical_sabnow honoursset.seed(). It was seeded fromstd::random_device, so identical input returned a different binning on every call. Four runs under one seed previously gave bins 4/3/5/5 with three distinct IV values — unusable for auditable or regulated models. -
obcorr()now returns a stable row order. Per-thread buffers were spliced together in anomp criticalblock, so row order depended on thread scheduling; two runs with the same data and same thread count could differ. Correlation values are unchanged.
CRAN policy
-
obcorr()no longer seizes every core. With the defaultthreads = 0it calledomp_set_num_threads(std::thread::hardware_concurrency()). CRAN Repository Policy requires never using more than two cores by default; this was an archival risk. The default is now at most 2, honouringOMP_NUM_THREADS, with an explicitthreadsargument still respected. -
Removed eight stale Dropbox conflict copies from
src/thatR CMD buildwould have shipped and compiled, producing duplicate symbols.
Compatibility
This release changes numeric output. Of 3,240 golden comparisons against 1.10.0, 2,353 are unchanged; 12 numerical algorithms plus ob_categorical_sab differ, concentrated in data with ties (integer, rounded or currency features). Continuous features without ties are largely unaffected. No public R API changed.
Users with scorecards in production should expect bin boundaries to shift on discrete features, and re-validate. The previous results were silently incorrect at those boundaries.
Verification
R CMD check green on Ubuntu (devel, release, oldrel-1/2/3) and Windows (release). Test suite runs with 0 failures and 0 skips — the ob_numerical_sketch test disabled since 1.0.7 for a segfault is re-enabled, re-verified clean under -fsanitize=address,undefined. The full suite is also clean under ASan/UBSan.
Known issues
ob_categorical_dpandob_categorical_fetbreportWoE = IV = 0for perfectly separating bins instead of applying smoothing.- Return fields are not uniform across algorithms:
event_rateappears in 13 of 37,total_ivin 28 of 37, andivis missing from both_dmivvariants. ob_apply_woe_num()does not support the multinomial*_jedi_mwoevariants.
v1.10.0 — C++ Engine Audit & Hardening
C++ Engine — Comprehensive Audit & Hardening
Full static audit of the C++ engine covering all 36 binning algorithms. Zero public R API changes — fully backward compatible.
Bug Fixes
OB_LogisticRegression— Threshold-based singularity guard;Eigen::LDLTreplaceshessian.inverse();cwiseMax(0)preventsNaNSEsOBN_MDLPmonotonicity direction —is_monotonic()/enforce_monotonicity()now auto-detect ascending vs descending trend (Welford slope); previously hardcoded ascending caused unnecessary merges on negative correlationsOBC_DPbacktracking OOB — Guard beforestatic_cast<size_t>(prev_j); invalid index raises descriptive error instead of UBOBN_DPpush-before-validate — Target validation now fires beforetarget_vec.push_back()OBN_MDLPlog2(0) — Guard for single-bin MDL costNumericalBinconstructor —countalways derived fromcount_pos + count_neg; invariant enforced at construction time
Performance
OBC_DPDP outer loop removed — Deterministic DP was wrapped in a redundant 1000-iteration loop; up to 1000× speedupOBC_DP::ensure_max_prebins()— O(m² log m) full re-sort per merge → O(m) binary-search insertionOBN_MDLP::apply_mdl_merging()— O(k³) full-vector copy per candidate → O(k²) analytical MDL deltaOBN_BB::quantile()— O(n_prebins × n log n) per-call sorts → single sort inprebinning()OBN_DPcorrelation — Naive Pearson (catastrophic cancellation) → Welford's online algorithm
CRAN / ODR Safety
safe_math.h—constexpr→inlinefor 6 math functions (SOLARIS/Studio compat)chi_square_utils.h— Namespace-scopeconstmap →staticlocal function (one copy per process)entropy_utils.h— 81 KBENTROPY_LUTper-TU → sharedstaticlocal instanceOBC_CM_v5— DuplicateChiSquareCacheclass removed; uses shared header- 35
.cppfiles — Duplicateusing namespace Rcppbefore#includeheaders removed
Code Quality
- Dead commented-out code blocks removed (
OBC_DP) - Variable shadowing fixed (
OBN_DP:total_count→rare_total) [[Rcpp::plugins(cpp11)]]standardised across all files- Auto monotonicity detection implemented for categorical DP (
"auto"trend)
OptimalBinningWoE 1.0.9
- CRAN Fix (2026-03-14) - Replaced
Rf_errorwithRcpp::stop:- Fixed C++ Exception Handling: Addressed an issue reported by @Enchufa2 regarding the usage of
::Rf_errorinsidecatch(...)blocks. Updated all instances to useRcpp::stopto ensure proper C++ stack unwinding and avoid memory leaks. - Affected Files:
src/OBN_LPDB_v5.cpp,src/OBN_EWB_v5.cpp,src/OBN_KMB_v5.cpp,src/OBN_LDB_v5.cpp,src/OBN_MBLP_v5.cpp.
- Fixed C++ Exception Handling: Addressed an issue reported by @Enchufa2 regarding the usage of
OptimalBinningWoE 1.0.8
- CRAN Fix (2026-01-28) - LTO/ODR Compliance:
- Fixed One Definition Rule (ODR) violations: Wrapped internal helper classes
IVCacheandCumulativeStatsCachein anonymous namespaces withinOBC_GMB_v5.cpp,OBC_IVB_v5.cpp, andOBC_JEDI_v5.cpp. This resolves Link-Time Optimization (LTO) warnings/errors on CRAN checks.
- Fixed One Definition Rule (ODR) violations: Wrapped internal helper classes
OptimalBinningWoE 1.0.7
-
UBSAN Investigation Fix (2026-01-27) - Addressing persistent memory safety errors:
-
Temporarily disabled
ob_categorical_sketchtests: The sketch-based categorical binning
algorithm is under investigation for persistent UBSAN memory errors that appear to be
related to cache invalidation timing in GitHub Actions CI environment. -
Removed
MergeCacheclass fromOBC_Sketch_v5.cpp: Completely removed the caching
mechanism and implemented on-the-fly divergence calculation to eliminate potential
memory corruption sources.
-
-
Affected Files:
src/OBC_Sketch_v5.cpp: MergeCache class removed, divergence calculated on-the-flytests/testthat/test-categorical-all.R: Sketch tests temporarily commented out
-
No API Changes: Fully backward compatible with v1.0.6.