From 66f38cbf53a2353fe36e81fb41750d8847dbe96d Mon Sep 17 00:00:00 2001 From: Linford24 Date: Fri, 29 Aug 2025 01:48:37 +0000 Subject: [PATCH 1/3] Update bonds.pyx. Added the check for an atom bonding to itself --- src/biotite/structure/bonds.pyx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/biotite/structure/bonds.pyx b/src/biotite/structure/bonds.pyx index 8a92ecd9e..db82c8f57 100644 --- a/src/biotite/structure/bonds.pyx +++ b/src/biotite/structure/bonds.pyx @@ -279,6 +279,9 @@ class BondList(Copyable): # Input contains bonds (index 0 and 1) # including the bond type value (index 2) # Bond indices: + for bond_a, bond_b, _ in bonds: + if bond_a == bond_b: + raise ValueError(f"Atom {bond_a} cannot bind to itself {bond_b}") self._bonds[:,:2] = np.sort( # Indices are sorted per bond # so that the lower index is at the first position @@ -294,6 +297,9 @@ class BondList(Copyable): # Indices are sorted per bond # so that the lower index is at the first position elif bonds.shape[1] == 2: + for bond_a, bond_b in bonds: + if bond_a == bond_b: + raise ValueError(f"Atom {bond_a} cannot bind to itself {bond_b}") # Input contains the bonds without bond type # -> Default: Set bond type ANY (0) self._bonds[:,:2] = np.sort( @@ -937,6 +943,9 @@ class BondList(Copyable): if bond_type >= len(BondType): raise ValueError(f"BondType {bond_type} is invalid") + if atom_index1 == atom_index2: + raise ValueError("Atom indices must be different because the same atom cannot be bonded to itself") + cdef uint32 index1 = _to_positive_index(atom_index1, self._atom_count) cdef uint32 index2 = _to_positive_index(atom_index2, self._atom_count) _sort(&index1, &index2) From c90a3ce45e1cdb99ed5483575c21ed64f90f0486 Mon Sep 17 00:00:00 2001 From: Linford24 Date: Fri, 12 Sep 2025 16:54:51 +0000 Subject: [PATCH 2/3] Updated check for to disallow redundant bonds to use memoryview instead of directly iterating over bonds in pure Python. --- src/biotite/structure/bonds.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biotite/structure/bonds.pyx b/src/biotite/structure/bonds.pyx index db82c8f57..57dfc50b5 100644 --- a/src/biotite/structure/bonds.pyx +++ b/src/biotite/structure/bonds.pyx @@ -269,7 +269,7 @@ class BondList(Copyable): def __init__(self, uint32 atom_count, np.ndarray bonds=None): self._atom_count = atom_count - + bonds = memoryview(bonds) if bonds is not None and len(bonds) > 0: if bonds.ndim != 2: raise ValueError("Expected a 2D-ndarray for input bonds") From cf0443de54f39136b790ebbb62a38bda88abca88 Mon Sep 17 00:00:00 2001 From: Linford24 Date: Wed, 15 Oct 2025 03:05:26 +0000 Subject: [PATCH 3/3] Update bonds.pyx --- src/biotite/structure/bonds.pyx | 44 ++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/biotite/structure/bonds.pyx b/src/biotite/structure/bonds.pyx index 57dfc50b5..275c97aa5 100644 --- a/src/biotite/structure/bonds.pyx +++ b/src/biotite/structure/bonds.pyx @@ -267,9 +267,20 @@ class BondList(Copyable): SINGLE bond between C6 and H6 """ - def __init__(self, uint32 atom_count, np.ndarray bonds=None): + cdef uint32 _atom_count + cdef uint32 _max_bonds_per_atom + cdef object _bonds + cdef int32 atom_index1 + cdef int32 atom_index2 + + def __init__(self, uint32 atom_count, long[:, :] bonds=None): + cdef: + Py_ssize_t i, n_bonds + int bond_a, bond_b + + n_bonds = bonds.shape[0] self._atom_count = atom_count - bonds = memoryview(bonds) + if bonds is not None and len(bonds) > 0: if bonds.ndim != 2: raise ValueError("Expected a 2D-ndarray for input bonds") @@ -279,33 +290,38 @@ class BondList(Copyable): # Input contains bonds (index 0 and 1) # including the bond type value (index 2) # Bond indices: - for bond_a, bond_b, _ in bonds: - if bond_a == bond_b: - raise ValueError(f"Atom {bond_a} cannot bind to itself {bond_b}") - self._bonds[:,:2] = np.sort( + for i in range(n_bonds): + bond_a = bonds[i, 0] + bond_b = bonds[i, 1] + + if bond_a == bond_b: + raise ValueError(f"Atom {bond_a} cannot bind to itself {bond_b}") + self._bonds[:, :2] = np.sort( # Indices are sorted per bond # so that the lower index is at the first position - _to_positive_index_array(bonds[:,:2], atom_count), axis=1 + _to_positive_index_array(np.asarray(bonds[:, :2]), atom_count), axis=1 ) # Bond type: - if (bonds[:, 2] >= len(BondType)).any(): + if (np.asarray(bonds[:, 2]) >= len(BondType)).any(): raise ValueError( f"BondType {np.max(bonds[:, 2])} is invalid" ) - self._bonds[:,2] = bonds[:, 2] + self._bonds[:, 2] = bonds[:, 2] # Indices are sorted per bond # so that the lower index is at the first position elif bonds.shape[1] == 2: - for bond_a, bond_b in bonds: - if bond_a == bond_b: - raise ValueError(f"Atom {bond_a} cannot bind to itself {bond_b}") + for i in range(n_bonds): + bond_a = bonds[i, 0] + bond_b = bonds[i, 1] + if bond_a == bond_b: + raise ValueError(f"Atom {bond_a} cannot bind to itself {bond_b}") # Input contains the bonds without bond type # -> Default: Set bond type ANY (0) - self._bonds[:,:2] = np.sort( + self._bonds[:, :2] = np.sort( # Indices are sorted per bond # so that the lower index is at the first position - _to_positive_index_array(bonds[:,:2], atom_count), axis=1 + _to_positive_index_array(np.asarray(bonds[:, :2]), atom_count), axis=1 ) else: raise ValueError(