{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "concave_fractional_function.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "collapsed_sections": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hj5vNlJzqoQr",
        "colab_type": "text"
      },
      "source": [
        "# Fractional optimization\n",
        "\n",
        "This notebook shows how to solve a simple *concave fractional problem*, in which the objective is to maximize the ratio of a nonnegative concave function and a positive\n",
        "convex function. Concave fractional problems are quasiconvex programs (QCPs). They can be specified using disciplined quasiconvex programming ([DQCP](https://www.cvxpy.org/tutorial/dqcp/index.html)), and hence can be solved using CVXPY."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "310PJvINl2JU",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 187
        },
        "outputId": "bdd451f6-2c9f-4185-8f3f-d8756dd97c95"
      },
      "source": [
        "!pip install --upgrade cvxpy"
      ],
      "execution_count": 13,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Requirement already up-to-date: cvxpy in /usr/local/lib/python3.6/dist-packages (1.0.23)\n",
            "Requirement already satisfied, skipping upgrade: scs>=1.1.3 in /usr/local/lib/python3.6/dist-packages (from cvxpy) (2.1.0)\n",
            "Requirement already satisfied, skipping upgrade: multiprocess in /usr/local/lib/python3.6/dist-packages (from cvxpy) (0.70.7)\n",
            "Requirement already satisfied, skipping upgrade: numpy>=1.15 in /usr/local/lib/python3.6/dist-packages (from cvxpy) (1.16.3)\n",
            "Requirement already satisfied, skipping upgrade: scipy>=1.1.0 in /usr/local/lib/python3.6/dist-packages (from cvxpy) (1.3.0)\n",
            "Requirement already satisfied, skipping upgrade: ecos>=2 in /usr/local/lib/python3.6/dist-packages (from cvxpy) (2.0.7.post1)\n",
            "Requirement already satisfied, skipping upgrade: osqp>=0.4.1 in /usr/local/lib/python3.6/dist-packages (from cvxpy) (0.5.0)\n",
            "Requirement already satisfied, skipping upgrade: six in /usr/local/lib/python3.6/dist-packages (from cvxpy) (1.12.0)\n",
            "Requirement already satisfied, skipping upgrade: dill>=0.2.9 in /usr/local/lib/python3.6/dist-packages (from multiprocess->cvxpy) (0.2.9)\n",
            "Requirement already satisfied, skipping upgrade: future in /usr/local/lib/python3.6/dist-packages (from osqp>=0.4.1->cvxpy) (0.16.0)\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ehIgvu6rmlRr",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "import cvxpy as cp\n",
        "import numpy as np\n",
        "import matplotlib.pyplot as plt"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "j8E5ouimrBoV",
        "colab_type": "text"
      },
      "source": [
        "Our goal is to minimize the function\n",
        "\n",
        "$$\\frac{\\sqrt{x}}{\\exp(x)}.$$\n",
        "\n",
        "This function is not concave, but it is quasiconcave, as can be seen by inspecting its graph."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "AgPgroRVm5Mz",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 269
        },
        "outputId": "65bd8263-698c-49a5-d7d1-290718e7a43e"
      },
      "source": [
        "plt.plot([np.sqrt(y) / np.exp(y) for y in np.linspace(0, 10)])\n",
        "plt.show()"
      ],
      "execution_count": 15,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-jgzBqQ6rZPM",
        "colab_type": "text"
      },
      "source": [
        "The below code specifies and solves the QCP, using DQCP. The concave fraction function is DQCP-compliant, because the ratio atom is quasiconcave (actually, quasilinear), increasing in the numerator when the denominator is positive, and decreasing in the denominator when the numerator is nonnegative."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "uAbu67IemoDK",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        },
        "outputId": "a1e231df-a97d-43ca-a427-5290bbb8bff5"
      },
      "source": [
        "x = cp.Variable()\n",
        "concave_fractional_fn = cp.sqrt(x) / cp.exp(x)\n",
        "problem = cp.Problem(cp.Maximize(concave_fractional_fn))\n",
        "assert problem.is_dqcp()\n",
        "problem.solve(qcp=True)"
      ],
      "execution_count": 16,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "0.4288821220397949"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 16
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "9auLCvH3m366",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        },
        "outputId": "8511813a-562a-4cae-deaf-504d6c825aa6"
      },
      "source": [
        "x.value"
      ],
      "execution_count": 17,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "array(0.50000165)"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 17
        }
      ]
    }
  ]
}