Refactor random number generation to factory
- Use a `make-random` factory to create isolated random number generators. - Remove the global `prng` from the `Environment`. - Ensure `make-random` can be called with or without a seed.
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
;; Benchmark: 2.0us
|
;; Benchmark: 3.0us
|
||||||
;; Benchmark-Repeat: 1015
|
;; Benchmark-Repeat: 593
|
||||||
;; Output: PipelineNode[last: Some(110.45243843391206)]
|
;; Output: PipelineNode[last: Some(110.45243843391206)]
|
||||||
|
|
||||||
(do
|
(do
|
||||||
;; Set the random seed to match the existing test output exactly
|
;; Set the random seed to match the existing test output exactly
|
||||||
(seed! 42)
|
(def random (make-random 42))
|
||||||
|
|
||||||
;; Use our new generic create-ticker to pulse 3 times
|
;; Use our new generic create-ticker to pulse 3 times
|
||||||
(def cnt 3)
|
(def cnt 3)
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ pub struct Environment {
|
|||||||
pub global_types: Rc<RefCell<HashMap<GlobalIdx, StaticType>>>,
|
pub global_types: Rc<RefCell<HashMap<GlobalIdx, StaticType>>>,
|
||||||
pub global_purity: Rc<RefCell<HashMap<GlobalIdx, Purity>>>,
|
pub global_purity: Rc<RefCell<HashMap<GlobalIdx, Purity>>>,
|
||||||
pub global_values: Rc<RefCell<Vec<Value>>>,
|
pub global_values: Rc<RefCell<Vec<Value>>>,
|
||||||
pub prng: Rc<RefCell<fastrand::Rng>>,
|
|
||||||
pub function_registry: Rc<RefCell<HashMap<GlobalIdx, BoundNode>>>,
|
pub function_registry: Rc<RefCell<HashMap<GlobalIdx, BoundNode>>>,
|
||||||
pub typed_function_registry: Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>,
|
pub typed_function_registry: Rc<RefCell<HashMap<GlobalIdx, AnalyzedNode>>>,
|
||||||
pub monomorph_cache: Rc<RefCell<MonoCache>>,
|
pub monomorph_cache: Rc<RefCell<MonoCache>>,
|
||||||
@@ -102,7 +101,6 @@ impl Environment {
|
|||||||
global_types: Rc::new(RefCell::new(HashMap::new())),
|
global_types: Rc::new(RefCell::new(HashMap::new())),
|
||||||
global_purity: Rc::new(RefCell::new(HashMap::new())),
|
global_purity: Rc::new(RefCell::new(HashMap::new())),
|
||||||
global_values: Rc::new(RefCell::new(Vec::new())),
|
global_values: Rc::new(RefCell::new(Vec::new())),
|
||||||
prng: Rc::new(RefCell::new(fastrand::Rng::new())),
|
|
||||||
function_registry: Rc::new(RefCell::new(HashMap::new())),
|
function_registry: Rc::new(RefCell::new(HashMap::new())),
|
||||||
typed_function_registry: Rc::new(RefCell::new(HashMap::new())),
|
typed_function_registry: Rc::new(RefCell::new(HashMap::new())),
|
||||||
monomorph_cache: Rc::new(RefCell::new(HashMap::new())),
|
monomorph_cache: Rc::new(RefCell::new(HashMap::new())),
|
||||||
|
|||||||
+29
-17
@@ -135,27 +135,39 @@ pub fn register(env: &Environment) {
|
|||||||
best
|
best
|
||||||
});
|
});
|
||||||
|
|
||||||
// Random
|
// Random generator factory (Closure-based)
|
||||||
let random_ty = StaticType::Function(Box::new(Signature {
|
let generator_sig = Signature {
|
||||||
params: StaticType::Tuple(vec![]),
|
params: StaticType::Tuple(vec![]),
|
||||||
ret: StaticType::Float,
|
ret: StaticType::Float,
|
||||||
}));
|
};
|
||||||
|
|
||||||
let prng = env.prng.clone();
|
let make_random_ty = StaticType::FunctionOverloads(vec![
|
||||||
env.register_native_fn("random", random_ty, Purity::SideEffectFree, move |_| {
|
Signature {
|
||||||
Value::Float(prng.borrow_mut().f64())
|
params: StaticType::Tuple(vec![]),
|
||||||
});
|
ret: StaticType::Function(Box::new(generator_sig.clone())),
|
||||||
|
},
|
||||||
|
Signature {
|
||||||
|
params: StaticType::Tuple(vec![StaticType::Int]),
|
||||||
|
ret: StaticType::Function(Box::new(generator_sig)),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
let seed_ty = StaticType::Function(Box::new(Signature {
|
env.register_native_fn("make-random", make_random_ty, Purity::SideEffectFree, |args| {
|
||||||
params: StaticType::Tuple(vec![StaticType::Int]),
|
let rng = if args.is_empty() {
|
||||||
ret: StaticType::Void,
|
fastrand::Rng::new()
|
||||||
}));
|
} else if let Value::Int(s) = args[0] {
|
||||||
|
fastrand::Rng::with_seed(s as u64)
|
||||||
|
} else {
|
||||||
|
fastrand::Rng::new()
|
||||||
|
};
|
||||||
|
|
||||||
let prng_seed = env.prng.clone();
|
let prng = std::rc::Rc::new(std::cell::RefCell::new(rng));
|
||||||
env.register_native_fn("seed!", seed_ty, Purity::Impure, move |args| {
|
|
||||||
if let Value::Int(s) = args[0] {
|
Value::Function(std::rc::Rc::new(crate::ast::types::NativeFunction {
|
||||||
prng_seed.borrow_mut().seed(s as u64);
|
func: std::rc::Rc::new(move |_| {
|
||||||
}
|
Value::Float(prng.borrow_mut().f64())
|
||||||
Value::Void
|
}),
|
||||||
|
purity: Purity::Impure,
|
||||||
|
}))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-14
@@ -175,12 +175,13 @@ mod tests {
|
|||||||
let env1 = Environment::new();
|
let env1 = Environment::new();
|
||||||
let env2 = Environment::new();
|
let env2 = Environment::new();
|
||||||
|
|
||||||
// 1. Seed env1
|
// 1. Create a seeded generator in env1
|
||||||
env1.run_script("(seed! 123)").unwrap();
|
env1.run_script("(def rand (make-random 123))").unwrap();
|
||||||
let val1_a = env1.run_script("(random)").unwrap();
|
let val1_a = env1.run_script("(rand)").unwrap();
|
||||||
|
|
||||||
// 2. env2 should have its own default seed state
|
// 2. env2 should have its own default seed state for its generators
|
||||||
let val2_a = env2.run_script("(random)").unwrap();
|
env2.run_script("(def rand (make-random))").unwrap();
|
||||||
|
let val2_a = env2.run_script("(rand)").unwrap();
|
||||||
|
|
||||||
// They are highly unlikely to be equal by default,
|
// They are highly unlikely to be equal by default,
|
||||||
// and seeding env1 MUST not have seeded env2.
|
// and seeding env1 MUST not have seeded env2.
|
||||||
@@ -189,9 +190,9 @@ mod tests {
|
|||||||
"Environments must have isolated PRNG states"
|
"Environments must have isolated PRNG states"
|
||||||
);
|
);
|
||||||
|
|
||||||
// 3. Seed env2 differently
|
// 3. Create another generator in env2 with the same seed
|
||||||
env2.run_script("(seed! 123)").unwrap();
|
env2.run_script("(def rand-same (make-random 123))").unwrap();
|
||||||
let val2_b = env2.run_script("(random)").unwrap();
|
let val2_b = env2.run_script("(rand-same)").unwrap();
|
||||||
|
|
||||||
// After same seeding, they should match (isolated but identical seed)
|
// After same seeding, they should match (isolated but identical seed)
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -205,12 +206,12 @@ mod tests {
|
|||||||
let env = Environment::new();
|
let env = Environment::new();
|
||||||
|
|
||||||
// 1. First run with seed 42
|
// 1. First run with seed 42
|
||||||
env.run_script("(seed! 42)").unwrap();
|
env.run_script("(def rand1 (make-random 42))").unwrap();
|
||||||
let val1 = env.run_script("(random)").unwrap();
|
let val1 = env.run_script("(rand1)").unwrap();
|
||||||
|
|
||||||
// 2. Second run with same seed 42
|
// 2. Second run with same seed 42
|
||||||
env.run_script("(seed! 42)").unwrap();
|
env.run_script("(def rand2 (make-random 42))").unwrap();
|
||||||
let val2 = env.run_script("(random)").unwrap();
|
let val2 = env.run_script("(rand2)").unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
val1, val2,
|
val1, val2,
|
||||||
@@ -218,8 +219,8 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 3. Third run with different seed
|
// 3. Third run with different seed
|
||||||
env.run_script("(seed! 123)").unwrap();
|
env.run_script("(def rand3 (make-random 123))").unwrap();
|
||||||
let val3 = env.run_script("(random)").unwrap();
|
let val3 = env.run_script("(rand3)").unwrap();
|
||||||
assert_ne!(val1, val3, "Random results must differ for different seeds");
|
assert_ne!(val1, val3, "Random results must differ for different seeds");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user