From 7643ae23bf8d9d0d18ad7bcf69ade2799b8ce1ab Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 8 Jan 2025 03:21:40 +0000
Subject: [PATCH 1/3] Check in artifacts-> Frozen Doc Dependencies Update
---
.github/stable/doc.txt | 199 +++++++++++++++++------------------------
1 file changed, 80 insertions(+), 119 deletions(-)
diff --git a/.github/stable/doc.txt b/.github/stable/doc.txt
index 929ae02c9ae..05d5cb80baf 100644
--- a/.github/stable/doc.txt
+++ b/.github/stable/doc.txt
@@ -1,121 +1,82 @@
-absl-py==2.1.0
-aiohappyeyeballs==2.4.3
-aiohttp==3.10.10
-aiosignal==1.3.1
-alabaster==0.7.16
-appdirs==1.4.4
-astunparse==1.6.3
-async-timeout==4.0.3
-attrs==24.2.0
-autograd==1.7.0
-autoray==0.6.12
-babel==2.16.0
-cachetools==5.5.0
-certifi==2024.8.30
-charset-normalizer==3.4.0
-cirq-core==1.3.0
-contourpy==1.3.0
-cycler==0.12.1
-deprecation==2.1.0
-docutils==0.16
-duet==0.2.9
-exceptiongroup==1.2.2
-flatbuffers==24.3.25
-fonttools==4.54.1
-frozenlist==1.4.1
-fsspec==2024.9.0
-gast==0.4.0
-google-auth==2.35.0
-google-auth-oauthlib==0.4.6
-google-pasta==0.2.0
-graphviz==0.20.3
-grpcio==1.67.0
-h5py==3.12.1
-idna==3.10
-imagesize==1.4.1
-importlib_metadata==8.5.0
-importlib_resources==6.4.5
-iniconfig==2.0.0
-jax==0.4.16
-jaxlib==0.4.16
-Jinja2==3.0.3
-keras==2.11.0
-kiwisolver==1.4.7
-latexcodec==3.0.0
-libclang==18.1.1
-m2r2==0.3.2
-Markdown==3.7
-MarkupSafe==3.0.1
-matplotlib==3.8.0
-mistune==0.8.4
-ml_dtypes==0.5.0
-mpmath==1.3.0
-multidict==6.1.0
-networkx==2.6
-numpy==1.26.4
+argcomplete==3.5.3
+attrs==23.2.0
+Automat==22.10.0
+Babel==2.10.3
+bcc==0.29.1
+bcrypt==3.2.2
+blinker==1.7.0
+boto3==1.34.46
+botocore==1.34.46
+certifi==2023.11.17
+chardet==5.2.0
+click==8.1.6
+cloud-init==24.3.1
+colorama==0.4.6
+command-not-found==0.3
+configobj==5.0.8
+constantly==23.10.4
+cryptography==41.0.7
+dbus-python==1.3.2
+distro==1.9.0
+distro-info==1.7+build1
+httplib2==0.20.4
+hyperlink==21.0.0
+idna==3.6
+incremental==22.10.0
+Jinja2==3.1.2
+jmespath==1.0.1
+jsonpatch==1.32
+jsonpointer==2.0
+jsonschema==4.10.3
+launchpadlib==1.11.0
+lazr.restfulclient==0.14.6
+lazr.uri==1.0.6
+markdown-it-py==3.0.0
+MarkupSafe==2.1.5
+mdurl==0.1.2
+mercurial==6.7.2
+netaddr==0.8.0
+netifaces==0.11.0
oauthlib==3.2.2
-openfermion==1.6.1
-openfermionpyscf==0.5
-opt_einsum==3.4.0
-packaging==24.1
-pandas==2.2.3
-pennylane-sphinx-theme==0.5.8
-PennyLane_Lightning==0.38.0
-pillow==11.0.0
-pluggy==1.5.0
-propcache==0.2.0
-protobuf==3.19.6
-PubChemPy==1.0.4
-pyasn1==0.6.1
-pyasn1_modules==0.4.1
-pybtex==0.24.0
-pybtex-docutils==1.0.3
-Pygments==2.18.0
-pygments-github-lexers==0.0.5
-pyparsing==3.2.0
-pyscf==2.7.0
-pytest==8.3.3
-python-dateutil==2.9.0.post0
-pytz==2024.2
-PyYAML==6.0.2
-requests==2.28.2
-requests-oauthlib==2.0.0
-rsa==4.9
-rustworkx==0.15.1
-scipy==1.13.1
+packaging==24.0
+pexpect==4.9.0
+pipx==1.7.1
+platformdirs==4.3.6
+ptyprocess==0.7.0
+pyasn1==0.4.8
+pyasn1-modules==0.2.8
+Pygments==2.17.2
+PyGObject==3.48.2
+PyHamcrest==2.1.0
+PyJWT==2.7.0
+pyOpenSSL==23.2.0
+pyparsing==3.1.1
+pyparted==3.12.0
+pyrsistent==0.20.0
+pyserial==3.5
+python-apt==2.7.7+ubuntu3
+python-dateutil==2.8.2
+python-debian==0.1.49+ubuntu2
+python-magic==0.4.27
+pytz==2024.1
+PyYAML==6.0.1
+requests==2.31.0
+rich==13.7.1
+s3transfer==0.10.1
+service-identity==24.1.0
+setuptools==68.1.2
six==1.16.0
-snowballstemmer==2.2.0
-sortedcontainers==2.4.0
-Sphinx==3.5.4
-sphinx-automodapi==0.13
-sphinx-copybutton==0.5.2
-sphinx-gallery==0.12.2
-sphinxcontrib-applehelp==1.0.4
-sphinxcontrib-bibtex==2.4.2
-sphinxcontrib-devhelp==1.0.2
-sphinxcontrib-htmlhelp==2.0.1
-sphinxcontrib-jsmath==1.0.1
-sphinxcontrib-qthelp==1.0.3
-sphinxcontrib-serializinghtml==1.1.5
-sphinxext-opengraph==0.6.3
-sympy==1.13.3
-tensorboard==2.11.2
-tensorboard-data-server==0.6.1
-tensorboard-plugin-wit==1.8.1
-tensorflow==2.11.1
-tensorflow-estimator==2.11.0
-tensorflow-io-gcs-filesystem==0.37.1
-tensornetwork==0.3.0
-termcolor==2.5.0
-toml==0.10.2
-tomli==2.0.2
-torch==1.9.0+cpu
-tqdm==4.66.5
-typing_extensions==4.12.2
-tzdata==2024.2
-urllib3==1.26.20
-Werkzeug==3.0.4
-wrapt==1.16.0
-xanadu-sphinx-theme==0.5.0
-yarl==1.15.4
-zipp==3.20.2
+sos==4.7.2
+ssh-import-id==5.11
+systemd-python==235
+Twisted==24.3.0
+typing_extensions==4.10.0
+ubuntu-pro-client==8001
+ufw==0.36.2
+urllib3==2.0.7
+userpath==1.9.2
+wadllib==1.3.6
+WALinuxAgent==2.9.1.1
+wheel==0.42.0
+zope.interface==6.1
+zstandard==0.22.0
From aeae8fc89747f9268b8997a7d3a274a44bee780f Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 13 Jan 2025 03:40:54 +0000
Subject: [PATCH 2/3] Check in artifacts-> Frozen Dependencies Update
---
.github/stable/all_interfaces.txt | 6 +-
.github/stable/core.txt | 4 +-
.github/stable/doc.txt | 199 +++++++++++-------
.github/stable/external.txt | 16 +-
.github/stable/jax.txt | 6 +-
.github/stable/tf.txt | 6 +-
.github/stable/torch.txt | 4 +-
.github/workflows/tests.yml | 2 +
doc/code/qml_fermi.rst | 8 +-
doc/releases/changelog-0.40.0.md | 10 +-
pennylane/__init__.py | 2 +
pennylane/_version.py | 2 +-
pennylane/bose/bosonic.py | 20 +-
pennylane/bose/bosonic_mapping.py | 10 +-
pennylane/devices/_legacy_device.py | 5 +-
pennylane/devices/qubit/sampling.py | 2 +-
pennylane/fermi/fermionic.py | 24 ++-
.../labs/vibrational/christiansen_utils.py | 70 +++---
pennylane/ops/functions/equal.py | 33 ---
pennylane/qchem/convert_openfermion.py | 10 +-
pennylane/qchem/observable_hf.py | 10 +-
pennylane/qchem/structure.py | 4 +-
.../templates/subroutines/basis_rotation.py | 14 +-
tests/bose/test_binary_mapping.py | 2 +-
tests/bose/test_christiansen_mapping.py | 2 +-
tests/bose/test_unary_mapping.py | 2 +-
tests/devices/test_legacy_device.py | 14 ++
tests/ops/functions/test_equal.py | 4 +-
.../test_convert_openfermion.py | 28 ++-
.../test_subroutines/test_basis_rotation.py | 6 +-
30 files changed, 276 insertions(+), 249 deletions(-)
diff --git a/.github/stable/all_interfaces.txt b/.github/stable/all_interfaces.txt
index b53f5569d44..85709e2cf5d 100644
--- a/.github/stable/all_interfaces.txt
+++ b/.github/stable/all_interfaces.txt
@@ -38,7 +38,7 @@ isort==5.13.2
jax==0.4.28
jaxlib==0.4.28
Jinja2==3.1.5
-keras==3.7.0
+keras==3.8.0
kiwisolver==1.4.8
lazy-object-proxy==1.10.0
libclang==18.1.1
@@ -83,7 +83,7 @@ protobuf==4.25.5
py==1.11.0
py-cpuinfo==9.0.0
pydot==3.0.4
-Pygments==2.19.0
+Pygments==2.19.1
pylint==2.7.4
pyparsing==3.2.1
pytest==8.3.4
@@ -101,7 +101,7 @@ qdldl==0.1.7.post5
requests==2.32.3
rich==13.9.4
rustworkx==0.15.1
-scipy==1.15.0
+scipy==1.15.1
scs==3.2.7.post2
six==1.17.0
smmap==5.0.2
diff --git a/.github/stable/core.txt b/.github/stable/core.txt
index fc24ed2dff0..a96ee5cb56b 100644
--- a/.github/stable/core.txt
+++ b/.github/stable/core.txt
@@ -52,7 +52,7 @@ prompt_toolkit==3.0.48
py==1.11.0
py-cpuinfo==9.0.0
pydot==3.0.4
-Pygments==2.19.0
+Pygments==2.19.1
pylint==2.7.4
pyparsing==3.2.1
pytest==8.3.4
@@ -70,7 +70,7 @@ qdldl==0.1.7.post5
requests==2.32.3
rich==13.9.4
rustworkx==0.15.1
-scipy==1.15.0
+scipy==1.15.1
scs==3.2.7.post2
six==1.17.0
smmap==5.0.2
diff --git a/.github/stable/doc.txt b/.github/stable/doc.txt
index 05d5cb80baf..929ae02c9ae 100644
--- a/.github/stable/doc.txt
+++ b/.github/stable/doc.txt
@@ -1,82 +1,121 @@
-argcomplete==3.5.3
-attrs==23.2.0
-Automat==22.10.0
-Babel==2.10.3
-bcc==0.29.1
-bcrypt==3.2.2
-blinker==1.7.0
-boto3==1.34.46
-botocore==1.34.46
-certifi==2023.11.17
-chardet==5.2.0
-click==8.1.6
-cloud-init==24.3.1
-colorama==0.4.6
-command-not-found==0.3
-configobj==5.0.8
-constantly==23.10.4
-cryptography==41.0.7
-dbus-python==1.3.2
-distro==1.9.0
-distro-info==1.7+build1
-httplib2==0.20.4
-hyperlink==21.0.0
-idna==3.6
-incremental==22.10.0
-Jinja2==3.1.2
-jmespath==1.0.1
-jsonpatch==1.32
-jsonpointer==2.0
-jsonschema==4.10.3
-launchpadlib==1.11.0
-lazr.restfulclient==0.14.6
-lazr.uri==1.0.6
-markdown-it-py==3.0.0
-MarkupSafe==2.1.5
-mdurl==0.1.2
-mercurial==6.7.2
-netaddr==0.8.0
-netifaces==0.11.0
+absl-py==2.1.0
+aiohappyeyeballs==2.4.3
+aiohttp==3.10.10
+aiosignal==1.3.1
+alabaster==0.7.16
+appdirs==1.4.4
+astunparse==1.6.3
+async-timeout==4.0.3
+attrs==24.2.0
+autograd==1.7.0
+autoray==0.6.12
+babel==2.16.0
+cachetools==5.5.0
+certifi==2024.8.30
+charset-normalizer==3.4.0
+cirq-core==1.3.0
+contourpy==1.3.0
+cycler==0.12.1
+deprecation==2.1.0
+docutils==0.16
+duet==0.2.9
+exceptiongroup==1.2.2
+flatbuffers==24.3.25
+fonttools==4.54.1
+frozenlist==1.4.1
+fsspec==2024.9.0
+gast==0.4.0
+google-auth==2.35.0
+google-auth-oauthlib==0.4.6
+google-pasta==0.2.0
+graphviz==0.20.3
+grpcio==1.67.0
+h5py==3.12.1
+idna==3.10
+imagesize==1.4.1
+importlib_metadata==8.5.0
+importlib_resources==6.4.5
+iniconfig==2.0.0
+jax==0.4.16
+jaxlib==0.4.16
+Jinja2==3.0.3
+keras==2.11.0
+kiwisolver==1.4.7
+latexcodec==3.0.0
+libclang==18.1.1
+m2r2==0.3.2
+Markdown==3.7
+MarkupSafe==3.0.1
+matplotlib==3.8.0
+mistune==0.8.4
+ml_dtypes==0.5.0
+mpmath==1.3.0
+multidict==6.1.0
+networkx==2.6
+numpy==1.26.4
oauthlib==3.2.2
-packaging==24.0
-pexpect==4.9.0
-pipx==1.7.1
-platformdirs==4.3.6
-ptyprocess==0.7.0
-pyasn1==0.4.8
-pyasn1-modules==0.2.8
-Pygments==2.17.2
-PyGObject==3.48.2
-PyHamcrest==2.1.0
-PyJWT==2.7.0
-pyOpenSSL==23.2.0
-pyparsing==3.1.1
-pyparted==3.12.0
-pyrsistent==0.20.0
-pyserial==3.5
-python-apt==2.7.7+ubuntu3
-python-dateutil==2.8.2
-python-debian==0.1.49+ubuntu2
-python-magic==0.4.27
-pytz==2024.1
-PyYAML==6.0.1
-requests==2.31.0
-rich==13.7.1
-s3transfer==0.10.1
-service-identity==24.1.0
-setuptools==68.1.2
+openfermion==1.6.1
+openfermionpyscf==0.5
+opt_einsum==3.4.0
+packaging==24.1
+pandas==2.2.3
+pennylane-sphinx-theme==0.5.8
+PennyLane_Lightning==0.38.0
+pillow==11.0.0
+pluggy==1.5.0
+propcache==0.2.0
+protobuf==3.19.6
+PubChemPy==1.0.4
+pyasn1==0.6.1
+pyasn1_modules==0.4.1
+pybtex==0.24.0
+pybtex-docutils==1.0.3
+Pygments==2.18.0
+pygments-github-lexers==0.0.5
+pyparsing==3.2.0
+pyscf==2.7.0
+pytest==8.3.3
+python-dateutil==2.9.0.post0
+pytz==2024.2
+PyYAML==6.0.2
+requests==2.28.2
+requests-oauthlib==2.0.0
+rsa==4.9
+rustworkx==0.15.1
+scipy==1.13.1
six==1.16.0
-sos==4.7.2
-ssh-import-id==5.11
-systemd-python==235
-Twisted==24.3.0
-typing_extensions==4.10.0
-ubuntu-pro-client==8001
-ufw==0.36.2
-urllib3==2.0.7
-userpath==1.9.2
-wadllib==1.3.6
-WALinuxAgent==2.9.1.1
-wheel==0.42.0
-zope.interface==6.1
-zstandard==0.22.0
+snowballstemmer==2.2.0
+sortedcontainers==2.4.0
+Sphinx==3.5.4
+sphinx-automodapi==0.13
+sphinx-copybutton==0.5.2
+sphinx-gallery==0.12.2
+sphinxcontrib-applehelp==1.0.4
+sphinxcontrib-bibtex==2.4.2
+sphinxcontrib-devhelp==1.0.2
+sphinxcontrib-htmlhelp==2.0.1
+sphinxcontrib-jsmath==1.0.1
+sphinxcontrib-qthelp==1.0.3
+sphinxcontrib-serializinghtml==1.1.5
+sphinxext-opengraph==0.6.3
+sympy==1.13.3
+tensorboard==2.11.2
+tensorboard-data-server==0.6.1
+tensorboard-plugin-wit==1.8.1
+tensorflow==2.11.1
+tensorflow-estimator==2.11.0
+tensorflow-io-gcs-filesystem==0.37.1
+tensornetwork==0.3.0
+termcolor==2.5.0
+toml==0.10.2
+tomli==2.0.2
+torch==1.9.0+cpu
+tqdm==4.66.5
+typing_extensions==4.12.2
+tzdata==2024.2
+urllib3==1.26.20
+Werkzeug==3.0.4
+wrapt==1.16.0
+xanadu-sphinx-theme==0.5.0
+yarl==1.15.4
+zipp==3.20.2
diff --git a/.github/stable/external.txt b/.github/stable/external.txt
index 5cb949a78d7..8fea222615a 100644
--- a/.github/stable/external.txt
+++ b/.github/stable/external.txt
@@ -27,7 +27,7 @@ clarabel==0.9.0
click==8.1.8
comm==0.2.2
contourpy==1.3.1
-cotengra==0.6.2
+cotengra==0.7.0
coverage==7.6.10
cryptography==44.0.0
cvxopt==1.3.2
@@ -60,8 +60,8 @@ h11==0.14.0
h5py==3.12.1
httpcore==1.0.7
httpx==0.28.1
-ibm-cloud-sdk-core==3.22.0
-ibm-platform-services==0.59.0
+ibm-cloud-sdk-core==3.22.1
+ibm-platform-services==0.59.1
identify==2.6.5
idna==3.10
iniconfig==2.0.0
@@ -89,7 +89,7 @@ jupyterlab==4.3.4
jupyterlab_pygments==0.3.0
jupyterlab_server==2.27.3
jupyterlab_widgets==1.1.11
-keras==3.7.0
+keras==3.8.0
kiwisolver==1.4.8
lark==1.1.9
lazy-object-proxy==1.10.0
@@ -129,8 +129,8 @@ pandocfilters==1.5.1
parso==0.8.4
pathspec==0.12.1
pbr==6.1.0
-PennyLane-Catalyst==0.10.0.dev39
-PennyLane-qiskit @ git+https://github.com/PennyLaneAI/pennylane-qiskit.git@40a4d24f126e51e0e3e28a4cd737f883a6fd5ebc
+PennyLane-Catalyst==0.11.0.dev1
+PennyLane-qiskit @ git+https://github.com/PennyLaneAI/pennylane-qiskit.git@927bab3fc2dfdbac9d57999f2d7e9761a6254491
PennyLane_Lightning==0.40.0
PennyLane_Lightning_Kokkos==0.40.0.dev41
pexpect==4.9.0
@@ -148,10 +148,10 @@ pure_eval==0.2.3
py==1.11.0
py-cpuinfo==9.0.0
pycparser==2.22
-pydantic==2.10.4
+pydantic==2.10.5
pydantic_core==2.27.2
pydot==3.0.4
-Pygments==2.19.0
+Pygments==2.19.1
PyJWT==2.10.1
pylint==2.7.4
pyparsing==3.2.1
diff --git a/.github/stable/jax.txt b/.github/stable/jax.txt
index ed2c0b32037..1cfd4d83cd7 100644
--- a/.github/stable/jax.txt
+++ b/.github/stable/jax.txt
@@ -37,7 +37,7 @@ markdown-it-py==3.0.0
matplotlib==3.10.0
mccabe==0.6.1
mdurl==0.1.2
-ml_dtypes==0.5.0
+ml_dtypes==0.5.1
mypy-extensions==1.0.0
networkx==3.4.2
nodeenv==1.9.1
@@ -56,7 +56,7 @@ prompt_toolkit==3.0.48
py==1.11.0
py-cpuinfo==9.0.0
pydot==3.0.4
-Pygments==2.19.0
+Pygments==2.19.1
pylint==2.7.4
pyparsing==3.2.1
pytest==8.3.4
@@ -74,7 +74,7 @@ qdldl==0.1.7.post5
requests==2.32.3
rich==13.9.4
rustworkx==0.15.1
-scipy==1.15.0
+scipy==1.15.1
scs==3.2.7.post2
six==1.17.0
smmap==5.0.2
diff --git a/.github/stable/tf.txt b/.github/stable/tf.txt
index 634fbd09526..09fb2f13095 100644
--- a/.github/stable/tf.txt
+++ b/.github/stable/tf.txt
@@ -34,7 +34,7 @@ identify==2.6.5
idna==3.10
iniconfig==2.0.0
isort==5.13.2
-keras==3.7.0
+keras==3.8.0
kiwisolver==1.4.8
lazy-object-proxy==1.10.0
libclang==18.1.1
@@ -66,7 +66,7 @@ protobuf==4.25.5
py==1.11.0
py-cpuinfo==9.0.0
pydot==3.0.4
-Pygments==2.19.0
+Pygments==2.19.1
pylint==2.7.4
pyparsing==3.2.1
pytest==8.3.4
@@ -84,7 +84,7 @@ qdldl==0.1.7.post5
requests==2.32.3
rich==13.9.4
rustworkx==0.15.1
-scipy==1.15.0
+scipy==1.15.1
scs==3.2.7.post2
six==1.17.0
smmap==5.0.2
diff --git a/.github/stable/torch.txt b/.github/stable/torch.txt
index 3ab4c0e93f2..3b1ca9abc78 100644
--- a/.github/stable/torch.txt
+++ b/.github/stable/torch.txt
@@ -68,7 +68,7 @@ prompt_toolkit==3.0.48
py==1.11.0
py-cpuinfo==9.0.0
pydot==3.0.4
-Pygments==2.19.0
+Pygments==2.19.1
pylint==2.7.4
pyparsing==3.2.1
pytest==8.3.4
@@ -86,7 +86,7 @@ qdldl==0.1.7.post5
requests==2.32.3
rich==13.9.4
rustworkx==0.15.1
-scipy==1.15.0
+scipy==1.15.1
scs==3.2.7.post2
six==1.17.0
smmap==5.0.2
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index e0bfa8d1fe7..63c8074e524 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -9,6 +9,8 @@ on:
- reopened
- synchronize
- ready_for_review
+ branches-ignore:
+ - 'v[0-9]+.[0-9]+.[0-9]+-docs'
# Scheduled trigger on Monday at 2:47am UTC
schedule:
- cron: "47 2 * * 1"
diff --git a/doc/code/qml_fermi.rst b/doc/code/qml_fermi.rst
index 2c528c1f2fe..b6c9d1b6a57 100644
--- a/doc/code/qml_fermi.rst
+++ b/doc/code/qml_fermi.rst
@@ -78,15 +78,15 @@ the orbital it acts on. The values of the dictionary are one of ``'+'`` or ``'-'
denote creation and annihilation operators, respectively. The operator
:math:`a^{\dagger}_0 a_3 a^{\dagger}_1` can then be constructed with
->>> qml.fermi.FermiWord({(0, 0): '+', (1, 3): '-', (2, 1): '+'})
+>>> qml.FermiWord({(0, 0): '+', (1, 3): '-', (2, 1): '+'})
a⁺(0) a(3) a⁺(1)
A Fermi sentence can be constructed directly by passing a dictionary of Fermi words and their
corresponding coefficients to the :class:`~pennylane.fermi.FermiSentence` class. For instance, the
Fermi sentence :math:`1.2 a^{\dagger}_0 a_0 + 2.3 a^{\dagger}_3 a_3` can be constructed as
->>> fw1 = qml.fermi.FermiWord({(0, 0): '+', (1, 0): '-'})
->>> fw2 = qml.fermi.FermiWord({(0, 3): '+', (1, 3): '-'})
->>> qml.fermi.FermiSentence({fw1: 1.2, fw2: 2.3})
+>>> fw1 = qml.FermiWord({(0, 0): '+', (1, 0): '-'})
+>>> fw2 = qml.FermiWord({(0, 3): '+', (1, 3): '-'})
+>>> qml.FermiSentence({fw1: 1.2, fw2: 2.3})
1.2 * a⁺(0) a(0)
+ 2.3 * a⁺(3) a(3)
diff --git a/doc/releases/changelog-0.40.0.md b/doc/releases/changelog-0.40.0.md
index e3769326619..7d9707882e6 100644
--- a/doc/releases/changelog-0.40.0.md
+++ b/doc/releases/changelog-0.40.0.md
@@ -111,9 +111,6 @@
* Added support to build a vibrational Hamiltonian in Taylor form.
[(#6523)](https://github.com/PennyLaneAI/pennylane/pull/6523)
-* Added support to build a vibrational Hamiltonian in the Christiansen form.
- [(#6560)](https://github.com/PennyLaneAI/pennylane/pull/6560)
-
Improvements 🛠
QChem improvements
@@ -389,6 +386,7 @@
* `qml.BasisRotation` template is now JIT compatible.
[(#6019)](https://github.com/PennyLaneAI/pennylane/pull/6019)
+ [(#6779)](https://github.com/PennyLaneAI/pennylane/pull/6779)
* The Jaxpr primitives for `for_loop`, `while_loop` and `cond` now store slices instead of
numbers of args.
@@ -493,6 +491,12 @@ such as `shots`, `rng` and `prng_key`.
for the horizontal Cartan subalgebra instead of `$\mathfrak{h}$`.
[(#6747)](https://github.com/PennyLaneAI/pennylane/pull/6747)
+Construct vibrational Hamiltonians 🫨
+
+* Added support to build a vibrational Hamiltonian in the Christiansen form.
+ [(#6560)](https://github.com/PennyLaneAI/pennylane/pull/6560)
+ [(#6792)](https://github.com/PennyLaneAI/pennylane/pull/6792)
+
Breaking changes 💔
* The default graph coloring method of `qml.dot`, `qml.sum`, and `qml.pauli.optimize_measurements` for grouping observables was changed
diff --git a/pennylane/__init__.py b/pennylane/__init__.py
index 3d1c0f1b911..6e8a8fe903b 100644
--- a/pennylane/__init__.py
+++ b/pennylane/__init__.py
@@ -35,6 +35,8 @@
from pennylane.fermi import (
FermiC,
FermiA,
+ FermiWord,
+ FermiSentence,
jordan_wigner,
parity_transform,
bravyi_kitaev,
diff --git a/pennylane/_version.py b/pennylane/_version.py
index a5d50fca8dd..15db8ba2cd5 100644
--- a/pennylane/_version.py
+++ b/pennylane/_version.py
@@ -16,4 +16,4 @@
Version number (major.minor.patch[-label])
"""
-__version__ = "0.41.0-dev2"
+__version__ = "0.41.0-dev4"
diff --git a/pennylane/bose/bosonic.py b/pennylane/bose/bosonic.py
index 82da7b39fbb..cb09a1a1143 100644
--- a/pennylane/bose/bosonic.py
+++ b/pennylane/bose/bosonic.py
@@ -30,7 +30,7 @@ class BoseWord(dict):
symbols that denote creation and annihilation operators, respectively. The operator
:math:`b^{\dagger}_0 b_1` can then be constructed as
- >>> w = qml.bose.BoseWord({(0, 0) : '+', (1, 1) : '-'})
+ >>> w = qml.BoseWord({(0, 0) : '+', (1, 1) : '-'})
>>> print(w)
b⁺(0) b(1)
"""
@@ -112,7 +112,7 @@ def to_string(self):
represented by the number of the wire it operates on, and a `+` or `-` to indicate either
a creation or annihilation operator.
- >>> w = qml.bose.BoseWord({(0, 0) : '+', (1, 1) : '-'})
+ >>> w = qml.BoseWord({(0, 0) : '+', (1, 1) : '-'})
>>> w.to_string()
'b⁺(0) b(1)'
"""
@@ -209,7 +209,7 @@ def __rsub__(self, other):
def __mul__(self, other):
r"""Multiply a BoseWord with another BoseWord, a BoseSentence, or a constant.
- >>> w = qml.bose.BoseWord({(0, 0) : '+', (1, 1) : '-'})
+ >>> w = qml.BoseWord({(0, 0) : '+', (1, 1) : '-'})
>>> print(w * w)
b⁺(0) b(1) b⁺(0) b(1)
"""
@@ -263,7 +263,7 @@ def __rmul__(self, other):
def __pow__(self, value):
r"""Exponentiate a Bose word to an integer power.
- >>> w = qml.bose.BoseWord({(0, 0) : '+', (1, 1) : '-'})
+ >>> w = qml.BoseWord({(0, 0) : '+', (1, 1) : '-'})
>>> print(w**3)
b⁺(0) b(1) b⁺(0) b(1) b⁺(0) b(1)
"""
@@ -280,7 +280,7 @@ def __pow__(self, value):
def normal_order(self):
r"""Convert a BoseWord to its normal-ordered form.
- >>> bw = qml.bose.BoseWord({(0, 0): "-", (1, 0): "-", (2, 0): "+", (3, 0): "+"})
+ >>> bw = qml.BoseWord({(0, 0): "-", (1, 0): "-", (2, 0): "+", (3, 0): "+"})
>>> print(bw.normal_order())
4.0 * b⁺(0) b(0)
+ 2.0 * I
@@ -420,9 +420,9 @@ class BoseSentence(dict):
r"""Dictionary used to represent a Bose sentence, a linear combination of Bose words,
with the keys as BoseWord instances and the values correspond to coefficients.
- >>> w1 = qml.bose.BoseWord({(0, 0) : '+', (1, 1) : '-'})
- >>> w2 = qml.bose.BoseWord({(0, 1) : '+', (1, 2) : '-'})
- >>> s = BoseSentence({w1 : 1.2, w2: 3.1})
+ >>> w1 = qml.BoseWord({(0, 0) : '+', (1, 1) : '-'})
+ >>> w2 = qml.BoseWord({(0, 1) : '+', (1, 2) : '-'})
+ >>> s = qml.BoseSentence({w1 : 1.2, w2: 3.1})
>>> print(s)
1.2 * b⁺(0) b(1)
+ 3.1 * b⁺(1) b(2)
@@ -608,8 +608,8 @@ def simplify(self, tol=1e-8):
def normal_order(self):
r"""Convert a BoseSentence to its normal-ordered form.
- >>> bw = qml.bose.BoseWord({(0, 0): "-", (1, 0): "-", (2, 0): "+", (3, 0): "+"})
- >>> bs = qml.bose.BoseSentence({bw: 1})
+ >>> bw = qml.BoseWord({(0, 0): "-", (1, 0): "-", (2, 0): "+", (3, 0): "+"})
+ >>> bs = qml.BoseSentence({bw: 1})
>>> print(bw.normal_order())
4.0 * b⁺(0) b(0)
+ 2.0 * I
diff --git a/pennylane/bose/bosonic_mapping.py b/pennylane/bose/bosonic_mapping.py
index 5c5eb55341d..87112648b8e 100644
--- a/pennylane/bose/bosonic_mapping.py
+++ b/pennylane/bose/bosonic_mapping.py
@@ -65,7 +65,7 @@ def binary_mapping(
**Example**
- >>> w = qml.bose.BoseWord({(0, 0): "+"})
+ >>> w = qml.BoseWord({(0, 0): "+"})
>>> qml.binary_mapping(w, n_states=4)
(
0.6830127018922193 * X(0)
@@ -95,7 +95,7 @@ def binary_mapping(
@singledispatch
def _binary_mapping_dispatch(bose_operator, n_states, tol):
"""Dispatches to appropriate function if bose_operator is a BoseWord or BoseSentence."""
- raise ValueError(f"bose_operator must be a BoseWord or BoseSentence, got: {bose_operator}")
+ raise TypeError(f"bose_operator must be a BoseWord or BoseSentence, got: {bose_operator}")
@_binary_mapping_dispatch.register
@@ -190,7 +190,7 @@ def unary_mapping(
**Example**
- >>> w = qml.bose.BoseWord({(0, 0): "+"})
+ >>> w = qml.BoseWord({(0, 0): "+"})
>>> qml.unary_mapping(w, n_states=4)
(
0.25 * X(0) @ X(1)
@@ -224,7 +224,7 @@ def unary_mapping(
@singledispatch
def _unary_mapping_dispatch(bose_operator, n_states, ps=False, wires_map=None, tol=None):
"""Dispatches to appropriate function if bose_operator is a BoseWord or BoseSentence."""
- raise ValueError(f"bose_operator must be a BoseWord or BoseSentence, got: {bose_operator}")
+ raise TypeError(f"bose_operator must be a BoseWord or BoseSentence, got: {bose_operator}")
@_unary_mapping_dispatch.register
@@ -367,7 +367,7 @@ def christiansen_mapping(
@singledispatch
def _christiansen_mapping_dispatch(bose_operator, tol):
"""Dispatches to appropriate function if bose_operator is a BoseWord or BoseSentence."""
- raise ValueError(f"bose_operator must be a BoseWord or BoseSentence, got: {bose_operator}")
+ raise TypeError(f"bose_operator must be a BoseWord or BoseSentence, got: {bose_operator}")
@_christiansen_mapping_dispatch.register
diff --git a/pennylane/devices/_legacy_device.py b/pennylane/devices/_legacy_device.py
index 93342f9d2e2..33400c37695 100644
--- a/pennylane/devices/_legacy_device.py
+++ b/pennylane/devices/_legacy_device.py
@@ -813,7 +813,10 @@ def _all_multi_term_obs_supported(self, circuit):
# Some measurements are not observable based.
continue
- if mp.obs.name == "LinearCombination" and not self.supports_observable("Hamiltonian"):
+ if mp.obs.name == "LinearCombination" and not (
+ self.supports_observable("Hamiltonian")
+ or self.supports_observable("LinearCombination")
+ ):
return False
if mp.obs.name in (
diff --git a/pennylane/devices/qubit/sampling.py b/pennylane/devices/qubit/sampling.py
index d8036a6fb99..06ae78b5708 100644
--- a/pennylane/devices/qubit/sampling.py
+++ b/pennylane/devices/qubit/sampling.py
@@ -330,7 +330,7 @@ def _process_single_shot(samples):
prng_key=prng_key,
)
except ValueError as e:
- if str(e) != "probabilities contain NaN":
+ if "probabilities contain nan" not in str(e).lower():
raise e
samples = qml.math.full((shots.total_shots, len(wires)), 0)
diff --git a/pennylane/fermi/fermionic.py b/pennylane/fermi/fermionic.py
index 6aacbdabe96..f4162794176 100644
--- a/pennylane/fermi/fermionic.py
+++ b/pennylane/fermi/fermionic.py
@@ -30,7 +30,7 @@ class FermiWord(dict):
:math:`a^{\dagger}_0 a_1` can then be constructed as
>>> w = FermiWord({(0, 0) : '+', (1, 1) : '-'})
- >>> w
+ >>> print(w)
a⁺(0) a(1)
"""
@@ -114,7 +114,7 @@ def to_string(self):
>>> w = FermiWord({(0, 0) : '+', (1, 1) : '-'})
>>> w.to_string()
- a⁺(0) a(1)
+ 'a⁺(0) a(1)'
"""
if len(self) == 0:
return "I"
@@ -211,7 +211,7 @@ def __mul__(self, other):
r"""Multiply a FermiWord with another FermiWord, a FermiSentence, or a constant.
>>> w = FermiWord({(0, 0) : '+', (1, 1) : '-'})
- >>> w * w
+ >>> print(w * w)
a⁺(0) a(1) a⁺(0) a(1)
"""
@@ -265,7 +265,7 @@ def __pow__(self, value):
r"""Exponentiate a Fermi word to an integer power.
>>> w = FermiWord({(0, 0) : '+', (1, 1) : '-'})
- >>> w**3
+ >>> print(w**3)
a⁺(0) a(1) a⁺(0) a(1) a⁺(0) a(1)
"""
@@ -348,7 +348,7 @@ def shift_operator(self, initial_position, final_position):
**Example**
- >>> w = qml.fermi.FermiWord({(0, 0): '+', (1, 1): '-'})
+ >>> w = qml.FermiWord({(0, 0): '+', (1, 1): '-'})
>>> w.shift_operator(0, 1)
-1 * a(1) a⁺(0)
"""
@@ -427,7 +427,7 @@ class FermiSentence(dict):
>>> w1 = FermiWord({(0, 0) : '+', (1, 1) : '-'})
>>> w2 = FermiWord({(0, 1) : '+', (1, 2) : '-'})
>>> s = FermiSentence({w1 : 1.2, w2: 3.1})
- >>> s
+ >>> print(s)
1.2 * a⁺(0) a(1)
+ 3.1 * a⁺(1) a(2)
"""
@@ -754,13 +754,15 @@ class FermiC(FermiWord):
To construct the operator :math:`a^{\dagger}_0`:
- >>> FermiC(0)
+ >>> w = FermiC(0)
+ >>> print(w)
a⁺(0)
This can be combined with the annihilation operator :class:`~pennylane.FermiA`. For example,
:math:`a^{\dagger}_0 a_1 a^{\dagger}_2 a_3` can be constructed as:
- >>> qml.FermiC(0) * qml.FermiA(1) * qml.FermiC(2) * qml.FermiA(3)
+ >>> w = qml.FermiC(0) * qml.FermiA(1) * qml.FermiC(2) * qml.FermiA(3)
+ >>> print(w)
a⁺(0) a(1) a⁺(2) a(3)
"""
@@ -796,13 +798,15 @@ class FermiA(FermiWord):
To construct the operator :math:`a_0`:
- >>> FermiA(0)
+ >>> w = FermiA(0)
+ >>> print(w)
a(0)
This can be combined with the creation operator :class:`~pennylane.FermiC`. For example,
:math:`a^{\dagger}_0 a_1 a^{\dagger}_2 a_3` can be constructed as:
- >>> qml.FermiC(0) * qml.FermiA(1) * qml.FermiC(2) * qml.FermiA(3)
+ >>> w = qml.FermiC(0) * qml.FermiA(1) * qml.FermiC(2) * qml.FermiA(3)
+ >>> print(w)
a⁺(0) a(1) a⁺(2) a(3)
"""
diff --git a/pennylane/labs/vibrational/christiansen_utils.py b/pennylane/labs/vibrational/christiansen_utils.py
index 93ef5c7e275..33a6f26ed2f 100644
--- a/pennylane/labs/vibrational/christiansen_utils.py
+++ b/pennylane/labs/vibrational/christiansen_utils.py
@@ -13,7 +13,7 @@
# limitations under the License.
"""Utility functions related to the construction of the taylor form Hamiltonian."""
import itertools
-import subprocess
+from pathlib import Path
import h5py
import numpy as np
@@ -516,7 +516,7 @@ def _load_cform_onemode(num_proc, nmodes, quad_order):
for rank in range(num_proc):
f = h5py.File("cform_H1data" + f"_{rank}" + ".hdf5", "r+")
local_ham_cform_onebody = f["H1"][()]
- chunk = np.array_split(local_ham_cform_onebody, nmode_combos)[mode_combo] #
+ chunk = np.array_split(local_ham_cform_onebody, nmode_combos)[mode_combo]
l1 += len(chunk)
local_chunk[l0:l1] = chunk
l0 += len(chunk)
@@ -767,17 +767,16 @@ def christiansen_integrals(pes, n_states=16, cubic=False):
local_ham_cform_onebody = _cform_onemode(pes, n_states)
comm.Barrier()
- f = h5py.File("cform_H1data" + f"_{rank}" + ".hdf5", "w")
- f.create_dataset("H1", data=local_ham_cform_onebody)
- f.close()
+ file_path = Path(f"cform_H1data_{rank}.hdf5")
+ with h5py.File(file_path, "w") as f:
+ f.create_dataset("H1", data=local_ham_cform_onebody)
comm.Barrier()
ham_cform_onebody = None
if rank == 0:
ham_cform_onebody = _load_cform_onemode(size, len(pes.freqs), n_states)
- process = subprocess.Popen("rm " + "cform_H1data*", stdout=subprocess.PIPE, shell=True)
- _, _ = process.communicate()
-
+ for path in Path.cwd().glob("cform_H1data*"):
+ path.unlink()
comm.Barrier()
ham_cform_onebody = comm.bcast(ham_cform_onebody, root=0)
@@ -786,34 +785,32 @@ def christiansen_integrals(pes, n_states=16, cubic=False):
local_ham_cform_twobody += _cform_twomode_kinetic(pes, n_states)
comm.Barrier()
- f = h5py.File("cform_H2data" + f"_{rank}" + ".hdf5", "w")
- f.create_dataset("H2", data=local_ham_cform_twobody)
- f.close()
+ file_path = Path(f"cform_H2data_{rank}.hdf5")
+ with h5py.File(file_path, "w") as f:
+ f.create_dataset("H2", data=local_ham_cform_twobody)
comm.Barrier()
ham_cform_twobody = None
if rank == 0:
ham_cform_twobody = _load_cform_twomode(size, len(pes.freqs), n_states)
- process = subprocess.Popen("rm " + "cform_H2data*", stdout=subprocess.PIPE, shell=True)
- _, _ = process.communicate()
-
+ for path in Path.cwd().glob("cform_H2data*"):
+ path.unlink()
comm.Barrier()
ham_cform_twobody = comm.bcast(ham_cform_twobody, root=0)
if cubic:
local_ham_cform_threebody = _cform_threemode(pes, n_states)
- f = h5py.File("cform_H3data" + f"_{rank}" + ".hdf5", "w")
- f.create_dataset("H3", data=local_ham_cform_threebody)
- f.close()
+ file_path = Path(f"cform_H3data_{rank}.hdf5")
+ with h5py.File(file_path, "w") as f:
+ f.create_dataset("H3", data=local_ham_cform_threebody)
comm.Barrier()
ham_cform_threebody = None
if rank == 0:
ham_cform_threebody = _load_cform_threemode(size, len(pes.freqs), n_states)
- process = subprocess.Popen("rm " + "cform_H3data*", stdout=subprocess.PIPE, shell=True)
- _, _ = process.communicate()
-
+ for path in Path.cwd().glob("cform_H3data*"):
+ path.unlink()
comm.Barrier()
ham_cform_threebody = comm.bcast(ham_cform_threebody, root=0)
@@ -838,17 +835,16 @@ def christiansen_integrals_dipole(pes, n_states=16):
local_dipole_cform_onebody = _cform_onemode_dipole(pes, n_states)
comm.Barrier()
- f = h5py.File("cform_D1data" + f"_{rank}" + ".hdf5", "w")
- f.create_dataset("D1", data=local_dipole_cform_onebody)
- f.close()
+ file_path = Path(f"cform_D1data_{rank}.hdf5")
+ with h5py.File(file_path, "w") as f:
+ f.create_dataset("D1", data=local_dipole_cform_onebody)
comm.Barrier()
dipole_cform_onebody = None
if rank == 0:
dipole_cform_onebody = _load_cform_onemode_dipole(size, len(pes.freqs), n_states)
- process = subprocess.Popen("rm " + "cform_D1data*", stdout=subprocess.PIPE, shell=True)
- _, _ = process.communicate()
-
+ for path in Path.cwd().glob("cform_D1data*"):
+ path.unlink()
comm.Barrier()
dipole_cform_onebody = comm.bcast(dipole_cform_onebody, root=0)
@@ -856,16 +852,16 @@ def christiansen_integrals_dipole(pes, n_states=16):
local_dipole_cform_twobody = _cform_twomode_dipole(pes, n_states)
comm.Barrier()
- f = h5py.File("cform_D2data" + f"_{rank}" + ".hdf5", "w")
- f.create_dataset("D2", data=local_dipole_cform_twobody)
- f.close()
+ file_path = Path(f"cform_D2data_{rank}.hdf5")
+ with h5py.File(file_path, "w") as f:
+ f.create_dataset("D2", data=local_dipole_cform_twobody)
comm.Barrier()
dipole_cform_twobody = None
if rank == 0:
dipole_cform_twobody = _load_cform_twomode_dipole(size, len(pes.freqs), n_states)
- process = subprocess.Popen("rm " + "cform_D2data*", stdout=subprocess.PIPE, shell=True)
- _, _ = process.communicate()
+ for path in Path.cwd().glob("cform_D2data*"):
+ path.unlink()
comm.Barrier()
dipole_cform_twobody = comm.bcast(dipole_cform_twobody, root=0)
@@ -873,17 +869,17 @@ def christiansen_integrals_dipole(pes, n_states=16):
local_dipole_cform_threebody = _cform_threemode_dipole(pes, n_states)
comm.Barrier()
- f = h5py.File("cform_D3data" + f"_{rank}" + ".hdf5", "w")
- f.create_dataset("D3", data=local_dipole_cform_threebody)
- f.close()
+ file_path = Path(f"cform_D3data_{rank}.hdf5")
+ with h5py.File(file_path, "w") as f:
+ f.create_dataset("D3", data=local_dipole_cform_threebody)
+ comm.Barrier()
dipole_cform_threebody = None
if rank == 0:
dipole_cform_threebody = _load_cform_threemode_dipole(size, len(pes.freqs), n_states)
- process = subprocess.Popen("rm " + "cform_D3data*", stdout=subprocess.PIPE, shell=True)
- _, _ = process.communicate()
+ for path in Path.cwd().glob("cform_D3data*"):
+ path.unlink()
comm.Barrier()
-
dipole_cform_threebody = comm.bcast(dipole_cform_threebody, root=0)
D_arr = [dipole_cform_onebody, dipole_cform_twobody, dipole_cform_threebody]
diff --git a/pennylane/ops/functions/equal.py b/pennylane/ops/functions/equal.py
index c7afc235b10..f40769a7dc3 100644
--- a/pennylane/ops/functions/equal.py
+++ b/pennylane/ops/functions/equal.py
@@ -754,39 +754,6 @@ def _equal_counts(op1: CountsMP, op2: CountsMP, **kwargs):
return _equal_measurements(op1, op2, **kwargs) and op1.all_outcomes == op2.all_outcomes
-@_equal_dispatch.register
-# pylint: disable=unused-argument
-def _equal_basis_rotation(
- op1: qml.BasisRotation,
- op2: qml.BasisRotation,
- check_interface=True,
- check_trainability=True,
- rtol=1e-5,
- atol=1e-9,
-):
- if not qml.math.allclose(
- op1.hyperparameters["unitary_matrix"],
- op2.hyperparameters["unitary_matrix"],
- atol=atol,
- rtol=rtol,
- ):
- return (
- "The hyperparameter unitary_matrix is not equal for op1 and op2.\n"
- f"Got {op1.hyperparameters['unitary_matrix']}\n and {op2.hyperparameters['unitary_matrix']}."
- )
- if op1.wires != op2.wires:
- return f"op1 and op2 have different wires. Got {op1.wires} and {op2.wires}."
- if check_interface:
- interface1 = qml.math.get_interface(op1.hyperparameters["unitary_matrix"])
- interface2 = qml.math.get_interface(op2.hyperparameters["unitary_matrix"])
- if interface1 != interface2:
- return (
- "The hyperparameter unitary_matrix has different interfaces for op1 and op2."
- f" Got {interface1} and {interface2}."
- )
- return True
-
-
@_equal_dispatch.register
def _equal_hilbert_schmidt(
op1: qml.HilbertSchmidt,
diff --git a/pennylane/qchem/convert_openfermion.py b/pennylane/qchem/convert_openfermion.py
index e00c479b4e4..f0c616554b2 100644
--- a/pennylane/qchem/convert_openfermion.py
+++ b/pennylane/qchem/convert_openfermion.py
@@ -56,7 +56,7 @@ def from_openfermion(openfermion_op, wires=None, tol=1e-16):
tol (float): Tolerance for discarding negligible coefficients.
Returns:
- Union[FermiWord, FermiSentence, LinearCombination]: PennyLane operator.
+ Union[~.FermiWord, ~.FermiSentence, LinearCombination]: PennyLane operator.
**Example**
@@ -113,7 +113,7 @@ def to_openfermion(
`FermionOperator `__.
Args:
- pennylane_op (~ops.op_math.Sum, ~ops.op_math.LinearCombination, FermiWord, FermiSentence):
+ pennylane_op (~ops.op_math.Sum, ~ops.op_math.LinearCombination, ~.FermiWord, ~.FermiSentence):
PennyLane operator
wires (dict): Custom wire mapping used to convert a PennyLane qubit operator
to the external operator.
@@ -126,9 +126,9 @@ def to_openfermion(
**Example**
>>> import pennylane as qml
- >>> w1 = qml.fermi.FermiWord({(0, 0) : '+', (1, 1) : '-'})
- >>> w2 = qml.fermi.FermiWord({(0, 1) : '+', (1, 2) : '-'})
- >>> fermi_s = qml.fermi.FermiSentence({w1 : 1.2, w2: 3.1})
+ >>> w1 = qml.FermiWord({(0, 0) : '+', (1, 1) : '-'})
+ >>> w2 = qml.FermiWord({(0, 1) : '+', (1, 2) : '-'})
+ >>> fermi_s = qml.FermiSentence({w1 : 1.2, w2: 3.1})
>>> of_fermi_op = qml.to_openfermion(fermi_s)
>>> of_fermi_op
1.2 [0^ 1] +
diff --git a/pennylane/qchem/observable_hf.py b/pennylane/qchem/observable_hf.py
index 4ef1fe1f9a3..5002168ff3d 100644
--- a/pennylane/qchem/observable_hf.py
+++ b/pennylane/qchem/observable_hf.py
@@ -32,7 +32,7 @@ def fermionic_observable(constant, one=None, two=None, cutoff=1.0e-12):
cutoff (float): cutoff value for discarding the negligible integrals
Returns:
- FermiSentence: fermionic observable
+ ~.FermiSentence: fermionic observable
**Example**
@@ -98,7 +98,7 @@ def qubit_observable(o_ferm, cutoff=1.0e-12, mapping="jordan_wigner"):
r"""Convert a fermionic observable to a PennyLane qubit observable.
Args:
- o_ferm (Union[FermiWord, FermiSentence]): fermionic operator
+ o_ferm (Union[~.FermiWord, ~.FermiSentence]): fermionic operator
cutoff (float): cutoff value for discarding the negligible terms
mapping (str): Specifies the fermion-to-qubit mapping. Input values can
be ``'jordan_wigner'``, ``'parity'`` or ``'bravyi_kitaev'``.
@@ -107,9 +107,9 @@ def qubit_observable(o_ferm, cutoff=1.0e-12, mapping="jordan_wigner"):
**Example**
- >>> w1 = qml.fermi.FermiWord({(0, 0) : '+', (1, 1) : '-'})
- >>> w2 = qml.fermi.FermiWord({(0, 0) : '+', (1, 1) : '-'})
- >>> s = qml.fermi.FermiSentence({w1 : 1.2, w2: 3.1})
+ >>> w1 = qml.FermiWord({(0, 0) : '+', (1, 1) : '-'})
+ >>> w2 = qml.FermiWord({(0, 0) : '+', (1, 1) : '-'})
+ >>> s = qml.FermiSentence({w1 : 1.2, w2: 3.1})
>>> print(qubit_observable(s))
-0.775j * (Y(0) @ X(1)) + 0.775 * (Y(0) @ Y(1)) + 0.775 * (X(0) @ X(1)) + 0.775j * (X(0) @ Y(1))
"""
diff --git a/pennylane/qchem/structure.py b/pennylane/qchem/structure.py
index b8504af8d9c..02d84c9591b 100644
--- a/pennylane/qchem/structure.py
+++ b/pennylane/qchem/structure.py
@@ -292,9 +292,9 @@ def excitations(electrons, orbitals, delta_sz=0, fermionic=False):
if not fermionic:
return singles, doubles
- fermionic_singles = [qml.fermi.FermiWord({(0, x[0]): "+", (1, x[1]): "-"}) for x in singles]
+ fermionic_singles = [qml.FermiWord({(0, x[0]): "+", (1, x[1]): "-"}) for x in singles]
fermionic_doubles = [
- qml.fermi.FermiWord({(0, x[0]): "+", (1, x[1]): "+", (2, x[2]): "-", (3, x[3]): "-"})
+ qml.FermiWord({(0, x[0]): "+", (1, x[1]): "+", (2, x[2]): "-", (3, x[3]): "-"})
for x in doubles
]
diff --git a/pennylane/templates/subroutines/basis_rotation.py b/pennylane/templates/subroutines/basis_rotation.py
index 832a0684739..f522dcb8b09 100644
--- a/pennylane/templates/subroutines/basis_rotation.py
+++ b/pennylane/templates/subroutines/basis_rotation.py
@@ -107,6 +107,10 @@ def _primitive_bind_call(cls, wires, unitary_matrix, check=False, id=None):
return cls._primitive.bind(*wires, unitary_matrix, check=check, id=id)
+ @classmethod
+ def _unflatten(cls, data, metadata):
+ return cls(wires=metadata[0], unitary_matrix=data[0])
+
def __init__(self, wires, unitary_matrix, check=False, id=None):
M, N = qml.math.shape(unitary_matrix)
@@ -124,19 +128,15 @@ def __init__(self, wires, unitary_matrix, check=False, id=None):
if len(wires) < 2:
raise ValueError(f"This template requires at least two wires, got {len(wires)}")
- self._hyperparameters = {
- "unitary_matrix": unitary_matrix,
- }
-
- super().__init__(wires=wires, id=id)
+ super().__init__(unitary_matrix, wires=wires, id=id)
@property
def num_params(self):
- return 0
+ return 1
@staticmethod
def compute_decomposition(
- wires, unitary_matrix, check=False
+ unitary_matrix, wires, check=False
): # pylint: disable=arguments-differ
r"""Representation of the operator as a product of other operators.
diff --git a/tests/bose/test_binary_mapping.py b/tests/bose/test_binary_mapping.py
index 4f8dff4d591..d9cdb384c38 100644
--- a/tests/bose/test_binary_mapping.py
+++ b/tests/bose/test_binary_mapping.py
@@ -548,7 +548,7 @@ def test_binary_mapping_wiremap(bose_op, wire_map, result):
def test_error_is_raised_for_incompatible_type():
"""Test that an error is raised if the input is not a BoseWord or BoseSentence"""
- with pytest.raises(ValueError, match="bose_operator must be a BoseWord or BoseSentence"):
+ with pytest.raises(TypeError, match="bose_operator must be a BoseWord or BoseSentence"):
binary_mapping(X(0))
diff --git a/tests/bose/test_christiansen_mapping.py b/tests/bose/test_christiansen_mapping.py
index 121d679bfe7..085c45d3ac4 100644
--- a/tests/bose/test_christiansen_mapping.py
+++ b/tests/bose/test_christiansen_mapping.py
@@ -388,5 +388,5 @@ def test_christiansen_mapping_tolerance(bose_op, qubit_op_data, tol):
def test_error_is_raised_for_incompatible_type():
"""Test that an error is raised if the input is not a BoseWord or BoseSentence"""
- with pytest.raises(ValueError, match="bose_operator must be a BoseWord or BoseSentence"):
+ with pytest.raises(TypeError, match="bose_operator must be a BoseWord or BoseSentence"):
christiansen_mapping(X(0))
diff --git a/tests/bose/test_unary_mapping.py b/tests/bose/test_unary_mapping.py
index edebef362ad..51c064fd0ee 100644
--- a/tests/bose/test_unary_mapping.py
+++ b/tests/bose/test_unary_mapping.py
@@ -517,7 +517,7 @@ def test_n_states_error_unary():
def test_error_is_raised_for_incompatible_type():
"""Test that an error is raised if the input is not a BoseWord or BoseSentence"""
- with pytest.raises(ValueError, match="bose_operator must be a BoseWord or BoseSentence"):
+ with pytest.raises(TypeError, match="bose_operator must be a BoseWord or BoseSentence"):
unary_mapping(X(0))
diff --git a/tests/devices/test_legacy_device.py b/tests/devices/test_legacy_device.py
index 671425692d6..bd8d2e7b2df 100644
--- a/tests/devices/test_legacy_device.py
+++ b/tests/devices/test_legacy_device.py
@@ -278,6 +278,20 @@ def test_supports_observable_exception(self, mock_device):
):
dev.supports_observable(operation)
+ @pytest.mark.parametrize("supported_multi_term_obs", ["Hamiltonian", "LinearCombination"])
+ @pytest.mark.parametrize("obs_type", [qml.ops.LinearCombination, qml.Hamiltonian])
+ def test_all_multi_term_obs_supported_linear_combination(
+ self, mock_device, supported_multi_term_obs, obs_type
+ ):
+ """Test that LinearCombination is supported when the device supports either
+ LinearCombination or Hamiltonian."""
+ dev = mock_device()
+ dev.observables = dev.observables + [supported_multi_term_obs]
+
+ obs = obs_type([1.0, 2.0], [qml.Z(0), qml.Z(0)])
+ circuit = qml.tape.QuantumScript([], [qml.expval(obs)])
+ assert dev._all_multi_term_obs_supported(circuit) # pylint: disable=protected-access
+
class TestInternalFunctions: # pylint:disable=too-many-public-methods
"""Test the internal functions of the abstract Device class"""
diff --git a/tests/ops/functions/test_equal.py b/tests/ops/functions/test_equal.py
index 2a6f5270616..3ef0e9be2da 100644
--- a/tests/ops/functions/test_equal.py
+++ b/tests/ops/functions/test_equal.py
@@ -2598,7 +2598,7 @@ def test_different_tolerances_comparison(self, op, other_op):
assert_equal(op, other_op, atol=1e-5)
assert qml.equal(op, other_op, rtol=0, atol=1e-9) is False
- with pytest.raises(AssertionError, match="The hyperparameter unitary_matrix is not equal"):
+ with pytest.raises(AssertionError, match="op1 and op2 have different data"):
assert_equal(op, other_op, rtol=0, atol=1e-9)
@pytest.mark.parametrize("op, other_op", [(op1, op2)])
@@ -2629,7 +2629,7 @@ def test_non_equal_interfaces(self, op):
assert_equal(op, other_op, check_interface=False)
assert qml.equal(op, other_op) is False
- with pytest.raises(AssertionError, match=r"has different interfaces for op1 and op2"):
+ with pytest.raises(AssertionError, match=r"have different interfaces"):
assert_equal(op, other_op)
diff --git a/tests/qchem/openfermion_pyscf_tests/test_convert_openfermion.py b/tests/qchem/openfermion_pyscf_tests/test_convert_openfermion.py
index cb2cc19020f..fa3e1cf7bc0 100644
--- a/tests/qchem/openfermion_pyscf_tests/test_convert_openfermion.py
+++ b/tests/qchem/openfermion_pyscf_tests/test_convert_openfermion.py
@@ -183,7 +183,7 @@ def test_convert_fermionic_type_fw(self):
of_op = openfermion.FermionOperator("2^ 3")
converted_op = qml.qchem.from_openfermion(of_op)
- assert isinstance(converted_op, qml.fermi.FermiWord)
+ assert isinstance(converted_op, qml.FermiWord)
def test_convert_fermionic_type_fs(self):
r"""Test that FermiSentence object is returned when there are multiple
@@ -192,7 +192,7 @@ def test_convert_fermionic_type_fs(self):
of_op = openfermion.FermionOperator("2^ 3") + openfermion.FermionOperator("1^ 2")
converted_op = qml.qchem.from_openfermion(of_op)
- assert isinstance(converted_op, qml.fermi.FermiSentence)
+ assert isinstance(converted_op, qml.FermiSentence)
def test_tol_fermionic(self):
r"""Test that terms with coefficients larger than tolerance are discarded"""
@@ -226,19 +226,17 @@ def test_fail_import_openfermion_fermionic(self, monkeypatch):
class TestToOpenFermion:
FERMI_AND_OF_OPS = (
- ((qml.fermi.FermiWord({(0, 0): "+", (1, 1): "-"})), (openfermion.FermionOperator("0^ 1"))),
+ ((qml.FermiWord({(0, 0): "+", (1, 1): "-"})), (openfermion.FermionOperator("0^ 1"))),
(
- (qml.fermi.FermiWord({(1, 0): "+", (0, 1): "-", (2, 3): "+", (3, 2): "-"})),
+ (qml.FermiWord({(1, 0): "+", (0, 1): "-", (2, 3): "+", (3, 2): "-"})),
(openfermion.FermionOperator("1 0^ 3^ 2")),
),
(
(
- qml.fermi.FermiSentence(
+ qml.FermiSentence(
{
- qml.fermi.FermiWord(
- {(1, 0): "+", (0, 1): "-", (2, 3): "+", (3, 2): "-"}
- ): 0.5,
- qml.fermi.FermiWord({(0, 0): "+", (1, 1): "-"}): 0.3,
+ qml.FermiWord({(1, 0): "+", (0, 1): "-", (2, 3): "+", (3, 2): "-"}): 0.5,
+ qml.FermiWord({(0, 0): "+", (1, 1): "-"}): 0.3,
}
)
),
@@ -272,12 +270,10 @@ def test_conversion(self, fermi_op, of_op):
COMPLEX_OPS = (
(
- qml.fermi.FermiSentence(
+ qml.FermiSentence(
{
- qml.fermi.FermiWord(
- {(1, 0): "+", (0, 1): "-", (2, 3): "+", (3, 2): "-"}
- ): 1e-08j,
- qml.fermi.FermiWord({(0, 0): "+", (1, 1): "-"}): 0.3,
+ qml.FermiWord({(1, 0): "+", (0, 1): "-", (2, 3): "+", (3, 2): "-"}): 1e-08j,
+ qml.FermiWord({(0, 0): "+", (1, 1): "-"}): 0.3,
}
)
),
@@ -339,9 +335,9 @@ def test_invalid_op(self):
)
OPS_FERMI_WIRE = (
- ((qml.fermi.FermiWord({(0, 0): "+", (1, 1): "-"})), ({0: "a", 1: 2})),
+ ((qml.FermiWord({(0, 0): "+", (1, 1): "-"})), ({0: "a", 1: 2})),
(
- (qml.fermi.FermiSentence({qml.fermi.FermiWord({(0, 0): "+", (1, 1): "-"}): 1.2})),
+ (qml.FermiSentence({qml.FermiWord({(0, 0): "+", (1, 1): "-"}): 1.2})),
({0: "a", 1: 2}),
),
)
diff --git a/tests/templates/test_subroutines/test_basis_rotation.py b/tests/templates/test_subroutines/test_basis_rotation.py
index a4328ca5264..0f9f6412a2b 100644
--- a/tests/templates/test_subroutines/test_basis_rotation.py
+++ b/tests/templates/test_subroutines/test_basis_rotation.py
@@ -21,7 +21,6 @@
import pennylane as qml
-@pytest.mark.xfail # to be fixed by [sc-51603]
def test_standard_validity():
"""Run standard tests of operation validity."""
weights = np.array(
@@ -402,8 +401,9 @@ def test_autograd(self, tol):
assert np.allclose(grads, np.zeros_like(unitary_matrix, dtype=complex), atol=tol, rtol=0)
+ @pytest.mark.parametrize("device_name", ("default.qubit", "reference.qubit"))
@pytest.mark.jax
- def test_jax_jit(self, tol):
+ def test_jax_jit(self, device_name, tol):
"""Test the jax interface."""
import jax
@@ -417,7 +417,7 @@ def test_jax_jit(self, tol):
]
)
- dev = qml.device("default.qubit", wires=3)
+ dev = qml.device(device_name, wires=3)
circuit = jax.jit(qml.QNode(circuit_template, dev), static_argnames="check")
circuit2 = qml.QNode(circuit_template, dev)
From 93266aaee7f56f173c8e5e638c42105de3fe32b3 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 15 Jan 2025 03:18:29 +0000
Subject: [PATCH 3/3] Check in artifacts-> Frozen Doc Dependencies Update
---
.github/stable/all_interfaces.txt | 6 +-
.github/stable/core.txt | 4 +-
.github/stable/doc.txt | 199 ++--
.github/stable/external.txt | 16 +-
.github/stable/jax.txt | 6 +-
.github/stable/tf.txt | 6 +-
.github/stable/torch.txt | 4 +-
doc/code/qml_noise.rst | 2 +-
doc/conf.py | 1 +
doc/development/autograph.rst | 10 +-
doc/development/deprecations.rst | 10 -
doc/development/plugins.rst | 18 +-
doc/releases/changelog-0.40.0.md | 1038 ++++++++++++-----
pennylane/_version.py | 2 +-
pennylane/bose/bosonic_mapping.py | 26 +-
pennylane/capture/autograph/transformer.py | 4 +-
pennylane/capture/base_interpreter.py | 29 +-
pennylane/compiler/compiler.py | 2 +-
pennylane/debugging/snapshot.py | 2 +-
pennylane/devices/__init__.py | 1 +
pennylane/devices/_qubit_device.py | 4 +-
pennylane/devices/capabilities.py | 58 +-
pennylane/devices/default_clifford.py | 2 +-
pennylane/devices/default_tensor.py | 16 +-
pennylane/devices/device_api.py | 2 +-
pennylane/devices/execution_config.py | 4 +-
.../devices/qubit_mixed/apply_operation.py | 2 +-
pennylane/devices/qubit_mixed/measure.py | 4 +-
pennylane/devices/qubit_mixed/sampling.py | 6 +-
pennylane/devices/qubit_mixed/simulate.py | 2 +-
pennylane/drawer/mpldrawer.py | 42 +-
pennylane/drawer/tape_mpl.py | 2 +-
pennylane/gradients/vjp.py | 4 +-
pennylane/io.py | 2 +-
pennylane/labs/dla/cartan_subalgebra.py | 2 +-
pennylane/labs/dla/variational_kak.py | 2 +-
.../labs/resource_estimation/__init__.py | 11 +
.../labs/resource_estimation/ops/__init__.py | 4 +
.../labs/resource_estimation/ops/identity.py | 54 +
.../ops/op_math/controlled_ops.py | 268 ++++-
.../ops/qubit/non_parametric_ops.py | 239 +++-
.../ops/qubit/parametric_ops_multi_qubit.py | 295 ++++-
.../ops/qubit/parametric_ops_single_qubit.py | 231 +++-
.../ops/qubit/qchem_ops.py | 149 +++
.../resource_estimation/resource_tracking.py | 1 -
.../ops/op_math/test_controlled_ops.py | 854 +++++++++++++-
.../ops/op_math/test_symbolic.py | 58 +
.../ops/qubit/test_non_parametric_ops.py | 706 ++++++++++-
.../qubit/test_parametric_ops_multi_qubit.py | 245 +++-
.../qubit/test_parametric_ops_single_qubit.py | 336 +++++-
.../ops/qubit/test_qchem_ops.py | 156 +++
.../resource_estimation/ops/test_identity.py | 114 ++
pennylane/measurements/classical_shadow.py | 8 +-
pennylane/ops/functions/dot.py | 2 +-
pennylane/qchem/molecule.py | 10 +-
pennylane/qchem/vibrational/taylor_ham.py | 76 +-
pennylane/qchem/vibrational/vscf.py | 28 +-
pennylane/qcut/cutcircuit.py | 2 +-
pennylane/qcut/cutcircuit_mc.py | 2 +-
pennylane/tape/plxpr_conversion.py | 5 +-
pennylane/tape/qscript.py | 2 +-
pennylane/tape/tape.py | 4 +-
pennylane/templates/embeddings/amplitude.py | 6 +-
.../state_preparations/state_prep_mps.py | 17 +-
.../state_preparations/superposition.py | 9 +-
pennylane/templates/subroutines/adder.py | 2 +-
pennylane/templates/subroutines/gqsp.py | 2 +-
pennylane/templates/subroutines/mod_exp.py | 2 +-
pennylane/templates/subroutines/out_adder.py | 2 +-
.../templates/subroutines/out_multiplier.py | 2 +-
pennylane/templates/subroutines/out_poly.py | 15 +-
.../templates/subroutines/phase_adder.py | 2 +-
pennylane/templates/subroutines/qsvt.py | 8 +-
.../transforms/core/transform_program.py | 4 +-
.../transforms/diagonalize_measurements.py | 12 +-
pennylane/workflow/_capture_qnode.py | 10 +-
pennylane/workflow/execution.py | 14 +-
pennylane/workflow/qnode.py | 6 +-
pennylane/workflow/return_types_spec.rst | 2 +-
setup.py | 2 +-
tests/capture/autograph/test_autograph.py | 2 +-
tests/capture/test_base_interpreter.py | 2 +-
tests/devices/qubit_mixed/conftest.py | 2 +-
tests/drawer/test_draw_mpl.py | 59 +-
tests/drawer/test_drawer_utils.py | 2 +-
tests/drawer/test_mpldrawer.py | 133 ++-
tests/test_debugging.py | 8 +-
tests/test_vqe.py | 18 +-
.../interfaces/test_jacobian_products.py | 2 +-
89 files changed, 4954 insertions(+), 791 deletions(-)
diff --git a/.github/stable/all_interfaces.txt b/.github/stable/all_interfaces.txt
index 85709e2cf5d..b53f5569d44 100644
--- a/.github/stable/all_interfaces.txt
+++ b/.github/stable/all_interfaces.txt
@@ -38,7 +38,7 @@ isort==5.13.2
jax==0.4.28
jaxlib==0.4.28
Jinja2==3.1.5
-keras==3.8.0
+keras==3.7.0
kiwisolver==1.4.8
lazy-object-proxy==1.10.0
libclang==18.1.1
@@ -83,7 +83,7 @@ protobuf==4.25.5
py==1.11.0
py-cpuinfo==9.0.0
pydot==3.0.4
-Pygments==2.19.1
+Pygments==2.19.0
pylint==2.7.4
pyparsing==3.2.1
pytest==8.3.4
@@ -101,7 +101,7 @@ qdldl==0.1.7.post5
requests==2.32.3
rich==13.9.4
rustworkx==0.15.1
-scipy==1.15.1
+scipy==1.15.0
scs==3.2.7.post2
six==1.17.0
smmap==5.0.2
diff --git a/.github/stable/core.txt b/.github/stable/core.txt
index a96ee5cb56b..fc24ed2dff0 100644
--- a/.github/stable/core.txt
+++ b/.github/stable/core.txt
@@ -52,7 +52,7 @@ prompt_toolkit==3.0.48
py==1.11.0
py-cpuinfo==9.0.0
pydot==3.0.4
-Pygments==2.19.1
+Pygments==2.19.0
pylint==2.7.4
pyparsing==3.2.1
pytest==8.3.4
@@ -70,7 +70,7 @@ qdldl==0.1.7.post5
requests==2.32.3
rich==13.9.4
rustworkx==0.15.1
-scipy==1.15.1
+scipy==1.15.0
scs==3.2.7.post2
six==1.17.0
smmap==5.0.2
diff --git a/.github/stable/doc.txt b/.github/stable/doc.txt
index 929ae02c9ae..05d5cb80baf 100644
--- a/.github/stable/doc.txt
+++ b/.github/stable/doc.txt
@@ -1,121 +1,82 @@
-absl-py==2.1.0
-aiohappyeyeballs==2.4.3
-aiohttp==3.10.10
-aiosignal==1.3.1
-alabaster==0.7.16
-appdirs==1.4.4
-astunparse==1.6.3
-async-timeout==4.0.3
-attrs==24.2.0
-autograd==1.7.0
-autoray==0.6.12
-babel==2.16.0
-cachetools==5.5.0
-certifi==2024.8.30
-charset-normalizer==3.4.0
-cirq-core==1.3.0
-contourpy==1.3.0
-cycler==0.12.1
-deprecation==2.1.0
-docutils==0.16
-duet==0.2.9
-exceptiongroup==1.2.2
-flatbuffers==24.3.25
-fonttools==4.54.1
-frozenlist==1.4.1
-fsspec==2024.9.0
-gast==0.4.0
-google-auth==2.35.0
-google-auth-oauthlib==0.4.6
-google-pasta==0.2.0
-graphviz==0.20.3
-grpcio==1.67.0
-h5py==3.12.1
-idna==3.10
-imagesize==1.4.1
-importlib_metadata==8.5.0
-importlib_resources==6.4.5
-iniconfig==2.0.0
-jax==0.4.16
-jaxlib==0.4.16
-Jinja2==3.0.3
-keras==2.11.0
-kiwisolver==1.4.7
-latexcodec==3.0.0
-libclang==18.1.1
-m2r2==0.3.2
-Markdown==3.7
-MarkupSafe==3.0.1
-matplotlib==3.8.0
-mistune==0.8.4
-ml_dtypes==0.5.0
-mpmath==1.3.0
-multidict==6.1.0
-networkx==2.6
-numpy==1.26.4
+argcomplete==3.5.3
+attrs==23.2.0
+Automat==22.10.0
+Babel==2.10.3
+bcc==0.29.1
+bcrypt==3.2.2
+blinker==1.7.0
+boto3==1.34.46
+botocore==1.34.46
+certifi==2023.11.17
+chardet==5.2.0
+click==8.1.6
+cloud-init==24.3.1
+colorama==0.4.6
+command-not-found==0.3
+configobj==5.0.8
+constantly==23.10.4
+cryptography==41.0.7
+dbus-python==1.3.2
+distro==1.9.0
+distro-info==1.7+build1
+httplib2==0.20.4
+hyperlink==21.0.0
+idna==3.6
+incremental==22.10.0
+Jinja2==3.1.2
+jmespath==1.0.1
+jsonpatch==1.32
+jsonpointer==2.0
+jsonschema==4.10.3
+launchpadlib==1.11.0
+lazr.restfulclient==0.14.6
+lazr.uri==1.0.6
+markdown-it-py==3.0.0
+MarkupSafe==2.1.5
+mdurl==0.1.2
+mercurial==6.7.2
+netaddr==0.8.0
+netifaces==0.11.0
oauthlib==3.2.2
-openfermion==1.6.1
-openfermionpyscf==0.5
-opt_einsum==3.4.0
-packaging==24.1
-pandas==2.2.3
-pennylane-sphinx-theme==0.5.8
-PennyLane_Lightning==0.38.0
-pillow==11.0.0
-pluggy==1.5.0
-propcache==0.2.0
-protobuf==3.19.6
-PubChemPy==1.0.4
-pyasn1==0.6.1
-pyasn1_modules==0.4.1
-pybtex==0.24.0
-pybtex-docutils==1.0.3
-Pygments==2.18.0
-pygments-github-lexers==0.0.5
-pyparsing==3.2.0
-pyscf==2.7.0
-pytest==8.3.3
-python-dateutil==2.9.0.post0
-pytz==2024.2
-PyYAML==6.0.2
-requests==2.28.2
-requests-oauthlib==2.0.0
-rsa==4.9
-rustworkx==0.15.1
-scipy==1.13.1
+packaging==24.0
+pexpect==4.9.0
+pipx==1.7.1
+platformdirs==4.3.6
+ptyprocess==0.7.0
+pyasn1==0.4.8
+pyasn1-modules==0.2.8
+Pygments==2.17.2
+PyGObject==3.48.2
+PyHamcrest==2.1.0
+PyJWT==2.7.0
+pyOpenSSL==23.2.0
+pyparsing==3.1.1
+pyparted==3.12.0
+pyrsistent==0.20.0
+pyserial==3.5
+python-apt==2.7.7+ubuntu3
+python-dateutil==2.8.2
+python-debian==0.1.49+ubuntu2
+python-magic==0.4.27
+pytz==2024.1
+PyYAML==6.0.1
+requests==2.31.0
+rich==13.7.1
+s3transfer==0.10.1
+service-identity==24.1.0
+setuptools==68.1.2
six==1.16.0
-snowballstemmer==2.2.0
-sortedcontainers==2.4.0
-Sphinx==3.5.4
-sphinx-automodapi==0.13
-sphinx-copybutton==0.5.2
-sphinx-gallery==0.12.2
-sphinxcontrib-applehelp==1.0.4
-sphinxcontrib-bibtex==2.4.2
-sphinxcontrib-devhelp==1.0.2
-sphinxcontrib-htmlhelp==2.0.1
-sphinxcontrib-jsmath==1.0.1
-sphinxcontrib-qthelp==1.0.3
-sphinxcontrib-serializinghtml==1.1.5
-sphinxext-opengraph==0.6.3
-sympy==1.13.3
-tensorboard==2.11.2
-tensorboard-data-server==0.6.1
-tensorboard-plugin-wit==1.8.1
-tensorflow==2.11.1
-tensorflow-estimator==2.11.0
-tensorflow-io-gcs-filesystem==0.37.1
-tensornetwork==0.3.0
-termcolor==2.5.0
-toml==0.10.2
-tomli==2.0.2
-torch==1.9.0+cpu
-tqdm==4.66.5
-typing_extensions==4.12.2
-tzdata==2024.2
-urllib3==1.26.20
-Werkzeug==3.0.4
-wrapt==1.16.0
-xanadu-sphinx-theme==0.5.0
-yarl==1.15.4
-zipp==3.20.2
+sos==4.7.2
+ssh-import-id==5.11
+systemd-python==235
+Twisted==24.3.0
+typing_extensions==4.10.0
+ubuntu-pro-client==8001
+ufw==0.36.2
+urllib3==2.0.7
+userpath==1.9.2
+wadllib==1.3.6
+WALinuxAgent==2.9.1.1
+wheel==0.42.0
+zope.interface==6.1
+zstandard==0.22.0
diff --git a/.github/stable/external.txt b/.github/stable/external.txt
index 8fea222615a..5cb949a78d7 100644
--- a/.github/stable/external.txt
+++ b/.github/stable/external.txt
@@ -27,7 +27,7 @@ clarabel==0.9.0
click==8.1.8
comm==0.2.2
contourpy==1.3.1
-cotengra==0.7.0
+cotengra==0.6.2
coverage==7.6.10
cryptography==44.0.0
cvxopt==1.3.2
@@ -60,8 +60,8 @@ h11==0.14.0
h5py==3.12.1
httpcore==1.0.7
httpx==0.28.1
-ibm-cloud-sdk-core==3.22.1
-ibm-platform-services==0.59.1
+ibm-cloud-sdk-core==3.22.0
+ibm-platform-services==0.59.0
identify==2.6.5
idna==3.10
iniconfig==2.0.0
@@ -89,7 +89,7 @@ jupyterlab==4.3.4
jupyterlab_pygments==0.3.0
jupyterlab_server==2.27.3
jupyterlab_widgets==1.1.11
-keras==3.8.0
+keras==3.7.0
kiwisolver==1.4.8
lark==1.1.9
lazy-object-proxy==1.10.0
@@ -129,8 +129,8 @@ pandocfilters==1.5.1
parso==0.8.4
pathspec==0.12.1
pbr==6.1.0
-PennyLane-Catalyst==0.11.0.dev1
-PennyLane-qiskit @ git+https://github.com/PennyLaneAI/pennylane-qiskit.git@927bab3fc2dfdbac9d57999f2d7e9761a6254491
+PennyLane-Catalyst==0.10.0.dev39
+PennyLane-qiskit @ git+https://github.com/PennyLaneAI/pennylane-qiskit.git@40a4d24f126e51e0e3e28a4cd737f883a6fd5ebc
PennyLane_Lightning==0.40.0
PennyLane_Lightning_Kokkos==0.40.0.dev41
pexpect==4.9.0
@@ -148,10 +148,10 @@ pure_eval==0.2.3
py==1.11.0
py-cpuinfo==9.0.0
pycparser==2.22
-pydantic==2.10.5
+pydantic==2.10.4
pydantic_core==2.27.2
pydot==3.0.4
-Pygments==2.19.1
+Pygments==2.19.0
PyJWT==2.10.1
pylint==2.7.4
pyparsing==3.2.1
diff --git a/.github/stable/jax.txt b/.github/stable/jax.txt
index 1cfd4d83cd7..ed2c0b32037 100644
--- a/.github/stable/jax.txt
+++ b/.github/stable/jax.txt
@@ -37,7 +37,7 @@ markdown-it-py==3.0.0
matplotlib==3.10.0
mccabe==0.6.1
mdurl==0.1.2
-ml_dtypes==0.5.1
+ml_dtypes==0.5.0
mypy-extensions==1.0.0
networkx==3.4.2
nodeenv==1.9.1
@@ -56,7 +56,7 @@ prompt_toolkit==3.0.48
py==1.11.0
py-cpuinfo==9.0.0
pydot==3.0.4
-Pygments==2.19.1
+Pygments==2.19.0
pylint==2.7.4
pyparsing==3.2.1
pytest==8.3.4
@@ -74,7 +74,7 @@ qdldl==0.1.7.post5
requests==2.32.3
rich==13.9.4
rustworkx==0.15.1
-scipy==1.15.1
+scipy==1.15.0
scs==3.2.7.post2
six==1.17.0
smmap==5.0.2
diff --git a/.github/stable/tf.txt b/.github/stable/tf.txt
index 09fb2f13095..634fbd09526 100644
--- a/.github/stable/tf.txt
+++ b/.github/stable/tf.txt
@@ -34,7 +34,7 @@ identify==2.6.5
idna==3.10
iniconfig==2.0.0
isort==5.13.2
-keras==3.8.0
+keras==3.7.0
kiwisolver==1.4.8
lazy-object-proxy==1.10.0
libclang==18.1.1
@@ -66,7 +66,7 @@ protobuf==4.25.5
py==1.11.0
py-cpuinfo==9.0.0
pydot==3.0.4
-Pygments==2.19.1
+Pygments==2.19.0
pylint==2.7.4
pyparsing==3.2.1
pytest==8.3.4
@@ -84,7 +84,7 @@ qdldl==0.1.7.post5
requests==2.32.3
rich==13.9.4
rustworkx==0.15.1
-scipy==1.15.1
+scipy==1.15.0
scs==3.2.7.post2
six==1.17.0
smmap==5.0.2
diff --git a/.github/stable/torch.txt b/.github/stable/torch.txt
index 3b1ca9abc78..3ab4c0e93f2 100644
--- a/.github/stable/torch.txt
+++ b/.github/stable/torch.txt
@@ -68,7 +68,7 @@ prompt_toolkit==3.0.48
py==1.11.0
py-cpuinfo==9.0.0
pydot==3.0.4
-Pygments==2.19.1
+Pygments==2.19.0
pylint==2.7.4
pyparsing==3.2.1
pytest==8.3.4
@@ -86,7 +86,7 @@ qdldl==0.1.7.post5
requests==2.32.3
rich==13.9.4
rustworkx==0.15.1
-scipy==1.15.1
+scipy==1.15.0
scs==3.2.7.post2
six==1.17.0
smmap==5.0.2
diff --git a/doc/code/qml_noise.rst b/doc/code/qml_noise.rst
index f8c30d7bdca..adba65378f6 100644
--- a/doc/code/qml_noise.rst
+++ b/doc/code/qml_noise.rst
@@ -22,7 +22,7 @@ noise-related metadata can also be supplied to construct a noise model using:
~NoiseModel
Each conditional in the ``model_map`` (and ``meas_map``) evaluates the gate operations
-(and terminal measurments) in the quantum circuit based on some condition of its attributes
+(and terminal measurements) in the quantum circuit based on some condition of its attributes
(e.g., type, parameters, wires, etc.) and uses the corresponding callable to apply the
noise operations, using the user-provided metadata (e.g., hardware topologies or relaxation
times), whenever the condition is true. A noise model, once built, can be attached
diff --git a/doc/conf.py b/doc/conf.py
index 4b9bcf4bbb8..d8cac44db9e 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -260,6 +260,7 @@
"TensorFlow, the TensorFlow logo, and any related marks are trademarks " "of Google Inc."
],
"google_analytics_tracking_id": "G-C480Z9JL0D",
+ "search_on_pennylane_ai": True,
}
edit_on_github_project = "PennyLaneAI/pennylane"
diff --git a/doc/development/autograph.rst b/doc/development/autograph.rst
index 9fc376aff0f..e4455b3d25e 100644
--- a/doc/development/autograph.rst
+++ b/doc/development/autograph.rst
@@ -80,7 +80,7 @@ Python control flow:
return qml.expval(qml.PauliZ(0) + qml.PauliZ(3))
-While this function cannot be captured directly because there is control flow that depends on the function's inputs' values—the inputs are treated as JAX tracers at capture time, which don't have concrete values—it can be captured by converting to native PennyLane syntax
+While this function cannot be captured directly because there is control flow that depends on the values of the function's inputs (the inputs are treated as JAX tracers at capture time, which don't have concrete values) it can be captured by converting to native PennyLane syntax
via AutoGraph. This is the default behaviour of :func:`~.autograph.make_plxpr`.
>>> weights = jnp.linspace(-1, 1, 20).reshape([5, 4])
@@ -351,7 +351,7 @@ For example, using a ``for`` loop with static bounds to index a JAX array is str
However, indexing within a ``for`` loop with AutoGraph will require that the object indexed is
a JAX array or dynamic runtime variable.
-If the array you are indexing within the for loop is not a JAX array
+If the array you are indexing within the ``for`` loop is not a JAX array
or dynamic variable, an error will be raised:
>>> @qml.qnode(dev)
@@ -378,7 +378,7 @@ a JAX array:
>>> eval_jaxpr(plxpr.jaxpr, plxpr.consts)
[Array(0.99500417, dtype=float64)]
-If the object you are indexing **cannot** be converted to a JAX array, it is not possible for AutoGraph to capture this for loop.
+If the object you are indexing **cannot** be converted to a JAX array, it is not possible for AutoGraph to capture this ``for`` loop.
If you are updating elements of the array, this must be done using the JAX ``.at`` and ``.set`` syntax.
@@ -440,7 +440,7 @@ However, like with conditionals, a similar restriction applies: variables
which are updated across iterations of the loop must have a JAX compilable
type (Booleans, Python numeric types, and JAX arrays).
-You can also utilize temporary variables within a for loop:
+You can also utilize temporary variables within a ``for`` loop:
>>> def f(x):
... for y in [0, 4, 5]:
@@ -520,7 +520,7 @@ To allow AutoGraph conversion to work in this case, simply convert the list to a
>>> eval_jaxpr(plxpr.jaxpr, plxpr.consts)
[Array(0.99500417, dtype=float64)]
-If the object you are indexing **cannot** be converted to a JAX array, it is not possible for AutoGraph to capture this for loop.
+If the object you are indexing **cannot** be converted to a JAX array, it is not possible for AutoGraph to capture this ``while`` loop.
If you are updating elements of the array, this must be done using the JAX ``.at`` and ``.set`` syntax.
diff --git a/doc/development/deprecations.rst b/doc/development/deprecations.rst
index 5e8f02fe5da..fd1c5051e25 100644
--- a/doc/development/deprecations.rst
+++ b/doc/development/deprecations.rst
@@ -47,15 +47,6 @@ Pending deprecations
- Deprecated in v0.40
- Will be removed in v0.41
- - Deprecated in v0.39
- - Will be removed in v0.40
-
-* The ``QubitStateVector`` template is deprecated.
- Instead, use ``StatePrep``.
-
- - Deprecated in v0.39
- - Will be removed in v0.40
-
* ``op.ops`` and ``op.coeffs`` for ``Sum`` and ``Prod`` will be removed in the future. Use
:meth:`~.Operator.terms` instead.
@@ -70,7 +61,6 @@ Pending deprecations
values with a bit string. In the future, it will no longer accepts strings as control values.
- Deprecated in v0.36
- - Will be removed in v0.37
Completed removal of legacy operator arithmetic
-----------------------------------------------
diff --git a/doc/development/plugins.rst b/doc/development/plugins.rst
index ac9574d466e..4a15f2c7519 100644
--- a/doc/development/plugins.rst
+++ b/doc/development/plugins.rst
@@ -62,7 +62,7 @@ For example:
This execute method works in tandem with the optional :meth:`Device.preprocess_transforms `
and :meth:`Device.setup_execution_config`, described below in more detail. Preprocessing transforms
-turns generic circuits into ones supported by the device, or raises an error if the circuit is invalid.
+turns generic circuits into ones supported by the device or raises an error if the circuit is invalid.
Execution produces numerical results from those supported circuits.
In a more minimal example, for any initial batch of quantum tapes and a config object, we expect to be able to do:
@@ -376,7 +376,7 @@ Wires
Devices can now either:
1) Strictly use wires provided by the user on initialization: ``device(name, wires=wires)``
-2) Infer the number and ordering of wires provided by the submitted circuit.
+2) Infer the number and order of wires provided by the submitted circuit
3) Strictly require specific wire labels
Option 2 allows workflows to change the number and labeling of wires over time, but sometimes users want
@@ -434,18 +434,20 @@ The execution config stores two kinds of information:
Device options are any device specific options used to configure the behavior of an execution. For
example, ``default.qubit`` has ``max_workers``, ``rng``, and ``prng_key``. ``default.tensor`` has
-``contract``, ``cutoff``, ``dtype``, ``method``, and ``max_bond_dim``. These options are often set
+``contract``, ``contraction_optimizer``, ``cutoff``, ``c_dtype``, ``local_simplify``, ``method``, and ``max_bond_dim``. These options are often set
with default values on initialization. These values should be placed into the ``ExecutionConfig.device_options``
dictionary in :meth:`~.devices.Device.setup_execution_config`. Note that we do provide a default
implementation of this method, but you will most likely need to override it yourself.
->>> dev = qml.device('default.tensor', wires=2, max_bond_dim=4, contract="nonlocal", dtype=np.complex64)
+>>> dev = qml.device('default.tensor', wires=2, max_bond_dim=4, contract="nonlocal", c_dtype=np.complex64)
>>> dev.setup_execution_config().device_options
{'contract': 'nonlocal',
- 'cutoff': 1.1920929e-07,
- 'dtype': numpy.complex64,
- 'method': 'mps',
- 'max_bond_dim': 4}
+ 'contraction_optimizer': 'auto-hq',
+ 'cutoff': None,
+ 'c_dtype': numpy.complex64,
+ 'local_simplify': 'ADCRS',
+ 'max_bond_dim': 4,
+ 'method': 'mps'}
Even if the property is stored as an attribute on the device, execution should pull the value of
these properties from the config instead of from the device instance. While not yet integrated at
diff --git a/doc/releases/changelog-0.40.0.md b/doc/releases/changelog-0.40.0.md
index 7d9707882e6..bb9cb7cd3bc 100644
--- a/doc/releases/changelog-0.40.0.md
+++ b/doc/releases/changelog-0.40.0.md
@@ -6,61 +6,215 @@
Efficient state preparation methods 🦾
-* Added new ``MPSPrep`` template to prepare quantum states in tensor simulators.
+* State preparation tailored for matrix product states (MPS) is now supported with
+ :class:`qml.MPSPrep ` on the `lightning.tensor` device.
[(#6431)](https://github.com/PennyLaneAI/pennylane/pull/6431)
-* Users can prepare a linear combination of basis states using `qml.Superposition`.
+ Given a list of :math:`n` tensors that represents an MPS, :math:`[A^{(0)}, ..., A^{(n-1)}]`,
+ :class:`qml.MPSPrep ` lets you directly inject the MPS into a QNode as the initial
+ state of the circuit without any need for pre-processing. The first and last tensors in the list
+ must be rank-2, while all intermediate tensors should be rank-3.
+
+ ```python
+ import pennylane as qml
+ import numpy as np
+
+ mps = [
+ np.array([[0.0, 0.107], [0.994, 0.0]]),
+ np.array(
+ [
+ [[0.0, 0.0, 0.0, -0.0], [1.0, 0.0, 0.0, -0.0]],
+ [[0.0, 1.0, 0.0, -0.0], [0.0, 0.0, 0.0, -0.0]],
+ ]
+ ),
+ np.array(
+ [
+ [[-1.0, 0.0], [0.0, 0.0]],
+ [[0.0, 0.0], [0.0, 1.0]],
+ [[0.0, -1.0], [0.0, 0.0]],
+ [[0.0, 0.0], [1.0, 0.0]],
+ ]
+ ),
+ np.array([[-1.0, -0.0], [-0.0, -1.0]]),
+ ]
+
+ dev = qml.device("lightning.tensor", wires = [0, 1, 2, 3])
+ @qml.qnode(dev)
+ def circuit():
+ qml.MPSPrep(mps, wires = [0,1,2])
+ return qml.state()
+ ```
+
+ ```pycon
+ >>> print(circuit())
+ [ 0. +0.j 0. +0.j 0. +0.j -0.1066+0.j 0. +0.j 0. +0.j
+ 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j
+ 0.9943+0.j 0. +0.j 0. +0.j 0. +0.j]
+ ```
+
+ At this time, :class:`qml.MPSPrep ` is only supported on the `lightning.tensor` device.
+
+* Custom-made state preparation for linear combinations of quantum states is now available with
+ :class:`qml.Superposition `.
[(#6670)](https://github.com/PennyLaneAI/pennylane/pull/6670)
+ Given a list of :math:`m` coefficients :math:`c_i` and basic states :math:`|b_i\rangle`,
+ :class:`qml.Superposition ` prepares
+ :math:`|\phi\rangle = \sum_i^m c_i |b_i\rangle`. Here is a simple example showing how to use
+ :class:`qml.Superposition ` to prepare
+ :math:`\tfrac{1}{\sqrt{2}} |00\rangle + \tfrac{1}{\sqrt{2}} |10\rangle`.
+
```python
coeffs = np.array([0.70710678, 0.70710678])
basis = np.array([[0, 0], [1, 0]])
- @qml.qnode(qml.device('default.qubit', wires=2))
+ @qml.qnode(qml.device('default.qubit'))
def circuit():
- qml.Superposition(coeffs, basis)
+ qml.Superposition(coeffs, basis, wires=[0, 1], work_wire=[2])
return qml.state()
```
+
```
>>> circuit()
- tensor([0.70710678+0.j, 0.+0.j, 0.70710678+0.j, 0.+0.j], requires_grad=True)
+ Array([0.7071068 +0.j, 0. +0.j, 0. +0.j, 0. +0.j,
+ 0.70710677+0.j, 0. +0.j, 0. +0.j, 0. +0.j], dtype=complex64)
```
- This template is also JAX-jit compatible!
+ Note that specification of one `work_wire` is required.
Enhanced QSVT functionality 🤩
-* New functionality to calculate angles for QSP and QSVT has been added. This includes the function `qml.poly_to_angles`
- to obtain angles directly and the function `qml.transform_angles` to convert angles from one subroutine to another.
+* New functionality to calculate and convert phase angles for QSP and QSVT has been added
+ with :func:`qml.poly_to_angles ` and
+ :func:`qml.transform_angles `.
[(#6483)](https://github.com/PennyLaneAI/pennylane/pull/6483)
-* The `qml.qsvt` function has been improved to be more user-friendly. Old functionality is moved to `qml.qsvt_legacy`
- and it will be deprecated in release v0.40.
- [(#6520)](https://github.com/PennyLaneAI/pennylane/pull/6520/)
+ The :func:`qml.poly_to_angles ` function calculates phase angles
+ directly given polynomial coefficients and the routine in which the angles will be used (`"QSVT"`,
+ `"QSP"`, or `"GQSP"`):
+ ```pycon
+ >>> poly = [0, 1.0, 0, -1/2, 0, 1/3]
+ >>> qsvt_angles = qml.poly_to_angles(poly, "QSVT")
+ >>> print(qsvt_angles)
+ [-5.49778714 1.57079633 1.57079633 0.5833829 1.61095884 0.74753829]
+ ```
+
+ The :func:`qml.transform_angles ` function can be used to convert
+ angles from one routine to another:
+ ```pycon
+ >>> qsp_angles = np.array([0.2, 0.3, 0.5])
+ >>> qsvt_angles = qml.transform_angles(qsp_angles, "QSP", "QSVT")
+ >>> print(qsvt_angles)
+ [-6.86858347 1.87079633 -0.28539816]
+ ```
+
+* The :func:`qml.qsvt ` function has been improved to be more user-friendly,
+with enhanced capabilities.
+ [(#6520)](https://github.com/PennyLaneAI/pennylane/pull/6520)
[(#6693)](https://github.com/PennyLaneAI/pennylane/pull/6693)
-* New `qml.GQSP` template has been added to perform Generalized Quantum Signal Processing (GQSP).
- The functionality `qml.poly_to_angles` has been also extended to support GQSP.
+ Block encoding and phase angle computation are now handled automatically,
+ given a matrix to encode, polynomial coefficients, and a block encoding method
+ (`"prepselprep"`, `"qubitization"`, `"embedding"`, or `"fable"`, all implemented with their
+ corresponding operators in PennyLane).
+ ```python
+ # P(x) = -x + 0.5 x^3 + 0.5 x^5
+ poly = np.array([0, -1, 0, 0.5, 0, 0.5])
+ hamiltonian = qml.dot([0.3, 0.7], [qml.Z(1), qml.X(1) @ qml.Z(2)])
+
+ dev = qml.device("default.qubit")
+ @qml.qnode(dev)
+ def circuit():
+ qml.qsvt(hamiltonian, poly, encoding_wires=[0], block_encoding="prepselprep")
+ return qml.state()
+
+ matrix = qml.matrix(circuit, wire_order=[0, 1, 2])()
+ ```
+ ```pycon
+ >>> print(matrix[:4, :4].real)
+ [[-0.1625 0. -0.3793 0. ]
+ [ 0. -0.1625 0. 0.3793]
+ [-0.3793 0. 0.1625 0. ]
+ [ 0. 0.3793 0. 0.1625]]
+ ```
+ The old functionality can still be accessed with :func:`qml.qsvt_legacy `.
+
+* A new :class:`qml.GQSP ` template has been added to perform Generalized Quantum Signal Processing (GQSP).
[(#6565)](https://github.com/PennyLaneAI/pennylane/pull/6565)
+ Similar to QSVT, GQSP is an algorithm that polynomially transforms an input unitary operator,
+ but with fewer restrictions on the chosen polynomial.
+
+ You can also use :func:`qml.poly_to_angles ` to obtain angles for GQSP!
+
+ ```python
+ # P(x) = 0.1 + 0.2j x + 0.3 x^2
+ poly = [0.1, 0.2j, 0.3]
+ angles = qml.poly_to_angles(poly, "GQSP")
+
+ @qml.prod # transforms the qfunc into an Operator
+ def unitary(wires):
+ qml.RX(0.3, wires)
+
+ dev = qml.device("default.qubit")
+ @qml.qnode(dev)
+ def circuit(angles):
+ qml.GQSP(unitary(wires = 1), angles, control = 0)
+ return qml.state()
+
+ matrix = qml.matrix(circuit, wire_order=[0, 1])(angles)
+ ```
+ ```pycon
+ >>> print(np.round(matrix,3)[:2, :2])
+ [[0.387+0.198j 0.03 -0.089j]
+ [0.03 -0.089j 0.387+0.198j]]
+ ```
Generalized Trotter products 🐖
-* Added a function `qml.trotterize` to generalize the Suzuki-Trotter product to arbitrary quantum functions.
+* Trotter products that work on exponentiated operators directly instead of full system hamiltonians
+ can now be encoded into circuits with the addition of
+ :class:`qml.TrotterizedQfunc ` and :func:`qml.trotterize `. This
+ allows for custom specification of the first-order expansion of the Suzuki-Trotter product formula
+ and extrapolating it to the :math:`n^{\text{th}}` order.
[(#6627)](https://github.com/PennyLaneAI/pennylane/pull/6627)
+ If the first-order of the Suzuki-Trotter product formula for a given problem is known,
+ :class:`qml.TrotterizedQfunc ` and :func:`qml.trotterize `
+ let you implement the :math:`n^{\text{th}}`-order product formula while only specifying the
+ first-order term as a quantum function.
+
```python
def my_custom_first_order_expansion(time, theta, phi, wires, flip):
- "This is the first order expansion (U_1)."
- qml.RX(time*theta, wires[0])
- qml.RY(time*phi, wires[1])
+ qml.RX(time * theta, wires[0])
+ qml.RY(time * phi, wires[1])
if flip:
qml.CNOT(wires=wires[:2])
+ ```
+
+ :func:`qml.trotterize ` requires the quantum function representing the first-order
+ product formula, the number of Trotter steps, and the desired order. It returns a function with
+ the same call signature as the first-order product formula quantum function:
+ ```python
@qml.qnode(qml.device("default.qubit"))
- def my_circuit(time, angles, num_trotter_steps):
- TrotterizedQfunc(
+ def my_circuit(time, theta, phi, num_trotter_steps):
+ qml.trotterize(
+ first_order_expansion,
+ n=num_trotter_steps,
+ order=2,
+ )(time, theta, phi, wires=['a', 'b'], flip=True)
+ return qml.state()
+ ```
+
+ Alternatively, :class:`qml.TrotterizedQfunc ` can be used as follows:
+
+ ```python
+ @qml.qnode(qml.device("default.qubit"))
+ def my_circuit(time, theta, phi, num_trotter_steps):
+ qml.TrotterizedQfunc(
time,
- *angles,
+ theta,
+ phi,
qfunc=my_custom_first_order_expansion,
n=num_trotter_steps,
order=2,
@@ -69,47 +223,381 @@
)
return qml.state()
```
+
```pycon
>>> time = 0.1
- >>> angles = (0.12, -3.45)
- >>> print(qml.draw(my_circuit, level=3)(time, angles, num_trotter_steps=1))
+ >>> theta, phi = (0.12, -3.45)
+ >>> print(qml.draw(my_circuit, level="device")(time, theta, phi, num_trotter_steps=1))
a: ──RX(0.01)──╭●─╭●──RX(0.01)──┤ State
b: ──RY(-0.17)─╰X─╰X──RY(-0.17)─┤ State
```
+ Both methods produce the same results, but offer different interfaces based on the application or overall
+ preference.
+
Bosonic operators 🎈
-* Added `unary_mapping()` function to map `BoseWord` and `BoseSentence` to qubit operators, using unary mapping.
- [(#6576)](https://github.com/PennyLaneAI/pennylane/pull/6576)
+A new module, :mod:`qml.bose `, has been added to PennyLane that includes support
+for constructing and manipulating Bosonic operators and converting between Bosonic operators and
+qubit operators.
-* Added `binary_mapping()` function to map `BoseWord` and `BoseSentence` to qubit operators, using standard-binary mapping.
- [(#6564)](https://github.com/PennyLaneAI/pennylane/pull/6564)
+* Bosonic operators analogous to `qml.FermiWord` and `qml.FermiSentence` are now available with
+ :class:`qml.BoseWord ` and :class:`qml.BoseSentence `.
+ [(#6518)](https://github.com/PennyLaneAI/pennylane/pull/6518)
+
+ :class:`qml.BoseWord ` and :class:`qml.BoseSentence `
+ operate similarly to their fermionic counterparts. To create a Bose word, a dictionary
+ is required as input, where the keys are tuples of boson indices and values are `'+/-'` (denoting
+ the bosonic creation/annihilation operators). For example, the :math:`b^{\dagger}_0 b_1` can be
+ constructed as follows.
+
+ ```pycon
+ >>> w = qml.BoseWord({(0, 0) : '+', (1, 1) : '-'})
+ >>> print(w)
+ b⁺(0) b(1)
+ ```
-* Added `christiansen_mapping()` function to map `BoseWord` and `BoseSentence` to qubit operators, using christiansen mapping.
+ Multiple Bose words can then be combined to form a Bose sentence:
+
+ ```pycon
+ >>> w1 = qml.BoseWord({(0, 0) : '+', (1, 1) : '-'})
+ >>> w2 = qml.BoseWord({(0, 1) : '+', (1, 2) : '-'})
+ >>> s = qml.BoseSentence({w1 : 1.2, w2: 3.1})
+ >>> print(s)
+ 1.2 * b⁺(0) b(1)
+ + 3.1 * b⁺(1) b(2)
+ ```
+
+* Functionality for converting bosonic operators to qubit operators is available with
+ :func:`qml.unary_mapping `, :func:`qml.binary_mapping `,
+ and :func:`qml.christiansen_mapping `.
[(#6623)](https://github.com/PennyLaneAI/pennylane/pull/6623)
+ [(#6576)](https://github.com/PennyLaneAI/pennylane/pull/6576)
+ [(#6564)](https://github.com/PennyLaneAI/pennylane/pull/6564)
-* Added support for constructing `BoseWord` and `BoseSentence`, similar to `FermiWord` and `FermiSentence`.
- [(#6518)](https://github.com/PennyLaneAI/pennylane/pull/6518)
+ All three mappings follow the same syntax, where a :class:`qml.BoseWord ` or
+ :class:`qml.BoseSentence ` is required as input.
+
+ ```python
+ >>> w = qml.BoseWord({(0, 0): "+"})
+ >>> qml.binary_mapping(w, n_states=4)
+ 0.6830127018922193 * X(0)
+ + -0.1830127018922193 * X(0) @ Z(1)
+ + -0.6830127018922193j * Y(0)
+ + 0.1830127018922193j * Y(0) @ Z(1)
+ + 0.3535533905932738 * X(0) @ X(1)
+ + -0.3535533905932738j * X(0) @ Y(1)
+ + 0.3535533905932738j * Y(0) @ X(1)
+ + (0.3535533905932738+0j) * Y(0) @ Y(1)
+ ```
+
+ Additional fine-tuning is available within each function, such as the maximum number of allowed
+ bosonic states and a tolerance for discarding imaginary parts of the coefficients.
-Construct vibrational Hamiltonians 🫨
+Construct vibrational Hamiltonians 🔨
-* Added submodule for calculating vibrational Hamiltonians
- * Implemented helper functions for geometry optimization, harmonic analysis,
- and normal-mode localization.
+* Several new features are available in the :mod:`qml.qchem ` module to help with
+ the construction of vibrational Hamiltonians. This includes:
+
+ * The :class:`~.qchem.VibrationalPES` class to store potential energy surface information.
+ [(#6652)](https://github.com/PennyLaneAI/pennylane/pull/6652)
+
+ ```python
+ pes_onemode = np.array([[0.309, 0.115, 0.038, 0.008, 0.000, 0.006, 0.020, 0.041, 0.070]])
+ pes_twomode = np.zeros((1, 1, 9, 9))
+ dipole_onemode = np.zeros((1, 9, 3))
+ gauss_weights = np.array([3.96e-05, 4.94e-03, 8.85e-02,
+ 4.33e-01, 7.20e-01, 4.33e-01,
+ 8.85e-02, 4.94e-03, 3.96e-05])
+ grid = np.array([-3.19, -2.27, -1.47, -0.72, 0.0, 0.72, 1.47, 2.27, 3.19])
+ pes_object = qml.qchem.VibrationalPES(
+ freqs=np.array([0.025]),
+ grid=grid,
+ uloc=np.array([[1.0]]),
+ gauss_weights=gauss_weights,
+ pes_data=[pes_onemode, pes_twomode],
+ dipole_data=[dipole_onemode],
+ localized=False,
+ dipole_level=1,
+ )
+ ```
+
+ * The :func:`~.qchem.taylor_hamiltonian` function to build a Taylor Hamiltonian from a
+ :class:`~.qchem.VibrationalPES` object.
+ [(#6523)](https://github.com/PennyLaneAI/pennylane/pull/6523)
+
+ ```pycon
+ >>> qml.qchem.taylor_hamiltonian(pes_object, 4, 2)
+ (
+ 0.016867926879358452 * I(0)
+ + -0.007078617919572303 * Z(0)
+ + 0.0008679410939323631 * X(0)
+ )
+ ```
+
+ * The :func:`~.qchem.taylor_bosonic` function to build a Taylor Hamiltonian in terms of Bosonic
+ operators.
+ [(#6523)](https://github.com/PennyLaneAI/pennylane/pull/6523)
+
+ ```pycon
+ >>> coeffs_arr = qml.qchem.taylor_coeffs(pes_object)
+ >>> bose_op = qml.qchem.taylor_bosonic(coeffs_arr, pes_object.freqs, is_local=pes_object.localized, uloc=pes_object.uloc)
+ >>> type(bose_op)
+ pennylane.bose.bosonic.BoseSentence
+ ```
+
+ Additional functionality is also available to optimize molecular geometries and convert between
+ representations:
+
+ * Convert Christiansen Hamiltonian integrals in the harmonic oscillator basis to integrals in the
+ vibrational self-consistent field (VSCF) basis with the :func:`~.qchem.vscf_integrals` function.
+ [(#6688)](https://github.com/PennyLaneAI/pennylane/pull/6688)
+
+ ```pycon
+ >>> h1 = np.array([[[0.00968289, 0.00233724, 0.0007408, 0.00199125],
+ [0.00233724, 0.02958449, 0.00675431, 0.0021936],
+ [0.0007408, 0.00675431, 0.0506012, 0.01280986],
+ [0.00199125, 0.0021936, 0.01280986, 0.07282307]]])
+ >>> qml.qchem.vscf_integrals(h_integrals=[h1], modals=[4,4,4])
+ (
+ [array([[[ 9.36124041e-03, 3.63798208e-19, -3.42019607e-19,
+ -3.83743044e-19],
+ [ 9.59982270e-19, 2.77803512e-02, 5.18290259e-18,
+ -4.82000376e-18],
+ [-2.73826508e-19, 4.88583546e-18, 4.63297357e-02,
+ -2.87022759e-18],
+ [-1.94549340e-19, -5.48544743e-18, -1.41379640e-18,
+ 7.92203227e-02]]])], None
+ )
+ ```
+
+ * Find the lowest energy configuration of molecules with :func:`~.qchem.optimize_geometry`.
[(#6453)](https://github.com/PennyLaneAI/pennylane/pull/6453)
[(#6666)](https://github.com/PennyLaneAI/pennylane/pull/6666)
- * Implemented helper functions for calculating one-mode PES, two-mode PES, and
- three-mode PES.
+
+ ```pycon
+ >>> symbols = ['H', 'F']
+ >>> geometry = np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 1.0]])
+ >>> mol = qml.qchem.Molecule(symbols, geometry)
+ >>> eq_geom = qml.qchem.optimize_geometry(mol)
+ >>> eq_geom
+ array([[ 0. , 0. , -0.40277116],
+ [ 0. , 0. , 1.40277116]])
+ ```
+
+ * Separate normal mode frequencies and localize them with :func:`~.qchem.localize_normal_modes`.
+ [(#6453)](https://github.com/PennyLaneAI/pennylane/pull/6453)
+
+ ```pycon
+ >>> freqs = np.array([1326.66001461, 2297.26736859, 2299.65032901])
+ >>> vectors = np.array([[[ 5.71518696e-18, -4.55642350e-01, 5.20920552e-01],
+ [ 1.13167924e-17, 4.55642350e-01, 5.20920552e-01],
+ [-1.23163569e-17, 5.09494945e-12, -3.27565762e-02]],
+ [[-4.53008817e-17, 4.90364125e-01, 4.90363894e-01],
+ [-1.98591028e-16, 4.90361513e-01, -4.90361744e-01],
+ [-2.78235498e-18, -3.08350419e-02, -6.75886679e-08]],
+ [[ 5.75393451e-17, 5.37047963e-01, 4.41957355e-01],
+ [ 6.53049347e-17, -5.37050348e-01, 4.41959740e-01],
+ [-5.49709883e-17, 7.49851221e-08, -2.77912798e-02]]])
+ >>> freqs_loc, vecs_loc, uloc = qml.qchem.localize_normal_modes(freqs, vectors)
+ >>> freqs_loc
+ array([1332.62008773, 2296.73455892, 2296.7346082 ])
+ ```
+
+Labs: a place for unified and rapid prototyping of research software 🧑🔬
+
+The new :mod:`qml.labs ` module will house experimental research software 🔬.
+Features here may be useful for state-of-the-art research, beta testing, or getting a sneak peek
+into *potential* new features before they are added to PennyLane.
+
+.. warning::
+ This module is **experimental**! This means that features may not integrate well with other
+ PennyLane staples like differentiability, JAX, or JIT compatibility. There may also be
+ unexpected sharp bits 🔪 and errors ❌. Breaking changes and removals will happen without
+ warning.
+
+Please use these features carefully and let us know your thoughts. Your feedback will inform
+how these features become a part of mainline PennyLane.
+
+Resource estimation
+
+* Resource estimation functionality in Labs is focused on being light-weight and flexible.
+ The Labs :mod:`qml.labs.resource_estimation ` module involves
+ modifications to core PennyLane that reduce the memory requirements and computational time of
+ resource estimation. These include new or modified base classes and one new function:
+ * :class:`~.labs.resource_estimation.Resources` - This class is simplified in `labs`, removing the arguments: `gate_sizes`, `depth`,
+ and `shots`.
+ [(#6428)](https://github.com/PennyLaneAI/pennylane/pull/6428)
+
+ * :class:`~.labs.resource_estimation.ResourceOperator` - Replaces
+ :class:`~.resource.ResourceOperation`, expanded to include decompositions.
+ [(#6428)](https://github.com/PennyLaneAI/pennylane/pull/6428)
+
+ * :class:`~.labs.resource_estimation.CompressedResourceOp` - A new class with the minimum
+ information to estimate resources: the operator type and the parameters needed to decompose it.
+ [(#6428)](https://github.com/PennyLaneAI/pennylane/pull/6428)
+
+ * :class:`~.labs.resource_estimation.ResourceOperator` - versions of many existing PennyLane
+ operations, like Pauli operators, :class:`~.labs.resource_estimation.ResourceHadamard`, and
+ :class:`~.labs.resource_estimation.ResourceCNOT`.
+ [(#6447)](https://github.com/PennyLaneAI/pennylane/pull/6447)
+ [(#6579)](https://github.com/PennyLaneAI/pennylane/pull/6579)
+ [(#6538)](https://github.com/PennyLaneAI/pennylane/pull/6538)
+ [(#6592)](https://github.com/PennyLaneAI/pennylane/pull/6592)
+
+ * :func:`~.labs.resource_estimation.get_resources()` - The new entry point to efficiently obtain
+ the resources of quantum circuits.
+ [(#6500)](https://github.com/PennyLaneAI/pennylane/pull/6500)
+
+ Using new resource versions of existing operations and
+ :func:`~.labs.resource_estimation.get_resources`, we can estimate resources quickly:
+
+ ```python
+ import pennylane.labs.resource_estimation as re
+
+ def my_circuit():
+ for w in range(2):
+ re.ResourceHadamard(w)
+ re.ResourceCNOT([0, 1])
+ re.ResourceRX(1.23, 0)
+ re.ResourceRY(-4.56, 1)
+ re.ResourceQFT(wires=[0, 1, 2])
+ return qml.expval(re.ResourceHadamard(2))
+ ```
+ ```pycon
+ >>> res = re.get_resources(my_circuit)()
+ >>> print(res)
+ wires: 3
+ gates: 202
+ gate_types:
+ {'Hadamard': 5, 'CNOT': 10, 'T': 187}
+ ```
+
+ We can also set custom gate sets for decompositions:
+
+ ````pycon
+ >>> gate_set={"Hadamard","CNOT","RZ", "RX", "RY", "SWAP"}
+ >>> res = re.get_resources(my_circuit, gate_set=gate_set)()
+ >>> print(res)
+ wires: 3
+ gates: 24
+ gate_types:
+ {'Hadamard': 5, 'CNOT': 7, 'RX': 1, 'RY': 1, 'SWAP': 1, 'RZ': 9}
+ ````
+
+ Alternatively, it is possible to manually substitute associated resources:
+
+ ```pycon
+ >>> new_resources = re.substitute(res, "SWAP", re.Resources(2, 3, {"CNOT":3}))
+ >>> print(new_resources)
+ {'Hadamard': 5, 'CNOT': 10, 'RX': 1, 'RY': 1, 'RZ': 9}
+ ```
+
+Experimental functionality for handling dynamical Lie algebras (DLAs)
+
+* Use the :mod:`qml.labs.dla ` module to perform the
+ [KAK decomposition](https://pennylane.ai/qml/demos/tutorial_kak_decomposition):
+ * :func:`~.labs.dla.cartan_decomp`: obtain a **Cartan decomposition** of an input **Lie algebra**
+ via an **involution**.
+ [(#6392)](https://github.com/PennyLaneAI/pennylane/pull/6392)
+
+ * We provide a variety of **involutions** like :func:`~.labs.dla.concurrence_involution`,
+ :func:`~.labs.dla.even_odd_involution` and canonical Cartan involutions.
+ [(#6392)](https://github.com/PennyLaneAI/pennylane/pull/6392)
+ [(#6396)](https://github.com/PennyLaneAI/pennylane/pull/6396)
+
+ * :func:`~.labs.dla.cartan_subalgebra`: compute a horizontal **Cartan subalgebra**.
+ [(#6403)](https://github.com/PennyLaneAI/pennylane/pull/6403)
+
+ * :func:`~.labs.dla.variational_kak_adj` : compute a
+ [variational KAK decomposition](https://pennylane.ai/qml/demos/tutorial_fixed_depth_hamiltonian_simulation_via_cartan_decomposition)
+ of a Hermitian operator using a **Cartan decomposition** and the adjoint representation of a
+ horizontal **Cartan subalgebra**.
+ [(#6446)](https://github.com/PennyLaneAI/pennylane/pull/6446)
+
+ To use this functionality we start with a set of Hermitian operators.
+
+ ```pycon
+ >>> n = 3
+ >>> gens = [qml.X(i) @ qml.X(i + 1) for i in range(n - 1)]
+ >>> gens += [qml.Z(i) for i in range(n)]
+ >>> H = qml.sum(*gens)
+ ```
+
+ We then generate its Lie algebra by computing the Lie closure.
+
+ ```pycon
+ >>> g = qml.lie_closure(gens)
+ >>> g = [op.pauli_rep for op in g]
+ >>> print(g)
+ [1 * X(0) @ X(1), 1 * X(1) @ X(2), 1.0 * Z(0), ...]
+ ```
+
+ We then choose an involution (e.g. :func:`~.labs.dla.concurrence_involution`) that defines a
+ Cartan decomposition `g = k + m`. `k` is the vertical subalgebra, and `m` its horizontal
+ complement (not a subalgebra).
+
+ ```pycon
+ >>> from pennylane.labs.dla import concurrence_involution, cartan_decomp
+ >>> involution = concurrence_involution
+ >>> k, m = cartan_decomp(g, involution=involution)
+ ```
+
+ The next step is just re-ordering the basis elements in `g` and computing its
+ `structure_constants`.
+
+ ```pycon
+ >>> g = k + m
+ >>> adj = qml.structure_constants(g)
+ ```
+
+ We can then compute a (horizontal) Cartan subalgebra `a`, that is, a maximal Abelian subalgebra of
+ `m`.
+
+ ```pycon
+ >>> from pennylane.labs.dla import cartan_subalgebra
+ >>> g, k, mtilde, a, adj = cartan_subalgebra(g, k, m, adj)
+ ```
+
+ Having determined both subalgebras `k` and `a`, we can compute the KAK decomposition variationally
+ like in [2104.00728](https://arxiv.org/abs/2104.00728), see our
+ [demo on KAK decomposition in practice](https://pennylane.ai/qml/demos/tutorial_fixed_depth_hamiltonian_simulation_via_cartan_decomposition).
+
+ ```pycon
+ >>> from pennylane.labs.dla import variational_kak_adj
+ >>> dims = (len(k), len(mtilde), len(a))
+ >>> adjvec_a, theta_opt = variational_kak_adj(H, g, dims, adj, opt_kwargs={"n_epochs": 3000})
+ ```
+
+* We also provide some additional features that are useful for handling dynamical Lie algebras.
+ * :func:`~.labs.dla.recursive_cartan_decomp`: perform consecutive recursive Cartan decompositions.
+ [(#6396)](https://github.com/PennyLaneAI/pennylane/pull/6396)
+
+ * :func:`~.labs.dla.lie_closure_dense`: extension of `qml.lie_closure` using dense matrices.
+ [(#6371)](https://github.com/PennyLaneAI/pennylane/pull/6371)
+ [(#6695)](https://github.com/PennyLaneAI/pennylane/pull/6695)
+
+ * :func:`~.labs.dla.structure_constants_dense`: extension of `qml.structure_constants` using dense
+ matrices.
+ [(#6396)](https://github.com/PennyLaneAI/pennylane/pull/6396) [(#6376)](https://github.com/PennyLaneAI/pennylane/pull/6376)
+
+
+Vibrational Hamiltonians
+
+* New functionality in labs helps with the construction of vibrational Hamiltonians.
+ * Generate potential energy surfaces (PES) with `qml.labs.vibrational.vibrational_pes`.
[(#6616)](https://github.com/PennyLaneAI/pennylane/pull/6616)
[(#6676)](https://github.com/PennyLaneAI/pennylane/pull/6676)
- * Implemented wrapper function for vibrational Hamiltonian calculation and dataclass
- for storing the data.
- [(#6652)](https://github.com/PennyLaneAI/pennylane/pull/6652)
- * Implemented functions for generating the vibrational Hamiltonian in VSCF basis
- [(#6688)](https://github.com/PennyLaneAI/pennylane/pull/6688)
-
-* Added support to build a vibrational Hamiltonian in Taylor form.
- [(#6523)](https://github.com/PennyLaneAI/pennylane/pull/6523)
+
+ ```pycon
+ >>> symbols = ['H', 'F']
+ >>> geometry = np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 1.0]])
+ >>> mol = qml.qchem.Molecule(symbols, geometry)
+ >>> pes = vibrational_pes(mol)
+ ```
+ * Use the `qml.labs.vibrational.christiansen_hamiltonian` function and potential energy surfaces
+ to generate Hamiltonians in the Ch ristiansen form.
+ [(#6560)](https://github.com/PennyLaneAI/pennylane/pull/6560)
Improvements 🛠
@@ -120,99 +608,122 @@
[(#6573)](https://github.com/PennyLaneAI/pennylane/pull/6573)
[(#6611)](https://github.com/PennyLaneAI/pennylane/pull/6611)
-* Added `qml.qchem.symmetry_shift` function to perform the
- [block-invariant symmetry shift](https://arxiv.org/pdf/2304.13772) on the electronic integrals.
+* A new function for performing the [block-invariant symmetry shift](https://arxiv.org/pdf/2304.13772)
+ on electronic integrals has been added with `qml.qchem.symmetry_shift`.
[(#6574)](https://github.com/PennyLaneAI/pennylane/pull/6574)
-* Added JAX support for the differentiable Hartree-Fock workflow.
+* The differentiable Hartree-Fock workflow is now compatible with JAX.
[(#6096)](https://github.com/PennyLaneAI/pennylane/pull/6096)
[(#6707)](https://github.com/PennyLaneAI/pennylane/pull/6707)
Transform for combining GlobalPhase instances
-* Added a new `qml.transforms.combine_global_phases` transform to combine all `qml.GlobalPhase` gates in a circuit into a single one applied at the end.
- This can be useful for circuits that include a lot of `qml.GlobalPhase` gates, which can be introduced directly during circuit creation,
- decompositions that include `qml.GlobalPhase` gates, etc.
+* A new transform called `qml.transforms.combine_global_phases` has been added. It combines all
+ `qml.GlobalPhase` gates in a circuit into a single one applied at the end. This can be useful for
+ circuits that include a lot of `qml.GlobalPhase` gates that are introduced directly during
+ circuit creation, decompositions that include `qml.GlobalPhase` gates, etc.
[(#6686)](https://github.com/PennyLaneAI/pennylane/pull/6686)
Better drawing functionality
-* Added support for the `wire_options` dictionary to customize wire line formatting in `qml.draw_mpl` circuit
- visualizations, allowing global and per-wire customization with options like `color`, `linestyle`, and `linewidth`.
+* `qml.draw_mpl` now has a `wire_options` keyword argument, which allows for global- and per-wire
+ customization with options like `color`, `linestyle`, and `linewidth`.
[(#6486)](https://github.com/PennyLaneAI/pennylane/pull/6486)
+ Here is an example that would make all wires cyan and bold except for wires 2 and 6, which are
+ dashed and a different colour.
+
+ ```python
+ @qml.qnode(qml.device("default.qubit"))
+ def circuit(x):
+ for w in range(5):
+ qml.Hadamard(w)
+ return qml.expval(qml.PauliZ(0) @ qml.PauliY(1))
+
+ wire_options = {"color": "cyan",
+ "linewidth": 5,
+ 2: {"linestyle": "--", "color": "red"},
+ 6: {"linestyle": "--", "color": "orange"}
+ }
+ print(qml.draw_mpl(circuit, wire_options=wire_options)(0.52))
+ ```
+
New device capabilities 💾
-* Two new methods: `setup_execution_config` and `preprocess_transforms` are added to the `Device`
- class. Device developers are encouraged to override these two methods separately instead of the
+* Two new methods, `setup_execution_config` and `preprocess_transforms`, have been added to the
+ `Device` class. Device developers are encouraged to override these two methods separately instead of the
`preprocess` method. For now, to avoid ambiguity, a device is allowed to override either these
two methods or `preprocess`, but not both. In the long term, we will slowly phase out the use of
`preprocess` in favour of these two methods for better separation of concerns.
[(#6617)](https://github.com/PennyLaneAI/pennylane/pull/6617)
* Developers of plugin devices now have the option of providing a TOML-formatted configuration file
- to declare the capabilities of the device. See [Device Capabilities](https://docs.pennylane.ai/en/latest/development/plugins.html#device-capabilities) for details.
+ to declare the capabilities of the device. See
+ [Device Capabilities](https://docs.pennylane.ai/en/latest/development/plugins.html#device-capabilities)
+ for details.
-* An internal module `pennylane.devices.capabilities` is added that defines a new `DeviceCapabilites`
- data class, as well as functions that load and parse the TOML-formatted configuration files.
+* An internal module called `qml.devices.capabilities` has been added that defines a new
+ `DeviceCapabilites` data class, as well as functions that load and parse the TOML-formatted
+ configuration files.
[(#6407)](https://github.com/PennyLaneAI/pennylane/pull/6407)
```pycon
- >>> from pennylane.devices.capabilities import DeviceCapabilities
- >>> capabilities = DeviceCapabilities.from_toml_file("my_device.toml")
- >>> isinstance(capabilities, DeviceCapabilities)
- True
+ >>> from pennylane.devices.capabilities import DeviceCapabilities
+ >>> capabilities = DeviceCapabilities.from_toml_file("my_device.toml")
+ >>> isinstance(capabilities, DeviceCapabilities)
+ True
```
-* Devices that extends `qml.devices.Device` now has an optional class attribute `capabilities`
- that is an instance of the `DeviceCapabilities` data class, constructed from the configuration
- file if it exists. Otherwise, it is set to `None`.
+* Devices that extend `qml.devices.Device` now have an optional class attribute called
+ `capabilities`, which is an instance of the `DeviceCapabilities` data class constructed from the
+ configuration file if it exists. Otherwise, it is set to `None`.
[(#6433)](https://github.com/PennyLaneAI/pennylane/pull/6433)
```python
from pennylane.devices import Device
class MyDevice(Device):
-
config_filepath = "path/to/config.toml"
-
...
```
+
```pycon
>>> isinstance(MyDevice.capabilities, DeviceCapabilities)
True
```
* Default implementations of `Device.setup_execution_config` and `Device.preprocess_transforms`
- are added to the device API for devices that provides a TOML configuration file and thus have
- a `capabilities` property.
+ have been added to the device API for devices that provide a TOML configuration file and, thus,
+ have a `capabilities` property.
[(#6632)](https://github.com/PennyLaneAI/pennylane/pull/6632)
[(#6653)](https://github.com/PennyLaneAI/pennylane/pull/6653)
Capturing and representing hybrid programs
-* Support is added for `if`/`else` statements and `for` and `while` loops in circuits executed with `qml.capture.enabled`, via Autograph.
- Autograph conversion is now used by default in `make_plxpr`, but can be skipped with the keyword arg `autograph=False`.
+* Support has been added for `if`/`else` statements and `for` and `while` loops in circuits executed
+ with `qml.capture.enabled`, via Autograph. Autograph conversion is now used by default in
+ `make_plxpr`, but can be skipped with `autograph=False`.
[(#6406)](https://github.com/PennyLaneAI/pennylane/pull/6406)
[(#6413)](https://github.com/PennyLaneAI/pennylane/pull/6413)
[(#6426)](https://github.com/PennyLaneAI/pennylane/pull/6426)
[(#6645)](https://github.com/PennyLaneAI/pennylane/pull/6645)
[(#6685)](https://github.com/PennyLaneAI/pennylane/pull/6685)
-* `qml.transform` now accepts a `plxpr_transform` argument. This argument must be a function that can transform plxpr.
- Note that executing a transformed function will currently raise a `NotImplementedError`. To see more details, check
- out the documentation of `qml.transform`.
+* `qml.transform` now accepts a `plxpr_transform` argument. This argument must be a function that
+ can transform plxpr. Note that executing a transformed function will currently raise a
+ `NotImplementedError`. To see more details, check out the
+ :func:`documentation of qml.transform `.
[(#6633)](https://github.com/PennyLaneAI/pennylane/pull/6633)
[(#6722)](https://github.com/PennyLaneAI/pennylane/pull/6722)
-* Users can now apply transforms with program capture enabled. Transformed functions cannot be executed by default. To apply
- the transforms (and be able to execute the function), it must be decorated with the new `qml.capture.expand_plxpr_transforms`
- function, which accepts a callable as input and returns a new function to which all present transforms have been applied.
+* Users can now apply transforms with program capture enabled. Transformed functions cannot be
+ executed by default. To apply the transforms (and to be able to execute the function), it must be
+ decorated with the new `qml.capture.expand_plxpr_transforms` function, which accepts a callable as
+ input and returns a new function for which all present transforms have been applied.
[(#6722)](https://github.com/PennyLaneAI/pennylane/pull/6722)
```python
from functools import partial
- import jax
qml.capture.enable()
wire_map = {0: 3, 1: 6, 2: 9}
@@ -252,29 +763,31 @@
in (d,) }
```
-* The `qml.iterative_qpe` function can now be compactly captured into jaxpr.
+* The `qml.iterative_qpe` function can now be compactly captured into plxpr.
[(#6680)](https://github.com/PennyLaneAI/pennylane/pull/6680)
-* Functions and plxpr can now be natively transformed using the new `qml.capture.transforms.CancelInterpreter`
- when program capture is enabled. This class cancels operators appearing consecutively that are adjoints of each
- other, and follows the same API as `qml.transforms.cancel_inverses`.
- [(#6692)](https://github.com/PennyLaneAI/pennylane/pull/6692)
+* Three new plxpr interpreters have been added that allow for functions and plxpr to be natively
+ transformed with the same API as the corresponding existing transforms in PennyLane when program
+ capture is enabled:
+
+ * `qml.capture.transforms.CancelInterpreter`:this class cancels operators appearing consecutively
+ that are adjoints of each other following the same API as `qml.transforms.cancel_inverses`.
+ [(#6692)](https://github.com/PennyLaneAI/pennylane/pull/6692)
-* Functions and plxpr can now be natively transformed using the new `qml.capture.transforms.DecomposeInterpreter`
- when program capture is enabled. This class decomposes pennylane operators following the same API as
- `qml.transforms.decompose`.
- [(#6691)](https://github.com/PennyLaneAI/pennylane/pull/6691)
+ * `qml.capture.transforms.DecomposeInterpreter`: this class decomposes pennylane operators
+ following the same API as `qml.transforms.decompose`.
+ [(#6691)](https://github.com/PennyLaneAI/pennylane/pull/6691)
-* Implemented a `MapWiresInterpreter` class that can be used as a quantum transform to map
- operator and measurement wires with capture enabled.
- [(#6697)](https://github.com/PennyLaneAI/pennylane/pull/6697)
+ * `qml.capture.transforms.MapWiresInterpreter`: this class maps wires to new values following the
+ same API as `qml.map_wires`.
+ [(#6697)](https://github.com/PennyLaneAI/pennylane/pull/6697)
-* A `qml.tape.plxpr_to_tape` function can now convert plxpr to a tape.
+* A `qml.tape.plxpr_to_tape` function is now available that converts plxpr to a tape.
[(#6343)](https://github.com/PennyLaneAI/pennylane/pull/6343)
* Execution with capture enabled now follows a new execution pipeline and natively passes the
- captured jaxpr to the device. Since it no longer falls back to the old pipeline, execution
- only works with a reduced feature set.
+ captured plxpr to the device. Since it no longer falls back to the old pipeline, execution only
+ works with a reduced feature set.
[(#6655)](https://github.com/PennyLaneAI/pennylane/pull/6655)
[(#6596)](https://github.com/PennyLaneAI/pennylane/pull/6596)
@@ -286,16 +799,17 @@
[(#6422)](https://github.com/PennyLaneAI/pennylane/pull/6422)
[(#6668)](https://github.com/PennyLaneAI/pennylane/pull/6668)
-* `qml.capture.PlxprInterpreter` base class has been added for easy transformation and execution of
- pennylane variant jaxpr.
+* A `qml.capture.PlxprInterpreter` base class has been added for easy transformation and execution
+ of plxpr.
[(#6141)](https://github.com/PennyLaneAI/pennylane/pull/6141)
-* A `DefaultQubitInterpreter` class has been added to provide plxpr execution using python based tools,
- and the `DefaultQubit.eval_jaxpr` method is now implemented.
+* A `DefaultQubitInterpreter` class has been added to provide plxpr execution using python based
+ tools, and the `DefaultQubit.eval_jaxpr` method has been implemented.
[(#6594)](https://github.com/PennyLaneAI/pennylane/pull/6594)
[(#6328)](https://github.com/PennyLaneAI/pennylane/pull/6328)
-* An optional method `eval_jaxpr` is added to the device API for native execution of plxpr programs.
+* An optional method, `eval_jaxpr`, has been added to the device API for native execution of plxpr
+ programs.
[(#6580)](https://github.com/PennyLaneAI/pennylane/pull/6580)
* `qml.capture.qnode_call` has been made private and moved to the `workflow` module.
@@ -303,41 +817,45 @@
Other Improvements
-* `qml.math.grad` and `qml.math.jacobian` added to differentiate a function with inputs of any
- interface in a jax-like manner.
+* `qml.math.grad` and `qml.math.jacobian` have been added to differentiate a function with inputs of
+ any interface in a JAX-like manner.
[(#6741)](https://github.com/PennyLaneAI/pennylane/pull/6741)
* `qml.GroverOperator` now has a `work_wires` property.
[(#6738)](https://github.com/PennyLaneAI/pennylane/pull/6738)
-* `Wires` object usage across Pennylane source code has been tidied up.
+* The `Wires` object's usage across Pennylane source code has been tidied up for internal
+ consistency.
[(#6689)](https://github.com/PennyLaneAI/pennylane/pull/6689)
-* `qml.equal` now supports `PauliWord` and `PauliSentence` instances.
+* `qml.equal` now supports `qml.PauliWord` and `qml.PauliSentence` instances.
[(#6703)](https://github.com/PennyLaneAI/pennylane/pull/6703)
-* Remove redundant commutator computations from `qml.lie_closure`.
+* Redundant commutator computations from `qml.lie_closure` have been removed.
[(#6724)](https://github.com/PennyLaneAI/pennylane/pull/6724)
-* Raises a comprehensive error when using `qml.fourier.qnode_spectrum` with standard numpy
+* A comprehensive error is now raised when using `qml.fourier.qnode_spectrum` with standard Numpy
arguments and `interface="auto"`.
[(#6622)](https://github.com/PennyLaneAI/pennylane/pull/6622)
-* Added Pauli String representations for the gates X, Y, Z, S, T, SX, SWAP, ISWAP, ECR, SISWAP. Fixed a shape error in the matrix conversion of `PauliSentence`s with list or array input.
+* Pauli string representations for the gates `{X, Y, Z, S, T, SX, SWAP, ISWAP, ECR, SISWAP}` have
+ been added, and a shape error in the matrix conversion of `qml.PauliSentence`s with `list` or
+ `array` inputs has been fixed.
[(#6562)](https://github.com/PennyLaneAI/pennylane/pull/6562)
[(#6587)](https://github.com/PennyLaneAI/pennylane/pull/6587)
-* `QNode` and `qml.execute` now forbid certain keyword arguments from being passed positionally.
+* `qml.QNode` and `qml.execute` now forbid certain keyword arguments from being passed positionally.
[(#6610)](https://github.com/PennyLaneAI/pennylane/pull/6610)
-* Shortened the string representation for the `qml.S`, `qml.T`, and `qml.SX` operators.
+* The string representations for the `qml.S`, `qml.T`, and `qml.SX` have been shortened.
[(#6542)](https://github.com/PennyLaneAI/pennylane/pull/6542)
-* Added functions and dunder methods to add and multiply Resources objects in series and in parallel.
+* Internal class functions and dunder methods have been added to allow for multiplying Resources
+ objects in series and in parallel.
[(#6567)](https://github.com/PennyLaneAI/pennylane/pull/6567)
* The `diagonalize_measurements` transform no longer raises an error for unknown observables. Instead,
- they are left undiagonalized, with the expectation that observable validation will catch any undiagonalized
+ they are left un-diagonalized, with the expectation that observable validation will catch any un-diagonalized
observables that are also unsupported by the device.
[(#6653)](https://github.com/PennyLaneAI/pennylane/pull/6653)
@@ -349,26 +867,26 @@
[(#6630)](https://github.com/PennyLaneAI/pennylane/pull/6630)
[(#6736)](https://github.com/PennyLaneAI/pennylane/pull/6736)
-* Add developer focused `run` function to `qml.workflow` module.
+* A developer focused `run` function has been added to the `qml.workflow` module for a cleaner and
+ standardized approach to executing tapes on an ML interface.
[(#6657)](https://github.com/PennyLaneAI/pennylane/pull/6657)
-* Standardize supported interfaces to an internal `Enum` object.
+* Internal changes have been made to standardize execution interfaces, which resolves ambiguities in
+ how the `interface` value is handled during execution.
[(#6643)](https://github.com/PennyLaneAI/pennylane/pull/6643)
-* Moved all interface handling logic to `interface_utils.py` in the `qml.math` module.
+* All interface handling logic has been moved to `interface_utils.py` in the `qml.math` module.
[(#6649)](https://github.com/PennyLaneAI/pennylane/pull/6649)
* `qml.execute` can now be used with `diff_method="best"`.
Classical cotransform information is now handled lazily by the workflow. Gradient method
- validation and program setup is now handled inside of `qml.execute`, instead of in `QNode`.
+ validation and program setup are now handled inside of `qml.execute`, instead of in `QNode`.
[(#6716)](https://github.com/PennyLaneAI/pennylane/pull/6716)
-* Added PyTree support for measurements in a circuit.
+* PyTree support for measurements in a circuit has been added.
[(#6378)](https://github.com/PennyLaneAI/pennylane/pull/6378)
```python
- import pennylane as qml
-
@qml.qnode(qml.device("default.qubit"))
def circuit():
qml.Hadamard(0)
@@ -380,182 +898,139 @@
{'Probabilities': array([0.5, 0. , 0. , 0.5]), 'State': array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j])}
```
-* `_cache_transform` transform has been moved to its own file located
- at `qml.workflow._cache_transform.py`.
+* The `_cache_transform` transform has been moved to its own file located in
+ `pennylane/workflow/_cache_transform.py`.
[(#6624)](https://github.com/PennyLaneAI/pennylane/pull/6624)
-* `qml.BasisRotation` template is now JIT compatible.
+* The `qml.BasisRotation` template is now JIT compatible.
[(#6019)](https://github.com/PennyLaneAI/pennylane/pull/6019)
[(#6779)](https://github.com/PennyLaneAI/pennylane/pull/6779)
* The Jaxpr primitives for `for_loop`, `while_loop` and `cond` now store slices instead of
- numbers of args.
+ numbers of arguments. This helps with keeping track of what order the arguments come in.
[(#6521)](https://github.com/PennyLaneAI/pennylane/pull/6521)
-* Expand `ExecutionConfig.gradient_method` to store `TransformDispatcher` type.
+* The `ExecutionConfig.gradient_method` function has been expanded to store `TransformDispatcher`
+ type.
[(#6455)](https://github.com/PennyLaneAI/pennylane/pull/6455)
-* Fix the string representation of `Resources` instances to match the attribute names.
+* The string representation of `Resources` instances has been improved to match the attribute names.
[(#6581)](https://github.com/PennyLaneAI/pennylane/pull/6581)
-* Improved documentation for the `dynamic_one_shot` transform, and a warning is raised when a user-applied `dynamic_one_shot` transform is ignored in favour of the existing transform in a device's preprocessing transform program.
+* The documentation for the `dynamic_one_shot` transform has been improved, and a warning is raised
+ when a user-applied `dynamic_one_shot` transform is ignored in favour of the existing transform in
+ a device's preprocessing transform program.
[(#6701)](https://github.com/PennyLaneAI/pennylane/pull/6701)
-* Added `qml.devices.qubit_mixed` module for mixed-state qubit device support [(#6379)](https://github.com/PennyLaneAI/pennylane/pull/6379). This module introduces an `apply_operation` helper function that features:
-
+* A `qml.devices.qubit_mixed` module has been added for mixed-state qubit device support. This
+ module introduces an `apply_operation` helper function that features:
* Two density matrix contraction methods using `einsum` and `tensordot`
- * Optimized handling of special cases including: Diagonal operators, Identity operators, CX (controlled-X), Multi-controlled X gates, Grover operators
+ * Optimized handling of special cases including: Diagonal operators, Identity operators, CX
+ (controlled-X), Multi-controlled X gates, Grover operators
+ [(#6379)](https://github.com/PennyLaneAI/pennylane/pull/6379)
-* Added submodule 'initialize_state' featuring a `create_initial_state` function for initializing a density matrix from `qml.StatePrep` operations or `qml.QubitDensityMatrix` operations.
+* A function called `create_initial_state` has been added to allow for initializing a circuit with a
+ density matrix using `qml.StatePrep` or `qml.QubitDensityMatrix`.
[(#6503)](https://github.com/PennyLaneAI/pennylane/pull/6503)
-
-* Added method `preprocess` to the `QubitMixed` device class to preprocess the quantum circuit before execution. Necessary non-intrusive interfaces changes to class init method were made along the way to the `QubitMixed` device class to support new API feature.
- [(#6601)](https://github.com/PennyLaneAI/pennylane/pull/6601)
-
-* Added a second class `DefaultMixedNewAPI` to the `qml.devices.qubit_mixed` module, which is to be the replacement of legacy `DefaultMixed` which for now to hold the implementations of `preprocess` and `execute` methods.
- [(#6607)](https://github.com/PennyLaneAI/pennylane/pull/6607)
-
-* Added submodule `devices.qubit_mixed.measure` as a necessary step for the new API, featuring a `measure` function for measuring qubits in mixed-state devices.
- [(#6637)](https://github.com/PennyLaneAI/pennylane/pull/6637)
-
-* Added submodule `devices.qubit_mixed.simulate` as a necessary step for the new API,
-featuring a `simulate` function for simulating mixed states in analytic mode.
- [(#6618)](https://github.com/PennyLaneAI/pennylane/pull/6618)
-
-* Added submodule `devices.qubit_mixed.sampling` as a necessary step for the new API, featuring functions `sample_state`, `measure_with_samples` and `sample_probs` for sampling qubits in mixed-state devices.
- [(#6639)](https://github.com/PennyLaneAI/pennylane/pull/6639)
-
-* Implemented the finite-shot branch of `devices.qubit_mixed.simulate`. Now, the
-new device API of `default_mixed` should be able to take the stochastic arguments
-such as `shots`, `rng` and `prng_key`.
-[(#6665)](https://github.com/PennyLaneAI/pennylane/pull/6665)
-
-* Added support `qml.Snapshot` operation in `qml.devices.qubit_mixed.apply_operation`.
- [(#6659)](https://github.com/PennyLaneAI/pennylane/pull/6659)
-
-Labs: a place for unified and rapid prototyping of research software 🧪
-
-Resource estimation
-
-* Added base class `Resources`, `CompressedResourceOp`, `ResourceOperator` for advanced resource estimation.
- [(#6428)](https://github.com/PennyLaneAI/pennylane/pull/6428)
-
-* Added `get_resources()` functionality which allows users to extract resources from a quantum function, tape or
- resource operation. Additionally added some standard gatesets `DefaultGateSet` to track resources with respect to.
- [(#6500)](https://github.com/PennyLaneAI/pennylane/pull/6500)
-
-* Added `ResourceOperator` classes for QFT and all operators in QFT's decomposition.
- [(#6447)](https://github.com/PennyLaneAI/pennylane/pull/6447)
-
-* Added native `ResourceOperator` subclasses for each of the controlled operators.
- [(#6579)](https://github.com/PennyLaneAI/pennylane/pull/6579)
-
-* Added native `ResourceOperator` subclasses for each of the multi qubit operators.
- [(#6538)](https://github.com/PennyLaneAI/pennylane/pull/6538)
-
-* Added abstract `ResourceOperator` subclasses for Adjoint, Controlled, and Pow
- symbolic operation classes.
- [(#6592)](https://github.com/PennyLaneAI/pennylane/pull/6592)
-
-Experimental functionality for handling dynamical Lie algebras (DLAs)
-
-* Added a dense implementation of computing the Lie closure in a new function
- `lie_closure_dense` in `pennylane.labs.dla`.
- [(#6371)](https://github.com/PennyLaneAI/pennylane/pull/6371)
- [(#6695)](https://github.com/PennyLaneAI/pennylane/pull/6695)
-
-* Added a dense implementation of computing the structure constants in a new function
- `structure_constants_dense` in `pennylane.labs.dla`.
- [(#6376)](https://github.com/PennyLaneAI/pennylane/pull/6376)
-
-* Added utility functions for handling dense matrices and advanced functionality in the Lie theory context.
- [(#6563)](https://github.com/PennyLaneAI/pennylane/pull/6563)
- [(#6392)](https://github.com/PennyLaneAI/pennylane/pull/6392)
- [(#6396)](https://github.com/PennyLaneAI/pennylane/pull/6396)
-
-* Added a ``cartan_decomp`` function along with two standard involutions ``even_odd_involution`` and ``concurrence_involution``.
- [(#6392)](https://github.com/PennyLaneAI/pennylane/pull/6392)
-
-* Added a `recursive_cartan_decomp` function and all canonical Cartan involutions.
- [(#6396)](https://github.com/PennyLaneAI/pennylane/pull/6396)
-* Added a `cartan_subalgebra` function to compute the (horizontal) Cartan subalgebra of a Cartan decomposition.
- [(#6403)](https://github.com/PennyLaneAI/pennylane/pull/6403)
- [(#6396)](https://github.com/PennyLaneAI/pennylane/pull/6396)
-
-* Added a `variational_kak_adj` function to compute a KaK decomposition of a Hamiltonian given a Cartan
- decomposition and the ordered adjoint representation of the Lie algebra.
- [(#6446)](https://github.com/PennyLaneAI/pennylane/pull/6446)
-
-* Improved documentation by fixing broken links and latex issues. Also consistently use `$\mathfrak{a}$`
- for the horizontal Cartan subalgebra instead of `$\mathfrak{h}$`.
- [(#6747)](https://github.com/PennyLaneAI/pennylane/pull/6747)
+* Several additions have been made to eventually migrate the `"default.mixed"` device to the new
+ device API:
+ * A `preprocess` method has been added to the `QubitMixed` device class to preprocess the quantum
+ circuit before execution.
+ [(#6601)](https://github.com/PennyLaneAI/pennylane/pull/6601)
+ * A new class called `DefaultMixedNewAPI` has been added to the `qml.devices.qubit_mixed` module,
+ which will replace the legacy `DefaultMixed`.
+ [(#6607)](https://github.com/PennyLaneAI/pennylane/pull/6607)
+ * A new submodule called `devices.qubit_mixed.measure` has been added, featuring a `measure`
+ function for measuring qubits in mixed-state devices.
+ [(#6637)](https://github.com/PennyLaneAI/pennylane/pull/6637)
+ * A new submodule called `devices.qubit_mixed.simulate` has been added, featuring a `simulate`
+ function for simulating mixed states in analytic mode.
+ [(#6618)](https://github.com/PennyLaneAI/pennylane/pull/6618)
+ * A new submodule called `devices.qubit_mixed.sampling` has been added, featuring functions
+ `sample_state`, `measure_with_samples` and `sample_probs` for sampling qubits in mixed-state
+ devices.
+ [(#6639)](https://github.com/PennyLaneAI/pennylane/pull/6639)
+ * The finite-shot branch of `devices.qubit_mixed.simulate` has been added, which allows for
+ accepting stochastic arguments such as `shots`, `rng` and `prng_key`.
+ [(#6665)](https://github.com/PennyLaneAI/pennylane/pull/6665)
+ * Support for `qml.Snapshot` has been added.
+ [(#6659)](https://github.com/PennyLaneAI/pennylane/pull/6659)
+
+* Reporting of test warnings as failures has been added.
+ [(#6217)](https://github.com/PennyLaneAI/pennylane/pull/6217)
-Construct vibrational Hamiltonians 🫨
+* A warning message in the Gradients and training documentation has been added that pertains to
+ `ComplexWarning`s.
+ [(#6543)](https://github.com/PennyLaneAI/pennylane/pull/6543)
-* Added support to build a vibrational Hamiltonian in the Christiansen form.
- [(#6560)](https://github.com/PennyLaneAI/pennylane/pull/6560)
- [(#6792)](https://github.com/PennyLaneAI/pennylane/pull/6792)
+* A new figure was added to the landing page of the PennyLane website.
+ [(#6696)](https://github.com/PennyLaneAI/pennylane/pull/6696)
Breaking changes 💔
-* The default graph coloring method of `qml.dot`, `qml.sum`, and `qml.pauli.optimize_measurements` for grouping observables was changed
- from `"rlf"` to `"lf"`. Internally, `qml.pauli.group_observables` has been replaced with `qml.pauli.compute_partition_indices`
- in several places to improve efficiency.
+* The default graph coloring method of `qml.dot`, `qml.sum`, and `qml.pauli.optimize_measurements`
+ for grouping observables was changed from `"rlf"` to `"lf"`. Internally,
+ `qml.pauli.group_observables` has been replaced with `qml.pauli.compute_partition_indices` in
+ several places to improve efficiency.
[(#6706)](https://github.com/PennyLaneAI/pennylane/pull/6706)
-* `qml.fourier.qnode_spectrum` no longer automatically converts pure numpy parameters to the
+* `qml.fourier.qnode_spectrum` no longer automatically converts pure Numpy parameters to the
Autograd framework. As the function uses automatic differentiation for validation, parameters
- from an autodiff framework have to be used.
+ from such a framework have to be used.
[(#6622)](https://github.com/PennyLaneAI/pennylane/pull/6622)
-* `qml.math.jax_argnums_to_tape_trainable` is moved and made private to avoid a qnode dependency
- in the math module.
+* `qml.math.jax_argnums_to_tape_trainable` has been moved and made private to avoid an unnecessary
+ QNode dependency in the `qml.math` module.
[(#6609)](https://github.com/PennyLaneAI/pennylane/pull/6609)
-* Gradient transforms are now applied after the user's transform program.
+* Gradient transforms are now applied after the user's transform program. This ensures user
+ transforms work as expected on initial structures (e.g., embeddings or entangling layers),
+ guarantees that gradient transforms only process compatible operations, aligns transform order
+ with user expectations, and avoids confusion.
[(#6590)](https://github.com/PennyLaneAI/pennylane/pull/6590)
-* Legacy operator arithmetic has been removed. This includes `qml.ops.Hamiltonian`, `qml.operation.Tensor`,
- `qml.operation.enable_new_opmath`, `qml.operation.disable_new_opmath`, and `qml.operation.convert_to_legacy_H`.
- Note that `qml.Hamiltonian` will continue to dispatch to `qml.ops.LinearCombination`. For more information,
- check out the [updated operator troubleshooting page](https://docs.pennylane.ai/en/stable/news/new_opmath.html).
+* Legacy operator arithmetic has been removed. This includes `qml.ops.Hamiltonian`,
+ `qml.operation.Tensor`, `qml.operation.enable_new_opmath`, `qml.operation.disable_new_opmath`, and
+ `qml.operation.convert_to_legacy_H`. Note that `qml.Hamiltonian` will continue to dispatch to
+ `qml.ops.LinearCombination`. For more information, check out the
+ [updated operator troubleshooting page](https://docs.pennylane.ai/en/stable/news/new_opmath.html).
[(#6548)](https://github.com/PennyLaneAI/pennylane/pull/6548)
[(#6602)](https://github.com/PennyLaneAI/pennylane/pull/6602)
[(#6589)](https://github.com/PennyLaneAI/pennylane/pull/6589)
-* The developer-facing `qml.utils` module has been removed. Specifically, the
-following 4 sets of functions have been either moved or removed[(#6588)](https://github.com/PennyLaneAI/pennylane/pull/6588):
-
- * `qml.utils._flatten`, `qml.utils.unflatten` has been moved and renamed to `qml.optimize.qng._flatten_np` and `qml.optimize.qng._unflatten_np` respectively.
+* The developer-facing `qml.utils` module has been removed.
+ [(#6588)](https://github.com/PennyLaneAI/pennylane/pull/6588):
- * `qml.utils._inv_dict` and `qml._get_default_args` have been removed.
+ Specifically, the following 4 sets of functions have been either moved or removed:
+ * `qml.utils._flatten`, `qml.utils.unflatten` has been moved and renamed to `qml.optimize.qng._flatten_np` and `qml.optimize.qng._unflatten_np` respectively.
+ * `qml.utils._inv_dict` and `qml._get_default_args` have been removed.
+ * `qml.utils.pauli_eigs` has been moved to `qml.pauli.utils`.
+ * `qml.utils.expand_vector` has been moved to `qml.math.expand_vector`.
- * `qml.utils.pauli_eigs` has been moved to `qml.pauli.utils`.
-
- * `qml.utils.expand_vector` has been moved to `qml.math.expand_vector`.
-
-* The `qml.qinfo` module has been removed. Please see the respective functions in the `qml.math` and `qml.measurements`
- modules instead.
+* The `qml.qinfo` module has been removed. Please use the corresponding functions in the `qml.math`
+ and `qml.measurements` modules instead.
[(#6584)](https://github.com/PennyLaneAI/pennylane/pull/6584)
* Top level access to `Device`, `QubitDevice`, and `QutritDevice` have been removed. Instead, they
- are available as `qml.devices.LegacyDevice`, `qml.devices.QubitDevice`, and `qml.devices.QutritDevice`
- respectively.
+ are available as `qml.devices.LegacyDevice`, `qml.devices.QubitDevice`, and
+ `qml.devices.QutritDevice`, respectively.
[(#6537)](https://github.com/PennyLaneAI/pennylane/pull/6537)
-* The `'ancilla'` argument for `qml.iterative_qpe` has been removed. Instead, use the `'aux_wire'` argument.
+* The `'ancilla'` argument for `qml.iterative_qpe` has been removed. Instead, use the `'aux_wire'`
+ argument.
[(#6532)](https://github.com/PennyLaneAI/pennylane/pull/6532)
* The `qml.BasisStatePreparation` template has been removed. Instead, use `qml.BasisState`.
[(#6528)](https://github.com/PennyLaneAI/pennylane/pull/6528)
-* The `qml.workflow.set_shots` helper function has been removed. We no longer interact with the legacy device interface in our code.
- Instead, shots should be specified on the tape, and the device should use these shots.
+* The `qml.workflow.set_shots` helper function has been removed. We no longer interact with the
+ legacy device interface in our code. Instead, shots should be specified on the tape, and the
+ device should use these shots.
[(#6534)](https://github.com/PennyLaneAI/pennylane/pull/6534)
-* `QNode.gradient_fn` has been removed. Please use `QNode.diff_method` instead. `QNode.get_gradient_fn` can also be used to
- process the diff method.
+* `QNode.gradient_fn` has been removed. Please use `QNode.diff_method` instead.
+ `QNode.get_gradient_fn` can also be used to process the differentiation method.
[(#6535)](https://github.com/PennyLaneAI/pennylane/pull/6535)
* The `qml.QubitStateVector` template has been removed. Instead, use `qml.StatePrep`.
@@ -575,53 +1050,54 @@ following 4 sets of functions have been either moved or removed[(#6588)](https:/
[(#6530)](https://github.com/PennyLaneAI/pennylane/pull/6530)
[(#6561)](https://github.com/PennyLaneAI/pennylane/pull/6561)
+* The developer-facing ``qml.drawer.MPLDrawer`` argument `n_wires` has been replaced with `wire_map`,
+ which contains more complete information about wire labels and order. This allows the new functionality
+ to specify `wire_options` for specific wires when using string wire labels or non-sequential wire ordering.
+ [(#6805)](https://github.com/PennyLaneAI/pennylane/pull/6805)
+
Deprecations 👋
-* The `tape` and `qtape` properties of `QNode` have been deprecated.
- Instead, use the `qml.workflow.construct_tape` function.
+* The `tape` and `qtape` properties of `QNode` have been deprecated. Instead, use the
+ `qml.workflow.construct_tape` function.
[(#6583)](https://github.com/PennyLaneAI/pennylane/pull/6583)
[(#6650)](https://github.com/PennyLaneAI/pennylane/pull/6650)
-* The `max_expansion` argument in `qml.devices.preprocess.decompose` is deprecated and will be removed in v0.41.
+* The `max_expansion` argument in `qml.devices.preprocess.decompose` is deprecated and will be
+ removed in v0.41.
[(#6400)](https://github.com/PennyLaneAI/pennylane/pull/6400)
-* The `decomp_depth` argument in `qml.transforms.set_decomposition` is deprecated and will be removed in v0.41.
+* The `decomp_depth` argument in `qml.transforms.set_decomposition` is deprecated and will be
+ removed in v0.41.
[(#6400)](https://github.com/PennyLaneAI/pennylane/pull/6400)
-* The `output_dim` property of `qml.tape.QuantumScript` has been deprecated.
-Instead, use method `shape` of `QuantumScript` or `MeasurementProcess` to get the
-same information.
+* The `output_dim` property of `qml.tape.QuantumScript` has been deprecated. Instead, use method
+ `shape` of `QuantumScript` or `MeasurementProcess` to get the same information.
[(#6577)](https://github.com/PennyLaneAI/pennylane/pull/6577)
-* The `QNode.get_best_method` and `QNode.best_method_str` methods have been deprecated.
- Instead, use the `qml.workflow.get_best_diff_method` function.
+* The `QNode.get_best_method` and `QNode.best_method_str` methods have been deprecated. Instead, use
+ the `qml.workflow.get_best_diff_method` function.
[(#6418)](https://github.com/PennyLaneAI/pennylane/pull/6418)
-* The `qml.execute` `gradient_fn` keyword argument has been renamed `diff_method`,
- to better align with the termionology used by the `QNode`.
- `gradient_fn` will be removed in v0.41.
+* The `qml.execute` `gradient_fn` keyword argument has been renamed to `diff_method` to better
+ align with the termionology used by the QNode. `gradient_fn` will be removed in v0.41.
[(#6549)](https://github.com/PennyLaneAI/pennylane/pull/6549)
+* The old `qml.qsvt` functionality is moved to `qml.qsvt_legacy`
+ and is now deprecated. It will be removed in v0.41.
+ [(#6520)](https://github.com/PennyLaneAI/pennylane/pull/6520)
+
+
Documentation 📝
-* The docstrings for `qml.qchem.Molecule` and `qml.qchem.molecular_hamiltonian` have been updated to include a
- note that says that they are not compatible with qjit or jit.
+* The docstrings for `qml.qchem.Molecule` and `qml.qchem.molecular_hamiltonian` have been updated to
+ include a note that says that they are not compatible with `qjit` or `jit`.
[(#6702)](https://github.com/PennyLaneAI/pennylane/pull/6702)
-* Updated the documentation of `TrotterProduct` to include the impact of the operands in the
- Hamiltonian on the strucutre of the created circuit. Included an illustrative example on this.
+* The documentation of `TrotterProduct` has been updated to include the impact of the operands in
+ the Hamiltonian on the structure of the created circuit.
[(#6629)](https://github.com/PennyLaneAI/pennylane/pull/6629)
-* Add reporting of test warnings as failures.
- [(#6217)](https://github.com/PennyLaneAI/pennylane/pull/6217)
-
-* Add a warning message to Gradients and training documentation about ComplexWarnings.
- [(#6543)](https://github.com/PennyLaneAI/pennylane/pull/6543)
-
-* Added `opengraph.png` asset and configured `opengraph` metadata image. Overrode the documentation landing page `meta-description`.
- [(#6696)](https://github.com/PennyLaneAI/pennylane/pull/6696)
-
-* Updated the documentation of `QSVT` to include examples for different block encodings.
+* The documentation of `QSVT` has been updated to include examples for different block encodings.
[(#6673)](https://github.com/PennyLaneAI/pennylane/pull/6673)
* The link to `qml.ops.one_qubit_transform` was fixed in the `QubitUnitary` docstring.
@@ -629,42 +1105,44 @@ same information.
Bug fixes 🐛
-* Adds validation so the device vjp is only used when the device actually supports it.
+* Validation has been added to ensure that the device vjp is only used when the device actually
+ supports it.
[(#6755)](https://github.com/PennyLaneAI/pennylane/pull/6755/)
-* `qml.counts` returns all outcomes when the `all_outcomes` argument is `True` and mid-circuit measurements are present.
+* `qml.counts` now returns all outcomes when the `all_outcomes` argument is `True` and mid-circuit
+ measurements are present.
[(#6732)](https://github.com/PennyLaneAI/pennylane/pull/6732)
-* `qml.ControlledQubitUnitary` has consistent behaviour with program capture enabled.
+* `qml.ControlledQubitUnitary` now has consistent behaviour with program capture enabled.
[(#6719)](https://github.com/PennyLaneAI/pennylane/pull/6719)
-* The `Wires` object throws a `TypeError` if `wires=None`.
+* The `Wires` object now throws a `TypeError` if `wires=None`.
[(#6713)](https://github.com/PennyLaneAI/pennylane/pull/6713)
[(#6720)](https://github.com/PennyLaneAI/pennylane/pull/6720)
-* The `qml.Hermitian` class no longer checks that the provided matrix is hermitian.
- The reason for this removal is to allow for faster execution and avoid incompatibilities with `jax.jit`.
+* The `qml.Hermitian` class no longer checks that the provided matrix is hermitian. The reason for
+ this removal is to allow for faster execution and avoid incompatibilities with `jax.jit`.
[(#6642)](https://github.com/PennyLaneAI/pennylane/pull/6642)
-* Subclasses of `qml.ops.Controlled` no longer bind the primitives of their base operators when program capture
- is enabled.
+* Subclasses of `qml.ops.Controlled` no longer bind the primitives of their base operators when
+ program capture is enabled.
[(#6672)](https://github.com/PennyLaneAI/pennylane/pull/6672)
* The `qml.HilbertSchmidt` and `qml.LocalHilbertSchmidt` templates now apply the complex conjugate
of the unitaries instead of the adjoint, providing the correct result.
[(#6604)](https://github.com/PennyLaneAI/pennylane/pull/6604)
-* `QNode` return behaviour is now consistent for lists and tuples.
+* QNode return behaviour is now consistent for lists and tuples.
[(#6568)](https://github.com/PennyLaneAI/pennylane/pull/6568)
-* `qml.QNode` now accepts arguments with types defined in libraries that are not necessarily
- in the list of supported interfaces, such as the `Graph` class defined in `networkx`.
+* QNodes now accept arguments with types defined in libraries that are not necessarily in the list
+ of supported interfaces, such as the `Graph` class defined in `networkx`.
[(#6600)](https://github.com/PennyLaneAI/pennylane/pull/6600)
-* `qml.math.get_deep_interface` now works properly for autograd arrays.
+* `qml.math.get_deep_interface` now works properly for Autograd arrays.
[(#6557)](https://github.com/PennyLaneAI/pennylane/pull/6557)
-* Fixed `Identity.__repr__` to return correct wires list.
+* Printing instances of `qml.Identity` now returns the correct wires list.
[(#6506)](https://github.com/PennyLaneAI/pennylane/pull/6506)
Contributors ✍️
@@ -692,4 +1170,4 @@ Anton Naim Ibrahim,
Andrija Paurevic,
Justin Pickering,
Jay Soni,
-David Wierichs.
+David Wierichs.
\ No newline at end of file
diff --git a/pennylane/_version.py b/pennylane/_version.py
index 15db8ba2cd5..e13f6bf4d72 100644
--- a/pennylane/_version.py
+++ b/pennylane/_version.py
@@ -16,4 +16,4 @@
Version number (major.minor.patch[-label])
"""
-__version__ = "0.41.0-dev4"
+__version__ = "0.41.0-dev6"
diff --git a/pennylane/bose/bosonic_mapping.py b/pennylane/bose/bosonic_mapping.py
index 87112648b8e..01f34b3cd2e 100644
--- a/pennylane/bose/bosonic_mapping.py
+++ b/pennylane/bose/bosonic_mapping.py
@@ -51,8 +51,8 @@ def binary_mapping(
The mapping procedure is described in equations :math:`27-29` in `arXiv:1507.03271 `_.
Args:
- bose_operator(BoseWord, BoseSentence): the bosonic operator
- n_states(int): maximum number of allowed bosonic states
+ bose_operator (BoseWord, BoseSentence): the bosonic operator
+ n_states (int): Maximum number of allowed bosonic states. Defaults to ``2``.
ps (bool): Whether to return the result as a ``PauliSentence`` instead of an
operator. Defaults to ``False``.
wire_map (dict): A dictionary defining how to map the states of
@@ -61,7 +61,7 @@ def binary_mapping(
tol (float): tolerance for discarding the imaginary part of the coefficients
Returns:
- Union[PauliSentence, Operator]: A linear combination of qubit operators
+ Union[PauliSentence, Operator]: a linear combination of qubit operators
**Example**
@@ -177,16 +177,16 @@ def unary_mapping(
Args:
bose_operator(BoseWord, BoseSentence): the bosonic operator
- n_states(int): maximum number of allowed bosonic states
- ps (bool): Whether to return the result as a PauliSentence instead of an
- operator. Defaults to False.
+ n_states(int): Maximum number of allowed bosonic states. Defaults to ``2``.
+ ps (bool): Whether to return the result as a ``PauliSentence`` instead of an
+ operator. Defaults to ``False``.
wire_map (dict): A dictionary defining how to map the states of
- the Bose operator to qubit wires. If None, integers used to
- label the bosonic states will be used as wire labels. Defaults to None.
+ the Bose operator to qubit wires. If ``None``, integers used to
+ label the bosonic states will be used as wire labels. Defaults to ``None``.
tol (float): tolerance for discarding the imaginary part of the coefficients
Returns:
- Union[PauliSentence, Operator]: A linear combination of qubit operators.
+ Union[PauliSentence, Operator]: a linear combination of qubit operators
**Example**
@@ -329,11 +329,11 @@ def christiansen_mapping(
Args:
bose_operator(BoseWord, BoseSentence): the bosonic operator
- ps (bool): Whether to return the result as a PauliSentence instead of an
- operator. Defaults to False.
+ ps (bool): Whether to return the result as a ``PauliSentence`` instead of an
+ operator. Defaults to ``False``.
wire_map (dict): A dictionary defining how to map the states of
- the Bose operator to qubit wires. If None, integers used to
- label the bosonic states will be used as wire labels. Defaults to None.
+ the Bose operator to qubit wires. If ``None``, integers used to
+ label the bosonic states will be used as wire labels. Defaults to ``None``.
tol (float): tolerance for discarding the imaginary part of the coefficients
Returns:
diff --git a/pennylane/capture/autograph/transformer.py b/pennylane/capture/autograph/transformer.py
index 685bdb00fa7..ce859a955d7 100644
--- a/pennylane/capture/autograph/transformer.py
+++ b/pennylane/capture/autograph/transformer.py
@@ -125,8 +125,8 @@ def run_autograph(fn):
).
Args:
- fn (Callable): the callable to be converted. This could be a function, a QNode, or another callable object.
- For a QNode, the ``Qnode.func`` will be converted. For another callable object, a function calling the
+ fn (Callable): The callable to be converted. This could be a function, a QNode, or another callable object.
+ For a QNode, the ``QNode.func`` will be converted. For another callable object, a function calling the
object will be converted.
Returns:
diff --git a/pennylane/capture/base_interpreter.py b/pennylane/capture/base_interpreter.py
index b23f6be3e6b..4af11cb6198 100644
--- a/pennylane/capture/base_interpreter.py
+++ b/pennylane/capture/base_interpreter.py
@@ -84,13 +84,14 @@ def interpret_measurement(self, measurement):
Now the interpreter can be used to transform functions and jaxpr:
+ >>> qml.capture.enable()
>>> interpreter = SimplifyInterpreter()
>>> def f(x):
... qml.RX(x, 0)**2
... qml.adjoint(qml.Z(0))
... return qml.expval(qml.X(0) + qml.X(0))
>>> simplified_f = interpreter(f)
- >>> print(qml.draw(simplified_f)(0.5)
+ >>> print(qml.draw(simplified_f)(0.5))
0: ──RX(1.00)──Z─┤ <2.00*X>
>>> jaxpr = jax.make_jaxpr(f)(0.5)
>>> interpreter.eval(jaxpr.jaxpr, [], 0.5)
@@ -98,13 +99,12 @@ def interpret_measurement(self, measurement):
**Handling higher order primitives:**
- Two main strategies exist for handling higher order primitives (primitives with jaxpr as metatdata).
-
- 1) Structure preserving. Tracing the execution preserves the higher order primitive.
- 2) Structure flattening. Tracing the execution eliminates the higher order primitive.
+ Two main strategies exist for handling higher order primitives (primitives with jaxpr as metadata).
+ The first one is structure preserving (tracing the execution preserves the higher order primitive),
+ and the second one is structure flattening (tracing the execution eliminates the higher order primitive).
Compilation transforms, like the above ``SimplifyInterpreter``, may prefer to handle higher order primitives
- via a structure preserving method. After transforming the jaxpr, the `for_loop` still exists. This maintains
+ via a structure-preserving method. After transforming the jaxpr, the `for_loop` still exists. This maintains
the compact structure of the jaxpr and reduces the size of the program. This behavior is the default.
>>> def g(x):
@@ -117,23 +117,26 @@ def interpret_measurement(self, measurement):
>>> jax.make_jaxpr(interpreter(g))(0.5)
{ lambda ; a:f32[]. let
_:f32[] = for_loop[
- jaxpr_body_fn={ lambda ; b:i32[] c:f32[]. let
+ args_slice=slice(0, None, None)
+ consts_slice=slice(0, 0, None)
+ jaxpr_body_fn={ lambda ; b:i32[] c:f32[]. let
d:f32[] = convert_element_type[new_dtype=float32 weak_type=True] b
e:f32[] = mul c d
_:AbstractOperator() = RX[n_wires=1] e 0
- in (c,) }
- n_consts=0
+ in (c,) }
] 0 3 1 1.0
f:AbstractOperator() = PauliZ[n_wires=1] 0
g:AbstractOperator() = SProd[_pauli_rep=4.0 * Z(0)] 4.0 f
h:AbstractMeasurement(n_wires=None) = expval_obs g
- in (h,) }
+ in (h,) }
Accumulation transforms, like device execution or conversion to tapes, may need to flatten out
the higher order primitive to execute it.
.. code-block:: python
+ import copy
+
class AccumulateOps(PlxprInterpreter):
def __init__(self, ops=None):
@@ -152,14 +155,14 @@ def _(self, start, stop, step, *invals, jaxpr_body_fn, consts_slice, args_slice)
state = invals[args_slice]
for i in range(start, stop, step):
- state = copy(self).eval(jaxpr_body_fn, consts, i, *state)
+ state = copy.copy(self).eval(jaxpr_body_fn, consts, i, *state)
return state
>>> @qml.for_loop(3)
... def loop(i, x):
... qml.RX(x, i)
... return x
- >>> accumulator = AccumlateOps()
+ >>> accumulator = AccumulateOps()
>>> accumulator(loop)(0.5)
>>> accumulator.ops
[RX(0.5, wires=[0]), RX(0.5, wires=[1]), RX(0.5, wires=[2])]
@@ -222,7 +225,7 @@ def setup(self) -> None:
def cleanup(self) -> None:
"""Perform any final steps after iterating through all equations.
- Blank by default, this method can clean up instance variables. Particularily,
+ Blank by default, this method can clean up instance variables. Particularly,
this method can be used to deallocate qubits and registers when converting to
a Catalyst variant jaxpr.
"""
diff --git a/pennylane/compiler/compiler.py b/pennylane/compiler/compiler.py
index cbb38243d83..6c38bb3d203 100644
--- a/pennylane/compiler/compiler.py
+++ b/pennylane/compiler/compiler.py
@@ -23,7 +23,7 @@
from packaging.version import Version
-PL_CATALYST_MIN_VERSION = Version("0.9.0")
+PL_CATALYST_MIN_VERSION = Version("0.10.0")
class CompileError(Exception):
diff --git a/pennylane/debugging/snapshot.py b/pennylane/debugging/snapshot.py
index 84ee2d17311..430ba08f494 100644
--- a/pennylane/debugging/snapshot.py
+++ b/pennylane/debugging/snapshot.py
@@ -72,7 +72,7 @@ def snapshots(tape: QuantumScript) -> tuple[QuantumScriptBatch, PostprocessingFn
If tape splitting is carried out, the transform will be conservative about the wires that it includes in each tape.
So, if all operations preceding a snapshot in a 3-qubit circuit has been applied to only one wire,
- the tape would only be looking at this wire. This can be overriden by the configuration of the execution device
+ the tape would only be looking at this wire. This can be overridden by the configuration of the execution device
and its nature.
Regardless of the transform's behaviour, the output is a dictionary where each key is either
diff --git a/pennylane/devices/__init__.py b/pennylane/devices/__init__.py
index 463f6c3f163..40546c10bcd 100644
--- a/pennylane/devices/__init__.py
+++ b/pennylane/devices/__init__.py
@@ -24,6 +24,7 @@
.. autosummary::
:toctree: api
+ capabilities
default_qubit
default_gaussian
default_mixed
diff --git a/pennylane/devices/_qubit_device.py b/pennylane/devices/_qubit_device.py
index e777ceceacc..148b4331dc0 100644
--- a/pennylane/devices/_qubit_device.py
+++ b/pennylane/devices/_qubit_device.py
@@ -634,7 +634,7 @@ def statistics(
results = []
for m in measurements:
- # TODO: Remove this when all overriden measurements support the `MeasurementProcess` class
+ # TODO: Remove this when all overridden measurements support the `MeasurementProcess` class
if isinstance(m.mv, list):
# MeasurementProcess stores information needed for processing if terminal measurement
# uses a list of mid-circuit measurement values
@@ -642,7 +642,7 @@ def statistics(
else:
obs = m.obs or m.mv
obs = m if obs is None else obs
- # Check if there is an overriden version of the measurement process
+ # Check if there is an overridden version of the measurement process
if method := getattr(self, self.measurement_map[type(m)], False):
if isinstance(m, MeasurementTransform):
result = method(tape=circuit)
diff --git a/pennylane/devices/capabilities.py b/pennylane/devices/capabilities.py
index bbe6614ae67..da1cc5aa5ea 100644
--- a/pennylane/devices/capabilities.py
+++ b/pennylane/devices/capabilities.py
@@ -56,19 +56,19 @@ class ExecutionCondition(Enum):
@dataclass
class OperatorProperties:
- """Information about support for each operation.
-
- Attributes:
- invertible (bool): Whether the adjoint of the operation is also supported.
- controllable (bool): Whether the operation can be controlled.
- differentiable (bool): Whether the operation is supported for device gradients.
- conditions (list[ExecutionCondition]): Execution conditions that the operation must meet.
- """
+ """Information about support for each operation."""
invertible: bool = False
+ """Whether the adjoint of the operation is also supported."""
+
controllable: bool = False
+ """Whether the operation can be controlled."""
+
differentiable: bool = False
+ """Whether the operation is supported for device gradients."""
+
conditions: list[ExecutionCondition] = field(default_factory=list)
+ """Execution conditions that the operation must meet."""
def __and__(self, other: "OperatorProperties") -> "OperatorProperties":
# Take the intersection of support but the union of constraints (conditions)
@@ -103,31 +103,37 @@ def _get_supported_base_op(op_name: str, op_dict: dict[str, OperatorProperties])
@dataclass
class DeviceCapabilities: # pylint: disable=too-many-instance-attributes
- """Capabilities of a quantum device.
-
- Attributes:
- operations: Operations natively supported by the backend device.
- observables: Observables that the device can measure.
- measurement_processes: List of measurement processes supported by the backend device.
- qjit_compatible (bool): Whether the device is compatible with qjit.
- runtime_code_generation (bool): Whether the device requires run time generation of the quantum circuit.
- dynamic_qubit_management (bool): Whether the device supports dynamic qubit allocation/deallocation.
- overlapping_observables (bool): Whether the device supports measuring overlapping observables on the same tape.
- non_commuting_observables (bool): Whether the device supports measuring non-commuting observables on the same tape.
- initial_state_prep (bool): Whether the device supports initial state preparation.
- supported_mcm_methods (list[str]): List of supported methods of mid-circuit measurements.
- """
+ """Capabilities of a quantum device."""
operations: dict[str, OperatorProperties] = field(default_factory=dict)
+ """Operations natively supported by the backend device."""
+
observables: dict[str, OperatorProperties] = field(default_factory=dict)
+ """Observables that the device can measure."""
+
measurement_processes: dict[str, list[ExecutionCondition]] = field(default_factory=dict)
+ """List of measurement processes supported by the backend device."""
+
qjit_compatible: bool = False
+ """Whether the device is compatible with qjit."""
+
runtime_code_generation: bool = False
+ """Whether the device requires run time generation of the quantum circuit."""
+
dynamic_qubit_management: bool = False
+ """Whether the device supports dynamic qubit allocation/deallocation."""
+
overlapping_observables: bool = True
+ """Whether the device supports measuring overlapping observables on the same tape."""
+
non_commuting_observables: bool = False
+ """Whether the device supports measuring non-commuting observables on the same tape."""
+
initial_state_prep: bool = False
+ """Whether the device supports initial state preparation."""
+
supported_mcm_methods: list[str] = field(default_factory=list)
+ """List of supported methods of mid-circuit measurements."""
def filter(self, finite_shots: bool) -> "DeviceCapabilities":
"""Returns the device capabilities conditioned on the given program features."""
@@ -162,8 +168,8 @@ def from_toml_file(cls, file_path: str, runtime_interface="pennylane") -> "Devic
Args:
file_path (str): The path to the TOML file.
runtime_interface (str): The runtime execution interface to get the capabilities for.
- Acceptable values are "pennylane" and "qjit". Use "pennylane" for capabilities of
- the device's implementation of `Device.execute`, and "qjit" for capabilities of
+ Acceptable values are ``"pennylane"`` and ``"qjit"``. Use ``"pennylane"`` for capabilities of
+ the device's implementation of `Device.execute`, and ``"qjit"`` for capabilities of
the runtime execution function used by a qjit-compiled workflow.
"""
@@ -356,7 +362,7 @@ def parse_toml_document(document: dict) -> DeviceCapabilities:
"""Parses a TOML document into a DeviceCapabilities object.
This function will ignore sections that are specific to either runtime interface, such as
- "qjit.operators.gates". To include these sections, use :func:`update_device_capabilities`
+ ``"qjit.operators.gates"``. To include these sections, use :func:`update_device_capabilities`
on the capabilities object returned from this function.
"""
@@ -429,7 +435,7 @@ def observable_stopping_condition(obs: qml.operation.Operator) -> bool:
def validate_mcm_method(capabilities: DeviceCapabilities, mcm_method: str, shots_present: bool):
- """Validates an MCM method against the device's capabilities.'"""
+ """Validates an MCM method against the device's capabilities."""
if mcm_method is None or mcm_method == "deferred":
return # no need to validate if requested deferred or if no method is requested.
diff --git a/pennylane/devices/default_clifford.py b/pennylane/devices/default_clifford.py
index 11eaec5c577..773de2fcf67 100644
--- a/pennylane/devices/default_clifford.py
+++ b/pennylane/devices/default_clifford.py
@@ -579,7 +579,7 @@ def simulate(
tableau_simulator.do_circuit(stim_circuit)
global_phase = qml.GlobalPhase(qml.math.sum(op.data[0] for op in global_phase_ops))
- # Perform measurments based on whether shots are provided
+ # Perform measurements based on whether shots are provided
if circuit.shots:
meas_results = self.measure_statistical(circuit, stim_circuit, seed=seed)
else:
diff --git a/pennylane/devices/default_tensor.py b/pennylane/devices/default_tensor.py
index cb19f625d9c..100953c3777 100644
--- a/pennylane/devices/default_tensor.py
+++ b/pennylane/devices/default_tensor.py
@@ -258,8 +258,18 @@ def circuit(num_qubits):
We can provide additional keyword arguments to the device to customize the simulation. These are passed to the ``quimb`` backend.
.. note::
- Be aware that `quimb` uses multi-threading with `numba `_ as well as for linear algebra operations with `numpy.linalg `_. Proper setting of the corresponding environment variables (e.g. `OMP_NUM_THREADS`, `OPENBLAS_NUM_THREADS`, `NUMBA_NUM_THREADS` etc.) depending on your hardware is highly recommended and will have a strong impact on the device's performance.
- To avoid a slowdown in performance for circuits with more than 10 wires, we recommend setting the environment variable relevant for your BLAS library backend (e.g. `OMP_NUM_THREADS=1`, `OPENBLAS_NUM_THREADS=1` or `MKL_NUM_THREADS=1`), depending on your NumPy package & associated libraries. Alternatively, you can use `threadpoolctl `_ to limit the threads within your executing script. For optimal performance you can adjust the number of threads to find the best fit for your workload.
+
+ Be aware that ``quimb`` uses multi-threading with `numba `_
+ as well as for linear algebra operations with
+ `numpy.linalg `_. Proper setting of
+ the corresponding environment variables (e.g. ``OMP_NUM_THREADS``, ``OPENBLAS_NUM_THREADS``, ``NUMBA_NUM_THREADS`` etc.)
+ depending on your hardware is highly recommended and will have a strong impact on the device's performance.
+
+ To avoid a slowdown in performance for circuits with more than 10 wires, we recommend setting the environment variable relevant
+ for your BLAS library backend (e.g. ``OMP_NUM_THREADS=1``, ``OPENBLAS_NUM_THREADS=1`` or ``MKL_NUM_THREADS=1``), depending on your
+ NumPy package and associated libraries. Alternatively, you can use `threadpoolctl `_ to
+ limit the threads within your executing script. For optimal performance you can adjust the number of threads to find the best fit
+ for your workload.
.. details::
:title: Usage with MPS Method
@@ -403,7 +413,7 @@ def __init__(
# options for TN
self._local_simplify = kwargs.get("local_simplify", "ADCRS")
- # options both for MPS and TN
+ # options for both MPS and TN
self._contraction_optimizer = kwargs.get("contraction_optimizer", "auto-hq")
self._contract = None
diff --git a/pennylane/devices/device_api.py b/pennylane/devices/device_api.py
index 3969a3f011f..de9eaad6732 100644
--- a/pennylane/devices/device_api.py
+++ b/pennylane/devices/device_api.py
@@ -655,7 +655,7 @@ def execute(
>>> dev.execute([tape])
(array(1.0),)
- If the script has multiple measurments, then the device should return a tuple of measurements.
+ If the script has multiple measurements, then the device should return a tuple of measurements.
>>> tape = qml.tape.QuantumTape(measurements=[qml.expval(qml.Z(0)), qml.probs(wires=(0,1))])
>>> tape.shape(dev)
diff --git a/pennylane/devices/execution_config.py b/pennylane/devices/execution_config.py
index fc953a2dc22..2cf78cee394 100644
--- a/pennylane/devices/execution_config.py
+++ b/pennylane/devices/execution_config.py
@@ -26,7 +26,7 @@ class MCMConfig:
"""A class to store mid-circuit measurement configurations."""
mcm_method: Optional[str] = None
- """Which mid-circuit measurement strategy to use. Use ``"deferred"`` for the deferred
+ """The mid-circuit measurement strategy to use. Use ``"deferred"`` for the deferred
measurements principle and ``"one-shot"`` if using finite shots to execute the circuit for
each shot separately. Any other value will be passed to the device, and the device is
expected to handle mid-circuit measurements using the requested method. If not specified,
@@ -34,7 +34,7 @@ class MCMConfig:
postselect_mode: Optional[str] = None
"""How postselection is handled with finite-shots. If ``"hw-like"``, invalid shots will be
- discarded and only results for valid shots will be returned. In this case, less samples
+ discarded and only results for valid shots will be returned. In this case, fewer samples
may be returned than the original number of shots. If ``"fill-shots"``, the returned samples
will be of the same size as the original number of shots. If not specified, the device will
decide which mode to use. Note that internally ``"pad-invalid-samples"`` is used internally
diff --git a/pennylane/devices/qubit_mixed/apply_operation.py b/pennylane/devices/qubit_mixed/apply_operation.py
index aa6caa8bad6..a5417bb4b23 100644
--- a/pennylane/devices/qubit_mixed/apply_operation.py
+++ b/pennylane/devices/qubit_mixed/apply_operation.py
@@ -325,7 +325,7 @@ def apply_operation(
.. code-block:: python
@apply_operation.register
- def _(op: type_op, state):
+ def _(op: type_op, state, is_state_batched=False, **kwargs):
# custom op application method here
**Example:**
diff --git a/pennylane/devices/qubit_mixed/measure.py b/pennylane/devices/qubit_mixed/measure.py
index 09284594880..cb661174282 100644
--- a/pennylane/devices/qubit_mixed/measure.py
+++ b/pennylane/devices/qubit_mixed/measure.py
@@ -197,7 +197,7 @@ def calculate_variance(
to simulate readout errors.
Returns:
- TensorLike: the variance of the observable wrt the state.
+ TensorLike: the variance of the observable with respect to the state.
"""
probs = calculate_probability(measurementprocess, state, is_state_batched, readout_errors)
eigvals = math.asarray(measurementprocess.eigvals(), dtype="float64")
@@ -222,7 +222,7 @@ def calculate_expval_sum_of_terms(
to simulate readout errors.
Returns:
- TensorLike: the expectation value of the sum of Hamiltonian observable wrt the state.
+ TensorLike: the expectation value of the sum of Hamiltonian observable with respect to the state.
"""
# Recursively call measure on each term, so that the best measurement method can
# be used for each term
diff --git a/pennylane/devices/qubit_mixed/sampling.py b/pennylane/devices/qubit_mixed/sampling.py
index 585977cef5d..ed165dca58a 100644
--- a/pennylane/devices/qubit_mixed/sampling.py
+++ b/pennylane/devices/qubit_mixed/sampling.py
@@ -105,7 +105,7 @@ def _measure_with_samples_diagonalizing_gates(
Args:
mp (~.measurements.SampleMeasurement): The sample measurement to perform
- state (TensorLike): The state vector to sample from
+ state (TensorLike): The density matrix to sample from
shots (~.measurements.Shots): The number of samples to take
is_state_batched (bool): whether the state is batched or not
rng (Union[None, int, array_like[int], SeedSequence, BitGenerator, Generator]): A
@@ -224,7 +224,7 @@ def sample_state(
"""Returns a series of computational basis samples of a state.
Args:
- state (array[complex]): A state vector to be sampled
+ state (array[complex]): A density matrix to be sampled
shots (int): The number of samples to take
is_state_batched (bool): whether the state is batched or not
wires (Sequence[int]): The wires to sample
@@ -271,7 +271,7 @@ def measure_with_samples(
Args:
mp (SampleMeasurement): The sample measurement to perform
- state (np.ndarray[complex]): The state vector to sample from
+ state (np.ndarray[complex]): The density matrix to sample from
shots (Shots): The number of samples to take
is_state_batched (bool): whether the state is batched or not
rng (Union[None, int, array_like[int], SeedSequence, BitGenerator, Generator]): A
diff --git a/pennylane/devices/qubit_mixed/simulate.py b/pennylane/devices/qubit_mixed/simulate.py
index 426f1d2a248..392a1919d81 100644
--- a/pennylane/devices/qubit_mixed/simulate.py
+++ b/pennylane/devices/qubit_mixed/simulate.py
@@ -55,7 +55,7 @@ def get_final_state(circuit, debugger=None, interface=None, **kwargs):
interface (str): The machine learning interface to create the initial state with
Returns:
- Tuple[TensorLike, bool]: A tuple containing the final state of the quantum script and
+ tuple[TensorLike, bool]: A tuple containing the final state of the quantum script and
whether the state has a batch dimension.
"""
diff --git a/pennylane/drawer/mpldrawer.py b/pennylane/drawer/mpldrawer.py
index 64e92a50cfe..d3b089317d9 100644
--- a/pennylane/drawer/mpldrawer.py
+++ b/pennylane/drawer/mpldrawer.py
@@ -25,6 +25,8 @@
except (ModuleNotFoundError, ImportError) as e: # pragma: no cover
has_mpl = False
+# pylint: disable=too-many-positional-arguments
+
def _to_tuple(a):
"""Converts int or iterable to tuple"""
@@ -63,20 +65,20 @@ class MPLDrawer:
Args:
n_layers (int): the number of layers
- n_wires (int): the number of wires
+ wire_map (dict): the wires to be drawn. A dict mapping wire label to index (from top to bottom) in the figure
Keyword Args:
c_wires=0 (int): the number of classical wires to leave space for.
wire_options=None (dict): matplotlib configuration options for drawing the wire lines
figsize=None (Iterable): Allows users to specify the size of the figure manually. Defaults
- to scale with the size of the circuit via ``n_layers`` and ``n_wires``.
+ to scale with the size of the circuit via ``n_layers`` and ``len(wire_map)``.
fig=None (matplotlib Figure): Allows users to specify the figure window to plot to.
**Example**
.. code-block:: python
- drawer = qml.drawer.MPLDrawer(n_wires=5, n_layers=6)
+ drawer = qml.drawer.MPLDrawer(wire_map={i: i for i in range(5)}, n_layers=6)
drawer.label(["0", "a", r"$|\Psi\rangle$", r"$|\theta\rangle$", "aux"])
@@ -165,7 +167,7 @@ class MPLDrawer:
.. code-block:: python
wire_options = {"color": "indigo", "linewidth": 4}
- drawer = MPLDrawer(n_wires=2, n_layers=4, wire_options=wire_options)
+ drawer = MPLDrawer(wire_map={0: 0, 1: 1}, n_layers=4, wire_options=wire_options)
label_options = {"fontsize": "x-large", 'color': 'indigo'}
drawer.label(["0", "a"], text_options=label_options)
@@ -203,7 +205,7 @@ class MPLDrawer:
.. code-block:: python
- drawer = MPLDrawer(2, 2)
+ drawer = MPLDrawer(2, {0:0, 1:1})
drawer.box_gate(layer=0, wires=1, text="X")
drawer.box_gate(layer=1, wires=1, text="Y")
@@ -255,7 +257,7 @@ class MPLDrawer:
_cwire_scaling = 0.25
"""The distance between successive control wires."""
- def __init__(self, n_layers, n_wires, c_wires=0, wire_options=None, figsize=None, fig=None):
+ def __init__(self, n_layers, wire_map, c_wires=0, wire_options=None, figsize=None, fig=None):
if not has_mpl: # pragma: no cover
raise ImportError(
"Module matplotlib is required for ``MPLDrawer`` class. "
@@ -263,7 +265,7 @@ def __init__(self, n_layers, n_wires, c_wires=0, wire_options=None, figsize=None
)
self.n_layers = n_layers
- self.n_wires = n_wires
+ self.n_wires = len(wire_map)
## Creating figure and ax
@@ -300,14 +302,14 @@ def __init__(self, n_layers, n_wires, c_wires=0, wire_options=None, figsize=None
# Adding wire lines with individual styles based on wire_options
self._wire_lines = []
- for wire in range(self.n_wires):
- specific_options = wire_specific_options.get(wire, {})
+ for wire_label, idx in wire_map.items():
+ specific_options = wire_specific_options.get(wire_label, {})
line_options = {**global_options, **specific_options}
# Create Line2D with the combined options
line = plt.Line2D(
(-1, self.n_layers),
- (wire, wire),
+ (idx, idx),
zorder=1,
**line_options,
)
@@ -349,7 +351,7 @@ def label(self, labels, text_options=None):
.. code-block:: python
- drawer = MPLDrawer(n_wires=2, n_layers=1)
+ drawer = MPLDrawer(wire_map={0:0, 1:1}, n_layers=1)
drawer.label(["a", "b"])
.. figure:: ../../_static/drawer/labels.png
@@ -363,7 +365,7 @@ def label(self, labels, text_options=None):
.. code-block:: python
- drawer = MPLDrawer(n_wires=2, n_layers=1)
+ drawer = MPLDrawer(wire_map={0:0, 1:1}, n_layers=1)
drawer.label(["a", "b"], text_options={"color": "indigo", "fontsize": "xx-large"})
.. figure:: ../../_static/drawer/labels_formatted.png
@@ -419,7 +421,7 @@ def box_gate(self, layer, wires, text="", box_options=None, text_options=None, *
.. code-block:: python
- drawer = MPLDrawer(n_wires=2, n_layers=1)
+ drawer = MPLDrawer(wire_map={0:0, 1:1}, n_layers=1)
drawer.box_gate(layer=0, wires=(0, 1), text="CY")
@@ -441,7 +443,7 @@ def box_gate(self, layer, wires, text="", box_options=None, text_options=None, *
box_options = {'facecolor': 'lightcoral', 'edgecolor': 'maroon', 'linewidth': 5}
text_options = {'fontsize': 'xx-large', 'color': 'maroon'}
- drawer = MPLDrawer(n_wires=2, n_layers=1)
+ drawer = MPLDrawer(wire_map={0:0, 1:1}, n_layers=1)
drawer.box_gate(layer=0, wires=(0, 1), text="CY",
box_options=box_options, text_options=text_options)
@@ -456,7 +458,7 @@ def box_gate(self, layer, wires, text="", box_options=None, text_options=None, *
.. code-block:: python
- drawer = MPLDrawer(n_layers=4, n_wires=2)
+ drawer = MPLDrawer(n_layers=4, wire_map={0:0, 1:1})
drawer.box_gate(layer=0, wires=0, text="A longer label")
drawer.box_gate(layer=0, wires=1, text="Label")
@@ -615,7 +617,7 @@ def ctrl(self, layer, wires, wires_target=None, control_values=None, options=Non
.. code-block:: python
- drawer = MPLDrawer(n_wires=2, n_layers=3)
+ drawer = MPLDrawer(wire_map={0:0, 1:1}, n_layers=3)
drawer.ctrl(layer=0, wires=0, wires_target=1)
drawer.ctrl(layer=1, wires=(0, 1), control_values=[0, 1])
@@ -714,7 +716,7 @@ def CNOT(self, layer, wires, control_values=None, options=None):
.. code-block:: python
- drawer = MPLDrawer(n_wires=2, n_layers=2)
+ drawer = MPLDrawer(wire_map={0:0, 1:1}, n_layers=2)
drawer.CNOT(0, (0, 1))
@@ -778,7 +780,7 @@ def SWAP(self, layer, wires, options=None):
.. code-block:: python
- drawer = MPLDrawer(n_wires=2, n_layers=2)
+ drawer = MPLDrawer(wire_map={0:0, 1:1}, n_layers=2)
drawer.SWAP(0, (0, 1))
@@ -848,7 +850,7 @@ def measure(self, layer, wires, text=None, box_options=None, lines_options=None)
.. code-block:: python
- drawer = MPLDrawer(n_wires=2, n_layers=1)
+ drawer = MPLDrawer(wire_map={0:0, 1:1}, n_layers=1)
drawer.measure(layer=0, wires=0)
measure_box = {'facecolor': 'white', 'edgecolor': 'indigo'}
@@ -996,7 +998,7 @@ def cond(self, layer, measured_layer, wires, wires_target, options=None):
.. code-block:: python
- drawer = MPLDrawer(n_wires=3, n_layers=4)
+ drawer = MPLDrawer(wire_map={0:0, 1:1, 2:2}, n_layers=4)
drawer.cond(layer=1, measured_layer=0, wires=[0], wires_target=[1])
diff --git a/pennylane/drawer/tape_mpl.py b/pennylane/drawer/tape_mpl.py
index b1aa8f93af4..a41104e578d 100644
--- a/pennylane/drawer/tape_mpl.py
+++ b/pennylane/drawer/tape_mpl.py
@@ -241,7 +241,7 @@ def _tape_mpl(tape, wire_order=None, show_all_wires=False, decimals=None, *, fig
drawer = MPLDrawer(
n_layers=n_layers,
- n_wires=n_wires,
+ wire_map=wire_map,
c_wires=len(bit_map),
wire_options=wire_options,
fig=fig,
diff --git a/pennylane/gradients/vjp.py b/pennylane/gradients/vjp.py
index 650624b0c09..614406294b0 100644
--- a/pennylane/gradients/vjp.py
+++ b/pennylane/gradients/vjp.py
@@ -320,7 +320,7 @@ def vjp(tape, dy, gradient_fn, gradient_kwargs=None):
Executing the VJP tapes, and applying the processing function:
>>> dev = qml.device("default.qubit")
- >>> vjp = fn(qml.execute(vjp_tapes, dev, gradient_fn=qml.gradients.param_shift, interface="torch"))
+ >>> vjp = fn(qml.execute(vjp_tapes, dev, diff_method=qml.gradients.param_shift, interface="torch"))
>>> vjp
tensor([-1.1562e-01, -1.3862e-02, -9.0841e-03, -1.5214e-16, -4.8217e-01,
2.1329e-17], dtype=torch.float64, grad_fn=)
@@ -474,7 +474,7 @@ def batch_vjp(tapes, dys, gradient_fn, reduction="append", gradient_kwargs=None)
Executing the VJP tapes, and applying the processing function:
>>> dev = qml.device("default.qubit")
- >>> vjps = fn(qml.execute(vjp_tapes, dev, gradient_fn=qml.gradients.param_shift, interface="torch"))
+ >>> vjps = fn(qml.execute(vjp_tapes, dev, diff_method=qml.gradients.param_shift, interface="torch"))
>>> vjps
[tensor([-1.1562e-01, -1.3862e-02, -9.0841e-03, -1.5214e-16, -4.8217e-01,
2.1329e-17], dtype=torch.float64, grad_fn=),
diff --git a/pennylane/io.py b/pennylane/io.py
index c003ab881e3..d2678727e1d 100644
--- a/pennylane/io.py
+++ b/pennylane/io.py
@@ -107,7 +107,7 @@ def circuit():
``measurements=None``.
If an existing ``QuantumCircuit`` already contains measurements, ``from_qiskit``
- will return those measurements, provided that they are not overriden as shown above.
+ will return those measurements, provided that they are not overridden as shown above.
These measurements can be used, e.g., for conditioning with
:func:`qml.cond() <~.cond>`, or simply included directly within the QNode's return:
diff --git a/pennylane/labs/dla/cartan_subalgebra.py b/pennylane/labs/dla/cartan_subalgebra.py
index abd6b1e67ee..e0236595a3e 100644
--- a/pennylane/labs/dla/cartan_subalgebra.py
+++ b/pennylane/labs/dla/cartan_subalgebra.py
@@ -76,7 +76,7 @@ def cartan_subalgebra(
A non-unique CSA is a maximal Abelian subalgebra in the horizontal subspace :math:`\mathfrak{m}` of a Cartan decomposition.
Note that this is sometimes called a horizontal CSA, and is different from the definition of a CSA on `Wikipedia `__.
- .. seealso:: :func:`~cartan_decomp`, :func:`~structure_constants`, `The KAK theorem (demo) `__
+ .. seealso:: :func:`~cartan_decomp`, :func:`~structure_constants`, `The KAK decomposition theory(demo) `__, `The KAK decomposition in practice (demo) `__.
Args:
g (List[Union[PauliSentence, np.ndarray]]): Lie algebra :math:`\mathfrak{g}`, which is assumed to be ordered as :math:`\mathfrak{g} = \mathfrak{k} \oplus \mathfrak{m}`
diff --git a/pennylane/labs/dla/variational_kak.py b/pennylane/labs/dla/variational_kak.py
index 04d950f1d21..dbcdf886c11 100644
--- a/pennylane/labs/dla/variational_kak.py
+++ b/pennylane/labs/dla/variational_kak.py
@@ -82,7 +82,7 @@ def Kc(theta_opt: Iterable[float], k: Iterable[Operator]):
Instead of relying on having Pauli words, we use the adjoint representation
for a more general evaluation of the cost function. The rest is the same.
- .. seealso:: `Theory demo on KAK theorem `__, `demo on KAK decomposition in practice `__,
+ .. seealso:: `The KAK decomposition theory(demo) `__, `The KAK decomposition in practice (demo) `__.
Args:
H (Union[Operator, PauliSentence, np.ndarray]): Hamiltonian to decompose
diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py
index a62ac627a11..77e32ed632b 100644
--- a/pennylane/labs/resource_estimation/__init__.py
+++ b/pennylane/labs/resource_estimation/__init__.py
@@ -62,6 +62,8 @@
~ResourceCY
~ResourceCZ
~ResourceDoubleExcitation
+ ~ResourceDoubleExcitationMinus
+ ~ResourceDoubleExcitationPlus
~ResourceFermionicSWAP
~ResourceGlobalPhase
~ResourceHadamard
@@ -72,6 +74,10 @@
~ResourceIsingZZ
~ResourceMultiControlledX
~ResourceMultiRZ
+ ~ResourceMultiControlledX
+ ~ResourceOrbitalRotation
+ ~ResourcePauliRot
+ ~ResourcePhaseShift
~ResourcePauliRot
~ResourcePhaseShift
~ResourcePSWAP
@@ -80,6 +86,7 @@
~ResourceRY
~ResourceRZ
~ResourceS
+ ~ResourceSingleExcitation
~ResourceSingleExcitationMinus
~ResourceSingleExcitationPlus
~ResourceSWAP
@@ -152,6 +159,8 @@
ResourceCY,
ResourceCZ,
ResourceDoubleExcitation,
+ ResourceDoubleExcitationMinus,
+ ResourceDoubleExcitationPlus,
ResourceFermionicSWAP,
ResourceGlobalPhase,
ResourceHadamard,
@@ -162,6 +171,7 @@
ResourceIsingZZ,
ResourceMultiControlledX,
ResourceMultiRZ,
+ ResourceOrbitalRotation,
ResourcePauliRot,
ResourcePow,
ResourcePSWAP,
@@ -171,6 +181,7 @@
ResourceRY,
ResourceRZ,
ResourceS,
+ ResourceSingleExcitation,
ResourceSingleExcitationMinus,
ResourceSingleExcitationPlus,
ResourceSWAP,
diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py
index f7442259280..459e6dd195f 100644
--- a/pennylane/labs/resource_estimation/ops/__init__.py
+++ b/pennylane/labs/resource_estimation/ops/__init__.py
@@ -20,6 +20,8 @@
from .qubit import (
ResourceDoubleExcitation,
+ ResourceDoubleExcitationMinus,
+ ResourceDoubleExcitationPlus,
ResourceFermionicSWAP,
ResourceHadamard,
ResourceIsingXX,
@@ -27,6 +29,7 @@
ResourceIsingYY,
ResourceIsingZZ,
ResourceMultiRZ,
+ ResourceOrbitalRotation,
ResourcePauliRot,
ResourcePhaseShift,
ResourcePSWAP,
@@ -35,6 +38,7 @@
ResourceRY,
ResourceRZ,
ResourceS,
+ ResourceSingleExcitation,
ResourceSingleExcitationMinus,
ResourceSingleExcitationPlus,
ResourceSWAP,
diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py
index 7f9b59729db..9d612a75e98 100644
--- a/pennylane/labs/resource_estimation/ops/identity.py
+++ b/pennylane/labs/resource_estimation/ops/identity.py
@@ -34,6 +34,20 @@ def resource_params(self) -> dict:
def resource_rep(cls, **kwargs) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @classmethod
+ def controlled_resource_decomp(
+ cls, num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceOperator):
"""Resource class for the GlobalPhase gate."""
@@ -48,3 +62,43 @@ def resource_params(self) -> dict:
@classmethod
def resource_rep(cls, **kwargs) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+
+ @staticmethod
+ def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]:
+ """The adjoint of a global phase is itself another global phase"""
+ return {re.ResourceGlobalPhase.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ """
+ Resources:
+ The resources are generated from the identity that a global phase
+ controlled on a single qubit is equivalent to a local phase shift on that qubit.
+
+ This idea can be generalized to a multi-qubit global phase by introducing one
+ 'clean' auxilliary qubit which gets reset at the end of the computation. In this
+ case, we sandwich the phase shift operation with two multi-controlled X gates.
+ """
+ if num_ctrl_wires == 1:
+ gate_types = {re.ResourcePhaseShift.resource_rep(): 1}
+
+ if num_ctrl_values:
+ gate_types[re.ResourceX.resource_rep()] = 2
+
+ return gate_types
+
+ ps = re.ResourcePhaseShift.resource_rep()
+ mcx = re.ResourceMultiControlledX.resource_rep(
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+
+ return {ps: 1, mcx: 2}
+
+ @staticmethod
+ def pow_resource_decomp(z) -> Dict[re.CompressedResourceOp, int]:
+ """Taking arbitrary powers of a global phase produces another global phase"""
+ return {re.ResourceGlobalPhase.resource_rep(): 1}
diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py
index e30bb3f99ea..702dd2df1a0 100644
--- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py
+++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py
@@ -17,7 +17,7 @@
import pennylane as qml
import pennylane.labs.resource_estimation as re
-# pylint: disable=arguments-differ,too-many-ancestors
+# pylint: disable=arguments-differ,too-many-ancestors,too-many-arguments
class ResourceCH(qml.CH, re.ResourceOperator):
@@ -59,6 +59,24 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ return {
+ re.ResourceControlled.resource_rep(
+ re.ResourceHadamard, {}, num_ctrl_wires + 1, num_ctrl_values, num_work_wires
+ ): 1
+ }
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {} if z % 2 == 0 else {cls.resource_rep(): 1}
+
class ResourceCY(qml.CY, re.ResourceOperator):
r"""Resource class for the CY gate.
@@ -78,9 +96,11 @@ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]:
cnot = re.ResourceCNOT.resource_rep()
s = re.ResourceS.resource_rep()
+ s_dag = re.ResourceAdjoint.resource_rep(re.ResourceS, {})
gate_types[cnot] = 1
- gate_types[s] = 1 + 3 # S^dagg = 3*S in cost TODO: Update with Adjoint(S)
+ gate_types[s] = 1
+ gate_types[s_dag] = 1
return gate_types
@@ -91,6 +111,24 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ return {
+ re.ResourceControlled.resource_rep(
+ re.ResourceY, {}, num_ctrl_wires + 1, num_ctrl_values, num_work_wires
+ ): 1
+ }
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {} if z % 2 == 0 else {cls.resource_rep(): 1}
+
class ResourceCZ(qml.CZ, re.ResourceOperator):
r"""Resource class for the CZ gate.
@@ -123,6 +161,27 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0:
+ return {re.ResourceCCZ.resource_rep(): 1}
+
+ return {
+ re.ResourceControlled.resource_rep(
+ re.ResourceZ, {}, num_ctrl_wires + 1, num_ctrl_values, num_work_wires
+ ): 1
+ }
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {} if z % 2 == 0 else {cls.resource_rep(): 1}
+
class ResourceCSWAP(qml.CSWAP, re.ResourceOperator):
r"""Resource class for the CSWAP gate.
@@ -162,6 +221,24 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ return {
+ re.ResourceControlled.resource_rep(
+ re.ResourceSWAP, {}, num_ctrl_wires + 1, num_ctrl_values, num_work_wires
+ ): 1
+ }
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {} if z % 2 == 0 else {cls.resource_rep(): 1}
+
class ResourceCCZ(qml.CCZ, re.ResourceOperator):
r"""Resource class for the CCZ gate.
@@ -193,6 +270,24 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls, **kwargs):
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ return {
+ re.ResourceControlled.resource_rep(
+ re.ResourceZ, {}, num_ctrl_wires + 2, num_ctrl_values, num_work_wires
+ ): 1
+ }
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {} if z % 2 == 0 else {cls.resource_rep(): 1}
+
class ResourceCNOT(qml.CNOT, re.ResourceOperator):
r"""Resource class for the CNOT gate.
@@ -213,6 +308,27 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @classmethod
+ def controlled_resource_decomp(
+ cls, num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0:
+ return {re.ResourceToffoli.resource_rep(): 1}
+
+ return {
+ re.ResourceMultiControlledX.resource_rep(
+ num_ctrl_wires + 1, num_ctrl_values, num_work_wires
+ ): 1
+ }
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {} if z % 2 == 0 else {cls.resource_rep(): 1}
+
class ResourceToffoli(qml.Toffoli, re.ResourceOperator):
r"""Resource class for the Toffoli gate.
@@ -244,12 +360,14 @@ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]:
h = re.ResourceHadamard.resource_rep()
s = re.ResourceS.resource_rep()
cz = re.ResourceCZ.resource_rep()
+ t_dag = re.ResourceAdjoint.resource_rep(re.ResourceT, {})
gate_types[cnot] = 9
gate_types[h] = 3
gate_types[s] = 1
gate_types[cz] = 1
- gate_types[t] = 2 + 2 * (7) # T^dagg = 7*T in cost TODO: Update with Adjoint(T)
+ gate_types[t] = 2
+ gate_types[t_dag] = 2
return gate_types
@@ -275,10 +393,12 @@ def textbook_resource_decomp() -> Dict[re.CompressedResourceOp, int]:
cnot = re.ResourceCNOT.resource_rep()
t = re.ResourceT.resource_rep()
h = re.ResourceHadamard.resource_rep()
+ t_dag = re.ResourceAdjoint.resource_rep(re.ResourceT, {})
gate_types[cnot] = 6
gate_types[h] = 2
- gate_types[t] = 7
+ gate_types[t] = 4
+ gate_types[t_dag] = 3
return gate_types
@@ -289,6 +409,24 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ return {
+ re.ResourceMultiControlledX.resource_rep(
+ num_ctrl_wires + 2, num_ctrl_values, num_work_wires
+ ): 1
+ }
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {} if z % 2 == 0 else {cls.resource_rep(): 1}
+
class ResourceMultiControlledX(qml.MultiControlledX, re.ResourceOperator):
r"""Resource class for the MultiControlledX gate.
@@ -368,6 +506,38 @@ def resource_rep(
},
)
+ @classmethod
+ def adjoint_resource_decomp(
+ cls, num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(num_ctrl_wires, num_ctrl_values, num_work_wires): 1}
+
+ @classmethod
+ def controlled_resource_decomp(
+ cls,
+ outer_num_ctrl_wires,
+ outer_num_ctrl_values,
+ outer_num_work_wires,
+ num_ctrl_wires,
+ num_ctrl_values,
+ num_work_wires,
+ ) -> Dict[re.CompressedResourceOp, int]:
+ return cls.resources(
+ outer_num_ctrl_wires + num_ctrl_wires,
+ outer_num_ctrl_values + num_ctrl_values,
+ outer_num_work_wires + num_work_wires,
+ )
+
+ @classmethod
+ def pow_resource_decomp(
+ cls, z, num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ return (
+ {}
+ if z % 2 == 0
+ else {cls.resource_rep(num_ctrl_wires, num_ctrl_values, num_work_wires): 1}
+ )
+
class ResourceCRX(qml.CRX, re.ResourceOperator):
r"""Resource class for the CRX gate.
@@ -404,6 +574,24 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ return {
+ re.ResourceControlled.resource_rep(
+ re.ResourceRX, {}, num_ctrl_wires + 1, num_ctrl_values, num_work_wires
+ ): 1
+ }
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
class ResourceCRY(qml.CRY, re.ResourceOperator):
r"""Resource class for the CRY gate.
@@ -439,6 +627,24 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ return {
+ re.ResourceControlled.resource_rep(
+ re.ResourceRY, {}, num_ctrl_wires + 1, num_ctrl_values, num_work_wires
+ ): 1
+ }
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
class ResourceCRZ(qml.CRZ, re.ResourceOperator):
r"""Resource class for the CRZ gate.
@@ -474,6 +680,24 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ return {
+ re.ResourceControlled.resource_rep(
+ re.ResourceRZ, {}, num_ctrl_wires + 1, num_ctrl_values, num_work_wires
+ ): 1
+ }
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
class ResourceCRot(qml.CRot, re.ResourceOperator):
r"""Resource class for the CRot gate.
@@ -520,6 +744,24 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ return {
+ re.ResourceControlled.resource_rep(
+ re.ResourceRot, {}, num_ctrl_wires + 1, num_ctrl_values, num_work_wires
+ ): 1
+ }
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceOperator):
r"""Resource class for the ControlledPhaseShift gate.
@@ -551,3 +793,21 @@ def resource_params(self):
@classmethod
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ return {
+ re.ResourceControlled.resource_rep(
+ re.ResourcePhaseShift, {}, num_ctrl_wires + 1, num_ctrl_values, num_work_wires
+ ): 1
+ }
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py
index 7a3511022d4..c48d611d86a 100644
--- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py
+++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py
@@ -34,6 +34,36 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ if num_ctrl_wires == 1:
+ gate_types = {re.ResourceCH.resource_rep(): 1}
+
+ if num_ctrl_values:
+ gate_types[re.ResourceX.resource_rep()] = 2
+
+ return gate_types
+
+ ch = re.ResourceCH.resource_rep()
+ mcx = re.ResourceMultiControlledX.resource_rep(
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+ return {ch: 1, mcx: 2}
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ if z % 2 == 0:
+ return {}
+ return {cls.resource_rep(): 1}
+
class ResourceS(qml.S, re.ResourceOperator):
"""Resource class for the S gate."""
@@ -41,7 +71,7 @@ class ResourceS(qml.S, re.ResourceOperator):
@staticmethod
def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]:
gate_types = {}
- t = ResourceT.resource_rep(**kwargs)
+ t = ResourceT.resource_rep()
gate_types[t] = 2
return gate_types
@@ -53,6 +83,34 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 3}
+
+ @staticmethod
+ def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires):
+ if num_ctrl_wires == 1:
+ gate_types = {re.ResourceControlledPhaseShift.resource_rep(): 1}
+
+ if num_ctrl_values:
+ gate_types[re.ResourceX.resource_rep()] = 2
+
+ return gate_types
+
+ cs = re.ResourceControlledPhaseShift.resource_rep()
+ mcx = re.ResourceMultiControlledX.resource_rep(
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+ return {cs: 1, mcx: 2}
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ if (mod_4 := z % 4) == 0:
+ return {}
+ return {cls.resource_rep(): mod_4}
+
class ResourceSWAP(qml.SWAP, re.ResourceOperator):
r"""Resource class for the SWAP gate.
@@ -104,6 +162,36 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ if num_ctrl_wires == 1:
+ gate_types = {re.ResourceCSWAP.resource_rep(): 1}
+
+ if num_ctrl_values:
+ gate_types[re.ResourceX.resource_rep()] = 2
+
+ return gate_types
+
+ cnot = re.ResourceCNOT.resource_rep()
+ mcx = re.ResourceMultiControlledX.resource_rep(
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+ return {cnot: 2, mcx: 1}
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ if z % 2 == 0:
+ return {}
+ return {cls.resource_rep(): 1}
+
class ResourceT(qml.T, re.ResourceOperator):
"""Resource class for the T gate."""
@@ -119,14 +207,44 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ """Resources obtained from the identity T^8 = I."""
+ return {cls.resource_rep(): 7}
+
+ @staticmethod
+ def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires):
+ if num_ctrl_wires == 1:
+ gate_types = {re.ResourceControlledPhaseShift.resource_rep(): 1}
+
+ if num_ctrl_values:
+ gate_types[re.ResourceX.resource_rep()] = 2
+
+ return gate_types
+
+ ct = re.ResourceControlledPhaseShift.resource_rep()
+ mcx = re.ResourceMultiControlledX.resource_rep(
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+ return {ct: 1, mcx: 2}
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ """Resources obtained from the identity T^8 = I."""
+ if (mod_8 := z % 8) == 0:
+ return {}
+ return {cls.resource_rep(): mod_8}
+
class ResourceX(qml.X, re.ResourceOperator):
"""Resource class for the X gate."""
@staticmethod
def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]:
- s = re.ResourceS.resource_rep(**kwargs)
- h = re.ResourceHadamard.resource_rep(**kwargs)
+ s = re.ResourceS.resource_rep()
+ h = re.ResourceHadamard.resource_rep()
gate_types = {}
gate_types[s] = 2
@@ -141,14 +259,58 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires):
+ if num_ctrl_wires > 2:
+ return {
+ re.ResourceMultiControlledX.resource_rep(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ): 1
+ }
+
+ gate_types = {}
+ if num_ctrl_values:
+ gate_types[re.ResourceX.resource_rep()] = 2 * num_ctrl_values
+
+ if num_ctrl_wires == 1:
+ gate_types[re.ResourceCNOT.resource_rep()] = 1
+
+ if num_ctrl_wires == 2:
+ gate_types[re.ResourceToffoli.resource_rep()] = 1
+
+ return gate_types
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ if z % 2 == 0:
+ return {}
+ return {cls.resource_rep(): 1}
+
class ResourceY(qml.Y, re.ResourceOperator):
"""Resource class for the Y gate."""
@staticmethod
def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]:
- s = re.ResourceS.resource_rep(**kwargs)
- h = re.ResourceHadamard.resource_rep(**kwargs)
+ r"""
+ The resources are defined using the identity:
+
+ .. math::
+
+ \begin{align}
+ \hat{Y} &= \hat{S} \cdot \hat{X} \cdot \hat{S}^{\dagger}, \\
+ \hat{X} &= \hat{H} \cdot \hat{Z} \cdot \hat{H}, \\
+ \hat{Z} &= \hat{S}^{2}, \\
+ \hat{S}^{\dagger} &= 3 \hat{S}.
+ \end{align}
+
+ """
+ s = re.ResourceS.resource_rep()
+ h = re.ResourceHadamard.resource_rep()
gate_types = {}
gate_types[s] = 6
@@ -163,13 +325,43 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ if num_ctrl_wires == 1:
+ gate_types = {re.ResourceCY.resource_rep(): 1}
+
+ if num_ctrl_values:
+ gate_types[re.ResourceX.resource_rep()] = 2
+
+ return gate_types
+
+ cy = re.ResourceCY.resource_rep()
+ mcx = re.ResourceMultiControlledX.resource_rep(
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+ return {cy: 1, mcx: 2}
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ if z % 2 == 0:
+ return {}
+ return {cls.resource_rep(): 1}
+
class ResourceZ(qml.Z, re.ResourceOperator):
"""Resource class for the Z gate."""
@staticmethod
def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]:
- s = re.ResourceS.resource_rep(**kwargs)
+ s = re.ResourceS.resource_rep()
gate_types = {}
gate_types[s] = 2
@@ -182,3 +374,38 @@ def resource_params(self) -> dict:
@classmethod
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ if num_ctrl_wires > 2:
+ cz = re.ResourceCZ.resource_rep()
+ mcx = re.ResourceMultiControlledX.resource_rep(
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+ return {cz: 1, mcx: 2}
+
+ gate_types = {}
+ if num_ctrl_wires == 1:
+ gate_types[re.ResourceCZ.resource_rep()] = 1
+
+ if num_ctrl_wires == 2:
+ gate_types[re.ResourceCCZ.resource_rep()] = 1
+
+ if num_ctrl_values:
+ gate_types[re.ResourceX.resource_rep()] = 2 * num_ctrl_values
+
+ return gate_types
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ if z % 2 == 0:
+ return {}
+ return {cls.resource_rep(): 1}
diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py
index acfbb755da2..56c0bb88716 100644
--- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py
+++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py
@@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
r"""Resource operators for parametric multi qubit operations."""
+from typing import Dict
+
import pennylane as qml
import pennylane.labs.resource_estimation as re
@@ -31,8 +33,8 @@ class ResourceMultiRZ(qml.MultiRZ, re.ResourceOperator):
@staticmethod
def _resource_decomp(num_wires, **kwargs):
- cnot = re.CompressedResourceOp(re.ResourceCNOT, {})
- rz = re.CompressedResourceOp(re.ResourceRZ, {})
+ cnot = re.ResourceCNOT.resource_rep()
+ rz = re.ResourceRZ.resource_rep()
gate_types = {}
gate_types[cnot] = 2 * (num_wires - 1)
@@ -47,6 +49,39 @@ def resource_params(self):
def resource_rep(cls, num_wires):
return re.CompressedResourceOp(cls, {"num_wires": num_wires})
+ @classmethod
+ def adjoint_resource_decomp(cls, num_wires) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(num_wires=num_wires): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires,
+ num_ctrl_values,
+ num_work_wires,
+ num_wires,
+ ) -> Dict[re.CompressedResourceOp, int]:
+ if num_ctrl_values == 0:
+ cnot = re.ResourceCNOT.resource_rep()
+ ctrl_rz = re.ResourceControlled.resource_rep(
+ base_class=re.ResourceRZ,
+ base_params={},
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+
+ gate_types = {}
+ gate_types[cnot] = 2 * (num_wires - 1)
+ gate_types[ctrl_rz] = 1
+
+ return gate_types
+
+ raise re.ResourcesNotDefined
+
+ @classmethod
+ def pow_resource_decomp(cls, z, num_wires) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(num_wires=num_wires): 1}
+
class ResourcePauliRot(qml.PauliRot, re.ResourceOperator):
r"""Resource class for the PauliRot gate.
@@ -79,8 +114,9 @@ def _resource_decomp(pauli_word, **kwargs):
active_wires = len(pauli_word.replace("I", ""))
h = re.ResourceHadamard.resource_rep()
- s = re.ResourceS.resource_rep() # TODO: add Adjoint(S) in the symbolic PRs
+ s = re.ResourceS.resource_rep()
rz = re.ResourceRZ.resource_rep()
+ s_dagg = re.ResourceAdjoint.resource_rep(re.ResourceS, {})
cnot = re.ResourceCNOT.resource_rep()
h_count = 0
@@ -95,7 +131,8 @@ def _resource_decomp(pauli_word, **kwargs):
gate_types = {}
gate_types[h] = h_count
- gate_types[s] = s_count + (3 * s_count) # S^dagg = 3*S in cost
+ gate_types[s] = s_count
+ gate_types[s_dagg] = s_count
gate_types[rz] = 1
gate_types[cnot] = 2 * (active_wires - 1)
@@ -110,6 +147,67 @@ def resource_params(self):
def resource_rep(cls, pauli_word):
return re.CompressedResourceOp(cls, {"pauli_word": pauli_word})
+ @classmethod
+ def adjoint_resource_decomp(cls, pauli_word) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(pauli_word=pauli_word): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires,
+ num_ctrl_values,
+ num_work_wires,
+ pauli_word,
+ ) -> Dict[re.CompressedResourceOp, int]:
+ if set(pauli_word) == {"I"}:
+ ctrl_gp = re.ResourceControlled.resource_rep(
+ re.ResourceGlobalPhase,
+ {},
+ num_ctrl_wires,
+ num_ctrl_values,
+ num_work_wires,
+ )
+ return {ctrl_gp: 1}
+
+ active_wires = len(pauli_word.replace("I", ""))
+
+ h = re.ResourceHadamard.resource_rep()
+ s = re.ResourceS.resource_rep()
+ s_dagg = re.ResourceAdjoint.resource_rep(re.ResourceS, {})
+ cnot = re.ResourceCNOT.resource_rep()
+ ctrl_rz = re.ResourceControlled.resource_rep(
+ base_class=re.ResourceRZ,
+ base_params={},
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+
+ h_count = 0
+ s_count = 0
+
+ for gate in pauli_word:
+ if gate == "X":
+ h_count += 2
+ if gate == "Y":
+ h_count += 2
+ s_count += 1
+
+ gate_types = {}
+ if h_count:
+ gate_types[h] = h_count
+ if s_count:
+ gate_types[s] = s_count
+ gate_types[s_dagg] = s_count
+
+ gate_types[ctrl_rz] = 1
+ gate_types[cnot] = 2 * (active_wires - 1)
+
+ return gate_types
+
+ @classmethod
+ def pow_resource_decomp(cls, z, pauli_word) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(pauli_word=pauli_word): 1}
+
class ResourceIsingXX(qml.IsingXX, re.ResourceOperator):
r"""Resource class for the IsingXX gate.
@@ -134,7 +232,7 @@ class ResourceIsingXX(qml.IsingXX, re.ResourceOperator):
"""
@staticmethod
- def _resource_decomp(*args, **kwargs):
+ def _resource_decomp(**kwargs):
cnot = re.ResourceCNOT.resource_rep()
rx = re.ResourceRX.resource_rep()
@@ -148,9 +246,40 @@ def resource_params(self):
return {}
@classmethod
- def resource_rep(cls, *args):
+ def resource_rep(cls):
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires,
+ num_ctrl_values,
+ num_work_wires,
+ ) -> Dict[re.CompressedResourceOp, int]:
+ if num_ctrl_values == 0:
+ cnot = re.ResourceCNOT.resource_rep()
+ ctrl_rx = re.ResourceControlled.resource_rep(
+ base_class=re.ResourceRX,
+ base_params={},
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+
+ gate_types = {}
+ gate_types[cnot] = 2
+ gate_types[ctrl_rx] = 1
+
+ return gate_types
+ raise re.ResourcesNotDefined
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
class ResourceIsingYY(qml.IsingYY, re.ResourceOperator):
r"""Resource class for the IsingYY gate.
@@ -175,7 +304,7 @@ class ResourceIsingYY(qml.IsingYY, re.ResourceOperator):
"""
@staticmethod
- def _resource_decomp(*args, **kwargs):
+ def _resource_decomp(**kwargs):
cy = re.ops.ResourceCY.resource_rep()
ry = re.ops.ResourceRY.resource_rep()
@@ -189,9 +318,40 @@ def resource_params(self):
return {}
@classmethod
- def resource_rep(cls, *args):
+ def resource_rep(cls):
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires,
+ num_ctrl_values,
+ num_work_wires,
+ ) -> Dict[re.CompressedResourceOp, int]:
+ if num_ctrl_values == 0:
+ cy = re.ops.ResourceCY.resource_rep()
+ ctrl_ry = re.ResourceControlled.resource_rep(
+ base_class=re.ResourceRY,
+ base_params={},
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+
+ gate_types = {}
+ gate_types[cy] = 2
+ gate_types[ctrl_ry] = 1
+
+ return gate_types
+ raise re.ResourcesNotDefined
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
class ResourceIsingXY(qml.IsingXY, re.ResourceOperator):
r"""Resource class for the IsingXY gate.
@@ -216,7 +376,7 @@ class ResourceIsingXY(qml.IsingXY, re.ResourceOperator):
"""
@staticmethod
- def _resource_decomp(*args, **kwargs):
+ def _resource_decomp(**kwargs):
h = re.ResourceHadamard.resource_rep()
cy = re.ResourceCY.resource_rep()
ry = re.ResourceRY.resource_rep()
@@ -234,9 +394,50 @@ def resource_params(self):
return {}
@classmethod
- def resource_rep(cls, *args):
+ def resource_rep(cls):
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires,
+ num_ctrl_values,
+ num_work_wires,
+ ) -> Dict[re.CompressedResourceOp, int]:
+ if num_ctrl_values == 0:
+ h = re.ResourceHadamard.resource_rep()
+ cy = re.ResourceCY.resource_rep()
+ ctrl_rx = re.ResourceControlled.resource_rep(
+ base_class=re.ResourceRX,
+ base_params={},
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+ ctrl_ry = re.ResourceControlled.resource_rep(
+ base_class=re.ResourceRY,
+ base_params={},
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+
+ gate_types = {}
+ gate_types[h] = 2
+ gate_types[cy] = 2
+ gate_types[ctrl_ry] = 1
+ gate_types[ctrl_rx] = 1
+
+ return gate_types
+ raise re.ResourcesNotDefined
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
class ResourceIsingZZ(qml.IsingZZ, re.ResourceOperator):
r"""Resource class for the IsingZZ gate.
@@ -261,7 +462,7 @@ class ResourceIsingZZ(qml.IsingZZ, re.ResourceOperator):
"""
@staticmethod
- def _resource_decomp(*args, **kwargs):
+ def _resource_decomp(**kwargs):
cnot = re.ResourceCNOT.resource_rep()
rz = re.ResourceRZ.resource_rep()
@@ -275,9 +476,40 @@ def resource_params(self):
return {}
@classmethod
- def resource_rep(cls, *args):
+ def resource_rep(cls):
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires,
+ num_ctrl_values,
+ num_work_wires,
+ ) -> Dict[re.CompressedResourceOp, int]:
+ if num_ctrl_values == 0:
+ cnot = re.ResourceCNOT.resource_rep()
+ ctrl_rz = re.ResourceControlled.resource_rep(
+ base_class=re.ResourceRZ,
+ base_params={},
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+
+ gate_types = {}
+ gate_types[cnot] = 2
+ gate_types[ctrl_rz] = 1
+
+ return gate_types
+ raise re.ResourcesNotDefined
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
class ResourcePSWAP(qml.PSWAP, re.ResourceOperator):
r"""Resource class for the PSWAP gate.
@@ -299,7 +531,7 @@ class ResourcePSWAP(qml.PSWAP, re.ResourceOperator):
"""
@staticmethod
- def _resource_decomp(*args, **kwargs):
+ def _resource_decomp(**kwargs):
swap = re.ResourceSWAP.resource_rep()
cnot = re.ResourceCNOT.resource_rep()
phase = re.ResourcePhaseShift.resource_rep()
@@ -315,5 +547,40 @@ def resource_params(self):
return {}
@classmethod
- def resource_rep(cls, *args):
+ def resource_rep(cls):
return re.CompressedResourceOp(cls, {})
+
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires,
+ num_ctrl_values,
+ num_work_wires,
+ ) -> Dict[re.CompressedResourceOp, int]:
+ if num_ctrl_values == 0:
+ cnot = re.ResourceCNOT.resource_rep()
+ ctrl_swap = re.ResourceControlled.resource_rep(
+ base_class=re.ResourceSWAP,
+ base_params={},
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+ ctrl_ps = re.ResourceControlled.resource_rep(
+ base_class=re.ResourcePhaseShift,
+ base_params={},
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+
+ gate_types = {}
+ gate_types[ctrl_swap] = 1
+ gate_types[cnot] = 2
+ gate_types[ctrl_ps] = 1
+ return gate_types
+
+ raise re.ResourcesNotDefined
diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py
index 572c12f9ebd..ef6eee5c8dd 100644
--- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py
+++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py
@@ -46,7 +46,7 @@ class ResourcePhaseShift(qml.PhaseShift, re.ResourceOperator):
"""
@staticmethod
- def _resource_decomp() -> Dict[re.CompressedResourceOp, int]:
+ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]:
gate_types = {}
rz = re.ResourceRZ.resource_rep()
global_phase = re.ResourceGlobalPhase.resource_rep()
@@ -62,12 +62,44 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ r"""
+ The resources for a multi-controlled phase shift gate are generated using
+ the identity defined in (lemma 7.11) from https://arxiv.org/pdf/quant-ph/9503016.
+ """
+ if num_ctrl_wires == 1:
+ gate_types = {re.ResourceControlledPhaseShift.resource_rep(): 1}
+
+ if num_ctrl_values:
+ gate_types[re.ResourceX.resource_rep()] = 2
+
+ return gate_types
+
+ c_ps = re.ResourceControlledPhaseShift.resource_rep()
+ mcx = re.ResourceMultiControlledX.resource_rep(
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+ return {c_ps: 1, mcx: 2}
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
class ResourceRX(qml.RX, re.ResourceOperator):
"""Resource class for the RX gate."""
@staticmethod
- def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]:
+ def _resource_decomp(config, **kwargs) -> Dict[re.CompressedResourceOp, int]:
return _rotation_resources(epsilon=config["error_rx"])
def resource_params(self) -> dict:
@@ -77,12 +109,60 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ r"""
+ Resources:
+ The resources are taken from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit
+ unitary `_. In combination with the following identity:
+
+ .. math:: \hat{RX} = \hat{H} \cdot \hat{RZ} \cdot \hat{H},
+
+ we can express the :code:`CRX` gate as a :code:`CRZ` gate conjugated by :code:`Hadamard` gates.
+ The expression for controlled-RZ gates is used as defined in the reference above. By replacing
+ the :code:`X` gates with multi-controlled :code:`X` gates, we obtain a controlled-version
+ of that identity.
+ """
+ if num_ctrl_wires == 1:
+ gate_types = {re.ResourceCRX.resource_rep(): 1}
+
+ if num_ctrl_values:
+ gate_types[re.ResourceX.resource_rep()] = 2
+
+ return gate_types
+
+ gate_types = {}
+
+ h = re.ResourceHadamard.resource_rep()
+ rz = re.ResourceRZ.resource_rep()
+ mcx = re.ResourceMultiControlledX.resource_rep(
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+
+ gate_types[mcx] = 2
+ gate_types[rz] = 2
+ gate_types[h] = 2
+
+ return gate_types
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
class ResourceRY(qml.RY, re.ResourceOperator):
"""Resource class for the RY gate."""
@staticmethod
- def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]:
+ def _resource_decomp(config, **kwargs) -> Dict[re.CompressedResourceOp, int]:
return _rotation_resources(epsilon=config["error_ry"])
def resource_params(self) -> dict:
@@ -92,6 +172,46 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ r"""
+ Resources:
+ The resources are taken from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit
+ unitary `_. The resources are derived with the following identity:
+
+ .. math:: \hat{RY}(\theta) = \hat{X} \cdot \hat{RY}(- \theta) \cdot \hat{X}.
+
+ By replacing the :code:`X` gates with multi-controlled :code:`X` gates, we obtain a controlled-version
+ of this identity. Thus we are able to constructively or destructively interfere the gates based on the
+ value of the control qubits.
+ """
+ if num_ctrl_wires == 1:
+ gate_types = {re.ResourceCRY.resource_rep(): 1}
+
+ if num_ctrl_values:
+ gate_types[re.ResourceX.resource_rep()] = 2
+
+ return gate_types
+
+ ry = re.ResourceRY.resource_rep()
+ mcx = re.ResourceMultiControlledX.resource_rep(
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+
+ return {ry: 2, mcx: 2}
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
class ResourceRZ(qml.RZ, re.ResourceOperator):
r"""Resource class for the RZ gate.
@@ -102,7 +222,7 @@ class ResourceRZ(qml.RZ, re.ResourceOperator):
"""
@staticmethod
- def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]:
+ def _resource_decomp(config, **kwargs) -> Dict[re.CompressedResourceOp, int]:
return _rotation_resources(epsilon=config["error_rz"])
def resource_params(self) -> dict:
@@ -112,17 +232,55 @@ def resource_params(self) -> dict:
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ r"""
+ The resources are obtained from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit
+ unitary `_. They are derived from the following identity:
+
+ .. math:: \hat{RZ}(\theta) = \hat{X} \cdot \hat{RZ}(- \theta) \cdot \hat{X}.
+
+ By replacing the :code:`X` gates with multi-controlled :code:`X` gates, we obtain a controlled-version of
+ this identity. Thus we are able to constructively or destructively interfere the gates based on the value
+ of the control qubits.
+ """
+ if num_ctrl_wires == 1:
+ gate_types = {re.ResourceCRZ.resource_rep(): 1}
+
+ if num_ctrl_values:
+ gate_types[re.ResourceX.resource_rep()] = 2
+
+ return gate_types
+
+ rz = re.ResourceRZ.resource_rep()
+ mcx = re.ResourceMultiControlledX.resource_rep(
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+
+ return {rz: 2, mcx: 2}
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
class ResourceRot(qml.Rot, re.ResourceOperator):
"""Resource class for the Rot gate."""
@staticmethod
- def _resource_decomp() -> Dict[re.CompressedResourceOp, int]:
- rx = ResourceRX.resource_rep()
+ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]:
ry = ResourceRY.resource_rep()
rz = ResourceRZ.resource_rep()
- gate_types = {rx: 1, ry: 1, rz: 1}
+ gate_types = {ry: 1, rz: 2}
return gate_types
def resource_params(self):
@@ -131,3 +289,62 @@ def resource_params(self):
@classmethod
def resource_rep(cls) -> re.CompressedResourceOp:
return re.CompressedResourceOp(cls, {})
+
+ @classmethod
+ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
+
+ @staticmethod
+ def controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires
+ ) -> Dict[re.CompressedResourceOp, int]:
+ r"""
+ Resources:
+ The resources are derived from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit
+ unitary `_. The resources are derived with the following identities:
+
+ .. math::
+
+ \begin{align}
+ \hat{RZ}(\theta) = \hat{X} \cdot \hat{RZ}(- \theta) \cdot \hat{X}, \\
+ \hat{RY}(\theta) = \hat{X} \cdot \hat{RY}(- \theta) \cdot \hat{X}.
+ \end{align}
+
+ This identity is applied along with some clever choices for the angle values to combine rotation;
+ the final circuit takes the form:
+
+ .. code-block:: bash
+
+ ctrl: ─────╭●─────────╭●─────────┤
+ trgt: ──RZ─╰X──RZ──RY─╰X──RY──RZ─┤
+
+ The :code:`CNOT` gates are replaced with multi-controlled X gates to generalize to the multi-controlled case.
+
+ """
+ if num_ctrl_wires == 1:
+ gate_types = {re.ResourceCRot.resource_rep(): 1}
+
+ if num_ctrl_values:
+ gate_types[re.ResourceX.resource_rep()] = 2
+
+ return gate_types
+
+ gate_types = {}
+
+ rz = re.ResourceRZ.resource_rep()
+ ry = re.ResourceRY.resource_rep()
+ mcx = re.ResourceMultiControlledX.resource_rep(
+ num_ctrl_wires=num_ctrl_wires,
+ num_ctrl_values=num_ctrl_values,
+ num_work_wires=num_work_wires,
+ )
+
+ gate_types[mcx] = 2
+ gate_types[rz] = 3
+ gate_types[ry] = 2
+
+ return gate_types
+
+ @classmethod
+ def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]:
+ return {cls.resource_rep(): 1}
diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py
index 46e277638b2..e832314d204 100644
--- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py
+++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py
@@ -16,6 +16,52 @@
# pylint: disable=arguments-differ
+class ResourceSingleExcitation(qml.SingleExcitation, re.ResourceOperator):
+ r"""Resource class for the SingleExcitation gate.
+
+ Resources:
+ The resources are obtained by decomposing the following matrix into fundamental gates.
+
+ .. math:: U(\phi) = \begin{bmatrix}
+ 1 & 0 & 0 & 0 \\
+ 0 & \cos(\phi/2) & -\sin(\phi/2) & 0 \\
+ 0 & \sin(\phi/2) & \cos(\phi/2) & 0 \\
+ 0 & 0 & 0 & 1
+ \end{bmatrix}.
+
+ """
+
+ @staticmethod
+ def _resource_decomp(**kwargs):
+ t_dag = re.ResourceAdjoint.resource_rep(re.ResourceT, {})
+ h = re.ResourceHadamard.resource_rep()
+ s = re.ResourceS.resource_rep()
+ s_dag = re.ResourceAdjoint.resource_rep(re.ResourceS, {})
+ cnot = re.ResourceCNOT.resource_rep()
+ rz = re.ResourceRZ.resource_rep()
+ ry = re.ResourceRY.resource_rep()
+ t = re.ResourceT.resource_rep()
+
+ gate_types = {}
+ gate_types[t_dag] = 2
+ gate_types[h] = 4
+ gate_types[s] = 2
+ gate_types[s_dag] = 2
+ gate_types[cnot] = 2
+ gate_types[rz] = 1
+ gate_types[ry] = 1
+ gate_types[t] = 2
+
+ return gate_types
+
+ def resource_params(self):
+ return {}
+
+ @classmethod
+ def resource_rep(cls):
+ return re.CompressedResourceOp(cls, {})
+
+
class ResourceSingleExcitationMinus(qml.SingleExcitationMinus, re.ResourceOperator):
r"""Resource class for the SingleExcitationMinus gate.
@@ -149,6 +195,109 @@ def resource_rep(cls):
return re.CompressedResourceOp(cls, {})
+class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, re.ResourceOperator):
+ r"""Resource class for the DoubleExcitationMinus gate.
+
+
+ Resources:
+ The resources are obtained by decomposing the following mapping into fundamental gates.
+
+ .. math::
+
+ &|0011\rangle \rightarrow \cos(\phi/2) |0011\rangle - \sin(\phi/2) |1100\rangle\\
+ &|1100\rangle \rightarrow \cos(\phi/2) |1100\rangle + \sin(\phi/2) |0011\rangle\\
+ &|x\rangle \rightarrow e^{-i\phi/2} |x\rangle,
+ """
+
+ @staticmethod
+ def _resource_decomp(**kwargs):
+ phase = re.ResourceGlobalPhase.resource_rep()
+ double = re.ResourceDoubleExcitation.resource_rep()
+ ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0)
+ ctrl_phase = re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0)
+
+ gate_types = {}
+ gate_types[phase] = 1
+ gate_types[double] = 1
+ gate_types[ctrl_z] = 2
+ gate_types[ctrl_phase] = 2
+
+ return gate_types
+
+ def resource_params(self):
+ return {}
+
+ @classmethod
+ def resource_rep(cls):
+ return re.CompressedResourceOp(cls, {})
+
+
+class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, re.ResourceOperator):
+ r"""Resource class for the DoubleExcitationPlus gate.
+
+ Resources:
+ The resources are obtained by decomposing the following mapping into fundamental gates.
+
+ .. math::
+
+ &|0011\rangle \rightarrow \cos(\phi/2) |0011\rangle - \sin(\phi/2) |1100\rangle\\
+ &|1100\rangle \rightarrow \cos(\phi/2) |1100\rangle + \sin(\phi/2) |0011\rangle\\
+ &|x\rangle \rightarrow e^{i\phi/2} |x\rangle,
+ """
+
+ @staticmethod
+ def _resource_decomp(**kwargs):
+ phase = re.ResourceGlobalPhase.resource_rep()
+ double = re.ResourceDoubleExcitation.resource_rep()
+ ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0)
+ ctrl_phase = re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0)
+
+ gate_types = {}
+ gate_types[phase] = 1
+ gate_types[double] = 1
+ gate_types[ctrl_z] = 2
+ gate_types[ctrl_phase] = 2
+
+ return gate_types
+
+ def resource_params(self):
+ return {}
+
+ @classmethod
+ def resource_rep(cls):
+ return re.CompressedResourceOp(cls, {})
+
+
+class ResourceOrbitalRotation(qml.OrbitalRotation, re.ResourceOperator):
+ r"""Resource class for the OrbitalRotation gate.
+
+ Resources:
+ The resources are obtained by decomposing the following mapping into fundamental gates.
+
+ .. math::
+ &|\Phi_{0}\rangle = \cos(\phi/2)|\Phi_{0}\rangle - \sin(\phi/2)|\Phi_{1}\rangle\\
+ &|\Phi_{1}\rangle = \cos(\phi/2)|\Phi_{0}\rangle + \sin(\phi/2)|\Phi_{1}\rangle,
+ """
+
+ @staticmethod
+ def _resource_decomp(**kwargs):
+ fermionic_swap = re.ResourceFermionicSWAP.resource_rep()
+ single_excitation = re.ResourceSingleExcitation.resource_rep()
+
+ gate_types = {}
+ gate_types[fermionic_swap] = 2
+ gate_types[single_excitation] = 2
+
+ return gate_types
+
+ def resource_params(self):
+ return {}
+
+ @classmethod
+ def resource_rep(cls):
+ return re.CompressedResourceOp(cls, {})
+
+
class ResourceFermionicSWAP(qml.FermionicSWAP, re.ResourceOperator):
r"""Resource class for the FermionicSWAP gate.
diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py
index de93d687901..a682dc910bf 100644
--- a/pennylane/labs/resource_estimation/resource_tracking.py
+++ b/pennylane/labs/resource_estimation/resource_tracking.py
@@ -54,7 +54,6 @@
"Toffoli",
}
-
# parameters for further configuration of the decompositions
resource_config = {
"error_rx": 10e-3,
diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py
index 7465b945f26..6d59fea1b96 100644
--- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py
+++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py
@@ -18,7 +18,7 @@
import pennylane.labs.resource_estimation as re
-# pylint: disable=no-self-use, use-implicit-booleaness-not-comparison
+# pylint: disable=no-self-use, use-implicit-booleaness-not-comparison,too-many-arguments
class TestResourceCH:
@@ -46,6 +46,69 @@ def test_resource_params(self):
expected_params = {}
assert self.op.resource_params() == expected_params
+ def test_resource_adjoint(self):
+ """Test that the adjoint resources are as expected"""
+ expected_res = {self.op.resource_rep(): 1}
+ op2 = re.ResourceAdjoint(self.op)
+
+ assert self.op.adjoint_resource_decomp() == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {re.ResourceControlled.resource_rep(re.ResourceHadamard, {}, 2, 0, 0): 1},
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {re.ResourceControlled.resource_rep(re.ResourceHadamard, {}, 3, 0, 1): 1},
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {re.ResourceControlled.resource_rep(re.ResourceHadamard, {}, 4, 2, 2): 1},
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op2 = re.ResourceControlled(
+ self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {op.resource_rep(): 1}),
+ (2, {}),
+ (5, {op.resource_rep(): 1}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_resource_pow(self, z, expected_res):
+ """Test that the pow resources are as expected"""
+ op2 = re.ResourcePow(self.op, z)
+
+ assert self.op.pow_resource_decomp(z) == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestResourceCY:
"""Test the ResourceCY operation"""
@@ -56,8 +119,9 @@ def test_resources(self):
"""Test that the resources method produces the expected resources."""
expected_resources = {
- re.ResourceS.resource_rep(): 4,
+ re.ResourceS.resource_rep(): 1,
re.ResourceCNOT.resource_rep(): 1,
+ re.ResourceAdjoint.resource_rep(re.ResourceS, {}): 1,
}
assert self.op.resources(**self.op.resource_params()) == expected_resources
@@ -71,6 +135,69 @@ def test_resource_params(self):
expected_params = {}
assert self.op.resource_params() == expected_params
+ def test_resource_adjoint(self):
+ """Test that the adjoint resources are as expected"""
+ expected_res = {self.op.resource_rep(): 1}
+ op2 = re.ResourceAdjoint(self.op)
+
+ assert self.op.adjoint_resource_decomp() == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {re.ResourceControlled.resource_rep(re.ResourceY, {}, 2, 0, 0): 1},
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {re.ResourceControlled.resource_rep(re.ResourceY, {}, 3, 0, 1): 1},
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {re.ResourceControlled.resource_rep(re.ResourceY, {}, 4, 2, 2): 1},
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op2 = re.ResourceControlled(
+ self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {op.resource_rep(): 1}),
+ (2, {}),
+ (5, {op.resource_rep(): 1}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_resource_pow(self, z, expected_res):
+ """Test that the pow resources are as expected"""
+ op2 = re.ResourcePow(self.op, z)
+
+ assert self.op.pow_resource_decomp(z) == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestResourceCZ:
"""Test the ResourceCZ operation"""
@@ -96,6 +223,69 @@ def test_resource_params(self):
expected_params = {}
assert self.op.resource_params() == expected_params
+ def test_resource_adjoint(self):
+ """Test that the adjoint resources are as expected"""
+ expected_res = {self.op.resource_rep(): 1}
+ op2 = re.ResourceAdjoint(self.op)
+
+ assert self.op.adjoint_resource_decomp() == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {re.ResourceCCZ.resource_rep(): 1},
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 0, 1): 1},
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {re.ResourceControlled.resource_rep(re.ResourceZ, {}, 4, 2, 2): 1},
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op2 = re.ResourceControlled(
+ self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {op.resource_rep(): 1}),
+ (2, {}),
+ (5, {op.resource_rep(): 1}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_resource_pow(self, z, expected_res):
+ """Test that the pow resources are as expected"""
+ op2 = re.ResourcePow(self.op, z)
+
+ assert self.op.pow_resource_decomp(z) == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestResourceCSWAP:
"""Test the ResourceCSWAP operation"""
@@ -120,6 +310,69 @@ def test_resource_params(self):
expected_params = {}
assert self.op.resource_params() == expected_params
+ def test_resource_adjoint(self):
+ """Test that the adjoint resources are as expected"""
+ expected_res = {self.op.resource_rep(): 1}
+ op2 = re.ResourceAdjoint(self.op)
+
+ assert self.op.adjoint_resource_decomp() == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {re.ResourceControlled.resource_rep(re.ResourceSWAP, {}, 2, 0, 0): 1},
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {re.ResourceControlled.resource_rep(re.ResourceSWAP, {}, 3, 0, 1): 1},
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {re.ResourceControlled.resource_rep(re.ResourceSWAP, {}, 4, 2, 2): 1},
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op2 = re.ResourceControlled(
+ self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {op.resource_rep(): 1}),
+ (2, {}),
+ (5, {op.resource_rep(): 1}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_resource_pow(self, z, expected_res):
+ """Test that the pow resources are as expected"""
+ op2 = re.ResourcePow(self.op, z)
+
+ assert self.op.pow_resource_decomp(z) == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestResourceCCZ:
"""Test the ResourceCZZ operation"""
@@ -144,6 +397,69 @@ def test_resource_params(self):
expected_params = {}
assert self.op.resource_params() == expected_params
+ def test_resource_adjoint(self):
+ """Test that the adjoint resources are as expected"""
+ expected_res = {self.op.resource_rep(): 1}
+ op2 = re.ResourceAdjoint(self.op)
+
+ assert self.op.adjoint_resource_decomp() == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 0, 0): 1},
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {re.ResourceControlled.resource_rep(re.ResourceZ, {}, 4, 0, 1): 1},
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {re.ResourceControlled.resource_rep(re.ResourceZ, {}, 5, 2, 2): 1},
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op2 = re.ResourceControlled(
+ self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {op.resource_rep(): 1}),
+ (2, {}),
+ (5, {op.resource_rep(): 1}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_resource_pow(self, z, expected_res):
+ """Test that the pow resources are as expected"""
+ op2 = re.ResourcePow(self.op, z)
+
+ assert self.op.pow_resource_decomp(z) == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestResourceCNOT:
"""Test ResourceCNOT operation"""
@@ -165,6 +481,70 @@ def test_resource_params(self):
expected_params = {}
assert self.op.resource_params() == expected_params
+ def test_resource_adjoint(self):
+ """Test that the adjoint resources are as expected"""
+ expected_res = {self.op.resource_rep(): 1}
+ op2 = re.ResourceAdjoint(self.op)
+
+ assert self.op.adjoint_resource_decomp() == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {re.ResourceToffoli.resource_rep(): 1},
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {re.ResourceMultiControlledX.resource_rep(3, 0, 1): 1},
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {re.ResourceMultiControlledX.resource_rep(4, 2, 2): 1},
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op2 = re.ResourceControlled(
+ self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {op.resource_rep(): 1}),
+ (2, {}),
+ (5, {op.resource_rep(): 1}),
+ (8, {}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_resource_pow(self, z, expected_res):
+ """Test that the pow resources are as expected"""
+ op2 = re.ResourcePow(self.op, z)
+
+ assert self.op.pow_resource_decomp(z) == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestResourceToffoli:
"""Test the ResourceToffoli operation"""
@@ -176,7 +556,8 @@ def test_resources(self):
expected_resources = {
re.ResourceS.resource_rep(): 1,
- re.ResourceT.resource_rep(): 16,
+ re.ResourceT.resource_rep(): 2,
+ re.ResourceAdjoint.resource_rep(re.ResourceT, {}): 2,
re.ResourceCZ.resource_rep(): 1,
re.ResourceCNOT.resource_rep(): 9,
re.ResourceHadamard.resource_rep(): 3,
@@ -193,6 +574,70 @@ def test_resource_params(self):
expected_params = {}
assert self.op.resource_params() == expected_params
+ def test_resource_adjoint(self):
+ """Test that the adjoint resources are as expected"""
+ expected_res = {self.op.resource_rep(): 1}
+ op2 = re.ResourceAdjoint(self.op)
+
+ assert self.op.adjoint_resource_decomp() == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {re.ResourceMultiControlledX.resource_rep(3, 0, 0): 1},
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {re.ResourceMultiControlledX.resource_rep(4, 0, 1): 1},
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {re.ResourceMultiControlledX.resource_rep(5, 2, 2): 1},
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op2 = re.ResourceControlled(
+ self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {op.resource_rep(): 1}),
+ (2, {}),
+ (5, {op.resource_rep(): 1}),
+ (8, {}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_resource_pow(self, z, expected_res):
+ """Test that the pow resources are as expected"""
+ op2 = re.ResourcePow(self.op, z)
+
+ assert self.op.pow_resource_decomp(z) == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestResourceMultiControlledX:
"""Test the ResourceMultiControlledX operation"""
@@ -284,6 +729,94 @@ def test_resource_params(self, op, params):
expected_params = self._prep_params(*params)
assert op.resource_params() == expected_params
+ def test_resource_adjoint(self):
+ """Test that the adjoint resources are as expected"""
+ op = re.ResourceMultiControlledX(
+ control_wires=[0, 1, 2, 3, 4],
+ wires=["t"],
+ control_values=[1, 0, 0, 1, 0],
+ work_wires=["w1"],
+ )
+
+ expected_res = {op.resource_rep(**op.resource_params()): 1}
+ op2 = re.ResourceAdjoint(op)
+
+ assert op.adjoint_resource_decomp(**op.resource_params()) == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {re.ResourceToffoli.resource_rep(): 1},
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["work1"],
+ {
+ re.ResourceCNOT.resource_rep(): 2,
+ re.ResourceToffoli.resource_rep(): 1,
+ },
+ ),
+ (
+ ["c1", "c2", "c3", "c4"],
+ [1, 0, 0, 1],
+ ["work1", "work2"],
+ {
+ re.ResourceX.resource_rep(): 4,
+ re.ResourceCNOT.resource_rep(): 69,
+ },
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ op = re.ResourceMultiControlledX(control_wires=[0], wires=["t"], control_values=[1])
+
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op2 = re.ResourceControlled(
+ op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ op.controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires, **op.resource_params()
+ )
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {re.ResourceMultiControlledX.resource_rep(5, 3, 1): 1}),
+ (2, {}),
+ (5, {re.ResourceMultiControlledX.resource_rep(5, 3, 1): 1}),
+ (6, {}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_resource_pow(self, z, expected_res):
+ """Test that the pow resources are as expected"""
+ op = re.ResourceMultiControlledX(
+ control_wires=[0, 1, 2, 3, 4],
+ wires=["t"],
+ control_values=[1, 0, 0, 1, 0],
+ work_wires=["w1"],
+ )
+
+ op2 = re.ResourcePow(op, z)
+
+ assert op.pow_resource_decomp(z, **op.resource_params()) == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestResourceCRX:
"""Test the ResourceCRX operation"""
@@ -310,6 +843,69 @@ def test_resource_params(self):
expected_params = {}
assert self.op.resource_params() == expected_params
+ def test_resource_adjoint(self):
+ """Test that the adjoint resources are as expected"""
+ expected_res = {self.op.resource_rep(): 1}
+ op2 = re.ResourceAdjoint(self.op)
+
+ assert self.op.adjoint_resource_decomp() == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {re.ResourceControlled.resource_rep(re.ResourceRX, {}, 2, 0, 0): 1},
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {re.ResourceControlled.resource_rep(re.ResourceRX, {}, 3, 0, 1): 1},
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {re.ResourceControlled.resource_rep(re.ResourceRX, {}, 4, 2, 2): 1},
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op2 = re.ResourceControlled(
+ self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {op.resource_rep(): 1}),
+ (2, {op.resource_rep(): 1}),
+ (5, {op.resource_rep(): 1}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_resource_pow(self, z, expected_res):
+ """Test that the pow resources are as expected"""
+ op2 = re.ResourcePow(self.op, z)
+
+ assert self.op.pow_resource_decomp(z) == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestResourceCRY:
"""Test the ResourceCRY operation"""
@@ -335,6 +931,69 @@ def test_resource_params(self):
expected_params = {}
assert self.op.resource_params() == expected_params
+ def test_resource_adjoint(self):
+ """Test that the adjoint resources are as expected"""
+ expected_res = {self.op.resource_rep(): 1}
+ op2 = re.ResourceAdjoint(self.op)
+
+ assert self.op.adjoint_resource_decomp() == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {re.ResourceControlled.resource_rep(re.ResourceRY, {}, 2, 0, 0): 1},
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {re.ResourceControlled.resource_rep(re.ResourceRY, {}, 3, 0, 1): 1},
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {re.ResourceControlled.resource_rep(re.ResourceRY, {}, 4, 2, 2): 1},
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op2 = re.ResourceControlled(
+ self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {op.resource_rep(): 1}),
+ (2, {op.resource_rep(): 1}),
+ (5, {op.resource_rep(): 1}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_resource_pow(self, z, expected_res):
+ """Test that the pow resources are as expected"""
+ op2 = re.ResourcePow(self.op, z)
+
+ assert self.op.pow_resource_decomp(z) == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestResourceCRZ:
"""Test the ResourceCRZ operation"""
@@ -360,6 +1019,69 @@ def test_resource_params(self):
expected_params = {}
assert self.op.resource_params() == expected_params
+ def test_resource_adjoint(self):
+ """Test that the adjoint resources are as expected"""
+ expected_res = {self.op.resource_rep(): 1}
+ op2 = re.ResourceAdjoint(self.op)
+
+ assert self.op.adjoint_resource_decomp() == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 2, 0, 0): 1},
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 3, 0, 1): 1},
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 4, 2, 2): 1},
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op2 = re.ResourceControlled(
+ self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {op.resource_rep(): 1}),
+ (2, {op.resource_rep(): 1}),
+ (5, {op.resource_rep(): 1}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_resource_pow(self, z, expected_res):
+ """Test that the pow resources are as expected"""
+ op2 = re.ResourcePow(self.op, z)
+
+ assert self.op.pow_resource_decomp(z) == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestResourceCRot:
"""Test the ResourceCRot operation"""
@@ -385,6 +1107,69 @@ def test_resource_params(self):
expected_params = {}
assert self.op.resource_params() == expected_params
+ def test_resource_adjoint(self):
+ """Test that the adjoint resources are as expected"""
+ expected_res = {self.op.resource_rep(): 1}
+ op2 = re.ResourceAdjoint(self.op)
+
+ assert self.op.adjoint_resource_decomp() == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {re.ResourceControlled.resource_rep(re.ResourceRot, {}, 2, 0, 0): 1},
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {re.ResourceControlled.resource_rep(re.ResourceRot, {}, 3, 0, 1): 1},
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {re.ResourceControlled.resource_rep(re.ResourceRot, {}, 4, 2, 2): 1},
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op2 = re.ResourceControlled(
+ self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {op.resource_rep(): 1}),
+ (2, {op.resource_rep(): 1}),
+ (5, {op.resource_rep(): 1}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_resource_pow(self, z, expected_res):
+ """Test that the pow resources are as expected"""
+ op2 = re.ResourcePow(self.op, z)
+
+ assert self.op.pow_resource_decomp(z) == expected_res
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestResourceControlledPhaseShift:
"""Test ResourceControlledPhaseShift"""
@@ -445,3 +1230,66 @@ def test_resources_from_rep(self, phi, wires):
op_compressed_rep_type = op_compressed_rep.op_type
assert op_compressed_rep_type.resources(**op_resource_params) == expected
+
+ @pytest.mark.parametrize("phi, wires", params)
+ def test_adjoint_decomp(self, phi, wires):
+ """Test that the adjoint resources are correct."""
+
+ op = re.ResourceControlledPhaseShift(phi, wires)
+ adjoint = re.ResourceAdjoint(op)
+
+ assert re.get_resources(op) == re.get_resources(adjoint)
+
+ @pytest.mark.parametrize("phi, wires", params)
+ def test_pow_decomp(self, phi, wires):
+ """Test that the adjoint resources are correct."""
+
+ op = re.ResourceControlledPhaseShift(phi, wires)
+ pow = re.ResourcePow(op, 2)
+
+ assert re.get_resources(op) == re.get_resources(pow)
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 2, 0, 0): 1},
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 0, 1): 1},
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 4, 2, 2): 1},
+ ),
+ )
+
+ @pytest.mark.parametrize("phi, wires", params)
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(
+ self, phi, wires, ctrl_wires, ctrl_values, work_wires, expected_res
+ ):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op = re.ResourceControlledPhaseShift(phi, wires)
+ op2 = re.ResourceControlled(
+ op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py
index 6bf1a87cc90..db8baac9098 100644
--- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py
+++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py
@@ -60,6 +60,43 @@ def test_tracking_name(self, op, expected):
name = op.tracking_name_from_op()
assert name == expected
+ @pytest.mark.parametrize(
+ "nested_op, expected_op",
+ [
+ (
+ re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2]))),
+ re.ResourceQFT([0, 1, 2]),
+ ),
+ (
+ re.ResourceAdjoint(
+ re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2])))
+ ),
+ re.ResourceAdjoint(re.ResourceQFT([0, 1, 2])),
+ ),
+ (
+ re.ResourceAdjoint(
+ re.ResourceAdjoint(
+ re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2])))
+ )
+ ),
+ re.ResourceQFT([0, 1, 2]),
+ ),
+ (
+ re.ResourceAdjoint(
+ re.ResourceAdjoint(
+ re.ResourceAdjoint(
+ re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2])))
+ )
+ )
+ ),
+ re.ResourceAdjoint(re.ResourceQFT([0, 1, 2])),
+ ),
+ ],
+ )
+ def test_nested_adjoints(self, nested_op, expected_op):
+ """Test the resources of nested Adjoints."""
+ assert re.get_resources(nested_op) == re.get_resources(expected_op)
+
expected_resources = [
re.Resources(gate_types={"Adjoint(QFT)": 1}, num_gates=1, num_wires=2),
re.Resources(gate_types={"Adjoint(Adjoint(QFT))": 1}, num_gates=1, num_wires=2),
@@ -147,6 +184,27 @@ def test_tracking_name(self, op, expected):
name = op.tracking_name_from_op()
assert name == expected
+ @pytest.mark.parametrize(
+ "nested_op, expected_op",
+ [
+ (
+ re.ResourceControlled(
+ re.ResourceControlled(re.ResourceX(0), control_wires=[1]), control_wires=[2]
+ ),
+ re.ResourceToffoli([0, 1, 2]),
+ ),
+ (
+ re.ResourceControlled(
+ re.ResourceControlled(re.ResourceX(0), control_wires=[1]), control_wires=[2]
+ ),
+ re.ResourceControlled(re.ResourceX(0), control_wires=[1, 2]),
+ ),
+ ],
+ )
+ def test_nested_controls(self, nested_op, expected_op):
+ """Test the resources for nested Controlled operators."""
+ assert re.get_resources(nested_op) == re.get_resources(expected_op)
+
expected_resources = [
re.Resources(gate_types={"C(QFT,1,0,0)": 1}, num_gates=1, num_wires=3),
re.Resources(gate_types={"C(C(QFT,1,0,0),1,0,0)": 1}, num_gates=1, num_wires=4),
diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py
index b59c519d71d..3d8da6411fd 100644
--- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py
+++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py
@@ -40,6 +40,88 @@ def test_resource_rep(self):
expected = re.CompressedResourceOp(re.ResourceHadamard, {})
assert re.ResourceHadamard.resource_rep() == expected
+ def test_adjoint_decomp(self):
+ """Test that the adjoint decomposition is correct."""
+ h = re.ResourceHadamard(0)
+ h_dag = re.ResourceAdjoint(re.ResourceHadamard(0))
+
+ assert re.get_resources(h) == re.get_resources(h_dag)
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {
+ re.ResourceCH.resource_rep(): 1,
+ },
+ ),
+ (
+ ["c1"],
+ [0],
+ [],
+ {
+ re.ResourceCH.resource_rep(): 1,
+ re.ResourceX.resource_rep(): 2,
+ },
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {
+ re.ResourceCH.resource_rep(): 1,
+ re.ResourceMultiControlledX.resource_rep(2, 0, 1): 2,
+ },
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {
+ re.ResourceCH.resource_rep(): 1,
+ re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2,
+ },
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op = re.ResourceHadamard(0)
+ op2 = re.ResourceControlled(
+ op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {re.ResourceHadamard.resource_rep(): 1}),
+ (2, {}),
+ (3, {re.ResourceHadamard.resource_rep(): 1}),
+ (4, {}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_pow_decomp(self, z, expected_res):
+ """Test that the pow decomposition is correct."""
+ op = re.ResourceHadamard(0)
+ assert op.pow_resource_decomp(z) == expected_res
+
+ op2 = re.ResourcePow(op, z)
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestSWAP:
"""Tests for ResourceSWAP"""
@@ -73,6 +155,88 @@ def test_resources_from_rep(self):
op_resource_params = op_compressed_rep.params
assert op_resource_type.resources(**op_resource_params) == expected
+ def test_adjoint_decomp(self):
+ """Test that the adjoint decomposition is correct."""
+ swap = re.ResourceSWAP([0, 1])
+ swap_dag = re.ResourceAdjoint(re.ResourceSWAP([0, 1]))
+
+ assert re.get_resources(swap) == re.get_resources(swap_dag)
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {
+ re.ResourceCSWAP.resource_rep(): 1,
+ },
+ ),
+ (
+ ["c1"],
+ [0],
+ [],
+ {
+ re.ResourceCSWAP.resource_rep(): 1,
+ re.ResourceX.resource_rep(): 2,
+ },
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {
+ re.ResourceCNOT.resource_rep(): 2,
+ re.ResourceMultiControlledX.resource_rep(2, 0, 1): 1,
+ },
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {
+ re.ResourceCNOT.resource_rep(): 2,
+ re.ResourceMultiControlledX.resource_rep(3, 2, 2): 1,
+ },
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op = re.ResourceSWAP([0, 1])
+ op2 = re.ResourceControlled(
+ op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {re.ResourceSWAP.resource_rep(): 1}),
+ (2, {}),
+ (3, {re.ResourceSWAP.resource_rep(): 1}),
+ (4, {}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_pow_decomp(self, z, expected_res):
+ """Test that the pow decomposition is correct."""
+ op = re.ResourceSWAP([0, 1])
+ assert op.pow_resource_decomp(z) == expected_res
+
+ op2 = re.ResourcePow(op, z)
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestS:
"""Tests for ResourceS"""
@@ -104,12 +268,103 @@ def test_resources_from_rep(self):
op_resource_params = op_compressed_rep.params
assert op_resource_type.resources(**op_resource_params) == expected
+ def test_adjoint_decomposition(self):
+ """Test that the adjoint resources are correct."""
+ expected = {re.ResourceS.resource_rep(): 3}
+ assert re.ResourceS.adjoint_resource_decomp() == expected
+
+ s = re.ResourceS(0)
+ s_dag = re.ResourceAdjoint(s)
+
+ r1 = re.get_resources(s) * 3
+ r2 = re.get_resources(s_dag)
+ assert r1 == r2
+
+ pow_data = (
+ (1, {re.ResourceS.resource_rep(): 1}),
+ (2, {re.ResourceS.resource_rep(): 2}),
+ (3, {re.ResourceS.resource_rep(): 3}),
+ (4, {}),
+ (7, {re.ResourceS.resource_rep(): 3}),
+ (8, {}),
+ (14, {re.ResourceS.resource_rep(): 2}),
+ (15, {re.ResourceS.resource_rep(): 3}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_pow_decomp(self, z, expected_res):
+ """Test that the pow decomposition is correct."""
+ op = re.ResourceS(0)
+ assert op.pow_resource_decomp(z) == expected_res
+
+ op2 = re.ResourcePow(op, z)
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {
+ re.ResourceControlledPhaseShift.resource_rep(): 1,
+ },
+ ),
+ (
+ ["c1"],
+ [0],
+ [],
+ {
+ re.ResourceControlledPhaseShift.resource_rep(): 1,
+ re.ResourceX.resource_rep(): 2,
+ },
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {
+ re.ResourceControlledPhaseShift.resource_rep(): 1,
+ re.ResourceMultiControlledX.resource_rep(2, 0, 1): 2,
+ },
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {
+ re.ResourceControlledPhaseShift.resource_rep(): 1,
+ re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2,
+ },
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op = re.ResourceS(0)
+ op2 = re.ResourceControlled(
+ op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestT:
"""Tests for ResourceT"""
def test_resources(self):
- """Test that ResourceT does not implement a decomposition"""
+ """Test that there is no further decomposition of the T gate."""
op = re.ResourceT(0)
with pytest.raises(re.ResourcesNotDefined):
op.resources()
@@ -123,3 +378,452 @@ def test_resource_rep(self):
"""Test that the compact representation is correct"""
expected = re.CompressedResourceOp(re.ResourceT, {})
assert re.ResourceT.resource_rep() == expected
+
+ def test_adjoint_decomposition(self):
+ """Test that the adjoint resources are correct."""
+ expected = {re.ResourceT.resource_rep(): 7}
+ assert re.ResourceT.adjoint_resource_decomp() == expected
+
+ t = re.ResourceT(0)
+ t_dag = re.ResourceAdjoint(t)
+
+ r1 = re.get_resources(t) * 7
+ r2 = re.get_resources(t_dag)
+ assert r1 == r2
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {
+ re.ResourceControlledPhaseShift.resource_rep(): 1,
+ },
+ ),
+ (
+ ["c1"],
+ [0],
+ [],
+ {
+ re.ResourceControlledPhaseShift.resource_rep(): 1,
+ re.ResourceX.resource_rep(): 2,
+ },
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {
+ re.ResourceControlledPhaseShift.resource_rep(): 1,
+ re.ResourceMultiControlledX.resource_rep(2, 0, 1): 2,
+ },
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {
+ re.ResourceControlledPhaseShift.resource_rep(): 1,
+ re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2,
+ },
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op = re.ResourceT(0)
+ op2 = re.ResourceControlled(
+ op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {re.ResourceT.resource_rep(): 1}),
+ (2, {re.ResourceT.resource_rep(): 2}),
+ (3, {re.ResourceT.resource_rep(): 3}),
+ (7, {re.ResourceT.resource_rep(): 7}),
+ (8, {}),
+ (14, {re.ResourceT.resource_rep(): 6}),
+ (15, {re.ResourceT.resource_rep(): 7}),
+ (16, {}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_pow_decomp(self, z, expected_res):
+ """Test that the pow decomposition is correct."""
+ op = re.ResourceT(0)
+ assert op.pow_resource_decomp(z) == expected_res
+
+ op2 = re.ResourcePow(op, z)
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+
+class TestX:
+ """Tests for the ResourceX gate"""
+
+ def test_resources(self):
+ """Tests for the ResourceX gate"""
+ expected = {
+ re.ResourceS.resource_rep(): 2,
+ re.ResourceHadamard.resource_rep(): 2,
+ }
+ assert re.ResourceX.resources() == expected
+
+ def test_resource_params(self):
+ """Test that the resource params are correct"""
+ op = re.ResourceX(0)
+ assert op.resource_params() == {}
+
+ def test_resource_rep(self):
+ """Test that the compact representation is correct"""
+ expected = re.CompressedResourceOp(re.ResourceX, {})
+ assert re.ResourceX.resource_rep() == expected
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {
+ re.ResourceCNOT.resource_rep(): 1,
+ },
+ ),
+ (
+ ["c1"],
+ [0],
+ [],
+ {
+ re.ResourceCNOT.resource_rep(): 1,
+ re.ResourceX.resource_rep(): 2,
+ },
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {
+ re.ResourceToffoli.resource_rep(): 1,
+ },
+ ),
+ (
+ ["c1", "c2"],
+ [0, 0],
+ ["w1"],
+ {
+ re.ResourceToffoli.resource_rep(): 1,
+ re.ResourceX.resource_rep(): 4,
+ },
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {
+ re.ResourceMultiControlledX.resource_rep(3, 2, 2): 1,
+ },
+ ),
+ (
+ ["c1", "c2", "c3", "c4"],
+ [1, 0, 0, 1],
+ ["w1", "w2"],
+ {
+ re.ResourceMultiControlledX.resource_rep(4, 2, 2): 1,
+ },
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op = re.ResourceX(0)
+ op2 = re.ResourceControlled(
+ op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ def test_adjoint_decomposition(self):
+ """Test that the adjoint resources are correct."""
+ expected = {re.ResourceX.resource_rep(): 1}
+ assert re.ResourceX.adjoint_resource_decomp() == expected
+
+ x = re.ResourceX(0)
+ x_dag = re.ResourceAdjoint(x)
+
+ r1 = re.get_resources(x)
+ r2 = re.get_resources(x_dag)
+ assert r1 == r2
+
+ pow_data = (
+ (1, {re.ResourceX.resource_rep(): 1}),
+ (2, {}),
+ (3, {re.ResourceX.resource_rep(): 1}),
+ (4, {}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_pow_decomp(self, z, expected_res):
+ """Test that the pow decomposition is correct."""
+ op = re.ResourceX(0)
+ assert op.pow_resource_decomp(z) == expected_res
+
+ op2 = re.ResourcePow(op, z)
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+
+class TestY:
+ """Tests for the ResourceY gate"""
+
+ def test_resources(self):
+ """Test that ResourceT does not implement a decomposition"""
+ expected = {
+ re.ResourceS.resource_rep(): 6,
+ re.ResourceHadamard.resource_rep(): 2,
+ }
+ assert re.ResourceY.resources() == expected
+
+ def test_resource_params(self):
+ """Test that the resource params are correct"""
+ op = re.ResourceY(0)
+ assert op.resource_params() == {}
+
+ def test_resource_rep(self):
+ """Test that the compact representation is correct"""
+ expected = re.CompressedResourceOp(re.ResourceY, {})
+ assert re.ResourceY.resource_rep() == expected
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {
+ re.ResourceCY.resource_rep(): 1,
+ },
+ ),
+ (
+ ["c1"],
+ [0],
+ [],
+ {
+ re.ResourceCY.resource_rep(): 1,
+ re.ResourceX.resource_rep(): 2,
+ },
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {
+ re.ResourceCY.resource_rep(): 1,
+ re.ResourceMultiControlledX.resource_rep(2, 0, 1): 2,
+ },
+ ),
+ (
+ ["c1", "c2"],
+ [0, 0],
+ ["w1"],
+ {
+ re.ResourceCY.resource_rep(): 1,
+ re.ResourceMultiControlledX.resource_rep(2, 2, 1): 2,
+ },
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {
+ re.ResourceCY.resource_rep(): 1,
+ re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2,
+ },
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op = re.ResourceY(0)
+ op2 = re.ResourceControlled(
+ op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ def test_adjoint_decomposition(self):
+ """Test that the adjoint resources are correct."""
+ expected = {re.ResourceY.resource_rep(): 1}
+ assert re.ResourceY.adjoint_resource_decomp() == expected
+
+ y = re.ResourceY(0)
+ y_dag = re.ResourceAdjoint(y)
+
+ r1 = re.get_resources(y)
+ r2 = re.get_resources(y_dag)
+ assert r1 == r2
+
+ pow_data = (
+ (1, {re.ResourceY.resource_rep(): 1}),
+ (2, {}),
+ (3, {re.ResourceY.resource_rep(): 1}),
+ (4, {}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_pow_decomp(self, z, expected_res):
+ """Test that the pow decomposition is correct."""
+ op = re.ResourceY(0)
+ assert op.pow_resource_decomp(z) == expected_res
+
+ op2 = re.ResourcePow(op, z)
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+
+class TestZ:
+ """Tests for the ResourceZ gate"""
+
+ def test_resources(self):
+ """Test that ResourceT does not implement a decomposition"""
+ expected = {
+ re.ResourceS.resource_rep(): 2,
+ }
+ assert re.ResourceZ.resources() == expected
+
+ def test_resource_params(self):
+ """Test that the resource params are correct"""
+ op = re.ResourceZ(0)
+ assert op.resource_params() == {}
+
+ def test_resource_rep(self):
+ """Test that the compact representation is correct"""
+ expected = re.CompressedResourceOp(re.ResourceZ, {})
+ assert re.ResourceZ.resource_rep() == expected
+
+ ctrl_data = (
+ (
+ ["c1"],
+ [1],
+ [],
+ {
+ re.ResourceCZ.resource_rep(): 1,
+ },
+ ),
+ (
+ ["c1"],
+ [0],
+ [],
+ {
+ re.ResourceCZ.resource_rep(): 1,
+ re.ResourceX.resource_rep(): 2,
+ },
+ ),
+ (
+ ["c1", "c2"],
+ [1, 1],
+ ["w1"],
+ {
+ re.ResourceCCZ.resource_rep(): 1,
+ },
+ ),
+ (
+ ["c1", "c2"],
+ [0, 0],
+ ["w1"],
+ {
+ re.ResourceCCZ.resource_rep(): 1,
+ re.ResourceX.resource_rep(): 4,
+ },
+ ),
+ (
+ ["c1", "c2", "c3"],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {
+ re.ResourceCZ.resource_rep(): 1,
+ re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2,
+ },
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res",
+ ctrl_data,
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op = re.ResourceZ(0)
+ op2 = re.ResourceControlled(
+ op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ def test_adjoint_decomposition(self):
+ """Test that the adjoint resources are correct."""
+ expected = {re.ResourceZ.resource_rep(): 1}
+ assert re.ResourceZ.adjoint_resource_decomp() == expected
+
+ z = re.ResourceZ(0)
+ z_dag = re.ResourceAdjoint(z)
+
+ r1 = re.get_resources(z)
+ r2 = re.get_resources(z_dag)
+ assert r1 == r2
+
+ pow_data = (
+ (1, {re.ResourceZ.resource_rep(): 1}),
+ (2, {}),
+ (3, {re.ResourceZ.resource_rep(): 1}),
+ (4, {}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_pow_decomp(self, z, expected_res):
+ """Test that the pow decomposition is correct."""
+ op = re.ResourceZ(0)
+ assert op.pow_resource_decomp(z) == expected_res
+
+ op2 = re.ResourcePow(op, z)
+ assert op2.resources(**op2.resource_params()) == expected_res
diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py
index 2d47dbd5a3e..3f08d24ba33 100644
--- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py
+++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py
@@ -17,7 +17,7 @@
import pennylane.labs.resource_estimation as re
-# pylint: disable=use-implicit-booleaness-not-comparison,no-self-use
+# pylint: disable=use-implicit-booleaness-not-comparison,no-self-use,too-many-arguments
class TestMultiRZ:
@@ -58,6 +58,102 @@ def test_resources_from_rep(self, num_wires):
op_resource_params = op_compressed_rep.params
assert op_resource_type.resources(**op_resource_params) == expected
+ @pytest.mark.parametrize("num_wires", range(1, 5))
+ def test_adjoint_decomp(self, num_wires):
+ """Test that the adjoint decomposition is correct."""
+ expected = {re.ResourceMultiRZ.resource_rep(num_wires=num_wires): 1}
+ assert re.ResourceMultiRZ.adjoint_resource_decomp(num_wires=num_wires) == expected
+
+ multi_rz = re.ResourceMultiRZ(0.123, wires=range(num_wires))
+ multi_rz_dag = re.ResourceAdjoint(multi_rz)
+
+ assert re.get_resources(multi_rz) == re.get_resources(multi_rz_dag)
+
+ ctrl_data = (
+ (
+ [1],
+ [1],
+ [],
+ {
+ re.ResourceCNOT.resource_rep(): 4,
+ re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 1, 0, 0): 1,
+ },
+ ),
+ (
+ [1],
+ [0],
+ [],
+ {
+ re.ResourceControlled.resource_rep(
+ re.ResourceMultiRZ, {"num_wires": 3}, 1, 0, 0
+ ): 1,
+ re.ResourceX.resource_rep(): 2,
+ },
+ ),
+ (
+ [1, 2],
+ [1, 1],
+ ["w1"],
+ {
+ re.ResourceCNOT.resource_rep(): 4,
+ re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 2, 0, 1): 1,
+ },
+ ),
+ (
+ [1, 2, 3],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {
+ re.ResourceControlled.resource_rep(
+ re.ResourceMultiRZ, {"num_wires": 3}, 3, 0, 2
+ ): 1,
+ re.ResourceX.resource_rep(): 4,
+ },
+ ),
+ )
+
+ @pytest.mark.parametrize("ctrl_wires, ctrl_values, work_wires, expected_res", ctrl_data)
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op = re.ResourceMultiRZ(1.24, wires=range(5, 8))
+ op2 = re.ResourceControlled(
+ op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ if num_ctrl_values != 0:
+ with pytest.raises(re.ResourcesNotDefined):
+ op.controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires, **op.resource_params()
+ )
+ else:
+ assert (
+ op.controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires, **op.resource_params()
+ )
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {re.ResourceMultiRZ.resource_rep(num_wires=4): 1}),
+ (2, {re.ResourceMultiRZ.resource_rep(num_wires=4): 1}),
+ (3, {re.ResourceMultiRZ.resource_rep(num_wires=4): 1}),
+ (4, {re.ResourceMultiRZ.resource_rep(num_wires=4): 1}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_pow_decomp(self, z, expected_res):
+ """Test that the pow decomposition is correct."""
+ op = re.ResourceMultiRZ(1.23, wires=range(4))
+ assert op.pow_resource_decomp(z, **op.resource_params()) == expected_res
+
+ op2 = re.ResourcePow(op, z)
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestPauliRot:
"""Test the ResourcePauliRot class."""
@@ -90,7 +186,8 @@ def test_resources(self, pauli_word, expected_h_count, expected_s_count):
else:
expected = {
re.ResourceHadamard.resource_rep(): expected_h_count,
- re.ResourceS.resource_rep(): 4 * expected_s_count,
+ re.ResourceS.resource_rep(): expected_s_count,
+ re.ResourceAdjoint.resource_rep(re.ResourceS, {}): expected_s_count,
re.ResourceRZ.resource_rep(): 1,
re.ResourceCNOT.resource_rep(): 2 * (active_wires - 1),
}
@@ -117,6 +214,150 @@ def test_resources_from_rep(self, pauli_word, expected_h_count, expected_rx_coun
op_resource_params = op_compressed_rep.params
assert op_resource_type.resources(**op_resource_params) == expected
+ @pytest.mark.parametrize("pauli_word", pauli_words)
+ def test_adjoint_decomp(self, pauli_word):
+ """Test that the adjoint decomposition is correct."""
+ expected = {re.ResourcePauliRot.resource_rep(pauli_word=pauli_word): 1}
+ assert re.ResourcePauliRot.adjoint_resource_decomp(pauli_word=pauli_word) == expected
+
+ op = re.ResourcePauliRot(theta=0.5, pauli_word=pauli_word, wires=range(len(pauli_word)))
+ op_dag = re.ResourceAdjoint(op)
+
+ assert re.get_resources(op) == re.get_resources(op_dag)
+
+ ctrl_data = (
+ (
+ "XXX",
+ [1],
+ [1],
+ [],
+ {
+ re.ResourceHadamard.resource_rep(): 6,
+ re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 1, 0, 0): 1,
+ re.ResourceCNOT.resource_rep(): 4,
+ },
+ ),
+ (
+ "XXX",
+ [1],
+ [0],
+ [],
+ {
+ re.ResourceHadamard.resource_rep(): 6,
+ re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 1, 1, 0): 1,
+ re.ResourceCNOT.resource_rep(): 4,
+ },
+ ),
+ (
+ "XXX",
+ [1, 2],
+ [1, 1],
+ ["w1"],
+ {
+ re.ResourceHadamard.resource_rep(): 6,
+ re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 2, 0, 1): 1,
+ re.ResourceCNOT.resource_rep(): 4,
+ },
+ ),
+ (
+ "XIYIZIX",
+ [1],
+ [1],
+ [],
+ {
+ re.ResourceHadamard.resource_rep(): 6,
+ re.ResourceS.resource_rep(): 1,
+ re.ResourceAdjoint.resource_rep(re.ResourceS, {}): 1,
+ re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 1, 0, 0): 1,
+ re.ResourceCNOT.resource_rep(): 6,
+ },
+ ),
+ (
+ "XIYIZIX",
+ [1],
+ [0],
+ [],
+ {
+ re.ResourceHadamard.resource_rep(): 6,
+ re.ResourceS.resource_rep(): 1,
+ re.ResourceAdjoint.resource_rep(re.ResourceS, {}): 1,
+ re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 1, 1, 0): 1,
+ re.ResourceCNOT.resource_rep(): 6,
+ },
+ ),
+ (
+ "XIYIZIX",
+ [1, 2],
+ [1, 1],
+ ["w1"],
+ {
+ re.ResourceHadamard.resource_rep(): 6,
+ re.ResourceS.resource_rep(): 1,
+ re.ResourceAdjoint.resource_rep(re.ResourceS, {}): 1,
+ re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 2, 0, 1): 1,
+ re.ResourceCNOT.resource_rep(): 6,
+ },
+ ),
+ (
+ "III",
+ [1],
+ [1],
+ [],
+ {re.ResourceControlled.resource_rep(re.ResourceGlobalPhase, {}, 1, 0, 0): 1},
+ ),
+ (
+ "III",
+ [1],
+ [0],
+ [],
+ {re.ResourceControlled.resource_rep(re.ResourceGlobalPhase, {}, 1, 1, 0): 1},
+ ),
+ (
+ "III",
+ [1, 2],
+ [1, 1],
+ ["w1"],
+ {re.ResourceControlled.resource_rep(re.ResourceGlobalPhase, {}, 2, 0, 1): 1},
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "pauli_word, ctrl_wires, ctrl_values, work_wires, expected_res", ctrl_data
+ )
+ def test_resource_controlled(
+ self, ctrl_wires, ctrl_values, work_wires, pauli_word, expected_res
+ ):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op = re.ResourcePauliRot(
+ 1.24, pauli_word, wires=list(f"wire_{i}" for i in range(len(pauli_word)))
+ )
+ op2 = re.ResourceControlled(
+ op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ op.controlled_resource_decomp(
+ num_ctrl_wires, num_ctrl_values, num_work_wires, **op.resource_params()
+ )
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ @pytest.mark.parametrize("z", range(1, 5))
+ @pytest.mark.parametrize("pauli_word", pauli_words)
+ def test_pow_decomp(self, z, pauli_word):
+ """Test that the pow decomposition is correct."""
+ op = re.ResourcePauliRot(theta=0.5, pauli_word=pauli_word, wires=range(len(pauli_word)))
+ expected_res = {re.ResourcePauliRot.resource_rep(pauli_word=pauli_word): 1}
+ assert op.pow_resource_decomp(z, **op.resource_params()) == expected_res
+
+ op2 = re.ResourcePow(op, z)
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestIsingXX:
"""Test the IsingXX class."""
diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py
index f93c363dbde..88d044bb79d 100644
--- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py
+++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py
@@ -14,6 +14,8 @@
"""
Tests for parametric single qubit resource operators.
"""
+import copy
+
import pytest
import pennylane.labs.resource_estimation as re
@@ -21,7 +23,7 @@
_rotation_resources,
)
-# pylint: disable=no-self-use, use-implicit-booleaness-not-comparison
+# pylint: disable=no-self-use, use-implicit-booleaness-not-comparison,too-many-arguments
params = list(zip([10e-3, 10e-4, 10e-5], [17, 21, 24]))
@@ -41,6 +43,18 @@ class TestPauliRotation:
params_classes = [re.ResourceRX, re.ResourceRY, re.ResourceRZ]
params_errors = [10e-3, 10e-4, 10e-5]
+ params_ctrl_res = [
+ {
+ re.ResourceHadamard.resource_rep(): 2,
+ re.ResourceRZ.resource_rep(): 2,
+ },
+ {
+ re.ResourceRY.resource_rep(): 2,
+ },
+ {
+ re.ResourceRZ.resource_rep(): 2,
+ },
+ ]
@pytest.mark.parametrize("resource_class", params_classes)
@pytest.mark.parametrize("epsilon", params_errors)
@@ -82,6 +96,121 @@ def test_resource_params(self, resource_class, epsilon): # pylint: disable=unus
op = resource_class(1.24, wires=0)
assert op.resource_params() == {}
+ @pytest.mark.parametrize("resource_class", params_classes)
+ @pytest.mark.parametrize("epsilon", params_errors)
+ def test_adjoint_decomposition(self, resource_class, epsilon):
+ """Test that the adjoint decompositions are correct."""
+
+ expected = {resource_class.resource_rep(): 1}
+ assert resource_class.adjoint_resource_decomp() == expected
+
+ op = resource_class(1.24, wires=0)
+ dag = re.ResourceAdjoint(op)
+
+ label = "error_" + resource_class.__name__.replace("Resource", "").lower()
+ config = {label: epsilon}
+
+ r1 = re.get_resources(op, config=config)
+ r2 = re.get_resources(dag, config=config)
+
+ assert r1 == r2
+
+ @pytest.mark.parametrize("resource_class", params_classes)
+ @pytest.mark.parametrize("epsilon", params_errors)
+ @pytest.mark.parametrize("z", list(range(10)))
+ def test_pow_decomposition(self, resource_class, epsilon, z):
+ """Test that the pow decompositions are correct."""
+
+ expected = {resource_class.resource_rep(): 1}
+ assert resource_class.pow_resource_decomp(z) == expected
+
+ op = resource_class(1.24, wires=0)
+ dag = re.ResourcePow(op, z)
+
+ label = "error_" + resource_class.__name__.replace("Resource", "").lower()
+ config = {label: epsilon}
+
+ r1 = re.get_resources(op, config=config)
+ r2 = re.get_resources(dag, config=config)
+
+ assert r1 == r2
+
+ params_ctrl_classes = (
+ (re.ResourceRX, re.ResourceCRX),
+ (re.ResourceRY, re.ResourceCRY),
+ (re.ResourceRZ, re.ResourceCRZ),
+ )
+
+ @pytest.mark.parametrize("resource_class, controlled_class", params_ctrl_classes)
+ @pytest.mark.parametrize("epsilon", params_errors)
+ def test_controlled_decomposition_single_control(
+ self, resource_class, controlled_class, epsilon
+ ):
+ """Test that the controlled decompositions are correct."""
+ expected = {controlled_class.resource_rep(): 1}
+ assert resource_class.controlled_resource_decomp(1, 0, 0) == expected
+
+ expected = {controlled_class.resource_rep(): 1, re.ResourceX.resource_rep(): 2}
+ assert resource_class.controlled_resource_decomp(1, 1, 0) == expected
+
+ op = resource_class(1.24, wires=0)
+ c_op = re.ResourceControlled(op, control_wires=[1])
+
+ c = controlled_class(1.24, wires=[0, 1])
+
+ config = {"error_rx": epsilon, "error_ry": epsilon, "error_rz": epsilon}
+
+ r1 = re.get_resources(c, config=config)
+ r2 = re.get_resources(c_op, config=config)
+
+ assert r1 == r2
+
+ ctrl_res_data = (
+ (
+ [1, 2],
+ [1, 1],
+ ["w1"],
+ {re.ResourceMultiControlledX.resource_rep(2, 0, 1): 2},
+ ),
+ (
+ [1, 2],
+ [1, 0],
+ [],
+ {re.ResourceMultiControlledX.resource_rep(2, 1, 0): 2},
+ ),
+ (
+ [1, 2, 3],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2},
+ ),
+ )
+
+ @pytest.mark.parametrize("resource_class, local_res", zip(params_classes, params_ctrl_res))
+ @pytest.mark.parametrize("ctrl_wires, ctrl_values, work_wires, general_res", ctrl_res_data)
+ def test_controlled_decomposition_multi_controlled(
+ self, resource_class, local_res, ctrl_wires, ctrl_values, work_wires, general_res
+ ):
+ """Test that the controlled docomposition is correct when controlled on multiple wires."""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op = resource_class(1.23, wires=0)
+ op2 = re.ResourceControlled(
+ op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ expected_resources = copy.copy(local_res)
+ for k, v in general_res.items():
+ expected_resources[k] = v
+
+ assert (
+ op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_resources
+ )
+ assert op2.resources(**op2.resource_params()) == expected_resources
+
class TestRot:
"""Test ResourceRot"""
@@ -89,10 +218,9 @@ class TestRot:
def test_resources(self):
"""Test the resources method"""
op = re.ResourceRot(0.1, 0.2, 0.3, wires=0)
- rx = re.ResourceRX.resource_rep()
ry = re.ResourceRY.resource_rep()
rz = re.ResourceRZ.resource_rep()
- expected = {rx: 1, ry: 1, rz: 1}
+ expected = {ry: 1, rz: 2}
assert op.resources() == expected
@@ -105,10 +233,9 @@ def test_resource_rep(self):
def test_resources_from_rep(self):
"""Test that the resources can be obtained from the compact representation"""
op = re.ResourceRot(0.1, 0.2, 0.3, wires=0)
- rx = re.CompressedResourceOp(re.ResourceRX, {})
- ry = re.CompressedResourceOp(re.ResourceRY, {})
- rz = re.CompressedResourceOp(re.ResourceRZ, {})
- expected = {rx: 1, ry: 1, rz: 1}
+ ry = re.ResourceRY.resource_rep()
+ rz = re.ResourceRZ.resource_rep()
+ expected = {ry: 1, rz: 2}
op_compressed_rep = op.resource_rep_from_op()
op_resource_type = op_compressed_rep.op_type
@@ -119,3 +246,198 @@ def test_resource_params(self):
"""Test that the resource params are correct"""
op = re.ResourceRot(0.1, 0.2, 0.3, wires=0)
assert op.resource_params() == {}
+
+ def test_adjoint_decomp(self):
+ """Test that the adjoint decomposition is correct"""
+
+ expected = {re.ResourceRot.resource_rep(): 1}
+ assert re.ResourceRot.adjoint_resource_decomp() == expected
+
+ op = re.ResourceRot(1.24, 1.25, 1.26, wires=0)
+ dag = re.ResourceAdjoint(op)
+
+ r1 = re.get_resources(op)
+ r2 = re.get_resources(dag)
+
+ assert r1 == r2
+
+ ctrl_data = (
+ ([1], [1], [], {re.ResourceCRot.resource_rep(): 1}),
+ (
+ [1],
+ [0],
+ [],
+ {
+ re.ResourceCRot.resource_rep(): 1,
+ re.ResourceX.resource_rep(): 2,
+ },
+ ),
+ (
+ [1, 2],
+ [1, 1],
+ ["w1"],
+ {
+ re.ResourceRZ.resource_rep(): 3,
+ re.ResourceRY.resource_rep(): 2,
+ re.ResourceMultiControlledX.resource_rep(2, 0, 1): 2,
+ },
+ ),
+ (
+ [1, 2, 3],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {
+ re.ResourceRZ.resource_rep(): 3,
+ re.ResourceRY.resource_rep(): 2,
+ re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2,
+ },
+ ),
+ )
+
+ @pytest.mark.parametrize("ctrl_wires, ctrl_values, work_wires, expected_res", ctrl_data)
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op = re.ResourceRot(1.24, 1.25, 1.26, wires=0)
+ op2 = re.ResourceControlled(
+ op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {re.ResourceRot.resource_rep(): 1}),
+ (2, {re.ResourceRot.resource_rep(): 1}),
+ (5, {re.ResourceRot.resource_rep(): 1}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_resource_pow(self, z, expected_res):
+ """Test that the pow resources are as expected"""
+ op = re.ResourceRot(1.24, 1.25, 1.26, wires=0)
+ assert op.pow_resource_decomp(z) == expected_res
+
+ op2 = re.ResourcePow(op, z)
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+
+class TestPhaseShift:
+ """Test ResourcePhaseShift"""
+
+ def test_resources(self):
+ """Test the resources method"""
+ op = re.ResourcePhaseShift(0.1, wires=0)
+ rz = re.ResourceRZ.resource_rep()
+ global_phase = re.ResourceGlobalPhase.resource_rep()
+
+ expected = {rz: 1, global_phase: 1}
+
+ assert op.resources() == expected
+
+ def test_resource_rep(self):
+ """Test the compressed representation"""
+ op = re.ResourcePhaseShift(0.1, wires=0)
+ expected = re.CompressedResourceOp(re.ResourcePhaseShift, {})
+ assert op.resource_rep() == expected
+
+ def test_resources_from_rep(self):
+ """Test that the resources can be obtained from the compact representation"""
+ op = re.ResourcePhaseShift(0.1, wires=0)
+ global_phase = re.ResourceGlobalPhase.resource_rep()
+ rz = re.ResourceRZ.resource_rep()
+ expected = {global_phase: 1, rz: 1}
+
+ op_compressed_rep = op.resource_rep_from_op()
+ op_resource_type = op_compressed_rep.op_type
+ op_resource_params = op_compressed_rep.params
+ assert op_resource_type.resources(**op_resource_params) == expected
+
+ def test_resource_params(self):
+ """Test that the resource params are correct"""
+ op = re.ResourcePhaseShift(0.1, wires=0)
+ assert op.resource_params() == {}
+
+ def test_adjoint_decomp(self):
+ """Test that the adjoint decomposition is correct"""
+
+ expected = {re.ResourcePhaseShift.resource_rep(): 1}
+ assert re.ResourcePhaseShift.adjoint_resource_decomp() == expected
+
+ op = re.ResourcePhaseShift(0.1, wires=0)
+ dag = re.ResourceAdjoint(op)
+
+ r1 = re.get_resources(op)
+ r2 = re.get_resources(dag)
+
+ assert r1 == r2
+
+ ctrl_data = (
+ ([1], [1], [], {re.ResourceControlledPhaseShift.resource_rep(): 1}),
+ (
+ [1],
+ [0],
+ [],
+ {
+ re.ResourceControlledPhaseShift.resource_rep(): 1,
+ re.ResourceX.resource_rep(): 2,
+ },
+ ),
+ (
+ [1, 2],
+ [1, 1],
+ ["w1"],
+ {
+ re.ResourceControlledPhaseShift.resource_rep(): 1,
+ re.ResourceMultiControlledX.resource_rep(2, 0, 1): 2,
+ },
+ ),
+ (
+ [1, 2, 3],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {
+ re.ResourceControlledPhaseShift.resource_rep(): 1,
+ re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2,
+ },
+ ),
+ )
+
+ @pytest.mark.parametrize("ctrl_wires, ctrl_values, work_wires, expected_res", ctrl_data)
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op = re.ResourcePhaseShift(0.1, wires=0)
+ op2 = re.ResourceControlled(
+ op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ pow_data = (
+ (1, {re.ResourcePhaseShift.resource_rep(): 1}),
+ (2, {re.ResourcePhaseShift.resource_rep(): 1}),
+ (5, {re.ResourcePhaseShift.resource_rep(): 1}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", pow_data)
+ def test_resource_pow(self, z, expected_res):
+ """Test that the pow resources are as expected"""
+ op = re.ResourcePhaseShift(0.1, wires=0)
+ assert op.pow_resource_decomp(z) == expected_res
+
+ op2 = re.ResourcePow(op, z)
+ assert op2.resources(**op2.resource_params()) == expected_res
diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py
index 8406e54ea7b..3a0690049cf 100644
--- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py
+++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py
@@ -18,6 +18,52 @@
# pylint: disable=use-implicit-booleaness-not-comparison,no-self-use
+class TestSingleExcitation:
+ """Tests for the ResourceSingleExcitation class."""
+
+ def test_resources(self):
+ """Test that the resources are correct."""
+ expected = {
+ re.ResourceAdjoint.resource_rep(re.ResourceT, {}): 2,
+ re.ResourceHadamard.resource_rep(): 4,
+ re.ResourceS.resource_rep(): 2,
+ re.ResourceAdjoint.resource_rep(re.ResourceS, {}): 2,
+ re.ResourceCNOT.resource_rep(): 2,
+ re.ResourceRZ.resource_rep(): 1,
+ re.ResourceRY.resource_rep(): 1,
+ re.ResourceT.resource_rep(): 2,
+ }
+ assert re.ResourceSingleExcitation.resources() == expected
+
+ def test_resource_params(self):
+ """Test that the resource params are correct."""
+ op = re.ResourceSingleExcitation(0.5, wires=[0, 1])
+ assert op.resource_params() == {}
+
+ def test_resource_rep(self):
+ """Test that the compressed representation is correct."""
+ expected = re.CompressedResourceOp(re.ResourceSingleExcitation, {})
+ assert re.ResourceSingleExcitation.resource_rep() == expected
+
+ def test_resources_from_rep(self):
+ """Test that the resources can be obtained from the compressed representation."""
+ op = re.ResourceSingleExcitation(0.5, wires=[0, 1])
+ expected = {
+ re.ResourceAdjoint.resource_rep(re.ResourceT, {}): 2,
+ re.ResourceHadamard.resource_rep(): 4,
+ re.ResourceS.resource_rep(): 2,
+ re.ResourceAdjoint.resource_rep(re.ResourceS, {}): 2,
+ re.ResourceCNOT.resource_rep(): 2,
+ re.ResourceRZ.resource_rep(): 1,
+ re.ResourceRY.resource_rep(): 1,
+ re.ResourceT.resource_rep(): 2,
+ }
+ op_compressed_rep = op.resource_rep_from_op()
+ op_resource_type = op_compressed_rep.op_type
+ op_resource_params = op_compressed_rep.params
+ assert op_resource_type.resources(**op_resource_params) == expected
+
+
class TestSingleExcitationMinus:
"""Tests for the ResourceSingleExcitationMinus class."""
@@ -130,6 +176,116 @@ def test_resources_from_rep(self):
assert op_resource_type.resources(**op_resource_params) == expected
+class TestDoubleExcitationMinus:
+ """Tests for the ResourceDoubleExcitationMinus class."""
+
+ def test_resources(self):
+ """Test that the resources are correct."""
+ expected = {
+ re.ResourceGlobalPhase.resource_rep(): 1,
+ re.ResourceDoubleExcitation.resource_rep(): 1,
+ re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0): 2,
+ re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0): 2,
+ }
+ assert re.ResourceDoubleExcitationMinus.resources() == expected
+
+ def test_resource_params(self):
+ """Test that the resource params are correct."""
+ op = re.ResourceDoubleExcitationMinus(0.5, wires=[0, 1, 2, 3])
+ assert op.resource_params() == {}
+
+ def test_resource_rep(self):
+ """Test that the compressed representation is correct."""
+ expected = re.CompressedResourceOp(re.ResourceDoubleExcitationMinus, {})
+ assert re.ResourceDoubleExcitationMinus.resource_rep() == expected
+
+ def test_resources_from_rep(self):
+ """Test that the resources can be obtained from the compressed representation."""
+ op = re.ResourceDoubleExcitationMinus(0.5, wires=[0, 1, 2, 3])
+ expected = {
+ re.ResourceGlobalPhase.resource_rep(): 1,
+ re.ResourceDoubleExcitation.resource_rep(): 1,
+ re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0): 2,
+ re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0): 2,
+ }
+ op_compressed_rep = op.resource_rep_from_op()
+ op_resource_type = op_compressed_rep.op_type
+ op_resource_params = op_compressed_rep.params
+ assert op_resource_type.resources(**op_resource_params) == expected
+
+
+class TestDoubleExcitationPlus:
+ """Tests for the ResourceDoubleExcitationPlus class."""
+
+ def test_resources(self):
+ """Test that the resources are correct."""
+ expected = {
+ re.ResourceGlobalPhase.resource_rep(): 1,
+ re.ResourceDoubleExcitation.resource_rep(): 1,
+ re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0): 2,
+ re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0): 2,
+ }
+ assert re.ResourceDoubleExcitationPlus.resources() == expected
+
+ def test_resource_params(self):
+ """Test that the resource params are correct."""
+ op = re.ResourceDoubleExcitationPlus(0.5, wires=[0, 1, 3, 4])
+ assert op.resource_params() == {}
+
+ def test_resource_rep(self):
+ """Test that the compressed representation is correct."""
+ expected = re.CompressedResourceOp(re.ResourceDoubleExcitationPlus, {})
+ assert re.ResourceDoubleExcitationPlus.resource_rep() == expected
+
+ def test_resources_from_rep(self):
+ """Test that the resources can be obtained from the compressed representation."""
+ op = re.ResourceDoubleExcitationPlus(0.5, wires=[0, 1, 3, 4])
+ expected = {
+ re.ResourceGlobalPhase.resource_rep(): 1,
+ re.ResourceDoubleExcitation.resource_rep(): 1,
+ re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0): 2,
+ re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0): 2,
+ }
+ op_compressed_rep = op.resource_rep_from_op()
+ op_resource_type = op_compressed_rep.op_type
+ op_resource_params = op_compressed_rep.params
+ assert op_resource_type.resources(**op_resource_params) == expected
+
+
+class TestOrbitalRotation:
+ """Tests for the ResourceOrbitalRotation class."""
+
+ def test_resources(self):
+ """Test that the resources are correct."""
+ expected = {
+ re.ResourceFermionicSWAP.resource_rep(): 2,
+ re.ResourceSingleExcitation.resource_rep(): 2,
+ }
+ assert re.ResourceOrbitalRotation.resources() == expected
+
+ def test_resource_params(self):
+ """Test that the resource params are correct."""
+ op = re.ResourceOrbitalRotation(0.5, wires=[0, 1, 3, 4])
+ assert op.resource_params() == {}
+
+ def test_resource_rep(self):
+ """Test that the compressed representation is correct."""
+ expected = re.CompressedResourceOp(re.ResourceOrbitalRotation, {})
+ assert re.ResourceOrbitalRotation.resource_rep() == expected
+
+ def test_resources_from_rep(self):
+ """Test that the resources can be obtained from the compressed representation."""
+ op = re.ResourceOrbitalRotation(0.5, wires=[0, 1, 3, 4])
+ expected = {
+ re.ResourceFermionicSWAP.resource_rep(): 2,
+ re.ResourceSingleExcitation.resource_rep(): 2,
+ }
+ op_compressed_rep = op.resource_rep_from_op()
+ op_resource_type = op_compressed_rep.op_type
+ op_resource_params = op_compressed_rep.params
+ assert op_resource_type.resources(**op_resource_params) == expected
+
+
class TestFermionicSWAP:
"""Tests for the ResourceFermionicSWAP class."""
diff --git a/pennylane/labs/tests/resource_estimation/ops/test_identity.py b/pennylane/labs/tests/resource_estimation/ops/test_identity.py
index c1cf7d04528..f909d06bb26 100644
--- a/pennylane/labs/tests/resource_estimation/ops/test_identity.py
+++ b/pennylane/labs/tests/resource_estimation/ops/test_identity.py
@@ -14,6 +14,8 @@
"""
Tests for identity resource operators
"""
+import pytest
+
import pennylane.labs.resource_estimation as re
# pylint: disable=no-self-use,use-implicit-booleaness-not-comparison
@@ -47,6 +49,54 @@ def test_resources_from_rep(self):
op_resource_params = op_compressed_rep.params
assert op_resource_type.resources(**op_resource_params) == expected
+ def test_resource_adjoint(self):
+ """Test that the adjoint resources are as expected"""
+ op = re.ResourceIdentity(0)
+ op2 = re.ResourceAdjoint(op)
+ assert op.adjoint_resource_decomp() == {re.ResourceIdentity.resource_rep(): 1}
+ assert op2.resources(**op2.resource_params()) == {re.ResourceIdentity.resource_rep(): 1}
+
+ identity_ctrl_data = (
+ ([1], [1], [], {re.ResourceIdentity.resource_rep(): 1}),
+ ([1, 2], [1, 1], ["w1"], {re.ResourceIdentity.resource_rep(): 1}),
+ ([1, 2, 3], [1, 0, 0], ["w1", "w2"], {re.ResourceIdentity.resource_rep(): 1}),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res", identity_ctrl_data
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_work_wires = len(work_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+
+ op = re.ResourceIdentity(0)
+ op2 = re.ResourceControlled(
+ op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ identity_pow_data = (
+ (1, {re.ResourceIdentity.resource_rep(): 1}),
+ (2, {re.ResourceIdentity.resource_rep(): 1}),
+ (5, {re.ResourceIdentity.resource_rep(): 1}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", identity_pow_data)
+ def test_resource_pow(self, z, expected_res):
+ """Test that the pow resources are as expected"""
+ op = re.ResourceIdentity(0)
+ assert op.pow_resource_decomp(z) == expected_res
+
+ op2 = re.ResourcePow(op, z)
+ assert op2.resources(**op2.resource_params()) == expected_res
+
class TestGlobalPhase:
"""Test ResourceGlobalPhase"""
@@ -75,3 +125,67 @@ def test_resources_from_rep(self):
op_resource_type = op_compressed_rep.op_type
op_resource_params = op_compressed_rep.params
assert op_resource_type.resources(**op_resource_params) == expected
+
+ def test_resource_adjoint(self):
+ """Test that the adjoint resources are as expected"""
+ op = re.ResourceGlobalPhase(0.1, wires=0)
+ op2 = re.ResourceAdjoint(op)
+ assert op.adjoint_resource_decomp() == {re.ResourceGlobalPhase.resource_rep(): 1}
+ assert op2.resources(**op2.resource_params()) == {re.ResourceGlobalPhase.resource_rep(): 1}
+
+ globalphase_ctrl_data = (
+ ([1], [1], [], {re.ResourcePhaseShift.resource_rep(): 1}),
+ (
+ [1, 2],
+ [1, 1],
+ ["w1"],
+ {
+ re.ResourcePhaseShift.resource_rep(): 1,
+ re.ResourceMultiControlledX.resource_rep(2, 0, 1): 2,
+ },
+ ),
+ (
+ [1, 2, 3],
+ [1, 0, 0],
+ ["w1", "w2"],
+ {
+ re.ResourcePhaseShift.resource_rep(): 1,
+ re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2,
+ },
+ ),
+ )
+
+ @pytest.mark.parametrize(
+ "ctrl_wires, ctrl_values, work_wires, expected_res", globalphase_ctrl_data
+ )
+ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res):
+ """Test that the controlled resources are as expected"""
+ num_ctrl_wires = len(ctrl_wires)
+ num_ctrl_values = len([v for v in ctrl_values if not v])
+ num_work_wires = len(work_wires)
+
+ op = re.ResourceGlobalPhase(0.1, wires=0)
+ op2 = re.ResourceControlled(
+ op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires
+ )
+
+ assert (
+ op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires)
+ == expected_res
+ )
+ assert op2.resources(**op2.resource_params()) == expected_res
+
+ globalphase_pow_data = (
+ (1, {re.ResourceGlobalPhase.resource_rep(): 1}),
+ (2, {re.ResourceGlobalPhase.resource_rep(): 1}),
+ (5, {re.ResourceGlobalPhase.resource_rep(): 1}),
+ )
+
+ @pytest.mark.parametrize("z, expected_res", globalphase_pow_data)
+ def test_resource_pow(self, z, expected_res):
+ """Test that the pow resources are as expected"""
+ op = re.ResourceGlobalPhase(0.1, wires=0)
+ assert op.pow_resource_decomp(z) == expected_res
+
+ op2 = re.ResourcePow(op, z)
+ assert op2.resources(**op2.resource_params()) == expected_res
diff --git a/pennylane/measurements/classical_shadow.py b/pennylane/measurements/classical_shadow.py
index 7e9dc5285c8..d2e2490921f 100644
--- a/pennylane/measurements/classical_shadow.py
+++ b/pennylane/measurements/classical_shadow.py
@@ -180,8 +180,8 @@ def circuit():
measurements = [qml.classical_shadow(wires=(0,1))]
tape = qml.tape.QuantumTape(ops, measurements, shots=5)
- >>> bits1, recipes1 = qml.execute([tape], device=dev, gradient_fn=None)[0]
- >>> bits2, recipes2 = qml.execute([tape], device=dev, gradient_fn=None)[0]
+ >>> bits1, recipes1 = qml.execute([tape], device=dev, diff_method=None)[0]
+ >>> bits2, recipes2 = qml.execute([tape], device=dev, diff_method=None)[0]
>>> np.all(recipes1 == recipes2)
True
>>> np.all(bits1 == bits2)
@@ -200,8 +200,8 @@ def circuit():
measurements2 = [qml.classical_shadow(wires=(0,1), seed=15)]
tape2 = qml.tape.QuantumTape(ops, measurements2, shots=5)
- >>> bits1, recipes1 = qml.execute([tape1], device=dev, gradient_fn=None)[0]
- >>> bits2, recipes2 = qml.execute([tape2], device=dev, gradient_fn=None)[0]
+ >>> bits1, recipes1 = qml.execute([tape1], device=dev, diff_method=None)[0]
+ >>> bits2, recipes2 = qml.execute([tape2], device=dev, diff_method=None)[0]
>>> np.all(recipes1 == recipes2)
False
>>> np.all(bits1 == bits2)
diff --git a/pennylane/ops/functions/dot.py b/pennylane/ops/functions/dot.py
index c7cc5b5d253..de4c2f76225 100644
--- a/pennylane/ops/functions/dot.py
+++ b/pennylane/ops/functions/dot.py
@@ -52,7 +52,7 @@ def dot(
method (str): The graph colouring heuristic to use in solving minimum clique cover for
grouping, which can be ``'lf'`` (Largest First), ``'rlf'`` (Recursive Largest First),
``'dsatur'`` (Degree of Saturation), or ``'gis'`` (Greedy Independent Set).
- This keyword argument is ignored if ``grouping_type`` is ``None``.
+ This keyword argument is ignored if ``grouping_type`` is ``None``. Defaults to ``'lf'`` if no method is provided.
Raises:
ValueError: if the number of coefficients and operators does not match or if they are empty
diff --git a/pennylane/qchem/molecule.py b/pennylane/qchem/molecule.py
index ed49cd0ac3a..6aa77a3bd80 100644
--- a/pennylane/qchem/molecule.py
+++ b/pennylane/qchem/molecule.py
@@ -62,12 +62,20 @@ class Molecule:
**Example**
+ Import necessary modules:
+
+ >>> from pennylane import numpy as np
+ >>> from pennylane.qchem import Molecule
+
+ Define molecular symbols and geometry:
+
>>> symbols = ['H', 'H']
>>> geometry = np.array([[0.0, 0.0, -0.694349],
- >>> [0.0, 0.0, 0.694349]], requires_grad = True)
+ ... [0.0, 0.0, 0.694349]], requires_grad = True)
>>> mol = Molecule(symbols, geometry)
>>> print(mol.n_electrons)
2
+
"""
def __init__(
diff --git a/pennylane/qchem/vibrational/taylor_ham.py b/pennylane/qchem/vibrational/taylor_ham.py
index 9f3dbbfc60b..a566024641a 100644
--- a/pennylane/qchem/vibrational/taylor_ham.py
+++ b/pennylane/qchem/vibrational/taylor_ham.py
@@ -303,19 +303,19 @@ def taylor_coeffs(pes, max_deg=4, min_deg=3):
>>> pes_twomode = np.zeros((1, 1, 9, 9))
>>> dipole_onemode = np.zeros((1, 9, 3))
>>> gauss_weights = np.array([3.96e-05, 4.94e-03, 8.85e-02,
- 4.33e-01, 7.20e-01, 4.33e-01,
- 8.85e-02, 4.94e-03, 3.96e-05])
+ ... 4.33e-01, 7.20e-01, 4.33e-01,
+ ... 8.85e-02, 4.94e-03, 3.96e-05])
>>> grid = np.array([-3.19, -2.27, -1.47, -0.72, 0.0, 0.72, 1.47, 2.27, 3.19])
>>> pes_object = qml.qchem.VibrationalPES(
- freqs=np.array([0.025]),
- grid=grid,
- uloc=np.array([[1.0]]),
- gauss_weights=gauss_weights,
- pes_data=[pes_onemode, pes_twomode],
- dipole_data=[dipole_onemode],
- localized=True,
- dipole_level=1,
- )
+ ... freqs=np.array([0.025]),
+ ... grid=grid,
+ ... uloc=np.array([[1.0]]),
+ ... gauss_weights=gauss_weights,
+ ... pes_data=[pes_onemode, pes_twomode],
+ ... dipole_data=[dipole_onemode],
+ ... localized=True,
+ ... dipole_level=1,
+ ... )
>>> one, two = qml.qchem.taylor_coeffs(pes_object, 4, 2)
>>> print(one)
[[-0.00088528 -0.00361425 0.00068143]]
@@ -359,22 +359,26 @@ def taylor_dipole_coeffs(pes, max_deg=4, min_deg=1):
>>> freqs = np.array([0.01885397])
>>> grid, weights = np.polynomial.hermite.hermgauss(9)
>>> pes_onebody = np.array([[0.05235573, 0.03093067, 0.01501878, 0.00420778, 0.0,
- 0.00584504, 0.02881817, 0.08483433, 0.22025702]])
+ ... 0.00584504, 0.02881817, 0.08483433, 0.22025702]])
>>> pes_twobody = None
>>> dipole_onebody = np.array([[[-1.92201700e-16, 1.45397041e-16, -1.40451549e-01],
- [-1.51005108e-16, 9.53185441e-17, -1.03377032e-01],
- [-1.22793018e-16, 7.22781963e-17, -6.92825934e-02],
- [-1.96537436e-16, -5.86686504e-19, -3.52245369e-02],
- [ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
- [ 5.24758835e-17, -1.40650833e-16, 3.69955543e-02],
- [-4.52407941e-17, 1.38406311e-16, 7.60888733e-02],
- [-4.63820104e-16, 5.42928787e-17, 1.17726042e-01],
- [ 1.19224372e-16, 9.12491386e-17, 1.64013197e-01]]])
- >>>
- >>> vib_obj = qml.qchem.VibrationalPES(freqs=freqs, grid=grid, gauss_weights=weights,
- uloc = None, pes_data=[pes_onebody, pes_twobody],
- dipole_data=[dipole_onebody], localized=False)
- >>>
+ ... [-1.51005108e-16, 9.53185441e-17, -1.03377032e-01],
+ ... [-1.22793018e-16, 7.22781963e-17, -6.92825934e-02],
+ ... [-1.96537436e-16, -5.86686504e-19, -3.52245369e-02],
+ ... [ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
+ ... [ 5.24758835e-17, -1.40650833e-16, 3.69955543e-02],
+ ... [-4.52407941e-17, 1.38406311e-16, 7.60888733e-02],
+ ... [-4.63820104e-16, 5.42928787e-17, 1.17726042e-01],
+ ... [ 1.19224372e-16, 9.12491386e-17, 1.64013197e-01]]])
+ >>> vib_obj = qml.qchem.VibrationalPES(
+ ... freqs=freqs,
+ ... grid=grid,
+ ... gauss_weights=weights,
+ ... uloc=None,
+ ... pes_data=[pes_onebody, pes_twobody],
+ ... dipole_data=[dipole_onebody],
+ ... localized=False
+ ... )
>>> x, y, z = qml.qchem.taylor_dipole_coeffs(vib_obj, 4, 2)
>>> print(z)
[array([[ 1.64124324e-03, 5.39120159e-03, -4.80053702e-05]])]
@@ -657,19 +661,19 @@ def taylor_hamiltonian(
>>> pes_twomode = np.zeros((1, 1, 9, 9))
>>> dipole_onemode = np.zeros((1, 9, 3))
>>> gauss_weights = np.array([3.96e-05, 4.94e-03, 8.85e-02,
- 4.33e-01, 7.20e-01, 4.33e-01,
- 8.85e-02, 4.94e-03, 3.96e-05])
+ ... 4.33e-01, 7.20e-01, 4.33e-01,
+ ... 8.85e-02, 4.94e-03, 3.96e-05])
>>> grid = np.array([-3.19, -2.27, -1.47, -0.72, 0.0, 0.72, 1.47, 2.27, 3.19])
>>> pes_object = qml.qchem.VibrationalPES(
- freqs=np.array([0.025]),
- grid=grid,
- uloc=np.array([[1.0]]),
- gauss_weights=gauss_weights,
- pes_data=[pes_onemode, pes_twomode],
- dipole_data=[dipole_onemode],
- localized=True,
- dipole_level=1,
- )
+ ... freqs=np.array([0.025]),
+ ... grid=grid,
+ ... uloc=np.array([[1.0]]),
+ ... gauss_weights=gauss_weights,
+ ... pes_data=[pes_onemode, pes_twomode],
+ ... dipole_data=[dipole_onemode],
+ ... localized=True,
+ ... dipole_level=1,
+ ... )
>>> qml.qchem.taylor_hamiltonian(pes_object, 4, 2)
(
-0.003833496032473659 * X(0)
diff --git a/pennylane/qchem/vibrational/vscf.py b/pennylane/qchem/vibrational/vscf.py
index e08e0d15580..95dedbceaba 100644
--- a/pennylane/qchem/vibrational/vscf.py
+++ b/pennylane/qchem/vibrational/vscf.py
@@ -403,25 +403,25 @@ def vscf_integrals(h_integrals, d_integrals=None, modals=None, cutoff=None, cuto
tuple: a tuple containing:
- list[TensorLike[float]]: List of Hamiltonian integrals in VSCF basis for up to 3 coupled vibrational modes.
- list[TensorLike[float]]: List of dipole integrals in VSCF basis for up to 3 coupled vibrational modes.
- Returns ``None`` if ``d_integrals`` are ``None``.
+
+ ``None`` is returned if ``d_integrals`` is ``None``.
**Example**
>>> h1 = np.array([[[0.00968289, 0.00233724, 0.0007408, 0.00199125],
- [0.00233724, 0.02958449, 0.00675431, 0.0021936],
- [0.0007408, 0.00675431, 0.0506012, 0.01280986],
- [0.00199125, 0.0021936, 0.01280986, 0.07282307]]])
+ ... [0.00233724, 0.02958449, 0.00675431, 0.0021936],
+ ... [0.0007408, 0.00675431, 0.0506012, 0.01280986],
+ ... [0.00199125, 0.0021936, 0.01280986, 0.07282307]]])
>>> qml.qchem.vscf_integrals(h_integrals=[h1], modals=[4,4,4])
- (
- [array([[[ 9.36124041e-03, 3.63798208e-19, -3.42019607e-19,
- -3.83743044e-19],
- [ 9.59982270e-19, 2.77803512e-02, 5.18290259e-18,
- -4.82000376e-18],
- [-2.73826508e-19, 4.88583546e-18, 4.63297357e-02,
- -2.87022759e-18],
- [-1.94549340e-19, -5.48544743e-18, -1.41379640e-18,
- 7.92203227e-02]]])], None
- )
+ ([array([[[ 9.36124041e-03, -4.20128342e-19, 3.25260652e-19,
+ 1.08420217e-18],
+ [-9.21571847e-19, 2.77803512e-02, -3.46944695e-18,
+ 5.63785130e-18],
+ [-3.25260652e-19, -8.67361738e-19, 4.63297357e-02,
+ -1.04083409e-17],
+ [ 1.30104261e-18, 5.20417043e-18, -1.38777878e-17,
+ 7.92203227e-02]]])],
+ None)
.. details::
diff --git a/pennylane/qcut/cutcircuit.py b/pennylane/qcut/cutcircuit.py
index a0988e3d3a7..c75b78a7c60 100644
--- a/pennylane/qcut/cutcircuit.py
+++ b/pennylane/qcut/cutcircuit.py
@@ -353,7 +353,7 @@ def circuit(x):
:func:`~.qcut_processing_fn`, which processes the results to the original full circuit
output via a tensor network contraction
- >>> results = qml.execute(tapes, dev, gradient_fn=None)
+ >>> results = qml.execute(tapes, dev, diff_method=None)
>>> qml.qcut.qcut_processing_fn(
... results,
... communication_graph,
diff --git a/pennylane/qcut/cutcircuit_mc.py b/pennylane/qcut/cutcircuit_mc.py
index 7e9e2b0abb0..5118cdddc17 100644
--- a/pennylane/qcut/cutcircuit_mc.py
+++ b/pennylane/qcut/cutcircuit_mc.py
@@ -355,7 +355,7 @@ def circuit(x):
:func:`~.qcut_processing_fn_sample`, which processes the results to approximate the original full circuit
output bitstrings.
- >>> results = qml.execute(tapes, dev, gradient_fn=None)
+ >>> results = qml.execute(tapes, dev, diff_method=None)
>>> qml.qcut.qcut_processing_fn_sample(
... results,
... communication_graph,
diff --git a/pennylane/tape/plxpr_conversion.py b/pennylane/tape/plxpr_conversion.py
index 05d98ee644a..08254fa5c1f 100644
--- a/pennylane/tape/plxpr_conversion.py
+++ b/pennylane/tape/plxpr_conversion.py
@@ -60,8 +60,9 @@ def f(x):
return qml.probs(wires=0), qml.expval(qml.Z(1))
>>> from pennylane.tape.plxpr_conversion import CollectOpsandMeas
+ >>> from jax import make_jaxpr
>>> qml.capture.enable()
- >>> plxpr = jax.make_plxpr(f)(0.5)
+ >>> plxpr = make_jaxpr(f)(0.5)
>>> collector = CollectOpsandMeas()
>>> collector.eval(plxpr.jaxpr, plxpr.consts, 1.2)
[probs(wires=[0]), expval(Z(1))]
@@ -223,7 +224,7 @@ def f(x):
qml.capture.enable()
plxpr = jax.make_jaxpr(f)(0.5)
- tape = qml.capture.convert_to_tape(plxpr.jaxpr, plxpr.consts, 1.2)
+ tape = qml.tape.plxpr_to_tape(plxpr.jaxpr, plxpr.consts, 1.2)
print(qml.drawer.tape_text(tape, decimals=2))
.. code-block::
diff --git a/pennylane/tape/qscript.py b/pennylane/tape/qscript.py
index a0caf54bd41..38cdf5ff6bc 100644
--- a/pennylane/tape/qscript.py
+++ b/pennylane/tape/qscript.py
@@ -141,7 +141,7 @@ class QuantumScript:
using the :func:`~.pennylane.execute` function:
>>> dev = qml.device('default.qubit', wires=(0,'a'))
- >>> qml.execute([qscript], dev, gradient_fn=None)
+ >>> qml.execute([qscript], dev, diff_method=None)
[array([-0.77750694])]
Quantum scripts can also store information about the number and batches of
diff --git a/pennylane/tape/tape.py b/pennylane/tape/tape.py
index 6dff9d64479..4d08187b147 100644
--- a/pennylane/tape/tape.py
+++ b/pennylane/tape/tape.py
@@ -419,7 +419,7 @@ class QuantumTape(QuantumScript, AnnotatedQueue):
>>> tape.num_params
3
- The existing circuit is overriden upon exiting a recording context.
+ The existing circuit is overridden upon exiting a recording context.
Iterating over the quantum circuit can be done by iterating over the tape
object:
@@ -448,7 +448,7 @@ class QuantumTape(QuantumScript, AnnotatedQueue):
device via the :func:`~.pennylane.execute` function:
>>> dev = qml.device("default.qubit", wires=[0, 'a'])
- >>> qml.execute([tape], dev, gradient_fn=None)
+ >>> qml.execute([tape], dev, diff_method=None)
[array([0.77750694])]
A new tape can be created by passing new parameters along with the indices
diff --git a/pennylane/templates/embeddings/amplitude.py b/pennylane/templates/embeddings/amplitude.py
index eef3aa462db..ea940fdfdd0 100644
--- a/pennylane/templates/embeddings/amplitude.py
+++ b/pennylane/templates/embeddings/amplitude.py
@@ -62,7 +62,7 @@ def circuit(f=None):
The final state of the device is - up to a global phase - equivalent to the input passed to the circuit:
>>> state
- tensor([0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], requires_grad=True)
+ array([0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j])
**Differentiating with respect to the features**
@@ -84,7 +84,7 @@ def circuit(f=None):
state = circuit(f=[15, 15, 15, 15])
>>> state
- tensor([0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], requires_grad=True)
+ array([0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j])
**Padding**
@@ -103,7 +103,7 @@ def circuit(f=None):
state = circuit(f=[1/sqrt(2), 1/sqrt(2)])
>>> state
- tensor([0.70710678+0.j, 0.70710678+0.j, 0. +0.j, 0. +0.j], requires_grad=True)
+ array([0.70710678+0.j, 0.70710678+0.j, 0. +0.j, 0. +0.j])
"""
diff --git a/pennylane/templates/state_preparations/state_prep_mps.py b/pennylane/templates/state_preparations/state_prep_mps.py
index 36dd85f8d10..92fd7edda7f 100644
--- a/pennylane/templates/state_preparations/state_prep_mps.py
+++ b/pennylane/templates/state_preparations/state_prep_mps.py
@@ -42,22 +42,14 @@ class MPSPrep(Operation):
np.array([[0.0, 0.107], [0.994, 0.0]]),
np.array(
[
- [[0.0, 0.0, 0.0, -0.0], [1.0, 0.0, 0.0, -0.0]],
- [[0.0, 1.0, 0.0, -0.0], [0.0, 0.0, 0.0, -0.0]],
- ]
- ),
- np.array(
- [
- [[-1.0, 0.0], [0.0, 0.0]],
- [[0.0, 0.0], [0.0, 1.0]],
- [[0.0, -1.0], [0.0, 0.0]],
[[0.0, 0.0], [1.0, 0.0]],
+ [[0.0, 1.0], [0.0, 0.0]],
]
),
np.array([[-1.0, -0.0], [-0.0, -1.0]]),
]
- dev = qml.device("lightning.tensor", wires = 3)
+ dev = qml.device("lightning.tensor", wires=3)
@qml.qnode(dev)
def circuit():
qml.MPSPrep(mps, wires = [0,1,2])
@@ -66,9 +58,8 @@ def circuit():
.. code-block:: pycon
>>> print(circuit())
- [ 0. +0.j 0. +0.j 0. +0.j -0.1066+0.j 0. +0.j 0. +0.j
- 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j
- 0.9943+0.j 0. +0.j 0. +0.j 0. +0.j]
+ [ 0. +0.j -0.10705513+0.j 0. +0.j 0. +0.j
+ 0. +0.j 0. +0.j -0.99451217+0.j 0. +0.j]
.. details::
:title: Usage Details
diff --git a/pennylane/templates/state_preparations/superposition.py b/pennylane/templates/state_preparations/superposition.py
index da6e53d7d55..b892fa180d7 100644
--- a/pennylane/templates/state_preparations/superposition.py
+++ b/pennylane/templates/state_preparations/superposition.py
@@ -140,6 +140,9 @@ class Superposition(Operation):
.. code-block::
+ import pennylane as qml
+ import numpy as np
+
coeffs = np.sqrt(np.array([1/3, 1/3, 1/3]))
bases = np.array([[1, 1, 1], [0, 1, 0], [0, 0, 0]])
wires = [0, 1, 2]
@@ -265,7 +268,7 @@ def decomposition(self): # pylint: disable=arguments-differ
)
@staticmethod
- def compute_decomposition(coefs, bases, wires, work_wire): # pylint: disable=arguments-differ
+ def compute_decomposition(coeffs, bases, wires, work_wire): # pylint: disable=arguments-differ
r"""Representation of the operator as a product of other operators.
Args:
@@ -289,7 +292,7 @@ def compute_decomposition(coefs, bases, wires, work_wire): # pylint: disable=ar
"""
- dic_state = dict(zip(bases, coefs))
+ dic_state = dict(zip(bases, coeffs))
perms = _assign_states(bases)
new_dic_state = {perms[key]: dic_state[key] for key in dic_state if key in perms}
@@ -304,7 +307,7 @@ def compute_decomposition(coefs, bases, wires, work_wire): # pylint: disable=ar
op_list.append(
qml.StatePrep(
qml.math.stack(sorted_coefficients),
- wires=wires[-int(qml.math.ceil(qml.math.log2(len(coefs)))) :],
+ wires=wires[-int(qml.math.ceil(qml.math.log2(len(coeffs)))) :],
pad_with=0,
)
)
diff --git a/pennylane/templates/subroutines/adder.py b/pennylane/templates/subroutines/adder.py
index ade18bdad44..2ad462abf0f 100644
--- a/pennylane/templates/subroutines/adder.py
+++ b/pennylane/templates/subroutines/adder.py
@@ -47,7 +47,7 @@ class Adder(Operation):
mod (int): the modulo for performing the addition. If not provided, it will be set to its maximum value, :math:`2^{\text{len(x_wires)}}`.
work_wires (Sequence[int]): the auxiliary wires to use for the addition. The
work wires are not needed if :math:`mod=2^{\text{len(x_wires)}}`, otherwise two work wires
- should be provided. Defaults to empty set.
+ should be provided. Defaults to empty tuple.
**Example**
diff --git a/pennylane/templates/subroutines/gqsp.py b/pennylane/templates/subroutines/gqsp.py
index c980b19b462..d676391adc1 100644
--- a/pennylane/templates/subroutines/gqsp.py
+++ b/pennylane/templates/subroutines/gqsp.py
@@ -39,7 +39,7 @@ class GQSP(Operation):
* & * \\
\end{pmatrix}
- The implementation requires one auxiliary qubit.
+ The implementation requires one control qubit.
Args:
diff --git a/pennylane/templates/subroutines/mod_exp.py b/pennylane/templates/subroutines/mod_exp.py
index 103e22a918b..467ca7396b8 100644
--- a/pennylane/templates/subroutines/mod_exp.py
+++ b/pennylane/templates/subroutines/mod_exp.py
@@ -50,7 +50,7 @@ class ModExp(Operation):
mod (int): the modulo for performing the exponentiation. If not provided, it will be set to its maximum value, :math:`2^{\text{len(output_wires)}}`
work_wires (Sequence[int]): the auxiliary wires to use for the exponentiation. If
:math:`mod=2^{\text{len(output_wires)}}`, the number of auxiliary wires must be ``len(output_wires)``. Otherwise
- ``len(output_wires) + 2`` auxiliary wires are needed. Defaults to empty set.
+ ``len(output_wires) + 2`` auxiliary wires are needed. Defaults to empty tuple.
**Example**
diff --git a/pennylane/templates/subroutines/out_adder.py b/pennylane/templates/subroutines/out_adder.py
index edf091572d9..d8ead9c5121 100644
--- a/pennylane/templates/subroutines/out_adder.py
+++ b/pennylane/templates/subroutines/out_adder.py
@@ -44,7 +44,7 @@ class OutAdder(Operation):
y_wires (Sequence[int]): the wires that store the integer :math:`y`
output_wires (Sequence[int]): the wires that store the addition result. If the register is in a non-zero state :math:`b`, the solution will be added to this value.
mod (int): the modulo for performing the addition. If not provided, it will be set to its maximum value, :math:`2^{\text{len(output_wires)}}`.
- work_wires (Sequence[int]): the auxiliary wires to use for the addition. The work wires are not needed if :math:`mod=2^{\text{len(output_wires)}}`, otherwise two work wires should be provided. Defaults to empty set.
+ work_wires (Sequence[int]): the auxiliary wires to use for the addition. The work wires are not needed if :math:`mod=2^{\text{len(output_wires)}}`, otherwise two work wires should be provided. Defaults to empty tuple.
**Example**
diff --git a/pennylane/templates/subroutines/out_multiplier.py b/pennylane/templates/subroutines/out_multiplier.py
index 58ecc1c611d..c5842882527 100644
--- a/pennylane/templates/subroutines/out_multiplier.py
+++ b/pennylane/templates/subroutines/out_multiplier.py
@@ -45,7 +45,7 @@ class OutMultiplier(Operation):
mod (int): the modulo for performing the multiplication. If not provided, it will be set to its maximum value, :math:`2^{\text{len(output_wires)}}`
work_wires (Sequence[int]): the auxiliary wires to use for the multiplication. The
work wires are not needed if :math:`mod=2^{\text{len(output_wires)}}`, otherwise two work wires
- should be provided. Defaults to empty set.
+ should be provided. Defaults to empty tuple.
**Example**
diff --git a/pennylane/templates/subroutines/out_poly.py b/pennylane/templates/subroutines/out_poly.py
index 4472bfb4955..f54947ce8d4 100644
--- a/pennylane/templates/subroutines/out_poly.py
+++ b/pennylane/templates/subroutines/out_poly.py
@@ -152,14 +152,15 @@ class OutPoly(Operation):
Args:
polynomial_function (callable): The polynomial function to be applied. The number of arguments in the function
- must be equal to the number of input registers.
- input_registers (List[Union[Wires, Sequence[int]]]): List containing the wires (or the wire indices) used to store each variable of the polynomial.
+ must be equal to the number of input registers.
+ input_registers (List[Union[Wires, Sequence[int]]]): List containing the wires (or the wire indices) used to
+ store each variable of the polynomial.
output_wires (Union[Wires, Sequence[int]]): The wires (or wire indices) used to store the output of the operation.
- mod (int, optional): The integer for performing the modulo on the result of the polynomial operation. If not provided, it defaults
- to :math:`2^{n}`, where :math:`n` is the number of qubits in the output register.
- work_wires (Union[Wires, Sequence[int]], optional): The auxiliary wires to use for performing the polynomial operation. The
- work wires are not needed if :math:`mod=2^{\text{length(output_wires)}}`, otherwise two work wires
- should be provided. Default is empty set.
+ mod (int, optional): The integer for performing the modulo on the result of the polynomial operation. If not provided,
+ it defaults to :math:`2^{n}`, where :math:`n` is the number of qubits in the output register.
+ work_wires (Union[Wires, Sequence[int]], optional): The auxiliary wires to use for performing the polynomial operation.
+ The work wires are not needed if :math:`mod=2^{\text{length(output_wires)}}`, otherwise two work wires should be
+ provided. Defaults to empty tuple.
Raises:
ValueError: If `mod` is not :math:`2^{\text{length(output_wires)}}` and insufficient number of work wires are provided.
diff --git a/pennylane/templates/subroutines/phase_adder.py b/pennylane/templates/subroutines/phase_adder.py
index 9460f0e3feb..4c4ffa433df 100644
--- a/pennylane/templates/subroutines/phase_adder.py
+++ b/pennylane/templates/subroutines/phase_adder.py
@@ -65,7 +65,7 @@ class PhaseAdder(Operation):
value for `mod`.
mod (int): the modulo for performing the addition. If not provided, it will be set to its maximum value, :math:`2^{\text{len(x_wires)}}`.
work_wire (Sequence[int] or int): the auxiliary wire to use for the addition. Optional
- when `mod` is :math:`2^{len(x\_wires)}`. Defaults to empty set.
+ when `mod` is :math:`2^{len(x\_wires)}`. Defaults to empty tuple.
**Example**
diff --git a/pennylane/templates/subroutines/qsvt.py b/pennylane/templates/subroutines/qsvt.py
index 03f1411276b..129e6d930a1 100644
--- a/pennylane/templates/subroutines/qsvt.py
+++ b/pennylane/templates/subroutines/qsvt.py
@@ -17,6 +17,7 @@
# pylint: disable=too-many-arguments
import copy
import warnings
+from typing import Literal
import numpy as np
from numpy.polynomial import Polynomial, chebyshev
@@ -215,6 +216,8 @@ def qsvt(A, poly, encoding_wires=None, block_encoding=None, **kwargs):
(Operator): A quantum operator implementing QSVT on the matrix ``A`` with the
specified encoding and projector phases.
+ .. seealso:: :class:`~.QSVT`
+
Example:
.. code-block:: python
@@ -326,7 +329,7 @@ def circuit():
"""
- if encoding_wires is None or block_encoding is None or "wires" in kwargs.keys():
+ if encoding_wires is None or block_encoding is None or "wires" in kwargs:
warnings.warn(
"You may be trying to use the old `qsvt` functionality (now `qml.qsvt_legacy`).\n"
"Make sure you pass a polynomial instead of angles.\n"
@@ -1035,7 +1038,7 @@ def circuit_qsvt():
)
-def poly_to_angles(poly, routine, angle_solver="root-finding"):
+def poly_to_angles(poly, routine, angle_solver: Literal["root-finding"] = "root-finding"):
r"""
Computes the angles needed to implement a polynomial transformation with quantum signal processing (QSP)
or quantum singular value transformation (QSVT).
@@ -1050,6 +1053,7 @@ def poly_to_angles(poly, routine, angle_solver="root-finding"):
routine (str): the routine for which the angles are computed. Must be either ``"QSP"`` or ``"QSVT"``.
angle_solver (str): the method used to calculate the angles. Default is ``"root-finding"``.
+ ``"root-finding"`` is currently the only supported method.
Returns:
(tensor-like): computed angles for the specified routine
diff --git a/pennylane/transforms/core/transform_program.py b/pennylane/transforms/core/transform_program.py
index b4a132b4ac6..fa74b78e0dd 100644
--- a/pennylane/transforms/core/transform_program.py
+++ b/pennylane/transforms/core/transform_program.py
@@ -237,6 +237,8 @@ class TransformProgram:
Programs have several implemented dunder methods for easy manipulation.
+ >>> from pennylane.transforms.core.transform_program import TransformProgram
+ >>> from copy import copy
>>> program = TransformProgram()
>>> program.add_transform(qml.compile)
>>> program.add_transform(qml.transforms.cancel_inverses)
@@ -252,7 +254,7 @@ class TransformProgram:
True
>>> True if TransformProgram() else False
False
- >>> program2 = copy.copy(program)
+ >>> program2 = copy(program)
>>> program2 == program
True
>>> qml.compile in program
diff --git a/pennylane/transforms/diagonalize_measurements.py b/pennylane/transforms/diagonalize_measurements.py
index bad7a9fd020..79729748403 100644
--- a/pennylane/transforms/diagonalize_measurements.py
+++ b/pennylane/transforms/diagonalize_measurements.py
@@ -90,19 +90,19 @@ def circuit(x):
measurements to be in the Z basis, so the original circuit
>>> print(qml.draw(circuit, level=0)([np.pi/4, np.pi/4]))
- 0: ──RY(0.79)─┤ ╭ ╭Var[(0.50*Y)+X]
+ 0: ──RY(0.79)─┤ ╭ ╭Var[𝓗(0.50)]
1: ──RX(0.79)─┤ ╰ │
- 2: ───────────┤ ╰Var[(0.50*Y)+X]
+ 2: ───────────┤ ╰Var[𝓗(0.50)]
becomes
>>> print(qml.draw(circuit)([np.pi/4, np.pi/4]))
- 0: ──RY(0.79)──H────┤ ╭ ╭Var[(0.50*Z)+Z]
+ 0: ──RY(0.79)──H────┤ ╭ ╭Var[𝓗(0.50)]
1: ──RX(0.79)───────┤ ╰ │
- 2: ──Z─────────S──H─┤ ╰Var[(0.50*Z)+Z]
+ 2: ──Z─────────S──H─┤ ╰Var[𝓗(0.50)]
>>> circuit([np.pi/4, np.pi/4])
- (tensor(0.5, requires_grad=True), tensor(0.75, requires_grad=True))
+ (0.5, 0.75)
.. details::
:title: Usage Details
@@ -116,7 +116,7 @@ def circuit(x):
qml.expval(qml.X(0) + qml.Hermitian([[1, 0], [0, 1]], wires=[1]))
]
tape = qml.tape.QuantumScript(measurements=measurements)
- tapes, processsing_fn = diagnalize_measurements(tape)
+ tapes, processsing_fn = diagonalize_measurements(tape)
>>> tapes[0].operations
[H(0)]
diff --git a/pennylane/workflow/_capture_qnode.py b/pennylane/workflow/_capture_qnode.py
index 41b8b37e175..940d8edddc6 100644
--- a/pennylane/workflow/_capture_qnode.py
+++ b/pennylane/workflow/_capture_qnode.py
@@ -27,12 +27,12 @@
and reshape the outputs from measurements on the device when multiple measurements are present.
**Gradients other than default qubit backprop**. We managed to get backprop of default qubit for
-free, but no other gradients methods have support yet.
+free, but no other gradient methods have support yet.
-**MCM methods other than single branch statistics**. Mid circuit measurements
+**MCM methods other than single branch statistics**. Mid-circuit measurements
are only handled via a "single branch statistics" algorithm, which will lead to unexpected
results. Even on analytic devices, one branch will be randomly chosen on each execution.
-Returning measurements based on mid circuit measurements, ``qml.sample(m0)``,
+Returning measurements based on mid-circuit measurements, ``qml.sample(m0)``,
is also not yet supported on default qubit or lightning.
>>> @qml.qnode(qml.device('default.qubit', wires=1))
@@ -76,8 +76,8 @@
2. (self: pennylane_lightning.lightning_qubit_ops.StateVectorC128, arg0: list[int], arg1: list[bool], arg2: list[int], arg3: bool, arg4: list[float]) -> None
**Grouping commuting measurements and/or splitting up non-commuting measurements.** Currently, each
-measurment is fully independent and generated from different raw samples than every other measurement.
-To generate multiple measurments from the same samples, we need a way of denoting which measurements
+measurement is fully independent and generated from different raw samples than every other measurement.
+To generate multiple measurement from the same samples, we need a way of denoting which measurements
should be taken together. A "Combination measurement process" higher order primitive, or something like it.
We will also need to figure out how to implement splitting up a circuit with non-commuting measurements into
multiple circuits.
diff --git a/pennylane/workflow/execution.py b/pennylane/workflow/execution.py
index 4a84b4724ac..7c9fb93b001 100644
--- a/pennylane/workflow/execution.py
+++ b/pennylane/workflow/execution.py
@@ -68,27 +68,27 @@ def execute(
for the gradient (if supported).
interface (str, Interface): The interface that will be used for classical auto-differentiation.
This affects the types of parameters that can exist on the input tapes.
- Available options include ``autograd``, ``torch``, ``tf``, ``jax`` and ``auto``.
+ Available options include ``autograd``, ``torch``, ``tf``, ``jax``, and ``auto``.
transform_program(.TransformProgram): A transform program to be applied to the initial tape.
inner_transform (.TransformProgram): A transform program to be applied to the tapes in
inner execution, inside the ml interface.
config (qml.devices.ExecutionConfig): A data structure describing the parameters
needed to fully describe the execution.
grad_on_execution (bool, str): Whether the gradients should be computed
- on the execution or not. Only applies
+ on the execution or not. It only applies
if the device is queried for the gradient; gradient transform
functions available in ``qml.gradients`` are only supported on the backward
pass. The 'best' option chooses automatically between the two options and is default.
gradient_kwargs (dict): dictionary of keyword arguments to pass when
- determining the gradients of tapes
+ determining the gradients of tapes.
cache (None, bool, dict, Cache): Whether to cache evaluations. This can result in
a significant reduction in quantum evaluations during gradient computations.
- cachesize (int): the size of the cache
+ cachesize (int): the size of the cache.
max_diff (int): If ``gradient_fn`` is a gradient transform, this option specifies
the maximum number of derivatives to support. Increasing this value allows
- for higher order derivatives to be extracted, at the cost of additional
- (classical) computational overhead during the backwards pass.
- device_vjp=False (Optional[bool]): whether or not to use the device provided jacobian
+ for higher-order derivatives to be extracted, at the cost of additional
+ (classical) computational overhead during the backward pass.
+ device_vjp=False (Optional[bool]): whether or not to use the device-provided Jacobian
product if it is available.
mcm_config (dict): Dictionary containing configuration options for handling
mid-circuit measurements.
diff --git a/pennylane/workflow/qnode.py b/pennylane/workflow/qnode.py
index 53e50d21e1d..14f4f6fc9cc 100644
--- a/pennylane/workflow/qnode.py
+++ b/pennylane/workflow/qnode.py
@@ -364,11 +364,7 @@ class QNode:
to support it.) A list of supporting operators is available in
:obj:`~.pennylane.ops.qubit.attributes.supports_broadcasting`.
Whether or not broadcasting delivers an increased performance will depend on whether the
- used device is a classical simulator and natively supports this. The latter can be checked
- with the capabilities of the device:
-
- >>> dev.capabilities()["supports_broadcasting"]
- True
+ used device is a classical simulator and natively supports this.
If a device does not natively support broadcasting, it will execute broadcasted QNode calls
by expanding the input arguments into separate executions. That is, every device can
diff --git a/pennylane/workflow/return_types_spec.rst b/pennylane/workflow/return_types_spec.rst
index 90233e8bedd..38597c914e9 100644
--- a/pennylane/workflow/return_types_spec.rst
+++ b/pennylane/workflow/return_types_spec.rst
@@ -53,7 +53,7 @@ array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Empty Wires
^^^^^^^^^^^
-Some measurments allow broadcasting over all available wires, like ``qml.probs()``, ``qml.sample()``,
+Some measurements allow broadcasting over all available wires, like ``qml.probs()``, ``qml.sample()``,
or ``qml.state()``. In such a case, the measurement process instance should have empty wires.
The shape of the result object may be dictated either by the device or the other operations present in the circuit.
diff --git a/setup.py b/setup.py
index a6ce2fb7a08..47d6484df65 100644
--- a/setup.py
+++ b/setup.py
@@ -30,7 +30,7 @@
"appdirs",
"autoray>=0.6.11",
"cachetools",
- "pennylane-lightning>=0.39",
+ "pennylane-lightning>=0.40",
"requests",
"typing_extensions",
"packaging",
diff --git a/tests/capture/autograph/test_autograph.py b/tests/capture/autograph/test_autograph.py
index 1665eb3fec0..5ec9e7fa59a 100644
--- a/tests/capture/autograph/test_autograph.py
+++ b/tests/capture/autograph/test_autograph.py
@@ -270,7 +270,7 @@ def circ():
assert jax.core.eval_jaxpr(plxpr.jaxpr, plxpr.consts)[0] == -1
def test_ctrl_op(self):
- """Test that the adjoint of an operator successfully passes through autograph without raising an error"""
+ """Test that controlled operators successfully pass through autograph"""
@qml.qnode(qml.device("default.qubit", wires=2))
def circ():
diff --git a/tests/capture/test_base_interpreter.py b/tests/capture/test_base_interpreter.py
index e02d1d3917e..a453005d97f 100644
--- a/tests/capture/test_base_interpreter.py
+++ b/tests/capture/test_base_interpreter.py
@@ -188,7 +188,7 @@ def f():
def test_measurement_handling():
- """Test that the default measurment handling works."""
+ """Test that the default measurement handling works."""
@SimplifyInterpreter()
def f(w):
diff --git a/tests/devices/qubit_mixed/conftest.py b/tests/devices/qubit_mixed/conftest.py
index 1997eb1614d..5f412ebc9dc 100644
--- a/tests/devices/qubit_mixed/conftest.py
+++ b/tests/devices/qubit_mixed/conftest.py
@@ -17,7 +17,7 @@
from scipy.stats import unitary_group
-def get_random_mixed_state(num_qubits):
+def get_random_mixed_state(num_qubits: int) -> np.ndarray:
"""
Generates a random mixed state for testing purposes.
diff --git a/tests/drawer/test_draw_mpl.py b/tests/drawer/test_draw_mpl.py
index 23ca78d19a2..c8c162cce42 100644
--- a/tests/drawer/test_draw_mpl.py
+++ b/tests/drawer/test_draw_mpl.py
@@ -319,7 +319,7 @@ def test_wire_order_not_on_device(self):
assert ax.texts[2].get_text() == "0"
plt.close()
- def test_wire_options(self):
+ def test_uniform_wire_options(self):
"""Test wire options modifies wire styling"""
_, ax = qml.draw_mpl(circuit1, wire_options={"color": "black", "linewidth": 4})(1.23, 2.34)
@@ -328,6 +328,11 @@ def test_wire_options(self):
assert w.get_color() == "black"
assert w.get_linewidth() == 4
+ plt.close()
+
+ def test_individual_wire_options(self):
+ """Test wire option styling when individual wires have their own options specified"""
+
@qml.qnode(dev)
def f_circ(x):
"""Circuit on ten qubits."""
@@ -399,6 +404,58 @@ def f_circ(x):
plt.close()
+ def test_individual_wire_options_with_string_labels(self):
+ """Test that individual wire options work with string wire labels"""
+
+ @qml.qnode(qml.device("default.qubit"))
+ def circuit():
+ qml.X("a")
+ qml.Y("b")
+ return qml.expval(qml.Z("a"))
+
+ wire_options = {
+ "color": "teal",
+ "linewidth": 5,
+ "b": {"color": "orange", "linestyle": "--"},
+ }
+ _, ax = qml.draw_mpl(circuit, wire_options=wire_options)()
+
+ for i, w in enumerate(ax.lines):
+ assert w.get_linewidth() == 5
+ if i == 0:
+ assert w.get_color() == "teal"
+ assert w.get_linestyle() == "-"
+ if i == 1:
+ assert w.get_color() == "orange"
+ assert w.get_linestyle() == "--"
+
+ def test_wire_options_and_wire_order(self):
+ """Test that individual wire options work with specifying a wire_order"""
+
+ device = qml.device("default.qubit", wires=4)
+
+ @qml.qnode(device)
+ def circuit():
+ for w in device.wires:
+ qml.X(w)
+ return qml.expval(qml.Z(0))
+
+ wire_options = {
+ "color": "teal",
+ "linewidth": 5,
+ 3: {"color": "orange", "linestyle": "--"}, # wire 3 should be orange and dashed
+ }
+ _, ax = qml.draw_mpl(circuit, wire_order=[1, 3, 0, 2], wire_options=wire_options)()
+
+ for i, w in enumerate(ax.lines):
+ assert w.get_linewidth() == 5
+ if i == 1:
+ assert w.get_color() == "orange"
+ assert w.get_linestyle() == "--"
+ else:
+ assert w.get_color() == "teal"
+ assert w.get_linestyle() == "-"
+
class TestMPLIntegration:
"""Test using matplotlib styling to modify look of graphic."""
diff --git a/tests/drawer/test_drawer_utils.py b/tests/drawer/test_drawer_utils.py
index 6ef4c2f6e57..ba4b35c1cff 100644
--- a/tests/drawer/test_drawer_utils.py
+++ b/tests/drawer/test_drawer_utils.py
@@ -220,7 +220,7 @@ def test_single_measure_single_cond(self):
assert wires == [[0, 0]]
def test_multiple_measure_multiple_cond(self):
- """Test a case with multiple measurments and multiple conditionals."""
+ """Test a case with multiple measurements and multiple conditionals."""
m0 = qml.measure(0)
m1 = qml.measure(1)
m2_nonused = qml.measure(2)
diff --git a/tests/drawer/test_mpldrawer.py b/tests/drawer/test_mpldrawer.py
index b28e6070bbf..1dd5f00a20a 100644
--- a/tests/drawer/test_mpldrawer.py
+++ b/tests/drawer/test_mpldrawer.py
@@ -35,17 +35,19 @@
class TestInitialization:
"""Tests drawer creation"""
- @pytest.mark.parametrize("n_wires", [2, 3])
+ @pytest.mark.parametrize("wire_map", [{0: 0, 1: 1}, {"a": 0, "b": 1, "c": 2}])
@pytest.mark.parametrize("n_layers", [2, 3])
- def test_figsize_wires(self, n_wires, n_layers):
+ def test_figsize_wires(self, wire_map, n_layers):
"""Tests the figure is sized correctly."""
- drawer = MPLDrawer(n_wires=n_wires, n_layers=n_layers)
+ n_wires = len(wire_map)
+
+ drawer = MPLDrawer(wire_map=wire_map, n_layers=n_layers)
assert drawer.fig.get_figwidth() == (n_layers + 3)
assert drawer.fig.get_figheight() == (n_wires + 1)
- drawer = MPLDrawer(n_wires=n_wires, n_layers=n_layers)
+ drawer = MPLDrawer(wire_map=wire_map, n_layers=n_layers)
lines = drawer.ax.lines
@@ -62,7 +64,9 @@ def test_figsize_classical_wires(self):
c_wires = 4
n_layers = 1
- drawer = MPLDrawer(n_wires=n_wires, n_layers=n_layers, c_wires=c_wires)
+ drawer = MPLDrawer(
+ wire_map={i: i for i in range(n_wires)}, n_layers=n_layers, c_wires=c_wires
+ )
assert drawer.fig.get_figheight() == n_wires + 1 + 0.25 * 4 + 0.5
assert drawer.fig.get_figwidth() == n_layers + 3
@@ -73,7 +77,7 @@ def test_figsize_classical_wires(self):
def test_customfigsize(self):
"""Tests a custom figsize alters the size"""
- drawer = MPLDrawer(1, 1, figsize=(5, 5))
+ drawer = MPLDrawer(1, {0: 0}, figsize=(5, 5))
assert drawer.fig.get_figwidth() == 5
assert drawer.fig.get_figheight() == 5
@@ -83,7 +87,7 @@ def test_customfigure(self):
"""Tests a custom figure is used"""
fig = plt.figure()
- drawer = MPLDrawer(1, 1, fig=fig)
+ drawer = MPLDrawer(1, {0: 0}, fig=fig)
assert drawer.fig == fig
plt.close()
@@ -91,7 +95,7 @@ def test_customfigure(self):
def test_config_params_set(self):
"""Tests sizing hidden variables are set."""
- drawer = MPLDrawer(1, 1)
+ drawer = MPLDrawer(1, {0: 0})
assert drawer._box_length == 0.75
assert drawer._circ_rad == 0.3
@@ -111,7 +115,7 @@ def test_wires_formatting(self):
rgba_red = (1, 0, 0, 1)
options = {"linewidth": 3, "color": rgba_red}
- drawer = MPLDrawer(n_wires=2, n_layers=2, wire_options=options)
+ drawer = MPLDrawer(wire_map={0: 0, 1: 1}, n_layers=2, wire_options=options)
for line in drawer.ax.lines:
assert line.get_linewidth() == 3
@@ -121,7 +125,7 @@ def test_wires_formatting(self):
def test_fontsize(self):
"""Test fontsize can be get and set via property."""
- drawer = MPLDrawer(1, 1)
+ drawer = MPLDrawer(1, {0: 0})
assert drawer._fontsize == drawer.fontsize
assert drawer.fontsize == 14
@@ -139,9 +143,11 @@ class TestLabels:
def test_labels(self):
"""Tests labels are added"""
- drawer = MPLDrawer(1, 3)
+ wire_map = {"a": 0, "b": 1, "c": 2}
+
+ drawer = MPLDrawer(1, wire_map)
- labels = ("a", "b", "c")
+ labels = wire_map.keys()
drawer.label(labels)
drawn_labels = drawer.ax.texts
@@ -156,10 +162,12 @@ def test_labels(self):
def test_labels_formatting(self):
"""Test labels are formatted with text options."""
- drawer = MPLDrawer(1, 3)
+ wire_map = {0: 0, 1: 1, 2: 2}
+
+ drawer = MPLDrawer(1, wire_map)
rgba_red = (1, 0, 0, 1)
- labels = (0, 1, 2)
+ labels = wire_map.keys()
options = {"fontsize": 10, "color": rgba_red}
drawer.label(labels, text_options=options)
@@ -172,9 +180,12 @@ def test_labels_formatting(self):
@pytest.mark.parametrize("n_layers", [1, 4])
def test_crop_wire_labels(self, n_layers):
"""Test that cropping wire labels works."""
- drawer = MPLDrawer(n_layers, 3)
- labels = ("a", "b", "c")
+ wire_map = {"a": 0, "b": 1, "c": 2}
+
+ drawer = MPLDrawer(n_layers, wire_map)
+
+ labels = wire_map.keys()
drawer.label(labels)
old_width = drawer.fig.get_figwidth()
assert old_width == n_layers + 3
@@ -190,7 +201,7 @@ def test_crop_wire_labels(self, n_layers):
def test_erase_wire():
"""Test the erase wire method."""
- drawer = MPLDrawer(5, 1)
+ drawer = MPLDrawer(5, {0: 0})
drawer.erase_wire(1, 0, 3)
assert len(drawer.ax.patches) == 1
@@ -208,7 +219,7 @@ class TestBoxGate:
def test_simple_box(self):
"""tests basic functionality of box_gate."""
- drawer = MPLDrawer(1, 1)
+ drawer = MPLDrawer(1, {0: 0})
drawer.box_gate(0, 0, "X")
@@ -230,7 +241,7 @@ def test_simple_box(self):
def test_multiwire_box(self):
"""tests a gate spanning multiple wires."""
- drawer = MPLDrawer(1, 3)
+ drawer = MPLDrawer(1, {0: 0, 1: 1, 2: 2})
drawer.box_gate(0, (0, 2), text="Tall Gate")
box = drawer.ax.patches[0]
@@ -251,7 +262,7 @@ def test_multiwire_box(self):
def test_notch_standard_styling(self):
"""Test notch styling is correct"""
- drawer = MPLDrawer(n_layers=1, n_wires=3)
+ drawer = MPLDrawer(n_layers=1, wire_map={i: i for i in range(3)})
drawer.box_gate(0, (0, 2))
xs = [-0.415, 0.375, -0.415, 0.375]
@@ -273,7 +284,7 @@ def test_notch_standard_styling(self):
def test_active_wire_notches_number(self, wires, n_notches):
"""Tests the number of active wires drawn is the expected amount."""
- drawer = MPLDrawer(n_layers=1, n_wires=4)
+ drawer = MPLDrawer(n_layers=1, wire_map={i: i for i in range(4)})
drawer.box_gate(layer=0, wires=wires)
assert len(drawer.ax.patches) == (n_notches + 1)
@@ -281,7 +292,7 @@ def test_active_wire_notches_number(self, wires, n_notches):
def test_no_active_wire_notches(self):
"""Test active wire notches deactivated by keyword."""
- drawer = MPLDrawer(n_layers=1, n_wires=3)
+ drawer = MPLDrawer(n_layers=1, wire_map={i: i for i in range(3)})
drawer.box_gate(layer=0, wires=(0, 2), active_wire_notches=False)
# only one patch for big box, no patches for notches
@@ -291,7 +302,7 @@ def test_no_active_wire_notches(self):
def test_extra_width(self):
"""tests a box with added width."""
- drawer = MPLDrawer(1, 3)
+ drawer = MPLDrawer(1, {i: i for i in range(3)})
drawer.box_gate(0, (0, 2), text="Wide Gate", extra_width=0.4)
box = drawer.ax.patches[0]
@@ -315,7 +326,7 @@ def test_extra_width(self):
def test_box_formatting(self):
"""Tests that box_options influences the rectangle"""
- drawer = MPLDrawer(1, 3)
+ drawer = MPLDrawer(1, {i: i for i in range(3)})
rgba_red = (1, 0, 0, 1)
rgba_green = (0, 1, 0, 1)
options = {"facecolor": rgba_red, "edgecolor": rgba_green, "zorder": 5}
@@ -335,7 +346,7 @@ def test_box_formatting(self):
def test_text_formatting(self):
"""Tests rotated text"""
- drawer = MPLDrawer(1, 1)
+ drawer = MPLDrawer(1, {0: 0})
rgba_red = (1, 0, 0, 1)
options = {"color": rgba_red, "rotation": "vertical"}
drawer.box_gate(0, 0, text="X", text_options=options)
@@ -352,7 +363,7 @@ class TestCTRL:
def test_ctrl_no_target(self):
"""Tests a single control with no target"""
- drawer = MPLDrawer(1, 1)
+ drawer = MPLDrawer(1, {0: 0})
drawer.ctrl(0, 0)
@@ -371,7 +382,7 @@ def test_ctrl_no_target(self):
def test_ctrl_multi_wires(self):
"""Tests two control wires with no target."""
- drawer = MPLDrawer(1, 3)
+ drawer = MPLDrawer(1, {i: i for i in range(3)})
ctrl_wires = (0, 1)
drawer.ctrl(0, ctrl_wires)
@@ -392,7 +403,7 @@ def test_ctrl_multi_wires(self):
def test_ctrl_on_zero(self):
"""Tests a control on zero circle is open"""
- drawer = MPLDrawer(1, 1)
+ drawer = MPLDrawer(1, {0: 0})
drawer.ctrl(0, 0, control_values=False)
@@ -409,7 +420,7 @@ def test_ctrl_on_zero(self):
def test_ctrl_control_values_error(self):
"""Tests a ValueError is raised if different number of wires and control_values."""
- drawer = MPLDrawer(1, 2)
+ drawer = MPLDrawer(1, {0: 0, 1: 1})
with pytest.raises(ValueError, match="`control_values` must be the same length"):
drawer.ctrl(0, (0, 1), control_values=True)
@@ -419,7 +430,7 @@ def test_ctrl_control_values_error(self):
def test_ctrl_formatting(self):
"""Tests two control wires with no target."""
- drawer = MPLDrawer(1, 3)
+ drawer = MPLDrawer(1, {i: i for i in range(3)})
ctrl_wires = (0, 1)
rgba_red = (1, 0, 0, 1)
@@ -443,7 +454,7 @@ def test_ctrl_formatting(self):
def test_ctrl_circ(self):
"""Test only the ``_ctrl_circ`` private method."""
- drawer = MPLDrawer(1, 1)
+ drawer = MPLDrawer(1, {0: 0})
drawer._ctrl_circ(0, 0)
circ = drawer.ax.patches[0]
@@ -457,7 +468,7 @@ def test_ctrl_circ(self):
def test_ctrlo_circ(self):
"""Test only the ``ctrlo_circ`` private method."""
- drawer = MPLDrawer(1, 1)
+ drawer = MPLDrawer(1, {0: 0})
drawer._ctrlo_circ(0, 0)
circ = drawer.ax.patches[0]
@@ -469,7 +480,7 @@ def test_ctrlo_circ(self):
def test_ctrl_target(self):
"""Tests target impacts line extent"""
- drawer = MPLDrawer(1, 3)
+ drawer = MPLDrawer(1, {i: i for i in range(3)})
drawer.ctrl(0, 0, 2)
@@ -497,14 +508,14 @@ def test_ctrl_target(self):
)
def test_ctrl_raises_warning_with_overlap(self, control_wires, target_wires):
"""Tests that a warning is raised if some control indicators are not visible."""
- drawer = MPLDrawer(1, 4)
+ drawer = MPLDrawer(1, {i: i for i in range(4)})
with pytest.warns(UserWarning, match="control indicators are hidden behind an operator"):
drawer.ctrl(0, control_wires, target_wires)
plt.close()
@pytest.mark.parametrize("control_wires,target_wires", [((0,), (1, 2)), ((2,), (0, 1))])
def test_ctrl_no_warning_without_overlap(self, control_wires, target_wires):
- drawer = MPLDrawer(1, 3)
+ drawer = MPLDrawer(1, {i: i for i in range(3)})
with warnings.catch_warnings(record=True) as w:
drawer.ctrl(0, control_wires, target_wires)
assert len(w) == 0
@@ -513,7 +524,7 @@ def test_ctrl_no_warning_without_overlap(self, control_wires, target_wires):
def test_target_x(self):
"""Tests hidden target_x drawing method"""
- drawer = MPLDrawer(1, 3)
+ drawer = MPLDrawer(1, {i: i for i in range(3)})
drawer._target_x(0, 0)
@@ -533,7 +544,7 @@ def test_target_x(self):
def test_target_x_color(self):
"""Test the color of target_x."""
- drawer = MPLDrawer(1, 3)
+ drawer = MPLDrawer(1, {i: i for i in range(3)})
rgba_red = (1, 0, 0, 1)
drawer._target_x(0, 0, options={"color": rgba_red})
@@ -553,7 +564,7 @@ def test_target_x_color(self):
def test_CNOT(self):
"""Tests the CNOT method"""
- drawer = MPLDrawer(1, 3)
+ drawer = MPLDrawer(1, {i: i for i in range(3)})
drawer.CNOT(0, (0, 1))
@@ -578,7 +589,7 @@ def test_CNOT(self):
def test_CNOT_control_values(self):
"""Tests the ``control_values`` keyword for CNOT."""
- drawer = MPLDrawer(1, 3)
+ drawer = MPLDrawer(1, {i: i for i in range(3)})
drawer.CNOT(0, (0, 1, 2), control_values=[True, False])
@@ -593,7 +604,7 @@ def test_CNOT_control_values(self):
def test_CNOT_color(self):
"""Tests the color of CNOT."""
- drawer = MPLDrawer(1, 3)
+ drawer = MPLDrawer(1, {i: i for i in range(3)})
rgba_red = (1, 0, 0, 1)
drawer.CNOT(0, (0, 1), options={"color": rgba_red})
@@ -618,7 +629,7 @@ class TestSWAP:
def test_swap_x(self):
"""Tests the ``_swap_x`` private method."""
- drawer = MPLDrawer(1, 1)
+ drawer = MPLDrawer(1, {0: 0})
drawer._swap_x(0, 0)
l1 = drawer.ax.lines[1]
@@ -631,7 +642,7 @@ def test_swap_x(self):
def test_SWAP(self):
"""Tests the SWAP method."""
- drawer = MPLDrawer(1, 3)
+ drawer = MPLDrawer(1, {i: i for i in range(3)})
drawer.SWAP(0, (0, 2))
connecting_line = drawer.ax.lines[3]
@@ -647,7 +658,7 @@ def test_SWAP(self):
def test_SWAP_options(self):
"""Tests that SWAP can be colored."""
- drawer = MPLDrawer(1, 3)
+ drawer = MPLDrawer(1, {i: i for i in range(3)})
rgba_red = (1, 0, 0, 1)
options = {"color": rgba_red, "linewidth": 3}
drawer.SWAP(0, (0, 2), options=options)
@@ -665,7 +676,7 @@ class TestMeasure:
def test_measure(self):
"""Tests the measure method."""
- drawer = MPLDrawer(1, 1)
+ drawer = MPLDrawer(1, {0: 0})
drawer.measure(0, 0)
box = drawer.ax.patches[0]
@@ -688,7 +699,7 @@ def test_measure(self):
def test_measure_multiple_wires(self):
"""Tests the measure method when multiple wires are provided."""
- drawer = MPLDrawer(n_layers=1, n_wires=2)
+ drawer = MPLDrawer(n_layers=1, wire_map={0: 0, 1: 1})
drawer.measure(layer=0, wires=(0, 1))
box = drawer.ax.patches[0]
@@ -712,7 +723,7 @@ def test_measure_multiple_wires(self):
def test_measure_classical_wires(self):
"""Tests the measure method when multiple wires are provided."""
- drawer = MPLDrawer(n_layers=1, n_wires=2, c_wires=2)
+ drawer = MPLDrawer(n_layers=1, wire_map={0: 0, 1: 1}, c_wires=2)
drawer.measure(layer=0, wires=(2, 3))
box = drawer.ax.patches[0]
@@ -735,7 +746,7 @@ def test_measure_classical_wires(self):
def test_measure_text(self):
"""Test adding a postselection label to a measure box."""
- drawer = MPLDrawer(1, 1)
+ drawer = MPLDrawer(1, {0: 0})
drawer.measure(0, 0, text="0")
assert len(drawer.ax.texts) == 1
assert drawer.ax.texts[0].get_text() == "0"
@@ -745,7 +756,7 @@ def test_measure_text(self):
def test_measure_formatted(self):
"""Tests you can color the measure box"""
- drawer = MPLDrawer(1, 1)
+ drawer = MPLDrawer(1, {0: 0})
rgba_red = (1.0, 0, 0, 1.0)
rgba_green = (0, 1, 0, 1)
box_options = {"facecolor": rgba_red, "edgecolor": rgba_green}
@@ -794,7 +805,7 @@ class `text_dims` method
def test_autosize_false(self):
"""Test that the text is unchanged if autosize is set to False."""
- drawer = MPLDrawer(n_layers=1, n_wires=2)
+ drawer = MPLDrawer(n_layers=1, wire_map={0: 0, 1: 1})
drawer.box_gate(0, (0, 1), text="very very long text", autosize=False)
t = drawer.ax.texts[0]
@@ -807,7 +818,7 @@ def test_autosize_one_wire(self):
"""Test case where the box is on only one wire. The text should still
be inside the box, but not rotated."""
- drawer = MPLDrawer(n_layers=1, n_wires=1)
+ drawer = MPLDrawer(n_layers=1, wire_map={0: 0})
drawer.box_gate(0, 0, text="very very long text", autosize=True)
t = drawer.ax.texts[0]
@@ -821,7 +832,7 @@ def test_autosize_multiwires(self):
"""Test case where the box is on multiple wires. The text should
be rotated 90deg, and still inside the box."""
- drawer = MPLDrawer(n_layers=1, n_wires=2)
+ drawer = MPLDrawer(n_layers=1, wire_map={0: 0, 1: 1})
drawer.box_gate(0, (0, 1), text="very very long text", autosize=True)
t = drawer.ax.texts[0]
@@ -835,7 +846,7 @@ def test_multiline_text_single_wire(self):
"""Test case where the box is on one wire and the text is skinny and tall.
If the text is too tall, it should still be shrunk to fit inside the box."""
- drawer = MPLDrawer(n_layers=1, n_wires=1)
+ drawer = MPLDrawer(n_layers=1, wire_map={0: 0})
drawer.box_gate(0, 0, text="text\nwith\nall\nthe\nlines\nyep", autosize=True)
t = drawer.ax.texts[0]
@@ -850,7 +861,7 @@ def text_tall_multitline_text_multiwires(self):
If the text is just too tall and the width fits inside the box, the text should not
be rotated."""
- drawer = MPLDrawer(n_layers=1, n_wires=2)
+ drawer = MPLDrawer(n_layers=1, wire_map={0: 0, 1: 1})
drawer.box_gate(
0,
(0, 1),
@@ -869,7 +880,7 @@ def test_wide_multline_text_multiwires(self):
"""Test case where the box is on multiple wires and text is fat, tall,
and fatter than it is tall. It should be rotated."""
- drawer = MPLDrawer(n_layers=1, n_wires=2)
+ drawer = MPLDrawer(n_layers=1, wire_map={0: 0, 1: 1})
drawer.box_gate(0, (0, 1), text="very very long text\nall\nthe\nlines\nyep", autosize=True)
assert self.text_in_box(drawer)
@@ -880,7 +891,7 @@ def test_wide_multline_text_multiwires(self):
class TestClassicalWires:
def test_classical_wire(self):
"""Test the addition of horiziontal classical wires."""
- drawer = MPLDrawer(n_wires=1, n_layers=4, c_wires=3)
+ drawer = MPLDrawer(wire_map={0: 0}, n_layers=4, c_wires=3)
layers = [0, 0, 1, 1]
wires = [0, 1, 1, 0]
@@ -906,7 +917,7 @@ def test_classical_wire(self):
def test_cwire_join(self):
"""Test the cwire join method."""
- drawer = MPLDrawer(n_wires=1, n_layers=4, c_wires=3)
+ drawer = MPLDrawer(wire_map={0: 0}, n_layers=4, c_wires=3)
drawer.cwire_join(1, 2)
@@ -920,7 +931,7 @@ def test_cwire_join(self):
def test_cwire_join_erase_right(self):
"""Test the cwire join method."""
- drawer = MPLDrawer(n_wires=1, n_layers=4, c_wires=3)
+ drawer = MPLDrawer(wire_map={0: 0}, n_layers=4, c_wires=3)
drawer.cwire_join(1, 1, erase_right=True)
@@ -938,7 +949,7 @@ class TestCond:
def test_cond_basic(self):
"""Tests cond from one wire to the next."""
- drawer = MPLDrawer(n_wires=2, n_layers=2)
+ drawer = MPLDrawer(wire_map={0: 0, 1: 1}, n_layers=2)
wire_data_before = [line.get_data() for line in drawer._wire_lines]
drawer.cond(layer=1, measured_layer=0, wires=[0], wires_target=[1])
@@ -957,7 +968,7 @@ def test_cond_basic(self):
def test_cond_two_ctrl_wires(self):
"""Tests cond from two separated wires."""
- drawer = MPLDrawer(n_wires=4, n_layers=2)
+ drawer = MPLDrawer(wire_map={i: i for i in range(4)}, n_layers=2)
drawer.cond(layer=1, measured_layer=0, wires=[0, 2], wires_target=[3])
actual_data = [line.get_data() for line in drawer.ax.lines]
@@ -978,7 +989,7 @@ def test_cond_two_ctrl_wires(self):
def test_cond_two_ctrl_wires_upward(self):
"""Test cond when the conditional operation is above the control wires."""
- drawer = MPLDrawer(n_wires=3, n_layers=2)
+ drawer = MPLDrawer(wire_map={i: i for i in range(3)}, n_layers=2)
drawer.cond(layer=1, measured_layer=0, wires=[1, 2], wires_target=[0])
actual_data = [line.get_data() for line in drawer.ax.lines]
assert actual_data == [
@@ -1006,7 +1017,7 @@ def test_cond_two_ctrl_wires_upward(self):
)
def test_cond_fail_with_bad_order(self, ctrl_wires, target_wires):
"""Tests cond raises an error when the wires aren't neatly separated."""
- drawer = MPLDrawer(n_wires=4, n_layers=2)
+ drawer = MPLDrawer(wire_map={i: i for i in range(4)}, n_layers=2)
with pytest.raises(ValueError, match="Cannot draw interspersed mid-circuit measurements"):
drawer.cond(layer=1, measured_layer=0, wires=ctrl_wires, wires_target=target_wires)
plt.close()
diff --git a/tests/test_debugging.py b/tests/test_debugging.py
index 46fcb59f973..a07cb29a587 100644
--- a/tests/test_debugging.py
+++ b/tests/test_debugging.py
@@ -401,7 +401,7 @@ def circuit(add_bad_snapshot: bool):
)
assert result["execution_results"] == expected["execution_results"]
- # Make sure shots are overriden correctly
+ # Make sure shots are overridden correctly
result = qml.snapshots(circuit)(add_bad_snapshot=False, shots=200)
assert result[0] == {"00": 74, "10": 58, "20": 68}
@@ -555,7 +555,7 @@ def circuit():
_compare_numpy_dicts(result, expected)
- # Make sure shots are overriden correctly
+ # Make sure shots are overridden correctly
result = qml.snapshots(circuit)(shots=200)
assert result[3] == {"0": 98, "1": 102}
assert np.allclose(result[5], expected[5])
@@ -612,7 +612,7 @@ def circuit():
assert ttest_ind([count["0"] for count in counts], 250).pvalue >= 0.75
assert ttest_ind(expvals, 0.0).pvalue >= 0.75
- # Make sure shots are overriden correctly
+ # Make sure shots are overridden correctly
counts, _ = tuple(zip(*(qml.snapshots(circuit)(shots=1000).values() for _ in range(50))))
assert ttest_ind([count["0"] for count in counts], 500).pvalue >= 0.75
@@ -687,7 +687,7 @@ def circuit():
_compare_numpy_dicts(result, expected)
- # Make sure shots are overriden correctly
+ # Make sure shots are overridden correctly
result = circuit(shots=200)
assert np.allclose(
result[0],
diff --git a/tests/test_vqe.py b/tests/test_vqe.py
index 8ef5b27497b..483c9254f45 100644
--- a/tests/test_vqe.py
+++ b/tests/test_vqe.py
@@ -415,9 +415,9 @@ def test_optimize_multiple_terms_autograd(self, seed):
qml.PauliZ(wires=[4]) @ qml.PauliZ(wires=[3]),
]
- coefs = (np.random.rand(len(obs)) - 0.5) * 2
- hamiltonian1 = qml.Hamiltonian(coefs, obs)
- hamiltonian2 = qml.Hamiltonian(coefs, obs)
+ coeffs = (np.random.rand(len(obs)) - 0.5) * 2
+ hamiltonian1 = qml.Hamiltonian(coeffs, obs)
+ hamiltonian2 = qml.Hamiltonian(coeffs, obs)
hamiltonian1.compute_grouping()
cost = generate_cost_fn(
@@ -471,9 +471,9 @@ def test_optimize_multiple_terms_torch(self, seed):
qml.PauliZ(wires=[4]) @ qml.PauliZ(wires=[3]),
]
- coefs = (np.random.rand(len(obs)) - 0.5) * 2
- hamiltonian1 = qml.Hamiltonian(coefs, obs)
- hamiltonian2 = qml.Hamiltonian(coefs, obs)
+ coeffs = (np.random.rand(len(obs)) - 0.5) * 2
+ hamiltonian1 = qml.Hamiltonian(coeffs, obs)
+ hamiltonian2 = qml.Hamiltonian(coeffs, obs)
hamiltonian1.compute_grouping()
cost = generate_cost_fn(
@@ -527,9 +527,9 @@ def test_optimize_multiple_terms_tf(self, seed):
qml.PauliZ(wires=[4]) @ qml.PauliZ(wires=[3]),
]
- coefs = (np.random.rand(len(obs)) - 0.5) * 2
- hamiltonian1 = qml.Hamiltonian(coefs, obs)
- hamiltonian2 = qml.Hamiltonian(coefs, obs)
+ coeffs = (np.random.rand(len(obs)) - 0.5) * 2
+ hamiltonian1 = qml.Hamiltonian(coeffs, obs)
+ hamiltonian2 = qml.Hamiltonian(coeffs, obs)
hamiltonian1.compute_grouping()
cost = generate_cost_fn(
diff --git a/tests/workflow/interfaces/test_jacobian_products.py b/tests/workflow/interfaces/test_jacobian_products.py
index 1991ba2d523..9a090d3af0a 100644
--- a/tests/workflow/interfaces/test_jacobian_products.py
+++ b/tests/workflow/interfaces/test_jacobian_products.py
@@ -273,7 +273,7 @@ def test_batch_vjp(self, jpc, shots):
pytest.skip("jpc does not work with finite shots.")
if jpc is hadamard_grad_jpc and qml.measurements.Shots(shots).has_partitioned_shots:
pytest.skip(
- "hadamard gradient does not support multiple measurments with partitioned shots."
+ "hadamard gradient does not support multiple measurements with partitioned shots."
)
x = 0.385