如何使用scipy.optimize.linprog获取整数解?

时间:2016-08-23 12:24:01

标签: python scipy

当我解决线性规划问题时,如下面的公式所示,我希望x all的结果为int type

考虑以下问题:

最小化:f = -1*x[0] + 4*x[1]

受制于:

-3*x[0] + 1*x[1] <= 6    
1*x[0] + 2*x[1] <= 4    
x[1] >= -3

其中:-inf <= x[0] <= inf

接下来是python编码器

>>> c = [-1, 4]
>>> A = [[-3, 1], [1, 2]]
>>> b = [6, 4]
>>> x0_bounds = (None, None)
>>> x1_bounds = (-3, None)
>>> res = linprog(c, A_ub=A, b_ub=b, bounds=(x0_bounds, x1_bounds),
...               options={"disp": True})
>>> print(res)
Optimization terminated successfully.
Current function value: -11.428571
Iterations: 2
status: 0
success: True
fun: -11.428571428571429
x: array([-1.14285714,  2.57142857])
message: 'Optimization terminated successfully.'
nit: 2

2 个答案:

答案 0 :(得分:7)

来自docs

  

方法:str,可选的求解器类型。此时只有'单纯'才是   支撑。

Simplex无法处理完整性约束,因此您无法使用scipy.optimize.linprog解决整数编程问题。您可以尝试其他库,例如PuLPPyomoCVXOPT

答案 1 :(得分:0)

Following 是一个 Python 模块,它包含一个函数 LPmi(.) 来解决混合整数线性程序。它在 scipy.optimize.linprog(.) 之上使用了分支定界算法。这是这个响应者的创造;任何人都可以自由使用或修改它。它还包括一个 test(.) 函数形式的示例。

import numpy as np
from scipy.optimize import linprog
import copy

class LP:
    minimise = True
    c = None
    A_ub = None
    b_ub = None
    A_eq = None
    b_eq = None
    bounds = None
    method = ""
    fun = 0.
    x = None
    success = False
    
    def __init__(self,c,minimise=True,Aub=None, bub=None, Aeq=None, beq=None,
                 bounds=None,method="revised simplex"):
        self.minimise = minimise
        if self.minimise:
            self.c = c
        else:
            self.c = -1 * c
        self.A_ub = Aub
        self.b_ub = bub
        self.A_eq = Aeq
        self.b_eq = beq
        self.bounds = bounds
        self.method = method
        
    def cal(self): 
        res = linprog(self.c,A_ub=self.A_ub, b_ub=self.b_ub,
                      A_eq=self.A_eq, b_eq=self.b_eq,bounds=self.bounds,
                      method=self.method)                      
        if res["success"] == False:
            return res["message"]
        else:
            self.success = True
        
            if self.minimise:
                self.fun = res["fun"]
            else:
                self.fun = -1 * res["fun"]

            self.x = res["x"]
            
    def get_res(self):
        return (self.x, self.fun, self.success)

class LP_Data:
    
    minimise = True
    c = None
    A_ub = None
    b_ub = None
    A_eq = None
    b_eq = None
    bounds = None
    method = ""
    
    def __init__(self,c,minimise=True,Aub=None, bub=None, Aeq=None, beq=None,
                 bounds=None,method="revised simplex"):
        self.minimise = minimise
        if self.minimise:
            self.c = c
        else:
            self.c = -1 * c
        self.A_ub = Aub
        self.b_ub = bub
        self.A_eq = Aeq
        self.b_eq = beq
        self.bounds = bounds
        self.method = method

    def set_bounds(self,bounds_list):
        self.bounds = bounds_list

class LPd:
    
    data = []
    fun = 0.
    x = []
    success = False
    result = None
    
    def __init__(self,lp_data):
        self.data = lp_data

    def cal(self):
        result = None
        res = linprog(self.data.c,A_ub=self.data.A_ub, b_ub=self.data.b_ub,
                      A_eq=self.data.A_eq, b_eq=self.data.b_eq,
                      bounds=self.data.bounds,
                      method=self.data.method)                      
        if res["success"] == False:
            self.result = ([], np.NaN, False, None)
        else:
            self.success = True
            if self.data.minimise:
                self.fun = res["fun"]
            else:
                self.fun = -1 * res["fun"]
            self.x = res["x"]
            self.result = (self.x, self.fun, self.success, self.data)
            
    def get_res(self):
        return self.result

def level_iterator(level0, int_index):                                  

    level1 = []
    for k in range(len(level0)):
        for i in int_index:
            if level0[k][0][i-1] != np.floor(level0[k][0][i-1]):
                level0[k][3].bounds[i-1] = (0,np.floor(level0[k][0][i-1]))
                lp = LPd(copy.deepcopy(level0[k][3]))
                lp.cal()
                output = lp.get_res()
                level1.append(output)
                level0[k][3].bounds[i-1] = (np.ceil(level0[k][0][i-1]),None)
                lp = LPd(copy.deepcopy(level0[k][3]))
                lp.cal()
                output = lp.get_res()
                level1.append(output)
                break
    return level1

def intsol(solution,int_index):
    is_int = True 
    for i in int_index: 
        if solution[0][i-1] != np.floor(solution[0][i-1]):
            is_int = False
            break
    return is_int
 
def feasiblelevl(level, solution, int_index):
    newlevel = []
    solution = solution
    for k in range(len(level)):
        lp = level[k]
        if len(lp[0]) > 0:
            if lp[2] == False:
                pass
            elif intsol(lp,int_index) and lp[1] >= solution[1]:
                solution = lp
            elif lp[1] > solution[1]:
                newlevel.append(lp)
    return (newlevel, solution)

def LPmi(data, int_index):
    level0 = []
    lp  = LPd(data)
    lp.cal()
    level0.append(lp.get_res())
    solution = [None,-np.inf,None,None]
    level0 = (level0, solution)
    level0 = feasiblelevl(level0[0], solution, int_index)
    if len(level0[0]) == 0:
        return level0[1][0:3]
    else:
        for k in range(10):
            level1 = level_iterator(level0[0],int_index)
            level1 = feasiblelevl(level1, solution, int_index)
            if len(level1[0]) == 0:
                break
            level0 = level1
        return level1[1][0:3]
    
def test():
    c = np.array([3.,7])
    minimise = False
    A_ub = np.array([[7.,8],[1,3]])
    b_ub = np.array([56,12])
    data = LP_Data(c,minimise,A_ub,b_ub,bounds=[(0,None),(0,None)])
    int_index = [2] #or [1,2] or [] or [1]#
    out = LPmi(data,int_index)
    print(out)
    
if __name__ == "__main__":
    np.set_printoptions(precision=3,suppress=True)
    test()