
    rh                       S SK Jr  S SKrS SKrS SKrS SKrS SKrS SKJr  S SKJ	r	  \R                  R                  \5      r\\R                  -   S-   r\\R                  -   S-   r\\R                  -   S-   r\\R                  -   S-   rS	r\R(                  " S
/ SQ5      r\R(                  " S/ SQ5      r " S S5      r " S S5      r " S S5      r " S S5      r\S:X  a  S SKr\R8                  " 5         gg)    )annotationsN)note)streamzk525OMRMvt1.xmlzk525GTMvt1.xmlzk525OMRshort.xmlzk525GTshort.xmlFMeasureRelationship)flaggedMeasurePartflaggedMeasureIndexcorrectMeasurePartcorrectMeasureIndexcorrectionProbabilityPriorsIntegrationScore)total
horizontalverticalignoredc                  v    \ rS rSrSrSS jrS rS rS rS r	S	 r
S
 rS rS rS rS rS rS rS rS rSrg)ScoreCorrector,   zG
takes in a music21.stream.Score object and runs OMR correction on it.
Nc                    Xl         / U l        / U l        S U l        [	        [        UR                  5      5       H-  nU R                  R                  U R                  U5      5        M/     g N)	scoresinglePartsmeasureSlicesdistributionArrayrangelenpartsappendgetSinglePart)selfr   ps      P/home/james-whalen/.local/lib/python3.13/site-packages/music21/omr/correctors.py__init__ScoreCorrector.__init__0   sV    
!%s5;;'(A##D$6$6q$9: )    c                "    U R                  5       $ )z7
Run all known models for OMR correction on
this score
)runPriorModelr   s    r!   runScoreCorrector.run9   s    
 !!##r$   c                ~    U R                  5       nU R                  5       nU R                  UU5        U R                  $ )ze
run the horizontal and vertical correction models
on the score.  Returns the new self.score object.
)runHorizontalCorrectionModelrunVerticalCorrectionModelgenerateCorrectedScorer   )r   !correctingArrayHorizontalAllPartscorrectingArrayVerticalAllPartss      r!   r&   ScoreCorrector.runPriorModel@   sA    
 -1,M,M,O)*.*I*I*K'##$E$C	Ezzr$   c                f    / nU R                    H  nUR                  UR                  5        M      U$ )a  
Returns an array of arrays, each of which is the hashed notes for a part

>>> p1 = stream.Part()
>>> p1.insert(0, meter.TimeSignature('4/4'))
>>> p1.append(note.Note('C', type = 'half'))
>>> p1.append(note.Rest(type='half'))
>>> p1.append(note.Note('C', type = 'half'))
>>> p1.append(note.Rest(type='half'))
>>> p1.makeMeasures(inPlace=True)
>>> p2 = stream.Part()
>>> p2.insert(0, meter.TimeSignature('4/4'))
>>> p2.repeatAppend(note.Note('C', type='quarter'), 8)
>>> p2.makeMeasures(inPlace=True)
>>> s = stream.Score()
>>> s.insert(0, p1)
>>> s.insert(0, p2)
>>> ss = omr.correctors.ScoreCorrector(s)
>>> ss.getAllHashes()
[['Z[', 'Z['], ['PPPP', 'PPPP']]
)r   r   hashedNotes)r   allPartsHashesr    s      r!   getAllHashesScoreCorrector.getAllHashesK   s1    , !!A!!!--0 "r$   c                H    [        U R                  R                  U   U5      $ )zD
returns a NEW SinglePart object for part number pn from the score

)
SinglePartr   r   )r   pns     r!   r   ScoreCorrector.getSinglePartf   s      $****2.33r$   c                r    / nU R                    H$  nUR                  5       nUR                  U5        M&     U$ )zi
runs for sp in self.singleParts:
    sp.runHorizontalCorrectionModel()

returns correctingArrayAllParts
)r   r+   r   )r   correctingArrayAllPartsspcorrectingArrayOneParts       r!   r+   +ScoreCorrector.runHorizontalCorrectionModeln   s@     #%""B%'%D%D%F"#**+AB # '&r$   c           	         U R                   U   nUS:X  a  [        SU S35      e U$ ! [         a    [        X5      nU[        U R                   5      :  aB  U R                   R	                  S [        [        U R                   5      US-   5       5       5        X R                   U'   U R                  5       nX2l         U$ f = f)a  
Given an index, i, returns a MeasureSlice object at that index

