Пример #1
0
 def test_add_line_element(self, tonic: str, scale_type: str,
                           cantus_firmus: List[str],
                           counterpoint_specifications: Dict[str, Any],
                           rules: Dict[str, Any], steps: List[Tuple[int,
                                                                    int]],
                           expected_positions: List[int],
                           expected_current_measure_durations: List[int],
                           expected_current_motion_start: LineElement,
                           expected_is_last_element_consonant: bool,
                           expected_roll: np.ndarray) -> None:
     """Test `add_line_element` method."""
     piece = Piece(tonic,
                   scale_type,
                   cantus_firmus,
                   counterpoint_specifications,
                   rules,
                   rendering_params={})
     for movement, duration in steps:
         piece.add_line_element(movement, duration)
     positions = [
         x.scale_element.position_in_semitones for x in piece.counterpoint
     ]
     assert positions == expected_positions
     assert piece.current_measure_durations == expected_current_measure_durations
     assert piece.current_motion_start_element == expected_current_motion_start
     assert piece.is_last_element_consonant == expected_is_last_element_consonant
     np.testing.assert_equal(piece.piano_roll, expected_roll)
Пример #2
0
def test_create_lilypond_file_from_piece(path_to_tmp_file: str, piece: Piece,
                                         all_steps: List[Tuple[int, int]],
                                         expected: str) -> None:
    """Test `create_lilypond_file_from_piece` function."""
    for movement, duration in all_steps:
        piece.add_line_element(movement, duration)
    create_lilypond_file_from_piece(piece, path_to_tmp_file)
    with open(path_to_tmp_file) as in_file:
        result = in_file.read()
        assert result == expected
Пример #3
0
def main() -> None:
    """Parse CLI arguments, train agent, and test it."""
    cli_args = parse_cli_args()

    default_config_path = 'configs/default_config.yml'
    default_config_path = resource_filename(__name__, default_config_path)
    config_path = cli_args.config_path or default_config_path
    with open(config_path) as config_file:
        settings = yaml.safe_load(config_file)

    results_dir = settings['piece']['rendering_params']['dir']
    if not os.path.isdir(results_dir):
        os.mkdir(results_dir)

    piece = Piece(**settings['piece'])
    env = CounterpointEnv(piece, **settings['environment'])
    best_action_sequences = optimize_with_monte_carlo_beam_search(
        env, **settings['agent'])

    env.verbose = True
    for i_episode, action_sequence in enumerate(best_action_sequences):
        print(f"\nPiece #{i_episode}:")
        env.reset()
        for action in action_sequence:
            observation, reward, done, info = env.step(action)
        env.render()
        print(f"Reward is {reward}.")
Пример #4
0
def test_create_events_from_piece(path_to_tmp_file: str, piece: Piece,
                                  all_steps: List[Tuple[int, int]],
                                  measure_in_seconds: int, volume: float,
                                  row_number: int, expected: str) -> None:
    """Test `create_events_from_piece` function."""
    for movement, duration in all_steps:
        piece.add_line_element(movement, duration)
    create_events_from_piece(piece,
                             path_to_tmp_file,
                             measure_in_seconds=measure_in_seconds,
                             cantus_firmus_timbre='default_timbre',
                             counterpoint_timbre='default_timbre',
                             volume=volume)
    with open(path_to_tmp_file) as in_file:
        for i in range(row_number):
            in_file.readline()
        result = in_file.readline()
        assert result == expected
Пример #5
0
def test_create_midi_from_piece(path_to_tmp_file: str, piece: Piece,
                                all_steps: List[Tuple[int, int]],
                                instrument_number: int, note_number: int,
                                expected: Dict[str, float]) -> None:
    """Test `create_midi_from_piece` function."""
    for movement, duration in all_steps:
        piece.add_line_element(movement, duration)
    create_midi_from_piece(piece,
                           path_to_tmp_file,
                           measure_in_seconds=1,
                           cantus_firmus_instrument=0,
                           counterpoint_instrument=0,
                           velocity=100)
    midi_data = pretty_midi.PrettyMIDI(path_to_tmp_file)
    instrument = midi_data.instruments[instrument_number]
    midi_note = instrument.notes[note_number]
    result = {
        'pitch': midi_note.pitch,
        'start': midi_note.start,
        'end': midi_note.end
    }
    assert result == expected
Пример #6
0
 def test_check_validity(self, tonic: str, scale_type: str,
                         cantus_firmus: List[str],
                         counterpoint_specifications: Dict[str, Any],
                         rules: Dict[str,
                                     Any], previous_steps: List[Tuple[int,
                                                                      int]],
                         candidate_steps: List[Tuple[int, int]],
                         expected: List[bool]) -> None:
     """Test `check_validity` method."""
     piece = Piece(tonic,
                   scale_type,
                   cantus_firmus,
                   counterpoint_specifications,
                   rules,
                   rendering_params={})
     for movement, duration in previous_steps:
         piece.add_line_element(movement, duration)
     result = [
         piece.check_validity(movement, duration)
         for movement, duration in candidate_steps
     ]
     assert result == expected
