'Xi is 1-d array of size D1 (containing the image representation)
Xs is N x D2 (N time steps, rows are data containng word representations), and
it is assumed that the first row is already filled in as the start token. So a
sentence with 10 words will be of size 11xD2 in Xs.'

@staticmethod
def forward(Xi, Xs, model, params, **kwargs):

    predict_mode = kwargs.get('predict_mode', False)
    drop_prob_encoder = params.get('drop_prob_encoder', 0.0)
    drop_prob_decoder = params.get('drop_prob_decoder', 0.0)
    relu_encoders = params.get('rnn_relu_encoders', 0)
    rnn_feed_once = params.get('rnn_feed_once', 0)
    if (drop_prob_encoder > 0):
        if (not predict_mode):
            scale = (1.0 / (1.0 - drop_prob_encoder))
            Us = ((np.random.rand(*Xs.shape) < (1 - drop_prob_encoder)) * scale)
            Xs *= Us
            Ui = ((np.random.rand(*Xi.shape) < (1 - drop_prob_encoder)) * scale)
            Xi *= Ui
    Wxh = model['Wxh']
    bxh = model['bxh']
    Xsh = (Xs.dot(Wxh) + bxh)
    if relu_encoders:
        Xsh = np.maximum(Xsh, 0)
        Xi = np.maximum(Xi, 0)
    d = model['Wd'].shape[0]
    n = Xs.shape[0]
    H = np.zeros((n, d))
    Whh = model['Whh']
    bhh = model['bhh']
    for t in xrange(n):
        prev = (np.zeros(d) if (t == 0) else H[(t - 1)])
        if ((not rnn_feed_once) or (t == 0)):
            H[t] = np.maximum((((Xi + Xsh[t]) + prev.dot(Whh)) + bhh), 0)
        else:
            H[t] = np.maximum(((Xsh[t] + prev.dot(Whh)) + bhh), 0)
    if (drop_prob_decoder > 0):
        if (not predict_mode):
            scale2 = (1.0 / (1.0 - drop_prob_decoder))
            U2 = ((np.random.rand(*H.shape) < (1 - drop_prob_decoder)) * scale2)
            H *= U2
    Wd = model['Wd']
    bd = model['bd']
    Y = (H.dot(Wd) + bd)
    cache = {}
    if (not predict_mode):
        cache['Whh'] = Whh
        cache['H'] = H
        cache['Wd'] = Wd
        cache['Xs'] = Xs
        cache['Xsh'] = Xsh
        cache['Wxh'] = Wxh
        cache['Xi'] = Xi
        cache['relu_encoders'] = relu_encoders
        cache['drop_prob_encoder'] = drop_prob_encoder
        cache['drop_prob_decoder'] = drop_prob_decoder
        cache['rnn_feed_once'] = rnn_feed_once
        if (drop_prob_encoder > 0):
            cache['Us'] = Us
            cache['Ui'] = Ui
        if (drop_prob_decoder > 0):
            cache['U2'] = U2
    return (Y, cache)