>>> omrPath = omr.correctors.K525omrShortPath
>>> omrScore = converter.parse(omrPath)
>>> ssOMR = omr.correctors.ScoreCorrector(omrScore)
>>> ssOMR.getMeasureSlice(4)
<music21.omr.correctors.MeasureSlice object at 0x...>
r   zMeasure slice z out of rangec              3  &   #    U  H  nS v   M	     g7f)r   N ).0_s     r!   	<genexpr>1ScoreCorrector.getMeasureSlice.<locals>.<genexpr>   s     )[5Z!5Zs      )r   
IndexErrorMeasureSlicer   extendr   verticalProbabilityDistallProbabilities)r   imsvpds       r!   getMeasureSliceScoreCorrector.getMeasureSlice{   s    
	&##A&BQw >!M!BCC  	  	&d&BC**++"")))[U3t?Q?Q;RTUXYTY5Z)[[$&q!..0C"%		&s   $) BC Cc                    / n[        [        U R                  5      5       H-  nU R                  U   R                  nUR	                  U5        M/     U$ )a  
Returns an array of the incorrect measure indices arrays for each part.
This is used in the MeasureSlice object to make sure we're not comparing a flagged
measure to other flagged measures in its slice

>>> omrPath = omr.correctors.K525omrShortPath
>>> omrScore = converter.parse(omrPath)
>>> ssOMR = omr.correctors.ScoreCorrector(omrScore)
>>> ssOMR
<music21.omr.correctors.ScoreCorrector object at 0x...>
>>> ssOMR.getAllIncorrectMeasures()
[[1, 3, 9, 10, 12, 17, 20], [2, 12, 14, 17], [1, 9], []]
)r   r   r   incorrectMeasuresr   )r   allPartsIncorrectMeasuresr    ims       r!   getAllIncorrectMeasures&ScoreCorrector.getAllIncorrectMeasures   sP     %'!s4++,-A!!!$66B%,,R0 . )(r$   c                    U R                   b  U R                   $ / n[        U R                  5      n[        U5       H#  nUR	                  U R                  U5      5        M%     Xl         U$ )zq
Uses a score and returns an array of probabilities.
For n in the array, n is the probability that the nth part

)r   r   r   r   r   ,getVerticalProbabilityDistributionSinglePart)r   r   numberOfPartsrL   s       r!   rJ   &ScoreCorrector.verticalProbabilityDist   sg     !!-)))D,,-}%A$$T%V%VWX%YZ &!2  r$   c                <   Un[        U R                  5      nS/U-  n[        U R                  U   R                  5      n[        U5       H4  nU R	                  X&5      n[        U5       H  nXH==   Xx   -  ss'   M     M6     U V	s/ s H  oU-  PM	     n
n	U
$ s  sn	f )a  
Returns the Vertical Probability Distribution (PrP) for a single part.

Get the Priors for the Violin II part (first 20 measures only)

>>> omrPath = omr.correctors.K525omrShortPath
>>> omrScore = converter.parse(omrPath)
>>> ssOMR = omr.correctors.ScoreCorrector(omrScore)
>>> allDists = ssOMR.getVerticalProbabilityDistributionSinglePart(1)
>>> ['%0.3f' % p for p in allDists]
['0.571', '1.000', '0.667', '0.714']
r   )r   r   r2   r   9getVerticalProbabilityDistributionSinglePartSingleMeasure)r   r8   rL   rY   partDistArraylengthOfScorekmeasureDistArraypartCounterxnormalizedPartDistArrays              r!   rX   ;ScoreCorrector.getVerticalProbabilityDistributionSinglePart   s     D,,-m+D,,Q/;;<}%A#]]^_c$]3*.>.KK*  4 & ?L"Lm}#4m"L&& #Ms   Bc                   UnUn[        U R                  5      n[        U R                  U   R                  U   5      nS/U-  nUR	                  U R                  U   R
                  U   5        [        U5       HI  nX:X  a  SXx'   M  UR                  U R                  U   R
                  U   5      n	U	S:X  a  SXx'   ME  SXx'   MK     U$ )N              ?)r   r   MeasureHashmeasureStreamsetSequenceMatcherr2   r   getMeasureDifference)
