"""
Best
====
"""
import itertools as it
from stk.molecular import Inchi
from .selector import Selector
[docs]class Best(Selector):
"""
Selects batches of molecules, highest fitness value first.
Examples
--------
*Yielding Single Molecule Batches*
Yielding molecules one at a time. For example, if molecules need
to be selected for mutation or the next generation.
.. testcode:: yielding-single-molecule-batches
import stk
# Make the selector.
best = stk.Best()
population = (
stk.MoleculeRecord(
topology_graph=stk.polymer.Linear(
building_blocks=(
stk.BuildingBlock(
smiles='BrCCBr',
functional_groups=[stk.BromoFactory()],
),
),
repeating_unit='A',
num_repeating_units=2,
),
).with_fitness_value(1),
stk.MoleculeRecord(
topology_graph=stk.polymer.Linear(
building_blocks=(
stk.BuildingBlock(
smiles='BrCCBr',
functional_groups=[stk.BromoFactory()],
),
),
repeating_unit='A',
num_repeating_units=2,
),
).with_fitness_value(2)
)
# Select the molecules.
for selected, in best.select(population):
# Do stuff with each selected molecule.
pass
*Yielding Batches Holding Multiple Molecules*
Yielding multiple molecules at once. For example, if molecules need
to be selected for crossover.
.. testcode:: yielding-batches-holding-multiple-molecules
import stk
# Make the selector.
best = stk.Best(batch_size=2)
population = (
stk.MoleculeRecord(
topology_graph=stk.polymer.Linear(
building_blocks=(
stk.BuildingBlock(
smiles='BrCCBr',
functional_groups=[stk.BromoFactory()],
),
),
repeating_unit='A',
num_repeating_units=2,
),
).with_fitness_value(1),
stk.MoleculeRecord(
topology_graph=stk.polymer.Linear(
building_blocks=(
stk.BuildingBlock(
smiles='BrCCBr',
functional_groups=[stk.BromoFactory()],
),
),
repeating_unit='A',
num_repeating_units=2,
),
).with_fitness_value(2)
)
# Select the molecules.
for selected1, selected2 in best.select(population):
# Do stuff with the selected molecules.
pass
"""
[docs] def __init__(
self,
num_batches=None,
batch_size=1,
duplicate_molecules=True,
duplicate_batches=True,
key_maker=Inchi(),
fitness_modifier=None,
):
"""
Initialize a :class:`.Best` instance.
Parameters
----------
num_batches : :class:`int`, optional
The number of batches to yield. If ``None`` then yielding
will continue forever or until the generator is exhausted,
whichever comes first.
batch_size : :class:`int`, optional
The number of molecule records yielded at once.
duplicate_molecules : :class:`bool`, optional
If ``True`` the same molecule can be yielded in more than
one batch.
duplicate_batches : :class:`bool`, optional
If ``True`` the same batch can be yielded more than once.
Duplicate batches can occur if the same molecule is found
multiple times in a population.
key_maker : :class:`.MoleculeKeyMaker`, optional
Used to get the keys of molecules. If two molecules have
the same key, they are considered duplicates.
fitness_modifier : :class:`callable`, optional
Takes the `population` on which :meth:`.select` is called
and returns a :class:`dict`, which maps records in the
`population` to the fitness values the :class:`.Selector`
should use. If ``None``, the regular fitness values of the
records are used.
"""
if fitness_modifier is None:
fitness_modifier = self._get_fitness_values
self._duplicate_molecules = duplicate_molecules
self._duplicate_batches = duplicate_batches
self._num_batches = num_batches
self._batch_size = batch_size
super().__init__(
key_maker=key_maker,
fitness_modifier=fitness_modifier,
)
def _select_from_batches(self, batches, yielded_batches):
batches = sorted(batches, reverse=True)
if not self._duplicate_molecules:
batches = filter(
yielded_batches.has_no_yielded_molecules,
batches,
)
if not self._duplicate_batches:
batches = filter(
yielded_batches.is_unyielded_batch,
batches,
)
yield from it.islice(batches, self._num_batches)