Source code for UQpy.sampling.ThetaCriterionPCE

import numpy as np
import UQpy
from UQpy.surrogates import polynomial_chaos
from scipy.spatial.distance import cdist
from beartype import beartype


[docs]class ThetaCriterionPCE: @beartype def __init__(self, surrogates: list[UQpy.surrogates.polynomial_chaos.PolynomialChaosExpansion]): """ Active learning for polynomial chaos expansion using Theta criterion balancing between exploration and exploitation. :param surrogates: list of objects of the :py:meth:`UQpy` :class:`PolynomialChaosExpansion` class """ self.surrogates = surrogates
[docs] def run(self, existing_samples: np.ndarray, candidate_samples: np.ndarray, nsamples=1, samples_weights=None, candidate_weights=None, pce_weights=None, enable_criterium: bool=False): """ Execute the :class:`.ThetaCriterionPCE` active learning. :param existing_samples: Samples in existing ED used for construction of PCEs. :param candidate_samples: Candidate samples for selecting by Theta criterion. :param samples_weights: Weights associated to X samples (e.g. from Coherence Sampling). :param candidate_weights: Weights associated to candidate samples (e.g. from Coherence Sampling). :param nsamples: Number of samples selected from candidate set in a single run of this algorithm :param pce_weights: Weights associated to each PCE (e.g. Eigen values from dimension-reduction techniques) :param enable_criterium: If True, values of Theta criterion (variance density, average variance density, geometrical part, total Theta criterion) for all candidates are returned instead of a positions of best candidates The :meth:`run` method is the function that performs iterations in the :class:`.ThetaCriterionPCE` class. The :meth:`run` method of the :class:`.ThetaCriterionPCE` class can be invoked many times for sequential sampling. :return: Position of the best candidate in candidate set. If ``enable_criterium = True``, values of Theta criterion (variance density, average variance density, geometrical part, total Theta criterion) for all candidates are returned instead of a position. """ pces = self.surrogates npce = len(pces) nsimexisting, nvar = existing_samples.shape nsimcandidate, nvar = candidate_samples.shape criterium = np.zeros(nsimcandidate) if samples_weights is None: samples_weights = np.ones(nsimexisting) if candidate_weights is None: candidate_weights = np.ones(nsimcandidate) if pce_weights is None: pce_weights = np.ones(npce) pos = [] for _ in range(nsamples): S = polynomial_chaos.Polynomials.standardize_sample(existing_samples, pces[0].polynomial_basis.distributions) s_candidate = polynomial_chaos.Polynomials.standardize_sample(candidate_samples, pces[0].polynomial_basis.distributions) lengths = cdist(s_candidate, S) closest_s_position = np.argmin(lengths, axis=1) closest_value_x = existing_samples[closest_s_position] l = np.nanmin(lengths, axis=1) variance_candidate = 0 variance_closest = 0 for i in range(npce): pce = pces[i] variance_candidatei = self._local_variance(candidate_samples, pce, candidate_weights) variance_closesti = self._local_variance(closest_value_x, pce, samples_weights[closest_s_position]) variance_candidate = variance_candidate + variance_candidatei * pce_weights[i] variance_closest = variance_closest + variance_closesti * pce_weights[i] criterium_v = np.sqrt(variance_candidate * variance_closest) criterium_l = l ** nvar criterium = criterium_v * criterium_l pos.append(np.argmax(criterium)) existing_samples = np.append(existing_samples, candidate_samples[pos, :], axis=0) samples_weights = np.append(samples_weights, candidate_weights[pos]) if not enable_criterium: if nsamples == 1: pos = pos[0] return pos else: return variance_candidate, criterium_v, criterium_l, criterium
# calculate variance density of PCE for Theta Criterion @staticmethod def _local_variance(coordinates, pce, weight=1): beta = pce.coefficients beta[0] = 0 product = pce.polynomial_basis.evaluate_basis(coordinates) product = np.transpose(np.transpose(product) * weight) product = product.dot(beta) product = np.sum(product, axis=1) product = product ** 2 product = product * polynomial_chaos.Polynomials.standardize_pdf(coordinates, pce.polynomial_basis.distributions) return product