Repository: piskvorky/gensim

Issue:
Mismatch get_coherence_per_topic and get_coherence for single topic
#### Problem description

Hi! I am using Gensim to compute the NPMI coherence for each of my topics. I used the method `get_coherence_per_topic()` and also `get_coherence()` (in this case, just passing a list with a single topic), and I noticed that the coherences per topic do not match with the ones returned by `get_coherence()` of the corresponding topics. In my understanding, the NPMI of a topic should be independent of the number of topics or of the other input topics. 
This happens also with the other c_* coherences, not with the UMASS version. 

Thank you!

#### Steps/code/corpus to reproduce

```python
from gensim.test.utils import common_texts, common_dictionary
from gensim.models.ldamodel import LdaModel
from gensim.models.coherencemodel import CoherenceModel

topics = [
    ['human', 'computer', 'system', 'interface'],
    ['graph', 'minors', 'trees', 'eps']
]

cm = CoherenceModel(topics=topics, texts=common_texts, coherence='c_npmi', 
                    dictionary=common_dictionary)
coherence = cm.get_coherence_per_topic()  
print(coherence) # got [0.23583958321789514, -0.24456941091456053]

cm_topic0 = CoherenceModel(topics=[topics[0]], texts=common_texts, 
                           coherence='c_npmi', dictionary=common_dictionary)
coherence_topic0 = cm_topic0.get_coherence()  
print(coherence_topic0) # expect this to be == coherence[0] but got -0.14624062517782566

cm_topic1 = CoherenceModel(topics=[topics[1]], texts=common_texts, 
                           coherence='c_npmi', dictionary=common_dictionary)
coherence_topic1 = cm_topic1.get_coherence()  
print(coherence_topic1) # expect this to be == coherence[1] but got -0.31633310918174923

```

#### Versions

Linux-5.4.104+-x86_64-with-Ubuntu-18.04-bionic
Python 3.7.10 (default, May  3 2021, 02:48:31) 
[GCC 7.5.0]
Bits 64
NumPy 1.19.5
SciPy 1.4.1
gensim 3.8.3
FAST_VERSION 1


Discussion:

Resolving patch:
diff --git a/gensim/topic_coherence/text_analysis.py b/gensim/topic_coherence/text_analysis.py
--- a/gensim/topic_coherence/text_analysis.py
+++ b/gensim/topic_coherence/text_analysis.py
@@ -300,18 +300,11 @@ def accumulate(self, texts, window_size):
     def _iter_texts(self, texts):
         dtype = np.uint16 if np.iinfo(np.uint16).max >= self._vocab_size else np.uint32
         for text in texts:
-            if self.text_is_relevant(text):
-                yield np.fromiter((
-                    self.id2contiguous[self.token2id[w]] if w in self.relevant_words
-                    else self._none_token
-                    for w in text), dtype=dtype, count=len(text))
-
-    def text_is_relevant(self, text):
-        """Check if the text has any relevant words."""
-        for word in text:
-            if word in self.relevant_words:
-                return True
-        return False
+            ids = (
+                self.id2contiguous[self.token2id[w]] if w in self.relevant_words else self._none_token
+                for w in text
+            )
+            yield np.fromiter(ids, dtype=dtype, count=len(text))
 
 
 class InvertedIndexAccumulator(WindowedTextsAnalyzer, InvertedIndexBased):


Test patch:
diff --git a/gensim/test/test_coherencemodel.py b/gensim/test/test_coherencemodel.py
--- a/gensim/test/test_coherencemodel.py
+++ b/gensim/test/test_coherencemodel.py
@@ -50,6 +50,11 @@ def setUp(self):
             ['not a token', 'not an id', 'tests using', "this list"],
             ['should raise', 'an error', 'to pass', 'correctly']
         ]
+        # list of topics with unseen words in the dictionary
+        self.topics5 = [
+            ['aaaaa', 'bbbbb', 'ccccc', 'eeeee'],
+            ['ddddd', 'fffff', 'ggggh', 'hhhhh']
+        ]
         self.topicIds1 = []
         for topic in self.topics1:
             self.topicIds1.append([self.dictionary.token2id[token] for token in topic])
@@ -70,8 +75,14 @@ def check_coherence_measure(self, coherence):
         cm2 = CoherenceModel(topics=self.topics2, **kwargs)
         cm3 = CoherenceModel(topics=self.topics3, **kwargs)
         cm4 = CoherenceModel(topics=self.topicIds1, **kwargs)
+
+        # check if the same topic always returns the same coherence value
+        cm5 = CoherenceModel(topics=[self.topics1[0]], **kwargs)
+
         self.assertRaises(ValueError, lambda: CoherenceModel(topics=self.topics4, **kwargs))
+        self.assertRaises(ValueError, lambda: CoherenceModel(topics=self.topics5, **kwargs))
         self.assertEqual(cm1.get_coherence(), cm4.get_coherence())
+        self.assertEqual(cm1.get_coherence_per_topic()[0], cm5.get_coherence())
         self.assertIsInstance(cm3.get_coherence(), np.double)
         self.assertGreater(cm1.get_coherence(), cm2.get_coherence())