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

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

modification of some headers (+some corrections) to prepare usage of the new idldoc

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