import scipy.linalg as la
import numpy as np


from py_bh_tsne import _TSNE as TSNE


def fast_tsne(data, pca_d=50, d=2, perplexity=30., theta=0.5):
    """
    Run Barnes-Hut T-SNE on _data_.

    @param data         The data.

    @param pca_d        The dimensionality of data is reduced via PCA
                        to this dimensionality.

    @param d            The embedding dimensionality. Must be fixed to
                        2.

    @param perplexity   The perplexity controls the effective number of
                        neighbors.

    @param theta        If set to 0, exact t-SNE is run, which takes
                        very long for dataset > 5000 samples.
    """
    N, _ = data.shape
    
    # inplace!!
    data -= data.mean(axis=0)
    
    # working with covariance + (svd on cov.) is 
    # much faster than svd on data directly.
    cov = np.dot(data.T, data)/N
    u, s, v = la.svd(cov, full_matrices=False)
    u = u[:,0:pca_d]
    X = np.dot(data, u)

    tsne = TSNE()
    Y = tsne.run(X, N, pca_d, d, perplexity, theta)
    return Y

import scipy.linalg as la
import numpy as np


from fasttsne import _TSNE as TSNE


def fast_tsne(data, pca_d=None, d=2, perplexity=30., theta=0.5):
    """
    Run Barnes-Hut T-SNE on _data_.

    @param data         The data.

    @param pca_d        The dimensionality of data is reduced via PCA
                        to this dimensionality.

    @param d            The embedding dimensionality. Must be fixed to
                        2.

    @param perplexity   The perplexity controls the effective number of
                        neighbors.

    @param theta        If set to 0, exact t-SNE is run, which takes
                        very long for dataset > 5000 samples.
    """
    N, _ = data.shape
    
    # inplace!!

    if pca_d is None:
        X = data
    else:
        # do PCA
        data -= data.mean(axis=0)
    
        # working with covariance + (svd on cov.) is 
        # much faster than svd on data directly.
        cov = np.dot(data.T, data)/N
        u, s, v = la.svd(cov, full_matrices=False)
        u = u[:,0:pca_d]
        X = np.dot(data, u)

    tsne = TSNE()
    Y = tsne.run(X, N, X.shape[1], d, perplexity, theta)
    return Y

FIX (from Justin Bayer): avoid memory segfault when pca_d is choosen too big.
FIX (from Justin Bayer): avoid memory segfault when pca_d is choosen too big.

  import scipy.linalg as la
  import numpy as np
  
  
- from py_bh_tsne import _TSNE as TSNE
+ from fasttsne import _TSNE as TSNE
  
  
- def fast_tsne(data, pca_d=50, d=2, perplexity=30., theta=0.5):
+ def fast_tsne(data, pca_d=None, d=2, perplexity=30., theta=0.5):
      """
      Run Barnes-Hut T-SNE on _data_.
  
      @param data         The data.
  
      @param pca_d        The dimensionality of data is reduced via PCA
                          to this dimensionality.
  
      @param d            The embedding dimensionality. Must be fixed to
                          2.
  
      @param perplexity   The perplexity controls the effective number of
                          neighbors.
  
      @param theta        If set to 0, exact t-SNE is run, which takes
                          very long for dataset > 5000 samples.
      """
      N, _ = data.shape
      
      # inplace!!
+ 
+     if pca_d is None:
+         X = data
+     else:
+         # do PCA
-     data -= data.mean(axis=0)
+         data -= data.mean(axis=0)
      
-     # working with covariance + (svd on cov.) is 
+         # working with covariance + (svd on cov.) is 
-     # much faster than svd on data directly.
+         # much faster than svd on data directly.
-     cov = np.dot(data.T, data)/N
+         cov = np.dot(data.T, data)/N
-     u, s, v = la.svd(cov, full_matrices=False)
+         u, s, v = la.svd(cov, full_matrices=False)
-     u = u[:,0:pca_d]
+         u = u[:,0:pca_d]
-     X = np.dot(data, u)
+         X = np.dot(data, u)
  
      tsne = TSNE()
-     Y = tsne.run(X, N, pca_d, d, perplexity, theta)
+     Y = tsne.run(X, N, X.shape[1], d, perplexity, theta)
      return Y
  
FIX (from Justin Bayer): avoid memory segfault when pca_d is choosen too big.
## Code Before:
import scipy.linalg as la
import numpy as np


from py_bh_tsne import _TSNE as TSNE


def fast_tsne(data, pca_d=50, d=2, perplexity=30., theta=0.5):
    """
    Run Barnes-Hut T-SNE on _data_.

    @param data         The data.

    @param pca_d        The dimensionality of data is reduced via PCA
                        to this dimensionality.

    @param d            The embedding dimensionality. Must be fixed to
                        2.

    @param perplexity   The perplexity controls the effective number of
                        neighbors.

    @param theta        If set to 0, exact t-SNE is run, which takes
                        very long for dataset > 5000 samples.
    """
    N, _ = data.shape
    
    # inplace!!
    data -= data.mean(axis=0)
    
    # working with covariance + (svd on cov.) is 
    # much faster than svd on data directly.
    cov = np.dot(data.T, data)/N
    u, s, v = la.svd(cov, full_matrices=False)
    u = u[:,0:pca_d]
    X = np.dot(data, u)

    tsne = TSNE()
    Y = tsne.run(X, N, pca_d, d, perplexity, theta)
    return Y

## Instruction:
FIX (from Justin Bayer): avoid memory segfault when pca_d is choosen too big.
## Code After:
import scipy.linalg as la
import numpy as np


from fasttsne import _TSNE as TSNE


def fast_tsne(data, pca_d=None, d=2, perplexity=30., theta=0.5):
    """
    Run Barnes-Hut T-SNE on _data_.

    @param data         The data.

    @param pca_d        The dimensionality of data is reduced via PCA
                        to this dimensionality.

    @param d            The embedding dimensionality. Must be fixed to
                        2.

    @param perplexity   The perplexity controls the effective number of
                        neighbors.

    @param theta        If set to 0, exact t-SNE is run, which takes
                        very long for dataset > 5000 samples.
    """
    N, _ = data.shape
    
    # inplace!!

    if pca_d is None:
        X = data
    else:
        # do PCA
        data -= data.mean(axis=0)
    
        # working with covariance + (svd on cov.) is 
        # much faster than svd on data directly.
        cov = np.dot(data.T, data)/N
        u, s, v = la.svd(cov, full_matrices=False)
        u = u[:,0:pca_d]
        X = np.dot(data, u)

    tsne = TSNE()
    Y = tsne.run(X, N, X.shape[1], d, perplexity, theta)
    return Y
