def generateData_mean(numSamples, numDim, Xdist, bX_pareto=10):
    pareto_obj = sp.pareto(b=bX_pareto)
    b_mean, b_var, b_skew, b_kurt = sp.pareto.stats(b=bX_pareto,
                                                    moments="mvsk")
    Xsamples = (pareto_obj.rvs(size=(numSamples, numDim)) -
                b_mean) / np.sqrt(b_var)
    return Xsamples
Exemple #2
0
    def _draw_fire_development(self):  # {{{
        ''' 
        Generate fire. Alpha t square on the left, then constant in the
        middle, then fading on the right. At the end read hrrs at given times.
        '''

        hrrpua_d = self.conf['hrrpua']
        hrr_alpha = self.conf['hrr_alpha']
        '''
        Fire area is draw from pareto distrubution regarding the BS PD-7974-7. 
        There is lack of vent condition - underventilated fires
        '''
        p = pareto(b=0.668, scale=0.775)
        fire_area = p.rvs(size=1)[0]
        fire_origin = self.fire_origin
        orig_area = self.s.query(
            "SELECT (width * depth)/10000 as area FROM aamks_geom WHERE name='{}'"
            .format(fire_origin[0]))[0]['area']

        if fire_area > orig_area:
            fire_area = orig_area

        self.hrrpua = int(
            triangular(hrrpua_d['min'], hrrpua_d['mode'], hrrpua_d['max']))
        hrr_peak = int(self.hrrpua * 1000 * fire_area)
        self.alpha = int(
            triangular(hrr_alpha['min'], hrr_alpha['mode'], hrr_alpha['max']) *
            1000)

        self._psql_log_variable('hrrpeak', hrr_peak / 1000)
        self._psql_log_variable('alpha', self.alpha / 1000.0)

        # left
        t_up_to_hrr_peak = int((hrr_peak / self.alpha)**0.5)
        interval = int(round(t_up_to_hrr_peak / 10))
        times0 = list(range(0, t_up_to_hrr_peak,
                            interval)) + [t_up_to_hrr_peak]
        hrrs0 = [int((self.alpha * t**2)) for t in times0]

        # middle
        t_up_to_starts_dropping = 15 * 60
        times1 = [t_up_to_starts_dropping]
        hrrs1 = [hrr_peak]

        # right
        t_up_to_drops_to_zero = t_up_to_starts_dropping + t_up_to_hrr_peak
        interval = int(
            round((t_up_to_drops_to_zero - t_up_to_starts_dropping) / 10))
        times2 = list(
            range(t_up_to_starts_dropping, t_up_to_drops_to_zero,
                  interval)) + [t_up_to_drops_to_zero]
        hrrs2 = [
            int((self.alpha * (t - t_up_to_drops_to_zero)**2)) for t in times2
        ]

        times = list(times0 + times1 + times2)
        hrrs = list(hrrs0 + hrrs1 + hrrs2)

        return times, hrrs
Exemple #3
0
class WebUserLocust(HttpUser):
    weight = 2
    tasks = [UserTestSet]
    pareto_obj = pareto(b=1.4, scale=1)
    pareto_obj.random_state = 2

    def wait_time(self):
        return self.pareto_obj.rvs()
    def std(self):
        """
        Standard deviation of the posterior distribution.

        Returns
        -------
        std : float
        """
        return stats.pareto(b=self._shape_posterior,
                            scale=self._scale_posterior).std()
    def var(self):
        """
        Variance of the posterior distribution.

        Returns
        -------
        var : float
        """
        return stats.pareto(b=self._shape_posterior,
                            scale=self._scale_posterior).var()
    def mean(self):
        """
        Mean of the posterior distribution.

        Returns
        -------
        mean : float
        """
        return stats.pareto(b=self._shape_posterior,
                            scale=self._scale_posterior).mean()
def pareto_square_error(b, loc, scale):

    distribution = pareto(b=b, loc=loc, scale=scale)

    square_errors = [
        np.power(mean - distribution.mean(), 2.0),
        np.power(lejp - distribution.ppf(0.9), 2.0),
        np.power(uejp - distribution.ppf(0.975), 2.0)
    ]

    return square_errors
Exemple #8
0
def scipy_pareto_sequence(n,exponent=1.0):
    """
    Return sample sequence of length n from a Pareto distribution.
    """
    try: 
        import scipy.stats as stats
    except ImportError:
        print("Import error: not able to import scipy")
        return
    random._inst = random.Random()
    stats.seed(random.randint(1,2**30),random.randint(1,2**30))
    return stats.pareto(exponent,size=n)
