单纯形法求解线性规划

说明:以下内容为本人上机作业,仅供读者学习参考使用,禁止抄袭

问题描述

考虑从源节点到端节点传输数据的网络,我们首先引入一些定义:
∙ \bullet K\quad flows:传输K中不同的数据。
∙ \bullet L\quad link:连接网络节点的L条无向边。
∙ \bullet c l c_l cl:在第 l l l条边上传输数据的最大容量。
∙ \bullet P k P_k Pk:对于不同的k flow,运输成功可选择的路path的数目。
∙ \bullet x k = ( x k , 1 , … , x k , P K ) T \mathbf{x_k}=\left(x_{k,1}, \ldots, x_{k,P_{K}}\right)^T xk=(xk,1,,xk,PK)T x k , i x_{k,i} xk,i表示对于k\quad flow,在第 i i i条路某条边分配的数据量。
给定向量 C = ( c 1 , c 2 , … , c L ) T C=\left(c_{1}, c_{2}, \ldots, c_{L}\right)^T C=(c1,c2,,cL)T,其中 c l ∈ R + , ∀ l c_l \in R^{+},\forall{l} clR+,l。定义:
R k = ( R 1 , 1 k R 1 , 2 k ⋯ R 1 , P k k R 2 , 1 k R 2 , 2 k ⋯ R 2 , P k k ⋮ ⋮ ⋱ ⋮ R L , 1 k R L , 2 k ⋯ R L , P k k ) R_{k}=\left(\begin{array}{cccc} R_{1,1}^{k} & R_{1,2}^{k} & \cdots & R_{1, P_{k}}^{k} \\ R_{2,1}^{k} & R_{2,2}^{k} & \cdots & R_{2, P_{k}}^{k} \\ \vdots & \vdots & \ddots & \vdots \\ R_{L, 1}^{k} & R_{L, 2}^{k} & \cdots & R_{L, P_{k}}^{k} \end{array}\right) Rk= R1,1kR2,1kRL,1kR1,2kR2,2kRL,2kR1,PkkR2,PkkRL,Pkk
其中 R k R_{k} Rk中的任意一个元素 ∈ { 0 , 1 } \in \{0,1\} {0,1} P = ∑ i = 1 K P i P=\sum_{i=1}^{K}P_i P=i=1KPi,定义 R ∈ R L × P R \in R^{L\times P} RRL×P,其中 R l , i k = 1 R_{l,i}^k = 1 Rl,ik=1表示运输k\quad flow时,第 i i i条路会经过第 l l l条边:
R = ( R 1 , … , R K ) R=\left(R_{1}, \ldots, R_{K}\right) R=(R1,,RK)
定义向量 x k = ( x k , 1 , x k , 2 , … , x k , P k ) T x_k=\left(x_{k,1}, x_{k,2}, \ldots, x_{k,P_{k}}\right)^T xk=(xk,1,xk,2,,xk,Pk)T,其中 x k , i ≥ 0 , ∀ k , i x_{k,i} \geq 0,\forall{k,i} xk,i0,k,i,且 x k , P k > 0 , ∀ k x_{k,P_{k}} > 0,\forall{k} xk,Pk>0,k。定义 x = ( x 1 , … , x k ) ∈ R P x = (x_1,\ldots,x_k)\in R^P x=(x1,,xk)RP。有了这些定义以后,给出Problem 1的描述:
min ⁡ X max ⁡ l R [ l ] x c l \min _{X} \max _{l} \frac{R[l] x}{c_{l}} minXmaxlclR[l]x
 s.t.  R x ≤ C ∥ x k ∥ 1 = d k x ≥ 0 \text { s.t. } Rx \leq C \\{ } \left\|x_{k}\right\|_{1}=d_{k} \\ x \geq 0  s.t. RxCxk1=dkx0
转化为标准线性规划问题
引入中间变量 t = max ⁡ l y l c l t = \max_{l}\frac{y_l}{c_l} t=maxlclyl,则可以写成下面形式:
min ⁡ t , x , α , β t \min _{t,x,\alpha,\beta} t mint,x,α,βt
 s.t.  R x + α = C , R x + β = t C , 1 T x k = d k , x ≥ 0 , α ≥ 0 , β ≥ 0 , t ≥ 0. \text { s.t. } Rx + \alpha = C,\\{ } Rx + \beta = tC, \\{ } \boldsymbol{1}^T x_k = d_k,\\ x \geq 0,\alpha \geq 0,\beta \geq 0,t \geq 0.  s.t. Rx+α=C,Rx+β=tC,1Txk=dk,x0,α0,β0,t0.
进一步修改得到 ⟶ \longrightarrow
min ⁡ t , x , α , β t , \min _{t,x,\alpha,\beta} t, mint,x,α,βt,
 s.t.  R x + α = C , t C + α − β = C , 1 T x k = d k , x ≥ 0 , α ≥ 0 , β ≥ 0 , t ≥ 0. \text { s.t. } Rx + \alpha = C,\\{ } tC + \alpha - \beta = C, \\{ } \boldsymbol{1}^T x_k = d_k,\\ x \geq 0,\alpha \geq 0,\beta \geq 0,t \geq 0.  s.t. Rx+α=C,tC+αβ=C,1Txk=dk,x0,α0,β0,t0.
这样我们引入定义:\
∙ \bullet c ^ = ( 0 , … , 0 , 1 ) \hat{c} = (0,\ldots,0,1) c^=(0,,0,1)\
∙ \bullet x ^ = ( x , α , β , t ) T \hat{x} = (x,\alpha,\beta,t)^T x^=(x,α,β,t)T\
∙ \bullet b = ( c 1 , … , c l , c L , … , c L , d 1 , … , d K ) T b = (c_1,\ldots,c_l,c_L,\ldots,c_L,d_1,\ldots,d_K)^T b=(c1,,cl,cL,,cL,d1,,dK)T\
得到最终的标准线性规划问题:
min ⁡ x ^ c ^ T x ^ , \min _{\hat{x}} \hat{c}^T\hat{x}, minx^c^Tx^,
 s.t.  A x ^ = b , x ^ ≥ 0. \text { s.t. } A \hat{x} = b,\\{ } \hat{x} \geq 0.  s.t. Ax^=b,x^0.
其中定义 I L I_L IL L L L阶单位矩阵, C = ( c 1 , … , c L ) T C = (c_1,\ldots,c_L)^T C=(c1,,cL)T以及 S ∈ R K × P S\in R^{K\times P} SRK×P,矩阵 S S S的第一行只有前 P 1 P_1 P1个元素为1,其余为0,第二行元素只有 P 1 + 1 , … , P 1 + P 2 P_1 + 1,\ldots,P_1 + P_2 P1+1,,P1+P2为1,其余为0,类似地,最后一行只有后面 P K P_K PK个元素为1,其余为0.由此得到下面这个系数矩阵 A A A的表达式:
A = [ R I L 0 0 0 I L − I L C S 0 0 0 ] A=\left[\begin{array}{cccc} R & I_L & 0 & 0 \\ 0 & I_L & -I_L & C \\ S & 0 & 0 & 0 \end{array}\right] A= R0SILIL00IL00C0

标准线性规划

考虑标准的线性规划问题,其中 A ∈ R m × n , C ∈ R n , b ∈ R m A \in R^{m \times n},C \in R^{n},b \in R^{m} ARm×n,CRn,bRm
min ⁡ x C T x \min_{x} C^{T}x minxCTx
 s.t.  A x = b , \text { s.t. } Ax = b,  s.t. Ax=b,
x ≥ 0. x \geq 0. x0.
众所周知,在利用单纯形法求解线性规划的时候,从当前极点 x x x到下一个极点 x + x^{+} x+的迭代中,假设 x x x的基指标为 B B B,非基指标为 N N N,我们有
c T x + = c T x + ( C N − N T B − T C B ) T x N + c^Tx^{+}=c^Tx + (C_N - N^{T}B^{-T}C_B)^Tx_{N}^{+} cTx+=cTx+(CNNTBTCB)TxN+
由于在指标 N N N中,根据单纯形法, x + x^{+} x+只有一个元素大于0,记为 x q + x_{q}^{+} xq+,如果
( C N − N T B − T C B ) T x N + < 0 (C_N - N^{T}B^{-T}C_B)^Tx_{N}^{+} < 0 (CNNTBTCB)TxN+<0
x x x非最优点,再根据
x B + = B − 1 b − B − 1 A q x q + x_{B}^{+}=B^{-1}b-B^{-1}A_qx_{q}^{+} xB+=B1bB1Aqxq+
我们需要选取适当的 x q + x_{q}^{+} xq+,由此引出了以下几种准则。
Dantzig准则
q = a r g m i n j ∈ N ( C N − N T B − T C B ) j , ( C N − N T B − T C B ) j < 0. q = arg min_{j \in N}(C_N - N^{T}B^{-T}C_B)_j,(C_N - N^{T}B^{-T}C_B)_j < 0. q=argminjN(CNNTBTCB)j,(CNNTBTCB)j<0.
关于这个Dantzig准则,我们只选取了具体的需要修改的索引位置 q q q,但是具体的 x q + x_{q}^{+} xq+取值为多少,还需要根据来确定,具体的做法是选尽量大的 x q + x_{q}^{+} xq+,使得 x B + x_{B}^{+} xB+只有一个元素为0,其他元素均大于或等于0。
Steepest Edge 准则
q = a r g m i n j ∈ N ( C N − N T B − T C B ) j B − 1 N j , ( C N − N T B − T C B ) j < 0. q =arg min_{j \in N}\frac{(C_N - N^{T}B^{-T}C_B)_j}{B^{-1}N_j},(C_N - N^{T}B^{-T}C_B)_j < 0. q=argminjNB1Nj(CNNTBTCB)j,(CNNTBTCB)j<0.
我们可以这么理解:上面的Dantzig准则类似于最速下降法,每次迭代选取当前迭代的最优方向,但是整体来看却不见得最优,这个Steepest Edge 准则有点类似于做了一个正规化。

