Coverage for lasso/dimred/sphere/algorithms.py: 0%

67 statements  

« prev     ^ index     » next       coverage.py v7.2.4, created at 2023-04-28 18:42 +0100

1import numpy as np 

2 

3from sklearn.preprocessing import normalize 

4 

5# scipy is C-code which causes invalid linter warning about ConvexHull not 

6# being around. 

7# pylint: disable = no-name-in-module 

8from scipy.spatial import ConvexHull 

9from scipy.stats import binned_statistic_2d 

10from scipy.stats._binned_statistic import BinnedStatistic2dResult 

11 

12 

13def to_spherical_coordinates(points: np.ndarray, centroid: np.ndarray, axis: str = "Z"): 

14 """Converts the points to spherical coordinates. 

15 

16 Parameters 

17 ---------- 

18 points: np.ndarray 

19 The point cloud to be sphered. 

20 centroid: np.ndarray 

21 Centroid of the point cloud. 

22 axis: str 

23 Sphere axis in the global coordinate system. 

24 

25 Returns 

26 ------- 

27 az : np.ndarray 

28 Azimuthal angle vector. 

29 po: np.ndarray 

30 Polar angle vector. 

31 

32 Notes 

33 ----- 

34 The local x-axis is set as the zero marker for azimuthal angles. 

35 """ 

36 indexes = [0, 1, 2] 

37 # set the correct indexes for swapping if the sphere 

38 # axis is not aligned with the global z axis 

39 if axis == "Y": 

40 indexes = [0, 2, 1] # sphere z axis aligned with global y-axis 

41 elif axis == "X": 

42 indexes = [2, 1, 0] # sphere z axis aligned with global x-axis 

43 

44 # vectors from centroid to points 

45 vec = points - centroid 

46 vec = normalize(vec, axis=1, norm="l2") 

47 

48 # azimuthal angles on the local xy plane 

49 # x-axis is the zero marker, and we correct 

50 # all negative angles 

51 az = np.arctan2(vec[:, indexes[1]], vec[:, indexes[0]]) 

52 neg_indexes = np.where(az < 0) 

53 az[neg_indexes] += 2 * np.pi 

54 

55 # polar angles 

56 po = np.arccos(vec[:, indexes[2]]) 

57 

58 return az, po 

59 

60 

61def sphere_hashing(histo: BinnedStatistic2dResult, field: np.ndarray): 

62 """Compute the hash of each bucket in the histogram by mapping 

63 the bin numbers to the field values and scaling the field values 

64 by the number of counts in each bin. 

65 

66 Parameters 

67 ---------- 

68 histo: BinnedStatistic2dResult 

69 3D histogram containing the indexes of all points of a simulation 

70 mapped to their projected bins. 

71 field: ndarray 

72 

73 Returns 

74 ------- 

75 hashes: np.ndarray 

76 The hashing result of all points mapped to an embedding space. 

77 

78 """ 

79 bin_n = histo.binnumber 

80 

81 assert len(bin_n[0] == len(field)) 

82 

83 # get dims of the embedding space 

84 n_rows = histo.statistic.shape[0] 

85 n_cols = histo.statistic.shape[1] 

86 

87 # bin stores the indexes of the points 

88 # index 0 stores the azimuthal angles 

89 # index 1 stores the polar angles 

90 # we want zero indexing 

91 binx = np.asarray(bin_n[0]) - 1 

92 biny = np.asarray(bin_n[1]) - 1 

93 

94 # allocate arrays 

95 bin_count = np.zeros((n_rows, n_cols)) 

96 hashes = np.zeros((n_rows, n_cols)) 

97 

98 # sum all the field values to each bin 

99 hashes[binx[:], biny[:]] += field[:] 

100 bin_count[binx[:], biny[:]] += 1 

101 

102 hashes = hashes.flatten() 

103 bin_count = bin_count.flatten() 

104 

105 # exclude all zero entries 

106 nonzero_inds = np.where(bin_count != 0) 

107 

108 # average the fields 

109 hashes[nonzero_inds] /= bin_count[nonzero_inds] 

110 

111 return hashes 

112 

113 