Exemple #9
0
def scipy_powerlaw_sequence(n, exponent=2.0):
    """
    Return sample sequence of length n from a power law distribution.

    """
    try:
        import scipy.stats as stats
    except ImportError:
        print "Import error: not able to import scipy"
        return
    random._inst = random.Random()
    stats.seed(random.randint(1, 2**30), random.randint(1, 2**30))
    return stats.pareto(exponent - 1, size=n)
Exemple #10
0
def draw_pareto_changing_b(b_set, num_classes, max=1000, min=1, head=0.0, tail=0.99, save_name='./pareto_ref.jpg'):
    fig, ax = plt.subplots(1, 1)
    classes = np.linspace(0, num_classes, 10*num_classes)
    for i, b in enumerate(b_set):
        rv = pareto(b)
        classes_x = np.linspace(pareto.ppf(head, b), pareto.ppf(tail, b), 10*num_classes)
        dist = rv.pdf(classes_x) * (max-min) / b + min
        ax.plot(classes, dist, label='alpha={}'.format(b))
        plt.legend()
    ax.set_xlabel('sorted class index')
    ax.set_ylabel('sample numbers')

    _savefig(save_name)
Exemple #11
0
def pareto_dist(b, num_classes, max, min=1, tail=0.99, display=False):
    """ generate a pareto distribution reference dist
    """
    rv = pareto(b)
    classes = range(num_classes)
    classes_x = np.linspace(pareto.ppf(0.0, b), pareto.ppf(tail, b), num_classes)
    dist = rv.pdf(classes_x) * (max-min) / b + min
    dist  = dist.astype(int)
    if display:
        fig, ax = plt.subplots(1, 1)
        ax.bar(classes, dist)
        plt.savefig('./data/longtail/refer_num{:d}_b{:d}_max{:d}-min{:d}.jpg'.format(num_classes, b, max, min))
    return dist
Exemple #12
0
def generateData(
    numSamples, numDim, Xdist, noiseDist, noiseVar, tparam, bX_pareto=10, bNoise_pareto=10
):
    if Xdist == "gaussian":
        Xrv = sp.multivariate_normal(mean=np.zeros(numDim), cov=np.eye(numDim))
        Xsamples = Xrv.rvs(numSamples)
    elif Xdist == "pareto":
        pareto_obj = sp.pareto(b=bX_pareto)
        b_mean, b_var, b_skew, b_kurt = sp.pareto.stats(b=bX_pareto, moments="mvsk")
        Xsamples = (pareto_obj.rvs(size=(numSamples, numDim)) - b_mean) / np.sqrt(b_var)

    if noiseDist == "gaussian":
        noise_rv = sp.norm(loc=0, scale=np.sqrt(noiseVar))
        noiseVec = noise_rv.rvs(numSamples)
    elif noiseDist == "pareto":
        noise_rv = sp.pareto(b=bNoise_pareto)
        b_mean, b_var, b_skew, b_kurt = sp.pareto.stats(b=bNoise_pareto, moments="mvsk")
        noiseVec = np.sqrt(noiseVar) * (noise_rv.rvs(size=numSamples) - b_mean) / np.sqrt(b_var)

    response = np.dot(Xsamples, tparam) + noiseVec

    return Xsamples, response
def pareto(alpha):
    """ Create a frozen pareto distribution

    Parameters
    ----------
    alpha : float

    Returns
    -------
    scipy.stats.pareto

    """

    return stats.pareto(**process_args.pareto(alpha=alpha))
Exemple #14
0
def draw_pareto_changing_maxmin(b, num_classes, max=[1000, 500], min=[50, 10], head=0.0, tail=0.99, save_name='./pareto_ref.jpg'):
    fig, ax = plt.subplots(1, 1)
    classes = np.linspace(0, num_classes, 10*num_classes)
    for i, (max_c, min_c) in enumerate(zip(max, min)):
        rv = pareto(b)
        classes_x = np.linspace(pareto.ppf(head, b), pareto.ppf(tail, b), 10*num_classes)
        dist = rv.pdf(classes_x) / b * (max_c-min_c) + min_c
        ax.plot(classes, dist, label='{}'.format(i))
        plt.legend()
    ax.set_ylim(0, np.max(max)+50)
    ax.set_xlabel('sorted class index')
    ax.set_ylabel('sample numbers')

    _savefig(save_name)