大M法求解初始基本可行解

在求解线性规划问题时,首先我们需要一个初始点满足约束,然后从初始点出发进行单纯形法迭代,经过调研,大M法和两阶段法比较普遍,下面我们重点介绍大M法求初始基本可行解的过程.
我们考虑标准的线性规划问题,引入一个充分大的常数M,定义 C ^ = ( c 1 , … , c n , M , … , M ) , A ^ = ( A , I m ) , x ^ = ( x , x n + 1 , … , x n + m ) \hat{C} = (c_1,\ldots,c_n,M,\ldots,M),\hat{A} = (A,I_{m}),\hat{x} = (x,x_{n + 1},\ldots,x_{n + m}) C^=(c1,,cn,M,,M),A^=(A,Im),x^=(x,xn+1,,xn+m),现在考虑问题:
min ⁡ X C ^ T x ^ \min _{X} \hat{C}^{T}\hat{x} minXC^Tx^
 s.t.  A ^ x ^ = b , \text { s.t. } \hat{A}\hat{x} = b,  s.t. A^x^=b,
x ^ ≥ 0. \hat{x} \geq 0. x^0.
则对于线性规划问题,我们就有第一个初始可行点 x ^ = ( 0 , … , 0 , b 1 , … , b m ) \hat{x}=(0,\ldots,0,b1,\ldots,b_m) x^=(0,,0,b1,,bm),有了这个初始可行点以后,我们就可以根据单纯形法做迭代,得到一个形式为 ( x 1 , … , x n , 0 , … , 0 ) (x_1,\ldots,x_n,0,\ldots,0) (x1,,xn,0,,0)的解,最终得到一个原始问题的初始可行点 ( x 1 , … , x n ) (x_1,\ldots,x_n) (x1,,xn)
说明:这种方法仅限于约束项的 b ≥ 0 b \geq 0 b0的情况(这样才保证大M法在第一步是对的),这里我们需要把M选取为充分大的常数,这样就可以保证,如果原始问题有可行点,那么新问题的最优解一定是类似于 ( x 1 , … , x n , 0 , … , 0 ) (x_1,\ldots,x_n,0,\ldots,0) (x1,,xn,0,,0)的形式,在我们具体操作过程中,这个M的选取并不固定,本人做了几个实验,发现如果采取高斯分布生成 A , b , c A,b,c A,b,c,由于随机种子的不一样,有的时候M的选取会超过 1 0 20 10^{20} 1020,不过在我们这次项目中, A , b , c A,b,c A,b,c的元素相对较小,所以不会受到影响。
一般线性规划大M法的代码,求解初始可行解

import numpy as np
import time
import matplotlib.pyplot as plt
def Bland(s,indn):
    ind = np.where(s[:,0]<0)[0]
    q = ind[0]
    return indn[q], q

def DanzigSelect(s,indn):#indn非基索引
    q = s.argmin()
    return indn[q], q

def SteepSelect(A_n,s,indn):
    ratio = s/A_n.transpose()
    q = ratio.argmin()
    return indn[q], q
def bigM(c,A,b,piv,M):#使用大M方法求解初始基本可行解
    eps = 1e-6
    [m,n] = A.shape
    x = np.zeros([n + m,1]);x[n:,:] = b
    
    Ah = np.hstack([A,np.identity(m)])
    ch =  np.vstack([c,M*np.ones([m,1])])
    indb = np.arange(m + n)[n:]#初始基指标索引
    index = np.arange(0,m + n)
    indn = np.delete(index,indb)
    B = Ah[:,indb]#初始B是单位矩阵
    B_inv = B
    #print(B)
    N = Ah[:,indn]
    iteration = 0        # determine the max iteration
    
    while iteration < 500:
        y = np.dot(B_inv.T, ch[indb,:])
        s = ch[indn,:] - N.T@y#C_N - N^TB^{-T}C_B
        if all(s >= 0):#说明这个就是最优解
            print('unbelievabel,the resid:%.2e, done,the epoch:%d'%(np.linalg.norm(Ah@x - b),iteration))
            break
        if piv == 'Danzig':
            q1, q2 = DanzigSelect(s, indn)
        if piv == 'steepest-edge':
            A_re = B_inv@N
            A_n = np.linalg.norm(A_re,axis = 0,keepdims = True)
            q1, q2 = SteepSelect(A_n, s, indn)
        if piv == 'Bland':
            q1, q2 = Bland(s, indn)
        #q1是非基指标中需要被替换的索引
        u = B_inv@Ah[:, q1:(q1+1)]   
        if all(u <= 0):#说明最优值无界
            print('Linear program is unbounded.')
            break
            
        inds1 = np.where(u[:,0] > 0)[0]#找出所有大于0的索引
        inds2 = indb[inds1]#在基指标中把对应大于0的索引全找出来
        ratio = x[inds2,:]/u[inds1,:]
        x_q = min(ratio)        # a number
        p1 = inds2[ratio.argmin()]
        p2 = inds1[ratio.argmin()]
        
        x[indb,:] = x[indb,:]- x_q*u
        vec = np.zeros([indn.size,1])
        vec[q2,0] = x_q
        x[indn,:] = x[indn,:] + vec
        rho = (x[n:,:]**2).mean()
        if iteration%50 == 0:
            
            print('epoch:%d,enter basis:%d,quit basis:%d,resid:%.2e,rest err:%.2e'
                  %(iteration,q1,p1,np.linalg.norm(Ah@x - b),rho))
        
        if rho < eps:
            print('the end epoch:%d,rho:%.2e,the real resid:%.2e'
                  %(iteration,rho,np.linalg.norm(A@x[:n,:] - b)))
            break
        else:
            indb = np.delete(indb,p2)
            indb = np.append(indb,q1)
            indn = np.delete(indn,q2)
            indn = np.append(indn,p1)
            
            B = Ah[:, indb]
            B_inv = np.linalg.inv(B)
            N = Ah[:, indn]
            iteration = iteration + 1
    return x[:n,:]

def simplex(c,A,b,piv,M):
    eps = 1e-6
    [m,n] = A.shape
    x = bigM(c,A,b,piv,M)
    #print(x.shape,A.shape,b.shape,indb.size,m)
    if np.linalg.norm(A@x - b) > eps or (any(x < 0) and min(x) < -eps):
        print('the init solution error')
        return 0
    else:
        print('the init solution come on')
    indb = []
    geq = np.where(x > 0)[0]
    eq = np.where(x <= 0)[0]
    indb = np.append(indb,geq)
    if indb.size == m:
        pass
    elif indb.size < m:
        er = m - indb.size
        print('basic need:%d'%(er))
        ra = np.random.choice(eq,er,replace = False)
        indb = np.append(indb,ra)
    else:
        print('wrong')
        return 0
    index = np.arange(0,n)
    indb = indb.astype('int32')
    indn = np.delete(index,indb)
    B = A[:,indb]
    
    B_inv = np.linalg.inv(B)
    
    N = A[:,indn]
    iteration = 0        # determine the max iteration
    
    while iteration < 2000:
        y = np.dot(B_inv.T, c[indb,:])
        s = c[indn,:] - N.T@y#C_N - N^TB^{-T}C_B
        value = (c*x).sum()
        if all(s >= 0) or (any(s < 0) and min(s) > -eps):#说明这个就是最优解
            print('the end epoch:%d,resid:%.2e,opt val:%.2e'
                  %(iteration,np.linalg.norm(A@x - b),value))
            break
        if piv == 'Danzig':
            q1, q2 = DanzigSelect(s, indn)
        if piv == 'steepest-edge':
            A_re = B_inv@N
            A_n = np.linalg.norm(A_re,axis = 0,keepdims = True)
            q1, q2 = SteepSelect(A_n, s, indn)
        if piv == 'Bland':
            q1, q2 = Bland(s, indn)
        #q1是非基指标中需要被替换的索引
        u = B_inv@A[:, q1:(q1+1)]   
        if all(u <= 0):#说明最优值无界
            print('Linear program is unbounded.')
            break
            
        inds1 = np.where(u[:,0] > 0)[0]#找出所有大于0的索引
        inds2 = indb[inds1]#在基指标中把对应大于0的索引全找出来
        ratio = x[inds2,:]/u[inds1,:]
        x_q = min(ratio)        # a number
        p1 = inds2[ratio.argmin()]
        p2 = inds1[ratio.argmin()]
        
        x[indb,:] = x[indb,:]- x_q*u
        vec = np.zeros([indn.size,1])
        vec[q2,0] = x_q
        x[indn,:] = x[indn,:] + vec
        res = np.linalg.norm(A@x - b)
        if res > eps:
            print('blow up,epoch:%d,resid:%.2e'%(iteration,res))
            break
        if iteration%100 == 0:
            #print(np.linalg.det(B))
            print('epoch:%d,enter basis:%d,quit basis:%d,resid:%.2e,opt val:%.2e'
                  %(iteration,q1,p1,res,value))
        indb = np.delete(indb,p2)
        indb = np.append(indb,q1)
            
        indn = np.delete(indn,q2)
        indn = np.append(indn,p1)
            
        B = A[:, indb]
        B_inv = invB(B_inv,p2,u)
           
        N = A[:, indn]
        iteration = iteration + 1
    return x

