LatNet Builder Manual
2.0.1-11
Software Package for Constructing Highly Uniform Point Sets
|
The two parts of the software are mostly independent, but some dependencies were introduced so as to limit code duplication. More precisely:
In LatBuilder, the coordinate-uniform evaluation algorithm is based on viewing fully-projection regular lattice rules as permutations of the multiples of \(\frac1n\). The computation rules for these permutations are based on modular arithmetic in \(\mathbb Z\) for ordinary lattice rules (resp. arithmetic in the Laurent formal series space for polynomial lattice rules). This point of view can be extended to digital nets, by changing the computation rule for the permutations. The rule relies on linear algebra in \(\mathbb F_2\). All these rules are detailed on this page.
These operations are implemented in the LatBuilder::SizeParam and LatBuilder::Storage classes templated by the LatticeType enumeration. The actual implementation is in the Stride inner class. To reuse code from LatBuilder, in particular the coordinate-uniform evaluation algorithm, we added a new value (LatBuilder::LatticeType::DIGITAL) to this enumeration, and implemented the above-mentioned classes. Although the notion of digital lattice does not make sense mathematically, it does make sense from the implementation standpoint. Details on the coordinate-uniform evaluation algorithm are available in the original reference paper [18].
In multiple situations, LatNet Builder needs to access arrays of values that can interchangeably be computed on-the-fly or pre-computed. The concept of sequence, in the sense of ordered list rather than infinite mathematical sequence, provides in LatNet Builder a unified interface for both. The elements of a sequence are accessed via immutable iterators (const_iterator
) with the standard syntax as used for the standard C++ containers, as in the following example:
Or, using the new C++11 auto
keyword and for
syntax:
Concrete examples are given in the tutorial in sections Sequences of Generator Values, Sequences of Lattice Definitions and Sequences of Merit Values .
The values in a sequence often need to be mapped to other values, possibly of a different type. For that purpose, LatNet Builder introduces the concept of a bridge sequence, which contains the mapped elements in the same order as the sequence it is based on.
Traditional object-oriented programming makes use of dynamic polymorphism, where the exact class of an object is resolved at execution time, dynamically. It follows that polymorphic member function calls cannot be inlined, so it is often not advisable to place such a call in the core of a loop with many iterations, at the risk of cumulating inderections due to both polymorphism and function calls.
Some algorithms implemented by LatNet Builder come in several variants which require either polymorphic function calls inside such loops or a large amount of duplicated code with tiny differences. C++ allows to circumvent that problem through the use of static polymorphism, which enables compile-time resolution of the types together with member function inlining.
In some places, static polymorphism is used regardless that dynamic polymorphism could have been used without any notable performance loss, in order to maintain a consistent design. We prefer making common usage patterns of LatNet Builder's classes easier to recognize, even if that means a little bit more verbose code in places.
The most obvious example is the case of sequence classes. Their member functions begin()
and end()
return iterators; the type of an iterator is often specific to the type of the sequence it is pointing to.
Another example is the size parameter is represented by a different class depending on whether ordinary or embedded lattices are considered. In both cases, the class implements a numPoints()
member function, that returns the number of points in the lattice, and that is accessed frequently in some parts of the code. In a traditional object-oriented approach, the two classes would derive from the same abstract base class, and would have required runtime object-type resolution at the call points of numPoints()
. With the static polymorphism approach, the numPoints()
function can be efficiently inlined by the compiler, thus avoiding both the overhead due to the function call and to runtime object-type resolution.
Concepts in C++ can be thought of as the static-polymorphism counterpart of dynamic-polymorphism interfaces. Like interfaces, concepts can specify requirements on the members of a class; unlike interfaces, the data types of the arguments and of the return value of a member function can differ across classes implementing the same concept. Furthermore, a concept can require type definitions (typedefs or nested classes) inside of a class. A well-known example of concept it that of iterator
or of const_iterator
from the STL.
Although requirements on the types are not described with concepts definitions in LatNet Builder, but the code is built around the idiom of concepts.
By convention in LatNet Builder, classes representing the same concept are regrouped in the same namespace.
The most important concepts are described by means of example in the Library Tutorial.
In LatNet Builder, when a non-trivial object is no longer needed in its scope after it is passed as an argument to a constructor or factory function, it is generally std::move()'d to the constructor or function in question. The compiler takes care of using the default move constructor on the object that is passed to the constructor and thus avoids copying the object, which can safely be stored as a class member of the owner object. Generator sequences (see the GenSeq namespace) are an example of non-trivial objects that are transferred to other owner objects using this mechanism. Weights objects, such as instances of LatticeTester::ProductWeights or of LatticeTester::ProjectionDependentWeights, are other examples of non-trivial objects, but because they are polymorphic, they are stored using a std::unique_ptr, which in turn, is passed using either std::move() or, equivalently, std::unique_ptr::release(). There are a few exceptions to that ownership policy, notably figures of merit like instances of WeightedFigureOfMerit or of CoordUniformFigureOfMerit. Some classes store references to such objects, so the objects themselves are also stored using std::unique_ptr so that references remain valid even after the object ownership has changed.
LatNet Builder makes uses of the following C++11 features:
LatNet Builder makes uses of the following C++14 features:
The LatNet Builder library depends on the following Boost libraries:
Only the Program Options, the System and the Filesystem libraries require building and linking; the others are header-only libraries.
The fast CBC implementation of LatNet Builder, as well as the computation of the \(R_\alpha\) criterion, depends on the FFTW library.
LatNet Builder also depends on the NTL library for arithmetic computations (lattices, polynomials, etc.).
LatNet Builder uses LatticeTester, a software library to compute theoretical measures of uniformity for lattices. This library is embedded in the code source of LatNet Builder and in the binary distribution packages.