code: def _build_discount_curve(
      bond_cashflows,
      bond_cashflow_times,
      present_values,
      pv_settle_times,
      initial_discount_rates,
      discount_tolerance,
      maximum_iterations,
  ):
      """Estimates the discount curve.
  
      The procedure is recursive and as follows:
      1. Assume some initial set of discount rates/discount factors.
        Set this as the current yield curve.
      2. From the current yield curve, interpolate to get the discount rates
        for each time at which bond_cashflows occur.
      3. Using these discounts and the known bond prices, compute the discount
        rate to expiry of each bond by inverting the bond pricing formula as
        follows. We know that the bond price satisfies (`P` is the present value,
        `r_i` is the discount rate to time `t_i`, `c_i` is the cashflow occurring at
        time `t_i`.):
  
        ```None
          P e^{-r_0 t_0} = c_1 e^{-r_1 t_1} + ... + c_n e^{-r_n t_n}        (A)
  
        ```
        Assuming we have estimated r_0, r_1, r_2, ..., r_{n-1}, we can invert the
        above equation to calculate r_n. We write this in a suggestive form
        suitable for the implementation below.
  
        ```None
          -c_n z_n = -P z_0 + c_1 z_1 + c_2 z_2 + ... + c_{n-1} z_{n-1}     (B)
  
        ```
        where
  
        ```None
          z_i = e^{-r_i t_i}      (C)
  
        ```
        The RHS of Eq. (B) looks like the PV of cashflows
        `[-P, c_1, c_2, ... c_{n-1}]` paid out at times `[t_0, t_1, ..., t_{n-1}]`.
  
        Concatenate these "synthetic" cashflow times for each bond:
  
        `Ts = [t1_0, t1_1, ... t1_{n1-1}] + [t2_0, t2_1, ... t2_{n2-1}] ...`
  
        Also concatenate the synthetic bond cashflows as:
  
        `Cs = [-P1, c1_1, ..., c1_{n1-1}] + [-P2, c2_1, ..., c2_{n2-1}] ...`
  
        Then compute `Rs = InterpolateRates[Ts], Zs = exp(-Rs * Ts)`
  
        Let `Zns = [z_n1, z_n2, ... ], Cns = [c1_n, c2_n, ...]` be the discount
        factors to expiry and the final cashflow of each bond.
        We can derive `Zns = - SegmentSum(Cs * Zs) / Cns`.
  
        From that, we get Rns = -log(Zns) / Tns.
        Using this as the next guess for the discount rates and we repeat the
        procedure from Step (1) until convergence.
  
      Args:
        bond_cashflows: List of `Tensor`s. Each `Tensor` must be of rank 1 and of
          the same real dtype. They may be of different sizes. Each `Tensor`
          represents the bond cashflows defining a particular bond. The elements of
          the list are the bonds to be used to build the curve.
        bond_cashflow_times: List of `Tensor`s. The list must be of the same length
          as the `bond_cashflows` and each `Tensor` in the list must be of the same
          length as the `Tensor` at the same index in the `bond_cashflows` list.
          Each `Tensor` must be of rank 1 and of the same dtype as the `Tensor`s in
          `bond_cashflows` and contain strictly positive and increasing values. The
          times of the bond cashflows for the bonds must in an ascending order.
        present_values: List containing scalar `Tensor`s of the same dtype as
          elements of `bond_cashflows`. The length of the list must be the same as
          the length of `bond_cashflows`. The market price (i.e the all-in or dirty
          price) of the bond cashflows supplied in the `bond_cashflows`.
        pv_settle_times:   List containing scalar `Tensor`s of the same dtype as
          elements of `bond_cashflows`. The length of the list must be the same as
          the length of `bond_cashflows`. The settlement times for the present
          values is the time from now when the bond is traded to the time that the
          purchase price is actually delivered.
        initial_discount_rates: Rank 1 `Tensor` of same shape and dtype as
          `pv_settle_times`. The initial guess for the discount rates to bond expiry
          times.
        discount_tolerance: Positive scalar `Tensor` of same dtype as
          `initial_discount_factors`. The absolute tolerance for terminating the
          iterations used to fit the rate curve. The iterations are stopped when the
          estimated discounts at the expiry times of the bond cashflows change by a
          amount smaller than `discount_tolerance` in an iteration.
        maximum_iterations: Positive scalar `tf.int32` `Tensor`. The maximum number
          of iterations permitted.
  
      Returns:
        curve_builder_result: An instance of `CurveBuilderResult` containing the
          following attributes.
          times: Rank 1 real `Tensor`. Times for the computed discount rates.
          discount_rates: Rank 1 `Tensor` of the same dtype as `times`.
            The inferred discount rates.
          discount_factor: Rank 1 `Tensor` of the same dtype as `times`.
            The inferred discount factors.
          initial_discount_rates: Rank 1 `Tensor` of the same dtype as `times`. The
            initial guess for the discount rates.
          converged: Scalar boolean `Tensor`. Whether the procedure converged.
            The procedure is said to have converged when the maximum absolute
            difference in the discount factors from one iteration to the next falls
            below the `discount_tolerance`.
          failed: Scalar boolean `Tensor`. Whether the procedure failed. Procedure
            may fail either because a NaN value was encountered for the discount
            rates or the discount factors.
          iterations: Scalar `tf.int32` `Tensor`. Number of iterations performed.
      """
      calc_bond_cashflows = []  # Cs
      calc_times = []  # Ts
      expiry_times = []  # Tns
      expiry_bond_cashflows = []  # Cns
      calc_groups = []
      num_bonds = len(bond_cashflows)
      for i in range(num_bonds):
          calc_bond_cashflows.extend(
              [[-present_values[i]], bond_cashflows[i][:-1]]
          )
          calc_times.extend(
              [[pv_settle_times[i]], bond_cashflow_times[i][:-1]]
          )
          expiry_times.append(bond_cashflow_times[i][-1])
          expiry_bond_cashflows.append(bond_cashflows[i][-1])
          calc_groups.append(tf.fill(tf.shape(bond_cashflows[i]), i))
  
      calc_bond_cashflows = tf.concat(calc_bond_cashflows, axis=0)
      calc_times = tf.concat(calc_times, axis=0)
      expiry_times = tf.stack(expiry_times, axis=0)
      expiry_bond_cashflows = tf.stack(expiry_bond_cashflows, axis=0)
      calc_groups = tf.concat(calc_groups, axis=0)
  
      def one_step(converged, failed, iteration, expiry_discounts):
          """One step of the iteration."""
          expiry_rates = -tf.math.log(expiry_discounts) / expiry_times
          failed = tf.math.reduce_any(
              tf.math.is_nan(expiry_rates)
              | tf.math.is_nan(expiry_discounts)
          )
          calc_rates = monotone_convex.interpolate_yields(
              calc_times, expiry_times, yields=expiry_rates
          )
          calc_discounts = tf.math.exp(-calc_rates * calc_times)
          next_expiry_discounts = (
              -tf.math.segment_sum(
                  calc_bond_cashflows * calc_discounts, calc_groups
              )
              / expiry_bond_cashflows
          )
          discount_diff = tf.math.abs(
              next_expiry_discounts - expiry_discounts
          )
          converged = ~tf.math.reduce_any(
              tf.math.is_nan(discount_diff)
          ) & (tf.math.reduce_max(discount_diff) < discount_tolerance)
          return converged, failed, iteration + 1, next_expiry_discounts
  
      def cond(converged, failed, iteration, expiry_discounts):
          del expiry_discounts, iteration
          # 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.
          return ~tf.math.logical_or(converged, failed)
  
      initial_discount_factors = tf.math.exp(
          -initial_discount_rates * expiry_times
      )
      initial_vals = (False, False, 0, initial_discount_factors)
      loop_result = tf.while_loop(
          cond,
          one_step,
          initial_vals,
          maximum_iterations=maximum_iterations,
      )
      discount_factors = loop_result[-1]
      discount_rates = -tf.math.log(discount_factors) / expiry_times
      results = CurveBuilderResult(
          times=expiry_times,
          discount_rates=discount_rates,
          discount_factors=discount_factors,
          initial_discount_rates=initial_discount_rates,
          converged=loop_result[0],
          failed=loop_result[1],
          iterations=loop_result[2],
      )
      return results
  
file_path: tf-quant-finance/tf_quant_finance/rates/hagan_west/bond_curve.py
imports: import tensorflow.compat.v2
  from tf_quant_finance import types
  from tf_quant_finance import utils
  from tf_quant_finance.rates.analytics import cashflows
  from tf_quant_finance.rates.hagan_west import monotone_convex
label_desc: Use image node_id 2 for calling a global function with example usage: _build_discount_curve(bond_cashflows, bond_cashflow_times, present_values, pv_settle_times, initial_discount_rates, discount_tolerance, maximum_iterations) and returns: results, converged,  failed,  unknown,  next_expiry_discounts, unknown