def infer(self, x: np.ndarray, y: np.ndarray | None = None, **kwargs) -> np.ndarray:
        """
        Infer membership in the training set of the target estimator.

        :param x: Input records to attack. Can be None if supplying `pred`.
        :param y: True labels for `x`. If not supplied, attack will be based solely on model predictions.
        :param pred: Estimator predictions for the records, if not supplied will be generated by calling the estimators'
                     `predict` function. Only relevant for input_type='prediction'.
        :param probabilities: a boolean indicating whether to return the predicted probabilities per class, or just
                              the predicted class.
        :return: An array holding the inferred membership status, 1 indicates a member and 0 indicates non-member,
                 or class probabilities.
        """
        if "pred" in kwargs:
            pred = kwargs.get("pred")
        else:
            pred = None

        if "probabilities" in kwargs:
            probabilities = kwargs.get("probabilities")
        else:
            probabilities = False

        if x is None and pred is None:
            raise ValueError("Must supply either x or pred")

        if y is None and self.use_label:
            raise ValueError("y must be provided")

        if self.estimator.input_shape is not None and x is not None:  # pragma: no cover
            if self.estimator.input_shape[0] != x.shape[1]:
                raise ValueError("Shape of x does not match input_shape of estimator")

        if y is not None and not self._regressor_model:
            y = check_and_transform_label_format(y, nb_classes=self.estimator.nb_classes, return_one_hot=True)

        if x is not None and y is not None and y.shape[0] != x.shape[0]:  # pragma: no cover
            raise ValueError("Number of rows in x and y do not match")
        if pred is not None and y is not None and y.shape[0] != pred.shape[0]:  # pragma: no cover
            raise ValueError("Number of rows in pred and y do not match")

        if self.input_type == "prediction":
            if pred is None:
                features = self.estimator.predict(x).astype(np.float32)
            else:
                features = pred.astype(np.float32)
        elif self.input_type == "loss":
            if y is None:
                raise ValueError("Cannot compute loss values without y.")
            if x is not None:
                features = self.estimator.compute_loss(x, y).astype(np.float32).reshape(-1, 1)
            else:
                try:
                    features = self.estimator.compute_loss_from_predictions(pred, y).astype(np.float32).reshape(-1, 1)
                except NotImplementedError as err:
                    raise ValueError(
                        "For loss input type and no x, the estimator must implement 'compute_loss_from_predictions' "
                        "method"
                    ) from err
        else:
            raise ValueError("Value of `input_type` not recognized.")

        if y is not None and self._regressor_model:
            y = y.astype(np.float32).reshape(-1, 1)

        if self.default_model and self.attack_model_type == "nn":
            import torch
            from torch.utils.data import DataLoader
            from art.utils import to_cuda, from_cuda

            if self.scaler:
                features = self.scaler.transform(features)

            self.attack_model.eval()  # type: ignore
            predictions: np.ndarray | None = None

            if y is not None and self.use_label:
                test_set = self._get_attack_dataset(f_1=features, f_2=y)
                test_loader = DataLoader(test_set, batch_size=self.batch_size, shuffle=False, num_workers=0)
                for input1, input2, _ in test_loader:
                    input1, input2 = to_cuda(input1), to_cuda(input2)
                    outputs = self.attack_model(input1, input2)  # type: ignore
                    if not probabilities:
                        predicted = torch.round(outputs)
                    else:
                        predicted = outputs
                    predicted = from_cuda(predicted)

                    if predictions is None:
                        predictions = predicted.detach().numpy()
                    else:
                        predictions = np.vstack((predictions, predicted.detach().numpy()))
            else:
                test_set = self._get_attack_dataset_no_label(f_1=features)
                test_loader = DataLoader(test_set, batch_size=self.batch_size, shuffle=False, num_workers=0)
                for input1, _ in test_loader:
                    input1 = to_cuda(input1)
                    outputs = self.attack_model(input1)  # type: ignore
                    if not probabilities:
                        predicted = torch.round(outputs)
                    else:
                        predicted = outputs
                    predicted = from_cuda(predicted)

                    if predictions is None:
                        predictions = predicted.detach().numpy()
                    else:
                        predictions = np.vstack((predictions, predicted.detach().numpy()))
            if predictions is not None:
                if not probabilities:
                    inferred_return = np.round(predictions)
                else:
                    inferred_return = predictions
            else:  # pragma: no cover
                raise ValueError("No data available.")
        elif not self.default_model:
            # assumes the predict method of the supplied model returns probabilities
            if y is not None and self.use_label:
                features = np.c_[features, y]
                if self.scaler:
                    features = self.scaler.transform(features)
                inferred = self.attack_model.predict(features)  # type: ignore
            else:
                if self.scaler:
                    features = self.scaler.transform(features)
                inferred = self.attack_model.predict(features)  # type: ignore
            if probabilities:
                inferred_return = inferred
            else:
                inferred_return = np.round(inferred)
        else:
            if y is not None and self.use_label:
                features = np.c_[features, y]
                if self.scaler:
                    features = self.scaler.transform(features)
                inferred = self.attack_model.predict_proba(features)  # type: ignore
            else:
                if self.scaler:
                    features = self.scaler.transform(features)
                inferred = self.attack_model.predict_proba(features)  # type: ignore
            if probabilities:
                inferred_return = inferred[:, [1]]
            else:
                inferred_return = np.round(inferred[:, [1]])

        return inferred_return

        Infer membership in the training set of the target estimator.

        :param x: Input records to attack. Can be None if supplying `pred`.
        :param y: True labels for `x`. If not supplied, attack will be based solely on model predictions.
        :param pred: Estimator predictions for the records, if not supplied will be generated by calling the estimators'
                     `predict` function. Only relevant for input_type='prediction'.
        :param probabilities: a boolean indicating whether to return the predicted probabilities per class, or just
                              the predicted class.
        :return: An array holding the inferred membership status, 1 indicates a member and 0 indicates non-member,
                 or class probabilities.
        
art/attacks/inference/membership_inference/black_box.py