Skip to content

Commit c2c1b8b

Browse files
authored
Merge pull request #11 from vsbogd/random-seed-comments
Wrap StdRng into RandomGenerator grounded atom
2 parents a6b375d + aefcdda commit c2c1b8b

File tree

1 file changed

+118
-59
lines changed

1 file changed

+118
-59
lines changed

lib/src/metta/runner/stdlib/random.rs

Lines changed: 118 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,63 @@ use std::rc::Rc;
1717
// upgraded RandomInt and RandomFloat codes should be altered.
1818
// see comment https://github.com/trueagi-io/hyperon-experimental/pull/791#discussion_r1824355414
1919

20+
pub const ATOM_TYPE_RANDOM_GENERATOR : Atom = sym!("RandomGenerator");
21+
22+
#[derive(Clone, Debug)]
23+
pub struct RandomGenerator(Rc<RefCell<StdRng>>);
24+
25+
impl RandomGenerator {
26+
fn from_os_rng() -> Self {
27+
Self(Rc::new(RefCell::new(StdRng::from_os_rng())))
28+
}
29+
30+
fn from_seed_u64(seed: u64) -> Self {
31+
Self(Rc::new(RefCell::new(StdRng::seed_from_u64(seed))))
32+
}
33+
34+
fn reseed_from_u64(&self, seed: u64) {
35+
*self.0.borrow_mut() = StdRng::seed_from_u64(seed);
36+
}
37+
38+
fn reset(&self) {
39+
*self.0.borrow_mut() = StdRng::from_os_rng();
40+
}
41+
42+
fn random_range<T, R>(&self, range: R) -> T
43+
where
44+
T: rand::distr::uniform::SampleUniform,
45+
R: rand::distr::uniform::SampleRange<T>,
46+
{
47+
self.0.borrow_mut().random_range(range)
48+
}
49+
}
50+
51+
impl Grounded for RandomGenerator {
52+
fn type_(&self) -> Atom {
53+
ATOM_TYPE_RANDOM_GENERATOR
54+
}
55+
}
56+
57+
impl Display for RandomGenerator {
58+
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
59+
write!(f, "RandomGenerator-{:?}", self.0.as_ptr())
60+
}
61+
}
62+
63+
impl PartialEq for RandomGenerator {
64+
fn eq(&self, other: &Self) -> bool {
65+
self.0.as_ptr() == other.0.as_ptr()
66+
}
67+
}
68+
2069
#[derive(Clone, Debug)]
2170
pub struct RandomIntOp {}
2271

2372
grounded_op!(RandomIntOp, "random-int");
2473

