human
Complete this python program:
def fastica(X, n_comp=None, algorithm='parallel', whiten=True, fun='logcosh', fun_prime='', fun_args={}, maxit=200, tol=0.0001, w_init=None):
assistant
#Here is the Python program you requested.

    """Perform Fast Independent Component Analysis.
    Parameters
    X : (n,p) array
    Array with n observations (statistical units) measured on p variables.
    n_comp : int, optional
    Number of components to extract. If None no dimension reduction
    is performed.
    algorithm : {\parallel\,\deflation\}
    Apply an parallel or deflational FASTICA algorithm.
    whiten: boolean, optional
    If true perform an initial whitening of the data. Do not set to
    false unless the data is already white, as you will get incorrect
    results.
    If whiten is true, the data is assumed to have already been
    preprocessed: it should be centered, normed and white.
    fun : String or Function
    The functional form of the G function used in the
    approximation to neg-entropy. Could be either \logcosh\, \exp\,
    or \cube\.
    You can also provide your own function but in this case, its
    derivative should be provided via argument fun_prime
    fun_prime : Empty string (\\) or Function
    See fun.
    fun_args : Optional dictionnary
    If empty and if fun=\logcosh\, fun_args will take value
    {\alpha\ : 1.0}
    maxit : int
    Maximum number of iterations to perform
    tol : float
    A positive scalar giving the tolerance at which the
    un-mixing matrix is considered to have converged
    w_init : (n_comp,n_comp) array
    Initial un-mixing array of dimension (n.comp,n.comp).
    If None (default) then an array of normal r.v.\s is used
    source_only: if True, only the sources matrix is returned
    Results
    K : (p,n_comp) array
    pre-whitening matrix that projects data onto th first n.comp
    principal components. Returned only if whiten is True
    W : (n_comp,n_comp) array
    estimated un-mixing matrix
    The mixing matrix can be obtained by::
    w = np.asmatrix(W) * K.T
    A = w.T * (w * w.T).I
    S : (n,n_comp) array
    estimated source matrix
    Examples
    >>> X = np.array(
    [[5.,1.4,1.9,0],     [2,5.4,8.,1.1],     [3,6.4,9,1.2]])
    >>> w_init = np.array([[1,4],[7,2]])
  ..."""
    algorithm_funcs = {'parallel': _ica_par, 'deflation': _ica_def}
    alpha = fun_args.get('alpha', 1.0)
    if ((alpha < 1) or (alpha > 2)):
        raise ValueError('alpha    must    be    in    [1,2]')
    if (type(fun) is types.StringType):
        if (fun == 'logcosh'):
            def g(x, fun_args):
                alpha = fun_args.get('alpha', 1.0)
                return np.tanh((alpha * x))
            def gprime(x, fun_args):
                alpha = fun_args.get('alpha', 1.0)
                return (alpha * (1 - (np.tanh((alpha * x)) ** 2)))
        elif (fun == 'exp'):
            def g(x, fun_args):
                return (x * np.exp(((- (x ** 2)) / 2)))
            def gprime(x, fun_args):
                return ((1 - (x ** 2)) * np.exp(((- (x ** 2)) / 2)))
        elif (fun == 'cube'):
            def g(x, fun_args):
                return (x ** 3)
            def gprime(x, fun_args):
                return (3 * (x ** 2))
        else:
            raise ValueError('fun    argument    should    be    one    of    logcosh,    exp    or    cube')
    elif (type(fun) is not types.FunctionType):
        raise ValueError('fun    argument    should    be    either    a    string    (one    of    logcosh,    exp    or    cube)    or    a    function')
    else:
        def g(x, fun_args):
            return fun(x, **fun_args)
        def gprime(x, fun_args):
            return fun_prime(x, **fun_args)
    (n, p) = X.shape
    if (n_comp is None):
        n_comp = min(n, p)
    if (n_comp > min(n, p)):
        n_comp = min(n, p)
        print ('n_comp    is    too    large:    it    will    be    set    to    %s' % n_comp)
    if whiten:
        X = (X - X.mean(axis=0))
        (_, d, v) = np.linalg.svd(X, full_matrices=False)
        del _
        K = (v * (np.sqrt(n) / d)[:, np.newaxis])[:n_comp]
        del v, d
        X1 = np.dot(K, X.T)
    else:
        X1 = X.T
    if (w_init is None):
        w_init = np.random.normal(size=(n_comp, n_comp))
    else:
        w_init = np.asarray(w_init)
       ...