如何在Python PuLP中实现绝对差之和约束?
解决方案
线性规划为线性模型,无法直接支持绝对值运算,要实现绝对差之和的约束,可通过引入非负辅助变量的标准方案实现,具体修改如下:
- 为每个非现金资产新增非负辅助变量,用于存储对应权重差的绝对值
- 为每个辅助变量添加双向约束,保证其取值等于权重差的绝对值
- 替换原有
nearer constraint为辅助变量求和的约束
修改后完整代码如下:
import pandas as pd import pulp # initialize data nav = 1000 data = [['A', 0.2], ['B', 0.4], ['C', 0.1], ['D', 0.3], ['cash', 0.0]] # create the pandas DataFrame df = pd.DataFrame(data, columns=['asset', 'w_star']) df['prccd'] = [17, 21, 119, 49, None] df['q_tilde'] = [11, 19, 0, 6, None] df['val'] = df.prccd * df.q_tilde df.loc[df.asset == 'cash', 'val'] = nav - sum(df.loc[~df.val.isna(), 'val']) df['w_act'] = df.val / sum(df.val) df['diff_orig'] = abs(df.w_star - df.w_act) df = df.set_index('asset') # manipulate cash dfnc = df[df.index != 'cash'] # create variables and model dq = pulp.LpVariable.dicts("dq", dfnc.index, cat='Integer', lowBound=0) # 新增绝对值辅助变量 abs_diff = pulp.LpVariable.dicts("abs_diff", dfnc.index, lowBound=0) mod = pulp.LpProblem("CashReduction", pulp.LpMinimize) # objective function mod += nav - sum([dq[i] * dfnc.loc[i, 'prccd'] + dfnc.loc[i, 'q_tilde'] * dfnc.loc[i, 'prccd'] for i in dfnc.index]) # lower bounds: for i in dfnc.index: mod += dq[i] >= 0 # budget constraint mod += sum([dq[i] * dfnc.loc[i, 'prccd'] for i in dfnc.index]) <= df.loc['cash', 'val'] # 新增辅助变量约束,保证abs_diff[i]等于权重差的绝对值 for i in dfnc.index: w_new = (dq[i] * dfnc.loc[i, 'prccd'] + dfnc.loc[i, 'q_tilde'] * dfnc.loc[i, 'prccd']) / nav diff = dfnc.loc[i, 'w_star'] - w_new mod += abs_diff[i] >= diff mod += abs_diff[i] >= -diff # 替换后的nearer constraint:绝对差之和小于阈值 mod += sum([abs_diff[i] for i in dfnc.index]) <= sum(df['diff_orig']) # individual diff cannot be bigger than 3% for i in dfnc.index: mod += (dfnc.loc[i, 'w_star'] - (dq[i] * dfnc.loc[i, 'prccd'] + dfnc.loc[i, 'q_tilde'] * dfnc.loc[i, 'prccd']) / nav) <= 0.03 for i in dfnc.index: mod += (dfnc.loc[i, 'w_star'] - (dq[i] * dfnc.loc[i, 'prccd'] + dfnc.loc[i, 'q_tilde'] * dfnc.loc[i, 'prccd']) / nav) >= -0.03 # solve model mod.solve() # output solution for i in dfnc.index: print(i, dq[i].value())
内容的提问来源于stack exchange,提问作者quark99
相关产品推荐
相关产品推荐

