@@ -36,69 +36,50 @@ namespace geode
3636 Undecided
3737 };
3838
39- template < typename Type >
39+ template < typename ObjectType >
4040 struct StepResult
4141 {
4242 MHDecision decision{ MHDecision::Undecided };
43- typename Proposal< Type >::Move move_type{
44- Proposal< Type >::Move::Invalid
45- };
43+ MoveType move_type{ MoveType::Invalid };
4644 double log_accept{ -std::numeric_limits< double >::infinity () };
4745 double delta_log_energy{ 0.0 };
4846 };
4947
50- template < typename Type >
48+ template < typename ObjectType >
5149 class MetropolisHastings
5250 {
5351 public:
54- MetropolisHastings ( GibbsEnergy< Type >& energy,
55- std::unique_ptr< ProposalKernel< Type > > proposal_kernel )
52+ MetropolisHastings ( GibbsEnergy< ObjectType >& energy,
53+ std::unique_ptr< ProposalKernel< ObjectType > > proposal_kernel )
5654 : energy_( energy ),
5755 proposal_kernel_ ( std::move( proposal_kernel ) )
5856 {
5957 OPENGEODE_ASSERT (
6058 proposal_kernel_ != nullptr , " [MH] null proposal kernel" );
6159 }
6260
63- ObjectSet< Type > initialize_object_set_with_sampling (
64- RandomEngine& engine,
65- const absl::flat_hash_map< uuid, index_t >& group_targets ) const
66- {
67- ObjectSet< Type > config;
68- for ( const auto & [subset_id, target] : group_targets )
69- {
70- config.add_subset ( subset_id );
71- while ( config.nb_objects_in_subset ( subset_id ) < target )
72- {
73- OPENGEODE_EXCEPTION ( try_birth ( config, subset_id, engine ),
74- " [MH] Birth move need to be more probable for group: " ,
75- subset_id.string () );
76- }
77- }
78- return config;
79- }
80-
81- StepResult< Type > step (
82- ObjectSet< Type >& state, RandomEngine& engine ) const
61+ StepResult< ObjectType > step (
62+ ObjectSet< ObjectType >& state, RandomEngine& engine ) const
8363 {
84- Proposal< Type > proposal =
64+ Proposal< ObjectType > proposal =
8565 proposal_kernel_->propose ( state, engine );
86- if ( proposal.type == Proposal< Type >::Move::Birth )
66+ const auto & move_type = proposal.proposed_move .type ;
67+ if ( move_type == MoveType::Birth )
8768 {
8869 return birth_step ( proposal, state, engine );
8970 }
90- if ( proposal. type == Proposal< Type >::Move ::Death )
71+ if ( move_type == MoveType ::Death )
9172 {
9273 return death_step ( proposal, state, engine );
9374 }
94- if ( proposal. type == Proposal< Type >::Move ::Change )
75+ if ( move_type == MoveType ::Change )
9576 {
9677 return change_step ( proposal, state, engine );
9778 }
98- return StepResult< Type >{};
79+ return StepResult< ObjectType >{};
9980 }
10081
101- void walk ( ObjectSet< Type >& state,
82+ void walk ( ObjectSet< ObjectType >& state,
10283 RandomEngine& engine,
10384 index_t nb_steps ) const
10485 {
@@ -109,7 +90,7 @@ namespace geode
10990 }
11091 }
11192
112- ObjectSet< Type > walk_copy ( ObjectSet< Type > initial,
93+ ObjectSet< ObjectType > walk_copy ( ObjectSet< ObjectType > initial,
11394 RandomEngine& engine,
11495 index_t nb_steps ) const
11596 {
@@ -162,57 +143,34 @@ namespace geode
162143 return 0.0 ;
163144 if ( log_accept >= 0.0 )
164145 return 1.0 ;
165- // prevent expoential overflow
146+ // prevent exponential overflow
166147 constexpr double LOG_MIN = -745.0 ;
167148 if ( log_accept < LOG_MIN )
168149 return 0.0 ;
169150 return std::exp ( log_accept );
170151 }
171152
172153 private:
173- bool try_birth ( ObjectSet< Type >& config,
174- const uuid& subset_id,
175- RandomEngine& engine ) const
154+ const double compute_log_accept ( const double deltaU,
155+ const ProposalProbabilities& proposal_probas ) const
176156 {
177- // pbirth_should be > 0.01
178- for ( const auto attempt : geode::Range{ 100 } )
179- {
180- geode_unused ( attempt );
181- auto proposal = proposal_kernel_->propose ( config, engine );
182- if ( proposal.type == Proposal< Type >::Move::Birth
183- && proposal.new_object ->second == subset_id )
184- {
185- config.add_object ( std::move ( proposal.new_object ->first ),
186- proposal.new_object ->second );
187- return true ;
188- }
189- }
190- return false ;
191- }
192-
193- const double compute_log_accept (
194- const double deltaU, const Proposal< Type >& proposal ) const
195- {
196- OPENGEODE_ASSERT (
197- std::isfinite ( proposal.log_forward_prob )
198- && std::isfinite ( proposal.log_backward_prob ),
199- " [MH] Non-finite proposal log-probabilities" );
200- return -beta_ * deltaU + proposal.log_backward_prob
201- - proposal.log_forward_prob ;
157+ return -beta_ * deltaU + proposal_probas.transition_probability ();
202158 }
203159
204160 template < typename ApplyMove >
205- StepResult< Type > accept_or_reject ( Proposal< Type >& proposal,
206- ObjectSet< Type >& state,
161+ StepResult< ObjectType > accept_or_reject (
162+ Proposal< ObjectType >& proposal,
163+ ObjectSet< ObjectType >& state,
207164 RandomEngine& engine,
208165 const double delta_log_energy,
209166 ApplyMove&& apply_move ) const
210167 {
211- StepResult< Type > step_result;
212- step_result.move_type = proposal.type ;
168+ const auto & proposed_move = proposal.proposed_move ;
169+ StepResult< ObjectType > step_result;
170+ step_result.move_type = proposed_move.type ;
213171 step_result.delta_log_energy = delta_log_energy;
214- step_result.log_accept =
215- compute_log_accept ( delta_log_energy, proposal );
172+ step_result.log_accept = compute_log_accept (
173+ delta_log_energy, proposed_move. proposal_probabilities );
216174
217175 double log_u = engine.sample_log ();
218176 step_result.decision = ( log_u < step_result.log_accept )
@@ -224,65 +182,55 @@ namespace geode
224182 return step_result;
225183 }
226184
227- StepResult< Type > birth_step ( Proposal< Type >& proposal,
228- ObjectSet< Type >& state,
185+ StepResult< ObjectType > birth_step ( Proposal< ObjectType >& proposal,
186+ ObjectSet< ObjectType >& state,
229187 RandomEngine& engine ) const
230188 {
231- OPENGEODE_ASSERT ( proposal.new_object .has_value (),
232- " [MH] Birth proposal has no new_object" );
233- geode::ObjectRef< Type > new_object{
234- proposal.new_object .value ().first ,
235- proposal.new_object .value ().second
236- };
189+ const auto new_object = proposal.new_object ();
237190 const auto delta_log_energy =
238191 energy_.delta_log_energy_add ( state, new_object );
239192 return accept_or_reject ( proposal, state, engine, delta_log_energy,
240- []( auto & s, auto & p ) {
241- s.add_object ( std::move ( p.new_object .value ().first ),
242- p.new_object .value ().second );
193+ []( auto & state, auto & proposal ) {
194+ state.add_object (
195+ std::move ( proposal.proposed_move .new_object .value () ),
196+ proposal.subset_id );
243197 } );
244198 };
245199
246- StepResult< Type > death_step ( Proposal< Type >& proposal,
247- ObjectSet< Type >& state,
200+ StepResult< ObjectType > death_step ( Proposal< ObjectType >& proposal,
201+ ObjectSet< ObjectType >& state,
248202 RandomEngine& engine ) const
249203 {
250- OPENGEODE_ASSERT ( proposal.old_object_id .has_value (),
251- " [MH] Death proposal has no index" );
252- const auto delta_log_energy = energy_.delta_log_energy_remove (
253- state, proposal.old_object_id .value () );
204+ const auto old_object_id = proposal.old_object_id ();
205+ const auto delta_log_energy =
206+ energy_.delta_log_energy_remove ( state, old_object_id );
254207 return accept_or_reject ( proposal, state, engine, delta_log_energy,
255- []( auto & s , auto & p ) {
256- s .remove_object ( p .old_object_id . value () );
208+ []( auto & state , auto & proposal ) {
209+ state .remove_object ( proposal .old_object_id () );
257210 } );
258211 };
259212
260- StepResult< Type > change_step ( Proposal< Type >& proposal,
261- ObjectSet< Type >& state,
213+ StepResult< ObjectType > change_step ( Proposal< ObjectType >& proposal,
214+ ObjectSet< ObjectType >& state,
262215 RandomEngine& engine ) const
263216 {
264- OPENGEODE_ASSERT ( proposal.new_object .has_value (),
265- " [MH] Change proposal has no new_object" );
266- OPENGEODE_ASSERT ( proposal.old_object_id .has_value (),
267- " [MH] Change proposal has no index" );
268- geode::ObjectRef< Type > new_object{
269- proposal.new_object .value ().first ,
270- proposal.new_object .value ().second
271- };
217+ const auto new_object = proposal.new_object ();
218+ const auto old_object_id = proposal.old_object_id ();
272219 const auto delta_log_energy = energy_.delta_log_energy_change (
273- state, proposal. old_object_id . value () , new_object );
220+ state, old_object_id, new_object );
274221 // should we test that objects are in the same group?
275222 // should be ensured by the dynamic
276223 return accept_or_reject ( proposal, state, engine, delta_log_energy,
277- []( auto & s, auto & p ) {
278- s.update_object ( p.old_object_id .value (),
279- std::move ( p.new_object .value ().first ) );
224+ []( auto & state, auto & proposal ) {
225+ state.update_object ( proposal.old_object_id (),
226+ std::move (
227+ proposal.proposed_move .new_object .value () ) );
280228 } );
281229 };
282230
283231 private:
284- const GibbsEnergy< Type >& energy_;
285- std::unique_ptr< ProposalKernel< Type > > proposal_kernel_;
232+ const GibbsEnergy< ObjectType >& energy_;
233+ std::unique_ptr< ProposalKernel< ObjectType > > proposal_kernel_;
286234 double beta_{ 1.0 };
287235 };
288236} // namespace geode
0 commit comments