def __init__(self, grid, alpha, params, nk=None): """ Constructor @param grid: Grid @param alpha: surplus vector @param params: parameter set @param E: expectation value of grid, alpha @param V: variance of grid, alpha @param nk: maximum length of interactions """ self.__grid = grid self.__alpha = alpha self.__params = params self.__ap = self.__params.activeParams() self.__U = self.__ap.getIndependentJointDistribution() self.__T = self.__ap.getJointTransformation() self.__xlim = self.__U.getBounds() self.__dim = len(self.__ap) if self.__dim < 2: raise AttributeError('dimensionality has to be > 1') # check if highest order term is required self.__has_highest_order_term = not nk or nk > self.__dim - 1 if self.__has_highest_order_term: self.__nk = self.__dim - 1 else: self.__nk = nk self.__expectation_funcs = None self.__anova_components = None self.__variance_components = None self._verbose = True self.__marginalization = MarginalAnalyticEstimationStrategy() self.__estimation = AnalyticEstimationStrategy()
def testMarginalEstimationStrategy(self): xlim = np.array([[-1, 1], [-1, 1]]) trans = JointTransformation() dists = [] for idim in range(xlim.shape[0]): trans.add(LinearTransformation(xlim[idim, 0], xlim[idim, 1])) dists.append(Uniform(xlim[idim, 0], xlim[idim, 1])) dist = J(dists) def f(x): return np.prod([(1 + xi) * (1 - xi) for xi in x]) def F(x): return 1. - x**3 / 3. grid, alpha_vec = interpolate(f, 1, 2, gridType=GridType_Poly, deg=2, trans=trans) alpha = alpha_vec.array() q = (F(1) - F(-1))**2 q1 = doQuadrature(grid, alpha) q2 = AnalyticEstimationStrategy().mean(grid, alpha, dist, trans)["value"] self.assertTrue(abs(q - q1) < 1e-10) self.assertTrue(abs(q - q2) < 1e-10) ngrid, nalpha, _ = MarginalAnalyticEstimationStrategy().mean( grid, alpha, dist, trans, [[0]]) self.assertTrue(abs(nalpha[0] - 2. / 3.) < 1e-10) plotSG3d(grid, alpha) plt.figure() plotSG1d(ngrid, nalpha) plt.show()
class HDMRAnalytic(object): """ The HDMR class """ def __init__(self, grid, alpha, params, nk=None): """ Constructor @param grid: Grid @param alpha: surplus vector @param params: parameter set @param E: expectation value of grid, alpha @param V: variance of grid, alpha @param nk: maximum length of interactions """ self.__grid = grid self.__alpha = alpha self.__params = params self.__ap = self.__params.activeParams() self.__U = self.__ap.getIndependentJointDistribution() self.__T = self.__ap.getJointTransformation() self.__xlim = self.__U.getBounds() self.__dim = len(self.__ap) if self.__dim < 2: raise AttributeError('dimensionality has to be > 1') # check if highest order term is required self.__has_highest_order_term = not nk or nk > self.__dim - 1 if self.__has_highest_order_term: self.__nk = self.__dim - 1 else: self.__nk = nk self.__expectation_funcs = None self.__anova_components = None self.__variance_components = None self._verbose = True self.__marginalization = MarginalAnalyticEstimationStrategy() self.__estimation = AnalyticEstimationStrategy() def __computeMean(self): print "estimate mean:", self.__E, _ = self.__estimation.mean(self.__grid, self.__alpha, self.__U, self.__T) print "done" def __computeVariance(self): print "estimate variance:", self.__V, _ = self.__estimation.var(self.__grid, self.__alpha, self.__U, self.__T, self.__E) print "done" def getSortedPermutations(self, keys): """ Sort keys with respect (1) to their length and (2) their lexical order @param keys: """ ans = [tuple()] * len(keys) ix = 0 for x in sorted(np.unique(map(len, keys))): for ck in sorted(filter(lambda k: len(k) == x, keys)): ans[ix] = ck ix += 1 return ans def __expectation_functions(self): """ Compute the marginalized expectation functions for the ANOVA decomposition. """ if self.__nk < 1 or self.__nk > self.__dim - 1: raise AttributeError('The truncated order has to be in \ {1, ..., %i}' % (self.__dim - 1, )) # init expec = {} if self._verbose: print "-" * 60 # add higher order terms for k in xrange(self.__nk): perms = it.combinations(self.__U.getTupleIndices(), r=k + 1) for perm in perms: # select dimensions to be integrated dd = [d for d in self.__U.getTupleIndices() if d not in perm] if self._verbose: print "Explore %s, Integrate: %s" % (perm, dd), # ----------------------------------------------- # Make sure that perm and dd are disjoint sets # covering the whole set # assert sorted(list(perm) + dd) == range(self.__dim) assert len(dd) == self.__dim - len(perm) assert len(dd) > 0 # ----------------------------------------------- # compute first moment grid, alpha, err = self.__marginalization.mean( self.__grid, self.__alpha, self.__U, self.__T, dd) expec[tuple([d for di in perm for d in di])] = grid, alpha if self._verbose: print "L2 err = %g" % err # plot result # if len(perm) == 1: # plotSG1d(grid, alpha) # plt.show() # add highest order term if self.__has_highest_order_term: perm = tuple(range(self.__dim)) expec[perm] = self.__grid, self.__alpha if self._verbose: print "-" * 60 return expec def __combine_terms(self, perm): """ Combine the terms in order to get the ANOVA component specified in perm @param perm: tuple identifies the ANOVA component """ # init, constant term + own term fi = {'const': ((-1)**len(perm), self.__E), 'var': [(1, perm)]} # add all lower order terms with alternating coefficient for k in xrange(len(perm) - 1): pperms = it.combinations(list(perm), r=k + 1) for pperm in pperms: fi['var'] += [((-1)**(len(perm) - len(pperm)), pperm)] return fi def __decompose(self): """ Computes the ANOVA components for the given function when the marginalized parts are available """ fis = {} # add higher order terms for perm in self.__expectation_funcs.keys(): fis[perm] = self.__combine_terms(perm) return fis def doDecomposition(self): """ Computes the ANOVA decomposition for the given sparse grid function and the corresponding marginal distributions """ if not self.__anova_components: # compute mean and variance self.__computeMean() self.__computeVariance() # marginalize self.__expectation_funcs = self.__expectation_functions() # combine marginalized terms self.__anova_components = self.__decompose() def __evalHigherOrderComponent(self, fi, x): """ Evaluate the higher order components @param fi: linear combination of marginalized functions @param x: DataVector coordinate to be evaluated """ # constant term sign, f0 = fi['const'] s = sign * f0 # higher order terms terms for sign, pperm in fi['var']: grid, alpha = self.__expectation_funcs[pperm] p = DataVector([x[ix] for ix in pperm]) val = evalSGFunction(grid, alpha, p) s += sign * val return s def eval(self, x): """ Evaluate the ANOVA decomposition at the given position @param x: coordinates to be evaluated """ if not self.__anova_components: raise Exception('Do the decomposition first') # type check if isNumerical(x): x = np.array(x, dtype='float') # evaluation function # add constant term s = self.__E # add higher order terms for components in self.__anova_components.values(): s += self.__evalHigherOrderComponent(components, x) return s def evalComponent(self, perm, x): """ Evaluate a single ANOVA component @param perm: identifier @param x: coordinates """ if len(perm) == 0: return self.__E elif perm in self.__anova_components: fi = self.__anova_components[perm] return self.__evalHigherOrderComponent(fi, x) else: raise AttributeError('The component %s does not exist' % (perm, )) def getVarianceDecomposition(self): """ Compute the variance of each ANOVA component """ # check if this taks has alread been done if self.__variance_components: return self.__variance_components # do the anova decompisition first self.doDecomposition() # initialization vis = {} self.__variance_components = {} if self._verbose: print "-" * 60 # run over all available permutations and compute the variance keys = self.__ap.keys() for perm in self.getSortedPermutations(self.__anova_components.keys()): # get the sparse grid function grid, alpha = self.__expectation_funcs[perm] # prepare parameter set dd = [keys[d] for d in perm] params = self.__ap.getSubset(dd) # transformation, pdf and volume T = params.getJointTransformation() U = params.getIndependentJointDistribution() # estimate variance mean, _ = self.__estimation.mean(grid, alpha, U, T) vi, err = self.__estimation.var(grid, alpha, U, T, mean) # store the variance vis[perm] = vi if self._verbose: print "Estimated V[%s]: %g, L2 err = %g" % (perm, vi, err) # add lower order components fi = self.__anova_components[perm] for sign, pperm in fi['var']: if len(pperm) < len(perm): vi += sign * vis[pperm] self.__variance_components[perm] = vi if self._verbose: print "-" * 60 return self.__variance_components def getSobolIndices(self): """ Computes the relative influence of one single parameter combination to the whole variance. """ # make sure that there exists a variance if self.__V > 0: vis = self.getVarianceDecomposition() ans = dict([(perm, vi / self.__V) for perm, vi in vis.items()]) else: ans = {} for k in xrange(self.__nk): perms = it.combinations(range(self.__dim), r=k + 1) for perm in perms: ans[perm] = 0.0 if self.__has_highest_order_term: ans[tuple(range(self.__dim))] = 0.0 return ans def getMainEffects(self): """ Just compute the sobol indices without interactions """ sobol = self.getSobolIndices() me = {} for perm in sobol.keys(): if len(perm) == 1: me[perm] = sobol[perm] return me def getTotalEffects(self): """ Compute the sum of all sobol indices where one single parameter is part of the interaction """ sobol = self.getSobolIndices() te = {} for perm in sobol.keys(): if len(perm) == 1: s = [ sobol[pperm] for pperm in sobol.keys() if perm[0] in pperm ] te[perm] = sum(s) return te
def withAnalyticEstimationStrategy(self, *args, **kws): self.__estimationStrategy = AnalyticEstimationStrategy() return self