Context and ifp()
Sometimes you wish to perform an operation on only a subset of the
elements of a vector or matrix. In the following example, we wish to
compute the absolute value of a vector A:
ifp (A < 0)
B = -A;
else
B = A;
The ifp() statement works much like an if() statement in
C, except that it works in parallel, and the condition is a vector or
matrix condition. In the example above, the expression A < 0
is a vector condition. In parallel, every element of A is
compared with 0, resulting in a boolean value (true or false). (Thus,
the result of such a comparison is a booleanVector). The subset of
elements (virtual processors) where the expression was true
executes the then clause. The subset of elements (virtual
processors) where the expression was false executes the
else clause, if any. In this example, every element of B
will be assigned some value, either -A or A, as appropriate. Note
that both clauses are always executed; it's just that a different set
of virtual processors are active in each clause.
Here's another example:
ifp (A != 0)
B /= A;
else
B = 0;
ifp() may be nested. Each ifp reduces the size of the set of virtual
processors that are active. Thus, in
ifp (A == B)
C = 0; // A == B here
else ifp (A < B) // A != B here
C = -1; // A < B here
else
C = 1; // A > B here
The second ifp() is part of the else clause of the first ifp, so it
is only executed by those virtual processors who are active there,
i.e., those who found their elements A != B.
Within one of the clauses of an ifp() statement, as we said, some
subset of the virtual processors are active. Some subset
of which set of virtual processors? Of those that
correspond to the elements in the vector or matrix condition provided
to ifp(). Any other vectors or matrices used within the clause must
have the same shape , i.e., be a vector of the same size, or
a matrix with the same number of rows and columns, as that in the
condition expression. (Otherwise, what subset of virtual processors
would be active in that vector or matrix?) Thus, in
intVector A(10), B(10), C(20);
...
ifp (A > 0)
B = A; // ok
else
C = A; // error: C has a different shape
It is often convenient to define
const intVector VP(N, Identity);
const intMatrix VProw(N, M, IdentityR);
const intMatrix VPcol(N, M, IdentityC);
for use in situations that depend on the position, e.g.,
intVector X(N);
...
ifp (VP % 2 == 0)
// here we can work with X[i] where i is even
else
// here we can work with X[i] where i is odd
A subtle point about slices and context: most of the time, slices act
like a vector, but they can also act like a matrix. For example, in
intVector A(N);
intMatrix B(N,N);
const intVector VP(N, Identity);
const intMatrix VProw(N, N, IdentityR);
const intMatrix VPcol(N, N, IdentityC);
...
ifp (VP % 2 == 0)
B[2][_] = 33; // assign 33 to row 2 of matrix B
ifp (VPcol % 2 == 0)
B[2][_] = 33; // assign 33 to row 2 of matrix B
the context in the first ifp is that of an N-element vector, and in
the second ifp is that of an NxN-element matrix. Both have the same
effect, however. In other words, when deciding whether the virtual
processors of a slice are active, if the context is a vector, the
slice is thought of like a vector, and if the context is a matrix, the
slice is thought of like a matrix (using the appropriate row or column
of the context to determine the activeness of elements in the slice).
This shows up in the gauss example.