diff --git a/.github/workflows/CIBenchmark.yml b/.github/workflows/CIBenchmark.yml index 01d9a696..23d22878 100644 --- a/.github/workflows/CIBenchmark.yml +++ b/.github/workflows/CIBenchmark.yml @@ -103,7 +103,7 @@ jobs: source bin/setup.MaCh3.sh source bin/setup.MaCh3Tutorial.sh - pprof ./bin/MCMCTutorial ${{ matrix.outpath }}/MaCh3Tutorial --focus="RunMCMC" --pdf > ${{ matrix.outpath }}/${{ matrix.title }}.pdf + pprof ./bin/${{ matrix.app }} ${{ matrix.outpath }}/MaCh3Tutorial --focus="RunMCMC" --pdf > ${{ matrix.outpath }}/${{ matrix.title }}.pdf - name: Push Plot to gh-benchmarks Branch run: | diff --git a/CIValidations/MaCh3CLI b/CIValidations/MaCh3CLI index 901a2fe9..e2160457 100755 --- a/CIValidations/MaCh3CLI +++ b/CIValidations/MaCh3CLI @@ -109,7 +109,7 @@ run_multicanoical_validations() { mkdir ${MaCh3Tutorial_ROOT}/outputs/ mkdir ${MaCh3Tutorial_ROOT}/outputs/plotting/ - ${MaCh3Tutorial_ROOT}/bin/MCMCTutorial TutorialConfigs/FitterConfig.yaml General:MCMC:Multicanonical:Enabled:True General:OutputFile:umbrella/umbrellafile0.root + ${MaCh3Tutorial_ROOT}/bin/MCMCTutorial TutorialConfigs/FitterConfig.yaml -o General:MCMC:Multicanonical:Enabled:True -o General:OutputFile:umbrella/umbrellafile0.root # Create umbrellafile1.root ... umbrellafile59.root for i in $(seq 1 59); do diff --git a/CMakeLists.txt b/CMakeLists.txt index 6713a450..ed206064 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,6 +147,7 @@ add_subdirectory(CIValidations) add_subdirectory(Tutorial) add_subdirectory(SplinesTutorial) add_subdirectory(SamplesTutorial) +add_subdirectory(MaCh3Plugins) # MOVE THIS BLOCK HERE - BEFORE add_subdirectory(python) # After MaCh3 is added/found, determine MaCh3_PREFIX diff --git a/MaCh3Plugins/CMakeLists.txt b/MaCh3Plugins/CMakeLists.txt new file mode 100644 index 00000000..6254eab3 --- /dev/null +++ b/MaCh3Plugins/CMakeLists.txt @@ -0,0 +1,37 @@ +add_custom_target(TutorialPlugins) + +foreach(app + MCMCTutorialPlugin + ) + + add_library(${app} SHARED ${app}.cpp) + + target_link_libraries(${app} PRIVATE MaCh3Tutorial::SamplesTutorial MaCh3::All) + # target_include_directories(${app} PUBLIC $) + + target_include_directories(${app} + PUBLIC + $ + $ + ) + + install( + TARGETS ${app} + EXPORT MaCh3TutorialTargets + LIBRARY DESTINATION lib + ) + + install( + FILES ${app}.hpp + DESTINATION include/Mach3Plugins + ) + + add_library(MaCh3Tutorial::${app} ALIAS ${app}) + +# add_executable(${app} ${app}.cpp) +# target_link_libraries(${app} MaCh3Tutorial::ValidationsUtils MaCh3Tutorial::SamplesTutorial) + +# add_dependencies(TutorialApps ${app}) +# install(TARGETS ${app} DESTINATION bin) +endforeach(app) + diff --git a/MaCh3Plugins/MCMCTutorialPlugin.cpp b/MaCh3Plugins/MCMCTutorialPlugin.cpp new file mode 100644 index 00000000..73f79910 --- /dev/null +++ b/MaCh3Plugins/MCMCTutorialPlugin.cpp @@ -0,0 +1,70 @@ +// MaCh3 includes +#include "Manager/MaCh3Logger.h" +#include "Fitters/MaCh3Factory.h" +#include "SamplesTutorial/SampleHandlerTutorial.h" +#include "MaCh3Plugins/MCMCTutorialPlugin.hpp" + +namespace M3{ + + MaCh3ArgumentParser* MCMCTutorialPlugin::get_parser(){ + m_parser = std::make_unique("tutorial", "1.0", argparse::default_arguments::help); + m_parser->add_argument("config") + .help("Config file.") + .metavar("CONFIG") + .required(); + m_parser->add_argument("--MCMCSteps") + .scan<'d', int>() + .help("specify the number of steps."); + m_parser->add_argument("-o", "--override") + .append() + .help("specify any config overrides."); + return m_parser.get(); + } + + int MCMCTutorialPlugin::Run(){ + std::string config = m_parser->get("config"); + + std::vector args = { m_parser->name(), config }; + + if (auto fn = m_parser->present("--MCMCSteps")) { + args.push_back("General:MCMC:NSteps:" + std::to_string(*fn)); + } + + std::vector overrides = m_parser->get>("--override"); + for (const std::string& override_str : overrides) { + args.push_back(override_str); + } + + // Convert to char* array + std::vector argv; + argv.reserve(args.size()); + for (auto& s : args) + argv.push_back(s.data()); + + // Initialise manger responsible for config handling + auto FitManager = MaCh3ManagerFactory(argv.size(), argv.data()); + + // Initialise covariance class reasonable for Systematics + auto xsec = MaCh3CovarianceFactory(FitManager.get(), "Xsec"); + + // Initialise samplePDF + auto SampleConfig = Get>(FitManager->raw()["General"]["TutorialSamples"], __FILE__ , __LINE__); + auto mySamples = MaCh3SampleHandlerFactory(SampleConfig, xsec.get()); + + // Create MCMC Class + std::unique_ptr MaCh3Fitter = MaCh3FitterFactory(FitManager.get()); + // Add covariance to MCM + MaCh3Fitter->AddSystObj(xsec.get()); + for (size_t i = 0; i < SampleConfig.size(); ++i) { + MaCh3Fitter->AddSampleHandler(mySamples[i]); + } + // Run MCMCM + MaCh3Fitter->RunMCMC(); + + for (size_t i = 0; i < SampleConfig.size(); ++i) { + delete mySamples[i]; + } + return 0; + } + +}; diff --git a/MaCh3Plugins/MCMCTutorialPlugin.hpp b/MaCh3Plugins/MCMCTutorialPlugin.hpp new file mode 100644 index 00000000..b304e86f --- /dev/null +++ b/MaCh3Plugins/MCMCTutorialPlugin.hpp @@ -0,0 +1,18 @@ +#include "CLI/API/plugin.hpp" + +namespace M3{ + + class MCMCTutorialPlugin: public PluginBase{ + + public: + virtual ~MCMCTutorialPlugin() = default; + + MaCh3ArgumentParser* get_parser(); + + int Run(); + + }; + +}; + +MACH3_REGISTER_PLUGIN(M3::MCMCTutorialPlugin) diff --git a/README.md b/README.md index 7ab61c91..7d812a4e 100644 --- a/README.md +++ b/README.md @@ -126,10 +126,14 @@ And get some reference values from other users, this can look something like: By comparing rates for each sample much what other users observe you can very simply ensure you correctly setup MaCh3. ## How to run MCMC -To run MCMC simply +To run MCMC simply: ```bash -./bin/MCMCTutorial TutorialConfigs/FitterConfig.yaml +mach3 tutorial TutorialConfigs/FitterConfig.yaml ``` + +> [!NOTE] +> The `mach3` CLI is new, and not every executable in this tutorial has been integrated into it yet. If you have any issues or feedback, please contact the developers or raise issues on this GitHub repository or the MaCh3 GitHub repository. + Congratulations! 🎉 You have just finished your first MCMC chain. You can view `Test.root` for example in TBrowser like in plot below. @@ -146,33 +150,62 @@ General: ``` and then re-running `MCMCTutorial`. -**Warning**: If you modified files in the main `MaCh3Tutorial` folder instead of `build`, you will have to call `make install` for the changes to propagate! Generally speaking, it is good practice to work from the `build` directory and make your config changes there so that local changes do not have to be tracked by git. +> [!WARNING] +> If you modified files in the main `MaCh3Tutorial` folder instead of `build`, you will have to call `make install` for the changes to propagate! Generally speaking, it is good practice to work from the `build` directory and make your config changes there so that local changes do not have to be tracked by git. ### Config Overrides Instead of changing the config file `TutorialConfigs/FitterConfig.yaml` above directly, you can instead dynamically override your configurations at the command line like this: ```bash -./bin/MCMCTutorial TutorialConfigs/FitterConfig.yaml General:MCMC:NSteps:100000 +mach3 tutorial TutorialConfigs/FitterConfig.yaml --override General:MCMC:NSteps:100000 ``` -In this way, you can add as many configuration overrides here as you like, as long as the format is `[executable] [config] ([option1] [option2] ...)`. +You can also use the short form `-o` instead of `--override`. You can add as many configuration overrides here as you like: `[executable] [config] (-o [option1] -o [option2] ...)`. + +> [!WARNING] +> This overriding process is only possible for the "main config" (i.e., configs that respond directly to the `Manager` class in MaCh3 core). +> This main config is used with the apps in the `Tutorial` folder here; for the other apps (mainly plotting apps like `PredictivePlotting`) +> that read from `bin/TutorialDiagConfig.yaml`, this is not possible, +> as further command line arguments are interpreted as *input ROOT files*, not override directives. +> +> [!WARNING] +> The format required for overriding is different for the executables described later on in this tutorial which have not been integrated into the `mach3` CLI. +> For those executables, the `--override` option is not needed, simply write the settings you want to override as arguments at the end of the command. +> For example `./bin/PredictiveTutorial TutorialConfigs/FitterConfig.yaml General:OutputFile:PriorPredictiveOutputTest.root`. -**Warning** This overriding process is only possible for the "main config" (i.e., configs that respond directly to the `Manager` class in MaCh3 core). This main config is used with the apps in the `Tutorial` folder here; -for the other apps (mainly plotting apps like `PredictivePlotting`) that read from `bin/TutorialDiagConfig.yaml`, this is not possible, as further command line arguments are interpreted as *input ROOT files*, not override directives. +If you're lucky, the setting you plan to override may have an explicit option in `mach3 tutorial`. To find out run `mach3 tutorial --help`: +```console +Usage: mach3 tutorial [--help] [--MCMCSteps VAR] [--override VAR]... CONFIG + +Positional arguments: + CONFIG Config file. [required] + +Optional arguments: + -h, --help shows help message and exits + --MCMCSteps specify the number of steps. + -o, --override specify any config overrides. [may be repeated] +``` +You can see the `--MCMCSteps` option, so earlier we could have instead written: +```bash +mach3 tutorial TutorialConfigs/FitterConfig.yaml --MCMCSteps 100000 +``` ### Processing MCMC Outputs -Being able to visualise and analyse the output of the MCMC is standard procedure after a chain has finished. MaCh3 uses `ProcessMCMC` to transform the raw output from the MCMC into smaller outputs that are more easily digested by downstream plotting macros. You can run it using +Being able to visualise and analyse the output of the MCMC is standard procedure after a chain has finished. MaCh3 uses `mach3 process` (previously `ProcessMCMC`) to transform the raw output from the MCMC into smaller outputs that are more easily digested by downstream plotting macros. You can run it using ```bash -./bin/ProcessMCMC bin/TutorialDiagConfig.yaml Test.root +mach3 process bin/TutorialDiagConfig.yaml Test.root ``` -where `Test.root` is the output of running `MCMCTutorial` as described [here](#how-to-run-mcmc). You can find some quickly-generated plots in `Test_drawCorr.pdf`. One of plots you will encounter is: +where `Test.root` is the output of running `mach3 tutorial` as described [here](#how-to-run-mcmc). You can find some quickly-generated plots in `Test_drawCorr.pdf`. One of plots you will encounter is: Posterior example This is the marginalised posterior of a single parameter. This is the main output of the MCMC. -There are many options in `ProcessMCMC` that allow you to make many more analysis plots from the MCMC output; we recommend that you see [here](https://mach3-software.github.io/MaCh3/BayesianAnalysis.html) to get better idea what each plot means. In particular, we recommend comparing 2D posteriors with the correlation matrix, and playing with triangle plots. +There are many options in `mach3 process` that allow you to make many more analysis plots from the MCMC output; we recommend that you see [here](https://mach3-software.github.io/MaCh3/BayesianAnalysis.html) to get better idea what each plot means. In particular, we recommend comparing 2D posteriors with the correlation matrix, and playing with triangle plots. + +> [!TIP] +> Some of those option are immediately available in the command line, find out which ones with `mach3 process --help`. ### Plotting MCMC Posteriors -Once you have the processed MCMC output from `ProcessMCMC`, which will be called something like `_Process.root`, you can make fancier analysis plots from it using the `GetPostfitParamPlots` app like: +Once you have the processed MCMC output from `mach3 process`, which will be called something like `_Process.root`, you can make fancier analysis plots from it using the `GetPostfitParamPlots` app like: ```bash ./bin/GetPostfitParamPlots Test_drawCorr.root @@ -191,14 +224,14 @@ The output should look like plot below. This conveys same information as the ind Ridge **Violin Plot** - This also allow to see nicely non-Gaussian parameters but also is useful in comparing two chains. -`ProcessMCMC` must be run with option "PlotCorr" to be able to produce violin plot. +`mach3 process` must be run with option "--corr" to be able to produce violin plot. Violin example ### Plotting Correlation Matrix -If you have run `ProcessMCMC` with option "PlotCorr" you will have a correlation matrix in the outputs. This is a handy tool for viewing how correlated different parameters are. +If you have run `mach3 process` with option "-corr" you will have a correlation matrix in the outputs. This is a handy tool for viewing how correlated different parameters are. However, mature analyses with hundreds of parameters may run into the problem of having too large of plots to be useful. To combat this, you can plot a subset of parameters using `MatrixPlotter`: ```bash ./bin/MatrixPlotter bin/TutorialDiagConfig.yaml Test_drawCorr.root @@ -317,7 +350,7 @@ Congratulations, you have successfully modified the MCMC! 🎉 ### How to Compare Chains Now that you have two chains you can try comparing them using the following: ```bash -./bin/ProcessMCMC bin/TutorialDiagConfig.yaml Test.root Default_Chain Test_Modified.root Modified_Chain +mach3 process bin/TutorialDiagConfig.yaml Test.root Default_Chain Test_Modified.root Modified_Chain ``` This will produce a pdf with plots showing overlayed posteriors from both chains. Most should be similarly except modified parameter. @@ -426,7 +459,7 @@ You can also easily change what variable the sample is binned in to get slightly ``` You can run the MCMC again using this new configuration (after changing the output file name again!), and then compare all 3 chains using: ```bash -./bin/ProcessMCMC bin/TutorialDiagConfig.yaml Test.root Default_Chain Test_Modified.root Modified_Chain Test_Modified_Sample.root ModifiedSameple_Chain +mach3 process bin/TutorialDiagConfig.yaml Test.root Default_Chain Test_Modified.root Modified_Chain Test_Modified_Sample.root ModifiedSameple_Chain ```
@@ -621,7 +654,7 @@ The latter class deals with actual event reweighting and general heavy lifting. ## MCMC Diagnostic A crucial part of MCMC is diagnosing whether a chain has converged or not. You can produce chain diagnostics by running: ```bash -./bin/DiagMCMC Test.root bin/TutorialDiagConfig.yaml +mach3 diag Test.root bin/TutorialDiagConfig.yaml ``` This will produce a plethora of diagnostic information. One of most important of these are the autocorrelations for each of the parameters. Autocorrelation indicates how correlated MCMC steps are when they are n-steps apart in the chain. Generally speaking, we want the autocorrelation to drop to 0 fast and stay around there (like in the plot below). You can read about other diagnostics [here](https://mach3-software.github.io/MaCh3/MCMCconvergance.html), but it is sufficient for now to focus on autocorrelation. diff --git a/Tutorial/CMakeLists.txt b/Tutorial/CMakeLists.txt index 3b32241c..db92bbd9 100755 --- a/Tutorial/CMakeLists.txt +++ b/Tutorial/CMakeLists.txt @@ -1,7 +1,6 @@ add_custom_target(TutorialApps) foreach(app - MCMCTutorial LLHScanTutorial PredictiveTutorial SigmaVarTutorial @@ -20,6 +19,22 @@ foreach(app install(TARGETS ${app} DESTINATION bin) endforeach(app) +foreach(plugin_app + MCMCTutorial + ) + add_executable(${plugin_app} ${plugin_app}.cpp) + target_link_libraries(${plugin_app} MaCh3Tutorial::${plugin_app}Plugin MaCh3Tutorial::ValidationsUtils MaCh3Tutorial::SamplesTutorial) + add_dependencies(${plugin_app} ${plugin_app}Plugin) # must be real target, not alias + if(MaCh3Tutorial_PROFILING_ENABLED) + target_link_libraries(${plugin_app} + profiler # Add this line to link gperftools + ) + endif() + + add_dependencies(TutorialApps ${plugin_app}) + install(TARGETS ${plugin_app} DESTINATION bin) +endforeach(plugin_app) + install(FILES TutorialDiagConfig.yaml DESTINATION ${CMAKE_BINARY_DIR}/bin) diff --git a/Tutorial/MCMCTutorial.cpp b/Tutorial/MCMCTutorial.cpp index f6e2fe49..9ace3e0b 100755 --- a/Tutorial/MCMCTutorial.cpp +++ b/Tutorial/MCMCTutorial.cpp @@ -1,30 +1,16 @@ // MaCh3 includes -#include "Fitters/MaCh3Factory.h" -#include "SamplesTutorial/SampleHandlerTutorial.h" +#include "Manager/MaCh3Logger.h" +#include "MaCh3Plugins/MCMCTutorialPlugin.hpp" -int main(int argc, char *argv[]) { - // Initialise manger responsible for config handling - auto FitManager = MaCh3ManagerFactory(argc, argv); - - // Initialise covariance class reasonable for Systematics - auto xsec = MaCh3CovarianceFactory(FitManager.get(), "Xsec"); - - // Initialise samplePDF - auto SampleConfig = Get>(FitManager->raw()["General"]["TutorialSamples"], __FILE__ , __LINE__); - auto mySamples = MaCh3SampleHandlerFactory(SampleConfig, xsec.get()); - - // Create MCMC Class - std::unique_ptr MaCh3Fitter = MaCh3FitterFactory(FitManager.get()); - // Add covariance to MCM - MaCh3Fitter->AddSystObj(xsec.get()); - for (size_t i = 0; i < SampleConfig.size(); ++i) { - MaCh3Fitter->AddSampleHandler(mySamples[i]); - } - // Run MCMCM - MaCh3Fitter->RunMCMC(); - - for (size_t i = 0; i < SampleConfig.size(); ++i) { - delete mySamples[i]; - } - return 0; +int main(int argc, char const* argv[]) { + MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); + MACH3LOG_WARN(" : you can use 'mach3 tutorial' as a direct replacement instead."); + argv[0] = "tutorial"; + M3::MCMCTutorialPlugin tutorial; + ArgumentParser* parser = tutorial.get_parser(); + parser->parse_args(argc, argv); + tutorial.Run(); + MACH3LOG_WARN("Deprecation Warning: Use of the standalone executable will be deprecated in future releases."); + MACH3LOG_WARN(" : you can use 'mach3 tutorial' as a direct replacement instead."); + return 0; } diff --git a/cmake/Templates/setup.MaCh3Tutorial.sh.in b/cmake/Templates/setup.MaCh3Tutorial.sh.in index db250e63..8c2dc570 100755 --- a/cmake/Templates/setup.MaCh3Tutorial.sh.in +++ b/cmake/Templates/setup.MaCh3Tutorial.sh.in @@ -107,4 +107,6 @@ else fi fi +export MACH3_PLUGINS=${SETUPDIR}/MaCh3Plugins + unset SETUPDIR