crn_clusterizer.h 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764
  1. // File: crn_clusterizer.h
  2. // See Copyright Notice and license at the end of inc/crnlib.h
  3. #pragma once
  4. #include "crn_matrix.h"
  5. namespace crnlib
  6. {
  7. template<typename VectorType>
  8. class clusterizer
  9. {
  10. public:
  11. clusterizer() :
  12. m_overall_variance(0.0f),
  13. m_split_index(0),
  14. m_heap_size(0),
  15. m_quick(false)
  16. {
  17. }
  18. void clear()
  19. {
  20. m_training_vecs.clear();
  21. m_codebook.clear();
  22. m_nodes.clear();
  23. m_overall_variance = 0.0f;
  24. m_split_index = 0;
  25. m_heap_size = 0;
  26. m_quick = false;
  27. }
  28. void reserve_training_vecs(uint num_expected)
  29. {
  30. m_training_vecs.reserve(num_expected);
  31. }
  32. void add_training_vec(const VectorType& v, uint weight)
  33. {
  34. m_training_vecs.push_back( std::make_pair(v, weight) );
  35. }
  36. typedef bool (*progress_callback_func_ptr)(uint percentage_completed, void* pData);
  37. bool generate_codebook(uint max_size, progress_callback_func_ptr pProgress_callback = NULL, void* pProgress_data = NULL, bool quick = false)
  38. {
  39. if (m_training_vecs.empty())
  40. return false;
  41. m_quick = quick;
  42. double ttsum = 0.0f;
  43. vq_node root;
  44. root.m_vectors.reserve(m_training_vecs.size());
  45. for (uint i = 0; i < m_training_vecs.size(); i++)
  46. {
  47. const VectorType& v = m_training_vecs[i].first;
  48. const uint weight = m_training_vecs[i].second;
  49. root.m_centroid += (v * (float)weight);
  50. root.m_total_weight += weight;
  51. root.m_vectors.push_back(i);
  52. ttsum += v.dot(v) * weight;
  53. }
  54. root.m_variance = (float)(ttsum - (root.m_centroid.dot(root.m_centroid) / root.m_total_weight));
  55. root.m_centroid *= (1.0f / root.m_total_weight);
  56. m_nodes.clear();
  57. m_nodes.reserve(max_size * 2 + 1);
  58. m_nodes.push_back(root);
  59. m_heap.resize(max_size + 1);
  60. m_heap[1] = 0;
  61. m_heap_size = 1;
  62. m_split_index = 0;
  63. uint total_leaves = 1;
  64. m_left_children.reserve(m_training_vecs.size() + 1);
  65. m_right_children.reserve(m_training_vecs.size() + 1);
  66. int prev_percentage = -1;
  67. while ((total_leaves < max_size) && (m_heap_size))
  68. {
  69. int worst_node_index = m_heap[1];
  70. m_heap[1] = m_heap[m_heap_size];
  71. m_heap_size--;
  72. if (m_heap_size)
  73. down_heap(1);
  74. split_node(worst_node_index);
  75. total_leaves++;
  76. if ((pProgress_callback) && ((total_leaves & 63) == 0) && (max_size))
  77. {
  78. int cur_percentage = (total_leaves * 100U + (max_size / 2U)) / max_size;
  79. if (cur_percentage != prev_percentage)
  80. {
  81. if (!(*pProgress_callback)(cur_percentage, pProgress_data))
  82. return false;
  83. prev_percentage = cur_percentage;
  84. }
  85. }
  86. }
  87. m_codebook.clear();
  88. m_overall_variance = 0.0f;
  89. for (uint i = 0; i < m_nodes.size(); i++)
  90. {
  91. vq_node& node = m_nodes[i];
  92. if (node.m_left != -1)
  93. {
  94. CRNLIB_ASSERT(node.m_right != -1);
  95. continue;
  96. }
  97. CRNLIB_ASSERT((node.m_left == -1) && (node.m_right == -1));
  98. node.m_codebook_index = m_codebook.size();
  99. m_codebook.push_back(node.m_centroid);
  100. m_overall_variance += node.m_variance;
  101. }
  102. m_heap.clear();
  103. m_left_children.clear();
  104. m_right_children.clear();
  105. return true;
  106. }
  107. inline uint get_num_training_vecs() const { return m_training_vecs.size(); }
  108. const VectorType& get_training_vec(uint index) const { return m_training_vecs[index].first; }
  109. uint get_training_vec_weight(uint index) const { return m_training_vecs[index].second; }
  110. typedef crnlib::vector< std::pair<VectorType, uint> > training_vec_array;
  111. const training_vec_array& get_training_vecs() const { return m_training_vecs; }
  112. training_vec_array& get_training_vecs() { return m_training_vecs; }
  113. inline float get_overall_variance() const { return m_overall_variance; }
  114. inline uint get_codebook_size() const
  115. {
  116. return m_codebook.size();
  117. }
  118. inline const VectorType& get_codebook_entry(uint index) const
  119. {
  120. return m_codebook[index];
  121. }
  122. VectorType& get_codebook_entry(uint index)
  123. {
  124. return m_codebook[index];
  125. }
  126. typedef crnlib::vector<VectorType> vector_vec_type;
  127. inline const vector_vec_type& get_codebook() const
  128. {
  129. return m_codebook;
  130. }
  131. uint find_best_codebook_entry(const VectorType& v) const
  132. {
  133. uint cur_node_index = 0;
  134. for ( ; ; )
  135. {
  136. const vq_node& cur_node = m_nodes[cur_node_index];
  137. if (cur_node.m_left == -1)
  138. return cur_node.m_codebook_index;
  139. const vq_node& left_node = m_nodes[cur_node.m_left];
  140. const vq_node& right_node = m_nodes[cur_node.m_right];
  141. float left_dist = left_node.m_centroid.squared_distance(v);
  142. float right_dist = right_node.m_centroid.squared_distance(v);
  143. if (left_dist < right_dist)
  144. cur_node_index = cur_node.m_left;
  145. else
  146. cur_node_index = cur_node.m_right;
  147. }
  148. }
  149. const VectorType& find_best_codebook_entry(const VectorType& v, uint max_codebook_size) const
  150. {
  151. uint cur_node_index = 0;
  152. for ( ; ; )
  153. {
  154. const vq_node& cur_node = m_nodes[cur_node_index];
  155. if ((cur_node.m_left == -1) || ((cur_node.m_codebook_index + 1) >= (int)max_codebook_size))
  156. return cur_node.m_centroid;
  157. const vq_node& left_node = m_nodes[cur_node.m_left];
  158. const vq_node& right_node = m_nodes[cur_node.m_right];
  159. float left_dist = left_node.m_centroid.squared_distance(v);
  160. float right_dist = right_node.m_centroid.squared_distance(v);
  161. if (left_dist < right_dist)
  162. cur_node_index = cur_node.m_left;
  163. else
  164. cur_node_index = cur_node.m_right;
  165. }
  166. }
  167. uint find_best_codebook_entry_fs(const VectorType& v) const
  168. {
  169. float best_dist = math::cNearlyInfinite;
  170. uint best_index = 0;
  171. for (uint i = 0; i < m_codebook.size(); i++)
  172. {
  173. float dist = m_codebook[i].squared_distance(v);
  174. if (dist < best_dist)
  175. {
  176. best_dist = dist;
  177. best_index = i;
  178. if (best_dist == 0.0f)
  179. break;
  180. }
  181. }
  182. return best_index;
  183. }
  184. void retrieve_clusters(uint max_clusters, crnlib::vector< crnlib::vector<uint> >& clusters) const
  185. {
  186. clusters.resize(0);
  187. clusters.reserve(max_clusters);
  188. crnlib::vector<uint> stack;
  189. stack.reserve(512);
  190. uint cur_node_index = 0;
  191. for ( ; ; )
  192. {
  193. const vq_node& cur_node = m_nodes[cur_node_index];
  194. if ( (cur_node.is_leaf()) || ((cur_node.m_codebook_index + 2) > (int)max_clusters) )
  195. {
  196. clusters.resize(clusters.size() + 1);
  197. clusters.back() = cur_node.m_vectors;
  198. if (stack.empty())
  199. break;
  200. cur_node_index = stack.back();
  201. stack.pop_back();
  202. continue;
  203. }
  204. cur_node_index = cur_node.m_left;
  205. stack.push_back(cur_node.m_right);
  206. }
  207. }
  208. private:
  209. training_vec_array m_training_vecs;
  210. struct vq_node
  211. {
  212. vq_node() : m_centroid(cClear), m_total_weight(0), m_left(-1), m_right(-1), m_codebook_index(-1), m_unsplittable(false) { }
  213. VectorType m_centroid;
  214. uint64 m_total_weight;
  215. float m_variance;
  216. crnlib::vector<uint> m_vectors;
  217. int m_left;
  218. int m_right;
  219. int m_codebook_index;
  220. bool m_unsplittable;
  221. bool is_leaf() const { return m_left < 0; }
  222. };
  223. typedef crnlib::vector<vq_node> node_vec_type;
  224. node_vec_type m_nodes;
  225. vector_vec_type m_codebook;
  226. float m_overall_variance;
  227. uint m_split_index;
  228. crnlib::vector<uint> m_heap;
  229. uint m_heap_size;
  230. bool m_quick;
  231. void insert_heap(uint node_index)
  232. {
  233. const float variance = m_nodes[node_index].m_variance;
  234. uint pos = ++m_heap_size;
  235. if (m_heap_size >= m_heap.size())
  236. m_heap.resize(m_heap_size + 1);
  237. for ( ; ; )
  238. {
  239. uint parent = pos >> 1;
  240. if (!parent)
  241. break;
  242. float parent_variance = m_nodes[m_heap[parent]].m_variance;
  243. if (parent_variance > variance)
  244. break;
  245. m_heap[pos] = m_heap[parent];
  246. pos = parent;
  247. }
  248. m_heap[pos] = node_index;
  249. }
  250. void down_heap(uint pos)
  251. {
  252. uint child;
  253. uint orig = m_heap[pos];
  254. const float orig_variance = m_nodes[orig].m_variance;
  255. while ((child = (pos << 1)) <= m_heap_size)
  256. {
  257. if (child < m_heap_size)
  258. {
  259. if (m_nodes[m_heap[child]].m_variance < m_nodes[m_heap[child + 1]].m_variance)
  260. child++;
  261. }
  262. if (orig_variance > m_nodes[m_heap[child]].m_variance)
  263. break;
  264. m_heap[pos] = m_heap[child];
  265. pos = child;
  266. }
  267. m_heap[pos] = orig;
  268. }
  269. void compute_split_estimate(VectorType& left_child_res, VectorType& right_child_res, const vq_node& parent_node)
  270. {
  271. VectorType furthest(0);
  272. double furthest_dist = -1.0f;
  273. for (uint i = 0; i < parent_node.m_vectors.size(); i++)
  274. {
  275. const VectorType& v = m_training_vecs[parent_node.m_vectors[i]].first;
  276. double dist = v.squared_distance(parent_node.m_centroid);
  277. if (dist > furthest_dist)
  278. {
  279. furthest_dist = dist;
  280. furthest = v;
  281. }
  282. }
  283. VectorType opposite(0);
  284. double opposite_dist = -1.0f;
  285. for (uint i = 0; i < parent_node.m_vectors.size(); i++)
  286. {
  287. const VectorType& v = m_training_vecs[parent_node.m_vectors[i]].first;
  288. double dist = v.squared_distance(furthest);
  289. if (dist > opposite_dist)
  290. {
  291. opposite_dist = dist;
  292. opposite = v;
  293. }
  294. }
  295. left_child_res = (furthest + parent_node.m_centroid) * .5f;
  296. right_child_res = (opposite + parent_node.m_centroid) * .5f;
  297. }
  298. void compute_split_pca(VectorType& left_child_res, VectorType& right_child_res, const vq_node& parent_node)
  299. {
  300. if (parent_node.m_vectors.size() == 2)
  301. {
  302. left_child_res = m_training_vecs[parent_node.m_vectors[0]].first;
  303. right_child_res = m_training_vecs[parent_node.m_vectors[1]].first;
  304. return;
  305. }
  306. const uint N = VectorType::num_elements;
  307. matrix<N, N, float> covar;
  308. covar.clear();
  309. for (uint i = 0; i < parent_node.m_vectors.size(); i++)
  310. {
  311. const VectorType v(m_training_vecs[parent_node.m_vectors[i]].first - parent_node.m_centroid);
  312. const VectorType w(v * (float)m_training_vecs[parent_node.m_vectors[i]].second);
  313. for (uint x = 0; x < N; x++)
  314. for (uint y = x; y < N; y++)
  315. covar[x][y] = covar[x][y] + v[x] * w[y];
  316. }
  317. float one_over_total_weight = 1.0f / parent_node.m_total_weight;
  318. for (uint x = 0; x < N; x++)
  319. for (uint y = x; y < N; y++)
  320. covar[x][y] *= one_over_total_weight;
  321. for (uint x = 0; x < (N - 1); x++)
  322. for (uint y = x + 1; y < N; y++)
  323. covar[y][x] = covar[x][y];
  324. VectorType axis;//(1.0f);
  325. if (N == 1)
  326. axis.set(1.0f);
  327. else
  328. {
  329. for (uint i = 0; i < N; i++)
  330. axis[i] = math::lerp(.75f, 1.25f, i * (1.0f / math::maximum<int>(N - 1, 1)));
  331. }
  332. VectorType prev_axis(axis);
  333. for (uint iter = 0; iter < 10; iter++)
  334. {
  335. VectorType x;
  336. double max_sum = 0;
  337. for (uint i = 0; i < N; i++)
  338. {
  339. double sum = 0;
  340. for (uint j = 0; j < N; j++)
  341. sum += axis[j] * covar[i][j];
  342. x[i] = static_cast<float>(sum);
  343. max_sum = math::maximum(max_sum, fabs(sum));
  344. }
  345. if (max_sum != 0.0f)
  346. x *= static_cast<float>(1.0f / max_sum);
  347. VectorType delta_axis(prev_axis - x);
  348. prev_axis = axis;
  349. axis = x;
  350. if (delta_axis.norm() < .0025f)
  351. break;
  352. }
  353. axis.normalize();
  354. VectorType left_child(0.0f);
  355. VectorType right_child(0.0f);
  356. double left_weight = 0.0f;
  357. double right_weight = 0.0f;
  358. for (uint i = 0; i < parent_node.m_vectors.size(); i++)
  359. {
  360. const float weight = (float)m_training_vecs[parent_node.m_vectors[i]].second;
  361. const VectorType& v = m_training_vecs[parent_node.m_vectors[i]].first;
  362. double t = (v - parent_node.m_centroid) * axis;
  363. if (t < 0.0f)
  364. {
  365. left_child += v * weight;
  366. left_weight += weight;
  367. }
  368. else
  369. {
  370. right_child += v * weight;
  371. right_weight += weight;
  372. }
  373. }
  374. if ((left_weight > 0.0f) && (right_weight > 0.0f))
  375. {
  376. left_child_res = left_child * (float)(1.0f / left_weight);
  377. right_child_res = right_child * (float)(1.0f / right_weight);
  378. }
  379. else
  380. {
  381. compute_split_estimate(left_child_res, right_child_res, parent_node);
  382. }
  383. }
  384. #if 0
  385. void compute_split_pca2(VectorType& left_child_res, VectorType& right_child_res, const vq_node& parent_node)
  386. {
  387. if (parent_node.m_vectors.size() == 2)
  388. {
  389. left_child_res = m_training_vecs[parent_node.m_vectors[0]].first;
  390. right_child_res = m_training_vecs[parent_node.m_vectors[1]].first;
  391. return;
  392. }
  393. const uint N = VectorType::num_elements;
  394. VectorType furthest;
  395. double furthest_dist = -1.0f;
  396. for (uint i = 0; i < parent_node.m_vectors.size(); i++)
  397. {
  398. const VectorType& v = m_training_vecs[parent_node.m_vectors[i]].first;
  399. double dist = v.squared_distance(parent_node.m_centroid);
  400. if (dist > furthest_dist)
  401. {
  402. furthest_dist = dist;
  403. furthest = v;
  404. }
  405. }
  406. VectorType opposite;
  407. double opposite_dist = -1.0f;
  408. for (uint i = 0; i < parent_node.m_vectors.size(); i++)
  409. {
  410. const VectorType& v = m_training_vecs[parent_node.m_vectors[i]].first;
  411. double dist = v.squared_distance(furthest);
  412. if (dist > opposite_dist)
  413. {
  414. opposite_dist = dist;
  415. opposite = v;
  416. }
  417. }
  418. VectorType axis(opposite - furthest);
  419. if (axis.normalize() < .000125f)
  420. {
  421. left_child_res = (furthest + parent_node.m_centroid) * .5f;
  422. right_child_res = (opposite + parent_node.m_centroid) * .5f;
  423. return;
  424. }
  425. for (uint iter = 0; iter < 2; iter++)
  426. {
  427. double next_axis[N];
  428. utils::zero_object(next_axis);
  429. for (uint i = 0; i < parent_node.m_vectors.size(); i++)
  430. {
  431. const double weight = m_training_vecs[parent_node.m_vectors[i]].second;
  432. VectorType v(m_training_vecs[parent_node.m_vectors[i]].first - parent_node.m_centroid);
  433. double dot = (v * axis) * weight;
  434. for (uint j = 0; j < N; j++)
  435. next_axis[j] += dot * v[j];
  436. }
  437. double w = 0.0f;
  438. for (uint j = 0; j < N; j++)
  439. w += next_axis[j] * next_axis[j];
  440. if (w > 0.0f)
  441. {
  442. w = 1.0f / sqrt(w);
  443. for (uint j = 0; j < N; j++)
  444. axis[j] = static_cast<float>(next_axis[j] * w);
  445. }
  446. else
  447. break;
  448. }
  449. VectorType left_child(0.0f);
  450. VectorType right_child(0.0f);
  451. double left_weight = 0.0f;
  452. double right_weight = 0.0f;
  453. for (uint i = 0; i < parent_node.m_vectors.size(); i++)
  454. {
  455. const float weight = (float)m_training_vecs[parent_node.m_vectors[i]].second;
  456. const VectorType& v = m_training_vecs[parent_node.m_vectors[i]].first;
  457. double t = (v - parent_node.m_centroid) * axis;
  458. if (t < 0.0f)
  459. {
  460. left_child += v * weight;
  461. left_weight += weight;
  462. }
  463. else
  464. {
  465. right_child += v * weight;
  466. right_weight += weight;
  467. }
  468. }
  469. if ((left_weight > 0.0f) && (right_weight > 0.0f))
  470. {
  471. left_child_res = left_child * (float)(1.0f / left_weight);
  472. right_child_res = right_child * (float)(1.0f / right_weight);
  473. }
  474. else
  475. {
  476. left_child_res = (furthest + parent_node.m_centroid) * .5f;
  477. right_child_res = (opposite + parent_node.m_centroid) * .5f;
  478. }
  479. }
  480. #endif
  481. // thread safety warning: shared state!
  482. crnlib::vector<uint> m_left_children;
  483. crnlib::vector<uint> m_right_children;
  484. void split_node(uint index)
  485. {
  486. vq_node& parent_node = m_nodes[index];
  487. if (parent_node.m_vectors.size() == 1)
  488. return;
  489. VectorType left_child, right_child;
  490. if (m_quick)
  491. compute_split_estimate(left_child, right_child, parent_node);
  492. else
  493. compute_split_pca(left_child, right_child, parent_node);
  494. uint64 left_weight = 0;
  495. uint64 right_weight = 0;
  496. float prev_total_variance = 1e+10f;
  497. float left_variance = 0.0f;
  498. float right_variance = 0.0f;
  499. const uint cMaxLoops = m_quick ? 2 : 8;
  500. for (uint total_loops = 0; total_loops < cMaxLoops; total_loops++)
  501. {
  502. m_left_children.resize(0);
  503. m_right_children.resize(0);
  504. VectorType new_left_child(cClear);
  505. VectorType new_right_child(cClear);
  506. double left_ttsum = 0.0f;
  507. double right_ttsum = 0.0f;
  508. left_weight = 0;
  509. right_weight = 0;
  510. for (uint i = 0; i < parent_node.m_vectors.size(); i++)
  511. {
  512. const VectorType& v = m_training_vecs[parent_node.m_vectors[i]].first;
  513. const uint weight = m_training_vecs[parent_node.m_vectors[i]].second;
  514. double left_dist2 = left_child.squared_distance(v);
  515. double right_dist2 = right_child.squared_distance(v);
  516. if (left_dist2 < right_dist2)
  517. {
  518. m_left_children.push_back(parent_node.m_vectors[i]);
  519. new_left_child += (v * (float)weight);
  520. left_weight += weight;
  521. left_ttsum += v.dot(v) * weight;
  522. }
  523. else
  524. {
  525. m_right_children.push_back(parent_node.m_vectors[i]);
  526. new_right_child += (v * (float)weight);
  527. right_weight += weight;
  528. right_ttsum += v.dot(v) * weight;
  529. }
  530. }
  531. if ((!left_weight) || (!right_weight))
  532. {
  533. parent_node.m_unsplittable = true;
  534. return;
  535. }
  536. left_variance = (float)(left_ttsum - (new_left_child.dot(new_left_child) / left_weight));
  537. right_variance = (float)(right_ttsum - (new_right_child.dot(new_right_child) / right_weight));
  538. new_left_child *= (1.0f / left_weight);
  539. new_right_child *= (1.0f / right_weight);
  540. left_child = new_left_child;
  541. left_weight = left_weight;
  542. right_child = new_right_child;
  543. right_weight = right_weight;
  544. float total_variance = left_variance + right_variance;
  545. if (total_variance < .00001f)
  546. break;
  547. //const float variance_delta_thresh = .00001f;
  548. const float variance_delta_thresh = .00125f;
  549. if (((prev_total_variance - total_variance) / total_variance) < variance_delta_thresh)
  550. break;
  551. prev_total_variance = total_variance;
  552. }
  553. const uint left_child_index = m_nodes.size();
  554. const uint right_child_index = m_nodes.size() + 1;
  555. parent_node.m_left = m_nodes.size();
  556. parent_node.m_right = m_nodes.size() + 1;
  557. parent_node.m_codebook_index = m_split_index;
  558. m_split_index++;
  559. m_nodes.resize(m_nodes.size() + 2);
  560. // parent_node is invalid now, because m_nodes has been changed
  561. vq_node& left_child_node = m_nodes[left_child_index];
  562. vq_node& right_child_node = m_nodes[right_child_index];
  563. left_child_node.m_centroid = left_child;
  564. left_child_node.m_total_weight = left_weight;
  565. left_child_node.m_vectors.swap(m_left_children);
  566. left_child_node.m_variance = left_variance;
  567. if ((left_child_node.m_vectors.size() > 1) && (left_child_node.m_variance > 0.0f))
  568. insert_heap(left_child_index);
  569. right_child_node.m_centroid = right_child;
  570. right_child_node.m_total_weight = right_weight;
  571. right_child_node.m_vectors.swap(m_right_children);
  572. right_child_node.m_variance = right_variance;
  573. if ((right_child_node.m_vectors.size() > 1) && (right_child_node.m_variance > 0.0f))
  574. insert_heap(right_child_index);
  575. }
  576. };
  577. } // namespace crnlib