Warning, /AID2E-framework/examples/optimizers/starter_kit.ipynb is written in an unsupported language. File is not indexed.
0001 {
0002 "cells": [
0003 {
0004 "cell_type": "markdown",
0005 "id": "titlecell",
0006 "metadata": {},
0007 "source": [
0008 "# Getting started with Optimization\n"
0009 ]
0010 },
0011 {
0012 "cell_type": "markdown",
0013 "id": "overviewmd",
0014 "metadata": {},
0015 "source": [
0016 "This notebook starts with a tiny local DTLZ2 evaluator, then shows the common AID2E optimizer interface, and finally branches into Ax and PyMOO-specific workflows. The last section moves away from YAML and defines optimizer settings inline so the same ideas can be reused interactively.\n"
0017 ]
0018 },
0019 {
0020 "cell_type": "code",
0021 "execution_count": 12,
0022 "id": "setupcode",
0023 "metadata": {},
0024 "outputs": [
0025 {
0026 "name": "stdout",
0027 "output_type": "stream",
0028 "text": [
0029 "Notebook directory: /sciclone/scr10/ksuresh/AID2E-framework/examples/optimizers\n",
0030 "Ax config: dtlz2_ax_optimizer_only.yml\n",
0031 "PyMOO config: dtlz2_pymoo_optimizer_only.yml\n"
0032 ]
0033 }
0034 ],
0035 "source": [
0036 "from __future__ import annotations\n",
0037 "\n",
0038 "import math\n",
0039 "from pathlib import Path\n",
0040 "from pprint import pprint\n",
0041 "\n",
0042 "from aid2e.utilities import build_optimizer_from_config\n",
0043 "from aid2e.utilities.configurations import load_config\n",
0044 "from aid2e.utilities.configurations.optimizer_config import OptimizerConfiguration\n",
0045 "\n",
0046 "NOTEBOOK_DIR = Path.cwd().resolve()\n",
0047 "if not (NOTEBOOK_DIR / \"dtlz2_ax_optimizer_only.yml\").exists():\n",
0048 " candidate = NOTEBOOK_DIR / \"examples\" / \"optimizers\"\n",
0049 " if candidate.exists():\n",
0050 " NOTEBOOK_DIR = candidate.resolve()\n",
0051 " else:\n",
0052 " raise FileNotFoundError(\n",
0053 " \"Run this notebook from examples/optimizers or from the repository root.\"\n",
0054 " )\n",
0055 "\n",
0056 "AX_CONFIG_PATH = NOTEBOOK_DIR / \"dtlz2_ax_optimizer_only.yml\"\n",
0057 "PYMOO_CONFIG_PATH = NOTEBOOK_DIR / \"dtlz2_pymoo_optimizer_only.yml\"\n",
0058 "\n",
0059 "ax_full_config = load_config(str(AX_CONFIG_PATH))\n",
0060 "pymoo_full_config = load_config(str(PYMOO_CONFIG_PATH))\n",
0061 "\n",
0062 "print(\"Notebook directory:\", NOTEBOOK_DIR)\n",
0063 "print(\"Ax config:\", AX_CONFIG_PATH.name)\n",
0064 "print(\"PyMOO config:\", PYMOO_CONFIG_PATH.name)\n"
0065 ]
0066 },
0067 {
0068 "cell_type": "markdown",
0069 "id": "dtlzmd",
0070 "metadata": {},
0071 "source": [
0072 "We keep the evaluator local on purpose. That way the notebook focuses on the optimizer contract instead of workflow orchestration.\n",
0073 "\n",
0074 "In this notebook, the local evaluator uses the **DTLZ2** benchmark problem, a standard multi-objective test function designed to check whether an optimizer can recover a smooth **Pareto front**.\n",
0075 "\n",
0076 "For a problem with $m$ objectives and $n$ decision variables, let the decision vector be\n",
0077 "\n",
0078 "$$\n",
0079 "\\mathbf{x} = (x_1, x_2, \\dots, x_n), \\qquad 0 \\le x_i \\le 1.\n",
0080 "$$\n",
0081 "\n",
0082 "DTLZ2 defines\n",
0083 "\n",
0084 "$$\n",
0085 "g(\\mathbf{x}_m) = \\sum_{i=m}^{n} (x_i - 0.5)^2,\n",
0086 "$$\n",
0087 "\n",
0088 "where $\\mathbf{x}_m$ denotes the tail of the vector used in the distance term. The objectives are then\n",
0089 "\n",
0090 "$$\n",
0091 "f_j(\\mathbf{x}) = (1 + g(\\mathbf{x}_m))\n",
0092 "\\left(\n",
0093 "\\prod_{i=1}^{m-j} \\cos\\left(\\frac{\\pi}{2} x_i\\right)\n",
0094 "\\right)\n",
0095 "$$\n",
0096 "\n",
0097 "for $j = 1, \\dots, m$$, with the final sine factor applied in the standard DTLZ2 construction:\n",
0098 "\n",
0099 "$$\n",
0100 "f_j(\\mathbf{x}) =\n",
0101 "(1 + g(\\mathbf{x}_m))\n",
0102 "\\left(\n",
0103 "\\prod_{i=1}^{m-j} \\cos\\left(\\frac{\\pi}{2} x_i\\right)\n",
0104 "\\right)\n",
0105 "\\sin\\left(\\frac{\\pi}{2} x_{m-j+1}\\right)\n",
0106 "\\quad \\text{for } j > 1.\n",
0107 "$$\n",
0108 "\n",
0109 "For the **two-objective** case used here, this simplifies to\n",
0110 "\n",
0111 "$$\n",
0112 "g = \\sum_{i=2}^{n} (x_i - 0.5)^2,\n",
0113 "$$\n",
0114 "\n",
0115 "$$\n",
0116 "f_1 = (1 + g)\\cos\\left(\\frac{\\pi}{2}x_1\\right),\n",
0117 "\\qquad\n",
0118 "f_2 = (1 + g)\\sin\\left(\\frac{\\pi}{2}x_1\\right).\n",
0119 "$$\n",
0120 "\n",
0121 "In this notebook, $$n = 5$$, so the evaluator uses\n",
0122 "\n",
0123 "$$\n",
0124 "g = (x_2 - 0.5)^2 + (x_3 - 0.5)^2 + (x_4 - 0.5)^2 + (x_5 - 0.5)^2.\n",
0125 "$$\n",
0126 "\n",
0127 "The optimization goal is to **minimize both** $$f_1$$ and $$f_2$$. When $$g = 0$$, the solutions lie on the Pareto-optimal front, which for the two-objective case forms a quarter of the unit circle:\n",
0128 "\n",
0129 "$$\n",
0130 "f_1^2 + f_2^2 = 1, \\qquad f_1 \\ge 0,\\; f_2 \\ge 0.\n",
0131 "$$\n",
0132 "\n",
0133 "That makes DTLZ2 a useful example because it has a simple analytical form while still testing multi-objective behavior clearly."
0134 ]
0135 },
0136 {
0137 "cell_type": "code",
0138 "execution_count": 36,
0139 "id": "helpercode",
0140 "metadata": {},
0141 "outputs": [],
0142 "source": [
0143 "def dtlz2_objectives(parameters: dict[str, float]) -> dict[str, float]:\n",
0144 " \"\"\" \n",
0145 " Compute the two-objective DTLZ2 function from a parameter dict.\n",
0146 " Remember that the parameters dictonary will always have keys in the format DTLZ2_variables.x{N}, Refer the design.params.\n",
0147 " \"\"\"\n",
0148 " x1 = float(parameters[\"DTLZ2_variables.x1\"])\n",
0149 " tail = [\n",
0150 " float(parameters[\"DTLZ2_variables.x2\"]),\n",
0151 " float(parameters[\"DTLZ2_variables.x3\"]),\n",
0152 " float(parameters[\"DTLZ2_variables.x4\"]),\n",
0153 " float(parameters[\"DTLZ2_variables.x5\"]),\n",
0154 " ]\n",
0155 " g = sum((value - 0.5) ** 2 for value in tail)\n",
0156 " factor = 1.0 + g\n",
0157 " f1 = factor * math.cos(x1 * math.pi / 2.0)\n",
0158 " f2 = factor * math.sin(x1 * math.pi / 2.0)\n",
0159 " return {\"f1\": float(f1), \"f2\": float(f2)}\n",
0160 "\n",
0161 "\n",
0162 "def evaluate_candidates(optimizer, candidates: list[dict[str, float]], phase: str):\n",
0163 " \"\"\"Evaluate suggested candidates locally and push results back into the optimizer.\"\"\"\n",
0164 " start_index = len(optimizer.get_trials()) - len(candidates)\n",
0165 " records = []\n",
0166 " for offset, parameters in enumerate(candidates):\n",
0167 " trial_index = start_index + offset\n",
0168 " metrics = dtlz2_objectives(parameters)\n",
0169 " optimizer.update_with_results(\n",
0170 " trial_index=trial_index,\n",
0171 " parameters=parameters,\n",
0172 " metrics=metrics,\n",
0173 " )\n",
0174 " _record = {\n",
0175 " \"trial_index\": trial_index,\n",
0176 " \"phase\": phase,\n",
0177 " } | parameters | metrics\n",
0178 " records.append(_record)\n",
0179 " return records\n",
0180 "\n",
0181 "\n",
0182 "def summarize_optimizer(optimizer, label: str) -> dict[str, int | str]:\n",
0183 " results = optimizer.get_optimization_results()\n",
0184 " pareto_front = optimizer.get_pareto_front()\n",
0185 " return {\n",
0186 " \"label\": label,\n",
0187 " \"n_trials\": results[\"n_trials\"],\n",
0188 " \"pareto_points\": len(pareto_front),\n",
0189 " }\n"
0190 ]
0191 },
0192 {
0193 "cell_type": "markdown",
0194 "id": "f26b3ece",
0195 "metadata": {},
0196 "source": [
0197 "Lets quickly check by providing a dummy input. To make sure everything works"
0198 ]
0199 },
0200 {
0201 "cell_type": "code",
0202 "execution_count": 37,
0203 "id": "dtlzdemo",
0204 "metadata": {},
0205 "outputs": [
0206 {
0207 "name": "stdout",
0208 "output_type": "stream",
0209 "text": [
0210 "DTLZ2 objectives:\n",
0211 "{'f1': 0.9816220032932421, 'f2': 0.4066011468879079}\n"
0212 ]
0213 }
0214 ],
0215 "source": [
0216 "\n",
0217 "design_cfg = ax_full_config.problem.design_config\n",
0218 "\n",
0219 "demo_parameters = {\n",
0220 " name: 0.5\n",
0221 " for name in design_cfg.get_parameter_names()\n",
0222 "}\n",
0223 "\n",
0224 "demo_parameters.update(\n",
0225 " dict(\n",
0226 " zip(\n",
0227 " sorted(demo_parameters),\n",
0228 " [0.25, 0.50, 0.75, 0.50, 0.50],\n",
0229 " strict=False,\n",
0230 " )\n",
0231 " )\n",
0232 ")\n",
0233 "\n",
0234 "print(\"DTLZ2 objectives:\")\n",
0235 "pprint(dtlz2_objectives(demo_parameters))\n"
0236 ]
0237 },
0238 {
0239 "cell_type": "markdown",
0240 "id": "genericmd",
0241 "metadata": {},
0242 "source": [
0243 "The common optimizer contract is the same across backends: load config, build the optimizer, call `suggest_candidates(...)`, evaluate locally, then return metrics through `update_with_results(...)`.\n"
0244 ]
0245 },
0246 {
0247 "cell_type": "code",
0248 "execution_count": 38,
0249 "id": "inspectconfig",
0250 "metadata": {},
0251 "outputs": [
0252 {
0253 "name": "stdout",
0254 "output_type": "stream",
0255 "text": [
0256 "Problem: DTLZ2 Optimizer-Only Ax Example\n",
0257 "Objectives: ['f1', 'f2']\n",
0258 "Design parameters: ['DTLZ2_variables.x1', 'DTLZ2_variables.x2', 'DTLZ2_variables.x3', 'DTLZ2_variables.x4', 'DTLZ2_variables.x5']\n",
0259 "Optimizer section:\n",
0260 "{'name': 'ax',\n",
0261 " 'parameters': {'batch_size': 2,\n",
0262 " 'generator': 'BOTORCH_MODULAR',\n",
0263 " 'generator_gen_kwargs': {'model_gen_options': {'optimizer_kwargs': {'num_restarts': 10,\n",
0264 " 'sequential': False}}},\n",
0265 " 'generator_kwargs': {'acquisition_options': {'prune_baseline': True},\n",
0266 " 'botorch_acqf_class': 'qLogNoisyExpectedHypervolumeImprovement'},\n",
0267 " 'initialization_strategy': 'sobol',\n",
0268 " 'n_initial_samples': 4,\n",
0269 " 'n_iterations': 3,\n",
0270 " 'objective_thresholds': {'f1': 1.0, 'f2': 1.0},\n",
0271 " 'seed': 42},\n",
0272 " 'type': 'bayesian'}\n"
0273 ]
0274 }
0275 ],
0276 "source": [
0277 "generic_problem = ax_full_config.problem\n",
0278 "generic_optimizer_cfg = ax_full_config.optimizer\n",
0279 "\n",
0280 "print(\"Problem:\", generic_problem.name)\n",
0281 "print(\"Objectives:\", [objective.name for objective in generic_problem.objectives])\n",
0282 "print(\"Design parameters:\", generic_problem.design_config.get_parameter_names())\n",
0283 "print(\"Optimizer section:\")\n",
0284 "pprint(generic_optimizer_cfg.model_dump())\n"
0285 ]
0286 },
0287 {
0288 "cell_type": "code",
0289 "execution_count": 39,
0290 "id": "genericloop",
0291 "metadata": {},
0292 "outputs": [
0293 {
0294 "name": "stdout",
0295 "output_type": "stream",
0296 "text": [
0297 "Recorded trials from the generic loop:\n",
0298 "[{'DTLZ2_variables.x1': 0.9975133538246155,\n",
0299 " 'DTLZ2_variables.x2': 0.10436639189720154,\n",
0300 " 'DTLZ2_variables.x3': 0.8229788541793823,\n",
0301 " 'DTLZ2_variables.x4': 0.4194321632385254,\n",
0302 " 'DTLZ2_variables.x5': 0.5283221006393433,\n",
0303 " 'f1': 0.004953339804529975,\n",
0304 " 'f2': 1.268124935891223,\n",
0305 " 'phase': 'generic-step-1',\n",
0306 " 'trial_index': 0},\n",
0307 " {'DTLZ2_variables.x1': 0.39916136860847473,\n",
0308 " 'DTLZ2_variables.x2': 0.5109770465642214,\n",
0309 " 'DTLZ2_variables.x3': 0.3220283752307296,\n",
0310 " 'DTLZ2_variables.x4': 0.5257955053821206,\n",
0311 " 'DTLZ2_variables.x5': 0.4753276174888015,\n",
0312 " 'f1': 0.8365691769151665,\n",
0313 " 'f2': 0.6061209438136624,\n",
0314 " 'phase': 'generic-step-2',\n",
0315 " 'trial_index': 1}]\n",
0316 "Generic loop summary:\n",
0317 "{'label': 'generic-ax-demo', 'n_trials': 2, 'pareto_points': 2}\n"
0318 ]
0319 }
0320 ],
0321 "source": [
0322 "generic_optimizer = build_optimizer_from_config(generic_problem, generic_optimizer_cfg)\n",
0323 "generic_records = []\n",
0324 "\n",
0325 "for step in range(2):\n",
0326 " candidates = generic_optimizer.suggest_candidates(n_candidates=1)\n",
0327 " generic_records.extend(\n",
0328 " evaluate_candidates(generic_optimizer, candidates, phase=f\"generic-step-{step + 1}\")\n",
0329 " )\n",
0330 "\n",
0331 "print(\"Recorded trials from the generic loop:\")\n",
0332 "pprint(generic_records)\n",
0333 "print(\"Generic loop summary:\")\n",
0334 "pprint(summarize_optimizer(generic_optimizer, \"generic-ax-demo\"))\n"
0335 ]
0336 },
0337 {
0338 "cell_type": "markdown",
0339 "id": "axmd",
0340 "metadata": {},
0341 "source": [
0342 "Now we follow the full Ax optimizer-only example. The loop uses an explicit initialization phase and then a model-based phase.\n"
0343 ]
0344 },
0345 {
0346 "cell_type": "code",
0347 "execution_count": 40,
0348 "id": "axrun",
0349 "metadata": {},
0350 "outputs": [
0351 {
0352 "name": "stderr",
0353 "output_type": "stream",
0354 "text": [
0355 "/sciclone/home/ksuresh/.conda/envs/env_AID2E/lib/python3.11/site-packages/gpytorch/likelihoods/noise_models.py:150: NumericalWarning: Very small noise values detected. This will likely lead to numerical instabilities. Rounding small noise values up to 1e-06.\n",
0356 " warnings.warn(\n",
0357 "/sciclone/home/ksuresh/.conda/envs/env_AID2E/lib/python3.11/site-packages/gpytorch/likelihoods/noise_models.py:150: NumericalWarning: Very small noise values detected. This will likely lead to numerical instabilities. Rounding small noise values up to 1e-06.\n",
0358 " warnings.warn(\n",
0359 "/sciclone/home/ksuresh/.conda/envs/env_AID2E/lib/python3.11/site-packages/gpytorch/likelihoods/noise_models.py:150: NumericalWarning: Very small noise values detected. This will likely lead to numerical instabilities. Rounding small noise values up to 1e-06.\n",
0360 " warnings.warn(\n"
0361 ]
0362 },
0363 {
0364 "name": "stdout",
0365 "output_type": "stream",
0366 "text": [
0367 "First three Ax records:\n",
0368 "[{'DTLZ2_variables.x1': 0.9975133538246155,\n",
0369 " 'DTLZ2_variables.x2': 0.10436639189720154,\n",
0370 " 'DTLZ2_variables.x3': 0.8229788541793823,\n",
0371 " 'DTLZ2_variables.x4': 0.4194321632385254,\n",
0372 " 'DTLZ2_variables.x5': 0.5283221006393433,\n",
0373 " 'f1': 0.004953339804529975,\n",
0374 " 'f2': 1.268124935891223,\n",
0375 " 'phase': 'init',\n",
0376 " 'trial_index': 0},\n",
0377 " {'DTLZ2_variables.x1': 0.39916136860847473,\n",
0378 " 'DTLZ2_variables.x2': 0.5109770465642214,\n",
0379 " 'DTLZ2_variables.x3': 0.3220283752307296,\n",
0380 " 'DTLZ2_variables.x4': 0.5257955053821206,\n",
0381 " 'DTLZ2_variables.x5': 0.4753276174888015,\n",
0382 " 'f1': 0.8365691769151665,\n",
0383 " 'f2': 0.6061209438136624,\n",
0384 " 'phase': 'init',\n",
0385 " 'trial_index': 1},\n",
0386 " {'DTLZ2_variables.x1': 0.18095120228827,\n",
0387 " 'DTLZ2_variables.x2': 0.36145188845694065,\n",
0388 " 'DTLZ2_variables.x3': 0.5728367641568184,\n",
0389 " 'DTLZ2_variables.x4': 0.14431990031152964,\n",
0390 " 'DTLZ2_variables.x5': 0.22098449897021055,\n",
0391 " 'f1': 1.1795517337847434,\n",
0392 " 'f2': 0.3446034690561871,\n",
0393 " 'phase': 'init',\n",
0394 " 'trial_index': 2}]\n",
0395 "Total Ax records: 10\n"
0396 ]
0397 }
0398 ],
0399 "source": [
0400 "def run_ax_optimization(full_config):\n",
0401 " optimizer_config = full_config.optimizer.parse_algorithm_params()\n",
0402 " optimizer = build_optimizer_from_config(full_config.problem, full_config.optimizer)\n",
0403 " records = []\n",
0404 "\n",
0405 " remaining_init = optimizer_config.n_initial_samples\n",
0406 " while remaining_init > 0:\n",
0407 " current_batch = min(optimizer_config.batch_size, remaining_init)\n",
0408 " candidates = optimizer.suggest_candidates(n_candidates=current_batch)\n",
0409 " records.extend(evaluate_candidates(optimizer, candidates, phase=\"init\"))\n",
0410 " remaining_init -= current_batch\n",
0411 "\n",
0412 " for iteration in range(optimizer_config.n_iterations):\n",
0413 " candidates = optimizer.suggest_candidates(\n",
0414 " n_candidates=optimizer_config.batch_size\n",
0415 " )\n",
0416 " records.extend(\n",
0417 " evaluate_candidates(optimizer, candidates, phase=f\"iter-{iteration + 1}\")\n",
0418 " )\n",
0419 "\n",
0420 " return optimizer_config, optimizer, records\n",
0421 "\n",
0422 "\n",
0423 "ax_optimizer_config, ax_optimizer, ax_records = run_ax_optimization(ax_full_config)\n",
0424 "print(\"First three Ax records:\")\n",
0425 "pprint(ax_records[:3])\n",
0426 "print(\"Total Ax records:\", len(ax_records))\n"
0427 ]
0428 },
0429 {
0430 "cell_type": "code",
0431 "execution_count": 41,
0432 "id": "axsummary",
0433 "metadata": {},
0434 "outputs": [
0435 {
0436 "name": "stdout",
0437 "output_type": "stream",
0438 "text": [
0439 "{'generator': 'BOTORCH_MODULAR',\n",
0440 " 'label': 'ax',\n",
0441 " 'n_trials': 10,\n",
0442 " 'pareto_points': 4}\n"
0443 ]
0444 }
0445 ],
0446 "source": [
0447 "ax_summary = summarize_optimizer(ax_optimizer, \"ax\")\n",
0448 "ax_summary[\"generator\"] = ax_optimizer_config.generator\n",
0449 "pprint(ax_summary)\n"
0450 ]
0451 },
0452 {
0453 "cell_type": "code",
0454 "execution_count": 42,
0455 "id": "axinspect",
0456 "metadata": {},
0457 "outputs": [
0458 {
0459 "name": "stdout",
0460 "output_type": "stream",
0461 "text": [
0462 "Generation strategy name: Sobol+ModularBoTorch\n",
0463 "GenerationStrategy(name='Sobol+ModularBoTorch', nodes=[GenerationNode(name='Sobol', generator_specs=[GeneratorSpec(generator_enum=Sobol, generator_key_override=None)], transition_criteria=[MinTrials(transition_to='ModularBoTorch')], pausing_criteria=None), GenerationNode(name='ModularBoTorch', generator_specs=[GeneratorSpec(generator_enum=BoTorch, generator_key_override=None)], transition_criteria=None, pausing_criteria=None)])\n"
0464 ]
0465 }
0466 ],
0467 "source": [
0468 "print(\"Generation strategy name:\", ax_optimizer.generation_strategy.name)\n",
0469 "nodes = getattr(ax_optimizer.generation_strategy, \"nodes\", None)\n",
0470 "if nodes:\n",
0471 " print(\"Generation strategy nodes:\", [node.name for node in nodes])\n",
0472 "else:\n",
0473 " print(ax_optimizer.generation_strategy)\n"
0474 ]
0475 },
0476 {
0477 "cell_type": "markdown",
0478 "id": "pymoomd",
0479 "metadata": {},
0480 "source": [
0481 "PyMOO uses the same outer interface, but the inner optimization model is generation-based. In this configuration the algorithm is inferred automatically.\n"
0482 ]
0483 },
0484 {
0485 "cell_type": "code",
0486 "execution_count": 43,
0487 "id": "pymoorun",
0488 "metadata": {},
0489 "outputs": [
0490 {
0491 "name": "stdout",
0492 "output_type": "stream",
0493 "text": [
0494 "First three PyMOO records:\n",
0495 "[{'DTLZ2_variables.x1': 0.7739560485559633,\n",
0496 " 'DTLZ2_variables.x2': 0.4388784397520523,\n",
0497 " 'DTLZ2_variables.x3': 0.8585979199113825,\n",
0498 " 'DTLZ2_variables.x4': 0.6973680290593639,\n",
0499 " 'DTLZ2_variables.x5': 0.09417734788764953,\n",
0500 " 'f1': 0.46445830050774667,\n",
0501 " 'f2': 1.2526397290110498,\n",
0502 " 'phase': 'gen-1',\n",
0503 " 'trial_index': 0},\n",
0504 " {'DTLZ2_variables.x1': 0.9756223516367559,\n",
0505 " 'DTLZ2_variables.x2': 0.761139701990353,\n",
0506 " 'DTLZ2_variables.x3': 0.7860643052769538,\n",
0507 " 'DTLZ2_variables.x4': 0.12811363267554587,\n",
0508 " 'DTLZ2_variables.x5': 0.45038593789556713,\n",
0509 " 'f1': 0.04941518013165959,\n",
0510 " 'f2': 1.2898415294878025,\n",
0511 " 'phase': 'gen-1',\n",
0512 " 'trial_index': 1},\n",
0513 " {'DTLZ2_variables.x1': 0.37079802423258124,\n",
0514 " 'DTLZ2_variables.x2': 0.9267649888486018,\n",
0515 " 'DTLZ2_variables.x3': 0.6438651200806645,\n",
0516 " 'DTLZ2_variables.x4': 0.82276161327083,\n",
0517 " 'DTLZ2_variables.x5': 0.44341419882733113,\n",
0518 " 'f1': 1.0941743623514157,\n",
0519 " 'f2': 0.7207032409991352,\n",
0520 " 'phase': 'gen-1',\n",
0521 " 'trial_index': 2}]\n",
0522 "Total PyMOO records: 32\n"
0523 ]
0524 }
0525 ],
0526 "source": [
0527 "def run_pymoo_optimization(full_config):\n",
0528 " optimizer_config = full_config.optimizer.parse_algorithm_params()\n",
0529 " optimizer = build_optimizer_from_config(full_config.problem, full_config.optimizer)\n",
0530 " records = []\n",
0531 "\n",
0532 " for generation in range(optimizer_config.n_iterations):\n",
0533 " candidates = optimizer.suggest_candidates()\n",
0534 " records.extend(\n",
0535 " evaluate_candidates(optimizer, candidates, phase=f\"gen-{generation + 1}\")\n",
0536 " )\n",
0537 "\n",
0538 " return optimizer_config, optimizer, records\n",
0539 "\n",
0540 "\n",
0541 "pymoo_optimizer_config, pymoo_optimizer, pymoo_records = run_pymoo_optimization(\n",
0542 " pymoo_full_config\n",
0543 ")\n",
0544 "print(\"First three PyMOO records:\")\n",
0545 "pprint(pymoo_records[:3])\n",
0546 "print(\"Total PyMOO records:\", len(pymoo_records))\n"
0547 ]
0548 },
0549 {
0550 "cell_type": "code",
0551 "execution_count": 44,
0552 "id": "pymoosummary",
0553 "metadata": {},
0554 "outputs": [
0555 {
0556 "name": "stdout",
0557 "output_type": "stream",
0558 "text": [
0559 "{'label': 'pymoo',\n",
0560 " 'n_trials': 32,\n",
0561 " 'pareto_points': 9,\n",
0562 " 'resolved_algorithm': 'nsga2'}\n"
0563 ]
0564 }
0565 ],
0566 "source": [
0567 "pymoo_summary = summarize_optimizer(pymoo_optimizer, \"pymoo\")\n",
0568 "pymoo_summary[\"resolved_algorithm\"] = pymoo_optimizer.resolved_algorithm\n",
0569 "pprint(pymoo_summary)\n"
0570 ]
0571 },
0572 {
0573 "cell_type": "markdown",
0574 "id": "comparisonmd",
0575 "metadata": {},
0576 "source": [
0577 "Once both runs are complete, it is easy to compare them at a high level before digging into backend-specific details.\n"
0578 ]
0579 },
0580 {
0581 "cell_type": "code",
0582 "execution_count": 45,
0583 "id": "comparisoncode",
0584 "metadata": {},
0585 "outputs": [
0586 {
0587 "name": "stdout",
0588 "output_type": "stream",
0589 "text": [
0590 "{'ax': {'generator': 'BOTORCH_MODULAR',\n",
0591 " 'label': 'ax',\n",
0592 " 'n_trials': 10,\n",
0593 " 'pareto_points': 4},\n",
0594 " 'pymoo': {'label': 'pymoo',\n",
0595 " 'n_trials': 32,\n",
0596 " 'pareto_points': 9,\n",
0597 " 'resolved_algorithm': 'nsga2'}}\n"
0598 ]
0599 }
0600 ],
0601 "source": [
0602 "comparison = {\n",
0603 " \"ax\": ax_summary,\n",
0604 " \"pymoo\": pymoo_summary,\n",
0605 "}\n",
0606 "pprint(comparison)\n"
0607 ]
0608 },
0609 {
0610 "cell_type": "markdown",
0611 "id": "inlinemd",
0612 "metadata": {},
0613 "source": [
0614 "For more interactive work, you can keep the problem definition from YAML and define only the optimizer settings inline. Here we make that inline section more opinionated by choosing a SAASBO-style Ax surrogate with qNEHVI, and an explicit NSGA2 setup for PyMOO.\n"
0615 ]
0616 },
0617 {
0618 "cell_type": "code",
0619 "execution_count": null,
0620 "id": "axinline",
0621 "metadata": {},
0622 "outputs": [
0623 {
0624 "name": "stdout",
0625 "output_type": "stream",
0626 "text": [
0627 "Inline Ax optimizer payload (SAAS surrogate + qNEHVI):\n",
0628 "{'name': 'ax',\n",
0629 " 'parameters': {'batch_size': 1,\n",
0630 " 'generator': 'BOTORCH_MODULAR',\n",
0631 " 'generator_kwargs': {'botorch_acqf_class': 'qNoisyExpectedHypervolumeImprovement',\n",
0632 " 'surrogate_spec': {'model_configs': [{'botorch_model_class': 'SaasFullyBayesianSingleTaskGP'}]}},\n",
0633 " 'initialization_strategy': 'sobol',\n",
0634 " 'n_initial_samples': 2,\n",
0635 " 'n_iterations': 1,\n",
0636 " 'objective_thresholds': {'f1': 1.0, 'f2': 1.0},\n",
0637 " 'seed': 7},\n",
0638 " 'type': 'bayesian'}\n",
0639 "First Ax candidate from the inline config:\n",
0640 "{'DTLZ2_variables.x1': 0.19947312772274017,\n",
0641 " 'DTLZ2_variables.x2': 0.17093220353126526,\n",
0642 " 'DTLZ2_variables.x3': 0.7493569254875183,\n",
0643 " 'DTLZ2_variables.x4': 0.18431377410888672,\n",
0644 " 'DTLZ2_variables.x5': 0.5505224466323853}\n"
0645 ]
0646 }
0647 ],
0648 "source": [
0649 "ax_inline_optimizer_payload = {\n",
0650 " \"name\": \"ax\",\n",
0651 " \"type\": \"bayesian\",\n",
0652 " \"parameters\": {\n",
0653 " \"initialization_strategy\": \"sobol\",\n",
0654 " \"generator\": \"BOTORCH_MODULAR\",\n",
0655 " \"generator_kwargs\": {\n",
0656 " \"surrogate_spec\": {\n",
0657 " \"model_configs\": [\n",
0658 " {\n",
0659 " \"botorch_model_class\": \"SaasFullyBayesianSingleTaskGP\"\n",
0660 " }\n",
0661 " ]\n",
0662 " },\n",
0663 " \"botorch_acqf_class\": \"qLogNoisyExpectedHypervolumeImprovement\",\n",
0664 " },\n",
0665 " \"objective_thresholds\": {\"f1\": 1.0, \"f2\": 1.0},\n",
0666 " \"n_initial_samples\": 10,\n",
0667 " \"n_iterations\": 20,\n",
0668 " \"batch_size\": 4,\n",
0669 " \"seed\": 7,\n",
0670 " },\n",
0671 " }\n",
0672 "ax_inline_optimizer_cfg = OptimizerConfiguration(**ax_inline_optimizer_payload)\n",
0673 "ax_inline_optimizer = build_optimizer_from_config(\n",
0674 " ax_full_config.problem,\n",
0675 " ax_inline_optimizer_cfg,\n",
0676 ")\n",
0677 "\n"
0678 ]
0679 },
0680 {
0681 "cell_type": "code",
0682 "execution_count": null,
0683 "id": "d68b14c4",
0684 "metadata": {},
0685 "outputs": [],
0686 "source": [
0687 "from pprint import pformat\n",
0688 "from typing import Any\n",
0689 "\n",
0690 "records: list[dict[str, Any]] = []\n",
0691 "first_candidate: dict[str, Any] | None = None\n",
0692 "while remaining_init > 0:\n",
0693 " init_round += 1\n",
0694 " current_batch = min(ax_inline_optimizer_cfg.batch_size, remaining_init)\n",
0695 " candidates = ax_inline_optimizer.suggest_candidates(n_candidates=current_batch)\n",
0696 " if first_candidate is None and candidates:\n",
0697 " first_candidate = dict(candidates[0])\n",
0698 " print(\"First Ax candidate from the inline config:\")\n",
0699 " print(pformat(first_candidate))\n",
0700 " records.extend(\n",
0701 " evaluate_candidates(\n",
0702 " ax_inline_optimizer,\n",
0703 " candidates,\n",
0704 " phase=f\"init-{init_round}\",\n",
0705 " )\n",
0706 " )\n",
0707 " remaining_init -= current_batch\n",
0708 "\n",
0709 "for iteration in range(ax_inline_optimizer_cfg.n_iterations):\n",
0710 " candidates = ax_inline_optimizer.suggest_candidates(\n",
0711 " n_candidates=ax_inline_optimizer_cfg.batch_size\n",
0712 " )\n",
0713 " records.extend(\n",
0714 " evaluate_candidates(\n",
0715 " ax_inline_optimizer,\n",
0716 " candidates,\n",
0717 " phase=f\"iter-{iteration + 1}\",\n",
0718 " )\n",
0719 " )"
0720 ]
0721 },
0722 {
0723 "cell_type": "code",
0724 "execution_count": null,
0725 "id": "f2d633dd",
0726 "metadata": {},
0727 "outputs": [],
0728 "source": []
0729 }
0730 ],
0731 "metadata": {
0732 "kernelspec": {
0733 "display_name": "env_AID2E",
0734 "language": "python",
0735 "name": "python3"
0736 },
0737 "language_info": {
0738 "codemirror_mode": {
0739 "name": "ipython",
0740 "version": 3
0741 },
0742 "file_extension": ".py",
0743 "mimetype": "text/x-python",
0744 "name": "python",
0745 "nbconvert_exporter": "python",
0746 "pygments_lexer": "ipython3",
0747 "version": "3.11.15"
0748 }
0749 },
0750 "nbformat": 4,
0751 "nbformat_minor": 5
0752 }