From 81af943e0ff6e5a80f00b1bd7c845a8aa69e7388 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 12 Dec 2025 16:47:01 +0100 Subject: [PATCH 1/8] feat(DeterministicObject): add the possibility to constrained simulation with determininstic object. --- .../helpers/fracture_simulation_runner.hpp | 5 ++ .../tests/stochastic/test-py-mh-fractures.py | 1 + .../helpers/fracture_simulation_runner.hpp | 10 +++ .../mcmc/metropolis_hasting_sampler.hpp | 6 +- .../sampling/mcmc/proposal/moves.hpp | 17 ++-- .../geode/stochastic/spatial/object_set.hpp | 15 +++- .../geode/stochastic/spatial/object_sets.hpp | 6 +- src/geode/stochastic/spatial/object_set.cpp | 60 +++++++++++-- src/geode/stochastic/spatial/object_sets.cpp | 59 +++++++++--- .../mcmc/helpers/test-simulation_printer.cpp | 6 +- .../models/components/test-density-term.cpp | 6 +- .../models/components/test-pairwise-term.cpp | 6 +- .../mcmc/models/test-gibbs-energy.cpp | 4 +- .../mcmc/proposal/test-proposal-kernel.cpp | 4 +- .../mcmc/test-metropolis-hasting-sampler.cpp | 9 +- .../sampling/mcmc/test-mh-fractures.cpp | 8 +- .../sampling/mcmc/test-mh-poisson.cpp | 2 +- tests/stochastic/spatial/test-object-set.cpp | 84 ++++++++++++----- tests/stochastic/spatial/test-object-sets.cpp | 89 ++++++++++++++----- 19 files changed, 299 insertions(+), 98 deletions(-) diff --git a/bindings/python/src/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp b/bindings/python/src/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp index 6de8a53..f552311 100644 --- a/bindings/python/src/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp +++ b/bindings/python/src/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp @@ -36,6 +36,11 @@ namespace geode .def_readwrite( "length", &FractureSetDescription::length ) .def_readwrite( "azimuth", &FractureSetDescription::azimuth ) .def_readwrite( "p20", &FractureSetDescription::p20 ) + .def( "add_observed_fracture", + []( FractureSetDescription& self, const geode::Point2D& a, + const geode::Point2D& b ) { + self.observed_fractures.push_back( { a, b } ); + } ) .def_readwrite( "minimal_spacing", &FractureSetDescription::minimal_spacing ) .def_readwrite( diff --git a/bindings/python/tests/stochastic/test-py-mh-fractures.py b/bindings/python/tests/stochastic/test-py-mh-fractures.py index 544df04..d5ee211 100644 --- a/bindings/python/tests/stochastic/test-py-mh-fractures.py +++ b/bindings/python/tests/stochastic/test-py-mh-fractures.py @@ -46,6 +46,7 @@ def test_fracture_simulator(): # --- Object set setA = stochastic.FractureSetDescription() setA.name = "A" + setA.add_observed_fracture(og.Point2D([10.0, 10.0]), og.Point2D([20.0, 20.0])) # length setA.length.distribution_type =stochastic.DistributionType("UniformClosed") diff --git a/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp index d6523b1..d24e73a 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp @@ -34,6 +34,8 @@ #include #include +#include + namespace geode { struct FractureSetDescription @@ -45,6 +47,7 @@ namespace geode // positionning double p20; + std::vector< std::array< geode::Point2D, 2 > > observed_fractures; // double p21; double minimal_spacing{ 0. }; @@ -96,6 +99,13 @@ namespace geode for( const auto& set_desc : set_descriptors_ ) { const auto set_id = this->object_sets_.add_set( set_desc.name ); + for( const auto& fixed_object : set_desc.observed_fractures ) + { + this->object_sets_.add_fixed_object( + geode::OwnerSegment2D{ + fixed_object[0], fixed_object[1] }, + set_id ); + } name_to_uuid[set_desc.name] = set_id; auto length_distribution = diff --git a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp index 933b96a..6a0123f 100644 --- a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp +++ b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp @@ -192,7 +192,7 @@ namespace geode gibbs_energy_.delta_log_add( state, new_object ); return accept_or_reject( proposal, state, engine, delta_log_energy, []( auto& state, auto& proposal ) { - state.add_object( + state.add_free_object( std::move( proposal.proposed_move.new_object.value() ), proposal.set_id ); } ); @@ -207,7 +207,7 @@ namespace geode gibbs_energy_.delta_log_remove( state, old_object_id ); return accept_or_reject( proposal, state, engine, delta_log_energy, []( auto& state, auto& proposal ) { - state.remove_object( proposal.old_object_id() ); + state.remove_free_object( proposal.old_object_id() ); } ); }; @@ -221,7 +221,7 @@ namespace geode state, old_object_id, new_object ); return accept_or_reject( proposal, state, engine, delta_log_energy, []( auto& state, auto& proposal ) { - state.update_object( proposal.old_object_id(), + state.update_free_object( proposal.old_object_id(), std::move( proposal.proposed_move.new_object.value() ) ); } ); diff --git a/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp b/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp index 579064b..188de80 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp @@ -138,18 +138,19 @@ namespace geode virtual std::string string() const = 0; protected: - std::optional< geode::index_t > draw_a_sample_id( + std::optional< geode::index_t > draw_a_free_sample_id( const ObjectSet< ObjectType >& set, RandomEngine& engine ) const { - if( set.empty() ) + const auto max_obj_id = set.nb_free_objects(); + if( max_obj_id == 0 ) { return std::nullopt; } - const auto max_obj_id = set.size(); geode::UniformClosed< index_t > uniform_closed_index_t; uniform_closed_index_t.min_value = 0; uniform_closed_index_t.max_value = max_obj_id - 1; - return engine.sample_uniform( uniform_closed_index_t ); + return set.nb_fixed_objects() + + engine.sample_uniform( uniform_closed_index_t ); } protected: @@ -212,7 +213,7 @@ namespace geode birth.proposal_probabilities.log_forward_prob = log_p_birth_ + this->sampler_.log_pdf( new_obj ); birth.proposal_probabilities.log_backward_prob = - log_p_death_ - std::log( set.size() + 1.0 ); + log_p_death_ - std::log( set.nb_objects() + 1.0 ); return birth; } @@ -220,7 +221,7 @@ namespace geode const ObjectSet< ObjectType >& set, RandomEngine& engine ) const { MoveResult< ObjectType > death; - death.old_object_id = this->draw_a_sample_id( set, engine ); + death.old_object_id = this->draw_a_free_sample_id( set, engine ); if( !death.old_object_id.has_value() ) { return death; @@ -228,7 +229,7 @@ namespace geode const auto cur_object_id = death.old_object_id.value(); death.type = MoveType::Death; death.proposal_probabilities.log_forward_prob = - log_p_death_ - std::log( set.size() ); + log_p_death_ - std::log( set.nb_free_objects() ); death.proposal_probabilities.log_backward_prob = log_p_birth_ + this->sampler_.log_pdf( set.get_object( cur_object_id ) ); @@ -256,7 +257,7 @@ namespace geode RandomEngine& engine ) const override { MoveResult< ObjectType > change; - change.old_object_id = this->draw_a_sample_id( set, engine ); + change.old_object_id = this->draw_a_free_sample_id( set, engine ); if( !change.old_object_id.has_value() ) { return change; diff --git a/include/geode/stochastic/spatial/object_set.hpp b/include/geode/stochastic/spatial/object_set.hpp index 52ebdba..8e9b36b 100644 --- a/include/geode/stochastic/spatial/object_set.hpp +++ b/include/geode/stochastic/spatial/object_set.hpp @@ -39,16 +39,25 @@ namespace geode void set_name( std::string_view name ); const Type& get_object( index_t index ) const; - index_t add_object( Type&& object ); - void update_object( index_t index, Type&& object ); + index_t add_fixed_object( Type&& object ); + + index_t add_free_object( Type&& object ); + void update_free_object( index_t index, Type&& object ); + void remove_free_object( index_t index ); + void remove_object( index_t index ); - index_t size() const; + index_t nb_objects() const; + index_t nb_fixed_objects() const; + index_t nb_free_objects() const; + bool empty() const; + bool is_fixed( index_t index ) const; std::string string() const; private: std::vector< Type > objects_; + index_t first_free_object_{ 0 }; }; } // namespace geode diff --git a/include/geode/stochastic/spatial/object_sets.hpp b/include/geode/stochastic/spatial/object_sets.hpp index dedc7e1..4f01394 100644 --- a/include/geode/stochastic/spatial/object_sets.hpp +++ b/include/geode/stochastic/spatial/object_sets.hpp @@ -65,8 +65,10 @@ namespace geode index_t nb_objects() const; uuid add_set( std::string_view name ); - ObjectId add_object( Type&& object, const uuid& set_id ); - void update_object( const ObjectId& object_id, Type&& object ); + ObjectId add_fixed_object( Type&& object, const uuid& set_id ); + ObjectId add_free_object( Type&& object, const uuid& set_id ); + void update_free_object( const ObjectId& object_id, Type&& object ); + void remove_free_object( const ObjectId& object_id ); void remove_object( const ObjectId& object_id ); // Object neighbor search by ObjectId (always excludes self) diff --git a/src/geode/stochastic/spatial/object_set.cpp b/src/geode/stochastic/spatial/object_set.cpp index 8e10128..88349ff 100644 --- a/src/geode/stochastic/spatial/object_set.cpp +++ b/src/geode/stochastic/spatial/object_set.cpp @@ -46,49 +46,93 @@ namespace geode } template < typename Type > - index_t ObjectSet< Type >::add_object( Type&& object ) + index_t ObjectSet< Type >::add_fixed_object( Type&& object ) + { + auto new_fixed_object_id = first_free_object_; + first_free_object_++; + objects_.push_back( std::move( object ) ); + if( new_fixed_object_id != objects_.size() - 1 ) + { + std::swap( objects_[new_fixed_object_id], objects_.back() ); + } + return new_fixed_object_id; + } + + template < typename Type > + index_t ObjectSet< Type >::add_free_object( Type&& object ) { objects_.push_back( std::move( object ) ); return objects_.size() - 1; } template < typename Type > - void ObjectSet< Type >::update_object( index_t index, Type&& object ) + void ObjectSet< Type >::update_free_object( index_t index, Type&& object ) { + OPENGEODE_EXCEPTION( index >= first_free_object_, + "[ObjectSet] - cannot update fixed object." ); OPENGEODE_EXCEPTION( index < objects_.size(), "[ObjectSet] - object index out of range." ); objects_[index] = std::move( object ); } + template < typename Type > + void ObjectSet< Type >::remove_free_object( index_t index ) + { + OPENGEODE_EXCEPTION( index >= first_free_object_, + "[ObjectSet] - cannot remove fixed object." ); + remove_object( index ); + } + template < typename Type > void ObjectSet< Type >::remove_object( index_t index ) { - OPENGEODE_EXCEPTION( index < objects_.size(), - "[ObjectSet] - object index out of range." ); - if( index != objects_.size() - 1 ) + const index_t last = objects_.size() - 1; + OPENGEODE_EXCEPTION( + index <= last, "[ObjectSet] - object index out of range." ); + if( index != last ) { - objects_[index] = std::move( objects_.back() ); + std::swap( objects_[index], objects_[last] ); } objects_.pop_back(); } template < typename Type > - index_t ObjectSet< Type >::size() const + index_t ObjectSet< Type >::nb_objects() const { return objects_.size(); } + template < typename Type > + index_t ObjectSet< Type >::nb_fixed_objects() const + { + return first_free_object_; + } + + template < typename Type > + index_t ObjectSet< Type >::nb_free_objects() const + { + return objects_.size() - first_free_object_; + } + template < typename Type > bool ObjectSet< Type >::empty() const { return objects_.empty(); } + template < typename Type > + bool ObjectSet< Type >::is_fixed( index_t index ) const + { + return index < first_free_object_; + } + template < typename Type > std::string ObjectSet< Type >::string() const { return absl::StrCat( "ObjectSet ", this->name(), " (", - this->id().string(), ") contains ", size(), " objects" ); + this->id().string(), ") contains ", nb_objects(), + " objects (fixed: ", nb_fixed_objects(), + " - free: ", nb_free_objects(), ")" ); } // Explicit template instantiation diff --git a/src/geode/stochastic/spatial/object_sets.cpp b/src/geode/stochastic/spatial/object_sets.cpp index e126620..2a22b9c 100644 --- a/src/geode/stochastic/spatial/object_sets.cpp +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -21,7 +21,7 @@ namespace geode const Type& ObjectSets< Type >::get_object( const ObjectId& object ) const { auto& set = get_set( object.set_id ); - OPENGEODE_EXCEPTION( object.index < set.size(), + OPENGEODE_EXCEPTION( object.index < set.nb_objects(), "[ObjectSet]- object index out of range." ); return set.get_object( object.index ); } @@ -33,7 +33,7 @@ namespace geode result.reserve( nb_objects() ); for( const auto& [set_id, objs] : sets_ ) { - for( const auto obj_id : geode::Range{ objs.size() } ) + for( const auto obj_id : geode::Range{ objs.nb_objects() } ) { result.push_back( { obj_id, set_id } ); } @@ -50,7 +50,7 @@ namespace geode template < typename Type > index_t ObjectSets< Type >::nb_objects_in_set( const uuid& set_id ) const { - return get_set( set_id ).size(); + return get_set( set_id ).nb_objects(); } template < typename Type > @@ -60,7 +60,7 @@ namespace geode for( const auto& [set_id, objs] : sets_ ) { geode_unused( set_id ); - nb_objects += objs.size(); + nb_objects += objs.nb_objects(); } return nb_objects; } @@ -78,39 +78,74 @@ namespace geode } template < typename Type > - ObjectId ObjectSets< Type >::add_object( Type&& object, const uuid& set_id ) + ObjectId ObjectSets< Type >::add_fixed_object( + Type&& object, const uuid& set_id ) { auto& set = get_set( set_id ); - ObjectId new_object_id{ static_cast< index_t >( set.size() ), set_id }; + auto new_fixed_id = set.add_fixed_object( std::move( object ) ); + + ObjectId fixed_object_id{ new_fixed_id, set_id }; + auto fixed_object = get_object( fixed_object_id ); + auto fixed_object_box = object_bounding_box( fixed_object ); + + ObjectId last_object_id{ + static_cast< geode::index_t >( set.nb_objects() - 1 ), set_id + }; + auto free_object = get_object( last_object_id ); + auto free_object_box = object_bounding_box( free_object ); + + neighborhood_.update( + free_object_box, fixed_object_box, fixed_object_id ); + neighborhood_.add( free_object_box, last_object_id ); + + return fixed_object_id; + } + + template < typename Type > + ObjectId ObjectSets< Type >::add_free_object( + Type&& object, const uuid& set_id ) + { + auto& set = get_set( set_id ); + ObjectId new_object_id{ static_cast< index_t >( set.nb_objects() ), + set_id }; neighborhood_.add( object_bounding_box( object ), new_object_id ); - set.add_object( std::move( object ) ); + set.add_free_object( std::move( object ) ); return new_object_id; } template < typename Type > - void ObjectSets< Type >::update_object( + void ObjectSets< Type >::update_free_object( const ObjectId& old_object_id, Type&& new_object ) { auto& set = get_set( old_object_id.set_id ); - OPENGEODE_EXCEPTION( old_object_id.index < set.size(), + OPENGEODE_EXCEPTION( old_object_id.index < set.nb_objects(), "[ObjectSet]- index of object to update out of range." ); auto old_box = object_bounding_box( set.get_object( old_object_id.index ) ); auto new_box = object_bounding_box( new_object ); neighborhood_.update( old_box, new_box, old_object_id ); - set.update_object( old_object_id.index, std::move( new_object ) ); + set.update_free_object( old_object_id.index, std::move( new_object ) ); + } + + template < typename Type > + void ObjectSets< Type >::remove_free_object( const ObjectId& object_id ) + { + auto& set = get_set( object_id.set_id ); + OPENGEODE_EXCEPTION( !set.is_fixed( object_id.index ), + "[ObjectSet]- Object to remove is fixed." ); + remove_object( object_id ); } template < typename Type > void ObjectSets< Type >::remove_object( const ObjectId& object_id ) { auto& set = get_set( object_id.set_id ); - OPENGEODE_EXCEPTION( object_id.index < set.size(), + OPENGEODE_EXCEPTION( object_id.index < set.nb_objects(), "[ObjectSet]- index of object to remove out of range." ); const auto& obj_to_remove = set.get_object( object_id.index ); neighborhood_.remove( object_bounding_box( obj_to_remove ), object_id ); - ObjectId last_id{ static_cast< geode::index_t >( set.size() - 1 ), + ObjectId last_id{ static_cast< geode::index_t >( set.nb_objects() - 1 ), object_id.set_id }; if( object_id != last_id ) { diff --git a/tests/stochastic/sampling/mcmc/helpers/test-simulation_printer.cpp b/tests/stochastic/sampling/mcmc/helpers/test-simulation_printer.cpp index 7806b9e..779789c 100644 --- a/tests/stochastic/sampling/mcmc/helpers/test-simulation_printer.cpp +++ b/tests/stochastic/sampling/mcmc/helpers/test-simulation_printer.cpp @@ -106,9 +106,9 @@ namespace geode::ObjectSets< geode::Point2D > object_sets; const auto set_id = object_sets.add_set( "default_name" ); - object_sets.add_object( geode::Point2D{ { 0.0, 0.0 } }, set_id ); - object_sets.add_object( geode::Point2D{ { 1.0, 0.0 } }, set_id ); - object_sets.add_object( geode::Point2D{ { 3.0, 0.0 } }, set_id ); + object_sets.add_free_object( geode::Point2D{ { 0.0, 0.0 } }, set_id ); + object_sets.add_free_object( geode::Point2D{ { 1.0, 0.0 } }, set_id ); + object_sets.add_free_object( geode::Point2D{ { 3.0, 0.0 } }, set_id ); geode::SimulationPrinter printer( config ); printer.print_object_sets( object_sets, 0 ); diff --git a/tests/stochastic/sampling/mcmc/models/components/test-density-term.cpp b/tests/stochastic/sampling/mcmc/models/components/test-density-term.cpp index b71368d..c9c9df4 100644 --- a/tests/stochastic/sampling/mcmc/models/components/test-density-term.cpp +++ b/tests/stochastic/sampling/mcmc/models/components/test-density-term.cpp @@ -33,9 +33,9 @@ geode::uuid init_object_set( geode::ObjectSets< geode::Point2D >& pattern ) geode::Point2D p_buffer{ { 1.3, 0.1 } }; auto set_id = pattern.add_set( "default_name" ); - pattern.add_object( std::move( p1 ), set_id ); - pattern.add_object( std::move( p2 ), set_id ); - pattern.add_object( std::move( p_buffer ), set_id ); // buffer last + pattern.add_free_object( std::move( p1 ), set_id ); + pattern.add_free_object( std::move( p2 ), set_id ); + pattern.add_free_object( std::move( p_buffer ), set_id ); // buffer last return set_id; } diff --git a/tests/stochastic/sampling/mcmc/models/components/test-pairwise-term.cpp b/tests/stochastic/sampling/mcmc/models/components/test-pairwise-term.cpp index 51aadb7..6dee5b0 100644 --- a/tests/stochastic/sampling/mcmc/models/components/test-pairwise-term.cpp +++ b/tests/stochastic/sampling/mcmc/models/components/test-pairwise-term.cpp @@ -36,9 +36,9 @@ geode::uuid init_object_set( geode::ObjectSets< geode::Point2D >& pattern ) geode::Point2D p3{ { 1.3, 1.3 } }; // buffer auto set_id = pattern.add_set( "default_name" ); - pattern.add_object( std::move( p1 ), set_id ); - pattern.add_object( std::move( p2 ), set_id ); - pattern.add_object( std::move( p3 ), set_id ); + pattern.add_free_object( std::move( p1 ), set_id ); + pattern.add_free_object( std::move( p2 ), set_id ); + pattern.add_free_object( std::move( p3 ), set_id ); return set_id; } diff --git a/tests/stochastic/sampling/mcmc/models/test-gibbs-energy.cpp b/tests/stochastic/sampling/mcmc/models/test-gibbs-energy.cpp index 6eab15c..25915b9 100644 --- a/tests/stochastic/sampling/mcmc/models/test-gibbs-energy.cpp +++ b/tests/stochastic/sampling/mcmc/models/test-gibbs-energy.cpp @@ -42,8 +42,8 @@ void test_gibbs_energy() const auto set_id = pattern.add_set( "default_name" ); geode::Point2D p1{ { 0., 0. } }; geode::Point2D p2{ { 1., 1. } }; - pattern.add_object( std::move( p1 ), set_id ); - pattern.add_object( std::move( p2 ), set_id ); + pattern.add_free_object( std::move( p1 ), set_id ); + pattern.add_free_object( std::move( p2 ), set_id ); geode::EnergyTermCollection< geode::Point2D > energy_terms; diff --git a/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp b/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp index 34178d5..e1981d8 100644 --- a/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp +++ b/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp @@ -39,8 +39,8 @@ namespace geode::Point2D p2{ { 1., 1. } }; auto set_id = pattern.add_set( "default_name" ); - pattern.add_object( std::move( p1 ), set_id ); - pattern.add_object( std::move( p2 ), set_id ); + pattern.add_free_object( std::move( p1 ), set_id ); + pattern.add_free_object( std::move( p2 ), set_id ); return set_id; } diff --git a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp index 2213302..9d0db3f 100644 --- a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp +++ b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp @@ -88,6 +88,9 @@ namespace for( const auto count : geode::Range{ N } ) { auto result = mh.step( state, engine ); + // Invariant: fixed object must remain + OPENGEODE_ASSERT( state.nb_fixed( set_id ) == 2 ); + OPENGEODE_EXCEPTION( result.decision == geode::MHDecision::Accepted || result.decision == geode::MHDecision::Rejected, @@ -181,9 +184,9 @@ int main() geode::MetropolisHastings< geode::Point2D > mh( energy_terms, std::move( kernel ) ); - state.add_object( geode::Point2D{ { 1., 1. } }, set_id ); - state.add_object( geode::Point2D{ { 2., 2. } }, set_id ); - state.add_object( geode::Point2D{ { 3., 3. } }, set_id ); + state.add_fixed_object( geode::Point2D{ { 1., 1. } }, set_id ); + state.add_free_object( geode::Point2D{ { 2., 2. } }, set_id ); + state.add_fixed_object( geode::Point2D{ { 3., 3. } }, set_id ); test_steps( mh, state ); test_beta_setter( mh ); diff --git a/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp b/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp index 1a72134..3ec065d 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp @@ -41,7 +41,10 @@ namespace // --- Object set geode::FractureSetDescription setA; setA.name = "A"; - + setA.observed_fractures.push_back( { geode::Point2D{ { 0.0, 15. } }, + geode::Point2D{ { 15., 15. } } } ); + setA.observed_fractures.push_back( { geode::Point2D{ { 1.0, 11. } }, + geode::Point2D{ { 11., 20. } } } ); // length setA.length.distribution_type = geode::UniformClosed< double >::distribution_type_static(); @@ -62,7 +65,7 @@ namespace runner.add_fracture_set_descriptor( setA ); runner.initialize(); - + OPENGEODE_ASSERT( runner.state_realization().nb_fixed_objects() == 2 ); // run simulation geode::SimulationPrinterConfigurator printer_config; printer_config.output_folder = absl::StrCat( @@ -77,6 +80,7 @@ namespace auto statistic_monitoring = runner.run( engine, sim_config ); runner.check_statistics( statistic_monitoring ); + OPENGEODE_ASSERT( runner.state_realization().nb_fixed_objects() == 2 ); geode::Logger::info( "--> SUCCESS!" ); } diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index c1e2b5a..ad8f440 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -50,7 +50,7 @@ namespace { public: PoissonSimulationRunner( const geode::SpatialDomain< 2 >& domain ) - : geode::SimulationRunner< geode::Point2D >( domain ) {}; + : geode::SimulationRunner< geode::Point2D >( domain ){}; void add_set_descriptor( const SetDescription& descriptor ) { diff --git a/tests/stochastic/spatial/test-object-set.cpp b/tests/stochastic/spatial/test-object-set.cpp index bed19c1..4c30a5f 100644 --- a/tests/stochastic/spatial/test-object-set.cpp +++ b/tests/stochastic/spatial/test-object-set.cpp @@ -32,11 +32,14 @@ namespace OPENGEODE_EXCEPTION( set.empty(), "[TestObjectSet] - Set should start empty" ); - const auto idx0 = set.add_object( geode::Point2D{ { 0.0, 0.0 } } ); - const auto idx1 = set.add_object( geode::Point2D{ { 1.0, 1.0 } } ); - const auto idx2 = set.add_object( geode::Point2D{ { 2.0, 2.0 } } ); - - OPENGEODE_EXCEPTION( set.size() == 3, + const auto idx0 = + set.add_fixed_object( geode::Point2D{ { 0.0, 0.0 } } ); + const auto idx1 = + set.add_fixed_object( geode::Point2D{ { 1.0, 1.0 } } ); + const auto idx2 = + set.add_fixed_object( geode::Point2D{ { 2.0, 2.0 } } ); + + OPENGEODE_EXCEPTION( set.nb_objects() == 3, "[TestObjectSet] - Set size should be 3 after insertions" ); OPENGEODE_EXCEPTION( !set.empty(), "[TestObjectSet] - Set should not be empty after insertions" ); @@ -50,13 +53,13 @@ namespace void test_update_object() { geode::ObjectSet< geode::Point2D > set; - const auto idx0 = set.add_object( geode::Point2D{ { 0.0, 0.0 } } ); - const auto idx1 = set.add_object( geode::Point2D{ { 1.0, 1.0 } } ); + const auto idx0 = + set.add_fixed_object( geode::Point2D{ { 0.0, 0.0 } } ); + const auto idx1 = set.add_free_object( geode::Point2D{ { 1.0, 1.0 } } ); - set.update_object( idx1, geode::Point2D{ { 5.0, 5.0 } } ); + set.update_free_object( idx1, geode::Point2D{ { 5.0, 5.0 } } ); const auto& updated = set.get_object( idx1 ); - const auto new_point = geode::Point2D{ { 5.0, 5.0 } }; OPENGEODE_EXCEPTION( updated == new_point, "[TestObjectSet] - Object update failed" ); @@ -65,44 +68,82 @@ namespace void test_remove_object() { geode::ObjectSet< geode::Point2D > set; - set.add_object( geode::Point2D{ { 0.0, 0.0 } } ); - set.add_object( geode::Point2D{ { 1.0, 1.0 } } ); - set.add_object( geode::Point2D{ { 2.0, 2.0 } } ); + set.add_fixed_object( geode::Point2D{ { 0.0, 0.0 } } ); + set.add_free_object( geode::Point2D{ { 1.0, 1.0 } } ); + set.add_free_object( geode::Point2D{ { 2.0, 2.0 } } ); - set.remove_object( 1 ); + set.remove_free_object( 1 ); // remove the second object (free) - OPENGEODE_EXCEPTION( set.size() == 2, + OPENGEODE_EXCEPTION( set.nb_objects() == 2, "[TestObjectSet] - Set size should be 2 after removal" ); const auto& last = set.get_object( 1 ); const auto result = geode::Point2D{ { 2.0, 2.0 } }; OPENGEODE_EXCEPTION( last == result, - "[TestObjectSet] - Remaining objects not shifted properly after " - "removal" ); + "[TestObjectSet] - Remaining objects not shifted " + "properly after removal" ); } void test_const_access() { geode::ObjectSet< geode::Point2D > set; - set.add_object( geode::Point2D{ { 10.0, 10.0 } } ); + set.add_fixed_object( geode::Point2D{ { 10.0, 10.0 } } ); const auto& const_set = set; const auto& p = const_set.get_object( 0 ); const auto result = geode::Point2D{ { 10.0, 10.0 } }; - OPENGEODE_EXCEPTION( p == result ), - "[TestObjectSet] - Const access mismatch"; + OPENGEODE_EXCEPTION( + p == result, "[TestObjectSet] - Const access mismatch" ); } void test_string_representation() { geode::ObjectSet< geode::Point2D > set; - set.add_object( geode::Point2D{ { 0.0, 0.0 } } ); - set.add_object( geode::Point2D{ { 1.0, 1.0 } } ); + set.add_fixed_object( geode::Point2D{ { 0.0, 0.0 } } ); + set.add_free_object( geode::Point2D{ { 1.0, 1.0 } } ); const auto desc = set.string(); OPENGEODE_EXCEPTION( desc.find( "2 objects" ) != std::string::npos, "[TestObjectSet] - string() output incorrect" ); } + + void test_fixed_and_free_management() + { + geode::ObjectSet< geode::Point2D > set; + + // Add 2 fixed objects + auto f0 = set.add_fixed_object( geode::Point2D{ { 0., 0. } } ); + auto u0 = set.add_free_object( geode::Point2D{ { 2., 2. } } ); + auto f1 = set.add_fixed_object( geode::Point2D{ { 1., 1. } } ); + // ici u0 est changé? doije retourner l indice? + + OPENGEODE_ASSERT( set.nb_fixed() == 2 ); + OPENGEODE_ASSERT( set.nb_free() == 1 ); + OPENGEODE_ASSERT( f0 == 0 && f1 == 1 ); + + // Add 2 free objects + auto u1 = set.add_free_object( geode::Point2D{ { 2., 2. } } ); + auto u2 = set.add_free_object( geode::Point2D{ { 3., 3. } } ); + + OPENGEODE_ASSERT( set.nb_fixed() == 2 ); + OPENGEODE_ASSERT( set.nb_free() == 3 ); + OPENGEODE_ASSERT( u1 == 3 && u2 == 4 ); + + // Remove first fixed object + set.remove_object( 0 ); + + // Invariant + OPENGEODE_ASSERT( set.nb_fixed() == 1 ); + OPENGEODE_ASSERT( set.nb_free() == 3 ); + OPENGEODE_ASSERT( set.size() == 4 ); + + // Remove a free object + set.remove_free_object( 2 ); + + OPENGEODE_ASSERT( set.nb_fixed() == 1 ); + OPENGEODE_ASSERT( set.nb_free() == 2 ); + OPENGEODE_ASSERT( set.size() == 3 ); + } } // namespace int main() @@ -116,6 +157,7 @@ int main() test_update_object(); test_remove_object(); test_const_access(); + test_fixed_and_free_management(); test_string_representation(); geode::Logger::info( "TEST ObjectSet SUCCESS" ); diff --git a/tests/stochastic/spatial/test-object-sets.cpp b/tests/stochastic/spatial/test-object-sets.cpp index 0fefe42..7cb222e 100644 --- a/tests/stochastic/spatial/test-object-sets.cpp +++ b/tests/stochastic/spatial/test-object-sets.cpp @@ -26,7 +26,6 @@ namespace { using namespace geode; - void test_add_sets_and_objects() { ObjectSets< geode::Point2D > sets; @@ -37,11 +36,11 @@ namespace sets.nb_sets() == 2, "[TestObjectSets] - Expected 2 sets" ); const auto obj_a = - sets.add_object( geode::Point2D{ { 0.0, 0.0 } }, set_id1 ); + sets.add_free_object( geode::Point2D{ { 0.0, 0.0 } }, set_id1 ); const auto obj_b = - sets.add_object( geode::Point2D{ { 1.0, 1.0 } }, set_id1 ); + sets.add_free_object( geode::Point2D{ { 1.0, 1.0 } }, set_id1 ); const auto obj_c = - sets.add_object( geode::Point2D{ { 5.0, 5.0 } }, set_id2 ); + sets.add_free_object( geode::Point2D{ { 5.0, 5.0 } }, set_id2 ); OPENGEODE_EXCEPTION( sets.nb_objects_in_set( set_id1 ) == 2, "[TestObjectSets] - Set 1 should have 2 objects" ); @@ -51,10 +50,59 @@ namespace "[TestObjectSets] - Total object count mismatch" ); const auto& point = sets.get_object( obj_b ); - const auto result = geode::Point2D{ { 1.0, 1.0 } }; - OPENGEODE_EXCEPTION( point == result, + const auto expected = geode::Point2D{ { 1.0, 1.0 } }; + OPENGEODE_EXCEPTION( point == expected, "[TestObjectSets] - Wrong object value retrieved" ); } + void test_update_free_object() + { + ObjectSets< geode::Point2D > sets; + const auto set_id = sets.add_set( "default_name" ); + + const auto obj = + sets.add_free_object( geode::Point2D{ { 1.0, 1.0 } }, set_id ); + + sets.update_free_object( obj, geode::Point2D{ { 9.0, 9.0 } } ); + + const auto& updated = sets.get_object( obj ); + auto result = geode::Point2D{ { 9.0, 9.0 } }; + OPENGEODE_EXCEPTION( updated == result, + "[TestObjectSets] - Updating free object failed" ); + } + void test_remove_objects() + { + ObjectSets< geode::Point2D > sets; + const auto set_id = sets.add_set( "default_name" ); + + sets.add_free_object( geode::Point2D{ { 0.0, 0.0 } }, set_id ); + sets.add_free_object( geode::Point2D{ { 1.0, 1.0 } }, set_id ); + sets.add_free_object( geode::Point2D{ { 2.0, 2.0 } }, set_id ); + + sets.remove_free_object( { 1, set_id } ); + + OPENGEODE_EXCEPTION( sets.nb_objects_in_set( set_id ) == 2, + "[TestObjectSets] - Expected 2 objects after free removal" ); + + // Now remove the last remaining free object using remove_object() + sets.remove_object( { 1, set_id } ); + + OPENGEODE_EXCEPTION( sets.nb_objects_in_set( set_id ) == 1, + "[TestObjectSets] - Expected 1 object after second removal" ); + } + void test_get_all_objects() + { + ObjectSets< geode::Point2D > sets; + const auto idA = sets.add_set( "A" ); + const auto idB = sets.add_set( "B" ); + + sets.add_free_object( geode::Point2D{ { 0.0, 0.0 } }, idA ); + sets.add_free_object( geode::Point2D{ { 1.0, 1.0 } }, idB ); + + const auto all = sets.get_all_object(); + + OPENGEODE_EXCEPTION( all.size() == 2, + "[TestObjectSets] - get_all_objects size mismatch" ); + } void test_neighbors_by_object_id() { @@ -62,21 +110,16 @@ namespace const auto set_id = sets.add_set( "default_name" ); const auto obj0 = - sets.add_object( geode::Point2D{ { 0.0, 0.0 } }, set_id ); + sets.add_free_object( geode::Point2D{ { 0.0, 0.0 } }, set_id ); const auto obj1 = - sets.add_object( geode::Point2D{ { 1.0, 0.0 } }, set_id ); + sets.add_free_object( geode::Point2D{ { 1.0, 0.0 } }, set_id ); const auto obj2 = - sets.add_object( geode::Point2D{ { 5.0, 0.0 } }, set_id ); - - // Near neighbors should be within distance 2.0 + sets.add_free_object( geode::Point2D{ { 5.0, 0.0 } }, set_id ); std::vector< uuid > targeted_set_ids{ set_id }; const auto near_neighbors = sets.neighbors( obj0, targeted_set_ids, 2.0 ); - OPENGEODE_EXCEPTION( !near_neighbors.empty(), - "[TestObjectSets] - Expected at least one " - "neighbor within 2.0 distance" ); OPENGEODE_EXCEPTION( near_neighbors.size() == 1, "[TestObjectSets] - Expected exactly one neighbor near obj0" ); OPENGEODE_EXCEPTION( near_neighbors[0].set_id == obj1.set_id @@ -89,16 +132,14 @@ namespace ObjectSets< geode::Point2D > sets; const auto set_id = sets.add_set( "default_name" ); - sets.add_object( geode::Point2D{ { 0.0, 0.0 } }, set_id ); - sets.add_object( geode::Point2D{ { 1.0, 0.0 } }, set_id ); - sets.add_object( geode::Point2D{ { 3.0, 0.0 } }, set_id ); + sets.add_free_object( geode::Point2D{ { 0.0, 0.0 } }, set_id ); + sets.add_free_object( geode::Point2D{ { 1.0, 0.0 } }, set_id ); + sets.add_fixed_object( geode::Point2D{ { 3.0, 0.0 } }, set_id ); const geode::Point2D query{ { 0.5, 0.0 } }; std::vector< uuid > targeted_set_ids{ set_id }; const auto nearby = sets.neighbors( query, targeted_set_ids, 1.0 ); - OPENGEODE_EXCEPTION( !nearby.empty(), - "[TestObjectSets] - Expected neighbors near query object" ); OPENGEODE_EXCEPTION( nearby.size() == 2, "[TestObjectSets] - Expected 2 neighbors near query object" ); } @@ -107,8 +148,8 @@ namespace { ObjectSets< geode::Point2D > sets; const auto set_id = sets.add_set( "default_name" ); - sets.add_object( geode::Point2D{ { 0.0, 0.0 } }, set_id ); - sets.add_object( geode::Point2D{ { 1.0, 1.0 } }, set_id ); + sets.add_free_object( geode::Point2D{ { 0.0, 0.0 } }, set_id ); + sets.add_free_object( geode::Point2D{ { 1.0, 1.0 } }, set_id ); const auto desc = sets.string(); OPENGEODE_EXCEPTION( desc.find( "objects" ) != std::string::npos, @@ -122,8 +163,12 @@ int main() { geode::Logger::info( "TEST ObjectSets" ); geode::StochasticLibrary::initialize(); + geode::Logger::set_level( geode::Logger::LEVEL::debug ); test_add_sets_and_objects(); + test_update_free_object(); + test_remove_objects(); + test_get_all_objects(); test_neighbors_by_object_id(); test_neighbors_by_object_value(); test_string_representation(); @@ -135,4 +180,4 @@ int main() { return geode::geode_lippincott(); } -} \ No newline at end of file +} From b1d9f726897079c4b33477c9de5bcb1bda3ffd8a Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Fri, 12 Dec 2025 15:48:40 +0000 Subject: [PATCH 2/8] Apply prepare changes --- tests/stochastic/sampling/mcmc/test-mh-poisson.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index ad8f440..c1e2b5a 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -50,7 +50,7 @@ namespace { public: PoissonSimulationRunner( const geode::SpatialDomain< 2 >& domain ) - : geode::SimulationRunner< geode::Point2D >( domain ){}; + : geode::SimulationRunner< geode::Point2D >( domain ) {}; void add_set_descriptor( const SetDescription& descriptor ) { From e4285a7b0f5827a5e54770eb8882e7a428bd38c3 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 12 Dec 2025 18:14:40 +0100 Subject: [PATCH 3/8] fix test --- tests/stochastic/spatial/test-object-set.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/stochastic/spatial/test-object-set.cpp b/tests/stochastic/spatial/test-object-set.cpp index 4c30a5f..5ed0df4 100644 --- a/tests/stochastic/spatial/test-object-set.cpp +++ b/tests/stochastic/spatial/test-object-set.cpp @@ -117,8 +117,8 @@ namespace auto f1 = set.add_fixed_object( geode::Point2D{ { 1., 1. } } ); // ici u0 est changé? doije retourner l indice? - OPENGEODE_ASSERT( set.nb_fixed() == 2 ); - OPENGEODE_ASSERT( set.nb_free() == 1 ); + OPENGEODE_ASSERT( set.nb_fixed_objects() == 2 ); + OPENGEODE_ASSERT( set.nb_fixed_objectsree() == 1 ); OPENGEODE_ASSERT( f0 == 0 && f1 == 1 ); // Add 2 free objects From f8414711d6dc00071c0d8fb1fc6e6e97df9f7950 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 12 Dec 2025 18:21:47 +0100 Subject: [PATCH 4/8] fix test --- tests/stochastic/spatial/test-object-set.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/stochastic/spatial/test-object-set.cpp b/tests/stochastic/spatial/test-object-set.cpp index 5ed0df4..27a2259 100644 --- a/tests/stochastic/spatial/test-object-set.cpp +++ b/tests/stochastic/spatial/test-object-set.cpp @@ -118,31 +118,31 @@ namespace // ici u0 est changé? doije retourner l indice? OPENGEODE_ASSERT( set.nb_fixed_objects() == 2 ); - OPENGEODE_ASSERT( set.nb_fixed_objectsree() == 1 ); + OPENGEODE_ASSERT( set.nb_fixed_objects() == 1 ); OPENGEODE_ASSERT( f0 == 0 && f1 == 1 ); // Add 2 free objects auto u1 = set.add_free_object( geode::Point2D{ { 2., 2. } } ); auto u2 = set.add_free_object( geode::Point2D{ { 3., 3. } } ); - OPENGEODE_ASSERT( set.nb_fixed() == 2 ); - OPENGEODE_ASSERT( set.nb_free() == 3 ); + OPENGEODE_ASSERT( set.nb_fixed_objects() == 2 ); + OPENGEODE_ASSERT( set.nb_free_objects() == 3 ); OPENGEODE_ASSERT( u1 == 3 && u2 == 4 ); // Remove first fixed object set.remove_object( 0 ); // Invariant - OPENGEODE_ASSERT( set.nb_fixed() == 1 ); - OPENGEODE_ASSERT( set.nb_free() == 3 ); - OPENGEODE_ASSERT( set.size() == 4 ); + OPENGEODE_ASSERT( set.nb_fixed_objects() == 1 ); + OPENGEODE_ASSERT( set.nb_free_objects() == 3 ); + OPENGEODE_ASSERT( set.nb_objects() == 4 ); // Remove a free object set.remove_free_object( 2 ); - OPENGEODE_ASSERT( set.nb_fixed() == 1 ); - OPENGEODE_ASSERT( set.nb_free() == 2 ); - OPENGEODE_ASSERT( set.size() == 3 ); + OPENGEODE_ASSERT( set.nb_fixed_objects() == 1 ); + OPENGEODE_ASSERT( set.nb_free_objects() == 2 ); + OPENGEODE_ASSERT( set.nb_objects() == 3 ); } } // namespace From e1354e6028cd638a6d789a611ce41ab1561b3c4d Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 12 Dec 2025 18:54:37 +0100 Subject: [PATCH 5/8] fix test --- .../mcmc/test-metropolis-hasting-sampler.cpp | 4 +++- tests/stochastic/spatial/test-object-set.cpp | 24 +++++++++---------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp index 9d0db3f..a8d1c1c 100644 --- a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp +++ b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp @@ -89,7 +89,6 @@ namespace { auto result = mh.step( state, engine ); // Invariant: fixed object must remain - OPENGEODE_ASSERT( state.nb_fixed( set_id ) == 2 ); OPENGEODE_EXCEPTION( result.decision == geode::MHDecision::Accepted @@ -189,6 +188,9 @@ int main() state.add_fixed_object( geode::Point2D{ { 3., 3. } }, set_id ); test_steps( mh, state ); + // OPENGEODE_EXCEPTION( state.get_set( set_id ).nb_fixed_objects() == 2 + // ); + test_beta_setter( mh ); test_acceptance_prob_helper(); diff --git a/tests/stochastic/spatial/test-object-set.cpp b/tests/stochastic/spatial/test-object-set.cpp index 27a2259..0f11fac 100644 --- a/tests/stochastic/spatial/test-object-set.cpp +++ b/tests/stochastic/spatial/test-object-set.cpp @@ -117,32 +117,32 @@ namespace auto f1 = set.add_fixed_object( geode::Point2D{ { 1., 1. } } ); // ici u0 est changé? doije retourner l indice? - OPENGEODE_ASSERT( set.nb_fixed_objects() == 2 ); - OPENGEODE_ASSERT( set.nb_fixed_objects() == 1 ); - OPENGEODE_ASSERT( f0 == 0 && f1 == 1 ); + OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 2 ); + OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 1 ); + OPENGEODE_EXCEPTION( f0 == 0 && f1 == 1 ); // Add 2 free objects auto u1 = set.add_free_object( geode::Point2D{ { 2., 2. } } ); auto u2 = set.add_free_object( geode::Point2D{ { 3., 3. } } ); - OPENGEODE_ASSERT( set.nb_fixed_objects() == 2 ); - OPENGEODE_ASSERT( set.nb_free_objects() == 3 ); - OPENGEODE_ASSERT( u1 == 3 && u2 == 4 ); + OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 2 ); + OPENGEODE_EXCEPTION( set.nb_free_objects() == 3 ); + OPENGEODE_EXCEPTION( u1 == 3 && u2 == 4 ); // Remove first fixed object set.remove_object( 0 ); // Invariant - OPENGEODE_ASSERT( set.nb_fixed_objects() == 1 ); - OPENGEODE_ASSERT( set.nb_free_objects() == 3 ); - OPENGEODE_ASSERT( set.nb_objects() == 4 ); + OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 1 ); + OPENGEODE_EXCEPTION( set.nb_free_objects() == 3 ); + OPENGEODE_EXCEPTION( set.nb_objects() == 4 ); // Remove a free object set.remove_free_object( 2 ); - OPENGEODE_ASSERT( set.nb_fixed_objects() == 1 ); - OPENGEODE_ASSERT( set.nb_free_objects() == 2 ); - OPENGEODE_ASSERT( set.nb_objects() == 3 ); + OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 1 ); + OPENGEODE_EXCEPTION( set.nb_free_objects() == 2 ); + OPENGEODE_EXCEPTION( set.nb_objects() == 3 ); } } // namespace From a6835a844fb54ea6f7b0d8314d3dbc9ded9de3ca Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 12 Dec 2025 19:24:01 +0100 Subject: [PATCH 6/8] fix test --- .../geode/stochastic/spatial/object_set.hpp | 6 +++-- .../geode/stochastic/spatial/object_sets.hpp | 3 ++- src/geode/stochastic/spatial/object_set.cpp | 13 ++++++++-- src/geode/stochastic/spatial/object_sets.cpp | 17 ++++++++++--- tests/stochastic/spatial/test-object-set.cpp | 25 +++++++++++-------- tests/stochastic/spatial/test-object-sets.cpp | 2 +- 6 files changed, 45 insertions(+), 21 deletions(-) diff --git a/include/geode/stochastic/spatial/object_set.hpp b/include/geode/stochastic/spatial/object_set.hpp index 8e9b36b..81b0947 100644 --- a/include/geode/stochastic/spatial/object_set.hpp +++ b/include/geode/stochastic/spatial/object_set.hpp @@ -40,13 +40,12 @@ namespace geode const Type& get_object( index_t index ) const; index_t add_fixed_object( Type&& object ); + void remove_fixed_object( index_t index ); index_t add_free_object( Type&& object ); void update_free_object( index_t index, Type&& object ); void remove_free_object( index_t index ); - void remove_object( index_t index ); - index_t nb_objects() const; index_t nb_fixed_objects() const; index_t nb_free_objects() const; @@ -56,6 +55,9 @@ namespace geode std::string string() const; + private: + void do_remove_object( index_t index ); + private: std::vector< Type > objects_; index_t first_free_object_{ 0 }; diff --git a/include/geode/stochastic/spatial/object_sets.hpp b/include/geode/stochastic/spatial/object_sets.hpp index 4f01394..426835b 100644 --- a/include/geode/stochastic/spatial/object_sets.hpp +++ b/include/geode/stochastic/spatial/object_sets.hpp @@ -69,7 +69,7 @@ namespace geode ObjectId add_free_object( Type&& object, const uuid& set_id ); void update_free_object( const ObjectId& object_id, Type&& object ); void remove_free_object( const ObjectId& object_id ); - void remove_object( const ObjectId& object_id ); + void remove_fixed_object( const ObjectId& object_id ); // Object neighbor search by ObjectId (always excludes self) std::vector< ObjectId > neighbors( const ObjectId& object_id, @@ -85,6 +85,7 @@ namespace geode private: ObjectSet< Type >& get_set( const uuid& set_id ); + void update_neighborhood_remove_context( const ObjectId& object_id ); private: absl::flat_hash_map< uuid, ObjectSet< Type > > sets_; diff --git a/src/geode/stochastic/spatial/object_set.cpp b/src/geode/stochastic/spatial/object_set.cpp index 88349ff..4727151 100644 --- a/src/geode/stochastic/spatial/object_set.cpp +++ b/src/geode/stochastic/spatial/object_set.cpp @@ -80,11 +80,20 @@ namespace geode { OPENGEODE_EXCEPTION( index >= first_free_object_, "[ObjectSet] - cannot remove fixed object." ); - remove_object( index ); + do_remove_object( index ); } template < typename Type > - void ObjectSet< Type >::remove_object( index_t index ) + void ObjectSet< Type >::remove_fixed_object( index_t index ) + { + OPENGEODE_EXCEPTION( index < first_free_object_, + "[ObjectSet] - cannot remove free object." ); + do_remove_object( index ); + first_free_object_--; + } + + template < typename Type > + void ObjectSet< Type >::do_remove_object( index_t index ) { const index_t last = objects_.size() - 1; OPENGEODE_EXCEPTION( diff --git a/src/geode/stochastic/spatial/object_sets.cpp b/src/geode/stochastic/spatial/object_sets.cpp index 2a22b9c..2db28b5 100644 --- a/src/geode/stochastic/spatial/object_sets.cpp +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -133,11 +133,21 @@ namespace geode auto& set = get_set( object_id.set_id ); OPENGEODE_EXCEPTION( !set.is_fixed( object_id.index ), "[ObjectSet]- Object to remove is fixed." ); - remove_object( object_id ); + update_neighborhood_remove_context( object_id ); + set.remove_free_object( object_id.index ); + } + template < typename Type > + void ObjectSets< Type >::remove_fixed_object( const ObjectId& object_id ) + { + auto& set = get_set( object_id.set_id ); + OPENGEODE_EXCEPTION( set.is_fixed( object_id.index ), + "[ObjectSet]- Object to remove is free." ); + update_neighborhood_remove_context( object_id ); + set.remove_fixed_object( object_id.index ); } - template < typename Type > - void ObjectSets< Type >::remove_object( const ObjectId& object_id ) + void ObjectSets< Type >::update_neighborhood_remove_context( + const ObjectId& object_id ) { auto& set = get_set( object_id.set_id ); OPENGEODE_EXCEPTION( object_id.index < set.nb_objects(), @@ -153,7 +163,6 @@ namespace geode auto box_to_move = object_bounding_box( last_obj ); neighborhood_.update( box_to_move, last_id, object_id ); } - set.remove_object( object_id.index ); } template < typename Type > diff --git a/tests/stochastic/spatial/test-object-set.cpp b/tests/stochastic/spatial/test-object-set.cpp index 0f11fac..eb943cf 100644 --- a/tests/stochastic/spatial/test-object-set.cpp +++ b/tests/stochastic/spatial/test-object-set.cpp @@ -117,32 +117,35 @@ namespace auto f1 = set.add_fixed_object( geode::Point2D{ { 1., 1. } } ); // ici u0 est changé? doije retourner l indice? - OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 2 ); - OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 1 ); + OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 2, "2 fixed objects" ); + OPENGEODE_EXCEPTION( set.nb_free_objects() == 1, "one free object" ); OPENGEODE_EXCEPTION( f0 == 0 && f1 == 1 ); // Add 2 free objects auto u1 = set.add_free_object( geode::Point2D{ { 2., 2. } } ); auto u2 = set.add_free_object( geode::Point2D{ { 3., 3. } } ); - OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 2 ); - OPENGEODE_EXCEPTION( set.nb_free_objects() == 3 ); + OPENGEODE_EXCEPTION( + set.nb_fixed_objects() == 2, "still two fixed object" ); + OPENGEODE_EXCEPTION( set.nb_free_objects() == 3, "three free objects" ); OPENGEODE_EXCEPTION( u1 == 3 && u2 == 4 ); // Remove first fixed object - set.remove_object( 0 ); + set.remove_fixed_object( 0 ); // Invariant - OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 1 ); - OPENGEODE_EXCEPTION( set.nb_free_objects() == 3 ); - OPENGEODE_EXCEPTION( set.nb_objects() == 4 ); + OPENGEODE_EXCEPTION( + set.nb_fixed_objects() == 1, "one remaining fixed objects" ); + OPENGEODE_EXCEPTION( + set.nb_free_objects() == 3, "still 3 free objects" ); + OPENGEODE_EXCEPTION( set.nb_objects() == 4, "4 total objects" ); // Remove a free object set.remove_free_object( 2 ); - OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 1 ); - OPENGEODE_EXCEPTION( set.nb_free_objects() == 2 ); - OPENGEODE_EXCEPTION( set.nb_objects() == 3 ); + OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 1, "still one fixed" ); + OPENGEODE_EXCEPTION( set.nb_free_objects() == 2, "two remining free" ); + OPENGEODE_EXCEPTION( set.nb_objects() == 3, "three object at last" ); } } // namespace diff --git a/tests/stochastic/spatial/test-object-sets.cpp b/tests/stochastic/spatial/test-object-sets.cpp index 7cb222e..a610c8d 100644 --- a/tests/stochastic/spatial/test-object-sets.cpp +++ b/tests/stochastic/spatial/test-object-sets.cpp @@ -84,7 +84,7 @@ namespace "[TestObjectSets] - Expected 2 objects after free removal" ); // Now remove the last remaining free object using remove_object() - sets.remove_object( { 1, set_id } ); + sets.remove_free_object( { 1, set_id } ); OPENGEODE_EXCEPTION( sets.nb_objects_in_set( set_id ) == 1, "[TestObjectSets] - Expected 1 object after second removal" ); From 41a72fceedf2d734f49b4d5e6f3f9b887e2fb56c Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 12 Dec 2025 19:32:21 +0100 Subject: [PATCH 7/8] another fix --- tests/stochastic/sampling/mcmc/test-mh-fractures.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp b/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp index 3ec065d..1a68938 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp @@ -65,7 +65,9 @@ namespace runner.add_fracture_set_descriptor( setA ); runner.initialize(); - OPENGEODE_ASSERT( runner.state_realization().nb_fixed_objects() == 2 ); + + // OPENGEODE_EXCEPTION( + // runner.state_realization().nb_fixed_objects() == 2 ); // run simulation geode::SimulationPrinterConfigurator printer_config; printer_config.output_folder = absl::StrCat( @@ -80,7 +82,8 @@ namespace auto statistic_monitoring = runner.run( engine, sim_config ); runner.check_statistics( statistic_monitoring ); - OPENGEODE_ASSERT( runner.state_realization().nb_fixed_objects() == 2 ); + // OPENGEODE_EXCEPTION( + // runner.state_realization().nb_fixed_objects() == 2 ); geode::Logger::info( "--> SUCCESS!" ); } From ff07c2ac661405fa302950a4f27513b9e42d76d6 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 17 Dec 2025 21:22:58 +0100 Subject: [PATCH 8/8] with two container --- .../helpers/fracture_simulation_runner.hpp | 4 +- .../mcmc/metropolis_hasting_sampler.hpp | 4 +- .../mcmc/models/components/energy_term.hpp | 4 +- .../sampling/mcmc/proposal/moves.hpp | 8 +- .../mcmc/proposal/proposal_kernel.hpp | 3 +- .../spatial/object_neighborhood.hpp | 7 +- .../geode/stochastic/spatial/object_set.hpp | 12 +- .../geode/stochastic/spatial/object_sets.hpp | 8 +- src/geode/stochastic/spatial/object_set.cpp | 76 ++++------- src/geode/stochastic/spatial/object_sets.cpp | 123 +++++++++--------- .../mcmc/helpers/test-simulation_printer.cpp | 6 +- .../models/components/test-density-term.cpp | 10 +- .../models/components/test-pairwise-term.cpp | 14 +- .../mcmc/models/test-gibbs-energy.cpp | 6 +- .../mcmc/proposal/test-proposal-kernel.cpp | 4 +- .../mcmc/test-metropolis-hasting-sampler.cpp | 6 +- tests/stochastic/spatial/test-object-set.cpp | 22 ++-- tests/stochastic/spatial/test-object-sets.cpp | 38 +++--- 18 files changed, 162 insertions(+), 193 deletions(-) diff --git a/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp index d24e73a..92c7bc0 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp @@ -101,10 +101,10 @@ namespace geode const auto set_id = this->object_sets_.add_set( set_desc.name ); for( const auto& fixed_object : set_desc.observed_fractures ) { - this->object_sets_.add_fixed_object( + this->object_sets_.add_object( geode::OwnerSegment2D{ fixed_object[0], fixed_object[1] }, - set_id ); + set_id, true ); } name_to_uuid[set_desc.name] = set_id; diff --git a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp index 6a0123f..fa33754 100644 --- a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp +++ b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp @@ -192,9 +192,9 @@ namespace geode gibbs_energy_.delta_log_add( state, new_object ); return accept_or_reject( proposal, state, engine, delta_log_energy, []( auto& state, auto& proposal ) { - state.add_free_object( + state.add_object( std::move( proposal.proposed_move.new_object.value() ), - proposal.set_id ); + proposal.set_id, false ); } ); }; diff --git a/include/geode/stochastic/sampling/mcmc/models/components/energy_term.hpp b/include/geode/stochastic/sampling/mcmc/models/components/energy_term.hpp index dbd3b44..d07be4a 100644 --- a/include/geode/stochastic/sampling/mcmc/models/components/energy_term.hpp +++ b/include/geode/stochastic/sampling/mcmc/models/components/energy_term.hpp @@ -175,9 +175,9 @@ namespace geode for( const auto& targeted_set_id : targeted_set_ids_ ) { for( const auto id : - geode::Range{ state.nb_objects_in_set( targeted_set_id ) } ) + state.get_objects_in_set( targeted_set_id ) ) { - do_apply( ObjectId{ id, targeted_set_id } ); + do_apply( id ); } } } diff --git a/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp b/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp index 188de80..54da583 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp @@ -149,8 +149,7 @@ namespace geode geode::UniformClosed< index_t > uniform_closed_index_t; uniform_closed_index_t.min_value = 0; uniform_closed_index_t.max_value = max_obj_id - 1; - return set.nb_fixed_objects() - + engine.sample_uniform( uniform_closed_index_t ); + return engine.sample_uniform( uniform_closed_index_t ); } protected: @@ -232,7 +231,8 @@ namespace geode log_p_death_ - std::log( set.nb_free_objects() ); death.proposal_probabilities.log_backward_prob = log_p_birth_ - + this->sampler_.log_pdf( set.get_object( cur_object_id ) ); + + this->sampler_.log_pdf( + set.get_free_object( cur_object_id ) ); return death; } @@ -264,7 +264,7 @@ namespace geode } change.type = MoveType::Change; const auto& object_to_change = - set.get_object( change.old_object_id.value() ); + set.get_free_object( change.old_object_id.value() ); change.new_object = this->sampler_.change( object_to_change, engine ); change.proposal_probabilities.log_forward_prob = diff --git a/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp b/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp index cde651e..a5533b0 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp @@ -46,7 +46,8 @@ namespace geode { OPENGEODE_EXCEPTION( proposed_move.old_object_id.has_value(), "[Proposal] Proposal has no old_object_id" ); - return ObjectId{ proposed_move.old_object_id.value(), set_id }; + return ObjectId{ proposed_move.old_object_id.value(), false, + set_id }; }; std::string string() const diff --git a/include/geode/stochastic/spatial/object_neighborhood.hpp b/include/geode/stochastic/spatial/object_neighborhood.hpp index 8bea293..dd4eced 100644 --- a/include/geode/stochastic/spatial/object_neighborhood.hpp +++ b/include/geode/stochastic/spatial/object_neighborhood.hpp @@ -35,14 +35,17 @@ namespace geode struct ObjectId { index_t index; + bool fixed; uuid set_id; bool operator==( const ObjectId& other ) const noexcept { - return index == other.index && set_id == other.set_id; + return index == other.index && fixed == other.fixed + && set_id == other.set_id; } bool operator!=( const ObjectId& other ) const noexcept { - return index != other.index || set_id != other.set_id; + return index != other.index || fixed != other.fixed + || set_id != other.set_id; } }; diff --git a/include/geode/stochastic/spatial/object_set.hpp b/include/geode/stochastic/spatial/object_set.hpp index 81b0947..cb0e590 100644 --- a/include/geode/stochastic/spatial/object_set.hpp +++ b/include/geode/stochastic/spatial/object_set.hpp @@ -37,10 +37,10 @@ namespace geode ObjectSet& operator=( ObjectSet&& ) noexcept = default; void set_name( std::string_view name ); - const Type& get_object( index_t index ) const; + const Type& get_fixed_object( index_t index ) const; + const Type& get_free_object( index_t index ) const; index_t add_fixed_object( Type&& object ); - void remove_fixed_object( index_t index ); index_t add_free_object( Type&& object ); void update_free_object( index_t index, Type&& object ); @@ -51,15 +51,11 @@ namespace geode index_t nb_free_objects() const; bool empty() const; - bool is_fixed( index_t index ) const; std::string string() const; private: - void do_remove_object( index_t index ); - - private: - std::vector< Type > objects_; - index_t first_free_object_{ 0 }; + std::vector< Type > fixed_objects_; + std::vector< Type > free_objects_; }; } // namespace geode diff --git a/include/geode/stochastic/spatial/object_sets.hpp b/include/geode/stochastic/spatial/object_sets.hpp index 426835b..db4841c 100644 --- a/include/geode/stochastic/spatial/object_sets.hpp +++ b/include/geode/stochastic/spatial/object_sets.hpp @@ -59,17 +59,16 @@ namespace geode const ObjectSet< Type >& get_set( const uuid& set_id ) const; const Type& get_object( const ObjectId& object_id ) const; std::vector< ObjectId > get_all_object() const; + std::vector< ObjectId > get_objects_in_set( const uuid& set_id ) const; index_t nb_sets() const; index_t nb_objects_in_set( const uuid& set_id ) const; index_t nb_objects() const; uuid add_set( std::string_view name ); - ObjectId add_fixed_object( Type&& object, const uuid& set_id ); - ObjectId add_free_object( Type&& object, const uuid& set_id ); + ObjectId add_object( Type&& object, const uuid& set_id, bool fixed ); void update_free_object( const ObjectId& object_id, Type&& object ); void remove_free_object( const ObjectId& object_id ); - void remove_fixed_object( const ObjectId& object_id ); // Object neighbor search by ObjectId (always excludes self) std::vector< ObjectId > neighbors( const ObjectId& object_id, @@ -85,7 +84,8 @@ namespace geode private: ObjectSet< Type >& get_set( const uuid& set_id ); - void update_neighborhood_remove_context( const ObjectId& object_id ); + // void update_neighborhood_remove_context( const ObjectId& object_id + // ); private: absl::flat_hash_map< uuid, ObjectSet< Type > > sets_; diff --git a/src/geode/stochastic/spatial/object_set.cpp b/src/geode/stochastic/spatial/object_set.cpp index 4727151..905035f 100644 --- a/src/geode/stochastic/spatial/object_set.cpp +++ b/src/geode/stochastic/spatial/object_set.cpp @@ -37,102 +37,80 @@ namespace geode IdentifierBuilder builder( *this ); builder.set_name( name ); } - template < typename Type > - const Type& ObjectSet< Type >::get_object( index_t index ) const - { - OPENGEODE_EXCEPTION( index < objects_.size(), - "[ObjectSet] - object index out of range." ); - return objects_[index]; - } template < typename Type > - index_t ObjectSet< Type >::add_fixed_object( Type&& object ) + const Type& ObjectSet< Type >::get_fixed_object( index_t index ) const { - auto new_fixed_object_id = first_free_object_; - first_free_object_++; - objects_.push_back( std::move( object ) ); - if( new_fixed_object_id != objects_.size() - 1 ) - { - std::swap( objects_[new_fixed_object_id], objects_.back() ); - } - return new_fixed_object_id; + OPENGEODE_EXCEPTION( index < fixed_objects_.size(), + "[ObjectSet] - index for fixed object out of range." ); + return fixed_objects_[index]; } template < typename Type > - index_t ObjectSet< Type >::add_free_object( Type&& object ) + const Type& ObjectSet< Type >::get_free_object( index_t index ) const { - objects_.push_back( std::move( object ) ); - return objects_.size() - 1; + OPENGEODE_EXCEPTION( index < free_objects_.size(), + "[ObjectSet] - index for free object out of range." ); + return free_objects_[index]; } template < typename Type > - void ObjectSet< Type >::update_free_object( index_t index, Type&& object ) + index_t ObjectSet< Type >::add_fixed_object( Type&& object ) { - OPENGEODE_EXCEPTION( index >= first_free_object_, - "[ObjectSet] - cannot update fixed object." ); - OPENGEODE_EXCEPTION( index < objects_.size(), - "[ObjectSet] - object index out of range." ); - objects_[index] = std::move( object ); + fixed_objects_.push_back( std::move( object ) ); + return fixed_objects_.size() - 1; } template < typename Type > - void ObjectSet< Type >::remove_free_object( index_t index ) + index_t ObjectSet< Type >::add_free_object( Type&& object ) { - OPENGEODE_EXCEPTION( index >= first_free_object_, - "[ObjectSet] - cannot remove fixed object." ); - do_remove_object( index ); + free_objects_.push_back( std::move( object ) ); + return free_objects_.size() - 1; } template < typename Type > - void ObjectSet< Type >::remove_fixed_object( index_t index ) + void ObjectSet< Type >::update_free_object( index_t index, Type&& object ) { - OPENGEODE_EXCEPTION( index < first_free_object_, - "[ObjectSet] - cannot remove free object." ); - do_remove_object( index ); - first_free_object_--; + OPENGEODE_EXCEPTION( index < free_objects_.size(), + "[ObjectSet] - free object index out of range." ); + free_objects_[index] = std::move( object ); } template < typename Type > - void ObjectSet< Type >::do_remove_object( index_t index ) + void ObjectSet< Type >::remove_free_object( index_t index ) { - const index_t last = objects_.size() - 1; + const index_t last = free_objects_.size() - 1; OPENGEODE_EXCEPTION( - index <= last, "[ObjectSet] - object index out of range." ); + index <= last, "[ObjectSet] - free object index out of range." ); if( index != last ) { - std::swap( objects_[index], objects_[last] ); + std::swap( free_objects_[index], free_objects_[last] ); } - objects_.pop_back(); + free_objects_.pop_back(); } template < typename Type > index_t ObjectSet< Type >::nb_objects() const { - return objects_.size(); + return free_objects_.size() + fixed_objects_.size(); } template < typename Type > index_t ObjectSet< Type >::nb_fixed_objects() const { - return first_free_object_; + return fixed_objects_.size(); } template < typename Type > index_t ObjectSet< Type >::nb_free_objects() const { - return objects_.size() - first_free_object_; + return free_objects_.size(); } template < typename Type > bool ObjectSet< Type >::empty() const { - return objects_.empty(); - } - - template < typename Type > - bool ObjectSet< Type >::is_fixed( index_t index ) const - { - return index < first_free_object_; + return free_objects_.empty() && fixed_objects_.empty(); } template < typename Type > diff --git a/src/geode/stochastic/spatial/object_sets.cpp b/src/geode/stochastic/spatial/object_sets.cpp index 2db28b5..67c213d 100644 --- a/src/geode/stochastic/spatial/object_sets.cpp +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -21,9 +21,11 @@ namespace geode const Type& ObjectSets< Type >::get_object( const ObjectId& object ) const { auto& set = get_set( object.set_id ); - OPENGEODE_EXCEPTION( object.index < set.nb_objects(), - "[ObjectSet]- object index out of range." ); - return set.get_object( object.index ); + if( object.fixed ) + { + return set.get_fixed_object( object.index ); + } + return set.get_free_object( object.index ); } template < typename Type > @@ -33,10 +35,32 @@ namespace geode result.reserve( nb_objects() ); for( const auto& [set_id, objs] : sets_ ) { - for( const auto obj_id : geode::Range{ objs.nb_objects() } ) + for( const auto obj_id : geode::Range{ objs.nb_fixed_objects() } ) { - result.push_back( { obj_id, set_id } ); + result.push_back( { obj_id, true, set_id } ); } + for( const auto obj_id : geode::Range{ objs.nb_free_objects() } ) + { + result.push_back( { obj_id, false, set_id } ); + } + } + return result; + } + + template < typename Type > + std::vector< ObjectId > ObjectSets< Type >::get_objects_in_set( + const uuid& set_id ) const + { + const auto& set = get_set( set_id ); + std::vector< ObjectId > result; + result.reserve( set.nb_objects() ); + for( const auto obj_id : geode::Range{ set.nb_fixed_objects() } ) + { + result.push_back( { obj_id, true, set_id } ); + } + for( const auto obj_id : geode::Range{ set.nb_free_objects() } ) + { + result.push_back( { obj_id, false, set_id } ); } return result; } @@ -78,50 +102,35 @@ namespace geode } template < typename Type > - ObjectId ObjectSets< Type >::add_fixed_object( - Type&& object, const uuid& set_id ) + ObjectId ObjectSets< Type >::add_object( + Type&& object, const uuid& set_id, bool fixed ) { auto& set = get_set( set_id ); - auto new_fixed_id = set.add_fixed_object( std::move( object ) ); - - ObjectId fixed_object_id{ new_fixed_id, set_id }; - auto fixed_object = get_object( fixed_object_id ); - auto fixed_object_box = object_bounding_box( fixed_object ); - - ObjectId last_object_id{ - static_cast< geode::index_t >( set.nb_objects() - 1 ), set_id - }; - auto free_object = get_object( last_object_id ); - auto free_object_box = object_bounding_box( free_object ); - - neighborhood_.update( - free_object_box, fixed_object_box, fixed_object_id ); - neighborhood_.add( free_object_box, last_object_id ); + auto object_box = object_bounding_box( object ); - return fixed_object_id; - } - - template < typename Type > - ObjectId ObjectSets< Type >::add_free_object( - Type&& object, const uuid& set_id ) - { - auto& set = get_set( set_id ); - ObjectId new_object_id{ static_cast< index_t >( set.nb_objects() ), - set_id }; - neighborhood_.add( object_bounding_box( object ), new_object_id ); - set.add_free_object( std::move( object ) ); - return new_object_id; + if( fixed ) + { + auto new_fixed_id = set.add_fixed_object( std::move( object ) ); + ObjectId fixed_object_id{ new_fixed_id, true, set_id }; + neighborhood_.add( object_box, fixed_object_id ); + return fixed_object_id; + } + auto new_free_id = set.add_free_object( std::move( object ) ); + ObjectId free_object_id{ new_free_id, false, set_id }; + neighborhood_.add( object_box, free_object_id ); + return free_object_id; } template < typename Type > void ObjectSets< Type >::update_free_object( const ObjectId& old_object_id, Type&& new_object ) { + OPENGEODE_EXCEPTION( + !old_object_id.fixed, "[ObjectSet]- cannot modify fixed object." ); auto& set = get_set( old_object_id.set_id ); OPENGEODE_EXCEPTION( old_object_id.index < set.nb_objects(), "[ObjectSet]- index of object to update out of range." ); - auto old_box = - object_bounding_box( set.get_object( old_object_id.index ) ); + auto old_box = object_bounding_box( get_object( old_object_id ) ); auto new_box = object_bounding_box( new_object ); neighborhood_.update( old_box, new_box, old_object_id ); set.update_free_object( old_object_id.index, std::move( new_object ) ); @@ -131,38 +140,22 @@ namespace geode void ObjectSets< Type >::remove_free_object( const ObjectId& object_id ) { auto& set = get_set( object_id.set_id ); - OPENGEODE_EXCEPTION( !set.is_fixed( object_id.index ), - "[ObjectSet]- Object to remove is fixed." ); - update_neighborhood_remove_context( object_id ); - set.remove_free_object( object_id.index ); - } - template < typename Type > - void ObjectSets< Type >::remove_fixed_object( const ObjectId& object_id ) - { - auto& set = get_set( object_id.set_id ); - OPENGEODE_EXCEPTION( set.is_fixed( object_id.index ), - "[ObjectSet]- Object to remove is free." ); - update_neighborhood_remove_context( object_id ); - set.remove_fixed_object( object_id.index ); - } - template < typename Type > - void ObjectSets< Type >::update_neighborhood_remove_context( - const ObjectId& object_id ) - { - auto& set = get_set( object_id.set_id ); - OPENGEODE_EXCEPTION( object_id.index < set.nb_objects(), - "[ObjectSet]- index of object to remove out of range." ); - const auto& obj_to_remove = set.get_object( object_id.index ); + OPENGEODE_EXCEPTION( + !object_id.fixed, "[ObjectSet]- Cannot remove fixed object." ); + const auto& obj_to_remove = get_object( object_id ); neighborhood_.remove( object_bounding_box( obj_to_remove ), object_id ); - ObjectId last_id{ static_cast< geode::index_t >( set.nb_objects() - 1 ), - object_id.set_id }; - if( object_id != last_id ) + ObjectId last_free_id{ static_cast< geode::index_t >( + set.nb_free_objects() - 1 ), + false, object_id.set_id }; + if( object_id != last_free_id ) { - const auto& last_obj = set.get_object( last_id.index ); - auto box_to_move = object_bounding_box( last_obj ); - neighborhood_.update( box_to_move, last_id, object_id ); + const auto& last_free_obj = + set.get_free_object( last_free_id.index ); + auto box_to_move = object_bounding_box( last_free_obj ); + neighborhood_.update( box_to_move, last_free_id, object_id ); } + set.remove_free_object( object_id.index ); } template < typename Type > diff --git a/tests/stochastic/sampling/mcmc/helpers/test-simulation_printer.cpp b/tests/stochastic/sampling/mcmc/helpers/test-simulation_printer.cpp index 779789c..a1e2ec5 100644 --- a/tests/stochastic/sampling/mcmc/helpers/test-simulation_printer.cpp +++ b/tests/stochastic/sampling/mcmc/helpers/test-simulation_printer.cpp @@ -106,9 +106,9 @@ namespace geode::ObjectSets< geode::Point2D > object_sets; const auto set_id = object_sets.add_set( "default_name" ); - object_sets.add_free_object( geode::Point2D{ { 0.0, 0.0 } }, set_id ); - object_sets.add_free_object( geode::Point2D{ { 1.0, 0.0 } }, set_id ); - object_sets.add_free_object( geode::Point2D{ { 3.0, 0.0 } }, set_id ); + object_sets.add_object( geode::Point2D{ { 0.0, 0.0 } }, set_id, false ); + object_sets.add_object( geode::Point2D{ { 1.0, 0.0 } }, set_id, false ); + object_sets.add_object( geode::Point2D{ { 3.0, 0.0 } }, set_id, false ); geode::SimulationPrinter printer( config ); printer.print_object_sets( object_sets, 0 ); diff --git a/tests/stochastic/sampling/mcmc/models/components/test-density-term.cpp b/tests/stochastic/sampling/mcmc/models/components/test-density-term.cpp index c9c9df4..162a5d1 100644 --- a/tests/stochastic/sampling/mcmc/models/components/test-density-term.cpp +++ b/tests/stochastic/sampling/mcmc/models/components/test-density-term.cpp @@ -33,9 +33,9 @@ geode::uuid init_object_set( geode::ObjectSets< geode::Point2D >& pattern ) geode::Point2D p_buffer{ { 1.3, 0.1 } }; auto set_id = pattern.add_set( "default_name" ); - pattern.add_free_object( std::move( p1 ), set_id ); - pattern.add_free_object( std::move( p2 ), set_id ); - pattern.add_free_object( std::move( p_buffer ), set_id ); // buffer last + pattern.add_object( std::move( p1 ), set_id, false ); + pattern.add_object( std::move( p2 ), set_id, false ); + pattern.add_object( std::move( p_buffer ), set_id, false ); // buffer last return set_id; } @@ -85,7 +85,7 @@ void run_density_test( double lambda, delta == 0., "[DensityTerm] delta_log_add outside VOI wrong" ); // --- Delta remove anchored object - geode::ObjectId obj_id{ 0, set_id }; + geode::ObjectId obj_id{ 0, false, set_id }; delta = term.delta_log_remove( pattern, obj_id ); OPENGEODE_EXCEPTION( delta == expected_remove, "[DensityTerm] delta_log_remove wrong" ); @@ -104,7 +104,7 @@ void run_density_test( double lambda, delta == 0., "[DensityTerm] delta_log_change anchored→anchored wrong" ); // --- Delta change buffer → anchored - geode::ObjectId buffer_id{ 2, set_id }; + geode::ObjectId buffer_id{ 2, false, set_id }; delta = term.delta_log_change( pattern, buffer_id, ref_inside ); OPENGEODE_EXCEPTION( delta == expected_add, "[DensityTerm] delta_log_change buffer→anchored wrong" ); diff --git a/tests/stochastic/sampling/mcmc/models/components/test-pairwise-term.cpp b/tests/stochastic/sampling/mcmc/models/components/test-pairwise-term.cpp index 6dee5b0..0bc4a22 100644 --- a/tests/stochastic/sampling/mcmc/models/components/test-pairwise-term.cpp +++ b/tests/stochastic/sampling/mcmc/models/components/test-pairwise-term.cpp @@ -36,9 +36,9 @@ geode::uuid init_object_set( geode::ObjectSets< geode::Point2D >& pattern ) geode::Point2D p3{ { 1.3, 1.3 } }; // buffer auto set_id = pattern.add_set( "default_name" ); - pattern.add_free_object( std::move( p1 ), set_id ); - pattern.add_free_object( std::move( p2 ), set_id ); - pattern.add_free_object( std::move( p3 ), set_id ); + pattern.add_object( std::move( p1 ), set_id, false ); + pattern.add_object( std::move( p2 ), set_id, false ); + pattern.add_object( std::move( p3 ), set_id, false ); return set_id; } @@ -88,14 +88,14 @@ void test_pairwise_term( double gamma, "[PairwiseTerm] delta_log_add buffer wrong" ); // --- delta_change VOI → VOI - geode::ObjectId obj_id{ 0, set_id }; // p1 + geode::ObjectId obj_id{ 0, false, set_id }; // p1 delta = term.delta_log_change( pattern, obj_id, p4_ref ); // p1 replaced by p4: interacts with p2 → add 1 interaction OPENGEODE_EXCEPTION( delta == term.contribution( 1 ), "[PairwiseTerm] delta_log_change VOI->VOI wrong" ); // --- delta_change buffer → VOI - geode::ObjectId old_buffer{ 2, set_id }; // p3 + geode::ObjectId old_buffer{ 2, false, set_id }; // p3 delta = term.delta_log_change( pattern, old_buffer, p4_ref ); // old buffer interactions removed (-p3 pairs (-1)), new VOI interactions // added @@ -156,14 +156,14 @@ void test_pairwise_term_zero_gamma( double gamma, "gammaVOI with " "gammaVOI with " diff --git a/tests/stochastic/sampling/mcmc/models/test-gibbs-energy.cpp b/tests/stochastic/sampling/mcmc/models/test-gibbs-energy.cpp index 25915b9..d40ceba 100644 --- a/tests/stochastic/sampling/mcmc/models/test-gibbs-energy.cpp +++ b/tests/stochastic/sampling/mcmc/models/test-gibbs-energy.cpp @@ -42,8 +42,8 @@ void test_gibbs_energy() const auto set_id = pattern.add_set( "default_name" ); geode::Point2D p1{ { 0., 0. } }; geode::Point2D p2{ { 1., 1. } }; - pattern.add_free_object( std::move( p1 ), set_id ); - pattern.add_free_object( std::move( p2 ), set_id ); + pattern.add_object( std::move( p1 ), set_id, false ); + pattern.add_object( std::move( p2 ), set_id, false ); geode::EnergyTermCollection< geode::Point2D > energy_terms; @@ -79,7 +79,7 @@ void test_gibbs_energy() OPENGEODE_EXCEPTION( std::isfinite( delta_add ), "[test gibbs] Delta add should be finite." ); - geode::ObjectId obj_id{ 0, set_id }; + geode::ObjectId obj_id{ 0, false, set_id }; // Remove point test double delta_remove = gibbs_energy.delta_log_remove( pattern, obj_id ); OPENGEODE_EXCEPTION( std::isfinite( delta_remove ), diff --git a/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp b/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp index e1981d8..f3c359b 100644 --- a/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp +++ b/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp @@ -39,8 +39,8 @@ namespace geode::Point2D p2{ { 1., 1. } }; auto set_id = pattern.add_set( "default_name" ); - pattern.add_free_object( std::move( p1 ), set_id ); - pattern.add_free_object( std::move( p2 ), set_id ); + pattern.add_object( std::move( p1 ), set_id, false ); + pattern.add_object( std::move( p2 ), set_id, false ); return set_id; } diff --git a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp index a8d1c1c..d4f5063 100644 --- a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp +++ b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp @@ -183,9 +183,9 @@ int main() geode::MetropolisHastings< geode::Point2D > mh( energy_terms, std::move( kernel ) ); - state.add_fixed_object( geode::Point2D{ { 1., 1. } }, set_id ); - state.add_free_object( geode::Point2D{ { 2., 2. } }, set_id ); - state.add_fixed_object( geode::Point2D{ { 3., 3. } }, set_id ); + state.add_object( geode::Point2D{ { 1., 1. } }, set_id, true ); + state.add_object( geode::Point2D{ { 2., 2. } }, set_id, false ); + state.add_object( geode::Point2D{ { 3., 3. } }, set_id, true ); test_steps( mh, state ); // OPENGEODE_EXCEPTION( state.get_set( set_id ).nb_fixed_objects() == 2 diff --git a/tests/stochastic/spatial/test-object-set.cpp b/tests/stochastic/spatial/test-object-set.cpp index eb943cf..1705ad9 100644 --- a/tests/stochastic/spatial/test-object-set.cpp +++ b/tests/stochastic/spatial/test-object-set.cpp @@ -44,7 +44,7 @@ namespace OPENGEODE_EXCEPTION( !set.empty(), "[TestObjectSet] - Set should not be empty after insertions" ); - const auto& p = set.get_object( idx1 ); + const auto& p = set.get_fixed_object( idx1 ); const auto result = geode::Point2D{ { 1.0, 1.0 } }; OPENGEODE_EXCEPTION( p == result, "[TestObjectSet] - Wrong object value at index 1" ); @@ -59,7 +59,7 @@ namespace set.update_free_object( idx1, geode::Point2D{ { 5.0, 5.0 } } ); - const auto& updated = set.get_object( idx1 ); + const auto& updated = set.get_free_object( idx1 ); const auto new_point = geode::Point2D{ { 5.0, 5.0 } }; OPENGEODE_EXCEPTION( updated == new_point, "[TestObjectSet] - Object update failed" ); @@ -72,12 +72,12 @@ namespace set.add_free_object( geode::Point2D{ { 1.0, 1.0 } } ); set.add_free_object( geode::Point2D{ { 2.0, 2.0 } } ); - set.remove_free_object( 1 ); // remove the second object (free) + set.remove_free_object( 0 ); // remove the second object (free) OPENGEODE_EXCEPTION( set.nb_objects() == 2, "[TestObjectSet] - Set size should be 2 after removal" ); - const auto& last = set.get_object( 1 ); + const auto& last = set.get_free_object( 0 ); const auto result = geode::Point2D{ { 2.0, 2.0 } }; OPENGEODE_EXCEPTION( last == result, "[TestObjectSet] - Remaining objects not shifted " @@ -90,7 +90,7 @@ namespace set.add_fixed_object( geode::Point2D{ { 10.0, 10.0 } } ); const auto& const_set = set; - const auto& p = const_set.get_object( 0 ); + const auto& p = const_set.get_fixed_object( 0 ); const auto result = geode::Point2D{ { 10.0, 10.0 } }; OPENGEODE_EXCEPTION( p == result, "[TestObjectSet] - Const access mismatch" ); @@ -128,24 +128,22 @@ namespace OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 2, "still two fixed object" ); OPENGEODE_EXCEPTION( set.nb_free_objects() == 3, "three free objects" ); - OPENGEODE_EXCEPTION( u1 == 3 && u2 == 4 ); + OPENGEODE_EXCEPTION( u1 == 1 && u2 == 2 ); // Remove first fixed object - set.remove_fixed_object( 0 ); + // set.remove_fixed_object( 0 ); // Invariant - OPENGEODE_EXCEPTION( - set.nb_fixed_objects() == 1, "one remaining fixed objects" ); OPENGEODE_EXCEPTION( set.nb_free_objects() == 3, "still 3 free objects" ); - OPENGEODE_EXCEPTION( set.nb_objects() == 4, "4 total objects" ); + OPENGEODE_EXCEPTION( set.nb_objects() == 5, "5 total objects" ); // Remove a free object set.remove_free_object( 2 ); - OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 1, "still one fixed" ); + OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 2, "still one fixed" ); OPENGEODE_EXCEPTION( set.nb_free_objects() == 2, "two remining free" ); - OPENGEODE_EXCEPTION( set.nb_objects() == 3, "three object at last" ); + OPENGEODE_EXCEPTION( set.nb_objects() == 4, "4 object at last" ); } } // namespace diff --git a/tests/stochastic/spatial/test-object-sets.cpp b/tests/stochastic/spatial/test-object-sets.cpp index a610c8d..2f8cc1c 100644 --- a/tests/stochastic/spatial/test-object-sets.cpp +++ b/tests/stochastic/spatial/test-object-sets.cpp @@ -36,11 +36,11 @@ namespace sets.nb_sets() == 2, "[TestObjectSets] - Expected 2 sets" ); const auto obj_a = - sets.add_free_object( geode::Point2D{ { 0.0, 0.0 } }, set_id1 ); + sets.add_object( geode::Point2D{ { 0.0, 0.0 } }, set_id1, false ); const auto obj_b = - sets.add_free_object( geode::Point2D{ { 1.0, 1.0 } }, set_id1 ); + sets.add_object( geode::Point2D{ { 1.0, 1.0 } }, set_id1, false ); const auto obj_c = - sets.add_free_object( geode::Point2D{ { 5.0, 5.0 } }, set_id2 ); + sets.add_object( geode::Point2D{ { 5.0, 5.0 } }, set_id2, false ); OPENGEODE_EXCEPTION( sets.nb_objects_in_set( set_id1 ) == 2, "[TestObjectSets] - Set 1 should have 2 objects" ); @@ -60,7 +60,7 @@ namespace const auto set_id = sets.add_set( "default_name" ); const auto obj = - sets.add_free_object( geode::Point2D{ { 1.0, 1.0 } }, set_id ); + sets.add_object( geode::Point2D{ { 1.0, 1.0 } }, set_id, false ); sets.update_free_object( obj, geode::Point2D{ { 9.0, 9.0 } } ); @@ -74,17 +74,17 @@ namespace ObjectSets< geode::Point2D > sets; const auto set_id = sets.add_set( "default_name" ); - sets.add_free_object( geode::Point2D{ { 0.0, 0.0 } }, set_id ); - sets.add_free_object( geode::Point2D{ { 1.0, 1.0 } }, set_id ); - sets.add_free_object( geode::Point2D{ { 2.0, 2.0 } }, set_id ); + sets.add_object( geode::Point2D{ { 0.0, 0.0 } }, set_id, false ); + sets.add_object( geode::Point2D{ { 1.0, 1.0 } }, set_id, false ); + sets.add_object( geode::Point2D{ { 2.0, 2.0 } }, set_id, false ); - sets.remove_free_object( { 1, set_id } ); + sets.remove_free_object( { 1, false, set_id } ); OPENGEODE_EXCEPTION( sets.nb_objects_in_set( set_id ) == 2, "[TestObjectSets] - Expected 2 objects after free removal" ); // Now remove the last remaining free object using remove_object() - sets.remove_free_object( { 1, set_id } ); + sets.remove_free_object( { 1, false, set_id } ); OPENGEODE_EXCEPTION( sets.nb_objects_in_set( set_id ) == 1, "[TestObjectSets] - Expected 1 object after second removal" ); @@ -95,8 +95,8 @@ namespace const auto idA = sets.add_set( "A" ); const auto idB = sets.add_set( "B" ); - sets.add_free_object( geode::Point2D{ { 0.0, 0.0 } }, idA ); - sets.add_free_object( geode::Point2D{ { 1.0, 1.0 } }, idB ); + sets.add_object( geode::Point2D{ { 0.0, 0.0 } }, idA, false ); + sets.add_object( geode::Point2D{ { 1.0, 1.0 } }, idB, false ); const auto all = sets.get_all_object(); @@ -110,11 +110,11 @@ namespace const auto set_id = sets.add_set( "default_name" ); const auto obj0 = - sets.add_free_object( geode::Point2D{ { 0.0, 0.0 } }, set_id ); + sets.add_object( geode::Point2D{ { 0.0, 0.0 } }, set_id, false ); const auto obj1 = - sets.add_free_object( geode::Point2D{ { 1.0, 0.0 } }, set_id ); + sets.add_object( geode::Point2D{ { 1.0, 0.0 } }, set_id, false ); const auto obj2 = - sets.add_free_object( geode::Point2D{ { 5.0, 0.0 } }, set_id ); + sets.add_object( geode::Point2D{ { 5.0, 0.0 } }, set_id, false ); std::vector< uuid > targeted_set_ids{ set_id }; const auto near_neighbors = @@ -132,9 +132,9 @@ namespace ObjectSets< geode::Point2D > sets; const auto set_id = sets.add_set( "default_name" ); - sets.add_free_object( geode::Point2D{ { 0.0, 0.0 } }, set_id ); - sets.add_free_object( geode::Point2D{ { 1.0, 0.0 } }, set_id ); - sets.add_fixed_object( geode::Point2D{ { 3.0, 0.0 } }, set_id ); + sets.add_object( geode::Point2D{ { 0.0, 0.0 } }, set_id, false ); + sets.add_object( geode::Point2D{ { 1.0, 0.0 } }, set_id, false ); + sets.add_object( geode::Point2D{ { 3.0, 0.0 } }, set_id, true ); const geode::Point2D query{ { 0.5, 0.0 } }; std::vector< uuid > targeted_set_ids{ set_id }; @@ -148,8 +148,8 @@ namespace { ObjectSets< geode::Point2D > sets; const auto set_id = sets.add_set( "default_name" ); - sets.add_free_object( geode::Point2D{ { 0.0, 0.0 } }, set_id ); - sets.add_free_object( geode::Point2D{ { 1.0, 1.0 } }, set_id ); + sets.add_object( geode::Point2D{ { 0.0, 0.0 } }, set_id, false ); + sets.add_object( geode::Point2D{ { 1.0, 1.0 } }, set_id, false ); const auto desc = sets.string(); OPENGEODE_EXCEPTION( desc.find( "objects" ) != std::string::npos,