# Buggy code
def inference(self, chunk, collect_sstats=False):
    """
    Given a chunk of sparse document vectors, estimate gamma (parameters
    controlling the topic weights) for each document in the chunk.

    This function does not modify the model (=is read-only aka const). The
    whole input chunk of document is assumed to fit in RAM; chunking of a
    large corpus must be done earlier in the pipeline.

    If `collect_sstats` is True, also collect sufficient statistics needed
    to update the model's topic-word distributions, and return a 2-tuple
    `(gamma, sstats)`. Otherwise, return `(gamma, None)`. `gamma` is of shape
    `len(chunk) x self.num_topics`.

    Avoids computing the `phi` variational parameter directly using the
    optimization presented in **Lee, Seung: Algorithms for non-negative matrix factorization, NIPS 2001**.

    """
    try:
        _ = len(chunk)
    except:
        # convert iterators/generators to plain list, so we have len() etc.
        chunk = list(chunk)
    if len(chunk) > 1:
        logger.debug("performing inference on a chunk of %i documents", len(chunk))

    # Initialize the variational distribution q(theta|gamma) for the chunk
    gamma = self.random_state.gamma(100.0, 1.0 / 100.0, (len(chunk), self.num_topics))
    Elogtheta = dirichlet_expectation(gamma)
    expElogtheta = np.exp(Elogtheta)
    if collect_sstats:
        sstats = np.zeros_like(self.expElogbeta)
    else:
        sstats = None
    converged = 0

    # Now, for each document d update that document's gamma and phi
    # Inference code copied from Hoffman's `onlineldavb.py` (esp. the
    # Lee&Seung trick which speeds things up by an order of magnitude, compared
    # to Blei's original LDA-C code, cool!).
    for d, doc in enumerate(chunk):
        if doc and not isinstance(doc[0][0], six.integer_types):
            # make sure the term IDs are ints, otherwise np will get upset
            ids = [int(id) for id, _ in doc]
        else:
            ids = [id for id, _ in doc]
        cts = np.array([cnt for _, cnt in doc])
        gammad = gamma[d, :]
        Elogthetad = Elogtheta[d, :]
        expElogthetad = expElogtheta[d, :]
        expElogbetad = self.expElogbeta[:, ids]

        # The optimal phi_{dwk} is proportional to expElogthetad_k * expElogbetad_w.
        # phinorm is the normalizer.
        # TODO treat zeros explicitly, instead of adding 1e-100?
        phinorm = np.dot(expElogthetad, expElogbetad) + 1e-100

        # Iterate between gamma and phi until convergence
        for _ in xrange(self.iterations):
            lastgamma = gammad
            # We represent phi implicitly to save memory and time.
            # Substituting the value of the optimal phi back into
            # the update for gamma gives this update. Cf. Lee&Seung 2001.
            gammad = self.alpha + expElogthetad * np.dot(cts / phinorm, expElogbetad.T)
            Elogthetad = dirichlet_expectation(gammad)
            expElogthetad = np.exp(Elogthetad)
            phinorm = np.dot(expElogthetad, expElogbetad) + 1e-100
            # If gamma hasn't changed much, we're done.
            meanchange = np.mean(abs(gammad - lastgamma))
            if meanchange < self.gamma_threshold:
                converged += 1
                break
        gamma[d, :] = gammad
        if collect_sstats:
            # Contribution of document d to the expected sufficient
            # statistics for the M step.
            sstats[:, ids] += np.outer(expElogthetad.T, cts / phinorm)

    if len(chunk) > 1:
        logger.debug(
            "%i/%i documents converged within %i iterations",
            converged,
            len(chunk),
            self.iterations,
        )

    if collect_sstats:
        # This step finishes computing the sufficient statistics for the
        # M step, so that
        # sstats[k, w] = \sum_d n_{dw} * phi_{dwk}
        # = \sum_d n_{dw} * exp{Elogtheta_{dk} + Elogbeta_{kw}} / phinorm_{dw}.
        sstats *= self.expElogbeta
    return gamma, sstats


