def test_group_like_terms(self):
        num_vars = 2; degree = 2

        # define two set of indices that have a non-empty intersection
        indices1 = compute_hyperbolic_indices(num_vars, degree, 1.0)
        indices2 = compute_hyperbolic_indices(num_vars, degree-1, 1.0)
        num_indices1 = indices1.shape[1]
        coeffs = np.arange(num_indices1+indices2.shape[1])
        indices1 = np.hstack((indices1,indices2))

        # make it so coefficients increase by 1 with lexiographical order of
        # combined indices
        indices = indices1[:,argsort_indices_leixographically(indices1)]
        coeffs, indices = group_like_terms(coeffs, indices)

        # Check that only unique indices remain
        assert indices.shape[1]==num_indices1
        #print_indices(indices,num_vars)
        true_indices = np.asarray([[0,0],[0,1],[1,0],[0,2],[1,1],[2,0]]).T
        sorted_idx = argsort_indices_leixographically(indices)
        assert np.allclose(true_indices,indices[:,sorted_idx])

        # check that the coefficients of the unique indices are the sum of
        # all original common indices
        true_coeffs = [1,5,9,6,7,8]
        assert np.allclose(coeffs[sorted_idx][:,0],true_coeffs)
Example #2
0
    def test_coeffs_of_active_subspace_polynomial(self):
        num_vars = 3
        num_active_vars = 2
        degree = 4
        A = np.random.normal(0, 1, (num_vars, num_vars))
        Q, R = np.linalg.qr(A)
        W1 = Q[:, :num_active_vars]
        W1 = np.array([[1, 2, 3], [4, 5, 6]]).T
        as_poly_indices = compute_hyperbolic_indices(
            num_active_vars, degree, 1.0)
        sorted_as_poly_idx = argsort_indices_leixographically(as_poly_indices)

        # (dx+ey+fz)^2
        coeffs, indices = coeffs_of_active_subspace_polynomial(
            W1.T, as_poly_indices[:, sorted_as_poly_idx[3]])
        sorted_idx = argsort_indices_leixographically(indices)
        true_coeffs = np.array([W1[2, 1]**2, 2*W1[1, 1]*W1[2, 1], W1[1, 1]**2,
                                2*W1[0, 1]*W1[2, 1], 2*W1[0, 1]*W1[1, 1],
                                W1[0, 1]**2])
        assert np.allclose(true_coeffs, coeffs[sorted_idx])

        # (ax+by+cz)*(dx+ey+fz)=
        # a d x^2+a e x y+a f x z+b d x y+b e y^2+b f y z+c d x z+c e y z + c f z^2=
        # cfz^2 + (ce+bf)yz + bey^2 + (af+cd)xz + (ae+bd)xy + adx^2
        coeffs, indices = coeffs_of_active_subspace_polynomial(
            W1.T, as_poly_indices[:, sorted_as_poly_idx[4]])
        indices, coeffs = group_like_terms(coeffs, indices)
        sorted_idx = argsort_indices_leixographically(indices)
        a, b, c = W1[:, 0]
        d, e, f = W1[:, 1]
        true_coeffs = np.array([c*f, c*e+b*f, b*e, a*f+c*d, a*e+b*d, a*d])
        assert np.allclose(true_coeffs, coeffs[sorted_idx].squeeze())

        # (ax+by+cz)^4
        # a^4 x^4 + 4 a^3 c x^3 z + 4 b a^3 x^3 y + 6 a^2 c^2 x^2 z^2 + 12 b a^2 c x^2 y z + 6 b^2 a^2 x^2 y^2 + 4 a c^3 x z^3 + 12 b a c^2 x y z^2 + 12 b^2 a c x y^2 z + 4 b^3 a x y^3 + c^4 z^4 + 4 b c^3 y z^3 + 6 b^2 c^2 y^2 z^2 + 4 b^3 c y^3 z + b^4 y^4
        coeffs, indices = coeffs_of_active_subspace_polynomial(
            W1.T, as_poly_indices[:, sorted_as_poly_idx[14]])
        sorted_idx = argsort_indices_leixographically(indices)
        #print_sorted_indices(indices, num_vars, sorted_idx)
        true_coeffs = np.array(
            [c**4, 4.*b*c**3, 6.*b**2*c**2, 4.*b**3*c, b**4, 4*a*c**3,
             12.*b*a*c**2, 12.*b**2*a*c, 4.*b**3*a, 6.*a**2*c**2,
             12.*b*a**2*c, 6*b**2*a**2, 4*a**3*c, 4*b*a**3, a**4])
        assert np.allclose(true_coeffs, coeffs[sorted_idx])

        # (dx+ey+fz)^4
        # d^4 x^4 + 4 d^3 f x^3 z + 4 e d^3 x^3 y + 6 d^2 f^2 x^2 z^2 + 12 e d^2 f x^2 y z + 6 e^2 d^2 x^2 y^2 + 4 d f^3 x z^3 + 12 e d f^2 x y z^2 + 12 e^2 d f x y^2 z + 4 e^3 d x y^3 + f^4 z^4 + 4 e f^3 y z^3 + 6 e^2 f^2 y^2 z^2 + 4 e^3 f y^3 z + e^4 y^4
        coeffs, indices = coeffs_of_active_subspace_polynomial(
            W1.T, as_poly_indices[:, sorted_as_poly_idx[10]])
        sorted_idx = argsort_indices_leixographically(indices)
        true_coeffs = np.array(
            [f**4, 4.*e*f**3, 6.*e**2*f**2, 4.*e**3*f, e**4, 4*d*f**3,
             12.*e*d*f**2, 12.*e**2*d*f, 4.*e**3*d, 6.*d**2*f**2,
             12.*e*d**2*f, 6*e**2*d**2, 4*d**3*f, 4*e*d**3, d**4])
        assert np.allclose(true_coeffs, coeffs[sorted_idx])
