'Returns an op that runs an eval on an analogy dataset.
The eval dataset is assumed to be tab-separated, with four tokens per
line. The first three tokens are query terms, the last is the expected
answer. For each line (e.g., "man king woman queen"), the vectors
corresponding to the query terms are added ("king - man + woman") to produce
a query vector.  If the expected answer\'s vector is the nearest neighbor to
the query vector (not counting any of the query vectors themselves), then
the line is scored as correct.  The reported accuracy is the number of
correct rows divided by the total number of rows.  Missing terms are
replaced with an arbitrary vector and will almost certainly result in
incorrect answers.
Note that the results are approximate: for efficiency\'s sake, only the first
`max_vocab_size` terms are included in the nearest neighbor search.
Args:
filename: the filename containing the analogy data.
max_vocab_size: the maximum number of tokens to include in the nearest
neighbor search. By default, 20000.
Returns:
The accuracy on the analogy task.'

def analogy_eval_op(self, filename, max_vocab_size=20000):

    analogy_ixs = []
    with open(filename, 'r') as lines:
        for line in lines:
            parts = line.strip().split(' DCTB ')
            if (len(parts) == 4):
                analogy_ixs.append([self.row_word_to_ix.get(w, 0) for w in parts])
    (ix1s, ix2s, ix3s, _) = zip(*analogy_ixs)
    (v1s_t, v2s_t, v3s_t) = (tf.nn.l2_normalize(tf.nn.embedding_lookup(self.row_embedding, ixs), dim=1) for ixs in (ix1s, ix2s, ix3s))
    preds_t = ((v2s_t - v1s_t) + v3s_t)
    sims_t = tf.matmul(preds_t, tf.nn.l2_normalize(self.row_embedding[:max_vocab_size], dim=1), transpose_b=True)
    (_, preds_ixs_t) = tf.nn.top_k(sims_t, 4)
    def _op(preds_ixs):
        (correct, total) = (0, 0)
        for (pred_ixs, actual_ixs) in itertools.izip(preds_ixs, analogy_ixs):
            pred_ixs = [ix for ix in pred_ixs if (ix not in actual_ixs[:3])]
            correct += (pred_ixs[0] == actual_ixs[3])
            total += 1
        return (correct / total)
    return tf.py_func(_op, [preds_ixs_t], tf.float64)
