biotite.structure.BondList

class biotite.structure.BondList(atom_count, bonds=None)[source]

Bases: Copyable

A bond list stores indices of atoms (usually of an AtomArray or AtomArrayStack) that form chemical bonds together with the type (or order) of the bond.

Internally the bonds are stored as n x 3 ndarray. For each row, the first column specifies the index of the first atom, the second column the index of the second atom involved in the bond. The third column stores an integer that is interpreted as member of the the BondType enum, that specifies the order of the bond.

When indexing a BondList, the index is not forwarded to the internal ndarray. Instead the indexing behavior is consistent with indexing an AtomArray or AtomArrayStack: Bonds with at least one atom index that is not covered by the index are removed, atom indices that occur after an uncovered atom index move up. Effectively, this means that after indexing an AtomArray and a BondList with the same index, the atom indices in the BondList will still point to the same atoms in the AtomArray. Indexing a BondList with a single integer is equivalent to calling get_bonds().

The same consistency applies to adding BondList instances via the ‘+’ operator: The atom indices of the second BondList are increased by the atom count of the first BondList and then both BondList objects are merged.

Parameters
atom_countint

A positive integer, that specifies the number of atoms the BondList refers to (usually the length of an atom array (stack)). Effectively, this value is the exclusive maximum for the indices stored in the BondList.

bondsndarray, shape=(n,2) or shape=(n,3), dtype=int, optional

This array contains the indices of atoms which are bonded: For each row, the first column specifies the first atom, the second row the second atom involved in a chemical bond. If an n x 3 array is provided, the additional column specifies a BondType instead of BondType.ANY. By default, the created BondList is empty.

Notes

When initially providing the bonds as ndarray, the input is sanitized: Redundant bonds are removed, and each bond entry is sorted so that the lower one of the two atom indices is in the first column. If a bond appears multiple times with different bond types, the first bond takes precedence.

Examples

Construct a BondList, where a central atom (index 1) is connected to three other atoms (index 0, 3 and 4):

>>> bond_list = BondList(5, np.array([(1,0),(1,3),(1,4)]))
>>> print(bond_list)
[[0 1 0]
 [1 3 0]
 [1 4 0]]

Remove the first atom (index 0) via indexing: The bond containing index 0 is removed, since the corresponding atom does not exist anymore. Since all other atoms move up in their position, the indices in the bond list are decreased by one:

>>> bond_list = bond_list[1:]
>>> print(bond_list)
[[0 2 0]
 [0 3 0]]

BondList objects can be associated to an AtomArray or AtomArrayStack. The following snippet shows this for a benzene molecule:

>>> benzene = AtomArray(12)
>>> # Omit filling most required annotation categories for brevity
>>> benzene.atom_name = np.array(
...     ["C1", "C2", "C3", "C4", "C5", "C6", "H1", "H2", "H3", "H4", "H5", "H6"]
... )
>>> benzene.bonds = BondList(
...     benzene.array_length(),
...     np.array([
...         # Bonds between carbon atoms in the ring
...         (0,  1, BondType.AROMATIC_SINGLE),
...         (1,  2, BondType.AROMATIC_DOUBLE),
...         (2,  3, BondType.AROMATIC_SINGLE),
...         (3,  4, BondType.AROMATIC_DOUBLE),
...         (4,  5, BondType.AROMATIC_SINGLE),
...         (5,  0, BondType.AROMATIC_DOUBLE),
...         # Bonds between carbon and hydrogen
...         (0,  6, BondType.SINGLE),
...         (1,  7, BondType.SINGLE),
...         (2,  8, BondType.SINGLE),
...         (3,  9, BondType.SINGLE),
...         (4, 10, BondType.SINGLE),
...         (5, 11, BondType.SINGLE),
...     ])
... )
>>> for i, j, bond_type in benzene.bonds.as_array():
...     print(
...         f"{str(BondType(bond_type))} bond between "
...         f"{benzene.atom_name[i]} and {benzene.atom_name[j]}"
...     )
BondType.AROMATIC_SINGLE bond between C1 and C2
BondType.AROMATIC_DOUBLE bond between C2 and C3
BondType.AROMATIC_SINGLE bond between C3 and C4
BondType.AROMATIC_DOUBLE bond between C4 and C5
BondType.AROMATIC_SINGLE bond between C5 and C6
BondType.AROMATIC_DOUBLE bond between C1 and C6
BondType.SINGLE bond between C1 and H1
BondType.SINGLE bond between C2 and H2
BondType.SINGLE bond between C3 and H3
BondType.SINGLE bond between C4 and H4
BondType.SINGLE bond between C5 and H5
BondType.SINGLE bond between C6 and H6

