def build_from_interface(self, fun, dfun=None, avdim=None): """ Build the active subspace-enabled model with interfaces to the simulation. :param function fun: A function that interfaces with the simulation. It should take an ndarray of shape 1-by-m (e.g., a row of `X`), and it should return a scalar. That scalar is the quantity of interest from the simulation. :param function dfun: A function that interfaces with the simulation. It should take an ndarray of shape 1-by-m (e.g., a row of `X`), and it should return the gradient of the quantity of interest as an ndarray of shape 1-by-m. :param int avdim: The dimension of the active subspace. If `avdim` is not present, it is chosen after computing the active subspaces using the given interfaces. **Notes** This method follows these steps: #. Draw random points according to the weight function on the space\ of simulation inputs. #. Compute the quantity of interest and its gradient at the sampled\ inputs. If `dfun` is None, use finite differences. #. Use the collection of gradients to estimate the eigenvectors and\ eigenvalues that determine and define the active subspace. #. Train a response surface using the interface, which uses a careful\ design of experiments on the space of active variables. This design\ uses about 5 points per dimension of the active subspace. """ if not hasattr(fun, '__call__'): raise TypeError('fun should be a callable function.') if dfun is not None: if not hasattr(dfun, '__call__'): raise TypeError('dfun should be a callable function.') if avdim is not None: if not isinstance(avdim, int): raise TypeError('avdim should be an integer') m = self.m # number of gradient samples M = int(np.floor(6*(m+1)*np.log(m))) # sample points for gradients if self.bounded_inputs: X = np.random.uniform(-1.0, 1.0, size=(M, m)) else: X = np.random.normal(size=(M, m)) fun = SimulationRunner(fun) f = fun.run(X) self.X, self.f, self.fun = X, f, fun # sample the simulation's gradients if dfun == None: df = finite_difference_gradients(X, fun) else: dfun = SimulationGradientRunner(dfun) df = dfun.run(X) self.dfun = dfun # compute the active subspace ss = Subspaces() ss.compute(df) if avdim is not None: ss.partition(avdim) self.n = ss.W1.shape[1] print 'The dimension of the active subspace is {:d}.'.format(self.n) # set up the active variable domain and map if self.bounded_inputs: avdom = BoundedActiveVariableDomain(ss) avmap = BoundedActiveVariableMap(avdom) else: avdom = UnboundedActiveVariableDomain(ss) avmap = UnboundedActiveVariableMap(avdom) # build the response surface asrs = ActiveSubspaceResponseSurface(avmap) asrs.train_with_interface(fun, int(np.power(5,self.n))) # compute testing error as an R-squared self.Rsqr = 1.0 - ( np.linalg.norm(asrs.predict(X)[0] - f)**2 \ / np.var(f) ) self.as_respsurf = asrs
def build_from_data(self, X, f, df=None, avdim=None): """ Build the active subspace-enabled model with input/output pairs. :param ndarray X: M-by-m matrix with evaluations of the m-dimensional simulation inputs. :param ndarray f: M-by-1 matrix with corresponding simulation quantities of interest. :param ndarray df: M-by-m matrix that contains the gradients of the simulation quantity of interest, oriented row-wise, that correspond to the rows of `X`. If `df` is not present, then it is estimated with crude local linear models using the pairs `X` and `f`. :param int avdim: The dimension of the active subspace. If `avdim` is not present, a crude heuristic is used to choose an active subspace dimension based on the given data `X` and `f`---and possible `df`. **Notes** This method follows these steps: #. If `df` is None, estimate it with local linear models using the \ input/output pairs `X` and `f`. #. Compute the active and inactive subspaces using `df`. #. Train a response surface using `X` and `f` that exploits the active \ subspace. """ X, f, M, m = process_inputs_outputs(X, f) # check if the given inputs satisfy the assumptions if self.bounded_inputs: if np.any(X) > 1.0 or np.any(X) < -1.0: raise Exception('The supposedly bounded inputs exceed the \ bounds [-1,1].') else: if np.any(X) > 10.0 or np.any(X) < -10.0: raise Exception('There is a very good chance that your \ unbounded inputs are not properly scaled.') self.X, self.f, self.m = X, f, m if df is not None: df, M_df, m_df = process_inputs(df) if m_df != m: raise ValueError('The dimension of the gradients should be \ the same as the dimension of the inputs.') else: # if gradients aren't available, estimate them from data df = local_linear_gradients(X, f) # compute the active subspace ss = Subspaces() ss.compute(df) if avdim is not None: if not isinstance(avdim, int): raise TypeError('avdim should be an integer.') else: ss.partition(avdim) self.n = ss.W1.shape[1] print 'The dimension of the active subspace is {:d}.'.format(self.n) # set up the active variable domain and map if self.bounded_inputs: avdom = BoundedActiveVariableDomain(ss) avmap = BoundedActiveVariableMap(avdom) else: avdom = UnboundedActiveVariableDomain(ss) avmap = UnboundedActiveVariableMap(avdom) # build the response surface asrs = ActiveSubspaceResponseSurface(avmap) asrs.train_with_data(X, f) # set the R-squared coefficient self.Rsqr = asrs.respsurf.Rsqr self.as_respsurf = asrs