Commit 74173986 authored by Christoph Groth's avatar Christoph Groth
Browse files

make NoSymmetry a subgroup of itself, and reorganize related code

parent dc02e80e
......@@ -333,22 +333,16 @@ class Symmetry(metaclass=abc.ABCMeta):
pass
@abc.abstractmethod
def isstrictsupergroup(self, other):
"""Test whether `self` is a strict supergroup of `other`...
def has_subgroup(self, other):
"""Test whether `self` has the subgroup `other`...
or, in other words, whether `other` is a subgroup of `self`. The
reason why this is the abstract method (and not `issubgroup`) is that
reason why this is the abstract method (and not `is_subgroup`) is that
in general it's not possible for a subgroup to know its supergroups.
"""
pass
def issubgroup(self, other):
"""Test whether `self` is a subgroup of `other`."""
return other.isstrictsupergroup(self)
__le__ = issubgroup
class NoSymmetry(Symmetry):
"""A symmetry with a trivial symmetry group."""
......@@ -389,8 +383,8 @@ class NoSymmetry(Symmetry):
raise ValueError('Generators must be empty for NoSymmetry.')
return NoSymmetry(generators)
def isstrictsupergroup(self, other):
return False
def has_subgroup(self, other):
return isinstance(other, NoSymmetry)
......@@ -1303,7 +1297,7 @@ class Builder:
templ_sym = template.symmetry
# Check that symmetries are commensurate.
if not self.symmetry <= templ_sym:
if not templ_sym.has_subgroup(self.symmetry):
raise ValueError("Builder symmetry is not a subgroup of the "
"template symmetry")
......
......@@ -555,7 +555,7 @@ class TranslationalSymmetry(builder.Symmetry):
raise ValueError('Generators must be sequences of integers.')
return TranslationalSymmetry(*ta.dot(generators, self.periods))
def isstrictsupergroup(self, other):
def has_subgroup(self, other):
if isinstance(other, builder.NoSymmetry):
return True
elif not isinstance(other, TranslationalSymmetry):
......
......@@ -129,7 +129,7 @@ class VerySimpleSymmetry(builder.Symmetry):
def num_directions(self):
return 1
def isstrictsupergroup(self, other):
def has_subgroup(self, other):
if isinstance(other, builder.NoSymmetry):
return True
elif isinstance(other, VerySimpleSymmetry):
......@@ -652,32 +652,36 @@ def test_fill():
return -100 <= site.pos[0] < 100
## Test that copying a builder by "fill" preserves everything.
cubic = kwant.lattice.general(ta.identity(3))
sym = kwant.TranslationalSymmetry((3, 0, 0), (0, 4, 0), (0, 0, 5))
# Make a weird system.
orig = kwant.Builder(sym)
sites = cubic.shape(lambda pos: True, (0, 0, 0))
for i, site in enumerate(orig.expand(sites)):
if i % 7 == 0:
continue
orig[site] = i
for i, hopp in enumerate(orig.expand(cubic.neighbors(1))):
if i % 11 == 0:
continue
orig[hopp] = i * 1.2345
for i, hopp in enumerate(orig.expand(cubic.neighbors(2))):
if i % 13 == 0:
continue
orig[hopp] = i * 1j
# Clone the original using fill.
clone = kwant.Builder(sym)
clone.fill(orig, lambda s: True, (0, 0, 0))
# Verify that both are identical.
assert set(clone.site_value_pairs()) == set(orig.site_value_pairs())
assert set(clone.hopping_value_pairs()) == set(orig.hopping_value_pairs())
for sym, func in [(kwant.TranslationalSymmetry(*np.diag([3, 4, 5])),
lambda pos: True),
(builder.NoSymmetry(),
lambda pos: ta.dot(pos, pos) < 17)]:
cubic = kwant.lattice.general(ta.identity(3))
# Make a weird system.
orig = kwant.Builder(sym)
sites = cubic.shape(func, (0, 0, 0))
for i, site in enumerate(orig.expand(sites)):
if i % 7 == 0:
continue
orig[site] = i
for i, hopp in enumerate(orig.expand(cubic.neighbors(1))):
if i % 11 == 0:
continue
orig[hopp] = i * 1.2345
for i, hopp in enumerate(orig.expand(cubic.neighbors(2))):
if i % 13 == 0:
continue
orig[hopp] = i * 1j
# Clone the original using fill.
clone = kwant.Builder(sym)
clone.fill(orig, lambda s: True, (0, 0, 0))
# Verify that both are identical.
assert set(clone.site_value_pairs()) == set(orig.site_value_pairs())
assert (set(clone.hopping_value_pairs())
== set(orig.hopping_value_pairs()))
## Test for warning when "start" is out.
target = builder.Builder()
......
......@@ -251,21 +251,23 @@ def test_symmetry_act():
sym.act(ta.array(el), site)
def test_symmetry_subgroup():
def test_symmetry_has_subgroup():
rng = np.random.RandomState(0)
## test whether actual subgroups are detected as such
vecs = rng.randn(3, 3)
sym1 = lattice.TranslationalSymmetry(*vecs)
assert sym1 >= sym1
assert sym1 >= builder.NoSymmetry()
assert sym1 >= lattice.TranslationalSymmetry(2 * vecs[0],
3 * vecs[1] + 4 * vecs[2])
assert not sym1 <= lattice.TranslationalSymmetry(*(0.8 * vecs))
ns = builder.NoSymmetry()
assert ns.has_subgroup(ns)
assert sym1.has_subgroup(sym1)
assert sym1.has_subgroup(ns)
assert sym1.has_subgroup(lattice.TranslationalSymmetry(
2 * vecs[0], 3 * vecs[1] + 4 * vecs[2]))
assert not lattice.TranslationalSymmetry(*(0.8 * vecs)).has_subgroup(sym1)
## test subgroup creation
for dim in range(1, 4):
generators = rng.randint(10, size=(dim, 3))
assert sym1.subgroup(*generators) <= sym1
assert sym1.has_subgroup(sym1.subgroup(*generators))
# generators are not linearly independent
with raises(ValueError):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment