code: def test_shared_independent_mok() -> None:
      """
      In this test we use the same kernel and the same inducing inducing
      for each of the outputs. The outputs are considered to be uncorrelated.
      This is how GPflow handled multiple outputs before the multioutput framework was added.
      We compare three models here:
          1) an ineffient one, where we use a SharedIndepedentMok with InducingPoints.
             This combination will uses a Kff of size N x P x N x P, Kfu if size N x P x M x P
             which is extremely inefficient as most of the elements are zero.
          2) efficient: SharedIndependentMok and SharedIndependentMof
             This combinations uses the most efficient form of matrices
          3) the old way, efficient way: using Kernel and InducingPoints
          Model 2) and 3) follow more or less the same code path.
      """
      np.random.seed(0)
      # Model 1
      q_mu_1 = np.random.randn(Data.M * Data.P, 1)  # MP x 1
      q_sqrt_1 = np.tril(
          np.random.randn(Data.M * Data.P, Data.M * Data.P)
      )[
          None, ...
      ]  # 1 x MP x MP
      kernel_1 = mk.SharedIndependent(
          SquaredExponential(variance=0.5, lengthscales=1.2), Data.P
      )
      inducing_variable = InducingPoints(Data.X[: Data.M, ...])
      model_1 = SVGP(
          kernel_1,
          Gaussian(),
          inducing_variable,
          q_mu=q_mu_1,
          q_sqrt=q_sqrt_1,
          num_latent_gps=Data.Y.shape[-1],
      )
      set_trainable(model_1, False)
      set_trainable(model_1.q_sqrt, True)
  
      gpflow.optimizers.Scipy().minimize(
          model_1.training_loss_closure(Data.data),
          variables=model_1.trainable_variables,
          options=dict(maxiter=500),
          method="BFGS",
          compile=True,
      )
  
      # Model 2
      q_mu_2 = np.reshape(q_mu_1, [Data.M, Data.P])  # M x P
      q_sqrt_2: AnyNDArray = np.array(
          [
              np.tril(np.random.randn(Data.M, Data.M))
              for _ in range(Data.P)
          ]
      )  # P x M x M
      kernel_2 = SquaredExponential(variance=0.5, lengthscales=1.2)
      inducing_variable_2 = InducingPoints(Data.X[: Data.M, ...])
      model_2 = SVGP(
          kernel_2,
          Gaussian(),
          inducing_variable_2,
          num_latent_gps=Data.P,
          q_mu=q_mu_2,
          q_sqrt=q_sqrt_2,
      )
      set_trainable(model_2, False)
      set_trainable(model_2.q_sqrt, True)
  
      gpflow.optimizers.Scipy().minimize(
          model_2.training_loss_closure(Data.data),
          variables=model_2.trainable_variables,
          options=dict(maxiter=500),
          method="BFGS",
          compile=True,
      )
  
      # Model 3
      q_mu_3 = np.reshape(q_mu_1, [Data.M, Data.P])  # M x P
      q_sqrt_3: AnyNDArray = np.array(
          [
              np.tril(np.random.randn(Data.M, Data.M))
              for _ in range(Data.P)
          ]
      )  # P x M x M
      kernel_3 = mk.SharedIndependent(
          SquaredExponential(variance=0.5, lengthscales=1.2), Data.P
      )
      inducing_variable_3 = mf.SharedIndependentInducingVariables(
          InducingPoints(Data.X[: Data.M, ...])
      )
      model_3 = SVGP(
          kernel_3,
          Gaussian(),
          inducing_variable_3,
          num_latent_gps=Data.P,
          q_mu=q_mu_3,
          q_sqrt=q_sqrt_3,
      )
      set_trainable(model_3, False)
      set_trainable(model_3.q_sqrt, True)
  
      gpflow.optimizers.Scipy().minimize(
          model_3.training_loss_closure(Data.data),
          variables=model_3.trainable_variables,
          options=dict(maxiter=500),
          method="BFGS",
          compile=True,
      )
  
      check_equality_predictions(Data.data, [model_1, model_2, model_3])
  
imports: from typing import Callable, List, Sequence, Tuple, cast
  import numpy
  import pytest
  import scipy
  import tensorflow
  from _pytest.fixtures import SubRequest
  from check_shapes import ShapeChecker, check_shapes
  import gpflow
  import gpflow.inducing_variables.multioutput
  import gpflow.kernels.multioutput
  from gpflow import set_trainable
  from gpflow.base import AnyNDArray, RegressionData
  from gpflow.conditionals import sample_conditional
  from gpflow.conditionals.util import fully_correlated_conditional, fully_correlated_conditional_repeat, independent_interdomain_conditional, sample_mvn
  from gpflow.config import default_float, default_jitter
  from gpflow.inducing_variables import InducingPoints
  from gpflow.kernels import SquaredExponential
  from gpflow.likelihoods import Gaussian
  from gpflow.models import SVGP
label_desc: Use image node_id 15 for calling a global function with example usage: test_shared_independent_mok() without return types