Пример #7
0
 def test_improper_initialization(self, tonic: str, scale_type: str,
                                  cantus_firmus: List[str],
                                  counterpoint_specifications: Dict[str,
                                                                    Any],
                                  rules: Dict[str,
                                              Any], match: str) -> None:
     """Test that initialization with invalid values is prohibited."""
     with pytest.raises(ValueError, match=match):
         _ = Piece(tonic,
                   scale_type,
                   cantus_firmus,
                   counterpoint_specifications,
                   rules,
                   rendering_params={})
Пример #8
0
 def test_initialization(self, tonic: str, scale_type: str,
                         cantus_firmus: List[str],
                         counterpoint_specifications: Dict[str, Any],
                         rules: Dict[str, Any], rng: Tuple[int, int],
                         roll: np.ndarray) -> None:
     """Test that initialization with valid values works as expected."""
     piece = Piece(tonic,
                   scale_type,
                   cantus_firmus,
                   counterpoint_specifications,
                   rules,
                   rendering_params={})
     assert piece.current_measure_durations == []
     assert (piece.lowest_row_to_show, piece.highest_row_to_show) == rng
     np.testing.assert_equal(piece.piano_roll, roll)
Пример #9
0
 def test_reset(self, tonic: str, scale_type: str, cantus_firmus: List[str],
                counterpoint_specifications: Dict[str, Any],
                rules: Dict[str, Any], steps: List[Tuple[int, int]],
                expected_roll: np.ndarray) -> None:
     """Test `reset` method."""
     piece = Piece(tonic,
                   scale_type,
                   cantus_firmus,
                   counterpoint_specifications,
                   rules,
                   rendering_params={})
     for movement, duration in steps:
         piece.add_line_element(movement, duration)
     piece.reset()
     assert piece.past_movements == []
     assert piece.current_time_in_eighths == 8
     np.testing.assert_equal(piece.piano_roll, expected_roll)
Пример #10
0
                              create_wav_from_events)


