{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "928be80b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:40:07.078236Z",
     "iopub.status.busy": "2026-10-02T14:40:07.078045Z",
     "iopub.status.idle": "2026-10-02T14:40:07.082080Z",
     "shell.execute_reply": "2026-10-02T14:40:07.081478Z"
    },
    "papermill": {
     "duration": 0.006325,
     "end_time": "2026-10-02T14:40:07.082544+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:40:07.076219+00:00",
     "status": "completed"
    },
    "tags": [
     "remove-input",
     "active-ipynb",
     "remove-output"
    ]
   },
   "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": "7b5a7915",
   "metadata": {
    "papermill": {
     "duration": 0.000825,
     "end_time": "2026-10-02T14:40:07.084418+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:40:07.083593+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# Defining Partial Derivatives on Implicit Components\n",
    "\n",
    "For [ImplicitComponent](../../features/core_features/working_with_components/implicit_component.ipynb) instances, you will provide partial derivatives of **residuals with respect to inputs and outputs**. Note that this is slightly different than what you do for [ExplicitComponent instances](partial_derivs_explicit.ipynb), but the general procedure is similar:\n",
    "\n",
    "1. Declare the partial derivatives via `declare_partials`.\n",
    "2. Specify their values via `linearize`.\n",
    "\n",
    "Residual values are computed in the `apply_nonlinear` method, so those equations are the ones you will differentiate. For the sake of complete clarity, if your `ImplicitComponent` does happen to define a `solve_nonlinear` method, then you will still provide derivatives of the `apply_nonlinear` method to OpenMDAO.\n",
    "\n",
    "Here is a simple example to consider:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "4fd356b7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:40:07.086445Z",
     "iopub.status.busy": "2026-10-02T14:40:07.086316Z",
     "iopub.status.idle": "2026-10-02T14:40:15.455004Z",
     "shell.execute_reply": "2026-10-02T14:40:15.453980Z"
    },
    "papermill": {
     "duration": 8.370454,
     "end_time": "2026-10-02T14:40:15.455600+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:40:07.085146+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/runner/work/OpenMDAO/OpenMDAO/.pixi/envs/dev/lib/python3.13/site-packages/modopt/external_libraries/cvxopt/cvxopt.py:237: SyntaxWarning: invalid escape sequence '\\c'\n",
      "  z_0 \\nabla^2f_0(x) + z_1 \\nabla^2f_1(x) + \\cdots + z_m \\nabla^2f_m(x).\n"
     ]
    }
   ],
   "source": [
    "import openmdao.api as om\n",
    "\n",
    "\n",
    "class QuadraticComp(om.ImplicitComponent):\n",
    "    \"\"\"\n",
    "    A Simple Implicit Component representing a Quadratic Equation.\n",
    "\n",
    "    R(a, b, c, x) = ax^2 + bx + c\n",
    "\n",
    "    Solution via Quadratic Formula:\n",
    "    x = (-b + sqrt(b^2 - 4ac)) / 2a\n",
    "    \"\"\"\n",
    "\n",
    "    def setup(self):\n",
    "        self.add_input('a', val=1.)\n",
    "        self.add_input('b', val=1.)\n",
    "        self.add_input('c', val=1.)\n",
    "        self.add_output('x', val=0.)\n",
    "\n",
    "    def setup_partials(self):\n",
    "        self.declare_partials(of='x', wrt='*')\n",
    "\n",
    "    def apply_nonlinear(self, inputs, outputs, residuals):\n",
    "        a = inputs['a']\n",
    "        b = inputs['b']\n",
    "        c = inputs['c']\n",
    "        x = outputs['x']\n",
    "        residuals['x'] = a * x ** 2 + b * x + c\n",
    "\n",
    "    def solve_nonlinear(self, inputs, outputs):\n",
    "        a = inputs['a']\n",
    "        b = inputs['b']\n",
    "        c = inputs['c']\n",
    "        outputs['x'] = (-b + (b ** 2 - 4 * a * c) ** 0.5) / (2 * a)\n",
    "\n",
    "    def linearize(self, inputs, outputs, partials):\n",
    "        a = inputs['a']\n",
    "        b = inputs['b']\n",
    "        # c = inputs['c']  # not needed\n",
    "        x = outputs['x']\n",
    "\n",
    "        partials['x', 'a'] = x ** 2\n",
    "        partials['x', 'b'] = x\n",
    "        partials['x', 'c'] = 1.0\n",
    "        partials['x', 'x'] = 2 * a * x + b\n",
    "\n",
    "        self.inv_jac = 1.0 / (2 * a * x + b)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bf59d43a",
   "metadata": {
    "papermill": {
     "duration": 0.054705,
     "end_time": "2026-10-02T14:40:15.511909+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:40:15.457204+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "In this component, `x` is an output, and you take derivatives with respect to it. This might seem a bit strange to you if you're used to thinking about things from an [ExplicitComponent](partial_derivs_explicit.ipynb) perspective. But for implicit components it is necessary, because the values of those outputs are determined by a solver, like [NewtonSolver](../../features/building_blocks/solvers/newton.ipynb), which will need to know those derivatives. They are also necessary for the total derivative computations across the whole model. So if your residual is a function of one or more of the component outputs, make sure you provide those partials to OpenMDAO.\n",
    "\n",
    "## Check That Your Derivatives Are Correct!\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "fa2be914",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-02T14:40:15.516093Z",
     "iopub.status.busy": "2026-10-02T14:40:15.515485Z",
     "iopub.status.idle": "2026-10-02T14:40:17.202685Z",
     "shell.execute_reply": "2026-10-02T14:40:17.201943Z"
    },
    "papermill": {
     "duration": 1.690384,
     "end_time": "2026-10-02T14:40:17.203613+00:00",
     "exception": false,
     "start_time": "2026-10-02T14:40:15.513229+00:00",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1790952017.175651] [runnervm8df0l:5748 :0]        ib_iface.c:1269 UCX  ERROR mana_0: iface 0x55a823e42a80 failed to create UD QP TX wr:256 sge:6 inl:64 resp:0 RX wr:4096 sge:1 resp:0 failed: Operation not supported\n",
      "[1790952017.175907] [runnervm8df0l:5748 :0]      ucp_worker.c:1412 UCX  ERROR uct_iface_open(ud_verbs/mana_0:1) failed: Input/output error\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[runnervm8df0l:05748] pml_ucx.c:313  Error: Failed to create UCP worker\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">--------------------------------------------------------------------\n",
       "Component: QuadraticComp '<span style=\"color: #00ffff; text-decoration-color: #00ffff; font-weight: bold\">quad</span>'\n",
       "--------------------------------------------------------------------\n",
       "\n",
       "+---------------+----------------+---------------------+-------------------+------------------------+------------+\n",
       "| 'of' variable | 'wrt' variable | calc val @ max viol | fd val @ max viol | (calc-fd) - (a + r*fd) | error desc |\n",
       "+===============+================+=====================+===================+========================+============+\n",
       "| x             | a              |        4.356076e-02 |      4.356076e-02 |        (-4.350751e-08) |            |\n",
       "+---------------+----------------+---------------------+-------------------+------------------------+------------+\n",
       "| x             | b              |       -2.087122e-01 |     -2.087122e-01 |        (-2.086397e-07) |            |\n",
       "+---------------+----------------+---------------------+-------------------+------------------------+------------+\n",
       "| x             | c              |        1.000000e+00 |      1.000000e+00 |        (-9.999177e-07) |            |\n",
       "+---------------+----------------+---------------------+-------------------+------------------------+------------+\n",
       "| x             | x              |        4.582576e+00 |      4.582577e+00 |        (-3.582532e-06) |            |\n",
       "+---------------+----------------+---------------------+-------------------+------------------------+------------+\n",
       "\n",
       "</pre>\n"
      ],
      "text/plain": [
       "--------------------------------------------------------------------\n",
       "Component: QuadraticComp '\u001b[1;96mquad\u001b[0m'\n",
       "--------------------------------------------------------------------\n",
       "\n",
       "+---------------+----------------+---------------------+-------------------+------------------------+------------+\n",
       "| 'of' variable | 'wrt' variable | calc val @ max viol | fd val @ max viol | (calc-fd) - (a + r*fd) | error desc |\n",
       "+===============+================+=====================+===================+========================+============+\n",
       "| x             | a              |        4.356076e-02 |      4.356076e-02 |        (-4.350751e-08) |            |\n",
       "+---------------+----------------+---------------------+-------------------+------------------------+------------+\n",
       "| x             | b              |       -2.087122e-01 |     -2.087122e-01 |        (-2.086397e-07) |            |\n",
       "+---------------+----------------+---------------------+-------------------+------------------------+------------+\n",
       "| x             | c              |        1.000000e+00 |      1.000000e+00 |        (-9.999177e-07) |            |\n",
       "+---------------+----------------+---------------------+-------------------+------------------------+------------+\n",
       "| x             | x              |        4.582576e+00 |      4.582577e+00 |        (-3.582532e-06) |            |\n",
       "+---------------+----------------+---------------------+-------------------+------------------------+------------+\n",
       "\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from openmdao.test_suite.components.quad_implicit import QuadraticComp\n",
    "\n",
    "p = om.Problem()\n",
    "\n",
    "p.model.add_subsystem('quad', QuadraticComp())\n",
    "\n",
    "p.setup()\n",
    "\n",
    "p.check_partials(compact_print=True);"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.13.14"
  },
  "papermill": {
   "default_parameters": {},
   "duration": 11.400801,
   "end_time": "2026-10-02T14:40:17.820508+00:00",
   "environment_variables": {},
   "exception": null,
   "input_path": "/home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/openmdao_book/advanced_user_guide/analytic_derivatives/partial_derivs_implicit.ipynb",
   "output_path": "/home/runner/work/OpenMDAO/OpenMDAO/openmdao/docs/_executed_book/advanced_user_guide/analytic_derivatives/partial_derivs_implicit.ipynb",
   "parameters": {},
   "start_time": "2026-10-02T14:40:06.419707+00:00",
   "version": "2.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}