Example #1
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"])
Example #2
0
    def test_modified_chebyshev(self):
        nterms = 10
        alpha_stat, beta_stat = 2, 2
        probability_measure = True
        # using scipy to compute moments is extermely slow
        # moments = [stats.beta.moment(n,alpha_stat,beta_stat,loc=-1,scale=2)
        #           for n in range(2*nterms)]
        quad_x, quad_w = gauss_jacobi_pts_wts_1D(4 * nterms, beta_stat - 1,
                                                 alpha_stat - 1)

        true_ab = jacobi_recurrence(nterms,
                                    alpha=beta_stat - 1,
                                    beta=alpha_stat - 1,
                                    probability=probability_measure)

        ab = modified_chebyshev_orthonormal(nterms, [quad_x, quad_w],
                                            get_input_coefs=None,
                                            probability=True)
        assert np.allclose(true_ab, ab)

        get_input_coefs = partial(jacobi_recurrence,
                                  alpha=beta_stat - 2,
                                  beta=alpha_stat - 2)
        ab = modified_chebyshev_orthonormal(nterms, [quad_x, quad_w],
                                            get_input_coefs=get_input_coefs,
                                            probability=True)
        assert np.allclose(true_ab, ab)
    def test_christoffel_inv_gradients(self):
        degree = 2
        ab = jacobi_recurrence(degree + 1, 0, 0, True)
        basis_fun = partial(evaluate_orthonormal_polynomial_1d,
                            nmax=degree,
                            ab=ab)
        basis_fun_and_jac = partial(evaluate_orthonormal_polynomial_deriv_1d,
                                    nmax=degree,
                                    ab=ab,
                                    deriv_order=1)
        sample = np.random.uniform(-1, 1, (1, 1))
        # sample = np.atleast_2d(-0.99)

        fun = partial(christoffel_function_inv_1d, basis_fun)
        jac = partial(christoffel_function_inv_jac_1d, basis_fun_and_jac)

        # xx = np.linspace(-1, 1, 101); plt.plot(xx, fun(xx[None, :]));
        # plt.plot(sample[0], fun(sample), 'o'); plt.show()

        err = check_gradients(fun, jac, sample)
        assert err.max() > .5 and err.min() < 1e-7

        basis_fun_jac_hess = partial(evaluate_orthonormal_polynomial_deriv_1d,
                                     nmax=degree,
                                     ab=ab,
                                     deriv_order=2)
        hess = partial(christoffel_function_inv_hess_1d,
                       basis_fun_jac_hess,
                       normalize=False)
        err = check_gradients(jac, hess, sample)
        assert err.max() > .5 and err.min() < 1e-7
    def test_derivatives_of_legendre_polynomial(self):
        alpha = 0.
        beta = 0.
        degree = 3
        probability_measure = True
        deriv_order = 2

        ab = jacobi_recurrence(degree + 1,
                               alpha=alpha,
                               beta=beta,
                               probability=probability_measure)
        x, w = np.polynomial.legendre.leggauss(degree + 1)
        pd = evaluate_orthonormal_polynomial_deriv_1d(x, degree, ab,
                                                      deriv_order)

        pd_exact = [
            np.asarray([
                1 + 0. * x, x, 0.5 * (3. * x**2 - 1),
                0.5 * (5. * x**3 - 3. * x)
            ]).T
        ]
        pd_exact.append(
            np.asarray([0. * x, 1.0 + 0. * x, 3. * x, 7.5 * x**2 - 1.5]).T)
        pd_exact.append(np.asarray([0. * x, 0. * x, 3. + 0. * x, 15 * x]).T)
        pd_exact = np.asarray(pd_exact) / np.sqrt(
            1. / (2 * np.arange(degree + 1) + 1))
        for ii in range(deriv_order + 1):
            assert np.allclose(
                pd[:, ii * (degree + 1):(ii + 1) * (degree + 1)], pd_exact[ii])