r   r8   measureIndexrL   r_   rY   mhr`   partNummeasureDifferences
             r!   r\   HScoreCorrector.getVerticalProbabilityDistributionSinglePartSingleMeasure   s    D,,-))!,::1=>5=0
d..q1==a@A]+G|,/ ) %'$;$;$$W-99!<%! %+03$-03$- ,  r$   c                J    U R                  U5      nUR                  U5      nU$ )zx
Returns an array of the minimum distance measure indices
given a measure (with index i) within a part pn to compare to
)rO   runSliceSearch)r   rL   r8   rM   correctingMeasures        r!   runVerticalSearch ScoreCorrector.runVerticalSearch   s+    
 !!!$--b1  r$   c                x   U R                   U   R                  U   nU R                   U   R                  U   nUR                  [        R                  5       Vs/ s H  owR
                  PM     nnUR                   H  n	UR                  U	5        M     Sn
U H  n	[        R                  " U	5      n [        U[        R                  5      (       a?  X   nUR                  UR
                  l        UR                  UR
                  l        U
S-  n
UR                  U5        M     gs  snf ! [         a     N'f = f)a	  
Takes a destination measure, deletes its contents, and replaces them
with the contents of a source measure but retains as many pitches as possible

The destination measure would normally be in the set F of flagged measures
(having an incorrect number of beats)
while the source measure is in the set C of correcting measures.

>>> s = corpus.parse('bwv66.6').measures(1, 2)
>>> s.show('text')
{0.0} <music21.stream.Part Soprano>
    ...
    {0.0} <music21.stream.Measure 1 offset=0.0>
        {0.0} <music21.note.Note A>
        {1.0} <music21.note.Note B>
        {2.0} <music21.note.Note C#>
        {3.0} <music21.note.Note E>
    {4.0} <music21.stream.Measure 2 offset=4.0>
        {0.0} <music21.note.Note C#>
        {1.0} <music21.note.Note B>
        {2.0} <music21.note.Note A>
        {3.0} <music21.note.Note C#>
{0.0} <music21.stream.Part Alto>
     ...
    {0.0} <music21.stream.Measure 1 offset=0.0>
        {0.0} <music21.note.Note F#>
        {1.0} <music21.note.Note E>
        {2.0} <music21.note.Note E>
        {3.0} <music21.note.Note E>
    {4.0} <music21.stream.Measure 2 offset=4.0>
        {0.0} <music21.note.Note E>
        {0.5} <music21.note.Note A>
        {1.0} <music21.note.Note G#>
        {2.0} <music21.note.Note E>
        {3.0} <music21.note.Note G#>
...

Replace part 1, measure 2 (index 1) with part 0, measure 1 (index 0) while retaining
as many pitches as possible. The eighth-notes will become quarters:

>>> scOMR = omr.correctors.ScoreCorrector(s)
>>> scOMR.substituteOneMeasureContentsForAnother(0, 0, 1, 1)
>>> s2 = scOMR.score
>>> s2.show('text')
{0.0} <music21.stream.Part Soprano>
    ...
    {0.0} <music21.stream.Measure 1 offset=0.0>
        {0.0} <music21.note.Note A>
        {1.0} <music21.note.Note B>
        {2.0} <music21.note.Note C#>
        {3.0} <music21.note.Note E>
    {4.0} <music21.stream.Measure 2 offset=4.0>
        {0.0} <music21.note.Note C#>
        {1.0} <music21.note.Note B>
        {2.0} <music21.note.Note A>
        {3.0} <music21.note.Note C#>
{0.0} <music21.stream.Part Alto>
     ...
    {0.0} <music21.stream.Measure 1 offset=0.0>
        {0.0} <music21.note.Note F#>
        {1.0} <music21.note.Note E>
        {2.0} <music21.note.Note E>
        {3.0} <music21.note.Note E>
    {4.0} <music21.stream.Measure 2 offset=4.0>
        {0.0} <music21.note.Note E>
        {1.0} <music21.note.Note A>
        {2.0} <music21.note.Note G#>
        {3.0} <music21.note.Note E>
...
r   rF   N)r   ri   getElementsByClassr   Notepitchelementsremovecopydeepcopy
isinstanceoctavenamerG   r   )r   sourceHorizontalIndexsourceVerticalIndexdestinationHorizontalIndexdestinationVerticalIndexincorrectMeasurecorrectMeasurenoldNotePitchesel
pitchIndexnewEloldPitchs                r!   &substituteOneMeasureContentsForAnother5ScoreCorrector.substituteOneMeasureContentsForAnother   s   ` 56DDE_` 	 ))*=>LLMbc+;+N+Ntyy+YZ+Ya''+YZ"++B##B' , 
 BMM"%EeTYY//-9H)1EKK&'/}}EKK$!OJ ##E* ! [  s   D'3AD,,
D98D9c                N   U R                  5       n/ n[        [        U R                  5      5       Hp  n/ nU R                  U   R                  n[        [        U5      5       H)  nXV   nU R                  Xs5      nUR                  U5        M+     UR                  U5        Mr     U$ )z
Runs a basic vertical correction model on a ScoreCorrector object.
That is, for each flagged measure, this method replaces the rhythm in that flagged measure
with the rhythm of a measure with the least difference.
)rJ   r   r   r   rR   rt   r   )	r   unused_allProbabilitiescorrectingMeasuresAllPartsr    correctingMeasuresOnePartrT   rL   incorrectMeasureIndexrs   s	            r!   r,   )ScoreCorrector.runVerticalCorrectionModelP  s     #'">">"@%'"s4++,-A(*%!!!$66B3r7^(*%$($:$:;P$T!)001BC $ '--.GH . *)r$   c           	        SnSnSnSn[        U R                  5      n[        U5       GHN  n[        [        X   5      5       GH   n	[        [        X(   5      5       H  n
X   U	   nX(   U
   nUR                  UR                  :w  a  M-  UR                  UR                  :w  a  MI  UR                  nUR                  nUS-  nUR
                  UR
                  :  a2  US-  nUR                  nUR                  nU R                  UUX5        M  US-  nUR                  nUR                  nU R                  UUX5        M     GM     U R                  U   R                  5       U R                  U   l
        GMQ     [        X4XV5      $ )a  
Given two correcting arrays (one from the horizontal model and one from
the vertical model),
which offer source measures for each flagged measure in each part,
this method compares the probabilities of proposed
source measures for each flagged measure,
and replaces the flagged measures contents with the more probable source measure
using substituteOneMeasureContentsForAnother.
It then rehashes the score so that a new difference comparison can be run.

Returns a collections.namedtuple of the total number of flagged measures, the total number
corrected by the horizontal (Prior based on Distance) and the
vertical (Prior based on Parts)
methods.
r   rF   )r   r   r   r   r   r   r
   r	   r   "getSequenceHashesFromMeasureStreamr2   r   )r   horizontalArrayverticalArraytotalFlaggedtotalHorizontaltotalVerticaltotalIgnorednumPartsr    hvhorizontalTupleverticalTupler   r   r   r   s                    r!   r-   %ScoreCorrector.generateCorrectedScoreb  s     t''(xA3123s=#345A&5&8&;O$1$4Q$7M&99]=]=]] &::m>_>__ 1@1T1T./>/Q/Q, A%L '<<}?b?bb'1,0?0S0S-.=.P.P+CC13F6R &*0=0Q0Q-.;.N.N+CC13F6R; 6 4F   #FFH Q+G !J &l]aar$   )r   r   r   r   r   )__name__
__module____qualname____firstlineno____doc__r"   r(   r&   r4   r   r+   rO   rU   rJ   rX   r\   rt   r   r,   r-   __static_attributes__rA   r$   r!   r   r   ,   sU    ;$	64'.)*!'0 *!b+H*$;br$   r   c                  P    \ rS rSrSS jrS rSS jrS rSS jrS r	S	 r
S
 rSrg)r7   i  Nc                    Xl         X l        S U l        S U l        S U l        Ub?  U R                  5       U l        U R                  5       U l        U R                  SS9U l
        g S U l        S U l        S U l
        g )NT)runFast)	scorePart
partNumber
indexArrayprobabilityDistributionrs   getMeasuresri   r   r2   getIncorrectMeasureIndicesrR   )r   partr8   s      r!   r"   SinglePart.__init__  sy    '+$!%!%!1!1!3D#FFHD%)%D%DT%D%RD"!%D#D%)D"r$   c                v    U R                   R                  [        R                  5      U l        U R                  $ r   )r   rw   r   Measureri   r'   s    r!   r   SinglePart.getMeasures  s)    !^^>>v~~N!!!r$   c                   SSK Jn  / U l        USL aS   U R                  S   nUR                  =(       d    UR                  UR                  5      nUc  UR                  S5      nOUR                  S5      n[        [        U R                  5      5       H  nUSL a=  U R                  U   nUR                  =(       d    UR                  UR                  5      nUR                  R                  nU R                  U   R                  R                  U:X  a  M  U R                  R                  U5        M     U R                  $ ! [         a    UR                  S5      n GNf = f)a3  
Returns an array of all the measures that OMR software would flag - that is,
measures that do
not have the correct number of beats given the current time signature

if runFast is True (by default), assumes that the initial TimeSignature
is the TimeSignature for the entire piece.

>>> p = stream.Part()
>>> ts = meter.TimeSignature('6/8')
>>> m1 = stream.Measure()
>>> m1.number = 1
>>> m1.append(ts)
>>> m1.append(note.Note('C4', quarterLength = 3.0))
>>> p.append(m1)
>>> m2 = stream.Measure()
>>> m2.number = 2
>>> m2.append(note.Note('C4', quarterLength = 1.5))
>>> p.append(m2)

>>> sp = omr.correctors.SinglePart(p, pn = 0)
>>> sp.getIncorrectMeasureIndices()
[1]

>>> p[1]
<music21.stream.Measure 2 offset=3.0>
>>> p[1].insert(0, meter.TimeSignature('3/8'))
>>> sp.getIncorrectMeasureIndices(runFast=False)
[]

r   )meterTz4/4F)music21r   rR   ri   timeSignaturegetContextByClassTimeSignaturerG   r   r   barDurationquarterLengthdurationr   )r   r   r   mtsrL   tsOmrs          r!   r   %SinglePart.getIncorrectMeasureIndices  s8   B 	"!#d?0&&q)__P(;(;E<O<O(P z((/$$U+Bs4--./A%&&q)__P(;(;E<O<O(PNN00E!!!$--;;uD&&--a0 0 %%%%  0((/0s   =E E#"E#c                    / nU R                   R                  [        R                  5      n[	        [        U5      5       H1  n[        X#   5      nUR                  5       nUR                  U5        M3     U$ )zH
takes in a measure stream of a part
returns an array of hashed strings
)	ri   rw   r   r   r   r   rh   getHashStringr   )r   measureStreamNotesmeasureStreamMeasuresrL   rm   myHashedNotess         r!   r   -SinglePart.getSequenceHashesFromMeasureStream  sl    
   $ 2 2 E Efnn Us012A256B,,.M%%m4 3
 "!r$   c                   USL a  U R                   b  U R                   $ [        U R                  5      S-  nS/U-  nS/U-  n[        [        U R                  5      5       GH  n[	        U R
                  U   5      nUR                  U R                  U   5        / n[        [        U R                  5      5       H  n[        U R                  5      XX-
  -
  n	XX-
  * XI'   XX:X  a*  UR                  S5        [        U R                  5      X9'   MS  UR                  U R                  U   5      n
U
S:X  a  UR                  S5        X9==   S-  ss'   M  UR                  S5        X9==   S-  ss'   M     GM     UR                  S5        U Vs/ s H  o[        U R                  5      -  PM     nnUR                  S5        Xl         X@l
        U R                   $ s  snf )aX  
Uses (takes?) an array of hashed measures and returns an array of probabilities.
For n in the array, n is the probability that the measure (n-(length of score)) away
from a flagged measure will offer a rhythmic solution.

These are the probabilities that, within a part, a measure offers a solution, given its
distance from a flagged measure.
F   r   d   rg   rf   )r   r   r2   r   rh   ri   rj   r   rk   popr   )r   
regeneratesizeOfArrayallDistArrayr   rL   rm   	distArrayr_   
arrayIndexro   rb   normalizedDistArrays                r!   horizontalProbabilityDist$SinglePart.horizontalProbabilityDist  s    4#?#?#K///$**+a/s[(S;&
s4++,-AT//23B!!$"2"21"56I3t//01 !1!12ae<
+,5
&6$$S) 0343C3C/DL,(*(?(?@P@PQR@S(T%(C/!((-$0C70!((-$0C70 2	 .* 	qBNO,Q3t'7'7#88,O"':$$+++	 Ps   !Gc                   U R                  5       nU R                  nX1   nU R                  U   n[        U R                  U   5      nUR                  U5        / n[        [        U R                  5      5       Ha  nX;   a  UR                  S5        M  UR                  U R                  U   5      n	U R                  UU5      n
U	U
-  nUR                  U5        Mc     [        U5      n/ n[        U5       H  u  pX:X  d  M  UR                  U5        M     [        U R                  UU R                  US   U5      U l        U R                  $ )zq
Returns an array of the indices of the minimum distance measures
given a measure (with index i) to compare to.

rf   r   )r   rR   r2   rh   ri   rj   r   r   r   getProbabilityBasedOnChangesgetProbabilityDistributionmax	enumerater   r   rs   )r   rL   unused_probabilityDistributionrR   r   hashedNotesIrm   probabilityArrayr_   priorBasedOnChangesProbabilitypriorBasedOnDistanceProbabilitypriorBasedOnChangesAndDistancemaximumProbabilitymaximumProbabilityMeasures
lineNumberr   s                   r!   runHorizontalSearchSinglePart.runHorizontalSearch+  s`    *.)G)G)I& 22 1 4''(=>++,ABC
l+s4++,-A% '', 241P1P$$Q'2).262Q2Q)3+/ 3Q4S3T. ''(FG . !!12 &("&'78MJ&*11*= 9 "5T__5J59__5OPQ5R5G	"I %%%r$   c                    / n[        [        U R                  5      5       H%  nU R                  U5      nUR	                  U5        M'     U$ )z
Runs a basic horizontal correction model on a score.
That is, for each flagged measure, this method replaces the rhythm in that flagged measure
with the rhythm of a measure with the least difference.
)r   r   rR   r   r   )r   correctingArrayrL   rs   s       r!   r+   'SinglePart.runHorizontalCorrectionModelV  sK     s41123A $ 8 8 ;""#45 4 r$   c                `    U R                   nX-
  [        U R                  5      -   S-
  nX4   nU$ )NrF   )r   r   r2   )r   sourceIndexdestinationIndexr   indexdistanceProbabilitys         r!   r   %SinglePart.getProbabilityDistributionc  s:    "&">">/3t7G7G3HH1L5<""r$   )rs   r2   rR   r   ri   r   r   r   )NN)F)r   r   r   r   r"   r   r   r   r   r   r+   r   r   rA   r$   r!   r7   r7     s-    *"
