Skip to content

Commit f0fcf48

Browse files
committed
feat(01_sampling): added spawn + better introduction
1 parent c6f7023 commit f0fcf48

File tree

1 file changed

+76
-11
lines changed

1 file changed

+76
-11
lines changed

content/01_sampling.ipynb

+76-11
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@
99
"\n",
1010
"If you are working in simulation modelling in Python, you will likely need to use `numpy.random` namespace. It provides a variety of statistical distributions which you can use for efficient sampling. \n",
1111
"\n",
12-
"This notebook will guide you through an example of generating **100,000 samples** from the **uniform**, **exponential** and **normal** distributions."
12+
"This notebook will guide you through examples of \n",
13+
"\n",
14+
"1. Creating instances of a high quality Pseudo Random Number Generator (PRNG) using PCG64 provided by `numpy`\n",
15+
"2. Generating samples from the **uniform**, **exponential** and **normal** distributions.\n",
16+
"3. Spawning multiple non-overlapping streams of random numbers\n",
17+
"4. Using OOP to encapsulate PRNGs, distributions and parameters for simulation models."
1318
]
1419
},
1520
{
@@ -71,7 +76,7 @@
7176
" '''\n",
7277
" hist = np.histogram(samples, bins=np.arange(bins), \n",
7378
" density=True)\n",
74-
" \n",
79+
"\n",
7580
" fig = plt.figure(figsize=figsize)\n",
7681
" ax = fig.add_subplot()\n",
7782
" _ = ax.plot(hist[0])\n",
@@ -88,9 +93,13 @@
8893
"source": [
8994
"## 3. Creating a random number generator object\n",
9095
"\n",
91-
"To generate random numbers for sampling from each distribution, we can use the `default_rng()` function from the `numpy.random` module.\n",
96+
"To generate pseudo random numbers for sampling from each distribution, we can use the `default_rng()` function from the `numpy.random` module.\n",
9297
"\n",
93-
"This function constructs an instance of a `Generator` class, which can produce random numbers. For more information on `Generator` you can look at [`numpy` online documentation.](https://numpy.org/doc/stable/reference/random/generator.html)"
98+
"This function constructs an instance of a `Generator` class, which can produce random numbers. \n",
99+
"\n",
100+
"By default `numpy` uses a Pseudo-Random Number Generator (PRNG) called use of the [Permuted Congruential Generator 64-bit](https://www.pcg-random.org/) (PCG64; period = $2^{128}$; maximum number of streams = $2^{63}$).\n",
101+
"\n",
102+
"For more information on `Generator` you can look at [`numpy` online documentation.](https://numpy.org/doc/stable/reference/random/generator.html)"
94103
]
95104
},
96105
{
@@ -147,7 +156,7 @@
147156
"samples = rng.uniform(low=10, high=40, size=1_000_000)\n",
148157
"\n",
149158
"# Illustrate with plot.\n",
150-
"fig, ax = distribution_plot(samples, bins=50)"
159+
"_ = distribution_plot(samples, bins=50)"
151160
]
152161
},
153162
{
@@ -165,9 +174,9 @@
165174
"metadata": {},
166175
"outputs": [],
167176
"source": [
168-
"rng = np.random.default_rng()\n",
177+
"rng = np.random.default_rng(42)\n",
169178
"samples = rng.exponential(scale=12, size=1_000_000)\n",
170-
"distribution_plot(samples, bins=50)"
179+
"_ = distribution_plot(samples, bins=50)"
171180
]
172181
},
173182
{
@@ -185,9 +194,9 @@
185194
"metadata": {},
186195
"outputs": [],
187196
"source": [
188-
"rng = np.random.default_rng()\n",
197+
"rng = np.random.default_rng(42)\n",
189198
"samples = rng.normal(loc=25.0, scale=5.0, size=1_000_000)\n",
190-
"distribution_plot(samples, bins=50)"
199+
"_ = distribution_plot(samples, bins=50)"
191200
]
192201
},
193202
{
@@ -218,7 +227,7 @@
218227
"id": "6ed34200-c5ac-4a70-ae83-90adf48aee67",
219228
"metadata": {},
220229
"source": [
221-
"Note that you can also set `size` to 1. Just be aware that an array is returned. e.g."
230+
"**Note** that you can also set `size` to 1. Just be aware that an array is returned. e.g."
222231
]
223232
},
224233
{
@@ -238,6 +247,62 @@
238247
"print(sample[0])"
239248
]
240249
},
250+
{
251+
"cell_type": "markdown",
252+
"id": "b4bbf593-fa4a-4347-acb8-2f3df370d6ef",
253+
"metadata": {},
254+
"source": [
255+
"## 5. Spawning multiple non-overlapping PRN streams.\n",
256+
"\n",
257+
"For simulation we ideally want to use multiple streams of random numbers that do not overlap (i.e. they are independent). This is straightforward to implement in Python using `SeedSequence` and a user provided integer seed and the number of independent streams to spawn.\n",
258+
"\n",
259+
"> As a user we don't need to worry about the quality of the integer seed provided. This is useful for implementing multiple replications and common random numbers."
260+
]
261+
},
262+
{
263+
"cell_type": "markdown",
264+
"id": "82a41ef0-7198-403d-9fc9-a73eb293655a",
265+
"metadata": {},
266+
"source": [
267+
"Here's how we create the seeds from a single user supplied seed. The returned variable `seeds` is a Python `List`."
268+
]
269+
},
270+
{
271+
"cell_type": "code",
272+
"execution_count": null,
273+
"id": "4c404fa0-0987-4bff-9a1d-e784c485e59f",
274+
"metadata": {},
275+
"outputs": [],
276+
"source": [
277+
"n_streams = 2\n",
278+
"user_seed = 1\n",
279+
"\n",
280+
"seed_sequence = np.random.SeedSequence(user_seed)\n",
281+
"seeds = seed_sequence.spawn(n_streams)"
282+
]
283+
},
284+
{
285+
"cell_type": "markdown",
286+
"id": "002292d7-7bd8-4fec-9eb0-b9a2f6459b4f",
287+
"metadata": {},
288+
"source": [
289+
"We use `seeds` when creating our PRNGs. For example, one for inter-arrival times and one for service times."
290+
]
291+
},
292+
{
293+
"cell_type": "code",
294+
"execution_count": null,
295+
"id": "268d5a1b-0372-4d4e-b38d-75b3b9b8990e",
296+
"metadata": {},
297+
"outputs": [],
298+
"source": [
299+
"# e.g. to model arrival times\n",
300+
"arrival_rng = np.random.default_rng(seeds[0])\n",
301+
"\n",
302+
"# e.g. to model service times\n",
303+
"service_rng = np.random.default_rng(seeds[1])"
304+
]
305+
},
241306
{
242307
"cell_type": "markdown",
243308
"id": "0dc23b86-1ae1-4da0-b393-97dcf884f442",
@@ -273,7 +338,7 @@
273338
" mean: float\n",
274339
" The mean of the exponential distribution\n",
275340
"\n",
276-
" random_seed: int, optional (default=None)\n",
341+
" random_seed: int | SeedSequence, optional (default=None)\n",
277342
" A random seed to reproduce samples. If set to none then a unique\n",
278343
" sample is created.\n",
279344
" '''\n",

0 commit comments

Comments
 (0)