Rosetta 3.5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
MatchSet.cc
Go to the documentation of this file.
1 // -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*-
2 // vi: set ts=2 noet:
3 // :noTabs=false:tabSize=4:indentSize=4:
4 //
5 // (c) Copyright Rosetta Commons Member Institutions.
6 // (c) This file is part of the Rosetta software suite and is made available under license.
7 // (c) The Rosetta software is developed by the contributing members of the Rosetta Commons.
8 // (c) For more information, see http://www.rosettacommons.org. Questions about this can be
9 // (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu.
10 
11 /// @file protocols/match/MatchSet.cc
12 /// @brief
13 /// @author Alex Zanghellini (zanghell@u.washington.edu)
14 /// @author Andrew Leaver-Fay (aleaverfay@gmail.com), porting to mini
15 
16 // Unit headers
18 
19 // Project headers
21 
22 // Utility headers
23 #include <utility/LexicographicalIterator.hh>
24 #include <utility/FixedSizeLexicographicalIterator.hh>
25 #include <utility/FixedSizeLexicographicalIterator.tmpl.hh>
26 
27 #include <protocols/match/Hit.hh>
28 #include <utility/vector1.hh>
29 #include <set>
30 
31 namespace protocols {
32 namespace match {
33 
34 /// @brief Non-member function that handles the logic for finding a nearby bin in 6D when
35 /// taking a step of a specified size from an existing bin.
38  Real6 orig_point,
39  Real6 steps, // 0 if no step, non-zero if some real step
40  numeric::geometry::hashing::SixDCoordinateBinner const & binner,
41  numeric::geometry::hashing::Bin6D & next_bin
42 );
43 
44 
45 HitHasher::HitHasher() : initialized_( false ) {}
47 
48 void
50  BoundingBox const & bb
51 )
52 {
53  assert( ! initialized_ );
54  bb_ = bb;
55 }
56 
57 void
59 {
60  assert( ! initialized_ );
61  assert( bin_width > 0 );
62  for ( Size ii = 1; ii <= 3; ++ii ) { xyz_bin_widths_[ ii ] = bin_width; }
63 }
64 
65 void
67 {
68  assert( ! initialized_ );
69  assert( bin_width_degrees > 0 );
70  for ( Size ii = 1; ii <= 3; ++ii ) { euler_bin_widths_[ ii ] = bin_width_degrees; }
71 }
72 
73 void
75 {
76  assert( ! initialized_ );
77  for ( Size ii = 1; ii <= 3; ++ii ) {
78  assert( bin_widths( ii ) > 0 );
79  xyz_bin_widths_[ ii ] = bin_widths( ii );
80  }
81 
82 }
83 
84 void
85 HitHasher::set_euler_bin_widths( Vector const & euler_bin_widths )
86 {
87  assert( ! initialized_ );
88  for ( Size ii = 1; ii <= 3; ++ii ) {
89  assert( euler_bin_widths( ii ) > 0 );
90  euler_bin_widths_[ ii ] = euler_bin_widths( ii );
91  }
92 }
93 
94 void
95 HitHasher::set_nhits_per_match( Size num_geometric_constraints )
96 {
97  assert( ! initialized_ );
98  n_geometric_constraints_per_match_ = num_geometric_constraints;
99 }
100 
101 void
103 {
104  assert( ! initialized_ );
105 
106  initialized_ = true;
107 
108  hit_hashes_.resize( N_HASH_MAPS );
109 
110  /// Data for initializing the lower corner in xyz space
111  Vector xyz_lower;
112  Real3 half_xyzbin_widths = xyz_bin_widths_;
113  for ( Size ii = 1; ii <= 3; ++ii ) { half_xyzbin_widths[ ii ] *= 0.5; }
114 
115  /// Data for initializing the lower corner in Euler space
116  Size3 euler_offsets;
117  utility::vector1< Size > six_twos( 6, 2 );
118  utility::LexicographicalIterator lex( six_twos );
119 
120  Real6 bin_widths;
121  bin_widths[ 1 ] = xyz_bin_widths_[ 1 ]; bin_widths[ 2 ] = xyz_bin_widths_[ 2 ]; bin_widths[ 3 ] = xyz_bin_widths_[ 3 ];
122  bin_widths[ 4 ] = euler_bin_widths_[ 1 ]; bin_widths[ 5 ] = euler_bin_widths_[ 2 ]; bin_widths[ 6 ] = euler_bin_widths_[ 3 ];
123 
124  Size count = 1;
125 
126  //std::cout << "HitHasher initialize:";
127  //for ( Size ii = 1; ii <= 6; ++ii ) std::cout << bin_widths[ ii ] << " "; std::cout << std::endl;
128 
129  while ( ! lex.at_end() ) {
130 
131  for ( Size ii = 1; ii <= 3; ++ii ) {
132  xyz_lower( ii ) = bb_.lower()( ii ) + ( lex[ ii ] - 1 ) * ( half_xyzbin_widths[ ii ] );
133  }
134  for ( Size ii = 1, iip3 = 4; ii <= 3; ++ii, ++iip3 ) {
135  euler_offsets[ ii ] = ( lex[ iip3 ] - 1 ); // 1 or 0 for offset or not.
136  }
137 
138  //std::cout << "bounding box " << count << " lower: ";
139  //for ( Size ii = 1; ii <= 3; ++ii ) std::cout << xyz_lower( ii ) << " ";
140  //std::cout << "upper: ";
141  //for ( Size ii = 1; ii <= 3; ++ii ) std::cout << bb_.upper()( ii ) << " ";
142  //for ( Size ii = 1; ii <= 3; ++ii ) std::cout << euler_offsets[ ii ] << " "; std::cout << std::endl;
143 
144  BoundingBox bb( xyz_lower, bb_.upper() );
145 
146  hit_hashes_[ count ].first = new numeric::geometry::hashing::SixDCoordinateBinner( bb, euler_offsets, bin_widths );
147  ++lex;
148  ++count;
149  }
150 }
151 
152 void
153 HitHasher::insert_hit( Size geometric_constraint_id, Hit const * hit )
154 {
155  runtime_assert( initialized_ );
156  runtime_assert( hit );
157  Real6 const & geom( hit->second() );
158  Vector point( Vector( geom[ 1 ], geom[ 2 ], geom[ 3 ] ));
159  if ( ! bb_.contains( point ) ) {
160  utility_exit_with_message( "ERROR: Attempted to insert hit into HitHasher outside of the HitHasher bounding box!" );
161  }
162 
163  for ( Size ii = 1; ii <= N_HASH_MAPS; ++ii ) {
164  if ( hit_hashes_[ ii ].first->contains( geom ) ) {
165  boost::uint64_t bin_index = hit_hashes_[ ii ].first->bin_index( geom );
166  HitHash::iterator iter = hit_hashes_[ ii ].second.find( bin_index );
167  if ( iter == hit_hashes_[ ii ].second.end() ) {
168 
169  /*if ( geometric_constraint_id != 1 ) {
170  std::cout << "Weird -- this bin index " << bin_index << " should already have been inserted to hash " << ii;
171  for ( Size jj = 1; jj <= 6; ++jj ) std::cout << " " << geom[ jj ]; std::cout << std::endl;
172  } else {
173  std::cout << "Inserting bin index: " << bin_index << " into hash " << ii << std::endl;
174  }*/
175 
177  ms[ geometric_constraint_id ].push_back( hit );
178  hit_hashes_[ ii ].second.insert( std::make_pair( bin_index, ms ));
179  } else {
180  iter->second[ geometric_constraint_id ].push_back( hit );
181  }
182  }
183  }
184 }
185 
186 
187 /// @brief Insert a hits into a particular hash maps
188 void
189 HitHasher::insert_hit( Size which_hash_map, Size geometric_constraint_id, Hit const * hit )
190 {
191  runtime_assert( initialized_ );
192  runtime_assert( hit );
193  Real6 const & geom( hit->second() );
194  Vector point( Vector( geom[ 1 ], geom[ 2 ], geom[ 3 ] ));
195  if ( ! bb_.contains( point ) ) {
196  std::cerr << "Weird: Attempted to insert hit into HitHasher outside of the HitHasher bounding box!" << std::endl;
197  std::cerr << "point: " << point.x() << " " << point.y() << " " << point.z() << std::endl;
198  std::cerr << "bb: lower "<< bb_.lower().x() << " " << bb_.lower().y() << " " << bb_.lower().z() << std::endl;
199  std::cerr << "bb: upper "<< bb_.upper().x() << " " << bb_.upper().y() << " " << bb_.upper().z() << std::endl;
200  return;
201  //utility_exit_with_message( "ERROR: Attempted to insert hit into HitHasher outside of the HitHasher bounding box!" );
202  }
203 
204  if ( hit_hashes_[ which_hash_map ].first->contains( geom ) ) {
205  boost::uint64_t bin_index = hit_hashes_[ which_hash_map ].first->bin_index( geom );
206  HitHash::iterator iter = hit_hashes_[ which_hash_map ].second.find( bin_index );
207  if ( iter == hit_hashes_[ which_hash_map ].second.end() ) {
208 
209  /*if ( geometric_constraint_id != 1 ) {
210  std::cout << "Weird -- this bin index " << bin_index << " should already have been inserted to hash " << which_hash_map;
211  for ( Size jj = 1; jj <= 6; ++jj ) std::cout << " " << geom[ jj ]; std::cout << std::endl;
212  } else {
213  std::cout << "Inserting bin index: " << bin_index << " into hash " << which_hash_map << std::endl;
214  }*/
215 
217  ms[ geometric_constraint_id ].push_back( hit );
218  hit_hashes_[ which_hash_map ].second.insert( std::make_pair( bin_index, ms ));
219  } else {
220  iter->second[ geometric_constraint_id ].push_back( hit );
221  }
222  }
223 }
224 
225 void
227 {
228  hit_hashes_[ which_hash_map ].second.clear();
229 }
230 
231 HitHasher::HitHash::const_iterator
232 HitHasher::hit_hash_begin( Size which_hash_map ) const
233 {
234  return hit_hashes_[ which_hash_map ].second.begin();
235 }
236 
237 HitHasher::HitHash::const_iterator
238 HitHasher::hit_hash_end( Size which_hash_map ) const
239 {
240  return hit_hashes_[ which_hash_map ].second.end();
241 }
242 
243 //////////////////////////////////////////////////////////////////////
244 HitNeighborFinder::HitNeighborFinder() : initialized_( false ) {}
246 
247 void
249  BoundingBox const & bb
250 )
251 {
252  assert( ! initialized_ );
253  bb_ = bb;
254 }
255 
256 void
258 {
259  assert( ! initialized_ );
260  assert( bin_width > 0 );
261  for ( Size ii = 1; ii <= 3; ++ii ) { xyz_bin_widths_[ ii ] = bin_width; }
262 }
263 
264 void
266 {
267  assert( ! initialized_ );
268  assert( bin_width_degrees > 0 );
269  for ( Size ii = 1; ii <= 3; ++ii ) { euler_bin_widths_[ ii ] = bin_width_degrees; }
270 }
271 
272 void
274 {
275  assert( ! initialized_ );
276  for ( Size ii = 1; ii <= 3; ++ii ) {
277  assert( bin_widths( ii ) > 0 );
278  xyz_bin_widths_[ ii ] = bin_widths( ii );
279  }
280 
281 }
282 
283 void
285 {
286  assert( ! initialized_ );
287  for ( Size ii = 1; ii <= 3; ++ii ) {
288  assert( euler_bin_widths( ii ) > 0 );
289  euler_bin_widths_[ ii ] = euler_bin_widths( ii );
290  }
291 }
292 
293 void HitNeighborFinder::add_hits( std::list< Hit > const & hitlist )
294 {
295  assert( all_hits_.empty() );
296  assert( initialized_ );
297 
298  Size count_hits = 0;
299  for ( std::list< Hit >::const_iterator iter = hitlist.begin(), iter_end = hitlist.end(); iter != iter_end; ++iter ) {
300  ++count_hits;
301  all_hits_.push_back( std::make_pair( count_hits, & ( *iter ) ));
302  }
303  hash_hits();
304 }
305 
306 void HitNeighborFinder::add_hits( HitPtrList const & /*hitptrlist*/ )
307 {}
308 
309 
310 void
312 {
313  assert( ! initialized_ );
314 
315  initialized_ = true;
316 
317  /// Set all euler-offsets to 0 (no offset).
318  Size3 euler_offsets( 0 );
319 
320  Real6 bin_widths;
321  bin_widths[ 1 ] = xyz_bin_widths_[ 1 ]; bin_widths[ 2 ] = xyz_bin_widths_[ 2 ]; bin_widths[ 3 ] = xyz_bin_widths_[ 3 ];
322  bin_widths[ 4 ] = euler_bin_widths_[ 1 ]; bin_widths[ 5 ] = euler_bin_widths_[ 2 ]; bin_widths[ 6 ] = euler_bin_widths_[ 3 ];
323 
324  binner_ = new numeric::geometry::hashing::SixDCoordinateBinner( bb_, euler_offsets, bin_widths );
325 
326 }
327 
328 
331 {
333 
334  // we need to visit all 2^6=64 neighboring bins of the query halfbin; the lexicographical iterator
335  // ensures we visit them all.
336  Bin6D twos( 2 ); // numer of halfbin neighbors for each dimension
337  utility::FixedSizeLexicographicalIterator< 6 > halfbin_lex( twos );
338  Bin6D twopows( 1 );
339  for ( Size ii = 5; ii >= 1; --ii ) twopows[ ii ] = twopows[ ii + 1 ] * 2;
340 
341  //typedef fixedsizearray1< boost::uint64_t, 2 > halfbin_index_touple; // pos1 = bin index; pos2 = halfbin subindex
342  //typedef utility::OrderedTuple<
343 
344  utility::vector1< bool > visited_halfbins( 64, false );
345 
346  for ( HitHash::const_iterator iter = hash_.begin(), iter_end = hash_.end(); iter != iter_end; ++iter ) {
347  HitIndexList const & hitlist = iter->second;
348  if ( hitlist.empty() ) continue;
349  std::fill( visited_halfbins.begin(), visited_halfbins.end(), false ); // mark all halfbins as not-yet visited.
350  Size first_hit_index = hitlist.begin()->first;
351  // Hit const * first_hit = hitlist.begin()->second; // Unused variable causes a warning.
352  // Bin6D query_bin = binner_->bin6( first_hit->second() ); // Unused variable causes a warning.
353  //boost::uint64_t bin_index = binner_->bin_index( query_bin );
354 
355  /// 1. Mark all the hits in this bin as members of the same CC.
356  for ( HitIndexList::const_iterator
357  hitlist_iter = hitlist.begin(), hitlist_iter_end = hitlist.end();
358  hitlist_iter != hitlist_iter_end; ++hitlist_iter ) {
359  if ( ds.ds_find( first_hit_index ) == ds.ds_find( hitlist_iter->first )) continue;
360  ds.ds_union( first_hit_index, hitlist_iter->first );
361  }
362 
363  /// 2. Iterate across all hits, and for each halfbin, take one hit and look for all
364  /// halfbin-neighbors of that hit.
365  for ( HitIndexList::const_iterator
366  hitlist_iter = hitlist.begin(), hitlist_iter_end = hitlist.end();
367  hitlist_iter != hitlist_iter_end; ++hitlist_iter ) {
368  Size query_id = hitlist_iter->first;
369  Hit const * query_hit = hitlist_iter->second;
370  Bin6D query_halfbin = binner_->halfbin6( query_hit->second() );
371  Size query_halfbin_index = 1;
372  for ( Size ii = 1; ii <= 6; ++ii ) if ( query_halfbin[ ii ] == 1 ) query_halfbin_index += twopows[ ii ];
373 
374  // only explore the neighbor halfbins
375  if ( visited_halfbins[ query_halfbin_index ] ) continue;
376  visited_halfbins[ query_halfbin_index ] = true;
377 
378  Real6 halfsteps( binner_->halfbin_widths() );
379  for ( Size ii = 1; ii <= 6; ++ii ) {
380  if ( query_halfbin[ ii ] == 0 ) halfsteps[ ii ] *= -1;
381  }
382 
383  halfbin_lex.begin();
384  while ( !halfbin_lex.at_end() ) {
385  Bin6D nbbin;
386  Size skip_pos = find_next_bin( query_hit->second(), halfsteps, halfbin_lex, nbbin );
387  if ( skip_pos != 0 ) {
388  halfbin_lex.continue_at_dimension( skip_pos );
389  continue;
390  }
391 
392  /// now query the hash map and find all the hits in the neighbor bin (if any)
393  boost::uint64_t nbindex = binner_->bin_index( nbbin );
394  HitHash::const_iterator nbr_hits = hash_.find( nbindex );
395 
396  if ( nbr_hits != hash_.end() ) {
397  // we have hits in the neighbor bin
398  for ( HitIndexList::const_iterator nbr_hits_iter = nbr_hits->second.begin(),
399  nbr_hits_iter_end = nbr_hits->second.end();
400  nbr_hits_iter != nbr_hits_iter_end; ++nbr_hits_iter ) {
401  Size nbr_id = nbr_hits_iter->first;
402  if ( ds.ds_find( query_id ) == ds.ds_find( nbr_id ) ) continue; // already neighbors
403  Bin6D nb_halfbin = binner_->halfbin6( nbr_hits_iter->second->second() );
404  if ( within_reach( query_halfbin, nb_halfbin, nbbin, halfbin_lex )) {
405  ds.ds_union( query_id, nbr_id ); // mark these hits as neighbors
406  }
407  }
408  }
409  ++halfbin_lex;
410 
411  } // end while ( !halfbin_lex.at_end() )
412 
413 
414  }
415 
416 
417  }
418 
419  /*for ( HitIndexList::const_iterator iter = all_hits_.begin(), iter_end = all_hits_.end();
420  iter != iter_end; ++iter ) {
421  Size query_id = iter->first;
422  Hit const * query_hit = iter->second;
423  Bin6D query_bin = binner_->bin6( query_hit->second() );
424  Bin6D query_halfbin = binner_->halfbin6( query_hit->second() );
425  Real6 halfsteps( binner_->halfbin_widths() );
426  for ( Size ii = 1; ii <= 6; ++ii ) {
427  if ( query_halfbin[ ii ] == 0 ) halfsteps[ ii ] *= -1;
428  }
429 
430  halfbin_lex.begin();
431  while ( !halfbin_lex.at_end() ) {
432  Bin6D nbbin;
433  Size skip_pos = find_next_bin( query_hit->second(), halfsteps, halfbin_lex, nbbin );
434  if ( skip_pos != 0 ) {
435  halfbin_lex.continue_at_dimension( skip_pos );
436  continue;
437  }
438 
439  /// now query the hash map and find all the hits in the neighbor bin (if any)
440  boost::uint64_t nbindex = binner_->bin_index( nbbin );
441  HitHash::const_iterator nbr_hits = hash_.find( nbindex );
442 
443  if ( nbr_hits != hash_.end() ) {
444  // we have hits in the neighbor bin
445  for ( HitIndexList::const_iterator nbr_hits_iter = nbr_hits->second.begin(),
446  nbr_hits_iter_end = nbr_hits->second.end();
447  nbr_hits_iter != nbr_hits_iter_end; ++nbr_hits_iter ) {
448  Size nbr_id = nbr_hits_iter->first;
449  if ( ds.ds_find( query_id ) == ds.ds_find( nbr_id ) ) continue; // already neighbors
450  Bin6D nb_halfbin = binner_->halfbin6( nbr_hits_iter->second->second() );
451  if ( within_reach( query_halfbin, nb_halfbin, nbbin, halfbin_lex )) {
452  ds.ds_union( query_id, nbr_id ); // mark these hits as neighbors
453  }
454  }
455  }
456  ++halfbin_lex;
457 
458  } // end while ( !halfbin_lex.at_end() )
459  }*/
460 
461  /// Iterate across all bins. Then iterate across all halfbins, skipping over halfbins that have already been visited.
462 
463  // OK: now how many sets do we have, and who are their representatives?
464  Size const nccs = ds.n_disjoint_sets(); // number of connected-components.
465  utility::vector1< Size > ds_representatives_to_setnos( ds.n_nodes(), 0 );
466  Size count_set = 0;
467  for ( Size ii = 1; ii <= ds.n_nodes(); ++ii ) {
468  if ( ds.ds_find( ii ) == ii ) {
469  ++count_set;
470  ds_representatives_to_setnos[ ii ] = count_set;
471  }
472  }
473 
474  utility::vector1< HitPtrList > hit_ccs( nccs );
475  for (HitIndexList::const_iterator iter = all_hits_.begin(), iter_end = all_hits_.end();
476  iter != iter_end; ++iter ) {
477  Size iterrep = ds.ds_find( iter->first );
478  Size setid = ds_representatives_to_setnos[ iterrep ];
479  assert( setid != 0 );
480  hit_ccs[ setid ].push_back( iter->second );
481  }
482  return hit_ccs;
483 }
484 
485 
486 /// @brief Find the neighbors of the given set of query hits. This search iterates
487 /// across both the upper and the lower neighbors of the query hits (3^6 neighbors).
490 {
491  HitPtrList neighbors;
492  std::set< Size > neighbor_set;
493  std::set< boost::uint64_t > exhausted_bins;
494  // we need to visit all 2^6=64 neighboring bins of the query halfbin; the lexicographical iterator
495  // ensures we visit them all.
496  Bin6D twos( 2 ); // numer of halfbin neighbors for each dimension
497  utility::FixedSizeLexicographicalIterator< 6 > halfbin_lex( twos );
498 
499  for ( HitPtrList::const_iterator iter = queryhits.begin(), iter_end = queryhits.end();
500  iter != iter_end; ++iter ) {
501  //Size query_id = iter->first;
502  Hit const * query_hit = *iter;
503  // Bin6D query_bin = binner_->bin6( query_hit->second() ); // Unused variable causes a warning.
504  Bin6D query_halfbin = binner_->halfbin6( query_hit->second() );
505  Real6 halfsteps( binner_->halfbin_widths() );
506  for ( Size ii = 1; ii <= 6; ++ii ) {
507  if ( query_halfbin[ ii ] == 0 ) halfsteps[ ii ] *= -1;
508  }
509 
510  halfbin_lex.begin();
511  while ( !halfbin_lex.at_end() ) {
512  Bin6D nbbin;
513  Size skip_pos = find_next_bin( query_hit->second(), halfsteps, halfbin_lex, nbbin );
514  if ( skip_pos != 0 ) {
515  halfbin_lex.continue_at_dimension( skip_pos );
516  continue;
517  }
518 
519  /// now query the hash map and find all the hits in the neighbor bin (if any)
520  boost::uint64_t nbindex = binner_->bin_index( nbbin );
521  HitHash::const_iterator nbr_hits = hash_.find( nbindex );
522 
523  if ( exhausted_bins.find( nbindex ) != exhausted_bins.end() ) { ++halfbin_lex; continue; }
524 
525  if ( nbr_hits != hash_.end() ) {
526  // we have hits in the neighbor bin
527  bool all_inserted = true;
528  for ( HitIndexList::const_iterator nbr_hits_iter = nbr_hits->second.begin(),
529  nbr_hits_iter_end = nbr_hits->second.end();
530  nbr_hits_iter != nbr_hits_iter_end; ++nbr_hits_iter ) {
531  Size nbr_id = nbr_hits_iter->first;
532  if ( neighbor_set.find( nbr_id ) != neighbor_set.end() ) {
533  // already found this neighbor; note does not invalidate the "all_inserted" boolean
534  // since this hit need not be examined again in the future.
535  continue;
536  }
537 
538  Bin6D nb_halfbin = binner_->halfbin6( nbr_hits_iter->second->second() );
539  if ( within_reach( query_halfbin, nb_halfbin, nbbin, halfbin_lex )) {
540  neighbor_set.insert( nbr_id );
541  neighbors.push_back( nbr_hits_iter->second );
542  } else {
543  all_inserted = false; // a hit is out of range; don't add it.
544  }
545  }
546  if ( all_inserted ) {
547  // mark this bin as having no hits that need to be further examined
548  exhausted_bins.insert( nbindex );
549  }
550  } else {
551  /// If this bin is empty, we may as well avoid the hash lookup.
552  exhausted_bins.insert( nbindex );
553  }
554  ++halfbin_lex;
555 
556  } // end while ( !halfbin_lex.at_end() )
557  }
558 
559  return neighbors;
560 }
561 
562 void
564 {
565  for ( HitIndexList::const_iterator iter = all_hits_.begin(), iter_end = all_hits_.end();
566  iter != iter_end; ++iter ) {
567  boost::uint64_t bin_index = binner_->bin_index( iter->second->second() );
568  HitHash::iterator hash_iter = hash_.find( bin_index );
569  if ( hash_iter == hash_.end() ) {
570  HitIndexList hitlist;
571  hitlist.push_back( *iter );
572  hash_[ bin_index ] = hitlist;
573  } else {
574  hash_iter->second.push_back( *iter );
575  }
576  }
577 
578 }
579 
580 /// @brief Find the neighbor bin for a point by taking a step along a certain offset vector
581 /// for a subset of the dimensions as given by a lex iterator (the halfbin_lex).
582 /// Returns 0 if successful, and the index of the euclidean dimension which is out-of-bounds
583 /// if the step is not successful.
586  Real6 orig_point,
587  Real6 offsets,
588  utility::FixedSizeLexicographicalIterator< 6 > const & halfbin_lex,
589  Bin6D & next_bin
590 ) const
591 {
592  Real6 steps( 0.0 );
593  for ( Size ii = 1; ii <= 6; ++ii ) {
594  if ( halfbin_lex[ ii ] == 2 ) steps[ ii ] = offsets[ ii ];
595  }
596  return advance_to_neighbor_bin( orig_point, steps, *binner_, next_bin );
597 
598 }
599 
600 
601 bool
603  Bin6D const & query_halfbin,
604  Bin6D const & nb_halfbin,
605  Bin6D const & nbbin,
606  utility::FixedSizeLexicographicalIterator< 6 > const & halfbin_lex
607 ) const
608 {
609  Size const NEIGHBOR_BIN = 2; // 1 will denote "stay in the same bin; 2 will denote look in the neighbor bin"
610 
611  // check everything but theta!
612  for ( Size ii = 1; ii <= 5; ++ii ) {
613  if ( halfbin_lex[ ii ] == NEIGHBOR_BIN && query_halfbin[ ii ] == nb_halfbin[ ii ] )
614  {
615  /// Then this neighbor hit is too far away from the query hit: we've spanned
616  /// the bin boundary to a neighboring bin, but the neighbor hit is on
617  /// the other side of the halfbin divide from the query hit -- either
618  /// halfbin[ ii ] == 0 && nbhalfbin[ ii ]
619  return false;
620  }
621  }
622 
623  // Theta comparison: worry about wrapping!
624  if ( halfbin_lex[ 6 ] == NEIGHBOR_BIN ) {
625  if ( nbbin[ 6 ] == 0 || nbbin[ 6 ] + 1 == binner_->dimsizes()[ 6 ] ) {
626  // theta has wrapped, so we're only within range as long as both halfbins agree
627  if ( query_halfbin[ 6 ] != nb_halfbin[ 6 ] ) {
628  return false;
629  }
630  } else {
631  // theta did not wrap, so the two halfbins need to disagree to be within range.
632  if ( query_halfbin[ 6 ] == nb_halfbin[ 6 ] ) {
633  return false;
634  }
635  }
636  }
637  return true;
638 }
639 
640 
641 //////////////////////////////////////////////////////////////////////
642 MatchCounter::MatchCounter() : n_geom_csts_( 0 ), initialized_( false ) {}
644 
645 void
647  BoundingBox const & bb
648 )
649 {
650  assert( ! initialized_ );
651  bb_ = bb;
652 }
653 
654 void
656 {
657  assert( ! initialized_ );
658  assert( n_geom_csts_ == 0 ); // this function should only be called once
659  n_geom_csts_ = ngeomcsts;
660 }
661 
662 void
664 {
665  assert( ! initialized_ );
666  assert( bin_width > 0 );
667  for ( Size ii = 1; ii <= 3; ++ii ) { xyz_bin_widths_[ ii ] = bin_width; }
668 }
669 
670 void
672 {
673  assert( ! initialized_ );
674  assert( bin_width_degrees > 0 );
675  for ( Size ii = 1; ii <= 3; ++ii ) { euler_bin_widths_[ ii ] = bin_width_degrees; }
676 }
677 
678 void
680 {
681  assert( ! initialized_ );
682  for ( Size ii = 1; ii <= 3; ++ii ) {
683  assert( bin_widths( ii ) > 0 );
684  xyz_bin_widths_[ ii ] = bin_widths( ii );
685  }
686 
687 }
688 
689 void
690 MatchCounter::set_euler_bin_widths( Vector const & euler_bin_widths )
691 {
692  assert( ! initialized_ );
693  for ( Size ii = 1; ii <= 3; ++ii ) {
694  assert( euler_bin_widths( ii ) > 0 );
695  euler_bin_widths_[ ii ] = euler_bin_widths( ii );
696  }
697 }
698 
699 void MatchCounter::add_hits( Size geomcst_id, std::list< Hit > const & hitlist )
700 {
701  assert( initialized_ );
702 
703  for ( std::list< Hit >::const_iterator iter = hitlist.begin(), iter_end = hitlist.end(); iter != iter_end; ++iter ) {
704  boost::uint64_t bin_index = binner_->bin_index( iter->second() );
705  HitHash::iterator hash_iter = hash_.find( bin_index );
706  if ( hash_iter == hash_.end() ) {
707  HitCounts & hitcount_v = hash_[ bin_index ];
708  hitcount_v.resize( n_geom_csts_ );
709  std::fill( hitcount_v.begin(), hitcount_v.end(), 0 );
710  hitcount_v[ geomcst_id ] = 1;
711  } else {
712  hash_iter->second[ geomcst_id ] += 1;
713  }
714  }
715 }
716 
717 void MatchCounter::add_hits( Size geomcst_id, std::list< Hit const * > const & hitlist )
718 {
719  assert( initialized_ );
720 
721  for ( std::list< Hit const * >::const_iterator iter = hitlist.begin(), iter_end = hitlist.end(); iter != iter_end; ++iter ) {
722  boost::uint64_t bin_index = binner_->bin_index( (*iter)->second() );
723  HitHash::iterator hash_iter = hash_.find( bin_index );
724  if ( hash_iter == hash_.end() ) {
725  HitCounts & hitcount_v = hash_[ bin_index ];
726  hitcount_v.resize( n_geom_csts_ );
727  std::fill( hitcount_v.begin(), hitcount_v.end(), 0 );
728  hitcount_v[ geomcst_id ] = 1;
729  } else {
730  hash_iter->second[ geomcst_id ] += 1;
731  }
732  }
733 }
734 
735 /// @details hash based on halfbin widths.
736 void
738 {
739  assert( ! initialized_ );
740 
741  initialized_ = true;
742 
743  /// Set all euler-offsets to 0 (no offset).
744  Size3 euler_offsets( 0 );
745 
746  Real6 bin_widths;
747  bin_widths[ 1 ] = xyz_bin_widths_[ 1 ]; bin_widths[ 2 ] = xyz_bin_widths_[ 2 ]; bin_widths[ 3 ] = xyz_bin_widths_[ 3 ];
748  bin_widths[ 4 ] = euler_bin_widths_[ 1 ]; bin_widths[ 5 ] = euler_bin_widths_[ 2 ]; bin_widths[ 6 ] = euler_bin_widths_[ 3 ];
749 
750  for ( Size ii = 1; ii <=6; ++ii ) bin_widths[ ii ] *= 0.5;
751 
752  binner_ = new numeric::geometry::hashing::SixDCoordinateBinner( bb_, euler_offsets, bin_widths );
753 
754 }
755 
756 
759 {
760  assert( initialized_ );
761  Size const two_billion = 2000000000;
762  Real const ln2e9 = 21.4; // e^21.4 ~ 2 billion
763 
764  Size const three_to_the_sixth = 729;
765 
766  utility::vector1< utility::vector1< Size > > neighbor_bin_hit_counts( three_to_the_sixth );
767  utility::vector1< utility::vector1< Real > > log_neighbor_bin_hit_counts( three_to_the_sixth );
768  for ( Size ii = 1; ii <= three_to_the_sixth; ++ii ) {
769  neighbor_bin_hit_counts[ ii ].resize( n_geom_csts_, 0 );
770  log_neighbor_bin_hit_counts[ ii ].resize( n_geom_csts_, 0 );
771  }
772 
773  utility::vector1< Size > seventwentynines( n_geom_csts_ - 1, three_to_the_sixth );
774  utility::LexicographicalIterator lex( seventwentynines );
775 
776  Bin6D threes( 3 );
777  utility::FixedSizeLexicographicalIterator< 6 > neighbor_halfbin_lex( threes ), geomcst2_lex( threes ), comp_lex( threes );
778 
779  /// TEMP: reporting counts per bin
780  //for ( HitHash::const_iterator iter = hash_.begin(), iter_end = hash_.end(); iter != iter_end; ++iter ) {
781  // boost::uint64_t bin_index = iter->first;
782  // std::cout << " bin " << bin_index << " w/counts:";
783  // for ( Size ii = 1; ii <= n_geom_csts_; ++ii ) std::cout << " " << iter->second[ ii ];
784  // std::cout << std::endl;
785  //}//
786 
787 
788  Size grand_total = 0;
789  Size last_grand_total = 0;
790  for ( HitHash::const_iterator iter = hash_.begin(), iter_end = hash_.end(); iter != iter_end; ++iter ) {
791  for ( Size ii = 1; ii <= three_to_the_sixth; ++ii ) std::fill( neighbor_bin_hit_counts[ ii ].begin(), neighbor_bin_hit_counts[ ii ].end(), 0 );
792 
793  HitCounts const & center_hits = iter->second;
794  Size const halfbin_center_first_geom_cst_nhits = center_hits[ 1 ];
795 
796  if ( halfbin_center_first_geom_cst_nhits == 0 ) continue;
797 
798  Real const log_halfbin_center_first_geom_cst_nhits = std::log( (double) halfbin_center_first_geom_cst_nhits );
799  boost::uint64_t bin_index = iter->first;
800  Bin6D bin = binner_->bin_from_index( bin_index );
801  Real6 bin_center = binner_->bin_center_point( bin );
802 
803  /// 1. Look at all 729 neighbors of this halfbin and record the hit-counts for each
804  neighbor_halfbin_lex.begin();
805  while ( ! neighbor_halfbin_lex.at_end() ) {
806  Bin6D neighbor_bin;
807  Real6 steps( 0 );
808  for ( Size ii = 1; ii <= 6; ++ii ) {
809  if ( neighbor_halfbin_lex[ ii ] == 1 ) steps[ ii ] = -1 * binner_->bin_widths()[ ii ];
810  else if ( neighbor_halfbin_lex[ ii ] == 3 ) steps[ ii ] = binner_->bin_widths()[ ii ];
811  }
812  Size oo_bounds_dim = advance_to_neighbor_bin( bin_center, steps, *binner_, neighbor_bin );
813  if ( oo_bounds_dim != 0 ) {
814  /// advance the lex and continue;
815  neighbor_halfbin_lex.continue_at_dimension( oo_bounds_dim );
816  continue;
817  }
818  boost::uint64_t neighbor_bin_index = binner_->bin_index( neighbor_bin );
819  HitHash::const_iterator nbr_hits = hash_.find( neighbor_bin_index );
820  if ( nbr_hits != hash_.end() ) {
821  Size const neighbor_halfbin_lex_index = neighbor_halfbin_lex.index();
822  neighbor_bin_hit_counts[ neighbor_halfbin_lex_index ] = nbr_hits->second;
823  for ( Size ii = 1; ii <= n_geom_csts_; ++ii ) {
824  if ( neighbor_bin_hit_counts[ neighbor_halfbin_lex_index ][ ii ] > 1 ) {
825  // don't try to take the log of 0, don't bother taking the log of 1
826  log_neighbor_bin_hit_counts[ neighbor_halfbin_lex_index ][ ii ] =
827  std::log( (double) (neighbor_bin_hit_counts[ neighbor_halfbin_lex_index ][ ii ]) );
828  }
829  }
830  }
831  ++neighbor_halfbin_lex;
832  }
833 
834  /// 2. Now that we have all the neighbor bin hit counts, enumerate all halfbin combinations of hit sources
835  /// and add up the number of matches that each halfbin combination generates. At 2 billion, quit.
836  /// Make sure not to matches that are from bins which are too far apart -- use geom-cst #2 to restrict
837  /// how far apart hits can be before they should not be counted together.
838  Size total = 0;
839  Size last_total = 0;
840  lex.begin();
841  while ( ! lex.at_end() ) {
842  Size this_combo_n_hits = halfbin_center_first_geom_cst_nhits; // non-zero
843  Real this_combo_log_n_hits = log_halfbin_center_first_geom_cst_nhits;
844 
845  /// OK: the logic in here is pretty complicated.
846  if ( n_geom_csts_ > 2 ) {
847  if ( geomcst2_lex.index() != lex[ 1 ] ) geomcst2_lex.set_position_from_index( lex[ 1 ] );
848  Bin6D outer_corner;
849  for ( Size ii = 1; ii <= 6; ++ii ) outer_corner[ ii ] = geomcst2_lex[ ii ];
850  bool out_of_range = false;
851  for ( Size ii = 3; ii <= n_geom_csts_; ++ii ) {
852  comp_lex.set_position_from_index( lex[ ii-1 ] );
853  for ( Size jj = 1; jj <= 6; ++jj ) {
854  /// 3 ways in which the halfbin for geomcst ii at dimension jj is within range of a match.
855  if ( comp_lex[ jj ] == 2 ) continue; // 1. It's in the center.
856  if ( outer_corner[ jj ] == 2 ) { outer_corner[ jj ] = comp_lex[ jj ]; continue; } // 2. It's defined a new outer-edge
857  if ( comp_lex[ jj ] == outer_corner[ jj ] ) continue; // 3. It's
858 
859  out_of_range = true;
860  lex.continue_at_dimension( ii-1 );
861  break;
862  }
863  if ( out_of_range ) break;
864  }
865  if ( out_of_range ) continue; // don't count any matches from this bin; note lex has already been advanced
866  }
867 
868  for ( Size ii = 2; ii <= n_geom_csts_; ++ii ) {
869  this_combo_n_hits *= neighbor_bin_hit_counts[ lex[ ii-1 ] ][ ii ];
870  this_combo_log_n_hits += log_neighbor_bin_hit_counts[ lex[ ii-1 ] ][ ii ];
871  if ( this_combo_n_hits == 0 ) {
872  lex.continue_at_dimension( ii-1 ); // no matches possible for this lex combo; skip ahead
873  break;
874  }
875  }
876  if ( this_combo_n_hits == 0 ) continue; // the lex has already been advanced;
877 
878  if ( this_combo_log_n_hits > ln2e9 ) { return two_billion; } // bail: we can't represent this number
879  total += this_combo_n_hits;
880  if ( total < last_total ) { return two_billion; } // crap; we wrapped
881  last_total = total;
882 
883  ++lex;
884  }
885 
886  grand_total += total;
887  if ( grand_total < last_grand_total ) { return two_billion; } // crap; we wrapped
888  last_grand_total = grand_total;
889 
890  }
891 
892  return grand_total;
893 }
894 
895 ////////////////
896 
897 
900  Real6 orig_point,
901  Real6 steps, // 0 if no step, non-zero if some real step
902  numeric::geometry::hashing::SixDCoordinateBinner const & binner,
903  numeric::geometry::hashing::Bin6D & next_bin
904 )
905 {
906  using namespace core;
907 
908  numeric::geometry::hashing::Real6 alt_point( orig_point );
909  numeric::geometry::hashing::Real3 orig_euler( 0.0 );
910  numeric::geometry::hashing::Real3 euler_offsets( 0.0 );
911  for ( Size ii = 1; ii <= 3; ++ii ) {
912  alt_point[ ii ] += steps[ ii ];
913  euler_offsets[ ii ] = steps[ ii + 3 ];
914  orig_euler[ ii ] = orig_point[ ii + 3 ];
915  }
916 
917  /// are we in range? -- return out-of-range index for advancing the lex if so
918  for ( Size ii = 1; ii <= 3; ++ii ) {
919  if ( binner.bounding_box().lower()( ii ) > alt_point[ ii ] ||
920  binner.bounding_box().upper()( ii ) < alt_point[ ii ] ) {
921  return ii;
922  }
923  }
924  /// ok -- we're good!
925 
926  /// Complicated logic in advancing the euler angles; defer to another function
927  numeric::geometry::hashing::Real3 new_euler = advance_euler_angles( orig_euler, euler_offsets );
928  for ( Size ii = 1; ii <= 3; ++ii ) {
929  alt_point[ ii + 3 ] = new_euler[ ii ];
930  }
931 
932  next_bin = binner.bin6( alt_point );
933 
934  return 0;
935 
936 }
937 
938 /// @details "Advance" the euler angles by a given offset (postive or negative), and wrap
939 /// them back into their appropriate ranges ([0,360) for phi/psi, [0..180] for theta)
940 /// as necessary.
941 /// Requirements: orig_angles[ i ] + offsets[ i ] < 520 (360) && > -360 (-180) for phi/psi (theta).
944  numeric::geometry::hashing::Real3 const & orig_angles,
945  numeric::geometry::hashing::Real3 const & offsets
946 )
947 {
948  using core::Size;
949 
950  numeric::geometry::hashing::Real3 new_euler_angles( orig_angles );
951  for ( Size ii = 1; ii <= 3; ++ii ) new_euler_angles[ ii ] += offsets[ ii ];
952 
953  if ( new_euler_angles[ 3 ] < 0 || new_euler_angles[ 3 ] > 180 ) {
954  /// 1st handle theta wrapping.
955  if ( new_euler_angles[ 3 ] < 0 ) {
956  new_euler_angles[ 3 ] *= -1.0; // wrap the angle back to positive values
957  } else { // new_euler_angles[ 3 ] > 180
958  assert( new_euler_angles[ 3 ] < 360 );
959  new_euler_angles[ 3 ] = 360 - new_euler_angles[ 3 ]; // 182 wraps to 178...
960  }
961  /// wrap phi/psi
962  for ( Size ii = 1; ii <=2; ++ii ) {
963  if ( new_euler_angles[ ii ] < 180 ) {
964  new_euler_angles[ ii ] += 180;
965  } else {
966  new_euler_angles[ ii ] -= 180;
967  }
968  }
969  } else {
970  /// Theta doesn't wrap, so make sure phi and psi are in their proper ranges.
971  for ( Size ii = 1; ii <= 2; ++ii ) {
972  if ( new_euler_angles[ ii ] > 360 ) {
973  new_euler_angles[ ii ] -= 360;
974  } else if ( new_euler_angles[ ii ] < 0 ) {
975  new_euler_angles[ ii ] += 360;
976  }
977  }
978  }
979 
980  //std::cout << "orig_angles:"; for ( Size ii = 1; ii <= 3; ++ii ) std::cout << " " << orig_angles[ ii ]; std::cout << std::endl;
981  //std::cout << "offsets:"; for ( Size ii = 1; ii <= 3; ++ii ) std::cout << " " << offsets[ ii ]; std::cout << std::endl;
982  //std::cout << "new_euler_angles:"; for ( Size ii = 1; ii <= 3; ++ii ) std::cout << " " << new_euler_angles[ ii ]; std::cout << std::endl;
983  return new_euler_angles;
984 }
985 
986 ////////////////////////////////////////////////////////////////////////////////////
987 
988 
990 
991 void
993 {
994  MatchHash::const_iterator iter = hash_.find( m );
995  if ( iter == hash_.end() ) {
996  hash_.insert( std::make_pair( m, true ) );
997  }
998 }
999 
1000 bool
1002 {
1003  return hash_.find( m ) != hash_.end();
1004 }
1005 
1006 
1007 
1008 }
1009 }