def compose_and_evaluate_piece(rl_tuner,
                               stat_dict,
                               composition_length=32,
                               key=None,
                               tonic_note=rl_tuner_ops.C_MAJOR_TONIC,
                               sample_next_obs=True):
    """Composes a piece using the model, stores statistics about it in a dict.

  Args:
    rl_tuner: An RLTuner object.
    stat_dict: A dictionary storing statistics about a series of compositions.
    composition_length: The number of beats in the composition.
    key: The numeric values of notes belonging to this key. Defaults to
      C-major if not provided.
    tonic_note: The tonic/1st note of the desired key.
    sample_next_obs: If True, each note will be sampled from the model's
      output distribution. If False, each note will be the one with maximum
      value according to the model.
  Returns:
    A dictionary updated to include statistics about the composition just
    created.
  """
    last_observation = rl_tuner.prime_internal_models()
    rl_tuner.reset_composition()

    for _ in range(composition_length):
        if sample_next_obs:
            action, new_observation, _ = rl_tuner.action(
                last_observation,
                0,
                enable_random=False,
                sample_next_obs=sample_next_obs)
        else:
            action, _ = rl_tuner.action(
                last_observation,
                0,
                enable_random=False,
                sample_next_obs=sample_next_obs)
            new_observation = action

        obs_note = np.argmax(new_observation)

        # Compute note by note stats as it composes.
        stat_dict = add_interval_stat(rl_tuner, new_observation, stat_dict, key=key)
        stat_dict = add_in_key_stat(obs_note, stat_dict, key=key)
        stat_dict = add_tonic_start_stat(
            rl_tuner, obs_note, stat_dict, tonic_note=tonic_note)
        stat_dict = add_repeating_note_stat(rl_tuner, obs_note, stat_dict)
        stat_dict = add_motif_stat(rl_tuner, new_observation, stat_dict)
        stat_dict = add_repeated_motif_stat(rl_tuner, new_observation, stat_dict)
        stat_dict = add_leap_stats(rl_tuner, new_observation, stat_dict)

        rl_tuner.composition.append(np.argmax(new_observation))
        rl_tuner.beat += 1
        last_observation = new_observation

    for lag in [1, 2, 3]:
        stat_dict['autocorrelation' + str(lag)].append(
            rl_tuner_ops.autocorrelate(rl_tuner.composition, lag))

    add_high_low_unique_stats(rl_tuner, stat_dict)

    return stat_dict
def compose_and_evaluate_piece(rl_tuner,
                               stat_dict,
                               composition_length=32,
                               key=None,
                               tonic_note=rl_tuner_ops.C_MAJOR_TONIC,
                               sample_next_obs=True):
  """Composes a piece using the model, stores statistics about it in a dict.

  Args:
    rl_tuner: An RLTuner object.
    stat_dict: A dictionary storing statistics about a series of compositions.
    composition_length: The number of beats in the composition.
    key: The numeric values of notes belonging to this key. Defaults to
      C-major if not provided.
    tonic_note: The tonic/1st note of the desired key.
    sample_next_obs: If True, each note will be sampled from the model's
      output distribution. If False, each note will be the one with maximum
      value according to the model.
  Returns:
    A dictionary updated to include statistics about the composition just
    created.
  """
  last_observation = rl_tuner.prime_internal_models()
  rl_tuner.reset_composition()

  for _ in range(composition_length):
    if sample_next_obs:
      action, new_observation, _ = rl_tuner.action(
          last_observation,
          0,
          enable_random=False,
          sample_next_obs=sample_next_obs)
    else:
      action, _ = rl_tuner.action(
          last_observation,
          0,
          enable_random=False,
          sample_next_obs=sample_next_obs)
      new_observation = action

    obs_note = np.argmax(new_observation)

    # Compute note by note stats as it composes.
    stat_dict = add_interval_stat(rl_tuner, new_observation, stat_dict, key=key)
    stat_dict = add_in_key_stat(obs_note, stat_dict, key=key)
    stat_dict = add_tonic_start_stat(
        rl_tuner, obs_note, stat_dict, tonic_note=tonic_note)
    stat_dict = add_repeating_note_stat(rl_tuner, obs_note, stat_dict)
    stat_dict = add_motif_stat(rl_tuner, new_observation, stat_dict)
    stat_dict = add_repeated_motif_stat(rl_tuner, new_observation, stat_dict)
    stat_dict = add_leap_stats(rl_tuner, new_observation, stat_dict)

    rl_tuner.composition.append(np.argmax(new_observation))
    rl_tuner.beat += 1
    last_observation = new_observation

  for lag in [1, 2, 3]:
    stat_dict['autocorrelation' + str(lag)].append(
        rl_tuner_ops.autocorrelate(rl_tuner.composition, lag))

  add_high_low_unique_stats(rl_tuner, stat_dict)

  return stat_dict