38
38
from pymc .pytensorf import convert_observed_data
39
39
40
40
__all__ = [
41
- "to_tuple" ,
42
- "shapes_broadcasting" ,
43
41
"broadcast_dist_samples_shape" ,
44
- "get_broadcastable_dist_samples" ,
45
- "broadcast_distribution_samples" ,
46
- "broadcast_dist_samples_to" ,
42
+ "to_tuple" ,
47
43
"rv_size_is_none" ,
48
44
"change_dist_size" ,
49
45
]
@@ -91,47 +87,6 @@ def _check_shape_type(shape):
91
87
return tuple (out )
92
88
93
89
94
- def shapes_broadcasting (* args , raise_exception = False ):
95
- """Return the shape resulting from broadcasting multiple shapes.
96
- Represents numpy's broadcasting rules.
97
-
98
- Parameters
99
- ----------
100
- *args: array-like of int
101
- Tuples or arrays or lists representing the shapes of arrays to be
102
- broadcast.
103
- raise_exception: bool (optional)
104
- Controls whether to raise an exception or simply return `None` if
105
- the broadcasting fails.
106
-
107
- Returns
108
- -------
109
- Resulting shape. If broadcasting is not possible and `raise_exception` is
110
- False, then `None` is returned. If `raise_exception` is `True`, a
111
- `ValueError` is raised.
112
- """
113
- x = list (_check_shape_type (args [0 ])) if args else ()
114
- for arg in args [1 :]:
115
- y = list (_check_shape_type (arg ))
116
- if len (x ) < len (y ):
117
- x , y = y , x
118
- if len (y ) > 0 :
119
- x [- len (y ) :] = [
120
- j if i == 1 else i if j == 1 else i if i == j else 0
121
- for i , j in zip (x [- len (y ) :], y )
122
- ]
123
- if not all (x ):
124
- if raise_exception :
125
- raise ValueError (
126
- "Supplied shapes {} do not broadcast together" .format (
127
- ", " .join ([f"{ a } " for a in args ])
128
- )
129
- )
130
- else :
131
- return None
132
- return tuple (x )
133
-
134
-
135
90
def broadcast_dist_samples_shape (shapes , size = None ):
136
91
"""Apply shape broadcasting to shape tuples but assuming that the shapes
137
92
correspond to draws from random variables, with the `size` tuple possibly
@@ -152,27 +107,22 @@ def broadcast_dist_samples_shape(shapes, size=None):
152
107
Examples
153
108
--------
154
109
.. code-block:: python
155
-
156
110
size = 100
157
111
shape0 = (size,)
158
112
shape1 = (size, 5)
159
113
shape2 = (size, 4, 5)
160
114
out = broadcast_dist_samples_shape([shape0, shape1, shape2],
161
115
size=size)
162
116
assert out == (size, 4, 5)
163
-
164
117
.. code-block:: python
165
-
166
118
size = 100
167
119
shape0 = (size,)
168
120
shape1 = (5,)
169
121
shape2 = (4, 5)
170
122
out = broadcast_dist_samples_shape([shape0, shape1, shape2],
171
123
size=size)
172
124
assert out == (size, 4, 5)
173
-
174
125
.. code-block:: python
175
-
176
126
size = 100
177
127
shape0 = (1,)
178
128
shape1 = (5,)
@@ -182,7 +132,7 @@ def broadcast_dist_samples_shape(shapes, size=None):
182
132
assert out == (4, 5)
183
133
"""
184
134
if size is None :
185
- broadcasted_shape = shapes_broadcasting (* shapes )
135
+ broadcasted_shape = np . broadcast_shapes (* shapes )
186
136
if broadcasted_shape is None :
187
137
raise ValueError (
188
138
"Cannot broadcast provided shapes {} given size: {}" .format (
@@ -195,7 +145,7 @@ def broadcast_dist_samples_shape(shapes, size=None):
195
145
# samples shapes without the size prepend
196
146
sp_shapes = [s [len (_size ) :] if _size == s [: min ([len (_size ), len (s )])] else s for s in shapes ]
197
147
try :
198
- broadcast_shape = shapes_broadcasting (* sp_shapes , raise_exception = True )
148
+ broadcast_shape = np . broadcast_shapes (* sp_shapes )
199
149
except ValueError :
200
150
raise ValueError (
201
151
"Cannot broadcast provided shapes {} given size: {}" .format (
@@ -215,212 +165,7 @@ def broadcast_dist_samples_shape(shapes, size=None):
215
165
else :
216
166
p_shape = shape
217
167
broadcastable_shapes .append (p_shape )
218
- return shapes_broadcasting (* broadcastable_shapes , raise_exception = True )
219
-
220
-
221
- def get_broadcastable_dist_samples (
222
- samples , size = None , must_bcast_with = None , return_out_shape = False
223
- ):
224
- """Get a view of the samples drawn from distributions which adds new axes
225
- in between the `size` prepend and the distribution's `shape`. These views
226
- should be able to broadcast the samples from the distrubtions taking into
227
- account the `size` (i.e. the number of samples) of the draw, which is
228
- prepended to the sample's `shape`. Optionally, one can supply an extra
229
- `must_bcast_with` to try to force samples to be able to broadcast with a
230
- given shape. A `ValueError` is raised if it is not possible to broadcast
231
- the provided samples.
232
-
233
- Parameters
234
- ----------
235
- samples: Iterable of ndarrays holding the sampled values
236
- size: None, int or tuple (optional)
237
- size of the sample set requested.
238
- must_bcast_with: None, int or tuple (optional)
239
- Tuple shape to which the samples must be able to broadcast
240
- return_out_shape: bool (optional)
241
- If `True`, this function also returns the output's shape and not only
242
- samples views.
243
-
244
- Returns
245
- -------
246
- broadcastable_samples: List of the broadcasted sample arrays
247
- broadcast_shape: If `return_out_shape` is `True`, the resulting broadcast
248
- shape is returned.
249
-
250
- Examples
251
- --------
252
- .. code-block:: python
253
-
254
- must_bcast_with = (3, 1, 5)
255
- size = 100
256
- sample0 = np.random.randn(size)
257
- sample1 = np.random.randn(size, 5)
258
- sample2 = np.random.randn(size, 4, 5)
259
- out = broadcast_dist_samples_to(
260
- [sample0, sample1, sample2],
261
- size=size,
262
- must_bcast_with=must_bcast_with,
263
- )
264
- assert out[0].shape == (size, 1, 1, 1)
265
- assert out[1].shape == (size, 1, 1, 5)
266
- assert out[2].shape == (size, 1, 4, 5)
267
- assert np.all(sample0[:, None, None, None] == out[0])
268
- assert np.all(sample1[:, None, None] == out[1])
269
- assert np.all(sample2[:, None] == out[2])
270
-
271
- .. code-block:: python
272
-
273
- size = 100
274
- must_bcast_with = (3, 1, 5)
275
- sample0 = np.random.randn(size)
276
- sample1 = np.random.randn(5)
277
- sample2 = np.random.randn(4, 5)
278
- out = broadcast_dist_samples_to(
279
- [sample0, sample1, sample2],
280
- size=size,
281
- must_bcast_with=must_bcast_with,
282
- )
283
- assert out[0].shape == (size, 1, 1, 1)
284
- assert out[1].shape == (5,)
285
- assert out[2].shape == (4, 5)
286
- assert np.all(sample0[:, None, None, None] == out[0])
287
- assert np.all(sample1 == out[1])
288
- assert np.all(sample2 == out[2])
289
- """
290
- samples = [np .asarray (p ) for p in samples ]
291
- _size = to_tuple (size )
292
- must_bcast_with = to_tuple (must_bcast_with )
293
- # Raw samples shapes
294
- p_shapes = [p .shape for p in samples ] + [_check_shape_type (must_bcast_with )]
295
- out_shape = broadcast_dist_samples_shape (p_shapes , size = size )
296
- # samples shapes without the size prepend
297
- sp_shapes = [
298
- s [len (_size ) :] if _size == s [: min ([len (_size ), len (s )])] else s for s in p_shapes
299
- ]
300
- broadcast_shape = shapes_broadcasting (* sp_shapes , raise_exception = True )
301
- broadcastable_samples = []
302
- for param , p_shape , sp_shape in zip (samples , p_shapes , sp_shapes ):
303
- if _size == p_shape [: min ([len (_size ), len (p_shape )])]:
304
- # If size prepends the shape, then we have to add broadcasting axis
305
- # in the middle
306
- slicer_head = [slice (None )] * len (_size )
307
- slicer_tail = [np .newaxis ] * (len (broadcast_shape ) - len (sp_shape )) + [
308
- slice (None )
309
- ] * len (sp_shape )
310
- else :
311
- # If size does not prepend the shape, then we have leave the
312
- # parameter as is
313
- slicer_head = []
314
- slicer_tail = [slice (None )] * len (sp_shape )
315
- broadcastable_samples .append (param [tuple (slicer_head + slicer_tail )])
316
- if return_out_shape :
317
- return broadcastable_samples , out_shape
318
- else :
319
- return broadcastable_samples
320
-
321
-
322
- def broadcast_distribution_samples (samples , size = None ):
323
- """Broadcast samples drawn from distributions taking into account the
324
- size (i.e. the number of samples) of the draw, which is prepended to
325
- the sample's shape.
326
-
327
- Parameters
328
- ----------
329
- samples: Iterable of ndarrays holding the sampled values
330
- size: None, int or tuple (optional)
331
- size of the sample set requested.
332
-
333
- Returns
334
- -------
335
- List of broadcasted sample arrays
336
-
337
- Examples
338
- --------
339
- .. code-block:: python
340
-
341
- size = 100
342
- sample0 = np.random.randn(size)
343
- sample1 = np.random.randn(size, 5)
344
- sample2 = np.random.randn(size, 4, 5)
345
- out = broadcast_distribution_samples([sample0, sample1, sample2],
346
- size=size)
347
- assert all((o.shape == (size, 4, 5) for o in out))
348
- assert np.all(sample0[:, None, None] == out[0])
349
- assert np.all(sample1[:, None, :] == out[1])
350
- assert np.all(sample2 == out[2])
351
-
352
- .. code-block:: python
353
-
354
- size = 100
355
- sample0 = np.random.randn(size)
356
- sample1 = np.random.randn(5)
357
- sample2 = np.random.randn(4, 5)
358
- out = broadcast_distribution_samples([sample0, sample1, sample2],
359
- size=size)
360
- assert all((o.shape == (size, 4, 5) for o in out))
361
- assert np.all(sample0[:, None, None] == out[0])
362
- assert np.all(sample1 == out[1])
363
- assert np.all(sample2 == out[2])
364
- """
365
- return np .broadcast_arrays (* get_broadcastable_dist_samples (samples , size = size ))
366
-
367
-
368
- def broadcast_dist_samples_to (to_shape , samples , size = None ):
369
- """Broadcast samples drawn from distributions to a given shape, taking into
370
- account the size (i.e. the number of samples) of the draw, which is
371
- prepended to the sample's shape.
372
-
373
- Parameters
374
- ----------
375
- to_shape: Tuple shape onto which the samples must be able to broadcast
376
- samples: Iterable of ndarrays holding the sampled values
377
- size: None, int or tuple (optional)
378
- size of the sample set requested.
379
-
380
- Returns
381
- -------
382
- List of the broadcasted sample arrays
383
-
384
- Examples
385
- --------
386
- .. code-block:: python
387
-
388
- to_shape = (3, 1, 5)
389
- size = 100
390
- sample0 = np.random.randn(size)
391
- sample1 = np.random.randn(size, 5)
392
- sample2 = np.random.randn(size, 4, 5)
393
- out = broadcast_dist_samples_to(
394
- to_shape,
395
- [sample0, sample1, sample2],
396
- size=size
397
- )
398
- assert np.all((o.shape == (size, 3, 4, 5) for o in out))
399
- assert np.all(sample0[:, None, None, None] == out[0])
400
- assert np.all(sample1[:, None, None] == out[1])
401
- assert np.all(sample2[:, None] == out[2])
402
-
403
- .. code-block:: python
404
-
405
- size = 100
406
- to_shape = (3, 1, 5)
407
- sample0 = np.random.randn(size)
408
- sample1 = np.random.randn(5)
409
- sample2 = np.random.randn(4, 5)
410
- out = broadcast_dist_samples_to(
411
- to_shape,
412
- [sample0, sample1, sample2],
413
- size=size
414
- )
415
- assert np.all((o.shape == (size, 3, 4, 5) for o in out))
416
- assert np.all(sample0[:, None, None, None] == out[0])
417
- assert np.all(sample1 == out[1])
418
- assert np.all(sample2 == out[2])
419
- """
420
- samples , to_shape = get_broadcastable_dist_samples (
421
- samples , size = size , must_bcast_with = to_shape , return_out_shape = True
422
- )
423
- return [np .broadcast_to (o , to_shape ) for o in samples ]
168
+ return np .broadcast_shapes (* broadcastable_shapes )
424
169
425
170
426
171
# User-provided can be lazily specified as scalars
0 commit comments