o
    si\                     @   s   d Z ddlZddlZddlZddlZddlZddlm	Z	 ddl
mZ dd Zdd	 Zd#ddZdd Zdd Zdd Zdd Zd$ddZdd Z				d%ddZ		d&dd Zd!d" ZdS )'a  Evaluation criteria for hierarchical structure analysis.

Hierarchical structure analysis seeks to annotate a track with a nested
decomposition of the temporal elements of the piece, effectively providing
a kind of "parse tree" of the composition.  Unlike the flat segmentation
metrics defined in :mod:`mir_eval.segment`, which can only encode one level of
analysis, hierarchical annotations expose the relationships between short
segments and the larger compositional elements to which they belong.

Conventions
-----------
Annotations are assumed to take the form of an ordered list of segmentations.
As in the :mod:`mir_eval.segment` metrics, each segmentation itself consists of
an n-by-2 array of interval times, so that the ``i`` th segment spans time
``intervals[i, 0]`` to ``intervals[i, 1]``.

Hierarchical annotations are ordered by increasing specificity, so that the
first segmentation should contain the fewest segments, and the last
segmentation contains the most.

Metrics
-------
* :func:`mir_eval.hierarchy.tmeasure`: Precision, recall, and F-measure of
  triplet-based frame accuracy for boundary detection.

* :func:`mir_eval.hierarchy.lmeasure`: Precision, recall, and F-measure of
  triplet-based frame accuracy for segment labeling.

References
----------
  .. [#mcfee2015] Brian McFee, Oriol Nieto, and Juan P. Bello.
    "Hierarchical evaluation of segment boundary detection",
    International Society for Music Information Retrieval (ISMIR) conference,
    2015.

  .. [#mcfee2017] Brian McFee, Oriol Nieto, Morwaread Farbood, and
    Juan P. Bello.
    "Evaluating hierarchical structure in music annotations",
    Frontiers in Psychology, 2017.
    N   )util)validate_structurec                 C   s   | t | t| S )a  Round a time-stamp to a specified resolution.

    Equivalent to ``t - np.mod(t, frame_size)``.

    Examples
    --------
    >>> _round(53.279, 0.1)
    53.2
    >>> _round(53.279, 0.25)
    53.25

    Parameters
    ----------
    t : number or ndarray
        The time-stamp to round
    frame_size : number > 0
        The resolution to round to

    Returns
    -------
    t_round : number
        The rounded time-stamp
    )npmodfloat)t
frame_size r
   F/home/ubuntu/.local/lib/python3.10/site-packages/mir_eval/hierarchy.py_round6   s   r   c                 C   s(   t tjt tj|   }t|t|fS )ac  Compute the covered time range of a hierarchical segmentation.

    Parameters
    ----------
    intervals_hier : list of ndarray
        A hierarchical segmentation, encoded as a list of arrays of segment
        intervals.

    Returns
    -------
    t_min : float
    t_max : float
        The minimum and maximum times spanned by the annotation
    )list	itertoolschainminmax)intervals_hier
boundariesr
   r
   r   _hierarchy_boundsQ   s   r           c                    s(   dd t  fddt | |D  D S )a  Align a hierarchical annotation to span a fixed start and end time.

    Parameters
    ----------
    int_hier : list of list of intervals
    lab_hier : list of list of str
        Hierarchical segment annotations, encoded as a
        list of list of intervals (int_hier) and list of
        list of strings (lab_hier)
    t_min : None or number >= 0
        The minimum time value for the segmentation
    t_max : None or number >= t_min
        The maximum time value for the segmentation

    Returns
    -------
    intervals_hier : list of list of intervals
    labels_hier : list of list of str
        `int_hier` `lab_hier` aligned to span `[t_min, t_max]`.
    c                 S   s   g | ]}t |qS r
   )r   ).0_r
   r
   r   