Example #5
0
def sparsity_example(decay_rate, rank, num_vars, num_params_1d):
    num_trials = 20
    sparsity_fractions = np.arange(1, 10, dtype=float)/10.
    assert sparsity_fractions[-1] != 1  # error will always be zero

    ranks = ranks_vector(num_vars, rank)
    num_1d_functions = num_univariate_functions(ranks)
    num_ft_parameters = num_params_1d*num_1d_functions

    alpha = 0
    beta = 0
    recursion_coeffs = jacobi_recurrence(
        num_params_1d, alpha=alpha, beta=beta, probability=True)

    assert int(os.environ['OMP_NUM_THREADS']) == 1
    max_eval_concurrency = 4  # max(multiprocessing.cpu_count()-2,1)
    pool = Pool(max_eval_concurrency)
    partial_func = partial(
        sparsity_example_engine, ranks=ranks,
        num_ft_parameters=num_ft_parameters,
        decay_rate=decay_rate, recursion_coeffs=recursion_coeffs,
        sparsity_fractions=sparsity_fractions)

    filename = 'function-train-sparsity-effect-%d-%d-%d-%1.1f-%d.npz' % (
        num_vars, rank, num_params_1d, decay_rate, num_trials)
    if not os.path.exists(filename):
        result = pool.map(partial_func, [(num_params_1d)]*num_trials)
        l2_errors = np.asarray(result).T  # (num_sparsity_fractions,num_trials)
        np.savez(
            filename, l2_errors=l2_errors,
            sparsity_fractions=sparsity_fractions,
            num_ft_parameters=num_ft_parameters)
    return filename
    def test_orthonormality_asymetric_jacobi_polynomial(self):
        from scipy.stats import beta as beta_rv
        alpha = 4.
        beta = 1.
        degree = 3
        probability_measure = True

        ab = jacobi_recurrence(degree + 1,
                               alpha=alpha,
                               beta=beta,
                               probability=probability_measure)

        x, w = np.polynomial.legendre.leggauss(10 * degree)
        p = evaluate_orthonormal_polynomial_1d(x, degree, ab)
        w *= beta_rv.pdf((x + 1.) / 2., a=beta + 1, b=alpha + 1) / 2.

        # 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
        assert np.allclose(np.dot(p.T * w, p), np.eye(degree + 1))

        assert np.allclose(
            evaluate_orthonormal_polynomial_deriv_1d(x, degree, ab, 0), p)
    def test_evaluate_function_train_additive_function(self):
        """
        Test the evaluation of a function train representation of an additive
        function.

        Assume same parameterization for each core and for each univariate
        function within a core.

        Use polynomial basis for each univariate function.
        """
        alpha = 0
        beta = 0
        degree = 2
        num_vars = 3
        num_samples = 1
        recursion_coeffs = jacobi_recurrence(degree + 1,
                                             alpha=alpha,
                                             beta=beta,
                                             probability=True)

        univariate_function_params = [np.random.normal(0., 1., (degree + 1))
                                      ] * num_vars
        ft_data = generate_additive_function_in_function_train_format(
            univariate_function_params, True)

        samples = np.random.uniform(-1., 1., (num_vars, num_samples))
        values = evaluate_function_train(samples, ft_data, recursion_coeffs)

        true_values = additive_polynomial(samples, univariate_function_params,
                                          recursion_coeffs)

        assert np.allclose(values, true_values)
    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)
Example #9
0
    def test_arbitraty_polynomial_chaos(self):
        nterms = 5
        alpha_stat, beta_stat = 1, 1

        true_ab = jacobi_recurrence(nterms,
                                    alpha=beta_stat - 1,
                                    beta=alpha_stat - 1,
                                    probability=True)

        rv = stats.uniform(-1, 2)
        moments = [rv.moment(n) for n in range(2 * nterms + 1)]
        ab = arbitrary_polynomial_chaos_recursion_coefficients(moments, nterms)

        assert np.allclose(true_ab, ab)
Example #10
0
    def test_continous_induced_measure_ppf(self):
        degree = 2
        alpha_stat, beta_stat = 3, 3
        ab = jacobi_recurrence(
            degree+1, alpha=beta_stat-1, beta=alpha_stat-1, probability=True)

        tol = 1e-15
        var = stats.beta(alpha_stat, beta_stat, -5, 10)
        can_lb, can_ub = -1, 1
        lb, ub = var.support()
        print(lb, ub)
        cx = np.linspace(can_lb, can_ub, 51)

        def can_pdf(xx):
            loc, scale = lb+(ub-lb)/2, (ub-lb)/2
            return var.pdf(xx*scale+loc)*scale

        cdf_vals = continuous_induced_measure_cdf(
            can_pdf, ab, degree, can_lb, can_ub, tol, cx)
        assert np.all(cdf_vals <= 1.0)
        ppf_vals = continuous_induced_measure_ppf(
            var, ab, degree, cdf_vals, 1e-10, 1e-8)
        assert np.allclose(cx, ppf_vals)

        try:
            var = stats.loguniform(1.e-5, 1.e-3)
        except:
            var = stats.reciprocal(1.e-5, 1.e-3)
        ab = get_recursion_coefficients_from_variable(var, degree+5, {})
        can_lb, can_ub = -1, 1
        cx = np.linspace(can_lb, can_ub, 51)
        lb, ub = var.support()

        def can_pdf(xx):
            loc, scale = lb+(ub-lb)/2, (ub-lb)/2
            return var.pdf(xx*scale+loc)*scale
        cdf_vals = continuous_induced_measure_cdf(
            can_pdf, ab, degree, can_lb, can_ub, tol, cx)
        # differences caused by root finding optimization tolerance
        assert np.all(cdf_vals <= 1.0)
        ppf_vals = continuous_induced_measure_ppf(
            var, ab, degree, cdf_vals, 1e-10, 1e-8)
        # import matplotlib.pyplot as plt
        # plt.plot(cx, cdf_vals)
        # plt.plot(ppf_vals, cdf_vals, 'r*', ms=2)
        # plt.show()
        assert np.allclose(cx, ppf_vals)
