def concrete_forward(self, in_x: np.ndarray | "torch.Tensor") -> "torch.Tensor":
        """
        Do the forward pass using the concrete operations

        :param in_x: regular (concrete) data.
        :return: normal model predictions
        """
        if isinstance(in_x, np.ndarray):
            x = torch.from_numpy(in_x.astype("float32")).to(self.device)
        else:
            x = in_x
        for op_num, op in enumerate(self.ops):
            # as reshapes are not modules we infer when the reshape from convolutional to dense occurs
            if self.reshape_op_num == op_num:
                x = x.reshape((x.shape[0], -1))
            if isinstance(op, PyTorchIntervalConv2D) and self.forward_mode == "attack":
                x = op.conv_forward(x)
            else:
                x = op.concrete_forward(x)
        return x

        Do the forward pass using the concrete operations

        :param in_x: regular (concrete) data.
        :return: normal model predictions
        
art/estimators/certification/interval/pytorch.py