def halt(population, generation_count): """ Given a population of candidate solutions and generation count (the number of epochs the algorithm has run) will return a boolean to indicate if an acceptable solution has been found within the referenced population. """ fittest = population[0] max_fitness = MAX_REWARD for i in range(len(fittest.chromosome)): # Check for dissonances. Each dissonance should have incremented # the fitness because it has been "placed" correctly. cantus_firmus_note = cantus_firmus[i / 4] melody_note = fittest.chromosome[i] interval = melody_note - cantus_firmus_note if interval in DISSONANCES: max_fitness += REWARD_STEPWISE_MOTION else: if i > 0 and i < (len(fittest.chromosome) - 2): if is_stepwise_motion(fittest.chromosome, i): max_fitness += REWARD_STEPWISE_MOTION return (fittest.fitness >= max_fitness or generation_count > DEFAULT_MAX_GENERATION)
def fitness_function(genome): """ Given a candidate solution will return its fitness score assuming the cantus_firmus in this closure. Caches the fitness score in the genome. """ # Save some time! if genome.fitness is not None: return genome.fitness # The fitness score to be returned. fitness_score = 0.0 # Counts the number of repeated notes. repeats = 0 # Counts the amount of parallel motion. parallel_motion = 0 # Counts the number of jumps in the melodic contour. jump_contour = 0 contrapunctus = genome.chromosome # Make sure the solution starts correctly (at a 5th or octave). first_interval = contrapunctus[0] - cantus_firmus[0] if first_interval == 7 or first_interval == 4: fitness_score += REWARD_FIRST else: fitness_score -= PUNISH_FIRST # Make sure the solution finishes correctly (at an octave). if contrapunctus[-1] - cantus_firmus[-1] == 7: fitness_score += REWARD_LAST else: fitness_score -= PUNISH_LAST # Ensure the penultimate note is step wise onto the final note. if abs(contrapunctus[-1] - contrapunctus[-2]) == 1: fitness_score += REWARD_LAST_STEP else: fitness_score -= PUNISH_LAST_STEP # Reward contrary motion onto the final note. cantus_firmus_motion = cantus_firmus[-1] - cantus_firmus[-2] contrapunctus_motion = contrapunctus[-1] - contrapunctus[-2] if ((cantus_firmus_motion < 0 and contrapunctus_motion > 0) or (cantus_firmus_motion > 0 and contrapunctus_motion < 0)): fitness_score += REWARD_LAST_MOTION else: fitness_score -= PUNISH_LAST_MOTION # Make sure the penultimate note isn't a repeated note. penultimate_preparation = abs(contrapunctus[-2] - contrapunctus[-3]) if penultimate_preparation == 0: fitness_score -= PUNISH_REPEATED_PENULTIMATE else: # Make sure the movement to the penultimate note isn't from too # far away (not greater than a third). if penultimate_preparation < 2: fitness_score += REWARD_PENULTIMATE_PREPARATION else: fitness_score -= PUNISH_PENULTIMATE_PREPARATION # Check the fitness of the body of the solution. last_notes = (contrapunctus[0], cantus_firmus[0]) last_interval = last_notes[0] - last_notes[1] for i in range(1, len(contrapunctus) - 1): contrapunctus_note = contrapunctus[i] cantus_firmus_note = cantus_firmus[i / 4] current_notes = (contrapunctus_note, cantus_firmus_note) current_interval = contrapunctus_note - cantus_firmus_note # Punish parallel fifths or octaves. if ((current_interval == 4 or current_interval == 7) and (last_interval == 4 or last_interval == 7)): fitness_score -= PUNISH_PARALLEL_FIFTHS_OCTAVES # Check for parallel motion. if is_parallel(last_notes, current_notes): parallel_motion += 1 # Check if the melody is a repeating note. if contrapunctus_note == last_notes[0]: repeats += 1 # Check the melodic contour. contour_leap = abs(current_notes[0] - last_notes[0]) if contour_leap >= 2: jump_contour += contour_leap - 2 # Ensure dissonances are part of a step-wise movement. if i % 2 and current_interval in DISSONANCES: # The current_note is a dissonance on the third beat of a bar. # Check that both the adjacent notes are only a step away. if is_stepwise_motion(contrapunctus, i): fitness_score += REWARD_STEPWISE_MOTION else: fitness_score -= PUNISH_STEPWISE_MOTION else: if is_stepwise_motion(contrapunctus, i): fitness_score += REWARD_STEPWISE_MOTION last_notes = current_notes last_interval = current_interval # Punish too many (> 1/3) repeated notes. if repeats > repeat_threshold: fitness_score -= PUNISH_REPEATS # Punish too many (> 1/3) parallel movements. if parallel_motion > repeat_threshold: fitness_score -= PUNISH_PARALLEL # Punish too many large leaps in the melody. if jump_contour > jump_threshold: fitness_score -= PUNISH_LEAPS genome.fitness = fitness_score return fitness_score