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
Exemple #11
0
    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]