{ "cells": [ { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/bgoujaud/PEPit/blob/master/ressources/demo/PEPit_demo_extracting_a_proof.ipynb)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# PEPit : numerical examples of worst-case analyses" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook provides:\n", "- two simple examples illustrating how to extract numerical values for the dual variables to analyse **gradient descent** in the smooth and strongly convex setting.\n", "- We show how to search for simplified versions of the proofs, if possible, by explicitly removing certain constraints from the performance estimation problem and by observing they play no role in certain performance guarantees.\n", "- Finally, we show how to leverage those numerical findings to find a convergence proof with SymPy. More details about such strategies can be found in the repository [learning performance estimation](https://github.com/PerformanceEstimation/Learning-Performance-Estimation), from which those examples were taken.\n", "\n", "The examples below are taken from [this blog post](https://francisbach.com/computer-aided-analyses/) and in the [habilitation thesis](https://hal.science/tel-04794552v2/file/HDR_ATaylor_V_HAL2.pdf) that contain a more mathematical introduction to performance estimation problems." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook is organized as follows:\n", "* [1. Gradient descent, distance to a solution](#example1) : numerical study of **gradient descent** for smooth strongly convex minimization, convergence in terms of $\\frac{\\|x_{k+1}-x_\\star\\|^2}{\\|x_k-x_\\star\\|^2}$ (basic example).\n", "* [2. Gradient descent, function value accuracy](#example2) : numerical study of **gradient descent** for smooth strongly convex minimization, convergence in terms of $\\frac{f(x_{k+1})-f_\\star}{f(x_{k})-f_\\star}$ (more complicated example).\n", "* [3. Function values: numerical proof simplification](#example3) : a base (greedy) approach to search for which inequalities are needed for a proof of convergence in terms of $\\frac{f(x_{k+1})-f_\\star}{f(x_{k})-f_\\star}$.\n", "* [4. Using SymPy for the distance analysis](#example4) : leverage the SymPy package to get a closed form solution (and proof) for the analysis of gradient descent in terms of $\\frac{\\|x_{k+1}-x_\\star\\|^2}{\\|x_k-x_\\star\\|^2}$ (basic example).\n", "* [5. Using SymPy for the function value accuracy analysis](#example5) : leverage the SymPy package to get a closed form solution (and proof) for the analysis of gradient descent in terms of $\\frac{f(x_{k+1})-f_\\star}{f(x_{k})-f_\\star}$ (more technical and illustrates why step [3.](#example3) is useful/needed.\n", "* [6. Using symbolic regression (PySR package) for guessing closed-forms](#example6)" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "tags": [] }, "outputs": [], "source": [ "# import PEPit and the required function class\n", "from PEPit import PEP\n", "from PEPit.functions import SmoothStronglyConvexFunction\n", "# import numpy and matplotlib\n", "import numpy as np\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "tags": [] }, "outputs": [], "source": [ "# set up the smoothness and strong convexity constant for the whole notebook\n", "L, mu = 1, .1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## Table of Contents\n", "- [1. Gradient descent, distance to a solution](#example1)\n", "- [2. Gradient descent, function value accuracy](#example2)\n", "- [3. Function values: numerical proof simplification](#example3)\n", "- [4. Using SymPy for the distance analysis](#example4)\n", "- [5. Using SymPy for the function value accuracy analysis](#example5)\n", "- [6. Using symbolic regression (PySR)](#example6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Gradient descent, distance to a solution " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We start by a direct attempt: fix class parameters $L,\\mu$ (chosen above) and experiment with different step size values $\\gamma$. Verify that the obtained convergence rate is indeed smaller than one only when $\\gamma\\in\\left(0,\\frac{2}{L}\\right)$. Further verify that it matches the very well-known $\\max\\{(1-\\gamma L)^2,(1-\\gamma\\mu)^2\\}$." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "tags": [] }, "outputs": [], "source": [ "def wc_gradient_descent_distance(mu,L,gamma, verbose):\n", " # Instantiate PEP\n", " problem = PEP()\n", "\n", " # Declare a smooth convex function\n", " f = problem.declare_function(SmoothStronglyConvexFunction, L=L, mu=mu)\n", " \n", " # Start by defining its unique optimal point xs = x_* and corresponding function value fs = f_*\n", " xs = f.stationary_point()\n", " fs = f(xs)\n", " \n", " # Then define the starting point x0 of the algorithm\n", " x0 = problem.set_initial_point()\n", " \n", " # Set the initial constraint that is the distance between x0 and x^*\n", " problem.set_initial_condition((x0 - xs) ** 2 <= 1)\n", " \n", " # Run n steps of the GD method\n", " x1 = x0 - gamma * f.gradient(x0)\n", " \n", " # Set the performance metric to the function values accuracy\n", " problem.set_performance_metric( (x1 - xs) ** 2 )\n", " \n", " # Solve the PEP\n", " pepit_verbose = max(verbose, 0)\n", " pepit_tau = problem.solve(verbose=pepit_verbose)\n", " \n", " return pepit_tau, problem._list_of_prepared_constraints # outputs optimal value and list of constraints (with their associated dual variables)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following pieces of code compute and plot the numerical worst-case guarantees as a function of the tested step size value $\\gamma$, for fixed $\\mu,L$." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "tags": [] }, "outputs": [], "source": [ "verbose = 0\n", "\n", "gamma_min, gamma_max = -1, 3\n", "nb_gammas = 50\n", "gamma_list = np.linspace(gamma_min,gamma_max,nb_gammas)\n", "\n", "pepit_worst_case_value = list()\n", "known_worst_case_value = list()\n", "\n", "for gamma in gamma_list:\n", " pepit_tau, _ = wc_gradient_descent_distance(mu,L,gamma, verbose)\n", " pepit_worst_case_value.append(pepit_tau)\n", " known_worst_case_value.append(max((1-gamma*L)**2,(1-gamma*mu)**2))" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "tags": [] }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plt.plot(gamma_list, pepit_worst_case_value, color='red', linestyle='-', linewidth=3, label='PEPit')\n", "\n", "plt.plot(gamma_list, known_worst_case_value, color='blue', linestyle='--', linewidth=2, label='Known')\n", "\n", "plt.legend()\n", "plt.xlabel(r'$\\gamma$')\n", "plt.ylabel(r'$\\frac{\\|x_1 - x_\\star\\|^2}{\\|x_0 - x_\\star\\|^2}$')\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One can admit that the match between PEPit's results and the known convergence rate is pretty good. For completeness, let us also extract the dual variables. The next lines examplify how to do this extraction for given values of the parameters, and we integrate it in a loop for plotting below." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Constraint \"Performance metric 1\" value: 0.9999999999866084\n", "Constraint \"Initial condition\" value: 0.8100001805827125\n", "Constraint \"IC_Function_0_smoothness_strong_convexity(Point_0, Point_1)\" value: 1.7999998617137993\n", "Constraint \"IC_Function_0_smoothness_strong_convexity(Point_1, Point_0)\" value: 1.7999998617138078\n" ] } ], "source": [ "verbose = 0\n", "gamma = 1/L\n", "\n", "pepit_tau, list_of_constraints = wc_gradient_descent_distance(mu,L,gamma, verbose)\n", "\n", "nb_cons = len(list_of_constraints)\n", "\n", "for i in range(nb_cons):\n", " print('Constraint \\\"{}\\\" value: {}'.format(list_of_constraints[i].get_name(),\n", " list_of_constraints[i]._dual_variable_value))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One can observe that the performance estimation problem is actually involving 4 constraints, 2 of which are of interest for us: the 3rd and 4th ones (that encode the fact the function is smooth and strongly convex). We can therefore extract their values in a loop, plot them, and expect their closed-forms to be nice. Perhaps one could even guess their values." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "tags": [] }, "outputs": [], "source": [ "verbose = 0\n", "\n", "gamma_min, gamma_max = -1, 3\n", "nb_gammas = 50\n", "gamma_list = np.linspace(gamma_min,gamma_max,nb_gammas)\n", "\n", "pepit_worst_case_value = list()\n", "pepit_dual_value1 = list()\n", "pepit_dual_value2 = list()\n", "known_worst_case_value = list()\n", "\n", "for gamma in gamma_list:\n", " pepit_tau, list_of_constraints = wc_gradient_descent_distance(mu,L,gamma, verbose)\n", " pepit_worst_case_value.append(pepit_tau)\n", " known_worst_case_value.append(max((1-gamma*L)**2,(1-gamma*mu)**2))\n", " pepit_dual_value1.append(list_of_constraints[2]._dual_variable_value)\n", " pepit_dual_value2.append(list_of_constraints[3]._dual_variable_value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Playing a bit with candidate expressions, one can guess a closed-form for the multipliers (which happen to be always equal): $\\lambda=2|\\gamma| \\rho(\\gamma)$ with $\\rho(\\gamma)=\\max\\{|1-\\gamma L|,|1-\\gamma\\mu|\\}$." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "tags": [] }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "candidate_lambda = [ max(abs(1-gamma*L),abs(1-gamma*mu))*2*abs(gamma) for gamma in gamma_list ]\n", "\n", "plt.plot(gamma_list, pepit_dual_value1, color='red', linestyle='-', linewidth=3, label='PEPit')\n", "plt.plot(gamma_list, candidate_lambda, color='blue', linestyle='--', linewidth=2, label='Educated guess')\n", "\n", "plt.legend()\n", "plt.xlabel(r'$\\gamma$')\n", "plt.ylabel(r'$\\lambda(\\gamma)$')\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Section [4](#example4) below explores how to transform that into a formal mathematical proof (that include replacing the actual guessing game by formal mathematical steps). In the meantime, we explore a case where things are directly less convenient, that of function values. In fact, this slightly more complicated problem already show a typical feature appearing in computer-aided analyses: non-uniqueness of the proofs. This might seem like a good thing (in fact, it is, in many aspects), but it actually renders the formal translation and search more painful, as the numerical solvers actually generally do not provide the simplest proofs possible, but rather combinations of them and fail providing the *sparse* (and likely simpler) ones." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
↩ Back to TOC
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Gradient descent, function value accuracy " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let us continue with the a similar exercise, but now by changing the specific quantity under our radar to function values. Practically speaking, this means changing both the performance metric and the initial condition in the previous code. Executing the code in the same fashion as before, we observe the exact same worst-case ratios." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "tags": [] }, "outputs": [], "source": [ "def wc_gradient_descent_function_values(mu,L,gamma, verbose):\n", " # Instantiate PEP\n", " problem = PEP()\n", "\n", " # Declare a smooth convex function\n", " f = problem.declare_function(SmoothStronglyConvexFunction, L=L, mu=mu)\n", " \n", " # Start by defining its unique optimal point xs = x_* and corresponding function value fs = f_*\n", " xs = f.stationary_point()\n", " fs = f(xs)\n", " \n", " # Then define the starting point x0 of the algorithm\n", " x0 = problem.set_initial_point()\n", " \n", " # Set the initial constraint that is the distance between x0 and x^*\n", " problem.set_initial_condition( f(x0) - fs <= 1)\n", " \n", " # Run n steps of the GD method\n", " x1 = x0 - gamma * f.gradient(x0)\n", " \n", " # Set the performance metric to the function values accuracy\n", " problem.set_performance_metric( f(x1) - fs )\n", " \n", " # Solve the PEP\n", " pepit_verbose = max(verbose, 0)\n", " pepit_tau = problem.solve(verbose=pepit_verbose)\n", " \n", " return pepit_tau, problem._list_of_prepared_constraints # outputs optimal value and list of constraints (with their associated dual variables)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "tags": [] }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "verbose = 0\n", "\n", "gamma_min, gamma_max = -1, 3\n", "nb_gammas = 50\n", "gamma_list = np.linspace(gamma_min,gamma_max,nb_gammas)\n", "\n", "pepit_worst_case_value = list()\n", "known_worst_case_value = list()\n", "\n", "for gamma in gamma_list:\n", " pepit_tau, _ = wc_gradient_descent_function_values(mu,L,gamma, verbose)\n", " pepit_worst_case_value.append(pepit_tau)\n", " known_worst_case_value.append(max((1-gamma*L)**2,(1-gamma*mu)**2))\n", " \n", "plt.plot(gamma_list, pepit_worst_case_value, color='red', linestyle='-', linewidth=3, label='PEPit')\n", "\n", "plt.plot(gamma_list, known_worst_case_value, color='blue', linestyle='--', linewidth=2, label='Known')\n", "\n", "plt.legend()\n", "plt.xlabel(r'$\\gamma$')\n", "plt.ylabel(r'$\\frac{f(x_1) - f_\\star}{f(x_0) - f_\\star}$')\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, a bad surprise appears when inspecting the problem: there are actually more constraints, and hence possibly more dual multipliers to be identified. We do not describe here the reason for this difference (more constraints, explained at length, e.g., in [this blog post](https://francisbach.com/computer-aided-analyses/) or this [habilitation thesis](https://hal.science/tel-04794552v2/file/HDR_ATaylor_V_HAL2.pdf)) and instead focus on the consequences. First, let us investigate a bit the values of those (additional) dual variables." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Constraint \"Performance metric 1\" value: 1.000000000000004\n", "Constraint \"Initial condition\" value: 0.8099999986909497\n", "Constraint \"IC_Function_0_smoothness_strong_convexity(Point_0, Point_1)\" value: 0.07413167584202415\n", "Constraint \"IC_Function_0_smoothness_strong_convexity(Point_0, Point_2)\" value: 0.11586832639046385\n", "Constraint \"IC_Function_0_smoothness_strong_convexity(Point_1, Point_0)\" value: 1.1349094168952335e-09\n", "Constraint \"IC_Function_0_smoothness_strong_convexity(Point_1, Point_2)\" value: 1.0428149257255546\n", "Constraint \"IC_Function_0_smoothness_strong_convexity(Point_2, Point_0)\" value: 1.2819965105035798e-09\n", "Constraint \"IC_Function_0_smoothness_strong_convexity(Point_2, Point_1)\" value: 0.1586832515672777\n" ] } ], "source": [ "verbose = 0\n", "gamma = 1/L\n", "\n", "pepit_tau, list_of_constraints = wc_gradient_descent_function_values(mu,L,gamma, verbose)\n", "\n", "nb_cons = len(list_of_constraints)\n", "\n", "for i in range(nb_cons):\n", " print('Constraint \\\"{}\\\" value: {}'.format(list_of_constraints[i].get_name(),\n", " list_of_constraints[i]._dual_variable_value))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To get a clearer picture, let us plot all these values as a function of $\\gamma$ again. The code below is intentionally a bit raw for readability purposes---there are six dual variables to explore." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "tags": [] }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "verbose = 0\n", "\n", "gamma_min, gamma_max = -1, 3\n", "nb_gammas = 50\n", "gamma_list = np.linspace(gamma_min,gamma_max,nb_gammas)\n", "\n", "pepit_worst_case_value = list()\n", "pepit_dual_value1 = list()\n", "pepit_dual_value2 = list()\n", "pepit_dual_value3 = list()\n", "pepit_dual_value4 = list()\n", "pepit_dual_value5 = list()\n", "pepit_dual_value6 = list()\n", "known_worst_case_value = list()\n", "\n", "for gamma in gamma_list:\n", " pepit_tau, list_of_constraints = wc_gradient_descent_function_values(mu,L,gamma, verbose)\n", " pepit_worst_case_value.append(pepit_tau)\n", " known_worst_case_value.append(max((1-gamma*L)**2,(1-gamma*mu)**2))\n", " pepit_dual_value1.append(list_of_constraints[2]._dual_variable_value)\n", " pepit_dual_value2.append(list_of_constraints[3]._dual_variable_value)\n", " pepit_dual_value3.append(list_of_constraints[4]._dual_variable_value)\n", " pepit_dual_value4.append(list_of_constraints[5]._dual_variable_value)\n", " pepit_dual_value5.append(list_of_constraints[6]._dual_variable_value)\n", " pepit_dual_value6.append(list_of_constraints[7]._dual_variable_value)\n", " \n", " \n", "plt.plot(gamma_list, pepit_dual_value1, color='red', linestyle='-', linewidth=3, label=r'$\\lambda_1$')\n", "plt.plot(gamma_list, pepit_dual_value2, color='blue', linestyle='-', linewidth=3, label=r'$\\lambda_2$')\n", "plt.plot(gamma_list, pepit_dual_value3, color='green', linestyle='-', linewidth=3, label=r'$\\lambda_3$')\n", "plt.plot(gamma_list, pepit_dual_value4, color='gold', linestyle='-', linewidth=3, label=r'$\\lambda_4$')\n", "plt.plot(gamma_list, pepit_dual_value5, color='orange', linestyle='-', linewidth=3, label=r'$\\lambda_5$')\n", "plt.plot(gamma_list, pepit_dual_value6, color='brown', linestyle='-', linewidth=3, label=r'$\\lambda_6$')\n", "\n", "plt.legend()\n", "plt.xlabel(r'$\\gamma$')\n", "plt.ylabel(r'$\\lambda_i(\\gamma)$')\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Well, guessing closed-forms appears like a much more challenging game now. The next section illustrates that one can actually simplify this picture simply by removing certain of the constraints (or equivalently by forcing their multipliers to be zero)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
↩ Back to TOC
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Function values: numerical proof simplification " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The game for this section consists in forcing certain multipliers to be zero (deactivating constraints) while keeping the same optimal value. In other words, we search for possibly *sparse* proofs. For doing that, we must experiment with solve/resolve the PEP with different constraints patterns. In PEPit, this can be done through the *prepare_problem* command (instead of solve) as follows:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(PEPit) Setting up the problem: performance measure is the minimum of 1 element(s)\n", "(PEPit) Setting up the problem: Adding initial conditions and general constraints ...\n", "(PEPit) Setting up the problem: initial conditions and general constraints (1 constraint(s) added)\n", "(PEPit) Setting up the problem: interpolation conditions for 1 function(s)\n", "\t\t\tFunction 1 : Adding 6 scalar constraint(s) ...\n", "\t\t\tFunction 1 : 6 scalar constraint(s) added\n", "(PEPit) Setting up the problem: additional constraints for 0 function(s)\n" ] } ], "source": [ "def wc_gradient_descent_function_values_prepare(mu,L,gamma, verbose):\n", " # Instantiate PEP\n", " problem = PEP()\n", "\n", " # Declare a smooth convex function\n", " f = problem.declare_function(SmoothStronglyConvexFunction, L=L, mu=mu, name=\"f\")\n", " \n", " # Start by defining its unique optimal point xs = x_* and corresponding function value fs = f_*\n", " xs = f.stationary_point(name=\"xs\")\n", " fs = f(xs)\n", " \n", " # Then define the starting point x0 of the algorithm\n", " x0 = problem.set_initial_point(name=\"x0\")\n", " \n", " # Set the initial constraint that is the distance between x0 and x^*\n", " problem.set_initial_condition( f(x0) - fs <= 1)\n", " \n", " # Run n steps of the GD method\n", " x1 = x0 - gamma * f.gradient(x0)\n", " x1.set_name(\"x1\")\n", " \n", " # Set the performance metric to the function values accuracy\n", " problem.set_performance_metric( f(x1) - fs )\n", " \n", " # Output prepared problem\n", " pepit_verbose = max(verbose, 0)\n", " problem._prepare_constraints(verbose=pepit_verbose)\n", " \n", " return problem\n", "\n", "PEP_GD = wc_gradient_descent_function_values_prepare(mu,L,gamma=1/L, verbose=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can now interactively iterate between solving the problem and activating/deactivating constraints, and the goal is to identify a minimal number of constraints that allows recovering the same optimal problem value. Let us start by solving the original problem." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(PEPit) Setting up the problem: size of the Gram matrix: 4x4\n", "(PEPit) Compiling SDP\n", "(PEPit) Calling SDP solver\n", "(PEPit) Solver status: optimal (wrapper:cvxpy, solver: SCS); optimal value: 0.8099999973549602\n", "(PEPit) Primal feasibility check:\n", "\t\tThe solver found a Gram matrix that is positive semi-definite\n", "\t\tAll the primal scalar constraints are verified up to an error of 2.02780943356351e-08\n", "(PEPit) Dual feasibility check:\n", "\t\tThe solver found a residual matrix that is positive semi-definite up to an error of 2.7515637538840285e-16\n", "\t\tAll the dual scalar values associated with inequality constraints are nonnegative\n", "(PEPit) The worst-case guarantee proof is perfectly reconstituted up to an error of 5.729466262983668e-09\n", "(PEPit) Final upper bound (dual): 0.8099999986909497 and lower bound (primal example): 0.8099999973549602 \n", "(PEPit) Duality gap: absolute: 1.3359895412179412e-09 and relative: 1.649369809358753e-09\n", "Constraint \"Performance metric 1\" value: 1.000000000000004\n", "Constraint \"Initial condition\" value: 0.8099999986909497\n", "Constraint \"IC_f_smoothness_strong_convexity(xs, x0)\" value: 0.07413167584202415\n", "Constraint \"IC_f_smoothness_strong_convexity(xs, x1)\" value: 0.11586832639046385\n", "Constraint \"IC_f_smoothness_strong_convexity(x0, xs)\" value: 1.1349094168952335e-09\n", "Constraint \"IC_f_smoothness_strong_convexity(x0, x1)\" value: 1.0428149257255546\n", "Constraint \"IC_f_smoothness_strong_convexity(x1, xs)\" value: 1.2819965105035798e-09\n", "Constraint \"IC_f_smoothness_strong_convexity(x1, x0)\" value: 0.1586832515672777\n" ] } ], "source": [ "# Solve the original problem and output the dual variable values\n", "\n", "PEP_GD.solve(verbose = 1)\n", "list_of_constraints = PEP_GD._list_of_prepared_constraints\n", "nb_cons = len(list_of_constraints)\n", "\n", "for i in range(nb_cons):\n", " print('Constraint \\\"{}\\\" value: {}'.format(list_of_constraints[i].get_name(),\n", " list_of_constraints[i]._dual_variable_value))\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The corresponding optimal value matches what we would expect. Let us now deactivate some constraints and observe the result. We start with the first interpolation constraint (third constraint in the list) as follows:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(PEPit) Setting up the problem: size of the Gram matrix: 4x4\n", "(PEPit) Compiling SDP\n", "(PEPit) Calling SDP solver\n", "(PEPit) Solver status: optimal (wrapper:cvxpy, solver: SCS); optimal value: 0.8099959237526846\n", "\u001b[96m(PEPit) Postprocessing: solver's output is not entirely feasible (smallest eigenvalue of the Gram matrix is: -7.95e-06 < 0).\n", " Small deviation from 0 may simply be due to numerical error. Big ones should be deeply investigated.\n", " In any case, from now the provided values of parameters are based on the projection of the Gram matrix onto the cone of symmetric semi-definite matrix.\u001b[0m\n", "(PEPit) Primal feasibility check:\n", "\t\tThe solver found a Gram matrix that is positive semi-definite up to an error of 7.950263779793486e-06\n", "\t\tAll the primal scalar constraints are verified up to an error of 2.02780943356351e-08\n", "(PEPit) Dual feasibility check:\n", "\t\tThe solver found a residual matrix that is positive semi-definite up to an error of 1.294938016578548e-16\n", "\t\tAll the dual scalar values associated with inequality constraints are nonnegative\n", "(PEPit) The worst-case guarantee proof is perfectly reconstituted up to an error of 3.4788703067185206e-06\n", "(PEPit) Final upper bound (dual): 0.8100122825325423 and lower bound (primal example): 0.8099959237526846 \n", "(PEPit) Duality gap: absolute: 1.6358779857728045e-05 and relative: 2.0196126150781544e-05\n", "Constraint \"Performance metric 1\" dual value: 0.9999999999994755 [activated? True]\n", "Constraint \"Initial condition\" dual value: 0.8100122825325423 [activated? True]\n", "Constraint \"IC_f_smoothness_strong_convexity(xs, x0)\" dual value: 0.0 [activated? False]\n", "Constraint \"IC_f_smoothness_strong_convexity(xs, x1)\" dual value: 0.18999927669423558 [activated? True]\n", "Constraint \"IC_f_smoothness_strong_convexity(x0, xs)\" dual value: 0.0 [activated? True]\n", "Constraint \"IC_f_smoothness_strong_convexity(x0, x1)\" dual value: 1.710173729689888 [activated? True]\n", "Constraint \"IC_f_smoothness_strong_convexity(x1, xs)\" dual value: 1.164825840095675e-05 [activated? True]\n", "Constraint \"IC_f_smoothness_strong_convexity(x1, x0)\" dual value: 0.900161358121609 [activated? True]\n" ] } ], "source": [ "# Solve the problem with the first interpolation constraint deactivated\n", "\n", "list_of_constraints[2].deactivate()\n", "\n", "PEP_GD.solve(verbose = 1)\n", "for i in range(nb_cons):\n", " print('Constraint \\\"{}\\\" dual value: {} [activated? {}]'.format(list_of_constraints[i].get_name(),\n", " list_of_constraints[i]._dual_variable_value,\n", " list_of_constraints[i].activated))\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Good surprise: the same numerical bound can be achieved without using the first interpolation constraint... let's try to be more aggressive and deactivate more constraints." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(PEPit) Setting up the problem: size of the Gram matrix: 4x4\n", "(PEPit) Compiling SDP\n", "(PEPit) Calling SDP solver\n", "(PEPit) Solver status: unbounded (wrapper:cvxpy, solver: SCS); optimal value: None\n", "\u001b[96m(PEPit) Problem issue: PEPit didn't find any nontrivial worst-case guarantee. It seems that the optimal value of your problem is unbounded.\u001b[0m\n", "Constraint \"Performance metric 1\" dual value: 0.0 [activated? True]\n", "Constraint \"Initial condition\" dual value: 0.0 [activated? True]\n", "Constraint \"IC_f_smoothness_strong_convexity(xs, x0)\" dual value: 0.0 [activated? False]\n", "Constraint \"IC_f_smoothness_strong_convexity(xs, x1)\" dual value: 0.0 [activated? False]\n", "Constraint \"IC_f_smoothness_strong_convexity(x0, xs)\" dual value: 0.0 [activated? False]\n", "Constraint \"IC_f_smoothness_strong_convexity(x0, x1)\" dual value: 0.0 [activated? False]\n", "Constraint \"IC_f_smoothness_strong_convexity(x1, xs)\" dual value: 0.0 [activated? True]\n", "Constraint \"IC_f_smoothness_strong_convexity(x1, x0)\" dual value: 0.0 [activated? True]\n" ] } ], "source": [ "# Solve the problem with the first interpolation constraint deactivated\n", "\n", "list_of_constraints[3].deactivate()\n", "list_of_constraints[4].deactivate()\n", "list_of_constraints[5].deactivate()\n", "\n", "PEP_GD.solve(verbose = 1)\n", "for i in range(nb_cons):\n", " print('Constraint \\\"{}\\\" dual value: {} [activated? {}]'.format(list_of_constraints[i].get_name(),\n", " list_of_constraints[i]._dual_variable_value,\n", " list_of_constraints[i].activated))\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This was too much. We need to reactivate some." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(PEPit) Setting up the problem: size of the Gram matrix: 4x4\n", "(PEPit) Compiling SDP\n", "(PEPit) Calling SDP solver\n", "(PEPit) Solver status: optimal (wrapper:cvxpy, solver: SCS); optimal value: 0.8099888053087226\n", "\u001b[96m(PEPit) Postprocessing: solver's output is not entirely feasible (smallest eigenvalue of the Gram matrix is: -8.97e-06 < 0).\n", " Small deviation from 0 may simply be due to numerical error. Big ones should be deeply investigated.\n", " In any case, from now the provided values of parameters are based on the projection of the Gram matrix onto the cone of symmetric semi-definite matrix.\u001b[0m\n", "(PEPit) Primal feasibility check:\n", "\t\tThe solver found a Gram matrix that is positive semi-definite up to an error of 8.969189465201078e-06\n", "\t\tAll the primal scalar constraints are verified up to an error of 2.02780943356351e-08\n", "(PEPit) Dual feasibility check:\n", "\t\tThe solver found a residual matrix that is positive semi-definite\n", "\t\tAll the dual scalar values associated with inequality constraints are nonnegative\n", "(PEPit) The worst-case guarantee proof is perfectly reconstituted up to an error of 1.9538734712248873e-06\n", "(PEPit) Final upper bound (dual): 0.8100061200137512 and lower bound (primal example): 0.8099888053087226 \n", "(PEPit) Duality gap: absolute: 1.7314705028526056e-05 and relative: 2.1376474483405552e-05\n", "Constraint \"Performance metric 1\" dual value: 0.9999999999992356 [activated? True]\n", "Constraint \"Initial condition\" dual value: 0.8100061200137512 [activated? True]\n", "Constraint \"IC_f_smoothness_strong_convexity(xs, x0)\" dual value: 0.0 [activated? False]\n", "Constraint \"IC_f_smoothness_strong_convexity(xs, x1)\" dual value: 0.18999905903570885 [activated? True]\n", "Constraint \"IC_f_smoothness_strong_convexity(x0, xs)\" dual value: 0.0 [activated? False]\n", "Constraint \"IC_f_smoothness_strong_convexity(x0, x1)\" dual value: 1.7102118672605662 [activated? True]\n", "Constraint \"IC_f_smoothness_strong_convexity(x1, xs)\" dual value: 5.1790508283630655e-06 [activated? True]\n", "Constraint \"IC_f_smoothness_strong_convexity(x1, x0)\" dual value: 0.9002057472465224 [activated? True]\n" ] } ], "source": [ "# Solve the problem with the first interpolation constraint deactivated\n", "\n", "list_of_constraints[3].activate()\n", "list_of_constraints[5].activate()\n", "\n", "PEP_GD.solve(verbose = 1)\n", "for i in range(nb_cons):\n", " print('Constraint \\\"{}\\\" dual value: {} [activated? {}]'.format(list_of_constraints[i].get_name(),\n", " list_of_constraints[i]._dual_variable_value,\n", " list_of_constraints[i].activated))\n", " " ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "After a bit of experiment, one can arrive to the following pattern" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(PEPit) Setting up the problem: size of the Gram matrix: 4x4\n", "(PEPit) Compiling SDP\n", "(PEPit) Calling SDP solver\n", "(PEPit) Solver status: optimal (wrapper:cvxpy, solver: SCS); optimal value: 0.8100000003376879\n", "\u001b[96m(PEPit) Postprocessing: solver's output is not entirely feasible (smallest eigenvalue of the Gram matrix is: -6.16e-10 < 0).\n", " Small deviation from 0 may simply be due to numerical error. Big ones should be deeply investigated.\n", " In any case, from now the provided values of parameters are based on the projection of the Gram matrix onto the cone of symmetric semi-definite matrix.\u001b[0m\n", "(PEPit) Primal feasibility check:\n", "\t\tThe solver found a Gram matrix that is positive semi-definite up to an error of 6.157372501933532e-10\n", "\t\tAll the primal scalar constraints are verified up to an error of 2.02780943356351e-08\n", "(PEPit) Dual feasibility check:\n", "\t\tThe solver found a residual matrix that is positive semi-definite up to an error of 3.214747989495667e-16\n", "\t\tAll the dual scalar values associated with inequality constraints are nonnegative\n", "(PEPit) The worst-case guarantee proof is perfectly reconstituted up to an error of 1.6402668650411512e-09\n", "(PEPit) Final upper bound (dual): 0.810000000152885 and lower bound (primal example): 0.8100000003376879 \n", "(PEPit) Duality gap: absolute: -1.8480295072009767e-10 and relative: -2.2815179091735007e-10\n", "Constraint \"Performance metric 1\" dual value: 1.0000000000000062 [activated? True]\n", "Constraint \"Initial condition\" dual value: 0.810000000152885 [activated? True]\n", "Constraint \"IC_f_smoothness_strong_convexity(xs, x0)\" dual value: 0.08999999992515632 [activated? True]\n", "Constraint \"IC_f_smoothness_strong_convexity(xs, x1)\" dual value: 0.09999999992196733 [activated? True]\n", "Constraint \"IC_f_smoothness_strong_convexity(x0, xs)\" dual value: 0.0 [activated? False]\n", "Constraint \"IC_f_smoothness_strong_convexity(x0, x1)\" dual value: 0.9000000000780398 [activated? True]\n", "Constraint \"IC_f_smoothness_strong_convexity(x1, xs)\" dual value: 0.0 [activated? False]\n", "Constraint \"IC_f_smoothness_strong_convexity(x1, x0)\" dual value: 0.0 [activated? False]\n" ] } ], "source": [ "# Solve the problem with the first interpolation constraint deactivated\n", "\n", "list_of_constraints[2].activate()\n", "list_of_constraints[3].activate()\n", "list_of_constraints[5].activate()\n", "list_of_constraints[4].deactivate()\n", "list_of_constraints[6].deactivate()\n", "list_of_constraints[7].deactivate()\n", "\n", "PEP_GD.solve(verbose = 1)\n", "for i in range(nb_cons):\n", " print('Constraint \\\"{}\\\" dual value: {} [activated? {}]'.format(list_of_constraints[i].get_name(),\n", " list_of_constraints[i]._dual_variable_value,\n", " list_of_constraints[i].activated))\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let us verify that this pattern of inequalities works throughout the whole range of step sizes $\\gamma$." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "tags": [] }, "outputs": [], "source": [ "def wc_gradient_descent_function_values_sparse_proof(mu,L,gamma, verbose):\n", " # Instantiate PEP\n", " problem = PEP()\n", "\n", " # Declare a smooth convex function\n", " f = problem.declare_function(SmoothStronglyConvexFunction, L=L, mu=mu)\n", " \n", " # Start by defining its unique optimal point xs = x_* and corresponding function value fs = f_*\n", " xs = f.stationary_point()\n", " fs = f(xs)\n", " \n", " # Then define the starting point x0 of the algorithm\n", " x0 = problem.set_initial_point()\n", " \n", " # Set the initial constraint that is the distance between x0 and x^*\n", " problem.set_initial_condition( f(x0) - fs <= 1)\n", " \n", " # Run n steps of the GD method\n", " x1 = x0 - gamma * f.gradient(x0)\n", " \n", " # Set the performance metric to the function values accuracy\n", " problem.set_performance_metric( f(x1) - fs )\n", " \n", " # Output prepared problem\n", " pepit_verbose = max(verbose, 0)\n", " problem._prepare_constraints(verbose=pepit_verbose)\n", " problem._list_of_prepared_constraints[4].deactivate()\n", " problem._list_of_prepared_constraints[6].deactivate()\n", " problem._list_of_prepared_constraints[7].deactivate()\n", " \n", " pepit_tau = problem.solve(verbose=pepit_verbose)\n", " \n", " return pepit_tau, problem._list_of_prepared_constraints\n" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "tags": [] }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "verbose = 0\n", "\n", "gamma_min, gamma_max = 0.001, 2 # only test the step sizes of interest here\n", "nb_gammas = 50\n", "gamma_list = np.linspace(gamma_min,gamma_max,nb_gammas)\n", "\n", "pepit_worst_case_value = list()\n", "known_worst_case_value = list()\n", "\n", "for gamma in gamma_list:\n", " pepit_tau, _ = wc_gradient_descent_function_values_sparse_proof(mu,L,gamma, verbose)\n", " pepit_worst_case_value.append(pepit_tau)\n", " known_worst_case_value.append(max((1-gamma*L)**2,(1-gamma*mu)**2))\n", " \n", "plt.plot(gamma_list, pepit_worst_case_value, color='red', linestyle='-', linewidth=3, label='PEPit')\n", "\n", "plt.plot(gamma_list, known_worst_case_value, color='blue', linestyle='--', linewidth=2, label='Known')\n", "\n", "plt.legend()\n", "plt.xlabel(r'$\\gamma$')\n", "plt.ylabel(r'$\\frac{f(x_1) - f_\\star}{f(x_0) - f_\\star}$')\n", "\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "tags": [] }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "verbose = 0\n", "\n", "gamma_min, gamma_max = 0.001, 2\n", "nb_gammas = 50\n", "gamma_list = np.linspace(gamma_min,gamma_max,nb_gammas)\n", "\n", "pepit_worst_case_value = list()\n", "pepit_dual_value1 = list()\n", "pepit_dual_value2 = list()\n", "pepit_dual_value3 = list()\n", "pepit_dual_value4 = list()\n", "pepit_dual_value5 = list()\n", "pepit_dual_value6 = list()\n", "known_worst_case_value = list()\n", "\n", "for gamma in gamma_list:\n", " pepit_tau, list_of_constraints = wc_gradient_descent_function_values_sparse_proof(mu,L,gamma, verbose)\n", " pepit_worst_case_value.append(pepit_tau)\n", " known_worst_case_value.append(max((1-gamma*L)**2,(1-gamma*mu)**2))\n", " pepit_dual_value1.append(list_of_constraints[2]._dual_variable_value)\n", " pepit_dual_value2.append(list_of_constraints[3]._dual_variable_value)\n", " pepit_dual_value3.append(list_of_constraints[4]._dual_variable_value)\n", " pepit_dual_value4.append(list_of_constraints[5]._dual_variable_value)\n", " pepit_dual_value5.append(list_of_constraints[6]._dual_variable_value)\n", " pepit_dual_value6.append(list_of_constraints[7]._dual_variable_value)\n", " \n", " \n", "plt.plot(gamma_list, pepit_dual_value1, color='red', linestyle='-', linewidth=3, label=r'$\\lambda_1$')\n", "plt.plot(gamma_list, pepit_dual_value2, color='blue', linestyle='-', linewidth=3, label=r'$\\lambda_2$')\n", "plt.plot(gamma_list, pepit_dual_value3, color='green', linestyle='-', linewidth=3, label=r'$\\lambda_3$')\n", "plt.plot(gamma_list, pepit_dual_value4, color='gold', linestyle='-', linewidth=3, label=r'$\\lambda_4$')\n", "plt.plot(gamma_list, pepit_dual_value5, color='orange', linestyle='--', linewidth=3, label=r'$\\lambda_5$')\n", "plt.plot(gamma_list, pepit_dual_value6, color='brown', linestyle='-.', linewidth=3, label=r'$\\lambda_6$')\n", "\n", "plt.legend()\n", "plt.xlabel(r'$\\gamma$')\n", "plt.ylabel(r'$\\lambda_i(\\gamma)$')\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... much better! one can actually even fit; defining $\\rho(\\gamma)=\\max\\{|1-\\gamma L|,|1-\\gamma\\mu|\\}$, we find:\n", "* $\\lambda_1(\\gamma)= (1-\\rho(\\gamma))\\rho(\\gamma)$,\n", "* $\\lambda_2 (\\gamma)= 1-\\rho(\\gamma)$,\n", "* $\\lambda_4 (\\gamma)= \\rho(\\gamma)$.\n", "\n", "Those results, along with the corresponding proofs, are presented in [here, Theorem 3.3](https://arxiv.org/pdf/1705.04398), a numerical comparison is provided just below.\n", "In the next sections, we provide a constructive few way to find such expressions beyond guessing." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "scrolled": true, "tags": [] }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "rho = np.array([ max(abs(1-gamma*L),abs(1-gamma*mu)) for gamma in gamma_list ])\n", "plt.plot(gamma_list, pepit_dual_value1, color='red', linestyle='-', linewidth=1, label=r'$\\lambda_1$ (numerics)')\n", "plt.plot(gamma_list, pepit_dual_value2, color='blue', linestyle='-', linewidth=1, label=r'$\\lambda_2$ (numerics)')\n", "plt.plot(gamma_list, pepit_dual_value4, color='gold', linestyle='-', linewidth=1, label=r'$\\lambda_4$ (numerics)')\n", "plt.plot(gamma_list, (1-rho)*rho, color='red', linestyle='--', linewidth=3, label=r'$\\lambda_1$ (closed-form)')\n", "plt.plot(gamma_list, 1-rho, color='blue', linestyle='--', linewidth=3, label=r'$\\lambda_2$ (closed-form)')\n", "plt.plot(gamma_list, rho, color='gold', linestyle='--', linewidth=3, label=r'$\\lambda_4$ (closed-form)')\n", "\n", "plt.legend()\n", "plt.xlabel(r'$\\gamma$')\n", "plt.ylabel(r'$\\lambda_i(\\gamma)$')\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. Using SymPy for the distance analysis " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This section requires SymPy (!pip install sympy)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\left[\\begin{matrix}\\frac{\\frac{L \\lambda_{1} \\mu}{2} + \\frac{L \\lambda_{2} \\mu}{2} + \\left(L - \\mu\\right) \\left(\\rho - 1\\right)}{L - \\mu} & \\frac{- \\frac{L \\lambda_{1}}{2} + \\gamma \\left(L - \\mu\\right) - \\frac{\\lambda_{2} \\mu}{2}}{L - \\mu}\\\\\\frac{- \\frac{L \\lambda_{1}}{2} + \\gamma \\left(L - \\mu\\right) - \\frac{\\lambda_{2} \\mu}{2}}{L - \\mu} & \\frac{4 \\gamma^{2} \\left(- L + \\mu\\right) + 2.0 \\lambda_{1} + 2.0 \\lambda_{2}}{4 \\left(L - \\mu\\right)}\\end{matrix}\\right]$" ], "text/plain": [ "Matrix([\n", "[(L*lambda_1*mu/2 + L*lambda_2*mu/2 + (L - mu)*(rho - 1))/(L - mu), (-L*lambda_1/2 + gamma*(L - mu) - lambda_2*mu/2)/(L - mu)],\n", "[ (-L*lambda_1/2 + gamma*(L - mu) - lambda_2*mu/2)/(L - mu), (4*gamma**2*(-L + mu) + 2.0*lambda_1 + 2.0*lambda_2)/(4*(L - mu))]])" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import sympy as sm\n", "\n", "# create symbols for the problem parameters:\n", "L = sm.Symbol('L')\n", "mu = sm.Symbol('mu')\n", "gamma = sm.Symbol('gamma')\n", "\n", "# create symbols for the \"primal\" variables:\n", "x0 = sm.Symbol('x0')\n", "g0 = sm.Symbol('g0')\n", "f0 = sm.Symbol('f0')\n", "xs = 0 # wlog, x_* = 0\n", "gs = 0 # constraint g_* = 0\n", "fs = 0 # wlog, f_* = 0\n", "x1 = x0 - gamma * g0 # define x1 using previous symbols:\n", "\n", "# create symbols for the \"dual\" variables\n", "rho = sm.Symbol('rho')\n", "l1 = sm.Symbol('lambda_1')\n", "l2 = sm.Symbol('lambda_2')\n", "\n", "\n", "# the two interpolation constraints in the form \"constraint <= 0\"\n", "constraint1 = f0 - fs + g0*(xs-x0) + 1/2/L * g0**2 + mu/(2*(1-mu/L)) * (x0-xs-1/L*g0)**2\n", "constraint2 = fs - f0 + 1/2/L * g0**2 + mu/(2*(1-mu/L)) * (x0-xs-1/L*g0)**2\n", "# the objective and the \"initial condition\" constraint: (also in the form \"constraint <= 0\")\n", "primal_objective = (x1-xs)**2\n", "initial_condition = (x0-xs)**2-1\n", "\n", "# Lagrangian:\n", "Lagrangian = - l1*constraint1 - l2*constraint2 - rho * initial_condition + primal_objective\n", "\n", "# This is the LMI:\n", "LMI = sm.simplify(sm.hessian( -Lagrangian , (x0,g0))/2) \n", "LMI" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\lambda_{1} - \\lambda_{2}$" ], "text/plain": [ "lambda_1 - lambda_2" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# This is the linear constraint ==0 in the LMI\n", "LinearConst = sm.simplify(sm.diff(-Lagrangian,f0))\n", "\n", "LinearConst # show linear constraint (==0)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\left[\\begin{matrix}\\frac{L \\lambda_{1} \\mu + \\left(L - \\mu\\right) \\left(\\rho - 1\\right)}{L - \\mu} & \\frac{- \\frac{L \\lambda_{1}}{2} + \\gamma \\left(L - \\mu\\right) - \\frac{\\lambda_{1} \\mu}{2}}{L - \\mu}\\\\\\frac{- \\frac{L \\lambda_{1}}{2} + \\gamma \\left(L - \\mu\\right) - \\frac{\\lambda_{1} \\mu}{2}}{L - \\mu} & \\frac{- L \\gamma^{2} + \\gamma^{2} \\mu + \\lambda_{1}}{L - \\mu}\\end{matrix}\\right]$" ], "text/plain": [ "Matrix([\n", "[ (L*lambda_1*mu + (L - mu)*(rho - 1))/(L - mu), (-L*lambda_1/2 + gamma*(L - mu) - lambda_1*mu/2)/(L - mu)],\n", "[(-L*lambda_1/2 + gamma*(L - mu) - lambda_1*mu/2)/(L - mu), (-L*gamma**2 + gamma**2*mu + lambda_1)/(L - mu)]])" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# For getting the same LMI as in the document, substitute l2 by l1 and simplify\n", "LMI_simplified = sm.simplify(LMI.subs(l2,l1))\n", "\n", "LMI_simplified # show the LMI (>=0)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\frac{\\lambda_{1} \\cdot \\left(4 L \\gamma^{2} \\mu - 4 L \\gamma + L \\lambda_{1} - 4 \\gamma \\mu - \\lambda_{1} \\mu + 4\\right)}{4 \\left(- L \\gamma^{2} + \\gamma^{2} \\mu + \\lambda_{1}\\right)}$" ], "text/plain": [ "lambda_1*(4*L*gamma**2*mu - 4*L*gamma + L*lambda_1 - 4*gamma*mu - lambda_1*mu + 4)/(4*(-L*gamma**2 + gamma**2*mu + lambda_1))" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "candidate_rho=sm.solve(LMI_simplified.det(),rho)\n", "candidate_rho[0]\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are two possibilities for choosing $\\lambda_1$:\n", "\n", "- so that the LMI is rank defficient (i.e., solution is on the boundary of the PSD cone),\n", "- so that the LMI is full rank. Minimize $\\rho$ wrt $\\lambda_1$ and verify feasibility of the LMI.\n", "\n", "Experimenting with the numerics, one can observe that the rank of the LMI is one for most choices of $\\gamma$, let's focus on the second possibility.\n" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "[2*gamma*(L*gamma - 1), 2*gamma*(-gamma*mu + 1)]" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "drho=sm.simplify(sm.diff(candidate_rho[0],l1)) #differentiate $\\rho$ with respect to lambda_1\n", "l1sol=sm.solve(drho,l1) # solve drho/dlambda_1 == 0 in lambda1\n", "l1sol # show the two possibilities!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The corresponding \"final\" expressions for that must be checked are therefore:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "[(L*gamma - 1)**2, (gamma*mu - 1)**2]" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "exprrho1=sm.simplify(candidate_rho[0].subs(l1,l1sol[0]))\n", "exprrho2=sm.simplify(candidate_rho[0].subs(l1,l1sol[1]))\n", "\n", "[exprrho1.factor(), exprrho2.factor()]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... and we are done!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5. Using SymPy for the function value accuracy analysis " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... same game with the already-simplified LMI. But using a few shortcuts." ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\left[\\begin{matrix}\\frac{L \\mu \\left(\\lambda_{1} + \\lambda_{2}\\right)}{2 \\left(L - \\mu\\right)} & \\frac{- L \\lambda_{1} - \\lambda_{2} \\mu}{2 \\left(L - \\mu\\right)} & - \\frac{L \\lambda_{2}}{2 L - 2 \\mu}\\\\\\frac{- L \\lambda_{1} - \\lambda_{2} \\mu}{2 \\left(L - \\mu\\right)} & \\frac{L \\lambda_{1} + \\lambda_{2} \\mu + \\lambda_{4} \\left(L - \\mu\\right)}{2 L \\left(L - \\mu\\right)} & \\frac{\\lambda_{2}}{2 \\left(L - \\mu\\right)}\\\\- \\frac{L \\lambda_{2}}{2 L - 2 \\mu} & \\frac{\\lambda_{2}}{2 \\left(L - \\mu\\right)} & \\frac{0.5 \\left(\\lambda_{2} + \\lambda_{4}\\right)}{L - \\mu}\\end{matrix}\\right]$" ], "text/plain": [ "Matrix([\n", "[ L*mu*(lambda_1 + lambda_2)/(2*(L - mu)), (-L*lambda_1 - lambda_2*mu)/(2*(L - mu)), -L*lambda_2/(2*L - 2*mu)],\n", "[(-L*lambda_1 - lambda_2*mu)/(2*(L - mu)), (L*lambda_1 + lambda_2*mu + lambda_4*(L - mu))/(2*L*(L - mu)), lambda_2/(2*(L - mu))],\n", "[ -L*lambda_2/(2*L - 2*mu), lambda_2/(2*(L - mu)), 0.5*(lambda_2 + lambda_4)/(L - mu)]])" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import sympy as sm\n", "\n", "# create symbols for the problem parameters:\n", "L = sm.Symbol('L')\n", "mu = sm.Symbol('mu')\n", "gamma = 1/L\n", "\n", "# create symbols for the \"primal\" variables:\n", "x0 = sm.Symbol('x0')\n", "g0 = sm.Symbol('g0')\n", "g1 = sm.Symbol('g1')\n", "f0 = sm.Symbol('f0')\n", "f1 = sm.Symbol('f1')\n", "xs = 0 # wlog, x_* = 0\n", "gs = 0 # constraint g_* = 0\n", "fs = 0 # wlog, f_* = 0\n", "x1 = x0 - gamma * g0 # define x1 using previous symbols:\n", "\n", "# create symbols for the \"dual\" variables\n", "rho = sm.Symbol('rho')\n", "l1 = sm.Symbol('lambda_1')\n", "l2 = sm.Symbol('lambda_2')\n", "l4 = sm.Symbol('lambda_4')\n", "\n", "\n", "# the two interpolation constraints in the form \"constraint <= 0\"\n", "constraint1 = f0 - fs + g0*(xs-x0) + 1/2/L * g0**2 + mu/(2*(1-mu/L)) * (x0-xs-1/L*g0)**2\n", "constraint2 = f1 - fs + g1*(xs-x1) + 1/2/L * g1**2 + mu/(2*(1-mu/L)) * (x1-xs-1/L*g1)**2\n", "constraint4 = f1 - f0 + g1*(x0-x1) + 1/2/L * (g0-g1)**2 + mu/(2*(1-mu/L)) * (x1-x0-1/L*g1+1/L*g0)**2\n", "\n", "# the objective and the \"initial condition\" constraint: (also in the form \"constraint <= 0\")\n", "primal_objective = f1-fs\n", "initial_condition = f0-fs\n", "\n", "# Lagrangian:\n", "Lagrangian = - l1*constraint1 - l2*constraint2 - l4*constraint4 - rho * initial_condition + primal_objective\n", "\n", "# This is the LMI:\n", "LMI = sm.simplify(sm.hessian( -Lagrangian , (x0,g0,g1))/2) \n", "LMI" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "[lambda_1 - lambda_4 + rho, lambda_2 + lambda_4 - 1]" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# This is the linear constraint ==0 in the LMI\n", "LinearConst1 = sm.simplify(sm.diff(-Lagrangian,(f0)))\n", "LinearConst2 = sm.simplify(sm.diff(-Lagrangian,(f1)))\n", "\n", "[LinearConst1, LinearConst2] # show linear constraint (==0)" ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\left[\\begin{matrix}\\frac{L \\mu \\left(1 - \\rho\\right)}{2 \\left(L - \\mu\\right)} & \\frac{- L \\lambda_{1} + \\mu \\left(\\lambda_{1} + \\rho - 1\\right)}{2 \\left(L - \\mu\\right)} & \\frac{L \\left(\\lambda_{1} + \\rho - 1\\right)}{2 \\left(L - \\mu\\right)}\\\\\\frac{- L \\lambda_{1} + \\mu \\left(\\lambda_{1} + \\rho - 1\\right)}{2 \\left(L - \\mu\\right)} & \\frac{L \\lambda_{1} - \\mu \\left(\\lambda_{1} + \\rho - 1\\right) + \\left(L - \\mu\\right) \\left(\\lambda_{1} + \\rho\\right)}{2 L \\left(L - \\mu\\right)} & \\frac{- \\lambda_{1} - \\rho + 1}{2 \\left(L - \\mu\\right)}\\\\\\frac{L \\left(\\lambda_{1} + \\rho - 1\\right)}{2 \\left(L - \\mu\\right)} & \\frac{- \\lambda_{1} - \\rho + 1}{2 \\left(L - \\mu\\right)} & \\frac{0.5}{L - \\mu}\\end{matrix}\\right]$" ], "text/plain": [ "Matrix([\n", "[ L*mu*(1 - rho)/(2*(L - mu)), (-L*lambda_1 + mu*(lambda_1 + rho - 1))/(2*(L - mu)), L*(lambda_1 + rho - 1)/(2*(L - mu))],\n", "[(-L*lambda_1 + mu*(lambda_1 + rho - 1))/(2*(L - mu)), (L*lambda_1 - mu*(lambda_1 + rho - 1) + (L - mu)*(lambda_1 + rho))/(2*L*(L - mu)), (-lambda_1 - rho + 1)/(2*(L - mu))],\n", "[ L*(lambda_1 + rho - 1)/(2*(L - mu)), (-lambda_1 - rho + 1)/(2*(L - mu)), 0.5/(L - mu)]])" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# For getting the same LMI as in the document, substitute l2 by l1 and simplify\n", "LMI_simplified = sm.simplify(LMI.subs(l4,l1+rho))\n", "LMI_simplified = sm.simplify(LMI_simplified.subs(l2,1-l1-rho))\n", "\n", "LMI_simplified # show the LMI (>=0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One could here, again, apply the same strategy and search for $\\rho$ by cancelling out the determinant of this matrix (as the problem is linear in $\\rho$, the only possibility is actually to have $\\rho$ on the boundary of the domain). However, SymPy is not the most powerful symbolic tool and simplifications are typically not as efficient as they could be.\n", "\n", "So let us instead just fix $\\rho$ to its known value: $\\rho=(1-\\mu \\gamma)^2$ (as $\\gamma=1/L$ here), and only search for the corresponding multipliers." ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\left[\\begin{matrix}\\frac{\\mu^{2} \\left(L - \\frac{\\mu}{2}\\right)}{L \\left(L - \\mu\\right)} & \\frac{- L^{3} \\lambda_{1} + \\mu \\left(L^{2} \\left(\\lambda_{1} - 1\\right) + \\left(L - \\mu\\right)^{2}\\right)}{2 L^{2} \\left(L - \\mu\\right)} & \\frac{L^{2} \\left(\\lambda_{1} - 1\\right) + \\left(L - \\mu\\right)^{2}}{2 L \\left(L - \\mu\\right)}\\\\\\frac{- L^{3} \\lambda_{1} + \\mu \\left(L^{2} \\left(\\lambda_{1} - 1\\right) + \\left(L - \\mu\\right)^{2}\\right)}{2 L^{2} \\left(L - \\mu\\right)} & \\frac{L^{3} \\lambda_{1} - \\mu \\left(L^{2} \\left(\\lambda_{1} - 1\\right) + \\left(L - \\mu\\right)^{2}\\right) + \\left(L - \\mu\\right) \\left(L^{2} \\lambda_{1} + \\left(L - \\mu\\right)^{2}\\right)}{2 L^{3} \\left(L - \\mu\\right)} & \\frac{L^{2} \\cdot \\left(1 - \\lambda_{1}\\right) - \\left(L - \\mu\\right)^{2}}{2 L^{2} \\left(L - \\mu\\right)}\\\\\\frac{L^{2} \\left(\\lambda_{1} - 1\\right) + \\left(L - \\mu\\right)^{2}}{2 L \\left(L - \\mu\\right)} & \\frac{L^{2} \\cdot \\left(1 - \\lambda_{1}\\right) - \\left(L - \\mu\\right)^{2}}{2 L^{2} \\left(L - \\mu\\right)} & \\frac{0.5}{L - \\mu}\\end{matrix}\\right]$" ], "text/plain": [ "Matrix([\n", "[ mu**2*(L - mu/2)/(L*(L - mu)), (-L**3*lambda_1 + mu*(L**2*(lambda_1 - 1) + (L - mu)**2))/(2*L**2*(L - mu)), (L**2*(lambda_1 - 1) + (L - mu)**2)/(2*L*(L - mu))],\n", "[(-L**3*lambda_1 + mu*(L**2*(lambda_1 - 1) + (L - mu)**2))/(2*L**2*(L - mu)), (L**3*lambda_1 - mu*(L**2*(lambda_1 - 1) + (L - mu)**2) + (L - mu)*(L**2*lambda_1 + (L - mu)**2))/(2*L**3*(L - mu)), (L**2*(1 - lambda_1) - (L - mu)**2)/(2*L**2*(L - mu))],\n", "[ (L**2*(lambda_1 - 1) + (L - mu)**2)/(2*L*(L - mu)), (L**2*(1 - lambda_1) - (L - mu)**2)/(2*L**2*(L - mu)), 0.5/(L - mu)]])" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "LMI_simplified = sm.simplify(LMI_simplified.subs(rho,(1-mu*gamma)**2))\n", "LMI_simplified" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The value of $\\lambda_1$ can now be found by forcing the determinant to be zero, as follows." ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\frac{\\mu \\left(L - \\mu\\right)}{L^{2}}$" ], "text/plain": [ "mu*(L - mu)/L**2" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "candidate_lambda1=sm.solve(LMI_simplified.det(),l1)\n", "candidate_lambda1[0]" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "... which we can compare with the numerics from [Section 3](#example3) (which is nicely matching!) and to expression $\\lambda_1(\\gamma)= (1-\\rho(\\gamma))\\rho(\\gamma)=(1-\\mu\\gamma)(1-(1-\\mu\\gamma))$ (known solution) to be found in [here, Theorem 3.3](https://arxiv.org/pdf/1705.04398)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6. Using symbolic regression (PySR) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Up to this point, two complementary approaches have been used to obtain the Lagrange multipliers needed to reconstruct a proof:\n", "\n", "1. **By inspection** — guess the functional form from numerical data \n", "2. **Symbolic exploitation of the LMI** — exploit the LMI to solve for the multipliers symbolically \n", "\n", "This section focuses on automating the “inspection” step in (1).\n", "\n", "The task can be framed as **symbolic regression**: learning a function from measurements without restricting it to a prescribed functional form.\n", "\n", "Symbolic regression is hard in general, but effective heuristics are often available in practice. This demo uses [PySR](https://github.com/MilesCranmer/PySR). \n", "Install it first (note: PySR’s backend is implemented in the Julia programming language)---the installation might take some time; so the first run might seem unnecessarily long." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, imagine that you do not know the closed form of $\\lambda_1(\\gamma)$ given at the end of Section 3. The next cell automatically learns the closed form of $\\lambda_1(\\gamma)$ using measurements from PEPit:" ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "tags": [] }, "outputs": [], "source": [ "import numpy as np\n", "from pysr import PySRRegressor\n", "\n", "# seed for reproducibility\n", "np.random.seed(42)\n", "\n", "X = []\n", "y = []\n", "mu = 0.1\n", "L = 1\n", "gamma_list = np.linspace(0.01, 1.99, 20)\n", "\n", "for gamma in gamma_list:\n", " pepit_tau, list_of_constraints = wc_gradient_descent_function_values_sparse_proof(mu, L, gamma, verbose=0)\n", " l1 = list_of_constraints[2]._dual_variable_value\n", " \n", " X.append([np.sqrt(pepit_tau)])\n", " y.append(l1)\n", "\n", "model = PySRRegressor(\n", " maxsize=10,\n", " niterations=10,\n", " binary_operators=[\"+\", \"-\", \"*\"],\n", " verbosity=0,\n", " progress=False,\n", " deterministic=True, # Just to maintain consistency in outputs\n", " parallelism='serial',\n", ")\n", " \n", "result = model.fit(np.array(X), np.array(y), variable_names=[\"rho\"])" ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(1.0000063 - rho) * rho\n" ] } ], "source": [ "print(model.get_best().equation)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This recovers the expression $\\lambda_1(\\gamma)= (1-\\rho(\\gamma))\\rho(\\gamma)$. This was a simple, univariate function, which is where symbolic regression works best.\n", "\n", "The next cell uses the flexibility of the PySR API in order to learn the convergence rate of gradient descent." ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "tags": [] }, "outputs": [], "source": [ "import itertools\n", "np.random.seed(42)\n", "\n", "X = []\n", "y = []\n", "\n", "# Use different values for L and mu to generate dataset\n", "L_values = [1, 2, 10]\n", "mu_values = [0.1, 0.9]\n", "\n", "for L_val in L_values:\n", " for mu_val in mu_values:\n", " \n", " # Generate uniform points between 0 and 2/L (at which point we do not have convergence)\n", " limit = 2.0 / L_val\n", " gammas = np.linspace(0.01, limit - 0.01, 5)\n", " \n", " for g_val in gammas:\n", " pepit_tau, _ = wc_gradient_descent_function_values_sparse_proof(mu_val, L_val, g_val, verbose=0)\n", " X.append([mu_val, L_val, g_val])\n", " y.append(pepit_tau)\n", "\n", "# Increased maxsize to allow for the expression complexity\n", "model = PySRRegressor(\n", " niterations=200,\n", " binary_operators=[\"+\", \"-\", \"*\", \"max\"], # PySR supports many operators, such as max, min, abs, etc\n", " unary_operators=[\"square\"],\n", " maxsize=15,\n", " verbosity=0,\n", " progress=False,\n", " deterministic=True,\n", " parallelism='serial',\n", ")\n", "\n", "result = model.fit(np.array(X), np.array(y), variable_names=[\"mu\", \"L\", \"g\"])" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "square(max(1.0 - (g * mu), (L * g) - 1.0))\n" ] } ], "source": [ "print(model.get_best().equation)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "PySR was able to effectively learn this convergence rate, which is a function of 3 different variables. This did, however, require guiding it to use the max and square operators. In other problems, a larger number of operators should be used, which does decrease the speed of convergence of the heuristic. This is why this type of approach works best for problems with relatively simple closed forms. When it works, it can save a lot of time." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] } ], "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.7.9" } }, "nbformat": 4, "nbformat_minor": 5 }