@pytest.mark.parametrize(
    "piece, all_steps, instrument_number, note_number, expected",
    [
        (
            # `piece`
            Piece(tonic='C',
                  scale_type='major',
                  cantus_firmus=['C4', 'D4', 'E4', 'D4', 'C4'],
                  counterpoint_specifications={
                      'start_note': 'G4',
                      'end_note': 'C5',
                      'lowest_note': 'C4',
                      'highest_note': 'C6',
                      'start_pause_in_eighths': 4,
                      'max_skip_in_degrees': 2
                  },
                  rules={
                      'names': ['rearticulation_stability'],
                      'params': {}
                  },
                  rendering_params={}),
            # `all_steps`,
            [(2, 4), [2, 8], [-1, 1]],
            # `instrument_number`
            1,
            # `note_number`
            3,
            # `expected`
            {
Пример #11
0
class TestCounterpointEnv:
    """Tests for `CounterpointEnv` class."""

    @pytest.mark.parametrize(
        "env, actions, expected",
        [
            (
                # `env`
                CounterpointEnv(
                    piece=Piece(
                        tonic='C',
                        scale_type='major',
                        cantus_firmus=['C4', 'D4', 'E4', 'D4', 'C4'],
                        counterpoint_specifications={
                            'start_note': 'E4',
                            'end_note': 'E4',
                            'lowest_note': 'G3',
                            'highest_note': 'G4',
                            'start_pause_in_eighths': 4,
                            'max_skip_in_degrees': 2,
                        },
                        rules={
                            'names': ['rearticulation_stability'],
                            'params': {}
                        },
                        rendering_params={}
                    ),
                    scoring_coefs={'number_of_skips': 1},
                    scoring_fn_params={
                        'number_of_skips': {'rewards': {1: 1}}
                    },
                    reward_for_dead_end=-100,
                ),
                # `actions`
                [14, 6, 8],
                # `expected`
                np.array([
                    [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                ])
            ),
        ]
    )
    def test_observation(
            self, env: CounterpointEnv, actions: List[int],
            expected: np.ndarray
    ) -> None:
        """Test that `step` method returns proper observation."""
        for action in actions:
            observation, reward, done, info = env.step(action)
        assert not done
        np.testing.assert_equal(observation, expected)

    @pytest.mark.parametrize(
        "env, actions, expected",
        [
            (
                # `env`
                CounterpointEnv(
                    piece=Piece(
                        tonic='C',
                        scale_type='major',
                        cantus_firmus=['C4', 'D4', 'E4', 'D4', 'C4'],
                        counterpoint_specifications={
                            'start_note': 'E4',
                            'end_note': 'E4',
                            'lowest_note': 'G3',
                            'highest_note': 'G4',
                            'start_pause_in_eighths': 4,
                            'max_skip_in_degrees': 2,
                        },
                        rules={
                            'names': [
                                'rhythmic_pattern_validity',
                                'rearticulation_stability',
                                'consonance_on_strong_beat',
                                'resolution_of_suspended_dissonance',
                            ],
                            'params': {}
                        },
                        rendering_params={}
                    ),
                    scoring_coefs={'number_of_skips': 1},
                    scoring_fn_params={
                        'number_of_skips': {'rewards': {1: 1}}
                    },
                    reward_for_dead_end=-100,
                ),
                # `actions`
                [13, 15],
                # `expected`
                [6, 11]
            ),
        ]
    )
    def test_info(
            self, env: CounterpointEnv, actions: List[int],
            expected: np.ndarray
    ) -> None:
        """Test that `step` method returns proper info about next actions."""
        for action in actions:
            observation, reward, done, info = env.step(action)
        result = info['next_actions']
        assert result == expected

    @pytest.mark.parametrize(
        "env, actions, expected",
        [
            (
                # `env`
                CounterpointEnv(
                    piece=Piece(
                        tonic='C',
                        scale_type='major',
                        cantus_firmus=['C4', 'D4', 'E4', 'D4', 'C4'],
                        counterpoint_specifications={
                            'start_note': 'E4',
                            'end_note': 'E4',
                            'lowest_note': 'G3',
                            'highest_note': 'G4',
                            'start_pause_in_eighths': 4,
                            'max_skip_in_degrees': 2,
                        },
                        rules={
                            'names': ['rearticulation_stability'],
                            'params': {}
                        },
                        rendering_params={}
                    ),
                    scoring_coefs={'number_of_skips': 1},
                    scoring_fn_params={
                        'number_of_skips': {'rewards': {1: 1}}
                    },
                    reward_for_dead_end=-100,
                ),
                # `actions`
                [14, 6, 8, 11, 5, 15, 9],
                # `expected`
                0
            ),
            (
                # `env`
                CounterpointEnv(
                    piece=Piece(
                        tonic='C',
                        scale_type='major',
                        cantus_firmus=['C4', 'D4', 'E4', 'D4', 'C4'],
                        counterpoint_specifications={
                            'start_note': 'E4',
                            'end_note': 'E4',
                            'lowest_note': 'G3',
                            'highest_note': 'G4',
                            'start_pause_in_eighths': 4,
                            'max_skip_in_degrees': 2,
                        },
                        rules={
                            'names': ['rearticulation_stability'],
                            'params': {}
                        },
                        rendering_params={}
                    ),
                    scoring_coefs={'number_of_skips': 1},
                    scoring_fn_params={
                        'number_of_skips': {'rewards': {4: 1}}
                    },
                    reward_for_dead_end=-100,
                ),
                # `actions`
                [14, 6, 8, 11, 5, 15, 9],
                # `expected`
                1
            ),
            (
                # `env`
                CounterpointEnv(
                    piece=Piece(
                        tonic='C',
                        scale_type='major',
                        cantus_firmus=['C4', 'C4', 'C3', 'C4', 'C4'],
                        counterpoint_specifications={
                            'start_note': 'E4',
                            'end_note': 'E4',
                            'lowest_note': 'G3',
                            'highest_note': 'G4',
                            'start_pause_in_eighths': 4,
                            'max_skip_in_degrees': 2,
                        },
                        rules={
                            'names': ['absence_of_large_intervals'],
                            'params': {
                                'absence_of_large_intervals': {
                                    'max_n_semitones': 7
                                }
                            }
                        },
                        rendering_params={}
                    ),
                    scoring_coefs={'number_of_skips': 1},
                    scoring_fn_params={
                        'number_of_skips': {'rewards': {1: 1}}
                    },
                    reward_for_dead_end=-100,
                ),
                # `actions`
                [14, 12],
                # `expected`
                -100
            ),
        ]
    )
    def test_reward(
            self, env: CounterpointEnv, actions: List[int], expected: float
    ) -> None:
        """Test that `step` method returns proper reward."""
        for action in actions:
            observation, reward, done, info = env.step(action)
        assert done
        assert round(reward, 4) == expected

    @pytest.mark.parametrize(
        "env, actions, expected",
        [
            (
                # `env`
                CounterpointEnv(
                    piece=Piece(
                        tonic='C',
                        scale_type='major',
                        cantus_firmus=['C4', 'D4', 'E4', 'D4', 'C4'],
                        counterpoint_specifications={
                            'start_note': 'E4',
                            'end_note': 'E4',
                            'lowest_note': 'G3',
                            'highest_note': 'G4',
                            'start_pause_in_eighths': 4,
                            'max_skip_in_degrees': 2,
                        },
                        rules={
                            'names': ['rearticulation_stability'],
                            'params': {}
                        },
                        rendering_params={}
                    ),
                    scoring_coefs={'number_of_skips': 1},
                    scoring_fn_params={
                        'number_of_skips': {'rewards': {1: 1}}
                    },
                    reward_for_dead_end=-100,
                ),
                # `actions`
                [14, 6, 8, 11, 5, 15],
                # `expected`
                np.array([
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                ])
            ),
        ]
    )
    def test_reset(
            self, env: CounterpointEnv, actions: List[int],
            expected: np.ndarray
    ) -> None:
        """Test `reset` method."""
        for action in actions:
            env.step(action)
        observation = env.reset()
        np.testing.assert_equal(observation, expected)
        assert env.piece.current_time_in_eighths == 8