L1 = 100
P1 = 200
k = 5
p = 0.02

np.random.seed(1234)

R = np.random.binomial(1,p,[L1,P1])#二项分布,生成[L1,P1]行数组,元素全是0或1
d = np.random.gamma(2,2,[k,1])*1#gamma分布,
u = np.random.gamma(2,2,[L1,1])*3+20
s = P1 - k
ind = np.random.randint(0,k,s)#随机抽取0到K之间的整数
S = np.zeros((k,s))
for i in range(s):
    j = ind[i]
    S[j,i] = 1
S = np.hstack((np.identity(k),S))

n = P1+2*L1+1       #364
m = 2*L1+k          #196

# construct the vector c, only the last element is 1
c = np.zeros((n,1))
c[-1,0] = 1

print(c.shape, c[-1,0])

# construct the matrix A
S1 = np.hstack((R,np.zeros((L1,L1)),np.identity(L1),np.zeros((L1,1))))
S2 = np.hstack((np.zeros((L1,P1)),-np.identity(L1),np.identity(L1),u))
S3 = np.hstack((S,np.zeros((k,(2*L1+1)))))
A = np.vstack((S1,S2,S3))

b = np.vstack((u,u,d))
piv = 'Bland'

M = 1e6
x = bigM(c,A,b,piv,M)
#x = simplex(c,A,b,piv,M)

在这里插入图片描述

两阶段法求解初始可行解

简单介绍一下两阶段法的做法,和上面提到的大M法相比,两阶段法最大的优势就是不需要选择超参数M。仍然以标准的线性规划问题举例,我们的求解过程分成两个阶段进行。下面考虑第一个阶段,其中 y = ( y 1 , … , y m ) y = (y_1,\ldots,y_m) y=(y1,,ym)
min ⁡ x , y ∑ i = 1 m y i \min_{x,y} \sum_{i = 1}^m y_i minx,yi=1myi
 s.t.  A x + I m y = b , \text { s.t. } Ax + I_m y = b,  s.t. Ax+Imy=b,
x , y ≥ 0. x,y \geq 0. x,y0.
求解线性规划,如果求出最优值不为0,说明原始线性规划没有可行解。如果最优值求出来为0,那么我们需要分情况考虑:
第一种情况:问题的最优解 ( x , y ) (x,y) (x,y)的基指标包含人工变量 y y y的指标,此时说明原始线性规划退化,这个时候需要做一些替换,不断把包含 y y y的基指标替换出来,然后得到初始可行解。
第二种情况:问题的最优解 ( x , y ) (x,y) (x,y)的基指标不包含人工变量 y y y的指标,此时说明我们可以直接把 x x x作为一个初始可行解来使用。
在第一个阶段的基础上进行第二个阶段就很简单,直接根据初始可行解做单纯形法迭代即可。
一般线性规划两阶段法代码

import numpy as np

def Bland(s,indn):
    ind = np.where(s[:,0]<0)[0]
    q = ind[0]
    return indn[q], q

def DanzigSelect(s,indn):#indn非基索引
    q = s.argmin()
    return indn[q], q

def SteepSelect(A_n,s,indn):
    ratio = s/A_n.transpose()
    q = ratio.argmin()
    return indn[q], q
def invB(B_inv,p,u):
    Bv = B_inv.copy()
    Bv[p,:] = Bv[p,:]/u[p,0]
    m = u.size
    ind = []
    for i in range(m):
        if i == p:
            pass
        else:
            ind.append(i)
            Bv[i,:] = Bv[i,:] - u[i,0]*Bv[p,:]
    ind.append(p)
    Bv = Bv[ind,:]
    return Bv
def stage_one(A,b,piv):#使用大M方法求解初始基本可行解
    eps = 1e-6
    [m,n] = A.shape
    x = np.zeros([n + m,1]);x[n:,:] = b
    
    Ah = np.hstack([A,np.identity(m)])
    ch =  np.zeros([n + m,1]);ch[n:,:] = np.ones([m,1])
    indb = np.arange(m + n)[n:]#初始基指标索引
    index = np.arange(0,m + n)
    indn = np.delete(index,indb)
    B = Ah[:,indb]#初始B是单位矩阵
    B_inv = B
    
    N = Ah[:,indn]
    iteration = 0        # determine the max iteration
    
    while iteration < 1000:
        
        y = np.dot(B_inv.T, ch[indb,:])
        s = ch[indn,:] - N.T@y#C_N - N^TB^{-T}C_B
        if all(s >= 0):#说明这个就是最优解
            print('unbelievabel,the resid:%.2e, done,the epoch:%d'%(np.linalg.norm(Ah@x - b),iteration))
            break
        if piv == 'Danzig':
            q1, q2 = DanzigSelect(s, indn)
        if piv == 'steepest-edge':
            A_re = B_inv@N
            A_n = np.linalg.norm(A_re,axis = 0,keepdims = True)
            q1, q2 = SteepSelect(A_n, s, indn)
        if piv == 'Bland':
            q1, q2 = Bland(s, indn)
        #q1是非基指标中需要被替换的索引
        u = B_inv@Ah[:, q1:(q1+1)]   
        if all(u <= 0):#说明最优值无界
            print('Linear program is unbounded.')
            break
            
        inds1 = np.where(u[:,0] > 0)[0]#找出所有大于0的索引
        inds2 = indb[inds1]#在基指标中把对应大于0的索引全找出来
        ratio = x[inds2,:]/u[inds1,:]
        x_q = min(ratio)        # a number
        p1 = inds2[ratio.argmin()]
        p2 = inds1[ratio.argmin()]
        
        x[indb,:] = x[indb,:]- x_q*u
        vec = np.zeros([indn.size,1])
        vec[q2,0] = x_q
        x[indn,:] = x[indn,:] + vec
        aug = np.linalg.norm(Ah@x - b)
        if aug > eps:
            print('blow up,epoch:%d,aug resid:%.2e'%(iteration,aug))
            break
        value = x[n:,:].sum()
        if iteration%100 == 0:
            
            print('epoch:%d,enter basis:%d,quit basis:%d,resid:%.2e,opt val:%.2e'
                  %(iteration,q1,p1,aug,value))
        
        if value < eps:
            print('the end epoch:%d,opt val:%.2e,the real resid:%.2e'
                  %(iteration,value,np.linalg.norm(A@x[:n,:] - b)))
            break
        else:
            
            indb = np.delete(indb,p2)
            indb = np.append(indb,q1)
            
            indn = np.delete(indn,q2)
            indn = np.append(indn,p1)
            
            B = Ah[:, indb]
            B_inv = invB(B_inv,p2,u)
           
            N = Ah[:, indn]
            iteration = iteration + 1
    return x[:n,:]

