Exemple #1
0
def get_askey_recursion_coefficients_from_variable(var, num_coefs):
    var_name, scales, shapes = get_distribution_info(var)

    if var_name not in askey_variable_names:
        msg = f"Variable name {var_name} not in {askey_variable_names}"
        raise ValueError(msg)

    # Askey polynomials associated with continuous variables
    if var_name == "uniform":
        poly_name, opts = "legendre", {}
    elif var_name == "beta":
        poly_name = "jacobi"
        opts = {"alpha_poly": shapes["b"] - 1, "beta_poly": shapes["a"] - 1}
    elif var_name == "norm":
        poly_name, opts = "hermite", {}
        opts

    # Askey polynomials associated with discrete variables
    elif var_name == "binom":
        poly_name, opts = "krawtchouk", shapes
    elif var_name == "hypergeom":
        # note xk = np.arange(max(0, N-M+n), min(n, N)+1, dtype=float)
        poly_name = "hahn"
        M, n, N = [shapes[key] for key in ["M", "n", "N"]]
        opts = {"alpha_poly": -(n + 1), "beta_poly": -M - 1 + n, "N": N}
    elif var_name == "poisson":
        poly_name, opts = "charlier", shapes

    return get_askey_recursion_coefficients(poly_name, opts, num_coefs)
    def __init__(self, variable, enforce_bounds=False):
        """
        Variable uniquness dependes on both the type of random variable
        e.g. beta, gaussian, etc. and the parameters of that distribution
        e.g. loc and scale parameters as well as any additional parameters
        """
        if (type(variable) != IndependentMultivariateRandomVariable):
            variable = IndependentMultivariateRandomVariable(variable)
        self.variable = variable
        self.enforce_bounds = enforce_bounds
        self.identity_map_indices = None

        self.scale_parameters = np.empty((self.variable.nunique_vars, 2))
        for ii in range(self.variable.nunique_vars):
            var = self.variable.unique_variables[ii]
            name, scale_dict, __ = get_distribution_info(var)
            # copy is essential here because code below modifies scale
            loc, scale = scale_dict['loc'].copy(), scale_dict['scale'].copy()
            if (is_bounded_continuous_variable(var)
                    or (type(var.dist) == float_rv_discrete
                        and var.dist.name != 'discrete_chebyshev')):
                lb, ub = -1, 1
                scale /= (ub - lb)
                loc = loc - scale * lb
            self.scale_parameters[ii, :] = loc, scale
Exemple #3
0
    def test_get_univariate_leja_rule_float_rv_discrete(self):
        nmasses = 20
        xk = np.array(range(1, nmasses + 1), dtype='float')
        pk = np.ones(nmasses) / nmasses
        variable = float_rv_discrete(name='float_rv_discrete',
                                     values=(xk, pk))()
        growth_rule = partial(constant_increment_growth_rule, 2)
        quad_rule = get_univariate_leja_quadrature_rule(variable, growth_rule)
        level = 3
        scales, shapes = get_distribution_info(variable)[1:]
        print(scales)

        x, w = quad_rule(level)
        # x in [-1,1], scales for x in [0,1]
        loc, scale = scales['loc'], scales['scale']
        scale /= 2
        loc = loc + scale
        x = x * scale + loc

        true_moment = (xk**(x.shape[0] - 1)).dot(pk)
        moment = (x**(x.shape[0] - 1)).dot(w[-1])

        #print(moment)
        #print(true_moment)
        assert np.allclose(moment, true_moment)
Exemple #4
0
def continuous_induced_measure_ppf(var,
                                   ab,
                                   ii,
                                   u_samples,
                                   quad_tol=1e-8,
                                   opt_tol=1e-6):
    if is_bounded_continuous_variable(var):
        # canonical domain of bounded variables does not coincide with scipy
        # variable canonical pdf domain with loc,scale=0,1
        lb, ub = -1, 1
        loc, scale = -1, 2
    else:
        lb, ub = var.support()
        loc, scale = 0, 1
    shapes = get_distribution_info(var)[2]

    # need to map x from canonical polynomial domain to canonical domain of pdf
    pdf = lambda x: var.dist._pdf((x - loc) / scale, **shapes) / scale

    def pdf(x):
        vals = var.dist._pdf((x - loc) / scale, **shapes) / scale
        #print('x',x,(x-loc)/scale,vals)
        return vals

    #pdf = var.pdf
    #func = partial(continuous_induced_measure_cdf,pdf,ab,ii,lb,ub,quad_tol)
    from pyapprox.cython.orthonormal_polynomials_1d import\
        continuous_induced_measure_cdf_pyx
    func = partial(continuous_induced_measure_cdf_pyx, pdf, ab, ii, lb,
                   quad_tol)
    method = 'bisect'
    samples = invert_monotone_function(func, [lb, ub], u_samples, method,
                                       opt_tol)
    assert np.all(np.isfinite(samples))
    return samples
