Python 修正单纯形法进入无止境循环

Python 修正单纯形法进入无止境循环,python,numpy,mathematical-optimization,linear-programming,simplex-algorithm,Python,Numpy,Mathematical Optimization,Linear Programming,Simplex Algorithm,我正在尝试使用Python和numpy实现一个(RSM)算法。我坚持认为它要么只在最大化上工作(在2x4、5x5等小矩阵上正确,在较大的矩阵上错误),要么在大多数最小化情况下进入无限循环。下面的代码演示了我实现最小化的尝试: def __init__(self, A: np.ndarray, b: np.ndarray, c: np.ndarray): base_size = A.shape[0] # Number of basis vectors I

我正在尝试使用Python和numpy实现一个(RSM)算法。我坚持认为它要么只在最大化上工作(在2x4、5x5等小矩阵上正确,在较大的矩阵上错误),要么在大多数最小化情况下进入无限循环。下面的代码演示了我实现最小化的尝试:

    def __init__(self, A: np.ndarray, b: np.ndarray, c: np.ndarray):
        base_size = A.shape[0]  # Number of basis vectors
        I = np.eye(base_size)  # Identity matrix, an initial basis 
        self.extended_matrix = np.concatenate((A, I), axis=1)  # Extended matrix of the system
        self.free_size = A.shape[1]  # Number of free vectors
        self.b = b  # Right parts of the constraints
        self.base_inv = I  # Initial inverse basis matrix
        self.c = np.concatenate((c, np.zeros(base_size)))  # Objective function quotients including those related to the basis variables
        self.c_i = [i for i, coeff in enumerate(self.c)]  # Indices for all variables
        
    @property
    def c_f_indices(self):
        """
        Indices of the free variables.
        """
        return self.c_i[:self.free_size]
    
    @property
    def c_T_B(self):
        """
        Objective function quotients related to the basis variables.
        """
        c_B_indices = self.c_i[self.free_size:]  # Basis variables indices.
        return self.c[c_B_indices]
    
    @property
    def c_T_f(self):
        """
        Objective function quotients related to the free variables.
        """
        return self.c[self.c_f_indices]
        
    @property
    def free(self):
        """
        Free vectors.
        """
        return self.extended_matrix[:, self.c_f_indices]
    
    @property
    def y_T(self):
        """
        Lagrange multipliers.
        """
        return self.c_T_B @ self.base_inv
    
    @property
    def deltas(self):
        """
        Net evaluations. 
        """
        return (self.y_T @ self.free) - self.c_T_f 
    


    def _swap(self, exits: int, enters: int) -> None:
        """
        In-/excluding respective vectors into/from the basis.
        """
        self.c_i[enters], self.c_i[exits + self.free_size] = self.c_i[exits + self.free_size], self.c_i[enters]
    
    def optimize(self):
        while any([delta > 0 for delta in self.deltas]): # < 0 in case of maximization
            x_B = self.base_inv @ self.b  # Current basis variables
            enters_base = np.argmax(self.deltas)  # Vector to enter the basis; argmin in case of maximization
            
            # Finding the vector to leave the basis:
            alpha = self.base_inv @ self.free[:, enters_base]

            try:
                exits_base = np.argmin([b/a if a > 0 else np.inf for b, a in zip(x_B, alpha)])
                assert alpha[exits_base] != 0
            except (ValueError, AssertionError):
                raise Exception("No solutions")
            
            # Finding the E_r matrix, which current inverse basis will be left-multiplied with in order to achieve the new inverse basis:
            zetas = [-alpha[j] / alpha[exits_base] if j != exits_base else 1/alpha[exits_base] for j, a_j in enumerate(alpha)]
            E = np.eye(self.free.shape[0])
            E[:, exits_base] = zetas
            self.base_inv = E @ self.base_inv
            
            # In-/excluding respective vectors into/from the basis:
            self._swap(exits_base, enters_base)
            
        return self.c_T_B @ self.base_inv @ self.b # Final objective function value
def uuu init_uuuuuu(self,A:np.ndarray,b:np.ndarray,c:np.ndarray):
基本大小=A.shape[0]#基本向量数
I=np.eye(基本尺寸)#单位矩阵,初始基础
自扩展矩阵=np.串联((A,I),轴=1)#系统的扩展矩阵
self.free_size=A.shape[1]#自由向量数
self.b=b#约束的右部分
self.base_inv=I#初始逆基矩阵
self.c=np.concatenate((c,np.zero(base_size)))#目标函数商,包括与基变量相关的商
self.c_i=[i代表i,枚举中的系数(self.c)]#所有变量的索引
@财产
def c_f_索引(自):
"""
自由变量的指数。
"""
返回self.c_i[:self.free_size]
@财产
def c____B(自身):
"""
与基础变量相关的目标函数商。
"""
c_B_index=self.c_i[self.free_size:]#基变量索引。
返回self.c[c_B_索引]
@财产
def c_T_f(自身):
"""
与自由变量相关的目标函数商。
"""
返回self.c[self.c\u f\u索引]
@财产
无def(自):
"""
自由向量。
"""
返回self.extended_矩阵[:,self.c_f_索引]
@财产
定义y_T(自我):
"""
拉格朗日乘数。
"""
返回self.c\u T\u B@self.base\u inv
@财产
def增量(自):
"""
净评价。
"""
返回(self.y\T@self.free)-self.c\u\f
def_交换(self,exits:int,enters:int)->无:
"""
输入/排除输入/输出基中的各个向量。
"""
self.c_i[enters],self.c_i[exits+self.free_size]=self.c_i[exits+self.free_size],self.c_i[enters]
def优化(自我):
而任何([delta>0表示自增量中的delta]):#<0表示最大化
x_B=self.base_inv@self.B#当前基本变量
输入_base=np.argmax(self.delta)#向量以输入基;最大化情况下的argmin
#查找要离开基的向量:
alpha=self.base_inv@self.free[:,输入_base]
尝试:
退出\u base=np.argmin([b/a如果a>0,则为b,则为np.inf,zip中的a(x\u b,alpha)])
断言alpha[exits_base]!=0
除了(ValueError、AssertionError):
引发异常(“无解决方案”)
#找到E_r矩阵,当前逆基将与之相乘,以获得新的逆基:
齐塔人=[-alpha[j]/alpha[exits_base]如果j!=exits_base,否则1/alpha[exits_base]对于j,枚举(alpha)]
E=np.eye(self.free.shape[0])
E[:,退出_基地]=齐塔人
self.base\u inv=E@self.base\u inv
#输入/排除输入/输出基中的各个向量:
自交换(退出、进入)
返回self.c_T_B@self.base_inv@self.B#最终目标函数值
我也尝试过对c_f_指数进行排序,但仍然得到了无尽的循环。A在较大的矩阵(例如16x8)上也会产生错误的结果,而在较小的矩阵上效果很好


问题的根源在哪里?

正如Erwin Kalvelagen已经提到的,正常的Dantzig枢轴规则可能会导致循环和失速,长期以来目标值没有改善

一般来说,这种现象称为LP简并。有限的数值精度和舍入误差可能导致问题退化。这就是为什么它通常在大型LP中更为普遍


有几种方法可以解决这个问题。正如埃尔文所提到的,最常用的是干扰。但是,如果您将此作为一个学习项目,我建议您使用一种解决方案,使用更精细的数据透视规则,例如或,您只需保留一个表或变量,以确保在循环后选择不同的变量输入基准。

标准Dantzig规则可以显示循环。有不同的防循环方法。对于大型LP解算器,摄动是最常用的。