# Portfolio Selection Optimization
This model is an example of the classic [Markowitz portfolio selection optimization model](https://en.wikipedia.org/wiki/Markowitz_model). We want to find the fraction of the portfolio to invest among a set of stocks that balances risk and return. It is a Quadratic Programming (QP) model with vector and matrix data for returns and risk, respectively. This is best suited to a matrix formulation, so we use the Gurobi Python *matrix* interface. The basic model is fairly simple, so we also solve it parametrically to find the efficient frontier.

**Download the Repository**

You can download the repository containing this and other examples by clicking [here](https://github.com/Gurobi/modeling-examples/archive/master.zip).

**Gurobi License**

In order to run this Jupyter Notebook properly, you must have a Gurobi license. If you do not have one, you can request an [evaluation license](https://www.gurobi.com/downloads/request-an-evaluation-license/?utm_source=3PW&utm_medium=OT&utm_campaign=WW-MU-MUI-OR-O_LEA-PR_NO-Q3_FY20_WW_JPME_Lost_Luggage_Distribution_COM_EVAL_GitHub&utm_term=Lost%20Luggage%20Distribution&utm_content=C_JPM) as a *commercial user*, or download a [free license](https://www.gurobi.com/academia/academic-program-and-licenses/?utm_source=3PW&utm_medium=OT&utm_campaign=WW-MU-EDU-OR-O_LEA-PR_NO-Q3_FY20_WW_JPME_Lost_Luggage_Distribution_COM_EVAL_GitHub&utm_term=Lost%20Luggage%20Distribution&utm_content=C_JPM) as an *academic user*.

## Model Formulation
### Parameters

We use the [Greek values](https://en.wikipedia.org/wiki/Greeks_\(finance\)) that are traditional in finance:

- $\delta$: n-element vector measuring the change in price for each stock
- $\sigma$: n x n matrix measuring the covariance among stocks

There is one additional parameter when solving the model parametrically:

- r: target return

### Decision Variables
- $x \ge 0$: n-element vector where each element represents the fraction of the porfolio to invest in each stock

### Objective Function
Minimize the total risk, a convex quadratic function:

\begin{equation}
\min x^t \cdot \sigma \cdot x
\end{equation}

### Constraints

Allocate the entire portfolio: the total investments should be 1.0 (100%), where $e$ is a unit vector (all 1's):

\begin{equation}
e \cdot x = 1
\end{equation}

Return: When we solve the model parametrically for different return values $r$, we add a constraint on the target return:

\begin{equation}
\delta \cdot x = r
\end{equation}

## Python Implementation
### Stock data
Use [yfinance](https://pypi.org/project/yfinance/) library to get the latest 2 years of _actual stock data_ from the 20 most profitable US companies, [according to Wikipedia in April 2021](https://en.wikipedia.org/wiki/List_of_largest_companies_in_the_United_States_by_revenue#List_of_companies_by_profit).

! conda install -c conda-forge yfinance
import scipy as sp

```python
import yfinance as yf

stocks = ['BRK-A', 'AAPL', 'MSFT', 'JPM', 'GOOG', 'BAC', 'INTC', 'WFC',
          'C', 'VZ', 'FB', 'PFE', 'JNJ', 'WMT', 'XOM',
          'FNMA', 'T', 'UNH', 'CMCSA', 'V' ]

data = yf.download(stocks, period='2y')
```

    [*********************100%***********************]  20 of 20 completed

### Compute Greeks
Using the downloaded stock data, find the delta (return), sigma (covariance) and standard deviation values for stock prices:

```python
import numpy as np

closes = np.transpose(np.array(data.Close)) # matrix of daily closing prices
absdiff = np.diff(closes)                   # change in closing price each day
reldiff = np.divide(absdiff, closes[:,:-1]) # relative change in daily closing price
delta = np.mean(reldiff, axis=1)            # mean price change
sigma = np.cov(reldiff)                     # covariance (standard deviations)
std = np.std(reldiff, axis=1)               # standard deviation
```

## Minimize risk by solving QP model

```python
import gurobipy as gp
from gurobipy import GRB
from math import sqrt
import scipy as sp

# Create an empty model
m = gp.Model('portfolio')

# Add matrix variable for the stocks
x = m.addMVar(len(stocks))

# Objective is to minimize risk (squared).  This is modeled using the
# covariance matrix, which measures the historical correlation between stocks
portfolio_risk = x @ sigma @ x
m.setObjective(portfolio_risk, GRB.MINIMIZE)

# Fix budget with a constraint
m.addConstr(x.sum() == 1, 'budget')

# Verify model formulation
m.write('portfolio_selection_optimization.lp')

# Optimize model to find the minimum risk portfolio
m.optimize()
```

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 1 rows, 20 columns and 20 nonzeros
    Model fingerprint: 0xfd1b9e23
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e+00, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [1e+00, 1e+00]
    Presolve time: 0.02s
    Presolved: 1 rows, 20 columns, 20 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 1.900e+02
     Factor NZ  : 2.100e+02
     Factor Ops : 2.870e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22742206e+05 -2.22742206e+05  1.38e+04 1.08e-06  1.00e+06     0s
       1   7.80635413e+04 -7.80840060e+04  1.22e+03 9.61e-08  9.55e+04     0s
       2   8.53610430e+02 -8.78289798e+02  3.65e+01 2.88e-09  2.96e+03     0s
       3   1.09865862e-02 -2.53369805e+01  5.30e-02 4.17e-12  5.56e+00     0s
       4   6.06679177e-04 -1.52388278e+01  5.30e-08 1.04e-17  7.62e-01     0s
       5   6.06521787e-04 -1.58107922e-02  4.10e-12 2.97e-13  8.21e-04     0s
       6   4.84239634e-04 -4.68319716e-04  5.88e-14 4.19e-15  4.76e-05     0s
       7   1.46703447e-04 -2.49495073e-04  3.47e-18 1.04e-17  1.98e-05     0s
       8   8.51283723e-05  4.09182989e-05  1.73e-18 2.17e-18  2.21e-06     0s
       9   7.40144398e-05  6.85406519e-05  3.43e-16 3.25e-18  2.74e-07     0s
      10   7.22877963e-05  7.11647688e-05  4.86e-17 3.45e-18  5.62e-08     0s
      11   7.19346468e-05  7.18520568e-05  2.78e-17 3.30e-18  4.13e-09     0s
      12   7.18912924e-05  7.18887212e-05  2.03e-17 4.35e-18  1.29e-10     0s

    Barrier solved model in 12 iterations and 0.05 seconds (0.00 work units)
    Optimal objective 7.18912924e-05

## Display minimum risk portfolio using Pandas

```python
import pandas as pd
minrisk_volatility = sqrt(m.ObjVal)
minrisk_return = delta @ x.X
pd.DataFrame(data=np.append(x.X, [minrisk_volatility, minrisk_return]),
             index=stocks + ['Volatility', 'Expected Return'],
             columns=['Minimum Risk Portfolio'])
```

      Minimum Risk Portfolio

      BRK-A
      2.253084e-03

      AAPL
      9.842427e-07

      MSFT
      1.186609e-01

      JPM
      1.076550e-06

      GOOG
      1.058126e-06

      BAC
      4.492278e-03

      INTC
      4.073060e-02

      WFC
      2.410924e-02

      C
      1.041839e-04

      VZ
      1.291962e-01

      FB
      3.321008e-06

      PFE
      3.224407e-02

      JNJ
      4.338211e-02

      WMT
      1.630622e-05

      XOM
      1.971519e-06

      FNMA
      1.822780e-06

      T
      3.386273e-01

      UNH
      1.205406e-06

      CMCSA
      2.660991e-01

      V
      7.319782e-05

      Volatility
      8.478873e-03

      Expected Return
      5.927779e-04

## Compute the efficient frontier
Solve the QP parametrically to find the lowest risk portfolio for different expected returns.

```python
# Create an expression representing the expected return for the portfolio
portfolio_return = delta @ x
target = m.addConstr(portfolio_return == minrisk_return, 'target')

# Solve for efficient frontier by varying target return
frontier = np.empty((2,0))
for r in np.linspace(delta.min(), delta.max(), 25):
    target[0].rhs = r
    m.optimize()
    frontier = np.append(frontier, [[sqrt(m.ObjVal)],[r]], axis=1)
```

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0xebad5b5c
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [4e-04, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22793820e+05 -2.22793820e+05  1.32e+04 6.05e-03  1.00e+06     0s
       1   7.31004961e+04 -7.31192707e+04  1.04e+03 4.75e-04  8.50e+04     0s
       2   4.37576345e+02 -4.64208653e+02  3.34e+01 1.53e-05  2.83e+03     0s
       3   3.27427485e-03 -2.76655284e+01  4.51e-02 2.06e-08  5.20e+00     0s
       4   1.39936911e-03 -3.07483552e+00  2.14e-03 9.76e-10  3.19e-01     0s
       5   1.71848665e-03 -1.34484574e-02  8.63e-06 3.94e-12  1.49e-03     0s
       6   1.72275036e-03  1.69235266e-03  8.66e-09 3.96e-15  2.26e-06     0s
       7   1.72275463e-03  1.72272423e-03  8.66e-12 1.18e-13  2.26e-09     0s
       8   1.72275463e-03  1.72275460e-03  8.65e-15 9.10e-13  2.26e-12     0s

    Barrier solved model in 8 iterations and 0.11 seconds (0.00 work units)
    Optimal objective 1.72275463e-03

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0xb10d530e
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [3e-04, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22795103e+05 -2.22795103e+05  1.33e+04 6.20e-03  1.00e+06     0s
       1   7.31003771e+04 -7.31193662e+04  1.04e+03 4.87e-04  8.50e+04     0s
       2   4.37580788e+02 -4.64170302e+02  3.34e+01 1.56e-05  2.83e+03     0s
       3   2.96225318e-03 -2.75794270e+01  4.33e-02 2.03e-08  5.05e+00     0s
       4   1.18577922e-03 -4.37927032e+00  1.25e-03 5.84e-10  3.07e-01     0s
       5   1.32419687e-03 -4.38975682e-01  1.25e-09 5.84e-16  2.20e-02     0s
       6   1.32382894e-03 -6.16268387e-04  4.25e-12 1.73e-18  9.70e-05     0s
       7   1.01164819e-03  1.58846804e-05  3.77e-13 2.17e-19  4.98e-05     0s
       8   5.27495817e-04  2.17352240e-04  3.05e-16 1.67e-16  1.55e-05     0s
       9   3.65876986e-04  3.37079019e-04  1.14e-16 1.11e-16  1.44e-06     0s
      10   3.40706804e-04  3.40325438e-04  1.93e-16 1.11e-16  1.91e-08     0s
      11   3.40338344e-04  3.40337963e-04  1.08e-14 5.55e-17  1.90e-11     0s

    Barrier solved model in 11 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 3.40338344e-04

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x11bce6c9
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [2e-04, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22796386e+05 -2.22796386e+05  1.33e+04 6.35e-03  1.00e+06     0s
       1   7.31002543e+04 -7.31194579e+04  1.04e+03 4.99e-04  8.50e+04     0s
       2   4.37585228e+02 -4.64131900e+02  3.34e+01 1.60e-05  2.83e+03     0s
       3   2.66491222e-03 -2.74924692e+01  4.16e-02 1.99e-08  4.90e+00     0s
       4   1.00318861e-03 -5.72644763e+00  5.92e-04 2.84e-10  3.24e-01     0s
       5   1.04666171e-03 -1.11388422e-01  3.86e-08 1.85e-14  5.62e-03     0s
       6   1.04344380e-03 -1.79655736e-03  9.23e-10 4.42e-16  1.42e-04     0s
       7   2.09995948e-04 -2.87417382e-03  5.10e-11 2.39e-17  1.54e-04     0s
       8   2.07656421e-04  6.66491293e-05  1.98e-12 8.67e-19  7.05e-06     0s
       9   1.95072069e-04  1.40165842e-04  1.30e-15 6.07e-18  2.75e-06     0s
      10   1.84227536e-04  1.75479850e-04  3.12e-17 2.08e-17  4.37e-07     0s
      11   1.78208014e-04  1.77172859e-04  4.68e-17 1.39e-17  5.18e-08     0s
      12   1.77363770e-04  1.77361878e-04  7.81e-17 2.13e-17  9.46e-11     0s

    Barrier solved model in 12 iterations and 0.02 seconds (0.00 work units)
    Optimal objective 1.77363770e-04

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0xb67ddb65
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [1e-04, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22797670e+05 -2.22797670e+05  1.32e+04 6.50e-03  1.00e+06     0s
       1   7.31001314e+04 -7.31195495e+04  1.04e+03 5.11e-04  8.50e+04     0s
       2   4.37589670e+02 -4.64093451e+02  3.34e+01 1.64e-05  2.83e+03     0s
       3   2.38271409e-03 -2.74046503e+01  3.99e-02 1.96e-08  4.74e+00     0s
       4   8.65290374e-04 -7.05358898e+00  1.74e-04 8.53e-11  3.63e-01     0s
       5   8.72008610e-04 -1.05092627e-02  2.36e-09 1.16e-15  5.69e-04     0s
       6   7.63389651e-04 -1.83553640e-03  4.36e-10 2.13e-16  1.30e-04     0s
       7   1.99375207e-04 -2.17748510e-03  4.93e-16 1.11e-16  1.19e-04     0s
       8   1.89882009e-04 -1.90520035e-05  3.82e-17 1.46e-16  1.04e-05     0s
       9   1.65931837e-04  9.68361445e-05  5.29e-17 2.89e-17  3.45e-06     0s
      10   1.41378819e-04  1.13477605e-04  1.39e-17 6.94e-18  1.40e-06     0s
      11   1.32823416e-04  1.30387840e-04  1.91e-17 6.94e-18  1.22e-07     0s
      12   1.31520534e-04  1.31312853e-04  1.47e-17 4.97e-18  1.04e-08     0s
      13   1.31377431e-04  1.31371371e-04  3.82e-17 1.04e-17  3.03e-10     0s

    Barrier solved model in 13 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 1.31377431e-04

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0xf9d7d23e
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [1e-05, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22798953e+05 -2.22798953e+05  1.33e+04 6.65e-03  1.00e+06     0s
       1   7.31000086e+04 -7.31196412e+04  1.04e+03 5.23e-04  8.50e+04     0s
       2   4.37594114e+02 -4.64054957e+02  3.34e+01 1.68e-05  2.83e+03     0s
       3   2.11777399e-03 -2.73159427e+01  3.81e-02 1.91e-08  4.59e+00     0s
       4   7.36331654e-04 -8.58665693e+00  3.81e-08 1.91e-14  4.29e-01     0s
       5   7.36256636e-04 -1.02129611e-02  1.05e-11 5.20e-18  5.47e-04     0s
       6   6.23350272e-04 -1.59377608e-03  1.75e-12 1.73e-18  1.11e-04     0s
       7   1.73406728e-04 -1.40462228e-03  7.81e-18 1.11e-16  7.89e-05     0s
       8   1.66175028e-04  1.43442148e-05  3.47e-18 9.71e-17  7.59e-06     0s
       9   1.19017217e-04  4.55698621e-05  8.67e-18 6.94e-18  3.67e-06     0s
      10   1.05027778e-04  9.72587049e-05  8.67e-18 6.94e-18  3.88e-07     0s
      11   1.01680681e-04  1.01364709e-04  6.94e-17 7.23e-18  1.58e-08     0s
      12   1.01475700e-04  1.01475171e-04  1.39e-16 6.94e-18  2.64e-11     0s

    Barrier solved model in 12 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 1.01475700e-04

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x4ec486b0
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [1e-04, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22800236e+05 -2.22800236e+05  1.33e+04 6.80e-03  1.00e+06     0s
       1   7.30998858e+04 -7.31197329e+04  1.04e+03 5.34e-04  8.50e+04     0s
       2   4.37598561e+02 -4.64016418e+02  3.34e+01 1.72e-05  2.83e+03     0s
       3   1.87134593e-03 -2.72266437e+01  3.63e-02 1.86e-08  4.43e+00     0s
       4   6.35564216e-04 -9.26489047e+00  3.63e-08 1.86e-14  4.63e-01     0s
       5   6.35493249e-04 -1.09281405e-02  9.00e-12 4.34e-18  5.78e-04     0s
       6   5.30383454e-04 -1.59936927e-03  1.34e-12 1.73e-18  1.06e-04     0s
       7   1.66728294e-04 -1.32576236e-03  1.56e-17 8.33e-17  7.46e-05     0s
       8   1.55064627e-04 -2.23960683e-05  1.73e-18 4.37e-17  8.87e-06     0s
       9   1.08520801e-04  2.33636356e-05  2.43e-17 8.04e-18  4.26e-06     0s
      10   9.11039222e-05  6.04526258e-05  4.34e-18 5.75e-18  1.53e-06     0s
      11   8.58318802e-05  8.33078686e-05  3.47e-18 1.73e-18  1.26e-07     0s
      12   8.43527452e-05  8.42212407e-05  4.56e-16 3.47e-18  6.58e-09     0s
      13   8.42441018e-05  8.42435180e-05  5.66e-16 3.47e-18  2.92e-11     0s

    Barrier solved model in 13 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 8.42441018e-05

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x509ef8ce
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [2e-04, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22801519e+05 -2.22801519e+05  1.32e+04 6.95e-03  1.00e+06     0s
       1   7.30997630e+04 -7.31198246e+04  1.04e+03 5.46e-04  8.50e+04     0s
       2   4.37603009e+02 -4.63977833e+02  3.34e+01 1.75e-05  2.83e+03     0s
       3   1.64577538e-03 -2.71373405e+01  3.39e-02 1.78e-08  4.22e+00     0s
       4   5.48939250e-04 -9.73827880e+00  3.39e-08 1.78e-14  4.87e-01     0s
       5   5.48875494e-04 -1.13485217e-02  7.52e-12 1.95e-13  5.95e-04     0s
       6   4.53342106e-04 -1.45726219e-03  9.65e-13 2.50e-14  9.55e-05     0s
       7   1.56652582e-04 -1.09763315e-03  1.04e-17 2.78e-17  6.27e-05     0s
       8   1.42809048e-04 -3.30875309e-05  1.73e-18 8.67e-18  8.79e-06     0s
       9   9.83654762e-05  2.37404301e-06  1.91e-17 3.47e-18  4.80e-06     0s
      10   8.25905986e-05  5.75105796e-05  3.47e-18 5.20e-18  1.25e-06     0s
      11   7.90792982e-05  7.52487315e-05  3.47e-18 3.36e-18  1.92e-07     0s
      12   7.76073816e-05  7.68450543e-05  1.56e-17 1.73e-18  3.81e-08     0s
      13   7.72390229e-05  7.72170204e-05  6.07e-17 5.20e-18  1.10e-09     0s
      14   7.72213138e-05  7.72212918e-05  5.38e-17 8.67e-19  1.10e-12     0s

    Barrier solved model in 14 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 7.72213138e-05

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x3187f6f6
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [3e-04, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22802802e+05 -2.22802802e+05  1.32e+04 7.10e-03  1.00e+06     0s
       1   7.30996402e+04 -7.31199162e+04  1.04e+03 5.58e-04  8.50e+04     0s
       2   4.37607460e+02 -4.63939202e+02  3.34e+01 1.79e-05  2.83e+03     0s
       3   1.43476910e-03 -2.70465477e+01  3.17e-02 1.70e-08  4.04e+00     0s
       4   4.70103034e-04 -1.00700724e+01  3.17e-08 1.70e-14  5.04e-01     0s
       5   4.70048161e-04 -1.15480989e-02  6.15e-12 1.18e-14  6.01e-04     0s
       6   3.86719689e-04 -1.19510579e-03  6.37e-13 6.74e-16  7.91e-05     0s
       7   1.45891931e-04 -8.06499077e-04  6.94e-18 2.78e-17  4.76e-05     0s
       8   1.28637902e-04 -2.77522807e-05  3.47e-18 9.97e-18  7.82e-06     0s
       9   9.08630728e-05  2.62692062e-05  3.82e-17 6.94e-18  3.23e-06     0s
      10   7.83359209e-05  5.81505686e-05  6.94e-18 6.94e-18  1.01e-06     0s
      11   7.55012061e-05  7.12618499e-05  5.20e-18 1.69e-18  2.12e-07     0s
      12   7.44187375e-05  7.39003131e-05  1.34e-16 3.25e-18  2.59e-08     0s
      13   7.42088118e-05  7.41113432e-05  6.42e-17 1.54e-18  4.87e-09     0s
      14   7.41526737e-05  7.41500003e-05  2.08e-17 1.19e-18  1.34e-10     0s

    Barrier solved model in 14 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 7.41526737e-05

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0xdd648cad
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [5e-04, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22804085e+05 -2.22804085e+05  1.33e+04 7.25e-03  1.00e+06     0s
       1   7.30995170e+04 -7.31200075e+04  1.04e+03 5.70e-04  8.50e+04     0s
       2   4.37611895e+02 -4.63900508e+02  3.34e+01 1.83e-05  2.83e+03     0s
       3   1.23789309e-03 -2.69541203e+01  3.00e-02 1.64e-08  3.89e+00     0s
       4   3.98381760e-04 -1.02960625e+01  3.00e-08 1.64e-14  5.15e-01     0s
       5   3.98336507e-04 -1.15813099e-02  4.91e-12 3.47e-18  5.99e-04     0s
       6   3.30273910e-04 -9.08533546e-04  3.86e-13 1.73e-18  6.19e-05     0s
       7   1.36761223e-04 -5.30634576e-04  1.04e-17 2.08e-17  3.34e-05     0s
       8   1.12206991e-04 -8.10418672e-06  3.47e-18 8.67e-18  6.02e-06     0s
       9   8.21615680e-05  4.84239748e-05  1.39e-17 8.67e-19  1.69e-06     0s
      10   7.45934062e-05  6.96838748e-05  3.47e-18 4.34e-18  2.45e-07     0s
      11   7.29926441e-05  7.21931451e-05  8.67e-17 6.24e-18  4.00e-08     0s
      12   7.26173849e-05  7.25536588e-05  5.33e-17 1.95e-18  3.19e-09     0s
      13   7.25810670e-05  7.25760094e-05  5.55e-17 1.33e-18  2.53e-10     0s

    Barrier solved model in 13 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 7.25810670e-05

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x005c7052
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [6e-04, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22805368e+05 -2.22805368e+05  1.32e+04 7.40e-03  1.00e+06     0s
       1   7.30993921e+04 -7.31200971e+04  1.04e+03 5.82e-04  8.50e+04     0s
       2   4.37616246e+02 -4.63861682e+02  3.34e+01 1.87e-05  2.83e+03     0s
       3   1.05998004e-03 -2.68612582e+01  2.79e-02 1.56e-08  3.71e+00     0s
       4   3.39678127e-04 -1.02918529e+01  2.79e-08 1.56e-14  5.15e-01     0s
       5   3.39641772e-04 -1.13639405e-02  3.84e-12 6.77e-14  5.85e-04     0s
       6   2.87120737e-04 -6.84852160e-04  2.29e-13 4.10e-15  4.86e-05     0s
       7   1.34984739e-04 -5.45988624e-04  3.47e-18 2.43e-17  3.40e-05     0s
       8   1.11304402e-04 -6.06464109e-06  1.73e-18 1.99e-17  5.87e-06     0s
       9   8.51242533e-05  4.53807487e-05  5.03e-17 4.89e-18  1.99e-06     0s
      10   7.53118174e-05  6.28968168e-05  1.73e-17 1.08e-18  6.21e-07     0s
      11   7.30173377e-05  7.03875053e-05  5.20e-18 1.95e-18  1.31e-07     0s
      12   7.20898375e-05  7.17952763e-05  2.69e-17 3.25e-18  1.47e-08     0s
      13   7.19255147e-05  7.19097184e-05  7.29e-17 2.20e-18  7.90e-10     0s
      14   7.19129985e-05  7.19124281e-05  3.21e-16 4.24e-18  2.85e-11     0s

    Barrier solved model in 14 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 7.19129985e-05

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x91447a89
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [7e-04, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22806651e+05 -2.22806651e+05  1.32e+04 7.55e-03  1.00e+06     0s
       1   7.30992671e+04 -7.31201866e+04  1.04e+03 5.93e-04  8.50e+04     0s
       2   4.37620600e+02 -4.63822811e+02  3.34e+01 1.91e-05  2.83e+03     0s
       3   9.00255112e-04 -2.67678134e+01  2.56e-02 1.46e-08  3.50e+00     0s
       4   2.92773843e-04 -1.00336212e+01  2.56e-08 1.46e-14  5.02e-01     0s
       5   2.92745257e-04 -1.08897607e-02  2.93e-12 1.73e-18  5.59e-04     0s
       6   2.53238199e-04 -4.93988318e-04  1.31e-13 4.34e-19  3.74e-05     0s
       7   1.22687991e-04 -4.19630087e-04  6.94e-18 2.08e-17  2.71e-05     0s
       8   9.98707223e-05  1.76212311e-05  5.20e-18 6.94e-18  4.11e-06     0s
       9   8.02525812e-05  6.14390364e-05  1.53e-16 1.30e-18  9.41e-07     0s
      10   7.37840396e-05  7.02463683e-05  9.97e-17 1.52e-18  1.77e-07     0s
      11   7.24494404e-05  7.20188814e-05  5.20e-18 3.73e-18  2.15e-08     0s
      12   7.21794777e-05  7.21605730e-05  5.20e-17 1.45e-18  9.45e-10     0s
      13   7.21633968e-05  7.21630531e-05  1.05e-15 3.69e-18  1.72e-11     0s

    Barrier solved model in 13 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 7.21633968e-05

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0xf36d50aa
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [8e-04, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22807934e+05 -2.22807934e+05  1.32e+04 7.70e-03  1.00e+06     0s
       1   7.30991422e+04 -7.31202761e+04  1.04e+03 6.05e-04  8.50e+04     0s
       2   4.37624956e+02 -4.63783894e+02  3.35e+01 1.95e-05  2.83e+03     0s
       3   7.51956092e-04 -2.66733479e+01  2.32e-02 1.35e-08  3.30e+00     0s
       4   2.53469473e-04 -9.58101148e+00  2.32e-08 1.35e-14  4.79e-01     0s
       5   2.53447600e-04 -1.02198336e-02  2.16e-12 1.91e-13  5.24e-04     0s
       6   2.24447962e-04 -3.10813778e-04  6.71e-14 5.79e-15  2.68e-05     0s
       7   1.09355690e-04 -2.75831127e-04  3.47e-18 1.04e-17  1.93e-05     0s
       8   9.07117778e-05  4.45561753e-05  5.20e-18 8.67e-19  2.31e-06     0s
       9   7.77339438e-05  6.91820067e-05  9.54e-17 2.07e-18  4.28e-07     0s
      10   7.40186225e-05  7.29956735e-05  5.72e-17 2.53e-18  5.11e-08     0s
      11   7.34046579e-05  7.33553860e-05  2.26e-17 3.04e-18  2.46e-09     0s
      12   7.33712935e-05  7.33705759e-05  9.54e-17 2.57e-18  3.59e-11     0s

    Barrier solved model in 12 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 7.33712935e-05

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x9727ad06
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [9e-04, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22809217e+05 -2.22809217e+05  1.33e+04 7.85e-03  1.00e+06     0s
       1   7.30990172e+04 -7.31203657e+04  1.04e+03 6.17e-04  8.50e+04     0s
       2   4.37629313e+02 -4.63744931e+02  3.35e+01 1.98e-05  2.83e+03     0s
       3   6.20915160e-04 -2.65777967e+01  2.08e-02 1.23e-08  3.09e+00     0s
       4   2.22394880e-04 -8.94597463e+00  2.08e-08 1.23e-14  4.47e-01     0s
       5   2.22378699e-04 -9.38257428e-03  1.53e-12 1.73e-18  4.80e-04     0s
       6   2.01837556e-04 -2.10854303e-04  3.62e-14 1.73e-18  2.06e-05     0s
       7   9.89833276e-05 -1.78716507e-04  1.39e-17 3.47e-18  1.39e-05     0s
       8   8.66661948e-05  5.80210291e-05  6.94e-18 9.54e-18  1.43e-06     0s
       9   7.87386669e-05  6.96216681e-05  1.56e-17 3.92e-18  4.56e-07     0s
      10   7.59517084e-05  7.52924451e-05  6.94e-18 1.79e-18  3.30e-08     0s
      11   7.55375633e-05  7.55157576e-05  5.72e-17 4.61e-18  1.09e-09     0s
      12   7.55281060e-05  7.55262234e-05  4.02e-16 6.27e-18  9.41e-11     0s

    Barrier solved model in 12 iterations and 0.04 seconds (0.00 work units)
    Optimal objective 7.55281060e-05

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x4eed185d
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [1e-03, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22810501e+05 -2.22810501e+05  1.32e+04 8.00e-03  1.00e+06     0s
       1   7.30988923e+04 -7.31204552e+04  1.04e+03 6.29e-04  8.50e+04     0s
       2   4.37633673e+02 -4.63705922e+02  3.35e+01 2.02e-05  2.83e+03     0s
       3   5.11968322e-04 -2.64811613e+01  1.86e-02 1.12e-08  2.90e+00     0s
       4   1.98904627e-04 -8.21454181e+00  1.86e-08 1.12e-14  4.11e-01     0s
       5   1.98893132e-04 -8.48630366e-03  1.07e-12 2.95e-13  4.34e-04     0s
       6   1.85674583e-04 -1.52127350e-04  2.31e-14 6.57e-15  1.69e-05     0s
       7   9.51981114e-05 -1.18820396e-04  8.67e-18 5.20e-18  1.07e-05     0s
       8   8.74377094e-05  6.16809133e-05  6.94e-18 6.97e-18  1.29e-06     0s
       9   8.07490185e-05  7.32530784e-05  1.78e-17 2.60e-18  3.75e-07     0s
      10   7.87840654e-05  7.84672017e-05  6.94e-18 1.00e-18  1.58e-08     0s
      11   7.85931434e-05  7.85869394e-05  7.29e-17 2.17e-18  3.10e-10     0s

    Barrier solved model in 11 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 7.85931434e-05

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x10e64b5e
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [1e-03, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22811784e+05 -2.22811784e+05  1.32e+04 8.15e-03  1.00e+06     0s
       1   7.30987761e+04 -7.31205534e+04  1.04e+03 6.41e-04  8.50e+04     0s
       2   4.37638773e+02 -4.63667604e+02  3.35e+01 2.06e-05  2.83e+03     0s
       3   5.02756791e-04 -2.63834811e+01  2.02e-02 1.24e-08  3.03e+00     0s
       4   1.82277356e-04 -8.77286231e+00  2.02e-08 1.24e-14  4.39e-01     0s
       5   1.82270589e-04 -8.93177502e-03  7.85e-13 1.74e-13  4.56e-04     0s
       6   1.75088686e-04 -1.04965263e-04  1.44e-14 2.95e-15  1.40e-05     0s
       7   1.02874800e-04 -1.66642330e-05  3.47e-18 3.47e-18  5.98e-06     0s
       8   8.88005322e-05  7.18653685e-05  6.94e-18 3.04e-18  8.47e-07     0s
       9   8.38388450e-05  7.98355839e-05  8.24e-17 1.30e-18  2.00e-07     0s
      10   8.26127897e-05  8.25005018e-05  1.21e-17 1.80e-18  5.61e-09     0s
      11   8.25448247e-05  8.25439713e-05  3.26e-16 3.47e-18  4.27e-11     0s

    Barrier solved model in 11 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 8.25448247e-05

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x238775bd
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [1e-03, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22813067e+05 -2.22813067e+05  1.32e+04 8.31e-03  1.00e+06     0s
       1   7.30986998e+04 -7.31206916e+04  1.04e+03 6.52e-04  8.50e+04     0s
       2   4.37643993e+02 -4.63629358e+02  3.35e+01 2.10e-05  2.83e+03     0s
       3   5.27862823e-04 -2.62849557e+01  2.28e-02 1.43e-08  3.25e+00     0s
       4   1.72485030e-04 -9.53894090e+00  2.28e-08 1.43e-14  4.77e-01     0s
       5   1.72481003e-04 -9.62060969e-03  6.08e-13 2.70e-13  4.90e-04     0s
       6   1.68609802e-04 -6.70350154e-05  9.87e-15 4.29e-15  1.18e-05     0s
       7   1.04695644e-04 -1.89458593e-06  1.04e-17 3.17e-18  5.33e-06     0s
       8   9.27170507e-05  7.77728618e-05  5.20e-18 6.94e-18  7.47e-07     0s
       9   8.80795407e-05  8.67755725e-05  2.27e-16 2.60e-18  6.52e-08     0s
      10   8.74313205e-05  8.73536839e-05  1.39e-17 2.46e-18  3.88e-09     0s
      11   8.73938257e-05  8.73936994e-05  3.30e-17 1.08e-18  6.31e-12     0s

    Barrier solved model in 11 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 8.73938257e-05

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x8fc08853
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [1e-03, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22814350e+05 -2.22814350e+05  1.32e+04 8.46e-03  1.00e+06     0s
       1   7.30987059e+04 -7.31209121e+04  1.04e+03 6.64e-04  8.50e+04     0s
       2   4.37648086e+02 -4.63589938e+02  3.35e+01 2.14e-05  2.84e+03     0s
       3   5.56600688e-04 -2.61854665e+01  2.54e-02 1.62e-08  3.46e+00     0s
       4   1.68081982e-04 -1.00555416e+01  2.54e-08 1.62e-14  5.03e-01     0s
       5   1.68079340e-04 -1.00811411e-02  4.90e-13 2.63e-13  5.12e-04     0s
       6   1.65908128e-04 -3.24162666e-05  7.55e-15 4.28e-15  9.92e-06     0s
       7   1.03139765e-04 -4.35752432e-05  1.04e-17 6.94e-18  7.34e-06     0s
       8   9.69802485e-05  8.68683805e-05  6.94e-18 3.04e-18  5.06e-07     0s
       9   9.35568898e-05  9.30461050e-05  9.19e-17 1.73e-18  2.55e-08     0s
      10   9.32194437e-05  9.32166279e-05  4.86e-17 1.42e-18  1.41e-10     0s

    Barrier solved model in 10 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 9.32194437e-05

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0xbb4b312d
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [1e-03, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22815633e+05 -2.22815633e+05  1.32e+04 8.61e-03  1.00e+06     0s
       1   7.30987420e+04 -7.31211627e+04  1.04e+03 6.76e-04  8.50e+04     0s
       2   4.37651703e+02 -4.63549992e+02  3.35e+01 2.17e-05  2.84e+03     0s
       3   5.88771454e-04 -2.60849628e+01  2.80e-02 1.82e-08  3.67e+00     0s
       4   1.68299782e-04 -1.03169670e+01  2.80e-08 1.82e-14  5.16e-01     0s
       5   1.68297784e-04 -1.03295330e-02  4.91e-13 4.16e-13  5.25e-04     0s
       6   1.66639856e-04 -1.96202248e-05  7.04e-15 5.86e-15  9.31e-06     0s
       7   1.16660381e-04 -5.38608193e-06  1.39e-17 1.73e-17  6.10e-06     0s
       8   1.06205195e-04  9.47405209e-05  2.43e-17 4.98e-18  5.73e-07     0s
       9   1.01208557e-04  1.00288076e-04  1.98e-16 3.47e-18  4.60e-08     0s
      10   1.00660234e-04  1.00627479e-04  5.38e-17 1.73e-18  1.64e-09     0s
      11   1.00639996e-04  1.00638551e-04  6.65e-15 1.73e-18  7.22e-11     0s

    Barrier solved model in 11 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 1.00639996e-04

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x60f9e125
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [2e-03, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22816916e+05 -2.22816916e+05  1.33e+04 8.76e-03  1.00e+06     0s
       1   7.30987779e+04 -7.31214130e+04  1.04e+03 6.88e-04  8.50e+04     0s
       2   4.37655369e+02 -4.63510047e+02  3.35e+01 2.21e-05  2.84e+03     0s
       3   6.24374356e-04 -2.59834452e+01  3.06e-02 2.02e-08  3.89e+00     0s
       4   1.72622773e-04 -1.03011959e+01  3.06e-08 2.02e-14  5.15e-01     0s
       5   1.72621007e-04 -1.02934213e-02  4.90e-13 2.76e-13  5.23e-04     0s
       6   1.71140767e-04  1.84674246e-07  6.44e-15 3.67e-15  8.55e-06     0s
       7   1.27016560e-04  1.85634393e-05  1.99e-17 2.08e-17  5.42e-06     0s
       8   1.16033265e-04  1.05389481e-04  1.21e-17 5.20e-18  5.32e-07     0s
       9   1.10759079e-04  1.09766593e-04  2.67e-16 2.60e-18  4.96e-08     0s
      10   1.10140732e-04  1.10126300e-04  8.33e-17 8.62e-19  7.22e-10     0s
      11   1.10127144e-04  1.10127118e-04  4.68e-15 1.73e-18  1.29e-12     0s

    Barrier solved model in 11 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 1.10127144e-04

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0xb22eb206
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [2e-03, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22818199e+05 -2.22818199e+05  1.32e+04 8.91e-03  1.00e+06     0s
       1   7.30988138e+04 -7.31216633e+04  1.04e+03 7.00e-04  8.50e+04     0s
       2   4.37659037e+02 -4.63470055e+02  3.35e+01 2.25e-05  2.84e+03     0s
       3   6.62873437e-04 -2.58807792e+01  3.33e-02 2.24e-08  4.11e+00     0s
       4   1.80451704e-04 -1.00007024e+01  3.33e-08 2.24e-14  5.00e-01     0s
       5   1.80449962e-04 -9.98149721e-03  5.36e-13 4.75e-13  5.08e-04     0s
       6   1.78973893e-04  1.45319693e-05  6.97e-15 5.96e-15  8.22e-06     0s
       7   1.37615966e-04  5.81376485e-05  3.12e-17 8.24e-18  3.97e-06     0s
       8   1.27276243e-04  1.17144112e-04  3.30e-17 6.07e-18  5.07e-07     0s
       9   1.22626121e-04  1.22027862e-04  3.14e-16 1.73e-18  2.99e-08     0s
      10   1.22211959e-04  1.22210812e-04  8.85e-17 2.43e-18  5.73e-11     0s

    Barrier solved model in 10 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 1.22211959e-04

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x6bf7f63a
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [2e-03, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22819483e+05 -2.22819483e+05  1.32e+04 9.06e-03  1.00e+06     0s
       1   7.30988473e+04 -7.31219113e+04  1.04e+03 7.12e-04  8.50e+04     0s
       2   4.37662670e+02 -4.63429980e+02  3.35e+01 2.29e-05  2.84e+03     0s
       3   7.03562210e-04 -2.57767801e+01  3.61e-02 2.47e-08  4.35e+00     0s
       4   1.89947178e-04 -8.02291347e+00  9.92e-05 6.78e-11  4.06e-01     0s
       5   1.90044507e-04 -7.97552130e-03  2.47e-09 1.69e-15  4.08e-04     0s
       6   1.88450791e-04  1.59992107e-05  4.32e-11 3.12e-17  8.62e-06     0s
       7   1.54614697e-04  1.15519257e-04  6.94e-17 9.78e-18  1.95e-06     0s
       8   1.40429789e-04  1.30937947e-04  1.39e-17 1.73e-18  4.75e-07     0s
       9   1.37209341e-04  1.36909942e-04  5.26e-16 3.47e-18  1.50e-08     0s
      10   1.36980712e-04  1.36974072e-04  9.45e-16 4.34e-18  3.32e-10     0s

    Barrier solved model in 10 iterations and 0.04 seconds (0.00 work units)
    Optimal objective 1.36980712e-04

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x09d5e531
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [2e-03, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22820766e+05 -2.22820766e+05  1.33e+04 9.21e-03  1.00e+06     0s
       1   7.30988805e+04 -7.31221589e+04  1.04e+03 7.23e-04  8.50e+04     0s
       2   4.37666300e+02 -4.63389854e+02  3.35e+01 2.33e-05  2.84e+03     0s
       3   7.47454798e-04 -2.56717008e+01  3.90e-02 2.71e-08  4.58e+00     0s
       4   2.00382671e-04 -6.77614844e+00  1.12e-03 7.75e-10  4.02e-01     0s
       5   2.05860678e-04 -2.98786061e-01  1.12e-09 7.77e-16  1.49e-02     0s
       6   2.05803614e-04 -3.18430932e-04  8.41e-13 4.34e-19  2.62e-05     0s
       7   1.86705665e-04  1.24166237e-04  2.02e-14 1.73e-18  3.13e-06     0s
       8   1.66289456e-04  1.54302669e-04  9.99e-16 1.21e-17  5.99e-07     0s
       9   1.59034685e-04  1.55889400e-04  2.43e-17 3.47e-18  1.57e-07     0s
      10   1.57798003e-04  1.57772413e-04  2.86e-16 3.47e-18  1.28e-09     0s
      11   1.57776799e-04  1.57776773e-04  4.02e-16 5.32e-18  1.28e-12     0s

    Barrier solved model in 11 iterations and 0.06 seconds (0.00 work units)
    Optimal objective 1.57776799e-04

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x67b1f5ab
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [2e-03, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22822049e+05 -2.22822049e+05  1.33e+04 9.36e-03  1.00e+06     0s
       1   7.30989138e+04 -7.31224066e+04  1.04e+03 7.35e-04  8.50e+04     0s
       2   4.37669932e+02 -4.63349682e+02  3.35e+01 2.37e-05  2.84e+03     0s
       3   7.94549975e-04 -2.55655419e+01  4.18e-02 2.95e-08  4.82e+00     0s
       4   2.13899501e-04 -5.62707959e+00  1.50e-03 1.06e-09  3.82e-01     0s
       5   2.33888673e-04 -6.78606269e-01  1.50e-09 1.06e-15  3.39e-02     0s
       6   2.33866026e-04 -6.47617196e-04  4.49e-13 1.53e-13  4.41e-05     0s
       7   2.14410244e-04  1.25106687e-04  2.31e-14 7.90e-15  4.47e-06     0s
       8   1.95214557e-04  1.82301821e-04  5.21e-16 1.73e-17  6.46e-07     0s
       9   1.88086260e-04  1.86820776e-04  2.50e-15 1.04e-17  6.33e-08     0s
      10   1.87117650e-04  1.87099627e-04  2.31e-16 1.04e-17  9.01e-10     0s
      11   1.87101179e-04  1.87101158e-04  6.85e-15 6.94e-18  1.05e-12     0s

    Barrier solved model in 11 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 1.87101179e-04

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x4776b3eb
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [2e-03, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22823332e+05 -2.22823332e+05  1.32e+04 9.51e-03  1.00e+06     0s
       1   7.30989470e+04 -7.31226542e+04  1.04e+03 7.47e-04  8.50e+04     0s
       2   4.37673566e+02 -4.63309463e+02  3.35e+01 2.40e-05  2.84e+03     0s
       3   8.44846509e-04 -2.54583036e+01  4.47e-02 3.21e-08  5.06e+00     0s
       4   2.35244829e-04 -4.26185172e+00  2.49e-03 1.79e-09  4.08e-01     0s
       5   2.98777602e-04 -1.37820822e+00  2.49e-09 1.79e-15  6.89e-02     0s
       6   2.98770609e-04 -1.24574431e-03  3.00e-13 4.16e-13  7.72e-05     0s
       7   2.88827553e-04  1.50825138e-04  1.99e-14 2.80e-14  6.90e-06     0s
       8   2.56983542e-04  2.01276419e-04  3.02e-16 2.78e-17  2.79e-06     0s
       9   2.44252289e-04  2.35333481e-04  6.52e-16 6.59e-17  4.46e-07     0s
      10   2.39015872e-04  2.38244820e-04  8.36e-16 3.88e-17  3.86e-08     0s
      11   2.38468163e-04  2.38459495e-04  3.30e-15 1.56e-17  4.33e-10     0s

    Barrier solved model in 11 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 2.38468163e-04

    Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (win64)
    Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 2 rows, 20 columns and 40 nonzeros
    Model fingerprint: 0x42d292f7
    Model has 210 quadratic objective terms
    Coefficient statistics:
      Matrix range     [1e-04, 1e+00]
      Objective range  [0e+00, 0e+00]
      QObjective range [1e-05, 3e-03]
      Bounds range     [0e+00, 0e+00]
      RHS range        [2e-03, 1e+00]
    Presolve time: 0.01s
    Presolved: 2 rows, 20 columns, 40 nonzeros
    Presolved model has 210 quadratic objective terms
    Ordering time: 0.00s

    Barrier statistics:
     Free vars  : 19
     AA' NZ     : 2.100e+02
     Factor NZ  : 2.310e+02
     Factor Ops : 3.311e+03 (less than 1 second per iteration)
     Threads    : 1

                      Objective                Residual
    Iter       Primal          Dual         Primal    Dual     Compl     Time
       0   2.22824615e+05 -2.22824615e+05  1.32e+04 9.66e-03  1.00e+06     0s
       1   7.30989802e+04 -7.31229018e+04  1.04e+03 7.59e-04  8.50e+04     0s
       2   4.37677203e+02 -4.63269198e+02  3.35e+01 2.44e-05  2.84e+03     0s
       3   8.99930006e-04 -2.53505898e+01  4.73e-02 3.45e-08  5.27e+00     0s
       4   2.66188232e-04 -2.53230097e+00  3.98e-03 2.90e-09  4.81e-01     0s
       5   3.97444383e-04 -2.22388615e-01  9.59e-05 6.99e-11  2.18e-02     0s
       6   3.97266779e-04 -5.10785636e-05  9.72e-08 7.09e-14  3.33e-05     0s
       7   3.97266590e-04  3.96818244e-04  9.72e-11 6.94e-17  3.33e-08     0s
       8   3.97266590e-04  3.97266141e-04  9.72e-14 6.82e-13  3.33e-11     0s

    Barrier solved model in 8 iterations and 0.03 seconds (0.00 work units)
    Optimal objective 3.97266590e-04

## Plot results
Use the matplot library to plot the optimized solutions, along with the individual stocks:

```python
import matplotlib.pyplot as plt
#plt.figure(figsize=(10,10))

fig, ax = plt.subplots(figsize=(10,8))

# Plot volatility versus expected return for individual stocks
ax.scatter(x=std, y=delta,
           color='Blue', label='Individual Stocks')
for i, stock in enumerate(stocks):
    ax.annotate(stock, (std[i], delta[i]))

# Plot volatility versus expected return for minimum risk portfolio
ax.scatter(x=minrisk_volatility, y=minrisk_return, color='DarkGreen')
ax.annotate('Minimum\nRisk\nPortfolio', (minrisk_volatility, minrisk_return),
            horizontalalignment='right')

# Plot efficient frontier
ax.plot(frontier[0], frontier[1], label='Efficient Frontier', color='DarkGreen')

# Format and display the final plot
ax.axis([frontier[0].min()*0.7, frontier[0].max()*1.3, delta.min()*1.2, delta.max()*1.2])
ax.set_xlabel('Volatility (standard deviation)')
ax.set_ylabel('Expected Return')
ax.legend()
ax.grid()
plt.show()
```


Consider the standard incomplete markets model and answer the following:

Write a python program that returns the recursive competitive equilibrium for a economy with
the following parameters:

   * intertemporal discount factor($\beta$) = 0.98;
   * CRRA utility function with $\sigma$ = 2;
   * depreciation rate $\delta$= 0.08;
   * production function: $f(K,L) = K^{\alpha}L^{1-\alpha}$
   * $\alpha$ = 0.44
   * state-transition matrix:
   * $\pi(y'|y)=$ $$\begin{bmatrix} 0.4 & 0.5 & 0.1 \\ 0.3 & 0.2 & 0.5 \\ 0.2 & 0.4 & 0.4 \end{bmatrix}$$.

Answer:

Importing modules

```python
import numpy as np
```

The recursive problem is a little different than we were doing so far. A stationary recursive competitive equilibrium
is a value function $v:Z \times M \rightarrow \mathbb{R} $, policy function $a': Z \rightarrow \mathbb{R}$ and
consumption $c: Z \rightarrow \mathbb{R}$, policy functions for the firm $K$ and $L$, prices $r,w$ and a mesure
$\phi \in M$ such that:
* $v,a', c$ are mesurable with respect to $B(Z)$, v satisfies the household Bellman's equation:
* $v(a,y,\phi)$ = $max_{c\geq 0, a' \geq 0}$ $(U(c) + \beta \sum_{y \in Y} \pi(y'|y)v(a', y';\phi))$
s.t $c + a' = wy + (1+r)a$ where $a',c$ are the associated policy functions for a given r and w.
* $K, L$ satisfy, given $r$ and $w$:
* $r =$ $F_{k}(K,L)$ - $\delta$
* $w =$ $F_{L}(K, L)$
* $K' \equiv a'(a,y) d\phi = K$ (because its stationary)
* $L$ = $\int y $d$\phi$
* $\int c(a,y,\phi)d\phi$ + $\int a'(a,y,\phi)d\phi$ = $F[K(\phi), L(\phi)] + (1-\delta)K(\phi)$

When something appears with a line e.g: $a'$ it stands for the future value of this parameter in this
case, tomorrow assets distribution. Every future distribution is defined by the associated policy function
for that variable. $r$ is the interest rate, $w$ is the wage, $K$ and $L$ are the capital stock and labor supply
respectively.

Now we will solve this problem for case asked. We already know that for the standard CRRA utility function we have:
$u(c) = \frac{c^{1-\delta}-1}{1-\delta}$. But now $\delta$ stands for depreciation rate and our risk aversion turned
in $\sigma$ so now we have, $u(c) = \frac{c^{1-\sigma}-1}{1-\sigma}$.

```python
def util_func(x):
    return (x**(1-sigma))/(1-sigma)
```

Note that we didn't defined $\sigma$ , we will it next. We will create a function that assigns values for the parameters
needed to solve the equilibrium.

```python
def parameters_objects():
    global beta, sigma,delta,alpha,pi,y_domain, a_domain, V, iG, n_y, n_a, w,r
    beta = 0.98
    sigma = 2
    delta = 0.08
    alpha = 0.44
    pi = np.array([[0.4 , 0.5 , 0.1],
                   [0.3 , 0.2 , 0.5 ],
                   [0.2 , 0.4 , 0.4]])
    r = 0.05 #we will need some interest rate to maximize our objective function
    w = 1 #we will need the wage too
    # the income domain was not defined we will create one to make sure our program works properly
    y_min = 1e-2
    y_max = 1
    n_y = len(pi) #must have one income for each state
    y_domain = np.linspace(y_min , y_max, n_y)
    # neither the assets domain was defined
    a_max = 10
    n_a = 9 #three assets distributions for each income state
    # we need a non-Ponzi condition for this case
    barA = 0 # no borrow allowed
    a_domain = np.linspace(-barA, a_max, n_a)

    #Now we just neeed some place to store our value function and policy function
    V = np.zeros((n_y, n_a)) #value
    iG = np.zeros((n_y, n_a), dtype = np.int) #policy

```

Now we need to build our objective function: $U(c) + \beta \sum_{y \in Y} \pi(y'|y)v(a', y';\phi)$

```python
def build_objective(V):
    global n_y, n_a, w, r
    F_OBJ = np.zeros((n_y, n_a, n_a)) #one dimension to each income, asset, and future asset
    #looping through all income, assets and future assets
    for i_y in range(n_y):
        y = y_domain[i_y]
        for i_a in range(n_a):
            a = a_domain[i_a]
            for i_a_line in range(n_a): #i_a_line stands for future assets
                aa = a_domain[i_a_line]
                c = w*y + a - aa/(1+r) #consumption
                if c <= 0:
                    F_OBJ[i_y, i_a, i_a_line] = -np.inf
                else:
                    F_OBJ[i_y, i_a, i_a_line] = util_func(c) + beta*(np.dot(pi[i_y, :],V[:, i_a_line]))
    return F_OBJ
```

Solving the problem:

```python
def maximize_TV_IG(F_OBJ):
    #maximizing for time t
    TV = np.zeros((n_y, n_a))
    T_iG = np.zeros((n_y, n_a), dtype = np.int)
    for i_y, y in enumerate(y_domain):
        for i_a, a in enumerate(a_domain):
            TV[i_y, i_a] = np.max(F_OBJ[i_y, i_a, :]) # max value of f_obj
            T_iG[i_y, i_a] = np.argmax(F_OBJ[i_y, i_a, :]) # position associated to (y,a) pair that maximizes F_OBJ
    return TV, T_iG
```

Computing the stationary state:

```python
def compute_V_G_est():
    global V
    norm, tol = 2, 1e-7
    while norm>tol:
        F_OBJ = build_objective(V)
        TV, T_iG = maximize_TV_IG(F_OBJ)
        norm = np.max(abs(TV - V))
        V = np.copy(TV)
        iG = np.copy(T_iG)
    return V, iG
```

Now we will introduce future assets and heterogeneity, introducing an endogenous transition matrix function:

```python
def compute_Q(iG):
    #This function gives a markov transition function, Q_{r} to compute, a stationary measure phi_{r}
    #associated associated to this transition function
    Q = np.zeros((n_y*n_a, n_y*n_a))
    for i_y in range(n_y):
        for i_a in range(n_a):
            c_state = i_y*n_a + i_a
            for i_y_line in range(n_y):
                for i_a_line in range(n_a):
                    n_state = i_y_line*n_a + i_a_line
                    if iG[i_y, i_a] == i_a_line:
                        Q[c_state, n_state] += pi[i_y, i_y_line]
    return Q
```

Now we compute $\phi_{r}$

```python
def compute_phi(Q):
    global phi
    phi = np.ones(n_y*n_a) / (n_y*n_a)
    norm_Q, tol_Q = 1, 1e-6
    while norm_Q > tol_Q:
        T_phi = np.dot(phi, Q)
        norm_Q = max(abs(T_phi - phi))
        phi = T_phi/sum(T_phi)
    return phi
```

We need a few more things to compute the equilibrium:

```python
# Expected savings(assets)
def compute_Ea(phi):
    Ea = 0
    for i_y in range(n_y):
        for i_a in range(n_a):
            s_index = iG[i_y, i_a]
            savings = a_domain[s_index]
            t_index = i_y * n_a + i_a
            size = phi[t_index]
            Ea += savings*size
    return Ea
```

Labor supply

```python
def compute_L():
    L = 0
    for i_y in range(n_y):
        for i_a in range(n_a):
            labor_supply = y_domain[i_y]
            t_index = i_y*n_a + i_a
            size_l = phi[t_index]
            L += labor_supply*size_l
    return L
```

The production function is $f(K,L) = K^{\alpha}L^{1-\alpha}$. As we've seen:
\begin{equation}
r = F_{k}(K,L) - \delta
\end{equation}
So:
\begin{equation}
k = (\alpha/(r+\delta))^{(1/(1-\alpha)}
\end{equation}.

```python
def compute_k(r):
    k =(alpha/(r+delta))**(1/(1-alpha))
    return k
```

Similarly:
\begin{equation}
w = (1-\alpha) \times (k^{\alpha})
\end{equation}

```python
def compute_w(k):
    w = (1-alpha)*(k**alpha)
    return w
```

When we reach the equilibrium there should be no excess demand :
$d = K - E(a) = 0$. And finally we can compute the equilibrium.

```python
def compute_d(phi):
    k = compute_k(r)
    L = compute_L()
    K = k*L
    ea = compute_Ea(phi)
    d = K - ea
    return d
```

```python
def compute_equilibrium():
    global r, w, V, iG, Q, phi, L, k
    rho = beta**(-1)-1
    r_1, r_2 = -delta, rho #interest rate domain.
    norm_r, tol_r = 1, 1e-10
    while norm_r>tol_r:
        r = (r_1+r_2)/2
        k = compute_k(r)
        V, iG = compute_V_G_est()
        Q = compute_Q(iG)
        phi = compute_phi(Q)
        d = compute_d(phi)
        if d>0:
            r_1 = r
        elif d<0:
            r_2 = r
        norm_r = abs(r_1-r_2)
        #printing the interest rate at each step
        print('[d,r_L,r_H,norm]=[{:9.6f},{:9.6f},{:9.6f},{:9.6f}]'.format(d,r_1,r_2,norm_r))
```

```python
parameters_objects()
compute_equilibrium()
```

    <ipython-input-3-8bad40f2643f>:26: DeprecationWarning: `np.int` is a deprecated alias for the builtin `int`. To silence this warning, use `int` by itself. Doing this will not modify any behavior and is safe. When replacing `np.int`, you may wish to use e.g. `np.int64` or `np.int32` to specify the precision. If you wish to review your current use, check the release note link for additional information.
    Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations
      iG = np.zeros((n_y, n_a), dtype = np.int) #policy
    <ipython-input-5-d39ec0f74c3b>:4: DeprecationWarning: `np.int` is a deprecated alias for the builtin `int`. To silence this warning, use `int` by itself. Doing this will not modify any behavior and is safe. When replacing `np.int`, you may wish to use e.g. `np.int64` or `np.int32` to specify the precision. If you wish to review your current use, check the release note link for additional information.
    Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations
      T_iG = np.zeros((n_y, n_a), dtype = np.int)

    [d,r_L,r_H,norm]=[25.618551,-0.029796, 0.020408, 0.050204]
    [d,r_L,r_H,norm]=[12.419544,-0.004694, 0.020408, 0.025102]
    [d,r_L,r_H,norm]=[ 4.430990, 0.007857, 0.020408, 0.012551]
    [d,r_L,r_H,norm]=[ 3.337801, 0.014133, 0.020408, 0.006276]
    [d,r_L,r_H,norm]=[ 2.863614, 0.017270, 0.020408, 0.003138]
    [d,r_L,r_H,norm]=[ 2.642114, 0.018839, 0.020408, 0.001569]
    [d,r_L,r_H,norm]=[ 2.534993, 0.019624, 0.020408, 0.000784]
    [d,r_L,r_H,norm]=[ 2.482308, 0.020016, 0.020408, 0.000392]
    [d,r_L,r_H,norm]=[ 2.456181, 0.020212, 0.020408, 0.000196]
    [d,r_L,r_H,norm]=[ 2.443171, 0.020310, 0.020408, 0.000098]
    [d,r_L,r_H,norm]=[ 2.436679, 0.020359, 0.020408, 0.000049]
    [d,r_L,r_H,norm]=[ 2.433436, 0.020384, 0.020408, 0.000025]
    [d,r_L,r_H,norm]=[ 2.431816, 0.020396, 0.020408, 0.000012]
    [d,r_L,r_H,norm]=[ 2.431006, 0.020402, 0.020408, 0.000006]
    [d,r_L,r_H,norm]=[ 2.430601, 0.020405, 0.020408, 0.000003]
    [d,r_L,r_H,norm]=[ 2.430398, 0.020407, 0.020408, 0.000002]
    [d,r_L,r_H,norm]=[ 2.430297, 0.020407, 0.020408, 0.000001]
    [d,r_L,r_H,norm]=[ 2.430246, 0.020408, 0.020408, 0.000000]
    [d,r_L,r_H,norm]=[ 2.430221, 0.020408, 0.020408, 0.000000]
    [d,r_L,r_H,norm]=[ 2.430208, 0.020408, 0.020408, 0.000000]
    [d,r_L,r_H,norm]=[ 2.430202, 0.020408, 0.020408, 0.000000]
    [d,r_L,r_H,norm]=[ 2.430199, 0.020408, 0.020408, 0.000000]
    [d,r_L,r_H,norm]=[ 2.430197, 0.020408, 0.020408, 0.000000]
    [d,r_L,r_H,norm]=[ 2.430197, 0.020408, 0.020408, 0.000000]
    [d,r_L,r_H,norm]=[ 2.430196, 0.020408, 0.020408, 0.000000]
    [d,r_L,r_H,norm]=[ 2.430196, 0.020408, 0.020408, 0.000000]
    [d,r_L,r_H,norm]=[ 2.430196, 0.020408, 0.020408, 0.000000]
    [d,r_L,r_H,norm]=[ 2.430196, 0.020408, 0.020408, 0.000000]
    [d,r_L,r_H,norm]=[ 2.430196, 0.020408, 0.020408, 0.000000]
    [d,r_L,r_H,norm]=[ 2.430196, 0.020408, 0.020408, 0.000000]

Describe how the equilibrium interest rate depends of the intertemporal discount factor,𝛽, and intertemporal substitution/risk aversion, 𝜎.

Answer:(Change $/beta$  and see what happens)

When 𝛽=0.98, the equilibrium interest rate is 2.04, which is the same as 𝜌. Now, the agent became, impatient thus the his respective beta became lower. That is, in order to the agent give up present consumption, he needs to be compensated, in the intertemporal budget constraint context that means the equilibrium interest rate needs to be greater than before. So the respective interest rate equilibrium is about 5,9 which is a big increase compared to the variation of the betas.


# Introduction

首先，动态规划问题的一般形式就是**求最值**。动态规划其实是**运筹学**的一种最优化方法，只不过在计算机问题上应用比较多，比如说让你求*最长递增子序列*呀，*最小编辑距离*呀等等。

既然是要求最值，核心问题是什么呢？求解动态规划的核心问题是**穷举**。因为要求最值，肯定要把所有可行的答案穷举出来，然后在其中找最值呗。
1. 动态规划的穷举有点特别，因为这类问题存在 **「重叠子问题」**，如果暴力穷举的话效率会极其低下，所以需要 **「备忘录 cache」** 或者 **「DP table」** 来优化穷举过程，避免不必要的计算。
2. 动态规划问题一定会具备 **「最优子结构」**，才能通过子问题的最值得到原问题的最值.
3. 虽然动态规划的核心思想就是穷举求最值，但是问题可以千变万化，穷举所有可行解其实并不是一件容易的事，只有列出正确的 **「状态转移方程」** 才能正确地穷举

以上提到的重叠子问题、最优子结构、状态转移方程就是动态规划三要素。具体什么意思等会会举例详解，但是在实际的算法问题中，写出状态转移方程是最困难的，这也就是为什么很多朋友觉得动态规划问题困难的原因，我来提供我研究出来的一个思维框架，辅助你思考状态转移方程：

**1. 明确 base case -> 2. 明确「状态」-> 3. 明确「选择」 -> 4. 定义 dp 数组/函数的含义**

按上面的套路走，最后的结果就可以套这个框架：

```
# 初始化 base case
dp[0][0][...] = base
# 进行状态转移
for 状态1 in 状态1的所有取值：
    for 状态2 in 状态2的所有取值：
        for ...
            dp[状态1][状态2][...] = 求最值(选择1，选择2...)
```

```python

```

# Examples

## Fibonacci

###  brute-force resursive

```python
def fibonacci_recursive_force(n):
    # base case
    if n == 0: return 0
    if n == 1: return 1

    # Recursive case
    return fibonacci_recursive_force(n - 1) + fibonacci_recursive_force(n-2)
```

```python
fibonacci_recursive_force(10)
```

    55

这个写法虽然简单，但是十分低效，低效在哪里？假设 n = 20，请画出递归树：

这个递归树怎么理解？就是说想要计算原问题 f(20)，我就得先计算出子问题 f(19) 和 f(18)，然后要计算 f(19)，我就要先算出子问题 f(18) 和 f(17)，以此类推。最后遇到 f(1) 或者 f(2) 的时候，结果已知，就能直接返回结果，递归树不再向下生长了.

**递归算法的时间复杂度怎么计算？就是用子问题个数乘以解决一个子问题需要的时间。**

1. 首先计算子问题个数，即递归树中节点的总数。显然二叉树节点总数为指数级别，所以子问题个数为 $O(2^n)$
2. 然后计算解决一个子问题的时间，在本算法中，没有循环，只有 f(n - 1) + f(n - 2) 一个加法操作，时间为 O(1)。

所以，这个算法的时间复杂度为二者相乘，即 $O(2^n)$，指数级别，爆炸。

**[重叠子问题]:** 观察递归树，很明显发现了算法低效的原因：存在大量**重复计算**，比如 f(18) 被计算了两次，而且你可以看到，以 f(18) 为根的这个递归树体量巨大，多算一遍，会耗费巨大的时间。更何况，还不止 f(18) 这一个节点被重复计算，所以这个算法及其低效。

### Recursive with memo cache

明确了问题，其实就已经把问题解决了一半。即然耗时的原因是重复计算，那么我们可以造一个「备忘录」，每次算出某个子问题的答案后别急着返回，先记到「备忘录」里再返回；每次遇到一个子问题先去「备忘录」里查一查，如果发现之前已经解决过这个问题了，直接把答案拿出来用，不要再耗时去计算了。
一般使用一个数组充当这个「备忘录」，当然你也可以使用哈希表（字典），思想都是一样的。

```python
def fabonacci_recursive_with_cache(n, cache={}):
    if n in cache: return cache[n]

    if n == 0: return 0
    if n == 1: return 1

    cache[n] = fabonacci_recursive_with_cache(n - 1) + fabonacci_recursive_with_cache(n - 2)

    return cache[n]

```

```python
fabonacci_recursive_with_cache(10)
```

    55

现在，画出递归树，你就知道「备忘录」到底做了什么。

实际上，带「备忘录」的递归算法，把一棵存在巨量冗余的递归树通过「剪枝」，改造成了一幅不存在冗余的递归图，极大减少了子问题（即递归图中节点）的个数。

**递归算法的时间复杂度怎么计算？就是用子问题个数乘以解决一个子问题需要的时间。**

1. 子问题个数，即图中节点的总数，由于本算法不存在冗余计算，子问题就是 f(1), f(2), f(3) ... f(20)，数量和输入规模 n = 20 成正比，所以子问题个数为 O(n)。
2. 解决一个子问题的时间，同上，没有什么循环，时间为 O(1)。

所以，本算法的时间复杂度是 O(n)。比起暴力算法，是降维打击。

至此，带备忘录的递归解法的效率已经和迭代的动态规划解法一样了。实际上，这种解法和迭代的动态规划已经差不多了，只不过这种方法叫做 **「自顶向下」**，动态规划叫做 **「自底向上」**。

1. **「自顶向下」**: 注意我们刚才画的递归树（或者说图），是从上向下延伸，都是从一个规模较大的原问题比如说 f(20)，向下逐渐分解规模，直到 f(1) 和 f(2) 这两个 base case，然后逐层返回答案，这就叫「自顶向下」。
2. **「自底向上」**: 反过来，我们直接从最底下，最简单，问题规模最小的 f(1) 和 f(2) 开始往上推，直到推到我们想要的答案 f(20)，这就是动态规划的思路，这也是为什么动态规划一般都脱离了递归，而是由循环迭代完成计算。

### Dynamic Programming

有了上一步「备忘录」的启发，我们可以把这个「备忘录」独立出来成为一张表，就叫做 DP table 吧，在这张表上完成「自底向上」的推算岂不美哉！

```python
def fabonacci_dynamic_programming(n):
    if n == 0: return 0
    if n == 1: return 1
    dp = {0:0, 1:1}
    for i in range(2, n+1):
        dp[i] = dp[i - 1] + dp[i - 2]
    return dp[n]

```

```python
fabonacci_dynamic_programming(10)
```

    55

画个图就很好理解了，而且你发现这个 DP table 特别像之前那个「剪枝」后的结果，只是反过来算而已。实际上，带备忘录的递归解法中的「备忘录」，最终完成后就是这个 DP table，所以说这两种解法其实是差不多的，大部分情况下，效率也基本相同.

这里，引出 **「状态转移方程」** 这个名词，实际上就是描述问题结构的数学形式：

\begin{equation}
        f(n)=
        \begin{cases}
            1, \; n = 1, 2\\
            f(n - 1) + f(n -2), \; n > 2
        \end{cases}
\end{equation}

你会发现，上面的几种解法中的所有操作，例如 return f(n - 1) + f(n - 2)，dp[i] = dp[i - 1] + dp[i - 2]，以及对备忘录或 DP table 的初始化操作，都是围绕这个方程式的不同表现形式。可见列出「状态转移方程」的重要性，它是解决问题的核心。而且很容易发现，其实状态转移方程直接代表着暴力解法。

这个例子的最后，讲一个细节优化。细心的读者会发现，根据斐波那契数列的状态转移方程，当前状态只和之前的两个状态有关，其实并不需要那么长的一个 DP table 来存储所有的状态，只要想办法存储之前的两个状态就行了。所以，可以进一步优化，把空间复杂度降为 O(1)：

```python
def fabonacci_dynamic_programming(n):
    if n == 0: return 0
    if n == 1: return 1
    prev_state = 0
    curr_state = 1
    for i in range(2, n+1):
        _sum = prev_state + curr_state
        prev_state = curr_state
        curr_state = _sum
    return curr_state

```

```python
fabonacci_dynamic_programming(10)
```

    55

这个技巧就是所谓的 **「状态压缩」**，如果我们发现每次状态转移只需要 DP table 中的一部分，那么可以尝试用状态压缩来缩小 DP table 的大小，只记录必要的数据，上述例子就相当于把DP table 的大小从 n 缩小到 2。后续的动态规划章节中我们还会看到这样的例子，一般来说是把一个二维的 DP table 压缩成一维，即把空间复杂度从 O(n^2) 压缩到 O(n)。

斐波那契数列的例子严格来说不算动态规划，因为没有涉及求最值，以上旨在说明重叠子问题的消除方法，演示得到最优解法逐步求精的过程。

## Coin Change

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

You may assume that you have an infinite number of each kind of coin.

比如说 k = 3，面值分别为 1，2，5，总金额 amount = 11。那么最少需要 3 枚硬币凑出，即 11 = 5 + 5 + 1。

```python

```

### Brute Force Recursive

首先，这个问题是动态规划问题，因为它具有 **「最优子结构」** 的。要符合「最优子结构」，子问题间必须**互相独立**:

比如说，假设你考试，每门科目的成绩都是互相独立的。你的原问题是考出最高的总成绩，那么你的子问题就是要把语文考到最高，数学考到最高…… 为了每门课考到最高，你要把每门课相应的选择题分数拿到最高，填空题分数拿到最高…… 当然，最终就是你每门课都是满分，这就是最高的总成绩。

得到了正确的结果：最高的总成绩就是总分。因为这个过程符合最优子结构，“每门科目考到最高”这些子问题是互相独立，互不干扰的。但是，如果加一个条件：你的语文成绩和数学成绩会互相制约，数学分数高，语文分数就会降低，反之亦然。这样的话，显然你能考到的最高总成绩就达不到总分了，按刚才那个思路就会得到错误的结果。因为子问题并不独立，语文数学成绩无法同时最优，所以最优子结构被破坏。

回到凑零钱问题，为什么说它符合最优子结构呢？比如你想求 amount = 11 时的最少硬币数（原问题），如果你知道凑出 amount = 10 的最少硬币数（子问题），你只需要把子问题的答案加一（再选一枚面值为 1 的硬币）就是原问题的答案。因为硬币的数量是没有限制的，所以子问题之间没有相互制，是互相独立的。

那么，既然知道了这是个动态规划问题，就要思考**如何列出正确的状态转移方程**？
1. **确定 base case**，这个很简单，显然目标金额 amount 为 0 时算法返回 0，因为不需要任何硬币就已经凑出目标金额了。
2. **确定「状态」**，也就是原问题和子问题中会变化的变量。由于硬币数量无限，硬币的面额也是题目给定的，只有目标金额会不断地向 base case 靠近，所以唯一的「状态」就是目标金额 amount。
3. **确定「选择」**，也就是导致「状态」产生变化的行为。目标金额为什么变化呢，因为你在选择硬币，你每选择一枚硬币，就相当于减少了目标金额。所以说所有硬币的面值，就是你的「选择」。
4. **明确 dp 函数/数组的定义**。我们这里讲的是自顶向下的解法，所以会有一个递归的 dp 函数，一般来说函数的参数就是状态转移中会变化的量，也就是上面说到的「状态」；函数的返回值就是题目要求我们计算的量。就本题来说，状态只有一个，即「目标金额」，题目要求我们计算凑出目标金额所需的最少硬币数量。所以我们可以这样定义 dp 函数

**dp(n) 的定义**：输入一个目标金额 n，返回凑出目标金额 n 的最少硬币数量。

搞清楚上面这几个关键点，解法的伪码就可以写出来了：
```
# 伪码框架
def coinChange(coins: List[int], amount: int):

    # 定义：要凑出金额 n，至少要 dp(n) 个硬币
    def dp(n):
        # 做选择，选择需要硬币最少的那个结果
        for coin in coins:
            res = min(res, 1 + dp(n - coin))
        return res

    # 题目要求的最终结果是 dp(amount)
    return dp(amount)

```

根据伪码，我们加上 base case 即可得到最终的答案。显然目标金额为 0 时，所需硬币数量为 0；当目标金额小于 0 时，无解，返回 -1：

```python
from typing import List
def coinChange(coins: List[int], amount: int):

    def dp(n):
        # base case
        if n == 0: return 0
        if n < 0: return -1
        # 求最小值，所以初始化为正无穷
        res = float('INF')
        for coin in coins:
            subproblem = dp(n - coin)
            # 子问题无解，跳过
            if subproblem == -1: continue
            res = min(res, 1 + subproblem)

        return res if res != float('INF') else -1

    return dp(amount)
```

至此，状态转移方程其实已经完成了，以上算法已经是暴力解法了，以上代码的数学形式就是状态转移方程：

\begin{equation}
        dp(n)=
        \begin{cases}
            0, \; n = 0\\
            -1, \; n < 0 \\
            min\{dp(n - coin) + 1 | coin \in coins\}, n > 0
        \end{cases}
\end{equation}

至此，这个问题其实就解决了，只不过需要消除一下重叠子问题，比如 amount = 11, coins = {1,2,5} 时画出递归树看看：

**递归算法的时间复杂度分析：子问题总数 x 每个子问题的时间**

子问题总数为递归树节点个数，这个比较难看出来，是 O(n^k)，总之是指数级别的。每个子问题中含有一个 for 循环，复杂度为 O(k)。所以总时间复杂度为 O(k * n^k)，指数级别。

### Recursive with cache table

```python
def coinChange(coins: List[int], amount: int):
    def dp(n, cache={}):

        if n in cache: return cache[n]
        if n == 0: return 0
        if n < 0 : return -1
        cache[n] = float('INF')
        for coin in coins:
            sub_problem = dp(n - coin)

            if sub_problem == -1: continue

            cache[n] = min(cache[n], 1 + sub_problem)
        return cache[n] if cache[n] != float('INF') else -1

    return dp(amount)

```

很显然「备忘录」大大减小了子问题数目，完全消除了子问题的冗余，所以子问题总数不会超过金额数 n，即子问题数目为 O(n)。处理一个子问题的时间不变，仍是 O(k)，所以总的时间复杂度是 O(kn)。

### Dynamic Programming

当然，我们也可以自底向上使用 dp table 来消除重叠子问题，关于「状态」「选择」和 base case 与之前没有区别，dp 数组的定义和刚才 dp 函数类似，也是把「状态」，也就是目标金额作为变量。不过 dp 函数体现在函数参数，而 dp 数组体现在数组索引：

**dp 数组的定义：当目标金额为 i 时，至少需要 dp[i] 枚硬币凑出。**
根据我们文章开头给出的动态规划代码框架可以写出如下解法：

```python
def coinChange(coins: List[int], amount: int):
    dp = [amount + 1 for _ in range(amount + 1)]
    for idx, _ in enumerate(dp):
        for coin in coins:
            if idx == 0: dp[idx] = 0
            if idx - coin < 0: continue
            dp[idx] = min(dp[idx], 1 + dp[idx - coin])
    return -1 if dp[amount] == amount + 1 else dp[amount]
```

## DAG最短路

问题很简单：给定一个城市的地图，所有的道路都是单行道，而且不会构成环。每条道路都有过路费，问您从S点到T点花费的最少费用。

\begin{equation}
        dp(n)=
        \begin{cases}
            0, \; edge_in(n) \; is \; null\\
            min\{dp(i) + weight(i, n) \; for \;i \;in \;edge_in(n)\}
        \end{cases}
\end{equation}

# Reference

1. [什么是动态规划](https://www.zhihu.com/question/23995189)
2. [如何理解动态规划](https://www.zhihu.com/question/39948290)
3. [第三章 动态规划(Dynamic Programming )](https://zhuanlan.zhihu.com/p/154439652)
4. [算法·流年](https://zhuanlan.zhihu.com/p/65856122)

```python

```


```python
import numpy as np
import matplotlib.pyplot as plt
from solvers import *
import equations
import variance_solvers

%matplotlib inline
```

```python
def convergence(equation, L_x, L_y, delta_n_x=0, delta_n_y=0, type='Dirichlet'):
    E = []
    N = []
    for J in [4, 5, 6, 7]:
        n = 2**(J+delta_n_x) # inner x
        m = 2**(J+delta_n_y) # inner y
        N.append(n)

        A, rhs, exact_sol = equations.construct_matrix(equation, type, L_x, L_y, n, m)
        error = np.linalg.norm(A.dot(exact_sol) - rhs, ord=np.inf)
        E.append(error)
    return E, N
```

```python
alpha = 10

eq = lambda L_x, L_y: equations.equation_4(L_x, L_y, alpha)
```

```python
E, N = convergence(eq, 1, 1)
plt.plot(np.log10(N), np.log10(E))
```

```python
J = 3
h = 1/(1 + 2**J)

A, b, exact = equations.construct_matrix(eq, 'Dirichlet', 1, 1, 2**J, 2**J)
A = A.toarray()

abs_eig = abs(np.linalg.eig(A)[0])
condition_number = np.max(abs_eig)/np.min(abs_eig)

ALPHA = np.logspace(0, 3, 10)
Cond = []
split_N = []
thick_stripes_N = []
stripes_N = []
BP_N = []
BP_N_p = []
for alpha in ALPHA:
    B = A + (np.min(abs_eig) - alpha)*np.eye(2**(2*J))

    abs_eig_B = abs(np.linalg.eig(B)[0])
    condition_number = np.max(abs_eig_B)/np.min(abs_eig_B)
    Cond.append(condition_number)

    sol, E = split_BP_solver(B, b, tol=h**2, write=True)
    sol, EE = thick_stripes_BP_solver(B, b, h**2, write=True)
    sol, EEE = stripes_BP_solver(B, b, h**2, write=True)
    sol, EEEE = GaBP(B, b, tol=h**2, write=True)
    sol, EEEEE = GaBP(B, b, tol=h**2, type='Parallel', write=True)

    split_N.append(len(E))
    thick_stripes_N.append(len(EE))
    stripes_N.append(len(EEE))
    BP_N.append(len(EEEE))
    BP_N_p.append(len(EEEEE))
```

```python
plt.plot(Cond, split_N)
plt.plot(Cond, thick_stripes_N)
plt.plot(Cond, stripes_N)
plt.plot(Cond, BP_N)
plt.plot(Cond, BP_N_p)
```

```python
AL = []
for a, name in zip([split_N, thick_stripes_N, stripes_N, BP_N, BP_N_p], ['split', 'thick stripes', 'stripes', 'sequential GaBP', 'parallel GaBP']):
    alpha = np.polyfit(Cond, a, deg=1)[0]
    AL.append(alpha)
    print(f'Tangent coefficient for {name} scheme is {alpha:1.4}')
```

    Tangent coefficient for split scheme is 0.0312
    Tangent coefficient for thick stripes scheme is 0.08599
    Tangent coefficient for stripes scheme is 0.213
    Tangent coefficient for sequential GaBP scheme is 0.3473
    Tangent coefficient for parallel GaBP scheme is 1.349

```python
plt.plot(np.log10(Cond), np.log10(split_N), dashes=[2, 2, 10, 2], color='black', label=f'split ({AL[0]:1.2})')
plt.plot(np.log10(Cond), np.log10(thick_stripes_N), dashes=[2, 2, 2, 2], color='black', label=f'thick stripes ({AL[1]:1.2})')
plt.plot(np.log10(Cond), np.log10(stripes_N), color='black', dashes=[5, 5, 5, 5], label=f'stripes ({AL[2]:1.2})')
plt.plot(np.log10(Cond), np.log10(BP_N), color='black', label=f'sequential GaBP ({AL[3]:1.2})')
plt.plot(np.log10(Cond), np.log10(BP_N_p), '.', dashes=[2, 0, 2, 0], color='black', label=f'parallel GaBP ({AL[4]:1.2})')
plt.legend()
#plt.grid(1)
plt.ylabel('$\\log_{10}$ iterations till convergence')
plt.xlabel('$\\log_{10}$ condition number')
plt.savefig('condition_number_Dirichlet.eps', format='eps', pad_inches=0.01)
```

\begin{equation}
    L_{\text{eq4}}\left(u - \text{exact}_{\text{eq4}}\right) + \beta u = 0
\end{equation}

```python
abs_eig = abs(np.linalg.eig(A)[0])
condition_number = np.max(abs_eig)/np.min(abs_eig)

ALPHA = np.logspace(0, 3, 10)
Cond = []
split_N = []
thick_stripes_N = []
stripes_N = []
BP_N = []
beta = []
for alpha in ALPHA:
    beta.append(np.min(abs_eig) - alpha)

    B = A + (np.min(abs_eig) - alpha)*np.eye(2**(2*J))

    abs_eig_B = abs(np.linalg.eig(B)[0])
    condition_number = np.max(abs_eig_B)/np.min(abs_eig_B)
    Cond.append(condition_number)
```

```python
plt.plot(beta, np.log10(Cond), color='black')
plt.xlabel('$\\beta$')
#plt.grid(1)
plt.ylabel('$\\log_{10}$ condition number')
#plt.savefig('condition_number_vs_equation_grid.eps', format='eps', pad_inches=0.01)
```

**Variance only**

```python
alpha = 10

eq = lambda L_x, L_y: equations.equation_4(L_x, L_y, alpha)
```

```python
J = 3
h = 1/(1 + 2**J)

A, b, exact = equations.construct_matrix(eq, 'Dirichlet', 1, 1, 2**J, 2**J)
A = A.toarray()

abs_eig = abs(np.linalg.eig(A)[0])
condition_number = np.max(abs_eig)/np.min(abs_eig)

ALPHA = np.logspace(0, 3, 10)
Cond = []
split_N = []
thick_stripes_N = []
stripes_N = []
BP_N = []
BP_N_p = []
for alpha in ALPHA:
    B = A + (np.min(abs_eig) - alpha)*np.eye(2**(2*J))

    abs_eig_B = abs(np.linalg.eig(B)[0])
    condition_number = np.max(abs_eig_B)/np.min(abs_eig_B)
    Cond.append(condition_number)

    sol, E = variance_solvers.split_BP_solver(B, b, tol=h**2, write=True)
    sol, EE = variance_solvers.thick_stripes_BP_solver(B, b, h**2, write=True)
    sol, EEE = variance_solvers.stripes_BP_solver(B, b, h**2, write=True)
    sol, EEEE = variance_solvers.GaBP(B, b, tol=h**2, write=True)
    sol, EEEEE = variance_solvers.GaBP(B, b, tol=h**2, type='Parallel', write=True)

    split_N.append(len(E))
    thick_stripes_N.append(len(EE))
    stripes_N.append(len(EEE))
    BP_N.append(len(EEEE))
    BP_N_p.append(len(EEEEE))
```

```python
AL = []
for a, name in zip([split_N, thick_stripes_N, stripes_N, BP_N, BP_N_p], ['split', 'thick stripes', 'stripes', 'sequential GaBP', 'parallel GaBP']):
    alpha = np.polyfit(Cond, a, deg=1)[0]
    AL.append(alpha)
    print(f'Tangent coefficient for {name} scheme is {alpha:1.4}')
```

    Tangent coefficient for split scheme is -2.126e-19
    Tangent coefficient for thick stripes scheme is 8.321e-20
    Tangent coefficient for stripes scheme is -2.126e-19
    Tangent coefficient for sequential GaBP scheme is 4.161e-20
    Tangent coefficient for parallel GaBP scheme is 4.161e-20

```python
plt.plot(np.log10(Cond), split_N, color='black', label=f'split')
plt.plot(np.log10(Cond), stripes_N, color='black', label=f'stripes')
plt.plot(np.log10(Cond), thick_stripes_N, dashes=[2, 2, 2, 2], color='black', label=f'thick stripes')
plt.plot(np.log10(Cond), BP_N, '.', color='black', dashes=[2, 0, 2, 0], label=f'sequential GaBP')
plt.plot(np.log10(Cond), BP_N_p, '.', dashes=[2, 0, 2, 0], color='black', label=f'parallel GaBP')
plt.legend()
plt.legend(loc = (0.02, 0.55))
plt.grid(1)
plt.ylabel('iterations till convergence')
plt.xlabel('$\\log_{10}$ condition number')
#plt.savefig('condition_number_grid_variance.eps', format='eps', pad_inches=0.01)
```