W_constraint alternative in Pytorch

I am trying to implement IOC Network and thus am required to impose a non negative weight rule for all the layers except the first(along with ELU as activation function to allow flow of negative values)

I have got a tensorflow code up and running perfectly, which is:

class WeightClip(Constraint):
    def __init__(self, c=100):
        self.c = c
    def __call__(self, p):
        return K.clip(p, 0, self.c)
    def get_config(self):
        return {'name': self.__class__.__name__,'c': self.c}
def get_model(model_select):
    inp_1 = Input(shape=(2,))
    l11 = Dense(8, activation='elu',kernel_initializer='glorot_uniform')(inp_1)
    if model_select == 'NN':
        l12 = Dense(8, activation='elu',kernel_initializer='glorot_uniform')(l11)
        l13 = Dense(8, activation='elu',kernel_initializer='glorot_uniform')(l12)
        out = Dense(1, activation='sigmoid',kernel_initializer='glorot_uniform')(l13)
    elif(model_select == 'IOC'):
        l12 = Dense(8, activation='elu',kernel_initializer='glorot_uniform',W_constraint = WeightClip(2))(l11)
        l13 = Dense(8, activation='elu',kernel_initializer='glorot_uniform',W_constraint = WeightClip(2))(l12)
        out = Dense(1, activation='sigmoid',kernel_initializer='glorot_uniform',W_constraint = WeightClip(2))(l13)
    else:
        print ("invalid model")
        return 0
    model = Model(inputs=[inp_1], outputs=[out])
    return model

However as suggest in this post, I tried to make my Weight Constraint class which I’m applying after every iteration.

My Pytorch code looks like:

class weightConstraint(object):
    def __init__(self):
        pass
    
    def __call__(self,module):
        if hasattr(module,'weight'):
#             print("Entered")
            w=module.weight.data
#             w[torch.where(w<0)] = 0
            w=w.clamp(min = 0)
            module.weight.data=w
class Net(nn.Module):
    def __init__(self,type_ = "single"):
        super().__init__()
        if type_ == "one-hot":
            self.type = type_
        elif type_ == "single":
            self.type = type_
        else:
            print('{} type not defined. Proceeding with default single'.format(type_))
            self.type = "single"
        
        self.activation = F.elu
        
        self.dense1 = nn.Linear(2, 8)
        self.dense2 = nn.Linear(8, 8)
        self.dense3 = nn.Linear(8, 8)
        self.dense4 = nn.Linear(8, 8)
        self.dense5 = nn.Linear(8, 1) if self.type == 'single' else nn.Linear(8, 2)
        if self.type == 'single':
            self.out_activation = nn.Sigmoid()
            
        
    def forward(self,x):
        out = x.view(x.shape[0],-1)
        out = self.activation(self.dense1(out))
        out = self.activation(self.dense2(out))
        out = self.activation(self.dense3(out))
        out = self.activation(self.dense4(out))
        out = self.dense5(out)
        if self.type == 'single':
            out = self.out_activation(out)
        return out

I am later using constraints = weightConstraint() for the IOC case, followed by a loop on the modules, applying the constraint after every iteration.

def applyIOCConstraint(model, constraint):
    if 'module' in model._modules.keys():
        # if DataParallel is used
        idx = 0
        for key,val in model._modules['module']._modules.items():
            if hasattr(val,'weight'):
                if idx>0:
                    val.apply(constraint)
                idx += 1
    else:
        idx = 0
        for key,val in model._modules.items():
            if hasattr(val,'weight'):
                if idx>0:
                    val.apply(constraint)
                idx += 1

However, this doesn’t work for some reason with my code. I’d like to know if you guys are spotting an incorrect usage in here, or otherwise, is their a option to add a weight constraint inherently in the layer itself