Exemple #15
0
 def __init__(self, shape_parameter):
     self.shape_parameter = shape_parameter
     if self.shape_parameter is not None:
         self.bounds = np.array([0.999, np.inf])
         if self.shape_parameter > 0:
             mean, var, skew, kurt = pareto.stats(self.shape_parameter,
                                                  moments='mvsk')
             self.parent = pareto(self.shape_parameter)
             self.mean = mean
             self.variance = var
             self.skewness = skew
             self.kurtosis = kurt
             self.x_range_for_pdf = np.linspace(0.999,
                                                20.0 + shape_parameter,
                                                RECURRENCE_PDF_SAMPLES)
Exemple #16
0
def pareto_junk(m=10000, n=1, b=0.5):
    s = ss.pareto(b).rvs(m)
    a = np.empty((m, n))
    for i in range(n):
        a[:, i] = pd.Series(
            nr.permutation(s)).expanding(min_periods=100).mean().values
    d = pd.DataFrame(a)
    figure(1)
    clf()
    ax = gca()
    # nn = m // 200
    # d.loc[::nn].plot(ax=ax)
    d.plot(ax=ax)
    show()
    globals().update(locals())
    def _expected_value_max_mlhs(self, variants, mlhs_samples):
        """Compute expected value of the maximum of gamma random variables."""
        r = np.arange(1, mlhs_samples + 1)
        np.random.shuffle(r)
        v = (r - 0.5) / mlhs_samples
        v = v[..., np.newaxis]

        variant_params = [(self.models[v].shape_posterior,
                           self.models[v].scale_posterior) for v in variants]

        n = len(variant_params)
        aa, bb = map(np.array, zip(*variant_params))
        cc = aa * bb / (aa - 1)

        xx = stats.pareto(b=aa - 1, scale=bb).ppf(v)

        return np.sum([
            cc[i] * np.prod([
                stats.pareto(b=aa[j], scale=bb[j]).cdf(xx[:, i])
                for j in range(n) if j != i
            ],
                            axis=0) for i in range(n)
        ],
                      axis=0).mean()
Exemple #18
0
def square_error_pareto(alpha):

    from scipy.stats import pareto

    distribution = pareto(b=alpha)

    square_errors = [
        np.power(mean - distribution.mean(), 2.0) * mean_error_weight,
        np.power(lejp - distribution.ppf(percentile_lower), 2.0) *
        lejp_error_weight,
        np.power(uejp - distribution.ppf(percentile_upper), 2.0) *
        uejp_error_weight
    ]

    return square_errors
    def pdf(self, x):
        """
        Probability density function of the posterior distribution.

        Parameters
        ----------
        x : array-like
            Quantiles.

        Returns
        -------
        pdf : numpy.ndarray
           Probability density function evaluated at x.
        """
        return stats.pareto(b=self._shape_posterior,
                            scale=self._scale_posterior).pdf(x)
    def cdf(self, x):
        """
        Cumulative distribution function of the posterior distribution.

        Parameters
        ----------
        x : array-like
            Quantiles.

        Returns
        -------
        cdf : numpy.ndarray
            Cumulative distribution function evaluated at x.
        """
        return stats.pareto(b=self._shape_posterior,
                            scale=self._scale_posterior).cdf(x)
    def ppf(self, q):
        """
        Percent point function (quantile) of the posterior distribution.

        Parameters
        ----------
        x : array-like
            Lower tail probability.

        Returns
        -------
        ppf : numpy.ndarray
            Quantile corresponding to the lower tail probability q.
        """
        return stats.pareto(b=self._shape_posterior,
                            scale=self._scale_posterior).ppf(q)
Exemple #22
0
    def __init__(self, shape_parameter):
        if shape_parameter is None:
            self.shape_parameter = 1.0
        else:
            self.shape_parameter = shape_parameter

        self.bounds = np.array([0.999, np.inf])
        if self.shape_parameter < 0:
            raise ValueError(
                'Invalid parameters in Pareto distribution. Scale should be positive.'
            )
        self.parent = pareto(self.shape_parameter)
        self.mean, self.variance, self.skewness, self.kurtosis = self.parent.stats(
            moments='mvsk')
        self.x_range_for_pdf = np.linspace(0.999, self.shape_parameter + 20.0,
                                           RECURRENCE_PDF_SAMPLES)
