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.3"    # 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.3.tar.gz (28.0 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.3-cp38-abi3-win_amd64.whl (151.3 kB view details)

Uploaded CPython 3.8+Windows x86-64

digit_bin_index-0.2.3-cp38-abi3-win32.whl (150.8 kB view details)

Uploaded CPython 3.8+Windows x86

digit_bin_index-0.2.3-cp38-abi3-musllinux_1_2_x86_64.whl (448.0 kB view details)

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

digit_bin_index-0.2.3-cp38-abi3-musllinux_1_2_i686.whl (476.6 kB view details)

Uploaded CPython 3.8+musllinux: musl 1.2+ i686

digit_bin_index-0.2.3-cp38-abi3-musllinux_1_2_armv7l.whl (551.8 kB view details)

Uploaded CPython 3.8+musllinux: musl 1.2+ ARMv7l

digit_bin_index-0.2.3-cp38-abi3-musllinux_1_2_aarch64.whl (453.6 kB view details)

Uploaded CPython 3.8+musllinux: musl 1.2+ ARM64

digit_bin_index-0.2.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (277.1 kB view details)

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

digit_bin_index-0.2.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl (322.2 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ s390x

digit_bin_index-0.2.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl (311.6 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ppc64le

digit_bin_index-0.2.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl (288.0 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARMv7l

digit_bin_index-0.2.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (273.6 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARM64

digit_bin_index-0.2.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl (298.8 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.5+ i686

digit_bin_index-0.2.3-cp38-abi3-macosx_11_0_arm64.whl (246.8 kB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

digit_bin_index-0.2.3-cp38-abi3-macosx_10_12_x86_64.whl (257.3 kB view details)

Uploaded CPython 3.8+macOS 10.12+ x86-64

File details

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

File metadata

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

File hashes

Hashes for digit_bin_index-0.2.3.tar.gz
Algorithm Hash digest
SHA256 f6e98ce693ecb1aae99786abc8846272933bcfe782a5389ecd27f1e2c3dd9222
MD5 16fa6dd5ccadf192b87664706d52f4d6
BLAKE2b-256 0dd1d76ec29db6766f0d2ecd059cc56a4f366ea46e747bde0d19a9a73b71b323

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.3-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 aac2a84c84bf79fb96948d5d7015a8942c8290db455a6fd3eee14bc6d3331a42
MD5 3c52194a22813cfb230875528ad4ceb5
BLAKE2b-256 d1f404a78b4a42a941b876b7e8f9da745453805947b7cb83c66f84a010699892

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.3-cp38-abi3-win32.whl
Algorithm Hash digest
SHA256 21f99bcbeac2f25ff66b7cbcfb0fd3491959b8cd5c77a26dc20c73bb4b6c6875
MD5 a47ddb00526c27b303f4d10c476ce50a
BLAKE2b-256 b37ae6f45f4a3d07184f1ab0b6deb3809a3612f07e44a66b5ae976a06a199608

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.3-cp38-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 d0f42291a6ded078c2fb2453c9d2dce7b154efbb6259c0659dd98d00546bef8b
MD5 ca8745d5123a696bc55547512f267eb1
BLAKE2b-256 60fc093646321027c1f3dd69e6661960121096872724879a6607afbf0bdff26b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.3-cp38-abi3-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 fd6fc8cfa8d2a397d6726cbd0cd8fc78567b166701abf837536234a366a9205e
MD5 93af84f9086a2bf2e71cbb9fa41de2ae
BLAKE2b-256 37f95cb4020a95a7116806c50c8f263ec23bf0a477f8dab63eea1981c34872d6

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.3-cp38-abi3-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 0b2002829c76f6da81dadbea4402ec7d8279dfaf12f3e4ba3f742fe76dee4bc5
MD5 9b7313a1a19f83faa71664537818742b
BLAKE2b-256 3cdf03c4ba105042013279ea1be369a35309735ba81e00e535a2e321b2bcc8d7

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.3-cp38-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 a042d7eea457acd97576098e1d130ec01602484121447b8889c043facd80dbbc
MD5 f82e1234c98f601edace496c2e85ab1b
BLAKE2b-256 c975b18d21d609fc7d23f4ac03f046d3894bd5e7435386687b9ca1ff8d58e865

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 d5225fc3adc6ec1b0a9b17ca4d45c5059fe1bad63849ed2422d245b9f668d94c
MD5 e989df219f5364d434eb5ee2e9639138
BLAKE2b-256 717ab77fd4be8ae360f44b87cdbdcc39c82dcc47b2d029844a366324548ec62c

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl
Algorithm Hash digest
SHA256 519cb6f825cab364b17ffeaa30439452aaf73c6317c13704911dbe52fd0fd1b7
MD5 d511c8f9ce3a66284f8d64d29a77126c
BLAKE2b-256 277b68900ad0159dae08b873ac16c79ddefebb5ad64587e0901b223357666128

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl
Algorithm Hash digest
SHA256 5de2766124d358052a817bf423bbb8644ec4660722046164a66be57bedefbe46
MD5 f7d5e15b8828e9e571e62af410fb20a1
BLAKE2b-256 e657f0384985f86ff1ff254739730b130fea97129fd5f6c6da500b31b6969334

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
Algorithm Hash digest
SHA256 de8ebea799e9837d6ee61771d5876662ab1c29b37a25010e112f9363f687027f
MD5 f818eb8aac5b5e0f5b11239384896a11
BLAKE2b-256 8ef09337f7a01e18a51833af1062fef9d2286f5d23d41c1f10c545680eda5e07

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 476ec2f10f42c99d6f0626a714c44ef8560ef922fbb416db30457445942c5558
MD5 2a2aa550d5d6645ea95fd38b75ff506e
BLAKE2b-256 698bee5c1bb3e8e13e274703176156d2887a9494f8dae685c77c8c371c86c81e

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl
Algorithm Hash digest
SHA256 d88e4a555eb15094f29869df2a6e1b3c2b5fae099333dd93a08e1fc1f43645a0
MD5 75accc1212ee7f545c91021d7948c849
BLAKE2b-256 5be98f5a30a8185668d182e2186adb8fc5c68171dd36cad8ee27a08daf93a371

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.3-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 83ec117f0869841f0ba7aa27639210123ec0f7821dd7934c73e2e769e2d705ee
MD5 87913ba703c41eda9dd667572a094841
BLAKE2b-256 125916baed45054d14056e36b112ef2e5745b28c5216468028868eb2bfdeb75b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for digit_bin_index-0.2.3-cp38-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 2c40cc407a11538a567d4c8c0a13fdbab69292002cc2bd474e280edab3769476
MD5 e269522918f77ac1226fc7f181f9efd5
BLAKE2b-256 bdd7e9bce5df9c565f10a1408fc74d8c0fba57e22f50f70fb6e678d4a4e7d164

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