def stage_two(c,A,b,piv,L,P,K):
    eps = 1e-6
    [m,n] = A.shape
    x = stage_one(A,b,piv)
    
    if np.linalg.norm(A@x - b) > eps or (any(x < 0) and min(x) < -eps):
        print('the init solution error')
        print('---------------------------------------')
        return 0
    else:
        print('the init solution come on')
        print('---------------------------------------')
    geq = np.where(x > eps)[0]
    eq = np.where(x == 0)[0]
    #eeq = np.where(any(x > 0 and x < eps))[0]
    if geq.size < m:
        er = m - geq.size
        ra = np.random.choice(eq.size,er)
        indb = np.append(geq,eq[ra])
    elif geq.size > m:
        print('error')
        return 0
    else:
        indb = geq
    index = np.arange(0,n)
    indn = np.delete(index,indb)
    B = A[:,indb]
    
    B_inv = np.linalg.inv(B)
    
    N = A[:,indn]
    iteration = 0        # determine the max iteration
    
    while iteration < 2000:
        y = np.dot(B_inv.T, c[indb,:])
        s = c[indn,:] - N.T@y#C_N - N^TB^{-T}C_B
        value = (c*x).sum()
        if all(s >= 0) or (any(s < 0) and min(s) > -eps):#说明这个就是最优解
            print('the end epoch:%d,resid:%.2e,opt val:%.2e'
                  %(iteration,np.linalg.norm(A@x - b),value))
            break
        if piv == 'Danzig':
            q1, q2 = DanzigSelect(s, indn)
        if piv == 'steepest-edge':
            A_re = B_inv@N
            A_n = np.linalg.norm(A_re,axis = 0,keepdims = True)
            q1, q2 = SteepSelect(A_n, s, indn)
        if piv == 'Bland':
            q1, q2 = Bland(s, indn)
        #q1是非基指标中需要被替换的索引
        u = B_inv@A[:, q1:(q1+1)]   
        if all(u <= 0):#说明最优值无界
            print('Linear program is unbounded.')
            break
            
        inds1 = np.where(u[:,0] > 0)[0]#找出所有大于0的索引
        inds2 = indb[inds1]#在基指标中把对应大于0的索引全找出来
        ratio = x[inds2,:]/u[inds1,:]
        x_q = min(ratio)        # a number
        p1 = inds2[ratio.argmin()]
        p2 = inds1[ratio.argmin()]
        
        x[indb,:] = x[indb,:]- x_q*u
        vec = np.zeros([indn.size,1])
        vec[q2,0] = x_q
        x[indn,:] = x[indn,:] + vec
        res = np.linalg.norm(A@x - b)
        if res > eps:
            print('blow up,epoch:%d,resid:%.2e'%(iteration,res))
            break
        if iteration%100 == 0:
            #print(np.linalg.det(B))
            print('epoch:%d,enter basis:%d,quit basis:%d,resid:%.2e,opt val:%.2e'
                  %(iteration,q1,p1,res,value))
        indb = np.delete(indb,p2)
        indb = np.append(indb,q1)
            
        indn = np.delete(indn,q2)
        indn = np.append(indn,p1)
            
        B = A[:, indb]
        B_inv = invB(B_inv,p2,u)
           
        N = A[:, indn]
        iteration = iteration + 1
    return x


L = 64
P = 128
K = 30
p = 0.02

np.random.seed(1)

# seed 1 with 512,1024, Danzig beats steepest
# seed 1 with 256, 512 steepest beats Danzig

R = np.random.binomial(1,p,[L,P])#二项分布,生成[L1,P1]行数组,元素全是0或1
d = np.random.gamma(2,2,[K,1])*1#gamma分布,
u = np.random.gamma(2,2,[L,1])*3+20
s = P - K
ind = np.random.randint(0,K,s)#随机抽取0到K之间的整数
S = np.zeros((K,s))
for i in range(s):
    j = ind[i]
    S[j,i] = 1
S = np.hstack((np.identity(K),S))

#---------------------------------
L = R.shape[0];P = R.shape[1];K = S.shape[0]
m = 2*L + K;n = P + 2*L + 1
print(type(R),type(S),type(u),type(d))
print(R.shape,S.shape,u.shape,d.shape)
print(max(R.reshape(-1,1)),min(R.reshape(-1,1)),max(S.reshape(-1,1)),min(S.reshape(-1,1)))
row1 = np.hstack([R,np.identity(L),np.zeros([L,L]),np.zeros([L,1])])
row2 = np.hstack([np.zeros([L,P]),np.identity(L),-np.identity(L),u])
row3 = np.hstack([S,np.zeros([K,2*L + 1])])
A = np.vstack([row1,row2,row3])
print(np.linalg.matrix_rank(A),m)
b = np.vstack([u,u,d])
c = np.zeros([n,1]);c[-1,0] = 1.0
#piv = 'Bland'
piv = 'Danzig'
x = stage_two(c,A,b,piv,L,P,K)


print(all(x >= 0))
geq = np.where(x > 0)[0]
eq = np.where(x == 0)[0]
leq = np.where(x < 0)[0]
print(geq.size,eq.size,leq.size,m)
print('the nagetive value:')
print(x[leq])
#----------------------

在这里插入图片描述

单纯形法求解上述线性规划

初始可行解的选取
在我们接连介绍了单纯形法的一些准则,概念,以及给出了我们项目需要求解的线性规划具体的形式以后,我们将在这一小节给出使用单纯形法求解我们的项目的具体做法。
首先,我们需要获得第一个初始可行解来启动单纯形法,在实际操作种,我们发现如果简单利用大M法或者是两阶段法来求解第一个初始可行解,当矩阵规模过大的时候,算法变得很不稳定,由此我们想出了一种专门针对这样形式的矩阵的单纯形构造法来构造第一个可行解。具体做法是,首先给出第一个子问题如下:
min ⁡ x , y ∑ i = 1 K + L z i ,  s.t.  S x = d , R x + y = C , x ≥ 0. \begin{array}{cl} \min _{x,y} & \sum_{i = 1}^{K + L} z_i, \\ \text { s.t. } & Sx = d,\\{ } & Rx + y = C,\\{ }& x \geq 0. \end{array} minx,y s.t. i=1K+LziSx=d,Rx+y=C,x0.
利用两阶段法的第一个阶段来求解第一个子问题\ref{sub1},不妨记为 ( x , y ) = ( x 1 : P , x P : P + L ) (x,y)=(x_{1:P},x_{P:P + L}) (x,y)=(x1:P,xP:P+L)。\
然后我们定义一个变量 t t t,定义后续给出。现在我们定义第二个子问题: x P : P + L − x + t C = C x_{P:P+L} - x + tC=C xP:P+Lx+tC=C
求解第二个子问题我们得到的解记为 x P + L : P + 2 L x_{P+L:P+2L} xP+L:P+2L,很容易看出 x P + L : P + 2 L = x P : P + L + ( t − 1 ) C x_{P+L:P+2L}=x_{P:P+L} + (t-1)C xP+L:P+2L=xP:P+L+(t1)C。最终我们得到第一个初始可行解为:
∙ \bullet x = ( x 1 : P , x P : P + L , x P + L : P + 2 L , t ) x = (x_{1:P},x_{P:P+L},x_{P+L:P+2L},t) x=(x1:P,xP:P+L,xP+L:P+2L,t)
根据上面求解过程,我们知道 x 1 : P ≥ 0 , x P : P + L ≥ 0 x_{1:P} \geq 0,x_{P:P+L} \geq 0 x1:P0xP:P+L0,并且元素为0 的个数至少是 P − K P - K PK。如果元素为0的个数是 P − K + 1 P - K + 1 PK+1,我们选取合适的 t ≥ 0 t \geq 0 t0,则会有 x ≥ 0 x \geq 0 x0,此时得到的解非0元素的个数至少是 P + 1 − K = m − n P + 1 - K = m - n P+1K=mn,那么这个就可以作为我们的初始可行解。\par
如果元素为0的个数恰好是 P − K P - K PK,我们可以选取合适的 t ≥ 0 t \geq 0 t0,这里选择 t = 1 − min ⁡ 0 ≤ j ≤ L − 1 x P : P + j C j t = 1 - \min_{0 \leq j \leq L-1}\frac{x_{P:P+j}}{C_j} t=1min0jL1CjxP:P+j,这个其实还原为原始问题就是 t = 1 − min ⁡ 0 ≤ j ≤ L − 1 α C j t = 1 - \min_{0 \leq j \leq L-1}\frac{\alpha}{C_j} t=1min0jL1Cjα,由于 R x + α = C Rx+\alpha=C Rx+α=C,很明显有 t ≥ 0 t \geq 0 t0,使得 x P + L : P + 2 L x_{P+L:P+2L} xP+L:P+2L恰好只有一个元素是0,其余都大于0,经过这个过程,得到的解非0元素的个数至少是 P + 1 − K = n − m P + 1 - K = n - m P+1K=nm,那么也得到初始可行解
迭代法的选择
在得到初始可行解以后,我们需要做一个判断,如果初始可行解 x 0 x_0 x0存在 m m m个元素大于0,其余元素为0,那么我们就可以得到相应的基指标。如果初始可行解 x 0 x_0 x0大于0的元素个数小于 m m m,那么我们需要把那些大于0元素的索引取出来,然后再根据适当的原则选取差额索引数目放入得到基指标。
在具体的代码实现中,我们可以有上述提到的两种准则可以使用,这里需要求解基矩阵 B B B的逆矩阵,这个我们也做了相应的处理。在给定上一次迭代使用的基矩阵我们记为 B 1 B_1 B1,假设 B 1 = ( A 1 , … , A p − 1 , A p , A p + 1 , … , A m ) B_1 = (A_{1},\ldots,A_{p-1},A_{p},A_{p+1},\ldots,A_{m}) B1=(A1,,Ap1,Ap,Ap+1,,Am)和对应的逆矩阵 B 1 − 1 B_1^{-1} B11前提下,新的基矩阵 B 2 = ( A 1 , … , A p − 1 , A p + 1 , … , A m , A q ) B_2 = (A_{1},\ldots,A_{p-1},A_{p+1},\ldots,A_{m},A_q) B2=(A1,,Ap1,Ap+1,,Am,Aq),则我们有 B 1 − 1 B 2 = ( e 1 , … , e p − 1 , e p + 1 , … , e m , B 1 − 1 A q ) B_1^{-1}B_2 = (e_1,\ldots,e_{p-1},e_{p+1},\ldots,e_m,B_1^{-1}A_q) B11B2=(e1,,ep1,ep+1,,em,B11Aq),此时我们可以在 B 1 − 1 B_1^{-1} B11的基础上做初等变换得到新矩阵 B 2 B_2 B2的逆矩阵。

