blob: c59c57279f2f7979b705323992f3d8283c25608b [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
use std::f64::consts::PI;
#[inline]
pub fn seeded_rng(seed: u64) -> StdRng {
StdRng::seed_from_u64(seed)
}
#[inline]
pub fn unit_clamp(v: f64) -> f64 {
v.clamp(0.0, 1.0)
}
pub fn spider_seed_for_index(index: u64, global_seed: u64) -> u64 {
let mut z = index
.wrapping_add(global_seed)
.wrapping_add(0x9E3779B97F4A7C15);
z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB);
z ^ (z >> 31)
}
pub fn rand_normal(rng: &mut StdRng, mu: f64, sigma: f64) -> f64 {
let u1: f64 = rng.gen();
let u2: f64 = rng.gen();
mu + sigma * (-2.0 * u1.ln()).sqrt() * (2.0 * PI * u2).cos()
}
#[inline]
pub fn hash_to_unit_u64(x: u64, salt: u64) -> f64 {
let mut z = x.wrapping_add(salt).wrapping_add(0x9E3779B97F4A7C15);
z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB);
z ^= z >> 31;
((z >> 11) as f64) / ((1u64 << 53) as f64)
}
#[inline]
pub fn u01_from_seed(seed: u64) -> f64 {
let z = spider_seed_for_index(seed, 0xA1CE_CAFE);
((z >> 11) as f64) / ((1u64 << 53) as f64)
}
#[inline]
pub fn gauss_around(rng: &mut StdRng, center: (f64, f64), sigma: f64) -> (f64, f64) {
let (cx, cy) = center;
let dx = rand_normal(rng, 0.0, sigma);
let dy = rand_normal(rng, 0.0, sigma);
(unit_clamp(cx + dx), unit_clamp(cy + dy))
}
pub fn halton_2d(i: u64, base_x: u32, base_y: u32) -> (f64, f64) {
(radical_inverse(i, base_x), radical_inverse(i, base_y))
}
fn radical_inverse(mut n: u64, base: u32) -> f64 {
let b = base as u64;
let mut inv = 1.0 / b as f64;
let mut val = 0.0;
while n > 0 {
let d = (n % b) as f64;
val += d * inv;
n /= b;
inv /= b as f64;
}
val
}
pub fn pick_from_cdf(cdf: &[f64], u: f64) -> usize {
let (mut lo, mut hi) = (0usize, cdf.len());
while lo < hi {
let mid = (lo + hi) / 2;
if u <= cdf[mid] {
hi = mid;
} else {
lo = mid + 1;
}
}
lo.saturating_sub(0).min(cdf.len().saturating_sub(1))
}
pub fn build_cdf_from_weights(mut w: Vec<f64>) -> Vec<f64> {
let sum = w.iter().copied().sum::<f64>().max(1e-12);
let mut acc = 0.0;
for wi in &mut w {
acc += *wi / sum;
*wi = acc;
}
w
}
pub fn pareto_draw(u: f64, alpha: f64, xm: f64) -> f64 {
let a = alpha.max(1e-6);
let s = xm.max(1e-12);
s / (1.0 - u).powf(1.0 / a)
}
pub fn sample_normal_count(mu: f64, sd: f64, min_v: u32, max_v: u32, seed: u64) -> u32 {
let mut rng = seeded_rng(seed);
let draw = rand_normal(&mut rng, mu, sd).round();
let mut k = draw.max(min_v as f64) as u32;
if k > max_v {
k = max_v;
}
if k < 1 {
k = 1;
}
k
}