def psi(self, s): assert not np.isnan(self.params), "Copula must have parameters to calculate psi" s = np.asarray(s) if self.params <= -36: return -log1pexp(-s - self.params) / self.params elif self.params < 0: return -np.log1p(np.exp(-s) * np.expm1(-self.params)) / self.params elif self.params == 0: return np.exp(-s) else: const = log1mexp(self.params) m = np.less(s, const, where=~np.isnan(s)) s[m] = np.nan s[~m] = -log1mexp(s[~m] - log1mexp(self.params)) / self.params return s.item(0) if s.size == 1 else s
def pdf(self, u: Array, log=False): assert not np.isnan(self.params), "Copula must have parameters to calculate parameters" n, d = u.shape theta = self.params ok = valid_rows_in_u(u) res = np.repeat(np.nan, n) u_ = u[ok] u_sum = u_.sum(1) lp = log1mexp(theta) lpu = log1mexp(theta * u_) lu = lpu.sum(1) li_arg = np.exp(lp + (lpu - lp).sum(1)) li = poly_log(li_arg, 1 - d, log=True) res[ok] = (d - 1) * np.log(theta) + li - theta * u_sum - lu return res if log else np.exp(res)
def random(self, n: int, seed: int = None): u = random_uniform(n, self.dim, seed) if abs(self.params) < 1e-7: return u if self.dim == 2: v = u[:, 1] a = -abs(self.params) v = -1 / a * np.log1p(-v * np.expm1(-a) / (np.exp(-a * u[:, 0]) * (v - 1) - v)) u[:, 1] = 1 - v if self.params > 0 else v return u # alpha too large if log1mexp(self.params) == 0: return np.ones((n, self.dim)) fr = random_log_series_ln1p(-self.params, n)[:, None] return self.psi(-np.log(u) / fr)