Skip to content

Commit ff6b5f7

Browse files
committed
move ignore to Fingerprint constructor
1 parent 4ebd47d commit ff6b5f7

7 files changed

Lines changed: 35 additions & 107 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88

99
### Added
1010

11+
- Added `ignore` parameter in `Fingerprint` to ignore specific pairs of residues.
12+
Defaults to ignoring interactions of a residue with itself.
1113
- Support for `ImplicitHBAcceptor` and `ImplicitHBDonor` interactions to calculate
1214
hydrogen bond interactions with merely heavy atoms. This helps users to estimate
1315
the hydrogen bond interactions for structures from PDB database or AI-driven

docs/notebooks/advanced.ipynb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@
584584
"cell_type": "markdown",
585585
"metadata": {},
586586
"source": [
587-
"You can define your own predicate function to decide which residue pairs should be ignored. This can be done through the `ignore` parameter which takes two {class}`~prolif.residue.Residue` objects as input and returns a boolean:"
587+
"You can define your own predicate function to decide which residue pairs should be ignored. This can be done through the `ignore` parameter of the `Fingerprint` constructor, which takes two {class}`~prolif.residue.Residue` objects as input and returns a boolean:"
588588
]
589589
},
590590
{
@@ -604,7 +604,8 @@
604604
" )\n",
605605
"\n",
606606
"\n",
607-
"fp.run_from_iterable([protein_mol], protein_mol, ignore=ignore_sequence_neighbours)\n",
607+
"fp = plf.Fingerprint([\"HBAcceptor\", \"HBDonor\"], ignore=ignore_sequence_neighbours)\n",
608+
"fp.run_from_iterable([protein_mol], protein_mol)\n",
608609
"fp.to_dataframe()"
609610
]
610611
},

prolif/fingerprint.py

