{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Power control\n",
    "\n",
    "*This example is adapted from Boyd, Kim, Vandenberghe, and Hassibi,* \"[A Tutorial on Geometric Programming](https://web.stanford.edu/~boyd/papers/pdf/gp_tutorial.pdf).\"\n",
    "\n",
    "*The problem data is adapted from the corresponding example in CVX's example library (Almir Mutapcic).*\n",
    "\n",
    "This example formulates and solves a power control problem for communication systems, in which the goal is to minimize the total transmitter power across n trasmitters, each trasmitting positive\n",
    "power levels $P_1$, $P_2$, $\\ldots$, $P_n$ to $n$ receivers,\n",
    "labeled $1, \\ldots, n$, with receiver $i$ receiving signal from\n",
    "transmitter $i$.\n",
    "\n",
    "The power received from transmitter $j$ at receiver $i$ is $G_{ij} P_{j}$, where $G_{ij} > 0$ represents the\n",
    "path gain from transmitter $j$ to receiver $i$. The signal power at receiver $i$ is $G_{ii} P_i$, and the interference power at receiver $i$ is $\\sum_{k \\neq i} G_{ik}P_k$. The noise power at\n",
    "receiver $i$ is $\\sigma_i$, and the signal to noise ratio (SINR) of\n",
    "the $i$th receiver-transmitter pair is\n",
    "\n",
    "$$\n",
    "S_i = \\frac{G_{ii}P_i}{\\sigma_i + \\sum_{k \\neq i} G_{ik}P_k}.\n",
    "$$\n",
    "\n",
    "The transmitters and receivers are constrained to have a minimum \n",
    "SINR $S^{\\text min}$, and the $P_i$ are bounded between $P_i^{\\text min}$ and $P_i^{\\text max}$. This gives the problem\n",
    "\n",
    "$$\n",
    "\\begin{array}{ll}\n",
    "\\mbox{minimize} & P_1 + \\cdots + P_n \\\\\n",
    "\\mbox{subject to} & P_i^{\\text min} \\leq P_i \\leq P_i^{\\text max}, \\\\\n",
    "& 1/S^{\\text min} \\geq \\frac{\\sigma_i + \\sum_{k \\neq i} G_{ik}P_k}{G_{ii}P_i}\n",
    "\\end{array}.\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import cvxpy as cp\n",
    "import numpy as np\n",
    "\n",
    "# Problem data\n",
    "n = 5                     # number of transmitters and receivers\n",
    "sigma = 0.5 * np.ones(n)  # noise power at the receiver i\n",
    "p_min = 0.1 * np.ones(n)  # minimum power at the transmitter i\n",
    "p_max = 5 * np.ones(n)    # maximum power at the transmitter i\n",
    "sinr_min = 0.2            # threshold SINR for each receiver\n",
    "\n",
    "# Path gain matrix\n",
    "G = np.array(\n",
    "   [[1.0, 0.1, 0.2, 0.1, 0.05],\n",
    "    [0.1, 1.0, 0.1, 0.1, 0.05],\n",
    "    [0.2, 0.1, 1.0, 0.2, 0.2],\n",
    "    [0.1, 0.1, 0.2, 1.0, 0.1],\n",
    "    [0.05, 0.05, 0.2, 0.1, 1.0]])\n",
    "p = cp.Variable(shape=(n,), pos=True)\n",
    "objective = cp.Minimize(cp.sum(p))\n",
    "\n",
    "S_p = []\n",
    "for i in range(n):\n",
    "    S_p.append(cp.sum(cp.hstack(G[i, k]*p for k in range(n) if i != k)))\n",
    "S = sigma + cp.hstack(S_p)\n",
    "signal_power = cp.multiply(cp.diag(G), p)\n",
    "inverse_sinr = S/signal_power\n",
    "constraints = [\n",
    "    p >= p_min, \n",
    "    p <= p_max,\n",
    "    inverse_sinr <= (1/sinr_min),\n",
    "]\n",
    "\n",
    "problem = cp.Problem(objective, constraints)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "problem.is_dgp()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9615384629119621"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "problem.solve(gp=True)\n",
    "problem.value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.18653846, 0.16730769, 0.23461538, 0.19615385, 0.17692308])"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "p.value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([5., 5., 5., 5., 5.])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inverse_sinr.value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5.0"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(1/sinr_min)"
   ]
  }
 ],
 "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.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
