Previously we checked whether the subgraphs of a term used site
arrays corresponding to sites >= cell_size, now we can directly
check that the symmetry element of a term is not the identity (zero).
... | ... |
@@ -548,13 +548,12 @@ def vectorized_cell_hamiltonian(self, args=(), sparse=False, *, params=None): |
548 | 548 |
# Site array where next cell starts |
549 | 549 |
next_cell = bisect.bisect(site_offsets, self.cell_size) - 1 |
550 | 550 |
|
551 |
- def inside_cell(term): |
|
552 |
- (to_which, from_which), _= self.subgraphs[term.subgraph] |
|
553 |
- return to_which < next_cell and from_which < next_cell |
|
551 |
+ def inside_fd(term): |
|
552 |
+ return all(s == 0 for s in term.symmetry_element) |
|
554 | 553 |
|
555 | 554 |
cell_terms = [ |
556 | 555 |
n for n, t in enumerate(self.terms) |
557 |
- if inside_cell(t) |
|
556 |
+ if inside_fd(t) |
|
558 | 557 |
] |
559 | 558 |
|
560 | 559 |
subgraphs = [ |
... | ... |
@@ -593,34 +592,38 @@ def vectorized_cell_hamiltonian(self, args=(), sparse=False, *, params=None): |
593 | 592 |
def vectorized_inter_cell_hopping(self, args=(), sparse=False, *, params=None): |
594 | 593 |
"""Hopping Hamiltonian between two cells of the infinite system. |
595 | 594 |
|
595 |
+ This method returns a complex matrix that represents the hopping from |
|
596 |
+ the *interface sites* of unit cell ``n - 1`` to *all* the sites of |
|
597 |
+ unit cell ``n``. It is therefore generally a *rectangular* matrix of |
|
598 |
+ shape ``(N_uc, N_iface)`` where ``N_uc`` is the number of orbitals |
|
599 |
+ in the unit cell, and ``N_iface`` is the number of orbitals on the |
|
600 |
+ *interface* sites (i.e. the sites with hoppings *to* the next unit cell). |
|
601 |
+ |
|
596 | 602 |
Providing positional arguments via 'args' is deprecated, |
597 | 603 |
instead, provide named parameters as a dictionary via 'params'. |
598 | 604 |
""" |
599 | 605 |
|
600 |
- # Take advantage of the fact that there are distinct entries in |
|
601 |
- # onsite_terms for sites inside the unit cell, and distinct entries |
|
602 |
- # in onsite_terms for hoppings between the unit cell sites and |
|
603 |
- # interface sites. This way we only need to check the first entry |
|
604 |
- # in onsite/hopping_terms |
|
605 |
- |
|
606 | 606 |
site_offsets = np.cumsum([0] + [len(arr) for arr in self.site_arrays]) |
607 |
- # Site array where next cell starts |
|
608 |
- next_cell = bisect.bisect(site_offsets, self.cell_size) - 1 |
|
609 | 607 |
|
608 |
+ # This method is only meaningful for systems with a 1D translational |
|
609 |
+ # symmetry, and we use this fact in several places |
|
610 |
+ assert all(len(t.symmetry_element) == 1 for t in self.terms) |
|
611 |
+ |
|
612 |
+ # Symmetry element -1 means hoppings *from* the *previous* |
|
613 |
+ # unit cell. These are directly the hoppings we wish to return. |
|
610 | 614 |
inter_cell_hopping_terms = [ |
611 | 615 |
n for n, t in enumerate(self.terms) |
612 |
- # *from* site is in interface |
|
613 |
- if (self.subgraphs[t.subgraph][0][1] >= next_cell |
|
614 |
- and self.subgraphs[t.subgraph][0][0] < next_cell) |
|
616 |
+ if t.symmetry_element[0] == -1 |
|
615 | 617 |
] |
618 |
+ # Symmetry element +1 means hoppings *from* the *next* unit cell. |
|
619 |
+ # These are related by translational symmetry to hoppings *to* |
|
620 |
+ # the *previous* unit cell. We therefore need the *reverse* |
|
621 |
+ # (and conjugate) of these hoppings. |
|
616 | 622 |
reversed_inter_cell_hopping_terms = [ |
617 | 623 |
n for n, t in enumerate(self.terms) |
618 |
- # *to* site is in interface |
|
619 |
- if (self.subgraphs[t.subgraph][0][0] >= next_cell |
|
620 |
- and self.subgraphs[t.subgraph][0][1] < next_cell) |
|
624 |
+ if t.symmetry_element[0] == +1 |
|
621 | 625 |
] |
622 | 626 |
|
623 |
- # Evaluate inter-cell hoppings only |
|
624 | 627 |
inter_cell_hams = [ |
625 | 628 |
self.hamiltonian_term(n, args=args, params=params) |
626 | 629 |
for n in inter_cell_hopping_terms |
... | ... |
@@ -642,18 +645,21 @@ def vectorized_inter_cell_hopping(self, args=(), sparse=False, *, params=None): |
642 | 645 |
for n in reversed_inter_cell_hopping_terms |
643 | 646 |
] |
644 | 647 |
|
645 |
- # All the 'from' sites are in the previous domain, but to build a |
|
646 |
- # matrix we need to get the equivalent sites in the fundamental domain. |
|
647 |
- # We set the 'from' site array to the one from the fundamental domain. |
|
648 |
- subgraphs = [ |
|
649 |
- ((to_sa, from_sa - next_cell), (to_off, from_off)) |
|
650 |
- for (to_sa, from_sa), (to_off, from_off) in subgraphs |
|
651 |
- ] |
|
652 |
- |
|
653 | 648 |
_, norbs, orb_offsets = self.site_ranges.transpose() |
654 | 649 |
|
655 |
- shape = (orb_offsets[next_cell], |
|
656 |
- orb_offsets[len(self.site_arrays) - next_cell]) |
|
650 |
+ # SiteArrays containing interface sites appear before SiteArrays |
|
651 |
+ # containing non-interface sites, so the max of the site array |
|
652 |
+ # indices that appear in the 'from' site arrays of the inter-cell |
|
653 |
+ # hoppings allows us to get the number of interface orbitals. |
|
654 |
+ last_iface_site_array = max( |
|
655 |
+ from_site_array for (_, from_site_array), _ in subgraphs |
|
656 |
+ ) |
|
657 |
+ iface_norbs = orb_offsets[last_iface_site_array + 1] |
|
658 |
+ fd_norbs = orb_offsets[-1] |
|
659 |
+ |
|
660 |
+ # TODO: return a square matrix when we no longer need to maintain |
|
661 |
+ # backwards compatibility with unvectorized systems. |
|
662 |
+ shape = (fd_norbs, iface_norbs) |
|
657 | 663 |
func = _vectorized_make_sparse if sparse else _vectorized_make_dense |
658 | 664 |
mat = func(subgraphs, hams, norbs, orb_offsets, site_offsets, shape=shape) |
659 | 665 |
return mat |