@@ -240,10 +240,9 @@ def test_random_basic_properties(self):
240
240
assert np .var (draws ) > 0
241
241
242
242
def test_random_edge_cases (self ):
243
- """Test edge cases with more reasonable parameter values"""
244
- # Test with small beta and large alpha values
245
- beta_vals = [0.1 , 0.5 ]
246
- alpha_vals = [5.0 , 10.0 ]
243
+ """Test with very small and large beta and alpha values"""
244
+ beta_vals = [20.0 , 0.07 , 18.0 , 0.05 ]
245
+ alpha_vals = [20.0 , 14.0 , 0.06 , 0.05 ]
247
246
248
247
for beta in beta_vals :
249
248
for alpha in alpha_vals :
@@ -256,27 +255,10 @@ def test_random_edge_cases(self):
256
255
assert np .mean (draws ) > 0
257
256
assert np .var (draws ) > 0
258
257
259
- @pytest .mark .parametrize (
260
- "alpha" ,
261
- [
262
- (0.5 , 1.0 , 10.0 ),
263
- ],
264
- )
265
- def test_random_moments (self , alpha ):
266
- beta = np .array ([0.5 , 1.0 , 10.0 ])
267
- dist = self .pymc_dist .dist (alpha = alpha , beta = beta , size = (10_000 , len (beta )))
268
- draws = dist .eval ()
269
-
270
- assert np .all (draws > 0 )
271
- assert np .all (draws .astype (int ) == draws )
272
- assert np .mean (draws ) > 0
273
- assert np .var (draws ) > 0
274
-
275
258
def test_logp (self ):
276
- # Create PyTensor variables with explicit values to ensure proper initialization
277
259
alpha = pt .scalar ("alpha" )
278
260
beta = pt .scalar ("beta" )
279
- value = pt .vector ("value" , dtype = "int64" )
261
+ value = pt .vector (dtype = "int64" )
280
262
281
263
# Compile logp function for testing
282
264
dist = ShiftedBetaGeometric .dist (alpha , beta )
@@ -289,33 +271,33 @@ def test_logp(self):
289
271
assert not np .any (np .isnan (logp_vals ))
290
272
assert np .all (np .isfinite (logp_vals ))
291
273
292
- assert logp_fn (- 1 , alpha = 5 , beta = 1 ) == - np .inf
274
+ neg_value = np .array ([- 1 ], dtype = "int64" )
275
+ assert logp_fn (neg_value , alpha = 5 , beta = 1 )[0 ] == - np .inf
293
276
294
277
# Check alpha/beta restrictions
278
+ valid_value = np .array ([1 ], dtype = "int64" )
295
279
with pytest .raises (ParameterValueError ):
296
- logp_fn (1 , alpha = - 1 , beta = 2 )
280
+ logp_fn (valid_value , alpha = - 1 , beta = 2 ) # invalid neg alpha
297
281
with pytest .raises (ParameterValueError ):
298
- logp_fn (1 , alpha = 0 , beta = 0 )
282
+ logp_fn (valid_value , alpha = 0 , beta = 0 ) # invalid zero alpha and beta
299
283
with pytest .raises (ParameterValueError ):
300
- logp_fn (1 , alpha = 1 , beta = - 1 )
284
+ logp_fn (valid_value , alpha = 1 , beta = - 1 ) # invalid neg beta
301
285
302
- def test_logp_matches_paper_alpha1_beta1 (self ):
303
- # For alpha=1, beta=1, P(T=t) = 1 / (t (t+1)) → logp = -log(t(t+1))
304
- # Derived from B(2, t) / B(1,1); see Appendix B (B3)
305
- # Reference: Fader & Hardie (2007), Appendix B [Figure B1, expression (B3)]
286
+ def test_logp_matches_paper (self ):
287
+ # Reference: Fader & Hardie (2007), Appendix B (Figure B1, cells B6:B12)
306
288
# https://faculty.wharton.upenn.edu/wp-content/uploads/2012/04/Fader_hardie_jim_07.pdf
307
289
alpha = 1.0
308
290
beta = 1.0
309
- t_vec = np .array ([1 , 2 , 3 , 4 , 5 ], dtype = "int64" )
310
- expected = - np .log (t_vec * (t_vec + 1 ))
291
+ t_vec = np .array ([1 , 2 , 3 , 4 , 5 , 6 , 7 ], dtype = "int64" )
292
+ p_t = np .array ([0.5 , 0.167 , 0.083 , 0.05 , 0.033 , 0.024 , 0.018 ], dtype = "float64" )
293
+ expected = np .log (p_t )
311
294
312
- alpha_sym = pt .scalar ()
313
- beta_sym = pt .scalar ()
295
+ alpha_ = pt .scalar ("alpha" )
296
+ beta_ = pt .scalar ("beta" )
314
297
value = pt .vector (dtype = "int64" )
315
- dist = ShiftedBetaGeometric .dist (alpha_sym , beta_sym )
316
- logp = pm .logp (dist , value )
317
- fn = pytensor .function ([value , alpha_sym , beta_sym ], logp )
318
- np .testing .assert_allclose (fn (t_vec , alpha , beta ), expected , rtol = 1e-12 , atol = 1e-12 )
298
+ logp = pm .logp (ShiftedBetaGeometric .dist (alpha_ , beta_ ), value )
299
+ logp_fn = pytensor .function ([value , alpha_ , beta_ ], logp )
300
+ np .testing .assert_allclose (logp_fn (t_vec , alpha , beta ), expected , rtol = 1e-2 )
319
301
320
302
@pytest .mark .parametrize (
321
303
"alpha, beta, size, expected_shape" ,
@@ -339,24 +321,10 @@ def test_support_point(self, alpha, beta, size, expected_shape):
339
321
340
322
# Check values are positive integers
341
323
assert np .all (init_point > 0 )
342
- # assert np.all(init_point.astype(int) == init_point)
324
+ assert np .all (init_point .astype (int ) == init_point )
343
325
344
326
# Check values are finite and reasonable
345
327
assert np .all (np .isfinite (init_point ))
346
328
assert np .all (init_point < 1e6 ) # Should not be extremely large
347
329
348
- # TODO: expected values must be provided
349
330
assert_support_point_is_expected (model , init_point )
350
-
351
- # TODO: Adapt this to ShiftedBetaGeometric and delete above test?
352
- @pytest .mark .parametrize (
353
- "mu, lam, size, expected" ,
354
- [
355
- (50 , [- 0.6 , 0 , 0.6 ], None , np .floor (50 / (1 - np .array ([- 0.6 , 0 , 0.6 ])))),
356
- ([5 , 50 ], - 0.1 , (4 , 2 ), np .full ((4 , 2 ), np .floor (np .array ([5 , 50 ]) / 1.1 ))),
357
- ],
358
- )
359
- def test_moment (self , mu , lam , size , expected ):
360
- with pm .Model () as model :
361
- GeneralizedPoisson ("x" , mu = mu , lam = lam , size = size )
362
- assert_support_point_is_expected (model , expected )
0 commit comments