'Run in prediction mode with beam search. The input is the vector Xi, which
should be a 1-D array that contains the encoded image vector. We go from there.
Ws should be NxD array where N is size of vocabulary + 1. So there should be exactly
as many rows in Ws as there are outputs in the decoder Y. We are passing in Ws like
this because we may not want it to be exactly model[\'Ws\']. For example it could be
fixed word vectors from somewhere else.'

@staticmethod
def predict(Xi, model, Ws, params, **kwargs):

    tanhC_version = params['tanhC_version']
    beam_size = kwargs.get('beam_size', 1)
    WLSTM = model['WLSTM']
    d = model['Wd'].shape[0]
    Wd = model['Wd']
    bd = model['bd']
    def LSTMtick(x, h_prev, c_prev):
        t = 0
        Hin = np.zeros((1, WLSTM.shape[0]))
        Hin[(t, 0)] = 1
        Hin[t, 1:(1 + d)] = x
        Hin[t, (1 + d):] = h_prev
        IFOG = np.zeros((1, (d * 4)))
        IFOGf = np.zeros((1, (d * 4)))
        C = np.zeros((1, d))
        Hout = np.zeros((1, d))
        IFOG[t] = Hin[t].dot(WLSTM)
        IFOGf[t, :(3 * d)] = (1.0 / (1.0 + np.exp((- IFOG[t, :(3 * d)]))))
        IFOGf[t, (3 * d):] = np.tanh(IFOG[t, (3 * d):])
        C[t] = ((IFOGf[t, :d] * IFOGf[t, (3 * d):]) + (IFOGf[t, d:(2 * d)] * c_prev))
        if tanhC_version:
            Hout[t] = (IFOGf[t, (2 * d):(3 * d)] * np.tanh(C[t]))
        else:
            Hout[t] = (IFOGf[t, (2 * d):(3 * d)] * C[t])
        Y = (Hout.dot(Wd) + bd)
        return (Y, Hout, C)
    (y0, h, c) = LSTMtick(Xi, np.zeros(d), np.zeros(d))
    if (beam_size > 1):
        beams = [(0.0, [], h, c)]
        nsteps = 0
        while True:
            beam_candidates = []
            for b in beams:
                ixprev = (b[1][(-1)] if b[1] else 0)
                if ((ixprev == 0) and b[1]):
                    beam_candidates.append(b)
                    continue
                (y1, h1, c1) = LSTMtick(Ws[ixprev], b[2], b[3])
                y1 = y1.ravel()
                maxy1 = np.amax(y1)
                e1 = np.exp((y1 - maxy1))
                p1 = (e1 / np.sum(e1))
                y1 = np.log((1e-20 + p1))
                top_indices = np.argsort((- y1))
                for i in xrange(beam_size):
                    wordix = top_indices[i]
                    beam_candidates.append(((b[0] + y1[wordix]), (b[1] + [wordix]), h1, c1))
            beam_candidates.sort(reverse=True)
            beams = beam_candidates[:beam_size]
            nsteps += 1
            if (nsteps >= 20):
                break
        predictions = [(b[0], b[1]) for b in beams]
    else:
        ixprev = 0
        nsteps = 0
        predix = []
        predlogprob = 0.0
        while True:
            (y1, h, c) = LSTMtick(Ws[ixprev], h, c)
            (ixprev, ixlogprob) = ymax(y1)
            predix.append(ixprev)
            predlogprob += ixlogprob
            nsteps += 1
            if ((ixprev == 0) or (nsteps >= 20)):
                break
        predictions = [(predlogprob, predix)]
    return predictions
