Getting started using Lomap#

Lomap plans networks of relative free energy calculations across a set of ligands. Its interface provides gufe bindings, so Lomap’s atom mapping, scoring, and network planning interoperate with the rest of the Open Free Energy ecosystem. The workflow is: load your ligands, propose atom mappings between them, score those mappings, and assemble them into a network. Each step is available on its own, but most users only need generate_lomap_network(), covered at the end of this guide.

These bindings require the optional gufe dependency (see Installation).

All examples below use these two example ligands:

import importlib.resources

from gufe import SmallMoleculeComponent

data = importlib.resources.files("lomap.tests.data")
ligands = [
    SmallMoleculeComponent.from_sdf_file(data / name)
    for name in ["lig_41.sdf", "lig_74.sdf"]
]

Generating mappings#

A LomapAtomMapper proposes an atom mapping between a pair of ligands. suggest_mappings yields LigandAtomMapping objects, or nothing if the ligands share no suitable common substructure.

from lomap import LomapAtomMapper

mapper = LomapAtomMapper()
# suggest_mappings is a generator, so wrap it in list() to pull out all the possible mappings
mappings = list(mapper.suggest_mappings(ligands[0], ligands[1]))

mapping = mappings[0]

# atom indices in ligand A mapped onto the corresponding atoms in ligand B
print(mapping.componentA_to_componentB)

The mapper can be tuned with options such as time, threed, max3d, element_change, seed, and shift. See the API reference for details.

Generating scores#

A scorer takes a LigandAtomMapping and returns a value from 0.0 (worst) to 1.0 (best). default_lomap_score() is the standard Lomap score, a product of sub-scores penalizing changes such as broken rings, altered ring sizes, and net-charge differences.

from lomap import default_lomap_score

score = default_lomap_score(mapping)
print(f"{score:.3f}")

The score of 0.095 for this pair is expected. lig_41 and lig_74 differ at both ends; fused ring system is created on one end and the heteroaryl head changes, leaving a relatively small shared core, along with element and hybridization changes.

The sub-scores live in lomap.gufe_bindings.scorers if you need a custom scorer.

Generating networks#

generate_lomap_network() combines the previous steps. Given a set of ligands, a mapper, and a scorer, it proposes and scores edges and assembles a connected LigandNetwork.

from lomap import LomapAtomMapper, default_lomap_score, generate_lomap_network

network = generate_lomap_network(
    ligands=ligands,
    mappers=LomapAtomMapper(),
    scorer=default_lomap_score,
)

print(f"{len(network.nodes)} ligands, {len(network.edges)} edges")

network.nodes are the ligands and network.edges the scored mappings. Options such as distance_cutoff, require_cycle_covering, and radial are documented in the API reference.

Command line interface#

The legacy lomap command-line tool is deprecated and will be removed in the next major release; use generate_lomap_network() instead. See Lomap Legacy API.