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 = 1
motive2 are lists of integers
representing directed pitch intervals.
motive1 with original, inversion, retrograde, and retrograde
motive2. If the absolute pairwise differences for any of
these four comparisons are all less than or equal to the
True; otherwise, returns
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 False
Returns the inversion of
return [-el for el in motive]
Returns the retrograde of
motive is a list of integers representing directed pitch intervals.
motive_list is a list of such motives.
all_var to see if
motive. For successful matches, both
comparison are added to
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 signatures
work is a list of integers representing pitches.
Returns a list of intervals.
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
where n =
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
def match(interval_lists1, interval_lists2):
signatures =  for intervals in interval_lists1: signatures.extend(allow_variation(intervals, interval_lists2)) return signatures
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
The process used by
break_into_patterns is thorough in that it finds
every contiguous pattern of the length prescribed by
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 signatures
motive = [4, 3] motivelist = [[0, 0], [0, 0], [0, -4], [-4, -3], [-3, 0]] print('motive:', motive) print('motive list:', motivelist)
Signatures discovered between
print('motive in motive list?', allow_variation(motive, motivelist)) print()
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))