3. Editing a real molecule (SELFIES)¶
Raw SMILES mutations can easily produce invalid molecules (unbalanced rings/branches), which is why SmilesGenAlgSolver’s assembler has to validate-and-retry on every mutation. SELFIES tokens are constructed so that every mutation decodes to a valid molecule, making SelfiesGenAlgSolver the better default for open-ended molecule editing.
Starting from a real molecule and freezing it¶
This example starts from melatonin, appends genes for the GA to fill in, and optimizes logP while never touching the original scaffold:
from selfies import encoder
from navicatGA.selfies_solver import SelfiesGenAlgSolver
from navicatGA.fitness_functions_selfies import fitness_function_selfies
from navicatGA.chemistry_selfies import count_selfie_chars
from navicatGA.wrappers_selfies import sc2smiles
starting_smiles = "CC(=O)NCCc1c[nH]c2c1cc(cc2)OC" # melatonin
starting_selfies = [encoder(starting_smiles)]
n_starting_genes = count_selfie_chars(starting_selfies[0]) # 25
solver = SelfiesGenAlgSolver(
starting_selfies=starting_selfies,
n_genes=int(n_starting_genes * 2), # room to grow past the scaffold
excluded_genes=list(range(n_starting_genes)), # freeze the scaffold's own genes
fitness_function=fitness_function_selfies(1), # 1 = logP; see below for the full list
max_gen=10,
pop_size=10,
random_state=42,
to_file=False,
verbose=False,
)
result = solver.solve()
print(result.best_fitness) # 8.506...
print(sc2smiles(result.best_individual)) # the resulting SMILES
Bundled fitness functions¶
navicatGA.fitness_functions_selfies.fitness_function_selfies(function_number) is a factory: numbers 1 to 9 select logP, inverse logP, molecular weight, negative MW, MW×inverse-logP, molecular volume, HOMO energy, LUMO energy, HOMO-LUMO gap respectively (see the module docstring/source for the exact mapping). Numbers 7 to 9 need pyscf (imported lazily, so 1 to 6 work without it). fitness_function_target_property(target, function_number, score_modifier_number) scores how close a property is to a target value instead of maximizing it outright; referencing it with arguments from a YAML config works the same way as fitness_function_selfies(1) above, see Configuration Templates.
For anything else, write your own fn(chromosome) -> float and pass it as fitness_function directly. The numbered functions are examples, not a closed API.
Other SelfiesGenAlgSolver-specific params¶
starting_stoned: generate a chemical subspace around a single starting molecule via the STONED method, instead of a fixed/random starting population. Incompatible withstarting_random; needs exactly onestarting_selfiesentry.branching: add branch tokens to the alphabet so mutations can grow new branches, not just substitute atoms.variables_limits: ifTrue, sets SELFIES semantic constraints (valence rules) on the alphabet.
The rest (alphabet_list, excluded_genes, multi_alphabet/equivalences, max_counter, GA-mechanics params) work exactly as in Tutorial 2, since both solvers share the same AlphabetGenAlgSolver base.