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

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

improvements/corrections of some *.pro headers

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