我有一个表达列表
from sympy import *
x = symbols('x')
e0 = x
e1 = x**2
e2 = 2*x**2
如何找到线性独立表达式的最大子集? 您可以假设要排序的表达式,即首选索引较低的表达式。
我尝试迭代以下内容:
a = numbered_symbols('a')
a0 = next(a)
a1 = next(a)
a2 = next(a)
solve(a0*e0 + a1*e1, a0, a1)
# {a0: 0, a1: 0}
solve(a0*e0 + a1*e1 + a2*e2, a0, a1, a2)
# {a1: -2*a2, a0: 0}
所以我拿e0和e1。要自动化:
from operator import mul
from toolz import take
def _linear_independent(exprs):
c = list(take(len(exprs), numbered_symbols("c")))
expr = sum(map(mul, exprs, c))
res = solve(expr, c)
return all(v == 0 for v in res.values())
def max_independent_set(exprs):
max_set = [exprs[0]]
for e in exprs[1:]:
if _linear_independent(max_set + [e]):
max_set.append(e)
return max_set
max_independent_set([e0, e1, e2]) # [x, x**2]
是否有更高效(运行时)的方法来执行此操作? 目前,我需要调用求解N-1并且系统解决正在增加。也许人们可以把它分解成更小的任务?
奖励:我也在寻找一种方法来使用多个自变量。我当前的方法不起作用(它不仅解决了系数):
x, y = symbols('x y')
e0 = x
e1 = y
exprs = [e0, e1]
c = list(take(len(exprs), numbered_symbols("c")))
expr = sum(map(mul, exprs, c))
res = solve(expr, c) # [{c0: -c1*y/x}]
我的表达式描述了来自R ^ N的函数 - > R.以前我会根据我的数据集对它们进行评估,并根据相关性进行排除。
答案 0 :(得分:2)
您可以使用一些矩阵例程来计算它。函数linear_eq_to_matrix
会将方程组转换为矩阵:
>>> A, b = linear_eq_to_matrix([x, x**2, 2*x**2], [x, x**2])
>>> pprint(A)
⎡1 0⎤
⎢ ⎥
⎢0 1⎥
⎢ ⎥
⎣0 2⎦
(如果你有恒定的因子,那么这些因素会被放在b
作为等式的右边)。这是您想要的转置,因为您想要的矩阵操作适用于列。 A.T.columnspace
将返回跨越A.T
:
>>> A, b = linear_eq_to_matrix([x, x**2, 2*x**2], [x, x**2])
>>> pprint(A.T.columnspace())
⎡⎡1⎤ ⎡0⎤⎤
⎢⎢ ⎥, ⎢ ⎥⎥
⎣⎣0⎦ ⎣1⎦⎦
这告诉您第一个和第二个元素跨越空间(因为您获得了A.T
的第一列和第二列)。如果您还想知道如何根据线性独立元素重写其他元素,请使用A.T.nullspace()
。
例如:
>>> pprint(A.T.nullspace())
⎡⎡0 ⎤⎤
⎢⎢ ⎥⎥
⎢⎢-2⎥⎥
⎢⎢ ⎥⎥
⎣⎣1 ⎦⎦
这意味着-2*(x**2) + 1*(2*x**2) = 0
(所以最后两个元素是线性无关的。
举一个更大的例子:
>>> A, b = linear_eq_to_matrix([x, 2*x, x**2, 2*x**2, x**3, x + x**2], [x, x**2, x**3])
>>> pprint(A.T)
⎡1 2 0 0 0 1⎤
⎢ ⎥
⎢0 0 1 2 0 1⎥
⎢ ⎥
⎣0 0 0 0 1 0⎦
>>> pprint(A.T.columnspace())
⎡⎡1⎤ ⎡0⎤ ⎡0⎤⎤
⎢⎢ ⎥ ⎢ ⎥ ⎢ ⎥⎥
⎢⎢0⎥, ⎢1⎥, ⎢0⎥⎥
⎢⎢ ⎥ ⎢ ⎥ ⎢ ⎥⎥
⎣⎣0⎦ ⎣0⎦ ⎣1⎦⎦
>>> pprint(A.T.nullspace())
⎡⎡-2⎤ ⎡0 ⎤ ⎡-1⎤⎤
⎢⎢ ⎥ ⎢ ⎥ ⎢ ⎥⎥
⎢⎢1 ⎥ ⎢0 ⎥ ⎢0 ⎥⎥
⎢⎢ ⎥ ⎢ ⎥ ⎢ ⎥⎥
⎢⎢0 ⎥ ⎢-2⎥ ⎢-1⎥⎥
⎢⎢ ⎥, ⎢ ⎥, ⎢ ⎥⎥
⎢⎢0 ⎥ ⎢1 ⎥ ⎢0 ⎥⎥
⎢⎢ ⎥ ⎢ ⎥ ⎢ ⎥⎥
⎢⎢0 ⎥ ⎢0 ⎥ ⎢0 ⎥⎥
⎢⎢ ⎥ ⎢ ⎥ ⎢ ⎥⎥
⎣⎣0 ⎦ ⎣0 ⎦ ⎣1 ⎦⎦
请注意,我们有3个nullspace的生成向量和3个列空间的生成向量,它们匹配rank-nullity theorem(3 + 3 = 6)。对于列空间,我们得到A.T
的第一,第二和第五列,这意味着它们是线性独立的元素(或者,我们可以将列乘以我们从中提取矩阵的术语向量,{ {1}}。
在nullspace中,每列中的最后Matrix([x, x**2, x**3]).T
表示可以删除的元素,上面的条件(实际上是它们的否定)告诉您如何根据其他条件重写它(例如{ {1}},1
,2*x = -(-2)*x
)。
这确实需要您从一系列表达式开始,这些表达式您考虑成为术语(在此示例中为2*x**2 = -(2)*x**2
)。这很重要。要从您对问题的评论中做出示例,x + x**2 = -(1)*x + -(1)*x**2
如果您的字词仅为[x, x**2, x**3]
,则线性相关,如果您的字词为[cos(x), cos(x)*sin(y)]
,则线性系统甚至不是线性系统(并且线性独立)线性系统,如果它们是[cos(x)]
。
答案 1 :(得分:0)
根据asmeurer的回答,我想出了一个解决方案。
使用nullspace效果很好,但你仍然需要找到独立/原子表达式列表:
import itertools as it
import operator
from functools import reduce
import sympy
def get_atomic(expr, normalize=True):
if isinstance(expr, sympy.Mul):
if isinstance(expr.args[0], sympy.Number):
args_ = expr.args[1:]
else:
args_ = expr.args
return reduce(operator.mul, args_)
elif isinstance(expr, (sympy.Float, sympy.Integer)):
return 1 if normalize else expr
else:
return expr
all_summands = ((get_atomic(s) for s in sympy.Add.make_args(e)) for e in exprs)
atomic = list(set(it.chain.from_iterable(all_summands)))
A, b = sympy.linear_eq_to_matrix(exprs, atomic)
redundant_indices = [np.where(sympy.matrix2numpy(nullvector) == 1) [0][-1]
for nullvector in A.T.nullspace()]
exprs_ = [e for i, e in enumerate(exprs) if i not in redundant_indices]
这适用于我的测试用例:
exprs = [x, x**2, x**3, x**3 - x**2]
# exprs_ = [x, x**2, x**3]
exprs = [cos(x), cos(x)*sin(y), sin(y)]
# exprs_ = [cos(x), cos(x)*sin(y), sin(y)]