<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def ssim(data, ground_truth, size=11, sigma=1.5, K1=0.01, K2=0.03, dynamic_range=None, normalized=False, force_lower_is_better=False):
 r"""Structural SIMilarity between ``data`` and ``ground_truth``. The SSIM takes value -1 for maximum dissimilarity and +1 for maximum similarity. See also `this Wikipedia article <https://en.wikipedia.org/wiki/Structural_similarity>`_. Parameters data : `array-like` Input data to compare to the ground truth. ground_truth : `array-like` Reference to which ``data`` should be compared. size : odd int, optional Size in elements per axis of the Gaussian window that is used for all smoothing operations. sigma : positive float, optional Width of the Gaussian function used for smoothing. K1, K2 : positive float, optional Small constants to stabilize the result. See [Wan+2004] for details. dynamic_range : nonnegative float, optional Difference between the maximum and minimum value that the pixels can attain. Use 255 if pixel range is :math:`[0, 255]` and 1 if it is :math:`[0, 1]`. Default: `None`, obtain maximum and minimum from the ground truth. normalized : bool, optional If ``True``, the output values are mapped to the interval :math:`[0, 1]` (see `Notes` for details), otherwise return the original SSIM. force_lower_is_better : bool, optional If ``True``, it is ensured that lower values correspond to better matches by returning the negative of the SSIM, otherwise the (possibly normalized) SSIM is returned. If both `normalized` and `force_lower_is_better` are ``True``, then the order is reversed before mapping the outputs, so that the latter are still in the interval :math:`[0, 1]`. Returns ------- ssim : float FOM value, where a higher value means a better match if `force_lower_is_better` is ``False``. Notes ----- The SSIM is computed on small windows and then averaged over the whole image. The SSIM between two windows :math:`x` and :math:`y` of size :math:`N \times N` .. math:: SSIM(x,y) = \frac{(2\mu_x\mu_y + c_1)(2\sigma_{xy} + c_2)} {(\mu_x^2 + \mu_y^2 + c_1)(\sigma_x^2 + \sigma_y^2 + c_2)} where: * :math:`\mu_x`, :math:`\mu_y` is the mean of :math:`x` and :math:`y`, respectively. * :math:`\sigma_x`, :math:`\sigma_y` is the standard deviation of :math:`x` and :math:`y`, respectively. * :math:`\sigma_{xy}` the covariance of :math:`x` and :math:`y` * :math:`c_1 = (k_1L)^2`, :math:`c_2 = (k_2L)^2` where :math:`L` is the dynamic range of the image. The unnormalized values are contained in the interval :math:`[-1, 1]`, where 1 corresponds to a perfect match. The normalized values are given by .. math:: SSIM_{normalized}(x, y) = \frac{SSIM(x, y) + 1}{2} References [Wan+2004] Wang, Z, Bovik, AC, Sheikh, HR, and Simoncelli, EP. *Image Quality Assessment: From Error Visibility to Structural Similarity*. IEEE Transactions on Image Processing, 13.4 (2004), pp 600--612. """

    from scipy.signal import fftconvolve

    data = np.asarray(data)
    ground_truth = np.asarray(ground_truth)

    # Compute gaussian on a `size`-sized grid in each axis
    coords = np.linspace(-(size - 1) / 2, (size - 1) / 2, size)
    grid = sparse_meshgrid(*([coords] * data.ndim))

    window = np.exp(-(sum(xi ** 2 for xi in grid) / (2.0 * sigma ** 2)))
    window /= np.sum(window)

    def smoothen(img):
        """Smoothes an image by convolving with a window function."""
        return fftconvolve(window, img, mode='valid')

    if dynamic_range is None:
        dynamic_range = np.max(ground_truth) - np.min(ground_truth)

    C1 = (K1 * dynamic_range) ** 2
    C2 = (K2 * dynamic_range) ** 2
    mu1 = smoothen(data)
    mu2 = smoothen(ground_truth)

    mu1_sq = mu1 * mu1
    mu2_sq = mu2 * mu2
    mu1_mu2 = mu1 * mu2

    sigma1_sq = smoothen(data * data) - mu1_sq
    sigma2_sq = smoothen(ground_truth * ground_truth) - mu2_sq
    sigma12 = smoothen(data * ground_truth) - mu1_mu2

    num = (2 * mu1_mu2 + C1) * (2 * sigma12 + C2)
    denom = (mu1_sq + mu2_sq + C1) * (sigma1_sq + sigma2_sq + C2)
    pointwise_ssim = num / denom

    result = np.mean(pointwise_ssim)

    if force_lower_is_better:
        result = -result

    if normalized:
        result = (result + 1.0) / 2.0

    return result