{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Optimization\n",
    "\n",
    "of linear-quadratic problems.\n",
    "\n",
    "Many optimization problems in finance and econometrics involve linear-quadratic objectives/constraints. This notebook illustrates how the package [Convex.jl](https://github.com/jump-dev/Convex.jl) can be used for this. The example is (for pedagogical reasons) the same as in the other notebooks on optimization. Otherwise, the methods illustrated here are well suited for cases when the objective involves the portfolio variance ($ w'\\Sigma w $) or when the estimation problem is based on minimizing the sum of squared residuals ($u'u$).\n",
    "\n",
    "The notebook also uses [SCS.jl](https://github.com/jump-dev/SCS.jl) (for the optimization algorithm). To check for convergence, we also need a function from the [MathOptInterface.jl](https://github.com/jump-dev/MathOptInterface.jl) package."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load Packages and Utility Functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "printyellow (generic function with 1 method)"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "using Printf, LinearAlgebra, Convex, SCS\n",
    "import MathOptInterface\n",
    "const MOI = MathOptInterface\n",
    "\n",
    "include(\"jlFiles/printmat.jl\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## A Linear-Quadratic Minimization Problem\n",
    "\n",
    "with/without constraints.\n",
    "\n",
    "We specify a matrix $Q$ and a vector $c$ and write the loss function as $b'Qb + c'b$ where $b$ are the choice variables. \n",
    "\n",
    "We consider several cases below: no restrictions on $b$, bounds on $b$, a linear equality restriction and a non-linear inequality restriction."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2-element Vector{Int64}:\n",
       " -4\n",
       " 24"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Q = [1  0;           #we want to minimize b'Q*b + c'b\n",
    "     0  16]          #this is the same as minimizing (x-2)^2 + (4y+3)^2 \n",
    "c = [-4, 24]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Unconstrained Minimization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Unconstrained minimization: the solution should be (2,-3/4)\n",
      "     2.000\n",
      "    -0.750\n",
      "\n"
     ]
    }
   ],
   "source": [
    "n  = length(c)\n",
    "b  = Variable(n)              #define the choice variables\n",
    "L1 = quadform(b,Q)            #part 1 of the objective, b'Q*b\n",
    "L2 = dot(c,b)                 #part 2, c'b\n",
    "\n",
    "problem = minimize(L1+L2)\n",
    "solve!(problem,()->SCS.Optimizer(verbose=false))\n",
    "problem.status == MOI.OPTIMAL ? b_sol = evaluate(b) : b_sol = NaN\n",
    "\n",
    "println(\"Unconstrained minimization: the solution should be (2,-3/4)\")\n",
    "printmat(b_sol)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Constrained Minimization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "with bounds on the solution: the solution should be (2.75,-0.75)\n",
      "     2.750\n",
      "    -0.750\n",
      "\n"
     ]
    }
   ],
   "source": [
    "c1 = [2.75 <= b[1],b[2] <= -0.3]     #bounds on the solution\n",
    "\n",
    "problem = minimize(L1+L2,c1)\n",
    "solve!(problem,()->SCS.Optimizer(verbose=false))\n",
    "problem.status == MOI.OPTIMAL ? b_sol = evaluate(b) : b_sol = NaN\n",
    "\n",
    "println(\"with bounds on the solution: the solution should be (2.75,-0.75)\")\n",
    "printmat(b_sol)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "equality constraint: the solution should be (4,-1/2)\n",
      "     4.000\n",
      "    -0.500\n",
      "\n"
     ]
    }
   ],
   "source": [
    "c2 = dot([1,2],b) == 3     #equality constraint\n",
    "\n",
    "problem = minimize(L1+L2,c2)\n",
    "solve!(problem,()->SCS.Optimizer(verbose=false))\n",
    "problem.status == MOI.OPTIMAL ? b_sol = evaluate(b) : b_sol = NaN\n",
    "\n",
    "println(\"equality constraint: the solution should be (4,-1/2)\")\n",
    "printmat(b_sol)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "non-linear inequality constraint: the solution should be close to (3.1,-0.79)\n",
      "     3.112\n",
      "    -0.789\n",
      "\n"
     ]
    }
   ],
   "source": [
    "c3 = b[2] + square(b[1]-4) <= 0      #non-linear inequality constraint\n",
    "\n",
    "problem = minimize(L1+L2,c3)\n",
    "solve!(problem,()->SCS.Optimizer(verbose=false))\n",
    "problem.status == MOI.OPTIMAL ? b_sol = evaluate(b) : b_sol = NaN\n",
    "\n",
    "println(\"non-linear inequality constraint: the solution should be close to (3.1,-0.79)\")\n",
    "printmat(b_sol)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "@webio": {
   "lastCommId": null,
   "lastKernelId": null
  },
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Julia 1.6.1",
   "language": "julia",
   "name": "julia-1.6"
  },
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "1.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
