<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def calc_noise(volume, mask, template, noise_dict=None, ):
 """ Calculates the noise properties of the volume supplied. This estimates what noise properties the volume has. For instance it determines the spatial smoothness, the autoregressive noise, system noise etc. Read the doc string for generate_noise to understand how these different types of noise interact. Parameters volume : 4d numpy array, float Take in a functional volume (either the file name or the numpy array) to be used to estimate the noise properties of this mask : 3d numpy array, binary A binary mask of the brain, the same size as the volume template : 3d array, float A continuous (0 -> 1) volume describing the likelihood a voxel is in the brain. This can be used to contrast the brain and non brain. noise_dict : dict The initialized dictionary of the calculated noise parameters of the provided dataset (usually it is only the voxel size) Returns ------- noise_dict : dict Return a dictionary of the calculated noise parameters of the provided dataset """


    # Check the inputs
    if template.max() > 1.1:
        raise ValueError('Template out of range')

    # Create the mask if not supplied and set the mask size
    if mask is None:
        raise ValueError('Mask not supplied')

    # Update noise dict if it is not yet created
    if noise_dict is None:
        noise_dict = {'voxel_size': [1.0, 1.0, 1.0]}
    elif 'voxel_size' not in noise_dict:
        noise_dict['voxel_size'] = [1.0, 1.0, 1.0]

    # What is the max activation of the mean of this voxel (allows you to
    # convert between the mask and the mean of the brain volume)
    noise_dict['max_activity'] = np.nanmax(np.mean(volume, 3))

    # Calculate the temporal variability of the volume
    noise_dict['auto_reg_rho'], noise_dict['ma_rho'] = _calc_ARMA_noise(
        volume, mask)

    # Set it such that all of the temporal variability will be accounted for
    #  by the AR component
    noise_dict['auto_reg_sigma'] = 1

    # Preset these values to be zero, as in you are not attempting to
    # simulate them
    noise_dict['physiological_sigma'] = 0
    noise_dict['task_sigma'] = 0
    noise_dict['drift_sigma'] = 0

    # Calculate the sfnr
    noise_dict['sfnr'] = _calc_sfnr(volume,
                                    mask,
                                    )

    # Calculate the fwhm on a subset of volumes
    if volume.shape[3] > 100:
        # Take only 100 shuffled TRs
        trs = np.random.choice(volume.shape[3], size=100, replace=False)
    else:
        trs = list(range(0, volume.shape[3]))

    # Go through the trs and pull out the fwhm
    fwhm = [0] * len(trs)
    for tr in range(len(trs)):
        fwhm[tr] = _calc_fwhm(volume[:, :, :, trs[tr]],
                              mask,
                              noise_dict['voxel_size'],
                              )

    # Keep only the mean
    noise_dict['fwhm'] = np.mean(fwhm)
    noise_dict['snr'] = _calc_snr(volume,
                                  mask,
                                  )

    # Return the noise dictionary
    return noise_dict