@@ -13,7 +13,7 @@ defmodule Scholar.Cluster.Hierarchical do
1313 Due to the requirements of the current implementation, only these options are supported:
1414
1515 * `dissimilarity: :euclidean`
16- * `linkage: :average | :complete | :single | :weighted`
16+ * `linkage: :average | :complete | :single | :ward | : weighted`
1717
1818 Our current algorithm is $O(\\ frac{n^2}{p} \\ cdot \\ log(n))$ where $n$ is the number of data points
1919 and $p$ is the number of processors.
@@ -152,14 +152,14 @@ defmodule Scholar.Cluster.Hierarchical do
152152 :complete -> & complete / 6
153153 # :median -> &median/6
154154 :single -> & single / 6
155- # :ward -> &ward/6
155+ :ward -> & ward / 6
156156 :weighted -> & weighted / 6
157157 end
158158
159159 dendrogram_fun =
160160 case linkage do
161- # TODO: :centroid, :median, :ward
162- l when l in [ :average , :complete , :single , :weighted ] ->
161+ # TODO: :centroid, :median
162+ l when l in [ :average , :complete , :single , :ward , : weighted] ->
163163 & parallel_nearest_neighbor / 3
164164 end
165165
@@ -196,10 +196,13 @@ defmodule Scholar.Cluster.Hierarchical do
196196 clades = Nx . broadcast ( - 1 , { n - 1 , 2 } )
197197 sizes = Nx . broadcast ( 1 , { 2 * n - 1 } )
198198 pointers = Nx . broadcast ( - 1 , { 2 * n - 2 } )
199+
200+ cluster_sizes = Nx . broadcast ( 1 , { n } )
199201 diss = Nx . tensor ( :infinity , type: Nx . type ( pairwise ) ) |> Nx . broadcast ( { n - 1 } )
200202
201- { { clades , diss , sizes } , _ } =
202- while { { clades , diss , sizes } , { count = 0 , pointers , pairwise } } , count < n - 1 do
203+ { { clades , diss , sizes , _cluster_sizes } , _ } =
204+ while { { clades , diss , sizes , cluster_sizes } , { count = 0 , pointers , pairwise } } ,
205+ count < n - 1 do
203206 # Indexes of who I am nearest to
204207 nearest = Nx . argmin ( pairwise , axis: 1 )
205208
@@ -213,27 +216,49 @@ defmodule Scholar.Cluster.Hierarchical do
213216 # They are bidirectional but let's keep only one side.
214217 links = Nx . select ( clades_selector and nearest > nearest_of_nearest , nearest , n )
215218
216- { clades , count , pointers , pairwise , diss , sizes } =
217- merge_clades ( clades , count , pointers , pairwise , diss , sizes , links , n , update_fun )
218-
219- { { clades , diss , sizes } , { count , pointers , pairwise } }
219+ { clades , count , pointers , pairwise , diss , sizes , cluster_sizes } =
220+ merge_clades (
221+ clades ,
222+ count ,
223+ pointers ,
224+ pairwise ,
225+ diss ,
226+ sizes ,
227+ cluster_sizes ,
228+ links ,
229+ n ,
230+ update_fun
231+ )
232+
233+ { { clades , diss , sizes , cluster_sizes } , { count , pointers , pairwise } }
220234 end
221235
222236 sizes = sizes [ n .. ( 2 * n - 2 ) ]
223237 perm = Nx . argsort ( diss , stable: false , type: :u32 )
224238 { clades [ perm ] , diss [ perm ] , sizes [ perm ] }
225239 end
226240
227- defnp merge_clades ( clades , count , pointers , pairwise , diss , sizes , links , n , update_fun ) do
228- { { clades , count , pointers , pairwise , diss , sizes } , _ } =
229- while { { clades , count , pointers , pairwise , diss , sizes } , links } ,
241+ defnp merge_clades (
242+ clades ,
243+ count ,
244+ pointers ,
245+ pairwise ,
246+ diss ,
247+ sizes ,
248+ cluster_sizes ,
249+ links ,
250+ n ,
251+ update_fun
252+ ) do
253+ { { clades , count , pointers , pairwise , diss , sizes , cluster_sizes } , _ } =
254+ while { { clades , count , pointers , pairwise , diss , sizes , cluster_sizes } , links } ,
230255 i <- 0 .. ( Nx . size ( links ) - 1 ) do
231256 # i < j because of how links is formed.
232257 # i will become the new clade index and we "infinity-out" j.
233258 j = links [ i ]
234259
235260 if j == n do
236- { { clades , count , pointers , pairwise , diss , sizes } , links }
261+ { { clades , count , pointers , pairwise , diss , sizes , cluster_sizes } , links }
237262 else
238263 # Clades a and b (i and j of pairwise) are being merged into c.
239264 indices = [ i , j ] |> Nx . stack ( ) |> Nx . new_axis ( - 1 )
@@ -251,6 +276,9 @@ defmodule Scholar.Cluster.Hierarchical do
251276 sc = sa + sb
252277 sizes = Nx . indexed_put ( sizes , Nx . stack ( [ i , c ] ) |> Nx . new_axis ( - 1 ) , Nx . stack ( [ sc , sc ] ) )
253278
279+ cluster_sizes =
280+ Nx . indexed_put ( cluster_sizes , Nx . stack ( [ i , j ] ) |> Nx . new_axis ( - 1 ) , Nx . stack ( [ sc , sc ] ) )
281+
254282 # Update dissimilarities
255283 diss = Nx . indexed_put ( diss , Nx . stack ( [ count ] ) , pairwise [ i ] [ j ] )
256284
@@ -259,7 +287,7 @@ defmodule Scholar.Cluster.Hierarchical do
259287
260288 # Update pairwise
261289 updates =
262- update_fun . ( pairwise [ i ] , pairwise [ j ] , pairwise [ i ] [ j ] , sa , sb , sc )
290+ update_fun . ( pairwise [ i ] , pairwise [ j ] , pairwise [ i ] [ j ] , sa , sb , cluster_sizes )
263291 |> Nx . indexed_put ( indices , Nx . broadcast ( :infinity , { 2 } ) )
264292
265293 pairwise =
@@ -269,11 +297,11 @@ defmodule Scholar.Cluster.Hierarchical do
269297 |> Nx . put_slice ( [ j , 0 ] , Nx . broadcast ( :infinity , { 1 , n } ) )
270298 |> Nx . put_slice ( [ 0 , j ] , Nx . broadcast ( :infinity , { n , 1 } ) )
271299
272- { { clades , count + 1 , pointers , pairwise , diss , sizes } , links }
300+ { { clades , count + 1 , pointers , pairwise , diss , sizes , cluster_sizes } , links }
273301 end
274302 end
275303
276- { clades , count , pointers , pairwise , diss , sizes }
304+ { clades , count , pointers , pairwise , diss , sizes , cluster_sizes }
277305 end
278306
279307 defnp find_clade ( pointers , i ) do
@@ -302,8 +330,8 @@ defmodule Scholar.Cluster.Hierarchical do
302330 defnp single ( dac , dbc , _dab , _sa , _sb , _sc ) ,
303331 do: Nx . min ( dac , dbc )
304332
305- # defnp ward(dac, dbc, dab, sa, sb, sc ),
306- # do: Nx.sqrt(((sa + sc ) * dac + (sb + sc ) * dbc - sc * dab) / (sa + sb + sc ))
333+ defnp ward ( dac , dbc , dab , sa , sb , sk ) ,
334+ do: Nx . sqrt ( ( ( sa + sk ) * dac ** 2 + ( sb + sk ) * dbc ** 2 - sk * dab ** 2 ) / ( sa + sb + sk ) )
307335
308336 defnp weighted ( dac , dbc , _dab , _sa , _sb , _sc ) ,
309337 do: ( dac + dbc ) / 2
0 commit comments