https://github.com/daskol/fast-bernoulli
Fast generation of long sequencies of bernoulli-distributed random variables
Science Score: 13.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
○CITATION.cff file
-
✓codemeta.json file
Found codemeta.json file -
○.zenodo.json file
-
○DOI references
-
○Academic publication links
-
○Committers with academic emails
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (12.5%) to scientific vocabulary
Keywords
Repository
Fast generation of long sequencies of bernoulli-distributed random variables
Basic Info
Statistics
- Stars: 5
- Watchers: 1
- Forks: 2
- Open Issues: 0
- Releases: 1
Topics
Metadata Files
README.md
Fast Bernoulli
fast generation of long sequences of Bernoulli-distributed random variables
Overview
Fast Bernoulli — a library for sampling from Bernoulli distribution with a specific parameters. Performant Bernoulli samplers are highly useful in scientific and engineering fields: modeling, random graphs, coding theory, etc.
The standard way to obtain Bernoulli distributed random value is sampling from uniform distribution and thresholding with a parameter. The library exploits a different property of Bernoulli random variables to attain better efficiency in the sense of (P)RNG invocations.
Namely, logical operation (like not, or, and, xor) on two Bernoulli random
variables gives another random variable distributed according to Bernoulli law
with a new parameter. So, the concept behind is that Bernoulli distribution
with any parameter can be approximated with a sequence of other Bernoulli
random variables with different parameters and logical operators applied to the
sequence. Set of logical operators is not constrained in general but this
implementation is based on and(&) and not(~) operators. As soon as a target
distribution parameter is "quantized" in a sequence logical operators, one can
apply the transformations to a sequence of random values in run-time to draw a
sample.
In order to archive better performance on practice, one need to improve sample efficiency in sense of memory consumption. Standard implementation returns a value of random variable as a bool-typed value which in fact consumes one byte. In contrast, library returns block of random values. Each random value encoded as a bit in a block. This fact allows to use vector extensions to instruction set.
Another important assumption is that (P)RNG generates random number and bits in generated numbers are distributed uniformly. In general, this is true especially for top-quality (P)RNG (mt19937 as example). So, the assumption allows to apply the described algorithm to bits of numbers obtained with (P)RNG.
Another one improvement is related to implementation. Execution of a sequence of logical operations on bits requires iterations in loop. Many iterations affect performance through context switch and (un)conditional branching. The best performance could be archived if distribution parameter or a set of logical operators is known in compile-time. Just-in-time (JIT) compilation facilitates overcoming the obstacle. The library supports LLVM which provides JIT compiler infrastructure.
Features
- [x] High efficiency per random bit.
- [x] Vectorization with AVX.
- [x] JIT compilation with LLVM.
- [x] Thread-safe.
- [x] Python bindings.
Benchmarking
The only reason for creation of the project is efficient sampling. So, in this
section we present some benchmarks of sampler in different configurations. One
can see that the proposed implementation is ahead of standard Bernoulli sampler
(prefix Std in table or figure).
``` Run on (4 X 3400 MHz CPU s) CPU Caches: L1 Data 32K (x2) L1 Instruction 32K (x2) L2 Unified 256K (x2)
L3 Unified 4096K (x1)
Benchmark Time CPU Iterations Throughput
BMRandomNumberGeneration/128 4786 ns 4783 ns 147228 816.740 M/s BMStdSampler/128 72260140 ns 72208458 ns 10 55.3952 k/s BMGenericSampler/128 104076 ns 103545 ns 6745 37.7252 M/s BMAvxSampler/128 71017 ns 70982 ns 9759 55.0314 M/s BMJitGenericSampler/128 103482 ns 103431 ns 6735 37.7669 M/s BMJitAvxSampler/128 55667 ns 55640 ns 12393 70.2053 M/s ```
Since one of the aims is to generate bulks of random values, it is interesting to look at sampling efficiency in sense of bitrate with regards length of generated sequence.

Assembly
Build dependencies are (a) compiler with support C++17, (b) CMake as
build-system generator, (c) Make as default build system. Also building
requires (d) Google GTest for testing, and (e) Google Benchmark for
benchmarking. Optional dependency is (f) LLVM which provides JIT compiler
facility.
bash
mkdir -p build/release && cd build/release
cmake ../.. -DCMAKE_BUILD_TYPE=Release -DBUILD_WHEEL=ON -DUSE_LLVM=ON
make
LLVM is not required by default, so it could be turned on/off with option
-DUSE_LLVM=ON/OFF (as it is shown on the snippet above). Option BUILD_WHEEL
triggers building of Python bindings which could be find in directory
fast-bernoulli/python/. Target for building Python bindings is not included
to a set of default targets, so one should to run building of target
fast-bernoulli-python-wheel in advance.
bash
make fast-bernoulli-python-wheel
Usage
The simplest way to construct sampler in C++ is just calling CreateSampler()
routine with target probability. The function returns a functor which should be
invoked with an instance of (P)RNG and aligned memory buffer. Finally, one
could bring samples to common representation (vector of bools).
```cpp
include
using namespace NFastBernoulli;
// Assume 65536 bits with probability 0.6 should be generated. auto probability = 0.6; auto nobits = 65536;
// Create an instance of ISampler and make buffer for sampling. auto sampler = CreateSampler(probability); auto ptr = sampler.MakeBuffer(nobits);
// Use Mersenne Twister as a pseudo-random number generator. std::mt19937_64 rng;
// Draw samples from Bernoulli distribution. sampler(rng, ptr);
// Expand compressed representation of random values to vector of bools. auto values = Expand(ptr, nobits); ```
The function CreateSampler() is overridden in order to provide an advanced way
to instantiate sampler with structure TSamplerOpts.
Also, the library delivers Python bindings and an extremely simple usage
interface. Module fast_bernoulli provides function sample() which generates
random bits and returns memory view on resulting buffer of type ui8.
```python import numpy as np import fast_bernoulli as fb
Assume 65536 bits with probability 0.6 should be generated.
probability = 0.6; nobits = 65536;
Draw samples from Bernoulli distribution. JIT is used.
res = fb.sample(probability, nobits, seed=42, jit=True) # memoryview arr = np.array(res) # np.ndarray with dtype=np.uint8 ```
GitHub Events
Total
Last Year
Committers
Last synced: 8 months ago
Top Committers
| Name | Commits | |
|---|---|---|
| Daniel Bershatsky | d****y@s****u | 45 |
| Andrew Grigorev | a****w@e****u | 1 |
Committer Domains (Top 20 + Academic)
Issues and Pull Requests
Last synced: 8 months ago
All Time
- Total issues: 1
- Total pull requests: 1
- Average time to close issues: 13 days
- Average time to close pull requests: 5 minutes
- Total issue authors: 1
- Total pull request authors: 1
- Average comments per issue: 2.0
- Average comments per pull request: 0.0
- Merged pull requests: 1
- Bot issues: 0
- Bot pull requests: 0
Past Year
- Issues: 0
- Pull requests: 0
- Average time to close issues: N/A
- Average time to close pull requests: N/A
- Issue authors: 0
- Pull request authors: 0
- Average comments per issue: 0
- Average comments per pull request: 0
- Merged pull requests: 0
- Bot issues: 0
- Bot pull requests: 0
Top Authors
Issue Authors
- adomasbaliuka (1)
Pull Request Authors
- ei-grad (1)