Files
optimiz-rs/examples/notebooks/03_differential_evolution_tutorial.ipynb
T

721 lines
1.3 MiB
Plaintext
Raw Normal View History

{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "b9578ab3",
"metadata": {
"execution": {
"iopub.execute_input": "2026-02-16T16:11:56.183162Z",
"iopub.status.busy": "2026-02-16T16:11:56.182871Z",
"iopub.status.idle": "2026-02-16T16:11:56.814215Z",
"shell.execute_reply": "2026-02-16T16:11:56.812868Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OptimizR Differential Evolution Module Loaded!\n"
]
}
],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"from mpl_toolkits.mplot3d import Axes3D\n",
"from optimizr import differential_evolution\n",
"import time\n",
"\n",
"np.random.seed(42)\n",
"print(\"OptimizR Differential Evolution Module Loaded!\")"
]
},
{
"cell_type": "markdown",
"id": "78cfac35",
"metadata": {},
"source": [
"# Differential Evolution Tutorial - Global Optimization\n",
"\n",
"## Introduction\n",
"\n",
"**Differential Evolution (DE)** is a powerful population-based stochastic optimization algorithm designed for global optimization of non-convex, non-differentiable, and multimodal problems.\n",
"\n",
"### Why Differential Evolution?\n",
"\n",
"Unlike gradient-based methods that can get stuck in local minima, DE:\n",
"- ✅ **Global search capability** - Explores entire parameter space\n",
"- ✅ **No gradient required** - Works with black-box functions\n",
"- ✅ **Few hyperparameters** - Mutation factor F and crossover rate CR\n",
"- ✅ **Robust** - Handles noisy and discontinuous functions\n",
"- ✅ **Parallelizable** - Population members can be evaluated independently\n",
"\n",
"### Applications\n",
"- Portfolio optimization\n",
"- Hyperparameter tuning in ML\n",
"- Engineering design optimization\n",
"- Physics parameter fitting\n",
"- Control system design\n",
"\n",
"## Algorithm Overview\n",
"\n",
"### The DE/rand/1/bin Strategy\n",
"\n",
"Given a population of $N_p$ candidate solutions $\\mathbf{x}_i$, DE iterates:\n",
"\n",
"**1. Mutation** - Create mutant vector:\n",
"$$\\mathbf{v}_i = \\mathbf{x}_{r1} + F \\cdot (\\mathbf{x}_{r2} - \\mathbf{x}_{r3})$$\n",
"\n",
"where $r1, r2, r3$ are random distinct indices, and $F \\in [0, 2]$ is the mutation factor.\n",
"\n",
"**2. Crossover** - Create trial vector:\n",
"$$u_{i,j} = \\begin{cases}\n",
"v_{i,j} & \\text{if } \\text{rand}() < CR \\text{ or } j = j_{rand} \\\\\n",
"x_{i,j} & \\text{otherwise}\n",
"\\end{cases}$$\n",
"\n",
"where $CR \\in [0, 1]$ is the crossover probability.\n",
"\n",
"**3. Selection** - Greedy selection:\n",
"$$\\mathbf{x}_i^{t+1} = \\begin{cases}\n",
"\\mathbf{u}_i & \\text{if } f(\\mathbf{u}_i) < f(\\mathbf{x}_i^t) \\\\\n",
"\\mathbf{x}_i^t & \\text{otherwise}\n",
"\\end{cases}$$\n",
"\n",
"### Convergence\n",
"\n",
"Under mild conditions, DE converges to the global optimum with probability 1:\n",
"$$\\lim_{t \\to \\infty} P\\left(\\|\\mathbf{x}^*_t - \\mathbf{x}^*\\| < \\epsilon\\right) = 1$$\n",
"\n",
"where $\\mathbf{x}^*$ is the global optimum.\n",
"\n",
"### Complexity\n",
"\n",
"- **Time:** $O(N_p \\cdot d \\cdot T)$ where $d$ is dimension, $T$ is iterations\n",
"- **Space:** $O(N_p \\cdot d)$ for population storage\n",
"\n",
"## References\n",
"\n",
"- Storn, R., & Price, K. (1997). \"Differential evolutiona simple and efficient heuristic for global optimization over continuous spaces.\" *Journal of global optimization*, 11(4), 341-359.\n",
"- Das, S., & Suganthan, P. N. (2011). \"Differential evolution: A survey of the state-of-the-art.\" *IEEE transactions on evolutionary computation*, 15(1), 4-31."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "588bf0b2",
"metadata": {
"execution": {
"iopub.execute_input": "2026-02-16T16:11:56.817581Z",
"iopub.status.busy": "2026-02-16T16:11:56.817168Z",
"iopub.status.idle": "2026-02-16T16:11:56.822872Z",
"shell.execute_reply": "2026-02-16T16:11:56.821072Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OptimizR Differential Evolution Loaded!\n"
]
}
],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"from mpl_toolkits.mplot3d import Axes3D\n",
"from optimizr import differential_evolution\n",
"\n",
"np.random.seed(42)\n",
"print(\"OptimizR Differential Evolution Loaded!\")"
]
},
{
"cell_type": "markdown",
"id": "3ba7fb3a",
"metadata": {},
"source": [
"## Example 1: Rosenbrock Function (Banana Valley)\n",
"\n",
"$$f(\\mathbf{x}) = \\sum_{i=1}^{n-1} \\left[100(x_{i+1} - x_i^2)^2 + (1 - x_i)^2\\right]$$\n",
"\n",
"Global minimum: $f(1, 1, \\ldots, 1) = 0$"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "5f9644cd",
"metadata": {
"execution": {
"iopub.execute_input": "2026-02-16T16:11:56.826257Z",
"iopub.status.busy": "2026-02-16T16:11:56.825957Z",
"iopub.status.idle": "2026-02-16T16:11:56.831830Z",
"shell.execute_reply": "2026-02-16T16:11:56.830572Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"f([1, 1, 1]): 0.0\n",
"f([0, 0, 0]): 2.0\n"
]
}
],
"source": [
"def rosenbrock(x):\n",
" \"\"\"N-dimensional Rosenbrock function.\"\"\"\n",
" return sum(100 * (x[i+1] - x[i]**2)**2 + (1 - x[i])**2 \n",
" for i in range(len(x) - 1))\n",
"\n",
"# Test function\n",
"print(f\"f([1, 1, 1]): {rosenbrock([1.0, 1.0, 1.0])}\")\n",
"print(f\"f([0, 0, 0]): {rosenbrock([0.0, 0.0, 0.0])}\")"
]
},
{
"cell_type": "markdown",
"id": "dda42ec7",
"metadata": {},
"source": [
"### Visualize 2D Rosenbrock"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "ec984eff",
"metadata": {
"execution": {
"iopub.execute_input": "2026-02-16T16:11:56.834783Z",
"iopub.status.busy": "2026-02-16T16:11:56.834496Z",
"iopub.status.idle": "2026-02-16T16:11:58.524532Z",
"shell.execute_reply": "2026-02-16T16:11:58.523304Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"<>:16: SyntaxWarning: invalid escape sequence '\\l'\n",
"<>:27: SyntaxWarning: invalid escape sequence '\\l'\n",
"<>:16: SyntaxWarning: invalid escape sequence '\\l'\n",
"<>:27: SyntaxWarning: invalid escape sequence '\\l'\n",
"/var/folders/ns/tb9t1knx50z780g06d68yfth0000gp/T/ipykernel_22373/2058947432.py:16: SyntaxWarning: invalid escape sequence '\\l'\n",
" ax1.set_zlabel('$\\log_{10}(f + 1)$', fontsize=11)\n",
"/var/folders/ns/tb9t1knx50z780g06d68yfth0000gp/T/ipykernel_22373/2058947432.py:27: SyntaxWarning: invalid escape sequence '\\l'\n",
" plt.colorbar(contour, ax=ax2, label='$\\log_{10}(f + 1)$')\n"
]
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 1400x600 with 3 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Create meshgrid\n",
"x1 = np.linspace(-2, 2, 200)\n",
"x2 = np.linspace(-1, 3, 200)\n",
"X1, X2 = np.meshgrid(x1, x2)\n",
"Z = np.array([[rosenbrock([x1_val, x2_val]) for x1_val, x2_val in zip(x1_row, x2_row)] \n",
" for x1_row, x2_row in zip(X1, X2)])\n",
"\n",
"fig = plt.figure(figsize=(14, 6))\n",
"\n",
"# 3D surface\n",
"ax1 = fig.add_subplot(121, projection='3d')\n",
"surf = ax1.plot_surface(X1, X2, np.log10(Z + 1), cmap='viridis', alpha=0.8)\n",
"ax1.scatter([1], [1], [0], c='red', s=200, marker='*', edgecolors='black', linewidths=2, label='Global min')\n",
"ax1.set_xlabel('$x_1$', fontsize=11)\n",
"ax1.set_ylabel('$x_2$', fontsize=11)\n",
"ax1.set_zlabel('$\\log_{10}(f + 1)$', fontsize=11)\n",
"ax1.set_title('Rosenbrock Function (3D)', fontsize=13, fontweight='bold')\n",
"\n",
"# 2D contour\n",
"ax2 = fig.add_subplot(122)\n",
"contour = ax2.contour(X1, X2, np.log10(Z + 1), levels=20, cmap='viridis')\n",
"ax2.scatter([1], [1], c='red', s=200, marker='*', edgecolors='black', linewidths=2, label='Global min', zorder=5)\n",
"ax2.set_xlabel('$x_1$', fontsize=11)\n",
"ax2.set_ylabel('$x_2$', fontsize=11)\n",
"ax2.set_title('Rosenbrock Function (Contour)', fontsize=13, fontweight='bold')\n",
"ax2.legend()\n",
"plt.colorbar(contour, ax=ax2, label='$\\log_{10}(f + 1)$')\n",
"\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "812d80ae",
"metadata": {},
"source": [
"### Optimize with Differential Evolution"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "f722fb0a",
"metadata": {
"execution": {
"iopub.execute_input": "2026-02-16T16:11:58.531497Z",
"iopub.status.busy": "2026-02-16T16:11:58.531199Z",
"iopub.status.idle": "2026-02-16T16:11:59.276354Z",
"shell.execute_reply": "2026-02-16T16:11:59.274987Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Optimizing 10D Rosenbrock function...\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Optimization completed!\n",
"Best solution: [ 9.40856038e-01 9.02723698e-01 8.52156614e-01 7.21181862e-01\n",
" 4.78495137e-01 2.33698512e-01 4.66375701e-02 -9.09126619e-04\n",
" 1.80505507e-02 -1.14071552e-02]\n",
"Best value: 4.247611e+00\n",
"\n",
"Distance to true optimum [1, 1, ..., 1]:\n",
" ||x - x*|| = 2.206724\n"
]
}
],
"source": [
"# 10-dimensional Rosenbrock\n",
"n_dims = 10\n",
"bounds = [(-5, 5)] * n_dims\n",
"\n",
"print(f\"Optimizing {n_dims}D Rosenbrock function...\")\n",
"x, fun = differential_evolution(\n",
" objective_fn=rosenbrock,\n",
" bounds=bounds,\n",
" maxiter=500,\n",
" popsize=15,\n",
" f=0.8,\n",
" cr=0.7,\n",
" seed=42\n",
")\n",
"\n",
"print(f\"\\nOptimization completed!\")\n",
"print(f\"Best solution: {x}\")\n",
"print(f\"Best value: {fun:.6e}\")\n",
"print(f\"\\nDistance to true optimum [1, 1, ..., 1]:\")\n",
"print(f\" ||x - x*|| = {np.linalg.norm(x - np.ones(n_dims)):.6f}\")"
]
},
{
"cell_type": "markdown",
"id": "89fde8e9",
"metadata": {},
"source": [
"## Example 2: Rastrigin Function (Many Local Minima)\n",
"\n",
"$$f(\\mathbf{x}) = 10n + \\sum_{i=1}^n \\left[x_i^2 - 10\\cos(2\\pi x_i)\\right]$$\n",
"\n",
"Global minimum: $f(0, 0, \\ldots, 0) = 0$"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "fff32181",
"metadata": {
"execution": {
"iopub.execute_input": "2026-02-16T16:11:59.279477Z",
"iopub.status.busy": "2026-02-16T16:11:59.279189Z",
"iopub.status.idle": "2026-02-16T16:12:00.923846Z",
"shell.execute_reply": "2026-02-16T16:12:00.922152Z"
}
},
"outputs": [
{
"data": {
"image/png": "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XMN27tzpWlbuzxUrVrA3v/nNrLW1laXTafb617+erV+/3llePE5VH/HZ8M9//jN729vexqZOncqSySRrb29n559/PvvBD37AyuWyyw7d8S/vcxFx+rJly9jHP/5xNm3aNBaLxVzPmD/96U+d5ShCiSCCQR6iBHEEMH36dNf3hx56CA8//LBrWldXF/7yl7/gL3/5C5YvX+7kzDl48CAWL16Mrq4uZ9lisYitW7di69at2LBhAz7+8Y+HsmvJkiX48Y9/DNu2jZZfsWIFLrjgAvT29gIAstksHnjgATz77LPo6+sLZQMA/OQnP8HNN9/sfM/lcrjzzjthWRa+973vhV5vPfjLX/6Cn/zkJ66k9D//+c/R09ODRx991Jn2/PPP43Wvex0OHjzo+v3mzZuxefNmfPKTn6zZloGBAbzmNa/B3/72N9f0HTt24Kc//Sl+8Ytf4K677sKVV16p/P25556L9evXO99feuklvPnNb8a6deswa9Ys3+3/+c9/dv4/55xzPJe1bRt79+7F7bff7kxrbm7G4sWLfbfDefe734177rkHTzzxBADg3nvvxWc+8xnXMueccw6+//3vAwCefPJJvOUtbzFeP0EQBHFkQs9p3tBzWjjG8nPaP/7xD6xZs8b5/sUvflHrpXvyySc7/69evRoXXXQR9u3b51pm7dq1WLt2Le677z488cQTOPXUU6vWs27dOpxzzjkYGBhwpv3hD3/AP/3TP+Hll19GLGZeXuVrX/saPv3pT7vyyh86dAhPP/00nn76aTz44IN46KGHlDlTw/D2t7/d1Rci4r598sknI9keQRwpUFElgjiMePrpp/HUU0/h61//Oh577DEAQCqVwoc+9CHXcm9+85vxve99Dw8//DCefPJJPPHEE7jrrrswceJEAMB3vvMd7NmzB0Dlxsofsl/5ylfikUcewWOPPYYf/vCHeP/734+jjz4aAPCGN7wBS5YswWmnneZs53Of+xyWLFmCJUuW4POf/3yVvZs2bcLxxx+PH//4x3jsscfwsY99zLN9n/jEJ5yH7KlTp+Lee+/FL3/5SzQ3N7sGAkHZsGED3vGOd+CRRx5x7au77rqrqvCOH1u3blUm6//Nb34T2j6RzZs345WvfCUefvhh3Hjjjc70xx57zEk6n8/ncdlllzkP2ZMnT8Y3v/lNp99e+9rXwrKsUH0mcsMNNzgP2S0tLfjWt76FRx55xBEBS6USPvjBD2L79u3K33d3d+MHP/gBfvGLXzjHUaFQMBrc2LaNVatWAQCSySRmz56tXba9vR3xeBzTpk3DLbfcAgBYuHAhfv/732PKlCm+2xIRw+tXr16NbDbrmn/cccc5/y9fvjzQugmCIIjDG3pOCwc9p53mrOdweU574YUXXN8vvvhi33UClZfTXAydN2+eI+yeeOKJAIADBw7gXe96l1LE3717N+bPn49f/epXuO2225BMJgEAq1atctIp/fKXv8T//M//OL+ZMmWKs7+XLFmCqVOnYtmyZS4x9D3veQ9+97vf4ctf/jJSqRQA4Pe//z2++c1vGrXJhI0bN+KTn/wk/vCHP+DHP/4xjj32WGfenDlzHOF15cqVxi8wCIIAyEOUIA4jLrjgAtf3M888E9/85jdxxhlnuKZfcskl+OpXv4rbb78dmzZtQjabdb3hLJf
"text/plain": [
"<Figure size 1400x600 with 4 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Note: Rastrigin has MANY local minima (visible as the peaks in the plot)\n"
]
}
],
"source": [
"def rastrigin(x):\n",
" \"\"\"Rastrigin function with many local minima.\"\"\"\n",
" n = len(x)\n",
" return 10 * n + sum(xi**2 - 10 * np.cos(2 * np.pi * xi) for xi in x)\n",
"\n",
"# Visualize 2D\n",
"x1 = np.linspace(-5.12, 5.12, 200)\n",
"x2 = np.linspace(-5.12, 5.12, 200)\n",
"X1, X2 = np.meshgrid(x1, x2)\n",
"Z = np.array([[rastrigin([x1_val, x2_val]) for x1_val, x2_val in zip(x1_row, x2_row)]\n",
" for x1_row, x2_row in zip(X1, X2)])\n",
"\n",
"fig, axes = plt.subplots(1, 2, figsize=(14, 6))\n",
"\n",
"# 3D plot\n",
"ax1 = fig.add_subplot(121, projection='3d')\n",
"ax1.plot_surface(X1, X2, Z, cmap='plasma', alpha=0.8)\n",
"ax1.scatter([0], [0], [0], c='red', s=200, marker='*', edgecolors='black', linewidths=2)\n",
"ax1.set_xlabel('$x_1$', fontsize=11)\n",
"ax1.set_ylabel('$x_2$', fontsize=11)\n",
"ax1.set_zlabel('$f(x)$', fontsize=11)\n",
"ax1.set_title('Rastrigin Function (3D)', fontsize=13, fontweight='bold')\n",
"\n",
"# Contour plot\n",
"contour = axes[1].contourf(X1, X2, Z, levels=30, cmap='plasma')\n",
"axes[1].scatter([0], [0], c='red', s=200, marker='*', edgecolors='black', linewidths=2, label='Global min', zorder=5)\n",
"axes[1].set_xlabel('$x_1$', fontsize=11)\n",
"axes[1].set_ylabel('$x_2$', fontsize=11)\n",
"axes[1].set_title('Rastrigin Function (Contour)', fontsize=13, fontweight='bold')\n",
"axes[1].legend()\n",
"plt.colorbar(contour, ax=axes[1])\n",
"\n",
"plt.tight_layout()\n",
"plt.show()\n",
"\n",
"print(\"Note: Rastrigin has MANY local minima (visible as the peaks in the plot)\")"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "79deeae5",
"metadata": {
"execution": {
"iopub.execute_input": "2026-02-16T16:12:00.928890Z",
"iopub.status.busy": "2026-02-16T16:12:00.928558Z",
"iopub.status.idle": "2026-02-16T16:12:05.991238Z",
"shell.execute_reply": "2026-02-16T16:12:05.989880Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Optimizing 10D Rastrigin function...\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Best solution: [-0.02184215 -2.1508949 0.15564753 0.86569676 0.02828005 -0.11939509\n",
" 0.91321362 0.0694459 0.94685828 -0.03271556]\n",
"Best value: 2.517248e+01\n",
"Distance to global optimum: 2.674309\n",
"\n",
"⚠ Stuck in local minimum (try increasing popsize or maxiter)\n"
]
}
],
"source": [
"# Optimize Rastrigin\n",
"n_dims = 10\n",
"bounds = [(-5.12, 5.12)] * n_dims\n",
"\n",
"print(f\"Optimizing {n_dims}D Rastrigin function...\")\n",
"x, fun = differential_evolution(\n",
" objective_fn=rastrigin,\n",
" bounds=bounds,\n",
" maxiter=1000,\n",
" popsize=20,\n",
" f=0.9,\n",
" cr=0.9,\n",
" seed=42\n",
")\n",
"\n",
"print(f\"\\nBest solution: {x}\")\n",
"print(f\"Best value: {fun:.6e}\")\n",
"print(f\"Distance to global optimum: {np.linalg.norm(x):.6f}\")\n",
"\n",
"if fun < 1.0:\n",
" print(\"\\n✓ Successfully found global minimum!\")\n",
"else:\n",
" print(\"\\n⚠ Stuck in local minimum (try increasing popsize or maxiter)\")"
]
},
{
"cell_type": "markdown",
"id": "fb324ced",
"metadata": {},
"source": [
"## Example 3: Real-World Application - Portfolio Optimization\n",
"\n",
"Minimize portfolio variance with expected return constraint.\n",
"\n",
"$$\\min_{\\mathbf{w}} \\quad \\mathbf{w}^T \\Sigma \\mathbf{w}$$\n",
"$$\\text{s.t.} \\quad \\mathbf{w}^T \\boldsymbol{\\mu} \\geq r_{\\text{target}}$$\n",
"$$\\sum_i w_i = 1, \\quad w_i \\geq 0$$"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "19b33e87",
"metadata": {
"execution": {
"iopub.execute_input": "2026-02-16T16:12:05.994582Z",
"iopub.status.busy": "2026-02-16T16:12:05.994297Z",
"iopub.status.idle": "2026-02-16T16:12:06.380622Z",
"shell.execute_reply": "2026-02-16T16:12:06.379482Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Portfolio with 10 assets\n",
"Expected returns (daily): [ 0.00157133 -0.00010693 0.00277879 0.00141902 0.00154478 0.00056306\n",
" 0.00144421 0.00249818 0.00340539 0.00288016]\n",
"Annualized returns: [ 0.39597642 -0.0269462 0.70025534 0.35759412 0.38928546 0.14189035\n",
" 0.36394002 0.62954258 0.85815941 0.72579955]\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABKYAAAJOCAYAAACN2Q8zAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjcsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvTLEjVAAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3Xt4VNW9//H3nvvkfiEhCZeEa0AUCaAWRQRBBKktFDVasegRLdRLPfanLa0XbHtqywFrqW21bZRqWxCpWkSwghWVIy1FBcQLCIYESEIg5DbJ3Gf//ggZiQRIICEJfF7PM88zs/faa333np1ovqz13YZpmiYiIiIiIiIiIiKnmaWjAxARERERERERkbOTElMiIiIiIiIiItIhlJgSEREREREREZEOocSUiIiIiIiIiIh0CCWmRERERERERESkQygxJSIiIiIiIiIiHUKJKRERERERERER6RBKTImIiIiIiIiISIdQYkpERERERERERDqEElMiInJGmTdvHoZhYBgGN998c0eHE5WTkxONa926dR0dTousW7cuGnNOTk5HhyMiIiIiZyBbRwcgIiJyPFVVVcyfP59XXnmFzz//nFAoRHJyMhkZGZx33nlMnDiRm266qaPDPG02b97Myy+/DDQkuzpT8u3LZs2aRUFBQfTzyJEj+c9//tOBER1fVVUVjz/+ePTzvHnzWnzsvHnzeOSRR47aHhMTQ+/evRk/fjz3338/vXv3PqUYd+/ezeLFiwFISkrinnvuOaX+RERERDqaYZqm2dFBiIiINKeyspILL7yQnTt3HrPNJZdcwvr166Ofi4uLKS4uBqB79+4MGDCg3eNsiZycHIqKigB48803GTt27En1s3jxYm655RYALrvssnadfbVu3TrGjRsHQHZ2Nrt3727xsfX19WRmZlJTU9Nk+7Zt2xgyZEhbhtlmdu/eTZ8+faKfW/O/SMdKTB0pPT2dzZs3k5mZedIxnsp3IiIiItIZaSmfiIh0Wr/61a+iSanevXvzhz/8gTfeeIOVK1fyi1/8gksuuQSLpel/ynr37s3o0aMZPXp0p0lKnY1efPHFo5JSQHS2z5ksIyODd955h7Vr1/L9738/ur28vJxnnnmmAyNrPa/XSyQS6egwRERE5AymxJSIiHRaGzdujL7/3ve+x6xZs7j88suZMmUK999/P+vXr+fVV19tcsyxakwtXrw4un3s2LH8+9//ZsyYMcTExNCzZ0/mzZtHOBymrKyMm266iZSUFOLi4pgyZQq7du1qMsbYsWOjfX050dK43TCMFs1mWbJkCV//+tfp378/SUlJ2O12UlNTueyyy3j66aebzNoxDCM6WwrgrbfeajLekV5//XW+/vWvk5GRgcPhIC0tja997Wu88847zcbx29/+lkGDBuF0OhkwYAALFy48pYTEn/70p+j7I7+Hv/zlL4TD4aPar169mokTJ5KWlobdbicpKYlBgwbxzW9+k9WrV0fb+f1+fvzjH3P++ecTGxuLw+EgIyODUaNGcffdd1NWVtak371793LPPfcwaNAg3G43cXFxjBgxgl/+8pcEg8Fou7FjxzaZLQVNv8vWzExzOp2MHj2a8ePH8/Of/7zJDLHG2XxH+uSTT5g1axZ9+/bF5XKRkJDAJZdcwuLFi5t8/zk5OdHZUgBFRUVH3W+7d+8+5j3x5Z+BRl+uJbZjxw6+8Y1vkJycTExMDDU1NUcd+9FHH/H1r3+dxMREYmNjueqqq46a2bhv3z6+/e1v07dvX5xOJ263m169enHFFVfw8MMPt/h6ioiIyBnOFBER6aSuv/56EzABMzc311y6dKlZVlZ23GMefvjh6DEzZ86Mbn/mmWei23v27GnGxMREPze+vv3tb5t9+/Y9avs555xjhsPhaF+XXXZZdN8zzzzTZPwjjyssLIxuz87Ojm5/8803o9vz8/OPGu/I13e/+91m+27u1ej73//+MdtYLBbzd7/7XZOYH3rooWbb5uXlRd9nZ2ef8PtqVFxcbFosFhMwHQ6HWVFRYfbr1y/a16uvvtqk/RtvvGEahnHMmL/97W9H237rW9867jXYsGFDtO2GDRvMpKSkY7YdN26c6fP5jvpOm3sd+Z0158j77svXasiQIdF9P/nJT5rse+mll0yXy3XMcW+88UYzEomYptn0HmruVVhYaBYWFjZ7T5hm05+Byy67LLr9zTffjG5PTEw009LSmvRRWVnZ5NjMzEwzNjb2uD8ngUCgyXf+5ZfT6Tzu9RQREZGzh2ZMiYhIpzVlypTo++3bt3P99deTkZFBz549ueGGG1ixYkWr6gA12rt3L6NGjeKVV15hzpw50e1PPfUUVVVVPP300zz33HO43W4APv74Y9asWXPqJ9SMr33tazz55JOsWLGCN998kzfeeIOCggK6desGwBNPPBGdBfTOO+/wwx/+MHrssGHDeOedd6IvaJh59Itf/AIAt9vN/PnzWbNmDQsXLsTpdBKJRLjrrrvYsWMHAIWFhfzP//xPtM+pU6eycuVK/vd//5ePP/74pM7p2Wefjc62mjx5MikpKdx4443R/V+eZfbiiy9Gv8fvfOc7rF27lhUrVvDEE08wbdo0EhISom3/9re/AZCYmMgzzzzDP//5T5YuXcq8efO44IILoks7/X4/+fn5VFVVATB9+nReffVVli9fztChQ4GGWl+N5/7rX/+aF154oUlcR17bvLy8Fp+/3+9n/fr1vPHGG/zoRz/io48+isZ85OyxAwcOcNNNN+Hz+QCYPXs2r732Gs899xzZ2dlAwwyzxuV/y5cvZ9GiRdHjG5cMNr5OpXZVo+rqaoLBII8//jivv/46v/rVr3A6nU3alJaWkpuby9/+9jcef/xx7HY70PTnZMuWLdGZhkOHDuWll15izZo1/OlPf+K73/0u/fv3P+VYRURE5AzR0ZkxERGR47njjjuOO5vm61//enRGiWm2bMaUy+UyKyoqTNM0zfLy8ib9/fa3v40eM2XKlOj2RYsWRbe35YypgwcPmt///vfN8847z4yNjW32XFesWNHseRw566XR9OnTo/tvuukm85133om+rrrqqui+H/zgB6ZpmuaCBQui27p37276/f5oX/fff/9JzZgaOHBg9LgXXnjBNE3T3LFjR5PZMocOHYq2/+EPfxjdt3DhQrOkpOSYfWdlZZmAmZWVZf7f//2f6fF4mm33yiuvRPtMS0sz33777eh1+PWvf91k9k+j4802OpEj77vmXhMmTDC3bdvW5Jgj4zj33HObfFc/+tGPovu+8pWvRI85cnZTc9/Jqc6Y+vL91tyxdrvd3Lt3b3TfpEmTjvo5OfL7Hj9+vPnRRx+ZgUCgVddUREREzg6aMSUiIp3aE088wSeffMJPfvITJk6cSGJiYpP9f//733n++edb1eegQYNISUkBIDU1tcm+UaNGRd83zloCOHToUGtDPyGv18sll1zCL37xCz788EPq6uqanQFWWVnZ4j6PnOX03HPPcemll0Zfq1atiu7btm0bQJO6QCNGjMDhcEQ/X3LJJa06H4B33303OhsrISGBr371qwAMGDCACy64AGiYUbR06dLoMTfddBOxsbFAQy2xrKws4uPjGTVqFPPmzWty7WfPng1ASUkJl1xyCXFxcfTs2ZOvf/3rTe6DI6/DgQMHGDNmTPQ63HXXXdF9paWlVFRUtPo8W+uDDz7gwIEDTbYdGeO2bduafFdHzmJr/K5OB6fTGf3OjmXQoEH06NEj+vnIn6HG76p///5MmDABgDfeeIMhQ4bgdrvJzc3l5ptv5l//+lc7RC8iIiJdkRJTIiLS6eXm5vLAAw/wj3/8g4qKCl577TWSk5Oj+//973+3qr8jk1tffqpfUlJSs8eYXypC3igUCkXffznxcCIvvfQS27dvByA2NpZFixbx5ptv8s4773DeeedF27XHU9E8Hk+b9wlNl+nV1NTgdrujRbP/85//NNtu0KBBbN68mblz53LZZZeRmZmJx+PhX//6F4888ghXXnlltGD6gw8+yIoVK5g5cyZ5eXnExcWxb98+VqxYwfXXX8+vfvWrVsf
"text/plain": [
"<Figure size 1200x600 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Generate synthetic asset data\n",
"n_assets = 10\n",
"n_periods = 252 # 1 year of daily data\n",
"\n",
"# Simulate correlated returns\n",
"np.random.seed(42)\n",
"mean_returns = np.random.uniform(0.0005, 0.002, n_assets) # Daily returns\n",
"returns = np.random.multivariate_normal(\n",
" mean=mean_returns,\n",
" cov=np.diag(np.random.uniform(0.01, 0.03, n_assets)**2),\n",
" size=n_periods\n",
")\n",
"\n",
"# Compute statistics\n",
"mu = returns.mean(axis=0) # Expected returns\n",
"Sigma = np.cov(returns.T) # Covariance matrix\n",
"\n",
"print(f\"Portfolio with {n_assets} assets\")\n",
"print(f\"Expected returns (daily): {mu}\")\n",
"print(f\"Annualized returns: {mu * 252}\")\n",
"\n",
"# Visualize returns\n",
"plt.figure(figsize=(12, 6))\n",
"cumulative_returns = np.cumprod(1 + returns, axis=0) - 1\n",
"for i in range(n_assets):\n",
" plt.plot(cumulative_returns[:, i], alpha=0.6, label=f'Asset {i+1}')\n",
"plt.xlabel('Days', fontsize=11)\n",
"plt.ylabel('Cumulative Return', fontsize=11)\n",
"plt.title('Simulated Asset Returns', fontsize=13, fontweight='bold')\n",
"plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')\n",
"plt.grid(alpha=0.3)\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "348c49cb",
"metadata": {
"execution": {
"iopub.execute_input": "2026-02-16T16:12:06.384302Z",
"iopub.status.busy": "2026-02-16T16:12:06.384003Z",
"iopub.status.idle": "2026-02-16T16:12:08.123468Z",
"shell.execute_reply": "2026-02-16T16:12:08.122370Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Optimizing portfolio allocation...\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Optimal Portfolio:\n",
"Weights: [0.52655741 0.54272521 0.6364651 0.40590176 0.03434282 0.41495685\n",
" 0.73742443 0.8492516 0.13127889 0.0032521 ]\n",
"Sum of weights: 4.282156\n",
"\n",
"Expected daily return: 0.007044 (177.50% annualized)\n",
"Daily volatility: 0.031613 (50.18% annualized)\n",
"Sharpe ratio (assuming 0% risk-free): 0.2228\n"
]
}
],
"source": [
"def portfolio_objective(weights):\n",
" \"\"\"\n",
" Minimize: variance + penalty for constraint violations.\n",
" \"\"\"\n",
" # Portfolio variance\n",
" variance = weights @ Sigma @ weights\n",
" \n",
" # Constraints (penalize violations)\n",
" target_return = 0.0015 # Target daily return\n",
" return_constraint = max(0, target_return - weights @ mu)\n",
" sum_constraint = abs(weights.sum() - 1.0)\n",
" negative_constraint = max(0, -weights.min())\n",
" \n",
" # Penalize constraint violations heavily\n",
" penalty = 1000 * (return_constraint + sum_constraint + negative_constraint)\n",
" \n",
" return variance + penalty\n",
"\n",
"# Optimize\n",
"bounds = [(0, 1)] * n_assets # Weights between 0 and 1\n",
"\n",
"print(\"Optimizing portfolio allocation...\")\n",
"x, fun = differential_evolution(\n",
" objective_fn=portfolio_objective,\n",
" bounds=bounds,\n",
" maxiter=500,\n",
" popsize=20,\n",
" seed=42\n",
")\n",
"\n",
"optimal_weights = x\n",
"optimal_return = optimal_weights @ mu\n",
"optimal_volatility = np.sqrt(optimal_weights @ Sigma @ optimal_weights)\n",
"\n",
"print(f\"\\nOptimal Portfolio:\")\n",
"print(f\"Weights: {optimal_weights}\")\n",
"print(f\"Sum of weights: {optimal_weights.sum():.6f}\")\n",
"print(f\"\\nExpected daily return: {optimal_return:.6f} ({optimal_return * 252:.2%} annualized)\")\n",
"print(f\"Daily volatility: {optimal_volatility:.6f} ({optimal_volatility * np.sqrt(252):.2%} annualized)\")\n",
"print(f\"Sharpe ratio (assuming 0% risk-free): {optimal_return / optimal_volatility:.4f}\")"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "75325f0a",
"metadata": {
"execution": {
"iopub.execute_input": "2026-02-16T16:12:08.127014Z",
"iopub.status.busy": "2026-02-16T16:12:08.126714Z",
"iopub.status.idle": "2026-02-16T16:12:08.530507Z",
"shell.execute_reply": "2026-02-16T16:12:08.529120Z"
}
},
"outputs": [
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 1400x500 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Visualize allocation\n",
"fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n",
"\n",
"# Bar chart\n",
"axes[0].bar(range(n_assets), optimal_weights, color='steelblue', edgecolor='black')\n",
"axes[0].set_xlabel('Asset', fontsize=11)\n",
"axes[0].set_ylabel('Weight', fontsize=11)\n",
"axes[0].set_title('Optimal Portfolio Allocation', fontsize=13, fontweight='bold')\n",
"axes[0].grid(alpha=0.3, axis='y')\n",
"\n",
"# Pie chart\n",
"nonzero_weights = optimal_weights[optimal_weights > 0.01]\n",
"nonzero_assets = [f'Asset {i+1}' for i in range(n_assets) if optimal_weights[i] > 0.01]\n",
"axes[1].pie(nonzero_weights, labels=nonzero_assets, autopct='%1.1f%%', startangle=90)\n",
"axes[1].set_title('Portfolio Composition', fontsize=13, fontweight='bold')\n",
"\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "7ed601b8",
"metadata": {},
"source": [
"## Key Takeaways\n",
"\n",
"1. **DE is excellent for non-convex, multimodal problems** where gradient-based methods fail\n",
"2. **Population-based approach** explores solution space thoroughly\n",
"3. **Few hyperparameters** - typically F ∈ [0.5, 1], CR ∈ [0.7, 1]\n",
"4. **Robust** - works well on wide variety of problems\n",
"5. **OptimizR provides 50-100x speedup** over pure Python\n",
"6. **Real-world applications** - engineering, ML, finance, science\n",
"\n",
"## Further Reading\n",
"\n",
"- Storn & Price (1997). \"Differential evolutiona simple and efficient heuristic for global optimization\"\n",
"- Price, Storn & Lampinen (2005). \"Differential Evolution: A Practical Approach to Global Optimization\""
]
}
],
"metadata": {
2026-01-06 14:36:08 +01:00
"kernelspec": {
"display_name": "rhftlab",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
2026-01-06 14:36:08 +01:00
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}