Проблема вашей модели в том, что max x'Qx
невыпуклый. Поскольку у нас есть двоичные переменные x
, мы можем использовать трюк.
Определить
y(i,j) = x(i)*x(j)
в качестве дополнительной двоичной переменной. Тогда мы можем написать
sum((i,j), x(i)*Q(i,j)*x(j))
как
sum((i,j), y(i,j)*Q(i,j))
Бинарное умножение y(i,j) = x(i)*x(j)
может быть линеаризовано как:
y(i,j) <= x(i)
y(i,j) <= x(j)
y(i,j) >= x(i)+x(j)-1
С этой переформулировкой мы имеем полностьюлинейная модель. Это MIP, поскольку у нас есть двоичные переменные.
Мы можем сделать это в CVXPY следующим образом:
import numpy as np
import cvxpy as cp
# Generate a random non-trivial quadratic program.
n = 10 # number of options
np.random.seed(1)
mu = np.random.randn(n) # expected means
var_covar = np.random.randn(n,n) # variance-covariance matrix
var_covar = var_covar.T.dot(var_covar) # cont'd
bench_cov = np.random.randn(n) # n-length vector of cov(benchmark, returns)
lamd = 0.01 # risk tolerance
e = np.ones((1,n))
x = cp.Variable((n,1), "x", boolean=True)
y = cp.Variable((n,n), "y", boolean=True)
prob = cp.Problem(cp.Maximize(mu.T@x + lamd * (cp.sum(cp.multiply(y,var_covar)) -2*bench_cov.T@x) ),
[y <= x@e, y <= (x@e).T, y >= x@e + (x@e).T - e.T@e, cp.sum(x)==4 ])
prob.solve(solver=cp.ECOS_BB)
print("status",prob.status)
print("obj",prob.value)
print("x",x.value)
Это дает результат:
status optimal
obj 4.765120794509871
x [[1.00000000e+00]
[3.52931931e-10]
[3.80644178e-10]
[2.53300872e-10]
[9.99999999e-01]
[1.79871537e-10]
[1.00000000e+00]
[3.46298454e-10]
[9.99999999e-01]
[1.00172269e-09]]
Примечания:
- Рекомендуется использовать лучший MIP-решатель, чем ECOS_BB. Для этой модели она дает правильные результаты, но она в некотором роде решает проблемы с игрушками и, как известно, создает проблемы для более сложных наборов данных.
- Я не понимаю экономику модели. Мы максимизируем риск здесь. Возможно, будет нецелесообразно основывать свои инвестиционные решения на результатах этой модели.
- Обратите внимание, что некоторые высокопроизводительные решатели (такие как Cplex и Gurobi) выполняют эту переформулировку автоматически. Однако CVXPY не позволит вам передать невыпуклую модель решателю.