def variance(self): variances = tuple(p.variance() for p in self.parts) means = tuple(p.mean() for p in self.parts) if any(d is None for d in variances) or any(m is None for m in means): return None sq_means = tuple(m * m for m in means) pos = zip(variances, sq_means) return sum(prod(x) for x in product(*pos)) - prod(sq_means)
def probability(self, k) -> Optional[float]: cdf = [p.cumulative_density(k) for p in self.parts] prob = [p.probability(k) for p in self.parts] if any(x is None for x in cdf) or any(x is None for x in prob): return None inv_cdf = [1 - x for x in cdf] prod_inv_cdf = prod(inv_cdf) return sum( (prod_inv_cdf / icdf) * p for (icdf, p) in zip(inv_cdf, prob))
def sample_probability(self, k, sample_size=4096, epsilon=None) -> Optional[float]: cdf = [ p.approx_cumulative_density(k, sample_size=sample_size) for p in self.parts ] prob = [ p.approx_probability(k, sample_size=sample_size, epsilon=epsilon) for p in self.parts ] inv_cdf = [1 - x for x in cdf] prod_inv_cdf = prod(inv_cdf) return sum( (prod_inv_cdf / icdf) * p for (icdf, p) in zip(inv_cdf, prob))
def sample_probability(self, k, **kwargs): discrete_finite_parts = [] discrete_support_spaces = [] other_parts = [] for p in self.parts: ss = p.support_space() if ss is None or not ss.is_finite(): other_parts.append(p) else: discrete_support_spaces.append(ss) discrete_finite_parts.append(p) if not discrete_finite_parts: return super().sample_probability(k, **kwargs) if not other_parts: other_parts = discrete_finite_parts[-1:] del discrete_finite_parts[-1] del discrete_support_spaces[-1] other = self.func(other_parts) total = 0 for possibility in product(*discrete_support_spaces): probs = tuple( part.approx_probability(poss, **kwargs) for (part, poss) in zip(discrete_finite_parts, possibility)) if None in probs: return None poss_prob = prod(probs) if poss_prob == 0: continue req = self.rev_func(k, possibility) if req is None: continue other_prob = other.approx_probability(req, **kwargs) if other_prob is None: return None total += poss_prob * other_prob return total
def probability(self, k): finite_parts = [] finite_spaces = [] other_parts = [] for p in self.parts: ss = p.support_space() if ss is None or not ss.is_finite(): other_parts.append(p) if len(other_parts) >= 2: return None else: finite_spaces.append(ss) finite_parts.append(p) assert finite_parts if not other_parts: other_parts = finite_parts[-1:] del finite_parts[-1] del finite_spaces[-1] other, = other_parts total = 0 for possibility in product(*finite_spaces): probs = tuple( part.probability(poss) for (part, poss) in zip(finite_parts, possibility)) if None in probs: return None poss_prob = prod(probs) if poss_prob == 0: continue req = self.rev_func(k, possibility) if req is None: continue other_prob = other.probability(req) if other_prob is None: return None total += poss_prob * other_prob return total
def func(cls, parts): return prod(parts)
def func(cls, args): return prod(args)
def rev_func(cls, target, parts): if any(p == 0 for p in parts): return None return target / prod(parts)
def __init__(self, parts: Iterable[BufferedDistribution[T]]): ProductDistribution.__init__(self, parts) BufferedDistribution.__init__(self, prod(p.bufferer for p in self.parts))