• Keine Ergebnisse gefunden

3.3 Functional layers

3.3.6 Sequence encapsulation

void setVariableName(VariableNameType d) {

BaseType_::setMember(d);

}

VariableNameType getVariableName() {

return BaseType_::getMember();

} };

};

Examples for variable encapsulation in our library are:

• VerticalDimensionLayer: This layer is responsible for storing the vertical dimension, i.e. the number of rows of the matrix. It has getVerticalDimension()as the only accessor function delivering of course what its name suggests.

• HorizontalDimensionLayer: It obviously does the same as the previous one, just for the hori-zontal dimension, i.e. the number of columns.

• EncapsulationLayer: The encapsulation layer is a special variant of variable encapsulation.

Where all other layers are intended to be built on top of another layer (which is yet an incomplete type), the encapsulation layer is used to add functionality to an existing, complete (matrix) type.

If e.g. an algorithm has certain requirements for a data structure (concerning the functionality) that the current one doesn’t fulfill, then the type can be encapsulated in this layer and extended with other layers that implement the desired interface.

Note that the setVariableName()member function is only accessible (and sensible), if MemoryClass is not Fixed. Trying to instantiate that member function would result in a compile time error, since the specialization of Memberfor fixed values doesn’t have an according member function.

3.3.6 Sequence encapsulation

If the stored data is known to be a sequence, then we are interested in encapsulating its functionality, meaning its member functions. For that purpose, we revert to a small set of member functions that are also widely used by the STL containers.

Sequence encapsulation template <class Base_, class Config>

struct SequenceNameLayer {

template <class Final_>

class Type : public Member<typename Base_::template Type<Final_>, Config,

SequenceName SequenceName,

typename Config::ContainerType::size_type, typename Config::MemoryClass>

{

public:

typedef typename Base_::template Type<Final_> BaseLayer;

typedef typename Config::ContainerType SequenceNameContainerType;

typedef typename SequenceNameContainerType::iterator SequenceNameIterator;

typedef typename SequenceNameContainerType::const_iterator ConstSequenceNameIterator;

typedef typename SequenceNameContainerType::value_type value_type;

typedef typename SequenceNameContainerType::size_type size_type;

typedef SequenceName LayerName;

typedef Config LayerConfig;

private:

typedef Member<BaseLayer, Config,

SequenceName, size_type,

typename Config::MemoryClass> BaseType_;

typedef Type<Final_> Type_;

public:

typedef Data<typename BaseLayer::DataType, SequenceName,

SequenceNameContainerType, size_type,

typename Config::MemoryClass> ParameterType;

public:

Type() : BaseType_() {}

template <typename Arg_>

Type(Arg_& arg) : BaseType_(arg.getValue(SequenceName()),arg) {}

SequenceNameIterator SequenceName_begin() {

return BaseType_::begin();

}

SequenceNameIterator SequenceName_end() {

return BaseType_::end();

}

ConstSequenceNameIterator SequenceName_begin() const {

return BaseType_::begin();

}

ConstSequenceNameIterator SequenceName_end() const {

return BaseType_::end();

} };

};

In addition to the iterator delivering ...begin() and ...end()functions, each layer may define other member functions that offer valuable information about the sequence, like e.g. the length.

Examples for sequence encapsulation in our library include:

• EdgeTargetsLayer: The layer of edge targets is responsible for storing the numbers/indices of the targets of the edges (very much like the bottom row in Table 2.1) consecutively. The order of theses edge targets is not fixed - it depends on how the surrounding layers interprete them.

But normally we have row-by-row or column-by-column ordering.

The additional accessor function numberOfEdges()delivers the total number of edges (i.e. the number of nonzero entries in the matrix) that can be stored. The EdgeTargets begin() and EdgeTargets end() member functions deliver STL-like iterators that iterate through the se-quence of edge targets.

This layer is only of importance for sparse matrix types, i.e. matrix types where the numbering of the neighbouring targets is arbitrary (not consecutive and not even computable). Thus for dense matrices we need a different approach with the FirstTargetsLayer.

• NeighbourhoodStartsLayer: The neighbourhood starts layer is devised to give a sequence that stores information about where in the ordered sequence of edges a new neighbourhood begins. For sparse matrices, NeighbourhoodStarts begin()[i]gives the index where the neighbourhood of node (variable) istarts in edge target sequence (and in the weight value sequence).