Obtain the bonded atoms for the \(C_1\):

>>> bonds, types = benzene.bonds.get_bonds(0)
>>> print(bonds)
[1 5 6]
>>> print(types)
[5 6 1]
>>> print(f"C1 is bonded to {', '.join(benzene.atom_name[bonds])}")
C1 is bonded to C2, C6, H1

Cut the benzene molecule in half. Although the first half of the atoms are missing the indices of the cropped BondList still represents the bonds of the remaining atoms:

>>> half_benzene = benzene[
...     np.isin(benzene.atom_name, ["C4", "C5", "C6", "H4", "H5", "H6"])
... ]
>>> for i, j, bond_type in half_benzene.bonds.as_array():
...     print(
...         f"{str(BondType(bond_type))} bond between "
...         f"{half_benzene.atom_name[i]} and {half_benzene.atom_name[j]}"
...     )
BondType.AROMATIC_DOUBLE bond between C4 and C5
BondType.AROMATIC_SINGLE bond between C5 and C6
BondType.SINGLE bond between C4 and H4
BondType.SINGLE bond between C5 and H5
BondType.SINGLE bond between C6 and H6
add_bond(atom_index1, atom_index2, bond_type=BondType.ANY)

Add a bond to the BondList.

If the bond is already existent, only the bond type is updated.

Parameters
atom_index1, atom_index2int

The indices of the atoms to create a bond for.

bond_typeBondType or int, optional

The type of the bond. Default is BondType.ANY.

adjacency_matrix(bond_list)

Represent this BondList as adjacency matrix.

The adjacency matrix is a quadratic matrix with boolean values according to

\[\begin{split}M_{i,j} = \begin{cases} \text{True}, & \text{if } \text{Atom}_i \text{ and } \text{Atom}_j \text{ form a bond} \\ \text{False}, & \text{otherwise} \end{cases}.\end{split}\]
Returns
matrixndarray, dtype=bool, shape=(n,n)

The created adjacency matrix.

Examples

>>> # BondList for formaldehyde
>>> bond_list = BondList(
...     4,
...     np.array([
...         # Bond between carbon and oxygen
...         (0,  1, BondType.DOUBLE),
...         # Bonds between carbon and hydrogen
...         (0,  2, BondType.SINGLE),
...         (0,  3, BondType.SINGLE),
...     ])
... )
>>> print(bond_list.adjacency_matrix())
[[False  True  True  True]
 [ True False False False]
 [ True False False False]
 [ True False False False]]
as_array()

Obtain a copy of the internal ndarray.

Returns
arrayndarray, shape=(n,3), dtype=np.uint32

Copy of the internal ndarray. For each row, the first column specifies the index of the first atom, the second column the index of the second atom involved in the bond. The third column stores the BondType.

as_graph()

Obtain a graph representation of the BondList.

Returns
bond_setGraph

A NetworkX Graph. The atom indices are nodes, the bonds are edges. Each edge has a "bond_type" attribute containing the BondType.

Examples

>>> bond_list = BondList(5, np.array([(1,0,2), (1,3,1), (1,4,1)]))
>>> graph = bond_list.as_graph()
>>> print(graph.nodes)
[0, 1, 3, 4]
>>> print(graph.edges)
[(0, 1), (1, 3), (1, 4)]
>>> for i, j in graph.edges:
...     print(i, j, graph.get_edge_data(i, j))
0 1 {'bond_type': <BondType.DOUBLE: 2>}
1 3 {'bond_type': <BondType.SINGLE: 1>}
1 4 {'bond_type': <BondType.SINGLE: 1>}
as_set()

Obtain a set representation of the BondList.

Returns
bond_setset of tuple(int, int, int)

A set of tuples. Each tuple represents one bond: The first integer represents the first atom, the second integer represents the second atom, the third integer represents the BondType.

