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)
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
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}.")
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
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
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
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={})
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)
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)
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` {
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