Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
582 views
in Technique[技术] by (71.8m points)

python - How to include constraint to Scipy NNLS function solution so that it sums to 1

I have the following code to solve non-negative least square. Using 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.

What I want to do is to apply a constraint on x solution so that the it sums to 1. How can I do it?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I don't think you can use nnls directly as the Fortran code it calls doesn't allow extra constraints. However, the constraint that the equation sums to one can be introduced as a third equation, so your example system is of the form,

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

As this is now a set of linear equations, the exact solution can be obtained from x=np.dot(np.linalg.inv(A),b) so that x=[0.6875, 0.3750, -0.0625]. This requires x3 to be negative. Therefore, there is no exact solution when x is positive to this problem.

For an approximate solution where x is constrained to be positive, this can be obtained using,

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))

which gives, x=[0.60003332, 0.34998889, 0.] with a x.sum()=0.95.

I think if you wanted a more general solution including sum constraints, you'd need to use minimise with explicit constraints/bounds in the following form,

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))

which gives x=[0.674999366, 0.325000634, 0.] and x.sum()=1. From minimise, the sum is correct but the value of x is not quite right with np.dot(A,x)=[ 69.75001902, 59.25005706].


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...