Example #11
0
def gauss_jacobi_pts_wts_1D(num_samples, alpha_poly, beta_poly):
    """
    Return Gauss Jacobi quadrature rule that exactly integrates polynomials
    of num_samples 2*num_samples-1 with respect to the probabilty density
    function of Beta random variables on [-1,1]

    C*(1+x)^(beta_poly)*(1-x)^alpha_poly

    where

    C = 1/(2**(alpha_poly+beta_poly)*beta_fn(beta_poly+1,alpha_poly+1))

    or equivalently

    C*(1+x)**(alpha_stat-1)*(1-x)**(beta_stat-1)

    where

    C = 1/(2**(alpha_stat+beta_stat-2)*beta_fn(alpha_stat,beta_stat))

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

    alpha_poly : float
        The Jaocbi parameter alpha = beta_stat-1

    beta_poly : float
        The Jacobi parameter beta = alpha_stat-1

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

    w : np.ndarray(num_samples)
        Quadrature weights
    """
    ab = jacobi_recurrence(num_samples,
                           alpha=alpha_poly,
                           beta=beta_poly,
                           probability=True)
    return gauss_quadrature(ab, num_samples)
    def test_christoffel_leja_objective_gradients(self):
        # leja_sequence = np.array([[-1, 1]])
        leja_sequence = np.array([[-1, 0, 1]])
        degree = leja_sequence.shape[1] - 1
        ab = jacobi_recurrence(degree + 2, 0, 0, True)
        basis_fun = partial(evaluate_orthonormal_polynomial_1d,
                            nmax=degree + 1,
                            ab=ab)
        tmp = basis_fun(leja_sequence[0, :])
        nterms = degree + 1
        basis_mat = tmp[:, :nterms]
        new_basis = tmp[:, nterms:]
        coef = compute_coefficients_of_christoffel_leja_interpolant_1d(
            basis_mat, new_basis)
        fun = partial(christoffel_leja_objective_fun_1d, basis_fun, coef)

        # xx = np.linspace(-1, 1, 101); plt.plot(xx, fun(xx[None, :]));
        # plt.plot(leja_sequence[0, :], fun(leja_sequence), 'o'); plt.show()

        basis_fun_and_jac = partial(evaluate_orthonormal_polynomial_deriv_1d,
                                    nmax=degree + 1,
                                    ab=ab,
                                    deriv_order=1)
        jac = partial(christoffel_leja_objective_jac_1d, basis_fun_and_jac,
                      coef)

        sample = sample = np.random.uniform(-1, 1, (1, 1))
        err = check_gradients(fun, jac, sample)
        assert err.max() > 0.5 and err.min() < 1e-7

        basis_fun_jac_hess = partial(evaluate_orthonormal_polynomial_deriv_1d,
                                     nmax=degree + 1,
                                     ab=ab,
                                     deriv_order=2)
        hess = partial(christoffel_leja_objective_hess_1d, basis_fun_jac_hess,
                       coef)
        err = check_gradients(jac, hess, sample)
        assert err.max() > .5 and err.min() < 1e-7
    def test_pdf_weighted_leja_objective_gradients(self):
        # leja_sequence = np.array([[-1, 1]])
        leja_sequence = np.array([[-1, 0, 1]])
        degree = leja_sequence.shape[1] - 1
        ab = jacobi_recurrence(degree + 2, 0, 0, True)
        basis_fun = partial(evaluate_orthonormal_polynomial_1d,
                            nmax=degree + 1,
                            ab=ab)

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

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

        tmp = basis_fun(leja_sequence[0, :])
        nterms = degree + 1
        basis_mat = tmp[:, :nterms]
        new_basis = tmp[:, nterms:]
        coef = compute_coefficients_of_pdf_weighted_leja_interpolant_1d(
            pdf(leja_sequence[0, :]), basis_mat, new_basis)

        fun = partial(pdf_weighted_leja_objective_fun_1d, pdf, basis_fun, coef)

        # xx = np.linspace(-1, 1, 101); plt.plot(xx, fun(xx[None, :]));
        # plt.plot(leja_sequence[0, :], fun(leja_sequence), 'o'); plt.show()

        basis_fun_and_jac = partial(evaluate_orthonormal_polynomial_deriv_1d,
                                    nmax=degree + 1,
                                    ab=ab,
                                    deriv_order=1)
        jac = partial(pdf_weighted_leja_objective_jac_1d, pdf, pdf_jac,
                      basis_fun_and_jac, coef)

        sample = sample = np.random.uniform(-1, 1, (1, 1))
        err = check_gradients(fun, jac, sample)
        assert err.max() > 0.4 and err.min() < 1e-7
    def test_uniform_christoffel_leja_sequence_1d(self):
        max_nsamples = 3
        initial_points = np.array([[0]])
        ab = jacobi_recurrence(max_nsamples + 1, 0, 0, True)
        basis_fun = partial(evaluate_orthonormal_polynomial_deriv_1d, ab=ab)

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

        #     def plot_fun(x):
        #         return -christoffel_leja_objective_fun_1d(
        #             partial(basis_fun, nmax=degree+1, deriv_order=0), coef,
        #             x[None, :])
        #     xx = np.linspace(-1, 1, 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_christoffel_leja_sequence_1d(max_nsamples,
                                                         initial_points,
                                                         [-1, 1],
                                                         basis_fun, {
                                                             'gtol': 1e-8,
                                                             'verbose': False
                                                         },
                                                         callback=None)

        from pyapprox.univariate_quadrature import leja_growth_rule
        level = 3
        __basis_fun = partial(basis_fun, nmax=max_nsamples - 1, deriv_order=0)
        weights = get_christoffel_leja_quadrature_weights_1d(
            leja_sequence, leja_growth_rule, __basis_fun, level, True)
        assert np.allclose((leja_sequence**2).dot(weights[-1]), 1 / 3)
    def test_orthonormality_legendre_polynomial(self):
        alpha = 0.
        beta = 0.
        degree = 3
        probability_measure = True

        ab = jacobi_recurrence(degree + 1,
                               alpha=alpha,
                               beta=beta,
                               probability=probability_measure)

        x, w = np.polynomial.legendre.leggauss(degree + 1)
        # make weights have probablity weight function w=1/2
        w /= 2.0
        p = evaluate_orthonormal_polynomial_1d(x, degree, ab)
        # 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
        assert np.allclose(np.dot(p.T * w, p), np.eye(degree + 1))

        assert np.allclose(
            evaluate_orthonormal_polynomial_deriv_1d(x, degree, ab, 0), p)
    def test_gradient_random_function_train(self):
        """
        Test the gradient of a random function train.
        Gradient is with respect to coefficients of the univariate functions

        Assume different parameterization for some univariate functions.
        Zero and ones are stored as a constant basis. Where as other entries
        are stored as a polynomial of a fixed degree d.
        """
        alpha = 0
        beta = 0
        degree = 2
        num_vars = 3
        rank = 2
        recursion_coeffs = jacobi_recurrence(degree + 1,
                                             alpha=alpha,
                                             beta=beta,
                                             probability=True)

        ranks = np.ones((num_vars + 1), dtype=int)
        ranks[1:-1] = rank
        num_params_1d = degree + 1
        num_1d_functions = num_univariate_functions(ranks)
        ft_params = np.random.normal(0., 1.,
                                     (num_params_1d * num_1d_functions))
        ft_data = generate_homogeneous_function_train(ranks, num_params_1d,
                                                      ft_params)

        sample = np.random.uniform(-1., 1., (num_vars, 1))
        value, ft_gradient = evaluate_function_train_grad(
            sample, ft_data, recursion_coeffs)

        fd_gradient = ft_parameter_finite_difference_gradient(
            sample, ft_data, recursion_coeffs)

        assert np.allclose(fd_gradient, ft_gradient)
    def test_gradient_function_train_additive_function(self):
        """
        Test the gradient of a function train representation of an additive
        function. Gradient is with respect to coefficients of the univariate
        functions

        Assume different parameterization for some univariate functions.
        Zero and ones are stored as a constant basis. Where as other entries
        are stored as a polynomial of a fixed degree d.
        """
        alpha = 0
        beta = 0
        degree = 2
        num_vars = 3
        recursion_coeffs = jacobi_recurrence(degree + 1,
                                             alpha=alpha,
                                             beta=beta,
                                             probability=True)

        univariate_function_params = [np.random.normal(0., 1., (degree + 1))
                                      ] * num_vars
        ft_data = generate_additive_function_in_function_train_format(
            univariate_function_params, True)

        sample = np.random.uniform(-1., 1., (num_vars, 1))
        value, ft_gradient = evaluate_function_train_grad(
            sample, ft_data, recursion_coeffs)

        true_values, univariate_values = additive_polynomial(
            sample,
            univariate_function_params,
            recursion_coeffs,
            return_univariate_vals=True)
        true_value = true_values[0, 0]
        assert np.allclose(value, true_value)

        true_gradient = np.empty((0), dtype=float)
        # var 0 univariate function 1,1
        basis_matrix_var_0 = evaluate_orthonormal_polynomial_1d(
            sample[0, :], degree, recursion_coeffs)
        true_gradient = np.append(true_gradient, basis_matrix_var_0)
        # var 0 univariate function 1,2
        true_gradient = np.append(true_gradient,
                                  np.sum(univariate_values[0, 1:]))

        basis_matrix_var_1 = evaluate_orthonormal_polynomial_1d(
            sample[1, :], degree, recursion_coeffs)
        # var 1 univariate function 1,1
        true_gradient = np.append(true_gradient, univariate_values[0, 0])
        # var 1 univariate function 2,1
        true_gradient = np.append(true_gradient, basis_matrix_var_1)
        # var 1 univariate function 1,2
        true_gradient = np.append(
            true_gradient, univariate_values[0, 0] * univariate_values[0, 2])
        # var 1 univariate function 2,2
        true_gradient = np.append(true_gradient, univariate_values[0, 2])

        basis_matrix_var_2 = evaluate_orthonormal_polynomial_1d(
            sample[2, :], degree, recursion_coeffs)
        # var 2 univariate function 1,1
        true_gradient = np.append(true_gradient,
                                  univariate_values[0, :2].sum())
        # var 2 univariate function 2,1
        true_gradient = np.append(true_gradient, basis_matrix_var_2)

        fd_gradient = ft_parameter_finite_difference_gradient(
            sample, ft_data, recursion_coeffs)

        # print 'true',true_gradient
        # print 'ft  ',ft_gradient
        # print fd_gradient
        assert np.allclose(fd_gradient, true_gradient)
        assert np.allclose(ft_gradient, true_gradient)
    def test_restricted_least_squares_regression_sparse(self):
        """
        Use non-linear least squares to estimate the coefficients of the
        function train approximation of a rank-2 bivariate function,
        optimizing only over a subset of the FT parameters.
        """
        alpha = 0
        beta = 0
        degree = 5
        num_vars = 3
        rank = 2
        num_samples = 20
        sparsity_ratio = 0.2
        recursion_coeffs = jacobi_recurrence(degree + 1,
                                             alpha=alpha,
                                             beta=beta,
                                             probability=True)

        ranks = np.ones((num_vars + 1), dtype=int)
        ranks[1:-1] = rank

        ft_data = generate_random_sparse_function_train(
            num_vars, rank, degree + 1, sparsity_ratio)

        def function(samples):
            return evaluate_function_train(samples, ft_data, recursion_coeffs)

        samples = np.random.uniform(-1, 1, (num_vars, num_samples))
        values = function(samples)
        assert values.shape[0] == num_samples

        active_indices = np.where((ft_data[1] != 0) & (ft_data[1] != 1))[0]

        linear_ft_data = ft_linear_least_squares_regression(samples,
                                                            values,
                                                            degree,
                                                            perturb=None)
        initial_guess = linear_ft_data[1].copy()
        initial_guess = initial_guess[active_indices]
        # initial_guess = true_sol[active_indices] + np.random.normal(
        #    0.,1.,(active_indices.shape[0]))

        lstsq_ft_params = ft_non_linear_least_squares_regression(
            samples,
            values,
            ft_data,
            recursion_coeffs,
            initial_guess,
            active_indices=active_indices)
        lstsq_ft_data = copy.deepcopy(ft_data)
        lstsq_ft_data[1] = lstsq_ft_params

        num_valid_samples = 100
        validation_samples = np.random.uniform(-1., 1.,
                                               (num_vars, num_valid_samples))
        validation_values = function(validation_samples)

        ft_validation_values = evaluate_function_train(validation_samples,
                                                       lstsq_ft_data,
                                                       recursion_coeffs)
        ft_error = np.linalg.norm(validation_values - ft_validation_values
                                  ) / np.sqrt(num_valid_samples)
        # print ft_error
        assert ft_error < 1e-3, ft_error
    def test_restricted_least_squares_regression_additive_function(self):
        """
        Use non-linear least squares to estimate the coefficients of the
        function train approximation of a rank-2 bivariate function,
        optimizing only over a subset of the FT parameters.
        """
        alpha = 0
        beta = 0
        degree = 5
        num_vars = 3
        rank = 2
        num_samples = 20
        recursion_coeffs = jacobi_recurrence(degree + 1,
                                             alpha=alpha,
                                             beta=beta,
                                             probability=True)

        ranks = np.ones((num_vars + 1), dtype=int)
        ranks[1:-1] = rank
        num_params_1d = degree + 1

        univariate_function_params = [np.random.normal(0., 1., (degree + 1))
                                      ] * num_vars
        ft_data = generate_additive_function_in_function_train_format(
            univariate_function_params, False)

        def function(samples):
            return evaluate_function_train(samples, ft_data, recursion_coeffs)

        samples = np.random.uniform(-1, 1, (num_vars, num_samples))
        values = function(samples)
        assert values.shape[0] == num_samples

        linear_ft_data = ft_linear_least_squares_regression(samples,
                                                            values,
                                                            degree,
                                                            perturb=None)

        initial_guess = linear_ft_data[1].copy()

        active_indices = []
        active_indices += list(range(num_params_1d))
        active_indices += list(range(3 * num_params_1d, 4 * num_params_1d))
        active_indices += list(range(7 * num_params_1d, 8 * num_params_1d))
        active_indices = np.asarray(active_indices)
        # active_indices = np.where((ft_data[1]!=0)&(ft_data[1]!=1))[0]
        initial_guess = initial_guess[active_indices]

        lstsq_ft_params = ft_non_linear_least_squares_regression(
            samples,
            values,
            linear_ft_data,
            recursion_coeffs,
            initial_guess,
            active_indices=active_indices)
        lstsq_ft_data = copy.deepcopy(linear_ft_data)
        lstsq_ft_data[1] = lstsq_ft_params

        num_valid_samples = 100
        validation_samples = np.random.uniform(-1., 1.,
                                               (num_vars, num_valid_samples))
        validation_values = function(validation_samples)

        ft_validation_values = evaluate_function_train(validation_samples,
                                                       lstsq_ft_data,
                                                       recursion_coeffs)
        ft_error = np.linalg.norm(validation_values - ft_validation_values
                                  ) / np.sqrt(num_valid_samples)
        assert ft_error < 1e-3, ft_error
    def test_least_squares_regression(self):
        """
        Use non-linear least squares to estimate the coefficients of the
        function train approximation of a rank-2 bivariate function.
        """
        alpha = 0
        beta = 0
        degree = 5
        num_vars = 3
        rank = 2
        num_samples = 100
        recursion_coeffs = jacobi_recurrence(degree + 1,
                                             alpha=alpha,
                                             beta=beta,
                                             probability=True)

        ranks = np.ones((num_vars + 1), dtype=int)
        ranks[1:-1] = rank

        def function(samples):
            return np.cos(samples.sum(axis=0))[:, np.newaxis]

        samples = np.random.uniform(-1, 1, (num_vars, num_samples))
        values = function(samples)
        assert values.shape[0] == num_samples

        linear_ft_data = ft_linear_least_squares_regression(samples,
                                                            values,
                                                            degree,
                                                            perturb=None)

        initial_guess = linear_ft_data[1].copy()

        # test jacobian
        # residual_func = partial(
        #    least_squares_residual,samples,values,linear_ft_data,
        #    recursion_coeffs)
        #
        # jacobian = least_squares_jacobian(
        #     samples,values,linear_ft_data,recursion_coeffs,initial_guess)
        # finite difference is expensive check on subset of points
        # for ii in range(2):
        #    func = lambda x: residual_func(x)[ii]
        #    assert np.allclose(
        #        scipy.optimize.approx_fprime(initial_guess, func, 1e-7),
        #        jacobian[ii,:])

        lstsq_ft_params = ft_non_linear_least_squares_regression(
            samples, values, linear_ft_data, recursion_coeffs, initial_guess)
        lstsq_ft_data = copy.deepcopy(linear_ft_data)
        lstsq_ft_data[1] = lstsq_ft_params

        num_valid_samples = 100
        validation_samples = np.random.uniform(-1., 1.,
                                               (num_vars, num_valid_samples))
        validation_values = function(validation_samples)

        ft_validation_values = evaluate_function_train(validation_samples,
                                                       lstsq_ft_data,
                                                       recursion_coeffs)
        ft_error = np.linalg.norm(validation_values - ft_validation_values
                                  ) / np.sqrt(num_valid_samples)
        assert ft_error < 1e-3, ft_error

        # compare against tensor-product linear least squares
        from pyapprox.monomial import monomial_basis_matrix, evaluate_monomial
        from pyapprox.indexing import tensor_product_indices
        indices = tensor_product_indices([degree] * num_vars)
        basis_matrix = monomial_basis_matrix(indices, samples)
        coef = np.linalg.lstsq(basis_matrix, values, rcond=None)[0]
        monomial_validation_values = evaluate_monomial(indices, coef,
                                                       validation_samples)
        monomial_error = np.linalg.norm(validation_values -
                                        monomial_validation_values) / np.sqrt(
                                            num_valid_samples)
        assert ft_error < monomial_error
    def test_evaluate_multivariate_orthonormal_polynomial(self):
        num_vars = 2
        alpha = 0.
        beta = 0.
        degree = 2
        deriv_order = 1
        probability_measure = True

        ab = jacobi_recurrence(degree + 1,
                               alpha=alpha,
                               beta=beta,
                               probability=probability_measure)

        x, w = np.polynomial.legendre.leggauss(degree)
        samples = cartesian_product([x] * num_vars, 1)

        indices = compute_hyperbolic_indices(num_vars, degree, 1.0)

        # sort lexographically to make testing easier
        II = np.lexsort((indices[0, :], indices[1, :], indices.sum(axis=0)))
        indices = indices[:, II]

        basis_matrix = evaluate_multivariate_orthonormal_polynomial(
            samples, indices, ab, deriv_order)

        exact_basis_vals_1d = []
        exact_basis_derivs_1d = []
        for dd in range(num_vars):
            x = samples[dd, :]
            exact_basis_vals_1d.append(
                np.asarray([1 + 0. * x, x, 0.5 * (3. * x**2 - 1)]).T)
            exact_basis_derivs_1d.append(
                np.asarray([0. * x, 1.0 + 0. * x, 3. * x]).T)
            exact_basis_vals_1d[-1] /= np.sqrt(1. /
                                               (2 * np.arange(degree + 1) + 1))
            exact_basis_derivs_1d[-1] /= np.sqrt(
                1. / (2 * np.arange(degree + 1) + 1))

        exact_basis_matrix = np.asarray([
            exact_basis_vals_1d[0][:, 0], exact_basis_vals_1d[0][:, 1],
            exact_basis_vals_1d[1][:, 1], exact_basis_vals_1d[0][:, 2],
            exact_basis_vals_1d[0][:, 1] * exact_basis_vals_1d[1][:, 1],
            exact_basis_vals_1d[1][:, 2]
        ]).T

        # x1 derivative
        exact_basis_matrix = np.vstack(
            (exact_basis_matrix,
             np.asarray([
                 0. * x, exact_basis_derivs_1d[0][:, 1], 0. * x,
                 exact_basis_derivs_1d[0][:, 2],
                 exact_basis_derivs_1d[0][:, 1] * exact_basis_vals_1d[1][:, 1],
                 0. * x
             ]).T))

        # x2 derivative
        exact_basis_matrix = np.vstack(
            (exact_basis_matrix,
             np.asarray([
                 0. * x, 0. * x, exact_basis_derivs_1d[1][:, 1], 0. * x,
                 exact_basis_vals_1d[0][:, 1] * exact_basis_derivs_1d[1][:, 1],
                 exact_basis_derivs_1d[1][:, 2]
             ]).T))

        def func(x):
            return evaluate_multivariate_orthonormal_polynomial(
                x, indices, ab, 0)

        basis_matrix_derivs = basis_matrix[samples.shape[1]:]
        basis_matrix_derivs_fd = np.empty_like(basis_matrix_derivs)
        for ii in range(samples.shape[1]):
            basis_matrix_derivs_fd[ii::samples.shape[1], :] = approx_fprime(
                samples[:, ii:ii + 1], func, 1e-7)
        assert np.allclose(exact_basis_matrix[samples.shape[1]:],
                           basis_matrix_derivs_fd)

        assert np.allclose(exact_basis_matrix, basis_matrix)