:&z"(,T)&V#r$   r7   c                  *    \ rS rSrSrS rS rS rSrg)rH   ij  z,
represents a single measure from all parts
c                >   / U l         Xl        X l        / U l        S U l        S U l        [        [        U R                  R                  5      5       HI  nU R                  R                  U   nUR                  5       nU R                   R                  XR   5        MK     g r   )arrayOfMeasureObjectsr   r   sliceMeasureHashObjectsrK   rs   r   r   r   r   r   )r   r   rL   r   r   measuress         r!   r"   MeasureSlice.__init__o  s    %'"

')$ $!%DJJ$:$: ;<J::))*5D'')H&&--hk: =r$   c                    [        [        U R                  5      5       H6  n[        U R                  U   5      nU R                  R                  U5        M8     U R                  $ )a@  
>>> omrPath = omr.correctors.K525omrShortPath
>>> omrScore = converter.parse(omrPath)
>>> ssOMR = omr.correctors.ScoreCorrector(omrScore)
>>> ssOMR
<music21.omr.correctors.ScoreCorrector object at 0x...>
>>> measureSlice = ssOMR.getMeasureSlice(2)
>>> measureSlice
<music21.omr.correctors.MeasureSlice object at 0x...>
)r   r   r   rh   r   r   )r   rL   rm   s      r!   getSliceHashesMeasureSlice.getSliceHashes}  sU     s45567AT77:;B((//3 8 +++r$   c                   / nU R                  5       nU R                  R                  5       nX1   nUR                  5         [	        [        U R                  5      5       H  nXa:X  a  UR                  S5        M  U R                  XF   ;   a  UR                  S5        M@  X6   R                  5       nUR                  U5      nU R                  n	X   U   n
UU
-  nUR                  U5        M     [        U5      n/ n[        U5       H  u  pX:X  d  M  UR                  U5        M     [        UU R                  US   U R                  U5      U l        U R                  $ )am  
Takes in an incorrectPartIndex and returns an array
of the measure indices within the slice that have the
maximum probability to correct a given flagged measures.

Returns a namedtuple (MeasureRelationship)

>>> omrPath = omr.correctors.K525omrShortPath
>>> omrScore = converter.parse(omrPath)
>>> ssOMR = omr.correctors.ScoreCorrector(omrScore)
>>> measureSlice = ssOMR.getMeasureSlice(2)
>>> measureSlice
<music21.omr.correctors.MeasureSlice object at 0x...>
>>> measureSlice.runSliceSearch(1)
MeasureRelationship(flaggedMeasurePart=1, flaggedMeasureIndex=2,
    correctMeasurePart=3, correctMeasureIndex=2, correctionProbability=0.0054...)

>>> measureSlice = ssOMR.getMeasureSlice(3)
>>> measureSlice.runSliceSearch(0)
MeasureRelationship(flaggedMeasurePart=0,
    flaggedMeasureIndex=3, correctMeasurePart=1, correctMeasureIndex=3,
    correctionProbability=2.41...e-14)
rf   r   )r   r   rU   rj   r   r   r   r   r   r   r   rK   r   r   r   rs   )r   incorrectPartIndexr   sliceHashesallIncorrectMeasuresrm   r_   
hashStringr   ap'priorBasedOnVerticalDistanceProbabilityr   r   r   r   r   s                   r!   rr   MeasureSlice.runSliceSearch  s\   0 ))+#zzAAC,
s45567A& '',366 '', )^99;
131P1PQ[1\.**:<:PQR:S72P4[3\. ''(FG 8" !!12%'"&'78MJ&*11*= 9 "55G59ZZ5OPQ5R59ZZ5G	"I %%%r$   )rK   r   rs   r   r   r   N)	r   r   r   r   r   r"   r   rr   r   rA   r$   r!   rH   rH   j  s    ;,$9&r$   rH   c                      \ rS rSrSrSS jrS rS rS rS r	S	 r
SS
 jrS rSS jrS rSS jrS rS rS rS rS rSrg)rh   i  z`
Able to do a number of matching, substitution and hashing operations on
a given measure object
Nc                h    Xl         S U l        S U l        U R                   b  U R                  5         g g r   )measureObjectr   sequenceMatcherr   )r   r  s     r!   r"   MeasureHash.__init__  s4    *#)  *r$   c                4   SnU R                   c  gU R                   nUR                  SL a  UR                  nOUR                  5       nUR                  nU H  nUR                  R
                  S:X  a  XR                  U5      -  nM2  UR                  (       a  XR                  U5      -  nMX  UR                  (       a  Mk  UR                  (       a  XR                  U5      -  nM  UR                  (       d  M  XR                  U5      -  nM     Xl        U$ )a  
takes a stream and returns a hashed string for searching on
and stores it in self.hashString

If a measure object has multiple voices, use the first  voice.

>>> m = stream.Measure()
>>> m.append(note.Note('C', quarterLength=1.5))
>>> m.append(note.Note('C', quarterLength=0.5))
>>> m.append(note.Rest(quarterLength=1.5))
>>> m.append(note.Note('B', quarterLength=0.5))

>>> hasher = omr.correctors.MeasureHash(m)
>>> hasher.getHashString()
'VFUF'
>>> hasher.hashString == 'VFUF'
True
 Trf   )r  isFlatnotesAndRestschordifyr   r   	hashGraceisNotehashNoteisResthashRestisChordr   )r   r   mo	subStreamr   s        r!   r   MeasureHash.getHashString  s    ( 
%99!!BI((B Azz''3.nnQ//
mmA..
XXX88--"22JYYY--"22J  %r$   c                   U R                  UR                  R                  5      nSnUS-  S:X  a  US:  a  [        U5      nU$ US-  S:X  a  US:  a  [        US-   5      nU$ US:  a  [        S5      nU$ [	        S5      e)a'  
Encodes a note

>>> hasher = omr.correctors.MeasureHash()

>>> n = note.Note('C')
>>> n.duration.type = 'quarter'
>>> hasher.hashNote(n)
'P'
>>> n2 = note.Note('C')
>>> n2.duration.type = 'half'
>>> hasher.hashNote(n2)
'Z'
>>> n3 = note.Note('C', quarterLength=1.5)
>>> hasher.hashNote(n3)
'V'
r  r   r   rF   zInvalid Byte Encoding)hashQuarterLengthr   r   chr
ValueError)r   r   duration1to127byteEncodings       r!   r  MeasureHash.hashNote  s    ( //

0H0HIA"~'9~.L  a1$!);~12L  aq6L  455r$   c                >    U R                  S5      n[        U5      nU$ )z0
Gives a Grace Note a duration of a 128th note

g      ?)r  r  )r   r   graceNoteDurationr  s       r!   r
  MeasureHash.hashGrace&  s&    
 !228<,-r$   c                    U R                  UR                  R                  5      nSnUS-  S:X  a  US:  a  [        US-   5      nU$ US-  S:X  a  US:  a  [        U5      nU$ US:  a  [        S5      nU$ )zm
Encodes a rest

>>> r = note.Rest(1.0)
>>> hasher = omr.correctors.MeasureHash()
>>> hasher.hashRest(r)
'Q'
r  r   r   rF   )r  r   r   r  )r   rr  r  s       r!   r  MeasureHash.hashRest/  s     //

0H0HIA"~'9~12L
 	 a1$!);~.L  aq6Lr$   c                    SnU(       a;  [        [        R                  " US-  5      S-  5      n[        [	        US5      S5      nU$ )z
Turns a QuarterLength duration into an integer from 1 to 127

>>> hasher = omr.correctors.MeasureHash()
>>> hasher.hashQuarterLength(1.0)
80

>>> hasher.hashQuarterLength(2.0)
90
rF      
      )intmathlog2r   min)r   qlr  s      r!   r  MeasureHash.hashQuarterLengthD  sA      28!4r!9:N ^S!91=Nr$   c                    Uc0  U R                   c  U R                  5       nXl         OU R                   n[        R                  " S US5      U l        g )Nr  )r   r   difflibSequenceMatcherr  )r   hashess     r!   rj   MeasureHash.setSequenceMatcherU  sD    >&++-"(&66tVRHr$   c                    U R                   R                  U5        U R                   R                  5       nUS:X  a  SnSU-
  $ )a/  
Returns the difference ratio between two measures
b is the "correct" measure that we want to replace the flagged measure with

Takes a hashString

>>> m = stream.Measure()
>>> m.append(note.Note('C', quarterLength=1.5))
>>> m.append(note.Note('C', quarterLength=0.5))
>>> m.append(note.Rest(quarterLength=1.5))
>>> m.append(note.Note('B', quarterLength=0.5))

>>> hasher = omr.correctors.MeasureHash(m)
>>> hasher.setSequenceMatcher()
>>> hasher.getMeasureDifference('VGUF')
0.25

>>> m = stream.Measure()
>>> m.append(note.Note('C', quarterLength=1.5))
>>> m.append(note.Note('C', quarterLength=0.5))
>>> m.append(note.Rest(quarterLength=1.5))
>>> m.append(note.Note('B', quarterLength=0.5))

>>> hasher = omr.correctors.MeasureHash(m)
>>> hasher.setSequenceMatcher()
>>> hasher.getMeasureDifference('VFUF')
1.0

rg   rf   rF   )r  set_seq2ratio)r   r   myRatios      r!   rk    MeasureHash.getMeasureDifference_  sB    > 	%%j1&&,,.c>G7{r$   c                    U R                   c  U R                  5         Ub  U R                   R                  U5        U R                   R                  5       $ )a5  
Gets the opcodes from a simple sequenceMatcher for the current measureHash

Example of Violin II vs. Viola and Cello in K525 I, m. 17

>>> vlnII = converter.parse('tinynotation: 4/4 e4 e8. e8 c4 c8 c8').flatten().notes.stream()
>>> viola = converter.parse('tinynotation: 4/4 c4 c8  c8 A4 A8 A8').flatten().notes.stream()
>>> cello = converter.parse('tinynotation: 4/4 C4 C4     D4 D4   ').flatten().notes.stream()
>>> vlnII_MH = omr.correctors.MeasureHash(vlnII)
>>> viola_MH = omr.correctors.MeasureHash(viola)
>>> cello_MH = omr.correctors.MeasureHash(cello)
>>> vlnII_MH.getOpCodes(viola_MH.hashString)
[('equal', 0, 1, 0, 1), ('replace', 1, 2, 1, 2), ('equal', 2, 6, 2, 6)]
>>> vlnII_MH.getOpCodes(cello_MH.hashString)
[('equal', 0, 1, 0, 1), ('delete', 1, 3, 1, 1),
 ('equal', 3, 4, 1, 2), ('replace', 4, 6, 2, 4)]
)r  rj   r0  get_opcodes)r   	otherHashs     r!   
getOpCodesMeasureHash.getOpCodes  sK    $ '##%   )))4##//11r$   c                    U R                  U5      nSnU H1  nU R                  XA5      nUR                  U5      S:X  a  UnM-  X5-  nM3     U$ )aq  
Takes a hash string and gets the probability based on changes.

>>> otherHash = 'e'
>>> hashString = 'GFPGF'
>>> mh = omr.correctors.MeasureHash()
>>> mh.hashString = hashString
>>> mh.getProbabilityBasedOnChanges(otherHash)
2.9472832125e-14

Example of Violin II vs. Viola and Cello in K525 I, m. 17

>>> vlnII = converter.parse('tinynotation: 4/4 e4 e8. e8 c4 c8 c8').flatten().notes.stream()
>>> viola = converter.parse('tinynotation: 4/4 c4 c8  c8 A4 A8 A8').flatten().notes.stream()
>>> cello = converter.parse('tinynotation: 4/4 C4 C4     D4 D4   ').flatten().notes.stream()
>>> vlnII_MH = omr.correctors.MeasureHash(vlnII)
>>> viola_MH = omr.correctors.MeasureHash(viola)
>>> cello_MH = omr.correctors.MeasureHash(cello)
>>> vlnII_MH.getProbabilityBasedOnChanges(viola_MH.hashString)
0.0076295...
>>> vlnII_MH.getProbabilityBasedOnChanges(cello_MH.hashString)
4.077...e-09
rf   r   )r7  !differenceProbabilityForOneOpCoder   )r   r6  opcodesallProbabilityopcodeoneProbabilitys         r!   r   (MeasureHash.getProbabilityBasedOnChanges  sW    4 //),F!CCFVN}}V$)!/0  r$   c                   Uc  U R                   nUc  [        S5      eUS   nUS:X  a  US   US   -
  nU R                  5       U-  $ US:X  a%  X!S   US    nX1S   US    nU R                  Xg5      $ US	:X  a  US   US   -
  nU R	                  5       U-  $ US
:X  a  US   US   -
  n	U R                  5       U	-  $ [        S5      e)a  
Given an opCodeTuple and a source, differenceProbabilityForOneOpCode
returns the difference probability for one type of op-code
(replace, insert, delete, or equal).
Here, the destination is in the set F of flagged measures and the
source is in the set C of correcting measures.
Source and destination are both hashStrings

>>> source = 'PFPFFF'
>>> destination = 'PFPFGF'
>>> ops = ('equal', 0, 4, 0, 4)
>>> mh = omr.correctors.MeasureHash()
>>> mh.differenceProbabilityForOneOpCode(ops, source, destination)
0.8762013031640626

Omission

>>> ops2 = ('insert', 4, 4, 4, 5)
>>> mh2 = omr.correctors.MeasureHash()
>>> mh2.differenceProbabilityForOneOpCode(ops2, source, destination)
0.009

>>> ops3 = ('replace', 2, 4, 2, 4)
>>> mh3 = omr.correctors.MeasureHash()
>>> mh3.differenceProbabilityForOneOpCode(ops3, 'PPPPP', 'PPVZP')
0.0001485

Five deletes in a row:

>>> ops4 = ('delete', 0, 5, 0, 0)
>>> mh3 = omr.correctors.MeasureHash()
>>> mh3.differenceProbabilityForOneOpCode(ops4, 'e', 'GFPGF')
1.024e-12

Example of Violin II vs. Viola in K525 I, m. 17

>>> vlnII = converter.parse('tinynotation: 4/4 e4 e8. e8 c4 c8 c8').flatten().notes.stream()
>>> viola = converter.parse('tinynotation: 4/4 c4 c8  c8 A4 A8 A8').flatten().notes.stream()
>>> vlnIIMH = omr.correctors.MeasureHash(vlnII)
>>> violaMH = omr.correctors.MeasureHash(viola)
>>> vlnIIMH.hashString
'PLFPFF'
>>> violaMH.hashString
'PFFPFF'
>>> opCodes = vlnIIMH.getOpCodes(violaMH.hashString)
>>> for oc in opCodes:
...    print('%30r : %.3f' %
...           (oc, vlnIIMH.differenceProbabilityForOneOpCode(oc, violaMH.hashString)))
         ('equal', 0, 1, 0, 1) : 0.968
       ('replace', 1, 2, 1, 2) : 0.009
         ('equal', 2, 6, 2, 6) : 0.876
z HashString has not yet been set!r   equal      replacerF   r   insertdeletezIncorrect opCodeType value.)r   r  getProbabilityOnEqualitygetProbabilityOnSubstitutegetProbabilityOnOmissiongetProbabilityOnAddition)
r   opCodeTuplesourcedestination
opCodeTypelengthOfEqualSectionsourceSnippetdestinationSnippetnumberOfOmissionsnumberOfAdditionss
             r!   r:  -MeasureHash.differenceProbabilityForOneOpCode  s	   l //K" !CDD ^
 #.q>KN#B 1138LLL9$"q>+a.AM!,^KN!K22=UU8# +AQ ?0026GGG8# +AQ ?0026GGG:;;r$   c                    g)zp
Parts or the whole of a string were equal.

>>> omr.correctors.MeasureHash().getProbabilityOnEquality()
0.9675
g(\?rA   r'   s    r!   rG  $MeasureHash.getProbabilityOnEquality  s     r$   c                    g)a#  
In order for the source to be correct,
the destination omitted a symbol.
Associated with type 'delete' and in the case of replacement of
a dotted version of a note with an undotted version (or double dot with dotted, etc.)

>>> omr.correctors.MeasureHash().getProbabilityOnOmission()
0.009
g;On?rA   r'   s    r!   rI  $MeasureHash.getProbabilityOnOmission  s     r$   c                    g)z
In order for the source to be correct,
the destination added a symbol
Associated with type 'insert'

>>> omr.correctors.MeasureHash().getProbabilityOnAddition()
0.004
gMbp?rA   r'   s    r!   rJ  $MeasureHash.getProbabilityOnAddition#  s     r$   c                6   [        U5      n[        U5      nX4:  a   X4-
  nU R                  5       U-  nUSSU-   nO'X4:  a   XC-
  nU R                  5       U-  nUSSU-   nOSn[        [        U5      5       H  nX   n	X(   n
X`R	                  X5      -  nM      U$ )a[  
Source and destination are measureHash strings
Source is in set C of correcting measures.
Destination is in set F of flagged measures.

(Rossant & Bloch)

* value change: 50.77% of all errors (inverse: 0.0197)
* confusions: 9.23% of all errors (inverse: 0.108)
    Note: these get the most probability, because they are the rarest
* omission: 27.69% of all errors (inverse: 0.0361)
* addition: 12.31% of all errors (inverse: 0.08125)

>>> mh = omr.correctors.MeasureHash()

Replacement of eighth note (F) for quarter note (P) = shift of one value:

>>> mh.getProbabilityOnSubstitute('F', 'P')
0.0165

Replacement of eighth note (F) for eighth rest (G) = shift of one type:

>>> mh.getProbabilityOnSubstitute('F', 'G')
0.003

Omission of any symbol, less common so costs more
The proposed correction assumes that the incorrect measure omitted a symbol

>>> mh.getProbabilityOnSubstitute('', 'P')
0.009

Addition of any symbol, less common so costs more
The proposed correction assumes that the incorrect measure added a symbol

>>> mh.getProbabilityOnSubstitute('P', '')
0.004

Combination of value shift and an addition:

>>> mh.getProbabilityOnSubstitute('F', 'PP')
0.0001485


Take minimum length. Compare index to index. Any additional letters
in the flagged measure get graded as additions. Any additional letters
in the comparison measure get graded as omissions.

r   rg   )r   rJ  rI  r   getProbabilityFromOneCharSub)r   rL  rM  lsldrS  baseProbabilityrR  rL   
sourceChardestChars              r!   rH  &MeasureHash.getProbabilityOnSubstitute.  s    b [7 "";;=ARROAb#445FW "";;=ARRO%a->(>?K!Os6{#AJ"~H@@VVO $
 r$   c                >   [        U5      [        U5      -
  n[        R                  " U5      nUS:X  a  gUS-  S:X  a
  US-  nSU-  $ US:X  a  U R                  5       $ US:X  a  U R	                  5       $ US-  S	:w  a  g
U R	                  5       U R                  5       -  $ )aG  
Source and destination are strings of one character

>>> mh = omr.correctors.MeasureHash()

Eighth note to eighth rest:

>>> mh.getProbabilityFromOneCharSub('F', 'G')
0.003

Eighth note to quarter note:

>>> mh.getProbabilityFromOneCharSub('F', 'P')
0.0165

Eighth note to half note:

>>> mh.getProbabilityFromOneCharSub('F', 'Z')
0.0002722...

Quarter note to dotted quarter note:

>>> mh.getProbabilityFromOneCharSub('P', 'V')
0.009


Dotted quarter note to quarter note:

>>> mh.getProbabilityFromOneCharSub('V', 'P')
0.004

>>> mh.getProbabilityFromOneCharSub('A', 'Y')
3.6e-05
rf   rg   r"  g      $@gL7A`?g      @g      r   r   g~jth?)ordr%  fabsrJ  rI  )r   rL  rM  charDiffabsCharDiffnumberOfShiftss         r!   r]  (MeasureHash.getProbabilityFromOneCharSubr  s    F v;[!11ii)s?2$(4/N^++_002200221_!
 002T5R5R5TTTr$   )r   r  r  r   )r   r   r   r   r   r"   r   r  r
  r  r  rj   rk   r7  r   r:  rG  rI  rJ  rH  r]  r   rA   r$   r!   rh   rh     sa    
!*X D*"I#J22#JJ<X
	BH7Ur$   rh   __main__)
__future__r   r|   collectionsr+  r%  osr   r   r   pathdirname__file__pathNamesepK525omrFilePathK525groundTruthFilePathK525omrShortPathK525groundTruthShortPathdebug
namedtupler   r   r   r7   rH   rh   r   mainTestrA   r$   r!   <module>r{     s   #     	  77??8$RVV#&77"RVV+.>> bff$'99 #bff,/@@ !,,  %//2 qb qbhG# G#T^& ^&B^U ^UB z r$   