Browse code

move Symmetry and NoSymmetry to the system module

N-dimensional systems will need symmetry elements, so the
symmetry definition needs to be in the system module.

Joseph Weston authored on 26/11/2019 10:57:02
Showing 3 changed files
... ...
@@ -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