Example #3
0
    def test_moments_of_active_subspace(self):
        num_vars = 3
        num_active_vars = 2
        degree = 2
        A = np.random.normal(0, 1, (num_vars, num_vars))
        Q, R = np.linalg.qr(A)
        W1 = Q[:, :num_active_vars]

        as_poly_indices = compute_hyperbolic_indices(num_active_vars, degree,
                                                     1.0)
        moments = moments_of_active_subspace(W1.T, as_poly_indices,
                                             monomial_mean_uniform_variables)

        sorted_idx = argsort_indices_leixographically(as_poly_indices)
        # (ax+by+cz)^2=a^2 x^2 + 2 a b x y + 2 a c x z + b^2 y^2 + 2 b c y z + c^2 z^2
        # int (ax+by+cz)^2*1/2dx = a^2*1/3 + b^2*1/3 + c^2*1/3
        true_moments = [1, 0, 0,
                        # notice how if W1 has colums with unit norm np.sum(W1[:,1]**2) will always be one.
                        np.sum(W1[:, 1]**2)*1./3.,
                        1./3.*(W1[0, 0]*W1[0, 1]+W1[1, 0] * \
                               W1[1, 1]+W1[2, 0]*W1[2, 1]),
                        np.sum(W1[:, 0]**2)*1./3.]
        assert np.allclose(moments[sorted_idx], true_moments)

        num_vars = 3
        num_active_vars = 2
        degree = 4
        A = np.random.normal(0, 1, (num_vars, num_vars))
        Q, R = np.linalg.qr(A)
        W1 = Q[:, :num_active_vars]
        W1[:, 0] = [1, 2, 3]
        W1[:, 1] = [4, 5, 6]
        as_poly_indices = compute_hyperbolic_indices(num_active_vars, degree,
                                                     1.0)
        moments = moments_of_active_subspace(W1.T, as_poly_indices,
                                             monomial_mean_uniform_variables)
        sorted_idx = argsort_indices_leixographically(as_poly_indices)
        a, b, c = W1[:, 0]
        d, e, f = W1[:, 1]
        dummy = np.inf  # yet to analytically compute true moments for these indices
        true_moments = np.array([
            1, 0, 0,
            np.sum(W1[:, 1]**2) * 1. / 3., 1. / 3. * (c * f + b * e + a * d),
            np.sum(W1[:, 0]**2) * 1. / 3., 0., 0., 0., 0.,
            (3 * d**4 + 3 * e**4 + 10 * e**2 * f**2 + 3 * f**4 + 10 * d**2 *
             (e**2 + f**2)) / 15., dummy, dummy, dummy,
            (3 * a**4 + 3 * b**4 + 10 * b**2 * c**2 + 3 * c**4 + 10 * a**2 *
             (b**2 + c**2)) / 15.
        ])
        moments = moments[sorted_idx]
        # ignore dummy values until I compute them analytically
        I = np.where(true_moments != np.Inf)[0]
        assert np.allclose(moments[I], true_moments[I])