bond_type_matrix(bond_list)

Represent this BondList as a matrix depicting the bond type.

The matrix is a quadratic matrix:

\[\begin{split}M_{i,j} = \begin{cases} \text{BondType}_{ij}, & \text{if } \text{Atom}_i \text{ and } \text{Atom}_j \text{ form a bond} \\ -1, & \text{otherwise} \end{cases}.\end{split}\]
Returns
matrixndarray, dtype=bool, shape=(n,n)

The created bond type matrix.

Examples

>>> # BondList for formaldehyde
>>> bond_list = BondList(
...     4,
...     np.array([
...         # Bond between carbon and oxygen
...         (0,  1, BondType.DOUBLE),
...         # Bonds between carbon and hydrogen
...         (0,  2, BondType.SINGLE),
...         (0,  3, BondType.SINGLE),
...     ])
... )
>>> print(bond_list.bond_type_matrix())
[[-1  2  1  1]
 [ 2 -1 -1 -1]
 [ 1 -1 -1 -1]
 [ 1 -1 -1 -1]]
copy()

Create a deep copy of this object.

Returns
copy

A copy of this object.

get_all_bonds()

For each atom index, give the indices of the atoms bonded to this atom as well as the corresponding bond types.

Returns
bondsnp.ndarray, dtype=np.uint32, shape=(n,k)

The indices of connected atoms. The first dimension represents the atoms, the second dimension represents the indices of atoms bonded to the respective atom. Atoms can have have different numbers of atoms bonded to them. Therefore, the length of the second dimension k is equal to the maximum number of bonds for an atom in this BondList. For atoms with less bonds, the corresponding entry in the array is padded with -1 values.

bond_typesnp.ndarray, dtype=np.uint32, shape=(n,k)

Array of integers, interpreted as BondType instances. This array specifies the bond type (or order) corresponding to the returned bonds. It uses the same -1-padding.

Examples

>>> # BondList for benzene
>>> bond_list = BondList(
...     12,
...     np.array([
...         # Bonds between the carbon atoms in the ring
...         (0,  1, BondType.AROMATIC_SINGLE),
...         (1,  2, BondType.AROMATIC_DOUBLE),
...         (2,  3, BondType.AROMATIC_SINGLE),
...         (3,  4, BondType.AROMATIC_DOUBLE),
...         (4,  5, BondType.AROMATIC_SINGLE),
...         (5,  0, BondType.AROMATIC_DOUBLE),
...         # Bonds between carbon and hydrogen
...         (0,  6, BondType.SINGLE),
...         (1,  7, BondType.SINGLE),
...         (2,  8, BondType.SINGLE),
...         (3,  9, BondType.SINGLE),
...         (4, 10, BondType.SINGLE),
...         (5, 11, BondType.SINGLE),
...     ])
... )
>>> bonds, types = bond_list.get_all_bonds()
>>> print(bonds)
[[ 1  5  6]
 [ 0  2  7]
 [ 1  3  8]
 [ 2  4  9]
 [ 3  5 10]
 [ 4  0 11]
 [ 0 -1 -1]
 [ 1 -1 -1]
 [ 2 -1 -1]
 [ 3 -1 -1]
 [ 4 -1 -1]
 [ 5 -1 -1]]
>>> print(types)
[[ 5  6  1]
 [ 5  6  1]
 [ 6  5  1]
 [ 5  6  1]
 [ 6  5  1]
 [ 5  6  1]
 [ 1 -1 -1]
 [ 1 -1 -1]
 [ 1 -1 -1]
 [ 1 -1 -1]
 [ 1 -1 -1]
 [ 1 -1 -1]]
>>> for i in range(bond_list.get_atom_count()):
...     bonds_for_atom = bonds[i]
...     # Remove trailing '-1' values
...     bonds_for_atom = bonds_for_atom[bonds_for_atom != -1]
...     print(f"{i}: {bonds_for_atom}")
0: [1 5 6]
1: [0 2 7]
2: [1 3 8]
3: [2 4 9]
4: [ 3  5 10]
5: [ 4  0 11]
6: [0]
7: [1]
8: [2]
9: [3]
10: [4]
11: [5]
get_atom_count()