def get_discrete_univariate_leja_quadrature_rule(variable, growth_rule):
    var_type, __, shapes = get_distribution_info(variable)
    if var_type == 'binom':
        num_trials = variable_parameters['num_trials']
        prob_success = variable_parameters['prob_success']

        def generate_candidate_samples(num_samples):
            assert num_samples == num_trials + 1
            return np.arange(0, num_trials + 1)[np.newaxis, :]

        recursion_coeffs = krawtchouk_recurrence(num_trials,
                                                 num_trials,
                                                 probability=True)
        quad_rule = partial(candidate_based_christoffel_leja_rule_1d,
                            recursion_coeffs,
                            generate_candidate_samples,
                            num_trials + 1,
                            growth_rule=growth_rule,
                            initial_points=np.atleast_2d([
                                binomial_rv.ppf(0.5, num_trials, prob_success)
                            ]))
    elif var_type == 'float_rv_discrete' or var_type == 'discrete_chebyshev':
        from pyapprox.numerically_generate_orthonormal_polynomials_1d import \
            modified_chebyshev_orthonormal
        from pyapprox.orthonormal_polynomials_1d import \
            discrete_chebyshev_recurrence
        nmasses = shapes['xk'].shape[0]
        if var_type == 'discrete_chebyshev':
            xk = shapes['xk']  #do not map discrete_chebyshev
            assert np.allclose(shapes['xk'], np.arange(nmasses))
            assert np.allclose(shapes['pk'], np.ones(nmasses) / nmasses)
            num_coefs = nmasses
            recursion_coeffs = discrete_chebyshev_recurrence(
                num_coefs, nmasses)
        else:
            #shapes['xk'] will be in [0,1] but canonical domain is [-1,1]
            xk = shapes['xk'] * 2 - 1
            num_coefs = nmasses
            recursion_coeffs = modified_chebyshev_orthonormal(
                num_coefs, [xk, shapes['pk']])

        def generate_candidate_samples(num_samples):
            assert num_samples == nmasses
            return xk[np.newaxis, :]

        # do not specify init_samples in partial or a sparse grid cannot
        # update the samples_1d so that next level has same samples_1d
        # TODO: add test that samples_1d[ii] and samples_1d[ii+1] subset
        # are equal
        #init_samples = np.atleast_2d(np.sort(xk)[nmasses//2])
        quad_rule = partial(candidate_based_christoffel_leja_rule_1d,
                            recursion_coeffs,
                            generate_candidate_samples,
                            nmasses,
                            growth_rule=growth_rule)
    else:
        raise Exception('var_type %s not implemented' % var_type)
    return quad_rule
Exemple #6
0
def get_pymc_variable(rv,pymc_var_name):
    name, scales, shapes = get_distribution_info(rv)
    if rv.dist.name=='norm':
        return pm.Normal(
            pymc_var_name,mu=scales['loc'],sigma=scales['scale'])
    if rv.dist.name=='uniform':        
        return pm.Uniform(pymc_var_name,lower=scales['loc'],
                          upper=scales['loc']+scales['scale'])
    msg = f'Variable type: {name} not supported'
    raise Exception(msg)
def define_poly_options_from_variable_transformation(var_trans):
    pce_opts = {'var_trans':var_trans}
    basis_opts = dict()
    for ii in range(len(var_trans.variable.unique_variables)):
        var = var_trans.variable.unique_variables[ii]
        name, scales, shapes = get_distribution_info(var)
        opts = {'rv_type':name,'shapes':shapes,
                'var_nums':var_trans.variable.unique_variable_indices[ii]}
        basis_opts['basis%d'%ii]=opts
    pce_opts['poly_types']=basis_opts
    return pce_opts
def cvar_beta_variable(rv, beta):
    """
    cvar of Beta variable on [0,1]
    """
    qq = rv.ppf(beta)
    scales, shapes = get_distribution_info(rv)[1:]
    qq = (qq - scales["loc"]) / scales["scale"]
    g1, g2 = shapes["a"], shapes["b"]
    cvar01 = ((1 - betainc(1 + g1, g2, qq)) * gamma_fn(1 + g1) * gamma_fn(g2) /
              gamma_fn(1 + g1 + g2)) / (beta_fn(g1, g2) * (1 - beta))
    return cvar01 * scales["scale"] + scales["loc"]
Exemple #9
0
def define_poly_options_from_variable(variable):
    basis_opts = dict()
    for ii in range(len(variable.unique_variables)):
        var = variable.unique_variables[ii]
        name, scales, shapes = get_distribution_info(var)
        opts = {
            'var': var,
            'shapes': shapes,
            'var_nums': variable.unique_variable_indices[ii]
        }
        basis_opts['basis%d' % ii] = opts
    return basis_opts
Exemple #10
0
def get_univariate_leja_quadrature_rule(
        variable,
        growth_rule,
        method='pdf',
        numerically_generated_poly_accuracy_tolerance=1e-12,
        initial_points=None):

    if not is_continuous_variable(variable):
        return get_discrete_univariate_leja_quadrature_rule(
            variable, growth_rule,
            numerically_generated_poly_accuracy_tolerance=numerically_generated_poly_accuracy_tolerance,
            initial_points=initial_points)

    if method == 'christoffel':
        return partial(
            univariate_christoffel_leja_quadrature_rule, variable, growth_rule,
            numerically_generated_poly_accuracy_tolerance=numerically_generated_poly_accuracy_tolerance, initial_points=initial_points)

    if method == 'pdf':
        return partial(
            univariate_pdf_weighted_leja_quadrature_rule,
            variable, growth_rule,
            numerically_generated_poly_accuracy_tolerance=numerically_generated_poly_accuracy_tolerance,
            initial_points=initial_points)

    assert method == 'deprecated'
    var_name, __, shapes = get_distribution_info(variable)
    if var_name == 'uniform':
        quad_rule = partial(
            beta_leja_quadrature_rule, 1, 1, growth_rule=growth_rule,
            samples_filename=None)
    elif var_name == 'beta':
        quad_rule = partial(
            beta_leja_quadrature_rule, shapes['a'], shapes['b'],
            growth_rule=growth_rule)
    elif var_name == 'norm':
        quad_rule = partial(
            gaussian_leja_quadrature_rule, growth_rule=growth_rule)
    else:
        raise Exception('var_name %s not implemented' % var_name)

    return quad_rule
Exemple #11
0
def get_pdf_weight_functions(variable):
    name, scales, shapes = get_distribution_info(variable)
    if name == 'uniform' or name == 'beta':
        if name == 'uniform':
            alpha_stat, beta_stat = 1, 1
        else:
            alpha_stat, beta_stat = shapes['a'], shapes['b']

        def pdf(x):
            return beta_pdf(alpha_stat, beta_stat, (x+1)/2)/2

        def pdf_jac(x):
            return beta_pdf_derivative(alpha_stat, beta_stat, (x+1)/2)/4
        return pdf, pdf_jac

    if name == 'norm':
        return partial(gaussian_pdf, 0, 1), \
            partial(gaussian_pdf_derivative, 0, 1)

    raise Exception(f'var_type (name) not supported')
Exemple #12
0
    def test_get_univariate_leja_rule_discrete_chebyshev(self):

        nmasses = 20
        xk = np.array(range(0, nmasses), dtype='float')
        pk = np.ones(nmasses) / nmasses
        variable = float_rv_discrete(name='discrete_chebyshev',
                                     values=(xk, pk))()
        growth_rule = partial(constant_increment_growth_rule, 2)
        quad_rule = get_univariate_leja_quadrature_rule(variable, growth_rule)
        level = 3
        scales, shapes = get_distribution_info(variable)[1:]

        x, w = quad_rule(level)

        true_moment = (xk**(x.shape[0] - 1)).dot(pk)
        moment = (x**(x.shape[0] - 1)).dot(w[-1])

        #print(moment)
        #print(true_moment)
        assert np.allclose(moment, true_moment)
