23 std::string input =
"default/lha/testInput.flha";
24 std::string output =
"benchmark_decays.csv";
27 unsigned int threads = 0;
29 std::vector<std::pair<double, double>>
bins {{15.0, 17.0}};
30 bool include_unbinned =
false;
35 bool supports_threads =
false;
36 bool requires_bins =
false;
39QCDOrder parse_order(std::string value) {
40 std::transform(value.begin(), value.end(), value.begin(), [](
unsigned char c) { return static_cast<char>(std::toupper(c)); });
45 LOG_ERROR(
"ArgumentError",
"Unknown QCD order:", value,
"expected LO, NLO, NNLO or NONE.");
49void print_usage(
const char* exe) {
51 <<
"Usage: " << exe <<
" [options]\n"
53 <<
" --input PATH Input FLHA/LHA file (default: default/lha/testInput.flha)\n"
54 <<
" --out PATH Output CSV file (default: benchmark_decays.csv)\n"
55 <<
" --repeats N Timed repetitions per decay (default: 30)\n"
56 <<
" --warmups N Untimed warm-up calls per decay (default: 3)\n"
57 <<
" --threads N Threads for decays exposing set_*_threads; 0 uses hardware_concurrency (default: 0)\n"
58 <<
" --order ORDER QCD order: LO, NLO, NNLO or NONE (default: NLO)\n"
59 <<
" --bin QMIN:QMAX Add a q^2 bin for binned observables; can be repeated (default: 1:6)\n"
60 <<
" --include-unbinned Also benchmark non-binned observables inside mixed decays, such as BKstarll q0(A_FB)\n"
61 <<
" --help Show this message\n";
64std::pair<double, double> parse_bin(
const std::string& text) {
65 const auto pos = text.find(
':');
66 if (pos == std::string::npos) {
67 LOG_ERROR(
"ArgumentError",
"Invalid --bin value:", text,
"expected QMIN:QMAX.");
69 double qmin = std::stod(text.substr(0, pos));
70 double qmax = std::stod(text.substr(pos + 1));
72 LOG_ERROR(
"ArgumentError",
"Invalid --bin value:", text,
"expected QMIN < QMAX.");
77std::string format_bins(
const std::vector<std::pair<double, double>>& bins) {
79 for (
size_t i = 0; i <
bins.size(); ++i) {
80 if (i != 0) out +=
";";
81 out += std::to_string(bins[i].first) +
":" + std::to_string(bins[i].second);
86Options parse_args(
int argc,
char** argv) {
88 for (
int i = 1; i < argc; ++i) {
89 const std::string
arg = argv[i];
90 auto require_value = [&](
const std::string&
name) -> std::string {
92 LOG_ERROR(
"ArgumentError",
"Missing value after", name);
97 if (arg ==
"--input") opt.input = require_value(arg);
98 else if (arg ==
"--out") opt.output = require_value(arg);
99 else if (arg ==
"--repeats") opt.repeats = std::stoi(require_value(arg));
100 else if (arg ==
"--warmups") opt.warmups = std::stoi(require_value(arg));
101 else if (arg ==
"--threads") opt.threads =
static_cast<unsigned int>(std::stoul(require_value(arg)));
102 else if (arg ==
"--order") opt.order = parse_order(require_value(arg));
103 else if (arg ==
"--include-unbinned") opt.include_unbinned =
true;
104 else if (arg ==
"--bin") {
105 if (opt.bins.size() == 1 && opt.bins.front() == std::pair<double, double>{15.0, 17.0}) {
108 opt.bins.push_back(parse_bin(require_value(arg)));
110 else if (arg ==
"--help" || arg ==
"-h") {
111 print_usage(argv[0]);
114 LOG_ERROR(
"ArgumentError",
"Unknown option:", arg);
118 if (opt.repeats <= 0)
LOG_ERROR(
"ArgumentError",
"--repeats must be positive.");
119 if (opt.warmups < 0)
LOG_ERROR(
"ArgumentError",
"--warmups must be non-negative.");
120 if (opt.bins.empty())
LOG_ERROR(
"ArgumentError",
"At least one --bin is required.");
122 if (opt.threads == 0) {
123 opt.threads = std::thread::hardware_concurrency();
124 if (opt.threads == 0) opt.threads = 1;
129double checksum(
const std::map<
ObservableId, std::vector<ObservableValue>>& results) {
131 for (
const auto& [_, values] :
results) {
132 for (
const auto& value : values) {
140 double mean_ms = 0.0;
141 double stddev_ms = 0.0;
148 stats.mean_ms = std::accumulate(samples.begin(), samples.end(), 0.0) /
static_cast<double>(samples.size());
149 stats.min_ms = *std::min_element(samples.begin(), samples.end());
150 stats.max_ms = *std::max_element(samples.begin(), samples.end());
152 double variance = 0.0;
153 for (
double x : samples) {
154 variance += (x - stats.mean_ms) * (x - stats.mean_ms);
156 variance /=
static_cast<double>(samples.size());
157 stats.stddev_ms = std::sqrt(variance);
161std::vector<DecayCase> default_decay_cases() {
186 const std::vector<std::pair<double, double>>& bins,
187 bool include_unbinned)
190 if (observables.empty()) {
194 bool added_any =
false;
195 for (
const auto& obs : observables) {
197 for (
const auto& bin :
bins) {
201 }
else if (include_unbinned) {
209 "has no selected observable after applying benchmark filters.");
231int main(
int argc,
char** argv) {
233 const Options opt = parse_args(argc, argv);
238 hyperiso.
init(opt.input, config);
240 std::ofstream csv(opt.output);
242 LOG_ERROR(
"IOError",
"Cannot open output CSV:", opt.output);
245 csv <<
"decay,order,threads,bins,include_unbinned,repeats,warmups,n_observables,mean_ms,stddev_ms,min_ms,max_ms,checksum\n";
247 for (
const auto& decay_case : default_decay_cases()) {
249 if (decay_case.requires_bins) {
250 add_decay_observables_with_bins(interface, decay_case.decay, opt.order, opt.bins, opt.include_unbinned);
254 if (decay_case.supports_threads) {
255 apply_thread_setting(interface, decay_case.decay, opt.threads);
258 for (
int i = 0; i < opt.warmups; ++i) {
259 volatile double sink = checksum(interface.
compute_all());
263 std::vector<double> samples_ms;
264 samples_ms.reserve(
static_cast<size_t>(opt.repeats));
265 double last_checksum = 0.0;
266 size_t n_observables = 0;
268 for (
int i = 0; i < opt.repeats; ++i) {
269 const auto start = std::chrono::steady_clock::now();
271 const auto stop = std::chrono::steady_clock::now();
273 n_observables = results.size();
274 last_checksum = checksum(results);
275 samples_ms.push_back(std::chrono::duration<double, std::milli>(stop - start).count());
278 const Stats stats = compute_stats(samples_ms);
280 csv << decay_name <<
','
282 << (decay_case.supports_threads ? opt.threads : 1) <<
','
283 << (decay_case.requires_bins ? format_bins(opt.bins) :
"") <<
','
284 << (opt.include_unbinned ? 1 : 0) <<
','
285 << opt.repeats <<
','
286 << opt.warmups <<
','
287 << n_observables <<
','
288 << stats.mean_ms <<
','
289 << stats.stddev_ms <<
','
290 << stats.min_ms <<
','
291 << stats.max_ms <<
','
292 << last_checksum <<
'\n';
295 std::cout <<
"decay=" << decay_name
296 <<
" mean_ms=" << stats.mean_ms
297 <<
" stddev_ms=" << stats.stddev_ms
298 <<
" n_observables=" << n_observables <<
'\n';
301 std::cout <<
"Wrote " << opt.output <<
'\n';
High-level helpers for initializing and monitoring the Hyperiso framework.
#define LOG_ERROR(type,...)
Macro for logging error messages and terminating the application.
High-level, user-facing entry point to compute flavor observables.
static std::vector< Observables > get_observables(Decays d)
Legacy static lookup of builtin observables for a builtin decay.
static std::string str(const IdOf< QCDOrderTag > &id)
Returns the string representation associated with an identifier.
static std::string str(const IdOf< DecayTag > &id)
Returns the string representation of an identifier.
High-level interface to initialize and monitor the main framework configuration.
void init(const std::string &lhaFile, HyperisoConfig config)
Initializes Hyperiso using a LHA file and a full Config object.
void setLevel(LogLevel level)
Sets the logging level.
static Logger * getInstance()
Retrieves the singleton instance of the Logger.
void add_observables(std::map< Observables, QCDOrder > obss, bool add_dependencies=false)
Add multiple observables at once (enum map).
void set_bkstarll_threads(size_t n_threads)
Set the thread option for the bkstarll decay.
bool is_observable_binned(Observables obs) const
Return whether a specific observable requires q² bins.
void set_bkll_threads(size_t n_threads)
Set the thread option for the bkll decay.
std::map< ObservableId, std::vector< ObservableValue > > compute_all()
Compute all currently registered observables.
ObservableInterface & add_observable(Observables obs, QCDOrder order, bool add_dependencies=false)
Add an observable to the manager (enum API).
void set_bsphi_threads(size_t n_threads)
Set the thread option for the bsphi decay.
Dict[str, float] compute_stats(np.ndarray Y, np.ndarray R)
Identifies an observable together with a numerical bin.
Configuration object controlling model, input flags and optional MARTY resources.
Model model
Current model.