From 575063ae711d4849dba26a30b992f226ff126f7d Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 12 Nov 2025 16:30:32 +0100 Subject: [PATCH 01/10] feat(BindingAPI): exose necessary classes --- bindings/python/src/stochastic/CMakeLists.txt | 8 +- .../python/src/stochastic/random_engine.cpp | 52 ------ .../sampling/direct/double_sampler.hpp | 96 ++++++++++ .../src/stochastic/sampling/distributions.hpp | 16 +- .../helpers/fracture_simulation_runner.hpp | 84 +++++++++ .../mcmc/helpers/simulation_monitor.hpp | 51 ++++++ .../mcmc/helpers/simulation_printer.hpp | 70 ++++++++ .../mcmc/helpers/simulation_runner.hpp | 52 ++++++ .../src/stochastic/sampling/random_engine.hpp | 79 +++++++++ bindings/python/src/stochastic/stochastic.cpp | 24 ++- .../python/tests/stochastic/CMakeLists.txt | 4 +- .../tests/stochastic/test-py-hello-world.py | 34 ---- .../tests/stochastic/test-py-mh-fractures.py | 141 +++++++++++++++ include/geode/stochastic/hello_world.hpp | 31 ---- .../sampling/direct/double_sampler.hpp | 28 +++ .../stochastic/sampling/distributions.hpp | 30 ++++ .../helpers/fracture_simulation_runner.hpp | 166 ++++++++++++++++++ .../mcmc/helpers/simulation_manager.hpp | 0 .../mcmc/helpers/simulation_monitor.hpp | 6 +- .../mcmc/helpers/simulation_printer.hpp | 26 +++ .../mcmc/helpers/simulation_runner.hpp | 81 +++++---- src/geode/stochastic/CMakeLists.txt | 4 +- tests/stochastic/CMakeLists.txt | 10 -- .../sampling/mcmc/test-mh-fractures.cpp | 147 +--------------- .../sampling/mcmc/test-mh-poisson.cpp | 10 +- .../sampling/mcmc/test-mh-strauss.cpp | 8 +- tests/stochastic/test-hello-world.cpp | 44 ----- 27 files changed, 923 insertions(+), 379 deletions(-) delete mode 100644 bindings/python/src/stochastic/random_engine.cpp create mode 100644 bindings/python/src/stochastic/sampling/direct/double_sampler.hpp rename src/geode/stochastic/hello_world.cpp => bindings/python/src/stochastic/sampling/distributions.hpp (73%) create mode 100644 bindings/python/src/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp create mode 100644 bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp create mode 100644 bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_printer.hpp create mode 100644 bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_runner.hpp create mode 100644 bindings/python/src/stochastic/sampling/random_engine.hpp delete mode 100644 bindings/python/tests/stochastic/test-py-hello-world.py create mode 100644 bindings/python/tests/stochastic/test-py-mh-fractures.py delete mode 100644 include/geode/stochastic/hello_world.hpp create mode 100644 include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp delete mode 100644 include/geode/stochastic/sampling/mcmc/helpers/simulation_manager.hpp delete mode 100644 tests/stochastic/test-hello-world.cpp diff --git a/bindings/python/src/stochastic/CMakeLists.txt b/bindings/python/src/stochastic/CMakeLists.txt index 3a7faaa..40b1b02 100644 --- a/bindings/python/src/stochastic/CMakeLists.txt +++ b/bindings/python/src/stochastic/CMakeLists.txt @@ -21,7 +21,13 @@ add_geode_python_binding( NAME "py_stochastic" SOURCES -# "random_engine.cpp" + "sampling/mcmc/helpers/fracture_simulation_runner.hpp" + "sampling/mcmc/helpers/simulation_monitor.hpp" + "sampling/mcmc/helpers/simulation_printer.hpp" + "sampling/mcmc/helpers/simulation_runner.hpp" + "sampling/direct/double_sampler.hpp" + "sampling/random_engine.hpp" + "sampling/distributions.hpp" "stochastic.cpp" DEPENDENCIES ${PROJECT_NAME}::stochastic diff --git a/bindings/python/src/stochastic/random_engine.cpp b/bindings/python/src/stochastic/random_engine.cpp deleted file mode 100644 index b45e8a6..0000000 --- a/bindings/python/src/stochastic/random_engine.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2019 - 2025 Geode-solutions - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -#include - -namespace geode -{ - template < typename type > - type python_sample_uniform( pybind11::class_< RandomEngine >& rand_engine, - const std::string& suffix ) - { - const auto read_suffix = absl::StrCat( "sample_uniform_", suffix ); - manager.def( - read_suffix.c_str(), &RandomEngine::sample_uniform< type > ); - } - - void define_random_engine( pybind11::module& module ) - { - pybind11::class_< RandomEngine > rand_engine( module, "RandomEngine" ); - rand_engine.def( pybind11::init<>() ) - .def( "set_seed", &RandomEngine::set_seed ) - .def( "sample_gaussian", &RandomEngine::sample_gaussian ) - .def( "sample_bernoulli", &AttributeManager::sample_bernoulli ) - .def( "attribute_exists", &AttributeManager::attribute_exists ); - python_sample_uniform< index_t >( rand_engine, "index_t" ); - python_sample_uniform< local_index_t >( rand_engine, "local_index_t" ); - python_sample_uniform< signed_index_t >( - rand_engine, "signed_index_t" ); - python_sample_uniform< float >( rand_engine, "float" ); - python_sample_uniform< double >( rand_engine, "double" ); - } -} // namespace geode diff --git a/bindings/python/src/stochastic/sampling/direct/double_sampler.hpp b/bindings/python/src/stochastic/sampling/direct/double_sampler.hpp new file mode 100644 index 0000000..5eb7cfc --- /dev/null +++ b/bindings/python/src/stochastic/sampling/direct/double_sampler.hpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2019 - 2025 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include + +namespace geode +{ + void define_double_sampler( pybind11::module& module ) + { + // Bind DistributionDescription + pybind11::class_< DoubleSampler::DistributionDescription >( + module, "DoubleDistributionDescription" ) + .def( pybind11::init<>() ) + .def_readwrite( + "name", &DoubleSampler::DistributionDescription::name ) + .def_readwrite( "distribution_type", + &DoubleSampler::DistributionDescription::distribution_type ) + .def_readwrite( "min_value", + &DoubleSampler::DistributionDescription::min_value ) + .def_readwrite( "max_value", + &DoubleSampler::DistributionDescription::max_value ) + .def_readwrite( + "mean", &DoubleSampler::DistributionDescription::mean ) + .def_readwrite( "standard_deviation", + &DoubleSampler::DistributionDescription::standard_deviation ) + .def( "string", &DoubleSampler::DistributionDescription::string, + "Return a detailed description of the Distribution law" ) + .def( "__repr__", + []( const DoubleSampler::DistributionDescription& d ) { + return ""; + } ); + + // Bind Distribution variant + pybind11::class_< DoubleSampler::Distribution >( + module, "Distribution" ); + + // Bind DoubleSampler + pybind11::class_< DoubleSampler >( module, "DoubleSampler" ) + .def_static( "create_distribution", + &DoubleSampler::create_distribution, pybind11::arg( "desc" ), + "Create a distribution from a description" ) + .def_static( "sample", &DoubleSampler::sample, + pybind11::arg( "engine" ), pybind11::arg( "dist" ), + "Sample a value from a distribution using a RandomEngine" ); + + // Optionally, expose the variant types + pybind11::class_< UniformClosed< double > >( + module, "UniformClosedDouble" ) + .def( pybind11::init<>() ) + .def_readwrite( "min_value", &UniformClosed< double >::min_value ) + .def_readwrite( "max_value", &UniformClosed< double >::max_value ); + + pybind11::class_< UniformClosedOpen< double > >( + module, "UniformClosedOpenDouble" ) + .def( pybind11::init<>() ) + .def_readwrite( + "min_value", &UniformClosedOpen< double >::min_value ) + .def_readwrite( + "max_value", &UniformClosedOpen< double >::max_value ); + + pybind11::class_< Gaussian >( module, "Gaussian" ) + .def( pybind11::init<>() ) + .def_readwrite( "mean", &Gaussian::mean ) + .def_readwrite( + "standard_deviation", &Gaussian::standard_deviation ); + + pybind11::class_< TruncatedGaussian >( module, "TruncatedGaussian" ) + .def( pybind11::init<>() ) + .def_readwrite( "mean", &TruncatedGaussian::mean ) + .def_readwrite( + "standard_deviation", &TruncatedGaussian::standard_deviation ) + .def_readwrite( "min_value", &TruncatedGaussian::min_value ) + .def_readwrite( "max_value", &TruncatedGaussian::max_value ); + } +} // namespace geode diff --git a/src/geode/stochastic/hello_world.cpp b/bindings/python/src/stochastic/sampling/distributions.hpp similarity index 73% rename from src/geode/stochastic/hello_world.cpp rename to bindings/python/src/stochastic/sampling/distributions.hpp index 7c97213..ad5ee93 100644 --- a/src/geode/stochastic/hello_world.cpp +++ b/bindings/python/src/stochastic/sampling/distributions.hpp @@ -21,15 +21,19 @@ * */ -#include - -#include +#include namespace geode { - bool hello_world() + + void define_distributions( pybind11::module& module ) { - geode::Logger::info( "Hello Geode Stochastic World!" ); - return true; + // DistributionType + pybind11::class_< DistributionType >( module, "DistributionType" ) + .def( pybind11::init<>() ) + .def( pybind11::init< std::string >() ) + .def( "get", &DistributionType::get ) + .def( "matches", &DistributionType::operator== ); } + } // namespace geode 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 new file mode 100644 index 0000000..9df1c82 --- /dev/null +++ b/bindings/python/src/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2019 - 2025 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include + +namespace geode +{ + void define_fracture_simulation( pybind11::module& module ) + { + using namespace geode; + + pybind11::class_< FractureSetDescription >( + module, "FractureSetDescription" ) + .def( pybind11::init<>() ) + .def_readwrite( "name", &FractureSetDescription::name ) + .def_readwrite( "length", &FractureSetDescription::length ) + .def_readwrite( "azimuth", &FractureSetDescription::azimuth ) + .def_readwrite( "p20", &FractureSetDescription::p20 ) + .def_readwrite( + "minimal_spacing", &FractureSetDescription::minimal_spacing ) + .def_readwrite( + "birth_ratio", &FractureSetDescription::birth_ratio ) + .def_readwrite( + "death_ratio", &FractureSetDescription::death_ratio ) + .def_readwrite( + "change_ratio", &FractureSetDescription::change_ratio ) + .def( "string", &FractureSetDescription::string, + "Return a detailed description of the fracture set" ) + .def( "__repr__", []( const FractureSetDescription& self ) { + return ""; + } ); + + pybind11::class_< FractureSimulationRunner, + std::shared_ptr< FractureSimulationRunner > >( + module, "FractureSimulationRunner" ) + .def( pybind11::init< const BoundingBox2D& >(), + pybind11::arg( "box" ) ) + .def( "add_fracture_set_descriptor", + &FractureSimulationRunner::add_fracture_set_descriptor, + pybind11::arg( "descriptor" ), + "Add a fracture set configuration to the simulation." ) + .def( "initialize", &FractureSimulationRunner::initialize, + "Initialize internal samplers, energy terms, and proposal " + "kernels." ) + .def( "check_statistics", + &FractureSimulationRunner::check_statistics, + pybind11::arg( "statistic_monitoring" ), + "Check computed statistics after simulation." ) + // Explicit overload bindings + .def( "run", + static_cast< const ObjectSets< OwnerSegment2D >& ( + FractureSimulationRunner::*) ( RandomEngine&, index_t ) >( + &FractureSimulationRunner::run ), + pybind11::arg( "engine" ), pybind11::arg( "steps" ), + "Run simulation for a fixed number of steps." ) + .def( "run", + static_cast< StatisticsMonitor ( FractureSimulationRunner::* )( + RandomEngine&, const SimulationConfigurator& ) >( + &FractureSimulationRunner::run ), + pybind11::arg( "engine" ), pybind11::arg( "config" ), + "Run the simulation and return statistics monitoring " + "results." ); + } +} // namespace geode \ No newline at end of file diff --git a/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp b/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp new file mode 100644 index 0000000..b7b701d --- /dev/null +++ b/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019 - 2025 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include + +namespace geode +{ + void define_simulation_monitor( pybind11::module& module ) + { + pybind11::class_< geode::StatisticsMonitor >( + module, "StatisticsMonitor" ) + .def( pybind11::init< geode::index_t >(), + pybind11::arg( "nb_energy_terms" ), + "Create a StatisticsMonitor for a given number of energy " + "terms" ) + .def( "add_realization", &geode::StatisticsMonitor::add_realization, + pybind11::arg( "values" ), + "Add a realization (vector of doubles) to update statistics" ) + .def( "statiscal_count", &geode::StatisticsMonitor::statiscal_count, + "Return the number of realizations added" ) + .def_property_readonly( "means", &geode::StatisticsMonitor::means, + "Return the computed mean values for each energy term" ) + .def_property_readonly( "variances", + &geode::StatisticsMonitor::variances, + "Return the computed variances for each energy term" ) + .def( "__repr__", []( const geode::StatisticsMonitor& self ) { + return ""; + } ); + } +} // namespace geode \ No newline at end of file diff --git a/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_printer.hpp b/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_printer.hpp new file mode 100644 index 0000000..5a57f94 --- /dev/null +++ b/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_printer.hpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019 - 2025 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include + +namespace geode +{ + void define_simulation_printer( pybind11::module& module ) + { + pybind11::class_< SimulationPrinterConfigurator >( + module, "SimulationPrinterConfigurator" ) + .def( pybind11::init<>() ) + .def_readwrite( "print_statistics", + &SimulationPrinterConfigurator::print_statistics ) + .def_readwrite( "statistics_filename", + &SimulationPrinterConfigurator::statistics_filename ) + .def_readwrite( "print_statistics_summary", + &SimulationPrinterConfigurator::print_statistics_summary ) + .def_readwrite( "statistics_summary_filename", + &SimulationPrinterConfigurator::statistics_summary_filename ) + .def_readwrite( "print_realisations", + &SimulationPrinterConfigurator::print_realisations ) + .def_readwrite( "realisations_prefix", + &SimulationPrinterConfigurator::realisations_prefix ) + .def_readwrite( "realisations_print_frequency", + &SimulationPrinterConfigurator::realisations_print_frequency ) + .def_readwrite( + "output_folder", &SimulationPrinterConfigurator::output_folder ) + .def( "__repr__", []( const SimulationPrinterConfigurator& self ) { + return ""; + } ); + + // pybind11::class_< SimulationPrinter >( module, + // "SimulationPrinter" ) + // .def( pybind11::init< const SimulationPrinterConfigurator& + // >(), + // pybind11::arg( "config" ) ) + // .def( "print_statistics", + // &SimulationPrinter::print_statistics, + // pybind11::arg( "stats" ), pybind11::arg( "header" ) = + // "", "Print statistics vector to file." ) + // .def( "print_statistics_summary", + // &SimulationPrinter::print_statistics_summary, + // pybind11::arg( "monitor" ), + // pybind11::arg( "energy_term_names" ) = "", + // "Print statistics summary from a StatisticsMonitor." + // ); + } +} // namespace geode \ No newline at end of file diff --git a/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_runner.hpp b/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_runner.hpp new file mode 100644 index 0000000..cd58757 --- /dev/null +++ b/bindings/python/src/stochastic/sampling/mcmc/helpers/simulation_runner.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 - 2025 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include +#include + +namespace geode +{ + void define_simulation_runner( pybind11::module& module ) + { + pybind11::class_< SimulationConfigurator >( + module, "SimulationConfigurator" ) + .def( pybind11::init<>() ) + .def_readwrite( "realizations", + &SimulationConfigurator::realizations, + "Number of realizations to generate" ) + .def_readwrite( "metropolis_hasting_steps", + &SimulationConfigurator::metropolis_hasting_steps, + "Number of Metropolis-Hastings steps per realization" ) + .def_readwrite( "burn_in_steps", + &SimulationConfigurator::burn_in_steps, + "Number of burn-in steps before recording realizations" ) + .def_readwrite( "printer", &SimulationConfigurator::printer, + "Optional SimulationPrinter for output" ) + .def( "string", &SimulationConfigurator::string, + "Return a detailed description of the simulation configurator" ) + .def( "__repr__", []( const SimulationConfigurator& self ) { + return ""; + } ); + } +} // namespace geode \ No newline at end of file diff --git a/bindings/python/src/stochastic/sampling/random_engine.hpp b/bindings/python/src/stochastic/sampling/random_engine.hpp new file mode 100644 index 0000000..8034f1f --- /dev/null +++ b/bindings/python/src/stochastic/sampling/random_engine.hpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2019 - 2025 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include + +namespace geode +{ + void define_random_engine( pybind11::module& module ) + { + pybind11::class_< RandomEngine >( module, "RandomEngine" ) + .def( pybind11::init<>() ) + // Seed setting + .def( "set_seed", + pybind11::overload_cast< uint64_t >( &RandomEngine::set_seed ), + pybind11::arg( "number" ), "Set RNG seed using integer" ) + .def( "set_seed", + pybind11::overload_cast< std::string_view >( + &RandomEngine::set_seed ), + pybind11::arg( "word" ), "Set RNG seed using string" ) + + // Uniform sampling (double) + .def( + "sample_uniform_closed", + []( RandomEngine& self, const UniformClosed< double >& law ) { + return self.sample_uniform( law ); + }, + pybind11::arg( "law" ), + "Sample a double from a uniform closed distribution" ) + + .def( + "sample_uniform_closed_open", + []( RandomEngine& self, + const UniformClosedOpen< double >& law ) { + return self.sample_uniform( law ); + }, + pybind11::arg( "law" ), + "Sample a double from a uniform closed-open distribution" ) + + // Gaussian + .def( "sample_gaussian", &RandomEngine::sample_gaussian, + pybind11::arg( "law" ), + "Sample a value from a Gaussian distribution" ) + .def( "sample_truncated_gaussian", + &RandomEngine::sample_truncated_gaussian, + pybind11::arg( "law" ), + "Sample a value from a truncated Gaussian" ) + + // Other distributions + .def( "sample_log", &RandomEngine::sample_log, + "Return a logarithmically uniform random value" ) + .def( "sample_bernoulli", &RandomEngine::sample_bernoulli, + pybind11::arg( "probability_of_success" ), + "Sample a boolean with given success probability" ) + + .def( "__repr__", []( const RandomEngine& ) { + return ""; + } ); + } +} // namespace geode diff --git a/bindings/python/src/stochastic/stochastic.cpp b/bindings/python/src/stochastic/stochastic.cpp index 0f3e8ed..fde60c4 100644 --- a/bindings/python/src/stochastic/stochastic.cpp +++ b/bindings/python/src/stochastic/stochastic.cpp @@ -22,15 +22,31 @@ */ #include +#include -#include -#include +#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_printer.hpp" +#include "sampling/mcmc/helpers/simulation_runner.hpp" + +#include "sampling/distributions.hpp" +#include "sampling/random_engine.hpp" PYBIND11_MODULE( opengeode_stochastic_py_stochastic, module ) { module.doc() = "OpenGeode-Stochastic Python binding"; pybind11::class_< geode::StochasticLibrary >( module, "StochasticLibrary" ) .def( "initialize", &geode::StochasticLibrary::initialize ); - module.def( "hello_world", &geode::hello_world ); - // geode::define_random_engine( module ); + + geode::define_double_sampler( module ); + + geode::define_fracture_simulation( module ); + geode::define_simulation_monitor( module ); + geode::define_simulation_printer( module ); + geode::define_simulation_runner( module ); + + geode::define_distributions( module ); + geode::define_random_engine( module ); } \ No newline at end of file diff --git a/bindings/python/tests/stochastic/CMakeLists.txt b/bindings/python/tests/stochastic/CMakeLists.txt index 9fa1c38..f21188a 100644 --- a/bindings/python/tests/stochastic/CMakeLists.txt +++ b/bindings/python/tests/stochastic/CMakeLists.txt @@ -19,7 +19,7 @@ # SOFTWARE. add_geode_python_test( - SOURCE "test-py-hello-world.py" + SOURCE "test-py-mh-fractures.py" DEPENDENCIES ${PROJECT_NAME}::py_stochastic -) +) \ No newline at end of file diff --git a/bindings/python/tests/stochastic/test-py-hello-world.py b/bindings/python/tests/stochastic/test-py-hello-world.py deleted file mode 100644 index 60cec32..0000000 --- a/bindings/python/tests/stochastic/test-py-hello-world.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2019 - 2025 Geode-solutions -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import os -import sys -import platform - -if sys.version_info >= (3, 8, 0) and platform.system() == "Windows": - for path in [x.strip() for x in os.environ["PATH"].split(";") if x]: - os.add_dll_directory(path) - -import opengeode -import opengeode_stochastic_py_stochastic as stochastic - -if __name__ == "__main__": - stochastic.hello_world() diff --git a/bindings/python/tests/stochastic/test-py-mh-fractures.py b/bindings/python/tests/stochastic/test-py-mh-fractures.py new file mode 100644 index 0000000..9c796ad --- /dev/null +++ b/bindings/python/tests/stochastic/test-py-mh-fractures.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019 - 2025 Geode-solutions +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import os +import sys +import platform + +if sys.version_info >= (3, 8, 0) and platform.system() == "Windows": + for path in [x.strip() for x in os.environ["PATH"].split(";") if x]: + os.add_dll_directory(path) + +import opengeode as og +import opengeode_stochastic_py_stochastic as stochastic + +from pathlib import Path + +def test_fracture_simulator(): + print("TEST - MH SINGLE SET FRACTURE SIMULATOR (with intra-set interactions)") + + engine = stochastic.RandomEngine() + engine.set_seed("@mh-test-single-Fracture-set@") + + box = og.BoundingBox2D() + box.add_point(og.Point2D([0.0, 0.0])) + box.add_point(og.Point2D([100.0, 100.0])) + + # --- Object set + setA = stochastic.FractureSetDescription() + setA.name = "A" + + # length + setA.length.distribution_type =stochastic.DistributionType("UniformClosed") + setA.length.min_value = 1.0 + setA.length.max_value = 10.0 + + # azimuth + setA.azimuth.distribution_type =stochastic.DistributionType("UniformClosed") + setA.azimuth.min_value = 1.0 + setA.azimuth.max_value = 10.0 + + # positioning + setA.p20 = 0.1 + setA.minimal_spacing = 1.0 + + runner = stochastic.FractureSimulationRunner(box) + runner.add_fracture_set_descriptor(setA) + runner.initialize() + + # Simulation printer + printer_config = stochastic.SimulationPrinterConfigurator() + printer_config.output_folder = os.path.join(printer_config.output_folder , "single_fracture_set") + + sim_config = stochastic.SimulationConfigurator() + sim_config.realizations = 1000 + sim_config.metropolis_hasting_steps = 1000 + sim_config.burn_in_steps = 1000 + sim_config.printer = printer_config + + # Run simulation + statistic_monitoring = runner.run(engine, sim_config) + runner.check_statistics(statistic_monitoring) + + print("--> SUCCESS!") + + +def test_two_fracture_sets_simulator(): + print("TEST - MH TWO SET FRACTURE SIMULATOR (with intra-set interactions)") + + engine = stochastic.RandomEngine() + engine.set_seed("@mh-test-single-Fracture-set@") + + box = og.BoundingBox2D() + box.add_point(og.Point2D([0.0, 0.0])) + box.add_point(og.Point2D([100.0, 100.0])) + + # --- Object set A + setA = stochastic.FractureSetDescription() + setA.name = "A" + setA.length.distribution_type = stochastic.DistributionType("UniformClosed") + setA.length.min_value = 1.0 + setA.length.max_value = 10.0 + setA.azimuth.distribution_type = stochastic.DistributionType("UniformClosed") + setA.azimuth.min_value = 1.0 + setA.azimuth.max_value = 10.0 + setA.p20 = 0.1 + setA.minimal_spacing = 1.0 + + # --- Object set B + setB = stochastic.FractureSetDescription() + setB.name = "B" + setB.length.distribution_type =stochastic.DistributionType("UniformClosed") + setB.length.min_value = 1.0 + setB.length.max_value = 10.0 + setB.azimuth.distribution_type =stochastic.DistributionType("UniformClosed") + setB.azimuth.min_value = 90.0 + setB.azimuth.max_value = 100.0 + setB.p20 = 0.1 + setB.minimal_spacing = 2.0 + + runner = stochastic.FractureSimulationRunner(box) + runner.add_fracture_set_descriptor(setA) + runner.add_fracture_set_descriptor(setB) + runner.initialize() + + printer_config = stochastic.SimulationPrinterConfigurator() + printer_config.output_folder = os.path.join(printer_config.output_folder ,printer_config.output_folder, "two_fracture_sets") + + sim_config = stochastic.SimulationConfigurator() + sim_config.realizations = 1000 + sim_config.metropolis_hasting_steps = 1000 + sim_config.burn_in_steps = 1000 + sim_config.printer = printer_config + + statistic_monitoring = runner.run(engine, sim_config) + runner.check_statistics(statistic_monitoring) + + print("--> SUCCESS!") + + +if __name__ == "__main__": + + test_fracture_simulator() + test_two_fracture_sets_simulator() diff --git a/include/geode/stochastic/hello_world.hpp b/include/geode/stochastic/hello_world.hpp deleted file mode 100644 index f0df5ea..0000000 --- a/include/geode/stochastic/hello_world.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2019 - 2025 Geode-solutions - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -#pragma once - -#include - -namespace geode -{ - bool opengeode_stochastic_stochastic_api hello_world(); -} // namespace geode diff --git a/include/geode/stochastic/sampling/direct/double_sampler.hpp b/include/geode/stochastic/sampling/direct/double_sampler.hpp index ac962f2..277f9e2 100644 --- a/include/geode/stochastic/sampling/direct/double_sampler.hpp +++ b/include/geode/stochastic/sampling/direct/double_sampler.hpp @@ -52,6 +52,34 @@ namespace geode std::optional< double > max_value; std::optional< double > mean; std::optional< double > standard_deviation; + + std::string string() const + { + auto message = absl::StrCat( "Distribution - ", name ); + absl::StrAppend( &message, + "\n\t - distribution type: ", distribution_type.get() ); + if( min_value.has_value() ) + { + absl::StrAppend( + &message, "\n\t - min value: ", min_value.value() ); + } + if( max_value.has_value() ) + { + absl::StrAppend( + &message, "\n\t - max value: ", max_value.value() ); + } + if( mean.has_value() ) + { + absl::StrAppend( + &message, "\n\t - mean value: ", mean.value() ); + } + if( standard_deviation.has_value() ) + { + absl::StrAppend( &message, + "\n\t - std value: ", standard_deviation.value() ); + } + return message; + } }; static Distribution create_distribution( diff --git a/include/geode/stochastic/sampling/distributions.hpp b/include/geode/stochastic/sampling/distributions.hpp index d100911..5e258f6 100644 --- a/include/geode/stochastic/sampling/distributions.hpp +++ b/include/geode/stochastic/sampling/distributions.hpp @@ -55,6 +55,11 @@ namespace geode { return distribution_type_static(); } + std::string string() const + { + return absl::StrCat( + distribution_type().get(), "[", min_value, max_value, "]" ); + } }; template < typename Type > @@ -75,6 +80,12 @@ namespace geode { return distribution_type_static(); } + + std::string string() const + { + return absl::StrCat( + distribution_type().get(), "[", min_value, max_value, "]" ); + } }; struct opengeode_stochastic_stochastic_api Gaussian @@ -94,6 +105,12 @@ namespace geode { return distribution_type_static(); } + + std::string string() const + { + return absl::StrCat( distribution_type().get(), "(", mean, ",", + standard_deviation, ")" ); + } }; struct opengeode_stochastic_stochastic_api TruncatedGaussian @@ -117,6 +134,19 @@ namespace geode { return distribution_type_static(); } + + std::string string() const + { + std::string min_str = min_value.has_value() + ? std::to_string( min_value.value() ) + : "-inf"; + + std::string max_str = max_value.has_value() + ? std::to_string( max_value.value() ) + : "+inf"; + return absl::StrCat( distribution_type().get(), "(", mean, + standard_deviation, ") in [", min_str, ",", max_str, "]" ); + } }; } // 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 new file mode 100644 index 0000000..20c5848 --- /dev/null +++ b/include/geode/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2019 - 2025 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace geode +{ + struct FractureSetDescription + { + std::string name; + + DoubleSampler::DistributionDescription length; + DoubleSampler::DistributionDescription azimuth; + + // positionning + double p20; + // double p21; + 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 ); + 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 BoundingBox2D& box ) : box_( box ) {} + + 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 > 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; + + auto length_distribution = + DoubleSampler::create_distribution( set_desc.length ); + auto azimuth_distribution = + DoubleSampler::create_distribution( set_desc.azimuth ); + this->set_samplers_.push_back( + std::make_unique< UniformSegmentSetSampler >( + box_, 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_ ) + { + const auto set_id = name_to_uuid.at( set_desc.name ); + // p20 + 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, + absl::flat_hash_set< uuid >{ set_id } ) ) ); + // spacing + if( set_desc.minimal_spacing < GLOBAL_EPSILON ) + { + continue; + } + auto interaction = std::make_unique< + EuclideanCutoffInteraction< 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., + absl::flat_hash_set< uuid >{ set_id }, + std::move( interaction ) ) ) ); + } + + 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().data() ); + } + } + + private: + BoundingBox2D box_; + std::vector< FractureSetDescription > set_descriptors_; + }; + +} // namespace geode \ No newline at end of file diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_manager.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_manager.hpp deleted file mode 100644 index e69de29..0000000 diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp index 57923eb..d8de9cf 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_monitor.hpp @@ -39,15 +39,13 @@ namespace geode StatisticsMonitor( const index_t nb_energy_terms ) { - sum_.resize( nb_energy_terms, 0.0 ); - sum_squares_.resize( nb_energy_terms, 0.0 ); means_.resize( nb_energy_terms, 0.0 ); variances_.resize( nb_energy_terms, 0.0 ); } void add_realization( const std::vector< double >& values ) { - OPENGEODE_EXCEPTION( values.size() == sum_.size(), + OPENGEODE_EXCEPTION( values.size() == means_.size(), "[StatisticsMonitor] - Mismatch between realization size and " "expected number of statistics." ); ++count_; @@ -78,8 +76,6 @@ namespace geode } private: - std::vector< double > sum_; - std::vector< double > sum_squares_; std::vector< double > means_; std::vector< double > variances_; index_t count_{ 0 }; diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_printer.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_printer.hpp index 93dc7fd..dc466eb 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_printer.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_printer.hpp @@ -47,6 +47,32 @@ namespace geode index_t realisations_print_frequency{ 100 }; std::string output_folder{ std::filesystem::current_path().string() }; + + std::string string() const + { + auto message = absl::StrCat( + "SimulationPrinterConfigurator - print to folder: ", + output_folder ); + if( print_statistics ) + { + absl::StrAppend( &message, + "\n\t --> print statistics for each realisation to file: ", + statistics_filename ); + } + if( print_statistics_summary ) + { + absl::StrAppend( &message, + "\n\t --> print summary statistics to file: ", + statistics_summary_filename ); + } + if( print_realisations ) + { + absl::StrAppend( &message, "\n\t --> print pattern every ", + realisations_print_frequency, + " realizations behind the prefix: ", realisations_prefix ); + } + return message; + } }; class SimulationPrinter diff --git a/include/geode/stochastic/sampling/mcmc/helpers/simulation_runner.hpp b/include/geode/stochastic/sampling/mcmc/helpers/simulation_runner.hpp index a46de63..38b0a60 100644 --- a/include/geode/stochastic/sampling/mcmc/helpers/simulation_runner.hpp +++ b/include/geode/stochastic/sampling/mcmc/helpers/simulation_runner.hpp @@ -38,7 +38,24 @@ namespace geode index_t metropolis_hasting_steps{ 1000 }; index_t burn_in_steps{ 1000 }; - std::optional< SimulationPrinter > printer{ std::nullopt }; + std::optional< SimulationPrinterConfigurator > printer{ std::nullopt }; + + std::string string() const + { + auto message = absl::StrCat( "SimulationConfigurator: " ); + absl::StrAppend( &message, "\n\t --> ", realizations, + " metropolis hasting realizations" ); + absl::StrAppend( &message, "\n\t --> ", metropolis_hasting_steps, + " metropolis hasting steps" ); + absl::StrAppend( + &message, "\n\t --> ", burn_in_steps, " burnin steps" ); + if( printer.has_value() ) + { + absl::StrAppend( &message, "\n\t --> Simulation Printer: \n", + printer.value().string() ); + } + return message; + } }; template < typename ObjectType > @@ -57,55 +74,47 @@ namespace geode return object_sets_; } - void run( - RandomEngine& engine, const SimulationConfigurator& configurator ) + StatisticsMonitor run( + RandomEngine& engine, const SimulationConfigurator& config ) { - if( configurator.burn_in_steps > 0 ) + if( config.burn_in_steps > 0 ) { - run( engine, configurator.burn_in_steps ); + mh_sampler_->walk( object_sets_, engine, config.burn_in_steps ); } - for( const auto realization : Range{ configurator.realizations } ) - { - run( engine, configurator.metropolis_hasting_steps ); - if( configurator.printer.has_value() ) - { - configurator.printer->print_statistics( - state_statistics(), model_energy_term_names() ); - configurator.printer->print_object_sets( - object_sets_, realization ); - } - } - } + // Initialize monitoring + StatisticsMonitor stats_monitor( energy_terms_collection_.size() ); + std::unique_ptr< SimulationPrinter > printer; - StatisticsMonitor run_and_monitor( - RandomEngine& engine, const SimulationConfigurator& configurator ) - { - if( configurator.burn_in_steps > 0 ) + if( config.printer.has_value() ) { - run( engine, configurator.burn_in_steps ); + printer = std::make_unique< SimulationPrinter >( + config.printer.value() ); } - StatisticsMonitor stat_monitoring( - energy_terms_collection_.size() ); - for( const auto realization : Range{ configurator.realizations } ) + + for( const auto realization : Range{ config.realizations } ) { - run( engine, configurator.metropolis_hasting_steps ); + mh_sampler_->walk( + object_sets_, engine, config.metropolis_hasting_steps ); + const auto stats = state_statistics(); - stat_monitoring.add_realization( stats ); - if( configurator.printer.has_value() ) + stats_monitor.add_realization( stats ); + + if( printer ) { - configurator.printer->print_statistics( - state_statistics(), model_energy_term_names() ); - configurator.printer->print_object_sets( - object_sets_, realization ); + printer->print_statistics( + stats, model_energy_term_names() ); + printer->print_object_sets( object_sets_, realization ); } } - if( configurator.printer.has_value() ) + + if( printer ) { - configurator.printer->print_statistics_summary( - stat_monitoring, model_energy_term_names() ); + printer->print_statistics_summary( + stats_monitor, model_energy_term_names() ); } - return stat_monitoring; + + return stats_monitor; } const ObjectSets< ObjectType >& state_realization() const diff --git a/src/geode/stochastic/CMakeLists.txt b/src/geode/stochastic/CMakeLists.txt index ebd5d89..cebfadc 100644 --- a/src/geode/stochastic/CMakeLists.txt +++ b/src/geode/stochastic/CMakeLists.txt @@ -34,7 +34,6 @@ add_geode_library( "sampling/distributions.cpp" "sampling/random_engine.cpp" "common.cpp" - "hello_world.cpp" PUBLIC_HEADERS "spatial/object_set.hpp" "spatial/object_sets.hpp" @@ -50,8 +49,8 @@ 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/simulation_runner.hpp" - "sampling/mcmc/helpers/simulation_manager.hpp" "sampling/mcmc/helpers/simulation_printer.hpp" "sampling/mcmc/helpers/simulation_monitor.hpp" "sampling/mcmc/models/components/pairwise_term.hpp" @@ -66,7 +65,6 @@ add_geode_library( "sampling/distributions.hpp" "sampling/random_engine.hpp" "common.hpp" - "hello_world.hpp" PRIVATE_DEPENDENCIES OpenGeode::basic OpenGeode::geometry diff --git a/tests/stochastic/CMakeLists.txt b/tests/stochastic/CMakeLists.txt index fbe5d60..0e8624b 100644 --- a/tests/stochastic/CMakeLists.txt +++ b/tests/stochastic/CMakeLists.txt @@ -168,13 +168,3 @@ add_geode_test( OpenGeode::basic ${PROJECT_NAME}::stochastic ) - -add_geode_test( - SOURCE "test-hello-world.cpp" - DEPENDENCIES - OpenGeode::basic - ${PROJECT_NAME}::stochastic -) - - - diff --git a/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp b/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp index 2eccec7..ae70366 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-fractures.cpp @@ -20,136 +20,11 @@ * SOFTWARE. * */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include + +#include namespace { - struct FractureSetDescription - { - std::string name; - - geode::DoubleSampler::DistributionDescription length; - geode::DoubleSampler::DistributionDescription azimuth; - - // positionning - double p20; - // double p21; - double minimal_spacing; - - // mh dynamique - double birth_ratio{ 1.0 }; - double death_ratio{ 1.0 }; - double change_ratio{ 1.0 }; - }; - - class FractureSimulationRunner - : public geode::SimulationRunner< geode::OwnerSegment2D > - { - public: - FractureSimulationRunner( const geode::BoundingBox2D& box ) - : box_( box ) - { - } - - void add_fracture_set_descriptor( - const FractureSetDescription& descriptor ) - { - set_descriptors_.push_back( descriptor ); - } - - void initialize() override - { - auto proposal_kernel = std::make_unique< - geode::ProposalKernel< geode::OwnerSegment2D > >(); - - // 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; - - auto length_distribution = - geode::DoubleSampler::create_distribution( - set_desc.length ); - auto azimuth_distribution = - geode::DoubleSampler::create_distribution( - set_desc.azimuth ); - this->set_samplers_.push_back( - std::make_unique< geode::UniformSegmentSetSampler >( - box_, length_distribution, azimuth_distribution ) ); - - 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 ); - } - - // Step 2: create density energy terms - for( const auto& set_desc : set_descriptors_ ) - { - const auto set_id = name_to_uuid.at( set_desc.name ); - // p20 - this->ordered_energy_terms_.push_back( - this->energy_terms_collection_.add_energy_term( - std::make_unique< - geode::DensityTerm< geode::OwnerSegment2D > >( - absl::StrCat( set_desc.name, "_density" ), - set_desc.p20, - absl::flat_hash_set< geode::uuid >{ set_id } ) ) ); - // spacing - auto interaction = - std::make_unique< geode::EuclideanCutoffInteraction< - geode::OwnerSegment2D > >( set_desc.minimal_spacing, - geode::PairwiseInteraction< - geode::OwnerSegment2D >::SCOPE::same_set ); - - this->ordered_energy_terms_.push_back( - this->energy_terms_collection_.add_energy_term( - std::make_unique< - geode::PairwiseTerm< geode::OwnerSegment2D > >( - absl::StrCat( set_desc.name, "_min_spacing" ), 0., - absl::flat_hash_set< geode::uuid >{ set_id }, - std::move( interaction ) ) ) ); - } - - this->mh_sampler_ = std::make_unique< - geode::MetropolisHastings< geode::OwnerSegment2D > >( - this->energy_terms_collection_, std::move( proposal_kernel ) ); - } - - void check_statistics( - const geode::StatisticsMonitor& statistic_monitoring ) const - { - const auto& computed_means = statistic_monitoring.means(); - - for( const auto stat_id : - geode::Range{ this->energy_terms_collection_.size() } ) - { - const auto& term = energy_terms_collection_.get( - ordered_energy_terms_[stat_id] ); - geode::Logger::info( "[MH test] Statistic value ", - computed_means[stat_id], - " for energy term: ", term.name().data() ); - } - } - - private: - geode::BoundingBox2D box_; - std::vector< FractureSetDescription > set_descriptors_; - }; - void test_fracture_simulator() { geode::Logger::info( "TEST - MH SINGLE SET FRACTURE SIMULATOR (with " @@ -163,7 +38,7 @@ namespace box.add_point( geode::Point2D{ { 100.0, 100.0 } } ); // --- Object set - FractureSetDescription setA; + geode::FractureSetDescription setA; setA.name = "A"; // length @@ -182,7 +57,7 @@ namespace setA.p20 = 0.1; setA.minimal_spacing = 1.; - FractureSimulationRunner runner( box ); + geode::FractureSimulationRunner runner( box ); runner.add_fracture_set_descriptor( setA ); runner.initialize(); @@ -198,9 +73,7 @@ namespace sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; - // runner.run( engine, sim_config ); - auto statistic_monitoring = - runner.run_and_monitor( engine, sim_config ); + auto statistic_monitoring = runner.run( engine, sim_config ); runner.check_statistics( statistic_monitoring ); geode::Logger::info( "--> SUCCESS!" ); @@ -219,7 +92,7 @@ namespace box.add_point( geode::Point2D{ { 100.0, 100.0 } } ); // --- Object set - FractureSetDescription setA; + geode::FractureSetDescription setA; setA.name = "A"; // length @@ -239,7 +112,7 @@ namespace setA.minimal_spacing = 1.; // --- Object set - FractureSetDescription setB; + geode::FractureSetDescription setB; setB.name = "B"; // length @@ -258,7 +131,7 @@ namespace setB.p20 = 0.1; setB.minimal_spacing = 2.; - FractureSimulationRunner runner( box ); + geode::FractureSimulationRunner runner( box ); runner.add_fracture_set_descriptor( setA ); runner.add_fracture_set_descriptor( setB ); @@ -275,9 +148,7 @@ namespace sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; - // runner.run( engine, sim_config ); - auto statistic_monitoring = - runner.run_and_monitor( engine, sim_config ); + auto statistic_monitoring = runner.run( engine, sim_config ); runner.check_statistics( statistic_monitoring ); geode::Logger::info( "--> SUCCESS!" ); diff --git a/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp b/tests/stochastic/sampling/mcmc/test-mh-poisson.cpp index 97caaf5..864f79c 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::BoundingBox2D& box ) - : box_( box ) {}; + : box_( box ){}; void add_set_descriptor( const SetDescription& descriptor ) { @@ -199,9 +199,7 @@ namespace sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; - // runner.run( engine, sim_config ); - auto statistic_monitoring = - runner.run_and_monitor( engine, sim_config ); + auto statistic_monitoring = runner.run( engine, sim_config ); runner.check_statistics( statistic_monitoring ); } @@ -251,9 +249,7 @@ namespace sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; - // runner.run( engine, sim_config ); - auto statistic_monitoring = - runner.run_and_monitor( engine, sim_config ); + auto statistic_monitoring = runner.run( engine, sim_config ); runner.check_statistics( statistic_monitoring ); geode::Logger::info( "--> SUCCESS!" ); diff --git a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp index 13242c2..c097d8e 100644 --- a/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp +++ b/tests/stochastic/sampling/mcmc/test-mh-strauss.cpp @@ -250,9 +250,7 @@ namespace sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; - // runner.run( engine, sim_config ); - auto statistic_monitoring = - runner.run_and_monitor( engine, sim_config ); + auto statistic_monitoring = runner.run( engine, sim_config ); runner.check_statistics( statistic_monitoring ); } @@ -324,9 +322,7 @@ namespace sim_config.burn_in_steps = 1000; sim_config.printer = printer_config; - // runner.run( engine, sim_config ); - auto statistic_monitoring = - runner.run_and_monitor( engine, sim_config ); + auto statistic_monitoring = runner.run( engine, sim_config ); runner.check_statistics( statistic_monitoring ); } diff --git a/tests/stochastic/test-hello-world.cpp b/tests/stochastic/test-hello-world.cpp deleted file mode 100644 index f4c248b..0000000 --- a/tests/stochastic/test-hello-world.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2019 - 2025 Geode-solutions - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -#include -#include - -#include - -int main() -{ - try - { - geode::StochasticLibrary::initialize(); - OPENGEODE_EXCEPTION( geode::hello_world(), - "[Test] Hello Stochastic World is not correct" ); - - geode::Logger::info( "TEST SUCCESS" ); - return 0; - } - catch( ... ) - { - return geode::geode_lippincott(); - } -} From a731ce7a921342042c260b4f290cb8ba1c2e3e64 Mon Sep 17 00:00:00 2001 From: francoisbonneau <24669995+francoisbonneau@users.noreply.github.com> Date: Wed, 12 Nov 2025 15:31:39 +0000 Subject: [PATCH 02/10] 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 864f79c..d15e6d0 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::BoundingBox2D& box ) - : box_( box ){}; + : box_( box ) {}; void add_set_descriptor( const SetDescription& descriptor ) { From 7be9d5fde27415fa9c9ebd6513afda8f194f3610 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 12 Nov 2025 20:32:32 +0100 Subject: [PATCH 03/10] stub --- bindings/python/src/stochastic/stochastic.cpp | 7 +++---- bindings/python/tests/stochastic/test-py-mh-fractures.py | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/bindings/python/src/stochastic/stochastic.cpp b/bindings/python/src/stochastic/stochastic.cpp index fde60c4..88434a6 100644 --- a/bindings/python/src/stochastic/stochastic.cpp +++ b/bindings/python/src/stochastic/stochastic.cpp @@ -40,13 +40,12 @@ PYBIND11_MODULE( opengeode_stochastic_py_stochastic, module ) pybind11::class_< geode::StochasticLibrary >( module, "StochasticLibrary" ) .def( "initialize", &geode::StochasticLibrary::initialize ); + geode::define_random_engine( module ); + geode::define_distributions( module ); geode::define_double_sampler( module ); - geode::define_fracture_simulation( module ); geode::define_simulation_monitor( module ); geode::define_simulation_printer( module ); geode::define_simulation_runner( module ); - - geode::define_distributions( module ); - geode::define_random_engine( module ); + geode::define_fracture_simulation( module ); } \ No newline at end of file diff --git a/bindings/python/tests/stochastic/test-py-mh-fractures.py b/bindings/python/tests/stochastic/test-py-mh-fractures.py index 9c796ad..7f14fa7 100644 --- a/bindings/python/tests/stochastic/test-py-mh-fractures.py +++ b/bindings/python/tests/stochastic/test-py-mh-fractures.py @@ -66,7 +66,7 @@ def test_fracture_simulator(): # Simulation printer printer_config = stochastic.SimulationPrinterConfigurator() - printer_config.output_folder = os.path.join(printer_config.output_folder , "single_fracture_set") + printer_config.output_folder = os.path.join(printer_config.output_folder , "py_single_fracture_set") sim_config = stochastic.SimulationConfigurator() sim_config.realizations = 1000 @@ -121,7 +121,7 @@ def test_two_fracture_sets_simulator(): runner.initialize() printer_config = stochastic.SimulationPrinterConfigurator() - printer_config.output_folder = os.path.join(printer_config.output_folder ,printer_config.output_folder, "two_fracture_sets") + printer_config.output_folder = os.path.join(printer_config.output_folder ,printer_config.output_folder, "py_two_fracture_sets") sim_config = stochastic.SimulationConfigurator() sim_config.realizations = 1000 From 7a73f32081668ad6d2db50a031f168e2b6579b47 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 12 Nov 2025 20:38:12 +0100 Subject: [PATCH 04/10] stub2 --- bindings/python/src/stochastic/stochastic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/src/stochastic/stochastic.cpp b/bindings/python/src/stochastic/stochastic.cpp index 88434a6..7e8930a 100644 --- a/bindings/python/src/stochastic/stochastic.cpp +++ b/bindings/python/src/stochastic/stochastic.cpp @@ -40,8 +40,8 @@ PYBIND11_MODULE( opengeode_stochastic_py_stochastic, module ) pybind11::class_< geode::StochasticLibrary >( module, "StochasticLibrary" ) .def( "initialize", &geode::StochasticLibrary::initialize ); - geode::define_random_engine( module ); geode::define_distributions( module ); + geode::define_random_engine( module ); geode::define_double_sampler( module ); geode::define_simulation_monitor( module ); From 67eca25bb85d66e4e7b3fea972798d0d8accbfec Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 12 Nov 2025 20:54:37 +0100 Subject: [PATCH 05/10] cmake --- bindings/python/src/stochastic/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bindings/python/src/stochastic/CMakeLists.txt b/bindings/python/src/stochastic/CMakeLists.txt index 40b1b02..842ca0a 100644 --- a/bindings/python/src/stochastic/CMakeLists.txt +++ b/bindings/python/src/stochastic/CMakeLists.txt @@ -31,4 +31,7 @@ add_geode_python_binding( "stochastic.cpp" DEPENDENCIES ${PROJECT_NAME}::stochastic + OpenGeode::basic + OpenGeode::geometry + OpenGeode::model ) From 186637d445783334ca3d4d3663410137c30d2653 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 12 Nov 2025 21:22:19 +0100 Subject: [PATCH 06/10] distrib --- .../src/stochastic/sampling/distributions.hpp | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/bindings/python/src/stochastic/sampling/distributions.hpp b/bindings/python/src/stochastic/sampling/distributions.hpp index ad5ee93..a56c5c1 100644 --- a/bindings/python/src/stochastic/sampling/distributions.hpp +++ b/bindings/python/src/stochastic/sampling/distributions.hpp @@ -34,6 +34,62 @@ namespace geode .def( pybind11::init< std::string >() ) .def( "get", &DistributionType::get ) .def( "matches", &DistributionType::operator== ); + + // UniformClosed + pybind11::class_< geode::UniformClosed< double > >( m, "UniformClosed" ) + .def( pybind11::init<>() ) + .def_readwrite( + "min_value", &geode::UniformClosed< double >::min_value ) + .def_readwrite( + "max_value", &geode::UniformClosed< double >::max_value ) + .def( "is_valid", &geode::UniformClosed< double >::is_valid ) + .def( "distribution_type_static", + &geode::UniformClosed< double >::distribution_type_static ) + .def( "distribution_type", + &geode::UniformClosed< double >::distribution_type ) + .def( "string", &geode::UniformClosed< double >::string ); + + // UniformClosedOpen + pybind11::class_< geode::UniformClosedOpen< double > >( + m, "UniformClosedOpen" ) + .def( pybind11::init<>() ) + .def_readwrite( + "min_value", &geode::UniformClosedOpen< double >::min_value ) + .def_readwrite( + "max_value", &geode::UniformClosedOpen< double >::max_value ) + .def( "is_valid", &geode::UniformClosedOpen< double >::is_valid ) + .def( "distribution_type_static", + &geode::UniformClosedOpen< double >::distribution_type_static ) + .def( "distribution_type", + &geode::UniformClosedOpen< double >::distribution_type ) + .def( "string", &geode::UniformClosedOpen< double >::string ); + + // Gaussian + pybind11::class_< geode::Gaussian >( m, "Gaussian" ) + .def( pybind11::init<>() ) + .def_readwrite( "mean", &geode::Gaussian::mean ) + .def_readwrite( + "standard_deviation", &geode::Gaussian::standard_deviation ) + .def( "is_valid", &geode::Gaussian::is_valid ) + .def( "distribution_type_static", + &geode::Gaussian::distribution_type_static ) + .def( "distribution_type", &geode::Gaussian::distribution_type ) + .def( "string", &geode::Gaussian::string ); + + // TruncatedGaussian + pybind11::class_< geode::TruncatedGaussian >( m, "TruncatedGaussian" ) + .def( pybind11::init<>() ) + .def_readwrite( "mean", &geode::TruncatedGaussian::mean ) + .def_readwrite( "standard_deviation", + &geode::TruncatedGaussian::standard_deviation ) + .def_readwrite( "min_value", &geode::TruncatedGaussian::min_value ) + .def_readwrite( "max_value", &geode::TruncatedGaussian::max_value ) + .def( "is_valid", &geode::TruncatedGaussian::is_valid ) + .def( "distribution_type_static", + &geode::TruncatedGaussian::distribution_type_static ) + .def( "distribution_type", + &geode::TruncatedGaussian::distribution_type ) + .def( "string", &geode::TruncatedGaussian::string ); } } // namespace geode From 0afa6b80ca358ab4a32c64fe3342ab13d97e8b8c Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 12 Nov 2025 21:57:29 +0100 Subject: [PATCH 07/10] module --- .../src/stochastic/sampling/distributions.hpp | 67 +++++++++---------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/bindings/python/src/stochastic/sampling/distributions.hpp b/bindings/python/src/stochastic/sampling/distributions.hpp index a56c5c1..a749bc4 100644 --- a/bindings/python/src/stochastic/sampling/distributions.hpp +++ b/bindings/python/src/stochastic/sampling/distributions.hpp @@ -36,60 +36,57 @@ namespace geode .def( "matches", &DistributionType::operator== ); // UniformClosed - pybind11::class_< geode::UniformClosed< double > >( m, "UniformClosed" ) + pybind11::class_< UniformClosed< double > >( module, "UniformClosed" ) .def( pybind11::init<>() ) - .def_readwrite( - "min_value", &geode::UniformClosed< double >::min_value ) - .def_readwrite( - "max_value", &geode::UniformClosed< double >::max_value ) - .def( "is_valid", &geode::UniformClosed< double >::is_valid ) + .def_readwrite( "min_value", &UniformClosed< double >::min_value ) + .def_readwrite( "max_value", &UniformClosed< double >::max_value ) + .def( "is_valid", &UniformClosed< double >::is_valid ) .def( "distribution_type_static", - &geode::UniformClosed< double >::distribution_type_static ) + &UniformClosed< double >::distribution_type_static ) .def( "distribution_type", - &geode::UniformClosed< double >::distribution_type ) - .def( "string", &geode::UniformClosed< double >::string ); + &UniformClosed< double >::distribution_type ) + .def( "string", &UniformClosed< double >::string ); // UniformClosedOpen - pybind11::class_< geode::UniformClosedOpen< double > >( - m, "UniformClosedOpen" ) + pybind11::class_< UniformClosedOpen< double > >( + module, "UniformClosedOpen" ) .def( pybind11::init<>() ) .def_readwrite( - "min_value", &geode::UniformClosedOpen< double >::min_value ) + "min_value", &UniformClosedOpen< double >::min_value ) .def_readwrite( - "max_value", &geode::UniformClosedOpen< double >::max_value ) - .def( "is_valid", &geode::UniformClosedOpen< double >::is_valid ) + "max_value", &UniformClosedOpen< double >::max_value ) + .def( "is_valid", &UniformClosedOpen< double >::is_valid ) .def( "distribution_type_static", - &geode::UniformClosedOpen< double >::distribution_type_static ) + &UniformClosedOpen< double >::distribution_type_static ) .def( "distribution_type", - &geode::UniformClosedOpen< double >::distribution_type ) - .def( "string", &geode::UniformClosedOpen< double >::string ); + &UniformClosedOpen< double >::distribution_type ) + .def( "string", &UniformClosedOpen< double >::string ); // Gaussian - pybind11::class_< geode::Gaussian >( m, "Gaussian" ) + pybind11::class_< Gaussian >( module, "Gaussian" ) .def( pybind11::init<>() ) - .def_readwrite( "mean", &geode::Gaussian::mean ) + .def_readwrite( "mean", &Gaussian::mean ) .def_readwrite( - "standard_deviation", &geode::Gaussian::standard_deviation ) - .def( "is_valid", &geode::Gaussian::is_valid ) + "standard_deviation", &Gaussian::standard_deviation ) + .def( "is_valid", &Gaussian::is_valid ) .def( "distribution_type_static", - &geode::Gaussian::distribution_type_static ) - .def( "distribution_type", &geode::Gaussian::distribution_type ) - .def( "string", &geode::Gaussian::string ); + &Gaussian::distribution_type_static ) + .def( "distribution_type", &Gaussian::distribution_type ) + .def( "string", &Gaussian::string ); // TruncatedGaussian - pybind11::class_< geode::TruncatedGaussian >( m, "TruncatedGaussian" ) + pybind11::class_< TruncatedGaussian >( module, "TruncatedGaussian" ) .def( pybind11::init<>() ) - .def_readwrite( "mean", &geode::TruncatedGaussian::mean ) - .def_readwrite( "standard_deviation", - &geode::TruncatedGaussian::standard_deviation ) - .def_readwrite( "min_value", &geode::TruncatedGaussian::min_value ) - .def_readwrite( "max_value", &geode::TruncatedGaussian::max_value ) - .def( "is_valid", &geode::TruncatedGaussian::is_valid ) + .def_readwrite( "mean", &TruncatedGaussian::mean ) + .def_readwrite( + "standard_deviation", &TruncatedGaussian::standard_deviation ) + .def_readwrite( "min_value", &TruncatedGaussian::min_value ) + .def_readwrite( "max_value", &TruncatedGaussian::max_value ) + .def( "is_valid", &TruncatedGaussian::is_valid ) .def( "distribution_type_static", - &geode::TruncatedGaussian::distribution_type_static ) - .def( "distribution_type", - &geode::TruncatedGaussian::distribution_type ) - .def( "string", &geode::TruncatedGaussian::string ); + &TruncatedGaussian::distribution_type_static ) + .def( "distribution_type", &TruncatedGaussian::distribution_type ) + .def( "string", &TruncatedGaussian::string ); } } // namespace geode From 206b9c86442bb2a0b7d918f951fc7e9a3fb14a14 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 12 Nov 2025 22:11:36 +0100 Subject: [PATCH 08/10] fix --- .../sampling/direct/double_sampler.hpp | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/bindings/python/src/stochastic/sampling/direct/double_sampler.hpp b/bindings/python/src/stochastic/sampling/direct/double_sampler.hpp index 5eb7cfc..f23e7a0 100644 --- a/bindings/python/src/stochastic/sampling/direct/double_sampler.hpp +++ b/bindings/python/src/stochastic/sampling/direct/double_sampler.hpp @@ -63,34 +63,5 @@ namespace geode .def_static( "sample", &DoubleSampler::sample, pybind11::arg( "engine" ), pybind11::arg( "dist" ), "Sample a value from a distribution using a RandomEngine" ); - - // Optionally, expose the variant types - pybind11::class_< UniformClosed< double > >( - module, "UniformClosedDouble" ) - .def( pybind11::init<>() ) - .def_readwrite( "min_value", &UniformClosed< double >::min_value ) - .def_readwrite( "max_value", &UniformClosed< double >::max_value ); - - pybind11::class_< UniformClosedOpen< double > >( - module, "UniformClosedOpenDouble" ) - .def( pybind11::init<>() ) - .def_readwrite( - "min_value", &UniformClosedOpen< double >::min_value ) - .def_readwrite( - "max_value", &UniformClosedOpen< double >::max_value ); - - pybind11::class_< Gaussian >( module, "Gaussian" ) - .def( pybind11::init<>() ) - .def_readwrite( "mean", &Gaussian::mean ) - .def_readwrite( - "standard_deviation", &Gaussian::standard_deviation ); - - pybind11::class_< TruncatedGaussian >( module, "TruncatedGaussian" ) - .def( pybind11::init<>() ) - .def_readwrite( "mean", &TruncatedGaussian::mean ) - .def_readwrite( - "standard_deviation", &TruncatedGaussian::standard_deviation ) - .def_readwrite( "min_value", &TruncatedGaussian::min_value ) - .def_readwrite( "max_value", &TruncatedGaussian::max_value ); } } // namespace geode From e4301ac780f9c19b0ff095e8555c0889d930f941 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 12 Nov 2025 22:19:22 +0100 Subject: [PATCH 09/10] tmp --- .../mcmc/helpers/fracture_simulation_runner.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bindings/python/src/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp b/bindings/python/src/stochastic/sampling/mcmc/helpers/fracture_simulation_runner.hpp index 9df1c82..e6e4fb9 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 @@ -67,12 +67,12 @@ namespace geode pybind11::arg( "statistic_monitoring" ), "Check computed statistics after simulation." ) // Explicit overload bindings - .def( "run", - static_cast< const ObjectSets< OwnerSegment2D >& ( - FractureSimulationRunner::*) ( RandomEngine&, index_t ) >( - &FractureSimulationRunner::run ), - pybind11::arg( "engine" ), pybind11::arg( "steps" ), - "Run simulation for a fixed number of steps." ) + // .def( "run", + // static_cast< const ObjectSets< OwnerSegment2D >& ( + // FractureSimulationRunner::*) ( RandomEngine&, + // index_t ) >( &FractureSimulationRunner::run ), + // pybind11::arg( "engine" ), pybind11::arg( "steps" + // ), "Run simulation for a fixed number of steps." ) .def( "run", static_cast< StatisticsMonitor ( FractureSimulationRunner::* )( RandomEngine&, const SimulationConfigurator& ) >( From 3d950411f7804828230180c0923a93dd6849c30f Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Wed, 12 Nov 2025 22:37:01 +0100 Subject: [PATCH 10/10] fix windows --- .../stochastic/sampling/distributions.hpp | 31 +++++- .../stochastic/sampling/distributions.cpp | 102 +++++++++--------- 2 files changed, 81 insertions(+), 52 deletions(-) diff --git a/include/geode/stochastic/sampling/distributions.hpp b/include/geode/stochastic/sampling/distributions.hpp index 5e258f6..1c4a600 100644 --- a/include/geode/stochastic/sampling/distributions.hpp +++ b/include/geode/stochastic/sampling/distributions.hpp @@ -41,7 +41,24 @@ namespace geode struct UniformClosed { UniformClosed() = default; - bool is_valid() const; + bool is_valid() const + { + if( min_value < max_value ) + { + return true; + } + if( min_value == max_value ) + { + geode::Logger::warn( + "[Uniform Closed] - check range boundaries definintion [", + min_value, ",", max_value, "]." ); + return true; + } + geode::Logger::error( + "[Uniform Closed] - check range boundaries definintion [", + min_value, ",", max_value, "]." ); + return false; + } Type min_value{ static_cast< Type >( 0 ) }; Type max_value{ static_cast< Type >( 1 ) }; @@ -66,7 +83,17 @@ namespace geode struct UniformClosedOpen { UniformClosedOpen() = default; - bool is_valid() const; + bool is_valid() const + { + if( min_value < max_value ) + { + return true; + } + geode::Logger::error( + "[Uniform ClosedOpen] - check range boundaries definintion [", + min_value, ",", max_value, "]." ); + return false; + } Type min_value{ static_cast< Type >( 0 ) }; Type max_value{ static_cast< Type >( 1 ) }; diff --git a/src/geode/stochastic/sampling/distributions.cpp b/src/geode/stochastic/sampling/distributions.cpp index bef1f9f..57f103c 100644 --- a/src/geode/stochastic/sampling/distributions.cpp +++ b/src/geode/stochastic/sampling/distributions.cpp @@ -28,56 +28,58 @@ namespace geode { - template < typename Type > - bool UniformClosed< Type >::is_valid() const - { - if( min_value < max_value ) - { - return true; - } - if( min_value == max_value ) - { - geode::Logger::warn( - "[Uniform Closed] - check range boundaries definintion [", - min_value, ",", max_value, "]." ); - return true; - } - geode::Logger::error( - "[Uniform Closed] - check range boundaries definintion [", - min_value, ",", max_value, "]." ); - return false; - } - template opengeode_stochastic_stochastic_api struct UniformClosed< - index_t >; - template opengeode_stochastic_stochastic_api struct UniformClosed< - local_index_t >; - template opengeode_stochastic_stochastic_api struct UniformClosed< - signed_index_t >; - template opengeode_stochastic_stochastic_api struct UniformClosed< float >; - template opengeode_stochastic_stochastic_api struct UniformClosed< double >; - - template < typename Type > - bool UniformClosedOpen< Type >::is_valid() const - { - if( min_value < max_value ) - { - return true; - } - geode::Logger::error( - "[Uniform ClosedOpen] - check range boundaries definintion [", - min_value, ",", max_value, "]." ); - return false; - } - template opengeode_stochastic_stochastic_api struct UniformClosedOpen< - index_t >; - template opengeode_stochastic_stochastic_api struct UniformClosedOpen< - local_index_t >; - template opengeode_stochastic_stochastic_api struct UniformClosedOpen< - signed_index_t >; - template opengeode_stochastic_stochastic_api struct UniformClosedOpen< - float >; - template opengeode_stochastic_stochastic_api struct UniformClosedOpen< - double >; + // template < typename Type > + // bool UniformClosed< Type >::is_valid() const + // { + // if( min_value < max_value ) + // { + // return true; + // } + // if( min_value == max_value ) + // { + // geode::Logger::warn( + // "[Uniform Closed] - check range boundaries definintion [", + // min_value, ",", max_value, "]." ); + // return true; + // } + // geode::Logger::error( + // "[Uniform Closed] - check range boundaries definintion [", + // min_value, ",", max_value, "]." ); + // return false; + // } + // template opengeode_stochastic_stochastic_api struct UniformClosed< + // index_t >; + // template opengeode_stochastic_stochastic_api struct UniformClosed< + // local_index_t >; + // template opengeode_stochastic_stochastic_api struct UniformClosed< + // signed_index_t >; + // template opengeode_stochastic_stochastic_api struct UniformClosed< + // float >; template opengeode_stochastic_stochastic_api struct + // UniformClosed< double >; + // + // + // template < typename Type > + // bool UniformClosedOpen< Type >::is_valid() const + // { + // if( min_value < max_value ) + // { + // return true; + // } + // geode::Logger::error( + // "[Uniform ClosedOpen] - check range boundaries definintion [", + // min_value, ",", max_value, "]." ); + // return false; + // } + // template opengeode_stochastic_stochastic_api struct UniformClosedOpen< + // index_t >; + // template opengeode_stochastic_stochastic_api struct UniformClosedOpen< + // local_index_t >; + // template opengeode_stochastic_stochastic_api struct UniformClosedOpen< + // signed_index_t >; + // template opengeode_stochastic_stochastic_api struct UniformClosedOpen< + // float >; + // template opengeode_stochastic_stochastic_api struct UniformClosedOpen< + // double >; bool Gaussian::is_valid() const {