Coverage for chempropstereo/stereochemistry/utils.py: 100%
21 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-03-22 21:04 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-03-22 21:04 +0000
1"""Utility functions related to stereochemistry tagging in molecules.
3.. module:: stereochemistry.utils
4.. moduleauthor:: Charlles Abreu <craabreu@mit.edu>
5"""
7import typing as t
9from rdkit import Chem
12def _swap_if_ascending(
13 i: int, j: int, a: int, b: int, odd: bool
14) -> tuple[int, int, int, int, bool]:
15 """Conditionally swap indices and values, and update permutation parity.
17 This is a helper function for
18 :func:`~stereochemistry._argsort_descending_with_parity`.
20 Parameters
21 ----------
22 i
23 The first index.
24 j
25 The second index.
26 a
27 The value at the first index.
28 b
29 The value at the second index.
30 odd
31 Whether the permutation is currently odd rather than even.
33 Returns
34 -------
35 tuple[int, int, int, int, bool]
36 A tuple containing:
38 - The potentially swapped indices i and j.
39 - The potentially swapped values a and b.
40 - A boolean indicating whether the permutation is odd after having or not
41 performed the swap.
43 """
44 if a < b:
45 return j, i, b, a, not odd
46 return i, j, a, b, odd
49def argsort_descending_with_parity(
50 a: int, b: int, c: int, d: t.Optional[int] = None
51) -> tuple[tuple[int, ...], bool]:
52 """Perform an indirect sort on three or four integers.
54 This function takes three or four integers and returns the indices that
55 would arrange them in descending order, as well as a boolean indicating
56 whether the resulting permutation is odd rather than even.
58 Parameters
59 ----------
60 a
61 The first integer.
62 b
63 The second integer.
64 c
65 The third integer.
66 d
67 The fourth integer, optional.
69 Returns
70 -------
71 tuple[tuple[int, ...], bool]
72 A tuple containing:
74 - A tuple of indices that sort the integers in descending order.
75 - A boolean indicating whether the required permutation is odd.
77 Examples
78 --------
79 >>> from chempropstereo.stereochemistry import utils
80 >>> utils.argsort_descending_with_parity(9, 2, 1)
81 ((0, 1, 2), False)
82 >>> utils.argsort_descending_with_parity(3, 6, 1)
83 ((1, 0, 2), True)
84 >>> utils.argsort_descending_with_parity(3, 1, 2)
85 ((0, 2, 1), True)
86 >>> utils.argsort_descending_with_parity(3, 6, 1, 8)
87 ((3, 1, 0, 2), False)
89 """
90 i, j, a, b, odd = _swap_if_ascending(0, 1, a, b, False)
91 j, k, b, c, odd = _swap_if_ascending(j, 2, b, c, odd)
92 if d is None:
93 i, j, a, b, odd = _swap_if_ascending(i, j, a, b, odd)
94 return (i, j, k), odd
95 k, m, c, d, odd = _swap_if_ascending(k, 3, c, d, odd)
96 i, j, a, b, odd = _swap_if_ascending(i, j, a, b, odd)
97 j, k, b, c, odd = _swap_if_ascending(j, k, b, c, odd)
98 i, j, a, b, odd = _swap_if_ascending(i, j, a, b, odd)
99 return (i, j, k, m), odd
102def concat(*args: t.Any) -> str:
103 """Concatenate the string representations of the given arguments.
105 Parameters
106 ----------
107 *args
108 The arguments to be concatenated.
110 Returns
111 -------
112 str
113 The concatenated string.
115 Examples
116 --------
117 >>> from chempropstereo.stereochemistry import utils
118 >>> utils.concat(1, 2, 3)
119 '123'
120 >>> utils.concat("a", "b", "c")
121 'abc'
122 >>> utils.concat(1, "a", 2, "b", 3, "c")
123 '1a2b3c'
125 """
126 return "".join(map(str, args))
129def describe_atom(atom: Chem.Atom) -> str:
130 """Describe an atom.
132 Parameters
133 ----------
134 atom : Chem.Atom
135 The atom to describe.
137 Returns
138 -------
139 str
140 A string description of the atom.
142 Examples
143 --------
144 >>> from rdkit import Chem
145 >>> from chempropstereo.stereochemistry import utils
146 >>> mol = Chem.MolFromSmiles("N/C(O)=C(S)/C")
147 >>> utils.describe_atom(mol.GetAtomWithIdx(0))
148 'N0'
150 """
151 return f"{atom.GetSymbol()}{atom.GetIdx()}"