@albanD. Thanks for a quick response. That’s what I did, and looks like it’s working. There is no any for loop required initially, but I’m getting a different for loop at the end, and I do not know how to avoid it. I exactly did what you just said.
My approach :
I first concatenated all tensors into a 1D tensor, then used this concatenated tensor to index the matrix W.
concatenated = torch.cat(l1, dim=0)
concatenated.shape
>>>
torch.Size([27])

splits = tuple(torch.tensor([i.shape for i in l1]))

selected_rows = W[concatenated]

splits is defined to later split the concatenated result.
(Note that there is one for loop while defining splits, but I thought I must include it, it might not be that costly). After this point, I want to multiply all these rows with a 300 dimensional vector h, and I do it as follows.
dot_prods = torch.matmul(selected_rows, h)

# Now its time to split this single tensor according to the original list l1

splitted_dot_prods = torch.split(dot_prods, splits)
splitted_dot_prods 
>>> 
(tensor([ -8.1478, -21.9214]),
 tensor([-8.1478, 21.9214]),
 tensor([  8.1478,  33.1918,  -7.2199, -17.2290,   6.6762]),
 tensor([  8.1478,  33.1918,  -7.2199, -17.2290,  -6.6762]),
 tensor([ 8.1478, 33.1918, -7.2199, 17.2290]),
 tensor([ 8.1478, 33.1918,  7.2199]),
 tensor([  8.1478, -33.1918,  24.0853]),
 tensor([  8.1478, -33.1918, -24.0853]))

As you can see, now I’m getting a tuple of tensors, and for each tensor in this tuple, I want to get the sum of the tensor (torch.sum). Again a second for loop is coming on the way.
Is my approach any more efficient than the one I proposed earlier? Can you now identify any way to avoid any of the for loop?