From 8539299dfcca4b9e09e1314c469133688f1dc4c4 Mon Sep 17 00:00:00 2001 From: Cen Wang Date: Tue, 10 Feb 2026 01:39:59 -0500 Subject: [PATCH 1/9] Run ruff --- simopt/bootstrap.py | 72 ++++++++++++++++++---------------- simopt/models/amusementpark.py | 5 ++- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/simopt/bootstrap.py b/simopt/bootstrap.py index 035cac3c..c28ee25f 100644 --- a/simopt/bootstrap.py +++ b/simopt/bootstrap.py @@ -321,41 +321,45 @@ def functional_of_curves( for curves in solver_1_curves ] ), - PlotType.DIFFERENCE_OF_CDF_SOLVABILITY: lambda: curve_utils.difference_of_curves( # noqa: E501 - curve_utils.mean_of_curves( - [ - curve_utils.cdf_of_curves_crossing_times( - curves, threshold=solve_tol - ) - for curves in solver_1_curves - ] - ), - curve_utils.mean_of_curves( - [ - curve_utils.cdf_of_curves_crossing_times( - curves, threshold=solve_tol - ) - for curves in solver_2_curves # type: ignore - ] - ), + PlotType.DIFFERENCE_OF_CDF_SOLVABILITY: lambda: ( + curve_utils.difference_of_curves( + curve_utils.mean_of_curves( + [ + curve_utils.cdf_of_curves_crossing_times( + curves, threshold=solve_tol + ) + for curves in solver_1_curves + ] + ), + curve_utils.mean_of_curves( + [ + curve_utils.cdf_of_curves_crossing_times( + curves, threshold=solve_tol + ) + for curves in solver_2_curves # type: ignore + ] + ), + ) ), - PlotType.DIFFERENCE_OF_QUANTILE_SOLVABILITY: lambda: curve_utils.difference_of_curves( # noqa: E501 - curve_utils.mean_of_curves( - [ - curve_utils.quantile_cross_jump( - curves, threshold=solve_tol, beta=beta - ) - for curves in solver_1_curves - ] - ), - curve_utils.mean_of_curves( - [ - curve_utils.quantile_cross_jump( - curves, threshold=solve_tol, beta=beta - ) - for curves in solver_2_curves # type: ignore - ] - ), + PlotType.DIFFERENCE_OF_QUANTILE_SOLVABILITY: lambda: ( + curve_utils.difference_of_curves( + curve_utils.mean_of_curves( + [ + curve_utils.quantile_cross_jump( + curves, threshold=solve_tol, beta=beta + ) + for curves in solver_1_curves + ] + ), + curve_utils.mean_of_curves( + [ + curve_utils.quantile_cross_jump( + curves, threshold=solve_tol, beta=beta + ) + for curves in solver_2_curves # type: ignore + ] + ), + ) ), PlotType.MEAN_FEASIBILITY_PROGRESS: lambda: curve_utils.mean_of_curves( single_curves diff --git a/simopt/models/amusementpark.py b/simopt/models/amusementpark.py index c7a08f95..c3339fd5 100644 --- a/simopt/models/amusementpark.py +++ b/simopt/models/amusementpark.py @@ -245,8 +245,9 @@ class AmusementParkMinDepartConfig(BaseModel): initial_solution: Annotated[ tuple[int, ...], Field( - default_factory=lambda: (PARK_CAPACITY - NUM_ATTRACTIONS + 1,) - + (1,) * (NUM_ATTRACTIONS - 1), + default_factory=lambda: ( + (PARK_CAPACITY - NUM_ATTRACTIONS + 1,) + (1,) * (NUM_ATTRACTIONS - 1) + ), description="Initial solution from which solvers start.", ), ] From bda6f12f5e42173c950d6607e12d8c7ba5eb663d Mon Sep 17 00:00:00 2001 From: Cen Wang Date: Thu, 5 Feb 2026 16:44:57 -0500 Subject: [PATCH 2/9] Avoid shared solver/problem state across macroreps via deepcopy --- simopt/experiment/run_solver.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/simopt/experiment/run_solver.py b/simopt/experiment/run_solver.py index 7f5dc087..b0d29129 100644 --- a/simopt/experiment/run_solver.py +++ b/simopt/experiment/run_solver.py @@ -2,6 +2,7 @@ import logging import time +from copy import deepcopy import pandas as pd from joblib import Parallel, delayed @@ -111,13 +112,17 @@ def run_solver( logging.info(f"Running solver {solver.name} on problem {problem.name}.") logging.debug("Starting macroreplications") + # TODO: Long-term fix is to make Solver/Problem immutable (stateless) + # so macroreps can share instances safely without deepcopy. if n_jobs == 1: results: list[tuple] = [ - _run_mrep(solver, problem, i) for i in range(n_macroreps) + _run_mrep(deepcopy(solver), deepcopy(problem), i) + for i in range(n_macroreps) ] else: results: list[tuple] = Parallel(n_jobs=n_jobs)( - delayed(_run_mrep)(solver, problem, i) for i in range(n_macroreps) + delayed(_run_mrep)(deepcopy(solver), deepcopy(problem), i) + for i in range(n_macroreps) ) dfs = [] From 4f854207945c43d7b3645b9584937ac5ae23b85b Mon Sep 17 00:00:00 2001 From: Cen Wang Date: Sat, 24 Jan 2026 17:47:07 -0500 Subject: [PATCH 3/9] Pin numpy upper bound in pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 55eb6542..268eeadc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ dependencies = [ "joblib>=1.5.2", "matplotlib>=3.10.7", "mrg32k3a[rust]>=2.0.0", - "numpy>=2.3.4", + "numpy>=2.3.4,<2.4", "pandas>=2.3.3", "pillow>=12.0.0", "pydantic>=2.12.3", From 6efb378e22d385ad69c9e4687e21f5095173caa8 Mon Sep 17 00:00:00 2001 From: Cen Wang Date: Thu, 22 Jan 2026 23:25:09 -0500 Subject: [PATCH 4/9] Add library colorama --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 268eeadc..22e3d4c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ classifiers = [ ] dependencies = [ "boltons>=25.0.0", + "colorama>=0.4.6", "cvxpy>=1.7.3", "joblib>=1.5.2", "matplotlib>=3.10.7", From ecabb5c5175665d48b26fdf0da507eb79368029b Mon Sep 17 00:00:00 2001 From: Cen Wang Date: Fri, 23 Jan 2026 01:25:18 -0500 Subject: [PATCH 5/9] Colorize existing test message --- scripts/generate_experiment_results.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/generate_experiment_results.py b/scripts/generate_experiment_results.py index 37fbf2cd..35df8535 100644 --- a/scripts/generate_experiment_results.py +++ b/scripts/generate_experiment_results.py @@ -5,6 +5,7 @@ from pathlib import Path import zstandard as zstd +from colorama import Fore, init # Append the parent directory (simopt package) to the system path sys.path.append(str(Path(__file__).resolve().parent.parent)) @@ -12,6 +13,8 @@ import simopt.directory as directory from simopt.experiment_base import ProblemSolver, post_normalize +init(autoreset=True) + # Workaround for AutoAPI problem_directory = directory.problem_directory solver_directory = directory.solver_directory @@ -24,6 +27,10 @@ EXPECTED_RESULTS_DIR = HOME_DIR / "test" / "expected_results" +def _color_text(text: str, color: str | int) -> str: + return color + text # type: ignore + + # Based off the similar function in simopt/experiment_base.py def is_compatible(problem_name: str, solver_name: str) -> bool: """Check if a solver is compatible with a problem. @@ -123,7 +130,7 @@ def main() -> None: results_filename = f"{file_problem_name}_{file_solver_name}.pickle.zst" # If file exists, skip it if results_filename in existing_results: - print(f"Test for {pair} already exists") + print(_color_text(f"Test for {pair} already exists", Fore.GREEN)) continue # If file doesn't exist, create it print(f"Creating test for {pair}") From c70da5d5de17e193620ee4d5a8e6e70dfd4d0908 Mon Sep 17 00:00:00 2001 From: Cen Wang Date: Fri, 23 Jan 2026 01:25:52 -0500 Subject: [PATCH 6/9] Skip tests by problem name --- scripts/generate_experiment_results.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/generate_experiment_results.py b/scripts/generate_experiment_results.py index 35df8535..74acae8c 100644 --- a/scripts/generate_experiment_results.py +++ b/scripts/generate_experiment_results.py @@ -107,6 +107,7 @@ def create_test(problem_name: str, solver_name: str) -> None: def main() -> None: """Create test cases for all compatible problem-solver pairs.""" + skip_problems = {} # Create a list of compatible problem-solver pairs compatible_pairs = [ (problem_name, solver_name) @@ -132,6 +133,9 @@ def main() -> None: if results_filename in existing_results: print(_color_text(f"Test for {pair} already exists", Fore.GREEN)) continue + if problem_name in skip_problems: + print(_color_text(f"Skipping test for {pair}", Fore.YELLOW)) + continue # If file doesn't exist, create it print(f"Creating test for {pair}") create_test(problem_name, solver_name) From 4e594a10c8d71d5d20bc7a2caabbacc58c995856 Mon Sep 17 00:00:00 2001 From: Cen Wang Date: Fri, 23 Jan 2026 17:20:36 -0500 Subject: [PATCH 7/9] Skip expect test for ERM-EXAMPLE-1 --- scripts/generate_experiment_results.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generate_experiment_results.py b/scripts/generate_experiment_results.py index 74acae8c..9557fa05 100644 --- a/scripts/generate_experiment_results.py +++ b/scripts/generate_experiment_results.py @@ -107,7 +107,7 @@ def create_test(problem_name: str, solver_name: str) -> None: def main() -> None: """Create test cases for all compatible problem-solver pairs.""" - skip_problems = {} + skip_problems = {"ERM-EXAMPLE-1"} # Create a list of compatible problem-solver pairs compatible_pairs = [ (problem_name, solver_name) From b4989df7a7a9c8100159a6609022986eac145252 Mon Sep 17 00:00:00 2001 From: Cen Wang Date: Thu, 22 Jan 2026 23:09:37 -0500 Subject: [PATCH 8/9] Add discrete example problem --- simopt/models/example.py | 123 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/simopt/models/example.py b/simopt/models/example.py index e3a982ae..daf33f1d 100644 --- a/simopt/models/example.py +++ b/simopt/models/example.py @@ -167,3 +167,126 @@ def get_random_solution(self, rand_sol_rng: MRG32k3a) -> tuple: # noqa: D102 factorized=False, ) ) + + +class Example2ModelConfig(BaseModel): + """Configuration model for Example-2 simulation. + + A model that is a deterministic quadratic function evaluated with noise. + """ + + x: Annotated[ + tuple[int, ...], + Field( + default=(0, 0, 0, 0), + description="point to evaluate", + ), + ] + + +class Example2ProblemConfig(BaseModel): + """Configuration model for Example-2 Problem. + + Base class to implement simulation-optimization problems. + """ + + initial_solution: Annotated[ + tuple[int, ...], + Field( + default=(0, 0, 0, 0), + description="initial solution", + ), + ] + budget: Annotated[ + int, + Field( + default=1000, + description="max # of replications for a solver to take", + gt=0, + json_schema_extra={"isDatafarmable": False}, + ), + ] + + +class Example2Model(Model): + """A model that is a deterministic quadratic function evaluated with noise.""" + + class_name_abbr: ClassVar[str] = "EXAMPLE-2-MODEL" + class_name: ClassVar[str] = "Quadratic Function + Noise (Discrete)" + config_class: ClassVar[type[BaseModel]] = Example2ModelConfig + n_rngs: ClassVar[int] = 1 + n_responses: ClassVar[int] = 1 + + def __init__(self, fixed_factors: dict | None = None) -> None: + """Initialize the model. + + Args: + fixed_factors (dict | None): fixed factors of the model. + If None, use default values. + """ + # Let the base class handle default arguments. + super().__init__(fixed_factors) + self.noise_model = Normal() + + def before_replicate(self, rng_list: list[MRG32k3a]) -> None: # noqa: D102 + self.noise_model.set_rng(rng_list[0]) + + def replicate(self) -> tuple[dict, dict]: + """Evaluate a quadratic function f(x) with stochastic noise.""" + x = np.array(self.factors["x"]) + target = np.array([1, 2, 3, 4]) + fn_eval_at_x = np.sum((x - target) ** 2) + self.noise_model.random() + + responses = {"est_f(x)": fn_eval_at_x} + return responses, {} + + +class Example2Problem(Problem): + """Discrete quadratic minimization example with noise.""" + + class_name_abbr: ClassVar[str] = "EXAMPLE-2" + class_name: ClassVar[str] = "Min Quadratic Function + Noise (Discrete)" + config_class: ClassVar[type[BaseModel]] = Example2ProblemConfig + model_class: ClassVar[type[Model]] = Example2Model + n_objectives: ClassVar[int] = 1 + n_stochastic_constraints: ClassVar[int] = 0 + minmax: ClassVar[tuple[int, ...]] = (-1,) + constraint_type: ClassVar[ConstraintType] = ConstraintType.UNCONSTRAINED + variable_type: ClassVar[VariableType] = VariableType.DISCRETE + gradient_available: ClassVar[bool] = False + model_default_factors: ClassVar[dict] = {} + model_decision_factors: ClassVar[set[str]] = {"x"} + + @property + def optimal_value(self) -> float | None: # noqa: D102 + return 0.0 + + @property + def optimal_solution(self) -> tuple | None: # noqa: D102 + return (1, 2, 3, 4) + + @property + def dim(self) -> int: # noqa: D102 + return 4 + + @property + def lower_bounds(self) -> tuple: # noqa: D102 + return (-4,) * self.dim + + @property + def upper_bounds(self) -> tuple: # noqa: D102 + return (4,) * self.dim + + def vector_to_factor_dict(self, vector: tuple) -> dict: # noqa: D102 + return {"x": vector[:]} + + def factor_dict_to_vector(self, factor_dict: dict) -> tuple: # noqa: D102 + return tuple(factor_dict["x"]) + + def replicate(self, _x: tuple) -> RepResult: # noqa: D102 + responses, _ = self.model.replicate() + objectives = [Objective(stochastic=responses["est_f(x)"])] + return RepResult(objectives=objectives) + + def get_random_solution(self, rand_sol_rng: MRG32k3a) -> tuple: # noqa: D102 + return tuple(rand_sol_rng.randint(-4, 4) for _ in range(self.dim)) From 767c5de76e5c097bbb4cb0fc2d8490acc430cc9b Mon Sep 17 00:00:00 2001 From: Cen Wang Date: Sat, 24 Jan 2026 14:16:22 -0500 Subject: [PATCH 9/9] Add expect test data for EXAMPLE-2 problem --- .../EXAMPLE2_RNDSRCH.pickle.zst | Bin 0 -> 25592 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/expected_results/EXAMPLE2_RNDSRCH.pickle.zst diff --git a/test/expected_results/EXAMPLE2_RNDSRCH.pickle.zst b/test/expected_results/EXAMPLE2_RNDSRCH.pickle.zst new file mode 100644 index 0000000000000000000000000000000000000000..9d501381b526c7a7d49239ed5ed46fac337bb274 GIT binary patch literal 25592 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 Zs2R9xv2k^=5|ja5G#bx+eng;}`+sC4e%=58 literal 0 HcmV?d00001