{
"cells": [
{
"cell_type": "markdown",
"id": "1766a5fc",
"metadata": {},
"source": [
"# Example: Gradient approximation\n",
"\n",
"We will approximate the derivative of the Rosenbrock function at `(1,0,0)`, with the [forward and backward difference methods](https://en.wikipedia.org/wiki/Finite_difference#Basic_types), and with two different step sizes.\n",
"\n",
"We will also compute an approximation of the central difference, as the average of the forward and backward results.\n",
"\n",
"Success will be determined by whether results between the different methods (forward, backward, central) are consistent (i.e., equal, within some tolerance).\n",
"\n",
"Function inputs and outputs are NumPy arrays of arbitrary positive dimension."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "b82bcbc8",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[ 400.00001657 -202.00002612 0. ]\n"
]
}
],
"source": [
"import numpy as np\n",
"from scipy.optimize import rosen, rosen_der\n",
"\n",
"from fiddy import MethodId, get_derivative\n",
"from fiddy.analysis import ApproximateCentral\n",
"from fiddy.success import Consistency\n",
"\n",
"# Point at which to compute the derivative\n",
"point = np.array([1, 0, 0])\n",
"# Step sizes for finite differences\n",
"sizes = [1e-10, 1e-5]\n",
"\n",
"derivative = get_derivative(\n",
" function=rosen,\n",
" point=point,\n",
" sizes=sizes,\n",
" method_ids=[MethodId.FORWARD, MethodId.BACKWARD],\n",
" direction_ids=[\"x\", \"y\", \"z\"],\n",
" analysis_classes=[ApproximateCentral],\n",
" success_checker=Consistency(rtol=1e-2, atol=1e-15),\n",
")\n",
"print(\"Computed derivative:\", derivative.value)"
]
},
{
"cell_type": "markdown",
"id": "bac6607b",
"metadata": {},
"source": [
"The full (`derivative.df_full`) or the concise (`derivative.df`) dataframe can be used for debugging gradients.\n",
"\n",
"The IDs correspond to the directions in which finite differences were computed. These directions can be any vector in the function's parameter space. In this case, directions were not specified, so the default directions were used, which is the standard basis."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "4f5840fb",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" direction | \n",
" success | \n",
" value | \n",
" completed | \n",
" computer_results | \n",
" analysis_results | \n",
"
\n",
" \n",
" | direction | \n",
" | \n",
" | \n",
" | \n",
" | \n",
" | \n",
" | \n",
"
\n",
" \n",
" \n",
" \n",
" | x | \n",
" [1, 0, 0] | \n",
" True | \n",
" 400.000017 | \n",
" True | \n",
" method_id value metad... | \n",
" method_id value met... | \n",
"
\n",
" \n",
" | y | \n",
" [0, 1, 0] | \n",
" True | \n",
" -202.000026 | \n",
" True | \n",
" method_id value metad... | \n",
" method_id value met... | \n",
"
\n",
" \n",
" | z | \n",
" [0, 0, 1] | \n",
" True | \n",
" 0.000000 | \n",
" True | \n",
" method_id value metadata\n",
"0... | \n",
" method_id value metadata... | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" direction success value completed \\\n",
"direction \n",
"x [1, 0, 0] True 400.000017 True \n",
"y [0, 1, 0] True -202.000026 True \n",
"z [0, 0, 1] True 0.000000 True \n",
"\n",
" computer_results \\\n",
"direction \n",
"x method_id value metad... \n",
"y method_id value metad... \n",
"z method_id value metadata\n",
"0... \n",
"\n",
" analysis_results \n",
"direction \n",
"x method_id value met... \n",
"y method_id value met... \n",
"z method_id value metadata... "
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"derivative.df"
]
},
{
"cell_type": "markdown",
"id": "14799145",
"metadata": {},
"source": [
"The `*_results` columns can be printed separately to view the specific derivative values that were computed.\n",
"\n",
"These values differ from the values reported in `derivative.values`. This is because the `success_checker` (`Consistency`) provides the derivative values as the average of all consistent derivative values. Consistency is checked on the level of `size`, so if any of the values for `1e-05` were inconsistent to the rest, they would not contribute to the average reported by the `Consistency` success checker."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "63d7e327",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" method_id | \n",
" value | \n",
" metadata | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" MethodId.FORWARD | \n",
" 400.000033 | \n",
" {'size': 1e-10} | \n",
"
\n",
" \n",
" | 1 | \n",
" MethodId.BACKWARD | \n",
" 400.000033 | \n",
" {'size': 1e-10} | \n",
"
\n",
" \n",
" | 2 | \n",
" MethodId.FORWARD | \n",
" 400.006010 | \n",
" {'size': 1e-05} | \n",
"
\n",
" \n",
" | 3 | \n",
" MethodId.BACKWARD | \n",
" 399.993990 | \n",
" {'size': 1e-05} | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" method_id value metadata\n",
"0 MethodId.FORWARD 400.000033 {'size': 1e-10}\n",
"1 MethodId.BACKWARD 400.000033 {'size': 1e-10}\n",
"2 MethodId.FORWARD 400.006010 {'size': 1e-05}\n",
"3 MethodId.BACKWARD 399.993990 {'size': 1e-05}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"derivative.df.loc[\"x\", \"computer_results\"]"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "38b3d2b6",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" method_id | \n",
" value | \n",
" metadata | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" approximate_central | \n",
" 400.000000 | \n",
" {'size': 1e-05} | \n",
"
\n",
" \n",
" | 1 | \n",
" approximate_central | \n",
" 400.000033 | \n",
" {'size': 1e-10} | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" method_id value metadata\n",
"0 approximate_central 400.000000 {'size': 1e-05}\n",
"1 approximate_central 400.000033 {'size': 1e-10}"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"derivative.df.loc[\"x\", \"analysis_results\"]"
]
},
{
"cell_type": "markdown",
"id": "cd9807aa",
"metadata": {},
"source": [
"In this case, the finite difference values are all consistent with each other, and we now compare them with the expected derivative."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "5b8ae013",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[ 400 -202 0]\n"
]
}
],
"source": [
"expected_derivative = rosen_der(point)\n",
"print(f\"{expected_derivative=}\")"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "c01098c7",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.isclose(derivative.value, expected_derivative).all()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.9.16"
}
},
"nbformat": 4,
"nbformat_minor": 5
}