POOMA: A C++ Toolkit for High-Performance Parallel Scientific Computing | ||
---|---|---|
Prev | Chapter 7. Data-Parallel Expressions | Next |
POOMA containers can be used in data-parallel expressions and statements. The basic guidelines are simple:
The C++ built-in and mathematical operators operate on an entire container by operating element-wise on its values.
Binary operators operate only on containers with the same domain types and by combining values with the same indices. If the result is a container, it has a domain equal to the left operand's domain.
For assignment operators, the domains of the left operand and the right operand must have the same type and be conformable, i.e., have the "same shape".
The data-parallel operators operate element-wise on containers' values. For example, if A is a one-dimensional array, -A is a one-dimensional array with the same size such that the value at the ith position equals -A(i). If A and B are two-dimensional Arrays on the same domain, A+B is an array on the same domain with values equaling the sum of corresponding values in A and B.
Binary operators operate on containers with the same domain types. The domain's indices need not be the same, but the result will have a domain equal to the left operand. For example, the sum of an Array A with a one-dimensional interval [0,3] and an Array B with a one-dimensional interval [1,2] is well-defined because both domains are one-dimensional intervals. The result is an Array with a one-dimensional interval [0,3]. Its first and last entries equal A's first and last entries, while its middle two entries are the sums A(1)+B(1) and A(2)+B(2). We assume zero is the default value for the type of values stored in B. A more complicated example of adding two Arrays with different domains is illustrated in Figure 7-1. Code for these Arrays could be
Interval<1> H(0,2), I(1,3), J(2,4); Array<2, double, Brick> A(I,I), B(J,H); // ... fill A and B with values ... ... = A + B;
Figure 7-1. Adding Arrays with Different Domains
Adding Arrays with different domains is supported. Solid lines indicate the domains' extent. Values with the same indices are added.
Both A and B have domains of two-dimensional intervals so they may be added, but their domains' extent differ, as indicated by the solid lines in the figure. The sum has domain equal to the left operand's domain. Values with the same indices are added. For example, A(2,2) and B(2,2) are added. B's domain does not include index (1,1) so, when adding A(1,1) and B(1,1), the default value for B's value type is used. Usually this is 0. Thus, A(1,1) + B(1,1) equals 9 + 0.
Operations with both Arrays and scalar values are supported. Conceptually, a scalar value can be thought of as an Array with any desired domain and having the same value everywhere. For example, consider
Array<1, double, Brick> D(Interval<1>(7,10)); D += 2*D + 7;2*D obeys the guidelines because the scalar 2 can be thought of as an array with the same domain as D. It has the same value 2 everywhere. Likewise the conceptual domain for the scalar 7 is the same as 2*D's domain. Thus, 2*D(i) + 7 is added to D(i) wherever index i is in D's domain. In practice, the toolkit does not first convert scalar values to arrays but instead uses them directly in expressions.
Assignments to containers are also supported. The domain types of the assignment's left-hand side and its right-hand side must be the same. Their indices need not be the same, but they must correspond. That is, the domains must be conformable, or have the "same shape", i.e., have the same number of indices for each dimension. For example, the one-dimensional interval [0,3] is conformable to the one-dimensional interval [1,4] because they both have the same number of indices in each dimension. The domains of A and B, as declared
Interval<1> H(0,2), I(1,3), J(2,4), K(0,4); Array<2, double, Brick> A(I,I), B(H,J), C(I,K);are conformable because each dimension has the same number of indices. A and C are not conformable because, while their first dimensions are conformable, their second dimensions are not conformable. It has three indices while the other has five. We define conformable containers to be containers with conformable domains.
When assigning to a container, corresponding container values are assigned. (Since the left-hand side and the right-hand side are conformable, corresponding values exist.) In this code fragment,
Array<1, double, Brick> A(Interval<1>(0,1)); Array<1, double, Brick> B(Interval<1>(1,2)); A = B;A(0) is assigned B(1) and A(1) is assigned B(2).
Assigning a scalar value to an Array also is supported, but assigning an Array to a scalar is not. A scalar value is conformable to any domain because, conceptually it can be viewed as an Array with any desired domain and having the same value everywhere. Thus, the assignment B = 3 ensures every value in B equals 3. Even though a scalar value is conformable to any Array, it is not an l-value so it cannot appear on the left-hand side of an assignment.
Data-parallel expressions can involve typical mathematical functions and output operations. For example, sin(A) yields an Array with values equal to the sine of each of Array A's values. dot(A,B) has values equaling the dot product of corresponding values in Arrays A and B. The contents of an entire Array can be easily printed to standard output. For example, the program
Array<1, double, Brick> A(Interval<1>(0,2)); Array<1, double, Brick> B(Interval<1>(1,3)); A = 1.0; B = 2.0; std::cout < < A-B < < std::endl;yields (000:002:001) = 1 -1 -1. The initial (000:002:001) indicates the Array's domain ranges from 0 to 2 with a stride of 1. The three values in A-B follow.
The following four tables list the data-parallel operators that operate on Arrays. Table 7-1 lists standard C++ operators that can be applied to Arrays and also scalar values if appropriate. Each unary operator takes an Array parameter and returns an Array. The types of the two Arrays need not be the same. For example, ! can take an Array<bool>, Array<int>, Array<long>, or any other value type to which ! can be applied. The result is an Array<bool>. Each binary operator also returns an Array. When specifying two Arrays or an Array and a scalar value, a full set of operators is supported. When specifying an Array and a Tensor, TinyMatrix, or Vector, a more limited set of operators is supported. For example, == can take two Arrays, an Array and a scalar value, or a scalar value and an Array. If given two Arrays, corresponding values are used. If an argument is a scalar value, its same value is the used with each Array value. The + supports the same set of parameters but also supports adding an Array and a Tensor, an Array and a TinyMatrix, an Array and a Vector, a Tensor and an Array, a TinyMatrix and an Array, and a Vector and an Array. For these cases, the Array must have a value type that can be added to the other argument. For example, a Vector can be added to an Array of Vectors.
Table 7-1. Operators Permissible for Data-Parallel Expressions
supported operators | |
---|---|
unary operators | +, -, ~, ! |
binary operators with at least one Array and at most one scalar value | +, -, *, /, %, &, |, ^, <, <=, >=, >, ==, !=, &&, ||, < <, > > |
binary operators with at least one Array and at most one Tensor, TinyMatrix, or Vector | +, -, *, /, %, &, |, ^, ==, != |
Mathematical functions that can be used in data-parallel expressions appear in Table 7-2. For example, applying cos to an Array of values with type T yields an Array with the same type. The functions are split into five sections:
trigonometric and hyperbolic functions,
functions computing absolute values, rounding functions, and modulus functions,
functions computing powers, exponentiation, and logarithms,
functions involving complex numbers, and
functions for operating on matrices and tensors.
Several data-parallel functions require inclusion of header files declaring their underlying element-wise function. These header files are listed at the beginning of each section. For the data-parallel operator to be applicable, it must operate on the Array's type. For example, cos can be applied on Arrays of int, double, and even bool, but applying on Arrays of pointers is not supported because cos cannot be called with a pointer argument.
Two functions deserve special explanation. The PETE_identity function applies the identity operation to the array. That is, the returned array has values equaling the argument's values. pow2, pow3, and pow4 provide fast ways to compute squares, cubes, and fourth powers of their arguments.
Table 7-2. Mathematical Functions Permissible for Data-Parallel Expressions
function | effect |
---|---|
Trigonometric and Hyperbolic Functions | #include <math.h> |
Array<T> cos (const Array<T>& A) | Returns the cosines of the Array's values. |
Array<T> sin (const Array<T>& A) | Returns the sines of the Array's values. |
Array<T> tan (const Array<T>& A) | Returns the tangents of the Array's values. |
Array<T> acos (const Array<T1>& A) | Returns the arc cosines of the Array's values. |
Array<T> asin (const Array<T1>& A) | Returns the arc sines of the Array's values. |
Array<T> atan (const Array<T1>& A) | Returns the arc tangents of the Array's values. |
Array<T> atan2 (const Array<T1>& A, const Array<T2>& B) | Computes the arc tangents of the values from the division of elements in B by the elements in A. The resulting values are the signed angles in the range -π to π, inclusive. |
Array<T> atan2 (const Array<T1>& A, const T2& r) | Computes the arc tangents of the values from the division of r by the elements in A. The resulting values are the signed angles in the range -π to π, inclusive. |
Array<T> atan2 (const T1& l, const Array<T2>& B) | Computes the arc tangents of the values from the division of elements in B by l. The resulting values are the signed angles in the range -π to π, inclusive. |
Array<T> cosh (const Array<T>& A) | Returns the hyperbolic cosines of the Array's values. |
Array<T> sinh (const Array<T>& A) | Returns the hyperbolic sines of the Array's values. |
Array<T> tanh (const Array<T>& A) | Returns the hyperbolic tangents of the Array's values. |
Absolute Value, Rounding, and Modulus Functions | #include <math.h> |
Array<T> fabs (const Array<T1>& A) | Returns the absolute values of the floating point numbers in the Array. |
Array<T> ceil (const Array<T1>& A) | For each of the Array's values, return the integer larger than or equal to it (as a floating point number). |
Array<T> floor (const Array<T1>& A) | For each of the Array's values, return the integer smaller than or equal to it (as a floating point number). |
Array<T> fmod (const Array<T1>& A, const Array<T2>& B) | Computes the floating-point modulus (remainder) of A's values with the corresponding value in B. The results have the same signs as A and absolute values less than the absolute values of B. |
Array<T> fmod (const Array<T1>& A, const T2& r) | Computes the floating-point modulus (remainder) of A's values with r. The results have the same signs as A and absolute values less than the absolute value of r. |
Array<T> fmod (const T1& l, const Array<T2>& B) | Computes the floating-point modulus (remainder) of l with the values in B. The results have the same signs as l and absolute values less than the absolute values of B. |
Powers, Exponentiation, and Logarithmic Functions | #include <math.h> |
Array<T> PETE_identity (const Array<T>& A) | Returns the Array. That is, it applies the identity operation. |
Array<T> sqrt (const Array<T>& A) | Returns the square roots of the Array's values. |
Array<T> pow (const Array<T1>& A, const Array<T2>& B) | Raises A's values by the corresponding power in B. |
Array<T> pow (const Array<T1>& A, const T2& r) | Raises A's values by the power r. |
Array<T> pow (const T1& l, const Array<T2>& B) | Raises l by the powers in B. |
Array<T> pow2 (const Array<T>& A) | Returns the squares of A's values. |
Array<T> pow3 (const Array<T>& A) | Returns the cubes of A's values. |
Array<T> pow4 (const Array<T>& A) | Returns the fourth powers of A's values. |
Array<T> ldexp (const Array<T1>& A, const Array<int>& B) | Multiplies A's values by two raised to the corresponding value in B. |
Array<T> ldexp (const Array<T1>& A, int r) | Multiplies A's values by two raised to the rth power. |
Array<T> ldexp (const T1& l, const Array<int>& B) | Multiplies l by two raised to the values in B. |
Array<T> exp (const Array<T>& A) | Returns the exponentiations of the Array's values. |
Array<T> log (const Array<T>& A) | Returns the natural logarithms of the Array's values. |
Array<T> log10 (const Array<T>& A) | Returns the base-10 logarithms of the Array's values. |
Functions Involving Complex Numbers | #include <complex> |
Array<T> real (const Array<complex<T> >& A) | Returns the real parts of A's complex numbers. |
Array<T> imag (const Array<complex<T> >& A) | Returns the imaginary parts of A's complex numbers. |
Array<T> abs (const Array<complex<T> >& A) | Returns the absolute values (magnitudes) of A's complex numbers. |
Array<T> abs (const Array<T>& A) | Returns the absolute values of A's values. |
Array<T> arg (const Array<complex<T> >& A) | Returns the angle representations (in radians) of the polar representations of A's complex numbers. |
Array<T> norm (const Array<complex<T> >& A) | Returns the squared absolute values of A's complex numbers. |
Array<complex<T> > conj (const Array<complex<T> >& A) | Returns the complex conjugates of A's complex numbers. |
Array<complex<T> > polar (const Array<T1>& A, const Array<T2>& B) | Returns the complex numbers created from polar coordinates (magnitudes and phase angles) in corresponding Arrays. |
Array<complex<T> > polar (const T1& l, const Array<T2>& A) | Returns the complex numbers created from polar coordinates with magnitude l and phase angles in the Array. |
Array<complex<T> > polar (const Array<T1>& A, const T2& r) | Returns the complex numbers created from polar coordinates with magnitudes in the Array and phase angle r. |
Functions Involving Matrices and Tensors | #include "Pooma/Tiny.h" |
T trace (const Array<T>& A) | Returns the sum of the A's diagonal entries, viewed as a matrix. |
T det (const Array<T>& A) | Returns the determinant of A, viewed as a matrix. |
Array<T> transpose (const Array<T>& A) | Returns the transpose of A, viewed as a matrix. |
Array<T> symmetrize (const Array<T>& A) | Returns the tensors of A with the requested output symmetry. |
Array<T> dot (const Array<T1>& A, const Array<T2>& B) | Returns the dot products of values in the two Arrays. Value type T equals the type of the dot operating on T1 and T2. |
Array<T> dot (const Array<T1>& A, const T2& r) | Returns the dot products of values in the Array with r. Value type T equals the type of the dot operating on T1 and T2. |
Array<T> dot (const T1& l, const Array<T2>& A) | Returns the dot products of l with values in the Array. Value type T equals the type of the dot operating on T1 and T2. |
Array<Tensor<T> > outerProduct (const Array<T1>& A, const Array<T2>& B) | Returns tensors created by computing the outer product of corresponding vectors in the two Arrays. Value type T equals the type of the product of T1 and T2. The vectors must have the same length. |
Array<Tensor<T> > outerProduct (const T1& l, const Array<T2>& A) | Returns tensors created by computing the outer product of l with the vectors in the Array. Value type T equals the type of the product of T1 and T2. The vectors must have the same length. |
Array<Tensor<T> > outerProduct (const Array<T1>& A, const T2& r) | Returns tensors created by computing the outer product of vectors in the Array with r. Value type T equals the type of the product of T1 and T2. The vectors must have the same length. |
TinyMatrix<T> outerProductAsTinyMatrix (const Array<T1>& A, const Array<T2>& B) | Returns matrices created by computing the outer product of corresponding vectors in the two Arrays. Value type T equals the type of the product of T1 and T2. The vectors must have the same length. |
TinyMatrix<T> outerProductAsTinyMatrix (const T1& l, const Array<T2>& A) | Returns matrices created by computing the outer product of l with the vectors in the Array. Value type T equals the type of the product of T1 and T2. The vectors must have the same length. |
TinyMatrix<T> outerProductAsTinyMatrix (const Array<T1>& A, const T2& r) | Returns matrices created by computing the outer product of the vectors in the Array with r. Value type T equals the type of the product of T1 and T2. The vectors must have the same length. |
Type restrictions from how the underlying functions operate on individual elements may restrict permissible choices for the template type parameters. |
Comparison functions appear in Table 7-3. max and min functions supplement named comparison functions. For example, LT and LE compute the same thing as the < and <= operators.
Table 7-3. Comparison Functions Permissible for Data-Parallel Expressions
function | effect |
---|---|
Array<T> max (const Array<T1>& A, const Array<T2>& B) | Returns the maximum of corresponding Array values. |
Array<T> max (const T1& l, const Array<T2>& A) | Returns the maximums of l with the Array's values. |
Array<T> max (const Array<T1>& A, const T2& r) | Returns the maximums of the Array's values with r. |
Array<T> min (const Array<T1>& A, const Array<T2>& B) | Returns the minimum of corresponding Array values. |
Array<T> min (const T1& l, const Array<T2>& A) | Returns the minimums of l with the Array's values. |
Array<T> min (const Array<T1>& A, const T2& r) | Returns the minimums of the Array's values with r. |
Array<bool> LT (const Array<T1>& A, const Array<T2>& B) | Returns booleans from using the less-than operator < to compare corresponding Array values in A and B. |
Array<bool> LT (const T1& r, const Array<T2>& A) | Returns booleans from using the less-than operator < to compare l with the Array's values. |
Array<bool> LT (const Array<T1>& A, const T2& r) | Returns booleans from using the less-than operator < to compare the Array's values with r. |
Array<bool> LE (const Array<T1>& A, const Array<T2>& B) | Returns booleans from using the less-than-or-equal operator <= to compare Array values in A and B. |
Array<bool> LE (const T1& l, const Array<T2>& A) | Returns booleans from using the less-than-or-equal operator <= to compare l with the Array's values. |
Array<bool> LE (const Array<T1>& A, const T2& r) | Returns booleans from using the less-than-or-equal operator <= to compare the Array's values with r. |
Array<bool> GE (const Array<T1>& A, const Array<T2>& B) | Returns booleans from using the greater-than-or-equal operator >= to compare Array values in A and B. |
Array<bool> GE (const T1& l, const Array<T2>& A) | Returns booleans from using the greater-than-or-equal operator >= to compare l with the Array's values. |
Array<bool> GE (const Array<T1>& A, const T2& r) | Returns booleans from using the greater-than-or-equal operator >= to compare the Array's values with r. |
Array<bool> GT (const Array<T1>& A, const Array<T2>& B) | Returns booleans from using the greater-than operator > to compare Array values in A and B. |
Array<bool> GT (const T1& l, const Array<T2>& A) | Returns booleans from using the greater-than operator > to compare l with the Array's values. |
Array<bool> GT (const Array<T1>& A, const T2& r) | Returns booleans from using the greater-than operator > to compare the Array's values with r. |
Array<bool> EQ (const Array<T1>& A, const Array<T2>& B) | Returns booleans from determining whether corresponding Array values in A and B are equal. |
Array<bool> EQ (const T1& l, const Array<T2>& A) | Returns booleans from determining whether l equals the Array's values. |
Array<bool> EQ (const Array<T1>& A, const T2& r) | Returns booleans from determining whether the Array's values equal r. |
Array<bool> NE (const Array<T1>& A, const Array<T2>& B) | Returns booleans from determining whether corresponding Array values in A and B are not equal. |
Array<bool> NE (const T1& l, const Array<T2>& A) | Returns booleans from determining whether l does not equal the Array's values. |
Array<bool> NE (const Array<T1>& A, const T2& r) | Returns booleans from determining whether the Array's values are not equal to r. |
The table of miscellaneous functions (Table 7-4) contains two functions. peteCast casts all values in an Array to the type specified by its first parameter. The where function generalizes the trinary ?: operator. Using its first Array argument as boolean values, it returns an Array of just two values: t and f.
Table 7-4. Miscellaneous Functions Permissible for Data-Parallel Expressions
function | effect |
---|---|
Array<T> peteCast (const T1&, const Array<T>& A) | Returns the casting of the Array's values to type T1. |
Array<T> where (const Array<T1>& A, const T2& t, const T3& f) | Generalizes the ?: operator, returning an Array of t and f values depending on whether A's values are true or false, respectively. |
Throughout this chapter, we illustrate data-parallel expressions and statements operating on all of a container's values. Frequently, operating on a subset is useful. In POOMA, a subset of a container's values is called a view. Combining views and data-parallel expressions will enable us to more succinctly and more easily write the Doof2d diffusion program. Views are discussed in the next chapter.