Get the atom count.

Returns
atom_countint

The atom count.

get_bond_count()

Get the amount of bonds.

Returns
bond_countint

The amount of bonds. This is equal to the length of the internal ndarray containing the bonds.

get_bonds(atom_index)

Obtain the indices of the atoms bonded to the atom with the given index as well as the corresponding bond types.

Parameters
atom_indexint

The index of the atom to get the bonds for.

Returns
bondsnp.ndarray, dtype=np.uint32, shape=(k,)

The indices of connected atoms.

bond_typesnp.ndarray, dtype=np.uint8, shape=(k,)

Array of integers, interpreted as BondType instances. This array specifies the type (or order) of the bonds to the connected atoms.

Examples

>>> bond_list = BondList(5, np.array([(1,0),(1,3),(1,4)]))
>>> bonds, types = bond_list.get_bonds(1)
>>> print(bonds)
[0 3 4]
merge(bond_list)

Merge another BondList with this instance into a new object. If a bond appears in both BondList’s, the BondType from the given bond_list takes precedence.

The internal ndarray instances containg the bonds are simply concatenated and the new atom count is the maximum of both bond lists.

Parameters
bond_listBondList

This bond list is merged with this instance.

Returns
bond_listBondList

The merged BondList.

Notes

This is not equal to using the + operator.

Examples

>>> bond_list1 = BondList(3, np.array([(0,1),(1,2)]))
>>> bond_list2 = BondList(5, np.array([(2,3),(3,4)]))
>>> merged_list = bond_list2.merge(bond_list1)
>>> print(merged_list.get_atom_count())
5
>>> print(merged_list)
[[0 1 0]
 [1 2 0]
 [2 3 0]
 [3 4 0]]

The BondList given as parameter takes precedence:

>>> # Specifiy bond type to see where a bond is taken from
>>> bond_list1 = BondList(4, np.array([
...     (0, 1, BondType.SINGLE),
...     (1, 2, BondType.SINGLE)
... ]))
>>> bond_list2 = BondList(4, np.array([
...     (1, 2, BondType.DOUBLE),    # This one is a duplicate
...     (2, 3, BondType.DOUBLE)
... ]))
>>> merged_list = bond_list2.merge(bond_list1)
>>> print(merged_list)
[[0 1 1]
 [1 2 1]
 [2 3 2]]
offset_indices(offset)

Increase all atom indices in the BondList by the given offset.

Implicitly this increases the atom count.

Parameters
offsetint

The atom indices are increased by this value. Must be positive.

Examples

>>> bond_list = BondList(5, np.array([(1,0),(1,3),(1,4)]))
>>> print(bond_list)
[[0 1 0]
 [1 3 0]
 [1 4 0]]
>>> bond_list.offset_indices(2)
>>> print(bond_list)
[[2 3 0]
 [3 5 0]
 [3 6 0]]
remove_aromaticity()

Remove aromaticity from the bond types.

BondType.AROMATIC_{ORDER} is converted into BondType.{ORDER}.

Examples

>>> bond_list = BondList(3)
>>> bond_list.add_bond(0, 1, BondType.AROMATIC_SINGLE)
>>> bond_list.add_bond(1, 2, BondType.AROMATIC_DOUBLE)
>>> bond_list.remove_aromaticity()
>>> for i, j, bond_type in bond_list.as_array():
...     print(i, j, BondType(bond_type))
0 1 BondType.SINGLE
1 2 BondType.DOUBLE
remove_bond(atom_index1, atom_index2)

Remove a bond from the BondList.

If the bond is not existent in the BondList, nothing happens.

Parameters
atom_index1, atom_index2int

The indices of the atoms whose bond should be removed.

remove_bond_order()

Convert all bonds to BondType.ANY.

remove_bonds(bond_list)

Remove multiple bonds from the BondList.

All bonds present in bond_list are removed from this instance. If a bond is not existent in this instance, nothing happens. Only the bond indices, not the bond types, are relevant for this.

Parameters
bond_listBondList

The bonds in bond_list are removed from this instance.

remove_bonds_to(self, atom_index)

Remove all bonds from the BondList where the given atom is involved.

Parameters
atom_indexint

The index of the atom whose bonds should be removed.