From 57415c461068f8a3d7cdca7e274dd8bc039d9efa Mon Sep 17 00:00:00 2001
From: David DeTomaso <davedeto@gmail.com>
Date: Thu, 1 Jun 2017 04:14:35 -0700
Subject: [PATCH] [MRG] Fix gradient descent in TSNE (#8768)

Skip flaky test_n_iter_without_progress on 32bit
---
 doc/whats_new.rst                    |  2 ++
 sklearn/manifold/t_sne.py            | 16 ++++++++--------
 sklearn/manifold/tests/test_t_sne.py | 10 ++++++----
 3 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/doc/whats_new.rst b/doc/whats_new.rst
index 492a0647ee..dfc2be923e 100644
--- a/doc/whats_new.rst
+++ b/doc/whats_new.rst
@@ -292,6 +292,8 @@ Bug fixes
      when the length of features_names does not match n_features in the decision
      tree.
      :issue:`8512` by :user:`Li Li <aikinogard>`.
+   - Fixed a bug in :class:`manifold.TSNE` affecting convergence of the
+     gradient descent. :issue:`8768` by :user:`David DeTomaso <deto>`.
 
 API changes summary
 -------------------
diff --git a/sklearn/manifold/t_sne.py b/sklearn/manifold/t_sne.py
index b31f34d9ee..30b9ca16e8 100644
--- a/sklearn/manifold/t_sne.py
+++ b/sklearn/manifold/t_sne.py
@@ -388,11 +388,11 @@ def _gradient_descent(objective, p0, it, n_iter, objective_error=None,
         new_error, grad = objective(p, *args, **kwargs)
         grad_norm = linalg.norm(grad)
 
-        inc = update * grad >= 0.0
+        inc = update * grad < 0.0
         dec = np.invert(inc)
-        gains[inc] += 0.05
-        gains[dec] *= 0.95
-        np.clip(gains, min_gain, np.inf)
+        gains[inc] += 0.2
+        gains[dec] *= 0.8
+        np.clip(gains, min_gain, np.inf, out=gains)
         grad *= gains
         update = momentum * update - learning_rate * grad
         p += update
@@ -631,10 +631,10 @@ class TSNE(BaseEstimator):
     >>> model = TSNE(n_components=2, random_state=0)
     >>> np.set_printoptions(suppress=True)
     >>> model.fit_transform(X) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
-    array([[ 0.00017599,  0.00003993],
-           [ 0.00009891,  0.00021913],
-           [ 0.00018554, -0.00009357],
-           [ 0.00009528, -0.00001407]])
+    array([[ 0.00017619,  0.00004014],
+           [ 0.00010268,  0.00020546],
+           [ 0.00018298, -0.00008335],
+           [ 0.00009501, -0.00001388]])
 
     References
     ----------
diff --git a/sklearn/manifold/tests/test_t_sne.py b/sklearn/manifold/tests/test_t_sne.py
index ea9037776d..52c056a5ad 100644
--- a/sklearn/manifold/tests/test_t_sne.py
+++ b/sklearn/manifold/tests/test_t_sne.py
@@ -12,6 +12,7 @@ from sklearn.utils.testing import assert_array_almost_equal
 from sklearn.utils.testing import assert_less
 from sklearn.utils.testing import assert_raises_regexp
 from sklearn.utils.testing import assert_in
+from sklearn.utils.testing import skip_if_32bit
 from sklearn.utils import check_random_state
 from sklearn.manifold.t_sne import _joint_probabilities
 from sklearn.manifold.t_sne import _joint_probabilities_nn
@@ -563,12 +564,13 @@ def test_index_offset():
     assert_equal(_barnes_hut_tsne.test_index_offset(), 1)
 
 
+@skip_if_32bit
 def test_n_iter_without_progress():
-    # Make sure that the parameter n_iter_without_progress is used correctly
+    # Use a dummy negative n_iter_without_progress and check output on stdout
     random_state = check_random_state(0)
     X = random_state.randn(100, 2)
-    tsne = TSNE(n_iter_without_progress=2, verbose=2,
-                random_state=0, method='exact')
+    tsne = TSNE(n_iter_without_progress=-1, verbose=2,
+                random_state=1, method='exact')
 
     old_stdout = sys.stdout
     sys.stdout = StringIO()
@@ -581,7 +583,7 @@ def test_n_iter_without_progress():
 
     # The output needs to contain the value of n_iter_without_progress
     assert_in("did not make any progress during the "
-              "last 2 episodes. Finished.", out)
+              "last -1 episodes. Finished.", out)
 
 
 def test_min_grad_norm():
-- 
GitLab