def _fine_grained_binary_search_local(
        self,
        x_0: np.ndarray,
        y_0: int,
        theta: np.ndarray,
        target: int | None = None,
        initial_lbd: float = 1.0,
        tol: float = 1e-5,
    ) -> tuple[float, int]:
        """
        Perform the line search in a local region plus binary search.
        Details in paper (Chen and Zhang, 2019), paper link: https://openreview.net/pdf?id=rJlk6iRqKX

        :param x_0: An array with the original input to be attacked.
        :param y_0: Target value.
        :param theta: Initial query direction.
        :param target: Target value. If `self.targeted` is true, it presents the targeted label. Defaults to None.
        :param initial_lbd: Previous solution. Defaults to 1.0.
        :param tol: Maximum tolerance of computed error. Stop computing if tol is reached.
        Defaults to 1e-5.
        :return: optimal solution in local; the number of query performed
        """
        nquery = 0
        lbd = initial_lbd
        # For targeted: we want to expand(x1.01) boundary away from targeted dataset
        # For untargeted, we want to slim(x0.99) the boundary toward the original dataset
        if (self.targeted and not self._is_label(x_0 + lbd * theta, target)) or (
            not self.targeted and self._is_label(x_0 + lbd * theta, y_0)
        ):
            lbd_lo = lbd
            lbd_hi = lbd * 1.01
            nquery += 1
            while (self.targeted and not self._is_label(x_0 + lbd_hi * theta, target)) or (
                not self.targeted and self._is_label(x_0 + lbd_hi * theta, y_0)
            ):
                lbd_hi = lbd_hi * 1.01
                nquery += 1
                if lbd_hi > 20:
                    return float("inf"), nquery
        else:
            lbd_hi = lbd
            lbd_lo = lbd * 0.99
            nquery += 1
            while (self.targeted and self._is_label(x_0 + lbd_lo * theta, target)) or (
                not self.targeted and not self._is_label(x_0 + lbd_lo * theta, y_0)
            ):
                lbd_lo = lbd_lo * 0.99
                nquery += 1

        while (lbd_hi - lbd_lo) > tol:
            lbd_mid = (lbd_lo + lbd_hi) / 2.0
            nquery += 1
            if (self.targeted and self._is_label(x_0 + lbd_mid * theta, target)) or (
                not self.targeted and not self._is_label(x_0 + lbd_mid * theta, y_0)
            ):
                lbd_hi = lbd_mid
            else:
                lbd_lo = lbd_mid
        return lbd_hi, nquery

        Perform the line search in a local region plus binary search.
        Details in paper (Chen and Zhang, 2019), paper link: https://openreview.net/pdf?id=rJlk6iRqKX

        :param x_0: An array with the original input to be attacked.
        :param y_0: Target value.
        :param theta: Initial query direction.
        :param target: Target value. If `self.targeted` is true, it presents the targeted label. Defaults to None.
        :param initial_lbd: Previous solution. Defaults to 1.0.
        :param tol: Maximum tolerance of computed error. Stop computing if tol is reached.
        Defaults to 1e-5.
        :return: optimal solution in local; the number of query performed
        
art/attacks/evasion/sign_opt.py