Example #4
0
    def test_add_polynomials(self):
        num_vars = 2
        degree = 2

        # define two set of indices that have a non-empty intersection
        indices1 = compute_hyperbolic_indices(num_vars, degree, 1.0)
        indices1 = indices1[:, argsort_indices_leixographically(indices1)]
        coeffs1 = np.arange(indices1.shape[1])[:, np.newaxis]
        indices2 = compute_hyperbolic_indices(num_vars, degree-1, 1.0)
        indices2 = indices2[:, argsort_indices_leixographically(indices2)]
        coeffs2 = np.arange(indices2.shape[1])[:, np.newaxis]

        indices, coeffs = add_polynomials(
            [indices2, indices1], [coeffs2, coeffs1])

        # check that the coefficients of the new polynomial are the union
        # of the original polynomials
        true_indices = np.asarray(
            [[0, 0], [0, 1], [1, 0], [0, 2], [1, 1], [2, 0]]).T
        sorted_idx = argsort_indices_leixographically(indices)
        assert np.allclose(true_indices, indices[:, sorted_idx])

        # check that the coefficients of the new polynomials are the sum of
        # all original polynomials
        true_coeffs = np.asarray([[0, 2, 4, 3, 4, 5]]).T
        assert np.allclose(coeffs[sorted_idx], true_coeffs)

        num_vars = 2
        degree = 2

        # define two set of indices that have a non-empty intersection
        indices3 = compute_hyperbolic_indices(num_vars, degree+1, 1.0)
        indices3 = indices3[:, argsort_indices_leixographically(indices3)]
        coeffs3 = np.arange(indices3.shape[1])[:, np.newaxis]

        indices, coeffs = add_polynomials(
            [indices2, indices1, indices3], [coeffs2, coeffs1, coeffs3])

        # check that the coefficients of the new polynomial are the union
        # of the original polynomials
        true_indices = np.asarray(
            [[0, 0], [0, 1], [1, 0], [0, 2], [1, 1], [2, 0], [0, 3], [1, 2], [2, 1], [3, 0]]).T
        sorted_idx = argsort_indices_leixographically(indices)
        assert np.allclose(true_indices, indices[:, sorted_idx])

        # check that the coefficients of the new polynomials are the sum of
        # all original polynomials
        true_coeffs = np.asarray([[0, 3, 6, 6, 8, 10, 6, 7, 8, 9]]).T
        assert np.allclose(coeffs[sorted_idx], true_coeffs)
