I have a very simple example of 6 training data with 6 labels. I am trying to fit a simple logistic regression to it, and hoping it should over-fit with 0 loss, but the model does not converge.
Here is the rule, i am hoping the model to learn:

When input is  [1., 0., 0., 1.] , the label is 1
When input is  [0., 1., 1., 0.] , the label is 1
When input is  [1., 0., 1., 0.] , the label is 0
When input is  [0., 1., 0., 1.] , the label is 0

is such pattern something a non-linear model cannot capture ? Any idea what i am doing wrong ?
import torch
import torch.nn as nn


training_samples = torch.tensor([[0., 1., 1., 0.],
                                 [1., 0., 0., 1.],
                                 [0., 1., 0., 1.],
                                 [1., 0., 1., 0.]])

labels = torch.tensor([1., 1., 0., 0.]).view(-1, 1)


def normalize(x):
    x_normed = (x - x.mean(0, keepdim=True)[0])/(x.std(0, keepdim=True)[0])
    return x_normed



class LogisticRegression(torch.nn.Module):
    def __init__(self):
        super(LogisticRegression, self).__init__()
        self.linear1 = torch.nn.Linear(4, 2) 
        self.linear2 = torch.nn.Linear(2, 2) 
        self.linear3 = torch.nn.Linear(2, 1)
        
        
    def forward(self, x):
        x = torch.relu(self.linear1(x))
        x = torch.relu(self.linear2(x))
        x = torch.sigmoid(self.linear3(x))
        return x
    


model = LogisticRegression()
criterion = torch.nn.BCELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=.01)


model.train()
for epoch in range(10):
    optimizer.zero_grad()
    shuffle = torch.randperm(len(labels))  # shuffling the training data 
    labels = labels[shuffle] # shuffle labels
    training_samples = training_samples[shuffle]# shuffle features 
    y_pred = model(normalize(training_samples))    # normalization does not help
    loss = criterion(y_pred, labels)   
    loss.backward()
    optimizer.step()
    print('Epoch %d | Loss: %.4f' % (epoch, loss.item()))