In order to make use of this information, the sequence of edge targets from theEdgeTargetsLayer doesn’t even need to exist. For every matrixNeighbourhoodStarts begin()[i]states the num-ber of edges (nonzero entries) that are stored before ¯Ni. Thus

NeighbourhoodStarts begin()[i+1] - NeighbourhoodStarts begin()[i]

gives the cardinality of ¯Ni (i.e. the length of thei-th row/column).

• WeightValuesLayer: This layer stores the weights of the edges (i.e. the values of the matrix entries) in one long sequence of type Config::ContainerType, which must provide an STL type iterator. Thus, in the Memberlayer, a variable of type Config::ContainerTypestores all nonzero elements of the matrix.

The only accessor functions that this layer adds to the matrix type are WeightValues begin() and WeightValues end(), which yield an iterator that point to the start, respectively behind the end of the weight sequence.

• FirstTargetsLayer: The layer for the first targets sequence is needed for dense matrix types.

Dense matrices differ from sparse matrices in the type of sequence they store their index infor-mation. The numbering of their neighbourhood is consecutive and not arbitrary, thus, we could use a function value sequence, see Section A.2, for that purpose.

However, it would be cumbersome to construct one long sequence containing all targets of all nodes like it is provided in the edge targets layer, since in this case we somehow had to combine multiple sequences into one.

Instead, we provide a layer, that stores the first target index for each node. For full dense matrices, this sequence would be a constant (or even fixed) value sequence with the value 0 (since we have zero based indices). Banded matrices may use a function value sequence at this point.

The information in the first target sequence is used by other layers later in the hierarchy in order e.g. to construct appropriate row-wise iterators.

• SliceLayer: The slice layer can be used to indicate a subset of the matrix. More precisely, it defines one target node for each source node. It may be used e.g. to store the indices of the diagonal entries of a matrix.

• StrongNeighbourhoodLayer: The strong neighbourhood layer is capable of computing and storing the strong neighbourhood of a variable (cf. Sections 5.2.5 and 9.2.3 resp. 9.2.4).

The member functioncomputeStrongNeighbours()triggers the computation. Afterwards, the sequence of strong neighbourhoods can be accessed via theStrongNeighbourhood begin()and StrongNeighbourhood end()member functions.

• SplittingLayer: This layer offers to generate a splitting based on the previously computed strong neighbourhoods of the nodes/variables. After a call tocomputeSplitting(), the splitting sequence can be traversed by using theSplitting begin()and Splitting end()functions.

Ifiis a C-variable, then the according entry in the splitting sequence is 1, and 0 otherwise. In other words

Splitting begin()[i] == 1 ⇐⇒ i∈C and

Splitting begin()[i] == 0 ⇐⇒ i∈F.

WithgetCoarseDimension()the cardinality of C(i.e. the dimension of the coarse system) can be queried.

• CoarseLevelMappingLayer: Functionality for computing the mapping beween the fine level and the coarse level varibles is provided in this layer. With computeCoarseLevelMapping() the mapping is computed, andCoarseLevelMapping begin()andCoarseLevelMapping end() allow accessing it.

If i is a C-variable, then CoarseLevelMapping begin()[i] gives its index in the next, the coarser level. If elseiis an F-variable, the value at thei-th position is undefined (these variable are to be interpolated through the surroundingC-variables).

• NeighbourhoodStartsPartitionLayer: Imposes a view on the matrix that restricts the access on a certain interval of rows (or columns, depending on how the matrix is interpreted via the RowWiseLayeror the ColumnWiseLayer). More precisely, an iterator over a section of the orig-inal edge target starts sequence is provided. An existing NeighbourhoodStartsLayer with its types is shadowed, its functions overwritten. Thus, using this layer merely makes sense when encapsulating a given matrix with theEncapsulationLayer.

• PartitionLayer: This layer provides an iterator to a sequence of partitions of a matrix. The value type of this iterator is in turn a full matrix type, providing a row wise / column wise view on a certain part of the matrix. The NeighbourhoodStartsPartitionLayeris used to construct this entry type of the sequence.

ThePartitionLayeris mainly intended for parallelization in symmetric multiprocessing (SMP) environments with shared memory. On such machines, matrix-vector or matrix-matrix multipli-cations can be executed in parallel, thereby distributing the tasks associated with multiplying a partition, to an according number of threads.