33from __future__ import annotations
44
55import abc
6+ from typing import Any
67
78import numpy as np
8- from scipy .stats import laplace , norm , uniform
9+ from scipy .stats import (
10+ cauchy ,
11+ chi2 ,
12+ expon ,
13+ gamma ,
14+ laplace ,
15+ norm ,
16+ rayleigh ,
17+ uniform ,
18+ )
919
1020__all__ = [
1121 "Distribution" ,
@@ -277,6 +287,21 @@ def _inverse_transform_sample(self, shape) -> np.ndarray | float:
277287 )
278288 return self ._ppf_transformed_untruncated (uniform_sample )
279289
290+ def _repr (self , pars : dict [str , Any ] = None ) -> str :
291+ """Return a string representation of the distribution."""
292+ pars = ", " .join (f"{ k } ={ v } " for k , v in pars .items ()) if pars else ""
293+
294+ if self ._logbase is False :
295+ log = ""
296+ elif self ._logbase == np .exp (1 ):
297+ log = ", log=True"
298+ else :
299+ log = f", log={ self ._logbase } "
300+
301+ trunc = f", trunc={ self ._trunc } " if self ._trunc else ""
302+
303+ return f"{ self .__class__ .__name__ } ({ pars } { log } { trunc } )"
304+
280305
281306class Normal (Distribution ):
282307 """A (log-)normal distribution.
@@ -307,16 +332,7 @@ def __init__(
307332 super ().__init__ (log = log , trunc = trunc )
308333
309334 def __repr__ (self ):
310- if self ._logbase is False :
311- log = ""
312- if self ._logbase == np .exp (1 ):
313- log = ", log=True"
314- else :
315- log = f", log={ self ._logbase } "
316-
317- trunc = f", trunc={ self ._trunc } " if self ._trunc else ""
318-
319- return f"Normal(loc={ self ._loc } , scale={ self ._scale } { log } { trunc } )"
335+ return self ._repr ({"loc" : self ._loc , "scale" : self ._scale })
320336
321337 def _sample (self , shape = None ) -> np .ndarray | float :
322338 return np .random .normal (loc = self ._loc , scale = self ._scale , size = shape )
@@ -366,8 +382,7 @@ def __init__(
366382 super ().__init__ (log = log )
367383
368384 def __repr__ (self ):
369- log = f", log={ self ._logbase } " if self ._logbase else ""
370- return f"Uniform(low={ self ._low } , high={ self ._high } { log } )"
385+ return self ._repr ({"low" : self ._low , "high" : self ._high })
371386
372387 def _sample (self , shape = None ) -> np .ndarray | float :
373388 return np .random .uniform (low = self ._low , high = self ._high , size = shape )
@@ -411,9 +426,7 @@ def __init__(
411426 super ().__init__ (log = log , trunc = trunc )
412427
413428 def __repr__ (self ):
414- trunc = f", trunc={ self ._trunc } " if self ._trunc else ""
415- log = f", log={ self ._logbase } " if self ._logbase else ""
416- return f"Laplace(loc={ self ._loc } , scale={ self ._scale } { trunc } { log } )"
429+ return self ._repr ({"loc" : self ._loc , "scale" : self ._scale })
417430
418431 def _sample (self , shape = None ) -> np .ndarray | float :
419432 return np .random .laplace (loc = self ._loc , scale = self ._scale , size = shape )
@@ -436,3 +449,173 @@ def loc(self) -> float:
436449 def scale (self ) -> float :
437450 """The scale parameter of the underlying distribution."""
438451 return self ._scale
452+
453+
454+ class Cauchy (Distribution ):
455+ """A Cauchy distribution."""
456+
457+ def __init__ (
458+ self ,
459+ loc : float ,
460+ scale : float ,
461+ trunc : tuple [float , float ] | None = None ,
462+ log : bool | float = False ,
463+ ):
464+ self ._loc = loc
465+ self ._scale = scale
466+ super ().__init__ (log = log , trunc = trunc )
467+
468+ def __repr__ (self ):
469+ return self ._repr ({"loc" : self ._loc , "scale" : self ._scale })
470+
471+ def _pdf_untransformed_untruncated (self , x ) -> np .ndarray | float :
472+ return cauchy .pdf (x , loc = self ._loc , scale = self ._scale )
473+
474+ def _cdf_untransformed_untruncated (self , x ) -> np .ndarray | float :
475+ return cauchy .cdf (x , loc = self ._loc , scale = self ._scale )
476+
477+ def _ppf_untransformed_untruncated (self , q ) -> np .ndarray | float :
478+ return cauchy .ppf (q , loc = self ._loc , scale = self ._scale )
479+
480+ @property
481+ def loc (self ) -> float :
482+ """The location parameter of the underlying distribution."""
483+ return self ._loc
484+
485+ @property
486+ def scale (self ) -> float :
487+ """The scale parameter of the underlying distribution."""
488+ return self ._scale
489+
490+
491+ class ChiSquare (Distribution ):
492+ """A chi-squared distribution.
493+
494+ :param dof: Degrees of freedom.
495+ """
496+
497+ def __init__ (
498+ self ,
499+ dof : int ,
500+ trunc : tuple [float , float ] | None = None ,
501+ ):
502+ if not dof .is_integer () or dof < 1 :
503+ raise ValueError (
504+ f"`dof' must be a positive integer, but was `{ dof } '."
505+ )
506+
507+ self ._dof = dof
508+ super ().__init__ (log = False , trunc = trunc )
509+
510+ def __repr__ (self ):
511+ return self ._repr ({"dof" : self ._dof })
512+
513+ def _pdf_untransformed_untruncated (self , x ) -> np .ndarray | float :
514+ return chi2 .pdf (x , df = self ._dof )
515+
516+ def _cdf_untransformed_untruncated (self , x ) -> np .ndarray | float :
517+ return chi2 .cdf (x , df = self ._dof )
518+
519+ def _ppf_untransformed_untruncated (self , q ) -> np .ndarray | float :
520+ return chi2 .ppf (q , df = self ._dof )
521+
522+ @property
523+ def dof (self ) -> int :
524+ """The degrees of freedom parameter."""
525+ return self ._dof
526+
527+
528+ class Exponential (Distribution ):
529+ """
530+ An exponential distribution.
531+ """
532+
533+ def __init__ (
534+ self ,
535+ scale : float ,
536+ trunc : tuple [float , float ] | None = None ,
537+ ):
538+ self ._scale = scale
539+ super ().__init__ (log = False , trunc = trunc )
540+
541+ def __repr__ (self ):
542+ return self ._repr ({"scale" : self ._scale })
543+
544+ def _pdf_untransformed_untruncated (self , x ) -> np .ndarray | float :
545+ return expon .pdf (x , scale = self ._scale )
546+
547+ def _cdf_untransformed_untruncated (self , x ) -> np .ndarray | float :
548+ return expon .cdf (x , scale = self ._scale )
549+
550+ def _ppf_untransformed_untruncated (self , q ) -> np .ndarray | float :
551+ return expon .ppf (q , scale = self ._scale )
552+
553+ @property
554+ def scale (self ) -> float :
555+ """The scale parameter of the underlying distribution."""
556+ return self ._scale
557+
558+
559+ class Gamma (Distribution ):
560+ """A gamma distribution."""
561+
562+ def __init__ (
563+ self ,
564+ shape : float ,
565+ scale : float ,
566+ trunc : tuple [float , float ] | None = None ,
567+ ):
568+ self ._shape = shape
569+ self ._scale = scale
570+ super ().__init__ (log = False , trunc = trunc )
571+
572+ def __repr__ (self ):
573+ return self ._repr ({"shape" : self ._shape , "scale" : self ._scale })
574+
575+ def _pdf_untransformed_untruncated (self , x ) -> np .ndarray | float :
576+ return gamma .pdf (x , a = self ._shape , scale = self ._scale )
577+
578+ def _cdf_untransformed_untruncated (self , x ) -> np .ndarray | float :
579+ return gamma .cdf (x , a = self ._shape , scale = self ._scale )
580+
581+ def _ppf_untransformed_untruncated (self , q ) -> np .ndarray | float :
582+ return gamma .ppf (q , a = self ._shape , scale = self ._scale )
583+
584+ @property
585+ def shape (self ) -> float :
586+ """The shape parameter of the underlying distribution."""
587+ return self ._shape
588+
589+ @property
590+ def scale (self ) -> float :
591+ """The scale parameter of the underlying distribution."""
592+ return self ._scale
593+
594+
595+ class Rayleigh (Distribution ):
596+ """A Rayleigh distribution."""
597+
598+ def __init__ (
599+ self ,
600+ scale : float ,
601+ trunc : tuple [float , float ] | None = None ,
602+ ):
603+ self ._scale = scale
604+ super ().__init__ (log = False , trunc = trunc )
605+
606+ def __repr__ (self ):
607+ return self ._repr ({"scale" : self ._scale })
608+
609+ def _pdf_untransformed_untruncated (self , x ) -> np .ndarray | float :
610+ return rayleigh .pdf (x , scale = self ._scale )
611+
612+ def _cdf_untransformed_untruncated (self , x ) -> np .ndarray | float :
613+ return rayleigh .cdf (x , scale = self ._scale )
614+
615+ def _ppf_untransformed_untruncated (self , q ) -> np .ndarray | float :
616+ return rayleigh .ppf (q , scale = self ._scale )
617+
618+ @property
619+ def scale (self ) -> float :
620+ """The scale parameter of the underlying distribution."""
621+ return self ._scale
0 commit comments