A not that elegant way is to move the definition of optimizer inside the For loop. Will this generate thousands of versions of the tensor x in the memory?
For i in range(iteration):
    optimizer = torch.optim.SGD([x], lr=0.3)
    
    optimizer.zero_grad()
    probs = myfunc(x)
    probs.backward()
    optimizer.step() 

    x = projection(x) # _version number will be increased here