diff --git a/poetry.lock b/poetry.lock index a538b688d..4f5d36e7d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -471,21 +471,6 @@ files = [ {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, ] -[[package]] -name = "dill" -version = "0.3.9" -description = "serialize all of Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"}, - {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"}, -] - -[package.extras] -graph = ["objgraph (>=1.7.2)"] -profile = ["gprof2dot (>=2022.7.29)"] - [[package]] name = "distlib" version = "0.3.9" @@ -1167,17 +1152,6 @@ files = [ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] -[[package]] -name = "pbr" -version = "6.1.0" -description = "Python Build Reasonableness" -optional = false -python-versions = ">=2.6" -files = [ - {file = "pbr-6.1.0-py2.py3-none-any.whl", hash = "sha256:a776ae228892d8013649c0aeccbb3d5f99ee15e005a4cbb7e61d55a067b28a2a"}, - {file = "pbr-6.1.0.tar.gz", hash = "sha256:788183e382e3d1d7707db08978239965e8b9e4e5ed42669bf4758186734d5f24"}, -] - [[package]] name = "pep8" version = "1.7.1" @@ -1414,20 +1388,6 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - [[package]] name = "pyyaml" version = "6.0.2" @@ -1537,44 +1497,6 @@ files = [ numpy = ">=1.7" scipy = ">=0.13.2" -[[package]] -name = "qiskit" -version = "1.2.4" -description = "An open-source SDK for working with quantum computers at the level of extended quantum circuits, operators, and primitives." -optional = false -python-versions = ">=3.8" -files = [ - {file = "qiskit-1.2.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:1cdabddeed74956ac22db8b11b3479044a305b0895b142198f11a75e745aa079"}, - {file = "qiskit-1.2.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9daad1c3a0608c4a32a03112d0650cc51ced2cab16479703230acedafafa0548"}, - {file = "qiskit-1.2.4-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:3394ec724adeb481c02b050ac6e5929ca5e1f91ab94a1d571dadf0c702d6967e"}, - {file = "qiskit-1.2.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:124777b0d9caf40932e02d1d2c6be3a12ff058990e6ae55f45a315a9a51da75b"}, - {file = "qiskit-1.2.4-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c8d9fa7fdb0cf7727f27a1881cc9f9be7de2b6aaf7cab6861686e1a6e34d6c8"}, - {file = "qiskit-1.2.4-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a973089d54f379006df8ae9b0d406e909e02b11a9390b3e2f50aefd5e8070eb"}, - {file = "qiskit-1.2.4-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:409b43d48e33e5bbc316fd85f44f29a71db2d5ff802affabc5ee355fe9573d7e"}, - {file = "qiskit-1.2.4-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a66303ab4f939080ca0b8c4102040a89417a99f2621978c7bca8b1d5f6c6e103"}, - {file = "qiskit-1.2.4-cp38-abi3-win32.whl", hash = "sha256:93aa20398c5ab79adb4baf89a76d034a7c608fcc5bcbbd77355314378583e56a"}, - {file = "qiskit-1.2.4-cp38-abi3-win_amd64.whl", hash = "sha256:87175e179bbaa3d2e280f14fe90efa77d6d7521d58ee5afd9528ec6716a6d8e6"}, - {file = "qiskit-1.2.4.tar.gz", hash = "sha256:3f30b1fc6c66dec240428991da75cc6c35cc75152baff8daf3f8ca71b60684e8"}, -] - -[package.dependencies] -dill = ">=0.3" -numpy = ">=1.17,<3" -python-dateutil = ">=2.8.0" -rustworkx = ">=0.15.0" -scipy = ">=1.5" -stevedore = ">=3.0.0" -symengine = ">=0.11,<0.14" -sympy = ">=1.3" -typing-extensions = "*" - -[package.extras] -all = ["qiskit[crosstalk-pass,csp-layout-pass,qasm3-import,visualization]"] -crosstalk-pass = ["z3-solver (>=4.7)"] -csp-layout-pass = ["python-constraint (>=1.4)"] -qasm3-import = ["qiskit-qasm3-import (>=0.1.0)"] -visualization = ["Pillow (>=4.2.1)", "matplotlib (>=3.3)", "pydot", "pylatexenc (>=1.4)", "seaborn (>=0.9.0)"] - [[package]] name = "readthedocs-sphinx-ext" version = "2.2.5" @@ -1639,35 +1561,6 @@ files = [ {file = "ruff-0.6.9.tar.gz", hash = "sha256:b076ef717a8e5bc819514ee1d602bbdca5b4420ae13a9cf61a0c0a4f53a2baa2"}, ] -[[package]] -name = "rustworkx" -version = "0.15.1" -description = "A python graph library implemented in Rust" -optional = false -python-versions = ">=3.8" -files = [ - {file = "rustworkx-0.15.1-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:6cd4496d3298cd3205c03545e48cc37d21e0455d57752af801d3fb250452d590"}, - {file = "rustworkx-0.15.1-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:cb518f5649e62d753e29ca1e57290c8f58adbebcd154dc3159f4a36ebfa1e2b7"}, - {file = "rustworkx-0.15.1-cp38-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac68ae2515ece22ba3ef56f3d16ad6bf707955f650d623190b2e7d706c6dc92"}, - {file = "rustworkx-0.15.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b903edec1d803704b499959f9d6f6119cdda63b9b64194a4b4307e506b112f0"}, - {file = "rustworkx-0.15.1-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2c97a56ff8a0f6c273a83e26e627c72207442b4252aa550acad0bff42caac40"}, - {file = "rustworkx-0.15.1-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:241c502532e348ba89200823326dba30de4df4b886cb2fd5a140b359ff124bb3"}, - {file = "rustworkx-0.15.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e5f4156f46fa03177c9b0580450eab87786063495d48b457762a5bdd20c55e2"}, - {file = "rustworkx-0.15.1-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7834ab34748db6214ec3b3836b996b23882dc83184234e6d346d6bb85fd58ae5"}, - {file = "rustworkx-0.15.1-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ce53f173fed16e1d51d9df9f23475a16c981b03bf1a412d991c75a70db6b1dc1"}, - {file = "rustworkx-0.15.1-cp38-abi3-win32.whl", hash = "sha256:308bc76a01bcae9af4602d8b9ed58021df37dd0bb5a7b2e3831ae53c5e234ff0"}, - {file = "rustworkx-0.15.1-cp38-abi3-win_amd64.whl", hash = "sha256:89077382633e918d2392772f53b9d6d30eee51eb536f8d38ee195c212b2f0427"}, - {file = "rustworkx-0.15.1.tar.gz", hash = "sha256:0e0cc86599f979285b2ab9c357276f3272f3fcb3b2df5651a6bf9704c570d4c1"}, -] - -[package.dependencies] -numpy = ">=1.16.0,<3" - -[package.extras] -all = ["matplotlib (>=3.0)", "pillow (>=5.4)"] -graphviz = ["pillow (>=5.4)"] -mpl = ["matplotlib (>=3.0)"] - [[package]] name = "scipy" version = "1.14.1" @@ -2016,71 +1909,6 @@ pure-eval = "*" [package.extras] tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] -[[package]] -name = "stevedore" -version = "5.3.0" -description = "Manage dynamic plugins for Python applications" -optional = false -python-versions = ">=3.8" -files = [ - {file = "stevedore-5.3.0-py3-none-any.whl", hash = "sha256:1efd34ca08f474dad08d9b19e934a22c68bb6fe416926479ba29e5013bcc8f78"}, - {file = "stevedore-5.3.0.tar.gz", hash = "sha256:9a64265f4060312828151c204efbe9b7a9852a0d9228756344dbc7e4023e375a"}, -] - -[package.dependencies] -pbr = ">=2.0.0" - -[[package]] -name = "symengine" -version = "0.13.0" -description = "Python library providing wrappers to SymEngine" -optional = false -python-versions = "<4,>=3.8" -files = [ - {file = "symengine-0.13.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:259fd4111c7a70c72bdff5686de1949e8132baeb612eacdaf8837720c6fe449b"}, - {file = "symengine-0.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:44f2eb28a1e36db0bbd6679435412f79da9743bf9c1cb3eff25e0c343b7ddd48"}, - {file = "symengine-0.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d141712fa14d9138bd19e64b10392f850c68d88cd7db29f1bda33e32d1095559"}, - {file = "symengine-0.13.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:830226d933bfcdb93546e4062541627d9a3bc7a178a63fb16c002eb5c5221938"}, - {file = "symengine-0.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a08090163819a0bbfa97d64bd2d8dac2c5268147ed9c242799d7f7e8728a6f4e"}, - {file = "symengine-0.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:1e435dcd8ed25e4c7c21ab1c0376be910efc7f35da76d532367df27b359f0358"}, - {file = "symengine-0.13.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:da0eba7e106095cdce88eb275c8a9d7c4586ad88f229394c53e1184155c00745"}, - {file = "symengine-0.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b0c175f4f895a73a925508af03faf7efd6cad8593256bbdb5346bd996d3ec5c8"}, - {file = "symengine-0.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e58d1e2abd08381aa0cf24c88c0e8b7f592df92619b51e32d36835fbd2dd6ae8"}, - {file = "symengine-0.13.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1db745f2c7a3c5e83510cf4decb43201f43552dfb05ad8af9787c89708be9ede"}, - {file = "symengine-0.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2572c98b09ac284db6ecff63f6170461194dc94c4209afd34c092ec67873d85"}, - {file = "symengine-0.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:12727f02a2919f005aee48e68e0cbb70cf857b19385857b4d985d1c9b075f620"}, - {file = "symengine-0.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cf91d24f1bfd6d53228593c7804dd106b71b19674d5afc4fa322d516e1793bdd"}, - {file = "symengine-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c5615b7eb68890917abd390ebb10434a949165f6064741c1a8cc345fee14e855"}, - {file = "symengine-0.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb92bdf0890de264abaeacbfbdbd4dd7444b94057bd47958d913b662e549ad8a"}, - {file = "symengine-0.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b3bce486fbc0b87970ed1b10ca9d5cafb1fd6b66382fe631261d83592851d7e"}, - {file = "symengine-0.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7e6bae9cfcdde2775d92fbb0abe3ef04e32f65ebc4c2d164ca33f4da202d4a7"}, - {file = "symengine-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:bced0a1dbdb94737c299384c85ddbad6944ce8dadc334f7bb8dbbd8f6c965807"}, - {file = "symengine-0.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d34df77971538e4c29f2d8e5ef7f459c2179465e6cdb7dfd48b79b87ecd8f4d"}, - {file = "symengine-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ab2661d9b18867e7c6edbfa7a74b8b0a2a694bd24aa08003dc3214f77cb9d6f2"}, - {file = "symengine-0.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53f27b9013878ee4419d8e853664d8ae4b68419e3f4b9b5b7f503d32bf904755"}, - {file = "symengine-0.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:27987f75ce08c64f453e2b9b74fec6ffc5ca418c4deca0b75580979d4a4e242a"}, - {file = "symengine-0.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9ea9410330ea15ed4137d7a0a3c43caccacb71490e18036ce5182d08c93baf8"}, - {file = "symengine-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:5031eb7a5c6675d5195bb57f93cc7d9ac5a7a9a826d4ad6f6b2927746ed7e6e6"}, - {file = "symengine-0.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ce0e5dfb19943bcf3e44a4485bcac4c5533ba3705c63083494eed0b3bf246076"}, - {file = "symengine-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c3b77dc54bf1181f6bd3b3338c4e6e5973a8b0fa20a189d15563ef5626e57b04"}, - {file = "symengine-0.13.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca7c3f6c168f6f5b06b421833c3d3baae56067a94b671bdffbe09b8e4fefd9be"}, - {file = "symengine-0.13.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:847523de682416811bacb3ad11507e663b3522fbb35cd27184757e9956d0eaf0"}, - {file = "symengine-0.13.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2fc1b7d96426463f0c9011e9fb88459d906477c1baa8a996dde6fb2bfa99d4"}, - {file = "symengine-0.13.0-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:6e371bb2da3867085779c1c21bbb1c85f9634c76c8a76c08562ea113e3dfcd85"}, - {file = "symengine-0.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cf7c62478b19683d54e4d93faa5b89303beae25db0c503a105a70d266dc99fa9"}, - {file = "symengine-0.13.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdb21158cf3e2ba87e441f21ecc7724f108b8db17c0fd1880f9f531602bab1f3"}, - {file = "symengine-0.13.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1848d366b359ff69ef5dac148b30ca04c7339a7d3bcab28419d411e68c0cc011"}, - {file = "symengine-0.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:242f817e890a0a50d52ed6b2bfd1aad19636a58db700c7995bbe1ceeaebd9d08"}, - {file = "symengine-0.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:af79cf2b9645fb55216850185987b2e9347db71e42e87b6402e4bbd41710b316"}, - {file = "symengine-0.13.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:17ae9d1c781a60ac48d07fa30f39a2d237f9da95e9e81f6a24b1c16908e9cad2"}, - {file = "symengine-0.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5264157a95f6d09dd044cee6abcbc176e649c487638b7f32199f387f37ad82a5"}, - {file = "symengine-0.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f01a6d0829b4881d5f831ae7848eb0d82b80d8b46b5689f1bf27069672370f75"}, - {file = "symengine-0.13.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35ec68b11c2df2be1a236d0c028edb5b331909b16666d7a9fe99a4a5810afec7"}, - {file = "symengine-0.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4357fed87083e8719fcffd8bd0e7ddd16172e319343362512f681e472ac5668"}, - {file = "symengine-0.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a41be31816e5e51e9063bf26de07faf3751de7a133dbbec149632de702a28e18"}, - {file = "symengine-0.13.0.tar.gz", hash = "sha256:ab83a08897ebf12579702c2b71ba73d4732fb706cc4291d810aedf39c690c14c"}, -] - [[package]] name = "sympy" version = "1.13.3" diff --git a/pyproject.toml b/pyproject.toml index 31fada5e3..75c7a0db4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,6 @@ numpy = "2.1.2" scipy = "1.14.1" scs = "3.2.7" picos = "2.4.17" -qiskit = "1.2.4" [tool.poetry.group.dev.dependencies] diff --git a/toqito/rand/random_circulant_gram_matrix.py b/toqito/rand/random_circulant_gram_matrix.py index 4e92ea99f..3823f1de0 100644 --- a/toqito/rand/random_circulant_gram_matrix.py +++ b/toqito/rand/random_circulant_gram_matrix.py @@ -3,7 +3,7 @@ import numpy as np -def random_circulant_gram_matrix(dim: int) -> np.ndarray: +def random_circulant_gram_matrix(dim: int, seed: int | None = None) -> np.ndarray: r"""Generate a random circulant Gram matrix of specified dimension. A circulant matrix is a square matrix where the elements of each row are identical to the elements of the @@ -38,6 +38,16 @@ def random_circulant_gram_matrix(dim: int) -> np.ndarray: [0.04257471, 0.21058986, 0.42351891, 0.21058986], [0.21058986, 0.04257471, 0.21058986, 0.42351891]]) + It is also possible to pass a seed to this function for reproducibility. + + >>> from toqito.rand import random_circulant_gram_matrix + >>> circulant_matrix = random_circulant_gram_matrix(4, seed=42) + >>> circulant_matrix + array([[ 0.69220011, -0.02116047, 0.12407687, -0.02116047], + [-0.02116047, 0.69220011, -0.02116047, 0.12407687], + [ 0.12407687, -0.02116047, 0.69220011, -0.02116047], + [-0.02116047, 0.12407687, -0.02116047, 0.69220011]]) + References ========== @@ -46,13 +56,16 @@ def random_circulant_gram_matrix(dim: int) -> np.ndarray: :param dim: int The dimension of the circulant matrix to generate. + :param seed: int | None + A seed used to instantiate numpy's random number generator. :return: numpy.ndarray A `dim` x `dim` real, symmetric, circulant matrix. """ + gen = np.random.default_rng(seed=seed) # Step 1: Generate a random diagonal matrix with non-negative entries - diag_mat = np.diag(np.random.rand(dim)) + diag_mat = np.diag(gen.random(dim)) # Step 2: Construct the normalized DFT matrix dft_mat = np.fft.fft(np.eye(dim)) / np.sqrt(dim) diff --git a/toqito/rand/random_density_matrix.py b/toqito/rand/random_density_matrix.py index e1dab718f..7299f2f55 100644 --- a/toqito/rand/random_density_matrix.py +++ b/toqito/rand/random_density_matrix.py @@ -10,6 +10,7 @@ def random_density_matrix( is_real: bool = False, k_param: list[int] | int = None, distance_metric: str = "haar", + seed: int | None = None, ) -> np.ndarray: r"""Generate a random density matrix. @@ -74,6 +75,21 @@ def random_density_matrix( >>> is_density(bures_mat) np.True_ + It is also possible to pass a seed to this function for reproducibility. + + >>> from toqito.rand import random_density_matrix + >>> seeded = random_density_matrix(2, seed=42) + >>> seeded + array([[0.82448019+0.j , 0.14841568-0.33318114j], + [0.14841568+0.33318114j, 0.17551981+0.j ]]) + + We can once again verify that this is in fact a valid density matrix using the + :code:`is_density` function from :code:`toqito` as follows + + >>> from toqito.matrix_props import is_density + >>> is_density(seeded) + np.True_ + :param dim: The number of rows (and columns) of the density matrix. :param is_real: Boolean denoting whether the returned matrix will have all @@ -83,20 +99,22 @@ def random_density_matrix( density matrix. This metric is either the Haar measure or the Bures measure. Default value is to use the Haar measure. + :param seed: A seed used to instantiate numpy's random number generator. :return: A :code:`dim`-by-:code:`dim` random density matrix. """ + gen = np.random.default_rng(seed=seed) if k_param is None: k_param = dim # Haar / Hilbert-Schmidt measure. - gin = np.random.rand(dim, k_param) + gin = gen.random((dim, k_param)) if not is_real: - gin = gin + 1j * np.random.randn(dim, k_param) + gin = gin + 1j * gen.standard_normal((dim, k_param)) if distance_metric == "bures": - gin = random_unitary(dim, is_real) + np.identity(dim) @ gin + gin = random_unitary(dim, is_real, seed=seed) + np.identity(dim) @ gin rho = gin @ np.array(gin).conj().T diff --git a/toqito/rand/random_ginibre.py b/toqito/rand/random_ginibre.py index 9356d1967..1fd9492c9 100644 --- a/toqito/rand/random_ginibre.py +++ b/toqito/rand/random_ginibre.py @@ -3,7 +3,7 @@ import numpy as np -def random_ginibre(dim_n: int, dim_m: int) -> np.ndarray: +def random_ginibre(dim_n: int, dim_m: int, seed: int | None = None) -> np.ndarray: r"""Generate a Ginibre random matrix :cite:`WikiCircLaw`. Generates a random :code:`dim_n`-by-:code:`dim_m` Ginibre matrix. @@ -23,6 +23,12 @@ def random_ginibre(dim_n: int, dim_m: int) -> np.ndarray: array([[0.39166472-1.54657971j, 0.36538245+0.23324642j], [0.50103695-0.25857737j, 0.8357054 +0.31404353j]]) + It is also possible to pass a seed to this function for reproducibility. + + >>> from toqito.rand import random_ginibre + >>> random_ginibre(2, 2, seed=42) + array([[ 0.21546751-1.37959021j, -0.73537981-0.92077996j], + [ 0.53064913+0.09039682j, 0.66507969-0.22361728j]]) References @@ -33,7 +39,9 @@ def random_ginibre(dim_n: int, dim_m: int) -> np.ndarray: :param dim_n: The number of rows of the Ginibre random matrix. :param dim_m: The number of columns of the Ginibre random matrix. + :param seed: A seed used to instantiate numpy's random number generator. :return: A :code:`dim_n`-by-:code:`dim_m` Ginibre random density matrix. """ - return (np.random.randn(dim_n, dim_m) + 1j * np.random.randn(dim_n, dim_m)) / np.sqrt(2) + gen = np.random.default_rng(seed=seed) + return (gen.standard_normal((dim_n, dim_m)) + 1j * gen.standard_normal((dim_n, dim_m))) / np.sqrt(2) diff --git a/toqito/rand/random_orthonormal_basis.py b/toqito/rand/random_orthonormal_basis.py index 91ed9cbb7..cf03829fc 100644 --- a/toqito/rand/random_orthonormal_basis.py +++ b/toqito/rand/random_orthonormal_basis.py @@ -4,7 +4,7 @@ from toqito.rand import random_unitary -def random_orthonormal_basis(dim: int, is_real: bool = False) -> list[np.ndarray]: +def random_orthonormal_basis(dim: int, is_real: bool = False, seed: int | None = None) -> list[np.ndarray]: r"""Generate a real random orthonormal basis of given dimension :math:`d`. The basis is generated from the columns of a random unitary matrix of the same dimension @@ -21,6 +21,13 @@ def random_orthonormal_basis(dim: int, is_real: bool = False) -> list[np.ndarray array([ 0.23965404, -0.58538248, 0.187136 , 0.75158061]), array([ 0.658269 , 0.25243989, -0.69118291, 0.158815 ])] + It is also possible to add a seed for reproducibility. + + >>> from toqito.rand import random_orthonormal_basis + >>> random_orthonormal_basis(2, is_real=True, seed=42) + [array([0.66954664, 0.74277002]), array([-0.74277002, 0.66954664])] + + References ========== .. bibliography:: @@ -28,7 +35,9 @@ def random_orthonormal_basis(dim: int, is_real: bool = False) -> list[np.ndarray dim: int Number of elements in the random orthonormal basis. + seed: int | None + A seed used to instantiate numpy's random number generator. """ - random_mat = random_unitary(dim, is_real) + random_mat = random_unitary(dim, is_real, seed) return [random_mat[:, i] for i in range(dim)] diff --git a/toqito/rand/random_povm.py b/toqito/rand/random_povm.py index 7fa3d5c29..732e2ac03 100644 --- a/toqito/rand/random_povm.py +++ b/toqito/rand/random_povm.py @@ -3,7 +3,7 @@ import numpy as np -def random_povm(dim: int, num_inputs: int, num_outputs: int) -> np.ndarray: +def random_povm(dim: int, num_inputs: int, num_outputs: int, seed: int | None = None) -> np.ndarray: """Generate random positive operator valued measurements (POVMs) :cite:`WikiPOVM`. Examples @@ -40,6 +40,33 @@ def random_povm(dim: int, num_inputs: int, num_outputs: int) -> np.ndarray: [[1.+0.j, 0.+0.j], [0.+0.j, 1.+0.j]] + It is also possible to add a seed for reproducibility. + + >>> from toqito.rand import random_povm + >>> import numpy as np + >>> + >>> dim, num_inputs, num_outputs = 2, 2, 2 + >>> povms = random_povm(dim, num_inputs, num_outputs, seed=42) + >>> povms + array([[[[ 0.22988028+0.j, 0.77011972+0.j], + [ 0.45021752+0.j, 0.54978248+0.j]], + + [[-0.23938341+0.j, 0.23938341+0.j], + [ 0.32542956+0.j, -0.32542956+0.j]]], + + + [[[-0.23938341+0.j, 0.23938341+0.j], + [ 0.32542956+0.j, -0.32542956+0.j]], + + [[ 0.83184406+0.j, 0.16815594+0.j], + [ 0.61323275+0.j, 0.38676725+0.j]]]]) + + We can once again verify that this constitutes a valid set of POVM elements as checking that + these operators all sum to the identity operator. + + >>> np.round(povms[:, :, 0, 0] + povms[:, :, 0, 1]) + array([[ 1.+0.j, -0.+0.j], + [-0.+0.j, 1.+0.j]]) References ========== @@ -51,11 +78,13 @@ def random_povm(dim: int, num_inputs: int, num_outputs: int) -> np.ndarray: :param dim: The dimensions of the measurements. :param num_inputs: The number of inputs for the measurement. :param num_outputs: The number of outputs for the measurement. + :param seed: A seed used to instantiate numpy's random number generator. :return: A set of `dim`-by-`dim` POVMs of shape `(dim, dim, num_inputs, num_outputs)`. """ povms = [] - gram_vectors = np.random.normal(size=(num_inputs, num_outputs, dim, dim)) + gen = np.random.default_rng(seed=seed) + gram_vectors = gen.normal(size=(num_inputs, num_outputs, dim, dim)) for input_block in gram_vectors: normalizer = sum(np.array(output_block).T.conj() @ output_block for output_block in input_block) u_mat, d_mat, _ = np.linalg.svd(normalizer) diff --git a/toqito/rand/random_state_vector.py b/toqito/rand/random_state_vector.py index a7837fb41..ab3b000e2 100644 --- a/toqito/rand/random_state_vector.py +++ b/toqito/rand/random_state_vector.py @@ -6,7 +6,12 @@ from toqito.states import max_entangled -def random_state_vector(dim: list[int] | int, is_real: bool = False, k_param: int = 0) -> np.ndarray: +def random_state_vector( + dim: list[int] | int, + is_real: bool = False, + k_param: int = 0, + seed: int | None = None +) -> np.ndarray: r"""Generate a random pure state vector. Examples @@ -30,15 +35,33 @@ def random_state_vector(dim: list[int] | int, is_real: bool = False, k_param: in >>> is_pure(dm) True + It is also possible to pass a seed for reproducibility. + + >>> from toqito.rand import random_state_vector + >>> vec = random_state_vector(2, seed=42) + >>> vec + array([[0.54521054+0.60483621j], + [0.30916633+0.49125839j]]) + + We can once again verify that this is in fact a valid state vector by computing the + corresponding density matrix of the vector and checking if the density matrix is pure. + + >>> from toqito.state_props import is_pure + >>> dm = vec.conj().T @ vec + >>> is_pure(dm) + True + :param dim: The number of rows (and columns) of the unitary matrix. :param is_real: Boolean denoting whether the returned matrix has real entries or not. Default is :code:`False`. :param k_param: Default 0. + :param seed: A seed used to instantiate numpy's random number generator. :return: A :code:`dim`-by-:code:`dim` random unitary matrix. """ + gen = np.random.default_rng(seed=seed) # Schmidt rank plays a role. if 0 < k_param < np.min(dim): # Allow the user to enter a single number for dim. @@ -50,12 +73,12 @@ def random_state_vector(dim: list[int] | int, is_real: bool = False, k_param: in # get a Schmidt rank `<= k_param` state. psi = max_entangled(k_param, True, False).toarray() - a_param = np.random.rand(dim[0] * k_param, 1) - b_param = np.random.rand(dim[1] * k_param, 1) + a_param = gen.random((dim[0] * k_param, 1)) + b_param = gen.random((dim[1] * k_param, 1)) if not is_real: - a_param = a_param + 1j * np.random.rand(dim[0] * k_param, 1) - b_param = b_param + 1j * np.random.rand(dim[1] * k_param, 1) + a_param = a_param + 1j * gen.random((dim[0] * k_param, 1)) + b_param = b_param + 1j * gen.random((dim[1] * k_param, 1)) mat_1 = np.kron(psi.conj().T, np.identity(int(np.prod(dim)))) mat_2 = swap( @@ -68,7 +91,7 @@ def random_state_vector(dim: list[int] | int, is_real: bool = False, k_param: in return np.divide(ret_vec, np.linalg.norm(ret_vec)) # Schmidt rank is full, so ignore it. - ret_vec = np.random.rand(dim, 1) + ret_vec = gen.random((dim, 1)) if not is_real: - ret_vec = ret_vec + 1j * np.random.rand(dim, 1) + ret_vec = ret_vec + 1j * gen.random((dim, 1)) return np.divide(ret_vec, np.linalg.norm(ret_vec)) diff --git a/toqito/rand/random_states.py b/toqito/rand/random_states.py index 96811a7bf..de548fb0c 100644 --- a/toqito/rand/random_states.py +++ b/toqito/rand/random_states.py @@ -1,15 +1,13 @@ -"""Generate random quantum states using Qiskit.""" +"""Generate random quantum states.""" import numpy as np -from qiskit.quantum_info.states.random import random_statevector -def random_states(n: int, d: int) -> list[np.ndarray]: +def random_states(n: int, d: int, seed: int | None = None) -> list[np.ndarray]: r"""Generate a list of random quantum states. - This function utilizes Qiskit's `random_statevector` to generate a list of quantum states, - each of a specified dimension. The states are valid quantum states distributed according - to the Haar measure. + This function generates a list of quantum states, each of a specified dimension. The states are + valid quantum states distributed according to the Haar measure. Examples ========== @@ -33,16 +31,37 @@ def random_states(n: int, d: int) -> list[np.ndarray]: [-0.27852449+0.39980357j], [ 0.17033502+0.18562365j]])] + It is also possible to pass a seed to this function for reproducibility. + + >>> from toqito.rand import random_states + >>> states = random_states(3, 4, seed=42) + >>> states + [array([[ 0.13830446+0.0299699j ], + [-0.47202619+0.51163029j], + [ 0.34061349+0.21219233j], + [ 0.42690188-0.39001418j]]), array([[-0.71489214+0.1351165j ], + [-0.47714049-0.35135073j], + [ 0.04684288+0.32187898j], + [-0.11587661-0.01829369j]]), array([[-0.00827473-0.0910465j ], + [-0.42013238-0.33536439j], + [ 0.43311201+0.60211343j], + [ 0.38307005-0.07610726j]])] + :param n: int The number of random states to generate. :param d: int The dimension of each quantum state. + :param seed: int | None + A seed used to instantiate numpy's random number generator. :return: list[numpy.ndarray] A list of `n` numpy arrays, each representing a d-dimensional quantum state as a column vector. """ - return [random_statevector(d).data.reshape(-1, 1) for _ in range(n)] + gen = np.random.default_rng(seed=seed) + samples = gen.normal(size=(n, d)) + 1j * gen.normal(size=(n, d)) + samples /= np.linalg.norm(samples, axis=1)[:, np.newaxis] + return [sample.reshape(-1, 1) for sample in samples] diff --git a/toqito/rand/random_unitary.py b/toqito/rand/random_unitary.py index d586b5473..485585b40 100644 --- a/toqito/rand/random_unitary.py +++ b/toqito/rand/random_unitary.py @@ -3,7 +3,11 @@ import numpy as np -def random_unitary(dim: list[int] | int, is_real: bool = False) -> np.ndarray: +def random_unitary( + dim: list[int] | int, + is_real: bool = False, + seed: int | None = None +) -> np.ndarray: """Generate a random unitary or orthogonal matrix :cite:`Ozols_2009_RandU`. Calculates a random unitary matrix (if :code:`is_real = False`) or a random real orthogonal @@ -62,6 +66,20 @@ def random_unitary(dim: list[int] | int, is_real: bool = False) -> np.ndarray: >>> is_unitary(mat) True + It is also possible to pass a seed to this function for reproducibility. + + >>> from toqito.matrix_props import is_unitary + >>> seeded = random_unitary(2, seed=42) + >>> seeded + array([[0.34074554-0.85897194j, 0.32146645+0.20668575j], + [0.37801036+0.05628362j, 0.30953006-0.87070745j]]) + + And once again, we can verify that this matrix generated is a valid unitary matrix. + + >>> from toqito.matrix_props import is_unitary + >>> is_unitary(seeded) + True + References ========== .. bibliography:: @@ -71,9 +89,12 @@ def random_unitary(dim: list[int] | int, is_real: bool = False) -> np.ndarray: :param dim: The number of rows (and columns) of the unitary matrix. :param is_real: Boolean denoting whether the returned matrix has real entries or not. Default is :code:`False`. + :param seed: A seed used to instantiate numpy's random number generator. :return: A :code:`dim`-by-:code:`dim` random unitary matrix. """ + gen = np.random.default_rng(seed=seed) + if isinstance(dim, int): dim = [dim, dim] @@ -81,10 +102,10 @@ def random_unitary(dim: list[int] | int, is_real: bool = False) -> np.ndarray: raise ValueError("Unitary matrix must be square.") # Construct the Ginibre ensemble. - gin = np.random.rand(dim[0], dim[1]) + gin = gen.random((dim[0], dim[1])) if not is_real: - gin = gin + 1j * np.random.rand(dim[0], dim[1]) + gin = gin + 1j * gen.standard_normal((dim[0], dim[1])) # QR decomposition of the Ginibre ensemble. q_mat, r_mat = np.linalg.qr(gin) diff --git a/toqito/rand/tests/test_random_circulant_gram_matrix.py b/toqito/rand/tests/test_random_circulant_gram_matrix.py index 86640d203..5359367fd 100644 --- a/toqito/rand/tests/test_random_circulant_gram_matrix.py +++ b/toqito/rand/tests/test_random_circulant_gram_matrix.py @@ -38,3 +38,30 @@ def test_random_circulant_gram_matrix(dim): # all eigenvalues are non-negative. eigenvalues = np.linalg.eigvalsh(circulant_matrix) assert_array_almost_equal((eigenvalues >= 0), True) + +@pytest.mark.parametrize( + "dim,expected", + [ + ( + 2, + np.array([ + [0.36808644, 0.31426542], + [0.31426542, 0.36808644] + ]) + ), +( + 5, + np.array([ + [0.26336209, 0.08518132, 0.12431357, 0.12431357, 0.08518132], + [0.08518132, 0.26336209, 0.08518132, 0.12431357, 0.12431357], + [0.12431357, 0.08518132, 0.26336209, 0.08518132, 0.12431357], + [0.12431357, 0.12431357, 0.08518132, 0.26336209, 0.08518132], + [0.08518132, 0.12431357, 0.12431357, 0.08518132, 0.26336209] + ]) + ), + ] +) +def test_random_circulant_gram_matrix_with_seed(dim, expected): + """Test that the random_circulant_gram_matrix produces expected inputs with a seed.""" + mat = random_circulant_gram_matrix(dim, seed=123) + assert_array_almost_equal(mat, expected) diff --git a/toqito/rand/tests/test_random_density_matrix.py b/toqito/rand/tests/test_random_density_matrix.py index b92c51d20..f1e79ef08 100644 --- a/toqito/rand/tests/test_random_density_matrix.py +++ b/toqito/rand/tests/test_random_density_matrix.py @@ -2,6 +2,7 @@ import numpy as np import pytest +from numpy.ma.testutils import assert_array_almost_equal from toqito.matrix_props import is_density, is_positive_semidefinite from toqito.rand import random_density_matrix @@ -48,3 +49,63 @@ def test_random_density_matrix(dim, is_real, distance_metric): assert np.all(np.isreal(dm)), "Matrix should be real" else: assert np.any(np.iscomplex(dm)), "Matrix should be complex" + +@pytest.mark.parametrize( + "dim, is_real, k_param, distance_metric, expected", + [ + # Generate random non-real density matrix. + ( + 2, + False, + None, + "haar", + np.array([ + [0.67842043+0.j, -0.04636114+0.29398703j], + [-0.04636114-0.29398703j, 0.32157957+0.j] + ]) + ), + # Generate random real density matrix. + ( + 2, + True, + None, + "haar", + np.array([ + [0.85019307, 0.29087271], + [0.29087271, 0.14980693] + ]) + ), + # Random non-real density matrix according to Bures metric. + ( + 2, + False, + None, + "bures", + np.array([ + [0.60005221+0.j, -0.00344727+0.16976473j], + [-0.00344727-0.16976473j, 0.39994779+0.j] + ]) + ), + # Generate random non-real density matrix all params. + ( + 2, + True, + 2, + "haar", + np.array([ + [0.85019307, 0.29087271], + [0.29087271, 0.14980693] + ]) + ), + ], +) +def test_seed(dim, is_real, k_param, distance_metric, expected): + """Test that the function produces the expected output using a seed.""" + dm = random_density_matrix( + dim, + is_real, + k_param=k_param, + distance_metric=distance_metric, + seed=123 + ) + assert_array_almost_equal(dm, expected) diff --git a/toqito/rand/tests/test_random_ginibre.py b/toqito/rand/tests/test_random_ginibre.py index 394b64dcb..6797bc28f 100644 --- a/toqito/rand/tests/test_random_ginibre.py +++ b/toqito/rand/tests/test_random_ginibre.py @@ -2,6 +2,7 @@ import numpy as np import pytest +from numpy.ma.testutils import assert_array_almost_equal from toqito.rand import random_ginibre @@ -13,6 +14,32 @@ def test_random_ginibre_dims(dim_n, dim_m): gin_mat = random_ginibre(dim_n, dim_m) np.testing.assert_equal(gin_mat.shape, (dim_n, dim_m)) +@pytest.mark.parametrize( + "dim_n, dim_m, expected", + [ + ( + 2, + 3, + np.array([ + [-0.69941441-0.45004776j, -0.26006444+0.38321809j, 0.91070069-0.22386679j], + [0.13716063-0.22796353j, 0.65070151+0.06870767j, 0.408074-1.07899574j] + ]) + ), + ( + 2, + 2, + np.array([ + [-0.69941441+0.65070151j, -0.26006444+0.408074j], + [0.91070069-0.45004776j, 0.13716063+0.38321809j] + ]) + ), + ] +) +def test_seed(dim_n, dim_m, expected): + """Test that the function returns the expected output when seeded.""" + gin_mat = random_ginibre(dim_n, dim_m, seed=123) + assert_array_almost_equal(gin_mat, expected) + @pytest.mark.parametrize( "dim_n, dim_m", diff --git a/toqito/rand/tests/test_random_orthonormal_basis.py b/toqito/rand/tests/test_random_orthonormal_basis.py index cc9a0d419..9a11de8ed 100644 --- a/toqito/rand/tests/test_random_orthonormal_basis.py +++ b/toqito/rand/tests/test_random_orthonormal_basis.py @@ -2,6 +2,7 @@ import numpy as np import pytest +from numpy.ma.testutils import assert_array_almost_equal from toqito.matrix_props import is_orthonormal from toqito.rand import random_orthonormal_basis @@ -15,3 +16,28 @@ def test_random_orth_basis_int_dim(input_dim, bool): assert len(gen_basis) == input_dim assert is_orthonormal(np.array(gen_basis)) +@pytest.mark.parametrize( + "dim, is_real, expected", + [ + ( + 2, + False, + [ + np.array([0.51345691+0.6924564j, 0.16581665-0.47892689j]), + np.array([0.38709015+0.32715034j, -0.08796944+0.85755189j]) + ] + ), +( + 2, + True, + [ + np.array([0.95160818, 0.30731397]), + np.array([-0.30731397, 0.95160818]) + ] + ), + ] +) +def test_seed(dim, is_real, expected): + """Test that the function returns the expected output when seeded.""" + basis = random_orthonormal_basis(dim, is_real, seed=123) + assert_array_almost_equal(basis, expected) diff --git a/toqito/rand/tests/test_random_povm.py b/toqito/rand/tests/test_random_povm.py index 34250f860..61dc0ffde 100644 --- a/toqito/rand/tests/test_random_povm.py +++ b/toqito/rand/tests/test_random_povm.py @@ -2,6 +2,7 @@ import numpy as np import pytest +from numpy.ma.testutils import assert_array_almost_equal from toqito.rand import random_povm @@ -24,3 +25,40 @@ def test_random_povm_validity(dim, num_inputs, num_outputs): for i in range(num_inputs): sum_povms = sum(povms[:, :, i, j] for j in range(num_outputs)) assert np.allclose(sum_povms, np.identity(dim)) + +@pytest.mark.parametrize( + "dim, num_inputs, num_outputs, expected", + [ + ( + 2, + 2, + 2, + np.array([ + [ + [ + [0.68105648+0.j, 0.31894352+0.j], + [0.46623871+0.j, 0.53376129+0.j] + ], + [ + [0.01373155+0.j, -0.01373155+0.j], + [0.42523981+0.j, -0.42523981+0.j] + ] + ], + [ + [ + [0.01373155+0.j, -0.01373155+0.j], + [0.42523981+0.j, -0.42523981+0.j] + ], + [ + [0.04748388+0.j, 0.95251612+0.j], + [0.47081969+0.j, 0.52918031+0.j] + ] + ] + ]) + ) + ] +) +def test_seed(dim, num_inputs, num_outputs, expected): + """Test that the function returns the expected output when seeded.""" + povms = random_povm(dim=dim, num_inputs=num_inputs, num_outputs=num_outputs, seed=123) + assert_array_almost_equal(povms, expected) diff --git a/toqito/rand/tests/test_random_state_vector.py b/toqito/rand/tests/test_random_state_vector.py index f000dc6af..4966d9b77 100644 --- a/toqito/rand/tests/test_random_state_vector.py +++ b/toqito/rand/tests/test_random_state_vector.py @@ -1,6 +1,7 @@ """Test random_state_vector.""" - +import numpy as np import pytest +from numpy.ma.testutils import assert_array_almost_equal from toqito.rand import random_state_vector from toqito.state_props import is_pure @@ -27,3 +28,59 @@ def test_random_state_vector(dim, is_real, k_param): vec = random_state_vector(dim=dim, is_real=is_real, k_param=k_param).reshape(-1, 1) mat = vec @ vec.conj().T assert is_pure(mat) + +@pytest.mark.parametrize( + "dim, is_real, k_param, expected", + [ + ( + 2, + False, + 0, + np.array([ + [0.91920422+0.29684938j], + [0.07250293+0.24836943j] + ]) + ), + ( + 2, + False, + 1, + np.array([ + -0.01113702 + 0.61768143j, + 0.07125721 + 0.20424701j, + -0.68156797 + 0.21116929j, + -0.19827006 + 0.15202896j + ]) + ), + ( + [2, 2], + False, + 1, + np.array([ + -0.01113702 + 0.61768143j, + 0.07125721 + 0.20424701j, + -0.68156797 + 0.21116929j, + -0.19827006 + 0.15202896j + ]) + ), + ( + 2, + True, + 1, + np.array([0.76458086, 0.63971337, 0.06030689, 0.05045788]) + ), + ( + 2, + True, + 0, + np.array([ + [0.99690375], + [0.07863154] + ]) + ), + ], +) +def test_seed(dim, is_real, k_param, expected): + """Test that the function returns the expected output when seeded.""" + vec = random_state_vector(dim, is_real=is_real, k_param=k_param, seed=123) + assert_array_almost_equal(vec, expected) diff --git a/toqito/rand/tests/test_random_states.py b/toqito/rand/tests/test_random_states.py index f7a37ac13..9825e96f3 100644 --- a/toqito/rand/tests/test_random_states.py +++ b/toqito/rand/tests/test_random_states.py @@ -2,6 +2,7 @@ import numpy as np import pytest +from numpy.ma.testutils import assert_array_almost_equal from toqito.rand import random_states from toqito.state_props import is_pure @@ -35,3 +36,78 @@ def test_random_states(num_states, dim): dm = np.outer(state, np.conj(state)) # Verify each state is pure. assert is_pure(dm) + +@pytest.mark.parametrize( + "num_states, dim, expected", + [ + # Test with a single quantum state of dimension 2. + ( + 1, + 2, + [ + np.array([ + [-0.59005974+0.76831103j], + [-0.2194029 +0.11571532j] + ]) + ] + ), + # Test with multiple quantum states of the same dimension. + ( + 3, + 2, + [ + np.array([ + [-0.73471584-0.47276295j], + [-0.27319062+0.40256019j] + ]), + np.array([ + [0.93422522-0.22964955j], + [0.14070366-0.23385211j] + ]), + np.array([ + [0.49063955+0.0518067j], + [0.30769445-0.81358038j] + ]) + ] + ), + # Test with a single quantum state of higher dimension. + ( + 1, + 4, + [ + np.array([ + [-0.45679821+0.42498307j], + [-0.16985205+0.26651935j], + [0.5947925-0.29393305j], + [0.0895817+0.25028557j] + ]) + ] + ), + # Test with multiple quantum states of higher dimension. + ( + 2, + 4, + [ + np.array([ + [-0.42755142-0.13684957j], + [-0.15897716-0.13935391j], + [0.55671053+0.04200094j], + [0.08384617-0.65958915j] + ]), + np.array([ + [0.4213706+0.5458888j], + [0.26425386-0.30728968j], + [-0.29143454+0.45801996j], + [0.24815808+0.06242098j] + ]) + ] + ), + ], +) +def test_seed(num_states, dim, expected): + """Test that the function returns the expected output when seeded.""" + states = random_states(num_states, dim, seed=123) + assert len(states) == len(expected) + + for state, expected_state in zip(states, expected): + assert_array_almost_equal(state, expected_state) diff --git a/toqito/rand/tests/test_random_unitary.py b/toqito/rand/tests/test_random_unitary.py index 664e65053..fa4b2beb4 100644 --- a/toqito/rand/tests/test_random_unitary.py +++ b/toqito/rand/tests/test_random_unitary.py @@ -1,6 +1,7 @@ """Test random_unitary.""" - +import numpy as np import pytest +from numpy.ma.testutils import assert_array_almost_equal from toqito.matrix_props import is_unitary from toqito.rand import random_unitary @@ -32,3 +33,29 @@ def test_non_square_dims(dim_n, dim_m, is_real): if dim_n != dim_m: with pytest.raises(ValueError, match="Unitary matrix must be square."): random_unitary(dim=[dim_n, dim_m], is_real=is_real) + +@pytest.mark.parametrize( + "dim, is_real, expected", + [ + ( + 2, + False, + np.array([ + [0.51345691+0.6924564j, 0.38709015+0.32715034j], + [0.16581665-0.47892689j, -0.08796944+0.85755189j] + ]) + ), + ( + 2, + True, + np.array([ + [0.95160818, -0.30731397], + [0.30731397, 0.95160818] + ]) + ), + ] +) +def test_seed(dim, is_real, expected): + """Test that the function returns the expected output when seeded.""" + mat = random_unitary(dim=dim, is_real=is_real, seed=123) + assert_array_almost_equal(mat, expected)