TRIQS/nda 1.3.0
Multi-dimensional array library for C++
|
In this example, we show how use, spot and define symmetries in nda arrays.
All the following code snippets are part of the same main function:
Here, we have defined some basic quantities/types:
Let's take a look at a simple example.
We start by defining a hermitian symmetry for our matrix. A symmetry is simply an object of type sym_func_t that specifies how elements in an array are related to one another.
As you can see, h_symmetry is a callable object that takes an idx_t and returns a sym_t object. The two lines have the following meaning:
Once all of the desired symmetries have been defined, we can construct an nda::sym_grp object:
The constructor of the symmetry group takes an array and a list of symmetries as input. It uses the shape of the array and the symmetries to find all symmetry classes.
For our \( 3 \times 3 \) hermitian matrix, there should be 6 symmetry classes corresponding to the 6 independent elements of the matrix:
Output:
A symmetry class contains all the elements that are related to each other by some symmetry. It stores the flat index of the element as well as the nda::operation specifying how it is related to a special representative element of the class. In practice, the representative element is simply the first element of the class.
Let us print all the symmetry classes:
Output:
Keep in mind that elements of our matrix have the following flat indices:
Output:
Now we can see why the elements 0, 4 and 8 are in their own symmetry class and elements 1 and 3, 2 and 6 and elements 5 and 7 are related to each other via a complex conjugation.
One of the features of symmetry groups is that they can be used to initialize or assign to an existing array with an initializer function satisfying the nda::NdaInitFunc concept:
An nda::NdaInitFunc object should take a multi-dimensional index as input and return a value for the given index. Since we are working with complex matrices, the return type in our case is std::complex<double>.
count_eval simply counts the number of times init_func has been called.
Let's initialize our array:
Output:
As expected the array is hermitian.
The advantage of using symmetry groups to initialize an array becomes clear when we check how often our init_func object has been called:
Output:
It has been called exactly once for each symmetry class.
In our simple example, this does not really make a difference but for large arrays where the number of symmetry classes is much smaller than the size of the array and where evaluating init_func is expensive, this can give a considerable speedup.
We have already learned above about representative elements of symmetry classes. The symmetry group object gives us access to those elements:
Output:
The representative elements in our case simply belong to the upper triangular part of the matrix.
Representative data can also be used to initialize or assign to an array. Let's try this out:
Output:
Here, we first multiplied the original representative data by 2 and then initialized a new array with it.
The nda::sym_grp class provides a method that let's us symmetrize an existing array and simultaneously obtain the maximum symmetry violation.
For each symmetry class, it first calculates the representative element by looping over all elements of the class, applying the stored nda::operation to each element (for a sign flip and a complex conjugation this is equal to applying the inverse operation) and summing the resulting values. The sum divided by the number of elements in the symmetry class gives us the representative element.
It then compares each element in the class to the representative element, notes their absolute difference to obtain the maximum symmetry violation and sets the element to the value of the representative element (modulo the nda::operation).
Symmetrizing one of our already hermitian matrices shouldn't change the matrix and the maximum symmetry violation should be zero:
Output:
Let's change one of the off-diagonal elements:
Output:
The matrix is not hermitian anymore and therefore violates our initial h_symmetry.
If we symmetrize again:
Output:
We can see that the symmetrized matrix is now different from the original and that the maximum symmetry violation is not equal to zero.