{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "541f3ecd", "metadata": {}, "outputs": [], "source": [ "# DAOUST Alexandre 242015" ] }, { "cell_type": "code", "execution_count": 2, "id": "c68d497b", "metadata": {}, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "code", "execution_count": 3, "id": "018d8af6", "metadata": {}, "outputs": [], "source": [ "def fct(x, nargout=2):\n", " x1, x2 = x\n", " \n", " f = 2*x1**3 + 3*x2**3 + 2*x1**2 + 4*x2**2 + x1*x2 - 3*x1 - 2*x2\n", " g = np.array([6*x1**2 + 4*x1 + x2 - 3,\n", " 6*x2**2 + 8*x2 + x1 - 2])\n", " if nargout==3:\n", " H = np.array([[12*x1 + 4, 1 ],\n", " [ 1, 12*x2 + 8]])\n", " return f, g, H\n", " return f, g" ] }, { "cell_type": "code", "execution_count": 4, "id": "4ab88c2f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "f = 19.0000\n", "g = |28.0000|\n", " |-2.0000|\n", "H = |28.0000 1.0000|\n", " |1.0000 -4.0000|\n", "\n", "f = 0.0000\n", "g = |-3.0000|\n", " |-2.0000|\n", "H = |4.0000 1.0000|\n", " |1.0000 8.0000|\n" ] } ], "source": [ "# Test de f en 2,-1\n", "x = np.array([2.0, -1.0])\n", "f, g, H = fct(x, nargout=3)\n", "print(f\"f = {f:.4f}\")\n", "print(f\"g = |{g[0]:.4f}|\\n |{g[1]:.4f}|\")\n", "print(f\"H = |{H[0][0]:.4f} {H[0][1]:.4f}|\\n |{H[1][0]:.4f} {H[1][1]:.4f}|\")\n", "print()\n", "\n", "# Test de f en 0,0\n", "x = np.array([0.0, 0.0])\n", "f, g, H = fct(x, nargout=3)\n", "print(f\"f = {f:.4f}\")\n", "print(f\"g = |{g[0]:.4f}|\\n |{g[1]:.4f}|\")\n", "print(f\"H = |{H[0][0]:.4f} {H[0][1]:.4f}|\\n |{H[1][0]:.4f} {H[1][1]:.4f}|\")\n", "# On a bien le bon résultat" ] }, { "cell_type": "code", "execution_count": 5, "id": "958a11f2", "metadata": {}, "outputs": [], "source": [ "def CoordinateDescent(x0, maxiter=10):\n", " # Formules calculées à la main selon x1 et x2 respectivement\n", " # Elles correspondent à f(xi) = Somme(coeff*xi^n) + C (C constante reprennant le(s) xj!=xi actuel(s) et\n", " # les autres constantes) qu'on dérive et qu'on optimise.\n", " x = x0.copy()\n", " n = len(x)\n", " \n", " for i in range(maxiter):\n", " # Mise à jour de x1\n", " delta = 4**2-4*6*(x[1]-3)\n", " x[0] = (-4 + np.sqrt(delta))/12\n", " \n", " #Mise à jour de x2\n", " delta = 8**2 - 4*6*(x[0]-2)\n", " x[1] = (-8 + np.sqrt(delta))/12\n", " return x" ] }, { "cell_type": "code", "execution_count": 6, "id": "0b6c0173", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x* = |0.4297|\n", " |0.1737|\n", "f = -0.8975\n", "g = |-0.0000|\n", " |-0.0000|\n", "H = |9.1560 1.0000|\n", " |1.0000 10.0840|\n", "Valeurs propres:\n", " 8.5176\n", " 10.7224\n" ] } ], "source": [ "# Test de CoordinateDescent\n", "\n", "# On trouve bien un minimum car la gradient est nul (à une tolérance près) et la matrice hessienne est définie positive\n", "# (on augmente f si on se déplace du point)\n", "\n", "x = np.array([2.0, -1.0])\n", "x_opt = CoordinateDescent(x)\n", "print(f\"x* = |{x_opt[0]:.4f}|\\n |{x_opt[1]:.4f}|\")\n", "f, g, H = fct(x_opt, nargout=3)\n", "print(f\"f = {f:.4f}\")\n", "print(f\"g = |{g[0]:.4f}|\\n |{g[1]:.4f}|\")\n", "print(f\"H = |{H[0][0]:.4f} {H[0][1]:.4f}|\\n |{H[1][0]:.4f} {H[1][1]:.4f}|\")\n", "eigval, eigvec = np.linalg.eig(H)\n", "print(f\"Valeurs propres:\\n {eigval[0]:.4f}\\n {eigval[1]:.4f}\")" ] }, { "cell_type": "code", "execution_count": 7, "id": "230d6b8b", "metadata": {}, "outputs": [], "source": [ "def w1(fct, x, d, alpha, beta1):\n", " f0, g0 = fct(x)\n", " f1, g1 = fct(x + alpha*d)\n", " return f1 <= f0 + alpha*beta1*(g0@d)\n", "\n", "def w2(fct, x, d, alpha, beta2):\n", " f0, g0 = fct(x)\n", " f1, g1 = fct(x + alpha*d)\n", " return g1@d >= beta2*(g0@d)\n", "\n", "def wolfebissection(fct, x, d, alpha=1, beta1=0.0001, beta2=0.9):\n", " alphal = 0\n", " alphar = np.inf\n", " \n", " wf1 = False\n", " wf2 = False\n", " \n", " # k pour ne pas boucler à l'infini\n", " k = 0\n", " \n", " while not (wf1 and wf2) and k < 1e4:\n", " wf1 = w1(fct, x, d, alpha, beta1)\n", " wf2 = w2(fct, x, d, alpha, beta2)\n", " \n", " if not wf1:\n", " alphar = alpha\n", " alpha = (alphal + alphar)/2\n", " \n", " elif not wf2:\n", " alphal = alpha\n", " if alphar < np.inf:\n", " alpha = (alphal + alphar)/2\n", " else:\n", " alpha *= 2\n", " \n", " k += 1\n", " \n", " return alpha" ] }, { "cell_type": "code", "execution_count": 8, "id": "14f1cafd", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "alpha = 0.1250\n" ] } ], "source": [ "# Test de wolfebissection\n", "x = np.array([0.0, 0.0])\n", "d = np.array([3.0, 2.0])\n", "alpha = wolfebissection(fct, x, d)\n", "print(f\"alpha = {alpha:.4f}\")" ] }, { "cell_type": "code", "execution_count": 9, "id": "2e390c95", "metadata": {}, "outputs": [], "source": [ "def MethGradient(x0, maxiter=10):\n", " x = x0.copy()\n", " \n", " for k in range(maxiter):\n", " f, g = fct(x)\n", " d = -g\n", " alpha = wolfebissection(fct, x, d)\n", " x = x + alpha*d\n", " \n", " if np.linalg.norm(x) < 1e-9:\n", " break\n", " \n", " return x" ] }, { "cell_type": "code", "execution_count": 10, "id": "07f82a85", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x* = |0.4291|\n", " |0.1674|\n", "f = -0.8978\n", "g = |-0.0116|\n", " |-0.0640|\n", "H = |9.1490 1.0000|\n", " |1.0000 10.0083|\n", "Valeurs propres:\n", " 8.4903\n", " 10.6671\n" ] } ], "source": [ "# Test de MethGradient\n", "\n", "# La méthode du Gradient se raproche de la solution (x1 est correct, x2 est légèrement différent) mais n'y converge pas à cause\n", "# du calcul du pas alpha qui ne donne pas le pas exact pour arriver au minimum (autrement dit, on n'arrive pas à calculer\n", "# le pas alpha permettant de converger vers le minimum). Au plus on est proche, au plus le calcul d'un alpha admissible prend\n", "# du temps.\n", "\n", "x = np.array([0.0, 0.0])\n", "x_opt = MethGradient(x)\n", "print(f\"x* = |{x_opt[0]:.4f}|\\n |{x_opt[1]:.4f}|\")\n", "f, g, H = fct(x_opt, nargout=3)\n", "print(f\"f = {f:.4f}\")\n", "print(f\"g = |{g[0]:.4f}|\\n |{g[1]:.4f}|\")\n", "print(f\"H = |{H[0][0]:.4f} {H[0][1]:.4f}|\\n |{H[1][0]:.4f} {H[1][1]:.4f}|\")\n", "eigval, eigvec = np.linalg.eig(H)\n", "print(f\"Valeurs propres:\\n {eigval[0]:.4f}\\n {eigval[1]:.4f}\")" ] }, { "cell_type": "code", "execution_count": 11, "id": "1efec1bb", "metadata": {}, "outputs": [], "source": [ "def MethNewton(x0, maxiter=10):\n", " x = x0.copy()\n", " \n", " for k in range(maxiter):\n", " f, g, H = fct(x, nargout=3)\n", " z = np.linalg.solve(H,g)\n", " \n", " x = x - z\n", " \n", " if np.linalg.norm(x) < 1e-9:\n", " break\n", " \n", " return x" ] }, { "cell_type": "code", "execution_count": 12, "id": "f151f070", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x* = |-1.2757|\n", " |-1.6619|\n", "f = 5.6516\n", "g = |0.0000|\n", " |-0.0000|\n", "H = |-11.3086 1.0000|\n", " |1.0000 -11.9422|\n", "Valeurs propres:\n", " -10.5764\n", " -12.6744\n" ] } ], "source": [ "# Test de MethNewton\n", "\n", "# La méthode de Newton est très sensible au point de départ, cela peut conduire (comme c'est le cas ici) à trouver un point\n", "# qui n'est pas un minimum mais un maximum.\n", "\n", "x = np.array([-1.0, -1.0])\n", "x_opt = MethNewton(x)\n", "print(f\"x* = |{x_opt[0]:.4f}|\\n |{x_opt[1]:.4f}|\")\n", "f, g, H = fct(x_opt, nargout=3)\n", "print(f\"f = {f:.4f}\")\n", "print(f\"g = |{g[0]:.4f}|\\n |{g[1]:.4f}|\")\n", "print(f\"H = |{H[0][0]:.4f} {H[0][1]:.4f}|\\n |{H[1][0]:.4f} {H[1][1]:.4f}|\")\n", "eigval, eigvec = np.linalg.eig(H)\n", "print(f\"Valeurs propres:\\n {eigval[0]:.4f}\\n {eigval[1]:.4f}\")" ] }, { "cell_type": "code", "execution_count": 13, "id": "b023fe44", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x* = |0.4297|\n", " |0.1737|\n", "f = -0.8975\n", "g = |0.0000|\n", " |0.0000|\n", "H = |9.1560 1.0000|\n", " |1.0000 10.0840|\n", "Valeurs propres:\n", " 8.5176\n", " 10.7224\n" ] } ], "source": [ "# Validation du point calculé par la CoordinateDescent (et par la MethGradient aussi)\n", "\n", "# On trouve bien un minimum car la gradient est nul (à une tolérance près) et la matrice hessienne est définie positive\n", "# (on augmente f si on se déplace du point)\n", "\n", "x = np.array([0.0, 0.0])\n", "x_opt = MethNewton(x)\n", "print(f\"x* = |{x_opt[0]:.4f}|\\n |{x_opt[1]:.4f}|\")\n", "f, g, H = fct(x_opt, nargout=3)\n", "print(f\"f = {f:.4f}\")\n", "print(f\"g = |{g[0]:.4f}|\\n |{g[1]:.4f}|\")\n", "print(f\"H = |{H[0][0]:.4f} {H[0][1]:.4f}|\\n |{H[1][0]:.4f} {H[1][1]:.4f}|\")\n", "eigval, eigvec = np.linalg.eig(H)\n", "print(f\"Valeurs propres:\\n {eigval[0]:.4f}\\n {eigval[1]:.4f}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "e3b81f36", "metadata": {}, "outputs": [], "source": [] } ], "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.13" } }, "nbformat": 4, "nbformat_minor": 5 }