def compute_sobol_indices(self, fval, cardinality=-1): """ Compute Sobol indices based on Tang (2009), GLOBAL SENSITIVITY ANALYSIS FOR STOCHASTIC COLLOCATION EXPANSION. Approximates main-effect indices. Main effect indices (non-normalized) are returned in a dictionary D, with keys tuples of dimension indices. E.g. D[(0,3,4)] is the main-effect index (cardinality 3) of the variables x_0, x_3, x_4. """ cmax = self.dim if cardinality==-1 else cardinality mu = self.integrate(fval) # Compute mean, variance fval2 = dict([(i, val**2) for i, val in fval.items()]) var = self.integrate(fval2) - mu**2 D = {(): mu**2} full = range(self.dim) for c in range(1, cmax + 1): # Loop over all combinations of c indices # I.e. y = (0,), (1,), (dim-1,) then # y = (0,1), (0,2), (1,2), etc for y in itertools.combinations(full, c): z = mylib.complement(y, self.dim) # Integrate over dimensions z sp_r, fval_r = self.integrate_partial(fval, z) # Square result fval_r2 = dict([(i, val**2) for i, val in fval_r.items()]) # Integrate over remaining dimensions D[y] = sp_r.integrate(fval_r2) for t in mylib.all_subsets(y, strict=True, null=True): D[y] -= D[t] return D, mu, var
def compute_sobol_indices(self, fval, cardinality=-1): """ Compute Sobol indices based on Tang (2009), GLOBAL SENSITIVITY ANALYSIS FOR STOCHASTIC COLLOCATION EXPANSION. Approximates main-effect indices. Main effect indices (non-normalized) are returned in a dictionary D, with keys tuples of dimension indices. E.g. D[(0,3,4)] is the main-effect index (cardinality 3) of the variables x_0, x_3, x_4. """ cmax = self.dim if cardinality == -1 else cardinality mu = self.integrate(fval) # Compute mean, variance fval2 = dict([(i, val**2) for i, val in fval.items()]) var = self.integrate(fval2) - mu**2 D = {(): mu**2} full = range(self.dim) for c in range(1, cmax + 1): # Loop over all combinations of c indices # I.e. y = (0,), (1,), (dim-1,) then # y = (0,1), (0,2), (1,2), etc for y in itertools.combinations(full, c): z = mylib.complement(y, self.dim) # Integrate over dimensions z sp_r, fval_r = self.integrate_partial(fval, z) # Square result fval_r2 = dict([(i, val**2) for i, val in fval_r.items()]) # Integrate over remaining dimensions D[y] = sp_r.integrate(fval_r2) for t in mylib.all_subsets(y, strict=True, null=True): D[y] -= D[t] return D, mu, var
def _sobol_variances(self, fval, cardinality): """ Performs the integrals defined in Tang (2009), GLOBAL SENSITIVITY ANALYSIS FOR STOCHASTIC COLLOCATION EXPANSION, on the current sparse grid. Only for use by compute_sobol_variances(), do not call directly. """ cmax = self.dim if cardinality == -1 else cardinality mu = self.integrate(fval) # Compute mean, variance fval2 = dict([(i, val**2) for i, val in fval.items()]) var = self.integrate(fval2) - mu**2 D = {(): mu**2} # Temporary, to simplify following code full = range(self.dim) for c in range(1, cmax + 1): # Loop over all combinations of c indices # I.e. y = (0,), (1,), (dim-1,) then # y = (0,1), (0,2), (1,2), etc for y in itertools.combinations(full, c): z = mylib.complement(y, self.dim) # Integrate over dimensions z sp_r, fval_r = self.integrate_partial(fval, z) # Square result fval_r2 = dict([(i, val**2) for i, val in fval_r.items()]) # Integrate over remaining dimensions D[y] = sp_r.integrate(fval_r2) for t in mylib.all_subsets(y, strict=True, null=True): D[y] -= D[t] D[()] = var return D, mu, var
def reduce_axes(self, axes): """ Create new IndexSet by reducing this one along specified dimensions. Corresponds to equivalent, lower- dimensional sparse-grid rule. This IndexSet remains untouched. axes - dimensions to remove from the indexset Return: New IndexSet object """ # Axes to *keep* axes_r = mylib.complement(axes, self.dim) I_r = set([]) # New, reduced I for mi in self.I: I_r |= {tuple(np.array(mi, dtype=np.int8)[axes_r])} return IndexSet(I=I_r)
def reduce_axes(self, axes): """ Create new LevelSet by reducing this one along specified dimensions. Corresponds to equivalent, lower- dimensional sparse-grid rule. This LevelSet remains untouched. axes - dimensions to remove from the levelset Return: ls_r - LevelSet of reduced dimension """ axes_r = mylib.complement(axes, self.dim) dim_r = len(axes_r) ls_r = LevelSet(dim_r, 0, fill='none') # Delete unwanted dimensions and remove # duplicate multi-indices (lose order) ls_r.I = np.array(self.I)[:,axes_r] ls_r.I = np.array(list(set([tuple(i) for i in ls_r.I]))) # Reinitialize active and old points # TODO: finish this... currently all # active. Only needed for adaptivity ls_r.A, ls_r.O = set(range(len(ls_r.I))), set() return ls_r
def reduce_axes(self, axes): """ Create new LevelSet by reducing this one along specified dimensions. Corresponds to equivalent, lower- dimensional sparse-grid rule. This LevelSet remains untouched. axes - dimensions to remove from the levelset Return: ls_r - LevelSet of reduced dimension """ axes_r = mylib.complement(axes, self.dim) dim_r = len(axes_r) ls_r = LevelSet(dim_r, 0, fill='none') # Delete unwanted dimensions and remove # duplicate multi-indices (lose order) ls_r.I = np.array(self.I)[:, axes_r] ls_r.I = np.array(list(set([tuple(i) for i in ls_r.I]))) # Reinitialize active and old points # TODO: finish this... currently all # active. Only needed for adaptivity ls_r.A, ls_r.O = set(range(len(ls_r.I))), set() return ls_r
def integrate_partial(self, fval, axes): """ Integrate over a subset of the dimensions of the rule. If axes lists all the axes this is equivalent to integrate(). fval - data evaluated at all support points of the component rule. If f is vector-valued the last dimension is the vector axes - list of axes over which to integrate Return: sc_r - SparseComponentRule object of reduced dimension fval_r - reduced rank version of fval, dictionary indexed by new reduced-dim multi-indices axes_r - list of remaining axes in f_r (those not integrated yet) """ # Full-rank representation of f f = self.dimensionate(self.extract_fvec(fval)) if len(axes) == 0: return f, range(self.dim) # Creative use of einsum() assert f.ndim < 26, ValueError('Implementation limit dim < 26') ijk = np.array(list('abcdefghijklmnopqrstuvwxyz')) # Eg. cmd = 'abcde,b,c' for dim=3, # axis=[1,2], and matrix-valued f. # We avoid the ellipsis '...' because # of non-mature implementation. cmd = ''.join(ijk[:f.ndim]) + ',' + ','.join(ijk[list(axes)]) w = self.quad.dw if self.delta else self.quad.w warg = [w[self.levelidx[ai] - 1] for ai in axes] f_r = np.einsum(cmd, f, *warg) # Partial integral of f # Complement of axes (remaining axes) axes_r = mylib.complement(axes, self.dim) # Remaining level indices, new comp rule levelidx_r = list(np.array(self.levelidx)[axes_r]) sc_r = SparseComponentRule(self.quad, levelidx_r, delta=self.delta) # CHECK TODO: correct ordering? fvec_r = sc_r.undimensionate(f_r) fval_r = dict(zip([tuple(midx) for midx in sc_r.i], fvec_r)) return sc_r, fval_r, axes_r
def integrate_partial(self, fval, axes): """ Integrate over a subset of the dimensions of the rule. If axes lists all the axes this is equivalent to integrate(). fval - data evaluated at all support points of the component rule. If f is vector-valued the last dimension is the vector axes - list of axes over which to integrate Return: sc_r - SparseComponentRule object of reduced dimension fval_r - reduced rank version of fval, dictionary indexed by new reduced-dim multi-indices axes_r - list of remaining axes in f_r (those not integrated yet) """ # Full-rank representation of f f = self.dimensionate( self.extract_fvec(fval) ) if len(axes) == 0: return f, range(self.dim) # Creative use of einsum() assert f.ndim < 26, ValueError('Implementation limit dim < 26') ijk = np.array(list('abcdefghijklmnopqrstuvwxyz')) # Eg. cmd = 'abcde,b,c' for dim=3, # axis=[1,2], and matrix-valued f. # We avoid the ellipsis '...' because # of non-mature implementation. cmd = ''.join(ijk[:f.ndim]) + ',' + ','.join(ijk[list(axes)]) w = self.quad.dw if self.delta else self.quad.w warg = [w[self.levelidx[ai]-1] for ai in axes] f_r = np.einsum(cmd, f, *warg) # Partial integral of f # Complement of axes (remaining axes) axes_r = mylib.complement(axes, self.dim) # Remaining level indices, new comp rule levelidx_r = list(np.array(self.levelidx)[axes_r]) sc_r = SparseComponentRule(self.quad, levelidx_r, delta=self.delta) # CHECK TODO: correct ordering? fvec_r = sc_r.undimensionate(f_r) fval_r = dict(zip([tuple(midx) for midx in sc_r.i], fvec_r)) return sc_r, fval_r, axes_r