1+
12use crate :: * ;
23use crate :: metta:: * ;
34use super :: { grounded_op, regex} ;
45use crate :: metta:: text:: Tokenizer ;
56use crate :: metta:: runner:: number:: * ;
67use crate :: metta:: runner:: bool:: * ;
78
8- use std:: fmt:: Display ;
9- use rand:: Rng ;
9+ use std:: fmt:: { Display , Formatter } ;
10+ use std:: cell:: RefCell ;
11+ use rand:: { Rng , SeedableRng , rngs:: StdRng } ;
12+ use std:: rc:: Rc ;
1013
1114//TODO: In the current version of rand it is possible for rust to hang if range end's value is too
1215// big. In future releases (0.9+) of rand signature of sample_single will be changed and it will be
1316// possible to use match construction to cover overflow and other errors. So after library will be
1417// upgraded RandomInt and RandomFloat codes should be altered.
1518// see comment https://github.com/trueagi-io/hyperon-experimental/pull/791#discussion_r1824355414
19+
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+
1669#[ derive( Clone , Debug ) ]
1770pub struct RandomIntOp { }
1871
1972grounded_op ! ( RandomIntOp , "random-int" ) ;
2073
2174impl Grounded for RandomIntOp {
2275 fn type_ ( & self ) -> Atom {
23- Atom :: expr ( [ ARROW_SYMBOL , 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 ] )
2477 }
2578
2679 fn as_execute ( & self ) -> Option < & dyn CustomExecute > {
@@ -30,15 +83,16 @@ impl Grounded for RandomIntOp {
3083
3184impl CustomExecute for RandomIntOp {
3285 fn execute ( & self , args : & [ Atom ] ) -> Result < Vec < Atom > , ExecError > {
33- let arg_error = || ExecError :: from ( "random-int expects two arguments: number (start) and number (end)" ) ;
34- let start: i64 = args. get ( 0 ) . and_then ( Number :: from_atom) . ok_or_else ( arg_error) ?. into ( ) ;
35- let end: i64 = args. get ( 1 ) . and_then ( Number :: from_atom) . ok_or_else ( arg_error) ?. into ( ) ;
86+ let arg_error = || ExecError :: from ( "random-int expects three arguments: random generator, number (start) and number (end)" ) ;
87+ let generator = args. get ( 0 ) . ok_or_else ( arg_error) ?. into ( ) ;
88+ let start: i64 = args. get ( 1 ) . and_then ( Number :: from_atom) . ok_or_else ( arg_error) ?. into ( ) ;
89+ let end: i64 = args. get ( 2 ) . and_then ( Number :: from_atom) . ok_or_else ( arg_error) ?. into ( ) ;
90+ let generator = Atom :: as_gnd :: < RandomGenerator > ( generator) . ok_or ( "random-int expects a random generator as its argument" ) ?;
3691 let range = start..end;
3792 if range. is_empty ( ) {
38- return Err ( ExecError :: from ( "Range is empty " ) ) ;
93+ return Err ( ExecError :: from ( "RangeIsEmpty " ) ) ;
3994 }
40- let mut rng = rand:: thread_rng ( ) ;
41- Ok ( vec ! [ Atom :: gnd( Number :: Integer ( rng. gen_range( range) ) ) ] )
95+ Ok ( vec ! [ Atom :: gnd( Number :: Integer ( generator. random_range( range) ) ) ] )
4296 }
4397}
4498
@@ -49,7 +103,7 @@ grounded_op!(RandomFloatOp, "random-float");
49103
50104impl Grounded for RandomFloatOp {
51105 fn type_ ( & self ) -> Atom {
52- Atom :: expr ( [ ARROW_SYMBOL , 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 ] )
53107 }
54108
55109 fn as_execute ( & self ) -> Option < & dyn CustomExecute > {
@@ -59,24 +113,101 @@ impl Grounded for RandomFloatOp {
59113
60114impl CustomExecute for RandomFloatOp {
61115 fn execute ( & self , args : & [ Atom ] ) -> Result < Vec < Atom > , ExecError > {
62- let arg_error = || ExecError :: from ( "random-float expects two arguments: number (start) and number (end)" ) ;
63- let start: f64 = args. get ( 0 ) . and_then ( Number :: from_atom) . ok_or_else ( arg_error) ?. into ( ) ;
64- let end: f64 = args. get ( 1 ) . and_then ( Number :: from_atom) . ok_or_else ( arg_error) ?. into ( ) ;
116+ let arg_error = || ExecError :: from ( "random-float expects three arguments: random generator, number (start) and number (end)" ) ;
117+ let generator = args. get ( 0 ) . ok_or_else ( arg_error) ?. into ( ) ;
118+ let start: f64 = args. get ( 1 ) . and_then ( Number :: from_atom) . ok_or_else ( arg_error) ?. into ( ) ;
119+ let end: f64 = args. get ( 2 ) . and_then ( Number :: from_atom) . ok_or_else ( arg_error) ?. into ( ) ;
120+ let generator = Atom :: as_gnd :: < RandomGenerator > ( generator) . ok_or ( "random-float expects a random generator as its argument" ) ?;
65121 let range = start..end;
66122 if range. is_empty ( ) {
67- return Err ( ExecError :: from ( "Range is empty " ) ) ;
123+ return Err ( ExecError :: from ( "RangeIsEmpty " ) ) ;
68124 }
69- let mut rng = rand:: thread_rng ( ) ;
70- Ok ( vec ! [ Atom :: gnd( Number :: Float ( rng. gen_range( range) ) ) ] )
125+ Ok ( vec ! [ Atom :: gnd( Number :: Float ( generator. random_range( range) ) ) ] )
126+ }
127+ }
128+
129+ #[ derive( Clone , Debug ) ]
130+ pub struct SetRandomSeedOp { }
131+
132+ grounded_op ! ( SetRandomSeedOp , "set-random-seed" ) ;
133+
134+ impl Grounded for SetRandomSeedOp {
135+ fn type_ ( & self ) -> Atom {
136+ Atom :: expr ( [ ARROW_SYMBOL , ATOM_TYPE_RANDOM_GENERATOR , ATOM_TYPE_NUMBER , UNIT_TYPE ] )
137+ }
138+
139+ fn as_execute ( & self ) -> Option < & dyn CustomExecute > {
140+ Some ( self )
71141 }
72142}
73143
144+ impl CustomExecute for SetRandomSeedOp {
145+ fn execute ( & self , args : & [ Atom ] ) -> Result < Vec < Atom > , ExecError > {
146+ let arg_error = || ExecError :: from ( "set-random-seed expects two arguments: random generator and number (seed)" ) ;
147+ let generator = args. get ( 0 ) . ok_or_else ( arg_error) ?. into ( ) ;
148+ let seed: i64 = args. get ( 1 ) . and_then ( Number :: from_atom) . ok_or_else ( arg_error) ?. into ( ) ;
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 ) ;
151+ Ok ( vec ! [ UNIT_ATOM ] )
152+ }
153+ }
154+
155+ #[ derive( Clone , Debug ) ]
156+ pub struct NewRandomGeneratorOp { }
157+
158+ grounded_op ! ( NewRandomGeneratorOp , "new-random-generator" ) ;
159+
160+ impl Grounded for NewRandomGeneratorOp {
161+ fn type_ ( & self ) -> Atom {
162+ Atom :: expr ( [ ARROW_SYMBOL , ATOM_TYPE_NUMBER , ATOM_TYPE_RANDOM_GENERATOR ] )
163+ }
164+
165+ fn as_execute ( & self ) -> Option < & dyn CustomExecute > {
166+ Some ( self )
167+ }
168+ }
169+
170+ impl CustomExecute for NewRandomGeneratorOp {
171+ fn execute ( & self , args : & [ Atom ] ) -> Result < Vec < Atom > , ExecError > {
172+ let arg_error = || ExecError :: from ( "new-random-generator expects one argument: number (seed)" ) ;
173+ let seed: i64 = args. get ( 0 ) . and_then ( Number :: from_atom) . ok_or_else ( arg_error) ?. into ( ) ;
174+ let new_generator = RandomGenerator :: from_seed_u64 ( seed as u64 ) ;
175+ Ok ( vec ! [ Atom :: gnd( new_generator) ] )
176+ }
177+ }
178+
179+ #[ derive( Clone , Debug ) ]
180+ pub struct ResetRandomGeneratorOp { }
181+
182+ grounded_op ! ( ResetRandomGeneratorOp , "reset-random-generator" ) ;
183+
184+ impl Grounded for ResetRandomGeneratorOp {
185+ fn type_ ( & self ) -> Atom {
186+ Atom :: expr ( [ ARROW_SYMBOL , ATOM_TYPE_RANDOM_GENERATOR , UNIT_ATOM ] )
187+ }
188+
189+ fn as_execute ( & self ) -> Option < & dyn CustomExecute > {
190+ Some ( self )
191+ }
192+ }
193+
194+ impl CustomExecute for ResetRandomGeneratorOp {
195+ fn execute ( & self , args : & [ Atom ] ) -> Result < Vec < Atom > , ExecError > {
196+ let arg_error = || ExecError :: from ( "reset-random-generator expects one argument: random generator" ) ;
197+ let generator = args. get ( 0 ) . ok_or_else ( arg_error) ?. into ( ) ;
198+ let generator = Atom :: as_gnd :: < RandomGenerator > ( generator) . ok_or ( "set-random-seed expects a random generator as its argument" ) ?;
199+ generator. reset ( ) ;
200+ Ok ( vec ! [ UNIT_ATOM ] )
201+ }
202+ }
203+
204+
74205// NOTE: flip is absent in Python intentionally for conversion testing
75206#[ derive( Clone , PartialEq , Debug ) ]
76207pub struct FlipOp { }
77208
78209impl Display for FlipOp {
79- fn fmt ( & self , f : & mut std :: fmt :: Formatter < ' _ > ) -> std:: fmt:: Result {
210+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
80211 write ! ( f, "flip" )
81212 }
82213}
@@ -102,6 +233,14 @@ pub fn register_common_tokens(tref: &mut Tokenizer) {
102233 tref. register_token ( regex ( r"random-int" ) , move |_| { random_int_op. clone ( ) } ) ;
103234 let random_float_op = Atom :: gnd ( RandomFloatOp { } ) ;
104235 tref. register_token ( regex ( r"random-float" ) , move |_| { random_float_op. clone ( ) } ) ;
236+ let set_seed_op = Atom :: gnd ( SetRandomSeedOp { } ) ;
237+ tref. register_token ( regex ( r"set-random-seed" ) , move |_| { set_seed_op. clone ( ) } ) ;
238+ let new_random_generator_op = Atom :: gnd ( NewRandomGeneratorOp { } ) ;
239+ tref. register_token ( regex ( r"new-random-generator" ) , move |_| { new_random_generator_op. clone ( ) } ) ;
240+ let reset_random_generator_op = Atom :: gnd ( ResetRandomGeneratorOp { } ) ;
241+ tref. register_token ( regex ( r"reset-random-generator" ) , move |_| { reset_random_generator_op. clone ( ) } ) ;
242+ let generator = RandomGenerator :: from_os_rng ( ) ;
243+ tref. register_token ( regex ( r"&rng" ) , move |_| { Atom :: gnd ( generator. clone ( ) ) } ) ;
105244 let flip_op = Atom :: gnd ( FlipOp { } ) ;
106245 tref. register_token ( regex ( r"flip" ) , move |_| { flip_op. clone ( ) } ) ;
107246}
@@ -113,26 +252,88 @@ mod tests {
113252
114253 #[ test]
115254 fn metta_random ( ) {
116- assert_eq ! ( run_program( & format!( "!(chain (eval (random-int 0 5)) $rint (and (>= $rint 0) (< $rint 5)))" ) ) , Ok ( vec![ vec![ expr!( { Bool ( true ) } ) ] ] ) ) ;
117- assert_eq ! ( run_program( & format!( "!(random-int 0 0)" ) ) , Ok ( vec![ vec![ expr!( "Error" ( { RandomIntOp { } } { Number :: Integer ( 0 ) } { Number :: Integer ( 0 ) } ) "Range is empty" ) ] ] ) ) ;
118- assert_eq ! ( run_program( & format!( "!(chain (eval (random-float 0.0 5.0)) $rfloat (and (>= $rfloat 0.0) (< $rfloat 5.0)))" ) ) , Ok ( vec![ vec![ expr!( { Bool ( true ) } ) ] ] ) ) ;
119- assert_eq ! ( run_program( & format!( "!(random-float 0 -5)" ) ) , Ok ( vec![ vec![ expr!( "Error" ( { RandomFloatOp { } } { Number :: Integer ( 0 ) } { Number :: Integer ( -5 ) } ) "Range is empty" ) ] ] ) ) ;
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 ] ] ) ) ;
273+
274+ assert_eq ! ( run_program( & format!( "!(set-random-seed &rng 0)" ) ) , Ok ( vec![ vec![ UNIT_ATOM ] ] ) ) ;
275+
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+ " ) ) ) ;
283+
284+ assert_eq ! ( run_program( & format!(
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)
290+ (== $res_1 $res_2))))))"
291+ ) ) , Ok ( vec![ vec![ expr!( { Bool ( true ) } ) ] ] ) ) ;
292+
293+ assert_eq ! ( run_program( & format!(
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)
299+ (== $rfloat $rfloat2))))) )"
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 ) } ) ] ] ) ) ;
120308 }
121309
122310 #[ test]
123311 fn random_op ( ) {
124- let res = RandomIntOp { } . execute ( & mut vec ! [ 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 ) } ) ] ) ;
125313 let range = 0 ..5 ;
126314 let res_i64: i64 = res. unwrap ( ) . get ( 0 ) . and_then ( Number :: from_atom) . unwrap ( ) . into ( ) ;
127315 assert ! ( range. contains( & res_i64) ) ;
128- let res = RandomIntOp { } . execute ( & mut vec ! [ expr!( { Number :: Integer ( 2 ) } ) , expr!( { Number :: Integer ( -2 ) } ) ] ) ;
129- 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 " ) ) ) ;
130318
131- let res = RandomFloatOp { } . execute ( & mut vec ! [ 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 ) } ) ] ) ;
132320 let range = 0.0 ..5.0 ;
133321 let res_f64: f64 = res. unwrap ( ) . get ( 0 ) . and_then ( Number :: from_atom) . unwrap ( ) . into ( ) ;
134322 assert ! ( range. contains( & res_f64) ) ;
135- let res = RandomFloatOp { } . execute ( & mut vec ! [ expr!( { Number :: Integer ( 0 ) } ) , expr!( { Number :: Integer ( 0 ) } ) ] ) ;
136- 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" ) ) ) ;
325+
326+ let gen = NewRandomGeneratorOp { } . execute ( & mut vec ! [ expr!( { Number :: Integer ( 0 ) } ) ] ) ;
327+ let res1 = RandomFloatOp { } . execute ( & mut vec ! [ expr!( { gen } ) , expr!( { Number :: Integer ( 0 ) } ) , expr!( { Number :: Integer ( 5 ) } ) ] ) ;
328+ let gen = NewRandomGeneratorOp { } . execute ( & mut vec ! [ expr!( { Number :: Integer ( 0 ) } ) ] ) ;
329+ let res2 = RandomFloatOp { } . execute ( & mut vec ! [ expr!( { gen } ) , expr!( { Number :: Integer ( 0 ) } ) , expr!( { Number :: Integer ( 5 ) } ) ] ) ;
330+ assert_eq ! ( res1, res2) ;
331+
332+ let gen = NewRandomGeneratorOp { } . execute ( & mut vec ! [ expr!( { Number :: Integer ( 0 ) } ) ] ) ;
333+ let _ = SetRandomSeedOp { } . execute ( & mut vec ! [ expr!( { gen . clone( ) } ) , expr!( { Number :: Integer ( 0 ) } ) ] ) ;
334+ let res1 = RandomFloatOp { } . execute ( & mut vec ! [ expr!( { gen . clone( ) } ) , expr!( { Number :: Integer ( 0 ) } ) , expr!( { Number :: Integer ( 5 ) } ) ] ) ;
335+ let _ = SetRandomSeedOp { } . execute ( & mut vec ! [ expr!( { gen . clone( ) } ) , expr!( { Number :: Integer ( 5 ) } ) ] ) ;
336+ let res2 = RandomFloatOp { } . execute ( & mut vec ! [ expr!( { gen . clone( ) } ) , expr!( { Number :: Integer ( 0 ) } ) , expr!( { Number :: Integer ( 5 ) } ) ] ) ;
337+ assert_eq ! ( res1, res2) ;
137338 }
138339}
0 commit comments