Exemple #13
0
def get_discrete_univariate_leja_quadrature_rule(
        variable,
        growth_rule,
        initial_points=None,
        orthonormality_tol=1e-12,
        return_weights_for_all_levels=True,
        recursion_opts=None):
    from pyapprox.variables import get_probability_masses, \
        is_bounded_discrete_variable
    var_name = get_distribution_info(variable)[0]
    if is_bounded_discrete_variable(variable):
        xk, pk = get_probability_masses(variable)
        loc, scale = transform_scale_parameters(variable)
        xk = (xk - loc) / scale

        if initial_points is None:
            initial_points = (np.atleast_2d([variable.ppf(0.5)]) - loc) / scale
        # initial samples must be in canonical space
        assert np.all((initial_points >= -1) & (initial_points <= 1))
        assert np.all((xk >= -1) & (xk <= 1))

        def generate_candidate_samples(num_samples):
            return xk[None, :]

        if recursion_opts is None:
            recursion_opts = {"orthonormality_tol": orthonormality_tol}
        ab = get_recursion_coefficients_from_variable(variable, xk.shape[0],
                                                      recursion_opts)

        quad_rule = partial(
            candidate_based_christoffel_leja_rule_1d,
            ab,
            generate_candidate_samples,
            xk.shape[0],
            growth_rule=growth_rule,
            initial_points=initial_points,
            return_weights_for_all_levels=return_weights_for_all_levels)
        return quad_rule

    raise ValueError('var_name %s not implemented' % var_name)
Exemple #14
0
def get_discrete_univariate_leja_quadrature_rule(variable, growth_rule, initial_points=None, numerically_generated_poly_accuracy_tolerance=1e-12):
    from pyapprox.variables import get_probability_masses, \
        is_bounded_discrete_variable
    var_name, scales, shapes = get_distribution_info(variable)
    if is_bounded_discrete_variable(variable):
        if initial_points is None:
            initial_points = np.atleast_2d([variable.ppf(0.5)])

        xk, pk = get_probability_masses(variable)
        def generate_candidate_samples(num_samples):
            return xk[None, :]
        opts = {'rv_type': var_name, 'shapes': shapes}
        recursion_coeffs = get_recursion_coefficients(
            opts, xk.shape[0],
            numerically_generated_poly_accuracy_tolerance=numerically_generated_poly_accuracy_tolerance)
        quad_rule = partial(
            candidate_based_christoffel_leja_rule_1d, recursion_coeffs,
            generate_candidate_samples, xk.shape[0], growth_rule=growth_rule,
            initial_points=initial_points)
    else:
        raise Exception('var_name %s not implemented' % var_name)
    return quad_rule
Exemple #15
0
def get_recursion_coefficients_from_variable(var, num_coefs, opts):
    """
    Generate polynomial recursion coefficients by inspecting a random variable.
    """
    var_name, _, shapes = get_distribution_info(var)
    if var_name == "continuous_monomial":
        return None

    loc, scale = transform_scale_parameters(var)

    if var_name == "rv_function_indpndt_vars":
        shapes["loc"] = loc
        shapes["scale"] = scale
        return get_function_independent_vars_recursion_coefficients(
            shapes, num_coefs)

    if var_name == "rv_product_indpndt_vars":
        shapes["loc"] = loc
        shapes["scale"] = scale
        return get_product_independent_vars_recursion_coefficients(
            shapes, num_coefs)

    if (var_name in askey_variable_names
            and opts.get("numeric", False) is False):
        return get_askey_recursion_coefficients_from_variable(var, num_coefs)

    orthonormality_tol = opts.get("orthonormality_tol", 1e-8)
    truncated_probability_tol = opts.get("truncated_probability_tol", 0)
    if (not is_continuous_variable(var)):
        if hasattr(shapes, "xk"):
            xk, pk = shapes["xk"], shapes["pk"]
        else:
            xk, pk = get_probability_masses(var, truncated_probability_tol)
        xk = (xk - loc) / scale

        return get_numerically_generated_recursion_coefficients_from_samples(
            xk, pk, num_coefs, orthonormality_tol, truncated_probability_tol)

    # integration performed in canonical domain so need to map back to
    # domain of pdf
    lb, ub = var.interval(1)

    # Get version var.pdf without error checking which runs much faster
    pdf = get_pdf(var)

    def canonical_pdf(x):
        # print(x, lb, ub, x*scale+loc)
        # print(var.pdf(x*scale+loc)*scale)
        # assert np.all(x*scale+loc >= lb) and np.all(x*scale+loc <= ub)
        return pdf(x * scale + loc) * scale
        # return var.pdf(x*scale+loc)*scale

    if (is_bounded_continuous_variable(var)
            or is_bounded_discrete_variable(var)):
        can_lb, can_ub = -1, 1
    elif is_continuous_variable(var):
        can_lb = (lb - loc) / scale
        can_ub = (ub - loc) / scale

    return predictor_corrector_known_pdf(num_coefs, can_lb, can_ub,
                                         canonical_pdf, opts)