<listcomp>z   s    z$_align_intervals.<locals>.<listcomp>c                    s(   g | ]\}}t jt|| d qS ))labelst_mint_max)r   adjust_intervalsr   asarray)r   ivallabr   r   r
   r   r   }   s    )zip)int_hierlab_hierr   r   r
   r    r   _align_intervalse   s   r$   c           
      C   s   t |}t| \}}tt||t|| | }tjj||ftjd}t	| dD ]#\}}tt
||| tD ]}t|d |d }	|||	|	f< q9q(| S )a  Compute the (sparse) least-common-ancestor (LCA) matrix for a
    hierarchical segmentation.

    For any pair of frames ``(s, t)``, the LCA is the deepest level in
    the hierarchy such that ``(s, t)`` are contained within a single
    segment at that level.

    Parameters
    ----------
    intervals_hier : list of ndarray
        An ordered list of segment interval arrays.
        The list is assumed to be ordered by increasing specificity (depth).
    frame_size : number
        The length of the sample frames (in seconds)

    Returns
    -------
    lca_matrix : scipy.sparse.csr_matrix
        A sparse matrix such that ``lca_matrix[i, j]`` contains the depth
        of the deepest segment containing frames ``i`` and ``j``.
    dtyper   r   )r   r   intr   scipysparse
lil_matrixr   uint8	enumerater   astypeslicetocsr)
r   r	   n_startn_endn
lca_matrixlevel	intervalsr   idxr
   r
   r   _lca   s   r7   c                 C   s   t |}t| \}}tt||t|| | }tjj||ftjd}t	t
| |dD ]M\}\}}	t|	d }
ttj|
|
}t||| t}t
t| D ]$\}}tt||  }tt||  }||||f< ||krw||||f< qSq+tj|S )a}  Compute the (sparse) least-common-ancestor (LCA) matrix for a
    hierarchical segmentation.

    For any pair of frames ``(s, t)``, the LCA is the deepest level in
    the hierarchy such that ``(s, t)`` are contained within a single
    segment at that level.

    Parameters
    ----------
    intervals_hier : list of ndarray
        An ordered list of segment interval arrays.
        The list is assumed to be ordered by increasing specificity (depth).
    labels_hier : list of list of str
        ``labels_hier[i]`` contains the segment labels for the
        ``i``th layer of the annotations
    frame_size : number
        The length of the sample frames (in seconds)

    Returns
    -------
    meet_matrix : scipy.sparse.csr_matrix
        A sparse matrix such that ``meet_matrix[i, j]`` contains the depth
        of the deepest segment label containing both ``i`` and ``j``.
    r%   r   r   )r   r   r'   r   r(   r)   r*   r   r+   r,   r!   r   index_labelstriuequalouterr-   wherer.   r   
csr_matrix)r   labels_hierr	   r0   r1   r2   meet_matrixr4   r5   r   lab_enc	int_agree
int_framesseg_iseg_jidx_iidx_jr
   r
   r   _meet   s"   rG   c                 C   s*  | j |j kr
td| j d }|du r|}d}d}t|D ]i}ttd|| t||| }| ||f }	|||f }
|	  }	|
  }
t||}t	|	d| |	|d d f}	t	|
d| |
|d d f}
t
|	|
|d\}}|r|d|t|  7 }|d7 }q|r|t| }|S d}|S )a  Generalized area under the curve (GAUC)

    This function computes the normalized recall score for correctly
    ordering triples ``(q, i, j)`` where frames ``(q, i)`` are closer than
    ``(q, j)`` in the reference annotation.

    Parameters
    ----------
    ref_lca : scipy.sparse

    est_lca : scipy.sparse
        The least common ancestor matrices for the reference and
        estimated annotations

    transitive : bool
        If True, then transitive comparisons are counted, meaning that
        ``(q, i)`` and ``(q, j)`` can differ by any number of levels.

        If False, then ``(q, i)`` and ``(q, j)`` can differ by exactly one
        level.

    window : number or None
        The maximum number of frames to consider for each query.
        If `None`, then all frames are considered.

    Returns
    -------
    score : number [0, 1]
        The percentage of reference triples correctly ordered by
        the estimation.

    Raises
    ------
    ValueError
        If ``ref_lca`` and ``est_lca`` have different shapes
    z=Estimated and reference hierarchies must have the same shape.r   Nr   r   )
