The qiprng
package implements a pseudo-random number generator based on quadratic
irrational numbers with hardware acceleration and parallel generation capabilities.
It provides cryptographic security features and extensive statistical distribution support.
library(qiprng)
createPRNG()
x <- generatePRNG(10000)
hist(x, breaks = 50, main = "qiprng uniform distribution", col = "skyblue")
This release fixes critical issues with deterministic mode to ensure perfect reproducibility:
- Fixed Deterministic Seeding: Resolved thread-local RNG initialization to properly reset between PRNG creations
- Thread Counter Reset: Added proper reset of MultiQIOptimized thread counter for consistent thread IDs
- Fallback RNG Determinism: Replaced all non-deterministic fallback RNGs with deterministic alternatives
- Complete Test Coverage: All 23 deterministic mode tests now pass with identical sequences for same seeds
This release includes significant architectural improvements focused on eliminating concurrency bottlenecks:
- Lock-Free Architecture: Implemented thread-local PRNG instances with zero mutex contention
- Cache Optimization: Increased cache size to 4096 samples for better L1 cache utilization
- Golden Ratio Seeding: Thread-specific seeds using golden ratio prime (0x9E3779B97F4A7C15)
- Cache-Line Alignment: 64-byte aligned data structures to prevent false sharing
- Optimized Mixing Strategies: Enhanced CASCADE_MIX performance through mantissa extraction
For complete version history, see CHANGELOG.md.
The package offers high-precision computation using the MPFR library with configurable precision from 24 to 10,000 bits, supporting over 14 statistical distributions including standard continuous and discrete distributions as well as extended distributions like Levy stable, Pareto, and multivariate normal. Hardware acceleration is provided through SIMD vectorization with AVX2/NEON support and enhanced OpenMP parallelization with thread-local caching, SIMD integration, and optimized buffer management for improved performance on modern multi-core systems.
Advanced jump-ahead capabilities enable efficient parallel simulations with multiple algorithm options including MPFR-based high-precision matrix operations, fast modular arithmetic using Mersenne primes, and direct continued fraction manipulation, all with automatic overflow prevention for jumps of arbitrary size. The cryptographic security layer uses ChaCha20 stream cipher for deterministic cryptographic mixing, with XOR mixing for preserving uniformity or modular addition for enhanced entropy, providing cryptographic-grade unpredictability. Thread safety is guaranteed through proper synchronization primitives and thread-local resources, while deterministic mode with seed support enables reproducible sequences for research and testing. The generator has been validated using the NIST SP 800-22 framework using official implementation.
# Install from GitHub
remotes::install_github("biostochastics/qiprng")
# Generate random numbers
library(qiprng)
createPRNG()
random_values <- generatePRNG(1000)
For detailed installation instructions by platform, see the Installation section below.
This implementation is based on Vincent Granville's work on random number generators using quadratic irrationals. The mathematical foundation and the core algorithm design both follow Granville's approach.
Reference: Granville, V. (2022). Military Grade Fast Random Number Generator Based on Quadratic Irrationals
The generator achieves excellent performance in single-threaded mode with near-linear scaling in multi-threaded configurations. Memory usage remains constant at O(1) with thread-local caching. All 750 possible discriminants have been validated through comprehensive testing. The package defaults to using only the 370 discriminants rated as "Excellent" (49.3% of total), ensuring optimal statistical properties. For research purposes, all discriminants can be enabled through configuration options.
Independent validation using the NIST Statistical Test Suite confirms that qiprng produces cryptographically strong pseudorandom sequences with a 98.4% pass rate across all NIST SP 800-22 tests, matching the performance of established cryptographic RNGs.
This package contains C++ code and requires a compiler and several system libraries to be installed on your system before the R package can be built.
You will need to install the Command Line Tools for Xcode and the required libraries using Homebrew.
-
Install Xcode Command Line Tools:
xcode-select --install
-
Install Homebrew (if you don't have it):
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
-
Install required libraries:
brew install pkg-config gmp mpfr libsodium openssl
Use apt-get
to install the required development libraries:
sudo apt-get update
sudo apt-get install build-essential libmpfr-dev libgmp-dev libsodium-dev libssl-dev
Use yum
or dnf
to install the required development libraries:
sudo dnf groupinstall "Development Tools"
sudo dnf install gmp-devel mpfr-devel libsodium-devel openssl-devel
For Windows, the required libraries are downloaded automatically during installation, but you need to have Rtools installed and configured correctly.
Once the prerequisites are installed, you can install qiprng
from GitHub using the
remotes
package in R:
if (!require("remotes")) {
install.packages("remotes")
}
remotes::install_github("biostochastics/qiprng")
Continuous Distributions:
- Uniform
- Normal
- Exponential
- Gamma
- Beta
- Log-Normal
- Weibull
- Chi-Squared
- Student's t
- Levy Stable (Alpha-stable random variates using Chambers-Mallows-Stuck algorithm)
- Pareto (Heavy-tailed distribution for extreme events modeling)
- Cauchy (Fat-tailed distribution with undefined mean)
- Multivariate Normal (Correlated normal vectors, requires Eigen3)
Discrete Distributions:
- Bernoulli
- Binomial
- Poisson
- Negative Binomial
Special Distributions:
- Gaussian Copula (Complex dependencies between different marginal distributions)
# Standard distributions
createPRNG(list(distribution = "normal", normal_mean = 0, normal_sd = 1))
normal_samples <- generatePRNG(1000)
createPRNG(list(distribution = "exponential", exponential_lambda = 0.5))
exp_samples <- generatePRNG(1000)
# Extended distributions
levy_samples <- generate_levy_stable(n = 1000, alpha = 1.5, beta = 0.5, mu = 0, sigma = 1)
pareto_samples <- generate_pareto(n = 1000, xm = 1, alpha = 2.5)
cauchy_samples <- generate_cauchy(n = 1000, location = 0, scale = 1)
# Multivariate normal
mean_vec <- c(0, 0, 0)
cov_mat <- matrix(c(1, 0.5, 0.2, 0.5, 1, 0.3, 0.2, 0.3, 1), 3, 3)
mvn_samples <- generate_multivariate_normal(n = 1000, mean_vec, cov_mat)
# Gaussian copula
correlation <- matrix(c(1, 0.7, 0.7, 1), 2, 2)
marginals <- list(
list(type = "cauchy", location = 0, scale = 1),
list(type = "pareto", xm = 1, alpha = 3)
)
copula_samples <- generate_with_copula(n = 1000, correlation, marginals)
The optimized architecture eliminates mutex contention through thread-local storage:
// Each thread maintains its own PRNG state
static thread_local ThreadLocalData {
std::vector<QuadraticIrrational> qis; // Thread-local QI instances
std::vector<double> cache; // L1-optimized cache (4096 samples)
size_t cache_pos; // Current position in cache
} tl_data;
Key optimizations include:
- Zero Contention: Each thread has independent PRNG instances
- Cache-Line Alignment: 64-byte alignment prevents false sharing
- Golden Ratio Seeding:
thread_seed = base_seed + thread_id * 0x9E3779B97F4A7C15
- Prefetching: Hardware prefetch hints for sequential access patterns
The simplest way to use qiprng is with default settings. The generator automatically selects optimal parameters and uses uniform distribution over [0,1]:
library(qiprng)
createPRNG()
x <- generatePRNG(10000)
mean(x) # ~0.5
var(x) # ~1/12
Configure the generator for different statistical distributions by specifying the distribution type and its parameters:
# Normal distribution
createPRNG(list(
distribution = "normal",
normal_mean = 0,
normal_sd = 1
))
norm_samples <- generatePRNG(1000)
# Exponential distribution
createPRNG(list(
distribution = "exponential",
exponential_lambda = 0.5
))
exp_samples <- generatePRNG(1000)
# Switch distributions dynamically
updatePRNG(list(distribution = "poisson", poisson_lambda = 3.5))
pois_samples <- generatePRNG(1000)
For high-performance applications, you can customize precision, mixing strategies, and parallelization:
cfg <- list(
a = 2L, b = 5L, c = -2L, # Custom quadratic coefficients
mpfr_precision = 256L, # Higher precision (24-10000 bits)
mixing_strategy = "cascade_mix", # Enhanced entropy mixing
use_parallel_filling = TRUE, # Enable parallel generation
buffer_size = 100000 # Large buffer for efficiency
)
createPRNG(cfg)
high_quality_samples <- generatePRNG(1000000)
library(qiprng)
library(parallel)
# Create a thread-safe PRNG configuration
createPRNG(list(
distribution = "normal",
normal_method = "ziggurat", # Both ziggurat and box_muller are supported and fully thread-safe
use_threading = TRUE, # Enable thread safety
use_parallel_filling = FALSE, # For maximum stability
buffer_size = 10000, # Larger buffer for better performance
debug = TRUE # Enable debug output
))
# Use parallel processing with the thread-safe PRNG
cl <- makeCluster(4)
clusterEvalQ(cl, library(qiprng))
# The same PRNG can now be safely used across parallel workers
results <- parSapply(cl, 1:4, function(i) {
# Each worker gets values from the shared thread-safe PRNG
values <- generatePRNG(5000)
c(mean = mean(values), sd = sd(values))
})
stopCluster(cl)
print(results)
For scientific computing and testing, you can enable fully reproducible sequences. Version 0.6.5 ensures perfect determinism - identical seeds always produce identical sequences:
# Set a seed for reproducibility
cfg <- list(
seed = 12345,
a = 2L,
b = 5L,
c = -2L
)
createPRNG(cfg)
# This will always produce the same sequence
set1 <- generatePRNG(100)
# Create another PRNG with the same seed
cleanup_prng()
createPRNG(cfg)
set2 <- generatePRNG(100)
# Verify they're identical
all(set1 == set2) # TRUE
Note: When seed is provided, the PRNG uses deterministic initialization while maintaining
mathematical properties. The generator performs a warm-up period for proper mixing. This
mode is suitable for research and testing but not for cryptographic applications. For full
determinism with normal distributions, use normal_method = "box_muller"
.
# Multiple quadratic irrationals with mixing
cfg <- list(
a = c(2, 3, 5), # Multiple QI coefficients
b = c(7, 11, 13),
c = c(-3, -5, -7),
mixing_strategy = "cascade_mix" # Choose mixing strategy
)
createPRNG(cfg)
# Available strategies:
# round_robin: Cycles through QIs sequentially (fastest)
# xor_mix: XOR combines outputs for bit diffusion
# averaging: Averages multiple QI outputs
# modular_add: Modular addition of outputs
# cascade_mix: Cascaded mixing for maximum entropy
# Efficiently skip ahead in the sequence
createPRNG()
initial <- generatePRNG(5)
# Jump ahead 1 billion steps - O(log n) complexity
jumpAheadPRNG(1000000000)
after_jump <- generatePRNG(5)
# Uses matrix exponentiation with MPFR arithmetic for:
# - O(log n) time complexity
# - Astronomical jump distances
# - Full precision preservation
The package includes a comprehensive testing framework with over 70 statistical tests covering distribution uniformity, independence, correlation, entropy, and cryptographic randomness. The test suite implements the NIST SP 800-22 standards along with classical PRNG tests.
The qiprng generator has also been validated against the NIST Statistical Test Suite (STS), the standard battery of tests for cryptographic random number generators. Testing was performed on binary sequences generated from five different discriminant configurations.
Test Results Summary:
- Pass Rate: 98.4% (185/188 tests passed)
- Test Coverage: All 15 NIST test categories
- Sequence Length: 1 million bits per test
- Performance: Comparable to cryptographically secure RNGs
The generator passed all fundamental randomness tests including Frequency, Runs, DFT, Random Excursions, and Linear Complexity tests. The minor failures (3 out of 148 Non-overlapping Template tests) fall within acceptable statistical variation for truly random sequences.
To reproduce the NIST validation:
- Binary sequence generation script:
validation/nist_top5_sequences/generate_binary_for_nist.R
- Test discriminants used:
validation/nist_top5_sequences/discriminants_used.csv
- The script generates 100 sequences of 1M bits each per discriminant for NIST STS testing
## Mathematical Foundation
The generator implements the quadratic irrational recurrence relation
x_{n+1} = (a·x_n² + b·x_n + c) mod 1, where the coefficients satisfy a > 0, c < 0, and
the discriminant b² - 4ac is a non-perfect square. This formulation, based on ergodic
theory of quadratic maps, ensures excellent statistical properties and long periods. The
jump-ahead functionality uses matrix exponentiation with MPFR arithmetic to achieve
O(log n) complexity, enabling efficient advancement by astronomical distances (10^18+ steps)
for parallel stream generation.
For detailed mathematical theory and proofs, see [MATH.Md](MATH.Md).
## Performance Considerations
The qiprng generator prioritizes cryptographic-quality randomness and mathematical rigor
over raw speed. Using high-precision MPFR arithmetic and optional ChaCha20 cryptographic
mixing, it runs approximately 50x slower than simple linear congruential generators but
delivers superior statistical properties and security guarantees. The trade-off is
worthwhile for applications requiring cryptographic security, precise statistical control,
reproducible research results, or validated random number quality. For general Monte Carlo
simulations where speed is paramount and cryptographic security is not required, standard
generators like Mersenne Twister may be more appropriate.
## Caching Framework
The package includes a powerful caching system to optimize performance for repeated operations:
### Cache Management
```r
# Enable/disable caching
set_cache_enabled(TRUE)
is_cache_enabled() # Check status
# Clear cache entries
clear_qiprng_cache() # Clear all
clear_qiprng_cache("acf") # Clear by pattern (regex supported)
# Export and import cache
export_cached_results("cache_backup.rds")
import_cached_results("cache_backup.rds", overwrite = FALSE)
# Cache statistics
qiprng_cache_stats() # Overall cache info
test_cache_stats() # Test-specific cache stats
The framework provides cached versions of expensive computations:
# Cached statistical functions
cached_acf(x, lag.max = 50) # Autocorrelation
cached_pacf(x, lag.max = 50) # Partial autocorrelation
cached_spectrum(x) # Spectral density
cached_compress(x, type = "gzip") # Compression
# Test result caching
cached_test_result(test_func, test_name, test_category,
data, config, ...)
The package provides a simple yet powerful API for random number generation:
createPRNG(config)
- Initializes a new generator with specified configurationgeneratePRNG(n)
- Generates n random values using current settingsupdatePRNG(config)
- Updates generator configuration dynamicallyjumpAheadPRNG(n)
- Advances state by n steps for parallel streamsreseedPRNG()
- Reinitializes with fresh entropycleanupPRNG()
- Releases resources and cleans up memory
The generator accepts numerous configuration parameters to customize behavior. Key options include quadratic coefficients (a, b, c), MPFR precision (24-10000 bits), distribution type and parameters, mixing strategy for multiple QIs, cryptographic mixing settings, and parallelization options. See the package documentation for complete parameter descriptions and valid ranges.
The following environment variables can be used to control MPFR optimization behavior:
QIPRNG_FORCE_MPFR
- Set to1
to disable fast-path optimization and force MPFR operationsQIPRNG_FAST_PATH
- Set to1
to force fast-path even for precision > 64 bitsQIPRNG_ENABLE_MPFR_DIAGNOSTICS
- Set to1
to enable precision loss tracking (compile-time flag)
These variables are useful for debugging, benchmarking, and ensuring reproducible results in research environments.
The testing framework provides comprehensive validation capabilities through test_prng()
for quick validation, create_prng_test_suite()
for custom test configurations, and
run_prng_test_suite()
for executing full test batteries with detailed reporting.
- R (>= 4.0.0)
- Rcpp (>= 1.0.0)
- MPFR library (for high-precision arithmetic)
- libsodium (for optional cryptographic mixing)
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature
) - Commit your changes (
git commit -m 'Add some AmazingFeature'
) - Push to the branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
If you use this package in your research, please cite:
@Manual{qiprng,
title = {qiprng: Quadratic Irrational Pseudo-Random Number Generator for R},
author = {Sergey Kornilov},
year = {2025},
note = {R package version 0.6.3},
url = {https://github.com/biostochastics/qiprng}
}
This project is licensed under the MIT License - see the LICENSE file for details.
Sergey Kornilov - [email protected]
Project Link: https://github.com/biostochastics/qiprng
Part of the Biostochastics collection of tools for translational science and biomarker discovery