From d42a8b74c09eb42a1fd34009b4384b53516fdea2 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 27 Mar 2026 09:04:03 +0100 Subject: [PATCH 01/59] file reoganisation --- .../energy_terms/density_term.hpp | 2 +- .../energy_terms/energy_term.hpp | 0 .../energy_terms/energy_term_collection.hpp | 2 +- .../energy_terms/gibbs_energy.hpp | 2 +- .../energy_terms/intensity_term.hpp | 2 +- .../energy_terms/pairwise_term.hpp | 2 +- .../energy_terms/single_object_term.hpp | 2 +- .../stochastic/models/object_set_config.hpp | 104 ++++++++++++++++++ .../helpers/fracture_simulation_runner.hpp | 6 +- .../mcmc/metropolis_hasting_sampler.hpp | 2 +- .../sampling/mcmc/simulation_runner.hpp | 4 +- src/geode/stochastic/CMakeLists.txt | 16 +-- .../energy_terms/intensity_term.cpp | 2 +- tests/stochastic/CMakeLists.txt | 36 +++--- .../energy_terms/test-density-term.cpp | 2 +- .../energy_terms/test-gibbs-energy.cpp | 8 +- .../energy_terms/test-intensity-term.cpp | 2 +- .../energy_terms/test-pairwise-term.cpp | 2 +- .../mcmc/test-metropolis-hasting-sampler.cpp | 6 +- .../sampling/mcmc/test-mh-fractures.cpp | 2 +- .../sampling/mcmc/test-mh-poisson.cpp | 6 +- .../sampling/mcmc/test-mh-strauss.cpp | 12 +- 22 files changed, 163 insertions(+), 59 deletions(-) rename include/geode/stochastic/{sampling/mcmc => models}/energy_terms/density_term.hpp (97%) rename include/geode/stochastic/{sampling/mcmc => models}/energy_terms/energy_term.hpp (100%) rename include/geode/stochastic/{sampling/mcmc => models}/energy_terms/energy_term_collection.hpp (98%) rename include/geode/stochastic/{sampling/mcmc => models}/energy_terms/gibbs_energy.hpp (97%) rename include/geode/stochastic/{sampling/mcmc => models}/energy_terms/intensity_term.hpp (95%) rename include/geode/stochastic/{sampling/mcmc => models}/energy_terms/pairwise_term.hpp (99%) rename include/geode/stochastic/{sampling/mcmc => models}/energy_terms/single_object_term.hpp (98%) create mode 100644 include/geode/stochastic/models/object_set_config.hpp rename src/geode/stochastic/{sampling/mcmc => models}/energy_terms/intensity_term.cpp (98%) rename tests/stochastic/{sampling/mcmc => models}/energy_terms/test-density-term.cpp (98%) rename tests/stochastic/{sampling/mcmc => models}/energy_terms/test-gibbs-energy.cpp (93%) rename tests/stochastic/{sampling/mcmc => models}/energy_terms/test-intensity-term.cpp (98%) rename tests/stochastic/{sampling/mcmc => models}/energy_terms/test-pairwise-term.cpp (99%) diff --git a/include/geode/stochastic/sampling/mcmc/energy_terms/density_term.hpp b/include/geode/stochastic/models/energy_terms/density_term.hpp similarity index 97% rename from include/geode/stochastic/sampling/mcmc/energy_terms/density_term.hpp rename to include/geode/stochastic/models/energy_terms/density_term.hpp index 91e3858..9d10eed 100644 --- a/include/geode/stochastic/sampling/mcmc/energy_terms/density_term.hpp +++ b/include/geode/stochastic/models/energy_terms/density_term.hpp @@ -24,7 +24,7 @@ #include -#include +#include namespace geode { diff --git a/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term.hpp b/include/geode/stochastic/models/energy_terms/energy_term.hpp similarity index 100% rename from include/geode/stochastic/sampling/mcmc/energy_terms/energy_term.hpp rename to include/geode/stochastic/models/energy_terms/energy_term.hpp diff --git a/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term_collection.hpp b/include/geode/stochastic/models/energy_terms/energy_term_collection.hpp similarity index 98% rename from include/geode/stochastic/sampling/mcmc/energy_terms/energy_term_collection.hpp rename to include/geode/stochastic/models/energy_terms/energy_term_collection.hpp index 5826df9..9e05483 100644 --- a/include/geode/stochastic/sampling/mcmc/energy_terms/energy_term_collection.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term_collection.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include namespace geode { diff --git a/include/geode/stochastic/sampling/mcmc/energy_terms/gibbs_energy.hpp b/include/geode/stochastic/models/energy_terms/gibbs_energy.hpp similarity index 97% rename from include/geode/stochastic/sampling/mcmc/energy_terms/gibbs_energy.hpp rename to include/geode/stochastic/models/energy_terms/gibbs_energy.hpp index 2718107..dd40761 100644 --- a/include/geode/stochastic/sampling/mcmc/energy_terms/gibbs_energy.hpp +++ b/include/geode/stochastic/models/energy_terms/gibbs_energy.hpp @@ -22,7 +22,7 @@ */ #pragma once -#include +#include #include namespace geode diff --git a/include/geode/stochastic/sampling/mcmc/energy_terms/intensity_term.hpp b/include/geode/stochastic/models/energy_terms/intensity_term.hpp similarity index 95% rename from include/geode/stochastic/sampling/mcmc/energy_terms/intensity_term.hpp rename to include/geode/stochastic/models/energy_terms/intensity_term.hpp index 7cd9dc1..bc63a75 100644 --- a/include/geode/stochastic/sampling/mcmc/energy_terms/intensity_term.hpp +++ b/include/geode/stochastic/models/energy_terms/intensity_term.hpp @@ -24,7 +24,7 @@ #include -#include +#include namespace geode { diff --git a/include/geode/stochastic/sampling/mcmc/energy_terms/pairwise_term.hpp b/include/geode/stochastic/models/energy_terms/pairwise_term.hpp similarity index 99% rename from include/geode/stochastic/sampling/mcmc/energy_terms/pairwise_term.hpp rename to include/geode/stochastic/models/energy_terms/pairwise_term.hpp index 4f12ad1..55a2eb4 100644 --- a/include/geode/stochastic/sampling/mcmc/energy_terms/pairwise_term.hpp +++ b/include/geode/stochastic/models/energy_terms/pairwise_term.hpp @@ -26,7 +26,7 @@ #include #include -#include +#include namespace geode { diff --git a/include/geode/stochastic/sampling/mcmc/energy_terms/single_object_term.hpp b/include/geode/stochastic/models/energy_terms/single_object_term.hpp similarity index 98% rename from include/geode/stochastic/sampling/mcmc/energy_terms/single_object_term.hpp rename to include/geode/stochastic/models/energy_terms/single_object_term.hpp index d98240c..e9bf6f8 100644 --- a/include/geode/stochastic/sampling/mcmc/energy_terms/single_object_term.hpp +++ b/include/geode/stochastic/models/energy_terms/single_object_term.hpp @@ -24,7 +24,7 @@ #include -#include +#include #include namespace geode diff --git a/include/geode/stochastic/models/object_set_config.hpp b/include/geode/stochastic/models/object_set_config.hpp new file mode 100644 index 0000000..bead204 --- /dev/null +++ b/include/geode/stochastic/models/object_set_config.hpp @@ -0,0 +1,104 @@ + +class ObjectSetModelConfig +{ +public: + virtual ~ObjectSetModelConfig() = default; + virtual void add_energy_terms( const uuid& set_id, + EnergyTermCollection< OwnerSegment2D >& collection, + const SpatialDomain< 2 >& domain ) const = 0; +}; + +class PoissonSetModelConfig : public ObjectSetModelConfig +{ +public: + double intensity{ 1.0 }; + virtual void add_energy_terms( const uuid& set_id, + EnergyTermCollection< OwnerSegment2D >& collection, + const SpatialDomain< 2 >& domain ) const + { + 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_ ) ); + } +}; + +class ObjectSetDynamicsConfig +{ +public: + double birth_weight{ 1.0 }; + double death_weight{ 1.0 }; + double change_weight{ 1.0 }; +}; + +class ObjectSetConfig +{ +public: + std::string name; + + std::unique_ptr< ObjectSetModelConfig > set_model_; + SetDynamicsConfig dynamics; + std::unique_ptr< ObjectSetSampler< OwnerSegment2D > > build_sampler( + const SpatialDomain< 2 >& domain ) const + { + return std::make_unique< UniformSegmentSetSampler >( domain ); + } +}; + +class ObjectSetsConfig +{ +public: + std::string name; + // Nom du type de modèle + virtual std::string model_name() const = 0; + std::vector< ObjectSetConfig > objectset_config_; + + EnergyTermCollection< OwnerSegment2D > build_energy_terms( + const SpatialDomain< 2 >& domain ) const + { + EnergyTermCollection< OwnerSegment2D > collection; + for( const auto& set : sets ) + { + const uuid set_id = uuid::create(); + set.model->add_energy_terms( set_id, collection, domain ); + } + return collection; + } + + std::vector< std::unique_ptr< ObjectSetSampler< OwnerSegment2D > > > + build_samplers( const SpatialDomain< 2 >& domain ) const + { + std::vector< std::unique_ptr< ObjectSetSampler< OwnerSegment2D > > > + samplers; + for( const auto& set : sets ) + { + samplers.push_back( set.dynamics.build_sampler( domain ) ); + } + return samplers; + } + + std::unique_ptr< ProposalKernel< OwnerSegment2D > > build_kernel() const + { + auto kernel = std::make_unique< ProposalKernel< OwnerSegment2D > >(); + for( const auto& set : sets ) + { + const uuid set_id = uuid::create(); // doit correspondre au set réel + kernel->add_move( + set_id, std::make_unique< BirthMove< OwnerSegment2D > >( + set.dynamics.birth_weight ) ); + kernel->add_move( + set_id, std::make_unique< DeathMove< OwnerSegment2D > >( + set.dynamics.death_weight ) ); + kernel->add_move( + set_id, std::make_unique< ChangeMove< OwnerSegment2D > >( + set.dynamics.change_weight ) ); + } + return kernel; + } +}; +class FractureModelConfig : public ObjectSetsConfig +{ +public: + double beta_x_node_{ 1. }; +}; \ 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 237e741..f34d136 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp @@ -25,11 +25,11 @@ #include #include +#include +#include +#include #include #include -#include -#include -#include #include #include #include diff --git a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp index 64a80ec..0f09b67 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 diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index dd432db..cb555fa 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -23,7 +23,7 @@ #pragma once #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; diff --git a/src/geode/stochastic/CMakeLists.txt b/src/geode/stochastic/CMakeLists.txt index 0394ccc..80bb5ca 100644 --- a/src/geode/stochastic/CMakeLists.txt +++ b/src/geode/stochastic/CMakeLists.txt @@ -33,7 +33,7 @@ add_geode_library( "sampling/direct/segment_uniform_sampler.cpp" "sampling/distributions.cpp" "sampling/random_engine.cpp" - "sampling/mcmc/energy_terms/intensity_term.cpp" + "models/energy_terms/intensity_term.cpp" "common.cpp" PUBLIC_HEADERS "inference/abc_shadow.hpp" @@ -56,13 +56,13 @@ add_geode_library( "sampling/mcmc/helpers/fracture_simulation_runner.hpp" "sampling/mcmc/helpers/simulation_printer.hpp" "sampling/mcmc/helpers/simulation_monitor.hpp" - "sampling/mcmc/energy_terms/density_term.hpp" - "sampling/mcmc/energy_terms/energy_term.hpp" - "sampling/mcmc/energy_terms/intensity_term.hpp" - "sampling/mcmc/energy_terms/pairwise_term.hpp" - "sampling/mcmc/energy_terms/single_object_term.hpp" - "sampling/mcmc/energy_terms/energy_term_collection.hpp" - "sampling/mcmc/energy_terms/gibbs_energy.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_collection.hpp" + "models/energy_terms/gibbs_energy.hpp" "sampling/mcmc/proposal/classical_proposals.hpp" "sampling/mcmc/proposal/moves.hpp" "sampling/mcmc/proposal/proposal_kernel.hpp" diff --git a/src/geode/stochastic/sampling/mcmc/energy_terms/intensity_term.cpp b/src/geode/stochastic/models/energy_terms/intensity_term.cpp similarity index 98% rename from src/geode/stochastic/sampling/mcmc/energy_terms/intensity_term.cpp rename to src/geode/stochastic/models/energy_terms/intensity_term.cpp index a207110..30cb9ec 100644 --- a/src/geode/stochastic/sampling/mcmc/energy_terms/intensity_term.cpp +++ b/src/geode/stochastic/models/energy_terms/intensity_term.cpp @@ -22,7 +22,7 @@ * */ -#include +#include #include diff --git a/tests/stochastic/CMakeLists.txt b/tests/stochastic/CMakeLists.txt index 0239d46..370280c 100644 --- a/tests/stochastic/CMakeLists.txt +++ b/tests/stochastic/CMakeLists.txt @@ -106,7 +106,7 @@ add_geode_test( ) add_geode_test( - SOURCE "sampling/mcmc/energy_terms/test-density-term.cpp" + SOURCE "models/energy_terms/test-density-term.cpp" DEPENDENCIES OpenGeode::basic OpenGeode::geometry @@ -114,7 +114,7 @@ add_geode_test( ) add_geode_test( - SOURCE "sampling/mcmc/energy_terms/test-intensity-term.cpp" + SOURCE "models/energy_terms/test-intensity-term.cpp" DEPENDENCIES OpenGeode::basic OpenGeode::geometry @@ -122,7 +122,7 @@ add_geode_test( ) add_geode_test( - SOURCE "sampling/mcmc/energy_terms/test-pairwise-term.cpp" + SOURCE "models/energy_terms/test-pairwise-term.cpp" DEPENDENCIES OpenGeode::basic OpenGeode::geometry @@ -130,7 +130,7 @@ add_geode_test( ) add_geode_test( - SOURCE "sampling/mcmc/energy_terms/test-gibbs-energy.cpp" + SOURCE "models/energy_terms/test-gibbs-energy.cpp" DEPENDENCIES OpenGeode::basic OpenGeode::geometry @@ -153,13 +153,13 @@ add_geode_test( ${PROJECT_NAME}::stochastic ) -add_geode_test( - SOURCE "sampling/mcmc/test-mh-fractures.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) +#add_geode_test( +# SOURCE "sampling/mcmc/test-mh-fractures.cpp" +# DEPENDENCIES +# OpenGeode::basic +# OpenGeode::geometry +# ${PROJECT_NAME}::stochastic +#) add_geode_test( SOURCE "sampling/mcmc/test-mh-poisson.cpp" @@ -169,13 +169,13 @@ add_geode_test( ${PROJECT_NAME}::stochastic ) -add_geode_test( - SOURCE "sampling/mcmc/test-mh-strauss.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) +#add_geode_test( +# SOURCE "sampling/mcmc/test-mh-strauss.cpp" +# DEPENDENCIES +# OpenGeode::basic +# OpenGeode::geometry +# ${PROJECT_NAME}::stochastic +#) add_geode_test( diff --git a/tests/stochastic/sampling/mcmc/energy_terms/test-density-term.cpp b/tests/stochastic/models/energy_terms/test-density-term.cpp similarity index 98% rename from tests/stochastic/sampling/mcmc/energy_terms/test-density-term.cpp rename to tests/stochastic/models/energy_terms/test-density-term.cpp index 217d430..0d66c3f 100644 --- a/tests/stochastic/sampling/mcmc/energy_terms/test-density-term.cpp +++ b/tests/stochastic/models/energy_terms/test-density-term.cpp @@ -20,7 +20,7 @@ * SOFTWARE. * */ -#include +#include #include #include diff --git a/tests/stochastic/sampling/mcmc/energy_terms/test-gibbs-energy.cpp b/tests/stochastic/models/energy_terms/test-gibbs-energy.cpp similarity index 93% rename from tests/stochastic/sampling/mcmc/energy_terms/test-gibbs-energy.cpp rename to tests/stochastic/models/energy_terms/test-gibbs-energy.cpp index 8cdc90a..897ac84 100644 --- a/tests/stochastic/sampling/mcmc/energy_terms/test-gibbs-energy.cpp +++ b/tests/stochastic/models/energy_terms/test-gibbs-energy.cpp @@ -25,10 +25,10 @@ #include -#include -#include -#include -#include +#include +#include +#include +#include #include void test_gibbs_energy() diff --git a/tests/stochastic/sampling/mcmc/energy_terms/test-intensity-term.cpp b/tests/stochastic/models/energy_terms/test-intensity-term.cpp similarity index 98% rename from tests/stochastic/sampling/mcmc/energy_terms/test-intensity-term.cpp rename to tests/stochastic/models/energy_terms/test-intensity-term.cpp index 0a3f14f..aeb3db0 100644 --- a/tests/stochastic/sampling/mcmc/energy_terms/test-intensity-term.cpp +++ b/tests/stochastic/models/energy_terms/test-intensity-term.cpp @@ -20,7 +20,7 @@ * SOFTWARE. * */ -#include +#include #include #include diff --git a/tests/stochastic/sampling/mcmc/energy_terms/test-pairwise-term.cpp b/tests/stochastic/models/energy_terms/test-pairwise-term.cpp similarity index 99% rename from tests/stochastic/sampling/mcmc/energy_terms/test-pairwise-term.cpp rename to tests/stochastic/models/energy_terms/test-pairwise-term.cpp index a94a04f..2684021 100644 --- a/tests/stochastic/sampling/mcmc/energy_terms/test-pairwise-term.cpp +++ b/tests/stochastic/models/energy_terms/test-pairwise-term.cpp @@ -22,7 +22,7 @@ */ #include -#include +#include #include #include diff --git a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp index 4acc3b5..cca210e 100644 --- a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp +++ b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp @@ -21,10 +21,10 @@ * */ #include +#include +#include +#include #include -#include -#include -#include #include #include namespace diff --git a/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp b/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp index eaea6a7..3124cf9 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp @@ -21,7 +21,7 @@ * */ -#include +#include namespace { diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 4415275..5498bb8 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -21,9 +21,9 @@ * */ #include +#include +#include #include -#include -#include #include #include #include @@ -50,7 +50,7 @@ namespace { public: PoissonSimulationRunner( const geode::SpatialDomain< 2 >& domain ) - : geode::SimulationRunner< geode::Point2D >( domain ) {}; + : geode::SimulationRunner< geode::Point2D >( domain ){}; void add_set_descriptor( const SetDescription& descriptor ) { diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index e5e3eb0..737fa01 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -21,13 +21,13 @@ * */ #include +#include +#include +#include +#include +#include +#include #include -#include -#include -#include -#include -#include -#include #include #include From b3e9957fd6b705631d64da8b811b5912755d6bc6 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 31 Mar 2026 15:27:13 +0200 Subject: [PATCH 02/59] feat(ObjectSets): add map registry in ObjectSets. feat(Model): create a factory for energy terms. --- .../energy_term_collection.hpp | 0 .../energy_terms/energy_term_builder.hpp | 96 ++++++++++++++++ .../energy_term_config.hpp} | 28 ++++- .../{energy_terms => }/gibbs_energy.hpp | 2 +- .../stochastic/models/model_configuration.hpp | 64 +++++++++++ .../stochastic/models/object_set_config.hpp | 104 ------------------ .../sampling/mcmc/helpers/state_dynamics.hpp | 84 ++++++++++++++ .../mcmc/metropolis_hasting_sampler.hpp | 2 +- .../proposal/object_set_dynamic_config.hpp | 0 .../mcmc/proposal/proposal_kernel.hpp | 3 + .../sampling/mcmc/simulation_runner.hpp | 2 +- .../geode/stochastic/spatial/object_sets.hpp | 8 +- .../spatial/pairwise_interactions_builder.hpp | 23 ++++ .../spatial/pairwise_interactions_config.hpp | 38 +++++++ src/geode/stochastic/CMakeLists.txt | 8 +- .../sampling/direct/double_sampler.cpp | 1 + src/geode/stochastic/spatial/object_sets.cpp | 28 ++++- tests/stochastic/CMakeLists.txt | 7 ++ .../models/energy_terms/test-gibbs-energy.cpp | 4 +- .../mcmc/test-metropolis-hasting-sampler.cpp | 4 +- .../sampling/mcmc/test-mh-poisson-new.cpp | 82 ++++++++++++++ .../sampling/mcmc/test-mh-poisson.cpp | 2 +- .../sampling/mcmc/test-mh-strauss.cpp | 2 +- 23 files changed, 471 insertions(+), 121 deletions(-) rename include/geode/stochastic/models/{energy_terms => }/energy_term_collection.hpp (100%) create mode 100644 include/geode/stochastic/models/energy_terms/energy_term_builder.hpp rename include/geode/stochastic/models/{spatial_model.hpp => energy_terms/energy_term_config.hpp} (69%) rename include/geode/stochastic/models/{energy_terms => }/gibbs_energy.hpp (98%) create mode 100644 include/geode/stochastic/models/model_configuration.hpp delete mode 100644 include/geode/stochastic/models/object_set_config.hpp create mode 100644 include/geode/stochastic/sampling/mcmc/helpers/state_dynamics.hpp create mode 100644 include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp create mode 100644 include/geode/stochastic/spatial/pairwise_interactions_builder.hpp create mode 100644 include/geode/stochastic/spatial/pairwise_interactions_config.hpp create mode 100644 tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp diff --git a/include/geode/stochastic/models/energy_terms/energy_term_collection.hpp b/include/geode/stochastic/models/energy_term_collection.hpp similarity index 100% rename from include/geode/stochastic/models/energy_terms/energy_term_collection.hpp rename to include/geode/stochastic/models/energy_term_collection.hpp diff --git a/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp b/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp new file mode 100644 index 0000000..e71c2bb --- /dev/null +++ b/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp @@ -0,0 +1,96 @@ +/* + * 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 +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace geode +{ + template < typename ObjectType > + std::unique_ptr< EnergyTerm< ObjectType > > build_density_term( + const DensityTermConfig& cfg, + const ObjectSets< ObjectType >& object_sets, + const SpatialDomain< ObjectType::dim >& domain ) + { + auto set_ids = + object_sets.get_existing_set_uuids( cfg.object_set_names ); + + return std::make_unique< geode::DensityTerm< ObjectType > >( + cfg.term_name, cfg.lambda, set_ids, domain ); + } + + 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 interaction = + std::make_unique< geode::EuclideanCutoffInteraction< ObjectType > >( + 0.5 + /*,interaction_desc.interaction_scope*/ ); + + return std::make_unique< geode::PairwiseTerm< geode::Point2D > >( + cfg.term_name, cfg.gamma, set_ids, std::move( interaction ), + domain ); + } + + template < typename ObjectType > + std::unique_ptr< EnergyTerm< ObjectType > > build_term( + const EnergyTermConfig& 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 ) >; + + if constexpr( std::is_same_v< T, DensityTermConfig > ) + return build_density_term< ObjectType >( + c, object_sets, domain ); + + else if constexpr( std::is_same_v< T, PairwiseTermConfig > ) + return build_pairwise_term< ObjectType >( + c, object_sets, domain ); + }, + cfg ); + } + +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/models/spatial_model.hpp b/include/geode/stochastic/models/energy_terms/energy_term_config.hpp similarity index 69% rename from include/geode/stochastic/models/spatial_model.hpp rename to include/geode/stochastic/models/energy_terms/energy_term_config.hpp index 1daba84..b617856 100644 --- a/include/geode/stochastic/models/spatial_model.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term_config.hpp @@ -19,4 +19,30 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * - */ \ No newline at end of file + */ +#pragma once + +#include +#include +#include + +namespace geode +{ + struct DensityTermConfig + { + std::string term_name; + std::vector< std::string > object_set_names; + double lambda; + }; + + struct PairwiseTermConfig + { + std::string term_name; + std::vector< std::string > object_set_names; + double gamma; + }; + + using EnergyTermConfig = + std::variant< DensityTermConfig, PairwiseTermConfig >; + +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/models/energy_terms/gibbs_energy.hpp b/include/geode/stochastic/models/gibbs_energy.hpp similarity index 98% rename from include/geode/stochastic/models/energy_terms/gibbs_energy.hpp rename to include/geode/stochastic/models/gibbs_energy.hpp index dd40761..70352df 100644 --- a/include/geode/stochastic/models/energy_terms/gibbs_energy.hpp +++ b/include/geode/stochastic/models/gibbs_energy.hpp @@ -22,7 +22,7 @@ */ #pragma once -#include +#include #include namespace geode diff --git a/include/geode/stochastic/models/model_configuration.hpp b/include/geode/stochastic/models/model_configuration.hpp new file mode 100644 index 0000000..9221eb7 --- /dev/null +++ b/include/geode/stochastic/models/model_configuration.hpp @@ -0,0 +1,64 @@ +/* + * 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 + +#include +#include +#include + +#include +#include +#include + +#include +#include +namespace geode +{ + + struct ModelConfig + { + std::vector< EnergyTermConfig > terms; + }; + + template < typename ObjectType > + EnergyTermCollection< ObjectType > build_energy_term_collection( + const ModelConfig& config, + const ObjectSets< ObjectType >& object_sets, + const SpatialDomain< ObjectType::dim >& domain ) + { + EnergyTermCollection< ObjectType > collection; + + for( const auto& term_cfg : config.terms ) + { + auto term_id = collection.add_energy_term( + build_term< ObjectType >( term_cfg, object_sets, domain ) ); + } + + return collection; + } +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/models/object_set_config.hpp b/include/geode/stochastic/models/object_set_config.hpp deleted file mode 100644 index bead204..0000000 --- a/include/geode/stochastic/models/object_set_config.hpp +++ /dev/null @@ -1,104 +0,0 @@ - -class ObjectSetModelConfig -{ -public: - virtual ~ObjectSetModelConfig() = default; - virtual void add_energy_terms( const uuid& set_id, - EnergyTermCollection< OwnerSegment2D >& collection, - const SpatialDomain< 2 >& domain ) const = 0; -}; - -class PoissonSetModelConfig : public ObjectSetModelConfig -{ -public: - double intensity{ 1.0 }; - virtual void add_energy_terms( const uuid& set_id, - EnergyTermCollection< OwnerSegment2D >& collection, - const SpatialDomain< 2 >& domain ) const - { - 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_ ) ); - } -}; - -class ObjectSetDynamicsConfig -{ -public: - double birth_weight{ 1.0 }; - double death_weight{ 1.0 }; - double change_weight{ 1.0 }; -}; - -class ObjectSetConfig -{ -public: - std::string name; - - std::unique_ptr< ObjectSetModelConfig > set_model_; - SetDynamicsConfig dynamics; - std::unique_ptr< ObjectSetSampler< OwnerSegment2D > > build_sampler( - const SpatialDomain< 2 >& domain ) const - { - return std::make_unique< UniformSegmentSetSampler >( domain ); - } -}; - -class ObjectSetsConfig -{ -public: - std::string name; - // Nom du type de modèle - virtual std::string model_name() const = 0; - std::vector< ObjectSetConfig > objectset_config_; - - EnergyTermCollection< OwnerSegment2D > build_energy_terms( - const SpatialDomain< 2 >& domain ) const - { - EnergyTermCollection< OwnerSegment2D > collection; - for( const auto& set : sets ) - { - const uuid set_id = uuid::create(); - set.model->add_energy_terms( set_id, collection, domain ); - } - return collection; - } - - std::vector< std::unique_ptr< ObjectSetSampler< OwnerSegment2D > > > - build_samplers( const SpatialDomain< 2 >& domain ) const - { - std::vector< std::unique_ptr< ObjectSetSampler< OwnerSegment2D > > > - samplers; - for( const auto& set : sets ) - { - samplers.push_back( set.dynamics.build_sampler( domain ) ); - } - return samplers; - } - - std::unique_ptr< ProposalKernel< OwnerSegment2D > > build_kernel() const - { - auto kernel = std::make_unique< ProposalKernel< OwnerSegment2D > >(); - for( const auto& set : sets ) - { - const uuid set_id = uuid::create(); // doit correspondre au set réel - kernel->add_move( - set_id, std::make_unique< BirthMove< OwnerSegment2D > >( - set.dynamics.birth_weight ) ); - kernel->add_move( - set_id, std::make_unique< DeathMove< OwnerSegment2D > >( - set.dynamics.death_weight ) ); - kernel->add_move( - set_id, std::make_unique< ChangeMove< OwnerSegment2D > >( - set.dynamics.change_weight ) ); - } - return kernel; - } -}; -class FractureModelConfig : public ObjectSetsConfig -{ -public: - double beta_x_node_{ 1. }; -}; \ No newline at end of file diff --git a/include/geode/stochastic/sampling/mcmc/helpers/state_dynamics.hpp b/include/geode/stochastic/sampling/mcmc/helpers/state_dynamics.hpp new file mode 100644 index 0000000..69c4fa4 --- /dev/null +++ b/include/geode/stochastic/sampling/mcmc/helpers/state_dynamics.hpp @@ -0,0 +1,84 @@ +/* + * 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 +#include + +namespace geode +{ + + template < typename ObjectType > + class StateDynamics + { + public: + StateDynamics() = default; + ~StateDynamics() = default; + + ObjectSets< ObjectType >& state() + { + return object_sets_; + } + const ObjectSets< ObjectType >& state() const + { + return object_sets_; + } + + std::vector< std::unique_ptr< ObjectSetSampler< ObjectType > > >& + samplers() + { + return samplers_; + } + const std::vector< std::unique_ptr< ObjectSetSampler< ObjectType > > >& + samplers() const + { + return samplers_; + } + + uuid add_set( const std::string& name ) + { + return object_sets_.add_set( name ); + } + + void add_sampler( + std::unique_ptr< ObjectSetSampler< ObjectType > > sampler ) + { + samplers_.push_back( std::move( sampler ) ); + } + + ObjectSetSampler< ObjectType >& sampler( const index_t sampler_id ) + { + OPENGEODE_EXCEPTION( sampler_id < samplers_.size(), + "[STATE DYNAMICS]: Sampler out of range." ); + return samplers_[sampler_id].get(); + } + + private: + ObjectSets< ObjectType > object_sets_; + std::vector< std::unique_ptr< ObjectSetSampler< ObjectType > > > + samplers_; + }; + +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp index 0f09b67..565e01c 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 diff --git a/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp b/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp new file mode 100644 index 0000000..e69de29 diff --git a/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp b/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp index 97863f5..1b32271 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp @@ -23,6 +23,9 @@ #pragma once +#include +#include + #include #include diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index cb555fa..494fb20 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -23,7 +23,7 @@ #pragma once #include -#include +#include #include #include #include diff --git a/include/geode/stochastic/spatial/object_sets.hpp b/include/geode/stochastic/spatial/object_sets.hpp index c6881d4..e8b06a6 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 @@ -82,11 +82,15 @@ namespace geode std::string string() const; + std::vector< uuid > get_existing_set_uuids( + const std::vector< std::string > set_names ) const; + private: ObjectSet< Type >& get_set( const uuid& set_id ); private: - absl::flat_hash_map< uuid, ObjectSet< Type > > sets_; + absl::btree_map< std::string, geode::uuid > object_set_name_to_uuid_; + absl::btree_map< uuid, ObjectSet< Type > > sets_; ObjectNeighborhood< Type::dim > neighborhood_; }; } // namespace geode diff --git a/include/geode/stochastic/spatial/pairwise_interactions_builder.hpp b/include/geode/stochastic/spatial/pairwise_interactions_builder.hpp new file mode 100644 index 0000000..e7a780b --- /dev/null +++ b/include/geode/stochastic/spatial/pairwise_interactions_builder.hpp @@ -0,0 +1,23 @@ +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, 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 ); + } + }, + cfg ); +} \ No newline at end of file diff --git a/include/geode/stochastic/spatial/pairwise_interactions_config.hpp b/include/geode/stochastic/spatial/pairwise_interactions_config.hpp new file mode 100644 index 0000000..3b453bf --- /dev/null +++ b/include/geode/stochastic/spatial/pairwise_interactions_config.hpp @@ -0,0 +1,38 @@ +struct EuclideanCenterDistanceConfig +{ + double threshold; + double weight; +}; + +struct HausdorffDistanceConfig +{ + double threshold; + double weight; +}; + +using PairwiseInteractionConfig = + std::variant< EuclideanCenterDistanceConfig, HausdorffDistanceConfig >; + +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, 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 ); + } + }, + cfg ); +} \ No newline at end of file diff --git a/src/geode/stochastic/CMakeLists.txt b/src/geode/stochastic/CMakeLists.txt index 80bb5ca..158d4b5 100644 --- a/src/geode/stochastic/CMakeLists.txt +++ b/src/geode/stochastic/CMakeLists.txt @@ -37,7 +37,6 @@ add_geode_library( "common.cpp" PUBLIC_HEADERS "inference/abc_shadow.hpp" - "models/spatial_model.hpp" "spatial/object_set.hpp" "spatial/object_sets.hpp" "spatial/object_neighborhood.hpp" @@ -61,8 +60,11 @@ add_geode_library( "models/energy_terms/intensity_term.hpp" "models/energy_terms/pairwise_term.hpp" "models/energy_terms/single_object_term.hpp" - "models/energy_terms/energy_term_collection.hpp" - "models/energy_terms/gibbs_energy.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" "sampling/mcmc/proposal/classical_proposals.hpp" "sampling/mcmc/proposal/moves.hpp" "sampling/mcmc/proposal/proposal_kernel.hpp" diff --git a/src/geode/stochastic/sampling/direct/double_sampler.cpp b/src/geode/stochastic/sampling/direct/double_sampler.cpp index d034f16..4c406b9 100644 --- a/src/geode/stochastic/sampling/direct/double_sampler.cpp +++ b/src/geode/stochastic/sampling/direct/double_sampler.cpp @@ -20,6 +20,7 @@ * SOFTWARE. * */ +#include #include diff --git a/src/geode/stochastic/spatial/object_sets.cpp b/src/geode/stochastic/spatial/object_sets.cpp index 67c213d..2127c22 100644 --- a/src/geode/stochastic/spatial/object_sets.cpp +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -95,8 +95,14 @@ namespace geode ObjectSet< Type > new_set; 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 (", + auto [it_set_name, set_id_inserted] = + object_set_name_to_uuid_.emplace( name, new_set_id ); + 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; } @@ -188,6 +194,24 @@ namespace geode static_cast< const ObjectSets* >( this )->get_set( set_id ) ); } + template < typename Type > + std::vector< uuid > ObjectSets< Type >::get_existing_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 ); + } + } + return uuids; + } + template < typename Type > std::string ObjectSets< Type >::string() const { diff --git a/tests/stochastic/CMakeLists.txt b/tests/stochastic/CMakeLists.txt index 370280c..581dc80 100644 --- a/tests/stochastic/CMakeLists.txt +++ b/tests/stochastic/CMakeLists.txt @@ -168,6 +168,13 @@ add_geode_test( OpenGeode::geometry ${PROJECT_NAME}::stochastic ) +add_geode_test( + SOURCE "sampling/mcmc/test-mh-poisson-new.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) #add_geode_test( # SOURCE "sampling/mcmc/test-mh-strauss.cpp" diff --git a/tests/stochastic/models/energy_terms/test-gibbs-energy.cpp b/tests/stochastic/models/energy_terms/test-gibbs-energy.cpp index 897ac84..3ccf07e 100644 --- a/tests/stochastic/models/energy_terms/test-gibbs-energy.cpp +++ b/tests/stochastic/models/energy_terms/test-gibbs-energy.cpp @@ -25,10 +25,10 @@ #include +#include #include -#include -#include #include +#include #include void test_gibbs_energy() diff --git a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp index cca210e..0761786 100644 --- a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp +++ b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp @@ -21,9 +21,9 @@ * */ #include +#include #include -#include -#include +#include #include #include #include diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp new file mode 100644 index 0000000..bd7cc89 --- /dev/null +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp @@ -0,0 +1,82 @@ +/* + * 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 +{ + struct SetDescription + { + std::string name; + geode::index_t number_of_object; + std::vector< geode::Point2D > fixed_object; + }; +} // namespace + +int main() +{ + try + { + geode::StochasticLibrary::initialize(); + geode::Logger::set_level( geode::Logger::LEVEL::debug ); + + std::string set_name1 = "set01"; + std::string set_name2 = "set02"; + std::string set_name3 = "set03"; + std::string set_name4 = "set04"; + + geode::ObjectSets< geode::Point2D > object_sets; + const auto set_id1 = object_sets.add_set( set_name1 ); + 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::PairwiseTermConfig{ + "eclidiant_set2_3_4", { set_name4, set_name2, set_name3 }, 0.5 } ); + + geode::BoundingBox2D box; + box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); + 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" ); + return 0; + } + catch( ... ) + { + return geode::geode_lippincott(); + } +} \ No newline at end of file diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 5498bb8..063578e 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -22,7 +22,7 @@ */ #include #include -#include +#include #include #include #include diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index 737fa01..4545bd2 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -22,8 +22,8 @@ */ #include #include -#include #include +#include #include #include #include From 5b95c351c91a5e3311b5c6a5dcea28224f531c91 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 1 Apr 2026 13:58:52 +0200 Subject: [PATCH 03/59] keep refactoring --- .../energy_terms/energy_term_builder.hpp | 5 +- .../energy_terms/energy_term_config.hpp | 2 + .../models/energy_terms/pairwise_term.hpp | 2 +- .../helpers/fracture_simulation_runner.hpp | 18 +-- .../pairwise_interactions/distance_cutoff.hpp | 83 ++++++++++ .../pairwise_interactions.hpp | 22 --- .../pairwise_interactions_builder.hpp | 54 +++++++ .../pairwise_interactions_config.hpp | 71 +++++++++ .../spatial/pairwise_interactions_builder.hpp | 23 --- .../spatial/pairwise_interactions_config.hpp | 38 ----- src/geode/stochastic/CMakeLists.txt | 7 +- .../spatial/pairwise_interactions.cpp | 86 ----------- .../pairwise_interactions/distance_cutoff.cpp | 144 ++++++++++++++++++ .../models/energy_terms/test-gibbs-energy.cpp | 2 +- .../energy_terms/test-pairwise-term.cpp | 8 +- .../sampling/mcmc/test-mh-strauss.cpp | 2 +- tests/stochastic/spatial/test-object-sets.cpp | 4 +- .../spatial/test-pairwise-interactions.cpp | 14 +- 18 files changed, 385 insertions(+), 200 deletions(-) create mode 100644 include/geode/stochastic/spatial/pairwise_interactions/distance_cutoff.hpp rename include/geode/stochastic/spatial/{ => pairwise_interactions}/pairwise_interactions.hpp (77%) create mode 100644 include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp create mode 100644 include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_config.hpp delete mode 100644 include/geode/stochastic/spatial/pairwise_interactions_builder.hpp delete mode 100644 include/geode/stochastic/spatial/pairwise_interactions_config.hpp delete mode 100644 src/geode/stochastic/spatial/pairwise_interactions.cpp create mode 100644 src/geode/stochastic/spatial/pairwise_interactions/distance_cutoff.cpp 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 e71c2bb..b0de765 100644 --- a/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp @@ -35,7 +35,7 @@ #include #include -#include +#include #include namespace geode @@ -63,8 +63,7 @@ namespace geode object_sets.get_existing_set_uuids( cfg.object_set_names ); auto interaction = - std::make_unique< geode::EuclideanCutoffInteraction< ObjectType > >( - 0.5 + std::make_unique< geode::MinimalDistanceCutoff< ObjectType > >( 0.5 /*,interaction_desc.interaction_scope*/ ); return std::make_unique< geode::PairwiseTerm< geode::Point2D > >( 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 b617856..0194b77 100644 --- a/include/geode/stochastic/models/energy_terms/energy_term_config.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term_config.hpp @@ -26,6 +26,8 @@ #include #include +#include + namespace geode { struct DensityTermConfig diff --git a/include/geode/stochastic/models/energy_terms/pairwise_term.hpp b/include/geode/stochastic/models/energy_terms/pairwise_term.hpp index 55a2eb4..ce5e897 100644 --- a/include/geode/stochastic/models/energy_terms/pairwise_term.hpp +++ b/include/geode/stochastic/models/energy_terms/pairwise_term.hpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include 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 f34d136..1ed63f1 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include @@ -225,10 +225,10 @@ namespace geode return; } const auto set_id = set_name_to_uuid_.at( set_desc.name ); - auto interaction = std::make_unique< - EuclideanCutoffInteraction< OwnerSegment2D > >( - set_desc.minimal_spacing, - PairwiseInteraction< OwnerSegment2D >::SCOPE::same_set ); + 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( @@ -248,10 +248,10 @@ namespace geode { set_uuids.push_back( id ); } - auto interaction = std::make_unique< - EuclideanCutoffInteraction< OwnerSegment2D > >( - 0., PairwiseInteraction< - OwnerSegment2D >::SCOPE::different_set ); + 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( diff --git a/include/geode/stochastic/spatial/pairwise_interactions/distance_cutoff.hpp b/include/geode/stochastic/spatial/pairwise_interactions/distance_cutoff.hpp new file mode 100644 index 0000000..684703f --- /dev/null +++ b/include/geode/stochastic/spatial/pairwise_interactions/distance_cutoff.hpp @@ -0,0 +1,83 @@ +/* + * 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 +#include + +#include +#include +#include + +namespace geode +{ + /*! + * MinimalDistanceCutoff + * A pairwise interaction that returns 1 if the Euclidean distance + * between objects is within a cutoff radius, otherwise 0. + */ + template < typename Type > + class CenterEuclideanDistanceCutoff : public PairwiseInteraction< Type > + { + public: + explicit CenterEuclideanDistanceCutoff( double cutoff_distance ); + CenterEuclideanDistanceCutoff( double cutoff_distance, + typename PairwiseInteraction< Type >::SCOPE scope ); + + double neighborhood_searching_distance() const override; + + protected: + double compute( const ObjectRef< Type >& object_a, + const ObjectRef< Type >& object_b ) const override; + + private: + double cutoff_distance_{ GLOBAL_EPSILON }; + }; + + /*! + * MinimalDistanceCutoff + * A pairwise interaction that returns 1 if the Minimal distance + * between objects is within a cutoff radius, otherwise 0. + */ + template < typename Type > + class MinimalDistanceCutoff : public PairwiseInteraction< Type > + { + public: + explicit MinimalDistanceCutoff( double cutoff_distance ); + MinimalDistanceCutoff( double cutoff_distance, + typename PairwiseInteraction< Type >::SCOPE scope ); + + double neighborhood_searching_distance() const override; + + protected: + double compute( const ObjectRef< Type >& object_a, + const ObjectRef< Type >& object_b ) const override; + + private: + double cutoff_distance_{ GLOBAL_EPSILON }; + }; +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/spatial/pairwise_interactions.hpp b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions.hpp similarity index 77% rename from include/geode/stochastic/spatial/pairwise_interactions.hpp rename to include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions.hpp index 8034667..c10113b 100644 --- a/include/geode/stochastic/spatial/pairwise_interactions.hpp +++ b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions.hpp @@ -75,26 +75,4 @@ namespace geode SCOPE scope_{ SCOPE::all_set }; }; - /*! - * EuclideanCutoffInteraction - * A pairwise interaction that returns 1 if the Euclidean distance - * between objects is within a cutoff radius, otherwise 0. - */ - template < typename Type > - class EuclideanCutoffInteraction : public PairwiseInteraction< Type > - { - public: - explicit EuclideanCutoffInteraction( double cutoff_distance ); - EuclideanCutoffInteraction( double cutoff_distance, - typename PairwiseInteraction< Type >::SCOPE scope ); - - double neighborhood_searching_distance() const override; - - protected: - double compute( const ObjectRef< Type >& object_a, - const ObjectRef< Type >& object_b ) const override; - - private: - double cutoff_distance_{ GLOBAL_EPSILON }; - }; } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp new file mode 100644 index 0000000..6bb9a49 --- /dev/null +++ b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.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. + * + */ + +#include + +#pragma once +namespace geode +{ + 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, + 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 ); + } + }, + cfg ); + } +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_config.hpp b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_config.hpp new file mode 100644 index 0000000..905844b --- /dev/null +++ b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_config.hpp @@ -0,0 +1,71 @@ +/* + * 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 + +#pragma once +namespace geode +{ + struct EuclideanDistanceCutoffConfig + { + double threshold; + }; + + struct MinimalDistanceCutoffConfig + { + double threshold; + }; + + using PairwiseInteractionConfig = + std::variant< 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/pairwise_interactions_builder.hpp b/include/geode/stochastic/spatial/pairwise_interactions_builder.hpp deleted file mode 100644 index e7a780b..0000000 --- a/include/geode/stochastic/spatial/pairwise_interactions_builder.hpp +++ /dev/null @@ -1,23 +0,0 @@ -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, 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 ); - } - }, - cfg ); -} \ No newline at end of file diff --git a/include/geode/stochastic/spatial/pairwise_interactions_config.hpp b/include/geode/stochastic/spatial/pairwise_interactions_config.hpp deleted file mode 100644 index 3b453bf..0000000 --- a/include/geode/stochastic/spatial/pairwise_interactions_config.hpp +++ /dev/null @@ -1,38 +0,0 @@ -struct EuclideanCenterDistanceConfig -{ - double threshold; - double weight; -}; - -struct HausdorffDistanceConfig -{ - double threshold; - double weight; -}; - -using PairwiseInteractionConfig = - std::variant< EuclideanCenterDistanceConfig, HausdorffDistanceConfig >; - -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, 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 ); - } - }, - cfg ); -} \ No newline at end of file diff --git a/src/geode/stochastic/CMakeLists.txt b/src/geode/stochastic/CMakeLists.txt index 158d4b5..cfe03f1 100644 --- a/src/geode/stochastic/CMakeLists.txt +++ b/src/geode/stochastic/CMakeLists.txt @@ -25,7 +25,7 @@ add_geode_library( "spatial/object_set.cpp" "spatial/object_sets.cpp" "spatial/object_neighborhood.cpp" - "spatial/pairwise_interactions.cpp" + "spatial/pairwise_interactions/distance_cutoff.cpp" "sampling/direct/ball_sampler.cpp" "sampling/direct/bounding_box_sampler.cpp" "sampling/direct/double_sampler.cpp" @@ -41,7 +41,10 @@ add_geode_library( "spatial/object_sets.hpp" "spatial/object_neighborhood.hpp" "spatial/object_helpers.hpp" - "spatial/pairwise_interactions.hpp" + "spatial/pairwise_interactions/pairwise_interactions_builder.hpp" + "spatial/pairwise_interactions/pairwise_interactions_config.hpp" + "spatial/pairwise_interactions/pairwise_interactions.hpp" + "spatial/pairwise_interactions/distance_cutoff.hpp" "spatial/spatial_domain.hpp" "spatial/details/RTree.hpp" "sampling/direct/object_set_sampler/object_set_sampler.hpp" diff --git a/src/geode/stochastic/spatial/pairwise_interactions.cpp b/src/geode/stochastic/spatial/pairwise_interactions.cpp deleted file mode 100644 index 1b76b20..0000000 --- a/src/geode/stochastic/spatial/pairwise_interactions.cpp +++ /dev/null @@ -1,86 +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 -{ - template < geode::index_t dimension > - double compute_euclidean_distance( const geode::Point< dimension >& point1, - const geode::Point< dimension >& point2 ) - { - return geode::point_point_distance( point1, point2 ); - } - template < geode::index_t dimension > - double compute_euclidean_distance( const geode::Segment< dimension >& seg1, - const geode::Segment< dimension >& seg2 ) - { - return std::get< 0 >( geode::segment_segment_distance( seg1, seg2 ) ); - } -} // namespace -namespace geode -{ - template < typename Type > - EuclideanCutoffInteraction< Type >::EuclideanCutoffInteraction( - double cutoff_distance ) - : PairwiseInteraction< Type >(), cutoff_distance_( cutoff_distance ) - { - } - - template < typename Type > - EuclideanCutoffInteraction< Type >::EuclideanCutoffInteraction( - double cutoff_distance, - typename PairwiseInteraction< Type >::SCOPE scope ) - : PairwiseInteraction< Type >( scope ), - cutoff_distance_( cutoff_distance ) - { - } - - template < typename Type > - double EuclideanCutoffInteraction< Type >::neighborhood_searching_distance() - const - { - return cutoff_distance_; - } - - template < typename Type > - double EuclideanCutoffInteraction< Type >::compute( - const ObjectRef< Type >& object_a, - const ObjectRef< Type >& object_b ) const - { - auto dist = compute_euclidean_distance< Type::dim >( - object_a.object, object_b.object ); - return dist <= cutoff_distance_ ? 1.0 : 0.0; - } - - template class opengeode_stochastic_stochastic_api - EuclideanCutoffInteraction< Point< 2 > >; - template class opengeode_stochastic_stochastic_api - EuclideanCutoffInteraction< Point< 3 > >; - - template class opengeode_stochastic_stochastic_api - EuclideanCutoffInteraction< OwnerSegment< 2 > >; -} // namespace geode \ No newline at end of file diff --git a/src/geode/stochastic/spatial/pairwise_interactions/distance_cutoff.cpp b/src/geode/stochastic/spatial/pairwise_interactions/distance_cutoff.cpp new file mode 100644 index 0000000..913bd25 --- /dev/null +++ b/src/geode/stochastic/spatial/pairwise_interactions/distance_cutoff.cpp @@ -0,0 +1,144 @@ +/* + * 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 +#include + +namespace +{ + template < geode::index_t dimension > + double compute_center_euclidean_distance( + const geode::Point< dimension >& point1, + const geode::Point< dimension >& point2 ) + { + return geode::point_point_distance( point1, point2 ); + } + template < geode::index_t dimension > + double compute_center_euclidean_distance( + const geode::Segment< dimension >& seg1, + const geode::Segment< dimension >& seg2 ) + { + return geode::point_point_distance( + seg1.barycenter(), seg2.barycenter() ); + } + template < geode::index_t dimension > + double compute_min_distance( const geode::Point< dimension >& point1, + const geode::Point< dimension >& point2 ) + { + return geode::point_point_distance( point1, point2 ); + } + template < geode::index_t dimension > + double compute_min_distance( const geode::Segment< dimension >& seg1, + const geode::Segment< dimension >& seg2 ) + { + return std::get< 0 >( geode::segment_segment_distance( seg1, seg2 ) ); + } +} // namespace +namespace geode +{ + template < typename Type > + CenterEuclideanDistanceCutoff< Type >::CenterEuclideanDistanceCutoff( + double cutoff_distance ) + : PairwiseInteraction< Type >(), cutoff_distance_( cutoff_distance ) + { + } + + template < typename Type > + CenterEuclideanDistanceCutoff< Type >::CenterEuclideanDistanceCutoff( + double cutoff_distance, + typename PairwiseInteraction< Type >::SCOPE scope ) + : PairwiseInteraction< Type >( scope ), + cutoff_distance_( cutoff_distance ) + { + } + + template < typename Type > + double + CenterEuclideanDistanceCutoff< Type >::neighborhood_searching_distance() + const + { + return cutoff_distance_; + } + + template < typename Type > + double CenterEuclideanDistanceCutoff< Type >::compute( + const ObjectRef< Type >& object_a, + const ObjectRef< Type >& object_b ) const + { + auto dist = compute_center_euclidean_distance< Type::dim >( + object_a.object, object_b.object ); + return dist <= cutoff_distance_ ? 1.0 : 0.0; + } + + template class opengeode_stochastic_stochastic_api + CenterEuclideanDistanceCutoff< Point< 2 > >; + template class opengeode_stochastic_stochastic_api + CenterEuclideanDistanceCutoff< Point< 3 > >; + + template class opengeode_stochastic_stochastic_api + CenterEuclideanDistanceCutoff< OwnerSegment< 2 > >; + + template < typename Type > + MinimalDistanceCutoff< Type >::MinimalDistanceCutoff( + double cutoff_distance ) + : PairwiseInteraction< Type >(), cutoff_distance_( cutoff_distance ) + { + } + + template < typename Type > + MinimalDistanceCutoff< Type >::MinimalDistanceCutoff( + double cutoff_distance, + typename PairwiseInteraction< Type >::SCOPE scope ) + : PairwiseInteraction< Type >( scope ), + cutoff_distance_( cutoff_distance ) + { + } + + template < typename Type > + double + MinimalDistanceCutoff< Type >::neighborhood_searching_distance() const + { + return cutoff_distance_; + } + + template < typename Type > + double MinimalDistanceCutoff< Type >::compute( + const ObjectRef< Type >& object_a, + const ObjectRef< Type >& object_b ) const + { + auto dist = compute_min_distance< Type::dim >( + object_a.object, object_b.object ); + return dist <= cutoff_distance_ ? 1.0 : 0.0; + } + + template class opengeode_stochastic_stochastic_api + MinimalDistanceCutoff< Point< 2 > >; + template class opengeode_stochastic_stochastic_api + MinimalDistanceCutoff< Point< 3 > >; + + template class opengeode_stochastic_stochastic_api + MinimalDistanceCutoff< OwnerSegment< 2 > >; +} // namespace geode \ No newline at end of file diff --git a/tests/stochastic/models/energy_terms/test-gibbs-energy.cpp b/tests/stochastic/models/energy_terms/test-gibbs-energy.cpp index 3ccf07e..3b3c223 100644 --- a/tests/stochastic/models/energy_terms/test-gibbs-energy.cpp +++ b/tests/stochastic/models/energy_terms/test-gibbs-energy.cpp @@ -54,7 +54,7 @@ void test_gibbs_energy() geode_unused( term_id ); // Add pairwise term with trivial interaction: always counts 1 for each pair auto interaction = - std::make_unique< geode::EuclideanCutoffInteraction< geode::Point2D > >( + std::make_unique< geode::MinimalDistanceCutoff< geode::Point2D > >( 1000000 ); const auto pwterm_id = energy_terms.add_energy_term( diff --git a/tests/stochastic/models/energy_terms/test-pairwise-term.cpp b/tests/stochastic/models/energy_terms/test-pairwise-term.cpp index 2684021..b05c163 100644 --- a/tests/stochastic/models/energy_terms/test-pairwise-term.cpp +++ b/tests/stochastic/models/energy_terms/test-pairwise-term.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include @@ -57,8 +57,7 @@ void test_pairwise_term( double gamma, const geode::SpatialDomain< 2 >& domain ) { auto interaction = - std::make_unique< geode::EuclideanCutoffInteraction< geode::Point2D > >( - 1 ); + std::make_unique< geode::MinimalDistanceCutoff< geode::Point2D > >( 1 ); geode::PairwiseTerm< geode::Point2D > term( "strauss", gamma, { set_id }, std::move( interaction ), domain ); @@ -130,8 +129,7 @@ void test_pairwise_term_zero_gamma( double gamma, const geode::SpatialDomain< 2 >& domain ) { auto interaction = - std::make_unique< geode::EuclideanCutoffInteraction< geode::Point2D > >( - 1 ); + std::make_unique< geode::MinimalDistanceCutoff< geode::Point2D > >( 1 ); geode::PairwiseTerm< geode::Point2D > term( "strauss", gamma, { set_id }, std::move( interaction ), domain ); diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index 4545bd2..aad75ff 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -133,7 +133,7 @@ namespace } auto interaction = std::make_unique< - geode::EuclideanCutoffInteraction< geode::Point2D > >( + geode::MinimalDistanceCutoff< geode::Point2D > >( interaction_desc.distance_threshold /*,interaction_desc.interaction_scope*/ ); diff --git a/tests/stochastic/spatial/test-object-sets.cpp b/tests/stochastic/spatial/test-object-sets.cpp index c7b8685..a2416c2 100644 --- a/tests/stochastic/spatial/test-object-sets.cpp +++ b/tests/stochastic/spatial/test-object-sets.cpp @@ -29,8 +29,8 @@ namespace void test_add_sets_and_objects() { ObjectSets< geode::Point2D > sets; - const auto set_id1 = sets.add_set( "default_name" ); - const auto set_id2 = sets.add_set( "default_name" ); + const auto set_id1 = sets.add_set( "default_name1" ); + const auto set_id2 = sets.add_set( "default_name2" ); OPENGEODE_EXCEPTION( sets.nb_sets() == 2, "[TestObjectSets] - Expected 2 sets" ); diff --git a/tests/stochastic/spatial/test-pairwise-interactions.cpp b/tests/stochastic/spatial/test-pairwise-interactions.cpp index d605a40..648ef5a 100644 --- a/tests/stochastic/spatial/test-pairwise-interactions.cpp +++ b/tests/stochastic/spatial/test-pairwise-interactions.cpp @@ -24,12 +24,12 @@ #include #include -#include +#include namespace { void test_interaction() { - geode::EuclideanCutoffInteraction< geode::Point2D > interaction( 2.0 ); + geode::MinimalDistanceCutoff< geode::Point2D > interaction( 2.0 ); geode::Point2D p1{ { 0., 0. } }; geode::Point2D p2{ { 1., 1. } }; // distance = 1.44 < 2.0 @@ -39,11 +39,11 @@ namespace interaction.evaluate( geode::ObjectRef< geode::Point2D >{ p1, id }, geode::ObjectRef< geode::Point2D >{ p2, id } ); OPENGEODE_EXCEPTION( result == 1.0, - "[EuclideanCutoffInteraction] Failed for inside cutoff case." ); + "[MinimalDistanceCutoff] Failed for inside cutoff case." ); } void test_no_interaction() { - geode::EuclideanCutoffInteraction< geode::Point2D > interaction( 2.0 ); + geode::MinimalDistanceCutoff< geode::Point2D > interaction( 2.0 ); geode::Point2D p1{ { 0., 0. } }; geode::Point2D p2{ { 3., 0. } }; // distance = 3.0 > 2.0 @@ -53,11 +53,11 @@ namespace interaction.evaluate( geode::ObjectRef< geode::Point2D >{ p1, id }, geode::ObjectRef< geode::Point2D >{ p2, id } ); OPENGEODE_EXCEPTION( result == 0.0, - "[EuclideanCutoffInteraction] Failed for outside cutoff case." ); + "[MinimalDistanceCutoff] Failed for outside cutoff case." ); } void test_limit_interaction() { - geode::EuclideanCutoffInteraction< geode::Point2D > interaction( 2.0 ); + geode::MinimalDistanceCutoff< geode::Point2D > interaction( 2.0 ); geode::Point2D p1{ { 0., 0. } }; geode::Point2D p2{ { 2., 0. } }; // distance = 2.0 == cutoff @@ -67,7 +67,7 @@ namespace interaction.evaluate( geode::ObjectRef< geode::Point2D >{ p1, id }, geode::ObjectRef< geode::Point2D >{ p2, id } ); OPENGEODE_EXCEPTION( result == 1.0, - "[EuclideanCutoffInteraction] Failed for exact cutoff case." ); + "[MinimalDistanceCutoff] Failed for exact cutoff case." ); } } // namespace From 3d7e55c0f96099d1312d997eb82a2718b6a42d9a Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Thu, 9 Apr 2026 14:23:30 +0200 Subject: [PATCH 04/59] 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 05/59] 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 14e205554ca3915e2b3ac9ef6bc4ecaf44ee738e Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 28 Apr 2026 11:40:05 +0200 Subject: [PATCH 06/59] 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/59] 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 a1b1ea582f7e3a64f60372ea72392d3772d2a9ed Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Mon, 25 May 2026 09:22:36 +0200 Subject: [PATCH 08/59] 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 09/59] 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 10/59] 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 11/59] 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: From 43b70fb8f6359ebbc8c1f98e33f81b962a4b9264 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Mon, 25 May 2026 17:17:25 +0200 Subject: [PATCH 12/59] PA comments --- .../inference/statistic_objective.hpp | 4 +- .../inference/statistic_validator.hpp | 25 ++-- .../inference/statistics_tracker.hpp | 12 +- .../models/energy_term_collection.hpp | 6 +- .../models/energy_terms/energy_term.hpp | 2 +- .../energy_terms/energy_term_builder.hpp | 41 +----- .../object_set_sampler/point_set_sampler.hpp | 7 +- .../segment_set_sampler.hpp | 2 +- .../mcmc/helpers/simulation_monitor_old.hpp | 2 +- .../sampling/mcmc/helpers/state_dynamics.hpp | 4 +- .../mcmc/metropolis_hasting_sampler.hpp | 16 +-- .../mcmc/proposal/classical_proposals.hpp | 7 +- .../sampling/mcmc/proposal/moves.hpp | 7 +- .../mcmc/proposal/proposal_kernel.hpp | 11 +- .../single_object_feature_builder.hpp | 8 +- .../stochastic/spatial/spatial_domain.hpp | 8 +- src/geode/stochastic/CMakeLists.txt | 1 + .../energy_terms/energy_term_builder.cpp | 90 +++++++++++++ .../sampling/direct/double_sampler.cpp | 17 +-- .../stochastic/sampling/random_engine.cpp | 119 +++++++++--------- src/geode/stochastic/spatial/object_set.cpp | 6 +- src/geode/stochastic/spatial/object_sets.cpp | 18 +-- .../segment_length_feature.cpp | 2 +- .../mcmc/proposal/test-proposal-kernel.cpp | 2 +- .../mcmc/test-metropolis-hasting-sampler.cpp | 2 +- .../sampling/mcmc/test-mh-poisson.cpp | 6 +- .../sampling/mcmc/test-mh-strauss.cpp | 2 +- .../sampling/test-random-engine.cpp | 4 +- 28 files changed, 250 insertions(+), 181 deletions(-) create mode 100644 src/geode/stochastic/models/energy_terms/energy_term_builder.cpp diff --git a/include/geode/stochastic/inference/statistic_objective.hpp b/include/geode/stochastic/inference/statistic_objective.hpp index 0859115..e49fc64 100644 --- a/include/geode/stochastic/inference/statistic_objective.hpp +++ b/include/geode/stochastic/inference/statistic_objective.hpp @@ -40,8 +40,8 @@ namespace geode for( const auto& target : targets ) { - const double mean = monitor.mean( target.term_id ); - const double diff = mean - target.value; + const auto mean = monitor.mean( target.term_id ); + const auto diff = mean - target.value; loss += diff * diff; } diff --git a/include/geode/stochastic/inference/statistic_validator.hpp b/include/geode/stochastic/inference/statistic_validator.hpp index 60ebbe5..19c3721 100644 --- a/include/geode/stochastic/inference/statistic_validator.hpp +++ b/include/geode/stochastic/inference/statistic_validator.hpp @@ -38,18 +38,19 @@ namespace geode { 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 ); - // OpenGeodeStochasticStochasticException::check_exception( - // rel_error < t.tolerance, nullptr, - // OpenGeodeException::TYPE::data, - // "[StatisticsValidator] Failure for term ", - // t.term_id.string(), "\n mean = ", - // mean, - // "\n target = ", target.value, "\n error - // = ", rel_error, - // "\n tol = ", target.tolerance ); + const auto mean = monitor.mean( target.term_id ); + const auto rel_error = std::fabs( mean - target.value ) + / ( std::fabs( target.value ) + 1e-12 ); + OpenGeodeStochasticStochasticException::check_exception( + rel_error < target.tolerance, nullptr, + OpenGeodeException::TYPE::result, + "[StatisticsValidator] Failure for term ", + t.term_id.string(), "\n mean = ", mean, + "\n target = ", target.value, + "\n error + = ", rel_error, + "\n tol = ", + target.tolerance ); } } }; diff --git a/include/geode/stochastic/inference/statistics_tracker.hpp b/include/geode/stochastic/inference/statistics_tracker.hpp index 8e89eab..81816f3 100644 --- a/include/geode/stochastic/inference/statistics_tracker.hpp +++ b/include/geode/stochastic/inference/statistics_tracker.hpp @@ -31,12 +31,12 @@ namespace geode { auto& value = values[value_id]; auto& mean = means_[value_id]; - auto& m2 = m2_[value_id]; // somme des carrés + auto& sum_of_squares = m2_[value_id]; - double delta = value - mean; + const auto delta = value - mean; mean += delta / count_; - double delta2 = value - mean; - m2 += delta * delta2; + const auto delta2 = value - mean; + sum_of_squares += delta * delta2; } } @@ -79,8 +79,8 @@ namespace geode private: const Model< ObjectType >& model_; - std::vector< double > means_{}; - std::vector< double > m2_{}; + 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/models/energy_term_collection.hpp b/include/geode/stochastic/models/energy_term_collection.hpp index 20d77ed..d333259 100644 --- a/include/geode/stochastic/models/energy_term_collection.hpp +++ b/include/geode/stochastic/models/energy_term_collection.hpp @@ -49,21 +49,21 @@ namespace geode const auto term_name = term->name(); OpenGeodeStochasticStochasticException::check_exception( term_name.has_value(), nullptr, OpenGeodeException::TYPE::data, - absl::StrCat( "[EnergyTermCollection]- Energy Term name is not " + 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 ); OpenGeodeStochasticStochasticException::check_exception( inserted_uuid, nullptr, OpenGeodeException::TYPE::data, - absl::StrCat( "[EnergyTermCollection]- Energy Term named ", + absl::StrCat( "[EnergyTermCollection] Energy Term named ", term_name.value(), " already exists." ) ); auto [it2, inserted_index] = uuid_to_index_.emplace( term_uuid, term_idx ); OpenGeodeStochasticStochasticException::check_exception( inserted_index, nullptr, OpenGeodeException::TYPE::data, - absl::StrCat( "[EnergyTermCollection]- Energy Term ", + absl::StrCat( "[EnergyTermCollection] Energy Term ", term_uuid.string(), " already exists." ) ); energy_terms_.emplace_back( std::move( term ) ); diff --git a/include/geode/stochastic/models/energy_terms/energy_term.hpp b/include/geode/stochastic/models/energy_terms/energy_term.hpp index d62f157..4db0ecc 100644 --- a/include/geode/stochastic/models/energy_terms/energy_term.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term.hpp @@ -42,7 +42,7 @@ namespace geode::detail { OpenGeodeStochasticStochasticException::check_exception( param >= 0., nullptr, OpenGeodeException::TYPE::data, - "[Gibbs energy term] - The model parameter cannot be " + "[EnergyTerm] Model parameter cannot be " "negative." ); if( param >= geode::GLOBAL_EPSILON ) 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 8f2d599..80ba0c9 100644 --- a/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp @@ -66,39 +66,8 @@ namespace geode 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 ); - } - - absl::c_sort( interacting_set_ids ); - 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( _ ); - absl::c_sort( adjacent_set_uuids ); - 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 ); - } + const std::vector< std::pair< uuid, uuid > >& interacting_sets ); + template < typename ObjectType > std::unique_ptr< EnergyTerm< ObjectType > > build_pairwise_term( const PairwiseTermConfig& cfg, @@ -107,11 +76,9 @@ namespace geode { 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 = build_pairwise_interaction< ObjectType >( cfg.interaction_config ); @@ -148,6 +115,7 @@ namespace geode "Unsupported EnergyTermConfig type" ); return nullptr; } + template < typename ObjectType > std::unique_ptr< EnergyTerm< ObjectType > > build_energy_term_impl( const std::monostate&, @@ -156,8 +124,9 @@ namespace geode { throw OpenGeodeStochasticStochasticException{ nullptr, OpenGeodeException::TYPE::data, - "[EnergyTermBuilder] energy term config not initialized" }; + "[EnergyTermBuilder] Energy term config not initialized" }; } + template < typename ObjectType > std::unique_ptr< EnergyTerm< ObjectType > > build_energy_term( const EnergyTermConfig& cfg, 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 3f2145f..5aa9d81 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 @@ -42,13 +42,14 @@ namespace geode auto volume = domain_.extended_n_volume(); OpenGeodeStochasticStochasticException::check_exception( volume != 0., nullptr, OpenGeodeException::TYPE::data, - "[PointSetSampler] - Undefined Extended Bounding " + "[UniformPointSetSampler] Undefined Extended Bounding " "Box (volume ==0)." ); this->log_pdf_ = -std::log( volume ); step_move_ = define_step_for_move(); OpenGeodeStochasticStochasticException::check_exception( step_move_ > 0., nullptr, OpenGeodeException::TYPE::data, - "[PointSetSampler] - Undefined step length for move (value == ", + "[UniformPointSetSampler] Undefined step length for move " + "(value == ", step_move_, ")." ); } @@ -77,7 +78,7 @@ namespace geode } throw OpenGeodeStochasticStochasticException{ nullptr, OpenGeodeException::TYPE::internal, - "[PointSampler] - Cannot find a point in the " + "[UniformPointSetSampler] Cannot find a point in the " "extended domain" }; } 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 0e3e07d..755a3bb 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 @@ -47,7 +47,7 @@ namespace geode auto volume = domain_.extended_n_volume(); OpenGeodeStochasticStochasticException::check_exception( volume != 0., nullptr, OpenGeodeException::TYPE::data, - "[SegmentSetSampler] - Undefined Extended Bounding " + "[UniformSegmentSetSampler] Undefined Extended Bounding " "Box (volume ==0)." ); this->log_pdf_ = -std::log( volume ); } diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor_old.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor_old.hpp index 1c6c564..3ceaf32 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor_old.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor_old.hpp @@ -48,7 +48,7 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( values.size() == means_.size(), nullptr, OpenGeodeException::TYPE::data, - "[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/state_dynamics.hpp b/include/geode/stochastic/sampling/mcmc/helpers/state_dynamics.hpp index f4cdeef..58d8765 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/state_dynamics.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/state_dynamics.hpp @@ -72,8 +72,8 @@ namespace geode { OpenGeodeStochasticStochasticException::check_exception( sampler_id < samplers_.size(), nullptr, - OpenGeodeException::TYPE::data, - "[STATE DYNAMICS]: Sampler out of range." ); + OpenGeodeException::TYPE::internal, + "[StateDynamics]: Sampler out of range." ); return samplers_[sampler_id].get(); } diff --git a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp index 689e138..bf7d9d2 100644 --- a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp +++ b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp @@ -55,7 +55,8 @@ namespace geode { OpenGeodeStochasticStochasticException::check_exception( proposal_kernel_ != nullptr, nullptr, - OpenGeodeException::TYPE::data, "[MH] null proposal kernel" ); + OpenGeodeException::TYPE::data, + "[MetropolisHastings] Proposal kernel is not defined." ); } StepResult< ObjectType > step( @@ -107,28 +108,27 @@ namespace geode { OpenGeodeStochasticStochasticException::check_exception( b >= 0.0, nullptr, OpenGeodeException::TYPE::data, - "[MH] beta must be >= 0" ); + "[MetropolisHastings] The teperature (beta) must be >= 0" ); if( b == 0 ) { - Logger::info( - "[Metropolis Hastings] - beta == 0 all move will be " - "accepted - Uniform sampling." ); + Logger::info( "[MetropolisHastings] beta == 0 all move will be " + "accepted - Uniform sampling." ); } if( b < 1 ) { Logger::info( - "[Metropolis Hastings] - beta < 1 moves that increase " + "[MetropolisHastings] beta < 1 moves that increase " "energy are more likely to be accepted - Hot system " "introduce randomness for exploration." ); } if( b == 1 ) { - Logger::info( "[Metropolis Hastings] - beta == 1 default " + Logger::info( "[MetropolisHastings] beta == 1 default " "choice no temperature - only consider energy." ); } if( b > 1 ) { - Logger::info( "[Metropolis Hastings] - beta > 1 moves that " + Logger::info( "[MetropolisHastings] beta > 1 moves that " "increase energy are less likely to be accepted " "- Cold system to ensure convergence but may " "find local minimum randomness." ); diff --git a/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp b/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp index c46b0b0..73469e4 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp @@ -42,7 +42,8 @@ namespace geode const auto total_ratio = birth_ratio + death_ratio; OpenGeodeStochasticStochasticException::check_exception( total_ratio > 0., nullptr, OpenGeodeException::TYPE::data, - "BIRTH+DEATH ratio must be positive" ); + "[add_birth_death_change_moves] Birth + Death ratio must be " + "positive." ); const auto p_birth = birth_ratio / total_ratio; kernel.add_move( @@ -80,7 +81,9 @@ namespace geode auto birth_death_prob = birth_prob + death_prob; OpenGeodeStochasticStochasticException::check_exception( birth_death_prob < 1., nullptr, OpenGeodeException::TYPE::data, - "[Proposal Kernel] - changes should be allowed." ); + "[create_birth_death_change_kernel] The probability of birth + " + "death = ", + birth_death_prob, " but should be < 1 to enable change moves." ); auto kernel = std::make_unique< ProposalKernel< ObjectType > >(); kernel->add_move( set_id, std::make_unique< BirthDeathMove< ObjectType > >( sampler, diff --git a/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp b/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp index 82cde0e..718e152 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/moves.hpp @@ -54,7 +54,7 @@ namespace geode std::isfinite( log_forward_prob ) && std::isfinite( log_backward_prob ), nullptr, OpenGeodeException::TYPE::data, - "[Proposal Probabilities] Non-finite proposal " + "[ProposalProbabilities] Non-finite proposal " "log-probabilities" ); return log_backward_prob - log_forward_prob; } @@ -119,8 +119,7 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( proportion_weight_ > 0., nullptr, OpenGeodeException::TYPE::data, - "[Move] - the weight factor for a move should be in higher " - "than 0. (here = ", + "[Move] The weight factor for a move should be > 0. (here = ", proportion_weight_, ")" ); initialize_probability( 1. ); } @@ -190,7 +189,7 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( birth_ratio_ > 0. && birth_ratio_ < 1., nullptr, OpenGeodeException::TYPE::data, - "[BirthDeathMove]-the ratio of birth over mover should be in " + "[BirthDeathMove] The ratio of birth moves should be in " "]0,1[. (here = ", birth_ratio_, ")" ); log_p_birth_ = std::log( probability * 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 287767a..61d0be1 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/proposal_kernel.hpp @@ -41,7 +41,7 @@ namespace geode { OpenGeodeStochasticStochasticException::check_exception( proposed_move.new_object.has_value(), nullptr, - OpenGeodeException::TYPE::data, + OpenGeodeException::TYPE::internal, "[Proposal] Proposal has no new_object" ); return ObjectRef< ObjectType >{ proposed_move.new_object.value(), set_id }; @@ -51,7 +51,7 @@ namespace geode { OpenGeodeStochasticStochasticException::check_exception( proposed_move.old_object_id.has_value(), nullptr, - OpenGeodeException::TYPE::data, + OpenGeodeException::TYPE::internal, "[Proposal] Proposal has no old_object_id" ); return ObjectId{ proposed_move.old_object_id.value(), false, set_id }; @@ -75,7 +75,7 @@ namespace geode { OpenGeodeStochasticStochasticException::check_exception( !set_moves_.empty(), nullptr, OpenGeodeException::TYPE::data, - "[MCMC Proposal Kernel] - no move are defined in the Kernel." ); + "[ProposalKernel] No move are defined in the Kernel." ); auto rnd = engine.sample_uniform( uniform_distribution_closed_ ); for( const auto proba_id : Range{ cumulative_probs_.size() } ) { @@ -89,7 +89,7 @@ namespace geode } throw OpenGeodeStochasticStochasticException{ nullptr, OpenGeodeException::TYPE::internal, - "[MCMC Proposal Kernel]: Should not be reached move pdf is " + "[ProposalKernel]: Should not be reached move pdf is not" "correctly set." }; } @@ -137,8 +137,7 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( total > GLOBAL_EPSILON, nullptr, OpenGeodeException::TYPE::internal, - "[MCMC Proposal Kernel] - Total " - "probability is zero in Kernel." ); + "[ProposalKernel] - The probability of every moves is zero." ); // Normalize std::transform( probabilities.begin(), probabilities.end(), 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 bebd2a1..d0d810b 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 @@ -20,13 +20,13 @@ * SOFTWARE. * */ +#pragma once #include #include #include -#pragma once namespace geode { template < typename ObjectType > @@ -49,9 +49,9 @@ namespace geode } else { - throw std::runtime_error( - "SegmentLengthInsideBoxFeature not valid for this " - "ObjectType" ); + throw OpenGeodeStochasticStochasticException{ nullptr, + OpenGeodeException::TYPE::data, + "SegmentLengthInsideBoxFeature not valid for this ObjectType" }; } } diff --git a/include/geode/stochastic/spatial/spatial_domain.hpp b/include/geode/stochastic/spatial/spatial_domain.hpp index 3d8d08d..584d2a2 100644 --- a/include/geode/stochastic/spatial/spatial_domain.hpp +++ b/include/geode/stochastic/spatial/spatial_domain.hpp @@ -42,12 +42,12 @@ namespace geode auto volume = domain_.n_volume(); OpenGeodeStochasticStochasticException::check_exception( volume > 0., nullptr, OpenGeodeException::TYPE::data, - "[SpatialDomain] - Undefined Spatial Domain (volume == ", - volume, ")." ); + "[SpatialDomain] Undefined Spatial Domain (volume == ", volume, + ")." ); OpenGeodeStochasticStochasticException::check_exception( buffer_size_ >= 0.0, nullptr, OpenGeodeException::TYPE::data, - "[SpatialDomain] buffer size must be non-negative ( buffer " - "== ", + "[SpatialDomain] Buffer size must not be < 0 ( buffer " + "= ", buffer_size_, ")" ); if( buffer_size_ != 0. ) { diff --git a/src/geode/stochastic/CMakeLists.txt b/src/geode/stochastic/CMakeLists.txt index f8c365f..f71b40d 100644 --- a/src/geode/stochastic/CMakeLists.txt +++ b/src/geode/stochastic/CMakeLists.txt @@ -32,6 +32,7 @@ add_geode_library( "sampling/direct/double_sampler.cpp" "sampling/direct/point_uniform_sampler.cpp" "sampling/direct/segment_uniform_sampler.cpp" + "models/energy_terms/energy_term_builder.cpp" "sampling/distributions.cpp" "sampling/random_engine.cpp" "common.cpp" diff --git a/src/geode/stochastic/models/energy_terms/energy_term_builder.cpp b/src/geode/stochastic/models/energy_terms/energy_term_builder.cpp new file mode 100644 index 0000000..ba865cd --- /dev/null +++ b/src/geode/stochastic/models/energy_terms/energy_term_builder.cpp @@ -0,0 +1,90 @@ +/* + * 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 +{ + void register_interaction( + const std::pair< geode::uuid, geode::uuid >& interaction, + std::vector< geode::uuid >& interacting_set_ids, + absl::flat_hash_map< geode::uuid, std::vector< geode::uuid > >& + adjacency_map ) + { + const auto& [set1, set2] = interaction; + + adjacency_map[set1].push_back( set2 ); + adjacency_map[set2].push_back( set1 ); + + interacting_set_ids.push_back( set1 ); + interacting_set_ids.push_back( set2 ); + } + + void sort_unique_shrink( std::vector< geode::uuid >& values ) + { + absl::c_sort( values ); + values.erase( + std::unique( values.begin(), values.end() ), values.end() ); + values.shrink_to_fit(); + } + + void deduplicate_adjacency_map( + absl::flat_hash_map< geode::uuid, std::vector< geode::uuid > >& + adjacency_map ) + { + for( auto& [set_uuid, adjacent_set_uuids] : adjacency_map ) + { + geode_unused( set_uuid ); + sort_unique_shrink( adjacent_set_uuids ); + } + } +} // namespace + +namespace geode +{ + 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& interaction : interacting_sets ) + { + register_interaction( + interaction, interacting_set_ids, objectset_adjacency_map ); + } + + sort_unique_shrink( interacting_set_ids ); + deduplicate_adjacency_map( objectset_adjacency_map ); + + return { std::move( interacting_set_ids ), + std::move( objectset_adjacency_map ) }; + } +} // namespace geode diff --git a/src/geode/stochastic/sampling/direct/double_sampler.cpp b/src/geode/stochastic/sampling/direct/double_sampler.cpp index 96ea5fa..a23679a 100644 --- a/src/geode/stochastic/sampling/direct/double_sampler.cpp +++ b/src/geode/stochastic/sampling/direct/double_sampler.cpp @@ -53,7 +53,7 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( desc.min_value && desc.max_value, nullptr, OpenGeodeException::TYPE::data, - "[DoubleSampler] - Incomplete description for " + "[DoubleSampler] Incomplete description for " "Uniform distribution need at least min " "and max values" ); UniformClosed< double > dist; @@ -66,7 +66,7 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( desc.min_value && desc.max_value, nullptr, OpenGeodeException::TYPE::data, - "[DoubleSampler] - Incomplete description for " + "[DoubleSampler] Incomplete description for " "Uniform distribution need at least min " "and max values" ); UniformClosedOpen< double > dist; @@ -79,7 +79,7 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( desc.mean && desc.standard_deviation, nullptr, OpenGeodeException::TYPE::data, - "[DoubleSampler] - Incomplete description for " + "[DoubleSampler] Incomplete description for " "Gaussian distribution need at least mean " "and standard deviation values" ); Gaussian dist; @@ -92,7 +92,7 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( desc.mean && desc.standard_deviation, nullptr, OpenGeodeException::TYPE::data, - "[DoubleSampler] - Incomplete description for " + "[DoubleSampler] Incomplete description for " "Truncated Gaussian distribution need at least mean " "and standard deviation values" ); TruncatedGaussian dist; @@ -108,7 +108,7 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( desc.mean && desc.kappa, nullptr, OpenGeodeException::TYPE::data, - "[DoubleSampler] - Incomplete description for " + "[DoubleSampler] Incomplete description for " "Von Mises distribution need at least mean " "and concentration (kappa) values" ); VonMises dist; @@ -121,7 +121,7 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( desc.mean && desc.standard_deviation, nullptr, OpenGeodeException::TYPE::data, - "[DoubleSampler] - Incomplete description for " + "[DoubleSampler] Incomplete description for " "TruncatedLogNormal distribution need mean " "and standard deviation values of the underlying " "normal distribution." ); @@ -137,7 +137,7 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( desc.alpha.has_value(), nullptr, OpenGeodeException::TYPE::data, - "[DoubleSampler] - Incomplete description for " + "[DoubleSampler] Incomplete description for " "TruncatedPowerLaw distribution need power law " "exponent (alpha)." ); TruncatedPowerLaw dist; @@ -156,7 +156,8 @@ namespace geode { throw OpenGeodeStochasticStochasticException{ nullptr, OpenGeodeException::TYPE::data, - "Unknown distribution type: ", desc.distribution_type.get() }; + "[DoubleSampler] Unknown distribution type: ", + desc.distribution_type.get() }; } return it->second( desc ); } diff --git a/src/geode/stochastic/sampling/random_engine.cpp b/src/geode/stochastic/sampling/random_engine.cpp index 7220625..4511798 100644 --- a/src/geode/stochastic/sampling/random_engine.cpp +++ b/src/geode/stochastic/sampling/random_engine.cpp @@ -52,7 +52,7 @@ namespace 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 " + "[normal_quantile] p must be in (0,1). Check the consistencies " "between min,mean and max values." ); static const double a1 = -3.969683028665376e+01; @@ -150,8 +150,8 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( law.min_value <= law.max_value, nullptr, OpenGeodeException::TYPE::data, - "[Uniform sampling] - Wrong range ", law.min_value, - " is not <= than ", law.max_value, "." ); + "[RandomEngine] Uniform sampling with wrong range , ", + law.min_value, " is not <= than ", law.max_value, "." ); return absl::Uniform( absl::IntervalClosed, rand_gen_, law.min_value, law.max_value ); } @@ -162,8 +162,8 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( law.min_value < law.max_value, nullptr, OpenGeodeException::TYPE::data, - "[Uniform sampling] - Wrong range ", law.min_value, - " is not < than ", law.max_value, "." ); + "[RandomEngine] Uniform sampling with wrong range, ", + law.min_value, " is not < than ", law.max_value, "." ); return absl::Uniform( absl::IntervalClosedOpen, rand_gen_, law.min_value, law.max_value ); } @@ -175,7 +175,7 @@ namespace geode && std::isfinite( law.standard_deviation ) && std::isfinite( law.mean ), nullptr, OpenGeodeException::TYPE::data, - "[Gaussian sampling] - Infinite " + "[RandomEngine] Gaussian sampling with infinite " "parameters or negative standard deviation N(", law.mean, law.standard_deviation, ")." ); return absl::gaussian_distribution< double >( @@ -189,27 +189,28 @@ namespace geode && std::isfinite( law.standard_deviation ) && std::isfinite( law.mean ), nullptr, OpenGeodeException::TYPE::data, - "[Gaussian sampling] - Infinite parameters or " - "negative standard deviation N(", + "[RandomEngine] Gaussian sampling with infinite " + "parameters or negative standard deviation N(", law.mean, ",", law.standard_deviation, ")." ); - const double max = law.max_value.value_or( + const auto max = law.max_value.value_or( std::numeric_limits< double >::infinity() ); - const double min = law.min_value.value_or( + const auto min = law.min_value.value_or( -std::numeric_limits< double >::infinity() ); OpenGeodeStochasticStochasticException::check_exception( min < max, nullptr, OpenGeodeException::TYPE::data, - "[Gaussian sampling] - Wrong truncation range ", min, - " is not < than ", max, "." ); + "[RandomEngine] Gaussian sampling truncation with wrong " + "range, ", + min, " is not < than ", max, "." ); // Standardize bounds - const double alpha = ( min - law.mean ) / law.standard_deviation; - const double beta = ( max - law.mean ) / law.standard_deviation; + const auto alpha = ( min - law.mean ) / law.standard_deviation; + const auto beta = ( max - law.mean ) / law.standard_deviation; // Compute CDF of bounds, handling infinite alpha/beta - double F_min = std::isfinite( alpha ) ? normal_cdf( alpha ) : 0.0; - double F_max = std::isfinite( beta ) ? normal_cdf( beta ) : 1.0; + auto F_min = std::isfinite( alpha ) ? normal_cdf( alpha ) : 0.0; + auto F_max = std::isfinite( beta ) ? normal_cdf( beta ) : 1.0; // Clamp to avoid exact 0 or 1 (normal_quantile cannot handle them) F_min = std::max( F_min, geode::GLOBAL_EPSILON ); @@ -217,12 +218,12 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( F_min < F_max, nullptr, OpenGeodeException::TYPE::data, - "[Gaussian sampling] - truncation " + "[RandomEngine] Gaussian sampling truncation " "range is extreme please check inputs" ); // Sample uniform in [F_min, F_max] std::uniform_real_distribution< double > uniform( F_min, F_max ); - const double u = uniform( rand_gen_ ); + const auto u = uniform( rand_gen_ ); // Map through inverse CDF return law.mean + law.standard_deviation * normal_quantile( u ); @@ -233,8 +234,9 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( law.concentration >= 0.0 && std::isfinite( law.mean ), nullptr, OpenGeodeException::TYPE::data, - "[VonMises sampling] - Invalid parameters: mean=", law.mean, - ", concentration=", law.concentration, "." ); + "[RandomEngine] VonMises sampling with invalid parameters: " + "mean=", + law.mean, ", concentration=", law.concentration, "." ); // Uniform approximation for very small concentration (nearly // uniform) @@ -250,33 +252,33 @@ namespace geode if( law.concentration > LARGE_KAPPA ) { // Variance of approximate normal around mean - const double stddev = 1.0 / std::sqrt( law.concentration ); + const auto stddev = 1.0 / std::sqrt( law.concentration ); std::normal_distribution< double > normal_dist( law.mean, stddev ); - double theta = normal_dist( rand_gen_ ); + auto theta = normal_dist( rand_gen_ ); // Wrap to [0, 2π) return std::fmod( theta + 2.0 * M_PI, 2.0 * M_PI ); } // Best & Fisher (1979) rejection algorithm for moderate // concentration - const double a = + const auto a = 1.0 + std::sqrt( 1.0 + 4.0 * law.concentration * law.concentration ); - const double b = + const auto b = ( a - std::sqrt( 2.0 * a ) ) / ( 2.0 * law.concentration ); - const double r = ( 1.0 + b * b ) / ( 2.0 * b ); + const auto r = ( 1.0 + b * b ) / ( 2.0 * b ); double theta; UniformClosed< double > uniform_dist; while( true ) { - double u1 = sample_uniform( uniform_dist ); - double z = std::cos( M_PI * u1 ); - double f = ( 1.0 + r * std::abs( z ) ) / ( r + std::abs( z ) ); - double c = law.concentration * ( r - f ); - double u2 = sample_uniform( uniform_dist ); + auto u1 = sample_uniform( uniform_dist ); + auto z = std::cos( M_PI * u1 ); + auto f = ( 1.0 + r * std::fabs( z ) ) / ( r + std::fabs( z ) ); + auto c = law.concentration * ( r - f ); + auto u2 = sample_uniform( uniform_dist ); if( u2 < c * ( 2.0 - c ) || u2 <= c * std::exp( 1.0 - c ) ) { @@ -302,46 +304,47 @@ namespace geode && std::isfinite( law.standard_deviation ) && std::isfinite( law.mean ), nullptr, OpenGeodeException::TYPE::data, - "[Truncated LogNormal sampling] - Infinite parameters or " - "negative standard deviation N(", + "[RandomEngine] Truncated LogNormal sampling with infinite " + "parameters or negative standard deviation N(", law.mean, ", ", law.standard_deviation, ")." ); // Determine min and max, respecting optional values - const double min_val = law.min_value.value_or( 0.0 ); - const double max_val = law.max_value.value_or( + const auto min_val = law.min_value.value_or( 0.0 ); + const auto max_val = law.max_value.value_or( std::numeric_limits< double >::infinity() ); OpenGeodeStochasticStochasticException::check_exception( min_val < max_val, nullptr, OpenGeodeException::TYPE::data, - "[Truncated LogNormal sampling] - Wrong truncation range ", + "[RandomEngine] Truncated LogNormal sampling with wrong " + "truncation range ", min_val, " is not < than ", max_val, "." ); // Transform to standard normal space - const double zmin = + const auto zmin = ( std::log( min_val ) - law.mean ) / law.standard_deviation; - const double zmax = + const auto zmax = ( std::isfinite( max_val ) ? ( std::log( max_val ) - law.mean ) / law.standard_deviation : std::numeric_limits< double >::infinity() ); // Compute CDF bounds, handling infinite zmin/zmax - double F_min = - std::max( normal_cdf( zmin ), geode::GLOBAL_EPSILON ); - double F_max = + auto F_min = std::max( normal_cdf( zmin ), geode::GLOBAL_EPSILON ); + auto F_max = std::min( normal_cdf( zmax ), 1.0 - geode::GLOBAL_EPSILON ); OpenGeodeStochasticStochasticException::check_exception( F_min < F_max, nullptr, OpenGeodeException::TYPE::data, - "[Truncated LogNormal sampling] - truncation " - "range is extreme please chack inputs" ); + "[RandomEngine] Truncated LogNormal sampling with extreme " + "truncation " + "range, please chack inputs" ); // Sample uniform in [Fmin, Fmax] std::uniform_real_distribution< double > uniform( F_min, F_max ); - const double u = uniform( rand_gen_ ); + const auto u = uniform( rand_gen_ ); // Map through inverse CDF and exponentiate - const double z = normal_quantile( u ); + const auto z = normal_quantile( u ); return std::exp( law.mean + law.standard_deviation * z ); } @@ -349,30 +352,32 @@ namespace geode { OpenGeodeStochasticStochasticException::check_exception( law.alpha > 0, nullptr, OpenGeodeException::TYPE::data, - "Power-law exponent alpha must be > 0" ); + "[RandomEngine] Power-law sampling with wrong exponent value " + "(alpha should be > 0), gets ", + law.alpha ); // Set bounds - const double xmin = law.min_value.value_or( + const auto xmin = law.min_value.value_or( geode::GLOBAL_EPSILON ); // default 1.0 if unspecified - const double xmax = law.max_value.value_or( + const auto xmax = law.max_value.value_or( std::numeric_limits< double >::infinity() ); OpenGeodeStochasticStochasticException::check_exception( xmin < xmax, nullptr, OpenGeodeException::TYPE::data, - "Truncated power-law: min >= max" ); + "[RandomEngine] Wrong power-law trucation, ", xmin, + "si not < than", xmax ); // Sample uniform std::uniform_real_distribution< double > uniform( 0.0, 1.0 ); - const double u = uniform( rand_gen_ ); + const auto u = uniform( rand_gen_ ); // Inverse CDF - if( std::abs( law.alpha - 1.0 ) > geode::GLOBAL_EPSILON ) + if( std::fabs( law.alpha - 1.0 ) > geode::GLOBAL_EPSILON ) { - double xmin_pow = std::pow( xmin, 1.0 - law.alpha ); - double xmax_pow = - std::isfinite( xmax ) - ? std::pow( xmax, 1.0 - law.alpha ) - : std::numeric_limits< double >::infinity(); + auto xmin_pow = std::pow( xmin, 1.0 - law.alpha ); + auto xmax_pow = std::isfinite( xmax ) + ? std::pow( xmax, 1.0 - law.alpha ) + : std::numeric_limits< double >::infinity(); if( !std::isfinite( xmax_pow ) ) { return std::pow( xmin_pow + u, 1.0 / ( 1.0 - law.alpha ) ); @@ -383,7 +388,7 @@ namespace geode else { // alpha == 1 - const double xmax_eff = + const auto xmax_eff = std::isfinite( xmax ) ? xmax : xmin * geode::GLOBAL_EPSILON; return xmin * std::pow( xmax_eff / xmin, u ); } @@ -400,7 +405,7 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( probability_of_success >= 0. && probability_of_success <= 1.0, nullptr, OpenGeodeException::TYPE::data, - "Bernoulli sampling cannot be done since ", + "[RandomEngine] Bernoulli sampling cannot be done since ", probability_of_success, " is not in [0.,1.]." ); return absl::bernoulli_distribution( probability_of_success )( rand_gen_ ); diff --git a/src/geode/stochastic/spatial/object_set.cpp b/src/geode/stochastic/spatial/object_set.cpp index 21ce69a..e5afdaa 100644 --- a/src/geode/stochastic/spatial/object_set.cpp +++ b/src/geode/stochastic/spatial/object_set.cpp @@ -44,7 +44,7 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( index < fixed_objects_.size(), nullptr, OpenGeodeException::TYPE::data, - "[ObjectSet] - index for fixed object out of range." ); + "[ObjectSet] Index for fixed object out of range." ); return fixed_objects_[index]; } @@ -78,7 +78,7 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( index < free_objects_.size(), nullptr, OpenGeodeException::TYPE::data, - "[ObjectSet] - free object index out of range." ); + "[ObjectSet] Index for free object out of range." ); free_objects_[index] = std::move( object ); } @@ -88,7 +88,7 @@ namespace geode const index_t last = free_objects_.size() - 1; OpenGeodeStochasticStochasticException::check_exception( index <= last, nullptr, OpenGeodeException::TYPE::data, - "[ObjectSet] - free object index out of range." ); + "[ObjectSet] index for free object 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 5278195..2ba3b58 100644 --- a/src/geode/stochastic/spatial/object_sets.cpp +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -14,7 +14,7 @@ namespace geode auto it = uuid_to_index_.find( set_id ); OpenGeodeStochasticStochasticException::check_exception( it != uuid_to_index_.end(), nullptr, OpenGeodeException::TYPE::data, - "[ObjectSet] - group (", set_id.string(), ") is not defined." ); + "[ObjectSets] Group (", set_id.string(), ") is not defined." ); return sets_[it->second]; } @@ -102,13 +102,13 @@ namespace geode name_to_uuid_.emplace( name, set_uuid ); OpenGeodeStochasticStochasticException::check_exception( set_uuid_inserted, nullptr, OpenGeodeException::TYPE::data, - "[ObjectSet]- group named ", name, " already exists." ); + "[ObjectSet]- Group named ", name, " already exists." ); auto [it_set_uuid, set_index_inserted] = uuid_to_index_.emplace( set_uuid, set_index ); OpenGeodeStochasticStochasticException::check_exception( set_index_inserted, nullptr, OpenGeodeException::TYPE::data, - "[ObjectSet]- group (", set_uuid.string(), ") already exists." ); + "[ObjectSets] Group (", set_uuid.string(), ") already exists." ); return set_uuid; } @@ -138,12 +138,12 @@ namespace geode { OpenGeodeStochasticStochasticException::check_exception( !old_object_id.fixed, nullptr, OpenGeodeException::TYPE::data, - "[ObjectSet]- cannot modify fixed object." ); + "[ObjectSets]- Cannot modify a fixed object." ); auto& set = get_set( old_object_id.set_id ); OpenGeodeStochasticStochasticException::check_exception( old_object_id.index < set.nb_objects(), nullptr, - OpenGeodeException::TYPE::data, - "[ObjectSet]- index of object to update out of range." ); + OpenGeodeException::TYPE::internal, + "[ObjectSets] Index of an object to change is out of range." ); auto old_box = object_bounding_box( get_object( old_object_id ) ); auto new_box = object_bounding_box( new_object ); neighborhood_.update( old_box, new_box, old_object_id ); @@ -155,8 +155,8 @@ namespace geode { auto& set = get_set( object_id.set_id ); OpenGeodeStochasticStochasticException::check_exception( - !object_id.fixed, nullptr, OpenGeodeException::TYPE::data, - "[ObjectSet]- Cannot remove fixed object." ); + !object_id.fixed, nullptr, OpenGeodeException::TYPE::internal, + "[ObjectSets] Cannot remove fixed object." ); const auto& obj_to_remove = get_object( object_id ); neighborhood_.remove( object_bounding_box( obj_to_remove ), object_id ); @@ -202,7 +202,7 @@ namespace geode } throw OpenGeodeStochasticStochasticException{ nullptr, OpenGeodeException::TYPE::data, - "[ObjectSets] ObjectSet uuid accessor - group named ", name, + "[ObjectSets] ObjectSet uuid accessor of group named ", name, " does not exist." }; } 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 index 01fddb8..53d1002 100644 --- a/src/geode/stochastic/spatial/single_object_features/segment_length_feature.cpp +++ b/src/geode/stochastic/spatial/single_object_features/segment_length_feature.cpp @@ -109,7 +109,7 @@ namespace geode { auto seg_extremities = segment.vertices(); - const double length = length_inside_box( + const auto length = length_inside_box( seg_extremities[0], seg_extremities[1], domain.box() ); return inv_length_ * length; diff --git a/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp b/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp index 37d25f6..71aaf69 100644 --- a/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp +++ b/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp @@ -91,7 +91,7 @@ namespace "0." ); geode::OpenGeodeStochasticStochasticException::test( - std::abs( + std::fabs( proposed_move.proposal_probabilities .log_backward_prob - ( std::log( 0.8 * 0.5 ) diff --git a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp index 7f94e4b..b753854 100644 --- a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp +++ b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp @@ -50,7 +50,7 @@ namespace geode::MetropolisHastings< geode::Point2D >::acceptance_prob_helper( -1.0 ); geode::OpenGeodeStochasticStochasticException::test( - std::abs( val - std::exp( -1.0 ) ) < 1e-12, + std::fabs( val - std::exp( -1.0 ) ) < 1e-12, "[MH test] acceptance_prob_helper wrong for -1.0." ); } diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 50857df..f850dca 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 ) { @@ -131,7 +131,7 @@ namespace // this->ordered_target_statistics_[stat_id]; // // const auto target_vs_mean_error = - // std::abs( computed_means[stat_id] - + // std::fabs( computed_means[stat_id] - // expected_means ) / expected_means; // // geode::OpenGeodeStochasticStochasticException::test( @@ -143,7 +143,7 @@ namespace // : ", target_vs_mean_error ); // // const auto target_vs_variance_error = - // std::abs( computed_variances[stat_id] - + // std::fabs( computed_variances[stat_id] - // expected_means ) / expected_means; // // geode::OpenGeodeStochasticStochasticException::test( diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index 9311704..61cc11d 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -171,7 +171,7 @@ namespace const auto expected_mean = this->ordered_target_statistics_[stat_id]; auto target_vs_mean_error = - std::abs( computed_means[stat_id] - expected_mean ); + std::fabs( computed_means[stat_id] - expected_mean ); if( expected_mean > 0 ) { target_vs_mean_error /= expected_mean; diff --git a/tests/stochastic/sampling/test-random-engine.cpp b/tests/stochastic/sampling/test-random-engine.cpp index 49b0f53..03a8880 100644 --- a/tests/stochastic/sampling/test-random-engine.cpp +++ b/tests/stochastic/sampling/test-random-engine.cpp @@ -98,10 +98,10 @@ void test_distribution_mean_and_variance( const std::vector< T >& data, std::sqrt( 2.0 * expected_var * expected_var / ( n - 1 ) ); geode::OpenGeodeStochasticStochasticException::test( - std::abs( mean - expected_mean ) < k * se_mean, + std::fabs( mean - expected_mean ) < k * se_mean, "[Uniform] - Wrong expected mean." ); geode::OpenGeodeStochasticStochasticException::test( - std::abs( variance - expected_var ) < k * se_var, + std::fabs( variance - expected_var ) < k * se_var, "[Uniform] - Wrong expected std." ); } From adfec09cf87d3dd04d2a29dafbc5b90f4a3f1481 Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Mon, 25 May 2026 15:36:47 +0000 Subject: [PATCH 13/59] 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 f850dca..dc272e7 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 8ddb2df608bafea424066115fdd04576973ffd70 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Mon, 25 May 2026 20:53:32 +0200 Subject: [PATCH 14/59] feat(statisticstools): implement validator and quadratic loss. --- .../stochastic/inference/statistics_tools.hpp | 76 +++++++++++++ .../inference/target_statistics.hpp | 100 ++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 include/geode/stochastic/inference/statistics_tools.hpp create mode 100644 include/geode/stochastic/inference/target_statistics.hpp diff --git a/include/geode/stochastic/inference/statistics_tools.hpp b/include/geode/stochastic/inference/statistics_tools.hpp new file mode 100644 index 0000000..ae0abf2 --- /dev/null +++ b/include/geode/stochastic/inference/statistics_tools.hpp @@ -0,0 +1,76 @@ +/* + * 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 +{ + namespace statistics + { + template < typename ObjectType > + void validate( const StatisticsTracker< ObjectType >& tracker, + const TargetStatistics< ObjectType >& targets ) + { + const auto& model = targets.model(); + + for( const auto& term_uuid : targets.active_terms() ) + { + const auto mean = tracker.mean( term_uuid ); + const auto target = targets.target( term_uuid ); + + const auto rel_error = + std::fabs( mean - target ) + / ( std::fabs( target ) + geode::GLOBAL_EPSILON ); + + OpenGeodeStochasticStochasticException::check_exception( + rel_error < targets.tolerance( term_uuid ), nullptr, + OpenGeodeException::TYPE::result, + "[StatisticsValidator] Failure for term ", + model.term_name( term_uuid ), "\n mean = ", mean, + "\n target = ", target, "\n error = ", rel_error, + "\n tol = ", targets.tolerance( term_uuid ) ); + } + } + + template < typename ObjectType > + double quadratic_loss( const StatisticsTracker< ObjectType >& tracker, + const TargetStatistics< ObjectType >& targets ) + { + double loss = 0.0; + + for( const auto& term_uuid : targets.active_terms() ) + { + const auto diff = + tracker.mean( term_uuid ) - targets.value( term_uuid ); + + loss += diff * diff; + } + + return loss; + } + } // namespace statistics +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/inference/target_statistics.hpp b/include/geode/stochastic/inference/target_statistics.hpp new file mode 100644 index 0000000..b845322 --- /dev/null +++ b/include/geode/stochastic/inference/target_statistics.hpp @@ -0,0 +1,100 @@ +/* + * 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 +{ + struct TargetStatisticConfig + { + std::string term_name; + double value; + double tolerance; + }; + + template < typename ObjectType > + class TargetStatistics + { + public: + explicit TargetStatistics( const Model< ObjectType >& model ) + : model_( model ) + { + values_.resize( model.nb_terms(), 0.0 ); + tolerances_.resize( model.nb_terms(), 0.0 ); + active_.resize( model.nb_terms(), false ); + } + + const Model< ObjectType >& model() const + { + return model_; + } + + void set_target( const TargetStatisticConfig& statistic ) + { + const auto term_uuid = + model_.terms().get_term_uuid( statistic.term_name ); + const auto idx = model_.term_index( term_uuid ); + + values_[idx] = statistic.value; + tolerances_[idx] = statistic.tolerance; + active_[idx] = true; + } + + bool has_target( const uuid& term_uuid ) const + { + return active_[model_.term_index( term_uuid )]; + } + + double target( const uuid& term_uuid ) const + { + return values_[model_.term_index( term_uuid )]; + } + + double tolerance( const uuid& term_uuid ) const + { + return tolerances_[model_.term_index( term_uuid )]; + } + + std::vector< uuid > active_terms() const + { + std::vector< uuid > active_terms_uuid; + + for( const auto& term : model_.terms().energy_terms() ) + { + const auto& id = term->id(); + if( active_[model_.term_index( id )] ) + { + active_terms_uuid.push_back( id ); + } + } + return active_terms_uuid; + } + + private: + const Model< ObjectType >& model_; + + std::vector< double > values_; + std::vector< double > tolerances_; + std::vector< bool > active_; + }; +} // namespace geode From fda9affc31c015785c9e0c5b351a44fb0a22211a Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Mon, 25 May 2026 20:56:11 +0200 Subject: [PATCH 15/59] feat(StatsticTool): Implement validator --- .../inference/statistic_objective.hpp | 52 ----------- .../inference/statistic_validator.hpp | 57 ------------ .../stochastic/inference/target_statistic.hpp | 34 -------- .../mcmc/helpers/simulation_monitor_old.hpp | 86 ------------------- .../sampling/mcmc/helpers/state_dynamics.hpp | 86 ------------------- .../sampling/mcmc/simulation_runner.hpp | 23 +++-- src/geode/stochastic/CMakeLists.txt | 6 +- .../sampling/mcmc/test-mh-poisson.cpp | 61 ++++++++----- 8 files changed, 60 insertions(+), 345 deletions(-) delete mode 100644 include/geode/stochastic/inference/statistic_objective.hpp delete mode 100644 include/geode/stochastic/inference/statistic_validator.hpp delete mode 100644 include/geode/stochastic/inference/target_statistic.hpp delete mode 100644 include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor_old.hpp delete mode 100644 include/geode/stochastic/sampling/mcmc/helpers/state_dynamics.hpp diff --git a/include/geode/stochastic/inference/statistic_objective.hpp b/include/geode/stochastic/inference/statistic_objective.hpp deleted file mode 100644 index e49fc64..0000000 --- a/include/geode/stochastic/inference/statistic_objective.hpp +++ /dev/null @@ -1,52 +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. - * - */ -#pragma once - -#include -#include -#include - -namespace geode -{ - - template < typename ObjectType > - class StatisticObjective - { - public: - double compute_loss( const StatisticsTracker< ObjectType >& monitor, - const std::vector< TargetStatistic >& targets ) const - { - double loss = 0.0; - - for( const auto& target : targets ) - { - const auto mean = monitor.mean( target.term_id ); - const auto 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 deleted file mode 100644 index 19c3721..0000000 --- a/include/geode/stochastic/inference/statistic_validator.hpp +++ /dev/null @@ -1,57 +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. - * - */ -#pragma once - -#include - -#include -#include - -namespace geode -{ - template < typename ObjectType > - class StatisticsValidator - { - public: - void check( const StatisticsTracker& monitor, - const std::vector< TargetStatistic >& targets ) const - { - for( const auto& target : targets ) - { - const auto mean = monitor.mean( target.term_id ); - const auto rel_error = std::fabs( mean - target.value ) - / ( std::fabs( target.value ) + 1e-12 ); - OpenGeodeStochasticStochasticException::check_exception( - rel_error < target.tolerance, nullptr, - OpenGeodeException::TYPE::result, - "[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/inference/target_statistic.hpp b/include/geode/stochastic/inference/target_statistic.hpp deleted file mode 100644 index b637062..0000000 --- a/include/geode/stochastic/inference/target_statistic.hpp +++ /dev/null @@ -1,34 +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. - * - */ -#pragma once -#include - -namespace geode -{ - struct TargetStatistic - { - geode::uuid term_id; - double value; - double tolerance{ 0.05 }; - }; -} // namespace geode diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor_old.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor_old.hpp deleted file mode 100644 index 3ceaf32..0000000 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor_old.hpp +++ /dev/null @@ -1,86 +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. - * - */ - -#pragma once -#include -#include - -namespace geode -{ - - class StatisticsTracker - { - public: - StatisticsTracker( StatisticsTracker&& ) = default; - StatisticsTracker( const StatisticsTracker& ) = default; - StatisticsTracker& operator=( StatisticsTracker&& ) noexcept = default; - StatisticsTracker& operator=( - const StatisticsTracker& ) noexcept = default; - - StatisticsTracker( const index_t nb_energy_terms ) - { - means_.resize( nb_energy_terms, 0.0 ); - variances_.resize( nb_energy_terms, 0.0 ); - } - - void add_realization( const std::vector< double >& values ) - { - OpenGeodeStochasticStochasticException::check_exception( - values.size() == means_.size(), nullptr, - OpenGeodeException::TYPE::data, - "[StatisticsTracker] Mismatch between realization size and " - "expected number of statistics." ); - ++count_; - for( size_t i = 0; i < values.size(); ++i ) - { - double delta = values[i] - means_[i]; - means_[i] += delta / count_; - if( count_ > 1 ) - variances_[i] = ( ( count_ - 2 ) * variances_[i] - + delta * ( values[i] - means_[i] ) ) - / ( count_ - 1 ); - } - } - - const index_t statiscal_count() const - { - return count_; - } - - const std::vector< double >& means() const - { - return means_; - } - - const std::vector< double >& variances() const - { - return variances_; - } - - private: - std::vector< double > means_; - std::vector< double > variances_; - index_t count_{ 0 }; - }; - -} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/sampling/mcmc/helpers/state_dynamics.hpp b/include/geode/stochastic/sampling/mcmc/helpers/state_dynamics.hpp deleted file mode 100644 index 58d8765..0000000 --- a/include/geode/stochastic/sampling/mcmc/helpers/state_dynamics.hpp +++ /dev/null @@ -1,86 +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. - * - */ - -#pragma once -#include -#include -#include -#include - -namespace geode -{ - - template < typename ObjectType > - class StateDynamics - { - public: - StateDynamics() = default; - ~StateDynamics() = default; - - ObjectSets< ObjectType >& state() - { - return object_sets_; - } - const ObjectSets< ObjectType >& state() const - { - return object_sets_; - } - - std::vector< std::unique_ptr< ObjectSetSampler< ObjectType > > >& - samplers() - { - return samplers_; - } - const std::vector< std::unique_ptr< ObjectSetSampler< ObjectType > > >& - samplers() const - { - return samplers_; - } - - uuid add_set( const std::string& name ) - { - return object_sets_.add_set( name ); - } - - void add_sampler( - std::unique_ptr< ObjectSetSampler< ObjectType > > sampler ) - { - samplers_.push_back( std::move( sampler ) ); - } - - ObjectSetSampler< ObjectType >& sampler( const index_t sampler_id ) - { - OpenGeodeStochasticStochasticException::check_exception( - sampler_id < samplers_.size(), nullptr, - OpenGeodeException::TYPE::internal, - "[StateDynamics]: Sampler out of range." ); - return samplers_[sampler_id].get(); - } - - private: - ObjectSets< ObjectType > object_sets_; - std::vector< std::unique_ptr< ObjectSetSampler< ObjectType > > > - samplers_; - }; - -} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index c306efb..bc6aaac 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -24,6 +24,8 @@ #pragma once #include #include +#include + #include #include #include @@ -64,7 +66,7 @@ namespace geode { public: SimulationRunner( const SpatialDomain< ObjectType::dim >& domain ) - : domain_( domain ) {}; + : domain_( domain ){}; virtual ~SimulationRunner() = default; virtual void initialize() = 0; @@ -117,14 +119,21 @@ namespace geode return stats_monitor; } - const ObjectSets< ObjectType >& state_realization() const + [[nodiscard]] const TargetStatistics< ObjectType >& + target_statistics() const { - return object_sets_; + OpenGeodeStochasticStochasticException::check_exception( + target_statistics_.has_value(), nullptr, + OpenGeodeException::TYPE::data, + "[SimulationRunner] Target statistics not initialized" ); + + return *target_statistics_; } - protected: - // void initialize_sets_and_samplers() = 0; - // void initialize_model() = 0; + [[nodiscard]] const ObjectSets< ObjectType >& state_realization() const + { + return object_sets_; + } protected: SpatialDomain< ObjectType::dim > domain_; @@ -134,5 +143,7 @@ namespace geode set_samplers_; std::unique_ptr< Model< ObjectType > > model_; std::unique_ptr< geode::MetropolisHastings< ObjectType > > mh_sampler_; + + std::optional< TargetStatistics< ObjectType > > target_statistics_; }; } // namespace geode \ No newline at end of file diff --git a/src/geode/stochastic/CMakeLists.txt b/src/geode/stochastic/CMakeLists.txt index f71b40d..c624bbf 100644 --- a/src/geode/stochastic/CMakeLists.txt +++ b/src/geode/stochastic/CMakeLists.txt @@ -38,10 +38,9 @@ add_geode_library( "common.cpp" PUBLIC_HEADERS #"inference/abc_shadow.hpp" - "inference/statistic_objective.hpp" + "inference/statistics_tools.hpp" "inference/statistics_tracker.hpp" - "inference/target_statistic.hpp" - "inference/statistic_validator.hpp" + "inference/target_statistics.hpp" "spatial/object_set.hpp" "spatial/object_sets.hpp" "spatial/object_neighborhood.hpp" @@ -66,7 +65,6 @@ 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" "models/energy_terms/energy_term.hpp" "models/energy_terms/pairwise_term.hpp" "models/energy_terms/single_object_term.hpp" diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index dc272e7..eabfe37 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -21,7 +21,8 @@ * */ #include -#include +#include +#include #include #include #include @@ -39,17 +40,9 @@ namespace double birth_ratio{ 1.0 }; double death_ratio{ 1.0 }; double change_ratio{ 1.0 }; - - 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 > @@ -69,13 +62,18 @@ namespace density_descriptors_.push_back( descriptor ); } + void add_target_statistics( + const geode::TargetStatisticConfig& statistic_descriptor ) + { + targeted_statistics_descriptors_.push_back( statistic_descriptor ); + } + std::unique_ptr< geode::ProposalKernel< geode::Point2D > > create_sets_and_set_samplers() { auto proposal_kernel = std::make_unique< geode::ProposalKernel< geode::Point2D > >(); - // 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 ); @@ -101,6 +99,16 @@ namespace model_ = std::move( geode::build_model< geode::Point2D >( config, object_sets_, domain_ ) ); + create_target_statistics(); + } + + void create_target_statistics() + { + target_statistics_.emplace( *model_ ); + for( const auto& target_stat : targeted_statistics_descriptors_ ) + { + target_statistics_->set_target( target_stat ); + } } void initialize() override @@ -111,6 +119,7 @@ namespace this->mh_sampler_ = std::make_unique< geode::MetropolisHastings< geode::Point2D > >( *model_, std::move( proposal_kernel ) ); + create_target_statistics(); } void check_statistics( const geode::StatisticsTracker< geode::Point2D >& @@ -161,6 +170,8 @@ namespace geode::BoundingBox2D box_; std::vector< SetDescription > set_descriptors_; std::vector< PoissonDensityDescription > density_descriptors_; + std::vector< geode::TargetStatisticConfig > + targeted_statistics_descriptors_; }; void test_single_type_poisson() @@ -192,12 +203,14 @@ namespace densityA.term_name = "density"; densityA.object_set_names = { "A" }; densityA.lambda = 0.3; - // densityA.target_count = 30.0; densityA.object_feature = geode::ObjectInDomainFeatureConfig{}; + geode::TargetStatisticConfig statA{ "density", 30.0, 0.15 }; + PoissonSimulationRunner runner( domain ); runner.add_set_descriptor( setA ); runner.add_density_descriptor( densityA ); + runner.add_target_statistics( statA ); runner.initialize(); // run simulation @@ -212,9 +225,9 @@ namespace sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; - auto statistic_monitoring = runner.run( engine, sim_config ); - - runner.check_statistics( statistic_monitoring ); + auto statistic_tracker = runner.run( engine, sim_config ); + geode::statistics::validate( + statistic_tracker, runner.target_statistics() ); } geode::Logger::info( "--> SUCCESS!" ); @@ -242,23 +255,26 @@ namespace density01.term_name = "density01"; density01.object_set_names = { "set01" }; density01.lambda = 0.1; - // density01.target_count = 10.0; density01.object_feature = geode::ObjectInDomainFeatureConfig{}; + geode::TargetStatisticConfig stat01{ "density01", 10.0, 0.15 }; + PoissonDensityDescription density02; density02.term_name = "density02"; density02.object_set_names = { "set02" }; density02.lambda = 0.4; - // density02.target_count = 40.0; density02.object_feature = geode::ObjectInDomainFeatureConfig{}; + geode::TargetStatisticConfig stat02{ "density02", 40.0, 0.15 }; + PoissonDensityDescription density03; density03.term_name = "density03"; density03.object_set_names = { "set03" }; density03.lambda = 0.3; - // density03.target_count = 30.0; density03.object_feature = geode::ObjectInDomainFeatureConfig{}; + geode::TargetStatisticConfig stat03{ "density03", 30.0, 0.15 }; + PoissonSimulationRunner runner( domain ); runner.add_set_descriptor( set01 ); runner.add_set_descriptor( set02 ); @@ -268,6 +284,10 @@ namespace runner.add_density_descriptor( density02 ); runner.add_density_descriptor( density03 ); + runner.add_target_statistics( stat01 ); + runner.add_target_statistics( stat02 ); + runner.add_target_statistics( stat03 ); + runner.initialize(); // run simulation @@ -281,8 +301,9 @@ namespace sim_config.burn_in_steps = 3000; sim_config.printer = printer_config; - auto statistic_monitoring = runner.run( engine, sim_config ); - runner.check_statistics( statistic_monitoring ); + auto statistic_tracker = runner.run( engine, sim_config ); + geode::statistics::validate( + statistic_tracker, runner.target_statistics() ); geode::Logger::info( "--> SUCCESS!" ); } From f534c2ce7132cee5a09daf213ade0d25aa968fcd Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Mon, 25 May 2026 18:56:46 +0000 Subject: [PATCH 16/59] Apply prepare changes --- include/geode/stochastic/sampling/mcmc/simulation_runner.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index bc6aaac..74eb77a 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -66,7 +66,7 @@ namespace geode { public: SimulationRunner( const SpatialDomain< ObjectType::dim >& domain ) - : domain_( domain ){}; + : domain_( domain ) {}; virtual ~SimulationRunner() = default; virtual void initialize() = 0; From 9f30edab9d6ddd68f42d20b33a2952b4ee970c35 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Mon, 25 May 2026 20:59:37 +0200 Subject: [PATCH 17/59] fix clang error --- .../single_object_features/single_object_feature_builder.hpp | 1 + 1 file changed, 1 insertion(+) 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 d0d810b..edd3c88 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 @@ -34,6 +34,7 @@ namespace geode build_single_object_feature_impl( const ObjectInDomainFeatureConfig& cfg ) { + geode_unused( cfg ); return std::make_unique< ObjectInDomainFeature< ObjectType > >(); }; From 866cb8402912a5d78a93713d4affad535a2a36e8 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Mon, 25 May 2026 21:13:17 +0200 Subject: [PATCH 18/59] try linking issue --- .../stochastic/models/energy_terms/energy_term_builder.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 80ba0c9..c3b1503 100644 --- a/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp @@ -58,13 +58,14 @@ namespace geode auto object_feature = build_single_object_feature< ObjectType >( cfg.object_feature ); - return std::make_unique< geode::SingleObjectTerm< ObjectType > >( + return std::make_unique< SingleObjectTerm< ObjectType > >( cfg.term_name, cfg.lambda, std::move( set_ids ), domain, std::move( object_feature ) ); } - std::pair< std::vector< geode::uuid >, + std::pair< std::vector< uuid >, absl::flat_hash_map< uuid, std::vector< uuid > > > + opengeode_stochastic_stochastic_api pairwise_builder_initialize_interactions_helper( const std::vector< std::pair< uuid, uuid > >& interacting_sets ); From 64cc95d478cc021be481feda01edc00ee8293087 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 26 May 2026 16:11:43 +0200 Subject: [PATCH 19/59] feat(StraussTest): reactivate Strauss and Poisson Tests. --- .../stochastic/inference/statistics_tools.hpp | 2 +- .../models/energy_terms/pairwise_term.hpp | 20 +- .../sampling/mcmc/simulation_runner.hpp | 2 +- .../energy_terms/energy_term_builder.cpp | 4 +- tests/stochastic/CMakeLists.txt | 14 +- .../sampling/mcmc/test-mh-poisson.cpp | 47 +--- .../sampling/mcmc/test-mh-strauss.cpp | 263 +++++++++--------- 7 files changed, 163 insertions(+), 189 deletions(-) diff --git a/include/geode/stochastic/inference/statistics_tools.hpp b/include/geode/stochastic/inference/statistics_tools.hpp index ae0abf2..990e7e2 100644 --- a/include/geode/stochastic/inference/statistics_tools.hpp +++ b/include/geode/stochastic/inference/statistics_tools.hpp @@ -49,7 +49,7 @@ namespace geode OpenGeodeStochasticStochasticException::check_exception( rel_error < targets.tolerance( term_uuid ), nullptr, OpenGeodeException::TYPE::result, - "[StatisticsValidator] Failure for term ", + "[StatisticsValidator] Failure \n --> term ", model.term_name( term_uuid ), "\n mean = ", mean, "\n target = ", target, "\n error = ", rel_error, "\n tol = ", targets.tolerance( term_uuid ) ); diff --git a/include/geode/stochastic/models/energy_terms/pairwise_term.hpp b/include/geode/stochastic/models/energy_terms/pairwise_term.hpp index 747ab99..75fc792 100644 --- a/include/geode/stochastic/models/energy_terms/pairwise_term.hpp +++ b/include/geode/stochastic/models/energy_terms/pairwise_term.hpp @@ -119,10 +119,16 @@ namespace geode std::optional< ObjectId > exclude_id, const ObjectSets< ObjectType >& state ) const { - double sum = 0.0; + const auto impacted_set_it = + objectset_adjacency_map_.find( object_ref.set_id ); + if( impacted_set_it == objectset_adjacency_map_.end() ) + { + return 0.; + } const auto neighbors = state.neighbors( object_ref.object, - objectset_adjacency_map_.at( object_ref.set_id ), + impacted_set_it->second, interaction_->neighborhood_searching_distance(), exclude_id ); + double sum = 0.0; for( const auto& neigh_id : neighbors ) { ObjectRef< ObjectType > neigh_object{ @@ -147,11 +153,17 @@ namespace geode { return 0.; } - double sum = 0.0; + const auto impacted_set_it = + objectset_adjacency_map_.find( object_id.set_id ); + if( impacted_set_it == objectset_adjacency_map_.end() ) + { + return 0.; + } const auto neighbors = state.neighbors( cur_obj, - objectset_adjacency_map_.at( object_id.set_id ), + impacted_set_it->second, interaction_->neighborhood_searching_distance(), object_id ); ObjectRef< ObjectType > object_ref{ cur_obj, object_id.set_id }; + double sum = 0.0; for( const auto& neigh_id : neighbors ) { const auto& neigh_obj = state.get_object( neigh_id ); diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index 74eb77a..bc6aaac 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -66,7 +66,7 @@ namespace geode { public: SimulationRunner( const SpatialDomain< ObjectType::dim >& domain ) - : domain_( domain ) {}; + : domain_( domain ){}; virtual ~SimulationRunner() = default; virtual void initialize() = 0; diff --git a/src/geode/stochastic/models/energy_terms/energy_term_builder.cpp b/src/geode/stochastic/models/energy_terms/energy_term_builder.cpp index ba865cd..52ff4a0 100644 --- a/src/geode/stochastic/models/energy_terms/energy_term_builder.cpp +++ b/src/geode/stochastic/models/energy_terms/energy_term_builder.cpp @@ -63,12 +63,12 @@ namespace namespace geode { - std::pair< std::vector< geode::uuid >, + std::pair< std::vector< 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; + std::vector< uuid > interacting_set_ids; absl::flat_hash_map< uuid, std::vector< uuid > > objectset_adjacency_map; diff --git a/tests/stochastic/CMakeLists.txt b/tests/stochastic/CMakeLists.txt index b18c043..10cbb1e 100644 --- a/tests/stochastic/CMakeLists.txt +++ b/tests/stochastic/CMakeLists.txt @@ -176,13 +176,13 @@ add_geode_test( ${PROJECT_NAME}::stochastic ) -#add_geode_test( -# SOURCE "sampling/mcmc/test-mh-strauss.cpp" -# DEPENDENCIES -# OpenGeode::basic -# OpenGeode::geometry -# ${PROJECT_NAME}::stochastic -#) +add_geode_test( + SOURCE "sampling/mcmc/test-mh-strauss.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) add_geode_test( diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index eabfe37..9e2affc 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -49,7 +49,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 ) { @@ -122,52 +122,7 @@ namespace create_target_statistics(); } - 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::fabs( computed_means[stat_id] - - // expected_means ) / expected_means; - // - // 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 ); - // - // const auto target_vs_variance_error = - // std::fabs( computed_variances[stat_id] - - // expected_means ) / expected_means; - // - // 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() ), " not close enough to - // expected value ", expected_means, " --> error - // : ", target_vs_variance_error ); - // } - } - private: - geode::BoundingBox2D box_; std::vector< SetDescription > set_descriptors_; std::vector< PoissonDensityDescription > density_descriptors_; std::vector< geode::TargetStatisticConfig > diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index 61cc11d..bfc3830 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -21,15 +21,16 @@ * */ #include -#include -#include +#include +#include #include -#include -#include -#include #include +#include +#include +#include #include -#include +// #include +#include namespace { @@ -41,21 +42,8 @@ namespace double change_ratio{ 1.0 }; }; - struct PoissonDensityDescription - { - std::string name; - double density; - double target_count; - }; - - struct PairwiseInteractionDescription - { - std::vector< std::string > names; - double strength; - double distance_threshold; - // geode::PairwiseInteraction::SCOPE interaction_scope; - double target_interaction_count; - }; + using PoissonDensityDescription = geode::SingleObjectTermConfig; + using PairwiseInteractionDescription = geode::PairwiseTermConfig; class StraussSimulationRunner : public geode::SimulationRunner< geode::Point2D > @@ -83,113 +71,75 @@ namespace interaction_descriptors_.push_back( descriptor ); } - void initialize() override + void add_target_statistics( + const geode::TargetStatisticConfig& statistic_descriptor ) + { + targeted_statistics_descriptors_.push_back( statistic_descriptor ); + } + std::unique_ptr< geode::ProposalKernel< geode::Point2D > > + create_sets_and_set_samplers() { 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 > >( - this->domain_ ) ); + domain_ ) ); geode::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 ); } + return proposal_kernel; + } - // Step 2: create density energy terms - for( const auto& density_desc : density_descriptors_ ) + void create_model() + { + geode::ModelConfig config; + for( const auto& energy_desc : density_descriptors_ ) { - const auto set_id = name_to_uuid.at( density_desc.name ); - this->ordered_energy_terms_.push_back( - this->energy_terms_collection_.add_energy_term( - std::make_unique< - geode::DensityTerm< geode::Point2D > >( - absl::StrCat( density_desc.name, "_density" ), - density_desc.density, - std::vector< geode::uuid >{ set_id }, - this->domain_ ) ) ); - - this->ordered_target_statistics_.push_back( - density_desc.target_count ); + config.terms.push_back( energy_desc ); } - - // Step 3: create pairwise interaction terms for( const auto& interaction_desc : interaction_descriptors_ ) { - std::vector< geode::uuid > set_ids; - for( const auto& name : interaction_desc.names ) - { - set_ids.emplace_back( name_to_uuid.at( name ) ); - } - - auto interaction = std::make_unique< - geode::MinimalDistanceCutoff< geode::Point2D > >( - interaction_desc.distance_threshold - /*,interaction_desc.interaction_scope*/ ); - - this->ordered_energy_terms_.push_back( - this->energy_terms_collection_.add_energy_term( - std::make_unique< - geode::PairwiseTerm< geode::Point2D > >( - absl::StrCat( - absl::StrJoin( interaction_desc.names, "_" ), - "_interaction" ), - interaction_desc.strength, set_ids, - std::move( interaction ), this->domain_ ) ) ); - - this->ordered_target_statistics_.push_back( - interaction_desc.target_interaction_count ); + config.terms.push_back( interaction_desc ); } - this->mh_sampler_ = - std::make_unique< geode::MetropolisHastings< geode::Point2D > >( - this->energy_terms_collection_, - std::move( proposal_kernel ) ); + model_ = std::move( geode::build_model< geode::Point2D >( + config, object_sets_, domain_ ) ); + create_target_statistics(); } - void check_statistics( - const geode::StatisticsTracker& statistic_monitoring ) const + void create_target_statistics() { - const auto& computed_means = statistic_monitoring.means(); - - for( const auto stat_id : - geode::Range{ this->energy_terms_collection_.size() } ) + target_statistics_.emplace( *model_ ); + for( const auto& target_stat : targeted_statistics_descriptors_ ) { - const auto& term = energy_terms_collection_.get( - ordered_energy_terms_[stat_id] ); - - const auto expected_mean = - this->ordered_target_statistics_[stat_id]; - auto target_vs_mean_error = - std::fabs( computed_means[stat_id] - expected_mean ); - if( expected_mean > 0 ) - { - target_vs_mean_error /= expected_mean; - } - - 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 ); + target_statistics_->set_target( target_stat ); } } + void initialize() override + { + auto proposal_kernel = create_sets_and_set_samplers(); + create_model(); + + this->mh_sampler_ = + std::make_unique< geode::MetropolisHastings< geode::Point2D > >( + *model_, std::move( proposal_kernel ) ); + create_target_statistics(); + } + private: std::vector< SetDescription > set_descriptors_; std::vector< PoissonDensityDescription > density_descriptors_; std::vector< PairwiseInteractionDescription > interaction_descriptors_; + + std::vector< geode::TargetStatisticConfig > + targeted_statistics_descriptors_; }; void test_single_type_strauss() @@ -203,12 +153,11 @@ namespace geode::BoundingBox2D box; box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); box.add_point( geode::Point2D{ { 10.0, 10.0 } } ); - // todo change!! geode::SpatialDomain domain( box, 1. ); std::array< double, 5 > gamma_values{ 0, 0.3, 0.5, 0.7, 1.0 }; std::array< double, 5 > nb_points{ 19.5, 24.4, 31.3, 36.1, 50. }; - std::array< double, 5 > nb_interactions{ 0, 4, 8, 15, 43 }; + std::array< double, 5 > nb_interactions{ 0, 4.7, 9.8, 18.7, 50.3 }; for( const auto config : geode::Range{ gamma_values.size() } ) { @@ -219,26 +168,35 @@ namespace setA.death_ratio = 1.0; setA.change_ratio = 1.0; - // --- Density term + // --- Energy term description PoissonDensityDescription densityA; - densityA.name = "A"; - densityA.density = 0.5; - densityA.target_count = nb_points[config]; + densityA.term_name = "densityA"; + densityA.object_set_names = { "A" }; + densityA.lambda = 0.5; + densityA.object_feature = geode::ObjectInDomainFeatureConfig{}; + + geode::TargetStatisticConfig statA{ "densityA", nb_points[config], + 0.1 }; // --- Intra-set pairwise interaction (Strauss process) PairwiseInteractionDescription intraA; - intraA.names = { "A" }; // same set - intraA.strength = gamma_values[config]; - intraA.distance_threshold = 1; - // intraA.interaction_scope = - // geode::PairwiseInteraction::SCOPE::INTRA; - intraA.target_interaction_count = nb_interactions[config]; + intraA.term_name = "interactionA"; + intraA.object_set_names_interactions = { { "A", "A" } }; + intraA.gamma = gamma_values[config]; + + intraA.interaction_config = geode::MinimalDistanceCutoffConfig{ + 1. + }; /// ici cela devrait etre un paramètre utilisateur + + geode::TargetStatisticConfig stat_intra_A{ "interactionA", + nb_interactions[config], 0.1 }; StraussSimulationRunner runner( domain ); runner.add_set_descriptor( setA ); runner.add_density_descriptor( densityA ); runner.add_interaction_descriptor( intraA ); - + runner.add_target_statistics( statA ); + runner.add_target_statistics( stat_intra_A ); runner.initialize(); // run simulation @@ -253,8 +211,9 @@ namespace sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; - auto statistic_monitoring = runner.run( engine, sim_config ); - runner.check_statistics( statistic_monitoring ); + auto statistic_tracker = runner.run( engine, sim_config ); + geode::statistics::validate( + statistic_tracker, runner.target_statistics() ); } geode::Logger::info( "--> SUCCESS!" ); @@ -271,15 +230,14 @@ namespace geode::BoundingBox2D box; box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); box.add_point( geode::Point2D{ { 10.0, 10.0 } } ); - // todo change!! - geode::SpatialDomain domain( box, 0. ); + geode::SpatialDomain domain( box, 2. ); std::array< double, 3 > gamma_values{ 0, 0.5, 1.0 }; - std::array< double, 3 > nb_points01{ 3.5, 5, 10.0 }; - std::array< double, 3 > nb_points02{ 14, 21, 40.0 }; - std::array< double, 3 > nb_points03{ 11, 16, 30. }; - std::array< double, 3 > nb_interactions01{ 0, 15, 95 }; - std::array< double, 3 > nb_interactions02{ 8, 20, 85 }; + std::array< double, 3 > nb_points01{ 6.7, 8, 10.0 }; + std::array< double, 3 > nb_points02{ 17.5, 24.6, 40.0 }; + std::array< double, 3 > nb_points03{ 14.6, 19.4, 30. }; + std::array< double, 3 > nb_interactions01{ 0, 15, 59.8 }; + std::array< double, 3 > nb_interactions02{ 37.2, 70, 174 }; for( const auto config : geode::Range{ gamma_values.size() } ) { @@ -289,17 +247,59 @@ namespace SetDescription set03{ "set03", 4.0, 1.0, 1.0 }; // --- Density terms - PoissonDensityDescription d01{ "set01", 0.1, nb_points01[config] }; - PoissonDensityDescription d02{ "set02", 0.4, nb_points02[config] }; - PoissonDensityDescription d03{ "set03", 0.3, nb_points03[config] }; + PoissonDensityDescription d01; + d01.term_name = "density_set01"; + d01.object_set_names = { "set01" }; + d01.lambda = 0.1; + d01.object_feature = geode::ObjectInDomainFeatureConfig{}; + + geode::TargetStatisticConfig stat01{ "density_set01", + nb_points01[config], 0.1 }; + + PoissonDensityDescription d02; + d02.term_name = "density_set02"; + d02.object_set_names = { "set02" }; + d02.lambda = 0.4; + d02.object_feature = geode::ObjectInDomainFeatureConfig{}; + + geode::TargetStatisticConfig stat02{ "density_set02", + nb_points02[config], 0.1 }; + + PoissonDensityDescription d03; + d03.term_name = "density_set03"; + d03.object_set_names = { "set03" }; + d03.lambda = 0.3; + d03.object_feature = geode::ObjectInDomainFeatureConfig{}; + + geode::TargetStatisticConfig stat03{ "density_set03", + nb_points03[config], 0.1 }; // --- Pairwise interactions // 1. Intra-type (repulsion within same set) - PairwiseInteractionDescription intra01{ { "set01", "set02", - "set03" }, - gamma_values[config], 1., nb_interactions01[config] }; - PairwiseInteractionDescription intra02{ { "set02" }, 1., 2., - nb_interactions02[config] }; + PairwiseInteractionDescription intra01; + intra01.term_name = "interaction01"; + intra01.object_set_names_interactions = { { "set01", "set01" }, + { "set02", "set02" }, { "set03", "set03" } }; + intra01.gamma = gamma_values[config]; + + intra01.interaction_config = geode::MinimalDistanceCutoffConfig{ + 1. + }; /// ici cela devrait etre un paramètre utilisateur + + geode::TargetStatisticConfig stat_intra_01{ "interaction01", + nb_interactions01[config], 0.1 }; + + PairwiseInteractionDescription intra02; + intra02.term_name = "interaction02"; + intra02.object_set_names_interactions = { { "set02", "set02" } }; + intra02.gamma = 1.; // gamma_values[config]; + + intra02.interaction_config = geode::MinimalDistanceCutoffConfig{ + 2. + }; /// ici cela devrait etre un paramètre utilisateur + + geode::TargetStatisticConfig stat_intra_02{ "interaction02", + nb_interactions02[config], 0.1 }; StraussSimulationRunner runner( domain ); runner.add_set_descriptor( set01 ); @@ -313,6 +313,12 @@ namespace runner.add_interaction_descriptor( intra01 ); runner.add_interaction_descriptor( intra02 ); + runner.add_target_statistics( stat01 ); + runner.add_target_statistics( stat02 ); + runner.add_target_statistics( stat03 ); + runner.add_target_statistics( stat_intra_01 ); + runner.add_target_statistics( stat_intra_02 ); + runner.initialize(); // run simulation @@ -327,8 +333,9 @@ namespace sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; - auto statistic_monitoring = runner.run( engine, sim_config ); - runner.check_statistics( statistic_monitoring ); + auto statistic_tracker = runner.run( engine, sim_config ); + geode::statistics::validate( + statistic_tracker, runner.target_statistics() ); } geode::Logger::info( "--> SUCCESS!" ); From 07bdc97600d65110e823355c73fe167971ce1bc3 Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Tue, 26 May 2026 14:13:10 +0000 Subject: [PATCH 20/59] 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 bc6aaac..74eb77a 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -66,7 +66,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 9e2affc..9ce9be5 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -49,7 +49,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 cf5d65873edfcc21ca772deab82575d34a435673 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 26 May 2026 18:11:31 +0200 Subject: [PATCH 21/59] tidy 00 --- .../stochastic/inference/statistics_tools.hpp | 71 +++++++++---------- .../energy_terms/energy_term_builder.cpp | 1 - .../sampling/mcmc/test-mh-poisson.cpp | 38 +++++----- .../sampling/mcmc/test-mh-strauss.cpp | 58 +++++++-------- 4 files changed, 85 insertions(+), 83 deletions(-) diff --git a/include/geode/stochastic/inference/statistics_tools.hpp b/include/geode/stochastic/inference/statistics_tools.hpp index 990e7e2..4a426be 100644 --- a/include/geode/stochastic/inference/statistics_tools.hpp +++ b/include/geode/stochastic/inference/statistics_tools.hpp @@ -27,50 +27,47 @@ #include #include -namespace geode +namespace geode::statistics { - namespace statistics + template < typename ObjectType > + void validate( const StatisticsTracker< ObjectType >& tracker, + const TargetStatistics< ObjectType >& targets ) { - template < typename ObjectType > - void validate( const StatisticsTracker< ObjectType >& tracker, - const TargetStatistics< ObjectType >& targets ) - { - const auto& model = targets.model(); + const auto& model = targets.model(); - for( const auto& term_uuid : targets.active_terms() ) - { - const auto mean = tracker.mean( term_uuid ); - const auto target = targets.target( term_uuid ); + for( const auto& term_uuid : targets.active_terms() ) + { + const auto mean = tracker.mean( term_uuid ); + const auto target = targets.target( term_uuid ); - const auto rel_error = - std::fabs( mean - target ) - / ( std::fabs( target ) + geode::GLOBAL_EPSILON ); + const auto rel_error = + std::fabs( mean - target ) + / ( std::fabs( target ) + geode::GLOBAL_EPSILON ); - OpenGeodeStochasticStochasticException::check_exception( - rel_error < targets.tolerance( term_uuid ), nullptr, - OpenGeodeException::TYPE::result, - "[StatisticsValidator] Failure \n --> term ", - model.term_name( term_uuid ), "\n mean = ", mean, - "\n target = ", target, "\n error = ", rel_error, - "\n tol = ", targets.tolerance( term_uuid ) ); - } + OpenGeodeStochasticStochasticException::check_exception( + rel_error < targets.tolerance( term_uuid ), nullptr, + OpenGeodeException::TYPE::result, + "[StatisticsValidator] Failure \n --> term ", + model.term_name( term_uuid ), "\n mean = ", mean, + "\n target = ", target, "\n error = ", rel_error, + "\n tol = ", targets.tolerance( term_uuid ) ); } + } - template < typename ObjectType > - double quadratic_loss( const StatisticsTracker< ObjectType >& tracker, - const TargetStatistics< ObjectType >& targets ) - { - double loss = 0.0; - - for( const auto& term_uuid : targets.active_terms() ) - { - const auto diff = - tracker.mean( term_uuid ) - targets.value( term_uuid ); + template < typename ObjectType > + double quadratic_loss( const StatisticsTracker< ObjectType >& tracker, + const TargetStatistics< ObjectType >& targets ) + { + double loss = 0.0; - loss += diff * diff; - } + for( const auto& term_uuid : targets.active_terms() ) + { + const auto diff = + tracker.mean( term_uuid ) - targets.value( term_uuid ); - return loss; + loss += diff * diff; } - } // namespace statistics -} // namespace geode \ No newline at end of file + + return loss; + } +} // namespace geode::statistics diff --git a/src/geode/stochastic/models/energy_terms/energy_term_builder.cpp b/src/geode/stochastic/models/energy_terms/energy_term_builder.cpp index 52ff4a0..4f9e157 100644 --- a/src/geode/stochastic/models/energy_terms/energy_term_builder.cpp +++ b/src/geode/stochastic/models/energy_terms/energy_term_builder.cpp @@ -20,7 +20,6 @@ * SOFTWARE. * */ -#pragma once #include diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 9ce9be5..127c7b4 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -34,6 +34,8 @@ #include namespace { + // NOLINTBEGIN(readability-magic-numbers) + struct SetDescription { std::string name; @@ -48,8 +50,9 @@ namespace : public geode::SimulationRunner< geode::Point2D > { public: - PoissonSimulationRunner( const geode::SpatialDomain< 2 >& domain ) - : geode::SimulationRunner< geode::Point2D >( domain ) {}; + explicit PoissonSimulationRunner( + const geode::SpatialDomain< 2 >& domain ) + : geode::SimulationRunner< geode::Point2D >( domain ){}; void add_set_descriptor( const SetDescription& descriptor ) { @@ -94,7 +97,7 @@ namespace geode::ModelConfig config; for( const auto& energy_desc : density_descriptors_ ) { - config.terms.push_back( energy_desc ); + config.terms.emplace_back( energy_desc ); } model_ = std::move( geode::build_model< geode::Point2D >( @@ -147,25 +150,25 @@ namespace for( const auto config : geode::Range{ birth_ratio.size() } ) { // --- Set description - SetDescription setA; - setA.name = "A"; - setA.birth_ratio = birth_ratio[config]; - setA.death_ratio = 1.0; - setA.change_ratio = change_ratio[config]; + SetDescription set_a; + set_a.name = "A"; + set_a.birth_ratio = birth_ratio[config]; + set_a.death_ratio = 1.0; + set_a.change_ratio = change_ratio[config]; // --- Energy term description - PoissonDensityDescription densityA; - densityA.term_name = "density"; - densityA.object_set_names = { "A" }; - densityA.lambda = 0.3; - densityA.object_feature = geode::ObjectInDomainFeatureConfig{}; + PoissonDensityDescription density_a; + density_a.term_name = "density"; + density_a.object_set_names = { "A" }; + density_a.lambda = 0.3; + density_a.object_feature = geode::ObjectInDomainFeatureConfig{}; - geode::TargetStatisticConfig statA{ "density", 30.0, 0.15 }; + geode::TargetStatisticConfig stat_a{ "density", 30.0, 0.15 }; PoissonSimulationRunner runner( domain ); - runner.add_set_descriptor( setA ); - runner.add_density_descriptor( densityA ); - runner.add_target_statistics( statA ); + runner.add_set_descriptor( set_a ); + runner.add_density_descriptor( density_a ); + runner.add_target_statistics( stat_a ); runner.initialize(); // run simulation @@ -278,4 +281,5 @@ int main() { return geode::geode_lippincott(); } + // NOLINTEND(readability-magic-numbers) } \ No newline at end of file diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index bfc3830..8ffe106 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -34,6 +34,8 @@ namespace { + // NOLINTBEGIN(readability-magic-numbers) + struct SetDescription { std::string name; @@ -101,11 +103,11 @@ namespace geode::ModelConfig config; for( const auto& energy_desc : density_descriptors_ ) { - config.terms.push_back( energy_desc ); + config.terms.emplace_back( energy_desc ); } for( const auto& interaction_desc : interaction_descriptors_ ) { - config.terms.push_back( interaction_desc ); + config.terms.emplace_back( interaction_desc ); } model_ = std::move( geode::build_model< geode::Point2D >( @@ -149,7 +151,6 @@ namespace geode::RandomEngine engine; engine.set_seed( "@mh-test-single-STRAUSS@" ); - geode::BoundingBox2D box; box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); box.add_point( geode::Point2D{ { 10.0, 10.0 } } ); @@ -158,45 +159,45 @@ namespace std::array< double, 5 > gamma_values{ 0, 0.3, 0.5, 0.7, 1.0 }; std::array< double, 5 > nb_points{ 19.5, 24.4, 31.3, 36.1, 50. }; std::array< double, 5 > nb_interactions{ 0, 4.7, 9.8, 18.7, 50.3 }; - for( const auto config : geode::Range{ gamma_values.size() } ) { // --- Object set - SetDescription setA; - setA.name = "A"; - setA.birth_ratio = 1.0; - setA.death_ratio = 1.0; - setA.change_ratio = 1.0; + SetDescription set_a; + set_a.name = "A"; + set_a.birth_ratio = 1.0; + set_a.death_ratio = 1.0; + set_a.change_ratio = 1.0; // --- Energy term description - PoissonDensityDescription densityA; - densityA.term_name = "densityA"; - densityA.object_set_names = { "A" }; - densityA.lambda = 0.5; - densityA.object_feature = geode::ObjectInDomainFeatureConfig{}; + PoissonDensityDescription density_a; + density_a.term_name = "density_a"; + density_a.object_set_names = { "A" }; + density_a.lambda = 0.5; + density_a.object_feature = geode::ObjectInDomainFeatureConfig{}; - geode::TargetStatisticConfig statA{ "densityA", nb_points[config], + geode::TargetStatisticConfig stat_a{ "density_a", nb_points[config], 0.1 }; // --- Intra-set pairwise interaction (Strauss process) - PairwiseInteractionDescription intraA; - intraA.term_name = "interactionA"; - intraA.object_set_names_interactions = { { "A", "A" } }; - intraA.gamma = gamma_values[config]; + PairwiseInteractionDescription interaction_a; + interaction_a.term_name = "interactionA"; + interaction_a.object_set_names_interactions = { { "A", "A" } }; + interaction_a.gamma = gamma_values[config]; - intraA.interaction_config = geode::MinimalDistanceCutoffConfig{ - 1. - }; /// ici cela devrait etre un paramètre utilisateur + interaction_a.interaction_config = + geode::MinimalDistanceCutoffConfig{ + 1. + }; /// ici cela devrait etre un paramètre utilisateur - geode::TargetStatisticConfig stat_intra_A{ "interactionA", + geode::TargetStatisticConfig stat_intra_a{ "interactionA", nb_interactions[config], 0.1 }; StraussSimulationRunner runner( domain ); - runner.add_set_descriptor( setA ); - runner.add_density_descriptor( densityA ); - runner.add_interaction_descriptor( intraA ); - runner.add_target_statistics( statA ); - runner.add_target_statistics( stat_intra_A ); + runner.add_set_descriptor( set_a ); + runner.add_density_descriptor( density_a ); + runner.add_interaction_descriptor( interaction_a ); + runner.add_target_statistics( stat_a ); + runner.add_target_statistics( stat_intra_a ); runner.initialize(); // run simulation @@ -356,4 +357,5 @@ int main() { return geode::geode_lippincott(); } + // NOLINTEND(readability-magic-numbers) } \ No newline at end of file From 8a30a3f64db6a511ff32c50e8bf561ca4b585dab Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Tue, 26 May 2026 16:12:12 +0000 Subject: [PATCH 22/59] 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 127c7b4..2ce08d7 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: explicit PoissonSimulationRunner( const geode::SpatialDomain< 2 >& domain ) - : geode::SimulationRunner< geode::Point2D >( domain ){}; + : geode::SimulationRunner< geode::Point2D >( domain ) {}; void add_set_descriptor( const SetDescription& descriptor ) { From ddab9524014e5652339b54d7f51a01dba3383f93 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 26 May 2026 18:23:11 +0200 Subject: [PATCH 23/59] tidy01 --- tests/stochastic/sampling/mcmc/test-mh-poisson.cpp | 7 +++---- tests/stochastic/sampling/mcmc/test-mh-strauss.cpp | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 2ce08d7..2662683 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -34,8 +34,7 @@ #include namespace { - // NOLINTBEGIN(readability-magic-numbers) - + // NOLINT(*-magic-numbers) struct SetDescription { std::string name; @@ -52,7 +51,7 @@ namespace public: explicit PoissonSimulationRunner( const geode::SpatialDomain< 2 >& domain ) - : geode::SimulationRunner< geode::Point2D >( domain ) {}; + : geode::SimulationRunner< geode::Point2D >( domain ){}; void add_set_descriptor( const SetDescription& descriptor ) { @@ -281,5 +280,5 @@ int main() { return geode::geode_lippincott(); } - // NOLINTEND(readability-magic-numbers) + // NOLINTEND(*-magic-numbers) } \ No newline at end of file diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index 8ffe106..17791d5 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -34,8 +34,7 @@ namespace { - // NOLINTBEGIN(readability-magic-numbers) - + // NOLINT(*-magic-numbers) struct SetDescription { std::string name; @@ -357,5 +356,5 @@ int main() { return geode::geode_lippincott(); } - // NOLINTEND(readability-magic-numbers) + // NOLINTEND(*-magic-numbers) } \ No newline at end of file From 2e072ddc9ba38c55ca4201827a81efa323d828ca Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Tue, 26 May 2026 16:23:46 +0000 Subject: [PATCH 24/59] 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 2662683..5de527e 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -51,7 +51,7 @@ namespace public: explicit PoissonSimulationRunner( const geode::SpatialDomain< 2 >& domain ) - : geode::SimulationRunner< geode::Point2D >( domain ){}; + : geode::SimulationRunner< geode::Point2D >( domain ) {}; void add_set_descriptor( const SetDescription& descriptor ) { From c9128a979338e78c4e85d7a4d65b89407fa350b1 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 26 May 2026 19:07:24 +0200 Subject: [PATCH 25/59] tidy02 --- tests/stochastic/sampling/mcmc/test-mh-poisson.cpp | 10 +++++++--- tests/stochastic/sampling/mcmc/test-mh-strauss.cpp | 12 ++++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 5de527e..1f69604 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -34,7 +34,6 @@ #include namespace { - // NOLINT(*-magic-numbers) struct SetDescription { std::string name; @@ -138,6 +137,7 @@ namespace geode::RandomEngine engine; engine.set_seed( "@mh-test-single-POISSON@" ); + // NOLINT(*-magic-numbers) geode::BoundingBox2D box; box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); box.add_point( geode::Point2D{ { 10.0, 10.0 } } ); @@ -145,9 +145,11 @@ namespace std::array< double, 4 > birth_ratio{ 0.1, 0.5, 2., 4. }; std::array< double, 4 > change_ratio{ 0., 1., 1., 0. }; - + // NOLINTEND(*-magic-numbers) for( const auto config : geode::Range{ birth_ratio.size() } ) { + // NOLINT(*-magic-numbers) + // --- Set description SetDescription set_a; set_a.name = "A"; @@ -181,6 +183,7 @@ namespace sim_config.metropolis_hasting_steps = 1000; sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; + // NOLINTEND(*-magic-numbers) auto statistic_tracker = runner.run( engine, sim_config ); geode::statistics::validate( @@ -196,6 +199,7 @@ namespace geode::RandomEngine engine; engine.set_seed( "@mh-test-POISSON-multi@" ); + // NOLINT(*-magic-numbers) geode::BoundingBox2D box; box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); @@ -257,6 +261,7 @@ namespace sim_config.metropolis_hasting_steps = 1000; sim_config.burn_in_steps = 3000; sim_config.printer = printer_config; + // NOLINTEND(*-magic-numbers) auto statistic_tracker = runner.run( engine, sim_config ); geode::statistics::validate( @@ -280,5 +285,4 @@ int main() { return geode::geode_lippincott(); } - // NOLINTEND(*-magic-numbers) } \ No newline at end of file diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index 17791d5..57499bb 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -34,7 +34,6 @@ namespace { - // NOLINT(*-magic-numbers) struct SetDescription { std::string name; @@ -150,6 +149,7 @@ namespace geode::RandomEngine engine; engine.set_seed( "@mh-test-single-STRAUSS@" ); + // NOLINT(*-magic-numbers) geode::BoundingBox2D box; box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); box.add_point( geode::Point2D{ { 10.0, 10.0 } } ); @@ -158,8 +158,10 @@ namespace std::array< double, 5 > gamma_values{ 0, 0.3, 0.5, 0.7, 1.0 }; std::array< double, 5 > nb_points{ 19.5, 24.4, 31.3, 36.1, 50. }; std::array< double, 5 > nb_interactions{ 0, 4.7, 9.8, 18.7, 50.3 }; + // NOLINTEND(*-magic-numbers) for( const auto config : geode::Range{ gamma_values.size() } ) { + // NOLINT(*-magic-numbers) // --- Object set SetDescription set_a; set_a.name = "A"; @@ -210,6 +212,7 @@ namespace sim_config.metropolis_hasting_steps = 1000; sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; + // NOLINTEND(*-magic-numbers) auto statistic_tracker = runner.run( engine, sim_config ); geode::statistics::validate( @@ -226,7 +229,7 @@ namespace geode::RandomEngine engine; engine.set_seed( "@mh-test-multi-STRAUSS@" ); - + // NOLINT(*-magic-numbers) geode::BoundingBox2D box; box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); box.add_point( geode::Point2D{ { 10.0, 10.0 } } ); @@ -238,9 +241,10 @@ namespace std::array< double, 3 > nb_points03{ 14.6, 19.4, 30. }; std::array< double, 3 > nb_interactions01{ 0, 15, 59.8 }; std::array< double, 3 > nb_interactions02{ 37.2, 70, 174 }; - + // NOLINTEND(*-magic-numbers) for( const auto config : geode::Range{ gamma_values.size() } ) { + // NOLINT(*-magic-numbers) // --- Sets SetDescription set01{ "set01", 1.0, 3.0, 1.0 }; SetDescription set02{ "set02", 3.0, 0.5, 1.0 }; @@ -332,6 +336,7 @@ namespace sim_config.metropolis_hasting_steps = 1000; sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; + // NOLINTEND(*-magic-numbers) auto statistic_tracker = runner.run( engine, sim_config ); geode::statistics::validate( @@ -356,5 +361,4 @@ int main() { return geode::geode_lippincott(); } - // NOLINTEND(*-magic-numbers) } \ No newline at end of file From 405e0e49fb82cdfd5e4cae7b9a13a01441e2f0c0 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 26 May 2026 19:40:10 +0200 Subject: [PATCH 26/59] tidy 04 --- tests/stochastic/sampling/mcmc/test-mh-poisson.cpp | 11 ++++------- tests/stochastic/sampling/mcmc/test-mh-strauss.cpp | 13 ++++--------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 1f69604..973fb16 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: explicit PoissonSimulationRunner( const geode::SpatialDomain< 2 >& domain ) - : geode::SimulationRunner< geode::Point2D >( domain ) {}; + : geode::SimulationRunner< geode::Point2D >( domain ){}; void add_set_descriptor( const SetDescription& descriptor ) { @@ -137,7 +137,7 @@ namespace geode::RandomEngine engine; engine.set_seed( "@mh-test-single-POISSON@" ); - // NOLINT(*-magic-numbers) + // NOLINTBEGIN(*-magic-numbers) geode::BoundingBox2D box; box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); box.add_point( geode::Point2D{ { 10.0, 10.0 } } ); @@ -145,11 +145,8 @@ namespace std::array< double, 4 > birth_ratio{ 0.1, 0.5, 2., 4. }; std::array< double, 4 > change_ratio{ 0., 1., 1., 0. }; - // NOLINTEND(*-magic-numbers) for( const auto config : geode::Range{ birth_ratio.size() } ) { - // NOLINT(*-magic-numbers) - // --- Set description SetDescription set_a; set_a.name = "A"; @@ -183,12 +180,12 @@ namespace sim_config.metropolis_hasting_steps = 1000; sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; - // NOLINTEND(*-magic-numbers) auto statistic_tracker = runner.run( engine, sim_config ); geode::statistics::validate( statistic_tracker, runner.target_statistics() ); } + // NOLINTEND(*-magic-numbers) geode::Logger::info( "--> SUCCESS!" ); } @@ -199,7 +196,7 @@ namespace geode::RandomEngine engine; engine.set_seed( "@mh-test-POISSON-multi@" ); - // NOLINT(*-magic-numbers) + // NOLINTBEGIN(*-magic-numbers) geode::BoundingBox2D box; box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index 57499bb..06d0702 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -149,7 +149,7 @@ namespace geode::RandomEngine engine; engine.set_seed( "@mh-test-single-STRAUSS@" ); - // NOLINT(*-magic-numbers) + // NOLINTBEGIN(*-magic-numbers) geode::BoundingBox2D box; box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); box.add_point( geode::Point2D{ { 10.0, 10.0 } } ); @@ -158,10 +158,8 @@ namespace std::array< double, 5 > gamma_values{ 0, 0.3, 0.5, 0.7, 1.0 }; std::array< double, 5 > nb_points{ 19.5, 24.4, 31.3, 36.1, 50. }; std::array< double, 5 > nb_interactions{ 0, 4.7, 9.8, 18.7, 50.3 }; - // NOLINTEND(*-magic-numbers) for( const auto config : geode::Range{ gamma_values.size() } ) { - // NOLINT(*-magic-numbers) // --- Object set SetDescription set_a; set_a.name = "A"; @@ -212,13 +210,12 @@ namespace sim_config.metropolis_hasting_steps = 1000; sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; - // NOLINTEND(*-magic-numbers) auto statistic_tracker = runner.run( engine, sim_config ); geode::statistics::validate( statistic_tracker, runner.target_statistics() ); } - + // NOLINTEND(*-magic-numbers) geode::Logger::info( "--> SUCCESS!" ); } @@ -229,7 +226,7 @@ namespace geode::RandomEngine engine; engine.set_seed( "@mh-test-multi-STRAUSS@" ); - // NOLINT(*-magic-numbers) + // NOLINTBEGIN(*-magic-numbers) geode::BoundingBox2D box; box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); box.add_point( geode::Point2D{ { 10.0, 10.0 } } ); @@ -241,10 +238,8 @@ namespace std::array< double, 3 > nb_points03{ 14.6, 19.4, 30. }; std::array< double, 3 > nb_interactions01{ 0, 15, 59.8 }; std::array< double, 3 > nb_interactions02{ 37.2, 70, 174 }; - // NOLINTEND(*-magic-numbers) for( const auto config : geode::Range{ gamma_values.size() } ) { - // NOLINT(*-magic-numbers) // --- Sets SetDescription set01{ "set01", 1.0, 3.0, 1.0 }; SetDescription set02{ "set02", 3.0, 0.5, 1.0 }; @@ -336,12 +331,12 @@ namespace sim_config.metropolis_hasting_steps = 1000; sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; - // NOLINTEND(*-magic-numbers) auto statistic_tracker = runner.run( engine, sim_config ); geode::statistics::validate( statistic_tracker, runner.target_statistics() ); } + // NOLINTEND(*-magic-numbers) geode::Logger::info( "--> SUCCESS!" ); } From d43652002e6c742954e54be74c2f85863785bfac Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Tue, 26 May 2026 17:40:44 +0000 Subject: [PATCH 27/59] 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 973fb16..bdd5932 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: explicit PoissonSimulationRunner( const geode::SpatialDomain< 2 >& domain ) - : geode::SimulationRunner< geode::Point2D >( domain ){}; + : geode::SimulationRunner< geode::Point2D >( domain ) {}; void add_set_descriptor( const SetDescription& descriptor ) { From 0a326988a5dc76ac2f403d322a26e0750d33b66b Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 26 May 2026 19:57:38 +0200 Subject: [PATCH 28/59] tidy05 --- tests/stochastic/sampling/mcmc/test-mh-strauss.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index 06d0702..d41664e 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -49,7 +49,8 @@ namespace : public geode::SimulationRunner< geode::Point2D > { public: - StraussSimulationRunner( const geode::SpatialDomain< 2 >& domain ) + explicit StraussSimulationRunner( + const geode::SpatialDomain< 2 >& domain ) : geode::SimulationRunner< geode::Point2D >( domain ) { } From a813370efbcd95e9b073e5ef54c6d5e354a49d84 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 26 May 2026 19:58:30 +0200 Subject: [PATCH 29/59] reduce nb iteration --- tests/stochastic/sampling/mcmc/test-mh-strauss.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index d41664e..8f53855 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -328,7 +328,7 @@ namespace "/sim_point_multitype_strauss_test" ); geode::SimulationConfigurator sim_config; - sim_config.realizations = 750; + sim_config.realizations = 500; sim_config.metropolis_hasting_steps = 1000; sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; From 4c2ec7624d43e3b6e132357f184dfe546d10a3aa Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 26 May 2026 20:14:43 +0200 Subject: [PATCH 30/59] modif param --- tests/stochastic/sampling/mcmc/test-mh-strauss.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index 8f53855..5f09044 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -328,9 +328,9 @@ namespace "/sim_point_multitype_strauss_test" ); geode::SimulationConfigurator sim_config; - sim_config.realizations = 500; - sim_config.metropolis_hasting_steps = 1000; - sim_config.burn_in_steps = 1000; + sim_config.realizations = 600; + sim_config.metropolis_hasting_steps = 750; + sim_config.burn_in_steps = 5000; sim_config.printer = printer_config; auto statistic_tracker = runner.run( engine, sim_config ); From 683ebbaf6593c209e2aff30ff905d8e705b031cb Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 27 May 2026 10:20:54 +0200 Subject: [PATCH 31/59] avoid generic capture in lambdas --- .../stochastic/models/energy_terms/energy_term_builder.hpp | 2 +- .../stochastic/models/energy_terms/single_object_term.hpp | 6 +++--- .../pairwise_interactions/pairwise_interactions_builder.hpp | 2 +- .../single_object_feature_builder.hpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) 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 c3b1503..86aac82 100644 --- a/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp @@ -135,7 +135,7 @@ namespace geode const SpatialDomain< ObjectType::dim >& domain ) { return std::visit( - [&]( const auto& term_cfg ) + [&object_sets, &domain]( const auto& term_cfg ) -> std::unique_ptr< EnergyTerm< ObjectType > > { return build_energy_term_impl< ObjectType >( term_cfg, object_sets, domain ); 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 ae00091..fab3286 100644 --- a/include/geode/stochastic/models/energy_terms/single_object_term.hpp +++ b/include/geode/stochastic/models/energy_terms/single_object_term.hpp @@ -95,10 +95,10 @@ namespace geode const ObjectSets< ObjectType >& state ) const override { double sum = 0.0; - this->for_each_object_in_sets( - state, this->impacted_set_ids(), [&]( const ObjectId& obj_id ) { + this->for_each_object_in_sets( state, this->impacted_set_ids(), + [&state, &sum, this]( const ObjectId& obj_id ) { const auto& obj = state.get_object( obj_id ); - sum += feature_->evaluate( obj, this->domain() ); + sum += this->feature_->evaluate( obj, this->domain() ); } ); return sum; } 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 04a6fbf..d0e497e 100644 --- a/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp +++ b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp @@ -68,7 +68,7 @@ namespace geode build_pairwise_interaction( const PairwiseInteractionConfig& cfg ) { return std::visit( - [&]( auto&& interaction_cfg ) + []( auto&& interaction_cfg ) -> std::unique_ptr< PairwiseInteraction< ObjectType > > { return build_pairwise_interaction_impl< ObjectType >( interaction_cfg ); 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 edd3c88..683fa3b 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 @@ -80,7 +80,7 @@ namespace geode build_single_object_feature( const SingleObjectFeatureConfig& cfg ) { return std::visit( - [&]( auto&& interaction_cfg ) + []( auto&& interaction_cfg ) -> std::unique_ptr< SingleObjectFeature< ObjectType > > { return build_single_object_feature_impl< ObjectType >( interaction_cfg ); From 60b8dca045919990406440da5951ef3646b6de3b Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 27 May 2026 11:07:10 +0200 Subject: [PATCH 32/59] tidy 05 --- .../models/energy_terms/energy_term_builder.hpp | 12 ++++++------ .../models/energy_terms/pairwise_term.hpp | 2 +- .../pairwise_interactions_builder.hpp | 6 ++++-- .../single_object_feature_builder.hpp | 13 +++++++++---- 4 files changed, 20 insertions(+), 13 deletions(-) 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 86aac82..7026f3f 100644 --- a/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp @@ -108,9 +108,9 @@ namespace geode template < typename ObjectType, typename NewEnergyTypeConfig > std::unique_ptr< EnergyTerm< ObjectType > > build_energy_term_impl( - const NewEnergyTypeConfig&, - const ObjectSets< ObjectType >&, - const SpatialDomain< ObjectType::dim >& ) + const NewEnergyTypeConfig& /*unused*/, + const ObjectSets< ObjectType >& /*unused*/, + const SpatialDomain< ObjectType::dim >& /*unused*/ ) { static_assert( sizeof( NewEnergyTypeConfig ) == 0, "Unsupported EnergyTermConfig type" ); @@ -119,9 +119,9 @@ namespace geode template < typename ObjectType > std::unique_ptr< EnergyTerm< ObjectType > > build_energy_term_impl( - const std::monostate&, - const ObjectSets< ObjectType >&, - const SpatialDomain< ObjectType::dim >& ) + const std::monostate& /*unused*/, + const ObjectSets< ObjectType >& /*unused*/, + const SpatialDomain< ObjectType::dim >& /*unused*/ ) { throw OpenGeodeStochasticStochasticException{ nullptr, OpenGeodeException::TYPE::data, diff --git a/include/geode/stochastic/models/energy_terms/pairwise_term.hpp b/include/geode/stochastic/models/energy_terms/pairwise_term.hpp index 75fc792..4d3bf8c 100644 --- a/include/geode/stochastic/models/energy_terms/pairwise_term.hpp +++ b/include/geode/stochastic/models/energy_terms/pairwise_term.hpp @@ -106,7 +106,7 @@ namespace geode { double sum = 0.0; this->for_each_object_in_sets( state, this->impacted_set_ids(), - [&]( const ObjectId& cur_obj_id ) { + [&state]( const ObjectId& cur_obj_id ) { sum += accumulate_interactions_with_neighbors( cur_obj_id, state ); } ); 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 d0e497e..fc0ec2f 100644 --- a/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp +++ b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp @@ -21,6 +21,7 @@ * */ +#include #include #include @@ -47,7 +48,8 @@ namespace geode template < typename ObjectType, typename NewInteractionConfig > std::unique_ptr< PairwiseInteraction< ObjectType > > - build_pairwise_interaction_impl( const NewInteractionConfig& ) + build_pairwise_interaction_impl( + const NewInteractionConfig& /*unused*/ ) { static_assert( sizeof( NewInteractionConfig ) == 0, "Unsupported PairwiseInteractionConfig type" ); @@ -56,7 +58,7 @@ namespace geode template < typename ObjectType > std::unique_ptr< PairwiseInteraction< ObjectType > > - build_pairwise_interaction_impl( const std::monostate& ) + build_pairwise_interaction_impl( const std::monostate& /*unused*/ ) { throw OpenGeodeStochasticStochasticException{ nullptr, OpenGeodeException::TYPE::data, 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 683fa3b..54cb1bb 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 @@ -24,6 +24,8 @@ #include +#include + #include #include @@ -52,22 +54,25 @@ namespace geode { throw OpenGeodeStochasticStochasticException{ nullptr, OpenGeodeException::TYPE::data, - "SegmentLengthInsideBoxFeature not valid for this ObjectType" }; + "[SingleObjectFeatureBuilder] SegmentLengthInsideBoxFeature " + "not valid for this ObjectType" }; } } template < typename ObjectType, typename NewObjectFeatureConfig > std::unique_ptr< SingleObjectFeature< ObjectType > > - build_single_object_feature_impl( const NewObjectFeatureConfig& ) + build_single_object_feature_impl( + const NewObjectFeatureConfig& /*unused*/ ) { static_assert( sizeof( NewObjectFeatureConfig ) == 0, - "Unsupported SingleObjectFeatureConfig type" ); + "[SingleObjectFeatureBuilder] Unsupported " + "SingleObjectFeatureConfig type" ); return nullptr; } template < typename ObjectType > std::unique_ptr< SingleObjectFeature< ObjectType > > - build_single_object_feature_impl( const std::monostate& ) + build_single_object_feature_impl( const std::monostate& /*unused*/ ) { throw OpenGeodeStochasticStochasticException{ nullptr, OpenGeodeException::TYPE::data, From 40339c3693689d76f6d4064d0f01667b8d977761 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 27 May 2026 11:22:38 +0200 Subject: [PATCH 33/59] tidy06 --- .../stochastic/models/energy_terms/energy_term_builder.hpp | 2 ++ include/geode/stochastic/models/energy_terms/pairwise_term.hpp | 2 +- .../pairwise_interactions/pairwise_interactions_builder.hpp | 3 ++- .../single_object_features/single_object_feature_builder.hpp | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) 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 7026f3f..119f01c 100644 --- a/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp +++ b/include/geode/stochastic/models/energy_terms/energy_term_builder.hpp @@ -30,6 +30,8 @@ #include #include +#include + #include #include #include diff --git a/include/geode/stochastic/models/energy_terms/pairwise_term.hpp b/include/geode/stochastic/models/energy_terms/pairwise_term.hpp index 4d3bf8c..ce0658f 100644 --- a/include/geode/stochastic/models/energy_terms/pairwise_term.hpp +++ b/include/geode/stochastic/models/energy_terms/pairwise_term.hpp @@ -106,7 +106,7 @@ namespace geode { double sum = 0.0; this->for_each_object_in_sets( state, this->impacted_set_ids(), - [&state]( const ObjectId& cur_obj_id ) { + [&sum, &state]( const ObjectId& cur_obj_id ) { sum += accumulate_interactions_with_neighbors( cur_obj_id, state ); } ); 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 fc0ec2f..4421e0f 100644 --- a/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp +++ b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp @@ -21,7 +21,8 @@ * */ -#include +#include + #include #include 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 54cb1bb..97c954d 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 @@ -24,7 +24,7 @@ #include -#include +#include #include #include From 94c997a7e81e9526f36677df90817adfa6595784 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 27 May 2026 11:37:05 +0200 Subject: [PATCH 34/59] tidy07 --- .../geode/stochastic/models/energy_terms/pairwise_term.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/geode/stochastic/models/energy_terms/pairwise_term.hpp b/include/geode/stochastic/models/energy_terms/pairwise_term.hpp index ce0658f..c4feabb 100644 --- a/include/geode/stochastic/models/energy_terms/pairwise_term.hpp +++ b/include/geode/stochastic/models/energy_terms/pairwise_term.hpp @@ -106,8 +106,8 @@ namespace geode { double sum = 0.0; this->for_each_object_in_sets( state, this->impacted_set_ids(), - [&sum, &state]( const ObjectId& cur_obj_id ) { - sum += accumulate_interactions_with_neighbors( + [&sum, &state, this]( const ObjectId& cur_obj_id ) { + sum += this->accumulate_interactions_with_neighbors( cur_obj_id, state ); } ); return sum; From dbf2fdf6ea51c75e0fcdec5ad435032b6a200471 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 27 May 2026 18:46:15 +0200 Subject: [PATCH 35/59] feat(SimulationContext): remove inheritance in simulation runner --- include/geode/stochastic/models/model.hpp | 5 + .../mcmc/helpers/simulation_context.hpp | 73 ++++++++++ .../sampling/mcmc/simulation_runner.hpp | 52 +++---- .../stochastic/spatial/spatial_domain.hpp | 36 ++++- src/geode/stochastic/CMakeLists.txt | 1 + tests/stochastic/CMakeLists.txt | 37 ++--- .../sampling/mcmc/test-mh-poisson-new.cpp | 81 ----------- .../sampling/mcmc/test-mh-poisson.cpp | 132 ++++++++++-------- 8 files changed, 224 insertions(+), 193 deletions(-) create mode 100644 include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp delete mode 100644 tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp diff --git a/include/geode/stochastic/models/model.hpp b/include/geode/stochastic/models/model.hpp index 0aa2dc2..70c3289 100644 --- a/include/geode/stochastic/models/model.hpp +++ b/include/geode/stochastic/models/model.hpp @@ -118,6 +118,11 @@ namespace geode return names; } + [[nodiscard]] std::string string() const + { + return terms_collection_.string(); + } + private: EnergyTermCollection< ObjectType > terms_collection_; GibbsEnergy< ObjectType > energy_; diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp new file mode 100644 index 0000000..8ca3ce1 --- /dev/null +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp @@ -0,0 +1,73 @@ +/* + * 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 + +#include + +#include +#include + +#include + +namespace geode +{ + template < typename ObjectType > + struct SimulationContext + { + explicit SimulationContext( SpatialDomain< ObjectType::dim > domain_in ) + : domain( std::move( domain_in ) ) + { + } + + std::string string() const + { + auto message = std::string{ "SimulationContext: " }; + absl::StrAppend( &message, "\n\t --> ", domain.string() ); + absl::StrAppend( &message, "\n\t --> ", object_sets.string() ); + absl::StrAppend( + &message, "\n\t --> ", set_samplers.size(), " Sets samplers " ); + absl::StrAppend( &message, "\n\t --> ", model->string() ); + // absl::StrAppend( &message, "\n\t --> ", mh_sampler_ > string() ); + + return message; + } + + SpatialDomain< ObjectType::dim > domain; + + ObjectSets< ObjectType > object_sets; + std::vector< std::unique_ptr< geode::ObjectSetSampler< ObjectType > > > + set_samplers; + std::unique_ptr< Model< ObjectType > > model; + std::unique_ptr< geode::MetropolisHastings< ObjectType > > mh_sampler; + + std::optional< TargetStatistics< ObjectType > > target_statistics; + }; + +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index 74eb77a..d80e659 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -23,13 +23,9 @@ #pragma once #include -#include -#include - -#include #include -#include -#include + +#include #include @@ -65,49 +61,51 @@ namespace geode class SimulationRunner { public: - SimulationRunner( const SpatialDomain< ObjectType::dim >& domain ) - : domain_( domain ) {}; + SimulationRunner( SimulationContext< ObjectType >&& context ) + : context_( std::move( context ) ){}; virtual ~SimulationRunner() = default; - virtual void initialize() = 0; - const ObjectSets< ObjectType >& run( RandomEngine& engine, const index_t steps ) { - mh_sampler_->walk( object_sets_, engine, steps ); - return object_sets_; + context_.mh_sampler->walk( context_.object_sets, engine, steps ); + return context_.object_sets; } StatisticsTracker< ObjectType > run( RandomEngine& engine, const SimulationConfigurator& config ) { + Logger::info( context_.string() ); if( config.burn_in_steps > 0 ) { - mh_sampler_->walk( object_sets_, engine, config.burn_in_steps ); + context_.mh_sampler->walk( + context_.object_sets, engine, config.burn_in_steps ); } // Initialize monitoring - StatisticsTracker< ObjectType > stats_monitor( *model_ ); + StatisticsTracker< ObjectType > stats_monitor( *context_.model ); std::unique_ptr< SimulationPrinter< ObjectType > > printer; if( config.printer.has_value() ) { printer = std::make_unique< SimulationPrinter< ObjectType > >( - *model_, config.printer.value() ); + *context_.model, config.printer.value() ); } for( const auto realization : Range{ config.realizations } ) { - mh_sampler_->walk( - object_sets_, engine, config.metropolis_hasting_steps ); + context_.mh_sampler->walk( context_.object_sets, engine, + config.metropolis_hasting_steps ); - const auto stats = model_->compute_statistics( object_sets_ ); + const auto stats = + context_.model->compute_statistics( context_.object_sets ); stats_monitor.add_realization( stats ); if( printer ) { printer->print_statistics( stats ); - printer->print_object_sets( object_sets_, realization ); + printer->print_object_sets( + context_.object_sets, realization ); } } @@ -123,27 +121,19 @@ namespace geode target_statistics() const { OpenGeodeStochasticStochasticException::check_exception( - target_statistics_.has_value(), nullptr, + context_.target_statistics.has_value(), nullptr, OpenGeodeException::TYPE::data, "[SimulationRunner] Target statistics not initialized" ); - return *target_statistics_; + return *context_.target_statistics; } [[nodiscard]] const ObjectSets< ObjectType >& state_realization() const { - return object_sets_; + return context_.object_sets; } protected: - SpatialDomain< ObjectType::dim > domain_; - - ObjectSets< ObjectType > object_sets_; - std::vector< std::unique_ptr< geode::ObjectSetSampler< ObjectType > > > - set_samplers_; - std::unique_ptr< Model< ObjectType > > model_; - std::unique_ptr< geode::MetropolisHastings< ObjectType > > mh_sampler_; - - std::optional< TargetStatistics< ObjectType > > target_statistics_; + SimulationContext< ObjectType > context_; }; } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/spatial/spatial_domain.hpp b/include/geode/stochastic/spatial/spatial_domain.hpp index 584d2a2..3851e6a 100644 --- a/include/geode/stochastic/spatial/spatial_domain.hpp +++ b/include/geode/stochastic/spatial/spatial_domain.hpp @@ -33,11 +33,16 @@ namespace geode class SpatialDomain { public: - SpatialDomain( - const BoundingBox< dimension >& domain, double buffer_size ) + SpatialDomain( const SpatialDomain& ) = default; + SpatialDomain( SpatialDomain&& ) noexcept = default; + + SpatialDomain& operator=( const SpatialDomain& ) = default; + SpatialDomain& operator=( SpatialDomain&& ) noexcept = default; + + SpatialDomain( BoundingBox< dimension > domain, double buffer_size ) : domain_{ domain }, buffer_size_{ buffer_size }, - extended_domain_{ domain } + extended_domain_{ domain_ } { auto volume = domain_.n_volume(); OpenGeodeStochasticStochasticException::check_exception( @@ -53,6 +58,7 @@ namespace geode { extended_domain_.extends( buffer_size_ ); } + Logger::info( domain_.string(), " ", extended_domain_.string() ); } const BoundingBox< dimension > box() const @@ -90,6 +96,13 @@ namespace geode return extended_domain_; } + std::string string() const + { + return absl::StrCat( "Spatial Domain --> center ", domain_.string(), + " extended: ", extended_domain_.string(), + " buffer: ", buffer_size_ ); + } + private: BoundingBox< dimension > domain_; @@ -132,4 +145,21 @@ namespace geode return domain.box().intersects( seg ); } }; + + template < index_t dimension > + struct SpatialDomainConfig + { + Point< dimension > min_point; + Point< dimension > max_point; + double buffer_size{ 0.0 }; + }; + + template < index_t dimension > + SpatialDomain< dimension > build_spatial_domain( + const SpatialDomainConfig< dimension >& config ) + { + BoundingBox< dimension > box{ config.min_point, config.max_point }; + Logger::info( box.string() ); + return SpatialDomain{ std::move( box ), config.buffer_size }; + } } // namespace geode \ No newline at end of file diff --git a/src/geode/stochastic/CMakeLists.txt b/src/geode/stochastic/CMakeLists.txt index c624bbf..1e1302e 100644 --- a/src/geode/stochastic/CMakeLists.txt +++ b/src/geode/stochastic/CMakeLists.txt @@ -64,6 +64,7 @@ add_geode_library( "sampling/direct/point_uniform_sampler.hpp" "sampling/direct/segment_uniform_sampler.hpp" #"sampling/mcmc/helpers/fracture_simulation_runner.hpp" + "sampling/mcmc/helpers/simulation_context.hpp" "sampling/mcmc/helpers/simulation_printer.hpp" "models/energy_terms/energy_term.hpp" "models/energy_terms/pairwise_term.hpp" diff --git a/tests/stochastic/CMakeLists.txt b/tests/stochastic/CMakeLists.txt index 10cbb1e..a8b6661 100644 --- a/tests/stochastic/CMakeLists.txt +++ b/tests/stochastic/CMakeLists.txt @@ -161,28 +161,21 @@ add_geode_test( # ${PROJECT_NAME}::stochastic #) -add_geode_test( - SOURCE "sampling/mcmc/test-mh-poisson.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) -add_geode_test( - SOURCE "sampling/mcmc/test-mh-poisson-new.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) - -add_geode_test( - SOURCE "sampling/mcmc/test-mh-strauss.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) + add_geode_test( + SOURCE "sampling/mcmc/test-mh-poisson.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic + ) + +# add_geode_test( +# SOURCE "sampling/mcmc/test-mh-strauss.cpp" +# DEPENDENCIES +# OpenGeode::basic +# OpenGeode::geometry +# ${PROJECT_NAME}::stochastic +# ) add_geode_test( diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp deleted file mode 100644 index f93af1d..0000000 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson-new.cpp +++ /dev/null @@ -1,81 +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 -int main() -{ - try - { - geode::OpenGeodeStochasticStochasticLibrary::initialize(); - geode::Logger::set_level( geode::Logger::LEVEL::debug ); - - std::string set_name1 = "set01"; - std::string set_name2 = "set02"; - std::string set_name3 = "set03"; - std::string set_name4 = "set04"; - - 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::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{ - "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 } } ); - box.add_point( geode::Point2D{ { 10.0, 10.0 } } ); - geode::SpatialDomain domain( box, 0. ); - - auto model = - geode::build_model< geode::Point2D >( config, object_sets, domain ); - geode::Logger::info( model->terms().size() ); - geode::OpenGeodeStochasticStochasticException::test( - model->terms().size() == 5, "Collection not created" ); - return 0; - } - catch( ... ) - { - return geode::geode_lippincott(); - } -} \ No newline at end of file diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index bdd5932..9f6051b 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -21,15 +21,21 @@ * */ #include + #include #include + #include #include #include + #include + +#include #include #include #include + #include #include namespace @@ -44,13 +50,19 @@ namespace using PoissonDensityDescription = geode::SingleObjectTermConfig; - class PoissonSimulationRunner - : public geode::SimulationRunner< geode::Point2D > + class PoissonConfig { public: - explicit PoissonSimulationRunner( - const geode::SpatialDomain< 2 >& domain ) - : geode::SimulationRunner< geode::Point2D >( domain ) {}; + PoissonConfig() = default; + + void add_domain_config( const geode::Point2D& min_p, + const geode::Point2D& max_p, + double buffer_size ) + { + domain_config_.min_point = min_p; + domain_config_.max_point = max_p; + domain_config_.buffer_size = buffer_size; + } void add_set_descriptor( const SetDescription& descriptor ) { @@ -69,28 +81,48 @@ namespace targeted_statistics_descriptors_.push_back( statistic_descriptor ); } + geode::SimulationContext< geode::Point2D > build() const + { + geode::SimulationContext< geode::Point2D > context{ + geode::build_spatial_domain( domain_config_ ) + }; + auto proposal_kernel = create_sets_and_set_samplers( context ); + create_model( context ); + + context.mh_sampler = + std::make_unique< geode::MetropolisHastings< geode::Point2D > >( + *context.model, std::move( proposal_kernel ) ); + create_target_statistics( context ); + return context; + } + + private: std::unique_ptr< geode::ProposalKernel< geode::Point2D > > - create_sets_and_set_samplers() + create_sets_and_set_samplers( + geode::SimulationContext< geode::Point2D >& context ) const { auto proposal_kernel = std::make_unique< geode::ProposalKernel< geode::Point2D > >(); for( const auto& set_desc : set_descriptors_ ) { - const auto set_id = this->object_sets_.add_set( set_desc.name ); + const auto set_id = + context.object_sets.add_set( set_desc.name ); - this->set_samplers_.push_back( + context.set_samplers.push_back( std::make_unique< geode::UniformPointSetSampler< 2 > >( - domain_ ) ); + context.domain ) ); - geode::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 ); + geode::add_birth_death_change_moves( + context.set_samplers.back(), *proposal_kernel, set_id, + set_desc.birth_ratio, set_desc.death_ratio, + set_desc.change_ratio ); } return proposal_kernel; } - void create_model() + void create_model( + geode::SimulationContext< geode::Point2D >& context ) const { geode::ModelConfig config; for( const auto& energy_desc : density_descriptors_ ) @@ -98,32 +130,22 @@ namespace config.terms.emplace_back( energy_desc ); } - model_ = std::move( geode::build_model< geode::Point2D >( - config, object_sets_, domain_ ) ); - create_target_statistics(); + context.model = std::move( geode::build_model< geode::Point2D >( + config, context.object_sets, context.domain ) ); } - void create_target_statistics() + void create_target_statistics( + geode::SimulationContext< geode::Point2D >& context ) const { - target_statistics_.emplace( *model_ ); + context.target_statistics.emplace( *context.model ); for( const auto& target_stat : targeted_statistics_descriptors_ ) { - target_statistics_->set_target( target_stat ); + context.target_statistics->set_target( target_stat ); } } - void initialize() override - { - auto proposal_kernel = create_sets_and_set_samplers(); - create_model(); - - this->mh_sampler_ = - std::make_unique< geode::MetropolisHastings< geode::Point2D > >( - *model_, std::move( proposal_kernel ) ); - create_target_statistics(); - } - private: + geode::SpatialDomainConfig< 2 > domain_config_; std::vector< SetDescription > set_descriptors_; std::vector< PoissonDensityDescription > density_descriptors_; std::vector< geode::TargetStatisticConfig > @@ -137,11 +159,10 @@ namespace geode::RandomEngine engine; engine.set_seed( "@mh-test-single-POISSON@" ); + PoissonConfig poisson_config; // NOLINTBEGIN(*-magic-numbers) - geode::BoundingBox2D box; - box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); - box.add_point( geode::Point2D{ { 10.0, 10.0 } } ); - geode::SpatialDomain domain( box, 0. ); + poisson_config.add_domain_config( geode::Point2D{ { 0.0, 0.0 } }, + geode::Point2D{ { 10.0, 10.0 } }, 0. ); std::array< double, 4 > birth_ratio{ 0.1, 0.5, 2., 4. }; std::array< double, 4 > change_ratio{ 0., 1., 1., 0. }; @@ -163,11 +184,12 @@ namespace geode::TargetStatisticConfig stat_a{ "density", 30.0, 0.15 }; - PoissonSimulationRunner runner( domain ); - runner.add_set_descriptor( set_a ); - runner.add_density_descriptor( density_a ); - runner.add_target_statistics( stat_a ); - runner.initialize(); + poisson_config.add_set_descriptor( set_a ); + poisson_config.add_density_descriptor( density_a ); + poisson_config.add_target_statistics( stat_a ); + + geode::SimulationRunner< geode::Point2D > runner( + poisson_config.build() ); // run simulation geode::SimulationPrinterConfigurator printer_config; @@ -196,12 +218,11 @@ namespace geode::RandomEngine engine; engine.set_seed( "@mh-test-POISSON-multi@" ); - // NOLINTBEGIN(*-magic-numbers) + PoissonConfig poisson_config; - geode::BoundingBox2D box; - box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); - box.add_point( geode::Point2D{ { 10.0, 10.0 } } ); - geode::SpatialDomain domain( box, 0. ); + // NOLINTBEGIN(*-magic-numbers) + poisson_config.add_domain_config( geode::Point2D{ { 0.0, 0.0 } }, + geode::Point2D{ { 10.0, 10.0 } }, 0. ); // --- Set descriptions SetDescription set01{ "set01", 2.0, 3.0, 1.0 }; @@ -233,21 +254,20 @@ namespace geode::TargetStatisticConfig stat03{ "density03", 30.0, 0.15 }; - PoissonSimulationRunner runner( domain ); - runner.add_set_descriptor( set01 ); - runner.add_set_descriptor( set02 ); - runner.add_set_descriptor( set03 ); - - runner.add_density_descriptor( density01 ); - runner.add_density_descriptor( density02 ); - runner.add_density_descriptor( density03 ); + poisson_config.add_set_descriptor( set01 ); + poisson_config.add_set_descriptor( set02 ); + poisson_config.add_set_descriptor( set03 ); - runner.add_target_statistics( stat01 ); - runner.add_target_statistics( stat02 ); - runner.add_target_statistics( stat03 ); + poisson_config.add_density_descriptor( density01 ); + poisson_config.add_density_descriptor( density02 ); + poisson_config.add_density_descriptor( density03 ); - runner.initialize(); + poisson_config.add_target_statistics( stat01 ); + poisson_config.add_target_statistics( stat02 ); + poisson_config.add_target_statistics( stat03 ); + geode::SimulationRunner< geode::Point2D > runner( + poisson_config.build() ); // run simulation geode::SimulationPrinterConfigurator printer_config; printer_config.output_folder = absl::StrCat( From 1dd8903b73ac62307daf5a179e4886d4eff75b95 Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Wed, 27 May 2026 16:47:17 +0000 Subject: [PATCH 36/59] Apply prepare changes --- include/geode/stochastic/sampling/mcmc/simulation_runner.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index d80e659..df7e7a0 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -62,7 +62,7 @@ namespace geode { public: SimulationRunner( SimulationContext< ObjectType >&& context ) - : context_( std::move( context ) ){}; + : context_( std::move( context ) ) {}; virtual ~SimulationRunner() = default; const ObjectSets< ObjectType >& run( From 449ca08360746bd19a6392bd3a5b821da38695f4 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Thu, 28 May 2026 11:08:55 +0200 Subject: [PATCH 37/59] fix execution --- .../mcmc/helpers/simulation_context.hpp | 15 ++++----- .../sampling/mcmc/simulation_runner.hpp | 15 ++++----- .../stochastic/spatial/spatial_domain.hpp | 10 +++--- .../sampling/mcmc/test-mh-poisson.cpp | 33 +++++++++---------- 4 files changed, 34 insertions(+), 39 deletions(-) diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp index 8ca3ce1..faa4a94 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp @@ -41,16 +41,11 @@ namespace geode template < typename ObjectType > struct SimulationContext { - explicit SimulationContext( SpatialDomain< ObjectType::dim > domain_in ) - : domain( std::move( domain_in ) ) - { - } - std::string string() const { auto message = std::string{ "SimulationContext: " }; - absl::StrAppend( &message, "\n\t --> ", domain.string() ); - absl::StrAppend( &message, "\n\t --> ", object_sets.string() ); + absl::StrAppend( &message, "\n\t --> ", domain->string() ); + absl::StrAppend( &message, "\n\t --> ", object_sets->string() ); absl::StrAppend( &message, "\n\t --> ", set_samplers.size(), " Sets samplers " ); absl::StrAppend( &message, "\n\t --> ", model->string() ); @@ -59,9 +54,11 @@ namespace geode return message; } - SpatialDomain< ObjectType::dim > domain; + std::unique_ptr< SpatialDomain< ObjectType::dim > > domain; - ObjectSets< ObjectType > object_sets; + std::unique_ptr< ObjectSets< ObjectType > > object_sets{ + std::make_unique< ObjectSets< ObjectType > >() + }; std::vector< std::unique_ptr< geode::ObjectSetSampler< ObjectType > > > set_samplers; std::unique_ptr< Model< ObjectType > > model; diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index df7e7a0..a5cf135 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -68,18 +68,17 @@ namespace geode const ObjectSets< ObjectType >& run( RandomEngine& engine, const index_t steps ) { - context_.mh_sampler->walk( context_.object_sets, engine, steps ); - return context_.object_sets; + context_.mh_sampler->walk( *context_.object_sets, engine, steps ); + return *context_.object_sets; } StatisticsTracker< ObjectType > run( RandomEngine& engine, const SimulationConfigurator& config ) { - Logger::info( context_.string() ); if( config.burn_in_steps > 0 ) { context_.mh_sampler->walk( - context_.object_sets, engine, config.burn_in_steps ); + *context_.object_sets, engine, config.burn_in_steps ); } // Initialize monitoring @@ -94,18 +93,18 @@ namespace geode for( const auto realization : Range{ config.realizations } ) { - context_.mh_sampler->walk( context_.object_sets, engine, + context_.mh_sampler->walk( *context_.object_sets, engine, config.metropolis_hasting_steps ); const auto stats = - context_.model->compute_statistics( context_.object_sets ); + context_.model->compute_statistics( *context_.object_sets ); stats_monitor.add_realization( stats ); if( printer ) { printer->print_statistics( stats ); printer->print_object_sets( - context_.object_sets, realization ); + *context_.object_sets, realization ); } } @@ -130,7 +129,7 @@ namespace geode [[nodiscard]] const ObjectSets< ObjectType >& state_realization() const { - return context_.object_sets; + return *context_.object_sets; } protected: diff --git a/include/geode/stochastic/spatial/spatial_domain.hpp b/include/geode/stochastic/spatial/spatial_domain.hpp index 3851e6a..e118f17 100644 --- a/include/geode/stochastic/spatial/spatial_domain.hpp +++ b/include/geode/stochastic/spatial/spatial_domain.hpp @@ -42,7 +42,7 @@ namespace geode SpatialDomain( BoundingBox< dimension > domain, double buffer_size ) : domain_{ domain }, buffer_size_{ buffer_size }, - extended_domain_{ domain_ } + extended_domain_{ domain } { auto volume = domain_.n_volume(); OpenGeodeStochasticStochasticException::check_exception( @@ -58,7 +58,6 @@ namespace geode { extended_domain_.extends( buffer_size_ ); } - Logger::info( domain_.string(), " ", extended_domain_.string() ); } const BoundingBox< dimension > box() const @@ -155,11 +154,12 @@ namespace geode }; template < index_t dimension > - SpatialDomain< dimension > build_spatial_domain( + std::unique_ptr< SpatialDomain< dimension > > build_spatial_domain( const SpatialDomainConfig< dimension >& config ) { BoundingBox< dimension > box{ config.min_point, config.max_point }; - Logger::info( box.string() ); - return SpatialDomain{ std::move( box ), config.buffer_size }; + + return std::make_unique< SpatialDomain< dimension > >( + std::move( box ), config.buffer_size ); } } // namespace geode \ No newline at end of file diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 9f6051b..140177e 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -83,9 +83,9 @@ namespace geode::SimulationContext< geode::Point2D > build() const { - geode::SimulationContext< geode::Point2D > context{ - geode::build_spatial_domain( domain_config_ ) - }; + geode::SimulationContext< geode::Point2D > context; + + context.domain = geode::build_spatial_domain( domain_config_ ); auto proposal_kernel = create_sets_and_set_samplers( context ); create_model( context ); @@ -103,16 +103,13 @@ namespace { auto proposal_kernel = std::make_unique< geode::ProposalKernel< geode::Point2D > >(); - for( const auto& set_desc : set_descriptors_ ) { const auto set_id = - context.object_sets.add_set( set_desc.name ); - + context.object_sets->add_set( set_desc.name ); context.set_samplers.push_back( std::make_unique< geode::UniformPointSetSampler< 2 > >( - context.domain ) ); - + *context.domain ) ); geode::add_birth_death_change_moves( context.set_samplers.back(), *proposal_kernel, set_id, set_desc.birth_ratio, set_desc.death_ratio, @@ -131,7 +128,7 @@ namespace } context.model = std::move( geode::build_model< geode::Point2D >( - config, context.object_sets, context.domain ) ); + config, *context.object_sets, *context.domain ) ); } void create_target_statistics( @@ -158,16 +155,16 @@ namespace geode::RandomEngine engine; engine.set_seed( "@mh-test-single-POISSON@" ); - - PoissonConfig poisson_config; - // NOLINTBEGIN(*-magic-numbers) - poisson_config.add_domain_config( geode::Point2D{ { 0.0, 0.0 } }, - geode::Point2D{ { 10.0, 10.0 } }, 0. ); - std::array< double, 4 > birth_ratio{ 0.1, 0.5, 2., 4. }; std::array< double, 4 > change_ratio{ 0., 1., 1., 0. }; + for( const auto config : geode::Range{ birth_ratio.size() } ) { + PoissonConfig poisson_config; + + // NOLINTBEGIN(*-magic-numbers) + poisson_config.add_domain_config( geode::Point2D{ { 0.0, 0.0 } }, + geode::Point2D{ { 10.0, 10.0 } }, 0. ); // --- Set description SetDescription set_a; set_a.name = "A"; @@ -188,8 +185,10 @@ namespace poisson_config.add_density_descriptor( density_a ); poisson_config.add_target_statistics( stat_a ); - geode::SimulationRunner< geode::Point2D > runner( - poisson_config.build() ); + auto context = poisson_config.build(); + + geode::SimulationRunner< geode::Point2D > runner{ std::move( + context ) }; // run simulation geode::SimulationPrinterConfigurator printer_config; From 3c30a67b8ad095472b3a2047835a8956b32ce787 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Thu, 28 May 2026 11:50:51 +0200 Subject: [PATCH 38/59] tidy --- .../inference/target_statistics.hpp | 30 +++++++----- include/geode/stochastic/models/model.hpp | 2 +- .../mcmc/helpers/simulation_context.hpp | 4 +- .../sampling/mcmc/simulation_runner.hpp | 45 +++++++++-------- .../stochastic/spatial/spatial_domain.hpp | 27 ++++++----- .../sampling/mcmc/test-mh-poisson.cpp | 48 +++++++------------ 6 files changed, 76 insertions(+), 80 deletions(-) diff --git a/include/geode/stochastic/inference/target_statistics.hpp b/include/geode/stochastic/inference/target_statistics.hpp index b845322..dd5856d 100644 --- a/include/geode/stochastic/inference/target_statistics.hpp +++ b/include/geode/stochastic/inference/target_statistics.hpp @@ -36,12 +36,17 @@ namespace geode class TargetStatistics { public: - explicit TargetStatistics( const Model< ObjectType >& model ) + explicit TargetStatistics( const Model< ObjectType >& model, + const std::vector< TargetStatisticConfig >& statistic_targets ) : model_( model ) { values_.resize( model.nb_terms(), 0.0 ); tolerances_.resize( model.nb_terms(), 0.0 ); active_.resize( model.nb_terms(), false ); + for( const auto& target : statistic_targets ) + { + set_target( target ); + } } const Model< ObjectType >& model() const @@ -49,17 +54,6 @@ namespace geode return model_; } - void set_target( const TargetStatisticConfig& statistic ) - { - const auto term_uuid = - model_.terms().get_term_uuid( statistic.term_name ); - const auto idx = model_.term_index( term_uuid ); - - values_[idx] = statistic.value; - tolerances_[idx] = statistic.tolerance; - active_[idx] = true; - } - bool has_target( const uuid& term_uuid ) const { return active_[model_.term_index( term_uuid )]; @@ -90,6 +84,18 @@ namespace geode return active_terms_uuid; } + private: + void set_target( const TargetStatisticConfig& statistic ) + { + const auto term_uuid = + model_.terms().get_term_uuid( statistic.term_name ); + const auto idx = model_.term_index( term_uuid ); + + values_[idx] = statistic.value; + tolerances_[idx] = statistic.tolerance; + active_[idx] = true; + } + private: const Model< ObjectType >& model_; diff --git a/include/geode/stochastic/models/model.hpp b/include/geode/stochastic/models/model.hpp index 70c3289..3e0a46d 100644 --- a/include/geode/stochastic/models/model.hpp +++ b/include/geode/stochastic/models/model.hpp @@ -50,7 +50,7 @@ namespace geode template < typename ObjectType > class Model { - OPENGEODE_DISABLE_COPY( Model ); + OPENGEODE_DISABLE_COPY_AND_MOVE( Model ); public: Model() = delete; diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp index faa4a94..3a852b3 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp @@ -41,7 +41,7 @@ namespace geode template < typename ObjectType > struct SimulationContext { - std::string string() const + [[nodiscard]] std::string string() const { auto message = std::string{ "SimulationContext: " }; absl::StrAppend( &message, "\n\t --> ", domain->string() ); @@ -63,8 +63,6 @@ namespace geode set_samplers; std::unique_ptr< Model< ObjectType > > model; std::unique_ptr< geode::MetropolisHastings< ObjectType > > mh_sampler; - - std::optional< TargetStatistics< ObjectType > > target_statistics; }; } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index a5cf135..8c4c995 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -28,18 +28,26 @@ #include #include - namespace geode { + namespace detail + { + // NOLINTBEGIN(*-magic-numbers) + constexpr index_t default_realizations{ 1000 }; + constexpr index_t default_simulation_steps{ 1000 }; + constexpr index_t default_burn_in_steps{ 1000 }; + // NOLINTEND(*-magic-numbers) + } // namespace detail + struct SimulationConfigurator { - index_t realizations{ 1000 }; - index_t metropolis_hasting_steps{ 1000 }; - index_t burn_in_steps{ 1000 }; + index_t realizations{ detail::default_realizations }; + index_t metropolis_hasting_steps{ detail::default_simulation_steps }; + index_t burn_in_steps{ detail::default_burn_in_steps }; std::optional< SimulationPrinterConfigurator > printer{ std::nullopt }; - std::string string() const + [[nodiscard]] std::string string() const { auto message = absl::StrCat( "SimulationConfigurator: " ); absl::StrAppend( &message, "\n\t --> ", realizations, @@ -60,19 +68,22 @@ namespace geode template < typename ObjectType > class SimulationRunner { + OPENGEODE_DISABLE_COPY_AND_MOVE( SimulationRunner ); + public: - SimulationRunner( SimulationContext< ObjectType >&& context ) - : context_( std::move( context ) ) {}; + SimulationRunner() = delete; + explicit SimulationRunner( SimulationContext< ObjectType >&& context ) + : context_( std::move( context ) ){}; virtual ~SimulationRunner() = default; - const ObjectSets< ObjectType >& run( + [[nodiscard]] const ObjectSets< ObjectType >& run( RandomEngine& engine, const index_t steps ) { context_.mh_sampler->walk( *context_.object_sets, engine, steps ); return *context_.object_sets; } - StatisticsTracker< ObjectType > run( + [[nodiscard]] StatisticsTracker< ObjectType > run( RandomEngine& engine, const SimulationConfigurator& config ) { if( config.burn_in_steps > 0 ) @@ -116,23 +127,17 @@ namespace geode return stats_monitor; } - [[nodiscard]] const TargetStatistics< ObjectType >& - target_statistics() const + [[nodiscard]] const ObjectSets< ObjectType >& state_realization() const { - OpenGeodeStochasticStochasticException::check_exception( - context_.target_statistics.has_value(), nullptr, - OpenGeodeException::TYPE::data, - "[SimulationRunner] Target statistics not initialized" ); - - return *context_.target_statistics; + return *context_.object_sets; } - [[nodiscard]] const ObjectSets< ObjectType >& state_realization() const + [[nodiscard]] const Model< ObjectType >& model() const { - return *context_.object_sets; + return *context_.model; } - protected: + private: SimulationContext< ObjectType > context_; }; } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/spatial/spatial_domain.hpp b/include/geode/stochastic/spatial/spatial_domain.hpp index e118f17..58ff41a 100644 --- a/include/geode/stochastic/spatial/spatial_domain.hpp +++ b/include/geode/stochastic/spatial/spatial_domain.hpp @@ -23,6 +23,8 @@ #pragma once +#include + #include #include #include @@ -32,12 +34,10 @@ namespace geode template < index_t dimension > class SpatialDomain { - public: - SpatialDomain( const SpatialDomain& ) = default; - SpatialDomain( SpatialDomain&& ) noexcept = default; + OPENGEODE_DISABLE_COPY_AND_MOVE( SpatialDomain ); - SpatialDomain& operator=( const SpatialDomain& ) = default; - SpatialDomain& operator=( SpatialDomain&& ) noexcept = default; + public: + ~SpatialDomain() = default; SpatialDomain( BoundingBox< dimension > domain, double buffer_size ) : domain_{ domain }, @@ -60,42 +60,43 @@ namespace geode } } - const BoundingBox< dimension > box() const + [[nodiscard]] const BoundingBox< dimension >& box() const { return domain_; } - bool contains( const Point< dimension >& point ) const + [[nodiscard]] bool contains( const Point< dimension >& point ) const { return domain_.contains( point ); } - double n_volume() const + [[nodiscard]] double n_volume() const { return domain_.n_volume(); } - double smallest_length() const + [[nodiscard]] double smallest_length() const { return std::get< 1 >( domain_.smallest_length() ); } - bool extended_contains( const Point< dimension >& point ) const + [[nodiscard]] bool extended_contains( + const Point< dimension >& point ) const { return extended_domain_.contains( point ); } - double extended_n_volume() const + [[nodiscard]] double extended_n_volume() const { return extended_domain_.n_volume(); } - const BoundingBox< dimension > extended_box() const + [[nodiscard]] const BoundingBox< dimension >& extended_box() const { return extended_domain_; } - std::string string() const + [[nodiscard]] std::string string() const { return absl::StrCat( "Spatial Domain --> center ", domain_.string(), " extended: ", extended_domain_.string(), diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 140177e..67642d0 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -75,13 +75,7 @@ namespace density_descriptors_.push_back( descriptor ); } - void add_target_statistics( - const geode::TargetStatisticConfig& statistic_descriptor ) - { - targeted_statistics_descriptors_.push_back( statistic_descriptor ); - } - - geode::SimulationContext< geode::Point2D > build() const + [[nodiscard]] geode::SimulationContext< geode::Point2D > build() const { geode::SimulationContext< geode::Point2D > context; @@ -92,7 +86,6 @@ namespace context.mh_sampler = std::make_unique< geode::MetropolisHastings< geode::Point2D > >( *context.model, std::move( proposal_kernel ) ); - create_target_statistics( context ); return context; } @@ -131,22 +124,10 @@ namespace config, *context.object_sets, *context.domain ) ); } - void create_target_statistics( - geode::SimulationContext< geode::Point2D >& context ) const - { - context.target_statistics.emplace( *context.model ); - for( const auto& target_stat : targeted_statistics_descriptors_ ) - { - context.target_statistics->set_target( target_stat ); - } - } - private: geode::SpatialDomainConfig< 2 > domain_config_; std::vector< SetDescription > set_descriptors_; std::vector< PoissonDensityDescription > density_descriptors_; - std::vector< geode::TargetStatisticConfig > - targeted_statistics_descriptors_; }; void test_single_type_poisson() @@ -155,14 +136,17 @@ namespace geode::RandomEngine engine; engine.set_seed( "@mh-test-single-POISSON@" ); + + // NOLINTBEGIN(*-magic-numbers) std::array< double, 4 > birth_ratio{ 0.1, 0.5, 2., 4. }; std::array< double, 4 > change_ratio{ 0., 1., 1., 0. }; for( const auto config : geode::Range{ birth_ratio.size() } ) { PoissonConfig poisson_config; + std::vector< geode::TargetStatisticConfig > + targeted_statistics_descriptors; - // NOLINTBEGIN(*-magic-numbers) poisson_config.add_domain_config( geode::Point2D{ { 0.0, 0.0 } }, geode::Point2D{ { 10.0, 10.0 } }, 0. ); // --- Set description @@ -180,10 +164,10 @@ namespace density_a.object_feature = geode::ObjectInDomainFeatureConfig{}; geode::TargetStatisticConfig stat_a{ "density", 30.0, 0.15 }; + targeted_statistics_descriptors.push_back( stat_a ); poisson_config.add_set_descriptor( set_a ); poisson_config.add_density_descriptor( density_a ); - poisson_config.add_target_statistics( stat_a ); auto context = poisson_config.build(); @@ -203,8 +187,9 @@ namespace sim_config.printer = printer_config; auto statistic_tracker = runner.run( engine, sim_config ); - geode::statistics::validate( - statistic_tracker, runner.target_statistics() ); + geode::TargetStatistics target_stats{ runner.model(), + targeted_statistics_descriptors }; + geode::statistics::validate( statistic_tracker, target_stats ); } // NOLINTEND(*-magic-numbers) @@ -218,7 +203,8 @@ namespace geode::RandomEngine engine; engine.set_seed( "@mh-test-POISSON-multi@" ); PoissonConfig poisson_config; - + std::vector< geode::TargetStatisticConfig > + targeted_statistics_descriptors; // NOLINTBEGIN(*-magic-numbers) poisson_config.add_domain_config( geode::Point2D{ { 0.0, 0.0 } }, geode::Point2D{ { 10.0, 10.0 } }, 0. ); @@ -236,6 +222,7 @@ namespace density01.object_feature = geode::ObjectInDomainFeatureConfig{}; geode::TargetStatisticConfig stat01{ "density01", 10.0, 0.15 }; + targeted_statistics_descriptors.push_back( stat01 ); PoissonDensityDescription density02; density02.term_name = "density02"; @@ -244,6 +231,7 @@ namespace density02.object_feature = geode::ObjectInDomainFeatureConfig{}; geode::TargetStatisticConfig stat02{ "density02", 40.0, 0.15 }; + targeted_statistics_descriptors.push_back( stat02 ); PoissonDensityDescription density03; density03.term_name = "density03"; @@ -252,6 +240,7 @@ namespace density03.object_feature = geode::ObjectInDomainFeatureConfig{}; geode::TargetStatisticConfig stat03{ "density03", 30.0, 0.15 }; + targeted_statistics_descriptors.push_back( stat03 ); poisson_config.add_set_descriptor( set01 ); poisson_config.add_set_descriptor( set02 ); @@ -261,10 +250,6 @@ namespace poisson_config.add_density_descriptor( density02 ); poisson_config.add_density_descriptor( density03 ); - poisson_config.add_target_statistics( stat01 ); - poisson_config.add_target_statistics( stat02 ); - poisson_config.add_target_statistics( stat03 ); - geode::SimulationRunner< geode::Point2D > runner( poisson_config.build() ); // run simulation @@ -280,8 +265,9 @@ namespace // NOLINTEND(*-magic-numbers) auto statistic_tracker = runner.run( engine, sim_config ); - geode::statistics::validate( - statistic_tracker, runner.target_statistics() ); + geode::TargetStatistics target_stats{ runner.model(), + targeted_statistics_descriptors }; + geode::statistics::validate( statistic_tracker, target_stats ); geode::Logger::info( "--> SUCCESS!" ); } From e0a5a9413f4997b85447fbd548940b0f799d9aa0 Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Thu, 28 May 2026 09:51:24 +0000 Subject: [PATCH 39/59] Apply prepare changes --- include/geode/stochastic/sampling/mcmc/simulation_runner.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index 8c4c995..93b22fa 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -73,7 +73,7 @@ namespace geode public: SimulationRunner() = delete; explicit SimulationRunner( SimulationContext< ObjectType >&& context ) - : context_( std::move( context ) ){}; + : context_( std::move( context ) ) {}; virtual ~SimulationRunner() = default; [[nodiscard]] const ObjectSets< ObjectType >& run( From d724578f26b1e9ade5a5b6115af34a0032f8885a Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Thu, 28 May 2026 23:00:08 +0200 Subject: [PATCH 40/59] create simulation context builder --- .../mcmc/helpers/simulation_context.hpp | 92 ++++++++- .../proposal/object_set_dynamic_config.hpp | 35 ++++ .../geode/stochastic/spatial/object_sets.hpp | 9 + src/geode/stochastic/CMakeLists.txt | 1 + .../sampling/mcmc/test-mh-poisson.cpp | 175 +++++------------- 5 files changed, 186 insertions(+), 126 deletions(-) diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp index 3a852b3..b46b438 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp @@ -26,15 +26,19 @@ #include #include +#include #include #include #include -#include +#include -#include +#include + +#include +#include namespace geode { @@ -65,4 +69,88 @@ namespace geode std::unique_ptr< geode::MetropolisHastings< ObjectType > > mh_sampler; }; + template < typename ObjectType > + struct SimulationContextConfig + { + SpatialDomainConfig< ObjectType::dim > domain; + + std::vector< ObjectSetConfig > sets; + std::vector< ObjectSetDynamicsConfig > proposals; + + geode::ModelConfig model; + }; + + template < typename ObjectType > + [[nodiscard]] geode::SimulationContext< ObjectType > + build_simulation_context( + const SimulationContextConfig< ObjectType >& config ) + { + geode::SimulationContext< ObjectType > context; + + // ------------------------- + // Domain + // ------------------------- + context.domain = geode::build_spatial_domain( config.domain ); + + // ------------------------- + // Sets + // ------------------------- + + // auto proposal_kernel = + // std::make_unique< geode::ProposalKernel< geode::Point2D > + // >(); + // for( const auto& set_desc : set_descriptors_ ) + // { + // const auto set_id = context.object_sets->add_set( + // set_desc.name ); context.set_samplers.push_back( + // std::make_unique< geode::UniformPointSetSampler< 2 > + // >( + // *context.domain ) ); + // geode::add_birth_death_change_moves( + // context.set_samplers.back(), + // *proposal_kernel, set_id, set_desc.birth_ratio, + // set_desc.death_ratio, set_desc.change_ratio ); + // } + // return proposal_kernel; + + for( const auto& set_cfg : config.sets ) + { + const auto set_id = context.object_sets->add_set( set_cfg.name ); + geode_unused( set_id ); + } + + // ------------------------- + // Model + // ------------------------- + context.model = geode::build_model< ObjectType >( + config.model, *context.object_sets, *context.domain ); + + // ------------------------- + // Proposal + // ------------------------- + auto proposal_kernel = + std::make_unique< geode::ProposalKernel< ObjectType > >(); + for( const auto& set_proposal : config.proposals ) + { + const auto set_id = + context.object_sets->get_set_uuid( set_proposal.name ); + context.set_samplers.push_back( + std::make_unique< geode::UniformPointSetSampler< 2 > >( + *context.domain ) ); + + geode::add_birth_death_change_moves( context.set_samplers.back(), + *proposal_kernel, set_id, set_proposal.birth_ratio, + set_proposal.death_ratio, set_proposal.change_ratio ); + } + + // ------------------------- + // MH sampler + // ------------------------- + context.mh_sampler = + std::make_unique< geode::MetropolisHastings< geode::Point2D > >( + *context.model, std::move( proposal_kernel ) ); + + return context; + } + } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp b/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp index e69de29..212298b 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.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 +namespace geode +{ + struct ObjectSetDynamicsConfig + { + std::string name; + + double birth_ratio = 1.0; + double death_ratio = 1.0; + double change_ratio = 1.0; + }; +} // 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 e6af6e9..910ccae 100644 --- a/include/geode/stochastic/spatial/object_sets.hpp +++ b/include/geode/stochastic/spatial/object_sets.hpp @@ -46,6 +46,15 @@ namespace geode namespace geode { + struct ObjectSetConfig + { + std::string name; + + double birth_ratio = 1.0; + double death_ratio = 1.0; + double change_ratio = 1.0; + }; + template < typename Type > class ObjectSets { diff --git a/src/geode/stochastic/CMakeLists.txt b/src/geode/stochastic/CMakeLists.txt index 1e1302e..e188247 100644 --- a/src/geode/stochastic/CMakeLists.txt +++ b/src/geode/stochastic/CMakeLists.txt @@ -76,6 +76,7 @@ add_geode_library( "models/model.hpp" "sampling/mcmc/proposal/classical_proposals.hpp" "sampling/mcmc/proposal/moves.hpp" + "sampling/mcmc/proposal/object_set_dynamic_config.hpp" "sampling/mcmc/proposal/proposal_kernel.hpp" "sampling/mcmc/metropolis_hasting_sampler.hpp" "sampling/mcmc/simulation_runner.hpp" diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 67642d0..766d632 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -36,100 +36,15 @@ #include #include +#include #include #include + namespace { - struct SetDescription - { - std::string name; - double birth_ratio{ 1.0 }; - double death_ratio{ 1.0 }; - double change_ratio{ 1.0 }; - }; using PoissonDensityDescription = geode::SingleObjectTermConfig; - class PoissonConfig - { - public: - PoissonConfig() = default; - - void add_domain_config( const geode::Point2D& min_p, - const geode::Point2D& max_p, - double buffer_size ) - { - domain_config_.min_point = min_p; - domain_config_.max_point = max_p; - domain_config_.buffer_size = buffer_size; - } - - void add_set_descriptor( const SetDescription& descriptor ) - { - set_descriptors_.push_back( descriptor ); - } - - void add_density_descriptor( - const PoissonDensityDescription& descriptor ) - { - density_descriptors_.push_back( descriptor ); - } - - [[nodiscard]] geode::SimulationContext< geode::Point2D > build() const - { - geode::SimulationContext< geode::Point2D > context; - - context.domain = geode::build_spatial_domain( domain_config_ ); - auto proposal_kernel = create_sets_and_set_samplers( context ); - create_model( context ); - - context.mh_sampler = - std::make_unique< geode::MetropolisHastings< geode::Point2D > >( - *context.model, std::move( proposal_kernel ) ); - return context; - } - - private: - std::unique_ptr< geode::ProposalKernel< geode::Point2D > > - create_sets_and_set_samplers( - geode::SimulationContext< geode::Point2D >& context ) const - { - auto proposal_kernel = - std::make_unique< geode::ProposalKernel< geode::Point2D > >(); - for( const auto& set_desc : set_descriptors_ ) - { - const auto set_id = - context.object_sets->add_set( set_desc.name ); - context.set_samplers.push_back( - std::make_unique< geode::UniformPointSetSampler< 2 > >( - *context.domain ) ); - geode::add_birth_death_change_moves( - context.set_samplers.back(), *proposal_kernel, set_id, - set_desc.birth_ratio, set_desc.death_ratio, - set_desc.change_ratio ); - } - return proposal_kernel; - } - - void create_model( - geode::SimulationContext< geode::Point2D >& context ) const - { - geode::ModelConfig config; - for( const auto& energy_desc : density_descriptors_ ) - { - config.terms.emplace_back( energy_desc ); - } - - context.model = std::move( geode::build_model< geode::Point2D >( - config, *context.object_sets, *context.domain ) ); - } - - private: - geode::SpatialDomainConfig< 2 > domain_config_; - std::vector< SetDescription > set_descriptors_; - std::vector< PoissonDensityDescription > density_descriptors_; - }; - void test_single_type_poisson() { geode::Logger::info( "TEST - MH SINGLE TYPE POISSON" ); @@ -143,18 +58,21 @@ namespace for( const auto config : geode::Range{ birth_ratio.size() } ) { - PoissonConfig poisson_config; + geode::SimulationContextConfig< geode::Point2D > poisson_config; std::vector< geode::TargetStatisticConfig > targeted_statistics_descriptors; - poisson_config.add_domain_config( geode::Point2D{ { 0.0, 0.0 } }, - geode::Point2D{ { 10.0, 10.0 } }, 0. ); + poisson_config.domain = { geode::Point2D{ { 0.0, 0.0 } }, + geode::Point2D{ { 10.0, 10.0 } }, 0. }; // --- Set description - SetDescription set_a; + geode::ObjectSetConfig set_a; set_a.name = "A"; - set_a.birth_ratio = birth_ratio[config]; - set_a.death_ratio = 1.0; - set_a.change_ratio = change_ratio[config]; + + geode::ObjectSetDynamicsConfig dyn_a; + dyn_a.name = "A"; + dyn_a.birth_ratio = birth_ratio[config]; + dyn_a.death_ratio = 1.0; + dyn_a.change_ratio = change_ratio[config]; // --- Energy term description PoissonDensityDescription density_a; @@ -166,11 +84,11 @@ namespace geode::TargetStatisticConfig stat_a{ "density", 30.0, 0.15 }; targeted_statistics_descriptors.push_back( stat_a ); - poisson_config.add_set_descriptor( set_a ); - poisson_config.add_density_descriptor( density_a ); - - auto context = poisson_config.build(); + poisson_config.sets.emplace_back( set_a ); + poisson_config.proposals.emplace_back( dyn_a ); + poisson_config.model.terms.emplace_back( density_a ); + auto context = build_simulation_context( poisson_config ); geode::SimulationRunner< geode::Point2D > runner{ std::move( context ) }; @@ -202,17 +120,28 @@ namespace geode::RandomEngine engine; engine.set_seed( "@mh-test-POISSON-multi@" ); - PoissonConfig poisson_config; - std::vector< geode::TargetStatisticConfig > - targeted_statistics_descriptors; + + geode::SimulationContextConfig< geode::Point2D > poisson_config; + // NOLINTBEGIN(*-magic-numbers) - poisson_config.add_domain_config( geode::Point2D{ { 0.0, 0.0 } }, - geode::Point2D{ { 10.0, 10.0 } }, 0. ); + poisson_config.domain = { geode::Point2D{ { 0.0, 0.0 } }, + geode::Point2D{ { 10.0, 10.0 } }, 0. }; // --- Set descriptions - SetDescription set01{ "set01", 2.0, 3.0, 1.0 }; - SetDescription set02{ "set02", 3.0, 0.5, 1.0 }; - SetDescription set03{ "set03", 4.0, 1.0, 1.0 }; + geode::ObjectSetConfig set01{ "set01" }; + geode::ObjectSetConfig set02{ "set02" }; + geode::ObjectSetConfig set03{ "set03" }; + poisson_config.sets.emplace_back( set01 ); + poisson_config.sets.emplace_back( set02 ); + poisson_config.sets.emplace_back( set03 ); + + // --- proposal descriptions + geode::ObjectSetDynamicsConfig dyn01{ "set01", 2.0, 3.0, 1.0 }; + geode::ObjectSetDynamicsConfig dyn02{ "set02", 3.0, 0.5, 1.0 }; + geode::ObjectSetDynamicsConfig dyn03{ "set03", 4.0, 1.0, 1.0 }; + poisson_config.proposals.emplace_back( dyn01 ); + poisson_config.proposals.emplace_back( dyn02 ); + poisson_config.proposals.emplace_back( dyn03 ); // --- Energy term descriptions PoissonDensityDescription density01; @@ -221,37 +150,25 @@ namespace density01.lambda = 0.1; density01.object_feature = geode::ObjectInDomainFeatureConfig{}; - geode::TargetStatisticConfig stat01{ "density01", 10.0, 0.15 }; - targeted_statistics_descriptors.push_back( stat01 ); - PoissonDensityDescription density02; density02.term_name = "density02"; density02.object_set_names = { "set02" }; density02.lambda = 0.4; density02.object_feature = geode::ObjectInDomainFeatureConfig{}; - geode::TargetStatisticConfig stat02{ "density02", 40.0, 0.15 }; - targeted_statistics_descriptors.push_back( stat02 ); - PoissonDensityDescription density03; density03.term_name = "density03"; density03.object_set_names = { "set03" }; density03.lambda = 0.3; density03.object_feature = geode::ObjectInDomainFeatureConfig{}; + poisson_config.model.terms.emplace_back( density01 ); + poisson_config.model.terms.emplace_back( density02 ); + poisson_config.model.terms.emplace_back( density03 ); - geode::TargetStatisticConfig stat03{ "density03", 30.0, 0.15 }; - targeted_statistics_descriptors.push_back( stat03 ); + auto context = build_simulation_context( poisson_config ); + geode::SimulationRunner< geode::Point2D > runner{ std::move( + context ) }; - poisson_config.add_set_descriptor( set01 ); - poisson_config.add_set_descriptor( set02 ); - poisson_config.add_set_descriptor( set03 ); - - poisson_config.add_density_descriptor( density01 ); - poisson_config.add_density_descriptor( density02 ); - poisson_config.add_density_descriptor( density03 ); - - geode::SimulationRunner< geode::Point2D > runner( - poisson_config.build() ); // run simulation geode::SimulationPrinterConfigurator printer_config; printer_config.output_folder = absl::StrCat( @@ -265,6 +182,16 @@ namespace // NOLINTEND(*-magic-numbers) auto statistic_tracker = runner.run( engine, sim_config ); + + std::vector< geode::TargetStatisticConfig > + targeted_statistics_descriptors; + geode::TargetStatisticConfig stat01{ "density01", 10.0, 0.15 }; + targeted_statistics_descriptors.push_back( stat01 ); + geode::TargetStatisticConfig stat02{ "density02", 40.0, 0.15 }; + targeted_statistics_descriptors.push_back( stat02 ); + geode::TargetStatisticConfig stat03{ "density03", 30.0, 0.15 }; + targeted_statistics_descriptors.push_back( stat03 ); + geode::TargetStatistics target_stats{ runner.model(), targeted_statistics_descriptors }; geode::statistics::validate( statistic_tracker, target_stats ); From 68f5d6f9ad5318ce3549d9850e9c777cf73cfe26 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 29 May 2026 09:04:37 +0200 Subject: [PATCH 41/59] tidy --- .../inference/target_statistics.hpp | 11 ++--- include/geode/stochastic/models/model.hpp | 4 +- .../proposal/object_set_dynamic_config.hpp | 5 ++- .../sampling/mcmc/simulation_runner.hpp | 14 +++--- .../geode/stochastic/spatial/object_sets.hpp | 44 ++++++++++--------- .../sampling/mcmc/test-mh-poisson.cpp | 2 +- 6 files changed, 45 insertions(+), 35 deletions(-) diff --git a/include/geode/stochastic/inference/target_statistics.hpp b/include/geode/stochastic/inference/target_statistics.hpp index dd5856d..f5be587 100644 --- a/include/geode/stochastic/inference/target_statistics.hpp +++ b/include/geode/stochastic/inference/target_statistics.hpp @@ -22,6 +22,7 @@ */ #pragma once #include +#include namespace geode { @@ -49,27 +50,27 @@ namespace geode } } - const Model< ObjectType >& model() const + [[nodiscard]] const Model< ObjectType >& model() const { return model_; } - bool has_target( const uuid& term_uuid ) const + [[nodiscard]] bool has_target( const uuid& term_uuid ) const { return active_[model_.term_index( term_uuid )]; } - double target( const uuid& term_uuid ) const + [[nodiscard]] double target( const uuid& term_uuid ) const { return values_[model_.term_index( term_uuid )]; } - double tolerance( const uuid& term_uuid ) const + [[nodiscard]] double tolerance( const uuid& term_uuid ) const { return tolerances_[model_.term_index( term_uuid )]; } - std::vector< uuid > active_terms() const + [[nodiscard]] std::vector< uuid > active_terms() const { std::vector< uuid > active_terms_uuid; diff --git a/include/geode/stochastic/models/model.hpp b/include/geode/stochastic/models/model.hpp index 3e0a46d..532cec8 100644 --- a/include/geode/stochastic/models/model.hpp +++ b/include/geode/stochastic/models/model.hpp @@ -54,7 +54,9 @@ namespace geode public: Model() = delete; - Model( EnergyTermCollection< ObjectType >&& energy_terms ) + ~Model() = default; + + explicit Model( EnergyTermCollection< ObjectType >&& energy_terms ) : terms_collection_( std::move( energy_terms ) ), energy_{ terms_collection_ } { diff --git a/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp b/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp index 212298b..06053bd 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp @@ -22,11 +22,14 @@ */ #pragma once + +#include + namespace geode { struct ObjectSetDynamicsConfig { - std::string name; + std::string name{}; double birth_ratio = 1.0; double death_ratio = 1.0; diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index 93b22fa..0ec3acb 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -33,17 +33,17 @@ namespace geode namespace detail { // NOLINTBEGIN(*-magic-numbers) - constexpr index_t default_realizations{ 1000 }; - constexpr index_t default_simulation_steps{ 1000 }; - constexpr index_t default_burn_in_steps{ 1000 }; + constexpr index_t DEFAULT_REALIZATIONS{ 1000 }; + constexpr index_t DEFAULT_SIMULATION_STEPS{ 1000 }; + constexpr index_t DEFAULT_BURN_IN_STEPS{ 1000 }; // NOLINTEND(*-magic-numbers) } // namespace detail struct SimulationConfigurator { - index_t realizations{ detail::default_realizations }; - index_t metropolis_hasting_steps{ detail::default_simulation_steps }; - index_t burn_in_steps{ detail::default_burn_in_steps }; + index_t realizations{ detail::DEFAULT_REALIZATIONS }; + index_t metropolis_hasting_steps{ detail::DEFAULT_SIMULATION_STEPS }; + index_t burn_in_steps{ detail::DEFAULT_BURN_IN_STEPS }; std::optional< SimulationPrinterConfigurator > printer{ std::nullopt }; @@ -73,7 +73,7 @@ namespace geode public: SimulationRunner() = delete; explicit SimulationRunner( SimulationContext< ObjectType >&& context ) - : context_( std::move( context ) ) {}; + : context_( std::move( context ) ){}; virtual ~SimulationRunner() = default; [[nodiscard]] const ObjectSets< ObjectType >& run( diff --git a/include/geode/stochastic/spatial/object_sets.hpp b/include/geode/stochastic/spatial/object_sets.hpp index 910ccae..f606524 100644 --- a/include/geode/stochastic/spatial/object_sets.hpp +++ b/include/geode/stochastic/spatial/object_sets.hpp @@ -65,36 +65,40 @@ namespace geode ObjectSets( ObjectSets&& ) noexcept = default; ObjectSets& operator=( ObjectSets&& ) noexcept = default; - const ObjectSet< Type >& get_set( const uuid& set_id ) const; - const Type& get_object( const ObjectId& object_id ) const; - std::vector< ObjectId > get_all_object() const; - std::vector< ObjectId > get_objects_in_set( const uuid& set_id ) const; - - index_t nb_sets() const; - index_t nb_objects_in_set( const uuid& set_id ) const; - index_t nb_objects() const; - - uuid add_set( std::string_view name ); - ObjectId add_object( Type&& object, const uuid& set_id, bool fixed ); - void update_free_object( const ObjectId& object_id, Type&& object ); - void remove_free_object( const ObjectId& object_id ); - - std::vector< ObjectId > neighbors( const Type& object, + [[nodiscard]] const ObjectSet< Type >& get_set( + const uuid& set_id ) const; + [[nodiscard]] const Type& get_object( const ObjectId& object_id ) const; + [[nodiscard]] std::vector< ObjectId > get_all_object() const; + [[nodiscard]] std::vector< ObjectId > get_objects_in_set( + const uuid& set_id ) const; + + [[nodiscard]] index_t nb_sets() const; + [[nodiscard]] index_t nb_objects_in_set( const uuid& set_id ) const; + [[nodiscard]] index_t nb_objects() const; + + [[nodiscard]] uuid add_set( std::string_view name ); + [[nodiscard]] ObjectId add_object( + Type&& object, const uuid& set_id, bool fixed ); + [[nodiscard]] void update_free_object( + const ObjectId& object_id, Type&& object ); + [[nodiscard]] void remove_free_object( const ObjectId& object_id ); + + [[nodiscard]] std::vector< ObjectId > neighbors( const Type& object, const std::vector< uuid >& targeted_set_ids, double searching_distance, std::optional< ObjectId > exclude_id ) const; - std::string string() const; + [[nodiscard]] std::string string() const; - uuid get_set_uuid( std::string_view set_name ) const; - std::vector< uuid > get_set_uuids( + [[nodiscard]] uuid get_set_uuid( std::string_view set_name ) const; + [[nodiscard]] std::vector< uuid > get_set_uuids( const std::vector< std::string >& set_names ) const; - std::vector< std::pair< uuid, uuid > > get_set_uuid_pairs( + [[nodiscard]] 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 ); + [[nodiscard]] ObjectSet< Type >& get_set( const uuid& set_id ); private: absl::flat_hash_map< std::string, uuid > name_to_uuid_; diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 766d632..679ee19 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -179,7 +179,6 @@ namespace sim_config.metropolis_hasting_steps = 1000; sim_config.burn_in_steps = 3000; sim_config.printer = printer_config; - // NOLINTEND(*-magic-numbers) auto statistic_tracker = runner.run( engine, sim_config ); @@ -191,6 +190,7 @@ namespace targeted_statistics_descriptors.push_back( stat02 ); geode::TargetStatisticConfig stat03{ "density03", 30.0, 0.15 }; targeted_statistics_descriptors.push_back( stat03 ); + // NOLINTEND(*-magic-numbers) geode::TargetStatistics target_stats{ runner.model(), targeted_statistics_descriptors }; From 7d1a5be76712ba5c31dc4eea035a1606811bad87 Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Fri, 29 May 2026 07:05:12 +0000 Subject: [PATCH 42/59] Apply prepare changes --- include/geode/stochastic/sampling/mcmc/simulation_runner.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index 0ec3acb..4bfadbe 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -73,7 +73,7 @@ namespace geode public: SimulationRunner() = delete; explicit SimulationRunner( SimulationContext< ObjectType >&& context ) - : context_( std::move( context ) ){}; + : context_( std::move( context ) ) {}; virtual ~SimulationRunner() = default; [[nodiscard]] const ObjectSets< ObjectType >& run( From fd01ad83e23ac478c90c8dc69afaf5be95f9997c Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 29 May 2026 09:06:58 +0200 Subject: [PATCH 43/59] tidy2 --- include/geode/stochastic/spatial/object_sets.hpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/include/geode/stochastic/spatial/object_sets.hpp b/include/geode/stochastic/spatial/object_sets.hpp index f606524..55613b6 100644 --- a/include/geode/stochastic/spatial/object_sets.hpp +++ b/include/geode/stochastic/spatial/object_sets.hpp @@ -76,12 +76,10 @@ namespace geode [[nodiscard]] index_t nb_objects_in_set( const uuid& set_id ) const; [[nodiscard]] index_t nb_objects() const; - [[nodiscard]] uuid add_set( std::string_view name ); - [[nodiscard]] ObjectId add_object( - Type&& object, const uuid& set_id, bool fixed ); - [[nodiscard]] void update_free_object( - const ObjectId& object_id, Type&& object ); - [[nodiscard]] void remove_free_object( const ObjectId& object_id ); + uuid add_set( std::string_view name ); + ObjectId add_object( Type&& object, const uuid& set_id, bool fixed ); + void update_free_object( const ObjectId& object_id, Type&& object ); + void remove_free_object( const ObjectId& object_id ); [[nodiscard]] std::vector< ObjectId > neighbors( const Type& object, const std::vector< uuid >& targeted_set_ids, From 426a3a28182d228a3f3e3b8cc0524b1097764573 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 29 May 2026 10:29:48 +0200 Subject: [PATCH 44/59] Update include/geode/stochastic/sampling/mcmc/simulation_runner.hpp Co-authored-by: Arnaud Botella --- include/geode/stochastic/sampling/mcmc/simulation_runner.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index 4bfadbe..4f24649 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -73,7 +73,7 @@ namespace geode public: SimulationRunner() = delete; explicit SimulationRunner( SimulationContext< ObjectType >&& context ) - : context_( std::move( context ) ) {}; + : context_( std::move( context ) ) {} virtual ~SimulationRunner() = default; [[nodiscard]] const ObjectSets< ObjectType >& run( From 3f3192af578f39896ba3b503870ff9dcdb069d26 Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Fri, 29 May 2026 08:30:16 +0000 Subject: [PATCH 45/59] Apply prepare changes --- include/geode/stochastic/sampling/mcmc/simulation_runner.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp index 4f24649..df4fb8e 100644 --- a/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/simulation_runner.hpp @@ -73,7 +73,9 @@ namespace geode public: SimulationRunner() = delete; explicit SimulationRunner( SimulationContext< ObjectType >&& context ) - : context_( std::move( context ) ) {} + : context_( std::move( context ) ) + { + } virtual ~SimulationRunner() = default; [[nodiscard]] const ObjectSets< ObjectType >& run( From 2ae78a9ed2fe34485362cb26e143297ee80fe3d7 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 29 May 2026 10:33:35 +0200 Subject: [PATCH 46/59] more tidy --- include/geode/stochastic/inference/target_statistics.hpp | 6 +++--- .../sampling/mcmc/proposal/object_set_dynamic_config.hpp | 2 +- include/geode/stochastic/spatial/object_sets.hpp | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/geode/stochastic/inference/target_statistics.hpp b/include/geode/stochastic/inference/target_statistics.hpp index f5be587..538eca0 100644 --- a/include/geode/stochastic/inference/target_statistics.hpp +++ b/include/geode/stochastic/inference/target_statistics.hpp @@ -76,10 +76,10 @@ namespace geode for( const auto& term : model_.terms().energy_terms() ) { - const auto& id = term->id(); - if( active_[model_.term_index( id )] ) + const auto& term_id = term->id(); + if( active_[model_.term_index( term_id )] ) { - active_terms_uuid.push_back( id ); + active_terms_uuid.push_back( term_id ); } } return active_terms_uuid; diff --git a/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp b/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp index 06053bd..7dea345 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp @@ -29,7 +29,7 @@ namespace geode { struct ObjectSetDynamicsConfig { - std::string name{}; + std::string name; double birth_ratio = 1.0; double death_ratio = 1.0; diff --git a/include/geode/stochastic/spatial/object_sets.hpp b/include/geode/stochastic/spatial/object_sets.hpp index 55613b6..a5c39be 100644 --- a/include/geode/stochastic/spatial/object_sets.hpp +++ b/include/geode/stochastic/spatial/object_sets.hpp @@ -62,6 +62,8 @@ namespace geode public: ObjectSets() noexcept = default; + ~ObjectSets() noexcept = default; + ObjectSets( ObjectSets&& ) noexcept = default; ObjectSets& operator=( ObjectSets&& ) noexcept = default; From 9b52d6133c84977e8e2312d427ec1d377c21953a Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 29 May 2026 16:34:30 +0200 Subject: [PATCH 47/59] feat(Sampler): add genericity --- .../object_set_sampler/object_set_sampler.hpp | 11 +++ .../object_set_sampler/point_set_sampler.hpp | 24 +++++- .../segment_set_sampler.hpp | 29 +++++-- .../mcmc/helpers/simulation_context.hpp | 62 +++++---------- .../mcmc/proposal/classical_proposals.hpp | 6 +- .../geode/stochastic/spatial/object_sets.hpp | 9 --- .../mcmc/proposal/test-proposal-kernel.cpp | 3 +- .../sampling/mcmc/test-mh-poisson.cpp | 79 ++++++++++--------- 8 files changed, 123 insertions(+), 100 deletions(-) diff --git a/include/geode/stochastic/sampling/direct/object_set_sampler/object_set_sampler.hpp b/include/geode/stochastic/sampling/direct/object_set_sampler/object_set_sampler.hpp index e139fab..6001008 100644 --- a/include/geode/stochastic/sampling/direct/object_set_sampler/object_set_sampler.hpp +++ b/include/geode/stochastic/sampling/direct/object_set_sampler/object_set_sampler.hpp @@ -24,10 +24,13 @@ #pragma once #include + #include +#include namespace geode { + template < typename Type > class ObjectSetSampler { @@ -55,4 +58,12 @@ namespace geode protected: double log_pdf_{ LOG_PROB_INVALID }; }; + + template < typename ObjectType > + struct ObjectSamplerConfig; + + template < typename ObjectType > + std::unique_ptr< ObjectSetSampler< ObjectType > > build_objectset_sampler( + const SpatialDomain< ObjectType::dim >& domain, + const ObjectSamplerConfig< ObjectType >& config ); } // namespace geode \ No newline at end of file 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 5aa9d81..62c69bf 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 @@ -36,7 +36,8 @@ namespace geode class UniformPointSetSampler : public ObjectSetSampler< Point< dimension > > { public: - UniformPointSetSampler( const SpatialDomain< dimension >& domain ) + UniformPointSetSampler( const SpatialDomain< dimension >& domain, + const ObjectSamplerConfig< Point< dimension > >& config ) : ObjectSetSampler< Point< dimension > >{}, domain_( domain ) { auto volume = domain_.extended_n_volume(); @@ -45,7 +46,7 @@ namespace geode "[UniformPointSetSampler] Undefined Extended Bounding " "Box (volume ==0)." ); this->log_pdf_ = -std::log( volume ); - step_move_ = define_step_for_move(); + step_move_ = define_step_for_move( config.move_ratio ); OpenGeodeStochasticStochasticException::check_exception( step_move_ > 0., nullptr, OpenGeodeException::TYPE::data, "[UniformPointSetSampler] Undefined step length for move " @@ -83,9 +84,8 @@ namespace geode } private: - double define_step_for_move() + double define_step_for_move( double ratio ) { - double ratio = 0.1; return ratio * domain_.smallest_length(); } @@ -99,4 +99,20 @@ namespace geode double step_move_{ 0. }; }; + template < index_t dimension > + struct ObjectSamplerConfig< Point< dimension > > + { + // use to define the step for change move (move_ratio*domain volume) + double move_ratio = 0.1; + }; + + template < index_t dimension > + std::unique_ptr< ObjectSetSampler< Point2D > > build_objectset_sampler( + const SpatialDomain< 2 >& domain, + const ObjectSamplerConfig< Point< dimension > >& config ) + { + return std::make_unique< UniformPointSetSampler< 2 > >( + domain, config ); + } + } // namespace geode \ No newline at end of file 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 755a3bb..c7d58cc 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 @@ -37,12 +37,12 @@ namespace geode { public: UniformSegmentSetSampler( const SpatialDomain< 2 >& domain, - const DoubleSampler::Distribution& length, - const DoubleSampler::Distribution& azimuth ) + const ObjectSamplerConfig< Segment2D >& config ) : ObjectSetSampler< OwnerSegment2D >{}, domain_{ domain }, - length_{ length }, - azimuth_{ azimuth } + length_{ DoubleSampler::build_distribution( config.length ) }, + azimuth_{ DoubleSampler::build_distribution( config.azimuth ) }, + move_ratio_{ config.move_ratio } { auto volume = domain_.extended_n_volume(); OpenGeodeStochasticStochasticException::check_exception( @@ -62,14 +62,13 @@ namespace geode OwnerSegment2D change( const OwnerSegment2D& obj, RandomEngine& engine ) const override { - double ratio = 0.1; const auto& extremities = obj.vertices(); const auto current = static_cast< local_index_t >( engine.sample_bernoulli( 0.5 ) ); const auto other = 1 - current; geode::Sphere< 2 > ball{ extremities[current], - ratio * obj.length() }; + move_ratio_ * obj.length() }; auto new_point = PointUniformSampler::sample< 2 >( engine, ball ); constexpr index_t max_try{ 100 }; @@ -102,6 +101,24 @@ namespace geode const SpatialDomain< 2 >& domain_; DoubleSampler::Distribution length_; DoubleSampler::Distribution azimuth_; + double move_ratio_{ 0.1 }; }; + template <> + struct ObjectSamplerConfig< OwnerSegment2D > + { + double move_ratio = 0.1; + + DoubleSampler::DistributionDescription length; + DoubleSampler::DistributionDescription azimuth; + }; + + template <> + std::unique_ptr< ObjectSetSampler< OwnerSegment2D > > + build_objectset_sampler( const SpatialDomain< 2 >& domain, + const ObjectSamplerConfig< OwnerSegment2D >& config ) + { + return std::make_unique< UniformSegmentSetSampler >( domain, config ); + } + } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp index b46b438..538e312 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp @@ -69,13 +69,22 @@ namespace geode std::unique_ptr< geode::MetropolisHastings< ObjectType > > mh_sampler; }; + template < typename ObjectType > + struct ObjectSetDefinition + { + std::string name; + std::vector< ObjectType > fixed_objects; + + ObjectSamplerConfig< ObjectType > sampler; + ObjectSetDynamicsConfig dynamics; + }; + template < typename ObjectType > struct SimulationContextConfig { SpatialDomainConfig< ObjectType::dim > domain; - std::vector< ObjectSetConfig > sets; - std::vector< ObjectSetDynamicsConfig > proposals; + std::vector< ObjectSetDefinition< ObjectType > > sets; geode::ModelConfig model; }; @@ -95,28 +104,17 @@ namespace geode // ------------------------- // Sets // ------------------------- - - // auto proposal_kernel = - // std::make_unique< geode::ProposalKernel< geode::Point2D > - // >(); - // for( const auto& set_desc : set_descriptors_ ) - // { - // const auto set_id = context.object_sets->add_set( - // set_desc.name ); context.set_samplers.push_back( - // std::make_unique< geode::UniformPointSetSampler< 2 > - // >( - // *context.domain ) ); - // geode::add_birth_death_change_moves( - // context.set_samplers.back(), - // *proposal_kernel, set_id, set_desc.birth_ratio, - // set_desc.death_ratio, set_desc.change_ratio ); - // } - // return proposal_kernel; - - for( const auto& set_cfg : config.sets ) + auto proposal_kernel = + std::make_unique< geode::ProposalKernel< ObjectType > >(); + for( const auto& set_def : config.sets ) { - const auto set_id = context.object_sets->add_set( set_cfg.name ); - geode_unused( set_id ); + auto set_id = context.object_sets->add_set( set_def.name ); + auto sampler = + build_objectset_sampler( *context.domain, set_def.sampler ); + geode::add_birth_death_change_moves( *sampler, *proposal_kernel, + set_id, set_def.dynamics.birth_ratio, + set_def.dynamics.death_ratio, set_def.dynamics.change_ratio ); + context.set_samplers.emplace_back( std::move( sampler ) ); } // ------------------------- @@ -125,24 +123,6 @@ namespace geode context.model = geode::build_model< ObjectType >( config.model, *context.object_sets, *context.domain ); - // ------------------------- - // Proposal - // ------------------------- - auto proposal_kernel = - std::make_unique< geode::ProposalKernel< ObjectType > >(); - for( const auto& set_proposal : config.proposals ) - { - const auto set_id = - context.object_sets->get_set_uuid( set_proposal.name ); - context.set_samplers.push_back( - std::make_unique< geode::UniformPointSetSampler< 2 > >( - *context.domain ) ); - - geode::add_birth_death_change_moves( context.set_samplers.back(), - *proposal_kernel, set_id, set_proposal.birth_ratio, - set_proposal.death_ratio, set_proposal.change_ratio ); - } - // ------------------------- // MH sampler // ------------------------- diff --git a/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp b/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp index 73469e4..866b882 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/classical_proposals.hpp @@ -32,7 +32,7 @@ namespace geode { template < typename ObjectType > void add_birth_death_change_moves( - std::unique_ptr< geode::ObjectSetSampler< ObjectType > >& sampler, + const geode::ObjectSetSampler< ObjectType >& sampler, geode::ProposalKernel< ObjectType >& kernel, const uuid& set_id, double birth_ratio, @@ -48,13 +48,13 @@ namespace geode const auto p_birth = birth_ratio / total_ratio; kernel.add_move( set_id, std::make_unique< geode::BirthDeathMove< ObjectType > >( - *sampler, total_ratio, p_birth ) ); + sampler, total_ratio, p_birth ) ); if( change_ratio > 0. ) { kernel.add_move( set_id, std::make_unique< geode::ChangeMove< ObjectType > >( - *sampler, change_ratio ) ); + sampler, change_ratio ) ); } } diff --git a/include/geode/stochastic/spatial/object_sets.hpp b/include/geode/stochastic/spatial/object_sets.hpp index a5c39be..584ee8c 100644 --- a/include/geode/stochastic/spatial/object_sets.hpp +++ b/include/geode/stochastic/spatial/object_sets.hpp @@ -46,15 +46,6 @@ namespace geode namespace geode { - struct ObjectSetConfig - { - std::string name; - - double birth_ratio = 1.0; - double death_ratio = 1.0; - double change_ratio = 1.0; - }; - template < typename Type > class ObjectSets { diff --git a/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp b/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp index 71aaf69..dae7ab8 100644 --- a/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp +++ b/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp @@ -58,7 +58,8 @@ namespace box.add_point( max_point ); geode::SpatialDomain domain( box, 0. ); - geode::UniformPointSetSampler< 2 > sampler( domain ); + geode::ObjectSamplerConfig< geode::Point2D > sampler_config; + geode::UniformPointSetSampler< 2 > sampler( domain, sampler_config ); // Create classical birth-death-change kernel auto kernel = geode::create_birth_death_change_kernel< geode::Point2D >( diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 679ee19..cb58211 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -59,20 +59,17 @@ namespace for( const auto config : geode::Range{ birth_ratio.size() } ) { geode::SimulationContextConfig< geode::Point2D > poisson_config; - std::vector< geode::TargetStatisticConfig > - targeted_statistics_descriptors; poisson_config.domain = { geode::Point2D{ { 0.0, 0.0 } }, geode::Point2D{ { 10.0, 10.0 } }, 0. }; // --- Set description - geode::ObjectSetConfig set_a; + geode::ObjectSetDefinition< geode::Point2D > set_a; set_a.name = "A"; - - geode::ObjectSetDynamicsConfig dyn_a; - dyn_a.name = "A"; - dyn_a.birth_ratio = birth_ratio[config]; - dyn_a.death_ratio = 1.0; - dyn_a.change_ratio = change_ratio[config]; + set_a.sampler = geode::ObjectSamplerConfig< geode::Point2D >{}; + set_a.dynamics.birth_ratio = birth_ratio[config]; + set_a.dynamics.death_ratio = 1.0; + set_a.dynamics.change_ratio = change_ratio[config]; + poisson_config.sets.emplace_back( set_a ); // --- Energy term description PoissonDensityDescription density_a; @@ -80,12 +77,6 @@ namespace density_a.object_set_names = { "A" }; density_a.lambda = 0.3; density_a.object_feature = geode::ObjectInDomainFeatureConfig{}; - - geode::TargetStatisticConfig stat_a{ "density", 30.0, 0.15 }; - targeted_statistics_descriptors.push_back( stat_a ); - - poisson_config.sets.emplace_back( set_a ); - poisson_config.proposals.emplace_back( dyn_a ); poisson_config.model.terms.emplace_back( density_a ); auto context = build_simulation_context( poisson_config ); @@ -93,18 +84,25 @@ namespace context ) }; // run simulation - geode::SimulationPrinterConfigurator printer_config; - printer_config.output_folder = - absl::StrCat( printer_config.output_folder, - "/sim_point_poisson_test_", config ); geode::SimulationConfigurator sim_config; sim_config.realizations = 1000; sim_config.metropolis_hasting_steps = 1000; sim_config.burn_in_steps = 1000; + + geode::SimulationPrinterConfigurator printer_config; + printer_config.output_folder = + absl::StrCat( printer_config.output_folder, + "/sim_point_poisson_test_", config ); sim_config.printer = printer_config; auto statistic_tracker = runner.run( engine, sim_config ); + + std::vector< geode::TargetStatisticConfig > + targeted_statistics_descriptors; + geode::TargetStatisticConfig stat_a{ "density", 30.0, 0.15 }; + targeted_statistics_descriptors.push_back( stat_a ); + geode::TargetStatistics target_stats{ runner.model(), targeted_statistics_descriptors }; geode::statistics::validate( statistic_tracker, target_stats ); @@ -128,20 +126,29 @@ namespace geode::Point2D{ { 10.0, 10.0 } }, 0. }; // --- Set descriptions - geode::ObjectSetConfig set01{ "set01" }; - geode::ObjectSetConfig set02{ "set02" }; - geode::ObjectSetConfig set03{ "set03" }; + geode::ObjectSetDefinition< geode::Point2D > set01; + set01.name = "set01"; + set01.sampler = geode::ObjectSamplerConfig< geode::Point2D >{}; + set01.dynamics.birth_ratio = 2.0; + set01.dynamics.death_ratio = 3.0; + set01.dynamics.change_ratio = 1.0; poisson_config.sets.emplace_back( set01 ); + + geode::ObjectSetDefinition< geode::Point2D > set02; + set02.name = "set02"; + set02.sampler = geode::ObjectSamplerConfig< geode::Point2D >{}; + set02.dynamics.birth_ratio = 3.0; + set02.dynamics.death_ratio = 0.5; + set02.dynamics.change_ratio = 1.0; poisson_config.sets.emplace_back( set02 ); - poisson_config.sets.emplace_back( set03 ); - // --- proposal descriptions - geode::ObjectSetDynamicsConfig dyn01{ "set01", 2.0, 3.0, 1.0 }; - geode::ObjectSetDynamicsConfig dyn02{ "set02", 3.0, 0.5, 1.0 }; - geode::ObjectSetDynamicsConfig dyn03{ "set03", 4.0, 1.0, 1.0 }; - poisson_config.proposals.emplace_back( dyn01 ); - poisson_config.proposals.emplace_back( dyn02 ); - poisson_config.proposals.emplace_back( dyn03 ); + geode::ObjectSetDefinition< geode::Point2D > set03; + set03.name = "set03"; + set03.sampler = geode::ObjectSamplerConfig< geode::Point2D >{}; + set03.dynamics.birth_ratio = 4.0; + set03.dynamics.death_ratio = 1.0; + set03.dynamics.change_ratio = 1.0; + poisson_config.sets.emplace_back( set03 ); // --- Energy term descriptions PoissonDensityDescription density01; @@ -149,20 +156,20 @@ namespace density01.object_set_names = { "set01" }; density01.lambda = 0.1; density01.object_feature = geode::ObjectInDomainFeatureConfig{}; + poisson_config.model.terms.emplace_back( density01 ); PoissonDensityDescription density02; density02.term_name = "density02"; density02.object_set_names = { "set02" }; density02.lambda = 0.4; density02.object_feature = geode::ObjectInDomainFeatureConfig{}; + poisson_config.model.terms.emplace_back( density02 ); PoissonDensityDescription density03; density03.term_name = "density03"; density03.object_set_names = { "set03" }; density03.lambda = 0.3; density03.object_feature = geode::ObjectInDomainFeatureConfig{}; - poisson_config.model.terms.emplace_back( density01 ); - poisson_config.model.terms.emplace_back( density02 ); poisson_config.model.terms.emplace_back( density03 ); auto context = build_simulation_context( poisson_config ); @@ -170,14 +177,14 @@ namespace context ) }; // run simulation - geode::SimulationPrinterConfigurator printer_config; - printer_config.output_folder = absl::StrCat( - printer_config.output_folder, "/sim_point_multitype_poisson_test" ); - geode::SimulationConfigurator sim_config; sim_config.realizations = 1500; sim_config.metropolis_hasting_steps = 1000; sim_config.burn_in_steps = 3000; + + geode::SimulationPrinterConfigurator printer_config; + printer_config.output_folder = absl::StrCat( + printer_config.output_folder, "/sim_point_multitype_poisson_test" ); sim_config.printer = printer_config; auto statistic_tracker = runner.run( engine, sim_config ); From 700f13531540166fd99463aae12db237eafaf4ff Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Fri, 29 May 2026 17:31:44 +0200 Subject: [PATCH 48/59] fix strauss test --- tests/stochastic/CMakeLists.txt | 14 +- .../sampling/mcmc/test-mh-poisson.cpp | 22 +- .../sampling/mcmc/test-mh-strauss.cpp | 382 +++++++----------- 3 files changed, 163 insertions(+), 255 deletions(-) diff --git a/tests/stochastic/CMakeLists.txt b/tests/stochastic/CMakeLists.txt index a8b6661..0b16bcb 100644 --- a/tests/stochastic/CMakeLists.txt +++ b/tests/stochastic/CMakeLists.txt @@ -169,13 +169,13 @@ add_geode_test( ${PROJECT_NAME}::stochastic ) -# add_geode_test( -# SOURCE "sampling/mcmc/test-mh-strauss.cpp" -# DEPENDENCIES -# OpenGeode::basic -# OpenGeode::geometry -# ${PROJECT_NAME}::stochastic -# ) +add_geode_test( + SOURCE "sampling/mcmc/test-mh-strauss.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) add_geode_test( diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index cb58211..a41bb83 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp @@ -25,20 +25,10 @@ #include #include -#include -#include -#include - -#include - #include -#include -#include #include -#include #include -#include namespace { @@ -86,8 +76,8 @@ namespace // run simulation geode::SimulationConfigurator sim_config; - sim_config.realizations = 1000; - sim_config.metropolis_hasting_steps = 1000; + sim_config.realizations = 2000; + sim_config.metropolis_hasting_steps = 100; sim_config.burn_in_steps = 1000; geode::SimulationPrinterConfigurator printer_config; @@ -102,9 +92,9 @@ namespace targeted_statistics_descriptors; geode::TargetStatisticConfig stat_a{ "density", 30.0, 0.15 }; targeted_statistics_descriptors.push_back( stat_a ); - geode::TargetStatistics target_stats{ runner.model(), targeted_statistics_descriptors }; + geode::statistics::validate( statistic_tracker, target_stats ); } // NOLINTEND(*-magic-numbers) @@ -178,9 +168,9 @@ namespace // run simulation geode::SimulationConfigurator sim_config; - sim_config.realizations = 1500; - sim_config.metropolis_hasting_steps = 1000; - sim_config.burn_in_steps = 3000; + sim_config.realizations = 2000; + sim_config.metropolis_hasting_steps = 100; + sim_config.burn_in_steps = 1000; geode::SimulationPrinterConfigurator printer_config; printer_config.output_folder = absl::StrCat( diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index 5f09044..823360a 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -21,128 +21,20 @@ * */ #include -#include -#include -#include -#include -#include -#include + +#include +#include + +#include #include + #include -// #include -#include namespace { - struct SetDescription - { - std::string name; - double birth_ratio{ 1.0 }; - double death_ratio{ 1.0 }; - double change_ratio{ 1.0 }; - }; - using PoissonDensityDescription = geode::SingleObjectTermConfig; using PairwiseInteractionDescription = geode::PairwiseTermConfig; - class StraussSimulationRunner - : public geode::SimulationRunner< geode::Point2D > - { - public: - explicit StraussSimulationRunner( - const geode::SpatialDomain< 2 >& domain ) - : geode::SimulationRunner< geode::Point2D >( domain ) - { - } - - void add_set_descriptor( const SetDescription& descriptor ) - { - set_descriptors_.push_back( descriptor ); - } - - void add_density_descriptor( - const PoissonDensityDescription& descriptor ) - { - density_descriptors_.push_back( descriptor ); - } - - void add_interaction_descriptor( - const PairwiseInteractionDescription& descriptor ) - { - interaction_descriptors_.push_back( descriptor ); - } - - void add_target_statistics( - const geode::TargetStatisticConfig& statistic_descriptor ) - { - targeted_statistics_descriptors_.push_back( statistic_descriptor ); - } - std::unique_ptr< geode::ProposalKernel< geode::Point2D > > - create_sets_and_set_samplers() - { - auto proposal_kernel = - std::make_unique< geode::ProposalKernel< geode::Point2D > >(); - - for( const auto& set_desc : set_descriptors_ ) - { - const auto set_id = this->object_sets_.add_set( set_desc.name ); - this->set_samplers_.push_back( - std::make_unique< geode::UniformPointSetSampler< 2 > >( - domain_ ) ); - - geode::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 ); - } - return proposal_kernel; - } - - void create_model() - { - geode::ModelConfig config; - for( const auto& energy_desc : density_descriptors_ ) - { - config.terms.emplace_back( energy_desc ); - } - for( const auto& interaction_desc : interaction_descriptors_ ) - { - config.terms.emplace_back( interaction_desc ); - } - - model_ = std::move( geode::build_model< geode::Point2D >( - config, object_sets_, domain_ ) ); - create_target_statistics(); - } - - void create_target_statistics() - { - target_statistics_.emplace( *model_ ); - for( const auto& target_stat : targeted_statistics_descriptors_ ) - { - target_statistics_->set_target( target_stat ); - } - } - - void initialize() override - { - auto proposal_kernel = create_sets_and_set_samplers(); - create_model(); - - this->mh_sampler_ = - std::make_unique< geode::MetropolisHastings< geode::Point2D > >( - *model_, std::move( proposal_kernel ) ); - create_target_statistics(); - } - - private: - std::vector< SetDescription > set_descriptors_; - std::vector< PoissonDensityDescription > density_descriptors_; - std::vector< PairwiseInteractionDescription > interaction_descriptors_; - - std::vector< geode::TargetStatisticConfig > - targeted_statistics_descriptors_; - }; - void test_single_type_strauss() { geode::Logger::info( @@ -150,71 +42,77 @@ namespace geode::RandomEngine engine; engine.set_seed( "@mh-test-single-STRAUSS@" ); - // NOLINTBEGIN(*-magic-numbers) - geode::BoundingBox2D box; - box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); - box.add_point( geode::Point2D{ { 10.0, 10.0 } } ); - geode::SpatialDomain domain( box, 1. ); + // NOLINTBEGIN(*-magic-numbers) std::array< double, 5 > gamma_values{ 0, 0.3, 0.5, 0.7, 1.0 }; std::array< double, 5 > nb_points{ 19.5, 24.4, 31.3, 36.1, 50. }; std::array< double, 5 > nb_interactions{ 0, 4.7, 9.8, 18.7, 50.3 }; for( const auto config : geode::Range{ gamma_values.size() } ) { + geode::SimulationContextConfig< geode::Point2D > + simulation_cxt_config; + + simulation_cxt_config.domain = { geode::Point2D{ { 0.0, 0.0 } }, + geode::Point2D{ { 10.0, 10.0 } }, 1. }; + // --- Object set - SetDescription set_a; + geode::ObjectSetDefinition< geode::Point2D > set_a; set_a.name = "A"; - set_a.birth_ratio = 1.0; - set_a.death_ratio = 1.0; - set_a.change_ratio = 1.0; + set_a.sampler = geode::ObjectSamplerConfig< geode::Point2D >{}; + set_a.dynamics.birth_ratio = 1.0; + set_a.dynamics.death_ratio = 1.0; + set_a.dynamics.change_ratio = 1.0; + simulation_cxt_config.sets.emplace_back( set_a ); - // --- Energy term description + // --- Model - Energy term description PoissonDensityDescription density_a; density_a.term_name = "density_a"; density_a.object_set_names = { "A" }; density_a.lambda = 0.5; density_a.object_feature = geode::ObjectInDomainFeatureConfig{}; - - geode::TargetStatisticConfig stat_a{ "density_a", nb_points[config], - 0.1 }; + simulation_cxt_config.model.terms.emplace_back( density_a ); // --- Intra-set pairwise interaction (Strauss process) PairwiseInteractionDescription interaction_a; interaction_a.term_name = "interactionA"; interaction_a.object_set_names_interactions = { { "A", "A" } }; interaction_a.gamma = gamma_values[config]; - interaction_a.interaction_config = geode::MinimalDistanceCutoffConfig{ 1. }; /// ici cela devrait etre un paramètre utilisateur + simulation_cxt_config.model.terms.emplace_back( interaction_a ); - geode::TargetStatisticConfig stat_intra_a{ "interactionA", - nb_interactions[config], 0.1 }; - - StraussSimulationRunner runner( domain ); - runner.add_set_descriptor( set_a ); - runner.add_density_descriptor( density_a ); - runner.add_interaction_descriptor( interaction_a ); - runner.add_target_statistics( stat_a ); - runner.add_target_statistics( stat_intra_a ); - runner.initialize(); + auto context = build_simulation_context( simulation_cxt_config ); + geode::SimulationRunner< geode::Point2D > runner{ std::move( + context ) }; // run simulation + geode::SimulationConfigurator sim_config; + sim_config.realizations = 2000; + sim_config.metropolis_hasting_steps = 100; + sim_config.burn_in_steps = 1000; + geode::SimulationPrinterConfigurator printer_config; printer_config.output_folder = absl::StrCat( printer_config.output_folder, "/sim_point_strauss_test_", config ); - - geode::SimulationConfigurator sim_config; - sim_config.realizations = 1000; - sim_config.metropolis_hasting_steps = 1000; - sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; auto statistic_tracker = runner.run( engine, sim_config ); - geode::statistics::validate( - statistic_tracker, runner.target_statistics() ); + + std::vector< geode::TargetStatisticConfig > + targeted_statistics_descriptors; + geode::TargetStatisticConfig stat_a{ "density_a", nb_points[config], + 0.1 }; + geode::TargetStatisticConfig stat_intra_a{ "interactionA", + nb_interactions[config], 0.1 }; + targeted_statistics_descriptors.push_back( stat_a ); + targeted_statistics_descriptors.push_back( stat_intra_a ); + geode::TargetStatistics target_stats{ runner.model(), + targeted_statistics_descriptors }; + + geode::statistics::validate( statistic_tracker, target_stats ); } // NOLINTEND(*-magic-numbers) geode::Logger::info( "--> SUCCESS!" ); @@ -223,16 +121,12 @@ namespace void test_multitype_strauss() { geode::Logger::info( - "TEST - MH MULTITYPE STRAUSS (with inter-set interactions)" ); + "TEST - MH MULTITYPE STRAUSS (with inter-set interactions ) " ); geode::RandomEngine engine; engine.set_seed( "@mh-test-multi-STRAUSS@" ); - // NOLINTBEGIN(*-magic-numbers) - geode::BoundingBox2D box; - box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); - box.add_point( geode::Point2D{ { 10.0, 10.0 } } ); - geode::SpatialDomain domain( box, 2. ); + // NOLINTBEGIN(*-magic-numbers) std::array< double, 3 > gamma_values{ 0, 0.5, 1.0 }; std::array< double, 3 > nb_points01{ 6.7, 8, 10.0 }; std::array< double, 3 > nb_points02{ 17.5, 24.6, 40.0 }; @@ -241,101 +135,125 @@ namespace std::array< double, 3 > nb_interactions02{ 37.2, 70, 174 }; for( const auto config : geode::Range{ gamma_values.size() } ) { - // --- Sets - SetDescription set01{ "set01", 1.0, 3.0, 1.0 }; - SetDescription set02{ "set02", 3.0, 0.5, 1.0 }; - SetDescription set03{ "set03", 4.0, 1.0, 1.0 }; - - // --- Density terms - PoissonDensityDescription d01; - d01.term_name = "density_set01"; - d01.object_set_names = { "set01" }; - d01.lambda = 0.1; - d01.object_feature = geode::ObjectInDomainFeatureConfig{}; - - geode::TargetStatisticConfig stat01{ "density_set01", - nb_points01[config], 0.1 }; - - PoissonDensityDescription d02; - d02.term_name = "density_set02"; - d02.object_set_names = { "set02" }; - d02.lambda = 0.4; - d02.object_feature = geode::ObjectInDomainFeatureConfig{}; + geode::SimulationContextConfig< geode::Point2D > + simulation_cxt_config; - geode::TargetStatisticConfig stat02{ "density_set02", - nb_points02[config], 0.1 }; - - PoissonDensityDescription d03; - d03.term_name = "density_set03"; - d03.object_set_names = { "set03" }; - d03.lambda = 0.3; - d03.object_feature = geode::ObjectInDomainFeatureConfig{}; + simulation_cxt_config.domain = { geode::Point2D{ { 0.0, 0.0 } }, + geode::Point2D{ { 10.0, 10.0 } }, 2. }; - geode::TargetStatisticConfig stat03{ "density_set03", - nb_points03[config], 0.1 }; + // --- Object set + geode::ObjectSetDefinition< geode::Point2D > set01; + set01.name = "set01"; + set01.sampler = geode::ObjectSamplerConfig< geode::Point2D >{}; + set01.dynamics.birth_ratio = 1.0; + set01.dynamics.death_ratio = 3.0; + set01.dynamics.change_ratio = 1.0; + simulation_cxt_config.sets.emplace_back( set01 ); + + geode::ObjectSetDefinition< geode::Point2D > set02; + set02.name = "set02"; + set02.sampler = geode::ObjectSamplerConfig< geode::Point2D >{}; + set02.dynamics.birth_ratio = 3.0; + set02.dynamics.death_ratio = 0.5; + set02.dynamics.change_ratio = 1.0; + simulation_cxt_config.sets.emplace_back( set02 ); + + geode::ObjectSetDefinition< geode::Point2D > set03; + set03.name = "set03"; + set03.sampler = geode::ObjectSamplerConfig< geode::Point2D >{}; + set03.dynamics.birth_ratio = 4.0; + set03.dynamics.death_ratio = 1.0; + set03.dynamics.change_ratio = 1.0; + simulation_cxt_config.sets.emplace_back( set03 ); + + // --- Model - Energy term description + // density + PoissonDensityDescription density01; + density01.term_name = "density01"; + density01.object_set_names = { "set01" }; + density01.lambda = 0.1; + density01.object_feature = geode::ObjectInDomainFeatureConfig{}; + simulation_cxt_config.model.terms.emplace_back( density01 ); + + PoissonDensityDescription density02; + density02.term_name = "density02"; + density02.object_set_names = { "set02" }; + density02.lambda = 0.4; + density02.object_feature = geode::ObjectInDomainFeatureConfig{}; + simulation_cxt_config.model.terms.emplace_back( density02 ); + + PoissonDensityDescription density03; + density03.term_name = "density03"; + density03.object_set_names = { "set03" }; + density03.lambda = 0.3; + density03.object_feature = geode::ObjectInDomainFeatureConfig{}; + simulation_cxt_config.model.terms.emplace_back( density03 ); // --- Pairwise interactions // 1. Intra-type (repulsion within same set) - PairwiseInteractionDescription intra01; - intra01.term_name = "interaction01"; - intra01.object_set_names_interactions = { { "set01", "set01" }, - { "set02", "set02" }, { "set03", "set03" } }; - intra01.gamma = gamma_values[config]; - - intra01.interaction_config = geode::MinimalDistanceCutoffConfig{ - 1. - }; /// ici cela devrait etre un paramètre utilisateur - - geode::TargetStatisticConfig stat_intra_01{ "interaction01", - nb_interactions01[config], 0.1 }; - - PairwiseInteractionDescription intra02; - intra02.term_name = "interaction02"; - intra02.object_set_names_interactions = { { "set02", "set02" } }; - intra02.gamma = 1.; // gamma_values[config]; - - intra02.interaction_config = geode::MinimalDistanceCutoffConfig{ - 2. - }; /// ici cela devrait etre un paramètre utilisateur - - geode::TargetStatisticConfig stat_intra_02{ "interaction02", - nb_interactions02[config], 0.1 }; - - StraussSimulationRunner runner( domain ); - runner.add_set_descriptor( set01 ); - runner.add_set_descriptor( set02 ); - runner.add_set_descriptor( set03 ); - - runner.add_density_descriptor( d01 ); - runner.add_density_descriptor( d02 ); - runner.add_density_descriptor( d03 ); - - runner.add_interaction_descriptor( intra01 ); - runner.add_interaction_descriptor( intra02 ); + PairwiseInteractionDescription interaction_01; + interaction_01.term_name = "interaction_01"; + interaction_01.object_set_names_interactions = { + { "set01", "set01" }, { "set02", "set02" }, { "set03", "set03" } + }; + interaction_01.gamma = gamma_values[config]; + interaction_01.interaction_config = + geode::MinimalDistanceCutoffConfig{ + 1. + }; /// ici cela devrait etre un paramètre utilisateur + simulation_cxt_config.model.terms.emplace_back( interaction_01 ); + + PairwiseInteractionDescription interaction_02; + interaction_02.term_name = "interaction_02"; + interaction_02.object_set_names_interactions = { { "set02", + "set02" } }; + interaction_02.gamma = 1.; + interaction_02.interaction_config = + geode::MinimalDistanceCutoffConfig{ + 2. + }; /// ici cela devrait etre un paramètre utilisateur + simulation_cxt_config.model.terms.emplace_back( interaction_02 ); - runner.add_target_statistics( stat01 ); - runner.add_target_statistics( stat02 ); - runner.add_target_statistics( stat03 ); - runner.add_target_statistics( stat_intra_01 ); - runner.add_target_statistics( stat_intra_02 ); + // run simulation + auto context = build_simulation_context( simulation_cxt_config ); + geode::SimulationRunner< geode::Point2D > runner{ std::move( + context ) }; - runner.initialize(); + geode::SimulationConfigurator sim_config; + sim_config.realizations = 2000; + sim_config.metropolis_hasting_steps = 100; + sim_config.burn_in_steps = 1000; - // run simulation geode::SimulationPrinterConfigurator printer_config; printer_config.output_folder = absl::StrCat( printer_config.output_folder, "/sim_point_multitype_strauss_test" ); - - geode::SimulationConfigurator sim_config; - sim_config.realizations = 600; - sim_config.metropolis_hasting_steps = 750; - sim_config.burn_in_steps = 5000; sim_config.printer = printer_config; auto statistic_tracker = runner.run( engine, sim_config ); - geode::statistics::validate( - statistic_tracker, runner.target_statistics() ); + + std::vector< geode::TargetStatisticConfig > + targeted_statistics_descriptors; + geode::TargetStatisticConfig stat01{ "density01", + nb_points01[config], 0.1 }; + geode::TargetStatisticConfig stat02{ "density02", + nb_points02[config], 0.1 }; + geode::TargetStatisticConfig stat03{ "density03", + nb_points03[config], 0.1 }; + geode::TargetStatisticConfig stat_intra_01{ "interaction_01", + nb_interactions01[config], 0.1 }; + geode::TargetStatisticConfig stat_intra_02{ "interaction_02", + nb_interactions02[config], 0.1 }; + targeted_statistics_descriptors.push_back( stat01 ); + targeted_statistics_descriptors.push_back( stat02 ); + targeted_statistics_descriptors.push_back( stat03 ); + targeted_statistics_descriptors.push_back( stat_intra_01 ); + targeted_statistics_descriptors.push_back( stat_intra_02 ); + + geode::TargetStatistics target_stats{ runner.model(), + targeted_statistics_descriptors }; + + geode::statistics::validate( statistic_tracker, target_stats ); } // NOLINTEND(*-magic-numbers) @@ -349,7 +267,7 @@ int main() { geode::OpenGeodeStochasticStochasticLibrary::initialize(); geode::Logger::set_level( geode::Logger::LEVEL::debug ); - test_single_type_strauss(); + // test_single_type_strauss(); test_multitype_strauss(); return 0; } From 6da78cfb17c6dc17fc10b9a58ebfbf31335bffbd Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 2 Jun 2026 12:44:31 +0200 Subject: [PATCH 49/59] feat(Helpers): add poisson and strauss helpers --- .../applications/poisson_process.hpp | 49 +++ .../applications/strauss_process.hpp | 60 ++++ .../object_set_sampler/point_set_sampler.hpp | 9 +- .../mcmc/helpers/simulation_context.hpp | 9 +- .../mcmc/metropolis_hasting_sampler.hpp | 27 +- .../segment_length_feature.hpp | 1 + src/geode/stochastic/CMakeLists.txt | 4 + .../applications/poisson_process.cpp | 75 +++++ .../applications/strauss_process.cpp | 138 +++++++++ tests/stochastic/CMakeLists.txt | 170 +---------- tests/stochastic/applications/CMakeLists.txt | 44 +++ .../test-mh-fractures.cpp | 0 .../applications/test-poisson-process.cpp | 163 ++++++++++ .../applications/test-strauss-process.cpp | 203 +++++++++++++ tests/stochastic/models/CMakeLists.txt | 59 ++++ tests/stochastic/sampling/CMakeLists.txt | 97 ++++++ .../sampling/mcmc/test-mh-poisson.cpp | 214 -------------- .../sampling/mcmc/test-mh-strauss.cpp | 278 ------------------ .../sampling/test-random-engine.cpp | 6 +- tests/stochastic/spatial/CMakeLists.txt | 51 ++++ 20 files changed, 980 insertions(+), 677 deletions(-) create mode 100644 include/geode/stochastic/applications/poisson_process.hpp create mode 100644 include/geode/stochastic/applications/strauss_process.hpp create mode 100644 src/geode/stochastic/applications/poisson_process.cpp create mode 100644 src/geode/stochastic/applications/strauss_process.cpp create mode 100644 tests/stochastic/applications/CMakeLists.txt rename tests/stochastic/{sampling/mcmc => applications}/test-mh-fractures.cpp (100%) create mode 100644 tests/stochastic/applications/test-poisson-process.cpp create mode 100644 tests/stochastic/applications/test-strauss-process.cpp create mode 100644 tests/stochastic/models/CMakeLists.txt create mode 100644 tests/stochastic/sampling/CMakeLists.txt delete mode 100644 tests/stochastic/sampling/mcmc/test-mh-poisson.cpp delete mode 100644 tests/stochastic/sampling/mcmc/test-mh-strauss.cpp create mode 100644 tests/stochastic/spatial/CMakeLists.txt diff --git a/include/geode/stochastic/applications/poisson_process.hpp b/include/geode/stochastic/applications/poisson_process.hpp new file mode 100644 index 0000000..c14106c --- /dev/null +++ b/include/geode/stochastic/applications/poisson_process.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +namespace geode +{ + template < typename ObjectType > + struct PoissonSetDescription + { + std::string set_name; + + ObjectSamplerConfig< ObjectType > sampler; + + std::string density_name; + double lambda; + std::optional< double > expected_nb_objects; + + double birth_ratio{ 1.0 }; + double death_ratio{ 1.0 }; + double change_ratio{ 1.0 }; + }; + + template < typename ObjectType > + struct PoissonProcessDescription + { + SpatialDomainConfig< ObjectType::dim > domain; + + std::vector< PoissonSetDescription< ObjectType > > sets; + + PoissonSetDescription< ObjectType >& add_set( + absl::string_view set_name, absl::string_view density_name ) + { + auto& set = sets.emplace_back(); + set.set_name = set_name; + set.density_name = density_name; + return set; + } + }; + + template < typename ObjectType > + SimulationContext< ObjectType > build_poisson_process( + const PoissonProcessDescription< ObjectType >& description ); + + template < typename ObjectType > + std::vector< geode::TargetStatisticConfig > build_poisson_targeted_stat( + const PoissonProcessDescription< ObjectType >& description ); + +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/applications/strauss_process.hpp b/include/geode/stochastic/applications/strauss_process.hpp new file mode 100644 index 0000000..ba84561 --- /dev/null +++ b/include/geode/stochastic/applications/strauss_process.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include + +namespace geode +{ + template < typename ObjectType > + struct StraussInteractionDescription + { + std::string interaction_name; + + std::vector< std::string > set_names; + + double gamma; + double distance; + + bool include_intra_set{ true }; + bool include_inter_set{ false }; + + std::optional< double > expected_nb_interactions; + }; + + template < typename ObjectType > + struct StraussProcessDescription + { + SpatialDomainConfig< ObjectType::dim > domain; + + std::vector< PoissonSetDescription< ObjectType > > sets; + + std::vector< StraussInteractionDescription< ObjectType > > interactions; + + PoissonSetDescription< ObjectType >& add_set( + absl::string_view set_name, absl::string_view density_name ) + { + auto& set = sets.emplace_back(); + set.set_name = set_name; + set.density_name = density_name; + return set; + } + + StraussInteractionDescription< ObjectType >& add_interaction( + absl::string_view interaction_name ) + { + auto& interaction = interactions.emplace_back(); + interaction.interaction_name = interaction_name; + return interaction; + } + }; + + template < typename ObjectType > + SimulationContext< ObjectType > build_strauss_process( + const StraussProcessDescription< ObjectType >& description ); + + template < typename ObjectType > + std::vector< geode::TargetStatisticConfig > build_strauss_targeted_stat( + const StraussProcessDescription< ObjectType >& description ); + +} // namespace geode \ No newline at end of file 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 62c69bf..2bcffaf 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 @@ -70,6 +70,7 @@ namespace geode constexpr index_t max_try{ 100 }; for( const auto try_id : geode::Range{ max_try } ) { + geode_unused( try_id ); if( domain_.extended_contains( new_point ) ) { return new_point; @@ -107,11 +108,11 @@ namespace geode }; template < index_t dimension > - std::unique_ptr< ObjectSetSampler< Point2D > > build_objectset_sampler( - const SpatialDomain< 2 >& domain, - const ObjectSamplerConfig< Point< dimension > >& config ) + std::unique_ptr< ObjectSetSampler< Point< dimension > > > + build_objectset_sampler( const SpatialDomain< dimension >& domain, + const ObjectSamplerConfig< Point< dimension > >& config ) { - return std::make_unique< UniformPointSetSampler< 2 > >( + return std::make_unique< UniformPointSetSampler< dimension > >( domain, config ); } diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp index 538e312..da4ae10 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp @@ -82,6 +82,13 @@ namespace geode template < typename ObjectType > struct SimulationContextConfig { + ObjectSetDefinition< ObjectType >& add_set( absl::string_view name ) + { + auto& set = sets.emplace_back(); + set.name = name; + return set; + } + SpatialDomainConfig< ObjectType::dim > domain; std::vector< ObjectSetDefinition< ObjectType > > sets; @@ -127,7 +134,7 @@ namespace geode // MH sampler // ------------------------- context.mh_sampler = - std::make_unique< geode::MetropolisHastings< geode::Point2D > >( + std::make_unique< geode::MetropolisHastings< ObjectType > >( *context.model, std::move( proposal_kernel ) ); return context; diff --git a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp index bf7d9d2..5ba8548 100644 --- a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp +++ b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp @@ -150,8 +150,8 @@ namespace geode } private: - const double compute_log_accept( const double deltaU, - const ProposalProbabilities& proposal_probas ) const + double compute_log_accept( + double deltaU, const ProposalProbabilities& proposal_probas ) const { return -beta_ * deltaU + proposal_probas.transition_probability(); } @@ -189,10 +189,11 @@ namespace geode const auto delta_log_energy = 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( - std::move( proposal.proposed_move.new_object.value() ), - proposal.set_id, false ); + []( auto& cur_state, auto& accepted_proposal ) { + cur_state.add_object( + std::move( accepted_proposal.proposed_move.new_object + .value() ), + accepted_proposal.set_id, false ); } ); }; @@ -204,8 +205,9 @@ namespace geode const auto delta_log_energy = 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() ); + []( auto& cur_state, auto& accepted_proposal ) { + cur_state.remove_free_object( + accepted_proposal.old_object_id() ); } ); }; @@ -218,10 +220,11 @@ namespace geode 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 ) { - state.update_free_object( proposal.old_object_id(), - std::move( - proposal.proposed_move.new_object.value() ) ); + []( auto& cur_state, auto& accepted_proposal ) { + cur_state.update_free_object( + accepted_proposal.old_object_id(), + std::move( accepted_proposal.proposed_move.new_object + .value() ) ); } ); }; 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 944065d..579d3d3 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 @@ -21,6 +21,7 @@ * USE OR OTHER DEALINGS IN THE SOFTWARE. * */ +#pragma once #include diff --git a/src/geode/stochastic/CMakeLists.txt b/src/geode/stochastic/CMakeLists.txt index e188247..7425bec 100644 --- a/src/geode/stochastic/CMakeLists.txt +++ b/src/geode/stochastic/CMakeLists.txt @@ -22,6 +22,8 @@ add_geode_library( NAME stochastic FOLDER "geode/stochastic" SOURCES + "applications/poisson_process.cpp" + "applications/strauss_process.cpp" "spatial/object_set.cpp" "spatial/object_sets.cpp" "spatial/object_neighborhood.cpp" @@ -37,6 +39,8 @@ add_geode_library( "sampling/random_engine.cpp" "common.cpp" PUBLIC_HEADERS + "applications/poisson_process.hpp" + "applications/strauss_process.hpp" #"inference/abc_shadow.hpp" "inference/statistics_tools.hpp" "inference/statistics_tracker.hpp" diff --git a/src/geode/stochastic/applications/poisson_process.cpp b/src/geode/stochastic/applications/poisson_process.cpp new file mode 100644 index 0000000..f0ef580 --- /dev/null +++ b/src/geode/stochastic/applications/poisson_process.cpp @@ -0,0 +1,75 @@ +#pragma once + +#include + +namespace geode +{ + using PoissonDensityDescription = geode::SingleObjectTermConfig; + + template < typename ObjectType > + SimulationContext< ObjectType > build_poisson_process( + const PoissonProcessDescription< ObjectType >& desc ) + { + SimulationContextConfig< ObjectType > config; + + config.domain = desc.domain; + + for( const auto& set_desc : desc.sets ) + { + auto& set = config.add_set( set_desc.set_name ); + + set.sampler = set_desc.sampler; + + set.dynamics.birth_ratio = set_desc.birth_ratio; + set.dynamics.death_ratio = set_desc.death_ratio; + set.dynamics.change_ratio = set_desc.change_ratio; + + PoissonDensityDescription density; + + density.term_name = set_desc.density_name; + density.object_set_names = { set_desc.set_name }; + density.lambda = set_desc.lambda; + density.object_feature = ObjectInDomainFeatureConfig{}; + + config.model.terms.emplace_back( std::move( density ) ); + } + + return build_simulation_context( config ); + } + + template opengeode_stochastic_stochastic_api SimulationContext< Point2D > + build_poisson_process< Point2D >( + const PoissonProcessDescription< Point2D >& ); + template opengeode_stochastic_stochastic_api SimulationContext< Point3D > + build_poisson_process< Point3D >( + const PoissonProcessDescription< Point3D >& ); + + template < typename ObjectType > + std::vector< geode::TargetStatisticConfig > build_poisson_targeted_stat( + const PoissonProcessDescription< ObjectType >& description ) + { + std::vector< geode::TargetStatisticConfig > targets; + + for( const auto& set_desc : description.sets ) + { + if( !set_desc.expected_nb_objects ) + { + continue; + } + + targets.push_back( geode::TargetStatisticConfig{ + set_desc.density_name, *set_desc.expected_nb_objects, 0.1 } ); + } + + return targets; + } + + template opengeode_stochastic_stochastic_api + std::vector< geode::TargetStatisticConfig > + build_poisson_targeted_stat< Point2D >( + const PoissonProcessDescription< Point2D >& ); + template opengeode_stochastic_stochastic_api + std::vector< geode::TargetStatisticConfig > + build_poisson_targeted_stat< Point3D >( + const PoissonProcessDescription< Point3D >& ); +} // namespace geode \ No newline at end of file diff --git a/src/geode/stochastic/applications/strauss_process.cpp b/src/geode/stochastic/applications/strauss_process.cpp new file mode 100644 index 0000000..105df8f --- /dev/null +++ b/src/geode/stochastic/applications/strauss_process.cpp @@ -0,0 +1,138 @@ +#pragma once + +#include + +namespace +{ + template < typename ObjectType > + std::vector< std::pair< std::string, std::string > > + build_interaction_set_names( + const geode::StraussInteractionDescription< ObjectType >& + interaction_desc ) + { + std::vector< std::pair< std::string, std::string > > interactions; + + const auto& set_names = interaction_desc.set_names; + + if( set_names.empty() + || ( !interaction_desc.include_intra_set + && !interaction_desc.include_inter_set ) ) + { + return interactions; + } + + for( const auto name1_id : geode::Range{ set_names.size() } ) + { + if( interaction_desc.include_intra_set ) + { + interactions.emplace_back( + set_names[name1_id], set_names[name1_id] ); + } + + if( interaction_desc.include_inter_set ) + { + for( const auto name2_id : + geode::Range{ name1_id, set_names.size() } ) + { + interactions.emplace_back( + set_names[name1_id], set_names[name2_id] ); + } + } + } + + return interactions; + } +} // namespace + +namespace geode +{ + using DensityDescription = geode::SingleObjectTermConfig; + using InteractionDescription = geode::PairwiseTermConfig; + + template < typename ObjectType > + SimulationContext< ObjectType > build_strauss_process( + const StraussProcessDescription< ObjectType >& desc ) + { + SimulationContextConfig< ObjectType > config; + + config.domain = desc.domain; + + for( const auto& set_desc : desc.sets ) + { + auto& set = config.add_set( set_desc.set_name ); + + set.sampler = set_desc.sampler; + + set.dynamics.birth_ratio = set_desc.birth_ratio; + set.dynamics.death_ratio = set_desc.death_ratio; + set.dynamics.change_ratio = set_desc.change_ratio; + + DensityDescription density; + + density.term_name = set_desc.density_name; + density.object_set_names = { set_desc.set_name }; + density.lambda = set_desc.lambda; + density.object_feature = ObjectInDomainFeatureConfig{}; + config.model.terms.emplace_back( std::move( density ) ); + } + + for( const auto& interaction_desc : desc.interactions ) + { + InteractionDescription interaction; + interaction.term_name = interaction_desc.interaction_name; + interaction.object_set_names_interactions = + std::move( build_interaction_set_names( interaction_desc ) ); + interaction.gamma = interaction_desc.gamma; + interaction.interaction_config = + geode::MinimalDistanceCutoffConfig{ interaction_desc.distance }; + config.model.terms.emplace_back( std::move( interaction ) ); + } + + return build_simulation_context( config ); + } + + template opengeode_stochastic_stochastic_api SimulationContext< Point2D > + build_strauss_process< Point2D >( + const StraussProcessDescription< Point2D >& ); + template opengeode_stochastic_stochastic_api SimulationContext< Point3D > + build_strauss_process< Point3D >( + const StraussProcessDescription< Point3D >& ); + + template < typename ObjectType > + std::vector< geode::TargetStatisticConfig > build_strauss_targeted_stat( + const StraussProcessDescription< ObjectType >& description ) + { + std::vector< geode::TargetStatisticConfig > targets; + + for( const auto& set_desc : description.sets ) + { + if( !set_desc.expected_nb_objects ) + { + continue; + } + targets.push_back( geode::TargetStatisticConfig{ + set_desc.density_name, *set_desc.expected_nb_objects, 0.1 } ); + } + for( const auto& inter_desc : description.interactions ) + { + if( !inter_desc.expected_nb_interactions ) + { + continue; + } + targets.push_back( + geode::TargetStatisticConfig{ inter_desc.interaction_name, + *inter_desc.expected_nb_interactions, 0.1 } ); + } + + return targets; + } + + template opengeode_stochastic_stochastic_api + std::vector< geode::TargetStatisticConfig > + build_strauss_targeted_stat< Point2D >( + const StraussProcessDescription< Point2D >& ); + template opengeode_stochastic_stochastic_api + std::vector< geode::TargetStatisticConfig > + build_strauss_targeted_stat< Point3D >( + const StraussProcessDescription< Point3D >& ); +} // namespace geode \ No newline at end of file diff --git a/tests/stochastic/CMakeLists.txt b/tests/stochastic/CMakeLists.txt index 0b16bcb..d835bab 100644 --- a/tests/stochastic/CMakeLists.txt +++ b/tests/stochastic/CMakeLists.txt @@ -18,169 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -add_geode_test( - SOURCE "spatial/test-object-set.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) - -add_geode_test( - SOURCE "spatial/test-object-sets.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) - -add_geode_test( - SOURCE "spatial/test-pairwise-interactions.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) - -add_geode_test( - SOURCE "spatial/test-spatial-domain.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) - -add_geode_test( - SOURCE "sampling/direct/test-ball-sampler.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) - -add_geode_test( - SOURCE "sampling/direct/test-bounding-box-sampler.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) - -add_geode_test( - SOURCE "sampling/direct/test-double-sampler.cpp" - DEPENDENCIES - OpenGeode::basic - ${PROJECT_NAME}::stochastic -) - -add_geode_test( - SOURCE "sampling/direct/test-point-uniform-sampler.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) - -add_geode_test( - SOURCE "sampling/direct/test-segment-uniform-sampler.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 "models/energy_terms/test-density-term.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) - -add_geode_test( - SOURCE "models/energy_terms/test-intensity-term.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) - -add_geode_test( - SOURCE "models/energy_terms/test-pairwise-term.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" - 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" -# DEPENDENCIES -# OpenGeode::basic -# OpenGeode::geometry -# ${PROJECT_NAME}::stochastic -#) - - add_geode_test( - SOURCE "sampling/mcmc/test-mh-poisson.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic - ) - -add_geode_test( - SOURCE "sampling/mcmc/test-mh-strauss.cpp" - DEPENDENCIES - OpenGeode::basic - OpenGeode::geometry - ${PROJECT_NAME}::stochastic -) - - -add_geode_test( - SOURCE "sampling/test-random-engine.cpp" - DEPENDENCIES - OpenGeode::basic - ${PROJECT_NAME}::stochastic -) +add_subdirectory(applications) +add_subdirectory(models) +add_subdirectory(sampling) +add_subdirectory(spatial) \ No newline at end of file diff --git a/tests/stochastic/applications/CMakeLists.txt b/tests/stochastic/applications/CMakeLists.txt new file mode 100644 index 0000000..7ae7d2b --- /dev/null +++ b/tests/stochastic/applications/CMakeLists.txt @@ -0,0 +1,44 @@ +# 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. + +#add_geode_test( +# SOURCE "sampling/mcmc/test-mh-fractures.cpp" +# DEPENDENCIES +# OpenGeode::basic +# OpenGeode::geometry +# ${PROJECT_NAME}::stochastic +#) + +add_geode_test( + SOURCE "test-poisson-process.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic + ) + +add_geode_test( + SOURCE "test-strauss-process.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) + diff --git a/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp b/tests/stochastic/applications/test-mh-fractures.cpp similarity index 100% rename from tests/stochastic/sampling/mcmc/test-mh-fractures.cpp rename to tests/stochastic/applications/test-mh-fractures.cpp diff --git a/tests/stochastic/applications/test-poisson-process.cpp b/tests/stochastic/applications/test-poisson-process.cpp new file mode 100644 index 0000000..acc8e2a --- /dev/null +++ b/tests/stochastic/applications/test-poisson-process.cpp @@ -0,0 +1,163 @@ +/* + * 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 + +#include + +namespace +{ + + void test_single_type_poisson() + { + geode::Logger::info( "TEST - MH SINGLE TYPE POISSON" ); + + geode::RandomEngine engine; + engine.set_seed( "@mh-test-single-POISSON@" ); + + // NOLINTBEGIN(*-magic-numbers) + std::array< double, 4 > birth_ratio{ 0.1, 0.5, 2., 4. }; + std::array< double, 4 > change_ratio{ 0., 1., 1., 0. }; + + for( const auto config : geode::Range{ birth_ratio.size() } ) + { + geode::PoissonProcessDescription< geode::Point2D > poisson; + poisson.domain = { geode::Point2D{ { 0, 0 } }, + geode::Point2D{ { 10, 10 } }, 0. }; + auto& set_config = poisson.add_set( "set_A", "density_A" ); + set_config.lambda = 0.3; + set_config.expected_nb_objects = 30; + set_config.birth_ratio = birth_ratio[config]; + set_config.death_ratio = 1.0; + set_config.change_ratio = change_ratio[config]; + + auto simulation_context = build_poisson_process( poisson ); + geode::SimulationRunner< geode::Point2D > runner{ std::move( + simulation_context ) }; + + // run simulation + + geode::SimulationConfigurator sim_config; + sim_config.realizations = 2000; + sim_config.metropolis_hasting_steps = 100; + sim_config.burn_in_steps = 1000; + + geode::SimulationPrinterConfigurator printer_config; + printer_config.output_folder = + absl::StrCat( printer_config.output_folder, + "/sim_point_poisson_test_", config ); + sim_config.printer = printer_config; + + auto statistic_tracker = runner.run( engine, sim_config ); + + const auto targeted_statistics_descriptors = + build_poisson_targeted_stat( poisson ); + geode::TargetStatistics target_stats{ runner.model(), + targeted_statistics_descriptors }; + + geode::statistics::validate( statistic_tracker, target_stats ); + } + // NOLINTEND(*-magic-numbers) + + geode::Logger::info( "--> SUCCESS!" ); + } + + void test_multitype_poisson() + { + geode::Logger::info( "TEST - MH MULTITYPE POISSON" ); + + geode::RandomEngine engine; + engine.set_seed( "@mh-test-POISSON-multi@" ); + + // NOLINTBEGIN(*-magic-numbers) + geode::PoissonProcessDescription< geode::Point2D > poisson; + poisson.domain = { geode::Point2D{ { 0, 0 } }, + geode::Point2D{ { 10, 10 } }, 0. }; + auto& set_config_01 = poisson.add_set( "set01", "density_01" ); + set_config_01.lambda = 0.1; + set_config_01.expected_nb_objects = 10; + set_config_01.birth_ratio = 2.0; + set_config_01.death_ratio = 3.0; + set_config_01.change_ratio = 1.0; + + auto& set_config_02 = poisson.add_set( "set02", "density_02" ); + set_config_02.lambda = 0.4; + set_config_01.expected_nb_objects = 40; + set_config_02.birth_ratio = 3.0; + set_config_02.death_ratio = 0.5; + set_config_02.change_ratio = 1.0; + + auto& set_config_03 = poisson.add_set( "set03", "density_03" ); + set_config_03.lambda = 0.3; + set_config_01.expected_nb_objects = 30; + set_config_03.birth_ratio = 4.0; + set_config_03.death_ratio = 1.0; + set_config_03.change_ratio = 1.0; + + auto context = build_poisson_process( poisson ); + geode::SimulationRunner< geode::Point2D > runner{ std::move( + context ) }; + + // run simulation + geode::SimulationConfigurator sim_config; + sim_config.realizations = 2000; + sim_config.metropolis_hasting_steps = 100; + sim_config.burn_in_steps = 1000; + + geode::SimulationPrinterConfigurator printer_config; + printer_config.output_folder = absl::StrCat( + printer_config.output_folder, "/sim_point_multitype_poisson_test" ); + sim_config.printer = printer_config; + + auto statistic_tracker = runner.run( engine, sim_config ); + + // NOLINTEND(*-magic-numbers) + + const auto targeted_statistics_descriptors = + build_poisson_targeted_stat( poisson ); + geode::TargetStatistics target_stats{ runner.model(), + targeted_statistics_descriptors }; + geode::statistics::validate( statistic_tracker, target_stats ); + + geode::Logger::info( "--> SUCCESS!" ); + } +} // namespace + +int main() +{ + try + { + geode::OpenGeodeStochasticStochasticLibrary::initialize(); + geode::Logger::set_level( geode::Logger::LEVEL::debug ); + test_single_type_poisson(); + test_multitype_poisson(); + return 0; + } + catch( ... ) + { + return geode::geode_lippincott(); + } +} \ No newline at end of file diff --git a/tests/stochastic/applications/test-strauss-process.cpp b/tests/stochastic/applications/test-strauss-process.cpp new file mode 100644 index 0000000..f2919fd --- /dev/null +++ b/tests/stochastic/applications/test-strauss-process.cpp @@ -0,0 +1,203 @@ +/* + * 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 + +#include + +namespace +{ + using PoissonDensityDescription = geode::SingleObjectTermConfig; + using PairwiseInteractionDescription = geode::PairwiseTermConfig; + + void test_single_type_strauss() + { + geode::Logger::info( + "TEST - MH SINGLE TYPE STRAUSS (with intra-set interactions)" ); + + geode::RandomEngine engine; + engine.set_seed( "@mh-test-single-STRAUSS@" ); + + // NOLINTBEGIN(*-magic-numbers) + std::array< double, 5 > gamma_values{ 0, 0.3, 0.5, 0.7, 1.0 }; + std::array< double, 5 > nb_points{ 19.5, 24.4, 31.3, 36.1, 50. }; + std::array< double, 5 > nb_interactions{ 0, 4.7, 9.8, 18.7, 50.3 }; + for( const auto config : geode::Range{ gamma_values.size() } ) + { + geode::StraussProcessDescription< geode::Point2D > strauss; + + strauss.domain = { geode::Point2D{ { 0.0, 0.0 } }, + geode::Point2D{ { 10.0, 10.0 } }, 1. }; + + auto& set_config = strauss.add_set( "set_A", "density_A" ); + set_config.lambda = 0.5; + set_config.expected_nb_objects = nb_points[config]; + + auto& interaction_config = + strauss.add_interaction( "interaction_A" ); + interaction_config.set_names = { "set_A" }; + interaction_config.gamma = gamma_values[config]; + interaction_config.distance = 1.0; + interaction_config.expected_nb_interactions = + nb_interactions[config]; + + auto simulation_context = geode::build_strauss_process( strauss ); + geode::SimulationRunner< geode::Point2D > runner{ std::move( + simulation_context ) }; + + // run simulation + geode::SimulationConfigurator sim_config; + sim_config.realizations = 2000; + sim_config.metropolis_hasting_steps = 100; + sim_config.burn_in_steps = 1000; + + geode::SimulationPrinterConfigurator printer_config; + printer_config.output_folder = + absl::StrCat( printer_config.output_folder, + "/sim_point_strauss_test_", config ); + sim_config.printer = printer_config; + + auto statistic_tracker = runner.run( engine, sim_config ); + + const auto targeted_statistics_descriptors = + geode::build_strauss_targeted_stat( strauss ); + + geode::TargetStatistics target_stats{ runner.model(), + targeted_statistics_descriptors }; + + geode::statistics::validate( statistic_tracker, target_stats ); + } + // NOLINTEND(*-magic-numbers) + geode::Logger::info( "--> SUCCESS!" ); + } + + void test_multitype_strauss() + { + geode::Logger::info( + "TEST - MH MULTITYPE STRAUSS (with inter-set interactions ) " ); + + geode::RandomEngine engine; + engine.set_seed( "@mh-test-multi-STRAUSS@" ); + + // NOLINTBEGIN(*-magic-numbers) + std::array< double, 3 > gamma_values{ 0, 0.5, 1.0 }; + std::array< double, 3 > nb_points01{ 6.7, 8, 10.0 }; + std::array< double, 3 > nb_points02{ 17.5, 24.6, 40.0 }; + std::array< double, 3 > nb_points03{ 14.6, 19.4, 30. }; + std::array< double, 3 > nb_interactions01{ 0, 15, 59.8 }; + std::array< double, 3 > nb_interactions02{ 37.2, 70, 174 }; + for( const auto config : geode::Range{ gamma_values.size() } ) + { + geode::StraussProcessDescription< geode::Point2D > strauss; + strauss.domain = { geode::Point2D{ { 0, 0 } }, + geode::Point2D{ { 10, 10 } }, 2. }; + + auto& set_config_01 = strauss.add_set( "set01", "density_01" ); + set_config_01.lambda = 0.1; + set_config_01.expected_nb_objects = 10; + set_config_01.birth_ratio = 1.0; + set_config_01.death_ratio = 3.0; + set_config_01.change_ratio = 1.0; + + auto& set_config_02 = strauss.add_set( "set02", "density_02" ); + set_config_02.lambda = 0.4; + set_config_01.expected_nb_objects = 40; + set_config_02.birth_ratio = 3.0; + set_config_02.death_ratio = 0.5; + set_config_02.change_ratio = 1.0; + + auto& set_config_03 = strauss.add_set( "set03", "density_03" ); + set_config_03.lambda = 0.3; + set_config_01.expected_nb_objects = 30; + set_config_03.birth_ratio = 4.0; + set_config_03.death_ratio = 1.0; + set_config_03.change_ratio = 1.0; + + auto& interaction_config_01 = + strauss.add_interaction( "interaction_01" ); + interaction_config_01.set_names = { "set01", "set02", "set03" }; + interaction_config_01.gamma = gamma_values[config]; + interaction_config_01.distance = 1.0; + interaction_config_01.expected_nb_interactions = + nb_interactions01[config]; + + auto& interaction_config_02 = + strauss.add_interaction( "interaction_02" ); + interaction_config_02.set_names = { "set02" }; + interaction_config_02.gamma = 1.; + interaction_config_02.distance = 2.0; + interaction_config_02.expected_nb_interactions = + nb_interactions02[config]; + + // --- Pairwise interactions + // 1. Intra-type (repulsion within same set) + + // run simulation + auto context = build_strauss_process( strauss ); + geode::SimulationRunner< geode::Point2D > runner{ std::move( + context ) }; + + geode::SimulationConfigurator sim_config; + sim_config.realizations = 2000; + sim_config.metropolis_hasting_steps = 100; + sim_config.burn_in_steps = 1000; + + geode::SimulationPrinterConfigurator printer_config; + printer_config.output_folder = + absl::StrCat( printer_config.output_folder, + "/sim_point_multitype_strauss_test" ); + sim_config.printer = printer_config; + + auto statistic_tracker = runner.run( engine, sim_config ); + + const auto targeted_statistics_descriptors = + geode::build_strauss_targeted_stat( strauss ); + geode::TargetStatistics target_stats{ runner.model(), + targeted_statistics_descriptors }; + + geode::statistics::validate( statistic_tracker, target_stats ); + } + // NOLINTEND(*-magic-numbers) + + geode::Logger::info( "--> SUCCESS!" ); + } +} // namespace + +int main() +{ + try + { + geode::OpenGeodeStochasticStochasticLibrary::initialize(); + geode::Logger::set_level( geode::Logger::LEVEL::debug ); + test_single_type_strauss(); + // test_multitype_strauss(); + return 0; + } + catch( ... ) + { + return geode::geode_lippincott(); + } +} \ No newline at end of file diff --git a/tests/stochastic/models/CMakeLists.txt b/tests/stochastic/models/CMakeLists.txt new file mode 100644 index 0000000..1d9a142 --- /dev/null +++ b/tests/stochastic/models/CMakeLists.txt @@ -0,0 +1,59 @@ +# 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. + +add_geode_test( + SOURCE "energy_terms/test-density-term.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) + +add_geode_test( + SOURCE "energy_terms/test-intensity-term.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) + +add_geode_test( + SOURCE "energy_terms/test-pairwise-term.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) + +#add_geode_test( +# SOURCE "energy_terms/test-gibbs-energy.cpp" +# DEPENDENCIES +# OpenGeode::basic +# OpenGeode::geometry +# ${PROJECT_NAME}::stochastic +#) + +#add_geode_test( +# SOURCE "energy_term-collection.cpp" +# DEPENDENCIES +# OpenGeode::basic +# OpenGeode::geometry +# ${PROJECT_NAME}::stochastic +#) \ No newline at end of file diff --git a/tests/stochastic/sampling/CMakeLists.txt b/tests/stochastic/sampling/CMakeLists.txt new file mode 100644 index 0000000..ccb4743 --- /dev/null +++ b/tests/stochastic/sampling/CMakeLists.txt @@ -0,0 +1,97 @@ +# 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. + +add_geode_test( + SOURCE "direct/test-ball-sampler.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) + +add_geode_test( + SOURCE "direct/test-bounding-box-sampler.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) + +add_geode_test( + SOURCE "direct/test-double-sampler.cpp" + DEPENDENCIES + OpenGeode::basic + ${PROJECT_NAME}::stochastic +) + +add_geode_test( + SOURCE "direct/test-point-uniform-sampler.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) + +add_geode_test( + SOURCE "direct/test-segment-uniform-sampler.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) + +#add_geode_test( +# SOURCE "mcmc/helpers/test-simulation_printer.cpp" +# DEPENDENCIES +# OpenGeode::basic +# OpenGeode::geometry +# ${PROJECT_NAME}::stochastic +#) + +#add_geode_test( +# SOURCE "mcmc/helpers/test-simulation_monitor.cpp" +# DEPENDENCIES +# OpenGeode::basic +# OpenGeode::geometry +# ${PROJECT_NAME}::stochastic +#) + +add_geode_test( + SOURCE "mcmc/proposal/test-proposal-kernel.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) + +#add_geode_test( +# SOURCE "mcmc/test-metropolis-hasting-sampler.cpp" +# DEPENDENCIES +# OpenGeode::basic +# OpenGeode::geometry +# ${PROJECT_NAME}::stochastic +#) + +add_geode_test( + SOURCE "test-random-engine.cpp" + DEPENDENCIES + OpenGeode::basic + ${PROJECT_NAME}::stochastic +) diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp deleted file mode 100644 index a41bb83..0000000 --- a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp +++ /dev/null @@ -1,214 +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 - -#include -#include - -#include - -namespace -{ - - using PoissonDensityDescription = geode::SingleObjectTermConfig; - - void test_single_type_poisson() - { - geode::Logger::info( "TEST - MH SINGLE TYPE POISSON" ); - - geode::RandomEngine engine; - engine.set_seed( "@mh-test-single-POISSON@" ); - - // NOLINTBEGIN(*-magic-numbers) - std::array< double, 4 > birth_ratio{ 0.1, 0.5, 2., 4. }; - std::array< double, 4 > change_ratio{ 0., 1., 1., 0. }; - - for( const auto config : geode::Range{ birth_ratio.size() } ) - { - geode::SimulationContextConfig< geode::Point2D > poisson_config; - - poisson_config.domain = { geode::Point2D{ { 0.0, 0.0 } }, - geode::Point2D{ { 10.0, 10.0 } }, 0. }; - // --- Set description - geode::ObjectSetDefinition< geode::Point2D > set_a; - set_a.name = "A"; - set_a.sampler = geode::ObjectSamplerConfig< geode::Point2D >{}; - set_a.dynamics.birth_ratio = birth_ratio[config]; - set_a.dynamics.death_ratio = 1.0; - set_a.dynamics.change_ratio = change_ratio[config]; - poisson_config.sets.emplace_back( set_a ); - - // --- Energy term description - PoissonDensityDescription density_a; - density_a.term_name = "density"; - density_a.object_set_names = { "A" }; - density_a.lambda = 0.3; - density_a.object_feature = geode::ObjectInDomainFeatureConfig{}; - poisson_config.model.terms.emplace_back( density_a ); - - auto context = build_simulation_context( poisson_config ); - geode::SimulationRunner< geode::Point2D > runner{ std::move( - context ) }; - - // run simulation - - geode::SimulationConfigurator sim_config; - sim_config.realizations = 2000; - sim_config.metropolis_hasting_steps = 100; - sim_config.burn_in_steps = 1000; - - geode::SimulationPrinterConfigurator printer_config; - printer_config.output_folder = - absl::StrCat( printer_config.output_folder, - "/sim_point_poisson_test_", config ); - sim_config.printer = printer_config; - - auto statistic_tracker = runner.run( engine, sim_config ); - - std::vector< geode::TargetStatisticConfig > - targeted_statistics_descriptors; - geode::TargetStatisticConfig stat_a{ "density", 30.0, 0.15 }; - targeted_statistics_descriptors.push_back( stat_a ); - geode::TargetStatistics target_stats{ runner.model(), - targeted_statistics_descriptors }; - - geode::statistics::validate( statistic_tracker, target_stats ); - } - // NOLINTEND(*-magic-numbers) - - geode::Logger::info( "--> SUCCESS!" ); - } - - void test_multitype_poisson() - { - geode::Logger::info( "TEST - MH MULTITYPE POISSON" ); - - geode::RandomEngine engine; - engine.set_seed( "@mh-test-POISSON-multi@" ); - - geode::SimulationContextConfig< geode::Point2D > poisson_config; - - // NOLINTBEGIN(*-magic-numbers) - poisson_config.domain = { geode::Point2D{ { 0.0, 0.0 } }, - geode::Point2D{ { 10.0, 10.0 } }, 0. }; - - // --- Set descriptions - geode::ObjectSetDefinition< geode::Point2D > set01; - set01.name = "set01"; - set01.sampler = geode::ObjectSamplerConfig< geode::Point2D >{}; - set01.dynamics.birth_ratio = 2.0; - set01.dynamics.death_ratio = 3.0; - set01.dynamics.change_ratio = 1.0; - poisson_config.sets.emplace_back( set01 ); - - geode::ObjectSetDefinition< geode::Point2D > set02; - set02.name = "set02"; - set02.sampler = geode::ObjectSamplerConfig< geode::Point2D >{}; - set02.dynamics.birth_ratio = 3.0; - set02.dynamics.death_ratio = 0.5; - set02.dynamics.change_ratio = 1.0; - poisson_config.sets.emplace_back( set02 ); - - geode::ObjectSetDefinition< geode::Point2D > set03; - set03.name = "set03"; - set03.sampler = geode::ObjectSamplerConfig< geode::Point2D >{}; - set03.dynamics.birth_ratio = 4.0; - set03.dynamics.death_ratio = 1.0; - set03.dynamics.change_ratio = 1.0; - poisson_config.sets.emplace_back( set03 ); - - // --- Energy term descriptions - PoissonDensityDescription density01; - density01.term_name = "density01"; - density01.object_set_names = { "set01" }; - density01.lambda = 0.1; - density01.object_feature = geode::ObjectInDomainFeatureConfig{}; - poisson_config.model.terms.emplace_back( density01 ); - - PoissonDensityDescription density02; - density02.term_name = "density02"; - density02.object_set_names = { "set02" }; - density02.lambda = 0.4; - density02.object_feature = geode::ObjectInDomainFeatureConfig{}; - poisson_config.model.terms.emplace_back( density02 ); - - PoissonDensityDescription density03; - density03.term_name = "density03"; - density03.object_set_names = { "set03" }; - density03.lambda = 0.3; - density03.object_feature = geode::ObjectInDomainFeatureConfig{}; - poisson_config.model.terms.emplace_back( density03 ); - - auto context = build_simulation_context( poisson_config ); - geode::SimulationRunner< geode::Point2D > runner{ std::move( - context ) }; - - // run simulation - geode::SimulationConfigurator sim_config; - sim_config.realizations = 2000; - sim_config.metropolis_hasting_steps = 100; - sim_config.burn_in_steps = 1000; - - geode::SimulationPrinterConfigurator printer_config; - printer_config.output_folder = absl::StrCat( - printer_config.output_folder, "/sim_point_multitype_poisson_test" ); - sim_config.printer = printer_config; - - auto statistic_tracker = runner.run( engine, sim_config ); - - std::vector< geode::TargetStatisticConfig > - targeted_statistics_descriptors; - geode::TargetStatisticConfig stat01{ "density01", 10.0, 0.15 }; - targeted_statistics_descriptors.push_back( stat01 ); - geode::TargetStatisticConfig stat02{ "density02", 40.0, 0.15 }; - targeted_statistics_descriptors.push_back( stat02 ); - geode::TargetStatisticConfig stat03{ "density03", 30.0, 0.15 }; - targeted_statistics_descriptors.push_back( stat03 ); - // NOLINTEND(*-magic-numbers) - - geode::TargetStatistics target_stats{ runner.model(), - targeted_statistics_descriptors }; - geode::statistics::validate( statistic_tracker, target_stats ); - - geode::Logger::info( "--> SUCCESS!" ); - } -} // namespace - -int main() -{ - try - { - geode::OpenGeodeStochasticStochasticLibrary::initialize(); - geode::Logger::set_level( geode::Logger::LEVEL::debug ); - test_single_type_poisson(); - test_multitype_poisson(); - return 0; - } - catch( ... ) - { - return geode::geode_lippincott(); - } -} \ No newline at end of file diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp deleted file mode 100644 index 823360a..0000000 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ /dev/null @@ -1,278 +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 - -#include -#include - -#include - -namespace -{ - using PoissonDensityDescription = geode::SingleObjectTermConfig; - using PairwiseInteractionDescription = geode::PairwiseTermConfig; - - void test_single_type_strauss() - { - geode::Logger::info( - "TEST - MH SINGLE TYPE STRAUSS (with intra-set interactions)" ); - - geode::RandomEngine engine; - engine.set_seed( "@mh-test-single-STRAUSS@" ); - - // NOLINTBEGIN(*-magic-numbers) - std::array< double, 5 > gamma_values{ 0, 0.3, 0.5, 0.7, 1.0 }; - std::array< double, 5 > nb_points{ 19.5, 24.4, 31.3, 36.1, 50. }; - std::array< double, 5 > nb_interactions{ 0, 4.7, 9.8, 18.7, 50.3 }; - for( const auto config : geode::Range{ gamma_values.size() } ) - { - geode::SimulationContextConfig< geode::Point2D > - simulation_cxt_config; - - simulation_cxt_config.domain = { geode::Point2D{ { 0.0, 0.0 } }, - geode::Point2D{ { 10.0, 10.0 } }, 1. }; - - // --- Object set - geode::ObjectSetDefinition< geode::Point2D > set_a; - set_a.name = "A"; - set_a.sampler = geode::ObjectSamplerConfig< geode::Point2D >{}; - set_a.dynamics.birth_ratio = 1.0; - set_a.dynamics.death_ratio = 1.0; - set_a.dynamics.change_ratio = 1.0; - simulation_cxt_config.sets.emplace_back( set_a ); - - // --- Model - Energy term description - PoissonDensityDescription density_a; - density_a.term_name = "density_a"; - density_a.object_set_names = { "A" }; - density_a.lambda = 0.5; - density_a.object_feature = geode::ObjectInDomainFeatureConfig{}; - simulation_cxt_config.model.terms.emplace_back( density_a ); - - // --- Intra-set pairwise interaction (Strauss process) - PairwiseInteractionDescription interaction_a; - interaction_a.term_name = "interactionA"; - interaction_a.object_set_names_interactions = { { "A", "A" } }; - interaction_a.gamma = gamma_values[config]; - interaction_a.interaction_config = - geode::MinimalDistanceCutoffConfig{ - 1. - }; /// ici cela devrait etre un paramètre utilisateur - simulation_cxt_config.model.terms.emplace_back( interaction_a ); - - auto context = build_simulation_context( simulation_cxt_config ); - geode::SimulationRunner< geode::Point2D > runner{ std::move( - context ) }; - - // run simulation - geode::SimulationConfigurator sim_config; - sim_config.realizations = 2000; - sim_config.metropolis_hasting_steps = 100; - sim_config.burn_in_steps = 1000; - - geode::SimulationPrinterConfigurator printer_config; - printer_config.output_folder = - absl::StrCat( printer_config.output_folder, - "/sim_point_strauss_test_", config ); - sim_config.printer = printer_config; - - auto statistic_tracker = runner.run( engine, sim_config ); - - std::vector< geode::TargetStatisticConfig > - targeted_statistics_descriptors; - geode::TargetStatisticConfig stat_a{ "density_a", nb_points[config], - 0.1 }; - geode::TargetStatisticConfig stat_intra_a{ "interactionA", - nb_interactions[config], 0.1 }; - targeted_statistics_descriptors.push_back( stat_a ); - targeted_statistics_descriptors.push_back( stat_intra_a ); - geode::TargetStatistics target_stats{ runner.model(), - targeted_statistics_descriptors }; - - geode::statistics::validate( statistic_tracker, target_stats ); - } - // NOLINTEND(*-magic-numbers) - geode::Logger::info( "--> SUCCESS!" ); - } - - void test_multitype_strauss() - { - geode::Logger::info( - "TEST - MH MULTITYPE STRAUSS (with inter-set interactions ) " ); - - geode::RandomEngine engine; - engine.set_seed( "@mh-test-multi-STRAUSS@" ); - - // NOLINTBEGIN(*-magic-numbers) - std::array< double, 3 > gamma_values{ 0, 0.5, 1.0 }; - std::array< double, 3 > nb_points01{ 6.7, 8, 10.0 }; - std::array< double, 3 > nb_points02{ 17.5, 24.6, 40.0 }; - std::array< double, 3 > nb_points03{ 14.6, 19.4, 30. }; - std::array< double, 3 > nb_interactions01{ 0, 15, 59.8 }; - std::array< double, 3 > nb_interactions02{ 37.2, 70, 174 }; - for( const auto config : geode::Range{ gamma_values.size() } ) - { - geode::SimulationContextConfig< geode::Point2D > - simulation_cxt_config; - - simulation_cxt_config.domain = { geode::Point2D{ { 0.0, 0.0 } }, - geode::Point2D{ { 10.0, 10.0 } }, 2. }; - - // --- Object set - geode::ObjectSetDefinition< geode::Point2D > set01; - set01.name = "set01"; - set01.sampler = geode::ObjectSamplerConfig< geode::Point2D >{}; - set01.dynamics.birth_ratio = 1.0; - set01.dynamics.death_ratio = 3.0; - set01.dynamics.change_ratio = 1.0; - simulation_cxt_config.sets.emplace_back( set01 ); - - geode::ObjectSetDefinition< geode::Point2D > set02; - set02.name = "set02"; - set02.sampler = geode::ObjectSamplerConfig< geode::Point2D >{}; - set02.dynamics.birth_ratio = 3.0; - set02.dynamics.death_ratio = 0.5; - set02.dynamics.change_ratio = 1.0; - simulation_cxt_config.sets.emplace_back( set02 ); - - geode::ObjectSetDefinition< geode::Point2D > set03; - set03.name = "set03"; - set03.sampler = geode::ObjectSamplerConfig< geode::Point2D >{}; - set03.dynamics.birth_ratio = 4.0; - set03.dynamics.death_ratio = 1.0; - set03.dynamics.change_ratio = 1.0; - simulation_cxt_config.sets.emplace_back( set03 ); - - // --- Model - Energy term description - // density - PoissonDensityDescription density01; - density01.term_name = "density01"; - density01.object_set_names = { "set01" }; - density01.lambda = 0.1; - density01.object_feature = geode::ObjectInDomainFeatureConfig{}; - simulation_cxt_config.model.terms.emplace_back( density01 ); - - PoissonDensityDescription density02; - density02.term_name = "density02"; - density02.object_set_names = { "set02" }; - density02.lambda = 0.4; - density02.object_feature = geode::ObjectInDomainFeatureConfig{}; - simulation_cxt_config.model.terms.emplace_back( density02 ); - - PoissonDensityDescription density03; - density03.term_name = "density03"; - density03.object_set_names = { "set03" }; - density03.lambda = 0.3; - density03.object_feature = geode::ObjectInDomainFeatureConfig{}; - simulation_cxt_config.model.terms.emplace_back( density03 ); - - // --- Pairwise interactions - // 1. Intra-type (repulsion within same set) - PairwiseInteractionDescription interaction_01; - interaction_01.term_name = "interaction_01"; - interaction_01.object_set_names_interactions = { - { "set01", "set01" }, { "set02", "set02" }, { "set03", "set03" } - }; - interaction_01.gamma = gamma_values[config]; - interaction_01.interaction_config = - geode::MinimalDistanceCutoffConfig{ - 1. - }; /// ici cela devrait etre un paramètre utilisateur - simulation_cxt_config.model.terms.emplace_back( interaction_01 ); - - PairwiseInteractionDescription interaction_02; - interaction_02.term_name = "interaction_02"; - interaction_02.object_set_names_interactions = { { "set02", - "set02" } }; - interaction_02.gamma = 1.; - interaction_02.interaction_config = - geode::MinimalDistanceCutoffConfig{ - 2. - }; /// ici cela devrait etre un paramètre utilisateur - simulation_cxt_config.model.terms.emplace_back( interaction_02 ); - - // run simulation - auto context = build_simulation_context( simulation_cxt_config ); - geode::SimulationRunner< geode::Point2D > runner{ std::move( - context ) }; - - geode::SimulationConfigurator sim_config; - sim_config.realizations = 2000; - sim_config.metropolis_hasting_steps = 100; - sim_config.burn_in_steps = 1000; - - geode::SimulationPrinterConfigurator printer_config; - printer_config.output_folder = - absl::StrCat( printer_config.output_folder, - "/sim_point_multitype_strauss_test" ); - sim_config.printer = printer_config; - - auto statistic_tracker = runner.run( engine, sim_config ); - - std::vector< geode::TargetStatisticConfig > - targeted_statistics_descriptors; - geode::TargetStatisticConfig stat01{ "density01", - nb_points01[config], 0.1 }; - geode::TargetStatisticConfig stat02{ "density02", - nb_points02[config], 0.1 }; - geode::TargetStatisticConfig stat03{ "density03", - nb_points03[config], 0.1 }; - geode::TargetStatisticConfig stat_intra_01{ "interaction_01", - nb_interactions01[config], 0.1 }; - geode::TargetStatisticConfig stat_intra_02{ "interaction_02", - nb_interactions02[config], 0.1 }; - targeted_statistics_descriptors.push_back( stat01 ); - targeted_statistics_descriptors.push_back( stat02 ); - targeted_statistics_descriptors.push_back( stat03 ); - targeted_statistics_descriptors.push_back( stat_intra_01 ); - targeted_statistics_descriptors.push_back( stat_intra_02 ); - - geode::TargetStatistics target_stats{ runner.model(), - targeted_statistics_descriptors }; - - geode::statistics::validate( statistic_tracker, target_stats ); - } - // NOLINTEND(*-magic-numbers) - - geode::Logger::info( "--> SUCCESS!" ); - } -} // namespace - -int main() -{ - try - { - geode::OpenGeodeStochasticStochasticLibrary::initialize(); - geode::Logger::set_level( geode::Logger::LEVEL::debug ); - // test_single_type_strauss(); - test_multitype_strauss(); - return 0; - } - catch( ... ) - { - return geode::geode_lippincott(); - } -} \ No newline at end of file diff --git a/tests/stochastic/sampling/test-random-engine.cpp b/tests/stochastic/sampling/test-random-engine.cpp index 03a8880..f6b2e1d 100644 --- a/tests/stochastic/sampling/test-random-engine.cpp +++ b/tests/stochastic/sampling/test-random-engine.cpp @@ -29,7 +29,7 @@ #include #include -const int NUMBER_OF_SAMPLES = 10000; +const int NUMBER_OF_SAMPLES = 100000; void test_reproducibility() { @@ -84,7 +84,7 @@ template < typename T > void test_distribution_mean_and_variance( const std::vector< T >& data, double expected_mean, double expected_var, - double k = 3.0 ) + double k = 5.0 ) { const auto n = data.size(); const double mean = compute_mean( data ); @@ -234,6 +234,8 @@ int main() geode::RandomEngine random_engine; test_reproducibility(); + random_engine.set_seed( "@-test-radom-engine@" ); + 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 ); diff --git a/tests/stochastic/spatial/CMakeLists.txt b/tests/stochastic/spatial/CMakeLists.txt new file mode 100644 index 0000000..447e969 --- /dev/null +++ b/tests/stochastic/spatial/CMakeLists.txt @@ -0,0 +1,51 @@ +# 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. + +add_geode_test( + SOURCE "test-object-set.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) + +add_geode_test( + SOURCE "test-object-sets.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) + +add_geode_test( + SOURCE "test-pairwise-interactions.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) + +add_geode_test( + SOURCE "test-spatial-domain.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) From 34eb84d549e4dcda8402e7fe6e307d9d3c992a66 Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Tue, 2 Jun 2026 10:45:02 +0000 Subject: [PATCH 50/59] Apply prepare changes --- .../stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp index 5ba8548..7bcf366 100644 --- a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp +++ b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp @@ -192,7 +192,7 @@ namespace geode []( auto& cur_state, auto& accepted_proposal ) { cur_state.add_object( std::move( accepted_proposal.proposed_move.new_object - .value() ), + .value() ), accepted_proposal.set_id, false ); } ); }; @@ -224,7 +224,7 @@ namespace geode cur_state.update_free_object( accepted_proposal.old_object_id(), std::move( accepted_proposal.proposed_move.new_object - .value() ) ); + .value() ) ); } ); }; From 0f67b68a5d73038ccce5211dc0481b20b7f30a6f Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 2 Jun 2026 13:14:29 +0200 Subject: [PATCH 51/59] msvc try --- .../object_set_sampler/point_set_sampler.hpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 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 2bcffaf..0f3fba0 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 @@ -107,12 +107,21 @@ namespace geode double move_ratio = 0.1; }; - template < index_t dimension > - std::unique_ptr< ObjectSetSampler< Point< dimension > > > - build_objectset_sampler( const SpatialDomain< dimension >& domain, - const ObjectSamplerConfig< Point< dimension > >& config ) + template <> + std::unique_ptr< ObjectSetSampler< Point2D > > + build_objectset_sampler< Point2D >( const SpatialDomain< 2 >& domain, + const ObjectSamplerConfig< Point2D >& config ) + { + return std::make_unique< UniformPointSetSampler< 2 > >( + domain, config ); + } + + template <> + std::unique_ptr< ObjectSetSampler< Point3D > > + build_objectset_sampler< Point3D >( const SpatialDomain< 3 >& domain, + const ObjectSamplerConfig< Point3D >& config ) { - return std::make_unique< UniformPointSetSampler< dimension > >( + return std::make_unique< UniformPointSetSampler< 3 > >( domain, config ); } From deed2f5d5c9b6bb742212a8fc48d03f11df2714a Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 2 Jun 2026 19:26:16 +0200 Subject: [PATCH 52/59] tidy --- .../applications/poisson_process.hpp | 2 +- .../applications/strauss_process.hpp | 4 +- .../inference/target_statistics.hpp | 4 +- .../object_set_sampler/object_set_sampler.hpp | 11 +- .../object_set_sampler/point_set_sampler.hpp | 98 +---- .../segment_set_sampler.hpp | 95 +---- .../mcmc/metropolis_hasting_sampler.hpp | 59 +-- .../geode/stochastic/spatial/object_set.hpp | 2 + .../segment_length_feature.hpp | 2 +- src/geode/stochastic/CMakeLists.txt | 2 + .../applications/poisson_process.cpp | 26 +- .../applications/strauss_process.cpp | 82 ++-- .../object_set_sampler/point_set_sampler.cpp | 125 +++++++ .../segment_set_sampler.cpp | 110 ++++++ .../mcmc/proposal/test-proposal-kernel.cpp | 25 +- .../mcmc/test-metropolis-hasting-sampler.cpp | 14 +- .../sampling/test-random-engine.cpp | 350 ++++++++++-------- 17 files changed, 607 insertions(+), 404 deletions(-) create mode 100644 src/geode/stochastic/sampling/direct/object_set_sampler/point_set_sampler.cpp create mode 100644 src/geode/stochastic/sampling/direct/object_set_sampler/segment_set_sampler.cpp diff --git a/include/geode/stochastic/applications/poisson_process.hpp b/include/geode/stochastic/applications/poisson_process.hpp index c14106c..b324f71 100644 --- a/include/geode/stochastic/applications/poisson_process.hpp +++ b/include/geode/stochastic/applications/poisson_process.hpp @@ -13,7 +13,7 @@ namespace geode ObjectSamplerConfig< ObjectType > sampler; std::string density_name; - double lambda; + double lambda{ 0. }; std::optional< double > expected_nb_objects; double birth_ratio{ 1.0 }; diff --git a/include/geode/stochastic/applications/strauss_process.hpp b/include/geode/stochastic/applications/strauss_process.hpp index ba84561..8d4c6fc 100644 --- a/include/geode/stochastic/applications/strauss_process.hpp +++ b/include/geode/stochastic/applications/strauss_process.hpp @@ -13,8 +13,8 @@ namespace geode std::vector< std::string > set_names; - double gamma; - double distance; + double gamma{ 1. }; + double distance{ 0. }; bool include_intra_set{ true }; bool include_inter_set{ false }; diff --git a/include/geode/stochastic/inference/target_statistics.hpp b/include/geode/stochastic/inference/target_statistics.hpp index 538eca0..b962668 100644 --- a/include/geode/stochastic/inference/target_statistics.hpp +++ b/include/geode/stochastic/inference/target_statistics.hpp @@ -26,11 +26,13 @@ namespace geode { + constexpr double STATISTIC_TOLERANCE_VALUE{ 0.1 }; + struct TargetStatisticConfig { std::string term_name; double value; - double tolerance; + double tolerance{ STATISTIC_TOLERANCE_VALUE }; }; template < typename ObjectType > diff --git a/include/geode/stochastic/sampling/direct/object_set_sampler/object_set_sampler.hpp b/include/geode/stochastic/sampling/direct/object_set_sampler/object_set_sampler.hpp index 6001008..c978a66 100644 --- a/include/geode/stochastic/sampling/direct/object_set_sampler/object_set_sampler.hpp +++ b/include/geode/stochastic/sampling/direct/object_set_sampler/object_set_sampler.hpp @@ -34,7 +34,10 @@ namespace geode template < typename Type > class ObjectSetSampler { + OPENGEODE_DISABLE_COPY_AND_MOVE( ObjectSetSampler ); + public: + ObjectSetSampler() = default; virtual ~ObjectSetSampler() = default; [[nodiscard]] virtual Type sample( RandomEngine& engine ) const = 0; @@ -53,9 +56,13 @@ namespace geode } protected: - virtual bool is_valid_object( const Type& obj ) const = 0; + [[nodiscard]] virtual bool is_valid_object( const Type& obj ) const = 0; + void set_log_pdf( double value ) + { + log_pdf_ = value; + } - protected: + private: double log_pdf_{ LOG_PROB_INVALID }; }; 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 0f3fba0..3784cb4 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 @@ -23,106 +23,38 @@ #pragma once -#include -#include - #include -#include -#include namespace geode { + template < index_t dimension > + struct ObjectSamplerConfig< Point< dimension > > + { + // use to define the step for change move (move_ratio*domain volume) + double move_ratio{ 0.1 }; + }; + template < index_t dimension > class UniformPointSetSampler : public ObjectSetSampler< Point< dimension > > { public: UniformPointSetSampler( const SpatialDomain< dimension >& domain, - const ObjectSamplerConfig< Point< dimension > >& config ) - : ObjectSetSampler< Point< dimension > >{}, domain_( domain ) - { - auto volume = domain_.extended_n_volume(); - OpenGeodeStochasticStochasticException::check_exception( - volume != 0., nullptr, OpenGeodeException::TYPE::data, - "[UniformPointSetSampler] Undefined Extended Bounding " - "Box (volume ==0)." ); - this->log_pdf_ = -std::log( volume ); - step_move_ = define_step_for_move( config.move_ratio ); - OpenGeodeStochasticStochasticException::check_exception( - step_move_ > 0., nullptr, OpenGeodeException::TYPE::data, - "[UniformPointSetSampler] Undefined step length for move " - "(value == ", - step_move_, ")." ); - } - - Point< dimension > sample( RandomEngine& engine ) const override - { - return PointUniformSampler::sample< dimension >( - engine, domain_.extended_box() ); - } + const ObjectSamplerConfig< Point< dimension > >& config ); - Point< dimension > change( - const Point< dimension >& obj, RandomEngine& engine ) const override - { - geode::Sphere< dimension > ball{ obj, step_move_ }; + [[nodiscard]] Point< dimension > sample( + RandomEngine& engine ) const override; - auto new_point = - PointUniformSampler::sample< dimension >( engine, ball ); - constexpr index_t max_try{ 100 }; - for( const auto try_id : geode::Range{ max_try } ) - { - geode_unused( try_id ); - if( domain_.extended_contains( new_point ) ) - { - return new_point; - } - new_point = - PointUniformSampler::sample< dimension >( engine, ball ); - } - throw OpenGeodeStochasticStochasticException{ nullptr, - OpenGeodeException::TYPE::internal, - "[UniformPointSetSampler] Cannot find a point in the " - "extended domain" }; - } + [[nodiscard]] Point< dimension > change( const Point< dimension >& obj, + RandomEngine& engine ) const override; private: - double define_step_for_move( double ratio ) - { - return ratio * domain_.smallest_length(); - } - - bool is_valid_object( const Point< dimension >& obj ) const override - { - return domain_.extended_contains( obj ); - } + [[nodiscard]] double define_step_for_move( double ratio ); + [[nodiscard]] bool is_valid_object( + const Point< dimension >& obj ) const override; private: const SpatialDomain< dimension >& domain_; double step_move_{ 0. }; }; - template < index_t dimension > - struct ObjectSamplerConfig< Point< dimension > > - { - // use to define the step for change move (move_ratio*domain volume) - double move_ratio = 0.1; - }; - - template <> - std::unique_ptr< ObjectSetSampler< Point2D > > - build_objectset_sampler< Point2D >( const SpatialDomain< 2 >& domain, - const ObjectSamplerConfig< Point2D >& config ) - { - return std::make_unique< UniformPointSetSampler< 2 > >( - domain, config ); - } - - template <> - std::unique_ptr< ObjectSetSampler< Point3D > > - build_objectset_sampler< Point3D >( const SpatialDomain< 3 >& domain, - const ObjectSamplerConfig< Point3D >& config ) - { - return std::make_unique< UniformPointSetSampler< 3 > >( - domain, config ); - } - } // namespace geode \ No newline at end of file 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 c7d58cc..0a6e630 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 @@ -23,79 +23,37 @@ #pragma once -#include -#include +// #include +#include #include -#include -#include -#include namespace geode { + template <> + struct ObjectSamplerConfig< OwnerSegment2D > + { + double move_ratio = 0.1; + + DoubleSampler::DistributionDescription length; + DoubleSampler::DistributionDescription azimuth; + }; + class UniformSegmentSetSampler : public ObjectSetSampler< OwnerSegment2D > { public: UniformSegmentSetSampler( const SpatialDomain< 2 >& domain, - const ObjectSamplerConfig< Segment2D >& config ) - : ObjectSetSampler< OwnerSegment2D >{}, - domain_{ domain }, - length_{ DoubleSampler::build_distribution( config.length ) }, - azimuth_{ DoubleSampler::build_distribution( config.azimuth ) }, - move_ratio_{ config.move_ratio } - { - auto volume = domain_.extended_n_volume(); - OpenGeodeStochasticStochasticException::check_exception( - volume != 0., nullptr, OpenGeodeException::TYPE::data, - "[UniformSegmentSetSampler] Undefined Extended Bounding " - "Box (volume ==0)." ); - this->log_pdf_ = -std::log( volume ); - } + const ObjectSamplerConfig< OwnerSegment2D >& config ); - OwnerSegment2D sample( RandomEngine& engine ) const override - { - auto seg = SegmentUniformSampler::sample( - engine, domain_.extended_box(), length_, azimuth_ ); - return seg; - } + [[nodiscard]] OwnerSegment2D sample( + RandomEngine& engine ) const override; - OwnerSegment2D change( - const OwnerSegment2D& obj, RandomEngine& engine ) const override - { - const auto& extremities = obj.vertices(); - const auto current = - static_cast< local_index_t >( engine.sample_bernoulli( 0.5 ) ); - const auto other = 1 - current; - - geode::Sphere< 2 > ball{ extremities[current], - move_ratio_ * obj.length() }; - - auto new_point = PointUniformSampler::sample< 2 >( engine, ball ); - constexpr index_t max_try{ 100 }; - for( const auto try_id : geode::Range{ max_try } ) - { - if( domain_.extended_contains( new_point ) - || domain_.extended_contains( extremities[other] ) ) - { - OwnerSegment2D new_segment{ obj }; - new_segment.set_point( current, new_point ); - return new_segment; - } - new_point = PointUniformSampler::sample< 2 >( engine, ball ); - } - throw OpenGeodeStochasticStochasticException{ nullptr, - OpenGeodeException::TYPE::internal, - "[SegmentSetSampler] - Cannot find a point in the box" }; - return obj; - } + [[nodiscard]] OwnerSegment2D change( + const OwnerSegment2D& obj, RandomEngine& engine ) const override; private: - bool is_valid_object( const OwnerSegment2D& obj ) const override - { - const auto& extremities = obj.vertices(); - return domain_.extended_contains( extremities[0] ) - || domain_.extended_contains( extremities[1] ); - } + [[nodiscard]] bool is_valid_object( + const OwnerSegment2D& obj ) const override; private: const SpatialDomain< 2 >& domain_; @@ -104,21 +62,4 @@ namespace geode double move_ratio_{ 0.1 }; }; - template <> - struct ObjectSamplerConfig< OwnerSegment2D > - { - double move_ratio = 0.1; - - DoubleSampler::DistributionDescription length; - DoubleSampler::DistributionDescription azimuth; - }; - - template <> - std::unique_ptr< ObjectSetSampler< OwnerSegment2D > > - build_objectset_sampler( const SpatialDomain< 2 >& domain, - const ObjectSamplerConfig< OwnerSegment2D >& config ) - { - return std::make_unique< UniformSegmentSetSampler >( domain, config ); - } - } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp index 7bcf366..f953ff8 100644 --- a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp +++ b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp @@ -29,17 +29,17 @@ namespace geode { - enum struct MHDecision + enum struct MH_DECISION { - Accepted, - Rejected, - Undecided + accepted, + rejected, + undecided }; template < typename ObjectType > struct StepResult { - MHDecision decision{ MHDecision::Undecided }; + MH_DECISION decision{ MH_DECISION::undecided }; MoveType move_type{ MoveType::Invalid }; double log_accept{ -std::numeric_limits< double >::infinity() }; double delta_log_energy{ 0.0 }; @@ -91,7 +91,8 @@ namespace geode } } - ObjectSets< ObjectType > walk_copy( ObjectSets< ObjectType > initial, + [[nodiscard]] ObjectSets< ObjectType > walk_copy( + ObjectSets< ObjectType > initial, RandomEngine& engine, index_t nb_steps ) const { @@ -99,61 +100,68 @@ namespace geode return initial; } - double beta() const + [[nodiscard]] double beta() const { return beta_; } - void set_beta( double b ) + void set_beta( double beta ) { - OpenGeodeStochasticStochasticException::check_exception( b >= 0.0, - nullptr, OpenGeodeException::TYPE::data, + OpenGeodeStochasticStochasticException::check_exception( + beta >= 0.0, nullptr, OpenGeodeException::TYPE::data, "[MetropolisHastings] The teperature (beta) must be >= 0" ); - if( b == 0 ) + if( beta == 0 ) { Logger::info( "[MetropolisHastings] beta == 0 all move will be " "accepted - Uniform sampling." ); } - if( b < 1 ) + if( beta < 1 ) { Logger::info( "[MetropolisHastings] beta < 1 moves that increase " "energy are more likely to be accepted - Hot system " "introduce randomness for exploration." ); } - if( b == 1 ) + if( beta == 1 ) { Logger::info( "[MetropolisHastings] beta == 1 default " "choice no temperature - only consider energy." ); } - if( b > 1 ) + if( beta > 1 ) { Logger::info( "[MetropolisHastings] 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; + beta_ = beta; } - static double acceptance_prob_helper( double log_accept ) + [[nodiscard]] static double acceptance_prob_helper( double log_accept ) { if( std::isnan( log_accept ) ) + { return 0.0; + } if( log_accept >= 0.0 ) + { return 1.0; + } // prevent exponential overflow constexpr double LOG_MIN = -745.0; if( log_accept < LOG_MIN ) + { return 0.0; + } return std::exp( log_accept ); } private: - double compute_log_accept( + [[nodiscard]] double compute_log_accept( double deltaU, const ProposalProbabilities& proposal_probas ) const { - return -beta_ * deltaU + proposal_probas.transition_probability(); + return ( -beta_ * deltaU ) + + proposal_probas.transition_probability(); } template < typename ApplyMove > @@ -162,7 +170,7 @@ namespace geode ObjectSets< ObjectType >& state, RandomEngine& engine, const double delta_log_energy, - ApplyMove&& apply_move ) const + const ApplyMove& apply_move ) const { const auto& proposed_move = proposal.proposed_move; StepResult< ObjectType > step_result; @@ -173,11 +181,12 @@ namespace geode double log_u = engine.sample_log(); step_result.decision = ( log_u < step_result.log_accept ) - ? MHDecision::Accepted - : MHDecision::Rejected; - if( step_result.decision == MHDecision::Accepted ) + ? MH_DECISION::accepted + : MH_DECISION::rejected; + if( step_result.decision == MH_DECISION::accepted ) + { apply_move( state, proposal ); - + } return step_result; } @@ -192,7 +201,7 @@ namespace geode []( auto& cur_state, auto& accepted_proposal ) { cur_state.add_object( std::move( accepted_proposal.proposed_move.new_object - .value() ), + .value() ), accepted_proposal.set_id, false ); } ); }; @@ -224,7 +233,7 @@ namespace geode cur_state.update_free_object( accepted_proposal.old_object_id(), std::move( accepted_proposal.proposed_move.new_object - .value() ) ); + .value() ) ); } ); }; diff --git a/include/geode/stochastic/spatial/object_set.hpp b/include/geode/stochastic/spatial/object_set.hpp index 12c2de4..0311562 100644 --- a/include/geode/stochastic/spatial/object_set.hpp +++ b/include/geode/stochastic/spatial/object_set.hpp @@ -31,6 +31,8 @@ namespace geode template < typename Type > class ObjectSet : public Identifier { + OPENGEODE_DISABLE_COPY( ObjectSet ); + public: ObjectSet() noexcept = default; ObjectSet( ObjectSet&& ) noexcept = default; 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 579d3d3..d445dc1 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 @@ -33,7 +33,7 @@ namespace geode public: explicit SegmentLengthInsideBoxFeature( double characteristic_length ); - double evaluate( const OwnerSegment2D& segment, + [[nodiscard]] double evaluate( const OwnerSegment2D& segment, const SpatialDomain< 2 >& domain ) const override; private: diff --git a/src/geode/stochastic/CMakeLists.txt b/src/geode/stochastic/CMakeLists.txt index 7425bec..6ed09a8 100644 --- a/src/geode/stochastic/CMakeLists.txt +++ b/src/geode/stochastic/CMakeLists.txt @@ -29,6 +29,8 @@ add_geode_library( "spatial/object_neighborhood.cpp" "spatial/single_object_features/segment_length_feature.cpp" "spatial/pairwise_interactions/distance_cutoff.cpp" + "sampling/direct/object_set_sampler/segment_set_sampler.cpp" + "sampling/direct/object_set_sampler/point_set_sampler.cpp" "sampling/direct/ball_sampler.cpp" "sampling/direct/bounding_box_sampler.cpp" "sampling/direct/double_sampler.cpp" diff --git a/src/geode/stochastic/applications/poisson_process.cpp b/src/geode/stochastic/applications/poisson_process.cpp index f0ef580..788711b 100644 --- a/src/geode/stochastic/applications/poisson_process.cpp +++ b/src/geode/stochastic/applications/poisson_process.cpp @@ -1,4 +1,25 @@ -#pragma once +/* + * 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 @@ -56,9 +77,8 @@ namespace geode { continue; } - targets.push_back( geode::TargetStatisticConfig{ - set_desc.density_name, *set_desc.expected_nb_objects, 0.1 } ); + set_desc.density_name, *set_desc.expected_nb_objects } ); } return targets; diff --git a/src/geode/stochastic/applications/strauss_process.cpp b/src/geode/stochastic/applications/strauss_process.cpp index 105df8f..fb9fc38 100644 --- a/src/geode/stochastic/applications/strauss_process.cpp +++ b/src/geode/stochastic/applications/strauss_process.cpp @@ -1,9 +1,55 @@ -#pragma once +/* + * 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 { + template < typename ObjectType > + void add_intra_set_interactions( + std::vector< std::pair< std::string, std::string > >& interactions, + const std::vector< std::string >& set_names ) + { + for( const auto id : geode::Range{ set_names.size() } ) + { + interactions.emplace_back( set_names[id], set_names[id] ); + } + } + + template < typename ObjectType > + void add_inter_set_interactions( + std::vector< std::pair< std::string, std::string > >& interactions, + const std::vector< std::string >& set_names ) + { + for( const auto id1 : geode::Range{ set_names.size() } ) + { + for( const auto id2 : geode::Range{ id1 + 1, set_names.size() } ) + { + interactions.emplace_back( set_names[id1], set_names[id2] ); + } + } + } + template < typename ObjectType > std::vector< std::pair< std::string, std::string > > build_interaction_set_names( @@ -11,35 +57,19 @@ namespace interaction_desc ) { std::vector< std::pair< std::string, std::string > > interactions; - const auto& set_names = interaction_desc.set_names; - - if( set_names.empty() - || ( !interaction_desc.include_intra_set - && !interaction_desc.include_inter_set ) ) + if( set_names.empty() ) { return interactions; } - - for( const auto name1_id : geode::Range{ set_names.size() } ) + if( interaction_desc.include_intra_set ) { - if( interaction_desc.include_intra_set ) - { - interactions.emplace_back( - set_names[name1_id], set_names[name1_id] ); - } - - if( interaction_desc.include_inter_set ) - { - for( const auto name2_id : - geode::Range{ name1_id, set_names.size() } ) - { - interactions.emplace_back( - set_names[name1_id], set_names[name2_id] ); - } - } + add_intra_set_interactions< ObjectType >( interactions, set_names ); + } + if( interaction_desc.include_inter_set ) + { + add_inter_set_interactions< ObjectType >( interactions, set_names ); } - return interactions; } } // namespace @@ -111,7 +141,7 @@ namespace geode continue; } targets.push_back( geode::TargetStatisticConfig{ - set_desc.density_name, *set_desc.expected_nb_objects, 0.1 } ); + set_desc.density_name, *set_desc.expected_nb_objects } ); } for( const auto& inter_desc : description.interactions ) { @@ -121,7 +151,7 @@ namespace geode } targets.push_back( geode::TargetStatisticConfig{ inter_desc.interaction_name, - *inter_desc.expected_nb_interactions, 0.1 } ); + *inter_desc.expected_nb_interactions } ); } return targets; diff --git a/src/geode/stochastic/sampling/direct/object_set_sampler/point_set_sampler.cpp b/src/geode/stochastic/sampling/direct/object_set_sampler/point_set_sampler.cpp new file mode 100644 index 0000000..4bd7ce7 --- /dev/null +++ b/src/geode/stochastic/sampling/direct/object_set_sampler/point_set_sampler.cpp @@ -0,0 +1,125 @@ +/* + * 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 + +#include +#include +#include + +namespace geode +{ + template < index_t dimension > + UniformPointSetSampler< dimension >::UniformPointSetSampler( + const SpatialDomain< dimension >& domain, + const ObjectSamplerConfig< Point< dimension > >& config ) + : ObjectSetSampler< Point< dimension > >{}, domain_{ domain } + { + auto volume = domain_.extended_n_volume(); + OpenGeodeStochasticStochasticException::check_exception( volume != 0., + nullptr, OpenGeodeException::TYPE::data, + "[UniformPointSetSampler] Undefined Extended Bounding " + "Box (volume ==0)." ); + this->set_log_pdf( -std::log( volume ) ); + step_move_ = define_step_for_move( config.move_ratio ); + OpenGeodeStochasticStochasticException::check_exception( + step_move_ > 0., nullptr, OpenGeodeException::TYPE::data, + "[UniformPointSetSampler] Undefined step length for move " + "(value == ", + step_move_, ")." ); + } + template < index_t dimension > + Point< dimension > UniformPointSetSampler< dimension >::sample( + RandomEngine& engine ) const + { + return PointUniformSampler::sample< dimension >( + engine, domain_.extended_box() ); + } + + template < index_t dimension > + Point< dimension > UniformPointSetSampler< dimension >::change( + const Point< dimension >& obj, RandomEngine& engine ) const + { + geode::Sphere< dimension > ball{ obj, step_move_ }; + + auto new_point = + PointUniformSampler::sample< dimension >( engine, ball ); + constexpr index_t MAX_TRY{ 100 }; + for( const auto try_id : geode::Range{ MAX_TRY } ) + { + geode_unused( try_id ); + if( domain_.extended_contains( new_point ) ) + { + return new_point; + } + new_point = + PointUniformSampler::sample< dimension >( engine, ball ); + } + throw OpenGeodeStochasticStochasticException{ nullptr, + OpenGeodeException::TYPE::internal, + "[UniformPointSetSampler] Cannot find a point in the " + "extended domain" }; + } + + template < index_t dimension > + double UniformPointSetSampler< dimension >::define_step_for_move( + double ratio ) + { + return ratio * domain_.smallest_length(); + } + + template < index_t dimension > + bool UniformPointSetSampler< dimension >::is_valid_object( + const Point< dimension >& obj ) const + { + return domain_.extended_contains( obj ); + } + + template class opengeode_stochastic_stochastic_api + UniformPointSetSampler< 2 >; + template class opengeode_stochastic_stochastic_api + UniformPointSetSampler< 3 >; + + template <> + std::unique_ptr< ObjectSetSampler< Point2D > > + opengeode_stochastic_stochastic_api build_objectset_sampler< Point2D >( + const SpatialDomain< 2 >& domain, + const ObjectSamplerConfig< Point2D >& config ) + { + return std::make_unique< UniformPointSetSampler< 2 > >( + domain, config ); + } + + template <> + opengeode_stochastic_stochastic_api + std::unique_ptr< ObjectSetSampler< Point3D > > + build_objectset_sampler< Point3D >( const SpatialDomain< 3 >& domain, + const ObjectSamplerConfig< Point3D >& config ) + { + return std::make_unique< UniformPointSetSampler< 3 > >( + domain, config ); + } + +} // namespace geode \ No newline at end of file diff --git a/src/geode/stochastic/sampling/direct/object_set_sampler/segment_set_sampler.cpp b/src/geode/stochastic/sampling/direct/object_set_sampler/segment_set_sampler.cpp new file mode 100644 index 0000000..1e5335c --- /dev/null +++ b/src/geode/stochastic/sampling/direct/object_set_sampler/segment_set_sampler.cpp @@ -0,0 +1,110 @@ +/* + * 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 + +#include +#include +#include +#include + +namespace geode +{ + UniformSegmentSetSampler::UniformSegmentSetSampler( + const SpatialDomain< 2 >& domain, + const ObjectSamplerConfig< OwnerSegment2D >& config ) + : ObjectSetSampler< OwnerSegment2D >{}, + domain_{ domain }, + length_{ DoubleSampler::create_distribution( config.length ) }, + azimuth_{ DoubleSampler::create_distribution( config.azimuth ) }, + move_ratio_{ config.move_ratio } + { + auto volume = domain_.extended_n_volume(); + OpenGeodeStochasticStochasticException::check_exception( volume != 0., + nullptr, OpenGeodeException::TYPE::data, + "[UniformSegmentSetSampler] Undefined Extended Bounding " + "Box (volume ==0)." ); + this->set_log_pdf( -std::log( volume ) ); + } + + OwnerSegment2D UniformSegmentSetSampler::sample( + RandomEngine& engine ) const + { + auto seg = SegmentUniformSampler::sample( + engine, domain_.extended_box(), length_, azimuth_ ); + return seg; + } + + OwnerSegment2D UniformSegmentSetSampler::change( + const OwnerSegment2D& obj, RandomEngine& engine ) const + { + const auto& extremities = obj.vertices(); + const auto current = + static_cast< local_index_t >( engine.sample_bernoulli( 0.5 ) ); + const auto other = 1 - current; + + geode::Sphere< 2 > ball{ extremities[current], + move_ratio_ * obj.length() }; + + auto new_point = PointUniformSampler::sample< 2 >( engine, ball ); + constexpr index_t MAX_TRY{ 100 }; + for( const auto try_id : geode::Range{ MAX_TRY } ) + { + geode_unused( try_id ); + if( domain_.extended_contains( new_point ) + || domain_.extended_contains( extremities[other] ) ) + { + OwnerSegment2D new_segment{ obj }; + new_segment.set_point( current, new_point ); + return new_segment; + } + new_point = PointUniformSampler::sample< 2 >( engine, ball ); + } + throw OpenGeodeStochasticStochasticException{ nullptr, + OpenGeodeException::TYPE::internal, + "[SegmentSetSampler] - Cannot find a point in the box" }; + return obj; + } + + bool UniformSegmentSetSampler::is_valid_object( + const OwnerSegment2D& obj ) const + { + const auto& extremities = obj.vertices(); + return domain_.extended_contains( extremities[0] ) + || domain_.extended_contains( extremities[1] ); + } + + template <> + std::unique_ptr< ObjectSetSampler< OwnerSegment2D > > + build_objectset_sampler( const SpatialDomain< 2 >& domain, + const ObjectSamplerConfig< OwnerSegment2D >& config ) + { + return std::make_unique< UniformSegmentSetSampler >( domain, config ); + } + +} // namespace geode \ No newline at end of file diff --git a/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp b/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp index dae7ab8..51e4eaf 100644 --- a/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp +++ b/tests/stochastic/sampling/mcmc/proposal/test-proposal-kernel.cpp @@ -35,12 +35,11 @@ namespace { geode::uuid init_object_set( geode::ObjectSets< geode::Point2D >& pattern ) { - geode::Point2D p1{ { 0., 0. } }; - geode::Point2D p2{ { 1., 1. } }; - + // NOLINTBEGIN(*-magic-numbers) auto set_id = pattern.add_set( "default_name" ); - pattern.add_object( std::move( p1 ), set_id, false ); - pattern.add_object( std::move( p2 ), set_id, false ); + pattern.add_object( geode::Point2D{ { 0., 0. } }, set_id, false ); + pattern.add_object( geode::Point2D{ { 1., 1. } }, set_id, false ); + // NOLINTEND(*-magic-numbers) return set_id; } @@ -50,12 +49,10 @@ namespace geode::ObjectSets< geode::Point2D > config; auto set_id = init_object_set( config ); - geode::Point2D min_point{ { 0., 0. } }; - geode::Point2D max_point{ { 10., 100. } }; - + // NOLINTBEGIN(*-magic-numbers) geode::BoundingBox2D box; - box.add_point( min_point ); - box.add_point( max_point ); + box.add_point( geode::Point2D{ { 0., 0. } } ); + box.add_point( geode::Point2D{ { 10., 100. } } ); geode::SpatialDomain domain( box, 0. ); geode::ObjectSamplerConfig< geode::Point2D > sampler_config; @@ -67,10 +64,13 @@ namespace geode::RandomEngine engine; - bool saw_birth = false, saw_death = false, saw_change = false; + bool saw_birth = false; + bool saw_death = false; + bool saw_change = false; - for( const auto i : geode::Range{ 400 } ) + for( const auto count : geode::Range{ 400 } ) { + geode_unused( count ); auto proposal = kernel->propose( config, engine ); const auto& proposed_move = proposal.proposed_move; @@ -174,6 +174,7 @@ namespace proposed_move.type == geode::MoveType::Birth || proposed_move.type == geode::MoveType::Invalid, "[test proposal] On empty config, only Birth should be possible." ); + // NOLINTEND(*-magic-numbers) } } // namespace int main() diff --git a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp index b753854..14b382e 100644 --- a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp +++ b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp @@ -92,23 +92,23 @@ namespace // Invariant: fixed object must remain geode::OpenGeodeStochasticStochasticException::test( - result.decision == geode::MHDecision::Accepted - || result.decision == geode::MHDecision::Rejected, - "[MH test] decision should be Accepted or Rejected." ); + result.decision == geode::MH_DECISION::accepted + || result.decision == geode::MH_DECISION::rejected, + "[MH test] decision should be accepted or rejected." ); // Log each step (optional: comment out if too verbose) // geode::Logger::info( "Step: ", count, // " move_type= ", static_cast< int >( // result.move_type ), " decision= ", result.decision - // == geode::MHDecision::Accepted ? "Accepted" + // == geode::MH_DECISION::accepted ? "accepted" // : - // "Rejected", + // "rejected", // " delta_log_energy = ", result.delta_log_energy, // " log_accept = ", result.log_accept, // " state_size = ", state.size() ); // Keep track of accepted moves by type - if( result.decision == geode::MHDecision::Accepted ) + if( result.decision == geode::MH_DECISION::accepted ) { nb_accepted++; switch( result.move_type ) @@ -135,7 +135,7 @@ namespace " Mean objects = ", static_cast< double >( stat_sum ) / static_cast< double >( count ), - " nb accepted = ", nb_accepted, " Accepted(B/D/C) = ", + " nb accepted = ", nb_accepted, " accepted(B/D/C) = ", static_cast< double >( accepted_birth ) / static_cast< double >( nb_accepted ), " ", diff --git a/tests/stochastic/sampling/test-random-engine.cpp b/tests/stochastic/sampling/test-random-engine.cpp index f6b2e1d..eef51b8 100644 --- a/tests/stochastic/sampling/test-random-engine.cpp +++ b/tests/stochastic/sampling/test-random-engine.cpp @@ -28,204 +28,225 @@ #include #include - -const int NUMBER_OF_SAMPLES = 100000; - -void test_reproducibility() -{ - std::vector< std::string > seeds = { "same-seed", "geode-solutions", - "dfgzejhèçsodj", "&%;:!§" }; - for( const auto seed : seeds ) - { // Create first engine geode:: - geode::RandomEngine engine1; - engine1.set_seed( seed ); - - // Create second engine with same seed - geode::RandomEngine engine2; - engine2.set_seed( seed ); - - // Define a uniform distribution - geode::UniformClosed< int > dist; - dist.min_value = 1; - dist.max_value = 100; - - // Sample more values to check sequence reproducibility - for( const auto value : geode::Range{ 100 } ) - { - 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 " ); - } - geode::Logger::info( "Test Reproducibility: SUCCESS " ); -} - -template < typename T > -double compute_mean( const std::vector< T >& data ) +namespace { - double sum = std::accumulate( data.begin(), data.end(), 0.0 ); - return sum / data.size(); -} + const int NUMBER_OF_SAMPLES = 100000; -template < typename T > -double compute_variance( const std::vector< T >& data, double mean ) -{ - double accum = 0.0; - for( auto& x : data ) + void test_reproducibility() { - double diff = x - mean; - accum += diff * diff; + std::vector< std::string > seeds = { "same-seed", "geode-solutions", + "dfgzejhèçsodj", "&%;:!§" }; + for( const auto& seed : seeds ) + { // Create first engine geode:: + geode::RandomEngine engine1; + engine1.set_seed( seed ); + + // Create second engine with same seed + geode::RandomEngine engine2; + engine2.set_seed( seed ); + + // NOLINTBEGING(*-magic-numbers) + + // Define a uniform distribution + geode::UniformClosed< int > dist; + dist.min_value = 1; + dist.max_value = 100; + + // Sample more values to check sequence reproducibility + for( const auto value : geode::Range{ 100 } ) + { + geode_unused( value ); + 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 " ); + } + geode::Logger::info( "Test Reproducibility: SUCCESS " ); + // NOLINTEND(*-magic-numbers) } - return accum / ( data.size() - 1 ); -} -template < typename T > -void test_distribution_mean_and_variance( const std::vector< T >& data, - double expected_mean, - double expected_var, - double k = 5.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::fabs( mean - expected_mean ) < k * se_mean, - "[Uniform] - Wrong expected mean." ); - geode::OpenGeodeStochasticStochasticException::test( - std::fabs( 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 > ) + template < typename T > + double compute_mean( const std::vector< T >& data ) { - const double distance = - static_cast< double >( max_value - min_value + 1 ); - return ( distance * distance - 1.0 ) / 12.0; + double sum = std::accumulate( data.begin(), data.end(), 0.0 ); + return sum / data.size(); } - else if constexpr( std::is_floating_point_v< T > ) + + template < typename T > + double compute_variance( const std::vector< T >& data, double mean ) { - const double distance = static_cast< double >( max_value - min_value ); - return ( distance * distance ) / 12.0; + double accum = 0.0; + for( auto& x : data ) + { + double diff = x - mean; + accum += diff * diff; + } + return accum / ( data.size() - 1 ); } - else + template < typename T > + void test_distribution_mean_and_variance( const std::vector< T >& data, + double expected_mean, + double expected_var, + double k = 5.0 ) { - static_assert( std::is_arithmetic_v< T >, - "Unsupported type to compute theoretical variance." ); - } -} + // NOLINTBEGING(*-magic-numbers) + 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 ) ); -template < typename Type > -void test_uniform( - const Type min_value, const Type max_value, geode::RandomEngine& engine ) -{ - std::vector< Type > samples; - samples.reserve( NUMBER_OF_SAMPLES ); + geode::OpenGeodeStochasticStochasticException::test( + std::fabs( mean - expected_mean ) < k * se_mean, + "[Uniform] - Wrong expected mean." ); + geode::OpenGeodeStochasticStochasticException::test( + std::fabs( variance - expected_var ) < k * se_var, + "[Uniform] - Wrong expected std." ); + // NOLINTEND(*-magic-numbers) + } - for( const auto i : geode::Range{ NUMBER_OF_SAMPLES } ) + template < typename T > + double compute_expected_variance( T min_value, T max_value ) { - geode::UniformClosed< Type > dist_closed; - dist_closed.min_value = min_value; - dist_closed.max_value = max_value; + // NOLINTBEGING(*-magic-numbers) - Type value = engine.sample_uniform( dist_closed ); - samples.emplace_back( value ); - geode::OpenGeodeStochasticStochasticException::test( - value >= min_value && value <= max_value, - "[Uniform] - value out of range." ); + if constexpr( std::is_integral_v< T > ) + { + 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 distance = + static_cast< double >( max_value - min_value ); + return ( distance * distance ) / 12.0; + } + else + { + static_assert( std::is_arithmetic_v< T >, + "Unsupported type to compute theoretical variance." ); + } + // NOLINTEND(*-magic-numbers) } - double expected_mean = ( min_value + max_value ) / 2.0; - double expected_var = compute_expected_variance( min_value, max_value ); + template < typename Type > + void test_uniform( const Type min_value, + const Type max_value, + geode::RandomEngine& engine ) + { + std::vector< Type > samples; + samples.reserve( NUMBER_OF_SAMPLES ); - test_distribution_mean_and_variance( samples, expected_mean, expected_var ); -} + for( const auto count : geode::Range{ NUMBER_OF_SAMPLES } ) + { + geode_unused( count ); + geode::UniformClosed< Type > dist_closed; + dist_closed.min_value = min_value; + dist_closed.max_value = max_value; -void test_gaussian( - double mean_value, double std_value, geode::RandomEngine& engine ) -{ - std::vector< double > samples; - samples.reserve( NUMBER_OF_SAMPLES ); - geode::Gaussian spec; - spec.mean = mean_value; - spec.standard_deviation = std_value; + Type value = engine.sample_uniform( dist_closed ); + samples.emplace_back( value ); + geode::OpenGeodeStochasticStochasticException::test( + value >= min_value && value <= max_value, + "[Uniform] - value out of range." ); + } - for( const auto i : geode::Range{ NUMBER_OF_SAMPLES } ) - { - double value = engine.sample_gaussian( spec ); - samples.emplace_back( value ); - } + double expected_mean = ( min_value + max_value ) / 2.0; + double expected_var = compute_expected_variance( min_value, max_value ); - test_distribution_mean_and_variance( samples, mean_value, std_value ); -} + test_distribution_mean_and_variance( + samples, expected_mean, expected_var ); + } -void test_truncated_gaussian( double mean_value, - double std_value, - std::optional< double > min_value, - std::optional< double > max_value, - geode::RandomEngine& engine ) -{ - const auto max = - max_value.value_or( std::numeric_limits< double >::infinity() ); - const auto min = - min_value.value_or( -std::numeric_limits< double >::infinity() ); + void test_gaussian( + double mean_value, double std_value, geode::RandomEngine& engine ) + { + std::vector< double > samples; + samples.reserve( NUMBER_OF_SAMPLES ); + geode::Gaussian spec; + spec.mean = mean_value; + spec.standard_deviation = std_value; - std::vector< double > samples; - samples.reserve( NUMBER_OF_SAMPLES ); + for( const auto count : geode::Range{ NUMBER_OF_SAMPLES } ) + { + geode_unused( count ); + double value = engine.sample_gaussian( spec ); + samples.emplace_back( value ); + } - geode::TruncatedGaussian spec; - spec.mean = mean_value; - spec.standard_deviation = std_value; - spec.min_value = min_value; - spec.max_value = max_value; + test_distribution_mean_and_variance( samples, mean_value, std_value ); + } - for( const auto i : geode::Range{ NUMBER_OF_SAMPLES } ) + void test_truncated_gaussian( double mean_value, + double std_value, + std::optional< double > min_value, + std::optional< double > max_value, + geode::RandomEngine& engine ) { - double value = engine.sample_truncated_gaussian( spec ); - samples.emplace_back( value ); - geode::OpenGeodeStochasticStochasticException::test( - value >= min && value <= max, "[Gaussian] - value out of range." ); - } + const auto max = + max_value.value_or( std::numeric_limits< double >::infinity() ); + const auto min = + min_value.value_or( -std::numeric_limits< double >::infinity() ); - // Implementer un test de Chi2 ou Kolmogorov–Smirnov -} + std::vector< double > samples; + samples.reserve( NUMBER_OF_SAMPLES ); -void test_bernoulli( - double probability_of_success, geode::RandomEngine& engine ) -{ - int success_count = 0; + geode::TruncatedGaussian spec; + spec.mean = mean_value; + spec.standard_deviation = std_value; + spec.min_value = min_value; + spec.max_value = max_value; - for( const auto i : geode::Range{ NUMBER_OF_SAMPLES } ) - { - if( engine.sample_bernoulli( probability_of_success ) ) + for( const auto count : geode::Range{ NUMBER_OF_SAMPLES } ) { - ++success_count; + geode_unused( count ); + double value = engine.sample_truncated_gaussian( spec ); + samples.emplace_back( value ); + geode::OpenGeodeStochasticStochasticException::test( + value >= min && value <= max, + "[Gaussian] - value out of range." ); } + + // Implementer un test de Chi2 ou Kolmogorov–Smirnov } - const double empirical_probability = - static_cast< double >( success_count ) / NUMBER_OF_SAMPLES; + void test_bernoulli( + double probability_of_success, geode::RandomEngine& engine ) + { + int success_count = 0; - geode::Logger::info( "Test Bernoulli ", - ": empirical / expected = ", empirical_probability, " / ", - probability_of_success ); + for( const auto count : geode::Range{ NUMBER_OF_SAMPLES } ) + { + geode_unused( count ); + if( engine.sample_bernoulli( probability_of_success ) ) + { + ++success_count; + } + } - geode::OpenGeodeStochasticStochasticException::test( - abs( empirical_probability - probability_of_success ) < 0.05, - "[Bernoulli] - Empirical probability out of tolerance." ); -} + const double empirical_probability = + static_cast< double >( success_count ) / NUMBER_OF_SAMPLES; + + geode::Logger::info( "Test Bernoulli ", + ": empirical / expected = ", empirical_probability, " / ", + probability_of_success ); + // NOLINTBEGING(*-magic-numbers) + geode::OpenGeodeStochasticStochasticException::test( + abs( empirical_probability - probability_of_success ) < 0.05, + "[Bernoulli] - Empirical probability out of tolerance." ); + // NOLINTEND(*-magic-numbers) + } +} // namespace int main() { try @@ -236,6 +257,7 @@ int main() random_engine.set_seed( "@-test-radom-engine@" ); + // NOLINTBEGING(*-magic-numbers) 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 ); @@ -264,7 +286,7 @@ int main() test_bernoulli( 0.06, random_engine ); test_bernoulli( 0.96, random_engine ); geode::Logger::info( "TEST BERNOUILLI SAMPLING - SUCCESS" ); - + // NOLINTEND(*-magic-numbers) return 0; } catch( ... ) From 10e578ad378e2c91a20e5a382236aa8117fff794 Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Tue, 2 Jun 2026 17:26:58 +0000 Subject: [PATCH 53/59] Apply prepare changes --- .../stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp index f953ff8..0f6539d 100644 --- a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp +++ b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp @@ -201,7 +201,7 @@ namespace geode []( auto& cur_state, auto& accepted_proposal ) { cur_state.add_object( std::move( accepted_proposal.proposed_move.new_object - .value() ), + .value() ), accepted_proposal.set_id, false ); } ); }; @@ -233,7 +233,7 @@ namespace geode cur_state.update_free_object( accepted_proposal.old_object_id(), std::move( accepted_proposal.proposed_move.new_object - .value() ) ); + .value() ) ); } ); }; From d526b8f845876ecac5aa386d7993dc1d3ca18988 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Tue, 2 Jun 2026 20:27:20 +0200 Subject: [PATCH 54/59] more tidy --- .../object_set_sampler/point_set_sampler.hpp | 2 ++ .../object_set_sampler/segment_set_sampler.hpp | 6 ++++-- .../sampling/mcmc/metropolis_hasting_sampler.hpp | 2 +- include/geode/stochastic/spatial/object_set.hpp | 15 ++++++++------- .../stochastic/applications/strauss_process.cpp | 4 ++-- .../object_set_sampler/point_set_sampler.cpp | 5 +++-- .../object_set_sampler/segment_set_sampler.cpp | 5 +---- 7 files changed, 21 insertions(+), 18 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 3784cb4..e4d65d0 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 @@ -31,7 +31,9 @@ namespace geode struct ObjectSamplerConfig< Point< dimension > > { // use to define the step for change move (move_ratio*domain volume) + // NOLINTBEGING(*-magic-numbers) double move_ratio{ 0.1 }; + // NOLINTEND(*-magic-numbers) }; template < index_t dimension > 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 0a6e630..62196f1 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 @@ -33,7 +33,9 @@ namespace geode template <> struct ObjectSamplerConfig< OwnerSegment2D > { - double move_ratio = 0.1; + // NOLINTBEGING(*-magic-numbers) + double move_ratio{ 0.1 }; + // NOLINTEND(*-magic-numbers) DoubleSampler::DistributionDescription length; DoubleSampler::DistributionDescription azimuth; @@ -59,7 +61,7 @@ namespace geode const SpatialDomain< 2 >& domain_; DoubleSampler::Distribution length_; DoubleSampler::Distribution azimuth_; - double move_ratio_{ 0.1 }; + double move_ratio_; }; } // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp index 0f6539d..2f96b79 100644 --- a/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp +++ b/include/geode/stochastic/sampling/mcmc/metropolis_hasting_sampler.hpp @@ -29,7 +29,7 @@ namespace geode { - enum struct MH_DECISION + enum struct MH_DECISION : local_index_t { accepted, rejected, diff --git a/include/geode/stochastic/spatial/object_set.hpp b/include/geode/stochastic/spatial/object_set.hpp index 0311562..876888b 100644 --- a/include/geode/stochastic/spatial/object_set.hpp +++ b/include/geode/stochastic/spatial/object_set.hpp @@ -35,12 +35,13 @@ namespace geode public: ObjectSet() noexcept = default; + ~ObjectSet() = default; ObjectSet( ObjectSet&& ) noexcept = default; ObjectSet& operator=( ObjectSet&& ) noexcept = default; void set_name( std::string_view name ); - const Type& get_fixed_object( index_t index ) const; - const Type& get_free_object( index_t index ) const; + [[nodiscard]] const Type& get_fixed_object( index_t index ) const; + [[nodiscard]] const Type& get_free_object( index_t index ) const; index_t add_fixed_object( Type&& object ); @@ -48,13 +49,13 @@ namespace geode void update_free_object( index_t index, Type&& object ); void remove_free_object( index_t index ); - index_t nb_objects() const; - index_t nb_fixed_objects() const; - index_t nb_free_objects() const; + [[nodiscard]] index_t nb_objects() const; + [[nodiscard]] index_t nb_fixed_objects() const; + [[nodiscard]] index_t nb_free_objects() const; - bool empty() const; + [[nodiscard]] bool empty() const; - std::string string() const; + [[nodiscard]] std::string string() const; private: std::vector< Type > fixed_objects_; diff --git a/src/geode/stochastic/applications/strauss_process.cpp b/src/geode/stochastic/applications/strauss_process.cpp index fb9fc38..69f5198 100644 --- a/src/geode/stochastic/applications/strauss_process.cpp +++ b/src/geode/stochastic/applications/strauss_process.cpp @@ -30,9 +30,9 @@ namespace std::vector< std::pair< std::string, std::string > >& interactions, const std::vector< std::string >& set_names ) { - for( const auto id : geode::Range{ set_names.size() } ) + for( const auto name_id : geode::Range{ set_names.size() } ) { - interactions.emplace_back( set_names[id], set_names[id] ); + interactions.emplace_back( set_names[name_id], set_names[name_id] ); } } diff --git a/src/geode/stochastic/sampling/direct/object_set_sampler/point_set_sampler.cpp b/src/geode/stochastic/sampling/direct/object_set_sampler/point_set_sampler.cpp index 4bd7ce7..fc14962 100644 --- a/src/geode/stochastic/sampling/direct/object_set_sampler/point_set_sampler.cpp +++ b/src/geode/stochastic/sampling/direct/object_set_sampler/point_set_sampler.cpp @@ -35,7 +35,9 @@ namespace geode UniformPointSetSampler< dimension >::UniformPointSetSampler( const SpatialDomain< dimension >& domain, const ObjectSamplerConfig< Point< dimension > >& config ) - : ObjectSetSampler< Point< dimension > >{}, domain_{ domain } + : ObjectSetSampler< Point< dimension > >{}, + domain_{ domain }, + step_move_( define_step_for_move( config.move_ratio ) ) { auto volume = domain_.extended_n_volume(); OpenGeodeStochasticStochasticException::check_exception( volume != 0., @@ -43,7 +45,6 @@ namespace geode "[UniformPointSetSampler] Undefined Extended Bounding " "Box (volume ==0)." ); this->set_log_pdf( -std::log( volume ) ); - step_move_ = define_step_for_move( config.move_ratio ); OpenGeodeStochasticStochasticException::check_exception( step_move_ > 0., nullptr, OpenGeodeException::TYPE::data, "[UniformPointSetSampler] Undefined step length for move " diff --git a/src/geode/stochastic/sampling/direct/object_set_sampler/segment_set_sampler.cpp b/src/geode/stochastic/sampling/direct/object_set_sampler/segment_set_sampler.cpp index 1e5335c..6d3f659 100644 --- a/src/geode/stochastic/sampling/direct/object_set_sampler/segment_set_sampler.cpp +++ b/src/geode/stochastic/sampling/direct/object_set_sampler/segment_set_sampler.cpp @@ -21,8 +21,6 @@ * */ -#pragma once - #include #include @@ -38,8 +36,7 @@ namespace geode UniformSegmentSetSampler::UniformSegmentSetSampler( const SpatialDomain< 2 >& domain, const ObjectSamplerConfig< OwnerSegment2D >& config ) - : ObjectSetSampler< OwnerSegment2D >{}, - domain_{ domain }, + : domain_{ domain }, length_{ DoubleSampler::create_distribution( config.length ) }, azimuth_{ DoubleSampler::create_distribution( config.azimuth ) }, move_ratio_{ config.move_ratio } From 775427d429c53a1b3cf39ba5519e1b6e0a359e4a Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 3 Jun 2026 16:51:09 +0200 Subject: [PATCH 55/59] feat(applications): implement fracture application --- .../stochastic/applications/fractures.hpp | 136 ++++++++++++ .../sampling/direct/double_sampler.hpp | 7 + .../segment_set_sampler.hpp | 5 +- .../mcmc/helpers/simulation_context.hpp | 5 + .../stochastic/spatial/spatial_domain.hpp | 10 + src/geode/stochastic/CMakeLists.txt | 2 + .../stochastic/applications/fractures.cpp | 114 ++++++++++ tests/stochastic/applications/CMakeLists.txt | 14 +- .../applications/test-fractures.cpp | 208 ++++++++++++++++++ .../applications/test-mh-fractures.cpp | 186 ---------------- 10 files changed, 492 insertions(+), 195 deletions(-) create mode 100644 include/geode/stochastic/applications/fractures.hpp create mode 100644 src/geode/stochastic/applications/fractures.cpp create mode 100644 tests/stochastic/applications/test-fractures.cpp delete mode 100644 tests/stochastic/applications/test-mh-fractures.cpp diff --git a/include/geode/stochastic/applications/fractures.hpp b/include/geode/stochastic/applications/fractures.hpp new file mode 100644 index 0000000..f7b219b --- /dev/null +++ b/include/geode/stochastic/applications/fractures.hpp @@ -0,0 +1,136 @@ +#pragma once + +#include +#include +#include +#include + +namespace geode +{ + using Fracture = OwnerSegment2D; + using FractureSamplerConfig = ObjectSamplerConfig< Fracture >; + using FractureSimulationContext = SimulationContext< Fracture >; + using FractureSimulationRunner = SimulationRunner< Fracture >; + + struct FractureSetDescription + { + std::string fset_name; + + FractureSamplerConfig sampler; + double birth_ratio{ 1.0 }; + double death_ratio{ 1.0 }; + double change_ratio{ 1.0 }; + + std::string density_name() const + { + return absl::StrCat( fset_name, "_p20" ); + } + double p20{ 0. }; + std::optional< double > expected_number; + + std::string intensity_name() const + { + return absl::StrCat( fset_name, "_p21" ); + } + double p21{ 0. }; + std::optional< double > expected_total_length; + + std::vector< std::array< geode::Point2D, 2 > > observed_fractures; + + std::string spacing_name() const + { + return absl::StrCat( fset_name, "_spacing" ); + } + double minimal_spacing{ 0. }; + + std::string string() const + { + auto message = + absl::StrCat( "FractureSetDescription: ", fset_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: ", sampler.length.string() ); + absl::StrAppend( &message, + "\n\t --> azimuth distribution: ", sampler.azimuth.string() ); + absl::StrAppend( &message, "\n\t --> ", density_name(), ": ", p20 ); + absl::StrAppend( + &message, "\n\t --> ", intensity_name(), ": ", p21 ); + absl::StrAppend( + &message, "\n\t --> ", spacing_name(), ": ", minimal_spacing ); + + absl::StrAppend( &message, + "\n\t --> dynamic move ratio - birth/death/change (", + birth_ratio, " / ", death_ratio, " / ", change_ratio, ")" ); + return message; + } + }; + + // struct FractureInterSetDescription + // { + // std::string interaction_name; + // + // std::vector< std::string > set_names; + // + // double gamma{ 1. }; + // double distance{ 0. }; + // + // bool include_intra_set{ true }; + // bool include_inter_set{ false }; + // + // std::optional< double > expected_nb_interactions; + // }; + + struct FractureNetworkDescription + { + std::string fnet_name; + + SpatialDomainConfig< 2 > domain; + + std::vector< FractureSetDescription > fracture_sets; + + FractureSetDescription& add_fracture_set( absl::string_view fset_name ) + { + auto& fracture_set = fracture_sets.emplace_back(); + fracture_set.fset_name = fset_name; + return fracture_set; + } + + std::string string() const + { + auto message = + absl::StrCat( "FractureNetworkDescription: ", fnet_name ); + absl::StrAppend( &message, "\n\t --> ", domain.string() ); + for( const auto& fset_desc : fracture_sets ) + { + absl::StrAppend( &message, "\n\t --> ", fset_desc.string() ); + } + return message; + } + // std::vector< StraussInteractionDescription< ObjectType > > + // inter_set_interactions; + // + // void add_x_node_monitoring( double beta_x_node ) + // { + // OpenGeodeStochasticStochasticException::check_exception( + // 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; + // } + }; + + FractureSimulationContext build_fractures_simulation_context( + const FractureNetworkDescription& description ); + + std::vector< geode::TargetStatisticConfig > build_fractures_targeted_stat( + const FractureNetworkDescription& description ); + +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/sampling/direct/double_sampler.hpp b/include/geode/stochastic/sampling/direct/double_sampler.hpp index c02e4b6..c79f8ab 100644 --- a/include/geode/stochastic/sampling/direct/double_sampler.hpp +++ b/include/geode/stochastic/sampling/direct/double_sampler.hpp @@ -48,6 +48,13 @@ namespace geode struct DistributionDescription { + DistributionDescription( absl::string_view name_in ) + : name{ name_in } + { + } + + DistributionDescription() = default; + std::string name{ "default_distribution" }; DistributionType distribution_type; 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 62196f1..819de70 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 @@ -37,8 +37,9 @@ namespace geode double move_ratio{ 0.1 }; // NOLINTEND(*-magic-numbers) - DoubleSampler::DistributionDescription length; - DoubleSampler::DistributionDescription azimuth; + DoubleSampler::DistributionDescription length{ "length" }; + + DoubleSampler::DistributionDescription azimuth{ "azimuth" }; }; class UniformSegmentSetSampler : public ObjectSetSampler< OwnerSegment2D > diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp index da4ae10..1108bbe 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_context.hpp @@ -116,6 +116,11 @@ namespace geode for( const auto& set_def : config.sets ) { auto set_id = context.object_sets->add_set( set_def.name ); + for( auto fixed_object : set_def.fixed_objects ) + { + context.object_sets->add_object( + std::move( fixed_object ), set_id, true ); + } auto sampler = build_objectset_sampler( *context.domain, set_def.sampler ); geode::add_birth_death_change_moves( *sampler, *proposal_kernel, diff --git a/include/geode/stochastic/spatial/spatial_domain.hpp b/include/geode/stochastic/spatial/spatial_domain.hpp index 58ff41a..82f147c 100644 --- a/include/geode/stochastic/spatial/spatial_domain.hpp +++ b/include/geode/stochastic/spatial/spatial_domain.hpp @@ -152,6 +152,16 @@ namespace geode Point< dimension > min_point; Point< dimension > max_point; double buffer_size{ 0.0 }; + std::string string() const + { + std::string message = "Domain: "; + absl::StrAppend( + &message, "\n\t --> Min point: ", min_point.string() ); + absl::StrAppend( + &message, "\n\t --> Max point: ", max_point.string() ); + absl::StrAppend( &message, "\n\t --> Buffer size: ", buffer_size ); + return message; + } }; template < index_t dimension > diff --git a/src/geode/stochastic/CMakeLists.txt b/src/geode/stochastic/CMakeLists.txt index 6ed09a8..81d5776 100644 --- a/src/geode/stochastic/CMakeLists.txt +++ b/src/geode/stochastic/CMakeLists.txt @@ -22,6 +22,7 @@ add_geode_library( NAME stochastic FOLDER "geode/stochastic" SOURCES + "applications/fractures.cpp" "applications/poisson_process.cpp" "applications/strauss_process.cpp" "spatial/object_set.cpp" @@ -41,6 +42,7 @@ add_geode_library( "sampling/random_engine.cpp" "common.cpp" PUBLIC_HEADERS + "applications/fractures.hpp" "applications/poisson_process.hpp" "applications/strauss_process.hpp" #"inference/abc_shadow.hpp" diff --git a/src/geode/stochastic/applications/fractures.cpp b/src/geode/stochastic/applications/fractures.cpp new file mode 100644 index 0000000..89586f4 --- /dev/null +++ b/src/geode/stochastic/applications/fractures.cpp @@ -0,0 +1,114 @@ +/* + * 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 +{ + std::vector< geode::Fracture > build_observed_fractures( + const std::vector< std::array< geode::Point2D, 2 > >& + fracture_extremities ) + { + std::vector< geode::Fracture > fractures; + for( const auto& extremities : fracture_extremities ) + { + fractures.emplace_back( + geode::OwnerSegment2D{ extremities[0], extremities[1] } ); + } + return fractures; + } +} // namespace + +namespace geode +{ + using FractureDensityDescription = SingleObjectTermConfig; + using FractureIntensityDescription = SingleObjectTermConfig; + using FractureSpacingDescription = PairwiseTermConfig; + + using FractureSimulationConfig = SimulationContextConfig< Fracture >; + + FractureSimulationContext build_fractures_simulation_context( + const FractureNetworkDescription& fnet_desc ) + { + FractureSimulationConfig simulation_config; + simulation_config.domain = fnet_desc.domain; + + for( const auto& fset_desc : fnet_desc.fracture_sets ) + { + auto& fset = simulation_config.add_set( fset_desc.fset_name ); + + fset.sampler = fset_desc.sampler; + fset.dynamics.birth_ratio = fset_desc.birth_ratio; + fset.dynamics.death_ratio = fset_desc.death_ratio; + fset.dynamics.change_ratio = fset_desc.change_ratio; + fset.fixed_objects = + build_observed_fractures( fset_desc.observed_fractures ); + + FractureDensityDescription density; + density.term_name = fset_desc.density_name(); + density.object_set_names = { fset_desc.fset_name }; + density.lambda = fset_desc.p20; + density.object_feature = ObjectInDomainFeatureConfig{}; + simulation_config.model.terms.emplace_back( std::move( density ) ); + + FractureIntensityDescription intensity; + intensity.term_name = fset_desc.intensity_name(); + intensity.object_set_names = { fset_desc.fset_name }; + intensity.lambda = fset_desc.p21; + constexpr double caracteristic_length = 1.0; + intensity.object_feature = + SegmentLengthInsideBoxFeatureConfig{ caracteristic_length }; + simulation_config.model.terms.emplace_back( + std::move( intensity ) ); + + FractureSpacingDescription spacing; + spacing.term_name = fset_desc.spacing_name(); + spacing.object_set_names_interactions = { { fset_desc.fset_name, + fset_desc.fset_name } }; + spacing.gamma = 0.; + spacing.interaction_config = + geode::MinimalDistanceCutoffConfig{ fset_desc.minimal_spacing }; + simulation_config.model.terms.emplace_back( std::move( spacing ) ); + } + + return build_simulation_context( simulation_config ); + } + + std::vector< geode::TargetStatisticConfig > build_fractures_targeted_stat( + const FractureNetworkDescription& description ) + { + std::vector< geode::TargetStatisticConfig > targets; + + for( const auto& set_desc : description.fracture_sets ) + { + if( set_desc.expected_number ) + { + targets.push_back( geode::TargetStatisticConfig{ + set_desc.density_name(), *set_desc.expected_number } ); + } + } + + return targets; + } + +} // namespace geode \ No newline at end of file diff --git a/tests/stochastic/applications/CMakeLists.txt b/tests/stochastic/applications/CMakeLists.txt index 7ae7d2b..7e1fa98 100644 --- a/tests/stochastic/applications/CMakeLists.txt +++ b/tests/stochastic/applications/CMakeLists.txt @@ -18,13 +18,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -#add_geode_test( -# SOURCE "sampling/mcmc/test-mh-fractures.cpp" -# DEPENDENCIES -# OpenGeode::basic -# OpenGeode::geometry -# ${PROJECT_NAME}::stochastic -#) +add_geode_test( + SOURCE "test-fractures.cpp" + DEPENDENCIES + OpenGeode::basic + OpenGeode::geometry + ${PROJECT_NAME}::stochastic +) add_geode_test( SOURCE "test-poisson-process.cpp" diff --git a/tests/stochastic/applications/test-fractures.cpp b/tests/stochastic/applications/test-fractures.cpp new file mode 100644 index 0000000..a831ce6 --- /dev/null +++ b/tests/stochastic/applications/test-fractures.cpp @@ -0,0 +1,208 @@ +/* + * 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 +{ + void test_fracture_simulator() + { + geode::Logger::info( "TEST - MH SINGLE SET FRACTURE SIMULATOR( with " + "intra-set interactions)" ); + + geode::RandomEngine engine; + engine.set_seed( "@mh-test-single-Fracture-set@" ); + + // NOLINTBEGIN(*-magic-numbers) + geode::FractureNetworkDescription fnet_desc; + fnet_desc.fnet_name = "One_Set_FNet"; + constexpr double domain_buffer{ 10 }; + fnet_desc.domain = { geode::Point2D{ { 0, 0 } }, + geode::Point2D{ { 100.0, 100.0 } }, domain_buffer }; + + // --- Object set + auto& fset = fnet_desc.add_fracture_set( "fset_A" ); + // fractures sampler + // desc.name = "uniform_closed"; + // desc.distribution_type = + // geode::UniformClosed< double + // >::distribution_type_static(); + // desc.min_value = 2.; + // desc.max_value = 5.; + + fset.sampler.length.distribution_type = + geode::UniformClosed< double >::distribution_type_static(); + fset.sampler.length.min_value = 1; + fset.sampler.length.max_value = 10.; + + fset.sampler.azimuth.distribution_type = + geode::UniformClosed< double >::distribution_type_static(); + fset.sampler.azimuth.min_value = 1; + fset.sampler.azimuth.max_value = 10.; + + fset.p20 = 0.05; + fset.p21 = 200; + fset.minimal_spacing = 1.; + + // observed fractures + fset.observed_fractures.push_back( { geode::Point2D{ { 0.0, 15. } }, + geode::Point2D{ { 15., 15. } } } ); + fset.observed_fractures.push_back( { geode::Point2D{ { 1.0, 11. } }, + geode::Point2D{ { 11., 20. } } } ); + + geode::Logger::info( fnet_desc.string() ); + + // runner + auto context = build_fractures_simulation_context( fnet_desc ); + geode::FractureSimulationRunner runner{ std::move( context ) }; + // run simulation + geode::SimulationConfigurator sim_config; + sim_config.realizations = 2000; + sim_config.metropolis_hasting_steps = 100; + sim_config.burn_in_steps = 1000; + + geode::SimulationPrinterConfigurator printer_config; + printer_config.output_folder = absl::StrCat( + printer_config.output_folder, "/sim_one_fracture_set_test" ); + sim_config.printer = printer_config; + + auto statistic_tracker = runner.run( engine, sim_config ); + + // NOLINTEND(*-magic-numbers) + + // const auto targeted_statistics_descriptors = + // build_fractures_targeted_stat( fnet_desc ); + // geode::TargetStatistics target_stats{ runner.model(), + // targeted_statistics_descriptors }; + // geode::statistics::validate( statistic_tracker, target_stats + // ); + // + const auto& fset_state = runner.state_realization().get_set( + runner.state_realization().get_set_uuid( + fnet_desc.fracture_sets[0].fset_name ) ); + + geode::OpenGeodeStochasticStochasticException::test( + fset_state.nb_fixed_objects() == 2, + "nd fixed object = ", fset_state.nb_fixed_objects() ); + geode::Logger::info( "--> SUCCESS!" ); + } + + // void test_two_fracture_sets_simulator() + // { + // geode::Logger::info( "TEST - MH TWO SET FRACTURE SIMULATOR (with " + // "intra-set interactions)" ); + // + // geode::RandomEngine engine; + // engine.set_seed( "@mh-test-single-Fracture-set@" ); + // + // geode::BoundingBox2D box; + // box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); + // box.add_point( geode::Point2D{ { 100.0, 100.0 } } ); + // // todo change + // geode::SpatialDomain domain( box, 0. ); + // + // // --- Object set + // geode::FractureSetDescription setA; + // setA.name = "A"; + // + // // length + // setA.length.distribution_type = + // geode::TruncatedPowerLaw::distribution_type_static(); + // setA.length.alpha = 2.; + // setA.length.min_value = 1; + // setA.length.max_value = 10.; + // + // // azimuth + // setA.azimuth.distribution_type = + // geode::UniformClosed< double >::distribution_type_static(); + // setA.azimuth.min_value = 1; + // setA.azimuth.max_value = 10.; + // + // // positionning + // setA.p20 = 0.05; + // setA.minimal_spacing = 1.; + // + // // --- Object set + // geode::FractureSetDescription setB; + // setB.name = "B"; + // + // // length + // setB.length.distribution_type = + // geode::TruncatedLogNormal::distribution_type_static(); + // setB.length.min_value = 1; + // setB.length.max_value = 50.; + // setB.length.mean = 1; + // setB.length.standard_deviation = 1.; + // // azimuth + // setB.azimuth.distribution_type = + // geode::VonMises::distribution_type_static(); + // setB.azimuth.mean = 60.; + // setB.azimuth.kappa = 1.; + // + // // positionning + // setB.p20 = 0.05; + // setB.minimal_spacing = 2.; + // + // geode::FractureSimulationRunner runner( domain ); + // runner.add_fracture_set_descriptor( setA ); + // runner.add_fracture_set_descriptor( setB ); + // runner.add_x_node_monitoring( 0.3 ); + // + // runner.initialize(); + // + // // run simulation + // geode::SimulationPrinterConfigurator printer_config; + // printer_config.output_folder = + // absl::StrCat( printer_config.output_folder, + // "/two_fracture_sets" ); + // + // geode::SimulationConfigurator sim_config; + // sim_config.realizations = 300; + // sim_config.metropolis_hasting_steps = 1000; + // sim_config.burn_in_steps = 1000; + // sim_config.printer = printer_config; + // + // auto statistic_monitoring = runner.run( engine, sim_config ); + // runner.check_statistics( statistic_monitoring ); + // + // geode::Logger::info( "--> SUCCESS!" ); + // } +} // namespace + +int main() +{ + try + { + geode::OpenGeodeStochasticStochasticLibrary::initialize(); + geode::Logger::set_level( geode::Logger::LEVEL::debug ); + test_fracture_simulator(); + // test_two_fracture_sets_simulator(); + return 0; + } + catch( ... ) + { + return geode::geode_lippincott(); + } +} \ No newline at end of file diff --git a/tests/stochastic/applications/test-mh-fractures.cpp b/tests/stochastic/applications/test-mh-fractures.cpp deleted file mode 100644 index 07104a4..0000000 --- a/tests/stochastic/applications/test-mh-fractures.cpp +++ /dev/null @@ -1,186 +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 - -namespace -{ - void test_fracture_simulator() - { - geode::Logger::info( "TEST - MH SINGLE SET FRACTURE SIMULATOR (with " - "intra-set interactions)" ); - - geode::RandomEngine engine; - engine.set_seed( "@mh-test-single-Fracture-set@" ); - - geode::BoundingBox2D box; - box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); - box.add_point( geode::Point2D{ { 100.0, 100.0 } } ); - // todo change - geode::SpatialDomain domain( box, 0. ); - // --- Object set - geode::FractureSetDescription setA; - setA.name = "A"; - setA.observed_fractures.push_back( { geode::Point2D{ { 0.0, 15. } }, - geode::Point2D{ { 15., 15. } } } ); - setA.observed_fractures.push_back( { geode::Point2D{ { 1.0, 11. } }, - geode::Point2D{ { 11., 20. } } } ); - // length - setA.length.distribution_type = - geode::UniformClosed< double >::distribution_type_static(); - setA.length.min_value = 1; - setA.length.max_value = 10.; - - // azimuth - setA.azimuth.distribution_type = - geode::UniformClosed< double >::distribution_type_static(); - setA.azimuth.min_value = 1; - setA.azimuth.max_value = 10.; - - // positionning - setA.p20 = 0.05; - setA.p21 = 200; - setA.minimal_spacing = 1.; - - geode::FractureSimulationRunner runner( domain ); - runner.add_fracture_set_descriptor( setA ); - - runner.initialize(); - - // geode::OpenGeodeStochasticStochasticException::test( - // runner.state_realization().nb_fixed_objects() == 2 ); - // run simulation - geode::SimulationPrinterConfigurator printer_config; - printer_config.output_folder = absl::StrCat( - printer_config.output_folder, "/single_fracture_set" ); - - geode::SimulationConfigurator sim_config; - sim_config.realizations = 300; - sim_config.metropolis_hasting_steps = 1000; - sim_config.burn_in_steps = 1000; - sim_config.printer = printer_config; - - auto statistic_monitoring = runner.run( engine, sim_config ); - runner.check_statistics( statistic_monitoring ); - - // geode::OpenGeodeStochasticStochasticException::test( - // runner.state_realization().nb_fixed_objects() == 2 ); - geode::Logger::info( "--> SUCCESS!" ); - } - - void test_two_fracture_sets_simulator() - { - geode::Logger::info( "TEST - MH TWO SET FRACTURE SIMULATOR (with " - "intra-set interactions)" ); - - geode::RandomEngine engine; - engine.set_seed( "@mh-test-single-Fracture-set@" ); - - geode::BoundingBox2D box; - box.add_point( geode::Point2D{ { 0.0, 0.0 } } ); - box.add_point( geode::Point2D{ { 100.0, 100.0 } } ); - // todo change - geode::SpatialDomain domain( box, 0. ); - - // --- Object set - geode::FractureSetDescription setA; - setA.name = "A"; - - // length - setA.length.distribution_type = - geode::TruncatedPowerLaw::distribution_type_static(); - setA.length.alpha = 2.; - setA.length.min_value = 1; - setA.length.max_value = 10.; - - // azimuth - setA.azimuth.distribution_type = - geode::UniformClosed< double >::distribution_type_static(); - setA.azimuth.min_value = 1; - setA.azimuth.max_value = 10.; - - // positionning - setA.p20 = 0.05; - setA.minimal_spacing = 1.; - - // --- Object set - geode::FractureSetDescription setB; - setB.name = "B"; - - // length - setB.length.distribution_type = - geode::TruncatedLogNormal::distribution_type_static(); - setB.length.min_value = 1; - setB.length.max_value = 50.; - setB.length.mean = 1; - setB.length.standard_deviation = 1.; - // azimuth - setB.azimuth.distribution_type = - geode::VonMises::distribution_type_static(); - setB.azimuth.mean = 60.; - setB.azimuth.kappa = 1.; - - // positionning - setB.p20 = 0.05; - setB.minimal_spacing = 2.; - - geode::FractureSimulationRunner runner( domain ); - runner.add_fracture_set_descriptor( setA ); - runner.add_fracture_set_descriptor( setB ); - runner.add_x_node_monitoring( 0.3 ); - - runner.initialize(); - - // run simulation - geode::SimulationPrinterConfigurator printer_config; - printer_config.output_folder = - absl::StrCat( printer_config.output_folder, "/two_fracture_sets" ); - - geode::SimulationConfigurator sim_config; - sim_config.realizations = 300; - sim_config.metropolis_hasting_steps = 1000; - sim_config.burn_in_steps = 1000; - sim_config.printer = printer_config; - - auto statistic_monitoring = runner.run( engine, sim_config ); - runner.check_statistics( statistic_monitoring ); - - geode::Logger::info( "--> SUCCESS!" ); - } -} // namespace - -int main() -{ - try - { - geode::OpenGeodeStochasticStochasticLibrary::initialize(); - geode::Logger::set_level( geode::Logger::LEVEL::debug ); - test_fracture_simulator(); - test_two_fracture_sets_simulator(); - return 0; - } - catch( ... ) - { - return geode::geode_lippincott(); - } -} \ No newline at end of file From 9459cb1e2d4bc4cc5be40a8c36d20f09d6458df0 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 3 Jun 2026 18:08:54 +0200 Subject: [PATCH 56/59] wind --- .../geode/stochastic/applications/fractures.hpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/include/geode/stochastic/applications/fractures.hpp b/include/geode/stochastic/applications/fractures.hpp index f7b219b..836b8a9 100644 --- a/include/geode/stochastic/applications/fractures.hpp +++ b/include/geode/stochastic/applications/fractures.hpp @@ -12,7 +12,7 @@ namespace geode using FractureSimulationContext = SimulationContext< Fracture >; using FractureSimulationRunner = SimulationRunner< Fracture >; - struct FractureSetDescription + struct opengeode_stochastic_stochastic_api FractureSetDescription { std::string fset_name; @@ -86,7 +86,7 @@ namespace geode // std::optional< double > expected_nb_interactions; // }; - struct FractureNetworkDescription + struct opengeode_stochastic_stochastic_api FractureNetworkDescription { std::string fnet_name; @@ -127,10 +127,13 @@ namespace geode // } }; - FractureSimulationContext build_fractures_simulation_context( - const FractureNetworkDescription& description ); + opengeode_stochastic_stochastic_api FractureSimulationContext + build_fractures_simulation_context( + const FractureNetworkDescription& description ); - std::vector< geode::TargetStatisticConfig > build_fractures_targeted_stat( - const FractureNetworkDescription& description ); + opengeode_stochastic_stochastic_api + std::vector< geode::TargetStatisticConfig > + build_fractures_targeted_stat( + const FractureNetworkDescription& description ); } // namespace geode \ No newline at end of file From fb14a03a818275977918795b730685b5a16ec5bc Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 3 Jun 2026 19:48:00 +0200 Subject: [PATCH 57/59] tidy --- .../stochastic/applications/fractures.hpp | 13 +++---- .../applications/poisson_process.hpp | 5 +-- .../applications/strauss_process.hpp | 5 +-- .../sampling/direct/double_sampler.hpp | 4 +-- .../object_set_sampler/point_set_sampler.hpp | 2 +- .../segment_set_sampler.hpp | 2 +- .../stochastic/spatial/spatial_domain.hpp | 3 +- .../stochastic/applications/fractures.cpp | 9 ++--- .../applications/test-fractures.cpp | 4 +-- .../applications/test-poisson-process.cpp | 8 ++--- .../applications/test-strauss-process.cpp | 8 ++--- .../mcmc/test-metropolis-hasting-sampler.cpp | 36 +++++++++++-------- .../sampling/test-random-engine.cpp | 14 ++++---- 13 files changed, 63 insertions(+), 50 deletions(-) diff --git a/include/geode/stochastic/applications/fractures.hpp b/include/geode/stochastic/applications/fractures.hpp index 836b8a9..9107a9a 100644 --- a/include/geode/stochastic/applications/fractures.hpp +++ b/include/geode/stochastic/applications/fractures.hpp @@ -21,14 +21,14 @@ namespace geode double death_ratio{ 1.0 }; double change_ratio{ 1.0 }; - std::string density_name() const + [[nodiscard]] std::string density_name() const { return absl::StrCat( fset_name, "_p20" ); } double p20{ 0. }; std::optional< double > expected_number; - std::string intensity_name() const + [[nodiscard]] std::string intensity_name() const { return absl::StrCat( fset_name, "_p21" ); } @@ -37,13 +37,13 @@ namespace geode std::vector< std::array< geode::Point2D, 2 > > observed_fractures; - std::string spacing_name() const + [[nodiscard]] std::string spacing_name() const { return absl::StrCat( fset_name, "_spacing" ); } double minimal_spacing{ 0. }; - std::string string() const + [[nodiscard]] std::string string() const { auto message = absl::StrCat( "FractureSetDescription: ", fset_name ); @@ -94,14 +94,15 @@ namespace geode std::vector< FractureSetDescription > fracture_sets; - FractureSetDescription& add_fracture_set( absl::string_view fset_name ) + [[nodiscard]] FractureSetDescription& add_fracture_set( + absl::string_view fset_name ) { auto& fracture_set = fracture_sets.emplace_back(); fracture_set.fset_name = fset_name; return fracture_set; } - std::string string() const + [[nodiscard]] std::string string() const { auto message = absl::StrCat( "FractureNetworkDescription: ", fnet_name ); diff --git a/include/geode/stochastic/applications/poisson_process.hpp b/include/geode/stochastic/applications/poisson_process.hpp index b324f71..7e009d5 100644 --- a/include/geode/stochastic/applications/poisson_process.hpp +++ b/include/geode/stochastic/applications/poisson_process.hpp @@ -24,16 +24,17 @@ namespace geode template < typename ObjectType > struct PoissonProcessDescription { + std::string process_name{ "Poisson" }; SpatialDomainConfig< ObjectType::dim > domain; std::vector< PoissonSetDescription< ObjectType > > sets; PoissonSetDescription< ObjectType >& add_set( - absl::string_view set_name, absl::string_view density_name ) + absl::string_view set_name ) { auto& set = sets.emplace_back(); set.set_name = set_name; - set.density_name = density_name; + set.density_name = absl::StrCat( set_name, "_density" ); return set; } }; diff --git a/include/geode/stochastic/applications/strauss_process.hpp b/include/geode/stochastic/applications/strauss_process.hpp index 8d4c6fc..85c4654 100644 --- a/include/geode/stochastic/applications/strauss_process.hpp +++ b/include/geode/stochastic/applications/strauss_process.hpp @@ -32,11 +32,12 @@ namespace geode std::vector< StraussInteractionDescription< ObjectType > > interactions; PoissonSetDescription< ObjectType >& add_set( - absl::string_view set_name, absl::string_view density_name ) + absl::string_view set_name ) { auto& set = sets.emplace_back(); set.set_name = set_name; - set.density_name = density_name; + set.density_name = absl::StrCat( set_name, "_density" ); + return set; } diff --git a/include/geode/stochastic/sampling/direct/double_sampler.hpp b/include/geode/stochastic/sampling/direct/double_sampler.hpp index c79f8ab..2e94317 100644 --- a/include/geode/stochastic/sampling/direct/double_sampler.hpp +++ b/include/geode/stochastic/sampling/direct/double_sampler.hpp @@ -48,7 +48,7 @@ namespace geode struct DistributionDescription { - DistributionDescription( absl::string_view name_in ) + explicit DistributionDescription( absl::string_view name_in ) : name{ name_in } { } @@ -67,7 +67,7 @@ namespace geode std::optional< double > alpha; // exponent parameter for power law distribution - std::string string() const + [[nodiscard]] std::string string() const { auto message = absl::StrCat( "Distribution - ", name ); absl::StrAppend( &message, 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 e4d65d0..2f55f8c 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 @@ -31,7 +31,7 @@ namespace geode struct ObjectSamplerConfig< Point< dimension > > { // use to define the step for change move (move_ratio*domain volume) - // NOLINTBEGING(*-magic-numbers) + // NOLINTBEGIN(*-magic-numbers) double move_ratio{ 0.1 }; // NOLINTEND(*-magic-numbers) }; 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 819de70..7aca356 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 @@ -33,7 +33,7 @@ namespace geode template <> struct ObjectSamplerConfig< OwnerSegment2D > { - // NOLINTBEGING(*-magic-numbers) + // NOLINTBEGIN(*-magic-numbers) double move_ratio{ 0.1 }; // NOLINTEND(*-magic-numbers) diff --git a/include/geode/stochastic/spatial/spatial_domain.hpp b/include/geode/stochastic/spatial/spatial_domain.hpp index 82f147c..d1bccef 100644 --- a/include/geode/stochastic/spatial/spatial_domain.hpp +++ b/include/geode/stochastic/spatial/spatial_domain.hpp @@ -152,7 +152,8 @@ namespace geode Point< dimension > min_point; Point< dimension > max_point; double buffer_size{ 0.0 }; - std::string string() const + + [[nodiscard]] std::string string() const { std::string message = "Domain: "; absl::StrAppend( diff --git a/src/geode/stochastic/applications/fractures.cpp b/src/geode/stochastic/applications/fractures.cpp index 89586f4..9d2c7fb 100644 --- a/src/geode/stochastic/applications/fractures.cpp +++ b/src/geode/stochastic/applications/fractures.cpp @@ -30,10 +30,10 @@ namespace fracture_extremities ) { std::vector< geode::Fracture > fractures; + fractures.reserve( fracture_extremities.size() ); for( const auto& extremities : fracture_extremities ) { - fractures.emplace_back( - geode::OwnerSegment2D{ extremities[0], extremities[1] } ); + fractures.emplace_back( extremities[0], extremities[1] ); } return fractures; } @@ -75,9 +75,10 @@ namespace geode intensity.term_name = fset_desc.intensity_name(); intensity.object_set_names = { fset_desc.fset_name }; intensity.lambda = fset_desc.p21; - constexpr double caracteristic_length = 1.0; + constexpr double CARACTERISTIC_LENGTH = 1.0; // mean fracture + // length? intensity.object_feature = - SegmentLengthInsideBoxFeatureConfig{ caracteristic_length }; + SegmentLengthInsideBoxFeatureConfig{ CARACTERISTIC_LENGTH }; simulation_config.model.terms.emplace_back( std::move( intensity ) ); diff --git a/tests/stochastic/applications/test-fractures.cpp b/tests/stochastic/applications/test-fractures.cpp index a831ce6..b09e725 100644 --- a/tests/stochastic/applications/test-fractures.cpp +++ b/tests/stochastic/applications/test-fractures.cpp @@ -38,9 +38,9 @@ namespace // NOLINTBEGIN(*-magic-numbers) geode::FractureNetworkDescription fnet_desc; fnet_desc.fnet_name = "One_Set_FNet"; - constexpr double domain_buffer{ 10 }; + constexpr double DOMAIN_BUFFER{ 10 }; fnet_desc.domain = { geode::Point2D{ { 0, 0 } }, - geode::Point2D{ { 100.0, 100.0 } }, domain_buffer }; + geode::Point2D{ { 100.0, 100.0 } }, DOMAIN_BUFFER }; // --- Object set auto& fset = fnet_desc.add_fracture_set( "fset_A" ); diff --git a/tests/stochastic/applications/test-poisson-process.cpp b/tests/stochastic/applications/test-poisson-process.cpp index acc8e2a..a04cb7b 100644 --- a/tests/stochastic/applications/test-poisson-process.cpp +++ b/tests/stochastic/applications/test-poisson-process.cpp @@ -47,7 +47,7 @@ namespace geode::PoissonProcessDescription< geode::Point2D > poisson; poisson.domain = { geode::Point2D{ { 0, 0 } }, geode::Point2D{ { 10, 10 } }, 0. }; - auto& set_config = poisson.add_set( "set_A", "density_A" ); + auto& set_config = poisson.add_set( "set_A" ); set_config.lambda = 0.3; set_config.expected_nb_objects = 30; set_config.birth_ratio = birth_ratio[config]; @@ -96,21 +96,21 @@ namespace geode::PoissonProcessDescription< geode::Point2D > poisson; poisson.domain = { geode::Point2D{ { 0, 0 } }, geode::Point2D{ { 10, 10 } }, 0. }; - auto& set_config_01 = poisson.add_set( "set01", "density_01" ); + auto& set_config_01 = poisson.add_set( "set01" ); set_config_01.lambda = 0.1; set_config_01.expected_nb_objects = 10; set_config_01.birth_ratio = 2.0; set_config_01.death_ratio = 3.0; set_config_01.change_ratio = 1.0; - auto& set_config_02 = poisson.add_set( "set02", "density_02" ); + auto& set_config_02 = poisson.add_set( "set02" ); set_config_02.lambda = 0.4; set_config_01.expected_nb_objects = 40; set_config_02.birth_ratio = 3.0; set_config_02.death_ratio = 0.5; set_config_02.change_ratio = 1.0; - auto& set_config_03 = poisson.add_set( "set03", "density_03" ); + auto& set_config_03 = poisson.add_set( "set03" ); set_config_03.lambda = 0.3; set_config_01.expected_nb_objects = 30; set_config_03.birth_ratio = 4.0; diff --git a/tests/stochastic/applications/test-strauss-process.cpp b/tests/stochastic/applications/test-strauss-process.cpp index f2919fd..0ae9bb3 100644 --- a/tests/stochastic/applications/test-strauss-process.cpp +++ b/tests/stochastic/applications/test-strauss-process.cpp @@ -52,7 +52,7 @@ namespace strauss.domain = { geode::Point2D{ { 0.0, 0.0 } }, geode::Point2D{ { 10.0, 10.0 } }, 1. }; - auto& set_config = strauss.add_set( "set_A", "density_A" ); + auto& set_config = strauss.add_set( "set_A" ); set_config.lambda = 0.5; set_config.expected_nb_objects = nb_points[config]; @@ -115,21 +115,21 @@ namespace strauss.domain = { geode::Point2D{ { 0, 0 } }, geode::Point2D{ { 10, 10 } }, 2. }; - auto& set_config_01 = strauss.add_set( "set01", "density_01" ); + auto& set_config_01 = strauss.add_set( "set01" ); set_config_01.lambda = 0.1; set_config_01.expected_nb_objects = 10; set_config_01.birth_ratio = 1.0; set_config_01.death_ratio = 3.0; set_config_01.change_ratio = 1.0; - auto& set_config_02 = strauss.add_set( "set02", "density_02" ); + auto& set_config_02 = strauss.add_set( "set02" ); set_config_02.lambda = 0.4; set_config_01.expected_nb_objects = 40; set_config_02.birth_ratio = 3.0; set_config_02.death_ratio = 0.5; set_config_02.change_ratio = 1.0; - auto& set_config_03 = strauss.add_set( "set03", "density_03" ); + auto& set_config_03 = strauss.add_set( "set03" ); set_config_03.lambda = 0.3; set_config_01.expected_nb_objects = 30; set_config_03.birth_ratio = 4.0; diff --git a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp index 14b382e..ef5ae0f 100644 --- a/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp +++ b/tests/stochastic/sampling/mcmc/test-metropolis-hasting-sampler.cpp @@ -31,6 +31,8 @@ namespace { void test_acceptance_prob_helper() { + // NOLINTBEGIN(*-magic-numbers) + // log_accept >= 0 → prob = 1 geode::OpenGeodeStochasticStochasticException::test( geode::MetropolisHastings< geode::Point2D >::acceptance_prob_helper( @@ -46,24 +48,26 @@ namespace "[MH test] acceptance_prob_helper wrong for extreme negative." ); // moderate negative → exp(log_accept) - double val = + auto val = geode::MetropolisHastings< geode::Point2D >::acceptance_prob_helper( -1.0 ); geode::OpenGeodeStochasticStochasticException::test( - std::fabs( val - std::exp( -1.0 ) ) < 1e-12, + std::fabs( val - std::exp( -1.0 ) ) < geode::GLOBAL_EPSILON, "[MH test] acceptance_prob_helper wrong for -1.0." ); + // NOLINTEND(*-magic-numbers) } - void test_beta_setter( geode::MetropolisHastings< geode::Point2D >& mh ) + void test_beta_setter( geode::MetropolisHastings< geode::Point2D >& mh_eng ) { - mh.set_beta( 0.5 ); + // NOLINTBEGIN(*-magic-numbers) + mh_eng.set_beta( 0.5 ); geode::OpenGeodeStochasticStochasticException::test( - mh.beta() == 0.5, "[MH test] beta not set correctly." ); + mh_eng.beta() == 0.5, "[MH test] beta not set correctly." ); bool exception_thrown = false; try { - mh.set_beta( -1.0 ); + mh_eng.set_beta( -1.0 ); } catch( ... ) { @@ -71,24 +75,26 @@ namespace } geode::OpenGeodeStochasticStochasticException::test( exception_thrown, "[MH test] negative beta did not throw." ); + // NOLINTEND(*-magic-numbers) } - void test_steps( const geode::MetropolisHastings< geode::Point2D >& mh, + void test_steps( const geode::MetropolisHastings< geode::Point2D >& mh_eng, geode::ObjectSets< geode::Point2D >& state ) { + // NOLINTBEGIN(*-magic-numbers) geode::RandomEngine engine; geode::index_t stat_sum{ 0 }; - constexpr geode::index_t N{ 100000 }; + constexpr geode::index_t NUNBER_ITR{ 100000 }; geode::index_t accepted_birth{ 0 }; geode::index_t accepted_death{ 0 }; geode::index_t accepted_change{ 0 }; geode::index_t nb_accepted{ 0 }; - for( const auto count : geode::Range{ N } ) + for( const auto count : geode::Range{ NUNBER_ITR } ) { - auto result = mh.step( state, engine ); + auto result = mh_eng.step( state, engine ); // Invariant: fixed object must remain geode::OpenGeodeStochasticStochasticException::test( @@ -146,6 +152,7 @@ namespace / static_cast< double >( nb_accepted ) ); } } + // NOLINTEND(*-magic-numbers) } } // namespace @@ -155,7 +162,7 @@ int main() try { geode::OpenGeodeStochasticStochasticLibrary::initialize(); - + // NOLINTBEGIN(*-magic-numbers) geode::Point2D min_point{ { 0., 0. } }; geode::Point2D max_point{ { 10., 10. } }; @@ -182,23 +189,24 @@ int main() domain ) ); geode_unused( term_id ); - geode::MetropolisHastings< geode::Point2D > mh( + geode::MetropolisHastings< geode::Point2D > mh_eng( energy_terms, std::move( kernel ) ); state.add_object( geode::Point2D{ { 1., 1. } }, set_id, true ); state.add_object( geode::Point2D{ { 2., 2. } }, set_id, false ); state.add_object( geode::Point2D{ { 3., 3. } }, set_id, true ); - test_steps( mh, state ); + test_steps( mh_eng, state ); // geode::OpenGeodeStochasticStochasticException::test( state.get_set( // set_id ).nb_fixed_objects() == 2 // ); - test_beta_setter( mh ); + test_beta_setter( mh_eng ); test_acceptance_prob_helper(); geode::Logger::info( "MH TEST SUCCESS" ); return 0; + // NOLINTEND(*-magic-numbers) } catch( ... ) { diff --git a/tests/stochastic/sampling/test-random-engine.cpp b/tests/stochastic/sampling/test-random-engine.cpp index eef51b8..aa3ae70 100644 --- a/tests/stochastic/sampling/test-random-engine.cpp +++ b/tests/stochastic/sampling/test-random-engine.cpp @@ -90,7 +90,7 @@ namespace void test_distribution_mean_and_variance( const std::vector< T >& data, double expected_mean, double expected_var, - double k = 5.0 ) + double k_coef = 5.0 ) { // NOLINTBEGING(*-magic-numbers) const auto n = data.size(); @@ -105,10 +105,10 @@ namespace std::sqrt( 2.0 * expected_var * expected_var / ( n - 1 ) ); geode::OpenGeodeStochasticStochasticException::test( - std::fabs( mean - expected_mean ) < k * se_mean, + std::fabs( mean - expected_mean ) < k_coef * se_mean, "[Uniform] - Wrong expected mean." ); geode::OpenGeodeStochasticStochasticException::test( - std::fabs( variance - expected_var ) < k * se_var, + std::fabs( variance - expected_var ) < k_coef * se_var, "[Uniform] - Wrong expected std." ); // NOLINTEND(*-magic-numbers) } @@ -116,17 +116,17 @@ namespace template < typename T > double compute_expected_variance( T min_value, T max_value ) { - // NOLINTBEGING(*-magic-numbers) + // NOLINTBEGIN(*-magic-numbers) if constexpr( std::is_integral_v< T > ) { - const double distance = + const auto 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 distance = + const auto distance = static_cast< double >( max_value - min_value ); return ( distance * distance ) / 12.0; } @@ -257,7 +257,7 @@ int main() random_engine.set_seed( "@-test-radom-engine@" ); - // NOLINTBEGING(*-magic-numbers) + // NOLINTBEGIN(*-magic-numbers) 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 ); From 03b893e0da88c8efc7f5312c9e20a81d703e783f Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 3 Jun 2026 20:11:33 +0200 Subject: [PATCH 58/59] more tidy --- .../sampling/test-random-engine.cpp | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/tests/stochastic/sampling/test-random-engine.cpp b/tests/stochastic/sampling/test-random-engine.cpp index aa3ae70..d002ee5 100644 --- a/tests/stochastic/sampling/test-random-engine.cpp +++ b/tests/stochastic/sampling/test-random-engine.cpp @@ -28,6 +28,7 @@ #include #include +// NOLINTBEGIN(*-magic-numbers) namespace { const int NUMBER_OF_SAMPLES = 100000; @@ -45,8 +46,6 @@ namespace geode::RandomEngine engine2; engine2.set_seed( seed ); - // NOLINTBEGING(*-magic-numbers) - // Define a uniform distribution geode::UniformClosed< int > dist; dist.min_value = 1; @@ -65,7 +64,6 @@ namespace geode::Logger::info( "Test Reproducibility: ", seed, " SUCCESS " ); } geode::Logger::info( "Test Reproducibility: SUCCESS " ); - // NOLINTEND(*-magic-numbers) } template < typename T > @@ -86,23 +84,23 @@ namespace } return accum / ( data.size() - 1 ); } + // NOLINTBEGING(*-magic-numbers) template < typename T > void test_distribution_mean_and_variance( const std::vector< T >& data, double expected_mean, double expected_var, double k_coef = 5.0 ) { - // NOLINTBEGING(*-magic-numbers) - const auto n = data.size(); + const auto data_size = 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_mean = std::sqrt( expected_var / data_size ); const double se_var = - std::sqrt( 2.0 * expected_var * expected_var / ( n - 1 ) ); + std::sqrt( 2.0 * expected_var * expected_var / ( data_size - 1 ) ); geode::OpenGeodeStochasticStochasticException::test( std::fabs( mean - expected_mean ) < k_coef * se_mean, @@ -110,14 +108,11 @@ namespace geode::OpenGeodeStochasticStochasticException::test( std::fabs( variance - expected_var ) < k_coef * se_var, "[Uniform] - Wrong expected std." ); - // NOLINTEND(*-magic-numbers) } template < typename T > double compute_expected_variance( T min_value, T max_value ) { - // NOLINTBEGIN(*-magic-numbers) - if constexpr( std::is_integral_v< T > ) { const auto distance = @@ -135,7 +130,6 @@ namespace static_assert( std::is_arithmetic_v< T >, "Unsupported type to compute theoretical variance." ); } - // NOLINTEND(*-magic-numbers) } template < typename Type > @@ -244,7 +238,6 @@ namespace geode::OpenGeodeStochasticStochasticException::test( abs( empirical_probability - probability_of_success ) < 0.05, "[Bernoulli] - Empirical probability out of tolerance." ); - // NOLINTEND(*-magic-numbers) } } // namespace int main() @@ -257,7 +250,6 @@ int main() random_engine.set_seed( "@-test-radom-engine@" ); - // NOLINTBEGIN(*-magic-numbers) 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 ); @@ -286,7 +278,6 @@ int main() test_bernoulli( 0.06, random_engine ); test_bernoulli( 0.96, random_engine ); geode::Logger::info( "TEST BERNOUILLI SAMPLING - SUCCESS" ); - // NOLINTEND(*-magic-numbers) return 0; } catch( ... ) @@ -294,3 +285,4 @@ int main() return geode::geode_lippincott(); } } +// NOLINTEND(*-magic-numbers) From f558849e3d562fc7bdb4e65b9f632684ac46b376 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Thu, 4 Jun 2026 15:41:02 +0200 Subject: [PATCH 59/59] cleaning --- .../stochastic/applications/fractures.hpp | 25 ++++++++++++++++++- .../applications/poisson_process.hpp | 23 +++++++++++++++++ .../applications/strauss_process.hpp | 23 +++++++++++++++++ .../inference/statistics_tracker.hpp | 24 +++++++++++++++++- .../inference/target_statistics.hpp | 2 ++ .../sampling/direct/direction_sampler.hpp | 0 .../object_set_sampler/point_set_sampler.hpp | 3 +-- .../segment_set_sampler.hpp | 5 +--- .../proposal/object_set_dynamic_config.hpp | 2 +- .../pairwise_interactions_builder.hpp | 2 +- .../pairwise_interactions_config.hpp | 2 +- .../single_object_feature_config.hpp | 2 +- .../sampling/direct/direction_sampler.cpp | 0 src/geode/stochastic/spatial/object_sets.cpp | 22 ++++++++++++++++ 14 files changed, 123 insertions(+), 12 deletions(-) delete mode 100644 include/geode/stochastic/sampling/direct/direction_sampler.hpp delete mode 100644 src/geode/stochastic/sampling/direct/direction_sampler.cpp diff --git a/include/geode/stochastic/applications/fractures.hpp b/include/geode/stochastic/applications/fractures.hpp index 9107a9a..b10a8e2 100644 --- a/include/geode/stochastic/applications/fractures.hpp +++ b/include/geode/stochastic/applications/fractures.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 @@ -12,7 +35,7 @@ namespace geode using FractureSimulationContext = SimulationContext< Fracture >; using FractureSimulationRunner = SimulationRunner< Fracture >; - struct opengeode_stochastic_stochastic_api FractureSetDescription + struct FractureSetDescription { std::string fset_name; diff --git a/include/geode/stochastic/applications/poisson_process.hpp b/include/geode/stochastic/applications/poisson_process.hpp index 7e009d5..acf38ec 100644 --- a/include/geode/stochastic/applications/poisson_process.hpp +++ b/include/geode/stochastic/applications/poisson_process.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 diff --git a/include/geode/stochastic/applications/strauss_process.hpp b/include/geode/stochastic/applications/strauss_process.hpp index 85c4654..50c033a 100644 --- a/include/geode/stochastic/applications/strauss_process.hpp +++ b/include/geode/stochastic/applications/strauss_process.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 diff --git a/include/geode/stochastic/inference/statistics_tracker.hpp b/include/geode/stochastic/inference/statistics_tracker.hpp index 81816f3..be9cb32 100644 --- a/include/geode/stochastic/inference/statistics_tracker.hpp +++ b/include/geode/stochastic/inference/statistics_tracker.hpp @@ -1,6 +1,28 @@ +/* + * 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 diff --git a/include/geode/stochastic/inference/target_statistics.hpp b/include/geode/stochastic/inference/target_statistics.hpp index b962668..703f28d 100644 --- a/include/geode/stochastic/inference/target_statistics.hpp +++ b/include/geode/stochastic/inference/target_statistics.hpp @@ -20,7 +20,9 @@ * SOFTWARE. * */ + #pragma once + #include #include diff --git a/include/geode/stochastic/sampling/direct/direction_sampler.hpp b/include/geode/stochastic/sampling/direct/direction_sampler.hpp deleted file mode 100644 index e69de29..0000000 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 2f55f8c..db7749c 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 @@ -31,9 +31,8 @@ namespace geode struct ObjectSamplerConfig< Point< dimension > > { // use to define the step for change move (move_ratio*domain volume) - // NOLINTBEGIN(*-magic-numbers) + // NOLINTNEXTLINE(*-magic-numbers) double move_ratio{ 0.1 }; - // NOLINTEND(*-magic-numbers) }; template < index_t dimension > 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 7aca356..8d44851 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 @@ -23,8 +23,6 @@ #pragma once -// #include - #include #include @@ -33,9 +31,8 @@ namespace geode template <> struct ObjectSamplerConfig< OwnerSegment2D > { - // NOLINTBEGIN(*-magic-numbers) + // NOLINTNEXTLINE(*-magic-numbers) double move_ratio{ 0.1 }; - // NOLINTEND(*-magic-numbers) DoubleSampler::DistributionDescription length{ "length" }; diff --git a/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp b/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp index 7dea345..7d8edb0 100644 --- a/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp +++ b/include/geode/stochastic/sampling/mcmc/proposal/object_set_dynamic_config.hpp @@ -29,7 +29,7 @@ namespace geode { struct ObjectSetDynamicsConfig { - std::string name; + std::string object_set_name; double birth_ratio = 1.0; double death_ratio = 1.0; 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 4421e0f..72e1de2 100644 --- a/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp +++ b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_builder.hpp @@ -20,13 +20,13 @@ * SOFTWARE. * */ +#pragma once #include #include #include -#pragma once namespace geode { template < typename ObjectType > 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 f3cd615..3c3b628 100644 --- a/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_config.hpp +++ b/include/geode/stochastic/spatial/pairwise_interactions/pairwise_interactions_config.hpp @@ -20,12 +20,12 @@ * SOFTWARE. * */ +#pragma once #include #include -#pragma once namespace geode { struct EuclideanDistanceCutoffConfig 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 index db440d4..8389f23 100644 --- 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 @@ -20,10 +20,10 @@ * SOFTWARE. * */ +#pragma once #include -#pragma once namespace geode { struct ObjectInDomainFeatureConfig diff --git a/src/geode/stochastic/sampling/direct/direction_sampler.cpp b/src/geode/stochastic/sampling/direct/direction_sampler.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/src/geode/stochastic/spatial/object_sets.cpp b/src/geode/stochastic/spatial/object_sets.cpp index 2ba3b58..c471e3b 100644 --- a/src/geode/stochastic/spatial/object_sets.cpp +++ b/src/geode/stochastic/spatial/object_sets.cpp @@ -1,3 +1,25 @@ +/* + * 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