transitive      ?)shape
ValueErrorranger.   r   r   toarraysqueezer   concatenate_compare_frame_rankingsr   )ref_lcaest_lcarH   windowr2   score
num_framesqueryresults	ref_score	est_scorer6   
inversions
normalizerr
   r
   r   _gauc   s<   '

""
r\   c                 C   s   t j| dd\} }t j|dd\}}d}d}d}|t| k rX|t|k rX| | || k r1|d7 }n| | || krL|t ||d ||  7 }|d7 }|t| k rX|t|k s$|S )aw  Count the number of inversions in two numpy arrays:

    # points i, j where a[i] >= b[j]

    Parameters
    ----------
    a, b : np.ndarray, shape=(n,) (m,)
        The arrays to be compared.

        This implementation is optimized for arrays with many
        repeated values.

    Returns
    -------
    inversions : int
        The number of detected inversions
    T)return_countsr   r   N)r   uniquelensum)aba_countsb_countsrZ   ijr
   r
   r   _count_inversionsJ  s   
rg   Fc                    s4  t | }| | }|| }t j|ddd\}}}t|}|t| tdd }	tdd  t|||dd |dd D ]\}
}}}t	|||	|
< | |
< q@|r[t
|d	}nd
d |D }t
|\}}tt fdd|D }|dkr|dS d}|D ]\}}|t||	|  ||	|  7 }q|t|fS )a  Compute the number of ranking disagreements in two lists.

    Parameters
    ----------
    ref : np.ndarray, shape=(n,)
    est : np.ndarray, shape=(n,)
        Reference and estimate ranked lists.
        `ref[i]` is the relevance score for point `i`.
    transitive : bool
        If true, all pairs of reference levels are compared.
        If false, only adjacent pairs of reference levels are compared.

    Returns
    -------
    inversions : int
        The number of pairs of indices `i, j` where
        `ref[i] < ref[j]` but `est[i] >= est[j]`.
    normalizer : float
        The total number of pairs (i, j) under consideration.
        If transitive=True, then this is |{(i,j) : ref[i] < ref[j]}|
        If transitive=False, then this is |{i,j) : ref[i] +1 = ref[j]}|
    T)return_indexr]   c                   S   s   t dS Nr   )r.   r
   r
   r
   r   <lambda>  s    z)_compare_frame_rankings.<locals>.<lambda>c                   S   s   dS ri   r
   r
   r
   r
   r   rj     s    Nr      c                 S   s   g | ]}||d  fqS )r   r
   )r   re   r
   r
   r   r     s    z+_compare_frame_rankings.<locals>.<listcomp>c                    s    g | ]\}} |  |  qS r
   r
   )r   re   rf   ref_mapr
   r   r     s     r   )r   r   )r   argsortr^   r   appendr_   collectionsdefaultdictr!   r.   r   combinationsteer   r`   rg   )refestrH   r6   
ref_sorted
est_sortedlevels	positionscountsindexr4   cntstartendlevel_pairslcounterr[   rZ   level_1level_2r
   rm   r   rP   m  s4   
*
rP   c                 C   s   t | d }tt | d }t| dd dD ])\}}t |}t| d ||| tt |}|| r>td| ||O }qdS )a=  Validate a hierarchical segment annotation.

    Parameters
    ----------
    intervals_hier : ordered list of segmentations

    Raises
    ------
    ValueError
        If any segmentation does not span the full duration of the top-level
        segmentation.

        If any segmentation does not start at 0.
    r   r   Nz/Segment hierarchy is inconsistent at level {:d})	r   generate_labelssetintervals_to_boundariesr,   r   warningswarnformat)r   	label_topr   r4   r5   label_current
new_boundsr
   r
   r   validate_hier_intervals  s   

r         .@皙?rI   c                 C   s   |dkrt d||du rd}n||krt d||tt||| }t|  t| t| |}t||}t||||}	t||||}
tj|
|	|d}|
|	|fS )ar  Compute the tree measures for hierarchical segment annotations.

    Parameters
    ----------
    reference_intervals_hier : list of ndarray
        ``reference_intervals_hier[i]`` contains the segment intervals
        (in seconds) for the ``i`` th layer of the annotations.  Layers are
        ordered from top to bottom, so that the last list of intervals should
        be the most specific.
    estimated_intervals_hier : list of ndarray
        Like ``reference_intervals_hier`` but for the estimated annotation
    transitive : bool
        whether to compute the t-measures using transitivity or not.
    window : float > 0
        size of the window (in seconds).  For each query frame q,
        result frames are only counted within q +- window.
    frame_size : float > 0
        length (in seconds) of frames.  The frame size cannot be longer than
        the window.
    beta : float > 0
        beta parameter for the F-measure.

    Returns
    -------
    t_precision : number [0, 1]
        T-measure Precision
    t_recall : number [0, 1]
        T-measure Recall
    t_measure : number [0, 1]
        F-beta measure for ``(t_precision, t_recall)``

    Raises
    ------
    ValueError
        If either of the input hierarchies are inconsistent

        If the input hierarchies have different time durations

        If ``frame_size > window`` or ``frame_size <= 0``
    r   .frame_size ({:.2f}) must be a positive number.Nz1frame_size ({:.2f}) cannot exceed window ({:.2f})beta)	rK   r   r'   r   r   r7   r\   r   	f_measure)reference_intervals_hierestimated_intervals_hierrH   rS   r	   r   window_framesrQ   rR   t_recallt_precision	t_measurer
   r
   r   tmeasure  s(   1


r   c                 C   st   |dkrt d|t|  t| t| ||}t|||}t||dd}t||dd}	tj|	||d}
|	||
fS )ax  Compute the tree measures for hierarchical segment annotations.

    Parameters
    ----------
    reference_intervals_hier : list of ndarray
        ``reference_intervals_hier[i]`` contains the segment intervals
        (in seconds) for the ``i`` th layer of the annotations.  Layers are
        ordered from top to bottom, so that the last list of intervals should
        be the most specific.
    reference_labels_hier : list of list of str
        ``reference_labels_hier[i]`` contains the segment labels for the
        ``i`` th layer of the annotations
    estimated_intervals_hier : list of ndarray
    estimated_labels_hier : list of ndarray
        Like ``reference_intervals_hier`` and ``reference_labels_hier``
        but for the estimated annotation
    frame_size : float > 0
        length (in seconds) of frames.  The frame size cannot be longer than
        the window.
    beta : float > 0
        beta parameter for the F-measure.

    Returns
    -------
    l_precision : number [0, 1]
        L-measure Precision
    l_recall : number [0, 1]
        L-measure Recall
    l_measure : number [0, 1]
        F-beta measure for ``(l_precision, l_recall)``

    Raises
    ------
    ValueError
        If either of the input hierarchies are inconsistent

        If the input hierarchies have different time durations

        If ``frame_size > window`` or ``frame_size <= 0``
    r   r   TNr   )rK   r   r   rG   r\   r   r   )r   reference_labels_hierr   estimated_labels_hierr	   r   ref_meetest_meetl_recalll_precision	l_measurer
   r
   r   lmeasure$  s   1
r   c                 K   s   t | \}}t| |ddd\} }t||d|d\}}t }d|d< tjt| |fi |\|d< |d< |d< d	|d< tjt| |fi |\|d
< |d< |d< tjt| |||fi |\|d< |d< |d< |S )a  Compute all hierarchical structure metrics for the given reference and
    estimated annotations.

    Examples
    --------
    A toy example with two two-layer annotations

    >>> ref_i = [[[0, 30], [30, 60]], [[0, 15], [15, 30], [30, 45], [45, 60]]]
    >>> est_i = [[[0, 45], [45, 60]], [[0, 15], [15, 30], [30, 45], [45, 60]]]
    >>> ref_l = [ ['A', 'B'], ['a', 'b', 'a', 'c'] ]
    >>> est_l = [ ['A', 'B'], ['a', 'a', 'b', 'b'] ]
    >>> scores = mir_eval.hierarchy.evaluate(ref_i, ref_l, est_i, est_l)
    >>> dict(scores)
    {'T-Measure full': 0.94822745804853459,
     'T-Measure reduced': 0.8732458222764804,
     'T-Precision full': 0.96569179094693058,
     'T-Precision reduced': 0.89939075137018787,
     'T-Recall full': 0.93138358189386117,
     'T-Recall reduced': 0.84857799953694923}

    A more realistic example, using SALAMI pre-parsed annotations

    >>> def load_salami(filename):
    ...     "load SALAMI event format as labeled intervals"
    ...     events, labels = mir_eval.io.load_labeled_events(filename)
    ...     intervals = mir_eval.util.boundaries_to_intervals(events)[0]
    ...     return intervals, labels[:len(intervals)]
    >>> ref_files = ['data/10/parsed/textfile1_uppercase.txt',
    ...              'data/10/parsed/textfile1_lowercase.txt']
    >>> est_files = ['data/10/parsed/textfile2_uppercase.txt',
    ...              'data/10/parsed/textfile2_lowercase.txt']
    >>> ref = [load_salami(fname) for fname in ref_files]
    >>> ref_int = [seg[0] for seg in ref]
    >>> ref_lab = [seg[1] for seg in ref]
    >>> est = [load_salami(fname) for fname in est_files]
    >>> est_int = [seg[0] for seg in est]
    >>> est_lab = [seg[1] for seg in est]
    >>> scores = mir_eval.hierarchy.evaluate(ref_int, ref_lab,
    ...                                      est_hier, est_lab)
    >>> dict(scores)
    {'T-Measure full': 0.66029225561405358,
     'T-Measure reduced': 0.62001868041578034,
     'T-Precision full': 0.66844764668949885,
     'T-Precision reduced': 0.63252297209957919,
     'T-Recall full': 0.6523334654992341,
     'T-Recall reduced': 0.60799919710921635}

    Parameters
    ----------
    ref_intervals_hier : list of list-like
    ref_labels_hier : list of list of str
    est_intervals_hier : list of list-like
    est_labels_hier : list of list of str
        Hierarchical annotations are encoded as an ordered list
        of segmentations.  Each segmentation itself is a list (or list-like)
        of intervals (\*_intervals_hier) and a list of lists of labels
        (\*_labels_hier).
    **kwargs
        additional keyword arguments to the evaluation metrics.

    Returns
    -------
    scores : OrderedDict
        Dictionary of scores, where the key is the metric name (str) and
        the value is the (float) score achieved.

        T-measures are computed in both the "full" (``transitive=True``) and
        "reduced" (``transitive=False``) modes.

    Raises
    ------
    ValueError
        Thrown when the provided annotations are not valid.
    r   N)r   r   FrH   zT-Precision reducedzT-Recall reducedzT-Measure reducedTzT-Precision fullzT-Recall fullzT-Measure fullzL-PrecisionzL-Recallz	L-Measure)r   r$   rq   OrderedDictr   filter_kwargsr   r   )ref_intervals_hierref_labels_hierest_intervals_hierest_labels_hierkwargsr   t_endscoresr
   r
   r   evaluatek  sD   N

	r   )r   N)F)Fr   r   rI   )r   rI   )__doc__rq   r   r   numpyr   scipy.sparser(    r   segmentr   r   r   r$   r7   rG   r\   rg   rP   r   r   r   r   r
   r
   r
   r   <module>   s4   )
"+9_
#B&
W
G