def structural_match(): lge = LineGrammarExecutor() # test for non-structural match pattern = '{<C-Major: I> [iC:4 G F A] <:V> hB:4}' target = '{<F-Minor: v> qF:4 C:5 <C-Major: I> iC:4 G F A <:V> hB:4 }' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search(target_line, target_hct, GlobalSearchOptions(structural_match=False)) assert answers is not None assert 1 == len(answers) assert Position(1, 2) == answers[0] # test for illegal non-structural match answers = search.search(target_line, target_hct, GlobalSearchOptions(structural_match=True)) assert answers is not None assert 0 == len(answers) pattern = '{<C-Major: I> [iC:4 G F A] <:V> qB:4 (I, 2)[E:5 G C]}' target = '{<F-Minor: v> qF:4 C:5 <C-Major: I> [iC:4 G F A] <:V> qB:4 (I, 2)[E:5 G C]}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search(target_line, target_hct, GlobalSearchOptions(structural_match=True)) assert answers is not None assert 1 == len(answers)
def basic_search_example(): print('----- test simple hct setup -----') lge = LineGrammarExecutor() pattern = '{<C-Major: I> qC:4 D iE F}' target = '{qC:4 D iE F <E-Major: v> qF# hG# <Ab-Minor: ii> qAb:3 Cb:4 iDb F <D-Major: I> qD E F#}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search(target_line, target_hct) assert answers is not None assert 2 == len(answers) assert Position(0) == answers[0] assert Position(3, 2) == answers[1] answers = search.search(target_line, target_hct, GlobalSearchOptions(note_match_chordal=True)) assert answers is not None assert 1 == len(answers) assert Position(0) == answers[0] target = '{qC:4 D iE F <E-Major: v> qF# hG# <Ab-Minor: ii> qBb:3 Cb:4 iDb Eb <D-Major: I> qD E F#}' target_line, target_hct = lge.parse(target) answers = search.search(target_line, target_hct, GlobalSearchOptions(note_match_chordal=True)) assert answers is not None assert 2 == len(answers) assert Position(0) == answers[0] assert Position(3, 2) == answers[1]
def test_note_match_non_scalar_precision(self): print('----- test_note_match_non_scalar_precision -----') lge = LineGrammarExecutor() pattern = '{<C-Major: I> [iD:4 G F Ab]}' target = '{<G-Major: v> [iA:3 D:4 C F]}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search( target_line, target_hct, GlobalSearchOptions(note_match_non_scalar_precision=True)) assert answers is not None assert 0 == len(answers) target = '{<G-Major: v> [iA:3 D:4 C Eb]}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search( target_line, target_hct, GlobalSearchOptions(note_match_non_scalar_precision=True)) assert answers is not None assert 1 == len(answers)
def test_note_match_chordal(self): print('----- test_note_match_chordal -----') lge = LineGrammarExecutor() # note_match_chordal=True, and pattern has a chordal note that is not chordal in the pattern. # G in pattern is chordal but A in pattern is not pattern = '{<C-Major: I> [iD:4 A G F]}' target = '{<F-Minor: v> [iE:4 G A F]}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search(target_line, target_hct, GlobalSearchOptions(note_match_chordal=True)) assert answers is not None assert 0 == len(answers) target = '{<F-Minor: v> [iE:4 G E D]}' target_line, target_hct = lge.parse(target) answers = search.search(target_line, target_hct, GlobalSearchOptions(note_match_chordal=True)) assert answers is not None assert 1 == len(answers)
def test_note_match_chordal_precision(self): print('----- test_note_match_chordal_precision -----') lge = LineGrammarExecutor() # note_match_chordal=True, and pattern has a chordal note that is not chordal in the pattern. # E in pattern is chordal @ 3rd but C in target is choral @ 5th is pattern = '{<C-Major: I> [iD:4 G E C:5]}' target = '{<F-Minor: v> [iAb:3 G:4 C C:5]}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search( target_line, target_hct, GlobalSearchOptions(note_match_chordal=True, note_match_chordal_precision=True)) assert answers is not None assert 0 == len(answers) # C --> Ab: both 3rds target = '{<F-Minor: v> [iAb:3 G:4 E C:5]}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search( target_line, target_hct, GlobalSearchOptions(note_match_chordal=True, note_match_chordal_precision=True)) assert answers is not None assert 1 == len(answers)
def test_hct_match_relative_chord(self): print('----- test_hct_match_relative_chord -----') lge = LineGrammarExecutor() # hct_match_relative_chord=True, and pattern/target are different chords. pattern = '{<C-Major: I> [iC:4 G F A] <G-Major: I> qB:4 (I, 2)[iE:5 G C]}' target = '{<F-Minor: v> qF:4 C:5 <F-Major: I> [iF:4 C:5 Bb:4 D:5] <C-Minor: ii> qE:5 (I, 2)[iA:6 C:7 F:6]}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search( target_line, target_hct, GlobalSearchOptions(hct_match_relative_chord=True)) assert answers is not None assert 0 == len(answers) # hct_match_relative_chord=True, and pattern/target are same chords. pattern = '{<C-Major: I> [iC:4 G F A] <G-Major: I> qB:4 (I, 2)[iE:5 G C]}' target = '{<F-Minor: v> qF:4 C:5 <F-Major: I> [iF:4 C:5 Bb:4 D:5] <C-Minor: i> qEb:5 (I, 2)[iA:6 C:7 F:6]}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search( target_line, target_hct, GlobalSearchOptions(hct_match_relative_chord=True)) assert answers is not None assert 1 == len(answers)
def test_note_match_scalar_precision(self): print('----- test_note_match_scalar_precision -----') lge = LineGrammarExecutor() # note_match_scalar_precision=True, and pattern/target are different scale degrees on one note. pattern = '{<C-Major: I> [iC:4 G F A] <G-Major: I> qB:4 (I, 2)[iE:5 G C]}' target = '{<F-Minor: v> qF:4 C:5 <F-Major: I> [iF:4 C:5 Bb:4 D:5] <C-Minor: ii> qEb:5 (I, 2)[iA:6 C:7 G:6]}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search( target_line, target_hct, GlobalSearchOptions(note_match_scalar_precision=True)) assert answers is not None assert 0 == len(answers) # note_match_scalar_precision=True, and pattern/target note corrected. target = '{<F-Minor: v> qF:4 C:5 <F-Major: I> [iF:4 C:5 Bb:4 D:5] <C-Minor: ii> qEb:5 (I, 2)[iA:6 C:7 F:6]}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search( target_line, target_hct, GlobalSearchOptions(note_match_scalar_precision=True)) assert answers is not None assert 1 == len(answers)
def test_hct_match_tonality_modality(self): print('----- test_hct_match_tonality_modality -----') lge = LineGrammarExecutor() # hct_match_tonality_modality=True, and pattern/target are different modalities. pattern = '{<C-Major: I> [iC:4 G F A] <G-Major: I> qB:4 (I, 2)[iE:5 G C]}' target = '{<F-Minor: v> qF:4 C:5 <F-Major: I> [iF:4 C:5 Bb:4 D:5] <C-Minor: I> qE:5 (I, 2)[iA:6 C:7 F:6]}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search( target_line, target_hct, GlobalSearchOptions(hct_match_tonality_modality=True)) assert answers is not None assert 0 == len(answers) # hct_match_tonality_key_tone=True, and pattern/target are same modalities. target = '{<F-Minor: v> qF:4 C:5 <C-Major: I> [iF:4 C:5 B:4 D:5] <A-Major: I> qC#:6 (I, 2)[iF#:6 A D]}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search( target_line, target_hct, GlobalSearchOptions(hct_match_tonality_modality=True)) assert answers is not None assert 1 == len(answers)
def test_simple_hct_setup(self): print('----- test simple hct setup -----') lge = LineGrammarExecutor() pattern = '{<C-Major: I> qC:4 D}' target = '{qC:4 D <E-Major: v> F# <Ab-Minor: ii> Bb C Db <D-Major: I> D E F#}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search_hct(target_hct, GlobalSearchOptions()) assert answers is not None assert 3 == len(answers) assert 'C-Major' == str(answers[0][0].tonality) assert 'Ab-MelodicMinor' == str(answers[1][0].tonality) assert 'D-Major' == str(answers[2][0].tonality) for hc in answers: print('({0}, {1})'.format(hc[0], hc[1])) print('-----') answers = search.search_hct( target_hct, GlobalSearchOptions(hct_match_tonality_modality=True)) assert answers is not None assert 2 == len(answers) for hc in answers: print('({0}, {1})'.format(hc[0], hc[1])) print('-----') assert 'C-Major' == str(answers[0][0].tonality) assert 'D-Major' == str(answers[1][0].tonality)
def test_multi_hc_pattern_hct(self): print('----- test multi hc pattern hct setup -----') lge = LineGrammarExecutor() pattern = '{<C-Major: I> qC:4 D <F-Minor: iv> iBb:5 Db F}' # Test one hit target = '{qC:4 D <F-Minor: v> C G <Ab-Minor: ii> Bb C Db <D-Major: I> D E F#}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search_hct( target_hct, GlobalSearchOptions(hct_match_tonality_key_tone=True)) assert answers is not None assert 1 == len(answers) assert 'C-Major' == str(answers[0][0].tonality) # Test two hits target = '{qC:4 D <F-Minor: v> C G <Ab-Minor: ii> Bb C Db <D-Major: I> D E F# ' \ '<C-Major: I> qC:4 D <F-Minor: v> C G <Ab-Minor: ii> Bb C Db}' target_line, target_hct = lge.parse(target) answers = search.search_hct( target_hct, GlobalSearchOptions(hct_match_tonality_key_tone=True)) assert answers is not None assert 2 == len(answers) assert 'C-Major' == str(answers[0][0].tonality) assert 'C-Major' == str(answers[1][0].tonality) # Test 3-hc pattern pattern = '{<C-Major: I> qC:4 D <F-Minor: iv> iBb:5 Db F <A-Minor: iii> qA:5 C}' search = MelodicSearch.create(pattern) target = '{qC:4 D <F-Minor: v> C G <Ab-Minor: ii> Bb C Db <C-Major: I> D E F# ' \ '<F-Major: I> iC:4 D E <A-Minor: v> qC G A <Ab-Minor: ii> Bb C Db}' target_line, target_hct = lge.parse(target) answers = search.search_hct( target_hct, GlobalSearchOptions(hct_match_tonality_key_tone=True)) assert answers is not None assert 1 == len(answers) assert 'C-Major' == str(answers[0][0].tonality) assert Position(7, 4) == answers[0][0].position
def search(self, target_line, target_hct, search_options=GlobalSearchOptions()): """ Search a target_line/target_hct for matches to the pattern, ala GlobalSearchOptions. :param target_line: :param target_hct: :param search_options: :return: A list of starting positions that match pattern. """ target_hc_count = 0 position_answers = list() while True: hc_start = self.search_hct_incrementally(target_hct, target_hc_count, search_options) if hc_start is None: break search_answers = self.search_notes(target_line, target_hct, hc_start[1], search_options) target_hc_count = target_hc_count + 1 if search_answers is None or len(search_answers) == 0 \ else hc_start[1] + 1 if search_answers is not None and len(search_answers) != 0: position_answers.extend(search_answers) return position_answers
def test_note_match_non_scalar_to_scalar(self): print('----- test_note_match_non_scalar_to_scalar -----') lge = LineGrammarExecutor() pattern = '{<C-Major: I> [iC:4 Eb G B]}' target = '{<C-Minor: i> [iC:5 Eb:5 G B]}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search( target_line, target_hct, GlobalSearchOptions(note_match_non_scalar_to_scalar=False)) assert answers is not None assert 0 == len(answers) answers = search.search( target_line, target_hct, GlobalSearchOptions(note_match_non_scalar_to_scalar=True)) assert answers is not None assert 1 == len(answers)
def test_structural_match(self): print('----- test_structural_match -----') lge = LineGrammarExecutor() # Used in book pattern = '{<C-Major: I> [iC:4 G F A] <G-Major: I> qB:4 (I, 2)[iE:5 F# C]}' target = '{<F-Minor: v> qF:4 C:5 <F-Major: I> [iF:4 C:5 Bb:4 D:5] <C-Major: I> qE:5 (I, 2)[iA:5 C:6 F:5]}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search(target_line, target_hct, GlobalSearchOptions()) assert answers is not None print('test_multi_hc_pattern_search - test 2') for i, a in zip(range(1, len(answers) + 1), answers): print('[{0}] {1}'.format(i, a)) assert 1 == len(answers) assert Position(1, 2) == answers[0] # test for non-structural match pattern = '{<C-Major: I> iC:4 G F A <G-Major: I> qB:4 (1:12)E:5 G C}' target = '{<F-Minor: v> qF:4 C:5 <C-Major: I> [iC:4 G F A] <G-Major: I> qB:4 (I, 2)[E:5 G C]}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search(target_line, target_hct, GlobalSearchOptions(structural_match=False)) assert answers is not None assert 1 == len(answers) assert Position(1, 2) == answers[0] # test for illegal non-structural match answers = search.search(target_line, target_hct, GlobalSearchOptions(structural_match=True)) assert answers is not None assert 0 == len(answers)
def test_multi_hc_pattern_search(self): print('----- test_multi_hc_pattern_search -----') lge = LineGrammarExecutor() pattern = '{<C-Major: I> [iC:4 G F A] <G-Major: I> B:4 E:5 G C}' target = '{<F-Minor: v> qF:4 C:5 <C-Major: I> [iC:4 G F A] <G-Major: I> B:4 E:5 G C D}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search(target_line, target_hct, GlobalSearchOptions()) assert answers is not None print('test_multi_hc_pattern_search - test 1') for i, a in zip(range(1, len(answers) + 1), answers): print('[{0}] {1}'.format(i, a)) assert 1 == len(answers) assert Position(1, 2) == answers[0]
def test_single_hc_pattern_search(self): print('----- test_single_hc_pattern_search -----') lge = LineGrammarExecutor() pattern = '{<C-Major: I> qC:4 G iB E:5 C}' target = '{<F-Minor: v> qF:4 C:5 iE Ab:5 F C Db <D-Major: I> qD:3 A iC#:4 F#:6 D}' target_line, target_hct = lge.parse(target) search = MelodicSearch.create(pattern) answers = search.search(target_line, target_hct, GlobalSearchOptions()) assert answers is not None for i, a in zip(range(1, len(answers) + 1), answers): print('[{0}] {1}'.format(i, a)) assert 2 == len(answers) assert Position(0) == answers[0] assert Position(9, 8) == answers[1]