Lines changed: 13 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ class Fingerprint:
140140
implicit_hydrogens : bool
141141
Whether to use interactions compatible with implicit hydrogens instead of the
142142
default explicit hydrogen-based ones.
143+
ignore : Callable[[Residue, Residue], bool]
144+
Predicate function that returns ``True`` if the two residues should be
145+
skipped when calculating interactions. Default is to ignore interactions
146+
between the same residue (see :func:`~prolif.residue.ignore_self_interactions`).
143147
144148
Attributes
145149
----------
@@ -254,7 +258,7 @@ class for more information.
254258
255259
.. versionchanged:: 2.2.0
256260
Added ``implicit_hydrogens`` for easily switching to implicit-hydrogen
257-
interactions.
261+
interactions. Added ``ignore`` parameter.
258262
"""
259263

260264
def __init__(
@@ -265,6 +269,7 @@ def __init__(
265269
vicinity_cutoff: float = 6.0,
266270
use_segid: bool | None = None,
267271
implicit_hydrogens: bool = False,
272+
ignore: Callable[[Residue, Residue], bool] = ignore_self_interactions,
268273
) -> None:
269274
if interactions is None:
270275
interactions = DEFAULT_INTERACTIONS
@@ -295,6 +300,7 @@ def __init__(
295300
interactions = temp_interactions
296301

297302
self.count = count
303+
self.ignore = ignore
298304
self._set_interactions(interactions, parameters)
299305
self.vicinity_cutoff = vicinity_cutoff
300306
self.parameters = parameters
@@ -471,7 +477,6 @@ def generate(
471477
prot: Molecule,
472478
residues: "ResidueSelection" = None,
473479
metadata: Literal[False] = False,
474-
ignore: Callable[[Residue, Residue], bool] = ignore_self_interactions,
475480
) -> dict[tuple["ResidueId", "ResidueId"], "NDArray"]: ...
476481
@overload
477482
def generate(
@@ -480,15 +485,13 @@ def generate(
480485
prot: Molecule,
481486
residues: "ResidueSelection" = None,
482487
metadata: Literal[True] = True,
483-
ignore: Callable[[Residue, Residue], bool] = ignore_self_interactions,
484488
) -> IFP: ...
485489
def generate(
486490
self,
487491
lig: Molecule,
488492
prot: Molecule,
489493
residues: "ResidueSelection" = None,
490494
metadata: bool = False,
491-
ignore: Callable[[Residue, Residue], bool] = ignore_self_interactions,
492495
) -> IFP | dict[tuple["ResidueId", "ResidueId"], "NDArray"]:
493496
"""Generates the interaction fingerprint between 2 molecules
494497
@@ -509,13 +512,6 @@ def generate(
509512
metadata : bool
510513
For each residue pair and interaction, return an interaction metadata
511514
dictionary instead of bits.
512-
ignore : Callable[[Residue, Residue], bool]
513-
Predicate function that returns ``True`` if the two residues should be
514-
skipped when calculating interactions. Default is to ignore interactions
515-
between the same residue (see
516-
:func:`~prolif.residue.ignore_self_interactions`). Can be used to exclude
517-
residue pairs based on :class:`~prolif.residue.ResidueId` attributes, but
518-
also by geometric criteria (e.g. residues where any atom clashes).
519515
520516
Returns
521517
-------
@@ -544,9 +540,6 @@ def generate(
544540
dictionary of metadata tuples indexed by interaction name instead of a
545541
tuple of arrays.
546542
547-
.. versionchanged:: 2.2.0
548-
Added ``ignore`` parameter.
549-
550543
"""
551544
ifp: IFP | dict[tuple["ResidueId", "ResidueId"], "NDArray"] = (
552545
IFP() if metadata else {}
@@ -563,7 +556,7 @@ def generate(
563556
)
564557
for prot_key in prot_residues: # type:ignore[union-attr]
565558
pres = prot[prot_key]
566-
if ignore(lres, pres):
559+
if self.ignore(lres, pres):
567560
continue
568561
key = (lresid, pres.resid)
569562
interactions = get_interactions(lres, pres)
@@ -582,7 +575,6 @@ def run(
582575
progress: bool = True,
583576
n_jobs: int | None = None,
584577
parallel_strategy: Literal["chunk", "queue"] | None = None,
585-
ignore: Callable[[Residue, Residue], bool] = ignore_self_interactions,
586578
) -> "Fingerprint":
587579
"""Generates the fingerprint on a trajectory for a ligand and a protein
588580
@@ -625,16 +617,9 @@ def run(
625617
atoms.
626618
- ``None``: See :func:`~prolif.parallel.get_mda_parallel_strategy` for
627619
the default behavior.
628-
ignore : Callable[[Residue, Residue], bool]
629-
Predicate function that returns ``True`` if the two residues should be
630-
skipped when calculating interactions. Default is to ignore interactions
631-
between the same residue (see
632-
:func:`~prolif.residue.ignore_self_interactions`). Can be used to exclude
633-
residue pairs based on :class:`~prolif.residue.ResidueId` attributes, but
634-
also by geometric criteria (e.g. residues where any atom clashes).
635620
636621
Raises
637-
------
622+
------
638623
ValueError
639624
If ``n_jobs <= 0``
640625
@@ -679,9 +664,6 @@ def run(
679664
Added ``use_segid``, ``parallel_strategy`` parameter and changed the
680665
default behavior of ``n_jobs=None``.
681666
682-
.. versionchanged:: 2.2.0
683-
Added ``ignore`` parameter.
684-
685667
""" # noqa: E501
686668
if converter_kwargs is not None and len(converter_kwargs) != 2:
687669
raise ValueError("converter_kwargs must be a list of 2 dicts")
@@ -714,7 +696,6 @@ def run(
714696
residues=residues,
715697
converter_kwargs=converter_kwargs,
716698
progress=progress,
717-
ignore=ignore,
718699
)
719700
else:
720701
ifp = self._run_parallel(
@@ -726,7 +707,6 @@ def run(
726707
progress=progress,
727708
n_jobs=n_jobs,
728709
parallel_strategy=parallel_strategy,
729-
ignore=ignore,
730710
)
731711
self.ifp = ifp
732712

@@ -741,7 +721,6 @@ def run(
741721
use_segid=self.use_segid,
742722
n_jobs=n_jobs,
743723
parallel_strategy=parallel_strategy,
744-
ignore=ignore,
745724
)
746725
return self
747726

@@ -763,7 +742,6 @@ def _run_serial(
763742
residues: "ResidueSelection",
764743
converter_kwargs: tuple[dict, dict],
765744
progress: bool,
766-
ignore: Callable[[Residue, Residue], bool],
767745
**kwargs: Any,
768746
) -> "IFPResults":
769747
"""Serial implementation for trajectories."""
@@ -780,7 +758,7 @@ def _run_serial(
780758
prot, use_segid=self.use_segid, **converter_kwargs[1]
781759
)
782760
ifp[int(ts.frame)] = self.generate(
783-
lig_mol, prot_mol, residues=residues, metadata=True, ignore=ignore
761+
lig_mol, prot_mol, residues=residues, metadata=True
784762
)
785763
return ifp
786764

@@ -794,7 +772,6 @@ def _run_parallel(
794772
progress: bool,
795773
n_jobs: int | None,
796774
parallel_strategy: Literal["chunk", "queue"],
797-
ignore: Callable[[Residue, Residue], bool],
798775
**kwargs: Any,
799776
) -> "IFPResults":
800777
"""Parallel implementation of :meth:`~Fingerprint.run`"""
@@ -825,7 +802,6 @@ def _run_parallel(
825802
residues=residues,
826803
converter_kwargs=converter_kwargs,
827804
progress=progress,
828-
ignore=ignore,
829805
)
830806
else:
831807
frames = []
@@ -843,7 +819,6 @@ def _run_parallel(
843819
tqdm_kwargs=tqdm_kwargs,
844820
rdkitconverter_kwargs=converter_kwargs,
845821
use_segid=self.use_segid or False,
846-
ignore=ignore,
847822
) as pool:
848823
return pool.process(traj, lig, prot)
849824

@@ -860,7 +835,6 @@ def _run_parallel(
860835
tqdm_kwargs=tqdm_kwargs,
861836
rdkitconverter_kwargs=converter_kwargs,
862837
use_segid=self.use_segid or False,
863-
ignore=ignore,
864838
) as pool:
865839
for ifp_data_chunk in pool.process(args_iterable):
866840
ifp.update(ifp_data_chunk)
@@ -875,7 +849,6 @@ def run_from_iterable(
875849
residues: "ResidueSelection" = None,
876850
progress: bool = True,
877851
n_jobs: int | None = None,
878-
ignore: Callable[[Residue, Residue], bool] = ignore_self_interactions,
879852
) -> "Fingerprint":
880853
"""Generates the fingerprint between a list of ligands and a protein
881854
@@ -900,13 +873,6 @@ def run_from_iterable(
900873
Number of processes to run in parallel. If ``n_jobs=1``, the
901874
calculation will run in serial. If ``n_jobs=None``, see
902875
:func:`~prolif.parallel.get_n_jobs` for the default behavior.
903-
ignore : Callable[[Residue, Residue], bool]
904-
Predicate function that returns ``True`` if the two residues should be
905-
skipped when calculating interactions. Default is to ignore interactions
906-
between the same residue (see
907-
:func:`~prolif.residue.ignore_self_interactions`). Can be used to exclude
908-
residue pairs based on :class:`~prolif.residue.ResidueId` attributes, but
909-
also by geometric criteria (e.g. residues where any atom clashes).
910876
911877
Raises
912878
------
@@ -948,9 +914,6 @@ def run_from_iterable(
948914
.. versionchanged:: 2.1.0
949915
Changed the default behavior of ``n_jobs=None``.
950916
951-
.. versionchanged:: 2.2.0
952-
Added ``ignore`` parameter.
953-
954917
"""
955918
if residues == "all":
956919
residues = list(prot_mol.residues)
@@ -963,7 +926,6 @@ def run_from_iterable(
963926
prot_mol=prot_mol,
964927
residues=residues,
965928
progress=progress,
966-
ignore=ignore,
967929
)
968930
else:
969931
ifp = self._run_iter_parallel(
@@ -972,7 +934,6 @@ def run_from_iterable(
972934
residues=residues,
973935
progress=progress,
974936
n_jobs=n_jobs,
975-
ignore=ignore,
976937
)
977938
self.ifp = ifp
978939

@@ -983,7 +944,6 @@ def run_from_iterable(
983944
residues=residues,
984945
progress=progress,
985946
n_jobs=n_jobs,
986-
ignore=ignore,
987947
)
988948

989949
return self
@@ -994,14 +954,13 @@ def _run_iter_serial(
994954
prot_mol: Molecule,
995955
residues: "ResidueSelection" = None,
996956
progress: bool = True,
997-
ignore: Callable[[Residue, Residue], bool] = ignore_self_interactions,
998957
**kwargs: Any,
999958
) -> "IFPResults":
1000959
iterator = tqdm(lig_iterable, **kwargs) if progress else lig_iterable
1001960
ifp: "IFPResults" = {}
1002961
for i, lig_mol in enumerate(iterator):
1003962
ifp[i] = self.generate(
1004-
lig_mol, prot_mol, residues=residues, metadata=True, ignore=ignore
963+
lig_mol, prot_mol, residues=residues, metadata=True
1005964
)
1006965
return ifp
1007966

@@ -1012,7 +971,6 @@ def _run_iter_parallel(
1012971
residues: "ResidueSelection" = None,
1013972
progress: bool = True,
1014973
n_jobs: int | None = None,
1015-
ignore: Callable[[Residue, Residue], bool] = ignore_self_interactions,
1016974
**kwargs: Any,
1017975
) -> "IFPResults":
1018976
"""Parallel implementation of :meth:`~Fingerprint.run_from_iterable`"""
@@ -1028,7 +986,6 @@ def _run_iter_parallel(
1028986
fingerprint=self,
1029987
prot_mol=prot_mol,
1030988
residues=residues,
1031-
ignore=ignore,
1032989
tqdm_kwargs={"total": total, "disable": not progress, **kwargs},
1033990
) as pool:
1034991
for i, ifp_data in enumerate(pool.process(lig_iterable)):
@@ -1059,7 +1016,7 @@ def _run_bridged_analysis(
10591016
""" # noqa: E501
10601017
self.ifp = cast("IFPResults", getattr(self, "ifp", {}))
10611018
for interaction in self.bridged_interactions.values():
1062-
interaction.setup(ifp_store=self.ifp, **kwargs)
1019+
interaction.setup(ifp_store=self.ifp, ignore=self.ignore, **kwargs)
10631020
interaction.run(traj, lig, prot)
10641021
return self
10651022

@@ -1078,7 +1035,7 @@ def _run_iter_bridged_analysis(
10781035
"""
10791036
self.ifp = getattr(self, "ifp", {})
10801037
for interaction in self.bridged_interactions.values():
1081-
interaction.setup(ifp_store=self.ifp, **kwargs)
1038+
interaction.setup(ifp_store=self.ifp, ignore=self.ignore, **kwargs)
10821039
interaction.run_from_iterable(lig_iterable, prot_mol)
10831040
return self
10841041

prolif/interactions/water_bridge.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ def setup(self, ifp_store: Optional["IFPResults"] = None, **kwargs: Any) -> None
110110
self.residues = self.kwargs.pop("residues", None)
111111
self.converter_kwargs = self.kwargs.pop("converter_kwargs", ({}, {}))
112112
self.water_fp.use_segid = self.kwargs.pop("use_segid", False)
113+
self.water_fp.ignore = self.kwargs.pop("ignore", self.water_fp.ignore)
113114

114115
def run(
115116
self,

0 commit comments

Comments
 (0)