diff --git a/ScannerBit/src/scanners/python/plugins/gambit_binminpy.py b/ScannerBit/src/scanners/python/plugins/gambit_binminpy.py deleted file mode 100644 index 676874f6d3..0000000000 --- a/ScannerBit/src/scanners/python/plugins/gambit_binminpy.py +++ /dev/null @@ -1,167 +0,0 @@ -""" -Binminpy scanners -================= -""" - -import numpy as np -from scannerbit import with_mpi as scannerbit_with_mpi -from utils import copydoc, version, with_mpi -try: - import binminpy - binminpy_version = version(binminpy) - from binminpy.BinMinBottomUp import BinMinBottomUp as binminpy_BinMinBottomUp -except: - __error__ = "The binminpy package is not installed. To install it, run: pip install git+https://github.com/anderkve/binminpy.git" - binminpy_version = "n/a" - binminpy_BinMinBottomUp = None - -import scanner_plugin as splug - - -class BinMinBottomUp(splug.scanner): - """ -Sampling and optimization based on the "bottom-up" mode of binminpy, where the -parameter space is binned by working outwards from all identified local optima. - -See https://github.com/anderkve/binminpy - -YAML options: - like: Use the functors that correspond to the specified purpose. - run: - n_bins: Number of bins for each parameter, given as a list on the form "model::parameter: ". - sampled_parameters: List of the parameters that should be sampled within each bin, e.g ["model::par_1", "model::par_2"]. - (Remaining parameters will be optimized within each bin.) - sampler: Choice of sampler for sampling parameters within each bin. - optimizer Choice of optimizer for initial global optimization and optimizing parameters within each bin. - optimizer_kwargs: Keyword arguments to be forwarded to the optimzer. - n_initial_points: Number of starting points for the initial search for local optima. - n_sampler_points_per_bin: Number of sampled points within each bin. - accept_loglike_above: Add neighboring bins for bins that have a highest loglike above this threshold. - accept_delta_loglike: Add neighboring bins for bins that have a delta loglike (difference to best-fit point) within this threshold. - neighborhood_distance: If the current bin is accepted, how many bins in each direction should be added to the list of tasks. - inherit_best_init_point_within_bin: When optimizing parameters, start optimization from the current best point within the given bin. - n_optim_restarts_per_bin: Number of repeated attempts at optimizing parameters per bin. - n_tasks_per_batch: Number of tasks (bins) assigned to each MPI worker process at a time. - print_progress_every_n_batch: How frequently the progress message is printed. -""" - - __version__ = binminpy_version - __plugin_name__ = "binminpy" - - - def __init__(self, **kwargs): - if not scannerbit_with_mpi: - raise Exception(f"GAMBIT has been compiled with MPI disabled (WITH_MPI=0), but the " - f"binminpy scanner requires MPI parallelisation with >1 MPI processes. " - f"Rerun CMake with \"cmake -DWITH_MPI=1\" and then recompile GAMBIT.") - if not with_mpi: - raise Exception(f"The binminpy scanner requires MPI parallelisation with >1 MPI processes. " - f"Make sure that mpi4py is installed and restart GAMBIT with >1 processes.") - - super().__init__(use_mpi=True, use_resume=False) - - self.print_prefix = f"{BinMinBottomUp.__plugin_name__} scanner plugin:" - - - def run(self): - - # Define target function: this is where we call the GAMBIT loglike function - def target_function(x, *args): - return -self.loglike_hypercube(x) - - # Get the parameter ordering from GAMBIT - par_indices = {par_name:idx for idx,par_name in enumerate(self.parameter_names)} - - # Set up the list of binning tuples - binning_tuples = [] - if not "n_bins" in self.run_args: - raise RuntimeError(f"{self.print_prefix} The run argument 'n_bins' is missing.") - for param_name in self.parameter_names: - if not param_name in self.run_args["n_bins"]: - self.run_args["n_bins"][param_name] = 1 - par_n_bins = self.run_args["n_bins"][param_name] - binning_tuples.append([0., 1., par_n_bins]) # <-- Working in the unit hypercube - - # Read list of sampled parameters, and remove any duplicate entries - sampled_parameter_names = list(set(self.run_args.get("sampled_parameters", []))) - # Remove any parameter that is not scanned by GAMBIT - sampled_parameter_names = [par_name for par_name in sampled_parameter_names if par_name in self.parameter_names] - # All parameters that are not listed as sampled parameters should be optimized - optimized_parameter_names = list(set(self.parameter_names).difference(sampled_parameter_names)) - - if self.mpi_rank == 0: - print(f"{self.print_prefix} Parameters that will be *sampled* in each bin: {sampled_parameter_names}", flush=True) - print(f"{self.print_prefix} Parameters that will be *optimized* in each bin: {optimized_parameter_names}", flush=True) - - # Parse options for restricting the set of parameter bins - if ("accept_loglike_above" in self.run_args) and ("accept_delta_loglike" in self.run_args): - if self.mpi_rank == 0: - print(f"{self.print_prefix} Both 'accept_loglike_above' and 'accept_delta_loglike' have been set. " - f"Will use the weaker of the two requirements when constructing bins.", flush=True) - - accept_target_below = -np.inf - if "accept_loglike_above" in self.run_args: - accept_target_below = -1.0 * self.run_args["accept_loglike_above"] - - accept_delta_target_below = -np.inf - if "accept_delta_loglike" in self.run_args: - accept_delta_target_below = abs(self.run_args["accept_delta_loglike"]) - - if (accept_target_below == -np.inf) and (accept_delta_target_below == -np.inf): - if self.mpi_rank == 0: - print(f"{self.print_prefix} Running with no restrictions on the set of parameter space bins.", flush=True) - accept_target_below = np.inf - accept_delta_target_below = np.inf - - # Create the BinMinBottomUp instance - binned_opt = binminpy_BinMinBottomUp( - target_function, - binning_tuples, - args=(), - sampler=self.run_args.get("sampler", "latinhypercube"), - optimizer=self.run_args.get("optimizer", "minimize"), - optimizer_kwargs=self.run_args.get("optimizer_kwargs", {}), - sampled_parameters=tuple(par_indices[par_name] for par_name in sampled_parameter_names), - n_initial_points=self.run_args.get("n_initial_points", self.mpi_size), - n_sampler_points_per_bin=self.run_args.get("n_sampler_points_per_bin", 10), - inherit_best_init_point_within_bin=self.run_args.get("inherit_best_init_point_within_bin", False), - accept_target_below=accept_target_below, - accept_delta_target_below=accept_delta_target_below, - save_evals=self.run_args.get("save_evals", False), - return_evals=False, - return_bin_centers=False, - return_bin_results=False, - optima_comparison_rtol=self.run_args.get("optima_comparison_rtol", 1e-9), - optima_comparison_atol=self.run_args.get("optima_comparison_atol", 0.0), - neighborhood_distance=self.run_args.get("neighborhood_distance", 1), - n_optim_restarts_per_bin=self.run_args.get("n_optim_restarts_per_bin", 1), - n_tasks_per_batch=self.run_args.get("n_tasks_per_batch", 10), - print_progress_every_n_batch=self.run_args.get("print_progress_every_n_batch", 1000), - max_tasks_per_worker=self.run_args.get("max_tasks_per_worker", np.inf), - max_n_bins=self.run_args.get("max_n_bins", np.inf), - ) - - # Run the scan! - result = binned_opt.run() - - # Print a summary - if self.mpi_rank == 0: - best_bins = result["optimal_bins"] - print() - print(f"{self.print_prefix} # Global optima found in bin(s) {best_bins}:", flush=True) - for i, bin_index_tuple in enumerate(best_bins): - x_opt_physical = self.transform_to_vec(result['x_optimal'][i]) - x_print_dict = dict(zip(self.parameter_names, x_opt_physical.tolist())) - print(f"{self.print_prefix} - Bin {bin_index_tuple}:", flush=True) - print(f"{self.print_prefix} - Parameters:", flush=True) - for key, val in x_print_dict.items(): - print(f"{self.print_prefix} - {key}: {val}") - print(f"{self.print_prefix} - Log-likelihood: {result['y_optimal'][i]}", flush=True) - print() - print(f"{self.print_prefix} Target function calls: {result['n_target_calls']}", flush=True) - print() - - return 0 - - -__plugins__ = {BinMinBottomUp.__plugin_name__: BinMinBottomUp} diff --git a/ScannerBit/src/scanners/python/plugins/gambit_paraprof.py b/ScannerBit/src/scanners/python/plugins/gambit_paraprof.py new file mode 100644 index 0000000000..abba36d3b0 --- /dev/null +++ b/ScannerBit/src/scanners/python/plugins/gambit_paraprof.py @@ -0,0 +1,288 @@ +""" +ParaProf scanner +================ + +ScannerBit plugin wrapping the paraprof package +(https://github.com/anderkve/paraprof) for parallel grid-based profile +likelihood scans inside GAMBIT. + +ParaProf places populations on a regular grid over a user-chosen subset of +parameters and dynamically activates the region of interest, optimising the +profiled parameters at each grid point with differential evolution or +L-BFGS-B. The plugin uses paraprof's master/worker MPI scheme: rank 0 acts as +the orchestrator and ranks 1+ evaluate the GAMBIT loglike via +``self.loglike_hypercube``. Because the bound loglike cannot be pickled, the +target function is supplied to the workers directly rather than broadcast. + +Drop this file into ``ScannerBit/src/scanners/python/plugins/`` in your GAMBIT +source tree. Requires ``mpi4py`` and the ``paraprof`` package installed in the +Python environment GAMBIT is using. +""" + +import numpy as np + +from scannerbit import with_mpi as scannerbit_with_mpi +from utils import copydoc, version, with_mpi + +try: + import paraprof + paraprof_version = version(paraprof) + paraprof_ProfileProjector = paraprof.ProfileProjector + paraprof_run_scan = paraprof.run_scan + paraprof_worker_main = paraprof.worker_main +except Exception: + __error__ = ("The paraprof package is not installed. To install it, run: " + "pip install git+https://github.com/anderkve/paraprof.git") + paraprof_version = "n/a" + paraprof_ProfileProjector = None + paraprof_run_scan = None + paraprof_worker_main = None + +import scanner_plugin as splug + + +class ParaProf(splug.scanner): + """ +Parallel grid-based profile likelihood scan with paraprof. + +See https://github.com/anderkve/paraprof for the algorithm and tuning details. + +Requires MPI with at least 2 processes (one master + one or more workers); the +master rank performs no target evaluations. + +YAML options: + like: Use the functors that correspond to the specified purpose. + run: All paraprof-native settings live here. Required key: + projections: List of projection configurations. Each entry is a + dict with required keys 'dims' (list of parameter + names or indices) and 'grid_points' (list of ints, + same length as 'dims'). Optional per-projection keys: + optimization_method: 'de' (default) or 'lbfgsb' + grid_refinement_factor: int > 1 enables a refined run + refinement_method: interpolation method (default 'linear') + patch_coarse_grid: bool (default true) + patch_refined_grid: bool (default false) + Optional ProfileProjector tuning keys: + roi_threshold: ROI cutoff in chi^2 units (default 3.0). + pop_per_grid_point: DE population size per grid cell (default 3). + n_initial_optimizations: Global L-BFGS-B starts before grid optimization + (default min(100, 20*n_dims)). + max_patching_waves: Cap on patching iterations (default 10). + lbfgsb_max_iter: Max L-BFGS-B iterations per polish (default 50). + lbfgsb_polish: Apply L-BFGS-B polish after DE (default true). + use_clustering: Detect modes during refinement (default true). + refinement_direct_eval: Skip optimisation in refinement runs (default false). + initial_points: Optional list of starting points in physical + coordinates to activate explicitly. + samples_output_file: Optional CSV path; written by rank 0 only. Note that + GAMBIT's printers already record every evaluation, + so this is purely a paraprof-side diagnostic file. + warm_start_file: Optional CSV path read at the start of each projection + to pre-populate ``initial_maxima``, skipping the + global L-BFGS-B seeding step. Set equal to + ``samples_output_file`` to round-trip samples into + the next run. + advanced_config: Forwarded as-is to ProfileProjector. Sub-dicts: + 'de', 'lbfgsb', 'clustering', 'cross_projection' + (multi-projection knowledge transfer), + 'suspect_recheck' (wrong-optimum strip recheck). + See the paraprof README for the full key list. + Optional run-time keys: + save_plots: If true, paraprof writes its diagnostic plots to + the working directory after each projection. + plot_settings: Dict forwarded to paraprof's plotting helpers. + Common keys: dpi, filetype, vmin, vmax, + contour_levels, slice_mode, plot_profiled_params. + +paraprof's ``grad_func`` is not exposed: ScannerBit's Python API surfaces +only the loglike value, so L-BFGS-B uses finite differences. +""" + + __version__ = paraprof_version + __plugin_name__ = "paraprof" + + + def __init__(self, **kwargs): + if not scannerbit_with_mpi: + raise Exception( + "GAMBIT has been compiled with MPI disabled (WITH_MPI=0), but the " + "paraprof scanner requires MPI parallelisation with >=2 MPI " + "processes (1 master + >=1 worker). Rerun CMake with " + "\"cmake -DWITH_MPI=1\" and recompile GAMBIT." + ) + if not with_mpi: + raise Exception( + "The paraprof scanner requires MPI parallelisation. Make sure " + "mpi4py is installed in the Python environment GAMBIT is using." + ) + + super().__init__(use_mpi=True, use_resume=False) + + if self.mpi_size < 2: + raise Exception( + "The paraprof scanner requires >=2 MPI processes (1 master + " + ">=1 worker); the master rank performs no target evaluations. " + f"Detected MPI size = {self.mpi_size}." + ) + + self.print_prefix = f"{ParaProf.__plugin_name__} scanner plugin:" + + # All paraprof-native settings live under the YAML 'run:' block; the + # top level of the scanner block is reserved for ScannerBit itself. + ra = self.run_args + + if "projections" not in ra: + raise RuntimeError( + f"{self.print_prefix} The required scanner option 'projections' " + "is missing from the 'run:' block." + ) + # Defensive copy so downstream mutation (string-dim resolution etc.) + # doesn't disturb the YAML dict. + self.projections = [dict(p) for p in ra["projections"]] + for p in self.projections: + for k in ('dims', 'grid_points'): + if k in p and isinstance(p[k], tuple): + p[k] = list(p[k]) + + # ProfileProjector tuning. Each is optional; we only forward keys the + # user actually set so paraprof's own defaults stay authoritative. + self.projector_kwargs = {} + for key in ( + "roi_threshold", "pop_per_grid_point", "max_patching_waves", + "lbfgsb_max_iter", "lbfgsb_polish", "n_initial_optimizations", + "initial_points", "use_clustering", "refinement_direct_eval", + "samples_output_file", "warm_start_file", "advanced_config", + ): + if key in ra: + self.projector_kwargs[key] = ra[key] + + # Plot / output controls (paraprof-side, not GAMBIT printer side). + self.save_plots = bool(ra.get("save_plots", False)) + self.plot_settings = ra.get("plot_settings", None) + + + @copydoc(paraprof_ProfileProjector) + def run(self): + from mpi4py import MPI + comm = MPI.COMM_WORLD + + # The target function on every rank is GAMBIT's loglike, called via + # the unit-hypercube convenience method. Wrapping it lets us emit a + # weight=1 entry on the standard "Posterior" stream after each call, + # mirroring grid.py / the other ScannerBit Python plugins. + plugin = self # bind into closure; avoids capturing self by name twice + + def target_func(x): + lnL = plugin.loglike_hypercube(x) + plugin.print(1.0, "Posterior") + return lnL + + if self.mpi_rank != 0: + # Worker rank: hand the target function over directly. No bcast. + paraprof_worker_main(comm, self.mpi_rank, target_func=target_func) + return 0 + + # ---- Master rank ---- + # Bounds always live on the unit hypercube; physical-space bounds come + # from the YAML Parameters node. + bounds = [(0.0, 1.0)] * self.dim + + if self.mpi_rank == 0: + print(f"{self.print_prefix} Starting paraprof scan with " + f"{len(self.projections)} projection(s) on {self.mpi_size - 1} " + f"worker rank(s).", flush=True) + + with paraprof_ProfileProjector( + target_func=target_func, + bounds=bounds, + projections=self.projections, + parameter_names=list(self.parameter_names), + **self.projector_kwargs, + ) as sampler: + + results = paraprof_run_scan( + comm=comm, + sampler=sampler, + projections=self.projections, + save_plots=self.save_plots, + plot_settings=self.plot_settings, + broadcast_target_func=False, # workers already have target_func + myrank=0, + ) + + self._print_summary(results) + + return 0 + + + def _print_summary(self, results): + """Emit a per-projection summary on rank 0, in the scipy style.""" + prefix = self.print_prefix + print() + print(f"{prefix} === Scan summary ===", flush=True) + for i, res in enumerate(results): + cfg = res.get('projection_config', {}) + metrics = res.get('metrics', {}) + dims = cfg.get('dims', []) + dim_names = [self.parameter_names[d] for d in dims] + calls = metrics.get('total_target_calls', 'n/a') + global_max = metrics.get('global_max', float('nan')) + print(f"{prefix} - Projection {i + 1}:", flush=True) + print(f"{prefix} dims (indices): {dims}", flush=True) + print(f"{prefix} dims (names): {dim_names}", flush=True) + print(f"{prefix} grid_points: {cfg.get('grid_points')}", flush=True) + print(f"{prefix} target calls: {calls}", flush=True) + print(f"{prefix} best logL found: {global_max:.6e}", flush=True) + + # Best-fit point: prefer the refined solution if available, + # otherwise the coarse one. The exported solution dict doesn't + # carry a "global best" field, so we derive it ourselves from the + # global solution pool (capped pool of the best entries) and fall + # back to the per-cell solutions table if the pool is empty. + best_solution = (res.get('refined_solution') + or res.get('coarse_solution') + or {}) + best_x_unit = self._best_full_params(best_solution) + if best_x_unit is not None: + best_x_phys = self.transform_to_vec(np.asarray(best_x_unit)) + print(f"{prefix} best-fit point (physical):", flush=True) + for name, val in zip(self.parameter_names, best_x_phys): + print(f"{prefix} {name}: {val}", flush=True) + print() + + + @staticmethod + def _best_full_params(exported_solution): + """Return the unit-hypercube parameter vector with the highest likelihood. + + Looks first in ``global_solution_pool`` (the capped pool of best + entries), then falls back to scanning ``solutions``. Returns None if + nothing is available. + """ + if not exported_solution: + return None + + pool = exported_solution.get('global_solution_pool') or [] + best_entry = None + best_fitness = -float('inf') + for entry in pool: + f = entry.get('fitness', -float('inf')) + if f > best_fitness: + best_fitness = f + best_entry = entry + if best_entry is not None and 'full_params' in best_entry: + return best_entry['full_params'] + + solutions = exported_solution.get('solutions') or {} + for sol in solutions.values(): + f = sol.get('likelihood', -float('inf')) + if f > best_fitness: + best_fitness = f + best_entry = sol + if best_entry is not None: + return best_entry.get('full_params') + return None + + +__plugins__ = {ParaProf.__plugin_name__: ParaProf} + diff --git a/cmake/python_scanners.cmake b/cmake/python_scanners.cmake index b0cfc0c300..e2a38dabe3 100644 --- a/cmake/python_scanners.cmake +++ b/cmake/python_scanners.cmake @@ -82,5 +82,5 @@ check_python_scanner_modules(scipy_shgo "scipy,numpy" "scipy,numpy") check_python_scanner_modules(scipy_minimize "scipy,numpy" "scipy,numpy") check_python_scanner_modules(reactive_ultranest "ultranest,numpy,packaging" "ultranest,numpy,packaging") check_python_scanner_modules(zeus "zeus,numpy" "zeus-mcmc,numpy") -check_python_scanner_modules(binminpy "binminpy,numpy,scipy,mpi4py" "binminpy,numpy,scipy,mpi4py") +check_python_scanner_modules(paraprof "paraprof,numpy,scipy,mpi4py" "paraprof,numpy,scipy,mpi4py") diff --git a/optional.txt b/optional.txt index d639b518c6..0f33ebe17a 100644 --- a/optional.txt +++ b/optional.txt @@ -12,9 +12,9 @@ zeus-mcmc numba pyhf pandas +paraprof @ git+https://github.com/anderkve/paraprof.git@main matplotlib tensorflow iminuit future -dill -binminpy @ git+https://github.com/anderkve/binminpy.git@main \ No newline at end of file +dill \ No newline at end of file diff --git a/yaml_files/spartan.yaml b/yaml_files/spartan.yaml index f65251d8c1..7ca2c1bf84 100644 --- a/yaml_files/spartan.yaml +++ b/yaml_files/spartan.yaml @@ -352,29 +352,34 @@ Scanner: min_num_live_points: 1000 dlogz: 0.5 - binminpy: + paraprof: like: LogLike - plugin: binminpy + plugin: paraprof run: - n_bins: - # For any parameters not listed under 'n_bins' binminpy will assume a single bin - NormalDist::mu: 100 - NormalDist::sigma: 100 - # sampled_parameters: ["NormalDist::mu", "NormalDist::sigma"] # By default parameters are optimized within each bin - sampler: "latinhypercube" - optimizer: "minimize" - optimizer_kwargs: - method: "L-BFGS-B" - tol: 1e-4 - n_initial_points: 10 - n_sampler_points_per_bin: 10 - # accept_loglike_above: -30.0 - accept_delta_loglike: 6.5 - neighborhood_distance: 2 - inherit_best_init_point_within_bin: False - n_optim_restarts_per_bin: 1 - n_tasks_per_batch: 1 - print_progress_every_n_batch: 100 + # List of profile likelihood projections to compute. + projections: + - dims: ["NormalDist::mu", "NormalDist::sigma"] + grid_points: [60, 60] + optimization_method: de + grid_refinement_factor: 2 + patch_refined_grid: true + - dims: ["NormalDist::mu"] + grid_points: [50] + roi_threshold: 4.0 + pop_per_grid_point: 3 + n_initial_optimizations: 40 + max_patching_waves: 10 + lbfgsb_max_iter: 20 + lbfgsb_polish: true + use_clustering: true + refinement_direct_eval: false + # samples_output_file: paraprof_samples.csv + # Paraprof-side diagnostic plots. + save_plots: true + plot_settings: + dpi: 300 + filetype: png + contour_levels: [-3.09, -1.15] ObsLikes: diff --git a/yaml_files/spartan_5d.yaml b/yaml_files/spartan_5d.yaml new file mode 100644 index 0000000000..4ceb19287c --- /dev/null +++ b/yaml_files/spartan_5d.yaml @@ -0,0 +1,127 @@ +########################################################################## +## GAMBIT configuration for a 5D test scan of the Rosenbrock function +## using the paraprof scanner. +## +## Uses the `trivial_5d` toy model and the `rosenbrock` log-likelihood +## from ObjectivesBit -- no physics modules, no backends. +## +## Run with (rebuild GAMBIT with -DWITH_MPI=True first): +## +## OMP_NUM_THREADS=1 mpiexec -np 3 ./gambit -rf yaml_files/spartan_5d.yaml +## +## The 2D paraprof projection over (x1, x2) profiles over (x3, x4, x5) +## at every grid point, which is what makes this a real test of paraprof's +## profiling machinery. +########################################################################## + + +Parameters: + trivial_5d: + x1: + range: [-3.0, 3.0] + x2: + range: [-3.0, 3.0] + x3: + range: [-3.0, 3.0] + x4: + range: [-3.0, 3.0] + x5: + range: [-3.0, 3.0] + + +Priors: + + # None needed: simple flat priors are specified in the 'Parameters' section above. + + +Printer: + + printer: hdf5 + options: + output_file: "results.hdf5" + group: "/data" + delete_file_on_restart: true + buffer_length: 1000 + + +Scanner: + + use_scanner: paraprof + + scanners: + + paraprof: + like: LogLike + plugin: paraprof + run: + # 2D (x1, x2) projection profiles over (x3, x4, x5). + # 1D (x1) projection profiles over (x2, x3, x4, x5). + projections: + - dims: ["trivial_5d::x1", "trivial_5d::x2"] + grid_points: [200, 200] + optimization_method: de + grid_refinement_factor: 1 + patch_refined_grid: false + - dims: ["trivial_5d::x2", "trivial_5d::x3"] + grid_points: [200, 200] + optimization_method: de + grid_refinement_factor: 1 + patch_refined_grid: false + # - dims: ["trivial_5d::x1"] + # grid_points: [80] + # Rosenbrock's curved valley is deep; keep the ROI generous. + roi_threshold: 10.0 + pop_per_grid_point: 3 + n_initial_optimizations: 40 + max_patching_waves: 15 + lbfgsb_max_iter: 50 + lbfgsb_polish: true + use_clustering: true + refinement_direct_eval: false + # Paraprof-side diagnostic plots. + save_plots: true + plot_settings: + dpi: 300 + filetype: png + contour_levels: [-3.09, -1.15] + + +ObsLikes: + + - purpose: LogLike + capability: rosenbrock + module: ObjectivesBit + type: double + + +Rules: + + +Logger: + + redirection: + [Default] : "default.log" + [Scanner] : "Scanner.log" + debug: false + + +KeyValues: + + debug: false + + default_output_path: "${PWD}/runs/spartan_5d" + + print_scanID: true + scanID: 1 + + rng: + generator: ranlux48 + seed: -1 + + print_timing_data: false + + print_unitcube: true + + likelihood: + model_invalid_for_lnlike_below: -1e6 + use_lnlike_modifier: identity