找到最大线性独立表达式子集的最有效方法是什么

时间:2017-03-24 20:43:36

标签: python sympy

我有一个表达列表

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.以前我会根据我的数据集对它们进行评估,并根据相关性进行排除。

2 个答案:

答案 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}},12*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)]