diff --git a/examples/bioma/fig4.py b/examples/bioma/fig4.py index 2d96d7d..6b9a1d7 100644 --- a/examples/bioma/fig4.py +++ b/examples/bioma/fig4.py @@ -3,7 +3,7 @@ import matplotlib.pyplot as plt from problems import ackley4, griewank, schwefel2_26 -from utils import IMAGES_DIR, FunctionConfig, build_cmlon +from utils import IMAGES_DIR, FunctionConfig, build_cmlon, max_workers_from_env from lonkit import LONVisualizer @@ -108,7 +108,7 @@ def main() -> None: Path(IMAGES_DIR).mkdir(parents=True, exist_ok=True) func_names = list(FUNCTIONS.keys()) - with ProcessPoolExecutor() as executor: + with ProcessPoolExecutor(max_workers=max_workers_from_env()) as executor: futures = { func_name: executor.submit(build_cmlon, FUNCTIONS[func_name], N_VAR) for func_name in func_names diff --git a/examples/bioma/utils.py b/examples/bioma/utils.py index 8dbacdc..77592a8 100644 --- a/examples/bioma/utils.py +++ b/examples/bioma/utils.py @@ -1,20 +1,31 @@ from __future__ import annotations +import importlib.util from concurrent.futures import ProcessPoolExecutor from dataclasses import dataclass, field +from pathlib import Path from typing import TYPE_CHECKING import matplotlib.pyplot as plt from matplotlib.lines import Line2D +from lonkit import CMLON, BasinHoppingSampler, BasinHoppingSamplerConfig, LONVisualizer +from lonkit.visualization import COLORS + if TYPE_CHECKING: from collections.abc import Callable - from pathlib import Path import numpy as np -from lonkit import CMLON, BasinHoppingSampler, BasinHoppingSamplerConfig, LONVisualizer -from lonkit.visualization import COLORS +# examples/ is not a package; load shared helpers by path. +_spec = importlib.util.spec_from_file_location( + "example_utils", Path(__file__).resolve().parents[1] / "example_utils.py" +) +assert _spec is not None and _spec.loader is not None +_example_utils = importlib.util.module_from_spec(_spec) +_spec.loader.exec_module(_example_utils) +max_workers_from_env = _example_utils.max_workers_from_env + DEFAULT_N_RUNS = 100 DEFAULT_FITNESS_PRECISION = 2 @@ -95,7 +106,7 @@ def build_all( ] results: dict[tuple[str, int], tuple[CMLON, dict]] = {} - with ProcessPoolExecutor() as executor: + with ProcessPoolExecutor(max_workers=max_workers_from_env()) as executor: futures = [executor.submit(_build_one, *t) for t in tasks] for future in futures: func_name, n_var, cmlon, metrics = future.result() diff --git a/examples/example_utils.py b/examples/example_utils.py new file mode 100644 index 0000000..f8b7b30 --- /dev/null +++ b/examples/example_utils.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +import os + +LONKIT_N_JOBS_ENV = "LONKIT_N_JOBS" + + +def max_workers_from_env() -> int | None: + value = os.environ.get(LONKIT_N_JOBS_ENV) + return int(value) if value is not None else None diff --git a/examples/number_partitioning/plot_metrics.py b/examples/number_partitioning/plot_metrics.py index 2e487bd..521732b 100644 --- a/examples/number_partitioning/plot_metrics.py +++ b/examples/number_partitioning/plot_metrics.py @@ -21,7 +21,7 @@ Outputs one figure: ``NPP_phase_transition_sweep.png`` """ -import os +import importlib.util from concurrent.futures import ProcessPoolExecutor from pathlib import Path @@ -34,6 +34,16 @@ from lonkit import ILSSampler, ILSSamplerConfig, LONConfig, NumberPartitioning +# examples/ is not a package; load shared helpers by path. +_spec = importlib.util.spec_from_file_location( + "example_utils", Path(__file__).resolve().parents[1] / "example_utils.py" +) +assert _spec is not None and _spec.loader is not None +_example_utils = importlib.util.module_from_spec(_spec) +_spec.loader.exec_module(_example_utils) +max_workers_from_env = _example_utils.max_workers_from_env + + SMOOTH_WIDTH = 3 # uniform moving-average window (points); odd integer @@ -226,8 +236,7 @@ def _plot_derivative_ax(ax): EQ_ATOL = 1e-8 K_VALUES = np.linspace(0.1, 1.0, 50) -# Cap on worker processes, so the sweep behaves on shared machines. -N_JOBS = int(os.environ.get("LONKIT_N_JOBS", "8")) +N_JOBS = max_workers_from_env() def sweep_one(k: float) -> dict: @@ -261,10 +270,8 @@ def sweep_one(k: float) -> dict: def main(): Path(IMAGES_DIR).mkdir(parents=True, exist_ok=True) - print( - f"Sweeping k across {len(K_VALUES)} values (n={N}, n_runs={N_RUNS}, " - f"{N_JOBS} worker processes)\n" - ) + workers = f"{N_JOBS} worker processes" if N_JOBS is not None else "default workers" + print(f"Sweeping k across {len(K_VALUES)} values (n={N}, n_runs={N_RUNS}, " f"{workers})\n") with ProcessPoolExecutor(max_workers=N_JOBS) as executor: records = list(executor.map(sweep_one, K_VALUES)) diff --git a/images/fig1.png b/images/fig1.png index 76a9f84..1bcaf08 100644 Binary files a/images/fig1.png and b/images/fig1.png differ diff --git a/images/fig2.png b/images/fig2.png index cbdb2f7..8bd937e 100644 Binary files a/images/fig2.png and b/images/fig2.png differ diff --git a/images/fig3.png b/images/fig3.png index 433752f..c14d378 100644 Binary files a/images/fig3.png and b/images/fig3.png differ diff --git a/images/fig4.png b/images/fig4.png index d6d12ef..b5de183 100644 Binary files a/images/fig4.png and b/images/fig4.png differ diff --git a/images/fig4_Ackley_4.png b/images/fig4_Ackley_4.png index cdb1ccc..8a17ef0 100644 Binary files a/images/fig4_Ackley_4.png and b/images/fig4_Ackley_4.png differ diff --git a/images/fig4_Griewank.png b/images/fig4_Griewank.png index 5d1915e..eb3af34 100644 Binary files a/images/fig4_Griewank.png and b/images/fig4_Griewank.png differ diff --git a/images/fig4_Schwefel_2.26.png b/images/fig4_Schwefel_2.26.png index 0b90843..331879d 100644 Binary files a/images/fig4_Schwefel_2.26.png and b/images/fig4_Schwefel_2.26.png differ diff --git a/images/fig5.png b/images/fig5.png index 0b1bcaa..182198e 100644 Binary files a/images/fig5.png and b/images/fig5.png differ diff --git a/images/fig6.png b/images/fig6.png index fff2dde..525bc7c 100644 Binary files a/images/fig6.png and b/images/fig6.png differ diff --git a/images/number_partitioning/NPP_0.3_2d.png b/images/number_partitioning/NPP_0.3_2d.png index 76ae777..3daaf48 100644 Binary files a/images/number_partitioning/NPP_0.3_2d.png and b/images/number_partitioning/NPP_0.3_2d.png differ diff --git a/images/number_partitioning/NPP_0.3_3d.png b/images/number_partitioning/NPP_0.3_3d.png index 8cfcd12..9ff438f 100644 Binary files a/images/number_partitioning/NPP_0.3_3d.png and b/images/number_partitioning/NPP_0.3_3d.png differ diff --git a/images/number_partitioning/NPP_0.7_2d.png b/images/number_partitioning/NPP_0.7_2d.png index 45d1e35..677df4c 100644 Binary files a/images/number_partitioning/NPP_0.7_2d.png and b/images/number_partitioning/NPP_0.7_2d.png differ diff --git a/images/number_partitioning/NPP_0.7_3d.png b/images/number_partitioning/NPP_0.7_3d.png index 0f9f517..ef5e050 100644 Binary files a/images/number_partitioning/NPP_0.7_3d.png and b/images/number_partitioning/NPP_0.7_3d.png differ diff --git a/images/number_partitioning/NPP_0.95_2d.png b/images/number_partitioning/NPP_0.95_2d.png index 890cf95..41432b5 100644 Binary files a/images/number_partitioning/NPP_0.95_2d.png and b/images/number_partitioning/NPP_0.95_2d.png differ diff --git a/images/number_partitioning/NPP_0.95_3d.png b/images/number_partitioning/NPP_0.95_3d.png index db6c082..daa1fbb 100644 Binary files a/images/number_partitioning/NPP_0.95_3d.png and b/images/number_partitioning/NPP_0.95_3d.png differ diff --git a/images/number_partitioning/NPP_merged_cmlon_views.png b/images/number_partitioning/NPP_merged_cmlon_views.png index eef76dc..14cfdfb 100644 Binary files a/images/number_partitioning/NPP_merged_cmlon_views.png and b/images/number_partitioning/NPP_merged_cmlon_views.png differ diff --git a/images/number_partitioning/NPP_phase_transition_sweep.png b/images/number_partitioning/NPP_phase_transition_sweep.png index 0297366..73664b9 100644 Binary files a/images/number_partitioning/NPP_phase_transition_sweep.png and b/images/number_partitioning/NPP_phase_transition_sweep.png differ