Rosetta 3.5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
HamiltonianExchange.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 //
4 // (c) Copyright Rosetta Commons Member Institutions.
5 // (c) This file is part of the Rosetta software suite and is made available under license.
6 // (c) The Rosetta software is developed by the contributing members of the Rosetta Commons.
7 // (c) For more information, see http://www.rosettacommons.org. Questions about this can be
8 // (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu.
9 
10 /// @file protocols/canonical_sampling/HamiltonianExchangeMover.cc
11 /// @brief HamiltonianExchange methods implemented
12 /// @author
13 
14 
15 // Unit Headers
18 
19 
20 // protocols headers
23 #include <protocols/moves/Mover.hh>
27 
29 
30 //#include <protocols/jd2/JobDistributor.hh>
31 #include <protocols/jd2/util.hh>
32 #include <protocols/jd2/Job.hh>
33 
34 // core headers
36 
37 #include <basic/options/option_macros.hh>
38 #include <basic/options/keys/in.OptionKeys.gen.hh>
39 #include <basic/options/keys/packing.OptionKeys.gen.hh>
40 
41 #include <basic/Tracer.hh>
42 
45 
48 
49 #include <core/types.hh>
50 
51 // numeric headers
52 #include <numeric/random/random.hh>
53 
54 // utility headers
55 #include <utility/file/file_sys_util.hh>
56 #include <utility/pointer/owning_ptr.hh>
57 #include <utility/tag/Tag.hh>
58 #include <utility/io/ozstream.hh>
59 #include <utility/io/izstream.hh>
60 
61 // ObjexxFCL headers
62 #include <ObjexxFCL/format.hh>
63 
64 // C++ Headers
65 #include <utility/excn/Exceptions.hh>
66 #include <cmath>
67 
68 
69 using basic::T;
70 using basic::Error;
71 using basic::Warning;
72 
73 static basic::Tracer tr( "protocols.canonical_sampling.HamiltonianExchange" );
74 static numeric::random::RandomGenerator RG(2592747);
75 
76 
78 
79 //Mike: when you want to remove these Macros... leave them at least here as comment - since they provide documentation
81  if ( !options_registered_ ) {
82  options_registered_ = true;
84  }
85 }
86 
87 namespace protocols {
88 namespace canonical_sampling {
89 using namespace core;
90 
94 }
95 
98  return new HamiltonianExchange;
99 }
100 
103  return "HamiltonianExchange";
104 }
105 
107  rank_( -1 )
108 {
109 #ifndef USEMPI
110  utility_exit_with_message( "HamiltonianExchange requires MPI build" );
111 #endif
112 #ifdef USEMPI
113  mpi_comm_ = MPI_COMM_NULL;
114 #endif
115  set_defaults();
116 }
117 
119  Parent( other ),
120  rank_( other.rank_ ),
121  hamiltonians_( other.hamiltonians_ ),
122  exchange_schedules_( other.exchange_schedules_ ),
123  current_exchange_schedule_( other.current_exchange_schedule_ ),
124  exchange_grid_( other.exchange_grid_ ),
125  exchange_grid_dimension_( other.exchange_grid_dimension_ ),
126  successfully_initialized_( false )
127 {
128 #ifndef USEMPI
129  utility_exit_with_message( "HamiltonianExchange requires MPI build" );
130 #endif
131 #ifdef USEMPI
132  set_mpi_comm( other.mpi_comm() );
133 #endif
134 }
135 
137  if ( &other == this ) return *this;
138  Parent::operator=( other );
139  rank_ = other.rank_;
146 #ifdef USEMPI
147  set_mpi_comm( other.mpi_comm() );
148 #endif
149  Size const nlevels( n_temp_levels() );
150  runtime_assert( nlevels == hamiltonians_.size() );
151  return *this;
152 }
153 
155 }
156 
157 
160 {
161  return "HamiltonianExchange";
162 }
163 
166 {
168 }
169 
172 {
173  return new HamiltonianExchange;
174 }
175 
176 void
178  utility::tag::TagPtr const tag,
180  protocols::filters::Filters_map const & filters,
181  protocols::moves::Movers_map const & movers,
182  pose::Pose const & pose
183 ) {
184  Parent::parse_my_tag( tag, data, filters, movers, pose );
185  if ( !successfully_initialized_ ) {
186  throw utility::excn::EXCN_RosettaScriptsOption( "Initialization of HamiltonianExchange Module failed! " );
187  }
188 }
189 
190 
191 /// handling of options including command-line
193 }
194 
195 /// @brief Assigns user specified values to primitive members using command line options
197  using namespace basic::options;
198  using namespace basic::options::OptionKeys;
199  using namespace core;
200  using namespace basic::options;
201  using namespace basic::options::OptionKeys;
202  using namespace core;
204 }
205 
206 
207 void
209  core::pose::Pose& pose,
210  MetropolisHastingsMover const& metropolis_hastings_mover,
211  core::Size cycle //default=0; non-zero if trajectory is restarted
212 ) {
213  show( tr.Info );
214  Parent::initialize_simulation( pose, metropolis_hastings_mover,cycle );
215 #ifdef USEMPI
216  set_mpi_comm( jd2::current_mpi_comm() );
217 #endif
218  // Size const nlevels( n_temp_levels() );
220  set_current_temp( rank()+1 );
221  monte_carlo()->reset_scorefxn( pose, *hamiltonians_[ rank()+1 ] );
222 }
223 
224 void
226  core::pose::Pose & pose,
228  core::Size level,
229  core::Real temp_in,
230  core::Size cycle //default=0; non-zero if trajectory is restarted
231 ) {
232  Parent::initialize_simulation( pose, mhm, level, temp_in, cycle );
233  monte_carlo()->reset_scorefxn( pose, *hamiltonians_[ level ] );
234 }
235 
236 void
238  pose::Pose& pose,
240 ) {
241  Parent::finalize_simulation( pose, mhm );
242 }
243 
244 Size
245 HamiltonianExchange::coord2key( GridCoord const& coord, GridCoord const& max_coord, Size exclude_dim ) {
246  Size const n_dim( coord.size() );
247  Size key( 0 );
248  Size stride( 1 );
249  for ( Size d=1; d<=n_dim; ++d ) {
250  if ( d == exclude_dim ) continue;
251  key += (coord[ d ] - 1) * stride;
252  stride *= max_coord[d];
253  }
254  return key;
255 }
256 
258  exchange_schedules_.clear();
259  ExchangeSchedule list;
260 
261  //to compute unique keys we need the max coord per dimension
262  int const n_dim( exchange_grid_dimension_ );
263  GridCoord max_coord( n_dim, 0 );
264  for ( Grid::const_iterator it=exchange_grid_.begin(); it!=exchange_grid_.end(); ++it ) {
265  GridCoord const& coord( *it );
266  for ( Size dim=1; (int)dim<=n_dim; ++dim ) {
267  if ( coord[ dim ] > max_coord[ dim ] ) max_coord[ dim ] = coord[ dim ];
268  }
269  }
270 
271  for ( Size dim=1; (int)dim<=n_dim; ++dim ) {
272  //make list of uniqe groups: if dim==2 (1,1,1), (1,2,1), (1,3,1), ... (1, N, 1) should be one group
273  typedef std::list< std::pair< core::Size, core::Size > > Level2GridpointList;
274  typedef std::map< core::Size, Level2GridpointList > Groups;
275  Groups groups;
276  for ( Size i=1; i<=exchange_grid_.size(); ++i ) {
277  GridCoord const& coord( exchange_grid_[ i ] );
278  Size key( coord2key( coord, max_coord, dim ) );
279  groups[ key ].push_back( std::make_pair( coord[ dim ], i ) );
280  }
281 
282  //sort witin groups
283  for ( Groups::iterator it = groups.begin(); it != groups.end(); ++it ) {
284  it->second.sort();
285  }
286 
287  for ( Size phase = 1; phase <= 2; ++phase ) {
288  //PHASE 2 2<->3, 4<->5...
289  list.clear();
290  for ( Groups::const_iterator git = groups.begin(); git != groups.end(); ++git ) {
291  Level2GridpointList::const_iterator lit=git->second.begin();
292  if ( phase==2 && git->second.size() > 2 ) ++lit; //if more than 2 elements we need to switch phases
293  for ( ; lit != git->second.end(); ++lit ) {
294  Level2GridpointList::const_iterator first_lit = lit;
295  Level2GridpointList::const_iterator second_lit = ++lit;
296  if ( second_lit != git->second.end() ) {
297  std::pair<int, int> elem( first_lit->second, second_lit->second );
298  list.push_back(elem);
299  } else break;
300  }
301  }
302  exchange_schedules_.push_back(list);
303  } // phase
304  } // per dimension
305 }
306 
307 void
308 HamiltonianExchange::find_exchange_partner( int& partner, bool& is_master ) {
309 #ifdef USEMPI
310  using namespace ObjexxFCL::fmt;
311 
312  // tr.Trace<< "find exchange partner... " << std::endl;
313  int sendbuf = current_temp();
314  int* recvbuf = new int[ n_temp_levels() ];
315  MPI_Allgather( &sendbuf, 1, MPI_INT,
316  recvbuf, 1, MPI_INT, mpi_comm() );
317  //tr.Trace<< " received all current cell-indices: ";
318  // for ( Size i = 0; i < n_temp_levels(); ++i ) {
319  // tr.Trace<< I(2, recvbuf[ i ]) << " ";
320  // }
321  // tr.Trace<< std::endl;
322 
324  int self( current_temp() );
325  runtime_assert( recvbuf[ rank() ] == self );
326  int other( -1 );
327  for ( Size i=1; i<=ex.size() && other<0; i++ ) {
328  if ( ex[ i ].first == self ) other = ex[ i ].second;
329  if ( ex[ i ].second == self ) other = ex[ i ].first;
330  }
331  if ( other < 0 ) {
332  // tr.Trace<< "no partner for cell " << self << " on rank " << rank() << std::endl;
333  partner = -1;
334  return;
335  }
336  Size r;
337  for ( r=0; r<n_temp_levels(); ++r ) {
338  if ( recvbuf[ r ]==other ) break;
339  }
340  runtime_assert( r<n_temp_levels() );
341 
342  delete [ ] recvbuf;
343 #else
344  Size r( 0 );
345 #endif
346  partner = r;
347  is_master = rank() < partner;
348 }
349 
352  utility_exit_with_message( "HamiltonianExchange::temperature_move() called without pose... HamEx requires pose \
353  to evaluate alternative energy function prior to switching..." );
354  return -1;
355 }
356 
358 HamiltonianExchange::temperature_move( pose::Pose& MPI_ONLY( pose ), core::Real MPI_ONLY( score ) ) {
359  using namespace ObjexxFCL::fmt;
361  if ( !time_for_temp_move() ) return temperature();
363  //later: look in emap if all necessary energies have been computed...
364  /// if this is the case: reweight only
365  // if not : evaluate with alternative energy function
366  // now: just evaluate alternative.
367 
368  // communication strategies:
369  // I master-slave: (rank 0 gathers all information and decides for all )
370  // requires GATHER of score, SCATTER of alternative levels, GATHER of alternative score, SCATTER of assigned levels
371  // II peer2peer: ( pairwise communication; the lower-rank of a pair makes the decision)
372  // requires: Allgather --> everybody should know which level is where -- subsequently pairwise exchanges only
373  // requires: SEND UP ( alternative level ), SEND DOWN ( score, alternative score, previous level ), decision, SEND UP ( new level )
374  // strategy II involves less communication and is thus preferred.
375 
376  // strategy II: all processes must be in sync regarding the exchange schedule.
377  // communication will be non-blocking since pairwise swaps...
378  //
379 #ifdef USEMPI
380  //communication tags
381  int const mpi_LEVEL_INFORM = 1;
382  int const mpi_SCORE_INFORM = 2;
383  int const mpi_LEVEL_DECISION = 3;
384 
385  Size const nlevels( n_temp_levels() );
386  int exchange_partner;
387  bool is_master;
388  find_exchange_partner( exchange_partner, is_master );
389  int my_level( temperature_level() );
390  int new_level;
391 
392  MPI_Request reqs[2];
393  MPI_Status stats[2];
394  //SEND UP ( alternative levels )
395  if ( exchange_partner < 0 ) return temperature();
396  //tr.Trace << "exchange partner: " << exchange_partner << std::endl;
397  MPI_Isend( &my_level, 1, MPI_INT, exchange_partner, mpi_LEVEL_INFORM, mpi_comm(), &reqs[0]);
398  MPI_Irecv( &new_level, 1, MPI_INT, exchange_partner, mpi_LEVEL_INFORM, mpi_comm(), &reqs[1]);
399  MPI_Waitall(2, reqs, stats);
400  //tr.Trace<< "my_level: " << my_level << " other level: " << new_level << std::endl;
401  core::scoring::ScoreFunction& new_scorefxn( *hamiltonians_[ new_level ] );
402  Real new_score( new_scorefxn( pose ) );
403  float scores[ 2 ];
404  int swap( 0 );
405  if ( !is_master ) {
406  scores[ 0 ] = new_score; //the lower is other for the upper partner
407  scores[ 1 ] = score; //the higher is self for the upper partner
408  //tr.Trace << " send scores " << F( 8,5, scores[ 1 ]) << " " << F( 8,5, scores[ 0 ]) << std::endl;
409  MPI_Send( &scores, 2, MPI_FLOAT, exchange_partner, mpi_SCORE_INFORM, mpi_comm() );
410  MPI_Recv( &swap, 1, MPI_INT, exchange_partner, mpi_LEVEL_DECISION, mpi_comm(), &stats[0] );
411  } else {
412  MPI_Recv( &scores, 2, MPI_FLOAT, exchange_partner, mpi_SCORE_INFORM, mpi_comm(), &stats[0] );
413  //x1 : our process
414  //x2 : other process
415  // V0 : our scorefxn, V1 : other scorefxn
416  // V0(x2)-V0(x1) --> scores[1] - score
417  // V1(x1)-V1(x2) --> new_score - scores[0]
418  //tr.Trace << "level: " << my_level << " other level: " << new_level << " scores: "
419  //<< F( 8,5, score ) << " " << F( 8,5, new_score ) << " "
420  //<< F( 8,5, scores[ 0 ]) << " " << F( 8,5, scores[ 1 ]) << std::endl;
421  Real const invT1( 1.0 / temperature() );
422  Real const invT2( 1.0 / temperature( new_level ) );
423  Real const deltaE1( scores[ 0 ] - score );
424  Real const deltaE2( scores[ 1 ] - new_score );
425  Real const delta( invT1*deltaE1 - invT2*deltaE2 );
426  Real const r( RG.uniform() );
427  swap = r < std::min( 1.0, std::exp( std::max(-40.0, -delta) ) ) ? 1 : 0;
428  MPI_Send( &swap, 1, MPI_INT, exchange_partner, mpi_LEVEL_DECISION, mpi_comm() );
429  //tr.Trace << "decision: "<< F( 4,2, invT1) << " " << F( 4,2,invT2) << " "<< F( 4,2, deltaE1 )
430  // << " " << F( 4,2, deltaE2 ) << " " << F( 4,2, delta ) << " "
431  // << F( 4,2, std::exp( std::max(-40.0, -delta ) ) ) << " " << F( 4,2, r ) << std::endl;
432 
433  }
434  if ( swap ) {
435  //tr.Trace<< "swap! " << std::endl;
436  set_current_temp( new_level );
437  monte_carlo()->score_function( *hamiltonians_[ new_level ] );
438  }
439 #endif
440  return temperature();
441 }
442 
445 }
446 
448  exchange_schedules_.clear();
449  exchange_grid_.clear();
450  hamiltonians_.clear();
451  Parent::clear();
452 }
453 
454 using namespace core::scoring;
456 public:
457 
458  PatchOperation( ScoreType st, std::string const& op, Real wt ) :
459  score_type_( st ),
460  op_ ( op ),
461  wt_ ( wt ),
462  is_file_ (false)
463  {}
464 
465  PatchOperation( std::string const& file ) : file_( file ) {
466  is_file_ = true; //cheap Polymorphism
467  }
468 
469  void apply( ScoreFunction& score ) const {
470  if ( is_file_ ) {
471  score.apply_patch_from_file( file_ );
472  } else { //direct operation
473  if ( op_ == "*=" ) {
474  tr.Debug << "patching weight " << score_type_ << " with " << op_ << wt_ << std::endl;
475  score.set_weight( score_type_, score.get_weight( score_type_ )*wt_ );
476  } else if ( op_ == "=" ) {
477  score.set_weight( score_type_, wt_ );
478  } else {
479  utility_exit_with_message("unrecognized scorefunction patch operation "+op_ );
480  }
481  } //direct operation
482  } //apply
483 
484 private:
489  bool is_file_;
490 };
491 
492 
494  typedef utility::vector1< PatchOperation > PatchOperationList;
495  PatchOperationList global_patch_operations;
496 
497  clear();
498  utility::vector1< core::Real > temperatures;
499  utility::io::izstream in( filename );
500  if ( !in.good() ) {
501  tr.Error << "cannot open file " << filename << std::endl;
502  return false;
503  }
504 
505  std::string line;
506 
507  Size n_dim( 1 );
509  // table format
510  tr.Info << " reading Hamiltonian configuration file " << filename << "..." << std::endl;
511  while ( getline( in, line ) ) {
512  std::istringstream tag_stream( line );
513  std::string tag;
514  tag_stream >> tag;
515  tr << "tag: " << tag << std::endl; //zhe
516  if ( tag_stream.good() && tag == "GRID_DIM" ) {
517  if ( exchange_grid_dimension_ ) {
518  utility_exit_with_message( "GRID_DIM statement has to come before any score-function definitions" );
519  }
520  tag_stream >> n_dim;
521  if ( tag_stream.fail() ) {
522  utility_exit_with_message( "require integer after tag GRID_DIM");
523  }
524  tr.Info << "using " << n_dim << " grid-dimensions" << std::endl;
525  continue;
526  }
527  if ( tag_stream.good() && tag == "GLOBAL_PATCH" ) {
528  std::string tag1, tag2;
529  tag_stream >> tag1;
530  if ( tag_stream.fail() ) {
531  utility_exit_with_message( "GLOBAL_PATCH statement has to be followed by file-name or patch-operation" );
532  }
533  tag_stream >> tag2;
534  if ( tag_stream.fail() ) { //this was a single file-name
535  global_patch_operations.push_back( PatchOperation( tag1 ) );
536  } else {
537  std::istringstream line_stream( line );
538  line_stream >> tag; //read GLOBAL_PATCH again
539  core::scoring::ScoreType score_type;
540  std::string operation;
541  Real wt;
542  line_stream >> score_type >> operation >> wt;
543  if ( line_stream.fail() ) {
544  utility_exit_with_message( "Expected score_type, operation and weight after GLOBAL_PATCH or a file-name" );
545  }
546  global_patch_operations.push_back( PatchOperation( score_type, operation, wt ) );
547  }
548  continue;
549  }
550  if ( tag_stream.good() && tag[0]=='#' ) {
551  tr.Debug << "read comment line: " << line << std::endl;
552  continue;
553  }
554 
555  if ( !tag_stream.good() ) {
556  tr.Debug << "read empy line: " << line << std::endl;
557  continue;
558  }
559 
560  // first proper line ( not GRID_DIM or comment )
561  exchange_grid_dimension_ = n_dim;
562  std::istringstream line_stream( line ); //start over reading the same line
563  Real temp;
564  std::string score_name;
565  std::string patch_name;
566  GridCoord coord( n_dim );
567  for ( Size d = 1; d<=n_dim; ++d ) {
568  line_stream >> coord[ d ];
569  }
570  if ( !line_stream.good() ) {
571  tr.Error << "format error in hamiltonian exchange file : " << filename << " at line " << line << std::endl;
572  tr.Error << "expected " << n_dim << " integer values for the grid-coordinate" << std::endl;
573  return false;
574  }
575  line_stream >> temp >> score_name;
576  if ( !line_stream.good() ) {
577  tr.Error << "format error in hamiltonian exchange file : " << filename << " at line " << line << std::endl;
578  tr.Error << "expected " << n_dim << " integer values for the grid-coordinate" << std::endl;
579  return false;
580  }
581  line_stream >> patch_name;
582  using namespace core::scoring;
583  ScoreFunctionOP score;
584  if ( !line_stream.good() ) { // no patch
585  score = ScoreFunctionFactory::create_score_function( score_name );
586  } else { // patch or patch = "NO_PATCH"
587  score = ScoreFunctionFactory::create_score_function( score_name, patch_name );
588  }
589  for ( PatchOperationList::const_iterator it = global_patch_operations.begin(); it != global_patch_operations.end(); ++it ) {
590  it->apply( *score );
591  }
592  tr.Debug << "line_stream still good after PATCH reading" << std::endl;
593  while ( line_stream.good() ) {
594  std::string tag;
595  core::scoring::ScoreType score_type;
596  std::string operation;
597  Real wt;
598  line_stream >> tag;
599  if ( tag == "ETABLE" ) {
600  line_stream >> tag;
601  score->set_etable( tag );
602  continue;
603  } else {
604  std::istringstream tag_stream( tag );
605  tag_stream >> score_type;
606  line_stream >> operation >> wt;
607  }
608  if ( line_stream.fail() ) {
609  tr.Debug << "tried to read a X op wt triple and failed.. " << std::endl;
610  break;
611  }
612  PatchOperation patch( score_type, operation, wt );
613  patch.apply( *score );
614  }
615  temperatures.push_back( temp );
616  hamiltonians_.push_back( score );
617  exchange_grid_.push_back( coord );
618  }
619  set_temperatures( temperatures );
620  runtime_assert( n_temp_levels() == hamiltonians_.size() );
621  runtime_assert( n_temp_levels() == exchange_grid_.size() );
622 
625  return true; //succesfully initialized
626 }
627 
628 
629 #ifdef USEMPI
630 void HamiltonianExchange::set_mpi_comm( MPI_Comm const& mpi_comm ) {
631  if ( mpi_comm != MPI_COMM_NULL ) {
632  MPI_Comm_dup( mpi_comm, &mpi_comm_ );
633  MPI_Comm_rank( mpi_comm_, &rank_ );
634  int communicator_size;
635  MPI_Comm_size( mpi_comm_, &communicator_size );
636  if ( communicator_size != n_temp_levels() ) {
637  std::ostringstream os;
638  os << "For HamiltonianExchange the number of exchange cells " << n_temp_levels()
639  << "\n has to be consistent with the option -run:n_replica "
640  << communicator_size;
641  utility_exit_with_message( os.str() );
642  }
643  } else {
644  mpi_comm_ = MPI_COMM_NULL;
645  }
646 }
647 #endif
648 
649 void HamiltonianExchange::show( std::ostream& os ) const {
650  using namespace ObjexxFCL::fmt;
651  // All osput will be 80 characters - 80 is a nice number, don't you think?
652  std::string line_marker = "///";
653  os << "////////////////////////////////////////////////////////////////////////////////" << std::endl;
654  os << line_marker << A( 47, "HamiltonianExchange Module" ) << space( 27 ) << line_marker << std::endl;
655  os << line_marker << space( 74 ) << line_marker << std::endl;
656  // Display the movable jumps that will be used in docking
657  os << line_marker << A( 20, "Hamiltonian Cells: " ) << I( 5, n_temp_levels() )
658  << A( 40, "Hamiltonian Grid Dimension: " ) << I( 5, exchange_grid_dimension_ ) << space( 4 ) << line_marker << std::endl;
659  os << line_marker << repeat( 74, '-' ) << line_marker << std::endl;
660  for ( Size level=1; level<= n_temp_levels(); ++level ) {
661  os << line_marker << A( 20, "Grid Cell: " );
662  for ( Size d=1; d<=exchange_grid_dimension_; d++ ) {
663  os << I( 2, exchange_grid_[ level ][ d ] );
664  }
665  os << A( 15, " Temperature: " ) << F( 5, 3, temperature( level ) )
666  << space( 74-20-20-3*exchange_grid_dimension_ ) << line_marker << std::endl;
667  hamiltonians_[ level ]->show( os );
668  os << std::endl;
669  os << line_marker << repeat( 74, '-' ) << line_marker << std::endl;
670  }
671 
672  os << line_marker << repeat( 74, '=' ) << line_marker << std::endl;
673  os << line_marker << A( 40, "Exchange Schedules " ) << std::endl;
674  for ( utility::vector0< ExchangeSchedule >::const_iterator ex_it = exchange_schedules_.begin(); ex_it != exchange_schedules_.end(); ++ex_it ) {
675  ExchangeSchedule const& ex( *ex_it );
676  os << line_marker << repeat( 74, '-' ) << line_marker << std::endl;
677  for ( ExchangeSchedule::const_iterator it = ex.begin(); it != ex.end(); ++it ) {
678  GridCoord const& coord1( exchange_grid_[ it->first ] );
679  GridCoord const& coord2( exchange_grid_[ it->second ] );
680  Size const n_dim( exchange_grid_dimension_ );
681  os << line_marker << A( 20, "( ");
682  for ( Size d=1; d<=n_dim; ++d ) {
683  os << I( 2, coord1[ d ] ) << (d != n_dim ? ", " : " ) " );
684  }
685  os << A( 10, " <--> ( ");
686  for ( Size d=1; d<=n_dim; ++d ) {
687  os << I( 2, coord2[ d ] ) << (d != n_dim ? ", " : " ) " );
688  }
689  os << space( 74-( 20+2+4*n_dim+2+10+8+4*n_dim+2 ) ) << line_marker << std::endl;
690  }
691  }
692  // Close the box I have drawn
693  os << "////////////////////////////////////////////////////////////////////////////////" << std::endl;
694 }
695 
696 std::ostream& operator << ( std::ostream & os, HamiltonianExchange const& obj ) {
697  obj.show( os );
698  return os;
699 }
700 
701 } //moves
702 } //protocols
703