Пример #1
0
    def fit(self, X, y, **fit_params):
        input_dimension = X.shape[1]
        if self.distribution is None:
            self.distribution = BuildDistribution(X)
        if self.enumerate == 'linear':
            enumerateFunction = ot.LinearEnumerateFunction(input_dimension)
        elif self.enumerate == 'hyperbolic':
            enumerateFunction = ot.HyperbolicAnisotropicEnumerateFunction(input_dimension, self.q)
        else:
            raise ValueError('enumerate should be "linear" or "hyperbolic"')
        polynomials = [ot.StandardDistributionPolynomialFactory(self.distribution.getMarginal(i)) for i in range(input_dimension)]
        productBasis = ot.OrthogonalProductPolynomialFactory(polynomials, enumerateFunction)
        adaptiveStrategy = ot.FixedStrategy(productBasis, enumerateFunction.getStrataCumulatedCardinal(self.degree))
        if self.sparse:
            projectionStrategy = ot.LeastSquaresStrategy(ot.LeastSquaresMetaModelSelectionFactory(ot.LARS(), ot.CorrectedLeaveOneOut()))
        else:
            projectionStrategy = ot.LeastSquaresStrategy(X, y.reshape(-1, 1))
        algo = ot.FunctionalChaosAlgorithm(X, y.reshape(-1, 1), self.distribution, adaptiveStrategy, projectionStrategy)
        algo.run()
        self._result = algo.getResult()
        output_dimension = self._result.getMetaModel().getOutputDimension()

        # sensitivity
        si = ot.FunctionalChaosSobolIndices(self._result)
        if output_dimension == 1:
            self.feature_importances_ = [si.getSobolIndex(i) for i in range(input_dimension)]
        else:
            self.feature_importances_ = [[0.0] * input_dimension] * output_dimension
            for k in range(output_dimension):
                for i in range(input_dimension):
                    self.feature_importances_[k][i] = si.getSobolIndex(i, k)
        self.feature_importances_ = np.array(self.feature_importances_)
        return self
Пример #2
0
    def fit(self, sample, data):
        """Create the predictor.

        The result of the Polynomial Chaos is stored as :attr:`pc_result` and
        the surrogate is stored as :attr:`pc`. It exposes :attr:`self.weights`,
        :attr:`self.coefficients` and Sobol' indices :attr:`self.s_first` and
        :attr:`self.s_total`.

        :param array_like sample: The sample used to generate the data
          (n_samples, n_features).
        :param array_like data: The observed data (n_samples, [n_features]).
        """
        trunc_strategy = ot.FixedStrategy(self.basis, self.n_basis)

        if self.strategy == 'LS':  # least-squares method
            proj_strategy = ot.LeastSquaresStrategy(sample, data)
            _, self.weights = proj_strategy.getExperiment().generateWithWeights()

        elif self.strategy == 'SparseLS':
            app = ot.LeastSquaresMetaModelSelectionFactory(ot.LARS(), ot.CorrectedLeaveOneOut())
            proj_strategy = ot.LeastSquaresStrategy(sample, data, app)
            _, self.weights = proj_strategy.getExperiment().generateWithWeights()

            max_considered_terms = self.sparse_param.get('max_considered_terms', 120)
            most_significant = self.sparse_param.get('most_significant', 30)
            significance_factor = self.sparse_param.get('significance_factor', 1e-3)

            trunc_strategy = ot.CleaningStrategy(ot.OrthogonalBasis(self.basis),
                                                 max_considered_terms,
                                                 most_significant,
                                                 significance_factor, True)
        else:
            proj_strategy = self.proj_strategy
            sample_ = np.zeros_like(self.sample)
            sample_[:len(sample)] = sample
            sample_arg = np.all(np.isin(sample_, self.sample), axis=1)
            self.weights = np.array(self.weights)[sample_arg]

        # PC fitting
        pc_algo = ot.FunctionalChaosAlgorithm(sample, self.weights, data,
                                              self.dist, trunc_strategy)
        pc_algo.setProjectionStrategy(proj_strategy)
        ot.Log.Show(ot.Log.ERROR)
        pc_algo.run()

        # Accessors
        self.pc_result = pc_algo.getResult()
        self.pc = self.pc_result.getMetaModel()
        self.coefficients = self.pc_result.getCoefficients()

        # sensitivity indices
        sobol = ot.FunctionalChaosSobolIndices(self.pc_result)
        self.s_first, self.s_total = [], []
        for i, j in product(range(self.in_dim), range(np.array(data).shape[1])):
            self.s_first.append(sobol.getSobolIndex(i, j))
            self.s_total.append(sobol.getSobolTotalIndex(i, j))

        self.s_first = np.array(self.s_first).reshape(self.in_dim, -1).T
        self.s_total = np.array(self.s_total).reshape(self.in_dim, -1).T
