
    3j4                        d Z ddlmZ ddlmZ ddlmZ dZ G d d      Z	 	 dd	Z	d
 Z
edk(  rddlZ ej                          yy)z
This module takes two XML files and displays the number of measures that
differ between the two before and after running the combined correction models
    )annotations)
correctors)	converterFc                      e Zd ZdZddZed        Zej                  d        Zd Zd Z	d Z
d	 Zd
 Zd Zd Zd Zd Zy)OmrGroundTruthPairz
    Object for making comparisons between an OMR score and the GroundTruth

    Takes in a path to the OMR and a path to the groundTruth
    (or a pair of music21.stream.Score objects).

    See below for examples.

    Nc                @   d | _         d | _        t        |d      r|j                  | _        || _        n|| _        d | _        | j                         | _        t        |d      r|j                  | _        || _	        n|| _        d | _	        | j                         | _        y )NfilePath)_overriddenDebugnumberOfDifferenceshasattrr	   omrPathomrM21ScoregetOmrScoreomrScore
groundPathgroundM21ScoregetGroundScoregroundScore)selfomrgrounds      C/DATA/.local/lib/python3.12/site-packages/music21/omr/evaluators.py__init__zOmrGroundTruthPair.__init__"   s     $#' 3
#<<DL"DDL#D((*6:&$ooDO"(D$DO"&D..0    c                >    | j                   t        S | j                   S )z_
        Returns either the debug value set for this
        evaluator, or globalDebug
        )r
   globalDebugr   s    r   debugzOmrGroundTruthPair.debug7   s"       ((((r   c                    || _         y )N)r
   )r   newDebugs     r   r   zOmrGroundTruthPair.debugB   s
     (r   c                X    | j                         | _        | j                         | _        y)z$
        Parse both scores.
        N)r   r   r   r   r   s    r   parseAllzOmrGroundTruthPair.parseAllF   s%     ((*..0r   c                l    | j                   j                          | j                  j                          y)z3
        store the Hashes for both scores.
        N)r   getAllHashesr   r   s    r   hashAllzOmrGroundTruthPair.hashAllM   s&     	""$%%'r   c                    | j                   rt        d       | j                  s$t        j                  | j
                        | _        t        j                  | j                        S )a  
        Returns a ScoreCorrector object of the OMR score. does not store it anywhere

         >>> omrPath = omr.correctors.K525omrShortPath
         >>> ground = omr.correctors.K525groundTruthShortPath
         >>> omrGTP = omr.evaluators.OmrGroundTruthPair(omr=omrPath, ground=ground)
         >>> ssOMR = omrGTP.getOmrScore()
         >>> ssOMR
         <music21.omr.correctors.ScoreCorrector object at 0x...>
        zparsing OMR score)r   printr   r   parser   r   ScoreCorrectorr   s    r   r   zOmrGroundTruthPair.getOmrScoreT   sI     ::%&(t||<D(()9)9::r   c                    | j                   rt        d       | j                  s$t        j                  | j
                        | _        t        j                  | j                        S )a  
        Returns a ScoreCorrector object of the Ground truth score

         >>> omrPath = omr.correctors.K525omrShortPath
         >>> ground = omr.correctors.K525groundTruthShortPath
         >>> omrGTP = omr.evaluators.OmrGroundTruthPair(omr=omrPath, ground=ground)
         >>> ssGT = omrGTP.getGroundScore()
         >>> ssGT
         <music21.omr.correctors.ScoreCorrector object at 0x...>
        zparsing Ground Truth score)r   r'   r   r   r(   r   r   r)   r   s    r   r   z!OmrGroundTruthPair.getGroundScoreg   sI     ::./"""+//$//"BD(()<)<==r   c                    ||k(  ryy)z\
        define the substitution cost for x and y (2 if x and y are unequal else 0)
        r       )r   xys      r   	substCostzOmrGroundTruthPair.substCost   s     6r   c                     y)z;
        define the insertion cost for x and y (1)
           r-   r   r.   s     r   
insertCostzOmrGroundTruthPair.insertCost        r   c                     y)z:
        define the deletion cost for x and y (1)
        r2   r-   r3   s     r   
