random_mvnorm Subroutine

subroutine random_mvnorm(n, h, d, f, first, x, ier)

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: n
real, intent(in) :: h(:)
real, intent(in) :: d(:)
real, intent(inout) :: f(:)
logical, intent(in) :: first
real, intent(out) :: x(:)
integer, intent(out) :: ier

Calls

proc~~random_mvnorm~~CallsGraph proc~random_mvnorm random_mvnorm random_normal random_normal proc~random_mvnorm->random_normal

Source Code

SUBROUTINE random_mvnorm(n, h, d, f, first, x, ier)
  ! Adapted from Fortran 77 code from the book:
  !     Dagpunar, J. 'Principles of random variate generation'
  !     Clarendon Press, Oxford, 1988.   ISBN 0-19-852202-9
  ! N.B. An extra argument, ier, has been added to Dagpunar's routine
  !     SUBROUTINE GENERATES AN N VARIATE RANDOM NORMAL
  !     VECTOR USING A CHOLESKY DECOMPOSITION.
  ! ARGUMENTS:
  !        N = NUMBER OF VARIATES IN VECTOR
  !           (INPUT,INTEGER >= 1)
  !     H(J) = J'TH ELEMENT OF VECTOR OF MEANS
  !           (INPUT,REAL)
  !     X(J) = J'TH ELEMENT OF DELIVERED VECTOR
  !           (OUTPUT,REAL)
  !
  !    D(J*(J-1)/2+I) = (I,J)'TH ELEMENT OF VARIANCE MATRIX (J> = I)
  !            (INPUT,REAL)
  !    F((J-1)*(2*N-J)/2+I) = (I,J)'TH ELEMENT OF LOWER TRIANGULAR
  !           DECOMPOSITION OF VARIANCE MATRIX (J <= I)
  !            (OUTPUT,REAL)
  !    FIRST = .TRUE. IF THIS IS THE FIRST CALL OF THE ROUTINE
  !    OR IF THE DISTRIBUTION HAS CHANGED SINCE THE LAST CALL OF THE ROUTINE.
  !    OTHERWISE SET TO .FALSE.
  !            (INPUT,LOGICAL)
  !    ier = 1 if the input covariance matrix is not +ve definite
  !        = 0 otherwise
  INTEGER, INTENT(IN)   :: n
  REAL, INTENT(IN)      :: h(:), d(:)   ! d(n*(n+1)/2)
  REAL, INTENT(IN OUT)  :: f(:)         ! f(n*(n+1)/2)
  REAL, INTENT(OUT)     :: x(:)
  LOGICAL, INTENT(IN)   :: first
  INTEGER, INTENT(OUT)  :: ier
  !     Local variables
  INTEGER       :: j, i, m
  REAL          :: y, v
  INTEGER, SAVE :: n2
  IF (n < 1) THEN
     WRITE(*, *) 'SIZE OF VECTOR IS NON POSITIVE'
     STOP
  END IF
  ier = 0
  IF (first) THEN                        ! Initialization, if necessary
     n2 = 2*n
     IF (d(1) < zero) THEN
        ier = 1
        RETURN
     END IF
     f(1) = SQRT(d(1))
     y = one/f(1)
     DO j = 2,n
        f(j) = d(1+j*(j-1)/2) * y
     END DO
     DO i = 2,n
        v = d(i*(i-1)/2+i)
        DO m = 1,i-1
           v = v - f((m-1)*(n2-m)/2+i)**2
        END DO
        IF (v < zero) THEN
           ier = 1
           RETURN
        END IF
        v = SQRT(v)
        y = one/v
        f((i-1)*(n2-i)/2+i) = v
        DO j = i+1,n
           v = d(j*(j-1)/2+i)
           DO m = 1,i-1
              v = v - f((m-1)*(n2-m)/2+i)*f((m-1)*(n2-m)/2 + j)
           END DO ! m = 1,i-1
           f((i-1)*(n2-i)/2 + j) = v*y
        END DO ! j = i+1,n
     END DO ! i = 2,n
  END IF
  x(1:n) = h(1:n)
  DO j = 1,n
     y = random_normal()
     DO i = j,n
        x(i) = x(i) + f((j-1)*(n2-j)/2 + i) * y
     END DO ! i = j,n
  END DO ! j = 1,n
  RETURN
END SUBROUTINE random_mvnorm