def test_orthonormality_probabilists_hermite_polynomial(self):
        rho = 0.
        degree = 2
        probability_measure = True
        ab = hermite_recurrence(degree + 1,
                                rho,
                                probability=probability_measure)

        x, w = np.polynomial.hermite.hermgauss(degree + 1)
        # transform rule to probablity weight
        # function w=1/sqrt(2*PI)exp(-x^2/2)
        x *= np.sqrt(2.0)
        w /= np.sqrt(np.pi)
        p = evaluate_orthonormal_polynomial_1d(x, degree, ab)

        # Note if using pecos the following is done (i.e. ptFactpr=sqrt(2)),
        # but if I switch to using orthonormal recursion, used here, in Pecos
        # then I will need to set ptFactor=1.0 as done implicitly above
        p_exact = np.asarray([1 + 0. * x, x, x**2 - 1]).T / np.sqrt(
            sp.factorial(np.arange(degree + 1)))
        assert np.allclose(p, p_exact)

        # test orthogonality
        exact_moments = np.zeros((degree + 1))
        exact_moments[0] = 1.0
        assert np.allclose(np.dot(p.T, w), exact_moments)
        # test orthonormality
        print(np.allclose(np.dot(p.T * w, p), np.eye(degree + 1)))
        assert np.allclose(np.dot(p.T * w, p), np.eye(degree + 1))
    def test_orthonormality_physicists_hermite_polynomial(self):
        rho = 0.
        degree = 2
        probability_measure = False

        ab = hermite_recurrence(degree + 1,
                                rho,
                                probability=probability_measure)
        x, w = np.polynomial.hermite.hermgauss(degree + 1)

        p = evaluate_orthonormal_polynomial_1d(x, degree, ab)
        p_exact = np.asarray([1 + 0. * x, 2 * x, 4. * x**2 - 2]).T / np.sqrt(
            sp.factorial(np.arange(degree + 1)) * np.sqrt(np.pi) *
            2**np.arange(degree + 1))

        assert np.allclose(p, p_exact)

        # test orthogonality
        exact_moments = np.zeros((degree + 1))
        # basis is orthonormal so integration of constant basis will be
        # non-zero but will not integrate to 1.0
        exact_moments[0] = np.pi**0.25
        assert np.allclose(np.dot(p.T, w), exact_moments)
        # test orthonormality
        assert np.allclose(np.dot(p.T * w, p), np.eye(degree + 1))
Пример #3
0
def get_askey_recursion_coefficients(poly_name, opts, num_coefs):
    if poly_name not in askey_poly_names:
        raise ValueError(f"poly_name {poly_name} not in {askey_poly_names}")

    if poly_name == "legendre":
        return jacobi_recurrence(num_coefs, alpha=0, beta=0, probability=True)

    if poly_name == "jacobi":
        return jacobi_recurrence(num_coefs,
                                 alpha=opts["alpha_poly"],
                                 beta=opts["beta_poly"],
                                 probability=True)

    if poly_name == "hermite":
        return hermite_recurrence(num_coefs, rho=0., probability=True)

    if poly_name == "krawtchouk":
        msg = "Although bounded the Krawtchouk polynomials are not defined "
        msg += "on the canonical domain [-1,1]. Must use numeric recursion "
        msg += "to generate polynomials on [-1,1] for consistency"
        warn(msg, UserWarning)
        num_coefs = min(num_coefs, opts["n"])
        return krawtchouk_recurrence(num_coefs, opts["n"], opts["p"])

    if poly_name == "hahn":
        msg = "Although bounded the Hahn polynomials are not defined "
        msg += "on the canonical domain [-1,1]. Must use numeric recursion "
        msg += "to generate polynomials on [-1,1] for consistency"
        warn(msg, UserWarning)
        num_coefs = min(num_coefs, opts["N"])
        return hahn_recurrence(num_coefs, opts["N"], opts["alpha_poly"],
                               opts["beta_poly"])

    if poly_name == "charlier":
        return charlier_recurrence(num_coefs, opts["mu"])
Пример #4
0
 def test_continuous_rv_sample(self):
     N, degree = int(1e6), 5
     xk, pk = np.random.normal(0, 1, N), np.ones(N) / N
     ab = modified_chebyshev_orthonormal(degree + 1, [xk, pk])
     hermite_ab = hermite_recurrence(degree + 1, 0, True)
     x, w = gauss_quadrature(hermite_ab, degree + 1)
     p = evaluate_orthonormal_polynomial_1d(x, degree, ab)
     gaussian_moments = np.zeros(degree + 1)
     gaussian_moments[0] = 1
     assert np.allclose(p.T.dot(w), gaussian_moments, atol=1e-2)
     assert np.allclose(np.dot(p.T * w, p), np.eye(degree + 1), atol=7e-2)
    def test_convert_orthonormal_recurence_to_three_term_recurence(self):
        rho = 0.
        degree = 2
        probability_measure = True
        ab = hermite_recurrence(degree + 1,
                                rho,
                                probability=probability_measure)
        abc = convert_orthonormal_recurence_to_three_term_recurence(ab)

        x = np.linspace(-3, 3, 101)
        p_2term = evaluate_orthonormal_polynomial_1d(x, degree, ab)
        p_3term = evaluate_three_term_recurrence_polynomial_1d(abc, degree, x)
        assert np.allclose(p_2term, p_3term)
    def test_gauss_quadrature(self):
        degree = 4
        alpha = 0.
        beta = 0.
        ab = jacobi_recurrence(degree + 1,
                               alpha=alpha,
                               beta=beta,
                               probability=True)

        x, w = gauss_quadrature(ab, degree + 1)
        for ii in range(degree + 1):
            if ii % 2 == 0:
                assert np.allclose(np.dot(x**ii, w), 1. / (ii + 1.))
            else:
                assert np.allclose(np.dot(x**ii, w), 0.)

        x_np, w_np = np.polynomial.legendre.leggauss(degree + 1)
        assert np.allclose(x_np, x)
        assert np.allclose(w_np / 2, w)

        degree = 4
        alpha = 4.
        beta = 1.
        ab = jacobi_recurrence(degree + 1,
                               alpha=alpha,
                               beta=beta,
                               probability=True)

        x, w = gauss_quadrature(ab, degree + 1)

        true_moments = [1., -3. / 7., 2. / 7., -4. / 21., 1. / 7.]
        for ii in range(degree + 1):
            assert np.allclose(np.dot(x**ii, w), true_moments[ii])

        degree = 4
        rho = 0.
        ab = hermite_recurrence(degree + 1, rho, probability=True)
        x, w = gauss_quadrature(ab, degree + 1)
        from scipy.special import factorial2
        assert np.allclose(np.dot(x**degree, w), factorial2(degree - 1))

        x_sp, w_sp = sp.roots_hermitenorm(degree + 1)
        w_sp /= np.sqrt(2 * np.pi)
        assert np.allclose(x_sp, x)
        assert np.allclose(w_sp, w)
    def test_convert_orthonormal_expansion_to_monomial_expansion_1d(self):
        """
        Approximate function
        f1 = lambda x: ((x-mu)/sigma)**3 using hermite polynomials tailored for
        normal random variable with mean mu and variance sigma**2

        The function defined on canonical domain of the hermite polynomials,
        i.e. normal with mean zero and unit variance, is
        f2 = lambda x: x.T**3
        """
        degree = 4
        mu, sigma = 1, 2
        ortho_coef = np.array([0, 3, 0, np.sqrt(6)])
        ab = hermite_recurrence(degree + 1, 0, True)
        mono_coefs = convert_orthonormal_expansion_to_monomial_expansion_1d(
            ortho_coef, ab, mu, sigma)
        true_mono_coefs = np.array([-mu**3, 3 * mu**2, -3 * mu, 1]) / sigma**3
        assert np.allclose(mono_coefs, true_mono_coefs)
    def test_convert_monomials_to_orthonormal_polynomials_1d(self):
        rho = 0.
        degree = 10
        probability_measure = True
        ab = hermite_recurrence(degree + 1,
                                rho,
                                probability=probability_measure)

        basis_mono_coefs = convert_orthonormal_polynomials_to_monomials_1d(
            ab, degree)

        x = np.random.normal(0, 1, (100))
        print('Cond number', np.linalg.cond(basis_mono_coefs))
        basis_ortho_coefs = np.linalg.inv(basis_mono_coefs)
        ortho_basis_matrix = evaluate_orthonormal_polynomial_1d(x, degree, ab)
        mono_basis_matrix = x[:, None]**np.arange(degree + 1)[None, :]
        assert np.allclose(mono_basis_matrix,
                           ortho_basis_matrix.dot(basis_ortho_coefs.T))
    def test_convert_orthonormal_polynomials_to_monomials_1d(self):
        """
        Example: orthonormal Hermite polynomials
        deg  monomial coeffs
        0    [1,0,0]
        1    [0,1,0]         1/1*((x-0)*1-1*0)=x
        2    [1/c,0,1/c]     1/c*((x-0)*x-1*1)=(x**2-1)/c,            c=sqrt(2)
        3    [0,-3/d,0,1/d]  1/d*((x-0)*(x**2-1)/c-c*x)=
                             1/(c*d)*(x**3-x-c**2*x)=(x**3-3*x)/(c*d),d=sqrt(3)
        """
        rho = 0.
        degree = 10
        probability_measure = True
        ab = hermite_recurrence(degree + 1,
                                rho,
                                probability=probability_measure)

        basis_mono_coefs = convert_orthonormal_polynomials_to_monomials_1d(
            ab, 4)

        true_basis_mono_coefs = np.zeros((5, 5))
        true_basis_mono_coefs[0, 0] = 1
        true_basis_mono_coefs[1, 1] = 1
        true_basis_mono_coefs[2, [0, 2]] = -1 / np.sqrt(2), 1 / np.sqrt(2)
        true_basis_mono_coefs[3, [1, 3]] = -3 / np.sqrt(6), 1 / np.sqrt(6)
        true_basis_mono_coefs[4,
                              [0, 2, 4]] = np.array([3, -6, 1]) / np.sqrt(24)

        assert np.allclose(basis_mono_coefs, true_basis_mono_coefs)

        coefs = np.ones(degree + 1)
        basis_mono_coefs = convert_orthonormal_polynomials_to_monomials_1d(
            ab, degree)
        mono_coefs = np.sum(basis_mono_coefs * coefs, axis=0)

        x = np.linspace(-3, 3, 5)
        p_ortho = evaluate_orthonormal_polynomial_1d(x, degree, ab)
        ortho_vals = p_ortho.dot(coefs)

        from pyapprox.monomial import evaluate_monomial
        mono_vals = evaluate_monomial(
            np.arange(degree + 1)[np.newaxis, :], mono_coefs,
            x[np.newaxis, :])[:, 0]
        assert np.allclose(ortho_vals, mono_vals)
Пример #10
0
def gauss_hermite_pts_wts_1D(num_samples):
    """
    Return Gauss Hermite quadrature rule that exactly integrates polynomials
    of degree 2*num_samples-1 with respect to the Gaussian probability measure
    1/sqrt(2*pi)exp(-x**2/2)

    Parameters
    ----------
    num_samples : integer
        The number of samples in the quadrature rule

    Returns
    -------
    x : np.ndarray(num_samples)
        Quadrature samples

    w : np.ndarray(num_samples)
        Quadrature weights
    """
    rho = 0.0
    ab = hermite_recurrence(num_samples, rho, probability=True)
    x, w = gauss_quadrature(ab, num_samples)
    return x, w
Пример #11
0
    def test_hermite_pdf_weighted_leja_sequence_1d(self):
        max_nsamples = 3
        initial_points = np.array([[0]])
        ab = hermite_recurrence(max_nsamples + 1, True)
        basis_fun = partial(evaluate_orthonormal_polynomial_deriv_1d, ab=ab)

        pdf = partial(gaussian_pdf, 0, 1)
        pdf_jac = partial(gaussian_pdf_derivative, 0, 1)

        # def callback(leja_sequence, coef, new_samples, obj_vals,
        #              initial_guesses):
        #     degree = coef.shape[0]-1

        #     def plot_fun(x):
        #         return -pdf_weighted_leja_objective_fun_1d(
        #             pdf, partial(basis_fun, nmax=degree +
        #                          1, deriv_order=0), coef,
        #             x[None, :])
        #     xx = np.linspace(-10, 10, 101)
        #     plt.plot(xx, plot_fun(xx))
        #     plt.plot(leja_sequence[0, :], plot_fun(leja_sequence[0, :]), 'o')
        #     plt.plot(new_samples[0, :], obj_vals, 's')
        #     plt.plot(
        #         initial_guesses[0, :], plot_fun(initial_guesses[0, :]), '*')
        #     plt.show()

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

        # compare to lu based leja samples
        # given the same set of initial samples the next sample chosen
        # should be close with the one from the gradient based method having
        # slightly better objective value
        num_candidate_samples = 1001

        def generate_candidate_samples(n):
            return np.linspace(-5, 5, n)[None, :]

        discrete_leja_sequence = \
            get_candidate_based_pdf_weighted_leja_sequence_1d(
                max_nsamples, ab, generate_candidate_samples,
                num_candidate_samples, pdf,
                initial_points=leja_sequence[:, :max_nsamples-1])

        degree = max_nsamples - 1
        tmp = basis_fun(discrete_leja_sequence[0, :-1],
                        nmax=degree + 1,
                        deriv_order=0)
        basis_mat = tmp[:, :-1]
        new_basis = tmp[:, -1:]
        coef = compute_coefficients_of_pdf_weighted_leja_interpolant_1d(
            pdf(discrete_leja_sequence[0, :-1]), basis_mat, new_basis)
        discrete_obj_val = pdf_weighted_leja_objective_fun_1d(
            pdf, partial(basis_fun, nmax=degree + 1, deriv_order=0), coef,
            discrete_leja_sequence[:, -1:])

        tmp = basis_fun(leja_sequence[0, :-1], nmax=degree + 1, deriv_order=0)
        basis_mat = tmp[:, :-1]
        new_basis = tmp[:, -1:]
        coef = compute_coefficients_of_pdf_weighted_leja_interpolant_1d(
            pdf(leja_sequence[0, :-1]), basis_mat, new_basis)
        obj_val = pdf_weighted_leja_objective_fun_1d(
            pdf, partial(basis_fun, nmax=degree + 1, deriv_order=0), coef,
            leja_sequence[:, -1:])

        x = generate_candidate_samples(num_candidate_samples)
        assert obj_val > discrete_obj_val
        assert (abs(leja_sequence[0, -1] - discrete_leja_sequence[0, -1]) <
                x[0, 1] - x[0, 0])
