class_docstr: Hamming kernel implementation.
code: def _call(
      self,
      X: np.ndarray,
      Y: np.ndarray | None = None,
      eval_gradient: bool = False,
      active: np.ndarray | None = None,
  ) -> np.ndarray | tuple[np.ndarray, np.ndarray]:
      X = np.atleast_2d(X)
      length_scale = kernels._check_length_scale(X, self.length_scale)
  
      if Y is None:
          Y = X
      elif eval_gradient:
          raise ValueError("gradient can be evaluated only when Y != X")
      else:
          Y = np.atleast_2d(Y)
  
      indicator = np.expand_dims(X, axis=1) != Y
      K = (-1 / (2 * length_scale**2) * indicator).sum(axis=2)
      K = np.exp(K)
  
      if active is not None:
          K = K * active
  
      if eval_gradient:
          # dK / d theta = (dK / dl) * (dl / d theta)
          # theta = log(l) => dl / d (theta) = e^theta = l
          # dK / d theta = l * dK / dl
  
          # dK / dL computation
          if np.iterable(length_scale) and length_scale.shape[0] > 1:  # type: ignore
              grad = np.expand_dims(K, axis=-1) * np.array(
                  indicator, dtype=np.float32
              )
          else:
              grad = np.expand_dims(
                  K * np.sum(indicator, axis=2), axis=-1
              )
  
          grad *= 1 / length_scale**3
  
          return K, grad
  
      return K
  
file_path: SMAC3/smac/model/gaussian_process/kernels/hamming_kernel.py
imports: from __future__ import annotations
  from typing import Any
  import numpy
  import sklearn.gaussian_process.kernels
  from smac.model.gaussian_process.kernels.base_kernels import AbstractKernel
  from smac.model.gaussian_process.priors.abstract_prior import AbstractPrior
label_desc: Use image node_id 4 for calling the HammingKernel obj's underlying member method code with example usage: obj._call(X, Y, eval_gradient, active) and returns: K, K,  grad