# Traceback (ValueError)
In [16]: model = models.LdaModel(corpus, id2word=dictionary, num_topics=2, distributed=True)
2016-10-03 09:21:04,056 : INFO : using symmetric alpha at 0.5
2016-10-03 09:21:04,056 : INFO : using symmetric eta at 0.5
2016-10-03 09:21:04,057 : DEBUG : looking for dispatcher at PYRO:gensim.lda_dispatcher@127.0.0.1:41212
2016-10-03 09:21:04,089 : INFO : using distributed version with 3 workers
2016-10-03 09:21:04,090 : INFO : running online LDA training, 2 topics, 1 passes over the supplied corpus of 9 documents, updating model once every 9 documents, evaluating perplexity every 9 documents, iterating 50x with a convergence threshold of 0.001000
2016-10-03 09:21:04,090 : WARNING : too few updates, training might not converge; consider increasing the number of passes or iterations to improve accuracy
2016-10-03 09:21:04,090 : INFO : initializing 3 workers
2016-10-03 09:21:04,097 : DEBUG : bound: at document #0
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-16-46e2552e21a7> in <module>()
----> 1 model = models.LdaModel(corpus, id2word=dictionary, num_topics=2, distributed=True)

/home/lev/Dropbox/raretech/os/release/gensim/gensim/models/ldamodel.py in __init__(self, corpus, num_topics, id2word, distributed, chunksize, passes, update_every, alpha, eta, decay, offset, eval_every, iterations, gamma_threshold, minimum_probability, random_state, ns_conf)
344         if corpus is not None:
345             use_numpy = self.dispatcher is not None
--> 346             self.update(corpus, chunks_as_numpy=use_numpy)
347
348     def init_dir_prior(self, prior, name):

/home/lev/Dropbox/raretech/os/release/gensim/gensim/models/ldamodel.py in update(self, corpus, chunksize, decay, offset, passes, update_every, eval_every, iterations, gamma_threshold, chunks_as_numpy)
648
649                 if eval_every and ((reallen == lencorpus) or ((chunk_no + 1) % (eval_every * self.numworkers) == 0)):
--> 650                     self.log_perplexity(chunk, total_docs=lencorpus)
651
652                 if self.dispatcher:

/home/lev/Dropbox/raretech/os/release/gensim/gensim/models/ldamodel.py in log_perplexity(self, chunk, total_docs)
539         corpus_words = sum(cnt for document in chunk for _, cnt in document)
540         subsample_ratio = 1.0 * total_docs / len(chunk)
--> 541         perwordbound = self.bound(chunk, subsample_ratio=subsample_ratio) / (subsample_ratio * corpus_words)
542         logger.info("%.3f per-word bound, %.1f perplexity estimate based on a held-out corpus of %i documents with %i words" %
543                     (perwordbound, numpy.exp2(-perwordbound), len(chunk), corpus_words))

/home/lev/Dropbox/raretech/os/release/gensim/gensim/models/ldamodel.py in bound(self, corpus, gamma, subsample_ratio)
740                 logger.debug("bound: at document #%i", d)
741             if gamma is None:
--> 742                 gammad, _ = self.inference([doc])
743             else:
744                 gammad = gamma[d]

/home/lev/Dropbox/raretech/os/release/gensim/gensim/models/ldamodel.py in inference(self, chunk, collect_sstats)
438         # to Blei's original LDA-C code, cool!).
439         for d, doc in enumerate(chunk):
--> 440             if doc and not isinstance(doc[0][0], six.integer_types):
441                 # make sure the term IDs are ints, otherwise numpy will get upset
442                 ids = [int(id) for id, _ in doc]

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

