def cosine_score_matrix(spectra, tol, max_mz=1000.0, min_intens=0, mass_shifting=False, method='hungarian', num_workers=4, filename=None, safety_points=None):
    'Create Matrix of all modified cosine similarities.\n\n    Takes some time to calculate, so better only do it once and save as npy.\n\n    Now implemented: parallelization of code using concurrent.futures and numba options.\n\n    spectra: list\n        List of spectra (of Spectrum class)\n    tol: float\n        Tolerance to still count peaks a match (mz +- tolerance).\n    max_mz: float\n        Maxium m-z mass to take into account\n    #min_mz: float\n    #    Minimum m-z mass to take into account\n    min_intens: float\n        Sets the minimum relative intensity peaks must have to be looked at for\n        potential matches.\n    mass_shifting: bool\n        Set to \'True\' if mass difference between spectra should be accounted for\n        --> "modified cosine" score\n        Set to \'False\'  for --> "normal cosine" score\n    method: \'greedy\', \'greedy-numba\', \'hungarian\'\n        "greedy" will use Simon\'s molnet scoring which is faster than hungarian,\n        but not 100% accurate\n        regarding the weighted bipartite matching problem.\n        "hungarian" will use the Hungarian algorithm, which is more accurate.\n        Since its slower, numba is used here to compile in time.\n        "greedy-numba" will use a (partly) numba compiled version of greedy.\n        Much faster, but needs numba.\n    num_workers: int\n        Number of threads to use for calculation.\n    filename: str/ None\n        Filename to look for existing npy-file with molent matrix. Or, if not\n        found, to use to save the newly calculated matrix.\n    safety_points: int\n        Number of safety points, i.e. number of times the modcos-matrix is saved\n        during process. Set to \'None\' to avoid saving matrix on the way.\n    '
    if (filename is not None):
        if (filename[(- 4):] != '.npy'):
            filename = (filename + '.npy')
        try:
            print('Loading similarity scores from', filename)
            modcos_sim = np.load(filename)
            print('Loading min_match values from', (filename[:(- 4)] + '_matches.npy'))
            modcos_matches = np.load((filename[:(- 4)] + '_matches.npy'))
            diagonal = modcos_sim.diagonal()
            if (np.min(diagonal) == 0):
                print('Uncomplete cosine similarity scores found and loaded.')
                missing_scores = np.where((diagonal == 0))[0].astype(int)
                print('Missing cosine scores will be calculated.')
                counter_total = int(((len(spectra) ** 2) / 2))
                counter_init = (counter_total - np.sum((len(spectra) - missing_scores)))
                print('About ', (100 * (counter_init / counter_total)), '% of the values already completed.')
                collect_new_data = True
            else:
                print('Complete cosine similarity scores found and loaded.')
                missing_scores = []
                counter_init = 0
                collect_new_data = False
        except FileNotFoundError:
            print('Could not find file ', filename, 'or file', (filename[:(- 4)] + '_matches.npy'))
            if mass_shifting:
                print('Modified cosine scores will be calculated from scratch.')
            else:
                print('Cosine scores will be calculated from scratch.')
            collect_new_data = True
            missing_scores = np.arange(0, len(spectra))
            counter_init = 0
    else:
        collect_new_data = True
        missing_scores = np.arange(0, len(spectra))
        counter_init = 0
    if collect_new_data:
        if (counter_init == 0):
            modcos_sim = np.zeros((len(spectra), len(spectra)))
            modcos_matches = np.zeros((len(spectra), len(spectra)))
        counter = counter_init
        if (safety_points is not None):
            safety_save = int((((len(spectra) ** 2) / 2) / safety_points))
        print('Calculate pairwise scores by', num_workers, 'number of workers.')
        for i in missing_scores:
            spec1 = np.array(spectra[i].peaks, dtype=float)
            spec1 = spec1[(spec1[:, 0] < max_mz), :]
            parameter_collection = []
            for j in range(i, len(spectra)):
                spec2 = np.array(spectra[j].peaks, dtype=float)
                spec2 = spec2[(spec2[:, 0] < max_mz), :]
                if mass_shifting:
                    mass_shift = (spectra[i].parent_mz - spectra[j].parent_mz)
                else:
                    mass_shift = None
                parameter_collection.append([spec1, spec2, i, j, mass_shift, tol, min_intens, method, counter])
                counter += 1
            modcos_pairs = []
            with ThreadPoolExecutor(max_workers=num_workers) as executor:
                futures = [executor.submit(modcos_pair, X, len(spectra)) for X in parameter_collection]
                modcos_pairs.append(futures)
            for (m, future) in enumerate(modcos_pairs[0]):
                (_, _, ind_i, ind_j, _, _, _, _, counting) = parameter_collection[m]
                modcos_sim[(ind_i, ind_j)] = future.result()[0]
                modcos_matches[(ind_i, ind_j)] = future.result()[1]
                if ((filename is not None) and (safety_points is not None)):
                    if (((counting + 1) % safety_save) == 0):
                        np.save(filename, modcos_sim)
                        np.save((filename[:(- 4)] + '_matches.npy'), modcos_matches)
        for i in range(1, len(spectra)):
            for j in range(i):
                modcos_sim[(i, j)] = modcos_sim[(j, i)]
                modcos_matches[(i, j)] = modcos_matches[(j, i)]
        if (filename is not None):
            np.save(filename, modcos_sim)
            np.save((filename[:(- 4)] + '_matches.npy'), modcos_matches)
    return (modcos_sim, modcos_matches)
