def inverse_transform(self, X):
        """
        Perform an approximation of the input matrix of observations
        to the original dimensionality space

        :param X: The matrix of observations, where the training samples
        populate the rows, and the features populate the columns

        :return: Xhat, the dimensionality increased representation of the data
        """

        return linalg.dot(X, self.principal) + self.mean
    def transform(self, X):
        """
        Perform dimensionality reduction of the input matrix X

        :param X: The matrix of observations, where the training samples
        populate the rows, and the features populate the columns

        :return: Xtilde, the dimensionally reduced representation of the data
        """
        # center the data by subtracting the mean
        self.mean = T.mean(X, axis=0)
        X -= self.mean
        U, s, V = linalg.svd(X, full_matrices=False)

        # Keep track of the 'M' principal directions
        # The svd actually produces V^T, so the
        # principal directions are stored in the rows of
        # V as opposed to the columns
        self.principal = V[:self.dim]

        # Return the transformed data
        return linalg.dot(X, T.transpose(self.principal))