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

1"""Utility functions related to stereochemistry tagging in molecules. 

2 

3.. module:: stereochemistry.utils 

4.. moduleauthor:: Charlles Abreu <craabreu@mit.edu> 

5""" 

6 

7import typing as t 

8 

9from rdkit import Chem 

10 

11 

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. 

16 

17 This is a helper function for 

18 :func:`~stereochemistry._argsort_descending_with_parity`. 

19 

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. 

32 

33 Returns 

34 ------- 

35 tuple[int, int, int, int, bool] 

36 A tuple containing: 

37 

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. 

42 

43 """ 

44 if a < b: 

45 return j, i, b, a, not odd 

46 return i, j, a, b, odd 

47 

48 

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. 

53 

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. 

57 

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. 

68 

69 Returns 

70 ------- 

71 tuple[tuple[int, ...], bool] 

72 A tuple containing: 

73 

74 - A tuple of indices that sort the integers in descending order. 

75 - A boolean indicating whether the required permutation is odd. 

76 

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) 

88 

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 

100 

101 

102def concat(*args: t.Any) -> str: 

103 """Concatenate the string representations of the given arguments. 

104 

105 Parameters 

106 ---------- 

107 *args 

108 The arguments to be concatenated. 

109 

110 Returns 

111 ------- 

112 str 

113 The concatenated string. 

114 

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' 

124 

125 """ 

126 return "".join(map(str, args)) 

127 

128 

129def describe_atom(atom: Chem.Atom) -> str: 

130 """Describe an atom. 

131 

132 Parameters 

133 ---------- 

134 atom : Chem.Atom 

135 The atom to describe. 

136 

137 Returns 

138 ------- 

139 str 

140 A string description of the atom. 

141 

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' 

149 

150 """ 

151 return f"{atom.GetSymbol()}{atom.GetIdx()}"