//  [python]
def beam_ensemble_speak(beam_size, models, ensemble_average_probs, states, actions,
                        return_beam=False, max_utterance_length=35, length_constraints=None):

    assert len(states) == len(actions) + 1
    if length_constraints:
        assert len(length_constraints) == len(actions)
        assert all(lc <= max_utterance_length for lc in length_constraints)
    assert all(m.vocab_embeddings.int_to_word == models[0].vocab_embeddings.int_to_word for m in models[1:])
    EOS_INDEX = models[0].vocab_embeddings.EOS_INDEX

    BOS = models[0].vocab_embeddings.BOS
    EOS = models[0].vocab_embeddings.EOS

    initialize_decoder_fns, predict_word_fns, choose_word_fns = zip(*[
        model.state_machine(states, actions, testing=True) for model in models
    ])

    utterance_beams = []

    for i in range(len(actions)):
        token_count = 0
        completed = []

        beam = [(0, [init_decoder(blank_inference_state(i)) for init_decoder in initialize_decoder_fns])]

        while len(completed) < beam_size:

            successors = []
            for (beam_item_score, inference_states) in beam:
                inference_states = [predict(inf_state)
                                    for predict, inf_state in zip(predict_word_fns, inference_states)]

                lps = [inf_state.log_probs.npvalue() for inf_state in inference_states]
                if ensemble_average_probs:
                    log_probs = scipy.special.logsumexp(lps, axis=0) - np.log(len(models))
                else:
                    log_probs = np.mean(lps, axis=0)

                #num_successors = min(beam_size, len(scored_actions[scored_actions > 0]))
                num_successors = min(beam_size, len(log_probs))
                if token_count >= max_utterance_length or (length_constraints and token_count >= length_constraints[i]):
                    successor_token_ixs = [EOS_INDEX]
                else:
                    successor_token_ixs = np.argpartition(log_probs, -num_successors)[-num_successors:]

                for successor_number, token_index in enumerate(reversed(successor_token_ixs)):
                    if length_constraints and token_count < length_constraints[i] and token_index == EOS_INDEX:
                        continue
                    successor_inference_states = [choose(inf_state, token_index)
                                                  for choose, inf_state in zip(choose_word_fns, inference_states)]
                    marginal_log_prob = log_probs[token_index]
                    successor_score = beam_item_score + marginal_log_prob

                    complete = token_index == EOS_INDEX
                    successors.append((complete, successor_score, successor_inference_states))

            successors = sorted(successors, key=lambda t: t[1], reverse=True)[:beam_size]
            beam = []
            for (complete, score, inf_states) in successors:
                if complete:
                    completed.append((score, inf_states))
                else:
                    beam.append((score, inf_states))

            token_count += 1

        completed = sorted(completed, key=lambda t: t[0], reverse=True)[:beam_size]

        utterance_beams.append([_unpack_beam_item(bi, BOS=BOS, EOS=EOS) for bi in completed])

    if return_beam:
        return utterance_beams
    else:
        return [beam[0] for beam in utterance_beams]