Create Matrix of all modified cosine similarities.

Takes some time to calculate, so better only do it once and save as npy.

Now implemented: parallelization of code using concurrent.futures and numba options.

spectra: list
    List of spectra (of Spectrum class)
tol: float
    Tolerance to still count peaks a match (mz +- tolerance).
max_mz: float
    Maxium m-z mass to take into account
#min_mz: float
#    Minimum m-z mass to take into account
min_intens: float
    Sets the minimum relative intensity peaks must have to be looked at for
    potential matches.
mass_shifting: bool
    Set to 'True' if mass difference between spectra should be accounted for
    --> "modified cosine" score
    Set to 'False'  for --> "normal cosine" score
method: 'greedy', 'greedy-numba', 'hungarian'
    "greedy" will use Simon's molnet scoring which is faster than hungarian,
    but not 100% accurate
    regarding the weighted bipartite matching problem.
    "hungarian" will use the Hungarian algorithm, which is more accurate.
    Since its slower, numba is used here to compile in time.
    "greedy-numba" will use a (partly) numba compiled version of greedy.
    Much faster, but needs numba.
num_workers: int
    Number of threads to use for calculation.
filename: str/ None
    Filename to look for existing npy-file with molent matrix. Or, if not
    found, to use to save the newly calculated matrix.
safety_points: int
    Number of safety points, i.e. number of times the modcos-matrix is saved
    during process. Set to 'None' to avoid saving matrix on the way.