Exemple #16
0
def univariate_christoffel_leja_quadrature_rule(
        variable,
        growth_rule,
        level,
        return_weights_for_all_levels=True,
        initial_points=None,
        orthonormality_tol=1e-12,
        recursion_opts=None,
        minimizer_opts=None):
    """
    Return the samples and weights of the Leja quadrature rule for any
    continuous variable using the inverse Christoffel weight function

    By construction these rules have polynomial ordering.

    Parameters
    ----------
    variable : scipy.stats.dist
        The variable used to construct an orthogonormal polynomial

    growth_rule : callable
        Function which returns the number of samples in the quadrature rule
        With signature

        `growth_rule(level) -> integer`

        where level is an integer

    level : integer
        The level of the univariate rule.

    return_weights_for_all_levels : boolean
        True  - return weights [w(0),w(1),...,w(level)]
        False - return w(level)

    initial_points : np.ndarray (1, ninit_samples)
        Any points that must be included in the Leja sequence. This argument
        is typically used to pass in previously computed sequence which
        is updated efficiently here. MUST be in the canonical domain

    Return
    ------
    ordered_samples_1d : np.ndarray (num_samples_1d)
        The reordered samples.

    ordered_weights_1d : np.ndarray (num_samples_1d)
        The reordered weights.
    """
    if not is_continuous_variable(variable):
        raise Exception('Only supports continuous variables')

    name, scales, shapes = get_distribution_info(variable)
    max_nsamples = growth_rule(level)
    if recursion_opts is None:
        recursion_opts = {"orthonormality_tol": orthonormality_tol}
    ab = get_recursion_coefficients_from_variable(variable, max_nsamples + 1,
                                                  recursion_opts)
    basis_fun = partial(evaluate_orthonormal_polynomial_deriv_1d, ab=ab)

    initial_points, bounds = transform_initial_samples(variable,
                                                       initial_points)

    if minimizer_opts is None:
        minimizer_opts = {'gtol': 1e-8, 'verbose': False}

    if ("artificial_bounds" not in minimizer_opts
            and not is_bounded_continuous_variable(variable)):
        # make bounds twice that of gauss quadrature points
        xg, wg = gauss_quadrature(ab, max_nsamples)
        artificial_bounds = bounds.copy()
        if not np.isfinite(bounds[0]):
            artificial_bounds[0] = xg.min()
            artificial_bounds[0] = artificial_bounds[0] - abs(
                artificial_bounds[0])
        if not np.isfinite(bounds[1]):
            artificial_bounds[1] = xg.max()
            artificial_bounds[1] = artificial_bounds[1] + abs(
                artificial_bounds[1])
        minimizer_opts["artificial_bounds"] = artificial_bounds

    leja_sequence = get_christoffel_leja_sequence_1d(max_nsamples,
                                                     initial_points,
                                                     bounds,
                                                     basis_fun,
                                                     minimizer_opts,
                                                     callback=None)

    __basis_fun = partial(basis_fun, nmax=max_nsamples - 1, deriv_order=0)
    ordered_weights_1d = get_christoffel_leja_quadrature_weights_1d(
        leja_sequence, growth_rule, __basis_fun, level, True)
    if return_weights_for_all_levels:
        return leja_sequence[0, :], ordered_weights_1d
    return leja_sequence[0, :], ordered_weights_1d[-1]