Пример #3
0
    def _buildChaosAlgo(self, inputSample, outputSample):
        """
        Build the functional chaos algorithm without running it.
        """
        if self._distribution is None:
            # create default distribution : Uniform between min and max of the
            # input sample
            inputSample = ot.NumericalSample(inputSample)
            inputMin = inputSample.getMin()
            inputMin[0] = np.min(self._defectSizes)
            inputMax = inputSample.getMax()
            inputMax[0] = np.max(self._defectSizes)
            marginals = [
                ot.Uniform(inputMin[i], inputMax[i]) for i in range(self._dim)
            ]
            self._distribution = ot.ComposedDistribution(marginals)

        # put description of the inputSample into decription of the distribution
        self._distribution.setDescription(inputSample.getDescription())

        if self._adaptiveStrategy is None:
            # Create the adaptive strategy : default is fixed strategy of degree 5
            # with linear enumerate function
            polyCol = [0.] * self._dim
            for i in range(self._dim):
                polyCol[i] = ot.StandardDistributionPolynomialFactory(
                    self._distribution.getMarginal(i))

            enumerateFunction = ot.EnumerateFunction(self._dim)
            multivariateBasis = ot.OrthogonalProductPolynomialFactory(
                polyCol, enumerateFunction)
            # default degree is 3 (in __init__)
            indexMax = enumerateFunction.getStrataCumulatedCardinal(
                self._degree)
            self._adaptiveStrategy = ot.FixedStrategy(multivariateBasis,
                                                      indexMax)

        if self._projectionStrategy is None:
            # sparse polynomial chaos
            basis_sequence_factory = ot.LAR()
            fitting_algorithm = ot.KFold()
            approximation_algorithm = ot.LeastSquaresMetaModelSelectionFactory(
                basis_sequence_factory, fitting_algorithm)
            self._projectionStrategy = ot.LeastSquaresStrategy(
                inputSample, outputSample, approximation_algorithm)

        return ot.FunctionalChaosAlgorithm(inputSample, outputSample, \
                self._distribution, self._adaptiveStrategy, self._projectionStrategy)
def ComputeSparseLeastSquaresChaos(inputTrain, outputTrain, multivariateBasis,
                                   totalDegree, myDistribution):
    """
    Create a sparse polynomial chaos based on least squares.
    
    * Uses the enumerate rule in multivariateBasis. 
    * Uses the LeastSquaresStrategy to compute the coefficients based on 
      least squares. 
    * Uses LeastSquaresMetaModelSelectionFactory to use the LARS selection method. 
    * Uses FixedStrategy in order to keep all the coefficients that the 
      LARS method selected.

    Parameters
    ----------
    inputTrain : ot.Sample
        The input design of experiments.
    outputTrain : ot.Sample
        The output design of experiments.
    multivariateBasis : ot.Basis
        The multivariate chaos basis.
    totalDegree : int
        The total degree of the chaos polynomial.
    myDistribution : ot.Distribution.
        The distribution of the input variable.
    Returns
    -------
    result : ot.PolynomialChaosResult
        The estimated polynomial chaos.
    """
    selectionAlgorithm = ot.LeastSquaresMetaModelSelectionFactory()
    projectionStrategy = ot.LeastSquaresStrategy(inputTrain, outputTrain,
                                                 selectionAlgorithm)
    enumfunc = multivariateBasis.getEnumerateFunction()
    P = enumfunc.getStrataCumulatedCardinal(totalDegree)
    adaptiveStrategy = ot.FixedStrategy(multivariateBasis, P)
    chaosalgo = ot.FunctionalChaosAlgorithm(inputTrain, outputTrain,
                                            myDistribution, adaptiveStrategy,
                                            projectionStrategy)
    chaosalgo.run()
    result = chaosalgo.getResult()
    return result