def cosine_score_matrix(spectra, tol, max_mz=1000.0, min_intens=0, mass_shifting=False, method='hungarian', num_workers=4, filename=None, safety_points=None):
    'Create Matrix of all modified cosine similarities.\n\n    Takes some time to calculate, so better only do it once and save as npy.\n\n    Now implemented: parallelization of code using concurrent.futures and numba options.\n\n    spectra: list\n        List of spectra (of Spectrum class)\n    tol: float\n        Tolerance to still count peaks a match (mz +- tolerance).\n    max_mz: float\n        Maxium m-z mass to take into account\n    #min_mz: float\n    #    Minimum m-z mass to take into account\n    min_intens: float\n        Sets the minimum relative intensity peaks must have to be looked at for\n        potential matches.\n    mass_shifting: bool\n        Set to \'True\' if mass difference between spectra should be accounted for\n        --> "modified cosine" score\n        Set to \'False\'  for --> "normal cosine" score\n    method: \'greedy\', \'greedy-numba\', \'hungarian\'\n        "greedy" will use Simon\'s molnet scoring which is faster than hungarian,\n        but not 100% accurate\n        regarding the weighted bipartite matching problem.\n        "hungarian" will use the Hungarian algorithm, which is more accurate.\n        Since its slower, numba is used here to compile in time.\n        "greedy-numba" will use a (partly) numba compiled version of greedy.\n        Much faster, but needs numba.\n    num_workers: int\n        Number of threads to use for calculation.\n    filename: str/ None\n        Filename to look for existing npy-file with molent matrix. Or, if not\n        found, to use to save the newly calculated matrix.\n    safety_points: int\n        Number of safety points, i.e. number of times the modcos-matrix is saved\n        during process. Set to \'None\' to avoid saving matrix on the way.\n    '
    if (filename is not None):
        if (filename[(- 4):] != '.npy'):
            filename = (filename + '.npy')
        try:
            print('Loading similarity scores from', filename)
            modcos_sim = np.load(filename)
            print('Loading min_match values from', (filename[:(- 4)] + '_matches.npy'))
            modcos_matches = np.load((filename[:(- 4)] + '_matches.npy'))
            diagonal = modcos_sim.diagonal()
            if (np.min(diagonal) == 0):
                print('Uncomplete cosine similarity scores found and loaded.')
                missing_scores = np.where((diagonal == 0))[0].astype(int)
                print('Missing cosine scores will be calculated.')
                counter_total = int(((len(spectra) ** 2) / 2))
                counter_init = (counter_total - np.sum((len(spectra) - missing_scores)))
                print('About ', (100 * (counter_init / counter_total)), '% of the values already completed.')
                collect_new_data = True
            else:
                print('Complete cosine similarity scores found and loaded.')
                missing_scores = []
                counter_init = 0
                collect_new_data = False
        except FileNotFoundError:
            print('Could not find file ', filename, 'or file', (filename[:(- 4)] + '_matches.npy'))
            if mass_shifting:
                print('Modified cosine scores will be calculated from scratch.')
            else:
                print('Cosine scores will be calculated from scratch.')
            collect_new_data = True
            missing_scores = np.arange(0, len(spectra))
            counter_init = 0
    else:
        collect_new_data = True
        missing_scores = np.arange(0, len(spectra))
        counter_init = 0
    if collect_new_data:
        if (counter_init == 0):
            modcos_sim = np.zeros((len(spectra), len(spectra)))
            modcos_matches = np.zeros((len(spectra), len(spectra)))
        counter = counter_init
        if (safety_points is not None):
            safety_save = int((((len(spectra) ** 2) / 2) / safety_points))
        print('Calculate pairwise scores by', num_workers, 'number of workers.')
        for i in missing_scores:
            spec1 = np.array(spectra[i].peaks, dtype=float)
            spec1 = spec1[(spec1[:, 0] < max_mz), :]
            parameter_collection = []
            for j in range(i, len(spectra)):
                spec2 = np.array(spectra[j].peaks, dtype=float)
                spec2 = spec2[(spec2[:, 0] < max_mz), :]
                if mass_shifting:
                    mass_shift = (spectra[i].parent_mz - spectra[j].parent_mz)
                else:
                    mass_shift = None
                parameter_collection.append([spec1, spec2, i, j, mass_shift, tol, min_intens, method, counter])
                counter += 1
            modcos_pairs = []
            with ThreadPoolExecutor(max_workers=num_workers) as executor:
                futures = [executor.submit(modcos_pair, X, len(spectra)) for X in parameter_collection]
                modcos_pairs.append(futures)
            for (m, future) in enumerate(modcos_pairs[0]):
                (_, _, ind_i, ind_j, _, _, _, _, counting) = parameter_collection[m]
                modcos_sim[(ind_i, ind_j)] = future.result()[0]
                modcos_matches[(ind_i, ind_j)] = future.result()[1]
                if ((filename is not None) and (safety_points is not None)):
                    if (((counting + 1) % safety_save) == 0):
                        np.save(filename, modcos_sim)
                        np.save((filename[:(- 4)] + '_matches.npy'), modcos_matches)
        for i in range(1, len(spectra)):
            for j in range(i):
                modcos_sim[(i, j)] = modcos_sim[(j, i)]
                modcos_matches[(i, j)] = modcos_matches[(j, i)]
        if (filename is not None):
            np.save(filename, modcos_sim)
            np.save((filename[:(- 4)] + '_matches.npy'), modcos_matches)
    return (modcos_sim, modcos_matches)