def forward(self, inp, hidden, label=None, need_hidden=False):
        """
        Embeds input and applies LSTM, concatenate category vector into each embedding
        :param inp: batch_size * seq_len
        :param label: batch_size, specific label index
        :param hidden: memory size
        :param need_hidden: if return hidden, use for sampling
        """
        assert type(label) == torch.Tensor, 'missing label'
        emb = self.embeddings(inp)  # batch_size * len * embedding_dim

        # cat category vector
        label_onehot = F.one_hot(label, self.k_label).float()  # batch_size * k_label
        label_onehot_ex = label_onehot.unsqueeze(1).expand(-1, inp.size(1), -1)  # batch_size * len * k_label
        label_vec = torch.bmm(label_onehot_ex, self.cat_mat.expand(inp.size(0), -1, -1))  # batch_size * len * embed_dim
        emb = torch.cat((emb, label_vec), dim=-1)  # batch_sie * len * (k_label + embed_dim)

        out, hidden = self.lstm(emb, hidden)  # out: batch_size * seq_len * hidden_dim
        out = out.contiguous().view(-1, self.hidden_dim)  # out: (batch_size * len) * hidden_dim
        out = self.lstm2out(out)  # batch_size * seq_len * vocab_size
        # out = self.temperature * out  # temperature
        pred = self.softmax(out)

        if need_hidden:
            return pred, hidden
        else:
            return pred

        Embeds input and applies LSTM, concatenate category vector into each embedding
        :param inp: batch_size * seq_len
        :param label: batch_size, specific label index
        :param hidden: memory size
        :param need_hidden: if return hidden, use for sampling
        
models/CatGAN_G.py