def _wkt(eivals, timescales, normalization, normalized_laplacian):
nv = eivals.shape[<NUM_LIT:0>]<EOL>wkt = np.zeros(timescales.shape)<EOL>for idx, t in enumerate(timescales):<EOL><INDENT>wkt[idx] = np.sum(np.exp(-<NUM_LIT> * t * eivals))<EOL><DEDENT>if isinstance(normalization, np.ndarray):<EOL><INDENT>return hkt / normalization<EOL><DEDENT>if normalization == '<STR_LIT>' or normalization == True:<EOL><INDENT>return wkt / nv<EOL><DEDENT>if normalization == '<STR_LIT>':<EOL><INDENT>if normalized_laplacian:<EOL><INDENT>return wkt / (<NUM_LIT:1> + (nv - <NUM_LIT:1>) * np.cos(timescales))<EOL><DEDENT>else:<EOL><INDENT>return wkt / (<NUM_LIT:1> + (nv - <NUM_LIT:1>) * np.cos(nv * timescales))<EOL><DEDENT><DEDENT>return wkt<EOL>
Computes wave kernel trace from given eigenvalues, timescales, and normalization.

For precise definition, please refer to "NetLSD: Hearing the Shape of a Graph" by A. Tsitsulin, D. Mottin, P. Karras, A. Bronstein, E. Müller. Published at KDD'18.

Parameters
----------
eivals : numpy.ndarray
    Eigenvalue vector
timescales : numpy.ndarray
    Vector of discrete timesteps for the kernel computation
normalization : str or numpy.ndarray
    Either 'empty', 'complete' or None.
    If None or any ther value, return unnormalized wave kernel trace.
    For the details how 'empty' and 'complete' are computed, please refer to the paper.
    If np.ndarray, they are treated as exact normalization constants
normalized_laplacian: bool
    Defines whether the eigenvalues came from the normalized Laplacian. It only affects 'complete' normalization.

Returns
-------
numpy.ndarray
    Wave kernel trace signature