Example #5
0
def compress_and_sort_polynomial(coef, indices, tol=1e-12):
    I = np.where(np.absolute(coef) > tol)[0]
    indices = indices[:, I]
    coef = coef[I]
    J = argsort_indices_leixographically(indices)
    indices = indices[:, J]
    coef = coef[J, :]
    return indices, coef
    def test_coeffs_of_power_of_polynomial(self):
        num_vars,degree,power=1,2,3
        indices = compute_hyperbolic_indices(num_vars,degree,1.0)
        coeffs = np.ones((indices.shape[1],1))
        new_coeffs, new_indices = coeffs_of_power_of_polynomial(
            indices, coeffs, power)
        assert new_indices.shape[1]==10
        print(np.hstack((new_indices.T,new_coeffs)))
        true_indices = np.asarray([[0],[1],[2],[2],[3],[4],[3],[4],[5],[6]]).T
        true_coeffs  = np.asarray([[1,3,3,3,6,3,1,3,3,1]]).T
        # must include coefficient in sort because when there are mulitple
        # entries with same index, argsort can return different orders
        # if initial orders of indices is different.
        #new_sorted_idx = argsort_indices_leixographically(
        #    np.hstack([new_indices.T,new_coeffs]).T)
        #true_sorted_idx = argsort_indices_leixographically(
        #    np.hstack([true_indices.T,true_coeffs]).T)
        # alternatively just group like terms before sort
        new_coeffs, new_indices  = group_like_terms(new_coeffs,new_indices)
        true_coeffs,true_indices = group_like_terms(true_coeffs,true_indices)
        new_sorted_idx = argsort_indices_leixographically(new_indices)
        true_sorted_idx = argsort_indices_leixographically(true_indices)
        
        assert np.allclose(
            true_indices[:,true_sorted_idx],new_indices[:,new_sorted_idx])
        assert np.allclose(
            true_coeffs[true_sorted_idx],new_coeffs[new_sorted_idx])

        num_vars,degree,power=2,1,2
        indices = np.array([[0,0],[1,0],[0,1],[1,1]]).T
        coeffs = np.ones((indices.shape[1],1))
        new_coeffs, new_indices = coeffs_of_power_of_polynomial(
            indices, coeffs, power)
        true_indices = np.asarray(
            [[0,0],[1,0],[0,1],[1,1],[2,0],[1,1],[2,1],[0,2],[1,2],[2,2]]).T
        true_coeffs  = np.asarray([[1,2,2,2,1,2,2,1,2,1]]).T
        new_coeffs, new_indices  = group_like_terms(new_coeffs,new_indices)
        true_coeffs,true_indices = group_like_terms(true_coeffs,true_indices)
        new_sorted_idx = argsort_indices_leixographically(new_indices)
        true_sorted_idx = argsort_indices_leixographically(true_indices)
        assert np.allclose(
            true_indices[:,true_sorted_idx],new_indices[:,new_sorted_idx])
        assert np.allclose(
            true_coeffs[true_sorted_idx],new_coeffs[new_sorted_idx])
 def test_coeffs_of_power_of_nd_linear_polynomial(self):
     num_vars = 3; degree = 2
     linear_coeffs = [2.,3.,4]
     coeffs, indices = coeffs_of_power_of_nd_linear_polynomial(
         num_vars,degree,linear_coeffs)
     sorted_idx = argsort_indices_leixographically(indices)
     true_coeffs = [linear_coeffs[2]**2,2*linear_coeffs[1]*linear_coeffs[2],
                    linear_coeffs[1]**2,2*linear_coeffs[0]*linear_coeffs[2],
                    2*linear_coeffs[0]*linear_coeffs[1],linear_coeffs[0]**2]
     assert np.allclose(true_coeffs,coeffs[sorted_idx])
    def test_multinomial_coefficients(self):
        num_vars = 2; degree = 5
        coeffs, indices = multinomial_coeffs_of_power_of_nd_linear_polynomial(
            num_vars,degree)

        true_coeffs = np.empty(coeffs.shape[0],float)
        for i in range(0,degree+1):
            true_coeffs[i] = binom(degree,i)
        assert true_coeffs.shape[0]==coeffs.shape[0]
        coeffs=coeffs[argsort_indices_leixographically(indices)]
        assert np.allclose(coeffs, true_coeffs)

        num_vars=3; degree = 3
        coeffs, indices = multinomial_coeffs_of_power_of_nd_linear_polynomial(
            num_vars,degree)
        coeffs = multinomial_coefficients(indices)
        coeffs = coeffs[argsort_indices_leixographically(indices)]

        true_coeffs = np.array([1,3,3,1,3,6,3,3,3,1])
        assert np.allclose(coeffs, true_coeffs)
Example #9
0
    def test_multiply_multivariate_polynomials(self):
        num_vars = 2
        degree1 = 1
        degree2 = 2

        indices1 = compute_hyperbolic_indices(num_vars, degree1, 1.0)
        coeffs1 = np.ones((indices1.shape[1]), dtype=float)
        indices2 = compute_hyperbolic_indices(num_vars, degree2, 1.0)
        coeffs2 = 2.0 * np.ones((indices2.shape[1]), dtype=float)

        indices, coeffs = multiply_multivariate_polynomials(
            indices1, coeffs1, indices2, coeffs2)
        indices = indices[:, argsort_indices_leixographically(indices)]

        true_indices = compute_hyperbolic_indices(num_vars, degree1 + degree2,
                                                  1.0)
        true_indices = \
            true_indices[:,argsort_indices_leixographically(true_indices)]
        assert np.allclose(true_indices, indices)
        true_coeffs = np.array([2, 4, 4, 4, 4, 6, 2, 4, 4, 2])
        assert np.allclose(true_coeffs, coeffs)
Example #10
0
    def test_argsort_indices_leixographically(self):
        num_vars = 2
        degree = 2
        indices = compute_hyperbolic_indices(num_vars, degree, 1.0)
        sorted_idx = argsort_indices_leixographically(indices)

        sorted_indices = indices[:, sorted_idx]
        true_sorted_indices = np.array([[0, 0], [0, 1], [1, 0], [0, 2], [1, 1],
                                        [2, 0]]).T
        assert np.allclose(sorted_indices, true_sorted_indices)

        num_vars = 3
        degree = 2
        indices = compute_hyperbolic_indices(num_vars, degree, 1.0)
        sorted_idx = argsort_indices_leixographically(indices)

        sorted_indices = indices[:, sorted_idx]
        true_sorted_indices = np.array([[0, 0, 0], [0, 0, 1], [0, 1, 0],
                                        [1, 0, 0], [0, 0, 2], [0, 1, 1],
                                        [0, 2, 0], [1, 0, 1], [1, 1, 0],
                                        [2, 0, 0]]).T
        assert np.allclose(sorted_indices, true_sorted_indices)
