From 3d505b838b5762701d9a7c7b6d1d2ffa4777281e Mon Sep 17 00:00:00 2001 From: Arnaud Botella Date: Wed, 1 Apr 2026 15:38:53 +0200 Subject: [PATCH 01/17] fix(OpenGeode): update abseil From 6cedaa22968d8762c8abcba2ece6efe9540ae993 Mon Sep 17 00:00:00 2001 From: BotellaA <3213882+BotellaA@users.noreply.github.com> Date: Wed, 1 Apr 2026 13:42:20 +0000 Subject: [PATCH 02/17] Apply prepare changes --- .clang-tidy | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index b039849..725ab31 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -19,7 +19,8 @@ Checks: > -readability-use-anyofallof, -readability-redundant-access-specifiers, -readability-convert-member-functions-to-static, - -cppcoreguidelines-avoid-const-or-ref-data-members + -cppcoreguidelines-avoid-const-or-ref-data-members, + -cppcoreguidelines-pro-bounds-constant-array-index CheckOptions: - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic @@ -63,5 +64,9 @@ CheckOptions: value: lower_case - key: readability-function-cognitive-complexity.Threshold value: 10 + - key: readability-function-cognitive-complexity.IgnoreMacros + value: true - key: readability-function-size.ParameterThreshold value: 4 + - key: cppcoreguidelines-pro-type-member-init.IgnoreArrays + value: true From 3d7e55c0f96099d1312d997eb82a2718b6a42d9a Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Thu, 9 Apr 2026 14:23:30 +0200 Subject: [PATCH 03/17] feat(EnergyTerm): builder for energy terms --- bindings/python/src/stochastic/CMakeLists.txt | 2 +- bindings/python/src/stochastic/stochastic.cpp | 4 +- .../python/tests/stochastic/CMakeLists.txt | 10 +- .../models/energy_term_collection.hpp | 80 +-- .../models/energy_terms/density_term.hpp | 25 +- .../models/energy_terms/energy_term.hpp | 44 +- .../energy_terms/energy_term_builder.hpp | 127 ++++- .../energy_terms/energy_term_config.hpp | 12 +- .../models/energy_terms/intensity_term.hpp | 6 +- .../models/energy_terms/pairwise_term.hpp | 221 ++++---- .../energy_terms/single_object_term.hpp | 50 +- .../geode/stochastic/models/gibbs_energy.hpp | 26 +- .../stochastic/models/model_configuration.hpp | 2 +- .../helpers/fracture_simulation_runner.hpp | 473 +++++++++--------- .../spatial/object_neighborhood.hpp | 4 + .../geode/stochastic/spatial/object_sets.hpp | 17 +- .../pairwise_interactions_builder.hpp | 59 ++- .../pairwise_interactions_config.hpp | 32 +- .../segment_length_feature.hpp | 41 ++ .../single_object_feature.hpp | 54 ++ .../single_object_feature_builder.hpp | 88 ++++ .../single_object_feature_config.hpp | 42 ++ src/geode/stochastic/CMakeLists.txt | 7 +- .../models/energy_terms/intensity_term.cpp | 90 +--- src/geode/stochastic/spatial/object_sets.cpp | 57 ++- .../segment_length_feature.cpp | 117 +++++ tests/stochastic/CMakeLists.txt | 14 +- .../models/energy_terms/test-density-term.cpp | 3 +- .../models/energy_terms/test-gibbs-energy.cpp | 4 +- .../energy_terms/test-pairwise-term.cpp | 108 ++-- .../sampling/mcmc/test-mh-poisson-new.cpp | 38 +- tests/stochastic/spatial/test-object-sets.cpp | 8 +- 32 files changed, 1075 insertions(+), 790 deletions(-) create mode 100644 include/geode/stochastic/spatial/single_object_features/segment_length_feature.hpp create mode 100644 include/geode/stochastic/spatial/single_object_features/single_object_feature.hpp create mode 100644 include/geode/stochastic/spatial/single_object_features/single_object_feature_builder.hpp create mode 100644 include/geode/stochastic/spatial/single_object_features/single_object_feature_config.hpp create mode 100644 src/geode/stochastic/spatial/single_object_features/segment_length_feature.cpp diff --git a/bindings/python/src/stochastic/CMakeLists.txt b/bindings/python/src/stochastic/CMakeLists.txt index 48bd7a0..a79ce85 100644 --- a/bindings/python/src/stochastic/CMakeLists.txt +++ b/bindings/python/src/stochastic/CMakeLists.txt @@ -21,7 +21,7 @@ add_geode_python_binding( NAME "py_stochastic" SOURCES - "sampling/mcmc/helpers/fracture_simulation_runner.hpp" + # "sampling/mcmc/helpers/fracture_simulation_runner.hpp" "sampling/mcmc/helpers/simulation_monitor.hpp" "sampling/mcmc/helpers/simulation_printer.hpp" "sampling/mcmc/simulation_runner.hpp" diff --git a/bindings/python/src/stochastic/stochastic.cpp b/bindings/python/src/stochastic/stochastic.cpp index 27e9f2f..e03b15e 100644 --- a/bindings/python/src/stochastic/stochastic.cpp +++ b/bindings/python/src/stochastic/stochastic.cpp @@ -26,7 +26,7 @@ #include "sampling/direct/double_sampler.hpp" -#include "sampling/mcmc/helpers/fracture_simulation_runner.hpp" +// #include "sampling/mcmc/helpers/fracture_simulation_runner.hpp" #include "sampling/mcmc/helpers/simulation_monitor.hpp" #include "sampling/mcmc/helpers/simulation_printer.hpp" #include "sampling/mcmc/simulation_runner.hpp" @@ -50,5 +50,5 @@ PYBIND11_MODULE( opengeode_stochastic_py_stochastic, module ) geode::define_simulation_monitor( module ); geode::define_simulation_printer( module ); geode::define_simulation_runner( module ); - geode::define_fracture_simulation( module ); + // geode::define_fracture_simulation( module ); } \ No newline at end of file diff --git a/bindings/python/tests/stochastic/CMakeLists.txt b/bindings/python/tests/stochastic/CMakeLists.txt index ded7457..6e00e74 100644 --- a/bindings/python/tests/stochastic/CMakeLists.txt +++ b/bindings/python/tests/stochastic/CMakeLists.txt @@ -18,8 +18,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -add_geode_python_test( - SOURCE "test-py-mh-fractures.py" - DEPENDENCIES - ${PROJECT_NAME}::py_stochastic -) \ No newline at end of file +#add_geode_python_test( +# SOURCE "test-py-mh-fractures.py" +# DEPENDENCIES +# ${PROJECT_NAME}::py_stochastic +#) \ No newline at end of file diff --git a/include/geode/stochastic/models/energy_term_collection.hpp b/include/geode/stochastic/models/energy_term_collection.hpp index 9e05483..42e81b3 100644 --- a/include/geode/stochastic/models/energy_term_collection.hpp +++ b/include/geode/stochastic/models/energy_term_collection.hpp @@ -19,53 +19,35 @@ namespace geode EnergyTermCollection& operator=( EnergyTermCollection&& ) = default; [[nodiscard]] uuid add_energy_term( - std::shared_ptr< EnergyTerm< ObjectType > > term ) + std::unique_ptr< EnergyTerm< ObjectType > >&& term ) { - const uuid term_id = term->id(); - energy_terms_.emplace( term_id, term ); - for( const uuid& set_id : term->targeted_set_ids() ) - { - set_to_terms_[set_id].push_back( term ); - } - return term_id; + const uuid term_uuid = term->id(); + auto term_idx = energy_terms_.size(); + energy_terms_.emplace_back( std::move( term ) ); + energy_terms_map_.emplace( term_uuid, term_idx ); + return term_uuid; } [[nodiscard]] bool remove_energy_term( const uuid& term_id ) { - auto term_it = energy_terms_.find( term_id ); - if( term_it == energy_terms_.end() ) + auto term_it = energy_terms_map_.find( term_id ); + if( term_it == energy_terms_map_.end() ) { return false; } - - auto term = term_it->second; - - for( const uuid& set_id : term->targeted_set_ids() ) - { - auto vec_it = set_to_terms_.find( set_id ); - if( vec_it == set_to_terms_.end() ) - { - continue; - } - - auto& vec = vec_it->second; - vec.erase( - std::remove( vec.begin(), vec.end(), term ), vec.end() ); - - if( vec.empty() ) - { - set_to_terms_.erase( vec_it ); - } - } - - energy_terms_.erase( term_it ); + index_t idx = term_it->second; + index_t last = energy_terms_.size() - 1; + std::swap( energy_terms_[idx], energy_terms_[last] ); + energy_terms_map_[energy_terms_[idx]->id()] = idx; + energy_terms_.pop_back(); + energy_terms_map_.erase( term_it ); return true; } void clear() { energy_terms_.clear(); - set_to_terms_.clear(); + energy_terms_map_.clear(); } [[nodiscard]] index_t size() const @@ -76,36 +58,25 @@ namespace geode [[nodiscard]] const EnergyTerm< ObjectType >& get( const uuid& term_id ) const { - auto term_it = energy_terms_.find( term_id ); - OPENGEODE_EXCEPTION( term_it != energy_terms_.end(), + auto term_it = energy_terms_map_.find( term_id ); + OPENGEODE_EXCEPTION( term_it != energy_terms_map_.end(), absl::StrCat( "[EnergyTermCollection] Unknown energy term: ", term_id.string() ) ); - return *term_it->second; - } - - [[nodiscard]] const absl::flat_hash_map< uuid, - std::shared_ptr< EnergyTerm< ObjectType > > >& - all_terms() const - { - return energy_terms_; + return *energy_terms_[term_it->second]; } [[nodiscard]] const std::vector< - std::shared_ptr< EnergyTerm< ObjectType > > >& - terms_for_set( const uuid& set_id ) const + std::unique_ptr< EnergyTerm< ObjectType > > >& + energy_terms() const { - 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; + return energy_terms_; } [[nodiscard]] std::string string() const { auto message = absl::StrCat( "EnergyTermCollection: ", energy_terms_.size(), " terms:" ); - for( const auto& [id, term] : energy_terms_ ) + for( const auto& term : energy_terms_ ) { absl::StrAppend( &message, "\n\t --> ", term->string() ); } @@ -113,12 +84,9 @@ namespace geode } private: - absl::flat_hash_map< uuid, std::shared_ptr< EnergyTerm< ObjectType > > > + std::vector< std::unique_ptr< EnergyTerm< ObjectType > > > energy_terms_; - - absl::flat_hash_map< uuid, - std::vector< std::shared_ptr< EnergyTerm< ObjectType > > > > - set_to_terms_; + absl::flat_hash_map< uuid, index_t > energy_terms_map_; }; } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/models/energy_terms/density_term.hpp b/include/geode/stochastic/models/energy_terms/density_term.hpp index 9d10eed..320242b 100644 --- a/include/geode/stochastic/models/energy_terms/density_term.hpp +++ b/include/geode/stochastic/models/energy_terms/density_term.hpp @@ -22,39 +22,24 @@ */ #pragma once -#include - #include +#include namespace geode { template < typename ObjectType > - class DensityTerm : public SingleObjectTerm< ObjectType, - std::function< double( const ObjectType&, - const SpatialDomain< ObjectType::dim >& ) > > + class DensityTerm : public SingleObjectTerm< ObjectType > { public: explicit DensityTerm( std::string_view name, double lambda, std::vector< uuid > targeted_set_ids, const SpatialDomain< ObjectType::dim >& domain ) - : SingleObjectTerm< ObjectType, - std::function< double( const ObjectType&, - const SpatialDomain< ObjectType::dim >& ) > >( - name, + : SingleObjectTerm< ObjectType >( name, lambda, std::move( targeted_set_ids ), - 1.0, // scale by domain area to get density per unit - []( const ObjectType& obj, - const SpatialDomain< ObjectType::dim >& spatial_domain ) { - if( SpatialDomainChecker< ObjectType >:: - is_anchored_in_domain( spatial_domain, obj ) ) - { - return 1.0; - } - return 0.0; - }, // contribution = 1 anchoredin domain - domain ) + domain, + std::make_unique< ObjectInDomainFeature< ObjectType > >() ) { } }; diff --git a/include/geode/stochastic/models/energy_terms/energy_term.hpp b/include/geode/stochastic/models/energy_terms/energy_term.hpp index 8463bb6..fc33699 100644 --- a/include/geode/stochastic/models/energy_terms/energy_term.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term.hpp @@ -89,13 +89,17 @@ namespace geode public: explicit EnergyTerm( std::string_view name, double param, - std::vector< uuid >&& targeted_set_ids, + std::vector< uuid >&& impacted_set_ids, const SpatialDomain< ObjectType::dim >& domain ) : energy_scale_{ param }, - targeted_set_ids_{ std::move( targeted_set_ids ) }, + impacted_set_ids_{ std::move( impacted_set_ids ) }, domain_( domain ) { - std::sort( targeted_set_ids_.begin(), targeted_set_ids_.end() ); + std::sort( impacted_set_ids_.begin(), impacted_set_ids_.end() ); + impacted_set_ids_.erase( std::unique( impacted_set_ids_.begin(), + impacted_set_ids_.end() ), + impacted_set_ids_.end() ); + impacted_set_ids_.shrink_to_fit(); IdentifierBuilder builder( *this ); builder.set_name( name ); } @@ -107,9 +111,9 @@ namespace geode return energy_scale_.parameter(); } - [[nodiscard]] const std::vector< uuid >& targeted_set_ids() const + [[nodiscard]] const std::vector< uuid >& impacted_set_ids() const { - return targeted_set_ids_; + return impacted_set_ids_; } /// Energy contribution for a given statistic multiplier @@ -143,9 +147,9 @@ namespace geode absl::StrCat( "Term : ", name().value_or( id().string() ), "; uuid: ", id().string(), " parameter value: ", energy_scale_.parameter(), - " applyied on ", targeted_set_ids_.size(), + " applyied on ", impacted_set_ids_.size(), " object subsets -->" ); - for( const auto& set_id : targeted_set_ids_ ) + for( const auto& set_id : impacted_set_ids_ ) { absl::StrAppend( &message, "\t", set_id.string() ); } @@ -153,10 +157,10 @@ namespace geode } protected: - [[nodiscard]] bool is_targeted_set( const uuid& set_id ) const + [[nodiscard]] bool is_impacted_set( const uuid& set_id ) const { return std::binary_search( - targeted_set_ids_.begin(), targeted_set_ids_.end(), set_id ); + impacted_set_ids_.begin(), impacted_set_ids_.end(), set_id ); } [[nodiscard]] const SpatialDomain< ObjectType::dim >& domain() const @@ -164,30 +168,24 @@ namespace geode return domain_; } - // bool intersects_domain( const ObjectType& obj ) const - // { - // return SpatialDomainChecker< ObjectType - // >::intersects_domain( - // domain_, obj ); - // } - template < typename Func > - void for_each_targeted_object( - const ObjectSets< ObjectType >& state, Func&& do_apply ) const + void for_each_object_in_sets( const ObjectSets< ObjectType >& state, + const std::vector< uuid >& set_ids, + Func&& do_apply ) const { - for( const auto& targeted_set_id : targeted_set_ids_ ) + for( const auto& set_id : set_ids ) { - for( const auto set_id : - state.get_objects_in_set( targeted_set_id ) ) + for( const auto object_ids : + state.get_objects_in_set( set_id ) ) { - std::forward< Func >( do_apply )( set_id ); + std::forward< Func >( do_apply )( object_ids ); } } } private: detail::EnergyScale energy_scale_; - std::vector< uuid > targeted_set_ids_; + std::vector< uuid > impacted_set_ids_; const SpatialDomain< ObjectType::dim >& domain_; }; } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp b/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp index b0de765..7d49be6 100644 --- a/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp @@ -35,59 +35,138 @@ #include #include +#include +#include +#include + #include +#include +#include #include namespace geode { template < typename ObjectType > - std::unique_ptr< EnergyTerm< ObjectType > > build_density_term( - const DensityTermConfig& cfg, + std::unique_ptr< EnergyTerm< ObjectType > > build_single_term( + const SingleObjectTermConfig& cfg, const ObjectSets< ObjectType >& object_sets, const SpatialDomain< ObjectType::dim >& domain ) { - auto set_ids = - object_sets.get_existing_set_uuids( cfg.object_set_names ); + auto set_ids = object_sets.get_set_uuids( cfg.object_set_names ); + + auto object_feature = + build_single_object_feature< ObjectType >( cfg.object_feature ); - return std::make_unique< geode::DensityTerm< ObjectType > >( - cfg.term_name, cfg.lambda, set_ids, domain ); + return std::make_unique< geode::SingleObjectTerm< ObjectType > >( + cfg.term_name, cfg.lambda, std::move( set_ids ), domain, + std::move( object_feature ) ); } + std::pair< std::vector< geode::uuid >, + absl::flat_hash_map< uuid, std::vector< uuid > > > + pairwise_builder_initialize_interactions_helper( + const std::vector< std::pair< uuid, uuid > >& interacting_sets ) + { + std::vector< geode::uuid > interacting_set_ids; + absl::flat_hash_map< uuid, std::vector< uuid > > + objectset_adjacency_map; + interacting_set_ids.reserve( 2 * interacting_sets.size() ); + objectset_adjacency_map.reserve( 2 * interacting_sets.size() ); + for( const auto& [set1, set2] : interacting_sets ) + { + objectset_adjacency_map[set1].push_back( set2 ); + objectset_adjacency_map[set2].push_back( set1 ); + + interacting_set_ids.push_back( set1 ); + interacting_set_ids.push_back( set2 ); + } + + std::sort( interacting_set_ids.begin(), interacting_set_ids.end() ); + interacting_set_ids.erase( std::unique( interacting_set_ids.begin(), + interacting_set_ids.end() ), + interacting_set_ids.end() ); + interacting_set_ids.shrink_to_fit(); + + for( auto& [_, adjacent_set_uuids] : objectset_adjacency_map ) + { + geode_unused( _ ); + std::sort( adjacent_set_uuids.begin(), adjacent_set_uuids.end() ); + adjacent_set_uuids.erase( std::unique( adjacent_set_uuids.begin(), + adjacent_set_uuids.end() ), + adjacent_set_uuids.end() ); + adjacent_set_uuids.shrink_to_fit(); + } + return std::make_pair( interacting_set_ids, objectset_adjacency_map ); + } template < typename ObjectType > std::unique_ptr< EnergyTerm< ObjectType > > build_pairwise_term( const PairwiseTermConfig& cfg, const ObjectSets< ObjectType >& object_sets, const SpatialDomain< ObjectType::dim >& domain ) { - auto set_ids = - object_sets.get_existing_set_uuids( cfg.object_set_names ); + auto set_id_interactions = + object_sets.get_set_uuid_pairs( cfg.object_set_names_interactions ); + + auto [interacting_set_ids, adjacent_set_uuids] = + pairwise_builder_initialize_interactions_helper( + set_id_interactions ); auto interaction = - std::make_unique< geode::MinimalDistanceCutoff< ObjectType > >( 0.5 - /*,interaction_desc.interaction_scope*/ ); + build_pairwise_interaction< ObjectType >( cfg.interaction_config ); - return std::make_unique< geode::PairwiseTerm< geode::Point2D > >( - cfg.term_name, cfg.gamma, set_ids, std::move( interaction ), - domain ); + return std::make_unique< geode::PairwiseTerm< ObjectType > >( + cfg.term_name, cfg.gamma, std::move( interacting_set_ids ), domain, + std::move( adjacent_set_uuids ), std::move( interaction ) ); } template < typename ObjectType > - std::unique_ptr< EnergyTerm< ObjectType > > build_term( - const EnergyTermConfig& cfg, + std::unique_ptr< EnergyTerm< ObjectType > > build_energy_term_impl( + const SingleObjectTermConfig& cfg, const ObjectSets< ObjectType >& object_sets, const SpatialDomain< ObjectType::dim >& domain ) { - return std::visit( - [&]( auto&& c ) -> std::unique_ptr< EnergyTerm< ObjectType > > { - using T = std::decay_t< decltype( c ) >; + return build_single_term< ObjectType >( cfg, object_sets, domain ); + } - if constexpr( std::is_same_v< T, DensityTermConfig > ) - return build_density_term< ObjectType >( - c, object_sets, domain ); + template < typename ObjectType > + std::unique_ptr< EnergyTerm< ObjectType > > build_energy_term_impl( + const PairwiseTermConfig& cfg, + const ObjectSets< ObjectType >& object_sets, + const SpatialDomain< ObjectType::dim >& domain ) + { + return build_pairwise_term< ObjectType >( cfg, object_sets, domain ); + } - else if constexpr( std::is_same_v< T, PairwiseTermConfig > ) - return build_pairwise_term< ObjectType >( - c, object_sets, domain ); + template < typename ObjectType, typename NewEnergyTypeConfig > + std::unique_ptr< EnergyTerm< ObjectType > > build_energy_term_impl( + const NewEnergyTypeConfig&, + const ObjectSets< ObjectType >&, + const SpatialDomain< ObjectType::dim >& ) + { + static_assert( sizeof( NewEnergyTypeConfig ) == 0, + "Unsupported EnergyTermConfig type" ); + return nullptr; + } + template < typename ObjectType > + std::unique_ptr< EnergyTerm< ObjectType > > build_energy_term_impl( + const std::monostate&, + const ObjectSets< ObjectType >&, + const SpatialDomain< ObjectType::dim >& ) + { + throw OpenGeodeException( + "[EnergyTermBuilder] energy term config not initialized" ); + } + template < typename ObjectType > + std::unique_ptr< EnergyTerm< ObjectType > > build_energy_term( + const EnergyTermConfig& cfg, + const ObjectSets< ObjectType >& object_sets, + const SpatialDomain< ObjectType::dim >& domain ) + { + return std::visit( + [&]( const auto& term_cfg ) + -> std::unique_ptr< EnergyTerm< ObjectType > > { + return build_energy_term_impl< ObjectType >( + term_cfg, object_sets, domain ); }, cfg ); } diff --git a/include/geode/stochastic/models/energy_terms/energy_term_config.hpp b/include/geode/stochastic/models/energy_terms/energy_term_config.hpp index 0194b77..da7b3dc 100644 --- a/include/geode/stochastic/models/energy_terms/energy_term_config.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term_config.hpp @@ -27,24 +27,30 @@ #include #include +#include namespace geode { - struct DensityTermConfig + struct SingleObjectTermConfig { std::string term_name; std::vector< std::string > object_set_names; double lambda; + + SingleObjectFeatureConfig object_feature; }; struct PairwiseTermConfig { std::string term_name; - std::vector< std::string > object_set_names; + std::vector< std::pair< std::string, std::string > > + object_set_names_interactions; double gamma; + + PairwiseInteractionConfig interaction_config; }; using EnergyTermConfig = - std::variant< DensityTermConfig, PairwiseTermConfig >; + std::variant; } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/models/energy_terms/intensity_term.hpp b/include/geode/stochastic/models/energy_terms/intensity_term.hpp index bc63a75..f6edc37 100644 --- a/include/geode/stochastic/models/energy_terms/intensity_term.hpp +++ b/include/geode/stochastic/models/energy_terms/intensity_term.hpp @@ -30,14 +30,14 @@ namespace geode { FORWARD_DECLARATION_DIMENSION_CLASS( OwnerSegment ); ALIAS_2D( OwnerSegment ); + FORWARD_DECLARATION_DIMENSION_CLASS( SpatialDomain ); + } // namespace geode namespace geode { class opengeode_stochastic_stochastic_api IntensityTerm - : public SingleObjectTerm< OwnerSegment2D, - std::function< double( const OwnerSegment2D&, - const SpatialDomain< OwnerSegment2D::dim >& ) > > + : public SingleObjectTerm< OwnerSegment2D > { public: explicit IntensityTerm( std::string_view name, diff --git a/include/geode/stochastic/models/energy_terms/pairwise_term.hpp b/include/geode/stochastic/models/energy_terms/pairwise_term.hpp index ce5e897..747ab99 100644 --- a/include/geode/stochastic/models/energy_terms/pairwise_term.hpp +++ b/include/geode/stochastic/models/energy_terms/pairwise_term.hpp @@ -22,6 +22,8 @@ */ #pragma once +#include + #include #include #include @@ -30,18 +32,20 @@ namespace geode { - // rename PairwiseInteractionTerm template < typename ObjectType > class PairwiseTerm : public EnergyTerm< ObjectType > { public: explicit PairwiseTerm( std::string_view name, double gamma, - std::vector< uuid > targeted_set_ids, - std::unique_ptr< PairwiseInteraction< ObjectType > > interaction, - const SpatialDomain< ObjectType::dim >& domain ) + std::vector< uuid >&& impacted_set_ids, + const SpatialDomain< ObjectType::dim >& domain, + absl::flat_hash_map< uuid, std::vector< uuid > >&& + objectset_adjacency_map, + std::unique_ptr< PairwiseInteraction< ObjectType > > interaction ) : EnergyTerm< ObjectType >( - name, gamma, std::move( targeted_set_ids ), domain ), + name, gamma, std::move( impacted_set_ids ), domain ), + objectset_adjacency_map_( std::move( objectset_adjacency_map ) ), interaction_( std::move( interaction ) ) { } @@ -57,30 +61,12 @@ namespace geode const ObjectSets< ObjectType >& state, const ObjectRef< ObjectType >& new_object ) const final { - if( !this->is_targeted_set( new_object.set_id ) ) + if( !this->is_impacted_set( new_object.set_id ) ) { return 0.0; } - double delta = 0.0; - const auto neighbors = - state.neighbors( new_object.object, this->targeted_set_ids(), - interaction_->neighborhood_searching_distance() ); - for( const auto& neigh_id : neighbors ) - { - geode::ObjectRef< ObjectType > neigh_object{ - state.get_object( neigh_id ), neigh_id.set_id - }; - // is that really the good test? - // intersect? - if( SpatialDomainChecker< ObjectType >::is_anchored_in_domain( - this->domain(), new_object.object ) - || SpatialDomainChecker< - ObjectType >::is_anchored_in_domain( this->domain(), - neigh_object.object ) ) - { - delta += interaction_->evaluate( new_object, neigh_object ); - } - } + auto delta = compute_local_interactions_with_neighbors( + new_object, std::nullopt, state ); return this->contribution( delta ); } @@ -88,32 +74,14 @@ namespace geode const ObjectSets< ObjectType >& state, const ObjectId& object_id ) const override { - if( !this->is_targeted_set( object_id.set_id ) ) + if( !this->is_impacted_set( object_id.set_id ) ) { return 0.0; } - double delta = 0.0; - ObjectRef< ObjectType > object_to_remove{ - state.get_object( object_id ), object_id.set_id - }; - const auto neighbors = - state.neighbors( object_id, this->targeted_set_ids(), - interaction_->neighborhood_searching_distance() ); - for( auto neigh_id : neighbors ) - { - ObjectRef< ObjectType > neigh_object{ - state.get_object( neigh_id ), neigh_id.set_id - }; - if( SpatialDomainChecker< ObjectType >::is_anchored_in_domain( - this->domain(), object_to_remove.object ) - || SpatialDomainChecker< - ObjectType >::is_anchored_in_domain( this->domain(), - neigh_object.object ) ) - { - delta += interaction_->evaluate( - object_to_remove, neigh_object ); - } - } + ObjectRef< ObjectType > old_object{ state.get_object( object_id ), + object_id.set_id }; + auto delta = compute_local_interactions_with_neighbors( + old_object, object_id, state ); return this->contribution( -delta ); } @@ -122,58 +90,14 @@ namespace geode const ObjectId& old_object_id, const ObjectRef< ObjectType >& new_object ) const override { - if( !this->is_targeted_set( old_object_id.set_id ) - || !this->is_targeted_set( new_object.set_id ) ) - { - return 0.0; - } - double delta = 0.0; - - // Remove old object's interactions - ObjectRef< ObjectType > object_to_remove{ + auto delta_new = compute_local_interactions_with_neighbors( + new_object, old_object_id, state ); + ObjectRef< ObjectType > old_object{ state.get_object( old_object_id ), old_object_id.set_id }; - const auto old_neighbors = - state.neighbors( old_object_id, this->targeted_set_ids(), - interaction_->neighborhood_searching_distance() ); - for( auto neigh_id : old_neighbors ) - { - ObjectRef< ObjectType > neigh_object{ - state.get_object( neigh_id ), neigh_id.set_id - }; - if( SpatialDomainChecker< ObjectType >::is_anchored_in_domain( - this->domain(), object_to_remove.object ) - || SpatialDomainChecker< - ObjectType >::is_anchored_in_domain( this->domain(), - neigh_object.object ) ) - { - delta -= interaction_->evaluate( - object_to_remove, neigh_object ); - } - } - - // Add new object's interactions - const auto new_neighbors = - state.neighbors( new_object.object, this->targeted_set_ids(), - interaction_->neighborhood_searching_distance() ); - for( auto neigh_id : new_neighbors ) - { - if( old_object_id == neigh_id ) - { - continue; // avoid double-counting - } - ObjectRef< ObjectType > neigh_object{ - state.get_object( neigh_id ), neigh_id.set_id - }; - if( SpatialDomainChecker< ObjectType >::is_anchored_in_domain( - this->domain(), new_object.object ) - || SpatialDomainChecker< - ObjectType >::is_anchored_in_domain( this->domain(), - neigh_object.object ) ) - { - delta += interaction_->evaluate( new_object, neigh_object ); - } - } + auto delta_old = compute_local_interactions_with_neighbors( + old_object, old_object_id, state ); + auto delta = delta_new - delta_old; return this->contribution( delta ); } @@ -181,57 +105,82 @@ namespace geode const ObjectSets< ObjectType >& state ) const override { double sum = 0.0; - this->for_each_targeted_object( state, [&]( const ObjectId& - obj_id ) { - const auto& cur_obj = state.get_object( obj_id ); - if( !SpatialDomainChecker< ObjectType >::is_anchored_in_domain( - this->domain(), cur_obj ) ) - { - return; - } - ObjectRef< ObjectType > object{ cur_obj, obj_id.set_id }; - const auto neighbors = - state.neighbors( obj_id, this->targeted_set_ids(), - interaction_->neighborhood_searching_distance() ); - for( const auto& neigh_obj_id : neighbors ) - { - if( !is_new_pair( cur_obj, obj_id, neigh_obj_id ) ) - { - continue; - } - ObjectRef< ObjectType > neigh_object{ - state.get_object( neigh_obj_id ), neigh_obj_id.set_id - }; - - sum += interaction_->evaluate( object, neigh_object ); - } - } ); + this->for_each_object_in_sets( state, this->impacted_set_ids(), + [&]( const ObjectId& cur_obj_id ) { + sum += accumulate_interactions_with_neighbors( + cur_obj_id, state ); + } ); return sum; } private: - [[nodiscard]] bool is_new_pair( const ObjectType& obj, - const ObjectId& obj_id, - const ObjectId& neigh_obj_id ) const + double compute_local_interactions_with_neighbors( + const ObjectRef< ObjectType >& object_ref, + std::optional< ObjectId > exclude_id, + const ObjectSets< ObjectType >& state ) const { - if( !SpatialDomainChecker< ObjectType >::is_anchored_in_domain( - this->domain(), obj ) ) + double sum = 0.0; + const auto neighbors = state.neighbors( object_ref.object, + objectset_adjacency_map_.at( object_ref.set_id ), + interaction_->neighborhood_searching_distance(), exclude_id ); + for( const auto& neigh_id : neighbors ) { - return true; + ObjectRef< ObjectType > neigh_object{ + state.get_object( neigh_id ), neigh_id.set_id + }; + if( !is_any_in_domain( + object_ref.object, neigh_object.object ) ) + { + continue; + } + sum += interaction_->evaluate( object_ref, neigh_object ); } - if( neigh_obj_id.set_id < obj_id.set_id ) + return sum; + } + + double accumulate_interactions_with_neighbors( + const ObjectId& object_id, + const ObjectSets< ObjectType >& state ) const + { + const auto& cur_obj = state.get_object( object_id ); + if( !is_in_domain( cur_obj ) ) { - return false; + return 0.; } - if( neigh_obj_id.set_id == obj_id.set_id - && neigh_obj_id.index <= obj_id.index ) + double sum = 0.0; + const auto neighbors = state.neighbors( cur_obj, + objectset_adjacency_map_.at( object_id.set_id ), + interaction_->neighborhood_searching_distance(), object_id ); + ObjectRef< ObjectType > object_ref{ cur_obj, object_id.set_id }; + for( const auto& neigh_id : neighbors ) { - return false; + const auto& neigh_obj = state.get_object( neigh_id ); + if( neigh_id < object_id && is_in_domain( neigh_obj ) ) + { + continue; + } + ObjectRef< ObjectType > neigh_object{ neigh_obj, + neigh_id.set_id }; + sum += interaction_->evaluate( object_ref, neigh_object ); } - return true; + return sum; + } + + bool is_any_in_domain( + const ObjectType& object1, const ObjectType& object2 ) const + { + return is_in_domain( object1 ) || is_in_domain( object2 ); + } + + bool is_in_domain( const ObjectType& object ) const + { + return SpatialDomainChecker< ObjectType >::is_anchored_in_domain( + this->domain(), object ); } private: + absl::flat_hash_map< uuid, std::vector< uuid > > + objectset_adjacency_map_; std::unique_ptr< PairwiseInteraction< ObjectType > > interaction_; }; } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/models/energy_terms/single_object_term.hpp b/include/geode/stochastic/models/energy_terms/single_object_term.hpp index e9bf6f8..9c3c5e6 100644 --- a/include/geode/stochastic/models/energy_terms/single_object_term.hpp +++ b/include/geode/stochastic/models/energy_terms/single_object_term.hpp @@ -25,58 +25,54 @@ #include #include +#include #include namespace geode { - template < typename ObjectType, typename ObjectContributionFunc > + template < typename ObjectType > class SingleObjectTerm : public EnergyTerm< ObjectType > { public: explicit SingleObjectTerm( std::string_view name, double lambda, - std::vector< uuid > targeted_set_ids, - double scale, - ObjectContributionFunc contribution_func, - const SpatialDomain< ObjectType::dim >& domain ) + std::vector< uuid >&& impacted_set_ids, + const SpatialDomain< ObjectType::dim >& domain, + std::unique_ptr< SingleObjectFeature< ObjectType > > feature ) : EnergyTerm< ObjectType >( - name, lambda, std::move( targeted_set_ids ), domain ), - scale_( scale ), - contribution_func_( std::move( contribution_func ) ) + name, lambda, std::move( impacted_set_ids ), domain ), + feature_( std::move( feature ) ) { } [[nodiscard]] double total_log( const ObjectSets< ObjectType >& state ) const override { - return this->contribution( scale_ * statistic( state ) ); + return this->contribution( statistic( state ) ); } [[nodiscard]] double delta_log_add( const ObjectSets< ObjectType >& /*state*/, const ObjectRef< ObjectType >& new_object ) const override { - if( !this->is_targeted_set( new_object.set_id ) ) + if( !this->is_impacted_set( new_object.set_id ) ) { return 0.0; } return this->contribution( - scale_ - * contribution_func_( new_object.object, this->domain() ) ); + feature_->evaluate( new_object.object, this->domain() ) ); } [[nodiscard]] double delta_log_remove( const ObjectSets< ObjectType >& state, const ObjectId& object_id ) const override { - if( !this->is_targeted_set( object_id.set_id ) ) + if( !this->is_impacted_set( object_id.set_id ) ) { return 0.0; } - return this->contribution( - -scale_ - * contribution_func_( - state.get_object( object_id ), this->domain() ) ); + return this->contribution( -feature_->evaluate( + state.get_object( object_id ), this->domain() ) ); } [[nodiscard]] double delta_log_change( @@ -84,27 +80,31 @@ namespace geode const ObjectId& old_object_id, const ObjectRef< ObjectType >& new_object ) const override { + if( !this->is_impacted_set( new_object.set_id ) ) + { + return 0.0; + } double delta = - contribution_func_( new_object.object, this->domain() ) - - contribution_func_( + feature_->evaluate( new_object.object, this->domain() ) + - feature_->evaluate( state.get_object( old_object_id ), this->domain() ); - return this->contribution( scale_ * delta ); + return this->contribution( delta ); } [[nodiscard]] double statistic( const ObjectSets< ObjectType >& state ) const override { double sum = 0.0; - this->for_each_targeted_object( - state, [&]( const ObjectId& obj_id ) { + this->for_each_object_in_sets( + state, this->impacted_set_ids(), [&]( const ObjectId& obj_id ) { + DEBUG( sum ); const auto& obj = state.get_object( obj_id ); - sum += contribution_func_( obj, this->domain() ); + sum += feature_->evaluate( obj, this->domain() ); } ); return sum; } private: - double scale_{ 1. }; - ObjectContributionFunc contribution_func_; + std::unique_ptr< SingleObjectFeature< ObjectType > > feature_; }; } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/models/gibbs_energy.hpp b/include/geode/stochastic/models/gibbs_energy.hpp index 70352df..e626878 100644 --- a/include/geode/stochastic/models/gibbs_energy.hpp +++ b/include/geode/stochastic/models/gibbs_energy.hpp @@ -41,21 +41,8 @@ namespace geode const ObjectSets< ObjectType >& state ) const { double log_energy = 0.0; - const auto& energy_terms = energy_terms_collection_.all_terms(); - for( auto& [term_id, term] : energy_terms ) - { - geode_unused( term_id ); - log_energy += term->total_log( state ); - } - return log_energy; - } - - [[nodiscard]] 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_set( set_id ) ) + const auto& energy_terms = energy_terms_collection_.energy_terms(); + for( const auto& term : energy_terms ) { log_energy += term->total_log( state ); } @@ -67,8 +54,7 @@ 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 ) ) + for( const auto& term : energy_terms_collection_.energy_terms() ) { log_energy += term->delta_log_add( state, new_object ); } @@ -80,8 +66,7 @@ namespace geode const ObjectId& object_id ) const { double log_energy = 0.0; - for( const auto& term : - energy_terms_collection_.terms_for_set( object_id.set_id ) ) + for( const auto& term : energy_terms_collection_.energy_terms() ) { log_energy += term->delta_log_remove( state, object_id ); } @@ -94,8 +79,7 @@ namespace geode const ObjectRef< ObjectType >& new_object ) const { double log_energy = 0.0; - for( const auto& term : - energy_terms_collection_.terms_for_set( old_id.set_id ) ) + for( const auto& term : energy_terms_collection_.energy_terms() ) { log_energy += term->delta_log_change( state, old_id, new_object ); diff --git a/include/geode/stochastic/models/model_configuration.hpp b/include/geode/stochastic/models/model_configuration.hpp index 9221eb7..069ea7b 100644 --- a/include/geode/stochastic/models/model_configuration.hpp +++ b/include/geode/stochastic/models/model_configuration.hpp @@ -56,7 +56,7 @@ namespace geode for( const auto& term_cfg : config.terms ) { auto term_id = collection.add_energy_term( - build_term< ObjectType >( term_cfg, object_sets, domain ) ); + build_energy_term< ObjectType >( term_cfg, object_sets, domain ) ); } return collection; diff --git a/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp index 1ed63f1..929aa61 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp @@ -39,234 +39,247 @@ namespace geode { - struct FractureSetDescription - { - std::string name; - - DoubleSampler::DistributionDescription length; - DoubleSampler::DistributionDescription azimuth; - - // positionning - double p20; - std::vector< std::array< geode::Point2D, 2 > > observed_fractures; - double p21{ 1. }; - double minimal_spacing{ 0. }; - - // mh dynamique - double birth_ratio{ 1.0 }; - double death_ratio{ 1.0 }; - double change_ratio{ 1.0 }; - - std::string string() const - { - auto message = absl::StrCat( "FractureSetDescription: ", name ); - for( const auto& fixed_object : observed_fractures ) - { - absl::StrAppend( &message, - "\n\t --> observation (x,y,z)start: ", - fixed_object[0].string(), - " (x,y,z)end: ", fixed_object[1].string() ); - } - absl::StrAppend( - &message, "\n\t --> length distribution: ", length.string() ); - absl::StrAppend( - &message, "\n\t --> azimuth distribution: ", azimuth.string() ); - absl::StrAppend( &message, "\n\t --> targeted p20: ", p20 ); - absl::StrAppend( - &message, "\n\t --> minimal spacing: ", minimal_spacing ); - absl::StrAppend( &message, - "\n\t --> MH move ratio - birth/death/change (", birth_ratio, - " / ", death_ratio, " / ", change_ratio, ")" ); - return message; - } - }; - - class FractureSimulationRunner : public SimulationRunner< OwnerSegment2D > - { - public: - FractureSimulationRunner( const SpatialDomain< 2 >& domain ) - : SimulationRunner< OwnerSegment2D >( domain ) - { - } - - void add_x_node_monitoring( double beta_x_node ) - { - OPENGEODE_EXCEPTION( beta_x_node <= 1.0 && beta_x_node >= 0., - "[FractureSimulationRunner] x node should be inhibitated, " - "please provise a value in [0., 1.]." ); - beta_x_node_ = beta_x_node; - } - void add_fracture_set_descriptor( - const FractureSetDescription& descriptor ) - { - set_descriptors_.push_back( descriptor ); - } - - void initialize() override - { - auto proposal_kernel = - std::make_unique< ProposalKernel< OwnerSegment2D > >(); - - // Mapping set names -> UUID - // std::unordered_map< std::string, uuid > - // set_name_to_uuid_; - - // Step 1: create object sets and samplers - for( const auto& set_desc : set_descriptors_ ) - { - const auto set_id = this->object_sets_.add_set( set_desc.name ); - for( const auto& fixed_object : set_desc.observed_fractures ) - { - this->object_sets_.add_object( - geode::OwnerSegment2D{ - fixed_object[0], fixed_object[1] }, - set_id, true ); - } - set_name_to_uuid_[set_desc.name] = set_id; - - auto length_distribution = - DoubleSampler::create_distribution( set_desc.length ); - auto azimuth_distribution = - DoubleSampler::create_rad_angle_distribution_from_degree( - set_desc.azimuth ); - this->set_samplers_.push_back( - std::make_unique< UniformSegmentSetSampler >( - domain_, length_distribution, azimuth_distribution ) ); - - add_birth_death_change_moves( this->set_samplers_.back(), - *proposal_kernel, set_id, set_desc.birth_ratio, - set_desc.death_ratio, set_desc.change_ratio ); - } - - // Step 2: create density energy terms - for( const auto& set_desc : set_descriptors_ ) - { - set_density_term( set_desc ); - set_intensity_term( set_desc ); - set_minimal_spacing_term( set_desc ); - } - set_x_intersection_term(); - - this->mh_sampler_ = - std::make_unique< MetropolisHastings< OwnerSegment2D > >( - this->energy_terms_collection_, - std::move( proposal_kernel ) ); - } - - void check_statistics( - const StatisticsMonitor& statistic_monitoring ) const - { - const auto& computed_means = statistic_monitoring.means(); - - for( const auto stat_id : - Range{ this->energy_terms_collection_.size() } ) - { - const auto& term = energy_terms_collection_.get( - ordered_energy_terms_[stat_id] ); - Logger::info( "[MH test] Statistic value ", - computed_means[stat_id], " for energy term: ", - term.name().value_or( term.id().string() ) ); - } - } - - std::string string() const - { - auto message = - absl::StrCat( "Fracture Simulation Runner description" ); - for( const auto& desc : set_descriptors_ ) - { - absl::StrAppend( &message, "\n\t ", desc.string() ); - } - if( std::fabs( beta_x_node_ - 1. ) > GLOBAL_EPSILON ) - { - absl::StrAppend( &message, - "\n\t --> x node monitioring (beta inhibition value): ", - beta_x_node_ ); - } - else - { - absl::StrAppend( - &message, "\n\t --> x node monitioring : no inhibition." ); - } - return message; - } - - private: - void set_density_term( const FractureSetDescription& set_desc ) - { - const auto set_id = set_name_to_uuid_.at( set_desc.name ); - this->ordered_energy_terms_.push_back( - this->energy_terms_collection_.add_energy_term( - std::make_unique< DensityTerm< OwnerSegment2D > >( - absl::StrCat( set_desc.name, "_density" ), set_desc.p20, - std::vector< uuid >{ set_id }, this->domain_ ) ) ); - } - - void set_intensity_term( const FractureSetDescription& set_desc ) - { - if( std::fabs( set_desc.p21 - 1. ) < GLOBAL_EPSILON ) - { - return; - } - const auto set_id = set_name_to_uuid_.at( set_desc.name ); - this->ordered_energy_terms_.push_back( - this->energy_terms_collection_.add_energy_term( - std::make_unique< IntensityTerm >( - absl::StrCat( set_desc.name, "_intensity" ), - set_desc.p21, std::vector< uuid >{ set_id }, - 0.5 * this->domain_.smallest_length(), - this->domain_ ) ) ); - } - - void set_minimal_spacing_term( const FractureSetDescription& set_desc ) - { - if( set_desc.minimal_spacing < GLOBAL_EPSILON ) - { - return; - } - const auto set_id = set_name_to_uuid_.at( set_desc.name ); - auto interaction = - std::make_unique< MinimalDistanceCutoff< OwnerSegment2D > >( - set_desc.minimal_spacing, - PairwiseInteraction< OwnerSegment2D >::SCOPE::same_set ); - - this->ordered_energy_terms_.push_back( - this->energy_terms_collection_.add_energy_term( - std::make_unique< PairwiseTerm< OwnerSegment2D > >( - absl::StrCat( set_desc.name, "_min_spacing" ), 0., - std::vector< uuid >{ set_id }, std::move( interaction ), - this->domain_ ) ) ); - } - - void set_x_intersection_term() - { - if( std::fabs( beta_x_node_ - 1. ) > GLOBAL_EPSILON ) - { - std::vector< uuid > set_uuids; - set_uuids.reserve( set_name_to_uuid_.size() ); - for( const auto& [name, id] : set_name_to_uuid_ ) - { - set_uuids.push_back( id ); - } - auto interaction = - std::make_unique< MinimalDistanceCutoff< OwnerSegment2D > >( - 0., PairwiseInteraction< - OwnerSegment2D >::SCOPE::different_set ); - - this->ordered_energy_terms_.push_back( - this->energy_terms_collection_.add_energy_term( - std::make_unique< PairwiseTerm< OwnerSegment2D > >( - absl::StrCat( "inter_set_x_nodes" ), beta_x_node_, - set_uuids, std::move( interaction ), - this->domain_ ) ) ); - } - } - - private: - std::vector< FractureSetDescription > set_descriptors_; - std::unordered_map< std::string, uuid > set_name_to_uuid_; - // x node monitoring - double beta_x_node_{ 1. }; - }; - + // struct FractureSetDescription + // { + // std::string name; + // + // DoubleSampler::DistributionDescription length; + // DoubleSampler::DistributionDescription azimuth; + // + // // positionning + // double p20; + // std::vector< std::array< geode::Point2D, 2 > > observed_fractures; + // double p21{ 1. }; + // double minimal_spacing{ 0. }; + // + // // mh dynamique + // double birth_ratio{ 1.0 }; + // double death_ratio{ 1.0 }; + // double change_ratio{ 1.0 }; + // + // std::string string() const + // { + // auto message = absl::StrCat( "FractureSetDescription: ", name + // ); for( const auto& fixed_object : observed_fractures ) + // { + // absl::StrAppend( &message, + // "\n\t --> observation (x,y,z)start: ", + // fixed_object[0].string(), + // " (x,y,z)end: ", fixed_object[1].string() ); + // } + // absl::StrAppend( + // &message, "\n\t --> length distribution: ", + // length.string() ); + // absl::StrAppend( + // &message, "\n\t --> azimuth distribution: ", + // azimuth.string() ); + // absl::StrAppend( &message, "\n\t --> targeted p20: ", p20 ); + // absl::StrAppend( + // &message, "\n\t --> minimal spacing: ", minimal_spacing ); + // absl::StrAppend( &message, + // "\n\t --> MH move ratio - birth/death/change (", + // birth_ratio, " / ", death_ratio, " / ", change_ratio, ")" + // ); + // return message; + // } + // }; + // + // class FractureSimulationRunner : public SimulationRunner< + // OwnerSegment2D > + // { + // public: + // FractureSimulationRunner( const SpatialDomain< 2 >& domain ) + // : SimulationRunner< OwnerSegment2D >( domain ) + // { + // } + // + // void add_x_node_monitoring( double beta_x_node ) + // { + // OPENGEODE_EXCEPTION( beta_x_node <= 1.0 && beta_x_node >= 0., + // "[FractureSimulationRunner] x node should be inhibitated, + // " "please provise a value in [0., 1.]." ); + // beta_x_node_ = beta_x_node; + // } + // void add_fracture_set_descriptor( + // const FractureSetDescription& descriptor ) + // { + // set_descriptors_.push_back( descriptor ); + // } + // + // void initialize() override + // { + // auto proposal_kernel = + // std::make_unique< ProposalKernel< OwnerSegment2D > >(); + // + // // Mapping set names -> UUID + // // std::unordered_map< std::string, uuid > + // // set_name_to_uuid_; + // + // // Step 1: create object sets and samplers + // for( const auto& set_desc : set_descriptors_ ) + // { + // const auto set_id = this->object_sets_.add_set( + // set_desc.name ); for( const auto& fixed_object : + // set_desc.observed_fractures ) + // { + // this->object_sets_.add_object( + // geode::OwnerSegment2D{ + // fixed_object[0], fixed_object[1] }, + // set_id, true ); + // } + // set_name_to_uuid_[set_desc.name] = set_id; + // + // auto length_distribution = + // DoubleSampler::create_distribution( set_desc.length ); + // auto azimuth_distribution = + // DoubleSampler::create_rad_angle_distribution_from_degree( + // set_desc.azimuth ); + // this->set_samplers_.push_back( + // std::make_unique< UniformSegmentSetSampler >( + // domain_, length_distribution, azimuth_distribution + // ) ); + // + // add_birth_death_change_moves( this->set_samplers_.back(), + // *proposal_kernel, set_id, set_desc.birth_ratio, + // set_desc.death_ratio, set_desc.change_ratio ); + // } + // + // // Step 2: create density energy terms + // for( const auto& set_desc : set_descriptors_ ) + // { + // set_density_term( set_desc ); + // set_intensity_term( set_desc ); + // set_minimal_spacing_term( set_desc ); + // } + // set_x_intersection_term(); + // + // this->mh_sampler_ = + // std::make_unique< MetropolisHastings< OwnerSegment2D > >( + // this->energy_terms_collection_, + // std::move( proposal_kernel ) ); + // } + // + // void check_statistics( + // const StatisticsMonitor& statistic_monitoring ) const + // { + // const auto& computed_means = statistic_monitoring.means(); + // + // for( const auto stat_id : + // Range{ this->energy_terms_collection_.size() } ) + // { + // const auto& term = energy_terms_collection_.get( + // ordered_energy_terms_[stat_id] ); + // Logger::info( "[MH test] Statistic value ", + // computed_means[stat_id], " for energy term: ", + // term.name().value_or( term.id().string() ) ); + // } + // } + // + // std::string string() const + // { + // auto message = + // absl::StrCat( "Fracture Simulation Runner description" ); + // for( const auto& desc : set_descriptors_ ) + // { + // absl::StrAppend( &message, "\n\t ", desc.string() ); + // } + // if( std::fabs( beta_x_node_ - 1. ) > GLOBAL_EPSILON ) + // { + // absl::StrAppend( &message, + // "\n\t --> x node monitioring (beta inhibition value): + // ", beta_x_node_ ); + // } + // else + // { + // absl::StrAppend( + // &message, "\n\t --> x node monitioring : no + // inhibition." ); + // } + // return message; + // } + // + // private: + // void set_density_term( const FractureSetDescription& set_desc ) + // { + // const auto set_id = set_name_to_uuid_.at( set_desc.name ); + // this->ordered_energy_terms_.push_back( + // this->energy_terms_collection_.add_energy_term( + // std::make_unique< DensityTerm< OwnerSegment2D > >( + // absl::StrCat( set_desc.name, "_density" ), + // set_desc.p20, std::vector< uuid >{ set_id }, + // this->domain_ ) ) ); + // } + // + // void set_intensity_term( const FractureSetDescription& set_desc ) + // { + // if( std::fabs( set_desc.p21 - 1. ) < GLOBAL_EPSILON ) + // { + // return; + // } + // const auto set_id = set_name_to_uuid_.at( set_desc.name ); + // this->ordered_energy_terms_.push_back( + // this->energy_terms_collection_.add_energy_term( + // std::make_unique< IntensityTerm >( + // absl::StrCat( set_desc.name, "_intensity" ), + // set_desc.p21, std::vector< uuid >{ set_id }, + // 0.5 * this->domain_.smallest_length(), + // this->domain_ ) ) ); + // } + // + // void set_minimal_spacing_term( const FractureSetDescription& + // set_desc ) + // { + // if( set_desc.minimal_spacing < GLOBAL_EPSILON ) + // { + // return; + // } + // const auto set_id = set_name_to_uuid_.at( set_desc.name ); + // auto interaction = + // std::make_unique< MinimalDistanceCutoff< OwnerSegment2D > + // >( + // set_desc.minimal_spacing, + // PairwiseInteraction< OwnerSegment2D >::SCOPE::same_set + // ); + // + // this->ordered_energy_terms_.push_back( + // this->energy_terms_collection_.add_energy_term( + // std::make_unique< PairwiseTerm< OwnerSegment2D > >( + // absl::StrCat( set_desc.name, "_min_spacing" ), 0., + // std::vector< uuid >{ set_id }, std::move( + // interaction ), this->domain_ ) ) ); + // } + // + // void set_x_intersection_term() + // { + // if( std::fabs( beta_x_node_ - 1. ) > GLOBAL_EPSILON ) + // { + // std::vector< uuid > set_uuids; + // set_uuids.reserve( set_name_to_uuid_.size() ); + // for( const auto& [name, id] : set_name_to_uuid_ ) + // { + // set_uuids.push_back( id ); + // } + // auto interaction = + // std::make_unique< MinimalDistanceCutoff< + // OwnerSegment2D > >( + // 0., PairwiseInteraction< + // OwnerSegment2D >::SCOPE::different_set ); + // + // this->ordered_energy_terms_.push_back( + // this->energy_terms_collection_.add_energy_term( + // std::make_unique< PairwiseTerm< OwnerSegment2D > + // >( + // absl::StrCat( "inter_set_x_nodes" ), + // beta_x_node_, set_uuids, std::move( + // interaction ), this->domain_ ) ) ); + // } + // } + // + // private: + // std::vector< FractureSetDescription > set_descriptors_; + // std::unordered_map< std::string, uuid > set_name_to_uuid_; + // // x node monitoring + // double beta_x_node_{ 1. }; + // }; + // } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/spatial/object_neighborhood.hpp b/include/geode/stochastic/spatial/object_neighborhood.hpp index a3d752f..abbabaa 100644 --- a/include/geode/stochastic/spatial/object_neighborhood.hpp +++ b/include/geode/stochastic/spatial/object_neighborhood.hpp @@ -47,6 +47,10 @@ namespace geode return index != other.index || fixed != other.fixed || set_id != other.set_id; } + bool operator<( const ObjectId& other ) const noexcept + { + return index < other.index && (set_id < other.set_id||set_id == other.set_id); + } }; template < index_t dimension > diff --git a/include/geode/stochastic/spatial/object_sets.hpp b/include/geode/stochastic/spatial/object_sets.hpp index e8b06a6..886f1a1 100644 --- a/include/geode/stochastic/spatial/object_sets.hpp +++ b/include/geode/stochastic/spatial/object_sets.hpp @@ -70,20 +70,19 @@ namespace geode void update_free_object( const ObjectId& object_id, Type&& object ); void remove_free_object( const ObjectId& object_id ); - // Object neighbor search by ObjectId (always excludes self) - std::vector< ObjectId > neighbors( const ObjectId& object_id, - const std::vector< uuid >& targeted_set_ids, - double searching_distance ) const; - // Object neighbor search by arbitrary object (return self if in the - // object_set) std::vector< ObjectId > neighbors( const Type& object, const std::vector< uuid >& targeted_set_ids, - double searching_distance ) const; + double searching_distance, + std::optional< ObjectId > exclude_id ) const; std::string string() const; - std::vector< uuid > get_existing_set_uuids( - const std::vector< std::string > set_names ) const; + uuid get_set_uuid( std::string_view set_name ) const; + std::vector< uuid > get_set_uuids( + const std::vector< std::string >& set_names ) const; + std::vector< std::pair< uuid, uuid > > get_set_uuid_pairs( + const std::vector< std::pair< std::string, std::string > >& + set_name_pairs ) const; private: ObjectSet< Type >& get_set( const uuid& set_id ); diff --git a/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp index 6bb9a49..f637a39 100644 --- a/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp +++ b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp @@ -21,33 +21,56 @@ * */ +#include #include #pragma once namespace geode { template < typename ObjectType > - std::unique_ptr< PairwiseInteraction< ObjectType > > build_interaction( - const PairwiseInteractionConfig& cfg ) + std::unique_ptr< PairwiseInteraction< ObjectType > > + build_pairwise_interaction_impl( + const EuclideanDistanceCutoffConfig& cfg ) + { + return std::make_unique< CenterEuclideanDistanceCutoff< ObjectType > >( + cfg.threshold ); + } + + template < typename ObjectType > + std::unique_ptr< PairwiseInteraction< ObjectType > > + build_pairwise_interaction_impl( + const MinimalDistanceCutoffConfig& cfg ) + { + return std::make_unique< MinimalDistanceCutoff< ObjectType > >( + cfg.threshold ); + } + + template < typename ObjectType, typename NewInteractionConfig > + std::unique_ptr< PairwiseInteraction< ObjectType > > + build_pairwise_interaction_impl( const NewInteractionConfig& ) + { + static_assert( sizeof( NewInteractionConfig ) == 0, + "Unsupported PairwiseInteractionConfig type" ); + return nullptr; + } + + template < typename ObjectType > + std::unique_ptr< PairwiseInteraction< ObjectType > > + build_pairwise_interaction_impl( const std::monostate& ) + { + throw OpenGeodeException( + "[PairWiseInteractionBuilder] interaction config not initialized" ); + } + + template < typename ObjectType > + std::unique_ptr< PairwiseInteraction< ObjectType > > + build_pairwise_interaction( const PairwiseInteractionConfig& cfg ) { return std::visit( - [&]( auto&& c ) + [&]( auto&& interaction_cfg ) -> std::unique_ptr< PairwiseInteraction< ObjectType > > { - using T = std::decay_t< decltype( c ) >; - - if constexpr( std::is_same_v< T, - EuclideanCenterDistanceConfig > ) - { - return std::make_unique< - EuclideanCenterDistance< ObjectType > >( - c.threshold, c.weight ); - } - else if constexpr( std::is_same_v< T, - HausdorffDistanceConfig > ) - { - return std::make_unique< HausdorffDistance< ObjectType > >( - c.threshold, c.weight ); - } + return build_pairwise_interaction_impl< ObjectType >( + interaction_cfg ); }, cfg ); } diff --git a/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_config.hpp b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_config.hpp index 905844b..f3cd615 100644 --- a/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_config.hpp +++ b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_config.hpp @@ -38,34 +38,8 @@ namespace geode double threshold; }; - using PairwiseInteractionConfig = - std::variant< EuclideanDistanceCutoffConfig, - MinimalDistanceCutoffConfig >; + using PairwiseInteractionConfig = std::variant< std::monostate, + EuclideanDistanceCutoffConfig, + MinimalDistanceCutoffConfig >; - template < typename ObjectType > - std::unique_ptr< PairwiseInteraction< ObjectType > > build_interaction( - const PairwiseInteractionConfig& cfg ) - { - return std::visit( - [&]( auto&& c ) - -> std::unique_ptr< PairwiseInteraction< ObjectType > > { - using T = std::decay_t< decltype( c ) >; - - if constexpr( std::is_same_v< T, - EuclideanDistanceCutoffConfig > ) - { - return std::make_unique< - CenterEuclideanDistanceCutoff< ObjectType > >( - c.threshold ); - } - else if constexpr( std::is_same_v< T, - MinimalDistanceCutoffConfig > ) - { - return std::make_unique< - MinimalDistanceCutoff< ObjectType > >( - c.threshold, c.weight ); - } - }, - cfg ); - } } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/spatial/single_object_features/segment_length_feature.hpp b/include/geode/stochastic/spatial/single_object_features/segment_length_feature.hpp new file mode 100644 index 0000000..9cd4c5d --- /dev/null +++ b/include/geode/stochastic/spatial/single_object_features/segment_length_feature.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 - 2026 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 + +namespace geode +{ + class SegmentLengthInsideBoxFeature + : public SingleObjectFeature< OwnerSegment2D > + { + public: + explicit SegmentLengthInsideBoxFeature( double characteristic_length ); + + double evaluate( const OwnerSegment2D& segment, + const SpatialDomain< 2 >& domain ) const override; + + private: + double inv_length_; + }; +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/spatial/single_object_features/single_object_feature.hpp b/include/geode/stochastic/spatial/single_object_features/single_object_feature.hpp new file mode 100644 index 0000000..67fb5fc --- /dev/null +++ b/include/geode/stochastic/spatial/single_object_features/single_object_feature.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 - 2026 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 + +namespace geode +{ + template < typename ObjectType > + class SingleObjectFeature + { + public: + virtual ~SingleObjectFeature() = default; + + virtual double evaluate( const ObjectType& obj, + const SpatialDomain< ObjectType::dim >& domain ) const = 0; + }; + + template < typename ObjectType > + class ObjectInDomainFeature : public SingleObjectFeature< ObjectType > + { + public: + double evaluate( const ObjectType& obj, + const SpatialDomain< ObjectType::dim >& domain ) const override + { + return SpatialDomainChecker< ObjectType >::is_anchored_in_domain( + domain, obj ) + ? 1.0 + : 0.0; + } + }; + +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/spatial/single_object_features/single_object_feature_builder.hpp b/include/geode/stochastic/spatial/single_object_features/single_object_feature_builder.hpp new file mode 100644 index 0000000..78ac6da --- /dev/null +++ b/include/geode/stochastic/spatial/single_object_features/single_object_feature_builder.hpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019 - 2026 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 +#include + +#pragma once +namespace geode +{ + template < typename ObjectType > + std::unique_ptr< SingleObjectFeature< ObjectType > > + build_single_object_feature_impl( + const ObjectInDomainFeatureConfig& cfg ) + { + return std::make_unique< ObjectInDomainFeature< ObjectType > >(); + }; + + template < typename ObjectType > + std::unique_ptr< SingleObjectFeature< ObjectType > > + build_single_object_feature_impl( + const SegmentLengthInsideBoxFeatureConfig& cfg ) + { + if constexpr( std::is_same_v< ObjectType, OwnerSegment2D > ) + { + return std::make_unique< SegmentLengthInsideBoxFeature >( + cfg.characteristic_length ); + } + else + { + throw std::runtime_error( + "SegmentLengthInsideBoxFeature not valid for this " + "ObjectType" ); + } + } + + template < typename ObjectType, typename NewObjectFeatureConfig > + std::unique_ptr< SingleObjectFeature< ObjectType > > + build_single_object_feature_impl( const NewObjectFeatureConfig& ) + { + static_assert( sizeof( NewObjectFeatureConfig ) == 0, + "Unsupported SingleObjectFeatureConfig type" ); + return nullptr; + } + + template < typename ObjectType > + std::unique_ptr< SingleObjectFeature< ObjectType > > + build_single_object_feature_impl( const std::monostate& ) + { + throw OpenGeodeException( "[SingleObjectFeatureBuilder] object feature " + "config not initialized" ); + } + + template < typename ObjectType > + std::unique_ptr< SingleObjectFeature< ObjectType > > + build_single_object_feature( const SingleObjectFeatureConfig& cfg ) + { + return std::visit( + [&]( auto&& interaction_cfg ) + -> std::unique_ptr< SingleObjectFeature< ObjectType > > { + return build_single_object_feature_impl< ObjectType >( + interaction_cfg ); + }, + cfg ); + } + +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/spatial/single_object_features/single_object_feature_config.hpp b/include/geode/stochastic/spatial/single_object_features/single_object_feature_config.hpp new file mode 100644 index 0000000..db440d4 --- /dev/null +++ b/include/geode/stochastic/spatial/single_object_features/single_object_feature_config.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 - 2026 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 + +#pragma once +namespace geode +{ + struct ObjectInDomainFeatureConfig + { + }; + + struct SegmentLengthInsideBoxFeatureConfig + { + double characteristic_length; + }; + + using SingleObjectFeatureConfig = std::variant< std::monostate, + ObjectInDomainFeatureConfig, + SegmentLengthInsideBoxFeatureConfig >; + +} // namespace geode \ No newline at end of file diff --git a/src/geode/stochastic/CMakeLists.txt b/src/geode/stochastic/CMakeLists.txt index cfe03f1..f461300 100644 --- a/src/geode/stochastic/CMakeLists.txt +++ b/src/geode/stochastic/CMakeLists.txt @@ -25,6 +25,7 @@ add_geode_library( "spatial/object_set.cpp" "spatial/object_sets.cpp" "spatial/object_neighborhood.cpp" + "spatial/single_object_features/segment_length_feature.cpp" "spatial/pairwise_interactions/distance_cutoff.cpp" "sampling/direct/ball_sampler.cpp" "sampling/direct/bounding_box_sampler.cpp" @@ -41,6 +42,10 @@ add_geode_library( "spatial/object_sets.hpp" "spatial/object_neighborhood.hpp" "spatial/object_helpers.hpp" + "spatial/single_object_features/single_object_feature_builder.hpp" + "spatial/single_object_features/single_object_feature_config.hpp" + "spatial/single_object_features/segment_length_feature.hpp" + "spatial/single_object_features/single_object_feature.hpp" "spatial/pairwise_interactions/pairwise_interactions_builder.hpp" "spatial/pairwise_interactions/pairwise_interactions_config.hpp" "spatial/pairwise_interactions/pairwise_interactions.hpp" @@ -55,7 +60,7 @@ add_geode_library( "sampling/direct/double_sampler.hpp" "sampling/direct/point_uniform_sampler.hpp" "sampling/direct/segment_uniform_sampler.hpp" - "sampling/mcmc/helpers/fracture_simulation_runner.hpp" + #"sampling/mcmc/helpers/fracture_simulation_runner.hpp" "sampling/mcmc/helpers/simulation_printer.hpp" "sampling/mcmc/helpers/simulation_monitor.hpp" "models/energy_terms/density_term.hpp" diff --git a/src/geode/stochastic/models/energy_terms/intensity_term.cpp b/src/geode/stochastic/models/energy_terms/intensity_term.cpp index 30cb9ec..0d08d45 100644 --- a/src/geode/stochastic/models/energy_terms/intensity_term.cpp +++ b/src/geode/stochastic/models/energy_terms/intensity_term.cpp @@ -25,97 +25,21 @@ #include #include +#include -namespace -{ - double length_inside_box( const geode::Point2D& segment_start, - const geode::Point2D& segment_end, - const geode::BoundingBox2D& box ) - { - // Segment direction - const double dir_x = segment_end.value( 0 ) - segment_start.value( 0 ); - const double dir_y = segment_end.value( 1 ) - segment_start.value( 1 ); - - // Parameter interval where the segment is inside the box - double t_enter = 0.0; // start of valid interval - double t_exit = 1.0; // end of valid interval - - // Clips the parametric segment against one axis interval - auto clip_against_axis_interval = [&t_enter, &t_exit]( double direction, - double min_distance, - double max_distance ) -> bool { - // Segment is parallel to this axis - if( std::fabs( direction ) < geode::GLOBAL_EPSILON ) - { - // Outside the interval → no intersection - return min_distance <= 0.0 && 0.0 <= max_distance; - } - - double axis_min_t = min_distance / direction; - double axis_max_t = max_distance / direction; - - if( axis_min_t > axis_max_t ) - { - std::swap( axis_min_t, axis_max_t ); - } - - t_enter = std::max( t_enter, axis_min_t ); - t_exit = std::min( t_exit, axis_max_t ); - - return t_enter <= t_exit; - }; - - // Clip against X interval of the box - if( !clip_against_axis_interval( dir_x, - box.min().value( 0 ) - segment_start.value( 0 ), - box.max().value( 0 ) - segment_start.value( 0 ) ) ) - { - return 0.0; - } - - // Clip against Y interval of the box - if( !clip_against_axis_interval( dir_y, - box.min().value( 1 ) - segment_start.value( 1 ), - box.max().value( 1 ) - segment_start.value( 1 ) ) ) - { - return 0.0; - } - - // No portion of the segment is inside the box - if( t_exit <= t_enter ) - { - return 0.0; - } - - // Length of the clipped segment - const double clipped_dx = dir_x * ( t_exit - t_enter ); - const double clipped_dy = dir_y * ( t_exit - t_enter ); - - return std::sqrt( - ( clipped_dx * clipped_dx ) + ( clipped_dy * clipped_dy ) ); - } -} // namespace namespace geode { IntensityTerm::IntensityTerm( std::string_view name, double lambda, - std::vector< uuid > targeted_set_ids, + std::vector< uuid > impacted_set_ids, double caracteristic_length, const SpatialDomain< OwnerSegment2D::dim >& domain ) - : SingleObjectTerm< OwnerSegment2D, - std::function< double( const OwnerSegment2D&, - const SpatialDomain< OwnerSegment2D::dim >& ) > >( - name, + : SingleObjectTerm< OwnerSegment2D >( name, lambda, - std::move( targeted_set_ids ), - 1.0 / caracteristic_length, - []( const OwnerSegment2D& segment, - const SpatialDomain< OwnerSegment2D::dim >& spatial_domain ) { - auto seg_extremities = segment.vertices(); - return length_inside_box( seg_extremities[0], - seg_extremities[1], spatial_domain.box() ); - }, - domain ) + std::move( impacted_set_ids ), + domain, + std::make_unique< SegmentLengthInsideBoxFeature >( + caracteristic_length ) ) { } } // namespace geode \ No newline at end of file diff --git a/src/geode/stochastic/spatial/object_sets.cpp b/src/geode/stochastic/spatial/object_sets.cpp index 2127c22..925d921 100644 --- a/src/geode/stochastic/spatial/object_sets.cpp +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -164,27 +164,16 @@ namespace geode set.remove_free_object( object_id.index ); } - template < typename Type > - std::vector< ObjectId > ObjectSets< Type >::neighbors( - const ObjectId& object_id, - const std::vector< uuid >& targeted_set_ids, - double searching_distance ) const - { - auto box = object_bounding_box( get_object( object_id ) ); - box.extends( searching_distance * 2. ); - return neighborhood_.get_all_neighbor_ids( - box, targeted_set_ids, object_id ); - } - template < typename Type > std::vector< ObjectId > ObjectSets< Type >::neighbors( const Type& object, const std::vector< uuid >& targeted_set_ids, - double searching_distance ) const + double searching_distance, + std::optional< ObjectId > excluded_id ) const { auto box = object_bounding_box( object ); box.extends( searching_distance * 2. ); return neighborhood_.get_all_neighbor_ids( - box, targeted_set_ids, std::nullopt ); + box, targeted_set_ids, excluded_id ); } template < typename Type > @@ -195,23 +184,49 @@ namespace geode } template < typename Type > - std::vector< uuid > ObjectSets< Type >::get_existing_set_uuids( - const std::vector< std::string > set_names ) const + uuid ObjectSets< Type >::get_set_uuid( + const std::string_view set_name ) const + { + if( auto set_uuid = object_set_name_to_uuid_.find( set_name ); + set_uuid != object_set_name_to_uuid_.end() ) + { + return set_uuid->second; + } + throw OpenGeodeException( + "[ObjectSets] ObjectSet uuid accessor - group named ", set_name, + " does not exist." ); + } + + template < typename Type > + std::vector< uuid > ObjectSets< Type >::get_set_uuids( + const std::vector< std::string >& set_names ) const { std::vector< geode::uuid > uuids; uuids.reserve( set_names.size() ); for( const auto& name : set_names ) { - if( auto it = object_set_name_to_uuid_.find( name ); - it != object_set_name_to_uuid_.end() ) - { - uuids.push_back( it->second ); - } + uuids.emplace_back( get_set_uuid( name ) ); } return uuids; } + template < typename Type > + std::vector< std::pair< uuid, uuid > > + ObjectSets< Type >::get_set_uuid_pairs( + const std::vector< std::pair< std::string, std::string > >& + set_name_pairs ) const + { + std::vector< std::pair< uuid, uuid > > result; + result.reserve( set_name_pairs.size() ); + + for( const auto& [name1, name2] : set_name_pairs ) + { + result.emplace_back( get_set_uuid( name1 ), get_set_uuid( name2 ) ); + } + return result; + } + template < typename Type > std::string ObjectSets< Type >::string() const { diff --git a/src/geode/stochastic/spatial/single_object_features/segment_length_feature.cpp b/src/geode/stochastic/spatial/single_object_features/segment_length_feature.cpp new file mode 100644 index 0000000..01fddb8 --- /dev/null +++ b/src/geode/stochastic/spatial/single_object_features/segment_length_feature.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2019 - 2026 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 +{ + double length_inside_box( const geode::Point2D& segment_start, + const geode::Point2D& segment_end, + const geode::BoundingBox2D& box ) + { + // Segment direction + const double dir_x = segment_end.value( 0 ) - segment_start.value( 0 ); + const double dir_y = segment_end.value( 1 ) - segment_start.value( 1 ); + + // Parameter interval where the segment is inside the box + double t_enter = 0.0; // start of valid interval + double t_exit = 1.0; // end of valid interval + + // Clips the parametric segment against one axis interval + auto clip_against_axis_interval = [&t_enter, &t_exit]( double direction, + double min_distance, + double max_distance ) -> bool { + // Segment is parallel to this axis + if( std::fabs( direction ) < geode::GLOBAL_EPSILON ) + { + // Outside the interval → no intersection + return min_distance <= 0.0 && 0.0 <= max_distance; + } + + double axis_min_t = min_distance / direction; + double axis_max_t = max_distance / direction; + + if( axis_min_t > axis_max_t ) + { + std::swap( axis_min_t, axis_max_t ); + } + + t_enter = std::max( t_enter, axis_min_t ); + t_exit = std::min( t_exit, axis_max_t ); + + return t_enter <= t_exit; + }; + + // Clip against X interval of the box + if( !clip_against_axis_interval( dir_x, + box.min().value( 0 ) - segment_start.value( 0 ), + box.max().value( 0 ) - segment_start.value( 0 ) ) ) + { + return 0.0; + } + + // Clip against Y interval of the box + if( !clip_against_axis_interval( dir_y, + box.min().value( 1 ) - segment_start.value( 1 ), + box.max().value( 1 ) - segment_start.value( 1 ) ) ) + { + return 0.0; + } + + // No portion of the segment is inside the box + if( t_exit <= t_enter ) + { + return 0.0; + } + + // Length of the clipped segment + const double clipped_dx = dir_x * ( t_exit - t_enter ); + const double clipped_dy = dir_y * ( t_exit - t_enter ); + + return std::sqrt( + ( clipped_dx * clipped_dx ) + ( clipped_dy * clipped_dy ) ); + } +} // namespace + +namespace geode +{ + SegmentLengthInsideBoxFeature::SegmentLengthInsideBoxFeature( + double characteristic_length ) + : inv_length_( 1.0 / characteristic_length ) + { + } + + double SegmentLengthInsideBoxFeature::evaluate( + const OwnerSegment2D& segment, const SpatialDomain< 2 >& domain ) const + { + auto seg_extremities = segment.vertices(); + + const double length = length_inside_box( + seg_extremities[0], seg_extremities[1], domain.box() ); + + return inv_length_ * length; + } +} // namespace geode \ No newline at end of file diff --git a/tests/stochastic/CMakeLists.txt b/tests/stochastic/CMakeLists.txt index 581dc80..3869ab5 100644 --- a/tests/stochastic/CMakeLists.txt +++ b/tests/stochastic/CMakeLists.txt @@ -129,13 +129,13 @@ add_geode_test( ${PROJECT_NAME}::stochastic ) -add_geode_test( - SOURCE "models/energy_terms/test-gibbs-energy.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) +#add_geode_test( +# SOURCE "models/energy_terms/test-gibbs-energy.cpp" +# DEPENDENCIES +# OpenGeode::basic +# OpenGeode::geometry +# ${PROJECT_NAME}::stochastic +#) add_geode_test( SOURCE "sampling/mcmc/proposal/test-proposal-kernel.cpp" diff --git a/tests/stochastic/models/energy_terms/test-density-term.cpp b/tests/stochastic/models/energy_terms/test-density-term.cpp index 0d66c3f..15e7862 100644 --- a/tests/stochastic/models/energy_terms/test-density-term.cpp +++ b/tests/stochastic/models/energy_terms/test-density-term.cpp @@ -53,8 +53,9 @@ void run_density_test( double lambda, const geode::uuid& set_id, const geode::SpatialDomain< 2 >& domain ) { + std::string denity_name{ "density" }; geode::DensityTerm< geode::Point2D > term( - "density", lambda, { set_id }, domain ); + denity_name, lambda, { set_id }, domain ); auto neg_log_lambda = -std::log( lambda ); double expected_add = diff --git a/tests/stochastic/models/energy_terms/test-gibbs-energy.cpp b/tests/stochastic/models/energy_terms/test-gibbs-energy.cpp index 3b3c223..762b9c6 100644 --- a/tests/stochastic/models/energy_terms/test-gibbs-energy.cpp +++ b/tests/stochastic/models/energy_terms/test-gibbs-energy.cpp @@ -59,7 +59,9 @@ void test_gibbs_energy() const auto pwterm_id = energy_terms.add_energy_term( std::make_unique< geode::PairwiseTerm< geode::Point2D > >( - "interaction", 0.8, std::vector< geode::uuid >{ set_id }, + "interaction", 0.8, + std::vector< std::pair< geode::uuid, geode::uuid > >{ + { set_id, set_id } }, std::move( interaction ), domain ) ); geode_unused( pwterm_id ); diff --git a/tests/stochastic/models/energy_terms/test-pairwise-term.cpp b/tests/stochastic/models/energy_terms/test-pairwise-term.cpp index b05c163..26e5137 100644 --- a/tests/stochastic/models/energy_terms/test-pairwise-term.cpp +++ b/tests/stochastic/models/energy_terms/test-pairwise-term.cpp @@ -22,23 +22,28 @@ */ #include +#include +#include #include + #include #include #include #include +const std::string object_set_name = "single_set"; + geode::uuid init_object_set( geode::ObjectSets< geode::Point2D >& pattern ) { geode::Point2D p1{ { 0.1, 0.1 } }; // VOI geode::Point2D p2{ { 0.9, 0.9 } }; // VOI geode::Point2D p3{ { 1.3, 1.3 } }; // buffer - auto set_id = pattern.add_set( "default_name" ); + auto set_id = pattern.add_set( object_set_name ); + pattern.add_object( std::move( p3 ), set_id, false ); pattern.add_object( std::move( p1 ), set_id, false ); pattern.add_object( std::move( p2 ), set_id, false ); - pattern.add_object( std::move( p3 ), set_id, false ); return set_id; } @@ -51,136 +56,133 @@ geode::SpatialDomain< 2 > init_domain() return geode::SpatialDomain< 2 >{ box, 0.5 }; } -void test_pairwise_term( double gamma, +void test_pairwise_term( const geode::PairwiseTermConfig& term_config, const geode::ObjectSets< geode::Point2D >& pattern, - const geode::uuid& set_id, const geode::SpatialDomain< 2 >& domain ) { - auto interaction = - std::make_unique< geode::MinimalDistanceCutoff< geode::Point2D > >( 1 ); - geode::PairwiseTerm< geode::Point2D > term( - "strauss", gamma, { set_id }, std::move( interaction ), domain ); + auto term = geode::build_energy_term< geode::Point2D >( + term_config, pattern, domain ); + auto set_id = pattern.get_set_uuid( object_set_name ); - double neg_log_gamma = -std::log( gamma ); + double neg_log_gamma = -std::log( term_config.gamma ); // --- total_log - double total = term.total_log( pattern ); + double total = term->total_log( pattern ); // Only VOI-anchored interactions counted in statistic: p1-p2 OPENGEODE_EXCEPTION( - total == term.contribution( 1 ), "[PairwiseTerm] total_log wrong" ); + total == term->contribution( 1 ), "[PairwiseTerm] total_log wrong" ); // --- delta_add VOI → VOI geode::Point2D p4{ { 0.5, 0.5 } }; geode::ObjectRef< geode::Point2D > p4_ref{ p4, set_id }; - double delta = term.delta_log_add( pattern, p4_ref ); + double delta = term->delta_log_add( pattern, p4_ref ); // interacts with p1 and p2 → 2 pairs - OPENGEODE_EXCEPTION( delta == term.contribution( 2 ), + OPENGEODE_EXCEPTION( delta == term->contribution( 2 ), "[PairwiseTerm] delta_log_add VOI wrong" ); // --- delta_add buffer → buffer (outside VOI, inside buffer) geode::Point2D p_buffer{ { 1.2, 1.2 } }; geode::ObjectRef< geode::Point2D > buffer_ref{ p_buffer, set_id }; - delta = term.delta_log_add( pattern, buffer_ref ); + delta = term->delta_log_add( pattern, buffer_ref ); // energy counts interactions: buffer interacts with p2,p3 (p3 is in // buffer) → 2 pairs - OPENGEODE_EXCEPTION( delta == term.contribution( 1 ), + OPENGEODE_EXCEPTION( delta == term->contribution( 1 ), "[PairwiseTerm] delta_log_add buffer wrong" ); // --- delta_change VOI → VOI - geode::ObjectId obj_id{ 0, false, set_id }; // p1 - delta = term.delta_log_change( pattern, obj_id, p4_ref ); + geode::ObjectId obj_id{ 1, false, set_id }; // p1 + delta = term->delta_log_change( pattern, obj_id, p4_ref ); // p1 replaced by p4: interacts with p2 → add 1 interaction - OPENGEODE_EXCEPTION( delta == term.contribution( 1 ), + OPENGEODE_EXCEPTION( delta == term->contribution( 1 ), "[PairwiseTerm] delta_log_change VOI->VOI wrong" ); // --- delta_change buffer → VOI - geode::ObjectId old_buffer{ 2, false, set_id }; // p3 - delta = term.delta_log_change( pattern, old_buffer, p4_ref ); + geode::ObjectId old_buffer{ 0, false, set_id }; // p3 + delta = term->delta_log_change( pattern, old_buffer, p4_ref ); // old buffer interactions removed (-p3 pairs (-1)), new VOI interactions // added // (+p4 pairs (+2)) net change = 1 - double expected_delta = term.contribution( 1 ); // adjust based on count + double expected_delta = term->contribution( 1 ); // adjust based on count OPENGEODE_EXCEPTION( delta == expected_delta, "[PairwiseTerm] delta_log_change buffer->VOI wrong" ); // --- delta_change VOI → buffer - delta = term.delta_log_change( pattern, obj_id, buffer_ref ); + delta = term->delta_log_change( pattern, obj_id, buffer_ref ); // p1 → buffer: p1 old interaction = 0 // p4 → p_buffer interaction with p2 +1 - expected_delta = term.contribution( 1 ); + expected_delta = term->contribution( 1 ); OPENGEODE_EXCEPTION( delta == expected_delta, "[PairwiseTerm] delta_log_change VOI->buffer wrong" ); // --- delta_remove VOI - delta = term.delta_log_remove( pattern, obj_id ); + delta = term->delta_log_remove( pattern, obj_id ); // p1 removed: no interaction with p2 removed - OPENGEODE_EXCEPTION( delta == term.contribution( 0 ), + OPENGEODE_EXCEPTION( delta == term->contribution( 0 ), "[PairwiseTerm] delta_log_remove VOI wrong" ); // --- statistic (only anchored objects in VOI) - double stat = term.statistic( pattern ); + double stat = term->statistic( pattern ); // p1,p2 anchored → 1 pair OPENGEODE_EXCEPTION( stat == 1., "[PairwiseTerm] statistic wrong" ); } -void test_pairwise_term_zero_gamma( double gamma, +void test_pairwise_term_zero_gamma( + const geode::PairwiseTermConfig& term_config, const geode::ObjectSets< geode::Point2D >& pattern, - const geode::uuid& set_id, const geode::SpatialDomain< 2 >& domain ) { - auto interaction = - std::make_unique< geode::MinimalDistanceCutoff< geode::Point2D > >( 1 ); - geode::PairwiseTerm< geode::Point2D > term( - "strauss", gamma, { set_id }, std::move( interaction ), domain ); + auto term = geode::build_energy_term< geode::Point2D >( + term_config, pattern, domain ); + auto set_id = pattern.get_set_uuid( object_set_name ); // --- total_log - double total = term.total_log( pattern ); + double total = term->total_log( pattern ); OPENGEODE_EXCEPTION( std::isinf( total ), "[PairwiseTerm] total_log with gamma p4_ref{ p4, set_id }; - double delta = term.delta_log_add( pattern, p4_ref ); + double delta = term->delta_log_add( pattern, p4_ref ); OPENGEODE_EXCEPTION( std::isinf( delta ), "[PairwiseTerm] delta_log_add with gamma buffer_ref{ p_buffer, set_id }; - delta = term.delta_log_add( pattern, buffer_ref ); + delta = term->delta_log_add( pattern, buffer_ref ); OPENGEODE_EXCEPTION( std::isinf( delta ), "[PairwiseTerm] delta_log_add buffer with " "gammadelta_log_change( pattern, obj_id, p4_ref ); OPENGEODE_EXCEPTION( std::isinf( delta ), "[PairwiseTerm] delta_log_change VOI->VOI with " "gammadelta_log_change( pattern, old_buffer, p4_ref ); OPENGEODE_EXCEPTION( std::isinf( delta ), "[PairwiseTerm] delta_log_change buffer->VOI with " "gammadelta_log_change( pattern, obj_id, buffer_ref ); OPENGEODE_EXCEPTION( std::isinf( delta ), "[PairwiseTerm] delta_log_change VOI->buffer with " "gammadelta_log_remove( pattern, obj_id ); DEBUG( delta ); OPENGEODE_EXCEPTION( delta == 0, "[PairwiseTerm] delta_log_remove VOI with " "gammastatistic( pattern ); // p1,p2 anchored → 1 pair OPENGEODE_EXCEPTION( stat == 1., "[PairwiseTerm] statistic wrong" ); } @@ -196,12 +198,24 @@ int main() auto set_id = init_object_set( pattern ); auto domain = init_domain(); - test_pairwise_term( 0.5, pattern, set_id, domain ); - test_pairwise_term( geode::GLOBAL_EPSILON, pattern, set_id, domain ); - test_pairwise_term( 2.0, pattern, set_id, domain ); + geode::MinimalDistanceCutoffConfig interaction_cfg{ 1. }; + + geode::PairwiseTermConfig pw_interaction_cfg; + pw_interaction_cfg.term_name = "strauss"; + pw_interaction_cfg.gamma = 0.5; + pw_interaction_cfg.object_set_names_interactions = { { object_set_name, + object_set_name } }; + pw_interaction_cfg.interaction_config = interaction_cfg; + + test_pairwise_term( pw_interaction_cfg, pattern, domain ); + + pw_interaction_cfg.gamma = geode::GLOBAL_EPSILON; + test_pairwise_term( pw_interaction_cfg, pattern, domain ); + pw_interaction_cfg.gamma = 2.0; + test_pairwise_term( pw_interaction_cfg, pattern, domain ); - test_pairwise_term_zero_gamma( - 0.9999 * geode::GLOBAL_EPSILON, pattern, set_id, domain ); + pw_interaction_cfg.gamma = 0.9999 * geode::GLOBAL_EPSILON; + test_pairwise_term_zero_gamma( pw_interaction_cfg, pattern, domain ); // test_normal_positive_pairwise( 0.5, pattern, set_id ); // test_normal_positive_pairwise( diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp index bd7cc89..fd9b6ff 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp @@ -23,17 +23,7 @@ #include #include - -namespace -{ - struct SetDescription - { - std::string name; - geode::index_t number_of_object; - std::vector< geode::Point2D > fixed_object; - }; -} // namespace - +#include int main() { try @@ -48,21 +38,29 @@ int main() geode::ObjectSets< geode::Point2D > object_sets; const auto set_id1 = object_sets.add_set( set_name1 ); + geode::SingleObjectFeatureConfig density_feature = + geode::ObjectInDomainFeatureConfig{}; const auto set_id2 = object_sets.add_set( set_name2 ); const auto set_id3 = object_sets.add_set( set_name3 ); const auto set_id4 = object_sets.add_set( set_name4 ); geode::ModelConfig config; - config.terms.push_back( - geode::DensityTermConfig{ "density_set1", { set_name1 }, 1.0 } ); - config.terms.push_back( - geode::DensityTermConfig{ "density_set2", { set_name2 }, 1.0 } ); - config.terms.push_back( - geode::DensityTermConfig{ "density_set3", { set_name3 }, 1.0 } ); - config.terms.push_back( - geode::DensityTermConfig{ "density_set4", { set_name4 }, 1.0 } ); + config.terms.push_back( geode::SingleObjectTermConfig{ + "density_set1", { set_name1 }, 1.0, density_feature } ); + config.terms.push_back( geode::SingleObjectTermConfig{ + "density_set2", { set_name2 }, 1.0, density_feature } ); + config.terms.push_back( geode::SingleObjectTermConfig{ + "density_set3", { set_name3 }, 1.0, density_feature } ); + config.terms.push_back( geode::SingleObjectTermConfig{ + "density_set4", { set_name4 }, 1.0, density_feature } ); + + std::vector< std::pair< std::string, std::string > > + set_name_interactions{ { set_name4, set_name2 }, + { set_name4, set_name3 }, { set_name3, set_name2 } }; + config.terms.push_back( geode::PairwiseTermConfig{ - "eclidiant_set2_3_4", { set_name4, set_name2, set_name3 }, 0.5 } ); + "min_distance_set2_3_4", set_name_interactions, 0.5, + geode::MinimalDistanceCutoffConfig{ 1. } } ); geode::BoundingBox2D box; box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); diff --git a/tests/stochastic/spatial/test-object-sets.cpp b/tests/stochastic/spatial/test-object-sets.cpp index a2416c2..7169abf 100644 --- a/tests/stochastic/spatial/test-object-sets.cpp +++ b/tests/stochastic/spatial/test-object-sets.cpp @@ -117,8 +117,9 @@ namespace sets.add_object( geode::Point2D{ { 5.0, 0.0 } }, set_id, false ); std::vector< uuid > targeted_set_ids{ set_id }; - const auto near_neighbors = - sets.neighbors( obj0, targeted_set_ids, 2.0 ); + + const auto near_neighbors = sets.neighbors( + sets.get_object( obj0 ), targeted_set_ids, 2.0, obj0 ); OPENGEODE_EXCEPTION( near_neighbors.size() == 1, "[TestObjectSets] - Expected exactly one neighbor near obj0" ); @@ -138,7 +139,8 @@ namespace const geode::Point2D query{ { 0.5, 0.0 } }; std::vector< uuid > targeted_set_ids{ set_id }; - const auto nearby = sets.neighbors( query, targeted_set_ids, 1.0 ); + const auto nearby = + sets.neighbors( query, targeted_set_ids, 1.0, std::nullopt ); OPENGEODE_EXCEPTION( nearby.size() == 2, "[TestObjectSets] - Expected 2 neighbors near query object" ); From 4e4a5a6c0810b2c7fa2943646d23d0828a26019f Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Thu, 9 Apr 2026 12:27:10 +0000 Subject: [PATCH 04/17] Apply prepare changes --- .clang-tidy | 7 ++++++- .../stochastic/models/energy_terms/energy_term_config.hpp | 4 ++-- include/geode/stochastic/models/model_configuration.hpp | 5 +++-- .../geode/stochastic/sampling/mcmc/simulation_runner.hpp | 2 +- include/geode/stochastic/spatial/object_neighborhood.hpp | 3 ++- tests/stochastic/sampling/mcmc/test-mh-poisson.cpp | 2 +- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index b039849..725ab31 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -19,7 +19,8 @@ Checks: > -readability-use-anyofallof, -readability-redundant-access-specifiers, -readability-convert-member-functions-to-static, - -cppcoreguidelines-avoid-const-or-ref-data-members + -cppcoreguidelines-avoid-const-or-ref-data-members, + -cppcoreguidelines-pro-bounds-constant-array-index CheckOptions: - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic @@ -63,5 +64,9 @@ CheckOptions: value: lower_case - key: readability-function-cognitive-complexity.Threshold value: 10 + - key: readability-function-cognitive-complexity.IgnoreMacros + value: true - key: readability-function-size.ParameterThreshold value: 4 + - key: cppcoreguidelines-pro-type-member-init.IgnoreArrays + value: true diff --git a/include/geode/stochastic/models/energy_terms/energy_term_config.hpp b/include/geode/stochastic/models/energy_terms/energy_term_config.hpp index da7b3dc..a897c5d 100644 --- a/include/geode/stochastic/models/energy_terms/energy_term_config.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term_config.hpp @@ -50,7 +50,7 @@ namespace geode PairwiseInteractionConfig interaction_config; }; - using EnergyTermConfig = - std::variant; + using EnergyTermConfig = std:: + variant< std::monostate, SingleObjectTermConfig, PairwiseTermConfig >; } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/models/model_configuration.hpp b/include/geode/stochastic/models/model_configuration.hpp index 069ea7b..06a4d8c 100644 --- a/include/geode/stochastic/models/model_configuration.hpp +++ b/include/geode/stochastic/models/model_configuration.hpp @@ -55,8 +55,9 @@ namespace geode for( const auto& term_cfg : config.terms ) { - auto term_id = collection.add_energy_term( - build_energy_term< ObjectType >( term_cfg, object_sets, domain ) ); + auto term_id = + collection.add_energy_term( build_energy_term< ObjectType >( + term_cfg, object_sets, domain ) ); } return collection; diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index 494fb20..4b81275 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -64,7 +64,7 @@ namespace geode { public: SimulationRunner( const SpatialDomain< ObjectType::dim >& domain ) - : domain_( domain ){}; + : domain_( domain ) {}; virtual ~SimulationRunner() = default; virtual void initialize() = 0; diff --git a/include/geode/stochastic/spatial/object_neighborhood.hpp b/include/geode/stochastic/spatial/object_neighborhood.hpp index abbabaa..a960a08 100644 --- a/include/geode/stochastic/spatial/object_neighborhood.hpp +++ b/include/geode/stochastic/spatial/object_neighborhood.hpp @@ -49,7 +49,8 @@ namespace geode } bool operator<( const ObjectId& other ) const noexcept { - return index < other.index && (set_id < other.set_id||set_id == other.set_id); + return index < other.index + && ( set_id < other.set_id || set_id == other.set_id ); } }; diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 063578e..c6d0802 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -50,7 +50,7 @@ namespace { public: PoissonSimulationRunner( const geode::SpatialDomain< 2 >& domain ) - : geode::SimulationRunner< geode::Point2D >( domain ){}; + : geode::SimulationRunner< geode::Point2D >( domain ) {}; void add_set_descriptor( const SetDescription& descriptor ) { From 56437238b7c742d01ec16ce15cd53ec73f0cff60 Mon Sep 17 00:00:00 2001 From: Arnaud Botella Date: Tue, 28 Apr 2026 09:21:02 +0200 Subject: [PATCH 05/17] fix(Library): better handle exception and library --- .vscode/settings.json | 4 +- CMakeLists.txt | 6 ++ bindings/python/src/stochastic/stochastic.cpp | 6 +- bindings/python/stochastic.py | 2 +- include/geode/stochastic/common.hpp | 6 +- include/geode/stochastic/project.hpp | 35 +++++++++ .../object_set_sampler/point_set_sampler.hpp | 14 ++-- .../segment_set_sampler.hpp | 8 +- .../mcmc/energy_terms/energy_term.hpp | 7 +- .../energy_terms/energy_term_collection.hpp | 12 ++- .../helpers/fracture_simulation_runner.hpp | 4 +- .../mcmc/helpers/simulation_monitor.hpp | 4 +- .../mcmc/helpers/simulation_printer.hpp | 14 ++-- .../mcmc/metropolis_hasting_sampler.hpp | 31 ++++---- .../mcmc/proposal/classical_proposals.hpp | 8 +- .../sampling/mcmc/proposal/moves.hpp | 19 +++-- .../mcmc/proposal/proposal_kernel.hpp | 23 +++--- .../stochastic/spatial/spatial_domain.hpp | 6 +- src/geode/stochastic/common.cpp | 2 +- .../sampling/direct/double_sampler.cpp | 54 ++++++++++--- .../sampling/direct/point_uniform_sampler.cpp | 7 +- .../stochastic/sampling/random_engine.cpp | 65 ++++++++++------ src/geode/stochastic/spatial/object_set.cpp | 17 +++-- src/geode/stochastic/spatial/object_sets.cpp | 20 +++-- .../sampling/direct/test-ball-sampler.cpp | 7 +- .../direct/test-bounding-box-sampler.cpp | 4 +- .../sampling/direct/test-double-sampler.cpp | 16 ++-- .../direct/test-point-uniform-sampler.cpp | 10 ++- .../direct/test-segment-uniform-sampler.cpp | 8 +- .../mcmc/energy_terms/test-density-term.cpp | 19 ++--- .../mcmc/energy_terms/test-gibbs-energy.cpp | 20 +++-- .../mcmc/energy_terms/test-intensity-term.cpp | 17 +++-- .../mcmc/energy_terms/test-pairwise-term.cpp | 45 ++++++----- .../mcmc/helpers/test-simulation_monitor.cpp | 17 +++-- .../mcmc/helpers/test-simulation_printer.cpp | 26 ++++--- .../mcmc/proposal/test-proposal-kernel.cpp | 39 +++++----- .../mcmc/test-metropolis-hasting-sampler.cpp | 18 +++-- .../sampling/mcmc/test-mh-fractures.cpp | 6 +- .../sampling/mcmc/test-mh-poisson.cpp | 11 +-- .../sampling/mcmc/test-mh-strauss.cpp | 8 +- .../sampling/test-random-engine.cpp | 29 ++++--- tests/stochastic/spatial/test-object-set.cpp | 54 +++++++------ tests/stochastic/spatial/test-object-sets.cpp | 38 ++++++---- .../spatial/test-pairwise-interactions.cpp | 8 +- .../spatial/test-spatial-domain.cpp | 75 ++++++++++--------- 45 files changed, 531 insertions(+), 318 deletions(-) create mode 100644 include/geode/stochastic/project.hpp diff --git a/.vscode/settings.json b/.vscode/settings.json index af49037..f916929 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,9 @@ { - "C_Cpp.default.cppStandard": "c++11", + "C_Cpp.default.cppStandard": "c++17", "C_Cpp.default.includePath": [ "${workspaceFolder}/include", "${workspaceFolder}/build/include", + "${workspaceFolder:OpenGeode}/include", "${workspaceFolder:OpenGeode}/build/opengeode/include", "${workspaceFolder:OpenGeode}/build/third_party/abseil/install/include", "${workspaceFolder:OpenGeode}/build/third_party/bitsery/install/include", @@ -14,6 +15,7 @@ "${workspaceFolder}/include", "${workspaceFolder}/src", "${workspaceFolder}/build/include", + "${workspaceFolder:OpenGeode}/include", "${workspaceFolder:OpenGeode}/build/opengeode/include", "${workspaceFolder:OpenGeode}/build/third_party/abseil/install/include", "${workspaceFolder:OpenGeode}/build/third_party/bitsery/install/include", diff --git a/CMakeLists.txt b/CMakeLists.txt index b05006c..a00cd96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,12 @@ option(OPENGEODE_STOCHASTIC_WITH_PYTHON "Compile Python bindings" OFF) # Get MyModule dependencies find_package(OpenGeode REQUIRED CONFIG) +install( + FILES include/geode/stochastic/project.hpp + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/geode/stochastic + COMPONENT public +) + # ------------------------------------------------------------------------------------------------ # Configure the OpenGeode-Stochastic libraries add_subdirectory(src/geode/stochastic) diff --git a/bindings/python/src/stochastic/stochastic.cpp b/bindings/python/src/stochastic/stochastic.cpp index 27e9f2f..c1a7516 100644 --- a/bindings/python/src/stochastic/stochastic.cpp +++ b/bindings/python/src/stochastic/stochastic.cpp @@ -38,8 +38,10 @@ PYBIND11_MODULE( opengeode_stochastic_py_stochastic, module ) { module.doc() = "OpenGeode-Stochastic Python binding"; - pybind11::class_< geode::StochasticLibrary >( module, "StochasticLibrary" ) - .def( "initialize", &geode::StochasticLibrary::initialize ); + pybind11::class_< geode::OpenGeodeStochasticStochasticLibrary >( + module, "OpenGeodeStochasticStochasticLibrary" ) + .def( "initialize", + &geode::OpenGeodeStochasticStochasticLibrary::initialize ); geode::define_spatial_domain( module ); diff --git a/bindings/python/stochastic.py b/bindings/python/stochastic.py index bf691fd..ac8568a 100644 --- a/bindings/python/stochastic.py +++ b/bindings/python/stochastic.py @@ -22,4 +22,4 @@ from opengeode_stochastic_py_stochastic import * -StochasticLibrary.initialize() +OpenGeodeStochasticStochasticLibrary.initialize() diff --git a/include/geode/stochastic/common.hpp b/include/geode/stochastic/common.hpp index 60115dc..675026b 100644 --- a/include/geode/stochastic/common.hpp +++ b/include/geode/stochastic/common.hpp @@ -23,15 +23,19 @@ #pragma once +#include + #include #include #include #include +#include namespace geode { - OPENGEODE_LIBRARY( opengeode_stochastic_stochastic_api, Stochastic ); + OPENGEODE_LIBRARY( + opengeode_stochastic_stochastic_api, OpenGeodeStochastic, Stochastic ); static constexpr double LOG_PROB_INVALID = -std::numeric_limits< double >::infinity(); diff --git a/include/geode/stochastic/project.hpp b/include/geode/stochastic/project.hpp new file mode 100644 index 0000000..1621941 --- /dev/null +++ b/include/geode/stochastic/project.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019 - 2026 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 + +namespace geode +{ + class OpenGeodeStochasticException : public OpenGeodeException + { + protected: + using OpenGeodeException::OpenGeodeException; + }; +} // namespace geode diff --git a/include/geode/stochastic/sampling/direct/object_set_sampler/point_set_sampler.hpp b/include/geode/stochastic/sampling/direct/object_set_sampler/point_set_sampler.hpp index efdc61a..9718f50 100644 --- a/include/geode/stochastic/sampling/direct/object_set_sampler/point_set_sampler.hpp +++ b/include/geode/stochastic/sampling/direct/object_set_sampler/point_set_sampler.hpp @@ -40,12 +40,14 @@ namespace geode : ObjectSetSampler< Point< dimension > >{}, domain_( domain ) { auto volume = domain_.extended_n_volume(); - OPENGEODE_EXCEPTION( volume != 0., + OpenGeodeStochasticStochasticException::check( volume != 0., + nullptr, OpenGeodeException::TYPE::data, "[PointSetSampler] - Undefined Extended Bounding " "Box (volume ==0)." ); this->log_pdf_ = -std::log( volume ); step_move_ = define_step_for_move(); - OPENGEODE_EXCEPTION( step_move_ > 0., + OpenGeodeStochasticStochasticException::check( step_move_ > 0., + nullptr, OpenGeodeException::TYPE::data, "[PointSetSampler] - Undefined step length for move (value == ", step_move_, ")." ); } @@ -73,10 +75,10 @@ namespace geode new_point = PointUniformSampler::sample< dimension >( engine, ball ); } - throw OpenGeodeException( - absl::StrCat( "[PointSampler] - Cannot find a point in the " - "extended domain" ) ); - return obj; + throw OpenGeodeStochasticStochasticException{ nullptr, + OpenGeodeException::TYPE::internal, + "[PointSampler] - Cannot find a point in the " + "extended domain" }; } private: diff --git a/include/geode/stochastic/sampling/direct/object_set_sampler/segment_set_sampler.hpp b/include/geode/stochastic/sampling/direct/object_set_sampler/segment_set_sampler.hpp index eb15aba..884fd13 100644 --- a/include/geode/stochastic/sampling/direct/object_set_sampler/segment_set_sampler.hpp +++ b/include/geode/stochastic/sampling/direct/object_set_sampler/segment_set_sampler.hpp @@ -45,7 +45,8 @@ namespace geode azimuth_{ azimuth } { auto volume = domain_.extended_n_volume(); - OPENGEODE_EXCEPTION( volume != 0., + OpenGeodeStochasticStochasticException::check( volume != 0., + nullptr, OpenGeodeException::TYPE::data, "[SegmentSetSampler] - Undefined Extended Bounding " "Box (volume ==0)." ); this->log_pdf_ = -std::log( volume ); @@ -83,8 +84,9 @@ namespace geode } new_point = PointUniformSampler::sample< 2 >( engine, ball ); } - throw OpenGeodeException( absl::StrCat( - "[SegmentSetSampler] - Cannot find a point in the box" ) ); + throw OpenGeodeStochasticStochasticException{ nullptr, + OpenGeodeException::TYPE::internal, + "[SegmentSetSampler] - Cannot find a point in the box" }; return obj; } diff --git a/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term.hpp b/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term.hpp index 8463bb6..3712f9d 100644 --- a/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term.hpp +++ b/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term.hpp @@ -40,9 +40,10 @@ namespace geode::detail { explicit EnergyScale( double param ) { - OPENGEODE_EXCEPTION( param >= 0., - "[Gibbs energy term] - The model parameter " - "cannot be negative." ); + OpenGeodeStochasticStochasticException::check( param >= 0., nullptr, + OpenGeodeException::TYPE::data, + "[Gibbs energy term] - The model parameter cannot be " + "negative." ); if( param >= geode::GLOBAL_EPSILON ) { diff --git a/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term_collection.hpp b/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term_collection.hpp index 5826df9..5add193 100644 --- a/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term_collection.hpp +++ b/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term_collection.hpp @@ -77,9 +77,11 @@ namespace geode const uuid& term_id ) const { auto term_it = energy_terms_.find( term_id ); - OPENGEODE_EXCEPTION( term_it != energy_terms_.end(), - absl::StrCat( "[EnergyTermCollection] Unknown energy term: ", - term_id.string() ) ); + OpenGeodeStochasticStochasticException::check( + term_it != energy_terms_.end(), nullptr, + OpenGeodeException::TYPE::data, + "[EnergyTermCollection] Unknown energy term: ", + term_id.string() ); return *term_it->second; } @@ -95,7 +97,9 @@ namespace geode terms_for_set( const uuid& set_id ) const { const auto it = set_to_terms_.find( set_id ); - OPENGEODE_EXCEPTION( it != set_to_terms_.end(), + OpenGeodeStochasticStochasticException::check( + it != set_to_terms_.end(), nullptr, + OpenGeodeException::TYPE::data, "[EnergyTermCollection] - Object Subset (", set_id.string(), ") does not have any energy term." ); return it->second; diff --git a/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp index 237e741..11ab3a4 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp @@ -91,7 +91,9 @@ namespace geode void add_x_node_monitoring( double beta_x_node ) { - OPENGEODE_EXCEPTION( beta_x_node <= 1.0 && beta_x_node >= 0., + OpenGeodeStochasticStochasticException::check( + beta_x_node <= 1.0 && beta_x_node >= 0., nullptr, + OpenGeodeException::TYPE::data, "[FractureSimulationRunner] x node should be inhibitated, " "please provise a value in [0., 1.]." ); beta_x_node_ = beta_x_node; diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp index 95cc380..e7e36f9 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp @@ -45,7 +45,9 @@ namespace geode void add_realization( const std::vector< double >& values ) { - OPENGEODE_EXCEPTION( values.size() == means_.size(), + OpenGeodeStochasticStochasticException::check( + values.size() == means_.size(), nullptr, + OpenGeodeException::TYPE::data, "[StatisticsMonitor] - Mismatch between realization size and " "expected number of statistics." ); ++count_; diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_printer.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_printer.hpp index 34f8691..3ab9aa6 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_printer.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_printer.hpp @@ -164,16 +164,20 @@ namespace geode fs::path file_path{ std::string( path_filename ) }; if( !file_path.has_parent_path() ) + { file_path = fs::current_path() / file_path; - + } if( file_path.has_parent_path() ) + { fs::create_directories( file_path.parent_path() ); - + } std::ofstream file( file_path, mode ); if( !file.is_open() ) - throw geode::OpenGeodeException( - "Cannot open file: " + file_path.string() ); - + { + throw geode::OpenGeodeStochasticStochasticException{ nullptr, + OpenGeodeException::TYPE::data, + "Cannot open file: " + file_path.string() }; + } return file; } const std::string& create_statistics_file( diff --git a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp index 64a80ec..1ef2571 100644 --- a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp +++ b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp @@ -55,8 +55,9 @@ namespace geode : gibbs_energy_{ energy_term_collection }, proposal_kernel_( std::move( proposal_kernel ) ) { - OPENGEODE_ASSERT( - proposal_kernel_ != nullptr, "[MH] null proposal kernel" ); + OpenGeodeStochasticStochasticException::check( + proposal_kernel_ != nullptr, nullptr, + OpenGeodeException::TYPE::data, "[MH] null proposal kernel" ); } StepResult< ObjectType > step( @@ -106,34 +107,32 @@ namespace geode void set_beta( double b ) { - OPENGEODE_EXCEPTION( b >= 0.0, "[MH] beta must be >= 0" ); + OpenGeodeStochasticStochasticException::check( b >= 0.0, nullptr, + OpenGeodeException::TYPE::data, "[MH] beta must be >= 0" ); if( b == 0 ) { - geode::Logger::info( + Logger::info( "[Metropolis Hastings] - beta == 0 all move will be " "accepted - Uniform sampling." ); } if( b < 1 ) { - geode::Logger::info( + Logger::info( "[Metropolis Hastings] - beta < 1 moves that increase " - "energy are " - "more likely to be accepted - Hot system introduce " - "randomness for exploration." ); + "energy are more likely to be accepted - Hot system " + "introduce randomness for exploration." ); } if( b == 1 ) { - geode::Logger::info( "[Metropolis Hastings] - beta == 1 " - "default choice no temperature " - "- only consider energy." ); + Logger::info( "[Metropolis Hastings] - beta == 1 default " + "choice no temperature - only consider energy." ); } if( b > 1 ) { - geode::Logger::info( - "[Metropolis Hastings] - beta > 1 moves that increase " - "energy are " - "less likely to be accepted - Cold system to ensure " - "convergence but may find local minimum randomness." ); + Logger::info( "[Metropolis Hastings] - beta > 1 moves that " + "increase energy are less likely to be accepted " + "- Cold system to ensure convergence but may " + "find local minimum randomness." ); } beta_ = b; } diff --git a/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp b/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp index f76d40d..9f31d92 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp @@ -40,8 +40,9 @@ namespace geode double change_ratio ) { const auto total_ratio = birth_ratio + death_ratio; - OPENGEODE_EXCEPTION( - total_ratio > 0., "BIRTH+DEATH ratio must be positive" ); + OpenGeodeStochasticStochasticException::check( total_ratio > 0., + nullptr, OpenGeodeException::TYPE::data, + "BIRTH+DEATH ratio must be positive" ); const auto p_birth = birth_ratio / total_ratio; kernel.add_move( @@ -77,7 +78,8 @@ namespace geode double death_prob ) { auto birth_death_prob = birth_prob + death_prob; - OPENGEODE_EXCEPTION( birth_death_prob < 1., + OpenGeodeStochasticStochasticException::check( birth_death_prob < 1., + nullptr, OpenGeodeException::TYPE::data, "[Proposal Kernel] - changes should be allowed." ); auto kernel = std::make_unique< ProposalKernel< ObjectType > >(); kernel->add_move( diff --git a/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp b/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp index 1e4e921..74832df 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp @@ -50,8 +50,10 @@ namespace geode double transition_probability() const { - OPENGEODE_EXCEPTION( std::isfinite( log_forward_prob ) - && std::isfinite( log_backward_prob ), + OpenGeodeStochasticStochasticException::check( + std::isfinite( log_forward_prob ) + && std::isfinite( log_backward_prob ), + nullptr, OpenGeodeException::TYPE::data, "[Proposal Probabilities] Non-finite proposal " "log-probabilities" ); return log_backward_prob - log_forward_prob; @@ -80,9 +82,10 @@ namespace geode case MoveType::Change: return "Change"; default: - throw OpenGeodeException( + throw OpenGeodeStochasticStochasticException{ nullptr, + OpenGeodeException::TYPE::data, "[MoveResult] -Move result type should always be " - "defined." ); + "defined." }; return "Unknown"; } } @@ -113,7 +116,9 @@ namespace geode double proportion_weight ) : sampler_( sampler ), proportion_weight_{ proportion_weight } { - OPENGEODE_EXCEPTION( proportion_weight_ > 0., + OpenGeodeStochasticStochasticException::check( + proportion_weight_ > 0., nullptr, + OpenGeodeException::TYPE::data, "[Move] - the weight factor for a move should be in higher " "than 0. (here = ", proportion_weight_, ")" ); @@ -182,7 +187,9 @@ namespace geode void initialize_probability( double probability ) override { - OPENGEODE_EXCEPTION( birth_ratio_ > 0. && birth_ratio_ < 1., + OpenGeodeStochasticStochasticException::check( + birth_ratio_ > 0. && birth_ratio_ < 1., nullptr, + OpenGeodeException::TYPE::data, "[BirthDeathMove]-the ratio of birth over mover should be in " "]0,1[. (here = ", birth_ratio_, ")" ); diff --git a/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp b/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp index 97863f5..ecf37d1 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp @@ -36,7 +36,9 @@ namespace geode MoveResult< ObjectType > proposed_move; ObjectRef< ObjectType > new_object() { - OPENGEODE_EXCEPTION( proposed_move.new_object.has_value(), + OpenGeodeStochasticStochasticException::check( + proposed_move.new_object.has_value(), nullptr, + OpenGeodeException::TYPE::data, "[Proposal] Proposal has no new_object" ); return ObjectRef< ObjectType >{ proposed_move.new_object.value(), set_id }; @@ -44,7 +46,9 @@ namespace geode ObjectId old_object_id() { - OPENGEODE_EXCEPTION( proposed_move.old_object_id.has_value(), + OpenGeodeStochasticStochasticException::check( + proposed_move.old_object_id.has_value(), nullptr, + OpenGeodeException::TYPE::data, "[Proposal] Proposal has no old_object_id" ); return ObjectId{ proposed_move.old_object_id.value(), false, set_id }; @@ -66,7 +70,8 @@ namespace geode Proposal< ObjectType > propose( const ObjectSets< ObjectType >& current, RandomEngine& engine ) const { - OPENGEODE_EXCEPTION( !set_moves_.empty(), + OpenGeodeStochasticStochasticException::check( !set_moves_.empty(), + nullptr, OpenGeodeException::TYPE::data, "[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() } ) @@ -79,12 +84,10 @@ namespace geode current.get_set( set_id ), engine ) }; } } - throw OpenGeodeException( + throw OpenGeodeStochasticStochasticException{ nullptr, + OpenGeodeException::TYPE::internal, "[MCMC Proposal Kernel]: Should not be reached move pdf is " - "correctly set." ); - return Proposal< ObjectType >{ uuid{}, - set_moves_.back().second->propose_move( - current.get_set( uuid{} ), engine ) }; + "correctly set." }; } void add_move( @@ -128,7 +131,9 @@ namespace geode probabilities.begin(), probabilities.end(), 0.0 ); // Ensure total > 0 - OPENGEODE_EXCEPTION( total > GLOBAL_EPSILON, + OpenGeodeStochasticStochasticException::check( + total > GLOBAL_EPSILON, nullptr, + OpenGeodeException::TYPE::internal, "[MCMC Proposal Kernel] - Total " "probability is zero in Kernel." ); diff --git a/include/geode/stochastic/spatial/spatial_domain.hpp b/include/geode/stochastic/spatial/spatial_domain.hpp index cad72ed..14d69af 100644 --- a/include/geode/stochastic/spatial/spatial_domain.hpp +++ b/include/geode/stochastic/spatial/spatial_domain.hpp @@ -40,10 +40,12 @@ namespace geode extended_domain_{ domain } { auto volume = domain_.n_volume(); - OPENGEODE_EXCEPTION( volume > 0., + OpenGeodeStochasticStochasticException::check( volume > 0., nullptr, + OpenGeodeException::TYPE::data, "[SpatialDomain] - Undefined Spatial Domain (volume == ", volume, ")." ); - OPENGEODE_EXCEPTION( buffer_size_ >= 0.0, + OpenGeodeStochasticStochasticException::check( buffer_size_ >= 0.0, + nullptr, OpenGeodeException::TYPE::data, "[SpatialDomain] buffer size must be non-negative ( buffer " "== ", buffer_size_, ")" ); diff --git a/src/geode/stochastic/common.cpp b/src/geode/stochastic/common.cpp index 798da55..d503a31 100644 --- a/src/geode/stochastic/common.cpp +++ b/src/geode/stochastic/common.cpp @@ -28,7 +28,7 @@ namespace geode { - OPENGEODE_LIBRARY_IMPLEMENTATION( Stochastic ) + OPENGEODE_LIBRARY_IMPLEMENTATION( OpenGeodeStochastic, Stochastic ) { /* Here the functions to call when initializing the library * For exemple: registers, ... diff --git a/src/geode/stochastic/sampling/direct/double_sampler.cpp b/src/geode/stochastic/sampling/direct/double_sampler.cpp index d034f16..63ddd85 100644 --- a/src/geode/stochastic/sampling/direct/double_sampler.cpp +++ b/src/geode/stochastic/sampling/direct/double_sampler.cpp @@ -49,7 +49,9 @@ namespace geode distribution_registry = { { UniformClosed< double >::distribution_type_static(), []( const DoubleSampler::DistributionDescription& desc ) { - OPENGEODE_EXCEPTION( desc.min_value && desc.max_value, + OpenGeodeStochasticStochasticException::check( + desc.min_value && desc.max_value, nullptr, + OpenGeodeException::TYPE::data, "[DoubleSampler] - Incomplete description for " "Uniform distribution need at least min " "and max values" ); @@ -60,7 +62,9 @@ namespace geode } }, { UniformClosedOpen< double >::distribution_type_static(), []( const DoubleSampler::DistributionDescription& desc ) { - OPENGEODE_EXCEPTION( desc.min_value && desc.max_value, + OpenGeodeStochasticStochasticException::check( + desc.min_value && desc.max_value, nullptr, + OpenGeodeException::TYPE::data, "[DoubleSampler] - Incomplete description for " "Uniform distribution need at least min " "and max values" ); @@ -71,7 +75,9 @@ namespace geode } }, { Gaussian::distribution_type_static(), []( const DoubleSampler::DistributionDescription& desc ) { - OPENGEODE_EXCEPTION( desc.mean && desc.standard_deviation, + OpenGeodeStochasticStochasticException::check( + desc.mean && desc.standard_deviation, nullptr, + OpenGeodeException::TYPE::data, "[DoubleSampler] - Incomplete description for " "Gaussian distribution need at least mean " "and standard deviation values" ); @@ -82,7 +88,9 @@ namespace geode } }, { TruncatedGaussian::distribution_type_static(), []( const DoubleSampler::DistributionDescription& desc ) { - OPENGEODE_EXCEPTION( desc.mean && desc.standard_deviation, + OpenGeodeStochasticStochasticException::check( + desc.mean && desc.standard_deviation, nullptr, + OpenGeodeException::TYPE::data, "[DoubleSampler] - Incomplete description for " "Truncated Gaussian distribution need at least mean " "and standard deviation values" ); @@ -96,7 +104,9 @@ namespace geode } }, { VonMises::distribution_type_static(), []( const DoubleSampler::DistributionDescription& desc ) { - OPENGEODE_EXCEPTION( desc.mean && desc.kappa, + OpenGeodeStochasticStochasticException::check( + desc.mean && desc.kappa, nullptr, + OpenGeodeException::TYPE::data, "[DoubleSampler] - Incomplete description for " "Von Mises distribution need at least mean " "and concentration (kappa) values" ); @@ -107,7 +117,9 @@ namespace geode } }, { TruncatedLogNormal::distribution_type_static(), []( const DoubleSampler::DistributionDescription& desc ) { - OPENGEODE_EXCEPTION( desc.mean && desc.standard_deviation, + OpenGeodeStochasticStochasticException::check( + desc.mean && desc.standard_deviation, nullptr, + OpenGeodeException::TYPE::data, "[DoubleSampler] - Incomplete description for " "TruncatedLogNormal distribution need mean " "and standard deviation values of the underlying " @@ -121,7 +133,9 @@ namespace geode } }, { TruncatedPowerLaw::distribution_type_static(), []( const DoubleSampler::DistributionDescription& desc ) { - OPENGEODE_EXCEPTION( desc.alpha, + OpenGeodeStochasticStochasticException::check( + desc.alpha.has_value(), nullptr, + OpenGeodeException::TYPE::data, "[DoubleSampler] - Incomplete description for " "TruncatedPowerLaw distribution need power law " "exponent (alpha)." ); @@ -138,8 +152,11 @@ namespace geode { auto it = distribution_registry.find( desc.distribution_type ); if( it == distribution_registry.end() ) - throw geode::OpenGeodeException( absl::StrCat( - "Unknown distribution type: ", desc.distribution_type.get() ) ); + { + throw OpenGeodeStochasticStochasticException{ nullptr, + OpenGeodeException::TYPE::data, + "Unknown distribution type: ", desc.distribution_type.get() }; + } return it->second( desc ); } @@ -185,21 +202,36 @@ namespace geode [&engine]( auto&& d ) { using D = std::decay_t< decltype( d ) >; if constexpr( std::is_same_v< D, UniformClosed< double > > ) + { return engine.sample_uniform< double >( d ); + } if constexpr( std::is_same_v< D, UniformClosedOpen< double > > ) + { return engine.sample_uniform< double >( d ); + } if constexpr( std::is_same_v< D, Gaussian > ) + { return engine.sample_gaussian( d ); + } if constexpr( std::is_same_v< D, TruncatedGaussian > ) + { return engine.sample_truncated_gaussian( d ); + } if constexpr( std::is_same_v< D, VonMises > ) + { return engine.sample_von_mises( d ); + } if constexpr( std::is_same_v< D, TruncatedLogNormal > ) + { return engine.sample_truncated_lognormal( d ); + } if constexpr( std::is_same_v< D, TruncatedPowerLaw > ) + { return engine.sample_truncated_powerlaw( d ); - throw OpenGeodeException( "DoubleSampler - Unsupported " - "distribution for double" ); + } + throw OpenGeodeStochasticStochasticException{ nullptr, + OpenGeodeException::TYPE::data, + "DoubleSampler - Unsupported distribution for double" }; }, dist ); } diff --git a/src/geode/stochastic/sampling/direct/point_uniform_sampler.cpp b/src/geode/stochastic/sampling/direct/point_uniform_sampler.cpp index 8dae9fc..1502dac 100644 --- a/src/geode/stochastic/sampling/direct/point_uniform_sampler.cpp +++ b/src/geode/stochastic/sampling/direct/point_uniform_sampler.cpp @@ -52,9 +52,10 @@ namespace geode BallSampler< dimension > sampler{ obj }; return sampler.sample_uniform( engine ); } - throw OpenGeodeException( - "PointUniformSampler - Unsupported object " - "for point sampling" ); + throw OpenGeodeStochasticStochasticException{ nullptr, + OpenGeodeException::TYPE::data, + "PointUniformSampler - Unsupported object for point " + "sampling" }; }, object ); } diff --git a/src/geode/stochastic/sampling/random_engine.cpp b/src/geode/stochastic/sampling/random_engine.cpp index f904077..238c1bc 100644 --- a/src/geode/stochastic/sampling/random_engine.cpp +++ b/src/geode/stochastic/sampling/random_engine.cpp @@ -49,7 +49,9 @@ namespace // https://web.archive.org/web/20150910005011/http://home.online.no/~pjacklam double normal_quantile( double p ) { - OPENGEODE_EXCEPTION( p >= 0.0 && p <= 1.0, + geode::OpenGeodeStochasticStochasticException::check( + p >= 0.0 && p <= 1.0, nullptr, + geode::OpenGeodeException::TYPE::data, "[normal_quantile] - p must be in (0,1). Check the consistencies " "between min,mean and max values." ); @@ -145,7 +147,9 @@ namespace geode template < typename Type > Type sample_uniform( const UniformClosed< Type >& law ) { - OPENGEODE_ASSERT( law.min_value <= law.max_value, + OpenGeodeStochasticStochasticException::check( + law.min_value <= law.max_value, nullptr, + OpenGeodeException::TYPE::data, "[Uniform sampling] - Wrong range ", law.min_value, " is not <= than ", law.max_value, "." ); return absl::Uniform( @@ -155,7 +159,9 @@ namespace geode template < typename Type > Type sample_uniform( const UniformClosedOpen< Type >& law ) { - OPENGEODE_ASSERT( law.min_value < law.max_value, + OpenGeodeStochasticStochasticException::check( + law.min_value < law.max_value, nullptr, + OpenGeodeException::TYPE::data, "[Uniform sampling] - Wrong range ", law.min_value, " is not < than ", law.max_value, "." ); return absl::Uniform( absl::IntervalClosedOpen, rand_gen_, @@ -164,9 +170,11 @@ namespace geode double sample_gaussian( const Gaussian& law ) { - OPENGEODE_ASSERT( law.standard_deviation > 0 - && std::isfinite( law.standard_deviation ) - && std::isfinite( law.mean ), + OpenGeodeStochasticStochasticException::check( + law.standard_deviation > 0 + && std::isfinite( law.standard_deviation ) + && std::isfinite( law.mean ), + nullptr, OpenGeodeException::TYPE::data, "[Gaussian sampling] - Infinite " "parameters or negative standard deviation N(", law.mean, law.standard_deviation, ")." ); @@ -176,9 +184,11 @@ namespace geode double sample_truncated_gaussian( const TruncatedGaussian& law ) { - OPENGEODE_ASSERT( law.standard_deviation > 0 - && std::isfinite( law.standard_deviation ) - && std::isfinite( law.mean ), + OpenGeodeStochasticStochasticException::check( + law.standard_deviation > 0 + && std::isfinite( law.standard_deviation ) + && std::isfinite( law.mean ), + nullptr, OpenGeodeException::TYPE::data, "[Gaussian sampling] - Infinite parameters or " "negative standard deviation N(", law.mean, ",", law.standard_deviation, ")." ); @@ -188,7 +198,8 @@ namespace geode const double min = law.min_value.value_or( -std::numeric_limits< double >::infinity() ); - OPENGEODE_ASSERT( min < max, + OpenGeodeStochasticStochasticException::check( min < max, nullptr, + OpenGeodeException::TYPE::data, "[Gaussian sampling] - Wrong truncation range ", min, " is not < than ", max, "." ); @@ -204,7 +215,8 @@ namespace geode F_min = std::max( F_min, geode::GLOBAL_EPSILON ); F_max = std::min( F_max, 1.0 - geode::GLOBAL_EPSILON ); - OPENGEODE_EXCEPTION( F_min < F_max, + OpenGeodeStochasticStochasticException::check( F_min < F_max, + nullptr, OpenGeodeException::TYPE::data, "[Gaussian sampling] - truncation " "range is extreme please check inputs" ); @@ -218,8 +230,9 @@ namespace geode double sample_von_mises( const VonMises& law ) { - OPENGEODE_ASSERT( - law.concentration >= 0.0 && std::isfinite( law.mean ), + OpenGeodeStochasticStochasticException::check( + law.concentration >= 0.0 && std::isfinite( law.mean ), nullptr, + OpenGeodeException::TYPE::data, "[VonMises sampling] - Invalid parameters: mean=", law.mean, ", concentration=", law.concentration, "." ); @@ -284,9 +297,11 @@ namespace geode double sample_truncated_lognormal( const TruncatedLogNormal& law ) { // Basic sanity checks - OPENGEODE_ASSERT( law.standard_deviation > 0 - && std::isfinite( law.standard_deviation ) - && std::isfinite( law.mean ), + OpenGeodeStochasticStochasticException::check( + law.standard_deviation > 0 + && std::isfinite( law.standard_deviation ) + && std::isfinite( law.mean ), + nullptr, OpenGeodeException::TYPE::data, "[Truncated LogNormal sampling] - Infinite parameters or " "negative standard deviation N(", law.mean, ", ", law.standard_deviation, ")." ); @@ -296,7 +311,8 @@ namespace geode const double max_val = law.max_value.value_or( std::numeric_limits< double >::infinity() ); - OPENGEODE_ASSERT( min_val < max_val, + OpenGeodeStochasticStochasticException::check( min_val < max_val, + nullptr, OpenGeodeException::TYPE::data, "[Truncated LogNormal sampling] - Wrong truncation range ", min_val, " is not < than ", max_val, "." ); @@ -315,7 +331,8 @@ namespace geode double F_max = std::min( normal_cdf( zmax ), 1.0 - geode::GLOBAL_EPSILON ); - OPENGEODE_EXCEPTION( F_min < F_max, + OpenGeodeStochasticStochasticException::check( F_min < F_max, + nullptr, OpenGeodeException::TYPE::data, "[Truncated LogNormal sampling] - truncation " "range is extreme please chack inputs" ); @@ -330,8 +347,9 @@ namespace geode double sample_truncated_powerlaw( const TruncatedPowerLaw& law ) { - OPENGEODE_ASSERT( - law.alpha > 0, "Power-law exponent alpha must be > 0" ); + OpenGeodeStochasticStochasticException::check( law.alpha > 0, + nullptr, OpenGeodeException::TYPE::data, + "Power-law exponent alpha must be > 0" ); // Set bounds const double xmin = law.min_value.value_or( @@ -339,7 +357,9 @@ namespace geode const double xmax = law.max_value.value_or( std::numeric_limits< double >::infinity() ); - OPENGEODE_ASSERT( xmin < xmax, "Truncated power-law: min >= max" ); + OpenGeodeStochasticStochasticException::check( xmin < xmax, nullptr, + OpenGeodeException::TYPE::data, + "Truncated power-law: min >= max" ); // Sample uniform std::uniform_real_distribution< double > uniform( 0.0, 1.0 ); @@ -377,8 +397,9 @@ namespace geode bool sample_bernoulli( double probability_of_success ) { - OPENGEODE_ASSERT( + OpenGeodeStochasticStochasticException::check( probability_of_success >= 0. && probability_of_success <= 1.0, + nullptr, OpenGeodeException::TYPE::data, "Bernoulli sampling cannot be done since ", probability_of_success, " is not in [0.,1.]." ); return absl::bernoulli_distribution( probability_of_success )( diff --git a/src/geode/stochastic/spatial/object_set.cpp b/src/geode/stochastic/spatial/object_set.cpp index bf28a25..fe704bc 100644 --- a/src/geode/stochastic/spatial/object_set.cpp +++ b/src/geode/stochastic/spatial/object_set.cpp @@ -41,7 +41,9 @@ namespace geode template < typename Type > const Type& ObjectSet< Type >::get_fixed_object( index_t index ) const { - OPENGEODE_EXCEPTION( index < fixed_objects_.size(), + OpenGeodeStochasticStochasticException::check( + index < fixed_objects_.size(), nullptr, + OpenGeodeException::TYPE::data, "[ObjectSet] - index for fixed object out of range." ); return fixed_objects_[index]; } @@ -49,7 +51,9 @@ namespace geode template < typename Type > const Type& ObjectSet< Type >::get_free_object( index_t index ) const { - OPENGEODE_EXCEPTION( index < free_objects_.size(), + OpenGeodeStochasticStochasticException::check( + index < free_objects_.size(), nullptr, + OpenGeodeException::TYPE::data, "[ObjectSet] - index for free object out of range." ); return free_objects_[index]; } @@ -71,7 +75,9 @@ namespace geode template < typename Type > void ObjectSet< Type >::update_free_object( index_t index, Type&& object ) { - OPENGEODE_EXCEPTION( index < free_objects_.size(), + OpenGeodeStochasticStochasticException::check( + index < free_objects_.size(), nullptr, + OpenGeodeException::TYPE::data, "[ObjectSet] - free object index out of range." ); free_objects_[index] = std::move( object ); } @@ -80,8 +86,9 @@ namespace geode void ObjectSet< Type >::remove_free_object( index_t index ) { const index_t last = free_objects_.size() - 1; - OPENGEODE_EXCEPTION( - index <= last, "[ObjectSet] - free object index out of range." ); + OpenGeodeStochasticStochasticException::check( index <= last, nullptr, + OpenGeodeException::TYPE::data, + "[ObjectSet] - free object index out of range." ); if( index != last ) { std::swap( free_objects_[index], free_objects_[last] ); diff --git a/src/geode/stochastic/spatial/object_sets.cpp b/src/geode/stochastic/spatial/object_sets.cpp index 67c213d..caba6de 100644 --- a/src/geode/stochastic/spatial/object_sets.cpp +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -12,7 +12,8 @@ namespace geode const uuid& set_id ) const { auto it = sets_.find( set_id ); - OPENGEODE_EXCEPTION( it != sets_.end(), "[ObjectSet] - group (", + OpenGeodeStochasticStochasticException::check( it != sets_.end(), + nullptr, OpenGeodeException::TYPE::data, "[ObjectSet] - group (", set_id.string(), ") is not defined." ); return it->second; } @@ -96,7 +97,8 @@ namespace geode new_set.set_name( name ); 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 (", + OpenGeodeStochasticStochasticException::check( inserted, new_set_id, + OpenGeodeException::TYPE::data, "[ObjectSet]- group (", new_set_id.string(), ") already exists." ); return new_set_id; } @@ -125,10 +127,13 @@ namespace geode void ObjectSets< Type >::update_free_object( const ObjectId& old_object_id, Type&& new_object ) { - OPENGEODE_EXCEPTION( - !old_object_id.fixed, "[ObjectSet]- cannot modify fixed object." ); + OpenGeodeStochasticStochasticException::check( !old_object_id.fixed, + nullptr, OpenGeodeException::TYPE::data, + "[ObjectSet]- cannot modify fixed object." ); auto& set = get_set( old_object_id.set_id ); - OPENGEODE_EXCEPTION( old_object_id.index < set.nb_objects(), + OpenGeodeStochasticStochasticException::check( + old_object_id.index < set.nb_objects(), nullptr, + OpenGeodeException::TYPE::data, "[ObjectSet]- index of object to update out of range." ); auto old_box = object_bounding_box( get_object( old_object_id ) ); auto new_box = object_bounding_box( new_object ); @@ -140,8 +145,9 @@ namespace geode void ObjectSets< Type >::remove_free_object( const ObjectId& object_id ) { auto& set = get_set( object_id.set_id ); - OPENGEODE_EXCEPTION( - !object_id.fixed, "[ObjectSet]- Cannot remove fixed object." ); + OpenGeodeStochasticStochasticException::check( !object_id.fixed, + nullptr, OpenGeodeException::TYPE::data, + "[ObjectSet]- Cannot remove fixed object." ); const auto& obj_to_remove = get_object( object_id ); neighborhood_.remove( object_bounding_box( obj_to_remove ), object_id ); diff --git a/tests/stochastic/sampling/direct/test-ball-sampler.cpp b/tests/stochastic/sampling/direct/test-ball-sampler.cpp index b7a6b5c..2f5f304 100644 --- a/tests/stochastic/sampling/direct/test-ball-sampler.cpp +++ b/tests/stochastic/sampling/direct/test-ball-sampler.cpp @@ -43,8 +43,9 @@ void test_sample_ball( for( const auto i : geode::Range{ NUMBER_OF_SAMPLES } ) { auto value = spec_ball.sample_uniform( engine ); - OPENGEODE_EXCEPTION( geode::point_point_distance( value, ball.origin() ) - <= ball.radius() + geode::GLOBAL_EPSILON, + geode::OpenGeodeStochasticStochasticException::test( + geode::point_point_distance( value, ball.origin() ) + <= ball.radius() + geode::GLOBAL_EPSILON, "[Point Ball sampler] - point too far from center." ); } } @@ -71,7 +72,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); test_ball_sampling2D(); test_ball_sampling3D(); diff --git a/tests/stochastic/sampling/direct/test-bounding-box-sampler.cpp b/tests/stochastic/sampling/direct/test-bounding-box-sampler.cpp index 8b82afb..9af7a7d 100644 --- a/tests/stochastic/sampling/direct/test-bounding-box-sampler.cpp +++ b/tests/stochastic/sampling/direct/test-bounding-box-sampler.cpp @@ -43,7 +43,7 @@ void test_sample_bounding_box( for( const auto i : geode::Range{ NUMBER_OF_SAMPLES } ) { auto value = spec_box.sample_uniform( engine ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( box.contains( value ), "[Point Box sampler] - point out of box." ); } } @@ -91,7 +91,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); test_box_sampling1D(); test_box_sampling2D(); diff --git a/tests/stochastic/sampling/direct/test-double-sampler.cpp b/tests/stochastic/sampling/direct/test-double-sampler.cpp index 0ba44df..23b2cd2 100644 --- a/tests/stochastic/sampling/direct/test-double-sampler.cpp +++ b/tests/stochastic/sampling/direct/test-double-sampler.cpp @@ -40,13 +40,13 @@ void test_double_sampler( geode::RandomEngine& engine ) geode::UniformClosed< double > uniform_closed_double; double value = 1000.; value = geode::DoubleSampler::sample( engine, uniform_closed_double ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( value >= 0. && value <= 1., "[Uniform] - value out of range." ); geode::UniformClosedOpen< double > uniform_closedopen_double; value = 1000.; value = geode::DoubleSampler::sample( engine, uniform_closedopen_double ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( value >= 0. && value <= 1., "[Uniform] - value out of range." ); geode::TruncatedGaussian t_gaussian_double; @@ -56,7 +56,8 @@ void test_double_sampler( geode::RandomEngine& engine ) t_gaussian_double.min_value = -1.; value = 1000.; value = geode::DoubleSampler::sample( engine, t_gaussian_double ); - OPENGEODE_EXCEPTION( value >= -1. && value <= 1., + geode::OpenGeodeStochasticStochasticException::test( + value >= -1. && value <= 1., "[TruncatedGaussian] - value out of range." ); geode::Gaussian gaussian_double; @@ -65,7 +66,7 @@ void test_double_sampler( geode::RandomEngine& engine ) value = 1000.; value = geode::DoubleSampler::sample( engine, t_gaussian_double ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( value != 1000., "[Gaussian] - value did not changed." ); } @@ -81,7 +82,7 @@ void test_create_uniform_closed( geode::RandomEngine& engine ) auto dist = geode::DoubleSampler::create_distribution( desc ); double value = geode::DoubleSampler::sample( engine, dist ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( value >= 2. && value <= 5., "[UniformClosed] - value out of bounds" ); } @@ -101,7 +102,8 @@ void test_create_truncated_gaussian( geode::RandomEngine& engine ) for( int i = 0; i < 100; ++i ) { double value = geode::DoubleSampler::sample( engine, dist ); - OPENGEODE_EXCEPTION( value >= -2. && value <= 2., + geode::OpenGeodeStochasticStochasticException::test( + value >= -2. && value <= 2., "[TruncatedGaussian] - value out of bounds" ); } } @@ -110,7 +112,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); geode::RandomEngine random_engine; test_double_sampler( random_engine ); diff --git a/tests/stochastic/sampling/direct/test-point-uniform-sampler.cpp b/tests/stochastic/sampling/direct/test-point-uniform-sampler.cpp index e662b74..5ebd9d8 100644 --- a/tests/stochastic/sampling/direct/test-point-uniform-sampler.cpp +++ b/tests/stochastic/sampling/direct/test-point-uniform-sampler.cpp @@ -44,8 +44,9 @@ void test_sample_ball( { auto value = geode::PointUniformSampler::sample< dimension >( engine, ball ); - OPENGEODE_EXCEPTION( geode::point_point_distance( value, ball.origin() ) - <= ball.radius() + geode::GLOBAL_EPSILON, + geode::OpenGeodeStochasticStochasticException::test( + geode::point_point_distance( value, ball.origin() ) + <= ball.radius() + geode::GLOBAL_EPSILON, "[PointUniformSampler] - point too far from ball center." ); } } @@ -58,7 +59,8 @@ void test_sample_bounding_box( { auto value = geode::PointUniformSampler::sample< dimension >( engine, box ); - OPENGEODE_EXCEPTION( box.contains( value ), + geode::OpenGeodeStochasticStochasticException::test( + box.contains( value ), "[PointUniformSampler] - point out of box." ); } } @@ -100,7 +102,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); test_point_sampling2D(); test_point_sampling3D(); diff --git a/tests/stochastic/sampling/direct/test-segment-uniform-sampler.cpp b/tests/stochastic/sampling/direct/test-segment-uniform-sampler.cpp index 3b289b3..2270f3a 100644 --- a/tests/stochastic/sampling/direct/test-segment-uniform-sampler.cpp +++ b/tests/stochastic/sampling/direct/test-segment-uniform-sampler.cpp @@ -50,9 +50,11 @@ void test_sample_segment( { auto value = geode::SegmentUniformSampler::sample( engine, box, length, az ); - OPENGEODE_EXCEPTION( box_enlarged.contains( value.vertices()[0] ), + geode::OpenGeodeStochasticStochasticException::test( + box_enlarged.contains( value.vertices()[0] ), "[SegmentUniformSampler] - segment out of box." ); - OPENGEODE_EXCEPTION( box_enlarged.contains( value.vertices()[1] ), + geode::OpenGeodeStochasticStochasticException::test( + box_enlarged.contains( value.vertices()[1] ), "[SegmentUniformSampler] - segment out of box." ); } } @@ -72,7 +74,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); test_segment_sampling2D(); diff --git a/tests/stochastic/sampling/mcmc/energy_terms/test-density-term.cpp b/tests/stochastic/sampling/mcmc/energy_terms/test-density-term.cpp index 217d430..db1b648 100644 --- a/tests/stochastic/sampling/mcmc/energy_terms/test-density-term.cpp +++ b/tests/stochastic/sampling/mcmc/energy_terms/test-density-term.cpp @@ -67,46 +67,47 @@ void run_density_test( double lambda, ( lambda > 0. ? neg_log_lambda * 2. : std::numeric_limits< double >::infinity() ); double total = term.total_log( pattern ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( total == expected_total, "[DensityTerm] total_log wrong" ); // --- Delta add inside VOI geode::Point2D p_inside{ { 0.5, 0.5 } }; geode::ObjectRef< geode::Point2D > ref_inside{ p_inside, set_id }; double delta = term.delta_log_add( pattern, ref_inside ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( delta == expected_add, "[DensityTerm] delta_log_add inside VOI wrong" ); // --- Delta add in buffer (outside VOI) geode::Point2D p_buffer{ { 1.3, 0.0 } }; geode::ObjectRef< geode::Point2D > ref_buffer{ p_buffer, set_id }; delta = term.delta_log_add( pattern, ref_buffer ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( delta == 0., "[DensityTerm] delta_log_add outside VOI wrong" ); // --- Delta remove anchored object geode::ObjectId obj_id{ 0, false, set_id }; delta = term.delta_log_remove( pattern, obj_id ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( delta == expected_remove, "[DensityTerm] delta_log_remove wrong" ); // --- Delta change anchored → buffer geode::ObjectRef< geode::Point2D > new_buffer{ p_buffer, set_id }; delta = term.delta_log_change( pattern, obj_id, new_buffer ); - OPENGEODE_EXCEPTION( delta == expected_remove, + geode::OpenGeodeStochasticStochasticException::test( + delta == expected_remove, "[DensityTerm] delta_log_change anchored→buffer wrong" ); // --- Delta change anchored → anchored geode::Point2D p_anchored{ { 0.1, 0.1 } }; geode::ObjectRef< geode::Point2D > new_anchored{ p_anchored, set_id }; delta = term.delta_log_change( pattern, obj_id, new_anchored ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( delta == 0., "[DensityTerm] delta_log_change anchored→anchored wrong" ); // --- Delta change buffer → anchored geode::ObjectId buffer_id{ 2, false, set_id }; delta = term.delta_log_change( pattern, buffer_id, ref_inside ); - OPENGEODE_EXCEPTION( delta == expected_add, + geode::OpenGeodeStochasticStochasticException::test( delta == expected_add, "[DensityTerm] delta_log_change buffer→anchored wrong" ); } @@ -114,7 +115,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); geode::Logger::set_level( geode::Logger::LEVEL::debug ); geode::ObjectSets< geode::Point2D > pattern; @@ -134,7 +135,7 @@ int main() try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); geode::uuid set_id; auto domain = init_domain(); diff --git a/tests/stochastic/sampling/mcmc/energy_terms/test-gibbs-energy.cpp b/tests/stochastic/sampling/mcmc/energy_terms/test-gibbs-energy.cpp index 8cdc90a..dd95573 100644 --- a/tests/stochastic/sampling/mcmc/energy_terms/test-gibbs-energy.cpp +++ b/tests/stochastic/sampling/mcmc/energy_terms/test-gibbs-energy.cpp @@ -63,38 +63,44 @@ void test_gibbs_energy() std::move( interaction ), domain ) ); geode_unused( pwterm_id ); - OPENGEODE_EXCEPTION( energy_terms.size() == 2, + geode::OpenGeodeStochasticStochasticException::test( + energy_terms.size() == 2, "[test gibbs] Wrong number of components after adding terms." ); geode::GibbsEnergy< geode::Point2D > gibbs_energy( energy_terms ); // Check total log-energy is finite double total_energy = gibbs_energy.total_log_energy( pattern ); - OPENGEODE_EXCEPTION( std::isfinite( total_energy ), + geode::OpenGeodeStochasticStochasticException::test( + std::isfinite( total_energy ), "[test gibbs] Total energy should be finite." ); // Add new point to test delta_add geode::Point2D p3{ { 2., 2. } }; 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 ), + geode::OpenGeodeStochasticStochasticException::test( + std::isfinite( delta_add ), "[test gibbs] Delta add should be finite." ); geode::ObjectId obj_id{ 0, false, set_id }; // Remove point test double delta_remove = gibbs_energy.delta_log_remove( pattern, obj_id ); - OPENGEODE_EXCEPTION( std::isfinite( delta_remove ), + geode::OpenGeodeStochasticStochasticException::test( + std::isfinite( delta_remove ), "[test gibbs] Delta remove should be finite." ); // Change point test double delta_change = gibbs_energy.delta_log_change( pattern, obj_id, p_ref ); - OPENGEODE_EXCEPTION( std::isfinite( delta_change ), + geode::OpenGeodeStochasticStochasticException::test( + std::isfinite( delta_change ), "[test gibbs] Delta change should be finite." ); // Clear components and verify energy_terms.clear(); - OPENGEODE_EXCEPTION( energy_terms.size() == 0, + geode::OpenGeodeStochasticStochasticException::test( + energy_terms.size() == 0, "[test gibbs] Components not cleared properly." ); } @@ -102,7 +108,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); test_gibbs_energy(); geode::Logger::info( "MH TEST GIBBS ENERGY SUCCESS" ); return 0; diff --git a/tests/stochastic/sampling/mcmc/energy_terms/test-intensity-term.cpp b/tests/stochastic/sampling/mcmc/energy_terms/test-intensity-term.cpp index 0a3f14f..6899c5c 100644 --- a/tests/stochastic/sampling/mcmc/energy_terms/test-intensity-term.cpp +++ b/tests/stochastic/sampling/mcmc/energy_terms/test-intensity-term.cpp @@ -85,7 +85,7 @@ void run_intensity_test( double lambda, // --- Total log double total = term.total_log( pattern ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( total == expected_total, "[IntensityTerm] total_log wrong" ); // --- Delta add (segment fully inside) @@ -98,7 +98,7 @@ void run_intensity_test( double lambda, : std::numeric_limits< double >::infinity() ); double delta = term.delta_log_add( pattern, ref_inside ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( delta == expected_add, "[IntensityTerm] delta_log_add inside wrong" ); // --- Delta add outside domain @@ -107,7 +107,7 @@ void run_intensity_test( double lambda, geode::ObjectRef< geode::OwnerSegment2D > ref_out{ s_outside, set_id }; delta = term.delta_log_add( pattern, ref_out ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( delta == 0.0, "[IntensityTerm] delta_log_add outside wrong" ); // --- Delta remove (first segment) @@ -115,18 +115,19 @@ void run_intensity_test( double lambda, double expected_remove = ( lambda > 0. ? -expected_add : 0.0 ); delta = term.delta_log_remove( pattern, obj_id ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( delta == expected_remove, "[IntensityTerm] delta_log_remove wrong" ); // --- Delta change: inside → outside delta = term.delta_log_change( pattern, obj_id, ref_out ); - OPENGEODE_EXCEPTION( delta == expected_remove, + geode::OpenGeodeStochasticStochasticException::test( + delta == expected_remove, "[IntensityTerm] delta_log_change inside→outside wrong" ); // --- Delta change: outside → inside geode::ObjectId buffer_id{ 2, false, set_id }; delta = term.delta_log_change( pattern, buffer_id, ref_inside ); - OPENGEODE_EXCEPTION( delta == expected_add, + geode::OpenGeodeStochasticStochasticException::test( delta == expected_add, "[IntensityTerm] delta_log_change outside→inside wrong" ); // --- Delta change: inside → inside (same length) @@ -135,7 +136,7 @@ void run_intensity_test( double lambda, geode::ObjectRef< geode::OwnerSegment2D > ref_same{ s_same, set_id }; delta = term.delta_log_change( pattern, obj_id, ref_same ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( delta == 0.0, "[IntensityTerm] delta_log_change inside→inside wrong" ); } @@ -143,7 +144,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); geode::Logger::set_level( geode::Logger::LEVEL::debug ); geode::ObjectSets< geode::OwnerSegment2D > pattern; diff --git a/tests/stochastic/sampling/mcmc/energy_terms/test-pairwise-term.cpp b/tests/stochastic/sampling/mcmc/energy_terms/test-pairwise-term.cpp index a94a04f..fc48579 100644 --- a/tests/stochastic/sampling/mcmc/energy_terms/test-pairwise-term.cpp +++ b/tests/stochastic/sampling/mcmc/energy_terms/test-pairwise-term.cpp @@ -67,7 +67,7 @@ void test_pairwise_term( double gamma, // --- total_log double total = term.total_log( pattern ); // Only VOI-anchored interactions counted in statistic: p1-p2 - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( total == term.contribution( 1 ), "[PairwiseTerm] total_log wrong" ); // --- delta_add VOI → VOI @@ -75,7 +75,8 @@ void test_pairwise_term( double gamma, geode::ObjectRef< geode::Point2D > p4_ref{ p4, set_id }; double delta = term.delta_log_add( pattern, p4_ref ); // interacts with p1 and p2 → 2 pairs - OPENGEODE_EXCEPTION( delta == term.contribution( 2 ), + geode::OpenGeodeStochasticStochasticException::test( + delta == term.contribution( 2 ), "[PairwiseTerm] delta_log_add VOI wrong" ); // --- delta_add buffer → buffer (outside VOI, inside buffer) @@ -84,14 +85,16 @@ void test_pairwise_term( double gamma, delta = term.delta_log_add( pattern, buffer_ref ); // energy counts interactions: buffer interacts with p2,p3 (p3 is in // buffer) → 2 pairs - OPENGEODE_EXCEPTION( delta == term.contribution( 1 ), + geode::OpenGeodeStochasticStochasticException::test( + delta == term.contribution( 1 ), "[PairwiseTerm] delta_log_add buffer wrong" ); // --- delta_change VOI → VOI geode::ObjectId obj_id{ 0, false, set_id }; // p1 delta = term.delta_log_change( pattern, obj_id, p4_ref ); // p1 replaced by p4: interacts with p2 → add 1 interaction - OPENGEODE_EXCEPTION( delta == term.contribution( 1 ), + geode::OpenGeodeStochasticStochasticException::test( + delta == term.contribution( 1 ), "[PairwiseTerm] delta_log_change VOI->VOI wrong" ); // --- delta_change buffer → VOI @@ -101,7 +104,8 @@ void test_pairwise_term( double gamma, // added // (+p4 pairs (+2)) net change = 1 double expected_delta = term.contribution( 1 ); // adjust based on count - OPENGEODE_EXCEPTION( delta == expected_delta, + geode::OpenGeodeStochasticStochasticException::test( + delta == expected_delta, "[PairwiseTerm] delta_log_change buffer->VOI wrong" ); // --- delta_change VOI → buffer @@ -109,19 +113,22 @@ void test_pairwise_term( double gamma, // p1 → buffer: p1 old interaction = 0 // p4 → p_buffer interaction with p2 +1 expected_delta = term.contribution( 1 ); - OPENGEODE_EXCEPTION( delta == expected_delta, + geode::OpenGeodeStochasticStochasticException::test( + delta == expected_delta, "[PairwiseTerm] delta_log_change VOI->buffer wrong" ); // --- delta_remove VOI delta = term.delta_log_remove( pattern, obj_id ); // p1 removed: no interaction with p2 removed - OPENGEODE_EXCEPTION( delta == term.contribution( 0 ), + geode::OpenGeodeStochasticStochasticException::test( + delta == term.contribution( 0 ), "[PairwiseTerm] delta_log_remove VOI wrong" ); // --- statistic (only anchored objects in VOI) double stat = term.statistic( pattern ); // p1,p2 anchored → 1 pair - OPENGEODE_EXCEPTION( stat == 1., "[PairwiseTerm] statistic wrong" ); + geode::OpenGeodeStochasticStochasticException::test( + stat == 1., "[PairwiseTerm] statistic wrong" ); } void test_pairwise_term_zero_gamma( double gamma, @@ -137,61 +144,63 @@ void test_pairwise_term_zero_gamma( double gamma, // --- total_log double total = term.total_log( pattern ); - OPENGEODE_EXCEPTION( std::isinf( total ), + geode::OpenGeodeStochasticStochasticException::test( std::isinf( total ), "[PairwiseTerm] total_log with gamma p4_ref{ p4, set_id }; double delta = term.delta_log_add( pattern, p4_ref ); - OPENGEODE_EXCEPTION( std::isinf( delta ), + geode::OpenGeodeStochasticStochasticException::test( std::isinf( delta ), "[PairwiseTerm] delta_log_add with gamma buffer_ref{ p_buffer, set_id }; delta = term.delta_log_add( pattern, buffer_ref ); - OPENGEODE_EXCEPTION( std::isinf( delta ), + geode::OpenGeodeStochasticStochasticException::test( std::isinf( delta ), "[PairwiseTerm] delta_log_add buffer with " "gammaVOI with " "gammaVOI with " "gammabuffer with " "gamma pattern; diff --git a/tests/stochastic/sampling/mcmc/helpers/test-simulation_monitor.cpp b/tests/stochastic/sampling/mcmc/helpers/test-simulation_monitor.cpp index e007017..ed4512c 100644 --- a/tests/stochastic/sampling/mcmc/helpers/test-simulation_monitor.cpp +++ b/tests/stochastic/sampling/mcmc/helpers/test-simulation_monitor.cpp @@ -42,25 +42,26 @@ namespace monitor.add_realization( { 2.0, 3.0, 4.0 } ); // --- Check count - OPENGEODE_EXCEPTION( monitor.statiscal_count() == 2, "Count mismatch" ); + geode::OpenGeodeStochasticStochasticException::test( + monitor.statiscal_count() == 2, "Count mismatch" ); const auto& means = monitor.means(); const auto& variances = monitor.variances(); // --- Check means - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( std::fabs( means[0] - 1.5 ) < 1e-12, "Mean[0] incorrect" ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( std::fabs( means[1] - 2.5 ) < 1e-12, "Mean[1] incorrect" ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( std::fabs( means[2] - 3.5 ) < 1e-12, "Mean[2] incorrect" ); // --- Check variances - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( std::fabs( variances[0] - 0.5 ) < 1e-12, "Variance[0] incorrect" ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( std::fabs( variances[1] - 0.5 ) < 1e-12, "Variance[1] incorrect" ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( std::fabs( variances[2] - 0.5 ) < 1e-12, "Variance[2] incorrect" ); geode::Logger::info( "--> SUCCESS!" ); @@ -71,7 +72,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); geode::Logger::set_level( geode::Logger::LEVEL::debug ); test_statistics_monitor_basic(); return 0; diff --git a/tests/stochastic/sampling/mcmc/helpers/test-simulation_printer.cpp b/tests/stochastic/sampling/mcmc/helpers/test-simulation_printer.cpp index babe258..a69d147 100644 --- a/tests/stochastic/sampling/mcmc/helpers/test-simulation_printer.cpp +++ b/tests/stochastic/sampling/mcmc/helpers/test-simulation_printer.cpp @@ -44,19 +44,21 @@ namespace const std::filesystem::path temp_folder = config.output_folder; const auto stats_path = temp_folder / config.statistics_filename; - OPENGEODE_EXCEPTION( std::filesystem::exists( stats_path ), + geode::OpenGeodeStochasticStochasticException::test( + std::filesystem::exists( stats_path ), "Statistics file not created" ); std::ifstream stats_file( stats_path ); std::string line; std::getline( stats_file, line ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( line == "# Simulation Statistics", "Header not correctly written" ); std::getline( stats_file, line ); - OPENGEODE_EXCEPTION( line == "EnergyTerm1;EnergyTerm2;EnergyTerm3", + geode::OpenGeodeStochasticStochasticException::test( + line == "EnergyTerm1;EnergyTerm2;EnergyTerm3", "Header not correctly written" ); std::getline( stats_file, line ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( line == "1 ; 2.5 ; 3.7", "Statistics line not correctly written" ); geode::Logger::info( "--> Success!" ); @@ -78,21 +80,22 @@ namespace const auto summary_path = temp_folder / config.statistics_summary_filename; - OPENGEODE_EXCEPTION( std::filesystem::exists( summary_path ), + geode::OpenGeodeStochasticStochasticException::test( + std::filesystem::exists( summary_path ), "Summary file not created" ); std::ifstream summary_file( summary_path ); std::string content( ( std::istreambuf_iterator< char >( summary_file ) ), std::istreambuf_iterator< char >() ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( content.find( "EnergyTerm1;EnergyTerm2" ) != std::string::npos, "Energy term names missing" ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( content.find( "2" ) != std::string::npos, "Count missing" ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( content.find( "1 ; 2" ) != std::string::npos, "Means missing" ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( content.find( "0 ; 0" ) != std::string::npos, "Variances missing" ); geode::Logger::info( "--> Success!" ); @@ -116,7 +119,8 @@ namespace const std::filesystem::path temp_folder = config.output_folder; const auto obj_path = temp_folder / "pattern_0.txt"; - OPENGEODE_EXCEPTION( std::filesystem::exists( obj_path ), + geode::OpenGeodeStochasticStochasticException::test( + std::filesystem::exists( obj_path ), "Object sets file not created" ); geode::Logger::info( "--> Success!" ); @@ -128,7 +132,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); geode::Logger::set_level( geode::Logger::LEVEL::debug ); const std::filesystem::path temp_folder = diff --git a/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp b/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp index 713cd3a..37d25f6 100644 --- a/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp +++ b/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp @@ -77,19 +77,20 @@ namespace { case geode::MoveType::Birth: saw_birth = true; - OPENGEODE_EXCEPTION( proposed_move.new_object.has_value(), + geode::OpenGeodeStochasticStochasticException::test( + proposed_move.new_object.has_value(), "[test proposal] Birth must provide new_object." ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( !proposed_move.old_object_id.has_value(), "[test proposal] Birth should not provide index." ); // Probabilities - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( proposed_move.proposal_probabilities.log_forward_prob <= 0.0, "[test proposal] Birth forward log-prob must be <= " "0." ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( std::abs( proposed_move.proposal_probabilities .log_backward_prob @@ -102,18 +103,19 @@ namespace case geode::MoveType::Death: saw_death = true; - OPENGEODE_EXCEPTION( !proposed_move.new_object.has_value(), + geode::OpenGeodeStochasticStochasticException::test( + !proposed_move.new_object.has_value(), "[test proposal] Death should not provide " "new_object." ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( proposed_move.old_object_id.has_value(), "[test proposal] Death must provide index." ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( proposed_move.old_object_id.value() < config.nb_objects_in_set( set_id ), "[test proposal] Death index out of bounds." ); // Probabilities - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( proposed_move.proposal_probabilities.log_forward_prob <= 0.0, "[test proposal] Death forward log-prob must be <= " @@ -122,12 +124,13 @@ namespace case geode::MoveType::Change: saw_change = true; - OPENGEODE_EXCEPTION( proposed_move.new_object.has_value(), + geode::OpenGeodeStochasticStochasticException::test( + proposed_move.new_object.has_value(), "[test proposal] Change must provide new_object." ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( proposed_move.old_object_id.has_value(), "[test proposal] Change must provide index." ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( proposed_move.old_object_id.value() < config.nb_objects_in_set( set_id ), "[test proposal] Change index out of bounds." ); @@ -135,24 +138,26 @@ namespace case geode::MoveType::Invalid: default: - throw geode::OpenGeodeException( + throw geode::OpenGeodeStochasticStochasticException( + nullptr, geode::OpenGeodeException::TYPE::data, "[test proposal] Proposal type Invalid." ); } // Log probabilities must be finite proposed move should be // possible. - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( std::isfinite( proposed_move.proposal_probabilities.log_forward_prob ), "[test proposal] Forward probability is not finite - Move" ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( std::isfinite( proposed_move.proposal_probabilities.log_backward_prob ), "[test proposal] Backward probability is not finite." ); } // Ensure kernel actually produced all types of proposals - OPENGEODE_EXCEPTION( saw_birth && saw_death && saw_change, + geode::OpenGeodeStochasticStochasticException::test( + saw_birth && saw_death && saw_change, "[test proposal] Kernel did not produce all move types." ); // --- Edge case: empty object_set --- @@ -164,7 +169,7 @@ namespace auto proposal = empty_kernel->propose( empty_config, engine ); const auto& proposed_move = proposal.proposed_move; - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( proposed_move.type == geode::MoveType::Birth || proposed_move.type == geode::MoveType::Invalid, "[test proposal] On empty config, only Birth should be possible." ); @@ -174,7 +179,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); test_proposal_kernel(); } catch( ... ) diff --git a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp index 4acc3b5..a307bf1 100644 --- a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp +++ b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp @@ -32,14 +32,14 @@ namespace void test_acceptance_prob_helper() { // log_accept >= 0 → prob = 1 - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( geode::MetropolisHastings< geode::Point2D >::acceptance_prob_helper( 0.5 ) == 1.0, "[MH test] acceptance_prob_helper wrong for positive log_accept." ); // very negative → prob = 0 - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( geode::MetropolisHastings< geode::Point2D >::acceptance_prob_helper( -800.0 ) == 0.0, @@ -49,14 +49,15 @@ namespace double val = geode::MetropolisHastings< geode::Point2D >::acceptance_prob_helper( -1.0 ); - OPENGEODE_EXCEPTION( std::abs( val - std::exp( -1.0 ) ) < 1e-12, + geode::OpenGeodeStochasticStochasticException::test( + std::abs( val - std::exp( -1.0 ) ) < 1e-12, "[MH test] acceptance_prob_helper wrong for -1.0." ); } void test_beta_setter( geode::MetropolisHastings< geode::Point2D >& mh ) { mh.set_beta( 0.5 ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( mh.beta() == 0.5, "[MH test] beta not set correctly." ); bool exception_thrown = false; @@ -68,7 +69,7 @@ namespace { exception_thrown = true; } - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( exception_thrown, "[MH test] negative beta did not throw." ); } @@ -90,7 +91,7 @@ namespace auto result = mh.step( state, engine ); // Invariant: fixed object must remain - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( result.decision == geode::MHDecision::Accepted || result.decision == geode::MHDecision::Rejected, "[MH test] decision should be Accepted or Rejected." ); @@ -153,7 +154,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); geode::Point2D min_point{ { 0., 0. } }; geode::Point2D max_point{ { 10., 10. } }; @@ -189,7 +190,8 @@ int main() state.add_object( geode::Point2D{ { 3., 3. } }, set_id, true ); test_steps( mh, state ); - // OPENGEODE_EXCEPTION( state.get_set( set_id ).nb_fixed_objects() == 2 + // geode::OpenGeodeStochasticStochasticException::test( state.get_set( + // set_id ).nb_fixed_objects() == 2 // ); test_beta_setter( mh ); diff --git a/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp b/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp index eaea6a7..656b3be 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp @@ -67,7 +67,7 @@ namespace runner.initialize(); - // OPENGEODE_EXCEPTION( + // geode::OpenGeodeStochasticStochasticException::test( // runner.state_realization().nb_fixed_objects() == 2 ); // run simulation geode::SimulationPrinterConfigurator printer_config; @@ -83,7 +83,7 @@ namespace auto statistic_monitoring = runner.run( engine, sim_config ); runner.check_statistics( statistic_monitoring ); - // OPENGEODE_EXCEPTION( + // geode::OpenGeodeStochasticStochasticException::test( // runner.state_realization().nb_fixed_objects() == 2 ); geode::Logger::info( "--> SUCCESS!" ); } @@ -173,7 +173,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); geode::Logger::set_level( geode::Logger::LEVEL::debug ); test_fracture_simulator(); test_two_fracture_sets_simulator(); diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 4415275..944311b 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -129,9 +129,9 @@ namespace std::abs( computed_means[stat_id] - expected_means ) / expected_means; - OPENGEODE_EXCEPTION( target_vs_mean_error < 0.05, - "[MH test] statistic value ", computed_means[stat_id], - " for energy term: ", + geode::OpenGeodeStochasticStochasticException::test( + target_vs_mean_error < 0.05, "[MH test] statistic value ", + computed_means[stat_id], " for energy term: ", term.name().value_or( term.id().string() ), " not close enough to expected value ", expected_means, " --> error : ", target_vs_mean_error ); @@ -140,7 +140,8 @@ namespace std::abs( computed_variances[stat_id] - expected_means ) / expected_means; - OPENGEODE_EXCEPTION( target_vs_variance_error < 0.15, + geode::OpenGeodeStochasticStochasticException::test( + target_vs_variance_error < 0.15, "[MH test] variance of statistic ", computed_variances[stat_id], " for energy term: ", term.name().value_or( term.id().string() ), @@ -264,7 +265,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); geode::Logger::set_level( geode::Logger::LEVEL::debug ); test_single_type_poisson(); test_multitype_poisson(); diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index e5e3eb0..14d11e6 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -177,9 +177,9 @@ namespace target_vs_mean_error /= expected_mean; } - OPENGEODE_EXCEPTION( target_vs_mean_error < 0.1, - "[MH test] Statistic value ", computed_means[stat_id], - " for energy term: ", + geode::OpenGeodeStochasticStochasticException::test( + target_vs_mean_error < 0.1, "[MH test] Statistic value ", + computed_means[stat_id], " for energy term: ", term.name().value_or( term.id().string() ), " not close enough to expected value ", expected_mean, " --> error: ", target_vs_mean_error ); @@ -339,7 +339,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); geode::Logger::set_level( geode::Logger::LEVEL::debug ); test_single_type_strauss(); test_multitype_strauss(); diff --git a/tests/stochastic/sampling/test-random-engine.cpp b/tests/stochastic/sampling/test-random-engine.cpp index 06d8f81..2960b19 100644 --- a/tests/stochastic/sampling/test-random-engine.cpp +++ b/tests/stochastic/sampling/test-random-engine.cpp @@ -52,8 +52,9 @@ void test_reproducibility() // Sample more values to check sequence reproducibility for( const auto value : geode::Range{ 100 } ) { - OPENGEODE_ASSERT( engine1.sample_uniform( dist ) - == engine2.sample_uniform( dist ), + geode::OpenGeodeStochasticStochasticException::test( + engine1.sample_uniform( dist ) + == engine2.sample_uniform( dist ), "[REPRODUCIBILITY] - Same seed should produce same output." ); } geode::Logger::info( "Test Reproducibility: ", seed, " SUCCESS " ); @@ -94,7 +95,8 @@ void test_uniform( Type value = engine.sample_uniform( dist_closed ); samples.emplace_back( value ); - OPENGEODE_ASSERT( value >= min_value && value <= max_value, + geode::OpenGeodeStochasticStochasticException::test( + value >= min_value && value <= max_value, "[Uniform] - value out of range." ); } double mean = compute_mean( samples ); @@ -104,9 +106,10 @@ void test_uniform( double expected_var = ( ( max_value - min_value + 1 ) * ( max_value - min_value + 1 ) - 1 ) / 12.0; - OPENGEODE_ASSERT( mean > expected_mean - 0.1 && mean < expected_mean + 0.1, + geode::OpenGeodeStochasticStochasticException::test( + mean > expected_mean - 0.1 && mean < expected_mean + 0.1, "[Uniform] - Wrong expected mean." ); - OPENGEODE_ASSERT( + geode::OpenGeodeStochasticStochasticException::test( variance > expected_var - 0.1 && variance < expected_var + 0.1, "[Uniform] - Wrong expected std." ); geode::Logger::info( "Test Uniform ", @@ -134,9 +137,10 @@ void test_gaussian( double expected_mean = mean_value; double expected_var = std_value * std_value; - OPENGEODE_ASSERT( mean > expected_mean - 0.1 && mean < expected_mean + 0.1, + geode::OpenGeodeStochasticStochasticException::test( + mean > expected_mean - 0.1 && mean < expected_mean + 0.1, "[Gaussian] - Wrong expected mean." ); - OPENGEODE_ASSERT( + geode::OpenGeodeStochasticStochasticException::test( variance > expected_var - 0.1 && variance < expected_var + 0.1, "[Gaussian] - Wrong expected std." ); geode::Logger::info( "Test Gaussian ", @@ -168,7 +172,7 @@ void test_truncated_gaussian( double mean_value, { double value = engine.sample_truncated_gaussian( spec ); samples.emplace_back( value ); - OPENGEODE_ASSERT( + geode::OpenGeodeStochasticStochasticException::test( value >= min && value <= max, "[Gaussian] - value out of range." ); } @@ -184,9 +188,10 @@ void test_truncated_gaussian( double mean_value, double expected_mean = mean_value; double expected_var = std_value * std_value; - OPENGEODE_ASSERT( mean > expected_mean - 0.1 && mean < expected_mean + 0.1, + geode::OpenGeodeStochasticStochasticException::test( + mean > expected_mean - 0.1 && mean < expected_mean + 0.1, "[Gaussian] - Wrong expected mean." ); - OPENGEODE_ASSERT( + geode::OpenGeodeStochasticStochasticException::test( variance > expected_var - 0.1 && variance < expected_var + 0.1, "[Gaussian] - Wrong expected std." ); geode::Logger::info( "Test Gaussian ", @@ -210,7 +215,7 @@ void test_bernoulli( const double empirical_probability = static_cast< double >( success_count ) / NUMBER_OF_SAMPLES; - OPENGEODE_ASSERT( + geode::OpenGeodeStochasticStochasticException::test( abs( empirical_probability - probability_of_success ) < 0.05, "[Bernoulli] - Empirical probability out of tolerance." ); @@ -223,7 +228,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); geode::RandomEngine random_engine; test_reproducibility(); diff --git a/tests/stochastic/spatial/test-object-set.cpp b/tests/stochastic/spatial/test-object-set.cpp index fb10ea2..7ece443 100644 --- a/tests/stochastic/spatial/test-object-set.cpp +++ b/tests/stochastic/spatial/test-object-set.cpp @@ -29,7 +29,7 @@ namespace { geode::ObjectSet< geode::Point2D > set; - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( set.empty(), "[TestObjectSet] - Set should start empty" ); const auto idx0 = @@ -39,14 +39,15 @@ namespace const auto idx2 = set.add_fixed_object( geode::Point2D{ { 2.0, 2.0 } } ); - OPENGEODE_EXCEPTION( set.nb_objects() == 3, + geode::OpenGeodeStochasticStochasticException::test( + set.nb_objects() == 3, "[TestObjectSet] - Set size should be 3 after insertions" ); - OPENGEODE_EXCEPTION( !set.empty(), + geode::OpenGeodeStochasticStochasticException::test( !set.empty(), "[TestObjectSet] - Set should not be empty after insertions" ); const auto& p = set.get_fixed_object( idx1 ); const auto result = geode::Point2D{ { 1.0, 1.0 } }; - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( p == result, "[TestObjectSet] - Wrong object value at index 1" ); } @@ -61,7 +62,7 @@ namespace const auto& updated = set.get_free_object( idx1 ); const auto new_point = geode::Point2D{ { 5.0, 5.0 } }; - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( updated == new_point, "[TestObjectSet] - Object update failed" ); } @@ -74,12 +75,13 @@ namespace set.remove_free_object( 0 ); // remove the second object (free) - OPENGEODE_EXCEPTION( set.nb_objects() == 2, + geode::OpenGeodeStochasticStochasticException::test( + set.nb_objects() == 2, "[TestObjectSet] - Set size should be 2 after removal" ); const auto& last = set.get_free_object( 0 ); const auto result = geode::Point2D{ { 2.0, 2.0 } }; - OPENGEODE_EXCEPTION( last == result, + geode::OpenGeodeStochasticStochasticException::test( last == result, "[TestObjectSet] - Remaining objects not shifted " "properly after removal" ); } @@ -92,7 +94,7 @@ namespace const auto& const_set = set; const auto& p = const_set.get_fixed_object( 0 ); const auto result = geode::Point2D{ { 10.0, 10.0 } }; - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( p == result, "[TestObjectSet] - Const access mismatch" ); } @@ -103,7 +105,8 @@ namespace set.add_free_object( geode::Point2D{ { 1.0, 1.0 } } ); const auto desc = set.string(); - OPENGEODE_EXCEPTION( desc.find( "2 objects" ) != std::string::npos, + geode::OpenGeodeStochasticStochasticException::test( + desc.find( "2 objects" ) != std::string::npos, "[TestObjectSet] - string() output incorrect" ); } @@ -117,33 +120,42 @@ namespace auto f1 = set.add_fixed_object( geode::Point2D{ { 1., 1. } } ); // ici u0 est changé? doije retourner l indice? - OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 2, "2 fixed objects" ); - OPENGEODE_EXCEPTION( set.nb_free_objects() == 1, "one free object" ); - OPENGEODE_EXCEPTION( f0 == 0 && f1 == 1 ); + geode::OpenGeodeStochasticStochasticException::test( + set.nb_fixed_objects() == 2, "2 fixed objects" ); + geode::OpenGeodeStochasticStochasticException::test( + set.nb_free_objects() == 1, "one free object" ); + geode::OpenGeodeStochasticStochasticException::test( + f0 == 0 && f1 == 1 ); // Add 2 free objects auto u1 = set.add_free_object( geode::Point2D{ { 2., 2. } } ); auto u2 = set.add_free_object( geode::Point2D{ { 3., 3. } } ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( set.nb_fixed_objects() == 2, "still two fixed object" ); - OPENGEODE_EXCEPTION( set.nb_free_objects() == 3, "three free objects" ); - OPENGEODE_EXCEPTION( u1 == 1 && u2 == 2 ); + geode::OpenGeodeStochasticStochasticException::test( + set.nb_free_objects() == 3, "three free objects" ); + geode::OpenGeodeStochasticStochasticException::test( + u1 == 1 && u2 == 2 ); // Remove first fixed object // set.remove_fixed_object( 0 ); // Invariant - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( set.nb_free_objects() == 3, "still 3 free objects" ); - OPENGEODE_EXCEPTION( set.nb_objects() == 5, "5 total objects" ); + geode::OpenGeodeStochasticStochasticException::test( + set.nb_objects() == 5, "5 total objects" ); // Remove a free object set.remove_free_object( 2 ); - OPENGEODE_EXCEPTION( set.nb_fixed_objects() == 2, "still one fixed" ); - OPENGEODE_EXCEPTION( set.nb_free_objects() == 2, "two remining free" ); - OPENGEODE_EXCEPTION( set.nb_objects() == 4, "4 object at last" ); + geode::OpenGeodeStochasticStochasticException::test( + set.nb_fixed_objects() == 2, "still one fixed" ); + geode::OpenGeodeStochasticStochasticException::test( + set.nb_free_objects() == 2, "two remining free" ); + geode::OpenGeodeStochasticStochasticException::test( + set.nb_objects() == 4, "4 object at last" ); } } // namespace @@ -152,7 +164,7 @@ int main() try { geode::Logger::info( "TEST ObjectSet" ); - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); test_add_and_access(); test_update_object(); diff --git a/tests/stochastic/spatial/test-object-sets.cpp b/tests/stochastic/spatial/test-object-sets.cpp index c7b8685..848bea8 100644 --- a/tests/stochastic/spatial/test-object-sets.cpp +++ b/tests/stochastic/spatial/test-object-sets.cpp @@ -32,7 +32,7 @@ namespace const auto set_id1 = sets.add_set( "default_name" ); const auto set_id2 = sets.add_set( "default_name" ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( sets.nb_sets() == 2, "[TestObjectSets] - Expected 2 sets" ); const auto obj_a = @@ -42,16 +42,19 @@ namespace const auto obj_c = sets.add_object( geode::Point2D{ { 5.0, 5.0 } }, set_id2, false ); - OPENGEODE_EXCEPTION( sets.nb_objects_in_set( set_id1 ) == 2, + geode::OpenGeodeStochasticStochasticException::test( + 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, + geode::OpenGeodeStochasticStochasticException::test( + sets.nb_objects_in_set( set_id2 ) == 1, "[TestObjectSets] - Set 2 should have 1 object" ); - OPENGEODE_EXCEPTION( sets.nb_objects() == 3, + geode::OpenGeodeStochasticStochasticException::test( + sets.nb_objects() == 3, "[TestObjectSets] - Total object count mismatch" ); const auto& point = sets.get_object( obj_b ); const auto expected = geode::Point2D{ { 1.0, 1.0 } }; - OPENGEODE_EXCEPTION( point == expected, + geode::OpenGeodeStochasticStochasticException::test( point == expected, "[TestObjectSets] - Wrong object value retrieved" ); } void test_update_free_object() @@ -66,7 +69,7 @@ namespace const auto& updated = sets.get_object( obj ); auto result = geode::Point2D{ { 9.0, 9.0 } }; - OPENGEODE_EXCEPTION( updated == result, + geode::OpenGeodeStochasticStochasticException::test( updated == result, "[TestObjectSets] - Updating free object failed" ); } void test_remove_objects() @@ -80,13 +83,15 @@ namespace sets.remove_free_object( { 1, false, set_id } ); - OPENGEODE_EXCEPTION( sets.nb_objects_in_set( set_id ) == 2, + geode::OpenGeodeStochasticStochasticException::test( + sets.nb_objects_in_set( set_id ) == 2, "[TestObjectSets] - Expected 2 objects after free removal" ); // Now remove the last remaining free object using remove_object() sets.remove_free_object( { 1, false, set_id } ); - OPENGEODE_EXCEPTION( sets.nb_objects_in_set( set_id ) == 1, + geode::OpenGeodeStochasticStochasticException::test( + sets.nb_objects_in_set( set_id ) == 1, "[TestObjectSets] - Expected 1 object after second removal" ); } void test_get_all_objects() @@ -100,7 +105,7 @@ namespace const auto all = sets.get_all_object(); - OPENGEODE_EXCEPTION( all.size() == 2, + geode::OpenGeodeStochasticStochasticException::test( all.size() == 2, "[TestObjectSets] - get_all_objects size mismatch" ); } @@ -120,10 +125,12 @@ namespace const auto near_neighbors = sets.neighbors( obj0, targeted_set_ids, 2.0 ); - OPENGEODE_EXCEPTION( near_neighbors.size() == 1, + geode::OpenGeodeStochasticStochasticException::test( + 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, + geode::OpenGeodeStochasticStochasticException::test( + near_neighbors[0].set_id == obj1.set_id + && near_neighbors[0].index == obj1.index, "[TestObjectSets] - Wrong neighbor identified for obj0" ); } @@ -140,7 +147,7 @@ namespace std::vector< uuid > targeted_set_ids{ set_id }; const auto nearby = sets.neighbors( query, targeted_set_ids, 1.0 ); - OPENGEODE_EXCEPTION( nearby.size() == 2, + geode::OpenGeodeStochasticStochasticException::test( nearby.size() == 2, "[TestObjectSets] - Expected 2 neighbors near query object" ); } @@ -152,7 +159,8 @@ namespace sets.add_object( geode::Point2D{ { 1.0, 1.0 } }, set_id, false ); const auto desc = sets.string(); - OPENGEODE_EXCEPTION( desc.find( "objects" ) != std::string::npos, + geode::OpenGeodeStochasticStochasticException::test( + desc.find( "objects" ) != std::string::npos, "[TestObjectSets] - string() output should mention objects" ); } } // namespace @@ -162,7 +170,7 @@ int main() try { geode::Logger::info( "TEST ObjectSets" ); - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); geode::Logger::set_level( geode::Logger::LEVEL::debug ); test_add_sets_and_objects(); diff --git a/tests/stochastic/spatial/test-pairwise-interactions.cpp b/tests/stochastic/spatial/test-pairwise-interactions.cpp index d605a40..8208e2d 100644 --- a/tests/stochastic/spatial/test-pairwise-interactions.cpp +++ b/tests/stochastic/spatial/test-pairwise-interactions.cpp @@ -38,7 +38,7 @@ namespace double result = interaction.evaluate( geode::ObjectRef< geode::Point2D >{ p1, id }, geode::ObjectRef< geode::Point2D >{ p2, id } ); - OPENGEODE_EXCEPTION( result == 1.0, + geode::OpenGeodeStochasticStochasticException::test( result == 1.0, "[EuclideanCutoffInteraction] Failed for inside cutoff case." ); } void test_no_interaction() @@ -52,7 +52,7 @@ namespace double result = interaction.evaluate( geode::ObjectRef< geode::Point2D >{ p1, id }, geode::ObjectRef< geode::Point2D >{ p2, id } ); - OPENGEODE_EXCEPTION( result == 0.0, + geode::OpenGeodeStochasticStochasticException::test( result == 0.0, "[EuclideanCutoffInteraction] Failed for outside cutoff case." ); } void test_limit_interaction() @@ -66,7 +66,7 @@ namespace double result = interaction.evaluate( geode::ObjectRef< geode::Point2D >{ p1, id }, geode::ObjectRef< geode::Point2D >{ p2, id } ); - OPENGEODE_EXCEPTION( result == 1.0, + geode::OpenGeodeStochasticStochasticException::test( result == 1.0, "[EuclideanCutoffInteraction] Failed for exact cutoff case." ); } } // namespace @@ -75,7 +75,7 @@ int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); test_interaction(); test_no_interaction(); diff --git a/tests/stochastic/spatial/test-spatial-domain.cpp b/tests/stochastic/spatial/test-spatial-domain.cpp index e87285d..34f34bf 100644 --- a/tests/stochastic/spatial/test-spatial-domain.cpp +++ b/tests/stochastic/spatial/test-spatial-domain.cpp @@ -42,37 +42,40 @@ void test_spatial_domain_2D() auto domain = init_domain(); // Test volumes - OPENGEODE_EXCEPTION( - domain.n_volume() == 1.0, "[Test] Domain volume wrong." ); - OPENGEODE_EXCEPTION( domain.extended_n_volume() == 4, - "[Test] Extended domain volume wrong." ); // (1+0.5*2)^2 + geode::OpenGeodeStochasticStochasticException::test( + domain.n_volume() == 1.0, "Domain volume wrong." ); + geode::OpenGeodeStochasticStochasticException::test( + domain.extended_n_volume() == 4, + "Extended domain volume wrong." ); // (1+0.5*2)^2 // Test points inside domain geode::Point2D inside{ { 0.5, 0.5 } }; geode::Point2D boundary{ { 1., 1. } }; geode::Point2D outside{ { 1.4, 1.4 } }; - OPENGEODE_EXCEPTION( - domain.contains( inside ), "[Test] Point inside should be contained." ); - OPENGEODE_EXCEPTION( domain.contains( boundary ), - "[Test] Boundary point should be contained." ); - OPENGEODE_EXCEPTION( !domain.contains( outside ), - "[Test] Point outside should not be contained." ); + geode::OpenGeodeStochasticStochasticException::test( + domain.contains( inside ), "Point inside should be contained." ); + geode::OpenGeodeStochasticStochasticException::test( + domain.contains( boundary ), "Boundary point should be contained." ); + geode::OpenGeodeStochasticStochasticException::test( + !domain.contains( outside ), "Point outside should not be contained." ); - OPENGEODE_EXCEPTION( domain.extended_contains( outside ), - "[Test] Point outside original but in buffer should be contained." ); + geode::OpenGeodeStochasticStochasticException::test( + domain.extended_contains( outside ), + "Point outside original but in buffer should be contained." ); // Test SpatialDomainChecker for points - OPENGEODE_EXCEPTION( SpatialDomainChecker< Point2D >::is_anchored_in_domain( - domain, inside ), - "[Test] anchored_in_domain failed for inside point" ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( + SpatialDomainChecker< Point2D >::is_anchored_in_domain( + domain, inside ), + "anchored_in_domain failed for inside point" ); + geode::OpenGeodeStochasticStochasticException::test( !SpatialDomainChecker< Point2D >::is_anchored_in_domain( domain, outside ), - "[Test] anchored_in_domain failed for outside point" ); - OPENGEODE_EXCEPTION( + "anchored_in_domain failed for outside point" ); + geode::OpenGeodeStochasticStochasticException::test( SpatialDomainChecker< Point2D >::intersects_domain( domain, boundary ), - "[Test] intersects_domain failed for boundary point" ); + "intersects_domain failed for boundary point" ); // Test segments geode::OwnerSegment2D seg_inside{ geode::Point2D{ { 0.1, 0.1 } }, @@ -86,48 +89,48 @@ void test_spatial_domain_2D() geode::OwnerSegment2D seg_cross{ geode::Point2D{ { -0.5, 0.5 } }, geode::Point2D{ { 1.5, 0.5 } } }; - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( SpatialDomainChecker< OwnerSegment2D >::is_anchored_in_domain( domain, seg_inside ), - "[Test] Segment inside VOI should be anchored." ); - OPENGEODE_EXCEPTION( + "Segment inside VOI should be anchored." ); + geode::OpenGeodeStochasticStochasticException::test( SpatialDomainChecker< OwnerSegment2D >::is_anchored_in_domain( domain, seg_partial_anchored ), - "[Test] Segment with lower left extremity inside VOI should be " + "Segment with lower left extremity inside VOI should be " "anchored." ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( !SpatialDomainChecker< OwnerSegment2D >::is_anchored_in_domain( domain, seg_partial ), - "[Test] Segment with lower left extremity outside VOI is not anchored " + "Segment with lower left extremity outside VOI is not anchored " "by definition." ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( !SpatialDomainChecker< OwnerSegment2D >::is_anchored_in_domain( domain, seg_outside ), - "[Test] Segment completely outside should not be anchored." ); + "Segment completely outside should not be anchored." ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( SpatialDomainChecker< OwnerSegment2D >::intersects_domain( domain, seg_inside ), - "[Test] Segment inside VOI should intersect." ); - OPENGEODE_EXCEPTION( + "Segment inside VOI should intersect." ); + geode::OpenGeodeStochasticStochasticException::test( SpatialDomainChecker< OwnerSegment2D >::intersects_domain( domain, seg_partial ), - "[Test] Segment partially in VOI should intersect." ); - OPENGEODE_EXCEPTION( + "Segment partially in VOI should intersect." ); + geode::OpenGeodeStochasticStochasticException::test( !SpatialDomainChecker< OwnerSegment2D >::intersects_domain( domain, seg_outside ), - "[Test] Segment completely outside should not intersect." ); - OPENGEODE_EXCEPTION( + "Segment completely outside should not intersect." ); + geode::OpenGeodeStochasticStochasticException::test( SpatialDomainChecker< OwnerSegment2D >::intersects_domain( domain, seg_cross ), - "[Test] Segment crossing the voi should intersect." ); + "Segment crossing the voi should intersect." ); } int main() { try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); geode::Logger::set_level( geode::Logger::LEVEL::debug ); test_spatial_domain_2D(); From 14e205554ca3915e2b3ac9ef6bc4ecaf44ee738e Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 28 Apr 2026 11:40:05 +0200 Subject: [PATCH 06/17] static monitoring --- .../inference/statistic_monitor.hpp | 51 +++++++++++++ .../statistic_objective.hpp} | 30 ++++---- .../inference/statistic_validator.hpp | 54 ++++++++++++++ .../target_statistic.hpp} | 26 ++----- .../models/energy_term_collection.hpp | 60 ++++++++-------- .../energy_terms/energy_term_builder.hpp | 3 +- .../energy_terms/single_object_term.hpp | 1 - .../{model_configuration.hpp => model.hpp} | 60 ++++++++++++++-- .../geode/stochastic/spatial/object_sets.hpp | 8 ++- src/geode/stochastic/CMakeLists.txt | 11 +-- .../models/energy_terms/intensity_term.cpp | 45 ------------ src/geode/stochastic/spatial/object_sets.cpp | 50 ++++++------- tests/stochastic/CMakeLists.txt | 14 ++-- .../models/energy_terms/test-density-term.cpp | 66 +++++++++++------ .../energy_terms/test-intensity-term.cpp | 71 +++++++++++-------- .../sampling/mcmc/test-mh-poisson-new.cpp | 11 +-- .../sampling/mcmc/test-mh-poisson.cpp | 60 ++++++++++------ 17 files changed, 391 insertions(+), 230 deletions(-) create mode 100644 include/geode/stochastic/inference/statistic_monitor.hpp rename include/geode/stochastic/{models/energy_terms/density_term.hpp => inference/statistic_objective.hpp} (66%) create mode 100644 include/geode/stochastic/inference/statistic_validator.hpp rename include/geode/stochastic/{models/energy_terms/intensity_term.hpp => inference/target_statistic.hpp} (64%) rename include/geode/stochastic/models/{model_configuration.hpp => model.hpp} (55%) delete mode 100644 src/geode/stochastic/models/energy_terms/intensity_term.cpp diff --git a/include/geode/stochastic/inference/statistic_monitor.hpp b/include/geode/stochastic/inference/statistic_monitor.hpp new file mode 100644 index 0000000..4159e8d --- /dev/null +++ b/include/geode/stochastic/inference/statistic_monitor.hpp @@ -0,0 +1,51 @@ +#pragma once +#include +#include + +#include + +namespace geode +{ + + class StatMonitor + { + public: + void add_realization( + const absl::flat_hash_map< uuid, double >& values ) + { + ++count_; + for( const auto& [term_uuid, value] : values ) + { + auto& mean = means_[term_uuid]; + auto& m2 = m2_[term_uuid]; // somme des carrés + + double delta = value - mean; + mean += delta / count_; + double delta2 = value - mean; + m2 += delta * delta2; + } + } + + const index_t statiscal_count() const + { + return count_; + } + + double mean( const uuid& id ) const + { + return means_.at( id ); + } + + double variance( const uuid& id ) const + { + if( count_ < 2 ) + return 0.0; + return m2_.at( id ) / ( count_ - 1 ); + } + + private: + absl::flat_hash_map< uuid, double > means_; + absl::flat_hash_map< uuid, double > m2_; + index_t count_{ 0 }; + }; +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/models/energy_terms/density_term.hpp b/include/geode/stochastic/inference/statistic_objective.hpp similarity index 66% rename from include/geode/stochastic/models/energy_terms/density_term.hpp rename to include/geode/stochastic/inference/statistic_objective.hpp index 320242b..d6abf65 100644 --- a/include/geode/stochastic/models/energy_terms/density_term.hpp +++ b/include/geode/stochastic/inference/statistic_objective.hpp @@ -22,25 +22,31 @@ */ #pragma once -#include -#include +#include +#include +#include namespace geode { + template < typename ObjectType > - class DensityTerm : public SingleObjectTerm< ObjectType > + class StatisticObjective { public: - explicit DensityTerm( std::string_view name, - double lambda, - std::vector< uuid > targeted_set_ids, - const SpatialDomain< ObjectType::dim >& domain ) - : SingleObjectTerm< ObjectType >( name, - lambda, - std::move( targeted_set_ids ), - domain, - std::make_unique< ObjectInDomainFeature< ObjectType > >() ) + double compute_loss( const StatMonitor& monitor, + const std::vector< TargetStatistic >& targets ) const { + double loss = 0.0; + + for( const auto& target : targets ) + { + const double mean = monitor.mean( target.term_id ); + const double diff = mean - target.value; + + loss += diff * diff; + } + + return loss; } }; } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/inference/statistic_validator.hpp b/include/geode/stochastic/inference/statistic_validator.hpp new file mode 100644 index 0000000..94eab14 --- /dev/null +++ b/include/geode/stochastic/inference/statistic_validator.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 - 2026 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 + +namespace geode +{ + template < typename ObjectType > + class StatisticsValidator + { + public: + void check( const StatisticsMonitor& monitor, + const std::vector< TargetStatistic >& targets ) const + { + for( const auto& target : targets ) + { + const double mean = monitor.mean( target.term_id ); + const double rel_error = std::abs( mean - target.value ) + / ( std::abs( target.value ) + 1e-12 ); + // OPENGEODE_EXCEPTION( rel_error < t.tolerance, + // "[StatisticsValidator] Failure for term ", + // t.term_id.string(), "\n mean = ", + // mean, + // "\n target = ", target.value, "\n error + // = ", rel_error, + // "\n tol = ", target.tolerance ); + } + } + }; +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/models/energy_terms/intensity_term.hpp b/include/geode/stochastic/inference/target_statistic.hpp similarity index 64% rename from include/geode/stochastic/models/energy_terms/intensity_term.hpp rename to include/geode/stochastic/inference/target_statistic.hpp index f6edc37..9d61e14 100644 --- a/include/geode/stochastic/models/energy_terms/intensity_term.hpp +++ b/include/geode/stochastic/inference/target_statistic.hpp @@ -22,28 +22,12 @@ */ #pragma once -#include - -#include - -namespace geode -{ - FORWARD_DECLARATION_DIMENSION_CLASS( OwnerSegment ); - ALIAS_2D( OwnerSegment ); - FORWARD_DECLARATION_DIMENSION_CLASS( SpatialDomain ); - -} // namespace geode - namespace geode { - class opengeode_stochastic_stochastic_api IntensityTerm - : public SingleObjectTerm< OwnerSegment2D > + struct TargetStatistic { - public: - explicit IntensityTerm( std::string_view name, - double lambda, - std::vector< uuid > targeted_set_ids, - double caracteristic_length, - const SpatialDomain< OwnerSegment2D::dim >& domain ); + geode::uuid term_id; + double value; + double tolerance{ 0.05 }; }; -} // namespace geode \ No newline at end of file +} // namespace geode diff --git a/include/geode/stochastic/models/energy_term_collection.hpp b/include/geode/stochastic/models/energy_term_collection.hpp index 42e81b3..8ec9f7f 100644 --- a/include/geode/stochastic/models/energy_term_collection.hpp +++ b/include/geode/stochastic/models/energy_term_collection.hpp @@ -18,36 +18,30 @@ namespace geode EnergyTermCollection& operator=( EnergyTermCollection&& ) = default; - [[nodiscard]] uuid add_energy_term( + uuid add_energy_term( std::unique_ptr< EnergyTerm< ObjectType > >&& term ) { - const uuid term_uuid = term->id(); auto term_idx = energy_terms_.size(); - energy_terms_.emplace_back( std::move( term ) ); - energy_terms_map_.emplace( term_uuid, term_idx ); - return term_uuid; - } - [[nodiscard]] bool remove_energy_term( const uuid& term_id ) - { - auto term_it = energy_terms_map_.find( term_id ); - if( term_it == energy_terms_map_.end() ) - { - return false; - } - index_t idx = term_it->second; - index_t last = energy_terms_.size() - 1; - std::swap( energy_terms_[idx], energy_terms_[last] ); - energy_terms_map_[energy_terms_[idx]->id()] = idx; - energy_terms_.pop_back(); - energy_terms_map_.erase( term_it ); - return true; - } + const auto term_name = term->name(); + OPENGEODE_EXCEPTION( term_name.has_value(), + absl::StrCat( "[EnergyTermCollection]- Energy Term name is not " + "defined." ) ); + const auto term_uuid = term->id(); + auto [it, inserted_uuid] = + name_to_uuid_.emplace( term_name.value(), term_uuid ); + OPENGEODE_EXCEPTION( inserted_uuid, + absl::StrCat( "[EnergyTermCollection]- Energy Term named ", + term_name.value(), " already exists." ) ); - void clear() - { - energy_terms_.clear(); - energy_terms_map_.clear(); + auto [it2, inserted_index] = + uuid_to_index_.emplace( term_uuid, term_idx ); + OPENGEODE_EXCEPTION( inserted_index, + absl::StrCat( "[EnergyTermCollection]- Energy Term ", + term_uuid.string(), " already exists." ) ); + + energy_terms_.emplace_back( std::move( term ) ); + return term_uuid; } [[nodiscard]] index_t size() const @@ -58,13 +52,22 @@ namespace geode [[nodiscard]] const EnergyTerm< ObjectType >& get( const uuid& term_id ) const { - auto term_it = energy_terms_map_.find( term_id ); - OPENGEODE_EXCEPTION( term_it != energy_terms_map_.end(), + auto term_it = uuid_to_index_.find( term_id ); + OPENGEODE_EXCEPTION( term_it != uuid_to_index_.end(), absl::StrCat( "[EnergyTermCollection] Unknown energy term: ", term_id.string() ) ); return *energy_terms_[term_it->second]; } + [[nodiscard]] uuid get_term_uuid( std::string_view name ) const + { + auto uuid_it = name_to_uuid_.find( name ); + OPENGEODE_EXCEPTION( uuid_it != name_to_uuid_.end(), + absl::StrCat( + "[EnergyTermCollection] Unknown energy term: ", name ) ); + return uuid_it->second; + } + [[nodiscard]] const std::vector< std::unique_ptr< EnergyTerm< ObjectType > > >& energy_terms() const @@ -84,9 +87,10 @@ namespace geode } private: + absl::flat_hash_map< std::string, uuid > name_to_uuid_; + absl::flat_hash_map< uuid, index_t > uuid_to_index_; std::vector< std::unique_ptr< EnergyTerm< ObjectType > > > energy_terms_; - absl::flat_hash_map< uuid, index_t > energy_terms_map_; }; } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp b/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp index 7d49be6..dd1e237 100644 --- a/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp @@ -30,9 +30,10 @@ #include #include -#include +#include #include #include +#include #include #include diff --git a/include/geode/stochastic/models/energy_terms/single_object_term.hpp b/include/geode/stochastic/models/energy_terms/single_object_term.hpp index 9c3c5e6..ae00091 100644 --- a/include/geode/stochastic/models/energy_terms/single_object_term.hpp +++ b/include/geode/stochastic/models/energy_terms/single_object_term.hpp @@ -97,7 +97,6 @@ namespace geode double sum = 0.0; this->for_each_object_in_sets( state, this->impacted_set_ids(), [&]( const ObjectId& obj_id ) { - DEBUG( sum ); const auto& obj = state.get_object( obj_id ); sum += feature_->evaluate( obj, this->domain() ); } ); diff --git a/include/geode/stochastic/models/model_configuration.hpp b/include/geode/stochastic/models/model.hpp similarity index 55% rename from include/geode/stochastic/models/model_configuration.hpp rename to include/geode/stochastic/models/model.hpp index 06a4d8c..fad76a2 100644 --- a/include/geode/stochastic/models/model_configuration.hpp +++ b/include/geode/stochastic/models/model.hpp @@ -35,6 +35,8 @@ #include #include +#include + #include #include namespace geode @@ -46,8 +48,55 @@ namespace geode }; template < typename ObjectType > - EnergyTermCollection< ObjectType > build_energy_term_collection( - const ModelConfig& config, + class Model + { + OPENGEODE_DISABLE_COPY( Model ); + + public: + Model() = delete; + Model( EnergyTermCollection< ObjectType >&& energy_terms ) + : terms_( std::move( energy_terms ) ), energy_{ terms_ } + { + } + + const EnergyTermCollection< ObjectType >& terms() const + { + return terms_; + } + + const GibbsEnergy< ObjectType >& energy() const + { + return energy_; + } + + absl::flat_hash_map< uuid, double > compute_statistics( + const ObjectSets< ObjectType >& state ) const + { + absl::flat_hash_map< uuid, double > stats; + stats.reserve( terms_.size() ); + + for( const auto& term_ptr : terms_.energy_terms() ) + { + stats.emplace( term_ptr->id(), term_ptr->statistic( state ) ); + } + + return stats; + } + + absl::flat_hash_map< uuid, double > compute_statistics( + const ObjectSets< ObjectType >& state, const uuid& term_uuid ) const + { + const auto& term = terms_.get( term_uuid ); + return term.statistic( state ); + } + + private: + EnergyTermCollection< ObjectType > terms_; + GibbsEnergy< ObjectType > energy_; + }; + + template < typename ObjectType > + Model< ObjectType > build_model( const ModelConfig& config, const ObjectSets< ObjectType >& object_sets, const SpatialDomain< ObjectType::dim >& domain ) { @@ -55,11 +104,10 @@ namespace geode for( const auto& term_cfg : config.terms ) { - auto term_id = - collection.add_energy_term( build_energy_term< ObjectType >( - term_cfg, object_sets, domain ) ); + collection.add_energy_term( build_energy_term< ObjectType >( + term_cfg, object_sets, domain ) ); } - return collection; + return Model< ObjectType >{ std::move( collection ) }; } } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/spatial/object_sets.hpp b/include/geode/stochastic/spatial/object_sets.hpp index 886f1a1..e6af6e9 100644 --- a/include/geode/stochastic/spatial/object_sets.hpp +++ b/include/geode/stochastic/spatial/object_sets.hpp @@ -23,7 +23,7 @@ #pragma once -#include +#include #include @@ -88,8 +88,10 @@ namespace geode ObjectSet< Type >& get_set( const uuid& set_id ); private: - absl::btree_map< std::string, geode::uuid > object_set_name_to_uuid_; - absl::btree_map< uuid, ObjectSet< Type > > sets_; + absl::flat_hash_map< std::string, uuid > name_to_uuid_; + absl::flat_hash_map< uuid, index_t > uuid_to_index_; + std::vector< ObjectSet< Type > > sets_; + ObjectNeighborhood< Type::dim > neighborhood_; }; } // namespace geode diff --git a/src/geode/stochastic/CMakeLists.txt b/src/geode/stochastic/CMakeLists.txt index f461300..ffce372 100644 --- a/src/geode/stochastic/CMakeLists.txt +++ b/src/geode/stochastic/CMakeLists.txt @@ -34,10 +34,13 @@ add_geode_library( "sampling/direct/segment_uniform_sampler.cpp" "sampling/distributions.cpp" "sampling/random_engine.cpp" - "models/energy_terms/intensity_term.cpp" "common.cpp" PUBLIC_HEADERS - "inference/abc_shadow.hpp" + #"inference/abc_shadow.hpp" + "inference/statistic_objective.hpp" + "inference/statistic_monitor.hpp" + "inference/target_statistic.hpp" + "inference/statistic_validator.hpp" "spatial/object_set.hpp" "spatial/object_sets.hpp" "spatial/object_neighborhood.hpp" @@ -63,16 +66,14 @@ add_geode_library( #"sampling/mcmc/helpers/fracture_simulation_runner.hpp" "sampling/mcmc/helpers/simulation_printer.hpp" "sampling/mcmc/helpers/simulation_monitor.hpp" - "models/energy_terms/density_term.hpp" "models/energy_terms/energy_term.hpp" - "models/energy_terms/intensity_term.hpp" "models/energy_terms/pairwise_term.hpp" "models/energy_terms/single_object_term.hpp" "models/energy_terms/energy_term_config.hpp" "models/energy_terms/energy_term_builder.hpp" "models/energy_term_collection.hpp" "models/gibbs_energy.hpp" - "models/model_configuration.hpp" + "models/model.hpp" "sampling/mcmc/proposal/classical_proposals.hpp" "sampling/mcmc/proposal/moves.hpp" "sampling/mcmc/proposal/proposal_kernel.hpp" diff --git a/src/geode/stochastic/models/energy_terms/intensity_term.cpp b/src/geode/stochastic/models/energy_terms/intensity_term.cpp deleted file mode 100644 index 0d08d45..0000000 --- a/src/geode/stochastic/models/energy_terms/intensity_term.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2019 - 2026 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 -#include - -namespace geode -{ - IntensityTerm::IntensityTerm( std::string_view name, - double lambda, - std::vector< uuid > impacted_set_ids, - double caracteristic_length, - const SpatialDomain< OwnerSegment2D::dim >& domain ) - : SingleObjectTerm< OwnerSegment2D >( name, - lambda, - std::move( impacted_set_ids ), - domain, - std::make_unique< SegmentLengthInsideBoxFeature >( - caracteristic_length ) ) - { - } -} // namespace geode \ No newline at end of file diff --git a/src/geode/stochastic/spatial/object_sets.cpp b/src/geode/stochastic/spatial/object_sets.cpp index 925d921..fe6fabb 100644 --- a/src/geode/stochastic/spatial/object_sets.cpp +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -11,10 +11,10 @@ namespace geode 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; + auto it = uuid_to_index_.find( set_id ); + OPENGEODE_EXCEPTION( it != uuid_to_index_.end(), + "[ObjectSet] - group (", set_id.string(), ") is not defined." ); + return sets_[it->second]; } template < typename Type > @@ -33,8 +33,9 @@ namespace geode { std::vector< ObjectId > result; result.reserve( nb_objects() ); - for( const auto& [set_id, objs] : sets_ ) + for( const auto& objs : sets_ ) { + auto set_id = objs.id(); for( const auto obj_id : geode::Range{ objs.nb_fixed_objects() } ) { result.push_back( { obj_id, true, set_id } ); @@ -81,9 +82,8 @@ namespace geode index_t ObjectSets< Type >::nb_objects() const { index_t nb_objects{ 0 }; - for( const auto& [set_id, objs] : sets_ ) + for( const auto& objs : sets_ ) { - geode_unused( set_id ); nb_objects += objs.nb_objects(); } return nb_objects; @@ -92,19 +92,22 @@ namespace geode template < typename Type > uuid ObjectSets< Type >::add_set( std::string_view name ) { - ObjectSet< Type > new_set; + auto set_index = sets_.size(); + auto& new_set = sets_.emplace_back( ObjectSet< Type >{} ); + const auto set_uuid = new_set.id(); + new_set.set_name( name ); - const auto new_set_id = new_set.id(); - auto [it_set_name, set_id_inserted] = - object_set_name_to_uuid_.emplace( name, new_set_id ); + auto [it_set_name, set_uuid_inserted] = + name_to_uuid_.emplace( name, set_uuid ); OPENGEODE_EXCEPTION( - set_id_inserted, absl::StrCat( "[ObjectSet]- group named ", name, - " already exists." ) ); - auto [it_set_id, set_inserted] = - sets_.emplace( new_set_id, std::move( new_set ) ); - OPENGEODE_EXCEPTION( set_inserted, "[ObjectSet]- group (", - new_set_id.string(), ") already exists." ); - return new_set_id; + set_uuid_inserted, absl::StrCat( "[ObjectSet]- group named ", name, + " already exists." ) ); + + auto [it_set_uuid, set_index_inserted] = + uuid_to_index_.emplace( set_uuid, set_index ); + OPENGEODE_EXCEPTION( set_index_inserted, "[ObjectSet]- group (", + set_uuid.string(), ") already exists." ); + return set_uuid; } template < typename Type > @@ -184,16 +187,15 @@ namespace geode } template < typename Type > - uuid ObjectSets< Type >::get_set_uuid( - const std::string_view set_name ) const + uuid ObjectSets< Type >::get_set_uuid( const std::string_view name ) const { - if( auto set_uuid = object_set_name_to_uuid_.find( set_name ); - set_uuid != object_set_name_to_uuid_.end() ) + if( auto set_uuid = name_to_uuid_.find( name ); + set_uuid != name_to_uuid_.end() ) { return set_uuid->second; } throw OpenGeodeException( - "[ObjectSets] ObjectSet uuid accessor - group named ", set_name, + "[ObjectSets] ObjectSet uuid accessor - group named ", name, " does not exist." ); } @@ -232,7 +234,7 @@ namespace geode { auto message = absl::StrCat( "ObjectSets with ", nb_objects(), " objects in, ", nb_sets(), " sets" ); - for( const auto& [set_id, objs] : sets_ ) + for( const auto& objs : sets_ ) { absl::StrAppend( &message, "\n\t --> ", objs.string() ); } diff --git a/tests/stochastic/CMakeLists.txt b/tests/stochastic/CMakeLists.txt index 3869ab5..6132307 100644 --- a/tests/stochastic/CMakeLists.txt +++ b/tests/stochastic/CMakeLists.txt @@ -145,13 +145,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-fractures.cpp" diff --git a/tests/stochastic/models/energy_terms/test-density-term.cpp b/tests/stochastic/models/energy_terms/test-density-term.cpp index 15e7862..64605db 100644 --- a/tests/stochastic/models/energy_terms/test-density-term.cpp +++ b/tests/stochastic/models/energy_terms/test-density-term.cpp @@ -20,19 +20,24 @@ * SOFTWARE. * */ -#include +#include +#include + +#include #include #include #include +const std::string set_name{ "segments" }; + geode::uuid init_object_set( geode::ObjectSets< geode::Point2D >& pattern ) { geode::Point2D p1{ { 0., 0. } }; geode::Point2D p2{ { 1., 1. } }; geode::Point2D p_buffer{ { 1.3, 0.1 } }; - auto set_id = pattern.add_set( "default_name" ); + auto set_id = pattern.add_set( set_name ); pattern.add_object( std::move( p1 ), set_id, false ); pattern.add_object( std::move( p2 ), set_id, false ); pattern.add_object( std::move( p_buffer ), set_id, false ); // buffer last @@ -48,14 +53,14 @@ geode::SpatialDomain< 2 > init_domain() return geode::SpatialDomain< 2 >{ box, 0.5 }; } -void run_density_test( double lambda, +void run_density_test( const geode::SingleObjectTermConfig& term_config, const geode::ObjectSets< geode::Point2D >& pattern, - const geode::uuid& set_id, const geode::SpatialDomain< 2 >& domain ) { - std::string denity_name{ "density" }; - geode::DensityTerm< geode::Point2D > term( - denity_name, lambda, { set_id }, domain ); + auto term = geode::build_energy_term< geode::Point2D >( + term_config, pattern, domain ); + auto lambda = term_config.lambda; + const auto& set_id = pattern.get_set_uuid( set_name ); auto neg_log_lambda = -std::log( lambda ); double expected_add = @@ -67,46 +72,46 @@ void run_density_test( double lambda, double expected_total = ( lambda > 0. ? neg_log_lambda * 2. : std::numeric_limits< double >::infinity() ); - double total = term.total_log( pattern ); + double total = term->total_log( pattern ); OPENGEODE_EXCEPTION( total == expected_total, "[DensityTerm] total_log wrong" ); // --- Delta add inside VOI geode::Point2D p_inside{ { 0.5, 0.5 } }; geode::ObjectRef< geode::Point2D > ref_inside{ p_inside, set_id }; - double delta = term.delta_log_add( pattern, ref_inside ); + double delta = term->delta_log_add( pattern, ref_inside ); OPENGEODE_EXCEPTION( delta == expected_add, "[DensityTerm] delta_log_add inside VOI wrong" ); // --- Delta add in buffer (outside VOI) geode::Point2D p_buffer{ { 1.3, 0.0 } }; geode::ObjectRef< geode::Point2D > ref_buffer{ p_buffer, set_id }; - delta = term.delta_log_add( pattern, ref_buffer ); + delta = term->delta_log_add( pattern, ref_buffer ); OPENGEODE_EXCEPTION( delta == 0., "[DensityTerm] delta_log_add outside VOI wrong" ); // --- Delta remove anchored object geode::ObjectId obj_id{ 0, false, set_id }; - delta = term.delta_log_remove( pattern, obj_id ); + delta = term->delta_log_remove( pattern, obj_id ); OPENGEODE_EXCEPTION( delta == expected_remove, "[DensityTerm] delta_log_remove wrong" ); // --- Delta change anchored → buffer geode::ObjectRef< geode::Point2D > new_buffer{ p_buffer, set_id }; - delta = term.delta_log_change( pattern, obj_id, new_buffer ); + delta = term->delta_log_change( pattern, obj_id, new_buffer ); OPENGEODE_EXCEPTION( delta == expected_remove, "[DensityTerm] delta_log_change anchored→buffer wrong" ); // --- Delta change anchored → anchored geode::Point2D p_anchored{ { 0.1, 0.1 } }; geode::ObjectRef< geode::Point2D > new_anchored{ p_anchored, set_id }; - delta = term.delta_log_change( pattern, obj_id, new_anchored ); + delta = term->delta_log_change( pattern, obj_id, new_anchored ); OPENGEODE_EXCEPTION( delta == 0., "[DensityTerm] delta_log_change anchored→anchored wrong" ); // --- Delta change buffer → anchored geode::ObjectId buffer_id{ 2, false, set_id }; - delta = term.delta_log_change( pattern, buffer_id, ref_inside ); + delta = term->delta_log_change( pattern, buffer_id, ref_inside ); OPENGEODE_EXCEPTION( delta == expected_add, "[DensityTerm] delta_log_change buffer→anchored wrong" ); } @@ -121,12 +126,22 @@ int main() geode::ObjectSets< geode::Point2D > pattern; auto set_id = init_object_set( pattern ); auto domain = init_domain(); + geode::SingleObjectTermConfig term_config; + geode::ObjectInDomainFeatureConfig object_density; + + term_config.term_name = "density"; + term_config.object_set_names = { set_name }; + term_config.lambda = 0.5; + term_config.object_feature = object_density; // Test different lambda values including near-zero - run_density_test( 0.5, pattern, set_id, domain ); - run_density_test( geode::GLOBAL_EPSILON, pattern, set_id, domain ); - run_density_test( 100.0021165, pattern, set_id, domain ); - run_density_test( 0., pattern, set_id, domain ); // zero lambda + run_density_test( term_config, pattern, domain ); + term_config.lambda = geode::GLOBAL_EPSILON; + run_density_test( term_config, pattern, domain ); + term_config.lambda = 100.0021165; + run_density_test( term_config, pattern, domain ); + term_config.lambda = 0.; + run_density_test( term_config, pattern, domain ); // zero lambda } catch( ... ) { @@ -136,11 +151,20 @@ int main() try { geode::StochasticLibrary::initialize(); - geode::uuid set_id; + geode::ObjectSets< geode::Point2D > pattern; + auto set_id = init_object_set( pattern ); auto domain = init_domain(); - geode::DensityTerm< geode::Point2D > term( - "zero", -geode::GLOBAL_EPSILON, { set_id }, domain ); + geode::SingleObjectTermConfig term_config; + + term_config.term_name = "zero"; + term_config.object_set_names = { set_name }; + term_config.lambda = -geode::GLOBAL_EPSILON; + term_config.object_feature = geode::ObjectInDomainFeatureConfig{}; + + auto term = geode::build_energy_term< geode::Point2D >( + term_config, pattern, domain ); + geode::Logger::info( "TEST FAILED" ); return 1; } diff --git a/tests/stochastic/models/energy_terms/test-intensity-term.cpp b/tests/stochastic/models/energy_terms/test-intensity-term.cpp index aeb3db0..bb93d99 100644 --- a/tests/stochastic/models/energy_terms/test-intensity-term.cpp +++ b/tests/stochastic/models/energy_terms/test-intensity-term.cpp @@ -20,12 +20,16 @@ * SOFTWARE. * */ -#include - #include +#include +#include + #include +#include #include +const std::string set_name{ "segments" }; + geode::uuid init_segment_set( geode::ObjectSets< geode::OwnerSegment2D >& pattern ) { @@ -43,7 +47,7 @@ geode::uuid init_segment_set( Segment s_buffer{ geode::Point2D{ { 2.0, 2.0 } }, geode::Point2D{ { 3.0, 3.0 } } }; // clipped = 0.0 - auto set_id = pattern.add_set( "segments" ); + auto set_id = pattern.add_set( set_name ); pattern.add_object( std::move( s1 ), set_id, false ); pattern.add_object( std::move( s2 ), set_id, false ); pattern.add_object( std::move( s_buffer ), set_id, false ); @@ -59,18 +63,16 @@ geode::SpatialDomain< 2 > init_domain() return geode::SpatialDomain< 2 >{ box, 0.5 }; } -void run_intensity_test( double lambda, - double characteristic_length, +void run_intensity_test( const geode::SingleObjectTermConfig& term_config, const geode::ObjectSets< geode::OwnerSegment2D >& pattern, - const geode::uuid& set_id, - const geode::SpatialDomain< 2 >& domain ) + const geode::SpatialDomain< 2 >& domain, + double characteristic_length ) { - geode::IntensityTerm term( - "intensity", lambda, { set_id }, characteristic_length, domain ); - + auto term = build_energy_term( term_config, pattern, domain ); + const auto& set_id = pattern.get_set_uuid( set_name ); const double neg_log_lambda = - ( lambda > 0. ? -std::log( lambda ) - : std::numeric_limits< double >::infinity() ); + ( term_config.lambda > 0. ? -std::log( term_config.lambda ) + : std::numeric_limits< double >::infinity() ); // Total clipped length inside domain: // s1: 1.0 @@ -80,11 +82,11 @@ void run_intensity_test( double lambda, const double scaled_total = total_length / characteristic_length; const double expected_total = - ( lambda > 0. ? neg_log_lambda * scaled_total - : std::numeric_limits< double >::infinity() ); + ( term_config.lambda > 0. ? neg_log_lambda * scaled_total + : std::numeric_limits< double >::infinity() ); // --- Total log - double total = term.total_log( pattern ); + double total = term->total_log( pattern ); OPENGEODE_EXCEPTION( total == expected_total, "[IntensityTerm] total_log wrong" ); @@ -94,10 +96,11 @@ void run_intensity_test( double lambda, geode::ObjectRef< geode::OwnerSegment2D > ref_inside{ s_inside, set_id }; double expected_add = - ( lambda > 0. ? neg_log_lambda * ( 1.0 / characteristic_length ) - : std::numeric_limits< double >::infinity() ); + ( term_config.lambda > 0. + ? neg_log_lambda * ( 1.0 / characteristic_length ) + : std::numeric_limits< double >::infinity() ); - double delta = term.delta_log_add( pattern, ref_inside ); + double delta = term->delta_log_add( pattern, ref_inside ); OPENGEODE_EXCEPTION( delta == expected_add, "[IntensityTerm] delta_log_add inside wrong" ); @@ -106,26 +109,26 @@ void run_intensity_test( double lambda, geode::Point2D{ { 3.0, 3.0 } } }; geode::ObjectRef< geode::OwnerSegment2D > ref_out{ s_outside, set_id }; - delta = term.delta_log_add( pattern, ref_out ); + delta = term->delta_log_add( pattern, ref_out ); OPENGEODE_EXCEPTION( delta == 0.0, "[IntensityTerm] delta_log_add outside wrong" ); // --- Delta remove (first segment) geode::ObjectId obj_id{ 0, false, set_id }; - double expected_remove = ( lambda > 0. ? -expected_add : 0.0 ); + double expected_remove = ( term_config.lambda > 0. ? -expected_add : 0.0 ); - delta = term.delta_log_remove( pattern, obj_id ); + delta = term->delta_log_remove( pattern, obj_id ); OPENGEODE_EXCEPTION( delta == expected_remove, "[IntensityTerm] delta_log_remove wrong" ); // --- Delta change: inside → outside - delta = term.delta_log_change( pattern, obj_id, ref_out ); + delta = term->delta_log_change( pattern, obj_id, ref_out ); OPENGEODE_EXCEPTION( delta == expected_remove, "[IntensityTerm] delta_log_change inside→outside wrong" ); // --- Delta change: outside → inside geode::ObjectId buffer_id{ 2, false, set_id }; - delta = term.delta_log_change( pattern, buffer_id, ref_inside ); + delta = term->delta_log_change( pattern, buffer_id, ref_inside ); OPENGEODE_EXCEPTION( delta == expected_add, "[IntensityTerm] delta_log_change outside→inside wrong" ); @@ -134,7 +137,7 @@ void run_intensity_test( double lambda, geode::Point2D{ { 0.8, 0.0 } } }; // length still 1 geode::ObjectRef< geode::OwnerSegment2D > ref_same{ s_same, set_id }; - delta = term.delta_log_change( pattern, obj_id, ref_same ); + delta = term->delta_log_change( pattern, obj_id, ref_same ); OPENGEODE_EXCEPTION( delta == 0.0, "[IntensityTerm] delta_log_change inside→inside wrong" ); } @@ -152,11 +155,21 @@ int main() const double L0 = 1.0; - run_intensity_test( 0.5, L0, pattern, set_id, domain ); - run_intensity_test( - geode::GLOBAL_EPSILON, L0, pattern, set_id, domain ); - run_intensity_test( 50.0, L0, pattern, set_id, domain ); - run_intensity_test( 0.0, L0, pattern, set_id, domain ); + geode::SingleObjectTermConfig term_config; + geode::SegmentLengthInsideBoxFeatureConfig object_length{ L0 }; + + term_config.term_name = "intensity"; + term_config.object_set_names = { set_name }; + term_config.lambda = 0.5; + term_config.object_feature = object_length; + + run_intensity_test( term_config, pattern, domain, L0 ); + term_config.lambda = geode::GLOBAL_EPSILON; + run_intensity_test( term_config, pattern, domain, L0 ); + term_config.lambda = 50.0; + run_intensity_test( term_config, pattern, domain, L0 ); + term_config.lambda = 0.0; + run_intensity_test( term_config, pattern, domain, L0 ); } catch( ... ) { diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp index fd9b6ff..f9a34c9 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp @@ -22,7 +22,7 @@ */ #include -#include +#include #include int main() { @@ -67,10 +67,11 @@ int main() box.add_point( geode::Point2D{ { 10.0, 10.0 } } ); geode::SpatialDomain domain( box, 0. ); - auto collection = geode::build_energy_term_collection< geode::Point2D >( - config, object_sets, domain ); - geode::Logger::info( collection.size() ); - OPENGEODE_EXCEPTION( collection.size() == 5, "Collection not created" ); + auto model = + geode::build_model< geode::Point2D >( config, object_sets, domain ); + geode::Logger::info( model.terms().size() ); + OPENGEODE_EXCEPTION( + model.terms().size() == 5, "Collection not created" ); return 0; } catch( ... ) diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index c6d0802..bf61c0b 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -21,13 +21,16 @@ * */ #include -#include +#include +#include +#include #include #include #include #include #include #include +#include namespace { struct SetDescription @@ -40,8 +43,7 @@ namespace struct PoissonDensityDescription { - std::string name; - double density; + geode::SingleObjectTermConfig density_term_config; double target_count; }; @@ -50,7 +52,7 @@ namespace { public: PoissonSimulationRunner( const geode::SpatialDomain< 2 >& domain ) - : geode::SimulationRunner< geode::Point2D >( domain ) {}; + : geode::SimulationRunner< geode::Point2D >( domain ){}; void add_set_descriptor( const SetDescription& descriptor ) { @@ -68,14 +70,10 @@ namespace auto proposal_kernel = std::make_unique< geode::ProposalKernel< geode::Point2D > >(); - // Mapping set names -> UUID - std::unordered_map< std::string, geode::uuid > name_to_uuid; - // Step 1: create object sets and samplers for( const auto& set_desc : set_descriptors_ ) { const auto set_id = this->object_sets_.add_set( set_desc.name ); - name_to_uuid[set_desc.name] = set_id; this->set_samplers_.push_back( std::make_unique< geode::UniformPointSetSampler< 2 > >( @@ -89,16 +87,10 @@ namespace // Step 2: create energy terms for( const auto& energy_desc : density_descriptors_ ) { - const auto set_id = name_to_uuid.at( energy_desc.name ); - this->ordered_energy_terms_.push_back( - this->energy_terms_collection_.add_energy_term( - std::make_unique< - geode::DensityTerm< geode::Point2D > >( - absl::StrCat( energy_desc.name, "_density" ), - energy_desc.density, - std::vector< geode::uuid >{ set_id }, - this->domain_ ) ) ); + this->energy_terms_collection_.add_energy_term( std::move( + build_energy_term( energy_desc.density_term_config, + this->object_sets_, this->domain_ ) ) ) ); this->ordered_target_statistics_.push_back( energy_desc.target_count ); @@ -181,9 +173,12 @@ namespace // --- Energy term description PoissonDensityDescription densityA; - densityA.name = "A"; - densityA.density = 0.3; + densityA.density_term_config.term_name = "density"; + densityA.density_term_config.object_set_names = { "A" }; + densityA.density_term_config.lambda = 0.3; densityA.target_count = 30.0; + densityA.density_term_config.object_feature = + geode::ObjectInDomainFeatureConfig{}; PoissonSimulationRunner runner( domain ); runner.add_set_descriptor( setA ); @@ -203,6 +198,7 @@ namespace sim_config.printer = printer_config; auto statistic_monitoring = runner.run( engine, sim_config ); + runner.check_statistics( statistic_monitoring ); } @@ -227,9 +223,29 @@ namespace SetDescription set03{ "set03", 4.0, 1.0, 1.0 }; // --- Energy term descriptions - PoissonDensityDescription density01{ "set01", 0.1, 10.0 }; - PoissonDensityDescription density02{ "set02", 0.4, 40.0 }; - PoissonDensityDescription density03{ "set03", 0.3, 30.0 }; + PoissonDensityDescription density01; + density01.density_term_config.term_name = "density01"; + density01.density_term_config.object_set_names = { "set01" }; + density01.density_term_config.lambda = 0.1; + density01.target_count = 10.0; + density01.density_term_config.object_feature = + geode::ObjectInDomainFeatureConfig{}; + + PoissonDensityDescription density02; + density02.density_term_config.term_name = "density02"; + density02.density_term_config.object_set_names = { "set02" }; + density02.density_term_config.lambda = 0.4; + density02.target_count = 40.0; + density02.density_term_config.object_feature = + geode::ObjectInDomainFeatureConfig{}; + + PoissonDensityDescription density03; + density03.density_term_config.term_name = "density03"; + density03.density_term_config.object_set_names = { "set03" }; + density03.density_term_config.lambda = 0.3; + density03.target_count = 30.0; + density03.density_term_config.object_feature = + geode::ObjectInDomainFeatureConfig{}; PoissonSimulationRunner runner( domain ); runner.add_set_descriptor( set01 ); From 964ff1f0895feb742cc6e9f3ab10264a60587372 Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Tue, 28 Apr 2026 09:40:36 +0000 Subject: [PATCH 07/17] Apply prepare changes --- tests/stochastic/sampling/mcmc/test-mh-poisson.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index bf61c0b..2f6c54f 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -52,7 +52,7 @@ namespace { public: PoissonSimulationRunner( const geode::SpatialDomain< 2 >& domain ) - : geode::SimulationRunner< geode::Point2D >( domain ){}; + : geode::SimulationRunner< geode::Point2D >( domain ) {}; void add_set_descriptor( const SetDescription& descriptor ) { From 7d99efefdfb5c2831cfc8257026b10ba402989be Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 28 Apr 2026 11:49:23 +0200 Subject: [PATCH 08/17] fix test uniform random sampler. --- tests/stochastic/sampling/test-random-engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stochastic/sampling/test-random-engine.cpp b/tests/stochastic/sampling/test-random-engine.cpp index 2960b19..e6d66cc 100644 --- a/tests/stochastic/sampling/test-random-engine.cpp +++ b/tests/stochastic/sampling/test-random-engine.cpp @@ -47,7 +47,7 @@ void test_reproducibility() // Define a uniform distribution geode::UniformClosed< int > dist; dist.min_value = 1; - dist.min_value = 100; + dist.max_value = 100; // Sample more values to check sequence reproducibility for( const auto value : geode::Range{ 100 } ) From 1519815d21edb06ba58c01c381a7c4635eafd131 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 28 Apr 2026 16:49:51 +0200 Subject: [PATCH 09/17] improve tests --- .../sampling/test-random-engine.cpp | 121 ++++++++++-------- 1 file changed, 65 insertions(+), 56 deletions(-) diff --git a/tests/stochastic/sampling/test-random-engine.cpp b/tests/stochastic/sampling/test-random-engine.cpp index e6d66cc..80e894c 100644 --- a/tests/stochastic/sampling/test-random-engine.cpp +++ b/tests/stochastic/sampling/test-random-engine.cpp @@ -29,7 +29,8 @@ #include #include -const int NUMBER_OF_SAMPLES = 10000; +const int NUMBER_OF_SAMPLES = 50000; +const double TOLERANCE = 0.1; void test_reproducibility() { @@ -73,13 +74,58 @@ template < typename T > double compute_variance( const std::vector< T >& data, double mean ) { double accum = 0.0; - for( auto x : data ) + for( auto& x : data ) { double diff = x - mean; accum += diff * diff; } return accum / ( data.size() - 1 ); } +template < typename T > +void test_distribution( const std::vector< T >& data, + double expected_mean, + double expected_var, + double k = 3.0 ) +{ + const auto n = data.size(); + const double mean = compute_mean( data ); + const double variance = compute_variance( data, mean ); + geode::Logger::info( "Test Distribution ", + ": mean / expected mean = ", mean, "/", expected_mean, + " variance / expected variance = ", variance, "/", expected_var ); + + const double se_mean = std::sqrt( expected_var / n ); + const double se_var = + std::sqrt( 2.0 * expected_var * expected_var / ( n - 1 ) ); + + geode::OpenGeodeStochasticStochasticException::test( + std::abs( mean - expected_mean ) < k * se_mean, + "[Uniform] - Wrong expected mean." ); + geode::OpenGeodeStochasticStochasticException::test( + std::abs( variance - expected_var ) < k * se_var, + "[Uniform] - Wrong expected std." ); +} + +template < typename T > +double compute_expected_variance( T min_value, T max_value ) +{ + if constexpr( std::is_integral_v< T > ) + { + const double n = static_cast< double >( max_value - min_value + 1 ); + return ( n * n - 1.0 ) / 12.0; + } + else if constexpr( std::is_floating_point_v< T > ) + { + const double d = static_cast< double >( max_value - min_value ); + return ( d * d ) / 12.0; + } + else + { + static_assert( + std::is_arithmetic_v< T >, "Unsupported type for UniformStats" ); + } +} + template < typename Type > void test_uniform( const Type min_value, const Type max_value, geode::RandomEngine& engine ) @@ -99,22 +145,11 @@ void test_uniform( value >= min_value && value <= max_value, "[Uniform] - value out of range." ); } - double mean = compute_mean( samples ); - double variance = compute_variance( samples, mean ); double expected_mean = ( min_value + max_value ) / 2.0; - double expected_var = - ( ( max_value - min_value + 1 ) * ( max_value - min_value + 1 ) - 1 ) - / 12.0; - geode::OpenGeodeStochasticStochasticException::test( - mean > expected_mean - 0.1 && mean < expected_mean + 0.1, - "[Uniform] - Wrong expected mean." ); - geode::OpenGeodeStochasticStochasticException::test( - variance > expected_var - 0.1 && variance < expected_var + 0.1, - "[Uniform] - Wrong expected std." ); - geode::Logger::info( "Test Uniform ", - ": SUCCESS - mean / expected mean = ", mean, "/", expected_mean, - " variance / expected variance = ", variance, "/", expected_var ); + double expected_var = compute_expected_variance( min_value, max_value ); + + test_distribution( samples, expected_mean, expected_var ); } void test_gaussian( @@ -131,21 +166,8 @@ void test_gaussian( double value = engine.sample_gaussian( spec ); samples.emplace_back( value ); } - double mean = compute_mean( samples ); - double variance = compute_variance( samples, mean ); - - double expected_mean = mean_value; - double expected_var = std_value * std_value; - geode::OpenGeodeStochasticStochasticException::test( - mean > expected_mean - 0.1 && mean < expected_mean + 0.1, - "[Gaussian] - Wrong expected mean." ); - geode::OpenGeodeStochasticStochasticException::test( - variance > expected_var - 0.1 && variance < expected_var + 0.1, - "[Gaussian] - Wrong expected std." ); - geode::Logger::info( "Test Gaussian ", - ": SUCCESS - mean / expected mean = ", mean, "/", expected_mean, - " variance / expected variance = ", variance, "/", expected_var ); + test_distribution( samples, mean_value, std_value ); } void test_truncated_gaussian( double mean_value, @@ -176,27 +198,7 @@ void test_truncated_gaussian( double mean_value, value >= min && value <= max, "[Gaussian] - value out of range." ); } - if( 2 * mean_value - min - max < geode::GLOBAL_EPSILON ) - { - geode::Logger::info( "Truncated Gaussian Distribution not symetric " - "cannot compute theoretic parameters. " ); - return; - } - double mean = compute_mean( samples ); - double variance = compute_variance( samples, mean ); - - double expected_mean = mean_value; - double expected_var = std_value * std_value; - - geode::OpenGeodeStochasticStochasticException::test( - mean > expected_mean - 0.1 && mean < expected_mean + 0.1, - "[Gaussian] - Wrong expected mean." ); - geode::OpenGeodeStochasticStochasticException::test( - variance > expected_var - 0.1 && variance < expected_var + 0.1, - "[Gaussian] - Wrong expected std." ); - geode::Logger::info( "Test Gaussian ", - ": SUCCESS - mean / expected mean = ", mean, "/", expected_mean, - " variance / expected variance = ", variance, "/", expected_var ); + // Implementer un test de Chi2 ou Kolmogorov–Smirnov } void test_bernoulli( @@ -215,13 +217,13 @@ void test_bernoulli( const double empirical_probability = static_cast< double >( success_count ) / NUMBER_OF_SAMPLES; + geode::Logger::info( "Test Bernoulli ", + ": empirical / expected = ", empirical_probability, " / ", + probability_of_success ); + geode::OpenGeodeStochasticStochasticException::test( abs( empirical_probability - probability_of_success ) < 0.05, "[Bernoulli] - Empirical probability out of tolerance." ); - - geode::Logger::info( "Test Bernoulli ", - ": SUCCESS - empirical / expected = ", empirical_probability, " / ", - probability_of_success ); } int main() @@ -232,28 +234,35 @@ int main() geode::RandomEngine random_engine; test_reproducibility(); + geode::Logger::info( "TEST UNIFORM SAMPLING" ); test_uniform< geode::index_t >( 1, 15, random_engine ); test_uniform< geode::local_index_t >( 1, 6, random_engine ); test_uniform< geode::signed_index_t >( -18, 6, random_engine ); test_uniform< float >( 15.545, 18.9524, random_engine ); test_uniform< double >( -100.54, 100.6, random_engine ); + geode::Logger::info( "TEST UNIFORM SAMPLING - SUCCESS" ); + geode::Logger::info( "TEST GAUSSIAN SAMPLING" ); test_gaussian( 0., 1., random_engine ); test_gaussian( -100., 1., random_engine ); + geode::Logger::info( "TEST GAUSSIAN SAMPLING - SUCCESS" ); + geode::Logger::info( "TEST TRUNCATED GAUSSIAN SAMPLING" ); test_truncated_gaussian( 0., 1., std::nullopt, std::nullopt, random_engine ); test_truncated_gaussian( 0., 1., std::nullopt, 1., random_engine ); test_truncated_gaussian( 0., 1., -1., std::nullopt, random_engine ); test_truncated_gaussian( 10., 1., 5., 15., random_engine ); + geode::Logger::info( "TEST TRUNCATED GAUSSIAN SAMPLING - SUCCESS" ); + geode::Logger::info( "TEST BERNOUILLI SAMPLING" ); test_bernoulli( 0.5, random_engine ); test_bernoulli( 0.1, random_engine ); test_bernoulli( 0.25, random_engine ); test_bernoulli( 0.06, random_engine ); test_bernoulli( 0.96, random_engine ); + geode::Logger::info( "TEST BERNOUILLI SAMPLING - SUCCESS" ); - geode::Logger::info( "TEST SUCCESS" ); return 0; } catch( ... ) From b112711a26221ff81e2bb34dc707131474aa97fc Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 28 Apr 2026 17:00:54 +0200 Subject: [PATCH 10/17] cleaning --- .../sampling/test-random-engine.cpp | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/stochastic/sampling/test-random-engine.cpp b/tests/stochastic/sampling/test-random-engine.cpp index 80e894c..49b0f53 100644 --- a/tests/stochastic/sampling/test-random-engine.cpp +++ b/tests/stochastic/sampling/test-random-engine.cpp @@ -29,8 +29,7 @@ #include #include -const int NUMBER_OF_SAMPLES = 50000; -const double TOLERANCE = 0.1; +const int NUMBER_OF_SAMPLES = 10000; void test_reproducibility() { @@ -82,7 +81,7 @@ double compute_variance( const std::vector< T >& data, double mean ) return accum / ( data.size() - 1 ); } template < typename T > -void test_distribution( const std::vector< T >& data, +void test_distribution_mean_and_variance( const std::vector< T >& data, double expected_mean, double expected_var, double k = 3.0 ) @@ -111,18 +110,19 @@ double compute_expected_variance( T min_value, T max_value ) { if constexpr( std::is_integral_v< T > ) { - const double n = static_cast< double >( max_value - min_value + 1 ); - return ( n * n - 1.0 ) / 12.0; + const double distance = + static_cast< double >( max_value - min_value + 1 ); + return ( distance * distance - 1.0 ) / 12.0; } else if constexpr( std::is_floating_point_v< T > ) { - const double d = static_cast< double >( max_value - min_value ); - return ( d * d ) / 12.0; + const double distance = static_cast< double >( max_value - min_value ); + return ( distance * distance ) / 12.0; } else { - static_assert( - std::is_arithmetic_v< T >, "Unsupported type for UniformStats" ); + static_assert( std::is_arithmetic_v< T >, + "Unsupported type to compute theoretical variance." ); } } @@ -149,7 +149,7 @@ void test_uniform( double expected_mean = ( min_value + max_value ) / 2.0; double expected_var = compute_expected_variance( min_value, max_value ); - test_distribution( samples, expected_mean, expected_var ); + test_distribution_mean_and_variance( samples, expected_mean, expected_var ); } void test_gaussian( @@ -167,7 +167,7 @@ void test_gaussian( samples.emplace_back( value ); } - test_distribution( samples, mean_value, std_value ); + test_distribution_mean_and_variance( samples, mean_value, std_value ); } void test_truncated_gaussian( double mean_value, From 028451f51dadef8c31b280e0aa26f609fa2a6652 Mon Sep 17 00:00:00 2001 From: Arnaud Botella Date: Tue, 5 May 2026 14:27:27 +0200 Subject: [PATCH 11/17] check_ --- .../object_set_sampler/point_set_sampler.hpp | 8 ++-- .../segment_set_sampler.hpp | 4 +- .../mcmc/energy_terms/energy_term.hpp | 4 +- .../energy_terms/energy_term_collection.hpp | 4 +- .../helpers/fracture_simulation_runner.hpp | 2 +- .../mcmc/helpers/simulation_monitor.hpp | 2 +- .../mcmc/metropolis_hasting_sampler.hpp | 7 ++-- .../mcmc/proposal/classical_proposals.hpp | 8 ++-- .../sampling/mcmc/proposal/moves.hpp | 6 +-- .../mcmc/proposal/proposal_kernel.hpp | 10 ++--- .../stochastic/spatial/spatial_domain.hpp | 8 ++-- .../sampling/direct/double_sampler.cpp | 14 +++---- .../stochastic/sampling/random_engine.cpp | 40 +++++++++---------- src/geode/stochastic/spatial/object_set.cpp | 10 ++--- src/geode/stochastic/spatial/object_sets.cpp | 20 +++++----- 15 files changed, 74 insertions(+), 73 deletions(-) diff --git a/include/geode/stochastic/sampling/direct/object_set_sampler/point_set_sampler.hpp b/include/geode/stochastic/sampling/direct/object_set_sampler/point_set_sampler.hpp index 9718f50..3f2145f 100644 --- a/include/geode/stochastic/sampling/direct/object_set_sampler/point_set_sampler.hpp +++ b/include/geode/stochastic/sampling/direct/object_set_sampler/point_set_sampler.hpp @@ -40,14 +40,14 @@ namespace geode : ObjectSetSampler< Point< dimension > >{}, domain_( domain ) { auto volume = domain_.extended_n_volume(); - OpenGeodeStochasticStochasticException::check( volume != 0., - nullptr, OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + volume != 0., nullptr, OpenGeodeException::TYPE::data, "[PointSetSampler] - Undefined Extended Bounding " "Box (volume ==0)." ); this->log_pdf_ = -std::log( volume ); step_move_ = define_step_for_move(); - OpenGeodeStochasticStochasticException::check( step_move_ > 0., - nullptr, OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + step_move_ > 0., nullptr, OpenGeodeException::TYPE::data, "[PointSetSampler] - Undefined step length for move (value == ", step_move_, ")." ); } diff --git a/include/geode/stochastic/sampling/direct/object_set_sampler/segment_set_sampler.hpp b/include/geode/stochastic/sampling/direct/object_set_sampler/segment_set_sampler.hpp index 884fd13..0e3e07d 100644 --- a/include/geode/stochastic/sampling/direct/object_set_sampler/segment_set_sampler.hpp +++ b/include/geode/stochastic/sampling/direct/object_set_sampler/segment_set_sampler.hpp @@ -45,8 +45,8 @@ namespace geode azimuth_{ azimuth } { auto volume = domain_.extended_n_volume(); - OpenGeodeStochasticStochasticException::check( volume != 0., - nullptr, OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + volume != 0., nullptr, OpenGeodeException::TYPE::data, "[SegmentSetSampler] - Undefined Extended Bounding " "Box (volume ==0)." ); this->log_pdf_ = -std::log( volume ); diff --git a/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term.hpp b/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term.hpp index 3712f9d..4df8069 100644 --- a/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term.hpp +++ b/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term.hpp @@ -40,8 +40,8 @@ namespace geode::detail { explicit EnergyScale( double param ) { - OpenGeodeStochasticStochasticException::check( param >= 0., nullptr, - OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + param >= 0., nullptr, OpenGeodeException::TYPE::data, "[Gibbs energy term] - The model parameter cannot be " "negative." ); diff --git a/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term_collection.hpp b/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term_collection.hpp index 5add193..bbb76ea 100644 --- a/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term_collection.hpp +++ b/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term_collection.hpp @@ -77,7 +77,7 @@ namespace geode const uuid& term_id ) const { auto term_it = energy_terms_.find( term_id ); - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( term_it != energy_terms_.end(), nullptr, OpenGeodeException::TYPE::data, "[EnergyTermCollection] Unknown energy term: ", @@ -97,7 +97,7 @@ namespace geode terms_for_set( const uuid& set_id ) const { const auto it = set_to_terms_.find( set_id ); - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( it != set_to_terms_.end(), nullptr, OpenGeodeException::TYPE::data, "[EnergyTermCollection] - Object Subset (", set_id.string(), diff --git a/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp index 11ab3a4..f4042e2 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp @@ -91,7 +91,7 @@ namespace geode void add_x_node_monitoring( double beta_x_node ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( beta_x_node <= 1.0 && beta_x_node >= 0., nullptr, OpenGeodeException::TYPE::data, "[FractureSimulationRunner] x node should be inhibitated, " diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp index e7e36f9..9f08b69 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp @@ -45,7 +45,7 @@ namespace geode void add_realization( const std::vector< double >& values ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( values.size() == means_.size(), nullptr, OpenGeodeException::TYPE::data, "[StatisticsMonitor] - Mismatch between realization size and " diff --git a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp index 1ef2571..785bdd0 100644 --- a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp +++ b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp @@ -55,7 +55,7 @@ namespace geode : gibbs_energy_{ energy_term_collection }, proposal_kernel_( std::move( proposal_kernel ) ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( proposal_kernel_ != nullptr, nullptr, OpenGeodeException::TYPE::data, "[MH] null proposal kernel" ); } @@ -107,8 +107,9 @@ namespace geode void set_beta( double b ) { - OpenGeodeStochasticStochasticException::check( b >= 0.0, nullptr, - OpenGeodeException::TYPE::data, "[MH] beta must be >= 0" ); + OpenGeodeStochasticStochasticException::check_exception( b >= 0.0, + nullptr, OpenGeodeException::TYPE::data, + "[MH] beta must be >= 0" ); if( b == 0 ) { Logger::info( diff --git a/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp b/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp index 9f31d92..c46b0b0 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp @@ -40,8 +40,8 @@ namespace geode double change_ratio ) { const auto total_ratio = birth_ratio + death_ratio; - OpenGeodeStochasticStochasticException::check( total_ratio > 0., - nullptr, OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + total_ratio > 0., nullptr, OpenGeodeException::TYPE::data, "BIRTH+DEATH ratio must be positive" ); const auto p_birth = birth_ratio / total_ratio; @@ -78,8 +78,8 @@ namespace geode double death_prob ) { auto birth_death_prob = birth_prob + death_prob; - OpenGeodeStochasticStochasticException::check( birth_death_prob < 1., - nullptr, OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + birth_death_prob < 1., nullptr, OpenGeodeException::TYPE::data, "[Proposal Kernel] - changes should be allowed." ); auto kernel = std::make_unique< ProposalKernel< ObjectType > >(); kernel->add_move( diff --git a/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp b/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp index 74832df..82cde0e 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp @@ -50,7 +50,7 @@ namespace geode double transition_probability() const { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( std::isfinite( log_forward_prob ) && std::isfinite( log_backward_prob ), nullptr, OpenGeodeException::TYPE::data, @@ -116,7 +116,7 @@ namespace geode double proportion_weight ) : sampler_( sampler ), proportion_weight_{ proportion_weight } { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( proportion_weight_ > 0., nullptr, OpenGeodeException::TYPE::data, "[Move] - the weight factor for a move should be in higher " @@ -187,7 +187,7 @@ namespace geode void initialize_probability( double probability ) override { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( birth_ratio_ > 0. && birth_ratio_ < 1., nullptr, OpenGeodeException::TYPE::data, "[BirthDeathMove]-the ratio of birth over mover should be in " diff --git a/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp b/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp index ecf37d1..f539b0b 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp @@ -36,7 +36,7 @@ namespace geode MoveResult< ObjectType > proposed_move; ObjectRef< ObjectType > new_object() { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( proposed_move.new_object.has_value(), nullptr, OpenGeodeException::TYPE::data, "[Proposal] Proposal has no new_object" ); @@ -46,7 +46,7 @@ namespace geode ObjectId old_object_id() { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( proposed_move.old_object_id.has_value(), nullptr, OpenGeodeException::TYPE::data, "[Proposal] Proposal has no old_object_id" ); @@ -70,8 +70,8 @@ namespace geode Proposal< ObjectType > propose( const ObjectSets< ObjectType >& current, RandomEngine& engine ) const { - OpenGeodeStochasticStochasticException::check( !set_moves_.empty(), - nullptr, OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + !set_moves_.empty(), nullptr, OpenGeodeException::TYPE::data, "[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() } ) @@ -131,7 +131,7 @@ namespace geode probabilities.begin(), probabilities.end(), 0.0 ); // Ensure total > 0 - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( total > GLOBAL_EPSILON, nullptr, OpenGeodeException::TYPE::internal, "[MCMC Proposal Kernel] - Total " diff --git a/include/geode/stochastic/spatial/spatial_domain.hpp b/include/geode/stochastic/spatial/spatial_domain.hpp index 14d69af..3d8d08d 100644 --- a/include/geode/stochastic/spatial/spatial_domain.hpp +++ b/include/geode/stochastic/spatial/spatial_domain.hpp @@ -40,12 +40,12 @@ namespace geode extended_domain_{ domain } { auto volume = domain_.n_volume(); - OpenGeodeStochasticStochasticException::check( volume > 0., nullptr, - OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + volume > 0., nullptr, OpenGeodeException::TYPE::data, "[SpatialDomain] - Undefined Spatial Domain (volume == ", volume, ")." ); - OpenGeodeStochasticStochasticException::check( buffer_size_ >= 0.0, - nullptr, OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + buffer_size_ >= 0.0, nullptr, OpenGeodeException::TYPE::data, "[SpatialDomain] buffer size must be non-negative ( buffer " "== ", buffer_size_, ")" ); diff --git a/src/geode/stochastic/sampling/direct/double_sampler.cpp b/src/geode/stochastic/sampling/direct/double_sampler.cpp index 63ddd85..10d611a 100644 --- a/src/geode/stochastic/sampling/direct/double_sampler.cpp +++ b/src/geode/stochastic/sampling/direct/double_sampler.cpp @@ -49,7 +49,7 @@ namespace geode distribution_registry = { { UniformClosed< double >::distribution_type_static(), []( const DoubleSampler::DistributionDescription& desc ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( desc.min_value && desc.max_value, nullptr, OpenGeodeException::TYPE::data, "[DoubleSampler] - Incomplete description for " @@ -62,7 +62,7 @@ namespace geode } }, { UniformClosedOpen< double >::distribution_type_static(), []( const DoubleSampler::DistributionDescription& desc ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( desc.min_value && desc.max_value, nullptr, OpenGeodeException::TYPE::data, "[DoubleSampler] - Incomplete description for " @@ -75,7 +75,7 @@ namespace geode } }, { Gaussian::distribution_type_static(), []( const DoubleSampler::DistributionDescription& desc ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( desc.mean && desc.standard_deviation, nullptr, OpenGeodeException::TYPE::data, "[DoubleSampler] - Incomplete description for " @@ -88,7 +88,7 @@ namespace geode } }, { TruncatedGaussian::distribution_type_static(), []( const DoubleSampler::DistributionDescription& desc ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( desc.mean && desc.standard_deviation, nullptr, OpenGeodeException::TYPE::data, "[DoubleSampler] - Incomplete description for " @@ -104,7 +104,7 @@ namespace geode } }, { VonMises::distribution_type_static(), []( const DoubleSampler::DistributionDescription& desc ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( desc.mean && desc.kappa, nullptr, OpenGeodeException::TYPE::data, "[DoubleSampler] - Incomplete description for " @@ -117,7 +117,7 @@ namespace geode } }, { TruncatedLogNormal::distribution_type_static(), []( const DoubleSampler::DistributionDescription& desc ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( desc.mean && desc.standard_deviation, nullptr, OpenGeodeException::TYPE::data, "[DoubleSampler] - Incomplete description for " @@ -133,7 +133,7 @@ namespace geode } }, { TruncatedPowerLaw::distribution_type_static(), []( const DoubleSampler::DistributionDescription& desc ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( desc.alpha.has_value(), nullptr, OpenGeodeException::TYPE::data, "[DoubleSampler] - Incomplete description for " diff --git a/src/geode/stochastic/sampling/random_engine.cpp b/src/geode/stochastic/sampling/random_engine.cpp index 238c1bc..7220625 100644 --- a/src/geode/stochastic/sampling/random_engine.cpp +++ b/src/geode/stochastic/sampling/random_engine.cpp @@ -49,7 +49,7 @@ namespace // https://web.archive.org/web/20150910005011/http://home.online.no/~pjacklam double normal_quantile( double p ) { - geode::OpenGeodeStochasticStochasticException::check( + geode::OpenGeodeStochasticStochasticException::check_exception( p >= 0.0 && p <= 1.0, nullptr, geode::OpenGeodeException::TYPE::data, "[normal_quantile] - p must be in (0,1). Check the consistencies " @@ -147,7 +147,7 @@ namespace geode template < typename Type > Type sample_uniform( const UniformClosed< Type >& law ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( law.min_value <= law.max_value, nullptr, OpenGeodeException::TYPE::data, "[Uniform sampling] - Wrong range ", law.min_value, @@ -159,7 +159,7 @@ namespace geode template < typename Type > Type sample_uniform( const UniformClosedOpen< Type >& law ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( law.min_value < law.max_value, nullptr, OpenGeodeException::TYPE::data, "[Uniform sampling] - Wrong range ", law.min_value, @@ -170,7 +170,7 @@ namespace geode double sample_gaussian( const Gaussian& law ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( law.standard_deviation > 0 && std::isfinite( law.standard_deviation ) && std::isfinite( law.mean ), @@ -184,7 +184,7 @@ namespace geode double sample_truncated_gaussian( const TruncatedGaussian& law ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( law.standard_deviation > 0 && std::isfinite( law.standard_deviation ) && std::isfinite( law.mean ), @@ -198,8 +198,8 @@ namespace geode const double min = law.min_value.value_or( -std::numeric_limits< double >::infinity() ); - OpenGeodeStochasticStochasticException::check( min < max, nullptr, - OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( min < max, + nullptr, OpenGeodeException::TYPE::data, "[Gaussian sampling] - Wrong truncation range ", min, " is not < than ", max, "." ); @@ -215,8 +215,8 @@ namespace geode F_min = std::max( F_min, geode::GLOBAL_EPSILON ); F_max = std::min( F_max, 1.0 - geode::GLOBAL_EPSILON ); - OpenGeodeStochasticStochasticException::check( F_min < F_max, - nullptr, OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + F_min < F_max, nullptr, OpenGeodeException::TYPE::data, "[Gaussian sampling] - truncation " "range is extreme please check inputs" ); @@ -230,7 +230,7 @@ namespace geode double sample_von_mises( const VonMises& law ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( law.concentration >= 0.0 && std::isfinite( law.mean ), nullptr, OpenGeodeException::TYPE::data, "[VonMises sampling] - Invalid parameters: mean=", law.mean, @@ -297,7 +297,7 @@ namespace geode double sample_truncated_lognormal( const TruncatedLogNormal& law ) { // Basic sanity checks - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( law.standard_deviation > 0 && std::isfinite( law.standard_deviation ) && std::isfinite( law.mean ), @@ -311,8 +311,8 @@ namespace geode const double max_val = law.max_value.value_or( std::numeric_limits< double >::infinity() ); - OpenGeodeStochasticStochasticException::check( min_val < max_val, - nullptr, OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + min_val < max_val, nullptr, OpenGeodeException::TYPE::data, "[Truncated LogNormal sampling] - Wrong truncation range ", min_val, " is not < than ", max_val, "." ); @@ -331,8 +331,8 @@ namespace geode double F_max = std::min( normal_cdf( zmax ), 1.0 - geode::GLOBAL_EPSILON ); - OpenGeodeStochasticStochasticException::check( F_min < F_max, - nullptr, OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + F_min < F_max, nullptr, OpenGeodeException::TYPE::data, "[Truncated LogNormal sampling] - truncation " "range is extreme please chack inputs" ); @@ -347,8 +347,8 @@ namespace geode double sample_truncated_powerlaw( const TruncatedPowerLaw& law ) { - OpenGeodeStochasticStochasticException::check( law.alpha > 0, - nullptr, OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + law.alpha > 0, nullptr, OpenGeodeException::TYPE::data, "Power-law exponent alpha must be > 0" ); // Set bounds @@ -357,8 +357,8 @@ namespace geode const double xmax = law.max_value.value_or( std::numeric_limits< double >::infinity() ); - OpenGeodeStochasticStochasticException::check( xmin < xmax, nullptr, - OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + xmin < xmax, nullptr, OpenGeodeException::TYPE::data, "Truncated power-law: min >= max" ); // Sample uniform @@ -397,7 +397,7 @@ namespace geode bool sample_bernoulli( double probability_of_success ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( probability_of_success >= 0. && probability_of_success <= 1.0, nullptr, OpenGeodeException::TYPE::data, "Bernoulli sampling cannot be done since ", diff --git a/src/geode/stochastic/spatial/object_set.cpp b/src/geode/stochastic/spatial/object_set.cpp index fe704bc..21ce69a 100644 --- a/src/geode/stochastic/spatial/object_set.cpp +++ b/src/geode/stochastic/spatial/object_set.cpp @@ -41,7 +41,7 @@ namespace geode template < typename Type > const Type& ObjectSet< Type >::get_fixed_object( index_t index ) const { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( index < fixed_objects_.size(), nullptr, OpenGeodeException::TYPE::data, "[ObjectSet] - index for fixed object out of range." ); @@ -51,7 +51,7 @@ namespace geode template < typename Type > const Type& ObjectSet< Type >::get_free_object( index_t index ) const { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( index < free_objects_.size(), nullptr, OpenGeodeException::TYPE::data, "[ObjectSet] - index for free object out of range." ); @@ -75,7 +75,7 @@ namespace geode template < typename Type > void ObjectSet< Type >::update_free_object( index_t index, Type&& object ) { - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( index < free_objects_.size(), nullptr, OpenGeodeException::TYPE::data, "[ObjectSet] - free object index out of range." ); @@ -86,8 +86,8 @@ namespace geode void ObjectSet< Type >::remove_free_object( index_t index ) { const index_t last = free_objects_.size() - 1; - OpenGeodeStochasticStochasticException::check( index <= last, nullptr, - OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( index <= last, + nullptr, OpenGeodeException::TYPE::data, "[ObjectSet] - free object index out of range." ); if( index != last ) { diff --git a/src/geode/stochastic/spatial/object_sets.cpp b/src/geode/stochastic/spatial/object_sets.cpp index caba6de..4a540e0 100644 --- a/src/geode/stochastic/spatial/object_sets.cpp +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -12,9 +12,9 @@ namespace geode const uuid& set_id ) const { auto it = sets_.find( set_id ); - OpenGeodeStochasticStochasticException::check( it != sets_.end(), - nullptr, OpenGeodeException::TYPE::data, "[ObjectSet] - group (", - set_id.string(), ") is not defined." ); + OpenGeodeStochasticStochasticException::check_exception( + it != sets_.end(), nullptr, OpenGeodeException::TYPE::data, + "[ObjectSet] - group (", set_id.string(), ") is not defined." ); return it->second; } @@ -97,8 +97,8 @@ namespace geode new_set.set_name( name ); const auto new_set_id = new_set.id(); auto [it, inserted] = sets_.emplace( new_set_id, std::move( new_set ) ); - OpenGeodeStochasticStochasticException::check( inserted, new_set_id, - OpenGeodeException::TYPE::data, "[ObjectSet]- group (", + OpenGeodeStochasticStochasticException::check_exception( inserted, + new_set_id, OpenGeodeException::TYPE::data, "[ObjectSet]- group (", new_set_id.string(), ") already exists." ); return new_set_id; } @@ -127,11 +127,11 @@ namespace geode void ObjectSets< Type >::update_free_object( const ObjectId& old_object_id, Type&& new_object ) { - OpenGeodeStochasticStochasticException::check( !old_object_id.fixed, - nullptr, OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + !old_object_id.fixed, nullptr, OpenGeodeException::TYPE::data, "[ObjectSet]- cannot modify fixed object." ); auto& set = get_set( old_object_id.set_id ); - OpenGeodeStochasticStochasticException::check( + OpenGeodeStochasticStochasticException::check_exception( old_object_id.index < set.nb_objects(), nullptr, OpenGeodeException::TYPE::data, "[ObjectSet]- index of object to update out of range." ); @@ -145,8 +145,8 @@ namespace geode void ObjectSets< Type >::remove_free_object( const ObjectId& object_id ) { auto& set = get_set( object_id.set_id ); - OpenGeodeStochasticStochasticException::check( !object_id.fixed, - nullptr, OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + !object_id.fixed, nullptr, OpenGeodeException::TYPE::data, "[ObjectSet]- Cannot remove fixed object." ); const auto& obj_to_remove = get_object( object_id ); neighborhood_.remove( object_bounding_box( obj_to_remove ), object_id ); From 5602a1fb303a4cc2934247743405fb0789f7f9ea Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Mon, 18 May 2026 17:51:50 +0200 Subject: [PATCH 12/17] fix(abi): fix abi From 40540ba47d69bbef2510eb3ba80b55faf05f8364 Mon Sep 17 00:00:00 2001 From: BotellaA <3213882+BotellaA@users.noreply.github.com> Date: Wed, 20 May 2026 01:01:30 +0000 Subject: [PATCH 13/17] Apply prepare changes --- commitlint.config.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/commitlint.config.js b/commitlint.config.js index a397334..0c07c26 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -14,6 +14,7 @@ const Configuration = { "type-empty": [0], "type-enum": [2, "always", ["feat", "fix", "perf"]], }, -} + defaultIgnores: false, +}; -export default Configuration +export default Configuration; From a1b1ea582f7e3a64f60372ea72392d3772d2a9ed Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Mon, 25 May 2026 09:22:36 +0200 Subject: [PATCH 14/17] feat(Helpers): create inference helpers. --- .../helpers/fracture_simulation_runner.hpp | 2 +- .../mcmc/helpers/simulation_monitor.hpp | 20 +-- .../mcmc/helpers/simulation_printer.hpp | 2 +- bindings/python/src/stochastic/stochastic.cpp | 4 +- .../inference/statistic_monitor.hpp | 51 ------ .../inference/statistic_objective.hpp | 4 +- .../inference/statistic_validator.hpp | 4 +- .../inference/statistics_tracker.hpp | 86 +++++++++ .../stochastic/inference/target_statistic.hpp | 1 + .../models/energy_term_collection.hpp | 38 +++- .../models/energy_terms/energy_term.hpp | 2 +- .../energy_terms/energy_term_builder.hpp | 4 +- include/geode/stochastic/models/model.hpp | 64 +++++-- .../helpers/fracture_simulation_runner.hpp | 2 +- ...monitor.hpp => simulation_monitor_old.hpp} | 16 +- .../mcmc/helpers/simulation_printer.hpp | 158 +++++++++-------- .../mcmc/metropolis_hasting_sampler.hpp | 16 +- .../sampling/mcmc/simulation_runner.hpp | 65 ++----- src/geode/stochastic/CMakeLists.txt | 4 +- tests/stochastic/CMakeLists.txt | 28 +-- .../models/test-energy-term-collection.cpp | 0 .../mcmc/helpers/test-simulation_monitor.cpp | 6 +- .../mcmc/helpers/test-simulation_printer.cpp | 5 +- .../sampling/mcmc/test-mh-poisson-new.cpp | 4 +- .../sampling/mcmc/test-mh-poisson.cpp | 164 ++++++++++-------- .../sampling/mcmc/test-mh-strauss.cpp | 2 +- 26 files changed, 419 insertions(+), 333 deletions(-) delete mode 100644 include/geode/stochastic/inference/statistic_monitor.hpp create mode 100644 include/geode/stochastic/inference/statistics_tracker.hpp rename include/geode/stochastic/sampling/mcmc/helpers/{simulation_monitor.hpp => simulation_monitor_old.hpp} (85%) create mode 100644 tests/stochastic/models/test-energy-term-collection.cpp diff --git a/bindings/python/src/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp b/bindings/python/src/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp index dbb6e98..9766162 100644 --- a/bindings/python/src/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp +++ b/bindings/python/src/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp @@ -88,7 +88,7 @@ namespace geode // pybind11::arg( "engine" ), pybind11::arg( "steps" // ), "Run simulation for a fixed number of steps." ) .def( "run", - static_cast< StatisticsMonitor ( FractureSimulationRunner::* )( + static_cast< StatisticsTracker ( FractureSimulationRunner::* )( RandomEngine&, const SimulationConfigurator& ) >( &FractureSimulationRunner::run ), pybind11::arg( "engine" ), pybind11::arg( "config" ), diff --git a/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp b/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp index 3a18b60..9b2d960 100644 --- a/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp +++ b/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp @@ -21,30 +21,30 @@ * */ -#include +#include namespace geode { void define_simulation_monitor( pybind11::module &module ) { - pybind11::class_< geode::StatisticsMonitor >( - module, "StatisticsMonitor" ) + pybind11::class_< geode::StatisticsTracker >( + module, "StatisticsTracker" ) .def( pybind11::init< geode::index_t >(), pybind11::arg( "nb_energy_terms" ), - "Create a StatisticsMonitor for a given number of energy " + "Create a StatisticsTracker for a given number of energy " "terms" ) - .def( "add_realization", &geode::StatisticsMonitor::add_realization, + .def( "add_realization", &geode::StatisticsTracker::add_realization, pybind11::arg( "values" ), "Add a realization (vector of doubles) to update statistics" ) - .def( "statiscal_count", &geode::StatisticsMonitor::statiscal_count, + .def( "statiscal_count", &geode::StatisticsTracker::statiscal_count, "Return the number of realizations added" ) - .def_property_readonly( "means", &geode::StatisticsMonitor::means, + .def_property_readonly( "means", &geode::StatisticsTracker::means, "Return the computed mean values for each energy term" ) .def_property_readonly( "variances", - &geode::StatisticsMonitor::variances, + &geode::StatisticsTracker::variances, "Return the computed variances for each energy term" ) - .def( "__repr__", []( const geode::StatisticsMonitor &self ) { - return ""; } ); } diff --git a/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_printer.hpp b/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_printer.hpp index a1d36d1..ca8c889 100644 --- a/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_printer.hpp +++ b/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_printer.hpp @@ -64,7 +64,7 @@ namespace geode // &SimulationPrinter::print_statistics_summary, // pybind11::arg( "monitor" ), // pybind11::arg( "energy_term_names" ) = "", - // "Print statistics summary from a StatisticsMonitor." + // "Print statistics summary from a StatisticsTracker." // ); } } // namespace geode \ No newline at end of file diff --git a/bindings/python/src/stochastic/stochastic.cpp b/bindings/python/src/stochastic/stochastic.cpp index e03b15e..5ce2686 100644 --- a/bindings/python/src/stochastic/stochastic.cpp +++ b/bindings/python/src/stochastic/stochastic.cpp @@ -27,7 +27,7 @@ #include "sampling/direct/double_sampler.hpp" // #include "sampling/mcmc/helpers/fracture_simulation_runner.hpp" -#include "sampling/mcmc/helpers/simulation_monitor.hpp" +// #include "sampling/mcmc/helpers/simulation_monitor.hpp" #include "sampling/mcmc/helpers/simulation_printer.hpp" #include "sampling/mcmc/simulation_runner.hpp" @@ -47,7 +47,7 @@ PYBIND11_MODULE( opengeode_stochastic_py_stochastic, module ) geode::define_random_engine( module ); geode::define_double_sampler( module ); - geode::define_simulation_monitor( module ); + // geode::define_simulation_monitor( module ); geode::define_simulation_printer( module ); geode::define_simulation_runner( module ); // geode::define_fracture_simulation( module ); diff --git a/include/geode/stochastic/inference/statistic_monitor.hpp b/include/geode/stochastic/inference/statistic_monitor.hpp deleted file mode 100644 index 4159e8d..0000000 --- a/include/geode/stochastic/inference/statistic_monitor.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#include -#include - -#include - -namespace geode -{ - - class StatMonitor - { - public: - void add_realization( - const absl::flat_hash_map< uuid, double >& values ) - { - ++count_; - for( const auto& [term_uuid, value] : values ) - { - auto& mean = means_[term_uuid]; - auto& m2 = m2_[term_uuid]; // somme des carrés - - double delta = value - mean; - mean += delta / count_; - double delta2 = value - mean; - m2 += delta * delta2; - } - } - - const index_t statiscal_count() const - { - return count_; - } - - double mean( const uuid& id ) const - { - return means_.at( id ); - } - - double variance( const uuid& id ) const - { - if( count_ < 2 ) - return 0.0; - return m2_.at( id ) / ( count_ - 1 ); - } - - private: - absl::flat_hash_map< uuid, double > means_; - absl::flat_hash_map< uuid, double > m2_; - index_t count_{ 0 }; - }; -} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/inference/statistic_objective.hpp b/include/geode/stochastic/inference/statistic_objective.hpp index d6abf65..0859115 100644 --- a/include/geode/stochastic/inference/statistic_objective.hpp +++ b/include/geode/stochastic/inference/statistic_objective.hpp @@ -23,7 +23,7 @@ #pragma once #include -#include +#include #include namespace geode @@ -33,7 +33,7 @@ namespace geode class StatisticObjective { public: - double compute_loss( const StatMonitor& monitor, + double compute_loss( const StatisticsTracker< ObjectType >& monitor, const std::vector< TargetStatistic >& targets ) const { double loss = 0.0; diff --git a/include/geode/stochastic/inference/statistic_validator.hpp b/include/geode/stochastic/inference/statistic_validator.hpp index 94eab14..951a1e1 100644 --- a/include/geode/stochastic/inference/statistic_validator.hpp +++ b/include/geode/stochastic/inference/statistic_validator.hpp @@ -24,7 +24,7 @@ #include -#include +#include #include namespace geode @@ -33,7 +33,7 @@ namespace geode class StatisticsValidator { public: - void check( const StatisticsMonitor& monitor, + void check( const StatisticsTracker& monitor, const std::vector< TargetStatistic >& targets ) const { for( const auto& target : targets ) diff --git a/include/geode/stochastic/inference/statistics_tracker.hpp b/include/geode/stochastic/inference/statistics_tracker.hpp new file mode 100644 index 0000000..8e89eab --- /dev/null +++ b/include/geode/stochastic/inference/statistics_tracker.hpp @@ -0,0 +1,86 @@ +#pragma once + +// #include +#include +#include + +#include +#include + +namespace geode +{ + template < typename ObjectType > + class StatisticsTracker + { + public: + StatisticsTracker( const Model< ObjectType >& model ) : model_{ model } + { + means_.resize( model.nb_terms(), 0.0 ); + m2_.resize( model.nb_terms(), 0.0 ); + } + + [[nodiscard]] index_t statiscal_count() const + { + return count_; + } + + void add_realization( const std::vector< double >& values ) + { + ++count_; + for( const auto value_id : geode::Range{ values.size() } ) + { + auto& value = values[value_id]; + auto& mean = means_[value_id]; + auto& m2 = m2_[value_id]; // somme des carrés + + double delta = value - mean; + mean += delta / count_; + double delta2 = value - mean; + m2 += delta * delta2; + } + } + + [[nodiscard]] double mean( const uuid& term_uuid ) const + { + return means_[model_.term_index( term_uuid )]; + } + + [[nodiscard]] const std::vector< double >& means() const + { + return means_; + } + + [[nodiscard]] double variance( const uuid& term_uuid ) const + { + return variance( model_.term_index( term_uuid ) ); + } + + [[nodiscard]] std::vector< double > variances() const + { + std::vector< double > variances; + variances.reserve( model_.nb_terms() ); + for( const auto variance_id : geode::Range{ model_.nb_terms() } ) + { + variances.emplace_back( this->variance( variance_id ) ); + } + return variances; + } + + private: + [[nodiscard]] double variance( index_t term_index ) const + { + if( count_ < 2 ) + { + return 0.0; + } + return m2_[term_index] / ( count_ - 1 ); + } + + private: + const Model< ObjectType >& model_; + + std::vector< double > means_{}; + std::vector< double > m2_{}; + index_t count_{ 0 }; + }; +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/inference/target_statistic.hpp b/include/geode/stochastic/inference/target_statistic.hpp index 9d61e14..b637062 100644 --- a/include/geode/stochastic/inference/target_statistic.hpp +++ b/include/geode/stochastic/inference/target_statistic.hpp @@ -21,6 +21,7 @@ * */ #pragma once +#include namespace geode { diff --git a/include/geode/stochastic/models/energy_term_collection.hpp b/include/geode/stochastic/models/energy_term_collection.hpp index 8ec9f7f..957eba8 100644 --- a/include/geode/stochastic/models/energy_term_collection.hpp +++ b/include/geode/stochastic/models/energy_term_collection.hpp @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2019 - 2026 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 @@ -49,14 +72,19 @@ namespace geode return energy_terms_.size(); } - [[nodiscard]] const EnergyTerm< ObjectType >& get( - const uuid& term_id ) const + [[nodiscard]] index_t get_term_index( const uuid& term_uuid ) const { - auto term_it = uuid_to_index_.find( term_id ); + auto term_it = uuid_to_index_.find( term_uuid ); OPENGEODE_EXCEPTION( term_it != uuid_to_index_.end(), absl::StrCat( "[EnergyTermCollection] Unknown energy term: ", - term_id.string() ) ); - return *energy_terms_[term_it->second]; + term_uuid.string() ) ); + return term_it->second; + } + + [[nodiscard]] const EnergyTerm< ObjectType >& get( + const uuid& term_uuid ) const + { + return *energy_terms_[get_term_index( term_uuid )]; } [[nodiscard]] uuid get_term_uuid( std::string_view name ) const diff --git a/include/geode/stochastic/models/energy_terms/energy_term.hpp b/include/geode/stochastic/models/energy_terms/energy_term.hpp index fc33699..fe99d3b 100644 --- a/include/geode/stochastic/models/energy_terms/energy_term.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term.hpp @@ -95,7 +95,7 @@ namespace geode impacted_set_ids_{ std::move( impacted_set_ids ) }, domain_( domain ) { - std::sort( impacted_set_ids_.begin(), impacted_set_ids_.end() ); + absl::c_sort( impacted_set_ids_ ); impacted_set_ids_.erase( std::unique( impacted_set_ids_.begin(), impacted_set_ids_.end() ), impacted_set_ids_.end() ); diff --git a/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp b/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp index dd1e237..ebbc973 100644 --- a/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp @@ -82,7 +82,7 @@ namespace geode interacting_set_ids.push_back( set2 ); } - std::sort( interacting_set_ids.begin(), interacting_set_ids.end() ); + absl::c_sort( interacting_set_ids ); interacting_set_ids.erase( std::unique( interacting_set_ids.begin(), interacting_set_ids.end() ), interacting_set_ids.end() ); @@ -91,7 +91,7 @@ namespace geode for( auto& [_, adjacent_set_uuids] : objectset_adjacency_map ) { geode_unused( _ ); - std::sort( adjacent_set_uuids.begin(), adjacent_set_uuids.end() ); + absl::c_sort( adjacent_set_uuids ); adjacent_set_uuids.erase( std::unique( adjacent_set_uuids.begin(), adjacent_set_uuids.end() ), adjacent_set_uuids.end() ); diff --git a/include/geode/stochastic/models/model.hpp b/include/geode/stochastic/models/model.hpp index fad76a2..0aa2dc2 100644 --- a/include/geode/stochastic/models/model.hpp +++ b/include/geode/stochastic/models/model.hpp @@ -55,48 +55,77 @@ namespace geode public: Model() = delete; Model( EnergyTermCollection< ObjectType >&& energy_terms ) - : terms_( std::move( energy_terms ) ), energy_{ terms_ } + : terms_collection_( std::move( energy_terms ) ), + energy_{ terms_collection_ } { } - const EnergyTermCollection< ObjectType >& terms() const + [[nodiscard]] index_t nb_terms() const { - return terms_; + return terms_collection_.size(); } - const GibbsEnergy< ObjectType >& energy() const + [[nodiscard]] const EnergyTermCollection< ObjectType >& terms() const + { + return terms_collection_; + } + + [[nodiscard]] index_t term_index( const uuid& term_uuid ) const + { + return terms_collection_.get_term_index( term_uuid ); + } + + [[nodiscard]] const GibbsEnergy< ObjectType >& energy() const { return energy_; } - absl::flat_hash_map< uuid, double > compute_statistics( + [[nodiscard]] std::vector< double > compute_statistics( const ObjectSets< ObjectType >& state ) const { - absl::flat_hash_map< uuid, double > stats; - stats.reserve( terms_.size() ); - - for( const auto& term_ptr : terms_.energy_terms() ) + std::vector< double > statistic_values; + statistic_values.reserve( terms_collection_.size() ); + for( const auto& term : terms_collection_.energy_terms() ) { - stats.emplace( term_ptr->id(), term_ptr->statistic( state ) ); + statistic_values.emplace_back( term->statistic( state ) ); } - - return stats; + return statistic_values; } - absl::flat_hash_map< uuid, double > compute_statistics( + [[nodiscard]] double compute_statistic( const ObjectSets< ObjectType >& state, const uuid& term_uuid ) const { - const auto& term = terms_.get( term_uuid ); + const auto& term = terms_collection_.get( term_uuid ); return term.statistic( state ); } + [[nodiscard]] std::string term_name( const uuid& term_uuid ) const + { + const auto& term = terms_collection_.get( term_uuid ); + return term.name().value_or( "unnamed" ); + } + + [[nodiscard]] std::vector< std::string > term_names() const + { + std::vector< std::string > names; + names.reserve( terms_collection_.size() ); + + for( const auto& term : terms_collection_.energy_terms() ) + { + names.emplace_back( term->name().value_or( "unnamed" ) ); + } + + return names; + } + private: - EnergyTermCollection< ObjectType > terms_; + EnergyTermCollection< ObjectType > terms_collection_; GibbsEnergy< ObjectType > energy_; }; template < typename ObjectType > - Model< ObjectType > build_model( const ModelConfig& config, + std::unique_ptr< Model< ObjectType > > build_model( + const ModelConfig& config, const ObjectSets< ObjectType >& object_sets, const SpatialDomain< ObjectType::dim >& domain ) { @@ -108,6 +137,7 @@ namespace geode term_cfg, object_sets, domain ) ); } - return Model< ObjectType >{ std::move( collection ) }; + return std::make_unique< Model< ObjectType > >( + std::move( collection ) ); } } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp index 929aa61..3aded5f 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp @@ -160,7 +160,7 @@ namespace geode // } // // void check_statistics( - // const StatisticsMonitor& statistic_monitoring ) const + // const StatisticsTracker& statistic_monitoring ) const // { // const auto& computed_means = statistic_monitoring.means(); // diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor_old.hpp similarity index 85% rename from include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp rename to include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor_old.hpp index 95cc380..b6487aa 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor_old.hpp @@ -28,16 +28,16 @@ namespace geode { - class StatisticsMonitor + class StatisticsTracker { public: - StatisticsMonitor( StatisticsMonitor&& ) = default; - StatisticsMonitor( const StatisticsMonitor& ) = default; - StatisticsMonitor& operator=( StatisticsMonitor&& ) noexcept = default; - StatisticsMonitor& operator=( - const StatisticsMonitor& ) noexcept = default; + StatisticsTracker( StatisticsTracker&& ) = default; + StatisticsTracker( const StatisticsTracker& ) = default; + StatisticsTracker& operator=( StatisticsTracker&& ) noexcept = default; + StatisticsTracker& operator=( + const StatisticsTracker& ) noexcept = default; - StatisticsMonitor( const index_t nb_energy_terms ) + StatisticsTracker( const index_t nb_energy_terms ) { means_.resize( nb_energy_terms, 0.0 ); variances_.resize( nb_energy_terms, 0.0 ); @@ -46,7 +46,7 @@ namespace geode void add_realization( const std::vector< double >& values ) { OPENGEODE_EXCEPTION( values.size() == means_.size(), - "[StatisticsMonitor] - Mismatch between realization size and " + "[StatisticsTracker] - Mismatch between realization size and " "expected number of statistics." ); ++count_; for( size_t i = 0; i < values.size(); ++i ) diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_printer.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_printer.hpp index 34f8691..5dc87a2 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_printer.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_printer.hpp @@ -25,7 +25,7 @@ #include -#include +#include #include #include @@ -75,42 +75,84 @@ namespace geode } }; + template < typename ObjectType > class SimulationPrinter { public: - SimulationPrinter( const SimulationPrinterConfigurator& config ) - : config_( config ) + SimulationPrinter( const Model< ObjectType >& model, + const SimulationPrinterConfigurator& config ) + : model_( model ), config_( config ) { } // Print statistics to the configured statistics file - void print_statistics( - const std::vector< double >& stats, absl::string_view header ) const + void print_statistics( const std::vector< double >& stats ) const { if( !config_.print_statistics ) + { return; - const auto stats_file_path = - stats_file_path_.value_or( create_statistics_file( header ) ); + } + if( !stats_file_path_ ) + { + stats_file_path_ = + ( std::filesystem::path( config_.output_folder ) + / config_.statistics_filename ) + .string(); + create_file_and_write_header( *stats_file_path_, + absl::StrCat( "# Simulation Statistics\n", + energy_terms_name_header() ) ); + } std::ofstream file = - open_file_with_dirs( stats_file_path, std::ios::app ); + open_file_with_dirs( *stats_file_path_, std::ios::app ); file << absl::StrJoin( stats, " ; " ) << "\n"; } - template < typename ObjectType > + void print_statistics_summary( + const StatisticsTracker< ObjectType >& tracker ) const + { + if( !config_.print_statistics_summary ) + { + return; + } + + const auto summary_path = + ( std::filesystem::path( config_.output_folder ) + / config_.statistics_summary_filename ) + .string(); + create_file_and_write_header( + summary_path, absl::StrCat( "# Summary statistics\n", + energy_terms_name_header() ) ); + std::ofstream file = + open_file_with_dirs( summary_path, std::ios::app ); + file << absl::StrCat( + "# Count:\n", tracker.statiscal_count(), "\n" ); + file << absl::StrCat( + "# Means:\n", absl::StrJoin( tracker.means(), " ; " ), "\n" ); + file << absl::StrCat( "# Variances:\n", + absl::StrJoin( tracker.variances(), " ; " ), "\n" ); + } + void print_object_sets( const ObjectSets< ObjectType >& object_sets, index_t realization_id ) const { if( !config_.print_realisations || realization_id % config_.realisations_print_frequency != 0 ) + { return; + } - const auto filename = - ( std::filesystem::path( config_.output_folder ) - / absl::StrCat( - config_.realisations_prefix, realization_id, ".txt" ) ) - .string(); + // const auto filename = + // ( std::filesystem::path( config_.output_folder ) + // / absl::StrCat( + // config_.realisations_prefix, realization_id, ".txt" ) + // ) + // .string(); + const auto filename = std::filesystem::path( config_.output_folder ) + / absl::StrCat( config_.realisations_prefix, + realization_id, ".txt" ); - std::ofstream file = open_file_with_dirs( filename ); + std::ofstream file = + open_file_with_dirs( filename, std::ofstream::out ); const auto all_objects = object_sets.get_all_object(); file << "#nb_objects\t" << all_objects.size() << "\n"; @@ -122,78 +164,50 @@ namespace geode << "\n"; } } - void print_statistics_summary( const StatisticsMonitor& monitor, - absl::string_view energy_term_names ) const - { - if( !config_.print_statistics_summary ) - return; - const auto summary_path = - ( std::filesystem::path( config_.output_folder ) - / config_.statistics_summary_filename ) - .string(); + private: + void create_file_and_write_header( + const std::filesystem::path& filename, + absl::string_view header ) const + { + std::ofstream file = + open_file_with_dirs( filename, std::ofstream::out ); - std::ofstream file = open_file_with_dirs( summary_path ); - file << "# Summary statistics\n"; - file << energy_term_names.data() << "\n"; - file << absl::StrCat( - "# Count:\n", monitor.statiscal_count(), "\n" ); - file << absl::StrCat( - "# Means:\n", absl::StrJoin( monitor.means(), " ; " ), "\n" ); - file << absl::StrCat( "# Variances:\n", - absl::StrJoin( monitor.variances(), " ; " ), "\n" ); + file << header; } - private: - void write_header_if_new( - absl::string_view filename, absl::string_view header ) const + std::ofstream open_file_with_dirs( + const std::filesystem::path& file_path, + std::ios::openmode mode ) const { - namespace fs = std::filesystem; - fs::path file_path{ std::string( filename ) }; - if( !fs::exists( file_path ) ) + auto absolute_path = file_path; + if( !absolute_path.has_parent_path() ) { - std::ofstream file = open_file_with_dirs( filename ); - file << header; + absolute_path = std::filesystem::current_path() / absolute_path; } - } - - std::ofstream open_file_with_dirs( absl::string_view path_filename, - std::ios::openmode mode = std::ofstream::out ) const - { - namespace fs = std::filesystem; - fs::path file_path{ std::string( path_filename ) }; - - if( !file_path.has_parent_path() ) - file_path = fs::current_path() / file_path; - - if( file_path.has_parent_path() ) - fs::create_directories( file_path.parent_path() ); - - std::ofstream file( file_path, mode ); + if( absolute_path.has_parent_path() ) + { + std::filesystem::create_directories( + absolute_path.parent_path() ); + } + std::ofstream file( absolute_path, mode ); if( !file.is_open() ) + { throw geode::OpenGeodeException( - "Cannot open file: " + file_path.string() ); - + "Cannot open file: " + absolute_path.string() ); + } return file; } - const std::string& create_statistics_file( - absl::string_view header ) const - { - stats_file_path_ = ( std::filesystem::path( config_.output_folder ) - / config_.statistics_filename ) - .string(); - if( config_.print_statistics ) - { - write_header_if_new( *stats_file_path_, - absl::StrCat( - "# Simulation Statistics\n", header.data(), "\n" ) ); - } - return *stats_file_path_; + std::string energy_terms_name_header() const + { + return absl::StrCat( + absl::StrJoin( model_.term_names(), " ; " ), "\n" ); } private: - SimulationPrinterConfigurator config_; + const Model< ObjectType >& model_; + const SimulationPrinterConfigurator config_; mutable std::optional< std::string > stats_file_path_; }; diff --git a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp index 565e01c..796a872 100644 --- a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp +++ b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp @@ -24,7 +24,7 @@ #pragma once #include -#include +#include #include namespace geode @@ -49,11 +49,9 @@ namespace geode class MetropolisHastings { public: - MetropolisHastings( - const EnergyTermCollection< ObjectType >& energy_term_collection, + MetropolisHastings( const Model< ObjectType >& model, std::unique_ptr< ProposalKernel< ObjectType > > proposal_kernel ) - : gibbs_energy_{ energy_term_collection }, - proposal_kernel_( std::move( proposal_kernel ) ) + : model_{ model }, proposal_kernel_( std::move( proposal_kernel ) ) { OPENGEODE_ASSERT( proposal_kernel_ != nullptr, "[MH] null proposal kernel" ); @@ -189,7 +187,7 @@ namespace geode { const auto new_object = proposal.new_object(); const auto delta_log_energy = - gibbs_energy_.delta_log_add( state, new_object ); + model_.energy().delta_log_add( state, new_object ); return accept_or_reject( proposal, state, engine, delta_log_energy, []( auto& state, auto& proposal ) { state.add_object( @@ -204,7 +202,7 @@ namespace geode { const auto old_object_id = proposal.old_object_id(); const auto delta_log_energy = - gibbs_energy_.delta_log_remove( state, old_object_id ); + model_.energy().delta_log_remove( state, old_object_id ); return accept_or_reject( proposal, state, engine, delta_log_energy, []( auto& state, auto& proposal ) { state.remove_free_object( proposal.old_object_id() ); @@ -217,7 +215,7 @@ namespace geode { const auto new_object = proposal.new_object(); const auto old_object_id = proposal.old_object_id(); - const auto delta_log_energy = gibbs_energy_.delta_log_change( + const auto delta_log_energy = model_.energy().delta_log_change( state, old_object_id, new_object ); return accept_or_reject( proposal, state, engine, delta_log_energy, []( auto& state, auto& proposal ) { @@ -228,7 +226,7 @@ namespace geode }; private: - GibbsEnergy< ObjectType > gibbs_energy_; + const Model< ObjectType >& model_; std::unique_ptr< ProposalKernel< ObjectType > > proposal_kernel_; double beta_{ 1.0 }; }; diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index 4b81275..6b80b53 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -23,8 +23,8 @@ #pragma once #include +#include #include -#include #include #include #include @@ -64,7 +64,7 @@ namespace geode { public: SimulationRunner( const SpatialDomain< ObjectType::dim >& domain ) - : domain_( domain ) {}; + : domain_( domain ){}; virtual ~SimulationRunner() = default; virtual void initialize() = 0; @@ -76,7 +76,7 @@ namespace geode return object_sets_; } - StatisticsMonitor run( + StatisticsTracker< ObjectType > run( RandomEngine& engine, const SimulationConfigurator& config ) { if( config.burn_in_steps > 0 ) @@ -85,13 +85,13 @@ namespace geode } // Initialize monitoring - StatisticsMonitor stats_monitor( energy_terms_collection_.size() ); - std::unique_ptr< SimulationPrinter > printer; + StatisticsTracker< ObjectType > stats_monitor( *model_ ); + std::unique_ptr< SimulationPrinter< ObjectType > > printer; if( config.printer.has_value() ) { - printer = std::make_unique< SimulationPrinter >( - config.printer.value() ); + printer = std::make_unique< SimulationPrinter< ObjectType > >( + *model_, config.printer.value() ); } for( const auto realization : Range{ config.realizations } ) @@ -99,21 +99,19 @@ namespace geode mh_sampler_->walk( object_sets_, engine, config.metropolis_hasting_steps ); - const auto stats = state_statistics(); + const auto stats = model_->compute_statistics( object_sets_ ); stats_monitor.add_realization( stats ); if( printer ) { - printer->print_statistics( - stats, model_energy_term_names() ); + printer->print_statistics( stats ); printer->print_object_sets( object_sets_, realization ); } } if( printer ) { - printer->print_statistics_summary( - stats_monitor, model_energy_term_names() ); + printer->print_statistics_summary( stats_monitor ); } return stats_monitor; @@ -124,48 +122,17 @@ namespace geode return object_sets_; } - std::vector< double > state_statistics() const - { - std::vector< double > statistic_values; - statistic_values.reserve( ordered_energy_terms_.size() ); - - for( const auto& energy_term_uuid : ordered_energy_terms_ ) - { - const auto& term = - energy_terms_collection_.get( energy_term_uuid ); - statistic_values.push_back( term.statistic( object_sets_ ) ); - } - - return statistic_values; - } - - std::string model_energy_term_names() const - { - std::vector< std::string > term_names; - term_names.reserve( ordered_energy_terms_.size() ); - - for( const auto& energy_term_uuid : ordered_energy_terms_ ) - { - const auto& term = - energy_terms_collection_.get( energy_term_uuid ); - term_names.push_back( - term.name().value_or( term.id().string() ) ); - } - - return absl::StrCat( absl::StrJoin( term_names, " ; " ), "\n" ); - } + protected: + // void initialize_sets_and_samplers() = 0; + // void initialize_model() = 0; protected: SpatialDomain< ObjectType::dim > domain_; + + ObjectSets< ObjectType > object_sets_; std::vector< std::unique_ptr< geode::ObjectSetSampler< ObjectType > > > set_samplers_; - - std::vector< geode::uuid > ordered_energy_terms_; - std::vector< double > ordered_target_statistics_; - - EnergyTermCollection< ObjectType > energy_terms_collection_; + std::unique_ptr< Model< ObjectType > > model_; std::unique_ptr< geode::MetropolisHastings< ObjectType > > mh_sampler_; - - ObjectSets< ObjectType > object_sets_; }; } // namespace geode \ No newline at end of file diff --git a/src/geode/stochastic/CMakeLists.txt b/src/geode/stochastic/CMakeLists.txt index ffce372..f8c365f 100644 --- a/src/geode/stochastic/CMakeLists.txt +++ b/src/geode/stochastic/CMakeLists.txt @@ -38,7 +38,7 @@ add_geode_library( PUBLIC_HEADERS #"inference/abc_shadow.hpp" "inference/statistic_objective.hpp" - "inference/statistic_monitor.hpp" + "inference/statistics_tracker.hpp" "inference/target_statistic.hpp" "inference/statistic_validator.hpp" "spatial/object_set.hpp" @@ -65,7 +65,7 @@ add_geode_library( "sampling/direct/segment_uniform_sampler.hpp" #"sampling/mcmc/helpers/fracture_simulation_runner.hpp" "sampling/mcmc/helpers/simulation_printer.hpp" - "sampling/mcmc/helpers/simulation_monitor.hpp" + #"sampling/mcmc/helpers/simulation_monitor.hpp" "models/energy_terms/energy_term.hpp" "models/energy_terms/pairwise_term.hpp" "models/energy_terms/single_object_term.hpp" diff --git a/tests/stochastic/CMakeLists.txt b/tests/stochastic/CMakeLists.txt index 6132307..b18c043 100644 --- a/tests/stochastic/CMakeLists.txt +++ b/tests/stochastic/CMakeLists.txt @@ -89,21 +89,21 @@ add_geode_test( ${PROJECT_NAME}::stochastic ) -add_geode_test( - SOURCE "sampling/mcmc/helpers/test-simulation_printer.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) +#add_geode_test( +# SOURCE "sampling/mcmc/helpers/test-simulation_printer.cpp" +# DEPENDENCIES +# OpenGeode::basic +# OpenGeode::geometry +# ${PROJECT_NAME}::stochastic +#) -add_geode_test( - SOURCE "sampling/mcmc/helpers/test-simulation_monitor.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) +#add_geode_test( +# SOURCE "sampling/mcmc/helpers/test-simulation_monitor.cpp" +# DEPENDENCIES +# OpenGeode::basic +# OpenGeode::geometry +# ${PROJECT_NAME}::stochastic +#) add_geode_test( SOURCE "models/energy_terms/test-density-term.cpp" diff --git a/tests/stochastic/models/test-energy-term-collection.cpp b/tests/stochastic/models/test-energy-term-collection.cpp new file mode 100644 index 0000000..e69de29 diff --git a/tests/stochastic/sampling/mcmc/helpers/test-simulation_monitor.cpp b/tests/stochastic/sampling/mcmc/helpers/test-simulation_monitor.cpp index e007017..a12c1e4 100644 --- a/tests/stochastic/sampling/mcmc/helpers/test-simulation_monitor.cpp +++ b/tests/stochastic/sampling/mcmc/helpers/test-simulation_monitor.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include @@ -32,10 +32,10 @@ namespace { void test_statistics_monitor_basic() { - geode::Logger::info( "TEST - StatisticsMonitor basic functionality" ); + geode::Logger::info( "TEST - StatisticsTracker basic functionality" ); // --- Create monitor with 3 energy terms - geode::StatisticsMonitor monitor( 3 ); + geode::StatisticsTracker monitor; // --- Add 2 realizations monitor.add_realization( { 1.0, 2.0, 3.0 } ); diff --git a/tests/stochastic/sampling/mcmc/helpers/test-simulation_printer.cpp b/tests/stochastic/sampling/mcmc/helpers/test-simulation_printer.cpp index babe258..def7e24 100644 --- a/tests/stochastic/sampling/mcmc/helpers/test-simulation_printer.cpp +++ b/tests/stochastic/sampling/mcmc/helpers/test-simulation_printer.cpp @@ -22,7 +22,7 @@ */ #include -#include +#include #include #include @@ -69,7 +69,8 @@ namespace "[TEST] SimulationPrinter print statistics summary" ); geode::SimulationPrinter printer( config ); - geode::StatisticsMonitor monitor( 2 ); + + geode::StatisticsTracker tracker( 2 ); monitor.add_realization( { 1, 2 } ); printer.print_statistics_summary( monitor, "EnergyTerm1;EnergyTerm2" ); diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp index f9a34c9..475420e 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp @@ -69,9 +69,9 @@ int main() auto model = geode::build_model< geode::Point2D >( config, object_sets, domain ); - geode::Logger::info( model.terms().size() ); + geode::Logger::info( model->terms().size() ); OPENGEODE_EXCEPTION( - model.terms().size() == 5, "Collection not created" ); + model->terms().size() == 5, "Collection not created" ); return 0; } catch( ... ) diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 2f6c54f..1a13dfd 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -39,20 +39,24 @@ namespace double birth_ratio{ 1.0 }; double death_ratio{ 1.0 }; double change_ratio{ 1.0 }; - }; - struct PoissonDensityDescription - { - geode::SingleObjectTermConfig density_term_config; double target_count; }; + using PoissonDensityDescription = geode::SingleObjectTermConfig; + // struct PoissonDensityDescription + // { + // std::string density_name; + // + // std::vector< std::string > objectset_names{}; + // double density_value{ 1.0 }; + // }; class PoissonSimulationRunner : public geode::SimulationRunner< geode::Point2D > { public: PoissonSimulationRunner( const geode::SpatialDomain< 2 >& domain ) - : geode::SimulationRunner< geode::Point2D >( domain ) {}; + : geode::SimulationRunner< geode::Point2D >( domain ){}; void add_set_descriptor( const SetDescription& descriptor ) { @@ -65,7 +69,8 @@ namespace density_descriptors_.push_back( descriptor ); } - void initialize() override + std::unique_ptr< geode::ProposalKernel< geode::Point2D > > + create_sets_and_set_samplers() { auto proposal_kernel = std::make_unique< geode::ProposalKernel< geode::Point2D > >(); @@ -83,62 +88,73 @@ namespace *proposal_kernel, set_id, set_desc.birth_ratio, set_desc.death_ratio, set_desc.change_ratio ); } + return proposal_kernel; + } - // Step 2: create energy terms + void create_model() + { + geode::ModelConfig config; for( const auto& energy_desc : density_descriptors_ ) { - this->ordered_energy_terms_.push_back( - this->energy_terms_collection_.add_energy_term( std::move( - build_energy_term( energy_desc.density_term_config, - this->object_sets_, this->domain_ ) ) ) ); - - this->ordered_target_statistics_.push_back( - energy_desc.target_count ); + config.terms.push_back( energy_desc ); } + model_ = std::move( geode::build_model< geode::Point2D >( + config, object_sets_, domain_ ) ); + } + + void initialize() override + { + auto proposal_kernel = create_sets_and_set_samplers(); + create_model(); + this->mh_sampler_ = std::make_unique< geode::MetropolisHastings< geode::Point2D > >( - this->energy_terms_collection_, - std::move( proposal_kernel ) ); + *model_, std::move( proposal_kernel ) ); } - void check_statistics( - const geode::StatisticsMonitor& statistic_monitoring ) const + void check_statistics( const geode::StatisticsTracker< geode::Point2D >& + statistic_monitoring ) const { - const auto& computed_means = statistic_monitoring.means(); - const auto& computed_variances = statistic_monitoring.variances(); - - for( const auto stat_id : - geode::Range{ this->energy_terms_collection_.size() } ) - { - const auto& term = energy_terms_collection_.get( - ordered_energy_terms_[stat_id] ); - - const auto expected_means = - this->ordered_target_statistics_[stat_id]; - - const auto target_vs_mean_error = - std::abs( computed_means[stat_id] - expected_means ) - / expected_means; - - OPENGEODE_EXCEPTION( target_vs_mean_error < 0.05, - "[MH test] statistic value ", computed_means[stat_id], - " for energy term: ", - term.name().value_or( term.id().string() ), - " not close enough to expected value ", expected_means, - " --> error : ", target_vs_mean_error ); - - const auto target_vs_variance_error = - std::abs( computed_variances[stat_id] - expected_means ) - / expected_means; - - OPENGEODE_EXCEPTION( target_vs_variance_error < 0.15, - "[MH test] variance of statistic ", - computed_variances[stat_id], " for energy term: ", - term.name().value_or( term.id().string() ), - " not close enough to expected value ", expected_means, - " --> error : ", target_vs_variance_error ); - } + // const auto& computed_means = + // statistic_monitoring.means(); const auto& + // computed_variances = statistic_monitoring.variances(); + // + // for( const auto stat_id : + // geode::Range{ + // this->energy_terms_collection_.size() } ) + // { + // const auto& term = energy_terms_collection_.get( + // ordered_energy_terms_[stat_id] ); + // + // const auto expected_means = + // this->ordered_target_statistics_[stat_id]; + // + // const auto target_vs_mean_error = + // std::abs( computed_means[stat_id] - + // expected_means ) / expected_means; + // + // OPENGEODE_EXCEPTION( target_vs_mean_error < 0.05, + // "[MH test] statistic value ", + // computed_means[stat_id], " for energy term: ", + // term.name().value_or( term.id().string() ), + // " not close enough to expected value ", + // expected_means, " --> error : ", + // target_vs_mean_error ); + // + // const auto target_vs_variance_error = + // std::abs( computed_variances[stat_id] - + // expected_means ) / expected_means; + // + // OPENGEODE_EXCEPTION( target_vs_variance_error < + // 0.15, + // "[MH test] variance of statistic ", + // computed_variances[stat_id], " for energy + // term: ", term.name().value_or( + // term.id().string() ), " not close enough to + // expected value ", expected_means, " --> error + // : ", target_vs_variance_error ); + // } } private: @@ -173,12 +189,11 @@ namespace // --- Energy term description PoissonDensityDescription densityA; - densityA.density_term_config.term_name = "density"; - densityA.density_term_config.object_set_names = { "A" }; - densityA.density_term_config.lambda = 0.3; - densityA.target_count = 30.0; - densityA.density_term_config.object_feature = - geode::ObjectInDomainFeatureConfig{}; + densityA.term_name = "density"; + densityA.object_set_names = { "A" }; + densityA.lambda = 0.3; + // densityA.target_count = 30.0; + densityA.object_feature = geode::ObjectInDomainFeatureConfig{}; PoissonSimulationRunner runner( domain ); runner.add_set_descriptor( setA ); @@ -224,28 +239,25 @@ namespace // --- Energy term descriptions PoissonDensityDescription density01; - density01.density_term_config.term_name = "density01"; - density01.density_term_config.object_set_names = { "set01" }; - density01.density_term_config.lambda = 0.1; - density01.target_count = 10.0; - density01.density_term_config.object_feature = - geode::ObjectInDomainFeatureConfig{}; + density01.term_name = "density01"; + density01.object_set_names = { "set01" }; + density01.lambda = 0.1; + // density01.target_count = 10.0; + density01.object_feature = geode::ObjectInDomainFeatureConfig{}; PoissonDensityDescription density02; - density02.density_term_config.term_name = "density02"; - density02.density_term_config.object_set_names = { "set02" }; - density02.density_term_config.lambda = 0.4; - density02.target_count = 40.0; - density02.density_term_config.object_feature = - geode::ObjectInDomainFeatureConfig{}; + density02.term_name = "density02"; + density02.object_set_names = { "set02" }; + density02.lambda = 0.4; + // density02.target_count = 40.0; + density02.object_feature = geode::ObjectInDomainFeatureConfig{}; PoissonDensityDescription density03; - density03.density_term_config.term_name = "density03"; - density03.density_term_config.object_set_names = { "set03" }; - density03.density_term_config.lambda = 0.3; - density03.target_count = 30.0; - density03.density_term_config.object_feature = - geode::ObjectInDomainFeatureConfig{}; + density03.term_name = "density03"; + density03.object_set_names = { "set03" }; + density03.lambda = 0.3; + // density03.target_count = 30.0; + density03.object_feature = geode::ObjectInDomainFeatureConfig{}; PoissonSimulationRunner runner( domain ); runner.add_set_descriptor( set01 ); diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index aad75ff..2c7de25 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -158,7 +158,7 @@ namespace } void check_statistics( - const geode::StatisticsMonitor& statistic_monitoring ) const + const geode::StatisticsTracker& statistic_monitoring ) const { const auto& computed_means = statistic_monitoring.means(); From 2c94303e2b5c0a79effdd6d2470c4ad27d29beea Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Mon, 25 May 2026 08:56:29 +0000 Subject: [PATCH 15/17] Apply prepare changes --- include/geode/stochastic/sampling/mcmc/simulation_runner.hpp | 2 +- tests/stochastic/sampling/mcmc/test-mh-poisson.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index 6b80b53..c306efb 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -64,7 +64,7 @@ namespace geode { public: SimulationRunner( const SpatialDomain< ObjectType::dim >& domain ) - : domain_( domain ){}; + : domain_( domain ) {}; virtual ~SimulationRunner() = default; virtual void initialize() = 0; diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 5520e39..50857df 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -56,7 +56,7 @@ namespace { public: PoissonSimulationRunner( const geode::SpatialDomain< 2 >& domain ) - : geode::SimulationRunner< geode::Point2D >( domain ){}; + : geode::SimulationRunner< geode::Point2D >( domain ) {}; void add_set_descriptor( const SetDescription& descriptor ) { From 1dd751a74a4fa5d97c5534ff2344158ff7f01c93 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Mon, 25 May 2026 12:57:32 +0200 Subject: [PATCH 16/17] update to last OpenGeode Version --- .../inference/statistic_validator.hpp | 4 +++- .../models/energy_term_collection.hpp | 17 ++++++++++++----- .../energy_terms/energy_term_builder.hpp | 5 +++-- .../mcmc/helpers/simulation_printer.hpp | 5 +++-- .../sampling/mcmc/helpers/state_dynamics.hpp | 4 +++- .../pairwise_interactions_builder.hpp | 5 +++-- .../single_object_feature_builder.hpp | 6 ++++-- src/geode/stochastic/spatial/object_sets.cpp | 19 +++++++++++-------- .../models/energy_terms/test-density-term.cpp | 2 +- .../energy_terms/test-pairwise-term.cpp | 19 ++++++------------- .../sampling/mcmc/test-mh-poisson-new.cpp | 4 ++-- 11 files changed, 51 insertions(+), 39 deletions(-) diff --git a/include/geode/stochastic/inference/statistic_validator.hpp b/include/geode/stochastic/inference/statistic_validator.hpp index 951a1e1..60ebbe5 100644 --- a/include/geode/stochastic/inference/statistic_validator.hpp +++ b/include/geode/stochastic/inference/statistic_validator.hpp @@ -41,7 +41,9 @@ namespace geode const double mean = monitor.mean( target.term_id ); const double rel_error = std::abs( mean - target.value ) / ( std::abs( target.value ) + 1e-12 ); - // OPENGEODE_EXCEPTION( rel_error < t.tolerance, + // OpenGeodeStochasticStochasticException::check_exception( + // rel_error < t.tolerance, nullptr, + // OpenGeodeException::TYPE::data, // "[StatisticsValidator] Failure for term ", // t.term_id.string(), "\n mean = ", // mean, diff --git a/include/geode/stochastic/models/energy_term_collection.hpp b/include/geode/stochastic/models/energy_term_collection.hpp index 957eba8..20d77ed 100644 --- a/include/geode/stochastic/models/energy_term_collection.hpp +++ b/include/geode/stochastic/models/energy_term_collection.hpp @@ -47,19 +47,22 @@ namespace geode auto term_idx = energy_terms_.size(); const auto term_name = term->name(); - OPENGEODE_EXCEPTION( term_name.has_value(), + OpenGeodeStochasticStochasticException::check_exception( + term_name.has_value(), nullptr, OpenGeodeException::TYPE::data, absl::StrCat( "[EnergyTermCollection]- Energy Term name is not " "defined." ) ); const auto term_uuid = term->id(); auto [it, inserted_uuid] = name_to_uuid_.emplace( term_name.value(), term_uuid ); - OPENGEODE_EXCEPTION( inserted_uuid, + OpenGeodeStochasticStochasticException::check_exception( + inserted_uuid, nullptr, OpenGeodeException::TYPE::data, absl::StrCat( "[EnergyTermCollection]- Energy Term named ", term_name.value(), " already exists." ) ); auto [it2, inserted_index] = uuid_to_index_.emplace( term_uuid, term_idx ); - OPENGEODE_EXCEPTION( inserted_index, + OpenGeodeStochasticStochasticException::check_exception( + inserted_index, nullptr, OpenGeodeException::TYPE::data, absl::StrCat( "[EnergyTermCollection]- Energy Term ", term_uuid.string(), " already exists." ) ); @@ -75,7 +78,9 @@ namespace geode [[nodiscard]] index_t get_term_index( const uuid& term_uuid ) const { auto term_it = uuid_to_index_.find( term_uuid ); - OPENGEODE_EXCEPTION( term_it != uuid_to_index_.end(), + OpenGeodeStochasticStochasticException::check_exception( + term_it != uuid_to_index_.end(), nullptr, + OpenGeodeException::TYPE::data, absl::StrCat( "[EnergyTermCollection] Unknown energy term: ", term_uuid.string() ) ); return term_it->second; @@ -90,7 +95,9 @@ namespace geode [[nodiscard]] uuid get_term_uuid( std::string_view name ) const { auto uuid_it = name_to_uuid_.find( name ); - OPENGEODE_EXCEPTION( uuid_it != name_to_uuid_.end(), + OpenGeodeStochasticStochasticException::check_exception( + uuid_it != name_to_uuid_.end(), nullptr, + OpenGeodeException::TYPE::data, absl::StrCat( "[EnergyTermCollection] Unknown energy term: ", name ) ); return uuid_it->second; diff --git a/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp b/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp index ebbc973..8f2d599 100644 --- a/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp @@ -154,8 +154,9 @@ namespace geode const ObjectSets< ObjectType >&, const SpatialDomain< ObjectType::dim >& ) { - throw OpenGeodeException( - "[EnergyTermBuilder] energy term config not initialized" ); + throw OpenGeodeStochasticStochasticException{ nullptr, + OpenGeodeException::TYPE::data, + "[EnergyTermBuilder] energy term config not initialized" }; } template < typename ObjectType > std::unique_ptr< EnergyTerm< ObjectType > > build_energy_term( diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_printer.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_printer.hpp index 5dc87a2..4811835 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_printer.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_printer.hpp @@ -193,8 +193,9 @@ namespace geode std::ofstream file( absolute_path, mode ); if( !file.is_open() ) { - throw geode::OpenGeodeException( - "Cannot open file: " + absolute_path.string() ); + throw geode::OpenGeodeStochasticStochasticException{ nullptr, + OpenGeodeException::TYPE::data, + "Cannot open file: " + absolute_path.string() }; } return file; } diff --git a/include/geode/stochastic/sampling/mcmc/helpers/state_dynamics.hpp b/include/geode/stochastic/sampling/mcmc/helpers/state_dynamics.hpp index 69c4fa4..f4cdeef 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/state_dynamics.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/state_dynamics.hpp @@ -70,7 +70,9 @@ namespace geode ObjectSetSampler< ObjectType >& sampler( const index_t sampler_id ) { - OPENGEODE_EXCEPTION( sampler_id < samplers_.size(), + OpenGeodeStochasticStochasticException::check_exception( + sampler_id < samplers_.size(), nullptr, + OpenGeodeException::TYPE::data, "[STATE DYNAMICS]: Sampler out of range." ); return samplers_[sampler_id].get(); } diff --git a/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp index f637a39..04a6fbf 100644 --- a/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp +++ b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp @@ -58,8 +58,9 @@ namespace geode std::unique_ptr< PairwiseInteraction< ObjectType > > build_pairwise_interaction_impl( const std::monostate& ) { - throw OpenGeodeException( - "[PairWiseInteractionBuilder] interaction config not initialized" ); + throw OpenGeodeStochasticStochasticException{ nullptr, + OpenGeodeException::TYPE::data, + "[PairWiseInteractionBuilder] interaction config not initialized" }; } template < typename ObjectType > diff --git a/include/geode/stochastic/spatial/single_object_features/single_object_feature_builder.hpp b/include/geode/stochastic/spatial/single_object_features/single_object_feature_builder.hpp index 78ac6da..bebd2a1 100644 --- a/include/geode/stochastic/spatial/single_object_features/single_object_feature_builder.hpp +++ b/include/geode/stochastic/spatial/single_object_features/single_object_feature_builder.hpp @@ -68,8 +68,10 @@ namespace geode std::unique_ptr< SingleObjectFeature< ObjectType > > build_single_object_feature_impl( const std::monostate& ) { - throw OpenGeodeException( "[SingleObjectFeatureBuilder] object feature " - "config not initialized" ); + throw OpenGeodeStochasticStochasticException{ nullptr, + OpenGeodeException::TYPE::data, + "[SingleObjectFeatureBuilder] object feature " + "config not initialized" }; } template < typename ObjectType > diff --git a/src/geode/stochastic/spatial/object_sets.cpp b/src/geode/stochastic/spatial/object_sets.cpp index 4b3afbb..5278195 100644 --- a/src/geode/stochastic/spatial/object_sets.cpp +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -12,7 +12,8 @@ namespace geode const uuid& set_id ) const { auto it = uuid_to_index_.find( set_id ); - OPENGEODE_EXCEPTION( it != uuid_to_index_.end(), + OpenGeodeStochasticStochasticException::check_exception( + it != uuid_to_index_.end(), nullptr, OpenGeodeException::TYPE::data, "[ObjectSet] - group (", set_id.string(), ") is not defined." ); return sets_[it->second]; } @@ -99,14 +100,15 @@ namespace geode new_set.set_name( name ); auto [it_set_name, set_uuid_inserted] = name_to_uuid_.emplace( name, set_uuid ); - OPENGEODE_EXCEPTION( - set_uuid_inserted, absl::StrCat( "[ObjectSet]- group named ", name, - " already exists." ) ); + OpenGeodeStochasticStochasticException::check_exception( + set_uuid_inserted, nullptr, OpenGeodeException::TYPE::data, + "[ObjectSet]- group named ", name, " already exists." ); auto [it_set_uuid, set_index_inserted] = uuid_to_index_.emplace( set_uuid, set_index ); - OPENGEODE_EXCEPTION( set_index_inserted, "[ObjectSet]- group (", - set_uuid.string(), ") already exists." ); + OpenGeodeStochasticStochasticException::check_exception( + set_index_inserted, nullptr, OpenGeodeException::TYPE::data, + "[ObjectSet]- group (", set_uuid.string(), ") already exists." ); return set_uuid; } @@ -198,9 +200,10 @@ namespace geode { return set_uuid->second; } - throw OpenGeodeException( + throw OpenGeodeStochasticStochasticException{ nullptr, + OpenGeodeException::TYPE::data, "[ObjectSets] ObjectSet uuid accessor - group named ", name, - " does not exist." ); + " does not exist." }; } template < typename Type > diff --git a/tests/stochastic/models/energy_terms/test-density-term.cpp b/tests/stochastic/models/energy_terms/test-density-term.cpp index 36f8df8..fedbd49 100644 --- a/tests/stochastic/models/energy_terms/test-density-term.cpp +++ b/tests/stochastic/models/energy_terms/test-density-term.cpp @@ -151,7 +151,7 @@ int main() try { - geode::StochasticLibrary::initialize(); + geode::OpenGeodeStochasticStochasticLibrary::initialize(); geode::ObjectSets< geode::Point2D > pattern; auto set_id = init_object_set( pattern ); auto domain = init_domain(); diff --git a/tests/stochastic/models/energy_terms/test-pairwise-term.cpp b/tests/stochastic/models/energy_terms/test-pairwise-term.cpp index 288aea4..971378b 100644 --- a/tests/stochastic/models/energy_terms/test-pairwise-term.cpp +++ b/tests/stochastic/models/energy_terms/test-pairwise-term.cpp @@ -163,28 +163,29 @@ void test_pairwise_term_zero_gamma( "gammadelta_log_change( pattern, obj_id, p4_ref ); geode::OpenGeodeStochasticStochasticException::test( std::isinf( delta ), "[PairwiseTerm] delta_log_change VOI->VOI with " "gammadelta_log_change( pattern, old_buffer, p4_ref ); geode::OpenGeodeStochasticStochasticException::test( std::isinf( delta ), "[PairwiseTerm] delta_log_change buffer->VOI with " - "gammadelta_log_change( pattern, obj_id, buffer_ref ); geode::OpenGeodeStochasticStochasticException::test( std::isinf( delta ), "[PairwiseTerm] delta_log_change VOI->buffer with " - "gammadelta_log_remove( pattern, obj_id ); - DEBUG( delta ); geode::OpenGeodeStochasticStochasticException::test( delta == 0, "[PairwiseTerm] delta_log_remove VOI with " "gamma( config, object_sets, domain ); geode::Logger::info( model->terms().size() ); - OPENGEODE_EXCEPTION( + geode::OpenGeodeStochasticStochasticException::test( model->terms().size() == 5, "Collection not created" ); return 0; } From 538826d108c7dc530c931b484b98921f4fdfa2b6 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Mon, 25 May 2026 13:16:26 +0200 Subject: [PATCH 17/17] fix wind compile --- .../spatial/single_object_features/segment_length_feature.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/geode/stochastic/spatial/single_object_features/segment_length_feature.hpp b/include/geode/stochastic/spatial/single_object_features/segment_length_feature.hpp index 9cd4c5d..944065d 100644 --- a/include/geode/stochastic/spatial/single_object_features/segment_length_feature.hpp +++ b/include/geode/stochastic/spatial/single_object_features/segment_length_feature.hpp @@ -26,7 +26,7 @@ namespace geode { - class SegmentLengthInsideBoxFeature + class opengeode_stochastic_stochastic_api SegmentLengthInsideBoxFeature : public SingleObjectFeature< OwnerSegment2D > { public: