def _weight_recovery(
        self,
        delta_init_value: float,
        delta_value_max: float,
        d2_min: float,
        d_step: float,
        delta_sign: float,
    ) -> None:
        """
        Recover the weights and biases of the first layer.

        :param delta_init_value: Initial delta of weight value search.
        :param delta_value_max: Maximum delta  of weight value search.
        :param d2_min: Minimum acceptable value of sum of absolute second derivatives.
        :param d_step:  Step size of delta increase.
        :param delta_sign: Delta of weight sign search.
        """
        logger.info("Recovering weights of first layer.")

        if self.num_neurons is None:
            raise ValueError("The value of `num_neurons` is required for critical point search.")

        # Absolute Value Recovery
        d2_ol_d2ej_xi = np.zeros((self.num_features, self.num_neurons), dtype=NUMPY_DTYPE)

        for i in range(self.num_neurons):
            for j in range(self.num_features):

                delta = delta_init_value
                e_j = np.zeros((1, self.num_features))
                d2_ol_d2ej_xi_ok = False

                while not d2_ol_d2ej_xi_ok:

                    e_j[0, j] = delta

                    d_ol_dej_xi_p_cej = (
                        self._o_l(self.critical_points[i], e_j=e_j) - self._o_l(self.critical_points[i])
                    ) / delta
                    d_ol_dej_xi_m_cej = (
                        self._o_l(self.critical_points[i]) - self._o_l(self.critical_points[i], e_j=-e_j)
                    ) / delta

                    d2_ol_d2ej_xi[j, i] = np.sum(np.abs(d_ol_dej_xi_p_cej - d_ol_dej_xi_m_cej)) / delta

                    if d2_ol_d2ej_xi[j, i] < d2_min and delta < delta_value_max:
                        delta = delta + d_step
                    else:
                        d2_ol_d2ej_xi_ok = True

        self.a0_pairwise_ratios = np.zeros((self.num_features, self.num_neurons), dtype=NUMPY_DTYPE)

        for i in range(self.num_neurons):
            for k in range(self.num_features):
                self.a0_pairwise_ratios[k, i] = d2_ol_d2ej_xi[0, i] / d2_ol_d2ej_xi[k, i]

        # Weight Sign Recovery

        for i in range(self.num_neurons):
            d2_ol_dejek_xi_0 = None
            for j in range(self.num_features):

                e_j = np.zeros((1, self.num_features), dtype=NUMPY_DTYPE)

                e_j[0, 0] += delta_sign
                e_j[0, j] += delta_sign

                d_ol_dejek_xi_p_cejek = (
                    self._o_l(self.critical_points[i], e_j=e_j) - self._o_l(self.critical_points[i])
                ) / delta_sign
                d_ol_dejek_xi_m_cejek = (
                    self._o_l(self.critical_points[i]) - self._o_l(self.critical_points[i], e_j=-e_j)
                ) / delta_sign

                d2_ol_dejek_xi = d_ol_dejek_xi_p_cejek - d_ol_dejek_xi_m_cejek

                if j == 0:
                    d2_ol_dejek_xi_0 = d2_ol_dejek_xi / 2.0

                co_p = np.sum(np.abs(d2_ol_dejek_xi_0 * (1 + 1 / self.a0_pairwise_ratios[j, i]) - d2_ol_dejek_xi))
                co_m = np.sum(np.abs(d2_ol_dejek_xi_0 * (1 - 1 / self.a0_pairwise_ratios[j, i]) - d2_ol_dejek_xi))

                if co_m < co_p * np.max(1 / self.a0_pairwise_ratios[:, i]):
                    self.a0_pairwise_ratios[j, i] *= -1

        Recover the weights and biases of the first layer.

        :param delta_init_value: Initial delta of weight value search.
        :param delta_value_max: Maximum delta  of weight value search.
        :param d2_min: Minimum acceptable value of sum of absolute second derivatives.
        :param d_step:  Step size of delta increase.
        :param delta_sign: Delta of weight sign search.
        
art/attacks/extraction/functionally_equivalent_extraction.py