class_docstr: The SchurComplement kernel.
  
  Given a block matrix `M = [[A, B], [C, D]]`, the Schur complement of D in M is
  written `M / D = A - B @ Inverse(D) @ C`.
  
  This class represents a PositiveSemidefiniteKernel whose behavior is as
  follows. We compute a matrix, analogous to `D` in the above definition, by
  calling `base_kernel.matrix(fixed_inputs, fixed_inputs)`. Then given new input
  locations `x` and `y`, we can construct the remaining pieces of `M` above, and
  compute the Schur complement of `D` in `M` (see Mathematical Details, below).
  
  Notably, this kernel uses a bijector (Invert(CholeskyOuterProduct)), as an
  intermediary for the requisite matrix solve, which means we get a caching
  benefit after the first use.
  
  ### Mathematical Details
  
  Suppose we have a kernel `k` and some fixed collection of inputs
  `Z = [z0, z1, ..., zN]`. Given new inputs `x` and `y`, we can form a block
  matrix
  
   ```none
     M = [
       [k(x, y), k(x, z0), ..., k(x, zN)],
       [k(z0, y), k(z0, z0), ..., k(z0, zN)],
       ...,
       [k(zN, y), k(z0, zN), ..., k(zN, zN)],
     ]
   ```
  
  We might write this, so as to emphasize the block structure,
  
   ```none
     M = [
       [xy, xZ],
       [yZ^T, ZZ],
     ],
  
     xy = [k(x, y)]
     xZ = [k(x, z0), ..., k(x, zN)]
     yZ = [k(y, z0), ..., k(y, zN)]
     ZZ = "the matrix of k(zi, zj)'s"
   ```
  
  Then we have the definition of this kernel's apply method:
  
  `schur_comp.apply(x, y) = xy - xZ @ ZZ^{-1} @ yZ^T`
  
  and similarly, if x and y are collections of inputs.
  
  As with other PSDKernels, the `apply` method acts as a (possibly
  vectorized) scalar function of 2 inputs. Given a single `x` and `y`,
  `apply` will yield a scalar output. Given two (equal size!) collections `X`
  and `Y`, it will yield another (equal size!) collection of scalar outputs.
  
  ### Examples
  
  Here's a simple example usage, with no particular motivation.
  
  ```python
  from tensorflow_probability.math import psd_kernels
  
  base_kernel = psd_kernels.ExponentiatedQuadratic(amplitude=np.float64(1.))
  # 3 points in 1-dimensional space (shape [3, 1]).
  z = [[0.], [3.], [4.]]
  
  schur_kernel = psd_kernels.SchurComplement(
      base_kernel=base_kernel,
      fixed_inputs=z)
  
  # Two individual 1-d points
  x = [1.]
  y = [2.]
  print(schur_kernel.apply(x, y))
  # ==> k(x, y) - k(x, z) @ Inverse(k(z, z)) @ k(z, y)
  ```
  
  A more motivating application of this kernel is in constructing a Gaussian
  process that is conditioned on some observed data.
  
  ```python
  from tensorflow_probability import distributions as tfd
  from tensorflow_probability.math import psd_kernels
  
  base_kernel = psd_kernels.ExponentiatedQuadratic(amplitude=np.float64(1.))
  observation_index_points = np.random.uniform(-1., 1., [50, 1])
  observations = np.sin(2 * np.pi * observation_index_points[..., 0])
  
  posterior_kernel = psd_kernels.SchurComplement(
      base_kernel=base_kernel,
      fixed_inputs=observation_index_points)
  
  # Assume we use a zero prior mean, and compute the posterior mean.
  def posterior_mean_fn(x):
    k_x_obs_linop = tf.linalg.LinearOperatorFullMatrix(
        base_kernel.matrix(x, observation_index_points))
    chol_linop = tf.linalg.LinearOperatorLowerTriangular(
        posterior_kernel.divisor_matrix_cholesky())
  
    return k_x_obs_linop.matvec(
        chol_linop.solvevec(
            chol_linop.solvevec(observations),
            adjoint=True))
  
  # Construct the GP posterior distribution at some new points.
  gp_posterior = tfp.distributions.GaussianProcess(
      index_points=np.linspace(-1., 1., 100)[..., np.newaxis],
      kernel=posterior_kernel,
      mean_fn=posterior_mean_fn)
  
  # Draw 5 samples on the above 100-point grid
  samples = gp_posterior.sample(5)
  ```
code: def _parameter_properties(cls, dtype):
      from tensorflow_probability.python.bijectors import (
          softplus,
      )  # pylint:disable=g-import-not-at-top
  
      return dict(
          base_kernel=parameter_properties.BatchedComponentProperties(),
          fixed_inputs=parameter_properties.ParameterProperties(
              event_ndims=lambda self: tf.nest.map_structure(  # pylint: disable=g-long-lambda
                  lambda nd: nd + 1, self.base_kernel.feature_ndims
              )
          ),
          fixed_inputs_is_missing=parameter_properties.ParameterProperties(
              event_ndims=1
          ),
          diag_shift=parameter_properties.ParameterProperties(
              default_constraining_bijector_fn=(
                  lambda: softplus.Softplus(low=dtype_util.eps(dtype))
              )
          ),
          _precomputed_divisor_matrix_cholesky=(
              parameter_properties.ParameterProperties(event_ndims=2)
          ),
      )
  
imports: import tensorflow.compat.v2
  from tensorflow_probability.python.internal import distribution_util
  from tensorflow_probability.python.internal import dtype_util
  from tensorflow_probability.python.internal import nest_util
  from tensorflow_probability.python.internal import parameter_properties
  from tensorflow_probability.python.internal import tensor_util
  from tensorflow_probability.python.math.psd_kernels import positive_semidefinite_kernel
  from tensorflow_probability.python.math.psd_kernels.internal import util
label_desc: Use image node_id 12 for calling the SchurComplement obj's underlying member method code with example usage: obj._parameter_properties(cls, dtype) and returns: dict