Source code for copulae.elliptical.gaussian

from typing import Collection, Union

import numpy as np

from copulae.copula import Summary, TailDep
from copulae.elliptical.abstract import AbstractEllipticalCopula
from copulae.stats import multivariate_normal as mvn, norm
from copulae.types import Array
from copulae.utility.annotations import *

try:
    from typing import Literal
except ImportError:
    from typing_extensions import Literal


[docs]class GaussianCopula(AbstractEllipticalCopula[np.ndarray]): r""" The Gaussian (Normal) copula. It is elliptical and symmetric which gives it nice analytical properties. The Gaussian copula is determined entirely by its correlation matrix. Gaussian copulas do not model tail dependencies very well, it's tail is flat. Take not that by symmetry, it gives equal weight to tail scenarios. In English, this means upside scenarios happen as often as downside scenarios. A Gaussian copula is fined as .. math:: C_\Sigma (u_1, \dots, u_d) = \Phi_\Sigma (N^{-1} (u_1), \dots, N^{-1} (u_d)) where :math:`\Sigma` is the covariance matrix which is the parameter of the Gaussian copula and :math:`N^{-1}` is the quantile (inverse cdf) function """ def __init__(self, dim=2): """ Creates a Gaussian copula instance Parameters ---------- dim: int, optional Dimension of the copula """ super().__init__(dim, "Gaussian") n = sum(range(dim)) self._rhos = np.zeros(n) eps = 1e-6 self._bounds = np.repeat(-1., n) - eps, np.repeat(1., n) + eps @validate_data_dim({"x": [1, 2]}) @shape_first_input_to_cop_dim @squeeze_output def cdf(self, x: np.ndarray, log=False): q = norm.ppf(x) sigma = self.sigma return mvn.logcdf(q, cov=sigma) if log else mvn.cdf(q, cov=sigma) @cast_input(['rho']) @squeeze_output def irho(self, rho: Array): return np.sin(np.array(rho) * np.pi / 6) * 2 def lambda_(self): res = (self._rhos == 1).astype(float) return TailDep(res, res) @property def params(self): """The covariance parameters for the Gaussian copula""" return self._rhos @params.setter def params(self, params: Union[float, np.ndarray, Collection[float]]): if isinstance(params, (float, int)): params = np.repeat(params, len(self._rhos)) self._rhos = np.asarray(params) @validate_data_dim({"u": [1, 2]}) @shape_first_input_to_cop_dim @squeeze_output def pdf(self, u: np.ndarray, log=False): sigma = self.sigma q = norm.ppf(u) d = mvn.logpdf(q, cov=sigma) - norm.logpdf(q).sum(1) return d if log else np.exp(d) @cast_output def random(self, n: int, seed: int = None): r = mvn.rvs(cov=self.sigma, size=n, random_state=seed) return norm.cdf(r) @select_summary def summary(self, category: Literal['copula', 'fit'] = 'copula'): return Summary(self, {'Correlation Matrix': self.sigma})