我有一些非常大的数据集,分析管道的一部分是确定,如标题所述,每个点是否被n
维度的单纯形约束。如果可能的话,我正在试图找到一种方法来快速计算,而无需并行化。其中一个障碍是数据集的维度不同,因此解决方案需要应用于任何维度,而不是固定在2D或3D。
然而,为了简单起见,我使用了2D的例子,因为它们很容易表示,但在理论上,数学应该成立。
重心坐标
我最初的想法是使用重心坐标,从笛卡尔坐标转换而来,就像done here一样,但我对这个方法的实现至少可以说是不可信的:
import numpy as np
import matplotlib.pyplot as plt
def is_in_simplex(point, T_inv, simplex):
first_n = np.matmul(
T_inv, (point - simplex[-1])
)
last = 1 - np.sum(first_n)
bary = np.concatenate((first_n, [last]))
return np.all((bary <= 1) & (bary >= 0))
# setup
simplex = np.array([[0, 0,], [8, 8,], [10, 3]])
rng = np.random.default_rng()
test_points = rng.random((10, 2))*10
# Maths starts here
T = np.array(simplex[:-1] - simplex[-1]).T
T_inv = np.linalg.inv(T)
within = np.vectorize(is_in_simplex, excluded={1, 2})(test_points, T_inv, simplex)
# drawing
polygon = np.concatenate([simplex, np.array([simplex[0]])])
print()
plt.plot(*polygon.T)
plt.scatter(*test_points.T)
for i, p in enumerate(test_points, 0):
print(f"{i}\t{p}\t{test_points[i]}\t{within[i]}")
plt.annotate(i, p)
字符串
其输出是:
0 [4.15391239 4.85852344] [4.15391239 4.85852344] [ True True]
1 [5.24829898 9.22879891] [5.24829898 9.22879891] [ True False]
2 [3.31255765 0.75891285] [3.31255765 0.75891285] [ True True]
3 [3.67468612 1.30045647] [3.67468612 1.30045647] [ True True]
4 [9.95049042 5.932782 ] [9.95049042 5.932782 ] [False True]
5 [8.42621723 6.35824573] [8.42621723 6.35824573] [False True]
6 [4.19569122 3.41275362] [4.19569122 3.41275362] [ True True]
7 [1.57324033 8.00273677] [1.57324033 8.00273677] [False False]
8 [1.9183791 0.54945207] [1.9183791 0.54945207] [ True True]
9 [0.52448473 7.77920839] [0.52448473 7.77920839] [False True]
型
x1c 0d1x的数据
第一列是索引,第二列是笛卡尔坐标,第三列 * 应该是 * 前两个重心坐标(应该假设它们加为1),第四列 * 应该 * 显示点是否位于单纯形内。
正如你可能已经注意到的,有几件事是错误的。点3、5和6应该被标记为在单形内,但是它们的重心坐标完全错误。由于它们被单形约束,重心坐标应大于0,但总和为1。is_in_simplex()
的输出是一个数组,而每个点都应该是一个布尔值。
不包括RNG、打印和绘图,10个点需要0.0383秒,100个点需要0.0487秒,1,000个点需要0.0994秒,10,000个点需要0.523秒。
线性规划
另一种方法是使用线性规划,但有些东西是关闭的,因为我的时间远远大于那些reported here(第二个答案,我用它作为起点)。
import numpy as np
from scipy.optimize import linprog
import time
def vectorizable(point, simplexT, coeffs):
b = np.r_[point, np.ones(1)]
lp = linprog(coeffs, A_eq = simplexT, b_eq = b)
return lp.success
dims = 2
rng = np.random.default_rng()
test_points = rng.random((10, dims))*10
simplex = np.array([[0, 0,], [8, 8,], [10, 3]])
coeffs = np.zeros(len(simplex))
simplex_T = np.r_[simplex.T,np.ones((1,len(simplex)))]
start_time = time.time()
in_simplex = np.vectorize(vectorizable,
excluded={1, 2},
signature="(n) -> ()")(test_points, simplex_T, coeffs)
print(f"----- {time.time() - start_time} seconds -----")
polygon = np.concatenate([simplex, np.array([simplex[0]])])
print()
plt.plot(*polygon.T)
plt.scatter(*test_points.T)
for i, p in enumerate(test_points, 0):
print(f"{i}\t{p}\t{in_simplex[i]}")
plt.annotate(i, p)
型
这一次,我得到了想要的结果:
----- 0.019016504287719727 seconds -----
0 [5.90479358 5.75174668] True
1 [0.51156474 0.86088186] False
2 [9.22371526 4.025967 ] True
3 [9.35307399 5.38630723] False
4 [2.83575442 5.66318545] False
5 [7.89786072 6.06068206] True
6 [0.09838826 1.38358132] False
7 [3.19776368 9.73562359] False
8 [9.9122709 0.76862067] False
9 [4.52352281 6.2259428 ] False
型
的
对于10、100和1,000个点,定时或多或少处于相同的数量级。然而,当我跳到10,000点时,我突然看到4到8秒之间的任何地方,这太慢了,只有当我增加维度时才增加到几十秒和分钟。
如前所述,我希望尽可能避免并行化。任何关于重心部分的帮助/建议都将非常感谢,特别是如果它可以工作,将比线性规划方法更快。有什么方法可以加速LP方法吗?
谢啦,谢啦
1条答案
按热度按时间whlutmcx1#
线性代数方法(我不认为这里需要LP)。使用一个矩阵乘法进行超平面half-space测试,然后进行一些max()和sign()后处理。
您可以通过在任何半空间测试之前执行直线修剪来变得更聪明,并在任何一个半空间测试失败时将矩阵乘法分区并停止。当一些重要部分的点在单纯形之外测试时,它帮助最大。在最极端的情况下,如果单纯形中不包含任何点(尝试半径=1.1),则非分区算法需要约0.6秒,而50个分区需要约0.01秒。
个字符