数据生成

为了方便展示结果,这里我们展示数据的生成模式:
在这里插入图片描述
上述特定线性规划问题求解的代码
randsimplex.py
随机选择准则更新基指标

import numpy as np
import time
def Bland(s,indn):
    ind = np.where(s[:,0]<0)[0]
    q = ind[0]
    return indn[q], q

def DanzigSelect(s,indn):#indn非基索引
    q = s.argmin()
    return indn[q], q

def SteepSelect(A_n,s,indn):
    ratio = s/A_n.transpose()
    q = ratio.argmin()
    return indn[q], q
def invB(B_inv,p,u):
    Bv = B_inv.copy()
    Bv[p,:] = Bv[p,:]/u[p,0]
    m = u.size
    ind = []
    for i in range(m):
        if i == p:
            pass
        else:
            ind.append(i)
            Bv[i,:] = Bv[i,:] - u[i,0]*Bv[p,:]
    ind.append(p)
    Bv = Bv[ind,:]
    return Bv
def stage_one(A,b):#使用大M方法求解初始基本可行解
    eps = 1e-6
    [m,n] = A.shape
    x = np.zeros([n + m,1]);x[n:,:] = b
    
    Ah = np.hstack([A,np.identity(m)])
    ch =  np.zeros([n + m,1]);ch[n:,:] = np.ones([m,1])
    indb = np.arange(m + n)[n:]#初始基指标索引
    index = np.arange(0,m + n)
    indn = np.delete(index,indb)
    B = Ah[:,indb]#初始B是单位矩阵
    B_inv = B
    
    N = Ah[:,indn]
    iteration = 0        # determine the max iteration
    
    while iteration < 1000:
        
        y = np.dot(B_inv.T, ch[indb,:])
        s = ch[indn,:] - N.T@y#C_N - N^TB^{-T}C_B
        if all(s >= 0):#说明这个就是最优解
            print('unbelievabel,the resid:%.2e, done,the epoch:%d'%(np.linalg.norm(Ah@x - b),iteration))
            break
        z = np.random.rand(1)
        if (z >= 0 and z <= 1/3):
            q1, q2 = DanzigSelect(s, indn)
        elif (z >= 1/3 and z <= 2/3):
            A_re = B_inv@N
            A_n = np.linalg.norm(A_re,axis = 0,keepdims = True)
            q1, q2 = SteepSelect(A_n, s, indn)
        else:
            q1, q2 = Bland(s, indn)
        #q1是非基指标中需要被替换的索引
        u = B_inv@Ah[:, q1:(q1+1)]   
        if all(u <= 0):#说明最优值无界
            print('Linear program is unbounded.')
            break
            
        inds1 = np.where(u[:,0] > 0)[0]#找出所有大于0的索引
        inds2 = indb[inds1]#在基指标中把对应大于0的索引全找出来
        ratio = x[inds2,:]/u[inds1,:]
        x_q = min(ratio)        # a number
        p1 = inds2[ratio.argmin()]
        p2 = inds1[ratio.argmin()]
        
        x[indb,:] = x[indb,:]- x_q*u
        vec = np.zeros([indn.size,1])
        vec[q2,0] = x_q
        x[indn,:] = x[indn,:] + vec
        aug = np.linalg.norm(Ah@x - b)
        if aug > eps:
            print('blow up,epoch:%d,aug resid:%.2e'%(iteration,aug))
            break
        value = x[n:,:].sum()
        if iteration%100 == 0:
            
            print('epoch:%d,enter basis:%d,quit basis:%d,resid:%.2e,opt val:%.2e'
                  %(iteration,q1,p1,aug,value))
        
        if value < eps and all(s) >= 0:
            print('the end epoch:%d,opt val:%.2e,the real resid:%.2e'
                  %(iteration,value,np.linalg.norm(A@x[:n,:] - b)))
            break
        else:
            
            indb = np.delete(indb,p2)
            indb = np.append(indb,q1)
            
            indn = np.delete(indn,q2)
            indn = np.append(indn,p1)
            
            B = Ah[:, indb]
            B_inv = invB(B_inv,p2,u)
           
            N = Ah[:, indn]
            iteration = iteration + 1
    return x[:n]
def MCF(A,b,L,P,K):
    S = A[2*L:,:P]
    R = A[:L,:P]
    indb = []
    [m,n] = A.shape
    u = b[:L,:];d = b[2*L:,:]
    
    A1 = np.hstack([S,np.zeros([K,L])])
    A2 = np.hstack([R,np.identity(L)])
    Ar = np.vstack([A1,A2])
    br = np.vstack([d,u])
    x = np.zeros([n,1])
    x[:P + L,:] = stage_one(Ar,br)
    geq = np.where(x[:P + L,:] > 0)[0]
    indb = np.append(indb,geq)
    #print(indb.size,K + L)
    if geq.size < K + L:
        radio = min(x[P:P + L,:]/u)
        M = 1.0 - radio# + 1e-2
        
        geq3 = np.arange(P + L,P + 2*L)
        indb = np.append(indb,geq3)
        if indb.size == K + 2*L - 1:
            pass
        else:
            er = K + 2*L - 1 - indb.size
            index = np.setdiff1d(np.arange(0,P + L),geq)
            ra = np.random.choice(index,er,replace = False)
            indb = np.append(indb,ra)
    else:
        radio = min(x[P:P + L,:]/u)
        ind = np.argmin(x[P:P + L,:]/u) + P + L
        geq3 = np.setdiff1d(np.arange(P + L,P + 2*L),ind)
        indb = np.append(indb,geq3)
        M = 1 - radio
    x[-1,0] = M
    indb = np.append(indb,P + 2*L)
    for i in range(L):
        x[P + L + i,0] = x[P + i,0] + (M - 1)*u[i,0]
    
    #print(indb.size)
    return x,indb
def stage_two(c,A,b,L,P,K):
    eps = 1e-6
    [m,n] = A.shape
    x,indb = MCF(A,b,L,P,K)
    #print(x.shape,A.shape,b.shape,indb.size,m)
    if np.linalg.norm(A@x - b) > eps or (any(x < 0) and min(x) < -eps):
        print('the init solution error')
        return 0
    else:
        print('the init solution come on')
    
    indb = indb.astype('int32')
    index = np.arange(0,n)
    indn = np.delete(index,indb)
    B = A[:,indb]
    
    B_inv = np.linalg.inv(B)
    
    N = A[:,indn]
    iteration = 0        # determine the max iteration
    
    while iteration < 3000:
        y = np.dot(B_inv.T, c[indb,:])
        s = c[indn,:] - N.T@y#C_N - N^TB^{-T}C_B
        value = (c*x).sum()
        if all(s >= 0) or (any(s < 0) and min(s) > -eps):#说明这个就是最优解
            print('the end epoch:%d,resid:%.2e,opt val:%.2e'
                  %(iteration,np.linalg.norm(A@x - b),value))
            break
        z = np.random.rand(1)
        if (z >= 0 and z <= 1/3):
            q1, q2 = DanzigSelect(s, indn)
        elif (z >= 1/3 and z <= 2/3):
            A_re = B_inv@N
            A_n = np.linalg.norm(A_re,axis = 0,keepdims = True)
            q1, q2 = SteepSelect(A_n, s, indn)
        else:
            q1, q2 = Bland(s, indn)
        
        #q1是非基指标中需要被替换的索引
        u = B_inv@A[:, q1:(q1+1)]   
        if all(u <= 0):#说明最优值无界
            print('Linear program is unbounded.')
            break
            
        inds1 = np.where(u[:,0] > 0)[0]#找出所有大于0的索引
        inds2 = indb[inds1]#在基指标中把对应大于0的索引全找出来
        ratio = x[inds2,:]/u[inds1,:]
        x_q = min(ratio)        # a number
        p1 = inds2[ratio.argmin()]
        p2 = inds1[ratio.argmin()]
        
        x[indb,:] = x[indb,:]- x_q*u
        vec = np.zeros([indn.size,1])
        vec[q2,0] = x_q
        x[indn,:] = x[indn,:] + vec
        res = np.linalg.norm(A@x - b)
        if res > eps:
            print('blow up,epoch:%d,resid:%.2e'%(iteration,res))
            break
        if iteration%100 == 0:
            #print(np.linalg.det(B))
            print('epoch:%d,enter basis:%d,quit basis:%d,resid:%.2e,opt val:%.2e'
                  %(iteration,q1,p1,res,value))
        indb = np.delete(indb,p2)
        indb = np.append(indb,q1)
            
        indn = np.delete(indn,q2)
        indn = np.append(indn,p1)
            
        B = A[:, indb]
        B_inv = invB(B_inv,p2,u)
           
        N = A[:, indn]
        iteration = iteration + 1
    return x




#L1 = 128
L1 = 64
P1 = 1024
k = 50
p = 0.02