Exemple #23
0
    def filtered_pareto(self):
        pct_values = np.arange(self.filtered_data()['Impact Hours'].min(),
                               self.augmented_data()['Impact Hours'].max())
        cum_dist_values = [self.cum_dist(p) for p in pct_values]

        pareto_rv = ss.pareto(self.pareto_beta)
        pareto = [pareto_rv.cdf(p) for p in range(len(pct_values))]

        distributions = pd.DataFrame(
            zip(cum_dist_values, pareto),
            columns=[
                'IH Cumulative Distribution',
                f'Pareto Distribution beta={self.pareto_beta}'
            ])

        return distributions.hvplot.line().opts(
            hv.opts.VLine(color='red')).opts(shared_axes=False)
Exemple #24
0
 def sample(self, rand_seed, N):
     """
     Samples N observations with given random seed
     """
     np.random.seed(rand_seed)
     if self.typ == "Normal":
         mu = self.params["mu"]
         Sigma = self.params["Sigma"]
         if self.d > 1:
             Sigma_half = compute_sqrt(Sigma)
             traj = np.random.randn(N, self.d)
             traj = traj.dot(Sigma_half)
         else:  #1-dimensional example
             sigma_half = np.sqrt(Sigma)
             traj = sigma_half * np.random.randn(N, 1)
         traj += mu.reshape((1, self.d))
         traj_grad = self.gradpotential(traj)
     elif self.typ == "Laplace":
         mu = self.params["mu"]
         l = self.params["lambda"]
         traj = np.random.laplace(loc=mu, scale=l, size=(N, self.d))
         traj_grad = self.gradpotential(traj)
     elif self.typ == "Cauchy":
         traj = np.random.standard_cauchy((N, self.d))
         traj_grad = self.gradpotential(traj)
     elif self.typ == "Pareto":
         b = self.params["b"]
         rv = spstats.pareto(b)
         traj = rv.rvs(size=(N, self.d))
         traj_grad = self.gradpotential(traj)
     elif self.typ == "3rd_poly":
         #here we will use implicitly the generation by inverse cdf
         traj = np.random.rand(N, self.d)
         traj = np.sqrt(np.abs(np.tan(np.pi *
                                      (traj - 0.5)))) * np.sign(traj - 0.5)
         traj_grad = self.gradpotential(traj)
     elif self.typ == "Poly":
         sample_class = poly_dens()
         traj = sample_class.rvs(size=(N, self.d))
         traj_grad = self.gradpotential(traj)
     else:
         raise "Not implemented error in IndependentPotential::sample"
     return traj, traj_grad
    def rvs(self, size=1, random_state=None):
        """
        Random variates of the posterior distribution.

        Parameters
        ----------
        size : int (default=1)
            Number of random variates.

        random_state : int or None (default=None)
            The seed used by the random number generator.

        Returns
        -------
        rvs : numpy.ndarray or scalar
            Random variates of given size.
        """
        return stats.pareto(b=self._shape_posterior,
                            scale=self._scale_posterior).rvs(
                                size=size, random_state=random_state)
Exemple #26
0
    def __init__(self, alpha, beta):
        """
        Parameters
        ----------
        alpha : float, positive
            Scale parameter
        beta : float, positive
            Shape parameter
        """
        assert alpha > 0 and beta > 0, \
                "alpha and beta parameters must be positive"

        # Parameters
        self.alpha = alpha
        self.beta = beta

        # Scipy backend
        self.sp = pareto(b=beta, scale=alpha)

        # Initialize super
        super().__init__()
Exemple #27
0
dens_cvml = KDEMultivariate(data=[support], var_type='c', bw='cv_ml')

plt.figure(2)
plt.plot(support[ix], rv.pdf(support[ix]), label='Actual')
plt.plot(support[ix], dens_normal.pdf()[ix], label='Scott')
plt.plot(support[ix], dens_cvls.pdf()[ix], label='CV_LS')
plt.plot(support[ix], dens_cvml.pdf()[ix], label='CV_ML')
plt.title("Nonparametric Estimation of the Density of f Distributed " \
          "Random Variable")
plt.legend(('Actual', 'Scott', 'CV_LS', 'CV_ML'))

# Pareto distribution
a = 2
nobs = 150
support = np.random.pareto(a, size=nobs)
rv = stats.pareto(a)
ix = np.argsort(support)

dens_normal = KDEMultivariate(data=[support], var_type='c', bw='normal_reference')
dens_cvls = KDEMultivariate(data=[support], var_type='c', bw='cv_ls')
dens_cvml = KDEMultivariate(data=[support], var_type='c', bw='cv_ml')
plt.figure(3)
plt.plot(support[ix], rv.pdf(support[ix]), label='Actual')
plt.plot(support[ix], dens_normal.pdf()[ix], label='Scott')
plt.plot(support[ix], dens_cvls.pdf()[ix], label='CV_LS')
plt.plot(support[ix], dens_cvml.pdf()[ix], label='CV_ML')
plt.title("Nonparametric Estimation of the Density of Pareto " \
          "Distributed Random Variable")
