1c       this file contains the following user-callable routines:
2c
3c
4c       routine iddp_aid computes the ID, to a specified precision,
5c       of an arbitrary matrix. This routine is randomized.
6c
7c       routine idd_estrank estimates the numerical rank,
8c       to a specified precision, of an arbitrary matrix.
9c       This routine is randomized.
10c
11c
12ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
13c
14c
15c
16c
17        subroutine iddp_aid(eps,m,n,a,work,krank,list,proj)
18c
19c       computes the ID of the matrix a, i.e., lists in list
20c       the indices of krank columns of a such that
21c
22c       a(j,list(k))  =  a(j,list(k))
23c
24c       for all j = 1, ..., m; k = 1, ..., krank, and
25c
26c                        krank
27c       a(j,list(k))  =  Sigma  a(j,list(l)) * proj(l,k-krank)       (*)
28c                         l=1
29c
30c                     +  epsilon(j,k-krank)
31c
32c       for all j = 1, ..., m; k = krank+1, ..., n,
33c
34c       for some matrix epsilon dimensioned epsilon(m,n-krank)
35c       such that the greatest singular value of epsilon
36c       <= the greatest singular value of a * eps.
37c
38c       input:
39c       eps -- precision to which the ID is to be computed
40c       m -- first dimension of a
41c       n -- second dimension of a
42c       a -- matrix to be decomposed; the present routine does not
43c            alter a
44c       work -- initialization array that has been constructed
45c               by routine idd_frmi
46c
47c       output:
48c       krank -- numerical rank of a to precision eps
49c       list -- indices of the columns in the ID
50c       proj -- matrix of coefficients needed to interpolate
51c               from the selected columns to the other columns
52c               in the original matrix being ID'd;
53c               proj doubles as a work array in the present routine, so
54c               proj must be at least n*(2*n2+1)+n2+1 real*8 elements
55c               long, where n2 is the greatest integer less than
56c               or equal to m, such that n2 is a positive integer
57c               power of two.
58c
59c       _N.B._: The algorithm used by this routine is randomized.
60c               proj must be at least n*(2*n2+1)+n2+1 real*8 elements
61c               long, where n2 is the greatest integer less than
62c               or equal to m, such that n2 is a positive integer
63c               power of two.
64c
65c       reference:
66c       Halko, Martinsson, Tropp, "Finding structure with randomness:
67c            probabilistic algorithms for constructing approximate
68c            matrix decompositions," SIAM Review, 53 (2): 217-288,
69c            2011.
70c
71        implicit none
72        integer m,n,list(n),krank,kranki,n2
73        real*8 eps,a(m,n),proj(*),work(17*m+70)
74c
75c
76c       Allocate memory in proj.
77c
78        n2 = work(2)
79c
80c
81c       Find the rank of a.
82c
83        call idd_estrank(eps,m,n,a,work,kranki,proj)
84c
85c
86        if(kranki .eq. 0) call iddp_aid0(eps,m,n,a,krank,list,proj,
87     1                                   proj(m*n+1))
88c
89        if(kranki .ne. 0) call iddp_aid1(eps,n2,n,kranki,proj,
90     1                                   krank,list,proj(n2*n+1))
91c
92c
93        return
94        end
95c
96c
97c
98c
99        subroutine iddp_aid0(eps,m,n,a,krank,list,proj,rnorms)
100c
101c       uses routine iddp_id to ID a without modifying its entries
102c       (in contrast to the usual behavior of iddp_id).
103c
104c       input:
105c       eps -- precision of the decomposition to be constructed
106c       m -- first dimension of a
107c       n -- second dimension of a
108c
109c       output:
110c       krank -- numerical rank of the ID
111c       list -- indices of the columns in the ID
112c       proj -- matrix of coefficients needed to interpolate
113c               from the selected columns to the other columns in a;
114c               proj doubles as a work array in the present routine, so
115c               must be at least m*n real*8 elements long
116c
117c       work:
118c       rnorms -- must be at least n real*8 elements long
119c
120c       _N.B._: proj must be at least m*n real*8 elements long
121c
122        implicit none
123        integer m,n,krank,list(n),j,k
124        real*8 eps,a(m,n),proj(m,n),rnorms(n)
125c
126c
127c       Copy a into proj.
128c
129        do k = 1,n
130          do j = 1,m
131            proj(j,k) = a(j,k)
132          enddo ! j
133        enddo ! k
134c
135c
136c       ID proj.
137c
138        call iddp_id(eps,m,n,proj,krank,list,rnorms)
139c
140c
141        return
142        end
143c
144c
145c
146c
147        subroutine iddp_aid1(eps,n2,n,kranki,proj,krank,list,rnorms)
148c
149c       IDs the uppermost kranki x n block of the n2 x n matrix
150c       input as proj.
151c
152c       input:
153c       eps -- precision of the decomposition to be constructed
154c       n2 -- first dimension of proj as input
155c       n -- second dimension of proj as input
156c       kranki -- number of rows to extract from proj
157c       proj -- matrix containing the kranki x n block to be ID'd
158c
159c       output:
160c       proj -- matrix of coefficients needed to interpolate
161c               from the selected columns to the other columns
162c               in the original matrix being ID'd
163c       krank -- numerical rank of the ID
164c       list -- indices of the columns in the ID
165c
166c       work:
167c       rnorms -- must be at least n real*8 elements long
168c
169        implicit none
170        integer n,n2,kranki,krank,list(n),j,k
171        real*8 eps,proj(n2*n),rnorms(n)
172c
173c
174c       Move the uppermost kranki x n block of the n2 x n matrix proj
175c       to the beginning of proj.
176c
177        do k = 1,n
178          do j = 1,kranki
179            proj(j+kranki*(k-1)) = proj(j+n2*(k-1))
180          enddo ! j
181        enddo ! k
182c
183c
184c       ID proj.
185c
186        call iddp_id(eps,kranki,n,proj,krank,list,rnorms)
187c
188c
189        return
190        end
191c
192c
193c
194c
195        subroutine idd_estrank(eps,m,n,a,w,krank,ra)
196c
197c       estimates the numerical rank krank of an m x n matrix a
198c       to precision eps. This routine applies n2 random vectors
199c       to a, obtaining ra, where n2 is the greatest integer
200c       less than or equal to m such that n2 is a positive integer
201c       power of two. krank is typically about 8 higher than
202c       the actual numerical rank.
203c
204c       input:
205c       eps -- precision defining the numerical rank
206c       m -- first dimension of a
207c       n -- second dimension of a
208c       a -- matrix whose rank is to be estimated
209c       w -- initialization array that has been constructed
210c            by routine idd_frmi
211c
212c       output:
213c       krank -- estimate of the numerical rank of a;
214c                this routine returns krank = 0 when the actual
215c                numerical rank is nearly full (that is,
216c                greater than n - 8 or n2 - 8)
217c       ra -- product of an n2 x m random matrix and the m x n matrix
218c             a, where n2 is the greatest integer less than or equal
219c             to m such that n2 is a positive integer power of two;
220c             ra doubles as a work array in the present routine, and so
221c             must be at least n*n2+(n+1)*(n2+1) real*8 elements long
222c
223c       _N.B._: ra must be at least n*n2+(n2+1)*(n+1) real*8
224c               elements long for use in the present routine
225c               (here, n2 is the greatest integer less than or equal
226c               to m, such that n2 is a positive integer power of two).
227c               This routine returns krank = 0 when the actual
228c               numerical rank is nearly full.
229c
230        implicit none
231        integer m,n,krank,n2,irat,lrat,iscal,lscal,ira,lra,lra2
232        real*8 eps,a(m,n),ra(*),w(17*m+70)
233c
234c
235c       Extract from the array w initialized by routine idd_frmi
236c       the greatest integer less than or equal to m that is
237c       a positive integer power of two.
238c
239        n2 = w(2)
240c
241c
242c       Allocate memory in ra.
243c
244        lra = 0
245c
246        ira = lra+1
247        lra2 = n2*n
248        lra = lra+lra2
249c
250        irat = lra+1
251        lrat = n*(n2+1)
252        lra = lra+lrat
253c
254        iscal = lra+1
255        lscal = n2+1
256        lra = lra+lscal
257c
258        call idd_estrank0(eps,m,n,a,w,n2,krank,ra(ira),ra(irat),
259     1                    ra(iscal))
260c
261c
262        return
263        end
264c
265c
266c
267c
268        subroutine idd_estrank0(eps,m,n,a,w,n2,krank,ra,rat,scal)
269c
270c       routine idd_estrank serves as a memory wrapper
271c       for the present routine. (Please see routine idd_estrank
272c       for further documentation.)
273c
274        implicit none
275        integer m,n,n2,krank,ifrescal,k,nulls,j
276        real*8 a(m,n),ra(n2,n),scal(n2+1),eps,residual,
277     1         w(17*m+70),rat(n,n2+1),ss,ssmax
278c
279c
280c       Apply the random matrix to every column of a, obtaining ra.
281c
282        do k = 1,n
283          call idd_frm(m,n2,w,a(1,k),ra(1,k))
284        enddo ! k
285c
286c
287c       Compute the sum of squares of the entries in each column of ra
288c       and the maximum of all such sums.
289c
290        ssmax = 0
291c
292        do k = 1,n
293c
294          ss = 0
295          do j = 1,m
296            ss = ss+a(j,k)**2
297          enddo ! j
298c
299          if(ss .gt. ssmax) ssmax = ss
300c
301        enddo ! k
302c
303c
304c       Transpose ra to obtain rat.
305c
306        call idd_atransposer(n2,n,ra,rat)
307c
308c
309        krank = 0
310        nulls = 0
311c
312c
313c       Loop until nulls = 7, krank+nulls = n2, or krank+nulls = n.
314c
315 1000   continue
316c
317c
318          if(krank .gt. 0) then
319c
320c           Apply the previous Householder transformations
321c           to rat(:,krank+1).
322c
323            ifrescal = 0
324c
325            do k = 1,krank
326              call idd_houseapp(n-k+1,rat(1,k),rat(k,krank+1),
327     1                          ifrescal,scal(k),rat(k,krank+1))
328            enddo ! k
329c
330          endif ! krank .gt. 0
331c
332c
333c         Compute the Householder vector associated
334c         with rat(krank+1:*,krank+1).
335c
336          call idd_house(n-krank,rat(krank+1,krank+1),
337     1                   residual,rat(1,krank+1),scal(krank+1))
338          residual = abs(residual)
339c
340c
341          krank = krank+1
342          if(residual .le. eps*sqrt(ssmax)) nulls = nulls+1
343c
344c
345        if(nulls .lt. 7 .and. krank+nulls .lt. n2
346     1   .and. krank+nulls .lt. n)
347     2   goto 1000
348c
349c
350        if(nulls .lt. 7) krank = 0
351c
352c
353        return
354        end
355c
356c
357c
358c
359        subroutine idd_atransposer(m,n,a,at)
360c
361c       transposes a to obtain at.
362c
363c       input:
364c       m -- first dimension of a, and second dimension of at
365c       n -- second dimension of a, and first dimension of at
366c       a -- matrix to be transposed
367c
368c       output:
369c       at -- transpose of a
370c
371        implicit none
372        integer m,n,j,k
373        real*8 a(m,n),at(n,m)
374c
375c
376        do k = 1,n
377          do j = 1,m
378c
379            at(k,j) = a(j,k)
380c
381          enddo ! j
382        enddo ! k
383c
384c
385        return
386        end
387