pythoncplexconvex-optimizationquadratic-programmingdocplex

Error Passing a Linear Expression to a Quadratic Form in DocPlex


I have a cplex/docplex model with an "active risk" term. I believe I'm messing up the mix of Pandas and DocPlex, but I'm worried I'm trying to do something impossible.

The term should just be the quadratic form (Target-Optimal) \Sigma (Target-Optimal).

from docplex.mp.advmodel import AdvModel
from numpy import identity
from pandas import Series, DataFrame

model = AdvModel()
assets = ['AAA', 'BBB', 'CCC', 'DDD']
optimal = Series(1 / 4, assets)
covariances = DataFrame(identity(4) * 0.10, index=assets, columns=assets)

target = Series(model.continuous_var_list(assets, name='Target', lb=0, ub=1), index=assets)

active_risk = model.quad_matrix_sum(covariances, target - optimal) / 2
print(active_risk)

giving the error

AttributeError: 'LinearExpr' object has no attribute '_index'

Interestingly, something like the following works. So I could shift all the variables to be the difference but I'm trying to avoid this if possible as that would make other complicated terms in the optimization less clear.

# lb, ub are complicated now
difference = Series(model.continuous_var_list(assets, name='Target', lb=lb, ub=ub), index=assets)
model.quad_matrix_sum(covariances, difference) / 2

Solution

  • The problem comes from the conjonction of two events:

    1. Model.quad_sum expects variables, not expressions, as indicated in the documentation
    2. For performance reasons, class AdvModel disables type-checking of arguments. But this can be re-enabled.

    Re-enabling type-checking for AdvModel (e.g. calling AdvModel(checker='on') yields the right error message:

    docplex.mp.utils.DOcplexException: Expecting an iterable returning variables, docplex.mp.LinearExpr(Target_AAA-0.250) was passed at position 0
    

    To compute a quadratic form over expressions, use Model.sum() as in:

    #active_risk = model.quad_matrix_sum(covariances, target - optimal) / 2
    size = len(assets)
    active_risk = model.sum(covariances.iloc[i,j] * (target[i] - optimal[i]) * (target[j] - optimal[j])
                            for i in range(size) for j in range(size))
    
    print(active_risk)
    

    which yields

    0.100Target_AAA^2+0.100Target_BBB^2+0.100Target_CCC^2+0.100Target_DDD^2-0.050Target_AAA-0.050Target_BBB-0.050Target_CCC-0.050Target_DDD+0.025