code: def _build_swap_curve(
      float_leg_start_times,
      float_leg_end_times,
      float_leg_daycount_fractions,
      fixed_leg_start_times,
      fixed_leg_end_times,
      fixed_leg_cashflows,
      fixed_leg_daycount_fractions,
      float_leg_discount_rates,
      float_leg_discount_times,
      fixed_leg_discount_rates,
      fixed_leg_discount_times,
      self_discounting_float_leg,
      self_discounting_fixed_leg,
      present_values,
      pv_settlement_times,
      curve_interpolator,
      initial_rates,
      curve_tolerance,
      maximum_iterations,
      dtype,
  ):
      """Build the zero swap curve using the bootstrap method."""
  
      # The procedure is recursive and as follows:
      # 1. Start with an initial state of the swap curve. Set this as the current
      #   swap curve.
      # 2. From the current swap curve, compute the relevant forward rates and
      #   discount factors (if cashflows are discounted using the swap curve). Use
      #   the specified interpolation method to compute rates at intermediate times
      #   as needed to calculate either the forward rate or the discount factors.
      # 3. Using the above and the input present values of bootstarpping
      #   instruments,compute the zero swap rate at expiry by inverting the swap
      #   pricing formula. The following illustrates the procedure:
  
      #   Assuming that a swap pays fixed payments c_i at times t_i (i=[1,...,n])
      #   and receives floating payments at times t_j (j=[1,...,m]), then the
      #   present value of the swap is given by
  
      #   ```None
      #   PV = sum_{j=1}^m (P_{j-1}/P_j - 1.) * P_j - sum_{i=1}^n a_i * c_i * P_i
      #                                                                        (A)
      #
      #   ```
      #   where P_i = exp(-r(t_i) * t_i) and a_i are the daycount fractions. We
      #   update the current estimate of the rate at curve node t_k = t_n = t_m
      #   by inverting the above equation:
  
      #   ```None
      #   P_k * (1 + a_i * c_i) = sum_{j=1}^{m - 1} (P_{j-1}/P_j - 1.) * P_j -
      #                           sum_{i=1}^{n - 1} a_i * c_i * P_i - PV       (B)
  
      #   ```
      #   From Eq. (B), we get r(t_k) = -log(P_k) / t_k.
      #   Using this as the next guess for the discount rates and we repeat the
      #   procedure from Step (2) until convergence.
  
      del fixed_leg_start_times, pv_settlement_times
      curve_tensors = _create_curve_building_tensors(
          float_leg_start_times,
          float_leg_end_times,
          float_leg_daycount_fractions,
          fixed_leg_end_times,
          fixed_leg_cashflows,
          fixed_leg_daycount_fractions,
      )
  
      float_leg_calc_times_start = curve_tensors.float_leg_times_start
      float_leg_calc_times_end = curve_tensors.float_leg_times_end
      calc_fixed_leg_cashflows = curve_tensors.fixed_leg_cashflows
      calc_fixed_leg_daycount = curve_tensors.fixed_leg_daycount
      fixed_leg_calc_times = curve_tensors.fixed_leg_calc_times
      calc_groups_float = curve_tensors.calc_groups_float
      calc_groups_fixed = curve_tensors.calc_groups_fixed
      last_float_leg_start_time = (
          curve_tensors.last_float_leg_start_time
      )
      last_float_leg_end_time = curve_tensors.last_float_leg_end_time
      last_fixed_leg_end_time = curve_tensors.last_fixed_leg_calc_time
      last_fixed_leg_daycount = curve_tensors.last_fixed_leg_daycount
      last_fixed_leg_cashflows = curve_tensors.last_fixed_leg_cashflows
      expiry_times = curve_tensors.expiry_times
  
      def _one_step(converged, failed, iteration, discount_factor):
          """One step of the bootstrap iteration."""
  
          x = -tf.math.log(discount_factor) / expiry_times
          rates_start = curve_interpolator(
              float_leg_calc_times_start, expiry_times, x
          )
          rates_end = curve_interpolator(
              float_leg_calc_times_end, expiry_times, x
          )
          rates_start_last = curve_interpolator(
              last_float_leg_start_time, expiry_times, x
          )
  
          float_cashflows = (
              tf.math.exp(float_leg_calc_times_end * rates_end)
              / tf.math.exp(float_leg_calc_times_start * rates_start)
              - 1.0
          )
  
          if self_discounting_float_leg:
              float_discount_rates = rates_end
          else:
              float_discount_rates = curve_interpolator(
                  float_leg_calc_times_end,
                  float_leg_discount_times,
                  float_leg_discount_rates,
              )
              last_float_discount_rate = curve_interpolator(
                  last_float_leg_end_time,
                  float_leg_discount_times,
                  float_leg_discount_rates,
              )
              last_float_discount_factor = tf.math.exp(
                  -last_float_discount_rate * last_float_leg_end_time
              )
          if self_discounting_fixed_leg:
              fixed_discount_rates = curve_interpolator(
                  fixed_leg_calc_times, expiry_times, x
              )
          else:
              fixed_discount_rates = curve_interpolator(
                  fixed_leg_calc_times,
                  fixed_leg_discount_times,
                  fixed_leg_discount_rates,
              )
              last_fixed_discount_rate = curve_interpolator(
                  last_fixed_leg_end_time,
                  fixed_leg_discount_times,
                  fixed_leg_discount_rates,
              )
              last_fixed_discount_factor = tf.math.exp(
                  -last_fixed_leg_end_time * last_fixed_discount_rate
              )
              last_fixed_leg_cashflow_pv = (
                  last_fixed_leg_daycount
                  * last_fixed_leg_cashflows
                  * last_fixed_discount_factor
              )
  
          calc_discounts_float_leg = tf.math.exp(
              -float_discount_rates * float_leg_calc_times_end
          )
          calc_discounts_fixed_leg = tf.math.exp(
              -fixed_discount_rates * fixed_leg_calc_times
          )
  
          float_pv = tf.math.segment_sum(
              float_cashflows * calc_discounts_float_leg,
              calc_groups_float,
          )
          fixed_pv = tf.math.segment_sum(
              calc_fixed_leg_daycount
              * calc_fixed_leg_cashflows
              * calc_discounts_fixed_leg,
              calc_groups_fixed,
          )
  
          if self_discounting_float_leg and self_discounting_fixed_leg:
              p_n_minus_1 = tf.math.exp(
                  -rates_start_last * last_float_leg_start_time
              )
              scale = (
                  last_fixed_leg_cashflows * last_fixed_leg_daycount
                  - 1.0
              )
              next_discount = (
                  present_values - float_pv - fixed_pv - p_n_minus_1
              ) / scale
          elif self_discounting_float_leg:
              p_n_minus_1 = tf.math.exp(
                  -rates_start_last * last_float_leg_start_time
              )
              next_discount = (
                  float_pv
                  + (fixed_pv + last_fixed_leg_cashflow_pv)
                  - present_values
                  + p_n_minus_1
              )
          else:
              p_n_minus_1 = tf.math.exp(
                  -rates_start_last * last_float_leg_start_time
              )
              scale = (
                  present_values
                  - float_pv
                  - (fixed_pv + last_fixed_leg_cashflow_pv)
                  + last_float_discount_factor
              )
              next_discount = (
                  p_n_minus_1 * last_float_discount_factor / scale
              )
  
          discount_diff = tf.math.abs(next_discount - discount_factor)
          converged = ~tf.math.reduce_any(
              tf.math.is_nan(discount_diff)
          ) & (tf.math.reduce_max(discount_diff) < curve_tolerance)
  
          return (converged, failed, iteration + 1, next_discount)
  
      def cond(converged, failed, iteration, x):
          # Note we do not need to check iteration count here because that
          # termination mode is imposed by the maximum_iterations parameter in the
          # while loop.
          del iteration, x
          return ~tf.math.logical_or(converged, failed)
  
      initial_vals = (
          False,
          False,
          0,
          tf.math.exp(-initial_rates * expiry_times),
      )
      bootstrap_result = tf.compat.v2.while_loop(
          cond,
          _one_step,
          initial_vals,
          maximum_iterations=maximum_iterations,
      )
  
      discount_factors = bootstrap_result[-1]
      discount_rates = -tf.math.log(discount_factors) / expiry_times
      results = scc.SwapCurveBuilderResult(
          times=expiry_times,
          rates=discount_rates,
          discount_factors=discount_factors,
          initial_rates=initial_rates,
          converged=bootstrap_result[0],
          failed=bootstrap_result[1],
          iterations=bootstrap_result[2],
          objective_value=tf.constant(0, dtype=dtype),
      )
      return results
  
file_path: tf-quant-finance/tf_quant_finance/rates/swap_curve_bootstrap.py
imports: from typing import List, Callable
  import tensorflow.compat.v2
  from tf_quant_finance import types
  from tf_quant_finance import utils
  from tf_quant_finance.math.interpolation import linear
  from tf_quant_finance.rates import swap_curve_common
label_desc: Use image node_id 2 for calling a global function with example usage: _build_swap_curve(float_leg_start_times, float_leg_end_times, float_leg_daycount_fractions, fixed_leg_start_times, fixed_leg_end_times, fixed_leg_cashflows, fixed_leg_daycount_fractions, float_leg_discount_rates, float_leg_discount_times, fixed_leg_discount_rates, fixed_leg_discount_times, _discounting_float_leg, _discounting_fixed_leg, present_values, pv_settlement_times, curve_interpolator, initial_rates, curve_tolerance, maximum_iterations, dtype) and returns: results, converged,  failed,  unknown,  next_discount, unknown