Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ classifiers = [
]

dependencies = [
# Wheels compiled with NumPy 2.0 are backward compatible with NumPy 1.x
# https://numpy.org/devdocs/dev/depending_on_numpy.html#numpy-2-0-specific-advice
"numpy >= 1.25",
"numpy >= 2.1",
"biotraj >= 1.0, < 2.0",
"requests >= 2.12",
"msgpack >= 0.5.6",
Expand Down
19 changes: 8 additions & 11 deletions src/biotite/application_v2/sra/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from os.path import join
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Literal, TypeAlias
import numpy as np
from biotite.application_v2.localapp import (
CLIArgument,
Expand All @@ -37,10 +36,6 @@
from biotite.sequence.io.fastq.file import FastqFile
from biotite.sequence.seqtypes import NucleotideSequence

_OffsetFormat: TypeAlias = Literal[
"Sanger", "Solexa", "Illumina-1.3", "Illumina-1.5", "Illumina-1.8"
]


@dataclass(frozen=True)
class PrefetchDirectory:
Expand Down Expand Up @@ -85,13 +80,15 @@ class FastqResult:
default=None, repr=False, compare=False
)

def get_files(self, offset: int | _OffsetFormat = "Sanger") -> list[FastqFile]:
def get_files(
self, offset: int | FastqFile.Offset = FastqFile.Offset.SANGER
) -> list[FastqFile]:
"""
Parse the extracted FASTQ files.

Parameters
----------
offset : int or {'Sanger', 'Solexa', 'Illumina-1.3', 'Illumina-1.5', 'Illumina-1.8'}, optional
offset : int or FastqFile.Offset, optional
This value is subtracted from the FASTQ ASCII code to obtain
the quality score.

Expand All @@ -103,14 +100,14 @@ def get_files(self, offset: int | _OffsetFormat = "Sanger") -> list[FastqFile]:
return [FastqFile.read(path, offset=offset) for path in self.file_paths]

def get_sequences(
self, offset: int | _OffsetFormat = "Sanger"
self, offset: int | FastqFile.Offset = FastqFile.Offset.SANGER
) -> list[dict[str, NucleotideSequence]]:
"""
Get the reads from the extracted FASTQ files.

Parameters
----------
offset : int or {'Sanger', 'Solexa', 'Illumina-1.3', 'Illumina-1.5', 'Illumina-1.8'}, optional
offset : int or FastqFile.Offset, optional
This value is subtracted from the FASTQ ASCII code to obtain
the quality score.

Expand All @@ -127,14 +124,14 @@ def get_sequences(
]

def get_sequences_and_scores(
self, offset: int | _OffsetFormat = "Sanger"
self, offset: int | FastqFile.Offset = FastqFile.Offset.SANGER
) -> list[dict[str, tuple[NucleotideSequence, np.ndarray]]]:
"""
Get the reads and quality scores from the extracted FASTQ files.

Parameters
----------
offset : int or {'Sanger', 'Solexa', 'Illumina-1.3', 'Illumina-1.5', 'Illumina-1.8'}, optional
offset : int or FastqFile.Offset, optional
This value is subtracted from the FASTQ ASCII code to obtain
the quality score.

Expand Down
16 changes: 8 additions & 8 deletions src/biotite/database/rcsb/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ class FieldQuery(SingleQuery):

>>> query = FieldQuery("reflns.d_resolution_high", less_or_equal=0.5)
>>> print(sorted(search(query)))
['3NIR', '4JLJ', '5D8V', '7R0H']
['30OH', '30OR', '3NIR', '4JLJ', '5D8V', '7R0H']
"""

# Overloads expose the mutually exclusive operator keywords.
Expand Down Expand Up @@ -886,10 +886,10 @@ def count(

>>> query = FieldQuery("reflns.d_resolution_high", less_or_equal=0.5)
>>> print(count(query))
4
6
>>> ids = search(query)
>>> print(sorted(ids))
['3NIR', '4JLJ', '5D8V', '7R0H']
['30OH', '30OR', '3NIR', '4JLJ', '5D8V', '7R0H']
"""
query_dict = _initialize_query_dict(query, return_type, group_by, content_types)

Expand Down Expand Up @@ -1029,20 +1029,20 @@ def search(

>>> query = FieldQuery("reflns.d_resolution_high", less_or_equal=0.5)
>>> print(sorted(search(query)))
['3NIR', '4JLJ', '5D8V', '7R0H']
['30OH', '30OR', '3NIR', '4JLJ', '5D8V', '7R0H']
>>> print(search(query, sort_by="rcsb_accession_info.initial_release_date"))
['7R0H', '5D8V', '4JLJ', '3NIR']
['30OH', '30OR', '7R0H', '5D8V', '4JLJ', '3NIR']
>>> print(search(
... query, range=(1,4), sort_by="rcsb_accession_info.initial_release_date"
... ))
['5D8V', '4JLJ', '3NIR']
['30OR', '7R0H', '5D8V']
>>> print(sorted(search(query, return_type="polymer_instance")))
['3NIR.A', '4JLJ.A', '4JLJ.B', '5D8V.A', '7R0H.A']
['30OH.A', '30OR.A', '3NIR.A', '4JLJ.A', '4JLJ.B', '5D8V.A', '7R0H.A']
>>> print(search(
... query, return_type="polymer_entity", return_groups=True,
... group_by=UniprotGrouping(sort_by="rcsb_accession_info.initial_release_date"),
... ))
{'P27707': ['4JLJ_1'], 'P80176': ['5D8V_1'], 'O29777': ['7R0H_1'], 'P01542': ['3NIR_1']}
{'Q9V099': ['30OR_1', '30OH_1'], 'P27707': ['4JLJ_1'], 'P80176': ['5D8V_1'], 'O29777': ['7R0H_1'], 'P01542': ['3NIR_1']}
"""
query_dict = _initialize_query_dict(query, return_type, group_by, content_types)

Expand Down
6 changes: 3 additions & 3 deletions src/biotite/interface/openmm/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from biotite.structure.chains import get_chain_starts
from biotite.structure.error import BadStructureError
from biotite.structure.filter import filter_amino_acids, filter_nucleotides
from biotite.structure.info.masses import mass
from biotite.structure.info.masses import masses
from biotite.structure.residues import get_residue_starts
from biotite.typing import XYZ, K, NDArray1, NDArray2

Expand Down Expand Up @@ -56,8 +56,8 @@ def to_system(
"""
system = System()

for element in atoms.element.tolist():
system.addParticle(mass(element))
for atom_mass in masses(atoms).tolist():
system.addParticle(atom_mass)

if atoms.box is not None:
if isinstance(atoms, AtomArrayStack):
Expand Down
2 changes: 1 addition & 1 deletion src/biotite/interface/pymol/shapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def draw_arrows(
color = _arrayfy(color, expected_length, 2)
head_color = _arrayfy(head_color, expected_length, 2)

normal = (end - start) / np.linalg.norm(end - start, axis=-1)[:, np.newaxis]
normal = (end - start) / np.linalg.vector_norm(end - start, axis=-1)[:, np.newaxis]
middle = end - normal * head_length[:, np.newaxis]

cgo_list = []
Expand Down
2 changes: 1 addition & 1 deletion src/biotite/sequence/io/fasta/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ def set_a3m_alignments(
if not isinstance(alphabet, LetterAlphabet):
raise ValueError("A3M requires a letter sequence")
# Keep gaps in the query sequence as lower case letters
a3m_string_array[query_gaps] = np.char.lower(
a3m_string_array[query_gaps] = np.strings.lower(
query.alphabet.decode_multiple(target_code[query_gaps], as_bytes=True) # pyright: ignore[reportCallIssue,reportArgumentType]
)
# Matches/mismatches are indicated with upper case letters
Expand Down
4 changes: 2 additions & 2 deletions src/biotite/structure/alphabet/encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ def _calc_conformation_descriptors(
desc[i, 4] = numpy.sum(u1 * u4, axis=-1)
desc[i, 5] = numpy.sum(u2 * u3, axis=-1)
desc[i, 6] = numpy.sum(u1 * u3, axis=-1)
desc[i, 7] = numpy.linalg.norm(ca[i] - ca[j], axis=-1)
desc[i, 7] = numpy.linalg.vector_norm(ca[i] - ca[j], axis=-1)
desc[i, 8] = numpy.clip(j - i, -4, 4)
desc[i, 9] = numpy.copysign(numpy.log(numpy.abs(j - i) + 1), j - i)
return desc
Expand Down Expand Up @@ -380,5 +380,5 @@ def encode(


def _normalize(x: numpy.ndarray, *, inplace: bool = False) -> numpy.ndarray:
norm = numpy.linalg.norm(x, axis=-1).reshape(*x.shape[:-1], 1)
norm = numpy.linalg.vector_norm(x, axis=-1).reshape(*x.shape[:-1], 1)
return numpy.divide(x, norm, out=x if inplace else None, where=norm != 0)
2 changes: 1 addition & 1 deletion src/biotite/structure/basepairs.py
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ def base_pairs(
to_remove = []

# Get all bases that have non-unique pairing interactions
base_indices, occurrences = np.unique(basepairs, return_counts=True)
base_indices, occurrences = np.unique_counts(basepairs)
for base_index, occurrence in zip(base_indices, occurrences):
if occurrence > 1:
# Write the non-unique base pairs to a dictionary as
Expand Down
6 changes: 3 additions & 3 deletions src/biotite/structure/box.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,9 @@ def unitcell_from_vectors(
a = box[0]
b = box[1]
c = box[2]
len_a = linalg.norm(a)
len_b = linalg.norm(b)
len_c = linalg.norm(c)
len_a = linalg.vector_norm(a)
len_b = linalg.vector_norm(b)
len_c = linalg.vector_norm(c)
alpha = np.arccos(np.dot(b, c) / (len_b * len_c))
beta = np.arccos(np.dot(a, c) / (len_a * len_c))
gamma = np.arccos(np.dot(a, b) / (len_a * len_b))
Expand Down
14 changes: 6 additions & 8 deletions src/biotite/structure/connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from biotite.structure.bonds import BondList, BondType
from biotite.structure.error import BadStructureError, InconsistentBondTypeWarning
from biotite.typing import K, N, NDArray1
from biotite.util import map_unique

# Covalent radii (in Å) taken from Cordero et al.
# For carbon the sp3 radius is used, for manganese, iron and cobalt the low-spin radius
Expand Down Expand Up @@ -240,10 +241,9 @@ def connect_via_distances(

# NaN for unknown elements ensures that they never form bonds,
# as any comparison with NaN is false
radii = np.array(
[_COVALENT_RADII.get(element.upper(), np.nan) for element in atoms.element],
dtype=np.float32,
)
radii = map_unique(
lambda element: _COVALENT_RADII.get(element.upper(), np.nan), atoms.element
).astype(np.float32)

residue_starts = get_residue_starts(atoms, add_exclusive_stop=True)

Expand Down Expand Up @@ -559,7 +559,7 @@ def infer_bond_types(
else:
known_charges = None
bond_list, charges, converged = rust_infer_bond_types(
[element.upper() for element in atoms.element],
atoms.element.tolist(),
atoms.bonds,
total_charge,
known_charges,
Expand Down Expand Up @@ -668,9 +668,7 @@ def _connect_inter_residue(
# Avoid circular import
from biotite.structure.info.misc import link_type

link_types = [
link_type(res_name) for res_name in atoms.res_name[residue_starts[:-1]]
]
link_types = map_unique(link_type, atoms.res_name[residue_starts[:-1]]).tolist()
is_disconnected = (
# Residues are not inside the same chain
(atoms.chain_id[residue_starts[1:-1]] != atoms.chain_id[residue_starts[:-2]])
Expand Down
2 changes: 1 addition & 1 deletion src/biotite/structure/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ def filter_linear_bond_continuity(
Note that this function purely uses distances between consecutive atoms.
A potential ``BondList`` is not considered here.
"""
dist = np.linalg.norm(np.diff(array.coord, axis=0), axis=1)
dist = np.linalg.vector_norm(np.diff(array.coord, axis=0), axis=1)
mask = (dist >= min_len) & (dist <= max_len)
return np.append(mask, True) # pyright: ignore[reportReturnType]

Expand Down
8 changes: 5 additions & 3 deletions src/biotite/structure/graphics/rna.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,14 +279,16 @@ def plot_nucleotide_secondary_structure(
coordinates[i - 1][1] - coordinates[i][1],
]
)
vector_to_previous = vector_to_previous / np.linalg.norm(vector_to_previous)
vector_to_previous = vector_to_previous / np.linalg.vector_norm(
vector_to_previous
)
vector_to_next = np.array(
[
coordinates[i][0] - coordinates[i + 1][0],
coordinates[i][1] - coordinates[i + 1][1],
]
)
vector_to_next = vector_to_next / np.linalg.norm(vector_to_next)
vector_to_next = vector_to_next / np.linalg.vector_norm(vector_to_next)
vector = (vector_to_next + vector_to_previous) / 2
elif i > 0:
# For the last base get the direction vector to the previous
Expand All @@ -307,7 +309,7 @@ def plot_nucleotide_secondary_structure(
]
)
# Normalize the vector
vector = vector / np.linalg.norm(vector)
vector = vector / np.linalg.vector_norm(vector)
# Get the perpendicular vector
vector = np.array([vector[1], -vector[0]])
# The annotations are offset in the direction of the
Expand Down
20 changes: 9 additions & 11 deletions src/biotite/structure/hbond.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from biotite.structure.bonds import BondList
from biotite.structure.filter import filter_heavy
from biotite.structure.geometry import angle, distance
from biotite.structure.residues import get_all_residue_positions
from biotite.typing import C3, XYZ, K, M, N, NDArray1, NDArray2, NDArray3


Expand Down Expand Up @@ -413,7 +414,7 @@ def _get_bonded_h_via_distance(
CUTOFF = 1.5

coord = array.coord
res_id = array.res_id
residue_positions = get_all_residue_positions(array)
hydrogen_mask = ~filter_heavy(array)

donor_hydrogen_mask = np.zeros(len(array), dtype=bool)
Expand All @@ -427,15 +428,12 @@ def _get_bonded_h_via_distance(
associated_donor_indices,
) # pyright: ignore[reportReturnType]

# Sort the hydrogen atoms by residue ID, so that the candidates for each
# donor can be found via binary search instead of scanning all atoms
hydrogen_indices = hydrogen_indices[
np.argsort(res_id[hydrogen_indices], kind="stable")
]
hydrogen_res_ids = res_id[hydrogen_indices]
donor_res_ids = res_id[donor_indices]
candidate_start = np.searchsorted(hydrogen_res_ids, donor_res_ids, side="left")
candidate_stop = np.searchsorted(hydrogen_res_ids, donor_res_ids, side="right")
# The residue positions are already sorted in atom order, so the candidates
# for each donor can be found via binary search instead of scanning all atoms
hydrogen_residues = residue_positions[hydrogen_indices]
donor_residues = residue_positions[donor_indices]
candidate_start = np.searchsorted(hydrogen_residues, donor_residues, side="left")
candidate_stop = np.searchsorted(hydrogen_residues, donor_residues, side="right")

# Flatten the ragged donor-to-candidate mapping into plain index arrays,
# so that all distances can be computed in a single vectorized call
Expand All @@ -447,7 +445,7 @@ def _get_bonded_h_via_distance(
associated_donor_indices,
) # pyright: ignore[reportReturnType]
donor_of_pair = np.repeat(np.arange(len(donor_indices)), candidate_count)
offsets = np.cumsum(candidate_count) - candidate_count
offsets = np.cumulative_sum(candidate_count, include_initial=True)[:-1]
index_in_candidates = np.arange(total_count) - offsets[donor_of_pair]
pair_hydrogen_i = hydrogen_indices[
candidate_start[donor_of_pair] + index_in_candidates
Expand Down
6 changes: 4 additions & 2 deletions src/biotite/structure/info/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def ion_names() -> list[str]:
formulas = chem_comp["formula"].as_array()

# Simple filter: The formula must contain only one element
mask = np.char.isalpha(formulas)
mask = np.strings.isalpha(formulas)
comp_ids = comp_ids[mask]
formulas = formulas[mask]

Expand Down Expand Up @@ -164,4 +164,6 @@ def _get_group_members(match_types: list[str]) -> list[str]:
comp_ids = category["id"].as_array()
types = category["type"].as_array()
# Ignore case
return comp_ids[np.isin(np.char.lower(types), np.char.lower(match_types))].tolist()
return comp_ids[
np.isin(np.strings.lower(types), np.strings.lower(match_types))
].tolist()
Loading
Loading