# Fixed code
def inference(self, chunk, collect_sstats=False):
    """
    Given a chunk of sparse document vectors, estimate gamma (parameters
    controlling the topic weights) for each document in the chunk.

    This function does not modify the model (=is read-only aka const). The
    whole input chunk of document is assumed to fit in RAM; chunking of a
    large corpus must be done earlier in the pipeline.

    If `collect_sstats` is True, also collect sufficient statistics needed
    to update the model's topic-word distributions, and return a 2-tuple
    `(gamma, sstats)`. Otherwise, return `(gamma, None)`. `gamma` is of shape
    `len(chunk) x self.num_topics`.

    Avoids computing the `phi` variational parameter directly using the
    optimization presented in **Lee, Seung: Algorithms for non-negative matrix factorization, NIPS 2001**.

    """
    try:
        _ = len(chunk)
    except:
        # convert iterators/generators to plain list, so we have len() etc.
        chunk = list(chunk)
    if len(chunk) > 1:
        logger.debug("performing inference on a chunk of %i documents", len(chunk))

    # Initialize the variational distribution q(theta|gamma) for the chunk
    gamma = self.random_state.gamma(100.0, 1.0 / 100.0, (len(chunk), self.num_topics))
    Elogtheta = dirichlet_expectation(gamma)
    expElogtheta = np.exp(Elogtheta)
    if collect_sstats:
        sstats = np.zeros_like(self.expElogbeta)
    else:
        sstats = None
    converged = 0

    # Now, for each document d update that document's gamma and phi
    # Inference code copied from Hoffman's `onlineldavb.py` (esp. the
    # Lee&Seung trick which speeds things up by an order of magnitude, compared
    # to Blei's original LDA-C code, cool!).
    for d, doc in enumerate(chunk):
        if len(doc) > 0 and not isinstance(doc[0][0], six.integer_types):
            # make sure the term IDs are ints, otherwise np will get upset
            ids = [int(id) for id, _ in doc]
        else:
            ids = [id for id, _ in doc]
        cts = np.array([cnt for _, cnt in doc])
        gammad = gamma[d, :]
        Elogthetad = Elogtheta[d, :]
        expElogthetad = expElogtheta[d, :]
        expElogbetad = self.expElogbeta[:, ids]

        # The optimal phi_{dwk} is proportional to expElogthetad_k * expElogbetad_w.
        # phinorm is the normalizer.
        # TODO treat zeros explicitly, instead of adding 1e-100?
        phinorm = np.dot(expElogthetad, expElogbetad) + 1e-100

        # Iterate between gamma and phi until convergence
        for _ in xrange(self.iterations):
            lastgamma = gammad
            # We represent phi implicitly to save memory and time.
            # Substituting the value of the optimal phi back into
            # the update for gamma gives this update. Cf. Lee&Seung 2001.
            gammad = self.alpha + expElogthetad * np.dot(cts / phinorm, expElogbetad.T)
            Elogthetad = dirichlet_expectation(gammad)
            expElogthetad = np.exp(Elogthetad)
            phinorm = np.dot(expElogthetad, expElogbetad) + 1e-100
            # If gamma hasn't changed much, we're done.
            meanchange = np.mean(abs(gammad - lastgamma))
            if meanchange < self.gamma_threshold:
                converged += 1
                break
        gamma[d, :] = gammad
        if collect_sstats:
            # Contribution of document d to the expected sufficient
            # statistics for the M step.
            sstats[:, ids] += np.outer(expElogthetad.T, cts / phinorm)

    if len(chunk) > 1:
        logger.debug(
            "%i/%i documents converged within %i iterations",
            converged,
            len(chunk),
            self.iterations,
        )

    if collect_sstats:
        # This step finishes computing the sufficient statistics for the
        # M step, so that
        # sstats[k, w] = \sum_d n_{dw} * phi_{dwk}
        # = \sum_d n_{dw} * exp{Elogtheta_{dk} + Elogbeta_{kw}} / phinorm_{dw}.
        sstats *= self.expElogbeta
    return gamma, sstats