np.random.seed(1)

# seed 1 with 512,1024, Danzig beats steepest
# seed 1 with 256, 512 steepest beats Danzig

R = np.random.binomial(1,p,[L1,P1])#二项分布,生成[L1,P1]行数组,元素全是0或1
d = np.random.gamma(2,2,[k,1])*1#gamma分布,
u = np.random.gamma(2,2,[L1,1])*3+20
s = P1 - k
ind = np.random.randint(0,k,s)#随机抽取0到K之间的整数
S = np.zeros((k,s))
for i in range(s):
    j = ind[i]
    S[j,i] = 1
S = np.hstack((np.identity(k),S))
n = P1+2*L1+1       #364
m = 2*L1+k          #196

# construct the vector c, only the last element is 1
c = np.zeros((n,1))
c[-1,0] = 1

# construct the matrix A
S1 = np.hstack((R,np.identity(L1),np.zeros((L1,L1)),np.zeros((L1,1))))
S2 = np.hstack((np.zeros((L1,P1)),np.identity(L1),-np.identity(L1),u))
S3 = np.hstack((S,np.zeros((k,(2*L1+1)))))
A = np.vstack((S1,S2,S3))


b = np.vstack((u,u,d))



#---------------------------------
L = R.shape[0];P = R.shape[1];K = S.shape[0]
m = 2*L + K;n = P + 2*L + 1
#print(type(R),type(S),type(u),type(d))
print(R.shape,S.shape,u.shape,d.shape)
print(max(R.reshape(-1,1)),min(R.reshape(-1,1)),max(S.reshape(-1,1)),min(S.reshape(-1,1)))
row1 = np.hstack([R,np.identity(L),np.zeros([L,L]),np.zeros([L,1])])
row2 = np.hstack([np.zeros([L,P]),np.identity(L),-np.identity(L),u])
row3 = np.hstack([S,np.zeros([K,2*L + 1])])
A = np.vstack([row1,row2,row3])
print(A.shape)
#print(np.linalg.matrix_rank(A),m)
b = np.vstack([u,u,d])
c = np.zeros([n,1]);c[-1,0] = 1.0
#piv = 'Bland'
#piv = 'Danzig'

st = time.time()
x = stage_two(c,A,b,L,P,K)
ela = time.time() - st

print(all(x >= 0),': use time:%.2f'%(ela))
geq = np.where(x > 0)[0]
eq = np.where(x == 0)[0]
leq = np.where(x < 0)[0]
print('geq num:%d,eq num:%d,leq num:%d, m=%d,n=%d'%(geq.size,eq.size,leq.size,m,n))
print('the nagetive element:')
print(x[leq])
#----------------------

singlesimplex.py

import numpy as np
import time
def Bland(s,indn):
    ind = np.where(s[:,0]<0)[0]
    q = ind[0]
    return indn[q], q

def DanzigSelect(s,indn):#indn非基索引
    q = s.argmin()
    return indn[q], q

def SteepSelect(A_n,s,indn):
    ratio = s/A_n.transpose()
    q = ratio.argmin()
    return indn[q], q
def invB(B_inv,p,u):
    Bv = B_inv.copy()
    Bv[p,:] = Bv[p,:]/u[p,0]
    m = u.size
    ind = []
    for i in range(m):
        if i == p:
            pass
        else:
            ind.append(i)
            Bv[i,:] = Bv[i,:] - u[i,0]*Bv[p,:]
    ind.append(p)
    Bv = Bv[ind,:]
    return Bv
def stage_one(A,b,piv):#使用大M方法求解初始基本可行解
    eps = 1e-6
    [m,n] = A.shape
    x = np.zeros([n + m,1]);x[n:,:] = b
    
    Ah = np.hstack([A,np.identity(m)])
    ch =  np.zeros([n + m,1]);ch[n:,:] = np.ones([m,1])
    indb = np.arange(m + n)[n:]#初始基指标索引
    index = np.arange(0,m + n)
    indn = np.delete(index,indb)
    B = Ah[:,indb]#初始B是单位矩阵
    B_inv = B
    
    N = Ah[:,indn]
    iteration = 0        # determine the max iteration
    
    while iteration < 1000:
        
        y = np.dot(B_inv.T, ch[indb,:])
        s = ch[indn,:] - N.T@y#C_N - N^TB^{-T}C_B
        if all(s >= 0):#说明这个就是最优解
            print('unbelievabel,the resid:%.2e, done,the epoch:%d'%(np.linalg.norm(Ah@x - b),iteration))
            break
        if piv == 'Danzig':
            q1, q2 = DanzigSelect(s, indn)
        if piv == 'steepest-edge':
            A_re = B_inv@N
            A_n = np.linalg.norm(A_re,axis = 0,keepdims = True)
            q1, q2 = SteepSelect(A_n, s, indn)
        if piv == 'Bland':
            q1, q2 = Bland(s, indn)
        #q1是非基指标中需要被替换的索引
        u = B_inv@Ah[:, q1:(q1+1)]   
        if all(u <= 0):#说明最优值无界
            print('Linear program is unbounded.')
            break
            
        inds1 = np.where(u[:,0] > 0)[0]#找出所有大于0的索引
        inds2 = indb[inds1]#在基指标中把对应大于0的索引全找出来
        ratio = x[inds2,:]/u[inds1,:]
        x_q = min(ratio)        # a number
        p1 = inds2[ratio.argmin()]
        p2 = inds1[ratio.argmin()]
        
        x[indb,:] = x[indb,:]- x_q*u
        vec = np.zeros([indn.size,1])
        vec[q2,0] = x_q
        x[indn,:] = x[indn,:] + vec
        aug = np.linalg.norm(Ah@x - b)
        if aug > eps:
            print('blow up,epoch:%d,aug resid:%.2e'%(iteration,aug))
            break
        value = x[n:,:].sum()
        if iteration%100 == 0:
            
            print('epoch:%d,enter basis:%d,quit basis:%d,resid:%.2e,opt val:%.2e'
                  %(iteration,q1,p1,aug,value))
        
        if value < eps and all(s) >= 0:
            print('the end epoch:%d,opt val:%.2e,the real resid:%.2e'
                  %(iteration,value,np.linalg.norm(A@x[:n,:] - b)))
            break
        else:
            
            indb = np.delete(indb,p2)
            indb = np.append(indb,q1)
            
            indn = np.delete(indn,q2)
            indn = np.append(indn,p1)
            
            B = Ah[:, indb]
            B_inv = invB(B_inv,p2,u)
           
            N = Ah[:, indn]
            iteration = iteration + 1
    return x[:n]
def MCF(A,b,L,P,K):
    S = A[2*L:,:P]
    R = A[:L,:P]
    indb = []
    [m,n] = A.shape
    u = b[:L,:];d = b[2*L:,:]
    
    A1 = np.hstack([S,np.zeros([K,L])])
    A2 = np.hstack([R,np.identity(L)])
    Ar = np.vstack([A1,A2])
    br = np.vstack([d,u])
    x = np.zeros([n,1])
    x[:P + L,:] = stage_one(Ar,br,piv = 'Bland')
    geq = np.where(x[:P + L,:] > 0)[0]
    indb = np.append(indb,geq)
    #print(indb.size,K + L)
    if geq.size < K + L:
        radio = min(x[P:P + L,:]/u)
        M = 1.0 - radio# + 1e-2
        
        geq3 = np.arange(P + L,P + 2*L)
        indb = np.append(indb,geq3)
        if indb.size == K + 2*L - 1:
            pass
        else:
            er = K + 2*L - 1 - indb.size
            index = np.setdiff1d(np.arange(0,P + L),geq)
            ra = np.random.choice(index,er,replace = False)
            indb = np.append(indb,ra)
    else:
        radio = min(x[P:P + L,:]/u)
        ind = np.argmin(x[P:P + L,:]/u) + P + L
        geq3 = np.setdiff1d(np.arange(P + L,P + 2*L),ind)
        indb = np.append(indb,geq3)
        M = 1 - radio
    x[-1,0] = M
    indb = np.append(indb,P + 2*L)
    for i in range(L):
        x[P + L + i,0] = x[P + i,0] + (M - 1)*u[i,0]
    
    #print(indb.size)
    return x,indb
