python version of nnls/fnnls

Seems the last email didn’t deliver the attachement (even though I received it back?)

The attachment is here again but just in case, here’s the code as plain text:

#!/usr/bin/env python

fnnls.py (4.38 KB)

···

ported fnnls.m to fnnls.py

gjok - 20050816

import sys, math

import numpy, numpy.linalg.linalg as la

def any(X) : return len(numpy.nonzero(X)) != 0

def find(X) : return numpy.nonzero(X)

def norm(X, d) : return max(numpy.sum(abs(X)))

x, w = fnnls(XtX, Xty, tol)

def fnnls(XtX, Xty, tol = 0) :

#FNNLS Non-negative least-squares.

Adapted from NNLS of Mathworks, Inc.

[x,w] = nnls(X, y)

x, w = fnnls(XtX,Xty) returns the vector X that solves x = pinv(XtX)*Xty

in a least squares sense, subject to x >= 0.

Differently stated it solves the problem min ||y - Xx|| if

XtX = X’*X and Xty = X’*y.

A default tolerance of TOL = MAX(SIZE(XtX)) * NORM(XtX,1) * EPS

is used for deciding when elements of x are less than zero.

This can be overridden with x = fnnls(XtX,Xty,TOL).

[x,w] = fnnls(XtX,Xty) also returns dual vector w where

w(i) < 0 where x(i) = 0 and w(i) = 0 where x(i) > 0.

See also NNLS and FNNLSb

L. Shure 5-8-87

Revised, 12-15-88,8-31-89 LS.

(Partly) Copyright © 1984-94 by The MathWorks, Inc.

Modified by R. Bro 5-7-96 according to

Bro R., de Jong S., Journal of Chemometrics, 1997, 11, 393-401

Corresponds to the FNNLSa algorithm in the paper

Rasmus bro

Chemometrics Group, Food Technology

Dept. Dairy and Food Science

Royal Vet. & Agricultural

DK-1958 Frederiksberg C

Denmark

rb@…1118…

http://newton.foodsci.kvl.dk/users/rasmus.html

Reference:

Lawson and Hanson, “Solving Least Squares Problems”, Prentice-Hall, 1974.

initialize variables

m = XtX.shape[0]

n = XtX.shape[1]

if tol == 0 :

    eps = 2.2204e-16

    tol = 10 * eps * norm(XtX,1)*max(m, n);

#end

P = numpy.zeros(n, numpy.Int16)

P[:] = -1

Z = numpy.arange(0,n)

z = numpy.zeros(m, numpy.Float)

x = numpy.array(P)

ZZ = numpy.array(Z)

w = Xty - numpy.dot(XtX, x)

set up iteration criterion

iter = 0

itmax = 30 * n

outer loop to put variables into set to hold positive coefficients

while any(Z) and any(w[ZZ] > tol) :

    wt = w[ZZ].max()

    t = find(w[ZZ] == wt)

    t = t[-1:][0]

    t = ZZ[t]

    P[t] = t

    Z[t] = -1

    PP = find(P != -1)

    ZZ = find(Z != -1)

    if len(PP) == 1 :

        XtyPP = Xty[PP]

        XtXPP = XtX[PP, PP]

        z[PP] = XtyPP / XtXPP

    else :

        XtyPP = numpy.array(Xty[PP])

        XtXPP = numpy.array(XtX[PP, numpy.array(PP)[:, numpy.NewAxis]])

        z[PP] = numpy.dot(XtyPP, la.generalized_inverse(XtXPP))

    #end

    z[ZZ] = 0

inner loop to remove elements from the positive set which no longer belong

    while any(z[PP] <= tol) and (iter < itmax) :

        iter += 1

        iztol = find(z <= tol)

        ip = find(P[iztol] != -1)

        QQ = iztol[ip]

        if len(QQ) == 1 : alpha = x[QQ] / (x[QQ] - z[QQ])

        else :

            x_xz = x[QQ] / (x[QQ] - z[QQ])

            alpha = x_xz.min()

        #end

        x += alpha * (z - x)

        iabs = find(abs(x) < tol)

        ip = find(P[iabs] != -1)

        ij = iabs[ip]

        Z[ij] = numpy.array(ij)

        P[ij] = -1

        PP = find(P != -1)

        ZZ = find(Z != -1)

        if len(PP) == 1 :

            XtyPP = Xty[PP]

            XtXPP = XtX[PP, PP]

            z[PP] = XtyPP / XtXPP

        else :

            XtyPP = numpy.array(Xty[PP])

            XtXPP = numpy.array(XtX[PP, numpy.array(PP)[:, numpy.NewAxis]])

            z[PP] = numpy.dot(XtyPP, la.generalized_inverse(XtXPP))

        #endif

        z[ZZ] = 0

    #end while

    x = numpy.array(z)

    w = Xty - numpy.dot(XtX, x)

#end while

return x, w

#end def

if name == ‘main’ :

test [x, w] = fnnls(Xt.X, Xt.y, tol)

to solve min ||y - X.x|| s.t. x >= 0

matlab:lsqnonneg

X = [1, 10, 4, 10; 4, 5, 1, 12; 5, 1, 9, 20];

y = [4; 7; 4]

x = lsqnonneg(X, y) => x = [0.9312; 0.3683; 0; 0];

X = numpy.array([[1, 10, 4, 10], [4, 5, 1, 12], [5, 1, 9, 20]], numpy.Float)

y = numpy.array([4, 7, 4], numpy.Float)

if False :

    X = numpy.array([[1, 10, 4, 10],

                     [4, 5, 1, 12],

                     [5, 1, 9, 20],

                     [4, 3, 2, 1]], numpy.Float)

    y = numpy.array([4, 7, 4, 1], numpy.Float)

#end

if False :

    X = numpy.zeros((20, 20), numpy.Float)

    for n in range(20) : X[n,:] = numpy.arange(0.0, 400.0, step = 20)

    y = numpy.arange(0.0, 20.0)

#end

Xt = numpy.transpose(numpy.array(X))

x, w = fnnls(numpy.dot(Xt, X), numpy.dot(Xt, y))

print 'X = ', X

print 'y = ', y

print 'x = ', x

#end if name == ‘main

Begin forwarded message:

From: Graeme O’Keefe <gjok@…484…>

Date: 25 May 2006 8:46:07 PM

To: matplotlib user list matplotlib-users@lists.sourceforge.net

Subject: [Matplotlib-users] Fwd: python version of nnls/fnnls

Dear matplotlibers,

I seem to use Non Negative Least Squares every couple of months and the most recent problem I solved reminded me that I meant to post this last November.

fnnls.py is a port of fnnls.m which was written by Rasmus Bro (http://newton.foodsci.kvl.dk/users/rasmus.html, code available at http://www.ub.es/gesq/mcr/als/fnnls.m, and also available on Matlab Central).

I’ve retained Rasmus’ comments and translated the matlab to python/numpy.