Source code for buildamol.optimizers.circulatron

"""
The Circulatron class to circularize a molecule
"""

import numpy as np
import buildamol.optimizers.base_rotatron as Rotatron
from buildamol.optimizers.distance_rotatron import DistanceRotatron

__all__ = ["Circulatron"]


[docs] class Circulatron(Rotatron.Rotatron): """ A special rotatron to circularize a molecule. This rotatron works by minimizing the distance between two target nodes in the graph in order to superimpose them. This rotatron does not itself optimize the conformation of the resulting structure but instead uses one of the other rotatrons to do so. Note ---- In order for this environment to work, it is important that the graph is NOT already circularized! Parameters ---------- graph : AtomGraph or ResidueGraph The graph to circulize target_nodes : tuple Two nodes in the graph that should be superimposed. base_rotatron : Rotatron The rotatron class to use for basic conformer evaluation. By default, this is the DistanceRotatron. rotatable_edges : list A list of edges that are rotatable in the graph. If not given, all rotatable edges will be considered. **kwargs Additional keyword arguments to pass to the base rotatron """ def __init__( self, graph, target_nodes: tuple, base_rotatron: Rotatron = DistanceRotatron, hinge_node=None, rotatable_edges: list = None, **kwargs ): hinge_node = hinge_node or target_nodes[0] if rotatable_edges is None: rotatable_edges = graph.find_rotatable_edges(hinge_node) else: rotatable_edges = graph.direct_edges(hinge_node, rotatable_edges) super().__init__(graph, rotatable_edges=rotatable_edges, **kwargs) self.target_nodes = target_nodes self.tdx1 = list(graph.nodes).index(target_nodes[0]) self.tdx2 = list(graph.nodes).index(target_nodes[1]) self.base_rotatron = base_rotatron( graph, rotatable_edges, setup=False, **kwargs ) base_rotatron.edge_masks = self.edge_masks base_rotatron.edge_lengths = self.edge_lengths self.action_space = self.base_rotatron.action_space self._bounds_tuple = self.base_rotatron._bounds_tuple
[docs] def eval(self, state): """ Evaluate the state using the base rotatron Parameters ---------- state : dict The state to evaluate Returns ------- float The energy of the state """ e = self.base_rotatron.eval(state) # now evaluate the distance between the target nodes self._target_dist = np.linalg.norm(state[self.tdx1] - state[self.tdx2]) e += self._target_dist**2 return e
[docs] def done(self, state): """ Check if the state is done Parameters ---------- state : dict The state to check Returns ------- bool Whether the state is done """ return self._target_dist < 0.1
if __name__ == "__main__": import buildamol as bam bam.load_amino_acids() mol = bam.molecule("SER") % "LINK" * 15 targets = mol.get_atom("N", residue=1), mol.get_atom("HXT", residue=-1) hinge = mol.get_atom("CA", residue=5) res_graph = mol.get_residue_graph(True) res_graph.add_edge(targets[0], targets[0].parent) res_graph.add_edge(targets[1], targets[1].parent) res_graph.add_edge(hinge, hinge.parent) atom_graph = mol.get_atom_graph() edges = mol.get_residue_connections() graph = atom_graph rotatron = Circulatron( graph, targets, hinge_node=hinge, base_rotatron=bam.DistanceRotatron, rotatable_edges=edges, unfold=4.0, ) v = mol.draw(atoms=False) v.draw_points(mol.get_coords(*targets), colors="limegreen") v.show() out = bam.optimize(mol, rotatron, algorithm="scipy") out.remove_atoms(targets[1]) out.add_bond(targets[0], mol.get_atom("OXT", residue=-1)) v += out.draw(line_color="red", atoms=False) out.to_pdb("circular.pdb") v.show()