Exemple #17
0
# the following use the product of uniforms to define basis
from pyapprox.variables import get_distribution_info
from pyapprox.univariate_quadrature import gauss_jacobi_pts_wts_1D
from pyapprox.utilities import total_degree_space_dimension

def identity_fun(x):
    return x

degree = 3
poly = PolynomialChaosExpansion()
basis_opts = dict()
identity_map_indices = []
cnt = 0
for ii in range(re_variable.nunique_vars):
    rv = re_variable.unique_variables[ii]
    name, scales, shapes = get_distribution_info(rv)
    if ii not in [0, 2]:
        opts = {'rv_type': name, 'shapes': shapes,
                'var_nums': re_variable.unique_variable_indices[ii]}
        basis_opts['basis%d' % ii] = opts
        continue

    #identity_map_indices += re_variable.unique_variable_indices[ii] # wrong
    identity_map_indices += list(re_variable.unique_variable_indices[ii]) # right
    
    quad_rules = []    
    inds = index_product[cnt]
    nquad_samples_1d = 50

    for jj in inds:
        a, b = variable.all_variables()[jj].interval(1)
Exemple #18
0
def univariate_pdf_weighted_leja_quadrature_rule(
        variable, growth_rule, level, return_weights_for_all_levels=True,
        initial_points=None,
        numerically_generated_poly_accuracy_tolerance=1e-12):
    """
    Return the samples and weights of the Leja quadrature rule for any 
    continuous variable using the PDF of the random variable as the 
    weight function

    By construction these rules have polynomial ordering.

    Parameters
    ----------
    variable : scipy.stats.dist
        The variable used to construct an orthogonormal polynomial

    growth_rule : callable
        Function which returns the number of samples in the quadrature rule
        With signature

        `growth_rule(level) -> integer`

        where level is an integer

    level : integer
        The level of the univariate rule.

    return_weights_for_all_levels : boolean
        True  - return weights [w(0),w(1),...,w(level)]
        False - return w(level)

    initial_points : np.ndarray (1, ninit_samples)
        Any points that must be included in the Leja sequence. This argument
        is typically used to pass in previously computed sequence which
        is updated efficiently here.

    Return
    ------
    ordered_samples_1d : np.ndarray (num_samples_1d)
        The reordered samples.

    ordered_weights_1d : np.ndarray (num_samples_1d)
        The reordered weights.
    """
    if not is_continuous_variable(variable):
        raise Exception('Only supports continuous variables')

    name, scales, shapes = get_distribution_info(variable)
    opts = {'rv_type': name, 'shapes': shapes,
            'var_nums': variable}
    max_nsamples = growth_rule(level)
    ab = get_recursion_coefficients(
        opts, max_nsamples+1, numerically_generated_poly_accuracy_tolerance)
    basis_fun = partial(evaluate_orthonormal_polynomial_deriv_1d, ab=ab)

    pdf, pdf_jac = get_pdf_weight_functions(variable)

    if is_bounded_continuous_variable(variable):
        bounds = variable.interval(1.)
        canonical_bounds = [-1, 1]
        if initial_points is None:
            initial_points = np.asarray(
                [[variable.ppf(0.5)]]).T
            loc, scale = scales['loc'], scales['scale']
            lb, ub = -1, 1
            scale /= (ub-lb)
            loc = loc-scale*lb
            initial_points = (initial_points-loc)/scale
        assert np.all((initial_points >= canonical_bounds[0]) &
                      (initial_points <= canonical_bounds[1]))
        # always produce sequence in canonical space
        bounds = canonical_bounds
    else:
        bounds = list(variable.interval(1))
        if initial_points is None:
            initial_points = np.asarray(
                [[variable.ppf(0.5)]]).T
            loc, scale = scales['loc'], scales['scale']
            initial_points = (initial_points-loc)/scale

    leja_sequence = get_pdf_weighted_leja_sequence_1d(
        max_nsamples, initial_points, bounds, basis_fun, pdf, pdf_jac,
        {'gtol': 1e-8, 'verbose': False}, callback=None)

    __basis_fun = partial(basis_fun, nmax=max_nsamples-1, deriv_order=0)
    ordered_weights_1d = get_pdf_weighted_leja_quadrature_weights_1d(
        leja_sequence, growth_rule, pdf, __basis_fun, level, True)

    return leja_sequence[0, :], ordered_weights_1d
