... | ... | @@ -46,27 +46,28 @@ choice of ordering has two advantages: |
|
|
|
|
|
|
|
|
### Symmetries
|
|
|
Every system we will want to describe has an associated symmetry group. For a
|
|
|
Every system we will want to describe has an associated symmetry group. We are
|
|
|
only concerned with spatial symettries. For a generic
|
|
|
finite system this symmetry group is just the trivial group. For an infinite
|
|
|
system it is a translational symmetry characterized by N linearly independent
|
|
|
realspace vectors. All of the symmetry groups that we wish to deal with have a
|
|
|
representation as the additive group of tuples of integers (or integers modulo
|
|
|
some n for the case of finite subgroups such as discrete rotational symmetry).
|
|
|
For example, the symmetry group of a system with 1D translational symmetry and
|
|
|
6-fold rotational symmetry has a representation as `((ℤ, ℤ/6ℤ), +)`.
|
|
|
A symmetry can act on sites (by shifting their position) but they can also act
|
|
|
on wavefunctions (multiplication by a phase factor).
|
|
|
realspace vectors. There may also be some rotational symmetries, which may
|
|
|
render some symmetry groups non-abelian. For the most common cases, however,
|
|
|
the symmetry group is abelian (e.g. n-fold rotational symmetry + translational
|
|
|
symmetry).
|
|
|
|
|
|
Elements of the symmetry group will be represented in the low-level system as
|
|
|
tuples of integers. There will be a `Symmetry` object that is capable of
|
|
|
mapping this tuple-representation to a representation that can act on
|
|
|
wavefunctions or site positions[1]. We will require that the tuple that
|
|
|
contains all zeros must act as the identity.
|
|
|
All of the groups that we wish to describe are finitely generated. We can
|
|
|
therefore encode any group element as a sequence of pairs of integers.
|
|
|
The first integer in each pair is the index of the generator (the order
|
|
|
of the generators is defined by the Symmetry object), and the
|
|
|
second integer is the power to which the generator is raised. For abelian
|
|
|
groups it would be enough to store a sequence of integers - the powers to which
|
|
|
of the generators is raised - however this is not enough information for
|
|
|
non-abelian groups.
|
|
|
|
|
|
|
|
|
### Connection Sets
|
|
|
Once we have a system with sites, hoppings, and a given symmetry group we can
|
|
|
define what we will call the *connection set* of the system[2]. Informally we
|
|
|
define what we will call the *connection set* of the system[1]. Informally we
|
|
|
can define this in the following way. We take all the sites that are in the
|
|
|
fundamental domain of the symmetry group, or connected by a hopping to such
|
|
|
sites; call this set `S`. We now define the *connection set*, `C`, as the set
|
... | ... | @@ -82,14 +83,11 @@ lattice with nearest-neighbor hoppings the connection set would be `{(0, 0), |
|
|
|
|
|
---------------------
|
|
|
##### Footnotes
|
|
|
[1]: Currently, Symmetries are a uniquely high-level concept in Kwant, and only
|
|
|
the action of the group elements on sites is implemented.
|
|
|
|
|
|
[2]: This is not a very good name, hopefully a better one will be thought up
|
|
|
[1]: This is not a very good name, hopefully a better one will be thought up
|
|
|
|
|
|
|
|
|
## `System` API
|
|
|
Here we define the low-level system API[3].
|
|
|
Here we define the low-level system API[2].
|
|
|
|
|
|
### Data members
|
|
|
|
... | ... | @@ -99,47 +97,63 @@ that define the grouping of sites into low-level families. |
|
|
+ `symmetry`: an object that can act on a wavefunction, given a group element
|
|
|
(sequence of integers).
|
|
|
|
|
|
+ `connection_set`: sequence of arrays of integers. The arrays correspond to
|
|
|
the representation of the symmetry group elements mentioned above.
|
|
|
|
|
|
+ `elements`: sequence of arrays of pairs of integers; the hoppings in the
|
|
|
system. Also contains "self hoppings" `(i, i)` that represent on-site
|
|
|
values. The hoppings in an array must be from and to the same pair of
|
|
|
low-level site families.
|
|
|
+ `connection_set`: sequence of group elements. Each group element is a
|
|
|
sequence of pairs of integers, as described in the preliminary section on
|
|
|
symmetries.
|
|
|
|
|
|
+ `blocks`: sequence of tuples of integers,
|
|
|
`(sym_el, fam_to, fam_from, chunk, conj)` where `sym_el` indexes the
|
|
|
`connection_set`, `fam_to` and `fam_from` index the `site_families`,
|
|
|
`chunk` indexes the `elements`, and `conj` is a flag which signifies
|
|
|
that the complex conjugate of the hoppings specified should be taken.
|
|
|
+ `terms`: sequence of tuples of integers,
|
|
|
`(sym_el, fam_to, fam_from)` where `sym_el` indexes the
|
|
|
`connection_set`, `fam_to` and `fam_from` index the `site_families`.
|
|
|
|
|
|
### Methods
|
|
|
+ `hamiltonian_block`: evaluate the Hamiltonian of a single block.
|
|
|
Returns an array of shape `(N, norbs_fam_to, norbs_fam_from)` where
|
|
|
`N` is the number of hoppings in the block and the other numbers
|
|
|
are the number of orbitals in `fam_to` and `fam_from` for the block.
|
|
|
This method is implemented by subclasses, it is the equivalent of
|
|
|
the `hamiltonian` method of Kwant 1.x. This way, however, implementations
|
|
|
may vectorize the evaluation of the Hamiltonian over a block.
|
|
|
|
|
|
+ `hamiltonian`: evaluate the Hamiltonian associated with a particular
|
|
|
symmetry group element. This calls `hamiltonian_block` for each block
|
|
|
that has the given symmetry group element, and forms either a dense
|
|
|
matrix, or a sparse matrix in COO format.
|
|
|
+ `elements`: given an index into `terms`, returns a read-only sequence
|
|
|
of hoppings that are present in the given term of the Hamiltonian.
|
|
|
On-site elements are represented by "self-hoppings" `(i, i)`.
|
|
|
|
|
|
+ `hamiltonian_term`: evaluate a single term of the Hamiltonian.
|
|
|
Given an index into `terms`, and a dictionary of arguments,
|
|
|
returns an array of shape `(N, norbs_fam_to, norbs_fam_from)` where
|
|
|
`N` is the number of elements in the term and the `norbs_*`
|
|
|
are the number of orbitals in `fam_to` and `fam_from` for the term.
|
|
|
|
|
|
+ `hamiltonian`: evaluate the Hamiltonian of the system. Given a sequence
|
|
|
of representations for the generators of the symmetry group of the
|
|
|
Hamiltonian, returns either a sparse matrix in COO format, or a dense
|
|
|
matrix. The representations may be passed as a sequence of
|
|
|
|
|
|
|
|
|
## `Symmetry` API
|
|
|
This is a proposed API to low-level symmetries.
|
|
|
|
|
|
### Data members
|
|
|
|
|
|
+ `num_directions`: an integer -- the number of generators of the symmetry
|
|
|
+ `num_directions`: the number of generators of the symmetry group
|
|
|
+ `abelian`: True if the symmetry group is abelian, False otherwise
|
|
|
|
|
|
### Methods
|
|
|
+ `representation`: create a `U(N)` representation of a symmetry group element.
|
|
|
Returns either a dense `NxN` matrix or a sparse matrix in COO format
|
|
|
+ `simplify`: given a sequence of pairs of integers (representing
|
|
|
a symmetry group element, using the convention of `(generator_index, power)`)
|
|
|
returns a sequence of pairs of integers representing
|
|
|
|
|
|
|
|
|
### Discussion
|
|
|
|
|
|
##### Non-abelian Symmetry groups
|
|
|
Do we really need to support these? This makes the treatment of the symmetries
|
|
|
more complicated and for questionable gain.
|
|
|
|
|
|
##### The meaning of `terms`
|
|
|
The `terms` are just an arbitrary grouping of parts of the full Hamiltonian.
|
|
|
The abstract low-level system format requires that all parts of the Hamiltonian
|
|
|
grouped together into a single `term` must be associated with the same
|
|
|
symmetry group element and also with the same pair of site families.
|
|
|
|
|
|
Subclasses may then give further meaning to the `terms`. Finalized Builders,
|
|
|
for example, will most probably make `terms` from elements that are evaluated
|
|
|
by the same value function. We could imagine a low-level system optimized for
|
|
|
recursive Green's function that groups elements into `terms` corresponding
|
|
|
to a single slice.
|
|
|
|
|
|
##### More general class of systems
|
|
|
Might be useful to have a more general class of systems that that only knows
|
|
|
how to "act" on a wavefunction. If one has a system that is very connected (so
|
... | ... | @@ -147,12 +161,6 @@ that the Hamiltonian has essentially`O(N²)` non-zero elements) so that the |
|
|
Hamiltonian can barely fit in memory this may be useful. Are there some kind
|
|
|
of iterative algorithms that only require this?
|
|
|
|
|
|
In a similar vein, there should probably be a more general class of symmetries
|
|
|
that only know how to "act". This might often be more efficient than
|
|
|
constructing an explicit representation, e.g. for representations that are
|
|
|
actually `U(1)⊗𝟙ₙ`. They should also probably be able to act `m` times
|
|
|
on a wavefunction.
|
|
|
|
|
|
##### Actual data format
|
|
|
Above we have talked about `sequences` of various things. These could be
|
|
|
implemented as simple arrays, since they contain only simple types
|
... | ... | @@ -182,22 +190,20 @@ subgroup, then I guess the associated momenta will be quantized in units of |
|
|
|
|
|
We can see that the low-level format naturally provides the necessary
|
|
|
ingredients to be able to set up such a system.
|
|
|
|
|
|
##### Recursive Greens Function
|
|
|
Write this.
|
|
|
|
|
|
|
|
|
---------------------
|
|
|
##### Footnotes
|
|
|
[3]: We should probably define everything as arrays straight away to make it
|
|
|
[2]: We should probably define everything as arrays straight away to make it
|
|
|
easier to conform to the C system API.
|
|
|
|
|
|
|
|
|
## `kwant.builder.System` Implemetation
|
|
|
|
|
|
The low-level site families will correspond to the high-level site
|
|
|
families[4]. The `elements` will be grouped by value: a separate
|
|
|
families[3]. The `elements` will be grouped by value: a separate
|
|
|
array of hoppings (onsites) for each value function, and a separate
|
|
|
array for constant values. The `blocks` will thus refer to elements
|
|
|
array for constant values. The `terms` will thus refer to elements
|
|
|
that can be efficiently evaluated by a vectorized value function.
|
|
|
|
|
|
### Data members
|
... | ... | @@ -212,20 +218,23 @@ is only stored once). |
|
|
site family and then lexicographically by tag. The implementation
|
|
|
just looks up tags in `site_arrays`.
|
|
|
|
|
|
+ `values`: a sequence of the same length as `blocks`. Contains either
|
|
|
a callable or an array. If it is an array, it has shape
|
|
|
`(N, norbs_fam_to, norbs_fam_from)` where `N` is the length of the
|
|
|
`elements` pointed to by the corresponding `block`. If it is a
|
|
|
callable, then the first argument is a `Site` (or `SiteArray`
|
|
|
for vectorized value functions, see below). If the `elements`
|
|
|
are hoppings (i.e `site_to != site_from`) then the the second
|
|
|
argument is also a `Site`. This means we also need to either
|
|
|
explicitly store whether a particular sequence of `elements`
|
|
|
is onsites/hoppings, or check if the first pair has
|
|
|
`site_to == site_from` or not[5].
|
|
|
|
|
|
### Evaluating Hamiltonian Blocks
|
|
|
If a block has a corresponding `value` that is an array, return the array.
|
|
|
+ `term_elements`: a read-only sequence of arrays of pairs of integers; the
|
|
|
elements associated with each `term`.
|
|
|
|
|
|
+ `term_values`: a sequence of the same length as `terms`. Contains a pair
|
|
|
`(val, conj)`. `conj` is a flag that is true if the Hermitian conjugate of the
|
|
|
values is to be calculated. `val` is either a callable or an array. If it is an
|
|
|
array, it has shape `(N, norbs_fam_to, norbs_fam_from)` where `N` is the length
|
|
|
of the `elements` pointed to by the corresponding `term`. If it is a callable,
|
|
|
then the first argument is a `Site` (or `SiteArray` for vectorized value
|
|
|
functions, see below). If the `elements` are hoppings (i.e `site_to !=
|
|
|
site_from`) then the the second argument is also a `Site`. This means we also
|
|
|
need to either explicitly store whether a particular sequence of `elements` is
|
|
|
onsites/hoppings, or check if the first pair has `site_to == site_from` or
|
|
|
not[4].
|
|
|
|
|
|
### Evaluating Hamiltonian terms
|
|
|
If a term has a corresponding `value` that is an array, return the array.
|
|
|
Otherwise, if the callable `value` has an attribute `__vectorized__` then it
|
|
|
is called once with a `SiteArray` as its first (and second, for hoppings)
|
|
|
argument. If the callable is not vectorized then it is called once for
|
... | ... | @@ -235,7 +244,7 @@ is returned. |
|
|
|
|
|
### Discussion
|
|
|
|
|
|
##### Storing the `elements`
|
|
|
##### Storing the `term_elements`
|
|
|
It's kind of ugly to have to "check" if a particular sequence of elements
|
|
|
corresponds to onsites or hoppings. A distinction needs to be made because
|
|
|
in Kwant onsite value functions only take a *single* `Site`, whereas
|
... | ... | @@ -243,9 +252,9 @@ hopping value functions take *two* `Site`s. |
|
|
|
|
|
---------------------
|
|
|
##### Footnotes
|
|
|
[4]: Or not, see the discussion on implementing RGF
|
|
|
[3]: Or not, see the discussion on implementing RGF
|
|
|
|
|
|
[5]: This is ugly and weird, it should be tweaked. This may require a slight
|
|
|
[4]: This is ugly and weird, it should be tweaked. This may require a slight
|
|
|
re-definition of the low-level interface.
|
|
|
|
|
|
|
... | ... | @@ -291,27 +300,14 @@ typedef double complex complex_t ; |
|
|
struct CSymmetry_t {
|
|
|
|
|
|
uint_t num_directions ;
|
|
|
int abelian ;
|
|
|
|
|
|
// get a U(N) representation of a group element
|
|
|
// this representation can be used to act on wavefunctions
|
|
|
// returns a representation as a dense matrix
|
|
|
void (*get_dense_representation)(
|
|
|
const void* symmetry,
|
|
|
void (*simplify)(
|
|
|
const void* symmetry ;
|
|
|
const uint_t *group_element,
|
|
|
uint_t N,
|
|
|
complex_t* out
|
|
|
) ;
|
|
|
|
|
|
// get a U(N) representation of a group element
|
|
|
// this representation can be used to act on wavefunctions
|
|
|
// returns a representation as a sparse COO matrix
|
|
|
void (*get_sparse_representation)(
|
|
|
const void *symmetry, // the symmetry structure
|
|
|
const uint_t *group_element, // shape: (num_directions,)
|
|
|
uint_t N,
|
|
|
uint_t *out_row_idx,
|
|
|
uint_t *out_col_idx,
|
|
|
complex_t *out
|
|
|
uint_t n_factors,
|
|
|
uint_t *out_group_element,
|
|
|
uint_t *out_n_factors
|
|
|
) ;
|
|
|
|
|
|
} ;
|
... | ... | @@ -322,43 +318,50 @@ struct CSystem_t { |
|
|
uint_t n_site_fams ;
|
|
|
|
|
|
struct CSymmetry_t *symmetry ;
|
|
|
uint_t *connection_set ; // shape: (n_sym_els, symmetry->num_directions)
|
|
|
uint_t n_sym_els ;
|
|
|
|
|
|
|
|
|
// array of all the elements, ordered into chunks
|
|
|
uint_t *elements ; // shape: (n_elements, 2)
|
|
|
uint_t n_elements ;
|
|
|
// the indices of the first element in each chunk
|
|
|
uint_t *element_chunks ; // shape: (n_element_chunks,)
|
|
|
uint_t n_element_chunks ;
|
|
|
|
|
|
uint_t *blocks ; // shape: (n_blocks, 5)
|
|
|
uint_t n_blocks ;
|
|
|
uint_t **connection_set ; // pointers to symmetry group elements
|
|
|
uint_t n_sym_els ; // number of symmetry elements in connection set
|
|
|
uint_t *n_factors ; // number of pairs (generator, power) for each connection set element
|
|
|
|
|
|
uint_t *terms ; // shape: (n_terms, 3)
|
|
|
uint_t n_terms ;
|
|
|
|
|
|
// Get a pointer to the hoppings associated with a term
|
|
|
// The memory for the `out_elements` array is be managed internally
|
|
|
// by subclasses of CSystem_t. This prevents needless copying.
|
|
|
void (*elements)(
|
|
|
const void *system, // the system
|
|
|
uint_t term_index, // the index of the term
|
|
|
const uint_t **out_elements, // shape: (n_elements, 2)
|
|
|
uint_t *out_n_elements // number of elements
|
|
|
) ;
|
|
|
|
|
|
// Calculate a single Hamiltonian block
|
|
|
void (*hamiltonian_block)(
|
|
|
// The memory of the `out` array is managed by the caller
|
|
|
void (*hamiltonian_term)(
|
|
|
const void *system, // the system
|
|
|
gint_t block_index, // the index of the block to calculate
|
|
|
uint_t term_index, // the index of the term to calculate
|
|
|
const void *args, // the arguments to the Hamiltonian
|
|
|
complex_t *out // the array to hold the calculated Hamiltonian block
|
|
|
) ;
|
|
|
|
|
|
// Calculate the Hamiltonian and return in dense format
|
|
|
// The memory of the `out` array is managed by the caller
|
|
|
void (*hamiltonian_dense)(
|
|
|
const void *system, // the system
|
|
|
gint_t sym_index, // the index of the symmetry element to calculate
|
|
|
uint_t sym_index, // the index of the symmetry element to calculate
|
|
|
const void *args, // the arguments to the Hamiltonian
|
|
|
complex_t *out // the array to hold the calculated Hamiltonian block
|
|
|
) ;
|
|
|
|
|
|
// Calculate the Hamiltonian and return in sparse COO format
|
|
|
// The memory of the `out`, `out_row_idx` and `out_col_idx` arrays
|
|
|
// is managed by the caller
|
|
|
void (*hamiltonian_sparse)(
|
|
|
const void *system, // the system
|
|
|
gint_t sym_index, // the index of the symmetry element to calculate
|
|
|
uint_t sym_index, // the index of the symmetry element to calculate
|
|
|
const void *args, // the arguments to the Hamiltonian
|
|
|
gint_t *out_row_idx, // row indices of Hamiltonian elements
|
|
|
gint_t *out_col_idx, // column indices of Hamiltonian elements
|
|
|
uint_t *out_row_idx, // row indices of Hamiltonian elements
|
|
|
uint_t *out_col_idx, // column indices of Hamiltonian elements
|
|
|
complex_t *out_values // Hamiltonian values
|
|
|
) ;
|
|
|
} ;
|
... | ... | @@ -378,8 +381,8 @@ struct SitePair_t { |
|
|
uint_t to_site, from_site ;
|
|
|
}
|
|
|
|
|
|
struct Block_t {
|
|
|
uint_t sym_el, fam_to, fam_from, chunk, conj ;
|
|
|
struct Term_t {
|
|
|
uint_t sym_el, fam_to, fam_from ;
|
|
|
} ;
|
|
|
```
|
|
|
This would be more transparent, and might give us some performance
|
... | ... | |