Skip to main content

A high-performance, O(P) data structure for weighted random sampling of binned probabilities, ideal for large-scale simulations.

Project description

DigitBinIndex

A DigitBinIndex is a tree-based data structure designed for efficient weighted random selection and removal from large collections of items. It is optimized for scenarios involving millions of items where probabilities are approximate and high performance is critical, such as simulations for Wallenius' noncentral hypergeometric distribution or Fisher's noncentral hypergeometric distribution.

This library provides high-performance solutions for both major types of noncentral hypergeometric distributions:

  • Sequential Sampling (Wallenius'): Modeled by select_and_remove, where items are selected and removed one at a time.
  • Simultaneous Sampling (Fisher's): Modeled by select_many_and_remove, where a batch of unique items is selected and removed together.

The Core Problem

In simulations, forecasts, or statistical models (e.g., mortality models, Monte Carlo simulations, or machine learning sampling), managing a large, dynamic set of probabilities is common. A key task is to randomly select items based on their weights, often removing them afterward, and repeat this process efficiently. For datasets with millions of items, achieving high performance while maintaining reasonable accuracy is a significant challenge, especially for complex distributions like Wallenius' or Fisher's.

How It Works

DigitBinIndex is a radix tree that organizes items into bins based on the decimal digits of their rescaled probabilities, enabling fast weighted random selection and updates.

  1. Digit-based Tree Structure: Each level of the tree corresponds to a decimal place of the rescaled weight. For example, a weight of 0.543 at precision 3 is rescaled to 543 and placed by traversing the path: root -> child[5] -> child[4] -> child[3].

  2. Adaptive Bin Storage: Leaf nodes act as bins, storing item IDs in either a fast Vec<u32> (for small bins) or a compressed Roaring Bitmap (for large bins). The bin type is chosen automatically for optimal performance and memory use if a capacity hint is given.

  3. Accumulated Value Index: Each node tracks the accumulated_value (sum of weights beneath it), supporting O(P) weighted random selection, where P is the configured precision (number of decimal places).

Features

  • High Performance: Outperforms general-purpose data structures like Fenwick Trees for both sequential and simultaneous weighted sampling.
  • Dual-Model Support: Optimized methods for Wallenius' (select_and_remove) and Fisher's (select_many_and_remove) distributions.
  • O(P) Complexity: Core operations (add, remove, select) have a time complexity of O(P), where P is the fixed precision, effectively constant for a given configuration.
  • Memory Efficiency: Combines a sparse radix tree with Roaring Bitmaps for efficient storage, especially for sparse or clustered weight distributions.
  • Python Integration: Seamless Python bindings via pyo3 for cross-language support.

Performance

DigitBinIndex trades a small, controllable amount of precision by binning probabilities to achieve significant performance gains. Below are benchmarks comparing DigitBinIndex against an optimized Fenwick Tree implementation, run on a standard desktop (Intel i7, 16GB RAM, Rust 1.75) with typical weight distributions.

Wallenius' Draw (Sequential Selections)

Measures the total time for 1,000 select_and_remove operations, selecting and removing one item at a time. DigitBinIndex's O(P) complexity provides a significant advantage as dataset size grows.

Number of Items (N) DigitBinIndex Loop Time FenwickTree Loop Time Speedup Factor
100,000 ~0.46 ms ~1.77 ms ~3.9x faster
1,000,000 ~0.52 ms ~13.58 ms ~26.1x faster

Fisher's Draw (Simultaneous Selections)

Measures the time to select a batch of unique items (1% of the total population, e.g., k=1,000 for N=100,000). DigitBinIndex uses batched rejection sampling, optimized for efficiency.

Scenario (N items, draw k) DigitBinIndex Time FenwickTree Time Speedup Factor
N=100k, k=1k ~0.47 ms ~1.87 ms ~4.0x faster
N=1M, k=10k ~5.48 ms ~20.16 ms ~3.7x faster

Higher precision settings slightly increase runtime but remain O(P), independent of N. Memory usage depends on the weight distribution and precision, with sparse distributions benefiting most from Roaring Bitmaps.


When to Choose DigitBinIndex

Use DigitBinIndex when:

  • You need high-performance sampling for Wallenius' or Fisher's distributions.
  • Your dataset is large (N > 100,000).
  • Probabilities are approximate, as is common in empirical data, simulations, or machine learning models.
  • Performance is more critical than perfect precision.

Consider a Fenwick Tree if you require exact precision and your weights differ only at high decimal places (e.g., 0.12345 vs. 0.12346), though this comes at the cost of O(log N) complexity and higher memory usage for large datasets.


Choosing a Precision

The precision parameter controls the radix tree's depth, balancing accuracy, performance, and memory. Higher precision improves sampling accuracy but increases memory usage (up to 10x per additional level) and slightly impacts runtime.

The Rule of Thumb

A precision of 3 or 4 (default: 3) is recommended for most applications. This captures sufficient detail for typical weight distributions while maintaining excellent performance and low memory usage.

The Mathematical Intuition

Each decimal place contributes exponentially less to a weight’s value. For a weight of 0.12345:

  • 1st digit (1): 0.1
  • 2nd digit (2): 0.02
  • 3rd digit (3): 0.003
  • 4th digit (4): 0.0004

Truncating at 3 digits limits the error per item to <0.001. In large populations, these errors average out, minimally affecting the selection distribution.

Guidance

Precision Typical Use Case Trade-offs
1-2 Maximum performance, minimal memory usage. Best for coarse weights (e.g., 0.1, 0.5). Loses accuracy with fine-grained data.
3-4 Recommended Default. Optimal for most scenarios. Captures sufficient detail for simulation or model data. Negligible performance/memory cost.
5+ High-fidelity scenarios with very close weights. Distinguishes weights like 0.12345 vs. 0.12346. Increases memory (up to 10x per level) and slightly impacts performance.

Usage & Installation

DigitBinIndex is available as a Python library on PyPI or as a Rust crate on Crates.io. Ensure Python 3.6+ for Python bindings or Rust 1.75+ for the Rust crate.

For Python 🐍

Install from PyPI:

pip install digit-bin-index

Example usage:

from digit_bin_index import DigitBinIndex

def main():
    # Create an index with precision 3 (default).
    index = DigitBinIndex()

    # With custom precision
    index_5 = DigitBinIndex.with_precision(5)

    # With custom precision and capacity
    index_3_XL = DigitBinIndex.with_precision_and_capacity(3, 10_000_000)

    # Add items with IDs and weights.
    index.add(id=101, weight=0.123)  # Low weight
    index.add(id=202, weight=0.800)  # High weight
    index.add(id=303, weight=0.755)  # High weight
    index.add(id=404, weight=0.110)  # Low weight

    # Sequential (Wallenius') Draw: Select and remove one item.
    # Higher-weighted items (202, 303) are more likely.
    selected_item = index.select_and_remove()
    if selected_item:
        print(f"Wallenius draw: ID {selected_item[0]}, Weight ~{selected_item[1]}")
    
    print(f"Items remaining: {index.count()}")  # 3

    # Simultaneous (Fisher's) Draw: Select and remove 2 unique items.
    selected_ids = index.select_many_and_remove(2)
    if selected_ids:
        print(f"Fisher's draw: {selected_ids}")
    
    print(f"Items remaining: {index.count()}")  # 1

if __name__ == "__main__":
    main()

For Rust 🦀

Add to your Cargo.toml:

[dependencies]
digit-bin-index = "0.2.5"    # Replace with the latest version from crates.io
rust_decimal = "1.37"
rust_decimal_macros = "1.37"  # Optional, for convenient decimal literals

Example usage:

use digit_bin_index::DigitBinIndex;
use rust_decimal_macros::dec;

fn main() {
    // Create an index with precision 3.
    let mut index = DigitBinIndex::with_precision(3);

    // Add items with IDs and weights.
    index.add(101, dec!(0.123)); // Low weight
    index.add(202, dec!(0.800)); // High weight
    index.add(303, dec!(0.755)); // High weight
    index.add(404, dec!(0.110)); // Low weight

    // Sequential (Wallenius') Draw: Select and remove one item.
    if let Some((id, weight)) = index.select_and_remove() {
        println!("Wallenius draw: ID {}, Weight ~{}", id, weight);
    }
    println!("Items remaining: {}", index.count()); // 3

    // Simultaneous (Fisher's) Draw: Select and remove 2 unique items.
    if let Some(ids) = index.select_many_and_remove(2) {
        println!("Fisher's draw: {:?}", ids);
    }
    println!("Items remaining: {}", index.count()); // 1
}

License

This project is licensed under the MIT License, a permissive open-source license allowing free use, modification, and distribution.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

digit_bin_index-0.2.5.tar.gz (31.8 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

digit_bin_index-0.2.5-cp38-abi3-win_amd64.whl (158.2 kB view details)

Uploaded CPython 3.8+Windows x86-64

digit_bin_index-0.2.5-cp38-abi3-win32.whl (155.4 kB view details)

Uploaded CPython 3.8+Windows x86

digit_bin_index-0.2.5-cp38-abi3-musllinux_1_2_x86_64.whl (456.2 kB view details)

Uploaded CPython 3.8+musllinux: musl 1.2+ x86-64

digit_bin_index-0.2.5-cp38-abi3-musllinux_1_2_i686.whl (482.7 kB view details)

Uploaded CPython 3.8+musllinux: musl 1.2+ i686

digit_bin_index-0.2.5-cp38-abi3-musllinux_1_2_armv7l.whl (557.6 kB view details)

Uploaded CPython 3.8+musllinux: musl 1.2+ ARMv7l

digit_bin_index-0.2.5-cp38-abi3-musllinux_1_2_aarch64.whl (461.0 kB view details)

Uploaded CPython 3.8+musllinux: musl 1.2+ ARM64

digit_bin_index-0.2.5-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (285.5 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ x86-64

digit_bin_index-0.2.5-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl (330.0 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ s390x

digit_bin_index-0.2.5-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl (319.6 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ppc64le

digit_bin_index-0.2.5-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl (293.7 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARMv7l

digit_bin_index-0.2.5-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (280.4 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARM64

digit_bin_index-0.2.5-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl (304.9 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.5+ i686

digit_bin_index-0.2.5-cp38-abi3-macosx_11_0_arm64.whl (254.6 kB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

digit_bin_index-0.2.5-cp38-abi3-macosx_10_12_x86_64.whl (266.0 kB view details)

Uploaded CPython 3.8+macOS 10.12+ x86-64

File details

Details for the file digit_bin_index-0.2.5.tar.gz.

File metadata

  • Download URL: digit_bin_index-0.2.5.tar.gz
  • Upload date:
  • Size: 31.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.9.4

File hashes

Hashes for digit_bin_index-0.2.5.tar.gz
Algorithm Hash digest
SHA256 b3e728c0d1ec76a20d0f0ce3c780599d355e87ea940ff0d1e6afaadfe0539855
MD5 eee3b92885b4adbb71b1c6a94630c2dc
BLAKE2b-256 e8d3b76bb562c360abb3872ee854a1de80cc180204c18497e2b9f1cb5015efb8

See more details on using hashes here.

File details

Details for the file digit_bin_index-0.2.5-cp38-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for digit_bin_index-0.2.5-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 65370e1c4955ec94cdeba69a9635babbfea92f2325a53a23467bb7566b103258
MD5 48633eb2121574dd99b6f18ac9fe37ca
BLAKE2b-256 adff661ffd2b9aca9448e3c6004f89d79313d6d1885a554137e898e9f2699588

See more details on using hashes here.

File details

Details for the file digit_bin_index-0.2.5-cp38-abi3-win32.whl.

File metadata

File hashes

Hashes for digit_bin_index-0.2.5-cp38-abi3-win32.whl
Algorithm Hash digest
SHA256 c09fab9e03c84df16af7cfe580ea84c8174e6d55148091a349c2f949f5e9981c
MD5 97f2238bd7f533d1e897636a73020fba
BLAKE2b-256 bd59b738e112e2d0b814a0fde49d5a014100dac7b0760f0a0556a14bcfa8fc34

See more details on using hashes here.

File details

Details for the file digit_bin_index-0.2.5-cp38-abi3-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for digit_bin_index-0.2.5-cp38-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 ec0b523b1df8b1468a5077075aaca9fe45c8a555ca0c22870b4254c09280b1ca
MD5 c95612339e5f700a35702da676d41dc6
BLAKE2b-256 4ccb60fd9ee6fd2469e12e56b7691b40efbdc1196a2c2e2b8cefc88e1f77fc81

See more details on using hashes here.

File details

Details for the file digit_bin_index-0.2.5-cp38-abi3-musllinux_1_2_i686.whl.

File metadata

File hashes

Hashes for digit_bin_index-0.2.5-cp38-abi3-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 8740dff0c02e7196ae356fcaf2bd7aaf8de961489382dc5ab7a59c486ac79997
MD5 a6487099d982047a4aa1282af619a1bc
BLAKE2b-256 58c7e2013bf4b9c36a5f4114e71d2722dc77d2f713b2a932ab400f988fb5b81b

See more details on using hashes here.

File details

Details for the file digit_bin_index-0.2.5-cp38-abi3-musllinux_1_2_armv7l.whl.

File metadata

File hashes

Hashes for digit_bin_index-0.2.5-cp38-abi3-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 f4469b1749f76dc0a3f45bec441b4ce2f97f4f9e30cfb11f5693aa0a2d2903fd
MD5 19458abb4594d2184b693d877528b27c
BLAKE2b-256 260e377513fc71391b36d7589e977900e96439d1c370d03d6bfd5517907679a0

See more details on using hashes here.

File details

Details for the file digit_bin_index-0.2.5-cp38-abi3-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for digit_bin_index-0.2.5-cp38-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 b85047b146a30517deed087242fa7b64c0bbf2975b2881a61cc0f73c992be71d
MD5 ac0b5c638a6d7b67f9e64c8a26e1879e
BLAKE2b-256 827eb12448b13cfdc5c697d57de7167e4c3522b82a1b6639995d2c54841a03a7

See more details on using hashes here.

File details

Details for the file digit_bin_index-0.2.5-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for digit_bin_index-0.2.5-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 0a98d1eb8616cc08bc9a4b480673bc55c3c9dc9b1d2ca75144d2502b1e3e86b9
MD5 7a1cf6ea706238f700575be51993cc1b
BLAKE2b-256 a06486643efae168a056dc64b83cbb1b51a685e851372eb56c8ccec594d24a88

See more details on using hashes here.

File details

Details for the file digit_bin_index-0.2.5-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl.

File metadata

File hashes

Hashes for digit_bin_index-0.2.5-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl
Algorithm Hash digest
SHA256 73c77e976231d75af75611ed3ca0b2f56511204bbe9fff330d28315dc856b2eb
MD5 3e2d19d772408727f0af764b71d97fc4
BLAKE2b-256 28de1b8cff39e4fbfe3b825a1409e8dfe4550205cf4ebeb1b3247d1fab6c431a

See more details on using hashes here.

File details

Details for the file digit_bin_index-0.2.5-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl.

File metadata

File hashes

Hashes for digit_bin_index-0.2.5-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl
Algorithm Hash digest
SHA256 9c002afdfe81e43f4dd37f22913afc83398093dc1a5d3129fdec6f7e14a9eb72
MD5 1b32e7eb520c44278a584fea396a34e2
BLAKE2b-256 0c2edd0ea4df259e44d25eb3d9f77d6a4d44f6b2036d0ae42ecda296dcd54801

See more details on using hashes here.

File details

Details for the file digit_bin_index-0.2.5-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl.

File metadata

File hashes

Hashes for digit_bin_index-0.2.5-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
Algorithm Hash digest
SHA256 1f5ceb45f722fdc009b6b3a1dbc134b679b983cb3de7d93925bd9cc2cd09255e
MD5 5ea39555951f79a260e661a2e17f0a31
BLAKE2b-256 fdb1a46d09e4921b2ef5cdfb6f718517ba7359118a6d91899ad0c86bbacfcde9

See more details on using hashes here.

File details

Details for the file digit_bin_index-0.2.5-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for digit_bin_index-0.2.5-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 52862222aa85d6de2b5871dc8e1bf8a8213841844f207ca10caa612a88a373f6
MD5 f7abca3df98decb3c1f1cc2046ac017d
BLAKE2b-256 c492f668969b81c66e9b7cd6394ca090a75b398c6deb12957e96a531f4a0d4b3

See more details on using hashes here.

File details

Details for the file digit_bin_index-0.2.5-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl.

File metadata

File hashes

Hashes for digit_bin_index-0.2.5-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl
Algorithm Hash digest
SHA256 0b01aff22ce4f8ba185a01adec918d005458b4fc70cf1a108611be65c853923f
MD5 85cd8216f6503213e5fe8034c6fac993
BLAKE2b-256 7e473977a15a26c99fc1db1e977d2c1a5a09d26a2d0a75579961537537a009d3

See more details on using hashes here.

File details

Details for the file digit_bin_index-0.2.5-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for digit_bin_index-0.2.5-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 8f4e2405daa84a1c82e6e1e29b59ed024702610e18aeadaebf8ca01acfac49fd
MD5 ed617f7e9b1bbef0dff6be743b895f44
BLAKE2b-256 f846eb4884bd751fb25f50d08be56f1e649b13e71e58a4cb8364e4b0cd751226

See more details on using hashes here.

File details

Details for the file digit_bin_index-0.2.5-cp38-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for digit_bin_index-0.2.5-cp38-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 96dc994947dc2bdb70d687acd53dde22e1df9c3732ab8e5f7dc25f115bd98885
MD5 73a80263de370330e0732a5fbff6403d
BLAKE2b-256 7e56c8f885234446656e6fb2bb73a22ececa2b82737999d23394874e42ad96cb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page