source: trunk/SRC/Matrix/cmapply.pro @ 295

Last change on this file since 295 was 262, checked in by pinsard, 17 years ago

corrections of some headers and parameters and keywords case. change of pro2href to replace proidl

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 13.0 KB
Line 
1;+
2;
3; @file_comments
4; Utility function, adapted from CMPRODUCT
5;
6; @param X
7;
8; @version
9; $Id$
10;
11; @todo seb
12;-
13;
14FUNCTION cmapply_product, x
15;
16  compile_opt idl2, strictarrsubs
17;
18  sz = size(x)
19  n = sz[1]
20
21  while n GT 1 do begin
22      if (n mod 2) EQ 1 then x[0,*] = x[0,*] * x[n-1,*]
23      n2 = floor(n/2)
24      x = x[0:n2-1,*] * x[n2:*,*]
25      n = n2
26  endwhile
27  return, reform(x[0,*], /overwrite)
28end
29
30;+
31;
32; @file_comments
33; cmapply_redim : Utility function, used to collect collaped dimensions
34;
35; @param newarr
36;
37; @param dimapply
38;
39; @param dimkeep
40;
41; @param nkeep
42;
43; @param totcol
44;
45; @param totkeep
46;
47; @todo seb
48;
49;-
50;
51PRO cmapply_redim, newarr, dimapply, dimkeep, nkeep, totcol, totkeep
52;
53  compile_opt idl2, strictarrsubs
54;
55  sz = size(newarr)
56  ;; First task: rearrange dimensions so that the dimensions
57  ;; that are "kept" (ie, uncollapsed) are at the back
58  dimkeep = where(histogram(dimapply,min=1,max=sz[0]) ne 1, nkeep)
59  if nkeep EQ 0 then return
60
61  newarr = transpose(temporary(newarr), [dimapply-1, dimkeep])
62  ;; totcol is the total number of collapsed elements
63  totcol = sz[dimapply[0]]
64  for i = 1, n_elements(dimapply)-1 do totcol = totcol * sz[dimapply[i]]
65  totkeep = sz[dimkeep[0]+1]
66  for i = 1, n_elements(dimkeep)-1 do totkeep = totkeep * sz[dimkeep[i]+1]
67
68  ;; this new array has two dimensions:
69  ;;   * the first, all elements that will be collapsed
70  ;;   * the second, all dimensions that will be preserved
71  ;; (the ordering is so that all elements to be collapsed are
72  ;;  adjacent in memory)
73  newarr = reform(newarr, [totcol, totkeep], /overwrite)
74end
75
76;Main function
77;+
78;
79; @file_comments
80; Applies a function to specified dimensions of an array
81;
82; Description:
83;
84; CMAPPLY will apply one of a few select functions to specified
85; dimensions of an array.  Unlike some IDL functions, you *do* have
86; a choice of which dimensions that are to be "collapsed" by this
87; function.  Iterative loops are avoided where possible, for
88; performance reasons.
89;
90;   The possible functions are:             (and number of loop iterations:)
91;     +     - Performs a sum (as in TOTAL)       number of collapsed dimensions
92;     AND   - Finds LOGICAL "AND" (not bitwise)  same
93;     OR    - Finds LOGICAL "OR"  (not bitwise)  same
94;     *     - Performs a product                 LOG_2[no. of collapsed elts.]
95;
96;     MIN   - Finds the minimum value            smaller of no. of collapsed
97;     MAX   - Finds the maximum value            or output elements
98;
99;     USER  - Applies user-defined function      no. of output elements
100;
101;
102;   It is possible to perform user-defined operations arrays using
103;   CMAPPLY.  The OP parameter is set to 'USER:FUNCTNAME', where
104;   FUNCTNAME is the name of a user-defined function.  The user
105;   defined function should be defined such that it accepts a single
106;   parameter, a vector, and returns a single scalar value.  Here is a
107;   prototype for the function definition:
108;
109;      FUNCTION FUNCTNAME, x, KEYWORD1=key1, ...
110;         scalar = ... function of x or keywords ...
111;         RETURN, scalar
112;      END
113;
114;   The function may accept keywords.  Keyword values are passed in to
115;   CMAPPLY through the FUNCTARGS keywords parameter, and passed to
116;   the user function via the _EXTRA mechanism.  Thus, while the
117;   definition of the user function is highly constrained in the
118;   number of positional parameters, there is absolute freedom in
119;   passing keyword parameters.
120;
121;   It's worth noting however, that the implementation of user-defined
122;   functions is not particularly optimized for speed. Users are
123;   encouraged to implement their own array if the number of output
124;   elements is large.
125;
126; @categories
127; Array
128;
129; @param OP {in}{required}{type=string}
130; The operation to perform, as a string.  May be upper or lower case.
131;
132; If a user-defined operation is to be passed, then OP is of
133; the form, 'USER:FUNCTNAME', where FUNCTNAME is the name of
134; the user-defined function.
135;
136; @param ARRAY {in}{required}{type=array}
137; An array of values to be operated on.
138; Must not be of type STRING (7) or STRUCTURE (8).
139;
140; @param dimapply {in}{optional}{default=1 (ie, first dimension)}{type=array}
141; An array of dimensions that are to be "collapsed", where
142; the first dimension starts with 1 (ie, same convention
143; as IDL function TOTAL).  Whereas TOTAL only allows one
144; dimension to be added, you can specify multiple dimensions
145; to CMAPPLY.  Order does not matter, since all operations
146; are associative and transitive.  NOTE: the dimensions refer
147; to the *input* array, not the output array.  IDL allows a
148; maximum of 8 dimensions.
149;
150; @keyword DOUBLE {default=not set}
151; Set this if you wish the internal computations to be done
152; in double precision if necessary.  If ARRAY is double
153; precision (real or complex) then DOUBLE=1 is implied.
154;
155; @keyword TYPE {default=same as input type}
156; Set this to the IDL code of the desired output type (refer
157; to documentation of <proidl>SIZE</proidl>). Internal results will be
158; rounded to the nearest integer if the output type is an
159; integer type.
160;
161; @keyword FUNCTARGS
162; If OP is 'USER:...', then the contents of this keyword
163; are passed to the user function using the _EXTRA
164; mechanism.  This way you can pass additional data to
165; your user-supplied function, via keywords, without
166; using common blocks.
167; DEFAULT: undefined (i.e., no keywords passed by _EXTRA)
168;
169; @returns
170; An array of the required TYPE, whose elements are the result of
171; the requested operation.  Depending on the operation and number of
172; elements in the input array, the result may be vulnerable to
173; overflow or underflow.
174;
175; @examples
176;
177;   First example: 
178;   Shows how <pro>cmapply</pro> can be used to total the second dimension of
179;   the array called IN. This is equivalent to OUT = TOTAL(IN, 2)
180;
181;   IDL> IN  = INDGEN(5,5)
182;   IDL> OUT = CMAPPLY('+', IN, [2])
183;   IDL> HELP, OUT
184;   OUT             INT       = Array[5]
185;
186;   Second example:  Input is assumed to be an 5x100 array of 1's and
187;   0's indicating the status of 5 detectors at 100 points in time.
188;   The desired output is an array of 100 values, indicating whether
189;   all 5 detectors are on (=1) at one time.  Use the logical AND
190;   operation.
191;
192;   IDL> IN = detector_status             ; 5x100 array
193;   IDL> OUT = CMAPPLY('AND', IN, [1])    ; collapses 1st dimension
194;   IDL> HELP, OUT
195;   OUT             BYTE      = Array[100]
196;
197;   (note that MIN could also have been used in this particular case,
198;   although there would have been more loop iterations).
199;
200;   Third example:  Shows sum over first and third dimensions in an
201;   array with dimensions 4x4x4:
202;
203;   IDL> IN = INDGEN(4,4,4)
204;   IDL> OUT = CMAPPLY('+', IN, [1,3])
205;   IDL> PRINT, OUT
206;        408     472     536     600
207;
208;   Fourth example:  A user-function (MEDIAN) is used:
209;
210;   IDL> IN = RANDOMN(SEED,10,10,5)
211;   IDL> OUT = CMAPPLY('USER:MEDIAN', IN, 3)
212;   IDL> HELP, OUT
213;   OUT             FLOAT     = Array[10, 10]
214;
215;   (OUT[i,j] is the median value of IN[i,j,*])
216;
217; @history
218; Mar 1998, Written, CM
219;   Changed usage message to not bomb, 24 Mar 2000, CM
220;   Significant rewrite for *, MIN and MAX (inspired by Todd Clements
221;     <Todd_Clements\@alumni.hmc.edu>); FOR loop indices are now type
222;     LONG; copying terms are liberalized, CM, 22, Aug 2000
223;   More efficient MAX/MIN (inspired by Alex Schuster), CM, 25 Jan
224;     2002
225;   Make new MAX/MIN actually work with 3d arrays, CM, 08 Feb 2002
226;   Add user-defined functions, ON_ERROR, CM, 09 Feb 2002
227;   Correct bug in MAX/MIN initialization of RESULT, CM, 05 Dec 2002
228;
229;  Author: Craig B. Markwardt, NASA/GSFC Code 662, Greenbelt, MD 20770
230;  craigm\@lheamail.gsfc.nasa.gov
231;
232; @version
233; $Id$
234;
235;-
236;
237FUNCTION cmapply, op, array, dimapply, DOUBLE=dbl, TYPE=type, $
238                  FUNCTARGS=functargs, NOCATCH=nocatch
239;
240  compile_opt idl2, strictarrsubs
241;
242
243  if n_params() LT 2 then begin
244      message, "USAGE: XX = CMAPPLY('OP',ARRAY,2)", /info
245      message, '       where OP is +, *, AND, OR, MIN, MAX', /info
246      return, -1L
247  endif
248  if NOT keyword_set(nocatch) then $
249    on_error, 2 $
250  else $
251    on_error, 0
252
253  ;; Parameter checking
254  ;; 1) the dimensions of the array
255  sz = size(array)
256  if sz[0] EQ 0 then $
257    message, 'ERROR: ARRAY must be an array!'
258
259  ;; 2) The type of the array
260  if sz[sz[0]+1] EQ 0 OR sz[sz[0]+1] EQ 7 OR sz[sz[0]+1] EQ 8 then $
261    message, 'ERROR: Cannot apply to UNDEFINED, STRING, or STRUCTURE'
262  if n_elements(type) EQ 0 then type = sz[sz[0]+1]
263
264  ;; 3) The type of the operation
265  szop = size(op)
266  if szop[szop[0]+1] NE 7 then $
267    message, 'ERROR: operation OP was not a string'
268
269  ;; 4) The dimensions to apply (default is to apply to first dim)
270  if n_params() EQ 2 then dimapply = 1
271  dimapply = [ dimapply ]
272  dimapply = dimapply[sort(dimapply)]   ; Sort in ascending order
273  napply = n_elements(dimapply)
274
275  ;; 5) Use double precision if requested or if needed
276  if n_elements(dbl) EQ 0 then begin
277      dbl=0
278      if type EQ 5 OR type EQ 9 then dbl=1
279  endif
280
281  newop = strupcase(op)
282  newarr = array
283  newarr = reform(newarr, sz[1:sz[0]], /overwrite)
284  case 1 of
285
286      ;; *** Addition
287      (newop EQ '+'): begin
288          for i = 0L, napply-1 do begin
289              newarr = total(temporary(newarr), dimapply[i]-i, double=dbl)
290          endfor
291      end
292
293      ;; *** Multiplication
294      (newop EQ '*'): begin ;; Multiplication (by summation of logarithms)
295          cmapply_redim, newarr, dimapply, dimkeep, nkeep, totcol, totkeep
296          if nkeep EQ 0 then begin
297              newarr = reform(newarr, n_elements(newarr), 1, /overwrite)
298              return, (cmapply_product(newarr))[0]
299          endif
300
301          result = cmapply_product(newarr)
302          result = reform(result, sz[dimkeep+1], /overwrite)
303          return, result
304      end
305
306      ;; *** LOGICAL AND or OR
307      ((newop EQ 'AND') OR (newop EQ 'OR')): begin
308          newarr = temporary(newarr) NE 0
309          totelt = 1L
310          for i = 0L, napply-1 do begin
311              newarr = total(temporary(newarr), dimapply[i]-i)
312              totelt = totelt * sz[dimapply[i]]
313          endfor
314          if newop EQ 'AND' then return, (round(newarr) EQ totelt)
315          if newop EQ 'OR'  then return, (round(newarr) NE 0)
316      end
317
318      ;; Operations requiring a little more attention over how to
319      ;; iterate
320      ((newop EQ 'MAX') OR (newop EQ 'MIN')): begin
321          cmapply_redim, newarr, dimapply, dimkeep, nkeep, totcol, totkeep
322          if nkeep EQ 0 then begin
323              if newop EQ 'MAX' then return, max(newarr)
324              if newop EQ 'MIN' then return, min(newarr)
325          endif
326
327          ;; Next task: create result array
328          result = make_array(totkeep, type=type)
329
330          ;; Now either iterate over the number of output elements, or
331          ;; the number of collapsed elements, whichever is smaller.
332          if totcol LT totkeep then begin
333              ;; Iterate over the number of collapsed elements
334              result[0] = reform(newarr[0,*],totkeep,/overwrite)
335              case newop of
336                  'MAX': for i = 1L, totcol-1 do $
337                    result[0] = result > newarr[i,*]
338                  'MIN': for i = 1L, totcol-1 do $
339                    result[0] = result < newarr[i,*]
340              endcase
341          endif else begin
342              ;; Iterate over the number of output elements
343              case newop of
344                  'MAX': for i = 0L, totkeep-1 do result[i] = max(newarr[*,i])
345                  'MIN': for i = 0L, totkeep-1 do result[i] = min(newarr[*,i])
346              endcase
347          endelse
348
349          result = reform(result, sz[dimkeep+1], /overwrite)
350          return, result
351      end
352
353      ;; User function
354      (strmid(newop,0,4) EQ 'USER'): begin
355          functname = strmid(newop,5)
356          if functname EQ '' then $
357            message, 'ERROR: '+newop+' is not a valid operation'
358
359          cmapply_redim, newarr, dimapply, dimkeep, nkeep, totcol, totkeep
360          if nkeep EQ 0 then begin
361              if n_elements(functargs) GT 0 then $
362                return, call_function(functname, newarr, _EXTRA=functargs)
363              return, call_function(functname, newarr)
364          endif
365
366          ;; Next task: create result array
367          result = make_array(totkeep, type=type)
368
369          ;; Iterate over the number of output elements
370          if n_elements(functargs) GT 0 then begin
371              for i = 0L, totkeep-1 do $
372                result[i] = call_function(functname, newarr[*,i], _EXTRA=functargs)
373          endif else begin
374              for i = 0L, totkeep-1 do $
375                result[i] = call_function(functname, newarr[*,i])
376          endelse
377
378          result = reform(result, sz[dimkeep+1], /overwrite)
379          return, result
380      end
381
382
383  endcase
384
385  newsz = size(newarr)
386  if type EQ newsz[newsz[0]+1] then return, newarr
387
388  ;; Cast the result into the desired type, if necessary
389  castfns = ['UNDEF', 'BYTE', 'FIX', 'LONG', 'FLOAT', $
390             'DOUBLE', 'COMPLEX', 'UNDEF', 'UNDEF', 'DCOMPLEX' ]
391  if type GE 1 AND type LE 3 then $
392    return, call_function(castfns[type], round(newarr)) $
393  else $
394    return, call_function(castfns[type], newarr)
395end
Note: See TracBrowser for help on using the repository browser.