plt.legend(('Actual', 'Scott', 'CV_LS', 'CV_ML'))

# Laplace Distribution
Exemple #28
0
    lambda scale: osp.halfnorm(scale=scale),
    dist.InverseGamma:
    lambda conc, rate: osp.invgamma(conc, scale=rate),
    dist.LogNormal:
    lambda loc, scale: osp.lognorm(s=scale, scale=np.exp(loc)),
    dist.MultinomialProbs:
    lambda probs, total_count: osp.multinomial(n=total_count, p=probs),
    dist.MultinomialLogits:
    lambda logits, total_count: osp.multinomial(n=total_count,
                                                p=_to_probs_multinom(logits)),
    dist.MultivariateNormal:
    _mvn_to_scipy,
    dist.Normal:
    lambda loc, scale: osp.norm(loc=loc, scale=scale),
    dist.Pareto:
    lambda alpha, scale: osp.pareto(alpha, scale=scale),
    dist.Poisson:
    lambda rate: osp.poisson(rate),
    dist.StudentT:
    lambda df, loc, scale: osp.t(df=df, loc=loc, scale=scale),
    dist.Uniform:
    lambda a, b: osp.uniform(a, b - a),
}

CONTINUOUS = [
    T(dist.Beta, 1., 2.),
    T(dist.Beta, 1., np.array([2., 2.])),
    T(dist.Beta, 1., np.array([[1., 1.], [2., 2.]])),
    T(dist.Chi2, 2.),
    T(dist.Chi2, np.array([0.3, 1.3])),
    T(dist.Cauchy, 0., 1.),
diffs.hist(bins='auto')

######################################################################

ecdf = ECDF(diffs)
days, eucdf = ecdf.x, 1 - ecdf.y
plt.loglog(days, eucdf)
plt.xlabel('Days between murders')
plt.ylabel('Cumulative probability')

######################################################################
# Let's fit a powerlaw
#

pareto_idx, pareto_loc, pareto_scale = stats.pareto.fit(diffs)
pareto = stats.pareto(pareto_idx, pareto_loc, pareto_scale)

_ = bootstrap_fit(stats.pareto, diffs, n_iter=100)

######################################################################
# And a lognormal, because Gauss is not mocked.
#

lognorm_sig, lognorm_shape, lognorm_scale = stats.lognorm.fit(diffs)
lognorm = stats.lognorm(lognorm_sig, lognorm_shape, lognorm_scale)

_ = bootstrap_fit(stats.lognorm, diffs, n_iter=100)

######################################################################

plt.loglog(
Exemple #30
0
 def _scipy_pareto(self, concentration, scale):
   # In scipy pareto is defined with scale = 1, so we need to scale.
   return stats.pareto(concentration, scale=scale)
Exemple #31
0
 def _scipy_pareto(self, concentration, scale):
   # In scipy pareto is defined with scale = 1, so we need to scale.
   return stats.pareto(concentration, scale=scale)
class zhu_dist(rv_continuous):        
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.normalize, _ = quad(zhu_pdf, self.a, self.b)

    def _pdf(self, m):
        return zhu_pdf(m) / self.normalize  
    

a_dist = alsing_dist(a=1.1,b=2.8)
f_dist = farrow_dist(a=1.1,b=2.8)
z_dist = zhu_dist(a=2.8,b=25)
ns_astro_mass_dist = ss.norm(1.33, 0.09)
bh_astro_mass_dist = ss.pareto(b=1.3)

def calc_mej_from_masses(m1, m2, thetas, Type, Type_set, EOS):
    '''
    '''
    
    #if i%10 == 0:
    #    print(f'{i} out of {mass_draws} mej values calculated--------------------------')

    #print(f'{i} out of {mass_draws} mej values calculated--------------------------')    

    #m1m = m1[i]
    #m2m = m2[i]
    m1m = m1
    m2m = m2
    samples = run_EOS(EOS, m1m, m2m, thetas, N_EOS = N_EOS, type_set=Type)
    dist.Cauchy: lambda loc, scale: osp.cauchy(loc=loc, scale=scale),
    dist.Chi2: lambda df: osp.chi2(df),
    dist.Dirichlet: lambda conc: osp.dirichlet(conc),
    dist.Exponential: lambda rate: osp.expon(scale=np.reciprocal(rate)),
    dist.Gamma: lambda conc, rate: osp.gamma(conc, scale=1./rate),
    dist.HalfCauchy: lambda scale: osp.halfcauchy(scale=scale),
    dist.HalfNormal: lambda scale: osp.halfnorm(scale=scale),
    dist.InverseGamma: lambda conc, rate: osp.invgamma(conc, scale=rate),
    dist.LogNormal: lambda loc, scale: osp.lognorm(s=scale, scale=np.exp(loc)),
    dist.MultinomialProbs: lambda probs, total_count: osp.multinomial(n=total_count, p=probs),
    dist.MultinomialLogits: lambda logits, total_count: osp.multinomial(n=total_count,
                                                                        p=_to_probs_multinom(logits)),
    dist.MultivariateNormal: _mvn_to_scipy,
    dist.LowRankMultivariateNormal: _lowrank_mvn_to_scipy,
    dist.Normal: lambda loc, scale: osp.norm(loc=loc, scale=scale),
    dist.Pareto: lambda alpha, scale: osp.pareto(alpha, scale=scale),
    dist.Poisson: lambda rate: osp.poisson(rate),
    dist.StudentT: lambda df, loc, scale: osp.t(df=df, loc=loc, scale=scale),
    dist.Uniform: lambda a, b: osp.uniform(a, b - a),
}


CONTINUOUS = [
    T(dist.Beta, 1., 2.),
    T(dist.Beta, 1., np.array([2., 2.])),
    T(dist.Beta, 1., np.array([[1., 1.], [2., 2.]])),
    T(dist.Chi2, 2.),
    T(dist.Chi2, np.array([0.3, 1.3])),
    T(dist.Cauchy, 0., 1.),
    T(dist.Cauchy, 0., np.array([1., 2.])),
    T(dist.Cauchy, np.array([0., 1.]), np.array([[1.], [2.]])),
Exemple #34
0
def main(args=None):
    from ligo.lw import lsctables
    from ligo.lw import utils as ligolw_utils
    from ligo.lw import ligolw
    import lal.series
    from scipy import stats

    p = parser()
    args = p.parse_args(args)

    xmldoc = ligolw.Document()
    xmlroot = xmldoc.appendChild(ligolw.LIGO_LW())
    process = register_to_xmldoc(xmldoc, p, args)

    gwcosmo = GWCosmo(
        cosmology.default_cosmology.get_cosmology_from_string(args.cosmology))

    ns_mass_min = 1.0
    ns_mass_max = 2.0
    bh_mass_min = 5.0
    bh_mass_max = 50.0

    ns_astro_spin_min = -0.05
    ns_astro_spin_max = +0.05
    ns_astro_mass_dist = stats.norm(1.33, 0.09)
    ns_astro_spin_dist = stats.uniform(ns_astro_spin_min,
                                       ns_astro_spin_max - ns_astro_spin_min)

    ns_broad_spin_min = -0.4
    ns_broad_spin_max = +0.4
    ns_broad_mass_dist = stats.uniform(ns_mass_min, ns_mass_max - ns_mass_min)
    ns_broad_spin_dist = stats.uniform(ns_broad_spin_min,
                                       ns_broad_spin_max - ns_broad_spin_min)

    bh_astro_spin_min = -0.99
    bh_astro_spin_max = +0.99
    bh_astro_mass_dist = stats.pareto(b=1.3)
    bh_astro_spin_dist = stats.uniform(bh_astro_spin_min,
                                       bh_astro_spin_max - bh_astro_spin_min)

    bh_broad_spin_min = -0.99
    bh_broad_spin_max = +0.99
    bh_broad_mass_dist = stats.reciprocal(bh_mass_min, bh_mass_max)
    bh_broad_spin_dist = stats.uniform(bh_broad_spin_min,
                                       bh_broad_spin_max - bh_broad_spin_min)

    if args.distribution.startswith('bns_'):
        m1_min = m2_min = ns_mass_min
        m1_max = m2_max = ns_mass_max
        if args.distribution.endswith('_astro'):
            x1_min = x2_min = ns_astro_spin_min
            x1_max = x2_max = ns_astro_spin_max
            m1_dist = m2_dist = ns_astro_mass_dist
            x1_dist = x2_dist = ns_astro_spin_dist
        elif args.distribution.endswith('_broad'):
            x1_min = x2_min = ns_broad_spin_min
            x1_max = x2_max = ns_broad_spin_max
            m1_dist = m2_dist = ns_broad_mass_dist
            x1_dist = x2_dist = ns_broad_spin_dist
        else:  # pragma: no cover
            assert_not_reached()
    elif args.distribution.startswith('nsbh_'):
        m1_min = bh_mass_min
        m1_max = bh_mass_max
        m2_min = ns_mass_min
        m2_max = ns_mass_max
        if args.distribution.endswith('_astro'):
            x1_min = bh_astro_spin_min
            x1_max = bh_astro_spin_max
            x2_min = ns_astro_spin_min
            x2_max = ns_astro_spin_max
            m1_dist = bh_astro_mass_dist
            m2_dist = ns_astro_mass_dist
            x1_dist = bh_astro_spin_dist
            x2_dist = ns_astro_spin_dist
        elif args.distribution.endswith('_broad'):
            x1_min = bh_broad_spin_min
            x1_max = bh_broad_spin_max
            x2_min = ns_broad_spin_min
            x2_max = ns_broad_spin_max
            m1_dist = bh_broad_mass_dist
            m2_dist = ns_broad_mass_dist
            x1_dist = bh_broad_spin_dist
            x2_dist = ns_broad_spin_dist
        else:  # pragma: no cover
            assert_not_reached()
    elif args.distribution.startswith('bbh_'):
        m1_min = m2_min = bh_mass_min
        m1_max = m2_max = bh_mass_max
        if args.distribution.endswith('_astro'):
            x1_min = x2_min = bh_astro_spin_min
            x1_max = x2_max = bh_astro_spin_max
            m1_dist = m2_dist = bh_astro_mass_dist
            x1_dist = x2_dist = bh_astro_spin_dist
        elif args.distribution.endswith('_broad'):
            x1_min = x2_min = bh_broad_spin_min
            x1_max = x2_max = bh_broad_spin_max
            m1_dist = m2_dist = bh_broad_mass_dist
            x1_dist = x2_dist = bh_broad_spin_dist
        else:  # pragma: no cover
            assert_not_reached()
    else:  # pragma: no cover
        assert_not_reached()

    dists = (m1_dist, m2_dist, x1_dist, x2_dist)

    # Read PSDs
    psds = list(
        lal.series.read_psd_xmldoc(
            ligolw_utils.load_fileobj(
                args.reference_psd,
                contenthandler=lal.series.PSDContentHandler)).values())

    # Construct mass1, mass2, spin1z, spin2z grid.
    m1 = np.geomspace(m1_min, m1_max, 10)
    m2 = np.geomspace(m2_min, m2_max, 10)
    x1 = np.linspace(x1_min, x1_max, 10)
    x2 = np.linspace(x2_min, x2_max, 10)
    params = m1, m2, x1, x2

    # Calculate the maximum distance on the grid.
    max_z = gwcosmo.get_max_z(psds,
                              args.waveform,
                              args.f_low,
                              args.min_snr,
                              m1,
                              m2,
                              x1,
                              x2,
                              jobs=args.jobs)
    if args.max_distance is not None:
        new_max_z = cosmology.z_at_value(gwcosmo.cosmo.luminosity_distance,
                                         args.max_distance * units.Mpc)
        max_z[max_z > new_max_z] = new_max_z
    max_distance = gwcosmo.sensitive_distance(max_z).to_value(units.Mpc)

    # Find piecewise constant approximate upper bound on distance.
    max_distance = cell_max(max_distance)

    # Calculate V * T in each grid cell
    cdfs = [dist.cdf(param) for param, dist in zip(params, dists)]
    cdf_los = [cdf[:-1] for cdf in cdfs]
    cdfs = [np.diff(cdf) for cdf in cdfs]
    probs = np.prod(np.meshgrid(*cdfs, indexing='ij'), axis=0)
    probs /= probs.sum()
    probs *= 4 / 3 * np.pi * max_distance**3
    volume = probs.sum()
    probs /= volume
    probs = probs.ravel()

    volumetric_rate = args.nsamples / volume * units.year**-1 * units.Mpc**-3

    # Draw random grid cells
    dist = stats.rv_discrete(values=(np.arange(len(probs)), probs))
    indices = np.unravel_index(dist.rvs(size=args.nsamples),
                               max_distance.shape)

    # Draw random intrinsic params from each cell
    cols = {}
    cols['mass1'], cols['mass2'], cols['spin1z'], cols['spin2z'] = [
        dist.ppf(stats.uniform(cdf_lo[i], cdf[i]).rvs(size=args.nsamples))
        for i, dist, cdf_lo, cdf in zip(indices, dists, cdf_los, cdfs)
    ]

    # Swap binary components as needed to ensure that mass1 >= mass2.
    # Note that the .copy() is important.
    # See https://github.com/numpy/numpy/issues/14428
    swap = cols['mass1'] < cols['mass2']
    cols['mass1'][swap], cols['mass2'][swap] = \
        cols['mass2'][swap].copy(), cols['mass1'][swap].copy()
    cols['spin1z'][swap], cols['spin2z'][swap] = \
        cols['spin2z'][swap].copy(), cols['spin1z'][swap].copy()

    # Draw random extrinsic parameters
    cols['distance'] = stats.powerlaw(
        a=3, scale=max_distance[indices]).rvs(size=args.nsamples)
    cols['longitude'] = stats.uniform(0, 2 * np.pi).rvs(size=args.nsamples)
    cols['latitude'] = np.arcsin(stats.uniform(-1, 2).rvs(size=args.nsamples))
    cols['inclination'] = np.arccos(
        stats.uniform(-1, 2).rvs(size=args.nsamples))
    cols['polarization'] = stats.uniform(0, 2 * np.pi).rvs(size=args.nsamples)
    cols['coa_phase'] = stats.uniform(-np.pi,
                                      2 * np.pi).rvs(size=args.nsamples)
    cols['time_geocent'] = stats.uniform(1e9, units.year.to(
        units.second)).rvs(size=args.nsamples)

    # Convert from sensitive distance to redshift and comoving distance.
    # FIXME: Replace this brute-force lookup table with a solver.
    z = np.linspace(0, max_z.max(), 10000)
    ds = gwcosmo.sensitive_distance(z).to_value(units.Mpc)
    dc = gwcosmo.cosmo.comoving_distance(z).to_value(units.Mpc)
    z_for_ds = interp1d(ds, z, kind='cubic', assume_sorted=True)
    dc_for_ds = interp1d(ds, dc, kind='cubic', assume_sorted=True)
    zp1 = 1 + z_for_ds(cols['distance'])
    cols['distance'] = dc_for_ds(cols['distance'])

    # Apply redshift factor to convert from comoving distance and source frame
    # masses to luminosity distance and observer frame masses.
    for key in ['distance', 'mass1', 'mass2']:
        cols[key] *= zp1

    # Populate sim_inspiral table
    sims = xmlroot.appendChild(lsctables.New(lsctables.SimInspiralTable))
    for row in zip(*cols.values()):
        sims.appendRow(**dict(dict.fromkeys(sims.validcolumns, None),
                              process_id=process.process_id,
                              simulation_id=sims.get_next_id(),
                              waveform=args.waveform,
                              f_lower=args.f_low,
                              **dict(zip(cols.keys(), row))))

    # Record process end time.
    process.comment = str(volumetric_rate)
    process.set_end_time_now()

    # Write output file.
    write_fileobj(xmldoc, args.output)
dens_cvml = KDEMultivariate(data=[support], var_type='c', bw='cv_ml')

plt.figure(2)
plt.plot(support[ix], rv.pdf(support[ix]), label='Actual')
plt.plot(support[ix], dens_normal.pdf()[ix], label='Scott')
plt.plot(support[ix], dens_cvls.pdf()[ix], label='CV_LS')
plt.plot(support[ix], dens_cvml.pdf()[ix], label='CV_ML')
plt.title("Nonparametric Estimation of the Density of f Distributed " \
          "Random Variable")
plt.legend(('Actual', 'Scott', 'CV_LS', 'CV_ML'))

# Pareto distribution
a = 2
nobs = 150
support = np.random.pareto(a, size=nobs)
rv = stats.pareto(a)
ix = np.argsort(support)

dens_normal = KDEMultivariate(data=[support],
                              var_type='c',
                              bw='normal_reference')
dens_cvls = KDEMultivariate(data=[support], var_type='c', bw='cv_ls')
dens_cvml = KDEMultivariate(data=[support], var_type='c', bw='cv_ml')
plt.figure(3)
plt.plot(support[ix], rv.pdf(support[ix]), label='Actual')
plt.plot(support[ix], dens_normal.pdf()[ix], label='Scott')
plt.plot(support[ix], dens_cvls.pdf()[ix], label='CV_LS')
plt.plot(support[ix], dens_cvml.pdf()[ix], label='CV_ML')
plt.title("Nonparametric Estimation of the Density of Pareto " \
          "Distributed Random Variable")
plt.legend(('Actual', 'Scott', 'CV_LS', 'CV_ML'))