Progress Plotter

class ProgressPlotter(generations, get_property, y_label, filter=<function ProgressPlotter.<lambda>>)[source]

Bases: object

Plots how a property changes during an EA run.

The produced plot will show the EA generations on the x axis and the min, mean and max values of an attribute on the y axis.

Examples

Plotting How Fitness Values Change Across Generations

import stk

# Initialize an EA somehow.
ea = stk.EvolutionaryAlgorithm(
    initial_population=(
        stk.MoleculeRecord(
            topology_graph=stk.polymer.Linear(
                building_blocks=(
                    stk.BuildingBlock(
                        smiles='BrCCBr',
                        functional_groups=[stk.BromoFactory()],
                    ),
                ),
                repeating_unit='A',
                num_repeating_units=i,
            ),
        )
        for i in range(2, 22)
    ),
    fitness_calculator=stk.FitnessFunction(
        fitness_function=lambda molecule:
            molecule.get_num_atoms(),
    ),
    mutator=stk.RandomBuildingBlock(
        building_blocks=(
            stk.BuildingBlock(
                smiles='BrC[Si]CCBr',
                functional_groups=[stk.BromoFactory()],
            ),
            stk.BuildingBlock(
                smiles='BrCCCCCCCBr',
                functional_groups=[stk.BromoFactory()],
            ),
        ),
        is_replaceable=lambda building_block: True
    ),
    crosser=stk.GeneticRecombination(
        get_gene=lambda building_block: 0
    ),
    generation_selector=stk.Best(
        num_batches=22,
        duplicate_molecules=False,
    ),
    mutation_selector=stk.Roulette(
        num_batches=5,
        random_seed=10,
    ),
    crossover_selector=stk.Roulette(
        num_batches=5,
        batch_size=2,
        random_seed=10,
    ),
    num_processes=1,
)

generations = []
for generation in ea.get_generations(10):
    generations.append(generation)

# Make the plotter which plots the fitness change across
# generations.
progress = stk.ProgressPlotter(
    generations=generations,
    get_property=lambda record: record.get_fitness_value(),
    y_label='Fitness'
)
progress.write('fitness_plot.png')

Plotting How a Molecular Property Changes Across Generations

As an example, plotting how the number of atoms changes across generations

import stk

# Initialize an EA somehow.
ea = stk.EvolutionaryAlgorithm(
    initial_population=(
        stk.MoleculeRecord(
            topology_graph=stk.polymer.Linear(
                building_blocks=(
                    stk.BuildingBlock(
                        smiles='BrCCBr',
                        functional_groups=[stk.BromoFactory()],
                    ),
                ),
                repeating_unit='A',
                num_repeating_units=i,
            ),
        )
        for i in range(2, 22)
    ),
    fitness_calculator=stk.FitnessFunction(
        fitness_function=lambda molecule:
            molecule.get_num_atoms(),
    ),
    mutator=stk.RandomBuildingBlock(
        building_blocks=(
            stk.BuildingBlock(
                smiles='BrC[Si]CCBr',
                functional_groups=[stk.BromoFactory()],
            ),
            stk.BuildingBlock(
                smiles='BrCCCCCCCBr',
                functional_groups=[stk.BromoFactory()],
            ),
        ),
        is_replaceable=lambda building_block: True
    ),
    crosser=stk.GeneticRecombination(
        get_gene=lambda building_block: 0
    ),
    generation_selector=stk.Best(
        num_batches=22,
        duplicate_molecules=False,
    ),
    mutation_selector=stk.Roulette(
        num_batches=5,
        random_seed=10,
    ),
    crossover_selector=stk.Roulette(
        num_batches=5,
        batch_size=2,
        random_seed=10,
    ),
    num_processes=1,
)

generations = []
for generation in ea.get_generations(10):
    generations.append(generation)

# Make the plotter which plots the number of atoms across
# generations.
progress = stk.ProgressPlotter(
    generations=generations,
    get_property=lambda record:
        record.get_molecule().get_num_atoms(),
    y_label='Number of Atoms'
)
progress.write('number_of_atoms_plot.png')

Excluding Molecules From the Plot

Sometimes, you want to ignore some molecules from the plot you make. For example, If the fitness calculation failed on a molecule, you not want to include in a plot of fitness.

import stk

# Initialize an EA somehow.
ea = stk.EvolutionaryAlgorithm(
    initial_population=(
        stk.MoleculeRecord(
            topology_graph=stk.polymer.Linear(
                building_blocks=(
                    stk.BuildingBlock(
                        smiles='BrCCBr',
                        functional_groups=[stk.BromoFactory()],
                    ),
                ),
                repeating_unit='A',
                num_repeating_units=i,
            ),
        )
        for i in range(2, 22)
    ),
    fitness_calculator=stk.FitnessFunction(
        fitness_function=lambda molecule:
            molecule.get_num_atoms(),
    ),
    mutator=stk.RandomBuildingBlock(
        building_blocks=(
            stk.BuildingBlock(
                smiles='BrC[Si]CCBr',
                functional_groups=[stk.BromoFactory()],
            ),
            stk.BuildingBlock(
                smiles='BrCCCCCCCBr',
                functional_groups=[stk.BromoFactory()],
            ),
        ),
        is_replaceable=lambda building_block: True
    ),
    crosser=stk.GeneticRecombination(
        get_gene=lambda building_block: 0
    ),
    generation_selector=stk.Best(
        num_batches=22,
        duplicate_molecules=False,
    ),
    mutation_selector=stk.Roulette(
        num_batches=5,
        random_seed=10,
    ),
    crossover_selector=stk.Roulette(
        num_batches=5,
        batch_size=2,
        random_seed=10,
    ),
    num_processes=1,
)

generations = []
for generation in ea.get_generations(10):
    generations.append(generation)

# Make the plotter which plots the fitness change across
# generations.
progress = stk.ProgressPlotter(
    generations=generations,
    get_property=lambda record: record.get_fitness_value(),
    y_label='Fitness',
    # Only plot records whose unnormalized fitness value is not
    # None, which means the fitness calculation did not fail.
    filter=lambda record:
        record.get_fitness_value(normalized=False) is not None,
)
progress.write('fitness_plot.png')

Methods

get_plot_data()

Get the plot data.

write(path[, dpi])

Write a progress plot to a file.

__init__(generations, get_property, y_label, filter=<function ProgressPlotter.<lambda>>)[source]

Initialize a ProgressPlotter instance.

Parameters:
  • generations (iterable of Generation) – The generations of the EA, which are plotted.

  • get_property (callable) – A callable which takes a MoleculeRecord and returns a property value of that molecule, which is used for the plot. The callable must return a valid value for each MoleculeRecord in generations.

  • y_label (str) – The y label for the produced graph.

  • filter (callable, optional) – Takes an MoleculeRecord and returns True or False. Only records which return True are included in the plot. By default, all records will be plotted.

get_plot_data()[source]

Get the plot data.

Returns:

A data frame holding the plot data.

Return type:

pandas.DataFrame

write(path, dpi=500)[source]

Write a progress plot to a file.

Parameters:
  • path (str) – The path into which the plot is written.

  • dpi (int, optional) – The dpi of the image.

Returns:

The plotter is returned.

Return type:

ProgressPlotter