Exemple #19
0
    def test_get_distribution_params(self):
        name, scales, shapes = get_distribution_info(
            stats.beta(a=1, b=2, loc=0, scale=1))
        assert name == 'beta'
        assert shapes == {'a': 1, 'b': 2}
        assert scales == {'loc': 0, 'scale': 1}

        rv = stats.beta(a=1, b=2, loc=3, scale=4)
        pdf = get_pdf(rv)
        xx = rv.rvs(100)
        assert np.allclose(pdf(xx), rv.pdf(xx))

        name, scales, shapes = get_distribution_info(
            stats.beta(1, 2, loc=0, scale=1))
        assert name == 'beta'
        assert shapes == {'a': 1, 'b': 2}
        assert scales == {'loc': 0, 'scale': 1}

        name, scales, shapes = get_distribution_info(
            stats.beta(1, 2, 0, scale=1))
        assert name == 'beta'
        assert shapes == {'a': 1, 'b': 2}
        assert scales == {'loc': 0, 'scale': 1}

        name, scales, shapes = get_distribution_info(stats.beta(1, 2, 0, 1))
        assert name == 'beta'
        assert shapes == {'a': 1, 'b': 2}
        assert scales == {'loc': 0, 'scale': 1}

        name, scales, shapes = get_distribution_info(stats.norm(0, 1))
        assert name == 'norm'
        assert shapes == dict()
        assert scales == {'loc': 0, 'scale': 1}

        name, scales, shapes = get_distribution_info(stats.norm(0, scale=1))
        assert name == 'norm'
        assert shapes == dict()
        assert scales == {'loc': 0, 'scale': 1}

        name, scales, shapes = get_distribution_info(stats.norm(
            loc=0, scale=1))
        assert name == 'norm'
        assert shapes == dict()
        assert scales == {'loc': 0, 'scale': 1}

        name, scales, shapes = get_distribution_info(
            stats.gamma(a=1, loc=0, scale=1))
        assert name == 'gamma'
        assert shapes == {'a': 1}
        assert scales == {'loc': 0, 'scale': 1}

        name, scales, shapes = get_distribution_info(
            stats.gamma(1, loc=0, scale=1))
        assert name == 'gamma'
        assert shapes == {'a': 1}
        assert scales == {'loc': 0, 'scale': 1}

        name, scales, shapes = get_distribution_info(
            stats.gamma(1, 0, scale=1))
        assert name == 'gamma'
        assert shapes == {'a': 1}
        assert scales == {'loc': 0, 'scale': 1}

        name, scales, shapes = get_distribution_info(stats.gamma(1, 0, 1))
        assert name == 'gamma'
        assert shapes == {'a': 1}
        assert scales == {'loc': 0, 'scale': 1}

        name, scales, shapes = get_distribution_info(stats.gamma(1))
        assert name == 'gamma'
        assert shapes == {'a': 1}
        assert scales == {'loc': 0, 'scale': 1}

        name, scales, shapes = get_distribution_info(stats.gamma(1, loc=0))
        assert name == 'gamma'
        assert shapes == {'a': 1}
        assert scales == {'loc': 0, 'scale': 1}

        name, scales, shapes = get_distribution_info(stats.gamma(1, scale=1))
        assert name == 'gamma'
        assert shapes == {'a': 1}
        assert scales == {'loc': 0, 'scale': 1}

        name, scales, shapes = get_distribution_info(
            stats.binom(n=1, p=1, loc=0))
        assert name == 'binom'
        assert shapes == {'n': 1, 'p': 1}
        assert scales == {'loc': 0, 'scale': 1}

        name, scales, shapes = get_distribution_info(
            stats.binom(1, p=1, loc=0))
        assert name == 'binom'
        assert shapes == {'n': 1, 'p': 1}
        assert scales == {'loc': 0, 'scale': 1}

        name, scales, shapes = get_distribution_info(stats.binom(1, 1, loc=0))
        assert name == 'binom'
        assert shapes == {'n': 1, 'p': 1}
        assert scales == {'loc': 0, 'scale': 1}

        name, scales, shapes = get_distribution_info(stats.binom(1, 1, 0))
        assert name == 'binom'
        assert shapes == {'n': 1, 'p': 1}
        assert scales == {'loc': 0, 'scale': 1}