code: def _calculate_spline_coeffs(
      x_data,
      y_data,
      boundary_condition_type=BoundaryConditionType.NATURAL,
      left_boundary_value=None,
      right_boundary_value=None,
  ):
      """Calculates the coefficients for the spline interpolation.
  
      These are the values of the second derivative of the spline at `x_data`.
      See p.548 of [1].
  
      #### Below formula is for natural condition type.
      It is an outline of the function when number of observations if equal to 7.
      The coefficients are obtained by building and solving a tridiagonal linear
      system of equations with symmetric matrix
       1,  0,  0,    0,    0,   0,  0
      dx0  w0, dx1,  0,    0,   0,  0
       0, dx1,  w1,  dx2,  0,   0,  0
       0,  0,  dx2,  w2,  dx3,  0,  0
       0,  0,   0,   dx3,  w3, dx4, 0
       0,  0,   0,   0,   dx4,  w4, dx5
       0,  0,   0,   0,    0,   0,  1
       where:
       dxn = x_data[n+1] - x_data[n]
       wn = 2 * (dx[n] + dx[n+1])
  
       and the right hand side of the equation is:
       [[0],
        [3*( (y2-y1)/dx1 - (y1-y0)/dx0],
        [3*( (y3-y2)/dx2 - (y2-y1)/dx1],
        [3*( (y4-y3)/dx3 - (y3-y2)/dx2],
        [3*( (y5-y4)/dx4 - (y4-y3)/dx3],
        [3*( (y6-y5)/dx5 - (y5-y4)/dx4],
        [0]
       ]
  
       with yi = y_data[..., i]
  
       Solve for `spline_coeffs`, so that  matrix * spline_coeffs = rhs
  
       the solution is the `spline_coeffs` parameter of the spline equation:
  
       y_pred = a(spline_coeffs) * t^3 + b(spline_coeffs) * t^2
                + c(spline_coeffs) * t + d(spline_coeffs)
       with t being the proportion of the difference between the x value of
       the spline used and the nx_value of the next spline:
  
       t = (x - x_data[:,n]) / (x_data[:,n+1]-x_data[:,n])
  
       and `a`, `b`, `c`, and `d` are functions of `spline_coeffs` and `x_data` and
       are provided in the `interpolate` function.
  
      #### Below formula is for clamped/first_derivative condition type.
      Similar to natural condition type, let us assume the number of observations
      is equal to 7. The underlying mathematics can be found in [2].
      left hand side matrix:
      2*dx0, dx0,  0,   0,    0,   0,  0
       dx0    w0, dx1,  0,    0,   0,  0
        0,   dx1,  w1,  dx2,  0,   0,  0
        0,    0,  dx2,  w2,  dx3,  0,  0
        0,    0,   0,   dx3,  w3, dx4, 0
        0,    0,   0,   0,   dx4,  w4, dx5
        0,    0,   0,   0,    0,  dx5, 2*dx5
       where:
       dxn and wn is same as natural contition case.
  
       and the right hand side of the equation is:
       [[3* ((y1-y0)/dx0 - lb)],
        [3*( (y2-y1)/dx1 - (y1-y0)/dx0],
        [3*( (y3-y2)/dx2 - (y2-y1)/dx1],
        [3*( (y4-y3)/dx3 - (y3-y2)/dx2],
        [3*( (y5-y4)/dx4 - (y4-y3)/dx3],
        [3*( (y6-y5)/dx5 - (y5-y4)/dx4],
        [-3*((y6-y5)/dx5 - rb)]
       ]
       where dxn, yi is same as natural case.
       lb is specified first derivative at left boundary.
       rb is specified first derivative at right boundary.
  
      #### Special handling for right padding, imagine the number of observations
      is equal to 7. While there are 2 repeated points as right padding.
      The left hand matrix needs to be:
      2*dx0, dx0,  0,   0,    0,   0,  0     0,  0
       dx0    w0, dx1,  0,    0,   0,  0     0,  0
        0,   dx1,  w1,  dx2,  0,   0,  0     0,  0
        0,    0,  dx2,  w2,  dx3,  0,  0     0,  0
        0,    0,   0,   dx3,  w3, dx4, 0     0,  0
        0,    0,   0,   0,   dx4,  w4, dx5   0,  0
        0,    0,   0,   0,    0,  dx5, 2*dx5 0,  0
        0,    0,   0,   0,    0,  0,   0,    1,  0
        0,    0,   0,   0,    0,  0,   0,    0,  1
  
       The right hand matrix needs to be:
        [[3* ((y1-y0)/dx0 - lb)],
        [3*( (y2-y1)/dx1 - (y1-y0)/dx0],
        [3*( (y3-y2)/dx2 - (y2-y1)/dx1],
        [3*( (y4-y3)/dx3 - (y3-y2)/dx2],
        [3*( (y5-y4)/dx4 - (y4-y3)/dx3],
        [3*( (y6-y5)/dx5 - (y5-y4)/dx4],
        [-3*((y6-y5)/dx5 - rb)],
        [0],
        [0]
       ]
  
      #### References:
      [1]: R. Sedgewick, Algorithms in C, 1990, p. 545-550.
        Link: http://index-of.co.uk/Algorithms/Algorithms%20in%20C.pdf
  
      Args:
        x_data: A real `Tensor` of shape `[..., num_points]` containing
          X-coordinates of points to fit the splines to. The values have to be
          monotonically non-decreasing along the last dimension.
        y_data: A `Tensor` of the same shape and `dtype` as `x_data` containing
          Y-coordinates of points to fit the splines to.
        boundary_condition_type: Boundary condition type for current cubic
          interpolation.
        left_boundary_value: Set to non-empty value IFF boundary_condition_type is
          FIXED_FIRST_DERIVATIVE, in which case set to cubic spline's
          first derivative at x_data[: 0].
        right_boundary_value: Set to non-empty value IFF boundary_condition_type is
          FIXED_FIRST_DERIVATIVE, in which case set to cubic spline's
          first derivative at x_data[: num_points - 1]
  
      Returns:
         A `Tensor` of the same shape and `dtype` as `x_data`. Represents the
         spline coefficients for the cubic spline interpolation.
      [2]: http://macs.citadel.edu/chenm/343.dir/09.dir/lect3_4.pdf
      """
  
      # `dx` is the distances between the x points. It is 1 element shorter than
      # `x_data`
      dx = x_data[..., 1:] - x_data[..., :-1]
  
      # determine the rhs of the equation
      dd = (y_data[..., 1:] - y_data[..., :-1]) / dx
      dd = tf.where(tf.equal(dx, 0), tf.zeros_like(dd), dd)
      # rhs is a column vector:
      # [[-3((y1-y0)/dx0 - (y2-y1)/dx0], ...]
      # Its length is 2 shorter compared to number of points. Since this is the
      # "inner" part before we have finalized rhs.
      # The finalized rhs's first and last element will be
      # decided based on the boundary conditions.
      rhs = -3 * (dd[..., :-1] - dd[..., 1:])
  
      # `diag_values` are the diagonal values 2 * (x_data[i+1] - x_data[i-1]).
      # Its length is 2 shorter compared to number of points. Since this is the
      # "inner" part before we have finalized diagonals.
      # The finalized diagonals's first and last element will be
      # decided based on the boundary conditions.
  
      diag_values = 2.0 * (x_data[..., 2:] - x_data[..., :-2])
      superdiag = dx[..., 1:]
      subdiag = dx[..., :-1]
      # Compute first and last element for diagonal and rhs,
      # the computation varies per boundary condition.
      if boundary_condition_type == BoundaryConditionType.NATURAL:
          return _calculate_spline_coeffs_natural(
              dx, superdiag, subdiag, diag_values, rhs, x_data.dtype
          )
      elif boundary_condition_type in [
          BoundaryConditionType.FIXED_FIRST_DERIVATIVE,
          BoundaryConditionType.CLAMPED,
      ]:
          return _calculate_spline_coeffs_clamped_or_first_derivative(
              dx,
              dd,
              superdiag,
              subdiag,
              diag_values,
              rhs,
              x_data.dtype,
              boundary_condition_type,
              left_boundary_value,
              right_boundary_value,
          )
  
file_path: tf-quant-finance/tf_quant_finance/math/interpolation/cubic/cubic_interpolation.py
imports: import enum
  import tensorflow.compat.v2
  from tf_quant_finance import types
  from tf_quant_finance import utils
label_desc: Use image node_id 5 for calling a global function with example usage: _calculate_spline_coeffs(x_data, y_data, boundary_condition_type, left_boundary_value, right_boundary_value) and returns: _calculate_spline_coeffs_natural, _calculate_spline_coeffs_clamped_or_first_derivative