Example #1
0
    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
Example #2
0
    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