cvxpyライブラリの表記が古くて困ったこと

絶賛cvxpyを用いて最適化問題を解く練習をしている中で、参考書のcvxpyに関する記述が古くて困った話

何が困った

変なエラーが出る。実行したコードとエラーの内容は以下。

import numpy as np
import numpy.linalg as lin
import cvxpy as cvx

Mu=np.array([1.0,3.0,1.5,6.0,4.5])#期待値
Std=np.array([5.0,10.0,7.5,15.0,11.0]) #分散
CorrMatrix = np.array([[1.00, 0.25, 0.18, 0.10, 0.25],
                       [0.25, 1.00, 0.36, 0.20, 0.20],
                       [0.18, 0.36, 1.00, 0.25, 0.36],
                       [0.10, 0.20, 0.25, 1.00, 0.45],
                       [0.25, 0.20, 0.36, 0.45, 1.00]])

Sigma=np.diag(Std).dot(CorrMatrix).dot(np.diag(Std))#分散共分散行列
iota=np.ones(Mu.shape)
inv_Sigma=lin.inv(Sigma)

#空売り制約下での分散最小化問題の設定
Weight=cvx.Variable(Mu.shape[0])
Target_Return=cvx.Parameter(sign='positive')
Risk_Variance=cvx.quad_form(Weight,Sigma)
constraints=[Weight.T*Mu == Target_Return,
             cvx.sum_entries(Weight)==1.0,
             Weight>=0.0]
Opt_Portfolio=cvx.Problem(cvx.Minimize(Risk_Variance),constraints)

V_Target=np.linspace(Mu.min(),Mu.max(),num=250)
V_Risk=np.zeros(V_Target)
V_Weight=np.zeros((V_Target.shape[0],Mu.shape[0]))

for idx,Target_Return.value in enumerate(V_Target):
    Opt_Portfolio.solve()
    V_Weight[idx,:]
    V_Risk[idx]=np.sqrt(Risk_Variance.value)
  File "pyfin_bond_yield_curve.py", line 19, in <module>
    Target_Return = cvx.Parameter(sign='positive')
  File "/Users/ikamihiroaki/.pyenv/versions/anaconda3-5.3.1/lib/python3.7/site-packages/cvxpy/expressions/constants/parameter.py", line 41, in __init__
    super(Parameter, self).__init__(shape, value, **kwargs)
TypeError: __init__() got an unexpected keyword argument 'sign'

対応1

TypeErrorの内容を見るに、恐らく引数に入れてるsignってやつが古いバージョンの引数で現在は使われてないのではないかと考えた。

とりあえず、19行目を以下のように変更。

before

Target_Return=cvx.Parameter(sign='positive')

after

Target_Return=cvx.Parameter

結果

エラーが返ってきた。内容は以下。

Traceback (most recent call last):
  File "pyfin_mvf_example2.py", line 24, in <module>
    constraints=[Weight.T*Mu == Target_Return,
  File "/Users/ikamihiroaki/.pyenv/versions/anaconda3-5.3.1/lib/python3.7/site-packages/cvxpy/expressions/expression.py", line 45, in cast_op
    other = self.cast_to_const(other)
  File "/Users/ikamihiroaki/.pyenv/versions/anaconda3-5.3.1/lib/python3.7/site-packages/cvxpy/expressions/expression.py", line 446, in cast_to_const
    return expr if isinstance(expr, Expression) else cvxtypes.constant()(expr)
  File "/Users/ikamihiroaki/.pyenv/versions/anaconda3-5.3.1/lib/python3.7/site-packages/cvxpy/expressions/constants/constant.py", line 44, in __init__
    self._value = intf.DEFAULT_INTF.const_to_matrix(value)
  File "/Users/ikamihiroaki/.pyenv/versions/anaconda3-5.3.1/lib/python3.7/site-packages/cvxpy/interface/numpy_interface/ndarray_interface.py", line 50, in const_to_matrix
    return result.astype(numpy.float64)
TypeError: float() argument must be a string or a number, not 'ABCMeta'

対応2

エラーの内容を見るに、データの型がおかしいみたい。

なので、24行目の変数Weight,Mu,Target_Returnのデータの型を調べてみた。

  • 実行コード
print (type(Weight))
print (type(Mu))
print (type(Target_Return))
  • 結果
<class 'cvxpy.expressions.variable.Variable'>
<class 'numpy.ndarray'>
<class 'abc.ABCMeta'>

あやしい、、、結果の3行目があやしい、、、エラーに出てるワードじゃん、、

というか3行目ってさっきのパラメータ入れるところじゃん

ということで、ググった。

その結果、最近のcvxpyでは

Target_Return=cvx.Parameter(sign='positive')

ではなく

Target_Return=cvx.Parameter(nonneg=True)

と表記するらしい

これで実行すると、24行目のエラーは解消!次に進む!

でもその次の25行目ですぐエラー出た

  File "pyfin_mvf_example2.py", line 25, in <module>
    cvx.sum_entries(Weight)==1.0,
AttributeError: module 'cvxpy' has no attribute 'sum_entries'

対応3

sum_entriesなんて関数?はないみたい。時代はsum

before

             cvx.sum_entries(Weight)==1.0,

after

             cvx.sum(Weight)==1.0,

これで解決!

まとめ

結局何が困ったかというと

  1. cvxpyの関数Parameterの引数にsignは使われてなかった sign='positive'ではなくnonneg=True

  2. cvxpyの関数sum_entriesは使われてなかった cvxpy.sum_entriesではなくcvxpy.sum