Example #22
0
    def test_sparse_function_train(self):
        np.random.seed(5)
        num_vars = 2
        degree = 5
        rank = 2

        tol = 1e-5

        sparsity_ratio = 0.2
        sample_ratio = 0.6

        ranks = rank*np.ones(num_vars+1, dtype=np.uint64)
        ranks[0] = 1
        ranks[-1] = 1

        alpha = 0
        beta = 0
        recursion_coeffs = jacobi_recurrence(
            degree+1, alpha=alpha, beta=beta, probability=True)

        ft_data = generate_random_sparse_function_train(
            num_vars, rank, degree+1, sparsity_ratio)
        true_sol = ft_data[1]

        num_ft_params = true_sol.shape[0]
        num_samples = int(sample_ratio*num_ft_params)
        samples = np.random.uniform(-1., 1., (num_vars, num_samples))

        def function(samples): return evaluate_function_train(
            samples, ft_data, recursion_coeffs)
        #function = lambda samples: np.cos(samples.sum(axis=0))[:,np.newaxis]

        values = function(samples)
        print(values.shape)

        assert np.linalg.norm(values) > 0, (np.linalg.norm(values))

        num_validation_samples = 100
        validation_samples = np.random.uniform(
            -1., 1., (num_vars, num_validation_samples))
        validation_values = function(validation_samples)

        zero_ft_data = copy.deepcopy(ft_data)
        zero_ft_data[1] = np.zeros_like(zero_ft_data[1])
        # DO NOT use ft_data in following two functions.
        # These function only overwrites parameters associated with the
        # active indices the rest of the parameters are taken from ft_data.
        # If ft_data is used some of the true data will be kept and give
        # an unrealisticaly accurate answer
        approx_eval = partial(modify_and_evaluate_function_train, samples,
                              zero_ft_data, recursion_coeffs, None)

        apply_approx_adjoint_jacobian = partial(
            apply_function_train_adjoint_jacobian, samples, zero_ft_data,
            recursion_coeffs, 1e-3)

        def least_squares_regression(indices, initial_guess):
            # if initial_guess is None:
            st0 = np.random.get_state()
            np.random.seed(1)
            initial_guess = np.random.normal(0., .01, indices.shape[0])
            np.random.set_state(st0)
            result = ft_non_linear_least_squares_regression(
                samples, values, ft_data, recursion_coeffs, initial_guess,
                indices, {'gtol': tol, 'ftol': tol, 'xtol': tol, 'verbosity': 0})
            return result[indices]

        sparsity = np.where(true_sol != 0)[0].shape[0]
        print(('sparsity', sparsity, 'num_samples', num_samples,
               'num_ft_params', num_ft_params))

        print(true_sol)
        active_indices = None

        use_omp = True
        #use_omp = False
        if not use_omp:
            sol = least_squares_regression(np.arange(num_ft_params), None)
        else:
            result = orthogonal_matching_pursuit(
                approx_eval, apply_approx_adjoint_jacobian,
                least_squares_regression, values[:, 0], active_indices,
                num_ft_params, tol, min(num_samples, num_ft_params), verbosity=1)
            sol = result[0]
            residnorm = result[1]

        recovered_ft_data = copy.deepcopy(ft_data)
        recovered_ft_data[1] = sol
        ft_validation_values = evaluate_function_train(
            validation_samples, recovered_ft_data, recursion_coeffs)

        validation_error = np.linalg.norm(
            validation_values-ft_validation_values)
        rel_validation_error = validation_error / \
            np.linalg.norm(validation_values)
        # compare relative error because exit condition is based upon
        # relative residual
        print(rel_validation_error)
        assert rel_validation_error < 100*tol, rel_validation_error
