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. Roaring Bitmap Bins: Leaf nodes act as bins, storing item IDs in a Roaring Bitmap, a compressed data structure optimized for fast set operations. This enables efficient unique sampling, particularly for Fisher's distribution.

  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(precision=3)

    # 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.4"    # 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.4.tar.gz (28.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.4-cp38-abi3-win_amd64.whl (152.1 kB view details)

Uploaded CPython 3.8+Windows x86-64

digit_bin_index-0.2.4-cp38-abi3-win32.whl (151.0 kB view details)

Uploaded CPython 3.8+Windows x86

digit_bin_index-0.2.4-cp38-abi3-musllinux_1_2_x86_64.whl (448.9 kB view details)

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

digit_bin_index-0.2.4-cp38-abi3-musllinux_1_2_i686.whl (476.7 kB view details)

Uploaded CPython 3.8+musllinux: musl 1.2+ i686

digit_bin_index-0.2.4-cp38-abi3-musllinux_1_2_armv7l.whl (551.9 kB view details)

Uploaded CPython 3.8+musllinux: musl 1.2+ ARMv7l

digit_bin_index-0.2.4-cp38-abi3-musllinux_1_2_aarch64.whl (454.2 kB view details)

Uploaded CPython 3.8+musllinux: musl 1.2+ ARM64

digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (278.1 kB view details)

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

digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl (323.7 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ s390x

digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl (311.8 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ppc64le

digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl (288.2 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARMv7l

digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (274.3 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARM64

digit_bin_index-0.2.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl (298.7 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.5+ i686

digit_bin_index-0.2.4-cp38-abi3-macosx_11_0_arm64.whl (247.5 kB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

digit_bin_index-0.2.4-cp38-abi3-macosx_10_12_x86_64.whl (258.1 kB view details)

Uploaded CPython 3.8+macOS 10.12+ x86-64

File details

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

File metadata

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

File hashes

Hashes for digit_bin_index-0.2.4.tar.gz
Algorithm Hash digest
SHA256 2d649212ea5abc1a3a1d8b47a014103ca9ae9ca94e87d298fb971fce101d61e7
MD5 7e2158ba855f6ad52254ebfbaa6bc81f
BLAKE2b-256 7499480feb89770fbec201668680b018a59ff632c07e82e01a7cf3e07fe113d1

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.4-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 62930e011ef64c6f23e92fcab0f2e5c101327414b686eec071088fe7a8770150
MD5 435f2c7afb1a35140f88fac216f7e4e9
BLAKE2b-256 ad1e74469229fb6ccfd99511247a40e7fd0be308aa765a5727f3448acababb5d

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.4-cp38-abi3-win32.whl
Algorithm Hash digest
SHA256 2ed8f1f8e2e7103072a778cf141970e04d6f2c010ff90fd06843ce24f0ed8f63
MD5 6fcf5142978d6c7da78da519d06bded6
BLAKE2b-256 128331ff6789f96d1a9bac1e27389d5a551b7e8fecaac5fa416e431fb26393bc

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.4-cp38-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 402ef34f6e04ce3894783659356414c5fda9f7594bdad6b27a226a2dae718816
MD5 081526caaa3b387f2c8bfb369746ccd7
BLAKE2b-256 2fca5acadd231b95048f8c503fc42360fa8e0625a498406757df9dac1f7dbe6c

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.4-cp38-abi3-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 9775acf87d6569e06b0a3c73d029778254721c7e2353b2221c41b9abacbb0655
MD5 5348e7b54e6172e5ea4c9a5f94f0cf91
BLAKE2b-256 8549ec819b3a9f124a7b53aa2ef2425957300de046e9deed3f31368e0f65791c

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.4-cp38-abi3-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 b3e63d249098a575109b59be5b1435b7ceece977d58e3f2743a130a0b0397482
MD5 a14f1a786bb2be28be50aae275764b56
BLAKE2b-256 ad815b7a8f0d2405606407e1e378f310464e336f58ab0d3e7886d3f1bac2b881

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.4-cp38-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 b21f2912ca0b3d051a6a20c7eb6e0da275437e0c4a4e91e1cda0077a63a8fd0e
MD5 5a1078dd26572bf558cb64457e20fd01
BLAKE2b-256 b24cbc69327032a5da294a6763e2343ea293169981332f2a6351ce5a59e5c556

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 21dafb7a8e2b89e57ca7b9ba385370e9ee35e8843ed3604763bef24bf045ce76
MD5 ec1833f6b38700bab0fe5e4dce78e4b3
BLAKE2b-256 d3b37b28220784e8bdc79675a988cc8bed046ea00f3981274b0367bd1a8f6d7c

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl
Algorithm Hash digest
SHA256 ac3f2a24790d3c7812698598185bee27493824c9b4533d42d45cab3bb2b04841
MD5 6d6ca4665bc9360a783597eb7f17592e
BLAKE2b-256 14dbc3def4aec9f8f5b0f58b54c1ac356535c09a236b006f23695f7f60fc1c40

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl
Algorithm Hash digest
SHA256 71d3f77a3df8b48aa39feef1fa6baf9711edbdf9865b9f129ce66a7d1524f7a0
MD5 e7bbca526a331ee31e6d1dbb6057045a
BLAKE2b-256 32abb7f00c0810265a0511cc5afde9a2d5ff16675c40659e173b051a888c1899

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
Algorithm Hash digest
SHA256 8e4b261dda886ac29dc427d7ca115e0915459b8948f9a8c277c70cf222ec8d56
MD5 4ed77bd7bbe0febdeb4f8d01e353cf54
BLAKE2b-256 fb7062c4cbef6e1736e13281af00277ab53c725692a08a9c621f0af962abde34

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 359a71992140bea5ea4dc9bb6927ccac32e53e9a47be95da9f93a30fe49d4652
MD5 4266577f43de6141ef0c48984f64ce4b
BLAKE2b-256 13ec8ccd52e9e7076c979bf4da27e588d1e7f3e8601c9f8264ce38d6ac4179af

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl
Algorithm Hash digest
SHA256 bd0d998f754e97af79d55b97eddec9296d9f59bd1484152d7a03b6951ec2ff1f
MD5 ba7ce46b04019b0f2cb735cde8888565
BLAKE2b-256 eb289d903ada813c7ff925d19843a9027303ea2cc2e24f5179dad5152708e760

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.4-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 dd09858e492bcbd4735e811cfb85f4e24772eb894c81d14a5c90ff3f2350aa08
MD5 b0ea0c3e93cbd6b7645a5fbc4657c225
BLAKE2b-256 16ebbbc616b8db242793f3f1594a5fea127d8165620315f239d069cbd6ae60f9

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.4-cp38-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 01da4aa2e320c91a5196983486d59aa98c44db50d8b0285cca8999b45185d424
MD5 7e1986e26cc70c5f5abf595e6a1dd57e
BLAKE2b-256 b4f5c522a9203d57d753cf6efb137a05c519d2facf4e4c699a7d252d94d6844f

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