2574
impl Grounded for RandomIntOp {
2675
fn type_(&self) -> Atom {
27-
Atom::expr([ARROW_SYMBOL, rust_type_atom::<Rc<RefCell<StdRng>>>(), ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
76+
Atom::expr([ARROW_SYMBOL, ATOM_TYPE_RANDOM_GENERATOR, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
2877
}
2978

3079
fn as_execute(&self) -> Option<&dyn CustomExecute> {
@@ -38,12 +87,12 @@ impl CustomExecute for RandomIntOp {
3887
let generator = args.get(0).ok_or_else(arg_error)?.into();
3988
let start: i64 = args.get(1).and_then(Number::from_atom).ok_or_else(arg_error)?.into();
4089
let end: i64 = args.get(2).and_then(Number::from_atom).ok_or_else(arg_error)?.into();
41-
let generator = Atom::as_gnd::<Rc<RefCell<StdRng>>>(generator).ok_or("random-int expects a random generator as its argument")?;
90+
let generator = Atom::as_gnd::<RandomGenerator>(generator).ok_or("random-int expects a random generator as its argument")?;
4291
let range = start..end;
4392
if range.is_empty() {
44-
return Err(ExecError::from("Range is empty"));
93+
return Err(ExecError::from("RangeIsEmpty"));
4594
}
46-
Ok(vec![Atom::gnd(Number::Integer(generator.borrow_mut().random_range(range)))])
95+
Ok(vec![Atom::gnd(Number::Integer(generator.random_range(range)))])
4796
}
4897
}
4998

@@ -54,7 +103,7 @@ grounded_op!(RandomFloatOp, "random-float");
54103

55104
impl Grounded for RandomFloatOp {
56105
fn type_(&self) -> Atom {
57-
Atom::expr([ARROW_SYMBOL, rust_type_atom::<Rc<RefCell<StdRng>>>(), ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
106+
Atom::expr([ARROW_SYMBOL, ATOM_TYPE_RANDOM_GENERATOR, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
58107
}
59108

60109
fn as_execute(&self) -> Option<&dyn CustomExecute> {
@@ -68,12 +117,12 @@ impl CustomExecute for RandomFloatOp {
68117
let generator = args.get(0).ok_or_else(arg_error)?.into();
69118
let start: f64 = args.get(1).and_then(Number::from_atom).ok_or_else(arg_error)?.into();
70119
let end: f64 = args.get(2).and_then(Number::from_atom).ok_or_else(arg_error)?.into();
71-
let generator: &Rc<RefCell<StdRng>> = Atom::as_gnd::<Rc<RefCell<StdRng>>>(generator).ok_or("random-float expects a random generator as its argument")?;
120+
let generator = Atom::as_gnd::<RandomGenerator>(generator).ok_or("random-float expects a random generator as its argument")?;
72121
let range = start..end;
73122
if range.is_empty() {
74-
return Err(ExecError::from("Range is empty"));
123+
return Err(ExecError::from("RangeIsEmpty"));
75124
}
76-
Ok(vec![Atom::gnd(Number::Float(generator.borrow_mut().random_range(range)))])
125+
Ok(vec![Atom::gnd(Number::Float(generator.random_range(range)))])
77126
}
78127
}
79128

@@ -84,7 +133,7 @@ grounded_op!(SetRandomSeedOp, "set-random-seed");
84133

85134
impl Grounded for SetRandomSeedOp {
86135
fn type_(&self) -> Atom {
87-
Atom::expr([ARROW_SYMBOL, rust_type_atom::<Rc<RefCell<StdRng>>>(), ATOM_TYPE_NUMBER, UNIT_TYPE])
136+
Atom::expr([ARROW_SYMBOL, ATOM_TYPE_RANDOM_GENERATOR, ATOM_TYPE_NUMBER, UNIT_TYPE])
88137
}
89138

90139
fn as_execute(&self) -> Option<&dyn CustomExecute> {
@@ -97,8 +146,8 @@ impl CustomExecute for SetRandomSeedOp {
97146
let arg_error = || ExecError::from("set-random-seed expects two arguments: random generator and number (seed)");
98147
let generator = args.get(0).ok_or_else(arg_error)?.into();
99148
let seed: i64 = args.get(1).and_then(Number::from_atom).ok_or_else(arg_error)?.into();
100-
let generator: &Rc<RefCell<StdRng>> = Atom::as_gnd::<Rc<RefCell<StdRng>>>(generator).ok_or("set-random-seed expects a random generator as its argument")?;
101-
*generator.borrow_mut() = StdRng::seed_from_u64(seed as u64);
149+
let generator = Atom::as_gnd::<RandomGenerator>(generator).ok_or("set-random-seed expects a random generator as its argument")?;
150+
generator.reseed_from_u64(seed as u64);
102151
Ok(vec![UNIT_ATOM])
103152
}
104153
}
@@ -110,7 +159,7 @@ grounded_op!(NewRandomGeneratorOp, "new-random-generator");
110159

111160
impl Grounded for NewRandomGeneratorOp {
112161
fn type_(&self) -> Atom {
113-
Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, rust_type_atom::<Rc<RefCell<StdRng>>>()])
162+
Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_RANDOM_GENERATOR])
114163
}
115164

116165
fn as_execute(&self) -> Option<&dyn CustomExecute> {
@@ -122,8 +171,8 @@ impl CustomExecute for NewRandomGeneratorOp {
122171
fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
123172
let arg_error = || ExecError::from("new-random-generator expects one argument: number (seed)");
124173
let seed: i64 = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?.into();
125-
let new_generator: Rc<RefCell<StdRng>> = Rc::new(RefCell::new(StdRng::seed_from_u64(seed as u64)));
126-
Ok(vec![Atom::value(new_generator)])
174+
let new_generator = RandomGenerator::from_seed_u64(seed as u64);
175+
Ok(vec![Atom::gnd(new_generator)])
127176
}
128177
}
129178

@@ -134,7 +183,7 @@ grounded_op!(ResetRandomGeneratorOp, "reset-random-generator");
134183

135184
impl Grounded for ResetRandomGeneratorOp {
136185
fn type_(&self) -> Atom {
137-
Atom::expr([ARROW_SYMBOL, rust_type_atom::<Rc<RefCell<StdRng>>>(), UNIT_ATOM])
186+
Atom::expr([ARROW_SYMBOL, ATOM_TYPE_RANDOM_GENERATOR, UNIT_ATOM])
138187
}
139188

140189
fn as_execute(&self) -> Option<&dyn CustomExecute> {
@@ -146,8 +195,8 @@ impl CustomExecute for ResetRandomGeneratorOp {
146195
fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
147196
let arg_error = || ExecError::from("reset-random-generator expects one argument: random generator");
148197
let generator = args.get(0).ok_or_else(arg_error)?.into();
149-
let generator: &Rc<RefCell<StdRng>> = Atom::as_gnd::<Rc<RefCell<StdRng>>>(generator).ok_or("set-random-seed expects a random generator as its argument")?;
150-
*generator.borrow_mut() = StdRng::from_os_rng();
198+
let generator = Atom::as_gnd::<RandomGenerator>(generator).ok_or("set-random-seed expects a random generator as its argument")?;
199+
generator.reset();
151200
Ok(vec![UNIT_ATOM])
152201
}
153202
}
@@ -190,8 +239,8 @@ pub fn register_common_tokens(tref: &mut Tokenizer) {
190239
tref.register_token(regex(r"new-random-generator"), move |_| { new_random_generator_op.clone() });
191240
let reset_random_generator_op = Atom::gnd(ResetRandomGeneratorOp{});
192241
tref.register_token(regex(r"reset-random-generator"), move |_| { reset_random_generator_op.clone() });
193-
let generator = Rc::new(RefCell::new(StdRng::from_os_rng()));
194-
tref.register_token(regex(r"&rng"), move |_| { Atom::value(generator.clone()) });
242+
let generator = RandomGenerator::from_os_rng();
243+
tref.register_token(regex(r"&rng"), move |_| { Atom::gnd(generator.clone()) });
195244
let flip_op = Atom::gnd(FlipOp{});
196245
tref.register_token(regex(r"flip"), move |_| { flip_op.clone() });
197246
}
@@ -203,66 +252,76 @@ mod tests {
203252

204253
#[test]
205254
fn metta_random() {
206-
assert_eq!(run_program(&format!("!(chain (eval (random-int &rng 0 5)) $rint (and (>= $rint 0) (< $rint 5)))")), Ok(vec![vec![expr!({Bool(true)})]]));
207-
// assert_eq!(run_program(&format!(
208-
// "!(assertEqual
209-
// (random-int &rng 5 0)
210-
// (Error (random-int &rng 5 0) \"Range is empty\"))")),
211-
// Ok(vec![vec![UNIT_ATOM]]));
212-
assert_eq!(run_program(&format!("!(chain (eval (random-float &rng 0.0 5.0)) $rfloat (and (>= $rfloat 0.0) (< $rfloat 5.0)))")), Ok(vec![vec![expr!({Bool(true)})]]));
213-
// assert_eq!(run_program(&format!(
214-
// "!(assertEqual
215-
// (random-float &rng 5 0)
216-
// (Error (random-float &rng 5 0) \"Range is empty\"))")),
217-
// Ok(vec![vec![UNIT_ATOM]]));
255+
assert_eq!(run_program(&format!(
256+
"!(chain (eval (random-int &rng 0 5)) $rint
257+
(and (>= $rint 0) (< $rint 5)))")),
258+
Ok(vec![vec![expr!({Bool(true)})]]));
259+
assert_eq!(run_program(&format!(
260+
"!(assertEqual
261+
(random-int &rng 5 0)
262+
(Error (random-int &rng 5 0) RangeIsEmpty))")),
263+
Ok(vec![vec![UNIT_ATOM]]));
264+
assert_eq!(run_program(&format!(
265+
"!(chain (eval (random-float &rng 0.0 5.0)) $rfloat
266+
(and (>= $rfloat 0.0) (< $rfloat 5.0)))")),
267+
Ok(vec![vec![expr!({Bool(true)})]]));
268+
assert_eq!(run_program(&format!(
269+
"!(assertEqual
270+
(random-float &rng 5 0)
271+
(Error (random-float &rng 5 0) RangeIsEmpty))")),
272+
Ok(vec![vec![UNIT_ATOM]]));
218273

219274
assert_eq!(run_program(&format!("!(set-random-seed &rng 0)")), Ok(vec![vec![UNIT_ATOM]]));
220275

221-
assert_eq!(run_program(&format!(
222-
"!(bind! &newrng (new-random-generator 0))\
223-
!(random-float &newrng 0 10)"
224-
)), run_program(&format!(
225-
"!(bind! &newrng (new-random-generator 0))\
226-
!(random-float &newrng 0 10)"
227-
)));
276+
assert_eq!(run_program(&format!("
277+
!(bind! &newrng (new-random-generator 0))
278+
!(random-float &newrng 0 10)
279+
")), run_program(&format!("
280+
!(bind! &newrng (new-random-generator 0))
281+
!(random-float &newrng 0 10)
282+
")));
228283

229284
assert_eq!(run_program(&format!(
230-
"!(let $newrng (new-random-generator 0)\
231-
(let $t (set-random-seed $newrng 5)\
232-
(let $res_1 (random-float $newrng 0 5)\
233-
(let $t2 (set-random-seed $newrng 5)\
234-
(let $res_2 (random-float $newrng 0 5)\
285+
"!(let $newrng (new-random-generator 0)
286+
(let $t (set-random-seed $newrng 5)
287+
(let $res_1 (random-float $newrng 0 5)
288+
(let $t2 (set-random-seed $newrng 5)
289+
(let $res_2 (random-float $newrng 0 5)
235290
(== $res_1 $res_2))))))"
236-
)),
237-
Ok(vec![vec![expr!({Bool(true)})]]));
291+
)), Ok(vec![vec![expr!({Bool(true)})]]));
238292

239293
assert_eq!(run_program(&format!(
240-
"!(let $seededrng (new-random-generator 0) \
241-
(let $seededrng2 (new-random-generator 0) \
242-
(let $t (reset-random-generator $seededrng) \
243-
(let $rfloat (random-float $seededrng 0 100) \
244-
(let $rfloat2 (random-float $seededrng2 0 100) \
294+
"!(let $seededrng (new-random-generator 0)
295+
(let $seededrng2 (new-random-generator 0)
296+
(let $t (reset-random-generator $seededrng)
297+
(let $rfloat (random-float $seededrng 0 100)
298+
(let $rfloat2 (random-float $seededrng2 0 100)
245299
(== $rfloat $rfloat2))))) )"
246-
)),
247-
Ok(vec![vec![expr!({Bool(false)})]]));
248-
assert_eq!(run_program(&format!("!(let $newrng (new-random-generator 0) (let $t (reset-random-generator $newrng) (let $res (random-float $newrng 0 5) (and (>= $res 0.0) (< $res 5.0)))))")), Ok(vec![vec![expr!({Bool(true)})]]));
300+
)), Ok(vec![vec![expr!({Bool(false)})]]));
301+
302+
assert_eq!(run_program(&format!("
303+
!(let $newrng (new-random-generator 0)
304+
(let $t (reset-random-generator $newrng)
305+
(let $res (random-float $newrng 0 5)
306+
(and (>= $res 0.0) (< $res 5.0)))))")),
307+
Ok(vec![vec![expr!({Bool(true)})]]));
249308
}
250309

251310
#[test]
252311
fn random_op() {
253-
let res = RandomIntOp{}.execute(&mut vec![expr!({Rc::new(RefCell::new(StdRng::from_os_rng()))}), expr!({Number::Integer(0)}), expr!({Number::Integer(5)})]);
312+
let res = RandomIntOp{}.execute(&mut vec![expr!({RandomGenerator::from_os_rng()}), expr!({Number::Integer(0)}), expr!({Number::Integer(5)})]);
254313
let range = 0..5;
255314
let res_i64: i64 = res.unwrap().get(0).and_then(Number::from_atom).unwrap().into();
256315
assert!(range.contains(&res_i64));
257-
let res = RandomIntOp{}.execute(&mut vec![expr!({Rc::new(RefCell::new(StdRng::from_os_rng()))}), expr!({Number::Integer(2)}), expr!({Number::Integer(-2)})]);
258-
assert_eq!(res, Err(ExecError::from("Range is empty")));
316+
let res = RandomIntOp{}.execute(&mut vec![expr!({RandomGenerator::from_os_rng()}), expr!({Number::Integer(2)}), expr!({Number::Integer(-2)})]);
317+
assert_eq!(res, Err(ExecError::from("RangeIsEmpty")));
259318

260-
let res = RandomFloatOp{}.execute(&mut vec![expr!({Rc::new(RefCell::new(StdRng::from_os_rng()))}), expr!({Number::Integer(0)}), expr!({Number::Integer(5)})]);
319+
let res = RandomFloatOp{}.execute(&mut vec![expr!({RandomGenerator::from_os_rng()}), expr!({Number::Integer(0)}), expr!({Number::Integer(5)})]);
261320
let range = 0.0..5.0;
262321
let res_f64: f64 = res.unwrap().get(0).and_then(Number::from_atom).unwrap().into();
263322
assert!(range.contains(&res_f64));
264-
let res = RandomFloatOp{}.execute(&mut vec![expr!({Rc::new(RefCell::new(StdRng::from_os_rng()))}), expr!({Number::Integer(0)}), expr!({Number::Integer(0)})]);
265-
assert_eq!(res, Err(ExecError::from("Range is empty")));
323+
let res = RandomFloatOp{}.execute(&mut vec![expr!({RandomGenerator::from_os_rng()}), expr!({Number::Integer(0)}), expr!({Number::Integer(0)})]);
324+
assert_eq!(res, Err(ExecError::from("RangeIsEmpty")));
266325

267326
let gen = NewRandomGeneratorOp{}.execute(&mut vec![expr!({Number::Integer(0)})]);
268327
let res1 = RandomFloatOp{}.execute(&mut vec![expr!({gen}), expr!({Number::Integer(0)}), expr!({Number::Integer(5)})]);

0 commit comments

Comments
 (0)