diff --git a/poetry.lock b/poetry.lock index ce86f43..3350f71 100644 --- a/poetry.lock +++ b/poetry.lock @@ -20,6 +20,14 @@ six = "*" [package.extras] test = ["astroid", "pytest"] +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + [[package]] name = "attrs" version = "21.4.0" @@ -298,6 +306,14 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_version >= \"3.8\""} +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "ipython" version = "8.0.1" @@ -525,6 +541,18 @@ python-versions = ">=3.7" docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + [[package]] name = "prettytable" version = "3.1.1" @@ -577,6 +605,14 @@ python-versions = "*" [package.extras] tests = ["pytest"] +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + [[package]] name = "pygments" version = "2.11.2" @@ -612,6 +648,27 @@ category = "main" optional = false python-versions = ">=3.7" +[[package]] +name = "pytest" +version = "7.0.1" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +tomli = ">=1.0.0" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + [[package]] name = "python-dateutil" version = "2.8.2" @@ -857,7 +914,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [metadata] lock-version = "1.1" python-versions = ">=3.9,<3.11" -content-hash = "344fb808e013d47037546383e09e7d7c0dd9d78f980c86c074d1a34d85240cfd" +content-hash = "ba1521fe96275442a9a2a757978968604e7947d3e0fe7b715f4585a8b800ef1b" [metadata.files] appnope = [ @@ -868,6 +925,10 @@ asttokens = [ {file = "asttokens-2.0.5-py2.py3-none-any.whl", hash = "sha256:0844691e88552595a6f4a4281a9f7f79b8dd45ca4ccea82e5e05b4bbdb76705c"}, {file = "asttokens-2.0.5.tar.gz", hash = "sha256:9a54c114f02c7a9480d56550932546a3f1fe71d8a02f1bc7ccd0ee3ee35cf4d5"}, ] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] attrs = [ {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, @@ -1016,6 +1077,10 @@ humanfriendly = [ {file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"}, {file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"}, ] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] ipython = [ {file = "ipython-8.0.1-py3-none-any.whl", hash = "sha256:c503a0dd6ccac9c8c260b211f2dd4479c042b49636b097cc9a0d55fe62dff64c"}, {file = "ipython-8.0.1.tar.gz", hash = "sha256:ab564d4521ea8ceaac26c3a2c6e5ddbca15c8848fd5a5cc325f960da88d42974"}, @@ -1266,6 +1331,10 @@ platformdirs = [ {file = "platformdirs-2.5.1-py3-none-any.whl", hash = "sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227"}, {file = "platformdirs-2.5.1.tar.gz", hash = "sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d"}, ] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] prettytable = [ {file = "prettytable-3.1.1-py3-none-any.whl", hash = "sha256:f546d42de21ac14d791c91e4e9814f02271f4a5bf27b894457914be7513dc92b"}, {file = "prettytable-3.1.1.tar.gz", hash = "sha256:43c9e23272ca253d038ae76fe3adde89794e92e7fcab2ddf5b94b38642ef4f21"}, @@ -1310,6 +1379,10 @@ pure-eval = [ {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, ] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] pygments = [ {file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"}, {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, @@ -1345,6 +1418,10 @@ pyrsistent = [ {file = "pyrsistent-0.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07"}, {file = "pyrsistent-0.18.1.tar.gz", hash = "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96"}, ] +pytest = [ + {file = "pytest-7.0.1-py3-none-any.whl", hash = "sha256:9ce3ff477af913ecf6321fe337b93a2c0dcf2a0a1439c43f5452112c1e4280db"}, + {file = "pytest-7.0.1.tar.gz", hash = "sha256:e30905a0c131d3d94b89624a1cc5afec3e0ba2fbdb151867d8e0ebd49850f171"}, +] python-dateutil = [ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, diff --git a/pyproject.toml b/pyproject.toml index 8b4477b..2299827 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,14 @@ beartype = "^0.10.1" [tool.poetry.dev-dependencies] mypy = "^0.910" ipython = "^8.0.1" +pytest = "^7.0.1" [build-system] requires = ["poetry>=0.12"] build-backend = "poetry.core.masonry.api" + +[tool.pytest.ini_options] +filterwarnings = [ + "ignore::DeprecationWarning", + "ignore:this implementation:UserWarning", +] diff --git a/test_two_qubit_model.py b/test_two_qubit_model.py new file mode 100644 index 0000000..5ef418f --- /dev/null +++ b/test_two_qubit_model.py @@ -0,0 +1,147 @@ +import pytest +import random +from two_qubit_model import * +from qutip import * + + +def assert_serializable(model: TwoQubitModel): + """ + Serialize and restore ``model`` into json asserting that the + objects stay the same. + """ + + assert model == TwoQubitModel.from_json( + model.to_json() + ), "Serialization should not change the model." + + +class TestBasicConfigs: + def test_xx_zz(self): + model = TwoQubitModel(ω_2=2, γ=0.4, δ=[0.1, 0.2], s_vec=[[0, 1], [0, 1]]) + assert_serializable(model) + + assert model.local_system(0) == 1 / 2 * (tensor(sigmaz(), identity(2))) + assert model.local_system(1) == (tensor(identity(2), sigmaz())) + + for j in [1, 1000]: + model.j[0, 0] = j + assert model.system == 1 / 2 * ( + tensor(sigmaz(), identity(2)) + tensor(identity(2), sigmaz()) * 2 + ) + 0.4 / 2 * tensor(sigmax(), sigmax()) + + for s in [1, 10]: + model.s_vec[0][1] = s + model.s_vec[1][1] = s + + assert model.bath_coupling(0) == tensor(sigmaz(), identity(2)) + assert model.bath_coupling(1) == tensor(identity(2), sigmaz()) + + assert model.bcf_scale(0) == 0.1**2 + assert model.bcf_scale(1) == 0.2**2 + + def test_xy_zz(self): + model = TwoQubitModel(ω_2=0.11, γ=10, δ=[23, 123], s_vec=[[0, 1], [0, 1]]) + model.j[0, 0] = 0 + model.j[0, 1] = 123 + + assert_serializable(model) + + for j in [1, 1000]: + model.j[0, 1] = j + assert model.system == 1 / 2 * ( + tensor(sigmaz(), identity(2)) + tensor(identity(2), sigmaz()) * 0.11 + ) + 10 / 2 * tensor(sigmax(), sigmay()) + + for s in [1, 10e4]: + model.s_vec[0][1] = s + model.s_vec[1][1] = s + + assert model.bath_coupling(0) == tensor(sigmaz(), identity(2)) + assert model.bath_coupling(1) == tensor(identity(2), sigmaz()) + + def test_yx_zx(self): + model = TwoQubitModel(ω_2=0.11, γ=10, δ=[23, 123], s_vec=[[0, 1], [1, 0]]) + model.j[0, 0] = 0 + model.j[1, 0] = 123 + + assert_serializable(model) + + for j in [1, 1000]: + model.j[1, 0] = j + assert model.system == 1 / 2 * ( + tensor(sigmaz(), identity(2)) + tensor(identity(2), sigmaz()) * 0.11 + ) + 10 / 2 * tensor(sigmay(), sigmax()) + + for s in [1, 10e4]: + model.s_vec[0][1] = s + model.s_vec[1][0] = s + + assert model.bath_coupling(0) == tensor(sigmaz(), identity(2)) + assert model.bath_coupling(1) == tensor(identity(2), sigmax()) + + def test_yx_xz(self): + model = TwoQubitModel(ω_2=0.11, γ=10, δ=[23, 123], s_vec=[[1, 0], [0, 1]]) + model.j[0, 0] = 0 + model.j[1, 0] = 123 + + assert_serializable(model) + + for j in [1, 1000]: + model.j[1, 0] = j + assert model.system == 1 / 2 * ( + tensor(sigmaz(), identity(2)) + tensor(identity(2), sigmaz()) * 0.11 + ) + 10 / 2 * tensor(sigmay(), sigmax()) + + for s in [1, 10e4]: + model.s_vec[0][0] = s + model.s_vec[1][1] = s + + assert model.bath_coupling(0) == tensor(sigmax(), identity(2)) + assert model.bath_coupling(1) == tensor(identity(2), sigmaz()) + + def test_xz_xz(self): + model = TwoQubitModel(ω_2=0.11, γ=10, δ=[23, 123], s_vec=[[1, 0], [0, 1]]) + model.j[0, 0] = 0 + + assert_serializable(model) + + for j in [1, 1e5]: + model.j[0, 2] = j + assert model.system == 1 / 2 * ( + tensor(sigmaz(), identity(2)) + tensor(identity(2), sigmaz()) * 0.11 + ) + 10 / 2 * tensor(sigmax(), sigmaz()) + + for s in [1, 10e4]: + model.s_vec[0][0] = s + model.s_vec[1][1] = s + + assert model.bath_coupling(0) == tensor(sigmax(), identity(2)) + assert model.bath_coupling(1) == tensor(identity(2), sigmaz()) + + +def test_sd_bcf_norm(): + random.seed(0) + for _ in range(4): + model = TwoQubitModel( + ω_c=[random.uniform(0.5, 4), random.uniform(0.5, 4)], + s=random.choices([1.0, 0.1, 0.5, 0.3], k=2), + T=[random.uniform(0.1, 4), random.uniform(0.1, 4)], + ) + + for i in range(2): + assert model.spectral_density(i).integral() == pytest.approx(np.pi) + assert model.bcf(0)(0) == 1 + assert np.sum(model.bcf_coefficients(i)[0]) == pytest.approx(1, rel=1e-2) + + tsd = model.thermal_spectral_density(i) + assert tsd is not None + assert pytest.approx(tsd(1)) == model.spectral_density(i)(1) * 1 / np.expm1( + 1 / float(model.T[i]) + ) + + tcorr = model.thermal_correlations(i) + assert tcorr is not None + + # tests if the normalization of tcorr is correct by + # calculating the fourier transform + model.thermal_process(i)