29
29
; Original author: Jared Davis <jared@centtech.com>
31
31
(in-package "ACL2")
32
(include-book "xdoc/top" :dir :system)
33
(include-book "tools/include-raw" :dir :system)
37
:short "Influence the random numbers produced by @(see random$)."
38
:long "<p>General form:</p>
41
(include-book \"centaur/misc/seed-random\" :dir :system)
43
[:freshp t/nil] ;; default t
47
<p>Hypothetical example:</p>
50
(seed-random 'foo) ;; randomly seed the generator, name this seed 'foo
51
(random$ 50 state) --> (mv 31 state)
52
(random$ 50 state) --> (mv 49 state)
53
(random$ 50 state) --> (mv 2 state)
54
(random$ 50 state) --> (mv 23 state)
55
(random$ 50 state) --> (mv 15 state)
57
(seed-random 'foo) ;; return to the seed named 'foo
58
(random$ 50 state) --> (mv 31 state)
59
(random$ 50 state) --> (mv 49 state)
60
(random$ 50 state) --> (mv 2 state)
62
(seed-random 'bar :freshp nil) ;; copy current seed, name it 'bar
63
(random$ 50 state) --> (mv 23 state)
64
(random$ 50 state) --> (mv 15 state)
66
(seed-random 'foo) ;; return to 'foo
67
(random$ 50 state) --> (mv 31 state)
69
(seed-random 'bar) ;; return to 'bar
70
(random$ 50 state) --> (mv 23 state)
71
(random$ 50 state) --> (mv 15 state)
74
<p>Logically, @('seed-random$') ignores its arguments and just returns
75
@('nil'). We leave it enabled and would think it odd to ever prove a theorem
78
<p>Under the hood, @('seed-random$') influences the behavior of @(see random$).
79
Note that in its implementation, the ACL2 function @('(random$ limit state)')
80
basically just calls @('(random limit)') to produce its result. To understand
81
@('seed-random$'), it is useful to recall some features of Common Lisp:</p>
85
<li>A @('random-state') is an implementation-defined data type that is used by
86
the @('random') function to produce random numbers.</li>
88
<li>In particular, @('(random limit &optional random-state)') can use some
89
particular @('random-state') or, by default, uses whatever @('random-state') is
90
currently bound to the special variable @('*random-state*').</li>
92
<li>A fresh, \"randomly initialized\" @('random-state') can be produced with
93
@('(make-random-state t)').</li>
95
<li>The current @('*random-state*') can be copied with @('(make-random-state
100
<p>So, what does @('seed-random$') do?</p>
102
<p>We maintain a hidden association list that maps names to random-states.
103
These names can be any ACL2 objects, but we typically use symbols.</p>
105
<p>When @('seed-random$') is called with a name that is already bound to some
106
state, we just restore @('*random-state*') to this state. This effectively
107
resets the random number generator so that it produces the same random numbers
110
<p>When @('seed-random$') is called with a name that has not been bound yet,
111
its behavior depends on the optional @('freshp') keyword-argument.</p>
113
<p>When @('freshp') is @('t') (the default), we construct a \"randomly
114
initialized\" @('random-state'), bind @('name') to it, and install it as
115
@('*random-state*'). In other words, when @('foo') has never been used as a
116
name before, @('(seed-random$ 'foo)') effectively initializes the random number
117
generator to a truly random state.</p>
119
<p>On the other hand, when @('freshp') is @('nil') we simply copy and name the
120
current @('*random-state*'). It appears that, at least on CCL, the
121
@('*random-state*') starts the same upon every invocation of ACL2. Hence, if
122
you launch ACL2 and then immediately invoke</p>
125
(seed-random 'seed :freshp nil)
128
<p>you can obtain a sequence of random numbers that you can return to even
129
after restarting ACL2, and which can be returned to at any time during the
130
session by just calling @('(seed-random 'seed)').</p>")
33
132
(defun seed-random$-fn (name freshp)
34
133
(declare (xargs :guard t)
38
137
(defmacro seed-random$ (name &key (freshp 't))
40
Influence the random numbers produced by ~ilc[random$]~/
45
[:freshp t/nil] ;; default t
51
(seed-random 'foo) ;; randomly seed the generator, name this seed 'foo
52
(random$ 50 state) --> (mv 31 state)
53
(random$ 50 state) --> (mv 49 state)
54
(random$ 50 state) --> (mv 2 state)
55
(random$ 50 state) --> (mv 23 state)
56
(random$ 50 state) --> (mv 15 state)
58
(seed-random 'foo) ;; return to the seed named 'foo
59
(random$ 50 state) --> (mv 31 state)
60
(random$ 50 state) --> (mv 49 state)
61
(random$ 50 state) --> (mv 2 state)
63
(seed-random 'bar :freshp nil) ;; copy current seed, name it 'bar
64
(random$ 50 state) --> (mv 23 state)
65
(random$ 50 state) --> (mv 15 state)
67
(seed-random 'foo) ;; return to 'foo
68
(random$ 50 state) --> (mv 31 state)
70
(seed-random 'bar) ;; return to 'bar
71
(random$ 50 state) --> (mv 23 state)
72
(random$ 50 state) --> (mv 15 state)
75
Logically, ~c[seed-random$] ignores its arguments and just returns ~c[nil]; we
76
leave it enabled and would think it odd to ever prove a theorem about it.
78
Under the hood, ~c[seed-random$] influences the behavior of ~ilc[random$].
79
Note that in its implementation, the ACL2 function ~c[(random$ limit state)]
80
basically just calls ~c[(random limit)] to produce its result.
82
To understand ~c[seed-random$], it is useful to recall some features of Common
85
- A ~c[random-state] is an implementation-defined data type that is used by
86
the ~c[random] function to produce random numbers.
88
- In particular, ~c[(random limit &optional random-state)] can use some
89
particular ~c[random-state] or, by default, uses whatever ~c[random-state]
90
is currently bound to the special variable ~c[*random-state*].
92
- A fresh, \"randomly initialized\" ~c[random-state] can be produced with
93
~c[(make-random-state t)].
95
- The current ~c[*random-state*] can be copied with
96
~c[(make-random-state nil)].
98
So, what does ~c[seed-random$] do?
100
We maintain a hidden association list that maps names to random-states. These
101
names can be any ACL2 objects, but we typically use symbols.
103
When ~c[seed-random$] is called with a name that is already bound to some
104
state, we just restore ~c[*random-state*] to this state. This effectively
105
resets the random number generator so that it produces the same random numbers
108
When ~c[seed-random$] is called with a name that has not been bound yet, its
109
behavior depends on the optional ~c[freshp] keyword-argument.
111
When ~c[freshp] is ~c[t] (the default), we construct a \"randomly initialized\"
112
~c[random-state], bind ~c[name] to it, and install it as ~c[*random-state*].
113
In other words, when ~c[foo] has never been used as a name before,
114
~c[(seed-random$ 'foo)] effectively initializes the random number generator to
115
a truly random state.
117
On the other hand, when ~c[freshp] is ~c[nil] we simply copy and name the
118
current ~c[*random-state*]. It appears that, at least on CCL, the
119
~c[*random-state*] starts the same upon every invocation of ACL2. Hence, if
120
you launch ACL2 and then immediately invoke
122
(seed-random 'seed :freshp nil)
124
you can obtain a sequence of random numbers that you can return to even
125
after restarting ACL2, and which can be returned to at any time during the
126
session by just calling ~c[(seed-random 'seed)].~/~/"
128
138
`(seed-random$-fn ,name ,freshp))
130
140
(defttag seed-random$)
135
(defparameter *random-seeds* nil)
137
(defun seed-random$-fn (name freshp)
138
(let ((look (assoc-equal name *random-seeds*)))
140
;(format t "Restoring *random-state* to ~a.~%" (cdr look))
141
(setq *random-state* (make-random-state (cdr look))))
143
(let* ((new-st (make-random-state t)))
144
;(format t "Fresh seed ~a: ~a.~%" name new-st)
145
(setq *random-seeds* (acons name new-st *random-seeds*))
146
(setq *random-state* (make-random-state new-st))))
148
(let ((new-st (make-random-state nil)))
149
;(format t "Copy seed to ~a: ~a.~%" name new-st)
150
(setq *random-seeds* (acons name new-st *random-seeds*))))))
142
(include-raw "seed-random-raw.lsp")
154
145
;; Basic tests to make sure it works