deleteCostzOmrGroundTruthPair.deleteCost   r5   r   c                   t        |      }t        |      }t        |dz         D cg c]  }t        |dz         D cg c]  }d c}  }}}t        d|dz         D ]+  }||dz
     d   | j                  ||dz
           z   ||   d<   - t        d|dz         D ]+  }|d   |dz
     | j                  ||dz
           z   |d   |<   - t        d|dz         D ]l  }t        d|dz         D ]X  }t	        ||dz
     |   dz   ||   |dz
     dz   ||dz
     |dz
     | j                  ||dz
     ||dz
           z         ||   |<   Z n ||   |   S c c}w c c}}w )zS
        Computes the min edit distance from target to source. Figure 3.25
        r2   r   )lenranger4   r7   minr0   )r   targetsourcenmjidistances           r   minEditDistzOmrGroundTruthPair.minEditDist   s    KK7<QU|D|!a!e-1Q-|Dq!a%A%a!e_Q/$//&Q-2PPHQKN ! q!a%A%a[Q/$//&Q-2PPHQKN ! q!a%A1a!e_!$Xa!e_Q%7!%;%-a[Q%7!%;%-a!e_QU%;*...AqSTu*V&W"XA % ! {1~ .Ds   E	EEEc                $   d| _         | j                  j                         }| j                  j                         }t	        t        |            D ]3  }||   }||   }| j                  ||      }| xj                   |z  c_         5 | j                   S )a  
        Returns the total edit distance as an Int between
        the two scores

        This function is based on James H. Martin's minimum edit distance.

        >>> omrPath = omr.correctors.K525omrShortPath
        >>> ground = omr.correctors.K525groundTruthShortPath
        >>> omrGTP = omr.evaluators.OmrGroundTruthPair(omr=omrPath, ground=ground)
        >>> differences = omrGTP.getDifferences()
        >>> differences
        32
        r   )r   r   r$   r   r:   r9   rC   )r   omrListgtListpartNumomrPartgtPartmeasureErrorss          r   getDifferencesz!OmrGroundTruthPair.getDifferences   s     $% --,,.!!..0S\*Gg&GG_F ,,Wf=M$$5$	 +
 '''r   )NN)__name__
__module____qualname____doc__r   propertyr   setterr"   r%   r   r   r0   r4   r7   rC   rK   r-   r   r   r   r      sf    1* ) ) \\) )1(;&>J.(r   r   Nc                   |t         }d}t        | |      }|rt        d       ||j                         }n|}|rt        d|       |j                  }|}	|rt        d       g }
d}d}|du ra|	j
                  |   }|j                         }|r/t        d|       t        d	|	j
                  |   j                         |j                          nt        t        |	j
                              D ]u  }|	j
                  |   }|j                         }|t        |      z  }|j                         }|
j                  |       |t        |	j
                  |   j                        z  }w |r8t        d
       t        d       t        d|
       t        d       t        d       |	j                         }|r8t        d
       t        d       t        d|       t        d       t        d       |	j                  |
|      }|rt        d       t        |       |j                         }|r>t        d|       t        d|       t        d|       |	j                  j                          ||||d}|S )a  
    Get a dictionary showing the efficacy of the omr.correctors.ScoreCorrector on an OMR Score
    by comparing it to the GroundTruth.

    Set debug to True to see a lot of intermediary steps.

    >>> omrFilePath = omr.correctors.K525omrShortPath
    >>> groundTruthFilePath = omr.correctors.K525groundTruthShortPath
    >>> returnDict = omr.evaluators.evaluateCorrectingModel(omrFilePath, groundTruthFilePath)
    >>> for name in sorted(list(returnDict.keys())):
    ...     (name, returnDict[name])
    ('newEditDistance', 20)
    ('numberOfFlaggedMeasures', 13)
    ('originalEditDistance', 32)
    ('totalNumberOfMeasures', 84)
    r2   )r   r   zgetting differenceszOriginal edit distancez2Running Horizontal Model (Prior-based-on-distance)r   TzIncorrect measure indices:zHashed notes:z+for each entry in the array below, we have zt[flagged measure part, flagged measure index, source measure part, source measure index, source measure probability]zHORIZONTAL CORRECTING ARRAYz"**********************************z-Running Vertical Model (Prior-based-on-Parts)zVERTICAL CORRECTING MEASURESz]Finding best from Horizontal and Vertical and replacing flagged measures with source measuresz4done replacing flagged measures with source measuresznew edit distancez%number of flagged measures originallyztotal number of measures)originalEditDistancenewEditDistancenumberOfFlaggedMeasurestotalNumberOfMeasures)r   r   r'   rK   r   singlePartsgetIncorrectMeasureIndiceshashedNotesrunHorizontalCorrectionModelr:   r9   appendrunVerticalCorrectionModelgenerateCorrectedScorescoreshow)r   groundTruthPathr   originalDifferences
runOnePartpnomrGTPr   
myOmrScorescorrectingArrayHorAllPartnumberOfIncorrectMeasuresnumberOfTotalMeasures	scorePartincorrectMeasureIndicestempPNcorrectingArrayHorOnePartcorrectingArrayVertAllPart
priorScorenewNumberOfDifferences
returnDicts                        r   evaluateCorrectingModelrr      sm   & }	
B  GODF#$"$3351&(;<JABC " !TMM"%	"+"F"F"H.0GH/1==#4#@#@A..0C./Ff-I&/&J&J&L#%-D)EE%(1(N(N(P%%,,-FG!Sv)>)J)J%KK! 0 ;< D 	E+-FG23=>!"!=!=!?;< E 	F,.HI23 0 	1))*CE_`JDEj#224!#9:57PQ(*?@	*=%;-F+@BJ
 r   c                   t        j                  |       }|j                         }d}d}|j                  }t	        |      D ]  \  }}||   j                         }t	        |      D ]q  \  }	}
|	|v r|dz  }d}t	        |      D ]  \  }}|	|k(  r|
|k(  sd} n |du r0t        t        |            D ]  }||k(  r	||   |	   }||
k(  sd} n |du sm|dz  }s  ||fS )a  
    Essentially it's the ratio of amount of rhythmic similarity within a piece, which
    gives an upper bound on what the omr.corrector.prior measure should be able to
    achieve for the flagged measures. If a piece has low rhythmic similarity in general, then
    there's no way for a correct match to be found within the unflagged measures in the piece.

    Returns a tuple of the total number of NON-flagged measures and the total number
    of those measures that have a rhythmic match.

    Takes in a stream.Score.

    >>> c = converter.parse(omr.correctors.K525omrShortPath)  # first 21 measures
    >>> totalUnflagged, totalUnflaggedWithMatches = omr.evaluators.autoCorrelationBestMeasure(c)
    >>> (totalUnflagged, totalUnflaggedWithMatches)
    (71, 64)
    >>> print( float(totalUnflaggedWithMatches) / totalUnflagged )
    0.901...

    Schoenberg has low autoCorrelation.

    >>> c = corpus.parse('schoenberg/opus19/movement6')
    >>> totalUnflagged, totalUnflaggedWithMatches = omr.evaluators.autoCorrelationBestMeasure(c)
    >>> (totalUnflagged, totalUnflaggedWithMatches)
    (18, 6)
    >>> print( float(totalUnflaggedWithMatches) / totalUnflagged )
    0.333...

    r   r2   FT)r   r)   r$   rW   	enumeraterX   r:   r9   )
inputScoress	allHashestotalMeasurestotalMatchesrW   pNum
pHashArrayincorrectIndicesrA   mHashmatchr@   nHash	otherPNum	otherHashs                   r   autoCorrelationBestMeasurer   7  s   : 
	"	":	.B!IML..K%i0j&t,GGI!*-HAu$$QME &j156E> E 2 ~!&s;'7!8I D(  )) 4Q 7I E) $ "9 }!5 . 1: <((r   __main__)NNF)rO   
__future__r   music21.omrr   music21r   r   r   rr   r   rL   mainTestr-   r   r   <module>r      s^    # " w( w(t =AAFcLB)J zG r   