def stage_two(c,A,b,piv,L,P,K):
    eps = 1e-6
    [m,n] = A.shape
    x,indb = MCF(A,b,L,P,K)
    #print(x.shape,A.shape,b.shape,indb.size,m)
    if np.linalg.norm(A@x - b) > eps or (any(x < 0) and min(x) < -eps):
        print('the init solution error')
        return 0
    else:
        print('the init solution come on')
    
    indb = indb.astype('int32')
    index = np.arange(0,n)
    indn = np.delete(index,indb)
    B = A[:,indb]
    
    B_inv = np.linalg.inv(B)
    
    N = A[:,indn]
    iteration = 0        # determine the max iteration
    
    while iteration < 2000:
        y = np.dot(B_inv.T, c[indb,:])
        s = c[indn,:] - N.T@y#C_N - N^TB^{-T}C_B
        value = (c*x).sum()
        if all(s >= 0) or (any(s < 0) and min(s) > -eps):#说明这个就是最优解
            print('the end epoch:%d,resid:%.2e,opt val:%.2e'
                  %(iteration,np.linalg.norm(A@x - b),value))
            break
        if piv == 'Danzig':
            q1, q2 = DanzigSelect(s, indn)
        if piv == 'steepest-edge':
            A_re = B_inv@N
            A_n = np.linalg.norm(A_re,axis = 0,keepdims = True)
            q1, q2 = SteepSelect(A_n, s, indn)
        if piv == 'Bland':
            q1, q2 = Bland(s, indn)
        #q1是非基指标中需要被替换的索引
        u = B_inv@A[:, q1:(q1+1)]   
        if all(u <= 0):#说明最优值无界
            print('Linear program is unbounded.')
            break
            
        inds1 = np.where(u[:,0] > 0)[0]#找出所有大于0的索引
        inds2 = indb[inds1]#在基指标中把对应大于0的索引全找出来
        ratio = x[inds2,:]/u[inds1,:]
        x_q = min(ratio)        # a number
        p1 = inds2[ratio.argmin()]
        p2 = inds1[ratio.argmin()]
        
        x[indb,:] = x[indb,:]- x_q*u
        vec = np.zeros([indn.size,1])
        vec[q2,0] = x_q
        x[indn,:] = x[indn,:] + vec
        res = np.linalg.norm(A@x - b)
        if res > eps:
            print('blow up,epoch:%d,resid:%.2e'%(iteration,res))
            break
        if iteration%100 == 0:
            #print(np.linalg.det(B))
            print('epoch:%d,enter basis:%d,quit basis:%d,resid:%.2e,opt val:%.2e'
                  %(iteration,q1,p1,res,value))
        indb = np.delete(indb,p2)
        indb = np.append(indb,q1)
            
        indn = np.delete(indn,q2)
        indn = np.append(indn,p1)
            
        B = A[:, indb]
        B_inv = invB(B_inv,p2,u)
           
        N = A[:, indn]
        iteration = iteration + 1
    return x




L1 = 128
P1 = 1024
k = 50
p = 0.02

np.random.seed(1)

# seed 1 with 512,1024, Danzig beats steepest
# seed 1 with 256, 512 steepest beats Danzig

R = np.random.binomial(1,p,[L1,P1])#二项分布,生成[L1,P1]行数组,元素全是0或1
d = np.random.gamma(2,2,[k,1])*1#gamma分布,
u = np.random.gamma(2,2,[L1,1])*3+20
s = P1 - k
ind = np.random.randint(0,k,s)#随机抽取0到K之间的整数
S = np.zeros((k,s))
for i in range(s):
    j = ind[i]
    S[j,i] = 1
S = np.hstack((np.identity(k),S))
n = P1+2*L1+1       #364
m = 2*L1+k          #196

# construct the vector c, only the last element is 1
c = np.zeros((n,1))
c[-1,0] = 1

# construct the matrix A
S1 = np.hstack((R,np.identity(L1),np.zeros((L1,L1)),np.zeros((L1,1))))
S2 = np.hstack((np.zeros((L1,P1)),np.identity(L1),-np.identity(L1),u))
S3 = np.hstack((S,np.zeros((k,(2*L1+1)))))
A = np.vstack((S1,S2,S3))


b = np.vstack((u,u,d))



#---------------------------------
L = R.shape[0];P = R.shape[1];K = S.shape[0]
m = 2*L + K;n = P + 2*L + 1
#print(type(R),type(S),type(u),type(d))
print(R.shape,S.shape,u.shape,d.shape)
print(max(R.reshape(-1,1)),min(R.reshape(-1,1)),max(S.reshape(-1,1)),min(S.reshape(-1,1)))
row1 = np.hstack([R,np.identity(L),np.zeros([L,L]),np.zeros([L,1])])
row2 = np.hstack([np.zeros([L,P]),np.identity(L),-np.identity(L),u])
row3 = np.hstack([S,np.zeros([K,2*L + 1])])
A = np.vstack([row1,row2,row3])
#print(np.linalg.matrix_rank(A),m)
b = np.vstack([u,u,d])
c = np.zeros([n,1]);c[-1,0] = 1.0
#piv = 'Bland'
piv = 'Danzig'

st = time.time()
x = stage_two(c,A,b,piv,L,P,K)
ela = time.time() - st

print(all(x >= 0),': use time:%.2f'%(ela))
geq = np.where(x > 0)[0]
eq = np.where(x == 0)[0]
leq = np.where(x < 0)[0]
print('geq num:%d,eq num:%d,leq num:%d, m=%d,n=%d'%(geq.size,eq.size,leq.size,m,n))
print('the nagetive element:')
print(x[leq])
#----------------------

在这里插入图片描述

multisimplex.py

在这里插入图片描述

import numpy as np
import time
def Bland(s,indn):
    ind = np.where(s[:,0]<0)[0]
    q = ind[0]
    return indn[q], q

def DanzigSelect(s,indn):#indn非基索引
    q = s.argmin()
    return indn[q], q

def SteepSelect(A_n,s,indn):
    ratio = s/A_n.transpose()
    q = ratio.argmin()
    return indn[q], q
def invB(B_inv,p,u):
    Bv = B_inv.copy()
    Bv[p,:] = Bv[p,:]/u[p,0]
    m = u.size
    ind = []
    for i in range(m):
        if i == p:
            pass
        else:
            ind.append(i)
            Bv[i,:] = Bv[i,:] - u[i,0]*Bv[p,:]
    ind.append(p)
    Bv = Bv[ind,:]
    return Bv
def stage_one(A,b,piv):#使用大M方法求解初始基本可行解
    eps = 1e-6
    [m,n] = A.shape
    x = np.zeros([n + m,1]);x[n:,:] = b
    
    Ah = np.hstack([A,np.identity(m)])
    ch =  np.zeros([n + m,1]);ch[n:,:] = np.ones([m,1])
    indb = np.arange(m + n)[n:]#初始基指标索引
    index = np.arange(0,m + n)
    indn = np.delete(index,indb)
    B = Ah[:,indb]#初始B是单位矩阵
    B_inv = B
    
    N = Ah[:,indn]
    iteration = 0        # determine the max iteration
    
    while iteration < 1000:
        
        y = np.dot(B_inv.T, ch[indb,:])
        s = ch[indn,:] - N.T@y#C_N - N^TB^{-T}C_B
        if all(s >= 0):#说明这个就是最优解
            print('unbelievabel,the resid:%.2e, done,the epoch:%d'%(np.linalg.norm(Ah@x - b),iteration))
            break
        if piv == 'Danzig':
            q1, q2 = DanzigSelect(s, indn)
        if piv == 'steepest-edge':
            A_re = B_inv@N
            A_n = np.linalg.norm(A_re,axis = 0,keepdims = True)
            q1, q2 = SteepSelect(A_n, s, indn)
        if piv == 'Bland':
            q1, q2 = Bland(s, indn)
        #q1是非基指标中需要被替换的索引
        u = B_inv@Ah[:, q1:(q1+1)]   
        if all(u <= 0):#说明最优值无界
            print('Linear program is unbounded.')
            break
            
        inds1 = np.where(u[:,0] > 0)[0]#找出所有大于0的索引
        inds2 = indb[inds1]#在基指标中把对应大于0的索引全找出来
        ratio = x[inds2,:]/u[inds1,:]
        x_q = min(ratio)        # a number
        p1 = inds2[ratio.argmin()]
        p2 = inds1[ratio.argmin()]
        
        x[indb,:] = x[indb,:]- x_q*u
        vec = np.zeros([indn.size,1])
        vec[q2,0] = x_q
        x[indn,:] = x[indn,:] + vec
        aug = np.linalg.norm(Ah@x - b)
        if aug > eps:
            print('blow up,epoch:%d,aug resid:%.2e'%(iteration,aug))
            break
        value = x[n:,:].sum()
        if iteration%100 == 0:
            
            print('epoch:%d,enter basis:%d,quit basis:%d,resid:%.2e,opt val:%.2e'
                  %(iteration,q1,p1,aug,value))
        
        if value < eps:
            print('the end epoch:%d,opt val:%.2e,the real resid:%.2e'
                  %(iteration,value,np.linalg.norm(A@x[:n,:] - b)))
            break
        else:
            
            indb = np.delete(indb,p2)
            indb = np.append(indb,q1)
            
            indn = np.delete(indn,q2)
            indn = np.append(indn,p1)
            
            B = Ah[:, indb]
            B_inv = invB(B_inv,p2,u)
           
            N = Ah[:, indn]
            iteration = iteration + 1
    return x[:n,:]