Example #23
0
    def test_random_function_train(self):
        np.random.seed(5)
        num_vars = 2
        degree = 5
        rank = 2

        sparsity_ratio = 0.2
        sample_ratio = .9

        ranks = rank*np.ones(num_vars+1, dtype=np.uint64)
        ranks[0] = 1
        ranks[-1] = 1

        alpha = 0
        beta = 0
        recursion_coeffs = jacobi_recurrence(
            degree+1, alpha=alpha, beta=beta, probability=True)

        ft_data = generate_random_sparse_function_train(
            num_vars, rank, degree+1, sparsity_ratio)
        true_sol = ft_data[1]

        num_ft_params = true_sol.shape[0]
        num_samples = int(sample_ratio*num_ft_params)
        samples = np.random.uniform(-1., 1., (num_vars, num_samples))

        def function(samples): return evaluate_function_train(
            samples, ft_data, recursion_coeffs)

        values = function(samples)

        assert np.linalg.norm(values) > 0, (np.linalg.norm(values))

        num_validation_samples = 100
        validation_samples = np.random.uniform(
            -1., 1., (num_vars, num_validation_samples))
        validation_values = function(validation_samples)

        zero_ft_data = copy.deepcopy(ft_data)
        zero_ft_data[1] = np.zeros_like(zero_ft_data[1])
        # DO NOT use ft_data in following two functions.
        # These function only overwrites parameters associated with the
        # active indices the rest of the parameters are taken from ft_data.
        # If ft_data is used some of the true data will be kept and give
        # an unrealisticaly accurate answer
        approx_eval = partial(modify_and_evaluate_function_train, samples,
                              zero_ft_data, recursion_coeffs, None)

        apply_approx_adjoint_jacobian = partial(
            apply_function_train_adjoint_jacobian, samples, zero_ft_data,
            recursion_coeffs, 1e-3)

        sparsity = np.where(true_sol != 0)[0].shape[0]
        print(('sparsity', sparsity, 'num_samples', num_samples))

        # sparse
        project = partial(s_sparse_projection, sparsity=sparsity)
        # non-linear least squres
        #project = partial(s_sparse_projection,sparsity=num_ft_params)

        # use uninormative initial guess
        #initial_guess = np.zeros_like(true_sol)

        # use linear approximation as initial guess
        linear_ft_data = ft_linear_least_squares_regression(
            samples, values, degree, perturb=None)
        initial_guess = linear_ft_data[1]

        # use initial guess that is close to true solution
        # num_samples required to obtain accruate answer decreases signficantly
        # over linear or uniformative guesses. As size of perturbation from
        # truth increases num_samples must increase
        initial_guess = true_sol.copy()+np.random.normal(0., .1, (num_ft_params))

        tol = 5e-3
        max_iter = 1000
        result = iterative_hard_thresholding(
            approx_eval, apply_approx_adjoint_jacobian, project,
            values[:, 0], initial_guess, tol, max_iter, verbosity=1)
        sol = result[0]
        residnorm = result[1]

        recovered_ft_data = copy.deepcopy(ft_data)
        recovered_ft_data[1] = sol
        ft_validation_values = evaluate_function_train(
            validation_samples, recovered_ft_data, recursion_coeffs)

        validation_error = np.linalg.norm(
            validation_values-ft_validation_values)
        rel_validation_error = validation_error / \
            np.linalg.norm(validation_values)
        # compare relative error because exit condition is based upon
        # relative residual
        assert rel_validation_error < 10*tol, rel_validation_error
Example #24
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