def sampling_based_sobol_indices(fun,
                                 variables,
                                 interaction_terms,
                                 nsamples,
                                 sampling_method='sobol',
                                 qmc_start_index=0):
    """
    See I.M. Sobol. Mathematics and Computers in Simulation 55 (2001) 271–280

    and  

    Saltelli, Annoni et. al, Variance based sensitivity analysis of model 
    output. Design and estimator for the total sensitivity index. 2010.
    https://doi.org/10.1016/j.cpc.2009.09.018

    Parameters
    ----------
    interaction_terms : np.ndarray (nvars, nterms)
        Index defining the active terms in each interaction. If the
        ith  variable is active interaction_terms[i] == 1 and zero otherwise
        This index must be downward closed due to way sobol indices are computed
    """
    nvars = interaction_terms.shape[0]
    nterms = interaction_terms.shape[1]
    samplesA, samplesB = get_AB_sample_sets_for_sobol_sensitivity_analysis(
        variables, nsamples, sampling_method, qmc_start_index)
    assert nvars == samplesA.shape[0]
    valuesA = fun(samplesA)
    valuesB = fun(samplesB)
    mean = valuesA.mean(axis=0)
    variance = valuesA.var(axis=0)
    interaction_values = np.empty((nterms, valuesA.shape[1]))
    total_effect_values = [None for ii in range(nvars)]
    interaction_values_dict = dict()
    for ii in range(nterms):
        index = interaction_terms[:, ii]
        assert index.sum() > 0
        samplesAB = generate_sobol_index_sample_sets(samplesA, samplesB, index)
        valuesAB = fun(samplesAB)
        # entry b in Table 2 of Saltelli, Annoni et. al
        interaction_values[ii, :] = \
            (valuesB*(valuesAB-valuesA)).mean(axis=0)/variance
        interaction_values_dict[tuple(np.where(index > 0)[0])] = ii
        if index.sum() == 1:
            dd = np.where(index == 1)[0][0]
            # entry f in Table 2 of Saltelli, Annoni et. al
            total_effect_values[dd] = 0.5 * \
                np.mean((valuesA-valuesAB)**2, axis=0)/variance

    # must substract of contributions from lower-dimensional terms from
    # each interaction value For example, let R_ij be interaction_values
    # the sobol index S_ij satisfies R_ij = S_i + S_j + S_ij
    from pyapprox.indexing import argsort_indices_leixographically
    I = argsort_indices_leixographically(interaction_terms)
    from itertools import combinations
    sobol_indices = interaction_values.copy()
    sobol_indices_dict = dict()
    for ii in range(I.shape[0]):
        index = interaction_terms[:, I[ii]]
        active_vars = np.where(index > 0)[0]
        nactive_vars = index.sum()
        sobol_indices_dict[tuple(active_vars)] = I[ii]
        if nactive_vars > 1:
            for jj in range(nactive_vars - 1):
                indices = combinations(active_vars, jj + 1)
                for key in indices:
                    sobol_indices[I[ii]] -= \
                        sobol_indices[sobol_indices_dict[key]]

    total_effect_values = np.asarray(total_effect_values)
    assert np.all(variance >= 0)
    main_effects = sobol_indices[interaction_terms.sum(axis=0) == 1, :]
    # We cannot guarantee that the main_effects will be <= 1. Because
    # variance and each interaction_index are computed with different sample
    # sets. Consider function of two variables which is constant in one variable
    # then interaction_index[0] should equal variance. But with different sample
    # sets interaction_index could be smaller or larger than the variance.
    # assert np.all(main_effects<=1)
    # Similarly we cannot even guarantee main effects will be non-negative
    # assert np.all(main_effects>=0)
    # We also cannot guarantee that the sobol indices will be non-negative.
    # assert np.all(total_effect_values>=0)
    # assert np.all(sobol_indices>=0)
    return sobol_indices, total_effect_values, variance, mean