【问题标题】:How to include constraint to Scipy NNLS function solution so that it sums to 1如何在 Scipy NNLS 函数解决方案中包含约束,使其总和为 1
【发布时间】:2015-10-28 08:14:23
【问题描述】:

我有以下代码来解决非负最小二乘。 使用scipy.nnls.

import numpy as np
from scipy.optimize import nnls 

A = np.array([[60, 90, 120], 
              [30, 120, 90]])

b = np.array([67.5, 60])

x, rnorm = nnls(A,b)

print x
#[ 0.          0.17857143  0.42857143]
# Now need to have this array sum to 1.

我想要做的是对 x 解决方案应用一个约束,使其总和为 1。我该怎么做?

【问题讨论】:

    标签: python numpy scipy


    【解决方案1】:

    我认为您不能直接使用nnls,因为它调用的Fortran code 不允许额外的约束。但是,方程总和为 1 的约束可以作为第三个方程引入,因此您的示例系统具有以下形式,

    60 x1 + 90  x2 + 120 x3 = 67.5
    30 x1 + 120 x2 +  90 x3 = 60
       x1 +     x2 +     x3 = 1
    

    由于现在这是一组线性方程,因此可以从x=np.dot(np.linalg.inv(A),b) 获得精确解,因此x=[0.6875, 0.3750, -0.0625]。这要求x3 为负数。因此,当x 对此问题持肯定态度时,没有确切的解决方案。

    对于x 被约束为正的近似解,可以使用以下方法获得,

    import numpy as np
    from scipy.optimize import nnls 
    
    #Define minimisation function
    def fn(x, A, b):
        return np.sum(A*x,1) - b
    
    #Define problem
    A = np.array([[60., 90., 120.], 
                  [30., 120., 90.],
                  [1.,  1.,   1. ]])
    
    b = np.array([67.5, 60., 1.])
    
    x, rnorm = nnls(A,b)
    
    print(x,x.sum(),fn(x,A,b))
    

    这给出了x=[0.60003332, 0.34998889, 0.]x.sum()=0.95

    我认为,如果您想要一个更通用的解决方案,包括总和约束,您需要使用以下形式的具有显式约束/边界的最小化,

    import numpy as np
    from scipy.optimize import minimize 
    from scipy.optimize import nnls 
    
    #Define problem
    A = np.array([[60, 90, 120], 
                  [30, 120, 90]])
    
    b = np.array([67.5, 60])
    
    #Use nnls to get initial guess
    x0, rnorm = nnls(A,b)
    
    #Define minimisation function
    def fn(x, A, b):
        return np.linalg.norm(A.dot(x) - b)
    
    #Define constraints and bounds
    cons = {'type': 'eq', 'fun': lambda x:  np.sum(x)-1}
    bounds = [[0., None],[0., None],[0., None]]
    
    #Call minimisation subject to these values
    minout = minimize(fn, x0, args=(A, b), method='SLSQP',bounds=bounds,constraints=cons)
    x = minout.x
    
    print(x,x.sum(),fn(x,A,b))
    

    给出x=[0.674999366, 0.325000634, 0.]x.sum()=1。从最小化来看,总和是正确的,但x 的值与np.dot(A,x)=[ 69.75001902, 59.25005706] 并不完全正确。

    【讨论】:

    • 在您的第一个代码块print(x,x.sum(),fn(x,A,b)) 中,fn 来自哪里?
    • 对不起,应该是第二个例子中的 fn (Ax-b)。我已经更正了。
    • 谢谢。想看看我的其他相关question
    • 我已将fn 更正为使用np.linalg.norm,就像@chthonicdaemon 回答您的链接问题一样,这样可以最大限度地避免错误。
    猜你喜欢
    • 2020-04-18
    • 2017-08-29
    • 1970-01-01
    • 1970-01-01
    • 2011-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多