Functions from Cope's 1992 article in the Computer Music Journal, "Modeling of Musical Intelligence in EMI" translated into Python. Actual code is here.
motive_size = 2
variance = 1Used by allow-variation. motive1 and motive2 are lists of integers
representing directed pitch intervals.
Compares motive1 with original, inversion, retrograde, and retrograde
inversion of motive2. If the absolute pairwise differences for any of
these four comparisons are all less than or equal to the variance,
returns True; otherwise, returns False.
def allow_var(motive1, motive2):    min_length = min(len(motive1), len(motive2))
    comparisons = [motive2, inversion(motive2), retrograde(motive2),
                   inversion(retrograde(motive2))]
    for comparison in comparisons:
        for i in range(min_length):
            if abs(motive1[i] - comparison[i]) > variance:
                break
        else:
            return True
    return FalseReturns the inversion of motive.
def inversion(motive):    return [-el for el in motive]Returns the retrograde of motive.
def retrograde(motive):    return motive[::-1]motive is a list of integers representing directed pitch intervals.
motive_list is a list of such motives.
For each comparison in motive_list, calls all_var to see if
comparison matches motive. For successful matches, both motive
and comparison are added to signatures.
Returns signatures, a list of successful matches.
def allow_variation(motive, motive_list):    signatures = []
    for comparison in motive_list:
        if allow_var(motive, comparison):
            signatures.extend([motive, comparison])
    return signaturesdef produce_intervals(work):    return [work[i] - work[i-1] for i in range(1, len(work))]intervals is a list of integers. Returns a list of ngrams of intervals,
where n = size.
def break_into_patterns(intervals, size):    return [intervals[i:i+size] for i in range(len(intervals)-size+1)]Matches patterns for pattern_match. Steps through the motives in
interval_lists1 for comparisons with interval_lists2 by calling
allow_variation.
def match(interval_lists1, interval_lists2):    signatures = []
    for intervals in interval_lists1:
        signatures.extend(allow_variation(intervals, interval_lists2))
    return signatureswork1 and work2 are lists of integers representing sequences of pitches.
Matches the works under analysis after they have been reduced to
intervals and broken into motives the length of motive_size.
The process used by break_into_patterns is thorough in that it finds
every contiguous pattern of the length prescribed by motive_size so
that, for example, [1, 2, 3, -4, -5] becomes
[[1, 2, 3], [2, 3, -4], [3, -4, -5]]. Note that using intervals at
this stage means that a motive_size of two equates to three actual notes.
def pattern_match(work1, work2):    intervals1 = break_into_patterns(produce_intervals(work1), motive_size)
    intervals2 = break_into_patterns(produce_intervals(work2), motive_size)
    signatures = match(intervals1, intervals2)
    return signaturesExample motive and motivelist
motive = [4, 3]
motivelist = [[0, 0], [0, 0], [0, -4], [-4, -3], [-3, 0]]
print('motive:', motive)
print('motive list:', motivelist)Signatures discovered between motive and motivelist using
allow_varation
print('motive in motive list?', allow_variation(motive, motivelist))
print()Example works
work1 = [72, 76, 79, 71, 72, 74, 72]
work2 = [76, 76, 76, 76, 72, 69, 69, 68, 68, 74, 71]
print('work 1 pitches:', work1)
print('work 2 pitches:', work2)Signatures derived from comparison of the two works
print('patterns in common:', pattern_match(work1, work2))