docs: def reduce(self, values, inplace=True):
          """
          Reduces the distribution to the context of the given variable values.
  
          Let C(X,Y ; K, h, g) be some canonical form over X,Y where,
  
          k = [[K_XX, K_XY],        ;       h = [[h_X],
               [K_YX, K_YY]]                     [h_Y]]
  
          The formula for the obtained conditional distribution for setting
          Y = y is given by,
  
          .. math:: K' = K_{XX}
          .. math:: h' = h_X - K_{XY} * y
          .. math:: g' = g + {h^T}_Y * y - 0.5 * y^T * K_{YY} * y
  
  
          Parameters
          ----------
          values: list, array-like
              A list of tuples of the form (variable name, variable value).
  
          inplace: boolean
              If inplace=True it will modify the factor itself, else would return
              a new CaninicalFactor object.
  
          Returns
          -------
          CanonicalDistribution or None:
                  if inplace=True (default) returns None
                  if inplace=False returns a new CanonicalDistribution instance.
  
          Examples
          --------
          >>> import numpy as np
          >>> from pgmpy.factors.continuous import CanonicalDistribution
          >>> phi = CanonicalDistribution(['X1', 'X2', 'X3'],
          ...                       np.array([[1, -1, 0], [-1, 4, -2], [0, -2, 4]]),
          ...                       np.array([[1], [4], [-1]]), -2)
          >>> phi.variables
          ['X1', 'X2', 'X3']
  
          >>> phi.K
          array([[ 1., -1.],
                 [-1.,  3.]])
  
          >>> phi.h
          array([[ 1. ],
                 [ 3.5]])
  
          >>> phi.g
          -2
  
          >>> phi.reduce([('X3', 0.25)])
  
          >>> phi.variables
          ['X1', 'X2']
  
          >>> phi.K
          array([[ 1, -1],
                 [-1,  4]])
  
          >>> phi.h
          array([[ 1. ],
                 [ 4.5]])
  
          >>> phi.g
          -2.375
          """
          if not isinstance(values, (list, tuple, np.ndarray)):
              raise TypeError("variables: Expected type list or array-like, "
                              "got type {var_type}".format(var_type=type(values)))
  
          if not all([var in self.variables for var, value in values]):
              raise ValueError("Variable not in scope.")
  
          phi = self if inplace else self.copy()
  
          var_to_reduce = [var for var, value in values]
  
          # index_to_keep -> j vector
          index_to_keep = [self.variables.index(var) for var in self.variables
                           if var not in var_to_reduce]
          # index_to_reduce -> i vector
          index_to_reduce = [self.variables.index(var) for var in var_to_reduce]
  
          K_i_i = self.K[np.ix_(index_to_keep, index_to_keep)]
          K_i_j = self.K[np.ix_(index_to_keep, index_to_reduce)]
          K_j_j = self.K[np.ix_(index_to_reduce, index_to_reduce)]
          h_i = self.h[index_to_keep]
          h_j = self.h[index_to_reduce]
  
          # The values for the reduced variables.
          y = np.array([value for var, value in values]).reshape(len(index_to_reduce), 1)
  
          phi.variables = [self.variables[index] for index in index_to_keep]
          phi.K = K_i_i
          phi.h = h_i - np.dot(K_i_j, y)
          phi.g = self.g + (np.dot(h_j.T, y) - (0.5 * np.dot(np.dot(y.T, K_j_j), y)))[0][0]
  
          if not inplace:
              return phi