CPLEX Objective 函数中的线性项和二次项
Linear and Quadratic terms in CPLEX Objective Function
我想最小化一个相当简单的 objective 函数,但我在从 Python API 到 CPLEX[=19 的正确调用中遇到了问题=]
我查看了如何使用 set_quadratic
和 set_quadratic_coefficients
here,但这并没有解决我的问题。
我的 objective 函数有一组线性变量和一组二次变量
varCoefs = [1]*(numB + numQ)
varLower = [0]*(numB + numQ)
varNames = [(x,"b%s"%x) for x in range( numB )]
varNames += [(len(varNames) + x,"q%s"%x) for x in range( numQ )]
varCoefs += [10]*len(deltas)
varLower += [1]*len(deltas)
varNames += [(len(varNames) + x,"delta%s"%x) for x in range( len(deltas) )]
varCoefs += [0]*len(target.v)
varLower += [0]*len(target.v)
sContent = [(len(varNames) + x,"s%s"%x) for x in range( len(target.v) )]
varNames += sContent
varCoefs += [-1]
varLower += [0]
varNames += [(len(varNames),'mu')]
problem.variables.add(obj = varCoefs, lb = varLower)
problem.variables.set_names(varNames)
# problem.objective.set_quadratic_coefficients([[['s%s' % x], [1]] for x in range( len(target.v) )])
problem.objective.set_quadratic(
[cplex.SparsePair(ind=[sContent[x][0]], val=[1]) for x in range( len(target.v) )]
)
一切正常,直到最后一次调用添加二次项。此时 CPLEX 抛出以下错误 CPLEX Error 1226: Array entry 13919 not ascending.
两次,忽略命令,并且 Python 代码继续。
我查阅了 error,但这似乎也没有帮助我。
我确实尝试重写上面的代码以首先按名称和下限添加变量...然后调用 set_linear
和 set_quadratic
,但这也无济于事。
我在这里错过了什么?
如果您使用可分离的二次 objective 函数调用 set_quadratic
,则它对应于 CPXXcopyqpsep. If you're calling set_quadratic
with a quadratic objective function that is inseparable, it corresponds to CPXXcopyquad。我同意你得到的错误不是特别有用,但如果你知道它在可调用 C 库中的来源,它会更有意义。
话虽如此,这是一个完整的示例,使用您的代码段和一些虚拟输入:
import cplex
class MockTarget(object):
pass
# Dummy data for testing
numB = 3
numQ = 3
deltas = [0.1, 0.1, 0.1]
problem = cplex.Cplex()
target = MockTarget()
target.v = [1, 2, 3]
# Build the problem
varCoefs = [1]*(numB + numQ)
varLower = [0]*(numB + numQ)
varNames = [(x,"b%s"%x) for x in range( numB )]
varNames += [(len(varNames) + x,"q%s"%x) for x in range( numQ )]
varCoefs += [10]*len(deltas)
varLower += [1]*len(deltas)
varNames += [(len(varNames) + x,"delta%s"%x) for x in range( len(deltas) )]
varCoefs += [0]*len(target.v)
varLower += [0]*len(target.v)
sContent = [(len(varNames) + x,"s%s"%x) for x in range( len(target.v) )]
varNames += sContent
varCoefs += [-1]
varLower += [0]
varNames += [(len(varNames),'mu')]
problem.variables.add(obj = varCoefs, lb = varLower)
problem.variables.set_names(varNames)
# Print without quadratic terms so you can see the progression.
problem.write('test1.lp')
# Separable Q
qsepvec = []
for tpl in varNames:
if tpl in sContent:
qsepvec.append(1.0)
else:
qsepvec.append(0.0)
print qsepvec
problem.objective.set_quadratic(qsepvec)
problem.write('test2.lp')
# Inseparable Q (overwrites previous Q)
qmat = []
for tpl in varNames:
if tpl in sContent:
sp = cplex.SparsePair(ind=[tpl[0]], val=[1.0])
qmat.append(sp)
else:
sp = cplex.SparsePair(ind=[], val=[])
qmat.append(sp)
print qmat
problem.objective.set_quadratic(qmat)
problem.write('test3.lp')
我已经用长篇形式写了出来,而不是使用列表理解来让它更清楚一点。 LP文件的内容如下:
test1.lp:
\ENCODING=ISO-8859-1
\Problem name:
Minimize
obj: b0 + b1 + b2 + q0 + q1 + q2 + 10 delta0 + 10 delta1 + 10 delta2 + 0 s0
+ 0 s1 + 0 s2 - mu
Bounds
delta0 >= 1
delta1 >= 1
delta2 >= 1
End
test2.lp
\ENCODING=ISO-8859-1
\Problem name:
Minimize
obj: b0 + b1 + b2 + q0 + q1 + q2 + 10 delta0 + 10 delta1 + 10 delta2 + 0 s0
+ 0 s1 + 0 s2 - mu + [ s0 ^2 + s1 ^2 + s2 ^2 ] / 2
Bounds
delta0 >= 1
delta1 >= 1
delta2 >= 1
End
test3.lp
\ENCODING=ISO-8859-1
\Problem name:
Minimize
obj: b0 + b1 + b2 + q0 + q1 + q2 + 10 delta0 + 10 delta1 + 10 delta2 + 0 s0
+ 0 s1 + 0 s2 - mu + [ s0 ^2 + s1 ^2 + s2 ^2 ] / 2
Bounds
delta0 >= 1
delta1 >= 1
delta2 >= 1
End
可以看到test2.lp和test3.lp是一样的(后者覆盖了前者,但是做的是一样的)。希望这能让它更容易理解。一般来说,使用这种打印出 LP 的技术来解决非常简单的问题,是一种更有用的调试技术。
您还应该查看 CPLEX 附带的 python 示例。例如qpex1.py, miqpex1.py, indefqpex1.py.
我解决了这个问题,方法是先添加二次项,设置它们的系数,然后在单独的调用中添加线性项,见下文。
problem.objective.set_sense(problem.objective.sense.minimize)
varLower = [0]*len(target.v)
varNames = ["s%s"%x for x in range( len(target.v) )]
problem.variables.add(names=varNames, lb=varLower)
problem.objective.set_quadratic(
[[[x],[1]] for x in range( len(target.v) )]
)
varCoefs = [-1]
varLower = [0]
varNames = ['mu']
varCoefs += [1]*(numB + numQ)
varLower += [0]*(numB + numQ)
varNames += ["b%s"%x for x in range( numB )]
varNames += ["q%s"%x for x in range( numQ )]
varCoefs += [10]*len(deltas)
varLower += [1]*len(deltas)
varNames += ["delta%s"%x for x in range( len(deltas) )]
problem.variables.add(names=varNames, lb=varLower, obj=varCoefs)
不过,我还是想知道为什么它会这样工作,而不是相反。
我想最小化一个相当简单的 objective 函数,但我在从 Python API 到 CPLEX[=19 的正确调用中遇到了问题=]
我查看了如何使用 set_quadratic
和 set_quadratic_coefficients
here,但这并没有解决我的问题。
我的 objective 函数有一组线性变量和一组二次变量
varCoefs = [1]*(numB + numQ)
varLower = [0]*(numB + numQ)
varNames = [(x,"b%s"%x) for x in range( numB )]
varNames += [(len(varNames) + x,"q%s"%x) for x in range( numQ )]
varCoefs += [10]*len(deltas)
varLower += [1]*len(deltas)
varNames += [(len(varNames) + x,"delta%s"%x) for x in range( len(deltas) )]
varCoefs += [0]*len(target.v)
varLower += [0]*len(target.v)
sContent = [(len(varNames) + x,"s%s"%x) for x in range( len(target.v) )]
varNames += sContent
varCoefs += [-1]
varLower += [0]
varNames += [(len(varNames),'mu')]
problem.variables.add(obj = varCoefs, lb = varLower)
problem.variables.set_names(varNames)
# problem.objective.set_quadratic_coefficients([[['s%s' % x], [1]] for x in range( len(target.v) )])
problem.objective.set_quadratic(
[cplex.SparsePair(ind=[sContent[x][0]], val=[1]) for x in range( len(target.v) )]
)
一切正常,直到最后一次调用添加二次项。此时 CPLEX 抛出以下错误 CPLEX Error 1226: Array entry 13919 not ascending.
两次,忽略命令,并且 Python 代码继续。
我查阅了 error,但这似乎也没有帮助我。
我确实尝试重写上面的代码以首先按名称和下限添加变量...然后调用 set_linear
和 set_quadratic
,但这也无济于事。
我在这里错过了什么?
如果您使用可分离的二次 objective 函数调用 set_quadratic
,则它对应于 CPXXcopyqpsep. If you're calling set_quadratic
with a quadratic objective function that is inseparable, it corresponds to CPXXcopyquad。我同意你得到的错误不是特别有用,但如果你知道它在可调用 C 库中的来源,它会更有意义。
话虽如此,这是一个完整的示例,使用您的代码段和一些虚拟输入:
import cplex
class MockTarget(object):
pass
# Dummy data for testing
numB = 3
numQ = 3
deltas = [0.1, 0.1, 0.1]
problem = cplex.Cplex()
target = MockTarget()
target.v = [1, 2, 3]
# Build the problem
varCoefs = [1]*(numB + numQ)
varLower = [0]*(numB + numQ)
varNames = [(x,"b%s"%x) for x in range( numB )]
varNames += [(len(varNames) + x,"q%s"%x) for x in range( numQ )]
varCoefs += [10]*len(deltas)
varLower += [1]*len(deltas)
varNames += [(len(varNames) + x,"delta%s"%x) for x in range( len(deltas) )]
varCoefs += [0]*len(target.v)
varLower += [0]*len(target.v)
sContent = [(len(varNames) + x,"s%s"%x) for x in range( len(target.v) )]
varNames += sContent
varCoefs += [-1]
varLower += [0]
varNames += [(len(varNames),'mu')]
problem.variables.add(obj = varCoefs, lb = varLower)
problem.variables.set_names(varNames)
# Print without quadratic terms so you can see the progression.
problem.write('test1.lp')
# Separable Q
qsepvec = []
for tpl in varNames:
if tpl in sContent:
qsepvec.append(1.0)
else:
qsepvec.append(0.0)
print qsepvec
problem.objective.set_quadratic(qsepvec)
problem.write('test2.lp')
# Inseparable Q (overwrites previous Q)
qmat = []
for tpl in varNames:
if tpl in sContent:
sp = cplex.SparsePair(ind=[tpl[0]], val=[1.0])
qmat.append(sp)
else:
sp = cplex.SparsePair(ind=[], val=[])
qmat.append(sp)
print qmat
problem.objective.set_quadratic(qmat)
problem.write('test3.lp')
我已经用长篇形式写了出来,而不是使用列表理解来让它更清楚一点。 LP文件的内容如下:
test1.lp:
\ENCODING=ISO-8859-1
\Problem name:
Minimize
obj: b0 + b1 + b2 + q0 + q1 + q2 + 10 delta0 + 10 delta1 + 10 delta2 + 0 s0
+ 0 s1 + 0 s2 - mu
Bounds
delta0 >= 1
delta1 >= 1
delta2 >= 1
End
test2.lp
\ENCODING=ISO-8859-1
\Problem name:
Minimize
obj: b0 + b1 + b2 + q0 + q1 + q2 + 10 delta0 + 10 delta1 + 10 delta2 + 0 s0
+ 0 s1 + 0 s2 - mu + [ s0 ^2 + s1 ^2 + s2 ^2 ] / 2
Bounds
delta0 >= 1
delta1 >= 1
delta2 >= 1
End
test3.lp
\ENCODING=ISO-8859-1
\Problem name:
Minimize
obj: b0 + b1 + b2 + q0 + q1 + q2 + 10 delta0 + 10 delta1 + 10 delta2 + 0 s0
+ 0 s1 + 0 s2 - mu + [ s0 ^2 + s1 ^2 + s2 ^2 ] / 2
Bounds
delta0 >= 1
delta1 >= 1
delta2 >= 1
End
可以看到test2.lp和test3.lp是一样的(后者覆盖了前者,但是做的是一样的)。希望这能让它更容易理解。一般来说,使用这种打印出 LP 的技术来解决非常简单的问题,是一种更有用的调试技术。
您还应该查看 CPLEX 附带的 python 示例。例如qpex1.py, miqpex1.py, indefqpex1.py.
我解决了这个问题,方法是先添加二次项,设置它们的系数,然后在单独的调用中添加线性项,见下文。
problem.objective.set_sense(problem.objective.sense.minimize)
varLower = [0]*len(target.v)
varNames = ["s%s"%x for x in range( len(target.v) )]
problem.variables.add(names=varNames, lb=varLower)
problem.objective.set_quadratic(
[[[x],[1]] for x in range( len(target.v) )]
)
varCoefs = [-1]
varLower = [0]
varNames = ['mu']
varCoefs += [1]*(numB + numQ)
varLower += [0]*(numB + numQ)
varNames += ["b%s"%x for x in range( numB )]
varNames += ["q%s"%x for x in range( numQ )]
varCoefs += [10]*len(deltas)
varLower += [1]*len(deltas)
varNames += ["delta%s"%x for x in range( len(deltas) )]
problem.variables.add(names=varNames, lb=varLower, obj=varCoefs)
不过,我还是想知道为什么它会这样工作,而不是相反。