Пример #12
0
    def test_hermite_christoffel_leja_sequence_1d(self):
        import warnings
        warnings.filterwarnings('error')
        # for unbounded variables can get overflow warnings because when
        # optimizing interval with one side unbounded and no local minima
        # exists then optimization will move towards inifinity
        max_nsamples = 20
        initial_points = np.array([[0, stats.norm(0, 1).ppf(0.75)]])
        # initial_points = np.array([[stats.norm(0, 1).ppf(0.75)]])
        ab = hermite_recurrence(max_nsamples + 1, 0, False)
        basis_fun = partial(evaluate_orthonormal_polynomial_deriv_1d, ab=ab)

        # plot_degree = np.inf  # max_nsamples-1
        # assert plot_degree < max_nsamples

        # def callback(leja_sequence, coef, new_samples, obj_vals,
        #              initial_guesses):
        #     degree = coef.shape[0]-1
        #     new_basis_degree = degree+1
        #     if new_basis_degree != plot_degree:
        #         return
        #     plt.clf()

        #     def plot_fun(x):
        #         return -christoffel_leja_objective_fun_1d(
        #             partial(basis_fun, nmax=new_basis_degree, deriv_order=0),
        #             coef, x[None, :])
        #     xx = np.linspace(-20, 20, 1001)
        #     plt.plot(xx, plot_fun(xx))
        #     plt.plot(leja_sequence[0, :], plot_fun(leja_sequence[0, :]), 'o')
        #     I = np.argmin(obj_vals)
        #     plt.plot(new_samples[0, I], obj_vals[I], 's')
        #     plt.plot(
        #         initial_guesses[0, :], plot_fun(initial_guesses[0, :]), '*')
        #     #plt.xlim(-10, 10)
        #     print('s', new_samples[0], obj_vals)

        leja_sequence = get_christoffel_leja_sequence_1d(max_nsamples,
                                                         initial_points,
                                                         [-np.inf, np.inf],
                                                         basis_fun, {
                                                             'gtol': 1e-8,
                                                             'verbose': False,
                                                             'iprint': 2
                                                         },
                                                         callback=None)

        # compare to lu based leja samples
        # given the same set of initial samples the next sample chosen
        # should be close with the one from the gradient based method having
        # slightly better objective value
        num_candidate_samples = 10001

        def generate_candidate_samples(n):
            return np.linspace(-20, 20, n)[None, :]

        for ii in range(initial_points.shape[1], max_nsamples):
            degree = ii - 1
            new_basis_degree = degree + 1
            candidate_samples = generate_candidate_samples(
                num_candidate_samples)
            candidate_samples = np.hstack(
                [leja_sequence[:, :ii], candidate_samples])
            bfun = partial(basis_fun, nmax=new_basis_degree, deriv_order=0)
            basis_mat = bfun(candidate_samples[0, :])
            basis_mat = sqrt_christoffel_function_inv_1d(
                bfun, candidate_samples)[:, None] * basis_mat
            LU, pivots = truncated_pivoted_lu_factorization(
                basis_mat, ii + 1, ii, False)
            # cannot use get_candidate_based_leja_sequence_1d
            # because it uses christoffel function that is for maximum
            # degree
            discrete_leja_sequence = candidate_samples[:, pivots[:ii + 1]]

            # if new_basis_degree == plot_degree:
            #     # mulitply by LU[ii, ii]**2 to account for LU factorization
            #     # dividing the column by this number
            #     discrete_obj_vals = -LU[:, ii]**2*LU[ii, ii]**2
            #     # account for pivoting of ith column of LU factor
            #     # value of best objective can be found in the iith pivot
            #     discrete_obj_vals[ii] = discrete_obj_vals[pivots[ii]]
            #     discrete_obj_vals[pivots[ii]] = -LU[ii, ii]**2
            #     I = np.argsort(candidate_samples[0, ii:])+ii
            #     plt.plot(candidate_samples[0, I], discrete_obj_vals[I], '--')
            #     plt.plot(candidate_samples[0, pivots[ii]],
            #              -LU[ii, ii]**2, 'k^')
            #     plt.show()

            def objective_value(sequence):
                tmp = bfun(sequence[0, :])
                basis_mat = tmp[:, :-1]
                new_basis = tmp[:, -1:]
                coef = compute_coefficients_of_christoffel_leja_interpolant_1d(
                    basis_mat, new_basis)
                return christoffel_leja_objective_fun_1d(
                    bfun, coef, sequence[:, -1:])

            # discrete_obj_val = objective_value(
            #     discrete_leja_sequence[:, :ii+1])
            # obj_val = objective_value(leja_sequence[:, :ii+1])

            diff = candidate_samples[0, -1] - candidate_samples[0, -2]
            # print(ii, obj_val - discrete_obj_val)
            print(leja_sequence[:, :ii + 1], discrete_leja_sequence)
            # assert obj_val >= discrete_obj_val
            # obj_val will not always be greater than because of optimization
            # tolerance and discretization of candidate samples
            # assert abs(obj_val - discrete_obj_val) < 1e-4
            assert (abs(leja_sequence[0, ii] - discrete_leja_sequence[0, -1]) <
                    diff)
