N-dimensional systems will need symmetry elements, so the
symmetry definition needs to be in the system module.
... | ... |
@@ -21,7 +21,7 @@ import tinyarray as ta |
21 | 21 |
import numpy as np |
22 | 22 |
from scipy import sparse |
23 | 23 |
from . import system, graph, KwantDeprecationWarning, UserCodeError |
24 |
-from .system import Site, SiteArray, SiteFamily |
|
24 |
+from .system import Site, SiteArray, SiteFamily, Symmetry, NoSymmetry |
|
25 | 25 |
from .linalg import lll |
26 | 26 |
from .operator import Density |
27 | 27 |
from .physics import DiscreteSymmetry, magnetic_gauge |
... | ... |
@@ -29,7 +29,7 @@ from ._common import (ensure_isinstance, get_parameters, reraise_warnings, |
29 | 29 |
interleave, deprecate_args, memoize) |
30 | 30 |
|
31 | 31 |
|
32 |
-__all__ = ['Builder', 'Symmetry', 'HoppingKind', 'Lead', |
|
32 |
+__all__ = ['Builder', 'HoppingKind', 'Lead', |
|
33 | 33 |
'BuilderLead', 'SelfEnergyLead', 'ModesLead', 'add_peierls_phase'] |
34 | 34 |
|
35 | 35 |
|
... | ... |
@@ -64,183 +64,6 @@ def validate_hopping(hopping): |
64 | 64 |
raise ValueError("A hopping connects the following site to itself:\n" |
65 | 65 |
"{0}".format(a)) |
66 | 66 |
|
67 |
- |
|
68 |
- |
|
69 |
-################ Symmetries |
|
70 |
- |
|
71 |
-class Symmetry(metaclass=abc.ABCMeta): |
|
72 |
- """Abstract base class for spatial symmetries. |
|
73 |
- |
|
74 |
- Many physical systems possess a discrete spatial symmetry, which results in |
|
75 |
- special properties of these systems. This class is the standard way to |
|
76 |
- describe discrete spatial symmetries in Kwant. An instance of this class |
|
77 |
- can be passed to a `Builder` instance at its creation. The most important |
|
78 |
- kind of symmetry is translational symmetry, used to define scattering |
|
79 |
- leads. |
|
80 |
- |
|
81 |
- Each symmetry has a fundamental domain -- a set of sites and hoppings, |
|
82 |
- generating all the possible sites and hoppings upon action of symmetry |
|
83 |
- group elements. A class derived from `Symmetry` has to implement mapping |
|
84 |
- of any site or hopping into the fundamental domain, applying a symmetry |
|
85 |
- group element to a site or a hopping, and a method `which` to determine the |
|
86 |
- group element bringing some site from the fundamental domain to the |
|
87 |
- requested one. Additionally, it has to have a property `num_directions` |
|
88 |
- returning the number of independent symmetry group generators (number of |
|
89 |
- elementary periods for translational symmetry). |
|
90 |
- |
|
91 |
- A ``ValueError`` must be raised by the symmetry class whenever a symmetry |
|
92 |
- is used together with sites whose site family is not compatible with it. A |
|
93 |
- typical example of this is when the vector defining a translational |
|
94 |
- symmetry is not a lattice vector. |
|
95 |
- |
|
96 |
- The type of the domain objects as handled by the methods of this class is |
|
97 |
- not specified. The only requirement is that it must support the unary |
|
98 |
- minus operation. The reference implementation of `to_fd()` is hence |
|
99 |
- `self.act(-self.which(a), a, b)`. |
|
100 |
- """ |
|
101 |
- |
|
102 |
- @abc.abstractproperty |
|
103 |
- def num_directions(self): |
|
104 |
- """Number of elementary periods of the symmetry.""" |
|
105 |
- pass |
|
106 |
- |
|
107 |
- @abc.abstractmethod |
|
108 |
- def which(self, site): |
|
109 |
- """Calculate the domain of the site. |
|
110 |
- |
|
111 |
- Parameters |
|
112 |
- ---------- |
|
113 |
- site : `~kwant.system.Site` or `~kwant.system.SiteArray` |
|
114 |
- |
|
115 |
- Returns |
|
116 |
- ------- |
|
117 |
- group_element : tuple or sequence of tuples |
|
118 |
- A single tuple if ``site`` is a Site, or a sequence of tuples if |
|
119 |
- ``site`` is a SiteArray. The group element(s) whose action |
|
120 |
- on a certain site(s) from the fundamental domain will result |
|
121 |
- in the given ``site``. |
|
122 |
- """ |
|
123 |
- pass |
|
124 |
- |
|
125 |
- @abc.abstractmethod |
|
126 |
- def act(self, element, a, b=None): |
|
127 |
- """Act with symmetry group element(s) on site(s) or hopping(s). |
|
128 |
- |
|
129 |
- Parameters |
|
130 |
- ---------- |
|
131 |
- element : tuple or sequence of tuples |
|
132 |
- Group element(s) with which to act on the provided site(s) |
|
133 |
- or hopping(s) |
|
134 |
- a, b : `~kwant.system.Site` or `~kwant.system.SiteArray` |
|
135 |
- If Site then ``element`` is a single tuple, if SiteArray then |
|
136 |
- ``element`` is a single tuple or a sequence of tuples. |
|
137 |
- If only ``a`` is provided then ``element`` acts on the site(s) |
|
138 |
- of ``a``. If ``b`` is also provided then ``element`` acts |
|
139 |
- on the hopping(s) ``(a, b)``. |
|
140 |
- """ |
|
141 |
- pass |
|
142 |
- |
|
143 |
- def to_fd(self, a, b=None): |
|
144 |
- """Map a site or hopping to the fundamental domain. |
|
145 |
- |
|
146 |
- Parameters |
|
147 |
- ---------- |
|
148 |
- a, b : `~kwant.system.Site` or `~kwant.system.SiteArray` |
|
149 |
- |
|
150 |
- If ``b`` is None, return a site equivalent to ``a`` within the |
|
151 |
- fundamental domain. Otherwise, return a hopping equivalent to ``(a, |
|
152 |
- b)`` but where the first element belongs to the fundamental domain. |
|
153 |
- |
|
154 |
- Equivalent to `self.act(-self.which(a), a, b)`. |
|
155 |
- """ |
|
156 |
- return self.act(-self.which(a), a, b) |
|
157 |
- |
|
158 |
- def in_fd(self, site): |
|
159 |
- """Tell whether ``site`` lies within the fundamental domain. |
|
160 |
- |
|
161 |
- Parameters |
|
162 |
- ---------- |
|
163 |
- site : `~kwant.system.Site` or `~kwant.system.SiteArray` |
|
164 |
- |
|
165 |
- Returns |
|
166 |
- ------- |
|
167 |
- in_fd : bool or sequence of bool |
|
168 |
- single bool if ``site`` is a Site, or a sequence of |
|
169 |
- bool if ``site`` is a SiteArray. In the latter case |
|
170 |
- we return whether each site in the SiteArray is in |
|
171 |
- the fundamental domain. |
|
172 |
- """ |
|
173 |
- if isinstance(site, Site): |
|
174 |
- for d in self.which(site): |
|
175 |
- if d != 0: |
|
176 |
- return False |
|
177 |
- return True |
|
178 |
- elif isinstance(site, SiteArray): |
|
179 |
- which = self.which(site) |
|
180 |
- return np.logical_and.reduce(which != 0, axis=1) |
|
181 |
- else: |
|
182 |
- raise TypeError("'site' must be a Site or SiteArray") |
|
183 |
- |
|
184 |
- @abc.abstractmethod |
|
185 |
- def subgroup(self, *generators): |
|
186 |
- """Return the subgroup generated by a sequence of group elements.""" |
|
187 |
- pass |
|
188 |
- |
|
189 |
- @abc.abstractmethod |
|
190 |
- def has_subgroup(self, other): |
|
191 |
- """Test whether `self` has the subgroup `other`... |
|
192 |
- |
|
193 |
- or, in other words, whether `other` is a subgroup of `self`. The |
|
194 |
- reason why this is the abstract method (and not `is_subgroup`) is that |
|
195 |
- in general it's not possible for a subgroup to know its supergroups. |
|
196 |
- |
|
197 |
- """ |
|
198 |
- pass |
|
199 |
- |
|
200 |
- |
|
201 |
-class NoSymmetry(Symmetry): |
|
202 |
- """A symmetry with a trivial symmetry group.""" |
|
203 |
- |
|
204 |
- def __eq__(self, other): |
|
205 |
- return isinstance(other, NoSymmetry) |
|
206 |
- |
|
207 |
- def __ne__(self, other): |
|
208 |
- return not self.__eq__(other) |
|
209 |
- |
|
210 |
- def __repr__(self): |
|
211 |
- return 'NoSymmetry()' |
|
212 |
- |
|
213 |
- @property |
|
214 |
- def num_directions(self): |
|
215 |
- return 0 |
|
216 |
- |
|
217 |
- periods = () |
|
218 |
- |
|
219 |
- _empty_array = ta.array((), int) |
|
220 |
- |
|
221 |
- def which(self, site): |
|
222 |
- return self._empty_array |
|
223 |
- |
|
224 |
- def act(self, element, a, b=None): |
|
225 |
- if element: |
|
226 |
- raise ValueError('`element` must be empty for NoSymmetry.') |
|
227 |
- return a if b is None else (a, b) |
|
228 |
- |
|
229 |
- def to_fd(self, a, b=None): |
|
230 |
- return a if b is None else (a, b) |
|
231 |
- |
|
232 |
- def in_fd(self, site): |
|
233 |
- return True |
|
234 |
- |
|
235 |
- def subgroup(self, *generators): |
|
236 |
- if any(generators): |
|
237 |
- raise ValueError('Generators must be empty for NoSymmetry.') |
|
238 |
- return NoSymmetry(generators) |
|
239 |
- |
|
240 |
- def has_subgroup(self, other): |
|
241 |
- return isinstance(other, NoSymmetry) |
|
242 |
- |
|
243 |
- |
|
244 | 67 |
|
245 | 68 |
################ Hopping kinds |
246 | 69 |
|
... | ... |
@@ -514,7 +514,7 @@ class Monatomic(system.SiteFamily, Polyatomic): |
514 | 514 |
# The following class is designed such that it should avoid floating |
515 | 515 |
# point precision issues. |
516 | 516 |
|
517 |
-class TranslationalSymmetry(builder.Symmetry): |
|
517 |
+class TranslationalSymmetry(system.Symmetry): |
|
518 | 518 |
"""A translational symmetry defined in real space. |
519 | 519 |
|
520 | 520 |
An alias exists for this common name: ``kwant.TranslationalSymmetry``. |
... | ... |
@@ -9,7 +9,7 @@ |
9 | 9 |
"""Low-level interface of systems""" |
10 | 10 |
|
11 | 11 |
__all__ = [ |
12 |
- 'Site', 'SiteArray', 'SiteFamily', |
|
12 |
+ 'Site', 'SiteArray', 'SiteFamily', 'Symmetry', 'NoSymmetry', |
|
13 | 13 |
'System', 'VectorizedSystem', 'FiniteSystem', 'FiniteVectorizedSystem', |
14 | 14 |
'InfiniteSystem', 'InfiniteVectorizedSystem', |
15 | 15 |
'is_finite', 'is_infinite', 'is_vectorized', |
... | ... |
@@ -22,6 +22,7 @@ from copy import copy |
22 | 22 |
from collections import namedtuple |
23 | 23 |
from functools import total_ordering, lru_cache |
24 | 24 |
import numpy as np |
25 |
+import tinyarray as ta |
|
25 | 26 |
from . import _system |
26 | 27 |
from ._common import deprecate_args, KwantDeprecationWarning |
27 | 28 |
|
... | ... |
@@ -269,6 +270,180 @@ class SiteFamily: |
269 | 270 |
'site_family((1, 2))!') |
270 | 271 |
return Site(self, tag) |
271 | 272 |
|
273 |
+ |
|
274 |
+################ Symmetries |
|
275 |
+ |
|
276 |
+class Symmetry(metaclass=abc.ABCMeta): |
|
277 |
+ """Abstract base class for spatial symmetries. |
|
278 |
+ |
|
279 |
+ Many physical systems possess a discrete spatial symmetry, which results in |
|
280 |
+ special properties of these systems. This class is the standard way to |
|
281 |
+ describe discrete spatial symmetries in Kwant. An instance of this class |
|
282 |
+ can be passed to a `Builder` instance at its creation. The most important |
|
283 |
+ kind of symmetry is translational symmetry, used to define scattering |
|
284 |
+ leads. |
|
285 |
+ |
|
286 |
+ Each symmetry has a fundamental domain -- a set of sites and hoppings, |
|
287 |
+ generating all the possible sites and hoppings upon action of symmetry |
|
288 |
+ group elements. A class derived from `Symmetry` has to implement mapping |
|
289 |
+ of any site or hopping into the fundamental domain, applying a symmetry |
|
290 |
+ group element to a site or a hopping, and a method `which` to determine the |
|
291 |
+ group element bringing some site from the fundamental domain to the |
|
292 |
+ requested one. Additionally, it has to have a property `num_directions` |
|
293 |
+ returning the number of independent symmetry group generators (number of |
|
294 |
+ elementary periods for translational symmetry). |
|
295 |
+ |
|
296 |
+ A ``ValueError`` must be raised by the symmetry class whenever a symmetry |
|
297 |
+ is used together with sites whose site family is not compatible with it. A |
|
298 |
+ typical example of this is when the vector defining a translational |
|
299 |
+ symmetry is not a lattice vector. |
|
300 |
+ |
|
301 |
+ The type of the domain objects as handled by the methods of this class is |
|
302 |
+ not specified. The only requirement is that it must support the unary |
|
303 |
+ minus operation. The reference implementation of `to_fd()` is hence |
|
304 |
+ `self.act(-self.which(a), a, b)`. |
|
305 |
+ """ |
|
306 |
+ |
|
307 |
+ @abc.abstractproperty |
|
308 |
+ def num_directions(self): |
|
309 |
+ """Number of elementary periods of the symmetry.""" |
|
310 |
+ pass |
|
311 |
+ |
|
312 |
+ @abc.abstractmethod |
|
313 |
+ def which(self, site): |
|
314 |
+ """Calculate the domain of the site. |
|
315 |
+ |
|
316 |
+ Parameters |
|
317 |
+ ---------- |
|
318 |
+ site : `~kwant.system.Site` or `~kwant.system.SiteArray` |
|
319 |
+ |
|
320 |
+ Returns |
|
321 |
+ ------- |
|
322 |
+ group_element : tuple or sequence of tuples |
|
323 |
+ A single tuple if ``site`` is a Site, or a sequence of tuples if |
|
324 |
+ ``site`` is a SiteArray. The group element(s) whose action |
|
325 |
+ on a certain site(s) from the fundamental domain will result |
|
326 |
+ in the given ``site``. |
|
327 |
+ """ |
|
328 |
+ pass |
|
329 |
+ |
|
330 |
+ @abc.abstractmethod |
|
331 |
+ def act(self, element, a, b=None): |
|
332 |
+ """Act with symmetry group element(s) on site(s) or hopping(s). |
|
333 |
+ |
|
334 |
+ Parameters |
|
335 |
+ ---------- |
|
336 |
+ element : tuple or sequence of tuples |
|
337 |
+ Group element(s) with which to act on the provided site(s) |
|
338 |
+ or hopping(s) |
|
339 |
+ a, b : `~kwant.system.Site` or `~kwant.system.SiteArray` |
|
340 |
+ If Site then ``element`` is a single tuple, if SiteArray then |
|
341 |
+ ``element`` is a single tuple or a sequence of tuples. |
|
342 |
+ If only ``a`` is provided then ``element`` acts on the site(s) |
|
343 |
+ of ``a``. If ``b`` is also provided then ``element`` acts |
|
344 |
+ on the hopping(s) ``(a, b)``. |
|
345 |
+ """ |
|
346 |
+ pass |
|
347 |
+ |
|
348 |
+ def to_fd(self, a, b=None): |
|
349 |
+ """Map a site or hopping to the fundamental domain. |
|
350 |
+ |
|
351 |
+ Parameters |
|
352 |
+ ---------- |
|
353 |
+ a, b : `~kwant.system.Site` or `~kwant.system.SiteArray` |
|
354 |
+ |
|
355 |
+ If ``b`` is None, return a site equivalent to ``a`` within the |
|
356 |
+ fundamental domain. Otherwise, return a hopping equivalent to ``(a, |
|
357 |
+ b)`` but where the first element belongs to the fundamental domain. |
|
358 |
+ |
|
359 |
+ Equivalent to `self.act(-self.which(a), a, b)`. |
|
360 |
+ """ |
|
361 |
+ return self.act(-self.which(a), a, b) |
|
362 |
+ |
|
363 |
+ def in_fd(self, site): |
|
364 |
+ """Tell whether ``site`` lies within the fundamental domain. |
|
365 |
+ |
|
366 |
+ Parameters |
|
367 |
+ ---------- |
|
368 |
+ site : `~kwant.system.Site` or `~kwant.system.SiteArray` |
|
369 |
+ |
|
370 |
+ Returns |
|
371 |
+ ------- |
|
372 |
+ in_fd : bool or sequence of bool |
|
373 |
+ single bool if ``site`` is a Site, or a sequence of |
|
374 |
+ bool if ``site`` is a SiteArray. In the latter case |
|
375 |
+ we return whether each site in the SiteArray is in |
|
376 |
+ the fundamental domain. |
|
377 |
+ """ |
|
378 |
+ if isinstance(site, Site): |
|
379 |
+ for d in self.which(site): |
|
380 |
+ if d != 0: |
|
381 |
+ return False |
|
382 |
+ return True |
|
383 |
+ elif isinstance(site, SiteArray): |
|
384 |
+ which = self.which(site) |
|
385 |
+ return np.logical_and.reduce(which != 0, axis=1) |
|
386 |
+ else: |
|
387 |
+ raise TypeError("'site' must be a Site or SiteArray") |
|
388 |
+ |
|
389 |
+ @abc.abstractmethod |
|
390 |
+ def subgroup(self, *generators): |
|
391 |
+ """Return the subgroup generated by a sequence of group elements.""" |
|
392 |
+ pass |
|
393 |
+ |
|
394 |
+ @abc.abstractmethod |
|
395 |
+ def has_subgroup(self, other): |
|
396 |
+ """Test whether `self` has the subgroup `other`... |
|
397 |
+ |
|
398 |
+ or, in other words, whether `other` is a subgroup of `self`. The |
|
399 |
+ reason why this is the abstract method (and not `is_subgroup`) is that |
|
400 |
+ in general it's not possible for a subgroup to know its supergroups. |
|
401 |
+ |
|
402 |
+ """ |
|
403 |
+ pass |
|
404 |
+ |
|
405 |
+ |
|
406 |
+class NoSymmetry(Symmetry): |
|
407 |
+ """A symmetry with a trivial symmetry group.""" |
|
408 |
+ |
|
409 |
+ def __eq__(self, other): |
|
410 |
+ return isinstance(other, NoSymmetry) |
|
411 |
+ |
|
412 |
+ def __ne__(self, other): |
|
413 |
+ return not self.__eq__(other) |
|
414 |
+ |
|
415 |
+ def __repr__(self): |
|
416 |
+ return 'NoSymmetry()' |
|
417 |
+ |
|
418 |
+ @property |
|
419 |
+ def num_directions(self): |
|
420 |
+ return 0 |
|
421 |
+ |
|
422 |
+ periods = () |
|
423 |
+ |
|
424 |
+ _empty_array = ta.array((), int) |
|
425 |
+ |
|
426 |
+ def which(self, site): |
|
427 |
+ return self._empty_array |
|
428 |
+ |
|
429 |
+ def act(self, element, a, b=None): |
|
430 |
+ if element: |
|
431 |
+ raise ValueError('`element` must be empty for NoSymmetry.') |
|
432 |
+ return a if b is None else (a, b) |
|
433 |
+ |
|
434 |
+ def to_fd(self, a, b=None): |
|
435 |
+ return a if b is None else (a, b) |
|
436 |
+ |
|
437 |
+ def in_fd(self, site): |
|
438 |
+ return True |
|
439 |
+ |
|
440 |
+ def subgroup(self, *generators): |
|
441 |
+ if any(generators): |
|
442 |
+ raise ValueError('Generators must be empty for NoSymmetry.') |
|
443 |
+ return NoSymmetry(generators) |
|
444 |
+ |
|
445 |
+ def has_subgroup(self, other): |
|
446 |
+ return isinstance(other, NoSymmetry) |
|
272 | 447 |
|
273 | 448 |
|
274 | 449 |
################ Systems |