summaryrefslogtreecommitdiff
path: root/Utilib/src/ALNNLS.f
diff options
context:
space:
mode:
Diffstat (limited to 'Utilib/src/ALNNLS.f')
-rw-r--r--Utilib/src/ALNNLS.f301
1 files changed, 301 insertions, 0 deletions
diff --git a/Utilib/src/ALNNLS.f b/Utilib/src/ALNNLS.f
new file mode 100644
index 0000000..aa25a16
--- /dev/null
+++ b/Utilib/src/ALNNLS.f
@@ -0,0 +1,301 @@
+*DECK ALNNLS
+ SUBROUTINE ALNNLS(A,M,N,MP,NP,B,X,RNORM,MODE)
+*
+*-----------------------------------------------------------------------
+*
+*Purpose:
+* implementation of the nonnegative least square algorithm.
+*
+*Author(s): A. Miller
+*
+*Parameters: input
+* A input rectangular matrix.
+* M,N first/second mathematical dimension of matrix A.
+* MP,NP first/second physical dimension of matrix A.
+* B source M-vector.
+*
+*Parameters: output
+* A product matrix, Q*A, where Q IS AN M x M orthogonal matrix
+* generated implicitly by this subroutine.
+* B product Q*B.
+* X solution N-vector.
+* RNORM Euclidean norm of the residual vector.
+* MODE success-failure flag (1: the solution has been computed
+* successfully; 2: the dimensions of the problem are
+* inconsistent, either m.LE.0 or n.LE.0; 3: iteration count
+* exceeded with more than 10*n iterations).
+*
+*Reference:
+* Chen, Donghui; Plemmons, Robert J. (2009). Nonnegativity constraints
+* in numerical analysis. Symposium on the Birth of Numerical Analysis.
+*
+*-----------------------------------------------------------------------
+*
+ IMPLICIT NONE
+*----
+* Subroutine arguments
+*----
+ INTEGER M, N, MP, NP, MODE
+ DOUBLE PRECISION A(MP,NP), B(M), X(N), RNORM
+*----
+* Local variables
+*----
+ INTEGER I, II, IP, ITER, ITMAX, IZ, IZ1, IZ2, IZMAX, J, JJ, JZ,
+ > L, MDA, NPP1, NSETP
+ DOUBLE PRECISION DUMMY(1), ALPHA, ASAVE, CC, FACTOR, SM, SS, T,
+ > TEMP, UNORM, UP, WMAX, ZTEST, XR, YR
+ INTEGER, ALLOCATABLE, DIMENSION(:) :: INDX
+ DOUBLE PRECISION, ALLOCATABLE, DIMENSION(:) :: W, ZZ
+ PARAMETER(FACTOR = 0.01D0)
+*
+ MODE = 1
+ IF(M.LE.0 .OR. N.LE.0) THEN
+ MODE = 2
+ RETURN
+ ENDIF
+ ITER = 0
+ ITMAX = 10*N
+ ALLOCATE(INDX(N),W(N), ZZ(M))
+*----
+* Initialize the arrays INDX() and X().
+*----
+ DO I = 1,N
+ X(I) = 0.0D0
+ INDX(I) = I
+ ENDDO
+ IZ2 = N
+ IZ1 = 1
+ NSETP = 0
+ NPP1 = 1
+*----
+* ****** MAIN LOOP BEGINS HERE ******
+* Quit if all coefficients are already in the solution or if M cols of
+* A have been triangularized.
+*----
+ 30 IF(IZ1.GT.IZ2 .OR. NSETP.GE.M) GO TO 350
+*----
+* Compute components of the dual (negative gradient) vector W.
+*----
+ DO IZ = IZ1,IZ2
+ J = INDX(IZ)
+ W(J) = DOT_PRODUCT(A(NPP1:M,J), B(NPP1:M))
+ ENDDO
+*----
+* Find largest positive W(J).
+*----
+ 60 IZMAX = 0
+ WMAX = 0.0D0
+ DO IZ = IZ1,IZ2
+ J = INDX(IZ)
+ IF(W(J).GT.WMAX) THEN
+ WMAX = W(J)
+ IZMAX = IZ
+ ENDIF
+ ENDDO
+*----
+* If WMAX.le.0. go to termination. This indicates satisfaction of the
+* Kuhn-Tucker conditions.
+*----
+ IF(WMAX.LE.0.0D0) GO TO 350
+ IZ = IZMAX
+ J = INDX(IZ)
+*----
+* The sign of W(J) is ok for J to be moved to set P. Begin the
+* transformation and check new diagonal element to avoid near linear
+* dependence.
+*----
+ ASAVE = A(NPP1,J)
+ CALL ALH12(1, NPP1, NPP1+1, M, A(:,J), UP, DUMMY(1), 1, 1, 0)
+ UNORM = 0.0D0
+ IF(NSETP.NE.0) UNORM = SUM( A(1:NSETP,J)**2 )
+ UNORM = SQRT(UNORM)
+ IF(UNORM + ABS(A(NPP1,J))*FACTOR - UNORM .GT. 0.0D0) THEN
+*----
+* Col J is sufficiently independent. Copy B into ZZ, update ZZ
+* and solve for ZTEST ( = proposed new value for X(J) ).
+*----
+ ZZ(1:M) = B(1:M)
+ CALL ALH12(2, NPP1, NPP1+1, M, A(:,J), UP, ZZ(1), 1, 1, 1)
+ ZTEST = ZZ(NPP1)/A(NPP1,J)
+*----
+* See if ZTEST is positive.
+*----
+ IF(ZTEST.GT.0.0D0) GO TO 140
+ ENDIF
+*----
+* Reject J as a candidate to be moved from set Z to set P. Restore
+* A(NPP1,J), set W(J) = 0., and loop back to test dual coeffs again.
+*----
+ A(NPP1,J) = ASAVE
+ W(J) = 0.0D0
+ GO TO 60
+*----
+* The index J = INDX(IZ) has been selected to be moved from
+* set Z to set P. Update B, update indices, apply Householder
+* transformations to cols in new set Z, sero subdiagonal elts in
+* col J, set W(J) = 0.
+*----
+ 140 B(1:M) = ZZ(1:M)
+ INDX(IZ) = INDX(IZ1)
+ INDX(IZ1) = J
+ IZ1 = IZ1+1
+ NSETP = NPP1
+ NPP1 = NPP1+1
+ MDA = SIZE(A,1)
+ JJ = 0
+ IF(IZ1.LE.IZ2) THEN
+ DO JZ = IZ1,IZ2
+ JJ = INDX(JZ)
+ CALL ALH12(2, NSETP, NPP1, M, A(:,J), UP, A(1,JJ), 1, MDA, 1)
+ ENDDO
+ ENDIF
+ IF(NSETP.NE.M) A(NPP1:M,J) = 0.0D0
+ W(J) = 0.0D0
+*----
+* Solve the triangular system. Store the solution temporarily in ZZ().
+*----
+ DO L = 1, NSETP
+ IP = NSETP+1-L
+ IF(L .NE. 1) ZZ(1:IP) = ZZ(1:IP) - A(1:IP,JJ)*ZZ(IP+1)
+ JJ = INDX(IP)
+ ZZ(IP) = ZZ(IP) / A(IP,JJ)
+ ENDDO
+*----
+* ****** SECONDARY LOOP BEGINS HERE ******
+*----
+ 210 ITER = ITER+1
+ IF(ITER.GT.ITMAX) THEN
+ MODE = 3
+ WRITE (*,'(/A)') ' NNLS QUITTING ON ITERATION COUNT.'
+ GO TO 350
+ ENDIF
+*----
+* See if all new constrained coeffs are feasible; if not compute ALPHA.
+*----
+ ALPHA = 2.0D0
+ DO IP = 1,NSETP
+ L = INDX(IP)
+ IF(ZZ(IP).LE.0.0D0) THEN
+ T = -X(L)/(ZZ(IP)-X(L))
+ IF(ALPHA.GT.T) THEN
+ ALPHA = T
+ JJ = IP
+ ENDIF
+ ENDIF
+ ENDDO
+*----
+* If all new constrained coeffs are feasible then ALPHA will still be
+* equal to 2. If so exit from secondary loop to main loop.
+*----
+ IF(ALPHA == 2.0D0) GO TO 330
+*----
+* Otherwise use ALPHA which will be between 0. and 1. to interpolate
+* between the old X and the new ZZ.
+*----
+ DO IP = 1,NSETP
+ L = INDX(IP)
+ X(L) = X(L) + ALPHA*(ZZ(IP)-X(L))
+ ENDDO
+*----
+* Modify A and B and the index arrays to move coefficient I from set
+* P to set Z.
+*----
+ I = INDX(JJ)
+ 260 X(I) = 0.0D0
+*----
+* Compute.. matrix (C, S) so that (C, S)(A) = (SQRT(A**2+B**2))
+* (-S,C) (-S,C)(B) ( 0 )
+* Compute SIG = SQRT(A**2+B**2)
+* SIG is computed last to allow for the possibility that SIG
+* may be in the same location as A OR B .
+*----
+ IF(JJ.NE.NSETP) THEN
+ JJ = JJ+1
+ DO J = JJ,NSETP
+ II = INDX(J)
+ INDX(J-1) = II
+ IF(ABS(A(J-1,II)).GT.ABS(A(J,II))) THEN
+ XR = A(J,II) / A(J-1,II)
+ YR = SQRT(1.0D0 + XR**2)
+ CC = SIGN(1.0D0/YR, A(J-1,II))
+ SS = CC * XR
+ A(J-1,II) = ABS(A(J-1,II)) * YR
+ ELSE IF(A(J,II).NE.0.D0) THEN
+ XR = A(J-1,II) / A(J,II)
+ YR = SQRT(1.0D0 + XR**2)
+ SS = SIGN(1.0D0/YR, A(J,II))
+ CC = SS * XR
+ A(J-1,II) = ABS(A(J,II)) * YR
+ ELSE
+ CC = 0.0D0
+ SS = 1.0D0
+ ENDIF
+ A(J,II) = 0.0D0
+ DO L = 1,N
+ IF(L.NE.II) THEN
+*----
+* Apply procedure G2 (CC,SS,A(J-1,L),A(J,L))
+*----
+ TEMP = A(J-1,L)
+ A(J-1,L) = CC*TEMP + SS*A(J,L)
+ A(J,L) = -SS*TEMP + CC*A(J,L)
+ ENDIF
+ ENDDO
+*----
+* Apply procedure G2 (CC,SS,B(J-1),B(J))
+*----
+ TEMP = B(J-1)
+ B(J-1) = CC*TEMP + SS*B(J)
+ B(J) = -SS*TEMP + CC*B(J)
+ ENDDO
+ ENDIF
+ NPP1 = NSETP
+ NSETP = NSETP-1
+ IZ1 = IZ1-1
+ INDX(IZ1) = I
+*----
+* See if the remaining coeffs in set P are feasible. They should be
+* because of the way ALPHA was determined. if any are infeasible it is
+* due to round-off error. any that are nonpositive will be set to 0.0d0
+* and moved from set P to set Z.
+*----
+ DO JJ = 1,NSETP
+ I = INDX(JJ)
+ IF(X(I).LE.0.0D0) GO TO 260
+ ENDDO
+*----
+* Copy B( ) into ZZ( ). Then solve again and loop back.
+*----
+ ZZ(1:M) = B(1:M)
+ DO L = 1, NSETP
+ IP = NSETP+1-L
+ IF(L .NE. 1) ZZ(1:IP) = ZZ(1:IP) - A(1:IP,JJ)*ZZ(IP+1)
+ JJ = INDX(IP)
+ ZZ(IP) = ZZ(IP) / A(IP,JJ)
+ ENDDO
+ GO TO 210
+*----
+* ****** End of secondary loop ******
+*----
+ 330 DO IP = 1,NSETP
+ I = INDX(IP)
+ X(I) = ZZ(IP)
+ ENDDO
+*----
+* All new coeffs are positive. Loop back to beginning.
+*----
+ GO TO 30
+*----
+* Come to here for termination. Compute the norm of the final residual
+* vector.
+*----
+ 350 SM = 0.0D0
+ IF(NPP1.LE.M) THEN
+ SM = SUM( B(NPP1:M)**2 )
+ ELSE
+ W(1:N) = 0.0D0
+ ENDIF
+ RNORM = SQRT(SM)
+ DEALLOCATE(ZZ,W,INDX)
+ RETURN
+ END