Пример #5
0
Y = myModel(X)
dimension = X.getDimension()

# %%
# build the orthogonal basis
coll = [
    ot.StandardDistributionPolynomialFactory(distribution.getMarginal(i))
    for i in range(dimension)
]
enumerateFunction = ot.LinearEnumerateFunction(dimension)
productBasis = ot.OrthogonalProductPolynomialFactory(coll, enumerateFunction)

# %%
# create the algorithm
degree = 6
adaptiveStrategy = ot.FixedStrategy(
    productBasis, enumerateFunction.getStrataCumulatedCardinal(degree))
projectionStrategy = ot.LeastSquaresStrategy()
algo = ot.FunctionalChaosAlgorithm(X, Y, distribution, adaptiveStrategy,
                                   projectionStrategy)
algo.run()

# %%
# get the metamodel function
result = algo.getResult()
metamodel = result.getMetaModel()

# %%
# Print residuals
result.getResiduals()
Пример #6
0
print('sampling size = ', N)
output_database = ishigami_model(input_database)

# Learning input/output
# Usual chaos meta model
enumerate_function = ot.HyperbolicAnisotropicEnumerateFunction(dimension)
orthogonal_basis = ot.OrthogonalProductPolynomialFactory(
    dimension * [ot.LegendreFactory()], enumerate_function)
basis_size = 100
# Initial chaos algorithm
adaptive_strategy = ot.FixedStrategy(orthogonal_basis, basis_size)
# ProjectionStrategy ==> Sparse
fitting_algorithm = ot.KFold()
approximation_algorithm = ot.LeastSquaresMetaModelSelectionFactory(
    ot.LARS(), fitting_algorithm)
projection_strategy = ot.LeastSquaresStrategy(input_database, output_database,
                                              approximation_algorithm)
print('Surrogate model...')
distribution_ishigami = ot.ComposedDistribution(dimension *
                                                [ot.Uniform(-pi, pi)])
algo_pc = ot.FunctionalChaosAlgorithm(input_database, output_database,
                                      distribution_ishigami, adaptive_strategy,
                                      projection_strategy)
algo_pc.run()
chaos_result = algo_pc.getResult()
print('Surrogate model computed')

# Validation
lhs_validation = ot.LHSExperiment(distribution_ishigami, 100)
input_validation = lhs_validation.generate()
output_validation = ishigami_model(input_validation)
# Chaos model evaluation
sample_xi_X = result_X.project(sample_X)

print("project sample_Y")
sample_xi_Y = result_Y.project(sample_Y)

print("Compute PCE between coefficients")
degree = 1
dimension_xi_X = sample_xi_X.getDimension()
dimension_xi_Y = sample_xi_Y.getDimension()
enumerateFunction = ot.LinearEnumerateFunction(dimension_xi_X)
basis = ot.OrthogonalProductPolynomialFactory(
    [ot.HermiteFactory()] * dimension_xi_X, enumerateFunction)
basisSize = enumerateFunction.getStrataCumulatedCardinal(degree)
adaptive = ot.FixedStrategy(basis, basisSize)
projection = ot.LeastSquaresStrategy(
    ot.LeastSquaresMetaModelSelectionFactory(ot.LARS(),
                                             ot.CorrectedLeaveOneOut()))
