{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0",
   "metadata": {
    "hide_input": true,
    "tags": [
     "remove-input",
     "remove-output",
     "active-ipynb"
    ]
   },
   "outputs": [],
   "source": [
    "try:\n",
    "    from openmdao.utils.notebook_utils import notebook_mode  # noqa: F401\n",
    "except ImportError:\n",
    "    !python -m pip install openmdao[notebooks]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1",
   "metadata": {},
   "source": [
    "# modOptDriver\n",
    "\n",
    "modOptDriver wraps the optimizers available through the modOpt optimization framework. modOpt provides access to a variety of gradient-based and gradient-free optimization algorithms, including SLSQP, IPOPT, SNOPT, COBYLA, and others. Some of these are available in the package, some need to be separately installed, and some are commercial products that must be obtained from their respective authors (e.g. SNOPT). The modOptDriver supports sparse specification of constraint Jacobians, but not all optimizers can utilize the sparsity.\n",
    "\n",
    "In this example, we use the SLSQP optimizer to minimize the objective of the Paraboloid problem."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from openmdao.utils.notebook_utils import get_code\n",
    "from myst_nb import glue\n",
    "glue(\"code_src020\", get_code(\"openmdao.test_suite.components.paraboloid.Paraboloid\"), display=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3",
   "metadata": {},
   "source": [
    ":::{Admonition} `Paraboloid` class definition \n",
    ":class: dropdown\n",
    "\n",
    "{glue:}`code_src020`\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4",
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "from openmdao.test_suite.components.paraboloid import Paraboloid\n",
    "\n",
    "prob = om.Problem()\n",
    "model = prob.model\n",
    "\n",
    "model.add_subsystem('comp', Paraboloid(), promotes=['*'])\n",
    "\n",
    "prob.driver = om.modOptDriver()\n",
    "prob.driver.options['optimizer'] = 'SLSQP'\n",
    "prob.driver.options['maxiter'] = 200\n",
    "prob.driver.options['disp'] = True\n",
    "\n",
    "model.add_design_var('x', lower=-50.0, upper=50.0)\n",
    "model.add_design_var('y', lower=-50.0, upper=50.0)\n",
    "model.add_objective('f_xy')\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "prob.set_val('x', 50.0)\n",
    "prob.set_val('y', 50.0)\n",
    "\n",
    "prob.run_driver()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18cb7651",
   "metadata": {},
   "source": [
    "The \"optimizer\" option lets you choose which optimizer to use. modOptDriver provides access to a wide range of optimizers through the modOpt framework. The supported optimizers are (case sensitive):\n",
    "\n",
    "**Gradient-Based:**\n",
    "- \"SLSQP\" - Sequential Least Squares Programming (default optimizer)\n",
    "- \"PySLSQP\" - Pure Python implementation of SLSQP with expanded features\n",
    "- \"BFGS\" - Broyden-Fletcher-Goldfarb-Shanno (unconstrained only)\n",
    "- \"LBFGSB\" - Limited-memory BFGS with bounds\n",
    "- \"TrustConstr\" - Trust-region constrained algorithm\n",
    "- \"IPOPT\" - Interior Point Optimizer\n",
    "- \"SNOPT\" - Sparse Nonlinear Optimizer (requires license)\n",
    "- \"OpenSQP\" - Sequential Quadratic Programming built into modOpt\n",
    "\n",
    "**Gradient-Free:**\n",
    "- \"COBYLA\" - Constrained Optimization BY Linear Approximation\n",
    "- \"COBYQA\" - Constrained Optimization BY Quadratic Approximation (improved COBYLA)\n",
    "- \"NelderMead\" - Nelder-Mead simplex algorithm (unconstrained only)\n",
    "\n",
    "```{note}\n",
    "OpenMDAO installs only the base modOpt package. Some optimizers listed above require\n",
    "additional packages that must be installed manually in your environment. Most of the optimizers (SLSQP, BFGS, LBFGSB, TrustConstr, COBYLA, COBYQA, NelderMead)\n",
    "are available through SciPy and are included with the base modOpt installation.\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(prob.get_val('x'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(prob.get_val('y'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7",
   "metadata": {
    "hide_input": true,
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "from openmdao.utils.assert_utils import assert_near_equal\n",
    "assert_near_equal(prob.get_val('x'), 6.66666667, 1e-6)\n",
    "assert_near_equal(prob.get_val('y'), -7.3333333, 1e-6)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8",
   "metadata": {},
   "source": [
    "## modOptDriver Options"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9",
   "metadata": {},
   "outputs": [],
   "source": [
    "om.show_options_table(\"openmdao.drivers.modopt_driver.modOptDriver\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10",
   "metadata": {},
   "source": [
    "## modOptDriver Constructor\n",
    "\n",
    "The call signature for the *modOptDriver* constructor is:\n",
    "\n",
    "```{eval-rst}\n",
    "    .. automethod:: openmdao.drivers.modopt_driver.modOptDriver.__init__\n",
    "        :noindex:\n",
    "```\n",
    "\n",
    "## Using modOptDriver\n",
    "\n",
    "modOptDriver has a small number of unified options that can be specified as keyword arguments when it is instantiated or by using the “options” dictionary. We have already shown how to set the optimizer option. Next we will see how `maxiter` and `disp` can be used to control the maxinum number of iterations and what the optimizer displays. The \"disp\" option controls optimizer output verbosity. You can set it to True/False or an integer for fine control (0=quiet, higher=more verbose). This setting is automatically mapped to the optimizer's output settings."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "16",
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "from openmdao.test_suite.components.paraboloid import Paraboloid\n",
    "\n",
    "prob = om.Problem()\n",
    "model = prob.model\n",
    "\n",
    "model.add_subsystem('comp', Paraboloid(), promotes=['*'])\n",
    "\n",
    "prob.driver = om.modOptDriver()\n",
    "prob.driver.options['maxiter'] = 20\n",
    "prob.driver.options['disp'] = False\n",
    "\n",
    "model.add_design_var('x', lower=-50.0, upper=50.0)\n",
    "model.add_design_var('y', lower=-50.0, upper=50.0)\n",
    "model.add_objective('f_xy')\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "prob.set_val('x', 50.0)\n",
    "prob.set_val('y', 50.0)\n",
    "\n",
    "prob.run_driver()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "17",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(prob.get_val('x'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "18",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(prob.get_val('y'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19",
   "metadata": {
    "hide_input": true,
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(prob.get_val('x'), 6.66666667, 1e-6)\n",
    "assert_near_equal(prob.get_val('y'), -7.3333333, 1e-6)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23",
   "metadata": {},
   "source": [
    "## modOptDriver Optimizer-Specific Options\n",
    "\n",
    "Each optimizer in modOpt has its own set of specific options that control its behavior. You can set these optimizer-specific options through the `opt_settings` dictionary. See the [modOpt documentation](https://modopt.readthedocs.io) for detailed information about available settings for each optimizer.\n",
    "\n",
    "For example, here's code setting some specific options for the SLSQP optimizer:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "24",
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "from openmdao.test_suite.components.paraboloid import Paraboloid\n",
    "\n",
    "prob = om.Problem()\n",
    "model = prob.model\n",
    "\n",
    "model.add_subsystem('comp', Paraboloid(), promotes=['*'])\n",
    "\n",
    "prob.driver = driver = om.modOptDriver()\n",
    "driver.options['optimizer'] = 'SLSQP'\n",
    "driver.options['maxiter'] = 100\n",
    "driver.options['disp'] = False\n",
    "\n",
    "# Set optimizer-specific options through opt_settings. Here we set the precision for\n",
    "# the final solution with the \"ftol\" setting specific to SLSQP\n",
    "driver.opt_settings['ftol'] = 1e-8\n",
    "\n",
    "model.add_design_var('x', lower=-50.0, upper=50.0)\n",
    "model.add_design_var('y', lower=-50.0, upper=50.0)\n",
    "model.add_objective('f_xy')\n",
    "\n",
    "prob.setup()\n",
    "\n",
    "prob.set_val('x', 50.0)\n",
    "prob.set_val('y', 50.0)\n",
    "\n",
    "prob.run_driver()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "25",
   "metadata": {
    "hide_input": true,
    "tags": [
     "remove-input",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "assert_near_equal(prob.get_val('x'), 6.66666667, 1e-6)\n",
    "assert_near_equal(prob.get_val('y'), -7.3333333, 1e-6)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "om-dev",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.13"
  },
  "orphan": true
 },
 "nbformat": 4,
 "nbformat_minor": 5
}