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
« prev ^ index » next coverage.py v7.2.4, created at 2023-04-28 18:42 +0100
1import numpy as np
3from sklearn.preprocessing import normalize
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
13def to_spherical_coordinates(points: np.ndarray, centroid: np.ndarray, axis: str = "Z"):
14 """Converts the points to spherical coordinates.
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.
25 Returns
26 -------
27 az : np.ndarray
28 Azimuthal angle vector.
29 po: np.ndarray
30 Polar angle vector.
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
44 # vectors from centroid to points
45 vec = points - centroid
46 vec = normalize(vec, axis=1, norm="l2")
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
55 # polar angles
56 po = np.arccos(vec[:, indexes[2]])
58 return az, po
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.
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
73 Returns
74 -------
75 hashes: np.ndarray
76 The hashing result of all points mapped to an embedding space.
78 """
79 bin_n = histo.binnumber
81 assert len(bin_n[0] == len(field))
83 # get dims of the embedding space
84 n_rows = histo.statistic.shape[0]
85 n_cols = histo.statistic.shape[1]
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
94 # allocate arrays
95 bin_count = np.zeros((n_rows, n_cols))
96 hashes = np.zeros((n_rows, n_cols))
98 # sum all the field values to each bin
99 hashes[binx[:], biny[:]] += field[:]
100 bin_count[binx[:], biny[:]] += 1
102 hashes = hashes.flatten()
103 bin_count = bin_count.flatten()
105 # exclude all zero entries
106 nonzero_inds = np.where(bin_count != 0)
108 # average the fields
109 hashes[nonzero_inds] /= bin_count[nonzero_inds]
111 return hashes
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.
119 Parameters
120 ----------
121 diameter:
122 Diameter of the sphere.
124 Returns
125 -------
126 bin_beta: np.ndarray
127 Bin bounds for the beta angles.
129 bin_alpha: np.ndarray
130 Bin bounds for the alpha angles.
132 """
133 # number of partitions for equator
134 n_alpha = 145
135 # number of partitions for longitude
136 n_beta = 144
138 r = diameter / 2.0
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
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)
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
156 bin_beta = np.arccos(bin_beta)
157 return bin_alpha, bin_beta
160def compute_similarity(embeddings: np.ndarray) -> np.ndarray:
161 """Computes the similarity of each embedding.
163 Parameters
164 ----------
165 embeddings: np.ndarray
166 Model embeddings.
168 Returns
169 -------
170 smatrix: np.ndarray
171 Similarity matrix.
172 """
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 )
182 return smatrix
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.
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.
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))
208 qhull_options = ""
209 if planar:
210 qhull_options = "QJ"
212 hull = ConvexHull(cloud, qhull_options=qhull_options)
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)
220 bins_a, bins_b = create_sphere(dist)
222 cloud_alpha, cloud_beta = to_spherical_coordinates(cloud, centroid, axis=sphere_axis)
224 return binned_statistic_2d(
225 cloud_alpha, cloud_beta, None, "count", bins=[bins_a, bins_b], expand_binnumbers=True
226 )