@@ -4311,6 +4311,10 @@ class ConstantDelay(DelayDistribution):
43114311
43124312 def __init__ (self , delay : int ):
43134313 self .delay = delay
4314+ self ._log_pmf_value = torch .tensor (0.0 ,
4315+ dtype = torch .get_default_dtype ())
4316+ self ._neg_inf = torch .tensor (float ('-inf' ),
4317+ dtype = torch .get_default_dtype ())
43144318
43154319 def sample (self ):
43164320 return self .delay
@@ -4326,9 +4330,24 @@ def set_parameters(self, parameters):
43264330 def probability (self , k : int ) -> float :
43274331 return 1.0 if k == self .delay else 0.0
43284332
4333+ def log_prob (self , k : Union [int , torch .Tensor ]) -> torch .Tensor :
4334+ """Fast vectorized lookup of log P(delay = k)."""
4335+ if not isinstance (k , torch .Tensor ):
4336+ k_tensor = torch .tensor (k , dtype = torch .long )
4337+ else :
4338+ k_tensor = k .long ()
4339+
4340+ # Initialize with -inf
4341+ log_probs = torch .full_like (k_tensor ,
4342+ self ._neg_inf ,
4343+ dtype = torch .get_default_dtype ())
4344+ # Set log(1.0) = 0.0 where k == self.delay
4345+ log_probs [k_tensor == self .delay ] = self ._log_pmf_value
4346+ return log_probs
4347+
43294348 @cached_property
43304349 def _str (self ) -> str :
4331- return f"ConstantDelay({ self .delay } )"
4350+ return f"ConstantDelay({ self .delay :.4f } )"
43324351
43334352
43344353class GaussianDelay (DelayDistribution ):
@@ -4438,15 +4457,22 @@ def __init__(self, mu: float, phi: float, max_k: int = 50):
44384457 def _update_cache (self ) -> None :
44394458 ks = torch .arange (self ._max_k , dtype = torch .get_default_dtype ())
44404459 log_fact = gammaln (ks + 1 )
4441- log_C = - 0.5 * (torch .log (torch .tensor (2 * torch .pi )) +
4442- torch .log (self .phi ) + torch .log (self .mu ))
4443- log_p = (log_C + self .phi * (ks * torch .log (self .mu ) - log_fact ) -
4444- self .phi * self .mu )
4445- log_p -= torch .max (log_p )
4446- pmf = torch .exp (log_p )
4447- pmf /= pmf .sum ()
4448- self ._pmf = pmf
4449- self ._cdf = torch .cumsum (pmf , dim = 0 )
4460+ log_C = - 0.5 * (
4461+ torch .log (torch .tensor (2 * torch .pi , dtype = self .mu .dtype ))
4462+ + # Ensure tensor has same dtype
4463+ torch .log (self .phi ) + torch .log (self .mu ))
4464+ # Calculate unnormalized log probabilities
4465+ log_p_unnormalized = (log_C + self .phi *
4466+ (ks * torch .log (self .mu ) - log_fact ) -
4467+ self .phi * self .mu )
4468+
4469+ # Calculate normalized log PMF using logsumexp for numerical stability
4470+ log_normalization_constant = torch .logsumexp (log_p_unnormalized , dim = 0 )
4471+ self ._log_pmf = log_p_unnormalized - log_normalization_constant
4472+
4473+ # Calculate PMF from the normalized log PMF
4474+ self ._pmf = torch .exp (self ._log_pmf )
4475+ self ._cdf = torch .cumsum (self ._pmf , dim = 0 )
44504476
44514477 def set_parameters (self , parameters : Sequence [float ]) -> None :
44524478 """Update μ and φ, then rebuild the cache."""
@@ -4465,6 +4491,32 @@ def probability(self, k: int) -> float:
44654491 return float (self ._pmf [k ])
44664492 return 0.0
44674493
4494+ def log_prob (self , k : Union [int , torch .Tensor ]) -> torch .Tensor :
4495+ """Fast vectorized lookup of log P(delay = k)."""
4496+ if not isinstance (k , torch .Tensor ):
4497+ k_tensor = torch .tensor (
4498+ k , dtype = torch .long ) # Use long for potential indexing
4499+ else :
4500+ k_tensor = k .long () # Ensure long type for indexing
4501+
4502+ original_shape = k_tensor .shape
4503+ k_flat = k_tensor .flatten ()
4504+
4505+ # Initialize log_probs with -inf, matching the default dtype
4506+ log_probs_flat = torch .full_like (k_flat ,
4507+ float ('-inf' ),
4508+ dtype = torch .get_default_dtype ())
4509+
4510+ assert self ._log_pmf is not None , "Cache not updated, _log_pmf is None."
4511+
4512+ valid_mask = (k_flat >= 0 ) & (k_flat < self ._max_k )
4513+ valid_indices_in_k = k_flat [valid_mask ]
4514+
4515+ if valid_indices_in_k .numel () > 0 :
4516+ log_probs_flat [valid_mask ] = self ._log_pmf [valid_indices_in_k ]
4517+
4518+ return log_probs_flat .reshape (original_shape )
4519+
44684520 def sample (self ) -> int :
44694521 u = torch .rand (1 ).item ()
44704522 return int (torch .searchsorted (self ._cdf , torch .tensor (u )))
0 commit comments