114def create_sphere(diameter: float): 

115 """Creates two vectors along the alpha and beta axis of a sphere. Alpha represents 

116 the angle from the sphere axis to the equator. Beta between vectors from the 

117 center of the sphere to one of the poles and the equator. 

118 

119 Parameters 

120 ---------- 

121 diameter: 

122 Diameter of the sphere. 

123 

124 Returns 

125 ------- 

126 bin_beta: np.ndarray 

127 Bin bounds for the beta angles. 

128 

129 bin_alpha: np.ndarray 

130 Bin bounds for the alpha angles. 

131 

132 """ 

133 # number of partitions for equator 

134 n_alpha = 145 

135 # number of partitions for longitude 

136 n_beta = 144 

137 

138 r = diameter / 2.0 

139 

140 # area of sphere 

141 a_sphere = 4 * np.pi * r**2 

142 n_ele = n_beta**2 

143 a_ele = a_sphere / n_ele 

144 

145 # alpha angles around the equator and the size of one step 

146 bin_alpha, delt_alpha = np.linspace(0, 2 * np.pi, n_alpha, retstep=True) 

147 

148 # bins for beta axis in terms of axis coorindates between -1 and 1 

149 count = np.linspace(0.0, float(n_beta), 145) 

150 tmp = count * a_ele 

151 tmp /= r**2 * delt_alpha 

152 bin_beta = 1 - tmp 

153 if bin_beta[-1] < -1: 

154 bin_beta[-1] = -1 

155 

156 bin_beta = np.arccos(bin_beta) 

157 return bin_alpha, bin_beta 

158 

159 

160def compute_similarity(embeddings: np.ndarray) -> np.ndarray: 

161 """Computes the similarity of each embedding. 

162 

163 Parameters 

164 ---------- 

165 embeddings: np.ndarray 

166 Model embeddings. 

167 

168 Returns 

169 ------- 

170 smatrix: np.ndarray 

171 Similarity matrix. 

172 """ 

173 

174 n_runs = len(embeddings) 

175 smatrix = np.empty((n_runs, n_runs), dtype=np.float32) 

176 for ii in range(n_runs): 

177 for jj in range(n_runs): 

178 smatrix[ii, jj] = np.dot(embeddings[ii], embeddings[jj]) / np.sqrt( 

179 np.dot(embeddings[ii], embeddings[ii]) * np.dot(embeddings[jj], embeddings[jj]) 

180 ) 

181 

182 return smatrix 

183 

184 

185def create_historgram( 

186 cloud: np.ndarray, sphere_axis: str = "Z", planar: bool = False 

187) -> BinnedStatistic2dResult: 

188 """Builds a histogram using the blocks of a sphered globe and returns a 

189 binned statistics result for two dimensions. 

190 

191 Parameters 

192 ---------- 

193 cloud: np.ndarray 

194 Point cloud around which we create an embedding. 

195 sphere_axis: str 

196 Axis of the sphere. This is aligned with the global axis system. 

197 planar: bool 

198 Set to true for planar point clouds and false for higher dimensions. 

199 

200 Returns 

201 ------- 

202 stats: BinnedStatistic2dResult 

203 Returns a 2D histogram of the sphere with bin numbers and bin statistics. 

204 """ 

205 # casting to array because of typing 

206 centroid = np.array(np.mean(cloud, axis=0)) 

207 

208 qhull_options = "" 

209 if planar: 

210 qhull_options = "QJ" 

211 

212 hull = ConvexHull(cloud, qhull_options=qhull_options) 

213 

214 # we need to determine the largest distance in this point 

215 # cloud, so we can give the sphere a dimension 

216 # we can also create a sphere of random size but this could 

217 # skew the results 

218 dist = np.linalg.norm(hull.max_bound - hull.min_bound) 

219 

220 bins_a, bins_b = create_sphere(dist) 

221 

222 cloud_alpha, cloud_beta = to_spherical_coordinates(cloud, centroid, axis=sphere_axis) 

223 

224 return binned_statistic_2d( 

225 cloud_alpha, cloud_beta, None, "count", bins=[bins_a, bins_b], expand_binnumbers=True 

226 )