python - python中的数值精确线性编程用于检查点是否可以线性分离?

标签 python scipy linear-programming numerical-methods

我正在使用线性程序来测试两组点是否可以用一条线分开。理论上这是可行的,但实际上,如果这些点接近共线或彼此靠近,似乎会出现数值问题。

我使用的包是scipy linprog

我编写了一些代码来举例说明我正在谈论的内容。此代码生成 N 个点的云“位置”,并选择一些随机线,然后使用这些线周围的边距将“位置”集的子集划分为两个区域:X 和 Y,然后检查线性程序是否成功找到 X 和 Y 之间的分隔线,或者如果线性程序得出结论不存在这样的分隔线(找不到可行点)。

正如您从输出(如下)中看到的那样,随着边距从 1 变为 2^(-10),线性程序正确检测到两个区域是线性可分离的概率会下降。这表明在检测非常接近的点云可以分离时可能存在一些问题。

请注意,因为我正在输入保证线性可分的线性程序集,所以输出应全部为 1。

import numpy as np
from scipy.optimize import linprog

N = 100

for minv in range(10):
    margin = 1/(2**minv)
    locations = np.random.uniform(-1,1,[N,2])
    tests = []
    for i in range(50):
        p = np.random.normal(0,1,[3])
        X = []
        Y = []
        for a in locations:
            if np.dot(a, [p[0],p[1]]) < p[2] - margin:
                X.append(a)
            if np.dot(a, [p[0],p[1]]) > p[2] + margin:
                Y.append(a)
        #X and Y are two linearly seperable subsets of 'locations'
        A = []
        b = []
        if X != [] and Y != []:
            #This if is just to avoid an edge case that causes problems with linprog
            for s in X:
                A.append( [-1*v for v in s] + [1] )
                b.append(-1)
            for s in Y:
                A.append( list(s) + [-1])
                b.append(-1)
            #See: https://www.joyofdata.de/blog/testing-linear-separability-linear-programming-r-glpk/
            res = linprog(c = [0,0,0], A_ub = A, b_ub = b, bounds = [-np.inf, np.inf])
            tests.append(res["success"])
    print(np.mean(tests))

输出:

1.0
1.0
0.909090909091
0.8
0.805555555556
0.5
0.375
0.444444444444
0.378378378378
0.410256410256

我的问题:

  1. 如何可靠(且高效)地解决检测两组点是否线性可分的问题? (另一种方法是构建凸包,我已经将其大部分写出来了。使用 Qhull 时存在一些问题:Qhull Convex hull wants me to input at least 3 points)

  2. 这是 scipy linprog 中的错误吗?还是我只是没有正确使用它?

最佳答案

这是一个似乎可以工作的清理版本。我删除了阈值变量,因为它最多可以被吸收到法线向量中。缺点是我们必须检查两种情况。我的直觉是阈值变量在零处的跳跃导致求解器失效。

import numpy as np
from scipy.optimize import linprog

N = 100

def test(X, Y):
    if not (X.size and Y.size):
        return True, np.full((2,), np.nan)
    A = np.r_[-X, Y]
    b = np.repeat((-1, 1), (X.shape[0], Y.shape[0]))
    res1 = linprog(c=[0,0], A_ub=A, b_ub=b-1e-6, bounds=2*[(None, None)])
    res2 = linprog(c=[0,0], A_ub=A, b_ub=-b-1e-6, bounds=2*[(None, None)])
    if res1['success']:
        return True, res1['x']
    elif res2['success']:
        return True, res2['x']
    else:
        return False, np.full((2,), np.nan)

def split(locations, p, margin):
    proj = np.einsum('ij,j', locations, p[:2])
    X = locations[proj < p[2] - margin]
    Y = locations[proj > p[2] + margin]
    return X, Y

def validate(locations, p, p_recon, margin):
    proj = np.einsum('ij,j', locations, p[:2])
    X = locations[proj < p[2] - margin]
    Y = locations[proj > p[2] + margin]
    if not (X.size and Y.size):
        return True
    return ((np.all(np.einsum('ij,j', X, p_recon) > 1)
             and np.all(np.einsum('ij,j', Y, p_recon) < 1)) 
            or
            (np.all(np.einsum('ij,j', X, p_recon) > -1)
             and np.all(np.einsum('ij,j', Y, p_recon) < -1)))

def random_split(locations):
    inX = np.random.randint(0, 2, (locations.shape[0],), dtype=bool)
    return locations[inX], locations[~inX]

for minv in range(10):
    margin = 1/(2**minv)
    locations = np.random.uniform(-1,1,[N,2])
    P = np.random.normal(0, 1, (50, 3))
    tests, P_recon = zip(*(test(*split(locations, p, margin)) for p in P))
    assert all(validate(locations, *ps, margin) for ps in zip(P, P_recon))
    control = [test(*random_split(locations))[0] for p in P]
    print('test:', np.mean(tests), 'control:', np.mean(control))

示例输出:

test: 1.0 control: 0.0
test: 1.0 control: 0.0
test: 1.0 control: 0.0
test: 1.0 control: 0.0
test: 1.0 control: 0.0
test: 1.0 control: 0.0
test: 1.0 control: 0.0
test: 1.0 control: 0.0
test: 1.0 control: 0.0
test: 1.0 control: 0.0

关于python - python中的数值精确线性编程用于检查点是否可以线性分离?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50194282/

相关文章:

python - 从字典列表中获取 "keys"

python - 无法将 `scipy.interpolate.RectBivariateSpline` 与 `matplotlib.pyplot,plot_surface` 一起使用

python - Scipy basinhopping 不尊重 stepsize?

python - Pyomo 找到最优解,但解数为 0(水电模拟)

matlab - 线性规划约束 : multiplication of optimization variables

r - 使用 RGLPK 在 R 中进行梦幻足球线性规划

python - Django:查找非空字符串的优雅方式

python - 使用 df.apply() 将带参数的函数应用于每一行

python - 将 numpy/scipy 链接到串行 ATLAS

python - 如何在运行系统配置验收测试的 pytest 中管理 sys.path