Commit 7e7c3d94 authored by Joseph Weston's avatar Joseph Weston

Merge branch 'stable'

parents 691d8c85 f04429f9
image: kwant/testing
image: gitlab.kwant-project.org:5005/kwant/testing
stages:
- build
......@@ -12,7 +12,7 @@ variables:
IGNORE_HOSTKEY: "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
build package:
.build: &build
stage: build
script:
- echo -e "[DEFAULT]\ndefine_macros = CYTHON_TRACE=1" >build.conf
......@@ -22,12 +22,74 @@ build package:
untracked: true
expire_in: 1 hour
.stable-env: &stable_env
before_script:
- source deactivate
- source activate kwant-stable
- unset CFLAGS # https://github.com/conda-forge/toolchain-feedstock/issues/41
.no-extras-env: &no_extras_env
before_script:
- source deactivate
- source activate kwant-stable-no-extras
- unset CFLAGS # https://github.com/conda-forge/toolchain-feedstock/issues/41
# Note that this is 'latest' as of when the image was last built
.latest-env: &latest_env
before_script:
- source deactivate
- source activate kwant-latest
- unset CFLAGS # https://github.com/conda-forge/toolchain-feedstock/issues/41
.bleeding-edge-env: &bleeding_edge_env
before_script:
- source deactivate
- conda env update -f /kwant-latest.yml
- source activate kwant-latest
.ubuntu-env: &ubuntu_env
image: gitlab.kwant-project.org:5005/kwant/testing/ubuntu
.debian-env: &debian_env
image: gitlab.kwant-project.org:5005/kwant/testing/debian
## Build Jobs
build:ubuntu:
<<: *build
<<: *ubuntu_env
build:debian:
<<: *build
<<: *debian_env
build:stable:
<<: *build
<<: *stable_env
build:no-extras:
<<: *build
<<: *no_extras_env
build:latest:
<<: *build
<<: *latest_env
build:bleeding-edge:
<<: *build
<<: *bleeding_edge_env
only:
- schedules
## Test Jobs
check whitespace style:
stage: build
stage: test
script: ./check_whitespace
allow_failure: true
check for dependencies installed:
stage: test
script:
......@@ -35,10 +97,63 @@ check for dependencies installed:
allow_failure: true
.test: &test
stage: test
script:
- py.test -r w --cov=kwant --cov-report term --cov-report html --flakes kwant --junitxml=tests.xml
artifacts:
paths:
- htmlcov
reports:
junit: tests.xml
test:stable:
<<: *test
<<: *stable_env
dependencies:
- build:stable
test:no-extras:
<<: *test
<<: *no_extras_env
dependencies:
- build:no-extras
test:ubuntu:
<<: *test
<<: *ubuntu_env
dependencies:
- build:ubuntu
test:debian:
<<: *test
<<: *debian_env
dependencies:
- build:debian
test:latest:
<<: *test
<<: *latest_env
dependencies:
- build:latest
test:bleeding-edge:
<<: *test
<<: *bleeding_edge_env
dependencies:
- build:bleeding-edge
only:
- schedules
## Documentation building
build documentation:
<<: *latest_env
dependencies:
- build:latest
stage: test
script:
- pip3 install --upgrade matplotlib
- make -C doc realclean; make -C doc html SPHINXOPTS='-A website_deploy=True -n -W' SOURCE_LINK_TEMPLATE="$CI_PROJECT_URL"/blob/\$\$r/\$\$f
artifacts:
paths:
......@@ -46,10 +161,11 @@ build documentation:
expire_in: 1 month
build PDF documentation:
<<: *latest_env
dependencies:
- build:latest
stage: test
script:
- pip3 install sympy
- pip3 install --upgrade matplotlib
- make -C doc latex SPHINXOPTS='-n -W'
- cd doc/build/latex
- make all-pdf
......@@ -58,24 +174,18 @@ build PDF documentation:
- doc/build/latex/kwant.pdf
expire_in: 1 month
run tests:
stage: test
script:
- py.test -r w --cov=kwant --cov-report term --cov-report html --flakes kwant --junitxml=tests.xml
artifacts:
paths:
- htmlcov
reports:
junit: tests.xml
check for broken links in doc:
<<: *latest_env
dependencies:
- build:latest
stage: test
script:
- make -C doc linkcheck
allow_failure: true
## Conda package building
.conda-template: &conda_job
stage: deploy
image: condaforge/linux-anvil
......@@ -109,6 +219,7 @@ build and upload conda package (manual):
- master@kwant/kwant
when: manual
## Upload coverage reports and dev documentation
upload coverage:
stage: deploy
......@@ -196,7 +307,8 @@ upload dev version docs:
after_script:
- rm -rf ~/.ssh
# tagged version deploy
## Build documentation for tagged releases
.tagged-version: &tagged_version
only:
......
......@@ -158,7 +158,15 @@ def sympify(expr, locals=None):
'"locals" will not be used.',
RuntimeWarning,
stacklevel=2)
return expr
# We assume that all present functions, like "sin", "cos", will be
# provided by user during the final evaluation through "params".
# Therefore we make sure they are defined as AppliedUndef, not built-in
# sympy types.
subs = {r: sympy.Function(str(r.func))(*r.args)
for r in expr.atoms(sympy.Function)}
return expr.subs(subs)
# if ``expr`` is not a "sympy" then we proceed with sympifying process
if locals is None:
......@@ -189,14 +197,6 @@ def sympify(expr, locals=None):
else:
del converter[list]
# We assume that all present functions, like "sin", "cos", will be
# provided by user during the final evaluation through "params".
# Therefore we make sure they are define as AppliedUndef, not built-in
# sympy types.
subs = {r: sympy.Symbol(str(r.func))(*r.args)
for r in hamiltonian.atoms(sympy.Function)}
hamiltonian = hamiltonian.subs(subs)
return hamiltonian
......
......@@ -502,9 +502,16 @@ def _discretize_expression(expression, coords):
class _NumericPrinter(LambdaPrinter):
def __init__(self):
LambdaPrinter.__init__(self)
if 'allow_unknown_functions' in LambdaPrinter._default_settings:
settings = {'allow_unknown_functions': True}
else:
# We're on Sympy without "allow_unknown_functions" setting
settings = {}
LambdaPrinter.__init__(self, settings=settings)
self.known_functions = {}
self.known_constants = {'pi': 'pi', 'Pi': 'pi'}
self.known_constants = {'pi': 'pi', 'Pi': 'pi', 'I': 'I'}
def _print_ImaginaryUnit(self, expr):
# prevent sympy from printing 'I' for imaginary unit
......@@ -678,7 +685,10 @@ def _builder_value(expr, coords, grid_spacing, onsite,
header = 'def {}({}):'.format(name, site_string)
func_code = separator.join([header] + list(lines))
namespace = {'pi': np.pi}
# Add "I" to namespace just in case sympy again would miss to replace it
# with Python's 1j as it was the case with SymPy 1.2 when I was argument
# of some function.
namespace = {'pi': np.pi, 'I': 1j}
namespace.update(_cache)
source = []
......
......@@ -20,17 +20,18 @@ from kwant.continuum._common import lambdify
com_A, com_B, com_C = sympy.symbols('A B C')
fA, fB, fC = sympy.symbols('A B C', cls=sympy.Function)
x_op, y_op, z_op = position_operators
kx, ky, kz = momentum_operators
@pytest.mark.parametrize('input_expr, output_expr', [
('k_x * A(x) * k_x', kx * com_A(x_op) * kx),
('[[k_x * A(x) * k_x]]', sympy.Matrix([kx * com_A(x_op) * kx])),
('k_x * A(x) * k_x', kx * fA(x_op) * kx),
('[[k_x * A(x) * k_x]]', sympy.Matrix([kx * fA(x_op) * kx])),
('k_x * sigma_y + k_y * sigma_x', kx * msigma(2) + ky * msigma(1)),
('[[k_x*A(x)*k_x, B(x, y)*k_x], [k_x*B(x, y), C*k_y**2]]',
sympy.Matrix([[kx*com_A(x_op)*kx, com_B(x_op, y_op)*kx],
[kx*com_B(x_op, y_op), com_C*ky**2]])),
sympy.Matrix([[kx*fA(x_op)*kx, fB(x_op, y_op)*kx],
[kx*fB(x_op, y_op), com_C*ky**2]])),
('kron(sigma_x, sigma_y)', TensorProduct(msigma(1), msigma(2))),
('identity(2)', sympy.eye(2)),
('eye(2)', sympy.eye(2)),
......@@ -46,7 +47,7 @@ def test_sympify(input_expr, output_expr):
('k_x', kx + ky, {'k_x': 'k_x + k_y'}),
('x', x_op + y_op, {'x': 'x + y'}),
('A', com_A + com_B, {'A': 'A + B'}),
('A', com_A + com_B(x_op), {'A': 'A + B(x)'}),
('A', com_A + fB(x_op), {'A': 'A + B(x)'}),
('A', msigma(2), {'A': "[[0, -1j], [1j, 0]]"}),
])
def test_sympify_substitutions(input_expr, output_expr, subs):
......@@ -102,10 +103,11 @@ def test_make_commutative():
matr_monomials = sympify("[[x+y, a*x**2 + b*y], [y, x]]")
x, y, z = position_operators
a, b = sympy.symbols('a, b')
fA, fB = sympy.symbols('A B', cls=sympy.Function)
@pytest.mark.parametrize('expr, gens, output', [
(x * a(x) * x + x**2 * a, None, {x**2: a(x), a*x**2: 1}),
(x * a(x) * x + x**2 * a, [x], {x**2: a(x) + a}),
(x * fA(x) * x + x**2 * a, None, {x**2: fA(x), a*x**2: 1}),
(x * fA(x) * x + x**2 * a, [x], {x**2: fA(x) + a}),
(x**2, [x], {x**2: 1}),
(2 * x + 3 * x**2, [x], {x: 2, x**2: 3}),
(2 * x + 3 * x**2, 'x', {x: 2, x**2: 3}),
......
......@@ -51,6 +51,7 @@ a = sympy.symbols('a')
wf = _wf
Psi = wf(x, y, z)
A, B = sympy.symbols('A B', commutative=False)
fA, fB = sympy.symbols('A B', cls=sympy.Function)
ns = {'A': A, 'B': B, 'a_x': ax, 'a_y': ay, 'az': az, 'x': x, 'y': y, 'z': z}
......@@ -65,8 +66,8 @@ def test_reading_coordinates(commutative):
kx**2 + ky**2 + kz**2 : ['x', 'y', 'z'],
ky**2 + kz**2 : ['y', 'z'],
kz**2 : ['z'],
kx * A(x, y) * kx : ['x'],
kx**2 + kz * B(y) : ['x', 'z'],
kx * fA(x, y) * kx : ['x'],
kx**2 + kz * fB(y) : ['x', 'z'],
}
for inp, out in test.items():
ham, got = discretize_symbolic(inp)
......@@ -81,8 +82,8 @@ def test_reading_coordinates_matrix():
(sympy.Matrix([kx**2 + ky**2 + kz**2]) , ['x', 'y', 'z']),
(sympy.Matrix([ky**2 + kz**2]) , ['y', 'z']),
(sympy.Matrix([kz**2]) , ['z']),
(sympy.Matrix([kx * A(x, y) * kx]) , ['x']),
(sympy.Matrix([kx**2 + kz * B(y)]) , ['x', 'z']),
(sympy.Matrix([kx * fA(x, y) * kx]) , ['x']),
(sympy.Matrix([kx**2 + kz * fB(y)]) , ['x', 'z']),
]
for inp, out in test:
ham, got = discretize_symbolic(inp)
......@@ -121,16 +122,16 @@ def test_simple_derivations(commutative):
kz**2 : {(0,): 2/az**2, (1,): -1/az**2},
}
non_commutative_test = {
kx * A(x, y) * kx : {(1, ): -A(ax/2 + x, y)/ax**2,
(0, ): A(-ax/2 + x, y)/ax**2 + A(ax/2 + x, y)/ax**2},
kx**2 + kz * B(y) : {(1, 0): -1/ax**2, (0, 1): -I*B(y)/(2*az),
kx * fA(x, y) * kx : {(1, ): -fA(ax/2 + x, y)/ax**2,
(0, ): fA(-ax/2 + x, y)/ax**2 + fA(ax/2 + x, y)/ax**2},
kx**2 + kz * fB(y) : {(1, 0): -1/ax**2, (0, 1): -I*fB(y)/(2*az),
(0, 0): 2/ax**2},
kx * A(x) : {(0,): 0, (1,): -I*A(ax + x)/(2*ax)},
ky * A(x) : {(1,): -I*A(x)/(2*ay), (0,): 0},
kx * A(x) * B : {(0,): 0, (1,): -I*A(ax + x)*B/(2*ax)},
kx * fA(x) : {(0,): 0, (1,): -I*fA(ax + x)/(2*ax)},
ky * fA(x) : {(1,): -I*fA(x)/(2*ay), (0,): 0},
kx * fA(x) * B : {(0,): 0, (1,): -I*fA(ax + x)*B/(2*ax)},
5 * kx : {(0,): 0, (1,): -5*I/(2*ax)},
kx * (A(x) + B(x)) : {(0,): 0,
(1,): -I*A(ax + x)/(2*ax) - I*B(ax + x)/(2*ax)},
kx * (fA(x) + fB(x)) : {(0,): 0,
(1,): -I*fA(ax + x)/(2*ax) - I*fB(ax + x)/(2*ax)},
}
if not commutative:
......@@ -170,16 +171,16 @@ def test_simple_derivations_matrix():
(1, 0): -1/ay**2},
kz**2 : {(0,): 2/az**2, (1,): -1/az**2},
kx * A(x, y) * kx : {(1, ): -A(ax/2 + x, y)/ax**2,
(0, ): A(-ax/2 + x, y)/ax**2 + A(ax/2 + x, y)/ax**2},
kx**2 + kz * B(y) : {(1, 0): -1/ax**2, (0, 1): -I*B(y)/(2*az),
kx * fA(x, y) * kx : {(1, ): -fA(ax/2 + x, y)/ax**2,
(0, ): fA(-ax/2 + x, y)/ax**2 + fA(ax/2 + x, y)/ax**2},
kx**2 + kz * fB(y) : {(1, 0): -1/ax**2, (0, 1): -I*fB(y)/(2*az),
(0, 0): 2/ax**2},
kx * A(x) : {(0,): 0, (1,): -I*A(ax + x)/(2*ax)},
ky * A(x) : {(1,): -I*A(x)/(2*ay), (0,): 0},
kx * A(x) * B : {(0,): 0, (1,): -I*A(ax + x)*B/(2*ax)},
kx * fA(x) : {(0,): 0, (1,): -I*fA(ax + x)/(2*ax)},
ky * fA(x) : {(1,): -I*fA(x)/(2*ay), (0,): 0},
kx * fA(x) * B : {(0,): 0, (1,): -I*fA(ax + x)*B/(2*ax)},
5 * kx : {(0,): 0, (1,): -5*I/(2*ax)},
kx * (A(x) + B(x)) : {(0,): 0,
(1,): -I*A(ax + x)/(2*ax) - I*B(ax + x)/(2*ax)},
kx * (fA(x) + fB(x)) : {(0,): 0,
(1,): -I*fA(ax + x)/(2*ax) - I*fB(ax + x)/(2*ax)},
}
new_test = []
......@@ -296,10 +297,10 @@ def test_different_discrete_coordinates():
def test_non_expended_input():
symbolic, coords = discretize_symbolic(kx * (kx + A(x)))
symbolic, coords = discretize_symbolic(kx * (kx + fA(x)))
desired = {
(0,): 2/ax**2,
(1,): -I*A(ax + x)/(2*ax) - 1/ax**2
(1,): -I*fA(ax + x)/(2*ax) - 1/ax**2
}
assert symbolic == desired
......@@ -308,8 +309,8 @@ def test_matrix_with_zeros():
Matrix = sympy.Matrix
symbolic, _ = discretize_symbolic("[[k_x*A(x)*k_x, 0], [0, k_x*A(x)*k_x]]")
output = {
(0,): Matrix([[A(-ax/2 + x)/ax**2 + A(ax/2 + x)/ax**2, 0], [0, A(-ax/2 + x)/ax**2 + A(ax/2 + x)/ax**2]]),
(1,): Matrix([[-A(ax/2 + x)/ax**2, 0], [0, -A(ax/2 + x)/ax**2]]),
(0,): Matrix([[fA(-ax/2 + x)/ax**2 + fA(ax/2 + x)/ax**2, 0], [0, fA(-ax/2 + x)/ax**2 + fA(ax/2 + x)/ax**2]]),
(1,): Matrix([[-fA(ax/2 + x)/ax**2, 0], [0, -fA(ax/2 + x)/ax**2]]),
}
assert symbolic == output
......@@ -354,20 +355,26 @@ def test_numeric_functions_not_discrete_coords():
assert onsite(None, k_y=2, y=1) == 2 + 1
def test_numeric_functions_with_pi():
# Two cases because once it is casted
# to complex, one there is a function created
builder = discretize('A + pi', 'x')
lat = next(iter(builder.sites()))[0]
onsite = builder[lat(0)]
assert onsite(None, A=1) == 1 + np.pi
builder = discretize('pi', 'x')
@pytest.mark.parametrize('ham, val, params', [
("pi", np.pi, {}),
("A + pi", 1 + np.pi, {"A": 1}),
("A + B(pi)", 1 + np.pi, {"A": 1, "B": lambda x: x}),
("A + I", 1 + 1j, {"A": 1}),
("A + 1j", 1 + 1j, {"A": 1}),
("A + B(I)", 1 + 1j, {"A": 1, "B": lambda x: x}),
("A + B(1j)", 1 + 1j, {"A": 1, "B": lambda x: x}),
("exp(1j * pi)", np.exp(1j*np.pi), {"exp": np.exp}),
(sympy.exp(sympy.sympify("1j * pi * A")), np.exp(1j*np.pi),
{"exp": np.exp, "A": 1}),
])
def test_numeric_functions_advanced(ham, val, params):
builder = discretize(ham, 'x')
lat = next(iter(builder.sites()))[0]
onsite = builder[lat(0)]
assert onsite == np.pi
try:
assert np.allclose(onsite(None, **params), val)
except TypeError:
assert np.allclose(onsite, val)
def test_numeric_functions_basic_string():
......@@ -422,9 +429,9 @@ def test_numeric_functions_advance():
hams = [
kx**2,
kx**2 + x,
A(x),
kx*A(x)*kx,
sympy.Matrix([[kx * A(x) * kx, A(x)*kx], [kx*A(x), A(x)+B]]),
fA(x),
kx*fA(x)*kx,
sympy.Matrix([[kx * fA(x) * kx, fA(x)*kx], [kx*fA(x), fA(x)+B]]),
kx**2 + B * x,
'k_x**2 + sin(x)',
B ** 0.5 * kx**2,
......@@ -434,12 +441,12 @@ def test_numeric_functions_advance():
]
for hamiltonian in hams:
for a in [1, 2, 5]:
for fA in [lambda x: x, lambda x: x**2, lambda x: x**3]:
for func in [lambda x: x, lambda x: x**2, lambda x: x**3]:
symbolic, coords = discretize_symbolic(hamiltonian, 'x')
builder = build_discretized(symbolic, coords, grid=a)
lat = next(iter(builder.sites()))[0]
p = dict(A=fA, B=5, sin=np.sin)
p = dict(A=func, B=5, sin=np.sin)
# test onsite
v = symbolic.pop((0,)).subs({sympy.symbols('a_x'): a, B: p['B']})
......@@ -449,10 +456,10 @@ def test_numeric_functions_advance():
if callable(f_num):
f_num = swallows_extra_kwargs(f_num)
for n in range(-100, 100, 10):
assert np.allclose(f_sym(fA, a*n), f_num(lat(n), **p))
assert np.allclose(f_sym(func, a*n), f_num(lat(n), **p))
else:
for n in range(-100, 100, 10):
assert np.allclose(f_sym(fA, a*n), f_num)
assert np.allclose(f_sym(func, a*n), f_num)
# test hoppings
......@@ -464,7 +471,7 @@ def test_numeric_functions_advance():
if callable(f_num):
f_num = swallows_extra_kwargs(f_num)
for n in range(10):
lhs = f_sym(fA, a * n)
lhs = f_sym(func, a * n)
rhs = f_num(lat(n), lat(n+k[0]), **p)
assert np.allclose(lhs, rhs)
else:
......@@ -476,15 +483,15 @@ def test_numeric_functions_advance():
def test_numeric_functions_with_parameter():
hamiltonian = kx**2 + A(B, x)
hamiltonian = kx**2 + fA(B, x)
for a in [1, 2, 5]:
for fA in [lambda c, x: x+c, lambda c, x: x**2 + c]:
for func in [lambda c, x: x+c, lambda c, x: x**2 + c]:
symbolic, coords = discretize_symbolic(hamiltonian, 'x')
builder = build_discretized(symbolic, coords, grid=a)
lat = next(iter(builder.sites()))[0]
p = dict(A=fA, B=5)
p = dict(A=func, B=5)
# test onsite
v = symbolic.pop((0,)).subs({sympy.symbols('a_x'): a, B: p['B']})
......@@ -498,9 +505,9 @@ def test_numeric_functions_with_parameter():
s = lat(n)
xi = a * n
if callable(f_num):
assert np.allclose(f_sym(fA, xi), f_num(s, **p))
assert np.allclose(f_sym(func, xi), f_num(s, **p))
else:
assert np.allclose(f_sym(fA, xi), f_num)
assert np.allclose(f_sym(func, xi), f_num)
# test hoppings
for k, v in symbolic.items():
......@@ -515,7 +522,7 @@ def test_numeric_functions_with_parameter():
s = lat(n)
xi = a * n
lhs = f_sym(fA, xi)
lhs = f_sym(func, xi)
if callable(f_num):
rhs = f_num(lat(n), lat(n+k[0]), **p)
else:
......
......@@ -39,6 +39,7 @@ from kwant import plotter
from kwant import _plotter # for mpl_available
@pytest.mark.skipif(not plotter.mpl_available, reason="Matplotlib unavailable.")
def test_matplotlib_backend_unset():
"""Simply importing Kwant should not set the matplotlib backend."""
assert matplotlib_backend_chosen is False
......
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