Artelys Knitro Artelys Knitro Home
  • Documentation
  • logo-new-blancArtelys
Python / Knitro API Python / Pyomo Julia / JuMP

On this page

  • Introduction
  • Problem description
  • Input data
  • Solving a model
  • The Markowitz model
    • Varying the desired return
  • Adding diversification bounds
  • Adding a cardinality limit
  • Rebalancing with transaction costs
  • Optimizing portfolios with a large number of assets

Portfolio Optimization

Allocate capital across assets to minimize risk at a target return, the convex Markowitz model, then extend it with diversification, cardinality and transaction-cost constraints.

Notebook
Python / Knitro API Python / Pyomo Julia / JuMP

Introduction

We consider the portfolio optimization problem, where an investor aims to allocate capital among \(N\) assets within a single period. Portfolio optimization is a fundamental concept in finance that focuses on balancing risk and return. The objective is to determine the optimal weights to assign to each asset in the portfolio.

The curve on the left is the best risk attainable at each desired return, and the bars on the right show the allocation the model picks at one point on it.

Problem description

There are multiple variants of portfolio optimization problems. We start with the most fundamental one, the Markowitz model, and take the others in turn, each one adding to the last.

Input

  • \(N\) assets; for each asset \(i\), its expected return \(\mu_i\)
  • A desired return \(R\)
  • For each pair of assets \((i, j)\), their covariance \(\Sigma_{ij}\)

Problem: find for each asset, the proportion of the capital invested in it, such that the desired return is met.

Objective: minimize the risk \(w^\top \Sigma w\), where \(w\) represents the vector of allocations.

Input data

We retrieve realistic input data from nasdaq.com. A Market holds a name for each asset, its expected return, and the covariance between every pair. A Portfolio holds the weight given to each asset, together with the return and the risk that follow from it.

from dataclasses import dataclass


def expected_return(mu, weights):
    return sum(m * w for m, w in zip(mu, weights, strict=True))


def risk(sigma, weights):
    indices = range(len(weights))
    return sum(sigma[i][j] * weights[i] * weights[j] for i in indices for j in indices)


@dataclass
class Market:
    name: str
    names: list
    returns: list
    covariance: list

    @property
    def num_assets(self):
        return len(self.names)

    @property
    def assets(self):
        return range(self.num_assets)


@dataclass
class Portfolio:
    weights: list
    expected_return: float
    risk: float

A market too large to write out lives in a file. load_market reads one CSV from shared/data with one row per asset, holding its name, its expected return, and then its covariance with every asset in turn. That is where the hundred-asset market at the end of the page comes from.

import csv
from pathlib import Path

from plot import draw_comparison, draw_frontier, draw_market, draw_portfolio

DATA_DIR = Path("shared") / "data"


def load_market(file_name, name):
    with (DATA_DIR / file_name).open(encoding="utf-8") as handle:
        reader = csv.reader(handle)
        next(reader)
        rows = [row for row in reader if row]
    names = [row[0] for row in rows]
    returns = [float(row[1]) for row in rows]
    covariance = [[float(value) for value in row[2:]] for row in rows]
    return Market(name, names, returns, covariance)

The worked example is ten companies from the NASDAQ. It is small enough to write out in full, so the numbers the model is given stay in plain sight.

names = [
    "Facebook",
    "Intel",
    "Frontier",
    "Micron",
    "Apple",
    "Qualcomm",
    "Sirius",
    "App. Mat.",
    "Cisco",
    "Yahoo",
]

mu = [
    8.68097,
    -6.08624,
    -66.84089,
    -69.02419,
    -0.61631,
    10.46047,
    7.63278,
    20.49615,
    -0.257636,
    19.25747,
]

sigma = [
    [1.75, 0.38, 0.54, 0.63, -0.14, 0.29, -0.15, -0.19, 0.06, 0.10],
    [0.38, 1.74, 0.75, 2.13, -0.14, 0.30, 0.86, 0.24, 0.28, 0.47],
    [0.54, 0.75, 6.85, 0.31, -0.01, 0.84, -0.14, 0.71, 0.50, 0.95],
    [0.63, 2.13, 0.31, 10.31, 0.48, 0.27, 0.23, 0.50, -0.02, 0.47],
    [-0.14, -0.14, -0.01, 0.48, 1.24, -0.02, 0.12, 0.15, -0.10, 0.46],
    [0.29, 0.30, 0.84, 0.27, -0.02, 1.08, 0.54, 0.38, 0.54, 0.75],
    [-0.15, 0.86, -0.14, 0.23, 0.12, 0.54, 0.97, 0.49, 0.44, 0.60],
    [-0.19, 0.24, 0.71, 0.50, 0.15, 0.38, 0.49, 2.35, 0.46, 0.86],
    [0.06, 0.28, 0.50, -0.02, -0.10, 0.54, 0.44, 0.46, 0.84, 0.37],
    [0.10, 0.47, 0.95, 0.47, 0.46, 0.75, 0.60, 0.86, 0.37, 3.16],
]

nasdaq = Market("Ten companies from the NASDAQ", names, mu, sigma)

Five of the ten have a negative expected return. A model free to leave them out will, and the ones it does hold are those whose covariances let them offset each other.

draw_market(nasdaq, "Ten companies from the NASDAQ")

The bars are \(\mu\) and the grid is \(\Sigma\), over the same assets in the same order. The diagonal of \(\Sigma\) is each asset’s own variance, not the risk \(w^\top \Sigma w\) the model minimizes. Micron and Frontier swing hardest by a wide margin, so the color scale is set by the off-diagonal pairs and clips on the diagonal. Those two are also the worst on the left. The assets that move most are the ones the model is being asked to leave out. Off the diagonal, the red cells are the pairs that move against each other, and diversification is built from those.

Solving a model

Each model below has a function of its own. It assembles the model and returns it together with the weight variables, without solving anything. solve takes any one of them, runs Knitro, and turns the weights into a Portfolio.

import knitro


def solve(market, build, **kwargs):
    prob, w = build(market, **kwargs)
    prob.solve()
    weights = [var.value for var in w]
    expected = expected_return(market.returns, weights)
    return Portfolio(weights, expected, risk(market.covariance, weights))

The Markowitz model

The fundamental model minimizes the risk under a fixed return.

Variables

  • \(w_i \in [0, 1]\), \(i = 1, \dots, N\): proportion of capital invested in asset \(i\)

Objective: minimize the risk

\[ \min \sum_{i=1}^N \sum_{j=1}^N \Sigma_{ij} w_i w_j \]

Constraints

  • Expected return

\[ \sum_{i=1}^N \mu_i w_i \geq R \]

  • Sum of proportions equals 1

\[ \sum_{i=1}^N w_i = 1 \]

The model has the following properties:

  • Continuous variables only
  • Quadratic objective
  • Convex, since a covariance matrix is positive semi-definite
  • Therefore, it is a convex QP

markowitz writes the model above, with the quadratic objective handed to add_objective as the double sum it is written with.

def markowitz(market, *, desired_return):
    mu, sigma = market.returns, market.covariance
    assets = market.assets
    prob = knitro.Problem()
    w = [prob.add_variable(lb=0, ub=1) for _ in assets]

    variance = prob.nsum(sigma[i][j] * w[i] * w[j] for i in assets for j in assets)
    prob.add_objective(variance)

    prob.add_constraint(prob.nsum(w[i] for i in assets) == 1)
    prob.add_constraint(prob.nsum(mu[i] * w[i] for i in assets) >= desired_return)

    return prob, w

Solving for a desired return of 10:

portfolio = solve(nasdaq, markowitz, desired_return=10)
=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 1e+01] |                [1e+00, 1e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.087150e+00   6.79e-01
       6    4.289111e-01   1.11e-16   2.13e-08   4.61e-04      0.04

EXIT: Optimal solution found.

Final Statistics
----------------
Final objective value               =   4.28911054277269e-01
Final feasibility error (abs / rel) =   1.11e-16 / 1.93e-18
Final optimality error  (abs / rel) =   2.13e-08 / 1.68e-08
# of iterations                     =          6 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.03550 (     0.009 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================
Show the full outputHide the full output
draw_portfolio(portfolio, nasdaq.names, "Least risk at a desired return of 10")
print(f"Total risk: {portfolio.risk:.2f}")
Total risk: 0.43

Here for a desired return of 10, it seems better to diversify to reduce the portfolio risk.

Varying the desired return

In the previous section, we considered a single fixed return. Now let’s consider the optimization for different levels of return.

returns = [5, 7.5, 10, 15]
base = [solve(nasdaq, markowitz, desired_return=r) for r in returns]
Show the full outputHide the full output
=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 5e+00] |                [1e+00, 5e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.113473e+00   2.37e-01
       7    3.231341e-01   0.00e+00   2.56e-11   1.59e-04      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  21.1% of solution time (0.000309 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   3.23134092792703e-01
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   2.56e-11 / 2.56e-11
# of iterations                     =          7 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00151 (     0.002 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 8e+00] |                [1e+00, 8e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.096865e+00   2.32e-01
       8    3.577912e-01   2.22e-16   6.51e-08   5.03e-04      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  12.4% of solution time (0.000295 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   3.57791213002902e-01
Final feasibility error (abs / rel) =   2.22e-16 / 4.03e-18
Final optimality error  (abs / rel) =   6.51e-08 / 6.51e-08
# of iterations                     =          8 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00241 (     0.002 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 1e+01] |                [1e+00, 1e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.087150e+00   6.79e-01
       6    4.289111e-01   1.11e-16   2.13e-08   4.61e-04      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  25.2% of solution time (0.000253 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   4.28911054277269e-01
Final feasibility error (abs / rel) =   1.11e-16 / 1.93e-18
Final optimality error  (abs / rel) =   2.13e-08 / 1.68e-08
# of iterations                     =          6 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00102 (     0.001 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 2e+01] |                [1e+00, 2e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.101172e+00   3.92e+00
       6    7.666828e-01   0.00e+00   4.52e-11   2.11e-05      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  50.3% of solution time (0.000741 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   7.66682814832761e-01
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   4.52e-11 / 2.14e-11
# of iterations                     =          6 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00149 (     0.001 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================
draw_comparison(
    base,
    [f"Return {r}, Risk {s.risk:.2f}" for r, s in zip(returns, base)],
    nasdaq.names,
    "Least risk for varying desired returns",
)

Sweeping the desired return over a finer grid traces the efficient frontier: the best risk attainable at each level of return. Every point on it is a solve.

grid = [4 + 0.5 * k for k in range(23)]
frontier = [solve(nasdaq, markowitz, desired_return=r) for r in grid]
Show the full outputHide the full output
=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 4e+00] |                [1e+00, 4e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.120086e+00   2.38e-01
       8    3.187773e-01   0.00e+00   3.04e-11   3.89e-05      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  23.0% of solution time (0.000246 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   3.18777275782998e-01
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   3.04e-11 / 3.04e-11
# of iterations                     =          8 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00110 (     0.002 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 4e+00] |                [1e+00, 4e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.117635e+00   2.38e-01
       7    3.204971e-01   1.11e-16   6.11e-10   5.36e-04      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  23.8% of solution time (0.000236 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   3.20497086140137e-01
Final feasibility error (abs / rel) =   1.11e-16 / 2.13e-18
Final optimality error  (abs / rel) =   6.11e-10 / 6.11e-10
# of iterations                     =          7 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00102 (     0.001 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 5e+00] |                [1e+00, 5e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.113473e+00   2.37e-01
       7    3.231341e-01   0.00e+00   2.56e-11   1.59e-04      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  23.8% of solution time (0.000259 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   3.23134092792703e-01
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   2.56e-11 / 2.56e-11
# of iterations                     =          7 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00111 (     0.001 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 6e+00] |                [1e+00, 6e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.109591e+00   2.36e-01
       7    3.271836e-01   0.00e+00   7.73e-12   2.51e-05      0.00

EXIT: Optimal solution found.

HINT: Knitro spent   8.4% of solution time (0.000180 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   3.27183571757067e-01
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   7.73e-12 / 7.73e-12
# of iterations                     =          7 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00216 (     0.002 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 6e+00] |                [1e+00, 6e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.105989e+00   2.35e-01
       7    3.326742e-01   1.11e-16   1.63e-10   8.08e-05      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  12.9% of solution time (0.000271 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   3.32674176212433e-01
Final feasibility error (abs / rel) =   1.11e-16 / 2.07e-18
Final optimality error  (abs / rel) =   1.63e-10 / 1.63e-10
# of iterations                     =          7 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00212 (     0.002 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 6e+00] |                [1e+00, 6e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.102667e+00   2.34e-01
       7    3.396059e-01   0.00e+00   1.06e-09   3.37e-04      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  24.8% of solution time (0.000238 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   3.39605906702465e-01
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   1.06e-09 / 1.06e-09
# of iterations                     =          7 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00098 (     0.001 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 7e+00] |                [1e+00, 7e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.099626e+00   2.33e-01
       7    3.479790e-01   0.00e+00   2.58e-07   1.27e-03      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  24.1% of solution time (0.000235 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   3.47979026747516e-01
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   2.58e-07 / 2.58e-07
# of iterations                     =          7 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00100 (     0.001 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 8e+00] |                [1e+00, 8e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.096865e+00   2.32e-01
       8    3.577912e-01   2.22e-16   6.51e-08   5.03e-04      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  22.9% of solution time (0.000241 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   3.57791213002902e-01
Final feasibility error (abs / rel) =   2.22e-16 / 4.03e-18
Final optimality error  (abs / rel) =   6.51e-08 / 6.51e-08
# of iterations                     =          8 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00108 (     0.001 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 8e+00] |                [1e+00, 8e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.094384e+00   2.31e-01
       7    3.689799e-01   2.22e-16   2.18e-08   6.91e-04      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  24.9% of solution time (0.000238 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   3.68979871227832e-01
Final feasibility error (abs / rel) =   2.22e-16 / 4.00e-18
Final optimality error  (abs / rel) =   2.18e-08 / 2.11e-08
# of iterations                     =          7 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00098 (     0.001 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 8e+00] |                [1e+00, 8e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.092163e+00   2.30e-01
       7    3.815114e-01   7.79e-11   5.99e-11   1.92e-04      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  24.8% of solution time (0.000263 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   3.81511437462774e-01
Final feasibility error (abs / rel) =   7.79e-11 / 1.39e-12
Final optimality error  (abs / rel) =   5.99e-11 / 5.54e-11
# of iterations                     =          7 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00109 (     0.002 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 9e+00] |                [1e+00, 9e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.090213e+00   2.39e-01
       7    3.953859e-01   8.66e-11   7.93e-11   1.86e-04      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  12.5% of solution time (0.000226 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   3.95385911757145e-01
Final feasibility error (abs / rel) =   8.66e-11 / 1.53e-12
Final optimality error  (abs / rel) =   7.93e-11 / 7.05e-11
# of iterations                     =          7 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00183 (     0.002 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 1e+01] |                [1e+00, 1e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.088542e+00   4.59e-01
       6    4.108481e-01   0.00e+00   5.35e-07   3.64e-03      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  12.8% of solution time (0.000220 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   4.10848097034628e-01
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   5.35e-07 / 4.50e-07
# of iterations                     =          6 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00173 (     0.002 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 1e+01] |                [1e+00, 1e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.087150e+00   6.79e-01
       6    4.289111e-01   1.11e-16   2.13e-08   4.61e-04      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  13.6% of solution time (0.000239 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   4.28911054277269e-01
Final feasibility error (abs / rel) =   1.11e-16 / 1.93e-18
Final optimality error  (abs / rel) =   2.13e-08 / 1.68e-08
# of iterations                     =          6 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00178 (     0.002 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 1e+01] |                [1e+00, 1e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.086037e+00   8.99e-01
       6    4.496874e-01   0.00e+00   1.17e-10   7.54e-05      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  26.6% of solution time (0.000239 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   4.49687421120634e-01
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   1.17e-10 / 8.75e-11
# of iterations                     =          6 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00092 (     0.001 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 1e+01] |                [1e+00, 1e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.085204e+00   1.12e+00
       6    4.731767e-01   2.22e-16   1.69e-11   1.63e-05      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  26.5% of solution time (0.000236 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   4.73176703650771e-01
Final feasibility error (abs / rel) =   2.22e-16 / 3.79e-18
Final optimality error  (abs / rel) =   1.69e-11 / 1.19e-11
# of iterations                     =          6 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00091 (     0.001 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 1e+01] |                [1e+00, 1e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.084650e+00   1.34e+00
       5    4.993811e-01   0.00e+00   1.07e-06   7.43e-03      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  27.0% of solution time (0.000243 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   4.99381056261065e-01
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   1.07e-06 / 7.14e-07
# of iterations                     =          5 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00092 (     0.001 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 1e+01] |                [1e+00, 1e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.084375e+00   1.56e+00
       5    5.282955e-01   1.11e-16   8.20e-07   6.15e-03      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  27.0% of solution time (0.000233 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   5.28295468844518e-01
Final feasibility error (abs / rel) =   1.11e-16 / 1.86e-18
Final optimality error  (abs / rel) =   8.20e-07 / 5.22e-07
# of iterations                     =          5 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00089 (     0.001 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 1e+01] |                [1e+00, 1e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.084380e+00   1.78e+00
       6    5.599220e-01   0.00e+00   2.17e-11   1.10e-05      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  26.4% of solution time (0.000234 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   5.59922033425558e-01
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   2.17e-11 / 1.32e-11
# of iterations                     =          6 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00091 (     0.001 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 1e+01] |                [1e+00, 1e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.084664e+00   2.00e+00
       6    5.942630e-01   0.00e+00   2.48e-09   1.93e-04      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  25.8% of solution time (0.000250 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   5.94262971067329e-01
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   2.48e-09 / 1.44e-09
# of iterations                     =          6 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00099 (     0.002 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 1e+01] |                [1e+00, 1e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.088670e+00   2.47e+00
       7    6.313168e-01   8.36e-11   1.27e-11   5.78e-05      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  24.5% of solution time (0.000235 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   6.31316820917614e-01
Final feasibility error (abs / rel) =   8.36e-11 / 1.37e-12
Final optimality error  (abs / rel) =   1.27e-11 / 7.05e-12
# of iterations                     =          7 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00099 (     0.001 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 1e+01] |                [1e+00, 1e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.092828e+00   2.95e+00
       6    6.713871e-01   0.00e+00   9.09e-08   1.23e-03      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  12.7% of solution time (0.000223 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   6.71387134348436e-01
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   9.09e-08 / 4.80e-08
# of iterations                     =          6 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00178 (     0.002 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 1e+01] |                [1e+00, 1e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.096995e+00   3.44e+00
       6    7.163964e-01   0.00e+00   3.61e-10   5.63e-05      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  11.5% of solution time (0.000219 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   7.16396428775274e-01
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   3.61e-10 / 1.80e-10
# of iterations                     =          6 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00193 (     0.002 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |         2         0         8
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        20           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        20        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 2e+01] |                [1e+00, 2e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.101172e+00   3.92e+00
       6    7.666828e-01   0.00e+00   4.52e-11   2.11e-05      0.00

EXIT: Optimal solution found.

HINT: Knitro spent  24.0% of solution time (0.000235 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   7.66682814832761e-01
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   4.52e-11 / 2.14e-11
# of iterations                     =          6 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00100 (     0.001 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================
draw_frontier(grid, frontier, "Efficient frontier", highlight=grid.index(10))

Here we can observe the different assets and their weights based on the desired return. For low returns, it is advantageous to diversify the assets in which to allocate parts of the portfolio, to minimize the risk. On the other hand, as we aim for higher returns, and consequently accept higher risks, we end up investing in the asset that yields the most.

Adding diversification bounds

In portfolio optimization, diversification is a critical principle used to reduce risk by allocating investments among various financial assets, industries, and other categories. Diversification has multiple benefits:

  1. Risk Reduction: Diversification helps in spreading risk across different assets, reducing the impact of any single asset’s poor performance on the overall portfolio.
  2. Control Over Portfolio Composition: By setting bounds on asset weights and limiting the number of assets, investors can ensure that the portfolio remains manageable and aligned with investment objectives.
  3. Improved Returns: A well-diversified portfolio can improve the risk-adjusted returns by taking advantage of the benefits of diversification without excessive exposure to any single asset.

For each asset in the portfolio, we define a lower bound \(L_i\) and an upper bound \(U_i\) on the weight \(w_i\):

\[ \forall i = 1, \dots, N \qquad L_i \leq w_i \leq U_i \]

This constraint ensures that the weight of each asset remains within a specified range, promoting diversification by preventing over-concentration in any single asset. Nothing else changes, so the model is still a convex QP.

with_bounds calls markowitz and adds the two bounds per asset to what comes back.

def with_bounds(market, *, desired_return, lower, upper):
    prob, w = markowitz(market, desired_return=desired_return)

    for i in market.assets:
        prob.add_constraint(w[i] >= lower[i])
        prob.add_constraint(w[i] <= upper[i])

    return prob, w

We need to specify additional inputs for this extension. Each asset gets a floor of its own, and all of them share a ceiling of half the capital.

lower = [0.05, 0.05, 0.06, 0.07, 0.06, 0.05, 0.06, 0.07, 0.05, 0.06]
upper = [0.5] * nasdaq.num_assets
bounded = [
    solve(nasdaq, with_bounds, desired_return=r, lower=lower, upper=upper)
    for r in returns
]
Show the full outputHide the full output
=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 20 constraints (91%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |        10         0         0
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                   22 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1        21         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        40           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        40        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [5e-02, 7e-02]
  constraint bounds:         [5e-02, 5e+00] |                [1e+00, 5e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    7.716469e-01   5.43e+00
      10    1.005011e+00   1.65e-02   7.85e-03   2.83e-04      0.00
      11    1.005012e+00   1.65e-02   7.85e-03   5.78e-07      0.00

EXIT: Convergence to an infeasible point. The problem is determined
      to be infeasible.

HINT: Knitro spent  21.5% of solution time (0.000311 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   1.00501169801487e+00
Final feasibility error (abs / rel) =   1.65e-02 / 3.13e-04
Final optimality error  (abs / rel) =   7.85e-03 / 2.78e-03
# of iterations                     =         11 
# of CG iterations                  =          4 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00147 (     0.003 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 20 constraints (91%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |        10         0         0
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                   22 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1        21         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        40           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        40        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [5e-02, 7e-02]
  constraint bounds:         [5e-02, 8e+00] |                [1e+00, 8e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    7.867780e-01   7.86e+00
      10    1.068222e+00   5.82e-02   2.02e-02   2.34e-05      0.00
      20    1.068314e+00   5.83e-02   2.02e-02   2.28e-03      0.00
      29    1.068286e+00   5.82e-02   2.02e-02   1.50e-07      0.00

EXIT: Convergence to an infeasible point. The problem is determined
      to be infeasible.

HINT: Knitro spent  52.0% of solution time (0.002336 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   1.06828603559199e+00
Final feasibility error (abs / rel) =   5.82e-02 / 1.06e-03
Final optimality error  (abs / rel) =   2.02e-02 / 6.78e-03
# of iterations                     =         29 
# of CG iterations                  =         13 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00452 (     0.005 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 20 constraints (91%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |        10         0         0
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                   22 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1        21         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        40           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        40        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [5e-02, 7e-02]
  constraint bounds:         [5e-02, 1e+01] |                [1e+00, 1e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    8.867058e-01   9.23e+00
      10    1.143000e+00   1.00e-01   3.26e-02   4.86e-03      0.00
      18    1.143023e+00   1.00e-01   3.26e-02   1.19e-07      0.00

EXIT: Convergence to an infeasible point. The problem is determined
      to be infeasible.

HINT: Knitro spent   6.2% of solution time (0.000156 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   1.14302250322486e+00
Final feasibility error (abs / rel) =   1.00e-01 / 1.74e-03
Final optimality error  (abs / rel) =   3.26e-02 / 1.04e-02
# of iterations                     =         18 
# of CG iterations                  =          9 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00255 (     0.004 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 20 constraints (91%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                     10 |                            10
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        10 |        10         0         0
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                  273 |                             0
Number of constraints:                   22 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1        21         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        40           |         0        20          
  quadratic:         10         0        55 |        10         0        55
  total:             10        40        55 |        10        20        55
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-01, 7e+01] |                [3e-01, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [5e-02, 7e-02]
  constraint bounds:         [5e-02, 2e+01] |                [1e+00, 2e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    1.270330e+00   9.20e+00
      10    9.724567e+00   1.49e+00   4.22e+00   1.38e+00      0.00
      20    1.326475e+00   1.83e-01   5.75e-02   4.27e-02      0.00
      30    1.326869e+00   1.84e-01   5.74e-02   1.74e-05      0.00
      34    1.326885e+00   1.84e-01   5.74e-02   9.27e-08      0.00

EXIT: Convergence to an infeasible point. The problem is determined
      to be infeasible.

HINT: Knitro spent   6.1% of solution time (0.000230 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   1.32688503054724e+00
Final feasibility error (abs / rel) =   1.84e-01 / 2.93e-03
Final optimality error  (abs / rel) =   5.74e-02 / 1.66e-02
# of iterations                     =         34 
# of CG iterations                  =         18 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00381 (     0.004 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================
draw_comparison(
    bounded,
    [f"Return {r}, Risk {s.risk:.2f}" for r, s in zip(returns, bounded)],
    nasdaq.names,
    "Least risk with every weight bounded",
)

The floors force capital into assets the model had left out, including the ones with a negative expected return, so the same desired return has to be met from a worse starting position. Every portfolio here was already available to the model before and it did not choose one, so bounding the weights can only raise the risk.

Adding a cardinality limit

To further control the diversification, we introduce a cardinality constraint to ensure that exactly \(K\) assets are selected in the portfolio. For this, we introduce a new set of binary variables:

  • \(x_i \in \{0, 1\}\), \(i = 1, \dots, N\): \(x_i = 1\) iff asset \(i\) is selected in the portfolio

The diversification constraints and the linking constraints between \(x_i\) and \(w_i\) merge into a single pair of bounds, alongside the cardinality constraint itself:

\[ \forall i = 1, \dots, N \qquad L_i x_i \leq w_i \leq U_i x_i, \qquad \sum_{i=1}^N x_i = K \]

This ensures that if an asset \(i\) is not selected (\(x_i = 0\)), its weight \(w_i\) will be zero. If an asset is selected (\(x_i = 1\)), its weight will lie between \(L_i\) and \(U_i\).

The binary variables make this a mixed-integer QP, harder than the two models before it.

Every bound here is multiplied by a binary, so with_cardinality builds on markowitz rather than on with_bounds.

def with_cardinality(market, *, desired_return, lower, upper, cardinality):
    prob, w = markowitz(market, desired_return=desired_return)
    x = [prob.add_variable(vtype=knitro.KN_VARTYPE_BINARY) for _ in market.assets]

    for i in market.assets:
        prob.add_constraint(w[i] >= lower[i] * x[i])
        prob.add_constraint(w[i] <= upper[i] * x[i])
    prob.add_constraint(prob.nsum(x[i] for i in market.assets) == cardinality)

    return prob, w

The same bounds apply here, and exactly three assets may be held.

limited = [
    solve(
        nasdaq,
        with_cardinality,
        desired_return=r,
        lower=lower,
        upper=upper,
        cardinality=3,
    )
    for r in returns
]
Show the full outputHide the full output
=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro changing mip_method from AUTO to 1.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              1e-06
opttol                   1e-06
opttol_abs               0.001
Knitro changing mip_root_nlpalg from AUTO to 1.
Knitro changing mip_node_nlpalg from AUTO to 1.
Knitro changing mip_branchrule from AUTO to 2.
Knitro changing mip_selectrule from AUTO to 2.
Knitro changing mip_mir from AUTO to 2.
Knitro changing mip_clique from AUTO to 0.
Knitro changing mip_zerohalf from AUTO to 0.
Knitro changing mip_liftproject from AUTO to 0.
Knitro changing mip_knapsack from AUTO to 2.
Knitro changing mip_gomory from AUTO to 1.
Knitro changing mip_cut_flowcover from AUTO to 2.
Knitro changing mip_cut_probing from AUTO to 1.
Knitro changing mip_rounding from AUTO to 3.
Knitro changing mip_heuristic_strategy from AUTO to 1.
Knitro changing mip_heuristic_feaspump from AUTO to 1.
Knitro changing mip_heuristic_misqp from AUTO to 0.
Knitro changing mip_heuristic_mpec from AUTO to 1.
Knitro changing mip_heuristic_diving from AUTO to 1926.
Knitro changing mip_heuristic_fixpropagate from AUTO to 62.
Knitro changing mip_heuristic_lns from AUTO to 0.
Knitro changing mip_heuristic_localsearch from AUTO to 1.
Knitro changing mip_pseudoinit from AUTO to 1.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex MIQP
Objective: minimize / quadratic
Number of variables:                     20 |                            20
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        20 |         5         0        15
                             free     fixed |                free     fixed
                                0         0 |                   0         0
                  cont.    binary   integer |     cont.    binary   integer
                     10        10         0 |        10        10         0
Number of expressions:                  326 |                             0
Number of constraints:                   23 |                            23
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             2        21         0 |         2        21         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        70           |         0        70          
  quadratic:         10         0        55 |        10         0        55
  total:             10        70        55 |        10        70        55

Knitro using Branch and Bound method with 8 threads.

Initial points
--------------
No initial point provided for the root node relaxation.
No primal point provided for the MIP.

Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [5e-02, 7e+01] |                [5e-02, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 5e+00] |                [1e+00, 5e+00]

Root node relaxation
--------------------

 Iter      Objective      Feasibility        Optimality       Time 
                             error              error        (secs)
 ----      ---------      -----------        ----------      ------
    0       0.430856          2.59006          0.156436       0.004
    1       0.430856          2.59006          0.156436       0.004
    2       0.503552      4.44089e-16          0.182691       0.004
    3       0.417456      4.44089e-16       2.07379e-02       0.004
    4       0.347627      8.88178e-16       3.10890e-03       0.004
    5       0.328335      2.22045e-16       5.61765e-04       0.004
    6       0.323952      8.88178e-16       7.84677e-05       0.005
    7       0.323144      4.44089e-16       2.05361e-06       0.005
    8       0.323134      7.80792e-12       1.78962e-10       0.005
    9       0.323134      7.80792e-12       1.78962e-10       0.005
   10       0.323134      1.03398e-25       2.43939e-08       0.005

Tree search
-----------

       Nodes        Best solution   Best bound      Gap       Time 
   Expl  |  Unexpl      value         value                  (secs)
   ---------------  -------------   ----------      ---      ------
      1       2     0.505370   LS     0.323134     18.22%     0.006
      3       4     0.495263   LS     0.323134     17.21%     0.011
     10      11     0.391153 MPEC     0.329103      6.20%     0.017
     27       0     0.391153          0.391114      0.00%     0.022

EXIT: Optimal solution found.

HINT: Knitro spent   9.3% of solution time (0.002080 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics for MIP
------------------------
Final objective value               =  3.91152827306554263e-01
Final bound value                   =  3.91113712023823612e-01
Final optimality gap (abs / rel)    =  3.91153e-05 / 3.91153e-05 (0.00%)
# of root cutting plane rounds      =  1
# of restarts                       =  0
# of nodes processed                =  27 (0.045s)
# of strong branching evaluations   =  0 (0.000s)
# of function evaluations           =  0 (0.000s)
# of gradient evaluations           =  0 (0.000s)
# of hessian evaluations            =  0 (0.000s)
# of hessian-vector evaluations     =  0
# of subproblems processed          =  32 (0.055s)
Total program time (secs)           =  0.02225 (0.090 CPU time)
Time spent in evaluations (secs)    =  0.00000

Cuts statistics (gen / add)
---------------------------
Knapsack cuts                       =  0 / 0
Mixed-integer rounding cuts         =  2 / 2
Gomory cuts                         =  0 / 0
Flow-cover cuts                     =  0 / 0
Probing cuts                        =  0 / 0

Heuristics statistics (calls / successes / time)
------------------------------------------------
Feasibility pump                    =  1 / 1 / 0.006s
Rounding heuristic                  =  1 / 0 / 0.000s
MPEC heuristic                      =  1 / 1 / 0.005s
Local search heuristic              =  8 / 4 / 0.020s

===========================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro changing mip_method from AUTO to 1.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              1e-06
opttol                   1e-06
opttol_abs               0.001
Knitro changing mip_root_nlpalg from AUTO to 1.
Knitro changing mip_node_nlpalg from AUTO to 1.
Knitro changing mip_branchrule from AUTO to 2.
Knitro changing mip_selectrule from AUTO to 2.
Knitro changing mip_mir from AUTO to 2.
Knitro changing mip_clique from AUTO to 0.
Knitro changing mip_zerohalf from AUTO to 0.
Knitro changing mip_liftproject from AUTO to 0.
Knitro changing mip_knapsack from AUTO to 2.
Knitro changing mip_gomory from AUTO to 1.
Knitro changing mip_cut_flowcover from AUTO to 2.
Knitro changing mip_cut_probing from AUTO to 1.
Knitro changing mip_rounding from AUTO to 3.
Knitro changing mip_heuristic_strategy from AUTO to 1.
Knitro changing mip_heuristic_feaspump from AUTO to 1.
Knitro changing mip_heuristic_misqp from AUTO to 0.
Knitro changing mip_heuristic_mpec from AUTO to 1.
Knitro changing mip_heuristic_diving from AUTO to 1926.
Knitro changing mip_heuristic_fixpropagate from AUTO to 62.
Knitro changing mip_heuristic_lns from AUTO to 0.
Knitro changing mip_heuristic_localsearch from AUTO to 1.
Knitro changing mip_pseudoinit from AUTO to 1.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex MIQP
Objective: minimize / quadratic
Number of variables:                     20 |                            20
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        20 |         5         0        15
                             free     fixed |                free     fixed
                                0         0 |                   0         0
                  cont.    binary   integer |     cont.    binary   integer
                     10        10         0 |        10        10         0
Number of expressions:                  326 |                             0
Number of constraints:                   23 |                            23
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             2        21         0 |         2        21         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        70           |         0        70          
  quadratic:         10         0        55 |        10         0        55
  total:             10        70        55 |        10        70        55

Knitro using Branch and Bound method with 8 threads.

Initial points
--------------
No initial point provided for the root node relaxation.
No primal point provided for the MIP.

Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [5e-02, 7e+01] |                [5e-02, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 8e+00] |                [1e+00, 8e+00]

Root node relaxation
--------------------

 Iter      Objective      Feasibility        Optimality       Time 
                             error              error        (secs)
 ----      ---------      -----------        ----------      ------
    0       0.401827          3.61497          0.157052       0.001
    1       0.401827          3.61497          0.157052       0.001
    2       0.496356      8.51961e-03          0.187828       0.001
    3       0.477358      1.45431e-04       2.25667e-02       0.001
    4       0.376725      4.44089e-16       8.12537e-03       0.001
    5       0.361378      4.44089e-16       4.98045e-04       0.002
    6       0.357859      4.44089e-16       6.43383e-05       0.002
    7       0.357795      0.00000e+00       3.68238e-06       0.002
    8       0.357791      0.00000e+00       2.80556e-07       0.002
    9       0.357791      0.00000e+00       2.80556e-07       0.002
   10       0.357791      0.00000e+00       8.57410e-07       0.002

Tree search
-----------

       Nodes        Best solution   Best bound      Gap       Time 
   Expl  |  Unexpl      value         value                  (secs)
   ---------------  -------------   ----------      ---      ------
      1       2     0.506866 FCRD     0.357791     14.91%     0.004
     31       0     0.506866          0.506816      0.01%     0.016

EXIT: Optimal solution found.

HINT: Knitro spent   1.5% of solution time (0.000243 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics for MIP
------------------------
Final objective value               =  5.06866190634101987e-01
Final bound value                   =  5.06815504015038609e-01
Final optimality gap (abs / rel)    =  5.06866e-05 / 5.06866e-05 (0.01%)
# of root cutting plane rounds      =  1
# of restarts                       =  0
# of nodes processed                =  31 (0.043s)
# of strong branching evaluations   =  0 (0.000s)
# of function evaluations           =  0 (0.000s)
# of gradient evaluations           =  0 (0.000s)
# of hessian evaluations            =  0 (0.000s)
# of hessian-vector evaluations     =  0
# of subproblems processed          =  34 (0.047s)
Total program time (secs)           =  0.01568 (0.079 CPU time)
Time spent in evaluations (secs)    =  0.00000

Cuts statistics (gen / add)
---------------------------
Knapsack cuts                       =  0 / 0
Mixed-integer rounding cuts         =  0 / 0
Gomory cuts                         =  0 / 0
Flow-cover cuts                     =  0 / 0
Probing cuts                        =  0 / 0

Heuristics statistics (calls / successes / time)
------------------------------------------------
Feasibility pump                    =  1 / 0 / 0.001s
Rounding heuristic                  =  1 / 1 / 0.001s
MPEC heuristic                      =  1 / 0 / 0.003s
Local search heuristic              =  7 / 3 / 0.016s

===========================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro changing mip_method from AUTO to 1.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              1e-06
opttol                   1e-06
opttol_abs               0.001
Knitro changing mip_root_nlpalg from AUTO to 1.
Knitro changing mip_node_nlpalg from AUTO to 1.
Knitro changing mip_branchrule from AUTO to 2.
Knitro changing mip_selectrule from AUTO to 2.
Knitro changing mip_mir from AUTO to 2.
Knitro changing mip_clique from AUTO to 0.
Knitro changing mip_zerohalf from AUTO to 0.
Knitro changing mip_liftproject from AUTO to 0.
Knitro changing mip_knapsack from AUTO to 2.
Knitro changing mip_gomory from AUTO to 1.
Knitro changing mip_cut_flowcover from AUTO to 2.
Knitro changing mip_cut_probing from AUTO to 1.
Knitro changing mip_rounding from AUTO to 3.
Knitro changing mip_heuristic_strategy from AUTO to 1.
Knitro changing mip_heuristic_feaspump from AUTO to 1.
Knitro changing mip_heuristic_misqp from AUTO to 0.
Knitro changing mip_heuristic_mpec from AUTO to 1.
Knitro changing mip_heuristic_diving from AUTO to 1926.
Knitro changing mip_heuristic_fixpropagate from AUTO to 62.
Knitro changing mip_heuristic_lns from AUTO to 0.
Knitro changing mip_heuristic_localsearch from AUTO to 1.
Knitro changing mip_pseudoinit from AUTO to 1.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex MIQP
Objective: minimize / quadratic
Number of variables:                     20 |                            20
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        20 |         5         0        15
                             free     fixed |                free     fixed
                                0         0 |                   0         0
                  cont.    binary   integer |     cont.    binary   integer
                     10        10         0 |        10        10         0
Number of expressions:                  326 |                             0
Number of constraints:                   23 |                            23
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             2        21         0 |         2        21         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        70           |         0        70          
  quadratic:         10         0        55 |        10         0        55
  total:             10        70        55 |        10        70        55

Knitro using Branch and Bound method with 8 threads.

Initial points
--------------
No initial point provided for the root node relaxation.
No primal point provided for the MIP.

Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [5e-02, 7e+01] |                [5e-02, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 1e+01] |                [1e+00, 1e+01]

Root node relaxation
--------------------

 Iter      Objective      Feasibility        Optimality       Time 
                             error              error        (secs)
 ----      ---------      -----------        ----------      ------
    0       0.405243          3.56028          0.157915       0.001
    1       0.405243          3.56028          0.157915       0.001
    2       0.499915      2.14435e-02          0.186931       0.001
    3       0.576766      4.44089e-16       2.17350e-02       0.001
    4       0.476514      0.00000e+00       4.88309e-03       0.001
    5       0.433684      0.00000e+00       6.66356e-04       0.002
    6       0.429045      1.11022e-16       6.22491e-05       0.002
    7       0.428911      4.44089e-16       4.11592e-08       0.002
    8       0.428911      6.57219e-11       3.42538e-08       0.002
    9       0.428911      7.89762e-25       1.83348e-08       0.002

Tree search
-----------

       Nodes        Best solution   Best bound      Gap       Time 
   Expl  |  Unexpl      value         value                  (secs)
   ---------------  -------------   ----------      ---      ------
      1       2     0.891125   LS     0.428911     46.22%     0.004
      2       3     0.723236   LS     0.428911     29.43%     0.006
      9      10     0.511145 MPEC     0.428911      8.22%     0.012
     21       0     0.511145          0.511093      0.01%     0.016

EXIT: Optimal solution found.

HINT: Knitro spent   1.5% of solution time (0.000241 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics for MIP
------------------------
Final objective value               =  5.11144529489657606e-01
Final bound value                   =  5.11093415036708687e-01
Final optimality gap (abs / rel)    =  5.11145e-05 / 5.11145e-05 (0.01%)
# of root cutting plane rounds      =  1
# of restarts                       =  0
# of nodes processed                =  21 (0.023s)
# of strong branching evaluations   =  0 (0.000s)
# of function evaluations           =  0 (0.000s)
# of gradient evaluations           =  0 (0.000s)
# of hessian evaluations            =  0 (0.000s)
# of hessian-vector evaluations     =  0
# of subproblems processed          =  24 (0.032s)
Total program time (secs)           =  0.01607 (0.061 CPU time)
Time spent in evaluations (secs)    =  0.00000

Cuts statistics (gen / add)
---------------------------
Knapsack cuts                       =  0 / 0
Mixed-integer rounding cuts         =  1 / 1
Gomory cuts                         =  0 / 0
Flow-cover cuts                     =  0 / 0
Probing cuts                        =  0 / 0

Heuristics statistics (calls / successes / time)
------------------------------------------------
Feasibility pump                    =  1 / 1 / 0.004s
Rounding heuristic                  =  2 / 1 / 0.002s
MPEC heuristic                      =  1 / 1 / 0.004s
Local search heuristic              =  8 / 3 / 0.016s

===========================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro changing mip_method from AUTO to 1.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              1e-06
opttol                   1e-06
opttol_abs               0.001
Knitro changing mip_root_nlpalg from AUTO to 1.
Knitro changing mip_node_nlpalg from AUTO to 1.
Knitro changing mip_branchrule from AUTO to 2.
Knitro changing mip_selectrule from AUTO to 2.
Knitro changing mip_mir from AUTO to 2.
Knitro changing mip_clique from AUTO to 0.
Knitro changing mip_zerohalf from AUTO to 0.
Knitro changing mip_liftproject from AUTO to 0.
Knitro changing mip_knapsack from AUTO to 2.
Knitro changing mip_gomory from AUTO to 1.
Knitro changing mip_cut_flowcover from AUTO to 2.
Knitro changing mip_cut_probing from AUTO to 1.
Knitro changing mip_rounding from AUTO to 3.
Knitro changing mip_heuristic_strategy from AUTO to 1.
Knitro changing mip_heuristic_feaspump from AUTO to 1.
Knitro changing mip_heuristic_misqp from AUTO to 0.
Knitro changing mip_heuristic_mpec from AUTO to 1.
Knitro changing mip_heuristic_diving from AUTO to 1926.
Knitro changing mip_heuristic_fixpropagate from AUTO to 62.
Knitro changing mip_heuristic_lns from AUTO to 0.
Knitro changing mip_heuristic_localsearch from AUTO to 1.
Knitro changing mip_pseudoinit from AUTO to 1.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex MIQP
Objective: minimize / quadratic
Number of variables:                     20 |                            20
  bounds:         lower     upper     range |     lower     upper     range
                      0         0        20 |         5         0        15
                             free     fixed |                free     fixed
                                0         0 |                   0         0
                  cont.    binary   integer |     cont.    binary   integer
                     10        10         0 |        10        10         0
Number of expressions:                  326 |                             0
Number of constraints:                   23 |                            23
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             2        21         0 |         2        21         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0        70           |         0        70          
  quadratic:         10         0        55 |        10         0        55
  total:             10        70        55 |        10        70        55

Knitro using Branch and Bound method with 8 threads.

Initial points
--------------
No initial point provided for the root node relaxation.
No primal point provided for the MIP.

Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [5e-02, 7e+01] |                [5e-02, 7e+01]
  quadratic objective:       [2e-02, 1e+01] |                [2e-02, 1e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 2e+01] |                [1e+00, 2e+01]

Root node relaxation
--------------------

 Iter      Objective      Feasibility        Optimality       Time 
                             error              error        (secs)
 ----      ---------      -----------        ----------      ------
    0       0.422045          8.42097          0.158771       0.001
    1       0.422045          8.42097          0.158771       0.002
    2       0.536720      4.94265e-02          0.201772       0.002
    3       0.889723      4.89048e-03          0.142338       0.002
    4       0.836220      0.00000e+00       9.98626e-03       0.002
    5       0.780900      0.00000e+00       1.83991e-03       0.002
    6       0.766963      2.22045e-16       2.00489e-04       0.002
    7       0.766683      0.00000e+00       1.82529e-08       0.002
    8       0.766683      8.42165e-11       1.54466e-08       0.002
    9       0.766683      1.52462e-24       3.40515e-09       0.003

Tree search
-----------

       Nodes        Best solution   Best bound      Gap       Time 
   Expl  |  Unexpl      value         value                  (secs)
   ---------------  -------------   ----------      ---      ------
      1       2      1.40466   LS     0.766683     45.42%     0.004
      7       8     0.843554 FCRD     0.775295      6.83%     0.009
     15       0     0.843554          0.843470      0.01%     0.013

EXIT: Optimal solution found.

HINT: Knitro spent   2.0% of solution time (0.000255 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics for MIP
------------------------
Final objective value               =  8.43554338298756701e-01
Final bound value                   =  8.43469982864926804e-01
Final optimality gap (abs / rel)    =  8.43554e-05 / 8.43554e-05 (0.01%)
# of root cutting plane rounds      =  1
# of restarts                       =  0
# of nodes processed                =  15 (0.018s)
# of strong branching evaluations   =  0 (0.000s)
# of function evaluations           =  0 (0.000s)
# of gradient evaluations           =  0 (0.000s)
# of hessian evaluations            =  0 (0.000s)
# of hessian-vector evaluations     =  0
# of subproblems processed          =  17 (0.026s)
Total program time (secs)           =  0.01293 (0.048 CPU time)
Time spent in evaluations (secs)    =  0.00000

Cuts statistics (gen / add)
---------------------------
Knapsack cuts                       =  0 / 0
Mixed-integer rounding cuts         =  0 / 0
Gomory cuts                         =  0 / 0
Flow-cover cuts                     =  0 / 0
Probing cuts                        =  0 / 0

Heuristics statistics (calls / successes / time)
------------------------------------------------
Feasibility pump                    =  1 / 1 / 0.004s
Rounding heuristic                  =  2 / 1 / 0.001s
MPEC heuristic                      =  1 / 1 / 0.004s
Local search heuristic              =  7 / 1 / 0.012s

===========================================================================
draw_comparison(
    limited,
    [f"Return {r}, Risk {s.risk:.2f}" for r, s in zip(returns, limited)],
    nasdaq.names,
    "Least risk holding exactly three assets",
)

By incorporating extensions related to diversification and cardinality constraints, some assets may no longer be included in the portfolio, while others may see their weights increase. This occurs because such constraints can limit the number of assets selected and enforce a more concentrated allocation, thereby intensifying the focus on a smaller set of high-performing assets.

The risks come out below the ones in the figure above. That looks wrong for a model carrying an extra constraint. The reason is that a floor now reads \(L_i x_i\), so it applies only to an asset that is actually held, and the seven that are dropped stop having to be paid for.

Rebalancing with transaction costs

Rebalancing a portfolio involves buying and selling securities to adjust its composition, leading to turnover. While the basic Markowitz model assumes there are no trading costs, in practice, turnover incurs expenses. There are two kinds of transaction costs:

  • Fixed costs, which represent commissions or transfer fees
  • Variable costs, which depend on the transaction volume, representing costs such as market impact or bid/ask spread

In the variant considered in this section, the objective is to maximize the return and the maximum risk is set as a constraint. To make the model more meaningful, we consider that a proportion of the capital is already invested in some assets.

Input

  • \(W^0_i\): fraction of the portfolio value already invested in asset \(i\)
  • \(\Gamma\): limited risk level
  • \(F_i\): fixed cost if buying asset \(i\)
  • \(V_i\): variable factor cost when buying asset \(i\)

Variables. We need two additional sets of variables to model the absolute value \(|w_i - W^0_i|\), the proportion of the capital moved from asset \(i\) (bought or sold):

  • \(z^+_i \in [0, 1]\): positive part of \(w_i - W^0_i\)
  • \(z^-_i \in [0, 1]\): negative part of \(w_i - W^0_i\)

Objective: maximize the return

\[ \max \sum_{i=1}^N \mu_i w_i \]

Constraints

\[ z^+_i - z^-_i = w_i - W^0_i, \qquad \sum_{i=1}^N \sum_{j=1}^N \Sigma_{ij} w_i w_j \leq \Gamma, \qquad \sum_{i=1}^N w_i + \sum_{i=1}^N \big( F_i x_i + V_i (z^+_i + z^-_i) \big) = \sum_{i=1}^N W^0_i \]

rebalance keeps nothing from markowitz. The objective and the risk have swapped places.

def rebalance(market, *, holdings, max_risk, fixed_cost, variable_cost):
    mu, sigma = market.returns, market.covariance
    assets = market.assets
    prob = knitro.Problem()
    w = [prob.add_variable(lb=0, ub=1) for _ in assets]
    x = [prob.add_variable(vtype=knitro.KN_VARTYPE_BINARY) for _ in assets]
    z_plus = [prob.add_variable(lb=0) for _ in assets]
    z_minus = [prob.add_variable(lb=0) for _ in assets]

    expected = prob.nsum(mu[i] * w[i] for i in assets)
    prob.add_objective(expected, goal=knitro.KN_OBJGOAL_MAXIMIZE)

    for i in assets:
        prob.add_constraint(z_plus[i] - z_minus[i] == w[i] - holdings[i])

    variance = prob.nsum(sigma[i][j] * w[i] * w[j] for i in assets for j in assets)
    prob.add_constraint(variance <= max_risk)

    moved = [z_plus[i] + z_minus[i] for i in assets]
    cost = prob.nsum(fixed_cost[i] * x[i] + variable_cost[i] * moved[i] for i in assets)
    prob.add_constraint(prob.nsum(w[i] for i in assets) + cost == sum(holdings))

    return prob, w

A high variable cost limits the changes we can make to our current portfolio. That’s why it’s important to vary this factor to observe the differences. We start from an equally weighted portfolio.

gamma = 0.7
w0 = [1 / nasdaq.num_assets] * nasdaq.num_assets
fixed_cost = [0.1] * nasdaq.num_assets
variable_costs = [1, 0.75, 0.5, 0.25, 0]

initial_return = expected_return(nasdaq.returns, w0)
initial = Portfolio(w0, initial_return, risk(nasdaq.covariance, w0))
rebalanced = [
    solve(
        nasdaq,
        rebalance,
        holdings=w0,
        max_risk=gamma,
        fixed_cost=fixed_cost,
        variable_cost=[v] * nasdaq.num_assets,
    )
    for v in variable_costs
]
Show the full outputHide the full output
=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro changing mip_method from AUTO to 1.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              1e-06
opttol                   1e-06
opttol_abs               0.001
Knitro changing mip_root_nlpalg from AUTO to 1.
Knitro changing mip_node_nlpalg from AUTO to 1.
Knitro changing mip_branchrule from AUTO to 2.
Knitro changing mip_selectrule from AUTO to 2.
Knitro changing mip_mir from AUTO to 2.
Knitro changing mip_clique from AUTO to 0.
Knitro changing mip_zerohalf from AUTO to 0.
Knitro changing mip_liftproject from AUTO to 0.
Knitro changing mip_knapsack from AUTO to 2.
Knitro changing mip_gomory from AUTO to 1.
Knitro changing mip_cut_flowcover from AUTO to 2.
Knitro changing mip_cut_probing from AUTO to 1.
Knitro changing mip_rounding from AUTO to 3.
Knitro changing mip_heuristic_strategy from AUTO to 1.
Knitro changing mip_heuristic_feaspump from AUTO to 1.
Knitro changing mip_heuristic_misqp from AUTO to 0.
Knitro changing mip_heuristic_mpec from AUTO to 1.
Knitro changing mip_heuristic_diving from AUTO to 1926.
Knitro changing mip_heuristic_fixpropagate from AUTO to 62.
Knitro changing mip_heuristic_lns from AUTO to 0.
Knitro changing mip_heuristic_localsearch from AUTO to 1.
Knitro changing mip_pseudoinit from AUTO to 1.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex MIQCQP
Objective: maximize / linear   
Number of variables:                     40 |                            40
  bounds:         lower     upper     range |     lower     upper     range
                     20         0        20 |        20         0        20
                             free     fixed |                free     fixed
                                0         0 |                   0         0
                  cont.    binary   integer |     cont.    binary   integer
                     30        10         0 |        30        10         0
Number of expressions:                  366 |                             0
Number of constraints:                   12 |                            12
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:            11         0         0 |        11         0         0
  quadratic:          0         1         0 |         0         1         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:            10        70           |        10        70          
  quadratic:          0        10        55 |         0        10        55
  total:             10        80        55 |        10        80        55

Knitro using Branch and Bound method with 8 threads.

Initial points
--------------
No initial point provided for the root node relaxation.
No primal point provided for the MIP.

Coefficient range:
  linear objective:          [3e-01, 7e+01] |                [3e-01, 7e+01]
  linear constraints:        [1e-01, 1e+00] |                [1e-01, 1e+00]
  quadratic objective:       [0e+00, 0e+00] |                [0e+00, 0e+00]
  quadratic constraints:     [2e-02, 1e+01] |                [2e-02, 1e+01]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [7e-01, 1e+00] |                [1e-01, 1e+00]

Root node relaxation
--------------------

 Iter      Objective      Feasibility        Optimality       Time 
                             error              error        (secs)
 ----      ---------      -----------        ----------      ------
    0        3.62551         0.481231           22.5372       0.002
    1        5.66721         0.458116           1.27690       0.002
    2        6.16459         0.175048          0.681292       0.002
    3        5.74630      1.26574e-02          0.106390       0.002
    4        6.61044      1.96393e-04       9.68422e-03       0.002
    5        6.65243      1.42437e-06       8.59905e-05       0.002
    6        6.65278      1.36152e-10       8.22768e-09       0.002
    7        6.65278      2.28983e-15       1.77636e-15       0.003
    8        6.65278      2.28983e-15       1.77636e-15       0.003

Tree search
-----------

       Nodes        Best solution   Best bound      Gap       Time 
   Expl  |  Unexpl      value         value                  (secs)
   ---------------  -------------   ----------      ---      ------
      1       0      6.65278 LEAF      6.65278      0.00%     0.003

EXIT: Optimal solution found.

HINT: Knitro spent   7.8% of solution time (0.000283 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics for MIP
------------------------
Final objective value               =  6.65278400000000136e+00
Final bound value                   =  6.65278400000000136e+00
Final optimality gap (abs / rel)    =  0.00000e+00 / 0.00000e+00 (0.00%)
# of root cutting plane rounds      =  0
# of restarts                       =  0
# of nodes processed                =  1 (0.002s)
# of strong branching evaluations   =  0 (0.000s)
# of function evaluations           =  0 (0.000s)
# of gradient evaluations           =  0 (0.000s)
# of hessian evaluations            =  0 (0.000s)
# of hessian-vector evaluations     =  0
# of subproblems processed          =  1 (0.002s)
Total program time (secs)           =  0.00358 (0.021 CPU time)
Time spent in evaluations (secs)    =  0.00000

Cuts statistics (gen / add)
---------------------------
Knapsack cuts                       =  0 / 0
Mixed-integer rounding cuts         =  0 / 0
Gomory cuts                         =  0 / 0
Flow-cover cuts                     =  0 / 0
Probing cuts                        =  0 / 0

Heuristics statistics (calls / successes / time)
------------------------------------------------
Feasibility pump                    =  0 / 0 / 0.000s
Rounding heuristic                  =  0 / 0 / 0.000s
MPEC heuristic                      =  0 / 0 / 0.000s
Local search heuristic              =  1 / 1 / 0.002s

===========================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro changing mip_method from AUTO to 1.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              1e-06
opttol                   1e-06
opttol_abs               0.001
Knitro changing mip_root_nlpalg from AUTO to 1.
Knitro changing mip_node_nlpalg from AUTO to 1.
Knitro changing mip_branchrule from AUTO to 2.
Knitro changing mip_selectrule from AUTO to 2.
Knitro changing mip_mir from AUTO to 2.
Knitro changing mip_clique from AUTO to 0.
Knitro changing mip_zerohalf from AUTO to 0.
Knitro changing mip_liftproject from AUTO to 0.
Knitro changing mip_knapsack from AUTO to 2.
Knitro changing mip_gomory from AUTO to 1.
Knitro changing mip_cut_flowcover from AUTO to 2.
Knitro changing mip_cut_probing from AUTO to 1.
Knitro changing mip_rounding from AUTO to 3.
Knitro changing mip_heuristic_strategy from AUTO to 1.
Knitro changing mip_heuristic_feaspump from AUTO to 1.
Knitro changing mip_heuristic_misqp from AUTO to 0.
Knitro changing mip_heuristic_mpec from AUTO to 1.
Knitro changing mip_heuristic_diving from AUTO to 1926.
Knitro changing mip_heuristic_fixpropagate from AUTO to 62.
Knitro changing mip_heuristic_lns from AUTO to 0.
Knitro changing mip_heuristic_localsearch from AUTO to 1.
Knitro changing mip_pseudoinit from AUTO to 1.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex MIQCQP
Objective: maximize / linear   
Number of variables:                     40 |                            40
  bounds:         lower     upper     range |     lower     upper     range
                     20         0        20 |        20         0        20
                             free     fixed |                free     fixed
                                0         0 |                   0         0
                  cont.    binary   integer |     cont.    binary   integer
                     30        10         0 |        30        10         0
Number of expressions:                  375 |                             0
Number of constraints:                   12 |                            12
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:            11         0         0 |        11         0         0
  quadratic:          0         1         0 |         0         1         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:            10        70           |        10        70          
  quadratic:          0        10        55 |         0        10        55
  total:             10        80        55 |        10        80        55

Knitro using Branch and Bound method with 8 threads.

Initial points
--------------
No initial point provided for the root node relaxation.
No primal point provided for the MIP.

Coefficient range:
  linear objective:          [3e-01, 7e+01] |                [3e-01, 7e+01]
  linear constraints:        [1e-01, 1e+00] |                [1e-01, 1e+00]
  quadratic objective:       [0e+00, 0e+00] |                [0e+00, 0e+00]
  quadratic constraints:     [2e-02, 1e+01] |                [2e-02, 1e+01]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [7e-01, 1e+00] |                [1e-01, 1e+00]

Root node relaxation
--------------------

 Iter      Objective      Feasibility        Optimality       Time 
                             error              error        (secs)
 ----      ---------      -----------        ----------      ------
    0        1.49121         0.459886           22.9065       0.001
    1        4.53486         0.424316          0.630298       0.001
    2        4.76318      5.82386e-02          0.237583       0.001
    3        7.97230      3.05881e-03       7.36196e-02       0.002
    4        8.11599      1.54270e-05       4.46135e-04       0.002
    5        8.11679      6.86051e-09       2.05212e-07       0.002
    6        8.11679      1.11022e-15       7.22175e-14       0.002
    7        8.11679      2.22045e-16       1.77636e-15       0.002

Tree search
-----------

       Nodes        Best solution   Best bound      Gap       Time 
   Expl  |  Unexpl      value         value                  (secs)
   ---------------  -------------   ----------      ---      ------
      1       0      8.11679 LEAF      8.11679      0.00%     0.004

EXIT: Optimal solution found.

HINT: Knitro spent   3.6% of solution time (0.000135 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics for MIP
------------------------
Final objective value               =  8.11679471428571908e+00
Final bound value                   =  8.11679471428571908e+00
Final optimality gap (abs / rel)    =  0.00000e+00 / 0.00000e+00 (0.00%)
# of root cutting plane rounds      =  0
# of restarts                       =  0
# of nodes processed                =  1 (0.002s)
# of strong branching evaluations   =  0 (0.000s)
# of function evaluations           =  0 (0.000s)
# of gradient evaluations           =  0 (0.000s)
# of hessian evaluations            =  0 (0.000s)
# of hessian-vector evaluations     =  0
# of subproblems processed          =  1 (0.002s)
Total program time (secs)           =  0.00365 (0.015 CPU time)
Time spent in evaluations (secs)    =  0.00000

Cuts statistics (gen / add)
---------------------------
Knapsack cuts                       =  0 / 0
Mixed-integer rounding cuts         =  0 / 0
Gomory cuts                         =  0 / 0
Flow-cover cuts                     =  0 / 0
Probing cuts                        =  0 / 0

Heuristics statistics (calls / successes / time)
------------------------------------------------
Feasibility pump                    =  0 / 0 / 0.000s
Rounding heuristic                  =  0 / 0 / 0.000s
MPEC heuristic                      =  0 / 0 / 0.000s
Local search heuristic              =  3 / 0 / 0.003s

===========================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro changing mip_method from AUTO to 1.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              1e-06
opttol                   1e-06
opttol_abs               0.001
Knitro changing mip_root_nlpalg from AUTO to 1.
Knitro changing mip_node_nlpalg from AUTO to 1.
Knitro changing mip_branchrule from AUTO to 2.
Knitro changing mip_selectrule from AUTO to 2.
Knitro changing mip_mir from AUTO to 2.
Knitro changing mip_clique from AUTO to 0.
Knitro changing mip_zerohalf from AUTO to 0.
Knitro changing mip_liftproject from AUTO to 0.
Knitro changing mip_knapsack from AUTO to 2.
Knitro changing mip_gomory from AUTO to 1.
Knitro changing mip_cut_flowcover from AUTO to 2.
Knitro changing mip_cut_probing from AUTO to 1.
Knitro changing mip_rounding from AUTO to 3.
Knitro changing mip_heuristic_strategy from AUTO to 1.
Knitro changing mip_heuristic_feaspump from AUTO to 1.
Knitro changing mip_heuristic_misqp from AUTO to 0.
Knitro changing mip_heuristic_mpec from AUTO to 1.
Knitro changing mip_heuristic_diving from AUTO to 1926.
Knitro changing mip_heuristic_fixpropagate from AUTO to 62.
Knitro changing mip_heuristic_lns from AUTO to 0.
Knitro changing mip_heuristic_localsearch from AUTO to 1.
Knitro changing mip_pseudoinit from AUTO to 1.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex MIQCQP
Objective: maximize / linear   
Number of variables:                     40 |                            40
  bounds:         lower     upper     range |     lower     upper     range
                     20         0        20 |        20         0        20
                             free     fixed |                free     fixed
                                0         0 |                   0         0
                  cont.    binary   integer |     cont.    binary   integer
                     30        10         0 |        30        10         0
Number of expressions:                  375 |                             0
Number of constraints:                   12 |                            12
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:            11         0         0 |        11         0         0
  quadratic:          0         1         0 |         0         1         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:            10        70           |        10        70          
  quadratic:          0        10        55 |         0        10        55
  total:             10        80        55 |        10        80        55

Knitro using Branch and Bound method with 8 threads.

Initial points
--------------
No initial point provided for the root node relaxation.
No primal point provided for the MIP.

Coefficient range:
  linear objective:          [3e-01, 7e+01] |                [3e-01, 7e+01]
  linear constraints:        [1e-01, 1e+00] |                [1e-01, 1e+00]
  quadratic objective:       [0e+00, 0e+00] |                [0e+00, 0e+00]
  quadratic constraints:     [2e-02, 1e+01] |                [2e-02, 1e+01]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [7e-01, 1e+00] |                [1e-01, 1e+00]

Root node relaxation
--------------------

 Iter      Objective      Feasibility        Optimality       Time 
                             error              error        (secs)
 ----      ---------      -----------        ----------      ------
    0   -7.62974e-03         0.311630           20.7306       0.001
    1       0.687604         0.265939           3.32889       0.002
    2        7.25574      5.04446e-03          0.489625       0.002
    3        9.61621      1.34356e-05       8.85160e-02       0.002
    4        10.0644      7.03796e-08       4.76010e-03       0.002
    5        10.0688      3.34970e-10       2.27094e-05       0.002
    6        10.0688      4.68514e-14       9.82956e-10       0.002
    7        10.0688      4.44089e-16       1.77636e-15       0.003

Tree search
-----------

       Nodes        Best solution   Best bound      Gap       Time 
   Expl  |  Unexpl      value         value                  (secs)
   ---------------  -------------   ----------      ---      ------
      1       0      10.0688 LEAF      10.0688      0.00%     0.004

EXIT: Optimal solution found.

HINT: Knitro spent   6.4% of solution time (0.000248 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics for MIP
------------------------
Final objective value               =  1.00688090000000052e+01
Final bound value                   =  1.00688090000000052e+01
Final optimality gap (abs / rel)    =  0.00000e+00 / 0.00000e+00 (0.00%)
# of root cutting plane rounds      =  0
# of restarts                       =  0
# of nodes processed                =  1 (0.002s)
# of strong branching evaluations   =  0 (0.000s)
# of function evaluations           =  0 (0.000s)
# of gradient evaluations           =  0 (0.000s)
# of hessian evaluations            =  0 (0.000s)
# of hessian-vector evaluations     =  0
# of subproblems processed          =  1 (0.002s)
Total program time (secs)           =  0.00379 (0.015 CPU time)
Time spent in evaluations (secs)    =  0.00000

Cuts statistics (gen / add)
---------------------------
Knapsack cuts                       =  0 / 0
Mixed-integer rounding cuts         =  0 / 0
Gomory cuts                         =  0 / 0
Flow-cover cuts                     =  0 / 0
Probing cuts                        =  0 / 0

Heuristics statistics (calls / successes / time)
------------------------------------------------
Feasibility pump                    =  0 / 0 / 0.000s
Rounding heuristic                  =  0 / 0 / 0.000s
MPEC heuristic                      =  0 / 0 / 0.000s
Local search heuristic              =  5 / 1 / 0.007s

===========================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro changing mip_method from AUTO to 1.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              1e-06
opttol                   1e-06
opttol_abs               0.001
Knitro changing mip_root_nlpalg from AUTO to 1.
Knitro changing mip_node_nlpalg from AUTO to 1.
Knitro changing mip_branchrule from AUTO to 2.
Knitro changing mip_selectrule from AUTO to 2.
Knitro changing mip_mir from AUTO to 2.
Knitro changing mip_clique from AUTO to 0.
Knitro changing mip_zerohalf from AUTO to 0.
Knitro changing mip_liftproject from AUTO to 0.
Knitro changing mip_knapsack from AUTO to 2.
Knitro changing mip_gomory from AUTO to 1.
Knitro changing mip_cut_flowcover from AUTO to 2.
Knitro changing mip_cut_probing from AUTO to 1.
Knitro changing mip_rounding from AUTO to 3.
Knitro changing mip_heuristic_strategy from AUTO to 1.
Knitro changing mip_heuristic_feaspump from AUTO to 1.
Knitro changing mip_heuristic_misqp from AUTO to 0.
Knitro changing mip_heuristic_mpec from AUTO to 1.
Knitro changing mip_heuristic_diving from AUTO to 1926.
Knitro changing mip_heuristic_fixpropagate from AUTO to 62.
Knitro changing mip_heuristic_lns from AUTO to 0.
Knitro changing mip_heuristic_localsearch from AUTO to 1.
Knitro changing mip_pseudoinit from AUTO to 1.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex MIQCQP
Objective: maximize / linear   
Number of variables:                     40 |                            40
  bounds:         lower     upper     range |     lower     upper     range
                     20         0        20 |        20         0        20
                             free     fixed |                free     fixed
                                0         0 |                   0         0
                  cont.    binary   integer |     cont.    binary   integer
                     30        10         0 |        30        10         0
Number of expressions:                  376 |                             0
Number of constraints:                   12 |                            12
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:            11         0         0 |        11         0         0
  quadratic:          0         1         0 |         0         1         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:            10        70           |        10        70          
  quadratic:          0        10        55 |         0        10        55
  total:             10        80        55 |        10        80        55

Knitro using Branch and Bound method with 8 threads.

Initial points
--------------
No initial point provided for the root node relaxation.
No primal point provided for the MIP.

Coefficient range:
  linear objective:          [3e-01, 7e+01] |                [3e-01, 7e+01]
  linear constraints:        [1e-01, 1e+00] |                [1e-01, 1e+00]
  quadratic objective:       [0e+00, 0e+00] |                [0e+00, 0e+00]
  quadratic constraints:     [2e-02, 1e+01] |                [2e-02, 1e+01]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [7e-01, 1e+00] |                [1e-01, 1e+00]

Root node relaxation
--------------------

 Iter      Objective      Feasibility        Optimality       Time 
                             error              error        (secs)
 ----      ---------      -----------        ----------      ------
    0   -7.62974e-03         0.319289           17.7501       0.001
    1        6.95074         0.270550          0.503348       0.001
    2        4.23543      4.59978e-03          0.460908       0.002
    3        8.87075      2.22045e-16          0.202509       0.002
    4        12.4708      2.22045e-16       9.57692e-02       0.002
    5        13.0077      2.22045e-16          0.175908       0.002
    6        13.3561      1.52356e-03       7.64986e-03       0.002
    7        13.3721      1.10720e-05       7.59572e-05       0.002
    8        13.3722      8.17621e-10       5.83764e-09       0.003
    9        13.3722      1.00001e-10       1.00001e-10       0.003
   10        13.3722      4.44089e-16       3.30513e-11       0.003

Tree search
-----------

       Nodes        Best solution   Best bound      Gap       Time 
   Expl  |  Unexpl      value         value                  (secs)
   ---------------  -------------   ----------      ---      ------
      1       0      13.3722 LEAF      13.3722      0.00%     0.003

EXIT: Optimal solution found.

HINT: Knitro spent   5.8% of solution time (0.000207 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics for MIP
------------------------
Final objective value               =  1.33721742807601345e+01
Final bound value                   =  1.33721742807601345e+01
Final optimality gap (abs / rel)    =  0.00000e+00 / 0.00000e+00 (0.00%)
# of root cutting plane rounds      =  0
# of restarts                       =  0
# of nodes processed                =  1 (0.003s)
# of strong branching evaluations   =  0 (0.000s)
# of function evaluations           =  0 (0.000s)
# of gradient evaluations           =  0 (0.000s)
# of hessian evaluations            =  0 (0.000s)
# of hessian-vector evaluations     =  0
# of subproblems processed          =  1 (0.003s)
Total program time (secs)           =  0.00350 (0.015 CPU time)
Time spent in evaluations (secs)    =  0.00000

Cuts statistics (gen / add)
---------------------------
Knapsack cuts                       =  0 / 0
Mixed-integer rounding cuts         =  0 / 0
Gomory cuts                         =  0 / 0
Flow-cover cuts                     =  0 / 0
Probing cuts                        =  0 / 0

Heuristics statistics (calls / successes / time)
------------------------------------------------
Feasibility pump                    =  0 / 0 / 0.000s
Rounding heuristic                  =  0 / 0 / 0.000s
MPEC heuristic                      =  0 / 0 / 0.000s
Local search heuristic              =  4 / 0 / 0.004s

===========================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro changing mip_method from AUTO to 1.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              1e-06
opttol                   1e-06
opttol_abs               0.001
Knitro changing mip_root_nlpalg from AUTO to 1.
Knitro changing mip_node_nlpalg from AUTO to 1.
Knitro changing mip_branchrule from AUTO to 2.
Knitro changing mip_selectrule from AUTO to 2.
Knitro changing mip_mir from AUTO to 2.
Knitro changing mip_clique from AUTO to 0.
Knitro changing mip_zerohalf from AUTO to 0.
Knitro changing mip_liftproject from AUTO to 0.
Knitro changing mip_knapsack from AUTO to 2.
Knitro changing mip_gomory from AUTO to 1.
Knitro changing mip_cut_flowcover from AUTO to 2.
Knitro changing mip_cut_probing from AUTO to 1.
Knitro changing mip_rounding from AUTO to 3.
Knitro changing mip_heuristic_strategy from AUTO to 1.
Knitro changing mip_heuristic_feaspump from AUTO to 1.
Knitro changing mip_heuristic_misqp from AUTO to 0.
Knitro changing mip_heuristic_mpec from AUTO to 1.
Knitro changing mip_heuristic_diving from AUTO to 1926.
Knitro changing mip_heuristic_fixpropagate from AUTO to 62.
Knitro changing mip_heuristic_lns from AUTO to 0.
Knitro changing mip_heuristic_localsearch from AUTO to 1.
Knitro changing mip_pseudoinit from AUTO to 1.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex MIQCQP
Objective: maximize / linear   
Number of variables:                     40 |                            40
  bounds:         lower     upper     range |     lower     upper     range
                     20         0        20 |        20         0        20
                             free     fixed |                free     fixed
                                0         0 |                   0         0
                  cont.    binary   integer |     cont.    binary   integer
                     30        10         0 |        30        10         0
Number of expressions:                  356 |                             0
Number of constraints:                   12 |                            12
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:            11         0         0 |        11         0         0
  quadratic:          0         1         0 |         0         1         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:            10        50           |        10        50          
  quadratic:          0        10        55 |         0        10        55
  total:             10        60        55 |        10        60        55

Knitro using Branch and Bound method with 8 threads.

Initial points
--------------
No initial point provided for the root node relaxation.
No primal point provided for the MIP.

Coefficient range:
  linear objective:          [3e-01, 7e+01] |                [3e-01, 7e+01]
  linear constraints:        [1e-01, 1e+00] |                [1e-01, 1e+00]
  quadratic objective:       [0e+00, 0e+00] |                [0e+00, 0e+00]
  quadratic constraints:     [2e-02, 1e+01] |                [2e-02, 1e+01]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [7e-01, 1e+00] |                [1e-01, 1e+00]

Root node relaxation
--------------------

 Iter      Objective      Feasibility        Optimality       Time 
                             error              error        (secs)
 ----      ---------      -----------        ----------      ------
    0       -2.14993         0.247679           2.47881       0.001
    1        16.4376         0.498363          0.419039       0.001
    2        16.7830         0.524616          0.497705       0.001
    3        14.4156      4.52455e-02          0.535123       0.002
    4        14.2755      5.27356e-16          0.614291       0.002
    5        14.3339      4.70571e-05       2.91922e-02       0.002
    6        14.3374      1.71096e-05       4.09953e-03       0.002
    7        14.3373      6.00300e-08       2.65597e-05       0.002
    8        14.3373      1.04091e-10       1.22909e-09       0.002
    9        14.3373      2.13718e-15       4.25346e-09       0.002

Tree search
-----------

       Nodes        Best solution   Best bound      Gap       Time 
   Expl  |  Unexpl      value         value                  (secs)
   ---------------  -------------   ----------      ---      ------
      1       2      14.3245 FCRD      14.3373      0.09%     0.005
      4       3      14.3245 MPEC      14.3373      0.09%     0.012
     21       0      14.3245           14.3260      0.01%     0.041

EXIT: Optimal solution found.

Final Statistics for MIP
------------------------
Final objective value               =  1.43245316811231191e+01
Final bound value                   =  1.43259641331530609e+01
Final optimality gap (abs / rel)    =  1.43245e-03 / 9.99999e-05 (0.01%)
# of root cutting plane rounds      =  1
# of restarts                       =  0
# of nodes processed                =  21 (0.031s)
# of strong branching evaluations   =  0 (0.000s)
# of function evaluations           =  0 (0.000s)
# of gradient evaluations           =  0 (0.000s)
# of hessian evaluations            =  0 (0.000s)
# of hessian-vector evaluations     =  0
# of subproblems processed          =  28 (0.043s)
Total program time (secs)           =  0.04072 (0.153 CPU time)
Time spent in evaluations (secs)    =  0.00000

Cuts statistics (gen / add)
---------------------------
Knapsack cuts                       =  0 / 0
Mixed-integer rounding cuts         =  0 / 0
Gomory cuts                         =  0 / 0
Flow-cover cuts                     =  0 / 0
Probing cuts                        =  0 / 0

Heuristics statistics (calls / successes / time)
------------------------------------------------
Feasibility pump                    =  1 / 0 / 0.001s
Rounding heuristic                  =  1 / 1 / 0.002s
MPEC heuristic                      =  1 / 1 / 0.003s
Local search heuristic              =  7 / 2 / 0.057s

===========================================================================
draw_comparison(
    [initial] + rebalanced,
    [f"Initial, Return {initial.expected_return:.2f}"]
    + [
        f"Variable cost {v}, Return {s.expected_return:.2f}"
        for v, s in zip(variable_costs, rebalanced)
    ],
    nasdaq.names,
    "Rebalancing an equally weighted portfolio",
)

When transaction costs are taken into account, assets with negative expected returns are removed from the portfolio, because the costs associated with trading prevent extensive diversification, making it impractical to include assets that do not contribute positively to the portfolio’s overall return. The lower the variable cost, the further the portfolio can move from its initial allocation.

Optimizing portfolios with a large number of assets

In this section, we test the baseline model with a large number of assets. Despite the increased complexity, we expect to solve the optimization problem successfully using Knitro. The data is taken from the top 100 companies listed on the Australian Securities Exchange (ASX).

asx = load_market("asx.csv", "Top 100 of the Australian Securities Exchange")
large = [solve(asx, markowitz, desired_return=r) for r in [5.0, 7.5, 10.0, 15.0]]
Show the full outputHide the full output
=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                    100 |                           100
  bounds:         lower     upper     range |     lower     upper     range
                      0         0       100 |         0         0       100
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                25353 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0       200           |         0       200          
  quadratic:        100         0      5050 |       100         0      5050
  total:            100       200      5050 |       100       200      5050
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-02, 2e+01] |                [3e-02, 2e+01]
  quadratic objective:       [8e-04, 2e+01] |                [8e-04, 2e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 5e+00] |                [1e+00, 5e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    3.247304e+00   3.37e+00
      10    2.108031e-01   4.44e-16   5.25e-07   2.31e-03      0.01

EXIT: Optimal solution found.

HINT: Knitro spent   5.2% of solution time (0.000531 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   2.10803066843171e-01
Final feasibility error (abs / rel) =   4.44e-16 / 9.64e-18
Final optimality error  (abs / rel) =   5.25e-07 / 4.10e-07
# of iterations                     =         10 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.01018 (     0.011 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                    100 |                           100
  bounds:         lower     upper     range |     lower     upper     range
                      0         0       100 |         0         0       100
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                25353 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0       200           |         0       200          
  quadratic:        100         0      5050 |       100         0      5050
  total:            100       200      5050 |       100       200      5050
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-02, 2e+01] |                [3e-02, 2e+01]
  quadratic objective:       [8e-04, 2e+01] |                [8e-04, 2e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 8e+00] |                [1e+00, 8e+00]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    3.501597e+00   4.85e+00
      10    2.412573e-01   2.22e-16   1.58e-06   1.35e-03      0.01
      11    2.412571e-01   0.00e+00   1.45e-10   1.21e-04      0.01

EXIT: Optimal solution found.

HINT: Knitro spent   4.2% of solution time (0.000512 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   2.41257143933544e-01
Final feasibility error (abs / rel) =   0.00e+00 / 0.00e+00
Final optimality error  (abs / rel) =   1.45e-10 / 1.27e-10
# of iterations                     =         11 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.01211 (     0.013 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                    100 |                           100
  bounds:         lower     upper     range |     lower     upper     range
                      0         0       100 |         0         0       100
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                25353 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0       200           |         0       200          
  quadratic:        100         0      5050 |       100         0      5050
  total:            100       200      5050 |       100       200      5050
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-02, 2e+01] |                [3e-02, 2e+01]
  quadratic objective:       [8e-04, 2e+01] |                [8e-04, 2e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 1e+01] |                [1e+00, 1e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    2.653082e+00   5.37e+00
       9    3.220800e-01   6.66e-16   1.10e-07   9.30e-04      0.01

EXIT: Optimal solution found.

HINT: Knitro spent   4.9% of solution time (0.000525 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   3.22080025315401e-01
Final feasibility error (abs / rel) =   6.66e-16 / 1.45e-17
Final optimality error  (abs / rel) =   1.10e-07 / 9.24e-08
# of iterations                     =          9 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.01065 (     0.011 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================


=======================================
          Commercial License
         Artelys Knitro 16.0.0
=======================================

Knitro using 1 thread.
No start point provided -- Knitro computing one.

Knitro presolve eliminated 0 variables (0%) and 0 constraints (0%) in 0.00s.

concurrent_evals         0
feastol                  1e-06
feastol_abs              0.001
opttol                   1e-06
opttol_abs               0.001
Knitro running advanced initialization strategy specified by initpt_strategy.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex QP
Objective: minimize / quadratic
Number of variables:                    100 |                           100
  bounds:         lower     upper     range |     lower     upper     range
                      0         0       100 |         0         0       100
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of expressions:                25353 |                             0
Number of constraints:                    2 |                             2
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:             1         1         0 |         1         1         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0       200           |         0       200          
  quadratic:        100         0      5050 |       100         0      5050
  total:            100       200      5050 |       100       200      5050
Coefficient range:
  linear objective:          [0e+00, 0e+00] |                [0e+00, 0e+00]
  linear constraints:        [3e-02, 2e+01] |                [3e-02, 2e+01]
  quadratic objective:       [8e-04, 2e+01] |                [8e-04, 2e+01]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 1e+00] |                [1e+00, 1e+00]
  constraint bounds:         [1e+00, 2e+01] |                [1e+00, 2e+01]

Knitro using the Interior-Point/Barrier Direct algorithm.

    Iter       Objective  FeasError   OptError   ||Step||      Time 
--------  --------------  ---------  ---------  ---------  --------
       0    2.367075e+00   5.36e+00
       7    7.590161e-01   4.44e-16   5.27e-11   3.16e-05      0.01

EXIT: Optimal solution found.

HINT: Knitro spent   5.5% of solution time (0.000522 secs) checking model
      convexity. To skip the automatic convexity checker for QPs and QCQPs,
      explicity set the user option convex=0 or convex=1.

Final Statistics
----------------
Final objective value               =   7.59016080822773e-01
Final feasibility error (abs / rel) =   4.44e-16 / 9.48e-18
Final optimality error  (abs / rel) =   5.27e-11 / 2.36e-11
# of iterations                     =          7 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       0.00960 (     0.010 CPU time)
Time spent in evaluations (secs)    =       0.00000

================================================================================
draw_comparison(
    large,
    [f"Return {r}, Risk {s.risk:.2f}" for r, s in zip([5.0, 7.5, 10.0, 15.0], large)],
    asx.names,
    "Selected assets among the 100 available",
    threshold=0.05,
)

As the figure shows, only a handful of assets are selected to minimize the risk for these desired returns, and the convex QP is still solved to global optimality.

 

Solved with Artelys Knitro · artelys.com