Calculate coherence metric according to Mimno et al. 2011 (a.k.a. "U_Mass" coherence metric). There are two
    modifications to the originally suggested measure:
    - uses a different epsilon by default (set `eps=1` for original)
    - uses a normalizing constant by default (set `normalize=False` for original)

    Provide a topic word distribution $\phi$ as `topic_word_distrib` and a document-term-matrix `dtm` (can be sparse).
    `top_n` controls how many most probable words per topic are selected.

    By default, it will return a NumPy array of coherence values per topic (same ordering as in `topic_word_distrib`).
    Set `return_mean` to True to return the mean of all topics instead.

    D. Mimno, H. Wallach, E. Talley, M. Leenders, A. McCullum 2011: Optimizing semantic coherence in topic models

def metric_coherence_mimno_2011(topic_word_distrib, dtm, top_n=20, eps=1e-12, normalize=True, return_mean=False):
    """
    Calculate coherence metric according to Mimno et al. 2011 (a.k.a. "U_Mass" coherence metric). There are two
    modifications to the originally suggested measure:
    - uses a different epsilon by default (set `eps=1` for original)
    - uses a normalizing constant by default (set `normalize=False` for original)

    Provide a topic word distribution $\phi$ as `topic_word_distrib` and a document-term-matrix `dtm` (can be sparse).
    `top_n` controls how many most probable words per topic are selected.

    By default, it will return a NumPy array of coherence values per topic (same ordering as in `topic_word_distrib`).
    Set `return_mean` to True to return the mean of all topics instead.

    D. Mimno, H. Wallach, E. Talley, M. Leenders, A. McCullum 2011: Optimizing semantic coherence in topic models
    """
    n_topics, n_vocab = topic_word_distrib.shape

    if n_vocab != dtm.shape[1]:
        raise ValueError('shapes of provided `topic_word_distrib` and `dtm` do not match (vocab sizes differ)')

    if top_n > n_vocab:
        raise ValueError('`top_n=%d` is larger than the vocabulary size of %d words'
                         % (top_n, topic_word_distrib.shape[1]))

    top_words = top_words_for_topics(topic_word_distrib, top_n)   # V

    if issparse(dtm) and dtm.format != 'csc':
        dtm = dtm.tocsc()

    coh = []
    for t in range(n_topics):
        c_t = 0

        v = top_words[t]
        top_dtm = dtm[:, v]
        df = get_doc_frequencies(top_dtm)      # D(v)
        codf = get_codoc_frequencies(top_dtm)  # D(v, v')

        for m in range(1, top_n):
            for l in range(m):
                c_t += np.log((codf.get((m, l), codf.get((l, m))) + eps) /
                              df[l])

        coh.append(c_t)

    coh = np.array(coh)

    if normalize:
        coh *= 2 / (top_n * (top_n-1))

    if return_mean:
        return coh.mean()
    else:
        return coh