def METHOD_NAME(
    model: Model[Padded, Padded], Xp: Padded, is_train: bool
) -> Tuple[Padded, Callable]:
    dropout = model.attrs["dropout_rate"]
    Xr = _padded_to_packed(model.ops, Xp)
    LSTM = cast(Floats1d, model.get_param("LSTM"))
    HC0 = cast(Floats4d, model.get_param("HC0"))
    H0 = HC0[0]
    C0 = HC0[1]
    if is_train:
        # Apply dropout over *weights*, not *activations*. RNN activations are
        # heavily correlated, so dropout over the activations is less effective.
        # This trick was explained in Yarin Gal's thesis, and popularised by
        # Smerity in the AWD-LSTM. It also means we can do the dropout outside
        # of the backend, improving compatibility.
        mask = cast(Floats1d, model.ops.get_dropout_mask(LSTM.shape, dropout))
        LSTM = LSTM * mask
        Y, fwd_state = model.ops.lstm_forward_training(
            LSTM, H0, C0, cast(Floats2d, Xr.data), Xr.lengths
        )
    else:
        Y = model.ops.lstm_forward_inference(
            LSTM, H0, C0, cast(Floats2d, Xr.data), Xr.lengths
        )
        fwd_state = tuple()
    assert Y.shape == (Xr.data.shape[0], Y.shape[1]), (Xr.data.shape, Y.shape)
    Yp = _packed_to_padded(model.ops, Ragged(Y, Xr.lengths), Xp)
    def backprop(dYp: Padded) -> Padded:
        assert fwd_state
        dYr = _padded_to_packed(model.ops, dYp)
        dX, dLSTM = model.ops.backprop_lstm(
            cast(Floats2d, dYr.data), dYr.lengths, LSTM, fwd_state
        )
        dLSTM *= mask
        model.inc_grad("LSTM", dLSTM)
        return _packed_to_padded(model.ops, Ragged(dX, dYr.lengths), dYp)
    return Yp, backprop