class_docstr: This module implements the Functionally Equivalent Extraction attack for neural networks with two dense layers,
  ReLU activation at the first layer and logits output after the second layer.
  
  | Paper link: https://arxiv.org/abs/1909.01838
class_docstr_tok: ["This","module","implements","the","Functionally","Equivalent","Extraction","attack","for","neural","networks","with","two","dense","layers",",","ReLU","activation","at","the","first","layer","and","logits","output","after","the","second","layer",".","|","Paper","link",":","https",":","\/\/arxiv.org\/abs\/1909.01838"]
docstr: Search for critical points.
  
  :param delta_0: Initial step size of binary search.
  :param fraction_true: Fraction of output predictions that have to fulfill criteria for critical point.
  :param rel_diff_slope: Relative slope difference at critical points.
  :param rel_diff_value: Relative value difference at critical points.
docstr_tok: ["Search","for","critical","points",".",":","param","delta_0",":","Initial","step","size","of","binary","search",".",":","param","fraction_true",":","Fraction","of","output","predictions","that","have","to","fulfill","criteria","for","critical","point",".",":","param","rel_diff_slope",":","Relative","slope","difference","at","critical","points",".",":","param","rel_diff_value",":","Relative","value","difference","at","critical","points","."]
code: def _critical_point_search(
      self,
      delta_0: float,
      fraction_true: float,
      rel_diff_slope: float,
      rel_diff_value: float,
  ) -> None:
      """
      Search for critical points.
  
      :param delta_0: Initial step size of binary search.
      :param fraction_true: Fraction of output predictions that have to fulfill criteria for critical point.
      :param rel_diff_slope: Relative slope difference at critical points.
      :param rel_diff_value: Relative value difference at critical points.
      """
      logger.info("Searching for critical points.")
  
      if self.num_neurons is None:
          raise ValueError(
              "The value of `num_neurons` is required for critical point search."
          )
      h_square = self.num_neurons * self.num_neurons
  
      t_current = float(-h_square)
      while t_current < h_square:
          delta = delta_0
          found_critical_point = False
  
          while not found_critical_point:
              epsilon = delta / 10
  
              t_1 = t_current
              t_2 = t_current + delta
  
              x_1 = self._get_x(t_1)
              x_1_p = self._get_x(t_1 + epsilon)
              x_2 = self._get_x(t_2)
              x_2_m = self._get_x(t_2 - epsilon)
  
              m_1 = (self._o_l(x_1_p) - self._o_l(x_1)) / epsilon
              m_2 = (self._o_l(x_2) - self._o_l(x_2_m)) / epsilon
  
              y_1 = self._o_l(x_1)
              y_2 = self._o_l(x_2)
  
              if (
                  np.sum(np.abs((m_1 - m_2) / m_1) < rel_diff_slope)
                  > fraction_true * self.num_classes
              ):
                  t_current = t_2
                  break
  
              t_hat = t_1 + np.divide(
                  y_2 - y_1 - (t_2 - t_1) * m_2, m_1 - m_2
              )
              y_hat = y_1 + m_1 * np.divide(
                  y_2 - y_1 - (t_2 - t_1) * m_2, m_1 - m_2
              )
  
              t_mean = np.mean(t_hat[t_hat != -np.inf])
  
              x_mean = self._get_x(t_mean)
              x_mean_p = self._get_x(t_mean + epsilon)
              x_mean_m = self._get_x(t_mean - epsilon)
  
              y = self._o_l(x_mean)
  
              m_x_1 = (
                  self._o_l(x_mean_p) - self._o_l(x_mean)
              ) / epsilon
              m_x_2 = (
                  self._o_l(x_mean) - self._o_l(x_mean_m)
              ) / epsilon
  
              if (
                  np.sum(np.abs((y_hat - y) / y) < rel_diff_value)
                  > fraction_true * self.num_classes
                  and t_1 < t_mean < t_2
                  and np.sum(
                      np.abs((m_x_1 - m_x_2) / m_x_1) > rel_diff_slope
                  )
                  > fraction_true * self.num_classes
              ):
                  found_critical_point = True
                  self.critical_points.append(x_mean)
                  t_current = t_2
              else:
                  delta = delta / 2
  
      if len(self.critical_points) != self.num_neurons:
          raise AssertionError(
              f"The number of critical points found ({len(self.critical_points)}) does not equal the number of "
              f"expected neurons in the first layer ({self.num_neurons})."
          )
  
file_path: adversarial-robustness-toolbox/art/attacks/extraction/functionally_equivalent_extraction.py
imports: import logging
  import os
  from typing import List, Optional, TYPE_CHECKING
  import numpy
  from scipy.optimize import least_squares
  from art.attacks.attack import ExtractionAttack
  from art.estimators.estimator import BaseEstimator, NeuralNetworkMixin
  from art.estimators.classification.classifier import ClassifierMixin
  from art.estimators.classification.keras import KerasClassifier
  from art.estimators.classification.blackbox import BlackBoxClassifier
label_desc: Use image node_id 5 for calling the FunctionallyEquivalentExtraction obj's underlying member method code with example usage: obj._critical_point_search(delta_0, fraction_true, rel_diff_slope, rel_diff_value) without return types