ot.ResourceMap.SetAsScalar("LeastSquaresMetaModelSelection-ErrorThreshold",
                           1.0e-7)
algo_chaos = ot.FunctionalChaosAlgorithm(sample_xi_X, sample_xi_Y,
                                         basis.getMeasure(), adaptive,
                                         projection)
algo_chaos.run()
result_chaos = algo_chaos.getResult()
meta_model = result_chaos.getMetaModel()
print("myConvolution=", myConvolution.getInputDimension(), "->",
      myConvolution.getOutputDimension())
preprocessing = ot.KarhunenLoeveProjection(result_X)
print("preprocessing=", preprocessing.getInputDimension(), "->",
      preprocessing.getOutputDimension())
print("meta_model=", meta_model.getInputDimension(), "->",
enumerateFunction = ot.LinearEnumerateFunction(dimension)
productBasis = ot.OrthogonalProductPolynomialFactory(
    polynomialCollection, enumerateFunction)

# Create the adaptive strategy
degree = 8
basisSize = enumerateFunction.getStrataCumulatedCardinal(degree)
adaptiveStrategy = ot.FixedStrategy(productBasis, basisSize)

# Select the fitting algorithm
fittingAlgorithm = ot.KFold()
leastSquaresFactory = ot.LeastSquaresMetaModelSelectionFactory(
    ot.LARS(), fittingAlgorithm)

# Projection strategy
projectionStrategy = ot.LeastSquaresStrategy(
    inputSample, outputSample, leastSquaresFactory)

algo = ot.FunctionalChaosAlgorithm(
    inputSample, outputSample, distribution, adaptiveStrategy, projectionStrategy)
# Reinitialize the RandomGenerator to see the effect of the sampling
# method only
ot.RandomGenerator.SetSeed(0)
algo.run()

# Get the results
result = algo.getResult()

# MetaModelValidation - SPC
metaModelValidationSPC = ot.MetaModelValidation(
    inputValidation, outputValidation, result.getMetaModel())
print("")
Пример #9
0
# %%
# Create a training sample

# %%
N = 100
inputTrain = im.distributionX.getSample(N)
outputTrain = im.model(inputTrain)

# %%
# Create the chaos.

# %%
multivariateBasis = ot.OrthogonalProductPolynomialFactory(
    [im.X1, im.X2, im.X3])
selectionAlgorithm = ot.LeastSquaresMetaModelSelectionFactory()
projectionStrategy = ot.LeastSquaresStrategy(inputTrain, outputTrain,
                                             selectionAlgorithm)
totalDegree = 8
enumfunc = multivariateBasis.getEnumerateFunction()
P = enumfunc.getStrataCumulatedCardinal(totalDegree)
adaptiveStrategy = ot.FixedStrategy(multivariateBasis, P)
chaosalgo = ot.FunctionalChaosAlgorithm(inputTrain, outputTrain,
                                        im.distributionX, adaptiveStrategy,
                                        projectionStrategy)

# %%
chaosalgo.run()
result = chaosalgo.getResult()
metamodel = result.getMetaModel()

# %%
# Print Sobol' indices
Пример #10
0
# Create the correlated input distribution
S = ot.CorrelationMatrix(2)
S[1, 0] = 0.3
R = ot.NormalCopula.GetCorrelationFromSpearmanCorrelation(S)
copula = ot.NormalCopula(R)
distribution_corr = ot.ComposedDistribution([ot.Normal()] * 2, copula)

# %%
# ANCOVA needs a functional decomposition of the model
enumerateFunction = ot.LinearEnumerateFunction(2)
productBasis = ot.OrthogonalProductPolynomialFactory([ot.HermiteFactory()] * 2,
                                                     enumerateFunction)
adaptiveStrategy = ot.FixedStrategy(
    productBasis, enumerateFunction.getStrataCumulatedCardinal(4))
samplingSize = 250
projectionStrategy = ot.LeastSquaresStrategy(
    ot.MonteCarloExperiment(samplingSize))
algo = ot.FunctionalChaosAlgorithm(model, distribution, adaptiveStrategy,
                                   projectionStrategy)
algo.run()
result = ot.FunctionalChaosResult(algo.getResult())

# %%
# Create the input sample taking account the correlation
size = 2000
sample = distribution_corr.getSample(size)

# %%
# Perform the decomposition
ancova = ot.ANCOVA(result, sample)
# Compute the ANCOVA indices (first order and uncorrelated indices are computed together)
indices = ancova.getIndices()
Пример #11
0
dim = model.getInputDimension()
print("dim=", dim)
size = dim + 1
distribution = ot.ComposedDistribution([ot.Normal()] * dim)
weightedExperiment = ot.MonteCarloExperiment(distribution, size)
inSample, weights = weightedExperiment.generateWithWeights()
print("Sample model")
t0 = time()
outSample = model(inSample)
t1 = time()
print("t=", t1 - t0, "s, speed=", inSample.getSize() / (t1 - t0), "evals/s")

basis = ot.OrthogonalProductPolynomialFactory([ot.HermiteFactory()] * dim)
adaptive = ot.FixedStrategy(basis, dim + 1)
projection = ot.LeastSquaresStrategy(weightedExperiment)
algo = ot.FunctionalChaosAlgorithm(inSample, outSample, distribution, adaptive,
                                   projection)
algo.run()
vector = ot.FunctionalChaosRandomVector(algo.getResult())

# Field of Sobol indices
#for i in range(dim):
for i in range(15, 16):
    print("i=", i)
    sobol = [
        vector.getSobolIndex(i, j) for j in range(mesh.getVerticesNumber())
    ]
    field = ot.Field(mesh, [[x] for x in sobol])
    graph = field.draw()
    graph.setTitle("Sobol index field - component " + str(i))
Пример #12
0
maximumConsideredTerms = 500
mostSignificant = 50
significanceFactor = 1.0e-4
truncatureBasisStrategy_2 = ot.CleaningStrategy(
    multivariateBasis, maximumConsideredTerms, mostSignificant, significanceFactor, True)

# %%
# STEP 3: Evaluation strategy of the approximation coefficients
# -------------------------------------------------------------

# %%
# The technique illustrated is the Least Squares technique where the points come from an design of experiments. Here : the Monte Carlo sampling technique.

# %%
sampleSize = 100
evaluationCoeffStrategy = ot.LeastSquaresStrategy(
    ot.MonteCarloExperiment(sampleSize))

# %%
# You can specify the approximation algorithm. This is the algorithm that generates a sequence of basis using Least Angle Regression.

# %%
basisSequenceFactory = ot.LARS()

# %%
# This algorithm estimates the empirical error on each sub-basis using Leave One Out strategy.

# %%
fittingAlgorithm = ot.CorrectedLeaveOneOut()
# Finally the metamodel selection algorithm embbeded in LeastSquaresStrategy
approximationAlgorithm = ot.LeastSquaresMetaModelSelectionFactory(
    basisSequenceFactory, fittingAlgorithm)
Пример #13
0
    def fit(self, X, y, **fit_params):
        """Fit PC regression model.

        Parameters
        ----------
        X : array-like, shape = (n_samples, n_features)
            Training data.
        y : array-like, shape = (n_samples, [n_output_dims])
            Target values.

        Returns
        -------
        self : returns an instance of self.

        """
        if len(X) == 0:
            raise ValueError(
                "Can not perform chaos expansion with empty sample")
        # check data type is accurate
        if (len(np.shape(X)) != 2):
            raise ValueError("X has incorrect shape.")
        input_dimension = len(X[1])
        if (len(np.shape(y)) != 2):
            raise ValueError("y has incorrect shape.")
        if self.distribution is None:
            self.distribution = ot.MetaModelAlgorithm.BuildDistribution(X)
        if self.enumeratef == 'linear':
            enumerateFunction = ot.LinearEnumerateFunction(input_dimension)
        elif self.enumeratef == 'hyperbolic':
            enumerateFunction = ot.HyperbolicAnisotropicEnumerateFunction(
                input_dimension, self.q)
        else:
            raise ValueError('enumeratef should be "linear" or "hyperbolic"')
        polynomials = [
            ot.StandardDistributionPolynomialFactory(
                self.distribution.getMarginal(i))
            for i in range(input_dimension)
        ]
        productBasis = ot.OrthogonalProductPolynomialFactory(
            polynomials, enumerateFunction)
        adaptiveStrategy = ot.FixedStrategy(
            productBasis,
            enumerateFunction.getStrataCumulatedCardinal(self.degree))
        if self.sparse:
            # Filter according to the sparse_fitting_algorithm key
            if self.sparse_fitting_algorithm == "cloo":
                fitting_algorithm = ot.CorrectedLeaveOneOut()
            else:
                fitting_algorithm = ot.KFold()
            # Define the correspondinding projection strategy
            projectionStrategy = ot.LeastSquaresStrategy(
                ot.LeastSquaresMetaModelSelectionFactory(
                    ot.LARS(), fitting_algorithm))
        else:
            projectionStrategy = ot.LeastSquaresStrategy(X, y)
        algo = ot.FunctionalChaosAlgorithm(X, y, self.distribution,
                                           adaptiveStrategy,
                                           projectionStrategy)
        algo.run()
        self.result_ = algo.getResult()
        output_dimension = self.result_.getMetaModel().getOutputDimension()

        # sensitivity
        si = ot.FunctionalChaosSobolIndices(self.result_)
        if output_dimension == 1:
            self.feature_importances_ = [
                si.getSobolIndex(i) for i in range(input_dimension)
            ]
        else:
            self.feature_importances_ = [[0.0] * input_dimension
                                         ] * output_dimension
            for k in range(output_dimension):
                for i in range(input_dimension):
                    self.feature_importances_[k][i] = si.getSobolIndex(i, k)
        self.feature_importances_ = np.array(self.feature_importances_)
        return self
Пример #14
0
myCopula = ot.NormalCopula(R)
myCorrelatedInputDistribution = ot.ComposedDistribution(
    [ot.Normal()] * inputDimension, myCopula)

sample = myCorrelatedInputDistribution.getSample(2000)

# Orthogonal basis
enumerateFunction = ot.LinearEnumerateFunction(inputDimension)
productBasis = ot.OrthogonalProductPolynomialFactory(
    [ot.HermiteFactory()] * inputDimension, enumerateFunction)
# Adaptive strategy
adaptiveStrategy = ot.FixedStrategy(
    productBasis, enumerateFunction.getStrataCumulatedCardinal(4))
# Projection strategy
samplingSize = 250
projectionStrategy = ot.LeastSquaresStrategy(
    ot.MonteCarloExperiment(samplingSize))

# Polynomial chaos algorithm
algo = ot.FunctionalChaosAlgorithm(
    model, distribution, adaptiveStrategy, projectionStrategy)
algo.run()

# Post-process the results
result = ot.FunctionalChaosResult(algo.getResult())
ancova = ot.ANCOVA(result, sample)
indices = ancova.getIndices()
uncorrelatedIndices = ancova.getUncorrelatedIndices()

for i in range(inputDimension):
    value = indices[i]
    print("ANCOVA index", i, "= %.6g" %
########################
### Chaos Polynomial ###
########################

polyColl = ot.PolynomialFamilyCollection(dim)
for i in range(dim):
	polyColl[i] = ot.HermiteFactory()

enumerateFunction = ot.LinearEnumerateFunction(dim)
multivariateBasis = ot.OrthogonalProductPolynomialFactory(polyColl, enumerateFunction)

basisSequenceFactory = ot.LARS()
fittingAlgorithm = ot.CorrectedLeaveOneOut()
approximationAlgorithm = ot.LeastSquaresMetaModelSelectionFactory(basisSequenceFactory, fittingAlgorithm)

evalStrategy = ot.LeastSquaresStrategy(Data, Result,  approximationAlgorithm)

order = 3
P = enumerateFunction.getStrataCumulatedCardinal(order)
truncatureBasisStrategy = ot.FixedStrategy(multivariateBasis, P)

polynomialChaosAlgorithm = ot.FunctionalChaosAlgorithm(Data, Result, ot.Distribution(myDistribution), truncatureBasisStrategy, evalStrategy)

polynomialChaosAlgorithm.run()

The_Result = polynomialChaosAlgorithm.getResult()
Error = The_Result.getRelativeErrors()

ChaosRV = ot.FunctionalChaosRandomVector(The_Result)
Mean = ChaosRV.getMean()[0]
StD = np.sqrt(ChaosRV.getCovariance()[0,0])
Пример #16
0
    def train(self):
        self.input_dim = self.training_points[None][0][0].shape[1]
        x_train = ot.Sample(self.training_points[None][0][0])
        y_train = ot.Sample(self.training_points[None][0][1])

        # Distribution choice of the inputs to Create the input distribution
        distributions = []
        dist_specs = self.options["uncertainty_specs"]
        if dist_specs:
            if len(dist_specs) != self.input_dim:
                raise SurrogateOpenturnsException(
                    "Number of distributions should be equal to input \
                        dimensions. Should be {}, got {}".format(
                        self.input_dim, len(dist_specs)
                    )
                )
            for ds in dist_specs:
                dist_klass = getattr(sys.modules["openturns"], ds["name"])
                args = [ds["kwargs"][name] for name in DISTRIBUTION_SIGNATURES[ds["name"]]]
                distributions.append(dist_klass(*args))
        else:
            for i in range(self.input_dim):
                mean = np.mean(x_train[:, i])
                lower, upper = 0.95 * mean, 1.05 * mean
                if mean < 0:
                    lower, upper = upper, lower
                distributions.append(ot.Uniform(lower, upper))

        distribution = ot.ComposedDistribution(distributions)

        # Polynomial basis
        # step 1 - Construction of the multivariate orthonormal basis:
        # Build orthonormal or orthogonal univariate polynomial families
        # (associated to associated input distribution)
        polynoms = [0.0] * self.input_dim
        for i in range(distribution.getDimension()):
            polynoms[i] = ot.StandardDistributionPolynomialFactory(
                distribution.getMarginal(i)
            )
        enumerateFunction = ot.LinearEnumerateFunction(self.input_dim)
        productBasis = ot.OrthogonalProductPolynomialFactory(
            polynoms, enumerateFunction
        )

        # step 2 - Truncation strategy of the multivariate orthonormal basis:
        # a strategy must be chosen for the selection of the different terms
        # of the multivariate basis.
        # Truncature strategy of the multivariate orthonormal basis
        # We choose all the polynomials of degree <= degree
        degree = self.options["pce_degree"]
        index_max = enumerateFunction.getStrataCumulatedCardinal(degree)
        adaptive_strategy = ot.FixedStrategy(productBasis, index_max)

        basis_sequenceFactory = ot.LARS()
        fitting_algorithm = ot.CorrectedLeaveOneOut()
        approximation_algorithm = ot.LeastSquaresMetaModelSelectionFactory(
            basis_sequenceFactory, fitting_algorithm
        )
        projection_strategy = ot.LeastSquaresStrategy(
            x_train, y_train, approximation_algorithm
        )

        algo = ot.FunctionalChaosAlgorithm(
            x_train, y_train, distribution, adaptive_strategy, projection_strategy
        )
        # algo = ot.FunctionalChaosAlgorithm(X_train_NS, Y_train_NS)
        algo.run()
        self._pce_result = algo.getResult()