From ad90572d48b79fee740858322371231f651b86e9 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Thu, 16 Oct 2025 20:07:24 +0200 Subject: [PATCH 01/11] feat(ObjectSet): refactor and rename --- .../object_set_sampler/object_set_sampler.hpp | 2 +- .../mcmc/metropolis_hasting_sampler.hpp | 16 +- .../mcmc/models/components/density_term.hpp | 24 +- .../mcmc/models/components/energy_term.hpp | 50 ++-- .../components/energy_term_collection.hpp | 26 +- .../mcmc/models/components/pairwise_term.hpp | 44 ++-- .../models/components/single_object_term.hpp | 28 +-- .../sampling/mcmc/models/gibbs_energy.hpp | 30 +-- .../mcmc/proposal/classical_proposals.hpp | 42 +++- .../sampling/mcmc/proposal/moves.hpp | 39 ++- .../mcmc/proposal/proposal_kernel.hpp | 44 ++-- .../spatial/object_neighborhood.hpp | 6 +- .../geode/stochastic/spatial/object_set.hpp | 68 ++--- .../geode/stochastic/spatial/object_sets.hpp | 92 +++++++ .../spatial/pairwise_interactions.hpp | 18 +- src/geode/stochastic/CMakeLists.txt | 2 + src/geode/stochastic/spatial/object_set.cpp | 235 ++++-------------- src/geode/stochastic/spatial/object_sets.cpp | 189 ++++++++++++++ tests/stochastic/CMakeLists.txt | 30 ++- .../models/components/test-intensity-term.cpp | 52 ++-- .../models/components/test-pairwise-term.cpp | 50 ++-- .../mcmc/models/test-gibbs-energy.cpp | 30 +-- .../mcmc/proposal/test-proposal-kernel.cpp | 46 ++-- .../mcmc/test-metropolis-hasting-sampler.cpp | 29 ++- .../sampling/mcmc/test-mh-poisson.cpp | 72 +++--- ...-mh-strauss-statistics-and-convergence.cpp | 13 +- .../sampling/mcmc/test-mh-strauss.cpp | 91 ++++--- tests/stochastic/spatial/test-object-set.cpp | 123 +++++++++ tests/stochastic/spatial/test-object-sets.cpp | 133 ++++++++++ 29 files changed, 1006 insertions(+), 618 deletions(-) create mode 100644 include/geode/stochastic/spatial/object_sets.hpp create mode 100644 src/geode/stochastic/spatial/object_sets.cpp create mode 100644 tests/stochastic/spatial/test-object-set.cpp create mode 100644 tests/stochastic/spatial/test-object-sets.cpp diff --git a/include/geode/stochastic/sampling/direct/object_set_sampler/object_set_sampler.hpp b/include/geode/stochastic/sampling/direct/object_set_sampler/object_set_sampler.hpp index b3bd298..8ab882a 100644 --- a/include/geode/stochastic/sampling/direct/object_set_sampler/object_set_sampler.hpp +++ b/include/geode/stochastic/sampling/direct/object_set_sampler/object_set_sampler.hpp @@ -24,7 +24,7 @@ #pragma once #include -#include +#include namespace geode { diff --git a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp index ea03c40..b31789c 100644 --- a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp +++ b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp @@ -59,7 +59,7 @@ namespace geode } StepResult< ObjectType > step( - ObjectSet< ObjectType >& state, RandomEngine& engine ) const + ObjectSets< ObjectType >& state, RandomEngine& engine ) const { Proposal< ObjectType > proposal = proposal_kernel_->propose( state, engine ); @@ -79,7 +79,7 @@ namespace geode return StepResult< ObjectType >{}; } - void walk( ObjectSet< ObjectType >& state, + void walk( ObjectSets< ObjectType >& state, RandomEngine& engine, index_t nb_steps ) const { @@ -90,7 +90,7 @@ namespace geode } } - ObjectSet< ObjectType > walk_copy( ObjectSet< ObjectType > initial, + ObjectSets< ObjectType > walk_copy( ObjectSets< ObjectType > initial, RandomEngine& engine, index_t nb_steps ) const { @@ -160,7 +160,7 @@ namespace geode template < typename ApplyMove > StepResult< ObjectType > accept_or_reject( Proposal< ObjectType >& proposal, - ObjectSet< ObjectType >& state, + ObjectSets< ObjectType >& state, RandomEngine& engine, const double delta_log_energy, ApplyMove&& apply_move ) const @@ -183,7 +183,7 @@ namespace geode } StepResult< ObjectType > birth_step( Proposal< ObjectType >& proposal, - ObjectSet< ObjectType >& state, + ObjectSets< ObjectType >& state, RandomEngine& engine ) const { const auto new_object = proposal.new_object(); @@ -193,12 +193,12 @@ namespace geode []( auto& state, auto& proposal ) { state.add_object( std::move( proposal.proposed_move.new_object.value() ), - proposal.subset_id ); + proposal.set_id ); } ); }; StepResult< ObjectType > death_step( Proposal< ObjectType >& proposal, - ObjectSet< ObjectType >& state, + ObjectSets< ObjectType >& state, RandomEngine& engine ) const { const auto old_object_id = proposal.old_object_id(); @@ -211,7 +211,7 @@ namespace geode }; StepResult< ObjectType > change_step( Proposal< ObjectType >& proposal, - ObjectSet< ObjectType >& state, + ObjectSets< ObjectType >& state, RandomEngine& engine ) const { const auto new_object = proposal.new_object(); diff --git a/include/geode/stochastic/sampling/mcmc/models/components/density_term.hpp b/include/geode/stochastic/sampling/mcmc/models/components/density_term.hpp index b3d56be..8b3a1e5 100644 --- a/include/geode/stochastic/sampling/mcmc/models/components/density_term.hpp +++ b/include/geode/stochastic/sampling/mcmc/models/components/density_term.hpp @@ -22,7 +22,7 @@ */ #pragma once -#include +#include #include @@ -35,51 +35,51 @@ namespace geode public: explicit DensityTerm( std::string_view name, double lambda, - absl::flat_hash_set< uuid > targeted_subset_ids ) + absl::flat_hash_set< uuid > targeted_set_ids ) : EnergyTerm< ObjectType >( - name, lambda, std::move( targeted_subset_ids ) ) + name, lambda, std::move( targeted_set_ids ) ) { } - double total_log( const ObjectSet< ObjectType >& state ) const override + double total_log( const ObjectSets< ObjectType >& state ) const override { const auto n = this->statistic( state ); return this->contribution( n ); } - double delta_log_add( const ObjectSet< ObjectType >& /*state*/, + double delta_log_add( const ObjectSets< ObjectType >& /*state*/, const ObjectRef< ObjectType >& new_object ) const override { - if( !this->is_targeted_subset( new_object.subset ) ) + if( !this->is_targeted_set( new_object.set_id ) ) { return 0.0; } return this->contribution( 1.0 ); } - double delta_log_remove( const ObjectSet< ObjectType >& /*state*/, + double delta_log_remove( const ObjectSets< ObjectType >& /*state*/, const ObjectId& object_id ) const override { - if( !this->is_targeted_subset( object_id.subset ) ) + if( !this->is_targeted_set( object_id.set_id ) ) { return 0.0; } return this->contribution( -1.0 ); } - double delta_log_change( const ObjectSet< ObjectType >& /*state*/, + double delta_log_change( const ObjectSets< ObjectType >& /*state*/, const ObjectId& /*old_object_id*/, const ObjectRef< ObjectType >& /*new_object*/ ) const override { return 0.0; } - double statistic( const ObjectSet< ObjectType >& state ) const override + double statistic( const ObjectSets< ObjectType >& state ) const override { index_t count{ 0 }; - for( const auto& subset_uuid : this->targeted_subset_ids() ) + for( const auto& set_id : this->targeted_set_ids() ) { - count += state.nb_objects_in_subset( subset_uuid ); + count += state.nb_objects_in_set( set_id ); } return static_cast< double >( count ); } 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 48e0f75..c327a5e 100644 --- a/include/geode/stochastic/sampling/mcmc/models/components/energy_term.hpp +++ b/include/geode/stochastic/sampling/mcmc/models/components/energy_term.hpp @@ -28,7 +28,7 @@ #include #include -#include +#include #include @@ -88,14 +88,14 @@ namespace geode // std::string name; // std::string type; // double parameter_value; - // std::optional< uuid > targeted_subset_id{}; + // std::optional< uuid > targeted_set_id{}; // } // // struct StatisticalDescription // { // std::string label; // double value; - // std::optional< uuid > targeted_subset_id{}; + // std::optional< uuid > targeted_set_id{}; // }; template < typename ObjectType > @@ -104,10 +104,9 @@ namespace geode public: explicit EnergyTerm( std::string_view name, double param, - absl::flat_hash_set< uuid >&& targeted_subset_ids ) - : Identifier{}, - energy_scale_{ param }, - targeted_subset_ids_{ std::move( targeted_subset_ids ) } + absl::flat_hash_set< uuid >&& targeted_set_ids ) + : energy_scale_{ param }, + targeted_set_ids_{ std::move( targeted_set_ids ) } { IdentifierBuilder builder( *this ); builder.set_name( name ); @@ -120,9 +119,9 @@ namespace geode return energy_scale_.parameter(); } - const absl::flat_hash_set< uuid >& targeted_subset_ids() const + const absl::flat_hash_set< uuid >& targeted_set_ids() const { - return targeted_subset_ids_; + return targeted_set_ids_; } /// Energy contribution for a given statistic multiplier @@ -132,58 +131,57 @@ namespace geode } virtual double total_log( - const ObjectSet< ObjectType >& state ) const = 0; + const ObjectSets< ObjectType >& state ) const = 0; - virtual double delta_log_add( const ObjectSet< ObjectType >& state, + virtual double delta_log_add( const ObjectSets< ObjectType >& state, const ObjectRef< ObjectType >& new_object ) const = 0; - virtual double delta_log_remove( const ObjectSet< ObjectType >& state, + virtual double delta_log_remove( const ObjectSets< ObjectType >& state, const ObjectId& object_id ) const = 0; - virtual double delta_log_change( const ObjectSet< ObjectType >& state, + virtual double delta_log_change( const ObjectSets< ObjectType >& state, const ObjectId& old_object_id, const ObjectRef< ObjectType >& new_object ) const = 0; virtual double statistic( - const ObjectSet< ObjectType >& state ) const = 0; + const ObjectSets< ObjectType >& state ) const = 0; std::string string() const { auto message = absl::StrCat( "Term : ", name(), "; uuid: ", id().string(), " parameter value: ", energy_scale_.parameter(), - " applyied on ", targeted_subset_ids_.size(), + " applyied on ", targeted_set_ids_.size(), " object subsets -->" ); - for( const auto& subset_uuid : targeted_subset_ids_ ) + for( const auto& set_id : targeted_set_ids_ ) { - absl::StrAppend( &message, "\t", subset_uuid.string() ); + absl::StrAppend( &message, "\t", set_id.string() ); } return message; } protected: - bool is_targeted_subset( const uuid& subset_id ) const + bool is_targeted_set( const uuid& set_id ) const { - return targeted_subset_ids_.find( subset_id ) - != targeted_subset_ids_.end(); + return targeted_set_ids_.find( set_id ) != targeted_set_ids_.end(); } template < typename Func > void for_each_targeted_object( - const ObjectSet< ObjectType >& state, Func&& do_apply ) const + const ObjectSets< ObjectType >& state, Func&& do_apply ) const { - for( const auto& targeted_subset_id : targeted_subset_ids_ ) + for( const auto& targeted_set_id : targeted_set_ids_ ) { - for( const auto id : geode::Range{ - state.nb_objects_in_subset( targeted_subset_id ) } ) + for( const auto id : + geode::Range{ state.nb_objects_in_set( targeted_set_id ) } ) { - do_apply( ObjectId{ id, targeted_subset_id } ); + do_apply( ObjectId{ id, targeted_set_id } ); } } } private: detail::EnergyScale energy_scale_; - absl::flat_hash_set< uuid > targeted_subset_ids_; + absl::flat_hash_set< uuid > targeted_set_ids_; }; } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/sampling/mcmc/models/components/energy_term_collection.hpp b/include/geode/stochastic/sampling/mcmc/models/components/energy_term_collection.hpp index 1139aa5..0a85fe6 100644 --- a/include/geode/stochastic/sampling/mcmc/models/components/energy_term_collection.hpp +++ b/include/geode/stochastic/sampling/mcmc/models/components/energy_term_collection.hpp @@ -19,9 +19,9 @@ namespace geode { const uuid id = term->id(); energy_terms_.emplace( id, term ); - for( const uuid& subset_id : term->targeted_subset_ids() ) + for( const uuid& set_id : term->targeted_set_ids() ) { - subset_to_terms_[subset_id].push_back( term ); + set_to_terms_[set_id].push_back( term ); } return id; } @@ -34,10 +34,10 @@ namespace geode auto term = it->second; - for( const uuid& subset_id : term->targeted_subset_ids() ) + for( const uuid& set_id : term->targeted_set_ids() ) { - auto vec_it = subset_to_terms_.find( subset_id ); - if( vec_it == subset_to_terms_.end() ) + auto vec_it = set_to_terms_.find( set_id ); + if( vec_it == set_to_terms_.end() ) continue; auto& vec = vec_it->second; @@ -45,7 +45,7 @@ namespace geode std::remove( vec.begin(), vec.end(), term ), vec.end() ); if( vec.empty() ) - subset_to_terms_.erase( vec_it ); + set_to_terms_.erase( vec_it ); } energy_terms_.erase( it ); @@ -55,7 +55,7 @@ namespace geode void clear() { energy_terms_.clear(); - subset_to_terms_.clear(); + set_to_terms_.clear(); } [[nodiscard]] index_t size() const @@ -82,11 +82,11 @@ namespace geode [[nodiscard]] const std::vector< std::shared_ptr< EnergyTerm< ObjectType > > >& - terms_for_subset( const uuid& subset_id ) const + terms_for_set( const uuid& set_id ) const { - const auto it = subset_to_terms_.find( subset_id ); - OPENGEODE_EXCEPTION( it != subset_to_terms_.end(), - "[EnergyTermCollection] - Object Subset (", subset_id.string(), + const auto it = set_to_terms_.find( set_id ); + OPENGEODE_EXCEPTION( it != set_to_terms_.end(), + "[EnergyTermCollection] - Object Subset (", set_id.string(), ") does not have any energy term." ); return it->second; } @@ -103,14 +103,12 @@ namespace geode } private: - // strong ownership absl::flat_hash_map< uuid, std::shared_ptr< EnergyTerm< ObjectType > > > energy_terms_; - // subset index (shared ownership) absl::flat_hash_map< uuid, std::vector< std::shared_ptr< EnergyTerm< ObjectType > > > > - subset_to_terms_; + set_to_terms_; }; } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/sampling/mcmc/models/components/pairwise_term.hpp b/include/geode/stochastic/sampling/mcmc/models/components/pairwise_term.hpp index 02385ac..232893c 100644 --- a/include/geode/stochastic/sampling/mcmc/models/components/pairwise_term.hpp +++ b/include/geode/stochastic/sampling/mcmc/models/components/pairwise_term.hpp @@ -23,7 +23,7 @@ #pragma once #include -#include +#include #include #include @@ -37,24 +37,24 @@ namespace geode public: explicit PairwiseTerm( std::string_view name, double gamma, - absl::flat_hash_set< uuid > targeted_subset_ids, + absl::flat_hash_set< uuid > targeted_set_ids, std::unique_ptr< PairwiseInteraction< ObjectType > > interaction ) : EnergyTerm< ObjectType >( - name, gamma, std::move( targeted_subset_ids ) ), + name, gamma, std::move( targeted_set_ids ) ), interaction_( std::move( interaction ) ) { } - double total_log( const ObjectSet< ObjectType >& state ) const final + double total_log( const ObjectSets< ObjectType >& state ) const final { const auto interaction_weight = statistic( state ); return this->contribution( interaction_weight ); } - double delta_log_add( const ObjectSet< ObjectType >& state, + double delta_log_add( const ObjectSets< ObjectType >& state, const ObjectRef< ObjectType >& new_object ) const final { - if( !this->is_targeted_subset( new_object.subset ) ) + if( !this->is_targeted_set( new_object.set_id ) ) { return 0.0; } @@ -64,7 +64,7 @@ namespace geode for( const auto& neigh_id : neighbors ) { geode::ObjectRef< ObjectType > neigh_object{ - state.get_object( neigh_id ), neigh_id.subset + state.get_object( neigh_id ), neigh_id.set_id }; delta += interaction_->evaluate( new_object, neigh_object ); @@ -72,23 +72,23 @@ namespace geode return this->contribution( delta ); } - double delta_log_remove( const ObjectSet< ObjectType >& state, + double delta_log_remove( const ObjectSets< ObjectType >& state, const ObjectId& object_id ) const override { - if( !this->is_targeted_subset( object_id.subset ) ) + if( !this->is_targeted_set( object_id.set_id ) ) { return 0.0; } double delta = 0.0; ObjectRef< ObjectType > object_to_remove{ - state.get_object( object_id ), object_id.subset + state.get_object( object_id ), object_id.set_id }; const auto neighbors = state.neighbors( object_id, interaction_->neighborhood_searching_distance() ); for( auto neigh_id : neighbors ) { ObjectRef< ObjectType > neigh_object{ - state.get_object( neigh_id ), neigh_id.subset + state.get_object( neigh_id ), neigh_id.set_id }; delta += interaction_->evaluate( object_to_remove, neigh_object ); @@ -96,12 +96,12 @@ namespace geode return this->contribution( -delta ); } - double delta_log_change( const ObjectSet< ObjectType >& state, + double delta_log_change( const ObjectSets< ObjectType >& state, const ObjectId& old_object_id, const ObjectRef< ObjectType >& new_object ) const override { - if( !this->is_targeted_subset( old_object_id.subset ) - || !this->is_targeted_subset( new_object.subset ) ) + if( !this->is_targeted_set( old_object_id.set_id ) + || !this->is_targeted_set( new_object.set_id ) ) { return 0.0; } @@ -109,14 +109,14 @@ namespace geode // Remove old object's interactions ObjectRef< ObjectType > object_to_remove{ - state.get_object( old_object_id ), old_object_id.subset + state.get_object( old_object_id ), old_object_id.set_id }; const auto old_neighbors = state.neighbors( old_object_id, interaction_->neighborhood_searching_distance() ); for( auto neigh_id : old_neighbors ) { ObjectRef< ObjectType > neigh_object{ - state.get_object( neigh_id ), neigh_id.subset + state.get_object( neigh_id ), neigh_id.set_id }; delta -= interaction_->evaluate( object_to_remove, neigh_object ); @@ -132,7 +132,7 @@ namespace geode continue; // avoid double-counting } ObjectRef< ObjectType > neigh_object{ - state.get_object( neigh_id ), neigh_id.subset + state.get_object( neigh_id ), neigh_id.set_id }; delta += interaction_->evaluate( new_object, neigh_object ); } @@ -140,7 +140,7 @@ namespace geode return this->contribution( delta ); } - double statistic( const ObjectSet< ObjectType >& state ) const override + double statistic( const ObjectSets< ObjectType >& state ) const override { double sum = 0.0; this->for_each_targeted_object( state, [&]( const ObjectId& @@ -150,11 +150,11 @@ namespace geode state.get_all_object(); // state.neighbors( obj_id, 1.1 ); for( const auto& neigh_obj_id : neighbors ) { - // if( neigh_obj_id.subset_id < obj_id.subset_id ) + // if( neigh_obj_id.set_id < obj_id.set_id ) //{ // continue; // } - // if( neigh_obj_id.subset_id == obj_id.subset_id + // if( neigh_obj_id.set_id == obj_id.set_id // && neigh_obj_id.id <= obj_id.id ) //{ // continue; @@ -164,9 +164,9 @@ namespace geode { continue; } - ObjectRef< ObjectType > object{ cur_obj, obj_id.subset }; + ObjectRef< ObjectType > object{ cur_obj, obj_id.set_id }; ObjectRef< ObjectType > neigh_object{ - state.get_object( neigh_obj_id ), neigh_obj_id.subset + state.get_object( neigh_obj_id ), neigh_obj_id.set_id }; sum += interaction_->evaluate( object, neigh_object ); diff --git a/include/geode/stochastic/sampling/mcmc/models/components/single_object_term.hpp b/include/geode/stochastic/sampling/mcmc/models/components/single_object_term.hpp index dc0e690..ac415c0 100644 --- a/include/geode/stochastic/sampling/mcmc/models/components/single_object_term.hpp +++ b/include/geode/stochastic/sampling/mcmc/models/components/single_object_term.hpp @@ -22,7 +22,7 @@ */ #pragma once -#include +#include #include @@ -34,34 +34,34 @@ namespace geode public: explicit SingleObjectTerm( std::string_view name, double lambda, - absl::flat_hash_set< uuid > targeted_subset_ids, + absl::flat_hash_set< uuid > targeted_set_ids, ObjectContributionFunc contribution_func ) - : EnergyTerm< ObjectType >( name, lambda, targeted_subset_ids ), + : EnergyTerm< ObjectType >( name, lambda, targeted_set_ids ), contribution_func_( std::move( contribution_func ) ) { } - double total_log( const ObjectSet< ObjectType >& state ) const override + double total_log( const ObjectSets< ObjectType >& state ) const override { auto total = this->statistic( state ); return this->contribution( total ); } - double delta_log_add( const ObjectSet< ObjectType >& /*state*/, + double delta_log_add( const ObjectSets< ObjectType >& /*state*/, const ObjectType& new_object, - const uuid& new_object_subset_id ) const override + const uuid& new_object_set_id ) const override { - if( !this->is_targeted_subset( new_object_subset_id ) ) + if( !this->is_targeted_set( new_object_set_id ) ) { return 0.0; } return this->contribution( contribution_func_( new_object ) ); } - double delta_log_remove( const ObjectSet< ObjectType >& state, + double delta_log_remove( const ObjectSets< ObjectType >& state, ObjectId object_id ) const override { - if( !this->is_targeted_subset( object_id.subset ) ) + if( !this->is_targeted_set( object_id.set_id ) ) { return 0.0; } @@ -69,13 +69,13 @@ namespace geode -contribution_func_( state.get_object( object_id ) ) ); } - double delta_log_change( const ObjectSet< ObjectType >& state, + double delta_log_change( const ObjectSets< ObjectType >& state, ObjectId old_object_id, const ObjectType& new_object, - const uuid& new_object_subset_id ) const override + const uuid& new_object_set_id ) const override { - if( this->is_targeted_subset( old_object_id.subset ) - && this->is_targeted_subset( new_object_subset_id ) ) + if( this->is_targeted_set( old_object_id.set_id ) + && this->is_targeted_set( new_object_set_id ) ) { return 0.0; } @@ -85,7 +85,7 @@ namespace geode return this->contribution( delta ); } - double statistic( const ObjectSet< ObjectType >& state ) const override + double statistic( const ObjectSets< ObjectType >& state ) const override { double total = 0.0; this->for_each_targeted_object( state, [&]( const ObjectId& id ) { diff --git a/include/geode/stochastic/sampling/mcmc/models/gibbs_energy.hpp b/include/geode/stochastic/sampling/mcmc/models/gibbs_energy.hpp index f2fa7e3..efe4f85 100644 --- a/include/geode/stochastic/sampling/mcmc/models/gibbs_energy.hpp +++ b/include/geode/stochastic/sampling/mcmc/models/gibbs_energy.hpp @@ -23,7 +23,7 @@ #pragma once #include -#include +#include namespace geode { @@ -37,7 +37,7 @@ namespace geode { } - double total_log_energy( const ObjectSet< ObjectType >& state ) const + double total_log_energy( const ObjectSets< ObjectType >& state ) const { double log_energy = 0.0; const auto& energy_terms = energy_terms_collection_.all_terms(); @@ -48,25 +48,25 @@ namespace geode return log_energy; } - double total_log_energy_for_subset( - const ObjectSet< ObjectType >& state, const uuid& subset_id ) const + double total_log_energy_for_set( + const ObjectSets< ObjectType >& state, const uuid& set_id ) const { double log_energy = 0.0; for( const auto term : - energy_terms_collection_.terms_for_subset( subset_id ) ) + energy_terms_collection_.terms_for_set( set_id ) ) { log_energy += term->total_log( state ); } return log_energy; } - double delta_log_add( const ObjectSet< ObjectType >& state, + double delta_log_add( const ObjectSets< ObjectType >& state, const ObjectRef< ObjectType >& new_object ) const { double log_energy = 0.0; - for( const auto& term : - energy_terms_collection_.terms_for_subset( new_object - .subset ) ) // energy_terms_collection_.all_terms() ) + for( const auto& term : energy_terms_collection_.terms_for_set( + new_object + .set_id ) ) // energy_terms_collection_.all_terms() ) { log_energy += term->delta_log_add( state, new_object ); } @@ -74,24 +74,24 @@ namespace geode } double delta_log_remove( - const ObjectSet< ObjectType >& state, const ObjectId& id ) const + const ObjectSets< ObjectType >& state, const ObjectId& id ) const { double log_energy = 0.0; - for( const auto& term : energy_terms_collection_.terms_for_subset( - id.subset ) ) // energy_terms_collection_.all_terms() ) + for( const auto& term : energy_terms_collection_.terms_for_set( + id.set_id ) ) // energy_terms_collection_.all_terms() ) { log_energy += term->delta_log_remove( state, id ); } return log_energy; } - double delta_log_change( const ObjectSet< ObjectType >& state, + double delta_log_change( const ObjectSets< ObjectType >& state, const ObjectId& old_id, const ObjectRef< ObjectType >& new_object ) const { double log_energy = 0.0; - for( const auto& term : energy_terms_collection_.terms_for_subset( - old_id.subset ) ) // energy_terms_collection_.all_terms() ) + for( const auto& term : energy_terms_collection_.terms_for_set( + old_id.set_id ) ) // energy_terms_collection_.all_terms() ) { log_energy += term->delta_log_change( state, old_id, new_object ); diff --git a/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp b/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp index c903cb5..d98275e 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp @@ -30,22 +30,47 @@ namespace geode { + template < typename ObjectType > + void add_birth_death_change_moves( + std::unique_ptr< geode::ObjectSetSampler< ObjectType > >& sampler, + geode::ProposalKernel< ObjectType >& kernel, + double birth_ratio, + double death_ratio, + double change_ratio ) + { + const auto total_ratio = birth_ratio + death_ratio; + OPENGEODE_EXCEPTION( + total_ratio > 0., "BIRTH+DEATH ratio must be positive" ); + + const auto p_birth = birth_ratio / total_ratio; + kernel.add_move( + std::make_unique< geode::BirthDeathMove< ObjectType > >( + sampler, total_ratio, p_birth ) ); + + if( change_ratio > 0. ) + { + kernel.add_move( + std::make_unique< geode::ChangeMove< ObjectType > >( + sampler, change_ratio ) ); + } + } + template < typename ObjectType > std::unique_ptr< ProposalKernel< ObjectType > > create_birth_death_kernel( - const uuid& subset_uuid, + const uuid& set_id, const ObjectSetSampler< ObjectType >& sampler, double birth_prob ) { auto kernel = std::make_unique< ProposalKernel< ObjectType > >(); kernel->add_move( - { subset_uuid, std::make_unique< BirthDeathMove< ObjectType > >( - sampler, 1., birth_prob ) } ); + { set_id, std::make_unique< BirthDeathMove< ObjectType > >( + sampler, 1., birth_prob ) } ); return kernel; } template < typename ObjectType > std::unique_ptr< ProposalKernel< ObjectType > > - create_birth_death_change_kernel( const uuid& subset_uuid, + create_birth_death_change_kernel( const uuid& set_id, const ObjectSetSampler< ObjectType >& sampler, double birth_prob, double death_prob ) @@ -54,12 +79,11 @@ namespace geode OPENGEODE_EXCEPTION( birth_death_prob < 1., "[Proposal Kernel] - changes should be allowed." ); auto kernel = std::make_unique< ProposalKernel< ObjectType > >(); - kernel->add_move( subset_uuid, - std::make_unique< BirthDeathMove< ObjectType > >( - sampler, birth_death_prob, birth_prob / birth_death_prob ) ); kernel->add_move( - subset_uuid, std::make_unique< ChangeMove< ObjectType > >( - sampler, 1. - birth_death_prob ) ); + set_id, std::make_unique< BirthDeathMove< ObjectType > >( sampler, + birth_death_prob, birth_prob / birth_death_prob ) ); + kernel->add_move( set_id, std::make_unique< ChangeMove< ObjectType > >( + sampler, 1. - birth_death_prob ) ); return kernel; } } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp b/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp index 545be61..579064b 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp @@ -24,7 +24,7 @@ #pragma once #include #include -#include +#include namespace geode { @@ -122,7 +122,7 @@ namespace geode virtual ~Move() = default; virtual MoveResult< ObjectType > propose_move( - const std::vector< ObjectType >& subset, + const ObjectSet< ObjectType >& set, RandomEngine& engine ) const = 0; double proportion_weight() const @@ -139,14 +139,13 @@ namespace geode protected: std::optional< geode::index_t > draw_a_sample_id( - const std::vector< ObjectType >& subset, - RandomEngine& engine ) const + const ObjectSet< ObjectType >& set, RandomEngine& engine ) const { - if( subset.empty() ) + if( set.empty() ) { return std::nullopt; } - const auto max_obj_id = subset.size(); + 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; @@ -171,14 +170,14 @@ namespace geode } MoveResult< ObjectType > propose_move( - const std::vector< ObjectType >& subset, + const ObjectSet< ObjectType >& set, RandomEngine& engine ) const override { if( engine.sample_bernoulli( birth_ratio_ ) ) { - return propose_birth_move( subset, engine ); + return propose_birth_move( set, engine ); } - return propose_death_move( subset, engine ); + return propose_death_move( set, engine ); } void initialize_probability( double probability ) override @@ -200,8 +199,7 @@ namespace geode private: MoveResult< ObjectType > propose_birth_move( - const std::vector< ObjectType >& subset, - RandomEngine& engine ) const + const ObjectSet< ObjectType >& set, RandomEngine& engine ) const { MoveResult< ObjectType > birth; birth.type = MoveType::Birth; @@ -214,16 +212,15 @@ 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( subset.size() + 1.0 ); + log_p_death_ - std::log( set.size() + 1.0 ); return birth; } MoveResult< ObjectType > propose_death_move( - const std::vector< ObjectType >& subset, - RandomEngine& engine ) const + const ObjectSet< ObjectType >& set, RandomEngine& engine ) const { MoveResult< ObjectType > death; - death.old_object_id = this->draw_a_sample_id( subset, engine ); + death.old_object_id = this->draw_a_sample_id( set, engine ); if( !death.old_object_id.has_value() ) { return death; @@ -231,9 +228,10 @@ 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( subset.size() ); + log_p_death_ - std::log( set.size() ); death.proposal_probabilities.log_backward_prob = - log_p_birth_ + this->sampler_.log_pdf( subset[cur_object_id] ); + log_p_birth_ + + this->sampler_.log_pdf( set.get_object( cur_object_id ) ); return death; } @@ -254,17 +252,18 @@ namespace geode } MoveResult< ObjectType > propose_move( - const std::vector< ObjectType >& subset, + const ObjectSet< ObjectType >& set, RandomEngine& engine ) const override { MoveResult< ObjectType > change; - change.old_object_id = this->draw_a_sample_id( subset, engine ); + change.old_object_id = this->draw_a_sample_id( set, engine ); if( !change.old_object_id.has_value() ) { return change; } change.type = MoveType::Change; - const auto& object_to_change = subset[change.old_object_id.value()]; + const auto& object_to_change = + set.get_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 522dfcf..cde651e 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp @@ -31,7 +31,7 @@ namespace geode template < typename ObjectType > struct Proposal { - uuid subset_id; + uuid set_id; MoveResult< ObjectType > proposed_move; ObjectRef< ObjectType > new_object() @@ -39,19 +39,19 @@ namespace geode OPENGEODE_EXCEPTION( proposed_move.new_object.has_value(), "[Proposal] Proposal has no new_object" ); return ObjectRef< ObjectType >{ proposed_move.new_object.value(), - subset_id }; + set_id }; }; ObjectId old_object_id() { OPENGEODE_EXCEPTION( proposed_move.old_object_id.has_value(), "[Proposal] Proposal has no old_object_id" ); - return ObjectId{ proposed_move.old_object_id.value(), subset_id }; + return ObjectId{ proposed_move.old_object_id.value(), set_id }; }; std::string string() const { - return absl::StrCat( "Move proposal on subset: ", subset_id, " -- ", + return absl::StrCat( "Move proposal on subset: ", set_id, " -- ", proposed_move.string() ); } }; @@ -62,51 +62,51 @@ namespace geode public: virtual ~ProposalKernel() = default; - Proposal< ObjectType > propose( - const ObjectSet< ObjectType >& current, RandomEngine& engine ) const + Proposal< ObjectType > propose( const ObjectSets< ObjectType >& current, + RandomEngine& engine ) const { - OPENGEODE_EXCEPTION( !subset_moves_.empty(), + OPENGEODE_EXCEPTION( !set_moves_.empty(), "[MCMC Proposal Kernel] - no move are defined in the Kernel." ); auto rnd = engine.sample_uniform( uniform_distribution_closed_ ); for( const auto proba_id : Range{ cumulative_probs_.size() } ) { if( rnd <= cumulative_probs_[proba_id] ) { - auto& [subset_uuid, move] = subset_moves_[proba_id]; - return Proposal< ObjectType >{ subset_uuid, + auto& [set_id, move] = set_moves_[proba_id]; + return Proposal< ObjectType >{ set_id, move->propose_move( - current.get_subset( subset_uuid ), engine ) }; + current.get_set( set_id ), engine ) }; } } throw OpenGeodeException( "[MCMC Proposal Kernel]: Should not be reached move pdf is " "correctly set." ); return Proposal< ObjectType >{ uuid{}, - subset_moves_.back().second->propose_move( - current.get_subset( uuid{} ), engine ) }; + set_moves_.back().second->propose_move( + current.get_set( uuid{} ), engine ) }; } - void add_move( const uuid& subset_uuid, - std::unique_ptr< Move< ObjectType > > move ) + void add_move( + const uuid& set_id, std::unique_ptr< Move< ObjectType > > move ) { - subset_moves_.push_back( { subset_uuid, std::move( move ) } ); + set_moves_.push_back( { set_id, std::move( move ) } ); initialize_probabilities(); } std::string string() const { auto message = absl::StrCat( "Proposal Kernel:", - "\n\t - number of moves: ", subset_moves_.size() ); + "\n\t - number of moves: ", set_moves_.size() ); absl::StrAppend( &message, "\n\t --> move cumulative probabilities:" ); for( const auto cumsum : cumulative_probs_ ) { absl::StrAppend( &message, " ", cumsum ); } - for( const auto& [subset_uuid, move] : subset_moves_ ) + for( const auto& [set_id, move] : set_moves_ ) { absl::StrAppend( &message, " \n\t --> move on subset ", - subset_uuid.string(), ": ", move->string() ); + set_id.string(), ": ", move->string() ); } return message; } @@ -114,10 +114,10 @@ namespace geode private: std::vector< double > compute_probabilities() const { - std::vector< double > probabilities( subset_moves_.size(), 0. ); + std::vector< double > probabilities( set_moves_.size(), 0. ); // Extract weights - std::transform( subset_moves_.begin(), subset_moves_.end(), + std::transform( set_moves_.begin(), set_moves_.end(), probabilities.begin(), []( const auto& move ) { return move.second->proportion_weight(); } ); @@ -157,7 +157,7 @@ namespace geode { for( const auto move_id : geode::Range{ probabilities.size() } ) { - subset_moves_[move_id].second->initialize_probability( + set_moves_[move_id].second->initialize_probability( probabilities[move_id] ); } } @@ -171,7 +171,7 @@ namespace geode private: std::vector< std::pair< uuid, std::unique_ptr< Move< ObjectType > > > > - subset_moves_; + set_moves_; std::vector< double > cumulative_probs_; geode::UniformClosed< double > uniform_distribution_closed_; diff --git a/include/geode/stochastic/spatial/object_neighborhood.hpp b/include/geode/stochastic/spatial/object_neighborhood.hpp index f4e0aa6..dc03c68 100644 --- a/include/geode/stochastic/spatial/object_neighborhood.hpp +++ b/include/geode/stochastic/spatial/object_neighborhood.hpp @@ -35,14 +35,14 @@ namespace geode struct ObjectId { index_t index; - uuid subset; + uuid set_id; bool operator==( const ObjectId& other ) const noexcept { - return index == other.index && subset == other.subset; + return index == other.index && set_id == other.set_id; } bool operator!=( const ObjectId& other ) const noexcept { - return index != other.index || subset != other.subset; + return index != other.index || set_id != other.set_id; } }; diff --git a/include/geode/stochastic/spatial/object_set.hpp b/include/geode/stochastic/spatial/object_set.hpp index d940563..6c307ab 100644 --- a/include/geode/stochastic/spatial/object_set.hpp +++ b/include/geode/stochastic/spatial/object_set.hpp @@ -23,70 +23,34 @@ #pragma once -#include - -#include -#include -#include -#include -#include - -namespace geode -{ - struct SubSetDescriptor - { - geode::uuid unique_id; - bool operator==( SubSetDescriptor const& other ) const noexcept - { - { - return unique_id == other.unique_id; - } - } - }; - - template < typename Type > - struct ObjectRef - { - const Type& object; - uuid subset; - }; -} // namespace geode +#include +#include namespace geode { template < typename Type > - class ObjectSet + class ObjectSet : public Identifier { public: - const std::vector< Type >& get_subset( const uuid& subset_id ) const; - const Type& get_object( const ObjectId& object_id ) const; - std::vector< ObjectId > get_all_object() const; + ObjectSet() noexcept = default; + ObjectSet( ObjectSet&& ) noexcept = default; + ObjectSet& operator=( ObjectSet&& ) noexcept = default; - index_t nb_subsets() const; - index_t nb_objects_in_subset( const uuid& subset_id ) const; - index_t nb_objects() const; + // const std::vector< Type >& objects() const; + // std::vector< Type >& objects(); - void add_subset( const uuid& subset_id ); - uuid add_subset(); - ObjectId add_object( Type&& object, const uuid& subset_id ); - void update_object( const ObjectId& object_id, Type&& object ); - void remove_object( const ObjectId& object_id ); + const Type& get_object( index_t index ) const; - // Object neighbor search by ObjectId (always excludes self) - std::vector< ObjectId > neighbors( - const ObjectId& object_id, double searching_distance ) const; - // Object neighbor search by arbitrary object (return self if in the - // object_set) - std::vector< ObjectId > neighbors( - const Type& object, double searching_distance ) const; + index_t add_object( Type&& object ); + void update_object( index_t index, Type&& object ); + void remove_object( index_t index ); - std::string string() const; + index_t size() const; + bool empty() const; - private: - std::vector< Type >& get_subset( const uuid& subset_id ); + std::string string() const; private: - absl::flat_hash_map< uuid, std::vector< Type > > groups_; - ObjectNeighborhood< Type::dim > neighborhood_; + std::vector< Type > objects_; }; } // namespace geode diff --git a/include/geode/stochastic/spatial/object_sets.hpp b/include/geode/stochastic/spatial/object_sets.hpp new file mode 100644 index 0000000..32ba4c1 --- /dev/null +++ b/include/geode/stochastic/spatial/object_sets.hpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019 - 2025 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +namespace geode +{ + struct SubSetDescriptor + { + geode::uuid unique_id; + bool operator==( SubSetDescriptor const& other ) const noexcept + { + { + return unique_id == other.unique_id; + } + } + }; + + template < typename Type > + struct ObjectRef + { + const Type& object; + uuid set_id; + }; +} // namespace geode + +namespace geode +{ + template < typename Type > + class ObjectSets + { + public: + 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; + + index_t nb_sets() const; + index_t nb_objects_in_set( const uuid& set_id ) const; + index_t nb_objects() const; + + uuid add_set(); + ObjectId add_object( Type&& object, const uuid& set_id ); + void update_object( const ObjectId& object_id, Type&& object ); + void remove_object( const ObjectId& object_id ); + + // Object neighbor search by ObjectId (always excludes self) + std::vector< ObjectId > neighbors( + const ObjectId& object_id, double searching_distance ) const; + // Object neighbor search by arbitrary object (return self if in the + // object_set) + std::vector< ObjectId > neighbors( + const Type& object, double searching_distance ) const; + + std::string string() const; + + private: + ObjectSet< Type >& get_set( const uuid& set_id ); + + private: + absl::flat_hash_map< uuid, ObjectSet< Type > > sets_; + ObjectNeighborhood< Type::dim > neighborhood_; + }; +} // namespace geode diff --git a/include/geode/stochastic/spatial/pairwise_interactions.hpp b/include/geode/stochastic/spatial/pairwise_interactions.hpp index 4d9f056..3bda5bc 100644 --- a/include/geode/stochastic/spatial/pairwise_interactions.hpp +++ b/include/geode/stochastic/spatial/pairwise_interactions.hpp @@ -29,7 +29,7 @@ #include #include -#include +#include namespace geode { @@ -39,9 +39,9 @@ namespace geode public: enum struct SCOPE { - all_subset, - same_subset, // only within the same subset - different_subset // only across subsets + all_set, + same_set, // only within the same subset + different_set // only across subsets }; explicit PairwiseInteraction() = default; @@ -51,13 +51,13 @@ namespace geode double evaluate( const ObjectRef< Type >& object_a, const ObjectRef< Type >& object_b ) const { - if( scope_ == SCOPE::same_subset - && object_a.subset != object_b.subset ) + if( scope_ == SCOPE::same_set + && object_a.set_id != object_b.set_id ) { return 0.0; } - if( scope_ == SCOPE::different_subset - && object_a.subset == object_b.subset ) + if( scope_ == SCOPE::different_set + && object_a.set_id == object_b.set_id ) { return 0.0; } @@ -71,7 +71,7 @@ namespace geode const ObjectRef< Type >& object_b ) const = 0; private: - SCOPE scope_{ SCOPE::all_subset }; + SCOPE scope_{ SCOPE::all_set }; }; template < typename Type > diff --git a/src/geode/stochastic/CMakeLists.txt b/src/geode/stochastic/CMakeLists.txt index ea52b89..6a63ddf 100644 --- a/src/geode/stochastic/CMakeLists.txt +++ b/src/geode/stochastic/CMakeLists.txt @@ -23,6 +23,7 @@ add_geode_library( FOLDER "geode/stochastic" SOURCES "spatial/object_set.cpp" + "spatial/object_sets.cpp" "spatial/object_neighborhood.cpp" "sampling/direct/ball_sampler.cpp" "sampling/direct/bounding_box_sampler.cpp" @@ -35,6 +36,7 @@ add_geode_library( "hello_world.cpp" PUBLIC_HEADERS "spatial/object_set.hpp" + "spatial/object_sets.hpp" "spatial/object_neighborhood.hpp" "spatial/object_helpers.hpp" "spatial/pairwise_interactions.hpp" diff --git a/src/geode/stochastic/spatial/object_set.cpp b/src/geode/stochastic/spatial/object_set.cpp index e1edc17..6a726bf 100644 --- a/src/geode/stochastic/spatial/object_set.cpp +++ b/src/geode/stochastic/spatial/object_set.cpp @@ -1,226 +1,91 @@ - -#include -#include +/* + * Copyright (c) 2019 - 2025 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ #include #include -namespace -{ - template < geode::index_t dimension > - geode::Vector< dimension > enlarge_vector( double distance ); - template <> - geode::Vector2D enlarge_vector< 2 >( double distance ) - { - return geode::Vector2D{ { distance, distance } }; - } +#include +#include + +#include - template <> - geode::Vector3D enlarge_vector< 3 >( double distance ) - { - return geode::Vector3D{ { distance, distance, distance } }; - } - template < geode::index_t dimension > - void enlarge_box( - geode::BoundingBox< dimension >& box, double search_distance ) - { - auto vec = enlarge_vector< dimension >( search_distance ); - box.add_point( box.min() - vec ); - box.add_point( box.max() + vec ); - } -} // namespace namespace geode { template < typename Type > - const std::vector< Type >& ObjectSet< Type >::get_subset( - const uuid& subset_id ) const - { - auto it = groups_.find( subset_id ); - OPENGEODE_EXCEPTION( it != groups_.end(), "[ObjectSet] - group (", - subset_id.string(), ") is not defined." ); - return it->second; - } - - template < typename Type > - const Type& ObjectSet< Type >::get_object( const ObjectId& object ) const - { - auto& subset = get_subset( object.subset ); - OPENGEODE_EXCEPTION( object.index < subset.size(), - "[ObjectSet]- object index out of range." ); - return subset[object.index]; - } - - template < typename Type > - std::vector< ObjectId > ObjectSet< Type >::get_all_object() const - { - std::vector< ObjectId > result; - result.reserve( nb_objects() ); - for( const auto& [subset_id, objs] : groups_ ) - { - for( const auto obj_id : geode::Range{ objs.size() } ) - { - result.push_back( { obj_id, subset_id } ); - } - } - return result; - } - - template < typename Type > - index_t ObjectSet< Type >::nb_subsets() const + const Type& ObjectSet< Type >::get_object( index_t index ) const { - return groups_.size(); + OPENGEODE_EXCEPTION( index < objects_.size(), + "[ObjectSet] - object index out of range." ); + return objects_[index]; } template < typename Type > - index_t ObjectSet< Type >::nb_objects_in_subset( - const uuid& subset_id ) const + index_t ObjectSet< Type >::add_object( Type&& object ) { - return get_subset( subset_id ).size(); + objects_.push_back( std::move( object ) ); + return objects_.size() - 1; } template < typename Type > - index_t ObjectSet< Type >::nb_objects() const + void ObjectSet< Type >::update_object( index_t index, Type&& object ) { - index_t nb_objects{ 0 }; - for( const auto& [subset_id, objs] : groups_ ) - { - geode_unused( subset_id ); - nb_objects += objs.size(); - } - return nb_objects; - } - - template < typename Type > - void ObjectSet< Type >::add_subset( const uuid& subset_id ) - { - auto [it, inserted] = - groups_.emplace( subset_id, std::vector< Type >{} ); - OPENGEODE_EXCEPTION( inserted, "[ObjectSet]- group (", - subset_id.string(), ") already exists." ); - } - template < typename Type > - uuid ObjectSet< Type >::add_subset() - { - uuid subset_id{}; - auto [it, inserted] = - groups_.emplace( subset_id, std::vector< Type >{} ); - OPENGEODE_EXCEPTION( inserted, "[ObjectSet]- group (", - subset_id.string(), ") already exists." ); - return subset_id; - } - - template < typename Type > - ObjectId ObjectSet< Type >::add_object( - Type&& object, const uuid& subset_id ) - { - auto& group = get_subset( subset_id ); - ObjectId new_object_id{ static_cast< index_t >( group.size() ), - subset_id }; - group.push_back( std::move( object ) ); - neighborhood_.add( object_bounding_box( object ), new_object_id ); - return new_object_id; + OPENGEODE_EXCEPTION( index < objects_.size(), + "[ObjectSet] - object index out of range." ); + objects_[index] = std::move( object ); } template < typename Type > - void ObjectSet< Type >::update_object( - const ObjectId& old_object_id, Type&& new_object ) + void ObjectSet< Type >::remove_object( index_t index ) { - auto& subset = get_subset( old_object_id.subset ); - OPENGEODE_EXCEPTION( old_object_id.index < subset.size(), - "[ObjectSet]- index of object to update out of range." ); - auto old_box = object_bounding_box( subset[old_object_id.index] ); - auto new_box = object_bounding_box( new_object ); - neighborhood_.update( old_box, new_box, old_object_id ); - subset[old_object_id.index] = std::move( new_object ); - } - - template < typename Type > - void ObjectSet< Type >::remove_object( const ObjectId& object_id ) - { - auto& subset = get_subset( object_id.subset ); - OPENGEODE_EXCEPTION( object_id.index < subset.size(), - "[ObjectSet]- index of object to remove out of range." ); - neighborhood_.remove( - object_bounding_box( subset[object_id.index] ), object_id ); - - ObjectId last_id{ static_cast< geode::index_t >( subset.size() - 1 ), - object_id.subset }; - if( object_id != last_id ) + OPENGEODE_EXCEPTION( index < objects_.size(), + "[ObjectSet] - object index out of range." ); + if( index != objects_.size() - 1 ) { - auto box_to_move = object_bounding_box( subset.back() ); - neighborhood_.update( box_to_move, last_id, object_id ); - subset[object_id.index] = std::move( subset.back() ); + objects_[index] = std::move( objects_.back() ); } - subset.pop_back(); - } - - template < typename Type > - std::vector< ObjectId > ObjectSet< Type >::neighbors( - const ObjectId& object_id, double searching_distance ) const - { - auto box = object_bounding_box( get_object( object_id ) ); - enlarge_box< Type::dim >( box, searching_distance ); - return neighborhood_.get_all_neighbor_ids( box, object_id ); - // std::vector< ObjectId > - // result; - // for( const auto& [subset_id, objs] : groups_ ) - // { - // for( const auto obj_id : geode::Range{ objs.size() } ) - // { - // const ObjectId cur_object_id{ obj_id, subset_id }; - // if( cur_object_id == object_id ) - // { - // continue; - // } - // result.push_back( cur_object_id ); - // } - // } - // return result; + objects_.pop_back(); } template < typename Type > - std::vector< ObjectId > ObjectSet< Type >::neighbors( - const Type& object, double searching_distance ) const + index_t ObjectSet< Type >::size() const { - auto box = object_bounding_box( object ); - enlarge_box< Type::dim >( box, searching_distance ); - return neighborhood_.get_all_neighbor_ids( box, std::nullopt ); - - // geode_unused( searching_distance ); - // geode_unused( object ); - // std::vector< ObjectId > result; - // for( const auto& [subset_id, objs] : groups_ ) - // { - // for( const auto obj_id : geode::Range{ objs.size() } ) - // { - // const ObjectId cur_object_id{ obj_id, subset_id }; - // result.push_back( cur_object_id ); - // } - // } - // return result; + return objects_.size(); } template < typename Type > - std::vector< Type >& ObjectSet< Type >::get_subset( const uuid& subset_id ) + bool ObjectSet< Type >::empty() const { - return const_cast< std::vector< Type >& >( - static_cast< const ObjectSet* >( this )->get_subset( subset_id ) ); + return objects_.empty(); } template < typename Type > std::string ObjectSet< Type >::string() const { - auto message = - absl::StrCat( "ObjectSet with ", nb_subsets(), " subsets:" ); - for( const auto& [subset_id, objs] : groups_ ) - { - absl::StrAppend( &message, - "\n\t --> subset uuid: ", subset_id.string(), - "; number of objects: ", objs.size() ); - } - return message; + return absl::StrCat( + "ObjectSet ", this->id().string(), " with ", size(), " objects" ); } + // Explicit template instantiation template class opengeode_stochastic_stochastic_api ObjectSet< Point2D >; template class opengeode_stochastic_stochastic_api ObjectSet< Point3D >; template class opengeode_stochastic_stochastic_api diff --git a/src/geode/stochastic/spatial/object_sets.cpp b/src/geode/stochastic/spatial/object_sets.cpp new file mode 100644 index 0000000..c090997 --- /dev/null +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -0,0 +1,189 @@ + +#include +#include + +#include +#include +namespace +{ + template < geode::index_t dimension > + geode::Vector< dimension > enlarge_vector( double distance ); + + template <> + geode::Vector2D enlarge_vector< 2 >( double distance ) + { + return geode::Vector2D{ { distance, distance } }; + } + + template <> + geode::Vector3D enlarge_vector< 3 >( double distance ) + { + return geode::Vector3D{ { distance, distance, distance } }; + } + template < geode::index_t dimension > + void enlarge_box( + geode::BoundingBox< dimension >& box, double search_distance ) + { + auto vec = enlarge_vector< dimension >( search_distance ); + box.add_point( box.min() - vec ); + box.add_point( box.max() + vec ); + } +} // namespace +namespace geode +{ + template < typename Type > + const ObjectSet< Type >& ObjectSets< Type >::get_set( + const uuid& set_id ) const + { + auto it = sets_.find( set_id ); + OPENGEODE_EXCEPTION( it != sets_.end(), "[ObjectSet] - group (", + set_id.string(), ") is not defined." ); + return it->second; + } + + template < typename Type > + const Type& ObjectSets< Type >::get_object( const ObjectId& object ) const + { + auto& set = get_set( object.set_id ); + OPENGEODE_EXCEPTION( object.index < set.size(), + "[ObjectSet]- object index out of range." ); + return set.get_object( object.index ); + } + + template < typename Type > + std::vector< ObjectId > ObjectSets< Type >::get_all_object() const + { + std::vector< ObjectId > result; + result.reserve( nb_objects() ); + for( const auto& [set_id, objs] : sets_ ) + { + for( const auto obj_id : geode::Range{ objs.size() } ) + { + result.push_back( { obj_id, set_id } ); + } + } + return result; + } + + template < typename Type > + index_t ObjectSets< Type >::nb_sets() const + { + return sets_.size(); + } + + template < typename Type > + index_t ObjectSets< Type >::nb_objects_in_set( const uuid& set_id ) const + { + return get_set( set_id ).size(); + } + + template < typename Type > + index_t ObjectSets< Type >::nb_objects() const + { + index_t nb_objects{ 0 }; + for( const auto& [set_id, objs] : sets_ ) + { + geode_unused( set_id ); + nb_objects += objs.size(); + } + return nb_objects; + } + + template < typename Type > + uuid ObjectSets< Type >::add_set() + { + ObjectSet< Type > new_set; + const auto new_set_id = new_set.id(); + auto [it, inserted] = sets_.emplace( new_set_id, std::move( new_set ) ); + OPENGEODE_EXCEPTION( inserted, "[ObjectSet]- group (", + new_set_id.string(), ") already exists." ); + return new_set_id; + } + + template < typename Type > + ObjectId ObjectSets< Type >::add_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 }; + neighborhood_.add( object_bounding_box( object ), new_object_id ); + set.add_object( std::move( object ) ); + return new_object_id; + } + + template < typename Type > + void ObjectSets< Type >::update_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(), + "[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 ) ); + } + + 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(), + "[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 ), + object_id.set_id }; + if( object_id != last_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 ); + } + set.remove_object( object_id.index ); + } + + template < typename Type > + std::vector< ObjectId > ObjectSets< Type >::neighbors( + const ObjectId& object_id, double searching_distance ) const + { + auto box = object_bounding_box( get_object( object_id ) ); + enlarge_box< Type::dim >( box, searching_distance ); + return neighborhood_.get_all_neighbor_ids( box, object_id ); + } + + template < typename Type > + std::vector< ObjectId > ObjectSets< Type >::neighbors( + const Type& object, double searching_distance ) const + { + auto box = object_bounding_box( object ); + enlarge_box< Type::dim >( box, searching_distance ); + return neighborhood_.get_all_neighbor_ids( box, std::nullopt ); + } + + template < typename Type > + ObjectSet< Type >& ObjectSets< Type >::get_set( const uuid& set_id ) + { + return const_cast< ObjectSet< Type >& >( + static_cast< const ObjectSets* >( this )->get_set( set_id ) ); + } + + template < typename Type > + std::string ObjectSets< Type >::string() const + { + auto message = + absl::StrCat( "ObjectSets with ", nb_sets(), " ObjectSet:" ); + for( const auto& [set_id, objs] : sets_ ) + { + absl::StrAppend( &message, "\n\t - set uuid: ", set_id.string(), + "--> ", objs.string() ); + } + return message; + } + + template class opengeode_stochastic_stochastic_api ObjectSets< Point2D >; + template class opengeode_stochastic_stochastic_api ObjectSets< Point3D >; + template class opengeode_stochastic_stochastic_api + ObjectSets< OwnerSegment2D >; +} // namespace geode \ No newline at end of file diff --git a/tests/stochastic/CMakeLists.txt b/tests/stochastic/CMakeLists.txt index d12856a..f02b78e 100644 --- a/tests/stochastic/CMakeLists.txt +++ b/tests/stochastic/CMakeLists.txt @@ -18,6 +18,22 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +add_geode_test( + SOURCE "spatial/test-object-set.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) + +add_geode_test( + SOURCE "spatial/test-object-sets.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) + add_geode_test( SOURCE "spatial/test-pairwise-interactions.cpp" DEPENDENCIES @@ -97,13 +113,13 @@ add_geode_test( ${PROJECT_NAME}::stochastic ) - add_geode_test( - SOURCE "sampling/mcmc/test-metropolis-hasting-sampler.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic - ) +# add_geode_test( +# SOURCE "sampling/mcmc/test-metropolis-hasting-sampler.cpp" +# DEPENDENCIES +# OpenGeode::basic +# OpenGeode::geometry +# ${PROJECT_NAME}::stochastic +# ) #add_geode_test( # SOURCE "sampling/mcmc/test-mh-poisson.cpp" diff --git a/tests/stochastic/sampling/mcmc/models/components/test-intensity-term.cpp b/tests/stochastic/sampling/mcmc/models/components/test-intensity-term.cpp index 02fb4f8..18e4b0a 100644 --- a/tests/stochastic/sampling/mcmc/models/components/test-intensity-term.cpp +++ b/tests/stochastic/sampling/mcmc/models/components/test-intensity-term.cpp @@ -23,28 +23,26 @@ #include #include -#include +#include -geode::ObjectSet< geode::Point2D > create_object_set( - const geode::uuid &subset_id ) +geode::uuid init_object_set( geode::ObjectSets< geode::Point2D > &pattern ) { geode::Point2D p1{ { 0., 0. } }; geode::Point2D p2{ { 1., 1. } }; - geode::ObjectSet< geode::Point2D > pattern; - pattern.add_subset( subset_id ); - pattern.add_object( std::move( p1 ), subset_id ); - pattern.add_object( std::move( p2 ), subset_id ); + auto set_id = pattern.add_set(); + pattern.add_object( std::move( p1 ), set_id ); + pattern.add_object( std::move( p2 ), set_id ); - return pattern; + return set_id; } void test_normal_positive_intensity( double lambda, - const geode::ObjectSet< geode::Point2D > &pattern, - const geode::uuid &subset_id ) + const geode::ObjectSets< geode::Point2D > &pattern, + const geode::uuid &set_id ) { geode::DensityTerm< geode::Point2D > term( - "intensity", lambda, { subset_id } ); + "intensity", lambda, { set_id } ); auto neg_log_lambda = -std::log( lambda ); double total = term.total_log( pattern ); @@ -52,12 +50,12 @@ void test_normal_positive_intensity( double lambda, "[test intensity]- total_log wrong value." ); geode::Point2D p3{ { 2., 2. } }; - geode::ObjectRef< geode::Point2D > p_ref{ p3, subset_id }; + geode::ObjectRef< geode::Point2D > p_ref{ p3, set_id }; double delta_add = term.delta_log_add( pattern, p_ref ); OPENGEODE_EXCEPTION( delta_add == neg_log_lambda * 1., "[test intensity]- delta_log_add wrong value." ); - geode::ObjectId obj_id{ 0, subset_id }; + geode::ObjectId obj_id{ 0, set_id }; double delta_remove = term.delta_log_remove( pattern, obj_id ); OPENGEODE_EXCEPTION( delta_remove == neg_log_lambda * -1., @@ -68,23 +66,23 @@ void test_normal_positive_intensity( double lambda, } void test_normal_zero_intensity( double lambda, - const geode::ObjectSet< geode::Point2D > &pattern, - const geode::uuid &subset_id ) + const geode::ObjectSets< geode::Point2D > &pattern, + const geode::uuid &set_id ) { geode::DensityTerm< geode::Point2D > term( - "intensity", lambda, { subset_id } ); + "intensity", lambda, { set_id } ); double total = term.total_log( pattern ); OPENGEODE_EXCEPTION( std::isinf( total ), "[test zero intensity]- total_log wrong value." ); geode::Point2D p3{ { 2., 2. } }; - geode::ObjectRef< geode::Point2D > p_ref{ p3, subset_id }; + geode::ObjectRef< geode::Point2D > p_ref{ p3, set_id }; double delta_add = term.delta_log_add( pattern, p_ref ); OPENGEODE_EXCEPTION( std::isinf( delta_add ), "[test zero intensity]- delta_log_add wrong value." ); - geode::ObjectId obj_id{ 0, subset_id }; + geode::ObjectId obj_id{ 0, set_id }; double delta_remove = term.delta_log_remove( pattern, obj_id ); OPENGEODE_EXCEPTION( delta_remove == 0., @@ -99,18 +97,18 @@ int main() try { geode::StochasticLibrary::initialize(); - geode::uuid subset_id; - auto pattern = create_object_set( subset_id ); + geode::ObjectSets< geode::Point2D > pattern; + auto set_id = init_object_set( pattern ); - test_normal_positive_intensity( 0.5, pattern, subset_id ); + test_normal_positive_intensity( 0.5, pattern, set_id ); test_normal_positive_intensity( - geode::GLOBAL_EPSILON, pattern, subset_id ); - test_normal_positive_intensity( 100.0021165, pattern, subset_id ); + geode::GLOBAL_EPSILON, pattern, set_id ); + test_normal_positive_intensity( 100.0021165, pattern, set_id ); - test_normal_zero_intensity( 0., pattern, subset_id ); + test_normal_zero_intensity( 0., pattern, set_id ); test_normal_zero_intensity( - 0.9999 * geode::GLOBAL_EPSILON, pattern, subset_id ); + 0.9999 * geode::GLOBAL_EPSILON, pattern, set_id ); } catch( ... ) { @@ -120,9 +118,9 @@ int main() try { geode::StochasticLibrary::initialize(); - geode::uuid subset_id; + geode::uuid set_id; geode::DensityTerm< geode::Point2D > term( - "zero", -geode::GLOBAL_EPSILON, { subset_id } ); + "zero", -geode::GLOBAL_EPSILON, { set_id } ); geode::Logger::info( "TEST FAILED" ); return 1; } 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 6ab4063..c88c86d 100644 --- a/tests/stochastic/sampling/mcmc/models/components/test-pairwise-term.cpp +++ b/tests/stochastic/sampling/mcmc/models/components/test-pairwise-term.cpp @@ -26,32 +26,30 @@ #include #include -#include +#include -geode::ObjectSet< geode::Point2D > create_pairwise_object_set( - const geode::uuid& subset_id ) +geode::uuid init_object_set( geode::ObjectSets< geode::Point2D >& pattern ) { geode::Point2D p1{ { 0., 0. } }; geode::Point2D p2{ { 1., 1. } }; - geode::ObjectSet< geode::Point2D > pattern; - pattern.add_subset( subset_id ); - pattern.add_object( std::move( p1 ), subset_id ); - pattern.add_object( std::move( p2 ), subset_id ); + auto set_id = pattern.add_set(); + pattern.add_object( std::move( p1 ), set_id ); + pattern.add_object( std::move( p2 ), set_id ); - return pattern; + return set_id; } void test_normal_positive_pairwise( double gamma, - const geode::ObjectSet< geode::Point2D >& pattern, - const geode::uuid& subset_id ) + const geode::ObjectSets< geode::Point2D >& pattern, + const geode::uuid& set_id ) { auto interaction = std::make_unique< geode::EuclideanCutoffInteraction< geode::Point2D > >( 2.1 ); geode::PairwiseTerm< geode::Point2D > term( - "strauss", gamma, { subset_id }, std::move( interaction ) ); + "strauss", gamma, { set_id }, std::move( interaction ) ); auto neg_log_gamma = -std::log( gamma ); // p1 and p2 interact → 1 pair @@ -63,12 +61,12 @@ void test_normal_positive_pairwise( double gamma, // Adding a third point close to p1 → expect new interactions geode::Point2D p3{ { 0.5, 0.5 } }; - geode::ObjectRef< geode::Point2D > p_ref{ p3, subset_id }; + geode::ObjectRef< geode::Point2D > p_ref{ p3, set_id }; auto delta_add = term.delta_log_add( pattern, p_ref ); // p3 interacts with p1 and p2 → 2 new pairs OPENGEODE_EXCEPTION( delta_add == neg_log_gamma * 2., "[test pairwise] - delta_log_add wrong value." ); - geode::ObjectId obj_id{ 0, subset_id }; + geode::ObjectId obj_id{ 0, set_id }; auto delta_remove = term.delta_log_remove( pattern, obj_id ); // Removing p1 removes its interaction with p2 → 1 removed pair OPENGEODE_EXCEPTION( delta_remove == neg_log_gamma * -1., @@ -82,26 +80,26 @@ void test_normal_positive_pairwise( double gamma, } void test_zero_pairwise( double gamma, - const geode::ObjectSet< geode::Point2D >& pattern, - const geode::uuid& subset_id ) + const geode::ObjectSets< geode::Point2D >& pattern, + const geode::uuid& set_id ) { auto interaction = std::make_unique< geode::EuclideanCutoffInteraction< geode::Point2D > >( 2.1 ); geode::PairwiseTerm< geode::Point2D > term( - "interaction", gamma, { subset_id }, std::move( interaction ) ); + "interaction", gamma, { set_id }, std::move( interaction ) ); auto total = term.total_log( pattern ); OPENGEODE_EXCEPTION( std::isinf( total ), "[test zero pairwise] - log_total wrong value." ); geode::Point2D p3{ { 0.5, 0.5 } }; - geode::ObjectRef< geode::Point2D > p_ref{ p3, subset_id }; + geode::ObjectRef< geode::Point2D > p_ref{ p3, set_id }; auto delta_add = term.delta_log_add( pattern, p_ref ); OPENGEODE_EXCEPTION( std::isinf( delta_add ), "[test zero pairwise] - delta_log_add wrong value." ); - geode::ObjectId obj_id{ 0, subset_id }; + geode::ObjectId obj_id{ 0, set_id }; auto delta_remove = term.delta_log_remove( pattern, obj_id ); OPENGEODE_EXCEPTION( delta_remove == 0., "[test zero pairwise] - delta_log_remove wrong value." ); @@ -117,17 +115,15 @@ int main() { geode::StochasticLibrary::initialize(); - geode::uuid subset_id; - auto pattern = create_pairwise_object_set( subset_id ); + geode::ObjectSets< geode::Point2D > pattern; + auto set_id = init_object_set( pattern ); - test_normal_positive_pairwise( 0.5, pattern, subset_id ); - test_normal_positive_pairwise( - geode::GLOBAL_EPSILON, pattern, subset_id ); - test_normal_positive_pairwise( 2.0, pattern, subset_id ); + test_normal_positive_pairwise( 0.5, pattern, set_id ); + test_normal_positive_pairwise( geode::GLOBAL_EPSILON, pattern, set_id ); + test_normal_positive_pairwise( 2.0, pattern, set_id ); - test_zero_pairwise( 0., pattern, subset_id ); - test_zero_pairwise( - 0.9999 * geode::GLOBAL_EPSILON, pattern, subset_id ); + test_zero_pairwise( 0., pattern, set_id ); + test_zero_pairwise( 0.9999 * geode::GLOBAL_EPSILON, pattern, set_id ); geode::Logger::info( "TEST SUCCESS" ); return 0; } diff --git a/tests/stochastic/sampling/mcmc/models/test-gibbs-energy.cpp b/tests/stochastic/sampling/mcmc/models/test-gibbs-energy.cpp index 2a7813f..589168e 100644 --- a/tests/stochastic/sampling/mcmc/models/test-gibbs-energy.cpp +++ b/tests/stochastic/sampling/mcmc/models/test-gibbs-energy.cpp @@ -29,32 +29,32 @@ #include #include #include -#include +#include namespace { - geode::ObjectSet< geode::Point2D > create_object_set( - const geode::uuid& subset_id ) + geode::ObjectSets< geode::Point2D > create_object_set( + const geode::uuid& set_id ) { geode::Point2D p1{ { 0., 0. } }; geode::Point2D p2{ { 1., 1. } }; - geode::ObjectSet< geode::Point2D > pattern; - pattern.add_object( std::move( p1 ), subset_id ); - pattern.add_object( std::move( p2 ), subset_id ); + geode::ObjectSets< geode::Point2D > pattern; + pattern.add_object( std::move( p1 ), set_id ); + pattern.add_object( std::move( p2 ), set_id ); return pattern; } } // namespace -void test_gibbs_energy( const geode::uuid& subset_id ) +void test_gibbs_energy( const geode::uuid& set_id ) { geode::EnergyTermCollection< geode::Point2D > energy_terms; // Add intensity term energy_terms.add_energy_term( - std::make_unique< geode::DensityTerm< geode::Point2D > >( "intensity", - 0.5, absl::flat_hash_set< geode::uuid >{ subset_id } ) ); + std::make_unique< geode::DensityTerm< geode::Point2D > >( + "intensity", 0.5, absl::flat_hash_set< geode::uuid >{ set_id } ) ); // Add pairwise term with trivial interaction: always counts 1 for each pair auto interaction = @@ -63,7 +63,7 @@ void test_gibbs_energy( const geode::uuid& subset_id ) energy_terms.add_energy_term( std::make_unique< geode::PairwiseTerm< geode::Point2D > >( - "interaction", 0.8, absl::flat_hash_set< geode::uuid >{ subset_id }, + "interaction", 0.8, absl::flat_hash_set< geode::uuid >{ set_id }, std::move( interaction ) ) ); OPENGEODE_EXCEPTION( energy_terms.size() == 2, @@ -71,7 +71,7 @@ void test_gibbs_energy( const geode::uuid& subset_id ) geode::GibbsEnergy< geode::Point2D > gibbs_energy( energy_terms ); - auto pattern = create_object_set( subset_id ); + auto pattern = create_object_set( set_id ); // Check total log-energy is finite double total_energy = gibbs_energy.total_log_energy( pattern ); @@ -80,12 +80,12 @@ void test_gibbs_energy( const geode::uuid& subset_id ) // Add new point to test delta_add geode::Point2D p3{ { 2., 2. } }; - geode::ObjectRef< geode::Point2D > p_ref{ p3, subset_id }; + geode::ObjectRef< geode::Point2D > p_ref{ p3, set_id }; double delta_add = gibbs_energy.delta_log_add( pattern, p_ref ); OPENGEODE_EXCEPTION( std::isfinite( delta_add ), "[test gibbs] Delta add should be finite." ); - geode::ObjectId obj_id{ 0, subset_id }; + geode::ObjectId obj_id{ 0, set_id }; // Remove point test double delta_remove = gibbs_energy.delta_log_remove( pattern, obj_id ); OPENGEODE_EXCEPTION( std::isfinite( delta_remove ), @@ -108,9 +108,9 @@ int main() try { geode::StochasticLibrary::initialize(); - geode::uuid subset_id; + geode::uuid set_id; - // test_gibbs_energy( subset_id ); + // test_gibbs_energy( set_id ); } catch( ... ) { diff --git a/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp b/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp index 844e6ba..e776b62 100644 --- a/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp +++ b/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp @@ -29,28 +29,26 @@ #include #include #include -#include +#include namespace { - geode::ObjectSet< geode::Point2D > create_object_set( - const geode::uuid& subset_id ) + geode::uuid init_object_set( geode::ObjectSets< geode::Point2D >& pattern ) { geode::Point2D p1{ { 0., 0. } }; geode::Point2D p2{ { 1., 1. } }; - geode::ObjectSet< geode::Point2D > pattern; - pattern.add_subset( subset_id ); - pattern.add_object( std::move( p1 ), subset_id ); - pattern.add_object( std::move( p2 ), subset_id ); + auto set_id = pattern.add_set(); + pattern.add_object( std::move( p1 ), set_id ); + pattern.add_object( std::move( p2 ), set_id ); - return pattern; + return set_id; } void test_proposal_kernel() { - geode::uuid subset_id; - auto config = create_object_set( subset_id ); + geode::ObjectSets< geode::Point2D > config; + auto set_id = init_object_set( config ); geode::Point2D min_point{ { 0., 0. } }; geode::Point2D max_point{ { 10., 100. } }; @@ -63,7 +61,7 @@ namespace // Create classical birth-death-change kernel auto kernel = geode::create_birth_death_change_kernel< geode::Point2D >( - subset_id, sampler, 0.4, 0.4 ); + set_id, sampler, 0.4, 0.4 ); geode::RandomEngine engine; @@ -91,12 +89,12 @@ namespace "0." ); OPENGEODE_EXCEPTION( - std::abs( proposed_move.proposal_probabilities - .log_backward_prob - - ( std::log( 0.8 * 0.5 ) - - std::log( config.nb_objects_in_subset( - subset_id ) - + 1.0 ) ) ) + std::abs( + proposed_move.proposal_probabilities + .log_backward_prob + - ( std::log( 0.8 * 0.5 ) + - std::log( config.nb_objects_in_set( set_id ) + + 1.0 ) ) ) < geode::GLOBAL_EPSILON, "[test proposal] Birth backward log-prob mismatch." ); break; @@ -111,7 +109,7 @@ namespace "[test proposal] Death must provide index." ); OPENGEODE_EXCEPTION( proposed_move.old_object_id.value() - < config.nb_objects_in_subset( subset_id ), + < config.nb_objects_in_set( set_id ), "[test proposal] Death index out of bounds." ); // Probabilities OPENGEODE_EXCEPTION( @@ -130,7 +128,7 @@ namespace "[test proposal] Change must provide index." ); OPENGEODE_EXCEPTION( proposed_move.old_object_id.value() - < config.nb_objects_in_subset( subset_id ), + < config.nb_objects_in_set( set_id ), "[test proposal] Change index out of bounds." ); break; @@ -157,10 +155,12 @@ namespace "[test proposal] Kernel did not produce all move types." ); // --- Edge case: empty object_set --- - geode::ObjectSet< geode::Point2D > empty_config; - empty_config.add_subset( subset_id ); - - auto proposal = kernel->propose( empty_config, engine ); + geode::ObjectSets< geode::Point2D > empty_config; + const auto empty_set_id = empty_config.add_set(); + auto empty_kernel = + geode::create_birth_death_change_kernel< geode::Point2D >( + empty_set_id, sampler, 0.4, 0.4 ); + auto proposal = empty_kernel->propose( empty_config, engine ); const auto& proposed_move = proposal.proposed_move; OPENGEODE_EXCEPTION( diff --git a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp index 5535984..8f352e6 100644 --- a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp +++ b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp @@ -73,21 +73,20 @@ namespace } void test_steps( const geode::MetropolisHastings< geode::Point2D >& mh, - const geode::uuid& subset_id ) + const geode::uuid& set_id ) { geode::RandomEngine engine; - absl::flat_hash_map< geode::uuid, geode::index_t > targets = { - { subset_id, 20 } - }; - // geode::ObjectSet< geode::Point2D > state = + absl::flat_hash_map< geode::uuid, geode::index_t > targets = { { set_id, + 20 } }; + // geode::ObjectSets< geode::Point2D > state = // mh.initialize_object_set_with_sampling( engine, targets ); - geode::ObjectSet< geode::Point2D > state; - state.add_subset( subset_id ); - state.add_object( geode::Point2D{ { 1., 1. } }, subset_id ); - state.add_object( geode::Point2D{ { 2., 2. } }, subset_id ); - state.add_object( geode::Point2D{ { 3., 3. } }, subset_id ); + geode::ObjectSets< geode::Point2D > state; + state.add_set( set_id ); + 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 ); geode::index_t stat_sum{ 0 }; constexpr geode::index_t N{ 100000 }; @@ -136,7 +135,7 @@ namespace } } // should be change... only pone group here - stat_sum += state.nb_objects_in_subset( subset_id ); + stat_sum += state.nb_objects_in_set( set_id ); if( count % 1000 == 0 ) { @@ -172,13 +171,13 @@ int main() box.add_point( min_point ); box.add_point( max_point ); - geode::uuid subset_id; + geode::uuid set_id; geode::UniformPointSetSampler< 2 > sampler( box ); double birth_prob = 0.3; double death_prob = 0.1; auto kernel = geode::create_birth_death_change_kernel< geode::Point2D >( - subset_id, sampler, birth_prob, death_prob ); + set_id, sampler, birth_prob, death_prob ); geode::EnergyTermCollection< geode::Point2D > energy_terms; @@ -186,14 +185,14 @@ int main() energy_terms.add_energy_term( std::make_unique< geode::DensityTerm< geode::Point2D > >( "intensity", 0.5, - absl::flat_hash_set< geode::uuid >{ subset_id } ) ); + absl::flat_hash_set< geode::uuid >{ set_id } ) ); geode::GibbsEnergy< geode::Point2D > poisson_energy( energy_terms ); geode::MetropolisHastings< geode::Point2D > mh( poisson_energy, std::move( kernel ) ); - test_steps( mh, subset_id ); + test_steps( mh, set_id ); test_beta_setter( mh ); test_acceptance_prob_helper(); diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 7063a49..448821d 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include namespace { struct PoissonDescription @@ -56,17 +56,17 @@ namespace struct UserProblem { geode::BoundingBox2D box; - geode::ObjectSet< geode::Point2D > object_set; - std::vector< geode::uuid > object_subset_id; + geode::ObjectSets< geode::Point2D > object_set; + std::vector< geode::uuid > object_set_id; geode::GibbsEnergy< geode::Point2D > gibbs_energy; - absl::flat_hash_map< geode::uuid, geode::uuid > subset_energy_term_ids; + absl::flat_hash_map< geode::uuid, geode::uuid > set_energy_term_ids; - absl::flat_hash_map< geode::uuid, double > subset_stats_targets; + absl::flat_hash_map< geode::uuid, double > set_stats_targets; absl::flat_hash_map< geode::uuid, geode::UniformPointSetSampler< 2 > > - subset_samplers; + set_samplers; std::unique_ptr< geode::MetropolisHastings< geode::Point2D > > mh_sampler; @@ -85,37 +85,37 @@ namespace std::make_unique< geode::ProposalKernel< geode::Point2D > >(); for( const auto& points_desc : description.set_desc ) { - auto subset_id = problem.object_set.add_subset(); - problem.object_subset_id.push_back( subset_id ); + auto set_id = problem.object_set.add_set(); + problem.object_set_id.push_back( set_id ); // this should be linked to the object subset - // flat_hash_map - problem.subset_samplers.emplace( subset_id, - geode::UniformPointSetSampler< 2 >{ problem.box, subset_id } ); + // flat_hash_map + problem.set_samplers.emplace( set_id, + geode::UniformPointSetSampler< 2 >{ problem.box, set_id } ); OPENGEODE_EXCEPTION( points_desc.death_birth_ratio > 0., "Object cannot be add or removed. Please set a BIRTH-DEATH " "with a positive probability." ); proposal_kernel->add_move( std::make_unique< geode::BirthDeathMove< geode::Point2D > >( - problem.subset_samplers.at( subset_id ), + problem.set_samplers.at( set_id ), points_desc.death_birth_ratio, points_desc.birth_ratio ) ); if( points_desc.change_ratio > 0. ) { proposal_kernel->add_move( std::make_unique< geode::ChangeMove< geode::Point2D > >( - problem.subset_samplers.at( subset_id ), + problem.set_samplers.at( set_id ), points_desc.change_ratio ) ); } // energy terms here we define intra subset terms (can be // several of them) need to add inter set energy terms - problem.subset_energy_term_ids.emplace( subset_id, + problem.set_energy_term_ids.emplace( set_id, problem.gibbs_energy.add_energy_term( std::make_unique< geode::DensityTerm< geode::Point2D > >( - points_desc.name, points_desc.density, subset_id ) ) ); + points_desc.name, points_desc.density, set_id ) ) ); - problem.subset_stats_targets.emplace( - subset_id, points_desc.density * area ); + problem.set_stats_targets.emplace( + set_id, points_desc.density * area ); } problem.mh_sampler = std::make_unique< geode::MetropolisHastings< geode::Point2D > >( @@ -131,26 +131,24 @@ namespace problem.mh_sampler->walk( problem.object_set, engine, 500 ); - std::vector< double > sum_points( problem.object_subset_id.size(), 0. ); - std::vector< double > sum_sq_points( - problem.object_subset_id.size(), 0. ); + std::vector< double > sum_points( problem.object_set_id.size(), 0. ); + std::vector< double > sum_sq_points( problem.object_set_id.size(), 0. ); auto N = problem_description.nb_realizations; for( const auto i : geode::Range{ N } ) { problem.mh_sampler->walk( problem.object_set, engine, problem_description.nb_steps ); - for( const auto subset_id : - geode::Range{ problem.object_subset_id.size() } ) + for( const auto set_id : + geode::Range{ problem.object_set_id.size() } ) { - const auto& energy_term_uuid = - problem.subset_energy_term_ids.at( - problem.object_subset_id[subset_id] ); + const auto& energy_term_uuid = problem.set_energy_term_ids.at( + problem.object_set_id[set_id] ); const auto nb_points = problem.gibbs_energy.energy_term_statistic( problem.object_set, energy_term_uuid ); - sum_points[subset_id] += nb_points; - sum_sq_points[subset_id] += nb_points * nb_points; + sum_points[set_id] += nb_points; + sum_sq_points[set_id] += nb_points * nb_points; } } std::transform( sum_points.begin(), sum_points.end(), @@ -161,21 +159,17 @@ namespace sum_sq_points.begin(), [N]( double p ) { return p / N; } ); - for( const auto subset_id : - geode::Range{ problem.object_subset_id.size() } ) + for( const auto set_id : geode::Range{ problem.object_set_id.size() } ) { - const auto& subset_uuid = problem.object_subset_id[subset_id]; - const auto expected_points = - problem.subset_stats_targets.at( subset_uuid ); - - const auto variance = - sum_sq_points[subset_id] - - ( sum_points[subset_id] * sum_points[subset_id] ); - geode::Logger::info( - "[MH test] mean points = ", sum_points[subset_id], + const auto& set_id = problem.object_set_id[set_id]; + const auto expected_points = problem.set_stats_targets.at( set_id ); + + const auto variance = sum_sq_points[set_id] + - ( sum_points[set_id] * sum_points[set_id] ); + geode::Logger::info( "[MH test] mean points = ", sum_points[set_id], " and var = ", variance, " (expected ", expected_points, ")" ); const auto error_stat = - std::abs( sum_points[subset_id] - expected_points ) + std::abs( sum_points[set_id] - expected_points ) / expected_points; OPENGEODE_EXCEPTION( error_stat < 0.02, "[MH test] mean number of points not close to enought to " diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss-statistics-and-convergence.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss-statistics-and-convergence.cpp index 3dbe3e0..6117a14 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss-statistics-and-convergence.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss-statistics-and-convergence.cpp @@ -45,13 +45,13 @@ namespace double area = domain_length * domain_length; - geode::uuid subset_id; - geode::UniformPointSetSampler< 2 > sampler( box, subset_id ); + geode::uuid set_id; + geode::UniformPointSetSampler< 2 > sampler( box, set_id ); geode::GibbsEnergy< geode::Point2D > energy; energy.add_energy_term( std::make_unique< geode::DensityTerm< geode::Point2D > >( - "intensity", poisson_density, subset_id ) ); + "intensity", poisson_density, set_id ) ); // auto interaction_fn = // []( const geode::Point2D& a, const geode::uuid& a_uuid, @@ -78,10 +78,9 @@ namespace energy, std::move( kernel1 ) ); geode::RandomEngine engine; engine.set_seed( "@mh-test@" ); - absl::flat_hash_map< geode::uuid, geode::index_t > targets = { - { subset_id, 0 } - }; - geode::ObjectSet< geode::Point2D > state = + absl::flat_hash_map< geode::uuid, geode::index_t > targets = { { set_id, + 0 } }; + geode::ObjectSets< geode::Point2D > state = mh.initialize_object_set_with_sampling( engine, targets ); mh.walk( state, engine, 1000 ); constexpr geode::index_t N{ 5000 }; diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index 18a6a0c..47155eb 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include namespace { @@ -65,18 +65,18 @@ namespace struct UserProblem { geode::BoundingBox2D box; - geode::ObjectSet< geode::Point2D > object_set; - std::vector< geode::uuid > object_subset_id; + geode::ObjectSets< geode::Point2D > object_set; + std::vector< geode::uuid > object_set_id; geode::GibbsEnergy< geode::Point2D > gibbs_energy; absl::flat_hash_map< geode::uuid, std::vector< geode::uuid > > - subset_energy_term_ids; + set_energy_term_ids; absl::flat_hash_map< geode::uuid, std::vector< double > > - subset_stats_targets; + set_stats_targets; absl::flat_hash_map< geode::uuid, geode::UniformPointSetSampler< 2 > > - subset_samplers; + set_samplers; std::unique_ptr< geode::MetropolisHastings< geode::Point2D > > mh_sampler; @@ -87,7 +87,7 @@ namespace &message, "\n\t - VOI: BoundingBox ", box.string() ); absl::StrAppend( &message, "\n\t - ", object_set.string() ); absl::StrAppend( &message, "\n\t - subset uuid list: " ); - for( const auto& uuid : object_subset_id ) + for( const auto& uuid : object_set_id ) { absl::StrAppend( &message, "\n\t --> subset uuid: ", uuid.string() ); @@ -95,7 +95,7 @@ namespace absl::StrAppend( &message, "\n\t - ", gibbs_energy.string() ); absl::StrAppend( &message, "\n\t - subset energy term uuid list: " ); - for( const auto& [uuid, et_uuids] : subset_energy_term_ids ) + for( const auto& [uuid, et_uuids] : set_energy_term_ids ) { absl::StrAppend( &message, "\n\t --> subset uuid: ", uuid.string() ); @@ -107,7 +107,7 @@ namespace } absl::StrAppend( &message, "\n\t - subset stat target list: " ); - for( const auto& [uuid, stats] : subset_stats_targets ) + for( const auto& [uuid, stats] : set_stats_targets ) { absl::StrAppend( &message, "\n\t --> subset uuid: ", uuid.string() ); @@ -117,8 +117,8 @@ namespace } } absl::StrAppend( - &message, "\n\t - subset sampler: ", subset_samplers.size() ); - for( const auto& [uuid, sampler] : subset_samplers ) + &message, "\n\t - subset sampler: ", set_samplers.size() ); + for( const auto& [uuid, sampler] : set_samplers ) { absl::StrAppend( &message, "\n\t --> subset uuid: ", uuid.string() ); @@ -141,23 +141,23 @@ namespace std::make_unique< geode::ProposalKernel< geode::Point2D > >(); for( const auto& points_desc : description.set_desc ) { - auto subset_id = problem.object_set.add_subset(); - problem.object_subset_id.push_back( subset_id ); + auto set_id = problem.object_set.add_set(); + problem.object_set_id.push_back( set_id ); - problem.subset_samplers.emplace( subset_id, - geode::UniformPointSetSampler< 2 >{ problem.box, subset_id } ); + problem.set_samplers.emplace( set_id, + geode::UniformPointSetSampler< 2 >{ problem.box, set_id } ); OPENGEODE_EXCEPTION( points_desc.death_birth_ratio > 0., "Object cannot be add or removed. Please set a BIRTH-DEATH " "with a positive probability." ); proposal_kernel->add_move( std::make_unique< geode::BirthDeathMove< geode::Point2D > >( - problem.subset_samplers.at( subset_id ), + problem.set_samplers.at( set_id ), points_desc.death_birth_ratio, points_desc.birth_ratio ) ); if( points_desc.change_ratio > 0. ) { proposal_kernel->add_move( std::make_unique< geode::ChangeMove< geode::Point2D > >( - problem.subset_samplers.at( subset_id ), + problem.set_samplers.at( set_id ), points_desc.change_ratio ) ); } @@ -166,7 +166,7 @@ namespace std::vector< geode::uuid > energy_terms; energy_terms.push_back( problem.gibbs_energy.add_energy_term( std::make_unique< geode::DensityTerm< geode::Point2D > >( - points_desc.name, points_desc.density, subset_id ) ) ); + points_desc.name, points_desc.density, set_id ) ) ); auto interaction = std::make_unique< geode::EuclideanCutoffInteraction< geode::Point2D > >( @@ -174,16 +174,15 @@ namespace energy_terms.push_back( problem.gibbs_energy.add_energy_term( std::make_unique< geode::PairwiseTerm< geode::Point2D > >( "interaction", points_desc.gamma, std::move( interaction ), - subset_id ) ) ); - problem.subset_energy_term_ids.emplace( subset_id, energy_terms ); + set_id ) ) ); + problem.set_energy_term_ids.emplace( set_id, energy_terms ); std::vector< double > expected_statistics; expected_statistics.push_back( points_desc.expected_number_of_objects ); expected_statistics.push_back( points_desc.expected_number_of_intersections ); - problem.subset_stats_targets.emplace( - subset_id, expected_statistics ); + problem.set_stats_targets.emplace( set_id, expected_statistics ); } DEBUG( proposal_kernel->string() ); problem.mh_sampler = @@ -202,9 +201,9 @@ namespace problem.mh_sampler->walk( problem.object_set, engine, 500 ); // std::vector< double > sum_points( - // problem.object_subset_id.size(), 0. ); std::vector< double > + // problem.object_set_id.size(), 0. ); std::vector< double > // sum_nb_interactions( - // problem.object_subset_id.size(), 0. ); + // problem.object_set_id.size(), 0. ); // // auto N = problem_description.nb_realizations; @@ -213,16 +212,16 @@ namespace // problem.mh_sampler->walk( // problem.object_set, engine, // problem_description.nb_steps ); - // for( const auto subset_id : - // geode::Range{ problem.object_subset_id.size() } ) + // for( const auto set_id : + // geode::Range{ problem.object_set_id.size() } ) // { // const auto& energy_term_uuids = - // problem.subset_energy_term_ids.at( - // problem.object_subset_id[subset_id] ); - // sum_points[subset_id] += + // problem.set_energy_term_ids.at( + // problem.object_set_id[set_id] ); + // sum_points[set_id] += // problem.gibbs_energy.energy_term_statistic( // problem.object_set, energy_term_uuids[0] ); - // sum_nb_interactions[subset_id] += + // sum_nb_interactions[set_id] += // problem.gibbs_energy.energy_term_statistic( // problem.object_set, energy_term_uuids[1] ); // } @@ -236,23 +235,23 @@ namespace // sum_nb_interactions.begin(), [N]( double p ) { // return p / N; // } ); - // for( const auto subset_id : - // geode::Range{ problem.object_subset_id.size() } ) + // for( const auto set_id : + // geode::Range{ problem.object_set_id.size() } ) // { - // const auto& subset_uuid = - // problem.object_subset_id[subset_id]; const auto& + // const auto& set_id = + // problem.object_set_id[set_id]; const auto& // expected_stats = - // problem.subset_stats_targets.at( subset_uuid ); + // problem.set_stats_targets.at( set_id ); // // geode::Logger::info( "[MH test] mean points = ", - // sum_points[subset_id], " (expected ", + // sum_points[set_id], " (expected ", // expected_stats[0], // ")" // " and mean interactions = ", - // sum_nb_interactions[subset_id], " (expected ", + // sum_nb_interactions[set_id], " (expected ", // expected_stats[1], ")" ); // const auto error_stat = - // std::abs( sum_points[subset_id] - expected_stats[0] ) + // std::abs( sum_points[set_id] - expected_stats[0] ) // / expected_stats[0]; // // OPENGEODE_EXCEPTION( error_stat < 0.015, // // "[MH test] mean number of points not @@ -263,17 +262,17 @@ namespace // if( expected_stats[1] == 0 ) // { // OPENGEODE_EXCEPTION( - // sum_nb_interactions[subset_id] < + // sum_nb_interactions[set_id] < // geode::GLOBAL_EPSILON, // "[MH test] Number of interactions not close to // enought to " " expected value-- > error : ", - // sum_nb_interactions[subset_id] ); + // sum_nb_interactions[set_id] ); // } // else // { // const auto error_interactions = // std::abs( - // sum_nb_interactions[subset_id] - + // sum_nb_interactions[set_id] - // expected_stats[1] ) // / expected_stats[1]; // // OPENGEODE_EXCEPTION( @@ -310,7 +309,7 @@ namespace description1.expected_number_of_objects = nb_points[config]; description1.scope = - geode::PairwiseInteraction< geode::Point2D >::SCOPE::all_subset; + geode::PairwiseInteraction< geode::Point2D >::SCOPE::all_set; description1.distance_treshold = 1; description1.gamma = gamma_values[config]; description1.expected_number_of_intersections = @@ -354,7 +353,7 @@ namespace description1.expected_number_of_objects = nb_points[0]; description1.scope = - geode::PairwiseInteraction< geode::Point2D >::SCOPE::same_subset; + geode::PairwiseInteraction< geode::Point2D >::SCOPE::same_set; description1.distance_treshold = 1; description1.gamma = gamma_values[0]; description1.expected_number_of_intersections = nb_interactions[0]; @@ -370,7 +369,7 @@ namespace description2.expected_number_of_objects = nb_points[1]; description2.scope = - geode::PairwiseInteraction< geode::Point2D >::SCOPE::same_subset; + geode::PairwiseInteraction< geode::Point2D >::SCOPE::same_set; description2.distance_treshold = 2; description2.gamma = gamma_values[1]; description2.expected_number_of_intersections = nb_interactions[1]; @@ -386,7 +385,7 @@ namespace description3.expected_number_of_objects = nb_points[2]; description3.scope = - geode::PairwiseInteraction< geode::Point2D >::SCOPE::same_subset; + geode::PairwiseInteraction< geode::Point2D >::SCOPE::same_set; description3.distance_treshold = 1; description3.gamma = gamma_values[2]; description3.expected_number_of_intersections = nb_interactions[2]; @@ -402,7 +401,7 @@ namespace description4.expected_number_of_objects = nb_points[3]; description4.scope = - geode::PairwiseInteraction< geode::Point2D >::SCOPE::same_subset; + geode::PairwiseInteraction< geode::Point2D >::SCOPE::same_set; description4.distance_treshold = 1; description4.gamma = gamma_values[3]; description4.expected_number_of_intersections = nb_interactions[3]; diff --git a/tests/stochastic/spatial/test-object-set.cpp b/tests/stochastic/spatial/test-object-set.cpp new file mode 100644 index 0000000..2bcd210 --- /dev/null +++ b/tests/stochastic/spatial/test-object-set.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2019 - 2025 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include +#include + +namespace +{ + void test_add_and_access() + { + geode::ObjectSet< geode::Point2D > set; + + 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, + "[TestObjectSet] - Set size should be 3 after insertions" ); + OPENGEODE_EXCEPTION( !set.empty(), + "[TestObjectSet] - Set should not be empty after insertions" ); + + const auto& p = set.get_object( idx1 ); + OPENGEODE_EXCEPTION( p == geode::Point2D( { { 1.0, 1.0 } } ), + "[TestObjectSet] - Wrong object value at index 1" ); + } + + 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 } } ) ); + + set.update_object( idx1, geode::Point2D( { { 5.0, 5.0 } } ) ); + + const auto& updated = set.get_object( idx1 ); + OPENGEODE_EXCEPTION( updated == geode::Point2D( { { 5.0, 5.0 } } ), + "[TestObjectSet] - Object update failed" ); + } + + 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.remove_object( 1 ); + + OPENGEODE_EXCEPTION( set.size() == 2, + "[TestObjectSet] - Set size should be 2 after removal" ); + + const auto& last = set.get_object( 1 ); + OPENGEODE_EXCEPTION( last == geode::Point2D( { { 2.0, 2.0 } } ), + "[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 } } ) ); + + const auto& const_set = set; + const auto& p = const_set.get_object( 0 ); + OPENGEODE_EXCEPTION( p == geode::Point2D( { { 10.0, 10.0 } } ), + "[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 } } ) ); + + const auto desc = set.string(); + OPENGEODE_EXCEPTION( desc.find( "2 objects" ) != std::string::npos, + "[TestObjectSet] - string() output incorrect" ); + } +} // namespace + +int main() +{ + try + { + geode::Logger::info( "TEST ObjectSet" ); + geode::StochasticLibrary::initialize(); + + test_add_and_access(); + test_update_object(); + test_remove_object(); + test_const_access(); + test_string_representation(); + + geode::Logger::info( "TEST ObjectSet SUCCESS" ); + return 0; + } + catch( ... ) + { + return geode::geode_lippincott(); + } +} \ No newline at end of file diff --git a/tests/stochastic/spatial/test-object-sets.cpp b/tests/stochastic/spatial/test-object-sets.cpp new file mode 100644 index 0000000..f558447 --- /dev/null +++ b/tests/stochastic/spatial/test-object-sets.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2019 - 2025 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include +#include + +namespace +{ + using namespace geode; + + void test_add_sets_and_objects() + { + ObjectSets< Point2D > sets; + const auto set_id1 = sets.add_set(); + const auto set_id2 = sets.add_set(); + + OPENGEODE_EXCEPTION( + sets.nb_sets() == 2, "[TestObjectSets] - Expected 2 sets" ); + + const auto obj_a = + sets.add_object( geode::Point2D( { { 0.0, 0.0 } } ), set_id1 ); + const auto obj_b = + sets.add_object( geode::Point2D( { { 1.0, 1.0 } } ), set_id1 ); + const auto obj_c = + sets.add_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" ); + OPENGEODE_EXCEPTION( sets.nb_objects_in_set( set_id2 ) == 1, + "[TestObjectSets] - Set 2 should have 1 object" ); + OPENGEODE_EXCEPTION( sets.nb_objects() == 3, + "[TestObjectSets] - Total object count mismatch" ); + + const auto& point = sets.get_object( obj_b ); + OPENGEODE_EXCEPTION( point == Point2D( { { 1.0, 1.0 } } ), + "[TestObjectSets] - Wrong object value retrieved" ); + } + + void test_neighbors_by_object_id() + { + ObjectSets< Point2D > sets; + const auto set_id = sets.add_set(); + + const auto obj0 = + sets.add_object( geode::Point2D( { { 0.0, 0.0 } } ), set_id ); + const auto obj1 = + sets.add_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 + const auto near_neighbors = sets.neighbors( obj0, 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 + && near_neighbors[0].index == obj1.index, + "[TestObjectSets] - Wrong neighbor identified for obj0" ); + } + + void test_neighbors_by_object_value() + { + ObjectSets< Point2D > sets; + const auto set_id = sets.add_set(); + + 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 ); + + const geode::Point2D query( { { 0.5, 0.0 } } ); + const auto nearby = sets.neighbors( query, 1.0 ); + + OPENGEODE_EXCEPTION( !nearby.empty(), + "[TestObjectSets] - Expected neighbors near query object" ); + OPENGEODE_EXCEPTION( nearby.size() == 2, + "[TestObjectSets] - Expected 2 neighbors near query object" ); + } + + void test_string_representation() + { + ObjectSets< Point2D > sets; + const auto set_id = sets.add_set(); + sets.add_object( geode::Point2D( { { 0.0, 0.0 } } ), set_id ); + sets.add_object( geode::Point2D( { { 1.0, 1.0 } } ), set_id ); + + const auto desc = sets.string(); + OPENGEODE_EXCEPTION( desc.find( "objects" ) != std::string::npos, + "[TestObjectSets] - string() output should mention objects" ); + } +} // namespace + +int main() +{ + try + { + geode::Logger::info( "TEST ObjectSets" ); + geode::StochasticLibrary::initialize(); + + test_add_sets_and_objects(); + test_neighbors_by_object_id(); + test_neighbors_by_object_value(); + test_string_representation(); + + geode::Logger::info( "TEST ObjectSets SUCCESS" ); + return 0; + } + catch( ... ) + { + return geode::geode_lippincott(); + } +} \ No newline at end of file From 8935465bc28ead1088a4873504b62bee6b24a71e Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Thu, 16 Oct 2025 18:08:43 +0000 Subject: [PATCH 02/11] Apply prepare changes --- .../geode/stochastic/sampling/mcmc/models/gibbs_energy.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/geode/stochastic/sampling/mcmc/models/gibbs_energy.hpp b/include/geode/stochastic/sampling/mcmc/models/gibbs_energy.hpp index efe4f85..4a9a489 100644 --- a/include/geode/stochastic/sampling/mcmc/models/gibbs_energy.hpp +++ b/include/geode/stochastic/sampling/mcmc/models/gibbs_energy.hpp @@ -64,9 +64,9 @@ namespace geode const ObjectRef< ObjectType >& new_object ) const { double log_energy = 0.0; - for( const auto& term : energy_terms_collection_.terms_for_set( - new_object - .set_id ) ) // energy_terms_collection_.all_terms() ) + for( const auto& term : + energy_terms_collection_.terms_for_set( new_object + .set_id ) ) // energy_terms_collection_.all_terms() ) { log_energy += term->delta_log_add( state, new_object ); } From a9b5c85b4282575277749259512a9dedbbeb7c8d Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Thu, 16 Oct 2025 22:03:53 +0200 Subject: [PATCH 03/11] fix tests --- tests/stochastic/spatial/test-object-set.cpp | 37 +++++++++++-------- tests/stochastic/spatial/test-object-sets.cpp | 33 +++++++++-------- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/tests/stochastic/spatial/test-object-set.cpp b/tests/stochastic/spatial/test-object-set.cpp index 2bcd210..bed19c1 100644 --- a/tests/stochastic/spatial/test-object-set.cpp +++ b/tests/stochastic/spatial/test-object-set.cpp @@ -42,29 +42,32 @@ namespace "[TestObjectSet] - Set should not be empty after insertions" ); const auto& p = set.get_object( idx1 ); - OPENGEODE_EXCEPTION( p == geode::Point2D( { { 1.0, 1.0 } } ), - "[TestObjectSet] - Wrong object value at index 1" ); + const auto result = geode::Point2D{ { 1.0, 1.0 } }; + OPENGEODE_EXCEPTION( + p == result, "[TestObjectSet] - Wrong object value at index 1" ); } 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_object( geode::Point2D{ { 0.0, 0.0 } } ); + const auto idx1 = set.add_object( geode::Point2D{ { 1.0, 1.0 } } ); - set.update_object( idx1, geode::Point2D( { { 5.0, 5.0 } } ) ); + set.update_object( idx1, geode::Point2D{ { 5.0, 5.0 } } ); const auto& updated = set.get_object( idx1 ); - OPENGEODE_EXCEPTION( updated == geode::Point2D( { { 5.0, 5.0 } } ), - "[TestObjectSet] - Object update failed" ); + + const auto new_point = geode::Point2D{ { 5.0, 5.0 } }; + OPENGEODE_EXCEPTION( + updated == new_point, "[TestObjectSet] - Object update failed" ); } 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_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.remove_object( 1 ); @@ -72,7 +75,8 @@ namespace "[TestObjectSet] - Set size should be 2 after removal" ); const auto& last = set.get_object( 1 ); - OPENGEODE_EXCEPTION( last == geode::Point2D( { { 2.0, 2.0 } } ), + const auto result = geode::Point2D{ { 2.0, 2.0 } }; + OPENGEODE_EXCEPTION( last == result, "[TestObjectSet] - Remaining objects not shifted properly after " "removal" ); } @@ -80,19 +84,20 @@ namespace void test_const_access() { geode::ObjectSet< geode::Point2D > set; - set.add_object( geode::Point2D( { { 10.0, 10.0 } } ) ); + set.add_object( geode::Point2D{ { 10.0, 10.0 } } ); const auto& const_set = set; const auto& p = const_set.get_object( 0 ); - OPENGEODE_EXCEPTION( p == geode::Point2D( { { 10.0, 10.0 } } ), - "[TestObjectSet] - Const access mismatch" ); + const auto result = geode::Point2D{ { 10.0, 10.0 } }; + 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_object( geode::Point2D{ { 0.0, 0.0 } } ); + set.add_object( geode::Point2D{ { 1.0, 1.0 } } ); const auto desc = set.string(); OPENGEODE_EXCEPTION( desc.find( "2 objects" ) != std::string::npos, diff --git a/tests/stochastic/spatial/test-object-sets.cpp b/tests/stochastic/spatial/test-object-sets.cpp index f558447..cf90f98 100644 --- a/tests/stochastic/spatial/test-object-sets.cpp +++ b/tests/stochastic/spatial/test-object-sets.cpp @@ -29,7 +29,7 @@ namespace void test_add_sets_and_objects() { - ObjectSets< Point2D > sets; + ObjectSets< geode::Point2D > sets; const auto set_id1 = sets.add_set(); const auto set_id2 = sets.add_set(); @@ -37,11 +37,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_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_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_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,21 +51,22 @@ namespace "[TestObjectSets] - Total object count mismatch" ); const auto& point = sets.get_object( obj_b ); - OPENGEODE_EXCEPTION( point == Point2D( { { 1.0, 1.0 } } ), + const auto result = geode::Point2D{ { 1.0, 1.0 } }; + OPENGEODE_EXCEPTION( point == result, "[TestObjectSets] - Wrong object value retrieved" ); } void test_neighbors_by_object_id() { - ObjectSets< Point2D > sets; + ObjectSets< geode::Point2D > sets; const auto set_id = sets.add_set(); const auto obj0 = - sets.add_object( geode::Point2D( { { 0.0, 0.0 } } ), set_id ); + sets.add_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_object( geode::Point2D{ { 1.0, 0.0 } }, set_id ); const auto obj2 = - sets.add_object( geode::Point2D( { { 5.0, 0.0 } } ), set_id ); + sets.add_object( geode::Point2D{ { 5.0, 0.0 } }, set_id ); // Near neighbors should be within distance 2.0 const auto near_neighbors = sets.neighbors( obj0, 2.0 ); @@ -82,12 +83,12 @@ namespace void test_neighbors_by_object_value() { - ObjectSets< Point2D > sets; + ObjectSets< geode::Point2D > sets; const auto set_id = sets.add_set(); - 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_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 ); const geode::Point2D query( { { 0.5, 0.0 } } ); const auto nearby = sets.neighbors( query, 1.0 ); @@ -100,10 +101,10 @@ namespace void test_string_representation() { - ObjectSets< Point2D > sets; + ObjectSets< geode::Point2D > sets; const auto set_id = sets.add_set(); - sets.add_object( geode::Point2D( { { 0.0, 0.0 } } ), set_id ); - sets.add_object( geode::Point2D( { { 1.0, 1.0 } } ), set_id ); + sets.add_object( geode::Point2D{ { 0.0, 0.0 } }, set_id ); + sets.add_object( geode::Point2D{ { 1.0, 1.0 } }, set_id ); const auto desc = sets.string(); OPENGEODE_EXCEPTION( desc.find( "objects" ) != std::string::npos, From 13046c663f739391051dd6cf74d1b4a39de02269 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Thu, 16 Oct 2025 22:08:03 +0200 Subject: [PATCH 04/11] fix test --- tests/stochastic/spatial/test-object-sets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stochastic/spatial/test-object-sets.cpp b/tests/stochastic/spatial/test-object-sets.cpp index cf90f98..3bbcc75 100644 --- a/tests/stochastic/spatial/test-object-sets.cpp +++ b/tests/stochastic/spatial/test-object-sets.cpp @@ -90,7 +90,7 @@ namespace sets.add_object( geode::Point2D{ { 1.0, 0.0 } }, set_id ); sets.add_object( geode::Point2D{ { 3.0, 0.0 } }, set_id ); - const geode::Point2D query( { { 0.5, 0.0 } } ); + const geode::Point2D query{ { 0.5, 0.0 } }; const auto nearby = sets.neighbors( query, 1.0 ); OPENGEODE_EXCEPTION( !nearby.empty(), From b5e47121d9f213cd31b6e16e4594a57acbed830b Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 17 Oct 2025 09:52:50 +0200 Subject: [PATCH 05/11] new try --- include/geode/stochastic/spatial/object_set.hpp | 3 --- include/geode/stochastic/spatial/object_sets.hpp | 11 ----------- .../sampling/direct/bounding_box_sampler.cpp | 2 +- src/geode/stochastic/spatial/object_sets.cpp | 3 ++- 4 files changed, 3 insertions(+), 16 deletions(-) diff --git a/include/geode/stochastic/spatial/object_set.hpp b/include/geode/stochastic/spatial/object_set.hpp index 6c307ab..4d191e3 100644 --- a/include/geode/stochastic/spatial/object_set.hpp +++ b/include/geode/stochastic/spatial/object_set.hpp @@ -36,9 +36,6 @@ namespace geode ObjectSet( ObjectSet&& ) noexcept = default; ObjectSet& operator=( ObjectSet&& ) noexcept = default; - // const std::vector< Type >& objects() const; - // std::vector< Type >& objects(); - const Type& get_object( index_t index ) const; index_t add_object( Type&& object ); diff --git a/include/geode/stochastic/spatial/object_sets.hpp b/include/geode/stochastic/spatial/object_sets.hpp index 32ba4c1..c8fa4b2 100644 --- a/include/geode/stochastic/spatial/object_sets.hpp +++ b/include/geode/stochastic/spatial/object_sets.hpp @@ -34,17 +34,6 @@ namespace geode { - struct SubSetDescriptor - { - geode::uuid unique_id; - bool operator==( SubSetDescriptor const& other ) const noexcept - { - { - return unique_id == other.unique_id; - } - } - }; - template < typename Type > struct ObjectRef { diff --git a/src/geode/stochastic/sampling/direct/bounding_box_sampler.cpp b/src/geode/stochastic/sampling/direct/bounding_box_sampler.cpp index 824471d..6be3458 100644 --- a/src/geode/stochastic/sampling/direct/bounding_box_sampler.cpp +++ b/src/geode/stochastic/sampling/direct/bounding_box_sampler.cpp @@ -38,7 +38,7 @@ namespace geode public: Impl( const BoundingBox< dimension >& box ) { - for( const auto dim : Range( dimension ) ) + for( const auto dim : LRange( dimension ) ) { dist_coordinates[dim].min_value = box.min().value( dim ); dist_coordinates[dim].max_value = box.max().value( dim ); diff --git a/src/geode/stochastic/spatial/object_sets.cpp b/src/geode/stochastic/spatial/object_sets.cpp index c090997..2ea1bb4 100644 --- a/src/geode/stochastic/spatial/object_sets.cpp +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -94,7 +94,8 @@ namespace geode { ObjectSet< Type > new_set; const auto new_set_id = new_set.id(); - auto [it, inserted] = sets_.emplace( new_set_id, std::move( new_set ) ); + auto [it, inserted] = + sets_.insert( { new_set_id, std::move( new_set ) } ); OPENGEODE_EXCEPTION( inserted, "[ObjectSet]- group (", new_set_id.string(), ") already exists." ); return new_set_id; From 831b853d28a88613f36e9579babbd8718e6bc848 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 17 Oct 2025 10:15:39 +0200 Subject: [PATCH 06/11] another test --- src/geode/stochastic/spatial/object_sets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/geode/stochastic/spatial/object_sets.cpp b/src/geode/stochastic/spatial/object_sets.cpp index 2ea1bb4..531a0c6 100644 --- a/src/geode/stochastic/spatial/object_sets.cpp +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -93,7 +93,7 @@ namespace geode uuid ObjectSets< Type >::add_set() { ObjectSet< Type > new_set; - const auto new_set_id = new_set.id(); + auto new_set_id = new_set.id(); auto [it, inserted] = sets_.insert( { new_set_id, std::move( new_set ) } ); OPENGEODE_EXCEPTION( inserted, "[ObjectSet]- group (", From 44858fc3505e59c801b2a7c6d17026d6bd1a9c0f Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 17 Oct 2025 10:21:00 +0200 Subject: [PATCH 07/11] add copy constructeur --- include/geode/stochastic/spatial/object_set.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/geode/stochastic/spatial/object_set.hpp b/include/geode/stochastic/spatial/object_set.hpp index 4d191e3..555064b 100644 --- a/include/geode/stochastic/spatial/object_set.hpp +++ b/include/geode/stochastic/spatial/object_set.hpp @@ -35,6 +35,8 @@ namespace geode ObjectSet() noexcept = default; ObjectSet( ObjectSet&& ) noexcept = default; ObjectSet& operator=( ObjectSet&& ) noexcept = default; + ObjectSet( const ObjectSet& ) noexcept = default; + ObjectSet& operator=( const ObjectSet& ) noexcept = default; const Type& get_object( index_t index ) const; From cc0351c4c811524f382d3c7c933e56d4e0fae249 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 17 Oct 2025 10:26:52 +0200 Subject: [PATCH 08/11] revert changes --- include/geode/stochastic/spatial/object_set.hpp | 2 -- src/geode/stochastic/spatial/object_sets.cpp | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/include/geode/stochastic/spatial/object_set.hpp b/include/geode/stochastic/spatial/object_set.hpp index 555064b..4d191e3 100644 --- a/include/geode/stochastic/spatial/object_set.hpp +++ b/include/geode/stochastic/spatial/object_set.hpp @@ -35,8 +35,6 @@ namespace geode ObjectSet() noexcept = default; ObjectSet( ObjectSet&& ) noexcept = default; ObjectSet& operator=( ObjectSet&& ) noexcept = default; - ObjectSet( const ObjectSet& ) noexcept = default; - ObjectSet& operator=( const ObjectSet& ) noexcept = default; const Type& get_object( index_t index ) const; diff --git a/src/geode/stochastic/spatial/object_sets.cpp b/src/geode/stochastic/spatial/object_sets.cpp index 531a0c6..08ce5d5 100644 --- a/src/geode/stochastic/spatial/object_sets.cpp +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -93,9 +93,8 @@ namespace geode uuid ObjectSets< Type >::add_set() { ObjectSet< Type > new_set; - auto new_set_id = new_set.id(); - auto [it, inserted] = - sets_.insert( { new_set_id, std::move( new_set ) } ); + const auto& new_set_id = new_set.id(); + auto [it, inserted] = sets_.emplace( new_set_id, std::move( new_set ) ); OPENGEODE_EXCEPTION( inserted, "[ObjectSet]- group (", new_set_id.string(), ") already exists." ); return new_set_id; From 6ac8a04f83228542f53ef162b618d03c90bfb6e6 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 17 Oct 2025 10:40:01 +0200 Subject: [PATCH 09/11] remove const --- src/geode/stochastic/spatial/object_sets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/geode/stochastic/spatial/object_sets.cpp b/src/geode/stochastic/spatial/object_sets.cpp index 08ce5d5..8111ea3 100644 --- a/src/geode/stochastic/spatial/object_sets.cpp +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -93,7 +93,7 @@ namespace geode uuid ObjectSets< Type >::add_set() { ObjectSet< Type > new_set; - const auto& new_set_id = new_set.id(); + auto new_set_id = new_set.id(); auto [it, inserted] = sets_.emplace( new_set_id, std::move( new_set ) ); OPENGEODE_EXCEPTION( inserted, "[ObjectSet]- group (", new_set_id.string(), ") already exists." ); From 67cbcbeb43fe561aac52fb1a1e488029e4c299ac Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 17 Oct 2025 14:22:20 +0200 Subject: [PATCH 10/11] forbid copie for objectsets --- include/geode/stochastic/spatial/object_sets.hpp | 2 ++ src/geode/stochastic/spatial/object_sets.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/geode/stochastic/spatial/object_sets.hpp b/include/geode/stochastic/spatial/object_sets.hpp index c8fa4b2..3022e5e 100644 --- a/include/geode/stochastic/spatial/object_sets.hpp +++ b/include/geode/stochastic/spatial/object_sets.hpp @@ -47,6 +47,8 @@ namespace geode template < typename Type > class ObjectSets { + OPENGEODE_DISABLE_COPY( ObjectSets ); + public: const ObjectSet< Type >& get_set( const uuid& set_id ) const; const Type& get_object( const ObjectId& object_id ) const; diff --git a/src/geode/stochastic/spatial/object_sets.cpp b/src/geode/stochastic/spatial/object_sets.cpp index 8111ea3..c090997 100644 --- a/src/geode/stochastic/spatial/object_sets.cpp +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -93,7 +93,7 @@ namespace geode uuid ObjectSets< Type >::add_set() { ObjectSet< Type > new_set; - auto new_set_id = new_set.id(); + const auto new_set_id = new_set.id(); auto [it, inserted] = sets_.emplace( new_set_id, std::move( new_set ) ); OPENGEODE_EXCEPTION( inserted, "[ObjectSet]- group (", new_set_id.string(), ") already exists." ); From 2a26e7056c22d2cf59919b9e6cad840b2e1222d3 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 17 Oct 2025 15:11:22 +0200 Subject: [PATCH 11/11] add missing constructor --- include/geode/stochastic/spatial/object_sets.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/geode/stochastic/spatial/object_sets.hpp b/include/geode/stochastic/spatial/object_sets.hpp index 3022e5e..0ca68ff 100644 --- a/include/geode/stochastic/spatial/object_sets.hpp +++ b/include/geode/stochastic/spatial/object_sets.hpp @@ -50,6 +50,10 @@ namespace geode OPENGEODE_DISABLE_COPY( ObjectSets ); public: + ObjectSets() noexcept = default; + ObjectSets( ObjectSets&& ) noexcept = default; + ObjectSets& operator=( ObjectSets&& ) noexcept = default; + 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;