code: def _get_adaptation_matrix(wp_src, wp_dst, observer, adaptation):
      
      # Get the appropriate transformation matrix, [MsubA].
      m_sharp = color_constants.ADAPTATION_MATRICES[adaptation]
  
      # In case the white-points are still input as strings
      # Get white-points for illuminant
      if isinstance(wp_src, str):
          orig_illum = wp_src.lower()
          wp_src = color_constants.ILLUMINANTS[observer][orig_illum]
      elif hasattr(wp_src, '__iter__'):
          wp_src = wp_src
  
      if isinstance(wp_dst, str):
          targ_illum = wp_dst.lower()
          wp_dst = color_constants.ILLUMINANTS[observer][targ_illum]
      elif hasattr(wp_dst, '__iter__'):
          wp_dst = wp_dst
  
      # Sharpened cone responses ~ rho gamma beta ~ sharpened r g b
      rgb_src = numpy.dot(m_sharp, wp_src)
      rgb_dst = numpy.dot(m_sharp, wp_dst)
  
      # Ratio of whitepoint sharpened responses
      m_rat = numpy.diag(rgb_dst / rgb_src)
  
      # Final transformation matrix
      m_xfm = numpy.dot(numpy.dot(pinv(m_sharp), m_rat), m_sharp)
  
      return m_xfm
docs: Calculate the correct transformation matrix based on origin and target
      illuminants. The observer angle must be the same between illuminants.
  
      See colormath.color_constants.ADAPTATION_MATRICES for a list of possible
      adaptations.
  
      Detailed conversion documentation is available at:
      http://brucelindbloom.com/Eqn_ChromAdapt.html