Ejemplo n.º 1
0
    def to_graph(self):
        """
        Convert molecule to graph.

        Returns
        -------
        Graph
            Molecular graph.

        Notes
        -----
        If the molecule does not have an associated adjacency matrix, a simple
        bond perception is used.

        The molecular graph is cached.
        """
        if self.G is None:
            try:
                self.G = graph.graph_from_adjacency_matrix(
                    self.adjacency_matrix, self.atomicnums)
            except AttributeError:
                warnings.warn(
                    "Molecule was not initialized with an adjacency matrix. " +
                    "Using bond perception...")

                # Automatic bond perception (with very simple rule)
                self.adjacency_matrix = graph.adjacency_matrix_from_atomic_coordinates(
                    self.atomicnums, self.coordinates)

                self.G = graph.graph_from_adjacency_matrix(
                    self.adjacency_matrix, self.atomicnums)

        return self.G
Ejemplo n.º 2
0
def test_adjacency_matrix_from_atomic_coordinates(mol: molecule.Molecule,
                                                  n_bonds: int) -> None:

    A = graph.adjacency_matrix_from_atomic_coordinates(mol.atomicnums,
                                                       mol.coordinates)

    G = graph.graph_from_adjacency_matrix(A)

    assert graph.num_vertices(G) == len(mol)
    assert graph.num_edges(G) == n_bonds
Ejemplo n.º 3
0
def test_build_graph_node_features_unsupported() -> None:
    pytest.importorskip(
        "graph_tool",
        reason="NetworkX supports all Python objects as node properties.")

    A = np.array([[0, 1, 1], [1, 0, 0], [1, 0, 1]])

    property = [True, False, True]

    with pytest.raises(ValueError, match="Unsupported property type:"):
        _ = graph.graph_from_adjacency_matrix(A, property)
Ejemplo n.º 4
0
def test_graph_from_adjacency_matrix(mol) -> None:

    natoms = io.numatoms(mol)
    nbonds = io.numbonds(mol)

    A = io.adjacency_matrix(mol)

    assert A.shape == (natoms, natoms)
    assert np.alltrue(A == A.T)
    assert np.sum(A) == nbonds * 2

    G = graph.graph_from_adjacency_matrix(A)

    assert graph.num_vertices(G) == natoms
    assert graph.num_edges(G) == nbonds
Ejemplo n.º 5
0
def test_adjacency_matrix_from_atomic_coordinates_distance() -> None:
    # Lithium hydride (LiH)
    # H and Li have very different covalent radii
    atomicnums = np.array([1, 3])

    # Distance is the sum of covalent radii
    d = sum([constants.anum_to_covalentradius[anum] for anum in atomicnums])

    # Distance between two atoms is barely enough to create a bond
    # If the covalent radii are not correct, no bond will be created
    coordinates = np.array(
        [[0, 0, 0], [0, 0, d + constants.connectivity_tolerance - 0.01]])

    A = graph.adjacency_matrix_from_atomic_coordinates(atomicnums, coordinates)
    G = graph.graph_from_adjacency_matrix(A)

    assert graph.num_edges(G) == 1
Ejemplo n.º 6
0
def test_build_graph_node_features(property) -> None:
    A = np.array([[0, 1, 1], [1, 0, 0], [1, 0, 1]])
    G = graph.graph_from_adjacency_matrix(A, property)

    assert graph.num_edges(G) == 3
Ejemplo n.º 7
0
def _rmsd_isomorphic_core(
    coords1: np.ndarray,
    coords2: np.ndarray,
    aprops1: np.ndarray,
    aprops2: np.ndarray,
    am1: np.ndarray,
    am2: np.ndarray,
    center: bool = False,
    minimize: bool = False,
    isomorphisms: Optional[List[Tuple[List[int], List[int]]]] = None,
    atol: float = 1e-9,
) -> Tuple[float, List[Tuple[List[int], List[int]]]]:
    """
    Compute RMSD using graph isomorphism.

    Parameters
    ----------
    coords1: np.ndarray
        Coordinate of molecule 1
    coords2: np.ndarray
        Coordinates of molecule 2
    aprops1: np.ndarray
        Atomic properties for molecule 1
    aprops2: np.ndarray
        Atomic properties for molecule 2
    am1: np.ndarray
        Adjacency matrix for molecule 1
    am2: np.ndarray
        Adjacency matrix for molecule 2
    center: bool
        Centering flag
    minimize: bool
        Compute minized RMSD
    isomorphisms: Optional[List[Dict[int,int]]]
        Previously computed graph isomorphism
    atol: float
        Absolute tolerance parameter for QCP (see :func:`qcp_rmsd`)

    Returns
    -------
    Tuple[float, List[Dict[int, int]]]
        RMSD (after graph matching) and graph isomorphisms
    """

    assert coords1.shape == coords2.shape

    n = coords1.shape[0]

    # Center coordinates if required
    c1 = utils.center(coords1) if center or minimize else coords1
    c2 = utils.center(coords2) if center or minimize else coords2

    # No cached isomorphisms
    if isomorphisms is None:
        # Convert molecules to graphs
        G1 = graph.graph_from_adjacency_matrix(am1, aprops1)
        G2 = graph.graph_from_adjacency_matrix(am2, aprops2)

        # Get all the possible graph isomorphisms
        isomorphisms = graph.match_graphs(G1, G2)

    # Minimum result
    # Squared displacement (not minimize) or RMSD (minimize)
    min_result = np.inf

    # Loop over all graph isomorphisms to find the lowest RMSD
    for idx1, idx2 in isomorphisms:

        # Use the isomorphism to shuffle coordinates around (from original order)
        c1i = c1[idx1, :]
        c2i = c2[idx2, :]

        if not minimize:
            # Compute square displacement
            # Avoid dividing by n and an expensive sqrt() operation
            result = np.sum((c1i - c2i)**2)
        else:
            # Compute minimized RMSD using QCP
            result = qcp.qcp_rmsd(c1i, c2i, atol)

        min_result = result if result < min_result else min_result

    if not minimize:
        # Compute actual RMSD from square displacement
        min_result = np.sqrt(min_result / n)

    # Return the actual RMSD
    return min_result, isomorphisms