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 :func:`~lomap.generate_lomap_network`, covered at the end of this guide. These bindings require the optional ``gufe`` dependency (see :doc:`installation`). All examples below use these two example ligands: .. code-block:: python 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 :class:`~lomap.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. .. code-block:: python 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 :ref:`API reference ` for details. Generating scores ----------------- A scorer takes a ``LigandAtomMapping`` and returns a value from ``0.0`` (worst) to ``1.0`` (best). :func:`~lomap.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. .. code-block:: python 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 ------------------- :func:`~lomap.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``. .. code-block:: python 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 :ref:`API reference `. Command line interface ---------------------- The legacy ``lomap`` command-line tool is deprecated and will be removed in the next major release; use :func:`~lomap.generate_lomap_network` instead. See :doc:`legacy`.