Пример #13
0
    def test_predictor_corrector_known_pdf(self):
        nterms = 12
        tol = 1e-12
        quad_options = {
            'epsrel': tol,
            'epsabs': tol,
            "limlst": 10,
            "limit": 1000
        }

        rv = stats.beta(1, 1, -1, 2)
        ab = predictor_corrector_known_pdf(nterms, -1, 1, rv.pdf, quad_options)
        true_ab = jacobi_recurrence(nterms, 0, 0)
        assert np.allclose(ab, true_ab)

        rv = stats.beta(3, 3, -1, 2)
        ab = predictor_corrector_known_pdf(nterms, -1, 1, rv.pdf, quad_options)
        true_ab = jacobi_recurrence(nterms, 2, 2)

        rv = stats.norm(0, 2)
        loc, scale = transform_scale_parameters(rv)
        ab = predictor_corrector_known_pdf(
            nterms, -np.inf, np.inf, lambda x: rv.pdf(x * scale + loc) * scale,
            quad_options)
        true_ab = hermite_recurrence(nterms)
        assert np.allclose(ab, true_ab)

        # lognormal is a very hard test
        # rv = stats.lognorm(1)
        # custom_integrate_fun = native_recursion_integrate_fun
        # interval_size = abs(np.diff(rv.interval(0.99)))
        # integrate_fun = partial(custom_integrate_fun, interval_size)
        # quad_opts = {"integrate_fun": integrate_fun}
        # # quad_opts = {}
        # opts = {"numeric": True, "quad_options": quad_opts}

        # loc, scale = transform_scale_parameters(rv)
        # ab = predictor_corrector_known_pdf(
        #     nterms, 0, np.inf, lambda x: rv.pdf(x*scale+loc)*scale, opts)
        # for ii in range(1, nterms):
        #     assert np.all(gauss_quadrature(ab, ii)[0] > 0)
        # gram_mat = ortho_polynomial_grammian_bounded_continuous_variable(
        #     rv, ab, nterms-1, tol=tol, integrate_fun=integrate_fun)
        # # print(gram_mat-np.eye(gram_mat.shape[0]))
        # # print(np.absolute(gram_mat-np.eye(gram_mat.shape[0])).max())
        # assert np.absolute(gram_mat-np.eye(gram_mat.shape[0])).max() < 5e-10

        nterms = 2
        mean, std = 1e4, 7.5e3
        beta = std * np.sqrt(6) / np.pi
        mu = mean - beta * np.euler_gamma
        # mu, beta = 1, 1
        rv = stats.gumbel_r(loc=mu, scale=beta)
        custom_integrate_fun = native_recursion_integrate_fun
        tabulated_quad_rules = {}
        from numpy.polynomial.legendre import leggauss
        for nquad_samples in [100, 200, 400]:
            tabulated_quad_rules[nquad_samples] = leggauss(nquad_samples)
        # interval_size must be in canonical domain
        interval_size = abs(np.diff(rv.interval(0.99))) / beta
        integrate_fun = partial(custom_integrate_fun,
                                interval_size,
                                tabulated_quad_rules=tabulated_quad_rules,
                                verbose=3)
        quad_opts = {"integrate_fun": integrate_fun}
        # quad_opts = {}
        opts = {"numeric": True, "quad_options": quad_opts}

        loc, scale = transform_scale_parameters(rv)
        ab = predictor_corrector_known_pdf(
            nterms, -np.inf, np.inf, lambda x: rv.pdf(x * scale + loc) * scale,
            opts)
        gram_mat = ortho_polynomial_grammian_bounded_continuous_variable(
            rv, ab, nterms - 1, tol=tol, integrate_fun=integrate_fun)
        # print(gram_mat-np.eye(gram_mat.shape[0]))
        print(np.absolute(gram_mat - np.eye(gram_mat.shape[0])).max())
        assert np.absolute(gram_mat - np.eye(gram_mat.shape[0])).max() < 5e-10