1"""T-Copula.""" 2import numpy 3from scipy import special 4import chaospy 5 6from ..baseclass import CopulaDistribution, Distribution 7 8 9class t_copula(Distribution): 10 """T-Copula.""" 11 12 def __init__(self, df, covariance, rotation=None): 13 assert isinstance(df, float) 14 covariance = numpy.asarray(covariance) 15 assert covariance.ndim == 2, "Covariance must be a matrix" 16 assert covariance.shape[0] == covariance.shape[1], ( 17 "Parameters 'covariance' not a square matrix.") 18 19 dependencies, _, rotation = chaospy.declare_dependencies( 20 self, 21 parameters=dict(covariance=covariance), 22 rotation=rotation, 23 dependency_type="accumulate", 24 ) 25 correlation = covariance/numpy.sqrt(numpy.outer( 26 numpy.diag(covariance), numpy.diag(covariance))) 27 self._permute = numpy.eye(len(rotation), dtype=int)[rotation] 28 self._correlation = self._permute.dot(correlation).dot(self._permute.T) 29 cholesky = numpy.linalg.cholesky(self._correlation) 30 self._fwd_transform = self._permute.T.dot(numpy.linalg.inv(cholesky)) 31 self._inv_transform = self._permute.T.dot(cholesky) 32 33 super(t_copula, self).__init__( 34 parameters=dict(df=df), 35 dependencies=dependencies, 36 rotation=rotation, 37 repr_args=[covariance.tolist()], 38 ) 39 40 def _cdf(self, xloc, idx, df, cache): 41 dim = self._rotation.index(idx) 42 conditions = [self._get_cache(dim_, cache, get=0) 43 for dim_ in self._rotation[:dim]] 44 assert not any([isinstance(condition, chaospy.Distribution) 45 for condition in conditions]) 46 xloc = numpy.vstack(conditions+[xloc]) 47 zloc = self._fwd_transform[idx, :len(xloc)].dot(special.stdtrit(df, xloc)) 48 out = special.stdtr(df, zloc) 49 return out 50 51 def _ppf(self, qloc, idx, df, cache): 52 dim = self._rotation.index(idx) 53 conditions = [self._get_cache(dim_, cache, get=1) 54 for dim_ in self._rotation[:dim]] 55 assert not any([isinstance(condition, chaospy.Distribution) 56 for condition in conditions]) 57 qloc = numpy.vstack(conditions+[qloc]) 58 zloc = special.stdtrit(df, qloc) 59 out = special.stdtr(df, self._inv_transform[idx, :len(qloc)].dot(zloc)) 60 return out 61 62 def _pdf(self, xloc, idx, df, cache): 63 raise chaospy.UnsupportedFeature("Copula not supported.") 64 65 def _lower(self, idx, df, cache): 66 return 0. 67 68 def _upper(self, idx, df, cache): 69 return 1. 70 71 72class TCopula(CopulaDistribution): 73 """ 74 T-Copula. 75 76 Examples: 77 >>> distribution = chaospy.TCopula( 78 ... chaospy.Iid(chaospy.Uniform(-1, 1), 2), 79 ... df=5, covariance=[[1, .5], [.5, 1]]) 80 >>> distribution 81 TCopula(Iid(Uniform(lower=-1, upper=1), 2), 5.0, [[1.0, 0.5], [0.5, 1.0]]) 82 >>> samples = distribution.sample(3) 83 >>> samples.round(4) 84 array([[ 0.3072, -0.77 , 0.9006], 85 [ 0.1274, 0.3147, 0.1928]]) 86 >>> distribution.pdf(samples).round(4) 87 array([0.2932, 0.1367, 0.1969]) 88 >>> distribution.fwd(samples).round(4) 89 array([[0.6536, 0.115 , 0.9503], 90 [0.4822, 0.8725, 0.2123]]) 91 >>> mesh = numpy.meshgrid([.4, .5, .6], [.4, .5, .6]) 92 >>> distribution.inv(mesh).round(4) 93 array([[[-0.2 , 0. , 0.2 ], 94 [-0.2 , 0. , 0.2 ], 95 [-0.2 , 0. , 0.2 ]], 96 <BLANKLINE> 97 [[-0.2699, -0.1738, -0.0741], 98 [-0.1011, 0. , 0.1011], 99 [ 0.0741, 0.1738, 0.2699]]]) 100 101 """ 102 103 def __init__(self, dist, df, covariance): 104 """ 105 Args: 106 dist (Distribution): 107 The distribution to wrap in a copula. 108 R (numpy.ndarray): 109 Covariance matrix defining dependencies.. 110 df (float): 111 The degree of freedom in the underlying student-t distribution. 112 """ 113 assert len(dist) == len(covariance) 114 df = float(df) 115 covariance = numpy.asfarray(covariance) 116 super(TCopula, self).__init__( 117 dist=dist, 118 trans=t_copula(df, covariance, dist._rotation), 119 repr_args=[dist, df, covariance.tolist()], 120 ) 121