def MCF(A,b,L,P,K):
    S = A[2*L:,:P]
    R = A[:L,:P]
    indb = []
    [m,n] = A.shape
    u = b[:L,:];d = b[2*L:,:]
    
    A1 = np.hstack([S,np.zeros([K,L])])
    A2 = np.hstack([R,np.identity(L)])
    Ar = np.vstack([A1,A2])
    br = np.vstack([d,u])
    x = np.zeros([n,1])
    x[:P + L,:] = stage_one(Ar,br,piv = 'Bland')
    geq = np.where(x[:P + L,:] > 0)[0]
    indb = np.append(indb,geq)
    #print(indb.size,K + L)
    if geq.size < K + L:
        radio = min(x[P:P + L,:]/u)
        M = 1.0 - radio# + 1e-2
        
        geq3 = np.arange(P + L,P + 2*L)
        indb = np.append(indb,geq3)
        if indb.size == K + 2*L - 1:
            pass
        else:
            er = K + 2*L - 1 - indb.size
            index = np.setdiff1d(np.arange(0,P + L),geq)
            ra = np.random.choice(index,er,replace = False)
            indb = np.append(indb,ra)
    else:
        radio = min(x[P:P + L,:]/u)
        ind = np.argmin(x[P:P + L,:]/u) + P + L
        geq3 = np.setdiff1d(np.arange(P + L,P + 2*L),ind)
        indb = np.append(indb,geq3)
        M = 1 - radio
    x[-1,0] = M
    indb = np.append(indb,P + 2*L)
    for i in range(L):
        x[P + L + i,0] = x[P + i,0] + (M - 1)*u[i,0]
    
    #print(indb.size)
    return x,indb
def stage_two(c,A,b,L,P,K):
    eps = 1e-6
    [m,n] = A.shape
    x,indb = MCF(A,b,L,P,K)
    
    if np.linalg.norm(A@x - b) > eps or (any(x < 0) and min(x) < -eps):
        print('the init solution error')
        return 0
    else:
        print('the init solution come on')
    
    indb = indb.astype('int32')
    index = np.arange(0,n)
    indn = np.delete(index,indb)
    B = A[:,indb]
    
    B_inv = np.linalg.inv(B)
    
    N = A[:,indn]
    iteration = 0        # determine the max iteration
    value = 0
    while iteration < 2000:
        y = np.dot(B_inv.T, c[indb,:])
        s = c[indn,:] - N.T@y#C_N - N^TB^{-T}C_B
        
        if all(s >= 0) or (any(s < 0) and min(s) > -eps):#说明这个就是最优解
            print('the end epoch:%d,resid:%.2e,opt val:%.2e'
                  %(iteration,np.linalg.norm(A@x - b),value))
            break
        xd = x.copy()
        xs = x.copy()
        xb = x.copy()
        allx = []
        allq = []
        allp = []
        allu = []
        allval = []
        #------------------------
        q1, q2 = DanzigSelect(s, indn)
        allq.append(q1)
        allq.append(q2)
        u = B_inv@A[:, q1:(q1+1)]
        inds1 = np.where(u[:,0] > 0)[0]#找出所有大于0的索引
        inds2 = indb[inds1]#在基指标中把对应大于0的索引全找出来
        ratio = xd[inds2,:]/u[inds1,:]
        x_q = min(ratio)        # a number
        p1 = inds2[ratio.argmin()]
        p2 = inds1[ratio.argmin()]
        allp.append(p1)
        allp.append(p2)
        xd[indb,:] = xd[indb,:]- x_q*u
        vec = np.zeros([indn.size,1])
        vec[q2,0] = x_q
        xd[indn,:] = xd[indn,:] + vec
        
        val = (c*xd).sum()
        
        allx.append(xd)
        
        allu.append(u)
        allval.append(val)
        #------------------------
        A_re = B_inv@N
        A_n = np.linalg.norm(A_re,axis = 0,keepdims = True)
        q1, q2 = SteepSelect(A_n, s, indn)
        allq.append(q1)
        allq.append(q2)
        u = B_inv@A[:, q1:(q1+1)]
        inds1 = np.where(u[:,0] > 0)[0]#找出所有大于0的索引
        inds2 = indb[inds1]#在基指标中把对应大于0的索引全找出来
        ratio = xs[inds2,:]/u[inds1,:]
        x_q = min(ratio)        # a number
        p1 = inds2[ratio.argmin()]
        p2 = inds1[ratio.argmin()]
        allp.append(p1)
        allp.append(p2)
        xs[indb,:] = xs[indb,:]- x_q*u
        vec = np.zeros([indn.size,1])
        vec[q2,0] = x_q
        xs[indn,:] = xs[indn,:] + vec
        
        val = (c*xs).sum()
        
        allx.append(xs)
        
        allu.append(u)
        allval.append(val)
        #-------------------------
        q1, q2 = Bland(s, indn)
        allq.append(q1)
        allq.append(q2)
        u = B_inv@A[:, q1:(q1+1)]
        inds1 = np.where(u[:,0] > 0)[0]#找出所有大于0的索引
        inds2 = indb[inds1]#在基指标中把对应大于0的索引全找出来
        ratio = xb[inds2,:]/u[inds1,:]
        x_q = min(ratio)        # a number
        p1 = inds2[ratio.argmin()]
        p2 = inds1[ratio.argmin()]
        allp.append(p1)
        allp.append(p2)
        xb[indb,:] = xb[indb,:]- x_q*u
        vec = np.zeros([indn.size,1])
        vec[q2,0] = x_q
        xb[indn,:] = xb[indn,:] + vec
        
        val = (c*xb).sum()
        
        allx.append(xb)
        
        allu.append(u)
        allval.append(val)
        #----------------
        mind = np.argmin(allval)
        u = allu[mind]
        x = allx[mind]
        
        value = allval[mind]
        q1,q2 = allq[2*mind],allq[2*mind + 1]
        p1,p2 = allp[2*mind],allp[2*mind + 1]
        allu.clear();allx.clear();allval.clear();allq.clear();allp.clear()
        #q1是非基指标中需要被替换的索引
        if all(u <= 0):#说明最优值无界
            print('Linear program is unbounded.')
            break
        res = np.linalg.norm(A@x - b)
        if res > eps:
            print('blow up,epoch:%d,resid:%.2e'%(iteration,res))
            break
        if iteration%100 == 0:
            #print(np.linalg.det(B))
            print('epoch:%d,enter basis:%d,quit basis:%d,resid:%.2e,opt val:%.2e'
                  %(iteration,q1,p1,res,value))
        indb = np.delete(indb,p2)
        indb = np.append(indb,q1)
            
        indn = np.delete(indn,q2)
        indn = np.append(indn,p1)
            
        B = A[:, indb]
        B_inv = invB(B_inv,p2,u)
           
        N = A[:, indn]
        iteration = iteration + 1
    return x






L1 = 128
P1 = 1024
k = 50
p = 0.02

np.random.seed(1)

# seed 1 with 512,1024, Danzig beats steepest
# seed 1 with 256, 512 steepest beats Danzig

R = np.random.binomial(1,p,[L1,P1])#二项分布,生成[L1,P1]行数组,元素全是0或1
d = np.random.gamma(2,2,[k,1])*1#gamma分布,
u = np.random.gamma(2,2,[L1,1])*3+20
s = P1 - k
ind = np.random.randint(0,k,s)#随机抽取0到K之间的整数
S = np.zeros((k,s))
for i in range(s):
    j = ind[i]
    S[j,i] = 1
S = np.hstack((np.identity(k),S))
n = P1+2*L1+1       #364
m = 2*L1+k          #196

# construct the vector c, only the last element is 1
c = np.zeros((n,1))
c[-1,0] = 1

# construct the matrix A
S1 = np.hstack((R,np.identity(L1),np.zeros((L1,L1)),np.zeros((L1,1))))
S2 = np.hstack((np.zeros((L1,P1)),np.identity(L1),-np.identity(L1),u))
S3 = np.hstack((S,np.zeros((k,(2*L1+1)))))
A = np.vstack((S1,S2,S3))


b = np.vstack((u,u,d))


#---------------------------------
L = R.shape[0];P = R.shape[1];K = S.shape[0]
m = 2*L + K;n = P + 2*L + 1
#print(type(R),type(S),type(u),type(d))
print(R.shape,S.shape,u.shape,d.shape)
print(max(R.reshape(-1,1)),min(R.reshape(-1,1)),max(S.reshape(-1,1)),min(S.reshape(-1,1)))
row1 = np.hstack([R,np.identity(L),np.zeros([L,L]),np.zeros([L,1])])
row2 = np.hstack([np.zeros([L,P]),np.identity(L),-np.identity(L),u])
row3 = np.hstack([S,np.zeros([K,2*L + 1])])
A = np.vstack([row1,row2,row3])
#print(np.linalg.matrix_rank(A),m)
b = np.vstack([u,u,d])
c = np.zeros([n,1]);c[-1,0] = 1.0

st = time.time()
x = stage_two(c,A,b,L,P,K)
ela = time.time() - st

print(all(x >= 0),': use time:%.2f'%(ela))
geq = np.where(x > 0)[0]
eq = np.where(x == 0)[0]
leq = np.where(x < 0)[0]
print('geq num:%d,eq num:%d,leq num:%d, m=%d,n=%d'%(geq.size,eq.size,leq.size,m,n))
#print(geq.size,eq.size,leq.size,m)
print('the nagetive element:')
print(x[leq])
#----------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Galerkin码农选手

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值