Repository: piskvorky/gensim

Issue:
`save/load_word2vec_format` fails for `FastText` models
#### Description
Saving and loading using `save_word2vec_format` and `load_word2vec_format` fails for both native FastText models and models loaded using the wrapper.
#### Steps/Code/Corpus to Reproduce
Example:
```python
from gensim.models import fasttext as ft
from gensim.models.wrappers import fasttext as ft_wrapper
from gensim.models.word2vec import Text8Corpus

corpus = Text8Corpus('gensim/test/test_data/lee_background.cor')
native_model = ft.FastText()
native_model.build_vocab(corpus)

print(native_model.wv.most_similar('wars'))
>>> # prints results

print(native_model.wv['wars'])
>>> # prints results

native_model.wv.save_word2vec_format('test.wv')
wv = ft_wrapper.FastTextKeyedVectors.load_word2vec_format('test.wv')
```

```
print(wv.most_similar('wars'))

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-43e12f136081> in <module>()
----> 1 print(wv.most_similar('wars'))

~/Projects/gensim/gensim/gensim/models/keyedvectors.py in most_similar(self, positive, negative, topn, restrict_vocab, indexer)
    318             negative = []
    319 
--> 320         self.init_sims()
    321 
    322         if isinstance(positive, string_types) and not negative:

~/Projects/gensim/gensim/gensim/models/wrappers/fasttext.py in init_sims(self, replace)
    125             else:
    126                 self.syn0_ngrams_norm = \
--> 127                     (self.syn0_ngrams / sqrt((self.syn0_ngrams ** 2).sum(-1))[..., newaxis]).astype(REAL)
    128 
    129     def __contains__(self, word):

TypeError: unsupported operand type(s) for ** or pow(): 'NoneType' and 'int'
```

```
print(wv['wars'])

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-ce05f767b013> in <module>()
----> 1 print(wv['wars'])

~/Projects/gensim/gensim/gensim/models/keyedvectors.py in __getitem__(self, words)
    601         if isinstance(words, string_types):
    602             # allow calls like trained_model['office'], as a shorthand for trained_model[['office']]
--> 603             return self.word_vec(words)
    604 
    605         return vstack([self.word_vec(word) for word in words])

~/Projects/gensim/gensim/gensim/models/wrappers/fasttext.py in word_vec(self, word, use_norm)
     91             return super(FastTextKeyedVectors, self).word_vec(word, use_norm)
     92         else:
---> 93             word_vec = np.zeros(self.syn0_ngrams.shape[1], dtype=np.float32)
     94             ngrams = compute_ngrams(word, self.min_n, self.max_n)
     95             ngrams = [ng for ng in ngrams if ng in self.ngrams]

AttributeError: 'NoneType' object has no attribute 'shape'
```

From a quick glance, it looks like this resulted from the changes made to `FastTextKeyedVectors` during the native implementation of `FastText` where two different matrices - `syn0_vocab` and `syn0_ngrams` were created.
Although, I'm not sure `save_word2vec_format` is even suitable for `FastText` seeing as how the ngram vectors aren't stored to disk.


Discussion:
How could this happen? Aren't we testing `save_word2vec_format`/`load_word2vec_format` in unit tests?
ping @chinmayapancholi13, can you comment this?

Resolving patch:
diff --git a/gensim/models/wrappers/fasttext.py b/gensim/models/wrappers/fasttext.py
--- a/gensim/models/wrappers/fasttext.py
+++ b/gensim/models/wrappers/fasttext.py
@@ -137,6 +137,11 @@ def __contains__(self, word):
             char_ngrams = compute_ngrams(word, self.min_n, self.max_n)
             return any(ng in self.ngrams for ng in char_ngrams)
 
+    @classmethod
+    def load_word2vec_format(cls, *args, **kwargs):
+        """Not suppported. Use gensim.models.KeyedVectors.load_word2vec_format instead."""
+        raise NotImplementedError("Not supported. Use gensim.models.KeyedVectors.load_word2vec_format instead.")
+
 
 class FastText(Word2Vec):
     """


Test patch:
diff --git a/gensim/test/test_fasttext.py b/gensim/test/test_fasttext.py
--- a/gensim/test/test_fasttext.py
+++ b/gensim/test/test_fasttext.py
@@ -9,6 +9,7 @@
 import numpy as np
 
 from gensim import utils
+from gensim.models import keyedvectors
 from gensim.models.word2vec import LineSentence
 from gensim.models.fasttext import FastText as FT_gensim
 from gensim.models.wrappers.fasttext import FastTextKeyedVectors
@@ -465,6 +466,17 @@ def test_get_vocab_word_vecs(self):
         model.get_vocab_word_vecs()
         self.assertTrue(np.all(np.equal(model.wv.syn0_vocab, original_syn0_vocab)))
 
+    def test_persistence_word2vec_format(self):
+        """Test storing/loading the model in word2vec format."""
+        tmpf = get_tmpfile('gensim_fasttext_w2v_format.tst')
+        model = FT_gensim(sentences, min_count=1, size=10)
+        model.wv.save_word2vec_format(tmpf, binary=True)
+        loaded_model_kv = keyedvectors.KeyedVectors.load_word2vec_format(tmpf, binary=True)
+        self.assertEqual(len(model.wv.vocab), len(loaded_model_kv.vocab))
+        self.assertTrue(np.allclose(model['human'], loaded_model_kv['human']))
+        self.assertRaises(DeprecationWarning, FT_gensim.load_word2vec_format, tmpf)
+        self.assertRaises(NotImplementedError, FastTextKeyedVectors.load_word2vec_format, tmpf)
+
 
 if __name__ == '__main__':
     logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.DEBUG)