Next: , Previous: Expression evaluation, Up: Array Expressions


3.6 Index placeholders

Blitz++ provides objects called index placeholders which represent array indices. They can be used directly in expressions.

There is a distinct index placeholder type associated with each dimension of an array. The types are called firstIndex, secondIndex, thirdIndex, ..., tenthIndex, eleventhIndex. Here's an example of using an index placeholder:

     Array<float,1> A(10);
     firstIndex i;
     A = i;

This generates code which is similar to:

     for (int i=0; i < A.length(); ++i)
         A(i) = i;

Here's an example which fills an array with a sampled sine wave:

     Array<float,1> A(16);
     firstIndex i;
     
     A = sin(2 * M_PI * i / 16.);

If your destination array has rank greater than 1, you may use multiple index placeholders:

     // Fill a two-dimensional array with a radially
     // symmetric, decaying sinusoid
     
     // Create the array
     int N = 64;
     Array<float,2> F(N,N);
     
     // Some parameters
     float midpoint = (N-1)/2.;
     int cycles = 3;
     float omega = 2.0 * M_PI * cycles / double(N);
     float tau = - 10.0 / N;
     
     // Index placeholders
     firstIndex i;
     secondIndex j;
     
     // Fill the array
     F = cos(omega * sqrt(pow2(i-midpoint) + pow2(j-midpoint)))
         * exp(tau * sqrt(pow2(i-midpoint) + pow2(j-midpoint)));

Here's a plot of the resulting array:

sinsoid.gif
Array filled using an index placeholder expression.

You can use index placeholder expressions in up to 11 dimensions. Here's a three dimensional example:

     // Fill a three-dimensional array with a Gaussian function
     Array<float,3> A(16,16,16);
     firstIndex i;
     secondIndex j;
     thirdIndex k;
     float midpoint = 15/2.;
     float c = - 1/3.0;
     A = exp(c * (sqr(i-midpoint) + sqr(j-midpoint)
         + sqr(k-midpoint)));

You can mix array operands and index placeholders:

     Array<int,1> A(5), B(5);
     firstIndex i;
     
     A = 0, 1, 1, 0, 2;
     B = i * A;          // Results in [ 0, 1, 2, 0, 8 ]

For your convenience, there is a namespace within blitz called tensor which declares all the index placeholders:

     namespace blitz {
       namespace tensor {
         firstIndex i;
         secondIndex j;
         thirdIndex k;
          ...
         eleventhIndex t;
       }
     }

So instead of declaring your own index placeholder objects, you can just say

     namespace blitz::tensor;

when you would like to use them. Alternately, you can just preface all the index placeholders with tensor::, for example:

     A = sin(2 * M_PI * tensor::i / 16.);

This will make your code more readable, since it is immediately clear that i is an index placeholder, rather than a scalar value.

3.7 Type promotion

When operands of different numeric types are used in an expression, the result gets promoted according to the usual C-style type promotion. For example, the result of adding an Array<int> to an Arrray<float> will be promoted to float. Generally, the result is promoted to whichever type has greater precision.

Type promotion for user-defined types

The rules for type promotion of user-defined types (or types from another library) are a bit complicated. Here's how a pair of operand types are promoted:

If you wish to alter the default type promotion rules above, you have two choices:

Note that you can do these specializations in your own header files (you don't have to edit promote.h or ops.h).

Manual casts

There are some inconvenient aspects of C-style type promotion. For example, when you divide two integers in C, the result gets truncated. The same problem occurs when dividing two integer arrays in Blitz++:

     Array<int,1> A(4), B(4);
     Array<float,1> C(4);
     
     A = 1, 2, 3, 5;
     B = 2, 2, 2, 7;
     
     C = A / B;      // Result:  [ 0  1  1  0 ]

The usual solution to this problem is to cast one of the operands to a floating type. For this purpose, Blitz++ provides a function cast(expr,type) which will cast the result of expr as type:

     C = A / cast(B, float());   // Result: [ 0.5  1  1.5  0.714 ]

The first argument to cast() is an array or expression. The second argument is a dummy object of the type to which you want to cast. Once compilers support templates more thoroughly, it will be possible to use this cast syntax:

     C = A / cast<float>(B);

But this is not yet supported.