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

Last change on this file since 157 was 157, checked in by navarro, 18 years ago

header improvements + xxx doc

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